diff --git a/kernel-xenlinux.spec b/kernel-xenlinux.spec index b77ae91..5d87b4c 100644 --- a/kernel-xenlinux.spec +++ b/kernel-xenlinux.spec @@ -60,14 +60,14 @@ Source17: apply-patches Source33: check-for-config-changes Source60: config.sh Source100: config-%{build_flavor} -Source102: patches.arch.tar.bz2 -Source103: patches.drivers.tar.bz2 -Source104: patches.fixes.tar.bz2 -Source105: patches.rpmify.tar.bz2 -Source106: patches.suse.tar.bz2 -Source107: patches.xen.tar.bz2 -Source108: patches.addon.tar.bz2 -Source109: patches.kernel.org.tar.bz2 +Source200: patches.arch +Source201: patches.drivers +Source202: patches.fixes +Source203: patches.rpmify +Source204: patches.suse +Source205: patches.xen +Source206: patches.addon +Source207: patches.kernel.org BuildRoot: %{_tmppath}/%{name}-%{version}-build ExclusiveArch: x86_64 @@ -84,13 +84,13 @@ fi SYMBOLS="xen-dom0 xenlinux" # Unpack all sources and patches -%setup -q -c -T -a 0 -a 102 -a 103 -a 104 -a 105 -a 106 -a 107 -a 108 -a 109 +%setup -q -c -T -a 0 mkdir -p %kernel_build_dir cd linux-%version -%_sourcedir/apply-patches %_sourcedir/series.conf .. $SYMBOLS +%_sourcedir/apply-patches %_sourcedir/series.conf %_sourcedir $SYMBOLS cd %kernel_build_dir diff --git a/patches.addon.tar.bz2 b/patches.addon.tar.bz2 deleted file mode 100644 index b47fe1a..0000000 Binary files a/patches.addon.tar.bz2 and /dev/null differ diff --git a/patches.arch.tar.bz2 b/patches.arch.tar.bz2 deleted file mode 100644 index 81473ba..0000000 Binary files a/patches.arch.tar.bz2 and /dev/null differ diff --git a/patches.arch/UV-Expose-irq_desc-node-in-proc.patch b/patches.arch/UV-Expose-irq_desc-node-in-proc.patch new file mode 100644 index 0000000..aef2d88 --- /dev/null +++ b/patches.arch/UV-Expose-irq_desc-node-in-proc.patch @@ -0,0 +1,69 @@ +From: Dimitri Sivanich +Subject: Expose the irq_desc node as /proc/irq/*/node. +References: bnc#566745, fate#306952 +Patch-mainline: not yet + +This file provides device hardware locality information for apps desiring +to include hardware locality in irq mapping decisions. + +Signed-off-by: Dimitri Sivanich +Signed-off-by: Andrew Morton +Signed-off-by: Rafael J. Wysocki +--- + + Documentation/filesystems/proc.txt | 4 ++++ + kernel/irq/proc.c | 23 +++++++++++++++++++++++ + 2 files changed, 27 insertions(+) + +--- a/Documentation/filesystems/proc.txt ++++ b/Documentation/filesystems/proc.txt +@@ -566,6 +566,10 @@ The default_smp_affinity mask applies to + IRQs which have not yet been allocated/activated, and hence which lack a + /proc/irq/[0-9]* directory. + ++The node file on an SMP system shows the node to which the device using the IRQ ++reports itself as being attached. This hardware locality information does not ++include information about any possible driver locality preference. ++ + prof_cpu_mask specifies which CPUs are to be profiled by the system wide + profiler. Default value is ffffffff (all cpus). + +--- a/kernel/irq/proc.c ++++ b/kernel/irq/proc.c +@@ -146,6 +146,26 @@ static const struct file_operations defa + .release = single_release, + .write = default_affinity_write, + }; ++ ++static int irq_node_proc_show(struct seq_file *m, void *v) ++{ ++ struct irq_desc *desc = irq_to_desc((long) m->private); ++ ++ seq_printf(m, "%d\n", desc->node); ++ return 0; ++} ++ ++static int irq_node_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, irq_node_proc_show, PDE(inode)->data); ++} ++ ++static const struct file_operations irq_node_proc_fops = { ++ .open = irq_node_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; + #endif + + static int irq_spurious_proc_show(struct seq_file *m, void *v) +@@ -230,6 +250,9 @@ void register_irq_proc(unsigned int irq, + /* create /proc/irq//smp_affinity */ + proc_create_data("smp_affinity", 0600, desc->dir, + &irq_affinity_proc_fops, (void *)(long)irq); ++ ++ proc_create_data("node", 0444, desc->dir, ++ &irq_node_proc_fops, (void *)(long)irq); + #endif + + proc_create_data("spurious", 0444, desc->dir, diff --git a/patches.arch/acpi-export-hotplug_execute b/patches.arch/acpi-export-hotplug_execute new file mode 100644 index 0000000..c50a568 --- /dev/null +++ b/patches.arch/acpi-export-hotplug_execute @@ -0,0 +1,24 @@ +From: Jeff Mahoney +Subject: acpi: export acpi_os_hotplug_execute +Patch-mainline: not yet + + The ACPI dock driver changes require acpi_os_hotplug_execute, + which wasn't exported. + + This patch exports it. + +Signed-off-by: Jeff Mahoney +--- + drivers/acpi/osl.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/acpi/osl.c ++++ b/drivers/acpi/osl.c +@@ -782,6 +782,7 @@ acpi_status acpi_os_hotplug_execute(acpi + { + return __acpi_os_execute(0, function, context, 1); + } ++EXPORT_SYMBOL(acpi_os_hotplug_execute); + + void acpi_os_wait_events_complete(void *context) + { diff --git a/patches.arch/acpi_ec_provide_non_interrupt_mode_boot_param.patch b/patches.arch/acpi_ec_provide_non_interrupt_mode_boot_param.patch new file mode 100644 index 0000000..4888f5b --- /dev/null +++ b/patches.arch/acpi_ec_provide_non_interrupt_mode_boot_param.patch @@ -0,0 +1,67 @@ +From: Alexey Starikovskiy +Subject: ACPI: EC: Don't degrade to poll mode at storm automatically. +References: bnc#446142 +Patch-Mainline: no + +Signed-off-by: Thomas Renninger + +Not all users of semi-broken EC devices want to degrade to poll mode, so +give them right to choose. + +Signed-off-by: Alexey Starikovskiy +--- + + Documentation/kernel-parameters.txt | 5 +++++ + drivers/acpi/ec.c | 15 +++++++++++++++ + 2 files changed, 20 insertions(+) + + +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -691,6 +691,11 @@ and is between 256 and 4096 characters. + + eata= [HW,SCSI] + ++ ec_intr= [HW,ACPI] ACPI Embedded Controller interrupt mode ++ Format: ++ 0: polling mode ++ non-0: interrupt mode (default) ++ + edd= [EDD] + Format: {"off" | "on" | "skip[mbr]"} + +--- a/drivers/acpi/ec.c ++++ b/drivers/acpi/ec.c +@@ -118,6 +118,8 @@ static struct acpi_ec { + spinlock_t curr_lock; + } *boot_ec, *first_ec; + ++int acpi_ec_intr = 1; /* Default is interrupt mode */ ++ + static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ + + /* -------------------------------------------------------------------------- +@@ -754,6 +756,8 @@ static int ec_install_handlers(struct ac + &acpi_ec_gpe_handler, ec); + if (ACPI_FAILURE(status)) + return -ENODEV; ++ if (!acpi_ec_intr) ++ set_bit(EC_FLAGS_NO_GPE, &ec->flags); + acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe(NULL, ec->gpe); + status = acpi_install_address_space_handler(ec->handle, +@@ -1034,3 +1038,14 @@ static void __exit acpi_ec_exit(void) + return; + } + #endif /* 0 */ ++ ++static int __init acpi_ec_set_intr_mode(char *str) ++{ ++ if (!get_option(&str, &acpi_ec_intr)) { ++ acpi_ec_intr = 0; ++ return 0; ++ } ++ return 1; ++} ++ ++__setup("ec_intr=", acpi_ec_set_intr_mode); diff --git a/patches.arch/acpi_srat-pxm-rev-ia64.patch b/patches.arch/acpi_srat-pxm-rev-ia64.patch new file mode 100644 index 0000000..c01c731 --- /dev/null +++ b/patches.arch/acpi_srat-pxm-rev-ia64.patch @@ -0,0 +1,59 @@ +From: Kurt Garloff +Subject: Use SRAT table rev to use 8bit or 16/32bit PXM fields (ia64) +References: bnc#503038 +Patch-mainline: not yet + +In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides +32bits for these. The new fields were reserved before. +According to the ACPI spec, the OS must disregrard reserved fields. + +ia64 did handle the PXM fields almost consistently, but depending on +sgi's sn2 platform. This patch leaves the sn2 logic in, but does also +use 16/32 bits for PXM if the SRAT has rev 2 or higher. + +The patch also adds __init to the two pxm accessor functions, as they +access __initdata now and are called from an __init function only anyway. + +Note that the code only uses 16 bits for the PXM field in the processor +proximity field; the patch does not address this as 16 bits are more than +enough. + +This is patch 3/3. + +Signed-off-by: Kurt Garloff + +--- + arch/ia64/kernel/acpi.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/arch/ia64/kernel/acpi.c ++++ b/arch/ia64/kernel/acpi.c +@@ -428,22 +428,24 @@ static u32 __devinitdata pxm_flag[PXM_FL + static struct acpi_table_slit __initdata *slit_table; + cpumask_t early_cpu_possible_map = CPU_MASK_NONE; + +-static int get_processor_proximity_domain(struct acpi_srat_cpu_affinity *pa) ++static int __init ++get_processor_proximity_domain(struct acpi_srat_cpu_affinity *pa) + { + int pxm; + + pxm = pa->proximity_domain_lo; +- if (ia64_platform_is("sn2")) ++ if (ia64_platform_is("sn2") || acpi_srat_revision >= 2) + pxm += pa->proximity_domain_hi[0] << 8; + return pxm; + } + +-static int get_memory_proximity_domain(struct acpi_srat_mem_affinity *ma) ++static int __init ++get_memory_proximity_domain(struct acpi_srat_mem_affinity *ma) + { + int pxm; + + pxm = ma->proximity_domain; +- if (!ia64_platform_is("sn2")) ++ if (!ia64_platform_is("sn2") && acpi_srat_revision <= 1) + pxm &= 0xff; + + return pxm; diff --git a/patches.arch/acpi_srat-pxm-rev-store.patch b/patches.arch/acpi_srat-pxm-rev-store.patch new file mode 100644 index 0000000..08b2d79 --- /dev/null +++ b/patches.arch/acpi_srat-pxm-rev-store.patch @@ -0,0 +1,52 @@ +From: Kurt Garloff +Subject: Store SRAT table revision +References: bnc#503038 +Patch-mainline: not yet + +In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides +32bits for these. The new fields were reserved before. +According to the ACPI spec, the OS must disregrard reserved fields. +In order to know whether or not, we must know what version the SRAT +table has. + +This patch stores the SRAT table revision for later consumption +by arch specific __init functions. + +This is patch 1/3. + +Signed-off-by: Kurt Garloff + +--- + drivers/acpi/numa.c | 3 +++ + include/acpi/acpi_numa.h | 1 + + 2 files changed, 4 insertions(+) + +--- a/drivers/acpi/numa.c ++++ b/drivers/acpi/numa.c +@@ -45,6 +45,8 @@ static int pxm_to_node_map[MAX_PXM_DOMAI + static int node_to_pxm_map[MAX_NUMNODES] + = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; + ++unsigned char acpi_srat_revision __initdata; ++ + int pxm_to_node(int pxm) + { + if (pxm < 0) +@@ -259,6 +261,7 @@ static int __init acpi_parse_srat(struct + return -EINVAL; + + srat = (struct acpi_table_srat *)table; ++ acpi_srat_revision = srat->header.revision; + + return 0; + } +--- a/include/acpi/acpi_numa.h ++++ b/include/acpi/acpi_numa.h +@@ -15,6 +15,7 @@ extern int pxm_to_node(int); + extern int node_to_pxm(int); + extern void __acpi_map_pxm_to_node(int, int); + extern int acpi_map_pxm_to_node(int); ++extern unsigned char acpi_srat_revision; + + #endif /* CONFIG_ACPI_NUMA */ + #endif /* __ACP_NUMA_H */ diff --git a/patches.arch/acpi_srat-pxm-rev-x86-64.patch b/patches.arch/acpi_srat-pxm-rev-x86-64.patch new file mode 100644 index 0000000..a7a120b --- /dev/null +++ b/patches.arch/acpi_srat-pxm-rev-x86-64.patch @@ -0,0 +1,42 @@ +From: Kurt Garloff +Subject: Use SRAT table rev to use 8bit or 32bit PXM fields (x86-64) +References: bnc#503038 +Patch-mainline: not yet + +In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides +32bits for these. The new fields were reserved before. +According to the ACPI spec, the OS must disregrard reserved fields. + +x86-64 was rather inconsistent prior to this patch; it used 8 bits +for the pxm field in cpu_affinity, but 32 bits in mem_affinity. +This patch makes it consistent: Either use 8 bits consistently (SRAT +rev 1 or lower) or 32 bits (SRAT rev 2 or higher). + +This is patch 2/3. + +Signed-off-by: Kurt Garloff + +--- + arch/x86/mm/srat_64.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/x86/mm/srat_64.c ++++ b/arch/x86/mm/srat_64.c +@@ -156,6 +156,8 @@ acpi_numa_processor_affinity_init(struct + if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) + return; + pxm = pa->proximity_domain_lo; ++ if (acpi_srat_revision >= 2) ++ pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8; + node = setup_node(pxm); + if (node < 0) { + printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); +@@ -259,6 +261,8 @@ acpi_numa_memory_affinity_init(struct ac + start = ma->base_address; + end = start + ma->length; + pxm = ma->proximity_domain; ++ if (acpi_srat_revision <= 1) ++ pxm &= 0xff; + node = setup_node(pxm); + if (node < 0) { + printk(KERN_ERR "SRAT: Too many proximity domains.\n"); diff --git a/patches.arch/acpi_thermal_passive_blacklist.patch b/patches.arch/acpi_thermal_passive_blacklist.patch new file mode 100644 index 0000000..3f6331e --- /dev/null +++ b/patches.arch/acpi_thermal_passive_blacklist.patch @@ -0,0 +1,105 @@ +From: Thomas Renninger +Subject: Avoid critical temp shutdowns on specific ThinkPad T4x(p) and R40 +References: https://bugzilla.novell.com/show_bug.cgi?id=333043 +Patch-mainline: not yet + +--- + drivers/acpi/thermal.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 73 insertions(+) + +--- a/drivers/acpi/thermal.c ++++ b/drivers/acpi/thermal.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1383,6 +1384,66 @@ static void acpi_thermal_guess_offset(st + tz->kelvin_offset = 2732; + } + ++static struct dmi_system_id thermal_psv_dmi_table[] = { ++ { ++ .ident = "IBM ThinkPad T41", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), ++ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T41"), ++ }, ++ }, ++ { ++ .ident = "IBM ThinkPad T42", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), ++ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T42"), ++ }, ++ }, ++ { ++ .ident = "IBM ThinkPad T43", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), ++ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T43"), ++ }, ++ }, ++ { ++ .ident = "IBM ThinkPad T41p", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), ++ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T41p"), ++ }, ++ }, ++ { ++ .ident = "IBM ThinkPad T42p", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), ++ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T42p"), ++ }, ++ }, ++ { ++ .ident = "IBM ThinkPad T43p", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), ++ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T43p"), ++ }, ++ }, ++ { ++ .ident = "IBM ThinkPad R40", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), ++ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad R40"), ++ }, ++ }, ++ { ++ .ident = "IBM ThinkPad R50p", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), ++ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad R50p"), ++ }, ++ }, ++ {}, ++}; ++ + static int acpi_thermal_add(struct acpi_device *device) + { + int result = 0; +@@ -1414,6 +1475,18 @@ static int acpi_thermal_add(struct acpi_ + if (result) + goto free_memory; + ++ if (dmi_check_system(thermal_psv_dmi_table)) { ++ if (tz->trips.passive.flags.valid && ++ tz->trips.passive.temperature > CELSIUS_TO_KELVIN(85)) { ++ printk (KERN_INFO "Adjust passive trip point from %lu" ++ " to %lu\n", ++ KELVIN_TO_CELSIUS(tz->trips.passive.temperature), ++ KELVIN_TO_CELSIUS(tz->trips.passive.temperature - 150)); ++ tz->trips.passive.temperature -= 150; ++ acpi_thermal_set_polling(tz, 5); ++ } ++ } ++ + result = acpi_thermal_add_fs(device); + if (result) + goto unregister_thermal_zone; diff --git a/patches.arch/acpi_thinkpad_introduce_acpi_root_table_boot_param.patch b/patches.arch/acpi_thinkpad_introduce_acpi_root_table_boot_param.patch new file mode 100644 index 0000000..a932f2b --- /dev/null +++ b/patches.arch/acpi_thinkpad_introduce_acpi_root_table_boot_param.patch @@ -0,0 +1,118 @@ +From: Thomas Renninger +Subject: Introduce acpi_root_table=rsdt boot param and dmi list to force rsdt +Patch-mainline: not yet +References: http://bugzilla.kernel.org/show_bug.cgi?id=8246 + +This one is part of a patch series: +acpi_thinkpad_introduce_acpi_root_table_boot_param.patch +acpi_thinkpad_introduce_acpica_rsdt_global_variable.patch +acpi_thinkpad_remove_R40e_c-state_blacklist.patch + +Blacklist R40e, R51e and T40, T40p, T41, T41p, T42, T42p, R50 and R50p +ThinkPads to use the RSDT instead of the XSDT. + +Update: Jan 12 2009 jeffm +* 2.6.29-rc1 introduced acpi_rsdt_forced. I've updated the patch to issue + a warning that acpi=rsdt is the prefered method of forcing. +* Moved the dmi table stuff to the main dmi table in x86/kernel/acpi/boot. + +Update: Apr 10 2009 jeffm +* Removed documentation, since it's deprecated. + +Signed-off-by: Thomas Renninger +Tested-by: Mark Doughty +CC: Yakui Zhao + +--- + arch/x86/kernel/acpi/boot.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ + drivers/acpi/tables.c | 3 ++ + 2 files changed, 56 insertions(+) + +--- a/arch/x86/kernel/acpi/boot.c ++++ b/arch/x86/kernel/acpi/boot.c +@@ -1313,6 +1313,21 @@ static int __init dmi_ignore_irq0_timer_ + return 0; + } + ++static int __init force_acpi_rsdt(const struct dmi_system_id *d) ++{ ++ if (!acpi_force) { ++ printk(KERN_NOTICE "%s detected: force use of acpi=rsdt\n", ++ d->ident); ++ acpi_rsdt_forced = 1; ++ } else { ++ printk(KERN_NOTICE ++ "Warning: acpi=force overrules DMI blacklist: " ++ "acpi=rsdt\n"); ++ } ++ return 0; ++ ++} ++ + /* + * If your system is blacklisted here, but you find that acpi=force + * works for you, please contact linux-acpi@vger.kernel.org +@@ -1388,6 +1403,32 @@ static struct dmi_system_id __initdata a + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), + }, + }, ++ ++ /* ++ * Boxes that need RSDT as ACPI root table ++ */ ++ { ++ .callback = force_acpi_rsdt, ++ .ident = "ThinkPad ", /* R40e, broken C-states */ ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR, "IBM"), ++ DMI_MATCH(DMI_BIOS_VERSION, "1SET")}, ++ }, ++ { ++ .callback = force_acpi_rsdt, ++ .ident = "ThinkPad ", /* R50e, slow booting */ ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR, "IBM"), ++ DMI_MATCH(DMI_BIOS_VERSION, "1WET")}, ++ }, ++ { ++ .callback = force_acpi_rsdt, ++ .ident = "ThinkPad ", /* T40, T40p, T41, T41p, T42, T42p ++ R50, R50p */ ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR, "IBM"), ++ DMI_MATCH(DMI_BIOS_VERSION, "1RET")}, ++ }, + {} + }; + +@@ -1583,6 +1624,18 @@ static int __init parse_acpi(char *arg) + } + early_param("acpi", parse_acpi); + ++/* Alias for acpi=rsdt for compatibility with openSUSE 11.1 and SLE11 */ ++static int __init parse_acpi_root_table(char *opt) ++{ ++ if (!strcmp(opt, "rsdt")) { ++ acpi_rsdt_forced = 1; ++ printk(KERN_WARNING "acpi_root_table=rsdt is deprecated. " ++ "Please use acpi=rsdt instead.\n"); ++ } ++ return 0; ++} ++early_param("acpi_root_table", parse_acpi_root_table); ++ + /* FIXME: Using pci= for an ACPI parameter is a travesty. */ + static int __init parse_pci(char *arg) + { +--- a/drivers/acpi/tables.c ++++ b/drivers/acpi/tables.c +@@ -339,6 +339,9 @@ int __init acpi_table_init(void) + { + acpi_status status; + ++ if (acpi_rsdt_forced) ++ printk(KERN_INFO "Using RSDT as ACPI root table\n"); ++ + status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0); + if (ACPI_FAILURE(status)) + return 1; diff --git a/patches.arch/ia64-page-migration b/patches.arch/ia64-page-migration new file mode 100644 index 0000000..a23ae43 --- /dev/null +++ b/patches.arch/ia64-page-migration @@ -0,0 +1,603 @@ +From: Russ Anderson +Subject: ia64: Call migration code on correctable errors v8 +References: 415829 +Acked-by: schwab@suse.de +Patch-mainline: not yet + +Migrate data off pages with correctable memory errors. This patch is the +ia64 specific piece. It connects the CPE handler to the page migration +code. It is implemented as a kernel loadable module, similar to the mca +recovery code (mca_recovery.ko). This allows the feature to be turned off +by uninstalling the module. + +Update Jan 19 2009 jeffm: +- isolate_lru_page doesn't put the page on a list anymore + + +Signed-off-by: Russ Anderson + +--- + arch/ia64/Kconfig | 9 + arch/ia64/include/asm/mca.h | 6 + arch/ia64/include/asm/page.h | 1 + arch/ia64/kernel/Makefile | 1 + arch/ia64/kernel/cpe_migrate.c | 434 +++++++++++++++++++++++++++++++++++++++++ + arch/ia64/kernel/mca.c | 37 +++ + 6 files changed, 487 insertions(+), 1 deletion(-) + +--- a/arch/ia64/Kconfig ++++ b/arch/ia64/Kconfig +@@ -505,6 +505,15 @@ config ARCH_PROC_KCORE_TEXT + config IA64_MCA_RECOVERY + tristate "MCA recovery from errors other than TLB." + ++config IA64_CPE_MIGRATE ++ tristate "Migrate data off pages with correctable errors" ++ default m ++ help ++ Migrate data off pages with correctable memory errors. Selecting ++ Y will build this functionality into the kernel. Selecting M will ++ build this functionality as a kernel loadable module. Installing ++ the module will turn on the functionality. ++ + config PERFMON + bool "Performance monitor support" + help +--- a/arch/ia64/include/asm/mca.h ++++ b/arch/ia64/include/asm/mca.h +@@ -142,6 +142,7 @@ extern unsigned long __per_cpu_mca[NR_CP + + extern int cpe_vector; + extern int ia64_cpe_irq; ++extern int cpe_poll_enabled; + extern void ia64_mca_init(void); + extern void ia64_mca_cpu_init(void *); + extern void ia64_os_mca_dispatch(void); +@@ -156,11 +157,16 @@ extern void ia64_slave_init_handler(void + extern void ia64_mca_cmc_vector_setup(void); + extern int ia64_reg_MCA_extension(int (*fn)(void *, struct ia64_sal_os_state *)); + extern void ia64_unreg_MCA_extension(void); ++extern int ia64_reg_CE_extension(int (*fn)(void *)); ++extern void ia64_unreg_CE_extension(void); + extern unsigned long ia64_get_rnat(unsigned long *); + extern void ia64_set_psr_mc(void); + extern void ia64_mca_printk(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); + ++extern struct list_head badpagelist; ++extern unsigned int total_badpages; ++ + struct ia64_mca_notify_die { + struct ia64_sal_os_state *sos; + int *monarch_cpu; +--- a/arch/ia64/include/asm/page.h ++++ b/arch/ia64/include/asm/page.h +@@ -121,6 +121,7 @@ extern unsigned long max_low_pfn; + #endif + + #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) ++#define phys_to_page(kaddr) (pfn_to_page(kaddr >> PAGE_SHIFT)) + #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) + #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) + +--- a/arch/ia64/kernel/Makefile ++++ b/arch/ia64/kernel/Makefile +@@ -25,6 +25,7 @@ obj-$(CONFIG_PERFMON) += perfmon_defaul + obj-$(CONFIG_IA64_CYCLONE) += cyclone.o + obj-$(CONFIG_CPU_FREQ) += cpufreq/ + obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o ++obj-$(CONFIG_IA64_CPE_MIGRATE) += cpe_migrate.o + obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o + obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o + obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o +--- /dev/null ++++ b/arch/ia64/kernel/cpe_migrate.c +@@ -0,0 +1,434 @@ ++/* ++ * File: cpe_migrate.c ++ * Purpose: Migrate data from physical pages with excessive correctable ++ * errors to new physical pages. Keep the old pages on a discard ++ * list. ++ * ++ * Copyright (C) 2008 SGI - Silicon Graphics Inc. ++ * Copyright (C) 2008 Russ Anderson ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define BADRAM_BASENAME "badram" ++#define CE_HISTORY_LENGTH 30 ++ ++struct cpe_info { ++ u64 paddr; ++ u16 node; ++}; ++static struct cpe_info cpe[CE_HISTORY_LENGTH]; ++ ++static int cpe_polling_enabled = 1; ++static int cpe_head; ++static int cpe_tail; ++static int work_scheduled; ++static int mstat_cannot_isolate; ++static int mstat_failed_to_discard; ++static int mstat_already_marked; ++static int mstat_already_on_list; ++ ++DEFINE_SPINLOCK(cpe_migrate_lock); ++ ++static void ++get_physical_address(void *buffer, u64 *paddr, u16 *node) ++{ ++ sal_log_record_header_t *rh; ++ sal_log_mem_dev_err_info_t *mdei; ++ ia64_err_rec_t *err_rec; ++ sal_log_platform_err_info_t *plat_err; ++ efi_guid_t guid; ++ ++ err_rec = buffer; ++ rh = &err_rec->sal_elog_header; ++ *paddr = 0; ++ *node = 0; ++ ++ /* ++ * Make sure it is a corrected error. ++ */ ++ if (rh->severity != sal_log_severity_corrected) ++ return; ++ ++ plat_err = (sal_log_platform_err_info_t *)&err_rec->proc_err; ++ ++ guid = plat_err->mem_dev_err.header.guid; ++ if (efi_guidcmp(guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID) == 0) { ++ /* ++ * Memory cpe ++ */ ++ mdei = &plat_err->mem_dev_err; ++ if (mdei->valid.oem_data) { ++ if (mdei->valid.physical_addr) ++ *paddr = mdei->physical_addr; ++ ++ if (mdei->valid.node) { ++ if (ia64_platform_is("sn2")) ++ *node = nasid_to_cnodeid(mdei->node); ++ else ++ *node = mdei->node; ++ } ++ } ++ } ++} ++ ++static struct page * ++alloc_migrate_page(struct page *ignored, unsigned long node, int **x) ++{ ++ ++ return alloc_pages_node(node, GFP_HIGHUSER_MOVABLE, 0); ++} ++ ++static int ++validate_paddr_page(u64 paddr) ++{ ++ struct page *page; ++ ++ if (!paddr) ++ return -EINVAL; ++ ++ if (!ia64_phys_addr_valid(paddr)) ++ return -EINVAL; ++ ++ if (!pfn_valid(paddr >> PAGE_SHIFT)) ++ return -EINVAL; ++ ++ page = phys_to_page(paddr); ++ if (PageMemError(page)) ++ mstat_already_marked++; ++ return 0; ++} ++ ++extern int isolate_lru_page(struct page *); ++static int ++ia64_mca_cpe_move_page(u64 paddr, u32 node) ++{ ++ LIST_HEAD(pagelist); ++ struct page *page; ++ int ret; ++ ++ ret = validate_paddr_page(paddr); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * convert physical address to page number ++ */ ++ page = phys_to_page(paddr); ++ ++ migrate_prep(); ++ ret = isolate_lru_page(page); ++ if (ret) { ++ mstat_cannot_isolate++; ++ return ret; ++ } ++ ++ list_add(&page->lru, &pagelist); ++ ret = migrate_pages(&pagelist, alloc_migrate_page, node, 0); ++ if (ret == 0) { ++ total_badpages++; ++ list_add_tail(&page->lru, &badpagelist); ++ } else { ++ mstat_failed_to_discard++; ++ /* ++ * The page failed to migrate and is not on the bad page list. ++ * Clearing the error bit will allow another attempt to migrate ++ * if it gets another correctable error. ++ */ ++ ClearPageMemError(page); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ia64_mca_cpe_migrate ++ * The worker that does the actual migration. It pulls a ++ * physical address off the list and calls the migration code. ++ */ ++static void ++ia64_mca_cpe_migrate(struct work_struct *unused) ++{ ++ int ret; ++ u64 paddr; ++ u16 node; ++ ++ do { ++ paddr = cpe[cpe_tail].paddr; ++ if (paddr) { ++ /* ++ * There is a valid entry that needs processing. ++ */ ++ node = cpe[cpe_tail].node; ++ ++ ret = ia64_mca_cpe_move_page(paddr, node); ++ if (ret <= 0) ++ /* ++ * Even though the return status is negative, ++ * clear the entry. If the same address has ++ * another CPE it will be re-added to the list. ++ */ ++ cpe[cpe_tail].paddr = 0; ++ ++ } ++ if (++cpe_tail >= CE_HISTORY_LENGTH) ++ cpe_tail = 0; ++ ++ } while (cpe_tail != cpe_head); ++ work_scheduled = 0; ++} ++ ++static DECLARE_WORK(cpe_enable_work, ia64_mca_cpe_migrate); ++DEFINE_SPINLOCK(cpe_list_lock); ++ ++/* ++ * cpe_setup_migrate ++ * Get the physical address out of the CPE record, add it ++ * to the list of addresses to migrate (if not already on), ++ * and schedule the back end worker task. This is called ++ * in interrupt context so cannot directly call the migration ++ * code. ++ * ++ * Inputs ++ * rec The CPE record ++ * Outputs ++ * 1 on Success, -1 on failure ++ */ ++static int ++cpe_setup_migrate(void *rec) ++{ ++ u64 paddr; ++ u16 node; ++ /* int head, tail; */ ++ int i, ret; ++ ++ if (!rec) ++ return -EINVAL; ++ ++ get_physical_address(rec, &paddr, &node); ++ ret = validate_paddr_page(paddr); ++ if (ret < 0) ++ return -EINVAL; ++ ++ if ((cpe_head != cpe_tail) || (cpe[cpe_head].paddr != 0)) ++ /* ++ * List not empty ++ */ ++ for (i = 0; i < CE_HISTORY_LENGTH; i++) { ++ if (PAGE_ALIGN(cpe[i].paddr) == PAGE_ALIGN(paddr)) { ++ mstat_already_on_list++; ++ return 1; /* already on the list */ ++ } ++ } ++ ++ if (!spin_trylock(&cpe_list_lock)) { ++ /* ++ * Someone else has the lock. To avoid spinning in interrupt ++ * handler context, bail. ++ */ ++ return 1; ++ } ++ ++ if (cpe[cpe_head].paddr == 0) { ++ cpe[cpe_head].node = node; ++ cpe[cpe_head].paddr = paddr; ++ ++ if (++cpe_head >= CE_HISTORY_LENGTH) ++ cpe_head = 0; ++ } ++ spin_unlock(&cpe_list_lock); ++ ++ if (!work_scheduled) { ++ work_scheduled = 1; ++ schedule_work(&cpe_enable_work); ++ } ++ ++ return 1; ++} ++ ++/* ++ * ============================================================================= ++ */ ++ ++/* ++ * free_one_bad_page ++ * Free one page from the list of bad pages. ++ */ ++static int ++free_one_bad_page(unsigned long paddr) ++{ ++ LIST_HEAD(pagelist); ++ struct page *page, *page2, *target; ++ ++ /* ++ * Verify page address ++ */ ++ target = phys_to_page(paddr); ++ list_for_each_entry_safe(page, page2, &badpagelist, lru) { ++ if (page != target) ++ continue; ++ ++ ClearPageMemError(page); /* Mark the page as good */ ++ total_badpages--; ++ list_move_tail(&page->lru, &pagelist); ++ putback_lru_pages(&pagelist); ++ break; ++ } ++ return 0; ++} ++ ++/* ++ * free_all_bad_pages ++ * Free all of the pages on the bad pages list. ++ */ ++static int ++free_all_bad_pages(void) ++{ ++ struct page *page, *page2; ++ ++ list_for_each_entry_safe(page, page2, &badpagelist, lru) { ++ ClearPageMemError(page); /* Mark the page as good */ ++ total_badpages--; ++ } ++ putback_lru_pages(&badpagelist); ++ return 0; ++} ++ ++#define OPT_LEN 16 ++ ++static ssize_t ++badpage_store(struct kobject *kobj, ++ struct kobj_attribute *attr, const char *buf, size_t count) ++{ ++ char optstr[OPT_LEN]; ++ unsigned long opt; ++ int len = OPT_LEN; ++ int err; ++ ++ if (count < len) ++ len = count; ++ ++ strlcpy(optstr, buf, len); ++ ++ err = strict_strtoul(optstr, 16, &opt); ++ if (err) ++ return err; ++ ++ if (opt == 0) ++ free_all_bad_pages(); ++ else ++ free_one_bad_page(opt); ++ ++ return count; ++} ++ ++/* ++ * badpage_show ++ * Display the number, size, and addresses of all the pages on the ++ * bad page list. ++ * ++ * Note that sysfs provides buf of PAGE_SIZE length. bufend tracks ++ * the remaining space in buf to avoid overflowing. ++ */ ++static ssize_t ++badpage_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++ ++{ ++ struct page *page, *page2; ++ int i = 0, cnt = 0; ++ char *bufend = buf + PAGE_SIZE; ++ ++ cnt = snprintf(buf, bufend - (buf + cnt), ++ "Memory marked bad: %d kB\n" ++ "Pages marked bad: %d\n" ++ "Unable to isolate on LRU: %d\n" ++ "Unable to migrate: %d\n" ++ "Already marked bad: %d\n" ++ "Already on list: %d\n" ++ "List of bad physical pages\n", ++ total_badpages << (PAGE_SHIFT - 10), total_badpages, ++ mstat_cannot_isolate, mstat_failed_to_discard, ++ mstat_already_marked, mstat_already_on_list ++ ); ++ ++ list_for_each_entry_safe(page, page2, &badpagelist, lru) { ++ if (bufend - (buf + cnt) < 20) ++ break; /* Avoid overflowing the buffer */ ++ cnt += snprintf(buf + cnt, bufend - (buf + cnt), ++ " 0x%011lx", page_to_phys(page)); ++ if (!(++i % 5)) ++ cnt += snprintf(buf + cnt, bufend - (buf + cnt), "\n"); ++ } ++ cnt += snprintf(buf + cnt, bufend - (buf + cnt), "\n"); ++ ++ return cnt; ++} ++ ++static struct kobj_attribute badram_attr = { ++ .attr = { ++ .name = "badram", ++ .mode = S_IWUSR | S_IRUGO, ++ }, ++ .show = badpage_show, ++ .store = badpage_store, ++}; ++ ++static int __init ++cpe_migrate_external_handler_init(void) ++{ ++ int error; ++ ++ error = sysfs_create_file(kernel_kobj, &badram_attr.attr); ++ if (error) ++ return -EINVAL; ++ ++ /* ++ * register external ce handler ++ */ ++ if (ia64_reg_CE_extension(cpe_setup_migrate)) { ++ printk(KERN_ERR "ia64_reg_CE_extension failed.\n"); ++ return -EFAULT; ++ } ++ cpe_poll_enabled = cpe_polling_enabled; ++ ++ printk(KERN_INFO "Registered badram Driver\n"); ++ return 0; ++} ++ ++static void __exit ++cpe_migrate_external_handler_exit(void) ++{ ++ /* unregister external mca handlers */ ++ ia64_unreg_CE_extension(); ++ ++ sysfs_remove_file(kernel_kobj, &badram_attr.attr); ++} ++ ++module_init(cpe_migrate_external_handler_init); ++module_exit(cpe_migrate_external_handler_exit); ++ ++module_param(cpe_polling_enabled, int, 0644); ++MODULE_PARM_DESC(cpe_polling_enabled, ++ "Enable polling with migration"); ++ ++MODULE_AUTHOR("Russ Anderson "); ++MODULE_DESCRIPTION("ia64 Corrected Error page migration driver"); +--- a/arch/ia64/kernel/mca.c ++++ b/arch/ia64/kernel/mca.c +@@ -68,6 +68,9 @@ + * + * 2007-04-27 Russ Anderson + * Support multiple cpus going through OS_MCA in the same event. ++ * ++ * 2008-04-22 Russ Anderson ++ * Migrate data off pages with correctable memory errors. + */ + #include + #include +@@ -163,7 +166,14 @@ static int cmc_polling_enabled = 1; + * but encounters problems retrieving CPE logs. This should only be + * necessary for debugging. + */ +-static int cpe_poll_enabled = 1; ++int cpe_poll_enabled = 1; ++EXPORT_SYMBOL(cpe_poll_enabled); ++ ++unsigned int total_badpages; ++EXPORT_SYMBOL(total_badpages); ++ ++LIST_HEAD(badpagelist); ++EXPORT_SYMBOL(badpagelist); + + extern void salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe); + +@@ -523,6 +533,28 @@ int mca_recover_range(unsigned long addr + } + EXPORT_SYMBOL_GPL(mca_recover_range); + ++/* Function pointer to Corrected Error memory migration driver */ ++int (*ia64_mca_ce_extension)(void *); ++ ++int ++ia64_reg_CE_extension(int (*fn)(void *)) ++{ ++ if (ia64_mca_ce_extension) ++ return 1; ++ ++ ia64_mca_ce_extension = fn; ++ return 0; ++} ++EXPORT_SYMBOL(ia64_reg_CE_extension); ++ ++void ++ia64_unreg_CE_extension(void) ++{ ++ if (ia64_mca_ce_extension) ++ ia64_mca_ce_extension = NULL; ++} ++EXPORT_SYMBOL(ia64_unreg_CE_extension); ++ + #ifdef CONFIG_ACPI + + int cpe_vector = -1; +@@ -534,6 +566,7 @@ ia64_mca_cpe_int_handler (int cpe_irq, v + static unsigned long cpe_history[CPE_HISTORY_LENGTH]; + static int index; + static DEFINE_SPINLOCK(cpe_history_lock); ++ int recover; + + IA64_MCA_DEBUG("%s: received interrupt vector = %#x on CPU %d\n", + __func__, cpe_irq, smp_processor_id()); +@@ -580,6 +613,8 @@ ia64_mca_cpe_int_handler (int cpe_irq, v + out: + /* Get the CPE error record and log it */ + ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE); ++ recover = (ia64_mca_ce_extension && ia64_mca_ce_extension( ++ IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_CPE))); + + return IRQ_HANDLED; + } diff --git a/patches.arch/ia64-page-migration.fix b/patches.arch/ia64-page-migration.fix new file mode 100644 index 0000000..fbc4357 --- /dev/null +++ b/patches.arch/ia64-page-migration.fix @@ -0,0 +1,159 @@ +From: Russ Anderson +Subject: ia64: cpe_migrate.ko causes deadlock. +References: bnc#464676 +Patch-mainline: not yet, depends on patches.arch/ia64-page-migration + +schedule_on_each_cpu() deadlocks when called from an event thread. +Change cpe_migrate to use a kthread to avoid the problem. + +Signed-off-by: Russ Anderson +Acked-by: Raymund Will + +--- + arch/ia64/kernel/cpe_migrate.c | 72 +++++++++++++++++++++++++++++++---------- + 1 file changed, 56 insertions(+), 16 deletions(-) + +--- a/arch/ia64/kernel/cpe_migrate.c ++++ b/arch/ia64/kernel/cpe_migrate.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -40,12 +41,15 @@ static struct cpe_info cpe[CE_HISTORY_LE + static int cpe_polling_enabled = 1; + static int cpe_head; + static int cpe_tail; +-static int work_scheduled; + static int mstat_cannot_isolate; + static int mstat_failed_to_discard; + static int mstat_already_marked; + static int mstat_already_on_list; + ++/* IRQ handler notifies this wait queue on receipt of an IRQ */ ++DECLARE_WAIT_QUEUE_HEAD(cpe_activate_IRQ_wq); ++static DECLARE_COMPLETION(kthread_cpe_migrated_exited); ++int cpe_active; + DEFINE_SPINLOCK(cpe_migrate_lock); + + static void +@@ -160,12 +164,12 @@ ia64_mca_cpe_move_page(u64 paddr, u32 no + } + + /* +- * ia64_mca_cpe_migrate +- * The worker that does the actual migration. It pulls a +- * physical address off the list and calls the migration code. ++ * cpe_process_queue ++ * Pulls the physical address off the list and calls the migration code. ++ * Will process all the addresses on the list. + */ +-static void +-ia64_mca_cpe_migrate(struct work_struct *unused) ++void ++cpe_process_queue(void) + { + int ret; + u64 paddr; +@@ -193,10 +197,36 @@ ia64_mca_cpe_migrate(struct work_struct + cpe_tail = 0; + + } while (cpe_tail != cpe_head); +- work_scheduled = 0; ++ return; ++} ++ ++inline int ++cpe_list_empty(void) ++{ ++ return (cpe_head == cpe_tail) && (!cpe[cpe_head].paddr); ++} ++ ++/* ++ * kthread_cpe_migrate ++ * kthread_cpe_migrate is created at module load time and lives ++ * until the module is removed. When not active, it will sleep. ++ */ ++static int ++kthread_cpe_migrate(void *ignore) ++{ ++ while (cpe_active) { ++ /* ++ * wait for work ++ */ ++ (void)wait_event_interruptible(cpe_activate_IRQ_wq, ++ (!cpe_list_empty() || ++ !cpe_active)); ++ cpe_process_queue(); /* process work */ ++ } ++ complete(&kthread_cpe_migrated_exited); ++ return 0; + } + +-static DECLARE_WORK(cpe_enable_work, ia64_mca_cpe_migrate); + DEFINE_SPINLOCK(cpe_list_lock); + + /* +@@ -228,10 +258,7 @@ cpe_setup_migrate(void *rec) + if (ret < 0) + return -EINVAL; + +- if ((cpe_head != cpe_tail) || (cpe[cpe_head].paddr != 0)) +- /* +- * List not empty +- */ ++ if (!cpe_list_empty()) + for (i = 0; i < CE_HISTORY_LENGTH; i++) { + if (PAGE_ALIGN(cpe[i].paddr) == PAGE_ALIGN(paddr)) { + mstat_already_on_list++; +@@ -256,10 +283,7 @@ cpe_setup_migrate(void *rec) + } + spin_unlock(&cpe_list_lock); + +- if (!work_scheduled) { +- work_scheduled = 1; +- schedule_work(&cpe_enable_work); +- } ++ wake_up_interruptible(&cpe_activate_IRQ_wq); + + return 1; + } +@@ -396,12 +420,23 @@ static int __init + cpe_migrate_external_handler_init(void) + { + int error; ++ struct task_struct *kthread; + + error = sysfs_create_file(kernel_kobj, &badram_attr.attr); + if (error) + return -EINVAL; + + /* ++ * set up the kthread ++ */ ++ cpe_active = 1; ++ kthread = kthread_run(kthread_cpe_migrate, NULL, "cpe_migrate"); ++ if (IS_ERR(kthread)) { ++ complete(&kthread_cpe_migrated_exited); ++ return -EFAULT; ++ } ++ ++ /* + * register external ce handler + */ + if (ia64_reg_CE_extension(cpe_setup_migrate)) { +@@ -420,6 +455,11 @@ cpe_migrate_external_handler_exit(void) + /* unregister external mca handlers */ + ia64_unreg_CE_extension(); + ++ /* Stop kthread */ ++ cpe_active = 0; /* tell kthread_cpe_migrate to exit */ ++ wake_up_interruptible(&cpe_activate_IRQ_wq); ++ wait_for_completion(&kthread_cpe_migrated_exited); ++ + sysfs_remove_file(kernel_kobj, &badram_attr.attr); + } + diff --git a/patches.arch/kmsg-fix-parameter-limitations b/patches.arch/kmsg-fix-parameter-limitations new file mode 100644 index 0000000..1518817 --- /dev/null +++ b/patches.arch/kmsg-fix-parameter-limitations @@ -0,0 +1,54 @@ +From: Jeff Mahoney +Subject: kmsg: Fix parameter limitations +Patch-mainline: Whenever kmsg is upstream + + The kmsg infrastructure, currently only employed on s/390, has limitations + with the parameters it can handle due to the way it assembles the + magic string for parsing with scripts/kmsg-doc. + + cpp expects the result to be a valid expression and exits with an error + if it is not. + + The netfilter ipvs code causes this error, though there are more examples: + error: pasting "_ARGS_" and "&" does not give a valid preprocessing token + + This stems from an otherwise valid expression: + pr_info("Registered protocols (%s)\n", &protocols[2]); + + It tries to concatenate _ARGS_ and &protocols[2] and fails. + + This patch fixes the issue by stringifying the entire parameter list + and allowing kmsg-doc to unquote the resultant expression. + + The dev_* expressions that evaluate to __KMSG_DEV are unaffected because + the insertion of the "dev, " between _ARGS_ and the parameter list ends + up creating a valid expression. + +Signed-off-by: Jeff Mahoney +--- + include/linux/kernel.h | 2 +- + scripts/kmsg-doc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -391,7 +391,7 @@ static inline char *pack_hex_byte(char * + + /* generate magic string for scripts/kmsg-doc to parse */ + #define pr_printk_hash(level, format, ...) \ +- __KMSG_PRINT(level _FMT_ format _ARGS_ ##__VA_ARGS__ _END_) ++ __KMSG_PRINT(level _FMT_ format _ARGS_ #__VA_ARGS__ _END_) + + #elif defined(CONFIG_KMSG_IDS) && defined(KMSG_COMPONENT) + +--- a/scripts/kmsg-doc ++++ b/scripts/kmsg-doc +@@ -307,7 +307,7 @@ sub process_cpp_file($$$$) + + while () { + chomp; +- if (/.*__KMSG_PRINT\(\s*(\S*)\s*_FMT_(.*)_ARGS_\s*(.*)?_END_\s*\)/o) { ++ if (/.*__KMSG_PRINT\(\s*(\S*)\s*_FMT_(.*)_ARGS_\s*"(.*)"\s*_END_\s*\)/o) { + if ($component ne "") { + add_kmsg_print($component, $1, $2, $3); + } else { diff --git a/patches.arch/kvm-only-export-selected-pv-ops-feature-structs b/patches.arch/kvm-only-export-selected-pv-ops-feature-structs new file mode 100644 index 0000000..154fe80 --- /dev/null +++ b/patches.arch/kvm-only-export-selected-pv-ops-feature-structs @@ -0,0 +1,139 @@ +From: Alexander Graf +Date: Wed, 18 Nov 2009 00:39:12 +0100 +Subject: Only export selected pv-ops feature structs +References: bnc#556135, FATE#306453 +Patch-Mainline: Submitted to virtualization list + +To really check for sure that we're not using any pv-ops code by accident, +we should make sure that we don't even export the structures used to access +pv-ops exported functions. + +So let's surround the pv-ops structs by #ifdefs. + +Signed-off-by: Alexander Graf +--- + arch/x86/kernel/paravirt.c | 35 +++++++++++++++++++++++++++++------ + 1 file changed, 29 insertions(+), 6 deletions(-) + +--- a/arch/x86/kernel/paravirt.c ++++ b/arch/x86/kernel/paravirt.c +@@ -124,11 +124,21 @@ static void *get_call_destination(u8 typ + { + struct paravirt_patch_template tmpl = { + .pv_init_ops = pv_init_ops, ++#ifdef CONFIG_PARAVIRT_TIME + .pv_time_ops = pv_time_ops, ++#endif ++#ifdef CONFIG_PARAVIRT_CPU + .pv_cpu_ops = pv_cpu_ops, ++#endif ++#ifdef CONFIG_PARAVIRT_IRQ + .pv_irq_ops = pv_irq_ops, ++#endif ++#ifdef CONFIG_PARAVIRT_APIC + .pv_apic_ops = pv_apic_ops, ++#endif ++#ifdef CONFIG_PARAVIRT_MMU + .pv_mmu_ops = pv_mmu_ops, ++#endif + #ifdef CONFIG_PARAVIRT_SPINLOCKS + .pv_lock_ops = pv_lock_ops, + #endif +@@ -185,6 +195,7 @@ unsigned paravirt_patch_insns(void *insn + return insn_len; + } + ++#ifdef CONFIG_PARAVIRT_MMU + static void native_flush_tlb(void) + { + __native_flush_tlb(); +@@ -203,6 +214,7 @@ static void native_flush_tlb_single(unsi + { + __native_flush_tlb_single(addr); + } ++#endif /* CONFIG_PARAVIRT_MMU */ + + /* These are in entry.S */ + extern void native_iret(void); +@@ -284,6 +296,7 @@ enum paravirt_lazy_mode paravirt_get_laz + return percpu_read(paravirt_lazy_mode); + } + ++#ifdef CONFIG_PARAVIRT_MMU + void arch_flush_lazy_mmu_mode(void) + { + preempt_disable(); +@@ -295,6 +308,7 @@ void arch_flush_lazy_mmu_mode(void) + + preempt_enable(); + } ++#endif /* CONFIG_PARAVIRT_MMU */ + + struct pv_info pv_info = { + .name = "bare hardware", +@@ -306,11 +320,16 @@ struct pv_info pv_info = { + struct pv_init_ops pv_init_ops = { + .patch = native_patch, + }; ++EXPORT_SYMBOL_GPL(pv_info); + ++#ifdef CONFIG_PARAVIRT_TIME + struct pv_time_ops pv_time_ops = { + .sched_clock = native_sched_clock, + }; ++EXPORT_SYMBOL_GPL(pv_time_ops); ++#endif + ++#ifdef CONFIG_PARAVIRT_IRQ + struct pv_irq_ops pv_irq_ops = { + .save_fl = __PV_IS_CALLEE_SAVE(native_save_fl), + .restore_fl = __PV_IS_CALLEE_SAVE(native_restore_fl), +@@ -322,7 +341,10 @@ struct pv_irq_ops pv_irq_ops = { + .adjust_exception_frame = paravirt_nop, + #endif + }; ++EXPORT_SYMBOL (pv_irq_ops); ++#endif + ++#ifdef CONFIG_PARAVIRT_CPU + struct pv_cpu_ops pv_cpu_ops = { + .cpuid = native_cpuid, + .get_debugreg = native_get_debugreg, +@@ -383,12 +405,17 @@ struct pv_cpu_ops pv_cpu_ops = { + .start_context_switch = paravirt_nop, + .end_context_switch = paravirt_nop, + }; ++EXPORT_SYMBOL (pv_cpu_ops); ++#endif + ++#ifdef CONFIG_PARAVIRT_APIC + struct pv_apic_ops pv_apic_ops = { + #ifdef CONFIG_X86_LOCAL_APIC + .startup_ipi_hook = paravirt_nop, + #endif + }; ++EXPORT_SYMBOL_GPL(pv_apic_ops); ++#endif + + #if defined(CONFIG_X86_32) && !defined(CONFIG_X86_PAE) + /* 32-bit pagetable entries */ +@@ -398,6 +425,7 @@ struct pv_apic_ops pv_apic_ops = { + #define PTE_IDENT __PV_IS_CALLEE_SAVE(_paravirt_ident_64) + #endif + ++#ifdef CONFIG_PARAVIRT_MMU + struct pv_mmu_ops pv_mmu_ops = { + + .read_cr2 = native_read_cr2, +@@ -466,10 +494,5 @@ struct pv_mmu_ops pv_mmu_ops = { + + .set_fixmap = native_set_fixmap, + }; +- +-EXPORT_SYMBOL_GPL(pv_time_ops); +-EXPORT_SYMBOL (pv_cpu_ops); + EXPORT_SYMBOL (pv_mmu_ops); +-EXPORT_SYMBOL_GPL(pv_apic_ops); +-EXPORT_SYMBOL_GPL(pv_info); +-EXPORT_SYMBOL (pv_irq_ops); ++#endif diff --git a/patches.arch/kvm-replace-kvm-io-delay-pv-ops-with-linux-magic b/patches.arch/kvm-replace-kvm-io-delay-pv-ops-with-linux-magic new file mode 100644 index 0000000..7a6f87d --- /dev/null +++ b/patches.arch/kvm-replace-kvm-io-delay-pv-ops-with-linux-magic @@ -0,0 +1,80 @@ +From: Alexander Graf +Date: Wed, 18 Nov 2009 12:58:00 +0100 +Subject: Replace kvm io delay pv-ops with linux magic +References: bnc#556135, FATE#306453 +Patch-Mainline: Submitted to virtualization list + +Currently we use pv-ops to tell linux not to do anything on io_delay. + +While the basic idea is good IMHO, I don't see why we would need pv-ops +for that. The io delay function already has a switch that can do nothing +if you're so inclined. + +So here's a patch (stacked on top of the previous pv-ops series) that +removes the io delay pv-ops hook and just sets the native io delay +variable instead. + +Signed-off-by: Alexander Graf +--- + arch/x86/Kconfig | 14 -------------- + arch/x86/kernel/kvm.c | 16 +++------------- + 2 files changed, 3 insertions(+), 27 deletions(-) + +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -544,20 +544,6 @@ config KVM_GUEST + This option enables various optimizations for running under the KVM + hypervisor. + +-config KVM_IODELAY +- bool "KVM IO-delay support" +- depends on KVM_GUEST +- select PARAVIRT_CPU +- ---help--- +- Usually we wait for PIO access to complete. When inside KVM there's +- no need to do that, as we know that we're not going through a bus, +- but process PIO requests instantly. +- +- This option disables PIO waits, but drags in CPU-bound pv-ops. Thus +- you will probably get more speed loss than speedup using this option. +- +- If in doubt, say N. +- + config KVM_MMU + bool "KVM PV MMU support" + depends on KVM_GUEST +--- a/arch/x86/kernel/kvm.c ++++ b/arch/x86/kernel/kvm.c +@@ -29,15 +29,6 @@ + #include + #include + +-#ifdef CONFIG_KVM_IODELAY +-/* +- * No need for any "IO delay" on KVM +- */ +-static void kvm_io_delay(void) +-{ +-} +-#endif /* CONFIG_KVM_IODELAY */ +- + #ifdef CONFIG_KVM_MMU + #define MMU_QUEUE_SIZE 1024 + +@@ -201,13 +192,12 @@ static void kvm_leave_lazy_mmu(void) + + static void __init paravirt_ops_setup(void) + { ++ extern int io_delay_type; + pv_info.name = "KVM"; + pv_info.paravirt_enabled = 1; + +-#ifdef CONFIG_KVM_IODELAY +- if (kvm_para_has_feature(KVM_FEATURE_NOP_IO_DELAY)) +- pv_cpu_ops.io_delay = kvm_io_delay; +-#endif ++ /* Disable IO delay */ ++ io_delay_type = CONFIG_IO_DELAY_TYPE_NONE; + + #ifdef CONFIG_KVM_MMU + if (kvm_para_has_feature(KVM_FEATURE_MMU_OP)) { diff --git a/patches.arch/kvm-split-paravirt-ops-by-functionality b/patches.arch/kvm-split-paravirt-ops-by-functionality new file mode 100644 index 0000000..392748e --- /dev/null +++ b/patches.arch/kvm-split-paravirt-ops-by-functionality @@ -0,0 +1,743 @@ +From: Alexander Graf +Date: Wed, 18 Nov 2009 00:27:59 +0100 +Subject: Split paravirt ops by functionality +References: bnc#556135, FATE#306453 +Patch-Mainline: Submitted to virtualization list + +Currently when using paravirt ops it's an all-or-nothing option. We can either +use pv-ops for CPU, MMU, timing, etc. or not at all. + +Now there are some use cases where we don't need the full feature set, but only +a small chunk of it. KVM is a pretty prominent example for this. + +So let's make everything a bit more fine-grained. We already have a splitting +by function groups, namely "cpu", "mmu", "time", "irq", "apic" and "spinlock". + +Taking that existing splitting and extending it to only compile in the PV +capable bits sounded like a natural fit. That way we don't get performance hits +in MMU code from using the KVM PV clock which only needs the TIME parts of +pv-ops. + +We define a new CONFIG_PARAVIRT_ALL option that basically does the same thing +the CONFIG_PARAVIRT did before this splitting. We move all users of +CONFIG_PARAVIRT to CONFIG_PARAVIRT_ALL, so they behave the same way they did +before. + +So here it is - the splitting! I would have made the patch smaller, but this +was the closest I could get to atomic (for bisect) while staying sane. + +Signed-off-by: Alexander Graf +--- + arch/x86/Kconfig | 47 +++++++++++++++++++++++++--- + arch/x86/include/asm/apic.h | 2 - + arch/x86/include/asm/desc.h | 4 +- + arch/x86/include/asm/fixmap.h | 2 - + arch/x86/include/asm/io.h | 2 - + arch/x86/include/asm/irqflags.h | 21 +++++++++--- + arch/x86/include/asm/mmu_context.h | 4 +- + arch/x86/include/asm/msr.h | 4 +- + arch/x86/include/asm/paravirt.h | 44 +++++++++++++++++++++++++- + arch/x86/include/asm/paravirt_types.h | 12 +++++++ + arch/x86/include/asm/pgalloc.h | 2 - + arch/x86/include/asm/pgtable-3level_types.h | 2 - + arch/x86/include/asm/pgtable.h | 2 - + arch/x86/include/asm/processor.h | 2 - + arch/x86/include/asm/required-features.h | 2 - + arch/x86/include/asm/smp.h | 2 - + arch/x86/include/asm/system.h | 13 +++++-- + arch/x86/include/asm/tlbflush.h | 4 +- + arch/x86/kernel/head_64.S | 2 - + arch/x86/kernel/paravirt.c | 2 + + arch/x86/kernel/tsc.c | 2 - + arch/x86/kernel/vsmp_64.c | 2 - + arch/x86/kernel/x8664_ksyms_64.c | 2 - + arch/x86/xen/Kconfig | 2 - + 24 files changed, 146 insertions(+), 37 deletions(-) + +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -362,7 +362,7 @@ endif + + config X86_VSMP + bool "ScaleMP vSMP" +- select PARAVIRT ++ select PARAVIRT_ALL + depends on X86_64 && PCI + depends on X86_EXTENDED_PLATFORM + ---help--- +@@ -510,7 +510,7 @@ source "arch/x86/xen/Kconfig" + + config VMI + bool "VMI Guest support (DEPRECATED)" +- select PARAVIRT ++ select PARAVIRT_ALL + depends on X86_32 + ---help--- + VMI provides a paravirtualized interface to the VMware ESX server +@@ -529,7 +529,6 @@ config VMI + + config KVM_CLOCK + bool "KVM paravirtualized clock" +- select PARAVIRT + select PARAVIRT_CLOCK + ---help--- + Turning on this option will allow you to run a paravirtualized clock +@@ -540,7 +539,7 @@ config KVM_CLOCK + + config KVM_GUEST + bool "KVM Guest support" +- select PARAVIRT ++ select PARAVIRT_ALL + ---help--- + This option enables various optimizations for running under the KVM + hypervisor. +@@ -568,8 +567,48 @@ config PARAVIRT_SPINLOCKS + + If you are unsure how to answer this question, answer N. + ++config PARAVIRT_CPU ++ bool ++ select PARAVIRT ++ default n ++ ++config PARAVIRT_TIME ++ bool ++ select PARAVIRT ++ default n ++ ++config PARAVIRT_IRQ ++ bool ++ select PARAVIRT ++ default n ++ ++config PARAVIRT_APIC ++ bool ++ select PARAVIRT ++ default n ++ ++config PARAVIRT_MMU ++ bool ++ select PARAVIRT ++ default n ++ ++# ++# This is a placeholder to activate the old "include all pv-ops functionality" ++# behavior. If you're using this I'd recommend looking through your code to see ++# if you can be more specific. It probably saves you a few cycles! ++# ++config PARAVIRT_ALL ++ bool ++ select PARAVIRT_CPU ++ select PARAVIRT_TIME ++ select PARAVIRT_IRQ ++ select PARAVIRT_APIC ++ select PARAVIRT_MMU ++ default n ++ + config PARAVIRT_CLOCK + bool ++ select PARAVIRT_TIME + default n + + endif +--- a/arch/x86/include/asm/apic.h ++++ b/arch/x86/include/asm/apic.h +@@ -81,7 +81,7 @@ static inline bool apic_from_smp_config( + /* + * Basic functions accessing APICs. + */ +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_APIC + #include + #endif + +--- a/arch/x86/include/asm/desc.h ++++ b/arch/x86/include/asm/desc.h +@@ -78,7 +78,7 @@ static inline int desc_empty(const void + return !(desc[0] | desc[1]); + } + +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_CPU + #include + #else + #define load_TR_desc() native_load_tr_desc() +@@ -108,7 +108,7 @@ static inline void paravirt_alloc_ldt(st + static inline void paravirt_free_ldt(struct desc_struct *ldt, unsigned entries) + { + } +-#endif /* CONFIG_PARAVIRT */ ++#endif /* CONFIG_PARAVIRT_CPU */ + + #define store_ldt(ldt) asm("sldt %0" : "=m"(ldt)) + +--- a/arch/x86/include/asm/fixmap.h ++++ b/arch/x86/include/asm/fixmap.h +@@ -162,7 +162,7 @@ void __native_set_fixmap(enum fixed_addr + void native_set_fixmap(enum fixed_addresses idx, + phys_addr_t phys, pgprot_t flags); + +-#ifndef CONFIG_PARAVIRT ++#ifndef CONFIG_PARAVIRT_MMU + static inline void __set_fixmap(enum fixed_addresses idx, + phys_addr_t phys, pgprot_t flags) + { +--- a/arch/x86/include/asm/io.h ++++ b/arch/x86/include/asm/io.h +@@ -268,7 +268,7 @@ extern void native_io_delay(void); + extern int io_delay_type; + extern void io_delay_init(void); + +-#if defined(CONFIG_PARAVIRT) ++#if defined(CONFIG_PARAVIRT_CPU) + #include + #else + +--- a/arch/x86/include/asm/irqflags.h ++++ b/arch/x86/include/asm/irqflags.h +@@ -58,9 +58,11 @@ static inline void native_halt(void) + + #ifdef CONFIG_PARAVIRT + #include +-#else ++#endif ++ + #ifndef __ASSEMBLY__ + ++#ifndef CONFIG_PARAVIRT_IRQ + static inline unsigned long __raw_local_save_flags(void) + { + return native_save_fl(); +@@ -110,12 +112,17 @@ static inline unsigned long __raw_local_ + + return flags; + } +-#else ++#endif /* CONFIG_PARAVIRT_IRQ */ ++ ++#else /* __ASSEMBLY__ */ + ++#ifndef CONFIG_PARAVIRT_IRQ + #define ENABLE_INTERRUPTS(x) sti + #define DISABLE_INTERRUPTS(x) cli ++#endif /* !CONFIG_PARAVIRT_IRQ */ + + #ifdef CONFIG_X86_64 ++#ifndef CONFIG_PARAVIRT_CPU + #define SWAPGS swapgs + /* + * Currently paravirt can't handle swapgs nicely when we +@@ -128,8 +135,6 @@ static inline unsigned long __raw_local_ + */ + #define SWAPGS_UNSAFE_STACK swapgs + +-#define PARAVIRT_ADJUST_EXCEPTION_FRAME /* */ +- + #define INTERRUPT_RETURN iretq + #define USERGS_SYSRET64 \ + swapgs; \ +@@ -141,16 +146,22 @@ static inline unsigned long __raw_local_ + swapgs; \ + sti; \ + sysexit ++#endif /* !CONFIG_PARAVIRT_CPU */ ++ ++#ifndef CONFIG_PARAVIRT_IRQ ++#define PARAVIRT_ADJUST_EXCEPTION_FRAME /* */ ++#endif /* !CONFIG_PARAVIRT_IRQ */ + + #else ++#ifndef CONFIG_PARAVIRT_CPU + #define INTERRUPT_RETURN iret + #define ENABLE_INTERRUPTS_SYSEXIT sti; sysexit + #define GET_CR0_INTO_EAX movl %cr0, %eax ++#endif /* !CONFIG_PARAVIRT_CPU */ + #endif + + + #endif /* __ASSEMBLY__ */ +-#endif /* CONFIG_PARAVIRT */ + + #ifndef __ASSEMBLY__ + #define raw_local_save_flags(flags) \ +--- a/arch/x86/include/asm/mmu_context.h ++++ b/arch/x86/include/asm/mmu_context.h +@@ -6,14 +6,14 @@ + #include + #include + #include +-#ifndef CONFIG_PARAVIRT ++#ifndef CONFIG_PARAVIRT_MMU + #include + + static inline void paravirt_activate_mm(struct mm_struct *prev, + struct mm_struct *next) + { + } +-#endif /* !CONFIG_PARAVIRT */ ++#endif /* !CONFIG_PARAVIRT_MMU */ + + /* + * Used for LDT copy/destruction. +--- a/arch/x86/include/asm/msr.h ++++ b/arch/x86/include/asm/msr.h +@@ -135,7 +135,7 @@ static inline unsigned long long native_ + return EAX_EDX_VAL(val, low, high); + } + +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_CPU + #include + #else + #include +@@ -246,7 +246,7 @@ do { + + #define rdtscpll(val, aux) (val) = native_read_tscp(&(aux)) + +-#endif /* !CONFIG_PARAVIRT */ ++#endif /* !CONFIG_PARAVIRT_CPU */ + + + #define checking_wrmsrl(msr, val) wrmsr_safe((msr), (u32)(val), \ +--- a/arch/x86/include/asm/paravirt.h ++++ b/arch/x86/include/asm/paravirt.h +@@ -18,6 +18,7 @@ static inline int paravirt_enabled(void) + return pv_info.paravirt_enabled; + } + ++#ifdef CONFIG_PARAVIRT_CPU + static inline void load_sp0(struct tss_struct *tss, + struct thread_struct *thread) + { +@@ -58,7 +59,9 @@ static inline void write_cr0(unsigned lo + { + PVOP_VCALL1(pv_cpu_ops.write_cr0, x); + } ++#endif /* CONFIG_PARAVIRT_CPU */ + ++#ifdef CONFIG_PARAVIRT_MMU + static inline unsigned long read_cr2(void) + { + return PVOP_CALL0(unsigned long, pv_mmu_ops.read_cr2); +@@ -78,7 +81,9 @@ static inline void write_cr3(unsigned lo + { + PVOP_VCALL1(pv_mmu_ops.write_cr3, x); + } ++#endif /* CONFIG_PARAVIRT_MMU */ + ++#ifdef CONFIG_PARAVIRT_CPU + static inline unsigned long read_cr4(void) + { + return PVOP_CALL0(unsigned long, pv_cpu_ops.read_cr4); +@@ -92,8 +97,9 @@ static inline void write_cr4(unsigned lo + { + PVOP_VCALL1(pv_cpu_ops.write_cr4, x); + } ++#endif /* CONFIG_PARAVIRT_CPU */ + +-#ifdef CONFIG_X86_64 ++#if defined(CONFIG_X86_64) && defined(CONFIG_PARAVIRT_CPU) + static inline unsigned long read_cr8(void) + { + return PVOP_CALL0(unsigned long, pv_cpu_ops.read_cr8); +@@ -105,6 +111,7 @@ static inline void write_cr8(unsigned lo + } + #endif + ++#ifdef CONFIG_PARAVIRT_IRQ + static inline void raw_safe_halt(void) + { + PVOP_VCALL0(pv_irq_ops.safe_halt); +@@ -114,14 +121,18 @@ static inline void halt(void) + { + PVOP_VCALL0(pv_irq_ops.safe_halt); + } ++#endif /* CONFIG_PARAVIRT_IRQ */ + ++#ifdef CONFIG_PARAVIRT_CPU + static inline void wbinvd(void) + { + PVOP_VCALL0(pv_cpu_ops.wbinvd); + } ++#endif + + #define get_kernel_rpl() (pv_info.kernel_rpl) + ++#ifdef CONFIG_PARAVIRT_CPU + static inline u64 paravirt_read_msr(unsigned msr, int *err) + { + return PVOP_CALL2(u64, pv_cpu_ops.read_msr, msr, err); +@@ -224,12 +235,16 @@ do { \ + } while (0) + + #define rdtscll(val) (val = paravirt_read_tsc()) ++#endif /* CONFIG_PARAVIRT_CPU */ + ++#ifdef CONFIG_PARAVIRT_TIME + static inline unsigned long long paravirt_sched_clock(void) + { + return PVOP_CALL0(unsigned long long, pv_time_ops.sched_clock); + } ++#endif /* CONFIG_PARAVIRT_TIME */ + ++#ifdef CONFIG_PARAVIRT_CPU + static inline unsigned long long paravirt_read_pmc(int counter) + { + return PVOP_CALL1(u64, pv_cpu_ops.read_pmc, counter); +@@ -345,8 +360,9 @@ static inline void slow_down_io(void) + pv_cpu_ops.io_delay(); + #endif + } ++#endif /* CONFIG_PARAVIRT_CPU */ + +-#ifdef CONFIG_SMP ++#if defined(CONFIG_SMP) && defined(CONFIG_PARAVIRT_APIC) + static inline void startup_ipi_hook(int phys_apicid, unsigned long start_eip, + unsigned long start_esp) + { +@@ -355,6 +371,7 @@ static inline void startup_ipi_hook(int + } + #endif + ++#ifdef CONFIG_PARAVIRT_MMU + static inline void paravirt_activate_mm(struct mm_struct *prev, + struct mm_struct *next) + { +@@ -689,7 +706,9 @@ static inline void pmd_clear(pmd_t *pmdp + set_pmd(pmdp, __pmd(0)); + } + #endif /* CONFIG_X86_PAE */ ++#endif /* CONFIG_PARAVIRT_MMU */ + ++#ifdef CONFIG_PARAVIRT_CPU + #define __HAVE_ARCH_START_CONTEXT_SWITCH + static inline void arch_start_context_switch(struct task_struct *prev) + { +@@ -700,7 +719,9 @@ static inline void arch_end_context_swit + { + PVOP_VCALL1(pv_cpu_ops.end_context_switch, next); + } ++#endif /* CONFIG_PARAVIRT_CPU */ + ++#ifdef CONFIG_PARAVIRT_MMU + #define __HAVE_ARCH_ENTER_LAZY_MMU_MODE + static inline void arch_enter_lazy_mmu_mode(void) + { +@@ -719,6 +740,7 @@ static inline void __set_fixmap(unsigned + { + pv_mmu_ops.set_fixmap(idx, phys, flags); + } ++#endif /* CONFIG_PARAVIRT_MMU */ + + #if defined(CONFIG_SMP) && defined(CONFIG_PARAVIRT_SPINLOCKS) + +@@ -829,6 +851,7 @@ static __always_inline void arch_spin_un + #define __PV_IS_CALLEE_SAVE(func) \ + ((struct paravirt_callee_save) { func }) + ++#ifdef CONFIG_PARAVIRT_IRQ + static inline unsigned long __raw_local_save_flags(void) + { + return PVOP_CALLEE0(unsigned long, pv_irq_ops.save_fl); +@@ -857,6 +880,7 @@ static inline unsigned long __raw_local_ + raw_local_irq_disable(); + return f; + } ++#endif /* CONFIG_PARAVIRT_IRQ */ + + + /* Make sure as little as possible of this mess escapes. */ +@@ -939,10 +963,13 @@ extern void default_banner(void); + #define PARA_INDIRECT(addr) *%cs:addr + #endif + ++#ifdef CONFIG_PARAVIRT_CPU + #define INTERRUPT_RETURN \ + PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_iret), CLBR_NONE, \ + jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_iret)) ++#endif /* CONFIG_PARAVIRT_CPU */ + ++#ifdef CONFIG_PARAVIRT_IRQ + #define DISABLE_INTERRUPTS(clobbers) \ + PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_irq_disable), clobbers, \ + PV_SAVE_REGS(clobbers | CLBR_CALLEE_SAVE); \ +@@ -954,13 +981,17 @@ extern void default_banner(void); + PV_SAVE_REGS(clobbers | CLBR_CALLEE_SAVE); \ + call PARA_INDIRECT(pv_irq_ops+PV_IRQ_irq_enable); \ + PV_RESTORE_REGS(clobbers | CLBR_CALLEE_SAVE);) ++#endif /* CONFIG_PARAVIRT_IRQ */ + ++#ifdef CONFIG_PARAVIRT_CPU + #define USERGS_SYSRET32 \ + PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_usergs_sysret32), \ + CLBR_NONE, \ + jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_usergs_sysret32)) ++#endif /* CONFIG_PARAVIRT_CPU */ + + #ifdef CONFIG_X86_32 ++#ifdef CONFIG_PARAVIRT_CPU + #define GET_CR0_INTO_EAX \ + push %ecx; push %edx; \ + call PARA_INDIRECT(pv_cpu_ops+PV_CPU_read_cr0); \ +@@ -970,10 +1001,12 @@ extern void default_banner(void); + PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_irq_enable_sysexit), \ + CLBR_NONE, \ + jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_irq_enable_sysexit)) ++#endif /* CONFIG_PARAVIRT_CPU */ + + + #else /* !CONFIG_X86_32 */ + ++#ifdef CONFIG_PARAVIRT_CPU + /* + * If swapgs is used while the userspace stack is still current, + * there's no way to call a pvop. The PV replacement *must* be +@@ -993,17 +1026,23 @@ extern void default_banner(void); + PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_swapgs), CLBR_NONE, \ + call PARA_INDIRECT(pv_cpu_ops+PV_CPU_swapgs) \ + ) ++#endif /* CONFIG_PARAVIRT_CPU */ + ++#ifdef CONFIG_PARAVIRT_MMU + #define GET_CR2_INTO_RCX \ + call PARA_INDIRECT(pv_mmu_ops+PV_MMU_read_cr2); \ + movq %rax, %rcx; \ + xorq %rax, %rax; ++#endif /* CONFIG_PARAVIRT_MMU */ + ++#ifdef CONFIG_PARAVIRT_IRQ + #define PARAVIRT_ADJUST_EXCEPTION_FRAME \ + PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_adjust_exception_frame), \ + CLBR_NONE, \ + call PARA_INDIRECT(pv_irq_ops+PV_IRQ_adjust_exception_frame)) ++#endif /* CONFIG_PARAVIRT_IRQ */ + ++#ifdef CONFIG_PARAVIRT_CPU + #define USERGS_SYSRET64 \ + PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_usergs_sysret64), \ + CLBR_NONE, \ +@@ -1013,6 +1052,7 @@ extern void default_banner(void); + PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_irq_enable_sysexit), \ + CLBR_NONE, \ + jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_irq_enable_sysexit)) ++#endif /* CONFIG_PARAVIRT_CPU */ + #endif /* CONFIG_X86_32 */ + + #endif /* __ASSEMBLY__ */ +--- a/arch/x86/include/asm/paravirt_types.h ++++ b/arch/x86/include/asm/paravirt_types.h +@@ -339,12 +339,24 @@ struct paravirt_patch_template { + + extern struct pv_info pv_info; + extern struct pv_init_ops pv_init_ops; ++#ifdef CONFIG_PARAVIRT_TIME + extern struct pv_time_ops pv_time_ops; ++#endif ++#ifdef CONFIG_PARAVIRT_CPU + extern struct pv_cpu_ops pv_cpu_ops; ++#endif ++#ifdef CONFIG_PARAVIRT_IRQ + extern struct pv_irq_ops pv_irq_ops; ++#endif ++#ifdef CONFIG_PARAVIRT_APIC + extern struct pv_apic_ops pv_apic_ops; ++#endif ++#ifdef CONFIG_PARAVIRT_MMU + extern struct pv_mmu_ops pv_mmu_ops; ++#endif ++#ifdef CONFIG_PARAVIRT_SPINLOCKS + extern struct pv_lock_ops pv_lock_ops; ++#endif + + #define PARAVIRT_PATCH(x) \ + (offsetof(struct paravirt_patch_template, x) / sizeof(void *)) +--- a/arch/x86/include/asm/pgalloc.h ++++ b/arch/x86/include/asm/pgalloc.h +@@ -7,7 +7,7 @@ + + static inline int __paravirt_pgd_alloc(struct mm_struct *mm) { return 0; } + +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_MMU + #include + #else + #define paravirt_pgd_alloc(mm) __paravirt_pgd_alloc(mm) +--- a/arch/x86/include/asm/pgtable-3level_types.h ++++ b/arch/x86/include/asm/pgtable-3level_types.h +@@ -18,7 +18,7 @@ typedef union { + } pte_t; + #endif /* !__ASSEMBLY__ */ + +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_MMU + #define SHARED_KERNEL_PMD (pv_info.shared_kernel_pmd) + #else + #define SHARED_KERNEL_PMD 1 +--- a/arch/x86/include/asm/pgtable.h ++++ b/arch/x86/include/asm/pgtable.h +@@ -28,7 +28,7 @@ extern unsigned long empty_zero_page[PAG + extern spinlock_t pgd_lock; + extern struct list_head pgd_list; + +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_MMU + #include + #else /* !CONFIG_PARAVIRT */ + #define set_pte(ptep, pte) native_set_pte(ptep, pte) +--- a/arch/x86/include/asm/processor.h ++++ b/arch/x86/include/asm/processor.h +@@ -573,7 +573,7 @@ static inline void native_swapgs(void) + #endif + } + +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_CPU + #include + #else + #define __cpuid native_cpuid +--- a/arch/x86/include/asm/required-features.h ++++ b/arch/x86/include/asm/required-features.h +@@ -48,7 +48,7 @@ + #endif + + #ifdef CONFIG_X86_64 +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_MMU + /* Paravirtualized systems may not have PSE or PGE available */ + #define NEED_PSE 0 + #define NEED_PGE 0 +--- a/arch/x86/include/asm/smp.h ++++ b/arch/x86/include/asm/smp.h +@@ -66,7 +66,7 @@ struct smp_ops { + extern void set_cpu_sibling_map(int cpu); + + #ifdef CONFIG_SMP +-#ifndef CONFIG_PARAVIRT ++#ifndef CONFIG_PARAVIRT_APIC + #define startup_ipi_hook(phys_apicid, start_eip, start_esp) do { } while (0) + #endif + extern struct smp_ops smp_ops; +--- a/arch/x86/include/asm/system.h ++++ b/arch/x86/include/asm/system.h +@@ -304,13 +304,18 @@ static inline void native_wbinvd(void) + + #ifdef CONFIG_PARAVIRT + #include +-#else +-#define read_cr0() (native_read_cr0()) +-#define write_cr0(x) (native_write_cr0(x)) ++#endif/* CONFIG_PARAVIRT */ ++ ++#ifndef CONFIG_PARAVIRT_MMU + #define read_cr2() (native_read_cr2()) + #define write_cr2(x) (native_write_cr2(x)) + #define read_cr3() (native_read_cr3()) + #define write_cr3(x) (native_write_cr3(x)) ++#endif /* CONFIG_PARAVIRT_MMU */ ++ ++#ifndef CONFIG_PARAVIRT_CPU ++#define read_cr0() (native_read_cr0()) ++#define write_cr0(x) (native_write_cr0(x)) + #define read_cr4() (native_read_cr4()) + #define read_cr4_safe() (native_read_cr4_safe()) + #define write_cr4(x) (native_write_cr4(x)) +@@ -324,7 +329,7 @@ static inline void native_wbinvd(void) + /* Clear the 'TS' bit */ + #define clts() (native_clts()) + +-#endif/* CONFIG_PARAVIRT */ ++#endif /* CONFIG_PARAVIRT_CPU */ + + #define stts() write_cr0(read_cr0() | X86_CR0_TS) + +--- a/arch/x86/include/asm/tlbflush.h ++++ b/arch/x86/include/asm/tlbflush.h +@@ -7,7 +7,7 @@ + #include + #include + +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_MMU + #include + #else + #define __flush_tlb() __native_flush_tlb() +@@ -162,7 +162,7 @@ static inline void reset_lazy_tlbstate(v + + #endif /* SMP */ + +-#ifndef CONFIG_PARAVIRT ++#ifndef CONFIG_PARAVIRT_MMU + #define flush_tlb_others(mask, mm, va) native_flush_tlb_others(mask, mm, va) + #endif + +--- a/arch/x86/kernel/head_64.S ++++ b/arch/x86/kernel/head_64.S +@@ -20,7 +20,7 @@ + #include + #include + +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_MMU + #include + #include + #else +--- a/arch/x86/kernel/paravirt.c ++++ b/arch/x86/kernel/paravirt.c +@@ -155,12 +155,14 @@ unsigned paravirt_patch_default(u8 type, + else if (opfunc == _paravirt_ident_64) + ret = paravirt_patch_ident_64(insnbuf, len); + ++#ifdef CONFIG_PARAVIRT_CPU + else if (type == PARAVIRT_PATCH(pv_cpu_ops.iret) || + type == PARAVIRT_PATCH(pv_cpu_ops.irq_enable_sysexit) || + type == PARAVIRT_PATCH(pv_cpu_ops.usergs_sysret32) || + type == PARAVIRT_PATCH(pv_cpu_ops.usergs_sysret64)) + /* If operation requires a jmp, then jmp */ + ret = paravirt_patch_jmp(insnbuf, opfunc, addr, len); ++#endif + else + /* Otherwise call the function; assume target could + clobber any caller-save reg */ +--- a/arch/x86/kernel/tsc.c ++++ b/arch/x86/kernel/tsc.c +@@ -66,7 +66,7 @@ u64 native_sched_clock(void) + + /* We need to define a real function for sched_clock, to override the + weak default version */ +-#ifdef CONFIG_PARAVIRT ++#ifdef CONFIG_PARAVIRT_TIME + unsigned long long sched_clock(void) + { + return paravirt_sched_clock(); +--- a/arch/x86/kernel/vsmp_64.c ++++ b/arch/x86/kernel/vsmp_64.c +@@ -22,7 +22,7 @@ + #include + #include + +-#if defined CONFIG_PCI && defined CONFIG_PARAVIRT ++#if defined CONFIG_PCI && defined CONFIG_PARAVIRT_IRQ + /* + * Interrupt control on vSMPowered systems: + * ~AC is a shadow of IF. If IF is 'on' AC should be 'off' +--- a/arch/x86/kernel/x8664_ksyms_64.c ++++ b/arch/x86/kernel/x8664_ksyms_64.c +@@ -55,6 +55,6 @@ EXPORT_SYMBOL(__memcpy); + + EXPORT_SYMBOL(empty_zero_page); + EXPORT_SYMBOL(init_level4_pgt); +-#ifndef CONFIG_PARAVIRT ++#ifndef CONFIG_PARAVIRT_CPU + EXPORT_SYMBOL(native_load_gs_index); + #endif +--- a/arch/x86/xen/Kconfig ++++ b/arch/x86/xen/Kconfig +@@ -4,7 +4,7 @@ + + config XEN + bool "Xen guest support" +- select PARAVIRT ++ select PARAVIRT_ALL + select PARAVIRT_CLOCK + depends on X86_64 || (X86_32 && X86_PAE && !X86_VISWS) + depends on X86_CMPXCHG && X86_TSC diff --git a/patches.arch/kvm-split-the-KVM-pv-ops-support-by-feature b/patches.arch/kvm-split-the-KVM-pv-ops-support-by-feature new file mode 100644 index 0000000..75f1a4a --- /dev/null +++ b/patches.arch/kvm-split-the-KVM-pv-ops-support-by-feature @@ -0,0 +1,125 @@ +From: Alexander Graf +Date: Wed, 18 Nov 2009 00:45:10 +0100 +Subject: Split the KVM pv-ops support by feature +References: bnc#556135, FATE#306453 +Patch-Mainline: Submitted to virtualization list + +Currently selecting KVM guest support enabled multiple features at once that +not everyone necessarily wants to have, namely: + + - PV MMU + - zero io delay + - apic detection workaround + +Let's split them off so we don't drag in the full pv-ops framework just to +detect we're running on KVM. That gives us more chances to tweak performance! + +Signed-off-by: Alexander Graf +--- + arch/x86/Kconfig | 29 ++++++++++++++++++++++++++++- + arch/x86/kernel/kvm.c | 22 +++++++++++++++------- + 2 files changed, 43 insertions(+), 8 deletions(-) + +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -539,11 +539,38 @@ config KVM_CLOCK + + config KVM_GUEST + bool "KVM Guest support" +- select PARAVIRT_ALL ++ select PARAVIRT + ---help--- + This option enables various optimizations for running under the KVM + hypervisor. + ++config KVM_IODELAY ++ bool "KVM IO-delay support" ++ depends on KVM_GUEST ++ select PARAVIRT_CPU ++ ---help--- ++ Usually we wait for PIO access to complete. When inside KVM there's ++ no need to do that, as we know that we're not going through a bus, ++ but process PIO requests instantly. ++ ++ This option disables PIO waits, but drags in CPU-bound pv-ops. Thus ++ you will probably get more speed loss than speedup using this option. ++ ++ If in doubt, say N. ++ ++config KVM_MMU ++ bool "KVM PV MMU support" ++ depends on KVM_GUEST ++ select PARAVIRT_MMU ++ ---help--- ++ This option enables the paravirtualized MMU for KVM. In most cases ++ it's pretty useless and shouldn't be used. ++ ++ It will only cost you performance, because it drags in pv-ops for ++ memory management. ++ ++ If in doubt, say N. ++ + source "arch/x86/lguest/Kconfig" + + config PARAVIRT +--- a/arch/x86/kernel/kvm.c ++++ b/arch/x86/kernel/kvm.c +@@ -29,6 +29,16 @@ + #include + #include + ++#ifdef CONFIG_KVM_IODELAY ++/* ++ * No need for any "IO delay" on KVM ++ */ ++static void kvm_io_delay(void) ++{ ++} ++#endif /* CONFIG_KVM_IODELAY */ ++ ++#ifdef CONFIG_KVM_MMU + #define MMU_QUEUE_SIZE 1024 + + struct kvm_para_state { +@@ -43,13 +53,6 @@ static struct kvm_para_state *kvm_para_s + return &per_cpu(para_state, raw_smp_processor_id()); + } + +-/* +- * No need for any "IO delay" on KVM +- */ +-static void kvm_io_delay(void) +-{ +-} +- + static void kvm_mmu_op(void *buffer, unsigned len) + { + int r; +@@ -194,15 +197,19 @@ static void kvm_leave_lazy_mmu(void) + mmu_queue_flush(state); + paravirt_leave_lazy_mmu(); + } ++#endif /* CONFIG_KVM_MMU */ + + static void __init paravirt_ops_setup(void) + { + pv_info.name = "KVM"; + pv_info.paravirt_enabled = 1; + ++#ifdef CONFIG_KVM_IODELAY + if (kvm_para_has_feature(KVM_FEATURE_NOP_IO_DELAY)) + pv_cpu_ops.io_delay = kvm_io_delay; ++#endif + ++#ifdef CONFIG_KVM_MMU + if (kvm_para_has_feature(KVM_FEATURE_MMU_OP)) { + pv_mmu_ops.set_pte = kvm_set_pte; + pv_mmu_ops.set_pte_at = kvm_set_pte_at; +@@ -226,6 +233,7 @@ static void __init paravirt_ops_setup(vo + pv_mmu_ops.lazy_mode.enter = kvm_enter_lazy_mmu; + pv_mmu_ops.lazy_mode.leave = kvm_leave_lazy_mmu; + } ++#endif /* CONFIG_KVM_MMU */ + #ifdef CONFIG_X86_IO_APIC + no_timer_check = 1; + #endif diff --git a/patches.arch/mm-avoid-bad-page-on-lru b/patches.arch/mm-avoid-bad-page-on-lru new file mode 100644 index 0000000..38e13dd --- /dev/null +++ b/patches.arch/mm-avoid-bad-page-on-lru @@ -0,0 +1,148 @@ +From: Russ Anderson +Subject: mm: Avoid putting a bad page back on the LRU v8 +References: 415829 +Acked-by: schwab@suse.de +Patch-mainline: not yet + +Prevent a page with a physical memory error from being placed back +on the LRU. A new page flag (PG_memerror) is added if +CONFIG_PAGEFLAGS_EXTENDED is defined. + +Version 8 change: Removed hot path check for pages with memory +errors on the free list. + +Signed-off-by: Russ Anderson +Reviewed-by: Christoph Lameter + +--- + include/linux/page-flags.h | 16 +++++++++++++++- + mm/migrate.c | 33 +++++++++++++++++++++++++++++++++ + mm/vmscan.c | 1 + + 3 files changed, 49 insertions(+), 1 deletion(-) + +--- a/include/linux/page-flags.h ++++ b/include/linux/page-flags.h +@@ -88,6 +88,7 @@ enum pageflags { + PG_private_2, /* If pagecache, has fs aux data */ + PG_writeback, /* Page is under writeback */ + #ifdef CONFIG_PAGEFLAGS_EXTENDED ++ PG_memerror, /* Page has a physical memory error */ + PG_head, /* A head page */ + PG_tail, /* A tail page */ + #else +@@ -168,14 +169,21 @@ static inline int TestClearPage##uname(s + static inline int __TestClearPage##uname(struct page *page) \ + { return __test_and_clear_bit(PG_##lname, &page->flags); } + ++#define PAGEFLAGMASK(uname, lname) \ ++static inline int PAGEMASK_##uname(void) \ ++ { return (1 << PG_##lname); } ++ + #define PAGEFLAG(uname, lname) TESTPAGEFLAG(uname, lname) \ +- SETPAGEFLAG(uname, lname) CLEARPAGEFLAG(uname, lname) ++ SETPAGEFLAG(uname, lname) CLEARPAGEFLAG(uname, lname) \ ++ PAGEFLAGMASK(uname, lname) + + #define __PAGEFLAG(uname, lname) TESTPAGEFLAG(uname, lname) \ + __SETPAGEFLAG(uname, lname) __CLEARPAGEFLAG(uname, lname) + + #define PAGEFLAG_FALSE(uname) \ + static inline int Page##uname(struct page *page) \ ++ { return 0; } \ ++static inline int PAGEMASK_##uname(void) \ + { return 0; } + + #define TESTSCFLAG(uname, lname) \ +@@ -393,6 +401,12 @@ static inline void __ClearPageTail(struc + + #endif /* !PAGEFLAGS_EXTENDED */ + ++#ifdef CONFIG_PAGEFLAGS_EXTENDED ++PAGEFLAG(MemError, memerror) ++#else ++PAGEFLAG_FALSE(MemError) ++#endif ++ + #ifdef CONFIG_MMU + #define __PG_MLOCKED (1 << PG_mlocked) + #else +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -53,6 +53,7 @@ int migrate_prep(void) + + return 0; + } ++EXPORT_SYMBOL(migrate_prep); + + /* + * Add isolated pages on the list back to the LRU under page lock +@@ -75,6 +76,7 @@ int putback_lru_pages(struct list_head * + } + return count; + } ++EXPORT_SYMBOL(putback_lru_pages); + + /* + * Restore a potential migration pte to a working pte entry +@@ -658,6 +660,25 @@ unlock: + * restored. + */ + list_del(&page->lru); ++ if (PageMemError(page)) { ++ if (rc == 0) ++ /* ++ * A page with a memory error that has ++ * been migrated will not be moved to ++ * the LRU. ++ */ ++ goto move_newpage; ++ else ++ /* ++ * The page failed to migrate and will not ++ * be added to the bad page list. Clearing ++ * the error bit will allow another attempt ++ * to migrate if it gets another correctable ++ * error. ++ */ ++ ClearPageMemError(page); ++ } ++ + dec_zone_page_state(page, NR_ISOLATED_ANON + + page_is_file_cache(page)); + putback_lru_page(page); +@@ -732,6 +753,17 @@ int migrate_pages(struct list_head *from + } + } + } ++ ++ if (rc != 0) ++ list_for_each_entry_safe(page, page2, from, lru) ++ if (PageMemError(page)) ++ /* ++ * The page failed to migrate. Clearing ++ * the error bit will allow another attempt ++ * to migrate if it gets another correctable ++ * error. ++ */ ++ ClearPageMemError(page); + rc = 0; + out: + if (!swapwrite) +@@ -744,6 +776,7 @@ out: + + return nr_failed + retry; + } ++EXPORT_SYMBOL(migrate_pages); + + #ifdef CONFIG_NUMA + /* +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -1091,6 +1091,7 @@ int isolate_lru_page(struct page *page) + } + return ret; + } ++EXPORT_SYMBOL(isolate_lru_page); + + /* + * Are there way too many processes in the direct reclaim path already? diff --git a/patches.arch/powernow-k8-add-core-performance-boost-support b/patches.arch/powernow-k8-add-core-performance-boost-support new file mode 100644 index 0000000..02774e1 --- /dev/null +++ b/patches.arch/powernow-k8-add-core-performance-boost-support @@ -0,0 +1,266 @@ +From: Borislav Petkov +Date: Wed, 31 Mar 2010 19:56:42 +0000 (+0200) +Subject: powernow-k8: Add core performance boost support +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-tip.git +Git-commit: 73860c6b2fd159a35637e233d735e36887c266ad +References: bnc#602209 +Patch-mainline: 2.6.35-rc1 + +powernow-k8: Add core performance boost support + +Starting with F10h, revE, AMD processors add support for a dynamic +core boosting feature called Core Performance Boost. When a specific +condition is present, a subset of the cores on a system are boosted +beyond their P0 operating frequency to speed up the performance of +single-threaded applications. + +In the normal case, the system comes out of reset with core boosting +enabled. This patch adds a sysfs knob with which core boosting can be +switched on or off for benchmarking purposes. + +While at it, make the CPB code hotplug-aware so that taking cores +offline wouldn't interfere with boosting the remaining online cores. +Furthermore, add cpu_online_mask hotplug protection as suggested by +Andrew. + +Finally, cleanup the driver init codepath and update copyrights. + +Signed-off-by: Borislav Petkov +LKML-Reference: <1270065406-1814-3-git-send-email-bp@amd64.org> +Reviewed-by: Thomas Renninger +Signed-off-by: H. Peter Anvin +Acked-by: Jeff Mahoney +--- + + arch/x86/kernel/cpu/cpufreq/powernow-k8.c | 161 ++++++++++++++++++++++++++++-- + arch/x86/kernel/cpu/cpufreq/powernow-k8.h | 2 + 2 files changed, 151 insertions(+), 12 deletions(-) + +--- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c ++++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +@@ -1,6 +1,5 @@ +- + /* +- * (c) 2003-2006 Advanced Micro Devices, Inc. ++ * (c) 2003-2010 Advanced Micro Devices, Inc. + * Your use of this code is subject to the terms and conditions of the + * GNU general public license version 2. See "COPYING" or + * http://www.gnu.org/licenses/gpl.html +@@ -54,6 +53,10 @@ static DEFINE_PER_CPU(struct powernow_k8 + + static int cpu_family = CPU_OPTERON; + ++/* core performance boost */ ++static bool cpb_capable, cpb_enabled; ++static struct msr *msrs; ++ + #ifndef CONFIG_SMP + static inline const struct cpumask *cpu_core_mask(int cpu) + { +@@ -1394,8 +1397,77 @@ out: + return khz; + } + ++static void _cpb_toggle_msrs(bool t) ++{ ++ int cpu; ++ ++ get_online_cpus(); ++ ++ rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs); ++ ++ for_each_cpu(cpu, cpu_online_mask) { ++ struct msr *reg = per_cpu_ptr(msrs, cpu); ++ if (t) ++ reg->l &= ~BIT(25); ++ else ++ reg->l |= BIT(25); ++ } ++ wrmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs); ++ ++ put_online_cpus(); ++} ++ ++/* ++ * Switch on/off core performance boosting. ++ * ++ * 0=disable ++ * 1=enable. ++ */ ++static void cpb_toggle(bool t) ++{ ++ if (!cpb_capable) ++ return; ++ ++ if (t && !cpb_enabled) { ++ cpb_enabled = true; ++ _cpb_toggle_msrs(t); ++ printk(KERN_INFO PFX "Core Boosting enabled.\n"); ++ } else if (!t && cpb_enabled) { ++ cpb_enabled = false; ++ _cpb_toggle_msrs(t); ++ printk(KERN_INFO PFX "Core Boosting disabled.\n"); ++ } ++} ++ ++static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf, ++ size_t count) ++{ ++ int ret = -EINVAL; ++ unsigned long val = 0; ++ ++ ret = strict_strtoul(buf, 10, &val); ++ if (!ret && (val == 0 || val == 1) && cpb_capable) ++ cpb_toggle(val); ++ else ++ return -EINVAL; ++ ++ return count; ++} ++ ++static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf) ++{ ++ return sprintf(buf, "%u\n", cpb_enabled); ++} ++ ++#define define_one_rw(_name) \ ++static struct freq_attr _name = \ ++__ATTR(_name, 0644, show_##_name, store_##_name) ++ ++define_one_rw(cpb); ++ + static struct freq_attr *powernow_k8_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, ++ &cpb, + NULL, + }; + +@@ -1411,10 +1483,51 @@ static struct cpufreq_driver cpufreq_amd + .attr = powernow_k8_attr, + }; + ++/* ++ * Clear the boost-disable flag on the CPU_DOWN path so that this cpu ++ * cannot block the remaining ones from boosting. On the CPU_UP path we ++ * simply keep the boost-disable flag in sync with the current global ++ * state. ++ */ ++static int __cpuinit cpb_notify(struct notifier_block *nb, unsigned long action, ++ void *hcpu) ++{ ++ unsigned cpu = (long)hcpu; ++ u32 lo, hi; ++ ++ switch (action) { ++ case CPU_UP_PREPARE: ++ case CPU_UP_PREPARE_FROZEN: ++ ++ if (!cpb_enabled) { ++ rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi); ++ lo |= BIT(25); ++ wrmsr_on_cpu(cpu, MSR_K7_HWCR, lo, hi); ++ } ++ break; ++ ++ case CPU_DOWN_PREPARE: ++ case CPU_DOWN_PREPARE_FROZEN: ++ rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi); ++ lo &= ~BIT(25); ++ wrmsr_on_cpu(cpu, MSR_K7_HWCR, lo, hi); ++ break; ++ ++ default: ++ break; ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block __cpuinitdata cpb_nb = { ++ .notifier_call = cpb_notify, ++}; ++ + /* driver entry point for init */ + static int __cpuinit powernowk8_init(void) + { +- unsigned int i, supported_cpus = 0; ++ unsigned int i, supported_cpus = 0, cpu; + + for_each_online_cpu(i) { + int rc; +@@ -1423,15 +1536,36 @@ static int __cpuinit powernowk8_init(voi + supported_cpus++; + } + +- if (supported_cpus == num_online_cpus()) { +- printk(KERN_INFO PFX "Found %d %s " +- "processors (%d cpu cores) (" VERSION ")\n", +- num_online_nodes(), +- boot_cpu_data.x86_model_id, supported_cpus); +- return cpufreq_register_driver(&cpufreq_amd64_driver); ++ if (supported_cpus != num_online_cpus()) ++ return -ENODEV; ++ ++ printk(KERN_INFO PFX "Found %d %s (%d cpu cores) (" VERSION ")\n", ++ num_online_nodes(), boot_cpu_data.x86_model_id, supported_cpus); ++ ++ if (boot_cpu_has(X86_FEATURE_CPB)) { ++ ++ cpb_capable = true; ++ ++ register_cpu_notifier(&cpb_nb); ++ ++ msrs = msrs_alloc(); ++ if (!msrs) { ++ printk(KERN_ERR "%s: Error allocating msrs!\n", __func__); ++ return -ENOMEM; ++ } ++ ++ rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs); ++ ++ for_each_cpu(cpu, cpu_online_mask) { ++ struct msr *reg = per_cpu_ptr(msrs, cpu); ++ cpb_enabled |= !(!!(reg->l & BIT(25))); ++ } ++ ++ printk(KERN_INFO PFX "Core Performance Boosting: %s.\n", ++ (cpb_enabled ? "on" : "off")); + } + +- return -ENODEV; ++ return cpufreq_register_driver(&cpufreq_amd64_driver); + } + + /* driver entry point for term */ +@@ -1439,6 +1573,13 @@ static void __exit powernowk8_exit(void) + { + dprintk("exit\n"); + ++ if (boot_cpu_has(X86_FEATURE_CPB)) { ++ msrs_free(msrs); ++ msrs = NULL; ++ ++ unregister_cpu_notifier(&cpb_nb); ++ } ++ + cpufreq_unregister_driver(&cpufreq_amd64_driver); + } + +--- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h ++++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h +@@ -5,7 +5,6 @@ + * http://www.gnu.org/licenses/gpl.html + */ + +- + enum pstate { + HW_PSTATE_INVALID = 0xff, + HW_PSTATE_0 = 0, +@@ -55,7 +54,6 @@ struct powernow_k8_data { + struct cpumask *available_cores; + }; + +- + /* processor's cpuid instruction support */ + #define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */ + #define CPUID_XFAM 0x0ff00000 /* extended family */ diff --git a/patches.arch/ppc-ipic-suspend-without-83xx-fix b/patches.arch/ppc-ipic-suspend-without-83xx-fix new file mode 100644 index 0000000..611e6ee --- /dev/null +++ b/patches.arch/ppc-ipic-suspend-without-83xx-fix @@ -0,0 +1,33 @@ +From: Takashi Iwai +Subject: [PATCH] Fix build_error without CONFIG_PPC_83xx +Patch-mainline: +References: + +fsl_deep_sleep() is defined only with CONFIG_PPC_83xx although +CONFIG_IPIC is set for CONFIG_PPC_MPC512x, too. + +Signed-off-by: Takashi Iwai + +--- +--- + arch/powerpc/sysdev/ipic.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/powerpc/sysdev/ipic.c ++++ b/arch/powerpc/sysdev/ipic.c +@@ -921,6 +921,7 @@ static int ipic_suspend(struct sys_devic + ipic_saved_state.sermr = ipic_read(ipic->regs, IPIC_SERMR); + ipic_saved_state.sercr = ipic_read(ipic->regs, IPIC_SERCR); + ++#ifdef CONFIG_PPC_83xx + if (fsl_deep_sleep()) { + /* In deep sleep, make sure there can be no + * pending interrupts, as this can cause +@@ -931,6 +932,7 @@ static int ipic_suspend(struct sys_devic + ipic_write(ipic->regs, IPIC_SEMSR, 0); + ipic_write(ipic->regs, IPIC_SERMR, 0); + } ++#endif + + return 0; + } diff --git a/patches.arch/ppc-pegasos-console-autodetection.patch b/patches.arch/ppc-pegasos-console-autodetection.patch new file mode 100644 index 0000000..d26df80 --- /dev/null +++ b/patches.arch/ppc-pegasos-console-autodetection.patch @@ -0,0 +1,19 @@ +From: olh@suse.de +Subject: force speed to fix autodetection on pegasos2 +Patch-mainline: never + +--- + arch/powerpc/platforms/chrp/setup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/powerpc/platforms/chrp/setup.c ++++ b/arch/powerpc/platforms/chrp/setup.c +@@ -294,7 +294,7 @@ static void chrp_init_early(void) + if (!property) + goto out_put; + if (!strcmp(property, "failsafe") || !strcmp(property, "serial")) +- add_preferred_console("ttyS", 0, NULL); ++ add_preferred_console("ttyS", 0, "115200"); + out_put: + of_node_put(node); + } diff --git a/patches.arch/ppc-prom-nodisplay.patch b/patches.arch/ppc-prom-nodisplay.patch new file mode 100644 index 0000000..34a7379 --- /dev/null +++ b/patches.arch/ppc-prom-nodisplay.patch @@ -0,0 +1,77 @@ +From: Olaf Hering +Subject: new prom=nodisplay option to avoid crash in firmware on B50 +Patch-mainline: not yet + +add prom=nodisplay +avoid crash in firmware on IBM B50 when OF stdout is on serial. + + 0 > boot scsi/sd@4:1,yaboot | +yaboot starting: loaded at 00200000 00222530 (0/0/00c1a078; sp: 00efffd0) +brokenfirmware did not claim executable memory, fixed it myself +Config file 'yaboot.cnf' read, 213 bytes + +Welcome to yaboot version 10.1.22-r945.SuSE +booted from '/pci@80000000/scsi@10/sd@4:1,yaboot' +Enter "help" to get some basic usage information +boot: +* linux +boot: linux 3 +Please wait, loading kernel... +Allocated 00600000 bytes for executable @ 02000000 + Elf32 kernel loaded... +Loading ramdisk... +ramdisk loaded 0030e057 @ 04100000 +OF stdout device is: /pci@80000000/isa@b/serial@i3f8 +command line: root=/dev/system/root xmon=on sysrq=1 quiet panic=12 3 +memory layout at init: + memory_limit : 00000000 (16 MB aligned) + alloc_bottom : 0440f000 + alloc_top : 30000000 + alloc_top_hi : 40000000 + rmo_top : 30000000 + ram_top : 40000000 +Looking for displays +found display : /pci@80000000/display@16, opening ... +Unexpected Firmware Error: +DEFAULT CATCH!, code=fff00300 at %SRR0: 00c18ccc %SRR1: 00003030 + ok + 0 > reset-all + + +--- + arch/powerpc/kernel/prom_init.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/arch/powerpc/kernel/prom_init.c ++++ b/arch/powerpc/kernel/prom_init.c +@@ -169,6 +169,7 @@ static unsigned long __initdata dt_strin + + static unsigned long __initdata prom_initrd_start, prom_initrd_end; + ++static int __initdata prom_no_display; + #ifdef CONFIG_PPC64 + static int __initdata prom_iommu_force_on; + static int __initdata prom_iommu_off; +@@ -570,6 +571,14 @@ static void __init early_cmdline_parse(v + #endif /* CONFIG_CMDLINE */ + prom_printf("command line: %s\n", RELOC(prom_cmd_line)); + ++ opt = strstr(RELOC(prom_cmd_line), RELOC("prom=")); ++ if (opt) { ++ opt += 5; ++ while (*opt && *opt == ' ') ++ opt++; ++ if (!strncmp(opt, RELOC("nodisplay"), 9)) ++ RELOC(prom_no_display) = 1; ++ } + #ifdef CONFIG_PPC64 + opt = strstr(RELOC(prom_cmd_line), RELOC("iommu=")); + if (opt) { +@@ -2546,6 +2555,7 @@ unsigned long __init prom_init(unsigned + /* + * Initialize display devices + */ ++ if (RELOC(prom_no_display) == 0) + prom_check_displays(); + + #ifdef CONFIG_PPC64 diff --git a/patches.arch/ppc-vio-modalias.patch b/patches.arch/ppc-vio-modalias.patch new file mode 100644 index 0000000..ef17b56 --- /dev/null +++ b/patches.arch/ppc-vio-modalias.patch @@ -0,0 +1,36 @@ +From: Olaf Hering +Subject: [PATCH] poweroc: vio modalias +Patch-mainline: not yet + +Acked-by: Olaf Hering +--- + arch/powerpc/kernel/vio.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/arch/powerpc/kernel/vio.c ++++ b/arch/powerpc/kernel/vio.c +@@ -1319,9 +1319,24 @@ static ssize_t devspec_show(struct devic + return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none"); + } + ++static ssize_t modalias_show (struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct device_node *of_node = dev->archdata.of_node; ++ const char *compat; ++ int i = 0; ++ ++ if (of_node) { ++ compat = of_get_property(of_node, "compatible", &i); ++ i = sprintf (buf, "vio:T%sS%s\n", of_node->type, compat); ++ } ++ return i; ++} ++ + static struct device_attribute vio_dev_attrs[] = { + __ATTR_RO(name), + __ATTR_RO(devspec), ++ __ATTR_RO(modalias), + __ATTR_NULL + }; + diff --git a/patches.arch/ppc-vmcoreinfo.diff b/patches.arch/ppc-vmcoreinfo.diff new file mode 100644 index 0000000..5709b52 --- /dev/null +++ b/patches.arch/ppc-vmcoreinfo.diff @@ -0,0 +1,42 @@ +Date: Thu, 9 Oct 2008 11:20:27 -0400 +From: Neil Horman +To: linux-kernel@vger.kernel.org, kexec@lists.infradead.org, + vgoyal@redhat.com, hbabu@us.ibm.com +Subject: [PATCH] add additional symbols to /sys/kernel/vmcoreinfo data for + ppc(64) +Cc: nhorman@tuxdriver.com +Patch-mainline: not yet + +Hey- + The makdumpdile dump filtering program, in some modes of operation needs +the node_data and/or contig_page_data symbols to function properly. These +symbols are missing from the powerpc kernel. This patch adds those symbols in +properly. Tested successfully by myself and the reporter. + +Regards +Neil + +Signed-off-by: Neil Horman +Acked-by: Bernhard Walle + + arch/powerpc/kernel/machine_kexec.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + + +--- a/arch/powerpc/kernel/machine_kexec.c ++++ b/arch/powerpc/kernel/machine_kexec.c +@@ -45,6 +45,14 @@ void machine_kexec_cleanup(struct kimage + ppc_md.machine_kexec_cleanup(image); + } + ++void arch_crash_save_vmcoreinfo(void) ++{ ++#ifdef CONFIG_NEED_MULTIPLE_NODES ++ VMCOREINFO_SYMBOL(node_data); ++ VMCOREINFO_LENGTH(node_data, MAX_NUMNODES); ++#endif ++} ++ + /* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. diff --git a/patches.arch/ppc64-xmon-dmesg-printing.patch b/patches.arch/ppc64-xmon-dmesg-printing.patch new file mode 100644 index 0000000..edf18cc --- /dev/null +++ b/patches.arch/ppc64-xmon-dmesg-printing.patch @@ -0,0 +1,132 @@ +Subject: [PATCH] add syslog printing to xmon debugger. +From: Linas Vepstas +Patch-mainline: Not yet + + +This patch 'dmesg'/printk log buffer printing to xmon. I find this +useful because crashes are almost always preceeded by interesting +printk's. This patch is simple & straightforward, except for one +possibly controversial aspect: it embeds a small snippet in +kernel/printk.c to return the location of the syslog. This is +needed because kallsyms and even CONFIG_KALLSYMS_ALL is not enough +to reveal the location of log_buf. This code is about 90% +cut-n-paste of earlier code from Keith Owens. + +Signed-off-by: Olaf Hering +--- + + arch/powerpc/xmon/xmon.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++ + kernel/printk.c | 15 ++++++++++++ + 2 files changed, 72 insertions(+) + +--- a/arch/powerpc/xmon/xmon.c ++++ b/arch/powerpc/xmon/xmon.c +@@ -138,6 +138,7 @@ static struct bpt *in_breakpoint_table(u + static int do_step(struct pt_regs *); + static void bpt_cmds(void); + static void cacheflush(void); ++static void xmon_show_dmesg(void); + static int cpu_cmd(void); + static void csum(void); + static void bootcmds(void); +@@ -194,6 +195,7 @@ Commands:\n\ + #endif + "\ + C checksum\n\ ++ D show dmesg (printk) buffer\n\ + d dump bytes\n\ + di dump instructions\n\ + df dump float values\n\ +@@ -828,6 +830,9 @@ cmds(struct pt_regs *excp) + case 'd': + dump(); + break; ++ case 'D': ++ xmon_show_dmesg(); ++ break; + case 'l': + symbol_lookup(); + break; +@@ -2599,6 +2604,58 @@ static void xmon_print_symbol(unsigned l + printf("%s", after); + } + ++extern void debugger_syslog_data(char *syslog_data[4]); ++#define SYSLOG_WRAP(p) if (p < syslog_data[0]) p = syslog_data[1]-1; \ ++ else if (p >= syslog_data[1]) p = syslog_data[0]; ++ ++static void xmon_show_dmesg(void) ++{ ++ char *syslog_data[4], *start, *end, c; ++ int logsize; ++ ++ /* syslog_data[0,1] physical start, end+1. ++ * syslog_data[2,3] logical start, end+1. ++ */ ++ debugger_syslog_data(syslog_data); ++ if (syslog_data[2] == syslog_data[3]) ++ return; ++ logsize = syslog_data[1] - syslog_data[0]; ++ start = syslog_data[0] + (syslog_data[2] - syslog_data[0]) % logsize; ++ end = syslog_data[0] + (syslog_data[3] - syslog_data[0]) % logsize; ++ ++ /* Do a line at a time (max 200 chars) to reduce overhead */ ++ c = '\0'; ++ while(1) { ++ char *p; ++ int chars = 0; ++ if (!*start) { ++ while (!*start) { ++ ++start; ++ SYSLOG_WRAP(start); ++ if (start == end) ++ break; ++ } ++ if (start == end) ++ break; ++ } ++ p = start; ++ while (*start && chars < 200) { ++ c = *start; ++ ++chars; ++ ++start; ++ SYSLOG_WRAP(start); ++ if (start == end || c == '\n') ++ break; ++ } ++ if (chars) ++ printf("%.*s", chars, p); ++ if (start == end) ++ break; ++ } ++ if (c != '\n') ++ printf("\n"); ++} ++ + #ifdef CONFIG_PPC_BOOK3S_64 + static void dump_slb(void) + { +--- a/kernel/printk.c ++++ b/kernel/printk.c +@@ -413,6 +413,21 @@ SYSCALL_DEFINE3(syslog, int, type, char + return do_syslog(type, buf, len, SYSLOG_FROM_CALL); + } + ++#ifdef CONFIG_DEBUG_KERNEL ++/* Its very handy to be able to view the syslog buffer during debug. ++ * But do_syslog() uses locks so it cannot be used during debugging. ++ * Instead, provide the start and end of the physical and logical logs. ++ * This is equivalent to do_syslog(3). ++ */ ++void debugger_syslog_data(char *syslog_data[4]) ++{ ++ syslog_data[0] = log_buf; ++ syslog_data[1] = log_buf + log_buf_len; ++ syslog_data[2] = log_buf + log_end - (logged_chars < log_buf_len ? logged_chars : log_buf_len); ++ syslog_data[3] = log_buf + log_end; ++} ++#endif /* CONFIG_DEBUG_KERNEL */ ++ + /* + * Call the console drivers on a range of log_buf + */ diff --git a/patches.arch/s390-add-FREE_PTE_NR b/patches.arch/s390-add-FREE_PTE_NR new file mode 100644 index 0000000..e9f66e2 --- /dev/null +++ b/patches.arch/s390-add-FREE_PTE_NR @@ -0,0 +1,43 @@ +From: Jeff Mahoney +Subject: [PATCH] s390: Define FREE_PTE_NR +Patch-mainline: Never, unless FREE_PTE_NR is used in generic code + + Commit ba8a9229ab9e80278c28ad68b15053f65b2b0a7c from + Martin Schwidefsky removed the + #include from asm-s390/tlb.h when he defined the + s390-specific TLB operations. + + FREE_PTR_NR is generally an internal-only value, but our unmap_vmas-lat + patch uses it to make smarter decisions about dumping PTEs in chunks. + + This patch restores the generic value in asm-s390/tlb.h. Since it's only + used for an optimization, this should be safe. + +Signed-off-by: Jeff Mahoney + +--- + arch/s390/include/asm/tlb.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/arch/s390/include/asm/tlb.h ++++ b/arch/s390/include/asm/tlb.h +@@ -34,6 +34,19 @@ + #define TLB_NR_PTRS 508 + #endif + ++/* Lifted from asm-generic/tlb.h; Is used by patches.suse/unmap_vmas-lat */ ++/* ++ * For UP we don't need to worry about TLB flush ++ * and page free order so much.. ++ */ ++#ifdef CONFIG_SMP ++ #define FREE_PTE_NR 506 ++ #define tlb_fast_mode(tlb) ((tlb)->nr == ~0U) ++#else ++ #define FREE_PTE_NR 1 ++ #define tlb_fast_mode(tlb) 1 ++#endif ++ + struct mmu_gather { + struct mm_struct *mm; + unsigned int fullmm; diff --git a/patches.arch/s390-message-catalog.diff b/patches.arch/s390-message-catalog.diff new file mode 100644 index 0000000..d83df1a --- /dev/null +++ b/patches.arch/s390-message-catalog.diff @@ -0,0 +1,8629 @@ +From: Gerald Schaefer +Subject: Kernel message catalog. +Patch-mainline: Probably never +References: bnc#549193,FATE#306999,LTC#57210 +Patch-mainline: not yet + +Description: Add support for automatic message tags to the printk macro + families dev_xyz and pr_xyz. The message tag consists of a + component name and a 24 bit hash of the message text. For + each message that is documented in the included kernel message + catalog a man page can be created with a script (which is + included in the patch). The generated man pages contain + explanatory text that is intended to help understand the + messages. + + Note that only s390 specific messages are prepared + appropriately and included in the generated message catalog. + + This patch is optional as it is very unlikely to be accepted + in upstream kernel, but is recommended for all distributions + which are built based on the 'Development stream' + +Acked-by: John Jolly +--- + + Documentation/kmsg/s390/aes_s390 | 30 + Documentation/kmsg/s390/af_iucv | 33 + Documentation/kmsg/s390/ap | 47 + Documentation/kmsg/s390/appldata | 88 + + Documentation/kmsg/s390/cio | 145 ++ + Documentation/kmsg/s390/claw | 731 +++++++++++++ + Documentation/kmsg/s390/cpcmd | 17 + Documentation/kmsg/s390/cpu | 69 + + Documentation/kmsg/s390/ctcm | 199 +++ + Documentation/kmsg/s390/dasd | 466 ++++++++ + Documentation/kmsg/s390/dasd-diag | 118 ++ + Documentation/kmsg/s390/dasd-eckd | 1901 ++++++++++++++++++++++++++++++++++++ + Documentation/kmsg/s390/dasd-fba | 30 + Documentation/kmsg/s390/dcssblk | 192 +++ + Documentation/kmsg/s390/extmem | 290 +++++ + Documentation/kmsg/s390/hvc_iucv | 122 ++ + Documentation/kmsg/s390/hypfs | 56 + + Documentation/kmsg/s390/iucv | 33 + Documentation/kmsg/s390/lcs | 161 +++ + Documentation/kmsg/s390/monreader | 127 ++ + Documentation/kmsg/s390/monwriter | 16 + Documentation/kmsg/s390/netiucv | 139 ++ + Documentation/kmsg/s390/qeth | 606 +++++++++++ + Documentation/kmsg/s390/s390dbf | 83 + + Documentation/kmsg/s390/sclp_cmd | 16 + Documentation/kmsg/s390/sclp_config | 3 + Documentation/kmsg/s390/sclp_cpi | 2 + Documentation/kmsg/s390/sclp_sdias | 4 + Documentation/kmsg/s390/setup | 181 +++ + Documentation/kmsg/s390/tape | 104 + + Documentation/kmsg/s390/tape_34xx | 418 +++++++ + Documentation/kmsg/s390/tape_3590 | 184 +++ + Documentation/kmsg/s390/time | 36 + Documentation/kmsg/s390/vmcp | 13 + Documentation/kmsg/s390/vmlogrdr | 18 + Documentation/kmsg/s390/vmur | 47 + Documentation/kmsg/s390/vmwatchdog | 26 + Documentation/kmsg/s390/xpram | 73 + + Documentation/kmsg/s390/zdump | 12 + Documentation/kmsg/s390/zfcp | 865 ++++++++++++++++ + Makefile | 16 + arch/s390/Kconfig | 8 + include/linux/device.h | 34 + include/linux/kernel.h | 35 + kernel/printk.c | 45 + scripts/Makefile.build | 14 + scripts/kmsg-doc | 479 +++++++++ + 47 files changed, 8317 insertions(+), 15 deletions(-) + +--- /dev/null ++++ b/Documentation/kmsg/s390/aes_s390 +@@ -0,0 +1,30 @@ ++/*? ++ * Text: "Allocating AES fallback algorithm %s failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: algorithm name ++ * Description: ++ * The advanced encryption standard (AES) algorithm includes three modes with ++ * 128-bit, 192-bit, and 256-bit keys. Your hardware system only provides ++ * hardware acceleration for the 128-bit mode. The aes_s390 module failed to ++ * allocate a software fallback for the AES modes that are not supported by the ++ * hardware. A possible reason for this problem is that the aes_generic module ++ * that provides the fallback algorithms is not available. ++ * User action: ++ * Use the 128-bit mode only or ensure that the aes_generic module is available ++ * and loaded and reload the aes_s390 module. ++ */ ++ ++/*? ++ * Text: "AES hardware acceleration is only available for 128-bit keys\n" ++ * Severity: Informational ++ * Description: ++ * The advanced encryption standard (AES) algorithm includes three modes with ++ * 128-bit, 192-bit, and 256-bit keys. Your hardware system only provides ++ * hardware acceleration for the 128-bit key mode. The aes_s390 module ++ * will use the less performant software fallback algorithm for the 192-bit ++ * and 256-bit key modes. ++ * User action: ++ * None. ++ */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/af_iucv +@@ -0,0 +1,33 @@ ++/*? ++ * Text: "Application %s on z/VM guest %s exceeds message limit\n" ++ * Severity: Error ++ * Parameter: ++ * @1: application name ++ * @2: z/VM user ID ++ * Description: ++ * Messages or packets destined for the application have accumulated and ++ * reached the maximum value. The default for the message limit is 65535. ++ * You can specify a different limit as the value for MSGLIMIT within ++ * the IUCV statement of the z/VM virtual machine on which the application ++ * runs. ++ * User action: ++ * Ensure that you do not send data faster than the application retrieves ++ * them. Ensure that the message limit on the z/VM guest virtual machine ++ * on which the application runs is high enough. ++ */ ++ ++/*? ++ * Text: "The af_iucv module cannot be loaded without z/VM\n" ++ * Severity: Error ++ * Description: ++ * The AF_IUCV protocol connects socket applications running in Linux ++ * kernels on different z/VM virtual machines, or it connects a Linux ++ * application to another sockets application running in a z/VM virtual ++ * machine. On Linux instances that run in environments other than the ++ * z/VM hypervisor, the AF_IUCV protocol does not provide any useful ++ * function and the corresponding af_iucv module cannot be loaded. ++ * User action: ++ * Load the af_iucv module only on Linux instances that run as guest ++ * operating systems of the z/VM hypervisor. If the module has been ++ * compiled into the kernel, ignore this message. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/ap +@@ -0,0 +1,47 @@ ++/*? ++ * Text: "%d is not a valid cryptographic domain\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: AP domain index ++ * Description: ++ * The cryptographic domain specified for the 'domain=' module or kernel ++ * parameter must be an integer in the range 0 to 15. ++ * User action: ++ * Reload the cryptographic device driver with a correct module parameter. ++ * If the device driver has been compiled into the kernel, correct the value ++ * in the kernel parameter line and reboot Linux. ++ */ ++ ++/*? ++ * Text: "The hardware system does not support AP instructions\n" ++ * Severity: Warning ++ * Description: ++ * The ap module addresses AP adapters through AP instructions. The hardware ++ * system on which the Linux instance runs does not support AP instructions. ++ * The ap module cannot detect any AP adapters. ++ * User action: ++ * Load the ap module only if your Linux instance runs on hardware that ++ * supports AP instructions. If the ap module has been compiled into the kernel, ++ * ignore this message. ++ */ ++ ++/*? ++ * Text: "Registering adapter interrupts for AP %d failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: AP device ID ++ * Description: ++ * The hardware system supports AP adapter interrupts but failed to enable ++ * an adapter for interrupts. Possible causes for this error are: ++ * i) The AP adapter firmware does not support AP interrupts. ++ * ii) An AP adapter firmware update to a firmware level that supports AP ++ * adapter interrupts failed. ++ * iii) The AP adapter firmware has been successfully updated to a level that ++ * supports AP interrupts but the new firmware has not been activated. ++ * User action: ++ * Ensure that the firmware on your AP adapters support AP interrupts and that ++ * any firmware updates have completed successfully. If necessary, deconfigure ++ * your cryptographic adapters and reconfigure them to ensure that any firmware ++ * updates become active, then reload the ap module. If the ap module has been ++ * compiled into the kernel, reboot Linux. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/appldata +@@ -0,0 +1,88 @@ ++/*? ++ * Text: "Starting the data collection for %s failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: appldata module ++ * @2: return code ++ * Description: ++ * The specified data collection module used the z/VM diagnose call ++ * DIAG 0xDC to start writing data. z/VM returned an error and the data ++ * collection could not start. If the return code is 5, your z/VM guest ++ * virtual machine is not authorized to write data records. ++ * User action: ++ * If the return code is 5, ensure that your z/VM guest virtual machine's ++ * entry in the z/VM directory includes the OPTION APPLMON statement. ++ * For other return codes see the section about DIAGNOSE Code X'DC' ++ * in "z/VM CP Programming Services". ++ */ ++ ++/*? ++ * Text: "Stopping the data collection for %s failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: appldata module ++ * @2: return code ++ * Description: ++ * The specified data collection module used the z/VM diagnose call DIAG 0xDC ++ * to stop writing data. z/VM returned an error and the data collection ++ * continues. ++ * User action: ++ * See the section about DIAGNOSE Code X'DC' in "z/VM CP Programming Services". ++ */ ++ ++/*? ++ * Text: "Starting a new OS data collection failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: return code ++ * Description: ++ * After a CPU hotplug event, the record size for the running operating ++ * system data collection is no longer correct. The appldata_os module tried ++ * to start a new data collection with the correct record size but received ++ * an error from the z/VM diagnose call DIAG 0xDC. Any data collected with ++ * the current record size might be faulty. ++ * User action: ++ * Start a new data collection with the cappldata_os module. For information ++ * about starting data collections see "Device Drivers, Features, and ++ * Commands". For information about the return codes see the section about ++ * DIAGNOSE Code X'DC' in "z/VM CP Programming Services". ++ */ ++ ++/*? ++ * Text: "Stopping a faulty OS data collection failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: return code ++ * Description: ++ * After a CPU hotplug event, the record size for the running operating ++ * system data collection is no longer correct. The appldata_os module tried ++ * to stop the faulty data collection but received an error from the z/VM ++ * diagnose call DIAG 0xDC. Any data collected with the current record size ++ * might be faulty. ++ * User action: ++ * Try to restart appldata_os monitoring. For information about stopping ++ * and starting data collections see "Device Drivers, Features, and ++ * Commands". For information about the return codes see the section about ++ * DIAGNOSE Code X'DC' in "z/VM CP Programming Services". ++ */ ++ ++/*? ++ * Text: "Maximum OS record size %i exceeds the maximum record size %i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: no of bytes ++ * @2: no of bytes ++ * Description: ++ * The OS record size grows with the number of CPUs and is adjusted by the ++ * appldata_os module in response to CPU hotplug events. For more than 110 ++ * CPUs the record size would exceed the maximum record size of 4024 bytes ++ * that is supported by the z/VM hypervisor. To prevent the maximum supported ++ * record size from being exceeded while data collection is in progress, ++ * you cannot load the appldata_os module on Linux instances that are ++ * configured for a maximum of more than 110 CPUs. ++ * User action: ++ * If you do not want to collect operating system data, you can ignore this ++ * message. If you want to collect operating system data, reconfigure your ++ * Linux instance to support less than 110 CPUs. ++ */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/cio +@@ -0,0 +1,145 @@ ++/*? ++ * Text: "%s is not a valid device for the cio_ignore kernel parameter\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: device bus-ID ++ * Description: ++ * The device specification for the cio_ignore kernel parameter is ++ * syntactically incorrect or specifies an unknown device. This device is not ++ * excluded from being sensed and analyzed. ++ * User action: ++ * Correct your device specification in the kernel parameter line to have the ++ * device excluded when you next reboot Linux. You can write the correct ++ * device specification to /proc/cio_ignore to add the device to the list of ++ * devices to be excluded. This does not immediately make the device ++ * inaccessible but the device is ignored if it disappears and later reappears. ++ */ ++ ++/*? ++ * Text: "0.%x.%04x to 0.%x.%04x is not a valid range for cio_ignore\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: from subchannel set ID ++ * @2: from device number ++ * @3: to subchannel set ID ++ * @4: to device number ++ * Description: ++ * The device range specified for the cio_ignore kernel parameter is ++ * syntactically incorrect. No devices specified with this range are ++ * excluded from being sensed and analyzed. ++ * User action: ++ * Correct your range specification in the kernel parameter line to have the ++ * range of devices excluded when you next reboot Linux. You can write the ++ * correct range specification to /proc/cio_ignore to add the range of devices ++ * to the list of devices to be excluded. This does not immediately make the ++ * devices in the range inaccessible but any of these devices are ignored if ++ * they disappear and later reappear. ++ */ ++ ++/*? ++ * Text: "Processing %s for channel path %x.%02x\n" ++ * Severity: Notice ++ * Parameter: ++ * @1: configuration change ++ * @2: channel subsystem ID ++ * @3: CHPID ++ * Description: ++ * A configuration change is in progress for the given channel path. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "No CCW console was found\n" ++ * Severity: Warning ++ * Description: ++ * Linux did not find the expected CCW console and tries to use an alternative ++ * console. A possible reason why the console was not found is that the console ++ * has been specified in the cio_ignore list. ++ * User action: ++ * None, if an appropriate alternative console has been found, and you want ++ * to use this alternative console. If you want to use the CCW console, ensure ++ * that is not specified in the cio_ignore list, explicitly specify the console ++ * with the 'condev=' kernel parameter, and reboot Linux. ++ */ ++ ++/*? ++ * Text: "Channel measurement facility initialized using format %s (mode %s)\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: format ++ * @2: mode ++ * Description: ++ * The channel measurement facility has been initialized successfully. ++ * Format 'extended' should be used for z990 and later mainframe systems. ++ * Format 'basic' is intended for earlier mainframes. Mode 'autodetected' means ++ * that the format has been set automatically. Mode 'parameter' means that the ++ * format has been set according to the 'format=' kernel parameter. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The CSS device driver initialization failed with errno=%d\n" ++ * Severity: Alert ++ * Parameter: ++ * @1: Return code ++ * Description: ++ * The channel subsystem bus could not be established. ++ * User action: ++ * See the errno man page to find out what caused the problem. ++ */ ++ /*? Text: "%s: Got subchannel machine check but no sch_event handler provided.\n" */ ++ ++/*? ++ * Text: "%s: Setting the device online failed because it is boxed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: Device bus-ID ++ * Description: ++ * Initialization of a device did not complete because it did not respond in ++ * time or it was reserved by another operating system. ++ * User action: ++ * Make sure that the device is working correctly, then try again to set it ++ * online. For devices that support the reserve/release mechanism (for example ++ * DASDs), you can try to override the reservation of the other system by ++ * writing 'force' to the 'online' sysfs attribute of the affected device. ++ */ ++ ++/*? ++ * Text: "%s: Setting the device online failed because it is not operational\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: Device bus-ID ++ * Description: ++ * Initialization of a device did not complete because it is not present or ++ * not operational. ++ * User action: ++ * Make sure that the device is present and working correctly, then try again ++ * to set it online. ++ */ ++ ++/*? ++ * Text: "%s: The device stopped operating while being set offline\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: Device bus-ID ++ * Description: ++ * While the device was set offline, it was not present or not operational. ++ * The device is now inactive, but setting it online again might fail. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The device entered boxed state while being set offline\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: Device bus-ID ++ * Description: ++ * While the device was set offline, it did not respond in time or it was ++ * reserved by another operating system. The device is now inactive, but ++ * setting it online again might fail. ++ * User action: ++ * None. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/claw +@@ -0,0 +1,731 @@ ++/*? ++ * Text: "%s: Creating the /proc files for a new CLAW device failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the failed CLAW device ++ * Description: ++ * For each Common Link Access to Workstation (CLAW) device the CLAW device ++ * driver maintains files in the proc file system. The CLAW device driver ++ * failed to create a new CLAW device because it could not create these /proc ++ * files for the new device. You cannot create CLAW devices for Linux kernels ++ * that do not include a proc file system. ++ * User action: ++ * Ensure that your Linux kernel provides a proc file system. Reboot Linux. ++ * If your kernel provides a proc file system and the problem persists, contact ++ * your support organization. ++ */ ++ ++/*? ++ * Text: "%s: An uninitialized CLAW device received an IRQ, c-%02x d-%02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: subchannel status ++ * @3: device status ++ * Description: ++ * A Common Link Access to Workstation (CLAW) device was not initialized when ++ * it received a channel interrupt (IRQ). The IRQ is ignored. This might be a ++ * temporary condition while the device comes online or is taken offline. ++ * User action: ++ * If this problem occurs frequently, use the status information from the ++ * message and the channel and device traces to analyze the problem. See ++ * "Principles of Operation" for details about of the status information. ++ */ ++ ++/*? ++ * Text: "%s: The device is not a CLAW device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the device ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver received a ++ * channel interrupt (IRQ) for a subchannel that is not a CLAW read or write ++ * subchannel. A CLAW subchannel must be configured for a 3088 device of ++ * type x'61' and have an even bus ID. ++ * User action: ++ * Assure that the subchannels have been defined correctly to the real or ++ * virtual hardware, for example, in your IOCDS or in your z/VM configuration. ++ */ ++ ++/*? ++ * Text: "%s: The CLAW device received an unexpected IRQ, c-%02x d-%02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: subchannel status ++ * @3: device status ++ * Description: ++ * A Common Link Access to Workstation (CLAW) device received a channel ++ * interrupt (IRQ) while it was in a state in which it cannot process IRQs. ++ * The IRQ is ignored. This might be a temporary condition. ++ * User action: ++ * If this problem occurs frequently, use the status information from the ++ * message and the channel and device traces to analyze the problem. See ++ * "Principles of Operation" for details about the status information. ++ */ ++ ++/*? ++ * Text: "%s: The CLAW device for %s received an unexpected IRQ\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * A Common Link Access to Workstation (CLAW) device received a channel ++ * interrupt (IRQ) while the CLAW device driver had assigned a status to the ++ * device in which it cannot process IRQs. The IRQ is ignored. ++ * User action: ++ * Restart the remote channel adapter. If the problem persists, use s390dbf ++ * traces and CCW traces to diagnose the problem. ++ */ ++ ++/*? ++ * Text: "%s: Deactivating %s completed with incorrect subchannel status (read %02x, write %02x)\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * @3: read subchannel status ++ * @4: write subchannel status ++ * Description: ++ * When the Common Link Access to Workstation (CLAW) device driver closes a ++ * CLAW device, the device driver frees all storage that is used for the ++ * device. A successful closing operation results in status DEVICE END and ++ * CHANNEL END for both the read and write subchannel. At least one of these ++ * statuses is missing for a subchannel. Data might have been lost and there ++ * might be problems when the network interface is activated again. ++ * User action: ++ * If the network interface cannot be activated, vary the subchannels for the ++ * device offline and back online, for example, with chchp. If this does not ++ * resolve the problem, reset the remote channel adapter. ++ */ ++ ++/*? ++ * Text: "%s: The remote channel adapter is not available\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * Description: ++ * During an operation, the Common Link Access to Workstation (CLAW) device ++ * driver received errno ENODEV from the common I/O layer. This means that ++ * the remote channel adapter was not operational or offline. ++ * User action: ++ * Check the remote channel adapter and, if necessary, restart it. ++ */ ++ ++/*? ++ * Text: "%s: The status of the remote channel adapter is not valid\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * Description: ++ * During an operation, the Common Link Access to Workstation (CLAW) device ++ * driver received errno EINVAL from the common I/O layer. This indicates ++ * that the remote channel adapter was offline or not operational. ++ * User action: ++ * Check for related error messages to find the cause of the problem. If ++ * necessary, restart the remote channel adapter. ++ */ ++ ++/*? ++ * Text: "%s: The common device layer returned error code %d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: errno ++ * Description: ++ * During an I/O operation, the Common Link Access to Workstation (CLAW) device ++ * driver received an errno from the common I/O layer. This indicates a problem ++ * with the remote channel adapter. ++ * User action: ++ * See the errno man page to find out what the error code means. Check for ++ * related messages. Restart the remote channel adapter. If the problem ++ * persists, examine the subchannel trace for further diagnostic information. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer of %s disconnected\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver received a device ++ * status word DEV_STAT_UNIT_CHECK and sense code 0x41. This indicates that the ++ * remote network interface is no longer available. ++ * User action: ++ * Ensure that the remote channel adapter is operational and activate the ++ * remote interface. For information about the sense code see ++ * /Documentation/s390/cds.txt in the Linux source tree. Search for 'SNS0' to ++ * locate the information. ++ */ ++ ++/*? ++ * Text: "%s: The remote channel adapter for %s has been reset\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver received a device ++ * status word DEV_STAT_UNIT_CHECK and sense code 0x40. This indicates that the ++ * remote channel adapter has been reset. ++ * User action: ++ * When the remote channel adapter is operational again, activate the remote ++ * interface. For information about the sense code see ++ * /Documentation/s390/cds.txt in the Linux source tree. Search for 'SNS0' to ++ * locate the information. ++ */ ++ ++/*? ++ * Text: "%s: A data streaming timeout occurred for %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver received a device ++ * status word DEV_STAT_UNIT_CHECK and sense code 0x24. This indicates a data ++ * streaming timeout. The remote channel adapter or the channel might be ++ * faulty. ++ * User action: ++ * Restart the remote channel adapter and activate the remote interface. If the ++ * problem persists, examine the subchannel trace for further diagnostic ++ * information. For information about the sense code see ++ * /Documentation/s390/cds.txt in the Linux source tree. Search for 'SNS0' to ++ * locate the information. ++ */ ++ ++/*? ++ * Text: "%s: A data transfer parity error occurred for %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @1: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver received a device ++ * status word DEV_STAT_UNIT_CHECK and sense code 0x20. This indicates a data ++ * parity error. The remote channel adapter or the channel might be faulty. ++ * User action: ++ * Ensure that all cables are securely plugged. Restart the remote channel ++ * adapter and activate the remote interface. If the problem persists, examine ++ * the subchannel trace for further diagnostic information. For information ++ * about the sense code see /Documentation/s390/cds.txt in the Linux source ++ * tree. Search for 'SNS0' to locate the information. ++ */ ++ ++/*? ++ * Text: "%s: The remote channel adapter for %s is faulty\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver received a device ++ * status word DEV_STAT_UNIT_CHECK and sense code 0x30. This indicates that the ++ * remote channel adapter is faulty. ++ * User action: ++ * Check and restart the remote channel adapter and activate the remote ++ * interface. If the problem persists, perform device diagnosis for the remote ++ * channel adapter and examine the subchannel trace for further diagnostic ++ * information. For information about the sense code see ++ * /Documentation/s390/cds.txt in the Linux source tree. Search for 'SNS0' to ++ * locate the information. ++ */ ++ ++/*? ++ * Text: "%s: A read data parity error occurred for %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver received a device ++ * status word DEV_STAT_UNIT_CHECK and sense code 0x10. This indicates a read ++ * data parity error. The remote channel adapter might be faulty. ++ * User action: ++ * Ensure that all cables are securely plugged. Check and restart the remote ++ * channel adapter and activate the remote interface. If the problem persists, ++ * perform device diagnosis for the remote channel adapter and examine the ++ * subchannel trace for further diagnostic information. For information about ++ * the sense code see /Documentation/s390/cds.txt in the Linux source tree. ++ * Search for 'SNS0' to locate the information. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer of %s uses an incorrect API version %d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * @3: CLAW API version ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver received a ++ * SYSTEM_VALIDATE_REQUEST packet from the remote channel adapter. The packet ++ * included an unexpected version ID for the CLAW API. The version ID must ++ * be '2' for all packets. ++ * User action: ++ * Ensure that the remote channel adapter is at the latest firmware level. ++ * Restart the remote channel adapter and activate the remote interface. If the ++ * problem persists, examine the subchannel trace for further diagnostic ++ * information. ++ */ ++ ++/*? ++ * Text: "%s: Host name %s for %s does not match the remote adapter name %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: host name in the local CLAW device settings ++ * @3: network interface name ++ * @4: adapter name in the remote CLAW device settings ++ * Description: ++ * The host name in the local Common Link Access to Workstation (CLAW) device ++ * settings must match the adapter name in the CLAW device settings of the ++ * communication peer. The CLAW device driver discovered a mismatch between ++ * these settings. The connection cannot be established. ++ * User action: ++ * Check the configuration of the CLAW device and of its communication peer. ++ * Correct the erroneous setting and restart the CLAW device, local or remote, ++ * for which you have made corrections. ++ */ ++ ++/*? ++ * Text: "%s: Adapter name %s for %s does not match the remote host name %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: adapter name in the local CLAW device settings ++ * @3: network interface name ++ * @4: host name in the remote CLAW device settings ++ * Description: ++ * The adapter name in the local Common Link Access to Workstation (CLAW) device ++ * settings must match the host name in the CLAW device settings of the ++ * communication peer. The CLAW device driver discovered a mismatch between ++ * these settings. The connection cannot be established. ++ * User action: ++ * Check the configuration of the CLAW device and of its communication peer. ++ * Correct the erroneous setting and restart the CLAW device, local or remote, ++ * for which you have made corrections. ++ */ ++ ++/*? ++ * Text: "%s: The local write buffer is smaller than the remote read buffer\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * Description: ++ * You set the buffer size for the local Common Link Access to Workstation ++ * (CLAW) device implicitly by setting the connection type. For connection ++ * type 'packed' the buffer size is 32 KB, for the other connection types the ++ * buffer size is 4 KB. The connection cannot be established because the ++ * write buffer size of the local CLAW device does not match the read buffer ++ * size of the communication peer. ++ * User action: ++ * Confirm that you are using the correct connection type for the local CLAW ++ * device. Ensure that the read buffer size of the remote CLAW device is set ++ * accordingly. Restart the CLAW device, local or remote, for which you have ++ * made corrections. ++ */ ++ ++/*? ++ * Text: "%s: The local read buffer is smaller than the remote write buffer\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * Description: ++ * You set the buffer size for the local Common Link Access to Workstation ++ * (CLAW) device implicitly by setting the connection type. For connection ++ * type 'packed' the buffer size is 32 KB, for the other connection types the ++ * buffer size is 4 KB. The connection cannot be established because the ++ * read buffer size of the local CLAW device does not match the write buffer ++ * size of the communication peer. ++ * User action: ++ * Confirm that you are using the correct connection type for the local CLAW ++ * device. Ensure that the write buffer size of the remote CLAW device is set ++ * accordingly. Restart the CLAW device, local or remote, for which you have ++ * made corrections. ++ */ ++ ++/*? ++ * Text: "%s: Settings for %s validated (version=%d, remote device=%d, rc=%d, adapter name=%.8s, host name=%.8s)\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * @3: CLAW API version ++ * @4: identifier for the remote CLAW device ++ * @5: return code received from the remote CLAW device ++ * @6: adapter name ++ * @7: host name ++ * Description: ++ * The settings of the local Common Link Access to Workstation (CLAW) device ++ * have been validated by the communication peer. The message summarizes the ++ * content of the response. If the return code is zero, the validation was ++ * successful and the connection is activated. ++ * User action: ++ * If the return code is not equal to zero, look for related warning messages. ++ */ ++ ++/*? ++ * Text: "%s: Validating %s failed because of a host or adapter name mismatch\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) network interface cannot be ++ * activated because there is a mismatch between a host name and the ++ * corresponding adapter name. The local host name must match the remote ++ * adapter name and the local adapter name must match the remote host name. ++ * User action: ++ * Correct the erroneous setting and restart the CLAW device, local or remote, ++ * for which you have made corrections. ++ */ ++ ++/*? ++ * Text: "%s: Validating %s failed because of a version conflict\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) network interface cannot be ++ * activated because the remote CLAW device does not support CLAW version 2. ++ * The CLAW device driver requires CLAW version 2. ++ * User action: ++ * Ensure that the remote channel adapter supports CLAW version 2 and that the ++ * remote CLAW device is configured for CLAW version 2. ++ */ ++ ++/*? ++ * Text: "%s: Validating %s failed because of a frame size conflict\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * You set the frame size for the local Common Link Access to Workstation ++ * (CLAW) device implicitly by setting the connection type. For connection ++ * type 'packed' the frame size is 32 KB, for the other connection types the ++ * frame size is 4 KB. The connection cannot be activated because the ++ * the frame size of the local CLAW device does not match the frame size of the ++ * communication peer. ++ * User action: ++ * Confirm that you are using the correct connection type for the local CLAW ++ * device. Ensure that the frame size of the remote CLAW device is set ++ * accordingly. Restart the CLAW device, local or remote, for which you have ++ * have made corrections. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer of %s rejected the connection\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The remote CLAW device rejected the connection because of a mismatch between ++ * the settings of the local CLAW device and the remote CLAW device. ++ * User action: ++ * Check the settings of both the local and the remote CLAW device and ensure ++ * that the settings are consistent. Restart the CLAW device, local or remote ++ * for which you have made the correction. ++ */ ++ ++/*? ++ * Text: "%s: %s rejected a connection request because it is already active\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device rejected a connection ++ * request by its communication peer because the connection is already active. ++ * The CLAW device driver only supports a single connection for each CLAW ++ * device. This might be a runtime problem. ++ * User action: ++ * None if there is an active connection. If no connection can be established, ++ * restart the remote channel adapter. ++ */ ++ ++/*? ++ * Text: "%s: %s rejected a request to open multiple connections\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device rejected a request by ++ * its communication peer to open more than one connection. The CLAW device ++ * driver only supports a single connection for each CLAW device. ++ * User action: ++ * Reconfigure the remote CLAW device to only use one connection. Restart the ++ * remote CLAW device. ++ */ ++ ++/*? ++ * Text: "%s: %s rejected a connection request because of a type mismatch\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @1: network interface name ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device rejected a request by ++ * its communication peer to open a connection. A connection can only be opened ++ * if the same connection type has been set for both the local and the remote ++ * CLAW device. ++ * User action: ++ * Ensure that the connection types for the local and remote CLAW device match. ++ * Restart the CLAW device, local or remote, for which you have changed the ++ * connection type. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer of %s rejected a connection request\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @1: network interface name ++ * Description: ++ * The remote CLAW device detected an inconsistency in the configurations of the ++ * local and the remote CLAW device and rejected a connection request. ++ * User action: ++ * Examine the settings of your local and remote CLAW device. Correct the ++ * erroneous setting and restart the CLAW device, local or remote, for which ++ * you have made corrections. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer of %s rejected a connection request because of a type mismatch\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The remote Common Link Access to Workstation (CLAW) device rejected a ++ * request to open a connection. A connection can only be opened if the same ++ * connection type has been set for both the local and the remote CLAW device. ++ * not be started. ++ * User action: ++ * Ensure that the connection types for the local and remote CLAW device match. ++ * Restart the CLAW device, local or remote, for which you have changed the ++ * connection type. ++ */ ++ ++/*? ++ * Text: "%s: Activating %s failed because of an incorrect link ID=%d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * @3: link ID returned from the remote CLAW device ++ * Description: ++ * The remote Common Link Access to Workstation (CLAW) device accepted a ++ * connection request but returned an incorrect link ID. The CLAW device driver ++ * only supports a single connection at a time (link ID=1) for each network ++ * interface. ++ * User action: ++ * Restart the remote CLAW device and try again to activate the network ++ * interface. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer of %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The remote Common Link Access to Workstation (CLAW) device reported an ++ * error condition that cannot be recovered automatically. ++ * User action: ++ * Restart the remote CLAW device. If this does not resolve the error, gather ++ * logs and traces from the remote CLAW device to obtain further ++ * diagnostic data. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer of %s sent an unknown command code\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * Description: ++ * The remote Common Link Access to Workstation (CLAW) device sent a command ++ * code that is not defined. This might indicate that the remote CLAW device is ++ * malfunctioning. The connection remains operational. ++ * User action: ++ * If this problem occurs frequently, restart the remote CLAW device. If this ++ * does not resolve the error, gather logs and traces from the remote CLAW ++ * device to obtain further diagnostic data. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer of %s sent a faulty frame of length %02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: network interface name ++ * @3: incorrect frame length value ++ * Description: ++ * The remote Common Link Access to Workstation (CLAW) device sent a frame ++ * with an incorrect value in the length field. This problem might result from ++ * data errors or incorrect packing. The connection remains operational. ++ * User action: ++ * If this problem occurs frequently, restart the remote CLAW device. If this ++ * does not resolve the error, gather logs and traces from the remote CLAW ++ * device to obtain further diagnostic data. ++ */ ++ ++/*? ++ * Text: "%s: Allocating a buffer for incoming data failed\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * Description: ++ * A Common Link Access to Workstation (CLAW) data packet was received but ++ * the CLAW device driver could not allocate a receive buffer. A possible cause ++ * of this problem is memory constraints. The data packet is dropped but the ++ * connection remains operational. ++ * User action: ++ * Ensure that sufficient memory is available. If this problem occurs ++ * frequently, restart the remote CLAW device. If this does not resolve the ++ * error, gather logs and traces from the remote CLAW device to obtain further ++ * diagnostic data. ++ */ ++ ++/*? ++ * Text: "%s: Creating a CLAW group device failed with error code %d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: errno ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver failed to create ++ * a CLAW group device. A possible cause of this problem is memory constraints. ++ * User action: ++ * Ensure that there is sufficient free memory. See the errno man page and look ++ * for related messages to find out what caused the problem. If you cannot ++ * resolve the problem, contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Setting the read subchannel online failed with error code %d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: errno ++ * Description: ++ * Setting the Common Link Access to Workstation (CLAW) device online failed ++ * with an error for the read subchannel. This problem occurs, for example, if ++ * the read subchannel used to create the CLAW group device is not defined as a ++ * CLAW read subchannel in the hardware definitions. The CLAW read subchannel ++ * must be for a 3088 device of type x'61' and have an even bus ID. The bus ID ++ * of the read subchannel matches the bus ID of the CLAW device. ++ * User action: ++ * Confirm that you are using the correct bus ID for the read subchannel. If ++ * necessary, ungroup the device and recreate it with the correct bus ID. ++ * Assure that the read subchannel has been defined correctly to the real or ++ * virtual hardware, for example, in your IOCDS or in your z/VM configuration. ++ * Assure that a valid number of read buffers has been assigned to the device. ++ * See 'Device Drivers, Features, and Commands' for details about the read ++ * buffers. See the errno man page for information about the error code. ++ */ ++ ++/*? ++ * Text: "%s: Setting the write subchannel online failed with error code %d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * @2: errno ++ * Description: ++ * Setting the Common Link Access to Workstation (CLAW) device online failed ++ * with an error for the write subchannel. This problem occurs, for example, if ++ * the write subchannel used to create the CLAW group device is not defined as a ++ * CLAW write subchannel in the hardware definitions. The CLAW write subchannel ++ * must be for a 3088 device of type x'61' and have an uneven bus ID. The ++ * bus ID of the write subchannel can be found from the symbolic link ++ * /sys/bus/ccwgroup/drivers/claw//cdev1 where ++ * is the bus ID of the CLAW device. ++ * User action: ++ * Confirm that you are using the correct bus ID for the write subchannel. If ++ * necessary, ungroup the device and recreate it with the correct bus ID. ++ * Assure that the write subchannel has been defined correctly to the real or ++ * virtual hardware, for example, in your IOCDS or in your z/VM configuration. ++ * Assure that a valid number of write buffers has been assigned to the device. ++ * See 'Device Drivers, Features, and Commands' for details about the read ++ * buffers. See the errno man page for information about the error code. ++ */ ++ ++/*? ++ * Text: "%s: Activating the CLAW device failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CLAW device ++ * Description: ++ * Activating the Common Link Access to Workstation (CLAW) device failed. A ++ * possible cause of this problem is memory constraints. ++ * User action: ++ * Free some memory and try again to activate the CLAW device. If the problem ++ * persists, contact your support organization. ++ */ ++ ++/*? ++ * Text: "Registering with the S/390 debug feature failed with error code %d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: errno ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver failed to register ++ * with the S/390 debug feature. No debug traces will be available for CLAW. ++ * User action: ++ * Enter 'lsmod | grep dbf' or an equivalent command to check if the S/390 debug ++ * feature loaded. If the output does not show the dbf module, the S/390 debug ++ * feature has not been loaded, unload the CLAW device driver, load the debug ++ * feature, then reload the CLAW device driver. See the errno man page for ++ * information about the error code. ++ */ ++ ++/*? ++ * Text: "Registering with the cu3088 device driver failed with error code %d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: errno ++ * Description: ++ * The Common Link Access to Workstation (CLAW) device driver failed to register ++ * with the cu3088 channel subsystem device driver. The CLAW device driver ++ * requires the cu3088 device driver. ++ * User action: ++ * Enter 'lsmod | grep cu3088' or an equivalent command to check if the cu3088 ++ * device driver is loaded. If the output does not show the cu3088 module, ++ * unload the CLAW device driver, load the cu3088 device driver, then reload ++ * the CLAW device driver. See the errno man page for information about the ++ * error code. ++ */ ++ ++/*? Text: "%s: %s: CLAW device %.8s: Received Control Packet\n" */ ++/*? Text: "%s: %s: CLAW device %.8s: System validate completed.\n" */ ++/*? Text: "%s: %s: CLAW device %.8s: Connection completed link_id=%d.\n" */ ++/*? Text: "%s: %s: remote side is not ready\n" */ ++/*? Text: "%s: %s: write connection restarting\n" */ ++/*? Text: "%s: %s: subchannel check for device: %04x - Sch Stat %02x Dev Stat %02x CPA - %04x\n" */ ++/*? Text: "%s: %s: Unit Exception occurred in write channel\n" */ ++/*? Text: "%s: %s: Resetting Event occurred:\n" */ ++/*? Text: "%s: %s: Recv Conn Confirm:Vers=%d,link_id=%d,Corr=%d,Host appl=%.8s,WS appl=%.8s\n" */ ++/*? Text: "%s: %s: Recv Conn Req: Vers=%d,link_id=%d,Corr=%d,HOST appl=%.8s,WS appl=%.8s\n" */ ++/*? Text: "%s: %s: Recv Sys Validate Request: Vers=%d,link_id=%d,Corr=%d,WS name=%.8s,Host name=%.8s\n" */ ++/*? Text: "%s: %s: Confirmed Now packing\n" */ ++/*? Text: "%s: %s: Unit Check Occured in write channel\n" */ ++/*? Text: "%s: %s: Restart is required after remote side recovers \n" */ ++/*? Text: "%s: %s: sys Validate Rsize:%d Wsize:%d\n" */ ++/*? Text: "%s: %s:readsize=%d writesize=%d readbuffer=%d writebuffer=%d read=0x%04x write=0x%04x\n" */ ++/*? Text: "%s: %s:host_name:%.8s, adapter_name :%.8s api_type: %.8s\n" */ ++/*? Text: "Driver unloaded\n" */ ++/*? Text: "Loading %s\n" */ ++/*? Text: "%s: will be removed.\n" */ ++/*? Text: "%s: add for %s\n" */ ++/*? Text: "%s: %s: shutting down \n" */ ++/*? Text: "%s: CLAW device %.8s: System validate completed.\n" */ ++/*? Text: "%s: %s: Disconnect: Vers=%d,link_id=%d,Corr=%d\n" */ ++/*? Text: "%s: %s: Recv Conn Resp: Vers=%d,link_id=%d,Corr=%d,RC=%d,Host appl=%.8s, WS appl=%.8s\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/cpcmd +@@ -0,0 +1,17 @@ ++/*? ++ * Text: "The cpcmd kernel function failed to allocate a response buffer\n" ++ * Severity: Warning ++ * Description: ++ * IPL code, console detection, and device drivers like vmcp or vmlogrdr use ++ * the cpcmd kernel function to send commands to the z/VM control program (CP). ++ * If a program that uses the cpcmd function does not allocate a contiguous ++ * response buffer below 2 GB guest real storage, cpcmd creates a bounce buffer ++ * to be used as the response buffer. Because of low memory or memory ++ * fragmentation, cpcmd could not create the bounce buffer. ++ * User action: ++ * Look for related page allocation failure messages and at the stack trace to ++ * find out which program or operation failed. Free some memory and retry the ++ * failed operation. Consider allocating more memory to your z/VM guest virtual ++ * machine. ++ */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/cpu +@@ -0,0 +1,69 @@ ++/*? ++ * Text: "Processor %d started, address %d, identification %06X\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: logical CPU number ++ * @2: CPU address ++ * @3: CPU identification number ++ * Description: ++ * The kernel detected a CPU with the given characteristics. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Processor %d stopped\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: logical CPU number ++ * Description: ++ * A logical CPU has been set offline. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%d configured CPUs, %d standby CPUs\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: number of configured CPUs ++ * @2: number of standby CPUs ++ * Description: ++ * The kernel detected the given number of configured and standby CPUs. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The CPU configuration topology of the machine is:" ++ * Severity: Informational ++ * Description: ++ * The first six values of the topology information represent fields Mag6 to ++ * Mag1 of system-information block (SYSIB) 15.1.2. These fields specify the ++ * maximum numbers of topology-list entries (TLE) at successive topology nesting ++ * levels. The last value represents the MNest value of SYSIB 15.1.2 which ++ * specifies the maximum possible nesting that can be configured through ++ * dynamic changes. For details see the SYSIB 15.1.2 information in the ++ * "Principles of Operation." ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "CPU %i exceeds the maximum %i and is excluded from the dump\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: CPU number ++ * @2: maximum CPU number ++ * Description: ++ * The Linux kernel is used as a system dumper but it runs on more CPUs than ++ * it has been compiled for with the CONFIG_NR_CPUS kernel configuration ++ * option. The system dump will be created but information on one or more ++ * CPUs will be missing. ++ * User action: ++ * Update the system dump kernel to a newer version that supports more ++ * CPUs or reduce the number of installed CPUs and reproduce the problem ++ * that should be analyzed. If you send the system dump that prompted this ++ * message to a support organization, be sure to communicate that the dump ++ * does not include all CPU information. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/ctcm +@@ -0,0 +1,199 @@ ++/*? ++ * Text: "%s: An I/O-error occurred on the CTCM device\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the CTCM device ++ * Description: ++ * An I/O error was detected on one of the subchannels of the CTCM device. ++ * Depending on the error, the CTCM device driver might attempt an automatic ++ * recovery. ++ * User action: ++ * Check the status of the CTCM device, for example, with ifconfig. If the ++ * device is not operational, perform a manual recovery. See "Device Drivers, ++ * Features, and Commands" for details about how to recover a CTCM device. ++ */ ++ ++/*? ++ * Text: "%s: An adapter hardware operation timed out\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the CTCM device ++ * Description: ++ * The CTCM device uses an adapter to physically connect to its communication ++ * peer. An operation on this adapter timed out. ++ * User action: ++ * Check the status of the CTCM device, for example, with ifconfig. If the ++ * device is not operational, perform a manual recovery. See "Device Drivers, ++ * Features, and Commands" for details about how to recover a CTCM device. ++ */ ++ ++/*? ++ * Text: "%s: An error occurred on the adapter hardware\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the CTCM device ++ * Description: ++ * The CTCM device uses an adapter to physically connect to its communication ++ * peer. An operation on this adapter returned an error. ++ * User action: ++ * Check the status of the CTCM device, for example, with ifconfig. If the ++ * device is not operational, perform a manual recovery. See "Device Drivers, ++ * Features, and Commands" for details about how to recover a CTCM device. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer has disconnected\n" ++ * Severity: Notice ++ * Parameter: ++ * @1: channel ID ++ * Description: ++ * The remote device has disconnected. Possible reasons are that the remote ++ * interface has been closed or that the operating system instance with the ++ * communication peer has been rebooted or shut down. ++ * User action: ++ * Check the status of the peer device. Ensure that the peer operating system ++ * instance is running and that the peer interface is operational. ++ */ ++ ++/*? ++ * Text: "%s: The remote operating system is not available\n" ++ * Severity: Notice ++ * Parameter: ++ * @1: channel ID ++ * Description: ++ * The operating system instance with the communication peer has disconnected. ++ * Possible reasons are that the operating system instance has been rebooted ++ * or shut down. ++ * User action: ++ * Ensure that the peer operating system instance is running and that the peer ++ * interface is operational. ++ */ ++ ++/*? ++ * Text: "%s: The adapter received a non-specific IRQ\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CTCM device ++ * Description: ++ * The adapter hardware used by the CTCM device received an IRQ that cannot ++ * be mapped to a particular device. This is a hardware problem. ++ * User action: ++ * Check the status of the CTCM device, for example, with ifconfig. Check if ++ * the connection to the remote device still works. If the CTCM device is not ++ * operational, set it offline and back online. If this does not resolve the ++ * problem, perform a manual recovery. See "Device Drivers, Features, and ++ * Commands" for details about how to recover a CTCM device. If this problem ++ * persists, gather Linux debug data, collect the hardware logs, and report the ++ * problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: A check occurred on the subchannel\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CTCM device ++ * Description: ++ * A check condition has been detected on the subchannel. ++ * User action: ++ * Check if the connection to the remote device still works. If the CTCM device ++ * is not operational, set it offline and back online. If this does not resolve ++ * the problem, perform a manual recovery. See "Device Drivers, Features, and ++ * Commands" for details about how to recover a CTCM device. If this problem ++ * persists, gather Linux debug data and report the problem to your support ++ * organization. ++ */ ++ ++/*? ++ * Text: "%s: The communication peer is busy\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: channel ID ++ * Description: ++ * A busy target device was reported. This might be a temporary problem. ++ * User action: ++ * If this problem persists or is reported frequently ensure that the target ++ * device is working properly. ++ */ ++ ++/*? ++ * Text: "%s: The specified target device is not valid\n" ++ * Severity: Error ++ * Parameter: ++ * @1: channel ID ++ * Description: ++ * A target device was called with a faulty device specification. This is an ++ * adapter hardware problem. ++ * User action: ++ * Gather Linux debug data, collect the hardware logs, and contact IBM support. ++ */ ++ ++/*? ++ * Text: "An I/O operation resulted in error %04x\n" ++ * Severity: Error ++ * Parameter: ++ * @1: channel ID ++ * @2: error information ++ * Description: ++ * A hardware operation ended with an error. ++ * User action: ++ * Check the status of the CTCM device, for example, with ifconfig. If the ++ * device is not operational, perform a manual recovery. See "Device Drivers, ++ * Features, and Commands" for details about how to recover a CTCM device. ++ * If this problem persists, gather Linux debug data, collect the hardware logs, ++ * and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Initialization failed with RX/TX init handshake error %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CTCM device ++ * @2: error information ++ * Description: ++ * A problem occurred during the initialization of the connection. If the ++ * connection can be established after an automatic recovery, a success message ++ * is issued. ++ * User action: ++ * If the problem is not resolved by the automatic recovery process, check the ++ * local and remote device. If this problem persists, gather Linux debug data ++ * and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The network backlog for %s is exceeded, package dropped\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CTCM device ++ * @2: calling function ++ * Description: ++ * There is more network traffic than can be handled by the device. The device ++ * is closed and some data has not been transmitted. The device might be ++ * recovered automatically. ++ * User action: ++ * Investigate and resolve the congestion. If necessary, set the device ++ * online to make it operational. ++ */ ++ ++/*? ++ * Text: "%s: The XID used in the MPC protocol is not valid, rc = %d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the CTCM device ++ * @2: return code ++ * Description: ++ * The exchange identification (XID) used by the CTCM device driver when ++ * in MPC mode is not valid. ++ * User action: ++ * Note the error information provided with this message and contact your ++ * support organization. ++ */ ++ ++/*? Text: "CTCM driver unloaded\n" */ ++/*? Text: "%s: %s Internal error: net_device is NULL, ch = 0x%p\n" */ ++/*? Text: "%s / register_cu3088_discipline failed, ret = %d\n" */ ++/*? Text: "%s: %s: Internal error: Can't determine channel for interrupt device %s\n" */ ++/*? Text: "CTCM driver initialized\n" */ ++/*? Text: "%s: setup OK : r/w = %s/%s, protocol : %d\n" */ ++/*? Text: "%s: Connected with remote side\n" */ ++/*? Text: "%s: Restarting device\n" */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/dasd +@@ -0,0 +1,466 @@ ++/* dasd_ioctl */ ++ ++/*? ++ * Text: "%s: The DASD has been put in the quiesce state\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * No I/O operation is possible on this device. ++ * User action: ++ * Resume the DASD to enable I/O operations. ++ */ ++ ++/*? ++ * Text: "%s: I/O operations have been resumed on the DASD\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD is no longer in state quiesce and I/O operations can be performed ++ * on the device. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The DASD cannot be formatted while it is enabled\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD you try to format is enabled. Enabled devices cannot be formatted. ++ * User action: ++ * Contact the owner of the formatting tool. ++ */ ++ ++/*? ++ * Text: "%s: The specified DASD is a partition and cannot be formatted\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD you try to format is a partition. Partitions cannot be formatted ++ * separately. You can only format a complete DASD including all its partitions. ++ * User action: ++ * Format the complete DASD. ++ * ATTENTION: Formatting irreversibly destroys all data on all partitions ++ * of the DASD. ++ */ ++ ++/*? ++ * Text: "%s: Formatting unit %d failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: start track ++ * @3: return code ++ * Description: ++ * The formatting process might have been interrupted by a signal, for example, ++ * CTRL+C. If the process was not interrupted intentionally, an I/O error ++ * might have occurred. ++ * User action: ++ * Retry to format the device. If the error persists, check the log file for ++ * related error messages. If you cannot resolve the error, note the return ++ * code and contact your support organization. ++ */ ++ ++ ++/* dasd */ ++ ++/*? ++ * Text: "%s: start_IO run out of retries and failed with request %s\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: pointer to request ++ * Description: ++ * The start IO function tried to start an IO request but the number ++ * of retries for the I/O was exceeded before the request could be started. ++ * User action: ++ * Check for related previous error messages. ++ */ ++ ++/*? ++ * Text: "%s: Cancelling request %p failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: pointer to request ++ * @3: return code of previous function ++ * Description: ++ * In response to a user action, the DASD device driver tried but failed to ++ * cancel a previously started I/O operation. ++ * User action: ++ * Try the action again. ++ */ ++ ++/*? ++ * Text: "%s: Flushing the DASD request queue failed for request %p\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: pointer to request ++ * Description: ++ * As part of the unloading process, the DASD device driver flushes the ++ * request queue. This failed because a previously started I/O operation ++ * could not be canceled. ++ * User action: ++ * Try again to unload the DASD device driver or to shut down Linux. ++ */ ++ ++/*? ++ * Text: "The DASD device driver could not be initialized\n" ++ * Severity: Informational ++ * Description: ++ * The initialization of the DASD device driver failed because of previous ++ * errors. ++ * User action: ++ * Check for related previous error messages. ++ */ ++ ++/*? ++ * Text: "%s: Accessing the DASD failed because it is in probeonly mode\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The dasd= module or kernel parameter specified the probeonly attribute for ++ * the DASD you are trying to access. The DASD device driver cannot access ++ * DASDs that are in probeonly mode. ++ * User action: ++ * Change the dasd= parameter as to omit probeonly for the DASD and reload ++ * the DASD device driver. If the DASD device driver has been compiled into ++ * the kernel, reboot Linux. ++ */ ++ ++/*? ++ * Text: "%s: cqr %p timed out (%is), %i retries remaining\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: pointer to request ++ * @3: timeout value ++ * @4: number of retries left ++ * Description: ++ * One try of the error recovery procedure (ERP) for the channel queued request ++ * (cqr) timed out and failed to recover the error. ERP continues for the DASD. ++ * User action: ++ * Ignore this message if it occurs infrequently and if the recovery succeeds ++ * during one of the retries. If this error persists, check for related ++ * previous error messages and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: cqr %p timed out (%is) but cannot be ended, retrying in 5 s\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: pointer to request ++ * @3: timeout value ++ * Description: ++ * A try of the error recovery procedure (ERP) for the channel queued request ++ * (cqr) timed out and failed to recover the error. The I/O request submitted ++ * during the try could not be canceled. The ERP waits for 5 seconds before ++ * trying again. ++ * User action: ++ * Ignore this message if it occurs infrequently and if the recovery succeeds ++ * during one of the retries. If this error persists, check for related ++ * previous error messages and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The DASD cannot be set offline while it is in use\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD cannot be set offline because it is in use by an internal process. ++ * An action to free the DASD might not have completed yet. ++ * User action: ++ * Wait some time and set the DASD offline later. ++ */ ++ ++/*? ++ * Text: "%s: The DASD cannot be set offline with open count %i\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: count ++ * Description: ++ * The DASD is being used by one or more processes and cannot be set offline. ++ * User action: ++ * Ensure that the DASD is not in use anymore, for example, unmount all ++ * partitions. Then try again to set the DASD offline. ++ */ ++ ++/*? ++ * Text: "%s: Setting the DASD online failed with rc=%d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: return code ++ * Description: ++ * The DASD could not be set online because of previous errors. ++ * User action: ++ * Look for previous error messages. If you cannot resolve the error, note ++ * the return code and contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s Setting the DASD online with discipline %s failed with rc=%i\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: discipline ++ * @3: return code ++ * Description: ++ * The DASD could not be set online because of previous errors. ++ * User action: ++ * Look for previous error messages. If you cannot resolve the error, note the ++ * return code and contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s Setting the DASD online failed because of missing DIAG discipline\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD was to be set online with discipline DIAG but this discipline of ++ * the DASD device driver is not available. ++ * User action: ++ * Ensure that the dasd_diag_mod module is loaded. If your Linux system does ++ * not include this module, you cannot set DASDs online with the DIAG ++ * discipline. ++ */ ++ ++/*? ++ * Text: "%s Setting the DASD online failed because of a missing discipline\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD was to be set online with a DASD device driver discipline that ++ * is not available. ++ * User action: ++ * Ensure that all DASD modules are loaded correctly. ++ */ ++ ++--------------------------- ++ ++/*? ++ * Text: "The statistics feature has been switched off\n" ++ * Severity: Informational ++ * Description: ++ * The statistics feature of the DASD device driver has been switched off. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The statistics feature has been switched on\n" ++ * Severity: Informational ++ * Description: ++ * The statistics feature of the DASD device driver has been switched on. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The statistics have been reset\n" ++ * Severity: Informational ++ * Description: ++ * The DASD statistics data have been reset. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s is not a supported value for /proc/dasd/statistics\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: value ++ * Description: ++ * An incorrect value has been written to /proc/dasd/statistics. ++ * The supported values are: 'set on', 'set off', and 'reset'. ++ * User action: ++ * Write a supported value to /proc/dasd/statistics. ++ */ ++ ++/*? ++ * Text: "%s is not a valid device range\n" ++ * Severity: Error ++ * Parameter: ++ * @1: range ++ * Description: ++ * A device range specified with the dasd= parameter is not valid. ++ * User action: ++ * Examine the dasd= parameter and correct the device range. ++ */ ++ ++/*? ++ * Text: "The probeonly mode has been activated\n" ++ * Severity: Informational ++ * Description: ++ * The probeonly mode of the DASD device driver has been activated. In this ++ * mode the device driver rejects any 'open' syscalls with EPERM. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The IPL device is not a CCW device\n" ++ * Severity: Error ++ * Description: ++ * The value for the dasd= parameter contains the 'ipldev' keyword. During ++ * the boot process this keyword is replaced with the device from which the ++ * IPL was performed. The 'ipldev' keyword is not valid if the IPL device is ++ * not a CCW device. ++ * User action: ++ * Do not specify the 'ipldev' keyword when performing an IPL from a device ++ * other than a CCW device. ++ */ ++ ++/*? ++ * Text: "A closing parenthesis ')' is missing in the dasd= parameter\n" ++ * Severity: Warning ++ * Description: ++ * The specification for the dasd= kernel or module parameter has an opening ++ * parenthesis '(' * without a matching closing parenthesis ')'. ++ * User action: ++ * Correct the parameter value. ++ */ ++ ++/*? ++ * Text: "The autodetection mode has been activated\n" ++ * Severity: Informational ++ * Description: ++ * The autodetection mode of the DASD device driver has been activated. In ++ * this mode the DASD device driver sets all detected DASDs online. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%*s is not a supported device option\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: length of option code ++ * @2: option code ++ * Description: ++ * The dasd= parameter includes an unknown option for a DASD or a device range. ++ * Options are specified in parenthesis and immediately follow a device or ++ * device range. ++ * User action: ++ * Check the dasd= syntax and remove any unsupported options from the dasd= ++ * parameter specification. ++ */ ++ ++/*? ++ * Text: "PAV support has be deactivated\n" ++ * Severity: Informational ++ * Description: ++ * The 'nopav' keyword has been specified with the dasd= kernel or module ++ * parameter. The Parallel Access Volume (PAV) support of the DASD device ++ * driver has been deactivated. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "'nopav' is not supported on z/VM\n" ++ * Severity: Informational ++ * Description: ++ * For Linux instances that run as guest operating systems of the z/VM ++ * hypervisor Parallel Access Volume (PAV) support is controlled by z/VM not ++ * by Linux. ++ * User action: ++ * Remove 'nopav' from the dasd= module or kernel parameter specification. ++ */ ++ ++/*? ++ * Text: "High Performance FICON support has been deactivated\n" ++ * Severity: Informational ++ * Description: ++ * The 'nofcx' keyword has been specified with the dasd= kernel or module ++ * parameter. The High Performance FICON (transport mode) support of the DASD ++ * device driver has been deactivated. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The dasd= parameter value %s has an invalid ending\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: parameter value ++ * Description: ++ * The specified value for the dasd= kernel or module parameter is not correct. ++ * User action: ++ * Check the module or the kernel parameter. ++ */ ++ ++/*? ++ * Text: "Registering the device driver with major number %d failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: DASD major ++ * Description: ++ * Major number 94 is reserved for the DASD device driver. The DASD device ++ * driver failed to register with this major number. Another device driver ++ * might have used major number 94. ++ * User action: ++ * Determine which device driver uses major number 94 instead of the DASD ++ * device driver and unload this device driver. Then try again to load the ++ * DASD device driver. ++ */ ++ ++/*? ++ * Text: "%s: default ERP has run out of retries and failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The error recovery procedure (ERP) tried to recover an error but the number ++ * of retries for the I/O was exceeded before the error could be resolved. ++ * User action: ++ * Check for related previous error messages. ++ */ ++ ++/*? ++ * Text: "%s: Unable to terminate request %p on suspend\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: pointer to request ++ * Description: ++ * As part of the suspend process, the DASD device driver terminates requests ++ * on the request queue. This failed because a previously started I/O operation ++ * could not be canceled. The suspend process will be stopped. ++ * User action: ++ * Try again to suspend the system. ++ */ ++ ++/*? ++ * Text: "%s: ERP failed for the DASD\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * An error recovery procedure (ERP) was performed for the DASD but failed. ++ * User action: ++ * Check the message log for previous related error messages. ++ */ ++ ++/*? ++ * Text: "%s: An error occurred in the DASD device driver, reason=%s\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: reason code ++ * Description: ++ * This problem indicates a program error in the DASD device driver. ++ * User action: ++ * Note the reason code and contact your support organization. ++*/ +--- /dev/null ++++ b/Documentation/kmsg/s390/dasd-diag +@@ -0,0 +1,118 @@ ++/* dasd_diag */ ++ ++/*? ++ * Text: "%s: A 64-bit DIAG call failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * 64-bit DIAG calls require a 64-bit z/VM version. ++ * User action: ++ * Use z/VM 5.2 or later or set the sysfs 'use_diag' attribute of the DASD to 0 ++ * to switch off DIAG. ++ */ ++ ++/*? ++ * Text: "%s: Accessing the DASD failed because of an incorrect format (rc=%d)\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: return code ++ * Description: ++ * The format of the DASD is not correct. ++ * User action: ++ * Check the device format. For details about the return code see the ++ * section about the INITIALIZE function for DIAGNOSE Code X'250' ++ * in "z/VM CP Programming Services". If you cannot resolve the error, note ++ * the return code and contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: New DASD with %ld byte/block, total size %ld KB%s\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: bytes per block ++ * @3: size ++ * @4: access mode ++ * Description: ++ * A DASD with the indicated block size and total size has been set online. ++ * If the DASD is configured as read-only to the real or virtual hardware, ++ * the message includes an indication of this hardware access mode. The ++ * hardware access mode is independent from the 'readonly' attribute of ++ * the device in sysfs. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: DIAG ERP failed with rc=%d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: return code ++ * Description: ++ * An error in the DIAG processing could not be recovered by the error ++ * recovery procedure (ERP) of the DIAG discipline. ++ * User action: ++ * Note the return code, check for related I/O errors, and report this problem ++ * to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: DIAG initialization failed with rc=%d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: return code ++ * Description: ++ * Initializing the DASD with the DIAG discipline failed. Possible reasons for ++ * this problem are that the device has a device type other than FBA or ECKD, ++ * or has a block size other than one of the supported sizes: ++ * 512 byte, 1024 byte, 2048 byte, or 4096 byte. ++ * User action: ++ * Ensure that the device can be written to and has a supported device type ++ * and block size. For details about the return code see the section about ++ * the INITIALIZE function for DIAGNOSE Code X'250' in "z/VM CP Programming ++ * Services". If you cannot resolve the error, note the error code and contact ++ * your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Device type %d is not supported in DIAG mode\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: device type ++ * Description: ++ * Only DASD of type FBA and ECKD are supported in DIAG mode. ++ * User action: ++ * Set the sysfs 'use_diag' attribute of the DASD to 0 and try again to access ++ * the DASD. ++ */ ++ ++/*? ++ * Text: "Discipline %s cannot be used without z/VM\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: discipline name ++ * Description: ++ * The discipline that is specified with the dasd= kernel or module parameter ++ * is only available for Linux instances that run as guest operating ++ * systems of the z/VM hypervisor. ++ * User action: ++ * Remove the unsupported discipline from the parameter string. ++ */ ++ ++/*? ++ * Text: "%s: The access mode of a DIAG device changed to read-only" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A device changed its access mode from writeable to ++ * read-only while in use. ++ * User action: ++ * Set the device offline, ensure that the device is configured correctly in ++ * z/VM, then set the device online again. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/dasd-eckd +@@ -0,0 +1,1901 @@ ++/* dasd_eckd */ ++ ++/*? ++ * Text: "%s: ERP failed for the DASD\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * An error recovery procedure (ERP) was performed for the DASD but failed. ++ * User action: ++ * Check the message log for previous related error messages. ++ */ ++ ++/*? ++ * Text: "%s: An error occurred in the DASD device driver, reason=%s\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: reason code ++ * Description: ++ * This problem indicates a program error in the DASD device driver. ++ * User action: ++ * Note the reason code and contact your support organization. ++*/ ++ ++/*? ++ * Text: "%s: Allocating memory for private DASD data failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD device driver maintains data structures for each DASD it manages. ++ * There is not enough memory to allocate these data structures for one or ++ * more DASD. ++ * User action: ++ * Free some memory and try the operation again. ++ */ ++ ++/*? ++ * Text: "%s: DASD with %d KB/block, %d KB total size, %d KB/track, %s\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: block size ++ * @3: DASD size ++ * @4: track size ++ * @5: disc layout ++ * Description: ++ * A DASD with the shown characteristics has been set online. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: Start track number %d used in formatting is too big\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: track number ++ * Description: ++ * The DASD format I/O control was used incorrectly by a formatting tool. ++ * User action: ++ * Contact the owner of the formatting tool. ++ */ ++ ++/*? ++ * Text: "%s: The DASD is not formatted\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A DASD has been set online but it has not been formatted yet. You must ++ * format the DASD before you can use it. ++ * User action: ++ * Format the DASD, for example, with dasdfmt. ++ */ ++ ++/*? ++ * Text: "%s: 0x%x is not a known command\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: command ++ * Description: ++ * This problem is likely to be caused by a programming error. ++ * User action: ++ * Contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Track 0 has no records following the VTOC\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * Linux has identified a volume table of contents (VTOC) on the DASD but ++ * cannot read any data records following the VTOC. A possible cause of this ++ * problem is that the DASD has been used with another System z operating ++ * system. ++ * User action: ++ * Format the DASD for usage with Linux, for example, with dasdfmt. ++ * ATTENTION: Formatting irreversibly destroys all data on the DASD. ++ */ ++ ++/*? ++ * Text: "%s: An I/O control call used incorrect flags 0x%x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: flags ++ * Description: ++ * The DASD format I/O control was used incorrectly. ++ * User action: ++ * Contact the owner of the formatting tool. ++ */ ++ ++/*? ++ * Text: "%s: New DASD %04X/%02X (CU %04X/%02X) with %d cylinders, %d heads, %d sectors\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: device type ++ * @3: device model ++ * @4: control unit type ++ * @5: control unit model ++ * @6: number of cylinders ++ * @7: tracks per cylinder ++ * @8: sectors per track ++ * Description: ++ * A DASD with the shown characteristics has been set online. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The disk layout of the DASD is not supported\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD device driver only supports the following disk layouts: CDL, LDL, ++ * FBA, CMS, and CMS RESERVED. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: Start track %d used in formatting exceeds end track\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: track number ++ * Description: ++ * The DASD format I/O control was used incorrectly by a formatting tool. ++ * User action: ++ * Contact the owner of the formatting tool. ++ */ ++ ++/*? ++ * Text: "%s: The DASD cache mode was set to %x (%i cylinder prestage)\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: operation mode ++ * @3: number of cylinders ++ * Description: ++ * The DASD cache mode has been changed. See the storage system documentation ++ * for information about the different cache operation modes. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The DASD cannot be formatted with block size %d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: block size ++ * Description: ++ * The block size specified for a format instruction is not valid. The block ++ * size must be between 512 and 4096 byte and must be a power of 2. ++ * User action: ++ * Call the format command with a supported block size. ++ */ ++ ++/*? ++ * Text: "%s: The UID of the DASD has changed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The Unique Identifier (UID) of a DASD that is currently in use has changed. ++ * This indicates that the physical disk has been replaced. ++ * User action: ++ * None if the replacement was intentional. ++ * If the disk change is not expected, stop using the disk to prevent possible ++ * data loss. ++*/ ++ ++ ++/* dasd_3990_erp */ ++ ++/*? ++ * Text: "%s: is offline or not installed - INTERVENTION REQUIRED!!\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD to be accessed is not in an accessible state. The I/O operation ++ * will wait until the device is operational again. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * Make the DASD accessible again. For details see the storage system ++ * documentation. ++ */ ++ ++/*? ++ * Text: "%s: The DASD cannot be reached on any path (lpum=%x/opm=%x)\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: last path used mask ++ * @3: online path mask ++ * Description: ++ * After a path to the DASD failed, the error recovery procedure of the DASD ++ * device driver tried but failed to reconnect the DASD through an alternative ++ * path. ++ * User action: ++ * Ensure that the cabling between the storage server and the mainframe ++ * system is securely in place. Check the file systems on the DASD when it is ++ * accessible again. ++ */ ++ ++/*? ++ * Text: "%s: Unable to allocate DCTL-CQR\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an internal error. ++ * User action: ++ * Contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Invalid Parameter\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A data argument of a command is not valid. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - DPS Installation Check\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This operating system independent message is issued by the storage system ++ * for one of the following reasons: ++ * - A 3380 Model D or E DASD does not have the Dynamic Path Selection (DPS) ++ * feature in the DASD A-unit. ++ * - The device type of an attached DASD is not supported by the firmware. ++ * - A type 3390 DASD is attached to a 3 MB channel. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 2 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Drive motor switch is off\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - CCW Count less than required\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The CCW count of a command is less than required. This is an operating ++ * system independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Channel requested ... %02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: reason code ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. The possible reason codes indicate the following problems: ++ * 00 No Message. ++ * 01 The channel has requested unit check sense data. ++ * 02 The channel has requested retry and retry is exhausted. ++ * 03 A SA Check-2 error has occurred. This sense is presented with ++ * Equipment Check. ++ * 04 The channel has requested retry and retry is not possible. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Status Not As Required: reason %02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: reason code ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. There are several potential reasons for this message; ++ * byte 8 contains the reason code. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Device status 1 not valid\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Storage Path Restart\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * An operation for an active channel program was queued in a Storage Control ++ * when a warm start was received by the path. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Reset Notification\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A system reset or its equivalent was received on an interface. The Unit ++ * Check that generates this sense is posted to the next channel initiated ++ * selection following the resetting event. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Invalid Command Sequence\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * An incorrect sequence of commands has occurred. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Missing device address bit\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Subsystem Processing Error\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A firmware logic error has been detected. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Seek incomplete\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Invalid Command\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A command was issued that is not in the 2107/1750 command set. ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Command Invalid on Secondary Address\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A command or order not allowed on a PPRC secondary device has been received ++ * by the secondary device. This is an operating system independent message ++ * that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Invalid Defective/Alternate Track Pointer\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A defective track has been accessed. The subsystem generates an invalid ++ * Defective/Alternate Track Pointer as a part of RAID Recovery. ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Channel Returned with Incorrect retry CCW\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A command portion of the CCW returned after a command retry sequence does ++ * not match the command for which retry was signaled. This is an operating ++ * system independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Diagnostic of Special Command Violates File Mask\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A command is not allowed under the Access Authorization specified by the ++ * File Mask. This is an operating system independent message that is issued ++ * by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Head address does not compare\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Device did not respond to selection\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Device check-2 error or Set Sector is not complete\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Device Error Source\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The device has completed soft error logging. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Data Pinned for Device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * Modified data in cache or in persistent storage exists for the DASD. The ++ * data cannot be destaged to the device. This track is the first track pinned ++ * for this device. This is an operating system independent message that is ++ * issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Overrun on channel C\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Device Status 1 not as expected\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 0 - Device Fenced - device = %02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: sense data byte 4 ++ * Description: ++ * The device shown in sense byte 4 has been fenced. This is an operating ++ * system independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Interruption cannot be reset\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Index missing\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - DASD Fast Write inhibited\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * DASD Fast Write is not allowed because of a nonvolatile storage battery ++ * check condition. This is an operating system independent message that is ++ * issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - Invalid tag-in for an extended command sequence\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Key area error; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Count area error; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Track physical address did not compare\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 2 - 3990 check-2 error\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Offset active cannot be reset\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - RCC 1 and RCC 2 sequences not successful\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - No syn byte in count address area; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Data area error\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Overrun on channel A\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - No sync byte in count address area\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Data Check in the key area\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Caching status reset to default\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The storage director has assigned two new subsystem status devices and ++ * resets the status to its default value. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Data Check in the data area; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Device not ready\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - No sync byte in key area\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - DASD controller failed to set or reset the long busy latch\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 1 - Cylinder address did not compare\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 3 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - No syn byte in data area; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 2 - Support facility errors\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Key area error\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - End operation with transfer count not zero\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 2 - Microcode detected error %02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: error code ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Data Check in the count area; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 3 - Allegiance terminated\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * Allegiance terminated because of a Reset Allegiance or an Unconditional ++ * Reserve command on another channel. This is an operating system independent ++ * message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Home address area error\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Count area error\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - Invalid tag-in during selection sequence\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - No sync byte in data area\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - No sync byte in home address area; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Home address area error; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - Data area error; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - No sync byte in home address area\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Data Check in the home address area; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Data Check in the home address area\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Data Check in the count area\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 4 - No sync byte in key area; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - Invalid DCC selection response or timeout\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Data Check in the data area\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Operation Terminated\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The storage system ends an operation related to an active channel program ++ * when termination and redrive are required and logging is not desired. ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Overrun on channel B\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 5 - Data Check in the key area; offset active\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Volume is suspended duplex\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The duplex pair volume has entered the suspended duplex state because of a ++ * failure. This is an operating system independent message that is issued by ++ * the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Overrun on channel D\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - RCC 1 sequence not successful\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Overrun on channel E\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - 3990 microcode time out when stopping selection\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Overrun on channel F\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - RCC initiated by a connection check alert\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Overrun on channel G\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - extra RCC required\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 6 - Overrun on channel H\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - Unexpected end operation response code\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - Permanent path error (DASD controller not available)\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - Missing end operation; device transfer incomplete\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT D - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Cache or nonvolatile storage equipment failure\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * An equipment failure has occurred in the cache storage or nonvolatile ++ * storage of the storage system. This is an operating system independent ++ * message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - DPS cannot be filled\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - Error correction code hardware fault\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - Missing end operation; device transfer complete\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - DASD controller not available on disconnected command chain\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - No interruption from device during a command chain\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - No response to selection after a poll interruption\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 9 - Track physical address did not compare while oriented\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 9 - Head address did not compare\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - Invalid tag-in for an immediate command sequence\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 9 - Cylinder address did not compare\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - DPS checks after a system reset or selective reset\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Caching reinitiated\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * Caching has been automatically reinitiated following an error. ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - End operation with transfer count zero\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 7 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 9 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - Short busy time-out during device selection\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Caching terminated\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The storage system was unable to initiate caching or had to suspend caching ++ * for a 3990 control unit. If this problem is caused by a failure condition, ++ * an additional message will provide more information about the failure. ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * Check for additional messages that point out possible failures. For more ++ * information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Subsystem status cannot be determined\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The status of a DASD Fast Write or PPRC volume cannot be determined. ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Nonvolatile storage terminated\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The storage director has stopped using nonvolatile storage or cannot ++ * initiate nonvolatile storage. If this problem is caused by a failure, an ++ * additional message will provide more information about the failure. This is ++ * an operating system independent message that is issued by the storage system. ++ * User action: ++ * Check for additional messages that point out possible failures. For more ++ * information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 8 - Reserved\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: Write inhibited path encountered\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an informational message. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT 9 - Device check-2 error\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This is an operating system independent message that is issued by the ++ * storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Track format incorrect\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A track format error occurred while data was being written to the DASD or ++ * while a duplex pair was being established. This is an operating system ++ * independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: FORMAT F - Cache fast write access not authorized\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A request for Cache Fast Write Data access cannot be satisfied because ++ * of missing access authorization for the storage system. This is an operating ++ * system independent message that is issued by the storage system. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: Data recovered during retry with PCI fetch mode active\n" ++ * Severity: Emerg ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * A data error has been recovered on the storages system but the Linux file ++ * system cannot be informed about the data mismatch. To prevent Linux from ++ * running with incorrect data, the DASD device driver will trigger a kernel ++ * panic. ++ * User action: ++ * Reset your real or virtual hardware and reboot Linux. ++ */ ++ ++/*? ++ * Text: "%s: The specified record was not found\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The record to be accessed does not exist. The DASD might be unformatted ++ * or defect. ++ * User action: ++ * Try to format the DASD or replace it. ++ * ATTENTION: Formatting irreversibly destroys all data on the DASD. ++ */ ++ ++/*? ++ * Text: "%s: ERP %p (%02x) refers to %p\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: pointer to ERP ++ * @3: ERP status ++ * @4: cqr ++ * Description: ++ * This message provides debug information for the enhanced error recovery ++ * procedure (ERP). ++ * User action: ++ * If you do not need this information, you can suppress this message by ++ * switching off ERP logging, for example, by writing '1' to the 'erplog' ++ * sysfs attribute of the DASD. ++ */ ++ ++/*? ++ * Text: "%s: ERP chain at END of ERP-ACTION\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This message provides debug information for the enhanced error recovery ++ * procedure (ERP). ++ * User action: ++ * If you do not need this information, you can suppress this message by ++ * switching off ERP logging, for example, by writing '1' to the 'erplog' ++ * sysfs attribute of the DASD. ++ */ ++ ++/*? ++ * Text: "%s: The cylinder data for accessing the DASD is inconsistent\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * An error occurred in the storage system hardware. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: Accessing the DASD failed because of a hardware error\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * An error occurred in the storage system hardware. ++ * User action: ++ * For more information see the documentation of your storage system. ++ */ ++ ++/*? ++ * Text: "%s: ERP chain at BEGINNING of ERP-ACTION\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * This message provides debug information for the enhanced error recovery ++ * procedure (ERP). ++ * User action: ++ * If you do not need this information, you can suppress this message by ++ * switching off ERP logging, for example, by writing '1' to the 'erplog' ++ * sysfs attribute of the DASD. ++ */ ++ ++/*? ++ * Text: "%s: ERP %p has run out of retries and failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: ERP pointer ++ * Description: ++ * The error recovery procedure (ERP) tried to recover an error but the number ++ * of retries for the I/O was exceeded before the error could be resolved. ++ * User action: ++ * Check for related previous error messages. ++ */ ++ ++/*? ++ * Text: "%s: ERP failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The error recovery procedure (ERP) tried to recover an error but has ++ * failed. A retry is not recommended. The I/O will also fail. ++ * User action: ++ * Check for related previous error messages. ++ */ ++ ++/*? ++ * Text: "%s: SIM - SRC: %02x%02x%02x%02x\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: sense byte ++ * @3: sense byte ++ * @4: sense byte ++ * @5: sense byte ++ * Description: ++ * This error message is a System Information Message (SIM) generated by the ++ * storage system. The System Reference Code (SRC) defines the error in detail. ++ * User action: ++ * Look up the SRC in the storage server documentation. ++ */ ++ ++/*? ++ * Text: "%s: log SIM - SRC: %02x%02x%02x%02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: sense byte ++ * @3: sense byte ++ * @4: sense byte ++ * @5: sense byte ++ * Description: ++ * This System Information Message (SIM) is generated by the storage system. ++ * The System Reference Code (SRC) defines the error in detail. ++ * User action: ++ * Look up the SRC in the storage server documentation. ++ */ ++ ++/*? ++ * Text: "%s: Reading device feature codes failed with rc=%d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: return code ++ * Description: ++ * The device feature codes state which advanced features are supported by a ++ * device. ++ * Examples for advanced features are PAV or high performance FICON. ++ * Some early devices do not provide feature codes and no advanced features are ++ * available on these devices. ++ * User action: ++ * None, if the DASD does not provide feature codes. If the DASD provides ++ * feature codes, make sure that it is working correctly, then set it offline ++ * and back online. ++ */ ++ ++/*? ++ * Text: "%s: A channel path group could not be established\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * Initialization of a DASD did not complete because a channel path group ++ * could not be established. ++ * User action: ++ * Make sure that the DASD is working correctly, then try again to set it ++ * online. If initialization still fails, reboot. ++ */ ++ ++/*? ++ * Text: "%s: The DASD is not operating in multipath mode\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD channel path group could not be configured to use multipath mode. ++ * This might negatively affect I/O performance on this DASD. ++ * User action: ++ * Make sure that the DASD is working correctly, then try again to set it ++ * online. If initialization still fails, reboot. ++ */ ++ ++/*? ++ * Text: "%s: Detecting the DASD disk layout failed because of an I/O error\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The disk layout of the DASD could not be detected because of an unexpected ++ * I/O error. The DASD device driver treats the device like an unformatted DASD, ++ * and partitions on the device are not accessible. ++ * User action: ++ * If the DASD is formatted, make sure that the DASD is working correctly, ++ * then set it offline and back online. If the DASD is unformatted, format the ++ * DASD, for example, with dasdfmt. ++ * ATTENTION: Formatting irreversibly destroys all data on the DASD. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/dasd-fba +@@ -0,0 +1,30 @@ ++ ++/*? ++ * Text: "%s: New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB and %d B/blk\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the DASD ++ * @2: device type ++ * @3: device model ++ * @4: control unit type ++ * @5: control unit model ++ * @6: size ++ * @7: bytes per block ++ * Description: ++ * A DASD with the shown characteristics has been set online. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: Allocating memory for private DASD data failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the DASD ++ * Description: ++ * The DASD device driver maintains data structures for each DASD it manages. ++ * There is not enough memory to allocate these data structures for one or ++ * more DASD. ++ * User action: ++ * Free some memory and try the operation again. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/dcssblk +@@ -0,0 +1,192 @@ ++/*? ++ * Text: "Adjacent DCSSs %s and %s are not contiguous\n" ++ * Severity: Error ++ * Parameter: ++ * @1: name 1 ++ * @2: name 2 ++ * Description: ++ * You can only map a set of two or more DCSSs to a single DCSS device if the ++ * DCSSs in the set form a contiguous memory space. The DCSS device cannot be ++ * created because there is a memory gap between two adjacent DCSSs. ++ * User action: ++ * Ensure that you have specified all DCSSs that belong to the set. Check the ++ * definitions of the DCSSs on the z/VM hypervisor to verify that they form ++ * a contiguous memory space. ++ */ ++ ++/*? ++ * Text: "DCSS %s and DCSS %s have incompatible types\n" ++ * Severity: Error ++ * Parameter: ++ * @1: name 1 ++ * @2: name 2 ++ * Description: ++ * You can only map a set of two or more DCSSs to a single DCSS device if ++ * either all DCSSs in the set have the same type or if the set contains DCSSs ++ * of the two types EW and EN but no other type. The DCSS device cannot be ++ * created because at least two of the specified DCSSs are not compatible. ++ * User action: ++ * Check the definitions of the DCSSs on the z/VM hypervisor to verify that ++ * their types are compatible. ++ */ ++ ++/*? ++ * Text: "DCSS %s is of type SC and cannot be loaded as exclusive-writable\n" ++ * Severity: Error ++ * Parameter: ++ * @1: device name ++ * Description: ++ * You cannot load a DCSS device in exclusive-writable access mode if the DCSS ++ * devise maps to one or more DCSSs of type SC. ++ * User action: ++ * Load the DCSS in shared access mode. ++ */ ++ ++/*? ++ * Text: "DCSS device %s is removed after a failed access mode change\n" ++ * Severity: Error ++ * Parameter: ++ * @1: device name ++ * Description: ++ * To change the access mode of a DCSS device, all DCSSs that map to the device ++ * were unloaded. Reloading the DCSSs for the new access mode failed and the ++ * device is removed. ++ * User action: ++ * Look for related messages to find out why the DCSSs could not be reloaded. ++ * If necessary, add the device again. ++ */ ++ ++/*? ++ * Text: "All DCSSs that map to device %s are saved\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: device name ++ * Description: ++ * A save request has been submitted for the DCSS device. Changes to all DCSSs ++ * that map to the device are saved permanently. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Device %s is in use, its DCSSs will be saved when it becomes idle\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: device name ++ * Description: ++ * A save request for the device has been deferred until the device becomes ++ * idle. Then changes to all DCSSs that the device maps to will be saved ++ * permanently. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "A pending save request for device %s has been canceled\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: device name ++ * Description: ++ * A save request for the DCSSs that map to a DCSS device has been pending ++ * while the device was in use. This save request has been canceled. Changes to ++ * the DCSSs will not be saved permanently. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Loaded %s with total size %lu bytes and capacity %lu sectors\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: DCSS names ++ * @2: total size in bytes ++ * @3: total size in 512 byte sectors ++ * Description: ++ * The listed DCSSs have been verified as contiguous and successfully loaded. ++ * The displayed sizes are the sums of all DCSSs. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Device %s cannot be removed because it is not a known device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: device name ++ * Description: ++ * The DCSS device you are trying to remove is not known to the DCSS device ++ * driver. ++ * User action: ++ * List the entries under /sys/devices/dcssblk/ to see the names of the ++ * existing DCSS devices. ++ */ ++ ++/*? ++ * Text: "Device %s cannot be removed while it is in use\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: device name ++ * Description: ++ * You are trying to remove a device that is in use. ++ * User action: ++ * Make sure that all users of the device close the device before you try to ++ * remove it. ++ */ ++ ++/*? ++ * Text: "Device %s has become idle and is being saved now\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: device name ++ * Description: ++ * A save request for the DCSSs that map to a DCSS device has been pending ++ * while the device was in use. The device has become idle and all changes ++ * to the DCSSs are now saved permanently. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Writing to %s failed because it is a read-only device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: device name ++ * Description: ++ * The DCSS device is in shared access mode and cannot be written to. Depending ++ * on the type of the DCSSs that the device maps to, you might be able to ++ * change the access mode to exclusive-writable. ++ * User action: ++ * If the DCSSs of the device are of type SC, do not attempt to write to the ++ * device. If the DCSSs of the device are of type ER or SR, change the access ++ * mode to exclusive-writable before writing to the device. ++ */ ++ ++/*? ++ * Text: "The address range of DCSS %s changed while the system was suspended\n" ++ * Severity: Error ++ * Parameter: ++ * @1: device name ++ * Description: ++ * After resuming the system, the start address or end address of a DCSS does ++ * not match the address when the system was suspended. DCSSs must not be ++ * changed after the system was suspended. ++ * This error cannot be recovered. The system is stopped with a kernel panic. ++ * User action: ++ * Reboot Linux. ++ */ ++ ++/*? ++ * Text: "Suspending the system failed because DCSS device %s is writable\n" ++ * Severity: Error ++ * Parameter: ++ * @1: device name ++ * Description: ++ * A system cannot be suspended if one or more DCSSs are accessed in exclusive- ++ * writable mode. DCSS segment types EW, SW, and EN are always writable and ++ * must be removed before a system is suspended. ++ * User action: ++ * Remove all DCSSs of segment types EW, SW, and EN by writing the DCSS name to ++ * the sysfs 'remove' attribute. Set the access mode for all DCSSs of segment ++ * types SR and ER to read-only by writing 1 to the sysfs 'shared' attribute of ++ * the DCSS. Then try again to suspend the system. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/extmem +@@ -0,0 +1,290 @@ ++/*? ++ * Text: "Querying a DCSS type failed with rc=%ld\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: return code ++ * Description: ++ * The DCSS kernel interface used z/VM diagnose call X'64' to query the ++ * type of a DCSS. z/VM failed to determine the type and returned an error. ++ * User action: ++ * Look for related messages to find out which DCSS is affected. ++ * For details about the return codes see the section about DIAGNOSE Code ++ * X'64' in "z/VM CP Programming Services". ++ */ ++ ++/*? ++ * Text: "Loading DCSS %s failed with rc=%ld\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: DCSS name ++ * @2: return code ++ * Description: ++ * The DCSS kernel interface used diagnose call X'64' to load a DCSS. z/VM ++ * failed to load the DCSS and returned an error. ++ * User action: ++ * For details about the return codes see the section about DIAGNOSE Code ++ * X'64' in "z/VM CP Programming Services". ++ */ ++ ++/*? ++ * Text: "DCSS %s of range %p to %p and type %s loaded as exclusive-writable\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: DCSS name ++ * @2: starting page address ++ * @3: ending page address ++ * @4: DCSS type ++ * Description: ++ * The DCSS was loaded successfully in exclusive-writable access mode. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "DCSS %s of range %p to %p and type %s loaded in shared access mode\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: DCSS name ++ * @2: starting page address ++ * @3: ending page address ++ * @4: DCSS type ++ * Description: ++ * The DCSS was loaded successfully in shared access mode. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "DCSS %s is already in the requested access mode\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * A request to reload a DCSS with a new access mode has been rejected ++ * because the new access mode is the same as the current access mode. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "DCSS %s is in use and cannot be reloaded\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * Reloading a DCSS in a different access mode has failed because the DCSS is ++ * being used by one or more device drivers. The DCSS remains loaded with the ++ * current access mode. ++ * User action: ++ * Ensure that the DCSS is not used by any device driver then try again to ++ * load the DCSS with the new access mode. ++ */ ++ ++/*? ++ * Text: "DCSS %s overlaps with used memory resources and cannot be reloaded\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * The DCSS has been unloaded and cannot be reloaded because it overlaps with ++ * another loaded DCSS or with the memory of the z/VM guest virtual machine ++ * (guest storage). ++ * User action: ++ * Ensure that no DCSS is loaded that has overlapping memory resources ++ * with the DCSS you want to reload. If the DCSS overlaps with guest storage, ++ * use the DEF STORE CONFIG z/VM CP command to create a sufficient storage gap ++ * for the DCSS. For details, see the section about the DCSS device driver in ++ * "Device Drivers, Features, and Commands". ++ */ ++ ++/*? ++ * Text: "Reloading DCSS %s failed with rc=%ld\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: DCSS name ++ * @2: return code ++ * Description: ++ * The DCSS kernel interface used z/VM diagnose call X'64' to reload a DCSS ++ * in a different access mode. The DCSS was unloaded but z/VM failed to reload ++ * the DCSS. ++ * User action: ++ * For details about the return codes see the section about DIAGNOSE Code ++ * X'64' in "z/VM CP Programming Services". ++ */ ++ ++/*? ++ * Text: "Unloading unknown DCSS %s failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * The specified DCSS cannot be unloaded. The DCSS is known to the DCSS device ++ * driver but not to the DCSS kernel interface. This problem indicates a ++ * program error in extmem.c. ++ * User action: ++ * Report this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "Saving unknown DCSS %s failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * The specified DCSS cannot be saved. The DCSS is known to the DCSS device ++ * driver but not to the DCSS kernel interface. This problem indicates a ++ * program error in extmem.c. ++ * User action: ++ * Report this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "Saving a DCSS failed with DEFSEG response code %i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: response-code ++ * Description: ++ * The DEFSEG z/VM CP command failed to permanently save changes to a DCSS. ++ * User action: ++ * Look for related messages to find the cause of this error. See also message ++ * HCPE in the DEFSEG section of the "z/VM CP Command and ++ * Utility Reference". ++ */ ++ ++/*? ++ * Text: "Saving a DCSS failed with SAVESEG response code %i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: response-code ++ * Description: ++ * The SAVESEG z/VM CP command failed to permanently save changes to a DCSS. ++ * User action: ++ * Look for related messages to find the cause of this error. See also message ++ * HCPE in the SAVESEG section of the "z/VM CP Command and ++ * Utility Reference". ++ */ ++ ++/*? ++ * Text: "DCSS %s cannot be loaded or queried\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * You cannot load or query the specified DCSS because it either is not defined ++ * in the z/VM hypervisor, or it is a class S DCSS, or it is above 2047 MB ++ * and he Linux system is a 31-bit system. ++ * User action: ++ * Use the CP command "QUERY NSS" to find out if the DCSS is a valid ++ * DCSS that can be loaded. ++ */ ++ ++/*? ++ * Text: "DCSS %s cannot be loaded or queried without z/VM\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * A DCSS is a z/VM resource. Your Linux instance is not running as a z/VM ++ * guest operating system and, therefore, cannot load DCSSs. ++ * User action: ++ * Load DCSSs only on Linux instances that run as z/VM guest operating systems. ++ */ ++ ++/*? ++ * Text: "Loading or querying DCSS %s resulted in a hardware error\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * Either the z/VM DIAGNOSE X'64' query or load call issued for the DCSS ++ * returned with an error. ++ * User action: ++ * Look for previous extmem message to find the return code from the ++ * DIAGNOSE X'64' query or load call. For details about the return codes see ++ * the section about DIAGNOSE Code X'64' in "z/VM CP Programming Services". ++ */ ++ ++/*? ++ * Text: "DCSS %s has multiple page ranges and cannot be loaded or queried\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * You can only load or query a DCSS with multiple page ranges if: ++ * - The DCSS has 6 or fewer page ranges ++ * - The page ranges form a contiguous address space ++ * - The page ranges are of type EW or EN ++ * User action: ++ * Check the definition of the DCSS to make sure that the conditions for ++ * DCSSs with multiple page ranges are met. ++ */ ++ ++/*? ++ * Text: "%s needs used memory resources and cannot be loaded or queried\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * You cannot load or query the DCSS because it overlaps with an already ++ * loaded DCSS or with the memory of the z/VM guest virtual machine ++ * (guest storage). ++ * User action: ++ * Ensure that no DCSS is loaded that has overlapping memory resources ++ * with the DCSS you want to load or query. If the DCSS overlaps with guest ++ * storage, use the DEF STORE CONFIG z/VM CP command to create a sufficient ++ * storage gap for the DCSS. For details, see the section about the DCSS ++ * device driver in "Device Drivers, Features, and Commands". ++ */ ++ ++/*? ++ * Text: "DCSS %s is already loaded in a different access mode\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * The DCSS you are trying to load has already been loaded in a different ++ * access mode. You cannot simultaneously load the DCSS in different modes. ++ * User action: ++ * Reload the DCSS in a different mode or load it with the same mode in which ++ * it has already been loaded. ++ */ ++ ++/*? ++ * Text: "There is not enough memory to load or query DCSS %s\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * The available memory is not enough to load or query the DCSS. ++ * User action: ++ * Free some memory and repeat the failed operation. ++ */ ++ ++/*? ++ * Text: "DCSS %s overlaps with used storage and cannot be loaded\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * You cannot load the DCSS because it overlaps with an already loaded DCSS ++ * or with the memory of the z/VM guest virtual machine (guest storage). ++ * User action: ++ * Ensure that no DCSS is loaded that has overlapping memory resources ++ * with the DCSS you want to load. If the DCSS overlaps with guest storage, ++ * use the DEF STORE CONFIG z/VM CP command to create a sufficient storage gap ++ * for the DCSS. For details, see the section about the DCSS device driver in ++ * "Device Drivers, Features, and Commands". ++ */ ++ ++/*? ++ * Text: "DCSS %s exceeds the kernel mapping range (%lu) and cannot be loaded\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * @2: kernel mapping range in bytes ++ * Description: ++ * You cannot load the DCSS because it exceeds the kernel mapping range limit. ++ * User action: ++ * Ensure that the DCSS range is defined below the kernel mapping range. ++ */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/hvc_iucv +@@ -0,0 +1,122 @@ ++/*? ++ * Text: "The z/VM IUCV HVC device driver cannot be used without z/VM\n" ++ * Severity: Notice ++ * Description: ++ * The z/VM IUCV hypervisor console (HVC) device driver requires the ++ * z/VM inter-user communication vehicle (IUCV). ++ * User action: ++ * Set "hvc_iucv=" to zero in the kernel parameter line and reboot Linux. ++ */ ++ ++/*? ++ * Text: "%lu is not a valid value for the hvc_iucv= kernel parameter\n" ++ * Severity: Error ++ * Parameter: ++ * @1: hvc_iucv_devices ++ * Description: ++ * The "hvc_iucv=" kernel parameter specifies the number of z/VM IUCV ++ * hypervisor console (HVC) terminal devices. ++ * The parameter value ranges from 0 to 8. ++ * If zero is specified, the z/VM IUCV HVC device driver is disabled ++ * and no IUCV-based terminal access is available. ++ * User action: ++ * Correct the "hvc_iucv=" setting in the kernel parameter line and ++ * reboot Linux. ++ */ ++ ++/*? ++ * Text: "Creating a new HVC terminal device failed with error code=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: errno ++ * Description: ++ * The device driver initialization failed to allocate a new ++ * HVC terminal device. ++ * A possible cause of this problem is memory constraints. ++ * User action: ++ * If the error code is -12 (ENOMEM), consider assigning more memory ++ * to your z/VM guest virtual machine. ++ */ ++ ++/*? ++ * Text: "Registering HVC terminal device as Linux console failed\n" ++ * Severity: Error ++ * Description: ++ * The device driver initialization failed to set up the first HVC terminal ++ * device for use as Linux console. ++ * User action: ++ * If the error code is -12 (ENOMEM), consider assigning more memory ++ * to your z/VM guest virtual machine. ++ */ ++ ++/*? ++ * Text: "Registering IUCV handlers failed with error code=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: errno ++ * Description: ++ * The device driver initialization failed to register with z/VM IUCV to ++ * handle IUCV connections, as well as sending and receiving of IUCV messages. ++ * User action: ++ * Check for related IUCV error messages and see the errno manual page ++ * to find out what caused the problem. ++ */ ++ ++/*? ++ * Text: "Allocating memory failed with reason code=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: reason ++ * Description: ++ * The z/VM IUCV hypervisor console (HVC) device driver initialization failed, ++ * because of a general memory allocation failure. The reason code indicates ++ * the memory operation that has failed: ++ * kmem_cache (reason code=1), ++ * mempool (reason code=2), or ++ * hvc_iucv_allow= (reason code=3) ++ * User action: ++ * Consider assigning more memory to your z/VM guest virtual machine. ++ */ ++ ++/*? ++ * Text: "hvc_iucv_allow= does not specify a valid z/VM user ID list\n" ++ * Severity: Error ++ * Description: ++ * The "hvc_iucv_allow=" kernel parameter specifies a comma-separated list ++ * of z/VM user IDs that are permitted to connect to the z/VM IUCV hypervisor ++ * device driver. ++ * The z/VM user IDs in the list must not exceed eight characters and must ++ * not contain spaces. ++ * User action: ++ * Correct the "hvc_iucv_allow=" setting in the kernel parameter line and reboot ++ * Linux. ++ */ ++ ++/*? ++ * Text: "hvc_iucv_allow= specifies too many z/VM user IDs\n" ++ * Severity: Error ++ * Description: ++ * The "hvc_iucv_allow=" kernel parameter specifies a comma-separated list ++ * of z/VM user IDs that are permitted to connect to the z/VM IUCV hypervisor ++ * device driver. ++ * The number of z/VM user IDs that are specified with the "hvc_iucv_allow=" ++ * kernel parameter exceeds the maximum of 500. ++ * User action: ++ * Correct the "hvc_iucv_allow=" setting by reducing the z/VM user IDs in ++ * the list and reboot Linux. ++ */ ++ ++/*? ++ * Text: "A connection request from z/VM user ID %s was refused\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: ID ++ * Description: ++ * An IUCV connection request from another z/VM guest virtual machine has been ++ * refused. The request was from a z/VM guest virtual machine that is not ++ * listed by the "hvc_iucv_allow=" kernel parameter. ++ * User action: ++ * Check the "hvc_iucv_allow=" kernel parameter setting. ++ * Consider adding the z/VM user ID to the "hvc_iucv_allow=" list in the kernel ++ * parameter line and reboot Linux. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/hypfs +@@ -0,0 +1,56 @@ ++/*? ++ * Text: "The hardware system does not support hypfs\n" ++ * Severity: Error ++ * Description: ++ * hypfs requires DIAGNOSE Code X'204' but this diagnose code is not available ++ * on your hardware. You need more recent hardware to use hypfs. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The hardware system does not provide all functions required by hypfs\n" ++ * Severity: Error ++ * Description: ++ * hypfs requires DIAGNOSE Code X'224' but this diagnode code is not available ++ * on your hardware. You need more recent hardware to use hypfs. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Updating the hypfs tree failed\n" ++ * Severity: Error ++ * Description: ++ * There was not enough memory available to update the hypfs tree. ++ * User action: ++ * Free some memory and try again to update the hypfs tree. Consider assigning ++ * more memory to your LPAR or z/VM guest virtual machine. ++ */ ++ ++/*? ++ * Text: "%s is not a valid mount option\n" ++ * Severity: Error ++ * Parameter: ++ * @1: mount option ++ * Description: ++ * hypfs has detected mount options that are not valid. ++ * User action: ++ * See "Device Drivers Features and Commands" for information about valid ++ * mount options for hypfs. ++ */ ++ ++/*? ++ * Text: "Initialization of hypfs failed with rc=%i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: error code ++ * Description: ++ * Initialization of hypfs failed because of resource or hardware constraints. ++ * Possible reasons for this problem are insufficient free memory or missing ++ * hardware interfaces. ++ * User action: ++ * See errno.h for information about the error codes. ++ */ ++ ++/*? Text: "Hypervisor filesystem mounted\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/iucv +@@ -0,0 +1,33 @@ ++/*? ++ * Text: "Defining an interrupt buffer on CPU %i failed with 0x%02x (%s)\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: CPU number ++ * @2: hexadecimal error value ++ * @3: short error code explanation ++ * Description: ++ * Defining an interrupt buffer for external interrupts failed. Error ++ * value 0x03 indicates a problem with the z/VM directory entry of the ++ * z/VM guest virtual machine. This problem can also be caused by a ++ * program error. ++ * User action: ++ * If the error value is 0x03, examine the z/VM directory entry of your ++ * z/VM guest virtual machine. If the directory entry is correct or if the ++ * error value is not 0x03, report this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "Suspending Linux did not completely close all IUCV connections\n" ++ * Severity: Warning ++ * Description: ++ * When resuming a suspended Linux instance, the IUCV base code found ++ * data structures from one or more IUCV connections that existed before the ++ * Linux instance was suspended. Modules that use IUCV connections must close ++ * these connections when a Linux instance is suspended. This problem ++ * indicates an error in a program that used an IUCV connection. ++ * User action: ++ * Report this problem to your support organization. ++ */ ++ ++/*? Text: "iucv_external_interrupt: out of memory\n" */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/lcs +@@ -0,0 +1,161 @@ ++/*? ++ * Text: "%s: Allocating a socket buffer to interface %s failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * @2: network interface ++ * Description: ++ * LAN channel station (LCS) devices require a socket buffer (SKB) structure ++ * for storing incoming data. The LCS device driver failed to allocate an SKB ++ * structure to the LCS device. A likely cause of this problem is memory ++ * constraints. ++ * User action: ++ * Free some memory and repeat the failed operation. ++ */ ++ ++/*? ++ * Text: "%s: Shutting down the LCS device failed\n " ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * Description: ++ * A request to shut down a LAN channel station (LCS) device resulted in an ++ * error. The error is logged in the LCS trace at trace level 4. ++ * User action: ++ * Try again to shut down the device. If the error persists, see the LCS trace ++ * to find out what causes the error. ++ */ ++ ++/*? ++ * Text: "%s: Detecting a network adapter for LCS devices failed with rc=%d (0x%x)\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * @2: lcs_detect return code in decimal notation ++ * @3: lcs_detect return code in hexadecimal notation ++ * Description: ++ * The LCS device driver could not initialize a network adapter. ++ * User action: ++ * Note the return codes from the error message and contact IBM support. ++ */ ++ ++/*? ++ * Text: "%s: A recovery process has been started for the LCS device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * Description: ++ * The LAN channel station (LCS) device is shut down and restarted. The recovery ++ * process might have been initiated by a user or started automatically as a ++ * response to a device problem. ++ * User action: ++ * Wait until a message indicates the completion of the recovery process. ++ */ ++ ++/*? ++ * Text: "%s: An I/O-error occurred on the LCS device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * Description: ++ * The LAN channel station (LCS) device reported a problem that can be recovered ++ * by the LCS device driver. Repeated occurrences of this problem indicate a ++ * malfunctioning device. ++ * User action: ++ * If this problem occurs frequently, initiate a recovery process for the ++ * device, for example, by writing '1' to the 'recover' sysfs attribute of the ++ * device. ++ */ ++ ++/*? ++ * Text: "%s: A command timed out on the LCS device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * Description: ++ * The LAN channel station (LCS) device reported a problem that can be recovered ++ * by the LCS device driver. Repeated occurrences of this problem indicate a ++ * malfunctioning device. ++ * User action: ++ * If this problem occurs frequently, initiate a recovery process for the ++ * device, for example, by writing '1' to the 'recover' sysfs attribute of the ++ * device. ++ */ ++ ++/*? ++ * Text: "%s: An error occurred on the LCS device, rc=%ld\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * @2: return code ++ * Description: ++ * The LAN channel station (LCS) device reported a problem that can be recovered ++ * by the LCS device driver. Repeated occurrences of this problem indicate a ++ * malfunctioning device. ++ * User action: ++ * If this problem occurs frequently, initiate a recovery process for the ++ * device, for example, by writing '1' to the 'recover' sysfs attribute of the ++ * device. ++ */ ++ ++/*? ++ * Text: "%s: The LCS device stopped because of an error, dstat=0x%X, cstat=0x%X \n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * @2: device status ++ * @3: subchannel status ++ * Description: ++ * The LAN channel station (LCS) device reported an error. The LCS device driver ++ * might start a device recovery process. ++ * User action: ++ * If the device driver does not start a recovery process, initiate a recovery ++ * process, for example, by writing '1' to the 'recover' sysfs attribute of the ++ * device. If the problem persists, note the status information provided with ++ * the message and contact IBM support. ++ */ ++ ++/*? ++ * Text: "%s: Starting an LCS device resulted in an error, rc=%d!\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * @2: ccw_device_start return code in decimal notation ++ * Description: ++ * The LAN channel station (LCS) device driver failed to initialize an LCS ++ * device. The device is not operational. ++ * User action: ++ * Initiate a recovery process, for example, by writing '1' to the 'recover' ++ * sysfs attribute of the device. If the problem persists, contact IBM support. ++ */ ++ ++/*? ++ * Text: "%s: Sending data from the LCS device to the LAN failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the LCS device ++ * @2: ccw_device_resume return code in decimal notation ++ * Description: ++ * The LAN channel station (LCS) device driver could not send data to the LAN ++ * using the LCS device. This might be a temporary problem. Operations continue ++ * on the LCS device. ++ * User action: ++ * If this problem occurs frequently, initiate a recovery process, for example, ++ * by writing '1' to the 'recover' sysfs attribute of the device. If the ++ * problem persists, contact IBM support. ++ */ ++ ++/*? Text: "Query IPAssist failed. Assuming unsupported!\n" */ ++/*? Text: "Stoplan for %s initiated by LGW.\n" */ ++/*? Text: "Not enough memory to add new multicast entry!\n" */ ++/*? Text: "Not enough memory for debug facility.\n" */ ++/*? Text: "Adding multicast address failed. Table possibly full!\n" */ ++/*? Text: "Error in opening device!\n" */ ++/*? Text: "LCS device %s %s IPv6 support\n" */ ++/*? Text: "Device %s successfully recovered!\n" */ ++/*? Text: "LCS device %s %s Multicast support\n" */ ++/*? Text: " Initialization failed\n" */ ++/*? Text: "Loading %s\n" */ ++/*? Text: "Initialization failed\n" */ ++/*? Text: "Terminating lcs module.\n" */ ++/*? Text: "Device %s could not be recovered!\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/monreader +@@ -0,0 +1,127 @@ ++/*? ++ * Text: "Reading monitor data failed with rc=%i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: return code ++ * Description: ++ * The z/VM *MONITOR record device driver failed to read monitor data ++ * because the IUCV REPLY function failed. The read function against ++ * the monitor record device returns EIO. All monitor data that has been read ++ * since the last read with 0 size is incorrect. ++ * User action: ++ * Disregard all monitor data that has been read since the last read with ++ * 0 size. If the device driver has been compiled as a separate module, unload ++ * and reload the monreader module. If the device driver has been compiled ++ * into the kernel, reboot Linux. For more information about possible causes ++ * of the error see the IUCV section in "z/VM CP Programming Services" and ++ * the *MONITOR section in "z/VM Performance". ++ */ ++ ++/*? ++ * Text: "z/VM *MONITOR system service disconnected with rc=%i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: IPUSER SEVER return code ++ * Description: ++ * The z/VM *MONITOR record device driver receives monitor records through ++ * an IUCV connection to the z/VM *MONITOR system service. This connection ++ * has been severed and the read function of the z/VM *MONITOR device driver ++ * returns EIO. All data received since the last read with 0 size is incorrect. ++ * User action: ++ * Disregard all monitor data read since the last read with 0 size. Close and ++ * reopen the monitor record device. For information about the IPUSER SEVER ++ * return codes see "z/VM Performance". ++ */ ++ ++/*? ++ * Text: "The read queue for monitor data is full\n" ++ * Severity: Warning ++ * Description: ++ * The read function of the z/VM *MONITOR device driver returns EOVERFLOW ++ * because not enough monitor data has been read since the monitor device ++ * has been opened. Monitor data already read are valid and subsequent reads ++ * return valid data but some intermediate data might be missing. ++ * User action: ++ * Be aware that monitor data might be missing. Assure that you regularly ++ * read monitor data after opening the monitor record device. ++ */ ++ ++/*? ++ * Text: "Connecting to the z/VM *MONITOR system service failed with rc=%i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: IUCV CONNECT return code ++ * Description: ++ * The z/VM *MONITOR record device driver receives monitor records through ++ * an IUCV connection to the z/VM *MONITOR system service. This connection ++ * could not be established when the monitor record device was opened. If ++ * the return code is 15, your z/VM guest virtual machine is not authorized ++ * to connect to the *MONITOR system service. ++ * User action: ++ * If the return code is 15, ensure that the IUCV *MONITOR statement is ++ * included in the z/VM directory entry for your z/VM guest virtual machine. ++ * For other IUCV CONNECT return codes see the IUCV section in "CP Programming ++ * Services" and the *MONITOR section in "z/VM Performance". ++ */ ++ ++/*? ++ * Text: "Disconnecting the z/VM *MONITOR system service failed with rc=%i\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: IUCV SEVER return code ++ * Description: ++ * The z/VM *MONITOR record device driver receives monitor data through an ++ * IUCV connection to the z/VM *MONITOR system service. This connection ++ * could not be closed when the monitor record device was closed. You might ++ * not be able to resume monitoring. ++ * User action: ++ * No immediate action is necessary. If you cannot open the monitor record ++ * device in the future, reboot Linux. For information about the IUCV SEVER ++ * return codes see the IUCV section in "CP Programming Services" and the ++ * *MONITOR section in "z/VM Performance". ++ */ ++ ++/*? ++ * Text: "The z/VM *MONITOR record device driver cannot be loaded without z/VM\n" ++ * Severity: Error ++ * Description: ++ * The z/VM *MONITOR record device driver uses z/VM system services to provide ++ * monitor data about z/VM guest operating systems to applications on Linux. ++ * On Linux instances that run in environments other than the z/VM hypervisor, ++ * the z/VM *MONITOR record device driver does not provide any useful ++ * function and the corresponding monreader module cannot be loaded. ++ * User action: ++ * Load the z/VM *MONITOR record device driver only on Linux instances that run ++ * as guest operating systems of the z/VM hypervisor. If the z/VM *MONITOR ++ * record device driver has been compiled into the kernel, ignore this message. ++ */ ++ ++/*? ++ * Text: "The z/VM *MONITOR record device driver failed to register with IUCV\n" ++ * Severity: Error ++ * Description: ++ * The z/VM *MONITOR record device driver receives monitor data through an IUCV ++ * connection and needs to register with the IUCV device driver. This ++ * registration failed and the z/VM *MONITOR record device driver was not ++ * loaded. A possible cause of this problem is insufficient memory. ++ * User action: ++ * Free some memory and try again to load the module. If the z/VM *MONITOR ++ * record device driver has been compiled into the kernel, you might have to ++ * configure more memory and reboot Linux. If you do not want to read monitor ++ * data, ignore this message. ++ */ ++ ++/*? ++ * Text: "The specified *MONITOR DCSS %s does not have the required type SC\n" ++ * Severity: Error ++ * Parameter: ++ * @1: DCSS name ++ * Description: ++ * The DCSS that was specified with the monreader.mondcss kernel parameter or ++ * with the mondcss module parameter cannot be a *MONITOR DCSS because it is ++ * not of type SC. ++ * User action: ++ * Confirm that you are using the name of the DCSS that has been configured as ++ * the *MONITOR DCSS on the z/VM hypervisor. If the default name, MONDCSS, is ++ * used, omit the monreader.mondcss or mondcss parameter. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/monwriter +@@ -0,0 +1,16 @@ ++/*? ++ * Text: "Writing monitor data failed with rc=%i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: return code ++ * Description: ++ * The monitor stream application device driver used the z/VM diagnose call ++ * DIAG X'DC' to start writing monitor data. z/VM returned an error and the ++ * monitor data cannot be written. If the return code is 5, your z/VM guest ++ * virtual machine is not authorized to write monitor data. ++ * User action: ++ * If the return code is 5, ensure that your z/VM guest virtual machine's ++ * entry in the z/VM directory includes the OPTION APPLMON statement. ++ * For other return codes see the section about DIAGNOSE Code X'DC' ++ * in "z/VM CP Programming Services". ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/netiucv +@@ -0,0 +1,139 @@ ++/*? ++ * Text: "%s: The peer interface of the IUCV device has closed the connection\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * Description: ++ * The peer interface on the remote z/VM guest virtual machine has closed the ++ * connection. Do not expect further packets on this interface. Any packets ++ * you send to this interface will be dropped. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The IUCV device failed to connect to z/VM guest %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * @2: z/VM user ID ++ * Description: ++ * The connection cannot be established because the z/VM guest virtual ++ * machine with the peer interface is not running. ++ * User action: ++ * Ensure that the z/VM guest virtual machine with the peer interface is ++ * running; then try again to establish the connection. ++ */ ++ ++/*? ++ * Text: "%s: The IUCV device failed to connect to the peer on z/VM guest %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * @2: z/VM user ID ++ * Description: ++ * The connection cannot be established because the z/VM guest virtual machine ++ * with the peer interface is not configured for IUCV connections. ++ * User action: ++ * Configure the z/VM guest virtual machine with the peer interface for IUCV ++ * connections; then try again to establish the connection. ++ */ ++ ++/*? ++ * Text: "%s: Connecting the IUCV device would exceed the maximum number of IUCV connections\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * Description: ++ * The connection cannot be established because the maximum number of IUCV ++ * connections has been reached on the local z/VM guest virtual machine. ++ * User action: ++ * Close some of the established IUCV connections on the local z/VM guest ++ * virtual machine; then try again to establish the connection. ++ */ ++ ++/*? ++ * Text: "%s: z/VM guest %s has too many IUCV connections to connect with the IUCV device\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * @2: remote z/VM user ID ++ * Description: ++ * Connecting to the remote z/VM guest virtual machine failed because the ++ * maximum number of IUCV connections for the remote z/VM guest virtual ++ * machine has been reached. ++ * User action: ++ * Close some of the established IUCV connections on the remote z/VM guest ++ * virtual machine; then try again to establish the connection. ++ */ ++ ++/*? ++ * Text: "%s: The IUCV device cannot connect to a z/VM guest with no IUCV authorization\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * Description: ++ * Because the remote z/VM guest virtual machine is not authorized for IUCV ++ * connections, the connection cannot be established. ++ * User action: ++ * Add the statements 'IUCV ALLOW' and 'IUCV ANY' to the z/VM directory ++ * entry of the remote z/VM guest virtual machine; then try again to ++ * establish the connection. See "z/VM CP Planning and Administration" ++ * for details about the IUCV statements. ++ */ ++ ++/*? ++ * Text: "%s: Connecting the IUCV device failed with error %d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * @2: error code ++ * Description: ++ * The connection cannot be established because of an IUCV CONNECT error. ++ * User action: ++ * Report this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The IUCV device has been connected successfully to %s\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * @2: remote z/VM user ID ++ * Description: ++ * The connection has been established and the interface is ready to ++ * transmit communication packages. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The IUCV interface to %s has been established successfully\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * @2: remote z/VM user ID ++ * Description: ++ * The IUCV interface to the remote z/VM guest virtual machine has been ++ * established and can be activated with "ifconfig up" or an equivalent ++ * command. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The IUCV device is connected to %s and cannot be removed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the IUCV device ++ * @2: remote z/VM user ID ++ * Description: ++ * Removing a connection failed because the interface is active with a peer ++ * interface on a remote z/VM guest virtual machine. ++ * User action: ++ * Deactivate the interface with "ifconfig down" or an equivalent command; ++ * then try again to remove the interface. ++ */ ++ ++/*? Text: "driver unloaded\n" */ ++/*? Text: "driver initialized\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/qeth +@@ -0,0 +1,606 @@ ++/*? ++ * Text: "%s: The LAN is offline\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * A start LAN command was sent by the qeth device driver but the physical or ++ * virtual adapter has not started the LAN. The LAN might take a few seconds ++ * to become available. ++ * User action: ++ * Check the status of the qeth device, for example, with the lsqeth command. ++ * If the device does not become operational within a few seconds, initiate a ++ * recovery process, for example, by writing '1' to the 'recover' sysfs ++ * attribute of the device. ++ */ ++ ++/*? ++ * Text: "%s: The user canceled setting the qeth device offline\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * A user initiated setting the device offline but subsequently canceled the ++ * operation, for example, with CTRL+C. ++ * User action: ++ * Check the status of the qeth device, for example, with the lsqeth command. ++ * If necessary, repeat the operation to set the device offline. ++ */ ++ ++/*? ++ * Text: "%s: A recovery process has been started for the device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * A recovery process was started either by the qeth device driver or through ++ * a user command. ++ * User action: ++ * Wait until a message indicates the completion of the recovery process. ++ */ ++ ++/*? ++ * Text: "%s: The qeth device driver failed to recover an error on the device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * The qeth device driver performed an automatic recovery operation to recover ++ * an error on a qeth device. The recovery operation failed. ++ * User action: ++ * Try the following actions in the given order: i) Check the status of the ++ * qeth device, for example, with the lsqeth command. ii) Initiate a recovery ++ * process by writing '1' to the 'recover' sysfs attribute of the device. ++ * iii) Ungroup and regroup the subchannel triplet of the device. vi) Reboot ++ * Linux. v) If the problem persists, gather Linux debug data and report the ++ * problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The link for interface %s on CHPID 0x%X failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * @3: CHPID ++ * Description: ++ * A network link failed. A possible reason for this error is that a physical ++ * network cable has been disconnected. ++ * User action: ++ * Ensure that the network cable on the adapter hardware is connected properly. ++ * If the connection is to a guest LAN, ensure that the device is still coupled ++ * to the guest LAN. ++ */ ++ ++/*? ++ * Text: "%s: The link for %s on CHPID 0x%X has been restored\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * @3: CHPID ++ * Description: ++ * A failed network link has been re-established. A device recovery is in ++ * progress. ++ * User action: ++ * Wait until a message indicates the completion of the recovery process. ++ */ ++ ++/*? ++ * Text: "%s: A hardware operation timed out on the device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * A hardware operation timed out on the qeth device. ++ * User action: ++ * Check the status of the qeth device, for example, with the lsqeth command. ++ * If the device is not operational, initiate a recovery process, for example, ++ * by writing '1' to the 'recover' sysfs attribute of the device. ++ */ ++ ++/*? ++ * Text: "%s: The adapter hardware is of an unknown type\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * The qeth device driver does not recognize the adapter hardware. The cause ++ * of this problem could be a hardware error or a Linux level that does not ++ * support your adapter hardware. ++ * User action: ++ * i) Investigate if your adapter hardware is supported by your Linux level. ++ * Consider using hardware that is supported by your Linux level or upgrading ++ * to a Linux level that supports your hardware. ii) Install the latest ++ * firmware on your adapter hardware. iii) If the problem persists and is not ++ * caused by a version mismatch, contact IBM support. ++ */ ++ ++/*? ++ * Text: "%s: The adapter is used exclusively by another host\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * The qeth adapter is exclusively used by another host. ++ * User action: ++ * Use another qeth adapter or configure this one not exclusively to a ++ * particular host. ++ */ ++ ++/*? ++ * Text: "%s: QDIO reported an error, rc=%i\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: return code ++ * Description: ++ * The QDIO subsystem reported an error. ++ * User action: ++ * Check for related QDIO errors. Check the status of the qeth device, for ++ * example, with the lsqeth command. If the device is not operational, initiate ++ * a recovery process, for example, by writing '1' to the 'recover' sysfs ++ * attribute of the device. ++ */ ++ ++/*? ++ * Text: "%s: There is no kernel module to support discipline %d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: discipline ++ * Description: ++ * The qeth device driver or a user command requested a kernel module for a ++ * particular qeth discipline. Either the discipline is not supported by the ++ * qeth device driver or the requested module is not available to your Linux ++ * system. ++ * User action: ++ * Check if the requested discipline module has been compiled into the kernel ++ * or is present in /lib/modules//kernel/drivers/s390/net. ++ */ ++ ++/*? ++ * Text: "Initializing the qeth device driver failed\n" ++ * Severity: Error ++ * Parameter: ++ * Description: ++ * The base module of the qeth device driver could not be initialized. ++ * User action: ++ * See errno.h to determine the reason for the error. ++ * i) Reboot Linux. ii) If the problem persists, gather Linux debug data and ++ * report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Registering IP address %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: IP address ++ * Description: ++ * An IP address could not be registered with the network adapter. ++ * User action: ++ * Check if another operating system instance has already registered the ++ * IP address with the same network adapter or at the same logical IP subnet. ++ */ ++ ++/*? ++ * Text: "%s: Reading the adapter MAC address failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * The qeth device driver could not read the MAC address from the network ++ * adapter. ++ * User action: ++ * Ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Starting ARP processing support for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not start ARP support on the network adapter. ++ * User action: ++ * Ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Starting IP fragmentation support for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not start IP fragmentation support on the ++ * network adapter. ++ * User action: ++ * Ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Starting proxy ARP support for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not start proxy ARP support on the network ++ * adapter. ++ * User action: ++ * None if you do not require proxy ARP support. If you need proxy ARP, ++ * ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Starting VLAN support for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not start VLAN support on the network adapter. ++ * User action: ++ * None if you do not require VLAN support. If you need VLAN support, ++ * ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Starting multicast support for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not start multicast support on the network ++ * adapter. ++ * User action: ++ * Ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Activating IPv6 support for %s failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not activate IPv6 support on the network ++ * adapter. ++ * User action: ++ * None if you do not require IPv6 communication. If you need IPv6 support, ++ * ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Enabling the passthrough mode for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not enable the passthrough mode on the ++ * network adapter. The passthrough mode is required for all network traffic ++ * other than IPv4. In particular, the passthrough mode is required for IPv6 ++ * traffic. ++ * User action: ++ * None if all you want to support is IPv4 communication. If you want to support ++ * IPv6 or other network traffic apart from IPv4, ungroup and regroup the ++ * subchannel triplet of the device. If this does not resolve the problem, ++ * reboot Linux. If the problem persists, gather Linux debug data and report ++ * the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Enabling broadcast filtering for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not enable broadcast filtering on the network ++ * adapter. ++ * User action: ++ * Ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Setting up broadcast filtering for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not set up broadcast filtering on the network ++ * adapter. ++ * User action: ++ * Ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Setting up broadcast echo filtering for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not set up broadcast echo filtering on the ++ * network adapter. ++ * User action: ++ * Ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Starting HW checksumming for %s failed, using SW checksumming\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The network adapter supports hardware checksumming for incoming IP packages ++ * but the qeth device driver could not start hardware checksumming on the ++ * adapter. The qeth device driver continues to use software checksumming for ++ * incoming IP packages. ++ * User action: ++ * None if you do not require hardware checksumming for incoming network ++ * traffic. If you want to enable hardware checksumming, ungroup and regroup ++ * the subchannel triplet of the device. If this does not resolve the problem, ++ * reboot Linux. If the problem persists, gather Linux debug data and report ++ * the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Enabling HW checksumming for %s failed, using SW checksumming\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The network adapter supports hardware checksumming for incoming IP packages ++ * but the qeth device driver could not enable hardware checksumming on the ++ * adapter. The qeth device driver continues to use software checksumming for ++ * incoming IP packages. ++ * User action: ++ * None if you do not require hardware checksumming for incoming network ++ * traffic. If you want to enable hardware checksumming, ungroup and regroup ++ * the subchannel triplet of the device. If this does not resolve the problem, ++ * reboot Linux. If the problem persists, gather Linux debug data and report ++ * the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Starting outbound TCP segmentation offload for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The network adapter supports TCP segmentation offload, but the qeth device ++ * driver could not start this support on the adapter. ++ * User action: ++ * None if you do not require TCP segmentation offload. If you want to ++ * enable TCP segmentation offload, ungroup and regroup the subchannel triplet ++ * of the device. If this does not resolve the problem, reboot Linux. If the ++ * problem persists, gather Linux debug data and report the problem to your ++ * support organization. ++ */ ++ ++/*? ++ * Text: "%s: The network adapter failed to generate a unique ID\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * In IBM mainframe environments, network interfaces are not identified by ++ * a specific MAC address. Therefore, the network adapters provide the network ++ * interfaces with unique IDs to be used in their IPv6 link local addresses. ++ * Without such a unique ID, duplicate addresses might be assigned in other ++ * LPARs. ++ * User action: ++ * Install the latest firmware on the adapter hardware. Manually, configure ++ * an IPv6 link local address for this device. ++ */ ++ ++/*? ++ * Text: "There is no IPv6 support for the layer 3 discipline\n" ++ * Severity: Warning ++ * Description: ++ * If you want to use IPv6 with the layer 3 discipline, you need a Linux kernel ++ * with IPv6 support. Because your Linux kernel has not been compiled with ++ * IPv6 support, you cannot use IPv6 with the layer 3 discipline, even if your ++ * adapter supports IPv6. ++ * User action: ++ * Use a Linux kernel that has been complied to include IPv6 support if you ++ * want to use IPv6 with layer 3 qeth devices. ++ */ ++ ++/*? ++ * Text: "%s: The qeth device is not configured for the OSI layer required by z/VM\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * A qeth device that connects to a virtual network on z/VM must be configured for the ++ * same Open Systems Interconnection (OSI) layer as the virtual network. An ETHERNET ++ * guest LAN or VSWITCH uses the data link layer (layer 2) while an IP guest LAN ++ * or VSWITCH uses the network layer (layer 3). ++ * User action: ++ * If you are connecting to an ETHERNET guest LAN or VSWITCH, set the layer2 sysfs ++ * attribute of the qeth device to 1. If you are connecting to an IP guest LAN or ++ * VSWITCH, set the layer2 sysfs attribute of the qeth device to 0. ++ */ ++ ++/*? ++ * Text: "%s: Starting source MAC-address support for %s failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: network interface name ++ * Description: ++ * The qeth device driver could not enable source MAC-address on the network ++ * adapter. ++ * User action: ++ * Ungroup and regroup the subchannel triplet of the device. If this does not ++ * resolve the problem, reboot Linux. If the problem persists, gather Linux ++ * debug data and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x already exists\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: first token of the MAC-address ++ * @3: second token of the MAC-address ++ * @4: third token of the MAC-address ++ * @5: fourth token of the MAC-address ++ * @6: fifth token of the MAC-address ++ * @7: sixth token of the MAC-address ++ * Description: ++ * Setting the MAC address for the qeth device fails, because this ++ * MAC address is already defined on the OSA CHPID. ++ * User action: ++ * Use a different MAC address for this qeth device. ++ */ ++ ++/*? ++ * Text: "%s: MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x is not authorized\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * @2: first token of the MAC-address ++ * @3: second token of the MAC-address ++ * @4: third token of the MAC-address ++ * @5: fourth token of the MAC-address ++ * @6: fifth token of the MAC-address ++ * @7: sixth token of the MAC-address ++ * Description: ++ * This qeth device is a virtual network interface card (NIC), to which z/VM ++ * has already assigned a MAC address. z/VM MAC address verification does ++ * not allow you to change this predefined address. ++ * User action: ++ * None; use the MAC address that has been assigned by z/VM. ++ */ ++ ++/*? ++ * Text: "%s: The HiperSockets network traffic analyzer is activated\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * The sysfs 'sniffer' attribute of the HiperSockets device has the value '1'. ++ * The corresponding HiperSockets interface has been switched into promiscuous mode. ++ * As a result, the HiperSockets network traffic analyzer is started on the device. ++ * User action: ++ * None. ++ */ ++ ++ /*? ++ * Text: "%s: The HiperSockets network traffic analyzer is deactivated\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * The sysfs 'sniffer' attribute of the HiperSockets device has the value '1'. ++ * Promiscuous mode has been switched off for the corresponding HiperSockets interface ++ * As a result, the HiperSockets network traffic analyzer is stopped on the device. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The device is not authorized to run as a HiperSockets network traffic analyzer\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * The sysfs 'sniffer' attribute of the HiperSockets device has the value '1'. ++ * The corresponding HiperSockets interface is switched into promiscuous mode ++ * but the network traffic analyzer (NTA) rules configured at the Support Element (SE) ++ * do not allow tracing. Possible reasons are: ++ * - Tracing is not authorized for all HiperSockets channels in the mainframe system ++ * - Tracing is not authorized for this HiperSockets channel ++ * - LPAR is not authorized to enable an NTA ++ * User action: ++ * Configure appropriate HiperSockets NTA rules at the SE. ++ */ ++ ++/*? ++ * Text: "%s: A HiperSockets network traffic analyzer is already active in the HiperSockets LAN\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the qeth device ++ * Description: ++ * The sysfs 'sniffer' attribute of the HiperSockets device has the value '1'. ++ * The HiperSockets interface is switched into promiscuous mode but another ++ * HiperSockets device on the same HiperSockets channel is already running as ++ * a network traffic analyzer. ++ * A HiperSockets channel can only have one active network traffic analyzer. ++ * User action: ++ * Do not configure multiple HiperSockets devices in the same HiperSockets channel as ++ * tracing devices. ++ */ ++ ++ ++/*? Text: "core functions removed\n" */ ++/*? Text: "%s: Device is a%s card%s%s%s\nwith link type %s.\n" */ ++/*? Text: "%s: Device is a%s card%s%s%s\nwith link type %s (no portname needed by interface).\n" */ ++/*? Text: "%s: Device is a%s card%s%s%s\nwith link type %s (portname: %s)\n" */ ++/*? Text: "%s: issue_next_read failed: no iob available!\n" */ ++/*? Text: "%s: Priority Queueing not supported\n" */ ++/*? Text: "%s: sense data available. cstat 0x%X dstat 0x%X\n" */ ++/*? Text: "loading core functions\n" */ ++/*? Text: "%s: MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x successfully registered on device %s\n" */ ++/*? Text: "%s: Device successfully recovered!\n" */ ++/*? Text: "register layer 2 discipline\n" */ ++/*? Text: "unregister layer 2 discipline\n" */ ++/*? Text: "%s: Hardware IP fragmentation not supported on %s\n" */ ++/*? Text: "%s: IPv6 not supported on %s\n" */ ++/*? Text: "%s: VLAN not supported on %s\n" */ ++/*? Text: "%s: Inbound source MAC-address not supported on %s\n" */ ++/*? Text: "%s: IPV6 enabled\n" */ ++/*? Text: "%s: ARP processing not supported on %s!\n" */ ++/*? Text: "%s: Hardware IP fragmentation enabled \n" */ ++/*? Text: "%s: set adapter parameters not supported.\n" */ ++/*? Text: "%s: VLAN enabled\n" */ ++/*? Text: "register layer 3 discipline\n" */ ++/*? Text: "%s: Outbound TSO enabled\n" */ ++/*? Text: "%s: Broadcast not supported on %s\n" */ ++/*? Text: "%s: Outbound TSO not supported on %s\n" */ ++/*? Text: "%s: Inbound HW Checksumming not supported on %s,\ncontinuing using Inbound SW Checksumming\n" */ ++/*? Text: "%s: Using no checksumming on %s.\n" */ ++/*? Text: "%s: Broadcast enabled\n" */ ++/*? Text: "%s: Multicast not supported on %s\n" */ ++/*? Text: "%s: Using SW checksumming on %s.\n" */ ++/*? Text: "%s: HW Checksumming (inbound) enabled\n" */ ++/*? Text: "unregister layer 3 discipline\n" */ ++/*? Text: "%s: Multicast enabled\n" */ ++/*? Text: "%s: QDIO data connection isolation is deactivated\n" */ ++/*? Text: "%s: QDIO data connection isolation is activated\n" */ ++/*? Text: "%s: Adapter does not support QDIO data connection isolation\n" */ ++/*? Text: "%s: Adapter is dedicated. QDIO data connection isolation not supported\n" */ ++/*? Text: "%s: TSO does not permit QDIO data connection isolation\n" */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/s390dbf +@@ -0,0 +1,83 @@ ++/*? ++ * Text: "Root becomes the owner of all s390dbf files in sysfs\n" ++ * Severity: Warning ++ * Description: ++ * The S/390 debug feature you are using only supports uid/gid = 0. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Registering debug feature %s failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: feature name ++ * Description: ++ * The initialization of an S/390 debug feature failed. A likely cause of this ++ * problem is memory constraints. The system keeps running, but the debug ++ * data for this feature will not be available in sysfs. ++ * User action: ++ * Consider assigning more memory to your LPAR or z/VM guest virtual machine. ++ */ ++ ++/*? ++ * Text: "Registering view %s/%s would exceed the maximum number of views %i\n" ++ * Severity: Error ++ * Parameter: ++ * @1: feature name ++ * @2: view name ++ * @3: maximum ++ * Description: ++ * The maximum number of allowed debug feature views has been reached. The ++ * view has not been registered. The system keeps running but the new view ++ * will not be available in sysfs. This is a program error. ++ * User action: ++ * Report this problem to your support partner. ++ */ ++ ++/*? ++ * Text: "%s is not a valid level for a debug feature\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: level ++ * Description: ++ * Setting a new level for a debug feature by using the 'level' sysfs attribute ++ * failed. Valid levels are the minus sign (-) and the integers in the ++ * range 0 to 6. The minus sign switches off the feature. The numbers switch ++ * the feature on, where higher numbers produce more debug output. ++ * User action: ++ * Write a valid value to the 'level' sysfs attribute. ++ */ ++ ++/*? ++ * Text: "Flushing debug data failed because %c is not a valid area\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: debug area number ++ * Description: ++ * Flushing a debug area by using the 'flush' sysfs attribute failed. Valid ++ * values are the minus sign (-) for flushing all areas, or the number of the ++ * respective area for flushing a single area. ++ * User action: ++ * Write a valid area number or the minus sign (-) to the 'flush' sysfs ++ * attribute. ++ */ ++ ++/*? ++ * Text: "Allocating memory for %i pages failed\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: number of pages ++ * Description: ++ * Setting the debug feature size by using the 'page' sysfs attribute failed. ++ * Linux did not have enough memory for expanding the debug feature to the ++ * requested size. ++ * User action: ++ * Use a smaller number of pages for the debug feature or allocate more ++ * memory to your LPAR or z/VM guest virtual machine. ++ */ ++ ++/*? Text: "%s: set new size (%i pages)\n" */ ++/*? Text: "%s: switched off\n" */ ++/*? Text: "%s: level %i is out of range (%i - %i)\n" */ ++/*? Text: "Registering view %s/%s failed due to out of memory\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/sclp_cmd +@@ -0,0 +1,16 @@ ++/*? Text: "sync request failed (cmd=0x%08x, status=0x%02x)\n" */ ++/*? Text: "readcpuinfo failed (response=0x%04x)\n" */ ++/*? Text: "configure cpu failed (cmd=0x%08x, response=0x%04x)\n" */ ++/*? Text: "configure channel-path failed (cmd=0x%08x, response=0x%04x)\n" */ ++/*? Text: "read channel-path info failed (response=0x%04x)\n" */ ++/*? Text: "assign storage failed (cmd=0x%08x, response=0x%04x, rn=0x%04x)\n" */ ++ ++/*? ++ * Text: "Memory hotplug state changed, suspend refused.\n" ++ * Severity: Error ++ * Description: ++ * Suspend is refused after a memory hotplug operation was performed. ++ * User action: ++ * The system needs to be restarted and no memory hotplug operation must be ++ * performed in order to allow suspend. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/sclp_config +@@ -0,0 +1,3 @@ ++/*? Text: "cpu capability changed.\n" */ ++/*? Text: "no configuration management.\n" */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/sclp_cpi +@@ -0,0 +1,2 @@ ++/*? Text: "request failed (status=0x%02x)\n" */ ++/*? Text: "request failed with response code 0x%x\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/sclp_sdias +@@ -0,0 +1,4 @@ ++/*? Text: "sclp_send failed for get_nr_blocks\n" */ ++/*? Text: "SCLP error: %x\n" */ ++/*? Text: "sclp_send failed: %x\n" */ ++/*? Text: "Error from SCLP while copying hsa. Event status = %x\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/setup +@@ -0,0 +1,181 @@ ++/*? ++ * Text: "Execute protection active, mvcos available\n" ++ * Severity: Informational ++ * Description: ++ * The kernel parameter 'noexec' has been specified. The kernel will ++ * honor the execute bit of mappings and will use the mvcos instruction ++ * to copy between the user and kernel address space. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Execute protection active, mvcos not available\n" ++ * Severity: Informational ++ * Description: ++ * The kernel parameter 'noexec' has been specified. The kernel will ++ * honor the execute bit of mappings. The mvcos instruction is not ++ * available and the kernel will use the slower page table walk method ++ * to copy between the user and kernel address space. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Address spaces switched, mvcos available\n" ++ * Severity: Informational ++ * Description: ++ * The kernel parameter 'switch_amode' has been specified. The kernel ++ * will use the primary address space for user space processes and the ++ * home address space for the kernel. The mvcos instruction is used to ++ * copy between the user and kernel address space. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Address spaces switched, mvcos not available\n" ++ * Severity: Informational ++ * Description: ++ * The kernel parameter 'switch_amode' has been specified. The kernel ++ * will use the primary address space for user space processes and the ++ * home address space for the kernel. The mvcos instruction is not ++ * available and the kernel will use the slower page table walk method ++ * to copy between the user and kernel address space. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "initrd extends beyond end of memory (0x%08lx > 0x%08lx) disabling initrd\n" ++ * Severity: Error ++ * Parameter: ++ * @1: start address of the initial RAM disk ++ * @2: memory end address ++ * Description: ++ * The load address and the size of the initial RAM disk result in an end ++ * address of the initial RAM disk that is beyond the end of the system ++ * memory. ++ * User action: ++ * Lower the load address of the initial RAM disk, reduce the size of the ++ * initial RAM disk, or increase the size if the system memory to make the ++ * initial RAM disk fit into the memory. ++ */ ++ ++/*? ++ * Text: "Moving initrd (0x%08lx -> 0x%08lx, size: %ld)\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: old start address of the initial RAM disk ++ * @2: new start address of the initial RAM disk ++ * @3: size of the initial RAM disk ++ * Description: ++ * The location of the initial RAM disk conflicted with the boot memory bitmap. ++ * To resolve the conflict the initial RAM disk has been moved to a new ++ * location. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Linux is running as a z/VM guest operating system in 31-bit mode\n" ++ * Severity: Informational ++ * Description: ++ * The 31-bit Linux kernel detected that it is running as a guest operating ++ * system of the z/VM hypervisor. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Linux is running natively in 31-bit mode\n" ++ * Severity: Informational ++ * Description: ++ * The 31-bit Linux kernel detected that it is running on an IBM mainframe, ++ * either as the sole operating system in an LPAR or as the sole operating ++ * system on the entire mainframe. The Linux kernel is not running as a ++ * guest operating system of the z/VM hypervisor. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The hardware system has IEEE compatible floating point units\n" ++ * Severity: Informational ++ * Description: ++ * The Linux kernel detected that it is running on a hardware system with ++ * CPUs that have IEEE compatible floating point units. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The hardware system has no IEEE compatible floating point units\n" ++ * Severity: Informational ++ * Description: ++ * The Linux kernel detected that it is running on a hardware system with ++ * CPUs that do not have IEEE compatible floating point units. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Linux is running as a z/VM guest operating system in 64-bit mode\n" ++ * Severity: Informational ++ * Description: ++ * The 64-bit Linux kernel detected that it is running as a guest operating ++ * system of the z/VM hypervisor. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Linux is running natively in 64-bit mode\n" ++ * Severity: Informational ++ * Description: ++ * The 64-bit Linux kernel detected that it is running on an IBM mainframe, ++ * either as the sole operating system in an LPAR or as the sole operating ++ * system on the entire mainframe. The Linux kernel is not running as a ++ * guest operating system of the z/VM hypervisor. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "Defining the Linux kernel NSS failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: return code ++ * Description: ++ * The Linux kernel could not define the named saved system (NSS) with ++ * the z/VM CP DEFSYS command. The return code represents the numeric ++ * portion of the CP DEFSYS error message. ++ * User action: ++ * For return code 1, the z/VM guest virtual machine is not authorized ++ * to define named saved systems. ++ * Ensure that the z/VM guest virtual machine is authorized to issue ++ * the CP DEFSYS command (typically privilege class E). ++ * For other return codes, see the help and message documentation for ++ * the CP DEFSYS command. ++ */ ++ ++/*? ++ * Text: "Saving the Linux kernel NSS failed with rc=%d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: return code ++ * Description: ++ * The Linux kernel could not save the named saved system (NSS) with ++ * the z/VM CP SAVESYS command. The return code represents the numeric ++ * portion of the CP SAVESYS error message. ++ * User action: ++ * For return code 1, the z/VM guest virtual machine is not authorized ++ * to save named saved systems. ++ * Ensure that the z/VM guest virtual machine is authorized to issue ++ * the CP SAVESYS command (typically privilege class E). ++ * For other return codes, see the help and message documentation for ++ * the CP SAVESYS command. ++ */ ++ ++/*? Text: "Linux is running under KVM in 64-bit mode\n" */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/tape +@@ -0,0 +1,104 @@ ++/*? ++ * Text: "%s: A tape unit was detached while in use\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A tape unit has been detached from the I/O configuration while a tape ++ * was being accessed. This typically results in I/O error messages and ++ * potentially in damaged data on the tape. ++ * User action: ++ * Check the output of the application that accesses the tape device. ++ * If this problem occurred during a write-type operation, consider repeating ++ * the operation after bringing the tape device back online. ++ */ ++ ++/*? ++ * Text: "%s: A tape cartridge has been mounted\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A tape cartridge has been inserted into the tape unit. The tape in the ++ * tape unit is ready to be accessed. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The tape cartridge has been successfully unloaded\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape cartridge has been unloaded from the tape unit. Insert a tape ++ * cartridge before accessing the tape device. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: Determining the size of the recorded area...\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape block device driver is currently determining the size of the ++ * recorded area on the tape medium. This operation typically takes a ++ * few minutes. ++ * User action: ++ * Wait until the size is shown in a completion message. ++ */ ++ ++/*? ++ * Text: "%s: Opening the tape failed because of missing end-of-file marks\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape block device driver requires end-of-file marks at the end of ++ * the recorded area on a tape. If the tape device was to be opened in ++ * response to a mount command, the mount command will fail. ++ * User action: ++ * Insert a tape cartridge that has been prepared for use with the tape ++ * block device driver and try the operation again. ++ */ ++ ++/*? ++ * Text: "%s: The size of the recorded area is %i blocks\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: number of blocks ++ * Description: ++ * The tape block device driver has successfully determined the size of the ++ * recorded area on the tape medium. The tape device can now be used as ++ * a block device. See the mount(8) man page for details on how to access ++ * block devices. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "A cartridge is loaded in tape device %s, refusing to suspend\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A request to suspend a tape device currently loaded with a cartridge is ++ * rejected. ++ * User action: ++ * Unload the tape device. Then try to suspend the system again. ++ */ ++ ++/*? ++ * Text: "Tape device %s is busy, refusing to suspend\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A request to suspend a tape device being currently in use is rejected. ++ * User action: ++ * Terminate applications performing tape operations ++ * and then try to suspend the system again. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/tape_34xx +@@ -0,0 +1,418 @@ ++/*? ++ * Text: "%s: An unexpected condition %d occurred in tape error recovery\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: number ++ * Description: ++ * The control unit has reported an error condition that is not recognized by ++ * the error recovery process of the tape device driver. ++ * User action: ++ * Report this problem and the condition number from the message to your ++ * support organization. ++ */ ++ ++/*? ++ * Text: "%s: A data overrun occurred between the control unit and tape unit\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A data overrun error has occurred on the connection between the control ++ * unit and the tape unit. If this problem occurred during a write-type ++ * operation, the integrity of the data on the tape might be compromised. ++ * User action: ++ * Use a faster connection. If this problem occurred during a write-type ++ * operation, consider repositioning the tape and repeating the operation. ++ */ ++ ++/*? ++ * Text: "%s: The block ID sequence on the tape is incorrect\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The control unit has detected an incorrect block ID sequence on the tape. ++ * This problem typically indicates that the data on the tape is damaged. ++ * User action: ++ * If this problem occurred during a write-type operation reposition the tape ++ * and repeat the operation. ++ */ ++ ++/*? ++ * Text: "%s: A read error occurred that cannot be recovered\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A read error has occurred that cannot be recovered. The current tape might ++ * be damaged. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: A write error on the tape cannot be recovered\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A write error has occurred that could not be recovered by the automatic ++ * error recovery process. ++ * User action: ++ * Use a different tape cartridge. ++ */ ++ ++/*? ++ * Text: "%s: Writing the ID-mark failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The ID-mark at the beginning of tape could not be written. The tape medium ++ * might be write-protected. ++ * User action: ++ * Try a different tape cartridge. Ensure that the write-protection on the ++ * cartridge is switched off. ++ */ ++ ++/*? ++ * Text: "%s: Reading the tape beyond the end of the recorded area failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A read-type operation failed because it extended beyond the end of the ++ * recorded area on the tape medium. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The tape contains an incorrect block ID sequence\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The control unit has detected an incorrect block ID sequence on the tape. ++ * This problem typically indicates that the data on the tape is damaged. ++ * User action: ++ * If this problem occurred during a write-type operation reposition the tape ++ * and repeat the operation. ++ */ ++ ++/*? ++ * Text: "%s: A path equipment check occurred for the tape device\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A path equipment check has occurred. This check indicates problems with the ++ * connection between the mainframe system and the tape control unit. ++ * User action: ++ * Ensure that the cable connections between the mainframe system and the ++ * control unit are securely in place and not damaged. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit cannot process the tape format\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * Either the tape unit is not able to read the format ID mark, or the ++ * specified format is not supported by the tape unit. ++ * User action: ++ * If you do not need the data recorded on the current tape, use a different ++ * tape or write a new format ID mark at the beginning of the tape. Be aware ++ * that writing a new ID mark leads to a loss of all data that has been ++ * recorded on the tape. If you need the data on the current tape, use a tape ++ * unit that supports the tape format. ++ */ ++ ++/*? ++ * Text: "%s: The tape medium is write-protected\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A write-type operation failed because the tape medium is write-protected. ++ * User action: ++ * Eject the tape cartridge, switch off the write protection on the cartridge, ++ * insert the cartridge, and try the operation again. ++ */ ++ ++/*? ++ * Text: "%s: The tape does not have the required tape tension\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape does not have the required tape tension. ++ * User action: ++ * Rewind and reposition the tape, then repeat the operation. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit failed to load the cartridge\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * An error has occurred while loading the tape cartridge. ++ * User action: ++ * Unload the cartridge and load it again. ++ */ ++ ++/*? ++ * Text: "%s: Automatic unloading of the tape cartridge failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit failed to unload the cartridge. ++ * User action: ++ * Unload the cartridge manually by using the eject button on the tape unit. ++ */ ++ ++/*? ++ * Text: "%s: An equipment check has occurred on the tape unit\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * Possible reasons for the check condition are a unit adapter error, a buffer ++ * error on the lower interface, an unusable internal path, or an error that ++ * has occurred while loading the cartridge. ++ * User action: ++ * Examine the tape unit and the cartridge loader. Consult the tape unit ++ * documentation for details. ++ */ ++ ++/*? ++ * Text: "%s: The tape information states an incorrect length\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape is shorter than stated at the beginning of the tape data. A ++ * possible reason for this problem is that the tape might have been physically ++ * truncated. Data written to the tape might be incomplete or damaged. ++ * User action: ++ * If this problem occurred during a write-type operation, consider repeating ++ * the operation with a different tape cartridge. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit is not ready\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit is online but not ready. ++ * User action: ++ * Turn the ready switch on the tape unit to the ready position and try the ++ * operation again. ++ */ ++ ++/*? ++ * Text: "%s: The tape medium has been rewound or unloaded manually\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit rewind button, unload button, or both have been used to ++ * rewind or unload the tape cartridge. A tape cartridge other than the ++ * intended cartridge might have been inserted or the tape medium might not ++ * be at the expected position. ++ * User action: ++ * Verify that the correct tape cartridge has been inserted and that the tape ++ * medium is at the required position before continuing to work with the tape. ++ */ ++ ++/*? ++ * Text: "%s: The tape subsystem is running in degraded mode\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape subsystem is not operating at its maximum performance. ++ * User action: ++ * Contact your service representative for the tape unit and report this ++ * problem. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit is already assigned\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit is already assigned to another channel path. ++ * User action: ++ * Free the tape unit from the operating system instance to which it is ++ * currently assigned then try again. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit is not online\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit is not online to the tape device driver. ++ * User action: ++ * Ensure that the tape unit is operational and that the cable connections ++ * between the control unit and the tape unit are securely in place and not ++ * damaged. ++ */ ++ ++/*? ++ * Text: "%s: The control unit has fenced access to the tape volume\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The control unit fences further access to the current tape volume. The data ++ * integrity on the tape volume might have been compromised. ++ * User action: ++ * Rewind and unload the tape cartridge. ++ */ ++ ++/*? ++ * Text: "%s: A parity error occurred on the tape bus\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * A data parity check error occurred on the bus. Data that was read or written ++ * while the error occurred is not valid. ++ * User action: ++ * Reposition the tape and repeat the read-type or write-type operation. ++ */ ++ ++/*? ++ * Text: "%s: I/O error recovery failed on the tape control unit\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * An I/O error occurred that cannot be recovered by the automatic error ++ * recovery process of the tape control unit. The application that operates ++ * the tape unit will receive a return value of -EIO which indicates an ++ * I/O error. The data on the tape might be damaged. ++ * User action: ++ * If this problem occurred during a write-type operation, consider ++ * repositioning the tape and repeating the operation. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit requires a firmware update\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit requires firmware patches from the tape control unit but the ++ * required patches are not available on the control unit. ++ * User action: ++ * Make the require patches available on the control unit then reposition the ++ * tape and retry the operation. For details about obtaining and installing ++ * firmware updates see the control unit documentation. ++ */ ++ ++/*? ++ * Text: "%s: The maximum block size for buffered mode is exceeded\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The block to be written is larger than allowed for the buffered mode. ++ * User action: ++ * Use a smaller block size. ++ */ ++ ++/*? ++ * Text: "%s: A channel interface error cannot be recovered\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * An error has occurred on the channel interface. This error cannot ++ * be recovered by the control unit error recovery process. ++ * User action: ++ * See the documentation of the control unit. ++ */ ++ ++/*? ++ * Text: "%s: A channel protocol error occurred\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * An error was detected in the channel protocol. ++ * User action: ++ * Reposition the tape and try the operation again. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit does not support the compaction algorithm\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit cannot read the current tape. The data on the tape has been ++ * compressed with an algorithm that is not supported by the tape unit. ++ * User action: ++ * Use a tape unit that supports the compaction algorithm used for the ++ * current tape. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit does not support tape format 3480-2 XF\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit does not support tapes recorded in the 3480-2 XF format. ++ * User action: ++ * If you do not need the data recorded on the current tape, rewind the tape ++ * and overwrite it with a supported format. If you need the data on the ++ * current tape, use a tape unit that supports the tape format. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit does not support format 3480 XF\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit does not support tapes recorded in the 3480 XF format. ++ * User action: ++ * If you do not need the data recorded on the current tape, rewind the tape ++ * and overwrite it with a supported format. If you need the data on the ++ * current tape, use a tape unit that supports the tape format. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit does not support the current tape length\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The length of the tape in the cartridge is incompatible with the tape unit. ++ * User action: ++ * Either use a different tape unit or use a tape with a supported length. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit does not support the tape length\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The length of the tape in the cartridge is incompatible with the tape ++ * unit. ++ * User action: ++ * Either use a different tape unit or use a tape with a supported length. ++ */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/tape_3590 +@@ -0,0 +1,184 @@ ++/*? ++ * Text: "%s: The tape medium must be loaded into a different tape unit\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape device has indicated an error condition that requires loading ++ * the tape cartridge into a different tape unit to recover. ++ * User action: ++ * Unload the cartridge and use a different tape unit to retry the operation. ++ */ ++ ++/*? ++ * Text: "%s: Tape media information: exception %s, service %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: exception ++ * @3: service ++ * Description: ++ * This is an operating system independent tape medium information message ++ * that was issued by the tape unit. The information in the message is ++ * intended for the IBM customer engineer. ++ * User action: ++ * See the documentation for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: Device subsystem information: exception %s, service %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: exception ++ * @3: required service action ++ * Description: ++ * This is an operating system independent device subsystem information message ++ * that was issued by the tape unit. The information in the message is ++ * intended for the IBM customer engineer. ++ * User action: ++ * See the documentation for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: I/O subsystem information: exception %s, service %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: exception ++ * @3: required service action ++ * Description: ++ * This is an operating system independent I/O subsystem information message ++ * that was issued by the tape unit. The information in the message is ++ * intended for the IBM customer engineer. ++ * User action: ++ * See the documentation for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit has issued sense message %s\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: sense message code ++ * Description: ++ * The tape unit has issued an operating system independent sense message. ++ * User action: ++ * See the documentation for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit has issued an unknown sense message code 0x%x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: code ++ * Description: ++ * The tape device driver has received an unknown sense message from the ++ * tape unit. ++ * driver. ++ * User action: ++ * See the documentation for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: MIM SEV=%i, MC=%02x, ES=%x/%x, RC=%02x-%04x-%02x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: SEV ++ * @3: message code ++ * @4: exception ++ * @5: required service action ++ * @6: refcode ++ * @7: mid ++ * @8: fid ++ * Description: ++ * This is an operating system independent information message that was ++ * issued by the tape unit. The information in the message is intended for ++ * the IBM customer engineer. ++ * User action: ++ * See to the documentation for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: IOSIM SEV=%i, DEVTYPE=3590/%02x, MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: SEV ++ * @3: model ++ * @4: message code ++ * @5: exception ++ * @6: required service action ++ * @7: refcode1 ++ * @8: refcode2 ++ * @9: refcode3 ++ * Description: ++ * This is an operating system independent I/O subsystem information message ++ * that was issued by the tape unit. The information in the message is ++ * intended for the IBM customer engineer. ++ * User action: ++ * See the documentation for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: DEVSIM SEV=%i, DEVTYPE=3590/%02x, MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: SEV ++ * @3: model ++ * @4: message code ++ * @5: exception ++ * @6: required service action ++ * @7: refcode1 ++ * @8: refcode2 ++ * @9: refcode3 ++ * Description: ++ * This is an operating system independent device subsystem information message ++ * issued by the tape unit. The information in the message is intended for ++ * the IBM customer engineer. ++ * User action: ++ * See the documentation for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit has issued an unknown sense message code %x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * @2: code ++ * Description: ++ * The tape device has issued a sense message, that is unknown to the device ++ * driver. ++ * User action: ++ * Use the message code printed as hexadecimal value and see the documentation ++ * for the tape unit for further information. ++ */ ++ ++/*? ++ * Text: "%s: The tape unit failed to obtain the encryption key from EKM\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * The tape unit was unable to retrieve the encryption key required to decode ++ * the data on the tape from the enterprise key manager (EKM). ++ * User action: ++ * See the EKM and tape unit documentation for information about how to enable ++ * the tape unit to retrieve the encryption key. ++ */ ++ ++/*? ++ * Text: "%s: A different host has privileged access to the tape unit\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the tape device ++ * Description: ++ * You cannot access the tape unit because a different operating system ++ * instance has privileged access to the unit. ++ * User action: ++ * Unload the current cartridge to solve this problem. ++ */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/time +@@ -0,0 +1,36 @@ ++/*? ++ * Text: "The ETR interface has adjusted the clock by %li microseconds\n" ++ * Severity: Notice ++ * Parameter: ++ * @1: number of microseconds ++ * Description: ++ * The external time reference (ETR) interface has synchronized the system ++ * clock with the external reference and set it to a new value. The time ++ * difference between the old and new clock value has been passed to the ++ * network time protocol (NTP) as a single shot adjustment. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "The real or virtual hardware system does not provide an ETR interface\n" ++ * Severity: Warning ++ * Description: ++ * The 'etr=' parameter has been passed on the kernel parameter line for ++ * a Linux instance that does not have access to the external time reference ++ * (ETR) facility. ++ * User action: ++ * To avoid this warning remove the 'etr=' kernel parameter. ++ */ ++ ++/*? ++ * Text: "The real or virtual hardware system does not provide an STP interface\n" ++ * Severity: Warning ++ * Description: ++ * The 'stp=' parameter has been passed on the kernel parameter line for ++ * a Linux instance that does not have access to the server time protocol ++ * (STP) facility. ++ * User action: ++ * To avoid this warning remove the 'stp=' kernel parameter. ++ */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/vmcp +@@ -0,0 +1,13 @@ ++/*? ++ * Text: "The z/VM CP interface device driver cannot be loaded without z/VM\n" ++ * Severity: Warning ++ * Description: ++ * With the z/VM CP interface you can issue z/VM CP commands from a Linux ++ * terminal session. On Linux instances that run in environments other than ++ * the z/VM hypervisor, the z/VM CP interface does not provide any useful ++ * function and the corresponding vmcp device driver cannot be loaded. ++ * User action: ++ * Load the vmcp device driver only on Linux instances that run as guest ++ * operating systems of the z/VM hypervisor. If the device driver has been ++ * compiled into the kernel, ignore this message. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/vmlogrdr +@@ -0,0 +1,18 @@ ++/*? Text: "vmlogrdr: failed to start recording automatically\n" */ ++/*? Text: "vmlogrdr: connection severed with reason %i\n" */ ++/*? Text: "vmlogrdr: iucv connection to %s failed with rc %i \n" */ ++/*? Text: "vmlogrdr: failed to stop recording automatically\n" */ ++/*? Text: "not running under VM, driver not loaded.\n" */ ++ ++/*? ++ * Text: "vmlogrdr: device %s is busy. Refuse to suspend.\n" ++ * Severity: Error ++ * Parameter: ++ * @1: device name ++ * Description: ++ * Suspending vmlogrdr devices that are in uses is not supported. ++ * A request to suspend such a device is refused. ++ * User action: ++ * Close all applications that use any of the vmlogrdr devices ++ * and then try to suspend the system again. ++ */ +--- /dev/null ++++ b/Documentation/kmsg/s390/vmur +@@ -0,0 +1,47 @@ ++/*? ++ * Text: "The %s cannot be loaded without z/VM\n" ++ * Severity: Error ++ * Parameter: ++ * @1: z/VM virtual unit record device driver ++ * Description: ++ * The z/VM virtual unit record device driver provides Linux with access to ++ * z/VM virtual unit record devices like punch card readers, card punches, and ++ * line printers. On Linux instances that run in environments other than the ++ * z/VM hypervisor, the device driver does not provide any useful function and ++ * the corresponding vmur module cannot be loaded. ++ * User action: ++ * Load the vmur module only on Linux instances that run as guest operating ++ * systems of the z/VM hypervisor. If the z/VM virtual unit record device ++ * has been compiled into the kernel, ignore this message. ++ */ ++ ++/*? ++ * Text: "Kernel function alloc_chrdev_region failed with error code %d\n" ++ * Severity: Error ++ * Parameter: ++ * @1: error code according to errno definitions ++ * Description: ++ * The z/VM virtual unit record device driver (vmur) needs to register a range ++ * of character device minor numbers from 0x0000 to 0xffff. ++ * This registration failed, probably because of memory constraints. ++ * User action: ++ * Free some memory and reload the vmur module. If the z/VM virtual unit ++ * record device driver has been compiled into the kernel reboot Linux. ++ * Consider assigning more memory to your LPAR or z/VM guest virtual machine. ++ */ ++ ++/*? ++ * Text: "Unit record device %s is busy, %s refusing to suspend.\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the unit record device ++ * @1: z/VM virtual unit record device driver ++ * Description: ++ * Linux cannot be suspended while a unit record device is in use. ++ * User action: ++ * Stop all applications that work on z/VM spool file queues, for example, the ++ * vmur tool. Then try again to suspend Linux. ++ */ ++ ++/*? Text: "%s loaded.\n" */ ++/*? Text: "%s unloaded.\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/vmwatchdog +@@ -0,0 +1,26 @@ ++/*? ++ * Text: "The system cannot be suspended while the watchdog is in use\n" ++ * Severity: Error ++ * Description: ++ * A program is currently using the vmwatchdog device node. The watchdog ++ * device driver prevents the system from being suspended while the watchdog ++ * device is in use. ++ * User action: ++ * If you want to suspend the system, find out which program uses the watchdog ++ * device. Stop the program or reconfigure it to not use the watchdog. ++ */ ++ ++ ++/*? ++ * Text: "The system cannot be suspended while the watchdog is running\n" ++ * Severity: Error ++ * Description: ++ * The watchdog must not time out during hibernation. The watchdog ++ * device driver prevents the system from being suspended while the watchdog ++ * timer is running. ++ * User action: ++ * If you want to suspend the system, stop the watchdog, for example, by entering ++ * the command: 'echo V > /dev/vmwatchdog'. Alternatively, stop the program that ++ * uses the watchdog or reconfigure the program to not use the watchdog. ++ */ ++ +--- /dev/null ++++ b/Documentation/kmsg/s390/xpram +@@ -0,0 +1,73 @@ ++/*? ++ * Text: "%d is not a valid number of XPRAM devices\n" ++ * Severity: Error ++ * Parameter: ++ * @1: number of partitions ++ * Description: ++ * The number of XPRAM partitions specified for the 'devs' module parameter ++ * or with the 'xpram.parts' kernel parameter must be an integer in the ++ * range 1 to 32. The XPRAM device driver created a maximum of 32 partitions ++ * that are probably not configured as intended. ++ * User action: ++ * If the XPRAM device driver has been compiled as a separate module, ++ * unload the module and load it again with a correct value for the 'devs' ++ * module parameter. If the XPRAM device driver has been compiled ++ * into the kernel, correct the 'xpram.parts' parameter in the kernel ++ * command line and restart Linux. ++ */ ++ ++/*? ++ * Text: "Not enough expanded memory available\n" ++ * Severity: Error ++ * Description: ++ * The amount of expanded memory required to set up your XPRAM partitions ++ * depends on the 'sizes' parameter specified for the xpram module or on ++ * the specifications for the 'xpram.parts' parameter if the XPRAM device ++ * driver has been compiled into the kernel. Your ++ * current specification exceed the amount of available expanded memory. ++ * Your XPRAM partitions are probably not configured as intended. ++ * User action: ++ * If the XPRAM device driver has been compiled as a separate module, ++ * unload the xpram module and load it again with an appropriate value ++ * for the 'sizes' module parameter. If the XPRAM device driver has been ++ * compiled into the kernel, adjust the 'xpram.parts' parameter in the ++ * kernel command line and restart Linux. If you need more than the ++ * available expanded memory, increase the expanded memory allocation for ++ * your virtual hardware or LPAR. ++ */ ++ ++/*? ++ * Text: "No expanded memory available\n" ++ * Severity: Error ++ * Description: ++ * The XPRAM device driver has been loaded in a Linux instance that runs ++ * in an LPAR or virtual hardware without expanded memory. ++ * No XPRAM partitions are created. ++ * User action: ++ * Allocate expanded memory for your LPAR or virtual hardware or do not ++ * load the xpram module. You can ignore this message, if you do not want ++ * to create XPRAM partitions. ++ */ ++ ++/*? ++ * Text: "Resuming the system failed: %s\n" ++ * Severity: Error ++ * Parameter: ++ * @1: cause of the failure ++ * Description: ++ * A system cannot be resumed if the expanded memory setup changes ++ * after hibernation. Possible reasons for the failure are: ++ * - Expanded memory was removed after hibernation. ++ * - Size of the expanded memory changed after hibernation. ++ * The system is stopped with a kernel panic. ++ * User action: ++ * Reboot Linux. ++ */ ++ ++/*? Text: " number of devices (partitions): %d \n" */ ++/*? Text: " size of partition %d: %u kB\n" */ ++/*? Text: " size of partition %d to be set automatically\n" */ ++/*? Text: " memory needed (for sized partitions): %lu kB\n" */ ++/*? Text: " partitions to be sized automatically: %d\n" */ ++/*? Text: " automatically determined partition size: %lu kB\n" */ ++/*? Text: " %u pages expanded memory found (%lu KB).\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/zdump +@@ -0,0 +1,12 @@ ++/*? ++ * Text: "The 32-bit dump tool cannot be used for a 64-bit system\n" ++ * Severity: Alert ++ * Description: ++ * The dump process ends without creating a system dump. ++ * User action: ++ * Use a 64-bit dump tool to obtain a system dump for 64-bit Linux instance. ++ */ ++ ++/*? Text: "DETECTED 'S390 (32 bit) OS'\n" */ ++/*? Text: "0x%x is an unknown architecture.\n" */ ++/*? Text: "DETECTED 'S390X (64 bit) OS'\n" */ +--- /dev/null ++++ b/Documentation/kmsg/s390/zfcp +@@ -0,0 +1,865 @@ ++/*? ++ * Text: "%s is not a valid SCSI device\n" ++ * Severity: Error ++ * Parameter: ++ * @1: device specification ++ * Description: ++ * The specification for an initial SCSI device provided with the 'zfcp.device' ++ * kernel parameter or with the 'device' module parameter is syntactically ++ * incorrect. The specified SCSI device could not be attached to the Linux ++ * system. ++ * User action: ++ * Correct the value for the 'zfcp.device' or 'device' parameter and reboot ++ * Linux. See "Device Drivers, Features, and Commands" for information about ++ * the syntax. ++ */ ++ ++/*? ++ * Text: "Registering the misc device zfcp_cfdc failed\n" ++ * Severity: Error ++ * Description: ++ * The zfcp device driver failed to register the device that provides access to ++ * the adapter access control file (ACL tables). The device driver ++ * initialization failed. A possible cause for this problem is memory ++ * constraints. ++ * User action: ++ * Free some memory and try again to load the zfcp device driver. If the zfcp ++ * device driver has been compiled into the kernel, reboot Linux. Consider ++ * assigning more memory to your LPAR or z/VM guest virtual machine. If the ++ * problem persists, contact your support organization. ++ */ ++ ++/*? ++ * Text: "The zfcp device driver could not register with the common I/O layer\n" ++ * Severity: Error ++ * Description: ++ * The device driver initialization failed. A possible cause of this problem is ++ * memory constraints. ++ * User action: ++ * Free some memory and try again to load the zfcp device driver. If the zfcp ++ * device driver has been compiled into the kernel, reboot Linux. Consider ++ * assigning more memory to your LPAR or z/VM guest virtual machine. If the ++ * problem persists, contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Setting up data structures for the FCP adapter failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The zfcp device driver could not allocate data structures for an FCP adapter. ++ * A possible reason for this problem is memory constraints. ++ * User action: ++ * Set the FCP adapter offline or detach it from the Linux system, free some ++ * memory and set the FCP adapter online again or attach it again. If this ++ * problem persists, gather Linux debug data, collect the FCP adapter ++ * hardware logs, and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The FCP device is operational again\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * An FCP device has been unavailable because it had been detached from the ++ * Linux system or because the corresponding CHPID was offline. The FCP device ++ * is now available again and the zfcp device driver resumes all operations to ++ * the FCP device. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: The CHPID for the FCP device is offline\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The CHPID for an FCP device has been set offline, either logically in Linux ++ * or on the hardware. ++ * User action: ++ * Find out which CHPID corresponds to the FCP device, for example, with the ++ * lscss command. Check if the CHPID has been set logically offline in sysfs. ++ * Write 'on' to the CHPID's status attribute to set it online. If the CHPID is ++ * online in sysfs, find out if it has been varied offline through a hardware ++ * management interface, for example the service element (SE). ++ */ ++ ++/*? ++ * Text: "%s: The FCP device has been detached\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * An FCP device is no longer available to Linux. ++ * User action: ++ * Ensure that the FCP adapter is operational and attached to the LPAR or z/VM ++ * virtual machine. ++ */ ++ ++/*? ++ * Text: "%s: The FCP device did not respond within the specified time\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The common I/O layer waited for a response from the FCP adapter but ++ * no response was received within the specified time limit. This might ++ * indicate a hardware problem. ++ * User action: ++ * Consult your hardware administrator. If this problem persists, ++ * gather Linux debug data, collect the FCP adapter hardware logs, and ++ * report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Registering the FCP device with the SCSI stack failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP adapter could not be registered with the Linux SCSI ++ * stack. A possible reason for this problem is memory constraints. ++ * User action: ++ * Set the FCP adapter offline or detach it from the Linux system, free some ++ * memory and set the FCP adapter online again or attach it again. If this ++ * problem persists, gather Linux debug data, collect the FCP adapter ++ * hardware logs, and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: ERP cannot recover an error on the FCP device\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * An error occurred on an FCP device. The error recovery procedure (ERP) ++ * could not resolve the error. The FCP device driver cannot use the FCP device. ++ * User action: ++ * Check for previous error messages for the same FCP device to find the ++ * cause of the problem. ++ */ ++ ++/*? ++ * Text: "%s: Creating an ERP thread for the FCP device failed.\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The zfcp device driver could not set up error recovery procedure (ERP) ++ * processing for the FCP device. The FCP device is not available for use ++ * in Linux. ++ * User action: ++ * Free some memory and try again to load the zfcp device driver. If the zfcp ++ * device driver has been compiled into the kernel, reboot Linux. Consider ++ * assigning more memory to your LPAR or z/VM guest virtual machine. If the ++ * problem persists, contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: ERP failed for unit 0x%016Lx on port 0x%016Lx\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: WWPN ++ * Description: ++ * An error occurred on the SCSI device at the specified LUN. The error recovery ++ * procedure (ERP) could not resolve the error. The SCSI device is not ++ * available. ++ * User action: ++ * Verify that the LUN is correct. Check the fibre channel fabric for errors ++ * related to the specified WWPN and LUN, the storage server, and Linux. ++ */ ++ ++/*? ++ * Text: "%s: ERP failed for remote port 0x%016Lx\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: WWPN ++ * Description: ++ * An error occurred on a remote port. The error recovery procedure (ERP) ++ * could not resolve the error. The port is not available. ++ * User action: ++ * Verify that the WWPN is correct and check the fibre channel fabric for ++ * errors related to the WWPN. ++ */ ++ ++/*? ++ * Text: "%s: Attaching the name server port to the FCP device failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The zfcp device driver could not attach the name server port of the fibre ++ * channel fabric to an FCP device. A possible cause of this problem is ++ * memory constraints. ++ * User action: ++ * Set the FCP device offline, free some memory, then set the FCP device online ++ * again. If this does not resolve the problem, reboot Linux and try again to ++ * set the FCP device online. ++ */ ++ ++/*? ++ * Text: "%s: Registering unit 0x%016Lx on port 0x%016Lx failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: WWPN ++ * Description: ++ * The Linux kernel could not allocate enough memory to register the SCSI ++ * device at the indicated LUN with the SCSI stack. The SCSI device is not ++ * available. ++ * User action: ++ * Free some memory then detach the LUN and attach it again. ++ */ ++ ++/*? ++ * Text: "%s: Registering port 0x%016Lx failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: WWPN ++ * Description: ++ * The Linux kernel could not allocate enough memory to register the ++ * remote port with the indicated WWPN with the SCSI stack. The remote ++ * port is not available. ++ * User action: ++ * Free some memory and trigger the rescan for ports. ++ */ ++ ++/*? ++ * Text: "%s: A QDIO problem occurred\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * QDIO reported a problem to the zfcp device driver. The zfcp device driver ++ * tries to recover this problem. ++ * User action: ++ * Check for related error messages. If this problem occurs frequently, gather ++ * Linux debug data and contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: A QDIO protocol error occurred, operations continue\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The zfcp device driver detected a missing flag in a QDIO queue. The device ++ * driver tries to keep the FCP device operational. ++ * User action: ++ * Check for related error messages. If this problem occurs frequently, gather ++ * Linux debug data, collect the FCP adapter hardware logs, and report the ++ * problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Setting up the QDIO connection to the FCP adapter failed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The zfcp device driver failed to establish a QDIO connection with the FCP ++ * adapter. ++ * User action: ++ * Set the FCP adapter offline or detach it from the Linux system, free some ++ * memory and set the FCP adapter online again or attach it again. If this ++ * problem persists, gather Linux debug data, collect the FCP adapter ++ * hardware logs, and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The FCP adapter reported a problem that cannot be recovered\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP adapter has a problem that cannot be recovered by the zfcp device ++ * driver. The zfcp device driver stopped using the FCP device. ++ * User action: ++ * Gather Linux debug data, collect the FCP adapter hardware logs, and report ++ * this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: There is a wrap plug instead of a fibre channel cable\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP adapter is not physically connected to the fibre channel fabric. ++ * User action: ++ * Remove the wrap plug from the FCP adapter and connect the adapter with the ++ * fibre channel fabric. ++ */ ++ ++/*? ++ * Text: "%s: Access denied to unit 0x%016Lx on port 0x%016Lx\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: WWPN ++ * Description: ++ * The Linux system is not allowed to access the SCSI device at the indicated ++ * LUN. ++ * User action: ++ * Update the access control table of the FCP device to grant the Linux ++ * system access to the LUN or remove the LUN from the Linux system. ++ */ ++ ++/*? ++ * Text: "%s: FCP device not operational because of an unsupported FC class\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP adapter hardware does not support the fibre channel service class ++ * requested by the zfcp device driver. This problem indicates a program error ++ * in the zfcp device driver. ++ * User action: ++ * Gather Linux debug data, collect the FCP adapter hardware logs, and report ++ * this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: 0x%Lx is an ambiguous request identifier\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: request ID ++ * Description: ++ * The FCP adapter reported that it received the same request ID twice. This is ++ * an error. The zfcp device driver stopped using the FCP device. ++ * User action: ++ * Gather Linux debug data, collect the FCP adapter hardware logs, and report ++ * this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: QTCB version 0x%x not supported by FCP adapter (0x%x to 0x%x)\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: requested version ++ * @3: lowest supported version ++ * @4: highest supported version ++ * Description: ++ * See message text. ++ * The queue transfer control block (QTCB) version requested by the zfcp device ++ * driver is not supported by the FCP adapter hardware. ++ * User action: ++ * If the requested version is higher than the highest version supported by the ++ * hardware, install more recent firmware on the FCP adapter. If the requested ++ * version is lower then the lowest version supported by the hardware, upgrade ++ * to a Linux level with a more recent zfcp device driver. ++ */ ++ ++/*? ++ * Text: "%s: The FCP adapter could not log in to the fibre channel fabric\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The fibre channel switch rejected the login request from the FCP adapter. ++ * User action: ++ * Check the fibre channel fabric or switch logs for possible errors. ++ */ ++ ++/*? ++ * Text: "%s: The FCP device is suspended because of a firmware update\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP device is not available while a firmware update is in progress. This ++ * problem is temporary. The FCP device will resume operations when the ++ * firmware update is completed. ++ * User action: ++ * Wait 10 seconds and try the operation again. ++ */ ++ ++/*? ++ * Text: "%s: All NPIV ports on the FCP adapter have been assigned\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The number of N_Port ID Virtualization (NPIV) ports that can be assigned ++ * on an FCP adapter is limited. Once assigned, NPIV ports are not released ++ * automatically but have to be released explicitly through the support ++ * element (SE). ++ * User action: ++ * Identify NPIV ports that have been assigned but are no longer in use and ++ * release them from the SE. ++ */ ++ ++/*? ++ * Text: "%s: The link between the FCP adapter and the FC fabric is down\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP adapter is not usable. Specific error information is not available. ++ * User action: ++ * Check the cabling and the fibre channel fabric configuration. If this ++ * problem persists, gather Linux debug data, collect the FCP adapter ++ * hardware logs, and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Access denied to port 0x%016Lx\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: WWPN ++ * Description: ++ * The Linux system is not allowed to access the remote port with the specified ++ * WWPN. ++ * User action: ++ * Update the access control table of the FCP device to grant the Linux ++ * system access to the WWPN or remove the WWPN from the Linux system. ++ */ ++ ++/*? ++ * Text: "%s: The QTCB type is not supported by the FCP adapter\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The queue transfer control block (QTCB) type requested by the zfcp device ++ * driver is not supported by the FCP adapter hardware. ++ * User action: ++ * Install the latest firmware on your FCP adapter hardware. If this does not ++ * resolve the problem, upgrade to a Linux level with a more recent zfcp device ++ * driver. If the problem persists, contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The error threshold for checksum statistics has been exceeded\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP adapter has reported a large number of bit errors. This might ++ * indicate a problem with the physical components of the fibre channel fabric. ++ * Details about the errors have been written to the HBA trace for the FCP ++ * adapter. ++ * User action: ++ * Check for problems in the fibre channel fabric and ensure that all cables ++ * are properly plugged. ++ */ ++ ++/*? ++ * Text: "%s: The local link has been restored\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * A problem with the connection between the FCP adapter and the adjacent node ++ * on the fibre channel fabric has been resolved. The FCP adapter is now ++ * available again. ++ * User action: ++ * None. ++ */ ++ ++/*? ++ * Text: "%s: Access denied according to ACT rule type %s, rule %d\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: access rule type ++ * @3: access rule ++ * Description: ++ * A rule in the access control table (ACT) for the FCP device denies access ++ * to a remote port or a LUN. ++ * User action: ++ * Examine the access control tables for the FCP device to see if the ++ * specified rule is correct. ++ */ ++ ++/*? ++ * Text: "%s: The mode table on the FCP adapter has been damaged\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * This is an FCP adapter hardware problem. ++ * User action: ++ * Report this problem with FCP hardware logs to IBM support. ++ */ ++ ++/*? ++ * Text: "%s: The adjacent fibre channel node does not support FCP\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The fibre channel switch or storage system that is connected to the FCP ++ * channel does not support the fibre channel protocol (FCP). The zfcp ++ * device driver stopped using the FCP device. ++ * User action: ++ * Check the adjacent fibre channel node. ++ */ ++ ++/*? ++ * Text: "%s: The FCP adapter does not recognize the command 0x%x\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: command ++ * Description: ++ * A command code that was sent from the zfcp device driver to the FCP adapter ++ * is not valid. The zfcp device driver stopped using the FCP device. ++ * User action: ++ * Gather Linux debug data, collect the FCP adapter hardware logs, and report ++ * this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: There is no light signal from the local fibre channel cable\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * There is no signal on the fibre channel cable that connects the FCP adapter ++ * to the fibre channel fabric. ++ * User action: ++ * Ensure that the cable is in place and connected properly to the FCP adapter ++ * and to the adjacent fibre channel switch or storage system. ++ */ ++ ++/*? ++ * Text: "%s: The WWPN assignment file on the FCP adapter has been damaged\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * This is an FCP adapter hardware problem. ++ * User action: ++ * Report this problem with FCP hardware logs to IBM support. ++ */ ++ ++/*? ++ * Text: "%s: The FCP device detected a WWPN that is duplicate or not valid\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * This condition indicates an error in the FCP adapter hardware or in the z/VM ++ * hypervisor. ++ * User action: ++ * Gather Linux debug data, collect the FCP adapter hardware logs, and report ++ * this problem to IBM support. ++ */ ++ ++/*? ++ * Text: "%s: The fibre channel fabric does not support NPIV\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP adapter requires N_Port ID Virtualization (NPIV) from the adjacent ++ * fibre channel node. Either the FCP adapter is connected to a fibre channel ++ * switch that does not support NPIV or the FCP adapter tries to use NPIV in a ++ * point-to-point setup. The connection is not operational. ++ * User action: ++ * Verify that NPIV is correctly used for this connection. Check the FCP adapter ++ * configuration and the fibre channel switch configuration. If necessary, ++ * update the fibre channel switch firmware. ++ */ ++ ++/*? ++ * Text: "%s: The FCP adapter cannot support more NPIV ports\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * N_Port ID Virtualization (NPIV) ports consume physical resources on the FCP ++ * adapter. The FCP adapter resources are exhausted. The connection is not ++ * operational. ++ * User action: ++ * Analyze the number of available NPIV ports and which operating system ++ * instances use them. If necessary, reconfigure your setup to move some ++ * NPIV ports to an FCP adapter with free resources. ++ */ ++ ++/*? ++ * Text: "%s: The adjacent switch cannot support more NPIV ports\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * N_Port ID Virtualization (NPIV) ports consume physical resources. The ++ * resources of the fibre channel switch that is connected to the FCP adapter ++ * are exhausted. The connection is not operational. ++ * User action: ++ * Analyze the number of available NPIV ports on the adjacent fibre channel ++ * switch and how they are used. If necessary, reconfigure your fibre channel ++ * fabric to accommodate the required NPIV ports. ++ */ ++ ++/*? ++ * Text: "%s: 0x%x is not a valid transfer protocol status\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: status information ++ * Description: ++ * The transfer protocol status information reported by the FCP adapter is not ++ * a valid status for the zfcp device driver. The zfcp device driver stopped ++ * using the FCP device. ++ * User action: ++ * Gather Linux debug data, collect the FCP adapter hardware logs, and report ++ * this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Unknown or unsupported arbitrated loop fibre channel topology detected\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The FCP device is connected to a fibre channel arbitrated loop or the FCP adapter ++ * reported an unknown fibre channel topology. The zfcp device driver supports ++ * point-to-point connections and switched fibre channel fabrics but not arbitrated ++ * loop topologies. The FCP device cannot be used. ++ * User action: ++ * Check the fibre channel setup and ensure that only supported topologies are ++ * connected to the FCP adapter. ++ */ ++ ++/*? ++ * Text: "%s: FCP adapter maximum QTCB size (%d bytes) is too small\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: maximum supported size ++ * @3: requested QTCB size ++ * Description: ++ * The queue transfer control block (QTCB) size requested by the zfcp ++ * device driver is not supported by the FCP adapter hardware. ++ * User action: ++ * Update the firmware on your FCP adapter hardware to the latest ++ * available level and update the Linux kernel to the latest supported ++ * level. If the problem persists, contact your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The FCP adapter only supports newer control block versions\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The protocol supported by the FCP adapter is not compatible with the zfcp ++ * device driver. ++ * User action: ++ * Upgrade your Linux kernel to a level that includes a zfcp device driver ++ * with support for the control block version required by your FCP adapter. ++ */ ++ ++/*? ++ * Text: "%s: The FCP adapter only supports older control block versions\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * Description: ++ * The protocol supported by the FCP adapter is not compatible with the zfcp ++ * device driver. ++ * User action: ++ * Install the latest firmware on your FCP adapter. ++ */ ++ ++/*? ++ * Text: "%s: Not enough FCP adapter resources to open remote port 0x%016Lx\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: WWPN ++ * Description: ++ * Each port that is opened consumes physical resources of the FCP adapter to ++ * which it is attached. These resources are exhausted and the specified port ++ * cannot be opened. ++ * User action: ++ * Reduce the total number of remote ports that are attached to the ++ * FCP adapter. ++ */ ++ ++/*? ++ * Text: "%s: LUN 0x%Lx on port 0x%Lx is already in use by CSS%d, MIF Image ID %x\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: remote port WWPN ++ * @4: channel subsystem ID ++ * @5: MIF Image ID of the LPAR ++ * Description: ++ * The SCSI device at the indicated LUN is already in use by another system. ++ * Only one system at a time can use the SCSI device. ++ * User action: ++ * Ensure that the other system stops using the device before trying to use it. ++ */ ++ ++/*? ++ * Text: "%s: No handle is available for LUN 0x%016Lx on port 0x%016Lx\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: WWPN ++ * Description: ++ * The FCP adapter can only open a limited number of SCSI devices. This limit ++ * has been reached and the SCSI device at the indicated LUN cannot be opened. ++ * User action: ++ * Check all SCSI devices opened through the FCP adapter and close some of them. ++ */ ++ ++/*? ++ * Text: "%s: SCSI device at LUN 0x%016Lx on port 0x%016Lx opened read-only\n" ++ * Severity: Informational ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: WWPN ++ * Description: ++ * The access control tables in the FCP adapter allow read-only access for the ++ * LUN. Write access is not permitted for your Linux instance. The SCSI ++ * device has been opened successfully in read-only access mode. ++ * User action: ++ * None if read-only access is sufficient. If you require write access, change ++ * the access control tables in the FCP adapter. ++ */ ++ ++/*? ++ * Text: "%s: Exclusive read-only access not supported (unit 0x%016Lx, port 0x%016Lx)\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: WWPN ++ * Description: ++ * The access configuration specified in the access control tables of the FCP ++ * adapter is not valid. The SCSI device at the indicated LUN cannot be ++ * accessed. ++ * User action: ++ * Change the access control tables in the FCP adapter. ++ */ ++ ++/*? ++ * Text: "%s: Shared read-write access not supported (unit 0x%016Lx, port 0x%016Lx)\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: WWPN ++ * Description: ++ * The access configuration specified in the access control tables of the FCP ++ * adapter is not valid. The SCSI device at the indicated LUN cannot be ++ * accessed. ++ * User action: ++ * Change the access control tables in the FCP adapter. ++ */ ++ ++/*? ++ * Text: "%s: Incorrect direction %d, unit 0x%016Lx on port 0x%016Lx closed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: value in direction field ++ * @3: LUN ++ * @4: WWPN ++ * Description: ++ * The direction field in a SCSI request contains an incorrect value. The zfcp ++ * device driver closed down the SCSI device at the indicated LUN. ++ * User action: ++ * Gather Linux debug data and report this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Incorrect CDB length %d, unit 0x%016Lx on port 0x%016Lx closed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: value in length field ++ * @3: LUN ++ * @4: WWPN ++ * Description: ++ * The control-data-block (CDB) length field in a SCSI request is not valid or ++ * too large for the FCP adapter. The zfcp device driver closed down the SCSI ++ * device at the indicated LUN. ++ * User action: ++ * Gather Linux debug data and report this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Oversize data package, unit 0x%016Lx on port 0x%016Lx closed\n" ++ * Severity: Error ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: LUN ++ * @3: WWPN ++ * Description: ++ * A SCSI request with too much data has been sent to the SCSI device at the ++ * indicated LUN. The FCP adapter cannot handle data packets of this size and ++ * the SCSI device driver closed down the SCSI device. ++ * User action: ++ * Gather Linux debug data and report this problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: Opening WKA port 0x%x failed\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: destination ID of the WKA port ++ * Description: ++ * The FCP adapter rejected a request to open the specified ++ * well-known address (WKA) port. No retry is possible. ++ * User action: ++ * Verify the setup and check if the maximum number of remote ports ++ * used through this adapter is below the maximum allowed. If the ++ * problem persists, gather Linux debug data, collect the FCP adapter ++ * hardware logs, and report the problem to your support organization. ++ */ ++ ++/*? ++ * Text: "%s: The name server reported %d words residual data\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: number of words in residual data ++ * Description: ++ * The fibre channel name server sent too much information about remote ports. ++ * The zfcp device driver did not receive sufficient information to attach all ++ * available remote ports in the SAN. ++ * User action: ++ * Verify that you are running the latest firmware level on the FCP ++ * adapter. Check your SAN setup and consider reducing the number of ports ++ * visible to the FCP adapter by using more restrictive zoning in the SAN. ++ */ ++ ++/*? ++ * Text: "%s: A port opened with WWPN 0x%016Lx returned data that identifies it as WWPN 0x%016Lx\n" ++ * Severity: Warning ++ * Parameter: ++ * @1: bus ID of the zfcp device ++ * @2: expected WWPN ++ * @3: reported WWPN ++ * Description: ++ * A remote port was opened successfully, but it reported an ++ * unexpected WWPN in the returned port login (PLOGI) data. This ++ * condition might have been caused by a change applied to the SAN ++ * configuration while the port was being opened. ++ * User action: ++ * If this condition is only temporary and access to the remote port ++ * is possible, no action is required. If the condition persists, ++ * identify the storage system with the specified WWPN and contact the ++ * support organization of the storage system. ++ */ +--- a/Makefile ++++ b/Makefile +@@ -65,6 +65,20 @@ ifndef KBUILD_CHECKSRC + KBUILD_CHECKSRC = 0 + endif + ++# Call message checker as part of the C compilation ++# ++# Use 'make D=1' to enable checking ++# Use 'make D=2' to create the message catalog ++ ++ifdef D ++ ifeq ("$(origin D)", "command line") ++ KBUILD_KMSG_CHECK = $(D) ++ endif ++endif ++ifndef KBUILD_KMSG_CHECK ++ KBUILD_KMSG_CHECK = 0 ++endif ++ + # Use make M=dir to specify directory of external module to build + # Old syntax make ... SUBDIRS=$PWD is still supported + # Setting the environment variable KBUILD_EXTMOD take precedence +@@ -329,6 +343,7 @@ CHECK = sparse + + CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ + -Wbitwise -Wno-return-void $(CF) ++KMSG_CHECK = $(srctree)/scripts/kmsg-doc + MODFLAGS = -DMODULE + CFLAGS_MODULE = $(MODFLAGS) + AFLAGS_MODULE = $(MODFLAGS) +@@ -371,6 +386,7 @@ export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODU + export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS + export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV + export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE ++export KBUILD_KMSG_CHECK KMSG_CHECK + + # When compiling out-of-tree modules, put MODVERDIR in the module + # tree rather than in the kernel tree. The kernel tree might +--- a/arch/s390/Kconfig ++++ b/arch/s390/Kconfig +@@ -597,6 +597,14 @@ bool "s390 guest support for KVM (EXPERI + virtio transport. If KVM is detected, the virtio console will be + the default console. + ++config KMSG_IDS ++ bool "Kernel message numbers" ++ default y ++ help ++ Select this option if you want to include a message number to the ++ prefix for kernel messages issued by the s390 architecture and ++ driver code. See "Documentation/s390/kmsg.txt" for more details. ++ + config SECCOMP + bool "Enable seccomp to safely compute untrusted bytecode" + depends on PROC_FS +--- a/include/linux/device.h ++++ b/include/linux/device.h +@@ -628,20 +628,40 @@ extern const char *dev_driver_string(con + printk(level "%s %s: " format , dev_driver_string(dev) , \ + dev_name(dev) , ## arg) + ++/* dev_printk_hash for message documentation */ ++#if defined(__KMSG_CHECKER) && defined(KMSG_COMPONENT) ++ ++/* generate magic string for scripts/kmsg-doc to parse */ ++#define dev_printk_hash(level, dev, format, arg...) \ ++ __KMSG_DEV(level _FMT_ format _ARGS_ dev, ## arg _END_) ++ ++#elif defined(CONFIG_KMSG_IDS) && defined(KMSG_COMPONENT) ++ ++int printk_dev_hash(const char *, const char *, const char *, ...); ++#define dev_printk_hash(level, dev, format, arg...) \ ++ printk_dev_hash(level "%s.%06x: ", dev_driver_string(dev), \ ++ "%s: " format, dev_name(dev), ## arg) ++ ++#else /* !defined(CONFIG_KMSG_IDS) */ ++ ++#define dev_printk_hash dev_printk ++ ++#endif ++ + #define dev_emerg(dev, format, arg...) \ +- dev_printk(KERN_EMERG , dev , format , ## arg) ++ dev_printk_hash(KERN_EMERG , dev , format , ## arg) + #define dev_alert(dev, format, arg...) \ +- dev_printk(KERN_ALERT , dev , format , ## arg) ++ dev_printk_hash(KERN_ALERT , dev , format , ## arg) + #define dev_crit(dev, format, arg...) \ +- dev_printk(KERN_CRIT , dev , format , ## arg) ++ dev_printk_hash(KERN_CRIT , dev , format , ## arg) + #define dev_err(dev, format, arg...) \ +- dev_printk(KERN_ERR , dev , format , ## arg) ++ dev_printk_hash(KERN_ERR , dev , format , ## arg) + #define dev_warn(dev, format, arg...) \ +- dev_printk(KERN_WARNING , dev , format , ## arg) ++ dev_printk_hash(KERN_WARNING , dev , format , ## arg) + #define dev_notice(dev, format, arg...) \ +- dev_printk(KERN_NOTICE , dev , format , ## arg) ++ dev_printk_hash(KERN_NOTICE , dev , format , ## arg) + #define dev_info(dev, format, arg...) \ +- dev_printk(KERN_INFO , dev , format , ## arg) ++ dev_printk_hash(KERN_INFO , dev , format , ## arg) + + #if defined(DEBUG) + #define dev_dbg(dev, format, arg...) \ +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -387,22 +387,41 @@ static inline char *pack_hex_byte(char * + #define pr_fmt(fmt) fmt + #endif + ++#if defined(__KMSG_CHECKER) && defined(KMSG_COMPONENT) ++ ++/* generate magic string for scripts/kmsg-doc to parse */ ++#define pr_printk_hash(level, format, ...) \ ++ __KMSG_PRINT(level _FMT_ format _ARGS_ ##__VA_ARGS__ _END_) ++ ++#elif defined(CONFIG_KMSG_IDS) && defined(KMSG_COMPONENT) ++ ++int printk_hash(const char *, const char *, ...); ++#define pr_printk_hash(level, format, ...) \ ++ printk_hash(level KMSG_COMPONENT ".%06x" ": ", format, ##__VA_ARGS__) ++ ++#else /* !defined(CONFIG_KMSG_IDS) */ ++ ++#define pr_printk_hash(level, format, ...) \ ++ printk(level pr_fmt(format), ##__VA_ARGS__) ++ ++#endif ++ + #define pr_emerg(fmt, ...) \ +- printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__) ++ pr_printk_hash(KERN_EMERG, fmt, ##__VA_ARGS__) + #define pr_alert(fmt, ...) \ +- printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__) ++ pr_printk_hash(KERN_ALERT, fmt, ##__VA_ARGS__) + #define pr_crit(fmt, ...) \ +- printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) ++ pr_printk_hash(KERN_CRIT, fmt, ##__VA_ARGS__) + #define pr_err(fmt, ...) \ +- printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) ++ pr_printk_hash(KERN_ERR, fmt, ##__VA_ARGS__) + #define pr_warning(fmt, ...) \ +- printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) ++ pr_printk_hash(KERN_WARNING, fmt, ##__VA_ARGS__) + #define pr_notice(fmt, ...) \ +- printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) ++ pr_printk_hash(KERN_NOTICE, fmt, ##__VA_ARGS__) + #define pr_info(fmt, ...) \ +- printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) ++ pr_printk_hash(KERN_INFO, fmt, ##__VA_ARGS__) + #define pr_cont(fmt, ...) \ +- printk(KERN_CONT fmt, ##__VA_ARGS__) ++ pr_printk_hash(KERN_CONT, fmt, ##__VA_ARGS__) + + /* pr_devel() should produce zero code unless DEBUG is defined */ + #ifdef DEBUG +--- a/kernel/printk.c ++++ b/kernel/printk.c +@@ -36,6 +36,8 @@ + #include + #include + #include ++#include ++#include + + #include + +@@ -1534,3 +1536,46 @@ void kmsg_dump(enum kmsg_dump_reason rea + spin_unlock_irqrestore(&dump_list_lock, flags); + } + #endif ++ ++#if defined CONFIG_PRINTK && defined CONFIG_KMSG_IDS ++ ++/** ++ * printk_hash - print a kernel message include a hash over the message ++ * @prefix: message prefix including the ".%06x" for the hash ++ * @fmt: format string ++ */ ++asmlinkage int printk_hash(const char *prefix, const char *fmt, ...) ++{ ++ va_list args; ++ int r; ++ ++ r = printk(prefix, jhash(fmt, strlen(fmt), 0) & 0xffffff); ++ va_start(args, fmt); ++ r += vprintk(fmt, args); ++ va_end(args); ++ ++ return r; ++} ++EXPORT_SYMBOL(printk_hash); ++ ++/** ++ * printk_dev_hash - print a kernel message include a hash over the message ++ * @prefix: message prefix including the ".%06x" for the hash ++ * @dev: device this printk is all about ++ * @fmt: format string ++ */ ++asmlinkage int printk_dev_hash(const char *prefix, const char *driver_name, ++ const char *fmt, ...) ++{ ++ va_list args; ++ int r; ++ ++ r = printk(prefix, driver_name, jhash(fmt, strlen(fmt), 0) & 0xffffff); ++ va_start(args, fmt); ++ r += vprintk(fmt, args); ++ va_end(args); ++ ++ return r; ++} ++EXPORT_SYMBOL(printk_dev_hash); ++#endif +--- a/scripts/Makefile.build ++++ b/scripts/Makefile.build +@@ -229,12 +229,14 @@ endef + # Built-in and composite module parts + $(obj)/%.o: $(src)/%.c FORCE + $(call cmd,force_checksrc) ++ $(call cmd,force_check_kmsg) + $(call if_changed_rule,cc_o_c) + + # Single-part modules are special since we need to mark them in $(MODVERDIR) + + $(single-used-m): $(obj)/%.o: $(src)/%.c FORCE + $(call cmd,force_checksrc) ++ $(call cmd,force_check_kmsg) + $(call if_changed_rule,cc_o_c) + @{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod) + +@@ -358,6 +360,18 @@ $(multi-used-m) : %.o: $(multi-objs-m) F + + targets += $(multi-used-y) $(multi-used-m) + ++# kmsg check tool ++ifneq ($(KBUILD_KMSG_CHECK),0) ++ ifeq ($(KBUILD_KMSG_CHECK),2) ++ kmsg_cmd := print ++ quiet_cmd_force_check_kmsg = KMSG_PRINT $< ++ $(shell [ -d $(objtree)/man ] || mkdir -p $(objtree)/man) ++ else ++ kmsg_cmd := check ++ quiet_cmd_force_check_kmsg = KMSG_CHECK $< ++ endif ++ cmd_force_check_kmsg = $(KMSG_CHECK) $(kmsg_cmd) $(CC) $(c_flags) $< ; ++endif + + # Descending + # --------------------------------------------------------------------------- +--- /dev/null ++++ b/scripts/kmsg-doc +@@ -0,0 +1,479 @@ ++#!/usr/bin/perl -w ++# ++# kmsg kernel messages check and print tool. ++# ++# To check the source code for missing messages the script is called ++# with check, the name compiler and the compile parameters ++# kmsg-doc check $(CC) $(c_flags) $< ++# To create man pages for the messages the script is called with ++# kmsg-doc print $(CC) $(c_flags) $< ++# ++# Copyright IBM Corp. 2008 ++# Author(s): Martin Schwidefsky ++# Michael Holzheu ++# ++ ++use Cwd; ++use Switch; ++use bigint; ++ ++my $errors = 0; ++my $warnings = 0; ++my $srctree = ""; ++my $objtree = ""; ++my $kmsg_count = 0; ++ ++sub remove_quotes($) ++{ ++ my ($string) = @_; ++ my $inside = 0; ++ my $slash = 0; ++ my $result = ""; ++ ++ foreach my $str (split(/([\\"])/, $string)) { ++ if ($inside && ($str ne "\"" || $slash)) { ++ $result .= $str; ++ } ++ # Check for backslash before quote ++ if ($str eq "\"") { ++ if (!$slash) { ++ $inside = !$inside; ++ } ++ $slash = 0; ++ } elsif ($str eq "\\") { ++ $slash = !$slash; ++ } elsif ($str ne "") { ++ $slash = 0; ++ } ++ } ++ return $result; ++} ++ ++sub string_to_bytes($) ++{ ++ my ($string) = @_; ++ my %is_escape = ('"', 0x22, '\'', 0x27, 'n', 0x0a, 'r', 0x0d, 'b', 0x08, ++ 't', 0x09, 'f', 0x0c, 'a', 0x07, 'v', 0x0b, '?', 0x3f); ++ my (@ar, $slash, $len); ++ ++ # scan string, interpret backslash escapes and write bytes to @ar ++ $len = 0; ++ foreach my $ch (split(//, $string)) { ++ if ($ch eq '\\') { ++ $slash = !$slash; ++ if (!$slash) { ++ $ar[$len] = ord('\\'); ++ $len++; ++ } ++ } elsif ($slash && defined $is_escape{$ch}) { ++ # C99 backslash escapes: \\ \" \' \n \r \b \t \f \a \v \? ++ $ar[$len] = $is_escape{$ch}; ++ $len++; ++ $slash = 0; ++ } elsif ($slash) { ++ # FIXME: C99 backslash escapes \nnn \xhh ++ die("Unknown backslash escape in message $string."); ++ } else { ++ # normal character ++ $ar[$len] = ord($ch); ++ $len++; ++ } ++ } ++ return @ar; ++} ++ ++sub calc_jhash($) ++{ ++ my ($string) = @_; ++ my @ar; ++ my ($a, $b, $c, $i, $length, $len); ++ ++ @ar = string_to_bytes($string); ++ $length = @ar; ++ # add dummy elements to @ar to avoid if then else hell ++ push @ar, (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); ++ $a = 0x9e3779b9; ++ $b = 0x9e3779b9; ++ $c = 0; ++ $i = 0; ++ for ($len = $length + 12; $len >= 12; $len -= 12) { ++ if ($len < 24) { ++ # add length for last round ++ $c += $length; ++ } ++ $a += $ar[$i] + ($ar[$i+1]<<8) + ($ar[$i+2]<<16) + ($ar[$i+3]<<24); ++ $b += $ar[$i+4] + ($ar[$i+5]<<8) + ($ar[$i+6]<<16) + ($ar[$i+7]<<24); ++ if ($len >= 24) { ++ $c += $ar[$i+8] + ($ar[$i+9]<<8) + ($ar[$i+10]<<16) + ($ar[$i+11]<<24); ++ } else { ++ $c += ($ar[$i+8]<<8) + ($ar[$i+9]<<16) + ($ar[$i+10]<<24); ++ } ++ $a &= 0xffffffff; $b &= 0xffffffff; $c &= 0xffffffff; ++ $a -= $b; $a -= $c; $a ^= ($c >> 13); $a &= 0xffffffff; ++ $b -= $c; $b -= $a; $b ^= ($a << 8); $b &= 0xffffffff; ++ $c -= $a; $c -= $b; $c ^= ($b >> 13); $c &= 0xffffffff; ++ $a -= $b; $a -= $c; $a ^= ($c >> 12); $a &= 0xffffffff; ++ $b -= $c; $b -= $a; $b ^= ($a << 16); $b &= 0xffffffff; ++ $c -= $a; $c -= $b; $c ^= ($b >> 5); $c &= 0xffffffff; ++ $a -= $b; $a -= $c; $a ^= ($c >> 3); $a &= 0xffffffff; ++ $b -= $c; $b -= $a; $b ^= ($a << 10); $b &= 0xffffffff; ++ $c -= $a; $c -= $b; $c ^= ($b >> 15); $c &= 0xffffffff; ++ $i += 12; ++ } ++ return $c; ++} ++ ++sub add_kmsg_desc($$$$$$) ++{ ++ my ($component, $text, $sev, $argv, $desc, $user) = @_; ++ my ($hash, $tag); ++ ++ $text = remove_quotes($text); ++ $hash = substr(sprintf("%08x", calc_jhash($text)), 2, 6); ++ $tag = $component . "." . $hash; ++ ++ if ($kmsg_desc{$tag}) { ++ if ($text ne $kmsg_desc{$tag}->{'TEXT'}) { ++ warn "Duplicate message with tag $tag\n"; ++ warn " --- $kmsg_desc{$tag}->{'TEXT'}\n"; ++ warn " +++ $text\n"; ++ } else { ++ warn "Duplicate message description for \"$text\"\n"; ++ } ++ $errors++; ++ return; ++ } ++ $kmsg_desc{$tag}->{'TEXT'} = $text; ++ $kmsg_desc{$tag}->{'SEV'} = $sev; ++ $kmsg_desc{$tag}->{'ARGV'} = $argv; ++ $kmsg_desc{$tag}->{'DESC'} = $desc; ++ $kmsg_desc{$tag}->{'USER'} = $user; ++} ++ ++sub add_kmsg_print($$$$) ++{ ++ my ($component, $sev, $text, $argv) = @_; ++ my ($hash, $tag, $count, $parm); ++ ++ $text = remove_quotes($text); ++ $hash = substr(sprintf("%08x", calc_jhash($text)), 2, 6); ++ $tag = $component . "." . $hash; ++ ++ # Pretty print severity ++ $sev =~ s/"<0>"/Emerg/; ++ $sev =~ s/"<1>"/Alert/; ++ $sev =~ s/"<2>"/Critical/; ++ $sev =~ s/"<3>"/Error/; ++ $sev =~ s/"<4>"/Warning/; ++ $sev =~ s/"<5>"/Notice/; ++ $sev =~ s/"<6>"/Informational/; ++ $sev =~ s/"<7>"/Debug/; ++ $kmsg_print{$kmsg_count}->{'TAG'} = $tag; ++ $kmsg_print{$kmsg_count}->{'TEXT'} = $text; ++ $kmsg_print{$kmsg_count}->{'SEV'} = $sev; ++ $kmsg_print{$kmsg_count}->{'ARGV'} = $argv; ++ $kmsg_count += 1; ++} ++ ++sub process_source_file($$) ++{ ++ my ($component, $file) = @_; ++ my $state; ++ my ($text, $sev, $argv, $desc, $user); ++ ++ if (!open(FD, "$file")) { ++ return ""; ++ } ++ ++ $state = 0; ++ while () { ++ chomp; ++ # kmsg message component: #define KMSG_COMPONENT "" ++ if (/^#define\s+KMSG_COMPONENT\s+\"(.*)\"[^\"]*$/o) { ++ $component = $1; ++ } ++ if ($state == 0) { ++ # single line kmsg for undocumented messages, format: ++ # /*? Text: "" */ ++ if (/^\s*\/\*\?\s*Text:\s*(\".*\")\s*\*\/\s*$/o) { ++ add_kmsg_desc($component, $1, "", "", "", ""); ++ } ++ # kmsg message start: '/*?' ++ if (/^\s*\/\*\?\s*$/o) { ++ $state = 1; ++ ($text, $sev, $argv, $desc, $user) = ( "", "", "", "", "" ); ++ } ++ } elsif ($state == 1) { ++ # kmsg message end: ' */' ++ if (/^\s*\*\/\s*/o) { ++ add_kmsg_desc($component, $text, $sev, $argv, $desc, $user); ++ $state = 0; ++ } ++ # kmsg message text: ' * Text: ""' ++ elsif (/^\s*\*\s*Text:\s*(\".*\")\s*$/o) { ++ $text = $1; ++ } ++ # kmsg message severity: ' * Severity: ' ++ elsif (/^\s*\*\s*Severity:\s*(\S*)\s*$/o) { ++ $sev = $1; ++ } ++ # kmsg message parameter: ' * Parameter: ' ++ elsif (/^\s*\*\s*Parameter:\s*(\S*)\s*$/o) { ++ if (!defined($1)) { ++ $argv = ""; ++ } else { ++ $argv = $1; ++ } ++ $state = 2; ++ } ++ # kmsg message description start: ' * Description:' ++ elsif (/^\s*\*\s*Description:\s*(\S*)\s*$/o) { ++ if (!defined($1)) { ++ $desc = ""; ++ } else { ++ $desc = $1; ++ } ++ $state = 3; ++ } ++ # kmsg has unrecognizable lines ++ else { ++ warn "Warning(${file}:$.): Cannot understand $_"; ++ $warnings++; ++ $state = 0; ++ } ++ } elsif ($state == 2) { ++ # kmsg message end: ' */' ++ if (/^\s*\*\//o) { ++ warn "Warning(${file}:$.): Missing description, skipping message"; ++ $warnings++; ++ $state = 0; ++ } ++ # kmsg message description start: ' * Description:' ++ elsif (/^\s*\*\s*Description:\s*$/o) { ++ $desc = $1; ++ $state = 3; ++ } ++ # kmsg message parameter line: ' * ' ++ elsif (/^\s*\*(.*)$/o) { ++ $argv .= "\n" . $1; ++ } else { ++ warn "Warning(${file}:$.): Cannot understand $_"; ++ $warnings++; ++ $state = 0; ++ } ++ } elsif ($state == 3) { ++ # kmsg message end: ' */' ++ if (/^\s*\*\/\s*/o) { ++ add_kmsg_desc($component, $text, $sev, $argv, $desc, $user); ++ $state = 0; ++ } ++ # kmsg message description start: ' * User action:' ++ elsif (/^\s*\*\s*User action:\s*$/o) { ++ $user = $1; ++ $state = 4; ++ } ++ # kmsg message description line: ' * ' ++ elsif (/^\s*\*\s*(.*)$/o) { ++ $desc .= "\n" . $1; ++ } else { ++ warn "Warning(${file}:$.): Cannot understand $_"; ++ $warnings++; ++ $state = 0; ++ } ++ } elsif ($state == 4) { ++ # kmsg message end: ' */' ++ if (/^\s*\*\/\s*/o) { ++ add_kmsg_desc($component, $text, $sev, $argv, $desc, $user); ++ $state = 0; ++ } ++ # kmsg message user action line: ' * ' ++ elsif (/^\s*\*\s*(.*)$/o) { ++ $user .= "\n" . $1; ++ } else { ++ warn "Warning(${file}:$.): Cannot understand $_"; ++ $warnings++; ++ $state = 0; ++ } ++ } ++ } ++ return $component; ++} ++ ++sub process_cpp_file($$$$) ++{ ++ my ($cc, $options, $file, $component) = @_; ++ ++ open(FD, "$cc $gcc_options|") or die ("Preprocessing failed."); ++ ++ while () { ++ chomp; ++ if (/.*__KMSG_PRINT\(\s*(\S*)\s*_FMT_(.*)_ARGS_\s*(.*)?_END_\s*\)/o) { ++ if ($component ne "") { ++ add_kmsg_print($component, $1, $2, $3); ++ } else { ++ warn "Error(${file}:$.): kmsg without component\n"; ++ $errors++; ++ } ++ } elsif (/.*__KMSG_DEV\(\s*(\S*)\s*_FMT_(.*)_ARGS_\s*(.*)?_END_\s*\)/o) { ++ if ($component ne "") { ++ add_kmsg_print($component, $1, "\"%s: \"" . $2, $3); ++ } else { ++ warn "Error(${file}:$.): kmsg without component\n"; ++ $errors++; ++ } ++ } ++ } ++} ++ ++sub check_messages($) ++{ ++ my $component = "@_"; ++ my $failed = 0; ++ ++ for ($i = 0; $i < $kmsg_count; $i++) { ++ $tag = $kmsg_print{$i}->{'TAG'}; ++ if (!defined($kmsg_desc{$tag})) { ++ add_kmsg_desc($component, ++ "\"" . $kmsg_print{$i}->{'TEXT'} . "\"", ++ $kmsg_print{$i}->{'SEV'}, ++ $kmsg_print{$i}->{'ARGV'}, ++ "Please insert description here", ++ "What is the user supposed to do"); ++ $kmsg_desc{$tag}->{'CHECK'} = 1; ++ $failed = 1; ++ warn "$component: Missing description for: ". ++ $kmsg_print{$i}->{'TEXT'}."\n"; ++ $errors++; ++ next; ++ } ++ if ($kmsg_desc{$tag}->{'SEV'} ne "" && ++ $kmsg_desc{$tag}->{'SEV'} ne $kmsg_print{$i}->{'SEV'}) { ++ warn "Message severity mismatch for \"$kmsg_print{$i}->{'TEXT'}\"\n"; ++ warn " --- $kmsg_desc{$tag}->{'SEV'}\n"; ++ warn " +++ $kmsg_print{$i}->{'SEV'}\n"; ++ } ++ } ++ return $failed; ++} ++ ++sub print_templates() ++{ ++ print "Templates for missing messages:\n"; ++ foreach $tag ( sort { $kmsg_desc{$a} <=> $kmsg_desc{$b} } keys %kmsg_desc ) { ++ if (!defined($kmsg_desc{$tag}->{'CHECK'})) { ++ next; ++ } ++ print "/*?\n"; ++ print " * Text: \"$kmsg_desc{$tag}->{'TEXT'}\"\n"; ++ print " * Severity: $kmsg_desc{$tag}->{'SEV'}\n"; ++ $argv = $kmsg_desc{$tag}->{'ARGV'}; ++ if ($argv ne "") { ++ print " * Parameter:\n"; ++ @parms = split(/\s*,\s*/,$kmsg_desc{$tag}->{'ARGV'}); ++ $count = 0; ++ foreach $parm (@parms) { ++ $count += 1; ++ if (!($parm eq "")) { ++ print " * \@$count: $parm\n"; ++ } ++ } ++ } ++ print " * Description:\n"; ++ print " * $kmsg_desc{$tag}->{'DESC'}\n"; ++ print " * User action:\n"; ++ print " * $kmsg_desc{$tag}->{'USER'}\n"; ++ print " */\n\n"; ++ } ++} ++ ++sub write_man_pages() ++{ ++ my ($i, $file); ++ ++ for ($i = 0; $i < $kmsg_count; $i++) { ++ $tag = $kmsg_print{$i}->{'TAG'}; ++ if (!defined($kmsg_desc{$tag}) || ++ defined($kmsg_desc{$tag}->{'CHECK'}) || ++ $kmsg_desc{$tag}->{'DESC'} eq "") { ++ next; ++ } ++ $file = $objtree . "man/" . $tag . ".9"; ++ if (!open(WR, ">$file")) { ++ warn "Error: Cannot open file $file\n"; ++ $errors++; ++ return; ++ } ++ print WR ".TH \"$tag\" 9 \"Linux Messages\" LINUX\n"; ++ print WR ".SH Message\n"; ++ print WR $tag . ": " . $kmsg_desc{$tag}->{'TEXT'} . "\n"; ++ print WR ".SH Severity\n"; ++ print WR "$kmsg_desc{$tag}->{'SEV'}\n"; ++ $argv = $kmsg_desc{$tag}->{'ARGV'}; ++ if ($argv ne "") { ++ print WR ".SH Parameters\n"; ++ @parms = split(/\s*\n\s*/,$kmsg_desc{$tag}->{'ARGV'}); ++ foreach $parm (@parms) { ++ $parm =~ s/^\s*(.*)\s*$/$1/; ++ if (!($parm eq "")) { ++ print WR "$parm\n\n"; ++ } ++ } ++ } ++ print WR ".SH Description"; ++ print WR "$kmsg_desc{$tag}->{'DESC'}\n"; ++ $user = $kmsg_desc{$tag}->{'USER'}; ++ if ($user ne "") { ++ print WR ".SH User action"; ++ print WR "$user\n"; ++ } ++ } ++} ++ ++if (defined($ENV{'srctree'})) { ++ $srctree = "$ENV{'srctree'}" . "/"; ++} else { ++ $srctree = getcwd; ++} ++ ++if (defined($ENV{'objtree'})) { ++ $objtree = "$ENV{'objtree'}" . "/"; ++} else { ++ $objtree = getcwd; ++} ++ ++if (defined($ENV{'SRCARCH'})) { ++ $srcarch = "$ENV{'SRCARCH'}" . "/"; ++} else { ++ print "kmsg-doc called without a valid \$SRCARCH\n"; ++ exit 1; ++} ++ ++$option = shift; ++ ++$cc = shift; ++$gcc_options = "-E -D __KMSG_CHECKER "; ++foreach $tmp (@ARGV) { ++ $tmp =~ s/\(/\\\(/; ++ $tmp =~ s/\)/\\\)/; ++ $gcc_options .= " $tmp"; ++ $filename = $tmp; ++} ++ ++$component = process_source_file("", $filename); ++if ($component ne "") { ++ process_source_file($component, $srctree . "Documentation/kmsg/" . ++ $srcarch . $component); ++ process_source_file($component, $srctree . "Documentation/kmsg/" . ++ $component); ++} ++ ++process_cpp_file($cc, $gcc_options, $filename, $component); ++if ($option eq "check") { ++ if (check_messages($component)) { ++ print_templates(); ++ } ++} elsif ($option eq "print") { ++ write_man_pages(); ++} ++ ++exit($errors); diff --git a/patches.arch/x86-apic-force-bigsmp-apic-on-IBM-EXA3-4.patch b/patches.arch/x86-apic-force-bigsmp-apic-on-IBM-EXA3-4.patch new file mode 100644 index 0000000..a3d3570 --- /dev/null +++ b/patches.arch/x86-apic-force-bigsmp-apic-on-IBM-EXA3-4.patch @@ -0,0 +1,87 @@ +From: IBM +Subject: Use apic=bigsmp on specific xseries machines +References: bnc#440497 +Patch-Mainline: not yet + +Signed-off-by: Thomas Renninger + + + arch/x86/kernel/apic/bigsmp_32.c | 30 +++++++++++++++++++++++++++--- + arch/x86/kernel/apic/probe_32.c | 4 ++-- + 2 files changed, 29 insertions(+), 5 deletions(-) + +--- a/arch/x86/kernel/apic/bigsmp_32.c ++++ b/arch/x86/kernel/apic/bigsmp_32.c +@@ -156,7 +156,7 @@ static void bigsmp_send_IPI_all(int vect + + static int dmi_bigsmp; /* can be set by dmi scanners */ + +-static int hp_ht_bigsmp(const struct dmi_system_id *d) ++static int force_bigsmp_apic(const struct dmi_system_id *d) + { + printk(KERN_NOTICE "%s detected: force use of apic=bigsmp\n", d->ident); + dmi_bigsmp = 1; +@@ -166,17 +166,41 @@ static int hp_ht_bigsmp(const struct dmi + + + static const struct dmi_system_id bigsmp_dmi_table[] = { +- { hp_ht_bigsmp, "HP ProLiant DL760 G2", ++ { force_bigsmp_apic, "HP ProLiant DL760 G2", + { DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_BIOS_VERSION, "P44-"), + } + }, + +- { hp_ht_bigsmp, "HP ProLiant DL740", ++ { force_bigsmp_apic, "HP ProLiant DL740", + { DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_BIOS_VERSION, "P47-"), + } + }, ++ ++ { force_bigsmp_apic, "IBM x260 / x366 / x460", ++ { DMI_MATCH(DMI_BIOS_VENDOR, "IBM"), ++ DMI_MATCH(DMI_BIOS_VERSION, "-[ZT"), ++ } ++ }, ++ ++ { force_bigsmp_apic, "IBM x3800 / x3850 / x3950", ++ { DMI_MATCH(DMI_BIOS_VENDOR, "IBM"), ++ DMI_MATCH(DMI_BIOS_VERSION, "-[ZU"), ++ } ++ }, ++ ++ { force_bigsmp_apic, "IBM x3800 / x3850 / x3950", ++ { DMI_MATCH(DMI_BIOS_VENDOR, "IBM"), ++ DMI_MATCH(DMI_BIOS_VERSION, "-[ZS"), ++ } ++ }, ++ ++ { force_bigsmp_apic, "IBM x3850 M2 / x3950 M2", ++ { DMI_MATCH(DMI_BIOS_VENDOR, "IBM"), ++ DMI_MATCH(DMI_BIOS_VERSION, "-[A3"), ++ } ++ }, + { } /* NULL entry stops DMI scanning */ + }; + +--- a/arch/x86/kernel/apic/probe_32.c ++++ b/arch/x86/kernel/apic/probe_32.c +@@ -267,7 +267,7 @@ generic_mps_oem_check(struct mpc_table * + if (!apic_probe[i]->mps_oem_check(mpc, oem, productid)) + continue; + +- if (!cmdline_apic) { ++ if (!cmdline_apic && apic == &apic_default) { + apic = apic_probe[i]; + printk(KERN_INFO "Switched to APIC driver `%s'.\n", + apic->name); +@@ -287,7 +287,7 @@ int __init default_acpi_madt_oem_check(c + if (!apic_probe[i]->acpi_madt_oem_check(oem_id, oem_table_id)) + continue; + +- if (!cmdline_apic) { ++ if (!cmdline_apic && apic == &apic_default) { + apic = apic_probe[i]; + printk(KERN_INFO "Switched to APIC driver `%s'.\n", + apic->name); diff --git a/patches.arch/x86-cpu-add-amd-core-boosting-feature-flag-to-proc-cpuinfo b/patches.arch/x86-cpu-add-amd-core-boosting-feature-flag-to-proc-cpuinfo new file mode 100644 index 0000000..982f4e0 --- /dev/null +++ b/patches.arch/x86-cpu-add-amd-core-boosting-feature-flag-to-proc-cpuinfo @@ -0,0 +1,49 @@ +From: Borislav Petkov +Date: Wed, 31 Mar 2010 19:56:41 +0000 (+0200) +Subject: x86, cpu: Add AMD core boosting feature flag to /proc/cpuinfo +Git-commit: 5958f1d5d722df7a9e5d129676614a8e5219bacd +Patch-mainline: 2.6.35-rc1 + +x86, cpu: Add AMD core boosting feature flag to /proc/cpuinfo + +By semi-popular demand, this adds the Core Performance Boost feature +flag to /proc/cpuinfo. Possible use case for this is userspace tools +like cpufreq-aperf, for example, so that they don't have to jump through +hoops of accessing "/dev/cpu/%d/cpuid" in order to check for CPB hw +support, or call cpuid from userspace. + +Signed-off-by: Borislav Petkov +LKML-Reference: <1270065406-1814-2-git-send-email-bp@amd64.org> +Reviewed-by: Thomas Renninger +Signed-off-by: H. Peter Anvin +Acked-by: Jeff Mahoney +--- + + arch/x86/include/asm/cpufeature.h | 1 + + arch/x86/kernel/cpu/addon_cpuid_features.c | 5 +++-- + 2 files changed, 4 insertions(+), 2 deletions(-) + +--- a/arch/x86/include/asm/cpufeature.h ++++ b/arch/x86/include/asm/cpufeature.h +@@ -161,6 +161,7 @@ + */ + #define X86_FEATURE_IDA (7*32+ 0) /* Intel Dynamic Acceleration */ + #define X86_FEATURE_ARAT (7*32+ 1) /* Always Running APIC Timer */ ++#define X86_FEATURE_CPB (7*32+ 2) /* AMD Core Performance Boost */ + + /* Virtualization flags: Linux defined */ + #define X86_FEATURE_TPR_SHADOW (8*32+ 0) /* Intel TPR Shadow */ +--- a/arch/x86/kernel/cpu/addon_cpuid_features.c ++++ b/arch/x86/kernel/cpu/addon_cpuid_features.c +@@ -30,8 +30,9 @@ void __cpuinit init_scattered_cpuid_feat + const struct cpuid_bit *cb; + + static const struct cpuid_bit __cpuinitconst cpuid_bits[] = { +- { X86_FEATURE_IDA, CR_EAX, 1, 0x00000006 }, +- { X86_FEATURE_ARAT, CR_EAX, 2, 0x00000006 }, ++ { X86_FEATURE_IDA, CR_EAX, 1, 0x00000006 }, ++ { X86_FEATURE_ARAT, CR_EAX, 2, 0x00000006 }, ++ { X86_FEATURE_CPB, CR_EDX, 9, 0x80000007 }, + { X86_FEATURE_NPT, CR_EDX, 0, 0x8000000a }, + { X86_FEATURE_LBRV, CR_EDX, 1, 0x8000000a }, + { X86_FEATURE_SVML, CR_EDX, 2, 0x8000000a }, diff --git a/patches.arch/x86-cpufreq-add-aperf-mperf-support-for-amd-processors b/patches.arch/x86-cpufreq-add-aperf-mperf-support-for-amd-processors new file mode 100644 index 0000000..990f7b1 --- /dev/null +++ b/patches.arch/x86-cpufreq-add-aperf-mperf-support-for-amd-processors @@ -0,0 +1,222 @@ +From: Mark Langsdorf +Date: Thu, 18 Mar 2010 17:41:46 +0000 (+0100) +Subject: x86, cpufreq: Add APERF/MPERF support for AMD processors +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-tip.git +Git-commit: a2fed573f065e526bfd5cbf26e5491973d9e9aaa +References: bnc#602209 +Patch-mainline: 2.6.35-rc1 + +x86, cpufreq: Add APERF/MPERF support for AMD processors + +Starting with model 10 of Family 0x10, AMD processors may have +support for APERF/MPERF. Add support for identifying it and using +it within cpufreq. Move the APERF/MPERF functions out of the +acpi-cpufreq code and into their own file so they can easily be +shared. + +Signed-off-by: Mark Langsdorf +LKML-Reference: <20100401141956.GA1930@aftab> +Signed-off-by: Borislav Petkov +Reviewed-by: Thomas Renninger +Signed-off-by: H. Peter Anvin +Acked-by: Jeff Mahoney +--- + + arch/x86/kernel/cpu/cpufreq/Makefile | 4 +- + arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 44 +------------------------ + arch/x86/kernel/cpu/cpufreq/mperf.c | 51 +++++++++++++++++++++++++++++ + arch/x86/kernel/cpu/cpufreq/mperf.h | 9 +++++ + arch/x86/kernel/cpu/cpufreq/powernow-k8.c | 8 ++++ + 5 files changed, 72 insertions(+), 44 deletions(-) + +--- a/arch/x86/kernel/cpu/cpufreq/Makefile ++++ b/arch/x86/kernel/cpu/cpufreq/Makefile +@@ -2,8 +2,8 @@ + # K8 systems. ACPI is preferred to all other hardware-specific drivers. + # speedstep-* is preferred over p4-clockmod. + +-obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o +-obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o ++obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o mperf.o ++obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o mperf.o + obj-$(CONFIG_X86_PCC_CPUFREQ) += pcc-cpufreq.o + obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o + obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o +--- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c ++++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include "mperf.h" + + #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ + "acpi-cpufreq", msg) +@@ -70,8 +71,6 @@ struct acpi_cpufreq_data { + + static DEFINE_PER_CPU(struct acpi_cpufreq_data *, acfreq_data); + +-static DEFINE_PER_CPU(struct aperfmperf, acfreq_old_perf); +- + /* acpi_perf_data is a pointer to percpu data. */ + static struct acpi_processor_performance *acpi_perf_data; + +@@ -239,45 +238,6 @@ static u32 get_cur_val(const struct cpum + return cmd.val; + } + +-/* Called via smp_call_function_single(), on the target CPU */ +-static void read_measured_perf_ctrs(void *_cur) +-{ +- struct aperfmperf *am = _cur; +- +- get_aperfmperf(am); +-} +- +-/* +- * Return the measured active (C0) frequency on this CPU since last call +- * to this function. +- * Input: cpu number +- * Return: Average CPU frequency in terms of max frequency (zero on error) +- * +- * We use IA32_MPERF and IA32_APERF MSRs to get the measured performance +- * over a period of time, while CPU is in C0 state. +- * IA32_MPERF counts at the rate of max advertised frequency +- * IA32_APERF counts at the rate of actual CPU frequency +- * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and +- * no meaning should be associated with absolute values of these MSRs. +- */ +-static unsigned int get_measured_perf(struct cpufreq_policy *policy, +- unsigned int cpu) +-{ +- struct aperfmperf perf; +- unsigned long ratio; +- unsigned int retval; +- +- if (smp_call_function_single(cpu, read_measured_perf_ctrs, &perf, 1)) +- return 0; +- +- ratio = calc_aperfmperf_ratio(&per_cpu(acfreq_old_perf, cpu), &perf); +- per_cpu(acfreq_old_perf, cpu) = perf; +- +- retval = (policy->cpuinfo.max_freq * ratio) >> APERFMPERF_SHIFT; +- +- return retval; +-} +- + static unsigned int get_cur_freq_on_cpu(unsigned int cpu) + { + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, cpu); +@@ -699,7 +659,7 @@ static int acpi_cpufreq_cpu_init(struct + + /* Check for APERF/MPERF support in hardware */ + if (cpu_has(c, X86_FEATURE_APERFMPERF)) +- acpi_cpufreq_driver.getavg = get_measured_perf; ++ acpi_cpufreq_driver.getavg = cpufreq_get_measured_perf; + + dprintk("CPU%u - ACPI performance management activated.\n", cpu); + for (i = 0; i < perf->state_count; i++) +--- /dev/null ++++ b/arch/x86/kernel/cpu/cpufreq/mperf.c +@@ -0,0 +1,51 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mperf.h" ++ ++static DEFINE_PER_CPU(struct aperfmperf, acfreq_old_perf); ++ ++/* Called via smp_call_function_single(), on the target CPU */ ++static void read_measured_perf_ctrs(void *_cur) ++{ ++ struct aperfmperf *am = _cur; ++ ++ get_aperfmperf(am); ++} ++ ++/* ++ * Return the measured active (C0) frequency on this CPU since last call ++ * to this function. ++ * Input: cpu number ++ * Return: Average CPU frequency in terms of max frequency (zero on error) ++ * ++ * We use IA32_MPERF and IA32_APERF MSRs to get the measured performance ++ * over a period of time, while CPU is in C0 state. ++ * IA32_MPERF counts at the rate of max advertised frequency ++ * IA32_APERF counts at the rate of actual CPU frequency ++ * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and ++ * no meaning should be associated with absolute values of these MSRs. ++ */ ++unsigned int cpufreq_get_measured_perf(struct cpufreq_policy *policy, ++ unsigned int cpu) ++{ ++ struct aperfmperf perf; ++ unsigned long ratio; ++ unsigned int retval; ++ ++ if (smp_call_function_single(cpu, read_measured_perf_ctrs, &perf, 1)) ++ return 0; ++ ++ ratio = calc_aperfmperf_ratio(&per_cpu(acfreq_old_perf, cpu), &perf); ++ per_cpu(acfreq_old_perf, cpu) = perf; ++ ++ retval = (policy->cpuinfo.max_freq * ratio) >> APERFMPERF_SHIFT; ++ ++ return retval; ++} ++EXPORT_SYMBOL_GPL(cpufreq_get_measured_perf); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/x86/kernel/cpu/cpufreq/mperf.h +@@ -0,0 +1,9 @@ ++/* ++ * (c) 2010 Advanced Micro Devices, Inc. ++ * Your use of this code is subject to the terms and conditions of the ++ * GNU general public license version 2. See "COPYING" or ++ * http://www.gnu.org/licenses/gpl.html ++ */ ++ ++unsigned int cpufreq_get_measured_perf(struct cpufreq_policy *policy, ++ unsigned int cpu); +--- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c ++++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +@@ -45,6 +45,7 @@ + #define PFX "powernow-k8: " + #define VERSION "version 2.20.00" + #include "powernow-k8.h" ++#include "mperf.h" + + /* serialize freq changes */ + static DEFINE_MUTEX(fidvid_mutex); +@@ -57,6 +58,8 @@ static int cpu_family = CPU_OPTERON; + static bool cpb_capable, cpb_enabled; + static struct msr *msrs; + ++static struct cpufreq_driver cpufreq_amd64_driver; ++ + #ifndef CONFIG_SMP + static inline const struct cpumask *cpu_core_mask(int cpu) + { +@@ -1252,6 +1255,7 @@ static int __cpuinit powernowk8_cpu_init + struct powernow_k8_data *data; + struct init_on_cpu init_on_cpu; + int rc; ++ struct cpuinfo_x86 *c = &cpu_data(pol->cpu); + + if (!cpu_online(pol->cpu)) + return -ENODEV; +@@ -1326,6 +1330,10 @@ static int __cpuinit powernowk8_cpu_init + return -EINVAL; + } + ++ /* Check for APERF/MPERF support in hardware */ ++ if (cpu_has(c, X86_FEATURE_APERFMPERF)) ++ cpufreq_amd64_driver.getavg = cpufreq_get_measured_perf; ++ + cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu); + + if (cpu_family == CPU_HW_PSTATE) diff --git a/patches.arch/x86-hpet-pre-read b/patches.arch/x86-hpet-pre-read new file mode 100644 index 0000000..07a19da --- /dev/null +++ b/patches.arch/x86-hpet-pre-read @@ -0,0 +1,26 @@ +From: Takashi Iwai +Subject: x86: workaround for mccreary HPET read problem +Patch-mainline: not yet +References: bnc#433746 + +On mccreacy platform, the read of HPET CMP register seems not updated +immediately after the write and returns the previous value instead. +A workaround is to read the register twice. + +Signed-off-by: Takashi Iwai + +--- +--- + arch/x86/kernel/hpet.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/x86/kernel/hpet.c ++++ b/arch/x86/kernel/hpet.c +@@ -385,6 +385,7 @@ static int hpet_next_event(unsigned long + cnt += (u32) delta; + hpet_writel(cnt, HPET_Tn_CMP(timer)); + ++ hpet_readl(HPET_Tn_CMP(timer)); /* pre-read for bnc#433746 */ + /* + * We need to read back the CMP register on certain HPET + * implementations (ATI chipsets) which seem to delay the diff --git a/patches.arch/x86-mcp51-no-dac b/patches.arch/x86-mcp51-no-dac new file mode 100644 index 0000000..b94ea13 --- /dev/null +++ b/patches.arch/x86-mcp51-no-dac @@ -0,0 +1,38 @@ +From: Tejun Heo +Subject: x86: disallow DAC for MCP51 PCI bridge +References: bnc#463829 +Patch-mainline: not yet + +MCP51 corrupts DAC transfers. Disallow it. Reported by pgnet on +bnc#463829. + + https://bugzilla.novell.com/show_bug.cgi?id=463829 + +Signed-off-by: Tejun Heo +Reported-by: pgnet +Signed-off-by: Tejun Heo +--- + arch/x86/kernel/pci-dma.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/arch/x86/kernel/pci-dma.c ++++ b/arch/x86/kernel/pci-dma.c +@@ -318,4 +318,18 @@ static __devinit void via_no_dac(struct + } + } + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_ANY_ID, via_no_dac); ++ ++/* ++ * MCP51 PCI bridge corrupts data for DAC. Disable it. Reported in ++ * bnc#463829. ++ */ ++static __devinit void mcp51_no_dac(struct pci_dev *dev) ++{ ++ if (forbid_dac == 0) { ++ printk(KERN_INFO ++ "PCI: MCP51 PCI bridge detected. Disabling DAC.\n"); ++ forbid_dac = 1; ++ } ++} ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x026f, mcp51_no_dac); + #endif diff --git a/patches.arch/x86_64-hpet-64bit-timer.patch b/patches.arch/x86_64-hpet-64bit-timer.patch new file mode 100644 index 0000000..d7b889c --- /dev/null +++ b/patches.arch/x86_64-hpet-64bit-timer.patch @@ -0,0 +1,223 @@ +From: Jiri Bohac +Subject: allow 64-bit mode for HPET Timer0 +References: bnc#456700 + +The kernel uses the HPET timers in 32-bit mode for clock-events. +While 32 bits, with a wrap-around time of >4 minutes, is probably +good enough for the clock-event purposes, on some chipsets this +has a negative side-effect on the HPET main counter. + +Unlike the original HPET specification 1.0 from 2004, which does not +mention any side-effects of setting TN_32MODE_CNF on the +individual timers, the ICH9 documentation, for example, says: + + NOTE: When this bit is set to ‘1’, the hardware counter will + do a 32-bit operation on comparator match and rollovers, thus + the upper 32-bit of the Timer 0 Comparator Value register is + ignored. The upper 32-bit of the main counter is not involved + in any rollover from lower 32-bit of the main counter and + becomes all zeros. + +(see http://www.intel.com/assets/pdf/datasheet/316972.pdf, page +819, section 21.1.5, Bit 8). I've seen this behaviour also on +ICH8. I have no idea what other chipsets are affected. But I have +seen AMD chipsets that Do The Right Thing. + +This means, that when the kernel configures the Timer 0 to 32-bit +mode, on these chipsets it also cripples the 64-bit main counter +to 32 bits. + +The HPET may be mmapped in userspace and the main counter +accessed directly by applications, expecting a 64-bit main +counter. + +This patch allows the Timer0 to be configured in 64-bit mode +on x86_64 when a hpet64 command-line option is specified. + +Updated-by: Jeff Mahoney +Signed-off-by: Jiri Bohac + +--- + Documentation/kernel-parameters.txt | 2 + arch/x86/kernel/hpet.c | 88 ++++++++++++++++++++++++++++++++---- + 2 files changed, 81 insertions(+), 9 deletions(-) + +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -497,6 +497,8 @@ and is between 256 and 4096 characters. + Range: 0 - 8192 + Default: 64 + ++ hpet64 [X86-64,HPET] enable 64-bit mode of the HPET timer (bnc#456700) ++ + com20020= [HW,NET] ARCnet - COM20020 chipset + Format: + [,[,[,[,[,]]]]] +--- a/arch/x86/kernel/hpet.c ++++ b/arch/x86/kernel/hpet.c +@@ -37,6 +37,7 @@ unsigned long hpet_address; + static unsigned long hpet_num_timers; + #endif + static void __iomem *hpet_virt_address; ++static int hpet_legacy_use_64_bits; + + struct hpet_dev { + struct clock_event_device evt; +@@ -59,6 +60,33 @@ static inline void hpet_writel(unsigned + + #ifdef CONFIG_X86_64 + #include ++static inline unsigned long hpet_read_value(unsigned long a) ++{ ++ if (hpet_legacy_use_64_bits) ++ return readq(hpet_virt_address + a); ++ else ++ return readl(hpet_virt_address + a); ++} ++ ++static void hpet_write_value(unsigned long d, unsigned long a) ++{ ++ if (hpet_legacy_use_64_bits) ++ writeq(d, hpet_virt_address + a); ++ else ++ writel(d, hpet_virt_address + a); ++} ++ ++#else ++ ++static inline unsigned long hpet_read_value(unsigned long a) ++{ ++ return readl(hpet_virt_address + a); ++} ++ ++static void hpet_write_value(unsigned long d, unsigned long a) ++{ ++ writel(d, hpet_virt_address + a); ++} + #endif + + static inline void hpet_set_mapping(void) +@@ -103,6 +131,17 @@ static int __init disable_hpet(char *str + } + __setup("nohpet", disable_hpet); + ++#ifdef CONFIG_X86_64 ++static int hpet64 = 0; ++static int __init hpet64_setup(char *str) ++{ ++ hpet64 = 1; ++ return 1; ++} ++__setup("hpet64", hpet64_setup); ++#endif ++ ++ + static inline int is_hpet_capable(void) + { + return !boot_hpet_disable && hpet_address; +@@ -212,6 +251,7 @@ static void hpet_reserve_platform_timers + * Common hpet info + */ + static unsigned long hpet_period; ++static int hpet_legacy_use_64_bits; /* configure T0 in 64-bit mode? */ + + static void hpet_legacy_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt); +@@ -278,10 +318,38 @@ static void hpet_enable_legacy_int(void) + hpet_legacy_int_enabled = 1; + } + ++static int timer0_use_64_bits(void) ++{ ++#ifndef CONFIG_X86_64 ++ /* using the HPET in 64-bit mode without atomic 64-bit ++ * accesses is too inefficient ++ */ ++ return 0; ++#else ++ ++ if (unlikely(hpet64)) { ++ u32 id, t0_cfg; ++ id = hpet_readl(HPET_ID); ++ t0_cfg = hpet_readl(HPET_Tn_CFG(0)); ++ ++ if ((id & HPET_ID_64BIT) && (t0_cfg & HPET_TN_64BIT_CAP)) { ++ printk(KERN_DEBUG "hpet timer0 configured in 64-bit mode\n"); ++ return 1; ++ } ++ else { ++ printk(KERN_DEBUG "hpet timer0 does not support 64-bit mode\n"); ++ return 0; ++ } ++ } ++ else return 0; ++#endif ++} ++ + static void hpet_legacy_clockevent_register(void) + { + /* Start HPET legacy interrupts */ + hpet_enable_legacy_int(); ++ hpet_legacy_use_64_bits = timer0_use_64_bits(); + + /* + * The mult factor is defined as (include/linux/clockchips.h) +@@ -328,9 +396,10 @@ static void hpet_set_mode(enum clock_eve + /* Make sure we use edge triggered interrupts */ + cfg &= ~HPET_TN_LEVEL; + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | +- HPET_TN_SETVAL | HPET_TN_32BIT; ++ HPET_TN_SETVAL | ++ (hpet_legacy_use_64_bits ? 0 : HPET_TN_32BIT); + hpet_writel(cfg, HPET_Tn_CFG(timer)); +- hpet_writel(cmp, HPET_Tn_CMP(timer)); ++ hpet_write_value(cmp, HPET_Tn_CMP(timer)); + udelay(1); + /* + * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL +@@ -339,7 +408,7 @@ static void hpet_set_mode(enum clock_eve + * (See AMD-8111 HyperTransport I/O Hub Data Sheet, + * Publication # 24674) + */ +- hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); ++ hpet_write_value((unsigned long) delta, HPET_Tn_CMP(timer)); + hpet_start_counter(); + hpet_print_config(); + break; +@@ -347,7 +416,8 @@ static void hpet_set_mode(enum clock_eve + case CLOCK_EVT_MODE_ONESHOT: + cfg = hpet_readl(HPET_Tn_CFG(timer)); + cfg &= ~HPET_TN_PERIODIC; +- cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; ++ cfg |= HPET_TN_ENABLE | ++ (hpet_legacy_use_64_bits ? 0 : HPET_TN_32BIT); + hpet_writel(cfg, HPET_Tn_CFG(timer)); + break; + +@@ -376,11 +446,11 @@ static void hpet_set_mode(enum clock_eve + static int hpet_next_event(unsigned long delta, + struct clock_event_device *evt, int timer) + { +- u32 cnt; ++ unsigned long cnt; + +- cnt = hpet_readl(HPET_COUNTER); ++ cnt = hpet_read_value(HPET_COUNTER); + cnt += (u32) delta; +- hpet_writel(cnt, HPET_Tn_CMP(timer)); ++ hpet_write_value(cnt, HPET_Tn_CMP(timer)); + + hpet_readl(HPET_Tn_CMP(timer)); /* pre-read for bnc#433746 */ + /* +@@ -388,9 +458,9 @@ static int hpet_next_event(unsigned long + * what we wrote hit the chip before we compare it to the + * counter. + */ +- WARN_ON_ONCE((u32)hpet_readl(HPET_Tn_CMP(timer)) != cnt); ++ WARN_ON_ONCE((u32)hpet_readl(HPET_Tn_CMP(timer)) != (u32)cnt); + +- return (s32)((u32)hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; ++ return (s32)((u32)hpet_readl(HPET_COUNTER) - (u32)cnt) >= 0 ? -ETIME : 0; + } + + static void hpet_legacy_set_mode(enum clock_event_mode mode, diff --git a/patches.arch/x86_64-unwind-annotations b/patches.arch/x86_64-unwind-annotations new file mode 100644 index 0000000..48d45b8 --- /dev/null +++ b/patches.arch/x86_64-unwind-annotations @@ -0,0 +1,447 @@ +From: jbeulich@novell.com +Subject: fix unwind annotations +Patch-mainline: tbd +References: bnc#472783, bnc#588458 + +--- + arch/x86/kernel/entry_64.S | 193 +++++++++++++++++++++++---------------------- + arch/x86/kernel/head_64.S | 13 +++ + 2 files changed, 115 insertions(+), 91 deletions(-) + +--- a/arch/x86/kernel/entry_64.S ++++ b/arch/x86/kernel/entry_64.S +@@ -38,6 +38,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -240,21 +241,21 @@ ENDPROC(native_usergs_sysret64) + /* + * initial frame state for interrupts (and exceptions without error code) + */ +- .macro EMPTY_FRAME start=1 offset=0 +- .if \start ++ .macro EMPTY_FRAME offset=0 + CFI_STARTPROC simple + CFI_SIGNAL_FRAME +- CFI_DEF_CFA rsp,8+\offset +- .else +- CFI_DEF_CFA_OFFSET 8+\offset +- .endif ++ CFI_DEF_CFA rsp,\offset + .endm + + /* + * initial frame state for interrupts (and exceptions without error code) + */ + .macro INTR_FRAME start=1 offset=0 +- EMPTY_FRAME \start, SS+8+\offset-RIP ++ .if \start ++ EMPTY_FRAME __stringify(SS+8+\offset-RIP) ++ .else ++ CFI_DEF_CFA_OFFSET SS+8+\offset-RIP ++ .endif + /*CFI_REL_OFFSET ss, SS+\offset-RIP*/ + CFI_REL_OFFSET rsp, RSP+\offset-RIP + /*CFI_REL_OFFSET rflags, EFLAGS+\offset-RIP*/ +@@ -267,15 +268,16 @@ ENDPROC(native_usergs_sysret64) + * with vector already pushed) + */ + .macro XCPT_FRAME start=1 offset=0 +- INTR_FRAME \start, RIP+\offset-ORIG_RAX +- /*CFI_REL_OFFSET orig_rax, ORIG_RAX-ORIG_RAX*/ ++ INTR_FRAME \start, __stringify(RIP+\offset-ORIG_RAX) + .endm + + /* + * frame that enables calling into C. + */ + .macro PARTIAL_FRAME start=1 offset=0 +- XCPT_FRAME \start, ORIG_RAX+\offset-ARGOFFSET ++ .if \start >= 0 ++ XCPT_FRAME \start, __stringify(ORIG_RAX+\offset-ARGOFFSET) ++ .endif + CFI_REL_OFFSET rdi, RDI+\offset-ARGOFFSET + CFI_REL_OFFSET rsi, RSI+\offset-ARGOFFSET + CFI_REL_OFFSET rdx, RDX+\offset-ARGOFFSET +@@ -291,7 +293,9 @@ ENDPROC(native_usergs_sysret64) + * frame that enables passing a complete pt_regs to a C function. + */ + .macro DEFAULT_FRAME start=1 offset=0 +- PARTIAL_FRAME \start, R11+\offset-R15 ++ .if \start >= -1 ++ PARTIAL_FRAME \start, __stringify(R11+\offset-R15) ++ .endif + CFI_REL_OFFSET rbx, RBX+\offset + CFI_REL_OFFSET rbp, RBP+\offset + CFI_REL_OFFSET r12, R12+\offset +@@ -302,21 +306,23 @@ ENDPROC(native_usergs_sysret64) + + /* save partial stack frame */ + ENTRY(save_args) +- XCPT_FRAME ++ XCPT_FRAME offset=__stringify(ORIG_RAX-ARGOFFSET+16) + cld +- movq_cfi rdi, RDI+16-ARGOFFSET +- movq_cfi rsi, RSI+16-ARGOFFSET +- movq_cfi rdx, RDX+16-ARGOFFSET +- movq_cfi rcx, RCX+16-ARGOFFSET +- movq_cfi rax, RAX+16-ARGOFFSET +- movq_cfi r8, R8+16-ARGOFFSET +- movq_cfi r9, R9+16-ARGOFFSET +- movq_cfi r10, R10+16-ARGOFFSET +- movq_cfi r11, R11+16-ARGOFFSET ++ movq %rdi, RDI+16-ARGOFFSET(%rsp) ++ movq %rsi, RSI+16-ARGOFFSET(%rsp) ++ movq %rdx, RDX+16-ARGOFFSET(%rsp) ++ movq %rcx, RCX+16-ARGOFFSET(%rsp) ++ movq_cfi rax, __stringify(RAX+16-ARGOFFSET) ++ movq %r8, R8+16-ARGOFFSET(%rsp) ++ movq %r9, R9+16-ARGOFFSET(%rsp) ++ movq %r10, R10+16-ARGOFFSET(%rsp) ++ movq_cfi r11, __stringify(R11+16-ARGOFFSET) + + leaq -ARGOFFSET+16(%rsp),%rdi /* arg1 for handler */ + movq_cfi rbp, 8 /* push %rbp */ + leaq 8(%rsp), %rbp /* mov %rsp, %ebp */ ++ CFI_DEF_CFA_REGISTER rbp ++ CFI_ADJUST_CFA_OFFSET -8 + testl $3, CS(%rdi) + je 1f + SWAPGS +@@ -328,11 +334,10 @@ ENTRY(save_args) + */ + 1: incl PER_CPU_VAR(irq_count) + jne 2f +- popq_cfi %rax /* move return address... */ ++ popq %rax /* move return address... */ + mov PER_CPU_VAR(irq_stack_ptr),%rsp +- EMPTY_FRAME 0 +- pushq_cfi %rbp /* backlink for unwinder */ +- pushq_cfi %rax /* ... to the new stack */ ++ pushq %rbp /* backlink for unwinder */ ++ pushq %rax /* ... to the new stack */ + /* + * We entered an interrupt context - irqs are off: + */ +@@ -342,14 +347,14 @@ ENTRY(save_args) + END(save_args) + + ENTRY(save_rest) +- PARTIAL_FRAME 1 REST_SKIP+8 ++ CFI_STARTPROC + movq 5*8+16(%rsp), %r11 /* save return address */ +- movq_cfi rbx, RBX+16 +- movq_cfi rbp, RBP+16 +- movq_cfi r12, R12+16 +- movq_cfi r13, R13+16 +- movq_cfi r14, R14+16 +- movq_cfi r15, R15+16 ++ movq %rbx, RBX+16(%rsp) ++ movq %rbp, RBP+16(%rsp) ++ movq %r12, R12+16(%rsp) ++ movq %r13, R13+16(%rsp) ++ movq %r14, R14+16(%rsp) ++ movq %r15, R15+16(%rsp) + movq %r11, 8(%rsp) /* return address */ + FIXUP_TOP_OF_STACK %r11, 16 + ret +@@ -359,23 +364,23 @@ END(save_rest) + /* save complete stack frame */ + .pushsection .kprobes.text, "ax" + ENTRY(save_paranoid) +- XCPT_FRAME 1 RDI+8 ++ XCPT_FRAME offset=__stringify(ORIG_RAX-R15+8) + cld +- movq_cfi rdi, RDI+8 +- movq_cfi rsi, RSI+8 +- movq_cfi rdx, RDX+8 +- movq_cfi rcx, RCX+8 +- movq_cfi rax, RAX+8 +- movq_cfi r8, R8+8 +- movq_cfi r9, R9+8 +- movq_cfi r10, R10+8 +- movq_cfi r11, R11+8 +- movq_cfi rbx, RBX+8 +- movq_cfi rbp, RBP+8 +- movq_cfi r12, R12+8 +- movq_cfi r13, R13+8 +- movq_cfi r14, R14+8 +- movq_cfi r15, R15+8 ++ movq %rdi, RDI+8(%rsp) ++ movq %rsi, RSI+8(%rsp) ++ movq_cfi rdx, __stringify(RDX+8) ++ movq_cfi rcx, __stringify(RCX+8) ++ movq_cfi rax, __stringify(RAX+8) ++ movq %r8, R8+8(%rsp) ++ movq %r9, R9+8(%rsp) ++ movq %r10, R10+8(%rsp) ++ movq %r11, R11+8(%rsp) ++ movq_cfi rbx, __stringify(RBX+8) ++ movq %rbp, RBP+8(%rsp) ++ movq %r12, R12+8(%rsp) ++ movq %r13, R13+8(%rsp) ++ movq %r14, R14+8(%rsp) ++ movq %r15, R15+8(%rsp) + movl $1,%ebx + movl $MSR_GS_BASE,%ecx + rdmsr +@@ -685,7 +690,7 @@ ENTRY(\label) + subq $REST_SKIP, %rsp + CFI_ADJUST_CFA_OFFSET REST_SKIP + call save_rest +- DEFAULT_FRAME 0 8 /* offset 8: return address */ ++ DEFAULT_FRAME -2 8 /* offset 8: return address */ + leaq 8(%rsp), \arg /* pt_regs pointer */ + call \func + jmp ptregscall_common +@@ -702,12 +707,12 @@ END(\label) + ENTRY(ptregscall_common) + DEFAULT_FRAME 1 8 /* offset 8: return address */ + RESTORE_TOP_OF_STACK %r11, 8 +- movq_cfi_restore R15+8, r15 +- movq_cfi_restore R14+8, r14 +- movq_cfi_restore R13+8, r13 +- movq_cfi_restore R12+8, r12 +- movq_cfi_restore RBP+8, rbp +- movq_cfi_restore RBX+8, rbx ++ movq_cfi_restore __stringify(R15+8), r15 ++ movq_cfi_restore __stringify(R14+8), r14 ++ movq_cfi_restore __stringify(R13+8), r13 ++ movq_cfi_restore __stringify(R12+8), r12 ++ movq_cfi_restore __stringify(RBP+8), rbp ++ movq_cfi_restore __stringify(RBX+8), rbx + ret $REST_SKIP /* pop extended registers */ + CFI_ENDPROC + END(ptregscall_common) +@@ -719,9 +724,8 @@ END(ptregscall_common) + + ENTRY(stub_execve) + CFI_STARTPROC +- popq %r11 +- CFI_ADJUST_CFA_OFFSET -8 +- CFI_REGISTER rip, r11 ++ addq $8, %rsp ++ PARTIAL_FRAME 0 + SAVE_REST + FIXUP_TOP_OF_STACK %r11 + movq %rsp, %rcx +@@ -740,7 +744,7 @@ END(stub_execve) + ENTRY(stub_rt_sigreturn) + CFI_STARTPROC + addq $8, %rsp +- CFI_ADJUST_CFA_OFFSET -8 ++ PARTIAL_FRAME 0 + SAVE_REST + movq %rsp,%rdi + FIXUP_TOP_OF_STACK %r11 +@@ -796,10 +805,12 @@ END(interrupt) + + /* 0(%rsp): ~(interrupt number) */ + .macro interrupt func +- subq $10*8, %rsp +- CFI_ADJUST_CFA_OFFSET 10*8 ++ subq $ORIG_RAX-ARGOFFSET+8, %rsp ++ CFI_ADJUST_CFA_OFFSET ORIG_RAX-ARGOFFSET+8 + call save_args +- PARTIAL_FRAME 0 ++ PARTIAL_FRAME -1 8 ++ CFI_REL_OFFSET rbp, 0 ++ CFI_DEF_CFA_REGISTER rbp + call \func + .endm + +@@ -1036,10 +1047,10 @@ ENTRY(\sym) + INTR_FRAME + PARAVIRT_ADJUST_EXCEPTION_FRAME + pushq_cfi $-1 /* ORIG_RAX: no syscall to restart */ +- subq $15*8,%rsp +- CFI_ADJUST_CFA_OFFSET 15*8 ++ subq $ORIG_RAX-R15, %rsp ++ CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15 + call error_entry +- DEFAULT_FRAME 0 ++ DEFAULT_FRAME -1 + movq %rsp,%rdi /* pt_regs pointer */ + xorl %esi,%esi /* no error code */ + call \do_sym +@@ -1054,8 +1065,10 @@ ENTRY(\sym) + PARAVIRT_ADJUST_EXCEPTION_FRAME + pushq $-1 /* ORIG_RAX: no syscall to restart */ + CFI_ADJUST_CFA_OFFSET 8 +- subq $15*8, %rsp ++ subq $ORIG_RAX-R15, %rsp ++ CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15 + call save_paranoid ++ DEFAULT_FRAME -1 + TRACE_IRQS_OFF + movq %rsp,%rdi /* pt_regs pointer */ + xorl %esi,%esi /* no error code */ +@@ -1071,8 +1084,10 @@ ENTRY(\sym) + PARAVIRT_ADJUST_EXCEPTION_FRAME + pushq $-1 /* ORIG_RAX: no syscall to restart */ + CFI_ADJUST_CFA_OFFSET 8 +- subq $15*8, %rsp ++ subq $ORIG_RAX-R15, %rsp ++ CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15 + call save_paranoid ++ DEFAULT_FRAME -1 + TRACE_IRQS_OFF + movq %rsp,%rdi /* pt_regs pointer */ + xorl %esi,%esi /* no error code */ +@@ -1089,10 +1104,10 @@ END(\sym) + ENTRY(\sym) + XCPT_FRAME + PARAVIRT_ADJUST_EXCEPTION_FRAME +- subq $15*8,%rsp +- CFI_ADJUST_CFA_OFFSET 15*8 ++ subq $ORIG_RAX-R15, %rsp ++ CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15 + call error_entry +- DEFAULT_FRAME 0 ++ DEFAULT_FRAME -1 + movq %rsp,%rdi /* pt_regs pointer */ + movq ORIG_RAX(%rsp),%rsi /* get error code */ + movq $-1,ORIG_RAX(%rsp) /* no syscall to restart */ +@@ -1107,10 +1122,10 @@ END(\sym) + ENTRY(\sym) + XCPT_FRAME + PARAVIRT_ADJUST_EXCEPTION_FRAME +- subq $15*8,%rsp +- CFI_ADJUST_CFA_OFFSET 15*8 ++ subq $ORIG_RAX-R15, %rsp ++ CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15 + call save_paranoid +- DEFAULT_FRAME 0 ++ DEFAULT_FRAME -1 + TRACE_IRQS_OFF + movq %rsp,%rdi /* pt_regs pointer */ + movq ORIG_RAX(%rsp),%rsi /* get error code */ +@@ -1426,7 +1441,7 @@ paranoidzeroentry machine_check *machine + + /* ebx: no swapgs flag */ + ENTRY(paranoid_exit) +- INTR_FRAME ++ DEFAULT_FRAME + DISABLE_INTERRUPTS(CLBR_NONE) + TRACE_IRQS_OFF + testl %ebx,%ebx /* swapgs needed? */ +@@ -1476,25 +1491,24 @@ END(paranoid_exit) + * returns in "no swapgs flag" in %ebx. + */ + ENTRY(error_entry) +- XCPT_FRAME +- CFI_ADJUST_CFA_OFFSET 15*8 ++ XCPT_FRAME offset=__stringify(ORIG_RAX-R15+8) + /* oldrax contains error code */ + cld +- movq_cfi rdi, RDI+8 +- movq_cfi rsi, RSI+8 +- movq_cfi rdx, RDX+8 +- movq_cfi rcx, RCX+8 +- movq_cfi rax, RAX+8 +- movq_cfi r8, R8+8 +- movq_cfi r9, R9+8 +- movq_cfi r10, R10+8 +- movq_cfi r11, R11+8 +- movq_cfi rbx, RBX+8 +- movq_cfi rbp, RBP+8 +- movq_cfi r12, R12+8 +- movq_cfi r13, R13+8 +- movq_cfi r14, R14+8 +- movq_cfi r15, R15+8 ++ movq %rdi, RDI+8(%rsp) ++ movq %rsi, RSI+8(%rsp) ++ movq %rdx, RDX+8(%rsp) ++ movq %rcx, RCX+8(%rsp) ++ movq %rax, RAX+8(%rsp) ++ movq %r8, R8+8(%rsp) ++ movq %r9, R9+8(%rsp) ++ movq %r10, R10+8(%rsp) ++ movq %r11, R11+8(%rsp) ++ movq_cfi rbx, __stringify(RBX+8) ++ movq %rbp, RBP+8(%rsp) ++ movq %r12, R12+8(%rsp) ++ movq %r13, R13+8(%rsp) ++ movq %r14, R14+8(%rsp) ++ movq %r15, R15+8(%rsp) + xorl %ebx,%ebx + testl $3,CS+8(%rsp) + je error_kernelspace +@@ -1503,7 +1517,6 @@ error_swapgs: + error_sti: + TRACE_IRQS_OFF + ret +- CFI_ENDPROC + + /* + * There are two places in the kernel that can potentially fault with +@@ -1513,6 +1522,7 @@ error_sti: + * compat mode. Check for these here too. + */ + error_kernelspace: ++ CFI_REL_OFFSET rcx, RCX+8 + incl %ebx + leaq irq_return(%rip),%rcx + cmpq %rcx,RIP+8(%rsp) +@@ -1528,6 +1542,7 @@ bstep_iret: + /* Fix truncated RIP */ + movq %rcx,RIP+8(%rsp) + jmp error_swapgs ++ CFI_ENDPROC + END(error_entry) + + +@@ -1556,10 +1571,10 @@ ENTRY(nmi) + INTR_FRAME + PARAVIRT_ADJUST_EXCEPTION_FRAME + pushq_cfi $-1 +- subq $15*8, %rsp +- CFI_ADJUST_CFA_OFFSET 15*8 ++ subq $ORIG_RAX-R15, %rsp ++ CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15 + call save_paranoid +- DEFAULT_FRAME 0 ++ DEFAULT_FRAME -1 + /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */ + movq %rsp,%rdi + movq $-1,%rsi +--- a/arch/x86/kernel/head_64.S ++++ b/arch/x86/kernel/head_64.S +@@ -285,6 +285,8 @@ early_idt_handlers: + + ENTRY(early_idt_handler) + #ifdef CONFIG_EARLY_PRINTK ++#include ++#include + cmpl $2,early_recursion_flag(%rip) + jz 1f + incl early_recursion_flag(%rip) +@@ -300,6 +302,16 @@ ENTRY(early_idt_handler) + testl $0x27d00,%eax + je 0f + popq %r8 # get error code ++ ++ CFI_STARTPROC simple ++ CFI_SIGNAL_FRAME ++ CFI_DEF_CFA rsp, SS+8-RIP ++# CFI_REL_OFFSET ss, SS-RIP ++ CFI_REL_OFFSET rsp, RSP-RIP ++# CFI_REL_OFFSET rflags, EFLAGS-RIP ++# CFI_REL_OFFSET cs, CS-RIP ++ CFI_REL_OFFSET rip, RIP-RIP ++ + 0: movq 0(%rsp),%rcx # get ip + movq 8(%rsp),%rdx # get cs + xorl %eax,%eax +@@ -313,6 +325,7 @@ ENTRY(early_idt_handler) + movq 0(%rsp),%rsi # get rip again + call __print_symbol + #endif ++ CFI_ENDPROC + #endif /* EARLY_PRINTK */ + 1: hlt + jmp 1b diff --git a/patches.arch/x86_agpgart-g33-stoeln-fix-2.patch b/patches.arch/x86_agpgart-g33-stoeln-fix-2.patch new file mode 100644 index 0000000..a2cd417 --- /dev/null +++ b/patches.arch/x86_agpgart-g33-stoeln-fix-2.patch @@ -0,0 +1,74 @@ +From: Brandon Philips +Subject: Avoid oops on G33 in 1MB stolen Mem case +References: bnc#391261 +Patch-Mainline: soon (see bug for ref) + +This is similar to f443675affe3f16dd428e46f0f7fd3f4d703eeab which was +reverted because it broke with older XOrg driver. This patch only fixes +the 1MB stolen case since it causes an oops due to a calculation +problem. + +This will not work with older X drivers without the accompanying patch +but I think avoiding an oops and making it possible to work with an +up-to-date xorg driver is reasonable. + +Explanation of the oops: + +> static void intel_i830_init_gtt_entries(void) +... +> } else if (IS_G33) { +> /* G33's GTT size defined in gmch_ctrl */ +> switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) { +> case G33_PGETBL_SIZE_1M: +> size = 1024; +> break; +... +> size += 4; + +size = 1028 + +Then since we have the BIOS setting 1MB for the device in the GMCH +control we get to here: + +> } else { +> switch (gmch_ctrl & I855_GMCH_GMS_MASK) { +> case I855_GMCH_GMS_STOLEN_1M: +> gtt_entries = MB(1) - KB(size); +> break; + +MB(1) = 1 * 1024 * 1024 +KB(1028) = 1028 * 1024 + +MB(1) - KB(1028) = -4096 + +> gtt_entries /= KB(4); +> intel_private.gtt_entries = gtt_entries; + +We end up with -1 in gtt_entries. + +This leads to intel_i915_configure reading/writing to areas outside of +mapped memory and the oops. + +Signed-off-by: Brandon Philips +Acked-by: Thomas Renninger + +--- + drivers/char/agp/intel-agp.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/char/agp/intel-agp.c ++++ b/drivers/char/agp/intel-agp.c +@@ -801,6 +801,13 @@ static void intel_i830_init_gtt_entries( + } else { + switch (gmch_ctrl & I855_GMCH_GMS_MASK) { + case I855_GMCH_GMS_STOLEN_1M: ++ if (IS_G33) { ++ size = 0; ++ printk(KERN_WARNING PFX ++ "Warning: G33 chipset with 1MB" ++ " allocated. Older X.org Intel drivers" ++ " will not work.\n"); ++ } + gtt_entries = MB(1) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_4M: diff --git a/patches.arch/x86_mce_intel_decode_physical_address.patch b/patches.arch/x86_mce_intel_decode_physical_address.patch new file mode 100644 index 0000000..22a635d --- /dev/null +++ b/patches.arch/x86_mce_intel_decode_physical_address.patch @@ -0,0 +1,581 @@ +From: Andi Kleen +Subject: x86, mce: Xeon75xx specific interface to get corrected memory error information +Patch-Mainline: submitted to x86-tip, added but reverted due to a minor compile issue + which gets fixed by and incremental patch +References: bnc#573380, fate#307738 + +http://lkml.org/lkml/2010/1/22/98 + +Xeon 75xx doesn't log physical addresses on corrected machine check +events in the standard architectural MSRs. Instead the address has to +be retrieved in a model specific way. This makes it impossible to do +predictive failure analysis. + +Implement cpu model specific code to do this in mce-xeon75xx.c using a +new hook that is called from the generic poll code. The code retrieves +the physical address/DIMM of the last corrected error from the +platform and makes the address look like a standard architectural MCA +address for further processing. + +In addition the DIMM information is retrieved and put into two new +aux0/aux1 fields in struct mce. These fields are specific to a given +CPU. These fields can then be decoded by mcelog into specific DIMM +information. The latest mcelog version has support for this. + +Longer term this will be likely in a different output format, but +short term that seemed like the least intrusive solution. Older mcelog +can deal with an extended record. + +There's no code to print this information on a panic because this only +works for corrected errors, and corrected errors do not usually result +in panics. + +The act of retrieving the DIMM/PA information can take some time, so +this code has a rate limit to avoid taking too much CPU time on a +error flood. + +The whole thing can be loaded as a module and has suitable PCI-IDs so +that it can be auto-loaded by a distribution. The code also checks +explicitely for the expected CPU model number to make sure this code +doesn't run anywhere else. + +Signed-off-by: Thomas Renninger + +--- + arch/x86/Kconfig | 8 + arch/x86/include/asm/mce.h | 2 + arch/x86/kernel/cpu/mcheck/Makefile | 1 + arch/x86/kernel/cpu/mcheck/mce-internal.h | 1 + arch/x86/kernel/cpu/mcheck/mce-xeon75xx.c | 427 ++++++++++++++++++++++++++++++ + arch/x86/kernel/cpu/mcheck/mce.c | 11 + arch/x86/kernel/e820.c | 3 + 7 files changed, 452 insertions(+), 1 deletion(-) + +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -919,6 +919,14 @@ config X86_MCE_INTEL + Additional support for intel specific MCE features such as + the thermal monitor. + ++config X86_MCE_XEON75XX ++ tristate "Intel Xeon 7500 series corrected memory error driver" ++ depends on X86_MCE_INTEL ++ ---help--- ++ Add support for a Intel Xeon 7500 series specific memory error driver. ++ This allows to report the DIMM and physical address on a corrected ++ memory error machine check event. ++ + config X86_MCE_AMD + def_bool y + prompt "AMD MCE features" +--- a/arch/x86/include/asm/mce.h ++++ b/arch/x86/include/asm/mce.h +@@ -67,6 +67,8 @@ struct mce { + __u32 socketid; /* CPU socket ID */ + __u32 apicid; /* CPU initial apic ID */ + __u64 mcgcap; /* MCGCAP MSR: machine check capabilities of CPU */ ++ __u64 aux0; /* model specific */ ++ __u64 aux1; /* model specific */ + }; + + /* +--- a/arch/x86/kernel/cpu/mcheck/Makefile ++++ b/arch/x86/kernel/cpu/mcheck/Makefile +@@ -2,6 +2,7 @@ obj-y = mce.o mce-severity.o + + obj-$(CONFIG_X86_ANCIENT_MCE) += winchip.o p5.o + obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o ++obj-$(CONFIG_X86_MCE_XEON75XX) += mce-xeon75xx.o + obj-$(CONFIG_X86_MCE_AMD) += mce_amd.o + obj-$(CONFIG_X86_MCE_THRESHOLD) += threshold.o + obj-$(CONFIG_X86_MCE_INJECT) += mce-inject.o +--- a/arch/x86/kernel/cpu/mcheck/mce-internal.h ++++ b/arch/x86/kernel/cpu/mcheck/mce-internal.h +@@ -28,3 +28,4 @@ extern int mce_ser; + + extern struct mce_bank *mce_banks; + ++extern void (*cpu_specific_poll)(struct mce *); +--- /dev/null ++++ b/arch/x86/kernel/cpu/mcheck/mce-xeon75xx.c +@@ -0,0 +1,427 @@ ++/* ++ * Xeon 7500 series specific machine check support code. ++ * Copyright 2009, 2010 Intel Corporation ++ * Author: Andi Kleen ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 ++ * of the License. ++ * ++ * Implement Xeon 7500 series specific code to retrieve the physical address ++ * and DIMM information for corrected memory errors. ++ * ++ * Interface: mce->aux0/aux1 is mapped to a struct pfa_dimm with pad ++ * redefined to DIMM valid bits. Consumers check CPUID and bank and ++ * then interpret aux0/aux1 ++ */ ++ ++/* #define DEBUG 1 */ /* disable for production */ ++#define pr_fmt(x) "MCE: " x ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mce-internal.h" ++ ++#define PFA_SIG "$PFA" ++#define PFA_SIG_LEN 4 ++ ++/* DIMM description */ ++struct aux_pfa_dimm { ++ u8 fbd_channel_id; ++ u8 ddr_channel_id; ++ u8 ddr_dimm_id; ++ u8 ddr_rank_id; ++ u8 ddr_dimm_bank_id; ++ u8 ddr_dimm_row_id; ++ u8 ddr_dimm_column_id; ++ u8 valid; ++} __attribute__((packed)); ++ ++struct pfa_dimm { ++ u8 fbd_channel_id; ++ u8 ddr_channel_id; ++ u8 ddr_dimm_id; ++ u8 ddr_rank_id; ++ u8 ddr_dimm_bank_id; ++ u32 ddr_dimm_row_id; ++ u32 ddr_dimm_column_id; ++} __attribute__((packed)); ++ ++/* Memory translation table in memory. */ ++struct pfa_table { ++ u8 sig[PFA_SIG_LEN]; /* Signature: '$PFA' */ ++ u16 len; /* total length */ ++ u16 revision; /* 0x11 */ ++ u8 checksum; /* 8bit sum to zero */ ++ u8 db_value; /* mailbox port command value */ ++ u8 db_port; /* mailbox port */ ++ /* end of header; end of checksum */ ++ u8 command; /* input command */ ++ u32 valid; /* valid input/output bits */ ++ u16 status; /* output status */ ++ u8 socket_id; /* input socket id*/ ++ u8 bank_id; /* input MCE bank id */ ++ u32 pad1; ++ u64 mbox_address; ++ u64 physical_addr; /* physical address */ ++ struct pfa_dimm dimm[2]; ++ /* ++ * topology information follows: not used for now. ++ */ ++} __attribute__((packed)); ++ ++/* DIMM valid bits in valid: DIMM0: 8..12; DIMM1 16..20 */ ++#define DIMM_VALID_BITS(val, num) (((val) >> (4 + (num) * 8)) & DIMM_VALID_ALL) ++#define DIMM_SET_VALID(val, num) ((val) << (4 + (num) * 8)) ++ ++enum { ++ MCE_BANK_MBOX0 = 8, ++ MCE_BANK_MBOX1 = 9, ++ ++ PFA_REVISION = 0x11, /* v1.1 */ ++ ++ /* Status bits for valid field */ ++ PFA_VALID_MA = (1 << 0), ++ PFA_VALID_SOCKETID = (1 << 1), ++ PFA_VALID_BANKID = (1 << 2), ++ PFA_VALID_PA = (1 << 3), ++ ++ /* DIMM valid bits in valid */ ++ /* use with DIMM_VALID_BITS/DIMM_SET_VALID for pfa->valid */ ++ DIMM_VALID_FBD_CHAN = (1 << 0), ++ DIMM_VALID_DDR_CHAN = (1 << 1), ++ DIMM_VALID_DDR_DIMM = (1 << 2), ++ DIMM_VALID_DDR_RANK = (1 << 3), ++ DIMM_VALID_DIMM_BANK = (1 << 4), ++ DIMM_VALID_DIMM_ROW = (1 << 5), ++ DIMM_VALID_DIMM_COLUMN = (1 << 6), ++ DIMM_VALID_ALL = 0x7f, ++ ++ PFA_DIMM_VALID_MASK = DIMM_SET_VALID(DIMM_VALID_ALL, 0) ++ | DIMM_SET_VALID(DIMM_VALID_ALL, 1), ++ ++ /* Values for status field */ ++ PFA_STATUS_SUCCESS = 0, ++ PFA_STATUS_SOCKET_INVALID = (1 << 1), ++ PFA_STATUS_MBOX_INVALID = (1 << 2), ++ PFA_STATUS_MA_INVALID = (1 << 3), ++ PFA_STATUS_PA_INVALID = (1 << 4), ++ ++ /* Values for command field */ ++ PFA_CMD_GET_MEM_CORR_ERR_PA = 0, ++ PFA_CMD_PA_TO_DIMM_ADDR = 1, ++ PFA_CMD_DIMM_TO_PA = 2, ++ PFA_CMD_GET_TOPOLOGY = 3, ++ ++ /* PCI device IDs and the base register */ ++ ICH_PFA_CFG = 0x8c, /* SCRATCH4 */ ++ PCI_DEVICE_ID_BXB_ICH_LEGACY0 = 0x3422, ++}; ++ ++static struct pfa_table *pfa_table __read_mostly; ++static int memerr_max_conv_rate __read_mostly = 100; ++static int memerr_min_interval __read_mostly = 500; ++static int pfa_lost; /* for diagnosis */ ++ ++enum { ++ RATE_LIMIT_PERIOD = USEC_PER_SEC, /* in us; period of rate limit */ ++}; ++ ++module_param(memerr_max_conv_rate, int, 0644); ++MODULE_PARM_DESC(memerr_max_conv_rate, ++ "Maximum number of memory error conversions each second; 0 to disable"); ++module_param(memerr_min_interval, int, 0644); ++MODULE_PARM_DESC(memerr_min_interval, ++ "Minimum time delta between two memory conversions; in us; default 500"); ++ ++static int notest; ++static int nocsum; ++module_param(notest, int, 0); ++module_param(nocsum, int, 0); ++ ++static u64 encode_dimm(struct pfa_dimm *d, u8 valid) ++{ ++ union { ++ struct aux_pfa_dimm d; ++ u64 v; ++ } p; ++ ++ BUILD_BUG_ON(sizeof(struct aux_pfa_dimm) != sizeof(u64)); ++ p.d.fbd_channel_id = d->fbd_channel_id; ++ p.d.ddr_channel_id = d->ddr_channel_id; ++ p.d.ddr_dimm_id = d->ddr_dimm_id; ++ p.d.ddr_rank_id = d->ddr_rank_id; ++ p.d.ddr_dimm_bank_id = d->ddr_dimm_bank_id; ++ p.d.ddr_dimm_row_id = d->ddr_dimm_row_id; ++ if (p.d.ddr_dimm_row_id != d->ddr_dimm_row_id) /* truncated? */ ++ valid &= ~DIMM_VALID_DIMM_ROW; ++ p.d.ddr_dimm_column_id = d->ddr_dimm_column_id; ++ if (p.d.ddr_dimm_column_id != d->ddr_dimm_column_id) ++ valid &= ~DIMM_VALID_DIMM_COLUMN; ++ p.d.valid = valid; ++ pr_debug("PFA fbd_ch %u ddr_ch %u dimm %u rank %u bank %u valid %x\n", ++ d->fbd_channel_id, ++ d->ddr_channel_id, ++ d->ddr_dimm_id, ++ d->ddr_rank_id, ++ d->ddr_dimm_bank_id, ++ valid); ++ return p.v; ++} ++ ++static u8 csum(u8 *table, u16 len) ++{ ++ u8 sum = 0; ++ int i; ++ for (i = 0; i < len; i++) ++ sum += *table++; ++ return sum; ++} ++ ++/* ++ * Execute a command through the mailbox interface. ++ */ ++static int ++pfa_command(unsigned bank, unsigned socketid, unsigned command, unsigned valid) ++{ ++ pfa_table->bank_id = bank; ++ pfa_table->socket_id = socketid; ++ pfa_table->valid = valid | PFA_VALID_SOCKETID; ++ pfa_table->command = command; ++ ++ outb(pfa_table->db_value, pfa_table->db_port); ++ ++ mb(); /* Reread fields after they got changed */ ++ ++ if (pfa_table->status != PFA_STATUS_SUCCESS) { ++ pr_debug("Memory PFA command %d failed: socket:%d bank:%d status:%x\n", ++ command, socketid, bank, pfa_table->status); ++ return -pfa_table->status; ++ } ++ return 0; ++} ++ ++/* ++ * Retrieve physical address and DIMMs. ++ */ ++static int translate_memory_error(struct mce *m) ++{ ++ struct pfa_table *pfa = pfa_table; ++ u64 status; ++ int ret; ++ u32 valid; ++ int cpu = smp_processor_id(); ++ ++ /* Make sure our structures match the specification */ ++ BUILD_BUG_ON(offsetof(struct pfa_table, physical_addr) != 0x20); ++ BUILD_BUG_ON(offsetof(struct pfa_table, status) != 0x10); ++ BUILD_BUG_ON(offsetof(struct pfa_table, physical_addr) != 0x20); ++ BUILD_BUG_ON(offsetof(struct pfa_table, dimm[1].ddr_dimm_column_id) != ++ 0x3e); ++ ++ /* Ask for PA/DIMMs of last error */ ++ if (pfa_command(m->bank, m->socketid, ++ PFA_CMD_GET_MEM_CORR_ERR_PA, PFA_VALID_BANKID) < 0) ++ return -1; ++ ++ /* ++ * Recheck machine check bank. If the overflow bit was set ++ * there was a race. Don't use the information in this case. ++ */ ++ rdmsrl(MSR_IA32_MCx_STATUS(m->bank), status); ++ if (status & MCI_STATUS_OVER) { ++ pr_debug("%d: overflow race on bank %d\n", cpu, m->bank); ++ return -1; ++ } ++ ++ ret = -1; ++ valid = pfa->valid; ++ if (valid & PFA_VALID_PA) { ++ m->status |= MCI_STATUS_ADDRV; ++ m->addr = pfa_table->physical_addr; ++ pr_debug("%d: got physical address %llx valid %x\n", ++ cpu, m->addr, valid); ++ ret = 0; ++ } ++ ++ /* When DIMM information was supplied pass it out */ ++ if (valid & PFA_DIMM_VALID_MASK) { ++ m->aux0 = encode_dimm(&pfa->dimm[0], DIMM_VALID_BITS(valid, 0)); ++ m->aux1 = encode_dimm(&pfa->dimm[1], DIMM_VALID_BITS(valid, 1)); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Xeon 75xx specific mce poll method to retrieve the physical address ++ * and DIMM information. ++ */ ++static void xeon75xx_mce_poll(struct mce *m) ++{ ++ static DEFINE_SPINLOCK(convert_lock); /* Protect table and static */ ++ static unsigned long cperm; ++ static ktime_t last, last_int; ++ unsigned long flags; ++ ktime_t now; ++ s64 delta; ++ ++ /* Memory error? */ ++ if (m->bank != MCE_BANK_MBOX0 && m->bank != MCE_BANK_MBOX1) ++ return; ++ if (m->status & MCI_STATUS_OVER) ++ return; ++ if (memerr_max_conv_rate == 0) ++ return; ++ ++ spin_lock_irqsave(&convert_lock, flags); ++ /* ++ * Rate limit conversions. The conversion takes some time, ++ * but it's not good to use all the CPU time during a error ++ * flood. ++ * Enforce maximum number per second and minimum interval. ++ * The ktime call should use TSC on this machine and be fast. ++ */ ++ now = ktime_get(); ++ delta = ktime_us_delta(now, last); ++ if (delta >= RATE_LIMIT_PERIOD) { ++ cperm = 0; ++ last = now; ++ } ++ if (ktime_us_delta(now, last_int) >= memerr_min_interval && ++ ++cperm <= memerr_max_conv_rate) { ++ if (translate_memory_error(m) < 0) { ++ /* On error stop converting for the next second */ ++ cperm = memerr_max_conv_rate; ++ pr_debug("PFA translation failed\n"); ++ } ++ } else ++ pfa_lost++; ++ last_int = now; ++ spin_unlock_irqrestore(&convert_lock, flags); ++} ++ ++static struct pci_device_id bxb_mce_pciids[] = { ++ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_BXB_ICH_LEGACY0) }, ++ {} ++}; ++ ++static int __init xeon75xx_mce_init(void) ++{ ++ u32 addr = 0; ++ struct pci_dev *dev; ++ ++ if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || ++ boot_cpu_data.x86 != 6 || ++ boot_cpu_data.x86_model != 0x2e) ++ return -ENODEV; ++ ++ /* ++ * Get table address from register in IOH. ++ * This just looks up the device, because we don't want to "own" it. ++ */ ++ dev = NULL; ++ while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, dev)) ++ != NULL) { ++ if (!pci_match_id(bxb_mce_pciids, dev)) ++ continue; ++ pci_read_config_dword(dev, ICH_PFA_CFG, &addr); ++ if (addr) ++ break; ++ } ++ pci_dev_put(dev); ++ if (!addr) ++ return -ENODEV; ++ ++ if (!e820_all_mapped(addr, addr + PAGE_SIZE, E820_RESERVED)) { ++ pr_info("PFA table at %x not e820 reserved\n", addr); ++ return -ENODEV; ++ } ++ ++ pfa_table = (__force struct pfa_table *)ioremap_cache(addr, PAGE_SIZE); ++ if (!pfa_table) { ++ pr_err("Cannot map PFA table at %x\n", addr); ++ return -EIO; ++ } ++ ++ if (memcmp(&pfa_table->sig, PFA_SIG, PFA_SIG_LEN) || ++ pfa_table->len < sizeof(struct pfa_table) || ++ /* assume newer versions are compatible */ ++ pfa_table->revision < PFA_REVISION) { ++ pr_info("PFA table at %x invalid\n", addr); ++ goto error_unmap; ++ } ++ ++ if (!nocsum && csum((u8 *)pfa_table, ++ offsetof(struct pfa_table, command))) { ++ pr_info("PFA table at %x length %u has invalid checksum\n", ++ addr, pfa_table->len); ++ goto error_unmap; ++ } ++ ++ /* Not strictly needed today */ ++ if (pfa_table->len > PAGE_SIZE) { ++ unsigned len = roundup(pfa_table->len, PAGE_SIZE); ++ iounmap(pfa_table); ++ pfa_table = (__force void *)ioremap_cache(addr, len); ++ if (!pfa_table) { ++ pr_err("Cannot remap %u bytes PFA table at %x\n", ++ len, addr); ++ return -EIO; ++ } ++ } ++ ++ if (!notest) { ++ int status = pfa_command(0, 0, PFA_CMD_GET_TOPOLOGY, 0); ++ if (status < 0) { ++ pr_err("Test of PFA table failed: %x\n", -status); ++ goto error_unmap; ++ } ++ } ++ ++ pr_info("Found Xeon75xx PFA memory error translation table at %x\n", ++ addr); ++ mb(); ++ cpu_specific_poll = xeon75xx_mce_poll; ++ return 0; ++ ++error_unmap: ++ iounmap(pfa_table); ++ return -ENODEV; ++} ++ ++MODULE_DEVICE_TABLE(pci, bxb_mce_pciids); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Andi Kleen"); ++MODULE_DESCRIPTION("Intel Xeon 75xx specific DIMM error reporting"); ++ ++#ifdef CONFIG_MODULE ++static void __exit xeon75xx_mce_exit(void) ++{ ++ cpu_specific_poll = NULL; ++ wmb(); ++ /* Wait for all machine checks to finish before really unloading */ ++ synchronize_rcu(); ++ iounmap(pfa_table); ++} ++ ++module_init(xeon75xx_mce_init); ++module_exit(xeon75xx_mce_exit); ++#else ++/* When built-in run as soon as the PCI subsystem is up */ ++fs_initcall(xeon75xx_mce_init); ++#endif +--- a/arch/x86/kernel/cpu/mcheck/mce.c ++++ b/arch/x86/kernel/cpu/mcheck/mce.c +@@ -94,6 +94,8 @@ static char *mce_helper_argv[2] = { mc + static DECLARE_WAIT_QUEUE_HEAD(mce_wait); + static DEFINE_PER_CPU(struct mce, mces_seen); + static int cpu_missing; ++void (*cpu_specific_poll)(struct mce *); ++EXPORT_SYMBOL_GPL(cpu_specific_poll); + + /* + * CPU/chipset specific EDAC code can register a notifier call here to print +@@ -371,6 +373,11 @@ static void mce_wrmsrl(u32 msr, u64 v) + wrmsrl(msr, v); + } + ++static int under_injection(void) ++{ ++ return __get_cpu_var(injectm).finished; ++} ++ + /* + * Simple lockless ring to communicate PFNs from the exception handler with the + * process context work function. This is vastly simplified because there's +@@ -574,6 +581,10 @@ void machine_check_poll(enum mcp_flags f + + if (!(flags & MCP_TIMESTAMP)) + m.tsc = 0; ++ ++ if (cpu_specific_poll && !under_injection() && !mce_dont_log_ce) ++ cpu_specific_poll(&m); ++ + /* + * Don't get the IP here because it's unlikely to + * have anything to do with the actual error location. +--- a/arch/x86/kernel/e820.c ++++ b/arch/x86/kernel/e820.c +@@ -71,7 +71,7 @@ EXPORT_SYMBOL_GPL(e820_any_mapped); + * Note: this function only works correct if the e820 table is sorted and + * not-overlapping, which is the case + */ +-int __init e820_all_mapped(u64 start, u64 end, unsigned type) ++int e820_all_mapped(u64 start, u64 end, unsigned type) + { + int i; + +@@ -98,6 +98,7 @@ int __init e820_all_mapped(u64 start, u6 + } + return 0; + } ++EXPORT_SYMBOL_GPL(e820_all_mapped); + + /* + * Add a memory region to the kernel e820 map. diff --git a/patches.arch/x86_mce_intel_decode_physical_address_compile_fix.patch b/patches.arch/x86_mce_intel_decode_physical_address_compile_fix.patch new file mode 100644 index 0000000..aa0c677 --- /dev/null +++ b/patches.arch/x86_mce_intel_decode_physical_address_compile_fix.patch @@ -0,0 +1,25 @@ +From: Andi Kleen +Subject: x86, mce: Xeon75xx specific interface to get corrected memory error information +Patch-Mainline: submitted to x86-tip, added but reverted due to a minor compile issue + which gets fixed by this patch +References: bnc#573380, fate#307738 + +http://lkml.org/lkml/2010/1/23/50 + +Signed-off-by: Thomas Renninger + +--- + arch/x86/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -921,7 +921,7 @@ config X86_MCE_INTEL + + config X86_MCE_XEON75XX + tristate "Intel Xeon 7500 series corrected memory error driver" +- depends on X86_MCE_INTEL ++ depends on X86_MCE_INTEL && PCI + ---help--- + Add support for a Intel Xeon 7500 series specific memory error driver. + This allows to report the DIMM and physical address on a corrected diff --git a/patches.arch/x86_mce_intel_decode_physical_address_rename_fix.patch b/patches.arch/x86_mce_intel_decode_physical_address_rename_fix.patch new file mode 100644 index 0000000..0bd7300 --- /dev/null +++ b/patches.arch/x86_mce_intel_decode_physical_address_rename_fix.patch @@ -0,0 +1,72 @@ +From: H. Peter Anvin +Subject: x86, mce: Rename cpu_specific_poll to mce_cpu_specific_poll +Patch-Mainline: submitted to x86-tip, added but reverted due to a minor compile issue + which gets fixed by and incremental patch +References: bnc#573380, fate#307738 + +http://lkml.org/lkml/2010/1/22/99 + +cpu_specific_poll is a global variable, and it should have a global +namespace name. Since it is MCE-specific (it takes a struct mce *), +rename it mce_cpu_specific_poll. + +Signed-off-by: Thomas Renninger + +--- + arch/x86/kernel/cpu/mcheck/mce-internal.h | 2 +- + arch/x86/kernel/cpu/mcheck/mce-xeon75xx.c | 4 ++-- + arch/x86/kernel/cpu/mcheck/mce.c | 8 ++++---- + 3 files changed, 7 insertions(+), 7 deletions(-) + +--- a/arch/x86/kernel/cpu/mcheck/mce-internal.h ++++ b/arch/x86/kernel/cpu/mcheck/mce-internal.h +@@ -28,4 +28,4 @@ extern int mce_ser; + + extern struct mce_bank *mce_banks; + +-extern void (*cpu_specific_poll)(struct mce *); ++extern void (*mce_cpu_specific_poll)(struct mce *); +--- a/arch/x86/kernel/cpu/mcheck/mce-xeon75xx.c ++++ b/arch/x86/kernel/cpu/mcheck/mce-xeon75xx.c +@@ -396,7 +396,7 @@ static int __init xeon75xx_mce_init(void + pr_info("Found Xeon75xx PFA memory error translation table at %x\n", + addr); + mb(); +- cpu_specific_poll = xeon75xx_mce_poll; ++ mce_cpu_specific_poll = xeon75xx_mce_poll; + return 0; + + error_unmap: +@@ -412,7 +412,7 @@ MODULE_DESCRIPTION("Intel Xeon 75xx spec + #ifdef CONFIG_MODULE + static void __exit xeon75xx_mce_exit(void) + { +- cpu_specific_poll = NULL; ++ mce_cpu_specific_poll = NULL; + wmb(); + /* Wait for all machine checks to finish before really unloading */ + synchronize_rcu(); +--- a/arch/x86/kernel/cpu/mcheck/mce.c ++++ b/arch/x86/kernel/cpu/mcheck/mce.c +@@ -94,8 +94,8 @@ static char *mce_helper_argv[2] = { mc + static DECLARE_WAIT_QUEUE_HEAD(mce_wait); + static DEFINE_PER_CPU(struct mce, mces_seen); + static int cpu_missing; +-void (*cpu_specific_poll)(struct mce *); +-EXPORT_SYMBOL_GPL(cpu_specific_poll); ++void (*mce_cpu_specific_poll)(struct mce *); ++EXPORT_SYMBOL_GPL(mce_cpu_specific_poll); + + /* + * CPU/chipset specific EDAC code can register a notifier call here to print +@@ -582,8 +582,8 @@ void machine_check_poll(enum mcp_flags f + if (!(flags & MCP_TIMESTAMP)) + m.tsc = 0; + +- if (cpu_specific_poll && !under_injection() && !mce_dont_log_ce) +- cpu_specific_poll(&m); ++ if (mce_cpu_specific_poll && !under_injection() && !mce_dont_log_ce) ++ mce_cpu_specific_poll(&m); + + /* + * Don't get the IP here because it's unlikely to diff --git a/patches.drivers.tar.bz2 b/patches.drivers.tar.bz2 deleted file mode 100644 index 6400902..0000000 Binary files a/patches.drivers.tar.bz2 and /dev/null differ diff --git a/patches.drivers/0001-drm-i915-Use-spatio-temporal-dithering-on-PCH.patch b/patches.drivers/0001-drm-i915-Use-spatio-temporal-dithering-on-PCH.patch new file mode 100644 index 0000000..4c87cdd --- /dev/null +++ b/patches.drivers/0001-drm-i915-Use-spatio-temporal-dithering-on-PCH.patch @@ -0,0 +1,77 @@ +From 9286a0bc63de32c66d894b45dcf048a072a84cd7 Mon Sep 17 00:00:00 2001 +From: Adam Jackson +Date: Mon, 19 Apr 2010 15:57:25 -0400 +Subject: [PATCH 1/4] drm/i915: Use spatio-temporal dithering on PCH + +Spatial dither is better than nothing, but ST is even better. + +(from ajax's followup message:) + I noticed this with: + + http://ajax.fedorapeople.org/YellowFlower.jpg + + set as my desktop background in Gnome on a 1280x800 machine (in + particular, a Sony Vaio VPCB1 with 6-bit panel and a rather bright black + level). Easiest way to test this is by poking at PIPEACONF with + intel_reg_write directly: + + % sudo intel_reg_write 0x70008 0xc0000040 # no dither + % sudo intel_reg_write 0x70008 0xc0000050 # spatial + % sudo intel_reg_write 0x70008 0xc0000054 # ST + + I notice it especially strongly in the relatively flat dark area in the + top left. Closer than about 18" I can see a noticeable checkerboard + pattern with plain spatial dithering. ST smooths that out; I can still + tell that it's lacking color precision, but it's not offensive. + +Signed-off-by: Adam Jackson +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/i915/i915_reg.h | 5 ++++- + drivers/gpu/drm/i915/intel_display.c | 10 ++++++---- + 2 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h +index 4cbc521..89b6efc 100644 +--- a/drivers/gpu/drm/i915/i915_reg.h ++++ b/drivers/gpu/drm/i915/i915_reg.h +@@ -1924,7 +1924,10 @@ + /* Display & cursor control */ + + /* dithering flag on Ironlake */ +-#define PIPE_ENABLE_DITHER (1 << 4) ++#define PIPE_ENABLE_DITHER (1 << 4) ++#define PIPE_DITHER_TYPE_MASK (3 << 2) ++#define PIPE_DITHER_TYPE_SPATIAL (0 << 2) ++#define PIPE_DITHER_TYPE_ST01 (1 << 2) + /* Pipe A */ + #define PIPEADSL 0x70000 + #define PIPEACONF 0x70008 +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index c7502b6..f1a37d9 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -3321,14 +3321,16 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, + /* set the dithering flag */ + if (IS_I965G(dev)) { + if (dev_priv->lvds_dither) { +- if (HAS_PCH_SPLIT(dev)) ++ if (HAS_PCH_SPLIT(dev)) { + pipeconf |= PIPE_ENABLE_DITHER; +- else ++ pipeconf |= PIPE_DITHER_TYPE_ST01; ++ } else + lvds |= LVDS_ENABLE_DITHER; + } else { +- if (HAS_PCH_SPLIT(dev)) ++ if (HAS_PCH_SPLIT(dev)) { + pipeconf &= ~PIPE_ENABLE_DITHER; +- else ++ pipeconf &= ~PIPE_DITHER_TYPE_MASK; ++ } else + lvds &= ~LVDS_ENABLE_DITHER; + } + } +-- +1.7.0.1 + diff --git a/patches.drivers/0002-drm-i915-Honor-sync-polarity-from-VBT-panel-timing-d.patch b/patches.drivers/0002-drm-i915-Honor-sync-polarity-from-VBT-panel-timing-d.patch new file mode 100644 index 0000000..e616e04 --- /dev/null +++ b/patches.drivers/0002-drm-i915-Honor-sync-polarity-from-VBT-panel-timing-d.patch @@ -0,0 +1,37 @@ +From 7f588d4ca94f4efd146b47cdcb6483edda4886f4 Mon Sep 17 00:00:00 2001 +From: Adam Jackson +Date: Fri, 28 May 2010 17:17:37 -0400 +Subject: [PATCH 2/4] drm/i915: Honor sync polarity from VBT panel timing descriptors + +I'm actually kind of shocked that it works at all otherwise. + +Signed-off-by: Adam Jackson +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/i915/intel_bios.c | 10 ++++++++++ + 1 files changed, 10 insertions(+), 0 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c +index f9ba452..8905070 100644 +--- a/drivers/gpu/drm/i915/intel_bios.c ++++ b/drivers/gpu/drm/i915/intel_bios.c +@@ -95,6 +95,16 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, + panel_fixed_mode->clock = dvo_timing->clock * 10; + panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; + ++ if (dvo_timing->hsync_positive) ++ panel_fixed_mode->flags |= DRM_MODE_FLAG_PHSYNC; ++ else ++ panel_fixed_mode->flags |= DRM_MODE_FLAG_NHSYNC; ++ ++ if (dvo_timing->vsync_positive) ++ panel_fixed_mode->flags |= DRM_MODE_FLAG_PVSYNC; ++ else ++ panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC; ++ + /* Some VBTs have bogus h/vtotal values */ + if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) + panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; +-- +1.7.0.1 + diff --git a/patches.drivers/0003-drm-i915-Add-the-support-of-eDP-on-DP-D-for-Ibex-CPT.patch b/patches.drivers/0003-drm-i915-Add-the-support-of-eDP-on-DP-D-for-Ibex-CPT.patch new file mode 100644 index 0000000..92f40f3 --- /dev/null +++ b/patches.drivers/0003-drm-i915-Add-the-support-of-eDP-on-DP-D-for-Ibex-CPT.patch @@ -0,0 +1,266 @@ +From 2fb8b53110fdf163eae9e8a506bf769449e2ee4b Mon Sep 17 00:00:00 2001 +From: Joanna Rutkowska +Date: Tue, 29 Jun 2010 08:34:37 +0200 +Subject: [PATCH 3/4] drm/i915: Add the support of eDP on DP-D for Ibex/CPT + +On some machines the eDP is connected on the PCH DP-D instead of DP-A. + +Signed-off-by: Zhao Yakui + +Conflicts: + + drivers/gpu/drm/i915/intel_dp.c +--- + drivers/gpu/drm/i915/intel_display.c | 2 +- + drivers/gpu/drm/i915/intel_dp.c | 99 ++++++++++++++++++++++++++++++--- + drivers/gpu/drm/i915/intel_drv.h | 1 + + 3 files changed, 92 insertions(+), 10 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index f1a37d9..32ae849 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -3073,7 +3073,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, + temp |= PIPE_8BPC; + else + temp |= PIPE_6BPC; +- } else if (is_edp) { ++ } else if (is_edp || intel_edp_is_pch(crtc)) { + switch (dev_priv->edp_bpp/3) { + case 8: + temp |= PIPE_8BPC; +diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c +index 77e40cf..c13c3bf 100644 +--- a/drivers/gpu/drm/i915/intel_dp.c ++++ b/drivers/gpu/drm/i915/intel_dp.c +@@ -43,6 +43,7 @@ + #define DP_LINK_CONFIGURATION_SIZE 9 + + #define IS_eDP(i) ((i)->type == INTEL_OUTPUT_EDP) ++#define IS_PCH_eDP(dp_priv) ((dp_priv)->is_edpd) + + struct intel_dp_priv { + uint32_t output_reg; +@@ -58,6 +59,7 @@ struct intel_dp_priv { + struct intel_encoder *intel_encoder; + struct i2c_adapter adapter; + struct i2c_algo_dp_aux_data algo; ++ bool is_edpd; + }; + + static void +@@ -130,8 +132,9 @@ intel_dp_link_required(struct drm_device *dev, + struct intel_encoder *intel_encoder, int pixel_clock) + { + struct drm_i915_private *dev_priv = dev->dev_private; ++ struct intel_dp_priv *dp_priv = intel_encoder->dev_priv; + +- if (IS_eDP(intel_encoder)) ++ if (IS_eDP(intel_encoder) || IS_PCH_eDP(dp_priv)) + return (pixel_clock * dev_priv->edp_bpp) / 8; + else + return pixel_clock * 3; +@@ -534,14 +537,14 @@ intel_reduce_ratio(uint32_t *num, uint32_t *den) + } + + static void +-intel_dp_compute_m_n(int bytes_per_pixel, ++intel_dp_compute_m_n(int bpp, + int nlanes, + int pixel_clock, + int link_clock, + struct intel_dp_m_n *m_n) + { + m_n->tu = 64; +- m_n->gmch_m = pixel_clock * bytes_per_pixel; ++ m_n->gmch_m = (pixel_clock * bpp) >> 3; + m_n->gmch_n = link_clock * nlanes; + intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); + m_n->link_m = pixel_clock; +@@ -549,6 +552,31 @@ intel_dp_compute_m_n(int bytes_per_pixel, + intel_reduce_ratio(&m_n->link_m, &m_n->link_n); + } + ++bool intel_edp_is_pch(struct drm_crtc *crtc) ++{ ++ struct drm_device *dev = crtc->dev; ++ struct drm_mode_config *mode_config = &dev->mode_config; ++ struct drm_encoder *encoder; ++ bool ret = false; ++ ++ list_for_each_entry(encoder, &mode_config->encoder_list, head) { ++ struct intel_encoder *intel_encoder; ++ struct intel_dp_priv *dp_priv; ++ ++ if (!encoder || encoder->crtc != crtc) ++ continue; ++ ++ intel_encoder = enc_to_intel_encoder(encoder); ++ dp_priv = intel_encoder->dev_priv; ++ ++ if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { ++ ret = IS_PCH_eDP(dp_priv); ++ break; ++ } ++ } ++ return ret; ++} ++ + void + intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +@@ -558,7 +586,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_connector *connector; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +- int lane_count = 4; ++ int lane_count = 4, bpp = 24; + struct intel_dp_m_n m_n; + + /* +@@ -573,6 +601,8 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, + + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { + lane_count = dp_priv->lane_count; ++ if (IS_PCH_eDP(dp_priv)) ++ bpp = dev_priv->edp_bpp; + break; + } + } +@@ -582,7 +612,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, + * the number of bytes_per_pixel post-LUT, which we always + * set up for 8-bits of R/G/B, or 3 bytes total. + */ +- intel_dp_compute_m_n(3, lane_count, ++ intel_dp_compute_m_n(bpp, lane_count, + mode->clock, adjusted_mode->clock, &m_n); + + if (HAS_PCH_SPLIT(dev)) { +@@ -711,13 +741,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) + if (mode != DRM_MODE_DPMS_ON) { + if (dp_reg & DP_PORT_EN) { + intel_dp_link_down(intel_encoder, dp_priv->DP); +- if (IS_eDP(intel_encoder)) ++ if (IS_eDP(intel_encoder) || IS_PCH_eDP(dp_priv)) + ironlake_edp_backlight_off(dev); + } + } else { + if (!(dp_reg & DP_PORT_EN)) { + intel_dp_link_train(intel_encoder, dp_priv->DP, dp_priv->link_configuration); +- if (IS_eDP(intel_encoder)) ++ if (IS_eDP(intel_encoder) || IS_PCH_eDP(dp_priv)) + ironlake_edp_backlight_on(dev); + } + } +@@ -1225,6 +1255,7 @@ static int intel_dp_get_modes(struct drm_connector *connector) + struct intel_encoder *intel_encoder = to_intel_encoder(connector); + struct drm_device *dev = intel_encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; ++ struct intel_dp_priv *dp_priv = intel_encoder->dev_priv; + int ret; + + /* We should parse the EDID data and find out if it has an audio sink +@@ -1235,7 +1266,7 @@ static int intel_dp_get_modes(struct drm_connector *connector) + return ret; + + /* if eDP has no EDID, try to use fixed panel mode from VBT */ +- if (IS_eDP(intel_encoder)) { ++ if (IS_eDP(intel_encoder) || IS_PCH_eDP(dp_priv)) { + if (dev_priv->panel_fixed_mode != NULL) { + struct drm_display_mode *mode; + mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); +@@ -1299,6 +1330,50 @@ intel_dp_hot_plug(struct intel_encoder *intel_encoder) + intel_dp_check_link_status(intel_encoder); + } + ++/* Return which DP Port should be selected for Transcoder DP control */ ++int ++intel_trans_dp_port_sel (struct drm_crtc *crtc) ++{ ++ struct drm_device *dev = crtc->dev; ++ struct drm_mode_config *mode_config = &dev->mode_config; ++ struct drm_encoder *encoder; ++ struct intel_encoder *intel_encoder = NULL; ++ ++ list_for_each_entry(encoder, &mode_config->encoder_list, head) { ++ if (encoder->crtc != crtc) ++ continue; ++ ++ intel_encoder = enc_to_intel_encoder(encoder); ++ if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { ++ struct intel_dp_priv *dp_priv = intel_encoder->dev_priv; ++ return dp_priv->output_reg; ++ } ++ } ++ return -1; ++} ++ ++static bool intel_dpd_is_edp(struct drm_device *dev) ++{ ++ struct drm_i915_private *dev_priv = dev->dev_private; ++ struct child_device_config *p_child; ++ int i, ret = false; ++ ++ if (!dev_priv->child_dev_num) ++ return false; ++ ++ for (i = 0; i < dev_priv->child_dev_num; i++) { ++ p_child = dev_priv->child_dev + i; ++ if (p_child->device_type != DEVICE_TYPE_eDP) ++ continue; ++ ++ if (p_child->dvo_port == PORT_IDPD) { ++ ret = true; ++ break; ++ } ++ } ++ return ret; ++} ++ + void + intel_dp_init(struct drm_device *dev, int output_reg) + { +@@ -1320,6 +1395,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) + DRM_MODE_CONNECTOR_DisplayPort); + drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); + ++ dp_priv->is_edpd = false; + if (output_reg == DP_A) + intel_encoder->type = INTEL_OUTPUT_EDP; + else +@@ -1335,6 +1411,11 @@ intel_dp_init(struct drm_device *dev, int output_reg) + if (IS_eDP(intel_encoder)) + intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT); + ++ if (HAS_PCH_SPLIT(dev) && (output_reg == PCH_DP_D)) { ++ if (intel_dpd_is_edp(dev)) ++ dp_priv->is_edpd = true; ++ } ++ + intel_encoder->crtc_mask = (1 << 0) | (1 << 1); + connector->interlace_allowed = true; + connector->doublescan_allowed = 0; +@@ -1383,7 +1464,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) + intel_encoder->ddc_bus = &dp_priv->adapter; + intel_encoder->hot_plug = intel_dp_hot_plug; + +- if (output_reg == DP_A) { ++ if ((output_reg == DP_A) || IS_PCH_eDP(dp_priv)) { + /* initialize panel mode from VBT if available for eDP */ + if (dev_priv->lfp_lvds_vbt_mode) { + dev_priv->panel_fixed_mode = +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index e302537..0858a17 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -175,6 +175,7 @@ extern void intel_dp_init(struct drm_device *dev, int dp_reg); + void + intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); ++extern bool intel_edp_is_pch(struct drm_crtc *crtc); + extern void intel_edp_link_config (struct intel_encoder *, int *, int *); + + +-- +1.7.0.1 + diff --git a/patches.drivers/0004-drm-i915-Configure-the-PIPECONF-dither-correctly-for.patch b/patches.drivers/0004-drm-i915-Configure-the-PIPECONF-dither-correctly-for.patch new file mode 100644 index 0000000..fe5116c --- /dev/null +++ b/patches.drivers/0004-drm-i915-Configure-the-PIPECONF-dither-correctly-for.patch @@ -0,0 +1,38 @@ +From 46e3e699294d3fe4fecb08d697bb29addab29576 Mon Sep 17 00:00:00 2001 +From: Zhao Yakui +Date: Fri, 28 May 2010 20:28:41 +0800 +Subject: [PATCH 4/4] drm/i915: Configure the PIPECONF dither correctly for eDP + +The non-8 BPC can be used for the eDP output device that is connected through +DP-A or DP-D on PCH. In such case we should set the PIPECONF dither correctly. + +Signed-off-by: Zhao Yakui +--- + drivers/gpu/drm/i915/intel_display.c | 11 +++++++++++ + 1 files changed, 11 insertions(+), 0 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index 32ae849..49c9663 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -3239,6 +3239,17 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, + /* setup pipeconf */ + pipeconf = I915_READ(pipeconf_reg); + ++ if (HAS_PCH_SPLIT(dev) && (is_edp || intel_edp_is_pch(crtc))) { ++ /* configure the dither correctly for eDP */ ++ pipeconf &= ~PIPE_DITHER_TYPE_MASK; ++ if ((pipeconf & PIPE_BPC_MASK) != PIPE_8BPC) { ++ pipeconf |= PIPE_ENABLE_DITHER; ++ pipeconf |= PIPE_DITHER_TYPE_ST01; ++ } else { ++ pipeconf &= ~PIPE_ENABLE_DITHER; ++ } ++ } ++ + /* Set up the display plane register */ + dspcntr = DISPPLANE_GAMMA_ENABLE; + +-- +1.7.0.1 + diff --git a/patches.drivers/bnx2-entropy-source.patch b/patches.drivers/bnx2-entropy-source.patch new file mode 100644 index 0000000..dc5dc9d --- /dev/null +++ b/patches.drivers/bnx2-entropy-source.patch @@ -0,0 +1,40 @@ +From: Brandon Philips +Subject: [PATCH] bnx2: entropy source +Patch-mainline: never +References: FATE#307517 + +Current disk-less systems have no entropy source whatsoever. Therefore, the +network drivers tg3, bnx2, e1000, e1000e, igb and ixgbe should be enabled to +feed entropy to the kernel via the IRQF_SAMPLE_RANDOM flag when loaded. This +option shall not be enabled by default but implemented via a module option to +be activated by the administrator. + +Signed-off-by: Brandon Philips + +--- + drivers/net/bnx2.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/bnx2.c ++++ b/drivers/net/bnx2.c +@@ -84,6 +84,10 @@ MODULE_FIRMWARE(FW_MIPS_FILE_09); + MODULE_FIRMWARE(FW_RV2P_FILE_09); + MODULE_FIRMWARE(FW_RV2P_FILE_09_Ax); + ++static int entropy = 0; ++module_param(entropy, int, 0); ++MODULE_PARM_DESC(entropy, "Allow bnx2 to populate the /dev/random entropy pool"); ++ + static int disable_msi = 0; + + module_param(disable_msi, int, 0); +@@ -6081,6 +6085,9 @@ bnx2_request_irq(struct bnx2 *bp) + else + flags = IRQF_SHARED; + ++ if (entropy) ++ flags |= IRQF_SAMPLE_RANDOM; ++ + for (i = 0; i < bp->irq_nvecs; i++) { + irq = &bp->irq_tbl[i]; + rc = request_irq(irq->vector, irq->handler, flags, irq->name, diff --git a/patches.drivers/disable-catas_reset-by-default-to-avoid-problems-with-eeh.patch b/patches.drivers/disable-catas_reset-by-default-to-avoid-problems-with-eeh.patch new file mode 100644 index 0000000..dbf8ef8 --- /dev/null +++ b/patches.drivers/disable-catas_reset-by-default-to-avoid-problems-with-eeh.patch @@ -0,0 +1,45 @@ +From: Xiuling Ma +Subject: [PATCH] disable catas_reset by default to avoid problems with EEH +References: bnc#456389 +Patch-mainline: not yet + +PPC machines with EEH and Mellanox ib/net cards with catastrophic error +recovery that encounter a PCI bus error can crash and become +unresponsive. + +Disable the card reset to avoid this. + +NOTE: an upstream fix will come later once IBM can review a couple of +approaches I suggested since this fix is brute force. This driver didn't have +this reset on error feature in SLES10 so it isn't a feature removal. + +Signed-off-by: Xiuling Ma +Acked-by: Brandon Philips + +--- + drivers/infiniband/hw/mthca/mthca_catas.c | 2 +- + drivers/net/mlx4/catas.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/infiniband/hw/mthca/mthca_catas.c ++++ b/drivers/infiniband/hw/mthca/mthca_catas.c +@@ -51,7 +51,7 @@ static LIST_HEAD(catas_list); + static struct workqueue_struct *catas_wq; + static struct work_struct catas_work; + +-static int catas_reset_disable; ++static int catas_reset_disable = 1; + module_param_named(catas_reset_disable, catas_reset_disable, int, 0644); + MODULE_PARM_DESC(catas_reset_disable, "disable reset on catastrophic event if nonzero"); + +--- a/drivers/net/mlx4/catas.c ++++ b/drivers/net/mlx4/catas.c +@@ -44,7 +44,7 @@ static DEFINE_SPINLOCK(catas_lock); + static LIST_HEAD(catas_list); + static struct work_struct catas_work; + +-static int internal_err_reset = 1; ++static int internal_err_reset = 0; + module_param(internal_err_reset, int, 0644); + MODULE_PARM_DESC(internal_err_reset, + "Reset device on internal errors if non-zero (default 1)"); diff --git a/patches.drivers/driver-core-add-devname-module-aliases-to-allow-module-on-demand-auto-loading.patch b/patches.drivers/driver-core-add-devname-module-aliases-to-allow-module-on-demand-auto-loading.patch new file mode 100644 index 0000000..346fb42 --- /dev/null +++ b/patches.drivers/driver-core-add-devname-module-aliases-to-allow-module-on-demand-auto-loading.patch @@ -0,0 +1,178 @@ +From 578454ff7eab61d13a26b568f99a89a2c9edc881 Mon Sep 17 00:00:00 2001 +From: Kay Sievers +Date: Thu, 20 May 2010 18:07:20 +0200 +Subject: driver core: add devname module aliases to allow module on-demand auto-loading +Patch-mainline: 2.6.35 + +From: Kay Sievers + +commit 578454ff7eab61d13a26b568f99a89a2c9edc881 upstream. + +This adds: + alias: devname: +to some common kernel modules, which will allow the on-demand loading +of the kernel module when the device node is accessed. + +Ideally all these modules would be compiled-in, but distros seems too +much in love with their modularization that we need to cover the common +cases with this new facility. It will allow us to remove a bunch of pretty +useless init scripts and modprobes from init scripts. + +The static device node aliases will be carried in the module itself. The +program depmod will extract this information to a file in the module directory: + $ cat /lib/modules/2.6.34-00650-g537b60d-dirty/modules.devname + # Device nodes to trigger on-demand module loading. + microcode cpu/microcode c10:184 + fuse fuse c10:229 + ppp_generic ppp c108:0 + tun net/tun c10:200 + dm_mod mapper/control c10:235 + +Udev will pick up the depmod created file on startup and create all the +static device nodes which the kernel modules specify, so that these modules +get automatically loaded when the device node is accessed: + $ /sbin/udevd --debug + ... + static_dev_create_from_modules: mknod '/dev/cpu/microcode' c10:184 + static_dev_create_from_modules: mknod '/dev/fuse' c10:229 + static_dev_create_from_modules: mknod '/dev/ppp' c108:0 + static_dev_create_from_modules: mknod '/dev/net/tun' c10:200 + static_dev_create_from_modules: mknod '/dev/mapper/control' c10:235 + udev_rules_apply_static_dev_perms: chmod '/dev/net/tun' 0666 + udev_rules_apply_static_dev_perms: chmod '/dev/fuse' 0666 + +A few device nodes are switched to statically allocated numbers, to allow +the static nodes to work. This might also useful for systems which still run +a plain static /dev, which is completely unsafe to use with any dynamic minor +numbers. + +Note: +The devname aliases must be limited to the *common* and *single*instance* +device nodes, like the misc devices, and never be used for conceptually limited +systems like the loop devices, which should rather get fixed properly and get a +control node for losetup to talk to, instead of creating a random number of +device nodes in advance, regardless if they are ever used. + +This facility is to hide the mess distros are creating with too modualized +kernels, and just to hide that these modules are not compiled-in, and not to +paper-over broken concepts. Thanks! :) + +Cc: Greg Kroah-Hartman +Cc: David S. Miller +Cc: Miklos Szeredi +Cc: Chris Mason +Cc: Alasdair G Kergon +Cc: Tigran Aivazian +Cc: Ian Kent +Signed-Off-By: Kay Sievers +Signed-off-by: Greg Kroah-Hartman + +--- a/Documentation/devices.txt ++++ b/Documentation/devices.txt +@@ -443,6 +443,8 @@ Your cooperation is appreciated. + 231 = /dev/snapshot System memory snapshot device + 232 = /dev/kvm Kernel-based virtual machine (hardware virtualization extensions) + 233 = /dev/kmview View-OS A process with a view ++ 234 = /dev/btrfs-control Btrfs control device ++ 235 = /dev/autofs Autofs control device + 240-254 Reserved for local use + 255 Reserved for MISC_DYNAMIC_MINOR + +diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c +index 2cd8c54..fa6551d 100644 +--- a/arch/x86/kernel/microcode_core.c ++++ b/arch/x86/kernel/microcode_core.c +@@ -260,6 +260,7 @@ static void microcode_dev_exit(void) + } + + MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); ++MODULE_ALIAS("devname:cpu/microcode"); + #else + #define microcode_dev_init() 0 + #define microcode_dev_exit() do { } while (0) +diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c +index 5441688..c5f8eb1 100644 +--- a/drivers/net/ppp_generic.c ++++ b/drivers/net/ppp_generic.c +@@ -2926,5 +2926,5 @@ EXPORT_SYMBOL(ppp_output_wakeup); + EXPORT_SYMBOL(ppp_register_compressor); + EXPORT_SYMBOL(ppp_unregister_compressor); + MODULE_LICENSE("GPL"); +-MODULE_ALIAS_CHARDEV_MAJOR(PPP_MAJOR); +-MODULE_ALIAS("/dev/ppp"); ++MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0); ++MODULE_ALIAS("devname:ppp"); +diff --git a/drivers/net/tun.c b/drivers/net/tun.c +index 97b2553..005cad6 100644 +--- a/drivers/net/tun.c ++++ b/drivers/net/tun.c +@@ -1649,3 +1649,4 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); + MODULE_AUTHOR(DRV_COPYRIGHT); + MODULE_LICENSE("GPL"); + MODULE_ALIAS_MISCDEV(TUN_MINOR); ++MODULE_ALIAS("devname:net/tun"); +diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c +index d29b7f6..d832062 100644 +--- a/fs/autofs4/dev-ioctl.c ++++ b/fs/autofs4/dev-ioctl.c +@@ -736,11 +736,14 @@ static const struct file_operations _dev_ioctl_fops = { + }; + + static struct miscdevice _autofs_dev_ioctl_misc = { +- .minor = MISC_DYNAMIC_MINOR, ++ .minor = AUTOFS_MINOR, + .name = AUTOFS_DEVICE_NAME, + .fops = &_dev_ioctl_fops + }; + ++MODULE_ALIAS_MISCDEV(AUTOFS_MINOR); ++MODULE_ALIAS("devname:autofs"); ++ + /* Register/deregister misc character device */ + int autofs_dev_ioctl_init(void) + { +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index 1866dff..2909a03 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -832,11 +832,14 @@ static const struct file_operations btrfs_ctl_fops = { + }; + + static struct miscdevice btrfs_misc = { +- .minor = MISC_DYNAMIC_MINOR, ++ .minor = BTRFS_MINOR, + .name = "btrfs-control", + .fops = &btrfs_ctl_fops + }; + ++MODULE_ALIAS_MISCDEV(BTRFS_MINOR); ++MODULE_ALIAS("devname:btrfs-control"); ++ + static int btrfs_interface_init(void) + { + return misc_register(&btrfs_misc); +diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c +index eb7e942..e53df5e 100644 +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -18,6 +18,7 @@ + #include + + MODULE_ALIAS_MISCDEV(FUSE_MINOR); ++MODULE_ALIAS("devname:fuse"); + + static struct kmem_cache *fuse_req_cachep; + +diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h +index 8b5f7cc..b631c46 100644 +--- a/include/linux/miscdevice.h ++++ b/include/linux/miscdevice.h +@@ -31,6 +31,8 @@ + #define FUSE_MINOR 229 + #define KVM_MINOR 232 + #define VHOST_NET_MINOR 233 ++#define BTRFS_MINOR 234 ++#define AUTOFS_MINOR 235 + #define MISC_DYNAMIC_MINOR 255 + + struct device; diff --git a/patches.drivers/drm-nouveau-Don-t-clear-AGPCMD-completely-on-INIT_RE.patch b/patches.drivers/drm-nouveau-Don-t-clear-AGPCMD-completely-on-INIT_RE.patch new file mode 100644 index 0000000..c343430 --- /dev/null +++ b/patches.drivers/drm-nouveau-Don-t-clear-AGPCMD-completely-on-INIT_RE.patch @@ -0,0 +1,35 @@ +From feacc14de65224ccda1d8fae5140cdf043a151b0 Mon Sep 17 00:00:00 2001 +From: Francisco Jerez +Date: Thu, 17 Jun 2010 12:42:14 +0200 +Subject: [PATCH] drm/nouveau: Don't clear AGPCMD completely on INIT_RESET. + +We just need to clear the SBA and ENABLE bits to reset the AGP +controller: If the AGP bridge was configured to use "fast writes", +clearing the FW bit would break the subsequent MMIO writes and +eventually end with a lockup. + +Note that all the BIOSes I've seen do the same as we did (it works for +them because they don't use MMIO), OTOH the blob leaves FW untouched. + +Signed-off-by: Francisco Jerez +--- + drivers/gpu/drm/nouveau/nouveau_bios.c | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c +index abc382a..7c983d8 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_bios.c ++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c +@@ -1910,7 +1910,8 @@ init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) + /* no iexec->execute check by design */ + + pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19); +- bios_wr32(bios, NV_PBUS_PCI_NV_19, 0); ++ bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19 & ~0xf00); ++ + bios_wr32(bios, reg, value1); + + udelay(10); +-- +1.7.0.1 + diff --git a/patches.drivers/drm-nouveau-allow-cursor-image-and-position-to-survi.patch b/patches.drivers/drm-nouveau-allow-cursor-image-and-position-to-survi.patch new file mode 100644 index 0000000..b3151f3 --- /dev/null +++ b/patches.drivers/drm-nouveau-allow-cursor-image-and-position-to-survi.patch @@ -0,0 +1,110 @@ +From d83809c6fdb908ba708382c9a506f6647d1fa86d Mon Sep 17 00:00:00 2001 +From: Maarten Maathuis +Date: Sun, 9 May 2010 14:49:52 +0200 +Subject: [PATCH] drm/nouveau: allow cursor image and position to survive suspend + +- This isn't triggered yet on a normal kernel, because it still does a VT +switch, but it seemed like a good idea to fix this now. + +Tested-by: Maxim Levitsky +Signed-off-by: Maarten Maathuis +--- + drivers/gpu/drm/nouveau/nouveau_crtc.h | 2 ++ + drivers/gpu/drm/nouveau/nouveau_drv.c | 29 +++++++++++++++++++++++++++++ + drivers/gpu/drm/nouveau/nv04_cursor.c | 1 + + drivers/gpu/drm/nouveau/nv50_cursor.c | 1 + + 4 files changed, 33 insertions(+), 0 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h +index 49fa7b2..cb1ce2a 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_crtc.h ++++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h +@@ -40,6 +40,8 @@ struct nouveau_crtc { + int sharpness; + int last_dpms; + ++ int cursor_saved_x, cursor_saved_y; ++ + struct { + int cpp; + bool blanked; +diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c +index 1de974a..4bccba3 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_drv.c ++++ b/drivers/gpu/drm/nouveau/nouveau_drv.c +@@ -177,6 +177,13 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) + nouveau_bo_unpin(nouveau_fb->nvbo); + } + ++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { ++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); ++ ++ nouveau_bo_unmap(nv_crtc->cursor.nvbo); ++ nouveau_bo_unpin(nv_crtc->cursor.nvbo); ++ } ++ + NV_INFO(dev, "Evicting buffers...\n"); + ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM); + +@@ -318,12 +325,34 @@ nouveau_pci_resume(struct pci_dev *pdev) + nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM); + } + ++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { ++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); ++ int ret; ++ ++ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); ++ if (!ret) ++ ret = nouveau_bo_map(nv_crtc->cursor.nvbo); ++ if (ret) ++ NV_ERROR(dev, "Could not pin/map cursor.\n"); ++ } ++ + if (dev_priv->card_type < NV_50) { + nv04_display_restore(dev); + NVLockVgaCrtcs(dev, false); + } else + nv50_display_init(dev); + ++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { ++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); ++ ++ nv_crtc->cursor.set_offset(nv_crtc, ++ nv_crtc->cursor.nvbo->bo.offset - ++ dev_priv->vm_vram_base); ++ ++ nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, ++ nv_crtc->cursor_saved_y); ++ } ++ + /* Force CLUT to get re-loaded during modeset */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); +diff --git a/drivers/gpu/drm/nouveau/nv04_cursor.c b/drivers/gpu/drm/nouveau/nv04_cursor.c +index 89a91b9..aaf3de3 100644 +--- a/drivers/gpu/drm/nouveau/nv04_cursor.c ++++ b/drivers/gpu/drm/nouveau/nv04_cursor.c +@@ -20,6 +20,7 @@ nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) + static void + nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) + { ++ nv_crtc->cursor_saved_x = x; nv_crtc->cursor_saved_y = y; + NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index, + NV_PRAMDAC_CU_START_POS, + XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) | +diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c +index 753e723..03ad7ab 100644 +--- a/drivers/gpu/drm/nouveau/nv50_cursor.c ++++ b/drivers/gpu/drm/nouveau/nv50_cursor.c +@@ -107,6 +107,7 @@ nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) + { + struct drm_device *dev = nv_crtc->base.dev; + ++ nv_crtc->cursor_saved_x = x; nv_crtc->cursor_saved_y = y; + nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS(nv_crtc->index), + ((y & 0xFFFF) << 16) | (x & 0xFFFF)); + /* Needed to make the cursor move. */ +-- +1.7.0.1 + diff --git a/patches.drivers/e1000-enhance-frame-fragment-detection.patch b/patches.drivers/e1000-enhance-frame-fragment-detection.patch new file mode 100644 index 0000000..31f47e9 --- /dev/null +++ b/patches.drivers/e1000-enhance-frame-fragment-detection.patch @@ -0,0 +1,68 @@ +From: Brandeburg, Jesse +Subject: [PATCH] e1000: enhance frame fragment detection +References: bnc#567376, CVE-2009-4536 +Patch-Mainline: Yes + +A security discussion was recently given: +http://events.ccc.de/congress/2009/Fahrplan//events/3596.en.html And a patch +that I submitted awhile back was brought up. Apparently some of their testing +revealed that they were able to force a buffer fragment in e1000 in which the +trailing fragment was greater than 4 bytes. As a result the fragment check I +introduced failed to detect the fragement and a partial invalid frame was +passed up into the network stack. I've written this patch to correct it. I'm +in the process of testing it now, but it makes good logical sense to me. +Effectively it maintains a per-adapter state variable which detects a non-EOP +frame, and discards it and subsequent non-EOP frames leading up to _and_ +_including_ the next positive-EOP frame (as it is by definition the last +fragment). This should prevent any and all partial frames from entering the +network stack from e1000. + +Signed-off-by: Jesse Brandeburg +Signed-off-by: Neil Horman +Signed-off-by: Brandon Philips + +--- + + drivers/net/e1000/e1000.h | 2 ++ + drivers/net/e1000/e1000_main.c | 13 +++++++++++-- + 2 files changed, 13 insertions(+), 2 deletions(-) + + +--- a/drivers/net/e1000/e1000.h ++++ b/drivers/net/e1000/e1000.h +@@ -326,6 +326,8 @@ struct e1000_adapter { + /* for ioport free */ + int bars; + int need_ioport; ++ ++ bool discarding; + }; + + enum e1000_state_t { +--- a/drivers/net/e1000/e1000_main.c ++++ b/drivers/net/e1000/e1000_main.c +@@ -3834,13 +3834,22 @@ static bool e1000_clean_rx_irq(struct e1 + + length = le16_to_cpu(rx_desc->length); + /* !EOP means multiple descriptors were used to store a single +- * packet, also make sure the frame isn't just CRC only */ +- if (unlikely(!(status & E1000_RXD_STAT_EOP) || (length <= 4))) { ++ * packet, if thats the case we need to toss it. In fact, we ++ * to toss every packet with the EOP bit clear and the next ++ * frame that _does_ have the EOP bit set, as it is by ++ * definition only a frame fragment ++ */ ++ if (unlikely(!(status & E1000_RXD_STAT_EOP))) ++ adapter->discarding = true; ++ ++ if (adapter->discarding) { + /* All receives must fit into a single buffer */ + E1000_DBG("%s: Receive packet consumed multiple" + " buffers\n", netdev->name); + /* recycle */ + buffer_info->skb = skb; ++ if (status & E1000_RXD_STAT_EOP) ++ adapter->discarding = false; + goto next_desc; + } + diff --git a/patches.drivers/e1000-entropy-source.patch b/patches.drivers/e1000-entropy-source.patch new file mode 100644 index 0000000..d0ec715 --- /dev/null +++ b/patches.drivers/e1000-entropy-source.patch @@ -0,0 +1,47 @@ +From: Jiri Benc +Subject: Enable e1000 as entropy source (disabled by default) +References: FATE#307517 +Patch-mainline: never + +Based on the patch by Oracle: + +> e1000: Add IRQF_SAMPLE_RANDOM flag to e1000 as a module option +> +> This patch allows for the bnx2 to add to the /dev/random entropy pool +> via a module parameter, entropy. +> +> 0 - default for EL5 - do not populate the entropy pool +> 1 - optional - Uses IRQF_SAMPLE_RANDOM flag on request_irq calls to populate +> the /dev/random pool +> +> Signed-off-by: John Sobecki + +Signed-off-by: Brandon Philips + +--- + drivers/net/e1000/e1000_main.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/e1000/e1000_main.c ++++ b/drivers/net/e1000/e1000_main.c +@@ -213,6 +213,10 @@ static int debug = NETIF_MSG_DRV | NETIF + module_param(debug, int, 0); + MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + ++static int entropy = 0; ++module_param(entropy, int, 0); ++MODULE_PARM_DESC(entropy, "Allow e1000 to populate the /dev/random entropy pool"); ++ + /** + * e1000_init_module - Driver Registration Routine + * +@@ -262,6 +266,9 @@ static int e1000_request_irq(struct e100 + int irq_flags = IRQF_SHARED; + int err; + ++ if (entropy) ++ irq_flags |= IRQF_SAMPLE_RANDOM; ++ + err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name, + netdev); + if (err) { diff --git a/patches.drivers/e1000e-enhance-frame-fragment-detection.patch b/patches.drivers/e1000e-enhance-frame-fragment-detection.patch new file mode 100644 index 0000000..a68ecee --- /dev/null +++ b/patches.drivers/e1000e-enhance-frame-fragment-detection.patch @@ -0,0 +1,142 @@ +From: Neil Horman +Subject: [PATCH] e1000e: enhance frame fragment detection +References: bnc#567376, CVE-2009-4538 + +A security discussion was recently given: +http://events.ccc.de/congress/2009/Fahrplan//events/3596.en.html And a patch +that I submitted awhile back was brought up. Apparently some of their testing +revealed that they were able to force a buffer fragment in e1000e in which the +trailing fragment was greater than 4 bytes. As a result the fragment check I +introduced failed to detect the fragement and a partial invalid frame was +passed up into the network stack. I've written this patch to correct it. I'm +in the process of testing it now, but it makes good logical sense to me. +Effectively it maintains a per-adapter state variable which detects a non-EOP +frame, and discards it and subsequent non-EOP frames leading up to _and_ +_including_ the next positive-EOP frame (as it is by definition the last +fragment). This should prevent any and all partial frames from entering the +network stack from e1000e + +Signed-off-by: Neil Horman +Signed-off-by: Brandon Philips +--- + drivers/net/e1000e/e1000.h | 3 ++- + drivers/net/e1000e/netdev.c | 13 +++++++++++-- + 2 files changed, 13 insertions(+), 3 deletions(-) + +Index: linux-2.6.31-openSUSE-11.2/drivers/net/e1000e/e1000.h +=================================================================== +--- linux-2.6.31-openSUSE-11.2.orig/drivers/net/e1000e/e1000.h ++++ linux-2.6.31-openSUSE-11.2/drivers/net/e1000e/e1000.h +@@ -412,7 +412,8 @@ struct e1000_info { + enum e1000_state_t { + __E1000_TESTING, + __E1000_RESETTING, +- __E1000_DOWN ++ __E1000_DOWN, ++ __E1000_DISCARDING + }; + + enum latency_range { +Index: linux-2.6.31-openSUSE-11.2/drivers/net/e1000e/netdev.c +=================================================================== +--- linux-2.6.31-openSUSE-11.2.orig/drivers/net/e1000e/netdev.c ++++ linux-2.6.31-openSUSE-11.2/drivers/net/e1000e/netdev.c +@@ -483,12 +483,21 @@ static bool e1000_clean_rx_irq(struct e1 + length = le16_to_cpu(rx_desc->length); + + /* !EOP means multiple descriptors were used to store a single +- * packet, also make sure the frame isn't just CRC only */ +- if (!(status & E1000_RXD_STAT_EOP) || (length <= 4)) { ++ * packet, if thats the case we need to toss it. In fact, we ++ * to toss every packet with the EOP bit clear and the next ++ * frame that _does_ have the EOP bit set, as it is by ++ * definition only a frame fragment ++ */ ++ if (unlikely(!(status & E1000_RXD_STAT_EOP))) ++ set_bit(__E1000_DISCARDING, &adapter->state); ++ ++ if (test_bit(__E1000_DISCARDING, &adapter->state)) { + /* All receives must fit into a single buffer */ + e_dbg("Receive packet consumed multiple buffers\n"); + /* recycle */ + buffer_info->skb = skb; ++ if (status & E1000_RXD_STAT_EOP) ++ clear_bit(__E1000_DISCARDING, &adapter->state); + goto next_desc; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/patches.drivers/e1000e-entropy-source.patch b/patches.drivers/e1000e-entropy-source.patch new file mode 100644 index 0000000..738e9b2 --- /dev/null +++ b/patches.drivers/e1000e-entropy-source.patch @@ -0,0 +1,92 @@ +From: Jiri Benc +Subject: Enable e1000e as entropy source (disabled by default) +References: FATE#307517 +Patch-mainline: never + +Current disk-less systems have no entropy source whatsoever. Therefore, the +network drivers tg3, bnx2, e1000, e1000e, igb and ixgbe should be enabled to +feed entropy to the kernel via the IRQF_SAMPLE_RANDOM flag when loaded. This +option shall not be enabled by default but implemented via a module option to +be activated by the administrator. + +Signed-off-by: Brandon Philips + +--- + drivers/net/e1000e/e1000.h | 1 + + drivers/net/e1000e/netdev.c | 14 +++++++++----- + drivers/net/e1000e/param.c | 4 ++++ + 3 files changed, 14 insertions(+), 5 deletions(-) + +Index: linux-2.6.34-master/drivers/net/e1000e/e1000.h +=================================================================== +--- linux-2.6.34-master.orig/drivers/net/e1000e/e1000.h ++++ linux-2.6.34-master/drivers/net/e1000e/e1000.h +@@ -466,6 +466,7 @@ extern void e1000e_reset_interrupt_capab + extern void e1000e_disable_aspm(struct pci_dev *pdev, u16 state); + + extern unsigned int copybreak; ++extern int entropy; + + extern char *e1000e_get_hw_dev_name(struct e1000_hw *hw); + +Index: linux-2.6.34-master/drivers/net/e1000e/netdev.c +=================================================================== +--- linux-2.6.34-master.orig/drivers/net/e1000e/netdev.c ++++ linux-2.6.34-master/drivers/net/e1000e/netdev.c +@@ -1496,8 +1496,8 @@ static int e1000_request_msix(struct e10 + else + memcpy(adapter->rx_ring->name, netdev->name, IFNAMSIZ); + err = request_irq(adapter->msix_entries[vector].vector, +- e1000_intr_msix_rx, 0, adapter->rx_ring->name, +- netdev); ++ e1000_intr_msix_rx, entropy ? IRQF_SAMPLE_RANDOM : 0, ++ adapter->rx_ring->name, netdev); + if (err) + goto out; + adapter->rx_ring->itr_register = E1000_EITR_82574(vector); +@@ -1538,6 +1538,7 @@ static int e1000_request_irq(struct e100 + { + struct net_device *netdev = adapter->netdev; + int err; ++ int irq_flags = 0; + + if (adapter->msix_entries) { + err = e1000_request_msix(adapter); +@@ -1549,7 +1550,8 @@ static int e1000_request_irq(struct e100 + e1000e_set_interrupt_capability(adapter); + } + if (adapter->flags & FLAG_MSI_ENABLED) { +- err = request_irq(adapter->pdev->irq, e1000_intr_msi, 0, ++ err = request_irq(adapter->pdev->irq, e1000_intr_msi, ++ entropy ? IRQF_SAMPLE_RANDOM : 0, + netdev->name, netdev); + if (!err) + return err; +@@ -1559,8 +1561,10 @@ static int e1000_request_irq(struct e100 + adapter->int_mode = E1000E_INT_MODE_LEGACY; + } + +- err = request_irq(adapter->pdev->irq, e1000_intr, IRQF_SHARED, +- netdev->name, netdev); ++ if (entropy) ++ irq_flags |= IRQF_SAMPLE_RANDOM; ++ err = request_irq(adapter->pdev->irq, e1000_intr, ++ irq_flags | IRQF_SHARED, netdev->name, netdev); + if (err) + e_err("Unable to allocate interrupt, Error: %d\n", err); + +Index: linux-2.6.34-master/drivers/net/e1000e/param.c +=================================================================== +--- linux-2.6.34-master.orig/drivers/net/e1000e/param.c ++++ linux-2.6.34-master/drivers/net/e1000e/param.c +@@ -31,6 +31,10 @@ + + #include "e1000.h" + ++int entropy = 0; ++module_param(entropy, int, 0); ++MODULE_PARM_DESC(entropy, "Allow e1000e to populate the /dev/random entropy pool"); ++ + /* + * This is the only thing that needs to be changed to adjust the + * maximum number of ports that the driver can manage. diff --git a/patches.drivers/ehea-modinfo.patch b/patches.drivers/ehea-modinfo.patch new file mode 100644 index 0000000..f2f9049 --- /dev/null +++ b/patches.drivers/ehea-modinfo.patch @@ -0,0 +1,43 @@ +Subject: add alias entry for portN properties +From: olh@suse.de +References: 435215 - LTC48564 +Patch-mainline: not yet + +Use separate table for alias entries in the ehea module, +otherwise the probe() function will operate on the separate ports +instead of the lhea-"root" entry of the device-tree + +--- + drivers/net/ehea/ehea_main.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +--- a/drivers/net/ehea/ehea_main.c ++++ b/drivers/net/ehea/ehea_main.c +@@ -111,6 +111,19 @@ static int __devinit ehea_probe_adapter( + + static int __devexit ehea_remove(struct of_device *dev); + ++static struct of_device_id ehea_module_device_table[] = { ++ { ++ .name = "lhea", ++ .compatible = "IBM,lhea", ++ }, ++ { ++ .type = "network", ++ .compatible = "IBM,lhea-ethernet", ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ehea_module_device_table); ++ + static struct of_device_id ehea_device_table[] = { + { + .name = "lhea", +@@ -118,7 +131,6 @@ static struct of_device_id ehea_device_t + }, + {}, + }; +-MODULE_DEVICE_TABLE(of, ehea_device_table); + + static struct of_platform_driver ehea_driver = { + .name = "ehea", diff --git a/patches.drivers/elousb.patch b/patches.drivers/elousb.patch new file mode 100644 index 0000000..efc7e17 --- /dev/null +++ b/patches.drivers/elousb.patch @@ -0,0 +1,380 @@ +From: Jiri Kosina +Subject: Elo USB touchscreen driver +Patch-mainline: will be submitted for 2.6.28 +References: FATE#304972 + +This is a driver for Elo USB touchscreen devices. + +Signed-off-by: Vojtech Pavlik +Acked-by: Jiri Kosina + +--- + drivers/hid/hid-core.c | 2 + drivers/hid/hid-ids.h | 2 + drivers/input/touchscreen/Kconfig | 12 + + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/elousb.c | 305 +++++++++++++++++++++++++++++++++++++ + 5 files changed, 322 insertions(+) + +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -1567,6 +1567,8 @@ static const struct hid_device_id hid_ig + { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_4000U) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_4500U) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -171,7 +171,9 @@ + #define USB_VENDOR_ID_DRAGONRISE 0x0079 + + #define USB_VENDOR_ID_ELO 0x04E7 ++#define USB_DEVICE_ID_ELO_4000U 0x0009 + #define USB_DEVICE_ID_ELO_TS2700 0x0020 ++#define USB_DEVICE_ID_ELO_4500U 0x0030 + + #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f + #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -193,6 +193,18 @@ config TOUCHSCREEN_ELO + To compile this driver as a module, choose M here: the + module will be called elo. + ++config TOUCHSCREEN_ELOUSB ++ tristate "Elo USB touchscreens" ++ select USB ++ help ++ Say Y here if you have an Elo USB touchscreen connected to ++ your system. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called elousb. ++ + config TOUCHSCREEN_WACOM_W8001 + tristate "Wacom W8001 penabled serial touchscreen" + select SERIO +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dyn + obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o + obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o + obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o ++obj-$(CONFIG_TOUCHSCREEN_ELOUSB) += elousb.o + obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o + obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o + obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o +--- /dev/null ++++ b/drivers/input/touchscreen/elousb.c +@@ -0,0 +1,305 @@ ++/* ++ * Copyright (c) 1999-2001 Vojtech Pavlik ++ * ++ * Elo USB touchscreen support ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Should you need to contact me, the author, you can do so either by ++ * e-mail - mail your message to , or by paper mail: ++ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Version Information ++ */ ++#define DRIVER_VERSION "v1.1" ++#define DRIVER_AUTHOR "Vojtech Pavlik " ++#define DRIVER_DESC "Elo USB touchscreen driver" ++#define DRIVER_LICENSE "GPL" ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE(DRIVER_LICENSE); ++ ++struct elousb { ++ char name[128]; ++ char phys[64]; ++ struct usb_device *usbdev; ++ struct input_dev *dev; ++ struct urb *irq; ++ ++ unsigned char *data; ++ dma_addr_t data_dma; ++}; ++ ++static void elousb_irq(struct urb *urb) ++{ ++ struct elousb *elo = urb->context; ++ unsigned char *data = elo->data; ++ struct input_dev *dev = elo->dev; ++ int status; ++ ++ switch (urb->status) { ++ case 0: /* success */ ++ break; ++ case -ECONNRESET: /* unlink */ ++ case -ENOENT: ++ case -ESHUTDOWN: ++ return; ++ /* -EPIPE: should clear the halt */ ++ default: /* error */ ++ goto resubmit; ++ } ++ ++ if (data[0] != 'T') /* Mandatory ELO packet marker */ ++ return; ++ ++ ++ input_report_abs(dev, ABS_X, ((u32)data[3] << 8) | data[2]); ++ input_report_abs(dev, ABS_Y, ((u32)data[5] << 8) | data[4]); ++ ++ input_report_abs(dev, ABS_PRESSURE, ++ (data[1] & 0x80) ? (((u32)data[7] << 8) | data[6]): 0); ++ ++ if (data[1] & 0x03) { ++ input_report_key(dev, BTN_TOUCH, 1); ++ input_sync(dev); ++ } ++ ++ if (data[1] & 0x04) ++ input_report_key(dev, BTN_TOUCH, 0); ++ ++ input_sync(dev); ++ ++resubmit: ++ status = usb_submit_urb (urb, GFP_ATOMIC); ++ if (status) ++ err ("can't resubmit intr, %s-%s/input0, status %d", ++ elo->usbdev->bus->bus_name, ++ elo->usbdev->devpath, status); ++} ++ ++static int elousb_open(struct input_dev *dev) ++{ ++ struct elousb *elo = input_get_drvdata(dev); ++ ++ elo->irq->dev = elo->usbdev; ++ if (usb_submit_urb(elo->irq, GFP_KERNEL)) ++ return -EIO; ++ ++ return 0; ++} ++ ++static void elousb_close(struct input_dev *dev) ++{ ++ struct elousb *elo = input_get_drvdata(dev); ++ ++ usb_kill_urb(elo->irq); ++} ++ ++static int elousb_probe(struct usb_interface *intf, const struct usb_device_id *id) ++{ ++ struct usb_device *dev = interface_to_usbdev(intf); ++ struct usb_host_interface *interface; ++ struct usb_endpoint_descriptor *endpoint; ++ struct hid_descriptor *hdesc; ++ struct elousb *elo; ++ struct input_dev *input_dev; ++ int pipe, i; ++ unsigned int rsize = 0; ++ int error = -ENOMEM; ++ char *rdesc; ++ ++ interface = intf->cur_altsetting; ++ ++ if (interface->desc.bNumEndpoints != 1) ++ return -ENODEV; ++ ++ endpoint = &interface->endpoint[0].desc; ++ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) ++ return -ENODEV; ++ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) ++ return -ENODEV; ++ ++ if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && ++ (!interface->desc.bNumEndpoints || ++ usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { ++ err("HID class descriptor not present"); ++ return -ENODEV; ++ } ++ ++ for (i = 0; i < hdesc->bNumDescriptors; i++) ++ if (hdesc->desc[i].bDescriptorType == HID_DT_REPORT) ++ rsize = le16_to_cpu(hdesc->desc[i].wDescriptorLength); ++ ++ if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { ++ err("weird size of report descriptor (%u)", rsize); ++ return -ENODEV; ++ } ++ ++ ++ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); ++ ++ elo = kzalloc(sizeof(struct elousb), GFP_KERNEL); ++ input_dev = input_allocate_device(); ++ if (!elo || !input_dev) ++ goto fail1; ++ ++ elo->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &elo->data_dma); ++ if (!elo->data) ++ goto fail1; ++ ++ elo->irq = usb_alloc_urb(0, GFP_KERNEL); ++ if (!elo->irq) ++ goto fail2; ++ ++ if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) ++ goto fail3; ++ ++ elo->usbdev = dev; ++ elo->dev = input_dev; ++ ++ if ((error = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ++ HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, ++ interface->desc.bInterfaceNumber, ++ NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) { ++ err("setting HID idle timeout failed, error %d", error); ++ error = -ENODEV; ++ goto fail4; ++ } ++ ++ if ((error = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ++ USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, ++ HID_DT_REPORT << 8, interface->desc.bInterfaceNumber, ++ rdesc, rsize, USB_CTRL_GET_TIMEOUT)) < rsize) { ++ err("reading HID report descriptor failed, error %d", error); ++ error = -ENODEV; ++ goto fail4; ++ } ++ ++ if (dev->manufacturer) ++ strlcpy(elo->name, dev->manufacturer, sizeof(elo->name)); ++ ++ if (dev->product) { ++ if (dev->manufacturer) ++ strlcat(elo->name, " ", sizeof(elo->name)); ++ strlcat(elo->name, dev->product, sizeof(elo->name)); ++ } ++ ++ if (!strlen(elo->name)) ++ snprintf(elo->name, sizeof(elo->name), ++ "Elo touchscreen %04x:%04x", ++ le16_to_cpu(dev->descriptor.idVendor), ++ le16_to_cpu(dev->descriptor.idProduct)); ++ ++ usb_make_path(dev, elo->phys, sizeof(elo->phys)); ++ strlcat(elo->phys, "/input0", sizeof(elo->phys)); ++ ++ input_dev->name = elo->name; ++ input_dev->phys = elo->phys; ++ usb_to_input_id(dev, &input_dev->id); ++ input_dev->dev.parent = &intf->dev; ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); ++ set_bit(BTN_TOUCH, input_dev->keybit); ++ input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y); ++ set_bit(ABS_PRESSURE, input_dev->absbit); ++ ++ input_set_abs_params(input_dev, ABS_X, 0, 4000, 0, 0); ++ input_set_abs_params(input_dev, ABS_Y, 0, 3840, 0, 0); ++ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 0, 0); ++ ++ input_set_drvdata(input_dev, elo); ++ ++ input_dev->open = elousb_open; ++ input_dev->close = elousb_close; ++ ++ usb_fill_int_urb(elo->irq, dev, pipe, elo->data, 8, ++ elousb_irq, elo, endpoint->bInterval); ++ elo->irq->transfer_dma = elo->data_dma; ++ elo->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ input_register_device(elo->dev); ++ ++ usb_set_intfdata(intf, elo); ++ return 0; ++ ++fail4: ++ kfree(rdesc); ++fail3: ++ usb_free_urb(elo->irq); ++fail2: ++ usb_buffer_free(dev, 8, elo->data, elo->data_dma); ++fail1: ++ input_free_device(input_dev); ++ kfree(elo); ++ return -ENOMEM; ++} ++ ++static void elousb_disconnect(struct usb_interface *intf) ++{ ++ struct elousb *elo = usb_get_intfdata (intf); ++ ++ usb_set_intfdata(intf, NULL); ++ if (elo) { ++ usb_kill_urb(elo->irq); ++ input_unregister_device(elo->dev); ++ usb_free_urb(elo->irq); ++ usb_buffer_free(interface_to_usbdev(intf), 8, elo->data, elo->data_dma); ++ kfree(elo); ++ } ++} ++ ++static struct usb_device_id elousb_id_table [] = { ++ { USB_DEVICE(0x04e7, 0x0009) }, /* CarrolTouch 4000U */ ++ { USB_DEVICE(0x04e7, 0x0030) }, /* CarrolTouch 4500U */ ++ { } /* Terminating entry */ ++}; ++ ++MODULE_DEVICE_TABLE (usb, elousb_id_table); ++ ++static struct usb_driver elousb_driver = { ++ .name = "elousb", ++ .probe = elousb_probe, ++ .disconnect = elousb_disconnect, ++ .id_table = elousb_id_table, ++}; ++ ++static int __init elousb_init(void) ++{ ++ int retval = usb_register(&elousb_driver); ++ if (retval == 0) ++ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" DRIVER_DESC); ++ return retval; ++} ++ ++static void __exit elousb_exit(void) ++{ ++ usb_deregister(&elousb_driver); ++} ++ ++module_init(elousb_init); ++module_exit(elousb_exit); diff --git a/patches.drivers/igb-entropy-source.patch b/patches.drivers/igb-entropy-source.patch new file mode 100644 index 0000000..4b58900 --- /dev/null +++ b/patches.drivers/igb-entropy-source.patch @@ -0,0 +1,70 @@ +From: Jiri Benc +Subject: Enable igb as entropy source (disabled by default) +References: FATE#307517 +Patch-mainline: never + +Current disk-less systems have no entropy source whatsoever. Therefore, the +network drivers tg3, bnx2, e1000, e1000e, igb and ixgbe should be enabled to +feed entropy to the kernel via the IRQF_SAMPLE_RANDOM flag when loaded. This +option shall not be enabled by default but implemented via a module option to +be activated by the administrator. + +Signed-off-by: Brandon Philips + +--- + drivers/net/igb/igb_main.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +--- a/drivers/net/igb/igb_main.c ++++ b/drivers/net/igb/igb_main.c +@@ -60,6 +60,10 @@ static const struct e1000_info *igb_info + [board_82575] = &e1000_82575_info, + }; + ++static int entropy = 0; ++module_param(entropy, int, 0); ++MODULE_PARM_DESC(entropy, "Allow igb to populate the /dev/random entropy pool"); ++ + static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = { + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER), board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_FIBER), board_82575 }, +@@ -587,7 +591,8 @@ static int igb_request_msix(struct igb_a + int i, err = 0, vector = 0; + + err = request_irq(adapter->msix_entries[vector].vector, +- igb_msix_other, 0, netdev->name, adapter); ++ igb_msix_other, entropy ? IRQF_SAMPLE_RANDOM : 0, ++ netdev->name, adapter); + if (err) + goto out; + vector++; +@@ -882,6 +887,10 @@ static int igb_request_irq(struct igb_ad + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + int err = 0; ++ int irq_flags = 0; ++ ++ if (entropy) ++ irq_flags = IRQF_SAMPLE_RANDOM; + + if (adapter->msix_entries) { + err = igb_request_msix(adapter); +@@ -916,7 +925,7 @@ static int igb_request_irq(struct igb_ad + } + + if (adapter->flags & IGB_FLAG_HAS_MSI) { +- err = request_irq(adapter->pdev->irq, igb_intr_msi, 0, ++ err = request_irq(adapter->pdev->irq, igb_intr_msi, irq_flags, + netdev->name, adapter); + if (!err) + goto request_done; +@@ -926,7 +935,8 @@ static int igb_request_irq(struct igb_ad + adapter->flags &= ~IGB_FLAG_HAS_MSI; + } + +- err = request_irq(adapter->pdev->irq, igb_intr, IRQF_SHARED, ++ irq_flags |= IRQF_SHARED; ++ err = request_irq(adapter->pdev->irq, igb_intr, irq_flags, + netdev->name, adapter); + + if (err) diff --git a/patches.drivers/input-Add-LED-support-to-Synaptics-device b/patches.drivers/input-Add-LED-support-to-Synaptics-device new file mode 100644 index 0000000..f6ec12f --- /dev/null +++ b/patches.drivers/input-Add-LED-support-to-Synaptics-device @@ -0,0 +1,218 @@ +From: Takashi Iwai +Subject: [PATCH 2/2] input: Add LED support to Synaptics device +Patch-mainline: Submitted +References: bnc#547370,bnc#582529,bnc#589014 + +The new Synaptics devices have an LED on the top-left corner. +This patch adds a new LED class device to control it. It's created +dynamically upon synaptics device probing. + +The LED is controlled via the command 0x0a with parameters 0x88 or 0x10. +This seems only on/off control although other value might be accepted. + +The detection of the LED isn't clear yet. It should have been the new +capability bits that indicate the presence, but on real machines, it +doesn't fit. So, for the time being, the driver checks the product id +in the ext capability bits and assumes that LED exists on the known +devices. + +Signed-off-by: Takashi Iwai + +--- + drivers/input/mouse/Kconfig | 9 +++ + drivers/input/mouse/synaptics.c | 111 ++++++++++++++++++++++++++++++++++++++++ + drivers/input/mouse/synaptics.h | 3 + + 3 files changed, 123 insertions(+) + +--- a/drivers/input/mouse/Kconfig ++++ b/drivers/input/mouse/Kconfig +@@ -19,6 +19,7 @@ + select SERIO_LIBPS2 + select SERIO_I8042 if X86 + select SERIO_GSCPS2 if GSC ++ select LEDS_CLASS if MOUSE_PS2_SYNAPICS_LED + help + Say Y here if you have a PS/2 mouse connected to your system. This + includes the standard 2 or 3-button PS/2 mouse, as well as PS/2 +@@ -67,6 +68,14 @@ + + If unsure, say Y. + ++config MOUSE_PS2_SYNAPTICS_LED ++ bool "Support embedded LED on Synaptics devices" ++ depends on MOUSE_PS2_SYNAPTICS ++ select NEW_LEDS ++ help ++ Say Y here if you have a Synaptics device with an embedded LED. ++ This will enable LED class driver to control the LED device. ++ + config MOUSE_PS2_LIFEBOOK + bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED + default y +--- a/drivers/input/mouse/synaptics.c ++++ b/drivers/input/mouse/synaptics.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include "psmouse.h" + #include "synaptics.h" +@@ -335,6 +336,110 @@ + serio_register_port(serio); + } + ++#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_LED ++/* ++ * LED handling: ++ * Some Synaptics devices have an embeded LED at the top-left corner. ++ */ ++ ++struct synaptics_led { ++ struct psmouse *psmouse; ++ struct work_struct work; ++ struct led_classdev cdev; ++}; ++ ++static void synaptics_set_led(struct psmouse *psmouse, int on) ++{ ++ int i; ++ unsigned char cmd = on ? 0x88 : 0x10; ++ ++ ps2_begin_command(&psmouse->ps2dev); ++ if (__ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) ++ goto out; ++ for (i = 6; i >= 0; i -= 2) { ++ unsigned char d = (cmd >> i) & 3; ++ if (__ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES)) ++ goto out; ++ } ++ cmd = 0x0a; ++ __ps2_command(&psmouse->ps2dev, &cmd, PSMOUSE_CMD_SETRATE); ++ out: ++ ps2_end_command(&psmouse->ps2dev); ++} ++ ++static void synaptics_led_work(struct work_struct *work) ++{ ++ struct synaptics_led *led; ++ ++ led = container_of(work, struct synaptics_led, work); ++ synaptics_set_led(led->psmouse, led->cdev.brightness); ++} ++ ++static void synaptics_led_cdev_brightness_set(struct led_classdev *cdev, ++ enum led_brightness value) ++{ ++ struct synaptics_led *led; ++ ++ led = container_of(cdev, struct synaptics_led, cdev); ++ schedule_work(&led->work); ++} ++ ++static void synaptics_sync_led(struct psmouse *psmouse) ++{ ++ struct synaptics_data *priv = psmouse->private; ++ ++ if (priv->led) ++ synaptics_set_led(psmouse, priv->led->cdev.brightness); ++} ++ ++static int synaptics_init_led(struct psmouse *psmouse) ++{ ++ struct synaptics_data *priv = psmouse->private; ++ struct synaptics_led *led; ++ int err; ++ ++ /* FIXME: LED is supposedly detectable in cap0c[1] 0x20, but it seems ++ * not working on real machines. ++ * So we check the product id to be sure. ++ */ ++ if (!priv->ext_cap_0c || SYN_CAP_PRODUCT_ID(priv->ext_cap) != 0xe4) ++ return 0; ++ ++ printk(KERN_INFO "synaptics: support LED control\n"); ++ led = kzalloc(sizeof(struct synaptics_led), GFP_KERNEL); ++ if (!led) ++ return -ENOMEM; ++ led->psmouse = psmouse; ++ INIT_WORK(&led->work, synaptics_led_work); ++ led->cdev.name = "psmouse::synaptics"; ++ led->cdev.brightness_set = synaptics_led_cdev_brightness_set; ++ led->cdev.flags = LED_CORE_SUSPENDRESUME; ++ err = led_classdev_register(NULL, &led->cdev); ++ if (err < 0) { ++ kfree(led); ++ return err; ++ } ++ priv->led = led; ++ return 0; ++} ++ ++static void synaptics_free_led(struct psmouse *psmouse) ++{ ++ struct synaptics_data *priv = psmouse->private; ++ ++ if (!priv->led) ++ return; ++ cancel_work_sync(&priv->led->work); ++ synaptics_set_led(psmouse, 0); ++ led_classdev_unregister(&priv->led->cdev); ++ kfree(priv->led); ++} ++#else ++#define synaptics_init_led(ps) 0 ++#define synaptics_free_led(ps) do {} while (0) ++#define synaptics_sync_led(ps) do {} while (0) ++#endif ++ + /***************************************************************************** + * Functions to interpret the absolute mode packets + ****************************************************************************/ +@@ -622,6 +727,7 @@ + + static void synaptics_disconnect(struct psmouse *psmouse) + { ++ synaptics_free_led(psmouse); + synaptics_reset(psmouse); + kfree(psmouse->private); + psmouse->private = NULL; +@@ -653,6 +759,8 @@ + return -1; + } + ++ synaptics_sync_led(psmouse); ++ + return 0; + } + +@@ -727,6 +835,9 @@ + SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), + priv->model_id, priv->capabilities, priv->ext_cap, priv->ext_cap_0c); + ++ if (synaptics_init_led(psmouse) < 0) ++ goto init_fail; ++ + set_input_params(psmouse->dev, priv); + + /* +--- a/drivers/input/mouse/synaptics.h ++++ b/drivers/input/mouse/synaptics.h +@@ -94,6 +94,8 @@ + signed char scroll; + }; + ++struct synaptics_led; ++ + struct synaptics_data { + /* Data read from the touchpad */ + unsigned long int model_id; /* Model-ID */ +@@ -107,6 +109,7 @@ + unsigned char pkt_type; /* packet type - old, new, etc */ + unsigned char mode; /* current mode byte */ + int scroll; ++ struct synaptics_led *led; + }; + + void synaptics_module_init(void); diff --git a/patches.drivers/ixgbe-entropy-source.patch b/patches.drivers/ixgbe-entropy-source.patch new file mode 100644 index 0000000..015d60b --- /dev/null +++ b/patches.drivers/ixgbe-entropy-source.patch @@ -0,0 +1,90 @@ +From: Jiri Benc +Subject: Enable ixgbe as entropy source (disabled by default) +References: FATE#307517 +Patch-mainline: never + +Current disk-less systems have no entropy source whatsoever. Therefore, the +network drivers tg3, bnx2, e1000, e1000e, igb and ixgbe should be enabled to +feed entropy to the kernel via the IRQF_SAMPLE_RANDOM flag when loaded. This +option shall not be enabled by default but implemented via a module option to +be activated by the administrator. + +Signed-off-by: Brandon Philips + +--- + drivers/net/ixgbe/ixgbe_main.c | 25 +++++++++++++++++++++---- + 1 file changed, 21 insertions(+), 4 deletions(-) + +--- a/drivers/net/ixgbe/ixgbe_main.c ++++ b/drivers/net/ixgbe/ixgbe_main.c +@@ -55,6 +55,11 @@ static const char ixgbe_driver_string[] + const char ixgbe_driver_version[] = DRV_VERSION; + static char ixgbe_copyright[] = "Copyright (c) 1999-2010 Intel Corporation."; + ++static int entropy = 0; ++module_param(entropy, int, 0); ++MODULE_PARM_DESC(entropy, "Allow ixgbe to populate the /dev/random entropy pool"); ++ ++ + static const struct ixgbe_info *ixgbe_info_tbl[] = { + [board_82598] = &ixgbe_82598_info, + [board_82599] = &ixgbe_82599_info, +@@ -1717,6 +1722,7 @@ static int ixgbe_request_msix_irqs(struc + irqreturn_t (*handler)(int, void *); + int i, vector, q_vectors, err; + int ri=0, ti=0; ++ int irq_flags; + + /* Decrement for Other and TCP Timer vectors */ + q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS; +@@ -1732,20 +1738,26 @@ static int ixgbe_request_msix_irqs(struc + for (vector = 0; vector < q_vectors; vector++) { + handler = SET_HANDLER(adapter->q_vector[vector]); + ++ irq_flags = 0; + if(handler == &ixgbe_msix_clean_rx) { + sprintf(adapter->name[vector], "%s-%s-%d", + netdev->name, "rx", ri++); ++ if (entropy) ++ irq_flags = IRQF_SAMPLE_RANDOM; + } + else if(handler == &ixgbe_msix_clean_tx) { + sprintf(adapter->name[vector], "%s-%s-%d", + netdev->name, "tx", ti++); + } +- else ++ else { + sprintf(adapter->name[vector], "%s-%s-%d", + netdev->name, "TxRx", vector); ++ if (entropy) ++ irq_flags = IRQF_SAMPLE_RANDOM; ++ } + + err = request_irq(adapter->msix_entries[vector].vector, +- handler, 0, adapter->name[vector], ++ handler, irq_flags, adapter->name[vector], + adapter->q_vector[vector]); + if (err) { + DPRINTK(PROBE, ERR, +@@ -1931,14 +1943,19 @@ static int ixgbe_request_irq(struct ixgb + { + struct net_device *netdev = adapter->netdev; + int err; ++ int irq_flags = 0; ++ ++ if (entropy) ++ irq_flags = IRQF_SAMPLE_RANDOM; + + if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) { + err = ixgbe_request_msix_irqs(adapter); + } else if (adapter->flags & IXGBE_FLAG_MSI_ENABLED) { +- err = request_irq(adapter->pdev->irq, ixgbe_intr, 0, ++ err = request_irq(adapter->pdev->irq, ixgbe_intr, irq_flags, + netdev->name, netdev); + } else { +- err = request_irq(adapter->pdev->irq, ixgbe_intr, IRQF_SHARED, ++ irq_flags |= IRQF_SHARED; ++ err = request_irq(adapter->pdev->irq, ixgbe_intr, irq_flags, + netdev->name, netdev); + } + diff --git a/patches.drivers/libata-add-waits-for-govault b/patches.drivers/libata-add-waits-for-govault new file mode 100644 index 0000000..13bd3e5 --- /dev/null +++ b/patches.drivers/libata-add-waits-for-govault @@ -0,0 +1,35 @@ +From: Tejun Heo +Date: Wed, 7 Feb 2007 12:37:41 -0800 +Subject: [PATCH] libata: add waits for GoVault +References: 246451 +Patch-mainline: not yet + +Iomega GoVault drives need specific waits here and there. Upstream +approach hasn't been determined yet. This is temp solution from Gary +Hade. Read the following thread for details. + +http://thread.gmane.org/gmane.linux.ide/14545/focus=14663 + +With recent changes in the reset sequence (ATA_TMOUT_FF_WAIT and +prefer-hardreset), the only thing which needs adjustment is +ATA_TMOUT_FF_WAIT (the prereset wait part is unnecessary as the wait +is necessary only for softreset when SCR registers are accessible and +in those cases libata now always uses hardreset which doesn't require +such wait). + +Signed-off-by: Tejun Heo +--- + include/linux/libata.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/libata.h ++++ b/include/linux/libata.h +@@ -260,7 +260,7 @@ enum { + * HHD424020F7SV00. Increase to 2secs when parallel probing + * is in place. + */ +- ATA_TMOUT_FF_WAIT = 800, ++ ATA_TMOUT_FF_WAIT = 2000, + + /* Spec mandates to wait for ">= 2ms" before checking status + * after reset. We wait 150ms, because that was the magic diff --git a/patches.drivers/libata-unlock-hpa-by-default b/patches.drivers/libata-unlock-hpa-by-default new file mode 100644 index 0000000..2cae4ed --- /dev/null +++ b/patches.drivers/libata-unlock-hpa-by-default @@ -0,0 +1,24 @@ +From: Tejun Heo +Subject: [PATCH] libata: unlock HPA by default +References: 299267 +Patch-mainline: not yet + +Unlock HPA by default. This is to stay compatible with the old IDE +drivers. + +Signed-off-by: Tejun Heo +--- + drivers/ata/libata-core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -139,7 +139,7 @@ int libata_fua = 0; + module_param_named(fua, libata_fua, int, 0444); + MODULE_PARM_DESC(fua, "FUA support (0=off [default], 1=on)"); + +-static int ata_ignore_hpa; ++static int ata_ignore_hpa = 1; + module_param_named(ignore_hpa, ata_ignore_hpa, int, 0644); + MODULE_PARM_DESC(ignore_hpa, "Ignore HPA limit (0=keep BIOS limits, 1=ignore limits, using full disk)"); + diff --git a/patches.drivers/megaraid-mbox-fix-SG_IO b/patches.drivers/megaraid-mbox-fix-SG_IO new file mode 100644 index 0000000..41058ac --- /dev/null +++ b/patches.drivers/megaraid-mbox-fix-SG_IO @@ -0,0 +1,70 @@ +From: Martin Wilck +Subject: megaraid_mbox: Oops on SG_IO +References: bnc#475619 +Patch-mainline: not yet + +This patch fixes an Oops in megaraid_mbox that happens when a +MODE_SENSE command for a logical drive is started viaioctl(SG_IO). + +The problem only occurs if the buffer specified by the user to receive +the mode data resides in highmem and if the buffer is aligned for +direct dma (no bounce buffer necessary). megaraid_mbox emulates +the MODE_SENSE command and writes the data using memset() directly +into user buffer. If the buffer is at a currently unmapped highmem +page, this leads to an Oops. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/megaraid/megaraid_mbox.c | 28 +++++++++++++++++++++++----- + 1 file changed, 23 insertions(+), 5 deletions(-) + +--- a/drivers/scsi/megaraid/megaraid_mbox.c ++++ b/drivers/scsi/megaraid/megaraid_mbox.c +@@ -1585,13 +1585,20 @@ megaraid_mbox_build_cmd(adapter_t *adapt + case MODE_SENSE: + { + struct scatterlist *sgl; +- caddr_t vaddr; ++ struct page *pg; ++ unsigned char *vaddr; ++ unsigned long flags; + + sgl = scsi_sglist(scp); +- if (sg_page(sgl)) { +- vaddr = (caddr_t) sg_virt(&sgl[0]); ++ pg = sg_page(sgl); ++ if (pg) { ++ local_irq_save(flags); ++ vaddr = kmap_atomic(pg, KM_BIO_SRC_IRQ) + sgl->offset; + + memset(vaddr, 0, scp->cmnd[4]); ++ ++ kunmap_atomic(vaddr, KM_BIO_SRC_IRQ); ++ local_irq_restore(flags); + } + else { + con_log(CL_ANN, (KERN_WARNING +@@ -2329,9 +2336,20 @@ megaraid_mbox_dpc(unsigned long devp) + if (scp->cmnd[0] == INQUIRY && status == 0 && islogical == 0 + && IS_RAID_CH(raid_dev, scb->dev_channel)) { + ++ struct page *pg; ++ unsigned char *vaddr; ++ unsigned long flags; ++ + sgl = scsi_sglist(scp); +- if (sg_page(sgl)) { +- c = *(unsigned char *) sg_virt(&sgl[0]); ++ pg = sg_page(sgl); ++ if (pg) { ++ local_irq_save(flags); ++ vaddr = kmap_atomic(pg, KM_BIO_SRC_IRQ) + sgl->offset; ++ ++ c = *vaddr; ++ ++ kunmap_atomic(vaddr, KM_BIO_SRC_IRQ); ++ local_irq_restore(flags); + } else { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: invalid sg:%d\n", diff --git a/patches.drivers/mpt-fusion-4.22.00.00-update b/patches.drivers/mpt-fusion-4.22.00.00-update new file mode 100644 index 0000000..dcd68b1 --- /dev/null +++ b/patches.drivers/mpt-fusion-4.22.00.00-update @@ -0,0 +1,18610 @@ +From: Hannes Reinecke +Date: Tue, 24 Nov 2009 14:40:54 +0100 +Subject: Update MPT Fusion driver to 4.22.00.00-suse +References: bnc#556587 +Patch-Mainline: No + +This patch updates the MPT Fusion driver to version 4.22.00.00-suse. + +Signed-off-by: Hannes Reinecke + +--- + drivers/message/fusion/Kconfig | 16 + drivers/message/fusion/Makefile | 13 + drivers/message/fusion/csmi/csmisas.c | 5805 +++++++++++++++++++ + drivers/message/fusion/csmi/csmisas.h | 1854 ++++++ + drivers/message/fusion/lsi/mpi.h | 5 + drivers/message/fusion/lsi/mpi_cnfg.h | 21 + drivers/message/fusion/lsi/mpi_history.txt | 55 + drivers/message/fusion/lsi/mpi_log_sas.h | 14 + drivers/message/fusion/lsi/mpi_type.h | 15 + drivers/message/fusion/mptbase.c | 802 +- + drivers/message/fusion/mptbase.h | 250 + drivers/message/fusion/mptctl.c | 648 +- + drivers/message/fusion/mptctl.h | 5 + drivers/message/fusion/mptdebug.h | 11 + drivers/message/fusion/mptfc.c | 173 + drivers/message/fusion/mptlan.c | 222 + drivers/message/fusion/mptlan.h | 2 + drivers/message/fusion/mptsas.c | 1359 +++- + drivers/message/fusion/mptsas.h | 60 + drivers/message/fusion/mptscsih.c | 1043 ++- + drivers/message/fusion/mptscsih.h | 13 + drivers/message/fusion/mptspi.c | 262 + drivers/message/fusion/rejected_ioctls/diag_buffer.c | 671 ++ + drivers/message/fusion/rejected_ioctls/diag_buffer.h | 101 + 24 files changed, 12081 insertions(+), 1339 deletions(-) + +--- a/drivers/message/fusion/Kconfig ++++ b/drivers/message/fusion/Kconfig +@@ -61,13 +61,25 @@ config FUSION_SAS + LSISAS1078 + + config FUSION_MAX_SGE +- int "Maximum number of scatter gather entries (16 - 128)" ++ int "Maximum number of scatter gather entries for SAS and SPI (16 - 128)" + default "128" + range 16 128 + help + This option allows you to specify the maximum number of scatter- + gather entries per I/O. The driver default is 128, which matches +- SCSI_MAX_PHYS_SEGMENTS. However, it may decreased down to 16. ++ SAFE_PHYS_SEGMENTS. However, it may decreased down to 16. ++ Decreasing this parameter will reduce memory requirements ++ on a per controller instance. ++ ++config FUSION_MAX_FC_SGE ++ int "Maximum number of scatter gather entries for FC (16 - 256)" ++ depends on FUSION_FC ++ default "256" ++ range 16 256 ++ help ++ This option allows you to specify the maximum number of scatter- ++ gather entries per I/O. The driver default is 256, which matches ++ MAX_PHYS_SEGMENTS. However, it may decreased down to 16. + Decreasing this parameter will reduce memory requirements + on a per controller instance. + +--- a/drivers/message/fusion/Makefile ++++ b/drivers/message/fusion/Makefile +@@ -1,12 +1,17 @@ +-# Fusion MPT drivers; recognized debug defines... ++# ++# LSI mpt fusion ++# ++ ++# csmi ioctls enable ++EXTRA_CFLAGS += -DCPQ_CIM ++EXTRA_CFLAGS += -DDIAG_BUFFER_SUPPORT ++ ++EXTRA_CFLAGS += -DCONFIG_FUSION_LOGGING + + # enable verbose logging + # CONFIG_FUSION_LOGGING needs to be enabled in Kconfig + #EXTRA_CFLAGS += -DMPT_DEBUG_VERBOSE + +- +-#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-} LSI_LOGIC +- + obj-$(CONFIG_FUSION_SPI) += mptbase.o mptscsih.o mptspi.o + obj-$(CONFIG_FUSION_FC) += mptbase.o mptscsih.o mptfc.o + obj-$(CONFIG_FUSION_SAS) += mptbase.o mptscsih.o mptsas.o +--- /dev/null ++++ b/drivers/message/fusion/csmi/csmisas.c +@@ -0,0 +1,5805 @@ ++/* ++ * linux/drivers/message/fusion/csmi/csmisas.c ++ * For use with LSI PCI chip/adapter(s) ++ * running LSI Fusion MPT (Message Passing Technology) firmware. ++ * ++ * Copyright (c) 1999-2008 LSI Corporation ++ * (mailto:DL-MPTFusionLinux@lsi.com) ++ */ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++/* ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; version 2 of the License. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ NO WARRANTY ++ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ++ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT ++ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, ++ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is ++ solely responsible for determining the appropriateness of using and ++ distributing the Program and assumes all risks associated with its ++ exercise of rights under this Agreement, including but not limited to ++ the risks and costs of program errors, damage to or loss of data, ++ programs or equipment, and unavailability or interruption of operations. ++ ++ DISCLAIMER OF LIABILITY ++ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY ++ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ++ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ++ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED ++ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++*/ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++ ++#define MPT_CSMI_DESCRIPTION "LSI Corporation: Fusion MPT Driver "MPT_LINUX_VERSION_COMMON ++#define csmisas_is_this_sas_cntr(ioc) (ioc->bus_type == SAS) ? 1 : 0 ++ ++static int csmisas_do_raid(MPT_ADAPTER *ioc, u8 action, u8 PhysDiskNum, u8 VolumeBus, ++ u8 VolumeId, pMpiRaidActionReply_t reply); ++static u8 map_sas_status_to_csmi(u8 mpi_sas_status); ++ ++/** ++ * reverse_byte_order64 ++ * ++ * @data64 ++ * ++ **/ ++static u64 ++reverse_byte_order64(u64 data64) ++{ ++ int i; ++ u64 rc; ++ u8 *inWord = (u8*)&data64, *outWord = (u8*)&rc; ++ ++ for (i = 0 ; i < 8 ; i++) ++ outWord[i] = inWord[7-i]; ++ ++ return rc; ++} ++ ++/** ++ * csmisas_is_sata ++ * ++ * @phys_disk ++ * ++ **/ ++static int ++csmisas_is_sata(RaidPhysDiskPage0_t *phys_disk) ++{ ++ if ((phys_disk->ExtDiskIdentifier[0] == 'A') && ++ (phys_disk->ExtDiskIdentifier[1] == 'T') && ++ (phys_disk->ExtDiskIdentifier[2] == 'A')) ++ return 1; ++ else ++ return 0; ++} ++ ++/** ++ * csmisas_is_end_device ++ * ++ * @attached ++ * ++ **/ ++static inline int ++csmisas_is_end_device(struct mptsas_devinfo * attached) ++{ ++ if ((attached->sas_address) && ++ (attached->device_info & ++ MPI_SAS_DEVICE_INFO_END_DEVICE) && ++ ((attached->device_info & ++ MPI_SAS_DEVICE_INFO_SSP_TARGET) | ++ (attached->device_info & ++ MPI_SAS_DEVICE_INFO_STP_TARGET) | ++ (attached->device_info & ++ MPI_SAS_DEVICE_INFO_SATA_DEVICE))) ++ return 1; ++ else ++ return 0; ++} ++ ++/** ++ * csmisas_is_phys_disk ++ * ++ * returns (1) success (0) fail - not a phys disk ++ **/ ++static int ++csmisas_is_phys_disk(MPT_ADAPTER *ioc, int channel, int id) ++{ ++ struct inactive_raid_component_info *component_info; ++ int i; ++ int rc = 0; ++ ++ if (!ioc->raid_data.pIocPg3) ++ goto out; ++ for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) { ++ if ((id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID) && ++ (channel == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskBus)) { ++ rc = 1; ++ goto out; ++ } ++ } ++ ++ /* ++ * Check inactive list for matching phys disks ++ */ ++ if (list_empty(&ioc->raid_data.inactive_list)) ++ goto out; ++ ++ down(&ioc->raid_data.inactive_list_mutex); ++ list_for_each_entry(component_info, &ioc->raid_data.inactive_list, ++ list) { ++ if ((component_info->d.PhysDiskID == id) && ++ (component_info->d.PhysDiskBus == channel)) ++ rc = 1; ++ } ++ up(&ioc->raid_data.inactive_list_mutex); ++ ++ out: ++ return rc; ++} ++ ++/** ++ * csmisas_raid_id_to_num ++ * ++ * Obtains the phys disk num for given H:C:T nexus ++ * ++ * input (channel/id) ++ * output (phys disk number - used by SCSI_IO_PASSTHRU to access hidden component) ++ * ++ * returns - signed return means failure ++ **/ ++static s8 ++csmisas_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id) ++{ ++ struct inactive_raid_component_info *component_info; ++ int i; ++ s8 rc = -ENXIO; ++ ++ if (!ioc->raid_data.pIocPg3) ++ goto out; ++ for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) { ++ if ((id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID) && ++ (channel == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskBus)) { ++ rc = ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum; ++ goto out; ++ } ++ } ++ ++ /* ++ * Check inactive list for matching phys disks ++ */ ++ if (list_empty(&ioc->raid_data.inactive_list)) ++ goto out; ++ ++ down(&ioc->raid_data.inactive_list_mutex); ++ list_for_each_entry(component_info, &ioc->raid_data.inactive_list, ++ list) { ++ if ((component_info->d.PhysDiskID == id) && ++ (component_info->d.PhysDiskBus == channel)) ++ rc = component_info->d.PhysDiskNum; ++ } ++ up(&ioc->raid_data.inactive_list_mutex); ++ ++ out: ++ return rc; ++} ++ ++/** ++ * csmisas_get_device_component_by_os ++ * ++ * Obtain device component object by operating system mapping ++ * ++ * @ioc ++ * @channel ++ * @id ++ * ++ **/ ++static struct sas_device_info * ++csmisas_get_device_component_by_os(MPT_ADAPTER *ioc, u8 channel, u8 id) ++{ ++ struct sas_device_info *sas_info, *p; ++ ++ sas_info = NULL; ++ ++ down(&ioc->sas_device_info_mutex); ++ list_for_each_entry(p, &ioc->sas_device_info_list, list) { ++ if (p->os.channel == channel && p->os.id == id) { ++ sas_info = p; ++ goto out; ++ } ++ } ++ ++ out: ++ up(&ioc->sas_device_info_mutex); ++ return sas_info; ++} ++ ++/** ++ * csmisas_get_device_component ++ * ++ * Obtain device component object by firmware system mapping ++ * ++ * @ioc ++ * @channel ++ * @id ++ * ++ **/ ++static struct sas_device_info * ++csmisas_get_device_component_by_fw(MPT_ADAPTER *ioc, u8 channel, u8 id) ++{ ++ struct sas_device_info *sas_info, *p; ++ ++ sas_info = NULL; ++ ++ down(&ioc->sas_device_info_mutex); ++ list_for_each_entry(p, &ioc->sas_device_info_list, list) { ++ if (p->fw.channel == channel && p->fw.id == id) { ++ sas_info = p; ++ goto out; ++ } ++ } ++ ++ out: ++ up(&ioc->sas_device_info_mutex); ++ return sas_info; ++} ++ ++ ++/** ++ * csmisas_get_device_component_by_sas_addr ++ * ++ * Obtain device component object by sas address ++ * ++ * @ioc ++ * @channel ++ * @id ++ * ++ **/ ++static struct sas_device_info * ++csmisas_get_device_component_by_sas_addr(MPT_ADAPTER *ioc, u64 sas_address) ++{ ++ struct sas_device_info *sas_info, *p; ++ ++ sas_info = NULL; ++ ++ down(&ioc->sas_device_info_mutex); ++ list_for_each_entry(p, &ioc->sas_device_info_list, list) { ++ if (p->sas_address == sas_address) { ++ sas_info = p; ++ goto out; ++ } ++ } ++ ++ out: ++ up(&ioc->sas_device_info_mutex); ++ return sas_info; ++} ++ ++/** ++ * csmisas_send_command_wait ++ * ++ * Send mf to firmware ++ * ++ * @ioc ++ * @mf ++ * @timeout - timeout ++ * ++ * Return: 0 for success ++ * non-zero, failure ++ **/ ++static int ++csmisas_send_command_wait(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, unsigned long timeout) ++{ ++ int rc; ++ unsigned long timeleft; ++ ++ timeout = max_t(unsigned long, MPT_IOCTL_DEFAULT_TIMEOUT, timeout); ++ rc = 0; ++ timeleft = 0; ++ ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, ++ mf->u.hdr.MsgContext); ++ INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status) ++ mpt_put_msg_frame(mptctl_id, ioc, mf); ++ timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done, timeout*HZ); ++ if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ rc = -1; ++ printk("%s: failed\n", __FUNCTION__); ++ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) { ++ mpt_free_msg_frame(ioc, mf); ++ CLEAR_MGMT_STATUS(ioc->ioctl_cmds.status) ++ return rc; ++ } ++ if (!timeleft) ++ mptctl_timeout_expired(ioc, mf); ++ } ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, 0); ++ ++ return rc; ++} ++ ++/** ++ * csmisas_send_handshake_wait ++ * ++ * Handshake a mf to firmware ++ * ++ * @ioc ++ * @mf ++ * @mf_size ++ * @timeout - timeout ++ * ++ * Return: 0 for success ++ * non-zero, failure ++ **/ ++static int ++csmisas_send_handshake_wait(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, unsigned long timeout) ++{ ++ int rc; ++ unsigned long timeleft; ++ ++ timeout = max_t(unsigned long, MPT_IOCTL_DEFAULT_TIMEOUT, timeout); ++ rc = 0; ++ timeleft = 0; ++ ++ INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status) ++ mpt_put_msg_frame_hi_pri(mptctl_taskmgmt_id, ioc, mf); ++ timeleft = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done, timeout*HZ); ++ if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ rc = -1; ++ printk("%s: failed\n", __FUNCTION__); ++ mpt_clear_taskmgmt_in_progress_flag(ioc); ++ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) { ++ mpt_free_msg_frame(ioc, mf); ++ CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status) ++ return rc; ++ } ++ if (!timeleft) ++ mptctl_timeout_expired(ioc, mf); ++ } ++ return rc; ++} ++ ++/** ++ * csmisas_get_number_hotspares - returns num hot spares in this ioc ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * ++ * Return: number of hotspares ++ * ++ **/ ++static int ++csmisas_get_number_hotspares(MPT_ADAPTER *ioc) ++{ ++ ConfigPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ IOCPage5_t *buffer = NULL; ++ dma_addr_t dma_handle; ++ int data_sz; ++ int rc; ++ ++ memset(&hdr, 0, sizeof(ConfigPageHeader_t)); ++ memset(&cfg, 0, sizeof(CONFIGPARMS)); ++ ++ rc = 0; ++ data_sz = 0; ++ hdr.PageNumber = 5; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_IOC; ++ cfg.cfghdr.hdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if (mpt_config(ioc, &cfg) != 0) ++ goto get_ioc_pg5; ++ ++ if (hdr.PageLength == 0) ++ goto get_ioc_pg5; ++ ++ data_sz = hdr.PageLength * 4; ++ buffer = (IOCPage5_t *) pci_alloc_consistent(ioc->pcidev, ++ data_sz, &dma_handle); ++ if (!buffer) ++ goto get_ioc_pg5; ++ ++ memset((u8 *)buffer, 0, data_sz); ++ cfg.physAddr = dma_handle; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if (mpt_config(ioc, &cfg) != 0) ++ goto get_ioc_pg5; ++ ++ rc = buffer->NumHotSpares; ++ ++ get_ioc_pg5: ++ ++ if (buffer) ++ pci_free_consistent(ioc->pcidev, data_sz, ++ (u8 *) buffer, dma_handle); ++ ++ return rc; ++} ++ ++ ++/** ++ * csmisas_get_ioc_pg5 - ioc Page 5 hot spares ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @pIocPage5: ioc page 5 ++ * @data_size: expected data size(units=bytes) ++ * ++ * Return: 0 for success ++ * -ENOMEM if no memory available ++ * -EPERM if not allowed due to ISR context ++ * -EAGAIN if no msg frames currently available ++ * -EFAULT for non-successful reply or no reply (timeout) ++ **/ ++static int ++csmisas_get_ioc_pg5(MPT_ADAPTER *ioc, IOCPage5_t *iocPage5, int data_size) ++{ ++ ConfigPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ IOCPage5_t *buffer = NULL; ++ dma_addr_t dma_handle; ++ int data_sz; ++ int rc; ++ ++ memset(&hdr, 0, sizeof(ConfigPageHeader_t)); ++ memset(&cfg, 0, sizeof(CONFIGPARMS)); ++ ++ rc = 0; ++ data_sz = 0; ++ hdr.PageNumber = 5; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_IOC; ++ cfg.cfghdr.hdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) ++ goto get_ioc_pg5; ++ ++ if (hdr.PageLength == 0) { ++ rc = -EFAULT; ++ goto get_ioc_pg5; ++ } ++ ++ data_sz = hdr.PageLength * 4; ++ buffer = (IOCPage5_t *) pci_alloc_consistent(ioc->pcidev, ++ data_sz, &dma_handle); ++ if (!buffer) { ++ rc = -ENOMEM; ++ goto get_ioc_pg5; ++ } ++ ++ memset((u8 *)buffer, 0, data_sz); ++ cfg.physAddr = dma_handle; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) ++ goto get_ioc_pg5; ++ ++ memcpy(iocPage5, buffer, data_size); ++ ++ get_ioc_pg5: ++ ++ if (buffer) ++ pci_free_consistent(ioc->pcidev, data_sz, ++ (u8 *) buffer, dma_handle); ++ ++ return rc; ++} ++ ++/** ++ * csmisas_sas_device_pg0 - sas device page 0 ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @mptsas_devinfo: structure found in mptsas.h ++ * @form, @form_specific - defines the Page Address field in the config page ++ * (pls refer to chapter 5.1 in the mpi spec) ++ * ++ * Return: 0 for success ++ * -ENOMEM if no memory available ++ * -EPERM if not allowed due to ISR context ++ * -EAGAIN if no msg frames currently available ++ * -EFAULT for non-successful reply or no reply (timeout) ++ **/ ++static int ++csmisas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, ++ u32 form, u32 form_specific) ++{ ++ ConfigExtendedPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ SasDevicePage0_t *buffer; ++ dma_addr_t dma_handle; ++ u64 sas_address; ++ int rc; ++ ++ rc = 0; ++ hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 0; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.pageAddr = form + form_specific; ++ cfg.physAddr = -1; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = 10; ++ ++ memset(device_info, 0, sizeof(struct mptsas_devinfo)); ++ if ((rc = mpt_config(ioc, &cfg)) != 0) ++ goto out; ++ ++ if (!hdr.ExtPageLength) { ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ buffer = pci_alloc_consistent(ioc->pcidev, ++ hdr.ExtPageLength * 4, &dma_handle); ++ if (!buffer) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ cfg.physAddr = dma_handle; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) ++ goto out_free_consistent; ++ ++ device_info->handle = le16_to_cpu(buffer->DevHandle); ++ device_info->handle_parent = le16_to_cpu(buffer->ParentDevHandle); ++ device_info->handle_enclosure = ++ le16_to_cpu(buffer->EnclosureHandle); ++ device_info->slot = le16_to_cpu(buffer->Slot); ++ device_info->phy_id = buffer->PhyNum; ++ device_info->port_id = buffer->PhysicalPort; ++ device_info->id = buffer->TargetID; ++ device_info->channel = buffer->Bus; ++ memcpy(&sas_address, &buffer->SASAddress, sizeof(u64)); ++ device_info->sas_address = le64_to_cpu(sas_address); ++ device_info->device_info = ++ le32_to_cpu(buffer->DeviceInfo); ++ ++ out_free_consistent: ++ pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, ++ buffer, dma_handle); ++ out: ++ return rc; ++} ++ ++/** ++ * Routine for the CSMI Sas Get Driver Info command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_driver_info(unsigned long arg) ++{ ++ ++ CSMI_SAS_DRIVER_INFO_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_DRIVER_INFO_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ ++ if (copy_from_user(&karg, uarg, sizeof(CSMI_SAS_DRIVER_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in csmi_sas_get_driver_info_buffer struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* Fill in the data and return the structure to the calling ++ * program ++ */ ++ memcpy( karg.Information.szName, MPT_MISCDEV_BASENAME, ++ sizeof(MPT_MISCDEV_BASENAME)); ++ memcpy( karg.Information.szDescription, MPT_CSMI_DESCRIPTION, ++ sizeof(MPT_CSMI_DESCRIPTION)); ++ ++ karg.Information.usMajorRevision = MPT_LINUX_MAJOR_VERSION; ++ karg.Information.usMinorRevision = MPT_LINUX_MINOR_VERSION; ++ karg.Information.usBuildRevision = MPT_LINUX_BUILD_VERSION; ++ karg.Information.usReleaseRevision = MPT_LINUX_RELEASE_VERSION; ++ ++ karg.Information.usCSMIMajorRevision = CSMI_MAJOR_REVISION; ++ karg.Information.usCSMIMinorRevision = CSMI_MINOR_REVISION; ++ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_DRIVER_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to write out csmi_sas_get_driver_info_buffer @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI_SAS_GET_CNTLR_CONFIG command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_cntlr_config(unsigned long arg) ++{ ++ ++ CSMI_SAS_CNTLR_CONFIG_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_CNTLR_CONFIG_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ u64 mem_phys; ++ ++ if (copy_from_user(&karg, uarg, sizeof(CSMI_SAS_CNTLR_CONFIG_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in csmi_sas_get_cntlr_config_buffer struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_INVALID_PARAMETER; ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* Clear the struct before filling in data. */ ++ memset( &karg.Configuration, 0, sizeof(CSMI_SAS_CNTLR_CONFIG)); ++ ++ /* Fill in the data and return the structure to the calling ++ * program ++ */ ++ ++ karg.Configuration.uBaseIoAddress = ioc->pio_mem_phys; ++ karg.Configuration.BaseMemoryAddress.uLowPart = ioc->mem_phys; ++ if (sizeof(ioc->mem_phys) == sizeof(u64)) { ++ mem_phys = ioc->mem_phys; ++ karg.Configuration.BaseMemoryAddress.uHighPart = ++ (u32)(mem_phys >> 32); ++ } ++ ++ karg.Configuration.uBoardID = (ioc->pcidev->subsystem_device << 16) | ++ (ioc->pcidev->subsystem_vendor); ++ ++ karg.Configuration.usSlotNumber = ++ (ioc->pci_slot_number = 0xff) ? ++ SLOT_NUMBER_UNKNOWN : ioc->pci_slot_number; ++ karg.Configuration.bControllerClass = CSMI_SAS_CNTLR_CLASS_HBA; ++ karg.Configuration.bIoBusType = CSMI_SAS_BUS_TYPE_PCI; ++ karg.Configuration.BusAddress.PciAddress.bBusNumber = ++ ioc->pcidev->bus->number; ++ karg.Configuration.BusAddress.PciAddress.bDeviceNumber = ++ PCI_SLOT(ioc->pcidev->devfn); ++ karg.Configuration.BusAddress.PciAddress.bFunctionNumber = ++ PCI_FUNC(ioc->pcidev->devfn); ++ karg.Configuration.BusAddress.PciAddress.bReserved = 0; ++ memcpy( &karg.Configuration.szSerialNumber, ioc->board_tracer, 16 ); ++ karg.Configuration.usMajorRevision = ioc->facts.FWVersion.Struct.Major; ++ karg.Configuration.usMinorRevision = ioc->facts.FWVersion.Struct.Minor; ++ karg.Configuration.usBuildRevision = ioc->facts.FWVersion.Struct.Unit; ++ karg.Configuration.usReleaseRevision = ioc->facts.FWVersion.Struct.Dev; ++ karg.Configuration.usBIOSMajorRevision = ++ (ioc->biosVersion & 0xFF000000) >> 24; ++ karg.Configuration.usBIOSMinorRevision = ++ (ioc->biosVersion & 0x00FF0000) >> 16; ++ karg.Configuration.usBIOSBuildRevision = ++ (ioc->biosVersion & 0x0000FF00) >> 8; ++ karg.Configuration.usBIOSReleaseRevision = ++ (ioc->biosVersion & 0x000000FF); ++ karg.Configuration.uControllerFlags = CSMI_SAS_CNTLR_SAS_HBA | ++ CSMI_SAS_CNTLR_FWD_SUPPORT | CSMI_SAS_CNTLR_FWD_ONLINE | ++ CSMI_SAS_CNTLR_FWD_SRESET ; ++ ++ /* ++ * Enabling CSMI_SAS_CNTLR_SAS_RAID bit when IR fw detected ++ */ ++ if (ioc->ir_firmware) ++ karg.Configuration.uControllerFlags |= CSMI_SAS_CNTLR_SAS_RAID; ++ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* All Rrom entries will be zero. Skip them. */ ++ /* bReserved will also be zeros. */ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_DRIVER_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to write out csmi_sas_get_driver_info_buffer @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI Sas Get Controller Status command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_cntlr_status(unsigned long arg) ++{ ++ ++ CSMI_SAS_CNTLR_STATUS_BUFFER __user *uarg = (void __user *) arg; ++ MPT_ADAPTER *ioc = NULL; ++ CSMI_SAS_CNTLR_STATUS_BUFFER karg; ++ int iocnum; ++ int rc; ++ ++ if (copy_from_user(&karg, uarg, sizeof(CSMI_SAS_CNTLR_STATUS_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in csmi_sas_get_cntlr_status_buffer struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* Fill in the data and return the structure to the calling ++ * program ++ */ ++ ++ rc = mpt_GetIocState(ioc, 1); ++ switch (rc) { ++ case MPI_IOC_STATE_OPERATIONAL: ++ karg.Status.uStatus = CSMI_SAS_CNTLR_STATUS_GOOD; ++ karg.Status.uOfflineReason = 0; ++ break; ++ ++ case MPI_IOC_STATE_FAULT: ++ karg.Status.uStatus = CSMI_SAS_CNTLR_STATUS_FAILED; ++ karg.Status.uOfflineReason = 0; ++ break; ++ ++ case MPI_IOC_STATE_RESET: ++ case MPI_IOC_STATE_READY: ++ default: ++ karg.Status.uStatus = CSMI_SAS_CNTLR_STATUS_OFFLINE; ++ karg.Status.uOfflineReason = ++ CSMI_SAS_OFFLINE_REASON_INITIALIZING; ++ break; ++ } ++ ++ memset(&karg.Status.bReserved, 0, 28); ++ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_CNTLR_STATUS_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to write out csmi_sas_get_cntlr_status @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI Sas Get Phy Info command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_phy_info(unsigned long arg) ++{ ++ CSMI_SAS_PHY_INFO_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_PHY_INFO_BUFFER *karg; ++ MPT_ADAPTER *ioc = NULL; ++ ConfigExtendedPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ SasIOUnitPage0_t *sasIoUnitPg0; ++ dma_addr_t sasIoUnitPg0_dma; ++ int sasIoUnitPg0_data_sz; ++ SasPhyPage0_t *sasPhyPg0; ++ dma_addr_t sasPhyPg0_dma; ++ int sasPhyPg0_data_sz; ++ u16 protocol; ++ int iocnum; ++ int rc; ++ int ii; ++ u64 sas_address; ++ struct mptsas_devinfo device_info; ++ int memory_pages; ++ ++ sasIoUnitPg0=NULL; ++ sasPhyPg0=NULL; ++ sasIoUnitPg0_data_sz=0; ++ sasPhyPg0_data_sz=0; ++ ++ memory_pages = get_order(sizeof(CSMI_SAS_PHY_INFO_BUFFER)); ++ karg = (CSMI_SAS_PHY_INFO_BUFFER *)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!karg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc CSMI_SAS_PHY_INFO_BUFFER " ++ "malloc_data_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ (int)sizeof(CSMI_SAS_PHY_INFO_BUFFER), memory_pages); ++ return -ENOMEM; ++ } ++ ++ memset(karg, 0, sizeof(*karg)); ++ ++ if (copy_from_user(karg, uarg, sizeof(CSMI_SAS_PHY_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in csmisas_get_phy_info_buffer struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg->IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* Fill in the data and return the structure to the calling ++ * program ++ */ ++ ++ /* Issue a config request to get the number of phys ++ */ ++ hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 0; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.pageAddr = 0; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASIOUNITPAGE0_PAGEVERSION: HEADER\n")); ++ dcsmisasprintk(ioc, printk(": rc=%x\n",rc)); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto sas_get_phy_info_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ": hdr.ExtPageLength == 0\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto sas_get_phy_info_exit; ++ } ++ ++ sasIoUnitPg0_data_sz = hdr.ExtPageLength * 4; ++ rc = -ENOMEM; ++ ++ sasIoUnitPg0 = (SasIOUnitPage0_t *) pci_alloc_consistent(ioc->pcidev, ++ sasIoUnitPg0_data_sz, &sasIoUnitPg0_dma); ++ ++ if (!sasIoUnitPg0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto sas_get_phy_info_exit; ++ } ++ ++ memset((u8 *)sasIoUnitPg0, 0, sasIoUnitPg0_data_sz); ++ cfg.physAddr = sasIoUnitPg0_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASIOUNITPAGE0_PAGEVERSION: PAGE\n")); ++ dcsmisasprintk(ioc, printk(KERN_ERR ": rc=%x\n",rc)); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto sas_get_phy_info_exit; ++ } ++ ++ /* Number of Phys. */ ++ karg->Information.bNumberOfPhys = sasIoUnitPg0->NumPhys; ++ ++ /* Fill in information for each phy. */ ++ for (ii = 0; ii < karg->Information.bNumberOfPhys; ii++) { ++ ++/* EDM : dump IO Unit Page 0 data*/ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "---- IO UNIT PAGE 0 ------------\n")); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Handle=0x%X\n", ++ le16_to_cpu(sasIoUnitPg0->PhyData[ii].AttachedDeviceHandle))); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Controller Handle=0x%X\n", ++ le16_to_cpu(sasIoUnitPg0->PhyData[ii].ControllerDevHandle))); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Port=0x%X\n", ++ sasIoUnitPg0->PhyData[ii].Port)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Port Flags=0x%X\n", ++ sasIoUnitPg0->PhyData[ii].PortFlags)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "PHY Flags=0x%X\n", ++ sasIoUnitPg0->PhyData[ii].PhyFlags)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Negotiated Link Rate=0x%X\n", ++ sasIoUnitPg0->PhyData[ii].NegotiatedLinkRate)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Controller PHY Device Info=0x%X\n", ++ le32_to_cpu(sasIoUnitPg0->PhyData[ii].ControllerPhyDeviceInfo))); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "DiscoveryStatus=0x%X\n", ++ le32_to_cpu(sasIoUnitPg0->PhyData[ii].DiscoveryStatus))); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\n")); ++/* EDM : debug data */ ++ ++ /* PHY stuff. */ ++ karg->Information.Phy[ii].bPortIdentifier = ++ sasIoUnitPg0->PhyData[ii].Port; ++ ++ /* Get the negotiated link rate for the phy. */ ++ switch (sasIoUnitPg0->PhyData[ii].NegotiatedLinkRate) { ++ ++ case MPI_SAS_IOUNIT0_RATE_PHY_DISABLED: ++ karg->Information.Phy[ii].bNegotiatedLinkRate = ++ CSMI_SAS_PHY_DISABLED; ++ break; ++ ++ case MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION: ++ karg->Information.Phy[ii].bNegotiatedLinkRate = ++ CSMI_SAS_LINK_RATE_FAILED; ++ break; ++ ++ case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE: ++ break; ++ ++ case MPI_SAS_IOUNIT0_RATE_1_5: ++ karg->Information.Phy[ii].bNegotiatedLinkRate = ++ CSMI_SAS_LINK_RATE_1_5_GBPS; ++ break; ++ ++ case MPI_SAS_IOUNIT0_RATE_3_0: ++ karg->Information.Phy[ii].bNegotiatedLinkRate = ++ CSMI_SAS_LINK_RATE_3_0_GBPS; ++ break; ++ ++ case MPI_SAS_IOUNIT0_RATE_UNKNOWN: ++ default: ++ karg->Information.Phy[ii].bNegotiatedLinkRate = ++ CSMI_SAS_LINK_RATE_UNKNOWN; ++ break; ++ } ++ ++ if (sasIoUnitPg0->PhyData[ii].PortFlags & ++ MPI_SAS_IOUNIT0_PORT_FLAGS_DISCOVERY_IN_PROGRESS) { ++ karg->Information.Phy[ii].bAutoDiscover = ++ CSMI_SAS_DISCOVER_IN_PROGRESS; ++ } else { ++ karg->Information.Phy[ii].bAutoDiscover = ++ CSMI_SAS_DISCOVER_COMPLETE; ++ } ++ ++ /* Issue a config request to get ++ * phy information. ++ */ ++ hdr.PageVersion = MPI_SASPHY0_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 0; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.pageAddr = ii; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASPHY0_PAGEVERSION: HEADER\n")); ++ dcsmisasprintk(ioc, printk(": rc=%x\n",rc)); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto sas_get_phy_info_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto sas_get_phy_info_exit; ++ } ++ ++ sasPhyPg0_data_sz = hdr.ExtPageLength * 4; ++ rc = -ENOMEM; ++ ++ sasPhyPg0 = (SasPhyPage0_t *) pci_alloc_consistent( ++ ioc->pcidev, sasPhyPg0_data_sz, &sasPhyPg0_dma); ++ ++ if (! sasPhyPg0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto sas_get_phy_info_exit; ++ } ++ ++ memset((u8 *)sasPhyPg0, 0, sasPhyPg0_data_sz); ++ cfg.physAddr = sasPhyPg0_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASPHY0_PAGEVERSION: PAGE\n")); ++ dcsmisasprintk(ioc, printk(KERN_ERR ": rc=%x\n",rc)); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ pci_free_consistent(ioc->pcidev, sasPhyPg0_data_sz, ++ (u8 *) sasPhyPg0, sasPhyPg0_dma); ++ goto sas_get_phy_info_exit; ++ } ++ ++/* EDM : dump PHY Page 0 data*/ ++ memcpy(&sas_address, &sasPhyPg0->SASAddress, sizeof(u64)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "---- SAS PHY PAGE 0 ------------\n")); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Handle=0x%X\n", ++ le16_to_cpu(sasPhyPg0->AttachedDevHandle))); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "SAS Address=0x%llX\n", ++ (unsigned long long)sas_address)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Attached PHY Identifier=0x%X\n", ++ sasPhyPg0->AttachedPhyIdentifier)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Attached Device Info=0x%X\n", ++ le32_to_cpu(sasPhyPg0->AttachedDeviceInfo))); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Programmed Link Rate=0x%X\n", ++ sasPhyPg0->ProgrammedLinkRate)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Hardware Link Rate=0x%X\n", ++ sasPhyPg0->HwLinkRate)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Change Count=0x%X\n", ++ sasPhyPg0->ChangeCount)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "PHY Info=0x%X\n", ++ le32_to_cpu(sasPhyPg0->PhyInfo))); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\n")); ++/* EDM : debug data */ ++ ++ /* save the data */ ++ ++ /* Set Max hardware link rate. ++ * This value is hard coded ++ * because the HW link rate ++ * is currently being ++ * overwritten in FW. ++ */ ++ ++ /* Set Max hardware link rate. */ ++ switch (sasPhyPg0->HwLinkRate & ++ MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { ++ ++ case MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5: ++ karg->Information.Phy[ii].bMaximumLinkRate = ++ CSMI_SAS_LINK_RATE_1_5_GBPS; ++ break; ++ ++ case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: ++ karg->Information.Phy[ii].bMaximumLinkRate = ++ CSMI_SAS_LINK_RATE_3_0_GBPS; ++ break; ++ default: ++ break; ++ } ++ ++ /* Set Max programmed link rate. */ ++ switch (sasPhyPg0->ProgrammedLinkRate & ++ MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { ++ ++ case MPI_SAS_PHY0_PRATE_MAX_RATE_1_5: ++ karg->Information.Phy[ii].bMaximumLinkRate |= ++ (CSMI_SAS_PROGRAMMED_LINK_RATE_1_5_GBPS << 4); ++ break; ++ ++ case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: ++ karg->Information.Phy[ii].bMaximumLinkRate |= ++ (CSMI_SAS_PROGRAMMED_LINK_RATE_3_0_GBPS << 4); ++ break; ++ default: ++ break; ++ } ++ ++ /* Set Min hardware link rate. */ ++ switch (sasPhyPg0->HwLinkRate & ++ MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK) { ++ ++ case MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5: ++ karg->Information.Phy[ii].bMinimumLinkRate = ++ CSMI_SAS_LINK_RATE_1_5_GBPS; ++ break; ++ ++ case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: ++ karg->Information.Phy[ii].bMinimumLinkRate = ++ CSMI_SAS_LINK_RATE_3_0_GBPS; ++ break; ++ default: ++ break; ++ } ++ ++ /* Set Min programmed link rate. */ ++ switch (sasPhyPg0->ProgrammedLinkRate & ++ MPI_SAS_PHY0_PRATE_MIN_RATE_MASK) { ++ ++ case MPI_SAS_PHY0_PRATE_MIN_RATE_1_5: ++ karg->Information.Phy[ii].bMinimumLinkRate |= ++ (CSMI_SAS_PROGRAMMED_LINK_RATE_1_5_GBPS << 4); ++ break; ++ ++ case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: ++ karg->Information.Phy[ii].bMinimumLinkRate |= ++ (CSMI_SAS_PROGRAMMED_LINK_RATE_3_0_GBPS << 4); ++ break; ++ default: ++ break; ++ } ++ ++ karg->Information.Phy[ii].bPhyChangeCount = sasPhyPg0->ChangeCount; ++ if( sasPhyPg0->PhyInfo & MPI_SAS_PHY0_PHYINFO_VIRTUAL_PHY ) ++ karg->Information.Phy[ii].bPhyFeatures = CSMI_SAS_PHY_VIRTUAL_SMP; ++ ++ /* Fill in Attached Device ++ * Initiator Port Protocol. ++ * Bits 6:3 ++ * More than one bit can be set. ++ */ ++ protocol = le32_to_cpu(sasPhyPg0->AttachedDeviceInfo) & 0x78; ++ karg->Information.Phy[ii].Attached.bInitiatorPortProtocol = 0; ++ if (protocol & MPI_SAS_DEVICE_INFO_SSP_INITIATOR) ++ karg->Information.Phy[ii].Attached.bInitiatorPortProtocol = ++ CSMI_SAS_PROTOCOL_SSP; ++ if (protocol & MPI_SAS_DEVICE_INFO_STP_INITIATOR) ++ karg->Information.Phy[ii].Attached.bInitiatorPortProtocol |= ++ CSMI_SAS_PROTOCOL_STP; ++ if (protocol & MPI_SAS_DEVICE_INFO_SMP_INITIATOR) ++ karg->Information.Phy[ii].Attached.bInitiatorPortProtocol |= ++ CSMI_SAS_PROTOCOL_SMP; ++ if (protocol & MPI_SAS_DEVICE_INFO_SATA_HOST) ++ karg->Information.Phy[ii].Attached.bInitiatorPortProtocol |= ++ CSMI_SAS_PROTOCOL_SATA; ++ ++ /* Fill in Phy Target Port ++ * Protocol. Bits 10:7 ++ * More than one bit can be set. ++ */ ++ protocol = le32_to_cpu(sasPhyPg0->AttachedDeviceInfo) & 0x780; ++ karg->Information.Phy[ii].Attached.bTargetPortProtocol = 0; ++ if (protocol & MPI_SAS_DEVICE_INFO_SSP_TARGET) ++ karg->Information.Phy[ii].Attached.bTargetPortProtocol |= ++ CSMI_SAS_PROTOCOL_SSP; ++ if (protocol & MPI_SAS_DEVICE_INFO_STP_TARGET) ++ karg->Information.Phy[ii].Attached.bTargetPortProtocol |= ++ CSMI_SAS_PROTOCOL_STP; ++ if (protocol & MPI_SAS_DEVICE_INFO_SMP_TARGET) ++ karg->Information.Phy[ii].Attached.bTargetPortProtocol |= ++ CSMI_SAS_PROTOCOL_SMP; ++ if (protocol & MPI_SAS_DEVICE_INFO_SATA_DEVICE) ++ karg->Information.Phy[ii].Attached.bTargetPortProtocol |= ++ CSMI_SAS_PROTOCOL_SATA; ++ ++ ++ /* Fill in Attached device type */ ++ switch (le32_to_cpu(sasPhyPg0->AttachedDeviceInfo) & ++ MPI_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { ++ ++ case MPI_SAS_DEVICE_INFO_NO_DEVICE: ++ karg->Information.Phy[ii].Attached.bDeviceType = ++ CSMI_SAS_NO_DEVICE_ATTACHED; ++ break; ++ ++ case MPI_SAS_DEVICE_INFO_END_DEVICE: ++ karg->Information.Phy[ii].Attached.bDeviceType = ++ CSMI_SAS_END_DEVICE; ++ break; ++ ++ case MPI_SAS_DEVICE_INFO_EDGE_EXPANDER: ++ karg->Information.Phy[ii].Attached.bDeviceType = ++ CSMI_SAS_EDGE_EXPANDER_DEVICE; ++ break; ++ ++ case MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER: ++ karg->Information.Phy[ii].Attached.bDeviceType = ++ CSMI_SAS_FANOUT_EXPANDER_DEVICE; ++ break; ++ } ++ ++ /* Identify Info. */ ++ switch (le32_to_cpu(sasIoUnitPg0->PhyData[ii].ControllerPhyDeviceInfo) & ++ MPI_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { ++ ++ case MPI_SAS_DEVICE_INFO_NO_DEVICE: ++ karg->Information.Phy[ii].Identify.bDeviceType = ++ CSMI_SAS_NO_DEVICE_ATTACHED; ++ break; ++ ++ case MPI_SAS_DEVICE_INFO_END_DEVICE: ++ karg->Information.Phy[ii].Identify.bDeviceType = ++ CSMI_SAS_END_DEVICE; ++ break; ++ ++ case MPI_SAS_DEVICE_INFO_EDGE_EXPANDER: ++ karg->Information.Phy[ii].Identify.bDeviceType = ++ CSMI_SAS_EDGE_EXPANDER_DEVICE; ++ break; ++ ++ case MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER: ++ karg->Information.Phy[ii].Identify.bDeviceType = ++ CSMI_SAS_FANOUT_EXPANDER_DEVICE; ++ break; ++ } ++ ++ /* Fill in Phy Initiator Port Protocol. Bits 6:3 ++ * More than one bit can be set, fall through cases. ++ */ ++ protocol = le32_to_cpu( ++ sasIoUnitPg0->PhyData[ii].ControllerPhyDeviceInfo) & 0x78; ++ karg->Information.Phy[ii].Identify.bInitiatorPortProtocol = 0; ++ if( protocol & MPI_SAS_DEVICE_INFO_SSP_INITIATOR ) ++ karg->Information.Phy[ii].Identify.bInitiatorPortProtocol |= ++ CSMI_SAS_PROTOCOL_SSP; ++ if( protocol & MPI_SAS_DEVICE_INFO_STP_INITIATOR ) ++ karg->Information.Phy[ii].Identify.bInitiatorPortProtocol |= ++ CSMI_SAS_PROTOCOL_STP; ++ if( protocol & MPI_SAS_DEVICE_INFO_SMP_INITIATOR ) ++ karg->Information.Phy[ii].Identify.bInitiatorPortProtocol |= ++ CSMI_SAS_PROTOCOL_SMP; ++ if( protocol & MPI_SAS_DEVICE_INFO_SATA_HOST ) ++ karg->Information.Phy[ii].Identify.bInitiatorPortProtocol |= ++ CSMI_SAS_PROTOCOL_SATA; ++ ++ /* Fill in Phy Target Port Protocol. Bits 10:7 ++ * More than one bit can be set, fall through cases. ++ */ ++ protocol = le32_to_cpu( ++ sasIoUnitPg0->PhyData[ii].ControllerPhyDeviceInfo) & 0x780; ++ karg->Information.Phy[ii].Identify.bTargetPortProtocol = 0; ++ if( protocol & MPI_SAS_DEVICE_INFO_SSP_TARGET ) ++ karg->Information.Phy[ii].Identify.bTargetPortProtocol |= ++ CSMI_SAS_PROTOCOL_SSP; ++ if( protocol & MPI_SAS_DEVICE_INFO_STP_TARGET ) ++ karg->Information.Phy[ii].Identify.bTargetPortProtocol |= ++ CSMI_SAS_PROTOCOL_STP; ++ if( protocol & MPI_SAS_DEVICE_INFO_SMP_TARGET ) ++ karg->Information.Phy[ii].Identify.bTargetPortProtocol |= ++ CSMI_SAS_PROTOCOL_SMP; ++ if( protocol & MPI_SAS_DEVICE_INFO_SATA_DEVICE ) ++ karg->Information.Phy[ii].Identify.bTargetPortProtocol |= ++ CSMI_SAS_PROTOCOL_SATA; ++ ++ /* Setup SAS Address for the attached device */ ++ if (sasPhyPg0->AttachedDevHandle) { ++ sas_address = reverse_byte_order64(sas_address); ++ memcpy(karg->Information.Phy[ii].Attached.bSASAddress, ++ &sas_address, sizeof(u64)); ++ karg->Information.Phy[ii].Attached.bPhyIdentifier = ++ sasPhyPg0->AttachedPhyIdentifier; ++ } ++ ++ /* Setup SAS Address for the parent device */ ++ csmisas_sas_device_pg0(ioc, &device_info, ++ (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << ++ MPI_SAS_DEVICE_PGAD_FORM_SHIFT), ++ sasIoUnitPg0->PhyData[ii].ControllerDevHandle); ++ sas_address = reverse_byte_order64(device_info.sas_address); ++ memcpy(karg->Information.Phy[ii].Identify.bSASAddress, ++ &sas_address, sizeof(u64)); ++ karg->Information.Phy[ii].Identify.bPhyIdentifier = ii; ++ ++ pci_free_consistent(ioc->pcidev, sasPhyPg0_data_sz, ++ (u8 *) sasPhyPg0, sasPhyPg0_dma); ++ } ++ ++sas_get_phy_info_exit: ++ ++ if (sasIoUnitPg0) ++ pci_free_consistent(ioc->pcidev, sasIoUnitPg0_data_sz, ++ (u8 *) sasIoUnitPg0, sasIoUnitPg0_dma); ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, karg, ++ sizeof(CSMI_SAS_PHY_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to write out csmisas_get_phy_info_buffer @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ free_pages((unsigned long)karg, memory_pages); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Set PHY Info command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_set_phy_info(unsigned long arg) ++{ ++ CSMI_SAS_SET_PHY_INFO_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_SET_PHY_INFO_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ ++ if (copy_from_user(&karg, uarg, sizeof(CSMI_SAS_SET_PHY_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_set_phy_info struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++/* TODO - implement IOCTL here */ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_BAD_CNTL_CODE; ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": not implemented\n")); ++ ++// cim_set_phy_info_exit: ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_SET_PHY_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_set_phy_info @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++ ++} ++ ++/** ++ * Prototype Routine for the CSMI Sas Get SCSI Address command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_scsi_address(unsigned long arg) ++{ ++ CSMI_SAS_GET_SCSI_ADDRESS_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_GET_SCSI_ADDRESS_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ u64 sas_address; ++ struct sas_device_info *sas_info; ++ ++ if (copy_from_user(&karg, uarg, ++ sizeof(CSMI_SAS_GET_SCSI_ADDRESS_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_get_scsi_address struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* reverse byte order the sas address */ ++ memcpy(&sas_address, karg.bSASAddress, sizeof(u64)); ++ sas_address = reverse_byte_order64(sas_address); ++ ++ /* Search the list for the matching SAS address. */ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_NO_SCSI_ADDRESS; ++ karg.bPathId = 0; ++ karg.bTargetId = 0; ++ karg.bLun = 0; ++ ++ sas_info = csmisas_get_device_component_by_sas_addr(ioc, sas_address); ++ if (!sas_info || sas_info->is_cached || sas_info->is_logical_volume) ++ goto csmisas_get_scsi_address_exit; ++ ++ karg.bPathId = sas_info->os.channel; ++ karg.bTargetId = sas_info->os.id; ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ csmisas_get_scsi_address_exit: ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_GET_SCSI_ADDRESS_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_get_scsi_address @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI Sas Get SCSI Address command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_sata_signature(unsigned long arg) ++{ ++ CSMI_SAS_SATA_SIGNATURE_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_SATA_SIGNATURE_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ int rc, jj; ++ ConfigExtendedPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ SasPhyPage0_t *sasPhyPg0; ++ dma_addr_t sasPhyPg0_dma; ++ int sasPhyPg0_data_sz; ++ SasDevicePage1_t *sasDevicePg1; ++ dma_addr_t sasDevicePg1_dma; ++ int sasDevicePg1_data_sz; ++ u8 phyId; ++ u64 sas_address; ++ ++ sasPhyPg0=NULL; ++ sasPhyPg0_data_sz=0; ++ sasDevicePg1=NULL; ++ sasDevicePg1_data_sz=0; ++ ++ if (copy_from_user(&karg, uarg, ++ sizeof(CSMI_SAS_SATA_SIGNATURE_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_sata_signature struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ phyId = karg.Signature.bPhyIdentifier; ++ if (phyId >= ioc->num_ports) { ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_PHY_DOES_NOT_EXIST; ++ dcsmisasprintk(ioc, printk(KERN_WARNING ": phyId >= ioc->num_ports\n")); ++ goto cim_sata_signature_exit; ++ } ++ ++ /* Default to success.*/ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* Issue a config request to get the devHandle of the attached device ++ */ ++ ++ /* Issue a config request to get phy information. */ ++ hdr.PageVersion = MPI_SASPHY0_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 0; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.pageAddr = phyId; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASPHY0_PAGEVERSION: HEADER\n")); ++ dcsmisasprintk(ioc, printk(KERN_ERR ": rc=%x\n",rc)); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sata_signature_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ": hdr.ExtPageLength == 0\n")); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sata_signature_exit; ++ } ++ ++ ++ sasPhyPg0_data_sz = hdr.ExtPageLength * 4; ++ rc = -ENOMEM; ++ ++ sasPhyPg0 = (SasPhyPage0_t *) pci_alloc_consistent(ioc->pcidev, ++ sasPhyPg0_data_sz, &sasPhyPg0_dma); ++ ++ if (! sasPhyPg0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sata_signature_exit; ++ } ++ ++ memset((u8 *)sasPhyPg0, 0, sasPhyPg0_data_sz); ++ cfg.physAddr = sasPhyPg0_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASPHY0_PAGEVERSION: PAGE\n")); ++ dcsmisasprintk(ioc, printk(KERN_ERR ": rc=%x\n",rc)); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sata_signature_exit; ++ } ++ ++ /* Make sure a SATA device is attached. */ ++ if ((le32_to_cpu(sasPhyPg0->AttachedDeviceInfo) & ++ MPI_SAS_DEVICE_INFO_SATA_DEVICE) == 0) { ++ dcsmisasprintk(ioc, printk(KERN_WARNING ": NOT A SATA DEVICE\n")); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_NO_SATA_DEVICE; ++ goto cim_sata_signature_exit; ++ } ++ ++ /* Get device page 1 for FIS signature. */ ++ hdr.PageVersion = MPI_SASDEVICE1_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 1 /* page number 1 */; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ ++ cfg.pageAddr = ((MPI_SAS_DEVICE_PGAD_FORM_HANDLE << ++ MPI_SAS_DEVICE_PGAD_FORM_SHIFT) | ++ le16_to_cpu(sasPhyPg0->AttachedDevHandle)); ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASDEVICE1_PAGEVERSION: HEADER\n")); ++ dcsmisasprintk(ioc, printk(KERN_ERR ": rc=%x\n",rc)); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sata_signature_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": hdr.ExtPageLength == 0\n")); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sata_signature_exit; ++ } ++ ++ sasDevicePg1_data_sz = hdr.ExtPageLength * 4; ++ rc = -ENOMEM; ++ ++ sasDevicePg1 = (SasDevicePage1_t *) pci_alloc_consistent ++ (ioc->pcidev, sasDevicePg1_data_sz, &sasDevicePg1_dma); ++ ++ if (! sasDevicePg1) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sata_signature_exit; ++ } ++ ++ memset((u8 *)sasDevicePg1, 0, sasDevicePg1_data_sz); ++ cfg.physAddr = sasDevicePg1_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASDEVICE1_PAGEVERSION: PAGE\n")); ++ dcsmisasprintk(ioc, printk(KERN_ERR ": rc=%x\n",rc)); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sata_signature_exit; ++ } ++ ++/* EDM : dump Device Page 1 data*/ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "---- SAS DEVICE PAGE 1 ---------\n")); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Handle=0x%x\n",sasDevicePg1->DevHandle)); ++ memcpy(&sas_address, &sasDevicePg1->SASAddress, sizeof(u64)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "SAS Address=0x%llX\n", ++ (unsigned long long)sas_address)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\n")); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Target ID=0x%x\n",sasDevicePg1->TargetID)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Bus=0x%x\n",sasDevicePg1->Bus)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Initial Reg Device FIS=")); ++ for(jj=0;jj<20;jj++) ++ dcsmisasprintk(ioc, printk("%02x ", ++ ((u8 *)&sasDevicePg1->InitialRegDeviceFIS)[jj])); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\n\n")); ++/* EDM : debug data */ ++ ++ memcpy(karg.Signature.bSignatureFIS, ++ sasDevicePg1->InitialRegDeviceFIS,20); ++ ++ cim_sata_signature_exit: ++ ++ if (sasPhyPg0) ++ pci_free_consistent(ioc->pcidev, sasPhyPg0_data_sz, ++ (u8 *) sasPhyPg0, sasPhyPg0_dma); ++ ++ if (sasDevicePg1) ++ pci_free_consistent(ioc->pcidev, sasDevicePg1_data_sz, ++ (u8 *) sasDevicePg1, sasDevicePg1_dma); ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_SATA_SIGNATURE_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_sata_signature @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI Sas Get SCSI Address command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_device_address(unsigned long arg) ++{ ++ CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ struct sas_device_info *sas_info; ++ u64 sas_address; ++ ++ if (copy_from_user(&karg, uarg, ++ sizeof(CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_get_device_address_buffer struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_NO_DEVICE_ADDRESS; ++ memset(karg.bSASAddress, 0, sizeof(u64)); ++ memset(karg.bSASLun, 0, sizeof(karg.bSASLun)); ++ ++ /* Search the list for the matching SAS address. */ ++ sas_info = csmisas_get_device_component_by_os(ioc, karg.bPathId, ++ karg.bTargetId); ++ if (!sas_info || sas_info->is_cached || sas_info->is_logical_volume) ++ goto csmisas_get_device_address_exit; ++ ++ sas_address = reverse_byte_order64(sas_info->sas_address); ++ memcpy(karg.bSASAddress, &sas_address, sizeof(u64)); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ csmisas_get_device_address_exit: ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_get_device_address_buffer @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI Sas Get Link Errors command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_link_errors(unsigned long arg) ++{ ++ CSMI_SAS_LINK_ERRORS_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_LINK_ERRORS_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ MPT_FRAME_HDR *mf = NULL; ++ MPIHeader_t *mpi_hdr; ++ int iocnum; ++ int rc; ++ ConfigExtendedPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ SasPhyPage1_t *sasPhyPage1; ++ dma_addr_t sasPhyPage1_dma; ++ int sasPhyPage1_data_sz; ++ SasIoUnitControlRequest_t *sasIoUnitCntrReq; ++ SasIoUnitControlReply_t *sasIoUnitCntrReply; ++ u8 phyId; ++ u16 ioc_status; ++ u32 MsgContext; ++ ++ sasPhyPage1=NULL; ++ sasPhyPage1_data_sz=0; ++ ++ if (copy_from_user(&karg, uarg, ++ sizeof(CSMI_SAS_LINK_ERRORS_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmisas_get_link_errors struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ phyId = karg.Information.bPhyIdentifier; ++ if (phyId >= ioc->num_ports) { ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_PHY_DOES_NOT_EXIST; ++ dcsmisasprintk(ioc, printk(KERN_WARNING ": phyId >= ioc->num_ports\n")); ++ goto cim_get_link_errors_exit; ++ } ++ ++ /* Default to success.*/ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* Issue a config request to get the devHandle of the attached device ++ */ ++ ++ /* Issue a config request to get phy information. */ ++ hdr.PageVersion = MPI_SASPHY1_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 1 /* page number 1*/; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.pageAddr = phyId; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: MPI_SASPHY1_PAGEVERSION: HEADER\n")); ++ dcsmisasprintk(ioc, printk(KERN_ERR ": rc=%x\n",rc)); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_link_errors_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ": hdr.ExtPageLength == 0\n")); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_link_errors_exit; ++ } ++ ++ ++ sasPhyPage1_data_sz = hdr.ExtPageLength * 4; ++ rc = -ENOMEM; ++ ++ sasPhyPage1 = (SasPhyPage1_t *) pci_alloc_consistent(ioc->pcidev, ++ sasPhyPage1_data_sz, &sasPhyPage1_dma); ++ ++ if (! sasPhyPage1) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_link_errors_exit; ++ } ++ ++ memset((u8 *)sasPhyPage1, 0, sasPhyPage1_data_sz); ++ cfg.physAddr = sasPhyPage1_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ /* Don't check if this failed. Already in a ++ * failure case. ++ */ ++ dcsmisasprintk(ioc, printk(KERN_ERR ": FAILED: MPI_SASPHY1_PAGEVERSION: PAGE\n")); ++ dcsmisasprintk(ioc, printk(KERN_ERR ": rc=%x\n",rc)); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_link_errors_exit; ++ } ++ ++/* EDM : dump PHY Page 1 data*/ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "---- SAS PHY PAGE 1 ------------\n")); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Invalid Dword Count=0x%x\n", ++ sasPhyPage1->InvalidDwordCount)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Running Disparity Error Count=0x%x\n", ++ sasPhyPage1->RunningDisparityErrorCount)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "Loss Dword Synch Count=0x%x\n", ++ sasPhyPage1->LossDwordSynchCount)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "PHY Reset Problem Count=0x%x\n", ++ sasPhyPage1->PhyResetProblemCount)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\n\n")); ++/* EDM : debug data */ ++ ++ karg.Information.uInvalidDwordCount = ++ le32_to_cpu(sasPhyPage1->InvalidDwordCount); ++ karg.Information.uRunningDisparityErrorCount = ++ le32_to_cpu(sasPhyPage1->RunningDisparityErrorCount); ++ karg.Information.uLossOfDwordSyncCount = ++ le32_to_cpu(sasPhyPage1->LossDwordSynchCount); ++ karg.Information.uPhyResetProblemCount = ++ le32_to_cpu(sasPhyPage1->PhyResetProblemCount); ++ ++ if (karg.Information.bResetCounts == ++ CSMI_SAS_LINK_ERROR_DONT_RESET_COUNTS ) { ++ goto cim_get_link_errors_exit; ++ } ++ ++ /* Clear Error log ++ * ++ * Issue IOUNIT Control Reqeust Message ++ */ ++ ++ /* Get a MF for this command. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": no msg frames!\n")); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_link_errors_exit; ++ } ++ ++ mpi_hdr = (MPIHeader_t *) mf; ++ MsgContext = mpi_hdr->MsgContext; ++ sasIoUnitCntrReq = (SasIoUnitControlRequest_t *)mf; ++ memset(sasIoUnitCntrReq,0,sizeof(SasIoUnitControlRequest_t)); ++ sasIoUnitCntrReq->Function = MPI_FUNCTION_SAS_IO_UNIT_CONTROL; ++ sasIoUnitCntrReq->MsgContext = MsgContext; ++ sasIoUnitCntrReq->PhyNum = phyId; ++ sasIoUnitCntrReq->Operation = MPI_SAS_OP_PHY_CLEAR_ERROR_LOG; ++ ++ if (csmisas_send_command_wait(ioc, mf, karg.IoctlHeader.Timeout) != 0) { ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_link_errors_exit; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) { ++ ++ sasIoUnitCntrReply = ++ (SasIoUnitControlReply_t *)ioc->ioctl_cmds.reply; ++ ioc_status = le16_to_cpu(sasIoUnitCntrReply->IOCStatus) ++ & MPI_IOCSTATUS_MASK; ++ ++ if (ioc_status != MPI_IOCSTATUS_SUCCESS) { ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": SAS IO Unit Control: ")); ++ dcsmisasprintk(ioc, printk("IOCStatus=0x%X IOCLogInfo=0x%X\n", ++ sasIoUnitCntrReply->IOCStatus, ++ sasIoUnitCntrReply->IOCLogInfo)); ++ } ++ } ++ ++ cim_get_link_errors_exit: ++ ++ if (sasPhyPage1) ++ pci_free_consistent(ioc->pcidev, sasPhyPage1_data_sz, ++ (u8 *) sasPhyPage1, sasPhyPage1_dma); ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_LINK_ERRORS_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmisas_get_link_errors @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++ ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS SMP Passthru command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_smp_passthru(unsigned long arg) ++{ ++ CSMI_SAS_SMP_PASSTHRU_BUFFER __user *uarg = (void __user *) arg; ++ MPT_ADAPTER *ioc; ++ CSMI_SAS_SMP_PASSTHRU_BUFFER *karg; ++ pSmpPassthroughRequest_t smpReq; ++ pSmpPassthroughReply_t smpReply; ++ MPT_FRAME_HDR *mf = NULL; ++ MPIHeader_t *mpi_hdr; ++ char *psge; ++ int iocnum, flagsLength; ++ void * request_data; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz; ++ void * response_data; ++ dma_addr_t response_data_dma; ++ u32 response_data_sz; ++ u16 ioc_status; ++ u64 sas_address; ++ u32 MsgContext; ++ int malloc_data_sz; ++ int memory_pages; ++ ++ malloc_data_sz = sizeof(CSMI_SAS_SMP_PASSTHRU_BUFFER); ++ memory_pages = get_order(malloc_data_sz); ++ karg = (CSMI_SAS_SMP_PASSTHRU_BUFFER *)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!karg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc CSMI_SAS_SMP_PASSTHRU_BUFFER " ++ "malloc_data_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ malloc_data_sz, memory_pages); ++ return -ENOMEM; ++ } ++ ++ if (copy_from_user(karg, uarg, sizeof(CSMI_SAS_SMP_PASSTHRU_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_smp_passthru struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ request_data = NULL; ++ response_data = NULL; ++ response_data_sz = sizeof(CSMI_SAS_SMP_RESPONSE); ++ request_data_sz = karg->Parameters.uRequestLength; ++ ++ if (((iocnum = mpt_verify_adapter(karg->IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ if (ioc->ioc_reset_in_progress) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Busy with IOC Reset \n", ++ __FILE__, __LINE__,__FUNCTION__); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EBUSY; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* Default to success.*/ ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* Do some error checking on the request. */ ++ if (karg->Parameters.bPortIdentifier == CSMI_SAS_IGNORE_PORT) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_SELECT_PHY_OR_PORT; ++ goto cim_smp_passthru_exit; ++ } ++ ++ if ((request_data_sz > 0xFFFF) || (!request_data_sz)) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_smp_passthru_exit; ++ } ++ ++ /* Get a free request frame and save the message context. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": no msg frames!\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_smp_passthru_exit; ++ } ++ ++ mpi_hdr = (MPIHeader_t *) mf; ++ MsgContext = mpi_hdr->MsgContext; ++ smpReq = (pSmpPassthroughRequest_t ) mf; ++ ++ memset(smpReq,0,ioc->req_sz); ++ ++ memcpy(&sas_address, karg->Parameters.bDestinationSASAddress, ++ sizeof(u64)); ++ sas_address = cpu_to_le64(reverse_byte_order64(sas_address)); ++ memcpy(&smpReq->SASAddress, &sas_address, sizeof(u64)); ++ ++ /* Fill in smp request. */ ++ smpReq->PhysicalPort = karg->Parameters.bPortIdentifier; ++ smpReq->Function = MPI_FUNCTION_SMP_PASSTHROUGH; ++ smpReq->RequestDataLength = cpu_to_le16(request_data_sz); ++ smpReq->ConnectionRate = karg->Parameters.bConnectionRate; ++ smpReq->MsgContext = MsgContext; ++ smpReq->Reserved2 = 0; ++ smpReq->Reserved3 = 0; ++ ++ /* ++ * Prepare the necessary pointers to run ++ * through the SGL generation ++ */ ++ ++ psge = (char *)&smpReq->SGL; ++ ++ /* setup the *Request* payload SGE */ ++ flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI_SGE_FLAGS_SYSTEM_ADDRESS | ++ MPI_SGE_FLAGS_HOST_TO_IOC | ++ MPI_SGE_FLAGS_END_OF_BUFFER; ++ ++ flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT; ++ flagsLength |= request_data_sz; ++ ++ request_data = pci_alloc_consistent( ++ ioc->pcidev, request_data_sz, &request_data_dma); ++ ++ if (!request_data) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ mpt_free_msg_frame(ioc, mf); ++ goto cim_smp_passthru_exit; ++ } ++ ++ ioc->add_sge(psge, flagsLength, request_data_dma); ++ psge += ioc->SGE_size; ++ ++ memcpy(request_data, &karg->Parameters.Request, request_data_sz); ++ ++ /* setup the *Response* payload SGE */ ++ response_data = pci_alloc_consistent( ++ ioc->pcidev, response_data_sz, &response_data_dma); ++ ++ if (!response_data) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ mpt_free_msg_frame(ioc, mf); ++ goto cim_smp_passthru_exit; ++ } ++ ++ flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI_SGE_FLAGS_SYSTEM_ADDRESS | ++ MPI_SGE_FLAGS_IOC_TO_HOST | ++ MPI_SGE_FLAGS_END_OF_BUFFER; ++ ++ flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT; ++ flagsLength |= response_data_sz; ++ ++ ioc->add_sge(psge, flagsLength, response_data_dma); ++ ++ if (csmisas_send_command_wait(ioc, mf, karg->IoctlHeader.Timeout) != 0) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_smp_passthru_exit; ++ } ++ ++ if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) == 0) { ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": SMP Passthru: oh no, there is no reply!!")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_smp_passthru_exit; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ smpReply = (pSmpPassthroughReply_t )ioc->ioctl_cmds.reply; ++ ioc_status = le16_to_cpu(smpReply->IOCStatus) & MPI_IOCSTATUS_MASK; ++ ++ if ((ioc_status != MPI_IOCSTATUS_SUCCESS) && ++ (ioc_status != MPI_IOCSTATUS_SCSI_DATA_UNDERRUN)) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": SMP Passthru: ")); ++ dcsmisasprintk(ioc, printk("IOCStatus=0x%X IOCLogInfo=0x%X SASStatus=0x%X\n", ++ le16_to_cpu(smpReply->IOCStatus), ++ le32_to_cpu(smpReply->IOCLogInfo), ++ smpReply->SASStatus)); ++ goto cim_smp_passthru_exit; ++ } ++ ++ karg->Parameters.bConnectionStatus = ++ map_sas_status_to_csmi(smpReply->SASStatus); ++ ++ ++ if (le16_to_cpu(smpReply->ResponseDataLength)) { ++ karg->Parameters.uResponseBytes = le16_to_cpu(smpReply->ResponseDataLength); ++ memcpy(&karg->Parameters.Response, ++ response_data, le16_to_cpu(smpReply->ResponseDataLength)); ++ } ++ ++ cim_smp_passthru_exit: ++ ++ if (request_data) ++ pci_free_consistent(ioc->pcidev, request_data_sz, ++ (u8 *)request_data, request_data_dma); ++ ++ if (response_data) ++ pci_free_consistent(ioc->pcidev, response_data_sz, ++ (u8 *)response_data, response_data_dma); ++ ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, karg, ++ sizeof(CSMI_SAS_SMP_PASSTHRU_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_smp_passthru @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ free_pages((unsigned long)karg, memory_pages); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": %s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS SSP Passthru command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int csmisas_ssp_passthru(unsigned long arg) ++{ ++ CSMI_SAS_SSP_PASSTHRU_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_SSP_PASSTHRU_BUFFER karg_hdr, * karg; ++ MPT_ADAPTER *ioc = NULL; ++ pSCSIIORequest_t pScsiRequest; ++ pSCSIIOReply_t pScsiReply; ++ MPT_FRAME_HDR *mf = NULL; ++ MPIHeader_t *mpi_hdr; ++ int iocnum,ii; ++ u64 sas_address; ++ u16 req_idx; ++ char *psge; ++ int flagsLength; ++ void * request_data; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz; ++ int malloc_data_sz; ++ int memory_pages; ++ u16 ioc_status; ++ u8 volume_id; ++ u8 volume_bus; ++ u8 is_hidden_raid_component; ++ u8 channel; ++ u8 id; ++ struct sas_device_info *sas_info; ++ u8 skey, asc, ascq; ++ u32 MsgContext; ++ ++ if (copy_from_user(&karg_hdr, uarg, sizeof(CSMI_SAS_SSP_PASSTHRU_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_ssp_passthru struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ request_data = NULL; ++ request_data_sz = karg_hdr.Parameters.uDataLength; ++ channel = 0; ++ id = 0; ++ volume_id = 0; ++ volume_bus = 0; ++ is_hidden_raid_component = 0; ++ ++ malloc_data_sz = (request_data_sz + ++ offsetof(CSMI_SAS_SSP_PASSTHRU_BUFFER, bDataBuffer)); ++ memory_pages = get_order(malloc_data_sz); ++ karg = (CSMI_SAS_SSP_PASSTHRU_BUFFER *)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!karg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc SAS_SSP_PASSTHRU_BUFFER " ++ "malloc_data_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ malloc_data_sz, memory_pages); ++ return -ENOMEM; ++ } ++ ++ memset(karg, 0, sizeof(*karg)); ++ ++ if (copy_from_user(karg, uarg, request_data_sz + ++ offsetof(CSMI_SAS_SSP_PASSTHRU_BUFFER,bDataBuffer))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_ssp_passthru struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ /* ++ * some checks of the incoming frame ++ */ ++ if ( offsetof(CSMI_SAS_SSP_PASSTHRU_BUFFER,bDataBuffer) + ++ request_data_sz - sizeof(IOCTL_HEADER) > ++ karg->IoctlHeader.Length ) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_INVALID_PARAMETER; ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ "%s::%s()" ++ " @%d - expected datalen incorrect!\n", ++ __FILE__, __FUNCTION__, __LINE__)); ++ goto cim_ssp_passthru_exit; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg->IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_INVALID_PARAMETER; ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ goto cim_ssp_passthru_exit; ++ } ++ ++ if (ioc->ioc_reset_in_progress) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Busy with IOC Reset \n", ++ __FILE__, __LINE__,__FUNCTION__); ++ return -EBUSY; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_INVALID_PARAMETER; ++ printk(KERN_ERR "%s::%s()@%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ goto cim_ssp_passthru_exit; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* Default to success. ++ */ ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* Neither a phy nor a port has been selected. ++ */ ++ if ((karg->Parameters.bPhyIdentifier == CSMI_SAS_USE_PORT_IDENTIFIER) && ++ (karg->Parameters.bPortIdentifier == CSMI_SAS_IGNORE_PORT)) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_SELECT_PHY_OR_PORT; ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ "%s::%s()" ++ " @%d - incorrect bPhyIdentifier and bPortIdentifier!\n", ++ __FILE__, __FUNCTION__, __LINE__)); ++ goto cim_ssp_passthru_exit; ++ } ++ ++ /* A phy has been selected. Verify that it's valid. ++ */ ++ if (karg->Parameters.bPortIdentifier == CSMI_SAS_IGNORE_PORT) { ++ ++ /* Is the phy in range? */ ++ if (karg->Parameters.bPhyIdentifier >= ioc->num_ports) { ++ dcsmisasprintk(ioc, printk(KERN_WARNING ": phyId >= ioc->num_ports (%d %d)\n", ++ karg->Parameters.bPhyIdentifier, ++ ioc->num_ports)); ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_PHY_DOES_NOT_EXIST; ++ goto cim_ssp_passthru_exit; ++ } ++ } ++ ++ if(karg->Parameters.bAdditionalCDBLength) { ++ /* TODO - SCSI IO (32) Request Message support ++ */ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": greater than 16-byte cdb " ++ "is not supported!\n")); ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ goto cim_ssp_passthru_exit; ++ } ++ ++ /* we will use SAS address to resolve the scsi adddressing ++ */ ++ memcpy(&sas_address, karg->Parameters.bDestinationSASAddress, ++ sizeof(u64)); ++ sas_address = reverse_byte_order64(sas_address); ++ ++ /* Search the list for the matching SAS address. ++ */ ++ sas_info = csmisas_get_device_component_by_sas_addr(ioc, sas_address); ++ if (!sas_info || sas_info->is_cached) { ++ /* ++ *Invalid SAS address ++ */ ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ "%s::%s() @%d - couldn't find associated " ++ "SASAddress=%llX!\n", __FILE__, __FUNCTION__, __LINE__, ++ (unsigned long long)sas_address)); ++ goto cim_ssp_passthru_exit; ++ } ++ ++ id = sas_info->fw.id; ++ channel = sas_info->fw.channel; ++ ++ if (csmisas_is_phys_disk(ioc, channel, id)) { ++ id = csmisas_raid_id_to_num(ioc, channel, id); ++ channel = 0; ++ is_hidden_raid_component = 1; ++ } ++ ++ /* Get a free request frame and save the message context. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": no msg frames!\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_ssp_passthru_exit; ++ } ++ ++ mpi_hdr = (MPIHeader_t *) mf; ++ MsgContext = mpi_hdr->MsgContext; ++ pScsiRequest = (pSCSIIORequest_t) mf; ++ req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); ++ ++ memset(pScsiRequest,0,sizeof(SCSIIORequest_t)); ++ ++ /* Fill in SCSI IO (16) request. ++ */ ++ ++ pScsiRequest->Function = (is_hidden_raid_component == 1) ? ++ MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH : MPI_FUNCTION_SCSI_IO_REQUEST; ++ pScsiRequest->TargetID = id; ++ pScsiRequest->Bus = channel; ++ memcpy(pScsiRequest->LUN, &karg->Parameters.bLun, 8); ++ pScsiRequest->CDBLength = karg->Parameters.bCDBLength; ++ pScsiRequest->DataLength = cpu_to_le32(request_data_sz); ++ pScsiRequest->MsgContext = MsgContext; ++ memcpy(pScsiRequest->CDB, karg->Parameters.bCDB, ++ pScsiRequest->CDBLength); ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\tchannel = %d id = %d ", ++ sas_info->fw.channel, sas_info->fw.id)); ++ dcsmisasprintk(ioc, if(is_hidden_raid_component) ++ printk(KERN_DEBUG "num_id = %d ", id)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\n")); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\tcdb_len = %d request_len = %d\n", ++ pScsiRequest->CDBLength, request_data_sz)); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\t")); ++ dcsmisasprintk(ioc, for (ii = 0; ii < pScsiRequest->CDBLength; ++ii) ++ printk(" %02x", pScsiRequest->CDB[ii])); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\n")); ++ ++ /* direction ++ */ ++ if (karg->Parameters.uFlags & CSMI_SAS_SSP_READ) { ++ pScsiRequest->Control = cpu_to_le32(MPI_SCSIIO_CONTROL_READ); ++ } else if (karg->Parameters.uFlags & CSMI_SAS_SSP_WRITE) { ++ pScsiRequest->Control = cpu_to_le32(MPI_SCSIIO_CONTROL_WRITE); ++ } else if ((karg->Parameters.uFlags & CSMI_SAS_SSP_UNSPECIFIED) && ++ (!karg->Parameters.uDataLength)) { ++ /* no data transfer ++ */ ++ pScsiRequest->Control = cpu_to_le32(MPI_SCSIIO_CONTROL_NODATATRANSFER); ++ } else { ++ /* no direction specified ++ */ ++ pScsiRequest->Control = cpu_to_le32(MPI_SCSIIO_CONTROL_READ); ++ pScsiRequest->MsgFlags = ++ MPI_SCSIIO_MSGFLGS_CMD_DETERMINES_DATA_DIR; ++ } ++ ++ pScsiRequest->MsgFlags &= ~MPI_SCSIIO_MSGFLGS_SENSE_WIDTH; ++ if (ioc->sg_addr_size == sizeof(u64)) ++ pScsiRequest->MsgFlags |= MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64; ++ ++ /* task attributes ++ */ ++ if((karg->Parameters.uFlags && 0xFF) == 0) { ++ pScsiRequest->Control |= cpu_to_le32(MPI_SCSIIO_CONTROL_SIMPLEQ); ++ } else if (karg->Parameters.uFlags & ++ CSMI_SAS_SSP_TASK_ATTRIBUTE_HEAD_OF_QUEUE) { ++ pScsiRequest->Control |= cpu_to_le32(MPI_SCSIIO_CONTROL_HEADOFQ); ++ } else if (karg->Parameters.uFlags & ++ CSMI_SAS_SSP_TASK_ATTRIBUTE_ORDERED) { ++ pScsiRequest->Control |= cpu_to_le32(MPI_SCSIIO_CONTROL_ORDEREDQ); ++ } else if (karg->Parameters.uFlags & ++ CSMI_SAS_SSP_TASK_ATTRIBUTE_ACA) { ++ pScsiRequest->Control |= cpu_to_le32(MPI_SCSIIO_CONTROL_ACAQ); ++ } else { ++ pScsiRequest->Control |= cpu_to_le32(MPI_SCSIIO_CONTROL_UNTAGGED); ++ } ++ ++ /* setup sense ++ */ ++ pScsiRequest->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; ++ pScsiRequest->SenseBufferLowAddr = cpu_to_le32(ioc->sense_buf_low_dma + ++ (req_idx * MPT_SENSE_BUFFER_ALLOC)); ++ ++ /* setup databuffer sg, assuming we fit everything one contiguous buffer ++ */ ++ psge = (char *)&pScsiRequest->SGL; ++ ++ if (karg->Parameters.uFlags & CSMI_SAS_SSP_WRITE) { ++ flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE; ++ } else if (karg->Parameters.uFlags & CSMI_SAS_SSP_READ) { ++ flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ; ++ }else { ++ flagsLength = ( MPI_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI_SGE_FLAGS_DIRECTION ) ++ << MPI_SGE_FLAGS_SHIFT; ++ } ++ flagsLength |= request_data_sz; ++ ++ if ( request_data_sz > 0) { ++ request_data = pci_alloc_consistent( ++ ioc->pcidev, request_data_sz, &request_data_dma); ++ ++ if (request_data == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED " ++ "request_data_sz=%d\n", request_data_sz)); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ mpt_free_msg_frame(ioc, mf); ++ goto cim_ssp_passthru_exit; ++ } ++ ++ ioc->add_sge(psge, flagsLength, request_data_dma); ++ if (karg->Parameters.uFlags & CSMI_SAS_SSP_WRITE) ++ memcpy(request_data, karg->bDataBuffer, request_data_sz); ++ } else { ++ ioc->add_sge(psge, flagsLength, (dma_addr_t) -1); ++ } ++ ++ if (csmisas_send_command_wait(ioc, mf, karg->IoctlHeader.Timeout) != 0) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_ssp_passthru_exit; ++ } ++ ++ memset(&karg->Status,0,sizeof(CSMI_SAS_SSP_PASSTHRU_STATUS)); ++ karg->Status.bConnectionStatus = CSMI_SAS_OPEN_ACCEPT; ++ karg->Status.bDataPresent = CSMI_SAS_SSP_NO_DATA_PRESENT; ++ karg->Status.bStatus = GOOD; ++ karg->Status.bResponseLength[0] = 0; ++ karg->Status.bResponseLength[1] = 0; ++ karg->Status.uDataBytes = request_data_sz; ++ ++ /* process the completed Reply Message Frame */ ++ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) { ++ ++ pScsiReply = (pSCSIIOReply_t ) ioc->ioctl_cmds.reply; ++ karg->Status.bStatus = pScsiReply->SCSIStatus; ++ karg->Status.uDataBytes = min(le32_to_cpu(pScsiReply->TransferCount), ++ request_data_sz); ++ ioc_status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; ++ ++ if (pScsiReply->SCSIState == ++ MPI_SCSI_STATE_AUTOSENSE_VALID) { ++ karg->Status.bConnectionStatus = ++ CSMI_SAS_SSP_SENSE_DATA_PRESENT; ++ karg->Status.bResponseLength[0] = ++ (u8)le32_to_cpu(pScsiReply->SenseCount) & 0xFF; ++ memcpy(karg->Status.bResponse, ++ ioc->ioctl_cmds.sense, le32_to_cpu(pScsiReply->SenseCount)); ++ ++ skey = ioc->ioctl_cmds.sense[2] & 0x0F; ++ asc = ioc->ioctl_cmds.sense[12]; ++ ascq = ioc->ioctl_cmds.sense[13]; ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "\t [sense_key,asc,ascq]: " ++ "[0x%02x,0x%02x,0x%02x]\n", ++ skey, asc, ascq)); ++ ++ } else if(pScsiReply->SCSIState == ++ MPI_SCSI_STATE_RESPONSE_INFO_VALID) { ++ karg->Status.bDataPresent = ++ CSMI_SAS_SSP_RESPONSE_DATA_PRESENT; ++ karg->Status.bResponseLength[0] = ++ sizeof(pScsiReply->ResponseInfo); ++ for (ii=0;iiResponseInfo);ii++) { ++ karg->Status.bResponse[ii] = ++ ((u8*)&pScsiReply->ResponseInfo)[ ++ (sizeof(pScsiReply->ResponseInfo)-1)-ii]; ++ } ++ } else if ((ioc_status != MPI_IOCSTATUS_SUCCESS) && ++ (ioc_status != MPI_IOCSTATUS_SCSI_RECOVERED_ERROR) && ++ (ioc_status != MPI_IOCSTATUS_SCSI_DATA_UNDERRUN)) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": SCSI IO : ")); ++ dcsmisasprintk(ioc, printk("IOCStatus=0x%X IOCLogInfo=0x%X\n", ++ pScsiReply->IOCStatus, ++ pScsiReply->IOCLogInfo)); ++ } ++ } ++ ++ if ((karg->Status.uDataBytes) && (request_data) && ++ (karg->Parameters.uFlags & CSMI_SAS_SSP_READ)) { ++ if (copy_to_user((void __user *)uarg->bDataBuffer, ++ request_data, karg->Status.uDataBytes)) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to write data to user %p\n", ++ __FILE__, __LINE__,__FUNCTION__, ++ (void*)karg->bDataBuffer); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ } ++ } ++ ++ cim_ssp_passthru_exit: ++ ++ ++ if (request_data) ++ pci_free_consistent(ioc->pcidev, request_data_sz, ++ (u8 *)request_data, request_data_dma); ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, karg, ++ offsetof(CSMI_SAS_SSP_PASSTHRU_BUFFER, bDataBuffer))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_ssp_passthru @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ free_pages((unsigned long)karg, memory_pages); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS STP Passthru command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_stp_passthru(unsigned long arg) ++{ ++ CSMI_SAS_STP_PASSTHRU_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_STP_PASSTHRU_BUFFER karg_hdr, *karg; ++ MPT_ADAPTER *ioc = NULL; ++ pSataPassthroughRequest_t pSataRequest; ++ pSataPassthroughReply_t pSataReply; ++ MPT_FRAME_HDR *mf = NULL; ++ MPIHeader_t *mpi_hdr; ++ int iocnum; ++ u32 data_sz; ++ u64 sas_address; ++ u16 req_idx; ++ char *psge; ++ int flagsLength; ++ void * request_data; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz; ++ int malloc_data_sz; ++ int memory_pages; ++ u8 channel; ++ u8 id; ++ u8 volume_id; ++ u8 volume_bus; ++ struct sas_device_info *sas_info; ++ u16 ioc_status; ++ u32 MsgContext; ++ ++ if (copy_from_user(&karg_hdr, uarg, sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ request_data=NULL; ++ request_data_sz = karg_hdr.Parameters.uDataLength; ++ volume_id = 0; ++ volume_bus = 0; ++ channel = 0; ++ id = 0; ++ ++ malloc_data_sz = (request_data_sz + ++ offsetof(CSMI_SAS_STP_PASSTHRU_BUFFER, bDataBuffer)); ++ memory_pages = get_order(malloc_data_sz); ++ karg = (CSMI_SAS_STP_PASSTHRU_BUFFER *)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!karg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc CSMI_SAS_STP_PASSTHRU_BUFFER " ++ "malloc_data_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ malloc_data_sz, memory_pages); ++ return -ENOMEM; ++ } ++ ++ memset(karg, 0, sizeof(*karg)); ++ ++ if (copy_from_user(karg, uarg, malloc_data_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_ssp_passthru struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg->IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ if (ioc->ioc_reset_in_progress) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Busy with IOC Reset \n", ++ __FILE__, __LINE__,__FUNCTION__); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EBUSY; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* Default to success. ++ */ ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* Neither a phy nor a port has been selected. ++ */ ++ if ((karg->Parameters.bPhyIdentifier == CSMI_SAS_USE_PORT_IDENTIFIER) && ++ (karg->Parameters.bPortIdentifier == CSMI_SAS_IGNORE_PORT)) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_SELECT_PHY_OR_PORT; ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ "%s::%s() @%d - incorrect bPhyIdentifier and bPortIdentifier!\n", ++ __FILE__,__FUNCTION__, __LINE__)); ++ goto cim_stp_passthru_exit; ++ } ++ ++ /* A phy has been selected. Verify that it's valid. ++ */ ++ if (karg->Parameters.bPortIdentifier == CSMI_SAS_IGNORE_PORT) { ++ ++ /* Is the phy in range? */ ++ if (karg->Parameters.bPhyIdentifier >= ioc->num_ports) { ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_PHY_DOES_NOT_EXIST; ++ goto cim_stp_passthru_exit; ++ } ++ } ++ ++ data_sz = sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) - ++ sizeof(IOCTL_HEADER) - sizeof(u8*) + ++ request_data_sz; ++ ++ if ( data_sz > karg->IoctlHeader.Length ) { ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ "%s::%s() @%d - expected datalen incorrect!\n", ++ __FILE__, __FUNCTION__,__LINE__)); ++ goto cim_stp_passthru_exit; ++ } ++ ++ ++ /* we will use SAS address to resolve the scsi adddressing ++ */ ++ memcpy(&sas_address, karg->Parameters.bDestinationSASAddress, ++ sizeof(u64)); ++ sas_address = reverse_byte_order64(sas_address); ++ ++ /* Search the list for the matching SAS address. ++ */ ++ sas_info = csmisas_get_device_component_by_sas_addr(ioc, sas_address); ++ if (!sas_info || sas_info->is_cached || sas_info->is_logical_volume) { ++ /* ++ *Invalid SAS address ++ */ ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ "%s::%s() @%d - couldn't find associated " ++ "SASAddress=%llX!\n", __FILE__, __FUNCTION__, __LINE__, ++ (unsigned long long)sas_address)); ++ goto cim_stp_passthru_exit; ++ } ++ ++ id = sas_info->fw.id; ++ channel = sas_info->fw.channel; ++ ++ /* check that this is an STP or SATA target device ++ */ ++ if ( !(sas_info->device_info & MPI_SAS_DEVICE_INFO_STP_TARGET ) && ++ !(sas_info->device_info & MPI_SAS_DEVICE_INFO_SATA_DEVICE )) { ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ goto cim_stp_passthru_exit; ++ } ++ ++ /* Get a free request frame and save the message context. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": no msg frames!\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_stp_passthru_exit; ++ } ++ ++ mpi_hdr = (MPIHeader_t *) mf; ++ MsgContext = mpi_hdr->MsgContext; ++ pSataRequest = (pSataPassthroughRequest_t) mf; ++ req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); ++ ++ memset(pSataRequest,0,sizeof(pSataPassthroughRequest_t)); ++ ++ pSataRequest->TargetID = id; ++ pSataRequest->Bus = channel; ++ pSataRequest->Function = MPI_FUNCTION_SATA_PASSTHROUGH; ++ pSataRequest->PassthroughFlags = cpu_to_le16(karg->Parameters.uFlags); ++ pSataRequest->ConnectionRate = karg->Parameters.bConnectionRate; ++ pSataRequest->MsgContext = MsgContext; ++ pSataRequest->DataLength = cpu_to_le32(request_data_sz); ++ pSataRequest->MsgFlags = 0; ++ memcpy( pSataRequest->CommandFIS,karg->Parameters.bCommandFIS, 20); ++ ++ psge = (char *)&pSataRequest->SGL; ++ if (karg->Parameters.uFlags & CSMI_SAS_STP_WRITE) { ++ flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE; ++ } else if (karg->Parameters.uFlags & CSMI_SAS_STP_READ) { ++ flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ; ++ }else { ++ flagsLength = ( MPI_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI_SGE_FLAGS_DIRECTION ) ++ << MPI_SGE_FLAGS_SHIFT; ++ } ++ ++ flagsLength |= request_data_sz; ++ if (request_data_sz > 0) { ++ request_data = pci_alloc_consistent( ++ ioc->pcidev, request_data_sz, &request_data_dma); ++ ++ if (request_data == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ mpt_free_msg_frame(ioc, mf); ++ goto cim_stp_passthru_exit; ++ } ++ ++ ioc->add_sge(psge, flagsLength, request_data_dma); ++ if (karg->Parameters.uFlags & CSMI_SAS_SSP_WRITE) ++ memcpy(request_data, karg->bDataBuffer, request_data_sz); ++ } else { ++ ioc->add_sge(psge, flagsLength, (dma_addr_t) -1); ++ } ++ ++ if (csmisas_send_command_wait(ioc, mf, karg->IoctlHeader.Timeout) != 0) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_stp_passthru_exit; ++ } ++ ++ memset(&karg->Status,0,sizeof(CSMI_SAS_STP_PASSTHRU_STATUS)); ++ ++ if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) == 0) { ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": STP Passthru: oh no, there is no reply!!")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_stp_passthru_exit; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ pSataReply = (pSataPassthroughReply_t ) ioc->ioctl_cmds.reply; ++ ioc_status = le16_to_cpu(pSataReply->IOCStatus) & MPI_IOCSTATUS_MASK; ++ ++ if (ioc_status != MPI_IOCSTATUS_SUCCESS && ++ ioc_status != MPI_IOCSTATUS_SCSI_DATA_UNDERRUN) { ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": STP Passthru: ")); ++ dcsmisasprintk(ioc, printk("IOCStatus=0x%X IOCLogInfo=0x%X SASStatus=0x%X\n", ++ le16_to_cpu(pSataReply->IOCStatus), ++ le32_to_cpu(pSataReply->IOCLogInfo), ++ pSataReply->SASStatus)); ++ } ++ ++ karg->Status.bConnectionStatus = ++ map_sas_status_to_csmi(pSataReply->SASStatus); ++ ++ memcpy(karg->Status.bStatusFIS,pSataReply->StatusFIS, 20); ++ ++ /* ++ * for now, just zero out uSCR array, ++ * then copy the one dword returned ++ * in the reply frame into uSCR[0] ++ */ ++ memset( karg->Status.uSCR, 0, 64); ++ karg->Status.uSCR[0] = le32_to_cpu(pSataReply->StatusControlRegisters); ++ ++ if((le32_to_cpu(pSataReply->TransferCount)) && (request_data) && ++ (karg->Parameters.uFlags & CSMI_SAS_STP_READ)) { ++ karg->Status.uDataBytes = ++ min(le32_to_cpu(pSataReply->TransferCount),request_data_sz); ++ if (copy_to_user((void __user *)uarg->bDataBuffer, ++ request_data, karg->Status.uDataBytes)) { ++ printk(KERN_ERR "%s::%s() @%d - " ++ "Unable to write data to user %p\n", ++ __FILE__, __FUNCTION__, __LINE__, ++ (void*)karg->bDataBuffer); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ } ++ } ++ ++ cim_stp_passthru_exit: ++ ++ if (request_data) ++ pci_free_consistent(ioc->pcidev, request_data_sz, ++ (u8 *)request_data, request_data_dma); ++ ++ /* Copy th data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, karg, ++ offsetof(CSMI_SAS_STP_PASSTHRU_BUFFER, bDataBuffer))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_ssp_passthru @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ free_pages((unsigned long)karg, memory_pages); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": %s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Firmware Download command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_firmware_download(unsigned long arg) ++{ ++ CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ pMpiFwHeader_t pFwHeader=NULL; ++ ++ if (copy_from_user(&karg, uarg, ++ sizeof(CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_firmware_download struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ /* Default to success.*/ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ karg.Information.usStatus = CSMI_SAS_FWD_SUCCESS; ++ karg.Information.usSeverity = CSMI_SAS_FWD_INFORMATION; ++ ++ /* some checks of the incoming frame */ ++ if ((karg.Information.uBufferLength + ++ sizeof(CSMI_SAS_FIRMWARE_DOWNLOAD)) > ++ karg.IoctlHeader.Length) { ++ karg.IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ karg.Information.usStatus = CSMI_SAS_FWD_FAILED; ++ goto cim_firmware_download_exit; ++ } ++ ++ if ( karg.Information.uDownloadFlags & ++ (CSMI_SAS_FWD_SOFT_RESET | CSMI_SAS_FWD_VALIDATE)) { ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ karg.Information.usStatus = CSMI_SAS_FWD_REJECT; ++ karg.Information.usSeverity = CSMI_SAS_FWD_ERROR; ++ goto cim_firmware_download_exit; ++ } ++ ++ /* now we need to alloc memory so we can pull in the ++ * fw image attached to end of incoming packet. ++ */ ++ pFwHeader = kmalloc(karg.Information.uBufferLength, GFP_KERNEL); ++ if (!pFwHeader){ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ karg.Information.usStatus = CSMI_SAS_FWD_REJECT; ++ karg.Information.usSeverity = CSMI_SAS_FWD_ERROR; ++ goto cim_firmware_download_exit; ++ } ++ memset(pFwHeader, 0, sizeof(*pFwHeader)); ++ ++ if (copy_from_user(pFwHeader, uarg->bDataBuffer, ++ karg.Information.uBufferLength)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in pFwHeader @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if ( !((pFwHeader->Signature0 == MPI_FW_HEADER_SIGNATURE_0) && ++ (pFwHeader->Signature1 == MPI_FW_HEADER_SIGNATURE_1) && ++ (pFwHeader->Signature2 == MPI_FW_HEADER_SIGNATURE_2))) { ++ // the signature check failed ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ karg.Information.usStatus = CSMI_SAS_FWD_REJECT; ++ karg.Information.usSeverity = CSMI_SAS_FWD_ERROR; ++ goto cim_firmware_download_exit; ++ } ++ ++ if ( mptctl_do_fw_download(karg.IoctlHeader.IOControllerNumber, ++ uarg->bDataBuffer, karg.Information.uBufferLength) ++ != 0) { ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ karg.Information.usStatus = CSMI_SAS_FWD_FAILED; ++ karg.Information.usSeverity = CSMI_SAS_FWD_FATAL; ++ goto cim_firmware_download_exit; ++ } ++ ++ if((karg.Information.uDownloadFlags & CSMI_SAS_FWD_SOFT_RESET) || ++ (karg.Information.uDownloadFlags & CSMI_SAS_FWD_HARD_RESET)) { ++ if (mpt_HardResetHandler(ioc, CAN_SLEEP) != 0) { ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ karg.Information.usStatus = CSMI_SAS_FWD_FAILED; ++ karg.Information.usSeverity = CSMI_SAS_FWD_FATAL; ++ } ++ } ++ ++ cim_firmware_download_exit: ++ ++ if(pFwHeader) ++ kfree(pFwHeader); ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_firmware_download @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Get RAID Info command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_raid_info(unsigned long arg) ++{ ++ CSMI_SAS_RAID_INFO_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_RAID_INFO_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ u32 raidFlags; ++ u8 maxRaidTypes; ++ u8 maxDrivesPerSet; ++ ++ if (copy_from_user(&karg, uarg, sizeof(CSMI_SAS_RAID_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_get_raid_info struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ if (!ioc->raid_data.pIocPg2) ++ goto csmisas_get_raid_info_out; ++ karg.Information.uNumRaidSets = ++ ioc->raid_data.pIocPg2->NumActiveVolumes; ++ karg.Information.uMaxRaidSets = ioc->raid_data.pIocPg2->MaxVolumes; ++ if( ioc->raid_data.pIocPg6 ) { ++ // get absolute maximum for all RAID sets ++ maxDrivesPerSet = ioc->raid_data.pIocPg6->MaxDrivesIS; ++ maxDrivesPerSet = max(ioc->raid_data.pIocPg6->MaxDrivesIM, ++ maxDrivesPerSet); ++ maxDrivesPerSet = max(ioc->raid_data.pIocPg6->MaxDrivesIME, ++ maxDrivesPerSet); ++ karg.Information.uMaxDrivesPerSet = maxDrivesPerSet; ++ } ++ else ++ karg.Information.uMaxDrivesPerSet = 8; ++ // For bMaxRaidSets, count bits set in bits 0-6 of CapabilitiesFlags ++ raidFlags = ioc->raid_data.pIocPg2->CapabilitiesFlags & 0x0000007F; ++ for( maxRaidTypes=0; raidFlags; maxRaidTypes++ ) ++ raidFlags &= raidFlags - 1; ++ karg.Information.bMaxRaidTypes = maxRaidTypes; ++ // ulMinRaidSetBlocks hard coded to 1MB until available from config page ++ karg.Information.ulMinRaidSetBlocks.uLowPart = 2048; ++ karg.Information.ulMinRaidSetBlocks.uHighPart = 0; ++ karg.Information.ulMaxRaidSetBlocks.uLowPart = 0xffffffff; ++ if( ioc->raid_data.pIocPg2->CapabilitiesFlags & ++ MPI_IOCPAGE2_CAP_FLAGS_RAID_64_BIT_ADDRESSING ) ++ karg.Information.ulMaxRaidSetBlocks.uHighPart = 0xffffffff; ++ else ++ karg.Information.ulMaxRaidSetBlocks.uHighPart = 0; ++ karg.Information.uMaxPhysicalDrives = ++ ioc->raid_data.pIocPg2->MaxPhysDisks; ++ karg.Information.uMaxExtents = 1; ++ karg.Information.uMaxModules = 0; ++ karg.Information.uMaxTransformationMemory = 0; ++ karg.Information.uChangeCount = ioc->csmi_change_count; ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++csmisas_get_raid_info_out: ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_RAID_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_get_raid_info @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * csmisas_do_raid - Format and Issue a RAID volume request message. ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @action: What do be done. ++ * @PhysDiskNum: Logical target id. ++ * @VolumeBus: Target locations bus. ++ * @VolumeId: Volume id ++ * ++ * Returns: < 0 on a fatal error ++ * 0 on success ++ * ++ * Remark: Wait to return until reply processed by the ISR. ++ **/ ++static int ++csmisas_do_raid(MPT_ADAPTER *ioc, u8 action, u8 PhysDiskNum, u8 VolumeBus, u8 VolumeId, pMpiRaidActionReply_t reply) ++{ ++ MpiRaidActionRequest_t *pReq; ++ MpiRaidActionReply_t *pReply; ++ MPT_FRAME_HDR *mf; ++ ++ /* Get and Populate a free Frame ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": no msg frames!\n")); ++ return -EAGAIN; ++ } ++ pReq = (MpiRaidActionRequest_t *)mf; ++ pReq->Action = action; ++ pReq->Reserved1 = 0; ++ pReq->ChainOffset = 0; ++ pReq->Function = MPI_FUNCTION_RAID_ACTION; ++ pReq->VolumeID = VolumeId; ++ pReq->VolumeBus = VolumeBus; ++ pReq->PhysDiskNum = PhysDiskNum; ++ pReq->MsgFlags = 0; ++ pReq->Reserved2 = 0; ++ pReq->ActionDataWord = 0; /* Reserved for this action */ ++ //pReq->ActionDataSGE = 0; ++ ++ ioc->add_sge((char *)&pReq->ActionDataSGE, ++ MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1); ++ ++ if (csmisas_send_command_wait(ioc, mf, MPT_IOCTL_DEFAULT_TIMEOUT) != 0) ++ return -ENODATA; ++ ++ if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) && ++ (reply != NULL)){ ++ pReply = (MpiRaidActionReply_t *)&(ioc->ioctl_cmds.reply); ++ memcpy(reply, pReply, ++ min(ioc->reply_sz, ++ 4*pReply->MsgLength)); ++ } ++ ++ return 0; ++} ++ ++/** ++ * csmisas_raid_inq ++ * @ioc = per host instance ++ * @opcode = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH or ++ * MPI_FUNCTION_SCSI_IO_REQUEST ++ * @id = target id ++ * @bus = target bus ++ * @inq_vpd = inquiry data, returned ++ * @inq_vpd_sz = maximum size of inquiry data ++ * ++ * Return = 0(sucess), non-zero(failure) ++ **/ ++static int ++csmisas_raid_inq(MPT_ADAPTER *ioc, u8 opcode, u8 bus, u8 id, u8 inq_vpd_page, ++ u8 * inq_vpd, u32 inq_vpd_sz) ++{ ++ MPT_FRAME_HDR *mf = NULL; ++ MPIHeader_t *mpi_hdr; ++ pSCSIIORequest_t pScsiRequest; ++ u16 req_idx; ++ char *psge; ++ u8 inq_vpd_cdb[6]; ++ u8 *request_data=NULL; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz; ++ int rc = 0; ++ u32 MsgContext; ++ ++ request_data_sz = inq_vpd_sz; ++ ++ /* fill-in cdb */ ++ memset(inq_vpd_cdb, 0, sizeof(inq_vpd_cdb)); ++ inq_vpd_cdb[0] = 0x12; ++ if (inq_vpd_page) { ++ inq_vpd_cdb[1] = 0x01; /* evpd bit */ ++ inq_vpd_cdb[2] = inq_vpd_page; ++ } ++ inq_vpd_cdb[3] = (u8)(request_data_sz >> 8); ++ inq_vpd_cdb[4] = (u8)request_data_sz; ++ ++ /* Get a free request frame and save the message context. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": no msg frames!\n")); ++ goto csmisas_raid_inq_exit; ++ } ++ ++ mpi_hdr = (MPIHeader_t *) mf; ++ MsgContext = mpi_hdr->MsgContext; ++ pScsiRequest = (pSCSIIORequest_t) mf; ++ req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); ++ ++ memset(pScsiRequest,0,sizeof(SCSIIORequest_t)); ++ pScsiRequest->Function = opcode; ++ pScsiRequest->TargetID = id; ++ pScsiRequest->Bus = bus; ++ pScsiRequest->CDBLength = 6; ++ pScsiRequest->DataLength = cpu_to_le32(request_data_sz); ++ pScsiRequest->MsgContext = MsgContext; ++ memcpy(pScsiRequest->CDB,inq_vpd_cdb,pScsiRequest->CDBLength); ++ pScsiRequest->Control = cpu_to_le32(MPI_SCSIIO_CONTROL_READ); ++ pScsiRequest->Control |= cpu_to_le32(MPI_SCSIIO_CONTROL_SIMPLEQ); ++ pScsiRequest->MsgFlags &= ~MPI_SCSIIO_MSGFLGS_SENSE_WIDTH; ++ if (ioc->sg_addr_size == sizeof(u64)) ++ pScsiRequest->MsgFlags |= MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64; ++ ++ /* setup sense ++ */ ++ pScsiRequest->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; ++ pScsiRequest->SenseBufferLowAddr = cpu_to_le32(ioc->sense_buf_low_dma + ++ (req_idx * MPT_SENSE_BUFFER_ALLOC)); ++ ++ request_data = pci_alloc_consistent( ++ ioc->pcidev, request_data_sz, &request_data_dma); ++ ++ if (request_data == NULL) { ++ mpt_free_msg_frame(ioc, mf); ++ rc=-1; ++ goto csmisas_raid_inq_exit; ++ } ++ ++ memset(request_data,0,request_data_sz); ++ psge = (char *)&pScsiRequest->SGL; ++ ioc->add_sge(psge, (MPT_SGE_FLAGS_SSIMPLE_READ | 0xFC) , ++ request_data_dma); ++ ++ if (csmisas_send_command_wait(ioc, mf, MPT_IOCTL_DEFAULT_TIMEOUT) != 0) { ++ rc=-1; ++ goto csmisas_raid_inq_exit; ++ } ++ ++ /* copy the request_data */ ++ memcpy(inq_vpd, request_data, request_data_sz); ++ ++ csmisas_raid_inq_exit: ++ ++ if (request_data) ++ pci_free_consistent(ioc->pcidev, request_data_sz, ++ request_data, request_data_dma); ++ ++ return rc; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Get RAID Config command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_raid_config(unsigned long arg) ++{ ++ CSMI_SAS_RAID_CONFIG_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_RAID_CONFIG_BUFFER karg,*pKarg=NULL; ++ CONFIGPARMS cfg; ++ ConfigPageHeader_t header; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ u8 volumeID, VolumeBus; ++ u8 physDiskNum, physDiskNumMax; ++ int volumepage0sz = 0; ++ int physdiskpage0sz = 0, ioc_page5_sz = 0; ++ dma_addr_t volume0_dma, physdisk0_dma; ++ dma_addr_t ioc_page5_dma = 0; ++ pRaidVolumePage0_t pVolume0 = NULL; ++ pRaidPhysDiskPage0_t pPhysDisk0 = NULL; ++ pMpiRaidActionReply_t pRaidActionReply = NULL; ++ u32 device_info = 0; ++ pIOCPage5_t pIocPage5 = NULL; ++ int i, idx, csmi_sas_raid_config_buffer_sz; ++ int memory_pages; ++ int copy_buffer_sz = 0; ++ u64 totalMaxLBA, tmpTotalMaxLBA; ++ u64 sas_address; ++ struct sas_device_info *sas_info; ++ ++ if (copy_from_user(&karg, uarg, sizeof(IOCTL_HEADER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmisas_get_raid_config struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ csmi_sas_raid_config_buffer_sz = karg.IoctlHeader.Length; ++ memory_pages = get_order(csmi_sas_raid_config_buffer_sz); ++ pKarg = (CSMI_SAS_RAID_CONFIG_BUFFER *)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!pKarg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc RAID_CONFIG_BUFFER " ++ "csmi_sas_raid_config_buffer_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ csmi_sas_raid_config_buffer_sz, memory_pages); ++ return -ENOMEM; ++ } ++ memset(pKarg, 0, sizeof(*pKarg)); ++ ++ if (copy_from_user(pKarg, uarg, csmi_sas_raid_config_buffer_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmisas_get_raid_config struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(pKarg->IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ if (pKarg->Configuration.uChangeCount != 0 && ++ pKarg->Configuration.uChangeCount != ioc->csmi_change_count ) { ++ pKarg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ pKarg->Configuration.uFailureCode = ++ CSMI_SAS_FAIL_CODE_CHANGE_COUNT_INVALID; ++ goto cim_get_raid_config_exit; ++ } ++ ++ if (!ioc->raid_data.pIocPg2) { ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ ++ /* ++ * Check to see if the input uRaidSetIndex is ++ * greater than the number of RAID sets ++ */ ++ if (pKarg->Configuration.uRaidSetIndex >= ++ ioc->raid_data.pIocPg2->NumActiveVolumes) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_RAID_SET_OUT_OF_RANGE; ++ goto cim_get_raid_config_exit; ++ } ++ ++ /* ++ * get RAID Volume Page 0 ++ */ ++ volumeID = ioc->raid_data.pIocPg2->RaidVolume[pKarg->Configuration.uRaidSetIndex].VolumeID; ++ VolumeBus = ioc->raid_data.pIocPg2->RaidVolume[pKarg->Configuration.uRaidSetIndex].VolumeBus; ++ ++ header.PageVersion = 0; ++ header.PageLength = 0; ++ header.PageNumber = 0; ++ header.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME; ++ cfg.cfghdr.hdr = &header; ++ cfg.physAddr = -1; ++ cfg.pageAddr = (VolumeBus << 8) + volumeID; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ if (mpt_config(ioc, &cfg) != 0) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ ++ if (header.PageLength == 0) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ ++ volumepage0sz = header.PageLength * 4; ++ pVolume0 = pci_alloc_consistent(ioc->pcidev, volumepage0sz, ++ &volume0_dma); ++ if (!pVolume0) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ cfg.physAddr = volume0_dma; ++ if (mpt_config(ioc, &cfg) != 0) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ ++ totalMaxLBA = (u64)le32_to_cpu(pVolume0->MaxLBA) | ++ ((u64)le32_to_cpu(pVolume0->MaxLBAHigh)) << 32; ++ tmpTotalMaxLBA = totalMaxLBA + 1; ++ do_div(tmpTotalMaxLBA, 2048); ++ pKarg->Configuration.bDriveCount = 0; ++ pKarg->Configuration.uCapacity = tmpTotalMaxLBA; ++ pKarg->Configuration.uStripeSize = ++ le32_to_cpu(pVolume0->StripeSize)/2; ++ ++ switch(pVolume0->VolumeType) { ++ case MPI_RAID_VOL_TYPE_IS: ++ pKarg->Configuration.bRaidType = CSMI_SAS_RAID_TYPE_0; ++ break; ++ case MPI_RAID_VOL_TYPE_IME: ++ pKarg->Configuration.bRaidType = CSMI_SAS_RAID_TYPE_10; ++ break; ++ case MPI_RAID_VOL_TYPE_IM: ++ pKarg->Configuration.bRaidType = CSMI_SAS_RAID_TYPE_1; ++ break; ++ default: ++ pKarg->Configuration.bRaidType = CSMI_SAS_RAID_TYPE_OTHER; ++ break; ++ } ++ ++ switch (pVolume0->VolumeStatus.State) { ++ case MPI_RAIDVOL0_STATUS_STATE_OPTIMAL: ++ pKarg->Configuration.bStatus = CSMI_SAS_RAID_SET_STATUS_OK; ++ break; ++ case MPI_RAIDVOL0_STATUS_STATE_DEGRADED: ++ /* Volume is degraded, check if Resyncing or Inactive */ ++ pKarg->Configuration.bStatus = CSMI_SAS_RAID_SET_STATUS_DEGRADED; ++ break; ++ case MPI_RAIDVOL0_STATUS_STATE_FAILED: ++ pKarg->Configuration.bStatus = CSMI_SAS_RAID_SET_STATUS_FAILED; ++ break; ++ } ++ ++ /* check flags */ ++ if (pVolume0->VolumeStatus.Flags & ++ MPI_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE) ++ pKarg->Configuration.bStatus = CSMI_SAS_RAID_SET_STATUS_OFFLINE; ++ else if (pVolume0->VolumeStatus.Flags & ++ MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) ++ pKarg->Configuration.bStatus = CSMI_SAS_RAID_SET_STATUS_REBUILDING; ++ ++ pKarg->Configuration.bInformation = 0; /* default */ ++ if(pVolume0->VolumeStatus.Flags & ++ MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS ) { ++ ++ uint64_t * ptrUint64; ++ uint64_t totalBlocks64, blocksRemaining64; ++ uint32_t totalBlocks32, blocksRemaining32; ++ ++ /* get percentage complete */ ++ pRaidActionReply = kmalloc( sizeof(MPI_RAID_VOL_INDICATOR) + ++ offsetof(MSG_RAID_ACTION_REPLY,ActionData), ++ GFP_KERNEL); ++ ++ if (!pRaidActionReply){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__,pKarg); ++ goto cim_get_raid_config_exit; ++ } ++ memset(pRaidActionReply, 0, sizeof(*pRaidActionReply)); ++ ++ csmisas_do_raid(ioc, ++ MPI_RAID_ACTION_INDICATOR_STRUCT, ++ 0, VolumeBus, volumeID, pRaidActionReply); ++ ++ ptrUint64 = (uint64_t *)&pRaidActionReply->ActionData; ++ totalBlocks64 = *ptrUint64; ++ ptrUint64++; ++ blocksRemaining64 = *ptrUint64; ++ while(totalBlocks64 > 0xFFFFFFFFUL){ ++ totalBlocks64 = totalBlocks64 >> 1; ++ blocksRemaining64 = blocksRemaining64 >> 1; ++ } ++ totalBlocks32 = (uint32_t)totalBlocks64; ++ blocksRemaining32 = (uint32_t)blocksRemaining64; ++ ++ if(totalBlocks32) ++ pKarg->Configuration.bInformation = ++ (totalBlocks32 - blocksRemaining32) / ++ (totalBlocks32 / 100); ++ ++ kfree(pRaidActionReply); ++ } ++ ++ /* fill-in more information depending on data type */ ++ if (pKarg->Configuration.bDataType == ++ CSMI_SAS_RAID_DATA_ADDITIONAL_DATA) { ++ pKarg->Configuration.Data->bLabel[0] = '\0'; ++ pKarg->Configuration.Data->bRaidSetLun[1] = 0; ++ pKarg->Configuration.Data->bWriteProtection = ++ CSMI_SAS_RAID_SET_WRITE_PROTECT_UNKNOWN; ++ pKarg->Configuration.Data->bCacheSetting = ++ CSMI_SAS_RAID_SET_CACHE_UNKNOWN; ++ pKarg->Configuration.Data->bCacheRatio = 0; ++ pKarg->Configuration.Data->usBlockSize = 512; ++ pKarg->Configuration.Data->ulRaidSetExtentOffset.uLowPart = 0; ++ pKarg->Configuration.Data->ulRaidSetExtentOffset.uHighPart = 0; ++ pKarg->Configuration.Data->ulRaidSetBlocks.uLowPart = ++ le32_to_cpu(pVolume0->MaxLBA); ++ pKarg->Configuration.Data->ulRaidSetBlocks.uHighPart = ++ le32_to_cpu(pVolume0->MaxLBAHigh); ++ if (pVolume0->VolumeType == MPI_RAID_VOL_TYPE_IS || ++ pVolume0->VolumeType == MPI_RAID_VOL_TYPE_IME ) { ++ pKarg->Configuration.Data->uStripeSizeInBlocks = ++ le32_to_cpu(pVolume0->StripeSize); ++ } else { ++ pKarg->Configuration.Data->uStripeSizeInBlocks = 0; ++ } ++ pKarg->Configuration.Data->uSectorsPerTrack = 128; ++ for (i=0; i<16; i++) { ++ // unsupported ++ pKarg->Configuration.Data->bApplicationScratchPad[i] = ++ 0xFF; ++ } ++ pKarg->Configuration.Data->uNumberOfHeads = 16; ++ ++ tmpTotalMaxLBA = totalMaxLBA; ++ do_div(tmpTotalMaxLBA, ++ (pKarg->Configuration.Data->uNumberOfHeads * ++ pKarg->Configuration.Data->uSectorsPerTrack)); ++ pKarg->Configuration.Data->uNumberOfTracks = tmpTotalMaxLBA; ++ } else if ( pKarg->Configuration.bDataType == ++ CSMI_SAS_RAID_DATA_DEVICE_ID ) { ++ /* Send inquiry to get VPD Page 0x83 */ ++ u32 vpd_page_sz; ++ vpd_page_sz = csmi_sas_raid_config_buffer_sz - ++ offsetof(CSMI_SAS_RAID_CONFIG,DeviceId); ++ if (csmisas_raid_inq(ioc, MPI_FUNCTION_SCSI_IO_REQUEST, ++ VolumeBus, volumeID, 0x83, ++ (u8*)&pKarg->Configuration.DeviceId->bDeviceIdentificationVPDPage, ++ vpd_page_sz) != 0) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ } else { ++ /* suppress drive information */ ++ if (pKarg->Configuration.bDriveCount == ++ CSMI_SAS_RAID_DRIVE_COUNT_SUPRESSED) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ goto cim_get_raid_config_exit; ++ } ++ } ++ ++ /* get hotspare info, used later in this function */ ++ if (pVolume0->VolumeSettings.HotSparePool) { ++ /* Read and save IOC Page 5 ++ */ ++ header.PageVersion = 0; ++ header.PageLength = 0; ++ header.PageNumber = 5; ++ header.PageType = MPI_CONFIG_PAGETYPE_IOC; ++ cfg.cfghdr.hdr = &header; ++ cfg.physAddr = -1; ++ cfg.pageAddr = 0; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ if ((mpt_config(ioc, &cfg) == 0) && (header.PageLength)) { ++ ioc_page5_sz = header.PageLength * 4; ++ pIocPage5 = pci_alloc_consistent(ioc->pcidev, ++ ioc_page5_sz, ++ &ioc_page5_dma); ++ memset(pIocPage5,0,ioc_page5_sz); ++ if (ioc_page5_dma) { ++ cfg.physAddr = ioc_page5_dma; ++ cfg.action = ++ MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ mpt_config(ioc, &cfg); ++ } ++ } ++ } ++ ++ /* ++ * get RAID Physical Disk Page 0 ++ */ ++ header.PageVersion = 0; ++ header.PageLength = 0; ++ header.PageNumber = 0; ++ header.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK; ++ cfg.cfghdr.hdr = &header; ++ cfg.physAddr = -1; ++ cfg.pageAddr = 0; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ if (mpt_config(ioc, &cfg) != 0) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ ++ if (header.PageLength == 0) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ ++ physdiskpage0sz = header.PageLength * 4; ++ pPhysDisk0 = pci_alloc_consistent(ioc->pcidev, physdiskpage0sz, ++ &physdisk0_dma); ++ if (!pPhysDisk0) { ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ cfg.physAddr = physdisk0_dma; ++ ++ physDiskNumMax = (csmi_sas_raid_config_buffer_sz - ++ offsetof(CSMI_SAS_RAID_CONFIG,Drives)) ++ / sizeof(CSMI_SAS_RAID_DRIVES); ++ ++ tmpTotalMaxLBA = totalMaxLBA; ++ if (pVolume0->VolumeType == MPI_RAID_VOL_TYPE_IS) { ++ do_div(tmpTotalMaxLBA, pVolume0->NumPhysDisks); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "IS Volume tmpTotalMaxLBA=%llX\n", ++ (unsigned long long)tmpTotalMaxLBA)); ++ } ++ else if (pVolume0->VolumeType == MPI_RAID_VOL_TYPE_IME) { ++ do_div(tmpTotalMaxLBA, pVolume0->NumPhysDisks * 2); ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "IME Volume tmpTotalMaxLBA=%llX\n", ++ (unsigned long long)tmpTotalMaxLBA)); ++ } else { ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "IM Volume tmpTotalMaxLBA=%llX\n", ++ (unsigned long long)tmpTotalMaxLBA)); ++ } ++ ++ for (i=0; i< min(pVolume0->NumPhysDisks, physDiskNumMax); i++) { ++ ++ physDiskNum = pVolume0->PhysDisk[i].PhysDiskNum; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ cfg.pageAddr = physDiskNum; ++ if (mpt_config(ioc, &cfg) != 0){ ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_raid_config_exit; ++ } ++ ++ pKarg->Configuration.bDriveCount++; ++ if (pKarg->Configuration.bDataType != CSMI_SAS_RAID_DATA_DRIVES) ++ continue; ++ ++ /* Search the list for the matching SAS address. */ ++ sas_info = csmisas_get_device_component_by_fw(ioc, pPhysDisk0->PhysDiskBus, ++ pPhysDisk0->PhysDiskID); ++ if (sas_info) { ++ sas_address = reverse_byte_order64(sas_info->sas_address); ++ memcpy(pKarg->Configuration.Drives[i].bSASAddress, ++ &sas_address,sizeof(u64)); ++ if (!device_info) ++ device_info = sas_info->device_info; ++ } ++ ++ memcpy(pKarg->Configuration.Drives[i].bModel, ++ pPhysDisk0->InquiryData.VendorID, ++ offsetof(RAID_PHYS_DISK0_INQUIRY_DATA,ProductRevLevel)); ++ memcpy(pKarg->Configuration.Drives[i].bFirmware, ++ pPhysDisk0->InquiryData.ProductRevLevel, ++ sizeof(pPhysDisk0->InquiryData.ProductRevLevel)); ++ if (csmisas_is_sata(pPhysDisk0)) { ++ memcpy(&pKarg->Configuration.Drives[i].bSerialNumber, ++ &pPhysDisk0->ExtDiskIdentifier[4], ++ 4); ++ memcpy(&pKarg->Configuration.Drives[i].bSerialNumber[4], ++ &pPhysDisk0->DiskIdentifier, ++ sizeof(pPhysDisk0->DiskIdentifier)); ++ } else { ++ memcpy(pKarg->Configuration.Drives[i].bSerialNumber, ++ pPhysDisk0->DiskIdentifier, ++ sizeof(pPhysDisk0->DiskIdentifier)); ++ } ++ ++ pKarg->Configuration.Drives[i].bDriveUsage = ++ (pPhysDisk0->PhysDiskStatus.Flags & ++ MPI_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME) ? ++ CSMI_SAS_DRIVE_CONFIG_NOT_USED : ++ CSMI_SAS_DRIVE_CONFIG_MEMBER; ++ ++ pKarg->Configuration.Drives[i].bDriveStatus = ++ CSMI_SAS_DRIVE_STATUS_OK; ++ if (pPhysDisk0->PhysDiskStatus.State == ++ MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED) { ++ pKarg->Configuration.Drives[i].bDriveStatus = ++ CSMI_SAS_DRIVE_STATUS_OFFLINE; ++ } else if(pPhysDisk0->PhysDiskStatus.State) { ++ pKarg->Configuration.Drives[i].bDriveStatus = ++ CSMI_SAS_DRIVE_STATUS_FAILED; ++ if(pKarg->Configuration.bStatus == ++ CSMI_SAS_RAID_SET_STATUS_DEGRADED) ++ pKarg->Configuration.bInformation = i; ++ } else if((pVolume0->VolumeStatus.Flags & ++ MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) && ++ (pPhysDisk0->PhysDiskStatus.Flags & ++ MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC)) ++ pKarg->Configuration.Drives[i].bDriveStatus = ++ CSMI_SAS_DRIVE_STATUS_REBUILDING; ++ else if(pPhysDisk0->ErrorData.SmartCount || ++ (pPhysDisk0->PhysDiskStatus.Flags & ++ MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC)) ++ pKarg->Configuration.Drives[i].bDriveStatus = ++ CSMI_SAS_DRIVE_STATUS_DEGRADED; ++ ++ memset(pKarg->Configuration.Drives[i].bSASLun, ++ 0, sizeof(pKarg->Configuration.Drives[i].bSASLun)); ++ if (csmisas_is_sata(pPhysDisk0)) { ++ pKarg->Configuration.Drives[i].bDriveType = ++ CSMI_SAS_DRIVE_TYPE_SATA; ++ } else { /* drive in a volume can only be SAS/SATA */ ++ pKarg->Configuration.Drives[i].bDriveType = ++ CSMI_SAS_DRIVE_TYPE_SINGLE_PORT_SAS; ++ if (mpt_raid_phys_disk_get_num_paths(ioc, ++ pVolume0->PhysDisk[i].PhysDiskNum) > 1) ++ pKarg->Configuration.Drives[i].bDriveType = ++ CSMI_SAS_DRIVE_TYPE_DUAL_PORT_SAS; ++ } ++ ++ pKarg->Configuration.Drives[i].usBlockSize = 512; ++ pKarg->Configuration.Drives[i].uDriveIndex = ++ pPhysDisk0->PhysDiskNum; ++ pKarg->Configuration.Drives[i].ulTotalUserBlocks.uLowPart = ++ (u32)tmpTotalMaxLBA; ++ pKarg->Configuration.Drives[i].ulTotalUserBlocks.uHighPart = ++ (u32)(tmpTotalMaxLBA >> 32); ++ } ++ ++ /* adding hot spare info at the end */ ++ if ((pVolume0->VolumeSettings.HotSparePool) && (pIocPage5) && ++ (pVolume0->VolumeType != MPI_RAID_VOL_TYPE_IS)) { ++ for (idx = 0, i = pVolume0->NumPhysDisks ; ++ idx < pIocPage5->NumHotSpares ; idx++) { ++ if (i >= physDiskNumMax) ++ break; ++ if ((pVolume0->VolumeSettings.HotSparePool & ++ pIocPage5->HotSpare[idx].HotSparePool) == 0) ++ continue; ++ if(pIocPage5->HotSpare[idx].Flags != ++ MPI_IOC_PAGE_5_HOT_SPARE_ACTIVE) ++ continue; ++ physDiskNum = pIocPage5->HotSpare[idx].PhysDiskNum; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ cfg.pageAddr = physDiskNum; ++ if (mpt_config(ioc, &cfg) != 0) ++ continue; ++ ++ /* don't mix SSP hot spare ++ * in SATA volume ++ */ ++ if (!csmisas_is_sata(pPhysDisk0) && ++ (device_info & ++ MPI_SAS_DEVICE_INFO_SATA_DEVICE)) ++ continue; ++ ++ /* don't mix SATA hot spare ++ * in SSP volume ++ */ ++ if (csmisas_is_sata(pPhysDisk0) && ++ (device_info & ++ MPI_SAS_DEVICE_INFO_SSP_TARGET)) ++ continue; ++ ++ /* capacity check for IM volumes*/ ++ if ((pVolume0->VolumeType == ++ MPI_RAID_VOL_TYPE_IM) && ++ (totalMaxLBA + ++ (64*2*1024) /* metadata = 64MB*/ > ++ le32_to_cpu(pPhysDisk0->MaxLBA))) ++ continue; ++ ++ tmpTotalMaxLBA = totalMaxLBA; ++ do_div(tmpTotalMaxLBA, pVolume0->NumPhysDisks); ++ /* capacity check for IME volumes*/ ++ if ((pVolume0->VolumeType == ++ MPI_RAID_VOL_TYPE_IME) && ++ (((totalMaxLBA + ++ pVolume0->NumPhysDisks) * 2) + ++ (64*2*1024 ) /*metadata = 64MB*/ > ++ le32_to_cpu(pPhysDisk0->MaxLBA))) ++ continue; ++ ++ pKarg->Configuration.bDriveCount++; ++ if (pKarg->Configuration.bDataType != ++ CSMI_SAS_RAID_DATA_DRIVES) { ++ i++; ++ continue; ++ } ++ ++ /* Search the list for the matching SAS address. */ ++ sas_info = csmisas_get_device_component_by_fw(ioc, ++ pPhysDisk0->PhysDiskBus, pPhysDisk0->PhysDiskID); ++ if (sas_info) { ++ sas_address = reverse_byte_order64(sas_info->sas_address); ++ memcpy(pKarg->Configuration.Drives[i].bSASAddress, ++ &sas_address,sizeof(u64)); ++ } ++ ++ memcpy(pKarg->Configuration.Drives[i].bModel, ++ pPhysDisk0->InquiryData.VendorID, ++ offsetof(RAID_PHYS_DISK0_INQUIRY_DATA,ProductRevLevel)); ++ memcpy(pKarg->Configuration.Drives[i].bFirmware, ++ pPhysDisk0->InquiryData.ProductRevLevel, ++ sizeof(pPhysDisk0->InquiryData.ProductRevLevel)); ++ if (csmisas_is_sata(pPhysDisk0)) { ++ memcpy(&pKarg->Configuration.Drives[i].bSerialNumber, ++ &pPhysDisk0->ExtDiskIdentifier[4], ++ 4); ++ memcpy(&pKarg->Configuration.Drives[i].bSerialNumber[4], ++ &pPhysDisk0->DiskIdentifier, ++ sizeof(pPhysDisk0->DiskIdentifier)); ++ } else { ++ memcpy(pKarg->Configuration.Drives[i].bSerialNumber, ++ pPhysDisk0->DiskIdentifier, ++ sizeof(pPhysDisk0->DiskIdentifier)); ++ } ++ pKarg->Configuration.Drives[i].bDriveStatus = ++ CSMI_SAS_DRIVE_STATUS_OK; ++ if(pPhysDisk0->PhysDiskStatus.State) ++ pKarg->Configuration.Drives[i].bDriveStatus = ++ CSMI_SAS_DRIVE_STATUS_FAILED; ++ else if(pPhysDisk0->ErrorData.SmartCount) ++ pKarg->Configuration.Drives[i].bDriveStatus = ++ CSMI_SAS_DRIVE_STATUS_DEGRADED; ++ pKarg->Configuration.Drives[i].bDriveUsage = ++ CSMI_SAS_DRIVE_CONFIG_SPARE; ++ pKarg->Configuration.Drives[i].usBlockSize = 512; ++ pKarg->Configuration.Drives[i].uDriveIndex = ++ pPhysDisk0->PhysDiskNum; ++ if (csmisas_is_sata(pPhysDisk0)) { ++ pKarg->Configuration.Drives[i].bDriveType = ++ CSMI_SAS_DRIVE_TYPE_SATA; ++ } else { /* drive in a volume can only be SAS/SATA */ ++ pKarg->Configuration.Drives[i].bDriveType = ++ CSMI_SAS_DRIVE_TYPE_SINGLE_PORT_SAS; ++ if (mpt_raid_phys_disk_get_num_paths(ioc, ++ pVolume0->PhysDisk[i].PhysDiskNum) > 1) ++ pKarg->Configuration.Drives[i].bDriveType = ++ CSMI_SAS_DRIVE_TYPE_DUAL_PORT_SAS; ++ } ++ i++; ++ } ++ } ++ ++ // Only return data on the first 240 drives ++ if( pKarg->Configuration.bDriveCount > 0xF0 ) ++ pKarg->Configuration.bDriveCount = ++ CSMI_SAS_RAID_DRIVE_COUNT_TOO_BIG; ++ ++ pKarg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ cim_get_raid_config_exit: ++ ++ if (pVolume0 != NULL) ++ pci_free_consistent(ioc->pcidev, volumepage0sz, pVolume0, ++ volume0_dma); ++ ++ if(pPhysDisk0 != NULL) ++ pci_free_consistent(ioc->pcidev, physdiskpage0sz, pPhysDisk0, ++ physdisk0_dma); ++ ++ if(pIocPage5 != NULL) ++ pci_free_consistent(ioc->pcidev, ioc_page5_sz, pIocPage5, ++ ioc_page5_dma); ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ ++ /* find the buffer size to copy depending on how much is filled-in */ ++ switch (pKarg->Configuration.bDataType) { ++ case CSMI_SAS_RAID_DATA_ADDITIONAL_DATA: ++ copy_buffer_sz = sizeof(IOCTL_HEADER) + ++ offsetof(CSMI_SAS_RAID_CONFIG,Data) + ++ sizeof(CSMI_SAS_RAID_SET_ADDITIONAL_DATA); ++ break; ++ case CSMI_SAS_RAID_DATA_DRIVES: ++ if (pKarg->Configuration.bDriveCount == ++ CSMI_SAS_RAID_DRIVE_COUNT_SUPRESSED) ++ copy_buffer_sz = sizeof(IOCTL_HEADER) + ++ offsetof(CSMI_SAS_RAID_CONFIG,Drives); ++ else ++ copy_buffer_sz = sizeof(IOCTL_HEADER) + ++ offsetof(CSMI_SAS_RAID_CONFIG,Drives) + ++ (pKarg->Configuration.bDriveCount * ++ sizeof(CSMI_SAS_RAID_DRIVES)); ++ break; ++ case CSMI_SAS_RAID_DATA_DEVICE_ID: ++ copy_buffer_sz = csmi_sas_raid_config_buffer_sz; ++ break; ++ } ++ ++ if (copy_to_user(uarg, pKarg, copy_buffer_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_get_raid_config @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Get RAID Features command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_raid_features(unsigned long arg) ++{ ++ CSMI_SAS_RAID_FEATURES_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_RAID_FEATURES_BUFFER karg, *pKarg=NULL; ++ int csmi_sas_raid_features_buffer_sz, iocnum; ++ int memory_pages; ++ MPT_ADAPTER *ioc = NULL; ++ ++ if (copy_from_user(&karg, uarg, sizeof(IOCTL_HEADER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_get_raid_features struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ csmi_sas_raid_features_buffer_sz = karg.IoctlHeader.Length; ++ memory_pages = get_order(csmi_sas_raid_features_buffer_sz); ++ pKarg = (CSMI_SAS_RAID_FEATURES_BUFFER *)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!pKarg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc RAID_FEATURES_BUFFER " ++ "csmi_sas_raid_features_buffer_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ csmi_sas_raid_features_buffer_sz, memory_pages); ++ return -ENOMEM; ++ } ++ memset(pKarg, 0, sizeof(*pKarg)); ++ ++ if (copy_from_user(pKarg, uarg, csmi_sas_raid_features_buffer_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_get_raid_features struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(pKarg->IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ if (pKarg->Information.uChangeCount != 0 && ++ pKarg->Information.uChangeCount != ioc->csmi_change_count ) { ++ pKarg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ pKarg->Information.uFailureCode = ++ CSMI_SAS_FAIL_CODE_CHANGE_COUNT_INVALID; ++ goto cim_get_raid_features_exit; ++ } ++ ++ pKarg->Information.uFeatures = CSMI_SAS_RAID_FEATURE_REBUILD | ++ CSMI_SAS_RAID_FEATURE_SURFACE_SCAN | ++ CSMI_SAS_RAID_FEATURE_SPARES_SHARED; ++ pKarg->Information.bDefaultTransformPriority = ++ CSMI_SAS_PRIORITY_UNKNOWN; ++ pKarg->Information.bTransformPriority = CSMI_SAS_PRIORITY_UNKNOWN; ++ pKarg->Information.bDefaultRebuildPriority = CSMI_SAS_PRIORITY_UNKNOWN; ++ pKarg->Information.bRebuildPriority = ++ pKarg->Information.bDefaultRebuildPriority; ++ pKarg->Information.bDefaultSurfaceScanPriority = ++ CSMI_SAS_PRIORITY_UNKNOWN; ++ pKarg->Information.bSurfaceScanPriority = CSMI_SAS_PRIORITY_UNKNOWN; ++ pKarg->Information.uRaidSetTransformationRules = 0; ++ ++ /* IS */ ++ pKarg->Information.RaidType[0].bRaidType = CSMI_SAS_RAID_TYPE_0; ++ pKarg->Information.RaidType[0].uSupportedStripeSizeMap = 0x80; ++ ++ /* IM */ ++ pKarg->Information.RaidType[1].bRaidType = CSMI_SAS_RAID_TYPE_1; ++ pKarg->Information.RaidType[1].uSupportedStripeSizeMap = 0; ++ ++ /* IME */ ++ pKarg->Information.RaidType[2].bRaidType = CSMI_SAS_RAID_TYPE_1E; ++ pKarg->Information.RaidType[2].uSupportedStripeSizeMap = 0x80; ++ ++ pKarg->Information.RaidType[3].bRaidType = CSMI_SAS_RAID_TYPE_END; ++ pKarg->Information.bCacheRatiosSupported[0] = ++ CSMI_SAS_RAID_CACHE_RATIO_END; ++ ++ cim_get_raid_features_exit: ++ ++ /* ++ * Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, pKarg, ++ sizeof(CSMI_SAS_RAID_FEATURES_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_get_raid_features @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Set RAID Control command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_set_raid_control(unsigned long arg) ++{ ++ CSMI_SAS_RAID_CONTROL_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_RAID_CONTROL_BUFFER karg, *pKarg=NULL; ++ int csmi_sas_raid_control_buffer_sz, iocnum; ++ int memory_pages; ++ MPT_ADAPTER *ioc = NULL; ++ ++ if (copy_from_user(&karg, uarg, sizeof(IOCTL_HEADER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_set_raid_control struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ csmi_sas_raid_control_buffer_sz = karg.IoctlHeader.Length; ++ memory_pages = get_order(csmi_sas_raid_control_buffer_sz); ++ pKarg = (CSMI_SAS_RAID_CONTROL_BUFFER *)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!pKarg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc RAID_CONTROL_BUFFER " ++ "csmi_sas_raid_control_buffer_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ csmi_sas_raid_control_buffer_sz, memory_pages); ++ return -ENOMEM; ++ } ++ memset(pKarg, 0, sizeof(*pKarg)); ++ ++ if (copy_from_user(pKarg, uarg, csmi_sas_raid_control_buffer_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_set_raid_control struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(pKarg->IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ if (pKarg->Information.uChangeCount != 0 && ++ pKarg->Information.uChangeCount != ioc->csmi_change_count ) { ++ pKarg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ pKarg->Information.uFailureCode = ++ CSMI_SAS_FAIL_CODE_CHANGE_COUNT_INVALID; ++ goto cim_set_raid_control_exit; ++ } ++ ++ if (pKarg->Information.bTransformPriority != ++ CSMI_SAS_PRIORITY_UNCHANGED) { ++ pKarg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ pKarg->Information.uFailureCode = ++ CSMI_SAS_FAIL_CODE_TRANSFORM_PRIORITY_INVALID; ++ goto cim_set_raid_control_exit; ++ } ++ ++ if (pKarg->Information.bRebuildPriority != ++ CSMI_SAS_PRIORITY_AUTO && ++ pKarg->Information.bRebuildPriority != ++ CSMI_SAS_PRIORITY_UNCHANGED) { ++ pKarg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ pKarg->Information.uFailureCode = ++ CSMI_SAS_FAIL_CODE_REBUILD_PRIORITY_INVALID; ++ goto cim_set_raid_control_exit; ++ } ++ ++ if (pKarg->Information.bCacheRatioFlag == ++ CSMI_SAS_RAID_CACHE_RATIO_DISABLE) { ++ pKarg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ pKarg->Information.uFailureCode = ++ CSMI_SAS_FAIL_CODE_CACHE_RATIO_INVALID; ++ goto cim_set_raid_control_exit; ++ } ++ ++ if( !strcmp(pKarg->Information.bClearConfiguration, ++ CSMI_SAS_RAID_CLEAR_CONFIGURATION_SIGNATURE) ) { ++ pKarg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ pKarg->Information.uFailureCode = ++ CSMI_SAS_FAIL_CODE_CLEAR_CONFIGURATION_INVALID; ++ goto cim_set_raid_control_exit; ++ } ++ ++ pKarg->Information.bFailureDescription[0] = '\0'; ++ ++ cim_set_raid_control_exit: ++ ++ /* ++ * Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, pKarg, ++ sizeof(CSMI_SAS_RAID_CONTROL_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_set_raid_control @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ free_pages((unsigned long)pKarg, memory_pages); ++ return 0; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Get Raid Element. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_raid_element(unsigned long arg) ++{ ++ CSMI_SAS_RAID_ELEMENT_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_RAID_ELEMENT_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ ++ if (copy_from_user(&karg, uarg, sizeof(CSMI_SAS_RAID_ELEMENT_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmisas_get_raid_element struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++/* TODO - implement IOCTL here */ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_BAD_CNTL_CODE; ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": not implemented\n")); ++ ++// csmisas_get_raid_element_exit: ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_RAID_ELEMENT_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmisas_get_raid_element @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++ ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Set Raid Operation ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_set_raid_operation(unsigned long arg) ++{ ++ CSMI_SAS_RAID_SET_OPERATION_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_RAID_SET_OPERATION_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ ++ if (copy_from_user(&karg, uarg, sizeof(CSMI_SAS_RAID_SET_OPERATION_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_set_raid_operation struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++/* TODO - implement IOCTL here */ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_BAD_CNTL_CODE; ++ dcsmisasprintk(ioc, printk(KERN_DEBUG ": not implemented\n")); ++ ++// cim_set_raid_operation: ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_RAID_SET_OPERATION_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_set_raid_operation @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++ ++} ++ ++ ++/** ++ * Prototype Routine for the CSMI SAS Task Managment Config command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_task_managment(unsigned long arg) ++{ ++ CSMI_SAS_SSP_TASK_IU_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_SSP_TASK_IU_BUFFER karg; ++ pSCSITaskMgmt_t pScsiTm; ++ pSCSITaskMgmtReply_t pScsiTmReply; ++ MPT_ADAPTER *ioc = NULL; ++ MPT_SCSI_HOST *hd; ++ MPT_FRAME_HDR *mf = NULL; ++ MPIHeader_t *mpi_hdr; ++ int iocnum; ++ u8 taskType; ++ u8 channel; ++ u8 id; ++ u8 queueTag; ++ u32 TaskMsgContext = 0; ++ int i; ++ u8 found_qtag; ++ struct sas_device_info *sas_info; ++ u16 ioc_status; ++ u32 MsgContext; ++ ++ if (copy_from_user(&karg, uarg, sizeof(CSMI_SAS_SSP_TASK_IU_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_task_managment struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_INVALID_PARAMETER; ++ ++ sas_info = csmisas_get_device_component_by_os(ioc, ++ karg.Parameters.bPathId, karg.Parameters.bTargetId); ++ if (!sas_info || sas_info->is_cached || sas_info->is_logical_volume) ++ goto cim_get_task_managment_exit; ++ ++ channel = sas_info->fw.channel; ++ id = sas_info->fw.id; ++ queueTag = (u8)karg.Parameters.uQueueTag & 0xFF; ++ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; ++ ++ /* try to catch an error ++ */ ++ if ((karg.Parameters.uFlags & CSMI_SAS_TASK_IU) && ++ (karg.Parameters.uFlags & CSMI_SAS_HARD_RESET_SEQUENCE)) ++ goto cim_get_task_managment_exit; ++ ++ if (karg.Parameters.uFlags & CSMI_SAS_TASK_IU) { ++ switch (karg.Parameters.bTaskManagementFunction) { ++ ++ case CSMI_SAS_SSP_ABORT_TASK: ++ taskType = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK; ++ break; ++ case CSMI_SAS_SSP_ABORT_TASK_SET: ++ taskType = MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET; ++ break; ++ case CSMI_SAS_SSP_CLEAR_TASK_SET: ++ taskType = MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET; ++ break; ++ case CSMI_SAS_SSP_LOGICAL_UNIT_RESET: ++ taskType = MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET; ++ break; ++ case CSMI_SAS_SSP_CLEAR_ACA: ++ case CSMI_SAS_SSP_QUERY_TASK: ++ default: ++ goto cim_get_task_managment_exit; ++ } ++ } else if (karg.Parameters.uFlags & CSMI_SAS_HARD_RESET_SEQUENCE) ++ taskType = MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET; ++ else ++ goto cim_get_task_managment_exit; ++ ++ switch (karg.Parameters.uInformation) { ++ case CSMI_SAS_SSP_TEST: ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "TM request for test purposes\n")); ++ break; ++ case CSMI_SAS_SSP_EXCEEDED: ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "TM request due to timeout\n")); ++ break; ++ case CSMI_SAS_SSP_DEMAND: ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "TM request demanded by app\n")); ++ break; ++ case CSMI_SAS_SSP_TRIGGER: ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "TM request sent to trigger event\n")); ++ break; ++ } ++ ++ switch (taskType) { ++ ++ case MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK: ++ /* ++ * look up qtag in the ScsiLookup[] table ++ */ ++ for (i = 0, found_qtag = 0; i < hd->ioc->req_depth; i++) { ++ if ((ioc->ScsiLookup[i]) && ++ (ioc->ScsiLookup[i]->tag == queueTag)) { ++ mf = MPT_INDEX_2_MFPTR(hd->ioc, i); ++ TaskMsgContext = ++ mf->u.frame.hwhdr.msgctxu.MsgContext; ++ found_qtag=1; ++ break; ++ } ++ } ++ ++ if(!found_qtag) ++ goto cim_get_task_managment_exit; ++ ++ case MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: ++ case MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: ++ case MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET: ++ /* for now, this should work ++ */ ++ case MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET: ++ ++ /* Single threading .... ++ */ ++ mutex_lock(&ioc->taskmgmt_cmds.mutex); ++ if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) { ++ mutex_unlock(&ioc->taskmgmt_cmds.mutex); ++ karg.IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_FAILED; ++ goto cim_get_task_managment_exit; ++ } ++ /* Send request ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_taskmgmt_id, ioc)) == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": no msg frames!\n")); ++ mutex_unlock(&ioc->taskmgmt_cmds.mutex); ++ mpt_clear_taskmgmt_in_progress_flag(ioc); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_task_managment_exit; ++ } ++ ++ mpi_hdr = (MPIHeader_t *) mf; ++ MsgContext = mpi_hdr->MsgContext; ++ pScsiTm = (pSCSITaskMgmt_t ) mf; ++ ++ memset(pScsiTm,0,sizeof(SCSITaskMgmt_t)); ++ pScsiTm->TaskType = taskType; ++ pScsiTm->Bus = channel; ++ pScsiTm->TargetID = id; ++ int_to_scsilun(karg.Parameters.bLun, ++ (struct scsi_lun *)pScsiTm->LUN); ++ pScsiTm->MsgContext = MsgContext; ++ pScsiTm->TaskMsgContext = TaskMsgContext; ++ pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; ++ ++ if (csmisas_send_handshake_wait(ioc, mf, ++ karg.IoctlHeader.Timeout) != 0) { ++ mutex_unlock(&ioc->taskmgmt_cmds.mutex); ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_get_task_managment_exit; ++ } ++ ++ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) { ++ ++ pScsiTmReply = ++ (pSCSITaskMgmtReply_t ) ioc->ioctl_cmds.reply; ++ ++ ioc_status = le16_to_cpu(pScsiTmReply->IOCStatus) ++ & MPI_IOCSTATUS_MASK; ++ ++ memset(&karg.Status,0, ++ sizeof(CSMI_SAS_SSP_PASSTHRU_STATUS)); ++ ++ if(ioc_status == MPI_IOCSTATUS_SUCCESS) { ++ karg.IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_SUCCESS; ++ karg.Status.bSSPStatus = ++ CSMI_SAS_SSP_STATUS_COMPLETED; ++ }else if(ioc_status == MPI_IOCSTATUS_INSUFFICIENT_RESOURCES) { ++ karg.IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_SUCCESS; ++ karg.Status.bSSPStatus = ++ CSMI_SAS_SSP_STATUS_RETRY; ++ }else { ++ karg.IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_FAILED; ++ karg.Status.bSSPStatus = ++ CSMI_SAS_SSP_STATUS_FATAL_ERROR; ++ } ++ } else ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ ++ break; ++ ++ default: ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_INVALID_PARAMETER; ++ break; ++ } ++ ++ mutex_unlock(&ioc->taskmgmt_cmds.mutex); ++ ++ cim_get_task_managment_exit: ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_SSP_TASK_IU_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_task_managment @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * map_sas_status_to_csmi - Conversion for Connection Status ++ * @mpi_sas_status: Sas status returned by the firmware ++ * ++ * Returns converted connection status ++ * ++ **/ ++static u8 ++map_sas_status_to_csmi(u8 mpi_sas_status) ++{ ++ u8 csmi_connect_status; ++ ++ switch (mpi_sas_status) { ++ ++ case MPI_SASSTATUS_SUCCESS: ++ csmi_connect_status = CSMI_SAS_OPEN_ACCEPT; ++ break; ++ ++ case MPI_SASSTATUS_UTC_BAD_DEST: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_BAD_DESTINATION; ++ break; ++ ++ case MPI_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_RATE_NOT_SUPPORTED; ++ break; ++ ++ case MPI_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED: ++ csmi_connect_status = ++ CSMI_SAS_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED; ++ break; ++ ++ case MPI_SASSTATUS_UTC_STP_RESOURCES_BUSY: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_STP_RESOURCES_BUSY; ++ break; ++ ++ case MPI_SASSTATUS_UTC_WRONG_DESTINATION: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_WRONG_DESTINATION; ++ break; ++ ++ case MPI_SASSTATUS_SDSF_NAK_RECEIVED: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_RETRY; ++ break; ++ ++ case MPI_SASSTATUS_SDSF_CONNECTION_FAILED: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_PATHWAY_BLOCKED; ++ break; ++ ++ case MPI_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_NO_DESTINATION; ++ break; ++ ++ case MPI_SASSTATUS_UNKNOWN_ERROR: ++ case MPI_SASSTATUS_INVALID_FRAME: ++ case MPI_SASSTATUS_UTC_BREAK_RECEIVED: ++ case MPI_SASSTATUS_UTC_PORT_LAYER_REQUEST: ++ case MPI_SASSTATUS_SHORT_INFORMATION_UNIT: ++ case MPI_SASSTATUS_LONG_INFORMATION_UNIT: ++ case MPI_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA: ++ case MPI_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR: ++ case MPI_SASSTATUS_XFER_RDY_NOT_EXPECTED: ++ case MPI_SASSTATUS_DATA_INCORRECT_DATA_LENGTH: ++ case MPI_SASSTATUS_DATA_TOO_MUCH_READ_DATA: ++ case MPI_SASSTATUS_DATA_OFFSET_ERROR: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_RESERVE_STOP; ++ break; ++ ++ default: ++ csmi_connect_status = CSMI_SAS_OPEN_REJECT_RESERVE_STOP; ++ break; ++ } ++ ++ return csmi_connect_status; ++} ++ ++/** ++ * csmisas_phy_reset ++ * Issues a phy link reset or phy hard reset ++ * ++ * @ioc - Pointer to MPT_ADAPTER structure ++ * @PhyNum - phy number ++ * @opcode - {MPI_SAS_OP_PHY_LINK_RESET,MPI_SAS_OP_PHY_HARD_RESET} ++ * ++ * Returns: 0 for success, non-zero error ++ **/ ++static int ++csmisas_phy_reset(MPT_ADAPTER *ioc, u8 PhyNum, u8 opcode) ++{ ++ SasIoUnitControlRequest_t *sasIoUnitCntrReq; ++ SasIoUnitControlReply_t *sasIoUnitCntrReply; ++ MPT_FRAME_HDR *mf = NULL; ++ MPIHeader_t *mpi_hdr; ++ u16 ioc_status; ++ u32 MsgContext; ++ ++ if ((opcode != MPI_SAS_OP_PHY_LINK_RESET) && ++ (opcode != MPI_SAS_OP_PHY_HARD_RESET)) ++ return -1; ++ ++ /* Get a MF for this command. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": no msg frames!\n")); ++ return -1; ++ } ++ ++ mpi_hdr = (MPIHeader_t *) mf; ++ MsgContext = mpi_hdr->MsgContext; ++ sasIoUnitCntrReq = (SasIoUnitControlRequest_t *)mf; ++ memset(sasIoUnitCntrReq,0,sizeof(SasIoUnitControlRequest_t)); ++ sasIoUnitCntrReq->Function = MPI_FUNCTION_SAS_IO_UNIT_CONTROL; ++ sasIoUnitCntrReq->MsgContext = MsgContext; ++ sasIoUnitCntrReq->Operation = opcode; ++ sasIoUnitCntrReq->PhyNum = PhyNum; ++ ++ if (csmisas_send_command_wait(ioc, mf, MPT_IOCTL_DEFAULT_TIMEOUT) != 0) ++ return -1; ++ ++ if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) == 0) ++ return -1; ++ ++ /* process the completed Reply Message Frame */ ++ sasIoUnitCntrReply = (SasIoUnitControlReply_t *)ioc->ioctl_cmds.reply; ++ ioc_status = le16_to_cpu(sasIoUnitCntrReply->IOCStatus) ++ & MPI_IOCSTATUS_MASK; ++ if (ioc_status != MPI_IOCSTATUS_SUCCESS) { ++ printk(KERN_DEBUG "%s: IOCStatus=0x%X IOCLogInfo=0x%X\n", ++ __FUNCTION__, ++ sasIoUnitCntrReply->IOCStatus, ++ sasIoUnitCntrReply->IOCLogInfo); ++ return -1; ++ } ++ return 0; ++} ++ ++/** Prototype Routine for the CSMI SAS Phy Control command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_phy_control(unsigned long arg) ++{ ++ CSMI_SAS_PHY_CONTROL_BUFFER __user *uarg = (void __user *) arg; ++ IOCTL_HEADER ioctl_header; ++ PCSMI_SAS_PHY_CONTROL_BUFFER karg; ++ SasIOUnitPage0_t *sasIoUnitPg0=NULL; ++ dma_addr_t sasIoUnitPg0_dma; ++ int sasIoUnitPg0_data_sz=0; ++ SasIOUnitPage1_t *sasIoUnitPg1=NULL; ++ dma_addr_t sasIoUnitPg1_dma; ++ int sasIoUnitPg1_data_sz=0; ++ ConfigExtendedPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum; ++ int csmi_sas_phy_control_buffer_sz; ++ int memory_pages; ++ ++ if (copy_from_user(&ioctl_header, uarg, sizeof(IOCTL_HEADER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in IOCTL_HEADER" ++ "struct @ %p\n", __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ csmi_sas_phy_control_buffer_sz = ioctl_header.Length; ++ memory_pages = get_order(csmi_sas_phy_control_buffer_sz); ++ karg = (PCSMI_SAS_PHY_CONTROL_BUFFER)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!karg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc SAS_PHY_CONTROL_BUFFER " ++ "csmi_sas_phy_control_buffer_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ csmi_sas_phy_control_buffer_sz, memory_pages); ++ return -ENOMEM; ++ } ++ memset(karg, 0, sizeof(*karg)); ++ ++ if (copy_from_user(karg, uarg, csmi_sas_phy_control_buffer_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_phy_control_buffer " ++ "struct @ %p\n", __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(ioctl_header.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ if (karg->bPhyIdentifier >= ioc->num_ports) { ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ /* ++ * Retreive SAS IOUNIT PAGE 0 ++ */ ++ ++ hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 0; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.pageAddr = 0; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if (mpt_config(ioc, &cfg) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: READ MPI_SASIOUNITPAGE0: HEADER\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": hdr.ExtPageLength == 0\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ sasIoUnitPg0_data_sz = hdr.ExtPageLength * 4; ++ sasIoUnitPg0 = (SasIOUnitPage0_t *) pci_alloc_consistent(ioc->pcidev, ++ sasIoUnitPg0_data_sz, &sasIoUnitPg0_dma); ++ ++ if (!sasIoUnitPg0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ memset((u8 *)sasIoUnitPg0, 0, sasIoUnitPg0_data_sz); ++ cfg.physAddr = sasIoUnitPg0_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if (mpt_config(ioc, &cfg) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: READ MPI_SASIOUNITPAGE0: CURRENT\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ /* ++ * Retreive SAS IOUNIT PAGE 1 ++ */ ++ ++ hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 1; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.pageAddr = 0; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if (mpt_config(ioc, &cfg) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: READ MPI_SASIOUNITPAGE1: HEADER\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": hdr.ExtPageLength == 0\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ sasIoUnitPg1_data_sz = hdr.ExtPageLength * 4; ++ sasIoUnitPg1 = (SasIOUnitPage1_t *) pci_alloc_consistent(ioc->pcidev, ++ sasIoUnitPg1_data_sz, &sasIoUnitPg1_dma); ++ ++ if (!sasIoUnitPg1) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ": pci_alloc_consistent: FAILED\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ memset((u8 *)sasIoUnitPg1, 0, sasIoUnitPg1_data_sz); ++ cfg.physAddr = sasIoUnitPg1_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if (mpt_config(ioc, &cfg) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: READ MPI_SASIOUNITPAGE1: CURRENT\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ ++ switch (karg->uFunction) { ++ ++ case CSMI_SAS_PC_LINK_RESET: ++ case CSMI_SAS_PC_HARD_RESET: ++ { ++ u8 opcode = (karg->uFunction==CSMI_SAS_PC_LINK_RESET) ? ++ MPI_SAS_OP_PHY_LINK_RESET : MPI_SAS_OP_PHY_HARD_RESET; ++ ++ if((karg->uLinkFlags & CSMI_SAS_PHY_ACTIVATE_CONTROL) && ++ (karg->usLengthOfControl >= sizeof(CSMI_SAS_PHY_CONTROL)) && ++ (karg->bNumberOfControls > 0)){ ++ if(karg->Control[0].bRate == ++ CSMI_SAS_LINK_RATE_1_5_GBPS) { ++ sasIoUnitPg1->PhyData[karg->bPhyIdentifier].MaxMinLinkRate = ++ MPI_SAS_IOUNIT1_MAX_RATE_1_5 | ++ MPI_SAS_IOUNIT1_MIN_RATE_1_5; ++ } ++ else if(karg->Control[0].bRate == ++ CSMI_SAS_LINK_RATE_3_0_GBPS) { ++ sasIoUnitPg1->PhyData[karg->bPhyIdentifier].MaxMinLinkRate = ++ MPI_SAS_IOUNIT1_MAX_RATE_3_0 | ++ MPI_SAS_IOUNIT1_MIN_RATE_3_0; ++ } ++ sasIoUnitPg1->PhyData[karg->bPhyIdentifier].PhyFlags &= ++ ~MPI_SAS_IOUNIT1_PHY_FLAGS_PHY_DISABLE; ++ cfg.dir = 1; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM; ++ if (mpt_config(ioc, &cfg) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: WRITE MPI_SASIOUNITPAGE1 NVRAM\n")); ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; ++ if (mpt_config(ioc, &cfg) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: WRITE MPI_SASIOUNITPAGE1 CURRENT\n")); ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ } ++ if (csmisas_phy_reset(ioc, ++ karg->bPhyIdentifier, opcode) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: csmisas_phy_reset\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ break; ++ ++ } ++ case CSMI_SAS_PC_PHY_DISABLE: ++ if(karg->usLengthOfControl || karg->bNumberOfControls) { ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ break; ++ } ++ sasIoUnitPg1->PhyData[karg->bPhyIdentifier].PhyFlags |= ++ MPI_SAS_IOUNIT1_PHY_FLAGS_PHY_DISABLE; ++ cfg.dir = 1; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM; ++ if (mpt_config(ioc, &cfg) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: WRITE MPI_SASIOUNITPAGE1 NVRAM\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; ++ if (mpt_config(ioc, &cfg) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: WRITE MPI_SASIOUNITPAGE1 CURRENT\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ if (csmisas_phy_reset(ioc, ++ karg->bPhyIdentifier, MPI_SAS_OP_PHY_HARD_RESET) != 0) { ++ dcsmisasprintk(ioc, printk(KERN_ERR ++ ": FAILED: csmisas_phy_reset\n")); ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_phy_control_exit; ++ } ++ break; ++ ++ case CSMI_SAS_PC_GET_PHY_SETTINGS: ++ if(karg->usLengthOfControl || karg->bNumberOfControls) { ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ break; ++ } ++ if(csmi_sas_phy_control_buffer_sz < ++ offsetof(CSMI_SAS_PHY_CONTROL_BUFFER,Control) + ++ (4* sizeof(CSMI_SAS_PHY_CONTROL))) { ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_INVALID_PARAMETER; ++ break; ++ } ++ karg->usLengthOfControl = sizeof(CSMI_SAS_PHY_CONTROL); ++ karg->bNumberOfControls = 4; ++ karg->Control[0].bType = CSMI_SAS_SAS; ++ karg->Control[0].bRate = CSMI_SAS_LINK_RATE_1_5_GBPS; ++ karg->Control[1].bType = CSMI_SAS_SAS; ++ karg->Control[1].bRate = CSMI_SAS_LINK_RATE_3_0_GBPS; ++ karg->Control[2].bType = CSMI_SAS_SATA; ++ karg->Control[2].bRate = CSMI_SAS_LINK_RATE_1_5_GBPS; ++ karg->Control[3].bType = CSMI_SAS_SATA; ++ karg->Control[3].bRate = CSMI_SAS_LINK_RATE_3_0_GBPS; ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ break; ++ default: ++ break; ++ } ++ ++ cim_sas_phy_control_exit: ++ ++ if (sasIoUnitPg0) ++ pci_free_consistent(ioc->pcidev, sasIoUnitPg0_data_sz, ++ (u8 *) sasIoUnitPg0, sasIoUnitPg0_dma); ++ ++ if (sasIoUnitPg1) ++ pci_free_consistent(ioc->pcidev, sasIoUnitPg1_data_sz, ++ (u8 *) sasIoUnitPg1, sasIoUnitPg1_dma); ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, karg,csmi_sas_phy_control_buffer_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_phy_control_buffer @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ free_pages((unsigned long)karg, memory_pages); ++ return 0; ++} ++ ++/** ++ * csmisas_get_manuf_pg_7 - Fetch Manufacturing config Page7. ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @mfgpage7_buffer: pointer to ManufacturingPage7_t that returns config ++ * page data ++ * @mfg_size - max size of buffer ++ * ++ * Return: 0 for success ++ * -ENOMEM if no memory available ++ * -EPERM if not allowed due to ISR context ++ * -EAGAIN if no msg frames currently available ++ * -EFAULT for non-successful reply or no reply (timeout) ++ **/ ++static int ++csmisas_get_manuf_pg_7(MPT_ADAPTER *ioc, ManufacturingPage7_t *mfgpage7_buffer, int mfg_size) ++{ ++ ConfigPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ ManufacturingPage7_t *mfgPage7 = NULL; ++ dma_addr_t mfgPage7_dma; ++ int data_sz = 0; ++ int rc; ++ ++ /* Get Manufacturing Page 7 header */ ++ hdr.PageVersion = MPI_MANUFACTURING0_PAGEVERSION; ++ hdr.PageLength = 0; ++ hdr.PageNumber = 7; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_MANUFACTURING; ++ cfg.cfghdr.hdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; ++ cfg.pageAddr = 0; ++ cfg.timeout = 0; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) ++ goto csmisas_get_manuf_pg_7_exit; ++ ++ if (hdr.PageLength == 0) { ++ rc = -EFAULT; ++ goto csmisas_get_manuf_pg_7_exit; ++ } ++ ++ data_sz = hdr.PageLength * 4; ++ mfgPage7 = pci_alloc_consistent(ioc->pcidev, data_sz, &mfgPage7_dma); ++ if (!mfgPage7) { ++ rc = -ENOMEM; ++ goto csmisas_get_manuf_pg_7_exit; ++ } ++ ++ memset((u8 *)mfgPage7, 0, data_sz); ++ cfg.physAddr = mfgPage7_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) ++ goto csmisas_get_manuf_pg_7_exit; ++ ++ /* copy buffer back to user */ ++ memcpy(mfgpage7_buffer, mfgPage7, min(data_sz, mfg_size)); ++ ++ csmisas_get_manuf_pg_7_exit: ++ ++ if (mfgPage7) ++ pci_free_consistent(ioc->pcidev, data_sz, (u8 *)mfgPage7, ++ mfgPage7_dma); ++ ++ return rc; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Get Connector info command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ **/ ++static int ++csmisas_get_connector_info(unsigned long arg) ++{ ++ CSMI_SAS_CONNECTOR_INFO_BUFFER __user *uarg = (void __user *) arg; ++ CSMI_SAS_CONNECTOR_INFO_BUFFER karg; ++ MPT_ADAPTER *ioc = NULL; ++ ManufacturingPage7_t *mfgPg7 = NULL; ++ int mfgPg7_sz; ++ int iocnum; ++ int i; ++ ++ if (copy_from_user(&karg, uarg, ++ sizeof(CSMI_SAS_CONNECTOR_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_connector_info_buffer" ++ " struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ karg.IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ ++ /* `32` is the sizeof MPI_MANPAGE7_CONNECTOR_INFO */ ++ for (i = 0; i < 32; i++) { ++ karg.Reference[i].uPinout = CSMI_SAS_CON_UNKNOWN; ++ strcpy(karg.Reference[i].bConnector,""); ++ karg.Reference[i].bLocation = CSMI_SAS_CON_UNKNOWN; ++ } ++ ++ mfgPg7_sz = offsetof(CONFIG_PAGE_MANUFACTURING_7,ConnectorInfo) + ++ (ioc->num_ports * sizeof(MPI_MANPAGE7_CONNECTOR_INFO)); ++ mfgPg7 = kmalloc(mfgPg7_sz, GFP_KERNEL); ++ if (!mfgPg7){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, mfgPg7); ++ return -EFAULT; ++ } ++ memset(mfgPg7, 0, mfgPg7_sz); ++ ++ if (!csmisas_get_manuf_pg_7(ioc, mfgPg7, mfgPg7_sz)) { ++ for (i = 0; i < ioc->num_ports; i++) { ++ karg.Reference[i].uPinout = ++ le32_to_cpu(mfgPg7->ConnectorInfo[i].Pinout); ++ /*endian conversion , this is u8 * 16 ?? */ ++ strncpy(karg.Reference[i].bConnector, ++ mfgPg7->ConnectorInfo[i].Connector, 16); ++ karg.Reference[i].bLocation = ++ mfgPg7->ConnectorInfo[i].Location; ++ } ++ } ++ ++ kfree(mfgPg7); ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, &karg, ++ sizeof(CSMI_SAS_CONNECTOR_INFO_BUFFER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_connector_info_buffer @" ++ "%p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return 0; ++} ++ ++/** ++ * csmisas_fill_location_data ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ **/ ++static int ++csmisas_fill_location_data(MPT_ADAPTER *ioc, u8 bus, u8 id, u8 opcode, ++ CSMI_SAS_LOCATION_IDENTIFIER * location_ident) ++{ ++ ++ ConfigExtendedPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ int rc; ++ SasDevicePage0_t *sasDevicePg0=NULL; ++ SasEnclosurePage0_t *sasEnclosurePg0=NULL; ++ dma_addr_t sasDevicePg0_dma,sasEnclosurePg0_dma; ++ int sasDevicePg0_data_sz=0; ++ int sasEnclosurePg0_data_sz=0; ++ u64 sas_address; ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ memset (location_ident, 0, sizeof(*location_ident)); ++ ++ /* SAS Device Page 0 */ ++ hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 0; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ rc=-1; ++ goto fill_location_data_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ rc=-1; ++ goto fill_location_data_exit; ++ } ++ ++ sasDevicePg0_data_sz = hdr.ExtPageLength * 4; ++ sasDevicePg0 = (SasDevicePage0_t *) pci_alloc_consistent( ++ ioc->pcidev, sasDevicePg0_data_sz, &sasDevicePg0_dma); ++ if (!sasDevicePg0) { ++ rc=-1; ++ goto fill_location_data_exit; ++ } ++ ++ memset((u8 *)sasDevicePg0, 0, sasDevicePg0_data_sz); ++ cfg.physAddr = sasDevicePg0_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ cfg.pageAddr = (bus << 8) + id ++ + (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID << ++ MPI_SAS_DEVICE_PGAD_FORM_SHIFT); ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ rc=-1; ++ goto fill_location_data_exit; ++ } ++ ++ location_ident->bLocationFlags |= CSMI_SAS_LOCATE_SAS_ADDRESS_VALID; ++ memcpy(&sas_address, &sasDevicePg0->SASAddress, sizeof(u64)); ++ sas_address = reverse_byte_order64(sas_address); ++ memcpy(location_ident->bSASAddress, &sas_address, sizeof(u64)); ++ ++ location_ident->bLocationFlags |= CSMI_SAS_LOCATE_SAS_LUN_VALID; ++ memset(location_ident->bSASLun, 0, sizeof(location_ident->bSASLun)); ++ ++ /* SAS Enclosure Page 0 */ ++ hdr.PageVersion = MPI_SASENCLOSURE0_PAGEVERSION; ++ hdr.ExtPageLength = 0; ++ hdr.PageNumber = 0; ++ hdr.Reserved1 = 0; ++ hdr.Reserved2 = 0; ++ hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; ++ hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_ENCLOSURE; ++ ++ cfg.cfghdr.ehdr = &hdr; ++ cfg.physAddr = -1; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; /* read */ ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ rc=0; ++ goto fill_location_data_exit; ++ } ++ ++ if (hdr.ExtPageLength == 0) { ++ rc=0; ++ goto fill_location_data_exit; ++ } ++ ++ sasEnclosurePg0_data_sz = hdr.ExtPageLength * 4; ++ sasEnclosurePg0 = (SasEnclosurePage0_t *) pci_alloc_consistent( ++ ioc->pcidev, sasEnclosurePg0_data_sz, &sasEnclosurePg0_dma); ++ if (!sasEnclosurePg0) { ++ rc=0; ++ goto fill_location_data_exit; ++ } ++ cfg.physAddr = sasEnclosurePg0_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ cfg.pageAddr = sasDevicePg0->EnclosureHandle ++ + (MPI_SAS_ENCLOS_PGAD_FORM_HANDLE << MPI_SAS_ENCLOS_PGAD_FORM_SHIFT); ++ ++ if ((rc = mpt_config(ioc, &cfg)) != 0) { ++ rc=0; ++ goto fill_location_data_exit; ++ } ++ ++ location_ident->bLocationFlags |= CSMI_SAS_LOCATE_ENCLOSURE_IDENTIFIER_VALID; ++ memcpy(&sas_address, &sasEnclosurePg0->EnclosureLogicalID, sizeof(u64)); ++ sas_address = reverse_byte_order64(sas_address); ++ if (sas_address) ++ memcpy(location_ident->bEnclosureIdentifier, &sas_address, sizeof(u64)); ++ else ++ strcpy(location_ident->bEnclosureIdentifier,"Internal"); ++ ++// bBayPrefix - not supported ++ ++// TODO - We need to look at sasEnclosurePg0-.Flags , to determine ++// whether SEP BUS/TargetID is valid. Ifs its a SES device, then ++// issue internal inquiry to (bus/id) to gather the Enclosure name. ++// If the device is SMP, then issue SMP_MANUFACTURING to get enclosure name ++// If its direct attached, there is no enclosure name ++ location_ident->bLocationFlags |= CSMI_SAS_LOCATE_ENCLOSURE_NAME_VALID; ++ strcpy(location_ident->bEnclosureName,"Not Supported"); ++ ++ location_ident->bLocationFlags |= CSMI_SAS_LOCATE_LOCATION_STATE_VALID; ++ location_ident->bLocationState = CSMI_SAS_LOCATE_UNKNOWN; ++ ++ location_ident->bLocationFlags |= CSMI_SAS_LOCATE_BAY_IDENTIFIER_VALID; ++ location_ident->bBayIdentifier = le16_to_cpu(sasDevicePg0->Slot); ++ ++ ++// TODO - illuminating LEDs, ++// karg->bIdentify = CSMI_SAS_LOCATE_FORCE_OFF, CSMI_SAS_LOCATE_FORCE_ON ++// We can enable/disable LEDs by SCSI Enclosure Processor MPI request message ++// printk("Flags=0x%x\n",sasEnclosurePg0->Flags); ++ ++/* check sasEnclosurePg0->Flags - ++ * to validate whether we need to send the SEPRequest ++ * bit:5 should be set ++ * bit:3-0 any bit should be set. If zero, then SEPRequest will fail ++*/ ++ ++/* MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR ++ * Look in mpi_init.h ++ * SEPRequest_t = structure ++ * ++ * SEPRequest_t->Action should be set to MPI_SEP_REQ_ACTION_WRITE_STATUS ++ * ++ * SEPRequest_t->Flags should be set to ++ * MPI_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS, to pass along enclosure/slot ids ++ * ++ * SEPRequest_t->SlotStatus |= MPI_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST - this ++ * will illuminate the LEDs ++ */ ++ ++fill_location_data_exit: ++ ++ if (sasDevicePg0 != NULL) ++ pci_free_consistent(ioc->pcidev, sasDevicePg0_data_sz, ++ sasDevicePg0, sasDevicePg0_dma); ++ ++ if (sasEnclosurePg0 != NULL) ++ pci_free_consistent(ioc->pcidev, sasEnclosurePg0_data_sz, ++ sasEnclosurePg0, sasEnclosurePg0_dma); ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ return rc; ++} ++ ++static int ++csmisas_fill_location_data_raid(MPT_ADAPTER *ioc, PCSMI_SAS_GET_LOCATION_BUFFER karg, u8 VolumeBus, ++ u8 volumeID) ++{ ++ pRaidVolumePage0_t pVolume0 = NULL; ++ pRaidPhysDiskPage0_t pPhysDisk0 = NULL; ++ CONFIGPARMS cfg; ++ ConfigPageHeader_t header; ++ u8 physDiskNumMax; ++ int volumepage0sz = 0, physdiskpage0sz = 0; ++ dma_addr_t volume0_dma, physdisk0_dma; ++ int csmi_sas_get_location_sz; ++ int rc = 0, i, idx; ++ int num_hotpares; ++ u64 totalMaxLBA, tmpTotalMaxLBA; ++ IOCPage5_t *iocPage5 = NULL; ++ u32 device_info = 0; ++ struct sas_device_info *sas_info; ++ int sz; ++ ++ csmi_sas_get_location_sz = karg->IoctlHeader.Length; ++ physDiskNumMax = (csmi_sas_get_location_sz - ++ offsetof(CSMI_SAS_GET_LOCATION_BUFFER,Location)) ++ / sizeof(CSMI_SAS_LOCATION_IDENTIFIER); ++ karg->bNumberOfLocationIdentifiers=0; ++ ++ /* ++ * get RAID Volume Page 0 ++ */ ++ ++ header.PageVersion = 0; ++ header.PageLength = 0; ++ header.PageNumber = 0; ++ header.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME; ++ cfg.cfghdr.hdr = &header; ++ cfg.physAddr = -1; ++ cfg.pageAddr = (VolumeBus << 8) + volumeID; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ if (mpt_config(ioc, &cfg) != 0) { ++ rc = -1; ++ goto sas_fill_location_data_raid_exit; ++ } ++ ++ if (header.PageLength == 0) { ++ rc = -1; ++ goto sas_fill_location_data_raid_exit; ++ } ++ ++ volumepage0sz = header.PageLength * 4; ++ pVolume0 = pci_alloc_consistent(ioc->pcidev, volumepage0sz, ++ &volume0_dma); ++ if (!pVolume0) { ++ rc = -1; ++ goto sas_fill_location_data_raid_exit; ++ } ++ ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ cfg.physAddr = volume0_dma; ++ if (mpt_config(ioc, &cfg) != 0){ ++ rc = -1; ++ goto sas_fill_location_data_raid_exit; ++ } ++ ++ totalMaxLBA = (u64)le32_to_cpu(pVolume0->MaxLBA) | ++ ((u64)le32_to_cpu(pVolume0->MaxLBAHigh)) << 32; ++ ++ /* ++ * get RAID Physical Disk Page 0 ++ */ ++ header.PageVersion = 0; ++ header.PageLength = 0; ++ header.PageNumber = 0; ++ header.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK; ++ cfg.cfghdr.hdr = &header; ++ cfg.physAddr = -1; ++ cfg.pageAddr = 0; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; ++ cfg.timeout = MPT_IOCTL_DEFAULT_TIMEOUT; ++ if (mpt_config(ioc, &cfg) != 0) { ++ rc = -1; ++ goto sas_fill_location_data_raid_exit; ++ } ++ ++ if (header.PageLength == 0) { ++ rc = -1; ++ goto sas_fill_location_data_raid_exit; ++ } ++ ++ physdiskpage0sz = header.PageLength * 4; ++ pPhysDisk0 = pci_alloc_consistent(ioc->pcidev, physdiskpage0sz, ++ &physdisk0_dma); ++ if (!pPhysDisk0) { ++ rc = -1; ++ goto sas_fill_location_data_raid_exit; ++ } ++ cfg.physAddr = physdisk0_dma; ++ ++ for (i=0; i < min(pVolume0->NumPhysDisks, physDiskNumMax); i++) { ++ ++ /* obtain a refresh of pPhysDisk0 */ ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ cfg.pageAddr = pVolume0->PhysDisk[i].PhysDiskNum; ++ if (mpt_config(ioc, &cfg) != 0){ ++ rc = -1; ++ goto sas_fill_location_data_raid_exit; ++ } ++ ++ if((csmisas_fill_location_data(ioc, pPhysDisk0->PhysDiskBus, ++ pPhysDisk0->PhysDiskID, karg->bIdentify, ++ &karg->Location[karg->bNumberOfLocationIdentifiers])) == 0) ++ karg->bNumberOfLocationIdentifiers++; ++ ++ if (device_info) ++ continue; ++ sas_info = csmisas_get_device_component_by_fw(ioc, ++ pPhysDisk0->PhysDiskBus, pPhysDisk0->PhysDiskID); ++ if (!sas_info || sas_info->is_cached) ++ continue; ++ device_info = sas_info->device_info; ++ } ++ ++ if (pVolume0->VolumeType == MPI_RAID_VOL_TYPE_IS) ++ goto sas_fill_location_data_raid_exit; ++ ++ /* ++ * hot spare support ++ * ++ */ ++ ++ num_hotpares = csmisas_get_number_hotspares(ioc); ++ ++ if (num_hotpares) { ++ ++ sz = offsetof(IOCPage5_t, HotSpare) + ++ num_hotpares * sizeof(IOC_5_HOT_SPARE); ++ iocPage5 = kmalloc(sz, GFP_KERNEL); ++ ++ if (!iocPage5) ++ goto sas_fill_location_data_raid_exit; ++ memset(iocPage5, 0, sizeof(*iocPage5)); ++ ++ if (csmisas_get_ioc_pg5(ioc, iocPage5, sz) != 0) ++ goto sas_fill_location_data_raid_exit; ++ ++ for(i = 0, idx = pVolume0->NumPhysDisks ; i < num_hotpares; ++ i++, idx++) { ++ ++ if (idx >= physDiskNumMax) ++ break; ++ ++ /* obtain a refresh of pPhysDisk0 */ ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ cfg.pageAddr = iocPage5->HotSpare[i].PhysDiskNum; ++ if (mpt_config(ioc, &cfg) != 0) ++ goto sas_fill_location_data_raid_exit; ++ ++ /* Search the list for the matching SAS address. */ ++ sas_info = csmisas_get_device_component_by_fw(ioc, ++ pPhysDisk0->PhysDiskBus, pPhysDisk0->PhysDiskID); ++ ++ if (!sas_info || sas_info->is_cached) ++ continue; ++ ++ /* don't mix SSP hot spare ++ * in SATA volume ++ */ ++ if (!csmisas_is_sata(pPhysDisk0) && ++ (device_info & ++ MPI_SAS_DEVICE_INFO_SATA_DEVICE)) ++ continue; ++ ++ /* don't mix SATA hot spare ++ * in SSP volume ++ */ ++ if (csmisas_is_sata(pPhysDisk0) && ++ (device_info & ++ MPI_SAS_DEVICE_INFO_SSP_TARGET)) ++ continue; ++ ++ /* capacity check for IM volumes*/ ++ if ((pVolume0->VolumeType == ++ MPI_RAID_VOL_TYPE_IM) && ++ (totalMaxLBA + ++ (64*2*1024) /* metadata = 64MB*/ > ++ le32_to_cpu(pPhysDisk0->MaxLBA))) ++ continue; ++ ++ tmpTotalMaxLBA = totalMaxLBA; ++ do_div(tmpTotalMaxLBA, pVolume0->NumPhysDisks); ++ /* capacity check for IME volumes*/ ++ if ((pVolume0->VolumeType == ++ MPI_RAID_VOL_TYPE_IME) && ++ ((tmpTotalMaxLBA * 2) + ++ (64*2*1024 ) /*metadata = 64MB*/ > ++ le32_to_cpu(pPhysDisk0->MaxLBA))) ++ continue; ++ ++ if((csmisas_fill_location_data(ioc, ++ pPhysDisk0->PhysDiskBus, pPhysDisk0->PhysDiskID, ++ karg->bIdentify, ++ &karg->Location[karg->bNumberOfLocationIdentifiers])) == 0) ++ karg->bNumberOfLocationIdentifiers++; ++ } ++ } ++ ++ ++ sas_fill_location_data_raid_exit: ++ ++ kfree(iocPage5); ++ ++ if (pVolume0) ++ pci_free_consistent(ioc->pcidev, volumepage0sz, pVolume0, ++ volume0_dma); ++ ++ if(pPhysDisk0) ++ pci_free_consistent(ioc->pcidev, physdiskpage0sz, pPhysDisk0, ++ physdisk0_dma); ++ ++ return rc; ++} ++ ++/** ++ * Prototype Routine for the CSMI SAS Get location command. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -ENODEV if no such device/adapter ++ */ ++static int ++csmisas_get_location(unsigned long arg) ++{ ++ CSMI_SAS_GET_LOCATION_BUFFER __user *uarg = (void __user *) arg; ++ PCSMI_SAS_GET_LOCATION_BUFFER karg; ++ IOCTL_HEADER ioctl_header; ++ MPT_ADAPTER *ioc = NULL; ++ int iocnum,i; ++ int csmi_sas_get_location_sz; ++ int memory_pages; ++ struct sas_device_info *sas_info; ++ ++ if (copy_from_user(&ioctl_header, uarg, sizeof(IOCTL_HEADER))) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in IOCTL_HEADER" ++ "struct @ %p\n", __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ csmi_sas_get_location_sz = ioctl_header.Length; ++ memory_pages = get_order(csmi_sas_get_location_sz); ++ karg = (PCSMI_SAS_GET_LOCATION_BUFFER)__get_free_pages( ++ GFP_KERNEL, memory_pages); ++ if (!karg){ ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to malloc GET_LOCATION_BUFFER " ++ "csmi_sas_get_location_sz=%d memory_pages=%d\n", ++ __FILE__, __LINE__, __FUNCTION__, ++ csmi_sas_get_location_sz, memory_pages); ++ return -ENOMEM; ++ } ++ memset(karg, 0, sizeof(*karg)); ++ ++ if (copy_from_user(karg, uarg, csmi_sas_get_location_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to read in csmi_sas_phy_control_buffer " ++ "struct @ %p\n", __FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg->IoctlHeader.IOControllerNumber, ++ &ioc)) < 0) || (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ if (!csmisas_is_this_sas_cntr(ioc)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not SAS controller!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ free_pages((unsigned long)karg, memory_pages); ++ return -ENODEV; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s enter.\n",__FUNCTION__)); ++ ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_INVALID_PARAMETER; ++ if(karg->bLengthOfLocationIdentifier != ++ sizeof(CSMI_SAS_LOCATION_IDENTIFIER)) ++ goto cim_sas_get_location_exit; ++ ++ sas_info = csmisas_get_device_component_by_os(ioc, karg->bPathId, ++ karg->bTargetId); ++ if (!sas_info) ++ goto cim_sas_get_location_exit; ++ ++ /* RAID SUPPORT */ ++ if (ioc->raid_data.pIocPg2 && sas_info->is_logical_volume) { ++ for (i=0; iraid_data.pIocPg2->NumActiveVolumes; i++){ ++ if (sas_info->fw.id == ++ ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID && ++ sas_info->fw.channel == ++ ioc->raid_data.pIocPg2->RaidVolume[i].VolumeBus) { ++ if(csmisas_fill_location_data_raid(ioc, karg, ++ ioc->raid_data.pIocPg2->RaidVolume[i].VolumeBus, ++ ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID) == 0) ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_SUCCESS; ++ else ++ karg->IoctlHeader.ReturnCode = ++ CSMI_SAS_STATUS_FAILED; ++ goto cim_sas_get_location_exit; ++ } ++ } ++ } ++ ++ /* NON-RAID SUPPORT */ ++ if (sas_info->is_cached || sas_info->is_logical_volume) ++ goto cim_sas_get_location_exit; ++ ++ /* make sure there's enough room to populate the Location[] struct */ ++ if ((csmi_sas_get_location_sz - ++ offsetof(CSMI_SAS_GET_LOCATION_BUFFER,Location)) < ++ sizeof(CSMI_SAS_LOCATION_IDENTIFIER)) ++ goto cim_sas_get_location_exit; ++ ++ karg->bNumberOfLocationIdentifiers=1; ++ karg->Location[0].bLocationFlags=0; ++ if((csmisas_fill_location_data(ioc, sas_info->fw.channel, ++ sas_info->fw.id, karg->bIdentify, &karg->Location[0])) == 0) ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_SUCCESS; ++ else ++ karg->IoctlHeader.ReturnCode = CSMI_SAS_STATUS_FAILED; ++ ++ cim_sas_get_location_exit: ++ ++ /* Copy the data from kernel memory to user memory ++ */ ++ if (copy_to_user(uarg, karg, csmi_sas_get_location_sz)) { ++ printk(KERN_ERR "%s@%d::%s() - " ++ "Unable to write out csmi_sas_get_location_buffer " ++ "@ %p\n",__FILE__, __LINE__, __FUNCTION__, uarg); ++ free_pages((unsigned long)karg, memory_pages); ++ return -EFAULT; ++ } ++ ++ dcsmisasprintk(ioc, printk(KERN_DEBUG "%s exit.\n",__FUNCTION__)); ++ free_pages((unsigned long)karg, memory_pages); ++ return 0; ++} +--- /dev/null ++++ b/drivers/message/fusion/csmi/csmisas.h +@@ -0,0 +1,1854 @@ ++/************************************************************************** ++ ++Module Name: ++ ++ CSMISAS.H ++ ++ ++Abstract: ++ ++ This file contains constants and data structure definitions used by drivers ++ that support the Common Storage Management Interface specification for ++ SAS or SATA in either the Windows or Linux. ++ ++ This should be considered as a reference implementation only. Changes may ++ be necessary to accommodate a specific build environment or target OS. ++ ++Revision History: ++ ++ 001 SEF 8/12/03 Initial release. ++ 002 SEF 8/20/03 Cleanup to match documentation. ++ 003 SEF 9/12/03 Additional cleanup, created combined header ++ 004 SEF 9/23/03 Changed base types to match linux defaults ++ Added RAID signature ++ Added bControllerFlags to CSMI_SAS_CNTLR_CONFIG ++ Changed CSMI_SAS_BEGIN_PACK to 8 for common structures ++ Fixed other typos identified in first compilation test ++ 005 SEF 10/03/03 Additions to match first version of CSMI document ++ 006 SEF 10/14/03 Fixed typedef struct _CSMI_SAS_SMP_PASSTHRU_BUFFER ++ Added defines for bConnectionRate ++ 007 SEF 10/15/03 Added Firmware Download Control Code and support ++ Added CSMI revision support ++ 008 SEF 10/30/03 No functional change, just updated version to track ++ spec changes ++ 009 SEF 12/09/03 No functional change, just updated version to track ++ spec changes ++ 010 SEF 3/11/04 Fixed typedef struct CSMI_SAS_RAID_DRIVES to include the ++ bFirmware member that is defined in the spec, but ++ was missing in this file, ++ added CC_CSMI_SAS_TASK_MANAGEMENT ++ 011 SEF 4/02/04 No functional change, added comment line before ++ CC_CSMI_SAS_TASK_MANAGEMENT ++ 012 SEF 4/16/04 Added IOControllerNumber to linux header, ++ Modified linux control codes to have upper word of ++ 0xCC77.... to indicate CSMI version 77 ++ Added bSignalClass to CC_CSMI_SET_PHY_INFO ++ Added CC_CSMI_SAS_PHY_CONTROL support ++ 013 SEF 5/14/04 Added CC_CSMI_SAS_GET_CONNECTOR_INFO support ++ 014 SEF 5/24/04 No functional change, just updated version to track spec ++ changes ++ 015 SEF 6/16/04 changed bPinout to uPinout to reflect proper size, ++ changed width of bLocation defines to reflect size ++ 016 SEF 6/17/04 changed bLengthOfControls in CSMI_SAS_PHY_CONTROL ++ to be proper size ++ 017 SEF 9/17/04 added CSMI_SAS_SATA_PORT_SELECTOR, ++ CSMI_SAS_LINK_VIRTUAL, CSMI_SAS_CON_NOT_PRESENT, and ++ CSMI_SAS_CON_NOT_CONNECTED ++ 018 SEF 9/20/04 added CSMI_SAS_PHY_USER_PATTERN, ++ changed definition of CSMI_SAS_PHY_FIXED_PATTERN to not ++ conflict with activate definition ++ 019 SEF 12/06/04 added CSMI_SAS_GET_LOCATION ++ added bSSPStatus to CSMI_SAS_SSP_PASSTHRU_STATUS ++ structure ++ 020 SEF 5/25/05 added CSMI_SAS_PHY_VIRTUAL_SMP, and changes to ++ CSMI_SAS_GET_LOCATION ++ 021 SEF 11/03/05 added new RAID creation functionality ++ 022 SEF 2/01/06 corrected typo bNegotitiatedLInkRate ++ Added two more RAID_TYPES, 7 and 8 ++ 023 SEF 4/04/06 added CSMI_RAID_TYPE_1E ++ changed structures that contained surface scan ++ to priority approach rather than time, causes ++ 0.89 to incompatible with 0.87, so a version ++ check is necessary when interpreting the ++ raid structures ++ Added netware section ++ 024 DRG 5/22/06 Added uFailureCode to CSMI_SAS_RAID_CONFIG and ++ CSMI_SAS_RAID_FEATURES ++ Changed __u64 fields to high and low __u32 fields in ++ order to avoid backward compatibility issues with ++ packing and alignment. ++ Fixed alignment problem in CSMI_SAS_RAID_DRIVES. ++ Added CSMI_SAS_CNTLR_SMART_ARRAY to uControllerFlags ++ Reassigned the value of CSMI_SAS_CNTLR_RAID_CFG_SUPPORT ++ to avoid a conflict. ++ ++**************************************************************************/ ++ ++#ifndef _CSMI_SAS_H_ ++#define _CSMI_SAS_H_ ++ ++// CSMI Specification Revision, the intent is that all versions of the ++// specification will be backward compatible after the 1.00 release. ++// Major revision number, corresponds to xxxx. of CSMI specification ++// Minor revision number, corresponds to .xxxx of CSMI specification ++#define CSMI_MAJOR_REVISION 0 ++#define CSMI_MINOR_REVISION 90 ++ ++/*************************************************************************/ ++/* PATCHES FOR TYPOS */ ++/*************************************************************************/ ++ ++#define bNegotitiatedLInkRate bNegotiatedLinkRate ++ ++/*************************************************************************/ ++/* TARGET OS LINUX SPECIFIC CODE */ ++/*************************************************************************/ ++ ++// EDM #ifdef _linux ++#ifdef __KERNEL__ ++ ++// Linux base types ++ ++#include ++ ++#define __i8 char ++ ++// pack definition ++ ++// EDM #define CSMI_SAS_BEGIN_PACK(x) pack(x) ++// EDM #define CSMI_SAS_END_PACK pack() ++ ++// IOCTL Control Codes ++// (IoctlHeader.ControlCode) ++ ++// Control Codes prior to 0.77 ++ ++// Control Codes requiring CSMI_ALL_SIGNATURE ++ ++// #define CC_CSMI_SAS_GET_DRIVER_INFO 0x12345678 ++// #define CC_CSMI_SAS_GET_CNTLR_CONFIG 0x23456781 ++// #define CC_CSMI_SAS_GET_CNTLR_STATUS 0x34567812 ++// #define CC_CSMI_SAS_FIRMWARE_DOWNLOAD 0x92345678 ++ ++// Control Codes requiring CSMI_RAID_SIGNATURE ++ ++// #define CC_CSMI_SAS_GET_RAID_INFO 0x45678123 ++// #define CC_CSMI_SAS_GET_RAID_CONFIG 0x56781234 ++ ++// Control Codes requiring CSMI_SAS_SIGNATURE ++ ++// #define CC_CSMI_SAS_GET_PHY_INFO 0x67812345 ++// #define CC_CSMI_SAS_SET_PHY_INFO 0x78123456 ++// #define CC_CSMI_SAS_GET_LINK_ERRORS 0x81234567 ++// #define CC_CSMI_SAS_SMP_PASSTHRU 0xA1234567 ++// #define CC_CSMI_SAS_SSP_PASSTHRU 0xB1234567 ++// #define CC_CSMI_SAS_STP_PASSTHRU 0xC1234567 ++// #define CC_CSMI_SAS_GET_SATA_SIGNATURE 0xD1234567 ++// #define CC_CSMI_SAS_GET_SCSI_ADDRESS 0xE1234567 ++// #define CC_CSMI_SAS_GET_DEVICE_ADDRESS 0xF1234567 ++// #define CC_CSMI_SAS_TASK_MANAGEMENT 0xA2345678 ++ ++// Control Codes for 0.77 and later ++ ++// Control Codes requiring CSMI_ALL_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_DRIVER_INFO 0xCC770001 ++#define CC_CSMI_SAS_GET_CNTLR_CONFIG 0xCC770002 ++#define CC_CSMI_SAS_GET_CNTLR_STATUS 0xCC770003 ++#define CC_CSMI_SAS_FIRMWARE_DOWNLOAD 0xCC770004 ++ ++// Control Codes requiring CSMI_RAID_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_RAID_INFO 0xCC77000A ++#define CC_CSMI_SAS_GET_RAID_CONFIG 0xCC77000B ++#define CC_CSMI_SAS_GET_RAID_FEATURES 0xCC77000C ++#define CC_CSMI_SAS_SET_RAID_CONTROL 0xCC77000D ++#define CC_CSMI_SAS_GET_RAID_ELEMENT 0xCC77000E ++#define CC_CSMI_SAS_SET_RAID_OPERATION 0xCC77000F ++ ++// Control Codes requiring CSMI_SAS_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_PHY_INFO 0xCC770014 ++#define CC_CSMI_SAS_SET_PHY_INFO 0xCC770015 ++#define CC_CSMI_SAS_GET_LINK_ERRORS 0xCC770016 ++#define CC_CSMI_SAS_SMP_PASSTHRU 0xCC770017 ++#define CC_CSMI_SAS_SSP_PASSTHRU 0xCC770018 ++#define CC_CSMI_SAS_STP_PASSTHRU 0xCC770019 ++#define CC_CSMI_SAS_GET_SATA_SIGNATURE 0xCC770020 ++#define CC_CSMI_SAS_GET_SCSI_ADDRESS 0xCC770021 ++#define CC_CSMI_SAS_GET_DEVICE_ADDRESS 0xCC770022 ++#define CC_CSMI_SAS_TASK_MANAGEMENT 0xCC770023 ++#define CC_CSMI_SAS_GET_CONNECTOR_INFO 0xCC770024 ++#define CC_CSMI_SAS_GET_LOCATION 0xCC770025 ++ ++ ++// Control Codes requiring CSMI_PHY_SIGNATURE ++ ++#define CC_CSMI_SAS_PHY_CONTROL 0xCC77003C ++ ++// EDM #pragma CSMI_SAS_BEGIN_PACK(8) ++#pragma pack(8) ++ ++// IOCTL_HEADER ++typedef struct _IOCTL_HEADER { ++ __u32 IOControllerNumber; ++ __u32 Length; ++ __u32 ReturnCode; ++ __u32 Timeout; ++ __u16 Direction; ++} IOCTL_HEADER, ++ *PIOCTL_HEADER; ++ ++// EDM #pragma CSMI_SAS_END_PACK ++#pragma pack() ++ ++#endif ++ ++/*************************************************************************/ ++/* TARGET OS WINDOWS SPECIFIC CODE */ ++/*************************************************************************/ ++ ++#ifdef _WIN32 ++ ++// windows IOCTL definitions ++ ++#ifndef _NTDDSCSIH_ ++#include ++#endif ++ ++// pack definition ++ ++#if defined _MSC_VER ++ #define CSMI_SAS_BEGIN_PACK(x) pack(push,x) ++ #define CSMI_SAS_END_PACK pack(pop) ++#elif defined __BORLANDC__ ++ #define CSMI_SAS_BEGIN_PACK(x) option -a##x ++ #define CSMI_SAS_END_PACK option -a. ++#else ++ #error "CSMISAS.H - Must externally define a pack compiler designator." ++#endif ++ ++// base types ++ ++#define __u8 unsigned char ++#define __u16 unsigned short ++#define __u32 unsigned long ++#define __u64 unsigned __int64 ++ ++#define __i8 char ++ ++// IOCTL Control Codes ++// (IoctlHeader.ControlCode) ++ ++// Control Codes requiring CSMI_ALL_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_DRIVER_INFO 1 ++#define CC_CSMI_SAS_GET_CNTLR_CONFIG 2 ++#define CC_CSMI_SAS_GET_CNTLR_STATUS 3 ++#define CC_CSMI_SAS_FIRMWARE_DOWNLOAD 4 ++ ++// Control Codes requiring CSMI_RAID_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_RAID_INFO 10 ++#define CC_CSMI_SAS_GET_RAID_CONFIG 11 ++#define CC_CSMI_SAS_GET_RAID_FEATURES 12 ++#define CC_CSMI_SAS_SET_RAID_CONTROL 13 ++#define CC_CSMI_SAS_GET_RAID_ELEMENT 14 ++#define CC_CSMI_SAS_SET_RAID_OPERATION 15 ++ ++// Control Codes requiring CSMI_SAS_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_PHY_INFO 20 ++#define CC_CSMI_SAS_SET_PHY_INFO 21 ++#define CC_CSMI_SAS_GET_LINK_ERRORS 22 ++#define CC_CSMI_SAS_SMP_PASSTHRU 23 ++#define CC_CSMI_SAS_SSP_PASSTHRU 24 ++#define CC_CSMI_SAS_STP_PASSTHRU 25 ++#define CC_CSMI_SAS_GET_SATA_SIGNATURE 26 ++#define CC_CSMI_SAS_GET_SCSI_ADDRESS 27 ++#define CC_CSMI_SAS_GET_DEVICE_ADDRESS 28 ++#define CC_CSMI_SAS_TASK_MANAGEMENT 29 ++#define CC_CSMI_SAS_GET_CONNECTOR_INFO 30 ++#define CC_CSMI_SAS_GET_LOCATION 31 ++ ++// Control Codes requiring CSMI_PHY_SIGNATURE ++ ++#define CC_CSMI_SAS_PHY_CONTROL 60 ++ ++#define IOCTL_HEADER SRB_IO_CONTROL ++#define PIOCTL_HEADER PSRB_IO_CONTROL ++ ++#endif ++ ++/*************************************************************************/ ++/* TARGET OS NETWARE SPECIFIC CODE */ ++/*************************************************************************/ ++ ++#ifdef _NETWARE ++ ++// NetWare IOCTL definitions ++ ++#define CSMI_SAS_BEGIN_PACK(x) pack(x) ++#define CSMI_SAS_END_PACK pack() ++ ++#ifndef LONG ++typedef unsigned long LONG; ++#endif ++ ++#ifndef WORD ++typedef unsigned short WORD; ++#endif ++ ++#ifndef BYTE ++typedef unsigned char BYTE; ++#endif ++ ++/* Need to have these definitions for Netware */ ++#define __u8 unsigned char ++#define __u16 unsigned short ++#define __u32 unsigned long ++#define __u64 unsigned __int64 ++ ++#define __i8 char ++ ++ ++// EDM #pragma CSMI_SAS_BEGIN_PACK(8) ++#pragma pack(8) ++ ++// IOCTL_HEADER ++typedef struct _IOCTL_HEADER { ++ __u32 Length; ++ __u32 ReturnCode; ++} IOCTL_HEADER, ++ *PIOCTL_HEADER; ++ ++// EDM #pragma CSMI_SAS_END_PACK ++#pragma pack() ++ ++// IOCTL Control Codes ++// (IoctlHeader.ControlCode) ++ ++// Control Codes requiring CSMI_ALL_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_DRIVER_INFO 0x01FF0001 ++#define CC_CSMI_SAS_GET_CNTLR_CONFIG 0x01FF0002 ++#define CC_CSMI_SAS_GET_CNTLR_STATUS 0x01FF0003 ++#define CC_CSMI_SAS_FIRMWARE_DOWNLOAD 0x01FF0004 ++ ++// Control Codes requiring CSMI_RAID_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_RAID_INFO 0x01FF000A ++#define CC_CSMI_SAS_GET_RAID_CONFIG 0x01FF000B ++#define CC_CSMI_SAS_GET_RAID_FEATURES 0x01FF000C ++#define CC_CSMI_SAS_SET_RAID_CONTROL 0x01FF000D ++#define CC_CSMI_SAS_GET_RAID_ELEMENT 0x01FF000E ++#define CC_CSMI_SAS_SET_RAID_OPERATION 0x01FF000F ++ ++// Control Codes requiring CSMI_SAS_SIGNATURE ++ ++#define CC_CSMI_SAS_GET_PHY_INFO 0x01FF0014 ++#define CC_CSMI_SAS_SET_PHY_INFO 0x01FF0015 ++#define CC_CSMI_SAS_GET_LINK_ERRORS 0x01FF0016 ++#define CC_CSMI_SAS_SMP_PASSTHRU 0x01FF0017 ++#define CC_CSMI_SAS_SSP_PASSTHRU 0x01FF0018 ++#define CC_CSMI_SAS_STP_PASSTHRU 0x01FF0019 ++#define CC_CSMI_SAS_GET_SATA_SIGNATURE 0x01FF001A ++#define CC_CSMI_SAS_GET_SCSI_ADDRESS 0x01FF001B ++#define CC_CSMI_SAS_GET_DEVICE_ADDRESS 0x01FF001C ++#define CC_CSMI_SAS_TASK_MANAGEMENT 0x01FF001D ++#define CC_CSMI_SAS_GET_CONNECTOR_INFO 0x01FF001E ++#define CC_CSMI_SAS_GET_LOCATION 0x01FF001F ++ ++// Control Codes requiring CSMI_PHY_SIGNATURE ++ ++#define CC_CSMI_SAS_PHY_CONTROL 60 ++ ++#endif ++ ++/*************************************************************************/ ++/* TARGET OS NOT DEFINED ERROR */ ++/*************************************************************************/ ++ ++// EDM ++//#if (!_WIN32 && !_linux && !_NETWARE) ++// #error "Unknown target OS." ++//#endif ++ ++/*************************************************************************/ ++/* OS INDEPENDENT CODE */ ++/*************************************************************************/ ++ ++/* * * * * * * * * * Class Independent IOCTL Constants * * * * * * * * * */ ++ ++// Return codes for all IOCTL's regardless of class ++// (IoctlHeader.ReturnCode) ++ ++#define CSMI_SAS_STATUS_SUCCESS 0 ++#define CSMI_SAS_STATUS_FAILED 1 ++#define CSMI_SAS_STATUS_BAD_CNTL_CODE 2 ++#define CSMI_SAS_STATUS_INVALID_PARAMETER 3 ++#define CSMI_SAS_STATUS_WRITE_ATTEMPTED 4 ++ ++// Signature value ++// (IoctlHeader.Signature) ++ ++#define CSMI_ALL_SIGNATURE "CSMIALL" ++ ++// Timeout value default of 60 seconds ++// (IoctlHeader.Timeout) ++ ++#define CSMI_ALL_TIMEOUT 60 ++ ++// Direction values for data flow on this IOCTL ++// (IoctlHeader.Direction, Linux only) ++#define CSMI_SAS_DATA_READ 0 ++#define CSMI_SAS_DATA_WRITE 1 ++ ++// I/O Bus Types ++// ISA and EISA bus types are not supported ++// (bIoBusType) ++ ++#define CSMI_SAS_BUS_TYPE_PCI 3 ++#define CSMI_SAS_BUS_TYPE_PCMCIA 4 ++ ++// Controller Status ++// (uStatus) ++ ++#define CSMI_SAS_CNTLR_STATUS_GOOD 1 ++#define CSMI_SAS_CNTLR_STATUS_FAILED 2 ++#define CSMI_SAS_CNTLR_STATUS_OFFLINE 3 ++#define CSMI_SAS_CNTLR_STATUS_POWEROFF 4 ++ ++// Offline Status Reason ++// (uOfflineReason) ++ ++#define CSMI_SAS_OFFLINE_REASON_NO_REASON 0 ++#define CSMI_SAS_OFFLINE_REASON_INITIALIZING 1 ++#define CSMI_SAS_OFFLINE_REASON_BACKSIDE_BUS_DEGRADED 2 ++#define CSMI_SAS_OFFLINE_REASON_BACKSIDE_BUS_FAILURE 3 ++ ++// Controller Class ++// (bControllerClass) ++ ++#define CSMI_SAS_CNTLR_CLASS_HBA 5 ++ ++// Controller Flag bits ++// (uControllerFlags) ++ ++#define CSMI_SAS_CNTLR_SAS_HBA 0x00000001 ++#define CSMI_SAS_CNTLR_SAS_RAID 0x00000002 ++#define CSMI_SAS_CNTLR_SATA_HBA 0x00000004 ++#define CSMI_SAS_CNTLR_SATA_RAID 0x00000008 ++#define CSMI_SAS_CNTLR_SMART_ARRAY 0x00000010 ++ ++// for firmware download ++#define CSMI_SAS_CNTLR_FWD_SUPPORT 0x00010000 ++#define CSMI_SAS_CNTLR_FWD_ONLINE 0x00020000 ++#define CSMI_SAS_CNTLR_FWD_SRESET 0x00040000 ++#define CSMI_SAS_CNTLR_FWD_HRESET 0x00080000 ++#define CSMI_SAS_CNTLR_FWD_RROM 0x00100000 ++ ++// for RAID configuration supported ++#define CSMI_SAS_CNTLR_RAID_CFG_SUPPORT 0x01000000 ++ ++// Download Flag bits ++// (uDownloadFlags) ++#define CSMI_SAS_FWD_VALIDATE 0x00000001 ++#define CSMI_SAS_FWD_SOFT_RESET 0x00000002 ++#define CSMI_SAS_FWD_HARD_RESET 0x00000004 ++ ++// Firmware Download Status ++// (usStatus) ++#define CSMI_SAS_FWD_SUCCESS 0 ++#define CSMI_SAS_FWD_FAILED 1 ++#define CSMI_SAS_FWD_USING_RROM 2 ++#define CSMI_SAS_FWD_REJECT 3 ++#define CSMI_SAS_FWD_DOWNREV 4 ++ ++// Firmware Download Severity ++// (usSeverity> ++#define CSMI_SAS_FWD_INFORMATION 0 ++#define CSMI_SAS_FWD_WARNING 1 ++#define CSMI_SAS_FWD_ERROR 2 ++#define CSMI_SAS_FWD_FATAL 3 ++ ++/* * * * * * * * * * SAS RAID Class IOCTL Constants * * * * * * * * */ ++ ++// Return codes for the RAID IOCTL's regardless of class ++// (IoctlHeader.ReturnCode) ++ ++#define CSMI_SAS_RAID_SET_OUT_OF_RANGE 1000 ++#define CSMI_SAS_RAID_SET_BUFFER_TOO_SMALL 1001 ++#define CSMI_SAS_RAID_SET_DATA_CHANGED 1002 ++ ++// Signature value ++// (IoctlHeader.Signature) ++ ++#define CSMI_RAID_SIGNATURE "CSMIARY" ++ ++// Timeout value default of 60 seconds ++// (IoctlHeader.Timeout) ++ ++#define CSMI_RAID_TIMEOUT 60 ++ ++// RAID Types ++// (bRaidType) ++#define CSMI_SAS_RAID_TYPE_NONE 0 ++#define CSMI_SAS_RAID_TYPE_0 1 ++#define CSMI_SAS_RAID_TYPE_1 2 ++#define CSMI_SAS_RAID_TYPE_10 3 ++#define CSMI_SAS_RAID_TYPE_5 4 ++#define CSMI_SAS_RAID_TYPE_15 5 ++#define CSMI_SAS_RAID_TYPE_6 6 ++#define CSMI_SAS_RAID_TYPE_50 7 ++#define CSMI_SAS_RAID_TYPE_VOLUME 8 ++#define CSMI_SAS_RAID_TYPE_1E 9 ++#define CSMI_SAS_RAID_TYPE_OTHER 255 ++// the last value 255 was already defined for other ++// so end is defined as 254 ++#define CSMI_SAS_RAID_TYPE_END 254 ++ ++// RAID Status ++// (bStatus) ++#define CSMI_SAS_RAID_SET_STATUS_OK 0 ++#define CSMI_SAS_RAID_SET_STATUS_DEGRADED 1 ++#define CSMI_SAS_RAID_SET_STATUS_REBUILDING 2 ++#define CSMI_SAS_RAID_SET_STATUS_FAILED 3 ++#define CSMI_SAS_RAID_SET_STATUS_OFFLINE 4 ++#define CSMI_SAS_RAID_SET_STATUS_TRANSFORMING 5 ++#define CSMI_SAS_RAID_SET_STATUS_QUEUED_FOR_REBUILD 6 ++#define CSMI_SAS_RAID_SET_STATUS_QUEUED_FOR_TRANSFORMATION 7 ++ ++// RAID Drive Count ++// (bDriveCount, 0xF1 to 0xFF are reserved) ++#define CSMI_SAS_RAID_DRIVE_COUNT_TOO_BIG 0xF1 ++#define CSMI_SAS_RAID_DRIVE_COUNT_SUPRESSED 0xF2 ++ ++// RAID Data Type ++// (bDataType) ++#define CSMI_SAS_RAID_DATA_DRIVES 0 ++#define CSMI_SAS_RAID_DATA_DEVICE_ID 1 ++#define CSMI_SAS_RAID_DATA_ADDITIONAL_DATA 2 ++ ++// RAID Drive Status ++// (bDriveStatus) ++#define CSMI_SAS_DRIVE_STATUS_OK 0 ++#define CSMI_SAS_DRIVE_STATUS_REBUILDING 1 ++#define CSMI_SAS_DRIVE_STATUS_FAILED 2 ++#define CSMI_SAS_DRIVE_STATUS_DEGRADED 3 ++#define CSMI_SAS_DRIVE_STATUS_OFFLINE 4 ++#define CSMI_SAS_DRIVE_STATUS_QUEUED_FOR_REBUILD 5 ++ ++// RAID Drive Usage ++// (bDriveUsage) ++#define CSMI_SAS_DRIVE_CONFIG_NOT_USED 0 ++#define CSMI_SAS_DRIVE_CONFIG_MEMBER 1 ++#define CSMI_SAS_DRIVE_CONFIG_SPARE 2 ++#define CSMI_SAS_DRIVE_CONFIG_SPARE_ACTIVE 3 ++ ++// RAID Drive Type ++// (bDriveType) ++#define CSMI_SAS_DRIVE_TYPE_UNKNOWN 0 ++#define CSMI_SAS_DRIVE_TYPE_SINGLE_PORT_SAS 1 ++#define CSMI_SAS_DRIVE_TYPE_DUAL_PORT_SAS 2 ++#define CSMI_SAS_DRIVE_TYPE_SATA 3 ++#define CSMI_SAS_DRIVE_TYPE_SATA_PS 4 ++#define CSMI_SAS_DRIVE_TYPE_OTHER 255 ++ ++// RAID Write Protect ++// (bWriteProtect) ++#define CSMI_SAS_RAID_SET_WRITE_PROTECT_UNKNOWN 0 ++#define CSMI_SAS_RAID_SET_WRITE_PROTECT_UNCHANGED 0 ++#define CSMI_SAS_RAID_SET_WRITE_PROTECT_ENABLED 1 ++#define CSMI_SAS_RAID_SET_WRITE_PROTECT_DISABLED 2 ++ ++// RAID Cache Setting ++// (bCacheSetting) ++#define CSMI_SAS_RAID_SET_CACHE_UNKNOWN 0 ++#define CSMI_SAS_RAID_SET_CACHE_UNCHANGED 0 ++#define CSMI_SAS_RAID_SET_CACHE_ENABLED 1 ++#define CSMI_SAS_RAID_SET_CACHE_DISABLED 2 ++#define CSMI_SAS_RAID_SET_CACHE_CORRUPT 3 ++ ++// RAID Features ++// (uFeatures) ++#define CSMI_SAS_RAID_FEATURE_TRANSFORMATION 0x00000001 ++#define CSMI_SAS_RAID_FEATURE_REBUILD 0x00000002 ++#define CSMI_SAS_RAID_FEATURE_SPLIT_MIRROR 0x00000004 ++#define CSMI_SAS_RAID_FEATURE_MERGE_MIRROR 0x00000008 ++#define CSMI_SAS_RAID_FEATURE_LUN_RENUMBER 0x00000010 ++#define CSMI_SAS_RAID_FEATURE_SURFACE_SCAN 0x00000020 ++#define CSMI_SAS_RAID_FEATURE_SPARES_SHARED 0x00000040 ++ ++// RAID Priority ++// (bDefaultTransformPriority, etc.) ++#define CSMI_SAS_PRIORITY_UNKNOWN 0 ++#define CSMI_SAS_PRIORITY_UNCHANGED 0 ++#define CSMI_SAS_PRIORITY_AUTO 1 ++#define CSMI_SAS_PRIORITY_OFF 2 ++#define CSMI_SAS_PRIORITY_LOW 3 ++#define CSMI_SAS_PRIORITY_MEDIUM 4 ++#define CSMI_SAS_PRIORITY_HIGH 5 ++ ++// RAID Transformation Rules ++// (uRaidSetTransformationRules) ++#define CSMI_SAS_RAID_RULE_AVAILABLE_MEMORY 0x00000001 ++#define CSMI_SAS_RAID_RULE_OVERLAPPED_EXTENTS 0x00000002 ++ ++// RAID Cache Ratios Supported ++// (bCacheRatiosSupported) ++// from 0 to 100 defines the write to read ratio, 0 is 100% write ++#define CSMI_SAS_RAID_CACHE_RATIO_RANGE 101 ++#define CSMI_SAS_RAID_CACHE_RATIO_FIXED 102 ++#define CSMI_SAS_RAID_CACHE_RATIO_AUTO 103 ++#define CSMI_SAS_RAID_CACHE_RATIO_END 255 ++ ++// RAID Cache Ratio Flag ++// (bCacheRatioFlag) ++#define CSMI_SAS_RAID_CACHE_RATIO_DISABLE 0 ++#define CSMI_SAS_RAID_CACHE_RATIO_ENABLE 1 ++ ++// RAID Clear Configuration Signature ++// (bClearConfiguration) ++#define CSMI_SAS_RAID_CLEAR_CONFIGURATION_SIGNATURE "RAIDCLR" ++ ++// RAID Failure Codes ++// (uFailureCode) ++#define CSMI_SAS_FAIL_CODE_OK 0 ++#define CSMI_SAS_FAIL_CODE_PARAMETER_INVALID 1000 ++#define CSMI_SAS_FAIL_CODE_TRANSFORM_PRIORITY_INVALID 1001 ++#define CSMI_SAS_FAIL_CODE_REBUILD_PRIORITY_INVALID 1002 ++#define CSMI_SAS_FAIL_CODE_CACHE_RATIO_INVALID 1003 ++#define CSMI_SAS_FAIL_CODE_SURFACE_SCAN_INVALID 1004 ++#define CSMI_SAS_FAIL_CODE_CLEAR_CONFIGURATION_INVALID 1005 ++#define CSMI_SAS_FAIL_CODE_ELEMENT_INDEX_INVALID 1006 ++#define CSMI_SAS_FAIL_CODE_SUBELEMENT_INDEX_INVALID 1007 ++#define CSMI_SAS_FAIL_CODE_EXTENT_INVALID 1008 ++#define CSMI_SAS_FAIL_CODE_BLOCK_COUNT_INVALID 1009 ++#define CSMI_SAS_FAIL_CODE_DRIVE_INDEX_INVALID 1010 ++#define CSMI_SAS_FAIL_CODE_EXISTING_LUN_INVALID 1011 ++#define CSMI_SAS_FAIL_CODE_RAID_TYPE_INVALID 1012 ++#define CSMI_SAS_FAIL_CODE_STRIPE_SIZE_INVALID 1013 ++#define CSMI_SAS_FAIL_CODE_TRANSFORMATION_INVALID 1014 ++#define CSMI_SAS_FAIL_CODE_CHANGE_COUNT_INVALID 1015 ++#define CSMI_SAS_FAIL_CODE_ENUMERATION_TYPE_INVALID 1016 ++ ++#define CSMI_SAS_FAIL_CODE_EXCEEDED_RAID_SET_COUNT 2000 ++#define CSMI_SAS_FAIL_CODE_DUPLICATE_LUN 2001 ++ ++#define CSMI_SAS_FAIL_CODE_WAIT_FOR_OPERATION 3000 ++ ++// RAID Enumeration Types ++// (uEnumerationType) ++#define CSMI_SAS_RAID_ELEMENT_TYPE_DRIVE 0 ++#define CSMI_SAS_RAID_ELEMENT_TYPE_MODULE 1 ++#define CSMI_SAS_RAID_ELEMENT_TYPE_DRIVE_RAID_SET 2 ++#define CSMI_SAS_RAID_ELEMENT_TYPE_EXTENT_DRIVE 3 ++ ++// RAID Extent Types ++// (bExtentType) ++#define CSMI_SAS_RAID_EXTENT_RESERVED 0 ++#define CSMI_SAS_RAID_EXTENT_METADATA 1 ++#define CSMI_SAS_RAID_EXTENT_ALLOCATED 2 ++#define CSMI_SAS_RAID_EXTENT_UNALLOCATED 3 ++ ++// RAID Operation Types ++// (uOperationType) ++#define CSMI_SAS_RAID_SET_CREATE 0 ++#define CSMI_SAS_RAID_SET_LABEL 1 ++#define CSMI_SAS_RAID_SET_TRANSFORM 2 ++#define CSMI_SAS_RAID_SET_DELETE 3 ++#define CSMI_SAS_RAID_SET_WRITE_PROTECT 4 ++#define CSMI_SAS_RAID_SET_CACHE 5 ++#define CSMI_SAS_RAID_SET_ONLINE_STATE 6 ++#define CSMI_SAS_RAID_SET_SPARE 7 ++ ++// RAID Transform Types ++// (bTransformType) ++#define CSMI_SAS_RAID_SET_TRANSFORM_SPLIT_MIRROR 0 ++#define CSMI_SAS_RAID_SET_TRANSFORM_MERGE_RAID_0 1 ++#define CSMI_SAS_RAID_SET_TRANSFORM_LUN_RENUMBER 2 ++#define CSMI_SAS_RAID_SET_TRANSFORM_RAID_SET 3 ++ ++// RAID Online State ++// (bOnlineState) ++#define CSMI_SAS_RAID_SET_STATE_UNKNOWN 0 ++#define CSMI_SAS_RAID_SET_STATE_ONLINE 1 ++#define CSMI_SAS_RAID_SET_STATE_OFFLINE 2 ++ ++/* * * * * * * * * * SAS HBA Class IOCTL Constants * * * * * * * * * */ ++ ++// Return codes for SAS IOCTL's ++// (IoctlHeader.ReturnCode) ++ ++#define CSMI_SAS_PHY_INFO_CHANGED CSMI_SAS_STATUS_SUCCESS ++#define CSMI_SAS_PHY_INFO_NOT_CHANGEABLE 2000 ++#define CSMI_SAS_LINK_RATE_OUT_OF_RANGE 2001 ++ ++#define CSMI_SAS_PHY_DOES_NOT_EXIST 2002 ++#define CSMI_SAS_PHY_DOES_NOT_MATCH_PORT 2003 ++#define CSMI_SAS_PHY_CANNOT_BE_SELECTED 2004 ++#define CSMI_SAS_SELECT_PHY_OR_PORT 2005 ++#define CSMI_SAS_PORT_DOES_NOT_EXIST 2006 ++#define CSMI_SAS_PORT_CANNOT_BE_SELECTED 2007 ++#define CSMI_SAS_CONNECTION_FAILED 2008 ++ ++#define CSMI_SAS_NO_SATA_DEVICE 2009 ++#define CSMI_SAS_NO_SATA_SIGNATURE 2010 ++#define CSMI_SAS_SCSI_EMULATION 2011 ++#define CSMI_SAS_NOT_AN_END_DEVICE 2012 ++#define CSMI_SAS_NO_SCSI_ADDRESS 2013 ++#define CSMI_SAS_NO_DEVICE_ADDRESS 2014 ++ ++// Signature value ++// (IoctlHeader.Signature) ++ ++#define CSMI_SAS_SIGNATURE "CSMISAS" ++ ++// Timeout value default of 60 seconds ++// (IoctlHeader.Timeout) ++ ++#define CSMI_SAS_TIMEOUT 60 ++ ++// Device types ++// (bDeviceType) ++ ++#define CSMI_SAS_PHY_UNUSED 0x00 ++#define CSMI_SAS_NO_DEVICE_ATTACHED 0x00 ++#define CSMI_SAS_END_DEVICE 0x10 ++#define CSMI_SAS_EDGE_EXPANDER_DEVICE 0x20 ++#define CSMI_SAS_FANOUT_EXPANDER_DEVICE 0x30 ++ ++// Protocol options ++// (bInitiatorPortProtocol, bTargetPortProtocol) ++ ++#define CSMI_SAS_PROTOCOL_SATA 0x01 ++#define CSMI_SAS_PROTOCOL_SMP 0x02 ++#define CSMI_SAS_PROTOCOL_STP 0x04 ++#define CSMI_SAS_PROTOCOL_SSP 0x08 ++ ++// Negotiated and hardware link rates ++// (bNegotiatedLinkRate, bMinimumLinkRate, bMaximumLinkRate) ++ ++#define CSMI_SAS_LINK_RATE_UNKNOWN 0x00 ++#define CSMI_SAS_PHY_DISABLED 0x01 ++#define CSMI_SAS_LINK_RATE_FAILED 0x02 ++#define CSMI_SAS_SATA_SPINUP_HOLD 0x03 ++#define CSMI_SAS_SATA_PORT_SELECTOR 0x04 ++#define CSMI_SAS_LINK_RATE_1_5_GBPS 0x08 ++#define CSMI_SAS_LINK_RATE_3_0_GBPS 0x09 ++#define CSMI_SAS_LINK_VIRTUAL 0x10 ++ ++// Discover state ++// (bAutoDiscover) ++ ++#define CSMI_SAS_DISCOVER_NOT_SUPPORTED 0x00 ++#define CSMI_SAS_DISCOVER_NOT_STARTED 0x01 ++#define CSMI_SAS_DISCOVER_IN_PROGRESS 0x02 ++#define CSMI_SAS_DISCOVER_COMPLETE 0x03 ++#define CSMI_SAS_DISCOVER_ERROR 0x04 ++ ++// Phy features ++ ++#define CSMI_SAS_PHY_VIRTUAL_SMP 0x01 ++ ++// Programmed link rates ++// (bMinimumLinkRate, bMaximumLinkRate) ++// (bProgrammedMinimumLinkRate, bProgrammedMaximumLinkRate) ++ ++#define CSMI_SAS_PROGRAMMED_LINK_RATE_UNCHANGED 0x00 ++#define CSMI_SAS_PROGRAMMED_LINK_RATE_1_5_GBPS 0x08 ++#define CSMI_SAS_PROGRAMMED_LINK_RATE_3_0_GBPS 0x09 ++ ++// Link rate ++// (bNegotiatedLinkRate in CSMI_SAS_SET_PHY_INFO) ++ ++#define CSMI_SAS_LINK_RATE_NEGOTIATE 0x00 ++#define CSMI_SAS_LINK_RATE_PHY_DISABLED 0x01 ++ ++// Signal class ++// (bSignalClass in CSMI_SAS_SET_PHY_INFO) ++ ++#define CSMI_SAS_SIGNAL_CLASS_UNKNOWN 0x00 ++#define CSMI_SAS_SIGNAL_CLASS_DIRECT 0x01 ++#define CSMI_SAS_SIGNAL_CLASS_SERVER 0x02 ++#define CSMI_SAS_SIGNAL_CLASS_ENCLOSURE 0x03 ++ ++// Link error reset ++// (bResetCounts) ++ ++#define CSMI_SAS_LINK_ERROR_DONT_RESET_COUNTS 0x00 ++#define CSMI_SAS_LINK_ERROR_RESET_COUNTS 0x01 ++ ++// Phy identifier ++// (bPhyIdentifier) ++ ++#define CSMI_SAS_USE_PORT_IDENTIFIER 0xFF ++ ++// Port identifier ++// (bPortIdentifier) ++ ++#define CSMI_SAS_IGNORE_PORT 0xFF ++ ++// Programmed link rates ++// (bConnectionRate) ++ ++#define CSMI_SAS_LINK_RATE_NEGOTIATED 0x00 ++#define CSMI_SAS_LINK_RATE_1_5_GBPS 0x08 ++#define CSMI_SAS_LINK_RATE_3_0_GBPS 0x09 ++ ++// Connection status ++// (bConnectionStatus) ++ ++#define CSMI_SAS_OPEN_ACCEPT 0 ++#define CSMI_SAS_OPEN_REJECT_BAD_DESTINATION 1 ++#define CSMI_SAS_OPEN_REJECT_RATE_NOT_SUPPORTED 2 ++#define CSMI_SAS_OPEN_REJECT_NO_DESTINATION 3 ++#define CSMI_SAS_OPEN_REJECT_PATHWAY_BLOCKED 4 ++#define CSMI_SAS_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED 5 ++#define CSMI_SAS_OPEN_REJECT_RESERVE_ABANDON 6 ++#define CSMI_SAS_OPEN_REJECT_RESERVE_CONTINUE 7 ++#define CSMI_SAS_OPEN_REJECT_RESERVE_INITIALIZE 8 ++#define CSMI_SAS_OPEN_REJECT_RESERVE_STOP 9 ++#define CSMI_SAS_OPEN_REJECT_RETRY 10 ++#define CSMI_SAS_OPEN_REJECT_STP_RESOURCES_BUSY 11 ++#define CSMI_SAS_OPEN_REJECT_WRONG_DESTINATION 12 ++ ++// SSP Status ++// (bSSPStatus) ++ ++#define CSMI_SAS_SSP_STATUS_UNKNOWN 0x00 ++#define CSMI_SAS_SSP_STATUS_WAITING 0x01 ++#define CSMI_SAS_SSP_STATUS_COMPLETED 0x02 ++#define CSMI_SAS_SSP_STATUS_FATAL_ERROR 0x03 ++#define CSMI_SAS_SSP_STATUS_RETRY 0x04 ++#define CSMI_SAS_SSP_STATUS_NO_TAG 0x05 ++ ++// SSP Flags ++// (uFlags) ++ ++#define CSMI_SAS_SSP_READ 0x00000001 ++#define CSMI_SAS_SSP_WRITE 0x00000002 ++#define CSMI_SAS_SSP_UNSPECIFIED 0x00000004 ++ ++#define CSMI_SAS_SSP_TASK_ATTRIBUTE_SIMPLE 0x00000000 ++#define CSMI_SAS_SSP_TASK_ATTRIBUTE_HEAD_OF_QUEUE 0x00000010 ++#define CSMI_SAS_SSP_TASK_ATTRIBUTE_ORDERED 0x00000020 ++#define CSMI_SAS_SSP_TASK_ATTRIBUTE_ACA 0x00000040 ++ ++// SSP Data present ++// (bDataPresent) ++ ++#define CSMI_SAS_SSP_NO_DATA_PRESENT 0x00 ++#define CSMI_SAS_SSP_RESPONSE_DATA_PRESENT 0x01 ++#define CSMI_SAS_SSP_SENSE_DATA_PRESENT 0x02 ++ ++// STP Flags ++// (uFlags) ++ ++#define CSMI_SAS_STP_READ 0x00000001 ++#define CSMI_SAS_STP_WRITE 0x00000002 ++#define CSMI_SAS_STP_UNSPECIFIED 0x00000004 ++#define CSMI_SAS_STP_PIO 0x00000010 ++#define CSMI_SAS_STP_DMA 0x00000020 ++#define CSMI_SAS_STP_PACKET 0x00000040 ++#define CSMI_SAS_STP_DMA_QUEUED 0x00000080 ++#define CSMI_SAS_STP_EXECUTE_DIAG 0x00000100 ++#define CSMI_SAS_STP_RESET_DEVICE 0x00000200 ++ ++// Task Management Flags ++// (uFlags) ++ ++#define CSMI_SAS_TASK_IU 0x00000001 ++#define CSMI_SAS_HARD_RESET_SEQUENCE 0x00000002 ++#define CSMI_SAS_SUPPRESS_RESULT 0x00000004 ++ ++// Task Management Functions ++// (bTaskManagement) ++ ++#define CSMI_SAS_SSP_ABORT_TASK 0x01 ++#define CSMI_SAS_SSP_ABORT_TASK_SET 0x02 ++#define CSMI_SAS_SSP_CLEAR_TASK_SET 0x04 ++#define CSMI_SAS_SSP_LOGICAL_UNIT_RESET 0x08 ++#define CSMI_SAS_SSP_CLEAR_ACA 0x40 ++#define CSMI_SAS_SSP_QUERY_TASK 0x80 ++ ++// Task Management Information ++// (uInformation) ++ ++#define CSMI_SAS_SSP_TEST 1 ++#define CSMI_SAS_SSP_EXCEEDED 2 ++#define CSMI_SAS_SSP_DEMAND 3 ++#define CSMI_SAS_SSP_TRIGGER 4 ++ ++// Connector Pinout Information ++// (uPinout) ++ ++#define CSMI_SAS_CON_UNKNOWN 0x00000001 ++#define CSMI_SAS_CON_SFF_8482 0x00000002 ++#define CSMI_SAS_CON_SFF_8470_LANE_1 0x00000100 ++#define CSMI_SAS_CON_SFF_8470_LANE_2 0x00000200 ++#define CSMI_SAS_CON_SFF_8470_LANE_3 0x00000400 ++#define CSMI_SAS_CON_SFF_8470_LANE_4 0x00000800 ++#define CSMI_SAS_CON_SFF_8484_LANE_1 0x00010000 ++#define CSMI_SAS_CON_SFF_8484_LANE_2 0x00020000 ++#define CSMI_SAS_CON_SFF_8484_LANE_3 0x00040000 ++#define CSMI_SAS_CON_SFF_8484_LANE_4 0x00080000 ++ ++// Connector Location Information ++// (bLocation) ++ ++// same as uPinout above... ++// #define CSMI_SAS_CON_UNKNOWN 0x01 ++#define CSMI_SAS_CON_INTERNAL 0x02 ++#define CSMI_SAS_CON_EXTERNAL 0x04 ++#define CSMI_SAS_CON_SWITCHABLE 0x08 ++#define CSMI_SAS_CON_AUTO 0x10 ++#define CSMI_SAS_CON_NOT_PRESENT 0x20 ++#define CSMI_SAS_CON_NOT_CONNECTED 0x80 ++ ++// Device location identification ++// (bIdentify) ++ ++#define CSMI_SAS_LOCATE_UNKNOWN 0x00 ++#define CSMI_SAS_LOCATE_FORCE_OFF 0x01 ++#define CSMI_SAS_LOCATE_FORCE_ON 0x02 ++ ++// Location Valid flags ++// (uLocationFlags) ++ ++#define CSMI_SAS_LOCATE_SAS_ADDRESS_VALID 0x00000001 ++#define CSMI_SAS_LOCATE_SAS_LUN_VALID 0x00000002 ++#define CSMI_SAS_LOCATE_ENCLOSURE_IDENTIFIER_VALID 0x00000004 ++#define CSMI_SAS_LOCATE_ENCLOSURE_NAME_VALID 0x00000008 ++#define CSMI_SAS_LOCATE_BAY_PREFIX_VALID 0x00000010 ++#define CSMI_SAS_LOCATE_BAY_IDENTIFIER_VALID 0x00000020 ++#define CSMI_SAS_LOCATE_LOCATION_STATE_VALID 0x00000040 ++ ++/* * * * * * * * SAS Phy Control Class IOCTL Constants * * * * * * * * */ ++ ++// Return codes for SAS Phy Control IOCTL's ++// (IoctlHeader.ReturnCode) ++ ++// Signature value ++// (IoctlHeader.Signature) ++ ++#define CSMI_PHY_SIGNATURE "CSMIPHY" ++ ++// Phy Control Functions ++// (bFunction) ++ ++// values 0x00 to 0xFF are consistent in definition with the SMP PHY CONTROL ++// function defined in the SAS spec ++#define CSMI_SAS_PC_NOP 0x00000000 ++#define CSMI_SAS_PC_LINK_RESET 0x00000001 ++#define CSMI_SAS_PC_HARD_RESET 0x00000002 ++#define CSMI_SAS_PC_PHY_DISABLE 0x00000003 ++// 0x04 to 0xFF reserved... ++#define CSMI_SAS_PC_GET_PHY_SETTINGS 0x00000100 ++ ++// Link Flags ++#define CSMI_SAS_PHY_ACTIVATE_CONTROL 0x00000001 ++#define CSMI_SAS_PHY_UPDATE_SPINUP_RATE 0x00000002 ++#define CSMI_SAS_PHY_AUTO_COMWAKE 0x00000004 ++ ++// Device Types for Phy Settings ++// (bType) ++#define CSMI_SAS_UNDEFINED 0x00 ++#define CSMI_SAS_SATA 0x01 ++#define CSMI_SAS_SAS 0x02 ++ ++// Transmitter Flags ++// (uTransmitterFlags) ++#define CSMI_SAS_PHY_PREEMPHASIS_DISABLED 0x00000001 ++ ++// Receiver Flags ++// (uReceiverFlags) ++#define CSMI_SAS_PHY_EQUALIZATION_DISABLED 0x00000001 ++ ++// Pattern Flags ++// (uPatternFlags) ++// #define CSMI_SAS_PHY_ACTIVATE_CONTROL 0x00000001 ++#define CSMI_SAS_PHY_DISABLE_SCRAMBLING 0x00000002 ++#define CSMI_SAS_PHY_DISABLE_ALIGN 0x00000004 ++#define CSMI_SAS_PHY_DISABLE_SSC 0x00000008 ++ ++#define CSMI_SAS_PHY_FIXED_PATTERN 0x00000010 ++#define CSMI_SAS_PHY_USER_PATTERN 0x00000020 ++ ++// Fixed Patterns ++// (bFixedPattern) ++#define CSMI_SAS_PHY_CJPAT 0x00000001 ++#define CSMI_SAS_PHY_ALIGN 0x00000002 ++ ++// Type Flags ++// (bTypeFlags) ++#define CSMI_SAS_PHY_POSITIVE_DISPARITY 0x01 ++#define CSMI_SAS_PHY_NEGATIVE_DISPARITY 0x02 ++#define CSMI_SAS_PHY_CONTROL_CHARACTER 0x04 ++ ++// Miscellaneous ++#define SLOT_NUMBER_UNKNOWN 0xFFFF ++ ++/*************************************************************************/ ++/* DATA STRUCTURES */ ++/*************************************************************************/ ++ ++/* * * * * * * * * * Class Independent Structures * * * * * * * * * */ ++ ++// EDM #pragma CSMI_SAS_BEGIN_PACK(8) ++#pragma pack(8) ++ ++// CC_CSMI_SAS_DRIVER_INFO ++ ++typedef struct _CSMI_SAS_DRIVER_INFO { ++ __u8 szName[81]; ++ __u8 szDescription[81]; ++ __u16 usMajorRevision; ++ __u16 usMinorRevision; ++ __u16 usBuildRevision; ++ __u16 usReleaseRevision; ++ __u16 usCSMIMajorRevision; ++ __u16 usCSMIMinorRevision; ++} CSMI_SAS_DRIVER_INFO, ++ *PCSMI_SAS_DRIVER_INFO; ++ ++typedef struct _CSMI_SAS_DRIVER_INFO_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_DRIVER_INFO Information; ++} CSMI_SAS_DRIVER_INFO_BUFFER, ++ *PCSMI_SAS_DRIVER_INFO_BUFFER; ++ ++// CC_CSMI_SAS_CNTLR_CONFIGURATION ++ ++typedef struct _CSMI_SAS_PCI_BUS_ADDRESS { ++ __u8 bBusNumber; ++ __u8 bDeviceNumber; ++ __u8 bFunctionNumber; ++ __u8 bReserved; ++} CSMI_SAS_PCI_BUS_ADDRESS, ++ *PCSMI_SAS_PCI_BUS_ADDRESS; ++ ++typedef union _CSMI_SAS_IO_BUS_ADDRESS { ++ CSMI_SAS_PCI_BUS_ADDRESS PciAddress; ++ __u8 bReserved[32]; ++} CSMI_SAS_IO_BUS_ADDRESS, ++ *PCSMI_SAS_IO_BUS_ADDRESS; ++ ++typedef struct _CSMI_SAS_CNTLR_CONFIG { ++ __u32 uBaseIoAddress; ++ struct { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } BaseMemoryAddress; ++ __u32 uBoardID; ++ __u16 usSlotNumber; ++ __u8 bControllerClass; ++ __u8 bIoBusType; ++ CSMI_SAS_IO_BUS_ADDRESS BusAddress; ++ __u8 szSerialNumber[81]; ++ __u16 usMajorRevision; ++ __u16 usMinorRevision; ++ __u16 usBuildRevision; ++ __u16 usReleaseRevision; ++ __u16 usBIOSMajorRevision; ++ __u16 usBIOSMinorRevision; ++ __u16 usBIOSBuildRevision; ++ __u16 usBIOSReleaseRevision; ++ __u32 uControllerFlags; ++ __u16 usRromMajorRevision; ++ __u16 usRromMinorRevision; ++ __u16 usRromBuildRevision; ++ __u16 usRromReleaseRevision; ++ __u16 usRromBIOSMajorRevision; ++ __u16 usRromBIOSMinorRevision; ++ __u16 usRromBIOSBuildRevision; ++ __u16 usRromBIOSReleaseRevision; ++ __u8 bReserved[7]; ++} CSMI_SAS_CNTLR_CONFIG, ++ *PCSMI_SAS_CNTLR_CONFIG; ++ ++typedef struct _CSMI_SAS_CNTLR_CONFIG_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_CNTLR_CONFIG Configuration; ++} CSMI_SAS_CNTLR_CONFIG_BUFFER, ++ *PCSMI_SAS_CNTLR_CONFIG_BUFFER; ++ ++// CC_CSMI_SAS_CNTLR_STATUS ++ ++typedef struct _CSMI_SAS_CNTLR_STATUS { ++ __u32 uStatus; ++ __u32 uOfflineReason; ++ __u8 bReserved[28]; ++} CSMI_SAS_CNTLR_STATUS, ++ *PCSMI_SAS_CNTLR_STATUS; ++ ++typedef struct _CSMI_SAS_CNTLR_STATUS_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_CNTLR_STATUS Status; ++} CSMI_SAS_CNTLR_STATUS_BUFFER, ++ *PCSMI_SAS_CNTLR_STATUS_BUFFER; ++ ++// CC_CSMI_SAS_FIRMWARE_DOWNLOAD ++ ++typedef struct _CSMI_SAS_FIRMWARE_DOWNLOAD { ++ __u32 uBufferLength; ++ __u32 uDownloadFlags; ++ __u8 bReserved[32]; ++ __u16 usStatus; ++ __u16 usSeverity; ++} CSMI_SAS_FIRMWARE_DOWNLOAD, ++ *PCSMI_SAS_FIRMWARE_DOWNLOAD; ++ ++typedef struct _CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_FIRMWARE_DOWNLOAD Information; ++ __u8 bDataBuffer[1]; ++} CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER, ++ *PCSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER; ++ ++// CC_CSMI_SAS_RAID_INFO ++ ++typedef struct _CSMI_SAS_RAID_INFO { ++ __u32 uNumRaidSets; ++ __u32 uMaxDrivesPerSet; ++ __u32 uMaxRaidSets; ++ __u8 bMaxRaidTypes; ++ __u8 bReservedByteFields[7]; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulMinRaidSetBlocks; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulMaxRaidSetBlocks; ++ __u32 uMaxPhysicalDrives; ++ __u32 uMaxExtents; ++ __u32 uMaxModules; ++ __u32 uMaxTransformationMemory; ++ __u32 uChangeCount; ++ __u8 bReserved[44]; ++} CSMI_SAS_RAID_INFO, ++ *PCSMI_SAS_RAID_INFO; ++ ++typedef struct _CSMI_SAS_RAID_INFO_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_RAID_INFO Information; ++} CSMI_SAS_RAID_INFO_BUFFER, ++ *PCSMI_SAS_RAID_INFO_BUFFER; ++ ++// CC_CSMI_SAS_GET_RAID_CONFIG ++ ++typedef struct _CSMI_SAS_RAID_DRIVES { ++ __u8 bModel[40]; ++ __u8 bFirmware[8]; ++ __u8 bSerialNumber[40]; ++ __u8 bSASAddress[8]; ++ __u8 bSASLun[8]; ++ __u8 bDriveStatus; ++ __u8 bDriveUsage; ++ __u16 usBlockSize; ++ __u8 bDriveType; ++ __u8 bReserved[15]; ++ __u32 uDriveIndex; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulTotalUserBlocks; ++} CSMI_SAS_RAID_DRIVES, ++ *PCSMI_SAS_RAID_DRIVES; ++ ++typedef struct _CSMI_SAS_RAID_DEVICE_ID { ++ __u8 bDeviceIdentificationVPDPage[1]; ++} CSMI_SAS_RAID_DEVICE_ID, ++ *PCSMI_SAS_RAID_DEVICE_ID; ++ ++typedef struct _CSMI_SAS_RAID_SET_ADDITIONAL_DATA { ++ __u8 bLabel[16]; ++ __u8 bRaidSetLun[8]; ++ __u8 bWriteProtection; ++ __u8 bCacheSetting; ++ __u8 bCacheRatio; ++ __u16 usBlockSize; ++ __u8 bReservedBytes[11]; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulRaidSetExtentOffset; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulRaidSetBlocks; ++ __u32 uStripeSizeInBlocks; ++ __u32 uSectorsPerTrack; ++ __u8 bApplicationScratchPad[16]; ++ __u32 uNumberOfHeads; ++ __u32 uNumberOfTracks; ++ __u8 bReserved[24]; ++} CSMI_SAS_RAID_SET_ADDITIONAL_DATA, ++ *PCSMI_SAS_RAID_SET_ADDITIONAL_DATA; ++ ++typedef struct _CSMI_SAS_RAID_CONFIG { ++ __u32 uRaidSetIndex; ++ __u32 uCapacity; ++ __u32 uStripeSize; ++ __u8 bRaidType; ++ __u8 bStatus; ++ __u8 bInformation; ++ __u8 bDriveCount; ++ __u8 bDataType; ++ __u8 bReserved[11]; ++ __u32 uFailureCode; ++ __u32 uChangeCount; ++ union { ++ CSMI_SAS_RAID_DRIVES Drives[1]; ++ CSMI_SAS_RAID_DEVICE_ID DeviceId[1]; ++ CSMI_SAS_RAID_SET_ADDITIONAL_DATA Data[1]; ++ }; ++} CSMI_SAS_RAID_CONFIG, ++ *PCSMI_SAS_RAID_CONFIG; ++ ++typedef struct _CSMI_SAS_RAID_CONFIG_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_RAID_CONFIG Configuration; ++} CSMI_SAS_RAID_CONFIG_BUFFER, ++ *PCSMI_SAS_RAID_CONFIG_BUFFER; ++ ++// CC_CSMI_SAS_GET_RAID_FEATURES ++ ++typedef struct _CSMI_SAS_RAID_TYPE_DESCRIPTION { ++ __u8 bRaidType; ++ __u8 bReservedBytes[7]; ++ __u32 uSupportedStripeSizeMap; ++ __u8 bReserved[24]; ++} CSMI_SAS_RAID_TYPE_DESCRIPTION, ++ *PCSMI_SAS_RAID_TYPE_DESCRIPTION; ++ ++typedef struct _CSMI_SAS_RAID_FEATURES { ++ __u32 uFeatures; ++ __u8 bReservedFeatures[32]; ++ __u8 bDefaultTransformPriority; ++ __u8 bTransformPriority; ++ __u8 bDefaultRebuildPriority; ++ __u8 bRebuildPriority; ++ __u8 bDefaultSurfaceScanPriority; ++ __u8 bSurfaceScanPriority; ++ __u16 usReserved; ++ __u32 uRaidSetTransformationRules; ++ __u32 uReserved[11]; ++ CSMI_SAS_RAID_TYPE_DESCRIPTION RaidType[24]; ++ __u8 bCacheRatiosSupported[104]; ++ __u32 uChangeCount; ++ __u32 uFailureCode; ++ __u8 bReserved[120]; ++} CSMI_SAS_RAID_FEATURES, ++ *PCSMI_SAS_RAID_FEATURES; ++ ++typedef struct _CSMI_SAS_RAID_FEATURES_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_RAID_FEATURES Information; ++} CSMI_SAS_RAID_FEATURES_BUFFER, ++ *PCSMI_SAS_RAID_FEATURES_BUFFER; ++ ++// CC_CSMI_SAS_SET_RAID_CONTROL ++ ++typedef struct _CSMI_SAS_RAID_CONTROL { ++ __u8 bTransformPriority; ++ __u8 bRebuildPriority; ++ __u8 bCacheRatioFlag; ++ __u8 bCacheRatio; ++ __u8 bSurfaceScanPriority; ++ __u8 bReservedBytes[15]; ++ __u8 bClearConfiguration[8]; ++ __u32 uChangeCount; ++ __u8 bReserved[88]; ++ __u32 uFailureCode; ++ __u8 bFailureDescription[80]; ++} CSMI_SAS_RAID_CONTROL, ++ *PCSMI_SAS_RAID_CONTROL; ++ ++typedef struct _CSMI_SAS_RAID_CONTROL_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_RAID_CONTROL Information; ++} CSMI_SAS_RAID_CONTROL_BUFFER, ++ *PCSMI_SAS_RAID_CONTROL_BUFFER; ++ ++// CC_CSMI_SAS_GET_RAID_ELEMENT ++ ++typedef struct _CSMI_SAS_DRIVE_EXTENT_INFO { ++ __u32 uDriveIndex; ++ __u8 bExtentType; ++ __u8 bReservedBytes[7]; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulExtentOffset; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulExtentBlocks; ++ __u32 uRaidSetIndex; ++ __u8 bReserved[96]; ++} CSMI_SAS_DRIVE_EXTENT_INFO, ++ *PCSMI_SAS_DRIVE_EXTENT_INFO; ++ ++typedef struct _CSMI_SAS_RAID_MODULE_INFO { ++ __u8 bReserved[128]; ++} CSMI_SAS_RAID_MODULE_INFO, ++ *PCSMI_SAS_RAID_MODULE_INFO; ++ ++typedef struct _CSMI_SAS_DRIVE_LOCATION { ++ __u8 bConnector[16]; ++ __u8 bBoxName[16]; ++ __u32 uBay; ++ __u8 bReservedBytes[4]; ++ __u8 bAttachedSASAddress[8]; ++ __u8 bAttachedPhyIdentifier; ++ __u8 bReserved[79]; ++} CSMI_SAS_DRIVE_LOCATION, ++ *PCSMI_SAS_DRIVE_LOCATION; ++ ++typedef struct _CSMI_SAS_RAID_DRIVES_ADDITIONAL_DATA { ++ __u8 bNegotiatedLinkRate[2]; ++ __u8 bReserved[126]; ++} CSMI_SAS_RAID_DRIVES_ADDITIONAL_DATA, ++ *PCSMI_SAS_RAID_DRIVES_ADDITIONAL_DATA; ++ ++typedef struct _CSMI_SAS_DRIVE_INFO { ++ CSMI_SAS_RAID_DRIVES Device; ++ CSMI_SAS_RAID_DRIVES_ADDITIONAL_DATA Data; ++ CSMI_SAS_DRIVE_LOCATION Location; ++ __u8 bReserved[16]; ++} CSMI_SAS_DRIVE_INFO, ++ *PCSMI_SAS_DRIVE_INFO; ++ ++typedef struct _CSMI_SAS_RAID_ELEMENT { ++ __u32 uEnumerationType; ++ __u32 uElementIndex; ++ __u32 uNumElements; ++ __u32 uChangeCount; ++ __u32 uSubElementIndex; ++ __u8 bReserved[32]; ++ __u32 uFailureCode; ++ __u8 bFailureDescription[80]; ++ union { ++ CSMI_SAS_DRIVE_INFO Drive; ++ CSMI_SAS_RAID_MODULE_INFO Module; ++ CSMI_SAS_DRIVE_EXTENT_INFO Extent; ++ } Element; ++} CSMI_SAS_RAID_ELEMENT, ++ *PCSMI_SAS_RAID_ELEMENT; ++ ++typedef struct _CSMI_SAS_RAID_ELEMENT_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_RAID_ELEMENT Information; ++} CSMI_SAS_RAID_ELEMENT_BUFFER, ++ *PCSMI_SAS_RAID_ELEMENT_BUFFER; ++ ++// CC_CSMI_SAS_SET_RAID_OPERATION ++ ++typedef struct _CSMI_SAS_RAID_SET_LIST { ++ __u32 uRaidSetIndex; ++ __u8 bExistingLun[8]; ++ __u8 bNewLun[8]; ++ __u8 bReserved[12]; ++} CSMI_SAS_RAID_SET_LIST, ++ *PCSMI_SAS_RAID_SET_LIST; ++ ++typedef struct _CSMI_SAS_RAID_SET_DRIVE_LIST { ++ __u32 uDriveIndex; ++ __u8 bDriveUsage; ++ __u8 bReserved[27]; ++} CSMI_SAS_RAID_SET_DRIVE_LIST, ++ *PCSMI_SAS_RAID_SET_DRIVE_LIST; ++ ++typedef struct _CSMI_SAS_RAID_SET_SPARE_INFO { ++ __u32 uRaidSetIndex; ++ __u32 uDriveCount; ++ __u8 bApplicationScratchPad[16]; ++ __u8 bReserved[104]; ++} CSMI_SAS_RAID_SET_SPARE_INFO, ++ *PCSMI_SAS_RAID_SET_SPARE_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_ONLINE_STATE_INFO { ++ __u32 uRaidSetIndex; ++ __u8 bOnlineState; ++ __u8 bReserved[123]; ++} CSMI_SAS_RAID_SET_ONLINE_STATE_INFO, ++ *PCSMI_SAS_RAID_SET_ONLINE_STATE_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_CACHE_INFO { ++ __u32 uRaidSetIndex; ++ __u8 bCacheSetting; ++ __u8 bCacheRatioFlag; ++ __u8 bCacheRatio; ++ __u8 bReserved[121]; ++} CSMI_SAS_RAID_SET_CACHE_INFO, ++ *PCSMI_SAS_RAID_SET_CACHE_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_WRITE_PROTECT_INFO { ++ __u32 uRaidSetIndex; ++ __u8 bWriteProtectSetting; ++ __u8 bReserved[123]; ++} CSMI_SAS_RAID_SET_WRITE_PROTECT_INFO, ++ *PCSMI_SAS_RAID_SET_WRITE_PROTECT_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_DELETE_INFO { ++ __u32 uRaidSetIndex; ++ __u8 bReserved[124]; ++} CSMI_SAS_RAID_SET_DELETE_INFO, ++ *PCSMI_SAS_RAID_SET_DELETE_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_MODIFY_INFO { ++ __u8 bRaidType; ++ __u8 bReservedBytes[7]; ++ __u32 uStripeSize; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulRaidSetBlocks; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulRaidSetExtentOffset; ++ __u32 uDriveCount; ++ __u8 bReserved[96]; ++} CSMI_SAS_RAID_SET_MODIFY_INFO, ++ *PCSMI_SAS_RAID_SET_MODIFY_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_TRANSFORM_INFO { ++ __u8 bTransformType; ++ __u8 bReservedBytes[3]; ++ __u32 uRaidSetIndex; ++ __u8 bRaidType; ++ __u8 bReservedBytes2[11]; ++ __u32 uAdditionalRaidSetIndex; ++ __u32 uRaidSetCount; ++ __u8 bApplicationScratchPad[16]; ++ CSMI_SAS_RAID_SET_MODIFY_INFO Modify; ++ __u8 bReserved[80]; ++} CSMI_SAS_RAID_SET_TRANSFORM_INFO, ++ *PCSMI_SAS_RAID_SET_TRANSFORM_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_LABEL_INFO { ++ __u32 uRaidSetIndex; ++ __u8 bLabel[16]; ++ __u8 bReserved[108]; ++} CSMI_SAS_RAID_SET_LABEL_INFO, ++ *PCSMI_SAS_RAID_SET_LABEL_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_CREATE_INFO { ++ __u8 bRaidType; ++ __u8 bReservedBytes[7]; ++ __u32 uStripeSize; ++ __u32 uTrackSectorCount; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulRaidSetBlocks; ++ struct ++ { ++ __u32 uLowPart; ++ __u32 uHighPart; ++ } ulRaidSetExtentOffset; ++ __u32 uDriveCount; ++ __u8 bLabel[16]; ++ __u32 uRaidSetIndex; ++ __u8 bApplicationScratchPad[16]; ++ __u32 uNumberOfHeads; ++ __u32 uNumberOfTracks; ++ __u8 bReserved[48]; ++} CSMI_SAS_RAID_SET_CREATE_INFO, ++ *PCSMI_SAS_RAID_SET_CREATE_INFO; ++ ++typedef struct _CSMI_SAS_RAID_SET_OPERATION { ++ __u32 uOperationType; ++ __u32 uChangeCount; ++ __u32 uFailureCode; ++ __u8 bFailureDescription[80]; ++ __u8 bReserved[28]; ++ union { ++ CSMI_SAS_RAID_SET_CREATE_INFO Create; ++ CSMI_SAS_RAID_SET_LABEL_INFO Label; ++ CSMI_SAS_RAID_SET_TRANSFORM_INFO Transform; ++ CSMI_SAS_RAID_SET_DELETE_INFO Delete; ++ CSMI_SAS_RAID_SET_WRITE_PROTECT_INFO Protect; ++ CSMI_SAS_RAID_SET_CACHE_INFO Cache; ++ CSMI_SAS_RAID_SET_ONLINE_STATE_INFO State; ++ CSMI_SAS_RAID_SET_SPARE_INFO Spare; ++ } Operation; ++ union { ++ CSMI_SAS_RAID_SET_DRIVE_LIST DriveList[1]; ++ CSMI_SAS_RAID_SET_LIST RaidSetList[1]; ++ } Parameters; ++} CSMI_SAS_RAID_SET_OPERATION, ++ *PCSMI_SAS_RAID_SET_OPERATION; ++ ++typedef struct _CSMI_SAS_RAID_SET_OPERATION_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_RAID_SET_OPERATION Information; ++} CSMI_SAS_RAID_SET_OPERATION_BUFFER, ++ *PCSMI_SAS_RAID_SET_OPERATION_BUFFER; ++ ++/* * * * * * * * * * SAS HBA Class Structures * * * * * * * * * */ ++ ++// CC_CSMI_SAS_GET_PHY_INFO ++ ++typedef struct _CSMI_SAS_IDENTIFY { ++ __u8 bDeviceType; ++ __u8 bRestricted; ++ __u8 bInitiatorPortProtocol; ++ __u8 bTargetPortProtocol; ++ __u8 bRestricted2[8]; ++ __u8 bSASAddress[8]; ++ __u8 bPhyIdentifier; ++ __u8 bSignalClass; ++ __u8 bReserved[6]; ++} CSMI_SAS_IDENTIFY, ++ *PCSMI_SAS_IDENTIFY; ++ ++typedef struct _CSMI_SAS_PHY_ENTITY { ++ CSMI_SAS_IDENTIFY Identify; ++ __u8 bPortIdentifier; ++ __u8 bNegotiatedLinkRate; ++ __u8 bMinimumLinkRate; ++ __u8 bMaximumLinkRate; ++ __u8 bPhyChangeCount; ++ __u8 bAutoDiscover; ++ __u8 bPhyFeatures; ++ __u8 bReserved; ++ CSMI_SAS_IDENTIFY Attached; ++} CSMI_SAS_PHY_ENTITY, ++ *PCSMI_SAS_PHY_ENTITY; ++ ++typedef struct _CSMI_SAS_PHY_INFO { ++ __u8 bNumberOfPhys; ++ __u8 bReserved[3]; ++ CSMI_SAS_PHY_ENTITY Phy[32]; ++} CSMI_SAS_PHY_INFO, ++ *PCSMI_SAS_PHY_INFO; ++ ++typedef struct _CSMI_SAS_PHY_INFO_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_PHY_INFO Information; ++} CSMI_SAS_PHY_INFO_BUFFER, ++ *PCSMI_SAS_PHY_INFO_BUFFER; ++ ++// CC_CSMI_SAS_SET_PHY_INFO ++ ++typedef struct _CSMI_SAS_SET_PHY_INFO { ++ __u8 bPhyIdentifier; ++ __u8 bNegotiatedLinkRate; ++ __u8 bProgrammedMinimumLinkRate; ++ __u8 bProgrammedMaximumLinkRate; ++ __u8 bSignalClass; ++ __u8 bReserved[3]; ++} CSMI_SAS_SET_PHY_INFO, ++ *PCSMI_SAS_SET_PHY_INFO; ++ ++typedef struct _CSMI_SAS_SET_PHY_INFO_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_SET_PHY_INFO Information; ++} CSMI_SAS_SET_PHY_INFO_BUFFER, ++ *PCSMI_SAS_SET_PHY_INFO_BUFFER; ++ ++// CC_CSMI_SAS_GET_LINK_ERRORS ++ ++typedef struct _CSMI_SAS_LINK_ERRORS { ++ __u8 bPhyIdentifier; ++ __u8 bResetCounts; ++ __u8 bReserved[2]; ++ __u32 uInvalidDwordCount; ++ __u32 uRunningDisparityErrorCount; ++ __u32 uLossOfDwordSyncCount; ++ __u32 uPhyResetProblemCount; ++} CSMI_SAS_LINK_ERRORS, ++ *PCSMI_SAS_LINK_ERRORS; ++ ++typedef struct _CSMI_SAS_LINK_ERRORS_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_LINK_ERRORS Information; ++} CSMI_SAS_LINK_ERRORS_BUFFER, ++ *PCSMI_SAS_LINK_ERRORS_BUFFER; ++ ++// CC_CSMI_SAS_SMP_PASSTHRU ++ ++typedef struct _CSMI_SAS_SMP_REQUEST { ++ __u8 bFrameType; ++ __u8 bFunction; ++ __u8 bReserved[2]; ++ __u8 bAdditionalRequestBytes[1016]; ++} CSMI_SAS_SMP_REQUEST, ++ *PCSMI_SAS_SMP_REQUEST; ++ ++typedef struct _CSMI_SAS_SMP_RESPONSE { ++ __u8 bFrameType; ++ __u8 bFunction; ++ __u8 bFunctionResult; ++ __u8 bReserved; ++ __u8 bAdditionalResponseBytes[1016]; ++} CSMI_SAS_SMP_RESPONSE, ++ *PCSMI_SAS_SMP_RESPONSE; ++ ++typedef struct _CSMI_SAS_SMP_PASSTHRU { ++ __u8 bPhyIdentifier; ++ __u8 bPortIdentifier; ++ __u8 bConnectionRate; ++ __u8 bReserved; ++ __u8 bDestinationSASAddress[8]; ++ __u32 uRequestLength; ++ CSMI_SAS_SMP_REQUEST Request; ++ __u8 bConnectionStatus; ++ __u8 bReserved2[3]; ++ __u32 uResponseBytes; ++ CSMI_SAS_SMP_RESPONSE Response; ++} CSMI_SAS_SMP_PASSTHRU, ++ *PCSMI_SAS_SMP_PASSTHRU; ++ ++typedef struct _CSMI_SAS_SMP_PASSTHRU_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_SMP_PASSTHRU Parameters; ++} CSMI_SAS_SMP_PASSTHRU_BUFFER, ++ *PCSMI_SAS_SMP_PASSTHRU_BUFFER; ++ ++// CC_CSMI_SAS_SSP_PASSTHRU ++ ++typedef struct _CSMI_SAS_SSP_PASSTHRU { ++ __u8 bPhyIdentifier; ++ __u8 bPortIdentifier; ++ __u8 bConnectionRate; ++ __u8 bReserved; ++ __u8 bDestinationSASAddress[8]; ++ __u8 bLun[8]; ++ __u8 bCDBLength; ++ __u8 bAdditionalCDBLength; ++ __u8 bReserved2[2]; ++ __u8 bCDB[16]; ++ __u32 uFlags; ++ __u8 bAdditionalCDB[24]; ++ __u32 uDataLength; ++} CSMI_SAS_SSP_PASSTHRU, ++ *PCSMI_SAS_SSP_PASSTHRU; ++ ++typedef struct _CSMI_SAS_SSP_PASSTHRU_STATUS { ++ __u8 bConnectionStatus; ++ __u8 bSSPStatus; ++ __u8 bReserved[2]; ++ __u8 bDataPresent; ++ __u8 bStatus; ++ __u8 bResponseLength[2]; ++ __u8 bResponse[256]; ++ __u32 uDataBytes; ++} CSMI_SAS_SSP_PASSTHRU_STATUS, ++ *PCSMI_SAS_SSP_PASSTHRU_STATUS; ++ ++typedef struct _CSMI_SAS_SSP_PASSTHRU_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_SSP_PASSTHRU Parameters; ++ CSMI_SAS_SSP_PASSTHRU_STATUS Status; ++ __u8 bDataBuffer[1]; ++} CSMI_SAS_SSP_PASSTHRU_BUFFER, ++ *PCSMI_SAS_SSP_PASSTHRU_BUFFER; ++ ++// CC_CSMI_SAS_STP_PASSTHRU ++ ++typedef struct _CSMI_SAS_STP_PASSTHRU { ++ __u8 bPhyIdentifier; ++ __u8 bPortIdentifier; ++ __u8 bConnectionRate; ++ __u8 bReserved; ++ __u8 bDestinationSASAddress[8]; ++ __u8 bReserved2[4]; ++ __u8 bCommandFIS[20]; ++ __u32 uFlags; ++ __u32 uDataLength; ++} CSMI_SAS_STP_PASSTHRU, ++ *PCSMI_SAS_STP_PASSTHRU; ++ ++typedef struct _CSMI_SAS_STP_PASSTHRU_STATUS { ++ __u8 bConnectionStatus; ++ __u8 bReserved[3]; ++ __u8 bStatusFIS[20]; ++ __u32 uSCR[16]; ++ __u32 uDataBytes; ++} CSMI_SAS_STP_PASSTHRU_STATUS, ++ *PCSMI_SAS_STP_PASSTHRU_STATUS; ++ ++typedef struct _CSMI_SAS_STP_PASSTHRU_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_STP_PASSTHRU Parameters; ++ CSMI_SAS_STP_PASSTHRU_STATUS Status; ++ __u8 bDataBuffer[1]; ++} CSMI_SAS_STP_PASSTHRU_BUFFER, ++ *PCSMI_SAS_STP_PASSTHRU_BUFFER; ++ ++// CC_CSMI_SAS_GET_SATA_SIGNATURE ++ ++typedef struct _CSMI_SAS_SATA_SIGNATURE { ++ __u8 bPhyIdentifier; ++ __u8 bReserved[3]; ++ __u8 bSignatureFIS[20]; ++} CSMI_SAS_SATA_SIGNATURE, ++ *PCSMI_SAS_SATA_SIGNATURE; ++ ++typedef struct _CSMI_SAS_SATA_SIGNATURE_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_SATA_SIGNATURE Signature; ++} CSMI_SAS_SATA_SIGNATURE_BUFFER, ++ *PCSMI_SAS_SATA_SIGNATURE_BUFFER; ++ ++// CC_CSMI_SAS_GET_SCSI_ADDRESS ++ ++typedef struct _CSMI_SAS_GET_SCSI_ADDRESS_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ __u8 bSASAddress[8]; ++ __u8 bSASLun[8]; ++ __u8 bHostIndex; ++ __u8 bPathId; ++ __u8 bTargetId; ++ __u8 bLun; ++} CSMI_SAS_GET_SCSI_ADDRESS_BUFFER, ++ *PCSMI_SAS_GET_SCSI_ADDRESS_BUFFER; ++ ++// CC_CSMI_SAS_GET_DEVICE_ADDRESS ++ ++typedef struct _CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ __u8 bHostIndex; ++ __u8 bPathId; ++ __u8 bTargetId; ++ __u8 bLun; ++ __u8 bSASAddress[8]; ++ __u8 bSASLun[8]; ++} CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER, ++ *PCSMI_SAS_GET_DEVICE_ADDRESS_BUFFER; ++ ++// CC_CSMI_SAS_TASK_MANAGEMENT ++ ++typedef struct _CSMI_SAS_SSP_TASK_IU { ++ __u8 bHostIndex; ++ __u8 bPathId; ++ __u8 bTargetId; ++ __u8 bLun; ++ __u32 uFlags; ++ __u32 uQueueTag; ++ __u32 uReserved; ++ __u8 bTaskManagementFunction; ++ __u8 bReserved[7]; ++ __u32 uInformation; ++} CSMI_SAS_SSP_TASK_IU, ++ *PCSMI_SAS_SSP_TASK_IU; ++ ++typedef struct _CSMI_SAS_SSP_TASK_IU_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_SSP_TASK_IU Parameters; ++ CSMI_SAS_SSP_PASSTHRU_STATUS Status; ++} CSMI_SAS_SSP_TASK_IU_BUFFER, ++ *PCSMI_SAS_SSP_TASK_IU_BUFFER; ++ ++// CC_CSMI_SAS_GET_CONNECTOR_INFO ++ ++typedef struct _CSMI_SAS_GET_CONNECTOR_INFO { ++ __u32 uPinout; ++ __u8 bConnector[16]; ++ __u8 bLocation; ++ __u8 bReserved[15]; ++} CSMI_SAS_CONNECTOR_INFO, ++ *PCSMI_SAS_CONNECTOR_INFO; ++ ++typedef struct _CSMI_SAS_CONNECTOR_INFO_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ CSMI_SAS_CONNECTOR_INFO Reference[32]; ++} CSMI_SAS_CONNECTOR_INFO_BUFFER, ++ *PCSMI_SAS_CONNECTOR_INFO_BUFFER; ++ ++// CC_CSMI_SAS_GET_LOCATION ++ ++typedef struct _CSMI_SAS_LOCATION_IDENTIFIER { ++ __u32 bLocationFlags; ++ __u8 bSASAddress[8]; ++ __u8 bSASLun[8]; ++ __u8 bEnclosureIdentifier[8]; ++ __u8 bEnclosureName[32]; ++ __u8 bBayPrefix[32]; ++ __u8 bBayIdentifier; ++ __u8 bLocationState; ++ __u8 bReserved[2]; ++} CSMI_SAS_LOCATION_IDENTIFIER, ++ *PCSMI_SAS_LOCATION_IDENTIFIER; ++ ++typedef struct _CSMI_SAS_GET_LOCATION_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ __u8 bHostIndex; ++ __u8 bPathId; ++ __u8 bTargetId; ++ __u8 bLun; ++ __u8 bIdentify; ++ __u8 bNumberOfLocationIdentifiers; ++ __u8 bLengthOfLocationIdentifier; ++ CSMI_SAS_LOCATION_IDENTIFIER Location[1]; ++} CSMI_SAS_GET_LOCATION_BUFFER, ++ *PCSMI_SAS_GET_LOCATION_BUFFER; ++ ++// CC_CSMI_SAS_PHY_CONTROL ++ ++typedef struct _CSMI_SAS_CHARACTER { ++ __u8 bTypeFlags; ++ __u8 bValue; ++} CSMI_SAS_CHARACTER, ++ *PCSMI_SAS_CHARACTER; ++ ++typedef struct _CSMI_SAS_PHY_CONTROL { ++ __u8 bType; ++ __u8 bRate; ++ __u8 bReserved[6]; ++ __u32 uVendorUnique[8]; ++ __u32 uTransmitterFlags; ++ __i8 bTransmitAmplitude; ++ __i8 bTransmitterPreemphasis; ++ __i8 bTransmitterSlewRate; ++ __i8 bTransmitterReserved[13]; ++ __u8 bTransmitterVendorUnique[64]; ++ __u32 uReceiverFlags; ++ __i8 bReceiverThreshold; ++ __i8 bReceiverEqualizationGain; ++ __i8 bReceiverReserved[14]; ++ __u8 bReceiverVendorUnique[64]; ++ __u32 uPatternFlags; ++ __u8 bFixedPattern; ++ __u8 bUserPatternLength; ++ __u8 bPatternReserved[6]; ++ CSMI_SAS_CHARACTER UserPatternBuffer[16]; ++} CSMI_SAS_PHY_CONTROL, ++ *PCSMI_SAS_PHY_CONTROL; ++ ++typedef struct _CSMI_SAS_PHY_CONTROL_BUFFER { ++ IOCTL_HEADER IoctlHeader; ++ __u32 uFunction; ++ __u8 bPhyIdentifier; ++ __u16 usLengthOfControl; ++ __u8 bNumberOfControls; ++ __u8 bReserved[4]; ++ __u32 uLinkFlags; ++ __u8 bSpinupRate; ++ __u8 bLinkReserved[7]; ++ __u32 uVendorUnique[8]; ++ CSMI_SAS_PHY_CONTROL Control[1]; ++} CSMI_SAS_PHY_CONTROL_BUFFER, ++ *PCSMI_SAS_PHY_CONTROL_BUFFER; ++ ++//EDM #pragma CSMI_SAS_END_PACK ++#pragma pack() ++ ++#endif // _CSMI_SAS_H_ +--- a/drivers/message/fusion/lsi/mpi.h ++++ b/drivers/message/fusion/lsi/mpi.h +@@ -6,7 +6,7 @@ + * Title: MPI Message independent structures and definitions + * Creation Date: July 27, 2000 + * +- * mpi.h Version: 01.05.16 ++ * mpi.h Version: 01.05.17 + * + * Version History + * --------------- +@@ -82,6 +82,7 @@ + * 08-07-07 01.05.14 Bumped MPI_HEADER_VERSION_UNIT. + * 01-15-08 01.05.15 Bumped MPI_HEADER_VERSION_UNIT. + * 03-28-08 01.05.16 Bumped MPI_HEADER_VERSION_UNIT. ++ * 07-11-08 01.05.17 Bumped MPI_HEADER_VERSION_UNIT. + * -------------------------------------------------------------------------- + */ + +@@ -112,7 +113,7 @@ + /* Note: The major versions of 0xe0 through 0xff are reserved */ + + /* versioning for this MPI header set */ +-#define MPI_HEADER_VERSION_UNIT (0x13) ++#define MPI_HEADER_VERSION_UNIT (0x14) + #define MPI_HEADER_VERSION_DEV (0x00) + #define MPI_HEADER_VERSION_UNIT_MASK (0xFF00) + #define MPI_HEADER_VERSION_UNIT_SHIFT (8) +--- a/drivers/message/fusion/lsi/mpi_cnfg.h ++++ b/drivers/message/fusion/lsi/mpi_cnfg.h +@@ -6,7 +6,7 @@ + * Title: MPI Config message, structures, and Pages + * Creation Date: July 27, 2000 + * +- * mpi_cnfg.h Version: 01.05.18 ++ * mpi_cnfg.h Version: 01.05.19 + * + * Version History + * --------------- +@@ -322,6 +322,14 @@ + * 03-28-08 01.05.18 Defined new bits in Manufacturing Page 4 ExtFlags field + * to control coercion size and the mixing of SAS and SATA + * SSD drives. ++ * 07-11-08 01.05.19 Added defines MPI_MANPAGE4_EXTFLAGS_RAID0_SINGLE_DRIVE ++ * and MPI_MANPAGE4_EXTFLAGS_SSD_SCRUB_DISABLE for ExtFlags ++ * field of Manufacturing Page 4. ++ * Added defines for a new bit in BIOS Page 1 BiosOptions ++ * field to control adapter scan order. ++ * Added BootDeviceWaitTime field to SAS IO Unit Page 2. ++ * Added MPI_SAS_PHY0_PHYINFO_PHY_VACANT for use in PhyInfo ++ * field of SAS Expander Page 1. + * -------------------------------------------------------------------------- + */ + +@@ -700,6 +708,8 @@ typedef struct _CONFIG_PAGE_MANUFACTURIN + #define MPI_MANPAGE4_IR_NO_MIX_SAS_SATA (0x01) + + /* defines for the ExtFlags field */ ++#define MPI_MANPAGE4_EXTFLAGS_RAID0_SINGLE_DRIVE (0x0400) ++#define MPI_MANPAGE4_EXTFLAGS_SSD_SCRUB_DISABLE (0x0200) + #define MPI_MANPAGE4_EXTFLAGS_MASK_COERCION_SIZE (0x0180) + #define MPI_MANPAGE4_EXTFLAGS_SHIFT_COERCION_SIZE (7) + #define MPI_MANPAGE4_EXTFLAGS_1GB_COERCION_SIZE (0) +@@ -1219,6 +1229,10 @@ typedef struct _CONFIG_PAGE_BIOS_1 + #define MPI_BIOSPAGE1_OPTIONS_SPI_ENABLE (0x00000400) + #define MPI_BIOSPAGE1_OPTIONS_FC_ENABLE (0x00000200) + #define MPI_BIOSPAGE1_OPTIONS_SAS_ENABLE (0x00000100) ++ ++#define MPI_BIOSPAGE1_OPTIONS_SCAN_HIGH_TO_LOW (0x00000002) ++#define MPI_BIOSPAGE1_OPTIONS_SCAN_LOW_TO_HIGH (0x00000000) ++ + #define MPI_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001) + + /* values for the IOCSettings field */ +@@ -2712,7 +2726,7 @@ typedef struct _CONFIG_PAGE_SAS_IO_UNIT_ + { + CONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U8 NumDevsPerEnclosure; /* 08h */ +- U8 Reserved1; /* 09h */ ++ U8 BootDeviceWaitTime; /* 09h */ + U16 Reserved2; /* 0Ah */ + U16 MaxPersistentIDs; /* 0Ch */ + U16 NumPersistentIDsUsed; /* 0Eh */ +@@ -2722,7 +2736,7 @@ typedef struct _CONFIG_PAGE_SAS_IO_UNIT_ + } CONFIG_PAGE_SAS_IO_UNIT_2, MPI_POINTER PTR_CONFIG_PAGE_SAS_IO_UNIT_2, + SasIOUnitPage2_t, MPI_POINTER pSasIOUnitPage2_t; + +-#define MPI_SASIOUNITPAGE2_PAGEVERSION (0x06) ++#define MPI_SASIOUNITPAGE2_PAGEVERSION (0x07) + + /* values for SAS IO Unit Page 2 Status field */ + #define MPI_SAS_IOUNIT2_STATUS_DEVICE_LIMIT_EXCEEDED (0x08) +@@ -2997,6 +3011,7 @@ typedef struct _CONFIG_PAGE_SAS_PHY_0 + #define MPI_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01) + + /* values for SAS PHY Page 0 PhyInfo field */ ++#define MPI_SAS_PHY0_PHYINFO_PHY_VACANT (0x80000000) + #define MPI_SAS_PHY0_PHYINFO_SATA_PORT_ACTIVE (0x00004000) + #define MPI_SAS_PHY0_PHYINFO_SATA_PORT_SELECTOR (0x00002000) + #define MPI_SAS_PHY0_PHYINFO_VIRTUAL_PHY (0x00001000) +--- a/drivers/message/fusion/lsi/mpi_history.txt ++++ b/drivers/message/fusion/lsi/mpi_history.txt +@@ -6,15 +6,15 @@ + Copyright (c) 2000-2008 LSI Corporation. + + --------------------------------------- +- Header Set Release Version: 01.05.19 +- Header Set Release Date: 03-28-08 ++ Header Set Release Version: 01.05.20 ++ Header Set Release Date: 07-11-08 + --------------------------------------- + + Filename Current version Prior version + ---------- --------------- ------------- +- mpi.h 01.05.16 01.05.15 +- mpi_ioc.h 01.05.16 01.05.15 +- mpi_cnfg.h 01.05.18 01.05.17 ++ mpi.h 01.05.17 01.05.16 ++ mpi_ioc.h 01.05.16 01.05.16 ++ mpi_cnfg.h 01.05.19 01.05.18 + mpi_init.h 01.05.09 01.05.09 + mpi_targ.h 01.05.06 01.05.06 + mpi_fc.h 01.05.01 01.05.01 +@@ -24,7 +24,7 @@ + mpi_inb.h 01.05.01 01.05.01 + mpi_sas.h 01.05.05 01.05.05 + mpi_type.h 01.05.02 01.05.02 +- mpi_history.txt 01.05.19 01.05.18 ++ mpi_history.txt 01.05.20 01.05.19 + + + * Date Version Description +@@ -99,6 +99,7 @@ mpi.h + * 08-07-07 01.05.14 Bumped MPI_HEADER_VERSION_UNIT. + * 01-15-08 01.05.15 Bumped MPI_HEADER_VERSION_UNIT. + * 03-28-08 01.05.16 Bumped MPI_HEADER_VERSION_UNIT. ++ * 07-11-08 01.05.17 Bumped MPI_HEADER_VERSION_UNIT. + * -------------------------------------------------------------------------- + + mpi_ioc.h +@@ -130,7 +131,7 @@ mpi_ioc.h + * 08-08-01 01.02.01 Original release for v1.2 work. + * New format for FWVersion and ProductId in + * MSG_IOC_FACTS_REPLY and MPI_FW_HEADER. +- * 08-31-01 01.02.02 Added event MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE and ++ * 08-31-01 01.02.02 Addded event MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE and + * related structure and defines. + * Added event MPI_EVENT_ON_BUS_TIMER_EXPIRED. + * Added MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE. +@@ -190,7 +191,7 @@ mpi_ioc.h + * 10-11-06 01.05.12 Added MPI_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED. + * Added MaxInitiators field to PortFacts reply. + * Added SAS Device Status Change ReasonCode for +- * asynchronous notification. ++ * asynchronous notificaiton. + * Added MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE and event + * data structure. + * Added new ImageType values for FWDownload and FWUpload +@@ -523,6 +524,14 @@ mpi_cnfg.h + * 03-28-08 01.05.18 Defined new bits in Manufacturing Page 4 ExtFlags field + * to control coercion size and the mixing of SAS and SATA + * SSD drives. ++ * 07-11-08 01.05.19 Added defines MPI_MANPAGE4_EXTFLAGS_RAID0_SINGLE_DRIVE ++ * and MPI_MANPAGE4_EXTFLAGS_SSD_SCRUB_DISABLE for ExtFlags ++ * field of Manufacturing Page 4. ++ * Added defines for a new bit in BIOS Page 1 BiosOptions ++ * field to control adapter scan order. ++ * Added BootDeviceWaitTime field to SAS IO Unit Page 2. ++ * Added MPI_SAS_PHY0_PHYINFO_PHY_VACANT for use in PhyInfo ++ * field of SAS Expander Page 1. + * -------------------------------------------------------------------------- + + mpi_init.h +@@ -623,7 +632,7 @@ mpi_fc.h + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added messages for Common Transport Send and + * Primitive Send. +- * 01-09-01 01.01.03 Modified some of the new flags to have an MPI prefix ++ * 01-09-01 01.01.03 Modifed some of the new flags to have an MPI prefix + * and modified the FcPrimitiveSend flags. + * 01-25-01 01.01.04 Move InitiatorIndex in LinkServiceRsp reply to a larger + * field. +@@ -743,20 +752,20 @@ mpi_type.h + + mpi_history.txt Parts list history + +-Filename 01.05.19 01.05.18 01.05.17 01.05.16 01.05.15 +----------- -------- -------- -------- -------- -------- +-mpi.h 01.05.16 01.05.15 01.05.14 01.05.13 01.05.12 +-mpi_ioc.h 01.05.16 01.05.15 01.05.15 01.05.14 01.05.13 +-mpi_cnfg.h 01.05.18 01.05.17 01.05.16 01.05.15 01.05.14 +-mpi_init.h 01.05.09 01.05.09 01.05.09 01.05.09 01.05.09 +-mpi_targ.h 01.05.06 01.05.06 01.05.06 01.05.06 01.05.06 +-mpi_fc.h 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 +-mpi_lan.h 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 +-mpi_raid.h 01.05.05 01.05.05 01.05.04 01.05.03 01.05.03 +-mpi_tool.h 01.05.03 01.05.03 01.05.03 01.05.03 01.05.03 +-mpi_inb.h 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 +-mpi_sas.h 01.05.05 01.05.05 01.05.04 01.05.04 01.05.04 +-mpi_type.h 01.05.02 01.05.02 01.05.02 01.05.02 01.05.02 ++Filename 01.05.20 01.05.19 01.05.18 01.05.17 01.05.16 01.05.15 ++---------- -------- -------- -------- -------- -------- -------- ++mpi.h 01.05.17 01.05.16 01.05.15 01.05.14 01.05.13 01.05.12 ++mpi_ioc.h 01.05.16 01.05.16 01.05.15 01.05.15 01.05.14 01.05.13 ++mpi_cnfg.h 01.05.19 01.05.18 01.05.17 01.05.16 01.05.15 01.05.14 ++mpi_init.h 01.05.09 01.05.09 01.05.09 01.05.09 01.05.09 01.05.09 ++mpi_targ.h 01.05.06 01.05.06 01.05.06 01.05.06 01.05.06 01.05.06 ++mpi_fc.h 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 ++mpi_lan.h 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 ++mpi_raid.h 01.05.05 01.05.05 01.05.05 01.05.04 01.05.03 01.05.03 ++mpi_tool.h 01.05.03 01.05.03 01.05.03 01.05.03 01.05.03 01.05.03 ++mpi_inb.h 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 01.05.01 ++mpi_sas.h 01.05.05 01.05.05 01.05.05 01.05.04 01.05.04 01.05.04 ++mpi_type.h 01.05.02 01.05.02 01.05.02 01.05.02 01.05.02 01.05.02 + + Filename 01.05.14 01.05.13 01.05.12 01.05.11 01.05.10 01.05.09 + ---------- -------- -------- -------- -------- -------- -------- +--- a/drivers/message/fusion/lsi/mpi_log_sas.h ++++ b/drivers/message/fusion/lsi/mpi_log_sas.h +@@ -161,11 +161,10 @@ + + #define PL_LOGINFO_SUB_CODE_INVALID_SGL (0x00000200) + #define PL_LOGINFO_SUB_CODE_WRONG_REL_OFF_OR_FRAME_LENGTH (0x00000300) +-#define PL_LOGINFO_SUB_CODE_FRAME_XFER_ERROR (0x00000400) +-/* Bits 0-3 encode Transport Status Register (offset 0x08) */ +-/* Bit 0 is Status Bit 0: FrameXferErr */ +-/* Bit 1 & 2 are Status Bits 16 and 17: FrameXmitErrStatus */ +-/* Bit 3 is Status Bit 18 WriteDataLenghtGTDataLengthErr */ ++#define PL_LOGINFO_SUB_CODE_FRAME_XFER_ERROR (0x00000400) /* Bits 0-3 encode Transport Status Register (offset 0x08) */ ++ /* Bit 0 is Status Bit 0: FrameXferErr */ ++ /* Bit 1 & 2 are Status Bits 16 and 17: FrameXmitErrStatus */ ++ /* Bit 3 is Status Bit 18 WriteDataLenghtGTDataLengthErr */ + + #define PL_LOGINFO_SUB_CODE_TX_FM_CONNECTED_LOW (0x00000500) + #define PL_LOGINFO_SUB_CODE_SATA_NON_NCQ_RW_ERR_BIT_SET (0x00000600) +@@ -180,8 +179,7 @@ + #define PL_LOGINFO_SUB_CODE_DISCOVERY_REMOTE_SEP_RESET (0x00000E01) + #define PL_LOGINFO_SUB_CODE_SECOND_OPEN (0x00000F00) + #define PL_LOGINFO_SUB_CODE_DSCVRY_SATA_INIT_TIMEOUT (0x00001000) +-#define PL_LOGINFO_SUB_CODE_BREAK_ON_SATA_CONNECTION (0x00002000) +-/* not currently used in mainline */ ++#define PL_LOGINFO_SUB_CODE_BREAK_ON_SATA_CONNECTION (0x00002000) /* not currently used in mainline */ + #define PL_LOGINFO_SUB_CODE_BREAK_ON_STUCK_LINK (0x00003000) + #define PL_LOGINFO_SUB_CODE_BREAK_ON_STUCK_LINK_AIP (0x00004000) + #define PL_LOGINFO_SUB_CODE_BREAK_ON_INCOMPLETE_BREAK_RCVD (0x00005000) +@@ -309,6 +307,8 @@ + #define IR_LOGINFO_DEV_FW_UPDATE_ERR_PORT_IO_TIMEOUTS_REQUIRED (0x00010055) + /* Device Firmware Update: Unable to allocate memory for page */ + #define IR_LOGINFO_DEV_FW_UPDATE_ERR_ALLOC_CFG_PAGE (0x00010056) ++/* Device Firmware Update: */ ++//#define IR_LOGINFO_DEV_FW_UPDATE_ERR_ (0x00010054) + + + /****************************************************************************/ +--- a/drivers/message/fusion/lsi/mpi_type.h ++++ b/drivers/message/fusion/lsi/mpi_type.h +@@ -20,6 +20,7 @@ + * 08-08-01 01.02.01 Original release for v1.2 work. + * 05-11-04 01.03.01 Original release for MPI v1.3. + * 08-19-04 01.05.01 Original release for MPI v1.5. ++ * 08-30-05 01.05.02 Added PowerPC option to #ifdef's. + * -------------------------------------------------------------------------- + */ + +@@ -49,8 +50,18 @@ typedef signed short S16; + typedef unsigned short U16; + + +-typedef int32_t S32; +-typedef u_int32_t U32; ++#if defined(unix) || defined(__arm) || defined(ALPHA) || defined(__PPC__) || defined(__ppc) ++ ++ typedef signed int S32; ++ typedef unsigned int U32; ++ ++#else ++ ++ typedef signed long S32; ++ typedef unsigned long U32; ++ ++#endif ++ + + typedef struct _S64 + { +--- a/drivers/message/fusion/mptbase.c ++++ b/drivers/message/fusion/mptbase.c +@@ -58,6 +58,7 @@ + #include + #include /* needed for in_interrupt() proto */ + #include ++#include + #include + #ifdef CONFIG_MTRR + #include +@@ -100,12 +101,13 @@ static int mpt_channel_mapping; + module_param(mpt_channel_mapping, int, 0); + MODULE_PARM_DESC(mpt_channel_mapping, " Mapping id's to channels (default=0)"); + +-static int mpt_debug_level; ++int mpt_debug_level; + static int mpt_set_debug_level(const char *val, struct kernel_param *kp); + module_param_call(mpt_debug_level, mpt_set_debug_level, param_get_int, + &mpt_debug_level, 0600); + MODULE_PARM_DESC(mpt_debug_level, " debug level - refer to mptdebug.h \ + - (default=0)"); ++EXPORT_SYMBOL(mpt_debug_level); + + int mpt_fwfault_debug; + EXPORT_SYMBOL(mpt_fwfault_debug); +@@ -126,7 +128,7 @@ static int mfcounter = 0; + * Public data... + */ + +-static struct proc_dir_entry *mpt_proc_root_dir; ++struct proc_dir_entry *mpt_proc_root_dir; + + #define WHOINIT_UNKNOWN 0xAA + +@@ -144,7 +146,7 @@ static int MptDriverClass[MPT_MAX_PRO + static MPT_EVHANDLER MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS]; + /* Reset handler lookup table */ + static MPT_RESETHANDLER MptResetHandlers[MPT_MAX_PROTOCOL_DRIVERS]; +-static struct mpt_pci_driver *MptDeviceDriverHandlers[MPT_MAX_PROTOCOL_DRIVERS]; ++static struct mpt_pci_driver *MptDeviceDriverHandlers[MPT_MAX_PROTOCOL_DRIVERS]; + + + /* +@@ -157,7 +159,6 @@ static u8 last_drv_idx; + /* + * Forward protos... + */ +-static irqreturn_t mpt_interrupt(int irq, void *bus_id); + static int mptbase_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, + MPT_FRAME_HDR *reply); + static int mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes, +@@ -188,8 +189,8 @@ static int GetIoUnitPage2(MPT_ADAPTER *i + int mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode); + static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum); + static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum); +-static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc); +-static void mpt_read_ioc_pg_4(MPT_ADAPTER *ioc); ++static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc); ++static void mpt_read_ioc_pg_4(MPT_ADAPTER *ioc); + static void mpt_get_manufacturing_pg_0(MPT_ADAPTER *ioc); + static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch, + int sleepFlag); +@@ -220,11 +221,11 @@ static void mpt_inactive_raid_list_free( + static int __init fusion_init (void); + static void __exit fusion_exit (void); + +-#define CHIPREG_READ32(addr) readl_relaxed(addr) ++#define CHIPREG_READ32(addr) readl_relaxed(addr) + #define CHIPREG_READ32_dmasync(addr) readl(addr) +-#define CHIPREG_WRITE32(addr,val) writel(val, addr) ++#define CHIPREG_WRITE32(addr,val) writel(val, addr) + #define CHIPREG_PIO_WRITE32(addr,val) outl(val, (unsigned long)addr) +-#define CHIPREG_PIO_READ32(addr) inl((unsigned long)addr) ++#define CHIPREG_PIO_READ32(addr) inl((unsigned long)addr) + + static void + pci_disable_io_access(struct pci_dev *pdev) +@@ -246,6 +247,15 @@ pci_enable_io_access(struct pci_dev *pde + pci_write_config_word(pdev, PCI_COMMAND, command_reg); + } + ++/** ++ * mpt_set_debug_level - global setting of the mpt_debug_level ++ * found via /sys/module/mptbase/parameters/mpt_debug_level ++ * @val: ++ * @kp: ++ * ++ * Returns ++ **/ ++ + static int mpt_set_debug_level(const char *val, struct kernel_param *kp) + { + int ret = param_set_int(val, kp); +@@ -492,6 +502,9 @@ mpt_reply(MPT_ADAPTER *ioc, u32 pa) + mpt_sas_log_info(ioc, log_info); + } + ++ /* TODO - add shost_attrs, or command line option, and ++ * extend this to SAS/FC ++ */ + if (ioc_stat & MPI_IOCSTATUS_MASK) + mpt_iocstatus_info(ioc, (u32)ioc_stat, mf); + +@@ -782,6 +795,8 @@ mpt_device_driver_register(struct mpt_pc + + /* call per pci device probe entry point */ + list_for_each_entry(ioc, &ioc_list, list) { ++ if (!pci_get_drvdata(ioc->pcidev)) ++ continue; + id = ioc->pcidev->driver ? + ioc->pcidev->driver->id_table : NULL; + if (dd_cbfunc->probe) +@@ -914,7 +929,8 @@ mpt_put_msg_frame(u8 cb_idx, MPT_ADAPTER + + DBG_DUMP_PUT_MSG_FRAME(ioc, (u32 *)mf); + +- mf_dma_addr = (ioc->req_frames_low_dma + req_offset) | ioc->RequestNB[req_idx]; ++ mf_dma_addr = (ioc->req_frames_low_dma + req_offset) | ++ ioc->RequestNB[req_idx]; + dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mf_dma_addr=%x req_idx=%d " + "RequestNB=%x\n", ioc->name, mf_dma_addr, req_idx, + ioc->RequestNB[req_idx])); +@@ -1023,7 +1039,8 @@ mpt_add_sge_64bit(void *pAddr, u32 flags + } + + /** +- * mpt_add_sge_64bit_1078 - Place a simple 64 bit SGE at address pAddr (1078 workaround). ++ * mpt_add_sge_64bit_1078 - Place a simple 64 bit SGE at address pAddr ++ * (1078 workaround). + * @pAddr: virtual address for SGE + * @flagslength: SGE flags and data transfer length + * @dma_addr: Physical address +@@ -1140,7 +1157,7 @@ mpt_send_handshake_request(u8 cb_idx, MP + * is in proper (pre-alloc'd) request buffer range... + */ + ii = MFPTR_2_MPT_INDEX(ioc,(MPT_FRAME_HDR*)req); +- if (reqBytes >= 12 && ii >= 0 && ii < ioc->req_depth) { ++ if (ii >= 0 && ii < ioc->req_depth) { + MPT_FRAME_HDR *mf = (MPT_FRAME_HDR*)req; + mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(ii); + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = cb_idx; +@@ -1582,6 +1599,7 @@ mpt_get_product_name(u16 vendor, u16 dev + * @ioc: Pointer to pointer to IOC adapter + * + **/ ++#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10)) + static int + mpt_mapresources(MPT_ADAPTER *ioc) + { +@@ -1591,9 +1609,9 @@ mpt_mapresources(MPT_ADAPTER *ioc) + unsigned long port; + u32 msize; + u32 psize; +- u8 revision; + int r = -ENODEV; + struct pci_dev *pdev; ++ struct sysinfo s; + + pdev = ioc->pcidev; + ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM); +@@ -1608,22 +1626,21 @@ mpt_mapresources(MPT_ADAPTER *ioc) + return r; + } + +- pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); +- + if (sizeof(dma_addr_t) > 4) { +- const uint64_t required_mask = dma_get_required_mask +- (&pdev->dev); ++ uint64_t required_mask; ++ ++ required_mask = dma_get_required_mask(&pdev->dev); ++ + if (required_mask > DMA_BIT_MASK(32) + && !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) + && !pci_set_consistent_dma_mask(pdev, +- DMA_BIT_MASK(64))) { ++ DMA_BIT_MASK(64))) { + ioc->dma_mask = DMA_BIT_MASK(64); + dinitprintk(ioc, printk(MYIOC_s_INFO_FMT + ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n", + ioc->name)); + } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) +- && !pci_set_consistent_dma_mask(pdev, +- DMA_BIT_MASK(32))) { ++ && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + ioc->dma_mask = DMA_BIT_MASK(32); + dinitprintk(ioc, printk(MYIOC_s_INFO_FMT + ": 32 BIT PCI BUS DMA ADDRESSING SUPPORTED\n", +@@ -1635,8 +1652,7 @@ mpt_mapresources(MPT_ADAPTER *ioc) + } + } else { + if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) +- && !pci_set_consistent_dma_mask(pdev, +- DMA_BIT_MASK(32))) { ++ && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + ioc->dma_mask = DMA_BIT_MASK(32); + dinitprintk(ioc, printk(MYIOC_s_INFO_FMT + ": 32 BIT PCI BUS DMA ADDRESSING SUPPORTED\n", +@@ -1648,6 +1664,11 @@ mpt_mapresources(MPT_ADAPTER *ioc) + } + } + ++ si_meminfo(&s); ++ printk(MYIOC_s_INFO_FMT "%s BIT PCI BUS DMA ADDRESSING SUPPORTED, " ++ "total memory = %ld kB\n", ++ ioc->name, ioc->dma_mask == DMA_BIT_MASK(64) ? "64" : "32", ++ convert_to_kb(s.totalram)); + mem_phys = msize = 0; + port = psize = 0; + for (ii = 0; ii < DEVICE_COUNT_RESOURCE; ii++) { +@@ -1804,7 +1825,6 @@ mpt_attach(struct pci_dev *pdev, const s + /* Find lookup slot. */ + INIT_LIST_HEAD(&ioc->list); + +- + /* Initialize workqueue */ + INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work); + +@@ -1841,14 +1861,14 @@ mpt_attach(struct pci_dev *pdev, const s + case MPI_MANUFACTPAGE_DEVICEID_FC929X: + if (revision < XL_929) { + /* 929X Chip Fix. Set Split transactions level +- * for PCIX. Set MOST bits to zero. +- */ ++ * for PCIX. Set MOST bits to zero. ++ */ + pci_read_config_byte(pdev, 0x6a, &pcixcmd); + pcixcmd &= 0x8F; + pci_write_config_byte(pdev, 0x6a, pcixcmd); + } else { + /* 929XL Chip Fix. Set MMRBC to 0x08. +- */ ++ */ + pci_read_config_byte(pdev, 0x6a, &pcixcmd); + pcixcmd |= 0x08; + pci_write_config_byte(pdev, 0x6a, pcixcmd); +@@ -1948,7 +1968,6 @@ mpt_attach(struct pci_dev *pdev, const s + iounmap(ioc->memmap); + if (r != -5) + pci_release_selected_regions(pdev, ioc->bars); +- + destroy_workqueue(ioc->reset_work_q); + ioc->reset_work_q = NULL; + +@@ -2000,7 +2019,7 @@ mpt_attach(struct pci_dev *pdev, const s + void + mpt_detach(struct pci_dev *pdev) + { +- MPT_ADAPTER *ioc = pci_get_drvdata(pdev); ++ MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + char pname[32]; + u8 cb_idx; + unsigned long flags; +@@ -2273,6 +2292,15 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u3 + /* hard_reset_done = 0 if a soft reset was performed + * and 1 if a hard reset was performed. + */ ++ if (!hard_reset_done && reset_alt_ioc_active && ioc->alt_ioc) { ++ /* (re)Enable alt-IOC! (reply interrupt, FreeQ) */ ++ dprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": alt-ioc reply irq re-enabled\n", ++ ioc->alt_ioc->name)); ++ CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, MPI_HIM_DIM); ++ ioc->alt_ioc->active = 1; ++ reset_alt_ioc_active = 0; ++ } ++ + if (hard_reset_done && reset_alt_ioc_active && ioc->alt_ioc) { + if ((rc = MakeIocReady(ioc->alt_ioc, 0, sleepFlag)) == 0) + alt_ioc_ready = 1; +@@ -2472,7 +2500,7 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u3 + /* + * Initalize link list for inactive raid volumes. + */ +- mutex_init(&ioc->raid_data.inactive_list_mutex); ++ init_MUTEX(&ioc->raid_data.inactive_list_mutex); + INIT_LIST_HEAD(&ioc->raid_data.inactive_list); + + switch (ioc->bus_type) { +@@ -2641,13 +2669,12 @@ mpt_adapter_disable(MPT_ADAPTER *ioc) + if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_READY) + printk(MYIOC_s_ERR_FMT "%s: IOC msg unit " + "reset failed to put ioc in ready state!\n", +- ioc->name, __func__); ++ ioc->name, __FUNCTION__); + } else + printk(MYIOC_s_ERR_FMT "%s: IOC msg unit reset " +- "failed!\n", ioc->name, __func__); ++ "failed!\n", ioc->name, __FUNCTION__); + } + +- + /* Disable adapter interrupts! */ + synchronize_irq(ioc->pcidev->irq); + CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF); +@@ -2690,8 +2717,10 @@ mpt_adapter_disable(MPT_ADAPTER *ioc) + mpt_inactive_raid_list_free(ioc); + kfree(ioc->raid_data.pIocPg2); + kfree(ioc->raid_data.pIocPg3); ++ kfree(ioc->raid_data.pIocPg6); + ioc->spi_data.nvram = NULL; + ioc->raid_data.pIocPg3 = NULL; ++ ioc->raid_data.pIocPg6 = NULL; + + if (ioc->spi_data.pIocPg4 != NULL) { + sz = ioc->spi_data.IocPg4Sz; +@@ -2882,6 +2911,7 @@ MakeIocReady(MPT_ADAPTER *ioc, int force + */ + if ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) { + statefault = 2; ++ ioc->is_fault = 1; + printk(MYIOC_s_WARN_FMT "IOC is in FAULT state!!!\n", + ioc->name); + printk(MYIOC_s_WARN_FMT " FAULT code = %04xh\n", +@@ -3066,6 +3096,8 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepF + } + + facts->MsgVersion = le16_to_cpu(facts->MsgVersion); ++ if (facts->MsgVersion == MPI_VERSION_01_05) ++ facts->HeaderVersion = le16_to_cpu(facts->HeaderVersion); + facts->MsgContext = le32_to_cpu(facts->MsgContext); + facts->IOCExceptions = le16_to_cpu(facts->IOCExceptions); + facts->IOCStatus = le16_to_cpu(facts->IOCStatus); +@@ -3301,7 +3333,7 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepF + if (ioc->facts.MsgVersion >= MPI_VERSION_01_05) { + // set MsgVersion and HeaderVersion host driver was built with + ioc_init.MsgVersion = cpu_to_le16(MPI_VERSION); +- ioc_init.HeaderVersion = cpu_to_le16(MPI_HEADER_VERSION); ++ ioc_init.HeaderVersion = cpu_to_le16(MPI_HEADER_VERSION); + + if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_HOST_PAGE_BUFFER_PERSISTENT) { + ioc_init.HostPageBufferSGE = ioc->facts.HostPageBufferSGE; +@@ -3516,13 +3548,15 @@ mpt_do_upload(MPT_ADAPTER *ioc, int slee + u32 flagsLength; + int ii, sz, reply_sz; + int cmdStatus; +- int request_size; ++ int request_size; ++ + /* If the image size is 0, we are done. + */ +- if ((sz = ioc->facts.FWImageSize) == 0) ++ sz = ioc->facts.FWImageSize; ++ if (!sz) + return 0; + +- if (mpt_alloc_fw_memory(ioc, ioc->facts.FWImageSize) != 0) ++ if (mpt_alloc_fw_memory(ioc, sz) != 0) + return -ENOMEM; + + dinitprintk(ioc, printk(MYIOC_s_INFO_FMT ": FW Image @ %p[%p], sz=%d[%x] bytes\n", +@@ -3557,7 +3591,7 @@ mpt_do_upload(MPT_ADAPTER *ioc, int slee + ioc->SGE_size; + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending FW Upload " + " (req @ %p) fw_size=%d mf_request_size=%d\n", ioc->name, prequest, +- ioc->facts.FWImageSize, request_size)); ++ sz, request_size)); + DBG_DUMP_FW_REQUEST_FRAME(ioc, (u32 *)prequest); + + ii = mpt_handshake_req_reply_wait(ioc, request_size, (u32 *)prequest, +@@ -3574,9 +3608,9 @@ mpt_do_upload(MPT_ADAPTER *ioc, int slee + int status; + status = le16_to_cpu(preply->IOCStatus) & + MPI_IOCSTATUS_MASK; +- if (status == MPI_IOCSTATUS_SUCCESS && +- ioc->facts.FWImageSize == +- le32_to_cpu(preply->ActualImageSize)) ++ if ((status == MPI_IOCSTATUS_SUCCESS) && ++ (ioc->facts.FWImageSize == ++ le32_to_cpu(preply->ActualImageSize))) + cmdStatus = 0; + } + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": do_upload cmdStatus=%d \n", +@@ -3618,7 +3652,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, MpiFw + u32 diagRwData; + u32 nextImage; + u32 load_addr; +- u32 ioc_state=0; ++ u32 doorbell; + + ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "downloadboot: fw size 0x%x (%d), FW Ptr %p\n", + ioc->name, pFwHeader->ImageSize, pFwHeader->ImageSize, pFwHeader)); +@@ -3672,6 +3706,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, MpiFw + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); + + /* Set the DiagRwEn and Disable ARM bits */ ++ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, (MPI_DIAG_RW_ENABLE | MPI_DIAG_DISABLE_ARM)); + + fwSize = (pFwHeader->ImageSize + 3)/4; +@@ -3713,11 +3748,13 @@ mpt_downloadboot(MPT_ADAPTER *ioc, MpiFw + } + + /* Write the IopResetVectorRegAddr */ +- ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Write IopResetVector Addr=%x! \n", ioc->name, pFwHeader->IopResetRegAddr)); ++ ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Write IopResetVector Addr=%x! \n", ++ ioc->name, pFwHeader->IopResetRegAddr)); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, pFwHeader->IopResetRegAddr); + + /* Write the IopResetVectorValue */ +- ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Write IopResetVector Value=%x! \n", ioc->name, pFwHeader->IopResetVectorValue)); ++ ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Write IopResetVector Value=%x! \n", ++ ioc->name, pFwHeader->IopResetVectorValue)); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, pFwHeader->IopResetVectorValue); + + /* Clear the internal flash bad bit - autoincrementing register, +@@ -3734,17 +3771,6 @@ mpt_downloadboot(MPT_ADAPTER *ioc, MpiFw + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, diagRwData); + +- } else /* if((ioc->bus_type == SAS) || (ioc->bus_type == FC)) */ { +- diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); +- CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | +- MPI_DIAG_CLEAR_FLASH_BAD_SIG); +- +- /* wait 1 msec */ +- if (sleepFlag == CAN_SLEEP) { +- msleep (1); +- } else { +- mdelay (1); +- } + } + + if (ioc->errata_flag_1064) +@@ -3754,51 +3780,64 @@ mpt_downloadboot(MPT_ADAPTER *ioc, MpiFw + ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "downloadboot diag0val=%x, " + "turning off PREVENT_IOC_BOOT, DISABLE_ARM, RW_ENABLE\n", + ioc->name, diag0val)); +- diag0val &= ~(MPI_DIAG_PREVENT_IOC_BOOT | MPI_DIAG_DISABLE_ARM | MPI_DIAG_RW_ENABLE); ++ diag0val &= ~(MPI_DIAG_PREVENT_IOC_BOOT | MPI_DIAG_DISABLE_ARM); + ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "downloadboot now diag0val=%x\n", + ioc->name, diag0val)); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); + +- /* Write 0xFF to reset the sequencer */ +- CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); ++ if (ioc->bus_type == SAS ) { ++ /* wait 1 sec */ ++ if (sleepFlag == CAN_SLEEP) ++ msleep(1000); ++ else ++ mdelay(1000); + +- if (ioc->bus_type == SAS) { +- ioc_state = mpt_GetIocState(ioc, 0); +- if ( (GetIocFacts(ioc, sleepFlag, +- MPT_HOSTEVENT_IOC_BRINGUP)) != 0 ) { +- ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "GetIocFacts failed: IocState=%x\n", +- ioc->name, ioc_state)); +- return -EFAULT; ++ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); ++ ddlprintk(ioc, printk (MYIOC_s_DEBUG_FMT ++ "diag0val=%x, turning off RW_ENABLE\n", ioc->name, ++ diag0val)); ++ diag0val &= ~(MPI_DIAG_RW_ENABLE); ++ ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "now diag0val=%x\n", ioc->name, diag0val)); ++ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); ++ ++ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); ++ if (diag0val & MPI_DIAG_FLASH_BAD_SIG) { ++ diag0val |= MPI_DIAG_CLEAR_FLASH_BAD_SIG; ++ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); ++ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + } ++ diag0val &= ~(MPI_DIAG_DISABLE_ARM); ++ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); ++ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); ++ CHIPREG_WRITE32(&ioc->chip->DiagRwAddress, 0x3f000004); + } + +- for (count=0; countname, count, ioc_state)); +- if (ioc->bus_type == SAS) { ++ /* Write 0xFF to reset the sequencer */ ++ CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); ++ ++ for (count = 0; count < 30; count ++) { ++ doorbell = CHIPREG_READ32(&ioc->chip->Doorbell) & MPI_IOC_STATE_MASK; ++ if (doorbell == MPI_IOC_STATE_READY) { ++ if (ioc->bus_type == SAS) + return 0; +- } + if ((SendIocInit(ioc, sleepFlag)) != 0) { +- ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "downloadboot: SendIocInit failed\n", +- ioc->name)); ++ ddlprintk(ioc, printk(MYIOC_s_WARN_FMT ++ "SendIocInit failed\n", ioc->name)); + return -EFAULT; + } + ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "downloadboot: SendIocInit successful\n", +- ioc->name)); ++ "SendIocInit successful\n", ioc->name)); + return 0; + } +- if (sleepFlag == CAN_SLEEP) { +- msleep (10); +- } else { +- mdelay (10); +- } ++ ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "looking for READY STATE:" ++ " doorbell=%x count=%d\n", ioc->name, doorbell, count)); ++ if (sleepFlag == CAN_SLEEP) ++ msleep(1000); ++ else ++ mdelay(1000); + } +- ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "downloadboot failed! IocState=%x\n",ioc->name, ioc_state)); ++ ddlprintk(ioc, printk(MYIOC_s_WARN_FMT "downloadboot failed! count=%d\n", ioc->name, count)); + return -EFAULT; + } + +@@ -3853,6 +3892,7 @@ KickStart(MPT_ADAPTER *ioc, int force, i + if (hard_reset_done < 0) + return hard_reset_done; + ++ /* may not have worked but hard_reset_done doesn't always signal failure */ + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Diagnostic reset successful!\n", + ioc->name)); + +@@ -3861,7 +3901,7 @@ KickStart(MPT_ADAPTER *ioc, int force, i + ioc_state = mpt_GetIocState(ioc, 1); + if ((ioc_state == MPI_IOC_STATE_READY) || (ioc_state == MPI_IOC_STATE_OPERATIONAL)) { + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "KickStart successful! (cnt=%d)\n", +- ioc->name, cnt)); ++ ioc->name, cnt)); + return hard_reset_done; + } + if (sleepFlag == CAN_SLEEP) { +@@ -3899,7 +3939,7 @@ static int + mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag) + { + u32 diag0val; +- u32 doorbell; ++ u32 doorbell = 0; + int hard_reset_done = 0; + int count = 0; + u32 diag1val = 0; +@@ -3931,8 +3971,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + */ + for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { + if (MptResetHandlers[cb_idx]) +- (*(MptResetHandlers[cb_idx]))(ioc, +- MPT_IOC_PRE_RESET); ++ (*(MptResetHandlers[cb_idx]))(ioc, MPT_IOC_PRE_RESET); + } + + for (count = 0; count < 60; count ++) { +@@ -3941,29 +3980,39 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + + drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "looking for READY STATE: doorbell=%x" +- " count=%d\n", ++ " count=%d\n", + ioc->name, doorbell, count)); + + if (doorbell == MPI_IOC_STATE_READY) { + return 1; + } + ++ /* ++ * Early out for hard fault ++ */ ++ if (count && doorbell == MPI_IOC_STATE_FAULT) ++ break; ++ + /* wait 1 sec */ + if (sleepFlag == CAN_SLEEP) + msleep(1000); + else + mdelay(1000); + } ++ ++ if (doorbell != MPI_IOC_STATE_READY) ++ printk(MYIOC_s_ERR_FMT "Failed to come READY after " ++ "reset! IocState=%x", ioc->name, doorbell); + return -1; + } + + /* Use "Diagnostic reset" method! (only thing available!) */ + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + +- if (ioc->debug_level & MPT_DEBUG) { ++ if (ioc->debug_level & MPT_DEBUG_RESET) { + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); +- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DbG1: diag0=%08x, diag1=%08x\n", ++ drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DbG1: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); + } + +@@ -3999,14 +4048,14 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + +- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Wrote magic DiagWriteEn sequence (%x)\n", ++ drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Wrote magic DiagWriteEn sequence (%x)\n", + ioc->name, diag0val)); + } + +- if (ioc->debug_level & MPT_DEBUG) { ++ if (ioc->debug_level & MPT_DEBUG_RESET) { + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); +- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DbG2: diag0=%08x, diag1=%08x\n", ++ drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DbG2: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); + } + /* +@@ -4022,7 +4071,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + */ + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_RESET_ADAPTER); + hard_reset_done = 1; +- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Diagnostic reset performed\n", ++ drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Diagnostic reset performed\n", + ioc->name)); + + /* +@@ -4033,11 +4082,9 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + */ + for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { + if (MptResetHandlers[cb_idx]) { +- mpt_signal_reset(cb_idx, +- ioc, MPT_IOC_PRE_RESET); ++ mpt_signal_reset(cb_idx, ioc, MPT_IOC_PRE_RESET); + if (ioc->alt_ioc) { +- mpt_signal_reset(cb_idx, +- ioc->alt_ioc, MPT_IOC_PRE_RESET); ++ mpt_signal_reset(cb_idx, ioc->alt_ioc, MPT_IOC_PRE_RESET); + } + } + } +@@ -4059,7 +4106,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + break; + } + +- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "cached_fw: diag0val=%x count=%d\n", ++ drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "cached_fw: diag0val=%x count=%d\n", + ioc->name, diag0val, count)); + /* wait 1 sec */ + if (sleepFlag == CAN_SLEEP) { +@@ -4092,6 +4139,12 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + break; + } + ++ /* ++ * Early out for hard fault ++ */ ++ if (count && doorbell == MPI_IOC_STATE_FAULT) ++ break; ++ + /* wait 1 sec */ + if (sleepFlag == CAN_SLEEP) { + msleep (1000); +@@ -4108,10 +4161,10 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + } + + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); +- if (ioc->debug_level & MPT_DEBUG) { ++ if (ioc->debug_level & MPT_DEBUG_RESET) { + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); +- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DbG3: diag0=%08x, diag1=%08x\n", ++ drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DbG3: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); + } + +@@ -4167,11 +4220,11 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign + return -3; + } + +- if (ioc->debug_level & MPT_DEBUG) { ++ if (ioc->debug_level & MPT_DEBUG_RESET) { + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); +- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DbG4: diag0=%08x, diag1=%08x\n", +- ioc->name, diag0val, diag1val)); ++ drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DbG4: diag0=%08x, diag1=%08x\n", ++ ioc->name, diag0val, diag1val)); + } + + /* +@@ -4207,7 +4260,7 @@ SendIocReset(MPT_ADAPTER *ioc, u8 reset_ + drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending IOC reset(0x%02x)!\n", + ioc->name, reset_type)); + CHIPREG_WRITE32(&ioc->chip->Doorbell, reset_type<ReqToChain = (int *) mem; + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ReqToChain alloc @ %p, sz=%d bytes\n", +- ioc->name, mem, sz)); ++ ioc->name, mem, sz)); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + return -1; + + ioc->RequestNB = (int *) mem; + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RequestNB alloc @ %p, sz=%d bytes\n", +- ioc->name, mem, sz)); ++ ioc->name, mem, sz)); + } + for (ii = 0; ii < ioc->req_depth; ii++) { + ioc->ReqToChain[ii] = MPT_HOST_NO_CHAIN; +@@ -4345,7 +4398,7 @@ initChainBuffers(MPT_ADAPTER *ioc) + + ioc->ChainToChain = (int *) mem; + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ChainToChain alloc @ %p, sz=%d bytes\n", +- ioc->name, mem, sz)); ++ ioc->name, mem, sz)); + } else { + mem = (u8 *) ioc->ChainToChain; + } +@@ -4411,22 +4464,22 @@ PrimeIocFifos(MPT_ADAPTER *ioc) + + total_size = reply_sz = (ioc->reply_sz * ioc->reply_depth); + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ReplyBuffer sz=%d bytes, ReplyDepth=%d\n", +- ioc->name, ioc->reply_sz, ioc->reply_depth)); ++ ioc->name, ioc->reply_sz, ioc->reply_depth)); + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ReplyBuffer sz=%d[%x] bytes\n", +- ioc->name, reply_sz, reply_sz)); ++ ioc->name, reply_sz, reply_sz)); + + sz = (ioc->req_sz * ioc->req_depth); + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RequestBuffer sz=%d bytes, RequestDepth=%d\n", +- ioc->name, ioc->req_sz, ioc->req_depth)); ++ ioc->name, ioc->req_sz, ioc->req_depth)); + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RequestBuffer sz=%d[%x] bytes\n", +- ioc->name, sz, sz)); ++ ioc->name, sz, sz)); + total_size += sz; + + sz = num_chain * ioc->req_sz; /* chain buffer pool size */ + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ChainBuffer sz=%d bytes, ChainDepth=%d\n", +- ioc->name, ioc->req_sz, num_chain)); ++ ioc->name, ioc->req_sz, num_chain)); + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ChainBuffer sz=%d[%x] bytes num_chain=%d\n", +- ioc->name, sz, sz, num_chain)); ++ ioc->name, sz, sz, num_chain)); + + total_size += sz; + mem = pci_alloc_consistent(ioc->pcidev, total_size, &alloc_dma); +@@ -4437,7 +4490,7 @@ PrimeIocFifos(MPT_ADAPTER *ioc) + } + + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Total alloc @ %p[%p], sz=%d[%x] bytes\n", +- ioc->name, mem, (void *)(ulong)alloc_dma, total_size, total_size)); ++ ioc->name, mem, (void *)(ulong)alloc_dma, total_size, total_size)); + + memset(mem, 0, total_size); + ioc->alloc_total += total_size; +@@ -4448,7 +4501,7 @@ PrimeIocFifos(MPT_ADAPTER *ioc) + ioc->reply_frames_low_dma = (u32) (alloc_dma & 0xFFFFFFFF); + + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ReplyBuffers @ %p[%p]\n", +- ioc->name, ioc->reply_frames, (void *)(ulong)alloc_dma)); ++ ioc->name, ioc->reply_frames, (void *)(ulong)alloc_dma)); + + alloc_dma += reply_sz; + mem += reply_sz; +@@ -4459,7 +4512,7 @@ PrimeIocFifos(MPT_ADAPTER *ioc) + ioc->req_frames_dma = alloc_dma; + + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RequestBuffers @ %p[%p]\n", +- ioc->name, mem, (void *)(ulong)alloc_dma)); ++ ioc->name, mem, (void *)(ulong)alloc_dma)); + + ioc->req_frames_low_dma = (u32) (alloc_dma & 0xFFFFFFFF); + +@@ -4487,13 +4540,11 @@ PrimeIocFifos(MPT_ADAPTER *ioc) + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ChainBuffers @ %p(%p)\n", + ioc->name, ioc->ChainBuffer, (void *)(ulong)ioc->ChainBufferDMA)); + +- /* Initialize the free chain Q. +- */ ++ /* Initialize the free chain Q. */ + + INIT_LIST_HEAD(&ioc->FreeChainQ); + +- /* Post the chain buffers to the FreeChainQ. +- */ ++ /* Post the chain buffers to the FreeChainQ. */ + mem = (u8 *)ioc->ChainBuffer; + for (i=0; i < num_chain; i++) { + mf = (MPT_FRAME_HDR *) mem; +@@ -4530,15 +4581,14 @@ PrimeIocFifos(MPT_ADAPTER *ioc) + ioc->sense_buf_low_dma = (u32) (ioc->sense_buf_pool_dma & 0xFFFFFFFF); + ioc->alloc_total += sz; + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SenseBuffers @ %p[%p]\n", +- ioc->name, ioc->sense_buf_pool, (void *)(ulong)ioc->sense_buf_pool_dma)); ++ ioc->name, ioc->sense_buf_pool, (void *)(ulong)ioc->sense_buf_pool_dma)); + + } + +- /* Post Reply frames to FIFO +- */ ++ /* Post Reply frames to FIFO */ + alloc_dma = ioc->alloc_dma; + dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ReplyBuffers @ %p[%p]\n", +- ioc->name, ioc->reply_frames, (void *)(ulong)alloc_dma)); ++ ioc->name, ioc->reply_frames, (void *)(ulong)alloc_dma)); + + for (i = 0; i < ioc->reply_depth; i++) { + /* Write each address to the IOC! */ +@@ -4767,18 +4817,18 @@ WaitForDoorbellInt(MPT_ADAPTER *ioc, int + cntdn = 1000 * howlong; + if (sleepFlag == CAN_SLEEP) { + while (--cntdn) { ++ msleep(1); + intstat = CHIPREG_READ32(&ioc->chip->IntStatus); + if (intstat & MPI_HIS_DOORBELL_INTERRUPT) + break; +- msleep(1); + count++; + } + } else { + while (--cntdn) { ++ udelay (1000); + intstat = CHIPREG_READ32(&ioc->chip->IntStatus); + if (intstat & MPI_HIS_DOORBELL_INTERRUPT) + break; +- udelay (1000); + count++; + } + } +@@ -5015,7 +5065,7 @@ mptbase_sas_persist_operation(MPT_ADAPTE + MPT_FRAME_HDR *mf = NULL; + MPIHeader_t *mpi_hdr; + int ret = 0; +- unsigned long timeleft; ++ unsigned long timeleft; + + mutex_lock(&ioc->mptbase_cmds.mutex); + +@@ -5044,7 +5094,7 @@ mptbase_sas_persist_operation(MPT_ADAPTE + printk(KERN_DEBUG "%s: no msg frames!\n", __func__); + ret = -1; + goto out; +- } ++ } + + mpi_hdr = (MPIHeader_t *) mf; + sasIoUnitCntrReq = (SasIoUnitControlRequest_t *)mf; +@@ -5063,7 +5113,8 @@ mptbase_sas_persist_operation(MPT_ADAPTE + if (!timeleft) { + printk(KERN_DEBUG "%s: Issuing Reset from %s!!\n", + ioc->name, __func__); +- mpt_HardResetHandler(ioc, CAN_SLEEP); ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP) != 0) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_free_msg_frame(ioc, mf); + } + goto out; +@@ -5097,12 +5148,12 @@ static void + mptbase_raid_process_event_data(MPT_ADAPTER *ioc, + MpiEventDataRaid_t * pRaidEventData) + { +- int volume; +- int reason; +- int disk; +- int status; +- int flags; +- int state; ++ int volume; ++ int reason; ++ int disk; ++ int status; ++ int flags; ++ int state; + + volume = pRaidEventData->VolumeID; + reason = pRaidEventData->ReasonCode; +@@ -5198,8 +5249,8 @@ mptbase_raid_process_event_data(MPT_ADAP + : state == MPI_PHYSDISK0_STATUS_FAILED_REQUESTED + ? "failed requested" + : state == MPI_PHYSDISK0_STATUS_OTHER_OFFLINE +- ? "offline" +- : "state unknown", ++ ? "offline" ++ : "state unknown", + flags & MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC + ? ", out of sync" : "", + flags & MPI_PHYSDISK0_STATUS_FLAG_QUIESCED +@@ -5477,7 +5528,7 @@ mpt_GetScsiPortSettings(MPT_ADAPTER *ioc + */ + ioc->spi_data.bus_reset = + (le32_to_cpu(pPP2->PortFlags) & +- MPI_SCSIPORTPAGE2_PORT_FLAGS_AVOID_SCSI_RESET) ? ++ MPI_SCSIPORTPAGE2_PORT_FLAGS_AVOID_SCSI_RESET) ? + 0 : 1 ; + + /* Save the Port Page 2 data +@@ -5556,6 +5607,69 @@ mpt_readScsiDevicePageHeaders(MPT_ADAPTE + return 0; + } + ++static void ++mpt_read_ioc_pg_6(MPT_ADAPTER *ioc) ++{ ++ CONFIGPARMS cfg; ++ ConfigPageHeader_t header; ++ IOCPage6_t *pIoc6=NULL; ++ dma_addr_t ioc6_dma; ++ int iocpage6sz; ++ void *mem; ++ ++ /* Free the old page ++ */ ++ if (ioc->raid_data.pIocPg6) { ++ kfree(ioc->raid_data.pIocPg6); ++ ioc->raid_data.pIocPg6 = NULL; ++ } ++ ++ /* There is at least one physical disk. ++ * Read and save IOC Page 3 ++ */ ++ header.PageVersion = 0; ++ header.PageLength = 0; ++ header.PageNumber = 6; ++ header.PageType = MPI_CONFIG_PAGETYPE_IOC; ++ cfg.cfghdr.hdr = &header; ++ cfg.physAddr = -1; ++ cfg.pageAddr = 0; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.dir = 0; ++ cfg.timeout = 0; ++ if (mpt_config(ioc, &cfg) != 0) ++ goto out; ++ ++ if (header.PageLength == 0) ++ goto out; ++ ++ /* Read Header good, alloc memory ++ */ ++ iocpage6sz = header.PageLength * 4; ++ pIoc6 = pci_alloc_consistent(ioc->pcidev, iocpage6sz, &ioc6_dma); ++ if (!pIoc6) ++ goto out; ++ ++ /* Read the Page and save the data ++ * into malloc'd memory. ++ */ ++ cfg.physAddr = ioc6_dma; ++ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; ++ if (mpt_config(ioc, &cfg) != 0) ++ goto out; ++ ++ mem = kmalloc(iocpage6sz, GFP_ATOMIC); ++ if (!mem) ++ goto out; ++ ++ memcpy(mem, pIoc6, iocpage6sz); ++ ioc->raid_data.pIocPg6 = mem; ++ ++ out: ++ if (pIoc6) ++ pci_free_consistent(ioc->pcidev, iocpage6sz, pIoc6, ioc6_dma); ++} ++ + /** + * mpt_inactive_raid_list_free - This clears this link list. + * @ioc : pointer to per adapter structure +@@ -5568,13 +5682,13 @@ mpt_inactive_raid_list_free(MPT_ADAPTER + if (list_empty(&ioc->raid_data.inactive_list)) + return; + +- mutex_lock(&ioc->raid_data.inactive_list_mutex); ++ down(&ioc->raid_data.inactive_list_mutex); + list_for_each_entry_safe(component_info, pNext, + &ioc->raid_data.inactive_list, list) { + list_del(&component_info->list); + kfree(component_info); + } +- mutex_unlock(&ioc->raid_data.inactive_list_mutex); ++ up(&ioc->raid_data.inactive_list_mutex); + } + + /** +@@ -5591,10 +5705,12 @@ mpt_inactive_raid_volumes(MPT_ADAPTER *i + ConfigPageHeader_t hdr; + dma_addr_t dma_handle; + pRaidVolumePage0_t buffer = NULL; +- int i; +- RaidPhysDiskPage0_t phys_disk; ++ int i, j; ++ RaidPhysDiskPage0_t phys_disk; ++ RaidPhysDiskPage1_t *phys_disk_1; + struct inactive_raid_component_info *component_info; + int handle_inactive_volumes; ++ int num_paths, device_is_online; + + memset(&cfg, 0 , sizeof(CONFIGPARMS)); + memset(&hdr, 0 , sizeof(ConfigPageHeader_t)); +@@ -5633,12 +5749,35 @@ mpt_inactive_raid_volumes(MPT_ADAPTER *i + if (!handle_inactive_volumes) + goto out; + +- mutex_lock(&ioc->raid_data.inactive_list_mutex); ++ down(&ioc->raid_data.inactive_list_mutex); + for (i = 0; i < buffer->NumPhysDisks; i++) { + if(mpt_raid_phys_disk_pg0(ioc, + buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0) + continue; + ++ if (phys_disk.PhysDiskStatus.State != ++ MPI_PHYSDISK0_STATUS_ONLINE) ++ continue; ++ ++ /* check to see if device is online by checking phys_disk_pg1 */ ++ device_is_online = 0; ++ num_paths = mpt_raid_phys_disk_get_num_paths(ioc, ++ buffer->PhysDisk[i].PhysDiskNum); ++ if (num_paths < 2) ++ continue; ++ phys_disk_1 = kzalloc(offsetof(RaidPhysDiskPage1_t,Path) + ++ (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL); ++ if (!phys_disk_1) ++ continue; ++ mpt_raid_phys_disk_pg1(ioc, buffer->PhysDisk[i].PhysDiskNum, ++ phys_disk_1); ++ for (j = 0; j < num_paths && !device_is_online; j++) ++ if (!phys_disk_1->Path[j].Flags) ++ device_is_online = 1; ++ kfree(phys_disk_1); ++ if (!device_is_online) ++ continue; ++ + if ((component_info = kmalloc(sizeof (*component_info), + GFP_KERNEL)) == NULL) + continue; +@@ -5653,7 +5792,7 @@ mpt_inactive_raid_volumes(MPT_ADAPTER *i + list_add_tail(&component_info->list, + &ioc->raid_data.inactive_list); + } +- mutex_unlock(&ioc->raid_data.inactive_list_mutex); ++ up(&ioc->raid_data.inactive_list_mutex); + + out: + if (buffer) +@@ -5743,8 +5882,8 @@ mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, + int + mpt_raid_phys_disk_get_num_paths(MPT_ADAPTER *ioc, u8 phys_disk_num) + { +- CONFIGPARMS cfg; +- ConfigPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ ConfigPageHeader_t hdr; + dma_addr_t dma_handle; + pRaidPhysDiskPage1_t buffer = NULL; + int rc; +@@ -5795,7 +5934,6 @@ mpt_raid_phys_disk_get_num_paths(MPT_ADA + + return rc; + } +-EXPORT_SYMBOL(mpt_raid_phys_disk_get_num_paths); + + /** + * mpt_raid_phys_disk_pg1 - returns phys disk page 1 +@@ -5809,11 +5947,10 @@ EXPORT_SYMBOL(mpt_raid_phys_disk_get_num + * -ENOMEM if pci_alloc failed + **/ + int +-mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, u8 phys_disk_num, +- RaidPhysDiskPage1_t *phys_disk) ++mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, u8 phys_disk_num, RaidPhysDiskPage1_t *phys_disk) + { +- CONFIGPARMS cfg; +- ConfigPageHeader_t hdr; ++ CONFIGPARMS cfg; ++ ConfigPageHeader_t hdr; + dma_addr_t dma_handle; + pRaidPhysDiskPage1_t buffer = NULL; + int rc; +@@ -5863,17 +6000,14 @@ mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, + for (i = 0; i < phys_disk->NumPhysDiskPaths; i++) { + phys_disk->Path[i].PhysDiskID = buffer->Path[i].PhysDiskID; + phys_disk->Path[i].PhysDiskBus = buffer->Path[i].PhysDiskBus; +- phys_disk->Path[i].OwnerIdentifier = +- buffer->Path[i].OwnerIdentifier; ++ phys_disk->Path[i].OwnerIdentifier = buffer->Path[i].OwnerIdentifier; + phys_disk->Path[i].Flags = le16_to_cpu(buffer->Path[i].Flags); + memcpy(&sas_address, &buffer->Path[i].WWID, sizeof(__le64)); + sas_address = le64_to_cpu(sas_address); + memcpy(&phys_disk->Path[i].WWID, &sas_address, sizeof(__le64)); +- memcpy(&sas_address, +- &buffer->Path[i].OwnerWWID, sizeof(__le64)); ++ memcpy(&sas_address, &buffer->Path[i].OwnerWWID, sizeof(__le64)); + sas_address = le64_to_cpu(sas_address); +- memcpy(&phys_disk->Path[i].OwnerWWID, +- &sas_address, sizeof(__le64)); ++ memcpy(&phys_disk->Path[i].OwnerWWID, &sas_address, sizeof(__le64)); + } + + out: +@@ -5884,8 +6018,33 @@ mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, + + return rc; + } +-EXPORT_SYMBOL(mpt_raid_phys_disk_pg1); + ++/** ++ * mpt_sort_ioc_pg2 - compare function for sorting volumes ++ * in ascending order ++ * @a: ioc_pg2 raid volume page ++ * @b: ioc_pg2 raid volume page ++ * ++ * Return: ++ * 0 same, 1 (a is bigger), -1 (b is bigger) ++ **/ ++static int ++mpt_sort_ioc_pg2(const void *a, const void *b) ++{ ++ ConfigPageIoc2RaidVol_t * volume_a = (ConfigPageIoc2RaidVol_t *)a; ++ ConfigPageIoc2RaidVol_t * volume_b = (ConfigPageIoc2RaidVol_t *)b; ++ ++ if (volume_a->VolumeBus == volume_b->VolumeBus) { ++ if (volume_a->VolumeID == volume_b->VolumeID) ++ return 0; ++ if (volume_a->VolumeID < volume_b->VolumeID) ++ return -1; ++ return 1; ++ } ++ if (volume_a->VolumeBus < volume_b->VolumeBus) ++ return -1; ++ return 1; ++} + + /** + * mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes +@@ -5949,16 +6108,22 @@ mpt_findImVolumes(MPT_ADAPTER *ioc) + if (!mem) + goto out; + ++ /* ++ * sort volumes in ascending order ++ */ ++ sort(pIoc2->RaidVolume, pIoc2->NumActiveVolumes, ++ sizeof(ConfigPageIoc2RaidVol_t), mpt_sort_ioc_pg2, NULL); + memcpy(mem, (u8 *)pIoc2, iocpage2sz); + ioc->raid_data.pIocPg2 = (IOCPage2_t *) mem; + +- mpt_read_ioc_pg_3(ioc); +- + for (i = 0; i < pIoc2->NumActiveVolumes ; i++) + mpt_inactive_raid_volumes(ioc, + pIoc2->RaidVolume[i].VolumeBus, + pIoc2->RaidVolume[i].VolumeID); + ++ mpt_read_ioc_pg_3(ioc); ++ mpt_read_ioc_pg_6(ioc); ++ + out: + pci_free_consistent(ioc->pcidev, iocpage2sz, pIoc2, ioc2_dma); + +@@ -6118,6 +6283,9 @@ mpt_read_ioc_pg_1(MPT_ADAPTER *ioc) + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + if (mpt_config(ioc, &cfg) == 0) { + ++#if defined(CPQ_CIM) ++ ioc->pci_slot_number = pIoc1->PCISlotNum; ++#endif + tmp = le32_to_cpu(pIoc1->Flags) & MPI_IOCPAGE1_REPLY_COALESCING; + if (tmp == MPI_IOCPAGE1_REPLY_COALESCING) { + tmp = le32_to_cpu(pIoc1->CoalescingTimeout); +@@ -6294,9 +6462,9 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS + long timeout; + int ret; + u8 page_type = 0, extend_page; +- unsigned long timeleft; ++ unsigned long timeleft; + unsigned long flags; +- int in_isr; ++ int in_isr; + u8 issue_hard_reset = 0; + u8 retry_count = 0; + +@@ -6308,7 +6476,7 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS + dcprintk(ioc, printk(MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n", + ioc->name)); + return -EPERM; +- } ++ } + + /* don't send a config page during diag reset */ + spin_lock_irqsave(&ioc->taskmgmt_lock, flags); +@@ -6447,7 +6615,7 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS + dcprintk(ioc, printk(KERN_DEBUG "IOCStatus=%04xh, IOCLogInfo=%08xh\n", + ret, le32_to_cpu(pReply->IOCLogInfo))); + +-out: ++ out: + + CLEAR_MGMT_STATUS(ioc->mptbase_cmds.status) + mutex_unlock(&ioc->mptbase_cmds.mutex); +@@ -6455,7 +6623,8 @@ out: + issue_hard_reset = 0; + printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", + ioc->name, __func__); +- mpt_HardResetHandler(ioc, CAN_SLEEP); ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP) != 0) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_free_msg_frame(ioc, mf); + /* attempt one retry for a timed out command */ + if (!retry_count) { +@@ -6469,7 +6638,6 @@ out: + } + } + return ret; +- + } + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +@@ -6823,7 +6991,7 @@ mpt_print_ioc_summary(MPT_ADAPTER *ioc, + *size = y; + } + /** +- * mpt_set_taskmgmt_in_progress_flag - set flags associated with task management ++ * mpt_set_taskmgmt_in_progress_flag - set flags associated with task managment + * @ioc: Pointer to MPT_ADAPTER structure + * + * Returns 0 for SUCCESS or -1 if FAILED. +@@ -6840,6 +7008,7 @@ mpt_set_taskmgmt_in_progress_flag(MPT_AD + if (ioc->ioc_reset_in_progress || ioc->taskmgmt_in_progress || + (ioc->alt_ioc && ioc->alt_ioc->taskmgmt_in_progress)) { + retval = -1; ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + goto out; + } + retval = 0; +@@ -6849,14 +7018,14 @@ mpt_set_taskmgmt_in_progress_flag(MPT_AD + ioc->alt_ioc->taskmgmt_in_progress = 1; + ioc->alt_ioc->taskmgmt_quiesce_io = 1; + } +- out: + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ ++ out: + return retval; + } +-EXPORT_SYMBOL(mpt_set_taskmgmt_in_progress_flag); + + /** +- * mpt_clear_taskmgmt_in_progress_flag - clear flags associated with task management ++ * mpt_clear_taskmgmt_in_progress_flag - clear flags associated with task managment + * @ioc: Pointer to MPT_ADAPTER structure + * + **/ +@@ -6874,8 +7043,6 @@ mpt_clear_taskmgmt_in_progress_flag(MPT_ + } + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + } +-EXPORT_SYMBOL(mpt_clear_taskmgmt_in_progress_flag); +- + + /** + * mpt_halt_firmware - Halts the firmware if it is operational and panic +@@ -6893,20 +7060,173 @@ mpt_halt_firmware(MPT_ADAPTER *ioc) + if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) { + printk(MYIOC_s_ERR_FMT "IOC is in FAULT state (%04xh)!!!\n", + ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK); ++ if(mpt_fwfault_debug == 2) ++ for(;;); ++ else + panic("%s: IOC Fault (%04xh)!!!\n", ioc->name, + ioc_raw_state & MPI_DOORBELL_DATA_MASK); + } else { + CHIPREG_WRITE32(&ioc->chip->Doorbell, 0xC0FFEE00); +- panic("%s: Firmware is halted due to command timeout\n", +- ioc->name); ++ if(mpt_fwfault_debug == 2) { ++ printk("%s: Firmware is halted due to command timeout\n" ++ ,ioc->name); ++ for(;;); ++ } ++ else ++ panic("%s: Firmware is halted due to command timeout\n", ++ ioc->name); + } + } +-EXPORT_SYMBOL(mpt_halt_firmware); + +-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +-/* +- * Reset Handling +- */ ++/** ++ * mpt_SoftResetHandler - Issues a less expensive reset ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @sleepFlag: Indicates if sleep or schedule must be called. ++ ++ * ++ * Returns 0 for SUCCESS or -1 if FAILED. ++ * ++ * Message Unit Reset - instructs the IOC to reset the Reply Post and ++ * Free FIFO's. All the Message Frames on Reply Free FIFO are discarded. ++ * All posted buffers are freed, and event notification is turned off. ++ * IOC doesnt reply to any outstanding request. This will transfer IOC ++ * to READY state. ++ **/ ++int ++mpt_SoftResetHandler(MPT_ADAPTER *ioc, int sleepFlag) ++{ ++ int rc; ++ int ii; ++ u8 cb_idx; ++ unsigned long flags; ++ u32 ioc_state; ++ unsigned long time_count; ++ int i; ++ ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SoftResetHandler Entered!\n", ioc->name)); ++ ++ ioc_state = mpt_GetIocState(ioc, 0) & MPI_IOC_STATE_MASK; ++ ++ if(mpt_fwfault_debug) ++ mpt_halt_firmware(ioc); ++ ++ if (ioc_state == MPI_IOC_STATE_FAULT || ioc_state == MPI_IOC_STATE_RESET) { ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "skipping, either in FAULT or RESET state!\n", ioc->name)); ++ return -1; ++ } ++ ++ if (ioc->bus_type == FC) { ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "skipping, because the bus type is FC!\n", ioc->name)); ++ return -1; ++ } ++ ++ spin_lock_irqsave(&ioc->taskmgmt_lock, flags); ++ if (ioc->ioc_reset_in_progress) { ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ return -1; ++ } ++ ioc->ioc_reset_in_progress = 1; ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ ++ rc = -1; ++ ++ for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { ++ if (MptResetHandlers[cb_idx]) ++ mpt_signal_reset(cb_idx, ioc, MPT_IOC_SETUP_RESET); ++ } ++ ++ spin_lock_irqsave(&ioc->taskmgmt_lock, flags); ++ if (ioc->taskmgmt_in_progress) { ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ return -1; ++ } ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ /* Disable reply interrupts (also blocks FreeQ) */ ++ CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF); ++ ioc->active = 0; ++ time_count = jiffies; ++ ++ rc = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag); ++ ++ for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { ++ if (MptResetHandlers[cb_idx]) ++ mpt_signal_reset(cb_idx, ioc, MPT_IOC_PRE_RESET); ++ } ++ ++ if (rc) ++ goto out; ++ ++ ioc_state = mpt_GetIocState(ioc, 0) & MPI_IOC_STATE_MASK; ++ if (ioc_state != MPI_IOC_STATE_READY) ++ goto out; ++ ++ for (ii = 0; ii < 5; ii++) { ++ /* Get IOC facts! Allow 5 retries */ ++ if ((rc = GetIocFacts(ioc, sleepFlag, ++ MPT_HOSTEVENT_IOC_RECOVER)) == 0) ++ break; ++ if (sleepFlag == CAN_SLEEP) { ++ msleep(100); ++ } else { ++ mdelay(100); ++ } ++ } ++ if (ii == 5) ++ goto out; ++ ++ if ((rc = PrimeIocFifos(ioc)) != 0) ++ goto out; ++ ++ if ((rc = SendIocInit(ioc, sleepFlag)) != 0) ++ goto out; ++ ++ if ((rc = SendEventNotification(ioc, 1, sleepFlag)) != 0) ++ goto out; ++ ++ if (ioc->hard_resets < -1) ++ ioc->hard_resets++; ++ ++ /* ++ * At this point, we know soft reset succeeded. ++ */ ++ ++ ioc->active = 1; ++ CHIPREG_WRITE32(&ioc->chip->IntMask, MPI_HIM_DIM); ++ ++ out: ++ spin_lock_irqsave(&ioc->taskmgmt_lock, flags); ++ ioc->ioc_reset_in_progress = 0; ++ ioc->taskmgmt_quiesce_io = 0; ++ ioc->taskmgmt_in_progress = 0; ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ ++ if (ioc->active) { /* otherwise, hard reset coming */ ++ for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { ++ if (MptResetHandlers[cb_idx]) ++ mpt_signal_reset(cb_idx, ioc, MPT_IOC_POST_RESET); ++ } ++ } ++ /* ++ * Cleanup diag buffer allocated memory ++ */ ++ for (i = 0; i < MPI_DIAG_BUF_TYPE_COUNT; i++) { ++ if (ioc->DiagBuffer[i] == NULL) ++ continue; ++ pci_free_consistent(ioc->pcidev, ioc->DiagBuffer_sz[i], ++ ioc->DiagBuffer[i], ioc->DiagBuffer_dma[i]); ++ ioc->DiagBuffer[i] = NULL; ++ ioc->DiagBuffer_Status[i] = 0; ++ } ++ ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SoftResetHandler: completed (%d seconds): %s\n", ++ ioc->name, jiffies_to_msecs(jiffies - time_count)/1000, ++ ((rc == 0) ? "SUCCESS" : "FAILED"))); ++ ++ return rc; ++} ++ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * mpt_HardResetHandler - Generic reset handler +@@ -6931,7 +7251,7 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, i + u8 cb_idx; + unsigned long flags; + unsigned long time_count; +- ++ int i; + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "HardResetHandler Entered!\n", ioc->name)); + #ifdef MFCNT + printk(MYIOC_s_INFO_FMT "HardResetHandler Entered!\n", ioc->name); +@@ -6969,22 +7289,23 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, i + } + + time_count = jiffies; +- rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag); +- if (rc != 0) { +- printk(KERN_WARNING MYNAM +- ": WARNING - (%d) Cannot recover %s\n", rc, ioc->name); ++ if ((rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag)) != 0) { ++ printk(KERN_WARNING MYNAM ": WARNING - (%d) Cannot recover %s\n", ++ rc, ioc->name); + } else { + if (ioc->hard_resets < -1) + ioc->hard_resets++; + } + + spin_lock_irqsave(&ioc->taskmgmt_lock, flags); +- ioc->ioc_reset_in_progress = 0; ++ if (ioc->is_fault == 1) ++ ioc->is_fault = 2; + ioc->taskmgmt_quiesce_io = 0; ++ ioc->ioc_reset_in_progress = 0; + ioc->taskmgmt_in_progress = 0; + if (ioc->alt_ioc) { +- ioc->alt_ioc->ioc_reset_in_progress = 0; + ioc->alt_ioc->taskmgmt_quiesce_io = 0; ++ ioc->alt_ioc->ioc_reset_in_progress = 0; + ioc->alt_ioc->taskmgmt_in_progress = 0; + } + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); +@@ -6993,11 +7314,22 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, i + if (MptResetHandlers[cb_idx]) { + mpt_signal_reset(cb_idx, ioc, MPT_IOC_POST_RESET); + if (ioc->alt_ioc) +- mpt_signal_reset(cb_idx, +- ioc->alt_ioc, MPT_IOC_POST_RESET); ++ mpt_signal_reset(cb_idx, ioc->alt_ioc, MPT_IOC_POST_RESET); + } + } + ++ /* ++ * Cleanup diag buffer allocated memory ++ */ ++ for (i = 0; i < MPI_DIAG_BUF_TYPE_COUNT; i++) { ++ if (ioc->DiagBuffer[i] == NULL) ++ continue; ++ pci_free_consistent(ioc->pcidev, ioc->DiagBuffer_sz[i], ++ ioc->DiagBuffer[i], ioc->DiagBuffer_dma[i]); ++ ioc->DiagBuffer[i] = NULL; ++ ioc->DiagBuffer_Status[i] = 0; ++ } ++ + dtmprintk(ioc, + printk(MYIOC_s_DEBUG_FMT + "HardResetHandler: completed (%d seconds): %s\n", ioc->name, +@@ -7150,6 +7482,11 @@ mpt_display_event_info(MPT_ADAPTER *ioc, + "SAS Device Status Change: Internal Device " + "Reset : id=%d channel=%d", id, channel); + break; ++ case MPI_EVENT_SAS_DEV_STAT_RC_CMPL_INTERNAL_DEV_RESET: ++ snprintf(evStr, EVENT_DESCR_STR_SZ, ++ "SAS Device Status Change: Internal Device " ++ "Reset Completed: id=%d channel=%d", id, channel); ++ break; + case MPI_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL: + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS Device Status Change: Internal Task " +@@ -7170,6 +7507,11 @@ mpt_display_event_info(MPT_ADAPTER *ioc, + "SAS Device Status Change: Internal Query " + "Task : id=%d channel=%d", id, channel); + break; ++ case MPI_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION: ++ snprintf(evStr, EVENT_DESCR_STR_SZ, ++ "SAS Device Status Change: Async Notification " ++ "Task : id=%d channel=%d", id, channel); ++ break; + default: + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS Device Status Change: Unknown: " +@@ -7336,12 +7678,28 @@ mpt_display_event_info(MPT_ADAPTER *ioc, + { + u8 phy_num = (u8)(evData0); + u8 port_num = (u8)(evData0 >> 8); +- u8 port_width = (u8)(evData0 >> 16); ++ u8 num_phys = (u8)(evData0 >> 16); + u8 primative = (u8)(evData0 >> 24); ++ char *primative_str = NULL; ++ ++ switch (primative) { ++ case MPI_EVENT_PRIMITIVE_CHANGE: ++ primative_str = "change"; ++ break; ++ case MPI_EVENT_PRIMITIVE_EXPANDER: ++ primative_str = "expander"; ++ break; ++ case MPI_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT: ++ primative_str = "asyn event"; ++ break; ++ default: ++ primative_str = "reserved"; ++ break; ++ } + snprintf(evStr, EVENT_DESCR_STR_SZ, +- "SAS Broadcase Primative: phy=%d port=%d " +- "width=%d primative=0x%02x", +- phy_num, port_num, port_width, primative); ++ "SAS Broadcast Primative: phy=%d port=%d " ++ "num_phys=%d primative=%s (0x%02x)", ++ phy_num, port_num, num_phys, primative_str, primative); + break; + } + +@@ -7712,7 +8070,7 @@ mpt_spi_log_info(MPT_ADAPTER *ioc, u32 l + "IO Not Yet Executed", /* 13h */ + "IO Executed", /* 14h */ + "Persistent Reservation Out Not Affiliation " +- "Owner", /* 15h */ ++ "Owner", /* 15h */ + "Open Transmit DMA Abort", /* 16h */ + "IO Device Missing Delay Retry", /* 17h */ + "IO Cancelled Due to Recieve Error", /* 18h */ +@@ -7737,19 +8095,19 @@ mpt_spi_log_info(MPT_ADAPTER *ioc, u32 l + NULL /* 07h */ + }; + static char *raid_sub_code_str[] = { +- NULL, /* 00h */ ++ NULL, /* 00h */ + "Volume Creation Failed: Data Passed too " +- "Large", /* 01h */ ++ "Large", /* 01h */ + "Volume Creation Failed: Duplicate Volumes " +- "Attempted", /* 02h */ ++ "Attempted", /* 02h */ + "Volume Creation Failed: Max Number " + "Supported Volumes Exceeded", /* 03h */ + "Volume Creation Failed: DMA Error", /* 04h */ + "Volume Creation Failed: Invalid Volume Type", /* 05h */ + "Volume Creation Failed: Error Reading " +- "MFG Page 4", /* 06h */ ++ "MFG Page 4", /* 06h */ + "Volume Creation Failed: Creating Internal " +- "Structures", /* 07h */ ++ "Structures", /* 07h */ + NULL, /* 08h */ + NULL, /* 09h */ + NULL, /* 0Ah */ +@@ -7758,12 +8116,12 @@ mpt_spi_log_info(MPT_ADAPTER *ioc, u32 l + NULL, /* 0Dh */ + NULL, /* 0Eh */ + NULL, /* 0Fh */ +- "Activation failed: Already Active Volume", /* 10h */ +- "Activation failed: Unsupported Volume Type", /* 11h */ +- "Activation failed: Too Many Active Volumes", /* 12h */ +- "Activation failed: Volume ID in Use", /* 13h */ +- "Activation failed: Reported Failure", /* 14h */ +- "Activation failed: Importing a Volume", /* 15h */ ++ "Activation failed: Already Active Volume", /* 10h */ ++ "Activation failed: Unsupported Volume Type", /* 11h */ ++ "Activation failed: Too Many Active Volumes", /* 12h */ ++ "Activation failed: Volume ID in Use", /* 13h */ ++ "Activation failed: Reported Failure", /* 14h */ ++ "Activation failed: Importing a Volume", /* 15h */ + NULL, /* 16h */ + NULL, /* 17h */ + NULL, /* 18h */ +@@ -7774,12 +8132,12 @@ mpt_spi_log_info(MPT_ADAPTER *ioc, u32 l + NULL, /* 1Dh */ + NULL, /* 1Eh */ + NULL, /* 1Fh */ +- "Phys Disk failed: Too Many Phys Disks", /* 20h */ ++ "Phys Disk failed: Too Many Phys Disks", /* 20h */ + "Phys Disk failed: Data Passed too Large", /* 21h */ +- "Phys Disk failed: DMA Error", /* 22h */ +- "Phys Disk failed: Invalid ", /* 23h */ ++ "Phys Disk failed: DMA Error", /* 22h */ ++ "Phys Disk failed: Invalid ", /* 23h */ + "Phys Disk failed: Creating Phys Disk Config " +- "Page", /* 24h */ ++ "Page", /* 24h */ + NULL, /* 25h */ + NULL, /* 26h */ + NULL, /* 27h */ +@@ -7797,22 +8155,22 @@ mpt_spi_log_info(MPT_ADAPTER *ioc, u32 l + "Device ", /* 32h */ + "Compatibility Error: Removable Device Found", /* 33h */ + "Compatibility Error: Device SCSI Version not " +- "2 or Higher", /* 34h */ ++ "2 or Higher", /* 34h */ + "Compatibility Error: SATA Device, 48 BIT LBA " +- "not Supported", /* 35h */ ++ "not Supported", /* 35h */ + "Compatibility Error: Device doesn't have " +- "512 Byte Block Sizes", /* 36h */ ++ "512 Byte Block Sizes", /* 36h */ + "Compatibility Error: Volume Type Check Failed", /* 37h */ + "Compatibility Error: Volume Type is " +- "Unsupported by FW", /* 38h */ ++ "Unsupported by FW", /* 38h */ + "Compatibility Error: Disk Drive too Small for " +- "use in Volume", /* 39h */ ++ "use in Volume", /* 39h */ + "Compatibility Error: Phys Disk for Create " +- "Volume not Found", /* 3Ah */ ++ "Volume not Found", /* 3Ah */ + "Compatibility Error: Too Many or too Few " +- "Disks for Volume Type", /* 3Bh */ ++ "Disks for Volume Type", /* 3Bh */ + "Compatibility Error: Disk stripe Sizes " +- "Must be 64KB", /* 3Ch */ ++ "Must be 64KB", /* 3Ch */ + "Compatibility Error: IME Size Limited to < 2TB", /* 3Dh */ + }; + +@@ -8210,6 +8568,7 @@ EXPORT_SYMBOL(mpt_resume); + EXPORT_SYMBOL(mpt_suspend); + #endif + EXPORT_SYMBOL(ioc_list); ++EXPORT_SYMBOL(mpt_proc_root_dir); + EXPORT_SYMBOL(mpt_register); + EXPORT_SYMBOL(mpt_deregister); + EXPORT_SYMBOL(mpt_event_register); +@@ -8227,13 +8586,18 @@ EXPORT_SYMBOL(mpt_verify_adapter); + EXPORT_SYMBOL(mpt_GetIocState); + EXPORT_SYMBOL(mpt_print_ioc_summary); + EXPORT_SYMBOL(mpt_HardResetHandler); ++EXPORT_SYMBOL(mpt_SoftResetHandler); + EXPORT_SYMBOL(mpt_config); + EXPORT_SYMBOL(mpt_findImVolumes); + EXPORT_SYMBOL(mpt_alloc_fw_memory); + EXPORT_SYMBOL(mpt_free_fw_memory); + EXPORT_SYMBOL(mptbase_sas_persist_operation); + EXPORT_SYMBOL(mpt_raid_phys_disk_pg0); +- ++EXPORT_SYMBOL(mpt_raid_phys_disk_pg1); ++EXPORT_SYMBOL(mpt_raid_phys_disk_get_num_paths); ++EXPORT_SYMBOL(mpt_set_taskmgmt_in_progress_flag); ++EXPORT_SYMBOL(mpt_clear_taskmgmt_in_progress_flag); ++EXPORT_SYMBOL(mpt_halt_firmware); + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * fusion_init - Fusion MPT base driver initialization routine. +--- a/drivers/message/fusion/mptbase.h ++++ b/drivers/message/fusion/mptbase.h +@@ -49,10 +49,6 @@ + #define MPTBASE_H_INCLUDED + /*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +-#include +-#include +-#include +- + #include "lsi/mpi_type.h" + #include "lsi/mpi.h" /* Fusion MPI(nterface) basic defs */ + #include "lsi/mpi_ioc.h" /* Fusion MPT IOC(ontroller) defs */ +@@ -76,9 +72,13 @@ + #define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR + #endif + +-#define MPT_LINUX_VERSION_COMMON "3.04.13" +-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.13" ++#define MPT_LINUX_VERSION_COMMON "4.22.00.00" ++#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-4.22.00.00" + #define WHAT_MAGIC_STRING "@" "(" "#" ")" ++#define MPT_LINUX_MAJOR_VERSION 4 ++#define MPT_LINUX_MINOR_VERSION 22 ++#define MPT_LINUX_BUILD_VERSION 00 ++#define MPT_LINUX_RELEASE_VERSION 00 + + #define show_mptmod_ver(s,ver) \ + printk(KERN_INFO "%s %s\n", s, ver); +@@ -87,6 +87,8 @@ + /* + * Fusion MPT(linux) driver configurable stuff... + */ ++#define MPT_POLLING_INTERVAL 1000 /* in milliseconds */ ++ + #define MPT_MAX_ADAPTERS 18 + #define MPT_MAX_PROTOCOL_DRIVERS 16 + #define MPT_MAX_BUS 1 /* Do not change */ +@@ -135,7 +137,6 @@ + + #define MPT_COALESCING_TIMEOUT 0x10 + +- + /* + * SCSI transfer rate defines. + */ +@@ -164,10 +165,10 @@ + /* + * Set the MAX_SGE value based on user input. + */ +-#ifdef CONFIG_FUSION_MAX_SGE +-#if CONFIG_FUSION_MAX_SGE < 16 ++#ifdef CONFIG_FUSION_MAX_SGE ++#if CONFIG_FUSION_MAX_SGE < 16 + #define MPT_SCSI_SG_DEPTH 16 +-#elif CONFIG_FUSION_MAX_SGE > 128 ++#elif CONFIG_FUSION_MAX_SGE > 128 + #define MPT_SCSI_SG_DEPTH 128 + #else + #define MPT_SCSI_SG_DEPTH CONFIG_FUSION_MAX_SGE +@@ -176,10 +177,10 @@ + #define MPT_SCSI_SG_DEPTH 40 + #endif + +-#ifdef CONFIG_FUSION_MAX_FC_SGE +-#if CONFIG_FUSION_MAX_FC_SGE < 16 ++#ifdef CONFIG_FUSION_MAX_FC_SGE ++#if CONFIG_FUSION_MAX_FC_SGE < 16 + #define MPT_SCSI_FC_SG_DEPTH 16 +-#elif CONFIG_FUSION_MAX_FC_SGE > 256 ++#elif CONFIG_FUSION_MAX_FC_SGE > 256 + #define MPT_SCSI_FC_SG_DEPTH 256 + #else + #define MPT_SCSI_FC_SG_DEPTH CONFIG_FUSION_MAX_FC_SGE +@@ -189,9 +190,8 @@ + #endif + + /* debug print string length used for events and iocstatus */ +-# define EVENT_DESCR_STR_SZ 100 ++# define EVENT_DESCR_STR_SZ 100 + +-#define MPT_POLLING_INTERVAL 1000 /* in milliseconds */ + + #ifdef __KERNEL__ /* { */ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +@@ -239,7 +239,6 @@ typedef struct _ATTO_CONFIG_PAGE_SCSI_PO + } fATTO_CONFIG_PAGE_SCSI_PORT_2, MPI_POINTER PTR_ATTO_CONFIG_PAGE_SCSI_PORT_2, + ATTO_SCSIPortPage2_t, MPI_POINTER pATTO_SCSIPortPage2_t; + +- + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /* + * MPT protocol driver defs... +@@ -369,6 +368,31 @@ typedef struct _SYSIF_REGS + * in conjunction with SYSIF_REGS accesses! + */ + ++/* ++ * End to End Data Protection Support ++ */ ++#define EEDP_SUPPORT ++#ifdef EEDP_SUPPORT ++ ++#define PRO_R MPI_SCSIIO32_EEDPFLAGS_CHKRM_OP ++#define PRO_W MPI_SCSIIO32_EEDPFLAGS_INSERT_OP ++#define PRO_V MPI_SCSIIO32_EEDPFLAGS_INSERT_OP ++ ++/* the read capacity 16 byte parameter block - defined in SBC-3 */ ++struct read_cap_parameter{ ++ u64 logical_block_addr; ++ u32 logical_block_length; ++ u8 prot_en:1; ++ u8 p_type:3; ++ u8 reserved0:4; ++ u8 logical_blocks_per_phyical_block:4; ++ u8 reserved1:4; ++ u16 lowest_aligned_log_block_address:14; ++ u16 reserved2:2; ++ u8 reserved3[16]; ++}; ++#endif ++ + + /* + * Dynamic Multi-Pathing specific stuff... +@@ -378,7 +402,7 @@ typedef struct _SYSIF_REGS + #define MPT_TARGET_NO_NEGO_WIDE 0x01 + #define MPT_TARGET_NO_NEGO_SYNC 0x02 + #define MPT_TARGET_NO_NEGO_QAS 0x04 +-#define MPT_TAPE_NEGO_IDP 0x08 ++#define MPT_TAPE_NEGO_IDP 0x08 + + /* + * VirtDevice - FC LUN device or SCSI target device +@@ -387,8 +411,8 @@ typedef struct _VirtTarget { + struct scsi_target *starget; + u8 tflags; + u8 ioc_id; +- u8 id; +- u8 channel; ++ u8 id; /* logical target id */ ++ u8 channel; /* logical channel number */ + u8 minSyncFactor; /* 0xFF is async */ + u8 maxOffset; /* 0 if async */ + u8 maxWidth; /* 0 if narrow, 1 if wide */ +@@ -396,13 +420,18 @@ typedef struct _VirtTarget { + u8 raidVolume; /* set, if RAID Volume */ + u8 type; /* byte 0 of Inquiry data */ + u8 deleted; /* target in process of being removed */ +- u32 num_luns; ++ int num_luns; + } VirtTarget; + + typedef struct _VirtDevice { + VirtTarget *vtarget; + u8 configured_lun; + int lun; ++#ifdef EEDP_SUPPORT ++ u8 eedp_enable; ++ u8 eedp_type; ++ u32 eedp_block_length; ++#endif + } VirtDevice; + + /* +@@ -442,21 +471,14 @@ do { \ + } while (0) + + +-/* +- * IOCTL structure and associated defines +- */ +- +-#define MPTCTL_RESET_OK 0x01 /* Issue Bus Reset */ +- + #define MPT_MGMT_STATUS_RF_VALID 0x01 /* The Reply Frame is VALID */ + #define MPT_MGMT_STATUS_COMMAND_GOOD 0x02 /* Command Status GOOD */ + #define MPT_MGMT_STATUS_PENDING 0x04 /* command is pending */ +-#define MPT_MGMT_STATUS_DID_IOCRESET 0x08 /* IOC Reset occurred +- on the current*/ ++#define MPT_MGMT_STATUS_DID_IOCRESET 0x08 /* IOC Reset occurred on the current*/ + #define MPT_MGMT_STATUS_SENSE_VALID 0x10 /* valid sense info */ + #define MPT_MGMT_STATUS_TIMER_ACTIVE 0x20 /* obsolete */ +-#define MPT_MGMT_STATUS_FREE_MF 0x40 /* free the mf from +- complete routine */ ++#define MPT_MGMT_STATUS_FREE_MF 0x40 /* free the mf from complete routine */ ++ + + #define INITIALIZE_MGMT_STATUS(status) \ + status = MPT_MGMT_STATUS_PENDING; +@@ -475,7 +497,7 @@ typedef struct _MPT_MGMT { + u8 status; /* current command status */ + int completion_code; + u32 msg_context; +-} MPT_MGMT; ++}MPT_MGMT; + + /* + * Event Structure and define +@@ -538,7 +560,7 @@ typedef struct _SasCfgData { + * @inactive_list + */ + struct inactive_raid_component_info { +- struct list_head list; ++ struct list_head list; + u8 volumeID; /* volume target id */ + u8 volumeBus; /* volume channel */ + IOC_3_PHYS_DISK d; /* phys disk info */ +@@ -547,7 +569,8 @@ struct inactive_raid_component_info { + typedef struct _RaidCfgData { + IOCPage2_t *pIocPg2; /* table of Raid Volumes */ + IOCPage3_t *pIocPg3; /* table of physical disks */ +- struct mutex inactive_list_mutex; ++ IOCPage6_t *pIocPg6; /* table of IR static data */ ++ struct semaphore inactive_list_mutex; + struct list_head inactive_list; /* link list for physical + disk that belong in + inactive volumes */ +@@ -578,8 +601,8 @@ struct mptfc_rport_info + }; + + typedef void (*MPT_ADD_SGE)(void *pAddr, u32 flagslength, dma_addr_t dma_addr); +-typedef void (*MPT_ADD_CHAIN)(void *pAddr, u8 next, u16 length, +- dma_addr_t dma_addr); ++typedef void (*MPT_ADD_CHAIN)(void *pAddr, u8 next, u16 length, dma_addr_t dma_addr); ++typedef void (*MPT_SCHEDULE_TARGET_RESET)(void *ioc); + + /* + * Adapter Structure - pci_dev specific. Maximum: MPT_MAX_ADAPTERS +@@ -591,8 +614,7 @@ typedef struct _MPT_ADAPTER + char name[MPT_NAME_LENGTH]; /* "iocN" */ + char prod_name[MPT_NAME_LENGTH]; /* "LSIFC9x9" */ + #ifdef CONFIG_FUSION_LOGGING +- /* used in mpt_display_event_info */ +- char evStr[EVENT_DESCR_STR_SZ]; ++ char evStr[EVENT_DESCR_STR_SZ]; /* used in mpt_display_event_info */ + #endif + char board_name[16]; + char board_assembly[16]; +@@ -605,8 +627,8 @@ typedef struct _MPT_ADAPTER + SYSIF_REGS __iomem *chip; /* == c8817000 (mmap) */ + SYSIF_REGS __iomem *pio_chip; /* Programmed IO (downloadboot) */ + u8 bus_type; +- u32 mem_phys; /* == f4020000 (mmap) */ +- u32 pio_mem_phys; /* Programmed IO (downloadboot) */ ++ unsigned long mem_phys; /* == f4020000 (mmap) */ ++ unsigned long pio_mem_phys; /* Programmed IO (downloadboot) */ + int mem_size; /* mmap memory size */ + int number_of_buses; + int devices_per_bus; +@@ -621,10 +643,8 @@ typedef struct _MPT_ADAPTER + int reply_depth; /* Num Allocated reply frames */ + int reply_sz; /* Reply frame size */ + int num_chain; /* Number of chain buffers */ +- MPT_ADD_SGE add_sge; /* Pointer to add_sge +- function */ +- MPT_ADD_CHAIN add_chain; /* Pointer to add_chain +- function */ ++ MPT_ADD_SGE add_sge; /* Pointer to add_sge function */ ++ MPT_ADD_CHAIN add_chain; /* Pointer to add_chain function */ + /* Pool of buffers for chaining. ReqToChain + * and ChainToChain track index of chain buffers. + * ChainBuffer (DMA) virt/phys addresses. +@@ -653,18 +673,18 @@ typedef struct _MPT_ADAPTER + dma_addr_t sense_buf_pool_dma; + u32 sense_buf_low_dma; + u8 *HostPageBuffer; /* SAS - host page buffer support */ +- u32 HostPageBuffer_sz; +- dma_addr_t HostPageBuffer_dma; ++ u32 HostPageBuffer_sz; ++ dma_addr_t HostPageBuffer_dma; + int mtrr_reg; + struct pci_dev *pcidev; /* struct pci_dev pointer */ +- int bars; /* bitmask of BAR's that must be configured */ +- int msi_enable; ++ int bars; /* bitmask of BAR's that must be configured */ ++ int msi_enable; + u8 __iomem *memmap; /* mmap address */ + struct Scsi_Host *sh; /* Scsi Host pointer */ +- SpiCfgData spi_data; /* Scsi config. data */ +- RaidCfgData raid_data; /* Raid config. data */ +- SasCfgData sas_data; /* Sas config. data */ +- FcCfgData fc_data; /* Fc config. data */ ++ SpiCfgData spi_data; /* Scsi config. data */ ++ RaidCfgData raid_data; /* Raid config. data */ ++ SasCfgData sas_data; /* Sas config. data */ ++ FcCfgData fc_data; /* Fc config. data */ + struct proc_dir_entry *ioc_dentry; + struct _MPT_ADAPTER *alt_ioc; /* ptr to 929 bound adapter port */ + u32 biosVersion; /* BIOS version from IO Unit Page 2 */ +@@ -673,7 +693,7 @@ typedef struct _MPT_ADAPTER + int eventLogSize; /* Max number of cached events */ + struct _mpt_ioctl_events *events; /* pointer to event log */ + u8 *cached_fw; /* Pointer to FW */ +- dma_addr_t cached_fw_dma; ++ dma_addr_t cached_fw_dma; + int hs_reply_idx; + #ifndef MFCNT + u32 pad0; +@@ -688,8 +708,14 @@ typedef struct _MPT_ADAPTER + FCPortPage0_t fc_port_page0[2]; + LANPage0_t lan_cnfg_page0; + LANPage1_t lan_cnfg_page1; ++#if defined(CPQ_CIM) ++ u32 csmi_change_count; /* count to track all IR ++ events for CSMI */ ++ u8 pci_slot_number; /* ioc page 1 - pci slot number */ ++#endif + + u8 ir_firmware; /* =1 if IR firmware detected */ ++ + /* + * Description: errata_flag_1064 + * If a PCIX read occurs within 1 or 2 cycles after the chip receives +@@ -701,7 +727,6 @@ typedef struct _MPT_ADAPTER + u8 FirstWhoInit; + u8 upload_fw; /* If set, do a fw upload */ + u8 NBShiftFactor; /* NB Shift Factor based on Block Size (Facts) */ +- u8 pad1[4]; + u8 DoneCtx; + u8 TaskCtx; + u8 InternalCtx; +@@ -709,37 +734,39 @@ typedef struct _MPT_ADAPTER + struct net_device *netdev; + struct list_head sas_topology; + struct mutex sas_topology_mutex; ++ u8 disable_hotplug_remove; + +- struct workqueue_struct *fw_event_q; ++ struct workqueue_struct *fw_event_q; + struct list_head fw_event_list; + spinlock_t fw_event_lock; + u8 fw_events_off; /* if '1', then ignore events */ +- char fw_event_q_name[MPT_KOBJ_NAME_LEN]; +- +- struct mutex sas_discovery_mutex; +- u8 sas_discovery_runtime; +- u8 sas_discovery_ignore_events; ++ char fw_event_q_name[MPT_KOBJ_NAME_LEN]; + +- /* port_info object for the host */ +- struct mptsas_portinfo *hba_port_info; ++ struct mptsas_portinfo *hba_port_info; /* port_info object for the host */ + u64 hba_port_sas_addr; + u16 hba_port_num_phy; + struct list_head sas_device_info_list; +- struct mutex sas_device_info_mutex; ++ struct semaphore sas_device_info_mutex; + u8 old_sas_discovery_protocal; + u8 sas_discovery_quiesce_io; + int sas_index; /* index refrencing */ + MPT_MGMT sas_mgmt; +- MPT_MGMT mptbase_cmds; /* for sending config pages */ + MPT_MGMT internal_cmds; ++ MPT_MGMT mptbase_cmds; /* for sending config pages */ + MPT_MGMT taskmgmt_cmds; +- MPT_MGMT ioctl_cmds; +- spinlock_t taskmgmt_lock; /* diagnostic reset lock */ ++ MPT_MGMT ioctl_cmds; /* ioctl data pointer */ ++ spinlock_t taskmgmt_lock; /* diagnostic reset lock */ + int taskmgmt_in_progress; + u8 taskmgmt_quiesce_io; + u8 ioc_reset_in_progress; +- struct work_struct sas_persist_task; ++ MPT_SCHEDULE_TARGET_RESET schedule_target_reset; ++#if defined(CPQ_CIM) ++ u8 num_ports; ++#endif + ++ char reset_work_q_name[MPT_KOBJ_NAME_LEN]; ++ struct workqueue_struct *reset_work_q; ++ struct delayed_work fault_reset_work; + struct work_struct fc_setup_reset_work; + struct list_head fc_rports; + struct work_struct fc_lsc_work; +@@ -748,26 +775,32 @@ typedef struct _MPT_ADAPTER + struct work_struct fc_rescan_work; + char fc_rescan_work_q_name[MPT_KOBJ_NAME_LEN]; + struct workqueue_struct *fc_rescan_work_q; +- +- /* driver forced bus resets count */ +- unsigned long hard_resets; +- /* fw/external bus resets count */ +- unsigned long soft_resets; +- /* cmd timeouts */ +- unsigned long timeouts; +- ++ unsigned long hard_resets; /* driver forced bus resets count */ ++ unsigned long soft_resets; /* fw/external bus resets count */ ++ unsigned long timeouts; /* cmd timeouts */ + struct scsi_cmnd **ScsiLookup; + spinlock_t scsi_lookup_lock; +- u64 dma_mask; ++ int sdev_queue_depth; /* sdev queue depth */ ++ u64 dma_mask; + u32 broadcast_aen_busy; +- char reset_work_q_name[MPT_KOBJ_NAME_LEN]; +- struct workqueue_struct *reset_work_q; +- struct delayed_work fault_reset_work; +- ++#if defined(DIAG_BUFFER_SUPPORT) ++ u8 *DiagBuffer[MPI_DIAG_BUF_TYPE_COUNT]; ++ u32 DataSize[MPI_DIAG_BUF_TYPE_COUNT]; ++ u32 DiagBuffer_sz[MPI_DIAG_BUF_TYPE_COUNT]; ++ dma_addr_t DiagBuffer_dma[MPI_DIAG_BUF_TYPE_COUNT]; ++ u8 TraceLevel[MPI_DIAG_BUF_TYPE_COUNT]; ++ u8 DiagBuffer_Status[MPI_DIAG_BUF_TYPE_COUNT]; ++ u32 UniqueId[MPI_DIAG_BUF_TYPE_COUNT]; ++ u32 ExtendedType[MPI_DIAG_BUF_TYPE_COUNT]; ++ u32 ProductSpecific[MPI_DIAG_BUF_TYPE_COUNT][4]; ++#endif + u8 sg_addr_size; +- u8 in_rescan; + u8 SGE_size; +- ++ u8 in_rescan; ++ /* diag buffer bits for sysfs */ ++ u8 is_fault; ++ u32 ring_buffer_offset; ++ u32 ring_buffer_sz; + } MPT_ADAPTER; + + /* +@@ -804,11 +837,9 @@ typedef struct _mpt_sge { + dma_addr_t Address; + } MptSge_t; + +- + #define mpt_msg_flags(ioc) \ +- (ioc->sg_addr_size == sizeof(u64)) ? \ +- MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 : \ +- MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32 ++ (ioc->sg_addr_size == sizeof(u64)) ? MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 : \ ++ MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32 + + #define MPT_SGE_FLAGS_64_BIT_ADDRESSING \ + (MPI_SGE_FLAGS_64_BIT_ADDRESSING << MPI_SGE_FLAGS_SHIFT) +@@ -830,26 +861,10 @@ typedef struct _mpt_sge { + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +-#define SCSI_STD_SENSE_BYTES 18 +-#define SCSI_STD_INQUIRY_BYTES 36 +-#define SCSI_MAX_INQUIRY_BYTES 96 +- + /* + * MPT_SCSI_HOST defines - Used by the IOCTL and the SCSI drivers + * Private to the driver. + */ +-/* LOCAL structure and fields used when processing +- * internally generated commands. These include: +- * bus scan, dv and config requests. +- */ +-typedef struct _MPT_LOCAL_REPLY { +- ConfigPageHeader_t header; +- int completion; +- u8 sense[SCSI_STD_SENSE_BYTES]; +- u8 scsiStatus; +- u8 skip; +- u32 pad; +-} MPT_LOCAL_REPLY; + + #define MPT_HOST_BUS_UNKNOWN (0xFF) + #define MPT_HOST_TOO_MANY_TM (0x05) +@@ -865,13 +880,6 @@ typedef struct _MPT_LOCAL_REPLY { + #define MPT_NVRAM_WIDE_DISABLE (0x00100000) + #define MPT_NVRAM_BOOT_CHOICE (0x00200000) + +-/* The TM_STATE variable is used to provide strict single threading of TM +- * requests as well as communicate TM error conditions. +- */ +-#define TM_STATE_NONE (0) +-#define TM_STATE_IN_PROGRESS (1) +-#define TM_STATE_ERROR (2) +- + typedef enum { + FC, + SPI, +@@ -881,7 +889,7 @@ typedef enum { + typedef struct _MPT_SCSI_HOST { + MPT_ADAPTER *ioc; + ushort sel_timeout[MPT_MAX_FC_DEVICES]; +- char *info_kbuf; ++ char *info_kbuf; + long last_queue_full; + u16 spi_pending; + struct list_head target_reset_list; +@@ -889,14 +897,6 @@ typedef struct _MPT_SCSI_HOST { + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /* +- * More Dynamic Multi-Pathing stuff... +- */ +- +-/* Forward decl, a strange C thing, to prevent gcc compiler warnings */ +-struct scsi_cmnd; +- +-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +-/* + * Generic structure passed to the base mpt_config function. + */ + typedef struct _x_config_parms { +@@ -934,37 +934,37 @@ extern MPT_FRAME_HDR *mpt_get_msg_frame( + extern void mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf); + extern void mpt_put_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf); + extern void mpt_put_msg_frame_hi_pri(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf); +- + extern int mpt_send_handshake_request(u8 cb_idx, MPT_ADAPTER *ioc, int reqBytes, u32 *req, int sleepFlag); + extern int mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp); + extern u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked); + extern void mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buf, int *size, int len, int showlan); + extern int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag); ++extern int mpt_SoftResetHandler(MPT_ADAPTER *ioc, int sleepFlag); + extern int mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *cfg); + extern int mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size); + extern void mpt_free_fw_memory(MPT_ADAPTER *ioc); + extern int mpt_findImVolumes(MPT_ADAPTER *ioc); + extern int mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode); + extern int mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t phys_disk); +-extern int mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, u8 phys_disk_num, +- pRaidPhysDiskPage1_t phys_disk); +-extern int mpt_raid_phys_disk_get_num_paths(MPT_ADAPTER *ioc, +- u8 phys_disk_num); ++extern int mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage1_t phys_disk); ++extern int mpt_raid_phys_disk_get_num_paths(MPT_ADAPTER *ioc, u8 phys_disk_num); ++ + extern int mpt_set_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc); + extern void mpt_clear_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc); +-extern void mpt_halt_firmware(MPT_ADAPTER *ioc); +- ++extern void mpt_halt_firmware(MPT_ADAPTER *ioc); + + /* + * Public data decl's... + */ + extern struct list_head ioc_list; ++extern struct proc_dir_entry *mpt_proc_root_dir; ++extern int mpt_debug_level; + extern int mpt_fwfault_debug; + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + #endif /* } __KERNEL__ */ + +-#ifdef CONFIG_64BIT ++#if defined(__alpha__) || defined(__sparc_v9__) || defined(__ia64__) || defined(__x86_64__) || defined(__powerpc64__) + #define CAST_U32_TO_PTR(x) ((void *)(u64)x) + #define CAST_PTR_TO_U32(x) ((u32)(u64)x) + #else +--- a/drivers/message/fusion/mptctl.c ++++ b/drivers/message/fusion/mptctl.c +@@ -71,6 +71,15 @@ + #include "mptbase.h" + #include "mptctl.h" + ++#if defined(CPQ_CIM) ++#include "mptsas.h" ++#include "csmi/csmisas.h" ++#endif // CPQ_CIM ++ ++#if defined(DIAG_BUFFER_SUPPORT) ++#include "rejected_ioctls/diag_buffer.h" ++#endif ++ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + #define my_NAME "Fusion MPT misc device (ioctl) driver" + #define my_VERSION MPT_LINUX_VERSION_COMMON +@@ -113,6 +122,42 @@ static int mptctl_do_reset(unsigned long + static int mptctl_hp_hostinfo(unsigned long arg, unsigned int cmd); + static int mptctl_hp_targetinfo(unsigned long arg); + ++#if defined(CPQ_CIM) ++/* csmisas proto's*/ ++static int csmisas_get_driver_info(unsigned long arg); ++static int csmisas_get_cntlr_status(unsigned long arg); ++static int csmisas_get_cntlr_config(unsigned long arg); ++static int csmisas_get_phy_info(unsigned long arg); ++static int csmisas_get_scsi_address(unsigned long arg); ++static int csmisas_get_link_errors(unsigned long arg); ++static int csmisas_smp_passthru(unsigned long arg); ++static int csmisas_firmware_download(unsigned long arg); ++static int csmisas_get_raid_info(unsigned long arg); ++static int csmisas_get_raid_config(unsigned long arg); ++static int csmisas_get_raid_features(unsigned long arg); ++static int csmisas_set_raid_control(unsigned long arg); ++static int csmisas_get_raid_element(unsigned long arg); ++static int csmisas_set_raid_operation(unsigned long arg); ++static int csmisas_set_phy_info(unsigned long arg); ++static int csmisas_ssp_passthru(unsigned long arg); ++static int csmisas_stp_passthru(unsigned long arg); ++static int csmisas_get_sata_signature(unsigned long arg); ++static int csmisas_get_device_address(unsigned long arg); ++static int csmisas_task_managment(unsigned long arg); ++static int csmisas_phy_control(unsigned long arg); ++static int csmisas_get_connector_info(unsigned long arg); ++static int csmisas_get_location(unsigned long arg); ++#endif // CPQ_CIM ++ ++#if defined(DIAG_BUFFER_SUPPORT) ++/* diag_buffer proto's */ ++static int mptctl_register_diag_buffer(unsigned long arg); ++static int mptctl_release_diag_buffer(unsigned long arg); ++static int mptctl_unregister_diag_buffer(unsigned long arg); ++static int mptctl_query_diag_buffer(unsigned long arg); ++static int mptctl_read_diag_buffer(unsigned long arg); ++#endif // DIAG_BUFFER_SUPPORT ++ + static int mptctl_probe(struct pci_dev *, const struct pci_device_id *); + static void mptctl_remove(struct pci_dev *); + +@@ -128,7 +173,6 @@ static MptSge_t *kbuf_alloc_2_sgl(int by + struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc); + static void kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma, + struct buflist *buflist, MPT_ADAPTER *ioc); +-static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function); + + /* + * Reset Handler cleanup function +@@ -234,8 +278,7 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME + le32_to_cpu(reply->u.reply.IOCLogInfo))); + + if ((req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) || +- (req->u.hdr.Function == +- MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { ++ (req->u.hdr.Function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { + + if (reply->u.sreply.SCSIStatus || reply->u.sreply.SCSIState) + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT +@@ -246,8 +289,7 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME + le16_to_cpu(reply->u.sreply.TaskTag), + le32_to_cpu(reply->u.sreply.TransferCount))); + +- if (reply->u.sreply.SCSIState & +- MPI_SCSI_STATE_AUTOSENSE_VALID) { ++ if (reply->u.sreply.SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { + sz = req->u.scsireq.SenseBufferLength; + req_index = + le16_to_cpu(req->u.frame.hwhdr.msgctxu.fld.req_idx); +@@ -262,10 +304,16 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME + /* We are done, issue wake up + */ + if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_PENDING) { +- if (req->u.hdr.Function == MPI_FUNCTION_SCSI_TASK_MGMT) ++ if (req->u.hdr.Function == MPI_FUNCTION_SCSI_TASK_MGMT) { + mpt_clear_taskmgmt_in_progress_flag(ioc); +- ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING; +- complete(&ioc->ioctl_cmds.done); ++ ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING; ++ complete(&ioc->ioctl_cmds.done); ++ if (ioc->bus_type == SAS) ++ ioc->schedule_target_reset(ioc); ++ } else { ++ ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING; ++ complete(&ioc->ioctl_cmds.done); ++ } + } + + out_continuation: +@@ -275,55 +323,14 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME + return 1; + } + +-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +-/* mptctl_timeout_expired +- * +- * Expecting an interrupt, however timed out. +- * +- */ +-static void +-mptctl_timeout_expired(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) +-{ +- unsigned long flags; +- +- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": %s\n", +- ioc->name, __func__)); +- +- if (mpt_fwfault_debug) +- mpt_halt_firmware(ioc); +- +- spin_lock_irqsave(&ioc->taskmgmt_lock, flags); +- if (ioc->ioc_reset_in_progress) { +- spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); +- CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) +- mpt_free_msg_frame(ioc, mf); +- return; +- } +- spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); +- +- +- if (!mptctl_bus_reset(ioc, mf->u.hdr.Function)) +- return; +- +- /* Issue a reset for this device. +- * The IOC is not responding. +- */ +- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling HardReset! \n", +- ioc->name)); +- CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) +- mpt_HardResetHandler(ioc, CAN_SLEEP); +- mpt_free_msg_frame(ioc, mf); +-} +- + static int + mptctl_taskmgmt_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) + { + if (!mf) + return 0; + +- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "TaskMgmt completed (mf=%p, mr=%p)\n", +- ioc->name, mf, mr)); ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed (mf=%p, mr=%p)\n", ++ ioc->name, mf, mr)); + + ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD; + +@@ -338,17 +345,15 @@ mptctl_taskmgmt_reply(MPT_ADAPTER *ioc, + mpt_clear_taskmgmt_in_progress_flag(ioc); + ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING; + complete(&ioc->taskmgmt_cmds.done); ++ if (ioc->bus_type == SAS) ++ ioc->schedule_target_reset(ioc); + return 1; + } + return 0; + } + +-/* mptctl_bus_reset +- * +- * Bus reset code. +- * +- */ +-static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) ++static int ++mptctl_do_taskmgmt(MPT_ADAPTER *ioc, u8 tm_type, u8 bus_id, u8 target_id) + { + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; +@@ -359,13 +364,6 @@ static int mptctl_bus_reset(MPT_ADAPTER + unsigned long time_count; + u16 iocstatus; + +- /* bus reset is only good for SCSI IO, RAID PASSTHRU */ +- if (!(function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) || +- (function == MPI_FUNCTION_SCSI_IO_REQUEST)) { +- dtmprintk(ioc, printk(MYIOC_s_WARN_FMT +- "TaskMgmt, not SCSI_IO!!\n", ioc->name)); +- return -EPERM; +- } + + mutex_lock(&ioc->taskmgmt_cmds.mutex); + if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) { +@@ -375,15 +373,13 @@ static int mptctl_bus_reset(MPT_ADAPTER + + retval = 0; + +- /* Send request +- */ +- mf = mpt_get_msg_frame(mptctl_taskmgmt_id, ioc); +- if (mf == NULL) { +- dtmprintk(ioc, printk(MYIOC_s_WARN_FMT +- "TaskMgmt, no msg frames!!\n", ioc->name)); ++ if ((mf = mpt_get_msg_frame(mptctl_taskmgmt_id, ioc)) == NULL) { ++ dtmprintk(ioc, ++ printk(MYIOC_s_WARN_FMT "TaskMgmt, no msg frames!!\n", ++ ioc->name)); + mpt_clear_taskmgmt_in_progress_flag(ioc); + retval = -ENOMEM; +- goto mptctl_bus_reset_done; ++ goto tm_done; + } + + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n", +@@ -392,10 +388,13 @@ static int mptctl_bus_reset(MPT_ADAPTER + pScsiTm = (SCSITaskMgmt_t *) mf; + memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t)); + pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; +- pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; +- pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION; +- pScsiTm->TargetID = 0; +- pScsiTm->Bus = 0; ++ pScsiTm->TaskType = tm_type; ++ if ((tm_type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && ++ (ioc->bus_type == FC)) ++ pScsiTm->MsgFlags = ++ MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION; ++ pScsiTm->TargetID = target_id; ++ pScsiTm->Bus = bus_id; + pScsiTm->ChainOffset = 0; + pScsiTm->Reserved = 0; + pScsiTm->Reserved1 = 0; +@@ -406,43 +405,45 @@ static int mptctl_bus_reset(MPT_ADAPTER + pScsiTm->Reserved2[ii] = 0; + + switch (ioc->bus_type) { +- case FC: +- timeout = 40; +- break; +- case SAS: +- timeout = 30; +- break; +- case SPI: +- default: +- timeout = 2; +- break; ++ case FC: ++ timeout = 40; ++ break; ++ case SAS: ++ timeout = 30; ++ break; ++ case SPI: ++ default: ++ timeout = 10; ++ break; + } + +- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "TaskMgmt type=%d timeout=%ld\n", +- ioc->name, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, timeout)); ++ dtmprintk(ioc, ++ printk(MYIOC_s_DEBUG_FMT "TaskMgmt type=%d timeout=%ld\n", ++ ioc->name, tm_type, timeout)); + + INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status) +- CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status) + time_count = jiffies; + if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) && + (ioc->facts.MsgVersion >= MPI_VERSION_01_05)) + mpt_put_msg_frame_hi_pri(mptctl_taskmgmt_id, ioc, mf); + else { + retval = mpt_send_handshake_request(mptctl_taskmgmt_id, ioc, +- sizeof(SCSITaskMgmt_t), (u32 *)pScsiTm, CAN_SLEEP); ++ sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, CAN_SLEEP); + if (retval != 0) { +- dfailprintk(ioc, printk(MYIOC_s_ERR_FMT ++ dfailprintk(ioc, ++ printk(MYIOC_s_ERR_FMT + "TaskMgmt send_handshake FAILED!" + " (ioc %p, mf %p, rc=%d) \n", ioc->name, + ioc, mf, retval)); ++ mpt_free_msg_frame(ioc, mf); + mpt_clear_taskmgmt_in_progress_flag(ioc); +- goto mptctl_bus_reset_done; ++ goto tm_done; + } + } + + /* Now wait for the command to complete */ + ii = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done, timeout*HZ); ++ + if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "TaskMgmt failed\n", ioc->name)); +@@ -452,14 +453,14 @@ static int mptctl_bus_reset(MPT_ADAPTER + retval = 0; + else + retval = -1; /* return failure */ +- goto mptctl_bus_reset_done; ++ goto tm_done; + } + + if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) { + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "TaskMgmt failed\n", ioc->name)); + retval = -1; /* return failure */ +- goto mptctl_bus_reset_done; ++ goto tm_done; + } + + pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply; +@@ -467,7 +468,7 @@ static int mptctl_bus_reset(MPT_ADAPTER + "TaskMgmt fw_channel = %d, fw_id = %d, task_type=0x%02X, " + "iocstatus=0x%04X\n\tloginfo=0x%08X, response_code=0x%02X, " + "term_cmnds=%d\n", ioc->name, pScsiTmReply->Bus, +- pScsiTmReply->TargetID, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, ++ pScsiTmReply->TargetID, tm_type, + le16_to_cpu(pScsiTmReply->IOCStatus), + le32_to_cpu(pScsiTmReply->IOCLogInfo), + pScsiTmReply->ResponseCode, +@@ -485,14 +486,67 @@ static int mptctl_bus_reset(MPT_ADAPTER + retval = -1; /* return failure */ + } + +- +- mptctl_bus_reset_done: ++ tm_done: + mutex_unlock(&ioc->taskmgmt_cmds.mutex); + CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status) + return retval; + } + + ++static void ++mptctl_timeout_expired(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) ++{ ++ unsigned long flags; ++ int ret_val = -1; ++ SCSIIORequest_t *scsi_req = (SCSIIORequest_t *) mf; ++ u8 function = mf->u.hdr.Function; ++ ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": %s\n", ++ ioc->name, __FUNCTION__)); ++ ++ if(mpt_fwfault_debug) ++ mpt_halt_firmware(ioc); ++ ++ spin_lock_irqsave(&ioc->taskmgmt_lock, flags); ++ if (ioc->ioc_reset_in_progress) { ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) ++ mpt_free_msg_frame(ioc, mf); ++ return; ++ } ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ ++ ++ CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) ++ ++ if (ioc->bus_type == SAS) { ++ if (function == MPI_FUNCTION_SCSI_IO_REQUEST) ++ ret_val = mptctl_do_taskmgmt(ioc, ++ MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, ++ scsi_req->Bus, scsi_req->TargetID); ++ else if (function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) ++ ret_val = mptctl_do_taskmgmt(ioc, ++ MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, ++ scsi_req->Bus, 0); ++ if (!ret_val) ++ return; ++ } else { ++ if ((function == MPI_FUNCTION_SCSI_IO_REQUEST) || ++ (function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) ++ ret_val = mptctl_do_taskmgmt(ioc, ++ MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, ++ scsi_req->Bus, 0); ++ if (!ret_val) ++ return; ++ } ++ ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling Reset! \n", ++ ioc->name)); ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP) != 0) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); ++ mpt_free_msg_frame(ioc, mf); ++} ++ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /* mptctl_ioc_reset + * +@@ -580,15 +634,17 @@ static int + mptctl_fasync(int fd, struct file *filep, int mode) + { + MPT_ADAPTER *ioc; +- int ret; + +- lock_kernel(); + list_for_each_entry(ioc, &ioc_list, list) + ioc->aen_event_read_flag=0; + +- ret = fasync_helper(fd, filep, mode, &async_queue); +- unlock_kernel(); +- return ret; ++ return fasync_helper(fd, filep, mode, &async_queue); ++} ++ ++static int ++mptctl_release(struct inode *inode, struct file *filep) ++{ ++ return fasync_helper(-1, filep, 0, &async_queue); + } + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +@@ -608,6 +664,7 @@ __mptctl_ioctl(struct file *file, unsign + int ret; + MPT_ADAPTER *iocp = NULL; + ++ + if (copy_from_user(&khdr, uhdr, sizeof(khdr))) { + printk(KERN_ERR MYNAM "%s::mptctl_ioctl() @%d - " + "Unable to copy mpt_ioctl_header data @ %p\n", +@@ -624,12 +681,6 @@ __mptctl_ioctl(struct file *file, unsign + (iocp == NULL)) + return -ENODEV; + +- if (!iocp->active) { +- printk(KERN_DEBUG MYNAM "%s::mptctl_ioctl() @%d - Controller disabled.\n", +- __FILE__, __LINE__); +- return -EFAULT; +- } +- + /* Handle those commands that are just returning + * information stored in the driver. + * These commands should never time out and are unaffected +@@ -649,6 +700,25 @@ __mptctl_ioctl(struct file *file, unsign + return mptctl_eventreport(arg); + } else if (cmd == MPTFWREPLACE) { + return mptctl_replace_fw(arg); ++#if defined(DIAG_BUFFER_SUPPORT) ++/* diag_buffer static data calls*/ ++ } else if (cmd == MPTDIAGQUERY) { ++ return mptctl_query_diag_buffer(arg); ++ } else if (cmd == MPTDIAGUNREGISTER) { ++ return mptctl_unregister_diag_buffer(arg); ++#endif ++ ++#if defined(CPQ_CIM) ++/* csmisas static data calls*/ ++ } else if (cmd == CC_CSMI_SAS_GET_DRIVER_INFO) { ++ return csmisas_get_driver_info(arg); ++ } else if (cmd == CC_CSMI_SAS_GET_CNTLR_STATUS) { ++ return csmisas_get_cntlr_status(arg); ++ } else if (cmd == CC_CSMI_SAS_GET_SCSI_ADDRESS) { ++ return csmisas_get_scsi_address(arg); ++ } else if (cmd == CC_CSMI_SAS_GET_DEVICE_ADDRESS){ ++ return csmisas_get_device_address(arg); ++#endif // CPQ_CIM + } + + /* All of these commands require an interrupt or +@@ -657,6 +727,8 @@ __mptctl_ioctl(struct file *file, unsign + if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0) + return ret; + ++// dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT ": mptctl_ioctl()\n", iocp->name)); ++ + if (cmd == MPTFWDOWNLOAD) + ret = mptctl_fw_download(arg); + else if (cmd == MPTCOMMAND) +@@ -667,6 +739,57 @@ __mptctl_ioctl(struct file *file, unsign + ret = mptctl_hp_hostinfo(arg, _IOC_SIZE(cmd)); + else if (cmd == HP_GETTARGETINFO) + ret = mptctl_hp_targetinfo(arg); ++#if defined(CPQ_CIM) ++/* csmisas requiring fw calls*/ ++ else if (cmd == CC_CSMI_SAS_GET_CNTLR_CONFIG) ++ ret = csmisas_get_cntlr_config(arg); ++ else if (cmd == CC_CSMI_SAS_GET_PHY_INFO) ++ ret = csmisas_get_phy_info(arg); ++ else if (cmd == CC_CSMI_SAS_GET_SATA_SIGNATURE) ++ ret = csmisas_get_sata_signature(arg); ++ else if (cmd == CC_CSMI_SAS_GET_LINK_ERRORS) ++ ret = csmisas_get_link_errors(arg); ++ else if (cmd == CC_CSMI_SAS_SMP_PASSTHRU) ++ ret = csmisas_smp_passthru(arg); ++ else if (cmd == CC_CSMI_SAS_SSP_PASSTHRU) ++ ret = csmisas_ssp_passthru(arg); ++ else if (cmd == CC_CSMI_SAS_FIRMWARE_DOWNLOAD) ++ ret = csmisas_firmware_download(arg); ++ else if (cmd == CC_CSMI_SAS_GET_RAID_INFO) ++ ret = csmisas_get_raid_info(arg); ++ else if (cmd == CC_CSMI_SAS_GET_RAID_CONFIG) ++ ret = csmisas_get_raid_config(arg); ++ else if (cmd == CC_CSMI_SAS_GET_RAID_FEATURES) ++ ret = csmisas_get_raid_features(arg); ++ else if (cmd == CC_CSMI_SAS_SET_RAID_CONTROL) ++ ret = csmisas_set_raid_control(arg); ++ else if (cmd == CC_CSMI_SAS_GET_RAID_ELEMENT) ++ ret = csmisas_get_raid_element(arg); ++ else if (cmd == CC_CSMI_SAS_SET_RAID_OPERATION) ++ ret = csmisas_set_raid_operation(arg); ++ else if (cmd == CC_CSMI_SAS_SET_PHY_INFO) ++ ret = csmisas_set_phy_info(arg); ++ else if (cmd == CC_CSMI_SAS_STP_PASSTHRU) ++ ret = csmisas_stp_passthru(arg); ++ else if (cmd == CC_CSMI_SAS_TASK_MANAGEMENT) ++ ret = csmisas_task_managment(arg); ++ else if (cmd == CC_CSMI_SAS_PHY_CONTROL) ++ ret = csmisas_phy_control(arg); ++ else if (cmd == CC_CSMI_SAS_GET_CONNECTOR_INFO) ++ ret = csmisas_get_connector_info(arg); ++ else if (cmd == CC_CSMI_SAS_GET_LOCATION) ++ ret = csmisas_get_location(arg); ++#endif // CPQ_CIM ++ ++#if defined(DIAG_BUFFER_SUPPORT) ++/* diag_buffer requiring fw calls*/ ++ else if (cmd == MPTDIAGREGISTER) ++ ret = mptctl_register_diag_buffer(arg); ++ else if (cmd == MPTDIAGRELEASE) ++ ret = mptctl_release_diag_buffer(arg); ++ else if (cmd == MPTDIAGREADBUFFER) ++ ret = mptctl_read_diag_buffer(arg); ++#endif // DIAG_BUFFER_SUPPORT + else + ret = -EINVAL; + +@@ -699,6 +822,7 @@ static int mptctl_do_reset(unsigned long + } + + if (mpt_verify_adapter(krinfo.hdr.iocnum, &iocp) < 0) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s@%d::mptctl_do_reset - ioc%d not found!\n", + __FILE__, __LINE__, krinfo.hdr.iocnum); + return -ENODEV; /* (-6) No such device or address */ +@@ -789,13 +913,13 @@ mptctl_do_fw_download(int ioc, char __us + unsigned long timeleft; + + if (mpt_verify_adapter(ioc, &iocp) < 0) { +- printk(KERN_DEBUG MYNAM "ioctl_fwdl - ioc%d not found!\n", +- ioc); ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) ++ printk(KERN_DEBUG MYNAM "ioctl_fwdl - ioc%d not found!\n", ioc); + return -ENODEV; /* (-6) No such device or address */ + } else { + + /* Valid device. Get a message frame and construct the FW download message. +- */ ++ */ + if ((mf = mpt_get_msg_frame(mptctl_id, iocp)) == NULL) + return -EAGAIN; + } +@@ -870,8 +994,7 @@ mptctl_do_fw_download(int ioc, char __us + * 96 8 + * 64 4 + */ +- maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - +- sizeof(FWDownloadTCSGE_t)) ++ maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t)) + / iocp->SGE_size; + if (numfrags > maxfrags) { + ret = -EMLINK; +@@ -904,8 +1027,8 @@ mptctl_do_fw_download(int ioc, char __us + n++; + if (copy_from_user(bl->kptr, ufwbuf+fw_bytes_copied, bl->len)) { + printk(MYIOC_s_ERR_FMT "%s@%d::_ioctl_fwdl - " +- "Unable to copy f/w buffer hunk#%d @ %p\n", +- iocp->name, __FILE__, __LINE__, n, ufwbuf); ++ "Unable to copy f/w buffer hunk#%d @ %p\n", ++ iocp->name, __FILE__, __LINE__, n, ufwbuf); + goto fwdl_out; + } + fw_bytes_copied += bl->len; +@@ -930,7 +1053,7 @@ retry_wait: + timeleft = wait_for_completion_timeout(&iocp->ioctl_cmds.done, HZ*60); + if (!(iocp->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { + ret = -ETIME; +- printk(MYIOC_s_WARN_FMT "%s: failed\n", iocp->name, __func__); ++ printk(MYIOC_s_WARN_FMT "%s: failed\n", iocp->name, __FUNCTION__); + if (iocp->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) { + mpt_free_msg_frame(iocp, mf); + goto fwdl_out; +@@ -943,7 +1066,7 @@ retry_wait: + } + + if (!(iocp->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID)) { +- printk(MYIOC_s_WARN_FMT "%s: failed\n", iocp->name, __func__); ++ printk(MYIOC_s_WARN_FMT "%s: failed\n", iocp->name, __FUNCTION__); + mpt_free_msg_frame(iocp, mf); + ret = -ENODATA; + goto fwdl_out; +@@ -955,21 +1078,21 @@ retry_wait: + ReplyMsg = (pFWDownloadReply_t)iocp->ioctl_cmds.reply; + iocstat = le16_to_cpu(ReplyMsg->IOCStatus) & MPI_IOCSTATUS_MASK; + if (iocstat == MPI_IOCSTATUS_SUCCESS) { +- printk(MYIOC_s_INFO_FMT "F/W update successfull!\n", iocp->name); ++ printk(MYIOC_s_INFO_FMT ": F/W update successfully sent!\n", iocp->name); + return 0; + } else if (iocstat == MPI_IOCSTATUS_INVALID_FUNCTION) { +- printk(MYIOC_s_WARN_FMT "Hmmm... F/W download not supported!?!\n", +- iocp->name); ++ printk(MYIOC_s_WARN_FMT "Hmmm... doesn't support F/W download?\n", ++ iocp->name); + printk(MYIOC_s_WARN_FMT "(time to go bang on somebodies door)\n", +- iocp->name); ++ iocp->name); + return -EBADRQC; + } else if (iocstat == MPI_IOCSTATUS_BUSY) { + printk(MYIOC_s_WARN_FMT "IOC_BUSY!\n", iocp->name); + printk(MYIOC_s_WARN_FMT "(try again later?)\n", iocp->name); + return -EBUSY; + } else { +- printk(MYIOC_s_WARN_FMT "ioctl_fwdl() returned [bad] status = %04xh\n", +- iocp->name, iocstat); ++ printk(MYIOC_s_WARN_FMT "returned [bad] status = %04xh\n", ++ iocp->name, iocstat); + printk(MYIOC_s_WARN_FMT "(bad VooDoo)\n", iocp->name); + return -ENOMSG; + } +@@ -979,7 +1102,7 @@ fwdl_out: + + CLEAR_MGMT_STATUS(iocp->ioctl_cmds.status); + SET_MGMT_MSG_CONTEXT(iocp->ioctl_cmds.msg_context, 0); +- kfree_sgl(sgl, sgl_dma, buflist, iocp); ++ kfree_sgl(sgl, sgl_dma, buflist, iocp); + return ret; + } + +@@ -1061,9 +1184,9 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, i + alloc_sz = alloc_sz / 2; + if (alloc_sz == 0) { + printk(MYIOC_s_WARN_FMT "-SG: No can do - " +- "not enough memory! :-(\n", ioc->name); ++ "not enough memory! :-(\n", ioc->name); + printk(MYIOC_s_WARN_FMT "-SG: (freeing %d frags)\n", +- ioc->name, numfrags); ++ ioc->name, numfrags); + goto free_and_fail; + } + continue; +@@ -1072,8 +1195,9 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, i + + bytes_allocd += this_alloc; + sgl->FlagsLength = (0x10000000|sgdir|this_alloc); +- dma_addr = pci_map_single(ioc->pcidev, +- buflist[buflist_ent].kptr, this_alloc, dir); ++ if (ioc->sg_addr_size == sizeof(u64)) ++ sgl->FlagsLength |= MPT_SGE_FLAGS_64_BIT_ADDRESSING; ++ dma_addr = pci_map_single(ioc->pcidev, buflist[buflist_ent].kptr, this_alloc, dir); + sgl->Address = dma_addr; + + fragcnt++; +@@ -1087,8 +1211,8 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, i + + /* Need to chain? */ + if (fragcnt == sg_spill) { +- printk(MYIOC_s_WARN_FMT +- "-SG: No can do - " "Chain required! :-(\n", ioc->name); ++ printk(MYIOC_s_WARN_FMT "-SG: No can do - " ++ "Chain required! :-(\n", ioc->name); + printk(MYIOC_s_WARN_FMT "(freeing %d frags)\n", ioc->name, numfrags); + goto free_and_fail; + } +@@ -1097,9 +1221,9 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, i + if (numfrags*8 > MAX_SGL_BYTES){ + /* GRRRRR... */ + printk(MYIOC_s_WARN_FMT "-SG: No can do - " +- "too many SG frags! :-(\n", ioc->name); ++ "too many SG frags! :-(\n", ioc->name); + printk(MYIOC_s_WARN_FMT "-SG: (freeing %d frags)\n", +- ioc->name, numfrags); ++ ioc->name, numfrags); + goto free_and_fail; + } + } +@@ -1221,7 +1345,7 @@ mptctl_getiocinfo (unsigned long arg, un + unsigned int port; + int cim_rev; + u8 revision; +- struct scsi_device *sdev; ++ struct scsi_device *sdev; + VirtDevice *vdevice; + + /* Add of PCI INFO results in unaligned access for +@@ -1256,6 +1380,7 @@ mptctl_getiocinfo (unsigned long arg, un + + if (((iocnum = mpt_verify_adapter(karg->hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_getiocinfo() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + kfree(karg); +@@ -1265,8 +1390,8 @@ mptctl_getiocinfo (unsigned long arg, un + /* Verify the data transfer size is correct. */ + if (karg->hdr.maxDataSize != data_size) { + printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_getiocinfo - " +- "Structure size mismatch. Command not completed.\n", +- ioc->name, __FILE__, __LINE__); ++ "Structure size mismatch. Command not completed.\n", ++ ioc->name, __FILE__, __LINE__); + kfree(karg); + return -EFAULT; + } +@@ -1318,6 +1443,8 @@ mptctl_getiocinfo (unsigned long arg, un + if (ioc->sh) { + shost_for_each_device(sdev, ioc->sh) { + vdevice = sdev->hostdata; ++ if (vdevice == NULL || vdevice->vtarget == NULL) ++ continue; + if (vdevice->vtarget->tflags & + MPT_TARGET_FLAGS_RAID_COMPONENT) + continue; +@@ -1378,7 +1505,7 @@ mptctl_gettargetinfo (unsigned long arg) + int maxWordsLeft; + int numBytes; + u8 port; +- struct scsi_device *sdev; ++ struct scsi_device *sdev; + + if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_targetinfo))) { + printk(KERN_ERR MYNAM "%s@%d::mptctl_gettargetinfo - " +@@ -1389,6 +1516,7 @@ mptctl_gettargetinfo (unsigned long arg) + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_gettargetinfo() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; +@@ -1405,8 +1533,8 @@ mptctl_gettargetinfo (unsigned long arg) + port = karg.hdr.port; + + if (maxWordsLeft <= 0) { +- printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_gettargetinfo() - no memory available!\n", +- ioc->name, __FILE__, __LINE__); ++ printk(MYIOC_s_ERR_FMT "%s::mptctl_gettargetinfo() @%d - no memory available!\n", ++ ioc->name, __FILE__, __LINE__); + return -ENOMEM; + } + +@@ -1426,8 +1554,8 @@ mptctl_gettargetinfo (unsigned long arg) + */ + pmem = kzalloc(numBytes, GFP_KERNEL); + if (!pmem) { +- printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_gettargetinfo() - no memory available!\n", +- ioc->name, __FILE__, __LINE__); ++ printk(MYIOC_s_ERR_FMT "%s::mptctl_gettargetinfo() @%d - no memory available!\n", ++ ioc->name, __FILE__, __LINE__); + return -ENOMEM; + } + pdata = (int *) pmem; +@@ -1439,6 +1567,8 @@ mptctl_gettargetinfo (unsigned long arg) + if (!maxWordsLeft) + continue; + vdevice = sdev->hostdata; ++ if (vdevice == NULL || vdevice->vtarget == NULL) ++ continue; + if (vdevice->vtarget->tflags & + MPT_TARGET_FLAGS_RAID_COMPONENT) + continue; +@@ -1503,6 +1633,7 @@ mptctl_readtest (unsigned long arg) + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_readtest() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; +@@ -1564,6 +1695,7 @@ mptctl_eventquery (unsigned long arg) + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_eventquery() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; +@@ -1603,6 +1735,7 @@ mptctl_eventenable (unsigned long arg) + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_eventenable() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; +@@ -1616,15 +1749,14 @@ mptctl_eventenable (unsigned long arg) + int sz = MPTCTL_EVENT_LOG_SIZE * sizeof(MPT_IOCTL_EVENTS); + ioc->events = kzalloc(sz, GFP_KERNEL); + if (!ioc->events) { +- printk(MYIOC_s_ERR_FMT +- ": ERROR - Insufficient memory to add adapter!\n", ++ printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n", + ioc->name); + return -ENOMEM; + } + ioc->alloc_total += sz; + + ioc->eventContext = 0; +- } ++ } + + /* Update the IOC event logging flag. + */ +@@ -1652,13 +1784,14 @@ mptctl_eventreport (unsigned long arg) + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_eventreport() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; + } ++ + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_eventreport called.\n", + ioc->name)); +- + numBytes = karg.hdr.maxDataSize - sizeof(mpt_ioctl_header); + maxEvents = numBytes/sizeof(MPT_IOCTL_EVENTS); + +@@ -1706,6 +1839,7 @@ mptctl_replace_fw (unsigned long arg) + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_replace_fw() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; +@@ -1737,8 +1871,8 @@ mptctl_replace_fw (unsigned long arg) + */ + if (copy_from_user(ioc->cached_fw, uarg->newImage, newFwSize)) { + printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_replace_fw - " +- "Unable to read in mpt_ioctl_replace_fw image " +- "@ %p\n", ioc->name, __FILE__, __LINE__, uarg); ++ "Unable to read in mpt_ioctl_replace_fw image " ++ "@ %p\n", ioc->name, __FILE__, __LINE__, uarg); + mpt_free_fw_memory(ioc); + return -EFAULT; + } +@@ -1755,7 +1889,7 @@ mptctl_replace_fw (unsigned long arg) + * + * Outputs: None. + * Return: 0 if successful +- * -EBUSY if previous command timeout and IOC reset is not complete. ++ * -EBUSY if previous command timout and IOC reset is not complete. + * -EFAULT if data unavailable + * -ENODEV if no such device/adapter + * -ETIME if timer expires +@@ -1780,6 +1914,7 @@ mptctl_mpt_command (unsigned long arg) + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_mpt_command() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; +@@ -1795,7 +1930,7 @@ mptctl_mpt_command (unsigned long arg) + * + * Outputs: None. + * Return: 0 if successful +- * -EBUSY if previous command timeout and IOC reset is not complete. ++ * -EBUSY if previous command timout and IOC reset is not complete. + * -EFAULT if data unavailable + * -ENODEV if no such device/adapter + * -ETIME if timer expires +@@ -1818,7 +1953,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + int sz, rc = 0; + int msgContext; + u16 req_idx; +- ulong timeout; ++ unsigned long timeout; + unsigned long timeleft; + struct scsi_device *sdev; + unsigned long flags; +@@ -1831,6 +1966,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_do_mpt_command() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; +@@ -1862,8 +1998,8 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + + /* Get a free request frame and save the message context. + */ +- if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) +- return -EAGAIN; ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) ++ return -EAGAIN; + + hdr = (MPIHeader_t *) mf; + msgContext = le32_to_cpu(hdr->MsgContext); +@@ -1884,11 +2020,10 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + hdr->MsgContext = cpu_to_le32(msgContext); + function = hdr->Function; + +- + /* Verify that this request is allowed. + */ + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "sending mpi function (0x%02X), req=%p\n", +- ioc->name, hdr->Function, mf)); ++ ioc->name, function, mf)); + + switch (function) { + case MPI_FUNCTION_IOC_FACTS: +@@ -1967,6 +2102,8 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + struct scsi_target *starget = scsi_target(sdev); + VirtTarget *vtarget = starget->hostdata; + ++ if (vtarget == NULL) ++ continue; + if ((pScsiReq->TargetID == vtarget->id) && + (pScsiReq->Bus == vtarget->channel) && + (vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)) +@@ -1987,7 +2124,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + pScsiReq->Control = cpu_to_le32(scsidir | qtag); + pScsiReq->DataLength = cpu_to_le32(dataSize); + +- + } else { + printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - " + "SCSI driver is not loaded. \n", +@@ -2000,7 +2136,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + case MPI_FUNCTION_SMP_PASSTHROUGH: + /* Check mf->PassthruFlags to determine if + * transfer is ImmediateMode or not. +- * Immediate mode returns data in the ReplyFrame. ++ * Immediate mode returns data in the reply. + * Else, we are sending request and response data + * in two SGLs at the end of the mf. + */ +@@ -2077,12 +2213,10 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + { + SCSITaskMgmt_t *pScsiTm; + pScsiTm = (SCSITaskMgmt_t *)mf; +- dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "\tTaskType=0x%x MsgFlags=0x%x " +- "TaskMsgContext=0x%x id=%d channel=%d\n", +- ioc->name, pScsiTm->TaskType, le32_to_cpu +- (pScsiTm->TaskMsgContext), pScsiTm->MsgFlags, +- pScsiTm->TargetID, pScsiTm->Bus)); ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\tTaskType=0x%x MsgFlags=0x%x " ++ "TaskMsgContext=0x%x id=%d channel=%d\n", ioc->name, pScsiTm->TaskType, ++ le32_to_cpu(pScsiTm->TaskMsgContext), pScsiTm->MsgFlags, ++ pScsiTm->TargetID, pScsiTm->Bus)); + break; + } + +@@ -2094,7 +2228,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + /* Verify that all entries in the IOC INIT match + * existing setup (and in LE format). + */ +- if (sizeof(dma_addr_t) == sizeof(u64)) { ++ if (ioc->sg_addr_size == sizeof(u64)) { + high_addr = cpu_to_le32((u32)((u64)ioc->req_frames_dma >> 32)); + sense_high= cpu_to_le32((u32)((u64)ioc->sense_buf_pool_dma >> 32)); + } else { +@@ -2102,6 +2236,11 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + sense_high= 0; + } + ++ if (!pInit->MaxDevices && !pInit->MaxBuses) { ++ pInit->MaxDevices = ioc->facts.MaxDevices; ++ pInit->MaxBuses = ioc->facts.MaxBuses; ++ } ++ + if ((pInit->Flags != 0) || (pInit->MaxDevices != ioc->facts.MaxDevices) || + (pInit->MaxBuses != ioc->facts.MaxBuses) || + (pInit->ReplyFrameSize != cpu_to_le16(ioc->reply_sz)) || +@@ -2137,12 +2276,12 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + MPI_FUNCTION_FC_ABORT + MPI_FUNCTION_LAN_SEND + MPI_FUNCTION_LAN_RECEIVE +- MPI_FUNCTION_LAN_RESET ++ MPI_FUNCTION_LAN_RESET + */ + + printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - " + "Illegal request (function 0x%x) \n", +- ioc->name, __FILE__, __LINE__, hdr->Function); ++ ioc->name, __FILE__, __LINE__, function); + rc = -EFAULT; + goto done_free_mem; + } +@@ -2168,7 +2307,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + if (karg.dataInSize > 0) { + flagsLength = ( MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_END_OF_BUFFER | +- MPI_SGE_FLAGS_DIRECTION) ++ MPI_SGE_FLAGS_DIRECTION ) + << MPI_SGE_FLAGS_SHIFT; + } else { + flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE; +@@ -2230,7 +2369,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + + SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, hdr->MsgContext); + INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status) +- if (hdr->Function == MPI_FUNCTION_SCSI_TASK_MGMT) { ++ if (function == MPI_FUNCTION_SCSI_TASK_MGMT) { + + mutex_lock(&ioc->taskmgmt_cmds.mutex); + if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) { +@@ -2244,8 +2383,8 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + (ioc->facts.MsgVersion >= MPI_VERSION_01_05)) + mpt_put_msg_frame_hi_pri(mptctl_id, ioc, mf); + else { +- rc =mpt_send_handshake_request(mptctl_id, ioc, +- sizeof(SCSITaskMgmt_t), (u32*)mf, CAN_SLEEP); ++ rc = mpt_send_handshake_request(mptctl_id, ioc, ++ sizeof(SCSITaskMgmt_t), (u32*)mf, CAN_SLEEP); + if (rc != 0) { + dfailprintk(ioc, printk(MYIOC_s_ERR_FMT + "send_handshake FAILED! (ioc %p, mf %p)\n", +@@ -2256,19 +2395,17 @@ mptctl_do_mpt_command (struct mpt_ioctl_ + goto done_free_mem; + } + } +- + } else + mpt_put_msg_frame(mptctl_id, ioc, mf); + + /* Now wait for the command to complete */ + timeout = (karg.timeout > 0) ? karg.timeout : MPT_IOCTL_DEFAULT_TIMEOUT; + retry_wait: +- timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done, +- HZ*timeout); ++ timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done, HZ*timeout); + if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { + rc = -ETIME; + dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: TIMED OUT!\n", +- ioc->name, __func__)); ++ ioc->name, __FUNCTION__)); + if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) { + if (function == MPI_FUNCTION_SCSI_TASK_MGMT) + mutex_unlock(&ioc->taskmgmt_cmds.mutex); +@@ -2279,7 +2416,8 @@ retry_wait: + mutex_unlock(&ioc->taskmgmt_cmds.mutex); + mptctl_timeout_expired(ioc, mf); + mf = NULL; +- } else ++ } ++ else + goto retry_wait; + goto done_free_mem; + } +@@ -2287,7 +2425,6 @@ retry_wait: + if (function == MPI_FUNCTION_SCSI_TASK_MGMT) + mutex_unlock(&ioc->taskmgmt_cmds.mutex); + +- + mf = NULL; + + /* If a valid reply frame, copy to the user. +@@ -2295,8 +2432,7 @@ retry_wait: + */ + if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) { + if (karg.maxReplyBytes < ioc->reply_sz) { +- sz = min(karg.maxReplyBytes, +- 4*ioc->ioctl_cmds.reply[2]); ++ sz = min(karg.maxReplyBytes, 4*ioc->ioctl_cmds.reply[2]); + } else { + sz = min(ioc->reply_sz, 4*ioc->ioctl_cmds.reply[2]); + } +@@ -2318,8 +2454,7 @@ retry_wait: + if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_SENSE_VALID) { + sz = min(karg.maxSenseBytes, MPT_SENSE_BUFFER_SIZE); + if (sz > 0) { +- if (copy_to_user(karg.senseDataPtr, +- ioc->ioctl_cmds.sense, sz)) { ++ if (copy_to_user(karg.senseDataPtr, ioc->ioctl_cmds.sense, sz)) { + printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - " + "Unable to write sense data to user %p\n", + ioc->name, __FILE__, __LINE__, +@@ -2334,8 +2469,7 @@ retry_wait: + * to user. + */ + if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD) && +- (karg.dataInSize > 0) && (bufIn.kptr)) { +- ++ (karg.dataInSize > 0) && (bufIn.kptr)) { + if (copy_to_user(karg.dataInBufPtr, + bufIn.kptr, karg.dataInSize)) { + printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - " +@@ -2378,7 +2512,7 @@ done_free_mem: + * Outputs: None. + * Return: 0 if successful + * -EFAULT if data unavailable +- * -EBUSY if previous command timeout and IOC reset is not complete. ++ * -EBUSY if previous command timout and IOC reset is not complete. + * -ENODEV if no such device/adapter + * -ETIME if timer expires + * -ENOMEM if memory allocation error +@@ -2389,18 +2523,17 @@ mptctl_hp_hostinfo(unsigned long arg, un + hp_host_info_t __user *uarg = (void __user *) arg; + MPT_ADAPTER *ioc; + struct pci_dev *pdev; +- char *pbuf=NULL; ++ char *pbuf=NULL; + dma_addr_t buf_dma; + hp_host_info_t karg; +- CONFIGPARMS cfg; +- ConfigPageHeader_t hdr; + int iocnum; +- int rc, cim_rev; ++ int cim_rev; + ToolboxIstwiReadWriteRequest_t *IstwiRWRequest; + MPT_FRAME_HDR *mf = NULL; + MPIHeader_t *mpi_hdr; + unsigned long timeleft; + int retval; ++ u32 MsgContext; + + /* Reset long to int. Should affect IA64 and SPARC only + */ +@@ -2420,13 +2553,14 @@ mptctl_hp_hostinfo(unsigned long arg, un + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_hp_hostinfo() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; + } ++ + dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": mptctl_hp_hostinfo called.\n", + ioc->name)); +- + /* Fill in the data and return the structure to the calling + * program + */ +@@ -2466,42 +2600,9 @@ mptctl_hp_hostinfo(unsigned long arg, un + karg.fw_version[10] = (ioc->facts.FWVersion.Struct.Dev % 10 ) + '0'; + karg.fw_version[11] = '\0'; + +- /* Issue a config request to get the device serial number +- */ +- hdr.PageVersion = 0; +- hdr.PageLength = 0; +- hdr.PageNumber = 0; +- hdr.PageType = MPI_CONFIG_PAGETYPE_MANUFACTURING; +- cfg.cfghdr.hdr = &hdr; +- cfg.physAddr = -1; +- cfg.pageAddr = 0; +- cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; +- cfg.dir = 0; /* read */ +- cfg.timeout = 10; ++ strncpy(karg.serial_number, ioc->board_tracer, 16); + +- strncpy(karg.serial_number, " ", 24); +- if (mpt_config(ioc, &cfg) == 0) { +- if (cfg.cfghdr.hdr->PageLength > 0) { +- /* Issue the second config page request */ +- cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; +- +- pbuf = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4, &buf_dma); +- if (pbuf) { +- cfg.physAddr = buf_dma; +- if (mpt_config(ioc, &cfg) == 0) { +- ManufacturingPage0_t *pdata = (ManufacturingPage0_t *) pbuf; +- if (strlen(pdata->BoardTracerNumber) > 1) { +- strncpy(karg.serial_number, pdata->BoardTracerNumber, 24); +- karg.serial_number[24-1]='\0'; +- } +- } +- pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, pbuf, buf_dma); +- pbuf = NULL; +- } +- } +- } +- rc = mpt_GetIocState(ioc, 1); +- switch (rc) { ++ switch (mpt_GetIocState(ioc, 1)) { + case MPI_IOC_STATE_OPERATIONAL: + karg.ioc_status = HP_STATUS_OK; + break; +@@ -2537,21 +2638,23 @@ mptctl_hp_hostinfo(unsigned long arg, un + } + } + +- /* ++ /* + * Gather ISTWI(Industry Standard Two Wire Interface) Data + */ + if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { +- dfailprintk(ioc, printk(MYIOC_s_WARN_FMT +- "%s, no msg frames!!\n", ioc->name, __func__)); ++ dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n", ++ ioc->name,__FUNCTION__)); ++ retval = -ENOMEM; + goto out; + } + + IstwiRWRequest = (ToolboxIstwiReadWriteRequest_t *)mf; + mpi_hdr = (MPIHeader_t *) mf; ++ MsgContext = mpi_hdr->MsgContext; + memset(IstwiRWRequest,0,sizeof(ToolboxIstwiReadWriteRequest_t)); + IstwiRWRequest->Function = MPI_FUNCTION_TOOLBOX; + IstwiRWRequest->Tool = MPI_TOOLBOX_ISTWI_READ_WRITE_TOOL; +- IstwiRWRequest->MsgContext = mpi_hdr->MsgContext; ++ IstwiRWRequest->MsgContext = MsgContext; + IstwiRWRequest->Flags = MPI_TB_ISTWI_FLAGS_READ; + IstwiRWRequest->NumAddressBytes = 0x01; + IstwiRWRequest->DataLength = cpu_to_le16(0x04); +@@ -2561,23 +2664,21 @@ mptctl_hp_hostinfo(unsigned long arg, un + IstwiRWRequest->DeviceAddr = 0xB0; + + pbuf = pci_alloc_consistent(ioc->pcidev, 4, &buf_dma); +- if (!pbuf) ++ if (!pbuf) { ++ retval = -ENOMEM; + goto out; +- ioc->add_sge((char *)&IstwiRWRequest->SGL, +- (MPT_SGE_FLAGS_SSIMPLE_READ|4), buf_dma); ++ } ++ ioc->add_sge((char *)&IstwiRWRequest->SGL, (MPT_SGE_FLAGS_SSIMPLE_READ|4),buf_dma); + + retval = 0; +- SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, +- IstwiRWRequest->MsgContext); ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, IstwiRWRequest->MsgContext); + INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status) + mpt_put_msg_frame(mptctl_id, ioc, mf); +- + retry_wait: +- timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done, +- HZ*MPT_IOCTL_DEFAULT_TIMEOUT); ++ timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done, HZ*MPT_IOCTL_DEFAULT_TIMEOUT); + if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { + retval = -ETIME; +- printk(MYIOC_s_WARN_FMT "%s: failed\n", ioc->name, __func__); ++ printk(MYIOC_s_WARN_FMT "%s: failed\n", ioc->name, __FUNCTION__); + if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) { + mpt_free_msg_frame(ioc, mf); + goto out; +@@ -2617,7 +2718,7 @@ retry_wait: + return -EFAULT; + } + +- return 0; ++ return retval; + + } + +@@ -2627,7 +2728,7 @@ retry_wait: + * Outputs: None. + * Return: 0 if successful + * -EFAULT if data unavailable +- * -EBUSY if previous command timeout and IOC reset is not complete. ++ * -EBUSY if previous command timout and IOC reset is not complete. + * -ENODEV if no such device/adapter + * -ETIME if timer expires + * -ENOMEM if memory allocation error +@@ -2639,12 +2740,12 @@ mptctl_hp_targetinfo(unsigned long arg) + SCSIDevicePage0_t *pg0_alloc; + SCSIDevicePage3_t *pg3_alloc; + MPT_ADAPTER *ioc; +- MPT_SCSI_HOST *hd = NULL; ++ MPT_SCSI_HOST *hd = NULL; + hp_target_info_t karg; + int iocnum; + int data_sz; + dma_addr_t page_dma; +- CONFIGPARMS cfg; ++ CONFIGPARMS cfg; + ConfigPageHeader_t hdr; + int tmp, np, rc = 0; + +@@ -2657,13 +2758,14 @@ mptctl_hp_targetinfo(unsigned long arg) + + if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || + (ioc == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "%s::mptctl_hp_targetinfo() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnum); + return -ENODEV; + } +- dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_hp_targetinfo called.\n", +- ioc->name)); + ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": mptctl_hp_targetinfo called.\n", ++ ioc->name)); + /* There is nothing to do for FCP parts. + */ + if ((ioc->bus_type == SAS) || (ioc->bus_type == FC)) +@@ -2773,10 +2875,11 @@ mptctl_hp_targetinfo(unsigned long arg) + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +-static const struct file_operations mptctl_fops = { ++static struct file_operations mptctl_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, +- .fasync = mptctl_fasync, ++ .release = mptctl_release, ++ .fasync = mptctl_fasync, + .unlocked_ioctl = mptctl_ioctl, + #ifdef CONFIG_COMPAT + .compat_ioctl = compat_mpctl_ioctl, +@@ -2812,8 +2915,9 @@ compat_mptfwxfer_ioctl(struct file *filp + iocnumX = kfw32.iocnum & 0xFF; + if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) || + (iocp == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "::compat_mptfwxfer_ioctl @%d - ioc%d not found!\n", +- __LINE__, iocnumX); ++ __LINE__, iocnumX); + return -ENODEV; + } + +@@ -2852,8 +2956,9 @@ compat_mpt_command(struct file *filp, un + iocnumX = karg32.hdr.iocnum & 0xFF; + if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) || + (iocp == NULL)) { ++ if (mpt_debug_level & MPT_DEBUG_IOCTL) + printk(KERN_DEBUG MYNAM "::compat_mpt_command @%d - ioc%d not found!\n", +- __LINE__, iocnumX); ++ __LINE__, iocnumX); + return -ENODEV; + } + +@@ -2902,6 +3007,31 @@ static long compat_mpctl_ioctl(struct fi + case MPTHARDRESET: + case HP_GETHOSTINFO: + case HP_GETTARGETINFO: ++#if defined(CPQ_CIM) ++ case CC_CSMI_SAS_GET_DRIVER_INFO: ++ case CC_CSMI_SAS_GET_CNTLR_CONFIG: ++ case CC_CSMI_SAS_GET_CNTLR_STATUS: ++ case CC_CSMI_SAS_GET_SCSI_ADDRESS: ++ case CC_CSMI_SAS_GET_DEVICE_ADDRESS: ++ case CC_CSMI_SAS_GET_PHY_INFO: ++ case CC_CSMI_SAS_GET_SATA_SIGNATURE: ++ case CC_CSMI_SAS_GET_LINK_ERRORS: ++ case CC_CSMI_SAS_SMP_PASSTHRU: ++ case CC_CSMI_SAS_SSP_PASSTHRU: ++ case CC_CSMI_SAS_FIRMWARE_DOWNLOAD: ++ case CC_CSMI_SAS_GET_RAID_INFO: ++ case CC_CSMI_SAS_GET_RAID_CONFIG: ++ case CC_CSMI_SAS_GET_RAID_FEATURES: ++ case CC_CSMI_SAS_SET_RAID_CONTROL: ++ case CC_CSMI_SAS_GET_RAID_ELEMENT: ++ case CC_CSMI_SAS_SET_RAID_OPERATION: ++ case CC_CSMI_SAS_SET_PHY_INFO: ++ case CC_CSMI_SAS_STP_PASSTHRU: ++ case CC_CSMI_SAS_TASK_MANAGEMENT: ++ case CC_CSMI_SAS_PHY_CONTROL: ++ case CC_CSMI_SAS_GET_CONNECTOR_INFO: ++ case CC_CSMI_SAS_GET_LOCATION: ++#endif /* CPQ_CIM */ + case MPTTEST: + ret = __mptctl_ioctl(f, cmd, arg); + break; +@@ -2938,6 +3068,7 @@ mptctl_probe(struct pci_dev *pdev, const + + mutex_init(&ioc->ioctl_cmds.mutex); + init_completion(&ioc->ioctl_cmds.done); ++ + return 0; + } + +@@ -2951,6 +3082,22 @@ mptctl_probe(struct pci_dev *pdev, const + static void + mptctl_remove(struct pci_dev *pdev) + { ++#if defined(DIAG_BUFFER_SUPPORT) ++ MPT_ADAPTER *ioc = pci_get_drvdata(pdev); ++ int i; ++ ++ /* ++ * Cleanup diag buffer allocated memory ++ */ ++ for (i = 0; i < MPI_DIAG_BUF_TYPE_COUNT; i++) { ++ if (ioc->DiagBuffer[i] == NULL) ++ continue; ++ pci_free_consistent(ioc->pcidev, ioc->DiagBuffer_sz[i], ++ ioc->DiagBuffer[i], ioc->DiagBuffer_dma[i]); ++ ioc->DiagBuffer[i] = NULL; ++ ioc->DiagBuffer_Status[i] = 0; ++ } ++#endif + } + + static struct mpt_pci_driver mptctl_driver = { +@@ -3012,16 +3159,23 @@ static void mptctl_exit(void) + + /* De-register reset handler from base module */ + mpt_reset_deregister(mptctl_id); ++ mpt_reset_deregister(mptctl_taskmgmt_id); + + /* De-register callback handler from base module */ + mpt_deregister(mptctl_id); +- mpt_reset_deregister(mptctl_taskmgmt_id); +- +- mpt_device_driver_deregister(MPTCTL_DRIVER); + ++ mpt_device_driver_deregister(MPTCTL_DRIVER); + } + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + ++#if defined(CPQ_CIM) ++#include "csmi/csmisas.c" ++#endif // CPQ_CIM ++ ++#if defined(DIAG_BUFFER_SUPPORT) ++#include "rejected_ioctls/diag_buffer.c" ++#endif ++ + module_init(mptctl_init); + module_exit(mptctl_exit); +--- a/drivers/message/fusion/mptctl.h ++++ b/drivers/message/fusion/mptctl.h +@@ -1,5 +1,5 @@ + /* +- * linux/drivers/message/fusion/mptioctl.h ++ * linux/drivers/message/fusion/mptctl.h + * Fusion MPT misc device (ioctl) driver. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel +@@ -460,8 +460,5 @@ typedef struct _hp_target_info { + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +- +-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +- + #endif + +--- a/drivers/message/fusion/mptdebug.h ++++ b/drivers/message/fusion/mptdebug.h +@@ -17,6 +17,10 @@ + * + * Example: (programming for MPT_DEBUG_EVENTS on host 5) + * ++ * global setting: ++ * echo 8 > /sys/module/mptbase/parameters/mpt_debug_level ++ * ++ * per host setting: + * echo 8 > /sys/class/scsi_host/host5/debug_level + * + * -------------------------------------------------------- +@@ -55,10 +59,11 @@ + #define MPT_DEBUG_RESET 0x00008000 + #define MPT_DEBUG_SCSI 0x00010000 + #define MPT_DEBUG_IOCTL 0x00020000 ++#define MPT_DEBUG_CSMISAS 0x00040000 + #define MPT_DEBUG_FC 0x00080000 + #define MPT_DEBUG_SAS 0x00100000 + #define MPT_DEBUG_SAS_WIDE 0x00200000 +-#define MPT_DEBUG_36GB_MEM 0x00400000 ++#define MPT_DEBUG_36GB_MEM 0x00400000 + + /* + * CONFIG_FUSION_LOGGING - enabled in Kconfig +@@ -127,6 +132,9 @@ + #define dctlprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_IOCTL) + ++#define dcsmisasprintk(IOC, CMD) \ ++ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CSMISAS) ++ + #define dfcprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_FC) + +@@ -139,7 +147,6 @@ + #define d36memprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_36GB_MEM) + +- + /* + * Verbose logging + */ +--- a/drivers/message/fusion/mptfc.c ++++ b/drivers/message/fusion/mptfc.c +@@ -43,6 +43,7 @@ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++#include + #include + #include + #include +@@ -52,8 +53,10 @@ + #include /* for mdelay */ + #include /* needed for in_interrupt() proto */ + #include /* notifier code */ ++#include + #include + #include ++#include + + #include + #include +@@ -80,10 +83,18 @@ MODULE_VERSION(my_VERSION); + static int mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; /* reasonable default */ + module_param(mptfc_dev_loss_tmo, int, 0); + MODULE_PARM_DESC(mptfc_dev_loss_tmo, " Initial time the driver programs the " +- " transport to wait for an rport to " ++ " transport to wait for an rport to " + " return following a device loss event." + " Default=60."); + ++static int mpt_sdev_queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH; ++static int mptfc_set_sdev_queue_depth(const char *val, struct kernel_param *kp); ++module_param_call(mpt_sdev_queue_depth, mptfc_set_sdev_queue_depth, ++ param_get_int, &mpt_sdev_queue_depth, 0600); ++MODULE_PARM_DESC(mpt_sdev_queue_depth, ++ " Max Device Queue Depth (default=" ++ __MODULE_STRING(MPT_SCSI_CMD_PER_DEV_HIGH) ")"); ++ + /* scsi-mid layer global parmeter is max_report_luns, which is 511 */ + #define MPTFC_MAX_LUN (16895) + static int max_lun = MPTFC_MAX_LUN; +@@ -118,7 +129,7 @@ static struct scsi_host_template mptfc_d + .slave_configure = mptscsih_slave_configure, + .target_destroy = mptfc_target_destroy, + .slave_destroy = mptscsih_slave_destroy, +- .change_queue_depth = mptscsih_change_queue_depth, ++ .change_queue_depth = mptscsih_change_queue_depth, + .eh_abort_handler = mptfc_abort, + .eh_device_reset_handler = mptfc_dev_reset, + .eh_bus_reset_handler = mptfc_bus_reset, +@@ -183,6 +194,35 @@ static struct fc_function_template mptfc + .show_host_symbolic_name = 1, + }; + ++/** ++ * mptfc_set_sdev_queue_depth - global setting of the mpt_sdev_queue_depth ++ * found via /sys/module/mptfc/parameters/mpt_sdev_queue_depth ++ * @val: ++ * @kp: ++ * ++ * Returns ++ **/ ++static int ++mptfc_set_sdev_queue_depth(const char *val, struct kernel_param *kp) ++{ ++ int ret = param_set_int(val, kp); ++ MPT_ADAPTER *ioc; ++ struct scsi_device *sdev; ++ ++ if (ret) ++ return ret; ++ ++ list_for_each_entry(ioc, &ioc_list, list) { ++ if (ioc->bus_type != FC) ++ continue; ++ shost_for_each_device(sdev, ioc->sh) ++ mptscsih_change_queue_depth(sdev, mpt_sdev_queue_depth, ++ SCSI_QDEPTH_DEFAULT); ++ ioc->sdev_queue_depth = mpt_sdev_queue_depth; ++ } ++ return 0; ++} ++ + static int + mptfc_block_error_handler(struct scsi_cmnd *SCpnt, + int (*func)(struct scsi_cmnd *SCpnt), +@@ -194,7 +234,7 @@ mptfc_block_error_handler(struct scsi_cm + struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); + unsigned long flags; + int ready; +- MPT_ADAPTER *ioc; ++ MPT_ADAPTER *ioc; + + hd = shost_priv(SCpnt->device->host); + ioc = hd->ioc; +@@ -231,28 +271,28 @@ static int + mptfc_abort(struct scsi_cmnd *SCpnt) + { + return +- mptfc_block_error_handler(SCpnt, mptscsih_abort, __func__); ++ mptfc_block_error_handler(SCpnt, mptscsih_abort, __FUNCTION__); + } + + static int + mptfc_dev_reset(struct scsi_cmnd *SCpnt) + { + return +- mptfc_block_error_handler(SCpnt, mptscsih_dev_reset, __func__); ++ mptfc_block_error_handler(SCpnt, mptscsih_dev_reset, __FUNCTION__); + } + + static int + mptfc_bus_reset(struct scsi_cmnd *SCpnt) + { + return +- mptfc_block_error_handler(SCpnt, mptscsih_bus_reset, __func__); ++ mptfc_block_error_handler(SCpnt, mptscsih_bus_reset, __FUNCTION__); + } + + static int + mptfc_host_reset(struct scsi_cmnd *SCpnt) + { + return +- mptfc_block_error_handler(SCpnt, mptscsih_host_reset, __func__); ++ mptfc_block_error_handler(SCpnt, mptscsih_host_reset, __FUNCTION__); + } + + static void +@@ -335,7 +375,7 @@ mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in + + data_sz = hdr.PageLength * 4; + ppage0_alloc = pci_alloc_consistent(ioc->pcidev, data_sz, +- &page0_dma); ++ &page0_dma); + rc = -ENOMEM; + if (!ppage0_alloc) + break; +@@ -371,7 +411,7 @@ mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in + *p_pp0++ = p_p0++; /* save addr */ + } + pci_free_consistent(ioc->pcidev, data_sz, +- (u8 *) ppage0_alloc, page0_dma); ++ (u8 *) ppage0_alloc, page0_dma); + if (rc != 0) + break; + +@@ -476,6 +516,7 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int + if (vtarget) { + vtarget->id = pg0->CurrentTargetID; + vtarget->channel = pg0->CurrentBus; ++ vtarget->deleted = 0; + } + } + *((struct mptfc_rport_info **)rport->dd_data) = ri; +@@ -513,6 +554,7 @@ mptfc_target_destroy(struct scsi_target + struct fc_rport *rport; + struct mptfc_rport_info *ri; + ++ printk("%s - starget=%p\n", __FUNCTION__, starget); + rport = starget_to_rport(starget); + if (rport) { + ri = *((struct mptfc_rport_info **)rport->dd_data); +@@ -560,6 +602,7 @@ mptfc_target_alloc(struct scsi_target *s + + return rc; + } ++ + /* + * mptfc_dump_lun_info + * @ioc +@@ -589,7 +632,6 @@ mptfc_dump_lun_info(MPT_ADAPTER *ioc, st + (unsigned long long)nn)); + } + +- + /* + * OS entry point to allow host driver to alloc memory + * for each scsi device. Called once per device the bus scan. +@@ -604,7 +646,7 @@ mptfc_slave_alloc(struct scsi_device *sd + VirtDevice *vdevice; + struct scsi_target *starget; + struct fc_rport *rport; +- MPT_ADAPTER *ioc; ++ MPT_ADAPTER *ioc; + + starget = scsi_target(sdev); + rport = starget_to_rport(starget); +@@ -614,11 +656,10 @@ mptfc_slave_alloc(struct scsi_device *sd + + hd = shost_priv(sdev->host); + ioc = hd->ioc; +- + vdevice = kzalloc(sizeof(VirtDevice), GFP_KERNEL); + if (!vdevice) { + printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", +- ioc->name, sizeof(VirtDevice)); ++ ioc->name, sizeof(VirtDevice)); + return -ENOMEM; + } + +@@ -635,10 +676,7 @@ mptfc_slave_alloc(struct scsi_device *sd + vdevice->lun = sdev->lun; + + vtarget->num_luns++; +- +- + mptfc_dump_lun_info(ioc, rport, sdev, vtarget); +- + return 0; + } + +@@ -944,11 +982,12 @@ start_over: + return rc; + } + +-static void ++static int + mptfc_SetFcPortPage1_defaults(MPT_ADAPTER *ioc) + { + int ii; + FCPortPage1_t *pp1; ++ int rc; + + #define MPTFC_FW_DEVICE_TIMEOUT (1) + #define MPTFC_FW_IO_PEND_TIMEOUT (1) +@@ -956,8 +995,8 @@ mptfc_SetFcPortPage1_defaults(MPT_ADAPTE + #define OFF_FLAGS (MPI_FCPORTPAGE1_FLAGS_VERBOSE_RESCAN_EVENTS) + + for (ii=0; iifacts.NumberOfPorts; ii++) { +- if (mptfc_GetFcPortPage1(ioc, ii) != 0) +- continue; ++ if ((rc = mptfc_GetFcPortPage1(ioc, ii)) < 0) ++ return rc; + pp1 = ioc->fc_data.fc_port_page1[ii].data; + if ((pp1->InitiatorDeviceTimeout == MPTFC_FW_DEVICE_TIMEOUT) + && (pp1->InitiatorIoPendTimeout == MPTFC_FW_IO_PEND_TIMEOUT) +@@ -968,8 +1007,10 @@ mptfc_SetFcPortPage1_defaults(MPT_ADAPTE + pp1->InitiatorIoPendTimeout = MPTFC_FW_IO_PEND_TIMEOUT; + pp1->Flags &= ~OFF_FLAGS; + pp1->Flags |= ON_FLAGS; +- mptfc_WriteFcPortPage1(ioc, ii); ++ if ((rc = mptfc_WriteFcPortPage1(ioc, ii)) < 0) ++ return rc; + } ++ return 0; + } + + +@@ -1003,10 +1044,10 @@ mptfc_init_host_attr(MPT_ADAPTER *ioc,in + fc_host_maxframe_size(sh) = pp0->MaxFrameSize; + + fc_host_node_name(sh) = +- (u64)pp0->WWNN.High << 32 | (u64)pp0->WWNN.Low; ++ (u64)pp0->WWNN.High << 32 | (u64)pp0->WWNN.Low; + + fc_host_port_name(sh) = +- (u64)pp0->WWPN.High << 32 | (u64)pp0->WWPN.Low; ++ (u64)pp0->WWPN.High << 32 | (u64)pp0->WWPN.Low; + + fc_host_port_id(sh) = pp0->PortIdentifier; + +@@ -1082,10 +1123,13 @@ mptfc_link_status_change(struct work_str + static void + mptfc_setup_reset(struct work_struct *work) + { +- MPT_ADAPTER *ioc = ++ MPT_ADAPTER *ioc = + container_of(work, MPT_ADAPTER, fc_setup_reset_work); + u64 pn; + struct mptfc_rport_info *ri; ++ struct scsi_target *starget; ++ VirtTarget *vtarget; ++ + + /* reset about to happen, delete (block) all rports */ + list_for_each_entry(ri, &ioc->fc_rports, list) { +@@ -1093,6 +1137,12 @@ mptfc_setup_reset(struct work_struct *wo + ri->flags &= ~MPT_RPORT_INFO_FLAGS_REGISTERED; + fc_remote_port_delete(ri->rport); /* won't sleep */ + ri->rport = NULL; ++ starget = ri->starget; ++ if (starget) { ++ vtarget = starget->hostdata; ++ if (vtarget) ++ vtarget->deleted = 1; ++ } + + pn = (u64)ri->pg0.WWPN.High << 32 | + (u64)ri->pg0.WWPN.Low; +@@ -1111,8 +1161,22 @@ mptfc_rescan_devices(struct work_struct + MPT_ADAPTER *ioc = + container_of(work, MPT_ADAPTER, fc_rescan_work); + int ii; ++ int rc; + u64 pn; + struct mptfc_rport_info *ri; ++ struct scsi_target *starget; ++ VirtTarget *vtarget; ++ ++ /* ++ * if cannot set defaults, something's really wrong, bail out ++ */ ++ ++ if ((rc = mptfc_SetFcPortPage1_defaults(ioc)) < 0) { ++ dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT ++ "mptfc_rescan.%d: unable to set PP1 defaults, rc %d.\n", ++ ioc->name, ioc->sh->host_no, rc)); ++ return; ++ } + + /* start by tagging all ports as missing */ + list_for_each_entry(ri, &ioc->fc_rports, list) { +@@ -1140,6 +1204,12 @@ mptfc_rescan_devices(struct work_struct + MPT_RPORT_INFO_FLAGS_MISSING); + fc_remote_port_delete(ri->rport); /* won't sleep */ + ri->rport = NULL; ++ starget = ri->starget; ++ if (starget) { ++ vtarget = starget->hostdata; ++ if (vtarget) ++ vtarget->deleted = 1; ++ } + + pn = (u64)ri->pg0.WWPN.High << 32 | + (u64)ri->pg0.WWPN.Low; +@@ -1157,7 +1227,7 @@ mptfc_probe(struct pci_dev *pdev, const + { + struct Scsi_Host *sh; + MPT_SCSI_HOST *hd; +- MPT_ADAPTER *ioc; ++ MPT_ADAPTER *ioc; + unsigned long flags; + int ii; + int numSGE = 0; +@@ -1215,7 +1285,7 @@ mptfc_probe(struct pci_dev *pdev, const + ioc->name); + error = -1; + goto out_mptfc_probe; +- } ++ } + + spin_lock_init(&ioc->fc_rescan_work_lock); + INIT_WORK(&ioc->fc_rescan_work, mptfc_rescan_devices); +@@ -1238,6 +1308,10 @@ mptfc_probe(struct pci_dev *pdev, const + sh->max_id = ioc->pfacts->MaxDevices; + sh->max_lun = max_lun; + ++ sh->this_id = ioc->pfacts[0].PortSCSIID; ++ ++ ioc->sdev_queue_depth = mpt_sdev_queue_depth; ++ + /* Required entry. + */ + sh->unique_id = ioc->id; +@@ -1300,8 +1374,8 @@ mptfc_probe(struct pci_dev *pdev, const + + /* initialize workqueue */ + +- snprintf(ioc->fc_rescan_work_q_name, sizeof(ioc->fc_rescan_work_q_name), +- "mptfc_wq_%d", sh->host_no); ++ snprintf(ioc->fc_rescan_work_q_name, MPT_KOBJ_NAME_LEN, "mptfc_wq_%d", ++ sh->host_no); + ioc->fc_rescan_work_q = + create_singlethread_workqueue(ioc->fc_rescan_work_q_name); + if (!ioc->fc_rescan_work_q) +@@ -1314,7 +1388,6 @@ mptfc_probe(struct pci_dev *pdev, const + for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { + (void) mptfc_GetFcPortPage0(ioc, ii); + } +- mptfc_SetFcPortPage1_defaults(ioc); + + /* + * scan for rports - +@@ -1352,8 +1425,8 @@ mptfc_event_process(MPT_ADAPTER *ioc, Ev + unsigned long flags; + int rc=1; + +- devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", +- ioc->name, event)); ++ if (ioc->bus_type != FC) ++ return 0; + + if (ioc->sh == NULL || + ((hd = shost_priv(ioc->sh)) == NULL)) +@@ -1390,45 +1463,45 @@ mptfc_ioc_reset(MPT_ADAPTER *ioc, int re + unsigned long flags; + + rc = mptscsih_ioc_reset(ioc,reset_phase); +- if (rc == 0) ++ if ((ioc->bus_type != FC) || (!rc)) + return rc; + +- +- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- ": IOC %s_reset routed to FC host driver!\n",ioc->name, +- reset_phase==MPT_IOC_SETUP_RESET ? "setup" : ( +- reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"))); +- +- if (reset_phase == MPT_IOC_SETUP_RESET) { ++ switch(reset_phase) { ++ case MPT_IOC_SETUP_RESET: ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __FUNCTION__)); + spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); + if (ioc->fc_rescan_work_q) { + queue_work(ioc->fc_rescan_work_q, + &ioc->fc_setup_reset_work); + } + spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); +- } +- +- else if (reset_phase == MPT_IOC_PRE_RESET) { +- } +- +- else { /* MPT_IOC_POST_RESET */ +- mptfc_SetFcPortPage1_defaults(ioc); ++ break; ++ case MPT_IOC_PRE_RESET: ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "%s: MPT_IOC_PRE_RESET\n", ioc->name, __FUNCTION__)); ++ break; ++ case MPT_IOC_POST_RESET: ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "%s: MPT_IOC_POST_RESET\n", ioc->name, __FUNCTION__)); + spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); + if (ioc->fc_rescan_work_q) { + queue_work(ioc->fc_rescan_work_q, + &ioc->fc_rescan_work); + } + spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); ++ break; ++ default: ++ break; + } + return 1; + } + +-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * mptfc_init - Register MPT adapter(s) as SCSI host(s) with SCSI mid-layer. + * + * Returns 0 for success, non-zero for failure. +- */ ++ **/ + static int __init + mptfc_init(void) + { +@@ -1460,12 +1533,11 @@ mptfc_init(void) + return error; + } + +-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * mptfc_remove - Remove fc infrastructure for devices + * @pdev: Pointer to pci_dev structure + * +- */ ++ **/ + static void __devexit + mptfc_remove(struct pci_dev *pdev) + { +@@ -1475,6 +1547,8 @@ mptfc_remove(struct pci_dev *pdev) + unsigned long flags; + int ii; + ++ printk("%s -pdev=%p\n", __FUNCTION__, pdev); ++ + /* destroy workqueue */ + if ((work_q=ioc->fc_rescan_work_q)) { + spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); +@@ -1517,7 +1591,6 @@ mptfc_exit(void) + + mpt_reset_deregister(mptfcDoneCtx); + mpt_event_deregister(mptfcDoneCtx); +- + mpt_deregister(mptfcInternalCtx); + mpt_deregister(mptfcTaskCtx); + mpt_deregister(mptfcDoneCtx); +--- a/drivers/message/fusion/mptlan.c ++++ b/drivers/message/fusion/mptlan.c +@@ -6,7 +6,6 @@ + * + * Copyright (c) 2000-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.com) +- * + */ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /* +@@ -78,6 +77,12 @@ MODULE_VERSION(my_VERSION); + * Fusion MPT LAN private structures + */ + ++struct NAA_Hosed { ++ u16 NAA; ++ u8 ieee[FC_ALEN]; ++ struct NAA_Hosed *next; ++}; ++ + struct BufferControl { + struct sk_buff *skb; + dma_addr_t dma; +@@ -107,6 +112,7 @@ struct mpt_lan_priv { + + u32 total_posted; + u32 total_received; ++ struct net_device_stats stats; /* Per device statistics */ + + struct delayed_work post_buckets_task; + struct net_device *dev; +@@ -153,6 +159,16 @@ static u8 LanCtx = MPT_MAX_PROTOCOL_DRIV + static u32 max_buckets_out = 127; + static u32 tx_max_out_p = 127 - 16; + ++#ifdef QLOGIC_NAA_WORKAROUND ++static struct NAA_Hosed *mpt_bad_naa = NULL; ++DEFINE_RWLOCK(bad_naa_lock); ++#endif ++ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++/* ++ * Fusion MPT LAN external data ++ */ ++ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * lan_reply - Handle all data sent from the hardware. +@@ -179,8 +195,7 @@ lan_reply (MPT_ADAPTER *ioc, MPT_FRAME_H + u32 tmsg = CAST_PTR_TO_U32(reply); + + dioprintk((KERN_INFO MYNAM ": %s/%s: @lan_reply, tmsg %08x\n", +- IOC_AND_NETDEV_NAMES_s_s(dev), +- tmsg)); ++ IOC_AND_NETDEV_NAMES_s_s(dev), tmsg)); + + switch (GET_LAN_FORM(tmsg)) { + +@@ -429,6 +444,7 @@ mpt_lan_open(struct net_device *dev) + dlprintk((KERN_INFO MYNAM "/lo: Finished initializing RcvCtl\n")); + + mpt_lan_post_receive_buckets(priv); ++ + printk(KERN_INFO MYNAM ": %s/%s: interface up & active\n", + IOC_AND_NETDEV_NAMES_s_s(dev)); + +@@ -572,7 +588,6 @@ mpt_lan_tx_timeout(struct net_device *de + } + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +-//static inline int + static int + mpt_lan_send_turbo(struct net_device *dev, u32 tmsg) + { +@@ -585,12 +600,12 @@ mpt_lan_send_turbo(struct net_device *de + ctx = GET_LAN_BUFFER_CONTEXT(tmsg); + sent = priv->SendCtl[ctx].skb; + +- dev->stats.tx_packets++; +- dev->stats.tx_bytes += sent->len; ++ priv->stats.tx_packets++; ++ priv->stats.tx_bytes += sent->len; + + dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n", + IOC_AND_NETDEV_NAMES_s_s(dev), +- __func__, sent)); ++ __FUNCTION__, sent)); + + priv->SendCtl[ctx].skb = NULL; + pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma, +@@ -627,7 +642,7 @@ mpt_lan_send_reply(struct net_device *de + + switch (le16_to_cpu(pSendRep->IOCStatus) & MPI_IOCSTATUS_MASK) { + case MPI_IOCSTATUS_SUCCESS: +- dev->stats.tx_packets += count; ++ priv->stats.tx_packets += count; + break; + + case MPI_IOCSTATUS_LAN_CANCELED: +@@ -635,13 +650,13 @@ mpt_lan_send_reply(struct net_device *de + break; + + case MPI_IOCSTATUS_INVALID_SGL: +- dev->stats.tx_errors += count; ++ priv->stats.tx_errors += count; + printk (KERN_ERR MYNAM ": %s/%s: ERROR - Invalid SGL sent to IOC!\n", + IOC_AND_NETDEV_NAMES_s_s(dev)); + goto out; + + default: +- dev->stats.tx_errors += count; ++ priv->stats.tx_errors += count; + break; + } + +@@ -652,11 +667,11 @@ mpt_lan_send_reply(struct net_device *de + ctx = GET_LAN_BUFFER_CONTEXT(le32_to_cpu(*pContext)); + + sent = priv->SendCtl[ctx].skb; +- dev->stats.tx_bytes += sent->len; ++ priv->stats.tx_bytes += sent->len; + + dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n", + IOC_AND_NETDEV_NAMES_s_s(dev), +- __func__, sent)); ++ __FUNCTION__, sent)); + + priv->SendCtl[ctx].skb = NULL; + pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma, +@@ -695,7 +710,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, s + u16 cur_naa = 0x1000; + + dioprintk((KERN_INFO MYNAM ": %s called, skb_addr = %p\n", +- __func__, skb)); ++ __FUNCTION__, skb)); + + spin_lock_irqsave(&priv->txfidx_lock, flags); + if (priv->mpt_txfidx_tail < 0) { +@@ -703,8 +718,8 @@ mpt_lan_sdu_send (struct sk_buff *skb, s + spin_unlock_irqrestore(&priv->txfidx_lock, flags); + + printk (KERN_ERR "%s: no tx context available: %u\n", +- __func__, priv->mpt_txfidx_tail); +- return NETDEV_TX_BUSY; ++ __FUNCTION__, priv->mpt_txfidx_tail); ++ return 1; + } + + mf = mpt_get_msg_frame(LanCtx, mpt_dev); +@@ -713,8 +728,8 @@ mpt_lan_sdu_send (struct sk_buff *skb, s + spin_unlock_irqrestore(&priv->txfidx_lock, flags); + + printk (KERN_ERR "%s: Unable to alloc request frame\n", +- __func__); +- return NETDEV_TX_BUSY; ++ __FUNCTION__); ++ return 1; + } + + ctx = priv->mpt_txfidx[priv->mpt_txfidx_tail--]; +@@ -731,7 +746,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, s + skb_reset_mac_header(skb); + skb_pull(skb, 12); + +- dma = pci_map_single(mpt_dev->pcidev, skb->data, skb->len, ++ dma = pci_map_single(mpt_dev->pcidev, skb->data, skb->len, + PCI_DMA_TODEVICE); + + priv->SendCtl[ctx].skb = skb; +@@ -761,6 +776,32 @@ mpt_lan_sdu_send (struct sk_buff *skb, s + + mac = skb_mac_header(skb); + ++#ifdef QLOGIC_NAA_WORKAROUND ++{ ++ struct NAA_Hosed *nh; ++ ++ /* Munge the NAA for Tx packets to QLogic boards, which don't follow ++ RFC 2625. The longer I look at this, the more my opinion of Qlogic ++ drops. */ ++ read_lock_irq(&bad_naa_lock); ++ for (nh = mpt_bad_naa; nh != NULL; nh=nh->next) { ++ if ((nh->ieee[0] == mac[0]) && ++ (nh->ieee[1] == mac[1]) && ++ (nh->ieee[2] == mac[2]) && ++ (nh->ieee[3] == mac[3]) && ++ (nh->ieee[4] == mac[4]) && ++ (nh->ieee[5] == mac[5])) { ++ cur_naa = nh->NAA; ++ dlprintk ((KERN_INFO "mptlan/sdu_send: using NAA value " ++ "= %04x.\n", cur_naa)); ++ break; ++ } ++ } ++ read_unlock_irq(&bad_naa_lock); ++} ++#endif ++ ++ + pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa << 16) | + (mac[0] << 8) | + (mac[1] << 0)); +@@ -784,7 +825,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, s + MPI_SGE_FLAGS_END_OF_LIST) << MPI_SGE_FLAGS_SHIFT) | + skb->len); + pSimple->Address.Low = cpu_to_le32((u32) dma); +- if (sizeof(dma_addr_t) > sizeof(u32)) ++ if (mpt_dev->sg_addr_size > sizeof(u32)) + pSimple->Address.High = cpu_to_le32((u32) ((u64) dma >> 32)); + else + pSimple->Address.High = 0; +@@ -796,7 +837,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, s + IOC_AND_NETDEV_NAMES_s_s(dev), + le32_to_cpu(pSimple->FlagsLength))); + +- return NETDEV_TX_OK; ++ return 0; + } + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +@@ -807,7 +848,7 @@ mpt_lan_wake_post_buckets_task(struct ne + */ + { + struct mpt_lan_priv *priv = netdev_priv(dev); +- ++ + if (test_and_set_bit(0, &priv->post_buckets_active) == 0) { + if (priority) { + schedule_delayed_work(&priv->post_buckets_task, 0); +@@ -816,7 +857,7 @@ mpt_lan_wake_post_buckets_task(struct ne + dioprintk((KERN_INFO MYNAM ": post_buckets queued on " + "timer.\n")); + } +- dioprintk((KERN_INFO MYNAM ": %s/%s: Queued post_buckets task.\n", ++ dioprintk((KERN_INFO MYNAM ": %s/%s: Queued post_buckets task.\n", + IOC_AND_NETDEV_NAMES_s_s(dev) )); + } + } +@@ -833,8 +874,8 @@ mpt_lan_receive_skb(struct net_device *d + "delivered to upper level.\n", + IOC_AND_NETDEV_NAMES_s_s(dev), skb->len)); + +- dev->stats.rx_bytes += skb->len; +- dev->stats.rx_packets++; ++ priv->stats.rx_bytes += skb->len; ++ priv->stats.rx_packets++; + + skb->dev = dev; + netif_rx(skb); +@@ -1073,7 +1114,6 @@ mpt_lan_receive_post_reply(struct net_de + PCI_DMA_FROMDEVICE); + + skb_copy_from_linear_data(old_skb, skb_put(skb, len), len); +- + pci_dma_sync_single_for_device(mpt_dev->pcidev, + priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, +@@ -1121,22 +1161,20 @@ mpt_lan_receive_post_reply(struct net_de + "(priv->buckets_out = %d)\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + remaining, atomic_read(&priv->buckets_out)); +- ++ + if ((remaining < priv->bucketthresh) && + ((atomic_read(&priv->buckets_out) - remaining) > + MPT_LAN_BUCKETS_REMAIN_MISMATCH_THRESH)) { +- + printk (KERN_WARNING MYNAM " Mismatch between driver's " + "buckets_out count and fw's BucketsRemaining " + "count has crossed the threshold, issuing a " + "LanReset to clear the fw's hashtable. You may " + "want to check your /var/log/messages for \"CRC " + "error\" event notifications.\n"); +- + mpt_lan_reset(dev); + mpt_lan_wake_post_buckets_task(dev, 0); + } +- ++ + return mpt_lan_receive_skb(dev, skb); + } + +@@ -1164,7 +1202,7 @@ mpt_lan_post_receive_buckets(struct mpt_ + + dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, Start_buckets = %u, buckets_out = %u\n", + IOC_AND_NETDEV_NAMES_s_s(dev), +- __func__, buckets, curr)); ++ __FUNCTION__, buckets, curr)); + + max = (mpt_dev->req_sz - MPT_LAN_RECEIVE_POST_REQUEST_SIZE) / + (MPT_LAN_TRANSACTION32_SIZE + sizeof(SGESimple64_t)); +@@ -1173,9 +1211,9 @@ mpt_lan_post_receive_buckets(struct mpt_ + mf = mpt_get_msg_frame(LanCtx, mpt_dev); + if (mf == NULL) { + printk (KERN_ERR "%s: Unable to alloc request frame\n", +- __func__); ++ __FUNCTION__); + dioprintk((KERN_ERR "%s: %u buckets remaining\n", +- __func__, buckets)); ++ __FUNCTION__, buckets)); + goto out; + } + pRecvReq = (LANReceivePostRequest_t *) mf; +@@ -1200,7 +1238,7 @@ mpt_lan_post_receive_buckets(struct mpt_ + spin_lock_irqsave(&priv->rxfidx_lock, flags); + if (priv->mpt_rxfidx_tail < 0) { + printk (KERN_ERR "%s: Can't alloc context\n", +- __func__); ++ __FUNCTION__); + spin_unlock_irqrestore(&priv->rxfidx_lock, + flags); + break; +@@ -1223,7 +1261,7 @@ mpt_lan_post_receive_buckets(struct mpt_ + if (skb == NULL) { + printk (KERN_WARNING + MYNAM "/%s: Can't alloc skb\n", +- __func__); ++ __FUNCTION__); + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; + spin_unlock_irqrestore(&priv->rxfidx_lock, flags); + break; +@@ -1251,7 +1289,7 @@ mpt_lan_post_receive_buckets(struct mpt_ + MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_64_BIT_ADDRESSING) << MPI_SGE_FLAGS_SHIFT) | len); + pSimple->Address.Low = cpu_to_le32((u32) priv->RcvCtl[ctx].dma); +- if (sizeof(dma_addr_t) > sizeof(u32)) ++ if (mpt_dev->sg_addr_size > sizeof(u32)) + pSimple->Address.High = cpu_to_le32((u32) ((u64) priv->RcvCtl[ctx].dma >> 32)); + else + pSimple->Address.High = 0; +@@ -1261,7 +1299,7 @@ mpt_lan_post_receive_buckets(struct mpt_ + + if (pSimple == NULL) { + /**/ printk (KERN_WARNING MYNAM "/%s: No buckets posted\n", +-/**/ __func__); ++/**/ __FUNCTION__); + mpt_free_msg_frame(mpt_dev, mf); + goto out; + } +@@ -1285,9 +1323,9 @@ mpt_lan_post_receive_buckets(struct mpt_ + + out: + dioprintk((KERN_INFO MYNAM "/%s: End_buckets = %u, priv->buckets_out = %u\n", +- __func__, buckets, atomic_read(&priv->buckets_out))); ++ __FUNCTION__, buckets, atomic_read(&priv->buckets_out))); + dioprintk((KERN_INFO MYNAM "/%s: Posted %u buckets and received %u back\n", +- __func__, priv->total_posted, priv->total_received)); ++ __FUNCTION__, priv->total_posted, priv->total_received)); + + clear_bit(0, &priv->post_buckets_active); + } +@@ -1296,7 +1334,7 @@ static void + mpt_lan_post_receive_buckets_work(struct work_struct *work) + { + mpt_lan_post_receive_buckets(container_of(work, struct mpt_lan_priv, +- post_buckets_task.work)); ++ post_buckets_task.work)); + } + + static const struct net_device_ops mpt_netdev_ops = { +@@ -1311,11 +1349,10 @@ static const struct net_device_ops mpt_n + static struct net_device * + mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum) + { +- struct net_device *dev; +- struct mpt_lan_priv *priv; ++ struct net_device *dev = alloc_fcdev(sizeof(struct mpt_lan_priv)); ++ struct mpt_lan_priv *priv = NULL; + u8 HWaddr[FC_ALEN], *a; + +- dev = alloc_fcdev(sizeof(struct mpt_lan_priv)); + if (!dev) + return NULL; + +@@ -1327,8 +1364,9 @@ mpt_register_lan_device (MPT_ADAPTER *mp + priv->mpt_dev = mpt_dev; + priv->pnum = pnum; + ++ memset(&priv->post_buckets_task, 0, sizeof(priv->post_buckets_task)); + INIT_DELAYED_WORK(&priv->post_buckets_task, +- mpt_lan_post_receive_buckets_work); ++ mpt_lan_post_receive_buckets_work); + priv->post_buckets_active = 0; + + dlprintk((KERN_INFO MYNAM "@%d: bucketlen = %d\n", +@@ -1351,6 +1389,8 @@ mpt_register_lan_device (MPT_ADAPTER *mp + spin_lock_init(&priv->txfidx_lock); + spin_lock_init(&priv->rxfidx_lock); + ++ memset(&priv->stats, 0, sizeof(priv->stats)); ++ + /* Grab pre-fetched LANPage1 stuff. :-) */ + a = (u8 *) &mpt_dev->lan_cnfg_page1.HardwareAddressLow; + +@@ -1372,6 +1412,8 @@ mpt_register_lan_device (MPT_ADAPTER *mp + tx_max_out_p : MPT_TX_MAX_OUT_LIM; + + dev->netdev_ops = &mpt_netdev_ops; ++ ++/* Not in 2.3.42. Need 2.3.45+ */ + dev->watchdog_timeo = MPT_LAN_TX_TIMEOUT; + + dlprintk((KERN_INFO MYNAM ": Finished registering dev " +@@ -1387,7 +1429,7 @@ mpt_register_lan_device (MPT_ADAPTER *mp + static int + mptlan_probe(struct pci_dev *pdev, const struct pci_device_id *id) + { +- MPT_ADAPTER *ioc = pci_get_drvdata(pdev); ++ MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + struct net_device *dev; + int i; + +@@ -1414,14 +1456,16 @@ mptlan_probe(struct pci_dev *pdev, const + ioc->pfacts[i].PortNumber); + continue; + } +- ++ + printk(KERN_INFO MYNAM ": %s: Fusion MPT LAN device " + "registered as '%s'\n", ioc->name, dev->name); + printk(KERN_INFO MYNAM ": %s/%s: " +- "LanAddr = %pM\n", ++ "LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + IOC_AND_NETDEV_NAMES_s_s(dev), +- dev->dev_addr); +- ++ dev->dev_addr[0], dev->dev_addr[1], ++ dev->dev_addr[2], dev->dev_addr[3], ++ dev->dev_addr[4], dev->dev_addr[5]); ++ + ioc->netdev = dev; + + return 0; +@@ -1433,7 +1477,7 @@ mptlan_probe(struct pci_dev *pdev, const + static void + mptlan_remove(struct pci_dev *pdev) + { +- MPT_ADAPTER *ioc = pci_get_drvdata(pdev); ++ MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + struct net_device *dev = ioc->netdev; + + if(dev != NULL) { +@@ -1466,7 +1510,6 @@ static int __init mpt_lan_init (void) + } + + dlprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n")); +- + mpt_device_driver_register(&mptlan_driver, MPTLAN_DRIVER); + return 0; + } +@@ -1505,8 +1548,9 @@ mpt_lan_type_trans(struct sk_buff *skb, + + printk (KERN_WARNING MYNAM ": %s: WARNING - Broadcast swap F/W bug detected!\n", + NETDEV_PTR_TO_IOC_NAME_s(dev)); +- printk (KERN_WARNING MYNAM ": Please update sender @ MAC_addr = %pM\n", +- fch->saddr); ++ printk (KERN_WARNING MYNAM ": Please update sender @ MAC_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", ++ fch->saddr[0], fch->saddr[1], fch->saddr[2], ++ fch->saddr[3], fch->saddr[4], fch->saddr[5]); + } + + if (*fch->daddr & 1) { +@@ -1525,6 +1569,80 @@ mpt_lan_type_trans(struct sk_buff *skb, + + fcllc = (struct fcllc *)skb->data; + ++#ifdef QLOGIC_NAA_WORKAROUND ++{ ++ u16 source_naa = fch->stype, found = 0; ++ ++ /* Workaround for QLogic not following RFC 2625 in regards to the NAA ++ value. */ ++ ++ if ((source_naa & 0xF000) == 0) ++ source_naa = swab16(source_naa); ++ ++ if (fcllc->ethertype == htons(ETH_P_ARP)) ++ dlprintk ((KERN_INFO "mptlan/type_trans: got arp req/rep w/ naa of " ++ "%04x.\n", source_naa)); ++ ++ if ((fcllc->ethertype == htons(ETH_P_ARP)) && ++ ((source_naa >> 12) != MPT_LAN_NAA_RFC2625)){ ++ struct NAA_Hosed *nh, *prevnh; ++ int i; ++ ++ dlprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep from " ++ "system with non-RFC 2625 NAA value (%04x).\n", ++ source_naa)); ++ ++ write_lock_irq(&bad_naa_lock); ++ for (prevnh = nh = mpt_bad_naa; nh != NULL; ++ prevnh=nh, nh=nh->next) { ++ if ((nh->ieee[0] == fch->saddr[0]) && ++ (nh->ieee[1] == fch->saddr[1]) && ++ (nh->ieee[2] == fch->saddr[2]) && ++ (nh->ieee[3] == fch->saddr[3]) && ++ (nh->ieee[4] == fch->saddr[4]) && ++ (nh->ieee[5] == fch->saddr[5])) { ++ found = 1; ++ dlprintk ((KERN_INFO "mptlan/type_trans: ARP Re" ++ "q/Rep w/ bad NAA from system already" ++ " in DB.\n")); ++ break; ++ } ++ } ++ ++ if ((!found) && (nh == NULL)) { ++ ++ nh = kmalloc(sizeof(struct NAA_Hosed), GFP_KERNEL); ++ dlprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep w/" ++ " bad NAA from system not yet in DB.\n")); ++ ++ if (nh != NULL) { ++ nh->next = NULL; ++ if (!mpt_bad_naa) ++ mpt_bad_naa = nh; ++ if (prevnh) ++ prevnh->next = nh; ++ ++ nh->NAA = source_naa; /* Set the S_NAA value. */ ++ for (i = 0; i < FC_ALEN; i++) ++ nh->ieee[i] = fch->saddr[i]; ++ dlprintk ((KERN_INFO "Got ARP from %02x:%02x:%02x:%02x:" ++ "%02x:%02x with non-compliant S_NAA value.\n", ++ fch->saddr[0], fch->saddr[1], fch->saddr[2], ++ fch->saddr[3], fch->saddr[4],fch->saddr[5])); ++ } else { ++ printk (KERN_ERR "mptlan/type_trans: Unable to" ++ " kmalloc a NAA_Hosed struct.\n"); ++ } ++ } else if (!found) { ++ printk (KERN_ERR "mptlan/type_trans: found not" ++ " set, but nh isn't null. Evil " ++ "funkiness abounds.\n"); ++ } ++ write_unlock_irq(&bad_naa_lock); ++ } ++} ++#endif ++ + /* Strip the SNAP header from ARP packets since we don't + * pass them through to the 802.2/SNAP layers. + */ +--- a/drivers/message/fusion/mptlan.h ++++ b/drivers/message/fusion/mptlan.h +@@ -6,7 +6,6 @@ + * + * Copyright (c) 2000-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.com) +- * + */ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /* +@@ -73,6 +72,7 @@ + + #include + #include ++#include + + /* Override mptbase.h by pre-defining these! */ + #define MODULEAUTHOR "LSI Corporation" +--- a/drivers/message/fusion/mptsas.c ++++ b/drivers/message/fusion/mptsas.c +@@ -47,9 +47,11 @@ + #include + #include + #include +-#include ++#include + #include ++#include + #include /* for mdelay */ ++#include + + #include + #include +@@ -62,7 +64,6 @@ + #include "mptscsih.h" + #include "mptsas.h" + +- + #define my_NAME "Fusion MPT SAS Host driver" + #define my_VERSION MPT_LINUX_VERSION_COMMON + #define MYNAM "mptsas" +@@ -73,6 +74,7 @@ + #define MPTSAS_RAID_CHANNEL 1 + + #define SAS_CONFIG_PAGE_TIMEOUT 30 ++ + MODULE_AUTHOR(MODULEAUTHOR); + MODULE_DESCRIPTION(my_NAME); + MODULE_LICENSE("GPL"); +@@ -84,6 +86,25 @@ MODULE_PARM_DESC(mpt_pt_clear, + " Clear persistency table: enable=1 " + "(default=MPTSCSIH_PT_CLEAR=0)"); + ++static int mpt_cmd_retry_count = 300; ++module_param(mpt_cmd_retry_count, int, 0); ++MODULE_PARM_DESC(mpt_cmd_retry_count, ++ " Device discovery TUR command retry count: default=300"); ++ ++static int mpt_disable_hotplug_remove = 0; ++module_param(mpt_disable_hotplug_remove, int, 0); ++MODULE_PARM_DESC(mpt_disable_hotplug_remove, ++ " Disable hotpug remove events: default=0"); ++ ++static int mpt_sdev_queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH; ++static int mptsas_set_sdev_queue_depth(const char *val, ++ struct kernel_param *kp); ++module_param_call(mpt_sdev_queue_depth, mptsas_set_sdev_queue_depth, ++ param_get_int, &mpt_sdev_queue_depth, 0600); ++MODULE_PARM_DESC(mpt_sdev_queue_depth, ++ " Max Device Queue Depth (default=" ++ __MODULE_STRING(MPT_SCSI_CMD_PER_DEV_HIGH) ")"); ++ + /* scsi-mid layer global parmeter is max_report_luns, which is 511 */ + #define MPTSAS_MAX_LUN (16895) + static int max_lun = MPTSAS_MAX_LUN; +@@ -96,7 +117,6 @@ static u8 mptsasInternalCtx = MPT_MAX_PR + static u8 mptsasMgmtCtx = MPT_MAX_PROTOCOL_DRIVERS; + static u8 mptsasDeviceResetCtx = MPT_MAX_PROTOCOL_DRIVERS; + +-static void mptsas_firmware_event_work(struct work_struct *work); + static void mptsas_send_sas_event(struct fw_event_work *fw_event); + static void mptsas_send_raid_event(struct fw_event_work *fw_event); + static void mptsas_send_ir2_event(struct fw_event_work *fw_event); +@@ -126,6 +146,39 @@ static void mptsas_broadcast_primative_w + static void mptsas_handle_queue_full_event(struct fw_event_work *fw_event); + static void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id); + ++ ++void mptsas_schedule_target_reset(void *ioc); ++static void mptsas_firmware_event_work(struct work_struct *work); ++ ++/** ++ * mptsas_set_sdev_queue_depth - global setting of the mpt_sdev_queue_depth ++ * found via /sys/module/mptsas/parameters/mpt_sdev_queue_depth ++ * @val: ++ * @kp: ++ * ++ * Returns ++ **/ ++static int ++mptsas_set_sdev_queue_depth(const char *val, struct kernel_param *kp) ++{ ++ int ret = param_set_int(val, kp); ++ MPT_ADAPTER *ioc; ++ struct scsi_device *sdev; ++ ++ if (ret) ++ return ret; ++ ++ list_for_each_entry(ioc, &ioc_list, list) { ++ if (ioc->bus_type != SAS) ++ continue; ++ shost_for_each_device(sdev, ioc->sh) ++ mptscsih_change_queue_depth(sdev, mpt_sdev_queue_depth, ++ SCSI_QDEPTH_DEFAULT); ++ ioc->sdev_queue_depth = mpt_sdev_queue_depth; ++ } ++ return 0; ++} ++ + static void mptsas_print_phy_data(MPT_ADAPTER *ioc, + MPI_SAS_IO_UNIT0_PHY_DATA *phy_data) + { +@@ -279,6 +332,10 @@ mptsas_add_fw_event(MPT_ADAPTER *ioc, st + { + unsigned long flags; + ++#if defined(CPQ_CIM) ++ ioc->csmi_change_count++; ++#endif ++ + spin_lock_irqsave(&ioc->fw_event_lock, flags); + list_add_tail(&fw_event->list, &ioc->fw_event_list); + INIT_DELAYED_WORK(&fw_event->work, mptsas_firmware_event_work); +@@ -297,7 +354,7 @@ mptsas_requeue_fw_event(MPT_ADAPTER *ioc + unsigned long flags; + spin_lock_irqsave(&ioc->fw_event_lock, flags); + devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: reschedule task " +- "(fw_event=0x%p)\n", ioc->name, __func__, fw_event)); ++ "(fw_event=0x%p)\n", ioc->name,__FUNCTION__, fw_event)); + fw_event->retries++; + queue_delayed_work(ioc->fw_event_q, &fw_event->work, + msecs_to_jiffies(delay)); +@@ -325,7 +382,7 @@ mptsas_cleanup_fw_event_q(MPT_ADAPTER *i + { + struct fw_event_work *fw_event, *next; + struct mptsas_target_reset_event *target_reset_list, *n; +- MPT_SCSI_HOST *hd = shost_priv(ioc->sh); ++ MPT_SCSI_HOST *hd = shost_priv(ioc->sh); + + /* flush the target_reset_list */ + if (!list_empty(&hd->target_reset_list)) { +@@ -436,7 +493,14 @@ mptsas_is_end_device(struct mptsas_devin + return 0; + } + +-/* no mutex */ ++/** ++ * mptsas_port_delete - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @port_details: ++ * ++ * (no mutex) ++ * ++ **/ + static void + mptsas_port_delete(MPT_ADAPTER *ioc, struct mptsas_portinfo_details * port_details) + { +@@ -465,6 +529,11 @@ mptsas_port_delete(MPT_ADAPTER *ioc, str + kfree(port_details); + } + ++/** ++ * mptsas_get_rphy - ++ * @phy_info: ++ * ++ **/ + static inline struct sas_rphy * + mptsas_get_rphy(struct mptsas_phyinfo *phy_info) + { +@@ -474,6 +543,13 @@ mptsas_get_rphy(struct mptsas_phyinfo *p + return NULL; + } + ++/** ++ * mptsas_set_rphy - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @phy_info: ++ * @rphy: ++ * ++ **/ + static inline void + mptsas_set_rphy(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, struct sas_rphy *rphy) + { +@@ -491,6 +567,11 @@ mptsas_set_rphy(MPT_ADAPTER *ioc, struct + } + } + ++/** ++ * mptsas_get_port - ++ * @phy_info: ++ * ++ **/ + static inline struct sas_port * + mptsas_get_port(struct mptsas_phyinfo *phy_info) + { +@@ -500,6 +581,13 @@ mptsas_get_port(struct mptsas_phyinfo *p + return NULL; + } + ++/** ++ * mptsas_set_port - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @phy_info: ++ * @port: ++ * ++ **/ + static inline void + mptsas_set_port(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, struct sas_port *port) + { +@@ -514,6 +602,11 @@ mptsas_set_port(MPT_ADAPTER *ioc, struct + } + } + ++/** ++ * mptsas_get_starget - ++ * @phy_info: ++ * ++ **/ + static inline struct scsi_target * + mptsas_get_starget(struct mptsas_phyinfo *phy_info) + { +@@ -523,6 +616,12 @@ mptsas_get_starget(struct mptsas_phyinfo + return NULL; + } + ++/** ++ * mptsas_set_starget - ++ * @phy_info: ++ * @starget: ++ * ++ **/ + static inline void + mptsas_set_starget(struct mptsas_phyinfo *phy_info, struct scsi_target * + starget) +@@ -544,15 +643,15 @@ static void + mptsas_add_device_component(MPT_ADAPTER *ioc, u8 channel, u8 id, + u64 sas_address, u32 device_info, u16 slot, u64 enclosure_logical_id) + { +- struct mptsas_device_info *sas_info, *next; ++ struct sas_device_info *sas_info, *next; + struct scsi_device *sdev; + struct scsi_target *starget; +- struct sas_rphy *rphy; ++ struct sas_rphy *rphy; + + /* + * Delete all matching devices out of the list + */ +- mutex_lock(&ioc->sas_device_info_mutex); ++ down(&ioc->sas_device_info_mutex); + list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list, + list) { + if (!sas_info->is_logical_volume && +@@ -564,7 +663,7 @@ mptsas_add_device_component(MPT_ADAPTER + } + } + +- sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL); ++ sas_info = kzalloc(sizeof(struct sas_device_info), GFP_KERNEL); + if (!sas_info) + goto out; + +@@ -594,7 +693,7 @@ mptsas_add_device_component(MPT_ADAPTER + } + + out: +- mutex_unlock(&ioc->sas_device_info_mutex); ++ up(&ioc->sas_device_info_mutex); + return; + } + +@@ -631,23 +730,23 @@ mptsas_add_device_component_by_fw(MPT_AD + } + + /** +- * mptsas_add_device_component_starget_ir - Handle Integrated RAID, adding each individual device to list ++ * mptsas_add_device_component_starget_ir - Handle Integrated RAID, adding ++ * each individual device to list + * @ioc: Pointer to MPT_ADAPTER structure + * @channel: fw mapped id's + * @id: + * + **/ + static void +-mptsas_add_device_component_starget_ir(MPT_ADAPTER *ioc, +- struct scsi_target *starget) ++mptsas_add_device_component_starget_ir(MPT_ADAPTER *ioc, struct scsi_target *starget) + { + CONFIGPARMS cfg; + ConfigPageHeader_t hdr; + dma_addr_t dma_handle; + pRaidVolumePage0_t buffer = NULL; + int i; +- RaidPhysDiskPage0_t phys_disk; +- struct mptsas_device_info *sas_info, *next; ++ RaidPhysDiskPage0_t phys_disk; ++ struct sas_device_info *sas_info, *next; + + memset(&cfg, 0 , sizeof(CONFIGPARMS)); + memset(&hdr, 0 , sizeof(ConfigPageHeader_t)); +@@ -684,14 +783,14 @@ mptsas_add_device_component_starget_ir(M + */ + for (i = 0; i < buffer->NumPhysDisks; i++) { + +- if (mpt_raid_phys_disk_pg0(ioc, ++ if(mpt_raid_phys_disk_pg0(ioc, + buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0) + continue; + + mptsas_add_device_component_by_fw(ioc, phys_disk.PhysDiskBus, + phys_disk.PhysDiskID); + +- mutex_lock(&ioc->sas_device_info_mutex); ++ down(&ioc->sas_device_info_mutex); + list_for_each_entry(sas_info, &ioc->sas_device_info_list, + list) { + if (!sas_info->is_logical_volume && +@@ -701,14 +800,13 @@ mptsas_add_device_component_starget_ir(M + sas_info->volume_id = starget->id; + } + } +- mutex_unlock(&ioc->sas_device_info_mutex); +- ++ up(&ioc->sas_device_info_mutex); + } + + /* + * Delete all matching devices out of the list + */ +- mutex_lock(&ioc->sas_device_info_mutex); ++ down(&ioc->sas_device_info_mutex); + list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list, + list) { + if (sas_info->is_logical_volume && sas_info->fw.id == +@@ -718,7 +816,7 @@ mptsas_add_device_component_starget_ir(M + } + } + +- sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL); ++ sas_info = kzalloc(sizeof(struct sas_device_info), GFP_KERNEL); + if (sas_info) { + sas_info->fw.id = starget->id; + sas_info->os.id = starget->id; +@@ -727,7 +825,7 @@ mptsas_add_device_component_starget_ir(M + INIT_LIST_HEAD(&sas_info->list); + list_add_tail(&sas_info->list, &ioc->sas_device_info_list); + } +- mutex_unlock(&ioc->sas_device_info_mutex); ++ up(&ioc->sas_device_info_mutex); + + out: + if (buffer) +@@ -770,7 +868,8 @@ mptsas_add_device_component_starget(MPT_ + } + + /** +- * mptsas_del_device_component_by_os - Once a device has been removed, we mark the entry in the list as being cached ++ * mptsas_del_device_component_by_os - Once a device has been removed, we ++ * mark the entry in the list as being cached + * @ioc: Pointer to MPT_ADAPTER structure + * @channel: os mapped id's + * @id: +@@ -779,7 +878,7 @@ mptsas_add_device_component_starget(MPT_ + static void + mptsas_del_device_component_by_os(MPT_ADAPTER *ioc, u8 channel, u8 id) + { +- struct mptsas_device_info *sas_info, *next; ++ struct sas_device_info *sas_info, *next; + + /* + * Set is_cached flag +@@ -799,20 +898,24 @@ mptsas_del_device_component_by_os(MPT_AD + static void + mptsas_del_device_components(MPT_ADAPTER *ioc) + { +- struct mptsas_device_info *sas_info, *next; ++ struct sas_device_info *sas_info, *next; + +- mutex_lock(&ioc->sas_device_info_mutex); ++ down(&ioc->sas_device_info_mutex); + list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list, + list) { + list_del(&sas_info->list); + kfree(sas_info); + } +- mutex_unlock(&ioc->sas_device_info_mutex); ++ up(&ioc->sas_device_info_mutex); + } + + + /* + * mptsas_setup_wide_ports ++ * configuration ++ * in the sas_topology ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @port_info: + * + * Updates for new and existing narrow/wide port configuration + * in the sas_topology +@@ -836,13 +939,14 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc + continue; + if (port_details->num_phys < 2) + continue; ++ + /* + * Removing a phy from a port, letting the last + * phy be removed by firmware events. + */ + dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "%s: [%p]: deleting phy = %d\n", +- ioc->name, __func__, port_details, i)); ++ "%s: [%p]: deleting phy = %d\n", ++ ioc->name, __FUNCTION__, port_details, i)); + port_details->num_phys--; + port_details->phy_bitmask &= ~ (1 << phy_info->phy_id); + memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo)); +@@ -945,19 +1049,18 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc + } + + /** +- * csmisas_find_vtarget +- * +- * @ioc +- * @volume_id +- * @volume_bus ++ * mptsas_find_vtarget - obtain vtarget object for non-raid devices ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @channel: ++ * @id: + * + **/ + static VirtTarget * + mptsas_find_vtarget(MPT_ADAPTER *ioc, u8 channel, u8 id) + { +- struct scsi_device *sdev; ++ struct scsi_device *sdev; + VirtDevice *vdevice; +- VirtTarget *vtarget = NULL; ++ VirtTarget *vtarget = NULL; + + shost_for_each_device(sdev, ioc->sh) { + vdevice = sdev->hostdata; +@@ -1017,16 +1120,14 @@ mptsas_queue_rescan(MPT_ADAPTER *ioc) + + + /** +- * mptsas_target_reset +- * +- * Issues TARGET_RESET to end device using handshaking method +- * +- * @ioc +- * @channel +- * @id ++ * mptsas_target_reset - Issues TARGET_RESET to end device using ++ * handshaking method ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @channel: ++ * @id: + * +- * Returns (1) success +- * (0) failure ++ * Returns (1) success ++ * (0) failure + * + **/ + static int +@@ -1034,6 +1135,7 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 + { + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; ++ + if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) + return 0; + +@@ -1075,16 +1177,27 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 + return 0; + } + ++static void ++mptsas_block_io_sdev(struct scsi_device *sdev, void *data) ++{ ++ scsi_device_set_state(sdev, SDEV_BLOCK); ++} ++ ++static void ++mptsas_block_io_starget(struct scsi_target *starget) ++{ ++ if (starget) ++ starget_for_each_device(starget, NULL, mptsas_block_io_sdev); ++} ++ + /** +- * mptsas_target_reset_queue +- * +- * Receive request for TARGET_RESET after recieving an firmware +- * event NOT_RESPONDING_EVENT, then put command in link list +- * and queue if task_queue already in use. +- * +- * @ioc +- * @sas_event_data ++ * mptsas_target_reset_queue - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @sas_event_data: + * ++ * Receive request for TARGET_RESET after ++ * recieving an firmware event NOT_RESPONDING_EVENT, then put command in ++ * link list and queue if task_queue already in use. + **/ + static void + mptsas_target_reset_queue(MPT_ADAPTER *ioc, +@@ -1098,10 +1211,12 @@ mptsas_target_reset_queue(MPT_ADAPTER *i + id = sas_event_data->TargetID; + channel = sas_event_data->Bus; + +- if (!(vtarget = mptsas_find_vtarget(ioc, channel, id))) +- return; +- +- vtarget->deleted = 1; /* block IO */ ++ if ((vtarget = mptsas_find_vtarget(ioc, channel, id))) { ++ if (!ioc->disable_hotplug_remove) { ++ mptsas_block_io_starget(vtarget->starget); ++ vtarget->deleted = 1; /* block IO */ ++ } ++ } + + target_reset_list = kzalloc(sizeof(struct mptsas_target_reset_event), + GFP_ATOMIC); +@@ -1124,20 +1239,57 @@ mptsas_target_reset_queue(MPT_ADAPTER *i + } + + /** +- * mptsas_taskmgmt_complete - complete SAS task management function ++ * mptsas_schedule_target_reset- send pending target reset ++ * @iocp: per adapter object ++ * ++ * This function will delete scheduled target reset from the list and ++ * try to send next target reset. This will be called from completion ++ * context of any Task managment command. ++ */ ++ ++void ++mptsas_schedule_target_reset(void *iocp) ++{ ++ MPT_ADAPTER *ioc = (MPT_ADAPTER*)(iocp); ++ MPT_SCSI_HOST *hd = shost_priv(ioc->sh); ++ struct list_head *head = &hd->target_reset_list; ++ struct mptsas_target_reset_event *target_reset_list; ++ u8 id, channel; ++ /* ++ * issue target reset to next device in the queue ++ */ ++ ++ head = &hd->target_reset_list; ++ if (list_empty(head)) ++ return; ++ ++ target_reset_list = list_entry(head->next, ++ struct mptsas_target_reset_event, list); ++ ++ id = target_reset_list->sas_event_data.TargetID; ++ channel = target_reset_list->sas_event_data.Bus; ++ target_reset_list->time_count = jiffies; ++ ++ if (mptsas_target_reset(ioc, channel, id)) ++ target_reset_list->target_reset_issued = 1; ++ return; ++} ++ ++ ++/** ++ * mptsas_taskmgmt_complete - Completion for TARGET_RESET after ++ * NOT_RESPONDING_EVENT, enable work queue to finish off removing device ++ * from upper layers. then send next TARGET_RESET in the queue. + * @ioc: Pointer to MPT_ADAPTER structure + * +- * Completion for TARGET_RESET after NOT_RESPONDING_EVENT, enable work +- * queue to finish off removing device from upper layers. then send next +- * TARGET_RESET in the queue. + **/ + static int + mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) + { + MPT_SCSI_HOST *hd = shost_priv(ioc->sh); +- struct list_head *head = &hd->target_reset_list; +- u8 id, channel; ++ struct list_head *head = &hd->target_reset_list; + struct mptsas_target_reset_event *target_reset_list; ++ u8 id, channel; + SCSITaskMgmtReply_t *pScsiTmReply; + + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed: " +@@ -1212,32 +1364,15 @@ mptsas_taskmgmt_complete(MPT_ADAPTER *io + &target_reset_list->sas_event_data); + + +- /* +- * issue target reset to next device in the queue +- */ +- +- head = &hd->target_reset_list; +- if (list_empty(head)) +- return 1; +- +- target_reset_list = list_entry(head->next, struct mptsas_target_reset_event, +- list); +- +- id = target_reset_list->sas_event_data.TargetID; +- channel = target_reset_list->sas_event_data.Bus; +- target_reset_list->time_count = jiffies; +- +- if (mptsas_target_reset(ioc, channel, id)) +- target_reset_list->target_reset_issued = 1; ++ ioc->schedule_target_reset(ioc); + + return 1; + } + + /** +- * mptscsih_ioc_reset +- * +- * @ioc +- * @reset_phase ++ * mptsas_ioc_reset - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @reset_phase: + * + **/ + static int +@@ -1282,7 +1417,6 @@ mptsas_ioc_reset(MPT_ADAPTER *ioc, int r + return rc; + } + +- + /** + * enum device_state - + * @DEVICE_RETRY: need to retry the TUR +@@ -1296,6 +1430,15 @@ enum device_state{ + DEVICE_READY, + }; + ++ ++/** ++ * mptsas_sas_enclosure_pg0 - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @enclosure: ++ * @form: ++ * @form_specific: ++ * ++ **/ + static int + mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure, + u32 form, u32 form_specific) +@@ -1440,7 +1583,8 @@ mptsas_add_end_device(MPT_ADAPTER *ioc, + } + + /** +- * mptsas_del_end_device - report a deleted end device to sas transport layer ++ * mptsas_del_end_device - report a deleted end device to sas transport ++ * layer + * @ioc: Pointer to MPT_ADAPTER structure + * @phy_info: decribes attached device + * +@@ -1638,13 +1782,297 @@ mptsas_firmware_event_work(struct work_s + + + ++/** ++ * mptsas_get_lun_number - returns the first entry in report_luns table ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @channel: ++ * @id: ++ * @lun: ++ * ++ */ ++static int ++mptsas_get_lun_number(MPT_ADAPTER *ioc, u8 channel, u8 id, int *lun) ++{ ++ INTERNAL_CMD *iocmd; ++ struct scsi_lun *lun_data; ++ dma_addr_t lun_data_dma; ++ u32 lun_data_len; ++ u8 *data; ++ MPT_SCSI_HOST *hd; ++ int rc; ++ u32 length, num_luns; ++ ++ iocmd = NULL; ++ hd = shost_priv(ioc->sh); ++ lun_data_len = (255 * sizeof(struct scsi_lun)); ++ lun_data = pci_alloc_consistent(ioc->pcidev, lun_data_len, ++ &lun_data_dma); ++ if (!lun_data) { ++ printk(MYIOC_s_ERR_FMT "%s: pci_alloc_consistent(%d) FAILED!\n", ++ ioc->name, __FUNCTION__, lun_data_len); ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ iocmd = kzalloc(sizeof(INTERNAL_CMD), GFP_KERNEL); ++ if (!iocmd) { ++ printk(MYIOC_s_ERR_FMT "%s: kzalloc(%zd) FAILED!\n", ++ ioc->name, __FUNCTION__, sizeof(INTERNAL_CMD)); ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ /* ++ * Report Luns ++ */ ++ iocmd->cmd = REPORT_LUNS; ++ iocmd->data_dma = lun_data_dma; ++ iocmd->data = (u8 *)lun_data; ++ iocmd->size = lun_data_len; ++ iocmd->channel = channel; ++ iocmd->id = id; ++ ++ if ((rc = mptscsih_do_cmd(hd, iocmd)) < 0) { ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "report_luns failed due to rc=0x%x\n", ioc->name, ++ __FUNCTION__, channel, id, rc); ++ goto out; ++ } ++ ++ if (rc != MPT_SCANDV_GOOD) { ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "report_luns failed due to rc=0x%x\n", ioc->name, ++ __FUNCTION__, channel, id, rc); ++ rc = -rc; ++ goto out; ++ } ++ ++ data = (u8 *)lun_data; ++ length = ((data[0] << 24) | (data[1] << 16) | ++ (data[2] << 8) | (data[3] << 0)); ++ ++ num_luns = (length / sizeof(struct scsi_lun)); ++ if (!num_luns) ++ goto out; ++ /* return 1st lun in the list */ ++ *lun = scsilun_to_int(&lun_data[1]); ++ ++#if 0 ++ /* some debugging, left commented out */ ++ { ++ struct scsi_lun *lunp; ++ for (lunp = &lun_data[1]; lunp <= &lun_data[num_luns]; lunp++) ++ printk("%x\n", scsilun_to_int(lunp)); ++ } ++#endif ++ ++ out: ++ if (lun_data) ++ pci_free_consistent(ioc->pcidev, lun_data_len, lun_data, ++ lun_data_dma); ++ kfree(iocmd); ++ return rc; ++} ++ ++/** ++ * mptsas_test_unit_ready - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @channel: ++ * @id: ++ * @count: retry count ++ * ++ */ ++enum device_state ++mptsas_test_unit_ready(MPT_ADAPTER *ioc, u8 channel, u8 id, u16 count) ++{ ++ INTERNAL_CMD *iocmd; ++ MPT_SCSI_HOST *hd = shost_priv(ioc->sh); ++ enum device_state state; ++ int rc; ++ u8 skey, asc, ascq; ++ u8 retry_ua; ++ ++ if (count >= mpt_cmd_retry_count) ++ return DEVICE_ERROR; ++ ++ retry_ua = 0; ++ iocmd = kzalloc(sizeof(INTERNAL_CMD), GFP_KERNEL); ++ if (!iocmd) { ++ printk(MYIOC_s_ERR_FMT "%s: kzalloc(%zd) FAILED!\n", ++ __FUNCTION__, ioc->name, sizeof(INTERNAL_CMD)); ++ return DEVICE_ERROR; ++ } ++ ++ state = DEVICE_ERROR; ++ iocmd->cmd = TEST_UNIT_READY; ++ iocmd->data_dma = -1; ++ iocmd->data = NULL; ++ ++ if (mptscsih_is_phys_disk(ioc, channel, id)) { ++ iocmd->flags |= MPT_ICFLAG_PHYS_DISK; ++ iocmd->physDiskNum = mptscsih_raid_id_to_num(ioc, channel, id); ++ iocmd->id = id; ++ } ++ iocmd->channel = channel; ++ iocmd->id = id; ++ ++ retry: ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: fw_channel=%d " ++ "fw_id=%d retry=%d\n", ioc->name, __FUNCTION__, channel, id, count)); ++ rc = mptscsih_do_cmd(hd, iocmd); ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: rc=0x%02x\n", ++ ioc->name, __FUNCTION__, rc)); ++ if (rc < 0) { ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "tur failed due to timeout\n", ioc->name, ++ __FUNCTION__, channel, id); ++ goto tur_done; ++ } ++ ++ switch(rc) { ++ case MPT_SCANDV_GOOD: ++ state = DEVICE_READY; ++ goto tur_done; ++ case MPT_SCANDV_BUSY: ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: " ++ "fw_channel=%d fw_id=%d : device busy\n", ++ ioc->name, __FUNCTION__, channel, id)); ++ state = DEVICE_RETRY; ++ break; ++ case MPT_SCANDV_DID_RESET: ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: " ++ "fw_channel=%d fw_id=%d : did reset\n", ++ ioc->name, __FUNCTION__, channel, id)); ++ state = DEVICE_RETRY; ++ break; ++ case MPT_SCANDV_SENSE: ++ skey = ioc->internal_cmds.sense[2] & 0x0F; ++ asc = ioc->internal_cmds.sense[12]; ++ ascq = ioc->internal_cmds.sense[13]; ++ ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: " ++ "fw_channel=%d fw_id=%d : [sense_key,asc," ++ "ascq]: [0x%02x,0x%02x,0x%02x]\n", ioc->name, ++ __FUNCTION__, channel, id, skey, asc, ascq)); ++ ++ if (skey == UNIT_ATTENTION) { ++ state = DEVICE_RETRY; ++ break; ++ } else if (skey == NOT_READY) { ++ /* ++ * medium isn't present ++ */ ++ if (asc == 0x3a) { ++ state = DEVICE_READY; ++ goto tur_done; ++ } ++ /* ++ * LU becoming ready, or ++ * LU hasn't self-configured yet ++ */ ++ if ((asc == 0x04 && ascq == 0x01) || ++ (asc == 0x04 && ascq == 0x11) || ++ asc == 0x3e) { ++ state = DEVICE_RETRY; ++ break; ++ } ++ } else if (skey == ILLEGAL_REQUEST) { ++ /* try sending a tur to a non-zero lun number */ ++ if (!iocmd->lun && !mptsas_get_lun_number(ioc, ++ channel, id, &iocmd->lun) && iocmd->lun) ++ goto retry; ++ } ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d : " ++ "tur failed due to [sense_key,asc,ascq]: " ++ "[0x%02x,0x%02x,0x%02x]\n", ioc->name, ++ __FUNCTION__, channel, id, skey, asc, ascq); ++ goto tur_done; ++ case MPT_SCANDV_SELECTION_TIMEOUT: ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "tur failed due to no device\n", ioc->name, ++ __FUNCTION__, channel, ++ id); ++ goto tur_done; ++ case MPT_SCANDV_SOME_ERROR: ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "tur failed due to some error\n", ioc->name, ++ __FUNCTION__, ++ channel, id); ++ goto tur_done; ++ default: ++ printk(MYIOC_s_ERR_FMT ++ "%s: fw_channel=%d fw_id=%d: tur failed due to " ++ "unknown rc=0x%02x\n", ioc->name, __FUNCTION__, ++ channel, id, rc ); ++ goto tur_done; ++ } ++ tur_done: ++ kfree(iocmd); ++ return state; ++} ++ ++/** ++ * mptsas_issue_tlr - Enabling Transport Layer Retries ++ * @hd: ++ * @sdev: ++ * ++ **/ ++static void ++mptsas_issue_tlr(MPT_SCSI_HOST *hd, struct scsi_device *sdev) ++{ ++ INTERNAL_CMD *iocmd; ++ VirtDevice *vdevice = sdev->hostdata; ++ u8 retries; ++ u8 rc; ++ MPT_ADAPTER *ioc = hd->ioc; ++ ++ if ( sdev->inquiry[8] == 'H' && ++ sdev->inquiry[9] == 'P' && ++ sdev->inquiry[10] == ' ' && ++ sdev->inquiry[11] == ' ' && ++ sdev->inquiry[12] == ' ' && ++ sdev->inquiry[13] == ' ' && ++ sdev->inquiry[14] == ' ' && ++ sdev->inquiry[15] == ' ' ) { ++ ++ iocmd = kzalloc(sizeof(INTERNAL_CMD), GFP_KERNEL); ++ if (!iocmd) { ++ printk(MYIOC_s_ERR_FMT "%s: kzalloc(%zd) FAILED!\n", ++ __FUNCTION__, ioc->name, sizeof(INTERNAL_CMD)); ++ return; ++ } ++ iocmd->id = vdevice->vtarget->id; ++ iocmd->channel = vdevice->vtarget->channel; ++ iocmd->lun = vdevice->lun; ++ iocmd->physDiskNum = -1; ++ iocmd->cmd = TRANSPORT_LAYER_RETRIES; ++ iocmd->data_dma = -1; ++ for (retries = 0, rc = -1; retries < 3; retries++) { ++ rc = mptscsih_do_cmd(hd, iocmd); ++ if (!rc) ++ break; ++ } ++ if (rc != 0) ++ printk(MYIOC_s_DEBUG_FMT "unable to enable TLR on" ++ " fw_channel %d, fw_id %d, lun=%d\n", ++ ioc->name, vdevice->vtarget->channel, ++ vdevice->vtarget->id, sdev->lun); ++ kfree(iocmd); ++ } ++} ++ ++/** ++ * mptsas_slave_configure - ++ * @sdev: ++ * ++ **/ + static int + mptsas_slave_configure(struct scsi_device *sdev) + { + struct Scsi_Host *host = sdev->host; +- MPT_SCSI_HOST *hd = shost_priv(host); +- MPT_ADAPTER *ioc = hd->ioc; +- VirtDevice *vdevice = sdev->hostdata; ++ MPT_SCSI_HOST *hd = shost_priv(host); ++ MPT_ADAPTER *ioc = hd->ioc; ++ VirtDevice *vdevice = sdev->hostdata; + + if (vdevice->vtarget->deleted) { + sdev_printk(KERN_INFO, sdev, "clearing deleted flag\n"); +@@ -1664,10 +2092,19 @@ mptsas_slave_configure(struct scsi_devic + + mptsas_add_device_component_starget(ioc, scsi_target(sdev)); + ++ if (sdev->type == TYPE_TAPE && ++ (ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_TLR )) ++ mptsas_issue_tlr(hd, sdev); + out: ++ + return mptscsih_slave_configure(sdev); + } + ++/** ++ * mptsas_target_alloc - ++ * @starget: ++ * ++ **/ + static int + mptsas_target_alloc(struct scsi_target *starget) + { +@@ -1677,7 +2114,7 @@ mptsas_target_alloc(struct scsi_target * + u8 id, channel; + struct sas_rphy *rphy; + struct mptsas_portinfo *p; +- int i; ++ int i; + MPT_ADAPTER *ioc = hd->ioc; + + vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL); +@@ -1698,13 +2135,9 @@ mptsas_target_alloc(struct scsi_target * + kfree(vtarget); + return -ENXIO; + } +- for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) { +- if (id == ioc->raid_data.pIocPg2-> +- RaidVolume[i].VolumeID) { +- channel = ioc->raid_data.pIocPg2-> +- RaidVolume[i].VolumeBus; +- } +- } ++ for (i=0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) ++ if (id == ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID) ++ channel = ioc->raid_data.pIocPg2->RaidVolume[i].VolumeBus; + vtarget->raidVolume = 1; + goto out; + } +@@ -1720,6 +2153,12 @@ mptsas_target_alloc(struct scsi_target * + channel = p->phy_info[i].attached.channel; + mptsas_set_starget(&p->phy_info[i], starget); + ++ starget_printk(KERN_INFO, starget, MYIOC_s_FMT ++ "add device: fw_channel %d, fw_id %d, phy %d, sas_addr 0x%llx\n", ++ ioc->name, p->phy_info[i].attached.channel, ++ p->phy_info[i].attached.id, p->phy_info[i].attached.phy_id, ++ (unsigned long long)p->phy_info[i].attached.sas_address); ++ + /* + * Exposing hidden raid components + */ +@@ -1746,6 +2185,11 @@ mptsas_target_alloc(struct scsi_target * + return 0; + } + ++/** ++ * mptsas_target_destroy - ++ * @starget: ++ * ++ **/ + static void + mptsas_target_destroy(struct scsi_target *starget) + { +@@ -1753,7 +2197,7 @@ mptsas_target_destroy(struct scsi_target + MPT_SCSI_HOST *hd = shost_priv(host); + struct sas_rphy *rphy; + struct mptsas_portinfo *p; +- int i; ++ int i; + MPT_ADAPTER *ioc = hd->ioc; + VirtTarget *vtarget; + +@@ -1765,7 +2209,6 @@ mptsas_target_destroy(struct scsi_target + mptsas_del_device_component_by_os(ioc, starget->channel, + starget->id); + +- + if (starget->channel == MPTSAS_RAID_CHANNEL) + goto out; + +@@ -1794,7 +2237,11 @@ mptsas_target_destroy(struct scsi_target + starget->hostdata = NULL; + } + +- ++/** ++ * mptsas_slave_alloc - ++ * @sdev: ++ * ++ **/ + static int + mptsas_slave_alloc(struct scsi_device *sdev) + { +@@ -1803,8 +2250,8 @@ mptsas_slave_alloc(struct scsi_device *s + struct sas_rphy *rphy; + struct mptsas_portinfo *p; + VirtDevice *vdevice; +- struct scsi_target *starget; +- int i; ++ struct scsi_target *starget; ++ int i; + MPT_ADAPTER *ioc = hd->ioc; + + vdevice = kzalloc(sizeof(VirtDevice), GFP_KERNEL); +@@ -1816,6 +2263,9 @@ mptsas_slave_alloc(struct scsi_device *s + starget = scsi_target(sdev); + vdevice->vtarget = starget->hostdata; + ++ /* ++ * RAID volumes placed beyond the last expected port. ++ */ + if (sdev->channel == MPTSAS_RAID_CHANNEL) + goto out; + +@@ -1849,6 +2299,12 @@ mptsas_slave_alloc(struct scsi_device *s + return 0; + } + ++/** ++ * mptsas_qcmd - ++ * @SCpnt: ++ * @done: ++ * ++ **/ + static int + mptsas_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) + { +@@ -1868,7 +2324,8 @@ mptsas_qcmd(struct scsi_cmnd *SCpnt, voi + if (ioc->sas_discovery_quiesce_io) + return SCSI_MLQUEUE_HOST_BUSY; + +-// scsi_print_command(SCpnt); ++ if (ioc->debug_level & MPT_DEBUG_SCSI) ++ scsi_print_command(SCpnt); + + return mptscsih_qcmd(SCpnt,done); + } +@@ -1878,7 +2335,7 @@ static struct scsi_host_template mptsas_ + .module = THIS_MODULE, + .proc_name = "mptsas", + .proc_info = mptscsih_proc_info, +- .name = "MPT SPI Host", ++ .name = "MPT SAS Host", + .info = mptscsih_info, + .queuecommand = mptsas_qcmd, + .target_alloc = mptsas_target_alloc, +@@ -1886,7 +2343,7 @@ static struct scsi_host_template mptsas_ + .slave_configure = mptsas_slave_configure, + .target_destroy = mptsas_target_destroy, + .slave_destroy = mptscsih_slave_destroy, +- .change_queue_depth = mptscsih_change_queue_depth, ++ .change_queue_depth = mptscsih_change_queue_depth, + .eh_abort_handler = mptscsih_abort, + .eh_device_reset_handler = mptscsih_dev_reset, + .eh_bus_reset_handler = mptscsih_bus_reset, +@@ -1901,6 +2358,11 @@ static struct scsi_host_template mptsas_ + .shost_attrs = mptscsih_host_attrs, + }; + ++/** ++ * mptsas_get_linkerrors - ++ * @phy: ++ * ++ **/ + static int mptsas_get_linkerrors(struct sas_phy *phy) + { + MPT_ADAPTER *ioc = phy_to_ioc(phy); +@@ -1963,6 +2425,13 @@ static int mptsas_get_linkerrors(struct + return error; + } + ++/** ++ * mptsas_mgmt_done - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @req: ++ * @reply: ++ * ++ **/ + static int mptsas_mgmt_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, + MPT_FRAME_HDR *reply) + { +@@ -1981,6 +2450,12 @@ static int mptsas_mgmt_done(MPT_ADAPTER + return 0; + } + ++/** ++ * mptsas_phy_reset - ++ * @phy: ++ * @hard_reset: ++ * ++ **/ + static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset) + { + MPT_ADAPTER *ioc = phy_to_ioc(phy); +@@ -2019,14 +2494,16 @@ static int mptsas_phy_reset(struct sas_p + + INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status) + mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf); +- +- timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, +- 10 * HZ); +- if (!timeleft) { +- /* On timeout reset the board */ ++ timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10*HZ); ++ if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ error = -ETIME; + mpt_free_msg_frame(ioc, mf); +- mpt_HardResetHandler(ioc, CAN_SLEEP); +- error = -ETIMEDOUT; ++ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET) ++ goto out_unlock; ++ if (!timeleft) { ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP) != 0) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); ++ } + goto out_unlock; + } + +@@ -2055,6 +2532,12 @@ static int mptsas_phy_reset(struct sas_p + return error; + } + ++/** ++ * mptsas_get_enclosure_identifier - ++ * @rphy: ++ * @identifier: ++ * ++ **/ + static int + mptsas_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) + { +@@ -2089,6 +2572,11 @@ mptsas_get_enclosure_identifier(struct s + return error; + } + ++/** ++ * mptsas_get_bay_identifier - ++ * @rphy: ++ * ++ **/ + static int + mptsas_get_bay_identifier(struct sas_rphy *rphy) + { +@@ -2176,10 +2664,14 @@ static int mptsas_smp_handler(struct Scs + (((int *) mf) + (offsetof(SmpPassthroughRequest_t, SGL) / 4)); + + /* request */ +- flagsLength = (MPI_SGE_FLAGS_SIMPLE_ELEMENT | +- MPI_SGE_FLAGS_END_OF_BUFFER | +- MPI_SGE_FLAGS_DIRECTION) +- << MPI_SGE_FLAGS_SHIFT; ++ ++ flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI_SGE_FLAGS_SYSTEM_ADDRESS | ++ MPI_SGE_FLAGS_HOST_TO_IOC | ++ MPI_SGE_FLAGS_END_OF_BUFFER; ++ ++ flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT; ++ + flagsLength |= (blk_rq_bytes(req) - 4); + + dma_addr_out = pci_map_single(ioc->pcidev, bio_data(req->bio), +@@ -2200,20 +2692,27 @@ static int mptsas_smp_handler(struct Scs + dma_addr_in = pci_map_single(ioc->pcidev, bio_data(rsp->bio), + blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); + if (!dma_addr_in) +- goto unmap; ++ goto out_unmap; ++ + ioc->add_sge(psge, flagsLength, dma_addr_in); + + INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status) + mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf); + + timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ); +- if (!timeleft) { +- printk(MYIOC_s_ERR_FMT "%s: smp timeout!\n", ioc->name, __func__); +- /* On timeout reset the board */ +- mpt_HardResetHandler(ioc, CAN_SLEEP); +- ret = -ETIMEDOUT; +- goto unmap; ++ if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ ret = -ETIME; ++ mpt_free_msg_frame(ioc, mf); ++ mf = NULL; ++ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET) ++ goto out_unmap; ++ if (!timeleft) { ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP) != 0) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); ++ } ++ goto out_unmap; + } ++ + mf = NULL; + + if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) { +@@ -2230,7 +2729,7 @@ static int mptsas_smp_handler(struct Scs + ioc->name, __func__); + ret = -ENXIO; + } +-unmap: ++out_unmap: + if (dma_addr_out) + pci_unmap_single(ioc->pcidev, dma_addr_out, blk_rq_bytes(req), + PCI_DMA_BIDIRECTIONAL); +@@ -2247,6 +2746,7 @@ out: + return ret; + } + ++ + static struct sas_function_template mptsas_transport_functions = { + .get_linkerrors = mptsas_get_linkerrors, + .get_enclosure_identifier = mptsas_get_enclosure_identifier, +@@ -2257,6 +2757,12 @@ static struct sas_function_template mpts + + static struct scsi_transport_template *mptsas_transport_template; + ++/** ++ * mptsas_sas_io_unit_pg0 - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @port_info: ++ * ++ **/ + static int + mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) + { +@@ -2305,7 +2811,7 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, + + port_info->num_phys = buffer->NumPhys; + port_info->phy_info = kcalloc(port_info->num_phys, +- sizeof(struct mptsas_phyinfo), GFP_KERNEL); ++ sizeof(struct mptsas_phyinfo),GFP_KERNEL); + if (!port_info->phy_info) { + error = -ENOMEM; + goto out_free_consistent; +@@ -2335,6 +2841,11 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, + return error; + } + ++/** ++ * mptsas_sas_io_unit_pg1 - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * ++ **/ + static int + mptsas_sas_io_unit_pg1(MPT_ADAPTER *ioc) + { +@@ -2350,11 +2861,11 @@ mptsas_sas_io_unit_pg1(MPT_ADAPTER *ioc) + + cfg.cfghdr.ehdr = &hdr; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; +- cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT; + cfg.cfghdr.ehdr->PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + cfg.cfghdr.ehdr->ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; + cfg.cfghdr.ehdr->PageVersion = MPI_SASIOUNITPAGE1_PAGEVERSION; + cfg.cfghdr.ehdr->PageNumber = 1; ++ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT; + + error = mpt_config(ioc, &cfg); + if (error) +@@ -2392,6 +2903,14 @@ mptsas_sas_io_unit_pg1(MPT_ADAPTER *ioc) + return error; + } + ++/** ++ * mptsas_sas_phy_pg0 - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @phy_info: ++ * @form: ++ * @form_specific: ++ * ++ **/ + static int + mptsas_sas_phy_pg0(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, + u32 form, u32 form_specific) +@@ -2412,12 +2931,12 @@ mptsas_sas_phy_pg0(MPT_ADAPTER *ioc, str + + cfg.cfghdr.ehdr = &hdr; + cfg.dir = 0; /* read */ +- cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT; + + /* Get Phy Pg 0 for each Phy. */ + cfg.physAddr = -1; + cfg.pageAddr = form + form_specific; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT; + + error = mpt_config(ioc, &cfg); + if (error) +@@ -2456,6 +2975,14 @@ mptsas_sas_phy_pg0(MPT_ADAPTER *ioc, str + return error; + } + ++/** ++ * mptsas_sas_device_pg0 - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @device_info: ++ * @form: ++ * @form_specific: ++ * ++ **/ + static int + mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, + u32 form, u32 form_specific) +@@ -2482,7 +3009,6 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, + cfg.dir = 0; /* read */ + cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT; + +- memset(device_info, 0, sizeof(struct mptsas_devinfo)); + error = mpt_config(ioc, &cfg); + if (error) + goto out; +@@ -2502,6 +3028,12 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); ++ ++ if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) { ++ error = -ENODEV; ++ goto out_free_consistent; ++ } ++ + if (error) + goto out_free_consistent; + +@@ -2530,6 +3062,14 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, + return error; + } + ++/** ++ * mptsas_sas_expander_pg0 - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @port_info: ++ * @form: ++ * @form_specific: ++ * ++ **/ + static int + mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info, + u32 form, u32 form_specific) +@@ -2557,7 +3097,6 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc + cfg.dir = 0; /* read */ + cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT; + +- memset(port_info, 0, sizeof(struct mptsas_portinfo)); + error = mpt_config(ioc, &cfg); + if (error) + goto out; +@@ -2578,18 +3117,18 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); +- if (error) +- goto out_free_consistent; +- +- if (!buffer->NumPhys) { ++ if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) { + error = -ENODEV; + goto out_free_consistent; + } + ++ if (error) ++ goto out_free_consistent; ++ + /* save config data */ + port_info->num_phys = (buffer->NumPhys) ? buffer->NumPhys : 1; + port_info->phy_info = kcalloc(port_info->num_phys, +- sizeof(struct mptsas_phyinfo), GFP_KERNEL); ++ sizeof(struct mptsas_phyinfo),GFP_KERNEL); + if (!port_info->phy_info) { + error = -ENOMEM; + goto out_free_consistent; +@@ -2613,6 +3152,14 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc + return error; + } + ++/** ++ * mptsas_sas_expander_pg1 - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @phy_info: ++ * @form: ++ * @form_specific: ++ * ++ **/ + static int + mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, + u32 form, u32 form_specific) +@@ -2658,7 +3205,6 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); +- + if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) { + error = -ENODEV; + goto out; +@@ -2686,6 +3232,199 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc + return error; + } + ++struct rep_manu_request{ ++ u8 smp_frame_type; ++ u8 function; ++ u8 reserved; ++ u8 request_length; ++}; ++ ++struct rep_manu_reply{ ++ u8 smp_frame_type; /* 0x41 */ ++ u8 function; /* 0x01 */ ++ u8 function_result; ++ u8 response_length; ++ u16 expander_change_count; ++ u8 reserved0[2]; ++ u8 sas_format:1; ++ u8 reserved1:7; ++ u8 reserved2[3]; ++ u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN]; ++ u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN]; ++ u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN]; ++ u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN]; ++ u16 component_id; ++ u8 component_revision_id; ++ u8 reserved3; ++ u8 vendor_specific[8]; ++}; ++ ++/** ++ * mptsas_exp_repmanufacture_info - ++ * @ioc: per adapter object ++ * @sas_address: expander sas address ++ * @edev: the sas_expander_device object ++ * ++ * Fills in the sas_expander_device object when SMP port is created. ++ * ++ * Returns 0 for success, non-zero for failure. ++ */ ++static int ++mptsas_exp_repmanufacture_info(MPT_ADAPTER *ioc, ++ u64 sas_address, struct sas_expander_device *edev) ++{ ++ MPT_FRAME_HDR *mf; ++ SmpPassthroughRequest_t *smpreq; ++ SmpPassthroughReply_t *smprep; ++ struct rep_manu_reply *manufacture_reply; ++ struct rep_manu_request *manufacture_request; ++ int ret; ++ int flagsLength; ++ unsigned long timeleft; ++ char *psge; ++ unsigned long flags; ++ void *data_out = NULL; ++ dma_addr_t data_out_dma = 0; ++ u32 sz; ++ ++ spin_lock_irqsave(&ioc->taskmgmt_lock, flags); ++ if (ioc->ioc_reset_in_progress) { ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ printk(MYIOC_s_INFO_FMT "%s: host reset in progress!\n", ++ __func__, ioc->name); ++ return -EFAULT; ++ } ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); ++ ++ ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex); ++ if (ret) ++ goto out; ++ ++ mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc); ++ if (!mf) { ++ ret = -ENOMEM; ++ goto out_unlock; ++ } ++ ++ smpreq = (SmpPassthroughRequest_t *)mf; ++ memset(smpreq, 0, sizeof(*smpreq)); ++ ++ sz = sizeof(struct rep_manu_request) + sizeof(struct rep_manu_reply); ++ ++ data_out = pci_alloc_consistent(ioc->pcidev, sz, &data_out_dma); ++ if (!data_out) { ++ printk(KERN_ERR "Memory allocation failure at %s:%d/%s()!\n", ++ __FILE__, __LINE__, __func__); ++ ret = -ENOMEM; ++ goto put_mf; ++ } ++ ++ manufacture_request = data_out; ++ manufacture_request->smp_frame_type = 0x40; ++ manufacture_request->function = 1; ++ manufacture_request->reserved = 0; ++ manufacture_request->request_length = 0; ++ ++ smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH; ++ smpreq->PhysicalPort = 0xFF; ++ *((u64 *)&smpreq->SASAddress) = cpu_to_le64(sas_address); ++ smpreq->RequestDataLength = sizeof(struct rep_manu_request); ++ ++ psge = (char *) ++ (((int *) mf) + (offsetof(SmpPassthroughRequest_t, SGL) / 4)); ++ ++ flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI_SGE_FLAGS_SYSTEM_ADDRESS | ++ MPI_SGE_FLAGS_HOST_TO_IOC | ++ MPI_SGE_FLAGS_END_OF_BUFFER; ++ flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT; ++ flagsLength |= sizeof(struct rep_manu_request); ++ ++ ioc->add_sge(psge, flagsLength, data_out_dma); ++ psge += ioc->SGE_size; ++ ++ flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT | ++ MPI_SGE_FLAGS_SYSTEM_ADDRESS | ++ MPI_SGE_FLAGS_IOC_TO_HOST | ++ MPI_SGE_FLAGS_END_OF_BUFFER; ++ flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT; ++ flagsLength |= sizeof(struct rep_manu_reply); ++ ioc->add_sge(psge, flagsLength, data_out_dma + ++ sizeof(struct rep_manu_request)); ++ ++ INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status) ++ mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf); ++ ++ timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ); ++ if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ ret = -ETIME; ++ mpt_free_msg_frame(ioc, mf); ++ mf = NULL; ++ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET) ++ goto out_free; ++ if (!timeleft) { ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP) != 0) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); ++ } ++ goto out_free; ++ } ++ ++ mf = NULL; ++ ++ if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) { ++ u8 *tmp; ++ ++ smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply; ++ if (le16_to_cpu(smprep->ResponseDataLength) != ++ sizeof(struct rep_manu_reply)) ++ goto out_free; ++ ++ manufacture_reply = data_out + sizeof(struct rep_manu_request); ++ strncpy(edev->vendor_id, manufacture_reply->vendor_id, ++ SAS_EXPANDER_VENDOR_ID_LEN); ++ strncpy(edev->product_id, manufacture_reply->product_id, ++ SAS_EXPANDER_PRODUCT_ID_LEN); ++ strncpy(edev->product_rev, manufacture_reply->product_rev, ++ SAS_EXPANDER_PRODUCT_REV_LEN); ++ edev->level = manufacture_reply->sas_format; ++ if (manufacture_reply->sas_format) { ++ strncpy(edev->component_vendor_id, ++ manufacture_reply->component_vendor_id, ++ SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); ++ tmp = (u8 *)&manufacture_reply->component_id; ++ edev->component_id = tmp[0] << 8 | tmp[1]; ++ edev->component_revision_id = ++ manufacture_reply->component_revision_id; ++ } ++ ++ } else { ++ printk(MYIOC_s_ERR_FMT ++ "%s: smp passthru reply failed to be returned\n", ++ ioc->name, __func__); ++ ret = -ENXIO; ++ } ++ ++out_free: ++ if (data_out_dma) ++ pci_free_consistent(ioc->pcidev, sz, data_out, data_out_dma); ++put_mf: ++ if (mf) ++ mpt_free_msg_frame(ioc, mf); ++out_unlock: ++ CLEAR_MGMT_STATUS(ioc->sas_mgmt.status) ++ mutex_unlock(&ioc->sas_mgmt.mutex); ++out: ++ return ret; ++ ++} ++ ++ ++/** ++ * mptsas_parse_device_info - ++ * @identify: ++ * @device_info: ++ * ++ **/ + static void + mptsas_parse_device_info(struct sas_identify *identify, + struct mptsas_devinfo *device_info) +@@ -2745,6 +3484,13 @@ mptsas_parse_device_info(struct sas_iden + } + } + ++/** ++ * mptsas_probe_one_phy - ++ * @dev: ++ * @phy_info: ++ * @local: ++ * ++ **/ + static int mptsas_probe_one_phy(struct device *dev, + struct mptsas_phyinfo *phy_info, int index, int local) + { +@@ -2868,7 +3614,6 @@ static int mptsas_probe_one_phy(struct d + ioc = phy_to_ioc(phy_info->phy); + + if (phy_info->sas_port_add_phy) { +- + if (!port) { + port = sas_port_alloc_num(dev); + if (!port) { +@@ -2886,20 +3631,18 @@ static int mptsas_probe_one_phy(struct d + devtprintk(ioc, dev_printk(KERN_DEBUG, &port->dev, + MYIOC_s_FMT "add port %d, sas_addr (0x%llx)\n", + ioc->name, port->port_identifier, +- (unsigned long long)phy_info-> +- attached.sas_address)); ++ (unsigned long long)phy_info->attached.sas_address)); + } +- dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "sas_port_add_phy: phy_id=%d\n", +- ioc->name, phy_info->phy_id)); ++ dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "sas_port_add_phy: phy_id=%d\n", ++ ioc->name, phy_info->phy_id)); + sas_port_add_phy(port, phy_info->phy); + phy_info->sas_port_add_phy = 0; + devtprintk(ioc, dev_printk(KERN_DEBUG, &phy_info->phy->dev, + MYIOC_s_FMT "add phy %d, phy-obj (0x%p)\n", ioc->name, + phy_info->phy_id, phy_info->phy)); + } +- if (!mptsas_get_rphy(phy_info) && port && !port->rphy) { + ++ if (!mptsas_get_rphy(phy_info) && port && !port->rphy) { + struct sas_rphy *rphy; + struct device *parent; + struct sas_identify identify; +@@ -2967,12 +3710,23 @@ static int mptsas_probe_one_phy(struct d + goto out; + } + mptsas_set_rphy(ioc, phy_info, rphy); ++ if (identify.device_type == SAS_EDGE_EXPANDER_DEVICE || ++ identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) ++ mptsas_exp_repmanufacture_info(ioc, ++ identify.sas_address, ++ rphy_to_expander_device(rphy)); + } + + out: + return error; + } + ++/** ++ * mptsas_probe_hba_phys - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @handle: ++ * ++ **/ + static int + mptsas_probe_hba_phys(MPT_ADAPTER *ioc) + { +@@ -3403,13 +4157,12 @@ mptsas_send_link_status_event(struct fw_ + if (!port_info) { + if (ioc->old_sas_discovery_protocal) { + port_info = mptsas_expander_add(ioc, +- le16_to_cpu(link_data->DevHandle)); ++ le16_to_cpu(link_data->DevHandle)); + if (port_info) + goto out; + } + goto out; + } +- + if (port_info == ioc->hba_port_info) + mptsas_probe_hba_phys(ioc); + else +@@ -3434,7 +4187,7 @@ static void + mptsas_not_responding_devices(MPT_ADAPTER *ioc) + { + struct mptsas_portinfo buffer, *port_info; +- struct mptsas_device_info *sas_info; ++ struct sas_device_info *sas_info; + struct mptsas_devinfo sas_device; + u32 handle; + VirtTarget *vtarget = NULL; +@@ -3443,6 +4196,9 @@ mptsas_not_responding_devices(MPT_ADAPTE + int retval, retry_count; + unsigned long flags; + ++ if (ioc->disable_hotplug_remove) ++ return; ++ + mpt_findImVolumes(ioc); + + spin_lock_irqsave(&ioc->taskmgmt_lock, flags); +@@ -3456,74 +4212,66 @@ mptsas_not_responding_devices(MPT_ADAPTE + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + + /* devices, logical volumes */ +- mutex_lock(&ioc->sas_device_info_mutex); + redo_device_scan: + list_for_each_entry(sas_info, &ioc->sas_device_info_list, list) { + if (sas_info->is_cached) + continue; + if (!sas_info->is_logical_volume) { +- sas_device.handle = 0; +- retry_count = 0; ++ sas_device.handle = 0; ++ retry_count = 0; + retry_page: +- retval = mptsas_sas_device_pg0(ioc, &sas_device, ++ retval = mptsas_sas_device_pg0(ioc, &sas_device, + (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID + << MPI_SAS_DEVICE_PGAD_FORM_SHIFT), + (sas_info->fw.channel << 8) + + sas_info->fw.id); + +- if (sas_device.handle) +- continue; +- if (retval == -EBUSY) { +- spin_lock_irqsave(&ioc->taskmgmt_lock, flags); +- if (ioc->ioc_reset_in_progress) { +- dfailprintk(ioc, +- printk(MYIOC_s_DEBUG_FMT +- "%s: exiting due to reset\n", +- ioc->name, __func__)); +- spin_unlock_irqrestore +- (&ioc->taskmgmt_lock, flags); +- mutex_unlock(&ioc-> +- sas_device_info_mutex); +- return; +- } +- spin_unlock_irqrestore(&ioc->taskmgmt_lock, +- flags); ++ if (sas_device.handle) ++ continue; ++ if (retval == -EBUSY) { ++ spin_lock_irqsave(&ioc->taskmgmt_lock, flags); ++ if (ioc->ioc_reset_in_progress) { ++ dfailprintk(ioc, ++ printk(MYIOC_s_DEBUG_FMT ++ "%s: exiting due to reset\n", ++ ioc->name, __func__)); ++ spin_unlock_irqrestore ++ (&ioc->taskmgmt_lock, flags); ++ return; + } ++ spin_unlock_irqrestore(&ioc->taskmgmt_lock, ++ flags); ++ } + +- if (retval && (retval != -ENODEV)) { +- if (retry_count < 10) { +- retry_count++; +- goto retry_page; +- } else { +- devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "%s: Config page retry exceeded retry " +- "count deleting device 0x%llx\n", +- ioc->name, __func__, +- sas_info->sas_address)); +- } ++ if (retval && (retval != -ENODEV)) { ++ if (retry_count < 10) { ++ retry_count++; ++ goto retry_page; ++ } else { ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "%s: Config page retry exceeded retry " ++ "count deleting device 0x%llx\n", ++ ioc->name, __func__, ++ sas_info->sas_address)); + } ++ } + +- /* delete device */ +- vtarget = mptsas_find_vtarget(ioc, ++ /* delete device */ ++ vtarget = mptsas_find_vtarget(ioc, + sas_info->fw.channel, sas_info->fw.id); +- +- if (vtarget) +- vtarget->deleted = 1; +- +- phy_info = mptsas_find_phyinfo_by_sas_address(ioc, +- sas_info->sas_address); +- +- if (phy_info) { +- mptsas_del_end_device(ioc, phy_info); +- goto redo_device_scan; +- } ++ if (vtarget) ++ vtarget->deleted = 1; ++ phy_info = mptsas_find_phyinfo_by_sas_address(ioc, ++ sas_info->sas_address); ++ if (phy_info) { ++ mptsas_del_end_device(ioc, phy_info); ++ goto redo_device_scan; ++ } + } else + mptsas_volume_delete(ioc, sas_info->fw.id); + } +- mutex_unlock(&ioc->sas_device_info_mutex); + + /* expanders */ +- mutex_lock(&ioc->sas_topology_mutex); + redo_expander_scan: + list_for_each_entry(port_info, &ioc->sas_topology, list) { + +@@ -3551,7 +4299,6 @@ retry_page: + goto redo_expander_scan; + } + } +- mutex_unlock(&ioc->sas_topology_mutex); + } + + /** +@@ -3563,7 +4310,7 @@ static void + mptsas_probe_expanders(MPT_ADAPTER *ioc) + { + struct mptsas_portinfo buffer, *port_info; +- u32 handle; ++ u32 handle; + int i; + + handle = 0xFFFF; +@@ -3611,9 +4358,11 @@ mptsas_probe_expanders(MPT_ADAPTER *ioc) + static void + mptsas_probe_devices(MPT_ADAPTER *ioc) + { ++ u16 retry_count; + u16 handle; + struct mptsas_devinfo sas_device; + struct mptsas_phyinfo *phy_info; ++ enum device_state state; + + handle = 0xFFFF; + while (!(mptsas_sas_device_pg0(ioc, &sas_device, +@@ -3634,7 +4383,17 @@ mptsas_probe_devices(MPT_ADAPTER *ioc) + if (mptsas_get_rphy(phy_info)) + continue; + +- mptsas_add_end_device(ioc, phy_info); ++ state = DEVICE_RETRY; ++ retry_count = 0; ++ while(state == DEVICE_RETRY) { ++ state = mptsas_test_unit_ready(ioc, sas_device.channel, ++ sas_device.id, retry_count++); ++ ssleep(1); ++ } ++ if (state == DEVICE_READY) ++ mptsas_add_end_device(ioc, phy_info); ++ else ++ memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo)); + } + } + +@@ -3676,13 +4435,14 @@ mptsas_scan_sas_topology(MPT_ADAPTER *io + } + + ++ + static void + mptsas_handle_queue_full_event(struct fw_event_work *fw_event) + { + MPT_ADAPTER *ioc; + EventDataQueueFull_t *qfull_data; +- struct mptsas_device_info *sas_info; +- struct scsi_device *sdev; ++ struct sas_device_info *sas_info; ++ struct scsi_device *sdev; + int depth; + int id = -1; + int channel = -1; +@@ -3697,7 +4457,7 @@ mptsas_handle_queue_full_event(struct fw + current_depth = le16_to_cpu(qfull_data->CurrentDepth); + + /* if hidden raid component, look for the volume id */ +- mutex_lock(&ioc->sas_device_info_mutex); ++ down(&ioc->sas_device_info_mutex); + if (mptscsih_is_phys_disk(ioc, fw_channel, fw_id)) { + list_for_each_entry(sas_info, &ioc->sas_device_info_list, + list) { +@@ -3730,7 +4490,7 @@ mptsas_handle_queue_full_event(struct fw + } + + out: +- mutex_unlock(&ioc->sas_device_info_mutex); ++ up(&ioc->sas_device_info_mutex); + + if (id != -1) { + shost_for_each_device(sdev, ioc->sh) { +@@ -3764,6 +4524,12 @@ mptsas_handle_queue_full_event(struct fw + } + + ++/** ++ * mptsas_find_phyinfo_by_sas_address - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @sas_address: ++ * ++ **/ + static struct mptsas_phyinfo * + mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address) + { +@@ -3788,6 +4554,7 @@ mptsas_find_phyinfo_by_sas_address(MPT_A + return phy_info; + } + ++ + /** + * mptsas_find_phyinfo_by_phys_disk_num - + * @ioc: Pointer to MPT_ADAPTER structure +@@ -3814,7 +4581,7 @@ mptsas_find_phyinfo_by_phys_disk_num(MPT + num_paths = mpt_raid_phys_disk_get_num_paths(ioc, phys_disk_num); + if (!num_paths) + goto out; +- phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) + ++ phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t,Path) + + (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL); + if (!phys_disk) + goto out; +@@ -3827,8 +4594,7 @@ mptsas_find_phyinfo_by_phys_disk_num(MPT + (channel == phys_disk->Path[i].PhysDiskBus)) { + memcpy(&sas_address, &phys_disk->Path[i].WWID, + sizeof(u64)); +- phy_info = mptsas_find_phyinfo_by_sas_address(ioc, +- sas_address); ++ phy_info = mptsas_find_phyinfo_by_sas_address(ioc, sas_address); + goto out; + } + } +@@ -3850,11 +4616,9 @@ mptsas_find_phyinfo_by_phys_disk_num(MPT + continue; + if (port_info->phy_info[i].attached.phys_disk_num == ~0) + continue; +- if ((port_info->phy_info[i].attached.phys_disk_num == +- phys_disk_num) && +- (port_info->phy_info[i].attached.id == id) && +- (port_info->phy_info[i].attached.channel == +- channel)) ++ if (port_info->phy_info[i].attached.phys_disk_num == phys_disk_num && ++ port_info->phy_info[i].attached.id == id && ++ port_info->phy_info[i].attached.channel == channel) + phy_info = &port_info->phy_info[i]; + } + } +@@ -3862,6 +4626,12 @@ mptsas_find_phyinfo_by_phys_disk_num(MPT + return phy_info; + } + ++/** ++ * mptsas_reprobe_lun - ++ * @sdev: ++ * @data: ++ * ++ **/ + static void + mptsas_reprobe_lun(struct scsi_device *sdev, void *data) + { +@@ -3871,6 +4641,12 @@ mptsas_reprobe_lun(struct scsi_device *s + rc = scsi_device_reprobe(sdev); + } + ++/** ++ * mptsas_reprobe_target - ++ * @starget: ++ * @uld_attach: ++ * ++ **/ + static void + mptsas_reprobe_target(struct scsi_target *starget, int uld_attach) + { +@@ -3878,6 +4654,15 @@ mptsas_reprobe_target(struct scsi_target + mptsas_reprobe_lun); + } + ++/** ++ * mptsas_adding_inactive_raid_components - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @channel: ++ * @id: ++ * ++ * ++ * TODO: check for hotspares ++ **/ + static void + mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id) + { +@@ -3885,7 +4670,7 @@ mptsas_adding_inactive_raid_components(M + ConfigPageHeader_t hdr; + dma_addr_t dma_handle; + pRaidVolumePage0_t buffer = NULL; +- RaidPhysDiskPage0_t phys_disk; ++ RaidPhysDiskPage0_t phys_disk; + int i; + struct mptsas_phyinfo *phy_info; + struct mptsas_devinfo sas_device; +@@ -3896,6 +4681,7 @@ mptsas_adding_inactive_raid_components(M + cfg.pageAddr = (channel << 8) + id; + cfg.cfghdr.hdr = &hdr; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; ++ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT; + + if (mpt_config(ioc, &cfg) != 0) + goto out; +@@ -3956,6 +4742,7 @@ mptsas_hotplug_work(MPT_ADAPTER *ioc, st + struct scsi_target * starget; + struct mptsas_devinfo sas_device; + VirtTarget *vtarget; ++ enum device_state state; + int i; + + switch (hot_plug_info->event_type) { +@@ -3995,13 +4782,27 @@ mptsas_hotplug_work(MPT_ADAPTER *ioc, st + if (mptsas_get_rphy(phy_info)) + break; + +- mptsas_add_end_device(ioc, phy_info); ++ state = mptsas_test_unit_ready(ioc, phy_info->attached.channel, ++ phy_info->attached.id, fw_event->retries); ++ ++ if (state == DEVICE_RETRY && !ioc->fw_events_off) { ++ mptsas_requeue_fw_event(ioc, fw_event, 1000); ++ return; ++ } ++ ++ if (state == DEVICE_READY) ++ mptsas_add_end_device(ioc, phy_info); ++ else ++ memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo)); + break; + + case MPTSAS_DEL_DEVICE: +- phy_info = mptsas_find_phyinfo_by_sas_address(ioc, +- hot_plug_info->sas_address); +- mptsas_del_end_device(ioc, phy_info); ++ ++ if (!ioc->disable_hotplug_remove) { ++ phy_info = mptsas_find_phyinfo_by_sas_address(ioc, ++ hot_plug_info->sas_address); ++ mptsas_del_end_device(ioc, phy_info); ++ } + break; + + case MPTSAS_DEL_PHYSDISK: +@@ -4009,9 +4810,8 @@ mptsas_hotplug_work(MPT_ADAPTER *ioc, st + mpt_findImVolumes(ioc); + + phy_info = mptsas_find_phyinfo_by_phys_disk_num( +- ioc, hot_plug_info->phys_disk_num, +- hot_plug_info->channel, +- hot_plug_info->id); ++ ioc, hot_plug_info->phys_disk_num, hot_plug_info->channel, ++ hot_plug_info->id); + mptsas_del_end_device(ioc, phy_info); + break; + +@@ -4162,6 +4962,14 @@ mptsas_hotplug_work(MPT_ADAPTER *ioc, st + mptsas_free_fw_event(ioc, fw_event); + } + ++/** ++ * mptsas_send_sas_event ++ * ++ * ++ * @ioc ++ * @sas_event_data ++ * ++ **/ + static void + mptsas_send_sas_event(struct fw_event_work *fw_event) + { +@@ -4228,6 +5036,15 @@ mptsas_send_sas_event(struct fw_event_wo + } + } + ++ ++/** ++ * mptsas_send_raid_event ++ * ++ * ++ * @ioc ++ * @raid_event_data ++ * ++ **/ + static void + mptsas_send_raid_event(struct fw_event_work *fw_event) + { +@@ -4347,19 +5164,19 @@ mptsas_send_raid_event(struct fw_event_w + /** + * mptsas_issue_tm - send mptsas internal tm request + * @ioc: Pointer to MPT_ADAPTER structure +- * @type: Task Management type +- * @channel: channel number for task management +- * @id: Logical Target ID for reset (if appropriate) +- * @lun: Logical unit for reset (if appropriate) +- * @task_context: Context for the task to be aborted +- * @timeout: timeout for task management control ++ * @type ++ * @channel ++ * @id ++ * @lun ++ * @task_context ++ * @timeout + * +- * return 0 on success and -1 on failure: ++ * return: + * +- */ ++ **/ + static int +-mptsas_issue_tm(MPT_ADAPTER *ioc, u8 type, u8 channel, u8 id, u64 lun, +- int task_context, ulong timeout, u8 *issue_reset) ++mptsas_issue_tm(MPT_ADAPTER *ioc, u8 type, u8 channel, u8 id, u64 lun, int task_context, ulong timeout, ++ u8 *issue_reset) + { + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; +@@ -4367,8 +5184,7 @@ mptsas_issue_tm(MPT_ADAPTER *ioc, u8 typ + unsigned long timeleft; + + *issue_reset = 0; +- mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc); +- if (mf == NULL) { ++ if ((mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc)) == NULL) { + retval = -1; /* return failure */ + dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "TaskMgmt request: no " + "msg frames!!\n", ioc->name)); +@@ -4426,20 +5242,20 @@ mptsas_issue_tm(MPT_ADAPTER *ioc, u8 typ + } + + /** +- * mptsas_broadcast_primative_work - Handle broadcast primitives ++ * mptsas_broadcast_primative_work - Work queue thread to handle ++ * broadcast primitive events + * @work: work queue payload containing info describing the event + * +- * this will be handled in workqueue context. +- */ ++ **/ + static void + mptsas_broadcast_primative_work(struct fw_event_work *fw_event) + { + MPT_ADAPTER *ioc = fw_event->ioc; +- MPT_FRAME_HDR *mf; +- VirtDevice *vdevice; ++ MPT_FRAME_HDR *mf; ++ VirtDevice *vdevice; + int ii; + struct scsi_cmnd *sc; +- SCSITaskMgmtReply_t *pScsiTmReply; ++ SCSITaskMgmtReply_t * pScsiTmReply; + u8 issue_reset; + int task_context; + u8 channel, id; +@@ -4448,7 +5264,7 @@ mptsas_broadcast_primative_work(struct f + u32 query_count; + + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "%s - enter\n", ioc->name, __func__)); ++ "%s - enter\n", ioc->name, __FUNCTION__)); + + mutex_lock(&ioc->taskmgmt_cmds.mutex); + if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) { +@@ -4506,7 +5322,7 @@ mptsas_broadcast_primative_work(struct f + out: + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "%s - exit, query_count = %d termination_count = %d\n", +- ioc->name, __func__, query_count, termination_count)); ++ ioc->name, __FUNCTION__, query_count, termination_count)); + + ioc->broadcast_aen_busy = 0; + mpt_clear_taskmgmt_in_progress_flag(ioc); +@@ -4514,20 +5330,19 @@ mptsas_broadcast_primative_work(struct f + + if (issue_reset) { + printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", +- ioc->name, __func__); +- mpt_HardResetHandler(ioc, CAN_SLEEP); ++ ioc->name, __FUNCTION__); ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP)) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); + } + mptsas_free_fw_event(ioc, fw_event); + } + +-/* +- * mptsas_send_ir2_event - handle exposing hidden disk when +- * an inactive raid volume is added +- * +- * @ioc: Pointer to MPT_ADAPTER structure +- * @ir2_data ++/** ++ * mptsas_send_ir2_event - handle exposing hidden disk when an inactive raid volume is added ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @ir2_data: + * +- */ ++ **/ + static void + mptsas_send_ir2_event(struct fw_event_work *fw_event) + { +@@ -4569,6 +5384,13 @@ mptsas_send_ir2_event(struct fw_event_wo + mptsas_hotplug_work(ioc, fw_event, &hot_plug_info); + } + ++ ++/** ++ * mptsas_event_process - ++ * @ioc: Pointer to MPT_ADAPTER structure ++ * @reply: ++ * ++ **/ + static int + mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) + { +@@ -4577,6 +5399,9 @@ mptsas_event_process(MPT_ADAPTER *ioc, E + struct fw_event_work *fw_event; + unsigned long delay; + ++ if (ioc->bus_type != SAS) ++ return 0; ++ + /* events turned off due to host reset or driver unloading */ + if (ioc->fw_events_off) + return 0; +@@ -4659,6 +5484,7 @@ mptsas_event_process(MPT_ADAPTER *ioc, E + return 0; + } + ++ + /* Delete a volume when no longer listed in ioc pg2 + */ + static void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id) +@@ -4678,18 +5504,24 @@ static void mptsas_volume_delete(MPT_ADA + goto release_sdev; + out: + printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, " +- "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL, id); ++ "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,id); + scsi_remove_device(sdev); + release_sdev: + scsi_device_put(sdev); + } + ++/** ++ * mptsas_probe - ++ * @pdev: ++ * @id: ++ * ++ **/ + static int + mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) + { + struct Scsi_Host *sh; + MPT_SCSI_HOST *hd; +- MPT_ADAPTER *ioc; ++ MPT_ADAPTER *ioc; + unsigned long flags; + int ii; + int numSGE = 0; +@@ -4707,7 +5539,7 @@ mptsas_probe(struct pci_dev *pdev, const + ioc->DoneCtx = mptsasDoneCtx; + ioc->TaskCtx = mptsasTaskCtx; + ioc->InternalCtx = mptsasInternalCtx; +- ++ ioc->schedule_target_reset = &mptsas_schedule_target_reset; + /* Added sanity check on readiness of the MPT adapter. + */ + if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) { +@@ -4748,7 +5580,7 @@ mptsas_probe(struct pci_dev *pdev, const + ioc->name); + error = -1; + goto out_mptsas_probe; +- } ++ } + + spin_lock_irqsave(&ioc->FreeQlock, flags); + +@@ -4773,10 +5605,10 @@ mptsas_probe(struct pci_dev *pdev, const + + INIT_LIST_HEAD(&ioc->sas_topology); + mutex_init(&ioc->sas_topology_mutex); +- mutex_init(&ioc->sas_discovery_mutex); + mutex_init(&ioc->sas_mgmt.mutex); + init_completion(&ioc->sas_mgmt.done); + ++ + /* Verify that we won't exceed the maximum + * number of chain buffers + * We can optimize: ZZ = req_sz/sizeof(SGE) +@@ -4786,6 +5618,7 @@ mptsas_probe(struct pci_dev *pdev, const + * A slightly different algorithm is required for + * 64bit SGEs. + */ ++ + scale = ioc->req_sz/ioc->SGE_size; + if (ioc->sg_addr_size == sizeof(u64)) { + numSGE = (scale - 1) * +@@ -4822,12 +5655,16 @@ mptsas_probe(struct pci_dev *pdev, const + dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n", + ioc->name, ioc->ScsiLookup)); + ++ ioc->sdev_queue_depth = mpt_sdev_queue_depth; + ioc->sas_data.ptClear = mpt_pt_clear; +- + hd->last_queue_full = 0; ++ ioc->disable_hotplug_remove = mpt_disable_hotplug_remove; ++ if (ioc->disable_hotplug_remove) ++ printk(MYIOC_s_INFO_FMT "disabling hotplug remove\n", ioc->name); ++ + INIT_LIST_HEAD(&hd->target_reset_list); + INIT_LIST_HEAD(&ioc->sas_device_info_list); +- mutex_init(&ioc->sas_device_info_mutex); ++ init_MUTEX(&ioc->sas_device_info_mutex); + + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + +@@ -4865,7 +5702,14 @@ mptsas_shutdown(struct pci_dev *pdev) + mptsas_cleanup_fw_event_q(ioc); + } + +-static void __devexit mptsas_remove(struct pci_dev *pdev) ++ ++/** ++ * mptsas_remove - ++ * @pdev: ++ * ++ **/ ++static void __devexit ++mptsas_remove(struct pci_dev *pdev) + { + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + struct mptsas_portinfo *p, *n; +@@ -4875,7 +5719,6 @@ static void __devexit mptsas_remove(stru + + mptsas_del_device_components(ioc); + +- ioc->sas_discovery_ignore_events = 1; + sas_remove_host(ioc->sh); + + mutex_lock(&ioc->sas_topology_mutex); +@@ -4920,6 +5763,10 @@ static struct pci_driver mptsas_driver = + #endif + }; + ++/** ++ * mptsas_init - ++ * ++ **/ + static int __init + mptsas_init(void) + { +@@ -4950,6 +5797,10 @@ mptsas_init(void) + return error; + } + ++/** ++ * mptsas_exit - ++ * ++ **/ + static void __exit + mptsas_exit(void) + { +--- a/drivers/message/fusion/mptsas.h ++++ b/drivers/message/fusion/mptsas.h +@@ -50,8 +50,8 @@ + /*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + + struct mptsas_target_reset_event { +- struct list_head list; +- EVENT_DATA_SAS_DEVICE_STATUS_CHANGE sas_event_data; ++ struct list_head list; ++ MpiEventDataSasDeviceStatusChange_t sas_event_data; + u8 target_reset_issued; + unsigned long time_count; + }; +@@ -61,34 +61,32 @@ enum mptsas_hotplug_action { + MPTSAS_DEL_DEVICE, + MPTSAS_ADD_RAID, + MPTSAS_DEL_RAID, ++ MPTSAS_ADD_INACTIVE_VOLUME, + MPTSAS_ADD_PHYSDISK, + MPTSAS_ADD_PHYSDISK_REPROBE, + MPTSAS_DEL_PHYSDISK, + MPTSAS_DEL_PHYSDISK_REPROBE, +- MPTSAS_ADD_INACTIVE_VOLUME, ++ MPTSAS_REQUEUE_EVENT, + MPTSAS_IGNORE_EVENT, + }; + +-struct mptsas_mapping{ ++struct sas_mapping{ + u8 id; + u8 channel; + }; + +-struct mptsas_device_info { +- struct list_head list; +- struct mptsas_mapping os; /* operating system mapping*/ +- struct mptsas_mapping fw; /* firmware mapping */ ++struct sas_device_info { ++ struct list_head list; ++ struct sas_mapping os; /* operating system mapping*/ ++ struct sas_mapping fw; /* firmware mapping */ + u64 sas_address; + u32 device_info; /* specific bits for devices */ + u16 slot; /* enclosure slot id */ + u64 enclosure_logical_id; /*enclosure address */ + u8 is_logical_volume; /* is this logical volume */ +- /* this belongs to volume */ +- u8 is_hidden_raid_component; +- /* this valid when is_hidden_raid_component set */ +- u8 volume_id; +- /* cached data for a removed device */ +- u8 is_cached; ++ u8 is_hidden_raid_component; /* this belongs to volume */ ++ u8 volume_id; /* this valid when is_hidden_raid_component set */ ++ u8 is_cached; /* cached data for a removed device */ + }; + + struct mptsas_hotplug_event { +@@ -104,19 +102,23 @@ struct mptsas_hotplug_event { + struct scsi_device *sdev; + }; + ++ + struct fw_event_work { +- struct list_head list; ++ struct list_head list; + struct delayed_work work; +- MPT_ADAPTER *ioc; ++ MPT_ADAPTER *ioc; + u32 event; + u8 retries; + u8 event_data[1]; + }; + +-struct mptsas_discovery_event { ++#if 0 ++struct mptsas_link_status_event { + struct work_struct work; ++ MpiEventDataSasPhyLinkStatus_t link_data; + MPT_ADAPTER *ioc; + }; ++#endif + + /* + * SAS topology structures +@@ -146,20 +148,20 @@ struct mptsas_devinfo { + * Specific details on ports, wide/narrow + */ + struct mptsas_portinfo_details{ +- u16 num_phys; /* number of phys belong to this port */ +- u64 phy_bitmask; /* TODO, extend support for 255 phys */ +- struct sas_rphy *rphy; /* transport layer rphy object */ ++ u16 num_phys; /* number of phys beloing to this port */ ++ u64 phy_bitmask; /* this needs extending to support 128 phys */ ++ struct sas_rphy *rphy; /* rphy for end devices */ + struct sas_port *port; /* transport layer port object */ + struct scsi_target *starget; + struct mptsas_portinfo *port_info; + }; + + struct mptsas_phyinfo { +- u16 handle; /* unique id to address this */ +- u8 phy_id; /* phy index */ +- u8 port_id; /* firmware port identifier */ ++ u16 handle; /* handle for this phy */ ++ u8 phy_id; /* phy index */ ++ u8 port_id; /* port number this phy is part of */ + u8 negotiated_link_rate; /* nego'd link rate for this phy */ +- u8 hw_link_rate; /* hardware max/min phys link rate */ ++ u8 hw_link_rate; /* hardware max/min phys link rate */ + u8 programmed_link_rate; /* programmed max/min phy link rate */ + u8 sas_port_add_phy; /* flag to request sas_port_add_phy*/ + struct mptsas_devinfo identify; /* point to phy device info */ +@@ -171,7 +173,7 @@ struct mptsas_phyinfo { + + struct mptsas_portinfo { + struct list_head list; +- u16 num_phys; /* number of phys */ ++ u16 num_phys; /* number of phys */ + struct mptsas_phyinfo *phy_info; + }; + +@@ -186,6 +188,12 @@ struct mptsas_enclosure { + u8 sep_id; /* SEP device logical target id */ + u8 sep_channel; /* SEP channel logical channel id */ + }; +- ++#if 0 ++struct mptsas_broadcast_primative_event { ++ struct delayed_work aen_work; ++ MPT_ADAPTER *ioc; ++}; ++#endif + /*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + #endif ++ +--- a/drivers/message/fusion/mptscsih.c ++++ b/drivers/message/fusion/mptscsih.c +@@ -53,7 +53,9 @@ + #include /* for mdelay */ + #include /* needed for in_interrupt() proto */ + #include /* notifier code */ ++#include + #include ++#include + + #include + #include +@@ -77,10 +79,15 @@ MODULE_LICENSE("GPL"); + MODULE_VERSION(my_VERSION); + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++typedef struct _BIG_SENSE_BUF { ++ u8 data[MPT_SENSE_BUFFER_ALLOC]; ++} BIG_SENSE_BUF; ++ ++ + /* + * Other private/forward protos... + */ +-struct scsi_cmnd *mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i); ++struct scsi_cmnd * mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i); + static struct scsi_cmnd * mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i); + static void mptscsih_set_scsi_lookup(MPT_ADAPTER *ioc, int i, struct scsi_cmnd *scmd); + static int SCPNT_TO_LOOKUP_IDX(MPT_ADAPTER *ioc, struct scsi_cmnd *scmd); +@@ -93,32 +100,20 @@ static int mptscsih_AddSGE(MPT_ADAPTER * + static void mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx); + static void mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply); + +-int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, +- int lun, int ctx2abort, ulong timeout); +- + int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset); + int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); ++static void mptscsih_synchronize_cache(struct scsi_device *sdev, MPT_SCSI_HOST *hd, VirtDevice *vdevice); + +-void +-mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code); +-static int mptscsih_get_completion_code(MPT_ADAPTER *ioc, +- MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply); +-int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); +-static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd); +-static void mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice); +- +-static int +-mptscsih_taskmgmt_reply(MPT_ADAPTER *ioc, u8 type, +- SCSITaskMgmtReply_t *pScsiTmReply); +-void mptscsih_remove(struct pci_dev *); +-void mptscsih_shutdown(struct pci_dev *); ++void mptscsih_remove(struct pci_dev *); ++void mptscsih_shutdown(struct pci_dev *); + #ifdef CONFIG_PM +-int mptscsih_suspend(struct pci_dev *pdev, pm_message_t state); +-int mptscsih_resume(struct pci_dev *pdev); ++int mptscsih_suspend(struct pci_dev *pdev, pm_message_t state); ++int mptscsih_resume(struct pci_dev *pdev); + #endif +- +-#define SNS_LEN(scp) SCSI_SENSE_BUFFERSIZE +- ++static int mptscsih_taskmgmt_reply(MPT_ADAPTER *ioc, u8 type, ++ SCSITaskMgmtReply_t *pScsiTmReply); ++static int mptscsih_get_completion_code(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, ++ MPT_FRAME_HDR *reply); + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /* +@@ -178,7 +173,7 @@ static int + mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt, + SCSIIORequest_t *pReq, int req_idx) + { +- char *psge; ++ char *psge; + char *chainSge; + struct scatterlist *sg; + int frm_sz; +@@ -193,16 +188,29 @@ mptscsih_AddSGE(MPT_ADAPTER *ioc, struct + dma_addr_t v2; + u32 RequestNB; + +- sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; ++#ifdef EEDP_SUPPORT ++ if (pReq->Function == MPI_FUNCTION_SCSI_IO_32) { ++ SCSIIO32Request_t *mpi_request = (SCSIIO32Request_t *)pReq; ++ ++ sgdir = le32_to_cpu(mpi_request->Control) ++ & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; ++ psge = (char *) &mpi_request->SGL; ++ } else { ++#endif ++ sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; ++ psge = (char *) &pReq->SGL; ++#ifdef EEDP_SUPPORT ++ } ++#endif + if (sgdir == MPI_SCSIIO_CONTROL_WRITE) { + sgdir = MPT_TRANSFER_HOST_TO_IOC; + } else { + sgdir = MPT_TRANSFER_IOC_TO_HOST; + } + +- psge = (char *) &pReq->SGL; + frm_sz = ioc->req_sz; + ++ + /* Map the data portion, if any. + * sges_left = 0 if no data transfer. + */ +@@ -214,7 +222,7 @@ mptscsih_AddSGE(MPT_ADAPTER *ioc, struct + */ + sg = scsi_sglist(SCpnt); + sg_done = 0; +- sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION); ++ sgeOffset = psge - (char *) pReq; + chainSge = NULL; + + /* Prior to entering this loop - the following must be set +@@ -237,7 +245,7 @@ nextSGEset: + thisxfer = sg_dma_len(sg); + if (thisxfer == 0) { + /* Get next SG element from the OS */ +- sg = sg_next(sg); ++ sg = mpt_sg_next(sg); + sg_done++; + continue; + } +@@ -246,7 +254,7 @@ nextSGEset: + ioc->add_sge(psge, sgflags | thisxfer, v2); + + /* Get next SG element from the OS */ +- sg = sg_next(sg); ++ sg = mpt_sg_next(sg); + psge += ioc->SGE_size; + sgeOffset += ioc->SGE_size; + sg_done++; +@@ -346,7 +354,7 @@ nextSGEset: + if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) { + dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "getFreeChainBuffer FAILED SCSI cmd=%02x (%p)\n", +- ioc->name, pReq->CDB[0], SCpnt)); ++ ioc->name, pReq->CDB[0], SCpnt)); + return FAILED; + } + +@@ -392,7 +400,7 @@ mptscsih_issue_sep_command(MPT_ADAPTER * + U32 SlotStatus) + { + MPT_FRAME_HDR *mf; +- SEPRequest_t *SEPMsg; ++ SEPRequest_t *SEPMsg; + + if (ioc->bus_type != SAS) + return; +@@ -598,14 +606,16 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + req_idx_MR = (mr != NULL) ? + le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx) : req_idx; +- +- /* Special case, where already freed message frame is received from +- * Firmware. It happens with Resetting IOC. +- * Return immediately. Do not care +- */ + if ((req_idx != req_idx_MR) || +- (le32_to_cpu(mf->u.frame.linkage.arg1) == 0xdeadbeaf)) ++ (le32_to_cpu(mf->u.frame.linkage.arg1) == 0xdeadbeaf)) { ++ printk(MYIOC_s_ERR_FMT "Received a mf that was already freed\n", ++ ioc->name); ++ printk (MYIOC_s_ERR_FMT ++ "req_idx=%x req_idx_MR=%x mf=%p mr=%p sc=%p\n", ++ ioc->name, req_idx, req_idx_MR, mf, mr, ++ mptscsih_get_scsi_lookup(ioc, req_idx_MR)); + return 0; ++ } + + sc = mptscsih_getclear_scsi_lookup(ioc, req_idx); + if (sc == NULL) { +@@ -658,6 +668,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + ; + } else { + u32 xfer_cnt; ++ u32 difftransfer; + u16 status; + u8 scsi_state, scsi_status; + u32 log_info; +@@ -668,6 +679,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); + scsi_set_resid(sc, scsi_bufflen(sc) - xfer_cnt); + log_info = le32_to_cpu(pScsiReply->IOCLogInfo); ++ vdevice = sc->device->hostdata; + + /* + * if we get a data underrun indication, yet no data was +@@ -685,18 +697,6 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) + mptscsih_copy_sense_data(sc, hd, mf, pScsiReply); + +- /* +- * Look for + dump FCP ResponseInfo[]! +- */ +- if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID && +- pScsiReply->ResponseInfo) { +- printk(MYIOC_s_NOTE_FMT "[%d:%d:%d:%d] " +- "FCP_ResponseInfo=%08xh\n", ioc->name, +- sc->device->host->host_no, sc->device->channel, +- sc->device->id, sc->device->lun, +- le32_to_cpu(pScsiReply->ResponseInfo)); +- } +- + switch(status) { + case MPI_IOCSTATUS_BUSY: /* 0x0002 */ + case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */ +@@ -724,7 +724,6 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF) + hd->sel_timeout[pScsiReq->TargetID]++; + +- vdevice = sc->device->hostdata; + if (!vdevice) + break; + vtarget = vdevice->vtarget; +@@ -769,7 +768,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + sc->result = DID_RESET << 16; + + case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */ +- if (ioc->bus_type == FC) ++ if ( ioc->bus_type == FC ) + sc->result = DID_ERROR << 16; + else + sc->result = DID_RESET << 16; +@@ -830,7 +829,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + } + if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { + /* What to do? +- */ ++ */ + sc->result = DID_SOFT_ERROR << 16; + } + else if (scsi_state & MPI_SCSI_STATE_TERMINATED) { +@@ -923,7 +922,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + + } + else if (scsi_state & +- (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS) ++ (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS) + ) { + /* + * What to do? +@@ -954,6 +953,13 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F + case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */ + sc->result = DID_SOFT_ERROR << 16; + break; ++#ifdef EEDP_SUPPORT ++ case MPI_IOCSTATUS_EEDP_GUARD_ERROR: ++ case MPI_IOCSTATUS_EEDP_REF_TAG_ERROR: ++ case MPI_IOCSTATUS_EEDP_APP_TAG_ERROR: ++ sc->result = DID_PARITY << 16; ++ break; ++#endif /* EEDP Support */ + + case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */ + case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */ +@@ -1143,8 +1149,8 @@ mptscsih_report_queue_full(struct scsi_c + void + mptscsih_remove(struct pci_dev *pdev) + { +- MPT_ADAPTER *ioc = pci_get_drvdata(pdev); +- struct Scsi_Host *host = ioc->sh; ++ MPT_ADAPTER *ioc = pci_get_drvdata(pdev); ++ struct Scsi_Host *host = ioc->sh; + MPT_SCSI_HOST *hd; + int sz1; + +@@ -1204,7 +1210,7 @@ mptscsih_shutdown(struct pci_dev *pdev) + int + mptscsih_suspend(struct pci_dev *pdev, pm_message_t state) + { +- MPT_ADAPTER *ioc = pci_get_drvdata(pdev); ++ MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + + scsi_block_requests(ioc->sh); + flush_scheduled_work(); +@@ -1221,7 +1227,7 @@ mptscsih_suspend(struct pci_dev *pdev, p + int + mptscsih_resume(struct pci_dev *pdev) + { +- MPT_ADAPTER *ioc = pci_get_drvdata(pdev); ++ MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + int rc; + + rc = mpt_resume(pdev); +@@ -1280,13 +1286,13 @@ mptscsih_copy_mem_info(struct info_str * + } + + if (info->pos < info->offset) { +- data += (info->offset - info->pos); +- len -= (info->offset - info->pos); ++ data += (info->offset - info->pos); ++ len -= (info->offset - info->pos); + } + + if (len > 0) { +- memcpy(info->buffer + info->pos, data, len); +- info->pos += len; ++ memcpy(info->buffer + info->pos, data, len); ++ info->pos += len; + } + } + +@@ -1326,12 +1332,12 @@ mptscsih_host_info(MPT_ADAPTER *ioc, cha + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * mptscsih_proc_info - Return information about MPT adapter +- * @host: scsi host struct +- * @buffer: if write, user data; if read, buffer for user ++ * @host: scsi host struct ++ * @buffer: if write, user data; if read, buffer for user + * @start: returns the buffer address +- * @offset: if write, 0; if read, the current offset into the buffer from +- * the previous read. +- * @length: if write, return length; ++ * @offset: if write, 0; if read, the current offset into the buffer from ++ * the previous read. ++ * @length: if write, return length; + * @func: write = 1; read = 0 + * + * (linux scsi_host_template.info routine) +@@ -1358,6 +1364,103 @@ mptscsih_proc_info(struct Scsi_Host *hos + return size; + } + ++#ifdef EEDP_SUPPORT ++u8 opcode_protection[256] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V, ++ 0, 0, 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ++ ++/** ++ * _scsih_setup_eedp - setup MPI request for EEDP transfer ++ * @ioc: ++ * @scmd: pointer to scsi command object ++ * @mpi_request: pointer to the SCSI_IO reqest message frame ++ * ++ * Supporting protection 1 only. ++ * ++ * Returns nothing ++ */ ++static int ++_scsih_setup_eedp(MPT_ADAPTER *ioc, struct scsi_cmnd *scmd, SCSIIO32Request_t *mpi_request) ++{ ++ VirtDevice *vdevice = scmd->device->hostdata; ++ u16 eedp_flags; ++ u8 scsi_opcode; ++ int lba_byte; ++ u32 *lba32; ++ ++ vdevice = scmd->device->hostdata; ++ if (!vdevice->eedp_enable) ++ return -1; ++ ++ /* protection type 1 support only */ ++ if (vdevice->eedp_type != 0) ++ return -1; ++ ++ /* check whether scsi opcode supports eedp transfer */ ++ scsi_opcode = scmd->cmnd[0]; ++ eedp_flags = opcode_protection[scsi_opcode]; ++ if (!eedp_flags) ++ return -1; ++ ++ /* ++ * enable ref/app/guard checking ++ * auto increment ref and app tag ++ */ ++ mpi_request->EEDPFlags = eedp_flags | ++ MPI_SCSIIO32_EEDPFLAGS_INC_PRI_REFTAG | ++ MPI_SCSIIO32_EEDPFLAGS_T10_CHK_REFTAG | ++ MPI_SCSIIO32_EEDPFLAGS_T10_CHK_LBATAG | ++ MPI_SCSIIO32_EEDPFLAGS_T10_CHK_GUARD; ++ ++ /* set block size */ ++ mpi_request->EEDPBlockSize = vdevice->eedp_block_length; ++ mpi_request->EEDPBlockSize += 8; ++ memset(mpi_request->CDB.CDB32, 0, 32); ++ ++ mpi_request->CDB.EEDP32.PrimaryApplicationTagMask = 0xFFFF; ++ ++ /* set reference tag to low 32bit lba */ ++ lba_byte = (scmd->cmd_len == 16) ? 6 : 2; ++ lba32 = (u32 *)&scmd->cmnd[lba_byte]; ++ mpi_request->CDB.EEDP32.PrimaryReferenceTag = *lba32; ++ ++ /* set RDPROTECT, WRPROTECT, VRPROTECT bits to (001b) */ ++ scmd->cmnd[1] = (scmd->cmnd[1] & 0x1F) | 0x20; ++ ++ /* add the rest of the bits */ ++ mpi_request->Port = 0; ++ mpi_request->Flags = MPI_SCSIIO32_FLAGS_FORM_SCSIID; ++ mpi_request->DeviceAddress.SCSIID.TargetID = vdevice->vtarget->id; ++ mpi_request->DeviceAddress.SCSIID.Bus = vdevice->vtarget->channel; ++ mpi_request->ChainOffset = 0; ++ mpi_request->Function = MPI_FUNCTION_SCSI_IO_32; ++ mpi_request->CDBLength = scmd->cmd_len; ++ mpi_request->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; ++ mpi_request->MsgFlags = mpt_msg_flags(ioc); ++ int_to_scsilun(scmd->device->lun, (struct scsi_lun *)mpi_request->LUN); ++ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); ++ mpi_request->SGLOffset0 = offsetof(SCSIIO32Request_t, SGL) / 4; ++ mpi_request->SGLOffset1 = 0; ++ mpi_request->SGLOffset2 = 0; ++ mpi_request->SGLOffset3 = 0; ++ return 0; ++} ++#endif /* EEDP Support */ ++ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + #define ADD_INDEX_LOG(req_ent) do { } while(0) + +@@ -1444,6 +1547,36 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, v + + /* Use the above information to set up the message frame + */ ++#ifdef EEDP_SUPPORT ++ if (_scsih_setup_eedp(ioc, SCpnt, (SCSIIO32Request_t *)mf) == 0) { ++ SCSIIO32Request_t *mpi_request = (SCSIIO32Request_t *)mf; ++ ++ /* finish off setting the rest of the SCSIIO32 */ ++ mpi_request->Control = cpu_to_le32(scsictl); ++ mpi_request->DataLength = cpu_to_le32(datalen); ++ ++ /* SenseBuffer low address */ ++ mpi_request->SenseBufferLowAddr = ++ cpu_to_le32(ioc->sense_buf_low_dma ++ + (my_idx * MPT_SENSE_BUFFER_ALLOC)); ++ ++ /* Now add the SG list ++ * Always have a SGE even if null length. ++ */ ++ if (datalen == 0) { ++ /* Add a NULL SGE */ ++ ioc->add_sge((char *)&mpi_request->SGL, ++ MPT_SGE_FLAGS_SSIMPLE_READ | 0, ++ (dma_addr_t) -1); ++ } else { ++ /* Add a 32 or 64 bit SGE */ ++ if (mptscsih_AddSGE(ioc, SCpnt, ++ pScsiReq, my_idx) != SUCCESS) ++ goto fail; ++ } ++ goto send_mf; ++ } ++#endif + pScsiReq->TargetID = (u8) vdevice->vtarget->id; + pScsiReq->Bus = vdevice->vtarget->channel; + pScsiReq->ChainOffset = 0; +@@ -1489,6 +1622,9 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, v + goto fail; + } + ++#ifdef EEDP_SUPPORT ++ send_mf: ++#endif + SCpnt->host_scribble = (unsigned char *)mf; + mptscsih_set_scsi_lookup(ioc, my_idx, SCpnt); + +@@ -1560,6 +1696,137 @@ mptscsih_freeChainBuffers(MPT_ADAPTER *i + */ + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++ ++static int ++mptscsih_scandv_bus_reset(MPT_ADAPTER *ioc) ++{ ++ MPT_FRAME_HDR *mf; ++ SCSITaskMgmt_t *pScsiTm; ++ SCSITaskMgmtReply_t *pScsiTmReply; ++ int ii; ++ int retval; ++ unsigned long timeout; ++ unsigned long time_count; ++ u16 iocstatus; ++ ++ mutex_lock(&ioc->taskmgmt_cmds.mutex); ++ if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) { ++ mutex_unlock(&ioc->taskmgmt_cmds.mutex); ++ return -EPERM; ++ } ++ ++ /* Send request ++ */ ++ if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) { ++ dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "TaskMgmt, no msg frames!!\n", ++ ioc->name)); ++ mpt_clear_taskmgmt_in_progress_flag(ioc); ++ retval = -ENOMEM; ++ goto out; ++ } ++ ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n", ++ ioc->name, mf)); ++ ++ pScsiTm = (SCSITaskMgmt_t *) mf; ++ memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t)); ++ pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; ++ pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; ++ pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION; ++ pScsiTm->TargetID = 0; ++ pScsiTm->Bus = 0; ++ pScsiTm->ChainOffset = 0; ++ pScsiTm->Reserved = 0; ++ pScsiTm->Reserved1 = 0; ++ pScsiTm->TaskMsgContext = 0; ++ for (ii= 0; ii < 8; ii++) ++ pScsiTm->LUN[ii] = 0; ++ for (ii=0; ii < 7; ii++) ++ pScsiTm->Reserved2[ii] = 0; ++ ++ switch (ioc->bus_type) { ++ case FC: ++ timeout = 40; ++ break; ++ case SAS: ++ timeout = 30; ++ break; ++ case SPI: ++ default: ++ timeout = 2; ++ break; ++ } ++ ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt type=%d timeout=%ld\n", ++ ioc->name, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, timeout)); ++ ++ INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status) ++ CLEAR_MGMT_STATUS(ioc->internal_cmds.status) ++ retval = 0; ++ time_count = jiffies; ++ if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) && ++ (ioc->facts.MsgVersion >= MPI_VERSION_01_05)) ++ mpt_put_msg_frame_hi_pri(ioc->TaskCtx, ioc, mf); ++ else { ++ retval = mpt_send_handshake_request(ioc->TaskCtx, ioc, ++ sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, CAN_SLEEP); ++ if (retval != 0) { ++ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "TaskMgmt send_handshake FAILED!" ++ " (ioc %p, mf %p, rc=%d) \n", ioc->name, ++ ioc, mf, retval)); ++ mpt_clear_taskmgmt_in_progress_flag(ioc); ++ goto out; ++ } ++ } ++ ++ /* Now wait for the command to complete */ ++ ii = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done, timeout*HZ); ++ if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "TaskMgmt failed\n", ioc->name)); ++ mpt_free_msg_frame(ioc, mf); ++ mpt_clear_taskmgmt_in_progress_flag(ioc); ++ retval = -1; /* return failure */ ++ goto out; ++ } ++ ++ if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) { ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "TaskMgmt failed\n", ioc->name)); ++ retval = -1; /* return failure */ ++ goto out; ++ } ++ ++ pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply; ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "TaskMgmt fw_channel = %d, fw_id = %d, task_type = 0x%02X,\n" ++ "\tiocstatus = 0x%04X, loginfo = 0x%08X, response_code = 0x%02X,\n" ++ "\tterm_cmnds = %d\n", ioc->name, pScsiTmReply->Bus, ++ pScsiTmReply->TargetID, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, ++ le16_to_cpu(pScsiTmReply->IOCStatus), ++ le32_to_cpu(pScsiTmReply->IOCLogInfo), ++ pScsiTmReply->ResponseCode, ++ le32_to_cpu(pScsiTmReply->TerminationCount))); ++ ++ iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK; ++ ++ if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED || ++ iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED || ++ iocstatus == MPI_IOCSTATUS_SUCCESS) ++ retval = 0; ++ else { ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "TaskMgmt failed\n", ioc->name)); ++ retval = -1; /* return failure */ ++ } ++ ++ out: ++ mutex_unlock(&ioc->taskmgmt_cmds.mutex); ++ CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status) ++ return retval; ++} ++ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * mptscsih_IssueTaskMgmt - Generic send Task Management function. + * @hd: Pointer to MPT_SCSI_HOST structure +@@ -1579,14 +1846,13 @@ mptscsih_freeChainBuffers(MPT_ADAPTER *i + * + **/ + int +-mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, +- int ctx2abort, ulong timeout) ++mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout) + { + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; + int ii; + int retval; +- MPT_ADAPTER *ioc = hd->ioc; ++ MPT_ADAPTER *ioc = hd->ioc; + unsigned long timeleft; + u8 issue_hard_reset; + u32 ioc_raw_state; +@@ -1646,7 +1912,7 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd + pScsiTm->TaskType = type; + pScsiTm->Reserved1 = 0; + pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) +- ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0; ++ ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0; + + int_to_scsilun(lun, (struct scsi_lun *)pScsiTm->LUN); + +@@ -1705,7 +1971,8 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd + if (issue_hard_reset) { + printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", + ioc->name, __func__); +- retval = mpt_HardResetHandler(ioc, CAN_SLEEP); ++ if ((retval = mpt_SoftResetHandler(ioc, CAN_SLEEP)) != 0) ++ retval = mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_free_msg_frame(ioc, mf); + } + +@@ -1713,7 +1980,6 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd + mutex_unlock(&ioc->taskmgmt_cmds.mutex); + return retval; + } +-EXPORT_SYMBOL(mptscsih_IssueTaskMgmt); + + static int + mptscsih_get_tm_timeout(MPT_ADAPTER *ioc) +@@ -1722,6 +1988,7 @@ mptscsih_get_tm_timeout(MPT_ADAPTER *ioc + case FC: + return 40; + case SAS: ++ return 30; + case SPI: + default: + return 10; +@@ -1746,7 +2013,7 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) + int scpnt_idx; + int retval; + VirtDevice *vdevice; +- ulong sn = SCpnt->serial_number; ++ ulong sn = SCpnt->serial_number; + MPT_ADAPTER *ioc; + + /* If we can't locate our host adapter structure, return FAILED status. +@@ -1771,7 +2038,21 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) + ioc->name, SCpnt)); + SCpnt->result = DID_NO_CONNECT << 16; + SCpnt->scsi_done(SCpnt); +- retval = 0; ++ retval = SUCCESS; ++ goto out; ++ } ++ ++ /* Find this command ++ */ ++ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(ioc, SCpnt)) < 0) { ++ /* Cmd not found in ScsiLookup. ++ * Do OS callback. ++ */ ++ SCpnt->result = DID_RESET << 16; ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "task abort: command not in the active list! (sc=%p)\n", ++ ioc->name, SCpnt)); ++ retval = SUCCESS; + goto out; + } + +@@ -1786,25 +2067,23 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) + goto out; + } + +- /* Find this command ++ /* Task aborts are not supported for volumes. + */ +- if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(ioc, SCpnt)) < 0) { +- /* Cmd not found in ScsiLookup. +- * Do OS callback. +- */ ++ if (vdevice->vtarget->raidVolume) { ++ dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ++ "task abort: raid volume (sc=%p)\n", ++ ioc->name, SCpnt)); + SCpnt->result = DID_RESET << 16; +- dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "task abort: " +- "Command not in the active list! (sc=%p)\n", ioc->name, +- SCpnt)); +- retval = SUCCESS; ++ retval = FAILED; + goto out; + } + ++ if (mpt_fwfault_debug) ++ mpt_halt_firmware(ioc); ++ + if (ioc->timeouts < -1) + ioc->timeouts++; + +- if (mpt_fwfault_debug) +- mpt_halt_firmware(ioc); + + /* Most important! Set TaskMsgContext to SCpnt's MsgContext! + * (the IO to be ABORT'd) +@@ -1821,6 +2100,10 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) + vdevice->vtarget->id, vdevice->lun, + ctx2abort, mptscsih_get_tm_timeout(ioc)); + ++ ++ /* check to see whether command actually completed and/or ++ * terminated ++ */ + if (SCPNT_TO_LOOKUP_IDX(ioc, SCpnt) == scpnt_idx && + SCpnt->serial_number == sn) { + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT +@@ -1836,7 +2119,7 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) + + out: + printk(MYIOC_s_INFO_FMT "task abort: %s (sc=%p)\n", +- ioc->name, ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), SCpnt); ++ ioc->name, ((retval == SUCCESS) ? "SUCCESS" : "FAILED" ), SCpnt); + + return retval; + } +@@ -1964,7 +2247,7 @@ int + mptscsih_host_reset(struct scsi_cmnd *SCpnt) + { + MPT_SCSI_HOST * hd; +- int status = SUCCESS; ++ int status = SUCCESS; + MPT_ADAPTER *ioc; + int retval; + +@@ -1975,9 +2258,6 @@ mptscsih_host_reset(struct scsi_cmnd *SC + return FAILED; + } + +- /* make sure we have no outstanding commands at this stage */ +- mptscsih_flush_running_cmds(hd); +- + ioc = hd->ioc; + printk(MYIOC_s_INFO_FMT "attempting host reset! (sc=%p)\n", + ioc->name, SCpnt); +@@ -1985,7 +2265,9 @@ mptscsih_host_reset(struct scsi_cmnd *SC + /* If our attempts to reset the host failed, then return a failed + * status. The host will be taken off line by the SCSI mid-layer. + */ +- retval = mpt_HardResetHandler(ioc, CAN_SLEEP); ++ if ((retval = mpt_SoftResetHandler(ioc, CAN_SLEEP)) != 0) ++ retval = mpt_HardResetHandler(ioc, CAN_SLEEP); ++ + if (retval < 0) + status = FAILED; + else +@@ -1997,6 +2279,7 @@ mptscsih_host_reset(struct scsi_cmnd *SC + return status; + } + ++ + static int + mptscsih_taskmgmt_reply(MPT_ADAPTER *ioc, u8 type, + SCSITaskMgmtReply_t *pScsiTmReply) +@@ -2083,7 +2366,6 @@ mptscsih_taskmgmt_response_code(MPT_ADAP + printk(MYIOC_s_INFO_FMT "Response Code(0x%08x): F/W: %s\n", + ioc->name, response_code, desc); + } +-EXPORT_SYMBOL(mptscsih_taskmgmt_response_code); + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** +@@ -2119,6 +2401,8 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER * + mpt_clear_taskmgmt_in_progress_flag(ioc); + ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING; + complete(&ioc->taskmgmt_cmds.done); ++ if (ioc->bus_type == SAS) ++ ioc->schedule_target_reset(ioc); + return 1; + } + return 0; +@@ -2135,7 +2419,7 @@ mptscsih_bios_param(struct scsi_device * + int heads; + int sectors; + sector_t cylinders; +- ulong dummy; ++ ulong dummy; + + heads = 64; + sectors = 32; +@@ -2164,14 +2448,15 @@ mptscsih_bios_param(struct scsi_device * + return 0; + } + +-/* Search IOC page 3 to determine if this is hidden physical disk ++/** ++ * Search IOC page 3 to determine if this is hidden physical disk + * + */ + int + mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id) + { + struct inactive_raid_component_info *component_info; +- int i, j; ++ u8 i, j; + RaidPhysDiskPage1_t *phys_disk; + int rc = 0; + int num_paths; +@@ -2197,7 +2482,7 @@ mptscsih_is_phys_disk(MPT_ADAPTER *ioc, + ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum); + if (num_paths < 2) + continue; +- phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) + ++ phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t,Path) + + (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL); + if (!phys_disk) + continue; +@@ -2224,21 +2509,20 @@ mptscsih_is_phys_disk(MPT_ADAPTER *ioc, + kfree(phys_disk); + } + +- + /* + * Check inactive list for matching phys disks + */ + if (list_empty(&ioc->raid_data.inactive_list)) + goto out; + +- mutex_lock(&ioc->raid_data.inactive_list_mutex); ++ down(&ioc->raid_data.inactive_list_mutex); + list_for_each_entry(component_info, &ioc->raid_data.inactive_list, + list) { + if ((component_info->d.PhysDiskID == id) && + (component_info->d.PhysDiskBus == channel)) + rc = 1; + } +- mutex_unlock(&ioc->raid_data.inactive_list_mutex); ++ up(&ioc->raid_data.inactive_list_mutex); + + out: + return rc; +@@ -2249,10 +2533,10 @@ u8 + mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id) + { + struct inactive_raid_component_info *component_info; +- int i, j; ++ int i,j; + RaidPhysDiskPage1_t *phys_disk; + int rc = -ENXIO; +- int num_paths; ++ u8 num_paths; + + if (!ioc->raid_data.pIocPg3) + goto out; +@@ -2275,7 +2559,7 @@ mptscsih_raid_id_to_num(MPT_ADAPTER *ioc + ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum); + if (num_paths < 2) + continue; +- phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) + ++ phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t,Path) + + (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL); + if (!phys_disk) + continue; +@@ -2308,14 +2592,14 @@ mptscsih_raid_id_to_num(MPT_ADAPTER *ioc + if (list_empty(&ioc->raid_data.inactive_list)) + goto out; + +- mutex_lock(&ioc->raid_data.inactive_list_mutex); ++ down(&ioc->raid_data.inactive_list_mutex); + list_for_each_entry(component_info, &ioc->raid_data.inactive_list, + list) { + if ((component_info->d.PhysDiskID == id) && + (component_info->d.PhysDiskBus == channel)) + rc = component_info->d.PhysDiskNum; + } +- mutex_unlock(&ioc->raid_data.inactive_list_mutex); ++ up(&ioc->raid_data.inactive_list_mutex); + + out: + return rc; +@@ -2333,15 +2617,17 @@ mptscsih_slave_destroy(struct scsi_devic + MPT_SCSI_HOST *hd = shost_priv(host); + VirtTarget *vtarget; + VirtDevice *vdevice; +- struct scsi_target *starget; ++ struct scsi_target *starget; + + starget = scsi_target(sdev); + vtarget = starget->hostdata; ++ vtarget->num_luns--; + vdevice = sdev->hostdata; ++ if (!vdevice) ++ return; + + mptscsih_search_running_cmds(hd, vdevice); +- vtarget->num_luns--; +- mptscsih_synchronize_cache(hd, vdevice); ++ mptscsih_synchronize_cache(sdev, hd, vdevice); + kfree(vdevice); + sdev->hostdata = NULL; + } +@@ -2359,8 +2645,8 @@ int + mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) + { + MPT_SCSI_HOST *hd = shost_priv(sdev->host); +- VirtTarget *vtarget; +- struct scsi_target *starget; ++ VirtTarget *vtarget; ++ struct scsi_target *starget; + int max_depth; + int tagged; + MPT_ADAPTER *ioc = hd->ioc; +@@ -2372,15 +2658,13 @@ mptscsih_change_queue_depth(struct scsi_ + return -EOPNOTSUPP; + + if (ioc->bus_type == SPI) { +- if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)) +- max_depth = 1; +- else if (sdev->type == TYPE_DISK && +- vtarget->minSyncFactor <= MPT_ULTRA160) ++ if (sdev->type == TYPE_DISK && ++ vtarget->minSyncFactor <= MPT_ULTRA160) + max_depth = MPT_SCSI_CMD_PER_DEV_HIGH; + else + max_depth = MPT_SCSI_CMD_PER_DEV_LOW; + } else +- max_depth = ioc->sh->can_queue; ++ max_depth = ioc->sh->can_queue; + + if (!sdev->tagged_supported) + max_depth = 1; +@@ -2393,9 +2677,120 @@ mptscsih_change_queue_depth(struct scsi_ + tagged = MSG_SIMPLE_TAG; + + scsi_adjust_queue_depth(sdev, tagged, qdepth); ++ ++ if (sdev->inquiry_len > 7) ++ sdev_printk(KERN_INFO, sdev, MYIOC_s_FMT "qdepth=%d, " ++ "tagged=%d, simple=%d, ordered=%d, scsi_level=%d, " ++ "cmd_que=%d\n", ioc->name, sdev->queue_depth, ++ sdev->tagged_supported, sdev->simple_tags, ++ sdev->ordered_tags, sdev->scsi_level, ++ (sdev->inquiry[7] & 2) >> 1); ++ + return sdev->queue_depth; + } + ++#ifdef EEDP_SUPPORT ++/** ++ * _scsih_read_capacity_16 - send READ_CAPACITY_16 to target ++ * ++ */ ++static int ++_scsih_read_capacity_16(MPT_SCSI_HOST *hd, int id, int channel, u32 lun, ++ void *data, u32 length) ++{ ++ INTERNAL_CMD iocmd; ++ dma_addr_t data_dma; ++ struct read_cap_parameter *parameter_data; ++ u32 data_length; ++ MPT_ADAPTER *ioc = hd->ioc; ++ int rc; ++ int count; ++ u8 skey; ++ u8 asc; ++ u8 ascq; ++ ++ data_length = sizeof(struct read_cap_parameter); ++ parameter_data = pci_alloc_consistent(ioc->pcidev, ++ data_length, &data_dma); ++ if (!parameter_data) { ++ printk(MYIOC_s_ERR_FMT "failure at %s:%d/%s()!\n", ++ ioc->name, __FILE__, __LINE__, __func__); ++ return -1; ++ } ++ ++ iocmd.cmd = SERVICE_ACTION_IN; ++ iocmd.data_dma = data_dma; ++ iocmd.data = (u8 *)parameter_data; ++ iocmd.size = data_length; ++ iocmd.channel = channel; ++ iocmd.id = id; ++ iocmd.lun = lun; ++ ++ for (count=0; count < 4; count++) { ++ rc = mptscsih_do_cmd(hd, &iocmd); ++ ++ if(rc == MPT_SCANDV_GOOD) { ++ memcpy(data, parameter_data, ++ min_t(u32, data_length, length)); ++ break; ++ } else if(rc == MPT_SCANDV_BUSY) { ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: " ++ "fw_channel=%d fw_id=%d : device busy\n", ++ ioc->name, __FUNCTION__, channel, id)); ++ continue; ++ } else if(rc == MPT_SCANDV_DID_RESET) { ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: " ++ "fw_channel=%d fw_id=%d : did reset\n", ++ ioc->name, __FUNCTION__, channel, id)); ++ continue; ++ } else if(rc == MPT_SCANDV_SENSE) { ++ skey = ioc->internal_cmds.sense[2] & 0x0F; ++ asc = ioc->internal_cmds.sense[12]; ++ ascq = ioc->internal_cmds.sense[13]; ++ devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: " ++ "fw_channel=%d fw_id=%d : [sense_key,arc,ascq]: " ++ "[0x%02x,0x%02x,0x%02x]\n", ioc->name, ++ __FUNCTION__, channel, id, skey, asc, ascq)); ++ if( skey == UNIT_ATTENTION || ++ skey == NOT_READY || ++ skey == ILLEGAL_REQUEST ) { ++ continue; ++ } else { ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d : " ++ "tur failed due to [sense_key,asc,ascq]: " ++ "[0x%02x,0x%02x,0x%02x]\n", ioc->name, ++ __FUNCTION__, channel, id, skey, asc, ascq); ++ break; ++ } ++ } else if(rc == MPT_SCANDV_SELECTION_TIMEOUT) { ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "read capacity failed due to no device\n", ioc->name, ++ __FUNCTION__, channel, id); ++ break; ++ } else if(rc == MPT_SCANDV_SOME_ERROR) { ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "read capacity failed due to some error\n", ioc->name, ++ __FUNCTION__, channel, id); ++ break; ++ } else { ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "read capacity failed due to some error\n", ioc->name, ++ __FUNCTION__, channel, id); ++ break; ++ } ++ } ++ ++ if(count > 4 && rc != 0) { ++ printk(MYIOC_s_ERR_FMT "%s: fw_channel=%d fw_id=%d: " ++ "read capacity failed to many times\n", ioc->name, ++ __FUNCTION__, channel, id); ++ } ++ ++ pci_free_consistent(ioc->pcidev, data_length, parameter_data, data_dma); ++ return rc; ++} ++#endif ++ + /* + * OS entry point to adjust the queue_depths on a per-device basis. + * Called once per device the bus scan. Use it to force the queue_depth +@@ -2408,7 +2803,7 @@ mptscsih_slave_configure(struct scsi_dev + struct Scsi_Host *sh = sdev->host; + VirtTarget *vtarget; + VirtDevice *vdevice; +- struct scsi_target *starget; ++ struct scsi_target *starget; + MPT_SCSI_HOST *hd = shost_priv(sh); + MPT_ADAPTER *ioc = hd->ioc; + +@@ -2416,6 +2811,45 @@ mptscsih_slave_configure(struct scsi_dev + vtarget = starget->hostdata; + vdevice = sdev->hostdata; + ++#ifdef EEDP_SUPPORT ++ if ((!(vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)) && ++ (!(vdevice->vtarget->raidVolume))) { ++ ++ struct read_cap_parameter data; ++ memset(&data, 0, sizeof(struct read_cap_parameter)); ++ ++ /* ++ * check PROTECT bit ++ * ++ * NOTE: The crack monkey target mode driver doesn't ++ * set this bit(bug has been reported). ++ * The cm_target command line option is a work around. ++ */ ++ if (!(sdev->inquiry[5] & 1)) ++ goto out; ++ ++ if ((ioc->bus_type == FC) && ++ (_scsih_read_capacity_16(hd, vtarget->id, ++ vtarget->channel, sdev->lun, &data, ++ sizeof(struct read_cap_parameter)) == 0)) { ++ vdevice->eedp_enable = data.prot_en; ++ vdevice->eedp_type = data.p_type; ++ vdevice->eedp_block_length = ++ be32_to_cpu(data.logical_block_length); ++ ++ if (!vdevice->eedp_enable) ++ goto out; ++ ++ sdev_printk(KERN_INFO, sdev, "EEDP enabled: " ++ "protection_type(%d), block_length(%d)\n", ++ vdevice->eedp_type+1, ++ vdevice->eedp_block_length); ++ } ++ } ++ out: ++#endif /* EEDP Support */ ++ ++ + dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "device @ %p, channel=%d, id=%d, lun=%d\n", + ioc->name, sdev, sdev->channel, sdev->id, sdev->lun)); +@@ -2427,6 +2861,11 @@ mptscsih_slave_configure(struct scsi_dev + + vdevice->configured_lun = 1; + ++ if ((ioc->bus_type != SAS) && (sdev->id > sh->max_id)) { ++ /* error case, should never happen */ ++ scsi_adjust_queue_depth(sdev, 0, 1); ++ goto slave_configure_exit; ++ } + dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "Queue depth=%d, tflags=%x\n", + ioc->name, sdev->queue_depth, vtarget->tflags)); +@@ -2437,8 +2876,10 @@ mptscsih_slave_configure(struct scsi_dev + ioc->name, vtarget->negoFlags, vtarget->maxOffset, + vtarget->minSyncFactor)); + +- mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH, ++ mptscsih_change_queue_depth(sdev, ioc->sdev_queue_depth, + SCSI_QDEPTH_DEFAULT); ++ ++slave_configure_exit: + dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "tagged %d, simple %d, ordered %d\n", + ioc->name,sdev->tagged_supported, sdev->simple_tags, +@@ -2463,7 +2904,7 @@ mptscsih_copy_sense_data(struct scsi_cmn + VirtDevice *vdevice; + SCSIIORequest_t *pReq; + u32 sense_count = le32_to_cpu(pScsiReply->SenseCount); +- MPT_ADAPTER *ioc = hd->ioc; ++ MPT_ADAPTER *ioc = hd->ioc; + + /* Get target structure + */ +@@ -2477,8 +2918,11 @@ mptscsih_copy_sense_data(struct scsi_cmn + /* Copy the sense received into the scsi command block. */ + req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + sense_data = ((u8 *)ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC)); +- memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc)); + ++ if (sense_count > SCSI_SENSE_BUFFERSIZE) ++ sense_count = SCSI_SENSE_BUFFERSIZE; ++ ++ memcpy(sc->sense_buffer, sense_data, sense_count); + /* Log SMART data (asc = 0x5D, non-IM case only) if required. + */ + if ((ioc->events) && (ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) { +@@ -2496,12 +2940,10 @@ mptscsih_copy_sense_data(struct scsi_cmn + ioc->events[idx].data[1] = (sense_data[13] << 8) | sense_data[12]; + + ioc->eventContext++; +- if (ioc->pcidev->vendor == +- PCI_VENDOR_ID_IBM) { +- mptscsih_issue_sep_command(ioc, +- vdevice->vtarget, MPI_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); +- vdevice->vtarget->tflags |= +- MPT_TARGET_FLAGS_LED_ON; ++ if (ioc->pcidev->vendor == PCI_VENDOR_ID_IBM) { ++ mptscsih_issue_sep_command(ioc, vdevice->vtarget, ++ MPI_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); ++ vdevice->vtarget->tflags |= MPT_TARGET_FLAGS_LED_ON; + } + } + } +@@ -2512,12 +2954,16 @@ mptscsih_copy_sense_data(struct scsi_cmn + } + + /** +- * mptscsih_get_scsi_lookup - retrieves scmd entry ++ * mptscsih_get_scsi_lookup ++ * ++ * retrieves scmd entry from ScsiLookup[] array list ++ * + * @ioc: Pointer to MPT_ADAPTER structure + * @i: index into the array + * + * Returns the scsi_cmd pointer +- */ ++ * ++ **/ + struct scsi_cmnd * + mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i) + { +@@ -2530,10 +2976,12 @@ mptscsih_get_scsi_lookup(MPT_ADAPTER *io + + return scmd; + } +-EXPORT_SYMBOL(mptscsih_get_scsi_lookup); + + /** +- * mptscsih_getclear_scsi_lookup - retrieves and clears scmd entry from ScsiLookup[] array list ++ * mptscsih_getclear_scsi_lookup ++ * ++ * retrieves and clears scmd entry from ScsiLookup[] array list ++ * + * @ioc: Pointer to MPT_ADAPTER structure + * @i: index into the array + * +@@ -2604,7 +3052,7 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int + { + MPT_SCSI_HOST *hd; + +- if (ioc->sh == NULL || shost_priv(ioc->sh) == NULL) ++ if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL)) + return 0; + + hd = shost_priv(ioc->sh); +@@ -2684,7 +3132,7 @@ mptscsih_scandv_complete(MPT_ADAPTER *io + SCSIIOReply_t *pReply; + u8 cmd; + u16 req_idx; +- u8 *sense_data; ++ u8 *sense_data; + int sz; + + ioc->internal_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD; +@@ -2722,8 +3170,8 @@ mptscsih_scandv_complete(MPT_ADAPTER *io + /** + * mptscsih_get_completion_code - + * @ioc: Pointer to MPT_ADAPTER structure +- * @req: Pointer to original MPT request frame +- * @reply: Pointer to MPT reply frame (NULL if TurboReply) ++ * @reply: ++ * @cmd: + * + **/ + static int +@@ -2829,7 +3277,7 @@ mptscsih_get_completion_code(MPT_ADAPTER + * + * > 0 if command complete but some type of completion error. + */ +-static int ++int + mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io) + { + MPT_FRAME_HDR *mf; +@@ -2849,7 +3297,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER + if (ioc->ioc_reset_in_progress) { + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "%s: busy with host reset\n", ioc->name, __func__)); ++ "%s: busy with host reset\n", ioc->name, __FUNCTION__)); + return MPT_SCANDV_BUSY; + } + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); +@@ -2945,6 +3393,35 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER + timeout = 10; + break; + ++ case REPORT_LUNS: ++ cmdLen = 12; ++ dir = MPI_SCSIIO_CONTROL_READ; ++ CDB[0] = cmd; ++ CDB[6] = (io->size >> 24) & 0xFF; ++ CDB[7] = (io->size >> 16) & 0xFF; ++ CDB[8] = (io->size >> 8) & 0xFF; ++ CDB[9] = io->size & 0xFF; ++ timeout = 10; ++ break; ++ ++ case TRANSPORT_LAYER_RETRIES: ++ CDB[0] = cmd; ++ CDB[1] = 0x01; ++ cmdLen = 6; ++ dir = MPI_SCSIIO_CONTROL_READ; ++ timeout = 10; ++ break; ++#ifdef EEDP_SUPPORT ++ case SERVICE_ACTION_IN: ++ CDB[0] = cmd; ++ CDB[1] = 0x10; ++ CDB[13] = io->size & 0xFF; ++ dir = MPI_SCSIIO_CONTROL_READ; ++ timeout = 10; ++ cmdLen = 16; ++ break; ++#endif ++ + default: + /* Error Case */ + ret = -EFAULT; +@@ -3032,9 +3509,12 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER + goto out; + } + if (!timeleft) { ++ if (!mptscsih_scandv_bus_reset(ioc)) ++ goto out; + printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", + ioc->name, __func__); +- mpt_HardResetHandler(ioc, CAN_SLEEP); ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP) != 0) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_free_msg_frame(ioc, mf); + } + goto out; +@@ -3050,6 +3530,73 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER + return ret; + } + ++int ++mptscsih_quiesce_raid(MPT_SCSI_HOST *hd, int quiesce, u8 channel, u8 id) ++{ ++ MPT_ADAPTER *ioc = hd->ioc; ++ MpiRaidActionRequest_t *pReq; ++ MPT_FRAME_HDR *mf; ++ int ret; ++ unsigned long timeleft; ++ ++ mutex_lock(&ioc->internal_cmds.mutex); ++ ++ /* Get and Populate a free Frame ++ */ ++ if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) { ++ dfailprintk(hd->ioc, printk(MYIOC_s_WARN_FMT "%s: no msg frames!\n", ++ ioc->name, __FUNCTION__)); ++ ret = -EAGAIN; ++ goto out; ++ } ++ pReq = (MpiRaidActionRequest_t *)mf; ++ if (quiesce) ++ pReq->Action = MPI_RAID_ACTION_QUIESCE_PHYS_IO; ++ else ++ pReq->Action = MPI_RAID_ACTION_ENABLE_PHYS_IO; ++ pReq->Reserved1 = 0; ++ pReq->ChainOffset = 0; ++ pReq->Function = MPI_FUNCTION_RAID_ACTION; ++ pReq->VolumeID = id; ++ pReq->VolumeBus = channel; ++ pReq->PhysDiskNum = 0; ++ pReq->MsgFlags = 0; ++ pReq->Reserved2 = 0; ++ pReq->ActionDataWord = 0; /* Reserved for this action */ ++ ++ ioc->add_sge((char *)&pReq->ActionDataSGE, ++ MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1); ++ ++ ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RAID Volume action=%x channel=%d id=%d\n", ++ ioc->name, pReq->Action, channel, id)); ++ ++ INITIALIZE_MGMT_STATUS(ioc->internal_cmds.status) ++ mpt_put_msg_frame(ioc->InternalCtx, ioc, mf); ++ timeleft = wait_for_completion_timeout(&ioc->internal_cmds.done, 10*HZ); ++ if (!(ioc->internal_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ ret = -ETIME; ++ dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: TIMED OUT!\n", ++ ioc->name, __FUNCTION__)); ++ if (ioc->internal_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) ++ goto out; ++ if (!timeleft) { ++ printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", ++ ioc->name, __FUNCTION__); ++ if (mpt_SoftResetHandler(ioc, CAN_SLEEP) != 0) ++ mpt_HardResetHandler(ioc, CAN_SLEEP); ++ mpt_free_msg_frame(ioc, mf); ++ } ++ goto out; ++ } ++ ++ ret = ioc->internal_cmds.completion_code; ++ ++ out: ++ CLEAR_MGMT_STATUS(ioc->internal_cmds.status) ++ mutex_unlock(&ioc->internal_cmds.mutex); ++ return ret; ++} ++ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks. +@@ -3061,9 +3608,10 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER + * + */ + static void +-mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice) ++mptscsih_synchronize_cache(struct scsi_device *sdev, MPT_SCSI_HOST *hd, VirtDevice *vdevice) + { + INTERNAL_CMD iocmd; ++ MPT_ADAPTER *ioc = hd->ioc; + + /* Ignore hidden raid components, this is handled when the command + * is sent to the volume +@@ -3075,23 +3623,124 @@ mptscsih_synchronize_cache(MPT_SCSI_HOST + !vdevice->configured_lun) + return; + +- /* Following parameters will not change +- * in this routine. +- */ ++ memset(&iocmd, 0, sizeof(INTERNAL_CMD)); + iocmd.cmd = SYNCHRONIZE_CACHE; +- iocmd.flags = 0; + iocmd.physDiskNum = -1; + iocmd.data = NULL; + iocmd.data_dma = -1; +- iocmd.size = 0; +- iocmd.rsvd = iocmd.rsvd2 = 0; + iocmd.channel = vdevice->vtarget->channel; + iocmd.id = vdevice->vtarget->id; + iocmd.lun = vdevice->lun; + ++ sdev_printk(KERN_INFO, sdev, MYIOC_s_FMT "SYNCHRONIZE_CACHE: fw_channel %d," ++ " fw_id %d\n", ioc->name, vdevice->vtarget->channel, vdevice->vtarget->id); + mptscsih_do_cmd(hd, &iocmd); + } + ++/* ++ * shost attributes ++ */ ++static ssize_t ++mptscsih_fault_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *host = class_to_shost(dev); ++ MPT_SCSI_HOST *hd = shost_priv(host); ++ MPT_ADAPTER *ioc = hd->ioc; ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->is_fault); ++} ++static ssize_t ++mptscsih_fault_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *host = class_to_shost(dev); ++ MPT_SCSI_HOST *hd = shost_priv(host); ++ MPT_ADAPTER *ioc = hd->ioc; ++ int val = 0; ++ ++ if (sscanf(buf, "%d", &val) != 1) ++ return -EINVAL; ++ ++ ioc->is_fault = val; ++ return strlen(buf); ++ ++} ++ ++struct DIAG_BUFFER_START { ++ u32 Size; ++ u32 DiagVersion; ++ u8 BufferType; ++ u8 Reserved[3]; ++ u32 Reserved1; ++ u32 Reserved2; ++ u32 Reserved3; ++}; ++ ++static ssize_t ++mptscsih_ring_buffer_size_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *host = class_to_shost(dev); ++ MPT_SCSI_HOST *hd = shost_priv(host); ++ MPT_ADAPTER *ioc = hd->ioc; ++ u32 size = 0; ++ struct DIAG_BUFFER_START *request_data; ++ ++ ioc->ring_buffer_sz = 0; ++ if (!ioc->DiagBuffer[0]) ++ return 0; ++ ++ request_data = (struct DIAG_BUFFER_START *)ioc->DiagBuffer[0]; ++ if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 || ++ le32_to_cpu(request_data->DiagVersion) == 0x01000000) && ++ le32_to_cpu(request_data->Reserved3) == 0x4742444c) { ++ size = le32_to_cpu(request_data->Size); ++ ioc->ring_buffer_sz = size; ++ } ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", size); ++} ++ ++static ssize_t ++mptscsih_ring_buffer_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct Scsi_Host *host = class_to_shost(dev); ++ MPT_SCSI_HOST *hd = shost_priv(host); ++ MPT_ADAPTER *ioc = hd->ioc; ++ void *request_data; ++ u32 size; ++ ++ if (!ioc->DiagBuffer[0]) ++ return 0; ++ ++ if (ioc->ring_buffer_offset > ioc->ring_buffer_sz) ++ return 0; ++ ++ size = ioc->ring_buffer_sz - ioc->ring_buffer_offset; ++ size = (size > PAGE_SIZE) ? PAGE_SIZE : size; ++ request_data = ioc->DiagBuffer[0] + ioc->ring_buffer_offset; ++ memcpy(buf, request_data, size); ++ return size; ++} ++ ++static ssize_t ++mptscsih_ring_buffer_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *host = class_to_shost(dev); ++ MPT_SCSI_HOST *hd = shost_priv(host); ++ MPT_ADAPTER *ioc = hd->ioc; ++ int val = 0; ++ ++ if (sscanf(buf, "%d", &val) != 1) ++ return -EINVAL; ++ ++ ioc->ring_buffer_offset = val; ++ return strlen(buf); ++} ++ + static ssize_t + mptscsih_version_fw_show(struct device *dev, struct device_attribute *attr, + char *buf) +@@ -3106,7 +3755,6 @@ mptscsih_version_fw_show(struct device * + (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, + ioc->facts.FWVersion.Word & 0x000000FF); + } +-static DEVICE_ATTR(version_fw, S_IRUGO, mptscsih_version_fw_show, NULL); + + static ssize_t + mptscsih_version_bios_show(struct device *dev, struct device_attribute *attr, +@@ -3116,13 +3764,12 @@ mptscsih_version_bios_show(struct device + MPT_SCSI_HOST *hd = shost_priv(host); + MPT_ADAPTER *ioc = hd->ioc; + +- return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n", ++ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", + (ioc->biosVersion & 0xFF000000) >> 24, + (ioc->biosVersion & 0x00FF0000) >> 16, + (ioc->biosVersion & 0x0000FF00) >> 8, + ioc->biosVersion & 0x000000FF); + } +-static DEVICE_ATTR(version_bios, S_IRUGO, mptscsih_version_bios_show, NULL); + + static ssize_t + mptscsih_version_mpi_show(struct device *dev, struct device_attribute *attr, +@@ -3132,14 +3779,17 @@ mptscsih_version_mpi_show(struct device + MPT_SCSI_HOST *hd = shost_priv(host); + MPT_ADAPTER *ioc = hd->ioc; + +- return snprintf(buf, PAGE_SIZE, "%03x\n", ioc->facts.MsgVersion); ++ if (ioc->facts.MsgVersion >= MPI_VERSION_01_05) ++ return snprintf(buf, PAGE_SIZE, "%03x.%02x\n", ++ ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8); ++ else ++ return snprintf(buf, PAGE_SIZE, "%03x\n", ++ ioc->facts.MsgVersion); + } +-static DEVICE_ATTR(version_mpi, S_IRUGO, mptscsih_version_mpi_show, NULL); + + static ssize_t + mptscsih_version_product_show(struct device *dev, +- struct device_attribute *attr, +-char *buf) ++ struct device_attribute *attr, char *buf) + { + struct Scsi_Host *host = class_to_shost(dev); + MPT_SCSI_HOST *hd = shost_priv(host); +@@ -3147,8 +3797,6 @@ char *buf) + + return snprintf(buf, PAGE_SIZE, "%s\n", ioc->prod_name); + } +-static DEVICE_ATTR(version_product, S_IRUGO, +- mptscsih_version_product_show, NULL); + + static ssize_t + mptscsih_version_nvdata_persistent_show(struct device *dev, +@@ -3162,8 +3810,6 @@ mptscsih_version_nvdata_persistent_show( + return snprintf(buf, PAGE_SIZE, "%02xh\n", + ioc->nvdata_version_persistent); + } +-static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, +- mptscsih_version_nvdata_persistent_show, NULL); + + static ssize_t + mptscsih_version_nvdata_default_show(struct device *dev, +@@ -3175,8 +3821,6 @@ mptscsih_version_nvdata_default_show(str + + return snprintf(buf, PAGE_SIZE, "%02xh\n",ioc->nvdata_version_default); + } +-static DEVICE_ATTR(version_nvdata_default, S_IRUGO, +- mptscsih_version_nvdata_default_show, NULL); + + static ssize_t + mptscsih_board_name_show(struct device *dev, struct device_attribute *attr, +@@ -3188,11 +3832,10 @@ mptscsih_board_name_show(struct device * + + return snprintf(buf, PAGE_SIZE, "%s\n", ioc->board_name); + } +-static DEVICE_ATTR(board_name, S_IRUGO, mptscsih_board_name_show, NULL); + + static ssize_t +-mptscsih_board_assembly_show(struct device *dev, +- struct device_attribute *attr, char *buf) ++mptscsih_board_assembly_show(struct device *dev, struct device_attribute *attr, ++ char *buf) + { + struct Scsi_Host *host = class_to_shost(dev); + MPT_SCSI_HOST *hd = shost_priv(host); +@@ -3200,8 +3843,6 @@ mptscsih_board_assembly_show(struct devi + + return snprintf(buf, PAGE_SIZE, "%s\n", ioc->board_assembly); + } +-static DEVICE_ATTR(board_assembly, S_IRUGO, +- mptscsih_board_assembly_show, NULL); + + static ssize_t + mptscsih_board_tracer_show(struct device *dev, struct device_attribute *attr, +@@ -3213,8 +3854,6 @@ mptscsih_board_tracer_show(struct device + + return snprintf(buf, PAGE_SIZE, "%s\n", ioc->board_tracer); + } +-static DEVICE_ATTR(board_tracer, S_IRUGO, +- mptscsih_board_tracer_show, NULL); + + static ssize_t + mptscsih_io_delay_show(struct device *dev, struct device_attribute *attr, +@@ -3226,8 +3865,6 @@ mptscsih_io_delay_show(struct device *de + + return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay); + } +-static DEVICE_ATTR(io_delay, S_IRUGO, +- mptscsih_io_delay_show, NULL); + + static ssize_t + mptscsih_device_delay_show(struct device *dev, struct device_attribute *attr, +@@ -3239,8 +3876,6 @@ mptscsih_device_delay_show(struct device + + return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay); + } +-static DEVICE_ATTR(device_delay, S_IRUGO, +- mptscsih_device_delay_show, NULL); + + static ssize_t + mptscsih_debug_level_show(struct device *dev, struct device_attribute *attr, +@@ -3252,6 +3887,7 @@ mptscsih_debug_level_show(struct device + + return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->debug_level); + } ++ + static ssize_t + mptscsih_debug_level_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +@@ -3265,14 +3901,78 @@ mptscsih_debug_level_store(struct device + return -EINVAL; + + ioc->debug_level = val; +- printk(MYIOC_s_INFO_FMT "debug_level=%08xh\n", +- ioc->name, ioc->debug_level); ++ printk(MYIOC_s_INFO_FMT "debug_level=%08xh\n", ioc->name, ++ ioc->debug_level); ++ return strlen(buf); ++} ++ ++static ssize_t ++mptscsih_disable_hotplug_remove_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct Scsi_Host *host = class_to_shost(dev); ++ MPT_SCSI_HOST *hd = shost_priv(host); ++ MPT_ADAPTER *ioc = hd->ioc; ++ ++ return snprintf(buf, PAGE_SIZE, "%02xh\n", ioc->disable_hotplug_remove); ++} ++static ssize_t ++mptscsih_disable_hotplug_remove_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct Scsi_Host *host = class_to_shost(dev); ++ MPT_SCSI_HOST *hd = shost_priv(host); ++ MPT_ADAPTER *ioc = hd->ioc; ++ int val = 0; ++ ++ if (sscanf(buf, "%x", &val) != 1) ++ return -EINVAL; ++ ++ ioc->disable_hotplug_remove = val; ++ if (ioc->disable_hotplug_remove) ++ printk(MYIOC_s_INFO_FMT "disabling hotplug remove\n", ++ ioc->name); ++ else ++ printk(MYIOC_s_INFO_FMT "eanbling hotplug remove\n", ioc->name); + return strlen(buf); + } ++ ++static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, ++ mptscsih_fault_show, mptscsih_fault_store); ++static DEVICE_ATTR(ring_buffer_size, S_IRUGO, ++ mptscsih_ring_buffer_size_show, NULL); ++static DEVICE_ATTR(ring_buffer, S_IRUGO | S_IWUSR, ++ mptscsih_ring_buffer_show, mptscsih_ring_buffer_store); ++static DEVICE_ATTR(version_fw, S_IRUGO, mptscsih_version_fw_show, NULL); ++static DEVICE_ATTR(version_bios, S_IRUGO, ++ mptscsih_version_bios_show, NULL); ++static DEVICE_ATTR(version_mpi, S_IRUGO, mptscsih_version_mpi_show, NULL); ++static DEVICE_ATTR(version_product, S_IRUGO, ++ mptscsih_version_product_show, NULL); ++static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, ++ mptscsih_version_nvdata_persistent_show, NULL); ++static DEVICE_ATTR(version_nvdata_default, S_IRUGO, ++ mptscsih_version_nvdata_default_show, NULL); ++static DEVICE_ATTR(board_name, S_IRUGO, mptscsih_board_name_show, NULL); ++static DEVICE_ATTR(board_assembly, S_IRUGO, ++ mptscsih_board_assembly_show, NULL); ++static DEVICE_ATTR(board_tracer, S_IRUGO, ++ mptscsih_board_tracer_show, NULL); ++static DEVICE_ATTR(io_delay, S_IRUGO, ++ mptscsih_io_delay_show, NULL); ++static DEVICE_ATTR(device_delay, S_IRUGO, ++ mptscsih_device_delay_show, NULL); + static DEVICE_ATTR(debug_level, S_IRUGO | S_IWUSR, +- mptscsih_debug_level_show, mptscsih_debug_level_store); ++ mptscsih_debug_level_show, mptscsih_debug_level_store); ++static DEVICE_ATTR(disable_hotplug_remove, S_IRUGO | S_IWUSR, ++ mptscsih_disable_hotplug_remove_show, mptscsih_disable_hotplug_remove_store); + + struct device_attribute *mptscsih_host_attrs[] = { ++ &dev_attr_fault, ++ &dev_attr_ring_buffer_size, ++ &dev_attr_ring_buffer, + &dev_attr_version_fw, + &dev_attr_version_bios, + &dev_attr_version_mpi, +@@ -3285,6 +3985,7 @@ struct device_attribute *mptscsih_host_a + &dev_attr_io_delay, + &dev_attr_device_delay, + &dev_attr_debug_level, ++ &dev_attr_disable_hotplug_remove, + NULL, + }; + +@@ -3312,5 +4013,9 @@ EXPORT_SYMBOL(mptscsih_scandv_complete); + EXPORT_SYMBOL(mptscsih_event_process); + EXPORT_SYMBOL(mptscsih_ioc_reset); + EXPORT_SYMBOL(mptscsih_change_queue_depth); +- ++EXPORT_SYMBOL(mptscsih_IssueTaskMgmt); ++EXPORT_SYMBOL(mptscsih_do_cmd); ++EXPORT_SYMBOL(mptscsih_quiesce_raid); ++EXPORT_SYMBOL(mptscsih_get_scsi_lookup); ++EXPORT_SYMBOL(mptscsih_taskmgmt_response_code); + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +--- a/drivers/message/fusion/mptscsih.h ++++ b/drivers/message/fusion/mptscsih.h +@@ -85,12 +85,14 @@ + #define MPTSCSIH_DOMAIN_VALIDATION 1 + #define MPTSCSIH_MAX_WIDTH 1 + #define MPTSCSIH_MIN_SYNC 0x08 ++#define MPTSCSIH_QAS 1 + #define MPTSCSIH_SAF_TE 0 + #define MPTSCSIH_PT_CLEAR 0 + ++#define TRANSPORT_LAYER_RETRIES 0xC2 + #endif + +- ++#define mpt_sg_next(sg) sg_next(sg) + typedef struct _internal_cmd { + char *data; /* data pointer */ + dma_addr_t data_dma; /* data dma address */ +@@ -114,9 +116,7 @@ extern int mptscsih_resume(struct pci_de + extern int mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int func); + extern const char * mptscsih_info(struct Scsi_Host *SChost); + extern int mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)); +-extern int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, +- u8 id, int lun, int ctx2abort, ulong timeout); +-extern void mptscsih_slave_destroy(struct scsi_device *device); ++extern void mptscsih_slave_destroy(struct scsi_device *sdev); + extern int mptscsih_slave_configure(struct scsi_device *device); + extern int mptscsih_abort(struct scsi_cmnd * SCpnt); + extern int mptscsih_dev_reset(struct scsi_cmnd * SCpnt); +@@ -130,8 +130,11 @@ extern int mptscsih_event_process(MPT_AD + extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset); + extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, + int reason); ++extern int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout); + extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id); + extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id); ++extern int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd); + extern struct device_attribute *mptscsih_host_attrs[]; +-extern struct scsi_cmnd *mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i); ++extern int mptscsih_quiesce_raid(MPT_SCSI_HOST *hd, int quiesce, u8 channel, u8 id); ++extern struct scsi_cmnd * mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i); + extern void mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code); +--- a/drivers/message/fusion/mptspi.c ++++ b/drivers/message/fusion/mptspi.c +@@ -43,7 +43,7 @@ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +- ++#include + #include + #include + #include +@@ -53,8 +53,10 @@ + #include /* for mdelay */ + #include /* needed for in_interrupt() proto */ + #include /* notifier code */ ++#include + #include + #include ++#include + + #include + #include +@@ -83,6 +85,10 @@ static int mpt_saf_te = MPTSCSIH_SAF_TE; + module_param(mpt_saf_te, int, 0); + MODULE_PARM_DESC(mpt_saf_te, " Force enabling SEP Processor: enable=1 (default=MPTSCSIH_SAF_TE=0)"); + ++static int mpt_qas = MPTSCSIH_QAS; ++module_param(mpt_qas, int, 1); ++MODULE_PARM_DESC(mpt_qas, " Quick Arbitration and Selection (QAS) enabled=1, disabled=0 (default=MPTSCSIH_QAS=1)"); ++ + static void mptspi_write_offset(struct scsi_target *, int); + static void mptspi_write_width(struct scsi_target *, int); + static int mptspi_write_spi_device_pg1(struct scsi_target *, +@@ -95,12 +101,12 @@ static u8 mptspiTaskCtx = MPT_MAX_PROTOC + static u8 mptspiInternalCtx = MPT_MAX_PROTOCOL_DRIVERS; /* Used only for internal commands */ + + /** +- * mptspi_setTargetNegoParms - Update the target negotiation parameters ++ * mptspi_setTargetNegoParms - Update the target negotiation parameters + * @hd: Pointer to a SCSI Host Structure + * @target: per target private data + * @sdev: SCSI device + * +- * Update the target negotiation parameters based on the the Inquiry ++ * Update the target negotiation parameters based on the the Inquiry + * data, adapter capabilities, and NVRAM settings. + **/ + static void +@@ -131,7 +137,7 @@ mptspi_setTargetNegoParms(MPT_SCSI_HOST + if (scsi_device_sync(sdev)) { + factor = pspi_data->minSyncFactor; + if (!scsi_device_dt(sdev)) +- factor = MPT_ULTRA2; ++ factor = MPT_ULTRA2; + else { + if (!scsi_device_ius(sdev) && + !scsi_device_qas(sdev)) +@@ -209,6 +215,10 @@ mptspi_setTargetNegoParms(MPT_SCSI_HOST + target->maxOffset = offset; + target->maxWidth = width; + ++ spi_min_period(scsi_target(sdev)) = factor; ++ spi_max_offset(scsi_target(sdev)) = offset; ++ spi_max_width(scsi_target(sdev)) = width; ++ + target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO; + + /* Disable unused features. +@@ -230,7 +240,7 @@ mptspi_setTargetNegoParms(MPT_SCSI_HOST + */ + + ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "Disabling QAS due to noQas=%02x on id=%d!\n", ioc->name, noQas, id)); ++ "Disabling QAS due to noQas=%02x on id=%d!\n", ioc->name, noQas, id)); + } + } + +@@ -262,7 +272,7 @@ mptspi_writeIOCPage4(MPT_SCSI_HOST *hd, + */ + if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) { + dfailprintk(ioc, printk(MYIOC_s_WARN_FMT +- "writeIOCPage4 : no msg frames!\n",ioc->name)); ++ "writeIOCPage4 : no msg frames!\n", ioc->name)); + return -EAGAIN; + } + +@@ -304,7 +314,7 @@ mptspi_writeIOCPage4(MPT_SCSI_HOST *hd, + + ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n", +- ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, id, channel)); ++ ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, id, channel)); + + mpt_put_msg_frame(ioc->DoneCtx, ioc, mf); + +@@ -371,7 +381,7 @@ mptspi_initTarget(MPT_SCSI_HOST *hd, Vir + * non-zero = true + * zero = false + * +- */ ++ **/ + static int + mptspi_is_raid(struct _MPT_SCSI_HOST *hd, u32 id) + { +@@ -399,12 +409,11 @@ static int mptspi_target_alloc(struct sc + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct _MPT_SCSI_HOST *hd = shost_priv(shost); + VirtTarget *vtarget; +- MPT_ADAPTER *ioc; ++ MPT_ADAPTER *ioc = hd->ioc; + + if (hd == NULL) + return -ENODEV; + +- ioc = hd->ioc; + vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL); + if (!vtarget) + return -ENOMEM; +@@ -471,9 +480,12 @@ mptspi_target_destroy(struct scsi_target + static void + mptspi_print_write_nego(struct _MPT_SCSI_HOST *hd, struct scsi_target *starget, u32 ii) + { +- ddvprintk(hd->ioc, printk(MYIOC_s_DEBUG_FMT "id=%d Requested = 0x%08x" ++ if (!(hd->ioc->debug_level & MPT_DEBUG_DV)) ++ return; ++ ++ starget_printk(KERN_DEBUG, starget, MYIOC_s_FMT "Wrote = 0x%08x" + " ( %s factor = 0x%02x @ offset = 0x%02x %s%s%s%s%s%s%s%s)\n", +- hd->ioc->name, starget->id, ii, ++ hd->ioc->name, ii, + ii & MPI_SCSIDEVPAGE0_NP_WIDE ? "Wide ": "", + ((ii >> 8) & 0xFF), ((ii >> 16) & 0xFF), + ii & MPI_SCSIDEVPAGE0_NP_IU ? "IU ": "", +@@ -483,7 +495,7 @@ mptspi_print_write_nego(struct _MPT_SCSI + ii & MPI_SCSIDEVPAGE0_NP_WR_FLOW ? "WRFLOW ": "", + ii & MPI_SCSIDEVPAGE0_NP_RD_STRM ? "RDSTRM ": "", + ii & MPI_SCSIDEVPAGE0_NP_RTI ? "RTI ": "", +- ii & MPI_SCSIDEVPAGE0_NP_PCOMP_EN ? "PCOMP ": "")); ++ ii & MPI_SCSIDEVPAGE0_NP_PCOMP_EN ? "PCOMP ": ""); + } + + /** +@@ -496,9 +508,12 @@ mptspi_print_write_nego(struct _MPT_SCSI + static void + mptspi_print_read_nego(struct _MPT_SCSI_HOST *hd, struct scsi_target *starget, u32 ii) + { +- ddvprintk(hd->ioc, printk(MYIOC_s_DEBUG_FMT "id=%d Read = 0x%08x" ++ if (!(hd->ioc->debug_level & MPT_DEBUG_DV)) ++ return; ++ ++ starget_printk(KERN_DEBUG, starget, MYIOC_s_FMT "Read = 0x%08x" + " ( %s factor = 0x%02x @ offset = 0x%02x %s%s%s%s%s%s%s%s)\n", +- hd->ioc->name, starget->id, ii, ++ hd->ioc->name, ii, + ii & MPI_SCSIDEVPAGE0_NP_WIDE ? "Wide ": "", + ((ii >> 8) & 0xFF), ((ii >> 16) & 0xFF), + ii & MPI_SCSIDEVPAGE0_NP_IU ? "IU ": "", +@@ -508,7 +523,7 @@ mptspi_print_read_nego(struct _MPT_SCSI_ + ii & MPI_SCSIDEVPAGE0_NP_WR_FLOW ? "WRFLOW ": "", + ii & MPI_SCSIDEVPAGE0_NP_RD_STRM ? "RDSTRM ": "", + ii & MPI_SCSIDEVPAGE0_NP_RTI ? "RTI ": "", +- ii & MPI_SCSIDEVPAGE0_NP_PCOMP_EN ? "PCOMP ": "")); ++ ii & MPI_SCSIDEVPAGE0_NP_PCOMP_EN ? "PCOMP ": ""); + } + + static int mptspi_read_spi_device_pg0(struct scsi_target *starget, +@@ -557,9 +572,11 @@ static int mptspi_read_spi_device_pg0(st + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + cfg.dir = 0; + cfg.pageAddr = starget->id; ++ cfg.timeout = 60; + + if (mpt_config(ioc, &cfg)) { +- starget_printk(KERN_ERR, starget, MYIOC_s_FMT "mpt_config failed\n", ioc->name); ++ starget_printk(KERN_ERR, starget, ++ MYIOC_s_FMT "mpt_config failed\n", ioc->name); + goto out_free; + } + err = 0; +@@ -614,76 +631,11 @@ static void mptspi_read_parameters(struc + spi_width(starget) = (nego & MPI_SCSIDEVPAGE0_NP_WIDE) ? 1 : 0; + } + +-int +-mptscsih_quiesce_raid(MPT_SCSI_HOST *hd, int quiesce, u8 channel, u8 id) +-{ +- MPT_ADAPTER *ioc = hd->ioc; +- MpiRaidActionRequest_t *pReq; +- MPT_FRAME_HDR *mf; +- int ret; +- unsigned long timeleft; +- +- mutex_lock(&ioc->internal_cmds.mutex); +- +- /* Get and Populate a free Frame +- */ +- if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) { +- dfailprintk(hd->ioc, printk(MYIOC_s_WARN_FMT +- "%s: no msg frames!\n", ioc->name, __func__)); +- ret = -EAGAIN; +- goto out; +- } +- pReq = (MpiRaidActionRequest_t *)mf; +- if (quiesce) +- pReq->Action = MPI_RAID_ACTION_QUIESCE_PHYS_IO; +- else +- pReq->Action = MPI_RAID_ACTION_ENABLE_PHYS_IO; +- pReq->Reserved1 = 0; +- pReq->ChainOffset = 0; +- pReq->Function = MPI_FUNCTION_RAID_ACTION; +- pReq->VolumeID = id; +- pReq->VolumeBus = channel; +- pReq->PhysDiskNum = 0; +- pReq->MsgFlags = 0; +- pReq->Reserved2 = 0; +- pReq->ActionDataWord = 0; /* Reserved for this action */ +- +- ioc->add_sge((char *)&pReq->ActionDataSGE, +- MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1); +- +- ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RAID Volume action=%x channel=%d id=%d\n", +- ioc->name, pReq->Action, channel, id)); +- +- INITIALIZE_MGMT_STATUS(ioc->internal_cmds.status) +- mpt_put_msg_frame(ioc->InternalCtx, ioc, mf); +- timeleft = wait_for_completion_timeout(&ioc->internal_cmds.done, 10*HZ); +- if (!(ioc->internal_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { +- ret = -ETIME; +- dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: TIMED OUT!\n", +- ioc->name, __func__)); +- if (ioc->internal_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) +- goto out; +- if (!timeleft) { +- printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", +- ioc->name, __func__); +- mpt_HardResetHandler(ioc, CAN_SLEEP); +- mpt_free_msg_frame(ioc, mf); +- } +- goto out; +- } +- +- ret = ioc->internal_cmds.completion_code; +- +- out: +- CLEAR_MGMT_STATUS(ioc->internal_cmds.status) +- mutex_unlock(&ioc->internal_cmds.mutex); +- return ret; +-} +- + static void mptspi_dv_device(struct _MPT_SCSI_HOST *hd, + struct scsi_device *sdev) + { + VirtTarget *vtarget = scsi_target(sdev)->hostdata; ++ struct scsi_target *starget = scsi_target(sdev); + MPT_ADAPTER *ioc = hd->ioc; + + /* no DV on RAID devices */ +@@ -691,11 +643,20 @@ static void mptspi_dv_device(struct _MPT + mptspi_is_raid(hd, sdev->id)) + return; + ++ if (ioc->debug_level & MPT_DEBUG_DV) ++ starget_printk(KERN_DEBUG, starget, MYIOC_s_FMT ++ "sdtr=%d, wdtr=%d, ppr=%d, min_period=0x%02x, " ++ "max_offset=0x%02x, max_width=%d, nego_flags=0x%02x, " ++ "tflags=0x%02x\n", ioc->name, sdev->sdtr, sdev->wdtr, ++ sdev->ppr, spi_min_period(starget), ++ spi_max_offset(starget), spi_max_width(starget), ++ vtarget->negoFlags, vtarget->tflags); ++ + /* If this is a piece of a RAID, then quiesce first */ + if (sdev->channel == 1 && + mptscsih_quiesce_raid(hd, 1, vtarget->channel, vtarget->id) < 0) { +- starget_printk(KERN_ERR, scsi_target(sdev), MYIOC_s_FMT +- "Integrated RAID quiesce failed\n", ioc->name); ++ starget_printk(KERN_ERR, scsi_target(sdev), ++ MYIOC_s_FMT "Integrated RAID quiesce failed\n", ioc->name); + return; + } + +@@ -705,8 +666,8 @@ static void mptspi_dv_device(struct _MPT + + if (sdev->channel == 1 && + mptscsih_quiesce_raid(hd, 0, vtarget->channel, vtarget->id) < 0) +- starget_printk(KERN_ERR, scsi_target(sdev), MYIOC_s_FMT +- "Integrated RAID resume failed\n", ioc->name); ++ starget_printk(KERN_ERR, scsi_target(sdev), ++ MYIOC_s_FMT "Integrated RAID resume failed\n", ioc->name); + + mptspi_read_parameters(sdev->sdev_target); + spi_display_xfer_agreement(sdev->sdev_target); +@@ -728,7 +689,7 @@ static int mptspi_slave_alloc(struct scs + vdevice = kzalloc(sizeof(VirtDevice), GFP_KERNEL); + if (!vdevice) { + printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", +- ioc->name, sizeof(VirtDevice)); ++ ioc->name, sizeof(VirtDevice)); + return -ENOMEM; + } + +@@ -750,21 +711,13 @@ static int mptspi_slave_configure(struct + { + struct _MPT_SCSI_HOST *hd = shost_priv(sdev->host); + VirtTarget *vtarget = scsi_target(sdev)->hostdata; +- int ret; ++ int ret; + + mptspi_initTarget(hd, vtarget, sdev); +- + ret = mptscsih_slave_configure(sdev); +- + if (ret) + return ret; + +- ddvprintk(hd->ioc, printk(MYIOC_s_DEBUG_FMT "id=%d min_period=0x%02x" +- " max_offset=0x%02x max_width=%d\n", hd->ioc->name, +- sdev->id, spi_min_period(scsi_target(sdev)), +- spi_max_offset(scsi_target(sdev)), +- spi_max_width(scsi_target(sdev)))); +- + if ((sdev->channel == 1 || + !(mptspi_is_raid(hd, sdev->id))) && + !spi_initial_dv(sdev->sdev_target)) +@@ -869,8 +822,8 @@ static int mptspi_write_spi_device_pg1(s + + pg1 = dma_alloc_coherent(&ioc->pcidev->dev, size, &pg1_dma, GFP_KERNEL); + if (pg1 == NULL) { +- starget_printk(KERN_ERR, starget, MYIOC_s_FMT +- "dma_alloc_coherent for parameters failed\n", ioc->name); ++ starget_printk(KERN_ERR, starget, ++ MYIOC_s_FMT "dma_alloc_coherent for parameters failed\n", ioc->name); + return -EINVAL; + } + +@@ -899,8 +852,8 @@ static int mptspi_write_spi_device_pg1(s + mptspi_print_write_nego(hd, starget, le32_to_cpu(pg1->RequestedParameters)); + + if (mpt_config(ioc, &cfg)) { +- starget_printk(KERN_ERR, starget, MYIOC_s_FMT +- "mpt_config failed\n", ioc->name); ++ starget_printk(KERN_ERR, starget, ++ MYIOC_s_FMT "mpt_config failed\n", ioc->name); + goto out_free; + } + err = 0; +@@ -975,14 +928,15 @@ static void mptspi_write_dt(struct scsi_ + if (spi_period(starget) == -1) + mptspi_read_parameters(starget); + +- if (!dt && spi_period(starget) < 10) +- spi_period(starget) = 10; ++ if (!dt) { ++ spi_qas(starget) = 0; ++ spi_iu(starget) = 0; ++ } + + spi_dt(starget) = dt; + + nego = mptspi_getRP(starget); + +- + pg1.RequestedParameters = cpu_to_le32(nego); + pg1.Reserved = 0; + pg1.Configuration = 0; +@@ -998,9 +952,6 @@ static void mptspi_write_iu(struct scsi_ + if (spi_period(starget) == -1) + mptspi_read_parameters(starget); + +- if (!iu && spi_period(starget) < 9) +- spi_period(starget) = 9; +- + spi_iu(starget) = iu; + + nego = mptspi_getRP(starget); +@@ -1042,9 +993,11 @@ static void mptspi_write_qas(struct scsi + struct _MPT_SCSI_HOST *hd = shost_priv(shost); + VirtTarget *vtarget = starget->hostdata; + u32 nego; ++ MPT_ADAPTER *ioc = hd->ioc; + +- if ((vtarget->negoFlags & MPT_TARGET_NO_NEGO_QAS) || +- hd->ioc->spi_data.noQas) ++ if (!mpt_qas || ++ (vtarget->negoFlags & MPT_TARGET_NO_NEGO_QAS) || ++ ioc->spi_data.noQas) + spi_qas(starget) = 0; + else + spi_qas(starget) = qas; +@@ -1065,8 +1018,8 @@ static void mptspi_write_width(struct sc + + if (!width) { + spi_dt(starget) = 0; +- if (spi_period(starget) < 10) +- spi_period(starget) = 10; ++ spi_qas(starget) = 0; ++ spi_iu(starget) = 0; + } + + spi_width(starget) = width; +@@ -1086,10 +1039,17 @@ struct work_queue_wrapper { + int disk; + }; + +-static void mpt_work_wrapper(struct work_struct *work) ++static void ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) ++mpt_work_wrapper(struct work_struct *work) + { + struct work_queue_wrapper *wqw = + container_of(work, struct work_queue_wrapper, work); ++#else ++mpt_work_wrapper(void *data) ++{ ++ struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data; ++#endif + struct _MPT_SCSI_HOST *hd = wqw->hd; + MPT_ADAPTER *ioc = hd->ioc; + struct Scsi_Host *shost = ioc->sh; +@@ -1117,12 +1077,12 @@ static void mpt_work_wrapper(struct work + if(vtarget->id != disk) + continue; + +- starget_printk(KERN_INFO, vtarget->starget, MYIOC_s_FMT +- "Integrated RAID requests DV of new device\n", ioc->name); ++ starget_printk(KERN_INFO, vtarget->starget, ++ MYIOC_s_FMT "Integrated RAID requests DV of new device\n", ioc->name); + mptspi_dv_device(hd, sdev); + } +- shost_printk(KERN_INFO, shost, MYIOC_s_FMT +- "Integrated RAID detects new device %d\n", ioc->name, disk); ++ shost_printk(KERN_INFO, shost, ++ MYIOC_s_FMT "Integrated RAID detects new device %d\n", ioc->name, disk); + scsi_scan_target(&ioc->sh->shost_gendev, 1, disk, 0, 1); + } + +@@ -1133,12 +1093,16 @@ static void mpt_dv_raid(struct _MPT_SCSI + MPT_ADAPTER *ioc = hd->ioc; + + if (!wqw) { +- shost_printk(KERN_ERR, ioc->sh, MYIOC_s_FMT +- "Failed to act on RAID event for physical disk %d\n", ++ shost_printk(KERN_ERR, ioc->sh, ++ MYIOC_s_FMT "Failed to act on RAID event for physical disk %d\n", + ioc->name, disk); + return; + } ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) + INIT_WORK(&wqw->work, mpt_work_wrapper); ++#else ++ INIT_WORK(&wqw->work, mpt_work_wrapper, wqw); ++#endif + wqw->hd = hd; + wqw->disk = disk; + +@@ -1151,6 +1115,9 @@ mptspi_event_process(MPT_ADAPTER *ioc, E + u8 event = le32_to_cpu(pEvReply->Event) & 0xFF; + struct _MPT_SCSI_HOST *hd = shost_priv(ioc->sh); + ++ if (ioc->bus_type != SPI) ++ return 0; ++ + if (hd && event == MPI_EVENT_INTEGRATED_RAID) { + int reason + = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16; +@@ -1225,14 +1192,20 @@ static struct pci_device_id mptspi_pci_t + MODULE_DEVICE_TABLE(pci, mptspi_pci_table); + + +-/* ++/** + * renegotiate for a given target +- */ ++ **/ + static void ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) + mptspi_dv_renegotiate_work(struct work_struct *work) + { + struct work_queue_wrapper *wqw = + container_of(work, struct work_queue_wrapper, work); ++#else ++mptspi_dv_renegotiate_work(void *data) ++{ ++ struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data; ++#endif + struct _MPT_SCSI_HOST *hd = wqw->hd; + struct scsi_device *sdev; + struct scsi_target *starget; +@@ -1267,38 +1240,43 @@ mptspi_dv_renegotiate(struct _MPT_SCSI_H + if (!wqw) + return; + ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) + INIT_WORK(&wqw->work, mptspi_dv_renegotiate_work); ++#else ++ INIT_WORK(&wqw->work, mptspi_dv_renegotiate_work, wqw); ++#endif + wqw->hd = hd; + + schedule_work(&wqw->work); + } + +-/* ++/** + * spi module reset handler +- */ ++ **/ + static int + mptspi_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) + { ++ struct _MPT_SCSI_HOST *hd = NULL; + int rc; + + rc = mptscsih_ioc_reset(ioc, reset_phase); ++ if ((ioc->bus_type != SPI) || (!rc)) ++ goto out; + +- /* only try to do a renegotiation if we're properly set up +- * if we get an ioc fault on bringup, ioc->sh will be NULL */ +- if (reset_phase == MPT_IOC_POST_RESET && +- ioc->sh) { +- struct _MPT_SCSI_HOST *hd = shost_priv(ioc->sh); ++ hd = shost_priv(ioc->sh); ++ if (!hd->ioc) ++ goto out; + ++ if (ioc->active && reset_phase == MPT_IOC_POST_RESET) + mptspi_dv_renegotiate(hd); +- } +- ++ out: + return rc; + } + + #ifdef CONFIG_PM +-/* ++/** + * spi module resume handler +- */ ++ **/ + static int + mptspi_resume(struct pci_dev *pdev) + { +@@ -1315,13 +1293,13 @@ mptspi_resume(struct pci_dev *pdev) + + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +-/* ++/** + * mptspi_probe - Installs scsi devices per bus. + * @pdev: Pointer to pci_dev structure + * + * Returns 0 for success, non-zero for failure. + * +- */ ++ **/ + static int + mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id) + { +@@ -1446,6 +1424,7 @@ mptspi_probe(struct pci_dev *pdev, const + (ioc->req_sz - 64) / ioc->SGE_size; + } + ++ + if (numSGE < sh->sg_tablesize) { + /* Reset this value */ + dprintk(ioc, printk(MYIOC_s_DEBUG_FMT +@@ -1454,11 +1433,11 @@ mptspi_probe(struct pci_dev *pdev, const + sh->sg_tablesize = numSGE; + } + +- spin_unlock_irqrestore(&ioc->FreeQlock, flags); +- + hd = shost_priv(sh); + hd->ioc = ioc; + ++ spin_unlock_irqrestore(&ioc->FreeQlock, flags); ++ + /* SCSI needs scsi_cmnd lookup table! + * (with size equal to req_depth*PtrSz!) + */ +@@ -1472,12 +1451,11 @@ mptspi_probe(struct pci_dev *pdev, const + dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n", + ioc->name, ioc->ScsiLookup)); + ++ ioc->sdev_queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH; + ioc->spi_data.Saf_Te = mpt_saf_te; + ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT +- "saf_te %x\n", +- ioc->name, +- mpt_saf_te)); +- ioc->spi_data.noQas = 0; ++ "saf_te %x\n", ioc->name, mpt_saf_te)); ++ ioc->spi_data.noQas = mpt_qas ? 0 : MPT_TARGET_NO_NEGO_QAS; + + hd->last_queue_full = 0; + hd->spi_pending = 0; +@@ -1528,7 +1506,7 @@ static struct pci_driver mptspi_driver = + * mptspi_init - Register MPT adapter(s) as SCSI host(s) with SCSI mid-layer. + * + * Returns 0 for success, non-zero for failure. +- */ ++ **/ + static int __init + mptspi_init(void) + { +@@ -1558,7 +1536,8 @@ mptspi_init(void) + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + /** + * mptspi_exit - Unregisters MPT adapter(s) +- */ ++ * ++ **/ + static void __exit + mptspi_exit(void) + { +@@ -1566,7 +1545,6 @@ mptspi_exit(void) + + mpt_reset_deregister(mptspiDoneCtx); + mpt_event_deregister(mptspiDoneCtx); +- + mpt_deregister(mptspiInternalCtx); + mpt_deregister(mptspiTaskCtx); + mpt_deregister(mptspiDoneCtx); +--- /dev/null ++++ b/drivers/message/fusion/rejected_ioctls/diag_buffer.c +@@ -0,0 +1,671 @@ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++/* REGISTER DIAG BUFFER Routine. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -EBUSY if previous command timout and IOC reset is not complete. ++ * -ENODEV if no such device/adapter ++ * -ETIME if timer expires ++ * -ENOMEM if memory allocation error ++ */ ++static int ++mptctl_register_diag_buffer (unsigned long arg) ++{ ++ mpt_diag_register_t __user *uarg = (void __user *) arg; ++ mpt_diag_register_t karg; ++ MPT_ADAPTER *ioc; ++ int iocnum, rc, ii; ++ void * request_data; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz; ++ MPT_FRAME_HDR *mf; ++ DiagBufferPostRequest_t *diag_buffer_post_request; ++ DiagBufferPostReply_t *diag_buffer_post_reply; ++ u32 tmp; ++ u8 buffer_type; ++ unsigned long timeleft; ++ ++ rc = 0; ++ if (copy_from_user(&karg, uarg, sizeof(mpt_diag_register_t))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in mpt_diag_register_t struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || ++ (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s enter.\n", ioc->name, ++ __FUNCTION__)); ++ buffer_type = karg.data.BufferType; ++ if (!(ioc->facts.IOCCapabilities & MPT_DIAG_CAPABILITY(buffer_type))) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have Capability for " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type); ++ return -ENODEV; ++ } ++ ++ if (ioc->DiagBuffer_Status[buffer_type] & ++ MPT_DIAG_BUFFER_IS_REGISTERED) { ++ printk(MYIOC_s_DEBUG_FMT "%s: already has a Registered " ++ "buffer for buffer_type=%x\n", ioc->name, __FUNCTION__, ++ buffer_type); ++ return -EFAULT; ++ } ++ ++ /* Get a free request frame and save the message context. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) ++ return -EAGAIN; ++ ++ request_data = ioc->DiagBuffer[buffer_type]; ++ request_data_sz = karg.data.RequestedBufferSize; ++ ++ if (request_data) { ++ request_data_dma = ioc->DiagBuffer_dma[buffer_type]; ++ if (request_data_sz != ioc->DiagBuffer_sz[buffer_type]) { ++ pci_free_consistent(ioc->pcidev, ++ ioc->DiagBuffer_sz[buffer_type], ++ request_data, request_data_dma); ++ request_data = NULL; ++ } ++ } ++ ++ if (request_data == NULL) { ++ ioc->DiagBuffer_sz[buffer_type] = 0; ++ ioc->DiagBuffer_dma[buffer_type] = 0; ++ ioc->DataSize[buffer_type] = 0; ++ request_data = pci_alloc_consistent( ++ ioc->pcidev, request_data_sz, &request_data_dma); ++ if (request_data == NULL) { ++ printk(MYIOC_s_DEBUG_FMT "%s: pci_alloc_consistent" ++ " FAILED, (request_sz=%d)\n", ioc->name, ++ __FUNCTION__, request_data_sz); ++ mpt_free_msg_frame(ioc, mf); ++ return -EAGAIN; ++ } ++ ioc->DiagBuffer[buffer_type] = request_data; ++ ioc->DiagBuffer_sz[buffer_type] = request_data_sz; ++ ioc->DiagBuffer_dma[buffer_type] = request_data_dma; ++ } ++ ++ ioc->DiagBuffer_Status[buffer_type] = 0; ++ diag_buffer_post_request = (DiagBufferPostRequest_t *)mf; ++ diag_buffer_post_request->Function = MPI_FUNCTION_DIAG_BUFFER_POST; ++ diag_buffer_post_request->ChainOffset = 0; ++ diag_buffer_post_request->BufferType = karg.data.BufferType; ++ diag_buffer_post_request->TraceLevel = ioc->TraceLevel[buffer_type] = ++ karg.data.TraceLevel; ++ diag_buffer_post_request->MsgFlags = 0; ++ diag_buffer_post_request->Reserved1 = 0; ++ diag_buffer_post_request->Reserved2 = 0; ++ diag_buffer_post_request->Reserved3 = 0; ++ diag_buffer_post_request->BufferAddress.High = 0; ++ if (buffer_type == MPI_DIAG_BUF_TYPE_EXTENDED) ++ ioc->ExtendedType[buffer_type] = karg.data.ExtendedType; ++ else ++ ioc->ExtendedType[buffer_type] = 0; ++ diag_buffer_post_request->ExtendedType = ++ cpu_to_le32(ioc->ExtendedType[buffer_type]); ++ ioc->UniqueId[buffer_type] = karg.data.UniqueId; ++ diag_buffer_post_request->BufferLength = cpu_to_le32(request_data_sz); ++ for (ii = 0; ii < 4; ii++) { ++ ioc->ProductSpecific[buffer_type][ii] = ++ karg.data.ProductSpecific[ii]; ++ diag_buffer_post_request->ProductSpecific[ii] = ++ cpu_to_le32(ioc->ProductSpecific[buffer_type][ii]); ++ } ++ ++ tmp = request_data_dma & 0xFFFFFFFF; ++ diag_buffer_post_request->BufferAddress.Low = cpu_to_le32(tmp); ++ if (ioc->sg_addr_size == sizeof(u64)) { ++ tmp = (u32)((u64)request_data_dma >> 32); ++ diag_buffer_post_request->BufferAddress.High = cpu_to_le32(tmp); ++ } ++ ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, ++ diag_buffer_post_request->MsgContext); ++ INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status) ++ mpt_put_msg_frame(mptctl_id, ioc, mf); ++ timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done, ++ MPT_IOCTL_DEFAULT_TIMEOUT*HZ); ++ if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ rc = -ETIME; ++ printk(MYIOC_s_WARN_FMT "%s: failed\n", ioc->name, ++ __FUNCTION__); ++ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) { ++ mpt_free_msg_frame(ioc, mf); ++ goto out; ++ } ++ if (!timeleft) ++ mptctl_timeout_expired(ioc, mf); ++ goto out; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) == 0) { ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: status=%x\n", ++ ioc->name, __FUNCTION__, ioc->ioctl_cmds.status)); ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ diag_buffer_post_reply = (DiagBufferPostReply_t *)ioc->ioctl_cmds.reply; ++ if (le16_to_cpu(diag_buffer_post_reply->IOCStatus) == ++ MPI_IOCSTATUS_SUCCESS) { ++ if (diag_buffer_post_reply->MsgLength > 5) ++ ioc->DataSize[buffer_type] = ++ le32_to_cpu(diag_buffer_post_reply->TransferLength); ++ ioc->DiagBuffer_Status[buffer_type] |= ++ MPT_DIAG_BUFFER_IS_REGISTERED; ++ } else { ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: IOCStatus=%x " ++ "IOCLogInfo=%x\n", ioc->name, __FUNCTION__, ++ diag_buffer_post_reply->IOCStatus, ++ diag_buffer_post_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } ++ ++ out: ++ ++ CLEAR_MGMT_STATUS(ioc->ioctl_cmds.status) ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, 0); ++ if (rc) { ++ pci_free_consistent(ioc->pcidev, request_data_sz, ++ request_data, request_data_dma); ++ ioc->DiagBuffer[buffer_type] = NULL; ++ ioc->DiagBuffer_sz[buffer_type] = 0; ++ ioc->DiagBuffer_dma[buffer_type] = 0; ++ } ++ return rc; ++} ++ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++/* RELEASE DIAG BUFFER Routine. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -EBUSY if previous command timout and IOC reset is not complete. ++ * -ENODEV if no such device/adapter ++ * -ETIME if timer expires ++ * -ENOMEM if memory allocation error ++ */ ++static int ++mptctl_release_diag_buffer (unsigned long arg) ++{ ++ mpt_diag_release_t __user *uarg = (void __user *) arg; ++ mpt_diag_release_t karg; ++ MPT_ADAPTER *ioc; ++ void * request_data; ++ int iocnum, rc; ++ MPT_FRAME_HDR *mf; ++ DiagReleaseRequest_t *diag_release; ++ DiagReleaseReply_t *diag_release_reply; ++ u8 buffer_type; ++ unsigned long timeleft; ++ ++ rc = 0; ++ if (copy_from_user(&karg, uarg, sizeof(mpt_diag_release_t))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in mpt_diag_release_t struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || ++ (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s enter.\n", ioc->name, ++ __FUNCTION__)); ++ buffer_type = karg.data.UniqueId & 0x000000ff; ++ if (!(ioc->facts.IOCCapabilities & MPT_DIAG_CAPABILITY(buffer_type))) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have Capability for " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type); ++ return -ENODEV; ++ } ++ ++ if ((ioc->DiagBuffer_Status[buffer_type] & ++ MPT_DIAG_BUFFER_IS_REGISTERED) == 0 ) { ++ printk(MYIOC_s_DEBUG_FMT "%s: buffer_type=%x is not " ++ "registered\n", ioc->name, __FUNCTION__, buffer_type); ++ return -EFAULT; ++ } ++ ++ if (karg.data.UniqueId != ioc->UniqueId[buffer_type]) { ++ printk(MYIOC_s_DEBUG_FMT "%s: unique_id=%x is not registered\n", ++ ioc->name, __FUNCTION__, karg.data.UniqueId); ++ return -EFAULT; ++ } ++ ++ if (ioc->DiagBuffer_Status[buffer_type] & MPT_DIAG_BUFFER_IS_RELEASED) { ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: buffer_type=%x " ++ "is already released\n", ioc->name, __FUNCTION__, ++ buffer_type)); ++ return rc; ++ } ++ ++ request_data = ioc->DiagBuffer[buffer_type]; ++ ++ if (request_data == NULL) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have buffer for " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type); ++ return -ENODEV; ++ } ++ ++ /* Get a free request frame and save the message context. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) ++ return -EAGAIN; ++ ++ diag_release = (DiagReleaseRequest_t *)mf; ++ diag_release->Function = MPI_FUNCTION_DIAG_RELEASE; ++ diag_release->BufferType = buffer_type; ++ diag_release->ChainOffset = 0; ++ diag_release->Reserved1 = 0; ++ diag_release->Reserved2 = 0; ++ diag_release->Reserved3 = 0; ++ diag_release->MsgFlags = 0; ++ ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, ++ diag_release->MsgContext); ++ INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status) ++ mpt_put_msg_frame(mptctl_id, ioc, mf); ++ timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done, ++ MPT_IOCTL_DEFAULT_TIMEOUT*HZ); ++ if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ rc = -ETIME; ++ printk(MYIOC_s_WARN_FMT "%s: failed\n", ioc->name, ++ __FUNCTION__); ++ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) { ++ mpt_free_msg_frame(ioc, mf); ++ goto out; ++ } ++ if (!timeleft) ++ mptctl_timeout_expired(ioc, mf); ++ goto out; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) == 0) { ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: status=%x\n", ++ ioc->name, __FUNCTION__, ioc->ioctl_cmds.status)); ++ rc = -EFAULT; ++ goto out; ++ } ++ ++ diag_release_reply = (DiagReleaseReply_t *)ioc->ioctl_cmds.reply; ++ if (le16_to_cpu(diag_release_reply->IOCStatus) != ++ MPI_IOCSTATUS_SUCCESS) { ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: IOCStatus=%x " ++ "IOCLogInfo=%x\n", ++ ioc->name, __FUNCTION__, diag_release_reply->IOCStatus, ++ diag_release_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } else ++ ioc->DiagBuffer_Status[buffer_type] |= ++ MPT_DIAG_BUFFER_IS_RELEASED; ++ ++ out: ++ ++ CLEAR_MGMT_STATUS(ioc->ioctl_cmds.status) ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, 0); ++ return rc; ++} ++ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++/* UNREGISTER DIAG BUFFER Routine. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -EBUSY if previous command timout and IOC reset is not complete. ++ * -ENODEV if no such device/adapter ++ * -ETIME if timer expires ++ * -ENOMEM if memory allocation error ++ */ ++static int ++mptctl_unregister_diag_buffer (unsigned long arg) ++{ ++ mpt_diag_unregister_t __user *uarg = (void __user *) arg; ++ mpt_diag_unregister_t karg; ++ MPT_ADAPTER *ioc; ++ int iocnum; ++ void * request_data; ++ dma_addr_t request_data_dma; ++ u32 request_data_sz; ++ u8 buffer_type; ++ ++ if (copy_from_user(&karg, uarg, sizeof(mpt_diag_unregister_t))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in mpt_diag_unregister_t struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || ++ (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s enter.\n", ioc->name, ++ __FUNCTION__)); ++ buffer_type = karg.data.UniqueId & 0x000000ff; ++ if (!(ioc->facts.IOCCapabilities & MPT_DIAG_CAPABILITY(buffer_type))) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have Capability for " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type); ++ return -ENODEV; ++ } ++ ++ if ((ioc->DiagBuffer_Status[buffer_type] & ++ MPT_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ printk(MYIOC_s_DEBUG_FMT "%s: buffer_type=%x is not " ++ "registered\n", ioc->name, __FUNCTION__, buffer_type); ++ return -EFAULT; ++ } ++ if ((ioc->DiagBuffer_Status[buffer_type] & ++ MPT_DIAG_BUFFER_IS_RELEASED) == 0) { ++ printk(MYIOC_s_DEBUG_FMT "%s: buffer_type=%x has not been " ++ "released\n", ioc->name, __FUNCTION__, buffer_type); ++ return -EFAULT; ++ } ++ ++ if (karg.data.UniqueId != ioc->UniqueId[buffer_type]) { ++ printk(MYIOC_s_DEBUG_FMT "%s: unique_id=%x is not registered\n", ++ ioc->name, __FUNCTION__, karg.data.UniqueId); ++ return -EFAULT; ++ } ++ ++ request_data = ioc->DiagBuffer[buffer_type]; ++ if (!request_data) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have buffer for " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type); ++ return -ENODEV; ++ } ++ ++ request_data_sz = ioc->DiagBuffer_sz[buffer_type]; ++ request_data_dma = ioc->DiagBuffer_dma[buffer_type]; ++ pci_free_consistent(ioc->pcidev, request_data_sz, ++ request_data, request_data_dma); ++ ioc->DiagBuffer[buffer_type] = NULL; ++ ioc->DiagBuffer_Status[buffer_type] = 0; ++ return 0; ++} ++ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++/* QUERY DIAG BUFFER Routine. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -EBUSY if previous command timout and IOC reset is not complete. ++ * -ENODEV if no such device/adapter ++ * -ETIME if timer expires ++ * -ENOMEM if memory allocation error ++ */ ++static int ++mptctl_query_diag_buffer (unsigned long arg) ++{ ++ mpt_diag_query_t __user *uarg = (void __user *)arg; ++ mpt_diag_query_t karg; ++ MPT_ADAPTER *ioc; ++ void * request_data; ++ int iocnum, ii, rc; ++ u8 buffer_type; ++ ++ rc = -EFAULT; ++ if (copy_from_user(&karg, uarg, sizeof(mpt_diag_query_t))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in mpt_diag_query_t struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ karg.data.Flags = 0; ++ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || ++ (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ goto out; ++ } ++ ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s enter.\n", ioc->name, ++ __FUNCTION__)); ++ buffer_type = karg.data.BufferType; ++ if (!(ioc->facts.IOCCapabilities & MPT_DIAG_CAPABILITY(buffer_type))) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have Capability for " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type); ++ goto out; ++ } ++ ++ if ((ioc->DiagBuffer_Status[buffer_type] & ++ MPT_DIAG_BUFFER_IS_REGISTERED) == 0) { ++ printk(MYIOC_s_DEBUG_FMT "%s: buffer_type=%x is not " ++ "registered\n", ioc->name, __FUNCTION__, buffer_type); ++ goto out; ++ } ++ ++ if (karg.data.UniqueId & 0xffffff00) { ++ if (karg.data.UniqueId != ioc->UniqueId[buffer_type]) { ++ printk(MYIOC_s_DEBUG_FMT "%s: unique_id=%x is not " ++ "registered\n", ioc->name, __FUNCTION__, ++ karg.data.UniqueId); ++ goto out; ++ } ++ } ++ ++ request_data = ioc->DiagBuffer[buffer_type]; ++ if (!request_data) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have buffer for " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type); ++ goto out; ++ } ++ ++ rc = 0; ++ if (buffer_type == MPI_DIAG_BUF_TYPE_EXTENDED) { ++ if (karg.data.ExtendedType != ioc->ExtendedType[buffer_type]) ++ goto out; ++ } else ++ karg.data.ExtendedType = 0; ++ ++ if (ioc->DiagBuffer_Status[buffer_type] & MPT_DIAG_BUFFER_IS_RELEASED) ++ karg.data.Flags = 3; ++ else ++ karg.data.Flags = 7; ++ karg.data.TraceLevel = ioc->TraceLevel[buffer_type]; ++ for (ii = 0; ii < 4; ii++) ++ karg.data.ProductSpecific[ii] = ++ ioc->ProductSpecific[buffer_type][ii]; ++ karg.data.DataSize = ioc->DiagBuffer_sz[buffer_type]; ++ karg.data.DriverAddedBufferSize = 0; ++ karg.data.UniqueId = ioc->UniqueId[buffer_type]; ++ ++ out: ++ if (copy_to_user(uarg, &karg, sizeof(mpt_diag_query_t))) { ++ printk(MYIOC_s_ERR_FMT "%s Unable to write mpt_diag_query_t " ++ "data @ %p\n", ioc->name, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ return rc; ++} ++ ++/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ ++/* READ DIAG BUFFER Routine. ++ * ++ * Outputs: None. ++ * Return: 0 if successful ++ * -EFAULT if data unavailable ++ * -EBUSY if previous command timout and IOC reset is not complete. ++ * -ENODEV if no such device/adapter ++ * -ETIME if timer expires ++ * -ENOMEM if memory allocation error ++ */ ++static int ++mptctl_read_diag_buffer (unsigned long arg) ++{ ++ mpt_diag_read_buffer_t __user *uarg = (void __user *) arg; ++ mpt_diag_read_buffer_t karg; ++ MPT_ADAPTER *ioc; ++ void *request_data, *diagData; ++ dma_addr_t request_data_dma; ++ DiagBufferPostRequest_t *diag_buffer_post_request; ++ DiagBufferPostReply_t *diag_buffer_post_reply; ++ MPT_FRAME_HDR *mf; ++ int iocnum, rc, ii; ++ u8 buffer_type; ++ u32 tmp; ++ unsigned long timeleft; ++ ++ rc = 0; ++ if (copy_from_user(&karg, uarg, sizeof(mpt_diag_read_buffer_t))) { ++ printk(KERN_ERR "%s@%d::%s - " ++ "Unable to read in mpt_diag_read_buffer_t struct @ %p\n", ++ __FILE__, __LINE__, __FUNCTION__, uarg); ++ return -EFAULT; ++ } ++ ++ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || ++ (ioc == NULL)) { ++ printk(KERN_ERR "%s::%s() @%d - ioc%d not found!\n", ++ __FILE__, __FUNCTION__, __LINE__, iocnum); ++ return -ENODEV; ++ } ++ ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s enter.\n", ioc->name, ++ __FUNCTION__)); ++ buffer_type = karg.data.UniqueId & 0x000000ff; ++ if (!(ioc->facts.IOCCapabilities & MPT_DIAG_CAPABILITY(buffer_type))) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have Capability " ++ "for buffer_type=%x\n", ioc->name, __FUNCTION__, ++ buffer_type); ++ return -EFAULT; ++ } ++ ++ if (karg.data.UniqueId != ioc->UniqueId[buffer_type]) { ++ printk(MYIOC_s_DEBUG_FMT "%s: unique_id=%x is not registered\n", ++ ioc->name, __FUNCTION__, karg.data.UniqueId); ++ return -EFAULT; ++ } ++ ++ request_data = ioc->DiagBuffer[buffer_type]; ++ if (!request_data) { ++ printk(MYIOC_s_DEBUG_FMT "%s: doesn't have buffer for " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type); ++ return -EFAULT; ++ } ++ ++ diagData = (void *)(request_data + karg.data.StartingOffset); ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: diagData=%p " ++ "request_data=%p StartingOffset=%x\n", ioc->name, __FUNCTION__, ++ diagData, request_data, karg.data.StartingOffset)); ++ ++ if (copy_to_user((void __user *)&uarg->data.DiagnosticData[0], ++ diagData, karg.data.BytesToRead)) { ++ printk(MYIOC_s_ERR_FMT "%s: Unable to write " ++ "mpt_diag_read_buffer_t data @ %p\n", ioc->name, ++ __FUNCTION__, diagData); ++ return -EFAULT; ++ } ++ ++ if ((karg.data.Flags & MPI_FW_DIAG_FLAG_REREGISTER) == 0) ++ goto out; ++ ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: Reregister " ++ "buffer_type=%x\n", ioc->name, __FUNCTION__, buffer_type)); ++ if ((ioc->DiagBuffer_Status[buffer_type] & ++ MPT_DIAG_BUFFER_IS_RELEASED) == 0) { ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: buffer_type=%x " ++ "is still registered\n", ioc->name, __FUNCTION__, ++ buffer_type)); ++ return rc; ++ } ++ /* Get a free request frame and save the message context. ++ */ ++ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) ++ return -EAGAIN; ++ ++ diag_buffer_post_request = (DiagBufferPostRequest_t *)mf; ++ diag_buffer_post_request->Function = MPI_FUNCTION_DIAG_BUFFER_POST; ++ diag_buffer_post_request->ChainOffset = 0; ++ diag_buffer_post_request->BufferType = buffer_type; ++ diag_buffer_post_request->TraceLevel = ++ ioc->TraceLevel[buffer_type]; ++ diag_buffer_post_request->MsgFlags = 0; ++ diag_buffer_post_request->Reserved1 = 0; ++ diag_buffer_post_request->Reserved2 = 0; ++ diag_buffer_post_request->Reserved3 = 0; ++ diag_buffer_post_request->BufferAddress.High = 0; ++ if ( buffer_type == MPI_DIAG_BUF_TYPE_EXTENDED ) ++ diag_buffer_post_request->ExtendedType = ++ cpu_to_le32(ioc->ExtendedType[buffer_type]); ++ diag_buffer_post_request->BufferLength = ++ cpu_to_le32(ioc->DiagBuffer_sz[buffer_type]); ++ for (ii = 0; ii < 4; ii++) ++ diag_buffer_post_request->ProductSpecific[ii] = ++ cpu_to_le32(ioc->ProductSpecific[buffer_type][ii]); ++ request_data_dma = ioc->DiagBuffer_dma[buffer_type]; ++ tmp = request_data_dma & 0xFFFFFFFF; ++ diag_buffer_post_request->BufferAddress.Low = cpu_to_le32(tmp); ++ if (ioc->sg_addr_size == sizeof(u64)) { ++ tmp = (u32)((u64)request_data_dma >> 32); ++ diag_buffer_post_request->BufferAddress.High = cpu_to_le32(tmp); ++ } ++ ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, ++ diag_buffer_post_request->MsgContext); ++ INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status) ++ mpt_put_msg_frame(mptctl_id, ioc, mf); ++ timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done, ++ MPT_IOCTL_DEFAULT_TIMEOUT*HZ); ++ if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { ++ rc = -ETIME; ++ printk(MYIOC_s_WARN_FMT "%s: failed\n", ioc->name, ++ __FUNCTION__); ++ if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) { ++ mpt_free_msg_frame(ioc, mf); ++ goto out; ++ } ++ if (!timeleft) ++ mptctl_timeout_expired(ioc, mf); ++ goto out; ++ } ++ ++ /* process the completed Reply Message Frame */ ++ if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) == 0) { ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: status=%x\n", ++ ioc->name, __FUNCTION__, ioc->ioctl_cmds.status)); ++ rc = -EFAULT; ++ } ++ ++ diag_buffer_post_reply = (DiagBufferPostReply_t *)ioc->ioctl_cmds.reply; ++ if (le16_to_cpu(diag_buffer_post_reply->IOCStatus) == ++ MPI_IOCSTATUS_SUCCESS) { ++ if (diag_buffer_post_reply->MsgLength > 5) ++ ioc->DataSize[buffer_type] = ++ le32_to_cpu(diag_buffer_post_reply->TransferLength); ++ ioc->DiagBuffer_Status[buffer_type] |= ++ MPT_DIAG_BUFFER_IS_REGISTERED; ++ } else { ++ dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: IOCStatus=%x " ++ "IOCLogInfo=%x\n", ioc->name, __FUNCTION__, ++ diag_buffer_post_reply->IOCStatus, ++ diag_buffer_post_reply->IOCLogInfo)); ++ rc = -EFAULT; ++ } ++ ++ out: ++ CLEAR_MGMT_STATUS(ioc->ioctl_cmds.status) ++ SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, 0); ++ return rc; ++} +--- /dev/null ++++ b/drivers/message/fusion/rejected_ioctls/diag_buffer.h +@@ -0,0 +1,101 @@ ++#define MPTDIAGREGISTER _IOWR(MPT_MAGIC_NUMBER,26,mpt_diag_register_t) ++#define MPTDIAGRELEASE _IOWR(MPT_MAGIC_NUMBER,27,mpt_diag_release_t) ++#define MPTDIAGUNREGISTER _IOWR(MPT_MAGIC_NUMBER,28,mpt_diag_unregister_t) ++#define MPTDIAGQUERY _IOWR(MPT_MAGIC_NUMBER,29,mpt_diag_query_t) ++#define MPTDIAGREADBUFFER _IOWR(MPT_MAGIC_NUMBER,30,mpt_diag_read_buffer_t) ++ ++#define MPI_FW_DIAG_IOCTL (0x80646961) ++#define MPI_FW_DIAG_TYPE_REGISTER (0x00000001) ++#define MPI_FW_DIAG_TYPE_UNREGISTER (0x00000002) ++#define MPI_FW_DIAG_TYPE_QUERY (0x00000003) ++#define MPI_FW_DIAG_TYPE_READ_BUFFER (0x00000004) ++#define MPI_FW_DIAG_TYPE_RELEASE (0x00000005) ++ ++#define MPI_FW_DIAG_INVALID_UID (0x00000000) ++#define FW_DIAGNOSTIC_BUFFER_COUNT (3) ++#define FW_DIAGNOSTIC_UID_NOT_FOUND (0xFF) ++ ++#define MPI_FW_DIAG_ERROR_SUCCESS (0x00000000) ++#define MPI_FW_DIAG_ERROR_FAILURE (0x00000001) ++#define MPI_FW_DIAG_ERROR_INVALID_PARAMETER (0x00000002) ++#define MPI_FW_DIAG_ERROR_POST_FAILED (0x00000010) ++#define MPI_FW_DIAG_ERROR_INVALID_UID (0x00000011) ++ ++#define MPI_FW_DIAG_ERROR_RELEASE_FAILED (0x00000012) ++#define MPI_FW_DIAG_ERROR_NO_BUFFER (0x00000013) ++#define MPI_FW_DIAG_ERROR_ALREADY_RELEASED (0x00000014) ++ ++#define MPT_DIAG_CAPABILITY(bufftype) (MPI_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER << bufftype) ++ ++#define MPT_DIAG_BUFFER_IS_REGISTERED 1 ++#define MPT_DIAG_BUFFER_IS_RELEASED 2 ++ ++typedef struct _MPI_FW_DIAG_REGISTER { ++ u8 TraceLevel; ++ u8 BufferType; ++ u16 Flags; ++ u32 ExtendedType; ++ u32 ProductSpecific[4]; ++ u32 RequestedBufferSize; ++ u32 UniqueId; ++} MPI_FW_DIAG_REGISTER, *PTR_MPI_FW_DIAG_REGISTER; ++ ++typedef struct _mpt_diag_register { ++ mpt_ioctl_header hdr; ++ MPI_FW_DIAG_REGISTER data; ++} mpt_diag_register_t; ++ ++typedef struct _MPI_FW_DIAG_UNREGISTER { ++ u32 UniqueId; ++} MPI_FW_DIAG_UNREGISTER, *PTR_MPI_FW_DIAG_UNREGISTER; ++ ++typedef struct _mpt_diag_unregister { ++ mpt_ioctl_header hdr; ++ MPI_FW_DIAG_UNREGISTER data; ++} mpt_diag_unregister_t; ++ ++#define MPI_FW_DIAG_FLAG_APP_OWNED (0x0001) ++#define MPI_FW_DIAG_FLAG_BUFFER_VALID (0x0002) ++#define MPI_FW_DIAG_FLAG_FW_BUFFER_ACCESS (0x0004) ++ ++typedef struct _MPI_FW_DIAG_QUERY { ++ u8 TraceLevel; ++ u8 BufferType; ++ u16 Flags; ++ u32 ExtendedType; ++ u32 ProductSpecific[4]; ++ u32 DataSize; ++ u32 DriverAddedBufferSize; ++ u32 UniqueId; ++} MPI_FW_DIAG_QUERY, *PTR_MPI_FW_DIAG_QUERY; ++ ++typedef struct _mpt_diag_query { ++ mpt_ioctl_header hdr; ++ MPI_FW_DIAG_QUERY data; ++} mpt_diag_query_t; ++ ++typedef struct _MPI_FW_DIAG_RELEASE { ++ u32 UniqueId; ++} MPI_FW_DIAG_RELEASE, *PTR_MPI_FW_DIAG_RELEASE; ++ ++typedef struct _mpt_diag_release { ++ mpt_ioctl_header hdr; ++ MPI_FW_DIAG_RELEASE data; ++} mpt_diag_release_t; ++ ++#define MPI_FW_DIAG_FLAG_REREGISTER (0x0001) ++ ++typedef struct _MPI_FW_DIAG_READ_BUFFER { ++ u8 Status; ++ u8 Reserved; ++ u16 Flags; ++ u32 StartingOffset; ++ u32 BytesToRead; ++ u32 UniqueId; ++ u32 DiagnosticData[1]; ++} MPI_FW_DIAG_READ_BUFFER, *PTR_MPI_FW_DIAG_READ_BUFFER; ++ ++typedef struct _mpt_diag_read_buffer { ++ mpt_ioctl_header hdr; ++ MPI_FW_DIAG_READ_BUFFER data; ++} mpt_diag_read_buffer_t; diff --git a/patches.drivers/pci-disable-msi-on-K8M800 b/patches.drivers/pci-disable-msi-on-K8M800 new file mode 100644 index 0000000..325b2ef --- /dev/null +++ b/patches.drivers/pci-disable-msi-on-K8M800 @@ -0,0 +1,30 @@ +From: Tejun Heo +Subject: [PATCH] pci: disable MSI on VIA K8M800 +References: bnc#599508 +Patch-Mainline: Pending for 2.6.35 and -stable + +MSI delivery from on-board ahci controller doesn't work on K8M800. At +this point, it's unclear whether the culprit is with the ahci +controller or the host bridge. Given the track record and considering +the rather minimal impact of MSI, disabling it seems reasonable. + +Signed-off-by: Tejun Heo +Reported-by: Rainer Hurtado Navarro +Cc: stable@kernel.org +Signed-off-by: Tejun Heo +--- + drivers/pci/quirks.c | 1 + + 1 file changed, 1 insertion(+) + +Index: linux-2.6.34-master/drivers/pci/quirks.c +=================================================================== +--- linux-2.6.34-master.orig/drivers/pci/quirks.c ++++ linux-2.6.34-master/drivers/pci/quirks.c +@@ -2112,6 +2112,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AT + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3336, quirk_disable_all_msi); + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3351, quirk_disable_all_msi); + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3364, quirk_disable_all_msi); ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8380_0, quirk_disable_all_msi); + + /* Disable MSI on chipsets that are known to not support it */ + static void __devinit quirk_disable_msi(struct pci_dev *dev) diff --git a/patches.drivers/ppc64-adb b/patches.drivers/ppc64-adb new file mode 100644 index 0000000..fe87d31 --- /dev/null +++ b/patches.drivers/ppc64-adb @@ -0,0 +1,53 @@ +From: Olaf Hering +Subject: enable mouse button emulation also for G5 +Patch-mainline: never + +fix compile errors + + drivers/macintosh/Kconfig | 2 +- + drivers/macintosh/adb.c | 4 ++++ + drivers/macintosh/adbhid.c | 6 +++++- + 3 files changed, 10 insertions(+), 2 deletions(-) + +--- a/drivers/macintosh/Kconfig ++++ b/drivers/macintosh/Kconfig +@@ -13,7 +13,7 @@ if MACINTOSH_DRIVERS + + config ADB + bool "Apple Desktop Bus (ADB) support" +- depends on MAC || (PPC_PMAC && PPC32) ++ depends on MAC || PPC_PMAC + help + Apple Desktop Bus (ADB) support is for support of devices which + are connected to an ADB port. ADB devices tend to have 4 pins. +--- a/drivers/macintosh/adb.c ++++ b/drivers/macintosh/adb.c +@@ -298,6 +298,10 @@ static int __init adb_init(void) + if (!machine_is(chrp) && !machine_is(powermac)) + return 0; + #endif ++#ifdef CONFIG_PPC64 ++ if (!machine_is(powermac)) ++ return 0; ++#endif + #ifdef CONFIG_MAC + if (!MACH_IS_MAC) + return 0; +--- a/drivers/macintosh/adbhid.c ++++ b/drivers/macintosh/adbhid.c +@@ -1264,10 +1264,14 @@ init_ms_a3(int id) + + static int __init adbhid_init(void) + { +-#ifndef CONFIG_MAC ++#ifdef CONFIG_PPC32 + if (!machine_is(chrp) && !machine_is(powermac)) + return 0; + #endif ++#ifdef CONFIG_PPC64 ++ if (!machine_is(powermac)) ++ return 0; ++#endif + + led_request.complete = 1; + diff --git a/patches.drivers/qla4xxx-5.01.00-k9-5.01.00.00.11.01-k10.patch b/patches.drivers/qla4xxx-5.01.00-k9-5.01.00.00.11.01-k10.patch new file mode 100644 index 0000000..1af1c14 --- /dev/null +++ b/patches.drivers/qla4xxx-5.01.00-k9-5.01.00.00.11.01-k10.patch @@ -0,0 +1,2809 @@ +From: Ravi Anand +Subject: Update qla4xxx driver for SLES11 SP1 +References: bnc#556572,FATE#307128 +Patch-mainline: not yet + +Change log from v5.01.00-k9 to v5.01.00.00.11.01-k10: + +- Wait for device online in reset_lun + for 10s, for device to come online otherwise indicating + an error condition. + +- Updated IPv6 support + +- Link Down -> Mark all devices missing + This change will cut 20 seconds of failover times. + Previously, the driver took no action on a Link Down, + and waited for the I/O on a dead connection to timeout + in the firmware before marking the DDB missing. + +- Code Clean up - remove "marker_needed" + +- Updated firmware ready timeout algorithm to prevent long delays + and use jiffies to time out instead of counter. Also use + msleep_interruptible instead of msleep. + +- Added srb reference count support + Serialization between the error handler and recovery code. + +- Avoid relogin on device marked missing + causing mailbox command (0x63) failure + +- Check for command completion while device and host reset + Created variables to reference h, b, t, l, because + if the IOCTL scsi passthru command completes within + eh_device_reset, the cmd structure may no longer be valid. + Fix for ER67742: hang while sg_reset. + Also wait for hba online in device reset path + +- Do not retry login to CHAP auth failed targets + Per RFC 3720, Login Response Status Code 0x02 should not be retried. + Condensed connection error checking code to a single routine, and + added check for status class 0x02. + +- Added support for abort task management command + Handles SCSI aborts. + +- Add Async PDU support + Added support for Asynchronous PDU IOCB + +- handle DDB removal via DPC + mailbox command free device ddb (0x31) does not generate AEN, + so we need to process it separately where we defer the removal + of ddb to DPC. + +- Update data structure to use single ioctl module + update scsi_qla_host to match with qisioctl struct. + +- ioctl initialization + to interact with the application + +- Add support for ACB firmware features in the driver + to notify the firmware that the driver supports ifcb size + greater than 512B. + +- added active srb array implementation + for effective srb processing within the io path. + +- v5.01.00.00.11.01-k10 + Changed driver version for SLES11 SP1. + +Signed-off-by: Ravi Anand +Acked-by: Hannes Reinecke + +--- + drivers/scsi/qla4xxx/ql4_def.h | 120 +++++++- + drivers/scsi/qla4xxx/ql4_fw.h | 91 ++++-- + drivers/scsi/qla4xxx/ql4_glbl.h | 18 + + drivers/scsi/qla4xxx/ql4_init.c | 349 +++++++++++++++++------ + drivers/scsi/qla4xxx/ql4_inline.h | 71 ++++ + drivers/scsi/qla4xxx/ql4_iocb.c | 21 + + drivers/scsi/qla4xxx/ql4_isr.c | 53 +++ + drivers/scsi/qla4xxx/ql4_mbx.c | 426 ++++++++++++++++++++++------ + drivers/scsi/qla4xxx/ql4_os.c | 553 ++++++++++++++++++++++++++++++++++--- + drivers/scsi/qla4xxx/ql4_version.h | 2 + 10 files changed, 1432 insertions(+), 272 deletions(-) + +--- a/drivers/scsi/qla4xxx/ql4_def.h ++++ b/drivers/scsi/qla4xxx/ql4_def.h +@@ -90,16 +90,17 @@ + ***********************************/ + #define MAX_HBAS 16 + #define MAX_BUSES 1 +-#define MAX_TARGETS (MAX_PRST_DEV_DB_ENTRIES + MAX_DEV_DB_ENTRIES) ++#define MAX_TARGETS MAX_DEV_DB_ENTRIES + #define MAX_LUNS 0xffff + #define MAX_AEN_ENTRIES 256 /* should be > EXT_DEF_MAX_AEN_QUEUE */ +-#define MAX_DDB_ENTRIES (MAX_PRST_DEV_DB_ENTRIES + MAX_DEV_DB_ENTRIES) ++#define MAX_DDB_ENTRIES MAX_DEV_DB_ENTRIES + #define MAX_PDU_ENTRIES 32 + #define INVALID_ENTRY 0xFFFF + #define MAX_CMDS_TO_RISC 1024 + #define MAX_SRBS MAX_CMDS_TO_RISC + #define MBOX_AEN_REG_COUNT 5 + #define MAX_INIT_RETRIES 5 ++#define LEGACY_IFCB_SIZE 0x200 + + /* + * Buffer sizes +@@ -114,6 +115,7 @@ + */ + #define MAC_ADDR_LEN 6 /* in bytes */ + #define IP_ADDR_LEN 4 /* in bytes */ ++#define IPv6_ADDR_LEN 16 /* IPv6 address size */ + #define DRIVER_NAME "qla4xxx" + + #define MAX_LINKED_CMDS_PER_LUN 3 +@@ -146,6 +148,7 @@ + #define ISNS_DEREG_TOV 5 + + #define MAX_RESET_HA_RETRIES 2 ++#define DEVICE_ONLINE_TOV 10 + + /* + * SCSI Request Block structure (srb) that is placed +@@ -220,7 +223,7 @@ struct ddb_entry { + + uint16_t os_target_id; /* Target ID */ + uint16_t fw_ddb_index; /* DDB firmware index */ +- uint8_t reserved[2]; ++ uint8_t options; + uint32_t fw_ddb_device_state; /* F/W Device State -- see ql4_fw.h */ + + uint32_t CmdSn; +@@ -245,10 +248,13 @@ struct ddb_entry { + + uint16_t port; + uint32_t tpgt; +- uint8_t ip_addr[ISCSI_IPADDR_SIZE]; ++ uint8_t ip_addr[IP_ADDR_LEN]; + uint8_t iscsi_name[ISCSI_NAME_SIZE]; /* 72 x48 */ + uint8_t iscsi_alias[0x20]; + uint8_t isid[6]; ++ ++ struct in6_addr remote_ipv6_addr; ++ struct in6_addr link_local_ipv6_addr; + }; + + /* +@@ -260,6 +266,8 @@ struct ddb_entry { + * commands */ + #define DDB_STATE_MISSING 2 /* Device logged off, trying + * to re-login */ ++#define DDB_STATE_REMOVED 3 /* The fw ddb_entry is freed ++ * the session can be destroyed */ + + /* + * DDB flags. +@@ -269,16 +277,38 @@ struct ddb_entry { + * logged it out */ + #define DF_ISNS_DISCOVERED 2 /* Device was discovered via iSNS */ + #define DF_FO_MASKED 3 ++#define DF_REMOVE 4 /* FW DDB is destroyed */ + + + #include "ql4_fw.h" + #include "ql4_nvram.h" + ++/* shortcut to print ISID */ ++#define ISID(addr) \ ++ ((unsigned char *)&addr)[5], \ ++ ((unsigned char *)&addr)[4], \ ++ ((unsigned char *)&addr)[3], \ ++ ((unsigned char *)&addr)[2], \ ++ ((unsigned char *)&addr)[1], \ ++ ((unsigned char *)&addr)[0] ++#define ISID_FMT "0x%02x%02x%02x%02x%02x%02x" ++ + /* + * Linux Host Adapter structure + */ + struct scsi_qla_host { ++ struct klist_node node; ++ uint16_t instance; ++ uint16_t rsvd0; ++ ++ /* exported functions */ ++ int (*ql4cmd)(struct scsi_qla_host *ha, struct srb * srb); ++ int (*ql4mbx)(struct scsi_qla_host *ha, uint8_t inCount, ++ uint8_t outCount, uint32_t *mbx_cmd, uint32_t *mbx_sts); ++ + /* Linux adapter configuration data */ ++ struct Scsi_Host *host; /* pointer to host data */ ++ uint32_t tot_ddbs; + unsigned long flags; + + #define AF_ONLINE 0 /* 0x00000001 */ +@@ -290,6 +320,7 @@ struct scsi_qla_host { + #define AF_LINK_UP 8 /* 0x00000100 */ + #define AF_IRQ_ATTACHED 10 /* 0x00000400 */ + #define AF_DISABLE_ACB_COMPLETE 11 /* 0x00000800 */ ++#define AF_OS_INDEX_VALID 12 /* 0x00001000 */ + + unsigned long dpc_flags; + +@@ -301,9 +332,9 @@ struct scsi_qla_host { + #define DPC_ISNS_RESTART 7 /* 0x00000080 */ + #define DPC_AEN 9 /* 0x00000200 */ + #define DPC_GET_DHCP_IP_ADDR 15 /* 0x00008000 */ +- +- struct Scsi_Host *host; /* pointer to host data */ +- uint32_t tot_ddbs; ++#define DPC_REMOVE_DEVICE 17 /* 0x00020000 */ ++#define DPC_LINK_CHANGED 18 /* 0x00040000 */ ++#define DPC_ASYNC_MSG_PDU 19 /* 0x00080000 */ + + uint16_t iocb_cnt; + +@@ -320,14 +351,14 @@ struct scsi_qla_host { + #define MIN_IOBASE_LEN 0x100 + + uint16_t req_q_count; +- uint8_t marker_needed; +- uint8_t rsvd1; ++ uint8_t rsvd1[2]; + + unsigned long host_no; + + /* NVRAM registers */ + struct eeprom_data *nvram; + spinlock_t hardware_lock ____cacheline_aligned; ++ spinlock_t list_lock; + uint32_t eeprom_cmd_data; + + /* Counters for general statistics */ +@@ -352,7 +383,6 @@ struct scsi_qla_host { + uint32_t firmware_version[2]; + uint32_t patch_number; + uint32_t build_number; +- uint32_t board_id; + + /* --- From Init_FW --- */ + /* init_cb_t *init_cb; */ +@@ -372,6 +402,7 @@ struct scsi_qla_host { + + /* --- From GetFwState --- */ + uint32_t firmware_state; ++ uint32_t board_id; + uint32_t addl_fw_state; + + /* Linux kernel thread */ +@@ -394,6 +425,10 @@ struct scsi_qla_host { + uint16_t free_srb_q_count; + uint16_t num_srbs_allocated; + ++ /* Active array */ ++ struct srb *active_srb_array[MAX_SRBS]; ++ uint16_t current_active_index; ++ + /* DMA Memory Block */ + void *queues; + dma_addr_t queues_dma; +@@ -422,12 +457,20 @@ struct scsi_qla_host { + uint16_t aen_out; + struct aen aen_q[MAX_AEN_ENTRIES]; + +- struct ql4_aen_log aen_log;/* tracks all aens */ ++ /* pdu variables */ ++ uint16_t pdu_count; /* Number of available aen_q entries */ ++ uint16_t pdu_in; /* Current indexes */ ++ uint16_t pdu_out; ++ uint16_t pdu_active; ++ struct pdu_entry *free_pdu_top; ++ struct pdu_entry *free_pdu_bottom; ++ struct pdu_entry pdu_queue[MAX_PDU_ENTRIES]; + + /* This mutex protects several threads to do mailbox commands + * concurrently. + */ + struct mutex mbox_sem; ++ wait_queue_head_t mailbox_wait_queue; + + /* temporary mailbox status registers */ + volatile uint8_t mbox_status_count; +@@ -439,10 +482,63 @@ struct scsi_qla_host { + /* Map ddb_list entry by FW ddb index */ + struct ddb_entry *fw_ddb_index_map[MAX_DDB_ENTRIES]; + ++ struct ql4_aen_log aen_log;/* tracks all aens */ ++ void (*ql4getaenlog)(struct scsi_qla_host *ha, struct ql4_aen_log *aenl); ++ ++#define QL_INDICES_PER_ENTRY 32 ++#define QL_OSINDEX_ENTRIES (MAX_DDB_ENTRIES/QL_INDICES_PER_ENTRY) ++ volatile unsigned long os_map[QL_OSINDEX_ENTRIES]; ++ + /* Saved srb for status continuation entry processing */ + struct srb *status_srb; ++ ++ struct list_head async_iocb_list; ++ dma_addr_t gen_req_rsp_iocb_dma; ++ void *gen_req_rsp_iocb; ++ ++ /* IPv6 support info from InitFW */ ++ uint8_t acb_version; ++ uint8_t ipv4_addr_state; ++ uint16_t ipv4_options; ++ ++ uint32_t resvd2; ++ uint32_t ipv6_options; ++ uint32_t ipv6_addl_options; ++ uint8_t ipv6_link_local_state; ++ uint8_t ipv6_addr0_state; ++ uint8_t ipv6_addr1_state; ++ uint8_t ipv6_default_router_state; ++ struct in6_addr ipv6_link_local_addr; ++ struct in6_addr ipv6_addr0; ++ struct in6_addr ipv6_addr1; ++ struct in6_addr ipv6_default_router_addr; ++ ++ uint16_t ifcb_size; ++}; ++ ++static inline int is_ipv4_enabled(struct scsi_qla_host *ha) ++{ ++ return ((ha->ipv4_options & IPOPT_IPv4_PROTOCOL_ENABLE) != 0); ++} ++ ++static inline int is_ipv6_enabled(struct scsi_qla_host *ha) ++{ ++ return ((ha->ipv6_options & IPV6_OPT_IPV6_PROTOCOL_ENABLE) != 0); ++} ++ ++/* ++ * structure to buffer Async PDUs ++ */ ++struct async_msg_pdu_iocb { ++ struct list_head list; ++ uint8_t iocb[0x40]; + }; + ++typedef struct _ASYNC_PDU_SENSE { ++ uint16_t sense_len; /* 00-01 */ ++ uint8_t sense_data[0]; ++} ASYNC_PDU_SENSE; ++ + static inline int is_qla4010(struct scsi_qla_host *ha) + { + return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP4010; +@@ -511,7 +607,7 @@ static inline void __iomem* isp_port_err + &ha->reg->u2.isp4022.p0.port_err_status); + } + +-static inline void __iomem * isp_gp_out(struct scsi_qla_host *ha) ++static inline void __iomem *isp_gp_out(struct scsi_qla_host *ha) + { + return (is_qla4010(ha) ? + &ha->reg->u2.isp4010.gp_out : +--- a/drivers/scsi/qla4xxx/ql4_fw.h ++++ b/drivers/scsi/qla4xxx/ql4_fw.h +@@ -215,6 +215,7 @@ union external_hw_config_reg { + /* Mailbox command definitions */ + #define MBOX_CMD_ABOUT_FW 0x0009 + #define MBOX_CMD_PING 0x000B ++#define MBOX_CMD_ABORT_TASK 0x0015 + #define MBOX_CMD_LUN_RESET 0x0016 + #define MBOX_CMD_TARGET_WARM_RESET 0x0017 + #define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E +@@ -227,8 +228,8 @@ union external_hw_config_reg { + #define MBOX_CMD_READ_FLASH 0x0026 + #define MBOX_CMD_CLEAR_DATABASE_ENTRY 0x0031 + #define MBOX_CMD_CONN_CLOSE_SESS_LOGOUT 0x0056 +-#define LOGOUT_OPTION_CLOSE_SESSION 0x01 +-#define LOGOUT_OPTION_RELOGIN 0x02 ++#define LOGOUT_OPTION_CLOSE_SESSION 0x02 ++#define LOGOUT_OPTION_RESET 0x04 + #define MBOX_CMD_EXECUTE_IOCB_A64 0x005A + #define MBOX_CMD_INITIALIZE_FIRMWARE 0x0060 + #define MBOX_CMD_GET_INIT_FW_CTRL_BLOCK 0x0061 +@@ -258,13 +259,15 @@ union external_hw_config_reg { + /* Mailbox 1 */ + #define FW_STATE_READY 0x0000 + #define FW_STATE_CONFIG_WAIT 0x0001 +-#define FW_STATE_WAIT_LOGIN 0x0002 ++#define FW_STATE_WAIT_AUTOCONNECT 0x0002 + #define FW_STATE_ERROR 0x0004 +-#define FW_STATE_DHCP_IN_PROGRESS 0x0008 ++#define FW_STATE_CONFIGURING_IP 0x0008 + + /* Mailbox 3 */ + #define FW_ADDSTATE_OPTICAL_MEDIA 0x0001 +-#define FW_ADDSTATE_DHCP_ENABLED 0x0002 ++#define FW_ADDSTATE_DHCPv4_ENABLED 0x0002 ++#define FW_ADDSTATE_DHCPv4_LEASE_ACQUIRED 0x0004 ++#define FW_ADDSTATE_DHCPv4_LEASE_EXPIRED 0x0008 + #define FW_ADDSTATE_LINK_UP 0x0010 + #define FW_ADDSTATE_ISNS_SVC_ENABLED 0x0020 + #define MBOX_CMD_GET_DATABASE_ENTRY_DEFAULTS 0x006B +@@ -320,6 +323,8 @@ union external_hw_config_reg { + /* Host Adapter Initialization Control Block (from host) */ + struct addr_ctrl_blk { + uint8_t version; /* 00 */ ++#define IFCB_VER_MIN 0x01 ++#define IFCB_VER_MAX 0x02 + uint8_t control; /* 01 */ + + uint16_t fw_options; /* 02-03 */ +@@ -351,11 +356,15 @@ struct addr_ctrl_blk { + uint16_t iscsi_opts; /* 30-31 */ + uint16_t ipv4_tcp_opts; /* 32-33 */ + uint16_t ipv4_ip_opts; /* 34-35 */ ++#define IPOPT_IPv4_PROTOCOL_ENABLE 0x8000 + + uint16_t iscsi_max_pdu_size; /* 36-37 */ + uint8_t ipv4_tos; /* 38 */ + uint8_t ipv4_ttl; /* 39 */ + uint8_t acb_version; /* 3A */ ++#define ACB_NOT_SUPPORTED 0x00 ++#define ACB_SUPPORTED 0x02 /* Capable of ACB Version 2 Features */ ++ + uint8_t res2; /* 3B */ + uint16_t def_timeout; /* 3C-3D */ + uint16_t iscsi_fburst_len; /* 3E-3F */ +@@ -397,16 +406,34 @@ struct addr_ctrl_blk { + uint32_t cookie; /* 200-203 */ + uint16_t ipv6_port; /* 204-205 */ + uint16_t ipv6_opts; /* 206-207 */ ++#define IPV6_OPT_IPV6_PROTOCOL_ENABLE 0x8000 ++ + uint16_t ipv6_addtl_opts; /* 208-209 */ ++#define IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE 0x0002 /* Pri ACB Only */ ++#define IPV6_ADDOPT_AUTOCONFIG_LINK_LOCAL_ADDR 0x0001 ++ + uint16_t ipv6_tcp_opts; /* 20A-20B */ + uint8_t ipv6_tcp_wsf; /* 20C */ + uint16_t ipv6_flow_lbl; /* 20D-20F */ +- uint8_t ipv6_gw_addr[16]; /* 210-21F */ ++ uint8_t ipv6_dflt_rtr_addr[16]; /* 210-21F */ + uint16_t ipv6_vlan_tag; /* 220-221 */ + uint8_t ipv6_lnk_lcl_addr_state;/* 222 */ + uint8_t ipv6_addr0_state; /* 223 */ + uint8_t ipv6_addr1_state; /* 224 */ +- uint8_t ipv6_gw_state; /* 225 */ ++#define IP_ADDRSTATE_UNCONFIGURED 0 ++#define IP_ADDRSTATE_INVALID 1 ++#define IP_ADDRSTATE_ACQUIRING 2 ++#define IP_ADDRSTATE_TENTATIVE 3 ++#define IP_ADDRSTATE_DEPRICATED 4 ++#define IP_ADDRSTATE_PREFERRED 5 ++#define IP_ADDRSTATE_DISABLING 6 ++ ++ uint8_t ipv6_dflt_rtr_state; /* 225 */ ++#define IPV6_RTRSTATE_UNKNOWN 0 ++#define IPV6_RTRSTATE_MANUAL 1 ++#define IPV6_RTRSTATE_ADVERTISED 3 ++#define IPV6_RTRSTATE_STALE 4 ++ + uint8_t ipv6_traffic_class; /* 226 */ + uint8_t ipv6_hop_limit; /* 227 */ + uint8_t ipv6_if_id[8]; /* 228-22F */ +@@ -424,7 +451,7 @@ struct addr_ctrl_blk { + + struct init_fw_ctrl_blk { + struct addr_ctrl_blk pri; +- struct addr_ctrl_blk sec; ++/* struct addr_ctrl_blk sec;*/ + }; + + /*************************************************************************/ +@@ -433,6 +460,9 @@ struct dev_db_entry { + uint16_t options; /* 00-01 */ + #define DDB_OPT_DISC_SESSION 0x10 + #define DDB_OPT_TARGET 0x02 /* device is a target */ ++#define DDB_OPT_IPV6_DEVICE 0x100 ++#define DDB_OPT_IPV6_NULL_LINK_LOCAL 0x800 /* post connection */ ++#define DDB_OPT_IPV6_FW_DEFINED_LINK_LOCAL 0x800 /* pre connection */ + + uint16_t exec_throttle; /* 02-03 */ + uint16_t exec_count; /* 04-05 */ +@@ -468,7 +498,7 @@ struct dev_db_entry { + * pointer to a string so we + * don't have to reserve soooo + * much RAM */ +- uint8_t ipv6_addr[0x10];/* 1A0-1AF */ ++ uint8_t link_local_ipv6_addr[0x10]; /* 1A0-1AF */ + uint8_t res5[0x10]; /* 1B0-1BF */ + uint16_t ddb_link; /* 1C0-1C1 */ + uint16_t chap_tbl_idx; /* 1C2-1C3 */ +@@ -577,13 +607,14 @@ struct conn_event_log_entry { + /* IOCB header structure */ + struct qla4_header { + uint8_t entryType; +-#define ET_STATUS 0x03 +-#define ET_MARKER 0x04 +-#define ET_CONT_T1 0x0A +-#define ET_STATUS_CONTINUATION 0x10 +-#define ET_CMND_T3 0x19 +-#define ET_PASSTHRU0 0x3A +-#define ET_PASSTHRU_STATUS 0x3C ++#define ET_STATUS 0x03 ++#define ET_MARKER 0x04 ++#define ET_CONT_T1 0x0A ++#define ET_STATUS_CONTINUATION 0x10 ++#define ET_CMND_T3 0x19 ++#define ET_ASYNC_PDU 0x37 ++#define ET_PASSTHRU0 0x3A ++#define ET_PASSTHRU_STATUS 0x3C + + uint8_t entryStatus; + uint8_t systemDefined; +@@ -692,6 +723,18 @@ struct qla4_marker_entry { + uint64_t reserved6; /* 38-3F */ + }; + ++/* Asynchronous PDU IOCB structure */ ++struct async_pdu_iocb { ++ struct qla4_header hdr; /* 00-02 */ ++ uint32_t async_pdu_handle; /* 03-06 */ ++ uint16_t target_id; /* 07-08 */ ++ uint16_t status; /* 09-0A */ ++#define ASYNC_PDU_IOCB_STS_OK 0x01 ++ ++ uint32_t rsrvd; /* 0B-0F */ ++ uint8_t iscsi_pdu_hdr[48]; /* 10-3F */ ++}; ++ + /* Status entry structure*/ + struct status_entry { + struct qla4_header hdr; /* 00-03 */ +@@ -734,6 +777,15 @@ struct status_entry { + + }; + ++struct pdu_entry { ++ uint8_t *Buff; ++ uint32_t BuffLen; ++ uint32_t SendBuffLen; ++ uint32_t RecvBuffLen; ++ struct pdu_entry *Next; ++ dma_addr_t DmaBuff; ++}; ++ + /* Status Continuation entry */ + struct status_cont_entry { + struct qla4_header hdr; /* 00-03 */ +@@ -745,11 +797,9 @@ struct passthru0 { + uint32_t handle; /* 04-07 */ + uint16_t target; /* 08-09 */ + uint16_t connectionID; /* 0A-0B */ +-#define ISNS_DEFAULT_SERVER_CONN_ID ((uint16_t)0x8000) + + uint16_t controlFlags; /* 0C-0D */ +-#define PT_FLAG_ETHERNET_FRAME 0x8000 +-#define PT_FLAG_ISNS_PDU 0x8000 ++#define PT_FLAG_ISCSI_PDU 0x1000 + #define PT_FLAG_SEND_BUFFER 0x0200 + #define PT_FLAG_WAIT_4_RESPONSE 0x0100 + +@@ -759,7 +809,8 @@ struct passthru0 { + struct data_seg_a64 outDataSeg64; /* 10-1B */ + uint32_t res1; /* 1C-1F */ + struct data_seg_a64 inDataSeg64; /* 20-2B */ +- uint8_t res2[20]; /* 2C-3F */ ++ uint8_t res2[16]; /* 2C-3F */ ++ uint32_t async_pdu_handle; + }; + + struct passthru_status { +--- a/drivers/scsi/qla4xxx/ql4_glbl.h ++++ b/drivers/scsi/qla4xxx/ql4_glbl.h +@@ -20,17 +20,21 @@ int qla4xxx_soft_reset(struct scsi_qla_h + irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id); + + void qla4xxx_free_ddb_list(struct scsi_qla_host * ha); ++void qla4xxx_free_ddb(struct scsi_qla_host *, struct ddb_entry *); + void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen); + + int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha); + int qla4xxx_relogin_device(struct scsi_qla_host * ha, + struct ddb_entry * ddb_entry); ++int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb); + int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, + int lun); + int qla4xxx_reset_target(struct scsi_qla_host * ha, + struct ddb_entry * ddb_entry); + int qla4xxx_get_flash(struct scsi_qla_host * ha, dma_addr_t dma_addr, + uint32_t offset, uint32_t len); ++int qla4xxx_issue_iocb(struct scsi_qla_host *ha, uint32_t comp_offset, ++ dma_addr_t phys_addr); + int qla4xxx_get_firmware_status(struct scsi_qla_host * ha); + int qla4xxx_get_firmware_state(struct scsi_qla_host * ha); + int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha); +@@ -58,6 +62,10 @@ void qla4xxx_get_crash_record(struct scs + struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha); + int qla4xxx_add_sess(struct ddb_entry *); + void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry); ++int qla4xxx_conn_close_sess_logout(struct scsi_qla_host *ha, ++ uint16_t fw_ddb_index, ++ uint16_t connection_id, ++ uint16_t option); + int qla4xxx_is_nvram_configuration_valid(struct scsi_qla_host * ha); + int qla4xxx_get_fw_version(struct scsi_qla_host * ha); + void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, +@@ -67,12 +75,18 @@ struct srb * qla4xxx_del_from_active_arr + uint32_t index); + void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb); + int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha); +-int qla4xxx_process_ddb_changed(struct scsi_qla_host * ha, +- uint32_t fw_ddb_index, uint32_t state); ++int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, ++ uint32_t state, uint32_t conn_error); + void qla4xxx_dump_buffer(void *b, uint32_t size); + int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry, int lun, uint16_t mrkr_mod); + ++void sp_put(struct scsi_qla_host *ha, struct srb *sp); ++int qla4_is_relogin_allowed(struct scsi_qla_host *ha, uint32_t conn_err); ++int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, ++ uint8_t outCount, uint32_t *mbx_cmd, ++ uint32_t *mbx_sts); ++ + extern int ql4xextended_error_logging; + extern int ql4xdiscoverywait; + extern int ql4xdontresethba; +--- a/drivers/scsi/qla4xxx/ql4_init.c ++++ b/drivers/scsi/qla4xxx/ql4_init.c +@@ -51,7 +51,7 @@ static void ql4xxx_set_mac_number(struct + * This routine deallocates and unlinks the specified ddb_entry from the + * adapter's + **/ +-static void qla4xxx_free_ddb(struct scsi_qla_host *ha, ++void qla4xxx_free_ddb(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry) + { + /* Remove device entry from list */ +@@ -95,6 +95,7 @@ void qla4xxx_free_ddb_list(struct scsi_q + **/ + int qla4xxx_init_rings(struct scsi_qla_host *ha) + { ++ uint16_t i; + unsigned long flags = 0; + + /* Initialize request queue. */ +@@ -123,6 +124,10 @@ int qla4xxx_init_rings(struct scsi_qla_h + writel(0, &ha->reg->rsp_q_out); + readl(&ha->reg->rsp_q_out); + ++ /* Initialize active array */ ++ for (i = 0; i < MAX_SRBS; i++) ++ ha->active_srb_array[i] = NULL; ++ + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return QLA_SUCCESS; +@@ -189,6 +194,71 @@ static int qla4xxx_init_local_data(struc + return qla4xxx_get_firmware_status(ha); + } + ++static uint8_t ++qla4xxx_wait_for_ip_config(struct scsi_qla_host *ha) ++{ ++ uint8_t ipv4_wait = 0; ++ uint8_t ipv6_wait = 0; ++ int8_t ip_address[IPv6_ADDR_LEN] = {0} ; ++ ++ /* If both IPv4 & IPv6 are enabled, possibly only one ++ * IP address may be acquired, so check to see if we ++ * need to wait for another */ ++ if (is_ipv4_enabled(ha) && is_ipv6_enabled(ha)) { ++ if (((ha->addl_fw_state & FW_ADDSTATE_DHCPv4_ENABLED) != 0) && ++ ((ha->addl_fw_state & FW_ADDSTATE_DHCPv4_LEASE_ACQUIRED) == 0)) { ++ ipv4_wait = 1; ++ } ++ if (((ha->ipv6_addl_options & IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE) != 0) && ++ ((ha->ipv6_link_local_state == IP_ADDRSTATE_ACQUIRING) || ++ (ha->ipv6_addr0_state == IP_ADDRSTATE_ACQUIRING) || ++ (ha->ipv6_addr1_state == IP_ADDRSTATE_ACQUIRING))) { ++ ++ ipv6_wait = 1; ++ ++ if ((ha->ipv6_link_local_state == IP_ADDRSTATE_PREFERRED) || ++ (ha->ipv6_addr0_state == IP_ADDRSTATE_PREFERRED) || ++ (ha->ipv6_addr1_state == IP_ADDRSTATE_PREFERRED)) { ++ DEBUG2(printk("scsi%ld: %s: " ++ "Preferred IP configured. Don't wait! \n", ++ ha->host_no, __func__)); ++ ipv6_wait = 0; ++ } ++ if (memcmp(&ha->ipv6_default_router_addr, ip_address, ++ IPv6_ADDR_LEN) == 0) { ++ DEBUG2(printk("scsi%ld: %s: " ++ "No Router configured. Don't wait! \n", ++ ha->host_no, __func__)); ++ ipv6_wait = 0; ++ } ++ if ((ha->ipv6_default_router_state == IPV6_RTRSTATE_MANUAL) && ++ (ha->ipv6_link_local_state == IP_ADDRSTATE_TENTATIVE) && ++ (memcmp(&ha->ipv6_link_local_addr, ++ &ha->ipv6_default_router_addr, 4) == 0)) { ++ DEBUG2(printk("scsi%ld: %s: LinkLocal Router & " ++ "IP configured. Don't wait! \n", ++ ha->host_no, __func__)); ++ ipv6_wait = 0; ++ } ++ } ++ if (ipv4_wait || ipv6_wait) { ++ DEBUG2(printk("scsi%ld: %s: Wait for additional IP(s) \"", ++ ha->host_no, __func__)); ++ if (ipv4_wait) ++ DEBUG2(printk("IPv4 ")); ++ if (ha->ipv6_link_local_state == IP_ADDRSTATE_ACQUIRING) ++ DEBUG2(printk("IPv6LinkLocal ")); ++ if (ha->ipv6_addr0_state == IP_ADDRSTATE_ACQUIRING) ++ DEBUG2(printk("IPv6Addr0 ")); ++ if (ha->ipv6_addr1_state == IP_ADDRSTATE_ACQUIRING) ++ DEBUG2(printk("IPv6Addr1 ")); ++ DEBUG2(printk("\"\n")); ++ } ++ } ++ ++ return (ipv4_wait|ipv6_wait); ++} ++ + static int qla4xxx_fw_ready(struct scsi_qla_host *ha) + { + uint32_t timeout_count; +@@ -226,38 +296,75 @@ static int qla4xxx_fw_ready(struct scsi_ + continue; + } + ++ if (ha->firmware_state & FW_STATE_WAIT_AUTOCONNECT) { ++ DEBUG2(printk("scsi%ld: %s: fwstate:" ++ "AUTOCONNECT in progress\n", ++ ha->host_no, __func__)); ++ } ++ ++ if (ha->firmware_state & FW_STATE_CONFIGURING_IP) { ++ DEBUG2(printk("scsi%ld: %s: fwstate: CONFIGURING IP\n", ++ ha->host_no, __func__)); ++ /* ++ * Check for link state after 15 secs and if link is still DOWN then, ++ * cable is unplugged. Ignore "DHCP in Progress/CONFIGURING IP" bit ++ * to check if firmware is in ready state or not after 15 secs. ++ * This is applicable for both 2.x & 3.x firmware ++ */ ++ if (timeout_count <= (ADAPTER_INIT_TOV - 15)) { ++ if (ha->addl_fw_state & FW_ADDSTATE_LINK_UP) { ++ DEBUG2(printk("scsi%ld: %s: LINK UP " ++ "(Cable plugged)\n", ++ ha->host_no, __func__)); ++ } ++ else if (ha->firmware_state & ++ (FW_STATE_CONFIGURING_IP | FW_STATE_READY)) { ++ DEBUG2(printk("scsi%ld: %s: LINK DOWN " ++ "(Cable unplugged)\n", ++ ha->host_no, __func__)); ++ ha->firmware_state = FW_STATE_READY; ++ } ++ } ++ } ++ + if (ha->firmware_state == FW_STATE_READY) { +- DEBUG2(dev_info(&ha->pdev->dev, "Firmware Ready..\n")); +- /* The firmware is ready to process SCSI commands. */ +- DEBUG2(dev_info(&ha->pdev->dev, +- "scsi%ld: %s: MEDIA TYPE - %s\n", +- ha->host_no, +- __func__, (ha->addl_fw_state & +- FW_ADDSTATE_OPTICAL_MEDIA) +- != 0 ? "OPTICAL" : "COPPER")); +- DEBUG2(dev_info(&ha->pdev->dev, +- "scsi%ld: %s: DHCP STATE Enabled " +- "%s\n", +- ha->host_no, __func__, +- (ha->addl_fw_state & +- FW_ADDSTATE_DHCP_ENABLED) != 0 ? +- "YES" : "NO")); +- DEBUG2(dev_info(&ha->pdev->dev, +- "scsi%ld: %s: LINK %s\n", +- ha->host_no, __func__, +- (ha->addl_fw_state & +- FW_ADDSTATE_LINK_UP) != 0 ? +- "UP" : "DOWN")); +- DEBUG2(dev_info(&ha->pdev->dev, +- "scsi%ld: %s: iSNS Service " +- "Started %s\n", +- ha->host_no, __func__, +- (ha->addl_fw_state & +- FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? +- "YES" : "NO")); ++ /* If DHCP IP Addr is available, retrieve it now. */ ++ if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags)) ++ qla4xxx_get_dhcp_ip_address(ha); ++ ++ if (!qla4xxx_wait_for_ip_config(ha) || timeout_count == 1) { ++ DEBUG2(dev_info(&ha->pdev->dev, "Firmware Ready..\n")); ++ /* The firmware is ready to process SCSI commands. */ ++ DEBUG2(dev_info(&ha->pdev->dev, ++ "scsi%ld: %s: MEDIA TYPE - %s\n", ++ ha->host_no, ++ __func__, (ha->addl_fw_state & ++ FW_ADDSTATE_OPTICAL_MEDIA) ++ != 0 ? "OPTICAL" : "COPPER")); ++ DEBUG2(dev_info(&ha->pdev->dev, ++ "scsi%ld: %s: DHCPv4 STATE Enabled " ++ "%s\n", ++ ha->host_no, __func__, ++ (ha->addl_fw_state & ++ FW_ADDSTATE_DHCPv4_ENABLED) != 0 ? ++ "YES" : "NO")); ++ DEBUG2(dev_info(&ha->pdev->dev, ++ "scsi%ld: %s: LINK %s\n", ++ ha->host_no, __func__, ++ (ha->addl_fw_state & ++ FW_ADDSTATE_LINK_UP) != 0 ? ++ "UP" : "DOWN")); ++ DEBUG2(dev_info(&ha->pdev->dev, ++ "scsi%ld: %s: iSNS Service " ++ "Started %s\n", ++ ha->host_no, __func__, ++ (ha->addl_fw_state & ++ FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? ++ "YES" : "NO")); + +- ready = 1; +- break; ++ ready = 1; ++ break; ++ } + } + DEBUG2(printk("scsi%ld: %s: waiting on fw, state=%x:%x - " + "seconds expired= %d\n", ha->host_no, __func__, +@@ -272,15 +379,19 @@ static int qla4xxx_fw_ready(struct scsi_ + msleep(1000); + } /* end of for */ + +- if (timeout_count == 0) ++ if (timeout_count <= 0) + DEBUG2(printk("scsi%ld: %s: FW Initialization timed out!\n", + ha->host_no, __func__)); + +- if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) { +- DEBUG2(printk("scsi%ld: %s: FW is reporting its waiting to" +- " grab an IP address from DHCP server\n", +- ha->host_no, __func__)); ++ if (ha->firmware_state & FW_STATE_CONFIGURING_IP) { ++ DEBUG2(printk("scsi%ld: %s: FW initialized, but is reporting " ++ "it's waiting to configure an IP address\n", ++ ha->host_no, __func__)); + ready = 1; ++ } else if (ha->firmware_state & FW_STATE_WAIT_AUTOCONNECT) { ++ DEBUG2(printk("scsi%ld: %s: FW initialized, but " ++ "auto-discovery still in process\n", ++ ha->host_no, __func__)); + } + + return ready; +@@ -343,11 +454,11 @@ static struct ddb_entry* qla4xxx_get_ddb + __func__, fw_ddb_index)); + list_for_each_entry(ddb_entry, &ha->ddb_list, list) { + if ((memcmp(ddb_entry->iscsi_name, fw_ddb_entry->iscsi_name, +- ISCSI_NAME_SIZE) == 0) && +- (ddb_entry->tpgt == +- le32_to_cpu(fw_ddb_entry->tgt_portal_grp)) && +- (memcmp(ddb_entry->isid, fw_ddb_entry->isid, +- sizeof(ddb_entry->isid)) == 0)) { ++ ISCSI_NAME_SIZE) == 0) && ++ (ddb_entry->tpgt == ++ le32_to_cpu(fw_ddb_entry->tgt_portal_grp)) && ++ (memcmp(ddb_entry->isid, fw_ddb_entry->isid, ++ sizeof(ddb_entry->isid)) == 0)) { + found++; + break; + } +@@ -387,6 +498,7 @@ static int qla4xxx_update_ddb_entry(stru + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + int status = QLA_ERROR; ++ uint32_t conn_err; + + if (ddb_entry == NULL) { + DEBUG2(printk("scsi%ld: %s: ddb_entry is NULL\n", ha->host_no, +@@ -405,12 +517,13 @@ static int qla4xxx_update_ddb_entry(stru + goto exit_update_ddb; + } + +- if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, ++ if ((qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, + fw_ddb_entry_dma, NULL, NULL, +- &ddb_entry->fw_ddb_device_state, NULL, ++ &ddb_entry->fw_ddb_device_state, &conn_err, + &ddb_entry->tcp_source_port_num, +- &ddb_entry->connection_id) == +- QLA_ERROR) { ++ &ddb_entry->connection_id) == QLA_SUCCESS)) ++ status = QLA_SUCCESS; ++ else { + DEBUG2(printk("scsi%ld: %s: failed get_ddb_entry for " + "fw_ddb_index %d\n", ha->host_no, __func__, + fw_ddb_index)); +@@ -418,7 +531,6 @@ static int qla4xxx_update_ddb_entry(stru + goto exit_update_ddb; + } + +- status = QLA_SUCCESS; + ddb_entry->target_session_id = le16_to_cpu(fw_ddb_entry->tsid); + ddb_entry->task_mgmt_timeout = + le16_to_cpu(fw_ddb_entry->def_timeout); +@@ -442,9 +554,26 @@ static int qla4xxx_update_ddb_entry(stru + memcpy(&ddb_entry->ip_addr[0], &fw_ddb_entry->ip_addr[0], + min(sizeof(ddb_entry->ip_addr), sizeof(fw_ddb_entry->ip_addr))); + +- DEBUG2(printk("scsi%ld: %s: ddb[%d] - State= %x status= %d.\n", +- ha->host_no, __func__, fw_ddb_index, +- ddb_entry->fw_ddb_device_state, status)); ++ if (ddb_entry->options & DDB_OPT_IPV6_DEVICE) { ++ memcpy(&ddb_entry->remote_ipv6_addr, ++ fw_ddb_entry->ip_addr, ++ min(sizeof(ddb_entry->remote_ipv6_addr), ++ sizeof(fw_ddb_entry->ip_addr))); ++ memcpy(&ddb_entry->link_local_ipv6_addr, ++ fw_ddb_entry->link_local_ipv6_addr, ++ min(sizeof(ddb_entry->link_local_ipv6_addr), ++ sizeof(fw_ddb_entry->link_local_ipv6_addr))); ++ } ++ ++ DEBUG2(dev_info(&ha->pdev->dev, "%s: DDB[%d] osIdx = %d " ++ "State %04x ConnErr %08x " ++ NIPQUAD_FMT ":%04d \"%s\"\n", ++ __func__, fw_ddb_index, ++ ddb_entry->os_target_id, ++ ddb_entry->fw_ddb_device_state, conn_err, ++ NIPQUAD(fw_ddb_entry->ip_addr), ++ le16_to_cpu(fw_ddb_entry->port), ++ fw_ddb_entry->iscsi_name)); + + exit_update_ddb: + if (fw_ddb_entry) +@@ -492,6 +621,39 @@ static struct ddb_entry * qla4xxx_alloc_ + } + + /** ++ * qla4_is_relogin_allowed - Are we allowed to login? ++ * @ha: Pointer to host adapter structure. ++ * @conn_err: Last connection error associated with the ddb ++ * ++ * This routine tests the given connection error to determine if ++ * we are allowed to login. ++ **/ ++int qla4_is_relogin_allowed(struct scsi_qla_host *ha, uint32_t conn_err) ++{ ++ uint32_t err_code, login_rsp_sts_class; ++ int relogin = 1; ++ ++ err_code = ((conn_err & 0x00ff0000) >> 16); ++ login_rsp_sts_class = ((conn_err & 0x0000ff00) >> 8); ++ if (err_code == 0x1c || err_code == 0x06) { ++ DEBUG2(dev_info(&ha->pdev->dev, ++ ": conn_err=0x%08x, send target completed or access" ++ " denied failure\n", conn_err)); ++ relogin = 0; ++ } ++ if ((err_code == 0x08) && (login_rsp_sts_class == 0x02)) { ++ /* Login Response PDU returned an error. ++ Login Response Status in Error Code Detail ++ indicates login should not be retried.*/ ++ DEBUG2(dev_info(&ha->pdev->dev, ++ ": conn_err=0x%08x, do not retry relogin\n", conn_err)); ++ relogin = 0; ++ } ++ ++ return relogin; ++} ++ ++/** + * qla4xxx_configure_ddbs - builds driver ddb list + * @ha: Pointer to host adapter structure. + * +@@ -505,15 +667,25 @@ static int qla4xxx_build_ddb_list(struct + uint32_t fw_ddb_index = 0; + uint32_t next_fw_ddb_index = 0; + uint32_t ddb_state; +- uint32_t conn_err, err_code; ++ uint32_t conn_err; + struct ddb_entry *ddb_entry; ++ struct dev_db_entry *fw_ddb_entry = NULL; ++ dma_addr_t fw_ddb_entry_dma; + uint32_t new_tgt; ++ uint32_t ipv6_device; ++ ++ fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), ++ &fw_ddb_entry_dma, GFP_KERNEL); ++ if (fw_ddb_entry == NULL) { ++ DEBUG2(dev_info(&ha->pdev->dev, "%s: DMA alloc failed\n", __func__)); ++ return QLA_ERROR; ++ } + + dev_info(&ha->pdev->dev, "Initializing DDBs ...\n"); + for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES; + fw_ddb_index = next_fw_ddb_index) { + /* First, let's see if a device exists here */ +- if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, ++ if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, 0, NULL, + &next_fw_ddb_index, &ddb_state, + &conn_err, NULL, NULL) == + QLA_ERROR) { +@@ -533,13 +705,11 @@ static int qla4xxx_build_ddb_list(struct + /* Try and login to device */ + DEBUG2(printk("scsi%ld: %s: Login to DDB[%d]\n", + ha->host_no, __func__, fw_ddb_index)); +- err_code = ((conn_err & 0x00ff0000) >> 16); +- if (err_code == 0x1c || err_code == 0x06) { +- DEBUG2(printk("scsi%ld: %s send target " +- "completed " +- "or access denied failure\n", +- ha->host_no, __func__)); +- } else { ++ ipv6_device = le16_to_cpu(fw_ddb_entry->options) & ++ DDB_OPT_IPV6_DEVICE; ++ if (qla4_is_relogin_allowed(ha, conn_err) && ++ ((!ipv6_device && *((uint32_t *)fw_ddb_entry->ip_addr)) ++ || ipv6_device)) { + qla4xxx_set_ddb_entry(ha, fw_ddb_index, 0); + if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, + NULL, 0, NULL, &next_fw_ddb_index, +@@ -599,7 +769,6 @@ next_one: + struct qla4_relog_scan { + int halt_wait; + uint32_t conn_err; +- uint32_t err_code; + uint32_t fw_ddb_index; + uint32_t next_fw_ddb_index; + uint32_t fw_ddb_device_state; +@@ -609,18 +778,7 @@ static int qla4_test_rdy(struct scsi_qla + { + struct ddb_entry *ddb_entry; + +- /* +- * Don't want to do a relogin if connection +- * error is 0x1c. +- */ +- rs->err_code = ((rs->conn_err & 0x00ff0000) >> 16); +- if (rs->err_code == 0x1c || rs->err_code == 0x06) { +- DEBUG2(printk( +- "scsi%ld: %s send target" +- " completed or " +- "access denied failure\n", +- ha->host_no, __func__)); +- } else { ++ if (qla4_is_relogin_allowed(ha, rs->conn_err)) { + /* We either have a device that is in + * the process of relogging in or a + * device that is waiting to be +@@ -908,7 +1066,7 @@ static void qla4x00_pci_config(struct sc + static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) + { + int status = QLA_ERROR; +- uint32_t max_wait_time; ++ unsigned long max_wait_time; + unsigned long flags; + uint32_t mbox_status; + +@@ -927,6 +1085,13 @@ static int qla4xxx_start_firmware_from_f + ha->host_no, __func__)); + + spin_lock_irqsave(&ha->hardware_lock, flags); ++ /* ++ * Firmware must be informed that the driver supports ++ * ACB firmware features while starting firmware. ++ * If the firmware also supports these features it will ++ * be indicated in the IFCB offset 0x3A (acb_version). ++ */ ++ writel(ACB_SUPPORTED, &ha->reg->mailbox[6]); + writel(jiffies, &ha->reg->mailbox[7]); + if (is_qla4022(ha) | is_qla4032(ha)) + writel(set_rmask(NVR_WRITE_ENABLE), +@@ -940,7 +1105,10 @@ static int qla4xxx_start_firmware_from_f + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Wait for firmware to come UP. */ +- max_wait_time = FIRMWARE_UP_TOV * 4; ++ DEBUG2(printk("scsi%ld: %s: Wait up to %d seconds for " ++ "boot firmware to complete... \n", ++ ha->host_no, __func__, FIRMWARE_UP_TOV)); ++ max_wait_time = jiffies + (FIRMWARE_UP_TOV * HZ); + do { + uint32_t ctrl_status; + +@@ -955,12 +1123,11 @@ static int qla4xxx_start_firmware_from_f + break; + + DEBUG2(printk("scsi%ld: %s: Waiting for boot firmware to " +- "complete... ctrl_sts=0x%x, remaining=%d\n", +- ha->host_no, __func__, ctrl_status, +- max_wait_time)); ++ "complete... ctrl_sts=0x%x\n", ++ ha->host_no, __func__, ctrl_status)); + +- msleep(250); +- } while ((max_wait_time--)); ++ msleep_interruptible(250); ++ } while (!time_after_eq(jiffies, max_wait_time)); + + if (mbox_status == MBOX_STS_COMMAND_COMPLETE) { + DEBUG(printk("scsi%ld: %s: Firmware has started\n", +@@ -1141,6 +1308,7 @@ int qla4xxx_initialize_adapter(struct sc + int status = QLA_ERROR; + int8_t ip_address[IP_ADDR_LEN] = {0} ; + ++ clear_bit(AF_ONLINE, &ha->flags); + ha->eeprom_cmd_data = 0; + + qla4x00_pci_config(ha); +@@ -1166,7 +1334,7 @@ int qla4xxx_initialize_adapter(struct sc + * the ddb_list and wait for DHCP lease acquired aen to come in + * followed by 0x8014 aen" to trigger the tgt discovery process. + */ +- if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) ++ if (ha->firmware_state & FW_STATE_CONFIGURING_IP) + goto exit_init_online; + + /* Skip device discovery if ip and subnet is zero */ +@@ -1270,8 +1438,8 @@ static void qla4xxx_add_device_dynamical + * + * This routine processes a Decive Database Changed AEN Event. + **/ +-int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, +- uint32_t fw_ddb_index, uint32_t state) ++int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, ++ uint32_t state, uint32_t conn_err) + { + struct ddb_entry * ddb_entry; + uint32_t old_fw_ddb_device_state; +@@ -1318,19 +1486,24 @@ int qla4xxx_process_ddb_changed(struct s + * the device came back. + */ + } else { +- /* Device went away, try to relogin. */ +- /* Mark device missing */ +- if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) ++ /* Device went away, mark device missing */ ++ if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) { ++ DEBUG2(dev_info(&ha->pdev->dev, "%s mark missing " ++ "ddb_entry 0x%p sess 0x%p conn 0x%p\n", ++ __func__, ddb_entry, ++ ddb_entry->sess, ddb_entry->conn)); + qla4xxx_mark_device_missing(ha, ddb_entry); ++ } ++ + /* + * Relogin if device state changed to a not active state. +- * However, do not relogin if this aen is a result of an IOCTL +- * logout (DF_NO_RELOGIN) or if this is a discovered device. ++ * However, do not relogin if a RELOGIN is in process, or ++ * we are not allowed to relogin to this DDB. + */ + if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_FAILED && + !test_bit(DF_RELOGIN, &ddb_entry->flags) && + !test_bit(DF_NO_RELOGIN, &ddb_entry->flags) && +- !test_bit(DF_ISNS_DISCOVERED, &ddb_entry->flags)) { ++ qla4_is_relogin_allowed(ha, conn_err)) { + /* + * This triggers a relogin. After the relogin_timer + * expires, the relogin gets scheduled. We must wait a +@@ -1338,7 +1511,7 @@ int qla4xxx_process_ddb_changed(struct s + * with failed device_state or a logout response before + * we can issue another relogin. + */ +- /* Firmware padds this timeout: (time2wait +1). ++ /* Firmware pads this timeout: (time2wait +1). + * Driver retry to login should be longer than F/W. + * Otherwise F/W will fail + * set_ddb() mbx cmd with 0x4005 since it still +--- a/drivers/scsi/qla4xxx/ql4_inline.h ++++ b/drivers/scsi/qla4xxx/ql4_inline.h +@@ -35,6 +35,34 @@ qla4xxx_lookup_ddb_by_fw_index(struct sc + return ddb_entry; + } + ++/* ++ * The MBOX_CMD_CLEAR_DATABASE_ENTRY (0x31) mailbox command does not ++ * result in an AEN, so we need to process it seperately. ++ */ ++static inline void qla4xxx_check_for_clear_ddb(struct scsi_qla_host *ha, ++ uint32_t *mbox_cmd) ++{ ++ uint32_t fw_ddb_index; ++ struct ddb_entry *ddb_entry = NULL; ++ ++ if (mbox_cmd[0] == MBOX_CMD_CLEAR_DATABASE_ENTRY) { ++ ++ fw_ddb_index = mbox_cmd[1]; ++ ++ if (fw_ddb_index < MAX_DDB_ENTRIES) ++ ddb_entry = ha->fw_ddb_index_map[fw_ddb_index]; ++ ++ if (ddb_entry) { ++ dev_info(&ha->pdev->dev, "%s: ddb[%d] os[%d] freed\n", ++ __func__, ddb_entry->fw_ddb_index, ++ ddb_entry->os_target_id); ++ set_bit(DF_REMOVE, &ddb_entry->flags); ++ set_bit(DPC_REMOVE_DEVICE, &ha->dpc_flags); ++ queue_work(ha->dpc_thread, &ha->dpc_work); ++ } ++ } ++} ++ + static inline void + __qla4xxx_enable_intrs(struct scsi_qla_host *ha) + { +@@ -82,3 +110,46 @@ qla4xxx_disable_intrs(struct scsi_qla_ho + __qla4xxx_disable_intrs(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } ++ ++static inline void ++qla4xxx_remove_device(struct scsi_qla_host *ha) ++{ ++ struct ddb_entry *ddb_entry, *dtemp; ++ ++ if (test_and_clear_bit(DPC_REMOVE_DEVICE, &ha->dpc_flags)) { ++ list_for_each_entry_safe(ddb_entry, dtemp, ++ &ha->ddb_list, list) { ++ if (test_and_clear_bit(DF_REMOVE, &ddb_entry->flags)) { ++ dev_info(&ha->pdev->dev, ++ "%s: ddb[%d] os[%d] - removed\n", ++ __func__, ddb_entry->fw_ddb_index, ++ ddb_entry->os_target_id); ++ qla4xxx_free_ddb(ha, ddb_entry); ++ } ++ } ++ } ++} ++ ++static void ++ql4_get_aen_log(struct scsi_qla_host *ha, struct ql4_aen_log *aenl) ++{ ++ if (aenl) { ++ memcpy(aenl, &ha->aen_log, sizeof (ha->aen_log)); ++ ha->aen_log.count = 0; ++ } ++} ++ ++static inline int ++qla4xxx_ioctl_init(struct scsi_qla_host *ha) ++{ ++ ha->ql4mbx = qla4xxx_mailbox_command; ++ ha->ql4cmd = qla4xxx_send_command_to_isp; ++ ha->ql4getaenlog = ql4_get_aen_log; ++ return 0; ++} ++ ++static inline void ++qla4xxx_ioctl_exit(struct scsi_qla_host *ha) ++{ ++ return; ++} +--- a/drivers/scsi/qla4xxx/ql4_iocb.c ++++ b/drivers/scsi/qla4xxx/ql4_iocb.c +@@ -209,6 +209,7 @@ int qla4xxx_send_command_to_isp(struct s + int nseg; + uint16_t tot_dsds; + uint16_t req_cnt; ++ uint16_t i; + unsigned long flags; + uint32_t index; + char tag[2]; +@@ -221,7 +222,24 @@ int qla4xxx_send_command_to_isp(struct s + /* Acquire hardware specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); + +- index = (uint32_t)cmd->request->tag; ++ //index = (uint32_t)cmd->request->tag; ++ index = ha->current_active_index; ++ for (i = 0; i < MAX_SRBS; i++) { ++ index++; ++ if (index == MAX_SRBS) ++ index = 1; ++ if (ha->active_srb_array[index] == 0) { ++ ha->current_active_index = index; ++ break; ++ } ++ } ++ ++ if (i >= MAX_SRBS) { ++ printk(KERN_INFO "scsi%ld: %s: NO more SRB entries used " ++ "iocbs=%d, \n reqs remaining=%d\n", ha->host_no, ++ __func__, ha->iocb_cnt, ha->req_q_count); ++ goto queuing_error; ++ } + + /* + * Check to see if adapter is online before placing request on +@@ -300,6 +318,7 @@ int qla4xxx_send_command_to_isp(struct s + wmb(); + + srb->cmd->host_scribble = (unsigned char *)srb; ++ ha->active_srb_array[index] = srb; + + /* update counters */ + srb->state = SRB_ACTIVE_STATE; +--- a/drivers/scsi/qla4xxx/ql4_isr.c ++++ b/drivers/scsi/qla4xxx/ql4_isr.c +@@ -9,6 +9,7 @@ + #include "ql4_glbl.h" + #include "ql4_dbg.h" + #include "ql4_inline.h" ++#include + + /** + * qla4xxx_copy_sense - copy sense data into cmd sense buffer +@@ -97,7 +98,7 @@ qla4xxx_status_cont_entry(struct scsi_ql + + /* Place command on done queue. */ + if (srb->req_sense_len == 0) { +- qla4xxx_srb_compl(ha, srb); ++ sp_put(ha, srb); + ha->status_srb = NULL; + } + } +@@ -329,7 +330,7 @@ status_entry_exit: + /* complete the request, if not waiting for status_continuation pkt */ + srb->cc_stat = sts_entry->completionStatus; + if (ha->status_srb == NULL) +- qla4xxx_srb_compl(ha, srb); ++ sp_put(ha, srb); + } + + /** +@@ -344,6 +345,9 @@ static void qla4xxx_process_response_que + uint32_t count = 0; + struct srb *srb = NULL; + struct status_entry *sts_entry; ++ struct async_pdu_iocb *apdu; ++ struct iscsi_hdr *pdu_hdr; ++ struct async_msg_pdu_iocb *apdu_iocb; + + /* Process all responses from response queue */ + while ((ha->response_in = +@@ -371,6 +375,34 @@ static void qla4xxx_process_response_que + case ET_PASSTHRU_STATUS: + break; + ++ case ET_ASYNC_PDU: ++ apdu = (struct async_pdu_iocb *)sts_entry; ++ if (apdu->status != ASYNC_PDU_IOCB_STS_OK) ++ break; ++ ++ pdu_hdr = (struct iscsi_hdr *)apdu->iscsi_pdu_hdr; ++ if (pdu_hdr->hlength || pdu_hdr->dlength[0] || ++ pdu_hdr->dlength[1] || pdu_hdr->dlength[2]){ ++ apdu_iocb = kmalloc(sizeof(struct async_msg_pdu_iocb), ++ GFP_ATOMIC); ++ if (apdu_iocb) { ++ memcpy(apdu_iocb->iocb, apdu, ++ sizeof(struct async_pdu_iocb)); ++ list_add_tail(&apdu_iocb->list, ++ &ha->async_iocb_list); ++ DEBUG2(printk("scsi%ld:" ++ "%s: schedule async msg pdu\n", ++ ha->host_no, __func__)); ++ set_bit(DPC_ASYNC_MSG_PDU, ++ &ha->dpc_flags); ++ } else { ++ DEBUG2(printk("scsi%ld:" ++ "%s: unable to alloc ASYNC PDU\n", ++ ha->host_no, __func__)); ++ } ++ } ++ break; ++ + case ET_STATUS_CONTINUATION: + qla4xxx_status_cont_entry(ha, + (struct status_cont_entry *) sts_entry); +@@ -393,7 +425,7 @@ static void qla4xxx_process_response_que + /* ETRY normally by sending it back with + * DID_BUS_BUSY */ + srb->cmd->result = DID_BUS_BUSY << 16; +- qla4xxx_srb_compl(ha, srb); ++ sp_put(ha, srb); + break; + + case ET_CONTINUE: +@@ -498,15 +530,20 @@ static void qla4xxx_isr_decode_mailbox(s + break; + + case MBOX_ASTS_LINK_UP: +- DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n", +- ha->host_no, mbox_status)); + set_bit(AF_LINK_UP, &ha->flags); ++ if (test_bit(AF_INIT_DONE, &ha->flags)) ++ set_bit(DPC_LINK_CHANGED, &ha->dpc_flags); ++ ++ DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n", ++ ha->host_no, mbox_status)); + break; + + case MBOX_ASTS_LINK_DOWN: +- DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n", +- ha->host_no, mbox_status)); + clear_bit(AF_LINK_UP, &ha->flags); ++ set_bit(DPC_LINK_CHANGED, &ha->dpc_flags); ++ ++ DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n", ++ ha->host_no, mbox_status)); + break; + + case MBOX_ASTS_HEARTBEAT: +@@ -831,7 +868,7 @@ void qla4xxx_process_aen(struct scsi_qla + qla4xxx_reinitialize_ddb_list(ha); + } else if (mbox_sts[1] == 1) { /* Specific device. */ + qla4xxx_process_ddb_changed(ha, mbox_sts[2], +- mbox_sts[3]); ++ mbox_sts[3], mbox_sts[4]); + } + break; + } +--- a/drivers/scsi/qla4xxx/ql4_mbx.c ++++ b/drivers/scsi/qla4xxx/ql4_mbx.c +@@ -23,7 +23,7 @@ + * If outCount is 0, this routine completes successfully WITHOUT waiting + * for the mailbox command to complete. + **/ +-static int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, ++int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, + uint8_t outCount, uint32_t *mbx_cmd, + uint32_t *mbx_sts) + { +@@ -164,6 +164,8 @@ static int qla4xxx_mailbox_command(struc + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + mbox_exit: ++ if (status == QLA_SUCCESS) ++ qla4xxx_check_for_clear_ddb(ha, mbx_cmd); + mutex_lock(&ha->mbox_sem); + clear_bit(AF_MBOX_COMMAND, &ha->flags); + mutex_unlock(&ha->mbox_sem); +@@ -173,107 +175,284 @@ mbox_exit: + } + + /** ++ * qla4xxx_issue_iocb - issue mailbox iocb command ++ * @ha: adapter state pointer. ++ * @buffer: buffer pointer. ++ * @phys_addr: physical address of buffer. ++ * @size: size of buffer. ++ * ++ * Issues iocbs via mailbox commands. ++ * TARGET_QUEUE_LOCK must be released. ++ * ADAPTER_STATE_LOCK must be released. ++ **/ ++int ++qla4xxx_issue_iocb(struct scsi_qla_host *ha, uint32_t comp_offset, ++ dma_addr_t phys_addr) ++{ ++ uint32_t mbox_cmd[MBOX_REG_COUNT]; ++ uint32_t mbox_sts[MBOX_REG_COUNT]; ++ int status; ++ ++ memset(&mbox_cmd, 0, sizeof(mbox_cmd)); ++ memset(&mbox_sts, 0, sizeof(mbox_sts)); ++ ++ mbox_cmd[0] = MBOX_CMD_EXECUTE_IOCB_A64; ++ mbox_cmd[1] = comp_offset; ++ mbox_cmd[2] = LSDW(phys_addr); ++ mbox_cmd[3] = MSDW(phys_addr); ++ ++ status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]); ++ return status; ++} ++ ++int qla4xxx_conn_close_sess_logout(struct scsi_qla_host *ha, ++ uint16_t fw_ddb_index, ++ uint16_t connection_id, ++ uint16_t option) ++{ ++ uint32_t mbox_cmd[MBOX_REG_COUNT]; ++ uint32_t mbox_sts[MBOX_REG_COUNT]; ++ ++ memset(&mbox_cmd, 0, sizeof(mbox_cmd)); ++ memset(&mbox_sts, 0, sizeof(mbox_sts)); ++ ++ mbox_cmd[0] = MBOX_CMD_CONN_CLOSE_SESS_LOGOUT; ++ mbox_cmd[1] = fw_ddb_index; ++ mbox_cmd[2] = connection_id; ++ mbox_cmd[3] = LOGOUT_OPTION_RESET; ++ ++ if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 2, &mbox_cmd[0], &mbox_sts[0]) != ++ QLA_SUCCESS) { ++ DEBUG2(printk("scsi%ld: %s: MBOX_CMD_CONN_CLOSE_SESS_LOGOUT " ++ "option %04x failed sts %04X %04X", ++ ha->host_no, __func__, ++ option, mbox_sts[0], mbox_sts[1])); ++ if (mbox_sts[0] == 0x4005) ++ DEBUG2(printk("%s reason %04X\n", __func__, ++ mbox_sts[1])); ++ } ++ return QLA_SUCCESS; ++} ++ ++uint8_t ++qla4xxx_set_ifcb(struct scsi_qla_host *ha, uint32_t *mbox_cmd, uint32_t *mbox_sts, ++ dma_addr_t init_fw_cb_dma) ++{ ++ memset(mbox_cmd, 0, sizeof(mbox_cmd[0]) * MBOX_REG_COUNT); ++ memset(mbox_sts, 0, sizeof(mbox_sts[0]) * MBOX_REG_COUNT); ++ mbox_cmd[0] = MBOX_CMD_INITIALIZE_FIRMWARE; ++ mbox_cmd[1] = 0; ++ mbox_cmd[2] = LSDW(init_fw_cb_dma); ++ mbox_cmd[3] = MSDW(init_fw_cb_dma); ++ if (ha->ifcb_size > LEGACY_IFCB_SIZE) { ++ mbox_cmd[4] = ha->ifcb_size; ++ mbox_cmd[5] = (IFCB_VER_MAX << 8) | IFCB_VER_MIN; ++ } ++ ++ if (qla4xxx_mailbox_command(ha, 6, 6, mbox_cmd, mbox_sts) ++ != QLA_SUCCESS) { ++ DEBUG2(printk("scsi%ld: %s: " ++ "MBOX_CMD_INITIALIZE_FIRMWARE failed w/ status %04X\n", ++ ha->host_no, __func__, mbox_sts[0])); ++ return (QLA_ERROR); ++ } ++ return (QLA_SUCCESS); ++} ++ ++uint8_t ++qla4xxx_get_ifcb(struct scsi_qla_host *ha, uint32_t *mbox_cmd, uint32_t *mbox_sts, ++ dma_addr_t init_fw_cb_dma) ++{ ++ int i; ++ ++ memset(mbox_cmd, 0, sizeof(mbox_cmd[0]) * MBOX_REG_COUNT); ++ memset(mbox_sts, 0, sizeof(mbox_sts[0]) * MBOX_REG_COUNT); ++ mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; ++ mbox_cmd[2] = LSDW(init_fw_cb_dma); ++ mbox_cmd[3] = MSDW(init_fw_cb_dma); ++ if (init_fw_cb_dma != 0 && ha->ifcb_size > LEGACY_IFCB_SIZE) ++ mbox_cmd[4] = ha->ifcb_size; ++ ++ if (qla4xxx_mailbox_command(ha, 5, 5, mbox_cmd, mbox_sts) ++ != QLA_SUCCESS) { ++ if (init_fw_cb_dma == 0 && ++ mbox_sts[0] == MBOX_STS_COMMAND_ERROR) ++ return (QLA_SUCCESS); ++ ++ DEBUG2(printk("scsi%ld: %s: " ++ "MBOX_CMD_GET_INIT_FW_CTRL_BLOCK failed w/ status %04X\n", ++ ha->host_no, __func__, mbox_sts[0])); ++ ++ for (i = 0; i < MBOX_REG_COUNT; i++) { ++ DEBUG2(printk("mbox_cmd[%d] = %08x, " ++ "mbox_sts[%d] = %08x\n", ++ i, mbox_cmd[i], i, mbox_sts[i])); ++ } ++ ++ return (QLA_ERROR); ++ } ++ return (QLA_SUCCESS); ++} ++ ++void ++qla4xxx_update_local_ip(struct scsi_qla_host *ha, ++ struct addr_ctrl_blk *init_fw_cb) ++{ ++ /* Save IPv4 Address Info */ ++ memcpy(ha->ip_address, init_fw_cb->ipv4_addr, ++ min(sizeof(ha->ip_address), sizeof(init_fw_cb->ipv4_addr))); ++ memcpy(ha->subnet_mask, init_fw_cb->ipv4_subnet, ++ min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->ipv4_subnet))); ++ memcpy(ha->gateway, init_fw_cb->ipv4_gw_addr, ++ min(sizeof(ha->gateway), sizeof(init_fw_cb->ipv4_gw_addr))); ++ ++ if (is_ipv6_enabled(ha)) { ++ /* Save IPv6 Address */ ++ ha->ipv6_link_local_state = init_fw_cb->ipv6_lnk_lcl_addr_state; ++ ha->ipv6_addr0_state = init_fw_cb->ipv6_addr0_state; ++ ha->ipv6_addr1_state = init_fw_cb->ipv6_addr1_state; ++ ha->ipv6_default_router_state = init_fw_cb->ipv6_dflt_rtr_state; ++ ha->ipv6_link_local_addr.in6_u.u6_addr8[0] = 0xFE; ++ ha->ipv6_link_local_addr.in6_u.u6_addr8[1] = 0x80; ++ ++ memcpy(&ha->ipv6_link_local_addr.in6_u.u6_addr8[8], init_fw_cb->ipv6_if_id, ++ min(sizeof(ha->ipv6_link_local_addr)/2, sizeof(init_fw_cb->ipv6_if_id))); ++ memcpy(&ha->ipv6_addr0, init_fw_cb->ipv6_addr0, ++ min(sizeof(ha->ipv6_addr0), sizeof(init_fw_cb->ipv6_addr0))); ++ memcpy(&ha->ipv6_addr1, init_fw_cb->ipv6_addr1, ++ min(sizeof(ha->ipv6_addr1), sizeof(init_fw_cb->ipv6_addr1))); ++ memcpy(&ha->ipv6_default_router_addr, init_fw_cb->ipv6_dflt_rtr_addr, ++ min(sizeof(ha->ipv6_default_router_addr), sizeof(init_fw_cb->ipv6_dflt_rtr_addr))); ++ } ++} ++ ++uint8_t ++qla4xxx_update_local_ifcb(struct scsi_qla_host *ha, ++ uint32_t *mbox_cmd, ++ uint32_t *mbox_sts, ++ struct addr_ctrl_blk *init_fw_cb, ++ dma_addr_t init_fw_cb_dma) ++{ ++ if (qla4xxx_get_ifcb(ha, mbox_cmd, mbox_sts, init_fw_cb_dma) ++ != QLA_SUCCESS) { ++ DEBUG2(printk("scsi%ld: %s: Failed to get init_fw_ctrl_blk\n", ++ ha->host_no, __func__)); ++ return (QLA_ERROR); ++ } ++ ++ DEBUG2(qla4xxx_dump_buffer(init_fw_cb, sizeof(struct addr_ctrl_blk))); ++ ++ /* Save some info in adapter structure. */ ++ ha->acb_version = init_fw_cb->acb_version; ++ ha->firmware_options = le16_to_cpu(init_fw_cb->fw_options); ++ ha->tcp_options = le16_to_cpu(init_fw_cb->ipv4_tcp_opts); ++ ha->ipv4_options = le16_to_cpu(init_fw_cb->ipv4_ip_opts); ++ ha->ipv4_addr_state = le16_to_cpu(init_fw_cb->ipv4_addr_state); ++ ha->heartbeat_interval = init_fw_cb->hb_interval; ++ memcpy(ha->name_string, init_fw_cb->iscsi_name, ++ min(sizeof(ha->name_string), ++ sizeof(init_fw_cb->iscsi_name))); ++ /*memcpy(ha->alias, init_fw_cb->Alias, ++ min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));*/ ++ ++ /* Save Command Line Paramater info */ ++ ha->port_down_retry_count = le16_to_cpu(init_fw_cb->conn_ka_timeout); ++ ha->discovery_wait = ql4xdiscoverywait; ++ ++ if (ha->acb_version == ACB_SUPPORTED) { ++ ha->ipv6_options = init_fw_cb->ipv6_opts; ++ ha->ipv6_addl_options = init_fw_cb->ipv6_addtl_opts; ++ } ++ qla4xxx_update_local_ip(ha, init_fw_cb); ++ ++ return (QLA_SUCCESS); ++} ++ ++/** + * qla4xxx_initialize_fw_cb - initializes firmware control block. + * @ha: Pointer to host adapter structure. + **/ + int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha) + { +- struct init_fw_ctrl_blk *init_fw_cb; ++ struct addr_ctrl_blk *init_fw_cb; + dma_addr_t init_fw_cb_dma; + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + int status = QLA_ERROR; + ++ /* Default to Legacy IFCB Size */ ++ ha->ifcb_size = LEGACY_IFCB_SIZE; ++ + init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, +- sizeof(struct init_fw_ctrl_blk), ++ sizeof(struct addr_ctrl_blk), + &init_fw_cb_dma, GFP_KERNEL); + if (init_fw_cb == NULL) { + DEBUG2(printk("scsi%ld: %s: Unable to alloc init_cb\n", + ha->host_no, __func__)); + return 10; + } +- memset(init_fw_cb, 0, sizeof(struct init_fw_ctrl_blk)); ++ memset(init_fw_cb, 0, sizeof(struct addr_ctrl_blk)); + + /* Get Initialize Firmware Control Block. */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + +- mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; +- mbox_cmd[2] = LSDW(init_fw_cb_dma); +- mbox_cmd[3] = MSDW(init_fw_cb_dma); +- mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); +- +- if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) != +- QLA_SUCCESS) { ++ /* ++ * Determine if larger IFCB is supported ++ */ ++ if (qla4xxx_get_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) ++ != QLA_SUCCESS) { + dma_free_coherent(&ha->pdev->dev, +- sizeof(struct init_fw_ctrl_blk), ++ sizeof(struct addr_ctrl_blk), + init_fw_cb, init_fw_cb_dma); +- return status; ++ goto exit_init_fw_cb; ++ } ++ ++ if (mbox_sts[0] == MBOX_STS_COMMAND_ERROR && ++ mbox_sts[4] > LEGACY_IFCB_SIZE) { ++ /* Supports larger ifcb size */ ++ ha->ifcb_size = mbox_sts[4]; + } + + /* Initialize request and response queues. */ + qla4xxx_init_rings(ha); + + /* Fill in the request and response queue information. */ +- init_fw_cb->pri.rqq_consumer_idx = cpu_to_le16(ha->request_out); +- init_fw_cb->pri.compq_producer_idx = cpu_to_le16(ha->response_in); +- init_fw_cb->pri.rqq_len = __constant_cpu_to_le16(REQUEST_QUEUE_DEPTH); +- init_fw_cb->pri.compq_len = __constant_cpu_to_le16(RESPONSE_QUEUE_DEPTH); +- init_fw_cb->pri.rqq_addr_lo = cpu_to_le32(LSDW(ha->request_dma)); +- init_fw_cb->pri.rqq_addr_hi = cpu_to_le32(MSDW(ha->request_dma)); +- init_fw_cb->pri.compq_addr_lo = cpu_to_le32(LSDW(ha->response_dma)); +- init_fw_cb->pri.compq_addr_hi = cpu_to_le32(MSDW(ha->response_dma)); +- init_fw_cb->pri.shdwreg_addr_lo = +- cpu_to_le32(LSDW(ha->shadow_regs_dma)); +- init_fw_cb->pri.shdwreg_addr_hi = +- cpu_to_le32(MSDW(ha->shadow_regs_dma)); ++ init_fw_cb->rqq_consumer_idx = cpu_to_le16(ha->request_out); ++ init_fw_cb->compq_producer_idx = cpu_to_le16(ha->response_in); ++ init_fw_cb->rqq_len = __constant_cpu_to_le16(REQUEST_QUEUE_DEPTH); ++ init_fw_cb->compq_len = __constant_cpu_to_le16(RESPONSE_QUEUE_DEPTH); ++ init_fw_cb->rqq_addr_lo = cpu_to_le32(LSDW(ha->request_dma)); ++ init_fw_cb->rqq_addr_hi = cpu_to_le32(MSDW(ha->request_dma)); ++ init_fw_cb->compq_addr_lo = cpu_to_le32(LSDW(ha->response_dma)); ++ init_fw_cb->compq_addr_hi = cpu_to_le32(MSDW(ha->response_dma)); ++ init_fw_cb->shdwreg_addr_lo = cpu_to_le32(LSDW(ha->shadow_regs_dma)); ++ init_fw_cb->shdwreg_addr_hi = cpu_to_le32(MSDW(ha->shadow_regs_dma)); + + /* Set up required options. */ +- init_fw_cb->pri.fw_options |= ++ init_fw_cb->fw_options |= + __constant_cpu_to_le16(FWOPT_SESSION_MODE | + FWOPT_INITIATOR_MODE); +- init_fw_cb->pri.fw_options &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE); +- +- /* Save some info in adapter structure. */ +- ha->firmware_options = le16_to_cpu(init_fw_cb->pri.fw_options); +- ha->tcp_options = le16_to_cpu(init_fw_cb->pri.ipv4_tcp_opts); +- ha->heartbeat_interval = init_fw_cb->pri.hb_interval; +- memcpy(ha->ip_address, init_fw_cb->pri.ipv4_addr, +- min(sizeof(ha->ip_address), sizeof(init_fw_cb->pri.ipv4_addr))); +- memcpy(ha->subnet_mask, init_fw_cb->pri.ipv4_subnet, +- min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->pri.ipv4_subnet))); +- memcpy(ha->gateway, init_fw_cb->pri.ipv4_gw_addr, +- min(sizeof(ha->gateway), sizeof(init_fw_cb->pri.ipv4_gw_addr))); +- memcpy(ha->name_string, init_fw_cb->pri.iscsi_name, +- min(sizeof(ha->name_string), +- sizeof(init_fw_cb->pri.iscsi_name))); +- /*memcpy(ha->alias, init_fw_cb->Alias, +- min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));*/ +- +- /* Save Command Line Paramater info */ +- ha->port_down_retry_count = le16_to_cpu(init_fw_cb->pri.conn_ka_timeout); +- ha->discovery_wait = ql4xdiscoverywait; +- +- /* Send Initialize Firmware Control Block. */ +- memset(&mbox_cmd, 0, sizeof(mbox_cmd)); +- memset(&mbox_sts, 0, sizeof(mbox_sts)); ++ init_fw_cb->fw_options &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE); + +- mbox_cmd[0] = MBOX_CMD_INITIALIZE_FIRMWARE; +- mbox_cmd[1] = 0; +- mbox_cmd[2] = LSDW(init_fw_cb_dma); +- mbox_cmd[3] = MSDW(init_fw_cb_dma); +- mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); ++ if (qla4xxx_set_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) ++ != QLA_SUCCESS) { ++ DEBUG2(printk("scsi%ld: %s: Failed to set init_fw_ctrl_blk\n", ++ ha->host_no, __func__)); ++ goto exit_init_fw_cb; ++ } + +- if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) == +- QLA_SUCCESS) +- status = QLA_SUCCESS; +- else { +- DEBUG2(printk("scsi%ld: %s: MBOX_CMD_INITIALIZE_FIRMWARE " +- "failed w/ status %04X\n", ha->host_no, __func__, +- mbox_sts[0])); ++ if (qla4xxx_update_local_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], ++ init_fw_cb, init_fw_cb_dma) != QLA_SUCCESS) { ++ DEBUG2(printk("scsi%ld: %s: Failed to update local ifcb\n", ++ ha->host_no, __func__)); ++ goto exit_init_fw_cb; + } +- dma_free_coherent(&ha->pdev->dev, sizeof(struct init_fw_ctrl_blk), +- init_fw_cb, init_fw_cb_dma); ++ status = QLA_SUCCESS; ++ ++exit_init_fw_cb: ++ dma_free_coherent(&ha->pdev->dev, sizeof(struct addr_ctrl_blk), ++ init_fw_cb, init_fw_cb_dma); + + return status; + } +@@ -284,13 +463,13 @@ int qla4xxx_initialize_fw_cb(struct scsi + **/ + int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha) + { +- struct init_fw_ctrl_blk *init_fw_cb; ++ struct addr_ctrl_blk *init_fw_cb; + dma_addr_t init_fw_cb_dma; + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, +- sizeof(struct init_fw_ctrl_blk), ++ sizeof(struct addr_ctrl_blk), + &init_fw_cb_dma, GFP_KERNEL); + if (init_fw_cb == NULL) { + printk("scsi%ld: %s: Unable to alloc init_cb\n", ha->host_no, +@@ -299,35 +478,21 @@ int qla4xxx_get_dhcp_ip_address(struct s + } + + /* Get Initialize Firmware Control Block. */ +- memset(&mbox_cmd, 0, sizeof(mbox_cmd)); +- memset(&mbox_sts, 0, sizeof(mbox_sts)); +- +- memset(init_fw_cb, 0, sizeof(struct init_fw_ctrl_blk)); +- mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; +- mbox_cmd[2] = LSDW(init_fw_cb_dma); +- mbox_cmd[3] = MSDW(init_fw_cb_dma); +- mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); +- +- if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) != +- QLA_SUCCESS) { ++ memset(init_fw_cb, 0, sizeof(struct addr_ctrl_blk)); ++ if (qla4xxx_get_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) ++ != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: Failed to get init_fw_ctrl_blk\n", + ha->host_no, __func__)); + dma_free_coherent(&ha->pdev->dev, +- sizeof(struct init_fw_ctrl_blk), ++ sizeof(struct addr_ctrl_blk), + init_fw_cb, init_fw_cb_dma); + return QLA_ERROR; + } + + /* Save IP Address. */ +- memcpy(ha->ip_address, init_fw_cb->pri.ipv4_addr, +- min(sizeof(ha->ip_address), sizeof(init_fw_cb->pri.ipv4_addr))); +- memcpy(ha->subnet_mask, init_fw_cb->pri.ipv4_subnet, +- min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->pri.ipv4_subnet))); +- memcpy(ha->gateway, init_fw_cb->pri.ipv4_gw_addr, +- min(sizeof(ha->gateway), sizeof(init_fw_cb->pri.ipv4_gw_addr))); +- +- dma_free_coherent(&ha->pdev->dev, sizeof(struct init_fw_ctrl_blk), +- init_fw_cb, init_fw_cb_dma); ++ qla4xxx_update_local_ip(ha, init_fw_cb); ++ dma_free_coherent(&ha->pdev->dev, sizeof(struct addr_ctrl_blk), ++ init_fw_cb, init_fw_cb_dma); + + return QLA_SUCCESS; + } +@@ -441,14 +606,14 @@ int qla4xxx_get_fwddb_entry(struct scsi_ + goto exit_get_fwddb; + } + if (fw_ddb_entry) { +- dev_info(&ha->pdev->dev, "DDB[%d] MB0 %04x Tot %d Next %d " +- "State %04x ConnErr %08x %d.%d.%d.%d:%04d \"%s\"\n", +- fw_ddb_index, mbox_sts[0], mbox_sts[2], mbox_sts[3], +- mbox_sts[4], mbox_sts[5], fw_ddb_entry->ip_addr[0], +- fw_ddb_entry->ip_addr[1], fw_ddb_entry->ip_addr[2], +- fw_ddb_entry->ip_addr[3], +- le16_to_cpu(fw_ddb_entry->port), +- fw_ddb_entry->iscsi_name); ++ dev_info(&ha->pdev->dev, "%s: DDB[%d] MB0 %04x Tot %d " ++ "Next %d State %04x ConnErr %08x " NIPQUAD_FMT ++ ":%04d \"%s\"\n", __func__, fw_ddb_index, ++ mbox_sts[0], mbox_sts[2], mbox_sts[3], ++ mbox_sts[4], mbox_sts[5], ++ NIPQUAD(fw_ddb_entry->ip_addr), ++ le16_to_cpu(fw_ddb_entry->port), ++ fw_ddb_entry->iscsi_name); + } + if (num_valid_ddb_entries) + *num_valid_ddb_entries = mbox_sts[2]; +@@ -664,6 +829,53 @@ exit_get_event_log: + } + + /** ++ * qla4xxx_abort_task - issues Abort Task ++ * @ha: Pointer to host adapter structure. ++ * @srb: Pointer to srb entry ++ * ++ * This routine performs a LUN RESET on the specified target/lun. ++ * The caller must ensure that the ddb_entry and lun_entry pointers ++ * are valid before calling this routine. ++ **/ ++int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb) ++{ ++ uint32_t mbox_cmd[MBOX_REG_COUNT]; ++ uint32_t mbox_sts[MBOX_REG_COUNT]; ++ struct scsi_cmnd *cmd = srb->cmd; ++ int status = QLA_SUCCESS; ++ ++ DEBUG2(printk("scsi%ld:%d:%d:%d: abort task issued\n", ha->host_no, ++ cmd->device->channel, cmd->device->id, cmd->device->lun)); ++ ++ /* ++ * Send abort task command to ISP, so that the ISP will return ++ * request with ABORT status ++ */ ++ memset(&mbox_cmd, 0, sizeof(mbox_cmd)); ++ memset(&mbox_sts, 0, sizeof(mbox_sts)); ++ ++ mbox_cmd[0] = MBOX_CMD_ABORT_TASK; ++ mbox_cmd[1] = srb->fw_ddb_index; ++ mbox_cmd[2] = (unsigned long)(unsigned char *)cmd->host_scribble; ++ mbox_cmd[5] = 0x01; /* Immediate Command Enable */ ++ ++ qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 5, &mbox_cmd[0], &mbox_sts[0]); ++ if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE) { ++ status = QLA_ERROR; ++ ++ DEBUG2(printk("scsi%ld:%d:%d:%d: abort task FAILED: ", ha->host_no, ++ cmd->device->channel, cmd->device->id, cmd->device->lun)); ++ DEBUG2(printk("mbx0=%04X, mb1=%04X, mb2=%04X, mb3=%04X, mb4=%04X\n", ++ mbox_sts[0], mbox_sts[1], mbox_sts[2], mbox_sts[3], ++ mbox_sts[4])); ++ } ++ ++ return status; ++} ++ ++ ++ ++/** + * qla4xxx_reset_lun - issues LUN Reset + * @ha: Pointer to host adapter structure. + * @db_entry: Pointer to device database entry +@@ -679,11 +891,33 @@ int qla4xxx_reset_lun(struct scsi_qla_ho + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + int status = QLA_SUCCESS; ++ unsigned long wait_online; + + DEBUG2(printk("scsi%ld:%d:%d: lun reset issued\n", ha->host_no, + ddb_entry->os_target_id, lun)); + + /* ++ * If device is not online wait for 10 sec for device to come online. ++ * else return error ++ */ ++ if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { ++ wait_online = jiffies + (DEVICE_ONLINE_TOV * HZ); ++ while (time_before(jiffies, wait_online)) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(HZ); ++ if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) ++ break; ++ } ++ ++ if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { ++ DEBUG2(printk("scsi%ld: %s: Unable to reset lun." ++ "Device is not online.\n", ha->host_no ++ , __func__)); ++ return QLA_ERROR; ++ } ++ } ++ ++ /* + * Send lun reset command to ISP, so that the ISP will return all + * outstanding requests with RESET status + */ +--- a/drivers/scsi/qla4xxx/ql4_os.c ++++ b/drivers/scsi/qla4xxx/ql4_os.c +@@ -8,6 +8,8 @@ + + #include + #include ++#include ++#include + + #include "ql4_def.h" + #include "ql4_version.h" +@@ -18,7 +20,18 @@ + /* + * Driver version + */ +-static char qla4xxx_version_str[40]; ++char qla4xxx_version_str[40]; ++EXPORT_SYMBOL_GPL(qla4xxx_version_str); ++ ++/* ++ * List of host adapters ++ */ ++struct klist qla4xxx_hostlist; ++ ++struct klist *qla4xxx_hostlist_ptr = &qla4xxx_hostlist; ++EXPORT_SYMBOL_GPL(qla4xxx_hostlist_ptr); ++ ++static atomic_t qla4xxx_hba_count; + + /* + * SRB allocation cache +@@ -73,6 +86,7 @@ static enum blk_eh_timer_return qla4xxx_ + */ + static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, + void (*done) (struct scsi_cmnd *)); ++static int qla4xxx_eh_abort(struct scsi_cmnd *cmd); + static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); + static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); + static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); +@@ -87,6 +101,7 @@ static struct scsi_host_template qla4xxx + .proc_name = DRIVER_NAME, + .queuecommand = qla4xxx_queuecommand, + ++ .eh_abort_handler = qla4xxx_eh_abort, + .eh_device_reset_handler = qla4xxx_eh_device_reset, + .eh_target_reset_handler = qla4xxx_eh_target_reset, + .eh_host_reset_handler = qla4xxx_eh_host_reset, +@@ -95,6 +110,7 @@ static struct scsi_host_template qla4xxx + .slave_configure = qla4xxx_slave_configure, + .slave_alloc = qla4xxx_slave_alloc, + .slave_destroy = qla4xxx_slave_destroy, ++ .target_destroy = NULL, + + .scan_finished = iscsi_scan_finished, + .scan_start = qla4xxx_scan_start, +@@ -272,10 +288,8 @@ void qla4xxx_destroy_sess(struct ddb_ent + if (!ddb_entry->sess) + return; + +- if (ddb_entry->conn) { +- atomic_set(&ddb_entry->state, DDB_STATE_DEAD); ++ if (ddb_entry->conn) + iscsi_remove_session(ddb_entry->sess); +- } + iscsi_free_session(ddb_entry->sess); + } + +@@ -370,8 +384,19 @@ void qla4xxx_mark_device_missing(struct + ddb_entry->fw_ddb_index)); + iscsi_block_session(ddb_entry->sess); + iscsi_conn_error_event(ddb_entry->conn, ISCSI_ERR_CONN_FAILED); ++ set_bit(DF_NO_RELOGIN, &ddb_entry->flags); + } + ++/*** ++ * qla4xxx_get_new_srb - Allocate memory for a local srb. ++ * @ha: Pointer to host adapter structure. ++ * @ddb_entry: Pointer to device database entry ++ * @cmd: Pointer to Linux's SCSI command structure ++ * @done: Pointer to Linux's SCSI mid-layer done function ++ * ++ * NOTE: Sets te ref_count for non-NULL srb to one, ++ * and initializes some fields. ++ **/ + static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry, + struct scsi_cmnd *cmd, +@@ -417,6 +442,33 @@ void qla4xxx_srb_compl(struct scsi_qla_h + } + + /** ++ * sp_put - Decrement reference count and call callback. ++ * @ha: Pointer to host adapter structure. ++ * @sp: Pointer to srb structure ++ **/ ++void sp_put(struct scsi_qla_host *ha, struct srb *sp) ++{ ++ if (atomic_read(&sp->ref_count) == 0) { ++ DEBUG2(printk("%s: SP->ref_count ZERO\n", __func__)); ++ DEBUG2(BUG()); ++ return; ++ } ++ if (!atomic_dec_and_test(&sp->ref_count)) { ++ return; ++ } ++ qla4xxx_srb_compl(ha, sp); ++} ++ ++/** ++ * sp_get - Increment reference count of the specified sp. ++ * @sp: Pointer to srb structure ++ **/ ++void sp_get(struct srb *sp) ++{ ++ atomic_inc(&sp->ref_count); ++} ++ ++/** + * qla4xxx_queuecommand - scsi layer issues scsi command to driver. + * @cmd: Pointer to Linux's SCSI command structure + * @done_fn: Function that the driver calls to notify the SCSI mid-layer +@@ -451,7 +503,7 @@ static int qla4xxx_queuecommand(struct s + } + + if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { +- if (atomic_read(&ddb_entry->state) == DDB_STATE_DEAD) { ++ if ((atomic_read(&ddb_entry->state) == DDB_STATE_DEAD)) { + cmd->result = DID_NO_CONNECT << 16; + goto qc_fail_command; + } +@@ -498,10 +550,24 @@ qc_fail_command: + **/ + static void qla4xxx_mem_free(struct scsi_qla_host *ha) + { ++ struct list_head *ptr; ++ struct async_msg_pdu_iocb *apdu_iocb; ++ + if (ha->queues) + dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues, + ha->queues_dma); + ++ if (ha->gen_req_rsp_iocb) ++ dma_free_coherent(&ha->pdev->dev, PAGE_SIZE, ++ ha->gen_req_rsp_iocb, ha->gen_req_rsp_iocb_dma); ++ ++ while (!list_empty(&ha->async_iocb_list)) { ++ ptr = ha->async_iocb_list.next; ++ apdu_iocb = list_entry(ptr, struct async_msg_pdu_iocb, list); ++ list_del_init(&apdu_iocb->list); ++ kfree(apdu_iocb); ++ } ++ + ha->queues_len = 0; + ha->queues = NULL; + ha->queues_dma = 0; +@@ -587,6 +653,15 @@ static int qla4xxx_mem_alloc(struct scsi + goto mem_alloc_error_exit; + } + ++ ha->gen_req_rsp_iocb = dma_alloc_coherent(&ha->pdev->dev, PAGE_SIZE, ++ &ha->gen_req_rsp_iocb_dma, GFP_KERNEL); ++ if (ha->gen_req_rsp_iocb == NULL) { ++ dev_warn(&ha->pdev->dev, ++ "Memory Allocation failed - gen_req_rsp_iocb.\n"); ++ ++ goto mem_alloc_error_exit; ++ } ++ + return QLA_SUCCESS; + + mem_alloc_error_exit: +@@ -605,6 +680,24 @@ static void qla4xxx_timer(struct scsi_ql + + /* Search for relogin's to time-out and port down retry. */ + list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) { ++ /* First check to see if the device has exhausted the ++ * port down retry count */ ++ if (atomic_read(&ddb_entry->state) == DDB_STATE_MISSING) { ++ if (atomic_read(&ddb_entry->port_down_timer) == 0) ++ continue; ++ ++ if (atomic_dec_and_test(&ddb_entry->port_down_timer)) { ++ DEBUG2(printk("scsi%ld: %s: index [%d] " ++ "port down retry count of (%d) secs " ++ "exhausted.\n", ++ ha->host_no, __func__, ++ ddb_entry->fw_ddb_index, ++ ha->port_down_retry_count);) ++ ++ atomic_set(&ddb_entry->state, DDB_STATE_DEAD); ++ start_dpc++; ++ } ++ } + /* Count down time between sending relogins */ + if (adapter_up(ha) && + !test_bit(DF_RELOGIN, &ddb_entry->flags) && +@@ -639,7 +732,8 @@ static void qla4xxx_timer(struct scsi_ql + if (atomic_read(&ddb_entry->state) != + DDB_STATE_ONLINE && + ddb_entry->fw_ddb_device_state == +- DDB_DS_SESSION_FAILED) { ++ DDB_DS_SESSION_FAILED && ++ !test_bit(DF_NO_RELOGIN, &ddb_entry->flags)) { + /* Reset retry relogin timer */ + atomic_inc(&ddb_entry->relogin_retry_count); + DEBUG2(printk("scsi%ld: index[%d] relogin" +@@ -684,6 +778,9 @@ static void qla4xxx_timer(struct scsi_ql + test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) || + test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) || + test_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags) || ++ test_bit(DPC_REMOVE_DEVICE, &ha->dpc_flags) || ++ test_bit(DPC_LINK_CHANGED, &ha->dpc_flags) || ++ test_bit(DPC_ASYNC_MSG_PDU, &ha->dpc_flags) || + test_bit(DPC_AEN, &ha->dpc_flags)) && + ha->dpc_thread) { + DEBUG2(printk("scsi%ld: %s: scheduling dpc routine" +@@ -710,7 +807,6 @@ static int qla4xxx_cmd_wait(struct scsi_ + uint32_t index = 0; + int stat = QLA_SUCCESS; + unsigned long flags; +- struct scsi_cmnd *cmd; + int wait_cnt = WAIT_CMD_TOV; /* + * Initialized for 30 seconds as we + * expect all commands to retuned +@@ -720,9 +816,8 @@ static int qla4xxx_cmd_wait(struct scsi_ + while (wait_cnt) { + spin_lock_irqsave(&ha->hardware_lock, flags); + /* Find a command that hasn't completed. */ +- for (index = 0; index < ha->host->can_queue; index++) { +- cmd = scsi_host_find_tag(ha->host, index); +- if (cmd != NULL) ++ for (index = 1; index < MAX_SRBS; index++) { ++ if (ha->active_srb_array[index] != NULL) + break; + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); +@@ -881,11 +976,11 @@ static void qla4xxx_flush_active_srbs(st + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); +- for (i = 0; i < ha->host->can_queue; i++) { +- srb = qla4xxx_del_from_active_array(ha, i); +- if (srb != NULL) { ++ for (i = 1; i < MAX_SRBS; i++) { ++ if ((srb = ha->active_srb_array[i]) != NULL) { ++ qla4xxx_del_from_active_array(ha, i); + srb->cmd->result = DID_RESET << 16; +- qla4xxx_srb_compl(ha, srb); ++ sp_put(ha, srb); + } + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); +@@ -1000,6 +1095,134 @@ static int qla4xxx_recover_adapter(struc + return status; + } + ++/* ++ * qla4xxx_async_iocbs - processes ASYNC PDU IOCBS, if they are greater in ++ * length than 48 bytes (i.e., more than just the iscsi header). Used for ++ * unsolicited pdus received from target. ++ */ ++static void qla4xxx_async_iocbs(struct scsi_qla_host *ha, ++ struct async_msg_pdu_iocb *amsg_pdu_iocb) ++{ ++ struct iscsi_hdr *hdr; ++ struct async_pdu_iocb *apdu; ++ uint32_t len; ++ void *buf_addr; ++ dma_addr_t buf_addr_dma; ++ uint32_t offset; ++ struct passthru0 *pthru0_iocb; ++ struct ddb_entry *ddb_entry = NULL; ++ ASYNC_PDU_SENSE *pdu_sense; ++ ++ uint8_t using_prealloc = 1; ++ uint8_t async_event_type; ++ ++ apdu = (struct async_pdu_iocb *)amsg_pdu_iocb->iocb; ++ hdr = (struct iscsi_hdr *)apdu->iscsi_pdu_hdr; ++ len = hdr->hlength + hdr->dlength[2] + ++ (hdr->dlength[1]<<8) + (hdr->dlength[0]<<16); ++ ++ offset = sizeof(struct passthru0) + sizeof(struct passthru_status); ++ if (len <= (PAGE_SIZE - offset)) { ++ buf_addr_dma = ha->gen_req_rsp_iocb_dma + offset; ++ buf_addr = (uint8_t *)ha->gen_req_rsp_iocb + offset; ++ } else { ++ using_prealloc = 0; ++ buf_addr = dma_alloc_coherent(&ha->pdev->dev, len, ++ &buf_addr_dma, GFP_KERNEL); ++ if (!buf_addr) { ++ dev_info(&ha->pdev->dev, ++ "%s: dma_alloc_coherent failed\n", __func__); ++ return; ++ } ++ } ++ /* Create the pass-thru0 iocb */ ++ pthru0_iocb = ha->gen_req_rsp_iocb; ++ memset(pthru0_iocb, 0, offset); ++ pthru0_iocb->hdr.entryType = ET_PASSTHRU0; ++ pthru0_iocb->hdr.entryCount = 1; ++ pthru0_iocb->target = cpu_to_le16(apdu->target_id); ++ pthru0_iocb->controlFlags = ++ cpu_to_le16(PT_FLAG_ISCSI_PDU | PT_FLAG_WAIT_4_RESPONSE); ++ pthru0_iocb->timeout = cpu_to_le16(PT_DEFAULT_TIMEOUT); ++ pthru0_iocb->inDataSeg64.base.addrHigh = ++ cpu_to_le32(MSDW(buf_addr_dma)); ++ pthru0_iocb->inDataSeg64.base.addrLow = ++ cpu_to_le32(LSDW(buf_addr_dma)); ++ pthru0_iocb->inDataSeg64.count = cpu_to_le32(len); ++ pthru0_iocb->async_pdu_handle = cpu_to_le32(apdu->async_pdu_handle); ++ ++ dev_info(&ha->pdev->dev, ++ "%s: qla4xxx_issue_iocb\n", __func__); ++ ++ if (qla4xxx_issue_iocb(ha, sizeof(struct passthru0), ++ ha->gen_req_rsp_iocb_dma) != QLA_SUCCESS) { ++ dev_info(&ha->pdev->dev, ++ "%s: qla4xxx_issue_iocb failed\n", __func__); ++ goto exit_async_pdu_iocb; ++ } ++ ++ async_event_type = ((struct iscsi_async *)hdr)->async_event; ++ pdu_sense = (ASYNC_PDU_SENSE *)buf_addr; ++ ++ switch (async_event_type) { ++ case ISCSI_ASYNC_MSG_SCSI_EVENT: ++ dev_info(&ha->pdev->dev, ++ "%s: async msg event 0x%x processed\n" ++ , __func__, async_event_type); ++ ++ qla4xxx_dump_buffer(buf_addr, len); ++ ++ if (pdu_sense->sense_data[12] == 0x3F) { ++ if (pdu_sense->sense_data[13] == 0x0E) { ++ /* reported luns data has changed */ ++ uint16_t fw_index = apdu->target_id; ++ ++ ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_index); ++ if (ddb_entry == NULL) { ++ dev_info(&ha->pdev->dev, ++ "%s: No DDB entry for index [%d]\n" ++ , __func__, fw_index); ++ goto exit_async_pdu_iocb; ++ } ++ if (ddb_entry->fw_ddb_device_state != DDB_DS_SESSION_ACTIVE) { ++ dev_info(&ha->pdev->dev, ++ "scsi%ld: %s: No Active Session for index [%d]\n", ++ ha->host_no, __func__, fw_index); ++ goto exit_async_pdu_iocb; ++ } ++ ++ /* report new lun to kernel */ ++ scsi_scan_target(&ddb_entry->sess->dev, 0, ++ ddb_entry->sess->target_id, ++ SCAN_WILD_CARD, 0); ++ } ++ } ++ ++ break; ++ case ISCSI_ASYNC_MSG_REQUEST_LOGOUT: ++ case ISCSI_ASYNC_MSG_DROPPING_CONNECTION: ++ case ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS: ++ case ISCSI_ASYNC_MSG_PARAM_NEGOTIATION: ++ dev_info(&ha->pdev->dev, ++ "%s: async msg event 0x%x processed\n" ++ , __func__, async_event_type); ++ qla4xxx_conn_close_sess_logout(ha, apdu->target_id, 0, 0); ++ break; ++ default: ++ dev_info(&ha->pdev->dev, ++ "%s: async msg event 0x%x not processed\n", ++ __func__, async_event_type); ++ break; ++ }; ++ ++ exit_async_pdu_iocb: ++ if (!using_prealloc) ++ dma_free_coherent(&ha->pdev->dev, len, ++ buf_addr, buf_addr_dma); ++ ++ return; ++} ++ + /** + * qla4xxx_do_dpc - dpc routine + * @data: in our case pointer to adapter structure +@@ -1016,6 +1239,7 @@ static void qla4xxx_do_dpc(struct work_s + struct scsi_qla_host *ha = + container_of(work, struct scsi_qla_host, dpc_work); + struct ddb_entry *ddb_entry, *dtemp; ++ struct async_msg_pdu_iocb *apdu_iocb, *apdu_iocb_tmp; + int status = QLA_ERROR; + + DEBUG2(printk("scsi%ld: %s: DPC handler waking up." +@@ -1068,13 +1292,16 @@ static void qla4xxx_do_dpc(struct work_s + if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags)) + qla4xxx_get_dhcp_ip_address(ha); + ++ qla4xxx_remove_device(ha); ++ + /* ---- relogin device? --- */ + if (adapter_up(ha) && + test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) { + list_for_each_entry_safe(ddb_entry, dtemp, + &ha->ddb_list, list) { +- if (test_and_clear_bit(DF_RELOGIN, &ddb_entry->flags) && +- atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) ++ if ((test_and_clear_bit(DF_RELOGIN, &ddb_entry->flags)) && ++ (!test_bit(DF_NO_RELOGIN, &ddb_entry->flags)) && ++ (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE)) + qla4xxx_relogin_device(ha, ddb_entry); + + /* +@@ -1091,6 +1318,29 @@ static void qla4xxx_do_dpc(struct work_s + } + } + } ++ ++ if (test_and_clear_bit(DPC_LINK_CHANGED, &ha->dpc_flags)) { ++ if (!test_bit(AF_LINK_UP, &ha->flags)) { ++ /* ---- link down? --- */ ++ list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) { ++ if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) ++ qla4xxx_mark_device_missing(ha, ddb_entry); ++ } ++ } ++ } ++ ++ /* Check for ASYNC PDU IOCBs */ ++ if (adapter_up(ha) && ++ test_bit(DPC_ASYNC_MSG_PDU, &ha->dpc_flags)) { ++ ++ list_for_each_entry_safe(apdu_iocb, apdu_iocb_tmp, ++ &ha->async_iocb_list, list) { ++ qla4xxx_async_iocbs(ha, apdu_iocb); ++ list_del_init(&apdu_iocb->list); ++ kfree(apdu_iocb); ++ } ++ clear_bit(DPC_ASYNC_MSG_PDU, &ha->dpc_flags); ++ } + } + + /** +@@ -1244,6 +1494,7 @@ static int __devinit qla4xxx_probe_adapt + /* Initialize lists and spinlocks. */ + INIT_LIST_HEAD(&ha->ddb_list); + INIT_LIST_HEAD(&ha->free_srb_q); ++ INIT_LIST_HEAD(&ha->async_iocb_list); + + mutex_init(&ha->mbox_sem); + +@@ -1319,8 +1570,6 @@ static int __devinit qla4xxx_probe_adapt + /* Start timer thread. */ + qla4xxx_start_timer(ha, qla4xxx_timer, 1); + +- set_bit(AF_INIT_DONE, &ha->flags); +- + pci_set_drvdata(pdev, ha); + + ret = scsi_add_host(host, &pdev->dev); +@@ -1333,9 +1582,27 @@ static int __devinit qla4xxx_probe_adapt + qla4xxx_version_str, ha->pdev->device, pci_name(ha->pdev), + ha->host_no, ha->firmware_version[0], ha->firmware_version[1], + ha->patch_number, ha->build_number); ++ + scsi_scan_host(host); ++ ++ /* Insert new entry into the list of adapters. */ ++ klist_add_tail(&ha->node, &qla4xxx_hostlist); ++ ha->instance = atomic_inc_return(&qla4xxx_hba_count) - 1; ++ ++ if (qla4xxx_ioctl_init(ha)) { ++ dev_info(&ha->pdev->dev, "ioctl init failed\n"); ++ goto remove_host; ++ } ++ ++ set_bit(AF_INIT_DONE, &ha->flags); ++ dev_info(&ha->pdev->dev, "%s: AF_INIT_DONE\n", __func__); ++ + return 0; + ++remove_host: ++ qla4xxx_free_ddb_list(ha); ++ scsi_remove_host(host); ++ + probe_failed: + qla4xxx_free_adapter(ha); + scsi_host_put(ha->host); +@@ -1361,11 +1628,16 @@ static void __devexit qla4xxx_remove_ada + while (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) + ssleep(1); + ++ klist_remove(&ha->node); ++ atomic_dec(&qla4xxx_hba_count); ++ + /* remove devs from iscsi_sessions to scsi_devices */ + qla4xxx_free_ddb_list(ha); + + scsi_remove_host(ha->host); + ++ qla4xxx_ioctl_exit(ha); ++ + qla4xxx_free_adapter(ha); + + scsi_host_put(ha->host); +@@ -1429,12 +1701,14 @@ static void qla4xxx_slave_destroy(struct + struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t index) + { + struct srb *srb = NULL; +- struct scsi_cmnd *cmd; + +- if (!(cmd = scsi_host_find_tag(ha->host, index))) ++ /* validate handle and remove from active array */ ++ if (index >= MAX_SRBS) + return srb; + +- if (!(srb = (struct srb *)cmd->host_scribble)) ++ srb = ha->active_srb_array[index]; ++ ha->active_srb_array[index] = NULL; ++ if (!srb) + return srb; + + /* update counters */ +@@ -1451,16 +1725,20 @@ struct srb * qla4xxx_del_from_active_arr + * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware + * @ha: actual ha whose done queue will contain the comd returned by firmware. + * @cmd: Scsi Command to wait on. ++ * @got_ref: Additional reference retrieved by caller. + * + * This routine waits for the command to be returned by the Firmware + * for some max time. + **/ + static int qla4xxx_eh_wait_on_command(struct scsi_qla_host *ha, +- struct scsi_cmnd *cmd) ++ struct scsi_cmnd *cmd, int got_ref) + { ++#define ABORT_POLLING_PERIOD 1000 ++#define ABORT_WAIT_ITER 1 ++ + int done = 0; + struct srb *rp; +- uint32_t max_wait_time = EH_WAIT_CMD_TOV; ++ unsigned long wait_iter = ABORT_WAIT_ITER; + + do { + /* Checking to see if its returned to OS */ +@@ -1470,8 +1748,13 @@ static int qla4xxx_eh_wait_on_command(st + break; + } + +- msleep(2000); +- } while (max_wait_time--); ++ if (got_ref && (atomic_read(&rp->ref_count) == 1)) { ++ done++; ++ break; ++ } ++ ++ msleep(ABORT_POLLING_PERIOD); ++ } while (!(--wait_iter)); + + return done; + } +@@ -1513,26 +1796,176 @@ static int qla4xxx_eh_wait_for_commands( + { + int cnt; + int status = 0; ++ struct srb *sp; + struct scsi_cmnd *cmd; ++ unsigned long flags; + + /* + * Waiting for all commands for the designated target or dev + * in the active array + */ +- for (cnt = 0; cnt < ha->host->can_queue; cnt++) { +- cmd = scsi_host_find_tag(ha->host, cnt); +- if (cmd && stgt == scsi_target(cmd->device) && +- (!sdev || sdev == cmd->device)) { +- if (!qla4xxx_eh_wait_on_command(ha, cmd)) { +- status++; +- break; ++ for (cnt = 1; cnt < MAX_SRBS; cnt++) { ++ spin_lock_irqsave(&ha->hardware_lock, flags); ++ sp = ha->active_srb_array[cnt]; ++ if (sp) { ++ cmd = sp->cmd; ++ spin_unlock_irqrestore(&ha->hardware_lock, flags); ++ if (cmd && stgt == scsi_target(cmd->device) && ++ (!sdev || sdev == cmd->device)) { ++ if (!qla4xxx_eh_wait_on_command(ha, cmd, 0)) { ++ status++; ++ break; ++ } + } ++ } else { ++ spin_unlock_irqrestore(&ha->hardware_lock, flags); + } + } + return status; + } + + /** ++ * qla4xxx_eh_abort - callback for abort task. ++ * @cmd: Pointer to Linux's SCSI command structure ++ * ++ * This routine is called by the Linux OS to abort the specified ++ * command. ++ **/ ++static int qla4xxx_eh_abort(struct scsi_cmnd *cmd) ++{ ++ struct scsi_qla_host *ha; ++ struct srb *srb = NULL; ++ struct ddb_entry *ddb_entry; ++ int ret = SUCCESS; ++ unsigned int channel; ++ unsigned int id; ++ unsigned int lun; ++ unsigned long serial; ++ unsigned long flags = 0; ++ int i = 0; ++ int got_ref = 0; ++ unsigned long wait_online; ++ ++ if (cmd == NULL) { ++ DEBUG2(printk("ABORT - **** SCSI mid-layer passing in NULL cmd\n")); ++ return SUCCESS; ++ } ++ ++ ha = to_qla_host(cmd->device->host); ++ ddb_entry = cmd->device->hostdata; ++ channel = cmd->device->channel; ++ id = cmd->device->id; ++ lun = cmd->device->lun; ++ serial = cmd->serial_number; ++ ++ if (!ddb_entry) { ++ DEBUG2(printk("scsi%ld: ABORT - NULL ddb entry.\n", ha->host_no)); ++ return FAILED; ++ } ++ ++ if (!cmd->SCp.ptr) { ++ DEBUG2(printk("scsi%ld: ABORT - cmd already completed.\n", ++ ha->host_no)); ++ return ret; ++ } ++ ++ ++ ++ srb = (struct srb *) cmd->SCp.ptr; ++ ++ dev_info(&ha->pdev->dev, "scsi%ld:%d:%d:%d: ABORT ISSUED " ++ "cmd=%p, pid=%ld, ref=%d\n", ha->host_no, channel, id, lun, ++ cmd, serial, atomic_read(&srb->ref_count)); ++ ++ if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) { ++ DEBUG2(printk("scsi%ld:%d: %s: Unable to abort task. Adapter " ++ "DEAD.\n", ha->host_no, cmd->device->channel ++ , __func__)); ++ ++ return FAILED; ++ } ++ ++ /* Check active list for command */ ++ spin_lock_irqsave(&ha->hardware_lock, flags); ++ for (i = 1; i < MAX_SRBS; i++) { ++ srb = ha->active_srb_array[i]; ++ ++ if (srb == NULL) ++ continue; ++ ++ if (srb->cmd != cmd) ++ continue; ++ ++ DEBUG2(printk("scsi%ld:%d:%d:%d %s: aborting srb %p from RISC. " ++ "pid=%ld.\n", ha->host_no, channel, id, lun, ++ __func__, srb, serial)); ++ DEBUG3(qla4xxx_print_scsi_cmd(cmd)); ++ ++ /* Get a reference to the sp and drop the lock.*/ ++ sp_get(srb); ++ got_ref++; ++ ++ spin_unlock_irqrestore(&ha->hardware_lock, flags); ++ ++ /* ++ * If device is not online wait for 10 sec for device to come online, ++ * else return error and do not issue abort task. ++ */ ++ if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { ++ wait_online = jiffies + (DEVICE_ONLINE_TOV * HZ); ++ while (time_before(jiffies, wait_online)) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(HZ); ++ if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) ++ break; ++ } ++ if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { ++ DEBUG2(printk("scsi%ld:%d: %s: Unable to abort task." ++ "Device is not online.\n", ha->host_no ++ , cmd->device->channel, __func__)); ++ ++ return FAILED; ++ } ++ } ++ ++ if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) { ++ dev_info(&ha->pdev->dev, ++ "scsi%ld:%d:%d:%d: ABORT TASK - FAILED.\n", ++ ha->host_no, channel, id, lun); ++ } else { ++ dev_info(&ha->pdev->dev, ++ "scsi%ld:%d:%d:%d: ABORT TASK - mbx success.\n", ++ ha->host_no, channel, id, lun); ++ } ++ spin_lock_irqsave(&ha->hardware_lock, flags); ++ break; ++ } ++ spin_unlock_irqrestore(&ha->hardware_lock, flags); ++ ++ /* Wait for command to complete */ ++ spin_unlock_irqrestore(&ha->hardware_lock, flags); ++ if (qla4xxx_eh_wait_on_command(ha, cmd, got_ref)) { ++ dev_info(&ha->pdev->dev, ++ "scsi%ld:%d:%d:%d: ABORT SUCCEEDED - " ++ "cmd returned back to OS.\n", ++ ha->host_no, channel, id, lun); ++ ret = SUCCESS; ++ } ++ ++ if (got_ref) ++ sp_put(ha, srb); ++ ++ DEBUG2(printk("scsi%ld:%d:%d:%d: ABORT cmd=%p, pid=%ld, ref=%d, " ++ "ret=%x\n", ha->host_no, channel, id, lun, cmd, ++ serial, atomic_read(&srb->ref_count), ret)); ++ ++ return ret; ++} ++ ++ ++ ++ ++/** + * qla4xxx_eh_device_reset - callback for target reset. + * @cmd: Pointer to Linux's SCSI command structure + * +@@ -1541,16 +1974,34 @@ static int qla4xxx_eh_wait_for_commands( + **/ + static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) + { +- struct scsi_qla_host *ha = to_qla_host(cmd->device->host); +- struct ddb_entry *ddb_entry = cmd->device->hostdata; ++ struct scsi_qla_host *ha; ++ struct ddb_entry *ddb_entry; + int ret = FAILED, stat; ++ struct Scsi_Host *h; ++ unsigned int b, t, l; + +- if (!ddb_entry) ++ if (cmd == NULL) { ++ DEBUG2(printk("%s: **** SCSI mid-layer passing in NULL cmd" ++ "DEVICE RESET - cmd already completed.\n", ++ __func__)); ++ return SUCCESS; ++ } ++ ++ h = cmd->device->host; ++ b = cmd->device->channel; ++ t = cmd->device->id; ++ l = cmd->device->lun; ++ ha = to_qla_host(h); ++ ddb_entry = cmd->device->hostdata; ++ ++ if (!ddb_entry) { ++ DEBUG2(printk("scsi%ld: DEVICE RESET - NULL ddb entry.\n" ++ , ha->host_no)); + return ret; ++ } + +- dev_info(&ha->pdev->dev, +- "scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n", ha->host_no, +- cmd->device->channel, cmd->device->id, cmd->device->lun); ++ dev_info(&ha->pdev->dev, "scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n" ++ , ha->host_no, b, t, l); + + DEBUG2(printk(KERN_INFO + "scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, to=%x," +@@ -1558,8 +2009,13 @@ static int qla4xxx_eh_device_reset(struc + cmd, jiffies, cmd->request->timeout / HZ, + ha->dpc_flags, cmd->result, cmd->allowed)); + +- /* FIXME: wait for hba to go online */ +- stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun); ++ /* wait for hba to go online */ ++ if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) { ++ dev_info(&ha->pdev->dev, "%s: DEVICE RESET." ++ "Adapter Offline.\n", __func__); ++ return FAILED; ++ } ++ stat = qla4xxx_reset_lun(ha, ddb_entry, l); + if (stat != QLA_SUCCESS) { + dev_info(&ha->pdev->dev, "DEVICE RESET FAILED. %d\n", stat); + goto eh_dev_reset_done; +@@ -1574,14 +2030,13 @@ static int qla4xxx_eh_device_reset(struc + } + + /* Send marker. */ +- if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun, +- MM_LUN_RESET) != QLA_SUCCESS) ++ if (qla4xxx_send_marker_iocb(ha, ddb_entry, l, MM_LUN_RESET) ++ != QLA_SUCCESS) + goto eh_dev_reset_done; + + dev_info(&ha->pdev->dev, + "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n", +- ha->host_no, cmd->device->channel, cmd->device->id, +- cmd->device->lun); ++ ha->host_no, b, t, l); + + ret = SUCCESS; + +@@ -1655,6 +2110,13 @@ static int qla4xxx_eh_host_reset(struct + int return_status = FAILED; + struct scsi_qla_host *ha; + ++ if (cmd == NULL) { ++ DEBUG2(printk("%s: **** SCSI mid-layer passing in NULL cmd" ++ "HOST RESET - cmd already completed.\n", ++ __func__)); ++ return SUCCESS; ++ } ++ + ha = (struct scsi_qla_host *) cmd->device->host->hostdata; + + dev_info(&ha->pdev->dev, +@@ -1717,6 +2179,9 @@ static int __init qla4xxx_module_init(vo + { + int ret; + ++ atomic_set(&qla4xxx_hba_count, 0); ++ klist_init(&qla4xxx_hostlist, NULL, NULL); ++ + /* Allocate cache for SRBs. */ + srb_cachep = kmem_cache_create("qla4xxx_srbs", sizeof(struct srb), 0, + SLAB_HWCACHE_ALIGN, NULL); +--- a/drivers/scsi/qla4xxx/ql4_version.h ++++ b/drivers/scsi/qla4xxx/ql4_version.h +@@ -5,5 +5,5 @@ + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +-#define QLA4XXX_DRIVER_VERSION "5.01.00-k9" ++#define QLA4XXX_DRIVER_VERSION "5.01.00.00.11.01-k10" + diff --git a/patches.drivers/tg3-5785-and-57780-asic-revs-not-working.patch b/patches.drivers/tg3-5785-and-57780-asic-revs-not-working.patch new file mode 100644 index 0000000..29f6f58 --- /dev/null +++ b/patches.drivers/tg3-5785-and-57780-asic-revs-not-working.patch @@ -0,0 +1,197 @@ +From: Matt Carlson +Subject: tg3: 5785 and 57780 asic revs not working +References: bnc#580780 +Patch-mainline: Never + +There is a known problem with phylib that causes a lot of problems. +Phylib does not load phy modules as it detects devices on the MDIO bus. +If the phylib module gets loaded as a dependancy of tg3, there will be +no opportunity to load the needed broadcom.ko module before tg3 requests +phylib to probe the MDIO bus. The result will be that tg3 will fail to +attach to 5785 and 57780 devices. + +There are several known solutions to this problem. (None of these +should go upstream. The upstream fix should be to get phylib to load +modules for devices it encounters.) Only one of them need be applied. + +1) Statically link in the broadcom.ko module into the kernel. + +2) Add the following to /etc/modprobe.d/local.conf or its equivalent: + +install tg3 /sbin/modprobe broadcom; /sbin/modprobe --ignore-install tg3 + +3) Apply the following patch: + +Signed-off-by: Brandon Philips + +--- + drivers/net/tg3.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/tg3.h | 9 +++++ + 2 files changed, 92 insertions(+) + +Index: linux-2.6.34-master/drivers/net/tg3.c +=================================================================== +--- linux-2.6.34-master.orig/drivers/net/tg3.c ++++ linux-2.6.34-master/drivers/net/tg3.c +@@ -1956,6 +1956,58 @@ static int tg3_phy_reset(struct tg3 *tp) + tg3_phy_toggle_apd(tp, false); + + out: ++ if ((tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM50610 || ++ (tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM50610M) { ++ u32 reg; ++ ++ /* Enable SM_DSP clock and tx 6dB coding. */ ++ reg = MII_TG3_AUXCTL_SHDWSEL_AUXCTL | ++ MII_TG3_AUXCTL_ACTL_SMDSP_ENA | ++ MII_TG3_AUXCTL_ACTL_TX_6DB; ++ tg3_writephy(tp, MII_TG3_AUX_CTRL, reg); ++ ++ reg = MII_TG3_DSP_EXP8_REJ2MHz; ++ tg3_phydsp_write(tp, MII_TG3_DSP_EXP8, reg); ++ ++ /* Apply workaround to A0 revision parts only. */ ++ if (tp->phy_id == TG3_PHY_ID_BCM50610 || ++ tp->phy_id == TG3_PHY_ID_BCM50610M) { ++ tg3_phydsp_write(tp, 0x001F, 0x0300); ++ tg3_phydsp_write(tp, 0x601F, 0x0002); ++ tg3_phydsp_write(tp, 0x0F75, 0x003C); ++ tg3_phydsp_write(tp, 0x0F96, 0x0010); ++ tg3_phydsp_write(tp, 0x0F97, 0x0C0C); ++ } ++ ++ /* Turn off SM_DSP clock. */ ++ reg = MII_TG3_AUXCTL_SHDWSEL_AUXCTL | ++ MII_TG3_AUXCTL_ACTL_TX_6DB; ++ tg3_writephy(tp, MII_TG3_AUX_CTRL, reg); ++ ++ /* Clear all mode configuration bits. */ ++ reg = MII_TG3_MISC_SHDW_WREN | ++ MII_TG3_MISC_SHDW_RGMII_SEL; ++ tg3_writephy(tp, MII_TG3_MISC_SHDW, reg); ++ } ++ if ((tp->phy_id & TG3_PHY_ID_MASK) == TG3_PHY_ID_BCM57780) { ++ u32 reg; ++ ++ /* Enable SM_DSP clock and tx 6dB coding. */ ++ reg = MII_TG3_AUXCTL_SHDWSEL_AUXCTL | ++ MII_TG3_AUXCTL_ACTL_SMDSP_ENA | ++ MII_TG3_AUXCTL_ACTL_TX_6DB; ++ tg3_writephy(tp, MII_TG3_AUX_CTRL, reg); ++ ++ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, MII_TG3_DSP_EXP75); ++ tg3_readphy(tp, MII_TG3_DSP_RW_PORT, ®); ++ reg |= MII_TG3_DSP_EXP75_SUP_CM_OSC; ++ tg3_phydsp_write(tp, MII_TG3_DSP_EXP75, reg); ++ ++ /* Turn off SM_DSP clock. */ ++ reg = MII_TG3_AUXCTL_SHDWSEL_AUXCTL | ++ MII_TG3_AUXCTL_ACTL_TX_6DB; ++ tg3_writephy(tp, MII_TG3_AUX_CTRL, reg); ++ } + if (tp->tg3_flags2 & TG3_FLG2_PHY_ADC_BUG) { + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); +@@ -2018,6 +2070,22 @@ out: + /* adjust output voltage */ + tg3_writephy(tp, MII_TG3_FET_PTEST, 0x12); + } ++ else if (tp->tg3_flags3 & TG3_FLG3_PHY_IS_FET) { ++ u32 brcmtest; ++ if (!tg3_readphy(tp, MII_TG3_FET_TEST, &brcmtest) && ++ !tg3_writephy(tp, MII_TG3_FET_TEST, ++ brcmtest | MII_TG3_FET_SHADOW_EN)) { ++ u32 val, reg = MII_TG3_FET_SHDW_AUXMODE4; ++ ++ if (!tg3_readphy(tp, reg, &val)) { ++ val &= ~MII_TG3_FET_SHDW_AM4_LED_MASK; ++ val |= MII_TG3_FET_SHDW_AM4_LED_MODE1; ++ tg3_writephy(tp, reg, val); ++ } ++ ++ tg3_writephy(tp, MII_TG3_FET_TEST, brcmtest); ++ } ++ } + + tg3_phy_toggle_automdix(tp, 1); + tg3_phy_set_wirespeed(tp); +@@ -3260,6 +3328,15 @@ relink: + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + ++ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) { ++ if (tp->link_config.active_speed == SPEED_10) ++ tw32(MAC_MI_STAT, ++ MAC_MI_STAT_10MBPS_MODE | ++ MAC_MI_STAT_LNKSTAT_ATTN_ENAB); ++ else ++ tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB); ++ } ++ + if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { + /* Polled via timer. */ + tw32_f(MAC_EVENT, 0); +@@ -13505,9 +13582,11 @@ static int __devinit tg3_get_invariants( + GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX) + tp->coalesce_mode |= HOSTCC_MODE_32BYTE; + ++#if 0 + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780) + tp->tg3_flags3 |= TG3_FLG3_USE_PHYLIB; ++#endif + + err = tg3_mdio_init(tp); + if (err) +@@ -14293,6 +14372,10 @@ static char * __devinit tg3_phy_string(s + case TG3_PHY_ID_BCM5718C: return "5718C"; + case TG3_PHY_ID_BCM5718S: return "5718S"; + case TG3_PHY_ID_BCM57765: return "57765"; ++ case TG3_PHY_ID_BCM50610: return "50610"; ++ case TG3_PHY_ID_BCM50610M: return "50610M"; ++ case TG3_PHY_ID_BCMAC131: return "AC131"; ++ case TG3_PHY_ID_BCM57780: return "57780"; + case TG3_PHY_ID_BCM8002: return "8002/serdes"; + case 0: return "serdes"; + default: return "unknown"; +Index: linux-2.6.34-master/drivers/net/tg3.h +=================================================================== +--- linux-2.6.34-master.orig/drivers/net/tg3.h ++++ linux-2.6.34-master/drivers/net/tg3.h +@@ -2086,6 +2086,7 @@ + #define MII_TG3_DSP_EXP8_REJ2MHz 0x0001 + #define MII_TG3_DSP_EXP8_AEDW 0x0200 + #define MII_TG3_DSP_EXP75 0x0f75 ++#define MII_TG3_DSP_EXP75_SUP_CM_OSC 0x0001 + #define MII_TG3_DSP_EXP96 0x0f96 + #define MII_TG3_DSP_EXP97 0x0f97 + +@@ -2141,6 +2142,8 @@ + #define MII_TG3_MISC_SHDW_SCR5_LPED 0x0010 + #define MII_TG3_MISC_SHDW_SCR5_SEL 0x1400 + ++#define MII_TG3_MISC_SHDW_RGMII_SEL 0x2c00 ++ + #define MII_TG3_TEST1 0x1e + #define MII_TG3_TEST1_TRIM_EN 0x0010 + #define MII_TG3_TEST1_CRC_EN 0x8000 +@@ -2158,6 +2161,8 @@ + #define MII_TG3_FET_SHDW_MISCCTRL_MDIX 0x4000 + + #define MII_TG3_FET_SHDW_AUXMODE4 0x1a ++#define MII_TG3_FET_SHDW_AM4_LED_MODE1 0x0001 ++#define MII_TG3_FET_SHDW_AM4_LED_MASK 0x0003 + #define MII_TG3_FET_SHDW_AUXMODE4_SBPD 0x0008 + + #define MII_TG3_FET_SHDW_AUXSTAT2 0x1b +@@ -2943,6 +2948,10 @@ struct tg3 { + #define TG3_PHY_ID_BCM57765 0x5c0d8a40 + #define TG3_PHY_ID_BCM5906 0xdc00ac40 + #define TG3_PHY_ID_BCM8002 0x60010140 ++#define TG3_PHY_ID_BCM50610 0xbc050d60 ++#define TG3_PHY_ID_BCM50610M 0xbc050d70 ++#define TG3_PHY_ID_BCMAC131 0xbc050c70 ++#define TG3_PHY_ID_BCM57780 0x5c0d8990 + #define TG3_PHY_ID_INVALID 0xffffffff + + #define PHY_ID_RTL8211C 0x001cc910 diff --git a/patches.drivers/tg3-entropy-source.patch b/patches.drivers/tg3-entropy-source.patch new file mode 100644 index 0000000..b7e6926 --- /dev/null +++ b/patches.drivers/tg3-entropy-source.patch @@ -0,0 +1,61 @@ +From: Brandon Philips +Subject: [PATCH] tg3: entropy source +Patch-mainline: never +References: FATE#307517 + +Signed-off-by: Brandon Philips + +--- + drivers/net/tg3.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +--- a/drivers/net/tg3.c ++++ b/drivers/net/tg3.c +@@ -15,7 +15,6 @@ + * notice is accompanying it. + */ + +- + #include + #include + #include +@@ -66,6 +65,10 @@ + + #include "tg3.h" + ++static int entropy = 0; ++module_param(entropy, int, 0); ++MODULE_PARM_DESC(entropy, "Allow tg3 to populate the /dev/random entropy pool"); ++ + #define DRV_MODULE_NAME "tg3" + #define DRV_MODULE_VERSION "3.108" + #define DRV_MODULE_RELDATE "February 17, 2010" +@@ -8494,10 +8497,13 @@ restart_timer: + static int tg3_request_irq(struct tg3 *tp, int irq_num) + { + irq_handler_t fn; +- unsigned long flags; ++ unsigned long flags = 0; + char *name; + struct tg3_napi *tnapi = &tp->napi[irq_num]; + ++ if (entropy) ++ flags = IRQF_SAMPLE_RANDOM; ++ + if (tp->irq_cnt == 1) + name = tp->dev->name; + else { +@@ -8510,12 +8516,11 @@ static int tg3_request_irq(struct tg3 *t + fn = tg3_msi; + if (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI) + fn = tg3_msi_1shot; +- flags = IRQF_SAMPLE_RANDOM; + } else { + fn = tg3_interrupt; + if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) + fn = tg3_interrupt_tagged; +- flags = IRQF_SHARED | IRQF_SAMPLE_RANDOM; ++ flags |= IRQF_SHARED; + } + + return request_irq(tnapi->irq_vec, fn, flags, name, tnapi); diff --git a/patches.fixes.tar.bz2 b/patches.fixes.tar.bz2 deleted file mode 100644 index f4cdd1f..0000000 Binary files a/patches.fixes.tar.bz2 and /dev/null differ diff --git a/patches.fixes/acpi-cpufreq_fix_cpu_any_notification.patch b/patches.fixes/acpi-cpufreq_fix_cpu_any_notification.patch new file mode 100644 index 0000000..4b04f75 --- /dev/null +++ b/patches.fixes/acpi-cpufreq_fix_cpu_any_notification.patch @@ -0,0 +1,40 @@ +From: Thomas Renninger +Subject: acpi-cpufreq: Fix CPU_ANY CPUFREQ_{PRE,POST}CHANGE notification +Patch-Mainline: submitted - please revert after 2.6.35 +References: none + +Signed-off-by: Thomas Renninger +CC: venki@google.com +CC: davej@redhat.com +CC: arjan@infradead.org +CC: davej@redhat.com +CC: linux-kernel@vger.kernel.org +--- + arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +index 4591680..c6de3a9 100644 +--- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c ++++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +@@ -391,7 +391,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, + + freqs.old = perf->states[perf->state].core_frequency * 1000; + freqs.new = data->freq_table[next_state].frequency; +- for_each_cpu(i, cmd.mask) { ++ for_each_cpu(i, policy->cpus) { + freqs.cpu = i; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } +@@ -407,7 +407,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, + } + } + +- for_each_cpu(i, cmd.mask) { ++ for_each_cpu(i, policy->cpus) { + freqs.cpu = i; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } +-- +1.6.3 + diff --git a/patches.fixes/acpi_processor_check_maxcpus.patch b/patches.fixes/acpi_processor_check_maxcpus.patch new file mode 100644 index 0000000..cdc479f --- /dev/null +++ b/patches.fixes/acpi_processor_check_maxcpus.patch @@ -0,0 +1,42 @@ +From: Thomas Renninger +Subject: Do not try to set up acpi processor stuff on cores exceeding maxcpus= +References: bnc#601520 +Patch-Mainline: Not yet + +Signed-off-by: Thomas Renninger + +--- + drivers/acpi/processor_driver.c | 5 +++++ + init/main.c | 3 ++- + 2 files changed, 7 insertions(+), 1 deletion(-) + +Index: linux-2.6.34-master/init/main.c +=================================================================== +--- linux-2.6.34-master.orig/init/main.c ++++ linux-2.6.34-master/init/main.c +@@ -124,7 +124,8 @@ static char *ramdisk_execute_command; + + #ifdef CONFIG_SMP + /* Setup configured maximum number of CPUs to activate */ +-unsigned int __initdata setup_max_cpus = NR_CPUS; ++unsigned int setup_max_cpus = NR_CPUS; ++EXPORT_SYMBOL(setup_max_cpus); + + /* + * Setup routine for controlling SMP activation +Index: linux-2.6.34-master/drivers/acpi/processor_driver.c +=================================================================== +--- linux-2.6.34-master.orig/drivers/acpi/processor_driver.c ++++ linux-2.6.34-master/drivers/acpi/processor_driver.c +@@ -581,6 +581,11 @@ static int __cpuinit acpi_processor_add( + return 0; + } + ++#ifdef CONFIG_SMP ++ if (pr->id >= setup_max_cpus && pr->id != 0) ++ return 0; ++#endif ++ + BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0)); + + /* diff --git a/patches.fixes/aggressive-zone-reclaim.patch b/patches.fixes/aggressive-zone-reclaim.patch new file mode 100644 index 0000000..1a210f0 --- /dev/null +++ b/patches.fixes/aggressive-zone-reclaim.patch @@ -0,0 +1,67 @@ +From: Nick Piggin +Subject: be more aggressive with zone reclaims +References: bnc#476525 +Patch-mainline: no + +The zone reclaim design is not very good for parallel allocations. +The primary problem is that only one thread is allowed to perform +zone-reclaim at a time. If another thread needs memory from that +zone/node, then its zone-reclaim will fail and it will be forced +to fall back to allocating from another zone. + +Additionally, the default zone reclaim priority is insufficient +for massively parallel allocations. Lower ZONE_RECLAIM_PRIORITY +to fix it. This can result in higher latency spikes, but similar +kind of page allocation latency can often be encountered as +normal part of page reclaim when pagecache fills memory. + +Signed-off-by: Petr Tesarik + +--- + mm/vmscan.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -2501,7 +2501,7 @@ int zone_reclaim_mode __read_mostly; + * of a node considered for each zone_reclaim. 4 scans 1/16th of + * a zone. + */ +-#define ZONE_RECLAIM_PRIORITY 4 ++#define ZONE_RECLAIM_PRIORITY 0 + + /* + * Percentage of pages in a zone that must be unmapped for zone_reclaim to +@@ -2607,6 +2607,8 @@ static int __zone_reclaim(struct zone *z + + slab_reclaimable = zone_page_state(zone, NR_SLAB_RECLAIMABLE); + if (slab_reclaimable > zone->min_slab_pages) { ++ unsigned long lru_pages = zone_reclaimable_pages(zone); ++ + /* + * shrink_slab() does not currently allow us to determine how + * many pages were freed in this zone. So we take the current +@@ -2617,10 +2619,7 @@ static int __zone_reclaim(struct zone *z + * Note that shrink_slab will free memory on all zones and may + * take a long time. + */ +- while (shrink_slab(sc.nr_scanned, gfp_mask, order) && +- zone_page_state(zone, NR_SLAB_RECLAIMABLE) > +- slab_reclaimable - nr_pages) +- ; ++ shrink_slab(sc.nr_scanned, gfp_mask, lru_pages); + + /* + * Update nr_reclaimed by the number of slab pages we +@@ -2674,11 +2673,7 @@ int zone_reclaim(struct zone *zone, gfp_ + if (node_state(node_id, N_CPU) && node_id != numa_node_id()) + return ZONE_RECLAIM_NOSCAN; + +- if (zone_test_and_set_flag(zone, ZONE_RECLAIM_LOCKED)) +- return ZONE_RECLAIM_NOSCAN; +- + ret = __zone_reclaim(zone, gfp_mask, order); +- zone_clear_flag(zone, ZONE_RECLAIM_LOCKED); + + if (!ret) + count_vm_event(PGSCAN_ZONE_RECLAIM_FAILED); diff --git a/patches.fixes/bridge-module-get-put.patch b/patches.fixes/bridge-module-get-put.patch new file mode 100644 index 0000000..457444b --- /dev/null +++ b/patches.fixes/bridge-module-get-put.patch @@ -0,0 +1,45 @@ +From: jbeulich@novell.com +Subject: Module use count must be updated as bridges are created/destroyed +Patch-mainline: unknown +References: 267651 + +Otherwise 'modprobe -r' on a module having a dependency on bridge will +implicitly unload bridge, bringing down all connectivity that was using +bridges. + +--- + net/bridge/br_if.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -279,6 +279,11 @@ int br_add_bridge(struct net *net, const + if (!dev) + return -ENOMEM; + ++ if (!try_module_get(THIS_MODULE)) { ++ free_netdev(dev); ++ return -ENOENT; ++ } ++ + rtnl_lock(); + if (strchr(dev->name, '%')) { + ret = dev_alloc_name(dev, dev->name); +@@ -297,6 +302,8 @@ int br_add_bridge(struct net *net, const + unregister_netdevice(dev); + out: + rtnl_unlock(); ++ if (ret) ++ module_put(THIS_MODULE); + return ret; + + out_free: +@@ -328,6 +335,8 @@ int br_del_bridge(struct net *net, const + del_br(netdev_priv(dev), NULL); + + rtnl_unlock(); ++ if (ret == 0) ++ module_put(THIS_MODULE); + return ret; + } + diff --git a/patches.fixes/cifs-fix-oops-due-to-null-nameidata b/patches.fixes/cifs-fix-oops-due-to-null-nameidata new file mode 100644 index 0000000..0dab951 --- /dev/null +++ b/patches.fixes/cifs-fix-oops-due-to-null-nameidata @@ -0,0 +1,136 @@ +From: Steve French +Subject: [CIFS] Allow null nd (as nfs server uses) on create +References: bnc#593940 +Patch-mainline: queued (in cifs devel git) + +commit fa588e0c57048b3d4bfcd772d80dc0615f83fd35 in cifs-2.6.git + + While creating a file on a server which supports unix extensions + such as Samba, if a file is being created which does not supply + nameidata (i.e. nd is null), cifs client can oops when calling + cifs_posix_open. + +Signed-off-by: Shirish Pargaonkar +Signed-off-by: Steve French +Acked-by: Suresh Jayaraman +--- + fs/cifs/cifsproto.h | 6 ++++-- + fs/cifs/dir.c | 20 ++++++++++++-------- + fs/cifs/file.c | 11 +++++++---- + 3 files changed, 23 insertions(+), 14 deletions(-) + +Index: linux-2.6.33-master/fs/cifs/cifsproto.h +=================================================================== +--- linux-2.6.33-master.orig/fs/cifs/cifsproto.h ++++ linux-2.6.33-master/fs/cifs/cifsproto.h +@@ -95,8 +95,10 @@ extern struct cifsFileInfo *cifs_new_fil + __u16 fileHandle, struct file *file, + struct vfsmount *mnt, unsigned int oflags); + extern int cifs_posix_open(char *full_path, struct inode **pinode, +- struct vfsmount *mnt, int mode, int oflags, +- __u32 *poplock, __u16 *pnetfid, int xid); ++ struct vfsmount *mnt, ++ struct super_block *sb, ++ int mode, int oflags, ++ __u32 *poplock, __u16 *pnetfid, int xid); + extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, + FILE_UNIX_BASIC_INFO *info, + struct cifs_sb_info *cifs_sb); +Index: linux-2.6.33-master/fs/cifs/dir.c +=================================================================== +--- linux-2.6.33-master.orig/fs/cifs/dir.c ++++ linux-2.6.33-master/fs/cifs/dir.c +@@ -183,13 +183,14 @@ cifs_new_fileinfo(struct inode *newinode + } + + int cifs_posix_open(char *full_path, struct inode **pinode, +- struct vfsmount *mnt, int mode, int oflags, +- __u32 *poplock, __u16 *pnetfid, int xid) ++ struct vfsmount *mnt, struct super_block *sb, ++ int mode, int oflags, ++ __u32 *poplock, __u16 *pnetfid, int xid) + { + int rc; + FILE_UNIX_BASIC_INFO *presp_data; + __u32 posix_flags = 0; +- struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb); ++ struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_fattr fattr; + + cFYI(1, ("posix open %s", full_path)); +@@ -242,7 +243,7 @@ int cifs_posix_open(char *full_path, str + + /* get new inode and set it up */ + if (*pinode == NULL) { +- *pinode = cifs_iget(mnt->mnt_sb, &fattr); ++ *pinode = cifs_iget(sb, &fattr); + if (!*pinode) { + rc = -ENOMEM; + goto posix_open_ret; +@@ -251,7 +252,8 @@ int cifs_posix_open(char *full_path, str + cifs_fattr_to_inode(*pinode, &fattr); + } + +- cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt, oflags); ++ if (mnt) ++ cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt, oflags); + + posix_open_ret: + kfree(presp_data); +@@ -315,13 +317,14 @@ cifs_create(struct inode *inode, struct + if (nd && (nd->flags & LOOKUP_OPEN)) + oflags = nd->intent.open.flags; + else +- oflags = FMODE_READ; ++ oflags = FMODE_READ | SMB_O_CREAT; + + if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { +- rc = cifs_posix_open(full_path, &newinode, nd->path.mnt, +- mode, oflags, &oplock, &fileHandle, xid); ++ rc = cifs_posix_open(full_path, &newinode, ++ nd ? nd->path.mnt : NULL, ++ inode->i_sb, mode, oflags, &oplock, &fileHandle, xid); + /* EIO could indicate that (posix open) operation is not + supported, despite what server claimed in capability + negotation. EREMOTE indicates DFS junction, which is not +@@ -678,6 +681,7 @@ cifs_lookup(struct inode *parent_dir_ino + (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open && + (nd->intent.open.flags & O_CREAT)) { + rc = cifs_posix_open(full_path, &newInode, nd->path.mnt, ++ parent_dir_inode->i_sb, + nd->intent.open.create_mode, + nd->intent.open.flags, &oplock, + &fileHandle, xid); +Index: linux-2.6.33-master/fs/cifs/file.c +=================================================================== +--- linux-2.6.33-master.orig/fs/cifs/file.c ++++ linux-2.6.33-master/fs/cifs/file.c +@@ -298,10 +298,12 @@ int cifs_open(struct inode *inode, struc + (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + int oflags = (int) cifs_posix_convert_flags(file->f_flags); ++ oflags |= SMB_O_CREAT; + /* can not refresh inode info since size could be stale */ + rc = cifs_posix_open(full_path, &inode, file->f_path.mnt, +- cifs_sb->mnt_file_mode /* ignored */, +- oflags, &oplock, &netfid, xid); ++ inode->i_sb, ++ cifs_sb->mnt_file_mode /* ignored */, ++ oflags, &oplock, &netfid, xid); + if (rc == 0) { + cFYI(1, ("posix open succeeded")); + /* no need for special case handling of setting mode +@@ -513,8 +515,9 @@ reopen_error_exit: + int oflags = (int) cifs_posix_convert_flags(file->f_flags); + /* can not refresh inode info since size could be stale */ + rc = cifs_posix_open(full_path, NULL, file->f_path.mnt, +- cifs_sb->mnt_file_mode /* ignored */, +- oflags, &oplock, &netfid, xid); ++ inode->i_sb, ++ cifs_sb->mnt_file_mode /* ignored */, ++ oflags, &oplock, &netfid, xid); + if (rc == 0) { + cFYI(1, ("posix reopen succeeded")); + goto reopen_success; diff --git a/patches.fixes/cpufreq_ondemand_performance_optimise_default_settings.patch b/patches.fixes/cpufreq_ondemand_performance_optimise_default_settings.patch new file mode 100644 index 0000000..a100c96 --- /dev/null +++ b/patches.fixes/cpufreq_ondemand_performance_optimise_default_settings.patch @@ -0,0 +1,65 @@ +From: Thomas Renninger +Subject: CPUFREQ: ondemand: Limit default sampling rate to 300ms max. +References: bnc#464461 +Patch-Mainline: never, SLE11 only + +Modified for SP1 by Jiri Bohac + +HW cpufreq drivers (e.g. all non-acpi AMD) may report too high latency values. +The default sampling rate (how often the ondemand/conservative governor +checks for frequency adjustments) may therefore be much too high, +resulting in performance loss. + +Restrict default sampling rate to 300ms. 333ms sampling rate is field +tested with userspace governors, 300ms should be a fine maximum default +value for the ondemand kernel governor for all HW out there. + +Set default up_threshold to 40 on multi core systems. +This should avoid effects where two CPU intensive threads are waiting on +each other on separate cores. On a single core machine these would all be +processed on one core resulting in higher utilization of the one core. + +--- + drivers/cpufreq/cpufreq_ondemand.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +--- a/drivers/cpufreq/cpufreq_ondemand.c ++++ b/drivers/cpufreq/cpufreq_ondemand.c +@@ -35,6 +35,7 @@ + #define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) + #define MIN_FREQUENCY_UP_THRESHOLD (11) + #define MAX_FREQUENCY_UP_THRESHOLD (100) ++#define MAX_DEFAULT_SAMPLING_RATE (300 * 1000U) + + /* + * The polling frequency of this governor depends on the capability of +@@ -679,6 +680,29 @@ static int cpufreq_governor_dbs(struct c + dbs_tuners_ins.sampling_rate = + max(min_sampling_rate, + latency * LATENCY_MULTIPLIER); ++ /* ++ * Cut def_sampling rate to 300ms if it was above, ++ * still consider to not set it above latency ++ * transition * 100 ++ */ ++ if (dbs_tuners_ins.sampling_rate > MAX_DEFAULT_SAMPLING_RATE) { ++ dbs_tuners_ins.sampling_rate = ++ max(min_sampling_rate, MAX_DEFAULT_SAMPLING_RATE); ++ printk(KERN_INFO "CPUFREQ: ondemand sampling " ++ "rate set to %d ms\n", ++ dbs_tuners_ins.sampling_rate / 1000); ++ } ++ /* ++ * Be conservative in respect to performance. ++ * If an application calculates using two threads ++ * depending on each other, they will be run on several ++ * CPU cores resulting on 50% load on both. ++ * SLED might still want to prefer 80% up_threshold ++ * by default, but we cannot differ that here. ++ */ ++ if (num_online_cpus() > 1) ++ dbs_tuners_ins.up_threshold = ++ DEF_FREQUENCY_UP_THRESHOLD / 2; + } + mutex_unlock(&dbs_mutex); + diff --git a/patches.fixes/dm-mpath-reattach-dh b/patches.fixes/dm-mpath-reattach-dh new file mode 100644 index 0000000..11a98a3 --- /dev/null +++ b/patches.fixes/dm-mpath-reattach-dh @@ -0,0 +1,29 @@ +From: Hannes Reinecke +Subject: Reattach device handler for multipath devices +References: bnc#435688 +Patch-mainline: not yet + +The multipath daemon might have specified a different device_handler +than the one a device is attached to by default. +So we should try to re-attach with the user-specified device_handler +and only return an error if that fails. +And we should _not_ detach existing hardware handlers. This will +set the path to failed during failover. + +Signed-off-by: Hannes Reinecke list); +- if (m->hw_handler_name) +- scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev)); + dm_put_device(ti, pgpath->path.dev); + free_pgpath(pgpath); + } diff --git a/patches.fixes/dm-release-map_lock-before-set_disk_ro b/patches.fixes/dm-release-map_lock-before-set_disk_ro new file mode 100644 index 0000000..9063acc --- /dev/null +++ b/patches.fixes/dm-release-map_lock-before-set_disk_ro @@ -0,0 +1,39 @@ +From: Nikanth Karthikesan +Subject: Release md->map_lock before set_disk_ro +Patch-mainline: No +References: bnc#556899 bnc#479784 + +Signed-off-by: Nikanth Karthikesan + +Calling set_disk_ro() with irqs disabled triggers a warning. + +set_disk_ro() can be called outside the +write_lock_irqsave(&md->map_lock)? And to get the +dm_table_get_mode(md->map), we just need to hold a reference +with dm_get_table() and dm_table_put() + +--- + drivers/md/dm.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +Index: linux-2.6.33-master/drivers/md/dm.c +=================================================================== +--- linux-2.6.33-master.orig/drivers/md/dm.c ++++ linux-2.6.33-master/drivers/md/dm.c +@@ -2102,12 +2102,15 @@ static struct dm_table *__bind(struct ma + old_map = md->map; + md->map = t; + dm_table_set_restrictions(t, q, limits); ++ write_unlock_irqrestore(&md->map_lock, flags); ++ ++ dm_table_get(md->map); + if (!(dm_table_get_mode(t) & FMODE_WRITE)) { + set_disk_ro(md->disk, 1); + } else { + set_disk_ro(md->disk, 0); + } +- write_unlock_irqrestore(&md->map_lock, flags); ++ dm_table_put(md->map); + + return old_map; + } diff --git a/patches.fixes/dm-table-switch-to-readonly b/patches.fixes/dm-table-switch-to-readonly new file mode 100644 index 0000000..96fe175 --- /dev/null +++ b/patches.fixes/dm-table-switch-to-readonly @@ -0,0 +1,90 @@ +From: Hannes Reinecke +Subject: dm multipath devices are not getting created for readonly devices +References: bnc#382705 +Patch-mainline: not yet + +Currently we cannot create device-mapper tables for multipath devices +whenever they are read-only. +This patch modifies the device-mapper to set the 'READ-ONLY' flag +automatically whenever a read-only is added to the table. + +Signed-off-by: Hannes Reinecke + +--- + drivers/md/dm-table.c | 10 +++++++++- + drivers/md/dm.c | 18 ++++++++++++++++-- + 2 files changed, 25 insertions(+), 3 deletions(-) + +--- a/drivers/md/dm-table.c ++++ b/drivers/md/dm-table.c +@@ -462,11 +462,19 @@ static int __table_get_device(struct dm_ + dd->dm_dev.mode = mode; + dd->dm_dev.bdev = NULL; + +- if ((r = open_dev(dd, dev, t->md))) { ++ r = open_dev(dd, dev, t->md); ++ if (r == -EROFS) { ++ dd->dm_dev.mode &= ~FMODE_WRITE; ++ r = open_dev(dd, dev, t->md); ++ } ++ if (r) { + kfree(dd); + return r; + } + ++ if (dd->dm_dev.mode != mode) ++ t->mode = dd->dm_dev.mode; ++ + format_dev_t(dd->dm_dev.name, dev); + + atomic_set(&dd->count, 0); +--- a/drivers/md/dm.c ++++ b/drivers/md/dm.c +@@ -337,16 +337,25 @@ int dm_deleting_md(struct mapped_device + static int dm_blk_open(struct block_device *bdev, fmode_t mode) + { + struct mapped_device *md; ++ int retval = 0; + + spin_lock(&_minor_lock); + + md = bdev->bd_disk->private_data; +- if (!md) ++ if (!md) { ++ retval = -ENXIO; + goto out; ++ } + + if (test_bit(DMF_FREEING, &md->flags) || + dm_deleting_md(md)) { + md = NULL; ++ retval = -ENXIO; ++ goto out; ++ } ++ if (get_disk_ro(md->disk) && (mode & FMODE_WRITE)) { ++ md = NULL; ++ retval = -EROFS; + goto out; + } + +@@ -356,7 +365,7 @@ static int dm_blk_open(struct block_devi + out: + spin_unlock(&_minor_lock); + +- return md ? 0 : -ENXIO; ++ return retval; + } + + static int dm_blk_close(struct gendisk *disk, fmode_t mode) +@@ -2093,6 +2102,11 @@ static struct dm_table *__bind(struct ma + old_map = md->map; + md->map = t; + dm_table_set_restrictions(t, q, limits); ++ if (!(dm_table_get_mode(t) & FMODE_WRITE)) { ++ set_disk_ro(md->disk, 1); ++ } else { ++ set_disk_ro(md->disk, 0); ++ } + write_unlock_irqrestore(&md->map_lock, flags); + + return old_map; diff --git a/patches.fixes/dmar-fix-oops-with-no-dmar-table b/patches.fixes/dmar-fix-oops-with-no-dmar-table new file mode 100644 index 0000000..1cc623c --- /dev/null +++ b/patches.fixes/dmar-fix-oops-with-no-dmar-table @@ -0,0 +1,25 @@ +From: Jeff Mahoney +Subject: dmar: Fix oops with no DMAR table +References: bnc#548108 +Patch-mainline: submitted 17 Mar 2010 + + On systems without a DMAR table and with DMAR enabled, we will oops + in dmar_ir_supported. This patch makes sure we actually have a DMAR + table before checking it. + + +Signed-off-by: Jeff Mahoney +--- + + drivers/pci/dmar.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pci/dmar.c ++++ b/drivers/pci/dmar.c +@@ -1460,5 +1460,5 @@ int __init dmar_ir_support(void) + { + struct acpi_table_dmar *dmar; + dmar = (struct acpi_table_dmar *)dmar_tbl; +- return dmar->flags & 0x1; ++ return dmar && dmar->flags & 0x1; + } diff --git a/patches.fixes/ext3-mark-super-uptodate b/patches.fixes/ext3-mark-super-uptodate new file mode 100644 index 0000000..5be120d --- /dev/null +++ b/patches.fixes/ext3-mark-super-uptodate @@ -0,0 +1,41 @@ +From: Jeff Mahoney +Subject: [PATCH] ext3: always mark super uptodate before dirty +References: bnc#457043 +Patch-mainline: not yet + + The superblock's bh is something of an exception. It is only read + during mount and is only released during unmount. The in-memory + copy is invariably the most recent one. + + If a write error occurs while syncing the superblock, it will be marked + !uptodate. When another error occurs, ext3_error will invoke + ext3_commit_super, which will mark the superblock dirty and try to + sync it out again. If the buffer is !uptodate, then mark_buffer_dirty + will issue a warning, but continue anyway. + + This patch marks it uptodate before writing it out. This doesn't really + change anything other than silencing the warning in mark_buffer_dirty. + If the write succeeds, good. Otherwise, it will just have uptodate + cleared again. + +Signed-off-by: Jeff Mahoney +--- + fs/ext3/super.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/fs/ext3/super.c ++++ b/fs/ext3/super.c +@@ -2382,6 +2382,13 @@ static int ext3_commit_super(struct supe + es->s_free_blocks_count = cpu_to_le32(ext3_count_free_blocks(sb)); + es->s_free_inodes_count = cpu_to_le32(ext3_count_free_inodes(sb)); + BUFFER_TRACE(sbh, "marking dirty"); ++ ++ /* We only read the superblock once. The in-memory version is ++ * always the most recent. If ext3_error is called after a ++ * superblock write failure, it will be !uptodate. This write ++ * will likely fail also, but it avoids the WARN_ON in ++ * mark_buffer_dirty. */ ++ set_buffer_uptodate(sbh); + mark_buffer_dirty(sbh); + if (sync) + error = sync_dirty_buffer(sbh); diff --git a/patches.fixes/fix-nf_conntrack_slp b/patches.fixes/fix-nf_conntrack_slp new file mode 100644 index 0000000..09e8d82 --- /dev/null +++ b/patches.fixes/fix-nf_conntrack_slp @@ -0,0 +1,63 @@ +From: Ludwig Nussel +Subject: make nf_conntrack_slp actually work +References: bnc#470963 +Patch-mainline: not yet, depends on patches.suse/netfilter-ip_conntrack_slp.patch + +Acked-by: Jeff Mahoney +--- + + net/netfilter/nf_conntrack_slp.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +--- a/net/netfilter/nf_conntrack_slp.c ++++ b/net/netfilter/nf_conntrack_slp.c +@@ -47,15 +47,15 @@ static int help(struct sk_buff *skb, uns + struct nf_conn *ct, enum ip_conntrack_info ctinfo) + { + struct nf_conntrack_expect *exp; +- struct iphdr *iph = ip_hdr(skb); + struct rtable *rt = skb_rtable(skb); + struct in_device *in_dev; + __be32 mask = 0; ++ __be32 src = 0; + + /* we're only interested in locally generated packets */ + if (skb->sk == NULL) + goto out; +- if (rt == NULL || !(rt->rt_flags & RTCF_BROADCAST)) ++ if (rt == NULL || !(rt->rt_flags & (RTCF_MULTICAST|RTCF_BROADCAST))) + goto out; + if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) + goto out; +@@ -64,15 +64,18 @@ static int help(struct sk_buff *skb, uns + in_dev = __in_dev_get_rcu(rt->u.dst.dev); + if (in_dev != NULL) { + for_primary_ifa(in_dev) { +- if (ifa->ifa_broadcast == iph->daddr) { +- mask = ifa->ifa_mask; +- break; +- } ++ /* this is a hack as slp uses multicast we can't match ++ * the destination address to some broadcast address. So ++ * just take the first one. Better would be to install ++ * expectations for all addresses */ ++ mask = ifa->ifa_mask; ++ src = ifa->ifa_broadcast; ++ break; + } endfor_ifa(in_dev); + } + rcu_read_unlock(); + +- if (mask == 0) ++ if (mask == 0 || src == 0) + goto out; + + exp = nf_ct_expect_alloc(ct); +@@ -80,6 +83,7 @@ static int help(struct sk_buff *skb, uns + goto out; + + exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; ++ exp->tuple.src.u3.ip = src; + exp->tuple.src.u.udp.port = htons(SLP_PORT); + + exp->mask.src.u3.ip = mask; diff --git a/patches.fixes/grab-swap-token-oops b/patches.fixes/grab-swap-token-oops new file mode 100644 index 0000000..45031a2 --- /dev/null +++ b/patches.fixes/grab-swap-token-oops @@ -0,0 +1,30 @@ +From: Dean Roe +Subject: Prevent NULL pointer deref in grab_swap_token +References: 159260 +Patch-mainline: not yet + +grab_swap_token() assumes that the current process has an mm struct, +which is not true for kernel threads invoking get_user_pages(). Since +this should be extremely rare, just return from grab_swap_token() +without doing anything. + +Signed-off-by: Dean Roe +Acked-by: mason@suse.de +Acked-by: okir@suse.de + + + mm/thrash.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/mm/thrash.c ++++ b/mm/thrash.c +@@ -31,6 +31,9 @@ void grab_swap_token(struct mm_struct *m + int current_interval; + + global_faults++; ++ if (mm == NULL) ++ return; ++ + + current_interval = global_faults - mm->faultstamp; + diff --git a/patches.fixes/hp-wmi_detect_keys.patch b/patches.fixes/hp-wmi_detect_keys.patch new file mode 100644 index 0000000..0202e14 --- /dev/null +++ b/patches.fixes/hp-wmi_detect_keys.patch @@ -0,0 +1,110 @@ +From: Thomas Renninger +Subject: x86 platform drivers: hp-wmi Reorder event id processing +References: bnc#598059 +Patch-Mainline: submitted + +Event id 0x4 defines the hotkey event. +No need (or even wrong) to query HPWMI_HOTKEY_QUERY if event id is != 0x4. + +Reorder the eventcode conditionals and use switch case instead of if/else. +Use an enum for the event ids cases. + + +Signed-off-by: Thomas Renninger +CC: mjg@redhat.com +CC: linux-acpi@vger.kernel.org + +--- + drivers/platform/x86/hp-wmi.c | 51 ++++++++++++++++++++++++++---------------- + 1 file changed, 32 insertions(+), 19 deletions(-) + +Index: linux-2.6.33-master/drivers/platform/x86/hp-wmi.c +=================================================================== +--- linux-2.6.33-master.orig/drivers/platform/x86/hp-wmi.c ++++ linux-2.6.33-master/drivers/platform/x86/hp-wmi.c +@@ -58,6 +58,12 @@ enum hp_wmi_radio { + HPWMI_WWAN = 2, + }; + ++enum hp_wmi_event_ids { ++ HPWMI_DOCK_EVENT = 1, ++ HPWMI_BEZEL_BUTTON = 4, ++ HPWMI_WIRELESS = 5, ++}; ++ + static int __devinit hp_wmi_bios_setup(struct platform_device *device); + static int __exit hp_wmi_bios_remove(struct platform_device *device); + static int hp_wmi_resume_handler(struct device *device); +@@ -338,7 +344,7 @@ static void hp_wmi_notify(u32 value, voi + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + static struct key_entry *key; + union acpi_object *obj; +- int eventcode; ++ int eventcode, key_code; + acpi_status status; + + status = wmi_get_event_data(value, &response); +@@ -357,28 +363,32 @@ static void hp_wmi_notify(u32 value, voi + + eventcode = *((u8 *) obj->buffer.pointer); + kfree(obj); +- if (eventcode == 0x4) +- eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, +- 0); +- key = hp_wmi_get_entry_by_scancode(eventcode); +- if (key) { +- switch (key->type) { +- case KE_KEY: +- input_report_key(hp_wmi_input_dev, +- key->keycode, 1); +- input_sync(hp_wmi_input_dev); +- input_report_key(hp_wmi_input_dev, +- key->keycode, 0); +- input_sync(hp_wmi_input_dev); +- break; +- } +- } else if (eventcode == 0x1) { ++ switch (eventcode) { ++ case HPWMI_DOCK_EVENT: + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_dock_state()); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_tablet_state()); + input_sync(hp_wmi_input_dev); +- } else if (eventcode == 0x5) { ++ break; ++ case HPWMI_BEZEL_BUTTON: ++ key_code = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, ++ 0); ++ key = hp_wmi_get_entry_by_scancode(key_code); ++ if (key) { ++ switch (key->type) { ++ case KE_KEY: ++ input_report_key(hp_wmi_input_dev, ++ key->keycode, 1); ++ input_sync(hp_wmi_input_dev); ++ input_report_key(hp_wmi_input_dev, ++ key->keycode, 0); ++ input_sync(hp_wmi_input_dev); ++ break; ++ } ++ } ++ break; ++ case HPWMI_WIRELESS: + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), +@@ -391,9 +401,12 @@ static void hp_wmi_notify(u32 value, voi + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); +- } else ++ break; ++ default: + printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", + eventcode); ++ break; ++ } + } + + static int __init hp_wmi_input_setup(void) diff --git a/patches.fixes/hp_wmi_add_media_key.patch b/patches.fixes/hp_wmi_add_media_key.patch new file mode 100644 index 0000000..f828bca --- /dev/null +++ b/patches.fixes/hp_wmi_add_media_key.patch @@ -0,0 +1,25 @@ +From: Thomas Renninger +Subject: x86 platform drivers: hp-wmi Add media key 0x20e8 +References: bnc#598059 +Patch-Mainline: submitted + +Signed-off-by: Thomas Renninger +CC: mjg@redhat.com +CC: linux-acpi@vger.kernel.org + +--- + drivers/platform/x86/hp-wmi.c | 1 + + 1 file changed, 1 insertion(+) + +Index: linux-2.6.33-master/drivers/platform/x86/hp-wmi.c +=================================================================== +--- linux-2.6.33-master.orig/drivers/platform/x86/hp-wmi.c ++++ linux-2.6.33-master/drivers/platform/x86/hp-wmi.c +@@ -96,6 +96,7 @@ static struct key_entry hp_wmi_keymap[] + {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, + {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, + {KE_KEY, 0x20e6, KEY_PROG1}, ++ {KE_KEY, 0x20e8, KEY_MEDIA}, + {KE_KEY, 0x2142, KEY_MEDIA}, + {KE_KEY, 0x213b, KEY_INFO}, + {KE_KEY, 0x2169, KEY_DIRECTION}, diff --git a/patches.fixes/hp_wmi_catch_unkown_event_key_codes.patch b/patches.fixes/hp_wmi_catch_unkown_event_key_codes.patch new file mode 100644 index 0000000..235fbcf --- /dev/null +++ b/patches.fixes/hp_wmi_catch_unkown_event_key_codes.patch @@ -0,0 +1,39 @@ +From: Thomas Renninger +Subject: x86 platform drivers: hp-wmi Catch and log unkown event and key codes correctly +References: bnc#598059 +Patch-Mainline: submitted + +Signed-off-by: Thomas Renninger +CC: mjg@redhat.com +CC: linux-acpi@vger.kernel.org + +--- + drivers/platform/x86/hp-wmi.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +Index: linux-2.6.33-master/drivers/platform/x86/hp-wmi.c +=================================================================== +--- linux-2.6.33-master.orig/drivers/platform/x86/hp-wmi.c ++++ linux-2.6.33-master/drivers/platform/x86/hp-wmi.c +@@ -386,7 +386,9 @@ static void hp_wmi_notify(u32 value, voi + input_sync(hp_wmi_input_dev); + break; + } +- } ++ } else ++ printk(KERN_INFO "HP WMI: Unknown key code - 0x%x\n", ++ key_code); + break; + case HPWMI_WIRELESS: + if (wifi_rfkill) +@@ -403,8 +405,8 @@ static void hp_wmi_notify(u32 value, voi + hp_wmi_get_hw_state(HPWMI_WWAN)); + break; + default: +- printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", +- eventcode); ++ printk(KERN_INFO "HP WMI: Unknown eventcode - %d\n", ++ eventcode); + break; + } + } diff --git a/patches.fixes/hp_wmi_use_prefix_string.patch b/patches.fixes/hp_wmi_use_prefix_string.patch new file mode 100644 index 0000000..c9de151 --- /dev/null +++ b/patches.fixes/hp_wmi_use_prefix_string.patch @@ -0,0 +1,61 @@ +From: Thomas Renninger +Subject: x86 platform drivers: hp-wmi Use consistent prefix string for messages. +References: bnc#598059 +Patch-Mainline: submitted + +Signed-off-by: Thomas Renninger +CC: mjg@redhat.com +CC: linux-acpi@vger.kernel.org + +--- + drivers/platform/x86/hp-wmi.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +Index: linux-2.6.33-master/drivers/platform/x86/hp-wmi.c +=================================================================== +--- linux-2.6.33-master.orig/drivers/platform/x86/hp-wmi.c ++++ linux-2.6.33-master/drivers/platform/x86/hp-wmi.c +@@ -52,6 +52,8 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE9 + #define HPWMI_WIRELESS_QUERY 0x5 + #define HPWMI_HOTKEY_QUERY 0xc + ++#define PREFIX "HP WMI: " ++ + enum hp_wmi_radio { + HPWMI_WIFI = 0, + HPWMI_BLUETOOTH = 1, +@@ -349,14 +351,14 @@ static void hp_wmi_notify(u32 value, voi + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { +- printk(KERN_INFO "hp-wmi: bad event status 0x%x\n", status); ++ printk(KERN_INFO PREFIX "bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) { +- printk(KERN_INFO "HP WMI: Unknown response received\n"); ++ printk(KERN_INFO PREFIX "Unknown response received\n"); + kfree(obj); + return; + } +@@ -387,7 +389,7 @@ static void hp_wmi_notify(u32 value, voi + break; + } + } else +- printk(KERN_INFO "HP WMI: Unknown key code - 0x%x\n", ++ printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", + key_code); + break; + case HPWMI_WIRELESS: +@@ -405,7 +407,7 @@ static void hp_wmi_notify(u32 value, voi + hp_wmi_get_hw_state(HPWMI_WWAN)); + break; + default: +- printk(KERN_INFO "HP WMI: Unknown eventcode - %d\n", ++ printk(KERN_INFO PREFIX "Unknown eventcode - %d\n", + eventcode); + break; + } diff --git a/patches.fixes/ia64-configure-HAVE_UNSTABLE_SCHED_CLOCK-for-SGI_SN.patch b/patches.fixes/ia64-configure-HAVE_UNSTABLE_SCHED_CLOCK-for-SGI_SN.patch new file mode 100644 index 0000000..85321a5 --- /dev/null +++ b/patches.fixes/ia64-configure-HAVE_UNSTABLE_SCHED_CLOCK-for-SGI_SN.patch @@ -0,0 +1,44 @@ +Date: Tue, 6 Jan 2009 10:27:41 -0600 +From: Dimitri Sivanich +To: linux-ia64@vger.kernel.org, Tony Luck , + Greg KH +Cc: linux-kernel@vger.kernel.org, + Peter Zijlstra , + Gregory Haskins , Nick Piggin , + Tony Luck , Robin Holt +Subject: configure HAVE_UNSTABLE_SCHED_CLOCK for SGI_SN systems +Patch-mainline: not yet + +Turn on CONFIG_HAVE_UNSTABLE_SCHED_CLOCK for SGI_SN. + +SGI Altix has unsynchronized itc clocks. This results in rq->clock +occasionally being set to a time in the past by a remote cpu. + +Note that it is possible that this problem may exist for other ia64 +machines as well, based on the following comment for sched_clock() in +arch/ia64/kernel/head.S: + + * Return a CPU-local timestamp in nano-seconds. This timestamp is + * NOT synchronized across CPUs its return value must never be + * compared against the values returned on another CPU. The usage in + * kernel/sched.c ensures that. + + +Signed-off-by: Dimitri Sivanich +Signed-off-by: Gregory Haskins + +--- + + arch/ia64/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/ia64/Kconfig ++++ b/arch/ia64/Kconfig +@@ -545,6 +545,7 @@ config IA64_MC_ERR_INJECT + + config SGI_SN + def_bool y if (IA64_SGI_SN2 || IA64_GENERIC) ++ select HAVE_UNSTABLE_SCHED_CLOCK + + config IA64_ESI + bool "ESI (Extensible SAL Interface) support" diff --git a/patches.fixes/ia64-sparse-fixes.diff b/patches.fixes/ia64-sparse-fixes.diff new file mode 100644 index 0000000..7a7f7a3 --- /dev/null +++ b/patches.fixes/ia64-sparse-fixes.diff @@ -0,0 +1,53 @@ +From: Jan Blunck +Subject: ia64-kvm: fix sparse warnings +Patch-mainline: not yet + +This patch fixes some sparse warning about dubious one-bit signed bitfield. + +Signed-off-by: Jan Blunck +--- + arch/ia64/kvm/vti.h | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +--- a/arch/ia64/kvm/vti.h ++++ b/arch/ia64/kvm/vti.h +@@ -83,13 +83,13 @@ + union vac { + unsigned long value; + struct { +- int a_int:1; +- int a_from_int_cr:1; +- int a_to_int_cr:1; +- int a_from_psr:1; +- int a_from_cpuid:1; +- int a_cover:1; +- int a_bsw:1; ++ unsigned int a_int:1; ++ unsigned int a_from_int_cr:1; ++ unsigned int a_to_int_cr:1; ++ unsigned int a_from_psr:1; ++ unsigned int a_from_cpuid:1; ++ unsigned int a_cover:1; ++ unsigned int a_bsw:1; + long reserved:57; + }; + }; +@@ -97,12 +97,12 @@ union vac { + union vdc { + unsigned long value; + struct { +- int d_vmsw:1; +- int d_extint:1; +- int d_ibr_dbr:1; +- int d_pmc:1; +- int d_to_pmd:1; +- int d_itm:1; ++ unsigned int d_vmsw:1; ++ unsigned int d_extint:1; ++ unsigned int d_ibr_dbr:1; ++ unsigned int d_pmc:1; ++ unsigned int d_to_pmd:1; ++ unsigned int d_itm:1; + long reserved:58; + }; + }; diff --git a/patches.fixes/ieee1394-sbp2_long_sysfs_ieee1394_id.patch b/patches.fixes/ieee1394-sbp2_long_sysfs_ieee1394_id.patch new file mode 100644 index 0000000..cb779e4 --- /dev/null +++ b/patches.fixes/ieee1394-sbp2_long_sysfs_ieee1394_id.patch @@ -0,0 +1,27 @@ +From: unknown@suse.de +Subject: some unknown ieee1394 patch +Patch-mainline: not yet + +make the long format the default because its also the default in the +new firewire stack. +Maybe it simplifies migration for new 10.3 installs to 11.0 or later. +Maybe it is bad for existing 10.3 and earlier installs. + +modprobe -v sbp2 sbp2_long_sysfs_ieee1394_id=0 to get the old short name +modprobe -v sbp2 sbp2_long_sysfs_ieee1394_id=1 to get the new long name + +--- + drivers/ieee1394/sbp2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/ieee1394/sbp2.c ++++ b/drivers/ieee1394/sbp2.c +@@ -225,7 +225,7 @@ MODULE_PARM_DESC(workarounds, "Work arou + * independent of the implementation of the ieee1394 nodemgr, the longer format + * is recommended for future use. + */ +-static int sbp2_long_sysfs_ieee1394_id; ++static int sbp2_long_sysfs_ieee1394_id = 1; + module_param_named(long_ieee1394_id, sbp2_long_sysfs_ieee1394_id, bool, 0644); + MODULE_PARM_DESC(long_ieee1394_id, "8+3+2 bytes format of ieee1394_id in sysfs " + "(default = backwards-compatible = N, SAM-conforming = Y)"); diff --git a/patches.fixes/input-add-acer-aspire-5710-to-nomux.patch b/patches.fixes/input-add-acer-aspire-5710-to-nomux.patch new file mode 100644 index 0000000..4cd6b40 --- /dev/null +++ b/patches.fixes/input-add-acer-aspire-5710-to-nomux.patch @@ -0,0 +1,30 @@ +From: Jiri Kosina +Subject: Input: Add Acer Aspire 5710 to nomux blacklist +References: bnc#404881 +Patch-mainline: submitted + +Acer Aspire needs to be added to nomux blacklist, otherwise the touchpad +misbehaves. + +Signed-off-by: Jiri Kosina + +--- + drivers/input/serio/i8042-x86ia64io.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/input/serio/i8042-x86ia64io.h ++++ b/drivers/input/serio/i8042-x86ia64io.h +@@ -360,6 +360,13 @@ static const struct dmi_system_id __init + }, + }, + { ++ /* Acer Aspire 5710 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), ++ }, ++ }, ++ { + /* Gericom Bellagio */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), diff --git a/patches.fixes/kbuild-fix-generating-of-.symtypes-files b/patches.fixes/kbuild-fix-generating-of-.symtypes-files new file mode 100644 index 0000000..87813a6 --- /dev/null +++ b/patches.fixes/kbuild-fix-generating-of-.symtypes-files @@ -0,0 +1,28 @@ +Subject: kbuild: fix generating of *.symtypes files +From: Michal Marek +Patch-mainline: submitted 2009-06-29 + +Commit 37a8d9f ("kbuild: simplify use of genksyms") broke generating of +*.symtypes files during build (with KBUILD_SYMTYPES set). This patch +fixes it. + +Signed-off-by: Michal Marek + +--- + scripts/Makefile.build | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/scripts/Makefile.build ++++ b/scripts/Makefile.build +@@ -156,9 +156,9 @@ $(obj)/%.i: $(src)/%.c FORCE + + cmd_gensymtypes = \ + $(CPP) -D__GENKSYMS__ $(c_flags) $< | \ +- $(GENKSYMS) -T $@ -a $(ARCH) \ ++ $(GENKSYMS) $(if $(strip $(1)), -T $(@:.o=.symtypes)) -a $(ARCH) \ + $(if $(KBUILD_PRESERVE),-p) \ +- $(if $(1),-r $(firstword $(wildcard $(@:.symtypes=.symref) /dev/null))) ++ -r $(firstword $(wildcard $(basename $@).symref /dev/null)) + + quiet_cmd_cc_symtypes_c = SYM $(quiet_modtag) $@ + cmd_cc_symtypes_c = \ diff --git a/patches.fixes/kvm-ioapic.patch b/patches.fixes/kvm-ioapic.patch new file mode 100644 index 0000000..bc02206 --- /dev/null +++ b/patches.fixes/kvm-ioapic.patch @@ -0,0 +1,21 @@ +From: agraf@suse.de +Subject: Ignore apic polarity +Patch-mainline: unknown +References: bnc#556564 + +--- + virt/kvm/ioapic.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/virt/kvm/ioapic.c ++++ b/virt/kvm/ioapic.c +@@ -200,7 +200,8 @@ int kvm_ioapic_set_irq(struct kvm_ioapic + spin_lock(&ioapic->lock); + if (irq >= 0 && irq < IOAPIC_NUM_PINS) { + entry = ioapic->redirtbl[irq]; +- level ^= entry.fields.polarity; ++// polarity is always active high in qemu ++// level ^= entry.fields.polarity; + if (!level) + ioapic->irr &= ~mask; + else { diff --git a/patches.fixes/kvm-macos.patch b/patches.fixes/kvm-macos.patch new file mode 100644 index 0000000..cc7504b --- /dev/null +++ b/patches.fixes/kvm-macos.patch @@ -0,0 +1,73 @@ +From: agraf@suse.de +Subject: Implement some missing intercepts so osx doesn't blow up +Patch-mainline: unknown +References: bnc#556564 + +--- + arch/x86/kvm/svm.c | 20 ++++++++++++++++++-- + arch/x86/kvm/x86.c | 4 +++- + 2 files changed, 21 insertions(+), 3 deletions(-) + +--- a/arch/x86/kvm/svm.c ++++ b/arch/x86/kvm/svm.c +@@ -1995,6 +1995,22 @@ static int skinit_interception(struct vc + return 1; + } + ++static int monitor_interception(struct vcpu_svm *svm) ++{ ++ svm->next_rip = kvm_rip_read(&svm->vcpu) + 3; ++ skip_emulated_instruction(&svm->vcpu); ++ ++ return 1; ++} ++ ++static int mwait_interception(struct vcpu_svm *svm) ++{ ++ svm->next_rip = kvm_rip_read(&svm->vcpu) + 3; ++ skip_emulated_instruction(&svm->vcpu); ++ ++ return kvm_emulate_halt(&svm->vcpu); ++} ++ + static int invalid_op_interception(struct vcpu_svm *svm) + { + kvm_queue_exception(&svm->vcpu, UD_VECTOR); +@@ -2376,8 +2392,8 @@ static int (*svm_exit_handlers[])(struct + [SVM_EXIT_CLGI] = clgi_interception, + [SVM_EXIT_SKINIT] = skinit_interception, + [SVM_EXIT_WBINVD] = emulate_on_interception, +- [SVM_EXIT_MONITOR] = invalid_op_interception, +- [SVM_EXIT_MWAIT] = invalid_op_interception, ++ [SVM_EXIT_MONITOR] = monitor_interception, ++ [SVM_EXIT_MWAIT] = mwait_interception, + [SVM_EXIT_NPF] = pf_interception, + }; + +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -1144,6 +1144,7 @@ int kvm_set_msr_common(struct kvm_vcpu * + case MSR_VM_HSAVE_PA: + case MSR_AMD64_PATCH_LOADER: + break; ++ case 0xe2: + case 0x200 ... 0x2ff: + return set_msr_mtrr(vcpu, msr, data); + case MSR_IA32_APICBASE: +@@ -1400,6 +1401,7 @@ int kvm_get_msr_common(struct kvm_vcpu * + case MSR_K8_INT_PENDING_MSG: + case MSR_AMD64_NB_CFG: + case MSR_FAM10H_MMIO_CONF_BASE: ++ case 0xe2: + data = 0; + break; + case MSR_MTRRcap: +@@ -1848,7 +1850,7 @@ static void do_cpuid_ent(struct kvm_cpui + 0 /* Reserved */ | f_lm | F(3DNOWEXT) | F(3DNOW); + /* cpuid 1.ecx */ + const u32 kvm_supported_word4_x86_features = +- F(XMM3) | 0 /* Reserved, DTES64, MONITOR */ | ++ F(XMM3) | bit((4*32+ 3)) /* MONITOR */ | 0 /* Reserved, DTES64 */ | + 0 /* DS-CPL, VMX, SMX, EST */ | + 0 /* TM2 */ | F(SSSE3) | 0 /* CNXT-ID */ | 0 /* Reserved */ | + 0 /* Reserved */ | F(CX16) | 0 /* xTPR Update, PDCM */ | diff --git a/patches.fixes/make-note_interrupt-fast.diff b/patches.fixes/make-note_interrupt-fast.diff new file mode 100644 index 0000000..228f43a --- /dev/null +++ b/patches.fixes/make-note_interrupt-fast.diff @@ -0,0 +1,193 @@ +From: Bernhard Walle +Subject: Fix performance regression on large IA64 systems +References: bnc #469589 +Patch-mainline: no (and never will) + +This patch tries to address a performance regression discovered by SGI. + +Patch b60c1f6ffd88850079ae419aa933ab0eddbd5535 removes the call +to note_interrupt() in __do_IRQ(). Patch d85a60d85ea5b7c597508c1510c88e657773d378 +adds it again. Because it's needed for irqpoll. + +That patch now introduces a new parameter 'only_fixup' for note_interrupt(). +This parameter determines two cases: + + TRUE => The function should be only executed when irqfixup is set. + Either 'irqpoll' or 'irqfixup' directly set that. + + FALSE => Just the behaviour as note_interrupt() always had. + +Now the patch converts all calls of note_interrupt() to only_fixup=FALSE, +except the call that has been removed by b60c1f6ffd88850079ae419aa933ab0eddbd5535. +So that call is always done, but the body is only executed when either +'irqpoll' or 'irqfixup' are specified. + +This patch is not meant for mainline inclusion in the first run! + + +Signed-off-by: Bernhard Walle + +--- + arch/arm/mach-ns9xxx/irq.c | 2 +- + arch/powerpc/platforms/cell/interrupt.c | 2 +- + drivers/mfd/ezx-pcap.c | 3 ++- + drivers/mfd/twl4030-irq.c | 2 +- + include/linux/irq.h | 2 +- + kernel/irq/chip.c | 12 ++++++------ + kernel/irq/handle.c | 4 ++-- + kernel/irq/spurious.c | 10 +++++++++- + 8 files changed, 23 insertions(+), 14 deletions(-) + +--- a/arch/arm/mach-ns9xxx/irq.c ++++ b/arch/arm/mach-ns9xxx/irq.c +@@ -85,7 +85,7 @@ static void handle_prio_irq(unsigned int + /* XXX: There is no direct way to access noirqdebug, so check + * unconditionally for spurious irqs... + * Maybe this function should go to kernel/irq/chip.c? */ +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + + raw_spin_lock(&desc->lock); + desc->status &= ~IRQ_INPROGRESS; +--- a/arch/powerpc/platforms/cell/interrupt.c ++++ b/arch/powerpc/platforms/cell/interrupt.c +@@ -268,7 +268,7 @@ static void handle_iic_irq(unsigned int + raw_spin_unlock(&desc->lock); + action_ret = handle_IRQ_event(irq, action); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + raw_spin_lock(&desc->lock); + + } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING); +--- a/drivers/mfd/ezx-pcap.c ++++ b/drivers/mfd/ezx-pcap.c +@@ -203,7 +203,8 @@ static void pcap_isr_work(struct work_st + break; + + if (desc->status & IRQ_DISABLED) +- note_interrupt(irq, desc, IRQ_NONE); ++ note_interrupt(irq, desc, IRQ_NONE, ++ false); + else + desc->handle_irq(irq, desc); + } +--- a/drivers/mfd/twl4030-irq.c ++++ b/drivers/mfd/twl4030-irq.c +@@ -330,7 +330,7 @@ static int twl4030_irq_thread(void *data + */ + if (d->status & IRQ_DISABLED) + note_interrupt(module_irq, d, +- IRQ_NONE); ++ IRQ_NONE, false); + else + d->handle_irq(module_irq, d); + } +--- a/include/linux/irq.h ++++ b/include/linux/irq.h +@@ -324,7 +324,7 @@ static inline void generic_handle_irq(un + + /* Handling of unhandled and spurious interrupts: */ + extern void note_interrupt(unsigned int irq, struct irq_desc *desc, +- irqreturn_t action_ret); ++ irqreturn_t action_ret, bool only_fixup); + + /* Resending of interrupts :*/ + void check_irq_resend(struct irq_desc *desc, unsigned int irq); +--- a/kernel/irq/chip.c ++++ b/kernel/irq/chip.c +@@ -390,7 +390,7 @@ void handle_nested_irq(unsigned int irq) + + action_ret = action->thread_fn(action->irq, action->dev_id); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + + raw_spin_lock_irq(&desc->lock); + desc->status &= ~IRQ_INPROGRESS; +@@ -434,7 +434,7 @@ handle_simple_irq(unsigned int irq, stru + + action_ret = handle_IRQ_event(irq, action); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + + raw_spin_lock(&desc->lock); + desc->status &= ~IRQ_INPROGRESS; +@@ -479,7 +479,7 @@ handle_level_irq(unsigned int irq, struc + + action_ret = handle_IRQ_event(irq, action); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + + raw_spin_lock(&desc->lock); + desc->status &= ~IRQ_INPROGRESS; +@@ -535,7 +535,7 @@ handle_fasteoi_irq(unsigned int irq, str + + action_ret = handle_IRQ_event(irq, action); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + + raw_spin_lock(&desc->lock); + desc->status &= ~IRQ_INPROGRESS; +@@ -613,7 +613,7 @@ handle_edge_irq(unsigned int irq, struct + raw_spin_unlock(&desc->lock); + action_ret = handle_IRQ_event(irq, action); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + raw_spin_lock(&desc->lock); + + } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING); +@@ -642,7 +642,7 @@ handle_percpu_irq(unsigned int irq, stru + + action_ret = handle_IRQ_event(irq, desc->action); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + + if (desc->chip->eoi) + desc->chip->eoi(irq); +--- a/kernel/irq/handle.c ++++ b/kernel/irq/handle.c +@@ -465,7 +465,7 @@ unsigned int __do_IRQ(unsigned int irq) + if (likely(!(desc->status & IRQ_DISABLED))) { + action_ret = handle_IRQ_event(irq, desc->action); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, true); + } + desc->chip->end(irq); + return 1; +@@ -519,7 +519,7 @@ unsigned int __do_IRQ(unsigned int irq) + + action_ret = handle_IRQ_event(irq, action); + if (!noirqdebug) +- note_interrupt(irq, desc, action_ret); ++ note_interrupt(irq, desc, action_ret, false); + + raw_spin_lock(&desc->lock); + if (likely(!(desc->status & IRQ_PENDING))) +--- a/kernel/irq/spurious.c ++++ b/kernel/irq/spurious.c +@@ -213,9 +213,17 @@ try_misrouted_irq(unsigned int irq, stru + return action && (action->flags & IRQF_IRQPOLL); + } + ++/* ++ * The parameter "only_fixup" means that the function should be only executed ++ * if this parameter is set either to false or to true simultaneously with ++ * irqfixup enabled. ++ */ + void note_interrupt(unsigned int irq, struct irq_desc *desc, +- irqreturn_t action_ret) ++ irqreturn_t action_ret, bool only_fixup) + { ++ if (only_fixup && irqfixup == 0) ++ return; ++ + if (unlikely(action_ret != IRQ_HANDLED)) { + /* + * If we are seeing only the odd spurious IRQ caused by diff --git a/patches.fixes/netfilter-remove-pointless-config_nf_ct_acct-warning b/patches.fixes/netfilter-remove-pointless-config_nf_ct_acct-warning new file mode 100644 index 0000000..1a1e754 --- /dev/null +++ b/patches.fixes/netfilter-remove-pointless-config_nf_ct_acct-warning @@ -0,0 +1,120 @@ +From: Jeff Mahoney +Subject: netfilter: Remove pointless CONFIG_NF_CT_ACCT warning +References: bnc#552033 (and others) +Patch-mainline: not yet + + CONFIG_NF_CT_ACCT was scheduled at 2.6.27 release-time to be removed + in 2.6.29. That hasn't happened, and it's sort of pointless to remove the + option as it sets the default value for whether it's nf_conntrack_acct is + enabled at boot-time. + + It still issues a really annoying warning though. This patch properly + documents the option as controlling the default and undeprecates it. It + also renames the option to a more subsystem-consistent NF_CONNTRACK_ACCT. + +Signed-off-by: Jeff Mahoney +--- + Documentation/feature-removal-schedule.txt | 9 --------- + Documentation/kernel-parameters.txt | 3 +-- + net/netfilter/Kconfig | 11 +++++------ + net/netfilter/nf_conntrack_acct.c | 8 +------- + net/netfilter/nf_conntrack_netlink.c | 2 -- + 5 files changed, 7 insertions(+), 26 deletions(-) + +--- a/Documentation/feature-removal-schedule.txt ++++ b/Documentation/feature-removal-schedule.txt +@@ -313,15 +313,6 @@ Who: Johannes Berg +- +---------------------------- +- + What: sysfs ui for changing p4-clockmod parameters + When: September 2009 + Why: See commits 129f8ae9b1b5be94517da76009ea956e89104ce8 and +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -1567,8 +1567,7 @@ and is between 256 and 4096 characters. + [NETFILTER] Enable connection tracking flow accounting + 0 to disable accounting + 1 to enable accounting +- Default value depends on CONFIG_NF_CT_ACCT that is +- going to be removed in 2.6.29. ++ Default value depends on CONFIG_NF_CT_ACCT. + + nfsaddrs= [NFS] + See Documentation/filesystems/nfs/nfsroot.txt. +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -40,12 +40,13 @@ config NF_CONNTRACK + + if NF_CONNTRACK + +-config NF_CT_ACCT +- bool "Connection tracking flow accounting" ++config NF_CONNTRACK_ACCT ++ bool "Enable connection tracking flow accounting by default" + depends on NETFILTER_ADVANCED + help +- If this option is enabled, the connection tracking code will +- keep per-flow packet and byte counters. ++ ++ This option controls whether per-flow packet and byte counters ++ are enabled by default. + + Those counters can be used for flow-based accounting or the + `connbytes' match. +@@ -57,8 +58,6 @@ config NF_CT_ACCT + You may also disable/enable it on a running system with: + sysctl net.netfilter.nf_conntrack_acct=0/1 + +- This option will be removed in 2.6.29. +- + If unsure, say `N'. + + config NF_CONNTRACK_MARK +--- a/net/netfilter/nf_conntrack_acct.c ++++ b/net/netfilter/nf_conntrack_acct.c +@@ -16,7 +16,7 @@ + #include + #include + +-#ifdef CONFIG_NF_CT_ACCT ++#ifdef CONFIG_NF_CONNTRACK_ACCT + #define NF_CT_ACCT_DEFAULT 1 + #else + #define NF_CT_ACCT_DEFAULT 0 +@@ -113,12 +113,6 @@ int nf_conntrack_acct_init(struct net *n + net->ct.sysctl_acct = nf_ct_acct; + + if (net_eq(net, &init_net)) { +-#ifdef CONFIG_NF_CT_ACCT +- printk(KERN_WARNING "CONFIG_NF_CT_ACCT is deprecated and will be removed soon. Please use\n"); +- printk(KERN_WARNING "nf_conntrack.acct=1 kernel parameter, acct=1 nf_conntrack module option or\n"); +- printk(KERN_WARNING "sysctl net.netfilter.nf_conntrack_acct=1 to enable it.\n"); +-#endif +- + ret = nf_ct_extend_register(&acct_extend); + if (ret < 0) { + printk(KERN_ERR "nf_conntrack_acct: Unable to register extension\n"); +--- a/net/netfilter/nf_conntrack_netlink.c ++++ b/net/netfilter/nf_conntrack_netlink.c +@@ -435,11 +435,9 @@ ctnetlink_nlmsg_size(const struct nf_con + + 3 * nla_total_size(sizeof(u_int8_t)) /* CTA_PROTO_NUM */ + + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */ + + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */ +-#ifdef CONFIG_NF_CT_ACCT + + 2 * nla_total_size(0) /* CTA_COUNTERS_ORIG|REPL */ + + 2 * nla_total_size(sizeof(uint64_t)) /* CTA_COUNTERS_PACKETS */ + + 2 * nla_total_size(sizeof(uint64_t)) /* CTA_COUNTERS_BYTES */ +-#endif + + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */ + + nla_total_size(0) /* CTA_PROTOINFO */ + + nla_total_size(0) /* CTA_HELP */ diff --git a/patches.fixes/nfs-acl-caching.diff b/patches.fixes/nfs-acl-caching.diff new file mode 100644 index 0000000..3121700 --- /dev/null +++ b/patches.fixes/nfs-acl-caching.diff @@ -0,0 +1,46 @@ +From: Andreas Gruenbacher +Subject: "No acl" entry put in client-side acl cache instead of "not cached" +References: 171059 + +When the acl of a file is not cached and only the default acl of that +file is requested, a NULL "no acl" entry is put in the client-side acl +cache of nfs instead of ERR_PTR(-EAGAIN) "not cached". + +Signed-off-by: Andreas Gruenbacher + +Index: linux-2.6.16/fs/nfs/nfs3acl.c +=================================================================== +--- linux-2.6.16.orig/fs/nfs/nfs3acl.c ++++ linux-2.6.16/fs/nfs/nfs3acl.c +@@ -172,8 +172,10 @@ static void nfs3_cache_acls(struct inode + inode->i_ino, acl, dfacl); + spin_lock(&inode->i_lock); + __nfs3_forget_cached_acls(NFS_I(inode)); +- nfsi->acl_access = posix_acl_dup(acl); +- nfsi->acl_default = posix_acl_dup(dfacl); ++ if (!IS_ERR(acl)) ++ nfsi->acl_access = posix_acl_dup(acl); ++ if (!IS_ERR(dfacl)) ++ nfsi->acl_default = posix_acl_dup(dfacl); + spin_unlock(&inode->i_lock); + } + +@@ -250,7 +252,9 @@ struct posix_acl *nfs3_proc_getacl(struc + res.acl_access = NULL; + } + } +- nfs3_cache_acls(inode, res.acl_access, res.acl_default); ++ nfs3_cache_acls(inode, ++ (res.mask & NFS_ACL) ? res.acl_access : ERR_PTR(-EINVAL), ++ (res.mask & NFS_DFACL) ? res.acl_default : ERR_PTR(-EINVAL)); + + switch(type) { + case ACL_TYPE_ACCESS: +@@ -321,6 +325,7 @@ static int nfs3_proc_setacls(struct inod + switch (status) { + case 0: + status = nfs_refresh_inode(inode, &fattr); ++ nfs3_cache_acls(inode, acl, dfacl); + break; + case -EPFNOSUPPORT: + case -EPROTONOSUPPORT: diff --git a/patches.fixes/nfs-slot-table-alloc b/patches.fixes/nfs-slot-table-alloc new file mode 100644 index 0000000..0946ad9 --- /dev/null +++ b/patches.fixes/nfs-slot-table-alloc @@ -0,0 +1,31 @@ +From: Michal Hocko +Subject: Don't fail allocations for the slot table when mounting an NFS filesystem +Patch-mainline: no +References: bnc#519820 + +When the *_slot_table_entries exceeds 111, the slot_table_size +exceeds 32K and an order-4 allocation is forced. This does not +retry nearly as much as order-3 so failure is more likely. +But mount and autofs in particular doesn't cope well with failure. +So force __GFP_REPEAT - the assumption is that people will only +set the slot_table_size sysctl large on a machine with plenty +of memory, so this should not block indefinitely. + +Acked-by: Neil Brown +Signed-off-by: Neil Brown + +--- + net/sunrpc/xprtsock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/sunrpc/xprtsock.c ++++ b/net/sunrpc/xprtsock.c +@@ -2295,7 +2295,7 @@ static struct rpc_xprt *xs_setup_xprt(st + xprt = &new->xprt; + + xprt->max_reqs = slot_table_size; +- xprt->slot = kcalloc(xprt->max_reqs, sizeof(struct rpc_rqst), GFP_KERNEL); ++ xprt->slot = kcalloc(xprt->max_reqs, sizeof(struct rpc_rqst), GFP_KERNEL | __GFP_REPEAT); + if (xprt->slot == NULL) { + kfree(xprt); + dprintk("RPC: xs_setup_xprt: couldn't allocate slot " diff --git a/patches.fixes/nfs-write.c-bug-removal.patch b/patches.fixes/nfs-write.c-bug-removal.patch new file mode 100644 index 0000000..d501cfb --- /dev/null +++ b/patches.fixes/nfs-write.c-bug-removal.patch @@ -0,0 +1,170 @@ +From: ffilz@us.ibm.com +Subject: Revert "NFS: Allow redirtying of a completed unstable write." +Patch-mainline: REVERT patch from 2.6.27 +References: 442267 + +mainline commit e468bae97d243fe0e1515abaa1f7d0edf1476ad0 +introduces a BUG() that is apprently fairly easy to trigger. +As it is just making a minor performance enhancement, it is best to +revert the patch until the issue is better understood. + +Acked-by: NeilBrown +Signed-off-by: Neil Brown + +--- + fs/nfs/write.c | 65 ++++++++++++++++++++++++++++----------------------------- + 1 file changed, 33 insertions(+), 32 deletions(-) + +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -250,9 +250,12 @@ static int nfs_page_async_flush(struct n + return ret; + spin_lock(&inode->i_lock); + } +- if (test_bit(PG_CLEAN, &req->wb_flags)) { ++ if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { ++ /* This request is marked for commit */ + spin_unlock(&inode->i_lock); +- BUG(); ++ nfs_clear_page_tag_locked(req); ++ nfs_pageio_complete(pgio); ++ return 0; + } + if (nfs_set_page_writeback(page) != 0) { + spin_unlock(&inode->i_lock); +@@ -411,6 +414,19 @@ nfs_mark_request_dirty(struct nfs_page * + __set_page_dirty_nobuffers(req->wb_page); + } + ++/* ++ * Check if a request is dirty ++ */ ++static inline int ++nfs_dirty_request(struct nfs_page *req) ++{ ++ struct page *page = req->wb_page; ++ ++ if (page == NULL || test_bit(PG_NEED_COMMIT, &req->wb_flags)) ++ return 0; ++ return !PageWriteback(page); ++} ++ + #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) + /* + * Add a request to the inode's commit list. +@@ -422,7 +438,7 @@ nfs_mark_request_commit(struct nfs_page + struct nfs_inode *nfsi = NFS_I(inode); + + spin_lock(&inode->i_lock); +- set_bit(PG_CLEAN, &(req)->wb_flags); ++ set_bit(PG_NEED_COMMIT, &(req)->wb_flags); + radix_tree_tag_set(&nfsi->nfs_page_tree, + req->wb_index, + NFS_PAGE_TAG_COMMIT); +@@ -432,19 +448,6 @@ nfs_mark_request_commit(struct nfs_page + __mark_inode_dirty(inode, I_DIRTY_DATASYNC); + } + +-static int +-nfs_clear_request_commit(struct nfs_page *req) +-{ +- struct page *page = req->wb_page; +- +- if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { +- dec_zone_page_state(page, NR_UNSTABLE_NFS); +- dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); +- return 1; +- } +- return 0; +-} +- + static inline + int nfs_write_need_commit(struct nfs_write_data *data) + { +@@ -454,7 +457,7 @@ int nfs_write_need_commit(struct nfs_wri + static inline + int nfs_reschedule_unstable_write(struct nfs_page *req) + { +- if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { ++ if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { + nfs_mark_request_commit(req); + return 1; + } +@@ -470,12 +473,6 @@ nfs_mark_request_commit(struct nfs_page + { + } + +-static inline int +-nfs_clear_request_commit(struct nfs_page *req) +-{ +- return 0; +-} +- + static inline + int nfs_write_need_commit(struct nfs_write_data *data) + { +@@ -533,8 +530,11 @@ static void nfs_cancel_commit_list(struc + + while(!list_empty(head)) { + req = nfs_list_entry(head->next); ++ dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); ++ dec_bdi_stat(req->wb_page->mapping->backing_dev_info, ++ BDI_RECLAIMABLE); + nfs_list_remove_request(req); +- nfs_clear_request_commit(req); ++ clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); + nfs_inode_remove_request(req); + nfs_unlock_request(req); + } +@@ -614,7 +614,8 @@ static struct nfs_page *nfs_try_to_updat + * Note: nfs_flush_incompatible() will already + * have flushed out requests having wrong owners. + */ +- if (offset > rqend ++ if (!nfs_dirty_request(req) ++ || offset > rqend + || end < req->wb_offset) + goto out_flushme; + +@@ -630,10 +631,6 @@ static struct nfs_page *nfs_try_to_updat + spin_lock(&inode->i_lock); + } + +- if (nfs_clear_request_commit(req)) +- radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree, +- req->wb_index, NFS_PAGE_TAG_COMMIT); +- + /* Okay, the request matches. Update the region */ + if (offset < req->wb_offset) { + req->wb_offset = offset; +@@ -715,7 +712,8 @@ int nfs_flush_incompatible(struct file * + req = nfs_page_find_request(page); + if (req == NULL) + return 0; +- do_flush = req->wb_page != page || req->wb_context != ctx; ++ do_flush = req->wb_page != page || req->wb_context != ctx ++ || !nfs_dirty_request(req); + nfs_release_request(req); + if (!do_flush) + return 0; +@@ -1341,7 +1339,10 @@ static void nfs_commit_release(void *cal + while (!list_empty(&data->pages)) { + req = nfs_list_entry(data->pages.next); + nfs_list_remove_request(req); +- nfs_clear_request_commit(req); ++ clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); ++ dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); ++ dec_bdi_stat(req->wb_page->mapping->backing_dev_info, ++ BDI_RECLAIMABLE); + + dprintk("NFS: commit (%s/%lld %d@%lld)", + req->wb_context->path.dentry->d_inode->i_sb->s_id, +@@ -1516,7 +1517,7 @@ int nfs_wb_page_cancel(struct inode *ino + req = nfs_page_find_request(page); + if (req == NULL) + goto out; +- if (test_bit(PG_CLEAN, &req->wb_flags)) { ++ if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { + nfs_release_request(req); + break; + } diff --git a/patches.fixes/nfsd-05-sunrpc-cache-allow-thread-to-block-while-waiting-for.patch b/patches.fixes/nfsd-05-sunrpc-cache-allow-thread-to-block-while-waiting-for.patch new file mode 100644 index 0000000..9e052da --- /dev/null +++ b/patches.fixes/nfsd-05-sunrpc-cache-allow-thread-to-block-while-waiting-for.patch @@ -0,0 +1,155 @@ +Patch-mainline: submitted 04aug2009 +References: bnc#498708 +From: NeilBrown +Date: Tue, 4 Aug 2009 15:06:38 +1000 +Subject: [PATCH 07/12] sunrpc/cache: allow thread to block while waiting for cache update. + +The current practice of waiting for cache updates by queueing the +whole request to be retried has (at least) two problems. + +1/ We NFSv4, requests can be quite complex and re-trying a whole + request when a later part fails should only be a list-resort, not a + normal practice. + +2/ Large requests, and in particular any 'write' request, will not be + queued by the current code and doing so would be undesirable. + +In many cases only a very sort wait is needed before the cache gets +valid data. + +So, providing the underlying transport permits it by setting + ->thread_wait, +arrange to wait briefly for an upcall to be completed (as reflected in +the clearing of CACHE_PENDING). +If the short wait was not long enough and CACHE_PENDING is still set, +fall back on the old approach. + +The 'thread_wait' value is set to 5 seconds when there are spare +threads, and 1 second when there are no spare threads. + +These values are probably much higher than needed, but will ensure +some forward progress. + +[Fixed 18Jan2010 to return -ve from cache_refer_req waits for the + upcall to complete instead of deferring the request. + Thanks to Dong Yang Li +] + +Signed-off-by: NeilBrown + +--- + include/linux/sunrpc/cache.h | 3 ++ + net/sunrpc/cache.c | 44 ++++++++++++++++++++++++++++++++++++++++++- + net/sunrpc/svc_xprt.c | 11 ++++++++++ + 3 files changed, 57 insertions(+), 1 deletion(-) + +--- a/include/linux/sunrpc/cache.h ++++ b/include/linux/sunrpc/cache.h +@@ -125,6 +125,9 @@ struct cache_detail { + */ + struct cache_req { + struct cache_deferred_req *(*defer)(struct cache_req *req); ++ int thread_wait; /* How long (jiffies) we can block the ++ * current thread to wait for updates. ++ */ + }; + /* this must be embedded in a deferred_request that is being + * delayed awaiting cache-fill +--- a/net/sunrpc/cache.c ++++ b/net/sunrpc/cache.c +@@ -497,10 +497,22 @@ static LIST_HEAD(cache_defer_list); + static struct list_head cache_defer_hash[DFR_HASHSIZE]; + static int cache_defer_cnt; + ++struct thread_deferred_req { ++ struct cache_deferred_req handle; ++ wait_queue_head_t wait; ++}; ++static void cache_restart_thread(struct cache_deferred_req *dreq, int too_many) ++{ ++ struct thread_deferred_req *dr = ++ container_of(dreq, struct thread_deferred_req, handle); ++ wake_up(&dr->wait); ++} ++ + static int cache_defer_req(struct cache_req *req, struct cache_head *item) + { + struct cache_deferred_req *dreq, *discard; + int hash = DFR_HASH(item); ++ struct thread_deferred_req sleeper; + + if (cache_defer_cnt >= DFR_MAX) { + /* too much in the cache, randomly drop this one, +@@ -509,7 +521,14 @@ static int cache_defer_req(struct cache_ + if (net_random()&1) + return -ENOMEM; + } +- dreq = req->defer(req); ++ if (req->thread_wait) { ++ dreq = &sleeper.handle; ++ init_waitqueue_head(&sleeper.wait); ++ dreq->revisit = cache_restart_thread; ++ } else ++ dreq = req->defer(req); ++ ++ retry: + if (dreq == NULL) + return -ENOMEM; + +@@ -543,6 +562,29 @@ static int cache_defer_req(struct cache_ + cache_revisit_request(item); + return -EAGAIN; + } ++ ++ if (dreq == &sleeper.handle) { ++ wait_event_interruptible_timeout( ++ sleeper.wait, ++ !test_bit(CACHE_PENDING, &item->flags) ++ || list_empty(&sleeper.handle.hash), ++ req->thread_wait); ++ spin_lock(&cache_defer_lock); ++ if (!list_empty(&sleeper.handle.hash)) { ++ list_del_init(&sleeper.handle.recent); ++ list_del_init(&sleeper.handle.hash); ++ cache_defer_cnt--; ++ } ++ spin_unlock(&cache_defer_lock); ++ if (test_bit(CACHE_PENDING, &item->flags)) { ++ /* item is still pending, try request ++ * deferral ++ */ ++ dreq = req->defer(req); ++ goto retry; ++ } ++ return -EAGAIN; ++ } + return 0; + } + +--- a/net/sunrpc/svc_xprt.c ++++ b/net/sunrpc/svc_xprt.c +@@ -650,6 +650,11 @@ int svc_recv(struct svc_rqst *rqstp, lon + if (signalled() || kthread_should_stop()) + return -EINTR; + ++ /* Normally we will wait up to 5 seconds for any required ++ * cache information to be provided. ++ */ ++ rqstp->rq_chandle.thread_wait = 5*HZ; ++ + spin_lock_bh(&pool->sp_lock); + xprt = svc_xprt_dequeue(pool); + if (xprt) { +@@ -657,6 +662,12 @@ int svc_recv(struct svc_rqst *rqstp, lon + svc_xprt_get(xprt); + rqstp->rq_reserved = serv->sv_max_mesg; + atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved); ++ ++ /* As there is a shortage of threads and this request ++ * had to be queue, don't allow the thread to wait so ++ * long for cache updates. ++ */ ++ rqstp->rq_chandle.thread_wait = 1*HZ; + } else { + /* No data pending. Go to sleep */ + svc_thread_enqueue(pool, rqstp); diff --git a/patches.fixes/nfsd-06-sunrpc-cache-retry-cache-lookups-that-return-ETIMEDO.patch b/patches.fixes/nfsd-06-sunrpc-cache-retry-cache-lookups-that-return-ETIMEDO.patch new file mode 100644 index 0000000..907a3a2 --- /dev/null +++ b/patches.fixes/nfsd-06-sunrpc-cache-retry-cache-lookups-that-return-ETIMEDO.patch @@ -0,0 +1,127 @@ +Patch-mainline: submitted 04aug2009 +References: bnc#498708 +From: NeilBrown +Date: Tue, 4 Aug 2009 15:06:38 +1000 +Subject: [PATCH 08/12] sunrpc/cache: retry cache lookups that return -ETIMEDOUT + +If cache_check returns -ETIMEDOUT, then the cache item is not +up-to-date, but there is no pending upcall. +This could mean the data is not available, or it could mean that the +good data has been stored in a new cache item. + +So re-do the lookup and if that returns a new item, proceed using that +item. + +Signed-off-by: NeilBrown + +--- + fs/nfsd/export.c | 18 ++++++++++++++++++ + net/sunrpc/svcauth_unix.c | 23 ++++++++++++++++++++--- + 2 files changed, 38 insertions(+), 3 deletions(-) + +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -787,9 +787,18 @@ exp_find_key(svc_client *clp, int fsid_t + memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); + + ek = svc_expkey_lookup(&key); ++ again: + if (ek == NULL) + return ERR_PTR(-ENOMEM); + err = cache_check(&svc_expkey_cache, &ek->h, reqp); ++ if (err == -ETIMEDOUT) { ++ struct svc_expkey *prev_ek = ek; ++ ek = svc_expkey_lookup(&key); ++ if (ek != prev_ek) ++ goto again; ++ if (ek) ++ cache_put(&ek->h, &svc_expkey_cache); ++ } + if (err) + return ERR_PTR(err); + return ek; +@@ -859,9 +868,18 @@ static svc_export *exp_get_by_name(svc_c + key.ex_path = *path; + + exp = svc_export_lookup(&key); ++ retry: + if (exp == NULL) + return ERR_PTR(-ENOMEM); + err = cache_check(&svc_export_cache, &exp->h, reqp); ++ if (err == -ETIMEDOUT) { ++ struct svc_export *prev_exp = exp; ++ exp = svc_export_lookup(&key); ++ if (exp != prev_exp) ++ goto retry; ++ if (exp) ++ cache_put(&exp->h, &svc_export_cache); ++ } + if (err) + return ERR_PTR(err); + return exp; +--- a/net/sunrpc/svcauth_unix.c ++++ b/net/sunrpc/svcauth_unix.c +@@ -662,13 +662,14 @@ static struct unix_gid *unix_gid_lookup( + + static struct group_info *unix_gid_find(uid_t uid, struct svc_rqst *rqstp) + { +- struct unix_gid *ug; ++ struct unix_gid *ug, *prevug; + struct group_info *gi; + int ret; + + ug = unix_gid_lookup(uid); + if (!ug) + return ERR_PTR(-EAGAIN); ++retry: + ret = cache_check(&unix_gid_cache, &ug->h, &rqstp->rq_chandle); + switch (ret) { + case -ENOENT: +@@ -677,6 +678,13 @@ static struct group_info *unix_gid_find( + gi = get_group_info(ug->gi); + cache_put(&ug->h, &unix_gid_cache); + return gi; ++ case -ETIMEDOUT: ++ prevug = ug; ++ ug = unix_gid_lookup(uid); ++ if (ug != prevug) ++ goto retry; ++ if (ug) ++ cache_put(&ug->h, &unix_gid_cache); + default: + return ERR_PTR(-EAGAIN); + } +@@ -687,7 +695,7 @@ svcauth_unix_set_client(struct svc_rqst + { + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6, sin6_storage; +- struct ip_map *ipm; ++ struct ip_map *ipm, *prev_ipm; + struct group_info *gi; + struct svc_cred *cred = &rqstp->rq_cred; + +@@ -713,14 +721,23 @@ svcauth_unix_set_client(struct svc_rqst + ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, + &sin6->sin6_addr); + ++ retry: + if (ipm == NULL) + return SVC_DENIED; + + switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) { + default: + BUG(); +- case -EAGAIN: + case -ETIMEDOUT: ++ prev_ipm = ipm; ++ ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, ++ &sin6->sin6_addr); ++ if (ipm != prev_ipm) ++ goto retry; ++ if (ipm) ++ cache_put(&ipm->h, &ip_map_cache); ++ ++ case -EAGAIN: + return SVC_DROP; + case -ENOENT: + return SVC_DENIED; diff --git a/patches.fixes/nfsd-07-nfsd-idmap-drop-special-request-deferal-in-favour-of.patch b/patches.fixes/nfsd-07-nfsd-idmap-drop-special-request-deferal-in-favour-of.patch new file mode 100644 index 0000000..d3d1d88 --- /dev/null +++ b/patches.fixes/nfsd-07-nfsd-idmap-drop-special-request-deferal-in-favour-of.patch @@ -0,0 +1,141 @@ +Patch-mainline: submitted 04aug2009 +References: bnc#498708 +From: NeilBrown +Date: Tue, 4 Aug 2009 15:06:39 +1000 +Subject: [PATCH 09/12] nfsd/idmap: drop special request deferal in favour of improved default. + +The idmap code manages request deferal by waiting for a reply from +userspace rather than putting the NFS request on a queue to be retried +from the start. +Now that the comment deferal code does this there is no need for the +special code in idmap. + +Signed-off-by: NeilBrown + +--- + fs/nfsd/nfs4idmap.c | 105 +++++----------------------------------------------- + 1 file changed, 11 insertions(+), 94 deletions(-) + +--- a/fs/nfsd/nfs4idmap.c ++++ b/fs/nfsd/nfs4idmap.c +@@ -481,109 +481,26 @@ nfsd_idmap_shutdown(void) + cache_unregister(&nametoid_cache); + } + +-/* +- * Deferred request handling +- */ +- +-struct idmap_defer_req { +- struct cache_req req; +- struct cache_deferred_req deferred_req; +- wait_queue_head_t waitq; +- atomic_t count; +-}; +- +-static inline void +-put_mdr(struct idmap_defer_req *mdr) +-{ +- if (atomic_dec_and_test(&mdr->count)) +- kfree(mdr); +-} +- +-static inline void +-get_mdr(struct idmap_defer_req *mdr) +-{ +- atomic_inc(&mdr->count); +-} +- +-static void +-idmap_revisit(struct cache_deferred_req *dreq, int toomany) +-{ +- struct idmap_defer_req *mdr = +- container_of(dreq, struct idmap_defer_req, deferred_req); +- +- wake_up(&mdr->waitq); +- put_mdr(mdr); +-} +- +-static struct cache_deferred_req * +-idmap_defer(struct cache_req *req) +-{ +- struct idmap_defer_req *mdr = +- container_of(req, struct idmap_defer_req, req); +- +- mdr->deferred_req.revisit = idmap_revisit; +- get_mdr(mdr); +- return (&mdr->deferred_req); +-} +- +-static inline int +-do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *), struct ent *key, +- struct cache_detail *detail, struct ent **item, +- struct idmap_defer_req *mdr) +-{ +- *item = lookup_fn(key); +- if (!*item) +- return -ENOMEM; +- return cache_check(detail, &(*item)->h, &mdr->req); +-} +- +-static inline int +-do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *), +- struct ent *key, struct cache_detail *detail, +- struct ent **item) +-{ +- int ret = -ENOMEM; +- +- *item = lookup_fn(key); +- if (!*item) +- goto out_err; +- ret = -ETIMEDOUT; +- if (!test_bit(CACHE_VALID, &(*item)->h.flags) +- || (*item)->h.expiry_time < get_seconds() +- || detail->flush_time > (*item)->h.last_refresh) +- goto out_put; +- ret = -ENOENT; +- if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags)) +- goto out_put; +- return 0; +-out_put: +- cache_put(&(*item)->h, detail); +-out_err: +- *item = NULL; +- return ret; +-} +- + static int + idmap_lookup(struct svc_rqst *rqstp, + struct ent *(*lookup_fn)(struct ent *), struct ent *key, + struct cache_detail *detail, struct ent **item) + { +- struct idmap_defer_req *mdr; + int ret; + +- mdr = kzalloc(sizeof(*mdr), GFP_KERNEL); +- if (!mdr) ++ *item = lookup_fn(key); ++ if (!*item) + return -ENOMEM; +- atomic_set(&mdr->count, 1); +- init_waitqueue_head(&mdr->waitq); +- mdr->req.defer = idmap_defer; +- ret = do_idmap_lookup(lookup_fn, key, detail, item, mdr); +- if (ret == -EAGAIN) { +- wait_event_interruptible_timeout(mdr->waitq, +- test_bit(CACHE_VALID, &(*item)->h.flags), 1 * HZ); +- ret = do_idmap_lookup_nowait(lookup_fn, key, detail, item); ++ retry: ++ ret = cache_check(detail, &(*item)->h, &rqstp->rq_chandle); ++ ++ if (ret == -ETIMEDOUT) { ++ struct ent *prev_item = *item; ++ *item = lookup_fn(key); ++ if (*item != prev_item) ++ goto retry; ++ cache_put(&(*item)->h, detail); + } +- put_mdr(mdr); + return ret; + } + diff --git a/patches.fixes/novfs-LFS-initialization b/patches.fixes/novfs-LFS-initialization new file mode 100644 index 0000000..6e108b7 --- /dev/null +++ b/patches.fixes/novfs-LFS-initialization @@ -0,0 +1,26 @@ +From: Sankar P +Subject: fs: novfs: Initialize super-block with standard macros +Patch-mainline: no + +Initialize the super block's maxbytes with MAX_LFS_FILESIZE macro. + +Signed-off-by: Sankar P +Signed-off-by: Samrat Kannikar +Acked-by: Jan Kara + +diff --git a/fs/novfs/inode.c b/fs/novfs/inode.c +index e33a5f8..1c17f7f 100644 +--- a/fs/novfs/inode.c ++++ b/fs/novfs/inode.c +@@ -3826,7 +3826,7 @@ int novfs_fill_super(struct super_block *SB, void *Data, int Silent) + + SB->s_blocksize = PAGE_CACHE_SIZE; + SB->s_blocksize_bits = PAGE_CACHE_SHIFT; +- SB->s_maxbytes = 0xFFFFFFFFFFFFFFFFULL; /* Max file size */ ++ SB->s_maxbytes = MAX_LFS_FILESIZE; /* Max file size */ + SB->s_op = &novfs_ops; + SB->s_flags |= (MS_NODIRATIME | MS_NODEV | MS_POSIXACL); + SB->s_magic = NOVFS_MAGIC; +-- +1.6.4.2 + diff --git a/patches.fixes/novfs-dentry-cache-limit.patch b/patches.fixes/novfs-dentry-cache-limit.patch new file mode 100644 index 0000000..26d49d8 --- /dev/null +++ b/patches.fixes/novfs-dentry-cache-limit.patch @@ -0,0 +1,45 @@ +From: Samrat Kannikar +Subject: novfs: Remove dcache count restricting code +References: bnc#576026 +Patch-mainline: no + +Attached patch removes the code which restricts the +number of dir_cache entries, that are maintained by novfs + +Signed-off-by: Samrat Kannikar +Acked-by: Jan Kara + +diff --git linux-2.6.32-sle11-sp1/fs/novfs/inode.c linux-2.6.32-sle11-sp1/fs/novfs/inode.c +--- linux-2.6.32-sle11-sp1/fs/novfs/inode.c ++++ linux-2.6.32-sle11-sp1/fs/novfs/inode.c +@@ -4346,8 +4346,6 @@ int novfs_add_inode_entry(struct inode *i, + struct inode_data *id; + struct novfs_dir_cache *new; + int retVal = -ENOMEM; +- struct novfs_dir_cache *todel; +- struct list_head *todeltmp; + + //SClark + DbgPrint("i: %p", i); +@@ -4383,19 +4381,6 @@ int novfs_add_inode_entry(struct inode *i, + memcpy(new->name, name->name, name->len); + new->name[new->nameLen] = '\0'; + list_add(&new->list, &id->DirCache); +- +- if (id->cntDC > 20) { +- todeltmp = id->DirCache.prev; +- todel = list_entry(todeltmp, struct novfs_dir_cache, list); +- +- list_del(&todel->list); +- +- kfree(todel); +- +- DCCount--; +- id->cntDC--; +- } +- + } + } + return (retVal); +-- +1.6.4.2 diff --git a/patches.fixes/novfs-err_ptr-fix.diff b/patches.fixes/novfs-err_ptr-fix.diff new file mode 100644 index 0000000..8bdc17b --- /dev/null +++ b/patches.fixes/novfs-err_ptr-fix.diff @@ -0,0 +1,34 @@ +From: Anders Johansson +Subject: Oops in novfs:unlink_local +References: bnc#569071 +Patch-mainline: no + +Signed-off-by: Anders Johansson +Acked-by: Sankar P +--- + fs/novfs/daemon.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/fs/novfs/daemon.c ++++ b/fs/novfs/daemon.c +@@ -2046,14 +2046,14 @@ static long local_unlink(const char *pat + } + dentry = lookup_one_len(name, nd.path.dentry, strlen(name)); + error = PTR_ERR(dentry); +- DbgPrint("dentry %p", dentry); +- if (!(dentry->d_inode->i_mode & S_IFLNK)) { +- DbgPrint("%s not a link", name); +- error=-ENOENT; +- goto exit1; +- } + + if (!IS_ERR(dentry)) { ++ DbgPrint("dentry %p", dentry); ++ if (!(dentry->d_inode->i_mode & S_IFLNK)) { ++ DbgPrint("%s not a link", name); ++ error=-ENOENT; ++ goto exit1; ++ } + /* Why not before? Because we want correct error value */ + if (nd.last.name[nd.last.len]) + goto slashes; diff --git a/patches.fixes/novfs-fix-inode-uid b/patches.fixes/novfs-fix-inode-uid new file mode 100644 index 0000000..440cd22 --- /dev/null +++ b/patches.fixes/novfs-fix-inode-uid @@ -0,0 +1,34 @@ +From: Sankar P +Subject: novfs: Get proper UID when looking up inode +Patch-mainline: no +References: bnc#486997 + +novfs is incorrectly assigning the rights in the /var/opt/novell/nclmnt +directory. This causes nwlogin mappings to fail. + +Patch below is ported for SLED 11 SP1 and it is already submitted for +OES 2 service packs. + +Ported-by: Sankar P +Signed-off-by: Pary D +Acked-by: Jan Kara + +--- + fs/novfs/inode.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/fs/novfs/inode.c ++++ b/fs/novfs/inode.c +@@ -2760,7 +2760,11 @@ struct dentry *novfs_i_lookup(struct ino + } + + if (!inode && ino) { +- uid = novfs_scope_get_uid(id->Scope); ++ if (id && id->Scope) { ++ uid = novfs_scope_get_uid(id->Scope); ++ } else { ++ uid = novfs_scope_get_uid(novfs_get_scope(dentry)); ++ } + if (novfs_lock_inode_cache(dir)) { + inode = novfs_get_inode (dentry->d_sb, info->mode, 0, uid, ino, &name); + if (inode) { diff --git a/patches.fixes/novfs-fix-oops-in-scope-finding b/patches.fixes/novfs-fix-oops-in-scope-finding new file mode 100644 index 0000000..a925ba8 --- /dev/null +++ b/patches.fixes/novfs-fix-oops-in-scope-finding @@ -0,0 +1,36 @@ +From: Sankar P +Subject: novfs: fix an oops in novfs scope-finding code +References: bnc#588579 +Patch-mainline: no + +This patch removes an attempt to dereference a NULL pointer, +on failed memory allocation. The addscope boolean takes care +of the ScopeUserName field's validity. + +Signed-off-by: Sankar P +Acked-by: Jan Kara +--- + fs/novfs/scope.c | 7 +++---- + 1 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/tmp/linux-2.6.32-sle11sp1/fs/novfs/scope.c b/tmp/linux-2.6.32-sle11sp1/fs/novfs/scope.c +index 6de40a8..5b408f6 100644 +--- a/fs/novfs/scope.c ++++ b/fs/novfs/scope.c +@@ -170,10 +170,9 @@ static struct novfs_scope_list *Scope_Find_Scope(int Create) + kfree(scope); + scope = NULL; + } +- } +- +- if (addscope) { +- novfs_add_to_root(scope->ScopeUserName); ++ ++ if (scope && addscope) ++ novfs_add_to_root(scope->ScopeUserName); + } + } + +-- +1.6.4.2 + diff --git a/patches.fixes/novfs-incorrect-filesize-fix b/patches.fixes/novfs-incorrect-filesize-fix new file mode 100644 index 0000000..b548268 --- /dev/null +++ b/patches.fixes/novfs-incorrect-filesize-fix @@ -0,0 +1,39 @@ +From: Sankar P +Subject: novfs: novfs reports incorrect file size +Patch-mainline: no +References: bnc#426536 + +While updating the inode, make sure that the s_blocks member +is updated to the number of 512 blocks units. + +This fixes the issue of novfs reporting invalid file sizes. + +Signed-off-by: Sankar P +Acked-by: Jan Kara + +--- + fs/novfs/inode.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/fs/novfs/inode.c ++++ b/fs/novfs/inode.c +@@ -2671,8 +2671,17 @@ void update_inode(struct inode *Inode, s + Inode->i_mtime = Info->mtime; + + if (Inode->i_size && Inode->i_sb->s_blocksize) { +- Inode->i_blocks = +- (unsigned long) (Info->size >> (loff_t) Inode->i_blkbits); ++ ++ /* ++ * Filling number of blocks as in NSS filesystem. ++ * The s_blocksize is initialized to PAGE_CACHE_SIZE in ++ * the super block initialization. ++ * ++ * Update i_blocks to have the number of 512 blocks ++ */ ++ Inode->i_blocks = (((loff_t)Info->size) + Inode->i_sb->s_blocksize - 1) ++ >> (loff_t)Inode->i_blkbits; ++ Inode->i_blocks = Inode->i_blocks << (PAGE_CACHE_SHIFT - 9); + Inode->i_bytes = Info->size & (Inode->i_sb->s_blocksize - 1); + + DbgPrint("i_sb->s_blocksize=%d", Inode->i_sb->s_blocksize); diff --git a/patches.fixes/novfs-return-ENOTEMPTY-when-deleting-nonempty-dir b/patches.fixes/novfs-return-ENOTEMPTY-when-deleting-nonempty-dir new file mode 100644 index 0000000..08fe8f8 --- /dev/null +++ b/patches.fixes/novfs-return-ENOTEMPTY-when-deleting-nonempty-dir @@ -0,0 +1,42 @@ +From: Sankar P +Subject: fs: novfs: Return ENOTEMPTY when tyring to delete a non-empty folder +References: bnc#583964 +Patch-mainline: no + +The patch returns the ENOTEMPTY error code, when an user issues +delete command on a non-empty folder. This fix makes Nautilus +behave correctly in novfs, just like other file-systems. + +Signed-off-by: Sankar P +Acked-by: Jan Kara +--- + tmp/linux-2.6.32-sle11-sp1/fs/novfs/file.c | 11 ++++++++--- + 1 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/fs/novfs/file.c b/fs/novfs/file.c +index b7033ff..5da32ca 100644 +--- a/fs/novfs/file.c ++++ b/fs/novfs/file.c +@@ -1569,11 +1569,16 @@ int novfs_delete(unsigned char * Path, int DirectoryFlag, struct novfs_schandle + if (reply) { + retCode = 0; + if (reply->Reply.ErrorCode) { +- if ((reply->Reply.ErrorCode & 0xFFFF) == 0x0006) { /* Access Denied Error */ ++ ++ /* Refer to the file ncp.c, in xtier's ++ * NCP89_08 Function for various error codes */ ++ ++ if ((reply->Reply.ErrorCode & 0xFFFF) == 0x0006) + retCode = -EACCES; +- } else { ++ else if ((reply->Reply.ErrorCode & 0xFFFF) == 0x0513) ++ retCode = -ENOTEMPTY; ++ else + retCode = -EIO; +- } + } + kfree(reply); + } +-- +1.6.4.2 + diff --git a/patches.fixes/novfs-truncate-fix b/patches.fixes/novfs-truncate-fix new file mode 100644 index 0000000..97a4fbb --- /dev/null +++ b/patches.fixes/novfs-truncate-fix @@ -0,0 +1,58 @@ +From: Sankar P +Subject: novfs: Fixes corruption of OO documents on NSS Volumes +References: bnc#508259 +Patch-mainline: no + +OpenOffice documents stored in NSS volumes, when accessed +via NOVFS, get corrupted while using the ftruncate64 call. + +Removed stale code which unsets the size in the setattr call. + +Signed-off-by: Sankar P +Acked-by: Jan Kara + +--- + fs/novfs/inode.c | 25 ------------------------- + 1 file changed, 25 deletions(-) + +--- a/fs/novfs/inode.c ++++ b/fs/novfs/inode.c +@@ -3183,7 +3183,6 @@ int novfs_i_setattr(struct dentry *dentr + unsigned int ia_valid = attr->ia_valid; + struct novfs_schandle session; + int retVal = 0; +- struct iattr mattr; + + if (IS_ROOT(dentry) || /* Root */ + IS_ROOT(dentry->d_parent) || /* User */ +@@ -3241,30 +3240,6 @@ int novfs_i_setattr(struct dentry *dentr + attr->ia_size, + atime_buf, mtime_buf, ctime_buf); + +- if ((attr->ia_valid & ATTR_FILE) +- && (attr->ia_valid & ATTR_SIZE)) { +- memcpy(&mattr, attr, sizeof(mattr)); +- mattr.ia_valid &= +- ~(ATTR_FILE | ATTR_SIZE); +- attr = &mattr; +- ia_valid = attr->ia_valid; +-#if 0 // thanks to vfs changes in our tree... +- retVal = +- novfs_trunc_ex(attr-> +- ia_file-> +- private_data, +- attr-> +- ia_size, +- session); +- if (!retVal) { +- inode->i_size = attr->ia_size; +- ((struct inode_data *) inode-> +- i_private)->Flags |= +- UPDATE_INODE; +- } +-#endif +- } +- + if (ia_valid + && !(retVal = + novfs_set_attr(path, attr, session))) { diff --git a/patches.fixes/oom-warning b/patches.fixes/oom-warning new file mode 100644 index 0000000..cd14bcd --- /dev/null +++ b/patches.fixes/oom-warning @@ -0,0 +1,30 @@ +From: Andrea Arcangeli +Subject: Tell the end user they should not worry about GFP_ATOMIC failures +Patch-mainline: no +References: SUSE48965 + +x + +Signed-off-by: Andrea Arcangeli + +--- + mm/page_alloc.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1927,7 +1927,13 @@ rebalance: + + nopage: + if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) { +- printk(KERN_WARNING "%s: page allocation failure." ++ if (!wait) { ++ printk(KERN_INFO "The following is only an harmless informational message.\n"); ++ printk(KERN_INFO "Unless you get a _continuous_flood_ of these messages it means\n"); ++ printk(KERN_INFO "everything is working fine. Allocations from irqs cannot be\n"); ++ printk(KERN_INFO "perfectly reliable and the kernel is designed to handle that.\n"); ++ } ++ printk(KERN_INFO "%s: page allocation failure." + " order:%d, mode:0x%x\n", + p->comm, order, gfp_mask); + dump_stack(); diff --git a/patches.fixes/oprofile_bios_ctr.patch b/patches.fixes/oprofile_bios_ctr.patch new file mode 100644 index 0000000..cea76e0 --- /dev/null +++ b/patches.fixes/oprofile_bios_ctr.patch @@ -0,0 +1,103 @@ +From: Naga Chumbalkar +Subject: detect oprofile counters reserved by bios +References: FATE#307426 +Patch-mainline: pending +Signed-off-by: Tony Jones + +Currently, oprofile fails silently on platforms where a non-OS entity such as +the system firmware "enables" and uses a performance counter. The patch below +suggests a workaround to the user in such instances. + +The patch below has been well tested on AMD and Intel based platforms. It +improves on a previous version via better macro usage, use of rdmsrl and more +accurate/informative error messages. + +Signed-off-by: Naga Chumbalkar +Tested-by: Shashi Belur + +--- +--- + arch/x86/oprofile/nmi_int.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 58 insertions(+) + +--- a/arch/x86/oprofile/nmi_int.c ++++ b/arch/x86/oprofile/nmi_int.c +@@ -27,6 +27,19 @@ + #include "op_counter.h" + #include "op_x86_model.h" + ++static const char RSVD_MSG[] = ++ KERN_INFO "oprofile: performance counter #%d may already be" ++ " reserved." ++ " For counter #%d, EvntSel 0x%lx has value: 0x%llx.\n"; ++static const char PMC_MSG[] = ++ KERN_INFO "oprofile: if oprofile doesn't collect data, then" ++ " try using a different performance counter on your platform" ++ " to monitor the desired event." ++ " Delete counter #%d from the desired event by editing the" ++ " /usr/share/oprofile/%s//events file." ++ " If the event cannot be monitored by any other counter," ++ " contact your hardware or BIOS vendor.\n"; ++ + static struct op_x86_model_spec *model; + static DEFINE_PER_CPU(struct op_msrs, cpu_msrs); + static DEFINE_PER_CPU(unsigned long, saved_lvtpc); +@@ -336,6 +349,50 @@ static struct notifier_block profile_exc + .priority = 2 + }; + ++#define P4_CCCR_ENABLE (1 << 12) ++ ++/* check if the counter/evtsel is already enabled, say, by firmware */ ++static void nmi_is_counter_enabled(struct op_msrs * const msrs) ++{ ++ __u8 vendor = boot_cpu_data.x86_vendor; ++ __u8 family = boot_cpu_data.x86; ++ u64 val; ++ unsigned int i; ++ char *arch = "arch"; ++ ++ /* Fill in at least the "arch" value to help the user */ ++ if (vendor == X86_VENDOR_AMD) { ++ if (family == 6) ++ arch = "i386"; ++ else ++ arch = "x86-64"; ++ } else if (vendor == X86_VENDOR_INTEL) { ++ arch = "i386"; ++ } ++ ++ for (i = 0; i < model->num_controls; ++i) { ++ if (!counter_config[i].enabled) ++ continue; ++ rdmsrl(msrs->controls[i].addr, val); ++ ++ /* P4 is special. Other Intel, and all AMD CPUs ++ ** are consistent in using "bit 22" as "enable" ++ */ ++ if ((vendor == X86_VENDOR_INTEL) && (family == 0xf)) { ++ if (val & P4_CCCR_ENABLE) ++ goto err_rsvd; ++ } else if (val & ARCH_PERFMON_EVENTSEL_ENABLE) { ++ goto err_rsvd; ++ } ++ } ++ return; ++ ++err_rsvd: ++ printk(RSVD_MSG, i, i, msrs->controls[i].addr, val); ++ printk(PMC_MSG, i, arch); ++ return; ++} ++ + static int nmi_setup(void) + { + int err = 0; +@@ -360,6 +417,7 @@ static int nmi_setup(void) + + /* Assume saved/restored counters are the same on all CPUs */ + model->fill_in_addresses(&per_cpu(cpu_msrs, 0)); ++ nmi_is_counter_enabled(&per_cpu(cpu_msrs, 0)); + for_each_possible_cpu(cpu) { + if (!cpu) + continue; diff --git a/patches.fixes/parport-mutex b/patches.fixes/parport-mutex new file mode 100644 index 0000000..5b82478 --- /dev/null +++ b/patches.fixes/parport-mutex @@ -0,0 +1,42 @@ +From: salina@us.ibm.com +Subject: No lp_release_parport while write is going on +References: bnc#62947 - LTC11483 +Patch-mainline: not yet + +This patch was done by IBM a while back, but apparently never made it +into mainline. It fixes a problem in the lp driver that can cause oopses. + +Scenario: + process A: calls lp_write, which in turn calls parport_ieee1284_write_compat, + and that invokes parport_wait_peripheral + process B: meanwhile does an ioctl(LPGETSTATUS), which call lp_release_parport + when done. This function will set physport->cad = NULL. + process A: parport_wait_peripheral tries to dereference physport->cad and + dies + +The patch below simply protects that code with the port_mutex in order to +protect against simultaneous calls to lp_read/lp_write. + +Similar protection is probably required for ioctl(LPRESET). + +Signed-off-by: okir@suse.de + +--- + drivers/char/lp.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/char/lp.c ++++ b/drivers/char/lp.c +@@ -622,9 +622,12 @@ static int lp_do_ioctl(unsigned int mino + return -EFAULT; + break; + case LPGETSTATUS: ++ if (mutex_lock_interruptible(&lp_table[minor].port_mutex)) ++ return -EINTR; + lp_claim_parport_or_block (&lp_table[minor]); + status = r_str(minor); + lp_release_parport (&lp_table[minor]); ++ mutex_unlock(&lp_table[minor].port_mutex); + + if (copy_to_user(argp, &status, sizeof(int))) + return -EFAULT; diff --git a/patches.fixes/powerpc-fix-handling-of-strnlen-with-zero-len b/patches.fixes/powerpc-fix-handling-of-strnlen-with-zero-len new file mode 100644 index 0000000..e045dbe --- /dev/null +++ b/patches.fixes/powerpc-fix-handling-of-strnlen-with-zero-len @@ -0,0 +1,44 @@ +From: Jeff Mahoney +To: linuxppc-dev@ozlabs.org +CC: Steven Rostedt , Christian_Sellars@symantec.com +Subject: [PATCH] powerpc: fix handling of strnlen with zero len +Patch-mainline: submitted 17 Mar 2010 +References: bnc#582681 + + Commit 0119536c, which added the assembly version of strncmp to + powerpc, mentions that it adds two instructions to the version from + boot/string.S to allow it to handle len=0. Unfortunately, it doesn't + always return 0 when that is the case. The length is passed in r5, but + the return value is passed back in r3. In certain cases, this will + happen to work. Otherwise it will pass back the address of the first + string as the return value. + + This patch lifts the len <= 0 handling code from memcpy to handle that + case. + +Reported by: Christian_Sellars@symantec.com +Signed-off-by: Jeff Mahoney +--- + arch/powerpc/lib/string.S | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/arch/powerpc/lib/string.S ++++ b/arch/powerpc/lib/string.S +@@ -71,7 +71,7 @@ _GLOBAL(strcmp) + + _GLOBAL(strncmp) + PPC_LCMPI r5,0 +- beqlr ++ ble- 2f + mtctr r5 + addi r5,r3,-1 + addi r4,r4,-1 +@@ -82,6 +82,8 @@ _GLOBAL(strncmp) + beqlr 1 + bdnzt eq,1b + blr ++2: li r3,0 ++ blr + + _GLOBAL(strlen) + addi r4,r3,-1 diff --git a/patches.fixes/proc-scsi-scsi-fix.diff b/patches.fixes/proc-scsi-scsi-fix.diff new file mode 100644 index 0000000..55b7d37 --- /dev/null +++ b/patches.fixes/proc-scsi-scsi-fix.diff @@ -0,0 +1,110 @@ +From: Jeff Mahoney +Subject: [PATCH] scsi: iterate over devices individually for /proc/scsi/scsi +References: 263731 +Patch-mainline: Probably never, hch wants to kill /proc/scsi/scsi anyway. + + On systems with very large numbers (> 1600 or so) of SCSI devices, + cat /proc/scsi/scsi ends up failing with -ENOMEM. This is due to + the show routine simply iterating over all of the devices with + bus_for_each_dev(), and trying to dump all of them into the buffer + at the same time. On my test system (using scsi_debug with 4064 devices), + the output ends up being ~ 632k, far more than kmalloc will typically allow. + + This patch defines its own seq_file opreations to iterate over the scsi + devices.The result is that each show() operation only dumps ~ 180 bytes + into the buffer at a time so we don't run out of memory. + + If the "Attached devices" header isn't required, we can dump the + sfile->private bit completely. + +Signed-off-by: Jeff Mahoney + +--- + + drivers/scsi/scsi_proc.c | 58 ++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 52 insertions(+), 6 deletions(-) + +--- a/drivers/scsi/scsi_proc.c ++++ b/drivers/scsi/scsi_proc.c +@@ -386,13 +386,59 @@ static ssize_t proc_scsi_write(struct fi + * @s: output goes here + * @p: not used + */ +-static int proc_scsi_show(struct seq_file *s, void *p) ++static int always_match(struct device *dev, void *data) + { +- seq_printf(s, "Attached devices:\n"); +- bus_for_each_dev(&scsi_bus_type, NULL, s, proc_print_scsidevice); +- return 0; ++ return 1; + } + ++static inline struct device *next_scsi_device(struct device *start) ++{ ++ struct device *next = bus_find_device(&scsi_bus_type, start, NULL, ++ always_match); ++ put_device(start); ++ return next; ++} ++ ++static void *scsi_seq_start(struct seq_file *sfile, loff_t *pos) ++{ ++ struct device *dev = NULL; ++ loff_t n = *pos; ++ ++ while ((dev = next_scsi_device(dev))) { ++ if (!n--) ++ break; ++ sfile->private++; ++ } ++ return dev; ++} ++ ++static void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos) ++{ ++ (*pos)++; ++ sfile->private++; ++ return next_scsi_device(v); ++} ++ ++static void scsi_seq_stop(struct seq_file *sfile, void *v) ++{ ++ put_device(v); ++} ++ ++static int scsi_seq_show(struct seq_file *sfile, void *dev) ++{ ++ if (!sfile->private) ++ seq_puts(sfile, "Attached devices:\n"); ++ ++ return proc_print_scsidevice(dev, sfile); ++} ++ ++static struct seq_operations scsi_seq_ops = { ++ .start = scsi_seq_start, ++ .next = scsi_seq_next, ++ .stop = scsi_seq_stop, ++ .show = scsi_seq_show ++}; ++ + /** + * proc_scsi_open - glue function + * @inode: not used +@@ -406,7 +452,7 @@ static int proc_scsi_open(struct inode * + * We don't really need this for the write case but it doesn't + * harm either. + */ +- return single_open(file, proc_scsi_show, NULL); ++ return seq_open(file, &scsi_seq_ops); + } + + static const struct file_operations proc_scsi_operations = { +@@ -415,7 +461,7 @@ static const struct file_operations proc + .read = seq_read, + .write = proc_scsi_write, + .llseek = seq_lseek, +- .release = single_release, ++ .release = seq_release, + }; + + /** diff --git a/patches.fixes/ptrace-getsiginfo b/patches.fixes/ptrace-getsiginfo new file mode 100644 index 0000000..92a953a --- /dev/null +++ b/patches.fixes/ptrace-getsiginfo @@ -0,0 +1,79 @@ +From: Andreas Schwab +Subject: Add compat handler for PTRACE_GETSIGINFO +Patch-mainline: not yet + +Current versions of gdb require a working implementation of +PTRACE_GETSIGINFO for proper watchpoint support. Since struct siginfo +contains pointers it must be converted when passed to a 32-bit debugger. + +Signed-off-by: Andreas Schwab +--- + arch/powerpc/kernel/ppc32.h | 2 ++ + arch/powerpc/kernel/ptrace32.c | 27 +++++++++++++++++++++++++++ + 2 files changed, 29 insertions(+) + +--- a/arch/powerpc/kernel/ppc32.h ++++ b/arch/powerpc/kernel/ppc32.h +@@ -136,4 +136,6 @@ struct ucontext32 { + struct mcontext32 uc_mcontext; + }; + ++extern int copy_siginfo_to_user32(struct compat_siginfo __user *d, siginfo_t *s); ++ + #endif /* _PPC64_PPC32_H */ +--- a/arch/powerpc/kernel/ptrace32.c ++++ b/arch/powerpc/kernel/ptrace32.c +@@ -28,12 +28,15 @@ + #include + #include + #include ++#include + + #include + #include + #include + #include + ++#include "ppc32.h" ++ + /* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. +@@ -69,6 +72,27 @@ static long compat_ptrace_old(struct tas + #define FPRINDEX(i) TS_FPRWIDTH * FPRNUMBER(i) * 2 + FPRHALF(i) + #define FPRINDEX_3264(i) (TS_FPRWIDTH * ((i) - PT_FPR0)) + ++static int compat_ptrace_getsiginfo(struct task_struct *child, compat_siginfo_t __user *data) ++{ ++ siginfo_t lastinfo; ++ int error = -ESRCH; ++ ++ read_lock(&tasklist_lock); ++ if (likely(child->sighand != NULL)) { ++ error = -EINVAL; ++ spin_lock_irq(&child->sighand->siglock); ++ if (likely(child->last_siginfo != NULL)) { ++ lastinfo = *child->last_siginfo; ++ error = 0; ++ } ++ spin_unlock_irq(&child->sighand->siglock); ++ } ++ read_unlock(&tasklist_lock); ++ if (!error) ++ return copy_siginfo_to_user32(data, &lastinfo); ++ return error; ++} ++ + long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) + { +@@ -296,6 +320,9 @@ long compat_arch_ptrace(struct task_stru + 0, PT_REGS_COUNT * sizeof(compat_long_t), + compat_ptr(data)); + ++ case PTRACE_GETSIGINFO: ++ return compat_ptrace_getsiginfo(child, compat_ptr(data)); ++ + case PTRACE_GETFPREGS: + case PTRACE_SETFPREGS: + case PTRACE_GETVRREGS: diff --git a/patches.fixes/reiserfs-remove-2-tb-file-size-limit b/patches.fixes/reiserfs-remove-2-tb-file-size-limit new file mode 100644 index 0000000..84b2232 --- /dev/null +++ b/patches.fixes/reiserfs-remove-2-tb-file-size-limit @@ -0,0 +1,66 @@ +From: Jeff Mahoney +Subject: [PATCH] reiserfs: Remove 2 TB file size limit +References: bnc#592100 +Patch-mainline: Submitted 30 Mar 2010 + + In its early life, reiserfs had an evolving s_max_bytes. It started out + at 4 GB, then was raised to MAX_LFS_FILESIZE, then dropped to 2 TiB when + it was observed that struct stat only had a 32-bit st_blocks field. + + Since then, both the kernel and glibc have evolved as well and now both + support 64-bit st_blocks. Applications that can't deal with these ranges + are assumed to be "legacy" or "broken." File systems now routinely + support file sizes much larger than can be represented by 2^32 * 512. + + But we never revisited that limitation. ReiserFS has always been able to + support larger file sizes (up to 16 TiB, in fact), but the s_max_bytes + limitation has prevented that. + + This patch adds a max_file_offset helper to set s_max_bytes to a more + appropriate value. I noticed that XFS adjusts the limit based on the + CPU but I'd prefer to err on the side of compatibility and place the + limit at the smaller of the 32-bit MAX_LFS_FILESIZE and the maximum + supported by the file system. At a 4k block size, this is conveniently + also the advertised maximum file size of reiserfs. + + This bug is tracked at: https://bugzilla.novell.com/show_bug.cgi?id=592100 + +Signed-off-by: Jeff Mahoney +Acked-by: Jeff Mahoney +--- + fs/reiserfs/super.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +--- a/fs/reiserfs/super.c ++++ b/fs/reiserfs/super.c +@@ -1309,6 +1309,18 @@ out_err: + return err; + } + ++static inline loff_t ++reiserfs_max_file_offset(struct super_block *sb) ++{ ++ /* Limited by stat_data->sd_blocks, 2^32-1 blocks */ ++ loff_t fs_max = ((u64)sb->s_blocksize << 32) - sb->s_blocksize; ++ ++ /* Limited by 32-bit MAX_LFS_FILESIZE */ ++ loff_t page_cache_max = (((u64)PAGE_CACHE_SIZE << 31)-1); ++ ++ return min(fs_max, page_cache_max); ++} ++ + static int read_super_block(struct super_block *s, int offset) + { + struct buffer_head *bh; +@@ -1398,10 +1410,7 @@ static int read_super_block(struct super + s->dq_op = &reiserfs_quota_operations; + #endif + +- /* new format is limited by the 32 bit wide i_blocks field, want to +- ** be one full block below that. +- */ +- s->s_maxbytes = (512LL << 32) - s->s_blocksize; ++ s->s_maxbytes = reiserfs_max_file_offset(s); + return 0; + } + diff --git a/patches.fixes/remount-no-shrink-dcache b/patches.fixes/remount-no-shrink-dcache new file mode 100644 index 0000000..0b7b65f --- /dev/null +++ b/patches.fixes/remount-no-shrink-dcache @@ -0,0 +1,89 @@ +From: Olaf Kirch +Subject: Do not call shrink_dcache_sb when remounting procfs etc +Patch-mainline: Not yet +References: 165672 +Patch-mainline: not yet + +Avoid calls to shrink_dcache_sb when mounting a file system that +uses get_sb_single. shrink_dcache_sb is costly. On large ia64 +systems, this will keep the dcache lock for > 60 seconds at +a stretch. + +Signed-off-by: Olaf Kirch + + fs/super.c | 36 +++++++++++++++++++++++------------- + 1 file changed, 23 insertions(+), 13 deletions(-) + +--- a/fs/super.c ++++ b/fs/super.c +@@ -556,16 +556,10 @@ out: + return err; + } + +-/** +- * do_remount_sb - asks filesystem to change mount options. +- * @sb: superblock in question +- * @flags: numeric part of options +- * @data: the rest of options +- * @force: whether or not to force the change +- * +- * Alters the mount options of a mounted file system. +- */ +-int do_remount_sb(struct super_block *sb, int flags, void *data, int force) ++#define REMOUNT_FORCE 1 ++#define REMOUNT_SHRINK_DCACHE 2 ++ ++static int __do_remount_sb(struct super_block *sb, int flags, void *data, int rflags) + { + int retval; + int remount_rw, remount_ro; +@@ -580,7 +574,8 @@ int do_remount_sb(struct super_block *sb + + if (flags & MS_RDONLY) + acct_auto_close(sb); +- shrink_dcache_sb(sb); ++ if (rflags & REMOUNT_SHRINK_DCACHE) ++ shrink_dcache_sb(sb); + sync_filesystem(sb); + + remount_ro = (flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY); +@@ -589,7 +584,7 @@ int do_remount_sb(struct super_block *sb + /* If we are remounting RDONLY and current sb is read/write, + make sure there are no rw files opened */ + if (remount_ro) { +- if (force) ++ if (rflags & REMOUNT_FORCE) + mark_files_ro(sb); + else if (!fs_may_remount_ro(sb)) + return -EBUSY; +@@ -619,6 +614,21 @@ int do_remount_sb(struct super_block *sb + return 0; + } + ++/** ++ * do_remount_sb - asks filesystem to change mount options. ++ * @sb: superblock in question ++ * @flags: numeric part of options ++ * @data: the rest of options ++ * @force: whether or not to force the change ++ * ++ * Alters the mount options of a mounted file system. ++ */ ++int do_remount_sb(struct super_block *sb, int flags, void *data, int force) ++{ ++ return __do_remount_sb(sb, flags, data, ++ REMOUNT_SHRINK_DCACHE|(force? REMOUNT_FORCE : 0)); ++} ++ + static void do_emergency_remount(struct work_struct *work) + { + struct super_block *sb; +@@ -914,7 +924,7 @@ int get_sb_single(struct file_system_typ + } + s->s_flags |= MS_ACTIVE; + } else { +- do_remount_sb(s, flags, data, 0); ++ __do_remount_sb(s, flags, data, 0); + } + simple_set_mnt(mnt, s); + return 0; diff --git a/patches.fixes/scsi-add-tgps-setting b/patches.fixes/scsi-add-tgps-setting new file mode 100644 index 0000000..dbea9eb --- /dev/null +++ b/patches.fixes/scsi-add-tgps-setting @@ -0,0 +1,319 @@ +Subject: Add TGPS setting to scsi devices +From: Hannes Reinecke +Patch-mainline: Not yet + +Some multipath-capable storage arrays are capable of running +in compatible mode, ie supporting both the original vendor-specific +failover mode and the SPC-3 compliant ALUA mode. +This patch stores the TGPS setting in the sdev so that we can directly +match onto it and select the correct device handler automatically. +And we can save code in the ALUA device handler. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/device_handler/scsi_dh.c | 9 ++- + drivers/scsi/device_handler/scsi_dh_alua.c | 70 +++------------------------- + drivers/scsi/device_handler/scsi_dh_emc.c | 8 +-- + drivers/scsi/device_handler/scsi_dh_hp_sw.c | 10 ++-- + drivers/scsi/device_handler/scsi_dh_rdac.c | 56 +++++++++++----------- + drivers/scsi/scsi_scan.c | 1 + drivers/scsi/scsi_sysfs.c | 2 + include/scsi/scsi_device.h | 4 + + 8 files changed, 59 insertions(+), 101 deletions(-) + +--- a/drivers/scsi/device_handler/scsi_dh.c ++++ b/drivers/scsi/device_handler/scsi_dh.c +@@ -28,6 +28,7 @@ struct scsi_dh_devinfo_list { + struct list_head node; + char vendor[9]; + char model[17]; ++ char tgps; + struct scsi_device_handler *handler; + }; + +@@ -60,7 +61,8 @@ scsi_dh_cache_lookup(struct scsi_device + spin_lock(&list_lock); + list_for_each_entry(tmp, &scsi_dh_dev_list, node) { + if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) && +- !strncmp(sdev->model, tmp->model, strlen(tmp->model))) { ++ !strncmp(sdev->model, tmp->model, strlen(tmp->model)) && ++ (!tmp->tgps || (sdev->tgps & tmp->tgps) != 0)) { + found_dh = tmp->handler; + break; + } +@@ -79,7 +81,9 @@ static int scsi_dh_handler_lookup(struct + if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor, + strlen(scsi_dh->devlist[i].vendor)) && + !strncmp(sdev->model, scsi_dh->devlist[i].model, +- strlen(scsi_dh->devlist[i].model))) { ++ strlen(scsi_dh->devlist[i].model)) && ++ (!scsi_dh->devlist[i].tgps || ++ (sdev->tgps & scsi_dh->devlist[i].tgps) != 0)) { + found = 1; + break; + } +@@ -128,6 +132,7 @@ device_handler_match(struct scsi_device_ + strncpy(tmp->model, sdev->model, 16); + tmp->vendor[8] = '\0'; + tmp->model[16] = '\0'; ++ tmp->tgps = sdev->tgps; + tmp->handler = found_dh; + spin_lock(&list_lock); + list_add(&tmp->node, &scsi_dh_dev_list); +--- a/drivers/scsi/device_handler/scsi_dh_alua.c ++++ b/drivers/scsi/device_handler/scsi_dh_alua.c +@@ -124,43 +124,6 @@ static struct request *get_alua_req(stru + } + + /* +- * submit_std_inquiry - Issue a standard INQUIRY command +- * @sdev: sdev the command should be send to +- */ +-static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) +-{ +- struct request *rq; +- int err = SCSI_DH_RES_TEMP_UNAVAIL; +- +- rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ); +- if (!rq) +- goto done; +- +- /* Prepare the command. */ +- rq->cmd[0] = INQUIRY; +- rq->cmd[1] = 0; +- rq->cmd[2] = 0; +- rq->cmd[4] = ALUA_INQUIRY_SIZE; +- rq->cmd_len = COMMAND_SIZE(INQUIRY); +- +- rq->sense = h->sense; +- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); +- rq->sense_len = h->senselen = 0; +- +- err = blk_execute_rq(rq->q, NULL, rq, 1); +- if (err == -EIO) { +- sdev_printk(KERN_INFO, sdev, +- "%s: std inquiry failed with %x\n", +- ALUA_DH_NAME, rq->errors); +- h->senselen = rq->sense_len; +- err = SCSI_DH_IO; +- } +- blk_put_request(rq); +-done: +- return err; +-} +- +-/* + * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command + * @sdev: sdev the command should be sent to + */ +@@ -332,23 +295,19 @@ static unsigned submit_stpg(struct alua_ + } + + /* +- * alua_std_inquiry - Evaluate standard INQUIRY command ++ * alua_check_tgps - Evaluate TGPS setting + * @sdev: device to be checked + * +- * Just extract the TPGS setting to find out if ALUA ++ * Just examine the TPGS setting of the device to find out if ALUA + * is supported. + */ +-static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) ++static int alua_check_tgps(struct scsi_device *sdev, struct alua_dh_data *h) + { +- int err; +- +- err = submit_std_inquiry(sdev, h); +- +- if (err != SCSI_DH_OK) +- return err; ++ int err = SCSI_DH_OK; + + /* Check TPGS setting */ +- h->tpgs = (h->inq[5] >> 4) & 0x3; ++ h->tpgs = sdev->tgps; ++ + switch (h->tpgs) { + case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT: + sdev_printk(KERN_INFO, sdev, +@@ -610,7 +569,7 @@ static int alua_initialize(struct scsi_d + { + int err; + +- err = alua_std_inquiry(sdev, h); ++ err = alua_check_tgps(sdev, h); + if (err != SCSI_DH_OK) + goto out; + +@@ -684,19 +643,8 @@ static int alua_prep_fn(struct scsi_devi + } + + static const struct scsi_dh_devlist alua_dev_list[] = { +- {"HP", "MSA VOLUME" }, +- {"HP", "HSV101" }, +- {"HP", "HSV111" }, +- {"HP", "HSV200" }, +- {"HP", "HSV210" }, +- {"HP", "HSV300" }, +- {"IBM", "2107900" }, +- {"IBM", "2145" }, +- {"Pillar", "Axiom" }, +- {"Intel", "Multi-Flex"}, +- {"NETAPP", "LUN"}, +- {"AIX", "NVDISK"}, +- {NULL, NULL} ++ {"", "", 3 }, ++ {NULL, NULL, 0} + }; + + static int alua_bus_attach(struct scsi_device *sdev); +--- a/drivers/scsi/device_handler/scsi_dh_emc.c ++++ b/drivers/scsi/device_handler/scsi_dh_emc.c +@@ -623,10 +623,10 @@ done: + } + + static const struct scsi_dh_devlist clariion_dev_list[] = { +- {"DGC", "RAID"}, +- {"DGC", "DISK"}, +- {"DGC", "VRAID"}, +- {NULL, NULL}, ++ {"DGC", "RAID", 0}, ++ {"DGC", "DISK", 0}, ++ {"DGC", "VRAID", 0}, ++ {NULL, NULL, 0}, + }; + + static int clariion_bus_attach(struct scsi_device *sdev); +--- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c ++++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c +@@ -310,11 +310,11 @@ static int hp_sw_activate(struct scsi_de + } + + static const struct scsi_dh_devlist hp_sw_dh_data_list[] = { +- {"COMPAQ", "MSA1000 VOLUME"}, +- {"COMPAQ", "HSV110"}, +- {"HP", "HSV100"}, +- {"DEC", "HSG80"}, +- {NULL, NULL}, ++ {"COMPAQ", "MSA1000 VOLUME", 0}, ++ {"COMPAQ", "HSV110", 0}, ++ {"HP", "HSV100", 0}, ++ {"DEC", "HSG80", 0}, ++ {NULL, NULL, 0}, + }; + + static int hp_sw_bus_attach(struct scsi_device *sdev); +--- a/drivers/scsi/device_handler/scsi_dh_rdac.c ++++ b/drivers/scsi/device_handler/scsi_dh_rdac.c +@@ -744,34 +744,34 @@ static int rdac_check_sense(struct scsi_ + } + + static const struct scsi_dh_devlist rdac_dev_list[] = { +- {"IBM", "1722"}, +- {"IBM", "1724"}, +- {"IBM", "1726"}, +- {"IBM", "1742"}, +- {"IBM", "1745"}, +- {"IBM", "1746"}, +- {"IBM", "1814"}, +- {"IBM", "1815"}, +- {"IBM", "1818"}, +- {"IBM", "3526"}, +- {"SGI", "TP9400"}, +- {"SGI", "TP9500"}, +- {"SGI", "IS"}, +- {"STK", "OPENstorage D280"}, +- {"SUN", "CSM200_R"}, +- {"SUN", "LCSM100_I"}, +- {"SUN", "LCSM100_S"}, +- {"SUN", "LCSM100_E"}, +- {"SUN", "LCSM100_F"}, +- {"DELL", "MD3000"}, +- {"DELL", "MD3000i"}, +- {"DELL", "MD32xx"}, +- {"DELL", "MD32xxi"}, +- {"LSI", "INF-01-00"}, +- {"ENGENIO", "INF-01-00"}, +- {"STK", "FLEXLINE 380"}, +- {"SUN", "CSM100_R_FC"}, +- {NULL, NULL}, ++ {"IBM", "1722", 0}, ++ {"IBM", "1724", 0}, ++ {"IBM", "1726", 0}, ++ {"IBM", "1742", 0}, ++ {"IBM", "1745", 0}, ++ {"IBM", "1746", 0}, ++ {"IBM", "1814", 0}, ++ {"IBM", "1815", 0}, ++ {"IBM", "1818", 0}, ++ {"IBM", "3526", 0}, ++ {"SGI", "TP9400", 0}, ++ {"SGI", "TP9500", 0}, ++ {"SGI", "IS", 0}, ++ {"STK", "OPENstorage D280", 0}, ++ {"SUN", "CSM200_R", 0}, ++ {"SUN", "LCSM100_I", 0}, ++ {"SUN", "LCSM100_S", 0}, ++ {"SUN", "LCSM100_E", 0}, ++ {"SUN", "LCSM100_F", 0}, ++ {"DELL", "MD3000", 0}, ++ {"DELL", "MD3000i", 0}, ++ {"DELL", "MD32xx", 0}, ++ {"DELL", "MD32xxi", 0}, ++ {"LSI", "INF-01-00", 0}, ++ {"ENGENIO", "INF-01-00", 0}, ++ {"STK", "FLEXLINE 380", 0}, ++ {"SUN", "CSM100_R_FC", 0}, ++ {NULL, NULL, 0}, + }; + + static int rdac_bus_attach(struct scsi_device *sdev); +--- a/drivers/scsi/scsi_scan.c ++++ b/drivers/scsi/scsi_scan.c +@@ -837,6 +837,7 @@ static int scsi_add_lun(struct scsi_devi + sdev->inq_periph_qual = (inq_result[0] >> 5) & 7; + sdev->lockable = sdev->removable; + sdev->soft_reset = (inq_result[7] & 1) && ((inq_result[3] & 7) == 2); ++ sdev->tgps = (inq_result[5] >> 4) & 3; + + if (sdev->scsi_level >= SCSI_3 || + (sdev->inquiry_len > 56 && inq_result[56] & 0x04)) +--- a/drivers/scsi/scsi_sysfs.c ++++ b/drivers/scsi/scsi_sysfs.c +@@ -543,6 +543,7 @@ sdev_rd_attr (scsi_level, "%d\n"); + sdev_rd_attr (vendor, "%.8s\n"); + sdev_rd_attr (model, "%.16s\n"); + sdev_rd_attr (rev, "%.4s\n"); ++sdev_rd_attr (tgps, "%d\n"); + + /* + * TODO: can we make these symlinks to the block layer ones? +@@ -728,6 +729,7 @@ static struct attribute *scsi_sdev_attrs + &dev_attr_vendor.attr, + &dev_attr_model.attr, + &dev_attr_rev.attr, ++ &dev_attr_tgps.attr, + &dev_attr_rescan.attr, + &dev_attr_delete.attr, + &dev_attr_state.attr, +--- a/include/scsi/scsi_device.h ++++ b/include/scsi/scsi_device.h +@@ -99,7 +99,8 @@ struct scsi_device { + void *hostdata; /* available to low-level driver */ + char type; + char scsi_level; +- char inq_periph_qual; /* PQ from INQUIRY data */ ++ char inq_periph_qual; /* PQ from INQUIRY data */ ++ char tgps; /* Target port group support */ + unsigned char inquiry_len; /* valid bytes in 'inquiry' */ + unsigned char * inquiry; /* INQUIRY response data */ + const char * vendor; /* [back_compat] point into 'inquiry' ... */ +@@ -176,6 +177,7 @@ struct scsi_device { + struct scsi_dh_devlist { + char *vendor; + char *model; ++ char tgps; + }; + + typedef void (*activate_complete)(void *, int); diff --git a/patches.fixes/scsi-check-host-lookup-failure b/patches.fixes/scsi-check-host-lookup-failure new file mode 100644 index 0000000..9909f43 --- /dev/null +++ b/patches.fixes/scsi-check-host-lookup-failure @@ -0,0 +1,29 @@ +From: Laurie Barry +Subject: Correct scsi_host_lookup return value +References: bnc#456532 +Patch-mainline: not yet + +In the scsi_generic_msg_handler routine it make a call to scsi_host_lookup and +checks the return value for NULL, but the scsi_host_lookup routine can return +an error when it fails instead of NULL. So when the scsi_host_lookup fails the +scsi_generic_msg_handler crashes the kernel with "BUG: unable to handle kernel +NULL pointer dereference at 00000000000000aa" + +Signed-off-by: Laurie Barry +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/scsi_netlink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/scsi/scsi_netlink.c ++++ b/drivers/scsi/scsi_netlink.c +@@ -258,7 +258,7 @@ scsi_generic_msg_handler(struct sk_buff + + /* if successful, scsi_host_lookup takes a shost reference */ + shost = scsi_host_lookup(msg->host_no); +- if (!shost) { ++ if (IS_ERR(shost)) { + err = -ENODEV; + goto driver_exit; + } diff --git a/patches.fixes/scsi-dh-alua-retry-UA b/patches.fixes/scsi-dh-alua-retry-UA new file mode 100644 index 0000000..99b2d14 --- /dev/null +++ b/patches.fixes/scsi-dh-alua-retry-UA @@ -0,0 +1,53 @@ +From: Hannes Reinecke +Subject: Retry ALUA device handler initialization on Unit Attention +Patch-mainline: not yet + +Whenever we receive a UNIT ATTENTION sense code we should just retry +the command. No point in checking the various sense codes here. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/device_handler/scsi_dh_alua.c | 31 +++-------------------------- + 1 file changed, 4 insertions(+), 27 deletions(-) + +--- a/drivers/scsi/device_handler/scsi_dh_alua.c ++++ b/drivers/scsi/device_handler/scsi_dh_alua.c +@@ -495,33 +495,10 @@ static int alua_check_sense(struct scsi_ + return SUCCESS; + break; + case UNIT_ATTENTION: +- if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) +- /* +- * Power On, Reset, or Bus Device Reset, just retry. +- */ +- return ADD_TO_MLQUEUE; +- if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) { +- /* +- * ALUA state changed +- */ +- return ADD_TO_MLQUEUE; +- } +- if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) { +- /* +- * Implicit ALUA state transition failed +- */ +- return ADD_TO_MLQUEUE; +- } +- if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e) { +- /* +- * REPORTED_LUNS_DATA_HAS_CHANGED is reported +- * when switching controllers on targets like +- * Intel Multi-Flex. We can just retry. +- */ +- return ADD_TO_MLQUEUE; +- } +- +- break; ++ /* ++ * Just retry for UNIT_ATTENTION ++ */ ++ return ADD_TO_MLQUEUE; + } + + return SCSI_RETURN_NOT_HANDLED; diff --git a/patches.fixes/scsi-dh-alua-send-stpg b/patches.fixes/scsi-dh-alua-send-stpg new file mode 100644 index 0000000..2a316e3 --- /dev/null +++ b/patches.fixes/scsi-dh-alua-send-stpg @@ -0,0 +1,34 @@ +From: Hannes Reinecke +Subject: Always send STPG for explicit tgps mode +Patch-mainline: not yet + +When we are in explicit tgps mode we should always send an STPG +command to enable the active/optimized mode. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/device_handler/scsi_dh_alua.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +--- a/drivers/scsi/device_handler/scsi_dh_alua.c ++++ b/drivers/scsi/device_handler/scsi_dh_alua.c +@@ -601,13 +601,12 @@ static int alua_activate(struct scsi_dev + struct alua_dh_data *h = get_alua_data(sdev); + int err = SCSI_DH_OK; + +- if (h->group_id != -1) { +- err = alua_rtpg(sdev, h); +- if (err != SCSI_DH_OK) +- goto out; +- } ++ err = alua_rtpg(sdev, h); ++ if (err != SCSI_DH_OK) ++ goto out; + +- if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) { ++ if ((h->tpgs & TPGS_MODE_EXPLICIT) && ++ h->state != TPGS_STATE_OPTIMIZED) { + h->callback_fn = fn; + h->callback_data = data; + err = submit_stpg(h); diff --git a/patches.fixes/scsi-dh-queuedata-accessors b/patches.fixes/scsi-dh-queuedata-accessors new file mode 100644 index 0000000..72324e0 --- /dev/null +++ b/patches.fixes/scsi-dh-queuedata-accessors @@ -0,0 +1,98 @@ +From: Hannes Reinecke +Subject: Kernel bug triggered in multipath +References: bnc#486001 +Patch-mainline: not yet + +Starting multipath on a cciss device will cause a kernel +warning to be triggered. Problem is that we're using the +->queuedata field of the request_queue to derefence the +scsi device; however, for other (non-SCSI) devices this +points to a totally different structure. +So we should rather be using accessors here which make +sure we're only returning valid SCSI device structures. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/device_handler/scsi_dh.c | 10 +++++----- + drivers/scsi/scsi_lib.c | 11 +++++++++++ + include/scsi/scsi_device.h | 1 + + 3 files changed, 17 insertions(+), 5 deletions(-) + +--- a/drivers/scsi/device_handler/scsi_dh.c ++++ b/drivers/scsi/device_handler/scsi_dh.c +@@ -438,7 +438,7 @@ int scsi_dh_activate(struct request_queu + struct scsi_device_handler *scsi_dh = NULL; + + spin_lock_irqsave(q->queue_lock, flags); +- sdev = q->queuedata; ++ sdev = scsi_device_from_queue(q); + if (sdev && sdev->scsi_dh_data) + scsi_dh = sdev->scsi_dh_data->scsi_dh; + if (!scsi_dh || !get_device(&sdev->sdev_gendev)) +@@ -500,7 +500,7 @@ int scsi_dh_handler_exist(const char *na + EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); + + /* +- * scsi_dh_handler_attach - Attach device handler ++ * scsi_dh_attach - Attach device handler + * @sdev - sdev the handler should be attached to + * @name - name of the handler to attach + */ +@@ -516,7 +516,7 @@ int scsi_dh_attach(struct request_queue + return -EINVAL; + + spin_lock_irqsave(q->queue_lock, flags); +- sdev = q->queuedata; ++ sdev = scsi_device_from_queue(q); + if (!sdev || !get_device(&sdev->sdev_gendev)) + err = -ENODEV; + spin_unlock_irqrestore(q->queue_lock, flags); +@@ -530,7 +530,7 @@ int scsi_dh_attach(struct request_queue + EXPORT_SYMBOL_GPL(scsi_dh_attach); + + /* +- * scsi_dh_handler_detach - Detach device handler ++ * scsi_dh_detach - Detach device handler + * @sdev - sdev the handler should be detached from + * + * This function will detach the device handler only +@@ -544,7 +544,7 @@ void scsi_dh_detach(struct request_queue + struct scsi_device_handler *scsi_dh = NULL; + + spin_lock_irqsave(q->queue_lock, flags); +- sdev = q->queuedata; ++ sdev = scsi_device_from_queue(q); + if (!sdev || !get_device(&sdev->sdev_gendev)) + sdev = NULL; + spin_unlock_irqrestore(q->queue_lock, flags); +--- a/drivers/scsi/scsi_lib.c ++++ b/drivers/scsi/scsi_lib.c +@@ -1595,6 +1595,17 @@ static void scsi_request_fn(struct reque + spin_lock_irq(q->queue_lock); + } + ++struct scsi_device *scsi_device_from_queue(struct request_queue *q) ++{ ++ struct scsi_device *sdev = NULL; ++ ++ if (q->request_fn == scsi_request_fn) ++ sdev = q->queuedata; ++ ++ return sdev; ++} ++EXPORT_SYMBOL_GPL(scsi_device_from_queue); ++ + u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost) + { + struct device *host_dev; +--- a/include/scsi/scsi_device.h ++++ b/include/scsi/scsi_device.h +@@ -297,6 +297,7 @@ extern void starget_for_each_device(stru + extern void __starget_for_each_device(struct scsi_target *, void *, + void (*fn)(struct scsi_device *, + void *)); ++extern struct scsi_device *scsi_device_from_queue(struct request_queue *); + + /* only exposed to implement shost_for_each_device */ + extern struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *, diff --git a/patches.fixes/scsi-dh-rdac-add-stk b/patches.fixes/scsi-dh-rdac-add-stk new file mode 100644 index 0000000..304545a --- /dev/null +++ b/patches.fixes/scsi-dh-rdac-add-stk @@ -0,0 +1,25 @@ +From: Goldwyn Rodrigues +Subject: STK arrays missing from rdac devicehandler +References: bnc#503855 +Patch-mainline: not yet + +Some STK arrays are missing from the RDAC device handler, +causing multipath to malfunction. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/device_handler/scsi_dh_rdac.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/scsi/device_handler/scsi_dh_rdac.c ++++ b/drivers/scsi/device_handler/scsi_dh_rdac.c +@@ -758,6 +758,8 @@ static const struct scsi_dh_devlist rdac + {"SGI", "TP9500", 0}, + {"SGI", "IS", 0}, + {"STK", "OPENstorage D280", 0}, ++ {"STK", "FLEXLINE 380", 0}, ++ {"SUN", "STK6580_6780", 0}, + {"SUN", "CSM200_R", 0}, + {"SUN", "LCSM100_I", 0}, + {"SUN", "LCSM100_S", 0}, diff --git a/patches.fixes/scsi-ibmvscsi-module_alias.patch b/patches.fixes/scsi-ibmvscsi-module_alias.patch new file mode 100644 index 0000000..be04f21 --- /dev/null +++ b/patches.fixes/scsi-ibmvscsi-module_alias.patch @@ -0,0 +1,39 @@ +Subject: map scsi proc_name to module name +From: olh@suse.de +References: 459933 - LTC50724 +Patch-mainline: not yet + +--- + drivers/scsi/ibmvscsi/ibmvscsi.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/scsi/ibmvscsi/ibmvscsi.c ++++ b/drivers/scsi/ibmvscsi/ibmvscsi.c +@@ -106,6 +106,9 @@ static struct scsi_transport_template *i + + static struct ibmvscsi_ops *ibmvscsi_ops; + ++#define IBMVSCSI_PROC_NAME "ibmvscsi" ++/* The driver is named ibmvscsic, map ibmvscsi to module name */ ++MODULE_ALIAS(IBMVSCSI_PROC_NAME); + MODULE_DESCRIPTION("IBM Virtual SCSI"); + MODULE_AUTHOR("Dave Boutcher"); + MODULE_LICENSE("GPL"); +@@ -1841,7 +1844,7 @@ static struct device_attribute *ibmvscsi + static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = "IBM POWER Virtual SCSI Adapter " IBMVSCSI_VERSION, +- .proc_name = "ibmvscsi", ++ .proc_name = IBMVSCSI_PROC_NAME, + .queuecommand = ibmvscsi_queuecommand, + .eh_abort_handler = ibmvscsi_eh_abort_handler, + .eh_device_reset_handler = ibmvscsi_eh_device_reset_handler, +@@ -2026,7 +2029,7 @@ static struct vio_driver ibmvscsi_driver + .remove = ibmvscsi_remove, + .get_desired_dma = ibmvscsi_get_desired_dma, + .driver = { +- .name = "ibmvscsi", ++ .name = IBMVSCSI_PROC_NAME, + .owner = THIS_MODULE, + .pm = &ibmvscsi_pm_ops, + } diff --git a/patches.fixes/scsi-ibmvscsi-show-config.patch b/patches.fixes/scsi-ibmvscsi-show-config.patch new file mode 100644 index 0000000..3e047da --- /dev/null +++ b/patches.fixes/scsi-ibmvscsi-show-config.patch @@ -0,0 +1,85 @@ +Subject: /sys/class/scsi_host/hostX/config doesn't show any information +From: Linda Xie +References: 439970 - LTC49349 +Patch-mainline: not yet + +This patch changes the size of the buffer used for transfering config +data to 4K. It was tested against 2.6.19-rc2 tree. + +Signed-off-by: lxie@us.ibm.com +Signed-off-by: Olaf Hering + +--- + drivers/scsi/ibmvscsi/ibmvscsi.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +--- a/drivers/scsi/ibmvscsi/ibmvscsi.c ++++ b/drivers/scsi/ibmvscsi/ibmvscsi.c +@@ -97,10 +97,12 @@ static int max_requests = IBMVSCSI_MAX_R + static int max_events = IBMVSCSI_MAX_REQUESTS_DEFAULT + 2; + static int fast_fail = 1; + static int client_reserve = 1; ++/*host data buffer size*/ ++#define buff_size 4096 + + static struct scsi_transport_template *ibmvscsi_transport_template; + +-#define IBMVSCSI_VERSION "1.5.8" ++#define IBMVSCSI_VERSION "1.5.9" + + static struct ibmvscsi_ops *ibmvscsi_ops; + +@@ -1706,7 +1708,7 @@ static ssize_t show_host_srp_version(str + struct ibmvscsi_host_data *hostdata = shost_priv(shost); + int len; + +- len = snprintf(buf, PAGE_SIZE, "%s\n", ++ len = snprintf(buf, buff_size, "%s\n", + hostdata->madapter_info.srp_version); + return len; + } +@@ -1727,7 +1729,7 @@ static ssize_t show_host_partition_name( + struct ibmvscsi_host_data *hostdata = shost_priv(shost); + int len; + +- len = snprintf(buf, PAGE_SIZE, "%s\n", ++ len = snprintf(buf, buff_size, "%s\n", + hostdata->madapter_info.partition_name); + return len; + } +@@ -1748,7 +1750,7 @@ static ssize_t show_host_partition_numbe + struct ibmvscsi_host_data *hostdata = shost_priv(shost); + int len; + +- len = snprintf(buf, PAGE_SIZE, "%d\n", ++ len = snprintf(buf, buff_size, "%d\n", + hostdata->madapter_info.partition_number); + return len; + } +@@ -1768,7 +1770,7 @@ static ssize_t show_host_mad_version(str + struct ibmvscsi_host_data *hostdata = shost_priv(shost); + int len; + +- len = snprintf(buf, PAGE_SIZE, "%d\n", ++ len = snprintf(buf, buff_size, "%d\n", + hostdata->madapter_info.mad_version); + return len; + } +@@ -1788,7 +1790,7 @@ static ssize_t show_host_os_type(struct + struct ibmvscsi_host_data *hostdata = shost_priv(shost); + int len; + +- len = snprintf(buf, PAGE_SIZE, "%d\n", hostdata->madapter_info.os_type); ++ len = snprintf(buf, buff_size, "%d\n", hostdata->madapter_info.os_type); + return len; + } + +@@ -1807,7 +1809,7 @@ static ssize_t show_host_config(struct d + struct ibmvscsi_host_data *hostdata = shost_priv(shost); + + /* returns null-terminated host config data */ +- if (ibmvscsi_do_host_config(hostdata, buf, PAGE_SIZE) == 0) ++ if (ibmvscsi_do_host_config(hostdata, buf, buff_size) == 0) + return strlen(buf); + else + return 0; diff --git a/patches.fixes/scsi-inquiry-too-short-ratelimit b/patches.fixes/scsi-inquiry-too-short-ratelimit new file mode 100644 index 0000000..17697d8 --- /dev/null +++ b/patches.fixes/scsi-inquiry-too-short-ratelimit @@ -0,0 +1,26 @@ +From: Hannes Reinecke +Subject: INQUIRY result too short (5) message flood +References: bnc#432535 +Patch-mainline: not yet + +During installation with vioserver/vioclient lots of +scsi scan: INQUIRY result too short (5), using 36 +messages are printed. These really should be ratelimited. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/scsi_scan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/scsi/scsi_scan.c ++++ b/drivers/scsi/scsi_scan.c +@@ -695,7 +695,7 @@ static int scsi_probe_lun(struct scsi_de + * and displaying garbage for the Vendor, Product, or Revision + * strings. + */ +- if (sdev->inquiry_len < 36) { ++ if (sdev->inquiry_len < 36 && printk_ratelimit()) { + printk(KERN_INFO "scsi scan: INQUIRY result too short (%d)," + " using 36\n", sdev->inquiry_len); + sdev->inquiry_len = 36; diff --git a/patches.fixes/scsi-retry-alua-transition-in-progress b/patches.fixes/scsi-retry-alua-transition-in-progress new file mode 100644 index 0000000..4f44091 --- /dev/null +++ b/patches.fixes/scsi-retry-alua-transition-in-progress @@ -0,0 +1,33 @@ +From: Rajashekhar M A +Subject: I/O errors for ALUA state transitions +References: bnc#491289 +Patch-mainline: not yet + +When a SLES11 host is configured with a few LUNs and IO is running, +injecting FC faults repeatedly leads to path recovery problems. +The LUNs have 4 paths each and 3 of them come back active after +say an FC fault which makes two of the paths go down, instead of +all 4. This happens after several iterations of continuous FC faults. + +Reason here is that we're returning an I/O error whenever we're +encountering sense code 06/04/0a (LOGICAL UNIT NOT ACCESSIBLE, +ASYMMETRIC ACCESS STATE TRANSITION) instead of retrying. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/scsi_error.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/scsi/scsi_error.c ++++ b/drivers/scsi/scsi_error.c +@@ -371,7 +371,8 @@ static int scsi_check_sense(struct scsi_ + * if the device is in the process of becoming ready, we + * should retry. + */ +- if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01)) ++ if ((sshdr.asc == 0x04) && ++ (sshdr.ascq == 0x01 || sshdr.ascq == 0x0a)) + return NEEDS_RETRY; + /* + * if the device is not started, we need to wake diff --git a/patches.fixes/scsi-scan-blist-update b/patches.fixes/scsi-scan-blist-update new file mode 100644 index 0000000..ea83ac2 --- /dev/null +++ b/patches.fixes/scsi-scan-blist-update @@ -0,0 +1,26 @@ +From: Kurt Garloff +Subject: Add BLIST_REPORTLUN2 to EMC SYMMETRIX +Patch-mainline: not yet +References: bnc#185164, bnc#191648, bnc#505578 + +All EMC SYMMETRIX support REPORT_LUNS, even if configured to report +SCSI-2 for whatever reason. + +Signed-off-by: Kurt Garloff +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/scsi_devinfo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/scsi/scsi_devinfo.c ++++ b/drivers/scsi/scsi_devinfo.c +@@ -159,7 +159,7 @@ static struct { + {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, storage on LUN 0 */ + {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, no storage on LUN 0 */ + {"EMC", "Invista", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, +- {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, ++ {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_REPORTLUN2}, + {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN}, + {"easyRAID", "16P", NULL, BLIST_NOREPORTLUN}, + {"easyRAID", "X6P", NULL, BLIST_NOREPORTLUN}, diff --git a/patches.fixes/sd_liberal_28_sense_invalid.diff b/patches.fixes/sd_liberal_28_sense_invalid.diff new file mode 100644 index 0000000..0a1e1f0 --- /dev/null +++ b/patches.fixes/sd_liberal_28_sense_invalid.diff @@ -0,0 +1,28 @@ +From: Oliver Neukum +Subject: fix medium presence misdetection in usb storage device +References: bnc#362850 +Patch-mainline: not yet + +From reading the SCSI spec it seems that having the valid bit 0 (0x70 +checked in scsi_sense_valid) should does not invalidate the ASC or ASQ. +[See page 37 of spc4r02.pdf]. It should only invalidate the INFORMATION +field. Therefore remove the sense_valid check from the USB quirk. + +Signed-off-by: Brandon Philips + +--- + drivers/scsi/sd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -1334,8 +1334,7 @@ sd_spinup_disk(struct scsi_disk *sdkp) + * Yes, this sense key/ASC combination shouldn't + * occur here. It's characteristic of these devices. + */ +- } else if (sense_valid && +- sshdr.sense_key == UNIT_ATTENTION && ++ } else if (sshdr.sense_key == UNIT_ATTENTION && + sshdr.asc == 0x28) { + if (!spintime) { + spintime_expire = jiffies + 5 * HZ; diff --git a/patches.fixes/seccomp-disable-tsc-option b/patches.fixes/seccomp-disable-tsc-option new file mode 100644 index 0000000..520729a --- /dev/null +++ b/patches.fixes/seccomp-disable-tsc-option @@ -0,0 +1,57 @@ +From: Andrea Arcangeli +Subject: [PATCH seccomp: make tsc disabling optional +Patch-mainline: unknown +References: 191123 + +Make the TSC disable purely paranoid feature optional, so by default seccomp +returns absolutely zerocost. + +Ported from 2.6.19 to 2.6.24-rc7 by Jeff Mahoney. +Addition of x86-64 by Jan Beulich. + +Signed-off-by: Andrea Arcangeli +Acked-by: Jeff Mahoney +--- + arch/x86/Kconfig | 12 ++++++++++++ + arch/x86/kernel/process.c | 2 ++ + 2 files changed, 14 insertions(+) + +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -1485,6 +1485,18 @@ config SECCOMP + + If unsure, say Y. Only embedded should say N here. + ++config SECCOMP_DISABLE_TSC ++ bool "Disable the TSC for seccomp tasks" ++ depends on SECCOMP ++ default n ++ help ++ This feature mathematically prevents covert channels ++ for tasks running under SECCOMP. This can generate ++ a minuscule overhead in the scheduler. ++ ++ If you care most about performance say N. Say Y only if you're ++ paranoid about covert channels. ++ + config CC_STACKPROTECTOR + bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)" + ---help--- +--- a/arch/x86/kernel/process.c ++++ b/arch/x86/kernel/process.c +@@ -139,6 +139,7 @@ static void hard_disable_TSC(void) + + void disable_TSC(void) + { ++#ifdef CONFIG_SECCOMP_DISABLE_TSC + preempt_disable(); + if (!test_and_set_thread_flag(TIF_NOTSC)) + /* +@@ -147,6 +148,7 @@ void disable_TSC(void) + */ + hard_disable_TSC(); + preempt_enable(); ++#endif + } + + static void hard_enable_TSC(void) diff --git a/patches.fixes/sunrpc-monotonic-expiry b/patches.fixes/sunrpc-monotonic-expiry new file mode 100644 index 0000000..6a19ca6 --- /dev/null +++ b/patches.fixes/sunrpc-monotonic-expiry @@ -0,0 +1,250 @@ +From: NeilBrown +Date: Wed, 17 Feb 2010 16:53:34 +1100 +Subject: [PATCH] sunrpc: use monotonic time in expiry cache +Patch-mainline: Submitted for 2.6.34 +References: bnc#578668 + +this protects us from confusion when the wallclock time changes. + +We convert to and from wallclock when setting or reading expiry +times. + +Signed-off-by: NeilBrown +Acked-by: NeilBrown + +--- + fs/nfs/dns_resolve.c | 6 +++--- + fs/nfsd/export.c | 9 +++------ + include/linux/sunrpc/cache.h | 23 ++++++++++++++++++++++- + net/sunrpc/cache.c | 35 +++++++++++++++++++---------------- + 4 files changed, 47 insertions(+), 26 deletions(-) + +--- a/fs/nfs/dns_resolve.c ++++ b/fs/nfs/dns_resolve.c +@@ -143,7 +143,7 @@ static int nfs_dns_show(struct seq_file + return 0; + } + item = container_of(h, struct nfs_dns_ent, h); +- ttl = (long)item->h.expiry_time - (long)get_seconds(); ++ ttl = (long)item->h.expiry_time - (long)monotonic_seconds(); + if (ttl < 0) + ttl = 0; + +@@ -215,7 +215,7 @@ static int nfs_dns_parse(struct cache_de + ttl = get_expiry(&buf); + if (ttl == 0) + goto out; +- key.h.expiry_time = ttl + get_seconds(); ++ key.h.expiry_time = ttl + monotonic_seconds(); + + ret = -ENOMEM; + item = nfs_dns_lookup(cd, &key); +@@ -277,7 +277,7 @@ static int do_cache_lookup_nowait(struct + goto out_err; + ret = -ETIMEDOUT; + if (!test_bit(CACHE_VALID, &(*item)->h.flags) +- || (*item)->h.expiry_time < get_seconds() ++ || (*item)->h.expiry_time < monotonic_seconds() + || cd->flush_time > (*item)->h.last_refresh) + goto out_put; + ret = -ENOENT; +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -946,10 +946,9 @@ static void exp_fsid_unhash(struct svc_e + + ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); + if (!IS_ERR(ek)) { +- ek->h.expiry_time = get_seconds()-1; ++ sunrpc_invalidate(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); + } +- svc_expkey_cache.nextcheck = get_seconds(); + } + + static int exp_fsid_hash(svc_client *clp, struct svc_export *exp) +@@ -984,10 +983,9 @@ static void exp_unhash(struct svc_export + + ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); + if (!IS_ERR(ek)) { +- ek->h.expiry_time = get_seconds()-1; ++ sunrpc_invalidate(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); + } +- svc_expkey_cache.nextcheck = get_seconds(); + } + + /* +@@ -1108,8 +1106,7 @@ out: + static void + exp_do_unexport(svc_export *unexp) + { +- unexp->h.expiry_time = get_seconds()-1; +- svc_export_cache.nextcheck = get_seconds(); ++ sunrpc_invalidate(&unexp->h, &svc_export_cache); + exp_unhash(unexp); + exp_fsid_unhash(unexp); + } +--- a/include/linux/sunrpc/cache.h ++++ b/include/linux/sunrpc/cache.h +@@ -220,14 +220,35 @@ static inline int get_int(char **bpp, in + return 0; + } + ++/* ++ * timestamps kept in the cache are expressed in seconds ++ * since boot. This is the best for measuring differences in ++ * real time. ++ */ ++static inline unsigned long monotonic_seconds(void) ++{ ++ struct timespec boot; ++ getboottime(&boot); ++ return get_seconds() - boot.tv_sec; ++} ++ + static inline time_t get_expiry(char **bpp) + { + int rv; ++ struct timespec boot; ++ + if (get_int(bpp, &rv)) + return 0; + if (rv < 0) + return 0; +- return rv; ++ getboottime(&boot); ++ return rv - boot.tv_sec; + } + ++static inline void sunrpc_invalidate(struct cache_head *h, ++ struct cache_detail *detail) ++{ ++ h->expiry_time = monotonic_seconds() - 1; ++ detail->nextcheck = monotonic_seconds(); ++} + #endif /* _LINUX_SUNRPC_CACHE_H_ */ +--- a/net/sunrpc/cache.c ++++ b/net/sunrpc/cache.c +@@ -41,7 +41,7 @@ static void cache_revisit_request(struct + + static void cache_init(struct cache_head *h) + { +- time_t now = get_seconds(); ++ time_t now = monotonic_seconds(); + h->next = NULL; + h->flags = 0; + kref_init(&h->ref); +@@ -108,7 +108,7 @@ static void cache_dequeue(struct cache_d + static void cache_fresh_locked(struct cache_head *head, time_t expiry) + { + head->expiry_time = expiry; +- head->last_refresh = get_seconds(); ++ head->last_refresh = monotonic_seconds(); + set_bit(CACHE_VALID, &head->flags); + } + +@@ -184,7 +184,7 @@ static int cache_make_upcall(struct cach + static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h) + { + if (!test_bit(CACHE_VALID, &h->flags) || +- h->expiry_time < get_seconds()) ++ h->expiry_time < monotonic_seconds()) + return -EAGAIN; + else if (detail->flush_time > h->last_refresh) + return -EAGAIN; +@@ -222,7 +222,7 @@ int cache_check(struct cache_detail *det + + /* now see if we want to start an upcall */ + refresh_age = (h->expiry_time - h->last_refresh); +- age = get_seconds() - h->last_refresh; ++ age = monotonic_seconds() - h->last_refresh; + + if (rqstp == NULL) { + if (rv == -EAGAIN) +@@ -237,7 +237,7 @@ int cache_check(struct cache_detail *det + cache_revisit_request(h); + if (rv == -EAGAIN) { + set_bit(CACHE_NEGATIVE, &h->flags); +- cache_fresh_locked(h, get_seconds()+CACHE_NEW_EXPIRY); ++ cache_fresh_locked(h, monotonic_seconds()+CACHE_NEW_EXPIRY); + cache_fresh_unlocked(h, detail); + rv = -ENOENT; + } +@@ -372,11 +372,11 @@ static int cache_clean(void) + return -1; + } + current_detail = list_entry(next, struct cache_detail, others); +- if (current_detail->nextcheck > get_seconds()) ++ if (current_detail->nextcheck > monotonic_seconds()) + current_index = current_detail->hash_size; + else { + current_index = 0; +- current_detail->nextcheck = get_seconds()+30*60; ++ current_detail->nextcheck = monotonic_seconds()+30*60; + } + } + +@@ -401,7 +401,7 @@ static int cache_clean(void) + for (; ch; cp= & ch->next, ch= *cp) { + if (current_detail->nextcheck > ch->expiry_time) + current_detail->nextcheck = ch->expiry_time+1; +- if (ch->expiry_time >= get_seconds() && ++ if (ch->expiry_time >= monotonic_seconds() && + ch->last_refresh >= current_detail->flush_time) + continue; + if (test_and_clear_bit(CACHE_PENDING, &ch->flags)) +@@ -465,7 +465,7 @@ EXPORT_SYMBOL_GPL(cache_flush); + void cache_purge(struct cache_detail *detail) + { + detail->flush_time = LONG_MAX; +- detail->nextcheck = get_seconds(); ++ detail->nextcheck = monotonic_seconds(); + cache_flush(); + detail->flush_time = 1; + } +@@ -1249,7 +1249,8 @@ static int c_show(struct seq_file *m, vo + + ifdebug(CACHE) + seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n", +- cp->expiry_time, atomic_read(&cp->ref.refcount), cp->flags); ++ cp->expiry_time - monotonic_seconds() + get_seconds(), ++ atomic_read(&cp->ref.refcount), cp->flags); + cache_get(cp); + if (cache_check(cd, cp, NULL)) + /* cache_check does a cache_put on failure */ +@@ -1313,7 +1314,8 @@ static ssize_t read_flush(struct file *f + unsigned long p = *ppos; + size_t len; + +- sprintf(tbuf, "%lu\n", cd->flush_time); ++ sprintf(tbuf, "%lu\n", (cd->flush_time - monotonic_seconds() ++ + get_seconds())); + len = strlen(tbuf); + if (p >= len) + return 0; +@@ -1331,19 +1333,20 @@ static ssize_t write_flush(struct file * + struct cache_detail *cd) + { + char tbuf[20]; +- char *ep; +- long flushtime; ++ char *bp, *ep; ++ + if (*ppos || count > sizeof(tbuf)-1) + return -EINVAL; + if (copy_from_user(tbuf, buf, count)) + return -EFAULT; + tbuf[count] = 0; +- flushtime = simple_strtoul(tbuf, &ep, 0); ++ simple_strtoul(tbuf, &ep, 0); + if (*ep && *ep != '\n') + return -EINVAL; + +- cd->flush_time = flushtime; +- cd->nextcheck = get_seconds(); ++ bp = tbuf; ++ cd->flush_time = get_expiry(&bp); ++ cd->nextcheck = monotonic_seconds(); + cache_flush(); + + *ppos += count; diff --git a/patches.fixes/taskstats-alignment b/patches.fixes/taskstats-alignment new file mode 100644 index 0000000..8abb7a9 --- /dev/null +++ b/patches.fixes/taskstats-alignment @@ -0,0 +1,62 @@ +From: Jeff Mahoney +Subject: [PATCH] delayacct: align to 8 byte boundary on 64-bit systems +References: bnc#578065 +Patch-mainline: Hopefully 2.6.34, submitting when the window opens + + prepare_reply sets up an skb for the response. If I understand it correctly, + the payload contains: + + +--------------------------------+ + | genlmsghdr - 4 bytes | + +--------------------------------+ + | NLA header - 4 bytes | /* Aggregate header */ + +-+------------------------------+ + | | NLA header - 4 bytes | /* PID header */ + | +------------------------------+ + | | pid/tgid - 4 bytes | + | +------------------------------+ + | | NLA header - 4 bytes | /* stats header */ + | + -----------------------------+ <- oops. aligned on 4 byte boundary + | | struct taskstats - 328 bytes | + +-+------------------------------+ + + The start of the taskstats struct must be 8 byte aligned on IA64 (and other + systems with 8 byte alignment rules for 64-bit types) or runtime alignment + warnings will be issued. + + This patch pads the pid/tgid field out to sizeof(long), which forces + the alignment of taskstats. The getdelays userspace code is ok with this + since it assumes 32-bit pid/tgid and then honors that header's length field. + + An array is used to avoid exposing kernel memory contents to userspace in the + response. + +Signed-off-by: Jeff Mahoney +--- + kernel/taskstats.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/kernel/taskstats.c ++++ b/kernel/taskstats.c +@@ -359,6 +359,12 @@ static struct taskstats *mk_reply(struct + struct nlattr *na, *ret; + int aggr; + ++ /* If we don't pad, we end up with alignment on a 4 byte boundary. ++ * This causes lots of runtime warnings on systems requiring 8 byte ++ * alignment */ ++ u32 pids[2] = { pid, 0 }; ++ int pid_size = ALIGN(sizeof(pid), sizeof(long)); ++ + aggr = (type == TASKSTATS_TYPE_PID) + ? TASKSTATS_TYPE_AGGR_PID + : TASKSTATS_TYPE_AGGR_TGID; +@@ -366,7 +372,7 @@ static struct taskstats *mk_reply(struct + na = nla_nest_start(skb, aggr); + if (!na) + goto err; +- if (nla_put(skb, type, sizeof(pid), &pid) < 0) ++ if (nla_put(skb, type, pid_size, pids) < 0) + goto err; + ret = nla_reserve(skb, TASKSTATS_TYPE_STATS, sizeof(struct taskstats)); + if (!ret) diff --git a/patches.fixes/tehuti-firmware-name b/patches.fixes/tehuti-firmware-name new file mode 100644 index 0000000..d601bd3 --- /dev/null +++ b/patches.fixes/tehuti-firmware-name @@ -0,0 +1,23 @@ +From: Hannes Reinecke +Subject: Tehuti network driver references wrong firmware +References: bnc#562092 +Patch-mainline: not yet + +The tehuti network driver references the firmware +'tehuti/firmware.bin', which is actually named +'tehuti/bdx.bin'. + +Signed-off-by: Hannes Reinecke + +--- + drivers/net/tehuti.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/tehuti.c ++++ b/drivers/net/tehuti.c +@@ -2518,4 +2518,4 @@ module_exit(bdx_module_exit); + MODULE_LICENSE("GPL"); + MODULE_AUTHOR(DRIVER_AUTHOR); + MODULE_DESCRIPTION(BDX_DRV_DESC); +-MODULE_FIRMWARE("tehuti/firmware.bin"); ++MODULE_FIRMWARE("tehuti/bdx.bin"); diff --git a/patches.fixes/tg3-fix-default-wol.patch b/patches.fixes/tg3-fix-default-wol.patch new file mode 100644 index 0000000..38b5b3d --- /dev/null +++ b/patches.fixes/tg3-fix-default-wol.patch @@ -0,0 +1,43 @@ +From: Rafael J. Wysocki +Subject: net (tg3): Fix failure to enable WoL by default when possible +References: bnc#447371 +Patch-mainline: not yet + +tg3 is supposed to enable WoL by default on adapters which support +that, but it fails to do so unless the adapter's +/sys/devices/.../power/wakeup file contains 'enabled' during the +initialization of the adapter. Fix that by making tg3 update the +device's 'should_wakeup' bit automatically whenever WoL should be +enabled by default. + +Signed-off-by: Rafael J. Wysocki +--- + drivers/net/tg3.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/net/tg3.c ++++ b/drivers/net/tg3.c +@@ -12194,8 +12194,10 @@ static void __devinit tg3_get_eeprom_hw_ + if (val & VCPU_CFGSHDW_ASPM_DBNC) + tp->tg3_flags |= TG3_FLAG_ASPM_WORKAROUND; + if ((val & VCPU_CFGSHDW_WOL_ENABLE) && +- (val & VCPU_CFGSHDW_WOL_MAGPKT)) ++ (val & VCPU_CFGSHDW_WOL_MAGPKT)) { + tp->tg3_flags |= TG3_FLAG_WOL_ENABLE; ++ device_set_wakeup_enable(&tp->pdev->dev, true); ++ } + goto done; + } + +@@ -12329,8 +12331,10 @@ static void __devinit tg3_get_eeprom_hw_ + tp->tg3_flags &= ~TG3_FLAG_WOL_CAP; + + if ((tp->tg3_flags & TG3_FLAG_WOL_CAP) && +- (nic_cfg & NIC_SRAM_DATA_CFG_WOL_ENABLE)) ++ (nic_cfg & NIC_SRAM_DATA_CFG_WOL_ENABLE)) { + tp->tg3_flags |= TG3_FLAG_WOL_ENABLE; ++ device_set_wakeup_enable(&tp->pdev->dev, true); ++ } + + if (cfg2 & (1 << 17)) + tp->tg3_flags2 |= TG3_FLG2_CAPACITIVE_COUPLING; diff --git a/patches.fixes/tiocgdev b/patches.fixes/tiocgdev new file mode 100644 index 0000000..4ac0d7e --- /dev/null +++ b/patches.fixes/tiocgdev @@ -0,0 +1,154 @@ +Subject: tiocgdev ioctl +Patch-mainline: never, lkml guys don't like it +From: kraxel@suse.de + +add tty ioctl to figure physical device of the console. + + arch/alpha/include/asm/ioctls.h | 1 + + arch/arm/include/asm/ioctls.h | 1 + + arch/ia64/include/asm/ioctls.h | 1 + + arch/m68k/include/asm/ioctls.h | 1 + + arch/mips/include/asm/ioctls.h | 1 + + arch/powerpc/include/asm/ioctls.h | 1 + + arch/s390/include/asm/ioctls.h | 1 + + arch/sh/include/asm/ioctls.h | 1 + + arch/sparc/include/asm/ioctls.h | 1 + + drivers/char/tty_io.c | 15 +++++++++++++++ + fs/compat_ioctl.c | 1 + + include/asm-generic/ioctls.h | 1 + + 12 files changed, 26 insertions(+) + +--- a/arch/alpha/include/asm/ioctls.h ++++ b/arch/alpha/include/asm/ioctls.h +@@ -91,6 +91,7 @@ + #define TIOCGSID 0x5429 /* Return the session ID of FD */ + #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + + #define TIOCSERCONFIG 0x5453 + #define TIOCSERGWILD 0x5454 +--- a/arch/arm/include/asm/ioctls.h ++++ b/arch/arm/include/asm/ioctls.h +@@ -52,6 +52,7 @@ + #define TCSETSF2 _IOW('T',0x2D, struct termios2) + #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + + #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ + #define FIOCLEX 0x5451 +--- a/arch/ia64/include/asm/ioctls.h ++++ b/arch/ia64/include/asm/ioctls.h +@@ -59,6 +59,7 @@ + #define TCSETSF2 _IOW('T',0x2D, struct termios2) + #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + + #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ + #define FIOCLEX 0x5451 +--- a/arch/m68k/include/asm/ioctls.h ++++ b/arch/m68k/include/asm/ioctls.h +@@ -52,6 +52,7 @@ + #define TCSETSF2 _IOW('T',0x2D, struct termios2) + #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + + #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ + #define FIOCLEX 0x5451 +--- a/arch/mips/include/asm/ioctls.h ++++ b/arch/mips/include/asm/ioctls.h +@@ -83,6 +83,7 @@ + #define TCSETSF2 _IOW('T', 0x2D, struct termios2) + #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get real dev no below /dev/console */ + + /* I hope the range from 0x5480 on is free ... */ + #define TIOCSCTTY 0x5480 /* become controlling tty */ +--- a/arch/powerpc/include/asm/ioctls.h ++++ b/arch/powerpc/include/asm/ioctls.h +@@ -93,6 +93,7 @@ + #define TIOCSRS485 0x542f + #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + + #define TIOCSERCONFIG 0x5453 + #define TIOCSERGWILD 0x5454 +--- a/arch/s390/include/asm/ioctls.h ++++ b/arch/s390/include/asm/ioctls.h +@@ -60,6 +60,7 @@ + #define TCSETSF2 _IOW('T',0x2D, struct termios2) + #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + + #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ + #define FIOCLEX 0x5451 +--- a/arch/sh/include/asm/ioctls.h ++++ b/arch/sh/include/asm/ioctls.h +@@ -84,6 +84,7 @@ + #define TCSETSF2 _IOW('T', 45, struct termios2) + #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + + #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */ + #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ +--- a/arch/sparc/include/asm/ioctls.h ++++ b/arch/sparc/include/asm/ioctls.h +@@ -19,6 +19,7 @@ + #define TCSETS2 _IOW('T', 13, struct termios2) + #define TCSETSW2 _IOW('T', 14, struct termios2) + #define TCSETSF2 _IOW('T', 15, struct termios2) ++#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + + /* Note that all the ioctls that are not available in Linux have a + * double underscore on the front to: a) avoid some programs to +--- a/drivers/char/tty_io.c ++++ b/drivers/char/tty_io.c +@@ -2546,6 +2546,21 @@ long tty_ioctl(struct file *file, unsign + case TIOCSETD: + return tiocsetd(tty, p); + /* ++ * Without the real device to which /dev/console is connected, ++ * blogd can not work. ++ * blogd spawns a pty/tty pair, ++ * set /dev/console to the tty of that pair (ioctl TIOCCONS), ++ * then reads in all input from the current /dev/console, ++ * buffer or write the readed data to /var/log/boot.msg ++ * _and_ to the original real device. ++ */ ++ case TIOCGDEV: ++ { ++ unsigned int ret = new_encode_dev(tty_devnum(real_tty)); ++ return put_user(ret, (unsigned int __user *)p); ++ } ++ ++ /* + * Break handling + */ + case TIOCSBRK: /* Turn break on, unconditionally */ +--- a/fs/compat_ioctl.c ++++ b/fs/compat_ioctl.c +@@ -941,6 +941,7 @@ COMPATIBLE_IOCTL(TCSETSW) + COMPATIBLE_IOCTL(TCSETSF) + COMPATIBLE_IOCTL(TIOCLINUX) + COMPATIBLE_IOCTL(TIOCSBRK) ++COMPATIBLE_IOCTL(TIOCGDEV) + COMPATIBLE_IOCTL(TIOCCBRK) + COMPATIBLE_IOCTL(TIOCGSID) + COMPATIBLE_IOCTL(TIOCGICOUNT) +--- a/include/asm-generic/ioctls.h ++++ b/include/asm-generic/ioctls.h +@@ -65,6 +65,7 @@ + #define TIOCSRS485 0x542F + #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ + #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ ++#define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get real dev no below /dev/console */ + #define TCGETX 0x5432 /* SYS5 TCGETX compatibility */ + #define TCSETX 0x5433 + #define TCSETXF 0x5434 diff --git a/patches.fixes/tulip-quad-NIC-ifdown b/patches.fixes/tulip-quad-NIC-ifdown new file mode 100644 index 0000000..951031c --- /dev/null +++ b/patches.fixes/tulip-quad-NIC-ifdown @@ -0,0 +1,27 @@ +Subject: MCA when shutting down tulip quad-NIC +From: andrew.patterson@hp.com +References: SUSE39204 +Patch-mainline: not yet + +Shutting down the network causes an MCA because of an IO TLB error when +a DEC quad 10/100 card is in any slot. This problem was originally seen +on an HP rx4640. + +Acked-by: Olaf Kirch + + drivers/net/tulip/tulip_core.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/tulip/tulip_core.c ++++ b/drivers/net/tulip/tulip_core.c +@@ -1827,6 +1827,10 @@ static void __devexit tulip_remove_one ( + return; + + tp = netdev_priv(dev); ++ ++ /* shoot NIC in the head before deallocating descriptors */ ++ pci_disable_device(tp->pdev); ++ + unregister_netdev(dev); + pci_free_consistent (pdev, + sizeof (struct tulip_rx_desc) * RX_RING_SIZE + diff --git a/patches.fixes/twl6030-fix-note_interrupt-call b/patches.fixes/twl6030-fix-note_interrupt-call new file mode 100644 index 0000000..7f3e071 --- /dev/null +++ b/patches.fixes/twl6030-fix-note_interrupt-call @@ -0,0 +1,22 @@ +From: Jeff Mahoney +Subject: twl6030: Fix note_interrupt call +Patch-mainline: not yet + + note_interrupt takes 4 arguments, not 3. + +Signed-off-by: Jeff Mahoney +--- + drivers/mfd/twl6030-irq.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mfd/twl6030-irq.c ++++ b/drivers/mfd/twl6030-irq.c +@@ -143,7 +143,7 @@ static int twl6030_irq_thread(void *data + */ + if (d->status & IRQ_DISABLED) + note_interrupt(module_irq, d, +- IRQ_NONE); ++ IRQ_NONE, false); + else + d->handle_irq(module_irq, d); + diff --git a/patches.fixes/xfs-dmapi-fixes b/patches.fixes/xfs-dmapi-fixes new file mode 100644 index 0000000..f225757 --- /dev/null +++ b/patches.fixes/xfs-dmapi-fixes @@ -0,0 +1,38 @@ +From: Bill O'Donnel +Subject: xfs/dmapi: fix crash on mount +References: bnc#458027 +Patch-mainline: not yet, depends on dmapi + + This patch resolves a crash on mount problem with dmapi, relating to an + errant search and replace. + +Acked-by: Jeff Mahoney +--- + + fs/dmapi/dmapi_private.h | 4 ++-- + fs/xfs/xfs_dmops.c | 1 - + 2 files changed, 2 insertions(+), 3 deletions(-) + +--- a/fs/dmapi/dmapi_private.h ++++ b/fs/dmapi/dmapi_private.h +@@ -37,8 +37,8 @@ + #include "sv.h" + + #ifdef CONFIG_PROC_FS +-#define DMAPI_PROCFS "orig/fs/dmapi_v2" /* DMAPI device in /proc. */ +-#define DMAPI_DBG_PROCFS "orig/fs/dmapi_d" /* DMAPI debugging dir */ ++#define DMAPI_PROCFS "fs/dmapi_v2" /* DMAPI device in /proc. */ ++#define DMAPI_DBG_PROCFS "fs/dmapi_d" /* DMAPI debugging dir */ + #endif + + extern struct kmem_cache *dm_fsreg_cachep; +--- a/fs/xfs/xfs_dmops.c ++++ b/fs/xfs/xfs_dmops.c +@@ -57,7 +57,6 @@ xfs_dmops_get(struct xfs_mount *mp) + mp->m_dm_ops = &xfs_dmcore_stub; + } + +- mp->m_dm_ops = &xfs_dmcore_stub; + return 0; + } + diff --git a/patches.fixes/xfs-export-debug b/patches.fixes/xfs-export-debug new file mode 100644 index 0000000..239562c --- /dev/null +++ b/patches.fixes/xfs-export-debug @@ -0,0 +1,23 @@ +From: Jeff Mahoney +Subject: [PATCH] xfs: export assertion handler +Patch-mainline: not yet, depends on dmapi + + xfs dmapi support uses the xfs assertion infrastructure if debugging is + enabled. This patch exports the assfail function. + +Signed-off-by: Jeff Mahoney + +--- + fs/xfs/support/debug.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/xfs/support/debug.c ++++ b/fs/xfs/support/debug.c +@@ -108,6 +108,7 @@ assfail(char *expr, char *file, int line + printk("Assertion failed: %s, file: %s, line: %d\n", expr, file, line); + BUG(); + } ++EXPORT_SYMBOL_GPL(assfail); + + void + xfs_hex_dump(void *p, int length) diff --git a/patches.kernel.org.tar.bz2 b/patches.kernel.org.tar.bz2 deleted file mode 100644 index 43f9a46..0000000 Binary files a/patches.kernel.org.tar.bz2 and /dev/null differ diff --git a/patches.rpmify.tar.bz2 b/patches.rpmify.tar.bz2 deleted file mode 100644 index f5670b1..0000000 Binary files a/patches.rpmify.tar.bz2 and /dev/null differ diff --git a/patches.rpmify/buildhost b/patches.rpmify/buildhost new file mode 100644 index 0000000..4e82893 --- /dev/null +++ b/patches.rpmify/buildhost @@ -0,0 +1,38 @@ +From: Andreas Gruenbacher +Subject: Hide the build hostname +Patch-mainline: Never, SuSE-specific + +Instead of the real build host and user name, use "buildhost.suse.de" +and "geeko". + +Signed-off-by: Andreas Gruenbacher + + scripts/mkcompile_h | 17 +++-------------- + 1 file changed, 3 insertions(+), 14 deletions(-) + +--- a/scripts/mkcompile_h ++++ b/scripts/mkcompile_h +@@ -64,20 +64,9 @@ UTS_TRUNCATE="cut -b -$UTS_LEN" + echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\" + + echo \#define LINUX_COMPILE_TIME \"`date +%T`\" +- echo \#define LINUX_COMPILE_BY \"`whoami`\" +- echo \#define LINUX_COMPILE_HOST \"`hostname | $UTS_TRUNCATE`\" +- +- if [ -x /bin/dnsdomainname ]; then +- domain=`dnsdomainname 2> /dev/null` +- elif [ -x /bin/domainname ]; then +- domain=`domainname 2> /dev/null` +- fi +- +- if [ -n "$domain" ]; then +- echo \#define LINUX_COMPILE_DOMAIN \"`echo $domain | $UTS_TRUNCATE`\" +- else +- echo \#define LINUX_COMPILE_DOMAIN +- fi ++ echo \#define LINUX_COMPILE_BY \"geeko\" ++ echo \#define LINUX_COMPILE_HOST \"buildhost\" ++ echo \#define LINUX_COMPILE_DOMAIN \"suse.de\" + + echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | tail -n 1`\" + ) > .tmpcompile diff --git a/patches.rpmify/cloneconfig.diff b/patches.rpmify/cloneconfig.diff new file mode 100644 index 0000000..468ef03 --- /dev/null +++ b/patches.rpmify/cloneconfig.diff @@ -0,0 +1,38 @@ +From: Andreas Gruenbacher +Subject: Add ``cloneconfig'' target +Patch-mainline: not yet + +Cloneconfig takes the first configuration it finds which appears +to belong to the running kernel, and configures the kernel sources +to match this configuration as closely as possible. + +Signed-off-by: Andreas Gruenbacher + + scripts/kconfig/Makefile | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/scripts/kconfig/Makefile ++++ b/scripts/kconfig/Makefile +@@ -106,6 +106,22 @@ allnoconfig: $(obj)/conf + allmodconfig: $(obj)/conf + $< -m $(Kconfig) + ++UNAME_RELEASE := $(shell uname -r) ++CLONECONFIG := $(firstword $(wildcard /proc/config.gz \ ++ /lib/modules/$(UNAME_RELEASE)/.config \ ++ /etc/kernel-config \ ++ /boot/config-$(UNAME_RELEASE))) ++cloneconfig: $(obj)/conf ++ $(Q)case "$(CLONECONFIG)" in \ ++ '') echo -e "The configuration of the running" \ ++ "kernel could not be determined\n"; \ ++ false ;; \ ++ *.gz) gzip -cd $(CLONECONFIG) > .config.running ;; \ ++ *) cat $(CLONECONFIG) > .config.running ;; \ ++ esac && \ ++ echo -e "Cloning configuration file $(CLONECONFIG)\n" ++ $(Q)$< -D .config.running arch/$(SRCARCH)/Kconfig ++ + defconfig: $(obj)/conf + ifeq ($(KBUILD_DEFCONFIG),) + $< -d $(Kconfig) diff --git a/patches.rpmify/dmar-fix-section-mismatch b/patches.rpmify/dmar-fix-section-mismatch new file mode 100644 index 0000000..39f69b4 --- /dev/null +++ b/patches.rpmify/dmar-fix-section-mismatch @@ -0,0 +1,26 @@ +From: Jeff Mahoney +Subject: dmar: Fix section mismatch +Patch-mainline: Next linux-next sync +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/sfr/linux-next.git +Git-commit: 0b8973a81876d90f916507ac40d1381068dc986a + + dmar_ir_support uses dmar_tbl, which is __initdata. dmar_ir_support is + only called by intr_remapping_supported, which is __init. So, we mark + dmar_ir_support as __init as well. + +Signed-off-by: Jeff Mahoney +--- + drivers/pci/dmar.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pci/dmar.c ++++ b/drivers/pci/dmar.c +@@ -1456,7 +1456,7 @@ int dmar_reenable_qi(struct intel_iommu + /* + * Check interrupt remapping support in DMAR table description. + */ +-int dmar_ir_support(void) ++int __init dmar_ir_support(void) + { + struct acpi_table_dmar *dmar; + dmar = (struct acpi_table_dmar *)dmar_tbl; diff --git a/patches.rpmify/firmware-path b/patches.rpmify/firmware-path new file mode 100644 index 0000000..0c9509e --- /dev/null +++ b/patches.rpmify/firmware-path @@ -0,0 +1,26 @@ +From: Jeff Mahoney +Subject: [PATCH] firmware: Allow release-specific firmware dir +Patch-mainline: not yet + + Every kernel package trying to provide files under /lib/firmware runs + into problems really quickly with multiple kernels installed. + + This patch moves them to /lib/firmware/$KERNELRELEASE. udev v127's + firmware.sh looks there first before falling back to /lib/firmware. + +Signed-off-by: Jeff Mahoney +--- + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/Makefile ++++ b/Makefile +@@ -1028,7 +1028,7 @@ depend dep: + + # --------------------------------------------------------------------------- + # Firmware install +-INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware ++INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware/$(KERNELRELEASE) + export INSTALL_FW_PATH + + PHONY += firmware_install diff --git a/patches.rpmify/ia64-mca-fix-cast-from-integer-to-pointer-warning b/patches.rpmify/ia64-mca-fix-cast-from-integer-to-pointer-warning new file mode 100644 index 0000000..9c710da --- /dev/null +++ b/patches.rpmify/ia64-mca-fix-cast-from-integer-to-pointer-warning @@ -0,0 +1,25 @@ +From: Jeff Mahoney +Subject: ia64/mca: Fix cast from integer to pointer warning +Patch-mainline: not yet + + __get_free_pages() returns an unsigned long that is the address of the + pages returned. ia64_mca_cpu_init wants to use it as a data pointer, so + we cast it as void *. + +Signed-off-by: Jeff Mahoney +--- + arch/ia64/kernel/mca.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/arch/ia64/kernel/mca.c ++++ b/arch/ia64/kernel/mca.c +@@ -1858,7 +1858,8 @@ ia64_mca_cpu_init(void *cpu_data) + data = mca_bootmem(); + first_time = 0; + } else +- data = __get_free_pages(GFP_KERNEL, get_order(sz)); ++ data = (void *)__get_free_pages(GFP_KERNEL, ++ get_order(sz)); + if (!data) + panic("Could not allocate MCA memory for cpu %d\n", + cpu); diff --git a/patches.rpmify/powerpc-kvm-build-failure-workaround b/patches.rpmify/powerpc-kvm-build-failure-workaround new file mode 100644 index 0000000..e0c61b2 --- /dev/null +++ b/patches.rpmify/powerpc-kvm-build-failure-workaround @@ -0,0 +1,31 @@ +From: Jeff Mahoney +Subject: powerpc: kvm build failure workaround +Patch-mainline: Hopefully never + + This patch works around an issue with gcc 4.5 that is failing the build + with: + arch/powerpc/kvm/book3s.c:1102:23: error: 'ext_bkp.vrsave' may be used uninitialized in this function + + The warning is incorrect, so we work around it by explicitly setting it to + 0. + +Signed-off-by: Jeff Mahoney +--- + arch/powerpc/kvm/book3s.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/arch/powerpc/kvm/book3s.c ++++ b/arch/powerpc/kvm/book3s.c +@@ -1104,6 +1104,12 @@ int __kvmppc_vcpu_run(struct kvm_run *kv + bool save_vsx = current->thread.used_vsr; + ulong ext_msr; + ++#ifdef CONFIG_ALTIVEC ++ /* JDM This is functionally unnecessary but works around an ++ * over-eager unintialized usage checker in gcc 4.5 */ ++ ext_bkp.vrsave = current->thread.vrsave; ++#endif ++ + /* No need to go into the guest when all we do is going out */ + if (signal_pending(current)) { + kvm_run->exit_reason = KVM_EXIT_INTR; diff --git a/patches.rpmify/ppc-crashdump-typefix b/patches.rpmify/ppc-crashdump-typefix new file mode 100644 index 0000000..ff83706 --- /dev/null +++ b/patches.rpmify/ppc-crashdump-typefix @@ -0,0 +1,23 @@ +From: Jeff Mahoney +Subject: powerpc: use min_t in copy_oldmem_page +Patch-mainline: not yet + + The gcc used in Factory considers the comparison of csize and PAGE_SIZE + to be invalid and causes a build failure. This patch forces it to use size_t. + +Signed-off-by: Jeff Mahoney +--- + arch/powerpc/kernel/crash_dump.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/powerpc/kernel/crash_dump.c ++++ b/arch/powerpc/kernel/crash_dump.c +@@ -128,7 +128,7 @@ ssize_t copy_oldmem_page(unsigned long p + if (!csize) + return 0; + +- csize = min(csize, PAGE_SIZE); ++ csize = min_t(size_t, csize, PAGE_SIZE); + + if (pfn < max_pfn) { + vaddr = __va(pfn << PAGE_SHIFT); diff --git a/patches.rpmify/rpm-kernel-config b/patches.rpmify/rpm-kernel-config new file mode 100644 index 0000000..e3ef193 --- /dev/null +++ b/patches.rpmify/rpm-kernel-config @@ -0,0 +1,23 @@ +From: Andreas Gruenbacher +Subject: Add the CONFIG_SUSE_KERNEL option +Patch-mainline: Never, SuSE-specific + +CONFIG_SUSE_KERNEL is set automatically in our config files. It must +still be added in kconfig so that the option does not disappear +whenever the kernel is reconfigured (e.g., ``make oldconfig''). + +Signed-off-by: Andreas Gruenbacher + + init/Kconfig | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1,3 +1,7 @@ ++config SUSE_KERNEL ++ bool ++ default y ++ + config ARCH + string + option env="ARCH" diff --git a/patches.rpmify/split-package b/patches.rpmify/split-package new file mode 100644 index 0000000..b777922 --- /dev/null +++ b/patches.rpmify/split-package @@ -0,0 +1,33 @@ +From: Jeff Mahoney +Subject: Add SPLIT_PACKAGE option +Patch-mainline: Never + + This patch adds a SPLIT_PACKAGE option which allows the packager to + make decisions on a per-config basis. + +Signed-off-by: Jeff Mahoney +--- + init/Kconfig | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -2,6 +2,18 @@ config SUSE_KERNEL + bool + default y + ++config SPLIT_PACKAGE ++ bool "Split the kernel package into multiple RPMs" ++ depends on SUSE_KERNEL && MODULES ++ help ++ This is an option used by the kernel packaging infrastructure ++ to split kernel modules into different packages. It isn't used ++ by the kernel itself, but allows the the packager to make ++ decisions on a per-config basis. ++ ++ If you aren't packaging a kernel for distribution, it's safe to ++ say n. ++ + config ARCH + string + option env="ARCH" diff --git a/patches.rpmify/tioca-fix-assignment-from-incompatible-pointer-warnings b/patches.rpmify/tioca-fix-assignment-from-incompatible-pointer-warnings new file mode 100644 index 0000000..c8bfe29 --- /dev/null +++ b/patches.rpmify/tioca-fix-assignment-from-incompatible-pointer-warnings @@ -0,0 +1,23 @@ +From: Jeff Mahoney +Subject: tioca: Fix assignment from incompatible pointer warnings +Patch-mainline: not yet + + The prototype for sn_pci_provider->{dma_map,dma_map_consistent} expects + an unsigned long instead of a u64. + +Signed-off-by: Jeff Mahoney +--- + arch/ia64/sn/pci/tioca_provider.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/ia64/sn/pci/tioca_provider.c ++++ b/arch/ia64/sn/pci/tioca_provider.c +@@ -508,7 +508,7 @@ tioca_dma_unmap(struct pci_dev *pdev, dm + * use the GART mapped mode. + */ + static u64 +-tioca_dma_map(struct pci_dev *pdev, u64 paddr, size_t byte_count, int dma_flags) ++tioca_dma_map(struct pci_dev *pdev, unsigned long paddr, size_t byte_count, int dma_flags) + { + u64 mapaddr; + diff --git a/patches.suse.tar.bz2 b/patches.suse.tar.bz2 deleted file mode 100644 index e216d17..0000000 Binary files a/patches.suse.tar.bz2 and /dev/null differ diff --git a/patches.suse/8250-sysrq-ctrl_o.patch b/patches.suse/8250-sysrq-ctrl_o.patch new file mode 100644 index 0000000..f06115c --- /dev/null +++ b/patches.suse/8250-sysrq-ctrl_o.patch @@ -0,0 +1,135 @@ +Subject: no sysrq on Cell QS21/QS22 serial console +From: olh@suse.de +References: 422987 - LTC47675, 96313 - LTC16841 +Patch-mainline: not yet + + +a POWER4 system in 'full-system-partition' mode has the console device +on ttyS0. But the user interface to the Linux system console may still +be on the hardware management console (HMC). If this is the case, there +is no way to send a break to trigger a sysrq. +Other setups do already use 'ctrl o' to trigger sysrq. This includes iSeries +virtual console on tty1 or hvc0, and pSeries LPAR console on hvc0 or hvsi0. + +This affects also Cell Blades QS2x. + +To limit the 'ctrl o' only to the affected systems, query the model property +in the device-tree. The patch makes the serial console not-eight-bit-clean. +Booting with 'console=ttyS0' will disable 'ctrl o', it is only enabled +with console autodetection. + +'ctrl o' is currently mapped to 'flush output', see 'stty -a' + +Signed-off-by: Olaf Hering +--- + arch/powerpc/include/asm/serial.h | 6 ++++ + arch/powerpc/kernel/legacy_serial.c | 52 ++++++++++++++++++++++++++++++++++++ + drivers/serial/8250.c | 6 ++++ + 3 files changed, 64 insertions(+) + +--- a/arch/powerpc/include/asm/serial.h ++++ b/arch/powerpc/include/asm/serial.h +@@ -15,6 +15,12 @@ + /* Default baud base if not found in device-tree */ + #define BASE_BAUD ( 1843200 / 16 ) + ++#if defined(SUPPORT_SYSRQ) && defined(CONFIG_PPC_PSERIES) ++#undef arch_8250_sysrq_via_ctrl_o ++extern int do_sysrq_via_ctrl_o; ++#define arch_8250_sysrq_via_ctrl_o(ch, port) ((ch) == '\x0f' && do_sysrq_via_ctrl_o && uart_handle_break((port))) ++#endif ++ + #ifdef CONFIG_PPC_UDBG_16550 + extern void find_legacy_serial_ports(void); + #else +--- a/arch/powerpc/kernel/legacy_serial.c ++++ b/arch/powerpc/kernel/legacy_serial.c +@@ -494,6 +494,55 @@ device_initcall(serial_dev_init); + + + #ifdef CONFIG_SERIAL_8250_CONSOLE ++#if defined(CONFIG_PPC_PSERIES) && defined(CONFIG_SERIAL_8250_CONSOLE) ++/* ++ * Handle the SysRq ^O Hack also via ttyS0 on POWER4 systems ++ * but only on the system console, see asm/serial.h ++ * If they run in FullSystemPartition mode, the firmware console comes in via ttyS0 ++ * But BREAK does not work via the HMC, to trigger sysrq. ++ * The same is required for Cell blades ++ */ ++int do_sysrq_via_ctrl_o; ++static const char __initdata *need_ctrl_o[] = { ++ "IBM,079", /* QS2x */ ++ "IBM,0792-32G", /* QS21 */ ++ "IBM,0793-2RZ", /* QS22 */ ++ "IBM,7040-681", /* p690 */ ++ "IBM,7040-671", /* p670 */ ++ "IBM,7039-651", /* p655 */ ++ "IBM,7038-6M2", /* p650 */ ++ "IBM,7028-6E4", /* p630 tower */ ++ "IBM,7028-6C4", /* p630 rack */ ++ "IBM,7029-6E3", /* p615 tower */ ++ "IBM,7029-6C3", /* p615 rack */ ++ NULL ++}; ++static void __init detect_need_for_ctrl_o(void) ++{ ++ struct device_node *root; ++ const char *model, *p; ++ int i; ++ ++ root = of_find_node_by_path("/"); ++ if (!root) ++ return; ++ model = of_get_property(root, "model", NULL); ++ if (model) { ++ i = 0; ++ while (need_ctrl_o[i]) { ++ p = need_ctrl_o[i]; ++ if (strncmp(p, model, strlen(p)) == 0) { ++ do_sysrq_via_ctrl_o = 1; ++ DBG("Enable sysrq via CTRL o on model %s\n", model); ++ break; ++ } ++ i++; ++ } ++ } ++ of_node_put(root); ++} ++#endif ++ + /* + * This is called very early, as part of console_init() (typically just after + * time_init()). This function is respondible for trying to find a good +@@ -562,6 +611,9 @@ static int __init check_legacy_serial_co + if (i >= legacy_serial_count) + goto not_found; + ++#if defined(CONFIG_PPC_PSERIES) && defined(CONFIG_SERIAL_8250_CONSOLE) ++ detect_need_for_ctrl_o(); ++#endif + of_node_put(prom_stdout); + + DBG("Found serial console at ttyS%d\n", offset); +--- a/drivers/serial/8250.c ++++ b/drivers/serial/8250.c +@@ -100,6 +100,8 @@ static unsigned int skip_txen_test; /* f + #define CONFIG_SERIAL_MANY_PORTS 1 + #endif + ++#define arch_8250_sysrq_via_ctrl_o(a,b) 0 ++ + /* + * HUB6 is always on. This will be removed once the header + * files have been cleaned. +@@ -1386,7 +1388,11 @@ receive_chars(struct uart_8250_port *up, + + do { + if (likely(lsr & UART_LSR_DR)) ++ { + ch = serial_inp(up, UART_RX); ++ if (arch_8250_sysrq_via_ctrl_o(ch, &up->port)) ++ goto ignore_char; ++ } + else + /* + * Intel 82571 has a Serial Over Lan device that will diff --git a/patches.suse/Cleanup-and-make-boot-splash-work-with-KMS.patch b/patches.suse/Cleanup-and-make-boot-splash-work-with-KMS.patch new file mode 100644 index 0000000..185a8ea --- /dev/null +++ b/patches.suse/Cleanup-and-make-boot-splash-work-with-KMS.patch @@ -0,0 +1,1500 @@ +From 1319de907e12d28894d8db0b3215a0443ff4bd5d Mon Sep 17 00:00:00 2001 +From: Egbert Eich +Date: Thu, 22 Oct 2009 13:32:11 +0200 +Subject: [PATCH] Cleanup and make boot splash work with KMS +References: bnc#544645 +Patch-mainline: not yet + + - Fix API: remove unneeded function argument + - Remove unneeded function splash_putc(). + - Remove need for 2 framebuffer basis: when swiching from silent to + verbose rely on update_region() to redraw the verbose picture. + This removes the need to double the framebuffer size when using + splash. + - Use worker to switch to verbose mode. + - Add support for 24 and 32bpp (24bpp still disabled due to lack of testing). + +Acked-by: Michal Marek +--- + drivers/char/vt.c | 10 + drivers/video/bootsplash/bootsplash.c | 407 +++++++++++++++++++++--------- + drivers/video/bootsplash/bootsplash.h | 23 - + drivers/video/bootsplash/decode-jpg.c | 50 +++ + drivers/video/bootsplash/render.c | 451 +++++++++++++++++++++------------- + drivers/video/console/bitblit.c | 12 + drivers/video/console/fbcon.c | 4 + drivers/video/vesafb.c | 8 + include/linux/fb.h | 3 + 9 files changed, 652 insertions(+), 316 deletions(-) + +--- a/drivers/char/vt.c ++++ b/drivers/char/vt.c +@@ -4104,7 +4104,7 @@ void vcs_scr_writew(struct vc_data *vc, + #ifdef CONFIG_BOOTSPLASH + void con_remap_def_color(struct vc_data *vc, int new_color) + { +- unsigned short *sbuf = vc->vc_screenbuf; ++ unsigned short *sbuf = screenpos(vc, 0, 1); + unsigned c, len = vc->vc_screenbuf_size >> 1; + int old_color; + +@@ -4112,11 +4112,13 @@ void con_remap_def_color(struct vc_data + old_color = vc->vc_def_color << 8; + new_color <<= 8; + while(len--) { +- c = *sbuf; ++ c = scr_readw(sbuf); + if (((c ^ old_color) & 0xf000) == 0) +- *sbuf ^= (old_color ^ new_color) & 0xf000; ++ scr_writew(c ^ ((old_color ^ new_color) & 0xf000), sbuf); ++ *sbuf ^= (old_color ^ new_color) & 0xf000; + if (((c ^ old_color) & 0x0f00) == 0) +- *sbuf ^= (old_color ^ new_color) & 0x0f00; ++ scr_writew(c ^ ((old_color ^ new_color) & 0x0f00), sbuf); ++ *sbuf ^= (old_color ^ new_color) & 0x0f00; + sbuf++; + } + new_color >>= 8; +--- a/drivers/video/bootsplash/bootsplash.c ++++ b/drivers/video/bootsplash/bootsplash.c +@@ -19,6 +19,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -27,7 +29,12 @@ + #include "bootsplash.h" + #include "decode-jpg.h" + +-extern struct fb_ops vesafb_ops; ++#ifndef DEBUG ++# define SPLASH_DEBUG(fmt, args...) ++#else ++# define SPLASH_DEBUG(fmt, args...) \ ++ printk(KERN_WARNING "%s: " fmt "\n",__FUNCTION__, ##args) ++#endif + extern signed char con2fb_map[MAX_NR_CONSOLES]; + + #define SPLASH_VERSION "3.1.6-2004/03/31" +@@ -113,18 +120,20 @@ static int boxextract(unsigned char *buf + return 12; + } + +-static void boxit(unsigned char *pic, int bytes, unsigned char *buf, int num, int percent, int overpaint) ++static void boxit(unsigned char *pic, int bytes, unsigned char *buf, int num, int percent, int overpaint, int octpp) + { +- int x, y, i, p, doblend, r, g, b, a, add; ++ int x, y, p, doblend, r, g, b, a, add; ++ unsigned int i = 0; + unsigned short data1[4]; + unsigned char cols1[16]; + unsigned short data2[4]; + unsigned char cols2[16]; + unsigned char *bufend; +- unsigned short *picp; ++ union pt picp; + unsigned int stipple[32], sti, stin, stinn, stixs, stixe, stiys, stiye; + int xs, xe, ys, ye, xo, yo; + ++ SPLASH_DEBUG(); + if (num == 0) + return; + bufend = buf + num * 12; +@@ -236,11 +245,21 @@ static void boxit(unsigned char *pic, in + } + add = (xs & 1); + add ^= (add ^ y) & 1 ? 1 : 3; /* 2x2 ordered dithering */ +- picp = (unsigned short *)(pic + xs * 2 + y * bytes); ++ picp.ub = (pic + xs * octpp + y * bytes); + for (x = xs; x <= xe; x++) { + if (!(sti & 0x80000000)) { + sti <<= 1; +- picp++; ++ switch (octpp) { ++ case 2: ++ picp.us++; ++ break; ++ case 3: ++ picp.ub += 3; ++ break; ++ case 4: ++ picp.ul++; ++ break; ++ } + add ^= 3; + continue; + } +@@ -255,18 +274,37 @@ static void boxit(unsigned char *pic, in + r = cols2[0]; + g = cols2[1]; + b = cols2[2]; +- if (a != 255) { +- i = *picp; +- r = ((i >> 8 & 0xf8) * (255 - a) + r * a) / 255; +- g = ((i >> 3 & 0xfc) * (255 - a) + g * a) / 255; +- b = ((i << 3 & 0xf8) * (255 - a) + b * a) / 255; +- } +- #define CLAMP(x) ((x) >= 256 ? 255 : (x)) +- i = ((CLAMP(r + add*2+1) & 0xf8) << 8) | +- ((CLAMP(g + add ) & 0xfc) << 3) | +- ((CLAMP(b + add*2+1) ) >> 3); +- *picp++ = i; +- add ^= 3; ++#define CLAMP(x) ((x) >= 256 ? 255 : (x)) ++ switch (octpp) { ++ case 2: ++ i = *picp.us; ++ if (a != 255) { ++ r = ((i >> 8 & 0xf8) * (255 - a) + r * a) / 255; ++ g = ((i >> 3 & 0xfc) * (255 - a) + g * a) / 255; ++ b = ((i << 3 & 0xf8) * (255 - a) + b * a) / 255; ++ } ++ i = ((CLAMP(r + add*2+1) & 0xf8) << 8) | ++ ((CLAMP(g + add ) & 0xfc) << 3) | ++ ((CLAMP(b + add*2+1) ) >> 3); ++ *(picp.us++) = i; ++ break; ++ case 3: ++ *(picp.ub++) = CLAMP(a == 255 ? r : (((i & 0xff) * (255 - a) + r * a) / 255)); ++ *(picp.ub++) = CLAMP(a == 255 ? r : (((i >> 8 & 0xff) * (255 - a) + r * a) / 255)); ++ *(picp.ub++) = CLAMP(a == 255 ? r : (((i >> 16 & 0xff) * (255 - a) + r * a) / 255)); ++ break; ++ case 4: ++ i = *picp.ul; ++ if (a != 255) { ++ r = ((i >> 16 & 0xff) * (255 - a) + r * a) / 255; ++ g = ((i >> 8 & 0xff) * (255 - a) + r * a) / 255; ++ b = ((i & 0xff) * (255 - a) + r * a) / 255; ++ } ++ i = ((CLAMP(r) << 16) | (CLAMP(g) << 8) | (CLAMP(b))); ++ *(picp.ul++) = i; ++ break; ++ } ++ add ^= 3; + } + } + } +@@ -293,16 +331,14 @@ static int splash_check_jpeg(unsigned ch + + static void splash_free(struct vc_data *vc, struct fb_info *info) + { +- if (!vc->vc_splash_data) +- return; +- if (info->silent_screen_base) +- info->screen_base = info->silent_screen_base; +- info->silent_screen_base = 0; +- if (vc->vc_splash_data->splash_silentjpeg) +- vfree(vc->vc_splash_data->splash_sboxes); +- vfree(vc->vc_splash_data); +- vc->vc_splash_data = 0; +- info->splash_data = 0; ++ SPLASH_DEBUG(); ++ if (!vc->vc_splash_data) ++ return; ++ if (vc->vc_splash_data->splash_silentjpeg) ++ vfree(vc->vc_splash_data->splash_sboxes); ++ vfree(vc->vc_splash_data); ++ vc->vc_splash_data = 0; ++ info->splash_data = 0; + } + + static int splash_mkpenguin(struct splash_data *data, int pxo, int pyo, int pwi, int phe, int pr, int pg, int pb) +@@ -590,37 +626,69 @@ static int splash_getraw(unsigned char * + return -1; + } + +-int splash_verbose(void) ++int splash_do_verbose(void) + { +- struct vc_data *vc; +- struct fb_info *info; ++ struct vc_data *vc; ++ struct fb_info *info; ++ int ret = 0; + +- if (!splash_usesilent) +- return 0; ++ SPLASH_DEBUG(); ++ if (!oops_in_progress) ++ acquire_console_sem(); + +- vc = vc_cons[0].d; ++ if (!splash_usesilent) ++ goto done; + +- if (!vc || !vc->vc_splash_data || !vc->vc_splash_data->splash_state) +- return 0; +- if (fg_console != vc->vc_num) +- return 0; +- if (!vc->vc_splash_data->splash_silentjpeg || !vc->vc_splash_data->splash_dosilent) +- return 0; +- vc->vc_splash_data->splash_dosilent = 0; +- info = registered_fb[(int)con2fb_map[0]]; +- if (!info->silent_screen_base) ++ vc = vc_cons[0].d; ++ ++ if (!vc || !vc->vc_splash_data || !vc->vc_splash_data->splash_state) ++ goto done; ++ if (fg_console != vc->vc_num) ++ goto done; ++ if (!vc->vc_splash_data->splash_silentjpeg) ++ goto done; ++ ++ if(!vc->vc_splash_data->splash_dosilent) ++ goto done; ++ vc->vc_splash_data->splash_dosilent = 0; ++ ++ info = registered_fb[(int)con2fb_map[0]]; ++ ++ if (!info->splash_data) ++ goto done; ++ ++ update_region(vc, ++ vc->vc_origin + vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ splash_clear_margins(vc, info, 0); ++ ret = 0; ++ ++ done: ++ if (!oops_in_progress) ++ release_console_sem(); ++ ++ return ret; ++} ++ ++static void splash_verbose_callback(struct work_struct *ignored) ++{ ++ splash_do_verbose(); ++} ++ ++static DECLARE_WORK(splash_work, splash_verbose_callback); ++ ++int splash_verbose(void) ++{ ++ if (!oops_in_progress) ++ schedule_work(&splash_work); ++ else ++ return splash_do_verbose(); + return 0; +- splashcopy(info->silent_screen_base, info->screen_base, info->var.yres, info->var.xres, info->fix.line_length, info->fix.line_length); +- info->screen_base = info->silent_screen_base; +- info->silent_screen_base = 0; +- return 1; + } + + static void splash_off(struct fb_info *info) + { +- if (info->silent_screen_base) +- info->screen_base = info->silent_screen_base; +- info->silent_screen_base = 0; ++ SPLASH_DEBUG(); + info->splash_data = 0; + if (info->splash_pic) + vfree(info->splash_pic); +@@ -631,8 +699,9 @@ static void splash_off(struct fb_info *i + int splash_prepare(struct vc_data *vc, struct fb_info *info) + { + int err; +- int width, height, depth, size, sbytes; ++ int width, height, depth, octpp, size, sbytes; + ++ SPLASH_DEBUG("vc_num: %i", vc->vc_num); + if (!vc->vc_splash_data || !vc->vc_splash_data->splash_state) { + if (decdata) + vfree(decdata); +@@ -644,15 +713,19 @@ int splash_prepare(struct vc_data *vc, s + width = info->var.xres; + height = info->var.yres; + depth = info->var.bits_per_pixel; +- if (depth != 16) { /* Other targets might need fixing */ ++ octpp = (depth + 1) >> 3; ++ ++ if (depth == 24 || depth < 15) { /* Other targets might need fixing */ + splash_off(info); + return -2; + } + +- sbytes = ((width + 15) & ~15) * (depth >> 3); ++ sbytes = ((width + 15) & ~15) * octpp; + size = sbytes * ((height + 15) & ~15); +- if (size != info->splash_pic_size) +- splash_off(info); ++ if (size != info->splash_pic_size) { ++ vfree(info->splash_pic); ++ info->splash_pic = NULL; ++ } + if (!info->splash_pic) + info->splash_pic = vmalloc(size); + +@@ -668,38 +741,52 @@ int splash_prepare(struct vc_data *vc, s + if (vc->vc_splash_data->splash_silentjpeg && vc->vc_splash_data->splash_dosilent) { + /* fill area after framebuffer with other jpeg */ + if ((err = jpeg_decode(vc->vc_splash_data->splash_silentjpeg, info->splash_pic, +- ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { +- printk(KERN_INFO "bootsplash: error while decompressing silent picture: %s (%d)\n", jpg_errors[err - 1], err); +- if (info->silent_screen_base) +- info->screen_base = info->silent_screen_base; ++ ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { ++ printk(KERN_INFO "bootsplash: error while decompressing silent picture: %s (%d)\n", ++ jpg_errors[err - 1], err); + vc->vc_splash_data->splash_dosilent = 0; + } else { + if (vc->vc_splash_data->splash_sboxcount) +- boxit(info->splash_pic, sbytes, vc->vc_splash_data->splash_sboxes, +- vc->vc_splash_data->splash_sboxcount, vc->vc_splash_data->splash_percent, 0); +- +- if (!info->silent_screen_base) +- info->silent_screen_base = info->screen_base; +- splashcopy(info->silent_screen_base, info->splash_pic, info->var.yres, info->var.xres, info->fix.line_length, sbytes); +- info->screen_base = info->silent_screen_base + info->fix.line_length * info->var.yres; ++ boxit(info->splash_pic, ++ sbytes, ++ vc->vc_splash_data->splash_sboxes, ++ vc->vc_splash_data->splash_sboxcount, ++ vc->vc_splash_data->splash_percent, ++ 0, ++ octpp); ++ splashcopy(info->screen_base, ++ info->splash_pic, ++ info->var.yres, ++ info->var.xres, ++ info->fix.line_length, sbytes, ++ octpp ); + } +- } else if (info->silent_screen_base) +- info->screen_base = info->silent_screen_base; ++ } else ++ vc->vc_splash_data->splash_dosilent = 0; + + if ((err = jpeg_decode(vc->vc_splash_data->splash_jpeg, info->splash_pic, + ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { +- printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d) .\n", jpg_errors[err - 1], err); ++ printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d) .\n", ++ jpg_errors[err - 1], err); + splash_off(info); + return -4; + } + info->splash_pic_size = size; +- info->splash_bytes = sbytes; ++ info->splash_pic_stride = sbytes; + if (vc->vc_splash_data->splash_boxcount) +- boxit(info->splash_pic, sbytes, vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount, vc->vc_splash_data->splash_percent, 0); ++ boxit(info->splash_pic, ++ sbytes, ++ vc->vc_splash_data->splash_boxes, ++ vc->vc_splash_data->splash_boxcount, ++ vc->vc_splash_data->splash_percent, ++ 0, ++ octpp); + if (vc->vc_splash_data->splash_state) + info->splash_data = vc->vc_splash_data; +- else ++ else { + splash_off(info); ++ return -5; ++ } + return 0; + } + +@@ -720,6 +807,7 @@ static struct proc_dir_entry *proc_splas + + static int splash_recolor(struct vc_data *vc) + { ++ SPLASH_DEBUG(); + if (!vc->vc_splash_data) + return -1; + if (!vc->vc_splash_data->splash_state) +@@ -736,20 +824,27 @@ static int splash_recolor(struct vc_data + static int splash_status(struct vc_data *vc) + { + struct fb_info *info; +- printk(KERN_INFO "bootsplash: status on console %d changed to %s\n", vc->vc_num, vc->vc_splash_data && vc->vc_splash_data->splash_state ? "on" : "off"); ++ SPLASH_DEBUG("vc_num: %i",vc->vc_num); ++ printk(KERN_INFO "bootsplash: status on console %d changed to %s\n", ++ vc->vc_num, ++ vc->vc_splash_data && vc->vc_splash_data->splash_state ? "on" : "off"); + + info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ + if (fg_console == vc->vc_num) + splash_prepare(vc, info); + if (vc->vc_splash_data && vc->vc_splash_data->splash_state) { +- con_remap_def_color(vc, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color); +- /* vc_resize also calls con_switch which resets yscroll */ +- vc_resize(vc, vc->vc_splash_data->splash_text_wi / vc->vc_font.width, vc->vc_splash_data->splash_text_he / vc->vc_font.height); +- if (fg_console == vc->vc_num) { +- update_region(vc, +- vc->vc_origin + vc->vc_size_row * vc->vc_top, +- vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); +- splash_clear_margins(vc->vc_splash_data, vc, info, 0); ++ if (info->splash_data) { ++ con_remap_def_color(vc, info->splash_data->splash_color << 4 | info->splash_data->splash_fg_color); ++ /* vc_resize also calls con_switch which resets yscroll */ ++ vc_resize(vc, info->splash_data->splash_text_wi / vc->vc_font.width, ++ info->splash_data->splash_text_he / vc->vc_font.height); ++ if (fg_console == vc->vc_num) { ++ update_region(vc, ++ vc->vc_origin + vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ splash_clear_margins(vc, info, 0); ++ } + } + } else { + /* Switch bootsplash off */ +@@ -787,6 +882,8 @@ void splash_set_percent(struct vc_data * + struct fbcon_ops *ops; + int oldpe; + ++ SPLASH_DEBUG(); ++ + if (pe < 0) + pe = 0; + if (pe > 65535) +@@ -805,15 +902,38 @@ void splash_set_percent(struct vc_data * + ops = info->fbcon_par; + if (ops->blank_state) + return; +- if (!vc->vc_splash_data->splash_overpaintok || pe == 65536 || pe < oldpe) { +- if (splash_hasinter(vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount)) +- splash_status(vc); +- else +- splash_prepare(vc, info); ++ if (!vc->vc_splash_data->splash_overpaintok ++ || pe == 65536 ++ || pe < oldpe) { ++ if (splash_hasinter(vc->vc_splash_data->splash_boxes, ++ vc->vc_splash_data->splash_boxcount)) { ++ splash_status(vc); ++ } ++ else ++ splash_prepare(vc, info); + } else { +- if (vc->vc_splash_data->splash_silentjpeg && vc->vc_splash_data->splash_dosilent && info->silent_screen_base) +- boxit(info->silent_screen_base, info->fix.line_length, vc->vc_splash_data->splash_sboxes, vc->vc_splash_data->splash_sboxcount, vc->vc_splash_data->splash_percent, 1); +- boxit(info->screen_base, info->fix.line_length, vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount, vc->vc_splash_data->splash_percent, 1); ++ int octpp = (info->var.bits_per_pixel + 1) >> 3; ++ if (info->splash_data) { ++ if ( info->splash_data->splash_silentjpeg ++ && info->splash_data->splash_dosilent) ++ boxit(info->screen_base, ++ info->fix.line_length, ++ info->splash_data->splash_sboxes, ++ info->splash_data->splash_sboxcount, ++ info->splash_data->splash_percent, ++ 1, ++ octpp); ++#if 0 ++ if (!info->splash_dosilent) ++ boxit(info->screen_base, ++ info->fix.line_length, ++ info->splash_data->splash_boxes, ++ info->splash_data->splash_boxcount, ++ info->splash_data->splash_percent, ++ 1, ++ octpp); ++#endif ++ } + } + } + +@@ -823,6 +943,8 @@ static int splash_write_proc(struct file + int new, unit; + struct vc_data *vc; + ++ SPLASH_DEBUG(); ++ + if (!buffer || !splash_default) + return count; + +@@ -842,8 +964,10 @@ static int splash_write_proc(struct file + return count; + } + } ++ SPLASH_DEBUG(" unit: %i",unit); + vc = vc_cons[unit].d; + if (!strncmp(buffer, "redraw", 6)) { ++ SPLASH_DEBUG( " redraw"); + splash_status(vc); + release_console_sem(); + return count; +@@ -851,6 +975,7 @@ static int splash_write_proc(struct file + if (!strncmp(buffer, "show", 4) || !strncmp(buffer, "hide", 4)) { + int pe; + ++ SPLASH_DEBUG( " show/hide"); + if (buffer[4] == ' ' && buffer[5] == 'p') + pe = 0; + else if (buffer[4] == '\n') +@@ -867,51 +992,77 @@ static int splash_write_proc(struct file + release_console_sem(); + return count; + } ++ + if (!strncmp(buffer,"silent\n",7) || !strncmp(buffer,"verbose\n",8)) { ++ SPLASH_DEBUG( " silent/verbose"); + if (vc->vc_splash_data && vc->vc_splash_data->splash_silentjpeg) { +- if (vc->vc_splash_data->splash_dosilent != (buffer[0] == 's')) { +- vc->vc_splash_data->splash_dosilent = buffer[0] == 's'; +- splash_status(vc); +- } ++ if (vc->vc_splash_data->splash_dosilent != (buffer[0] == 's')) { ++ vc->vc_splash_data->splash_dosilent = buffer[0] == 's'; ++ splash_status(vc); ++ } + } + release_console_sem(); + return count; + } + if (!strncmp(buffer,"freesilent\n",11)) { ++ SPLASH_DEBUG( " freesilent"); + if (vc->vc_splash_data && vc->vc_splash_data->splash_silentjpeg) { +- printk(KERN_INFO "bootsplash: freeing silent jpeg\n"); +- vc->vc_splash_data->splash_silentjpeg = 0; +- vfree(vc->vc_splash_data->splash_sboxes); +- vc->vc_splash_data->splash_sboxes = 0; +- vc->vc_splash_data->splash_sboxcount = 0; +- if (vc->vc_splash_data->splash_dosilent) +- splash_status(vc); +- vc->vc_splash_data->splash_dosilent = 0; ++ printk(KERN_INFO "bootsplash: freeing silent jpeg\n"); ++ vc->vc_splash_data->splash_silentjpeg = 0; ++ vfree(vc->vc_splash_data->splash_sboxes); ++ vc->vc_splash_data->splash_sboxes = 0; ++ vc->vc_splash_data->splash_sboxcount = 0; ++ if (vc->vc_splash_data->splash_dosilent) { ++ splash_status(vc); ++ } ++ vc->vc_splash_data->splash_dosilent = 0; + } + release_console_sem(); + return count; + } +- + if (!strncmp(buffer, "BOOTSPL", 7)) { +- int up = -1; +- unit = splash_getraw((unsigned char *)buffer, (unsigned char *)buffer + count, &up); +- if (unit >= 0) { +- vc = vc_cons[unit].d; +- if (up == -1) +- splash_status(vc); +- else { +- struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; +- struct fbcon_ops *ops = info->fbcon_par; +- if (ops->blank_state) +- up = 0; +- if ((up & 2) != 0 && vc->vc_splash_data->splash_silentjpeg && vc->vc_splash_data->splash_dosilent && info->silent_screen_base) +- boxit(info->silent_screen_base, info->fix.line_length, vc->vc_splash_data->splash_sboxes, vc->vc_splash_data->splash_sboxcount, vc->vc_splash_data->splash_percent, 1); +- if ((up & 1) != 0) +- boxit(info->screen_base, info->fix.line_length, vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount, vc->vc_splash_data->splash_percent, 1); ++ int up = -1; ++ ++ SPLASH_DEBUG( " BOOTSPL"); ++ unit = splash_getraw((unsigned char *)buffer, ++ (unsigned char *)buffer + count, ++ &up); ++ SPLASH_DEBUG( " unit: %i up: %i",unit,up); ++ if (unit >= 0) { ++ struct fb_info *info; ++ ++ vc = vc_cons[unit].d; ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ if (up == -1) { ++ splash_status(vc); ++ } else { ++ struct fbcon_ops *ops = info->fbcon_par; ++ int octpp = (info->var.bits_per_pixel + 1) >> 3; ++ if (ops->blank_state || !vc->vc_splash_data || !info->splash_data) ++ up = 0; ++ if ((up & 2) != 0 ++ && info->splash_data->splash_silentjpeg ++ && info->splash_data->splash_dosilent) { ++ boxit(info->screen_base, ++ info->fix.line_length, ++ info->splash_data->splash_sboxes, ++ info->splash_data->splash_sboxcount, ++ info->splash_data->splash_percent, ++ 1, ++ octpp); ++ } else if ((up & 1) != 0) { ++ boxit(info->screen_base, ++ info->fix.line_length, ++ info->splash_data->splash_boxes, ++ info->splash_data->splash_boxcount, ++ info->splash_data->splash_percent, ++ 1, ++ octpp); ++ } ++ } + } +- } +- release_console_sem(); +- return count; ++ release_console_sem(); ++ return count; + } + if (!vc->vc_splash_data) { + release_console_sem(); +@@ -919,6 +1070,7 @@ static int splash_write_proc(struct file + } + if (buffer[0] == 't') { + vc->vc_splash_data->splash_state ^= 1; ++ SPLASH_DEBUG(" t"); + splash_status(vc); + release_console_sem(); + return count; +@@ -959,6 +1111,8 @@ static int splash_proc_unregister(void) + # endif + #endif /* CONFIG_PROC_FS */ + ++#define INIT_CONSOLE 0 ++ + void splash_init(void) + { + struct fb_info *info; +@@ -971,9 +1125,12 @@ void splash_init(void) + + if (splash_registered) + return; +- vc = vc_cons[0].d; +- info = registered_fb[0]; +- if (!vc || !info || info->var.bits_per_pixel != 16) ++ vc = vc_cons[INIT_CONSOLE].d; ++ info = registered_fb[(int)con2fb_map[INIT_CONSOLE]]; ++ if (!vc ++ || !info ++ || info->var.bits_per_pixel == 24 /* not tested */ ++ || info->var.bits_per_pixel < 15) /* not supported */ + return; + #ifdef CONFIG_PROC_FS + splash_proc_register(); +@@ -1004,7 +1161,9 @@ void splash_init(void) + mem = vmalloc(len); + if (mem) { + acquire_console_sem(); +- if ((int)sys_read(fd, mem, len) == len && splash_getraw((unsigned char *)mem, (unsigned char *)mem + len, (int *)0) == 0 && vc->vc_splash_data) ++ if ((int)sys_read(fd, mem, len) == len ++ && splash_getraw((unsigned char *)mem, (unsigned char *)mem + len, (int *)0) == INIT_CONSOLE ++ && vc->vc_splash_data) + vc->vc_splash_data->splash_state = splash_default & 1; + release_console_sem(); + vfree(mem); +--- a/drivers/video/bootsplash/bootsplash.h ++++ b/drivers/video/bootsplash/bootsplash.h +@@ -12,27 +12,30 @@ + #define __BOOTSPLASH_H + + struct fb_info; ++union pt { ++ u32 *ul; ++ u16 *us; ++ u8 *ub; ++}; + + /* splash.c */ + extern int splash_prepare(struct vc_data *, struct fb_info *); + extern void splash_init(void); + + /* splash_render.c */ +-extern void splash_putcs(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++extern void splash_putcs(struct vc_data *vc, struct fb_info *info, + const unsigned short *s, int count, int ypos, int xpos); +-extern void splash_putc(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, +- int c, int ypos, int xpos); +-extern void splashcopy(u8 *dst, u8 *src, int height, int width, int dstbytes, int srcbytes); +-extern void splash_clear(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int sy, ++extern void splashcopy(u8 *dst, u8 *src, int height, int width, int dstbytes, int srcbytes, int depth); ++extern void splash_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width); +-extern void splash_bmove(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int sy, ++extern void splash_bmove(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int dy, int dx, int height, int width); +-extern void splash_clear_margins(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++extern void splash_clear_margins(struct vc_data *vc, struct fb_info *info, + int bottom_only); +-extern int splash_cursor(struct splash_data *sd, struct fb_info *info, struct fb_cursor *cursor); +-extern void splash_bmove_redraw(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++extern int splash_cursor(struct fb_info *info, struct fb_cursor *cursor); ++extern void splash_bmove_redraw(struct vc_data *vc, struct fb_info *info, + int y, int sx, int dx, int width); +-extern void splash_blank(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++extern void splash_blank(struct vc_data *vc, struct fb_info *info, + int blank); + + /* vt.c */ +--- a/drivers/video/bootsplash/decode-jpg.c ++++ b/drivers/video/bootsplash/decode-jpg.c +@@ -86,6 +86,7 @@ static void initcol __P((PREC[][64])); + + static void col221111 __P((int *, unsigned char *, int)); + static void col221111_16 __P((int *, unsigned char *, int)); ++static void col221111_32 __P((int *, unsigned char *, int)); + + /*********************************/ + +@@ -369,6 +370,9 @@ struct jpeg_decdata *decdata; + idct(decdata->dcts + 320, decdata->out + 320, decdata->dquant[2], IFIX(0.5), max[5]); + + switch (depth) { ++ case 32: ++ col221111_32(decdata->out, pic + (my * 16 * mcusx + mx) * 16 * 4, mcusx * 16 * 4); ++ break; + case 24: + col221111(decdata->out, pic + (my * 16 * mcusx + mx) * 16 * 3, mcusx * 16 * 3); + break; +@@ -882,6 +886,15 @@ PREC q[][64]; + #endif + #endif + ++#define PIC_32(yin, xin, p, xout) \ ++( \ ++ y = outy[(yin) * 8 + xin], \ ++ STORECLAMP(p[(xout) * 4 + 0], y + cr), \ ++ STORECLAMP(p[(xout) * 4 + 1], y - cg), \ ++ STORECLAMP(p[(xout) * 4 + 2], y + cb), \ ++ p[(xout) * 4 + 3] = 0 \ ++) ++ + #define PIC221111(xin) \ + ( \ + CBCRCG(0, xin), \ +@@ -900,6 +913,15 @@ PREC q[][64]; + PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1, 2) \ + ) + ++#define PIC221111_32(xin) \ ++( \ ++ CBCRCG(0, xin), \ ++ PIC_32(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0),\ ++ PIC_32(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1),\ ++ PIC_32(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0),\ ++ PIC_32(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1) \ ++) ++ + static void col221111(out, pic, width) + int *out; + unsigned char *pic; +@@ -949,6 +971,34 @@ int width; + } + outc += 8; + outy += 16; ++ pic0 += 2 * width; ++ pic1 += 2 * width; ++ } ++ outy += 64 * 2 - 16 * 4; ++ } ++} ++ ++static void col221111_32(out, pic, width) ++int *out; ++unsigned char *pic; ++int width; ++{ ++ int i, j, k; ++ unsigned char *pic0, *pic1; ++ int *outy, *outc; ++ int cr, cg, cb, y; ++ ++ pic0 = pic; ++ pic1 = pic + width; ++ outy = out; ++ outc = out + 64 * 4; ++ for (i = 2; i > 0; i--) { ++ for (j = 4; j > 0; j--) { ++ for (k = 0; k < 8; k++) { ++ PIC221111_32(k); ++ } ++ outc += 8; ++ outy += 16; + pic0 += 2 * width; + pic1 += 2 * width; + } +--- a/drivers/video/bootsplash/render.c ++++ b/drivers/video/bootsplash/render.c +@@ -13,82 +13,131 @@ + #include "../console/fbcon.h" + #include "bootsplash.h" + +-void splash_putcs(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, +- const unsigned short *s, int count, int ypos, int xpos) +-{ +- unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; +- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; +- int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; +- u8 *src; +- u8 *dst, *splashsrc; +- unsigned int d, x, y; +- u32 dd, fgx, bgx; +- u16 c = scr_readw(s); ++#ifndef DEBUG ++# define SPLASH_DEBUG(fmt, args...) ++#else ++# define SPLASH_DEBUG(fmt, args...) \ ++ printk(KERN_WARNING "%s: " fmt "\n",__FUNCTION__, ##args) ++#endif + +- int fg_color, bg_color, transparent; +- if (console_blanked) +- return; +- fg_color = attr_fgcol(fgshift, c); +- bg_color = attr_bgcol(bgshift, c); +- transparent = sd->splash_color == bg_color; +- xpos = xpos * vc->vc_font.width + sd->splash_text_xo; +- ypos = ypos * vc->vc_font.height + sd->splash_text_yo; +- splashsrc = (u8 *)(info->splash_pic + ypos * info->splash_bytes + xpos * 2); +- dst = (u8 *)(info->screen_base + ypos * info->fix.line_length + xpos * 2); +- +- fgx = ((u32 *)info->pseudo_palette)[fg_color]; +- if (transparent && sd->splash_color == 15) { +- if (fgx == 0xffea) +- fgx = 0xfe4a; +- else if (fgx == 0x57ea) +- fgx = 0x0540; +- else if (fgx == 0xffff) +- fgx = 0x52aa; +- } +- bgx = ((u32 *)info->pseudo_palette)[bg_color]; +- d = 0; +- +- while (count--) { +- c = scr_readw(s++); +- src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * ((vc->vc_font.width + 7) >> 3); +- +- for (y = 0; y < vc->vc_font.height; y++) { +- for (x = 0; x < vc->vc_font.width; x += 2) { +- if ((x & 7) == 0) +- d = *src++; +- if (d & 0x80) +- dd = fgx; +- else +- dd = transparent ? *(u16 *)splashsrc : bgx; +- splashsrc += 2; +- if (d & 0x40) +- dd |= fgx << 16; +- else +- dd |= (transparent ? *(u16 *)splashsrc : bgx) << 16; +- splashsrc += 2; +- d <<= 2; +- fb_writel(dd, dst); +- dst += 4; +- } +- dst += info->fix.line_length - vc->vc_font.width * 2; +- splashsrc += info->splash_bytes - vc->vc_font.width * 2; +- } +- dst -= info->fix.line_length * vc->vc_font.height - vc->vc_font.width * 2; +- splashsrc -= info->splash_bytes * vc->vc_font.height - vc->vc_font.width * 2; +- } +-} +- +-static void splash_renderc(struct splash_data *sd, struct fb_info *info, int fg_color, int bg_color, u8 *src, int ypos, int xpos, int height, int width) ++void splash_putcs(struct vc_data *vc, struct fb_info *info, ++ const unsigned short *s, int count, int ypos, int xpos) ++{ ++ struct splash_data *sd; ++ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; ++ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; ++ union pt src; ++ union pt dst, splashsrc; ++ unsigned int d, x, y; ++ u32 dd, fgx, bgx; ++ u16 c = scr_readw(s); ++ int fg_color, bg_color, transparent; ++ int n; ++ int octpp = (info->var.bits_per_pixel + 1) >> 3; ++ ++ if (!oops_in_progress && (console_blanked || info->splash_data->splash_dosilent)) ++ return; ++ sd = info->splash_data; ++ ++ fg_color = attr_fgcol(fgshift, c); ++ bg_color = attr_bgcol(bgshift, c); ++ transparent = sd->splash_color == bg_color; ++ xpos = xpos * vc->vc_font.width + sd->splash_text_xo; ++ ypos = ypos * vc->vc_font.height + sd->splash_text_yo; ++ splashsrc.ub = (u8 *)(info->splash_pic + ypos * info->splash_pic_stride + xpos * octpp); ++ dst.ub = (u8 *)(info->screen_base + ypos * info->fix.line_length + xpos * octpp); ++ fgx = ((u32 *)info->pseudo_palette)[fg_color]; ++ if (transparent && sd->splash_color == 15) { ++ if (fgx == 0xffea) ++ fgx = 0xfe4a; ++ else if (fgx == 0x57ea) ++ fgx = 0x0540; ++ else if (fgx == 0xffff) ++ fgx = 0x52aa; ++ } ++ bgx = ((u32 *)info->pseudo_palette)[bg_color]; ++ d = 0; ++ while (count--) { ++ c = scr_readw(s++); ++ src.ub = vc->vc_font.data + (c & charmask) * vc->vc_font.height * ((vc->vc_font.width + 7) >> 3); ++ for (y = 0; y < vc->vc_font.height; y++) { ++ for (x = 0; x < vc->vc_font.width; ) { ++ if ((x & 7) == 0) ++ d = *src.ub++; ++ switch (octpp) { ++ case 2: ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? *splashsrc.us : bgx; ++ splashsrc.us += 1; ++ if (d & 0x40) ++ dd |= fgx << 16; ++ else ++ dd |= (transparent ? *splashsrc.us : bgx) << 16; ++ splashsrc.us += 1; ++ d <<= 2; ++ x += 2; ++ fb_writel(dd, dst.ul); ++ dst.ul += 1; ++ break; ++ case 3: ++ for (n = 0; n <= 16; n += 8) { ++ if (d & 0x80) ++ dd = (fgx >> n) && 0xff; ++ else ++ dd = (transparent ? *splashsrc.ul : ((bgx >> n) & 0xff) ); ++ splashsrc.ub += 1; ++ fb_writeb(dd, dst.ub); ++ dst.ub += 1; ++ } ++ d <<= 1; ++ x += 1; ++ break; ++ case 4: ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = (transparent ? *splashsrc.ul : bgx); ++ splashsrc.ul += 1; ++ d <<= 1; ++ x += 1; ++ fb_writel(dd, dst.ul); ++ dst.ul += 1; ++ break; ++ } ++ } ++ dst.ub += info->fix.line_length - vc->vc_font.width * octpp; ++ splashsrc.ub += info->splash_pic_stride - vc->vc_font.width * octpp; ++ } ++ dst.ub -= info->fix.line_length * vc->vc_font.height - vc->vc_font.width * octpp; ++ splashsrc.ub -= info->splash_pic_stride * vc->vc_font.height - vc->vc_font.width * octpp; ++ } ++} ++ ++static void splash_renderc(struct fb_info *info, ++ int fg_color, int bg_color, ++ u8 *src, ++ int ypos, int xpos, ++ int height, int width) + { +- int transparent = sd->splash_color == bg_color; ++ struct splash_data *sd; ++ int transparent; + u32 dd, fgx, bgx; +- u8 *dst, *splashsrc; ++ union pt dst, splashsrc; + unsigned int d, x, y; ++ int n; ++ int octpp = (info->var.bits_per_pixel + 1) >> 3; + +- if (console_blanked) +- return; +- splashsrc = (u8 *)(info->splash_pic + ypos * info->splash_bytes + xpos * 2); +- dst = (u8 *)(info->screen_base + ypos * info->fix.line_length + xpos * 2); ++ if (!oops_in_progress && (console_blanked || info->splash_data->splash_dosilent)) ++ return; ++ ++ sd = info->splash_data; ++ ++ transparent = sd->splash_color == bg_color; ++ splashsrc.ub = (u8*)(info->splash_pic + ypos * info->splash_pic_stride + xpos * octpp); ++ dst.ub = (u8*)(info->screen_base + ypos * info->fix.line_length + xpos * octpp); + fgx = ((u32 *)info->pseudo_palette)[fg_color]; + if (transparent && sd->splash_color == 15) { + if (fgx == 0xffea) +@@ -101,93 +150,136 @@ static void splash_renderc(struct splash + bgx = ((u32 *)info->pseudo_palette)[bg_color]; + d = 0; + for (y = 0; y < height; y++) { +- for (x = 0; x < width; x += 2) { +- if ((x & 7) == 0) +- d = *src++; +- if (d & 0x80) +- dd = fgx; +- else +- dd = transparent ? *(u16 *)splashsrc : bgx; +- splashsrc += 2; +- if (d & 0x40) +- dd |= fgx << 16; +- else +- dd |= (transparent ? *(u16 *)splashsrc : bgx) << 16; +- splashsrc += 2; +- d <<= 2; +- fb_writel(dd, dst); +- dst += 4; +- } +- dst += info->fix.line_length - width * 2; +- splashsrc += info->splash_bytes - width * 2; ++ for (x = 0; x < width; ) { ++ if ((x & 7) == 0) ++ d = *src++; ++ switch (octpp) { ++ case 2: ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? *splashsrc.us : bgx; ++ splashsrc.us += 1; ++ if (d & 0x40) ++ dd |= fgx << 16; ++ else ++ dd |= (transparent ? *splashsrc.us : bgx) << 16; ++ splashsrc.us += 1; ++ d <<= 2; ++ x += 2; ++ fb_writel(dd, dst.ul); ++ dst.ul += 1; ++ break; ++ case 3: ++ for (n = 0; n <= 16; n += 8) { ++ if (d & 0x80) ++ dd = (fgx >> n) & 0xff; ++ else ++ dd = transparent ? *splashsrc.ub : bgx; ++ splashsrc.ub += 1; ++ fb_writeb(dd, dst.ub); ++ dst.ub += 1; ++ } ++ d <<= 1; ++ x += 1; ++ break; ++ case 4: ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? *splashsrc.ul : bgx; ++ splashsrc.ul += 1; ++ d <<= 1; ++ x += 1; ++ fb_writel(dd, dst.ul); ++ dst.ul += 1; ++ break; ++ } ++ } ++ dst.ub += info->fix.line_length - width * octpp; ++ splashsrc.ub += info->splash_pic_stride - width * octpp; + } + } + +-void splash_putc(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, +- int c, int ypos, int xpos) +-{ +- unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; +- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; +- int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; +- u8 *src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * ((vc->vc_font.width + 7) >> 3); +- xpos = xpos * vc->vc_font.width + sd->splash_text_xo; +- ypos = ypos * vc->vc_font.height + sd->splash_text_yo; +- splash_renderc(sd, info, attr_fgcol(fgshift, c), attr_bgcol(bgshift, c), src, ypos, xpos, vc->vc_font.height, vc->vc_font.width); +-} +- +-void splashcopy(u8 *dst, u8 *src, int height, int width, int dstbytes, int srcbytes) ++void splashcopy(u8 *dst, u8 *src, int height, int width, int dstbytes, int srcbytes, int octpp) + { + int i; + ++ width *= octpp; + while (height-- > 0) { +- u32 *p = (u32 *)dst; +- u32 *q = (u32 *)src; +- for (i=0; i < width/4; i++) { +- fb_writel(*q++,p++); +- fb_writel(*q++,p++); +- } +- if (width & 2) +- fb_writel(*q++,p++); +- if (width & 1) +- fb_writew(*(u16*)q,(u16*)p); +- dst += dstbytes; +- src += srcbytes; ++ union pt p, q; ++ p.ul = (u32 *)dst; ++ q.ul = (u32 *)src; ++ for (i=0; i < width/8; i++) { ++ fb_writel(*q.ul++,p.ul++); ++ fb_writel(*q.ul++,p.ul++); ++ } ++ if (width & 4) ++ fb_writel(*q.ul++,p.ul++); ++ if (width & 2) ++ fb_writew(*q.us++,p.us++); ++ if (width & 1) ++ fb_writeb(*q.ub,p.ub); ++ dst += dstbytes; ++ src += srcbytes; + } + } + +-static void splashset(u8 *dst, int height, int width, int dstbytes, u32 bgx) { ++static void splashset(u8 *dst, int height, int width, int dstbytes, u32 bgx, int octpp) { + int i; + +- bgx |= bgx << 16; ++ width *= octpp; ++ if (octpp == 2) ++ bgx |= bgx << 16; + while (height-- > 0) { +- u32 *p = (u32 *)dst; +- for (i=0; i < width/4; i++) { +- fb_writel(bgx,p++); +- fb_writel(bgx,p++); +- } +- if (width & 2) +- fb_writel(bgx,p++); +- if (width & 1) +- fb_writew(bgx,(u16*)p); +- dst += dstbytes; ++ union pt p; ++ p.ul = (u32 *)dst; ++ if (octpp != 3) { ++ for (i=0; i < width/8; i++) { ++ fb_writel(bgx,p.ul++); ++ fb_writel(bgx,p.ul++); ++ } ++ if (width & 4) ++ fb_writel(bgx,p.ul++); ++ if (width & 2) ++ fb_writew(bgx,p.us++); ++ if (width & 1) ++ fb_writeb(bgx,p.ub); ++ dst += dstbytes; ++ } else { /* slow! */ ++ for (i=0; i < width; i++) ++ fb_writeb((bgx >> ((i & 0x3) * 8)) && 0xff,p.ub++); ++ } + } + } + + static void splashfill(struct fb_info *info, int sy, int sx, int height, int width) { +- splashcopy((u8 *)(info->screen_base + sy * info->fix.line_length + sx * 2), (u8 *)(info->splash_pic + sy * info->splash_bytes + sx * 2), height, width, info->fix.line_length, info->splash_bytes); ++ int octpp = (info->var.bits_per_pixel + 1) >> 3; ++ ++ splashcopy((u8 *)(info->screen_base + sy * info->fix.line_length + sx * octpp), ++ (u8 *)(info->splash_pic + sy * info->splash_pic_stride + sx * octpp), ++ height, width, info->fix.line_length, info->splash_pic_stride, ++ octpp); + } + +-void splash_clear(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int sy, ++void splash_clear(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int height, int width) + { ++ struct splash_data *sd; + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; + int bg_color = attr_bgcol_ec(bgshift, vc, info); +- int transparent = sd->splash_color == bg_color; ++ int transparent; ++ int octpp = (info->var.bits_per_pixel + 1) >> 3; + u32 bgx; + u8 *dst; + +- if (console_blanked) +- return; ++ if (!oops_in_progress && (console_blanked || info->splash_data->splash_dosilent)) ++ return; ++ ++ sd = info->splash_data; ++ ++ transparent = sd->splash_color == bg_color; ++ + sy = sy * vc->vc_font.height + sd->splash_text_yo; + sx = sx * vc->vc_font.width + sd->splash_text_xo; + height *= vc->vc_font.height; +@@ -196,18 +288,26 @@ void splash_clear(struct splash_data *sd + splashfill(info, sy, sx, height, width); + return; + } +- dst = (u8 *)(info->screen_base + sy * info->fix.line_length + sx * 2); ++ dst = (u8 *)(info->screen_base + sy * info->fix.line_length + sx * octpp); + bgx = ((u32 *)info->pseudo_palette)[bg_color]; +- splashset(dst, height, width, info->fix.line_length, bgx); ++ splashset(dst, ++ height, width, ++ info->fix.line_length, ++ bgx, ++ (info->var.bits_per_pixel + 1) >> 3); + } + +-void splash_bmove(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int sy, ++void splash_bmove(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int dy, int dx, int height, int width) + { ++ struct splash_data *sd; + struct fb_copyarea area; + +- if (console_blanked) +- return; ++ if (!oops_in_progress && (console_blanked || info->splash_data->splash_dosilent)) ++ return; ++ ++ sd = info->splash_data; ++ + area.sx = sx * vc->vc_font.width; + area.sy = sy * vc->vc_font.height; + area.dx = dx * vc->vc_font.width; +@@ -222,34 +322,57 @@ void splash_bmove(struct splash_data *sd + info->fbops->fb_copyarea(info, &area); + } + +-void splash_clear_margins(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++void splash_clear_margins(struct vc_data *vc, struct fb_info *info, + int bottom_only) + { ++ struct splash_data *sd; + unsigned int tw = vc->vc_cols*vc->vc_font.width; + unsigned int th = vc->vc_rows*vc->vc_font.height; ++ SPLASH_DEBUG(); ++ ++ if (!oops_in_progress && (console_blanked || info->splash_data->splash_dosilent)) ++ return; ++ ++ sd = info->splash_data; + +- if (console_blanked) +- return; + if (!bottom_only) { + /* top margin */ +- splashfill(info, 0, 0, sd->splash_text_yo, info->var.xres); ++ splashfill(info, ++ 0, ++ 0, ++ sd->splash_text_yo, ++ info->var.xres); + /* left margin */ +- splashfill(info, sd->splash_text_yo, 0, th, sd->splash_text_xo); ++ splashfill(info, ++ sd->splash_text_yo, ++ 0, ++ th, ++ sd->splash_text_xo); + /* right margin */ +- splashfill(info, sd->splash_text_yo, sd->splash_text_xo + tw, th, info->var.xres - sd->splash_text_xo - tw); +- ++ splashfill(info, ++ sd->splash_text_yo, ++ sd->splash_text_xo + tw, ++ th, ++ info->var.xres - sd->splash_text_xo - tw); + } +- splashfill(info, sd->splash_text_yo + th, 0, info->var.yres - sd->splash_text_yo - th, info->var.xres); ++ splashfill(info, ++ sd->splash_text_yo + th, ++ 0, ++ info->var.yres - sd->splash_text_yo - th, ++ info->var.xres); + } + +-int splash_cursor(struct splash_data *sd, struct fb_info *info, struct fb_cursor *cursor) ++int splash_cursor(struct fb_info *info, struct fb_cursor *cursor) + { ++ struct splash_data *sd; + int i; + unsigned int dsize, s_pitch; + + if (info->state != FBINFO_STATE_RUNNING) + return 0; + ++ sd = info->splash_data; ++ + s_pitch = (cursor->image.width + 7) >> 3; + dsize = s_pitch * cursor->image.height; + if (cursor->enable) { +@@ -267,13 +390,15 @@ int splash_cursor(struct splash_data *sd + } else if (info->fb_cursordata != cursor->image.data) + memcpy(info->fb_cursordata, cursor->image.data, dsize); + cursor->image.data = info->fb_cursordata; +- splash_renderc(sd, info, cursor->image.fg_color, cursor->image.bg_color, (u8 *)info->fb_cursordata, cursor->image.dy + sd->splash_text_yo, cursor->image.dx + sd->splash_text_xo, cursor->image.height, cursor->image.width); ++ splash_renderc(info, cursor->image.fg_color, cursor->image.bg_color, (u8 *)info->fb_cursordata, cursor->image.dy + sd->splash_text_yo, cursor->image.dx + sd->splash_text_xo, cursor->image.height, cursor->image.width); + return 0; + } + +-void splash_bmove_redraw(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) ++void splash_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) + { +- unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * y + dx * 2); ++ struct splash_data *sd; ++ int octpp = (info->var.bits_per_pixel + 1) >> 3; ++ unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * y + dx * octpp); + unsigned short *s = d + (dx - sx); + unsigned short *start = d; + unsigned short *ls = d; +@@ -282,21 +407,24 @@ void splash_bmove_redraw(struct splash_d + int x = dx; + unsigned short attr = 1; + +- if (console_blanked) ++ if (console_blanked || info->splash_data->splash_dosilent) + return; ++ ++ sd = info->splash_data; ++ + do { + c = scr_readw(d); + if (attr != (c & 0xff00)) { + attr = c & 0xff00; + if (d > start) { +- splash_putcs(sd, vc, info, start, d - start, y, x); ++ splash_putcs(vc, info, start, d - start, y, x); + x += d - start; + start = d; + } + } + if (s >= ls && s < le && c == scr_readw(s)) { + if (d > start) { +- splash_putcs(sd, vc, info, start, d - start, y, x); ++ splash_putcs(vc, info, start, d - start, y, x); + x += d - start + 1; + start = d + 1; + } else { +@@ -308,21 +436,22 @@ void splash_bmove_redraw(struct splash_d + d++; + } while (d < le); + if (d > start) +- splash_putcs(sd, vc, info, start, d - start, y, x); ++ splash_putcs(vc, info, start, d - start, y, x); + } + +-void splash_blank(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int blank) ++void splash_blank(struct vc_data *vc, struct fb_info *info, int blank) + { ++ SPLASH_DEBUG(); + if (blank) { +- if (info->silent_screen_base) +- splashset((u8 *)info->silent_screen_base, info->var.yres, info->var.xres, info->fix.line_length, 0); +- splashset((u8 *)info->screen_base, info->var.yres, info->var.xres, info->fix.line_length, 0); ++ splashset((u8 *)info->screen_base, ++ info->var.yres, info->var.xres, ++ info->fix.line_length, ++ 0, ++ (info->var.bits_per_pixel + 1) >> 3); + } else { +- if (info->silent_screen_base) +- splash_prepare(vc, info); +- splash_clear_margins(vc->vc_splash_data, vc, info, 0); ++ // splash_prepare(vc, info); /* do we really need this? */ ++ splash_clear_margins(vc, info, 0); + /* no longer needed, done in fbcon_blank */ + /* update_screen(vc->vc_num); */ + } + } +- +--- a/drivers/video/console/bitblit.c ++++ b/drivers/video/console/bitblit.c +@@ -52,7 +52,7 @@ static void bit_bmove(struct vc_data *vc + + #ifdef CONFIG_BOOTSPLASH + if (info->splash_data) { +- splash_bmove(info->splash_data, vc, info, ++ splash_bmove(vc, info, + sy, sx, dy, dx, height, width); + return; + } +@@ -75,8 +75,8 @@ static void bit_clear(struct vc_data *vc + + #ifdef CONFIG_BOOTSPLASH + if (info->splash_data) { +- splash_clear(info->splash_data, vc, info, +- sy, sx, height, width); ++ splash_clear(vc, info, ++ sy, sx, height, width); + return; + } + #endif +@@ -179,7 +179,7 @@ static void bit_putcs(struct vc_data *vc + + #ifdef CONFIG_BOOTSPLASH + if (info->splash_data) { +- splash_putcs(info->splash_data, vc, info, s, count, yy, xx); ++ splash_putcs(vc, info, s, count, yy, xx); + return; + } + #endif +@@ -239,7 +239,7 @@ static void bit_clear_margins(struct vc_ + + #ifdef CONFIG_BOOTSPLASH + if (info->splash_data) { +- splash_clear_margins(info->splash_data, vc, info, bottom_only); ++ splash_clear_margins(vc, info, bottom_only); + return; + } + #endif +@@ -412,7 +412,7 @@ static void bit_cursor(struct vc_data *v + + #ifdef CONFIG_BOOTSPLASH + if (info->splash_data) { +- splash_cursor(info->splash_data, info, &cursor); ++ splash_cursor(info, &cursor); + ops->cursor_reset = 0; + return; + } +--- a/drivers/video/console/fbcon.c ++++ b/drivers/video/console/fbcon.c +@@ -2072,7 +2072,7 @@ static void fbcon_bmove_rec(struct vc_da + #ifdef CONFIG_BOOTSPLASH + if (info->splash_data && sy == dy && height == 1) { + /* must use slower redraw bmove to keep background pic intact */ +- splash_bmove_redraw(info->splash_data, vc, info, sy, sx, dx, width); ++ splash_bmove_redraw(vc, info, sy, sx, dx, width); + return; + } + #endif +@@ -2323,7 +2323,7 @@ static void fbcon_generic_blank(struct v + + #ifdef CONFIG_BOOTSPLASH + if (info->splash_data) { +- splash_blank(info->splash_data, vc, info, blank); ++ splash_blank(vc, info, blank); + return; + } + #endif +--- a/drivers/video/vesafb.c ++++ b/drivers/video/vesafb.c +@@ -182,10 +182,7 @@ static void vesafb_destroy(struct fb_inf + framebuffer_release(info); + } + +-#ifndef CONFIG_BOOTSPLASH +-static +-#endif +-struct fb_ops vesafb_ops = { ++static struct fb_ops vesafb_ops = { + .owner = THIS_MODULE, + .fb_destroy = vesafb_destroy, + .fb_setcolreg = vesafb_setcolreg, +@@ -270,9 +267,6 @@ static int __devinit vesafb_probe(struct + * option to simply use size_total as that + * wastes plenty of kernel address space. */ + size_remap = size_vmode * 2; +-#ifdef CONFIG_BOOTSPLASH +- size_remap *= 2; /* some more for the images */ +-#endif + if (vram_remap) + size_remap = vram_remap * 1024 * 1024; + if (size_remap < size_vmode) +--- a/include/linux/fb.h ++++ b/include/linux/fb.h +@@ -863,8 +863,7 @@ struct fb_info { + struct splash_data *splash_data; + unsigned char *splash_pic; + int splash_pic_size; +- int splash_bytes; +- char *silent_screen_base; /* real screen base */ ++ int splash_pic_stride; + char fb_cursordata[64]; + #endif + /* we need the PCI or similiar aperture base/size not diff --git a/patches.suse/SoN-01-mm-setup_per_zone_wmarks.patch b/patches.suse/SoN-01-mm-setup_per_zone_wmarks.patch new file mode 100644 index 0000000..c5b97b8 --- /dev/null +++ b/patches.suse/SoN-01-mm-setup_per_zone_wmarks.patch @@ -0,0 +1,65 @@ +From: Peter Zijlstra +Subject: [PATCH 01/31] mm: serialize access to min_free_kbytes +Patch-mainline: not yet + +There is a small race between the procfs caller and the memory hotplug caller +of setup_per_zone_wmarks(). Not a big deal, but the next patch will add yet +another caller. Time to close the gap. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + mm/page_alloc.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -148,6 +148,7 @@ static char * const zone_names[MAX_NR_ZO + "Movable", + }; + ++static DEFINE_SPINLOCK(min_free_lock); + int min_free_kbytes = 1024; + + static unsigned long __meminitdata nr_kernel_pages; +@@ -4609,13 +4610,13 @@ static void setup_per_zone_lowmem_reserv + } + + /** +- * setup_per_zone_wmarks - called when min_free_kbytes changes ++ * __setup_per_zone_wmarks - called when min_free_kbytes changes + * or when memory is hot-{added|removed} + * + * Ensures that the watermark[min,low,high] values for each zone are set + * correctly with respect to min_free_kbytes. + */ +-void setup_per_zone_wmarks(void) ++static void __setup_per_zone_wmarks(void) + { + unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); + unsigned long lowmem_pages = 0; +@@ -4713,6 +4714,15 @@ static void __init setup_per_zone_inacti + calculate_zone_inactive_ratio(zone); + } + ++void setup_per_zone_wmarks(void) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&min_free_lock, flags); ++ __setup_per_zone_wmarks(); ++ spin_unlock_irqrestore(&min_free_lock, flags); ++} ++ + /* + * Initialise min_free_kbytes. + * +@@ -4748,7 +4758,7 @@ static int __init init_per_zone_wmark_mi + min_free_kbytes = 128; + if (min_free_kbytes > 65536) + min_free_kbytes = 65536; +- setup_per_zone_wmarks(); ++ __setup_per_zone_wmarks(); + setup_per_zone_lowmem_reserve(); + setup_per_zone_inactive_ratio(); + return 0; diff --git a/patches.suse/SoN-02-doc.patch b/patches.suse/SoN-02-doc.patch new file mode 100644 index 0000000..69fbacc --- /dev/null +++ b/patches.suse/SoN-02-doc.patch @@ -0,0 +1,286 @@ +From: Neil Brown +Subject: [PATCH 02/31] swap over network documentation +Patch-mainline: not yet + +Document describing the problem and proposed solution + +Signed-off-by: Peter Zijlstra +Signed-off-by: Neil Brown +Signed-off-by: Suresh Jayaraman +--- + Documentation/network-swap.txt | 270 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 270 insertions(+) + +--- /dev/null ++++ b/Documentation/network-swap.txt +@@ -0,0 +1,270 @@ ++ ++Problem: ++ When Linux needs to allocate memory it may find that there is ++ insufficient free memory so it needs to reclaim space that is in ++ use but not needed at the moment. There are several options: ++ ++ 1/ Shrink a kernel cache such as the inode or dentry cache. This ++ is fairly easy but provides limited returns. ++ 2/ Discard 'clean' pages from the page cache. This is easy, and ++ works well as long as there are clean pages in the page cache. ++ Similarly clean 'anonymous' pages can be discarded - if there ++ are any. ++ 3/ Write out some dirty page-cache pages so that they become clean. ++ The VM limits the number of dirty page-cache pages to e.g. 40% ++ of available memory so that (among other reasons) a "sync" will ++ not take excessively long. So there should never be excessive ++ amounts of dirty pagecache. ++ Writing out dirty page-cache pages involves work by the ++ filesystem which may need to allocate memory itself. To avoid ++ deadlock, filesystems use GFP_NOFS when allocating memory on the ++ write-out path. When this is used, cleaning dirty page-cache ++ pages is not an option so if the filesystem finds that memory ++ is tight, another option must be found. ++ 4/ Write out dirty anonymous pages to the "Swap" partition/file. ++ This is the most interesting for a couple of reasons. ++ a/ Unlike dirty page-cache pages, there is no need to write anon ++ pages out unless we are actually short of memory. Thus they ++ tend to be left to last. ++ b/ Anon pages tend to be updated randomly and unpredictably, and ++ flushing them out of memory can have a very significant ++ performance impact on the process using them. This contrasts ++ with page-cache pages which are often written sequentially ++ and often treated as "write-once, read-many". ++ So anon pages tend to be left until last to be cleaned, and may ++ be the only cleanable pages while there are still some dirty ++ page-cache pages (which are waiting on a GFP_NOFS allocation). ++ ++[I don't find the above wholly satisfying. There seems to be too much ++ hand-waving. If someone can provide better text explaining why ++ swapout is a special case, that would be great.] ++ ++So we need to be able to write to the swap file/partition without ++needing to allocate any memory ... or only a small well controlled ++amount. ++ ++The VM reserves a small amount of memory that can only be allocated ++for use as part of the swap-out procedure. It is only available to ++processes with the PF_MEMALLOC flag set, which is typically just the ++memory cleaner. ++ ++Traditionally swap-out is performed directly to block devices (swap ++files on block-device filesystems are supported by examining the ++mapping from file offset to device offset in advance, and then using ++the device offsets to write directly to the device). Block devices ++are (required to be) written to pre-allocate any memory that might be ++needed during write-out, and to block when the pre-allocated memory is ++exhausted and no other memory is available. They can be sure not to ++block forever as the pre-allocated memory will be returned as soon as ++the data it is being used for has been written out. The primary ++mechanism for pre-allocating memory is called "mempools". ++ ++This approach does not work for writing anonymous pages ++(i.e. swapping) over a network, using e.g NFS or NBD or iSCSI. ++ ++ ++The main reason that it does not work is that when data from an anon ++page is written to the network, we must wait for a reply to confirm ++the data is safe. Receiving that reply will consume memory and, ++significantly, we need to allocate memory to an incoming packet before ++we can tell if it is the reply we are waiting for or not. ++ ++The secondary reason is that the network code is not written to use ++mempools and in most cases does not need to use them. Changing all ++allocations in the networking layer to use mempools would be quite ++intrusive, and would waste memory, and probably cause a slow-down in ++the common case of not swapping over the network. ++ ++These problems are addressed by enhancing the system of memory ++reserves used by PF_MEMALLOC and requiring any in-kernel networking ++client that is used for swap-out to indicate which sockets are used ++for swapout so they can be handled specially in low memory situations. ++ ++There are several major parts to this enhancement: ++ ++1/ page->reserve, GFP_MEMALLOC ++ ++ To handle low memory conditions we need to know when those ++ conditions exist. Having a global "low on memory" flag seems easy, ++ but its implementation is problematic. Instead we make it possible ++ to tell if a recent memory allocation required use of the emergency ++ memory pool. ++ For pages returned by alloc_page, the new page->reserve flag ++ can be tested. If this is set, then a low memory condition was ++ current when the page was allocated, so the memory should be used ++ carefully. (Because low memory conditions are transient, this ++ state is kept in an overloaded member instead of in page flags, which ++ would suggest a more permanent state.) ++ ++ For memory allocated using slab/slub: If a page that is added to a ++ kmem_cache is found to have page->reserve set, then a s->reserve ++ flag is set for the whole kmem_cache. Further allocations will only ++ be returned from that page (or any other page in the cache) if they ++ are emergency allocation (i.e. PF_MEMALLOC or GFP_MEMALLOC is set). ++ Non-emergency allocations will block in alloc_page until a ++ non-reserve page is available. Once a non-reserve page has been ++ added to the cache, the s->reserve flag on the cache is removed. ++ ++ Because slab objects have no individual state its hard to pass ++ reserve state along, the current code relies on a regular alloc ++ failing. There are various allocation wrappers help here. ++ ++ This allows us to ++ a/ request use of the emergency pool when allocating memory ++ (GFP_MEMALLOC), and ++ b/ to find out if the emergency pool was used. ++ ++2/ SK_MEMALLOC, sk_buff->emergency. ++ ++ When memory from the reserve is used to store incoming network ++ packets, the memory must be freed (and the packet dropped) as soon ++ as we find out that the packet is not for a socket that is used for ++ swap-out. ++ To achieve this we have an ->emergency flag for skbs, and an ++ SK_MEMALLOC flag for sockets. ++ When memory is allocated for an skb, it is allocated with ++ GFP_MEMALLOC (if we are currently swapping over the network at ++ all). If a subsequent test shows that the emergency pool was used, ++ ->emergency is set. ++ When the skb is finally attached to its destination socket, the ++ SK_MEMALLOC flag on the socket is tested. If the skb has ++ ->emergency set, but the socket does not have SK_MEMALLOC set, then ++ the skb is immediately freed and the packet is dropped. ++ This ensures that reserve memory is never queued on a socket that is ++ not used for swapout. ++ ++ Similarly, if an skb is ever queued for delivery to user-space for ++ example by netfilter, the ->emergency flag is tested and the skb is ++ released if ->emergency is set. (so obviously the storage route may ++ not pass through a userspace helper, otherwise the packets will never ++ arrive and we'll deadlock) ++ ++ This ensures that memory from the emergency reserve can be used to ++ allow swapout to proceed, but will not get caught up in any other ++ network queue. ++ ++ ++3/ pages_emergency ++ ++ The above would be sufficient if the total memory below the lowest ++ memory watermark (i.e the size of the emergency reserve) were known ++ to be enough to hold all transient allocations needed for writeout. ++ I'm a little blurry on how big the current emergency pool is, but it ++ isn't big and certainly hasn't been sized to allow network traffic ++ to consume any. ++ ++ We could simply make the size of the reserve bigger. However in the ++ common case that we are not swapping over the network, that would be ++ a waste of memory. ++ ++ So a new "watermark" is defined: pages_emergency. This is ++ effectively added to the current low water marks, so that pages from ++ this emergency pool can only be allocated if one of PF_MEMALLOC or ++ GFP_MEMALLOC are set. ++ ++ pages_emergency can be changed dynamically based on need. When ++ swapout over the network is required, pages_emergency is increased ++ to cover the maximum expected load. When network swapout is ++ disabled, pages_emergency is decreased. ++ ++ To determine how much to increase it by, we introduce reservation ++ groups.... ++ ++3a/ reservation groups ++ ++ The memory used transiently for swapout can be in a number of ++ different places. e.g. the network route cache, the network ++ fragment cache, in transit between network card and socket, or (in ++ the case of NFS) in sunrpc data structures awaiting a reply. ++ We need to ensure each of these is limited in the amount of memory ++ they use, and that the maximum is included in the reserve. ++ ++ The memory required by the network layer only needs to be reserved ++ once, even if there are multiple swapout paths using the network ++ (e.g. NFS and NDB and iSCSI, though using all three for swapout at ++ the same time would be unusual). ++ ++ So we create a tree of reservation groups. The network might ++ register a collection of reservations, but not mark them as being in ++ use. NFS and sunrpc might similarly register a collection of ++ reservations, and attach it to the network reservations as it ++ depends on them. ++ When swapout over NFS is requested, the NFS/sunrpc reservations are ++ activated which implicitly activates the network reservations. ++ ++ The total new reservation is added to pages_emergency. ++ ++ Provided each memory usage stays beneath the registered limit (at ++ least when allocating memory from reserves), the system will never ++ run out of emergency memory, and swapout will not deadlock. ++ ++ It is worth noting here that it is not critical that each usage ++ stays beneath the limit 100% of the time. Occasional excess is ++ acceptable provided that the memory will be freed again within a ++ short amount of time that does *not* require waiting for any event ++ that itself might require memory. ++ This is because, at all stages of transmit and receive, it is ++ acceptable to discard all transient memory associated with a ++ particular writeout and try again later. On transmit, the page can ++ be re-queued for later transmission. On receive, the packet can be ++ dropped assuming that the peer will resend after a timeout. ++ ++ Thus allocations that are truly transient and will be freed without ++ blocking do not strictly need to be reserved for. Doing so might ++ still be a good idea to ensure forward progress doesn't take too ++ long. ++ ++4/ low-mem accounting ++ ++ Most places that might hold on to emergency memory (e.g. route ++ cache, fragment cache etc) already place a limit on the amount of ++ memory that they can use. This limit can simply be reserved using ++ the above mechanism and no more needs to be done. ++ ++ However some memory usage might not be accounted with sufficient ++ firmness to allow an appropriate emergency reservation. The ++ in-flight skbs for incoming packets is one such example. ++ ++ To support this, a low-overhead mechanism for accounting memory ++ usage against the reserves is provided. This mechanism uses the ++ same data structure that is used to store the emergency memory ++ reservations through the addition of a 'usage' field. ++ ++ Before we attempt allocation from the memory reserves, we much check ++ if the resulting 'usage' is below the reservation. If so, we increase ++ the usage and attempt the allocation (which should succeed). If ++ the projected 'usage' exceeds the reservation we'll either fail the ++ allocation, or wait for 'usage' to decrease enough so that it would ++ succeed, depending on __GFP_WAIT. ++ ++ When memory that was allocated for that purpose is freed, the ++ 'usage' field is checked again. If it is non-zero, then the size of ++ the freed memory is subtracted from the usage, making sure the usage ++ never becomes less than zero. ++ ++ This provides adequate accounting with minimal overheads when not in ++ a low memory condition. When a low memory condition is encountered ++ it does add the cost of a spin lock necessary to serialise updates ++ to 'usage'. ++ ++ ++ ++5/ swapon/swapoff/swap_out/swap_in ++ ++ So that a filesystem (e.g. NFS) can know when to set SK_MEMALLOC on ++ any network socket that it uses, and can know when to account ++ reserve memory carefully, new address_space_operations are ++ available. ++ "swapon" requests that an address space (i.e a file) be make ready ++ for swapout. swap_out and swap_in request the actual IO. They ++ together must ensure that each swap_out request can succeed without ++ allocating more emergency memory that was reserved by swapon. swapoff ++ is used to reverse the state changes caused by swapon when we disable ++ the swap file. ++ ++ ++Thanks for reading this far. I hope it made sense :-) ++ ++Neil Brown (with updates from Peter Zijlstra) ++ ++ diff --git a/patches.suse/SoN-03-mm-gfp-to-alloc_flags-expose.patch b/patches.suse/SoN-03-mm-gfp-to-alloc_flags-expose.patch new file mode 100644 index 0000000..812df3b --- /dev/null +++ b/patches.suse/SoN-03-mm-gfp-to-alloc_flags-expose.patch @@ -0,0 +1,70 @@ +From: Peter Zijlstra +Subject: [PATCH 03/31] mm: expose gfp_to_alloc_flags() +Patch-mainline: not yet + +Expose the gfp to alloc_flags mapping, so we can use it in other parts +of the vm. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + mm/internal.h | 15 +++++++++++++++ + mm/page_alloc.c | 16 +--------------- + 2 files changed, 16 insertions(+), 15 deletions(-) + +--- a/mm/internal.h ++++ b/mm/internal.h +@@ -185,6 +185,21 @@ static inline struct page *mem_map_next( + #define __paginginit __init + #endif + ++/* The ALLOC_WMARK bits are used as an index to zone->watermark */ ++#define ALLOC_WMARK_MIN WMARK_MIN ++#define ALLOC_WMARK_LOW WMARK_LOW ++#define ALLOC_WMARK_HIGH WMARK_HIGH ++#define ALLOC_NO_WATERMARKS 0x04 /* don't check watermarks at all */ ++ ++/* Mask to get the watermark bits */ ++#define ALLOC_WMARK_MASK (ALLOC_NO_WATERMARKS-1) ++ ++#define ALLOC_HARDER 0x10 /* try to alloc harder */ ++#define ALLOC_HIGH 0x20 /* __GFP_HIGH set */ ++#define ALLOC_CPUSET 0x40 /* check for correct cpuset */ ++ ++int gfp_to_alloc_flags(gfp_t gfp_mask); ++ + /* Memory initialisation debug and verification */ + enum mminit_level { + MMINIT_WARNING, +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1261,19 +1261,6 @@ failed: + return NULL; + } + +-/* The ALLOC_WMARK bits are used as an index to zone->watermark */ +-#define ALLOC_WMARK_MIN WMARK_MIN +-#define ALLOC_WMARK_LOW WMARK_LOW +-#define ALLOC_WMARK_HIGH WMARK_HIGH +-#define ALLOC_NO_WATERMARKS 0x04 /* don't check watermarks at all */ +- +-/* Mask to get the watermark bits */ +-#define ALLOC_WMARK_MASK (ALLOC_NO_WATERMARKS-1) +- +-#define ALLOC_HARDER 0x10 /* try to alloc harder */ +-#define ALLOC_HIGH 0x20 /* __GFP_HIGH set */ +-#define ALLOC_CPUSET 0x40 /* check for correct cpuset */ +- + #ifdef CONFIG_FAIL_PAGE_ALLOC + + static struct fail_page_alloc_attr { +@@ -1768,8 +1755,7 @@ void wake_all_kswapd(unsigned int order, + wakeup_kswapd(zone, order); + } + +-static inline int +-gfp_to_alloc_flags(gfp_t gfp_mask) ++int gfp_to_alloc_flags(gfp_t gfp_mask) + { + struct task_struct *p = current; + int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET; diff --git a/patches.suse/SoN-04-page_alloc-reserve.patch b/patches.suse/SoN-04-page_alloc-reserve.patch new file mode 100644 index 0000000..efd1381 --- /dev/null +++ b/patches.suse/SoN-04-page_alloc-reserve.patch @@ -0,0 +1,43 @@ +From: Peter Zijlstra +Subject: [PATCH 04/31] mm: tag reseve pages +Patch-mainline: not yet + +Tag pages allocated from the reserves with a non-zero page->reserve. +This allows us to distinguish and account reserve pages. + +Since low-memory situations are transient, and unrelated the the actual +page (any page can be on the freelist when we run low), don't mark the +page in any permanent way - just pass along the information to the +allocatee. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/mm_types.h | 1 + + mm/page_alloc.c | 4 +++- + 2 files changed, 4 insertions(+), 1 deletion(-) + +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -71,6 +71,7 @@ struct page { + union { + pgoff_t index; /* Our offset within mapping. */ + void *freelist; /* SLUB: freelist req. slab lock */ ++ int reserve; /* page_alloc: page is a reserve page */ + }; + struct list_head lru; /* Pageout list, eg. active_list + * protected by zone->lru_lock ! +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1572,8 +1572,10 @@ zonelist_scan: + try_this_zone: + page = buffered_rmqueue(preferred_zone, zone, order, + gfp_mask, migratetype); +- if (page) ++ if (page) { ++ page->reserve = !!(alloc_flags & ALLOC_NO_WATERMARKS); + break; ++ } + this_zone_full: + if (NUMA_BUILD) + zlc_mark_zone_full(zonelist, z); diff --git a/patches.suse/SoN-05-reserve-slub.patch b/patches.suse/SoN-05-reserve-slub.patch new file mode 100644 index 0000000..fe30a17 --- /dev/null +++ b/patches.suse/SoN-05-reserve-slub.patch @@ -0,0 +1,419 @@ +From: Peter Zijlstra +Subject: [PATCH 05/31] mm: sl[au]b: add knowledge of reserve pages +Patch-mainline: Not yet + +Restrict objects from reserve slabs (ALLOC_NO_WATERMARKS) to allocation +contexts that are entitled to it. This is done to ensure reserve pages don't +leak out and get consumed. + +The basic pattern used for all # allocators is the following, for each active +slab page we store if it came from an emergency allocation. When we find it +did, make sure the current allocation context would have been able to allocate +page from the emergency reserves as well. In that case allow the allocation. If +not, force a new slab allocation. When that works the memory pressure has +lifted enough to allow this context to get an object, otherwise fail the +allocation. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/slub_def.h | 1 + mm/slab.c | 61 ++++++++++++++++++++++++++++++++++++++++------- + mm/slob.c | 16 +++++++++++- + mm/slub.c | 43 +++++++++++++++++++++++++++------ + 4 files changed, 104 insertions(+), 17 deletions(-) + +--- a/include/linux/slub_def.h ++++ b/include/linux/slub_def.h +@@ -38,6 +38,7 @@ struct kmem_cache_cpu { + void **freelist; /* Pointer to first free per cpu object */ + struct page *page; /* The slab from which we are allocating */ + int node; /* The node of the page (or -1 for debug) */ ++ int reserve; /* Did the current page come from the reserve */ + #ifdef CONFIG_SLUB_STATS + unsigned stat[NR_SLUB_STAT_ITEMS]; + #endif +--- a/mm/slab.c ++++ b/mm/slab.c +@@ -120,6 +120,8 @@ + #include + #include + ++#include "internal.h" ++ + /* + * DEBUG - 1 for kmem_cache_create() to honour; SLAB_RED_ZONE & SLAB_POISON. + * 0 for faster, smaller code (especially in the critical paths). +@@ -268,7 +270,8 @@ struct array_cache { + unsigned int avail; + unsigned int limit; + unsigned int batchcount; +- unsigned int touched; ++ unsigned int touched:1, ++ reserve:1; + spinlock_t lock; + void *entry[]; /* + * Must have this definition in here for the proper +@@ -704,6 +707,27 @@ static inline struct array_cache *cpu_ca + return cachep->array[smp_processor_id()]; + } + ++/* ++ * If the last page came from the reserves, and the current allocation context ++ * does not have access to them, force an allocation to test the watermarks. ++ */ ++static inline int slab_force_alloc(struct kmem_cache *cachep, gfp_t flags) ++{ ++ if (unlikely(cpu_cache_get(cachep)->reserve) && ++ !(gfp_to_alloc_flags(flags) & ALLOC_NO_WATERMARKS)) ++ return 1; ++ ++ return 0; ++} ++ ++static inline void slab_set_reserve(struct kmem_cache *cachep, int reserve) ++{ ++ struct array_cache *ac = cpu_cache_get(cachep); ++ ++ if (unlikely(ac->reserve != reserve)) ++ ac->reserve = reserve; ++} ++ + static inline struct kmem_cache *__find_general_cachep(size_t size, + gfp_t gfpflags) + { +@@ -910,6 +934,7 @@ static struct array_cache *alloc_arrayca + nc->limit = entries; + nc->batchcount = batchcount; + nc->touched = 0; ++ nc->reserve = 0; + spin_lock_init(&nc->lock); + } + return nc; +@@ -1606,7 +1631,8 @@ __initcall(cpucache_init); + * did not request dmaable memory, we might get it, but that + * would be relatively rare and ignorable. + */ +-static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) ++static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid, ++ int *reserve) + { + struct page *page; + int nr_pages; +@@ -1628,6 +1654,7 @@ static void *kmem_getpages(struct kmem_c + if (!page) + return NULL; + ++ *reserve = page->reserve; + nr_pages = (1 << cachep->gfporder); + if (cachep->flags & SLAB_RECLAIM_ACCOUNT) + add_zone_page_state(page_zone(page), +@@ -2060,6 +2087,7 @@ static int __init_refok setup_cpu_cache( + cpu_cache_get(cachep)->limit = BOOT_CPUCACHE_ENTRIES; + cpu_cache_get(cachep)->batchcount = 1; + cpu_cache_get(cachep)->touched = 0; ++ cpu_cache_get(cachep)->reserve = 0; + cachep->batchcount = 1; + cachep->limit = BOOT_CPUCACHE_ENTRIES; + return 0; +@@ -2745,6 +2773,7 @@ static int cache_grow(struct kmem_cache + size_t offset; + gfp_t local_flags; + struct kmem_list3 *l3; ++ int reserve; + + /* + * Be lazy and only check for valid flags here, keeping it out of the +@@ -2783,7 +2812,7 @@ static int cache_grow(struct kmem_cache + * 'nodeid'. + */ + if (!objp) +- objp = kmem_getpages(cachep, local_flags, nodeid); ++ objp = kmem_getpages(cachep, local_flags, nodeid, &reserve); + if (!objp) + goto failed; + +@@ -2800,6 +2829,7 @@ static int cache_grow(struct kmem_cache + if (local_flags & __GFP_WAIT) + local_irq_disable(); + check_irq_off(); ++ slab_set_reserve(cachep, reserve); + spin_lock(&l3->list_lock); + + /* Make slab active. */ +@@ -2934,7 +2964,8 @@ bad: + #define check_slabp(x,y) do { } while(0) + #endif + +-static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags) ++static void *cache_alloc_refill(struct kmem_cache *cachep, ++ gfp_t flags, int must_refill) + { + int batchcount; + struct kmem_list3 *l3; +@@ -2944,6 +2975,8 @@ static void *cache_alloc_refill(struct k + retry: + check_irq_off(); + node = numa_node_id(); ++ if (unlikely(must_refill)) ++ goto force_grow; + ac = cpu_cache_get(cachep); + batchcount = ac->batchcount; + if (!ac->touched && batchcount > BATCHREFILL_LIMIT) { +@@ -3013,11 +3046,14 @@ alloc_done: + + if (unlikely(!ac->avail)) { + int x; ++force_grow: + x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL); + + /* cache_grow can reenable interrupts, then ac could change. */ + ac = cpu_cache_get(cachep); +- if (!x && ac->avail == 0) /* no objects in sight? abort */ ++ ++ /* no objects in sight? abort */ ++ if (!x && (ac->avail == 0 || must_refill)) + return NULL; + + if (!ac->avail) /* objects refilled by interrupt? */ +@@ -3107,17 +3143,18 @@ static inline void *____cache_alloc(stru + { + void *objp; + struct array_cache *ac; ++ int must_refill = slab_force_alloc(cachep, flags); + + check_irq_off(); + + ac = cpu_cache_get(cachep); +- if (likely(ac->avail)) { ++ if (likely(ac->avail && !must_refill)) { + STATS_INC_ALLOCHIT(cachep); + ac->touched = 1; + objp = ac->entry[--ac->avail]; + } else { + STATS_INC_ALLOCMISS(cachep); +- objp = cache_alloc_refill(cachep, flags); ++ objp = cache_alloc_refill(cachep, flags, must_refill); + /* + * the 'ac' may be updated by cache_alloc_refill(), + * and kmemleak_erase() requires its correct value. +@@ -3173,7 +3210,7 @@ static void *fallback_alloc(struct kmem_ + struct zone *zone; + enum zone_type high_zoneidx = gfp_zone(flags); + void *obj = NULL; +- int nid; ++ int nid, reserve; + + if (flags & __GFP_THISNODE) + return NULL; +@@ -3209,10 +3246,12 @@ retry: + if (local_flags & __GFP_WAIT) + local_irq_enable(); + kmem_flagcheck(cache, flags); +- obj = kmem_getpages(cache, local_flags, numa_node_id()); ++ obj = kmem_getpages(cache, local_flags, numa_node_id(), ++ &reserve); + if (local_flags & __GFP_WAIT) + local_irq_disable(); + if (obj) { ++ slab_set_reserve(cache, reserve); + /* + * Insert into the appropriate per node queues + */ +@@ -3251,6 +3290,9 @@ static void *____cache_alloc_node(struct + l3 = cachep->nodelists[nodeid]; + BUG_ON(!l3); + ++ if (unlikely(slab_force_alloc(cachep, flags))) ++ goto force_grow; ++ + retry: + check_irq_off(); + spin_lock(&l3->list_lock); +@@ -3288,6 +3330,7 @@ retry: + + must_grow: + spin_unlock(&l3->list_lock); ++force_grow: + x = cache_grow(cachep, flags | GFP_THISNODE, nodeid, NULL); + if (x) + goto retry; +--- a/mm/slob.c ++++ b/mm/slob.c +@@ -69,6 +69,7 @@ + #include + #include + #include ++#include "internal.h" + + /* + * slob_block has a field 'units', which indicates size of block if +ve, +@@ -191,6 +192,11 @@ struct slob_rcu { + static DEFINE_SPINLOCK(slob_lock); + + /* ++ * tracks the reserve state for the allocator. ++ */ ++static int slob_reserve; ++ ++/* + * Encode the given size and next info into a free slob block s. + */ + static void set_slob(slob_t *s, slobidx_t size, slob_t *next) +@@ -240,7 +246,7 @@ static int slob_last(slob_t *s) + + static void *slob_new_pages(gfp_t gfp, int order, int node) + { +- void *page; ++ struct page *page; + + #ifdef CONFIG_NUMA + if (node != -1) +@@ -252,6 +258,8 @@ static void *slob_new_pages(gfp_t gfp, i + if (!page) + return NULL; + ++ slob_reserve = page->reserve; ++ + return page_address(page); + } + +@@ -324,6 +332,11 @@ static void *slob_alloc(size_t size, gfp + slob_t *b = NULL; + unsigned long flags; + ++ if (unlikely(slob_reserve)) { ++ if (!(gfp_to_alloc_flags(gfp) & ALLOC_NO_WATERMARKS)) ++ goto grow; ++ } ++ + if (size < SLOB_BREAK1) + slob_list = &free_slob_small; + else if (size < SLOB_BREAK2) +@@ -362,6 +375,7 @@ static void *slob_alloc(size_t size, gfp + } + spin_unlock_irqrestore(&slob_lock, flags); + ++grow: + /* Not enough space: must allocate a new page */ + if (!b) { + b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node); +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -28,6 +28,8 @@ + #include + #include + #include ++#include "internal.h" ++ + + /* + * Lock order: +@@ -1148,7 +1150,8 @@ static void setup_object(struct kmem_cac + s->ctor(object); + } + +-static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) ++static ++struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node, int *reserve) + { + struct page *page; + void *start; +@@ -1162,6 +1165,8 @@ static struct page *new_slab(struct kmem + if (!page) + goto out; + ++ *reserve = page->reserve; ++ + inc_slabs_node(s, page_to_nid(page), page->objects); + page->slab = s; + page->flags |= 1 << PG_slab; +@@ -1611,10 +1616,20 @@ static void *__slab_alloc(struct kmem_ca + { + void **object; + struct page *new; ++ int reserve; + + /* We handle __GFP_ZERO in the caller */ + gfpflags &= ~__GFP_ZERO; + ++ if (unlikely(c->reserve)) { ++ /* ++ * If the current slab is a reserve slab and the current ++ * allocation context does not allow access to the reserves we ++ * must force an allocation to test the current levels. ++ */ ++ if (!(gfp_to_alloc_flags(gfpflags) & ALLOC_NO_WATERMARKS)) ++ goto grow_slab; ++ } + if (!c->page) + goto new_slab; + +@@ -1628,8 +1643,8 @@ load_freelist: + object = c->page->freelist; + if (unlikely(!object)) + goto another_slab; +- if (unlikely(SLABDEBUG && PageSlubDebug(c->page))) +- goto debug; ++ if (unlikely(PageSlubDebug(c->page) || c->reserve)) ++ goto slow_path; + + c->freelist = get_freepointer(s, object); + c->page->inuse = c->page->objects; +@@ -1651,16 +1666,18 @@ new_slab: + goto load_freelist; + } + ++grow_slab: + if (gfpflags & __GFP_WAIT) + local_irq_enable(); + +- new = new_slab(s, gfpflags, node); ++ new = new_slab(s, gfpflags, node, &reserve); + + if (gfpflags & __GFP_WAIT) + local_irq_disable(); + + if (new) { + c = __this_cpu_ptr(s->cpu_slab); ++ c->reserve = reserve; + stat(s, ALLOC_SLAB); + if (c->page) + flush_slab(s, c); +@@ -1672,10 +1689,21 @@ new_slab: + if (!(gfpflags & __GFP_NOWARN) && printk_ratelimit()) + slab_out_of_memory(s, gfpflags, node); + return NULL; +-debug: +- if (!alloc_debug_processing(s, c->page, object, addr)) ++ ++slow_path: ++ if (PageSlubDebug(c->page) && ++ !alloc_debug_processing(s, c->page, object, addr)) + goto another_slab; + ++ /* ++ * Avoid the slub fast path in slab_alloc() by not setting ++ * c->freelist and the fast path in slab_free() by making ++ * node_match() fail by setting c->node to -1. ++ * ++ * We use this for for debug and reserve checks which need ++ * to be done for each allocation. ++ */ ++ + c->page->inuse++; + c->page->freelist = get_freepointer(s, object); + c->node = -1; +@@ -2100,10 +2128,11 @@ static void early_kmem_cache_node_alloc( + struct page *page; + struct kmem_cache_node *n; + unsigned long flags; ++ int reserve; + + BUG_ON(kmalloc_caches->size < sizeof(struct kmem_cache_node)); + +- page = new_slab(kmalloc_caches, gfpflags, node); ++ page = new_slab(kmalloc_caches, gfpflags, node, &reserve); + + BUG_ON(!page); + if (page_to_nid(page) != node) { diff --git a/patches.suse/SoN-06-mm-kmem_estimate_pages.patch b/patches.suse/SoN-06-mm-kmem_estimate_pages.patch new file mode 100644 index 0000000..806175e --- /dev/null +++ b/patches.suse/SoN-06-mm-kmem_estimate_pages.patch @@ -0,0 +1,303 @@ +From: Peter Zijlstra +Subject: [PATCH 06/31] mm: kmem_alloc_estimate() +Patch-mainline: not yet + +Provide a method to get the upper bound on the pages needed to allocate +a given number of objects from a given kmem_cache. + +This lays the foundation for a generic reserve framework as presented in +a later patch in this series. This framework needs to convert object demand +(kmalloc() bytes, kmem_cache_alloc() objects) to pages. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/slab.h | 4 ++ + mm/slab.c | 75 +++++++++++++++++++++++++++++++++++++++++++ + mm/slob.c | 67 +++++++++++++++++++++++++++++++++++++++ + mm/slub.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 233 insertions(+) + +--- a/include/linux/slab.h ++++ b/include/linux/slab.h +@@ -107,6 +107,8 @@ void kmem_cache_free(struct kmem_cache * + const char *kmem_cache_name(struct kmem_cache *); + int kern_ptr_validate(const void *ptr, unsigned long size); + int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr); ++unsigned kmem_alloc_estimate(struct kmem_cache *cachep, ++ gfp_t flags, int objects); + + /* + * Please use this macro to create slab caches. Simply specify the +@@ -143,6 +145,8 @@ void * __must_check krealloc(const void + void kfree(const void *); + void kzfree(const void *); + size_t ksize(const void *); ++unsigned kmalloc_estimate_objs(size_t, gfp_t, int); ++unsigned kmalloc_estimate_bytes(gfp_t, size_t); + + /* + * Allocator specific definitions. These are mainly used to establish optimized +--- a/mm/slab.c ++++ b/mm/slab.c +@@ -3850,6 +3850,81 @@ const char *kmem_cache_name(struct kmem_ + EXPORT_SYMBOL_GPL(kmem_cache_name); + + /* ++ * Calculate the upper bound of pages required to sequentially allocate ++ * @objects objects from @cachep. ++ */ ++unsigned kmem_alloc_estimate(struct kmem_cache *cachep, ++ gfp_t flags, int objects) ++{ ++ /* ++ * (1) memory for objects, ++ */ ++ unsigned nr_slabs = DIV_ROUND_UP(objects, cachep->num); ++ unsigned nr_pages = nr_slabs << cachep->gfporder; ++ ++ /* ++ * (2) memory for each per-cpu queue (nr_cpu_ids), ++ * (3) memory for each per-node alien queues (nr_cpu_ids), and ++ * (4) some amount of memory for the slab management structures ++ * ++ * XXX: truely account these ++ */ ++ nr_pages += 1 + ilog2(nr_pages); ++ ++ return nr_pages; ++} ++ ++/* ++ * Calculate the upper bound of pages required to sequentially allocate ++ * @count objects of @size bytes from kmalloc given @flags. ++ */ ++unsigned kmalloc_estimate_objs(size_t size, gfp_t flags, int count) ++{ ++ struct kmem_cache *s = kmem_find_general_cachep(size, flags); ++ if (!s) ++ return 0; ++ ++ return kmem_alloc_estimate(s, flags, count); ++} ++EXPORT_SYMBOL_GPL(kmalloc_estimate_objs); ++ ++/* ++ * Calculate the upper bound of pages requires to sequentially allocate @bytes ++ * from kmalloc in an unspecified number of allocations of nonuniform size. ++ */ ++unsigned kmalloc_estimate_bytes(gfp_t flags, size_t bytes) ++{ ++ unsigned long pages; ++ struct cache_sizes *csizep = malloc_sizes; ++ ++ /* ++ * multiply by two, in order to account the worst case slack space ++ * due to the power-of-two allocation sizes. ++ */ ++ pages = DIV_ROUND_UP(2 * bytes, PAGE_SIZE); ++ ++ /* ++ * add the kmem_cache overhead of each possible kmalloc cache ++ */ ++ for (csizep = malloc_sizes; csizep->cs_cachep; csizep++) { ++ struct kmem_cache *s; ++ ++#ifdef CONFIG_ZONE_DMA ++ if (unlikely(flags & __GFP_DMA)) ++ s = csizep->cs_dmacachep; ++ else ++#endif ++ s = csizep->cs_cachep; ++ ++ if (s) ++ pages += kmem_alloc_estimate(s, flags, 0); ++ } ++ ++ return pages; ++} ++EXPORT_SYMBOL_GPL(kmalloc_estimate_bytes); ++ ++/* + * This initializes kmem_list3 or resizes various caches for all nodes. + */ + static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp) +--- a/mm/slob.c ++++ b/mm/slob.c +@@ -702,6 +702,73 @@ int slab_is_available(void) + return slob_ready; + } + ++static __slob_estimate(unsigned size, unsigned align, unsigned objects) ++{ ++ unsigned nr_pages; ++ ++ size = SLOB_UNIT * SLOB_UNITS(size + align - 1); ++ ++ if (size <= PAGE_SIZE) { ++ nr_pages = DIV_ROUND_UP(objects, PAGE_SIZE / size); ++ } else { ++ nr_pages = objects << get_order(size); ++ } ++ ++ return nr_pages; ++} ++ ++/* ++ * Calculate the upper bound of pages required to sequentially allocate ++ * @objects objects from @cachep. ++ */ ++unsigned kmem_alloc_estimate(struct kmem_cache *c, gfp_t flags, int objects) ++{ ++ unsigned size = c->size; ++ ++ if (c->flags & SLAB_DESTROY_BY_RCU) ++ size += sizeof(struct slob_rcu); ++ ++ return __slob_estimate(size, c->align, objects); ++} ++ ++/* ++ * Calculate the upper bound of pages required to sequentially allocate ++ * @count objects of @size bytes from kmalloc given @flags. ++ */ ++unsigned kmalloc_estimate_objs(size_t size, gfp_t flags, int count) ++{ ++ unsigned align = max(ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN); ++ ++ return __slob_estimate(size, align, count); ++} ++EXPORT_SYMBOL_GPL(kmalloc_estimate_objs); ++ ++/* ++ * Calculate the upper bound of pages requires to sequentially allocate @bytes ++ * from kmalloc in an unspecified number of allocations of nonuniform size. ++ */ ++unsigned kmalloc_estimate_bytes(gfp_t flags, size_t bytes) ++{ ++ unsigned long pages; ++ ++ /* ++ * Multiply by two, in order to account the worst case slack space ++ * due to the power-of-two allocation sizes. ++ * ++ * While not true for slob, it cannot do worse than that for sequential ++ * allocations. ++ */ ++ pages = DIV_ROUND_UP(2 * bytes, PAGE_SIZE); ++ ++ /* ++ * Our power of two series starts at PAGE_SIZE, so add one page. ++ */ ++ pages++; ++ ++ return pages; ++} ++EXPORT_SYMBOL_GPL(kmalloc_estimate_bytes); ++ + void __init kmem_cache_init(void) + { + slob_ready = 1; +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -2449,6 +2449,42 @@ const char *kmem_cache_name(struct kmem_ + } + EXPORT_SYMBOL(kmem_cache_name); + ++/* ++ * Calculate the upper bound of pages required to sequentially allocate ++ * @objects objects from @cachep. ++ * ++ * We should use s->min_objects because those are the least efficient. ++ */ ++unsigned kmem_alloc_estimate(struct kmem_cache *s, gfp_t flags, int objects) ++{ ++ unsigned long pages; ++ struct kmem_cache_order_objects x; ++ ++ if (WARN_ON(!s) || WARN_ON(!oo_objects(s->min))) ++ return 0; ++ ++ x = s->min; ++ pages = DIV_ROUND_UP(objects, oo_objects(x)) << oo_order(x); ++ ++ /* ++ * Account the possible additional overhead if the slab holds more that ++ * one object. Use s->max_objects because that's the worst case. ++ */ ++ x = s->oo; ++ if (oo_objects(x) > 1) { ++ /* ++ * Account the possible additional overhead if per cpu slabs ++ * are currently empty and have to be allocated. This is very ++ * unlikely but a possible scenario immediately after ++ * kmem_cache_shrink. ++ */ ++ pages += num_possible_cpus() << oo_order(x); ++ } ++ ++ return pages; ++} ++EXPORT_SYMBOL_GPL(kmem_alloc_estimate); ++ + static void list_slab_objects(struct kmem_cache *s, struct page *page, + const char *text) + { +@@ -2879,6 +2915,57 @@ void kfree(const void *x) + EXPORT_SYMBOL(kfree); + + /* ++ * Calculate the upper bound of pages required to sequentially allocate ++ * @count objects of @size bytes from kmalloc given @flags. ++ */ ++unsigned kmalloc_estimate_objs(size_t size, gfp_t flags, int count) ++{ ++ struct kmem_cache *s = get_slab(size, flags); ++ if (!s) ++ return 0; ++ ++ return kmem_alloc_estimate(s, flags, count); ++ ++} ++EXPORT_SYMBOL_GPL(kmalloc_estimate_objs); ++ ++/* ++ * Calculate the upper bound of pages requires to sequentially allocate @bytes ++ * from kmalloc in an unspecified number of allocations of nonuniform size. ++ */ ++unsigned kmalloc_estimate_bytes(gfp_t flags, size_t bytes) ++{ ++ int i; ++ unsigned long pages; ++ ++ /* ++ * multiply by two, in order to account the worst case slack space ++ * due to the power-of-two allocation sizes. ++ */ ++ pages = DIV_ROUND_UP(2 * bytes, PAGE_SIZE); ++ ++ /* ++ * add the kmem_cache overhead of each possible kmalloc cache ++ */ ++ for (i = 1; i < PAGE_SHIFT; i++) { ++ struct kmem_cache *s; ++ ++#ifdef CONFIG_ZONE_DMA ++ if (unlikely(flags & SLUB_DMA)) ++ s = dma_kmalloc_cache(i, flags); ++ else ++#endif ++ s = &kmalloc_caches[i]; ++ ++ if (s) ++ pages += kmem_alloc_estimate(s, flags, 0); ++ } ++ ++ return pages; ++} ++EXPORT_SYMBOL_GPL(kmalloc_estimate_bytes); ++ ++/* + * kmem_cache_shrink removes empty slabs from the partial lists and sorts + * the remaining slabs by the number of items in use. The slabs with the + * most items in use come first. New allocations will then fill those up diff --git a/patches.suse/SoN-07-mm-PF_MEMALLOC-softirq.patch b/patches.suse/SoN-07-mm-PF_MEMALLOC-softirq.patch new file mode 100644 index 0000000..927a060 --- /dev/null +++ b/patches.suse/SoN-07-mm-PF_MEMALLOC-softirq.patch @@ -0,0 +1,83 @@ +From: Peter Zijlstra +Subject: [PATCH 07/31] mm: allow PF_MEMALLOC from softirq context +Patch-mainline: not yet + +This is needed to allow network softirq packet processing to make use of +PF_MEMALLOC. + +Currently softirq context cannot use PF_MEMALLOC due to it not being associated +with a task, and therefore not having task flags to fiddle with - thus the gfp +to alloc flag mapping ignores the task flags when in interrupts (hard or soft) +context. + +Allowing softirqs to make use of PF_MEMALLOC therefore requires some trickery. +We basically borrow the task flags from whatever process happens to be +preempted by the softirq. + +So we modify the gfp to alloc flags mapping to not exclude task flags in +softirq context, and modify the softirq code to save, clear and restore the +PF_MEMALLOC flag. + +The save and clear, ensures the preempted task's PF_MEMALLOC flag doesn't +leak into the softirq. The restore ensures a softirq's PF_MEMALLOC flag cannot +leak back into the preempted process. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/sched.h | 7 +++++++ + kernel/softirq.c | 3 +++ + mm/page_alloc.c | 7 ++++--- + 3 files changed, 14 insertions(+), 3 deletions(-) + +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -1772,6 +1772,13 @@ static inline void rcu_copy_process(stru + + #endif + ++static inline void tsk_restore_flags(struct task_struct *p, ++ unsigned long pflags, unsigned long mask) ++{ ++ p->flags &= ~mask; ++ p->flags |= pflags & mask; ++} ++ + #ifdef CONFIG_SMP + extern int set_cpus_allowed_ptr(struct task_struct *p, + const struct cpumask *new_mask); +--- a/kernel/softirq.c ++++ b/kernel/softirq.c +@@ -194,6 +194,8 @@ asmlinkage void __do_softirq(void) + __u32 pending; + int max_restart = MAX_SOFTIRQ_RESTART; + int cpu; ++ unsigned long pflags = current->flags; ++ current->flags &= ~PF_MEMALLOC; + + pending = local_softirq_pending(); + account_system_vtime(current); +@@ -246,6 +248,7 @@ restart: + + account_system_vtime(current); + _local_bh_enable(); ++ tsk_restore_flags(current, pflags, PF_MEMALLOC); + } + + #ifndef __ARCH_HAS_DO_SOFTIRQ +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1785,9 +1785,10 @@ int gfp_to_alloc_flags(gfp_t gfp_mask) + alloc_flags |= ALLOC_HARDER; + + if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) { +- if (!in_interrupt() && +- ((p->flags & PF_MEMALLOC) || +- unlikely(test_thread_flag(TIF_MEMDIE)))) ++ if (!in_irq() && (p->flags & PF_MEMALLOC)) ++ alloc_flags |= ALLOC_NO_WATERMARKS; ++ else if (!in_interrupt() && ++ unlikely(test_thread_flag(TIF_MEMDIE))) + alloc_flags |= ALLOC_NO_WATERMARKS; + } + diff --git a/patches.suse/SoN-08-mm-page_alloc-emerg.patch b/patches.suse/SoN-08-mm-page_alloc-emerg.patch new file mode 100644 index 0000000..e9e8882 --- /dev/null +++ b/patches.suse/SoN-08-mm-page_alloc-emerg.patch @@ -0,0 +1,218 @@ +From: Peter Zijlstra +Subject: [PATCH 08/31] mm: emergency pool +Patch-mainline: not yet + +Provide means to reserve a specific amount of pages. + +The emergency pool is separated from the min watermark because ALLOC_HARDER +and ALLOC_HIGH modify the watermark in a relative way and thus do not ensure +a strict minimum. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/mmzone.h | 3 + + mm/page_alloc.c | 84 +++++++++++++++++++++++++++++++++++++++++++------ + mm/vmstat.c | 6 +-- + 3 files changed, 80 insertions(+), 13 deletions(-) + +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -279,6 +279,7 @@ struct zone_reclaim_stat { + + struct zone { + /* Fields commonly accessed by the page allocator */ ++ unsigned long pages_emerg; /* emergency pool */ + + /* zone watermarks, access with *_wmark_pages(zone) macros */ + unsigned long watermark[NR_WMARK]; +@@ -756,6 +757,8 @@ int sysctl_min_unmapped_ratio_sysctl_han + int sysctl_min_slab_ratio_sysctl_handler(struct ctl_table *, int, + void __user *, size_t *, loff_t *); + ++int adjust_memalloc_reserve(int pages); ++ + extern int numa_zonelist_order_handler(struct ctl_table *, int, + void __user *, size_t *, loff_t *); + extern char numa_zonelist_order[]; +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -150,6 +150,8 @@ static char * const zone_names[MAX_NR_ZO + + static DEFINE_SPINLOCK(min_free_lock); + int min_free_kbytes = 1024; ++static DEFINE_MUTEX(var_free_mutex); ++int var_free_kbytes; + + static unsigned long __meminitdata nr_kernel_pages; + static unsigned long __meminitdata nr_all_pages; +@@ -1373,7 +1375,7 @@ int zone_watermark_ok(struct zone *z, in + if (alloc_flags & ALLOC_HARDER) + min -= min / 4; + +- if (free_pages <= min + z->lowmem_reserve[classzone_idx]) ++ if (free_pages <= min+z->lowmem_reserve[classzone_idx]+z->pages_emerg) + return 0; + for (o = 0; o < order; o++) { + /* At the next order, this order's pages become unavailable */ +@@ -1803,7 +1805,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, u + { + const gfp_t wait = gfp_mask & __GFP_WAIT; + struct page *page = NULL; +- int alloc_flags; ++ int alloc_flags = 0; + unsigned long pages_reclaimed = 0; + unsigned long did_some_progress; + struct task_struct *p = current; +@@ -1924,8 +1926,8 @@ nopage: + printk(KERN_INFO "perfectly reliable and the kernel is designed to handle that.\n"); + } + printk(KERN_INFO "%s: page allocation failure." +- " order:%d, mode:0x%x\n", +- p->comm, order, gfp_mask); ++ " order:%d, mode:0x%x, alloc_flags:0x%x pflags:0x%x\n", ++ p->comm, order, gfp_mask, alloc_flags, p->flags); + dump_stack(); + show_mem(); + } +@@ -2256,9 +2258,9 @@ void show_free_areas(void) + "\n", + zone->name, + K(zone_page_state(zone, NR_FREE_PAGES)), +- K(min_wmark_pages(zone)), +- K(low_wmark_pages(zone)), +- K(high_wmark_pages(zone)), ++ K(zone->pages_emerg + min_wmark_pages(zone)), ++ K(zone->pages_emerg + low_wmark_pages(zone)), ++ K(zone->pages_emerg + high_wmark_pages(zone)), + K(zone_page_state(zone, NR_ACTIVE_ANON)), + K(zone_page_state(zone, NR_INACTIVE_ANON)), + K(zone_page_state(zone, NR_ACTIVE_FILE)), +@@ -4549,7 +4551,7 @@ static void calculate_totalreserve_pages + } + + /* we treat the high watermark as reserved pages. */ +- max += high_wmark_pages(zone); ++ max += high_wmark_pages(zone) + zone->pages_emerg; + + if (max > zone->present_pages) + max = zone->present_pages; +@@ -4607,7 +4609,8 @@ static void setup_per_zone_lowmem_reserv + */ + static void __setup_per_zone_wmarks(void) + { +- unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); ++ unsigned pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); ++ unsigned pages_emerg = var_free_kbytes >> (PAGE_SHIFT - 10); + unsigned long lowmem_pages = 0; + struct zone *zone; + unsigned long flags; +@@ -4619,11 +4622,13 @@ static void __setup_per_zone_wmarks(void + } + + for_each_zone(zone) { +- u64 tmp; ++ u64 tmp, tmp_emerg; + + spin_lock_irqsave(&zone->lock, flags); + tmp = (u64)pages_min * zone->present_pages; + do_div(tmp, lowmem_pages); ++ tmp_emerg = (u64)pages_emerg * zone->present_pages; ++ do_div(tmp_emerg, lowmem_pages); + if (is_highmem(zone)) { + /* + * __GFP_HIGH and PF_MEMALLOC allocations usually don't +@@ -4642,12 +4647,14 @@ static void __setup_per_zone_wmarks(void + if (min_pages > 128) + min_pages = 128; + zone->watermark[WMARK_MIN] = min_pages; ++ zone->pages_emerg = 0; + } else { + /* + * If it's a lowmem zone, reserve a number of pages + * proportionate to the zone's size. + */ + zone->watermark[WMARK_MIN] = tmp; ++ zone->pages_emerg = tmp_emerg; + } + + zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2); +@@ -4712,6 +4719,63 @@ void setup_per_zone_wmarks(void) + spin_unlock_irqrestore(&min_free_lock, flags); + } + ++static void __adjust_memalloc_reserve(int pages) ++{ ++ var_free_kbytes += pages << (PAGE_SHIFT - 10); ++ BUG_ON(var_free_kbytes < 0); ++ setup_per_zone_wmarks(); ++} ++ ++static int test_reserve_limits(void) ++{ ++ struct zone *zone; ++ int node; ++ ++ for_each_zone(zone) ++ wakeup_kswapd(zone, 0); ++ ++ for_each_online_node(node) { ++ struct page *page = alloc_pages_node(node, GFP_KERNEL, 0); ++ if (!page) ++ return -ENOMEM; ++ ++ __free_page(page); ++ } ++ ++ return 0; ++} ++ ++/** ++ * adjust_memalloc_reserve - adjust the memalloc reserve ++ * @pages: number of pages to add ++ * ++ * It adds a number of pages to the memalloc reserve; if ++ * the number was positive it kicks reclaim into action to ++ * satisfy the higher watermarks. ++ * ++ * returns -ENOMEM when it failed to satisfy the watermarks. ++ */ ++int adjust_memalloc_reserve(int pages) ++{ ++ int err = 0; ++ ++ mutex_lock(&var_free_mutex); ++ __adjust_memalloc_reserve(pages); ++ if (pages > 0) { ++ err = test_reserve_limits(); ++ if (err) { ++ __adjust_memalloc_reserve(-pages); ++ goto unlock; ++ } ++ } ++ printk(KERN_DEBUG "Emergency reserve: %d\n", var_free_kbytes); ++ ++unlock: ++ mutex_unlock(&var_free_mutex); ++ return err; ++} ++EXPORT_SYMBOL_GPL(adjust_memalloc_reserve); ++ + /* + * Initialise min_free_kbytes. + * +--- a/mm/vmstat.c ++++ b/mm/vmstat.c +@@ -721,9 +721,9 @@ static void zoneinfo_show_print(struct s + "\n spanned %lu" + "\n present %lu", + zone_page_state(zone, NR_FREE_PAGES), +- min_wmark_pages(zone), +- low_wmark_pages(zone), +- high_wmark_pages(zone), ++ zone->pages_emerg + min_wmark_pages(zone), ++ zone->pages_emerg + min_wmark_pages(zone), ++ zone->pages_emerg + high_wmark_pages(zone), + zone->pages_scanned, + zone->spanned_pages, + zone->present_pages); diff --git a/patches.suse/SoN-09-global-ALLOC_NO_WATERMARKS.patch b/patches.suse/SoN-09-global-ALLOC_NO_WATERMARKS.patch new file mode 100644 index 0000000..9c1c5d6 --- /dev/null +++ b/patches.suse/SoN-09-global-ALLOC_NO_WATERMARKS.patch @@ -0,0 +1,36 @@ +From: Peter Zijlstra +Subject: [PATCH 09/31] mm: system wide ALLOC_NO_WATERMARK +Patch-mainline: not yet + +The reserve is proportionally distributed over all (!highmem) zones in the +system. So we need to allow an emergency allocation access to all zones. In +order to do that we need to break out of any mempolicy boundaries we might +have. + +In my opinion that does not break mempolicies as those are user oriented +and not system oriented. That is, system allocations are not guaranteed to be +within mempolicy boundaries. For instance IRQs don't even have a mempolicy. + +So breaking out of mempolicy boundaries for 'rare' emergency allocations, +which are always system allocations (as opposed to user) is ok. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + mm/page_alloc.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1852,6 +1852,11 @@ restart: + rebalance: + /* Allocate without watermarks if the context allows */ + if (alloc_flags & ALLOC_NO_WATERMARKS) { ++ /* ++ * break out mempolicy boundaries ++ */ ++ zonelist = node_zonelist(numa_node_id(), gfp_mask); ++ + page = __alloc_pages_high_priority(gfp_mask, order, + zonelist, high_zoneidx, nodemask, + preferred_zone, migratetype); diff --git a/patches.suse/SoN-10-mm-page_alloc-GFP_EMERGENCY.patch b/patches.suse/SoN-10-mm-page_alloc-GFP_EMERGENCY.patch new file mode 100644 index 0000000..995aba9 --- /dev/null +++ b/patches.suse/SoN-10-mm-page_alloc-GFP_EMERGENCY.patch @@ -0,0 +1,49 @@ +From: Peter Zijlstra +Subject: [PATCH 10/31] mm: __GFP_MEMALLOC +Patch-mainline: not yet + +__GFP_MEMALLOC will allow the allocation to disregard the watermarks, +much like PF_MEMALLOC. + +It allows one to pass along the memalloc state in object related allocation +flags as opposed to task related flags, such as sk->sk_allocation. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/gfp.h | 3 ++- + mm/page_alloc.c | 4 +++- + 2 files changed, 5 insertions(+), 2 deletions(-) + +--- a/include/linux/gfp.h ++++ b/include/linux/gfp.h +@@ -47,6 +47,7 @@ struct vm_area_struct; + #define __GFP_REPEAT ((__force gfp_t)0x400u) /* See above */ + #define __GFP_NOFAIL ((__force gfp_t)0x800u) /* See above */ + #define __GFP_NORETRY ((__force gfp_t)0x1000u)/* See above */ ++#define __GFP_MEMALLOC ((__force gfp_t)0x2000u)/* Use emergency reserves */ + #define __GFP_COMP ((__force gfp_t)0x4000u)/* Add compound page metadata */ + #define __GFP_ZERO ((__force gfp_t)0x8000u)/* Return zeroed page on success */ + #define __GFP_NOMEMALLOC ((__force gfp_t)0x10000u) /* Don't use emergency reserves */ +@@ -98,7 +99,7 @@ struct vm_area_struct; + /* Control page allocator reclaim behavior */ + #define GFP_RECLAIM_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS|\ + __GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\ +- __GFP_NORETRY|__GFP_NOMEMALLOC) ++ __GFP_NORETRY|__GFP_MEMALLOC|__GFP_NOMEMALLOC) + + /* Control slab gfp mask during early boot */ + #define GFP_BOOT_MASK __GFP_BITS_MASK & ~(__GFP_WAIT|__GFP_IO|__GFP_FS) +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1787,7 +1787,9 @@ int gfp_to_alloc_flags(gfp_t gfp_mask) + alloc_flags |= ALLOC_HARDER; + + if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) { +- if (!in_irq() && (p->flags & PF_MEMALLOC)) ++ if (gfp_mask & __GFP_MEMALLOC) ++ alloc_flags |= ALLOC_NO_WATERMARKS; ++ else if (!in_irq() && (p->flags & PF_MEMALLOC)) + alloc_flags |= ALLOC_NO_WATERMARKS; + else if (!in_interrupt() && + unlikely(test_thread_flag(TIF_MEMDIE))) diff --git a/patches.suse/SoN-11-mm-reserve.patch b/patches.suse/SoN-11-mm-reserve.patch new file mode 100644 index 0000000..fb7bcd5 --- /dev/null +++ b/patches.suse/SoN-11-mm-reserve.patch @@ -0,0 +1,891 @@ +From: Peter Zijlstra +Subject: [PATCH 11/31] mm: memory reserve management +Patch-mainline: not yet + +Generic reserve management code. + +It provides methods to reserve and charge. Upon this, generic alloc/free style +reserve pools could be build, which could fully replace mempool_t +functionality. + +It should also allow for a Banker's algorithm replacement of __GFP_NOFAIL. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/reserve.h | 198 ++++++++++++++ + mm/Makefile | 2 + mm/reserve.c | 637 ++++++++++++++++++++++++++++++++++++++++++++++++ + mm/slub.c | 2 + 4 files changed, 837 insertions(+), 2 deletions(-) + +--- /dev/null ++++ b/include/linux/reserve.h +@@ -0,0 +1,198 @@ ++/* ++ * Memory reserve management. ++ * ++ * Copyright (C) 2007-2008 Red Hat, Inc., ++ * Peter Zijlstra ++ * ++ * This file contains the public data structure and API definitions. ++ */ ++ ++#ifndef _LINUX_RESERVE_H ++#define _LINUX_RESERVE_H ++ ++#include ++#include ++#include ++#include ++ ++struct mem_reserve { ++ struct mem_reserve *parent; ++ struct list_head children; ++ struct list_head siblings; ++ ++ const char *name; ++ ++ long pages; ++ long limit; ++ long usage; ++ spinlock_t lock; /* protects limit and usage */ ++ ++ wait_queue_head_t waitqueue; ++}; ++ ++extern struct mem_reserve mem_reserve_root; ++ ++void mem_reserve_init(struct mem_reserve *res, const char *name, ++ struct mem_reserve *parent); ++int mem_reserve_connect(struct mem_reserve *new_child, ++ struct mem_reserve *node); ++void mem_reserve_disconnect(struct mem_reserve *node); ++ ++int mem_reserve_pages_set(struct mem_reserve *res, long pages); ++int mem_reserve_pages_add(struct mem_reserve *res, long pages); ++int mem_reserve_pages_charge(struct mem_reserve *res, long pages); ++ ++int mem_reserve_kmalloc_set(struct mem_reserve *res, long bytes); ++int mem_reserve_kmalloc_charge(struct mem_reserve *res, long bytes); ++ ++struct kmem_cache; ++ ++int mem_reserve_kmem_cache_set(struct mem_reserve *res, ++ struct kmem_cache *s, ++ int objects); ++int mem_reserve_kmem_cache_charge(struct mem_reserve *res, ++ struct kmem_cache *s, long objs); ++ ++void *___kmalloc_reserve(size_t size, gfp_t flags, int node, unsigned long ip, ++ struct mem_reserve *res, int *emerg); ++ ++static inline ++void *__kmalloc_reserve(size_t size, gfp_t flags, int node, unsigned long ip, ++ struct mem_reserve *res, int *emerg) ++{ ++ void *obj; ++ ++ obj = kmalloc_node_track_caller(size, ++ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, node); ++ if (!obj) ++ obj = ___kmalloc_reserve(size, flags, node, ip, res, emerg); ++ ++ return obj; ++} ++ ++/** ++ * kmalloc_reserve() - kmalloc() and charge against @res for @emerg allocations ++ * @size - size of the requested memory region ++ * @gfp - allocation flags to use for this allocation ++ * @node - preferred memory node for this allocation ++ * @res - reserve to charge emergency allocations against ++ * @emerg - bit 0 is set when the allocation was an emergency allocation ++ * ++ * Returns NULL on failure ++ */ ++#define kmalloc_reserve(size, gfp, node, res, emerg) \ ++ __kmalloc_reserve(size, gfp, node, \ ++ __builtin_return_address(0), res, emerg) ++ ++void __kfree_reserve(void *obj, struct mem_reserve *res, int emerg); ++ ++/** ++ * kfree_reserve() - kfree() and uncharge against @res for @emerg allocations ++ * @obj - memory to free ++ * @res - reserve to uncharge emergency allocations from ++ * @emerg - was this an emergency allocation ++ */ ++static inline ++void kfree_reserve(void *obj, struct mem_reserve *res, int emerg) ++{ ++ if (unlikely(obj && res && emerg)) ++ __kfree_reserve(obj, res, emerg); ++ else ++ kfree(obj); ++} ++ ++void *__kmem_cache_alloc_reserve(struct kmem_cache *s, gfp_t flags, int node, ++ struct mem_reserve *res, int *emerg); ++ ++/** ++ * kmem_cache_alloc_reserve() - kmem_cache_alloc() and charge against @res ++ * @s - kmem_cache to allocate from ++ * @gfp - allocation flags to use for this allocation ++ * @node - preferred memory node for this allocation ++ * @res - reserve to charge emergency allocations against ++ * @emerg - bit 0 is set when the allocation was an emergency allocation ++ * ++ * Returns NULL on failure ++ */ ++static inline ++void *kmem_cache_alloc_reserve(struct kmem_cache *s, gfp_t flags, int node, ++ struct mem_reserve *res, int *emerg) ++{ ++ void *obj; ++ ++ obj = kmem_cache_alloc_node(s, ++ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, node); ++ if (!obj) ++ obj = __kmem_cache_alloc_reserve(s, flags, node, res, emerg); ++ ++ return obj; ++} ++ ++void __kmem_cache_free_reserve(struct kmem_cache *s, void *obj, ++ struct mem_reserve *res, int emerg); ++ ++/** ++ * kmem_cache_free_reserve() - kmem_cache_free() and uncharge against @res ++ * @s - kmem_cache to free to ++ * @obj - memory to free ++ * @res - reserve to uncharge emergency allocations from ++ * @emerg - was this an emergency allocation ++ */ ++static inline ++void kmem_cache_free_reserve(struct kmem_cache *s, void *obj, ++ struct mem_reserve *res, int emerg) ++{ ++ if (unlikely(obj && res && emerg)) ++ __kmem_cache_free_reserve(s, obj, res, emerg); ++ else ++ kmem_cache_free(s, obj); ++} ++ ++struct page *__alloc_pages_reserve(int node, gfp_t flags, int order, ++ struct mem_reserve *res, int *emerg); ++ ++/** ++ * alloc_pages_reserve() - alloc_pages() and charge against @res ++ * @node - preferred memory node for this allocation ++ * @gfp - allocation flags to use for this allocation ++ * @order - page order ++ * @res - reserve to charge emergency allocations against ++ * @emerg - bit 0 is set when the allocation was an emergency allocation ++ * ++ * Returns NULL on failure ++ */ ++static inline ++struct page *alloc_pages_reserve(int node, gfp_t flags, int order, ++ struct mem_reserve *res, int *emerg) ++{ ++ struct page *page; ++ ++ page = alloc_pages_node(node, ++ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, order); ++ if (!page) ++ page = __alloc_pages_reserve(node, flags, order, res, emerg); ++ ++ return page; ++} ++ ++void __free_pages_reserve(struct page *page, int order, ++ struct mem_reserve *res, int emerg); ++ ++/** ++ * free_pages_reserve() - __free_pages() and uncharge against @res ++ * @page - page to free ++ * @order - page order ++ * @res - reserve to uncharge emergency allocations from ++ * @emerg - was this an emergency allocation ++ */ ++static inline ++void free_pages_reserve(struct page *page, int order, ++ struct mem_reserve *res, int emerg) ++{ ++ if (unlikely(page && res && emerg)) ++ __free_pages_reserve(page, order, res, emerg); ++ else ++ __free_pages(page, order); ++} ++ ++#endif /* _LINUX_RESERVE_H */ +--- a/mm/Makefile ++++ b/mm/Makefile +@@ -11,7 +11,7 @@ obj-y := bootmem.o filemap.o mempool.o + maccess.o page_alloc.o page-writeback.o \ + readahead.o swap.o truncate.o vmscan.o shmem.o \ + prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ +- page_isolation.o mm_init.o mmu_context.o \ ++ page_isolation.o mm_init.o mmu_context.o reserve.o \ + $(mmu-y) + obj-y += init-mm.o + +--- /dev/null ++++ b/mm/reserve.c +@@ -0,0 +1,637 @@ ++/* ++ * Memory reserve management. ++ * ++ * Copyright (C) 2007-2008, Red Hat, Inc., ++ * Peter Zijlstra ++ * ++ * Description: ++ * ++ * Manage a set of memory reserves. ++ * ++ * A memory reserve is a reserve for a specified number of object of specified ++ * size. Since memory is managed in pages, this reserve demand is then ++ * translated into a page unit. ++ * ++ * So each reserve has a specified object limit, an object usage count and a ++ * number of pages required to back these objects. ++ * ++ * Usage is charged against a reserve, if the charge fails, the resource must ++ * not be allocated/used. ++ * ++ * The reserves are managed in a tree, and the resource demands (pages and ++ * limit) are propagated up the tree. Obviously the object limit will be ++ * meaningless as soon as the unit starts mixing, but the required page reserve ++ * (being of one unit) is still valid at the root. ++ * ++ * It is the page demand of the root node that is used to set the global ++ * reserve (adjust_memalloc_reserve() which sets zone->pages_emerg). ++ * ++ * As long as a subtree has the same usage unit, an aggregate node can be used ++ * to charge against, instead of the leaf nodes. However, do be consistent with ++ * who is charged, resource usage is not propagated up the tree (for ++ * performance reasons). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "internal.h" ++ ++static DEFINE_MUTEX(mem_reserve_mutex); ++ ++/** ++ * @mem_reserve_root - the global reserve root ++ * ++ * The global reserve is empty, and has no limit unit, it merely ++ * acts as an aggregation point for reserves and an interface to ++ * adjust_memalloc_reserve(). ++ */ ++struct mem_reserve mem_reserve_root = { ++ .children = LIST_HEAD_INIT(mem_reserve_root.children), ++ .siblings = LIST_HEAD_INIT(mem_reserve_root.siblings), ++ .name = "total reserve", ++ .lock = __SPIN_LOCK_UNLOCKED(mem_reserve_root.lock), ++ .waitqueue = __WAIT_QUEUE_HEAD_INITIALIZER(mem_reserve_root.waitqueue), ++}; ++EXPORT_SYMBOL_GPL(mem_reserve_root); ++ ++/** ++ * mem_reserve_init() - initialize a memory reserve object ++ * @res - the new reserve object ++ * @name - a name for this reserve ++ * @parent - when non NULL, the parent to connect to. ++ */ ++void mem_reserve_init(struct mem_reserve *res, const char *name, ++ struct mem_reserve *parent) ++{ ++ memset(res, 0, sizeof(*res)); ++ INIT_LIST_HEAD(&res->children); ++ INIT_LIST_HEAD(&res->siblings); ++ res->name = name; ++ spin_lock_init(&res->lock); ++ init_waitqueue_head(&res->waitqueue); ++ ++ if (parent) ++ mem_reserve_connect(res, parent); ++} ++EXPORT_SYMBOL_GPL(mem_reserve_init); ++ ++/* ++ * propagate the pages and limit changes up the (sub)tree. ++ */ ++static void __calc_reserve(struct mem_reserve *res, long pages, long limit) ++{ ++ unsigned long flags; ++ ++ for ( ; res; res = res->parent) { ++ res->pages += pages; ++ ++ if (limit) { ++ spin_lock_irqsave(&res->lock, flags); ++ res->limit += limit; ++ spin_unlock_irqrestore(&res->lock, flags); ++ } ++ } ++} ++ ++/** ++ * __mem_reserve_add() - primitive to change the size of a reserve ++ * @res - reserve to change ++ * @pages - page delta ++ * @limit - usage limit delta ++ * ++ * Returns -ENOMEM when a size increase is not possible atm. ++ */ ++static int __mem_reserve_add(struct mem_reserve *res, long pages, long limit) ++{ ++ int ret = 0; ++ long reserve; ++ ++ /* ++ * This looks more complex than need be, that is because we handle ++ * the case where @res isn't actually connected to mem_reserve_root. ++ * ++ * So, by propagating the new pages up the (sub)tree and computing ++ * the difference in mem_reserve_root.pages we find if this action ++ * affects the actual reserve. ++ * ++ * The (partial) propagation also makes that mem_reserve_connect() ++ * needs only look at the direct child, since each disconnected ++ * sub-tree is fully up-to-date. ++ */ ++ reserve = mem_reserve_root.pages; ++ __calc_reserve(res, pages, 0); ++ reserve = mem_reserve_root.pages - reserve; ++ ++ if (reserve) { ++ ret = adjust_memalloc_reserve(reserve); ++ if (ret) ++ __calc_reserve(res, -pages, 0); ++ } ++ ++ /* ++ * Delay updating the limits until we've acquired the resources to ++ * back it. ++ */ ++ if (!ret) ++ __calc_reserve(res, 0, limit); ++ ++ return ret; ++} ++ ++/** ++ * __mem_reserve_charge() - primitive to charge object usage of a reserve ++ * @res - reserve to charge ++ * @charge - size of the charge ++ * ++ * Returns non-zero on success, zero on failure. ++ */ ++static ++int __mem_reserve_charge(struct mem_reserve *res, long charge) ++{ ++ unsigned long flags; ++ int ret = 0; ++ ++ spin_lock_irqsave(&res->lock, flags); ++ if (charge < 0 || res->usage + charge < res->limit) { ++ res->usage += charge; ++ if (unlikely(res->usage < 0)) ++ res->usage = 0; ++ ret = 1; ++ } ++ if (charge < 0) ++ wake_up_all(&res->waitqueue); ++ spin_unlock_irqrestore(&res->lock, flags); ++ ++ return ret; ++} ++ ++/** ++ * mem_reserve_connect() - connect a reserve to another in a child-parent relation ++ * @new_child - the reserve node to connect (child) ++ * @node - the reserve node to connect to (parent) ++ * ++ * Connecting a node results in an increase of the reserve by the amount of ++ * pages in @new_child->pages if @node has a connection to mem_reserve_root. ++ * ++ * Returns -ENOMEM when the new connection would increase the reserve (parent ++ * is connected to mem_reserve_root) and there is no memory to do so. ++ * ++ * On error, the child is _NOT_ connected. ++ */ ++int mem_reserve_connect(struct mem_reserve *new_child, struct mem_reserve *node) ++{ ++ int ret; ++ ++ WARN_ON(!new_child->name); ++ ++ mutex_lock(&mem_reserve_mutex); ++ if (new_child->parent) { ++ ret = -EEXIST; ++ goto unlock; ++ } ++ new_child->parent = node; ++ list_add(&new_child->siblings, &node->children); ++ ret = __mem_reserve_add(node, new_child->pages, new_child->limit); ++ if (ret) { ++ new_child->parent = NULL; ++ list_del_init(&new_child->siblings); ++ } ++unlock: ++ mutex_unlock(&mem_reserve_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(mem_reserve_connect); ++ ++/** ++ * mem_reserve_disconnect() - sever a nodes connection to the reserve tree ++ * @node - the node to disconnect ++ * ++ * Disconnecting a node results in a reduction of the reserve by @node->pages ++ * if node had a connection to mem_reserve_root. ++ */ ++void mem_reserve_disconnect(struct mem_reserve *node) ++{ ++ int ret; ++ ++ BUG_ON(!node->parent); ++ ++ mutex_lock(&mem_reserve_mutex); ++ if (!node->parent) { ++ ret = -ENOENT; ++ goto unlock; ++ } ++ ret = __mem_reserve_add(node->parent, -node->pages, -node->limit); ++ if (!ret) { ++ node->parent = NULL; ++ list_del_init(&node->siblings); ++ } ++unlock: ++ mutex_unlock(&mem_reserve_mutex); ++ ++ /* ++ * We cannot fail to shrink the reserves, can we? ++ */ ++ WARN_ON(ret); ++} ++EXPORT_SYMBOL_GPL(mem_reserve_disconnect); ++ ++#ifdef CONFIG_PROC_FS ++ ++/* ++ * Simple output of the reserve tree in: /proc/reserve_info ++ * Example: ++ * ++ * localhost ~ # cat /proc/reserve_info ++ * 1:0 "total reserve" 6232K 0/278581 ++ * 2:1 "total network reserve" 6232K 0/278581 ++ * 3:2 "network TX reserve" 212K 0/53 ++ * 4:3 "protocol TX pages" 212K 0/53 ++ * 5:2 "network RX reserve" 6020K 0/278528 ++ * 6:5 "IPv4 route cache" 5508K 0/16384 ++ * 7:5 "SKB data reserve" 512K 0/262144 ++ * 8:7 "IPv4 fragment cache" 512K 0/262144 ++ */ ++ ++static void mem_reserve_show_item(struct seq_file *m, struct mem_reserve *res, ++ unsigned int parent, unsigned int *id) ++{ ++ struct mem_reserve *child; ++ unsigned int my_id = ++*id; ++ ++ seq_printf(m, "%d:%d \"%s\" %ldK %ld/%ld\n", ++ my_id, parent, res->name, ++ res->pages << (PAGE_SHIFT - 10), ++ res->usage, res->limit); ++ ++ list_for_each_entry(child, &res->children, siblings) ++ mem_reserve_show_item(m, child, my_id, id); ++} ++ ++static int mem_reserve_show(struct seq_file *m, void *v) ++{ ++ unsigned int ident = 0; ++ ++ mutex_lock(&mem_reserve_mutex); ++ mem_reserve_show_item(m, &mem_reserve_root, ident, &ident); ++ mutex_unlock(&mem_reserve_mutex); ++ ++ return 0; ++} ++ ++static int mem_reserve_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, mem_reserve_show, NULL); ++} ++ ++static const struct file_operations mem_reserve_opterations = { ++ .open = mem_reserve_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static __init int mem_reserve_proc_init(void) ++{ ++ proc_create("reserve_info", S_IRUSR, NULL, &mem_reserve_opterations); ++ return 0; ++} ++ ++module_init(mem_reserve_proc_init); ++ ++#endif ++ ++/* ++ * alloc_page helpers ++ */ ++ ++/** ++ * mem_reserve_pages_set() - set reserves size in pages ++ * @res - reserve to set ++ * @pages - size in pages to set it to ++ * ++ * Returns -ENOMEM when it fails to set the reserve. On failure the old size ++ * is preserved. ++ */ ++int mem_reserve_pages_set(struct mem_reserve *res, long pages) ++{ ++ int ret; ++ ++ mutex_lock(&mem_reserve_mutex); ++ pages -= res->pages; ++ ret = __mem_reserve_add(res, pages, pages * PAGE_SIZE); ++ mutex_unlock(&mem_reserve_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(mem_reserve_pages_set); ++ ++/** ++ * mem_reserve_pages_add() - change the size in a relative way ++ * @res - reserve to change ++ * @pages - number of pages to add (or subtract when negative) ++ * ++ * Similar to mem_reserve_pages_set, except that the argument is relative ++ * instead of absolute. ++ * ++ * Returns -ENOMEM when it fails to increase. ++ */ ++int mem_reserve_pages_add(struct mem_reserve *res, long pages) ++{ ++ int ret; ++ ++ mutex_lock(&mem_reserve_mutex); ++ ret = __mem_reserve_add(res, pages, pages * PAGE_SIZE); ++ mutex_unlock(&mem_reserve_mutex); ++ ++ return ret; ++} ++ ++/** ++ * mem_reserve_pages_charge() - charge page usage to a reserve ++ * @res - reserve to charge ++ * @pages - size to charge ++ * ++ * Returns non-zero on success. ++ */ ++int mem_reserve_pages_charge(struct mem_reserve *res, long pages) ++{ ++ return __mem_reserve_charge(res, pages * PAGE_SIZE); ++} ++EXPORT_SYMBOL_GPL(mem_reserve_pages_charge); ++ ++/* ++ * kmalloc helpers ++ */ ++ ++/** ++ * mem_reserve_kmalloc_set() - set this reserve to bytes worth of kmalloc ++ * @res - reserve to change ++ * @bytes - size in bytes to reserve ++ * ++ * Returns -ENOMEM on failure. ++ */ ++int mem_reserve_kmalloc_set(struct mem_reserve *res, long bytes) ++{ ++ int ret; ++ long pages; ++ ++ mutex_lock(&mem_reserve_mutex); ++ pages = kmalloc_estimate_bytes(GFP_ATOMIC, bytes); ++ pages -= res->pages; ++ bytes -= res->limit; ++ ret = __mem_reserve_add(res, pages, bytes); ++ mutex_unlock(&mem_reserve_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(mem_reserve_kmalloc_set); ++ ++/** ++ * mem_reserve_kmalloc_charge() - charge bytes to a reserve ++ * @res - reserve to charge ++ * @bytes - bytes to charge ++ * ++ * Returns non-zero on success. ++ */ ++int mem_reserve_kmalloc_charge(struct mem_reserve *res, long bytes) ++{ ++ if (bytes < 0) ++ bytes = -roundup_pow_of_two(-bytes); ++ else ++ bytes = roundup_pow_of_two(bytes); ++ ++ return __mem_reserve_charge(res, bytes); ++} ++EXPORT_SYMBOL_GPL(mem_reserve_kmalloc_charge); ++ ++/* ++ * kmem_cache helpers ++ */ ++ ++/** ++ * mem_reserve_kmem_cache_set() - set reserve to @objects worth of kmem_cache_alloc of @s ++ * @res - reserve to set ++ * @s - kmem_cache to reserve from ++ * @objects - number of objects to reserve ++ * ++ * Returns -ENOMEM on failure. ++ */ ++int mem_reserve_kmem_cache_set(struct mem_reserve *res, struct kmem_cache *s, ++ int objects) ++{ ++ int ret; ++ long pages, bytes; ++ ++ mutex_lock(&mem_reserve_mutex); ++ pages = kmem_alloc_estimate(s, GFP_ATOMIC, objects); ++ pages -= res->pages; ++ bytes = objects * kmem_cache_size(s) - res->limit; ++ ret = __mem_reserve_add(res, pages, bytes); ++ mutex_unlock(&mem_reserve_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(mem_reserve_kmem_cache_set); ++ ++/** ++ * mem_reserve_kmem_cache_charge() - charge (or uncharge) usage of objs ++ * @res - reserve to charge ++ * @objs - objects to charge for ++ * ++ * Returns non-zero on success. ++ */ ++int mem_reserve_kmem_cache_charge(struct mem_reserve *res, struct kmem_cache *s, ++ long objs) ++{ ++ return __mem_reserve_charge(res, objs * kmem_cache_size(s)); ++} ++EXPORT_SYMBOL_GPL(mem_reserve_kmem_cache_charge); ++ ++/* ++ * Alloc wrappers. ++ * ++ * Actual usage is commented in linux/reserve.h where the interface functions ++ * live. Furthermore, the code is 3 instances of the same paradigm, hence only ++ * the first contains extensive comments. ++ */ ++ ++/* ++ * kmalloc/kfree ++ */ ++ ++void *___kmalloc_reserve(size_t size, gfp_t flags, int node, unsigned long ip, ++ struct mem_reserve *res, int *emerg) ++{ ++ void *obj; ++ gfp_t gfp; ++ ++ /* ++ * Try a regular allocation, when that fails and we're not entitled ++ * to the reserves, fail. ++ */ ++ gfp = flags | __GFP_NOMEMALLOC | __GFP_NOWARN; ++ obj = kmalloc_node_track_caller(size, gfp, node); ++ ++ if (obj || !(gfp_to_alloc_flags(flags) & ALLOC_NO_WATERMARKS)) ++ goto out; ++ ++ /* ++ * If we were given a reserve to charge against, try that. ++ */ ++ if (res && !mem_reserve_kmalloc_charge(res, size)) { ++ /* ++ * If we failed to charge and we're not allowed to wait for ++ * it to succeed, bail. ++ */ ++ if (!(flags & __GFP_WAIT)) ++ goto out; ++ ++ /* ++ * Wait for a successfull charge against the reserve. All ++ * uncharge operations against this reserve will wake us up. ++ */ ++ wait_event(res->waitqueue, ++ mem_reserve_kmalloc_charge(res, size)); ++ ++ /* ++ * After waiting for it, again try a regular allocation. ++ * Pressure could have lifted during our sleep. If this ++ * succeeds, uncharge the reserve. ++ */ ++ obj = kmalloc_node_track_caller(size, gfp, node); ++ if (obj) { ++ mem_reserve_kmalloc_charge(res, -size); ++ goto out; ++ } ++ } ++ ++ /* ++ * Regular allocation failed, and we've successfully charged our ++ * requested usage against the reserve. Do the emergency allocation. ++ */ ++ obj = kmalloc_node_track_caller(size, flags, node); ++ WARN_ON(!obj); ++ if (emerg) ++ *emerg = 1; ++ ++out: ++ return obj; ++} ++ ++void __kfree_reserve(void *obj, struct mem_reserve *res, int emerg) ++{ ++ /* ++ * ksize gives the full allocated size vs the requested size we used to ++ * charge; however since we round up to the nearest power of two, this ++ * should all work nicely. ++ */ ++ size_t size = ksize(obj); ++ ++ kfree(obj); ++ /* ++ * Free before uncharge, this ensures memory is actually present when ++ * a subsequent charge succeeds. ++ */ ++ mem_reserve_kmalloc_charge(res, -size); ++} ++ ++/* ++ * kmem_cache_alloc/kmem_cache_free ++ */ ++ ++void *__kmem_cache_alloc_reserve(struct kmem_cache *s, gfp_t flags, int node, ++ struct mem_reserve *res, int *emerg) ++{ ++ void *obj; ++ gfp_t gfp; ++ ++ gfp = flags | __GFP_NOMEMALLOC | __GFP_NOWARN; ++ obj = kmem_cache_alloc_node(s, gfp, node); ++ ++ if (obj || !(gfp_to_alloc_flags(flags) & ALLOC_NO_WATERMARKS)) ++ goto out; ++ ++ if (res && !mem_reserve_kmem_cache_charge(res, s, 1)) { ++ if (!(flags & __GFP_WAIT)) ++ goto out; ++ ++ wait_event(res->waitqueue, ++ mem_reserve_kmem_cache_charge(res, s, 1)); ++ ++ obj = kmem_cache_alloc_node(s, gfp, node); ++ if (obj) { ++ mem_reserve_kmem_cache_charge(res, s, -1); ++ goto out; ++ } ++ } ++ ++ obj = kmem_cache_alloc_node(s, flags, node); ++ WARN_ON(!obj); ++ if (emerg) ++ *emerg = 1; ++ ++out: ++ return obj; ++} ++ ++void __kmem_cache_free_reserve(struct kmem_cache *s, void *obj, ++ struct mem_reserve *res, int emerg) ++{ ++ kmem_cache_free(s, obj); ++ mem_reserve_kmem_cache_charge(res, s, -1); ++} ++ ++/* ++ * alloc_pages/free_pages ++ */ ++ ++struct page *__alloc_pages_reserve(int node, gfp_t flags, int order, ++ struct mem_reserve *res, int *emerg) ++{ ++ struct page *page; ++ gfp_t gfp; ++ long pages = 1 << order; ++ ++ gfp = flags | __GFP_NOMEMALLOC | __GFP_NOWARN; ++ page = alloc_pages_node(node, gfp, order); ++ ++ if (page || !(gfp_to_alloc_flags(flags) & ALLOC_NO_WATERMARKS)) ++ goto out; ++ ++ if (res && !mem_reserve_pages_charge(res, pages)) { ++ if (!(flags & __GFP_WAIT)) ++ goto out; ++ ++ wait_event(res->waitqueue, ++ mem_reserve_pages_charge(res, pages)); ++ ++ page = alloc_pages_node(node, gfp, order); ++ if (page) { ++ mem_reserve_pages_charge(res, -pages); ++ goto out; ++ } ++ } ++ ++ page = alloc_pages_node(node, flags, order); ++ WARN_ON(!page); ++ if (emerg) ++ *emerg = 1; ++ ++out: ++ return page; ++} ++ ++void __free_pages_reserve(struct page *page, int order, ++ struct mem_reserve *res, int emerg) ++{ ++ __free_pages(page, order); ++ mem_reserve_pages_charge(res, -(1 << order)); ++} +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -2810,6 +2810,7 @@ void *__kmalloc(size_t size, gfp_t flags + } + EXPORT_SYMBOL(__kmalloc); + ++#ifdef CONFIG_NUMA + static void *kmalloc_large_node(size_t size, gfp_t flags, int node) + { + struct page *page; +@@ -2824,7 +2825,6 @@ static void *kmalloc_large_node(size_t s + return ptr; + } + +-#ifdef CONFIG_NUMA + void *__kmalloc_node(size_t size, gfp_t flags, int node) + { + struct kmem_cache *s; diff --git a/patches.suse/SoN-12-mm-selinux-emergency.patch b/patches.suse/SoN-12-mm-selinux-emergency.patch new file mode 100644 index 0000000..5c20707 --- /dev/null +++ b/patches.suse/SoN-12-mm-selinux-emergency.patch @@ -0,0 +1,24 @@ +From: Peter Zijlstra +Subject: [PATCH 12/31] selinux: tag avc cache alloc as non-critical +Patch-mainline: not yet + +Failing to allocate a cache entry will only harm performance not correctness. +Do not consume valuable reserve pages for something like that. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + security/selinux/avc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/security/selinux/avc.c ++++ b/security/selinux/avc.c +@@ -284,7 +284,7 @@ static struct avc_node *avc_alloc_node(v + { + struct avc_node *node; + +- node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC); ++ node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC|__GFP_NOMEMALLOC); + if (!node) + goto out; + diff --git a/patches.suse/SoN-13-net-ps_rx.patch b/patches.suse/SoN-13-net-ps_rx.patch new file mode 100644 index 0000000..2166ff4 --- /dev/null +++ b/patches.suse/SoN-13-net-ps_rx.patch @@ -0,0 +1,213 @@ +From: Peter Zijlstra +Subject: [PATCH 13/31] net: packet split receive api +Patch-mainline: Not yet + +Add some packet-split receive hooks. + +For one this allows to do NUMA node affine page allocs. Later on these hooks +will be extended to do emergency reserve allocations for fragments. + +Thanks to Jiri Bohac for fixing a bug in bnx2. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Jiri Bohac +Signed-off-by: Suresh Jayaraman +--- + drivers/net/bnx2.c | 9 +++------ + drivers/net/e1000e/netdev.c | 7 ++----- + drivers/net/igb/igb_main.c | 6 +----- + drivers/net/ixgbe/ixgbe_main.c | 14 ++++++-------- + drivers/net/sky2.c | 16 ++++++---------- + include/linux/skbuff.h | 3 +++ + 6 files changed, 21 insertions(+), 34 deletions(-) + +--- a/drivers/net/bnx2.c ++++ b/drivers/net/bnx2.c +@@ -2656,7 +2656,7 @@ bnx2_alloc_rx_page(struct bnx2 *bp, stru + struct sw_pg *rx_pg = &rxr->rx_pg_ring[index]; + struct rx_bd *rxbd = + &rxr->rx_pg_desc_ring[RX_RING(index)][RX_IDX(index)]; +- struct page *page = alloc_page(GFP_ATOMIC); ++ struct page *page = netdev_alloc_page(bp->dev); + + if (!page) + return -ENOMEM; +@@ -2686,7 +2686,7 @@ bnx2_free_rx_page(struct bnx2 *bp, struc + pci_unmap_page(bp->pdev, pci_unmap_addr(rx_pg, mapping), PAGE_SIZE, + PCI_DMA_FROMDEVICE); + +- __free_page(page); ++ netdev_free_page(bp->dev, page); + rx_pg->page = NULL; + } + +@@ -3019,7 +3019,7 @@ bnx2_rx_skb(struct bnx2 *bp, struct bnx2 + if (i == pages - 1) + frag_len -= 4; + +- skb_fill_page_desc(skb, i, rx_pg->page, 0, frag_len); ++ skb_add_rx_frag(skb, i, rx_pg->page, 0, frag_len); + rx_pg->page = NULL; + + err = bnx2_alloc_rx_page(bp, rxr, +@@ -3036,9 +3036,6 @@ bnx2_rx_skb(struct bnx2 *bp, struct bnx2 + PAGE_SIZE, PCI_DMA_FROMDEVICE); + + frag_size -= frag_len; +- skb->data_len += frag_len; +- skb->truesize += frag_len; +- skb->len += frag_len; + + pg_prod = NEXT_RX_BD(pg_prod); + pg_cons = RX_PG_RING_IDX(NEXT_RX_BD(pg_cons)); +--- a/drivers/net/e1000e/netdev.c ++++ b/drivers/net/e1000e/netdev.c +@@ -241,7 +241,7 @@ static void e1000_alloc_rx_buffers_ps(st + continue; + } + if (!ps_page->page) { +- ps_page->page = alloc_page(GFP_ATOMIC); ++ ps_page->page = netdev_alloc_page(netdev); + if (!ps_page->page) { + adapter->alloc_rx_buff_failed++; + goto no_buffers; +@@ -834,11 +834,8 @@ static bool e1000_clean_rx_irq_ps(struct + pci_unmap_page(pdev, ps_page->dma, PAGE_SIZE, + PCI_DMA_FROMDEVICE); + ps_page->dma = 0; +- skb_fill_page_desc(skb, j, ps_page->page, 0, length); ++ skb_add_rx_frag(skb, j, ps_page->page, 0, length); + ps_page->page = NULL; +- skb->len += length; +- skb->data_len += length; +- skb->truesize += length; + } + + /* strip the ethernet crc, problem is we're using pages now so +--- a/drivers/net/igb/igb_main.c ++++ b/drivers/net/igb/igb_main.c +@@ -5251,7 +5251,7 @@ static bool igb_clean_rx_irq_adv(struct + PAGE_SIZE / 2, PCI_DMA_FROMDEVICE); + buffer_info->page_dma = 0; + +- skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags++, ++ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags++, + buffer_info->page, + buffer_info->page_offset, + length); +@@ -5261,10 +5261,6 @@ static bool igb_clean_rx_irq_adv(struct + buffer_info->page = NULL; + else + get_page(buffer_info->page); +- +- skb->len += length; +- skb->data_len += length; +- skb->truesize += length; + } + + if (!(staterr & E1000_RXD_STAT_EOP)) { +--- a/drivers/net/ixgbe/ixgbe_main.c ++++ b/drivers/net/ixgbe/ixgbe_main.c +@@ -696,6 +696,7 @@ static void ixgbe_alloc_rx_buffers(struc + int cleaned_count) + { + struct pci_dev *pdev = adapter->pdev; ++ struct net_device *netdev = adapter->netdev; + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *bi; + unsigned int i; +@@ -709,7 +710,7 @@ static void ixgbe_alloc_rx_buffers(struc + if (!bi->page_dma && + (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED)) { + if (!bi->page) { +- bi->page = alloc_page(GFP_ATOMIC); ++ bi->page = netdev_alloc_page(netdev); + if (!bi->page) { + adapter->alloc_rx_page_failed++; + goto no_buffers; +@@ -896,10 +897,10 @@ static bool ixgbe_clean_rx_irq(struct ix + pci_unmap_page(pdev, rx_buffer_info->page_dma, + PAGE_SIZE / 2, PCI_DMA_FROMDEVICE); + rx_buffer_info->page_dma = 0; +- skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, +- rx_buffer_info->page, +- rx_buffer_info->page_offset, +- upper_len); ++ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, ++ rx_buffer_info->page, ++ rx_buffer_info->page_offset, ++ upper_len); + + if ((rx_ring->rx_buf_len > (PAGE_SIZE / 2)) || + (page_count(rx_buffer_info->page) != 1)) +@@ -907,9 +908,6 @@ static bool ixgbe_clean_rx_irq(struct ix + else + get_page(rx_buffer_info->page); + +- skb->len += upper_len; +- skb->data_len += upper_len; +- skb->truesize += upper_len; + } + + i++; +--- a/drivers/net/sky2.c ++++ b/drivers/net/sky2.c +@@ -1344,7 +1344,7 @@ static struct sk_buff *sky2_rx_alloc(str + skb_reserve(skb, NET_IP_ALIGN); + + for (i = 0; i < sky2->rx_nfrags; i++) { +- struct page *page = alloc_page(GFP_ATOMIC); ++ struct page *page = netdev_alloc_page(sky2->netdev); + + if (!page) + goto free_partial; +@@ -2304,8 +2304,8 @@ static struct sk_buff *receive_copy(stru + } + + /* Adjust length of skb with fragments to match received data */ +-static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space, +- unsigned int length) ++static void skb_put_frags(struct sky2_port *sky2, struct sk_buff *skb, ++ unsigned int hdr_space, unsigned int length) + { + int i, num_frags; + unsigned int size; +@@ -2322,15 +2322,11 @@ static void skb_put_frags(struct sk_buff + + if (length == 0) { + /* don't need this page */ +- __free_page(frag->page); ++ netdev_free_page(sky2->netdev, frag->page); + --skb_shinfo(skb)->nr_frags; + } else { + size = min(length, (unsigned) PAGE_SIZE); +- +- frag->size = size; +- skb->data_len += size; +- skb->truesize += size; +- skb->len += size; ++ skb_add_rx_frag(skb, i, frag->page, 0, size); + length -= size; + } + } +@@ -2358,7 +2354,7 @@ static struct sk_buff *receive_new(struc + *re = nre; + + if (skb_shinfo(skb)->nr_frags) +- skb_put_frags(skb, hdr_space, length); ++ skb_put_frags(sky2, skb, hdr_space, length); + else + skb_put(skb, length); + return skb; +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -1069,6 +1069,9 @@ static inline void skb_fill_page_desc(st + extern void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, + int off, int size); + ++extern void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, ++ int off, int size); ++ + #define SKB_PAGE_ASSERT(skb) BUG_ON(skb_shinfo(skb)->nr_frags) + #define SKB_FRAG_ASSERT(skb) BUG_ON(skb_has_frags(skb)) + #define SKB_LINEAR_ASSERT(skb) BUG_ON(skb_is_nonlinear(skb)) diff --git a/patches.suse/SoN-14-net-sk_allocation.patch b/patches.suse/SoN-14-net-sk_allocation.patch new file mode 100644 index 0000000..47f616f --- /dev/null +++ b/patches.suse/SoN-14-net-sk_allocation.patch @@ -0,0 +1,156 @@ +From: Peter Zijlstra +Subject: [PATCH 14/31] net: sk_allocation() - concentrate socket related allocations +Patch-mainline: Not yet + +Introduce sk_allocation(), this function allows to inject sock specific +flags to each sock related allocation. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/net/sock.h | 5 +++++ + net/ipv4/tcp.c | 3 ++- + net/ipv4/tcp_output.c | 11 ++++++----- + net/ipv6/tcp_ipv6.c | 15 +++++++++++---- + 4 files changed, 24 insertions(+), 10 deletions(-) + +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -556,6 +556,11 @@ static inline int sock_flag(struct sock + return test_bit(flag, &sk->sk_flags); + } + ++static inline gfp_t sk_allocation(struct sock *sk, gfp_t gfp_mask) ++{ ++ return gfp_mask; ++} ++ + static inline void sk_acceptq_removed(struct sock *sk) + { + sk->sk_ack_backlog--; +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -682,7 +682,8 @@ struct sk_buff *sk_stream_alloc_skb(stru + /* The TCP header must be at least 32-bit aligned. */ + size = ALIGN(size, 4); + +- skb = alloc_skb_fclone(size + sk->sk_prot->max_header, gfp); ++ skb = alloc_skb_fclone(size + sk->sk_prot->max_header, ++ sk_allocation(sk, gfp)); + if (skb) { + if (sk_wmem_schedule(sk, skb->truesize)) { + /* +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -2306,7 +2306,7 @@ void tcp_send_fin(struct sock *sk) + /* Socket is locked, keep trying until memory is available. */ + for (;;) { + skb = alloc_skb_fclone(MAX_TCP_HEADER, +- sk->sk_allocation); ++ sk_allocation(sk, GFP_KERNEL)); + if (skb) + break; + yield(); +@@ -2332,7 +2332,7 @@ void tcp_send_active_reset(struct sock * + struct sk_buff *skb; + + /* NOTE: No TCP options attached and we never retransmit this. */ +- skb = alloc_skb(MAX_TCP_HEADER, priority); ++ skb = alloc_skb(MAX_TCP_HEADER, sk_allocation(sk, priority)); + if (!skb) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED); + return; +@@ -2405,7 +2405,8 @@ struct sk_buff *tcp_make_synack(struct s + + if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired) + s_data_desired = cvp->s_data_desired; +- skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15 + s_data_desired, 1, GFP_ATOMIC); ++ skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15 + s_data_desired, 1, ++ sk_allocation(sk, GFP_ATOMIC)); + if (skb == NULL) + return NULL; + +@@ -2685,7 +2686,7 @@ void tcp_send_ack(struct sock *sk) + * tcp_transmit_skb() will set the ownership to this + * sock. + */ +- buff = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); ++ buff = alloc_skb(MAX_TCP_HEADER, sk_allocation(sk, GFP_ATOMIC)); + if (buff == NULL) { + inet_csk_schedule_ack(sk); + inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN; +@@ -2720,7 +2721,7 @@ static int tcp_xmit_probe_skb(struct soc + struct sk_buff *skb; + + /* We don't queue it, tcp_transmit_skb() sets ownership. */ +- skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); ++ skb = alloc_skb(MAX_TCP_HEADER, sk_allocation(sk, GFP_ATOMIC)); + if (skb == NULL) + return -1; + +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -594,7 +594,8 @@ static int tcp_v6_md5_do_add(struct sock + } else { + /* reallocate new list if current one is full. */ + if (!tp->md5sig_info) { +- tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC); ++ tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), ++ sk_allocation(sk, GFP_ATOMIC)); + if (!tp->md5sig_info) { + kfree(newkey); + return -ENOMEM; +@@ -607,7 +608,8 @@ static int tcp_v6_md5_do_add(struct sock + } + if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) { + keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) * +- (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC); ++ (tp->md5sig_info->entries6 + 1)), ++ sk_allocation(sk, GFP_ATOMIC)); + + if (!keys) { + tcp_free_md5sig_pool(); +@@ -731,7 +733,8 @@ static int tcp_v6_parse_md5_keys (struct + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_md5sig_info *p; + +- p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL); ++ p = kzalloc(sizeof(struct tcp_md5sig_info), ++ sk_allocation(sk, GFP_KERNEL)); + if (!p) + return -ENOMEM; + +@@ -998,6 +1001,7 @@ static void tcp_v6_send_response(struct + unsigned int tot_len = sizeof(struct tcphdr); + struct dst_entry *dst; + __be32 *topt; ++ gfp_t gfp_mask = GFP_ATOMIC; + + if (ts) + tot_len += TCPOLEN_TSTAMP_ALIGNED; +@@ -1007,7 +1011,7 @@ static void tcp_v6_send_response(struct + #endif + + buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len, +- GFP_ATOMIC); ++ gfp_mask); + if (buff == NULL) + return; + +@@ -1085,6 +1089,7 @@ static void tcp_v6_send_reset(struct soc + struct tcphdr *th = tcp_hdr(skb); + u32 seq = 0, ack_seq = 0; + struct tcp_md5sig_key *key = NULL; ++ gfp_t gfp_mask = GFP_ATOMIC; + + if (th->rst) + return; +@@ -1096,6 +1101,8 @@ static void tcp_v6_send_reset(struct soc + if (sk) + key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr); + #endif ++ if (sk) ++ gfp_mask = sk_allocation(sk, gfp_mask); + + if (th->ack) + seq = ntohl(th->ack_seq); diff --git a/patches.suse/SoN-15-netvm-reserve.patch b/patches.suse/SoN-15-netvm-reserve.patch new file mode 100644 index 0000000..df242ca --- /dev/null +++ b/patches.suse/SoN-15-netvm-reserve.patch @@ -0,0 +1,254 @@ +From: Peter Zijlstra +Subject: [PATCH 15/31] netvm: network reserve infrastructure +Patch-mainline: not yet + +Provide the basic infrastructure to reserve and charge/account network memory. + +We provide the following reserve tree: + +1) total network reserve +2) network TX reserve +3) protocol TX pages +4) network RX reserve +5) SKB data reserve + +[1] is used to make all the network reserves a single subtree, for easy +manipulation. + +[2] and [4] are merely for eastetic reasons. + +The TX pages reserve [3] is assumed bounded by it being the upper bound of +memory that can be used for sending pages (not quite true, but good enough) + +The SKB reserve [5] is an aggregate reserve, which is used to charge SKB data +against in the fallback path. + +The consumers for these reserves are sockets marked with: + SOCK_MEMALLOC + +Such sockets are to be used to service the VM (iow. to swap over). They +must be handled kernel side, exposing such a socket to user-space is a BUG. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/net/sock.h | 43 ++++++++++++++++++++- + net/Kconfig | 3 + + net/core/sock.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 152 insertions(+), 1 deletion(-) + +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -51,6 +51,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -525,6 +526,7 @@ enum sock_flags { + SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */ + SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ + SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */ ++ SOCK_MEMALLOC, /* the VM depends on us - make sure we're serviced */ + SOCK_TIMESTAMPING_TX_HARDWARE, /* %SOF_TIMESTAMPING_TX_HARDWARE */ + SOCK_TIMESTAMPING_TX_SOFTWARE, /* %SOF_TIMESTAMPING_TX_SOFTWARE */ + SOCK_TIMESTAMPING_RX_HARDWARE, /* %SOF_TIMESTAMPING_RX_HARDWARE */ +@@ -556,9 +558,48 @@ static inline int sock_flag(struct sock + return test_bit(flag, &sk->sk_flags); + } + ++static inline int sk_has_memalloc(struct sock *sk) ++{ ++ return sock_flag(sk, SOCK_MEMALLOC); ++} ++ ++extern struct mem_reserve net_rx_reserve; ++extern struct mem_reserve net_skb_reserve; ++ ++#ifdef CONFIG_NETVM ++/* ++ * Guestimate the per request queue TX upper bound. ++ * ++ * Max packet size is 64k, and we need to reserve that much since the data ++ * might need to bounce it. Double it to be on the safe side. ++ */ ++#define TX_RESERVE_PAGES DIV_ROUND_UP(2*65536, PAGE_SIZE) ++ ++extern int memalloc_socks; ++ ++static inline int sk_memalloc_socks(void) ++{ ++ return memalloc_socks; ++} ++ ++extern int sk_adjust_memalloc(int socks, long tx_reserve_pages); ++extern int sk_set_memalloc(struct sock *sk); ++extern int sk_clear_memalloc(struct sock *sk); ++#else ++static inline int sk_memalloc_socks(void) ++{ ++ return 0; ++} ++ ++static inline int sk_clear_memalloc(struct sock *sk) ++{ ++ return 0; ++} ++#endif ++ + static inline gfp_t sk_allocation(struct sock *sk, gfp_t gfp_mask) + { +- return gfp_mask; ++ return gfp_mask | (sk->sk_allocation & __GFP_MEMALLOC); + } + + static inline void sk_acceptq_removed(struct sock *sk) +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -276,4 +276,7 @@ source "net/wimax/Kconfig" + source "net/rfkill/Kconfig" + source "net/9p/Kconfig" + ++config NETVM ++ def_bool n ++ + endif # if NET +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -110,6 +110,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -217,6 +218,105 @@ __u32 sysctl_rmem_default __read_mostly + int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512); + EXPORT_SYMBOL(sysctl_optmem_max); + ++static struct mem_reserve net_reserve; ++struct mem_reserve net_rx_reserve; ++EXPORT_SYMBOL_GPL(net_rx_reserve); /* modular ipv6 only */ ++struct mem_reserve net_skb_reserve; ++EXPORT_SYMBOL_GPL(net_skb_reserve); /* modular ipv6 only */ ++static struct mem_reserve net_tx_reserve; ++static struct mem_reserve net_tx_pages; ++ ++#ifdef CONFIG_NETVM ++static DEFINE_MUTEX(memalloc_socks_lock); ++int memalloc_socks; ++ ++/** ++ * sk_adjust_memalloc - adjust the global memalloc reserve for critical RX ++ * @socks: number of new %SOCK_MEMALLOC sockets ++ * @tx_resserve_pages: number of pages to (un)reserve for TX ++ * ++ * This function adjusts the memalloc reserve based on system demand. ++ * The RX reserve is a limit, and only added once, not for each socket. ++ * ++ * NOTE: ++ * @tx_reserve_pages is an upper-bound of memory used for TX hence ++ * we need not account the pages like we do for RX pages. ++ */ ++int sk_adjust_memalloc(int socks, long tx_reserve_pages) ++{ ++ int err; ++ ++ mutex_lock(&memalloc_socks_lock); ++ err = mem_reserve_pages_add(&net_tx_pages, tx_reserve_pages); ++ if (err) ++ goto unlock; ++ ++ /* ++ * either socks is positive and we need to check for 0 -> !0 ++ * transition and connect the reserve tree when we observe it. ++ */ ++ if (!memalloc_socks && socks > 0) { ++ err = mem_reserve_connect(&net_reserve, &mem_reserve_root); ++ if (err) { ++ /* ++ * if we failed to connect the tree, undo the tx ++ * reserve so that failure has no side effects. ++ */ ++ mem_reserve_pages_add(&net_tx_pages, -tx_reserve_pages); ++ goto unlock; ++ } ++ } ++ memalloc_socks += socks; ++ /* ++ * or socks is negative and we must observe the !0 -> 0 transition ++ * and disconnect the reserve tree. ++ */ ++ if (!memalloc_socks && socks) ++ mem_reserve_disconnect(&net_reserve); ++ ++unlock: ++ mutex_unlock(&memalloc_socks_lock); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(sk_adjust_memalloc); ++ ++/** ++ * sk_set_memalloc - sets %SOCK_MEMALLOC ++ * @sk: socket to set it on ++ * ++ * Set %SOCK_MEMALLOC on a socket and increase the memalloc reserve ++ * accordingly. ++ */ ++int sk_set_memalloc(struct sock *sk) ++{ ++ int set = sock_flag(sk, SOCK_MEMALLOC); ++ ++ if (!set) { ++ int err = sk_adjust_memalloc(1, 0); ++ if (err) ++ return err; ++ ++ sock_set_flag(sk, SOCK_MEMALLOC); ++ sk->sk_allocation |= __GFP_MEMALLOC; ++ } ++ return !set; ++} ++EXPORT_SYMBOL_GPL(sk_set_memalloc); ++ ++int sk_clear_memalloc(struct sock *sk) ++{ ++ int set = sock_flag(sk, SOCK_MEMALLOC); ++ if (set) { ++ sk_adjust_memalloc(-1, 0); ++ sock_reset_flag(sk, SOCK_MEMALLOC); ++ sk->sk_allocation &= ~__GFP_MEMALLOC; ++ } ++ return set; ++} ++EXPORT_SYMBOL_GPL(sk_clear_memalloc); ++#endif ++ + static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen) + { + struct timeval tv; +@@ -1074,6 +1174,7 @@ static void __sk_free(struct sock *sk) + { + struct sk_filter *filter; + ++ sk_clear_memalloc(sk); + if (sk->sk_destruct) + sk->sk_destruct(sk); + +@@ -1249,6 +1350,12 @@ void __init sk_init(void) + sysctl_wmem_max = 131071; + sysctl_rmem_max = 131071; + } ++ ++ mem_reserve_init(&net_reserve, "total network reserve", NULL); ++ mem_reserve_init(&net_rx_reserve, "network RX reserve", &net_reserve); ++ mem_reserve_init(&net_skb_reserve, "SKB data reserve", &net_rx_reserve); ++ mem_reserve_init(&net_tx_reserve, "network TX reserve", &net_reserve); ++ mem_reserve_init(&net_tx_pages, "protocol TX pages", &net_tx_reserve); + } + + /* diff --git a/patches.suse/SoN-16-netvm-reserve-inet.patch b/patches.suse/SoN-16-netvm-reserve-inet.patch new file mode 100644 index 0000000..0e665ee --- /dev/null +++ b/patches.suse/SoN-16-netvm-reserve-inet.patch @@ -0,0 +1,504 @@ +From: Peter Zijlstra +Subject: [PATCH 16/31] netvm: INET reserves. +Patch-mainline: Not yet + +Add reserves for INET. + +The two big users seem to be the route cache and ip-fragment cache. + +Reserve the route cache under generic RX reserve, its usage is bounded by +the high reclaim watermark, and thus does not need further accounting. + +Reserve the ip-fragement caches under SKB data reserve, these add to the +SKB RX limit. By ensuring we can at least receive as much data as fits in +the reassmbly line we avoid fragment attack deadlocks. + +Adds to the reserve tree: + + total network reserve + network TX reserve + protocol TX pages + network RX reserve ++ IPv6 route cache ++ IPv4 route cache + SKB data reserve ++ IPv6 fragment cache ++ IPv4 fragment cache + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/net/inet_frag.h | 7 +++++ + include/net/netns/ipv6.h | 4 +++ + net/ipv4/inet_fragment.c | 3 ++ + net/ipv4/ip_fragment.c | 56 +++++++++++++++++++++++++++++++++++++++++++-- + net/ipv4/route.c | 58 +++++++++++++++++++++++++++++++++++++++++++++-- + net/ipv6/reassembly.c | 55 ++++++++++++++++++++++++++++++++++++++++++-- + net/ipv6/route.c | 47 ++++++++++++++++++++++++++++++++++++-- + 7 files changed, 222 insertions(+), 8 deletions(-) + +--- a/include/net/inet_frag.h ++++ b/include/net/inet_frag.h +@@ -1,6 +1,9 @@ + #ifndef __NET_FRAG_H__ + #define __NET_FRAG_H__ + ++#include ++#include ++ + struct netns_frags { + int nqueues; + atomic_t mem; +@@ -10,6 +13,10 @@ struct netns_frags { + int timeout; + int high_thresh; + int low_thresh; ++ ++ /* reserves */ ++ struct mutex lock; ++ struct mem_reserve reserve; + }; + + struct inet_frag_queue { +--- a/include/net/netns/ipv6.h ++++ b/include/net/netns/ipv6.h +@@ -25,6 +25,8 @@ struct netns_sysctl_ipv6 { + int ip6_rt_mtu_expires; + int ip6_rt_min_advmss; + int icmpv6_time; ++ ++ struct mutex ip6_rt_lock; + }; + + struct netns_ipv6 { +@@ -58,6 +60,8 @@ struct netns_ipv6 { + struct sock *ndisc_sk; + struct sock *tcp_sk; + struct sock *igmp_sk; ++ ++ struct mem_reserve ip6_rt_reserve; + #ifdef CONFIG_IPV6_MROUTE + struct sock *mroute6_sk; + struct mfc6_cache **mfc6_cache_array; +--- a/net/ipv4/inet_fragment.c ++++ b/net/ipv4/inet_fragment.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include + +@@ -75,6 +76,8 @@ void inet_frags_init_net(struct netns_fr + nf->nqueues = 0; + atomic_set(&nf->mem, 0); + INIT_LIST_HEAD(&nf->lru_list); ++ mutex_init(&nf->lock); ++ mem_reserve_init(&nf->reserve, "IP fragement cache", NULL); + } + EXPORT_SYMBOL(inet_frags_init_net); + +--- a/net/ipv4/ip_fragment.c ++++ b/net/ipv4/ip_fragment.c +@@ -45,6 +45,8 @@ + #include + #include + #include ++#include ++#include + + /* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6 + * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c +@@ -626,6 +628,34 @@ int ip_defrag(struct sk_buff *skb, u32 u + } + + #ifdef CONFIG_SYSCTL ++static int ++proc_dointvec_fragment(struct ctl_table *table, int write, ++ void __user *buffer, size_t *lenp, loff_t *ppos) ++{ ++ struct net *net = container_of(table->data, struct net, ++ ipv4.frags.high_thresh); ++ ctl_table tmp = *table; ++ int new_bytes, ret; ++ ++ mutex_lock(&net->ipv4.frags.lock); ++ if (write) { ++ tmp.data = &new_bytes; ++ table = &tmp; ++ } ++ ++ ret = proc_dointvec(table, write, buffer, lenp, ppos); ++ ++ if (!ret && write) { ++ ret = mem_reserve_kmalloc_set(&net->ipv4.frags.reserve, ++ new_bytes); ++ if (!ret) ++ net->ipv4.frags.high_thresh = new_bytes; ++ } ++ mutex_unlock(&net->ipv4.frags.lock); ++ ++ return ret; ++} ++ + static int zero; + + static struct ctl_table ip4_frags_ns_ctl_table[] = { +@@ -634,7 +664,7 @@ static struct ctl_table ip4_frags_ns_ctl + .data = &init_net.ipv4.frags.high_thresh, + .maxlen = sizeof(int), + .mode = 0644, +- .proc_handler = proc_dointvec ++ .proc_handler = proc_dointvec_fragment, + }, + { + .procname = "ipfrag_low_thresh", +@@ -732,6 +762,8 @@ static inline void ip4_frags_ctl_registe + + static int __net_init ipv4_frags_init_net(struct net *net) + { ++ int ret; ++ + /* + * Fragment cache limits. We will commit 256K at one time. Should we + * cross that limit we will prune down to 192K. This should cope with +@@ -749,11 +781,31 @@ static int __net_init ipv4_frags_init_ne + + inet_frags_init_net(&net->ipv4.frags); + +- return ip4_frags_ns_ctl_register(net); ++ ret = ip4_frags_ns_ctl_register(net); ++ if (ret) ++ goto out_reg; ++ ++ mem_reserve_init(&net->ipv4.frags.reserve, "IPv4 fragment cache", ++ &net_skb_reserve); ++ ret = mem_reserve_kmalloc_set(&net->ipv4.frags.reserve, ++ net->ipv4.frags.high_thresh); ++ if (ret) ++ goto out_reserve; ++ ++ return 0; ++ ++out_reserve: ++ mem_reserve_disconnect(&net->ipv4.frags.reserve); ++ ip4_frags_ns_ctl_unregister(net); ++out_reg: ++ inet_frags_exit_net(&net->ipv4.frags, &ip4_frags); ++ ++ return ret; + } + + static void __net_exit ipv4_frags_exit_net(struct net *net) + { ++ mem_reserve_disconnect(&net->ipv4.frags.reserve); + ip4_frags_ns_ctl_unregister(net); + inet_frags_exit_net(&net->ipv4.frags, &ip4_frags); + } +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -108,6 +108,7 @@ + #ifdef CONFIG_SYSCTL + #include + #endif ++#include + + #define RT_FL_TOS(oldflp) \ + ((u32)(oldflp->fl4_tos & (IPTOS_RT_MASK | RTO_ONLINK))) +@@ -225,6 +226,7 @@ struct rt_hash_bucket { + # define RT_HASH_LOCK_SZ 256 + # endif + #endif ++#include + + static spinlock_t *rt_hash_locks; + # define rt_hash_lock_addr(slot) &rt_hash_locks[(slot) & (RT_HASH_LOCK_SZ - 1)] +@@ -271,6 +273,10 @@ static inline int rt_genid(struct net *n + return atomic_read(&net->ipv4.rt_genid); + } + ++static struct mem_reserve ipv4_route_reserve; ++ ++static struct mem_reserve ipv4_route_reserve; ++ + #ifdef CONFIG_PROC_FS + struct rt_cache_iter_state { + struct seq_net_private p; +@@ -400,6 +406,36 @@ static int rt_cache_seq_show(struct seq_ + return 0; + } + ++static struct mutex ipv4_route_lock; ++ ++static int ++proc_dointvec_route(struct ctl_table *table, int write, void __user *buffer, ++ size_t *lenp, loff_t *ppos) ++{ ++ ctl_table tmp = *table; ++ int new_size, ret; ++ ++ mutex_lock(&ipv4_route_lock); ++ if (write) { ++ tmp.data = &new_size; ++ table = &tmp; ++ } ++ ++ ret = proc_dointvec(table, write, buffer, lenp, ppos); ++ ++ if (!ret && write) { ++ ret = mem_reserve_kmem_cache_set(&ipv4_route_reserve, ++ ipv4_dst_ops.kmem_cachep, new_size); ++ if (!ret) ++ ip_rt_max_size = new_size; ++ } ++ mutex_unlock(&ipv4_route_lock); ++ ++ return ret; ++} ++ ++static struct mutex ipv4_route_lock; ++ + static const struct seq_operations rt_cache_seq_ops = { + .start = rt_cache_seq_start, + .next = rt_cache_seq_next, +@@ -3157,7 +3193,7 @@ static ctl_table ipv4_route_table[] = { + .data = &ip_rt_max_size, + .maxlen = sizeof(int), + .mode = 0644, +- .proc_handler = proc_dointvec, ++ .proc_handler = proc_dointvec_route, + }, + { + /* Deprecated. Use gc_min_interval_ms */ +@@ -3194,7 +3230,7 @@ static ctl_table ipv4_route_table[] = { + .data = &ip_rt_redirect_load, + .maxlen = sizeof(int), + .mode = 0644, +- .proc_handler = proc_dointvec, ++ .proc_handler = proc_dointvec_route, + }, + { + .procname = "redirect_number", +@@ -3414,6 +3450,24 @@ int __init ip_rt_init(void) + ipv4_dst_ops.gc_thresh = (rt_hash_mask + 1); + ip_rt_max_size = (rt_hash_mask + 1) * 16; + ++#ifdef CONFIG_PROCFS ++ mutex_init(&ipv4_route_lock); ++#endif ++ ++ mem_reserve_init(&ipv4_route_reserve, "IPv4 route cache", ++ &net_rx_reserve); ++ mem_reserve_kmem_cache_set(&ipv4_route_reserve, ++ ipv4_dst_ops.kmem_cachep, ip_rt_max_size); ++ ++#ifdef CONFIG_PROCFS ++ mutex_init(&ipv4_route_lock); ++#endif ++ ++ mem_reserve_init(&ipv4_route_reserve, "IPv4 route cache", ++ &net_rx_reserve); ++ mem_reserve_kmem_cache_set(&ipv4_route_reserve, ++ ipv4_dst_ops.kmem_cachep, ip_rt_max_size); ++ + devinet_init(); + ip_fib_init(); + +--- a/net/ipv6/reassembly.c ++++ b/net/ipv6/reassembly.c +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -632,13 +633,41 @@ static const struct inet6_protocol frag_ + }; + + #ifdef CONFIG_SYSCTL ++static int ++proc_dointvec_fragment(struct ctl_table *table, int write, ++ void __user *buffer, size_t *lenp, loff_t *ppos) ++{ ++ struct net *net = container_of(table->data, struct net, ++ ipv6.frags.high_thresh); ++ ctl_table tmp = *table; ++ int new_bytes, ret; ++ ++ mutex_lock(&net->ipv6.frags.lock); ++ if (write) { ++ tmp.data = &new_bytes; ++ table = &tmp; ++ } ++ ++ ret = proc_dointvec(table, write, buffer, lenp, ppos); ++ ++ if (!ret && write) { ++ ret = mem_reserve_kmalloc_set(&net->ipv6.frags.reserve, ++ new_bytes); ++ if (!ret) ++ net->ipv6.frags.high_thresh = new_bytes; ++ } ++ mutex_unlock(&net->ipv6.frags.lock); ++ ++ return ret; ++} ++ + static struct ctl_table ip6_frags_ns_ctl_table[] = { + { + .procname = "ip6frag_high_thresh", + .data = &init_net.ipv6.frags.high_thresh, + .maxlen = sizeof(int), + .mode = 0644, +- .proc_handler = proc_dointvec ++ .proc_handler = proc_dointvec_fragment, + }, + { + .procname = "ip6frag_low_thresh", +@@ -743,17 +772,39 @@ static inline void ip6_frags_sysctl_unre + + static int __net_init ipv6_frags_init_net(struct net *net) + { ++ int ret; ++ + net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; + net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH; + net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT; + + inet_frags_init_net(&net->ipv6.frags); + +- return ip6_frags_ns_sysctl_register(net); ++ ret = ip6_frags_ns_sysctl_register(net); ++ if (ret) ++ goto out_reg; ++ ++ mem_reserve_init(&net->ipv6.frags.reserve, "IPv6 fragment cache", ++ &net_skb_reserve); ++ ret = mem_reserve_kmalloc_set(&net->ipv6.frags.reserve, ++ net->ipv6.frags.high_thresh); ++ if (ret) ++ goto out_reserve; ++ ++ return 0; ++ ++out_reserve: ++ mem_reserve_disconnect(&net->ipv6.frags.reserve); ++ ip6_frags_ns_sysctl_unregister(net); ++out_reg: ++ inet_frags_exit_net(&net->ipv6.frags, &ip6_frags); ++ ++ return ret; + } + + static void __net_exit ipv6_frags_exit_net(struct net *net) + { ++ mem_reserve_disconnect(&net->ipv6.frags.reserve); + ip6_frags_ns_sysctl_unregister(net); + inet_frags_exit_net(&net->ipv6.frags, &ip6_frags); + } +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2537,6 +2538,34 @@ int ipv6_sysctl_rtcache_flush(ctl_table + return -EINVAL; + } + ++static int ++proc_dointvec_route(struct ctl_table *table, int write, ++ void __user *buffer, size_t *lenp, loff_t *ppos) ++{ ++ struct net *net = container_of(table->data, struct net, ++ ipv6.sysctl.ip6_rt_max_size); ++ ctl_table tmp = *table; ++ int new_size, ret; ++ ++ mutex_lock(&net->ipv6.sysctl.ip6_rt_lock); ++ if (write) { ++ tmp.data = &new_size; ++ table = &tmp; ++ } ++ ++ ret = proc_dointvec(table, write, buffer, lenp, ppos); ++ ++ if (!ret && write) { ++ ret = mem_reserve_kmem_cache_set(&net->ipv6.ip6_rt_reserve, ++ net->ipv6.ip6_dst_ops.kmem_cachep, new_size); ++ if (!ret) ++ net->ipv6.sysctl.ip6_rt_max_size = new_size; ++ } ++ mutex_unlock(&net->ipv6.sysctl.ip6_rt_lock); ++ ++ return ret; ++} ++ + ctl_table ipv6_route_table_template[] = { + { + .procname = "flush", +@@ -2557,7 +2586,7 @@ ctl_table ipv6_route_table_template[] = + .data = &init_net.ipv6.sysctl.ip6_rt_max_size, + .maxlen = sizeof(int), + .mode = 0644, +- .proc_handler = proc_dointvec, ++ .proc_handler = proc_dointvec_route, + }, + { + .procname = "gc_min_interval", +@@ -2632,6 +2661,8 @@ struct ctl_table * __net_init ipv6_route + table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; + } + ++ mutex_init(&net->ipv6.sysctl.ip6_rt_lock); ++ + return table; + } + #endif +@@ -2681,6 +2712,14 @@ static int __net_init ip6_route_net_init + net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; + net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; + ++ mem_reserve_init(&net->ipv6.ip6_rt_reserve, "IPv6 route cache", ++ &net_rx_reserve); ++ ret = mem_reserve_kmem_cache_set(&net->ipv6.ip6_rt_reserve, ++ net->ipv6.ip6_dst_ops.kmem_cachep, ++ net->ipv6.sysctl.ip6_rt_max_size); ++ if (ret) ++ goto out_reserve_fail; ++ + #ifdef CONFIG_PROC_FS + proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops); + proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops); +@@ -2691,12 +2730,15 @@ static int __net_init ip6_route_net_init + out: + return ret; + ++out_reserve_fail: ++ mem_reserve_disconnect(&net->ipv6.ip6_rt_reserve); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES ++ kfree(net->ipv6.ip6_blk_hole_entry); + out_ip6_prohibit_entry: + kfree(net->ipv6.ip6_prohibit_entry); + out_ip6_null_entry: +- kfree(net->ipv6.ip6_null_entry); + #endif ++ kfree(net->ipv6.ip6_null_entry); + out_ip6_dst_ops: + goto out; + } +@@ -2707,6 +2749,7 @@ static void __net_exit ip6_route_net_exi + proc_net_remove(net, "ipv6_route"); + proc_net_remove(net, "rt6_stats"); + #endif ++ mem_reserve_disconnect(&net->ipv6.ip6_rt_reserve); + kfree(net->ipv6.ip6_null_entry); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + kfree(net->ipv6.ip6_prohibit_entry); diff --git a/patches.suse/SoN-17-netvm-reserve-inet.patch-fix b/patches.suse/SoN-17-netvm-reserve-inet.patch-fix new file mode 100644 index 0000000..8394d71 --- /dev/null +++ b/patches.suse/SoN-17-netvm-reserve-inet.patch-fix @@ -0,0 +1,23 @@ +From: Jeff Mahoney +Subject: [PATCH 17/31] Fix initialization of ipv4_route_lock +Patch-mainline: not yet + + It's CONFIG_PROC_FS, not CONFIG_PROCFS. + +Signed-off-by: Jeff Mahoney +Signed-off-by: Suresh Jayaraman +--- + net/ipv4/route.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -3437,7 +3437,7 @@ int __init ip_rt_init(void) + ipv4_dst_ops.gc_thresh = (rt_hash_mask + 1); + ip_rt_max_size = (rt_hash_mask + 1) * 16; + +-#ifdef CONFIG_PROCFS ++#ifdef CONFIG_PROC_FS + mutex_init(&ipv4_route_lock); + #endif + diff --git a/patches.suse/SoN-18-netvm-skbuff-reserve.patch b/patches.suse/SoN-18-netvm-skbuff-reserve.patch new file mode 100644 index 0000000..1c36141 --- /dev/null +++ b/patches.suse/SoN-18-netvm-skbuff-reserve.patch @@ -0,0 +1,451 @@ +From: Peter Zijlstra +Subject: [PATCH 18/31] netvm: hook skb allocation to reserves +Patch-mainline: not yet + +Change the skb allocation api to indicate RX usage and use this to fall back to +the reserve when needed. SKBs allocated from the reserve are tagged in +skb->emergency. + +Teach all other skb ops about emergency skbs and the reserve accounting. + +Use the (new) packet split API to allocate and track fragment pages from the +emergency reserve. Do this using an atomic counter in page->index. This is +needed because the fragments have a different sharing semantic than that +indicated by skb_shinfo()->dataref. + +Note that the decision to distinguish between regular and emergency SKBs allows +the accounting overhead to be limited to the later kind. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/mm_types.h | 1 + include/linux/skbuff.h | 25 +++++++- + net/core/skbuff.c | 137 +++++++++++++++++++++++++++++++++++++---------- + 3 files changed, 132 insertions(+), 31 deletions(-) + +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -72,6 +72,7 @@ struct page { + pgoff_t index; /* Our offset within mapping. */ + void *freelist; /* SLUB: freelist req. slab lock */ + int reserve; /* page_alloc: page is a reserve page */ ++ atomic_t frag_count; /* skb fragment use count */ + }; + struct list_head lru; /* Pageout list, eg. active_list + * protected by zone->lru_lock ! +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -380,6 +380,9 @@ struct sk_buff { + #ifdef CONFIG_IPV6_NDISC_NODETYPE + __u8 ndisc_nodetype:2; + #endif ++#ifdef CONFIG_NETVM ++ __u8 emergency:1; ++#endif + kmemcheck_bitfield_end(flags2); + + /* 0/14 bit hole */ +@@ -417,6 +420,18 @@ struct sk_buff { + + #include + ++#define SKB_ALLOC_FCLONE 0x01 ++#define SKB_ALLOC_RX 0x02 ++ ++static inline bool skb_emergency(const struct sk_buff *skb) ++{ ++#ifdef CONFIG_NETVM ++ return unlikely(skb->emergency); ++#else ++ return false; ++#endif ++} ++ + static inline struct dst_entry *skb_dst(const struct sk_buff *skb) + { + return (struct dst_entry *)skb->_skb_dst; +@@ -436,7 +451,7 @@ extern void kfree_skb(struct sk_buff *sk + extern void consume_skb(struct sk_buff *skb); + extern void __kfree_skb(struct sk_buff *skb); + extern struct sk_buff *__alloc_skb(unsigned int size, +- gfp_t priority, int fclone, int node); ++ gfp_t priority, int flags, int node); + static inline struct sk_buff *alloc_skb(unsigned int size, + gfp_t priority) + { +@@ -446,7 +461,7 @@ static inline struct sk_buff *alloc_skb( + static inline struct sk_buff *alloc_skb_fclone(unsigned int size, + gfp_t priority) + { +- return __alloc_skb(size, priority, 1, -1); ++ return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, -1); + } + + extern int skb_recycle_check(struct sk_buff *skb, int skb_size); +@@ -1456,7 +1471,8 @@ static inline void __skb_queue_purge(str + static inline struct sk_buff *__dev_alloc_skb(unsigned int length, + gfp_t gfp_mask) + { +- struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask); ++ struct sk_buff *skb = ++ __alloc_skb(length + NET_SKB_PAD, gfp_mask, SKB_ALLOC_RX, -1); + if (likely(skb)) + skb_reserve(skb, NET_SKB_PAD); + return skb; +@@ -1497,6 +1513,7 @@ static inline struct sk_buff *netdev_all + } + + extern struct page *__netdev_alloc_page(struct net_device *dev, gfp_t gfp_mask); ++extern void __netdev_free_page(struct net_device *dev, struct page *page); + + /** + * netdev_alloc_page - allocate a page for ps-rx on a specific device +@@ -1513,7 +1530,7 @@ static inline struct page *netdev_alloc_ + + static inline void netdev_free_page(struct net_device *dev, struct page *page) + { +- __free_page(page); ++ __netdev_free_page(dev, page); + } + + /** +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -170,23 +170,29 @@ EXPORT_SYMBOL(skb_under_panic); + * %GFP_ATOMIC. + */ + struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, +- int fclone, int node) ++ int flags, int node) + { + struct kmem_cache *cache; + struct skb_shared_info *shinfo; + struct sk_buff *skb; + u8 *data; ++ int emergency = 0; ++ int memalloc = sk_memalloc_socks(); + +- cache = fclone ? skbuff_fclone_cache : skbuff_head_cache; ++ size = SKB_DATA_ALIGN(size); ++ cache = (flags & SKB_ALLOC_FCLONE) ++ ? skbuff_fclone_cache : skbuff_head_cache; ++ ++ if (memalloc && (flags & SKB_ALLOC_RX)) ++ gfp_mask |= __GFP_MEMALLOC; + + /* Get the HEAD */ + skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); + if (!skb) + goto out; + +- size = SKB_DATA_ALIGN(size); +- data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info), +- gfp_mask, node); ++ data = kmalloc_reserve(size + sizeof(struct skb_shared_info), ++ gfp_mask, node, &net_skb_reserve, &emergency); + if (!data) + goto nodata; + +@@ -196,6 +202,9 @@ struct sk_buff *__alloc_skb(unsigned int + * the tail pointer in struct sk_buff! + */ + memset(skb, 0, offsetof(struct sk_buff, tail)); ++#ifdef CONFIG_NETVM ++ skb->emergency = emergency; ++#endif + skb->truesize = size + sizeof(struct sk_buff); + atomic_set(&skb->users, 1); + skb->head = data; +@@ -220,7 +229,7 @@ struct sk_buff *__alloc_skb(unsigned int + skb_frag_list_init(skb); + memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps)); + +- if (fclone) { ++ if (flags & SKB_ALLOC_FCLONE) { + struct sk_buff *child = skb + 1; + atomic_t *fclone_ref = (atomic_t *) (child + 1); + +@@ -230,6 +239,9 @@ struct sk_buff *__alloc_skb(unsigned int + atomic_set(fclone_ref, 1); + + child->fclone = SKB_FCLONE_UNAVAILABLE; ++#ifdef CONFIG_NETVM ++ child->emergency = skb->emergency; ++#endif + } + out: + return skb; +@@ -259,7 +271,7 @@ struct sk_buff *__netdev_alloc_skb(struc + int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1; + struct sk_buff *skb; + +- skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, 0, node); ++ skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, SKB_ALLOC_RX, node); + if (likely(skb)) { + skb_reserve(skb, NET_SKB_PAD); + skb->dev = dev; +@@ -273,11 +285,19 @@ struct page *__netdev_alloc_page(struct + int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1; + struct page *page; + +- page = alloc_pages_node(node, gfp_mask, 0); ++ page = alloc_pages_reserve(node, gfp_mask | __GFP_MEMALLOC, 0, ++ &net_skb_reserve, NULL); ++ + return page; + } + EXPORT_SYMBOL(__netdev_alloc_page); + ++void __netdev_free_page(struct net_device *dev, struct page *page) ++{ ++ free_pages_reserve(page, 0, &net_skb_reserve, page->reserve); ++} ++EXPORT_SYMBOL(__netdev_free_page); ++ + void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, + int size) + { +@@ -285,6 +305,27 @@ void skb_add_rx_frag(struct sk_buff *skb + skb->len += size; + skb->data_len += size; + skb->truesize += size; ++ ++#ifdef CONFIG_NETVM ++ /* ++ * In the rare case that skb_emergency() != page->reserved we'll ++ * skew the accounting slightly, but since its only a 'small' constant ++ * shift its ok. ++ */ ++ if (skb_emergency(skb)) { ++ /* ++ * We need to track fragment pages so that we properly ++ * release their reserve in skb_put_page(). ++ */ ++ atomic_set(&page->frag_count, 1); ++ } else if (unlikely(page->reserve)) { ++ /* ++ * Release the reserve now, because normal skbs don't ++ * do the emergency accounting. ++ */ ++ mem_reserve_pages_charge(&net_skb_reserve, -1); ++ } ++#endif + } + EXPORT_SYMBOL(skb_add_rx_frag); + +@@ -336,21 +377,38 @@ static void skb_clone_fraglist(struct sk + skb_get(list); + } + ++static void skb_get_page(struct sk_buff *skb, struct page *page) ++{ ++ get_page(page); ++ if (skb_emergency(skb)) ++ atomic_inc(&page->frag_count); ++} ++ ++static void skb_put_page(struct sk_buff *skb, struct page *page) ++{ ++ if (skb_emergency(skb) && atomic_dec_and_test(&page->frag_count)) ++ mem_reserve_pages_charge(&net_skb_reserve, -1); ++ put_page(page); ++} ++ + static void skb_release_data(struct sk_buff *skb) + { + if (!skb->cloned || + !atomic_sub_return(skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1, + &skb_shinfo(skb)->dataref)) { ++ + if (skb_shinfo(skb)->nr_frags) { + int i; +- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +- put_page(skb_shinfo(skb)->frags[i].page); ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ skb_put_page(skb, ++ skb_shinfo(skb)->frags[i].page); ++ } + } + + if (skb_has_frags(skb)) + skb_drop_fraglist(skb); + +- kfree(skb->head); ++ kfree_reserve(skb->head, &net_skb_reserve, skb_emergency(skb)); + } + } + +@@ -547,6 +605,9 @@ static void __copy_skb_header(struct sk_ + #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE) + new->ipvs_property = old->ipvs_property; + #endif ++#ifdef CONFIG_NETVM ++ new->emergency = old->emergency; ++#endif + new->protocol = old->protocol; + new->mark = old->mark; + new->skb_iif = old->skb_iif; +@@ -641,6 +702,9 @@ struct sk_buff *skb_clone(struct sk_buff + n->fclone = SKB_FCLONE_CLONE; + atomic_inc(fclone_ref); + } else { ++ if (skb_emergency(skb)) ++ gfp_mask |= __GFP_MEMALLOC; ++ + n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); + if (!n) + return NULL; +@@ -677,6 +741,14 @@ static void copy_skb_header(struct sk_bu + skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; + } + ++static inline int skb_alloc_rx_flag(const struct sk_buff *skb) ++{ ++ if (skb_emergency(skb)) ++ return SKB_ALLOC_RX; ++ ++ return 0; ++} ++ + /** + * skb_copy - create private copy of an sk_buff + * @skb: buffer to copy +@@ -697,15 +769,17 @@ static void copy_skb_header(struct sk_bu + struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) + { + int headerlen = skb->data - skb->head; ++ int size; + /* + * Allocate the copy buffer + */ + struct sk_buff *n; + #ifdef NET_SKBUFF_DATA_USES_OFFSET +- n = alloc_skb(skb->end + skb->data_len, gfp_mask); ++ size = skb->end + skb->data_len; + #else +- n = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask); ++ size = skb->end - skb->head + skb->data_len; + #endif ++ n = __alloc_skb(size, gfp_mask, skb_alloc_rx_flag(skb), -1); + if (!n) + return NULL; + +@@ -740,12 +814,14 @@ struct sk_buff *pskb_copy(struct sk_buff + /* + * Allocate the copy buffer + */ ++ int size; + struct sk_buff *n; + #ifdef NET_SKBUFF_DATA_USES_OFFSET +- n = alloc_skb(skb->end, gfp_mask); ++ size = skb->end; + #else +- n = alloc_skb(skb->end - skb->head, gfp_mask); ++ size = skb->end - skb->head; + #endif ++ n = __alloc_skb(size, gfp_mask, skb_alloc_rx_flag(skb), -1); + if (!n) + goto out; + +@@ -764,8 +840,9 @@ struct sk_buff *pskb_copy(struct sk_buff + int i; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { +- skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; +- get_page(skb_shinfo(n)->frags[i].page); ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ skb_shinfo(n)->frags[i] = *frag; ++ skb_get_page(n, frag->page); + } + skb_shinfo(n)->nr_frags = i; + } +@@ -816,7 +893,11 @@ int pskb_expand_head(struct sk_buff *skb + + size = SKB_DATA_ALIGN(size); + +- data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); ++ if (skb_emergency(skb)) ++ gfp_mask |= __GFP_MEMALLOC; ++ ++ data = kmalloc_reserve(size + sizeof(struct skb_shared_info), ++ gfp_mask, -1, &net_skb_reserve, NULL); + if (!data) + goto nodata; + +@@ -831,7 +912,7 @@ int pskb_expand_head(struct sk_buff *skb + sizeof(struct skb_shared_info)); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +- get_page(skb_shinfo(skb)->frags[i].page); ++ skb_get_page(skb, skb_shinfo(skb)->frags[i].page); + + if (skb_has_frags(skb)) + skb_clone_fraglist(skb); +@@ -912,8 +993,8 @@ struct sk_buff *skb_copy_expand(const st + /* + * Allocate the copy buffer + */ +- struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom, +- gfp_mask); ++ struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom, ++ gfp_mask, skb_alloc_rx_flag(skb), -1); + int oldheadroom = skb_headroom(skb); + int head_copy_len, head_copy_off; + int off; +@@ -1105,7 +1186,7 @@ drop_pages: + skb_shinfo(skb)->nr_frags = i; + + for (; i < nfrags; i++) +- put_page(skb_shinfo(skb)->frags[i].page); ++ skb_put_page(skb, skb_shinfo(skb)->frags[i].page); + + if (skb_has_frags(skb)) + skb_drop_fraglist(skb); +@@ -1274,7 +1355,7 @@ pull_pages: + k = 0; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + if (skb_shinfo(skb)->frags[i].size <= eat) { +- put_page(skb_shinfo(skb)->frags[i].page); ++ skb_put_page(skb, skb_shinfo(skb)->frags[i].page); + eat -= skb_shinfo(skb)->frags[i].size; + } else { + skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i]; +@@ -2052,6 +2133,7 @@ static inline void skb_split_no_header(s + skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i]; + + if (pos < len) { ++ struct page *page = skb_shinfo(skb)->frags[i].page; + /* Split frag. + * We have two variants in this case: + * 1. Move all the frag to the second +@@ -2060,7 +2142,7 @@ static inline void skb_split_no_header(s + * where splitting is expensive. + * 2. Split is accurately. We make this. + */ +- get_page(skb_shinfo(skb)->frags[i].page); ++ skb_get_page(skb1, page); + skb_shinfo(skb1)->frags[0].page_offset += len - pos; + skb_shinfo(skb1)->frags[0].size -= len - pos; + skb_shinfo(skb)->frags[i].size = len - pos; +@@ -2559,8 +2641,9 @@ struct sk_buff *skb_segment(struct sk_bu + skb_release_head_state(nskb); + __skb_push(nskb, doffset); + } else { +- nskb = alloc_skb(hsize + doffset + headroom, +- GFP_ATOMIC); ++ nskb = __alloc_skb(hsize + doffset + headroom, ++ GFP_ATOMIC, skb_alloc_rx_flag(skb), ++ -1); + + if (unlikely(!nskb)) + goto err; +@@ -2602,7 +2685,7 @@ struct sk_buff *skb_segment(struct sk_bu + + while (pos < offset + len && i < nfrags) { + *frag = skb_shinfo(skb)->frags[i]; +- get_page(frag->page); ++ skb_get_page(nskb, frag->page); + size = frag->size; + + if (pos < offset) { diff --git a/patches.suse/SoN-19-netvm-sk_filter.patch b/patches.suse/SoN-19-netvm-sk_filter.patch new file mode 100644 index 0000000..4de0a16 --- /dev/null +++ b/patches.suse/SoN-19-netvm-sk_filter.patch @@ -0,0 +1,28 @@ +From: Peter Zijlstra +Subject: [PATCH 19/31] netvm: filter emergency skbs. +Patch-mainline: not yet + +Toss all emergency packets not for a SOCK_MEMALLOC socket. This ensures our +precious memory reserve doesn't get stuck waiting for user-space. + +The correctness of this approach relies on the fact that networks must be +assumed lossy. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + net/core/filter.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -81,6 +81,9 @@ int sk_filter(struct sock *sk, struct sk + int err; + struct sk_filter *filter; + ++ if (skb_emergency(skb) && !sk_has_memalloc(sk)) ++ return -ENOMEM; ++ + err = security_sock_rcv_skb(sk, skb); + if (err) + return err; diff --git a/patches.suse/SoN-20-netvm-tcp-deadlock.patch b/patches.suse/SoN-20-netvm-tcp-deadlock.patch new file mode 100644 index 0000000..302301f --- /dev/null +++ b/patches.suse/SoN-20-netvm-tcp-deadlock.patch @@ -0,0 +1,105 @@ +From: Peter Zijlstra +Subject: [PATCH 20/31] netvm: prevent a stream specific deadlock +Patch-mainline: not yet + +It could happen that all !SOCK_MEMALLOC sockets have buffered so much data +that we're over the global rmem limit. This will prevent SOCK_MEMALLOC buffers +from receiving data, which will prevent userspace from running, which is needed +to reduce the buffered data. + +Fix this by exempting the SOCK_MEMALLOC sockets from the rmem limit. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/net/sock.h | 7 ++++--- + net/core/sock.c | 2 +- + net/ipv4/tcp_input.c | 12 ++++++------ + net/sctp/ulpevent.c | 2 +- + 4 files changed, 12 insertions(+), 11 deletions(-) + +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -923,12 +923,13 @@ static inline int sk_wmem_schedule(struc + __sk_mem_schedule(sk, size, SK_MEM_SEND); + } + +-static inline int sk_rmem_schedule(struct sock *sk, int size) ++static inline int sk_rmem_schedule(struct sock *sk, struct sk_buff *skb) + { + if (!sk_has_account(sk)) + return 1; +- return size <= sk->sk_forward_alloc || +- __sk_mem_schedule(sk, size, SK_MEM_RECV); ++ return skb->truesize <= sk->sk_forward_alloc || ++ __sk_mem_schedule(sk, skb->truesize, SK_MEM_RECV) || ++ skb_emergency(skb); + } + + static inline void sk_mem_reclaim(struct sock *sk) +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -392,7 +392,7 @@ int sock_queue_rcv_skb(struct sock *sk, + if (err) + return err; + +- if (!sk_rmem_schedule(sk, skb->truesize)) { ++ if (!sk_rmem_schedule(sk, skb)) { + atomic_inc(&sk->sk_drops); + return -ENOBUFS; + } +--- a/net/ipv4/tcp_input.c ++++ b/net/ipv4/tcp_input.c +@@ -4335,19 +4335,19 @@ static void tcp_ofo_queue(struct sock *s + static int tcp_prune_ofo_queue(struct sock *sk); + static int tcp_prune_queue(struct sock *sk); + +-static inline int tcp_try_rmem_schedule(struct sock *sk, unsigned int size) ++static inline int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb) + { + if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || +- !sk_rmem_schedule(sk, size)) { ++ !sk_rmem_schedule(sk, skb)) { + + if (tcp_prune_queue(sk) < 0) + return -1; + +- if (!sk_rmem_schedule(sk, size)) { ++ if (!sk_rmem_schedule(sk, skb)) { + if (!tcp_prune_ofo_queue(sk)) + return -1; + +- if (!sk_rmem_schedule(sk, size)) ++ if (!sk_rmem_schedule(sk, skb)) + return -1; + } + } +@@ -4399,7 +4399,7 @@ static void tcp_data_queue(struct sock * + if (eaten <= 0) { + queue_and_out: + if (eaten < 0 && +- tcp_try_rmem_schedule(sk, skb->truesize)) ++ tcp_try_rmem_schedule(sk, skb)) + goto drop; + + skb_set_owner_r(skb, sk); +@@ -4470,7 +4470,7 @@ drop: + + TCP_ECN_check_ce(tp, skb); + +- if (tcp_try_rmem_schedule(sk, skb->truesize)) ++ if (tcp_try_rmem_schedule(sk, skb)) + goto drop; + + /* Disable header prediction. */ +--- a/net/sctp/ulpevent.c ++++ b/net/sctp/ulpevent.c +@@ -701,7 +701,7 @@ struct sctp_ulpevent *sctp_ulpevent_make + if (rx_count >= asoc->base.sk->sk_rcvbuf) { + + if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) || +- (!sk_rmem_schedule(asoc->base.sk, chunk->skb->truesize))) ++ (!sk_rmem_schedule(asoc->base.sk, chunk->skb))) + goto fail; + } + diff --git a/patches.suse/SoN-21-emergency-nf_queue.patch b/patches.suse/SoN-21-emergency-nf_queue.patch new file mode 100644 index 0000000..3d247b8 --- /dev/null +++ b/patches.suse/SoN-21-emergency-nf_queue.patch @@ -0,0 +1,29 @@ +From: Peter Zijlstra +Subject: [PATCH 21/31] netfilter: NF_QUEUE vs emergency skbs +Patch-mainline: not yet + +Avoid memory getting stuck waiting for userspace, drop all emergency packets. +This of course requires the regular storage route to not include an NF_QUEUE +target ;-) + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + net/netfilter/core.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/netfilter/core.c ++++ b/net/netfilter/core.c +@@ -175,9 +175,12 @@ next_hook: + if (verdict == NF_ACCEPT || verdict == NF_STOP) { + ret = 1; + } else if (verdict == NF_DROP) { ++drop: + kfree_skb(skb); + ret = -EPERM; + } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { ++ if (skb_emergency(skb)) ++ goto drop; + if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn, + verdict >> NF_VERDICT_BITS)) + goto next_hook; diff --git a/patches.suse/SoN-22-netvm.patch b/patches.suse/SoN-22-netvm.patch new file mode 100644 index 0000000..c92c9ab --- /dev/null +++ b/patches.suse/SoN-22-netvm.patch @@ -0,0 +1,181 @@ +From: Peter Zijlstra +Subject: [PATCH 22/31] netvm: skb processing +Patch-mainline: Not yet + +In order to make sure emergency packets receive all memory needed to proceed +ensure processing of emergency SKBs happens under PF_MEMALLOC. + +Use the (new) sk_backlog_rcv() wrapper to ensure this for backlog processing. + +Skip taps, since those are user-space again. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/net/sock.h | 5 ++++ + net/core/dev.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++----- + net/core/sock.c | 16 ++++++++++++++ + 3 files changed, 73 insertions(+), 5 deletions(-) + +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -660,8 +660,13 @@ static inline __must_check int sk_add_ba + return 0; + } + ++extern int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb); ++ + static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) + { ++ if (skb_emergency(skb)) ++ return __sk_backlog_rcv(sk, skb); ++ + return sk->sk_backlog_rcv(sk, skb); + } + +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -2464,6 +2464,30 @@ void netif_nit_deliver(struct sk_buff *s + rcu_read_unlock(); + } + ++/* ++ * Filter the protocols for which the reserves are adequate. ++ * ++ * Before adding a protocol make sure that it is either covered by the existing ++ * reserves, or add reserves covering the memory need of the new protocol's ++ * packet processing. ++ */ ++static int skb_emergency_protocol(struct sk_buff *skb) ++{ ++ if (skb_emergency(skb)) ++ switch (skb->protocol) { ++ case __constant_htons(ETH_P_ARP): ++ case __constant_htons(ETH_P_IP): ++ case __constant_htons(ETH_P_IPV6): ++ case __constant_htons(ETH_P_8021Q): ++ break; ++ ++ default: ++ return 0; ++ } ++ ++ return 1; ++} ++ + /** + * netif_receive_skb - process receive buffer from network + * @skb: buffer to process +@@ -2487,6 +2511,7 @@ int netif_receive_skb(struct sk_buff *sk + struct net_device *null_or_bond; + int ret = NET_RX_DROP; + __be16 type; ++ unsigned long pflags = current->flags; + + if (!skb->tstamp.tv64) + net_timestamp(skb); +@@ -2494,9 +2519,21 @@ int netif_receive_skb(struct sk_buff *sk + if (vlan_tx_tag_present(skb) && vlan_hwaccel_do_receive(skb)) + return NET_RX_SUCCESS; + ++ /* Emergency skb are special, they should ++ * - be delivered to SOCK_MEMALLOC sockets only ++ * - stay away from userspace ++ * - have bounded memory usage ++ * ++ * Use PF_MEMALLOC as a poor mans memory pool - the grouping kind. ++ * This saves us from propagating the allocation context down to all ++ * allocation sites. ++ */ ++ if (skb_emergency(skb)) ++ current->flags |= PF_MEMALLOC; ++ + /* if we've gotten here through NAPI, check netpoll */ + if (netpoll_receive_skb(skb)) +- return NET_RX_DROP; ++ goto out; + + if (!skb->skb_iif) + skb->skb_iif = skb->dev->ifindex; +@@ -2527,6 +2564,9 @@ int netif_receive_skb(struct sk_buff *sk + } + #endif + ++ if (skb_emergency(skb)) ++ goto skip_taps; ++ + list_for_each_entry_rcu(ptype, &ptype_all, list) { + if (ptype->dev == null_or_orig || ptype->dev == skb->dev || + ptype->dev == orig_dev) { +@@ -2536,19 +2576,23 @@ int netif_receive_skb(struct sk_buff *sk + } + } + ++skip_taps: + #ifdef CONFIG_NET_CLS_ACT + skb = handle_ing(skb, &pt_prev, &ret, orig_dev); + if (!skb) +- goto out; ++ goto unlock; + ncls: + #endif + ++ if (!skb_emergency_protocol(skb)) ++ goto drop; ++ + skb = handle_bridge(skb, &pt_prev, &ret, orig_dev); + if (!skb) +- goto out; ++ goto unlock; + skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev); + if (!skb) +- goto out; ++ goto unlock; + + /* + * Make sure frames received on VLAN interfaces stacked on +@@ -2577,6 +2621,7 @@ ncls: + if (pt_prev) { + ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev); + } else { ++drop: + kfree_skb(skb); + /* Jamal, now you will not able to escape explaining + * me how you were going to use this. :-) +@@ -2584,8 +2629,10 @@ ncls: + ret = NET_RX_DROP; + } + +-out: ++unlock: + rcu_read_unlock(); ++out: ++ tsk_restore_flags(current, pflags, PF_MEMALLOC); + return ret; + } + EXPORT_SYMBOL(netif_receive_skb); +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -315,6 +315,22 @@ int sk_clear_memalloc(struct sock *sk) + return set; + } + EXPORT_SYMBOL_GPL(sk_clear_memalloc); ++ ++int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) ++{ ++ int ret; ++ unsigned long pflags = current->flags; ++ ++ /* these should have been dropped before queueing */ ++ BUG_ON(!sk_has_memalloc(sk)); ++ ++ current->flags |= PF_MEMALLOC; ++ ret = sk->sk_backlog_rcv(sk, skb); ++ tsk_restore_flags(current, pflags, PF_MEMALLOC); ++ ++ return ret; ++} ++EXPORT_SYMBOL(__sk_backlog_rcv); + #endif + + static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen) diff --git a/patches.suse/SoN-23-mm-swapfile.patch b/patches.suse/SoN-23-mm-swapfile.patch new file mode 100644 index 0000000..45cccc2 --- /dev/null +++ b/patches.suse/SoN-23-mm-swapfile.patch @@ -0,0 +1,348 @@ +From: Peter Zijlstra +Subject: [PATCH 23/31] mm: add support for non block device backed swap files +Patch-mainline: not yet + +New addres_space_operations methods are added: + int swapon(struct file *); + int swapoff(struct file *); + int swap_out(struct file *, struct page *, struct writeback_control *); + int swap_in(struct file *, struct page *); + +When during sys_swapon() the ->swapon() method is found and returns no error +the swapper_space.a_ops will proxy to sis->swap_file->f_mapping->a_ops, and +make use of ->swap_{out,in}() to write/read swapcache pages. + +The ->swapon() method will be used to communicate to the file that the VM +relies on it, and the address_space should take adequate measures (like +reserving memory for mempools or the like). The ->swapoff() method will be +called on sys_swapoff() when ->swapon() was found and returned no error. + +This new interface can be used to obviate the need for ->bmap in the swapfile +code. A filesystem would need to load (and maybe even allocate) the full block +map for a file into memory and pin it there on ->swapon() so that +->swap_{out,in}() have instant access to it. It can be released on ->swapoff(). + +The reason to provide ->swap_{out,in}() over using {write,read}page() is to + 1) make a distinction between swapcache and pagecache pages, and + 2) to provide a struct file * for credential context (normally not needed + in the context of writepage, as the page content is normally dirtied + using either of the following interfaces: + write_{begin,end}() + {prepare,commit}_write() + page_mkwrite() + which do have the file context. + +[miklos@szeredi.hu: cleanups] +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + Documentation/filesystems/Locking | 22 ++++++++++++++++ + Documentation/filesystems/vfs.txt | 18 +++++++++++++ + include/linux/buffer_head.h | 1 + include/linux/fs.h | 9 ++++++ + include/linux/swap.h | 4 ++ + mm/page_io.c | 52 ++++++++++++++++++++++++++++++++++++++ + mm/swap_state.c | 4 +- + mm/swapfile.c | 30 ++++++++++++++++++++- + 8 files changed, 136 insertions(+), 4 deletions(-) + +--- a/Documentation/filesystems/Locking ++++ b/Documentation/filesystems/Locking +@@ -174,6 +174,10 @@ prototypes: + int (*direct_IO)(int, struct kiocb *, const struct iovec *iov, + loff_t offset, unsigned long nr_segs); + int (*launder_page) (struct page *); ++ int (*swapon) (struct file *); ++ int (*swapoff) (struct file *); ++ int (*swap_out) (struct file *, struct page *, struct writeback_control *); ++ int (*swap_in) (struct file *, struct page *); + + locking rules: + All except set_page_dirty may block +@@ -193,6 +197,10 @@ invalidatepage: no yes + releasepage: no yes + direct_IO: no + launder_page: no yes ++swapon no ++swapoff no ++swap_out no yes, unlocks ++swap_in no yes, unlocks + + ->write_begin(), ->write_end(), ->sync_page() and ->readpage() + may be called from the request handler (/dev/loop). +@@ -292,6 +300,20 @@ cleaned, or an error value if not. Note + getting mapped back in and redirtied, it needs to be kept locked + across the entire operation. + ++ ->swapon() will be called with a non-zero argument on files backing ++(non block device backed) swapfiles. A return value of zero indicates success, ++in which case this file can be used for backing swapspace. The swapspace ++operations will be proxied to the address space operations. ++ ++ ->swapoff() will be called in the sys_swapoff() path when ->swapon() ++returned success. ++ ++ ->swap_out() when swapon() returned success, this method is used to ++write the swap page. ++ ++ ->swap_in() when swapon() returned success, this method is used to ++read the swap page. ++ + Note: currently almost all instances of address_space methods are + using BKL for internal serialization and that's one of the worst sources + of contention. Normally they are calling library functions (in fs/buffer.c) +--- a/Documentation/filesystems/vfs.txt ++++ b/Documentation/filesystems/vfs.txt +@@ -537,6 +537,11 @@ struct address_space_operations { + int (*migratepage) (struct page *, struct page *); + int (*launder_page) (struct page *); + int (*error_remove_page) (struct mapping *mapping, struct page *page); ++ int (*swapon)(struct file *); ++ int (*swapoff)(struct file *); ++ int (*swap_out)(struct file *file, struct page *page, ++ struct writeback_control *wbc); ++ int (*swap_in)(struct file *file, struct page *page); + }; + + writepage: called by the VM to write a dirty page to backing store. +@@ -701,6 +706,19 @@ struct address_space_operations { + unless you have them locked or reference counts increased. + + ++ swapon: Called when swapon is used on a file. A ++ return value of zero indicates success, in which case this ++ file can be used to back swapspace. The swapspace operations ++ will be proxied to this address space's ->swap_{out,in} methods. ++ ++ swapoff: Called during swapoff on files where swapon was successfull. ++ ++ swap_out: Called to write a swapcache page to a backing store, similar to ++ writepage. ++ ++ swap_in: Called to read a swapcache page from a backing store, similar to ++ readpage. ++ + The File Object + =============== + +--- a/include/linux/buffer_head.h ++++ b/include/linux/buffer_head.h +@@ -339,6 +339,7 @@ static inline int inode_has_buffers(stru + static inline void invalidate_inode_buffers(struct inode *inode) {} + static inline int remove_inode_buffers(struct inode *inode) { return 1; } + static inline int sync_mapping_buffers(struct address_space *mapping) { return 0; } ++static inline void block_sync_page(struct page *) { } + + #endif /* CONFIG_BLOCK */ + #endif /* _LINUX_BUFFER_HEAD_H */ +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -608,6 +608,15 @@ struct address_space_operations { + int (*is_partially_uptodate) (struct page *, read_descriptor_t *, + unsigned long); + int (*error_remove_page)(struct address_space *, struct page *); ++ ++ /* ++ * swapfile support ++ */ ++ int (*swapon)(struct file *file); ++ int (*swapoff)(struct file *file); ++ int (*swap_out)(struct file *file, struct page *page, ++ struct writeback_control *wbc); ++ int (*swap_in)(struct file *file, struct page *page); + }; + + /* +--- a/include/linux/swap.h ++++ b/include/linux/swap.h +@@ -146,6 +146,7 @@ enum { + SWP_DISCARDING = (1 << 3), /* now discarding a free cluster */ + SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ + SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */ ++ SWP_FILE = (1 << 6), /* file swap area */ + /* add others here before... */ + SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ + }; +@@ -291,6 +292,8 @@ extern void swap_unplug_io_fn(struct bac + /* linux/mm/page_io.c */ + extern int swap_readpage(struct page *); + extern int swap_writepage(struct page *page, struct writeback_control *wbc); ++extern void swap_sync_page(struct page *page); ++extern int swap_set_page_dirty(struct page *page); + extern void end_swap_bio_read(struct bio *bio, int err); + + /* linux/mm/swap_state.c */ +@@ -327,6 +330,7 @@ extern int swap_type_of(dev_t, sector_t, + extern unsigned int count_swap_pages(int, int); + extern sector_t map_swap_page(struct page *, struct block_device **); + extern sector_t swapdev_block(int, pgoff_t); ++extern struct swap_info_struct *page_swap_info(struct page *); + extern int reuse_swap_page(struct page *); + extern int try_to_free_swap(struct page *); + struct backing_dev_info; +--- a/mm/page_io.c ++++ b/mm/page_io.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -92,11 +93,23 @@ int swap_writepage(struct page *page, st + { + struct bio *bio; + int ret = 0, rw = WRITE; ++ struct swap_info_struct *sis = page_swap_info(page); + + if (try_to_free_swap(page)) { + unlock_page(page); + goto out; + } ++ ++ if (sis->flags & SWP_FILE) { ++ struct file *swap_file = sis->swap_file; ++ struct address_space *mapping = swap_file->f_mapping; ++ ++ ret = mapping->a_ops->swap_out(swap_file, page, wbc); ++ if (!ret) ++ count_vm_event(PSWPOUT); ++ return ret; ++ } ++ + bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write); + if (bio == NULL) { + set_page_dirty(page); +@@ -114,13 +127,52 @@ out: + return ret; + } + ++void swap_sync_page(struct page *page) ++{ ++ struct swap_info_struct *sis = page_swap_info(page); ++ ++ if (sis->flags & SWP_FILE) { ++ struct address_space *mapping = sis->swap_file->f_mapping; ++ ++ if (mapping->a_ops->sync_page) ++ mapping->a_ops->sync_page(page); ++ } else { ++ block_sync_page(page); ++ } ++} ++ ++int swap_set_page_dirty(struct page *page) ++{ ++ struct swap_info_struct *sis = page_swap_info(page); ++ ++ if (sis->flags & SWP_FILE) { ++ struct address_space *mapping = sis->swap_file->f_mapping; ++ ++ return mapping->a_ops->set_page_dirty(page); ++ } else { ++ return __set_page_dirty_nobuffers(page); ++ } ++} ++ + int swap_readpage(struct page *page) + { + struct bio *bio; + int ret = 0; ++ struct swap_info_struct *sis = page_swap_info(page); + + VM_BUG_ON(!PageLocked(page)); + VM_BUG_ON(PageUptodate(page)); ++ ++ if (sis->flags & SWP_FILE) { ++ struct file *swap_file = sis->swap_file; ++ struct address_space *mapping = swap_file->f_mapping; ++ ++ ret = mapping->a_ops->swap_in(swap_file, page); ++ if (!ret) ++ count_vm_event(PSWPIN); ++ return ret; ++ } ++ + bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read); + if (bio == NULL) { + unlock_page(page); +--- a/mm/swap_state.c ++++ b/mm/swap_state.c +@@ -28,8 +28,8 @@ + */ + static const struct address_space_operations swap_aops = { + .writepage = swap_writepage, +- .sync_page = block_sync_page, +- .set_page_dirty = __set_page_dirty_nobuffers, ++ .sync_page = swap_sync_page, ++ .set_page_dirty = swap_set_page_dirty, + .migratepage = migrate_page, + }; + +--- a/mm/swapfile.c ++++ b/mm/swapfile.c +@@ -1346,6 +1346,14 @@ static void destroy_swap_extents(struct + list_del(&se->list); + kfree(se); + } ++ ++ if (sis->flags & SWP_FILE) { ++ struct file *swap_file = sis->swap_file; ++ struct address_space *mapping = swap_file->f_mapping; ++ ++ sis->flags &= ~SWP_FILE; ++ mapping->a_ops->swapoff(swap_file); ++ } + } + + /* +@@ -1427,7 +1435,9 @@ add_swap_extent(struct swap_info_struct + */ + static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) + { +- struct inode *inode; ++ struct file *swap_file = sis->swap_file; ++ struct address_space *mapping = swap_file->f_mapping; ++ struct inode *inode = mapping->host; + unsigned blocks_per_page; + unsigned long page_no; + unsigned blkbits; +@@ -1438,13 +1448,22 @@ static int setup_swap_extents(struct swa + int nr_extents = 0; + int ret; + +- inode = sis->swap_file->f_mapping->host; + if (S_ISBLK(inode->i_mode)) { + ret = add_swap_extent(sis, 0, sis->max, 0); + *span = sis->pages; + goto out; + } + ++ if (mapping->a_ops->swapon) { ++ ret = mapping->a_ops->swapon(swap_file); ++ if (!ret) { ++ sis->flags |= SWP_FILE; ++ ret = add_swap_extent(sis, 0, sis->max, 0); ++ *span = sis->pages; ++ } ++ goto out; ++ } ++ + blkbits = inode->i_blkbits; + blocks_per_page = PAGE_SIZE >> blkbits; + +@@ -2220,6 +2239,13 @@ int swapcache_prepare(swp_entry_t entry) + return __swap_duplicate(entry, SWAP_HAS_CACHE); + } + ++struct swap_info_struct *page_swap_info(struct page *page) ++{ ++ swp_entry_t swap = { .val = page_private(page) }; ++ BUG_ON(!PageSwapCache(page)); ++ return swap_info[swp_type(swap)]; ++} ++ + /* + * swap_lock prevents swap_map being freed. Don't grab an extra + * reference on the swaphandle, it doesn't matter if it becomes unused. diff --git a/patches.suse/SoN-24-mm-page_file_methods.patch b/patches.suse/SoN-24-mm-page_file_methods.patch new file mode 100644 index 0000000..949df32 --- /dev/null +++ b/patches.suse/SoN-24-mm-page_file_methods.patch @@ -0,0 +1,112 @@ +From: Peter Zijlstra +Subject: [PATCH 24/31] mm: methods for teaching filesystems about PG_swapcache pages +Patch-mainline: not yet + +In order to teach filesystems to handle swap cache pages, three new page +functions are introduced: + + pgoff_t page_file_index(struct page *); + loff_t page_file_offset(struct page *); + struct address_space *page_file_mapping(struct page *); + +page_file_index() - gives the offset of this page in the file in +PAGE_CACHE_SIZE blocks. Like page->index is for mapped pages, this function +also gives the correct index for PG_swapcache pages. + +page_file_offset() - uses page_file_index(), so that it will give the expected +result, even for PG_swapcache pages. + +page_file_mapping() - gives the mapping backing the actual page; that is for +swap cache pages it will give swap_file->f_mapping. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + include/linux/mm.h | 25 +++++++++++++++++++++++++ + include/linux/pagemap.h | 5 +++++ + mm/swapfile.c | 19 +++++++++++++++++++ + 3 files changed, 49 insertions(+) + +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -662,6 +662,17 @@ static inline void *page_rmapping(struct + return (void *)((unsigned long)page->mapping & ~PAGE_MAPPING_FLAGS); + } + ++extern struct address_space *__page_file_mapping(struct page *); ++ ++static inline ++struct address_space *page_file_mapping(struct page *page) ++{ ++ if (unlikely(PageSwapCache(page))) ++ return __page_file_mapping(page); ++ ++ return page->mapping; ++} ++ + static inline int PageAnon(struct page *page) + { + return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0; +@@ -678,6 +689,20 @@ static inline pgoff_t page_index(struct + return page->index; + } + ++extern pgoff_t __page_file_index(struct page *page); ++ ++/* ++ * Return the file index of the page. Regular pagecache pages use ->index ++ * whereas swapcache pages use swp_offset(->private) ++ */ ++static inline pgoff_t page_file_index(struct page *page) ++{ ++ if (unlikely(PageSwapCache(page))) ++ return __page_file_index(page); ++ ++ return page->index; ++} ++ + /* + * The atomic page->_mapcount, like _count, starts from -1: + * so that transitions both from it and to it can be tracked, +--- a/include/linux/pagemap.h ++++ b/include/linux/pagemap.h +@@ -281,6 +281,11 @@ static inline loff_t page_offset(struct + return ((loff_t)page->index) << PAGE_CACHE_SHIFT; + } + ++static inline loff_t page_file_offset(struct page *page) ++{ ++ return ((loff_t)page_file_index(page)) << PAGE_CACHE_SHIFT; ++} ++ + static inline pgoff_t linear_page_index(struct vm_area_struct *vma, + unsigned long address) + { +--- a/mm/swapfile.c ++++ b/mm/swapfile.c +@@ -2247,6 +2247,25 @@ struct swap_info_struct *page_swap_info( + } + + /* ++ * out-of-line __page_file_ methods to avoid include hell. ++ */ ++ ++struct address_space *__page_file_mapping(struct page *page) ++{ ++ VM_BUG_ON(!PageSwapCache(page)); ++ return page_swap_info(page)->swap_file->f_mapping; ++} ++EXPORT_SYMBOL_GPL(__page_file_mapping); ++ ++pgoff_t __page_file_index(struct page *page) ++{ ++ swp_entry_t swap = { .val = page_private(page) }; ++ VM_BUG_ON(!PageSwapCache(page)); ++ return swp_offset(swap); ++} ++EXPORT_SYMBOL_GPL(__page_file_index); ++ ++/* + * swap_lock prevents swap_map being freed. Don't grab an extra + * reference on the swaphandle, it doesn't matter if it becomes unused. + */ diff --git a/patches.suse/SoN-25-nfs-swapcache.patch b/patches.suse/SoN-25-nfs-swapcache.patch new file mode 100644 index 0000000..193bd41 --- /dev/null +++ b/patches.suse/SoN-25-nfs-swapcache.patch @@ -0,0 +1,291 @@ +From: Peter Zijlstra +Subject: [PATCH 25/31] nfs: teach the NFS client how to treat PG_swapcache pages +Patch-mainline: Not yet + +Replace all relevant occurences of page->index and page->mapping in the NFS +client with the new page_file_index() and page_file_mapping() functions. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + fs/nfs/file.c | 6 +++--- + fs/nfs/internal.h | 7 ++++--- + fs/nfs/pagelist.c | 6 +++--- + fs/nfs/read.c | 6 +++--- + fs/nfs/write.c | 43 +++++++++++++++++++++++-------------------- + 5 files changed, 36 insertions(+), 32 deletions(-) + +--- a/fs/nfs/file.c ++++ b/fs/nfs/file.c +@@ -476,7 +476,7 @@ static void nfs_invalidate_page(struct p + if (offset != 0) + return; + /* Cancel any unstarted writes on this page */ +- nfs_wb_page_cancel(page->mapping->host, page); ++ nfs_wb_page_cancel(page_file_mapping(page)->host, page); + + nfs_fscache_invalidate_page(page, page->mapping->host); + } +@@ -509,7 +509,7 @@ static int nfs_release_page(struct page + */ + static int nfs_launder_page(struct page *page) + { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + struct nfs_inode *nfsi = NFS_I(inode); + + dfprintk(PAGECACHE, "NFS: launder_page(%ld, %llu)\n", +@@ -558,7 +558,7 @@ static int nfs_vm_page_mkwrite(struct vm + nfs_fscache_wait_on_page_write(NFS_I(dentry->d_inode), page); + + lock_page(page); +- mapping = page->mapping; ++ mapping = page_file_mapping(page); + if (mapping != dentry->d_inode->i_mapping) + goto out_unlock; + +--- a/fs/nfs/internal.h ++++ b/fs/nfs/internal.h +@@ -344,13 +344,14 @@ void nfs_super_set_maxbytes(struct super + static inline + unsigned int nfs_page_length(struct page *page) + { +- loff_t i_size = i_size_read(page->mapping->host); ++ loff_t i_size = i_size_read(page_file_mapping(page)->host); + + if (i_size > 0) { ++ pgoff_t page_index = page_file_index(page); + pgoff_t end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; +- if (page->index < end_index) ++ if (page_index < end_index) + return PAGE_CACHE_SIZE; +- if (page->index == end_index) ++ if (page_index == end_index) + return ((i_size - 1) & ~PAGE_CACHE_MASK) + 1; + } + return 0; +--- a/fs/nfs/pagelist.c ++++ b/fs/nfs/pagelist.c +@@ -76,11 +76,11 @@ nfs_create_request(struct nfs_open_conte + * update_nfs_request below if the region is not locked. */ + req->wb_page = page; + atomic_set(&req->wb_complete, 0); +- req->wb_index = page->index; ++ req->wb_index = page_file_index(page); + page_cache_get(page); + BUG_ON(PagePrivate(page)); + BUG_ON(!PageLocked(page)); +- BUG_ON(page->mapping->host != inode); ++ BUG_ON(page_file_mapping(page)->host != inode); + req->wb_offset = offset; + req->wb_pgbase = offset; + req->wb_bytes = count; +@@ -369,7 +369,7 @@ void nfs_pageio_cond_complete(struct nfs + * nfs_scan_list - Scan a list for matching requests + * @nfsi: NFS inode + * @dst: Destination list +- * @idx_start: lower bound of page->index to scan ++ * @idx_start: lower bound of page_file_index(page) to scan + * @npages: idx_start + npages sets the upper bound to scan. + * @tag: tag to scan for + * +--- a/fs/nfs/read.c ++++ b/fs/nfs/read.c +@@ -501,11 +501,11 @@ static const struct rpc_call_ops nfs_rea + int nfs_readpage(struct file *file, struct page *page) + { + struct nfs_open_context *ctx; +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + int error; + + dprintk("NFS: nfs_readpage (%p %ld@%lu)\n", +- page, PAGE_CACHE_SIZE, page->index); ++ page, PAGE_CACHE_SIZE, page_file_index(page)); + nfs_inc_stats(inode, NFSIOS_VFSREADPAGE); + nfs_add_stats(inode, NFSIOS_READPAGES, 1); + +@@ -559,7 +559,7 @@ static int + readpage_async_filler(void *data, struct page *page) + { + struct nfs_readdesc *desc = (struct nfs_readdesc *)data; +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + struct nfs_page *new; + unsigned int len; + int error; +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -123,7 +123,7 @@ static struct nfs_page *nfs_page_find_re + + static struct nfs_page *nfs_page_find_request(struct page *page) + { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + struct nfs_page *req = NULL; + + spin_lock(&inode->i_lock); +@@ -135,16 +135,16 @@ static struct nfs_page *nfs_page_find_re + /* Adjust the file length if we're writing beyond the end */ + static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count) + { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + loff_t end, i_size; + pgoff_t end_index; + + spin_lock(&inode->i_lock); + i_size = i_size_read(inode); + end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; +- if (i_size > 0 && page->index < end_index) ++ if (i_size > 0 && page_file_index(page) < end_index) + goto out; +- end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count); ++ end = page_file_offset(page) + ((loff_t)offset+count); + if (i_size >= end) + goto out; + i_size_write(inode, end); +@@ -157,7 +157,7 @@ out: + static void nfs_set_pageerror(struct page *page) + { + SetPageError(page); +- nfs_zap_mapping(page->mapping->host, page->mapping); ++ nfs_zap_mapping(page_file_mapping(page)->host, page_file_mapping(page)); + } + + /* We can set the PG_uptodate flag if we see that a write request +@@ -198,7 +198,7 @@ static int nfs_set_page_writeback(struct + int ret = test_set_page_writeback(page); + + if (!ret) { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + struct nfs_server *nfss = NFS_SERVER(inode); + + page_cache_get(page); +@@ -212,7 +212,7 @@ static int nfs_set_page_writeback(struct + + static void nfs_end_page_writeback(struct page *page) + { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + struct nfs_server *nfss = NFS_SERVER(inode); + + end_page_writeback(page); +@@ -222,7 +222,7 @@ static void nfs_end_page_writeback(struc + + static struct nfs_page *nfs_find_and_lock_request(struct page *page) + { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + struct nfs_page *req; + int ret; + +@@ -280,12 +280,12 @@ out: + + static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio) + { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + + nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE); + nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1); + +- nfs_pageio_cond_complete(pgio, page->index); ++ nfs_pageio_cond_complete(pgio, page_file_index(page)); + return nfs_page_async_flush(pgio, page); + } + +@@ -297,7 +297,8 @@ static int nfs_writepage_locked(struct p + struct nfs_pageio_descriptor pgio; + int err; + +- nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc)); ++ nfs_pageio_init_write(&pgio, page_file_mapping(page)->host, ++ wb_priority(wbc)); + err = nfs_do_writepage(page, wbc, &pgio); + nfs_pageio_complete(&pgio); + if (err < 0) +@@ -441,7 +442,8 @@ nfs_mark_request_commit(struct nfs_page + nfsi->ncommit++; + spin_unlock(&inode->i_lock); + inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); +- inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); ++ inc_bdi_stat(page_file_mapping(req->wb_page)->backing_dev_info, ++ BDI_RECLAIMABLE); + __mark_inode_dirty(inode, I_DIRTY_DATASYNC); + } + +@@ -452,7 +454,8 @@ nfs_clear_request_commit(struct nfs_page + + if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) { + dec_zone_page_state(page, NR_UNSTABLE_NFS); +- dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE); ++ dec_bdi_stat(page_file_mapping(page)->backing_dev_info, ++ BDI_RECLAIMABLE); + return 1; + } + return 0; +@@ -513,7 +516,7 @@ nfs_need_commit(struct nfs_inode *nfsi) + * nfs_scan_commit - Scan an inode for commit requests + * @inode: NFS inode to scan + * @dst: destination list +- * @idx_start: lower bound of page->index to scan. ++ * @idx_start: lower bound of page_file_index(page) to scan. + * @npages: idx_start + npages sets the upper bound to scan. + * + * Moves requests from the inode's 'commit' request list. +@@ -633,7 +636,7 @@ out_err: + static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx, + struct page *page, unsigned int offset, unsigned int bytes) + { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + struct nfs_page *req; + int error; + +@@ -688,7 +691,7 @@ int nfs_flush_incompatible(struct file * + nfs_release_request(req); + if (!do_flush) + return 0; +- status = nfs_wb_page(page->mapping->host, page); ++ status = nfs_wb_page(page_file_mapping(page)->host, page); + } while (status == 0); + return status; + } +@@ -714,7 +717,7 @@ int nfs_updatepage(struct file *file, st + unsigned int offset, unsigned int count) + { + struct nfs_open_context *ctx = nfs_file_open_context(file); +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + int status = 0; + + nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE); +@@ -722,7 +725,7 @@ int nfs_updatepage(struct file *file, st + dprintk("NFS: nfs_updatepage(%s/%s %d@%lld)\n", + file->f_path.dentry->d_parent->d_name.name, + file->f_path.dentry->d_name.name, count, +- (long long)(page_offset(page) + offset)); ++ (long long)(page_file_offset(page) + offset)); + + /* If we're not using byte range locks, and we know the page + * is up to date, it may be more efficient to extend the write +@@ -997,7 +1000,7 @@ static void nfs_writeback_release_partia + } + + if (nfs_write_need_commit(data)) { +- struct inode *inode = page->mapping->host; ++ struct inode *inode = page_file_mapping(page)->host; + + spin_lock(&inode->i_lock); + if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) { +@@ -1278,7 +1281,7 @@ nfs_commit_list(struct inode *inode, str + nfs_list_remove_request(req); + nfs_mark_request_commit(req); + dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); +- dec_bdi_stat(req->wb_page->mapping->backing_dev_info, ++ dec_bdi_stat(page_file_mapping(req->wb_page)->backing_dev_info, + BDI_RECLAIMABLE); + nfs_clear_page_tag_locked(req); + } diff --git a/patches.suse/SoN-26-nfs-swapper.patch b/patches.suse/SoN-26-nfs-swapper.patch new file mode 100644 index 0000000..be8e767 --- /dev/null +++ b/patches.suse/SoN-26-nfs-swapper.patch @@ -0,0 +1,160 @@ +From: Peter Zijlstra +Subject: [PATCH 26/31] nfs: disable data cache revalidation for swapfiles +Patch-mainline: Not yet + +Do as Trond suggested: + http://lkml.org/lkml/2006/8/25/348 + +Disable NFS data cache revalidation on swap files since it doesn't really +make sense to have other clients change the file while you are using it. + +Thereby we can stop setting PG_private on swap pages, since there ought to +be no further races with invalidate_inode_pages2() to deal with. + +And since we cannot set PG_private we cannot use page->private (which is +already used by PG_swapcache pages anyway) to store the nfs_page. Thus +augment the new nfs_page_find_request logic. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + fs/nfs/inode.c | 6 ++++ + fs/nfs/write.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++---------- + 2 files changed, 65 insertions(+), 12 deletions(-) + +--- a/fs/nfs/inode.c ++++ b/fs/nfs/inode.c +@@ -780,6 +780,12 @@ int nfs_revalidate_mapping(struct inode + struct nfs_inode *nfsi = NFS_I(inode); + int ret = 0; + ++ /* ++ * swapfiles are not supposed to be shared. ++ */ ++ if (IS_SWAPFILE(inode)) ++ goto out; ++ + if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) + || nfs_attribute_timeout(inode) || NFS_STALE(inode)) { + ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -109,25 +109,64 @@ static void nfs_context_set_write_error( + set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); + } + +-static struct nfs_page *nfs_page_find_request_locked(struct page *page) ++static struct nfs_page * ++__nfs_page_find_request_locked(struct nfs_inode *nfsi, struct page *page, ++ int get) + { + struct nfs_page *req = NULL; + +- if (PagePrivate(page)) { ++ if (PagePrivate(page)) + req = (struct nfs_page *)page_private(page); +- if (req != NULL) +- kref_get(&req->wb_kref); +- } ++ else if (unlikely(PageSwapCache(page))) ++ req = radix_tree_lookup(&nfsi->nfs_page_tree, ++ page_file_index(page)); ++ ++ if (get && req) ++ kref_get(&req->wb_kref); ++ + return req; + } + ++static inline struct nfs_page * ++nfs_page_find_request_locked(struct nfs_inode *nfsi, struct page *page) ++{ ++ return __nfs_page_find_request_locked(nfsi, page, 1); ++} ++ ++static int __nfs_page_has_request(struct page *page) ++{ ++ struct inode *inode = page_file_mapping(page)->host; ++ struct nfs_page *req = NULL; ++ ++ spin_lock(&inode->i_lock); ++ req = __nfs_page_find_request_locked(NFS_I(inode), page, 0); ++ spin_unlock(&inode->i_lock); ++ ++ /* ++ * hole here plugged by the caller holding onto PG_locked ++ */ ++ ++ return req != NULL; ++} ++ ++static inline int nfs_page_has_request(struct page *page) ++{ ++ if (PagePrivate(page)) ++ return 1; ++ ++ if (unlikely(PageSwapCache(page))) ++ return __nfs_page_has_request(page); ++ ++ return 0; ++} ++ + static struct nfs_page *nfs_page_find_request(struct page *page) + { + struct inode *inode = page_file_mapping(page)->host; + struct nfs_page *req = NULL; + + spin_lock(&inode->i_lock); +- req = nfs_page_find_request_locked(page); ++ req = nfs_page_find_request_locked(NFS_I(inode), page); + spin_unlock(&inode->i_lock); + return req; + } +@@ -228,7 +267,7 @@ static struct nfs_page *nfs_find_and_loc + + spin_lock(&inode->i_lock); + for (;;) { +- req = nfs_page_find_request_locked(page); ++ req = nfs_page_find_request_locked(NFS_I(inode), page); + if (req == NULL) + break; + if (nfs_set_page_tag_locked(req)) +@@ -382,8 +421,14 @@ static int nfs_inode_add_request(struct + if (nfs_have_delegation(inode, FMODE_WRITE)) + nfsi->change_attr++; + } +- SetPagePrivate(req->wb_page); +- set_page_private(req->wb_page, (unsigned long)req); ++ /* ++ * Swap-space should not get truncated. Hence no need to plug the race ++ * with invalidate/truncate. ++ */ ++ if (likely(!PageSwapCache(req->wb_page))) { ++ SetPagePrivate(req->wb_page); ++ set_page_private(req->wb_page, (unsigned long)req); ++ } + nfsi->npages++; + kref_get(&req->wb_kref); + radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, +@@ -405,8 +450,10 @@ static void nfs_inode_remove_request(str + BUG_ON (!NFS_WBACK_BUSY(req)); + + spin_lock(&inode->i_lock); +- set_page_private(req->wb_page, 0); +- ClearPagePrivate(req->wb_page); ++ if (likely(!PageSwapCache(req->wb_page))) { ++ set_page_private(req->wb_page, 0); ++ ClearPagePrivate(req->wb_page); ++ } + radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index); + nfsi->npages--; + if (!nfsi->npages) { +@@ -574,7 +621,7 @@ static struct nfs_page *nfs_try_to_updat + spin_lock(&inode->i_lock); + + for (;;) { +- req = nfs_page_find_request_locked(page); ++ req = nfs_page_find_request_locked(NFS_I(inode), page); + if (req == NULL) + goto out_unlock; + diff --git a/patches.suse/SoN-27-nfs-swap_ops.patch b/patches.suse/SoN-27-nfs-swap_ops.patch new file mode 100644 index 0000000..110378c --- /dev/null +++ b/patches.suse/SoN-27-nfs-swap_ops.patch @@ -0,0 +1,339 @@ +From: Peter Zijlstra +Subject: [PATCH 27/31] nfs: enable swap on NFS +Patch-mainline: not yet + +Implement all the new swapfile a_ops for NFS. This will set the NFS socket to +SOCK_MEMALLOC and run socket reconnect under PF_MEMALLOC as well as reset +SOCK_MEMALLOC before engaging the protocol ->connect() method. + +PF_MEMALLOC should allow the allocation of struct socket and related objects +and the early (re)setting of SOCK_MEMALLOC should allow us to receive the +packets required for the TCP connection buildup. + +(swapping continues over a server reset during heavy network traffic) + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + fs/nfs/Kconfig | 10 ++++++ + fs/nfs/file.c | 18 +++++++++++ + fs/nfs/write.c | 22 +++++++++++++ + include/linux/nfs_fs.h | 2 + + include/linux/sunrpc/xprt.h | 5 ++- + net/sunrpc/Kconfig | 5 +++ + net/sunrpc/sched.c | 9 ++++- + net/sunrpc/xprtsock.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ + 8 files changed, 138 insertions(+), 3 deletions(-) + +--- a/fs/nfs/Kconfig ++++ b/fs/nfs/Kconfig +@@ -74,6 +74,16 @@ config NFS_V4 + + If unsure, say N. + ++config NFS_SWAP ++ bool "Provide swap over NFS support" ++ default n ++ depends on NFS_FS ++ select SUNRPC_SWAP ++ help ++ This option enables swapon to work on files located on NFS mounts. ++ ++ For more details, see Documentation/network-swap.txt ++ + config NFS_V4_1 + bool "NFS client support for NFSv4.1 (DEVELOPER ONLY)" + depends on NFS_V4 && EXPERIMENTAL +--- a/fs/nfs/file.c ++++ b/fs/nfs/file.c +@@ -519,6 +519,18 @@ static int nfs_launder_page(struct page + return nfs_wb_page(inode, page); + } + ++#ifdef CONFIG_NFS_SWAP ++static int nfs_swapon(struct file *file) ++{ ++ return xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 1); ++} ++ ++static int nfs_swapoff(struct file *file) ++{ ++ return xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 0); ++} ++#endif ++ + const struct address_space_operations nfs_file_aops = { + .readpage = nfs_readpage, + .readpages = nfs_readpages, +@@ -533,6 +545,12 @@ const struct address_space_operations nf + .migratepage = nfs_migrate_page, + .launder_page = nfs_launder_page, + .error_remove_page = generic_error_remove_page, ++#ifdef CONFIG_NFS_SWAP ++ .swapon = nfs_swapon, ++ .swapoff = nfs_swapoff, ++ .swap_out = nfs_swap_out, ++ .swap_in = nfs_readpage, ++#endif + }; + + /* +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -356,6 +356,28 @@ int nfs_writepage(struct page *page, str + return ret; + } + ++static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page, ++ unsigned int offset, unsigned int count); ++ ++int nfs_swap_out(struct file *file, struct page *page, ++ struct writeback_control *wbc) ++{ ++ struct nfs_open_context *ctx = nfs_file_open_context(file); ++ int status; ++ ++ status = nfs_writepage_setup(ctx, page, 0, nfs_page_length(page)); ++ if (status < 0) { ++ nfs_set_pageerror(page); ++ goto out; ++ } ++ ++ status = nfs_writepage_locked(page, wbc); ++ ++out: ++ unlock_page(page); ++ return status; ++} ++ + static int nfs_writepages_callback(struct page *page, struct writeback_control *wbc, void *data) + { + int ret; +--- a/include/linux/nfs_fs.h ++++ b/include/linux/nfs_fs.h +@@ -469,6 +469,8 @@ extern int nfs_writepages(struct addres + extern int nfs_flush_incompatible(struct file *file, struct page *page); + extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int); + extern int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *); ++extern int nfs_swap_out(struct file *file, struct page *page, ++ struct writeback_control *wbc); + + /* + * Try to write back everything synchronously (but check the +--- a/include/linux/sunrpc/xprt.h ++++ b/include/linux/sunrpc/xprt.h +@@ -168,7 +168,9 @@ struct rpc_xprt { + unsigned int max_reqs; /* total slots */ + unsigned long state; /* transport state */ + unsigned char shutdown : 1, /* being shut down */ +- resvport : 1; /* use a reserved port */ ++ resvport : 1, /* use a reserved port */ ++ swapper : 1; /* we're swapping over this ++ transport */ + unsigned int bind_index; /* bind function index */ + + /* +@@ -302,6 +304,7 @@ void xprt_release_rqst_cong(struct rpc + void xprt_disconnect_done(struct rpc_xprt *xprt); + void xprt_force_disconnect(struct rpc_xprt *xprt); + void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie); ++int xs_swapper(struct rpc_xprt *xprt, int enable); + + /* + * Reserved bit positions in xprt->state +--- a/net/sunrpc/Kconfig ++++ b/net/sunrpc/Kconfig +@@ -17,6 +17,11 @@ config SUNRPC_XPRT_RDMA + + If unsure, say N. + ++config SUNRPC_SWAP ++ def_bool n ++ depends on SUNRPC ++ select NETVM ++ + config RPCSEC_GSS_KRB5 + tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" + depends on SUNRPC && EXPERIMENTAL +--- a/net/sunrpc/sched.c ++++ b/net/sunrpc/sched.c +@@ -747,7 +747,10 @@ static void rpc_async_schedule(struct wo + void *rpc_malloc(struct rpc_task *task, size_t size) + { + struct rpc_buffer *buf; +- gfp_t gfp = RPC_IS_SWAPPER(task) ? GFP_ATOMIC : GFP_NOWAIT; ++ gfp_t gfp = GFP_NOWAIT; ++ ++ if (RPC_IS_SWAPPER(task)) ++ gfp |= __GFP_MEMALLOC; + + size += sizeof(struct rpc_buffer); + if (size <= RPC_BUFFER_MAXSIZE) +@@ -818,6 +821,8 @@ static void rpc_init_task(struct rpc_tas + kref_get(&task->tk_client->cl_kref); + if (task->tk_client->cl_softrtry) + task->tk_flags |= RPC_TASK_SOFT; ++ if (task->tk_client->cl_xprt->swapper) ++ task->tk_flags |= RPC_TASK_SWAPPER; + } + + if (task->tk_ops->rpc_call_prepare != NULL) +@@ -843,7 +848,7 @@ static void rpc_init_task(struct rpc_tas + static struct rpc_task * + rpc_alloc_task(void) + { +- return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOFS); ++ return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOIO); + } + + /* +--- a/net/sunrpc/xprtsock.c ++++ b/net/sunrpc/xprtsock.c +@@ -1642,6 +1642,57 @@ static inline void xs_reclassify_socket6 + } + #endif + ++#ifdef CONFIG_SUNRPC_SWAP ++static void xs_set_memalloc(struct rpc_xprt *xprt) ++{ ++ struct sock_xprt *transport = container_of(xprt, struct sock_xprt, ++ xprt); ++ ++ if (xprt->swapper) ++ sk_set_memalloc(transport->inet); ++} ++ ++#define RPC_BUF_RESERVE_PAGES \ ++ kmalloc_estimate_objs(sizeof(struct rpc_rqst), GFP_KERNEL, RPC_MAX_SLOT_TABLE) ++#define RPC_RESERVE_PAGES (RPC_BUF_RESERVE_PAGES + TX_RESERVE_PAGES) ++ ++/** ++ * xs_swapper - Tag this transport as being used for swap. ++ * @xprt: transport to tag ++ * @enable: enable/disable ++ * ++ */ ++int xs_swapper(struct rpc_xprt *xprt, int enable) ++{ ++ struct sock_xprt *transport = container_of(xprt, struct sock_xprt, ++ xprt); ++ int err = 0; ++ ++ if (enable) { ++ /* ++ * keep one extra sock reference so the reserve won't dip ++ * when the socket gets reconnected. ++ */ ++ err = sk_adjust_memalloc(1, RPC_RESERVE_PAGES); ++ if (!err) { ++ xprt->swapper = 1; ++ xs_set_memalloc(xprt); ++ } ++ } else if (xprt->swapper) { ++ xprt->swapper = 0; ++ sk_clear_memalloc(transport->inet); ++ sk_adjust_memalloc(-1, -RPC_RESERVE_PAGES); ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(xs_swapper); ++#else ++static void xs_set_memalloc(struct rpc_xprt *xprt) ++{ ++} ++#endif ++ + static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) + { + struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); +@@ -1666,6 +1717,8 @@ static void xs_udp_finish_connecting(str + transport->sock = sock; + transport->inet = sk; + ++ xs_set_memalloc(xprt); ++ + write_unlock_bh(&sk->sk_callback_lock); + } + xs_udp_do_set_buffer_size(xprt); +@@ -1683,11 +1736,15 @@ static void xs_udp_connect_worker4(struc + container_of(work, struct sock_xprt, connect_worker.work); + struct rpc_xprt *xprt = &transport->xprt; + struct socket *sock = transport->sock; ++ unsigned long pflags = current->flags; + int err, status = -EIO; + + if (xprt->shutdown) + goto out; + ++ if (xprt->swapper) ++ current->flags |= PF_MEMALLOC; ++ + /* Start by resetting any existing state */ + xs_reset_transport(transport); + +@@ -1714,6 +1771,7 @@ static void xs_udp_connect_worker4(struc + out: + xprt_clear_connecting(xprt); + xprt_wake_pending_tasks(xprt, status); ++ tsk_restore_flags(current, pflags, PF_MEMALLOC); + } + + /** +@@ -1728,11 +1786,15 @@ static void xs_udp_connect_worker6(struc + container_of(work, struct sock_xprt, connect_worker.work); + struct rpc_xprt *xprt = &transport->xprt; + struct socket *sock = transport->sock; ++ unsigned long pflags = current->flags; + int err, status = -EIO; + + if (xprt->shutdown) + goto out; + ++ if (xprt->swapper) ++ current->flags |= PF_MEMALLOC; ++ + /* Start by resetting any existing state */ + xs_reset_transport(transport); + +@@ -1759,6 +1821,7 @@ static void xs_udp_connect_worker6(struc + out: + xprt_clear_connecting(xprt); + xprt_wake_pending_tasks(xprt, status); ++ tsk_restore_flags(current, pflags, PF_MEMALLOC); + } + + /* +@@ -1833,6 +1896,8 @@ static int xs_tcp_finish_connecting(stru + if (!xprt_bound(xprt)) + return -ENOTCONN; + ++ xs_set_memalloc(xprt); ++ + /* Tell the socket layer to start connecting... */ + xprt->stat.connect_count++; + xprt->stat.connect_start = jiffies; +@@ -1853,11 +1918,15 @@ static void xs_tcp_setup_socket(struct r + struct sock_xprt *)) + { + struct socket *sock = transport->sock; ++ unsigned long pflags = current->flags; + int status = -EIO; + + if (xprt->shutdown) + goto out; + ++ if (xprt->swapper) ++ current->flags |= PF_MEMALLOC; ++ + if (!sock) { + clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); + sock = create_sock(xprt, transport); +@@ -1918,6 +1987,7 @@ out_eagain: + out: + xprt_clear_connecting(xprt); + xprt_wake_pending_tasks(xprt, status); ++ tsk_restore_flags(current, pflags, PF_MEMALLOC); + } + + static struct socket *xs_create_tcp_sock4(struct rpc_xprt *xprt, diff --git a/patches.suse/SoN-28-nfs-alloc-recursions.patch b/patches.suse/SoN-28-nfs-alloc-recursions.patch new file mode 100644 index 0000000..4356761 --- /dev/null +++ b/patches.suse/SoN-28-nfs-alloc-recursions.patch @@ -0,0 +1,59 @@ +From: Peter Zijlstra +Subject: [PATCH 28/31] nfs: fix various memory recursions possible with swap over NFS. +Patch-mainline: not yet + +GFP_NOFS is _more_ permissive than GFP_NOIO in that it will initiate IO, +just not of any filesystem data. + +The problem is that previuosly NOFS was correct because that avoids +recursion into the NFS code, it now is not, because also IO (swap) can +lead to this recursion. + +Signed-off-by: Peter Zijlstra +Signed-off-by: Suresh Jayaraman +--- + fs/nfs/pagelist.c | 2 +- + fs/nfs/write.c | 7 ++++--- + 2 files changed, 5 insertions(+), 4 deletions(-) + +--- a/fs/nfs/pagelist.c ++++ b/fs/nfs/pagelist.c +@@ -27,7 +27,7 @@ static inline struct nfs_page * + nfs_page_alloc(void) + { + struct nfs_page *p; +- p = kmem_cache_alloc(nfs_page_cachep, GFP_KERNEL); ++ p = kmem_cache_alloc(nfs_page_cachep, GFP_NOIO); + if (p) { + memset(p, 0, sizeof(*p)); + INIT_LIST_HEAD(&p->wb_list); +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -50,7 +50,7 @@ static mempool_t *nfs_commit_mempool; + + struct nfs_write_data *nfs_commitdata_alloc(void) + { +- struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS); ++ struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOIO); + + if (p) { + memset(p, 0, sizeof(*p)); +@@ -69,7 +69,7 @@ void nfs_commit_free(struct nfs_write_da + + struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) + { +- struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); ++ struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO); + + if (p) { + memset(p, 0, sizeof(*p)); +@@ -79,7 +79,8 @@ struct nfs_write_data *nfs_writedata_all + if (pagecount <= ARRAY_SIZE(p->page_array)) + p->pagevec = p->page_array; + else { +- p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); ++ p->pagevec = kcalloc(pagecount, sizeof(struct page *), ++ GFP_NOIO); + if (!p->pagevec) { + mempool_free(p, nfs_wdata_mempool); + p = NULL; diff --git a/patches.suse/SoN-29-fix-swap_sync_page-race b/patches.suse/SoN-29-fix-swap_sync_page-race new file mode 100644 index 0000000..bbf0567 --- /dev/null +++ b/patches.suse/SoN-29-fix-swap_sync_page-race @@ -0,0 +1,58 @@ +From: NeilBrown +Subject: [PATCH 29/31] Cope with racy nature of sync_page in swap_sync_page +Patch-mainline: not yet + +sync_page is called without that PageLock held. This means that, +for example, PageSwapCache can be cleared at any time. +We need to be careful not to put much trust any any part of the page. + +So allow page_swap_info to return NULL of the page is no longer +in a SwapCache, and handle the NULL gracefully in swap_sync_page. + +No other calls need to handle the NULL as that all hold PageLock, +so PageSwapCache cannot be cleared by surprise. Add a WARN_ON to +document this fact and help find out if I am wrong. + +Acked-by: Miklos Szeredi +Signed-off-by: NeilBrown +Signed-off-by: Suresh Jayaraman +--- + mm/page_io.c | 5 +++++ + mm/swapfile.c | 8 +++++++- + 2 files changed, 12 insertions(+), 1 deletion(-) + +--- a/mm/page_io.c ++++ b/mm/page_io.c +@@ -127,10 +127,15 @@ out: + return ret; + } + ++/* this comment ensure the patch applies to swap_sync_page ++ * and not swap_set_page_dirty by mistake ++ */ + void swap_sync_page(struct page *page) + { + struct swap_info_struct *sis = page_swap_info(page); + ++ if (!sis) ++ return; + if (sis->flags & SWP_FILE) { + struct address_space *mapping = sis->swap_file->f_mapping; + +--- a/mm/swapfile.c ++++ b/mm/swapfile.c +@@ -2242,7 +2242,13 @@ int swapcache_prepare(swp_entry_t entry) + struct swap_info_struct *page_swap_info(struct page *page) + { + swp_entry_t swap = { .val = page_private(page) }; +- BUG_ON(!PageSwapCache(page)); ++ if (!PageSwapCache(page) || !swap.val) { ++ /* This should only happen from sync_page. ++ * In other cases the page should be locked and ++ * should be in a SwapCache ++ */ ++ return NULL; ++ } + return swap_info[swp_type(swap)]; + } + diff --git a/patches.suse/SoN-30-fix-uninitialized-var.patch b/patches.suse/SoN-30-fix-uninitialized-var.patch new file mode 100644 index 0000000..415991c --- /dev/null +++ b/patches.suse/SoN-30-fix-uninitialized-var.patch @@ -0,0 +1,40 @@ +From: Miklos Szeredi +Subject: [PATCH 30/31] Fix use of uninitialized variable in cache_grow() +Patch-mainline: not yet + +This fixes a bug in reserve-slub.patch. + +If cache_grow() was called with objp != NULL then the 'reserve' local +variable wasn't initialized. This resulted in ac->reserve being set to +a rubbish value. Due to this in some circumstances huge amounts of +slab pages were allocated (due to slab_force_alloc() returning true), +which caused atomic page allocation failures and slowdown of the +system. + +Signed-off-by: Miklos Szeredi +Signed-off-by: Suresh Jayaraman +--- + mm/slab.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/mm/slab.c ++++ b/mm/slab.c +@@ -2773,7 +2773,7 @@ static int cache_grow(struct kmem_cache + size_t offset; + gfp_t local_flags; + struct kmem_list3 *l3; +- int reserve; ++ int reserve = -1; + + /* + * Be lazy and only check for valid flags here, keeping it out of the +@@ -2829,7 +2829,8 @@ static int cache_grow(struct kmem_cache + if (local_flags & __GFP_WAIT) + local_irq_disable(); + check_irq_off(); +- slab_set_reserve(cachep, reserve); ++ if (reserve != -1) ++ slab_set_reserve(cachep, reserve); + spin_lock(&l3->list_lock); + + /* Make slab active. */ diff --git a/patches.suse/acpi-don-t-preempt-until-the-system-is-up b/patches.suse/acpi-don-t-preempt-until-the-system-is-up new file mode 100644 index 0000000..9888d0e --- /dev/null +++ b/patches.suse/acpi-don-t-preempt-until-the-system-is-up @@ -0,0 +1,25 @@ +From: Jeff Mahoney +Subject: acpi: don't preempt until the system is up + + This is needed to avoid scheduling while atomic BUGs with the + DSDT in initramfs patches. + + +Signed-off-by: Jeff Mahoney +Acked-by: Jeff Mahoney +--- + drivers/acpi/acpica/psloop.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/acpi/acpica/psloop.c ++++ b/drivers/acpi/acpica/psloop.c +@@ -843,7 +843,8 @@ acpi_ps_complete_op(struct acpi_walk_sta + *op = NULL; + } + +- ACPI_PREEMPTION_POINT(); ++ if (system_state == SYSTEM_RUNNING) ++ ACPI_PREEMPTION_POINT(); + + return_ACPI_STATUS(AE_OK); + } diff --git a/patches.suse/acpi-dsdt-initrd-v0.9a-2.6.25.patch b/patches.suse/acpi-dsdt-initrd-v0.9a-2.6.25.patch new file mode 100644 index 0000000..325202c --- /dev/null +++ b/patches.suse/acpi-dsdt-initrd-v0.9a-2.6.25.patch @@ -0,0 +1,410 @@ +From: Eric Piel +Subject: [PATCH 1/1] ACPI: initramfs DSDT override support +Patch-mainline: not yet + + +Permits to load of DSDT (the main ACPI table) from initramfs. In case this +option is selected, the initramfs is parsed at ACPI initialization (very early +boot time) to look for a file DSDT.aml . This aims at allowing users to +override the DSDT without recompiling the kernel. This is done by adding a new +feature to the initramfs parser so that one specific file can be directly +copied into memory. + +This is derived from the patch v0.8 from http://gaugusch.at/kernel.shtml but +with kernel inclusion in mind: some clean-up's in the documentation, default +set to No, a kernel parameter to disable it at runtime, and most important, a +different approach for reading the initramfs which avoids using the filesystem +infrastructure. + +It also contains a fix for compilation on non-ACPI platforms provided by Rene Rebe. + +Update 17 Sep 2009 jeffm@suse.com: +- 2.6.30 (or so) introduced very early ACPI initialization for proper SMP + detection. This caused crashes since things like the mm caches weren't + set up yet, so kmalloc would crash. This update delays overriding the DSDT + until the acpi_early_init() call that used to override it. Since there is + a DSDT already loaded, it is necessarily a bit hacky. + +Signed-off-by: Eric Piel +Signed-off-by: Thomas Renninger +Signed-off-by: Len Brown +Signed-off-by: Jeff Mahoney +--- + Documentation/acpi/dsdt-override.txt | 12 +++- + Documentation/acpi/initramfs-add-dsdt.sh | 43 +++++++++++++++ + Documentation/kernel-parameters.txt | 3 + + drivers/acpi/Kconfig | 11 ++++ + drivers/acpi/acpica/tbxface.c | 36 +++++++++++-- + drivers/acpi/osl.c | 28 +++++++++- + init/initramfs.c | 84 +++++++++++++++++++++++++++++++ + 7 files changed, 209 insertions(+), 8 deletions(-) + create mode 100644 Documentation/acpi/initramfs-add-dsdt.sh + +--- a/Documentation/acpi/dsdt-override.txt ++++ b/Documentation/acpi/dsdt-override.txt +@@ -1,7 +1,15 @@ +-Linux supports a method of overriding the BIOS DSDT: ++Linux supports two methods of overriding the BIOS DSDT: + + CONFIG_ACPI_CUSTOM_DSDT builds the image into the kernel. + +-When to use this method is described in detail on the ++CONFIG_ACPI_CUSTOM_DSDT_INITRD adds the image to the initrd. ++ ++When to use these methods is described in detail on the + Linux/ACPI home page: + http://www.lesswatts.org/projects/acpi/overridingDSDT.php ++ ++Note that if both options are used, the DSDT supplied ++by the INITRD method takes precedence. ++ ++Documentation/initramfs-add-dsdt.sh is provided for convenience ++for use with the CONFIG_ACPI_CUSTOM_DSDT_INITRD method. +--- /dev/null ++++ b/Documentation/acpi/initramfs-add-dsdt.sh +@@ -0,0 +1,43 @@ ++#!/bin/bash ++# Adds a DSDT file to the initrd (if it's an initramfs) ++# first argument is the name of archive ++# second argument is the name of the file to add ++# The file will be copied as /DSDT.aml ++ ++# 20060126: fix "Premature end of file" with some old cpio (Roland Robic) ++# 20060205: this time it should really work ++ ++# check the arguments ++if [ $# -ne 2 ]; then ++ program_name=$(basename $0) ++ echo "\ ++$program_name: too few arguments ++Usage: $program_name initrd-name.img DSDT-to-add.aml ++Adds a DSDT file to an initrd (in initramfs format) ++ ++ initrd-name.img: filename of the initrd in initramfs format ++ DSDT-to-add.aml: filename of the DSDT file to add ++ " 1>&2 ++ exit 1 ++fi ++ ++# we should check it's an initramfs ++ ++tempcpio=$(mktemp -d) ++# cleanup on exit, hangup, interrupt, quit, termination ++trap 'rm -rf $tempcpio' 0 1 2 3 15 ++ ++# extract the archive ++gunzip -c "$1" > "$tempcpio"/initramfs.cpio || exit 1 ++ ++# copy the DSDT file at the root of the directory so that we can call it "/DSDT.aml" ++cp -f "$2" "$tempcpio"/DSDT.aml ++ ++# add the file ++cd "$tempcpio" ++(echo DSDT.aml | cpio --quiet -H newc -o -A -O "$tempcpio"/initramfs.cpio) || exit 1 ++cd "$OLDPWD" ++ ++# re-compress the archive ++gzip -c "$tempcpio"/initramfs.cpio > "$1" ++ +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -217,6 +217,9 @@ and is between 256 and 4096 characters. + + acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT + ++ acpi_no_initrd_override [KNL,ACPI] ++ Disable loading custom ACPI tables from the initramfs ++ + acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS + Format: To spoof as Windows 98: ="Microsoft Windows" + +--- a/drivers/acpi/Kconfig ++++ b/drivers/acpi/Kconfig +@@ -248,6 +248,17 @@ config ACPI_CUSTOM_DSDT + bool + default ACPI_CUSTOM_DSDT_FILE != "" + ++config ACPI_CUSTOM_DSDT_INITRD ++ bool "Read Custom DSDT from initramfs" ++ depends on BLK_DEV_INITRD ++ default n ++ help ++ This option supports a custom DSDT by optionally loading it from initrd. ++ See Documentation/acpi/dsdt-override.txt ++ ++ If you are not using this feature now, but may use it later, ++ it is safe to say Y here. ++ + config ACPI_BLACKLIST_YEAR + int "Disable ACPI for systems before Jan 1st this year" if X86_32 + default 0 +--- a/drivers/acpi/acpica/tbxface.c ++++ b/drivers/acpi/acpica/tbxface.c +@@ -484,6 +484,33 @@ acpi_get_table_by_index(u32 table_index, + + ACPI_EXPORT_SYMBOL(acpi_get_table_by_index) + ++static void ++acpi_dsdt_initrd_override(void) ++{ ++#if defined(CONFIG_ACPI_CUSTOM_DSDT_INITRD) ++ struct acpi_table_header *new = NULL; ++ struct acpi_table_desc *table; ++ acpi_status status; ++ ++ table = &acpi_gbl_root_table_list.tables[ACPI_TABLE_INDEX_DSDT]; ++ status = acpi_os_table_override(table->pointer, &new); ++ if (ACPI_SUCCESS(status) && new) { ++ acpi_tb_delete_table(table); ++ ++ /* This is the top part of acpi_table_load */ ++ memset(table, 0, sizeof(*table)); ++ table->address = ACPI_PTR_TO_PHYSADDR(new); ++ table->pointer = new; ++ table->length = new->length; ++ table->flags |= ACPI_TABLE_ORIGIN_OVERRIDE; ++ table->flags |= ACPI_TABLE_ORIGIN_ALLOCATED; ++ memcpy(table->signature.ascii, new->signature, ACPI_NAME_SIZE); ++ acpi_tb_print_table_header(table->address, new); ++ } ++#endif ++} ++ ++ + /******************************************************************************* + * + * FUNCTION: acpi_tb_load_namespace +@@ -496,7 +523,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_by_ind + * the RSDT/XSDT. + * + ******************************************************************************/ +-static acpi_status acpi_tb_load_namespace(void) ++static acpi_status __init acpi_tb_load_namespace(void) + { + acpi_status status; + u32 i; +@@ -522,6 +549,8 @@ static acpi_status acpi_tb_load_namespac + goto unlock_and_exit; + } + ++ acpi_dsdt_initrd_override(); ++ + /* A valid DSDT is required */ + + status = +@@ -590,7 +619,7 @@ static acpi_status acpi_tb_load_namespac + * + ******************************************************************************/ + +-acpi_status acpi_load_tables(void) ++acpi_status __init acpi_load_tables(void) + { + acpi_status status; + +@@ -607,9 +636,6 @@ acpi_status acpi_load_tables(void) + return_ACPI_STATUS(status); + } + +-ACPI_EXPORT_SYMBOL(acpi_load_tables) +- +- + /******************************************************************************* + * + * FUNCTION: acpi_install_table_handler +--- a/drivers/acpi/osl.c ++++ b/drivers/acpi/osl.c +@@ -98,6 +98,11 @@ static DEFINE_SPINLOCK(acpi_res_lock); + #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ + static char osi_additional_string[OSI_STRING_LENGTH_MAX]; + ++#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD ++static int acpi_no_initrd_override; ++extern struct acpi_table_header *acpi_find_dsdt_initrd(void); ++#endif ++ + /* + * The story of _OSI(Linux) + * +@@ -352,7 +357,7 @@ acpi_os_predefined_override(const struct + return AE_OK; + } + +-acpi_status ++acpi_status __init + acpi_os_table_override(struct acpi_table_header * existing_table, + struct acpi_table_header ** new_table) + { +@@ -365,6 +370,18 @@ acpi_os_table_override(struct acpi_table + if (strncmp(existing_table->signature, "DSDT", 4) == 0) + *new_table = (struct acpi_table_header *)AmlCode; + #endif ++#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD ++ if ((strncmp(existing_table->signature, "DSDT", 4) == 0) && ++ !acpi_no_initrd_override && acpi_gbl_permanent_mmap) { ++ /* JDM: acpi_gbl_permanent_mmap means acpi_early_init() has ++ * been called so things like kmalloc are ok. */ ++ struct acpi_table_header *initrd_table; ++ ++ initrd_table = acpi_find_dsdt_initrd(); ++ if (initrd_table) ++ *new_table = initrd_table; ++ } ++#endif + if (*new_table != NULL) { + printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], " + "this is unsafe: tainting kernel\n", +@@ -375,6 +392,15 @@ acpi_os_table_override(struct acpi_table + return AE_OK; + } + ++#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD ++static int __init acpi_no_initrd_override_setup(char *s) ++{ ++ acpi_no_initrd_override = 1; ++ return 1; ++} ++__setup("acpi_no_initrd_override", acpi_no_initrd_override_setup); ++#endif ++ + static irqreturn_t acpi_irq(int irq, void *dev_id) + { + u32 handled; +--- a/init/initramfs.c ++++ b/init/initramfs.c +@@ -8,6 +8,9 @@ + #include + #include + #include ++#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD ++#include ++#endif + + static __initdata char *message; + static void __init error(char *x) +@@ -125,6 +128,12 @@ static __initdata unsigned long body_len + static __initdata uid_t uid; + static __initdata gid_t gid; + static __initdata unsigned rdev; ++#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD ++static __initdata char *file_looked_for; ++static __initdata struct acpi_table_header *file_mem; ++#else ++const char *file_looked_for = NULL; ++#endif + + static void __init parse_header(char *s) + { +@@ -159,6 +168,7 @@ static __initdata enum state { + SkipIt, + GotName, + CopyFile, ++ CopyFileMem, + GotSymlink, + Reset + } state, next_state; +@@ -228,6 +238,11 @@ static int __init do_header(void) + parse_header(collected); + next_header = this_header + N_ALIGN(name_len) + body_len; + next_header = (next_header + 3) & ~3; ++ if (file_looked_for) { ++ read_into(name_buf, N_ALIGN(name_len), GotName); ++ return 0; ++ } ++ + state = SkipIt; + if (name_len <= 0 || name_len > PATH_MAX) + return 0; +@@ -298,6 +313,12 @@ static int __init do_name(void) + free_hash(); + return 0; + } ++ if (file_looked_for) { ++ if (S_ISREG(mode) && ++ (strcmp(collected, file_looked_for) == 0)) ++ state = CopyFileMem; ++ return 0; ++ } + clean_path(collected, mode); + if (S_ISREG(mode)) { + int ml = maybe_link(); +@@ -333,6 +354,40 @@ static int __init do_name(void) + return 0; + } + ++#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD ++static int __init do_copy_mem(void) ++{ ++ static void *file_current; /* current position in the memory */ ++ if (file_mem == NULL) { ++ if (body_len < 4) { /* check especially against empty files */ ++ error("file is less than 4 bytes"); ++ return 1; ++ } ++ file_mem = kmalloc(body_len, GFP_ATOMIC); ++ if (!file_mem) { ++ error("failed to allocate enough memory"); ++ return 1; ++ } ++ file_current = file_mem; ++ } ++ if (count >= body_len) { ++ memcpy(file_current, victim, body_len); ++ eat(body_len); ++ file_looked_for = NULL; /* don't find files with same name */ ++ state = SkipIt; ++ return 0; ++ } else { ++ memcpy(file_current, victim, count); ++ file_current += count; ++ body_len -= count; ++ eat(count); ++ return 1; ++ } ++} ++#else ++#define do_copy_mem NULL ++#endif ++ + static int __init do_copy(void) + { + if (count >= body_len) { +@@ -370,6 +425,7 @@ static __initdata int (*actions[])(void) + [SkipIt] = do_skip, + [GotName] = do_name, + [CopyFile] = do_copy, ++ [CopyFileMem] = do_copy_mem, + [GotSymlink] = do_symlink, + [Reset] = do_reset, + }; +@@ -606,3 +662,31 @@ static int __init populate_rootfs(void) + return 0; + } + rootfs_initcall(populate_rootfs); ++ ++#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD ++struct acpi_table_header * __init acpi_find_dsdt_initrd(void) ++{ ++ char *err, *ramfs_dsdt_name = "DSDT.aml"; ++ ++ printk(KERN_INFO "ACPI: Checking initramfs for custom DSDT\n"); ++ file_mem = NULL; ++ file_looked_for = ramfs_dsdt_name; ++ err = unpack_to_rootfs((char *)initrd_start, ++ initrd_end - initrd_start); ++ file_looked_for = NULL; ++ ++ if (err) { ++ /* ++ * Even if reading the DSDT file was successful, ++ * we give up if the initramfs cannot be entirely read. ++ */ ++ kfree(file_mem); ++ printk(KERN_ERR "ACPI: Aborted because %s.\n", err); ++ return NULL; ++ } ++ if (file_mem) ++ printk(KERN_INFO "ACPI: Found DSDT in %s.\n", ramfs_dsdt_name); ++ ++ return file_mem; ++} ++#endif diff --git a/patches.suse/acpi-generic-initramfs-table-override-support b/patches.suse/acpi-generic-initramfs-table-override-support new file mode 100644 index 0000000..0f63b2c --- /dev/null +++ b/patches.suse/acpi-generic-initramfs-table-override-support @@ -0,0 +1,401 @@ +From: Jeff Mahoney +Subject: ACPI: generic initramfs table override support +References: bnc#533555 +Patch-mainline: Probably never + +This patch allows the system administrator to override ACPI tables with +versions provided in an initramfs. + +This works by moving the initialization of populate_rootfs earlier +in the initialization so that we can use the VFS file system routines. +The system is initialized enough to support this by acpi_early_init(). +My understanding is that an early version of original patch posted at +http://gaugusch.at/kernel.shtml may have done something similar. + +This version provides the infrastructure to override any ACPI table, but +only provides support for overriding DSDT. If other tables are desired, +extending the support is trivial. + +During early ACPI initialization, when the initramfs is still loaded, +we go through a table of override entries which specify the name of the +table to override, the file name that contains it, and a pointer to the +data loaded from the file. The override tables and headers are kept in +memory so that they available to the ACPI subsystem after the __init +sections and the initramfs have been jettisoned. + +This patch is derived from the work by Éric Piel . + +13 Jan 2010 jeffm: Uses initramfs_{read,write} now to avoid + "scheduling while atomic" warnings. + +Signed-off-by: Jeff Mahoney +--- + + Documentation/acpi/dsdt-override.txt | 8 + + Documentation/acpi/initramfs-add-dsdt.sh | 43 +++++++ + Documentation/acpi/table-override.txt | 21 +++ + Documentation/kernel-parameters.txt | 4 + drivers/acpi/Kconfig | 13 ++ + drivers/acpi/bus.c | 7 + + drivers/acpi/osl.c | 171 +++++++++++++++++++++++++++++++ + include/acpi/acpiosxf.h | 3 + 8 files changed, 269 insertions(+), 1 deletion(-) + +--- a/Documentation/acpi/dsdt-override.txt ++++ b/Documentation/acpi/dsdt-override.txt +@@ -1,7 +1,13 @@ +-Linux supports a method of overriding the BIOS DSDT: ++Linux supports two methods of overriding the BIOS DSDT: + + CONFIG_ACPI_CUSTOM_DSDT builds the image into the kernel. + ++CONFIG_ACPI_CUSTOM_OVERRIDE_INITRAMFS loads the image from ++the initramfs at boot-time. It is more flexible in that it ++does not need to be built into the kernel and tables other ++than DSDT can potentially be overridden. Please see ++Documentation/acpi/table-override.txt for more information. ++ + When to use this method is described in detail on the + Linux/ACPI home page: + http://www.lesswatts.org/projects/acpi/overridingDSDT.php +--- /dev/null ++++ b/Documentation/acpi/initramfs-add-dsdt.sh +@@ -0,0 +1,43 @@ ++#!/bin/bash ++# Adds a DSDT file to the initrd (if it's an initramfs) ++# first argument is the name of archive ++# second argument is the name of the file to add ++# The file will be copied as /DSDT.aml ++ ++# 20060126: fix "Premature end of file" with some old cpio (Roland Robic) ++# 20060205: this time it should really work ++ ++# check the arguments ++if [ $# -ne 2 ]; then ++ program_name=$(basename $0) ++ echo "\ ++$program_name: too few arguments ++Usage: $program_name initrd-name.img DSDT-to-add.aml ++Adds a DSDT file to an initrd (in initramfs format) ++ ++ initrd-name.img: filename of the initrd in initramfs format ++ DSDT-to-add.aml: filename of the DSDT file to add ++ " 1>&2 ++ exit 1 ++fi ++ ++# we should check it's an initramfs ++ ++tempcpio=$(mktemp -d) ++# cleanup on exit, hangup, interrupt, quit, termination ++trap 'rm -rf $tempcpio' 0 1 2 3 15 ++ ++# extract the archive ++gunzip -c "$1" > "$tempcpio"/initramfs.cpio || exit 1 ++ ++# copy the DSDT file at the root of the directory so that we can call it "/DSDT.aml" ++cp -f "$2" "$tempcpio"/DSDT.aml ++ ++# add the file ++cd "$tempcpio" ++(echo DSDT.aml | cpio --quiet -H newc -o -A -O "$tempcpio"/initramfs.cpio) || exit 1 ++cd "$OLDPWD" ++ ++# re-compress the archive ++gzip -c "$tempcpio"/initramfs.cpio > "$1" ++ +--- /dev/null ++++ b/Documentation/acpi/table-override.txt +@@ -0,0 +1,21 @@ ++CONFIG_ACPI_CUSTOM_OVERRIDE_INITRAMFS provides a mechanism for ++the user to add table images to the initramfs for loading at ++runtime. Tables used before expansion of the initramfs may not ++be replaced. Fortunately this list is small and the one most ++typically used, DSDT, is not one of them. ++ ++In order to override a table, the image must be placed in the root ++of the initramfs with a filename of .aml (e.g. DSDT.aml). ++ ++As the ACPI subsystem initializes, it will load the tables into memory ++and override them as the tables are needed. ++ ++This option takes precedence over the in-kernel method provided by ++the ACPI_CUSTOM_DSDT config option. ++ ++When to use these methods is described in detail on the ++Linux/ACPI home page: ++http://www.lesswatts.org/projects/acpi/overridingDSDT.php ++ ++Documentation/initramfs-add-dsdt.sh is provided for convenience ++for use with the CONFIG_ACPI_CUSTOM_OVERRIDE_INITRAMFS method. +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -217,6 +217,10 @@ and is between 256 and 4096 characters. + + acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT + ++ acpi_no_initrd_override [KNL,ACPI] ++ acpi_no_initramfs_override [KNL,ACPI] ++ Disable loading custom ACPI tables from the initramfs ++ + acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS + Format: To spoof as Windows 98: ="Microsoft Windows" + +--- a/drivers/acpi/Kconfig ++++ b/drivers/acpi/Kconfig +@@ -260,6 +260,19 @@ config ACPI_CUSTOM_DSDT + bool + default ACPI_CUSTOM_DSDT_FILE != "" + ++config ACPI_CUSTOM_OVERRIDE_INITRAMFS ++ bool "Load ACPI override tables from initramfs" ++ depends on BLK_DEV_INITRD ++ default n ++ help ++ This option supports loading custom replacement tables by optionally ++ loading them from the initramfs. ++ ++ See Documentation/acpi/table-override.txt ++ ++ If you are not using this feature now, but may use it later, ++ it is safe to say Y here. ++ + config ACPI_BLACKLIST_YEAR + int "Disable ACPI for systems before Jan 1st this year" if X86_32 + default 0 +--- a/drivers/acpi/bus.c ++++ b/drivers/acpi/bus.c +@@ -665,6 +665,13 @@ void __init acpi_early_init(void) + goto error0; + } + ++ status = acpi_load_override_tables(); ++ if (ACPI_FAILURE(status)) { ++ printk(KERN_ERR PREFIX ++ "Unable to load Override Tables\n"); ++ goto error0; ++ } ++ + status = acpi_load_tables(); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX +--- a/drivers/acpi/osl.c ++++ b/drivers/acpi/osl.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -51,6 +52,15 @@ + #include + #include + ++/* We need these to manipulate the global table array. The existing ++ * accessors in acpica/ only pass back the table header and we need ++ * the descriptor. */ ++#include "acpica/acconfig.h" ++#include "acpica/aclocal.h" ++#include "acpica/acglobal.h" ++#include "acpica/acutils.h" ++#include "acpica/actables.h" ++ + #define _COMPONENT ACPI_OS_SERVICES + ACPI_MODULE_NAME("osl"); + #define PREFIX "ACPI: " +@@ -98,6 +108,23 @@ static DEFINE_SPINLOCK(acpi_res_lock); + #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ + static char osi_additional_string[OSI_STRING_LENGTH_MAX]; + ++#ifdef CONFIG_ACPI_CUSTOM_OVERRIDE_INITRAMFS ++static int acpi_no_initrd_override; ++static int __init acpi_no_initrd_override_setup(char *s) ++{ ++ acpi_no_initrd_override = 1; ++ return 1; ++} ++ ++static int __init acpi_no_initramfs_override_setup(char *s) ++{ ++ return acpi_no_initrd_override_setup(s); ++} ++ ++__setup("acpi_no_initrd_override", acpi_no_initrd_override_setup); ++__setup("acpi_no_initramfs_override", acpi_no_initramfs_override_setup); ++#endif ++ + /* + * The story of _OSI(Linux) + * +@@ -352,6 +379,146 @@ acpi_os_predefined_override(const struct + return AE_OK; + } + ++#ifdef CONFIG_ACPI_CUSTOM_OVERRIDE_INITRAMFS ++struct acpi_override_table_entry ++{ ++ const char *name; ++ struct acpi_table_header *table; ++}; ++ ++static struct acpi_override_table_entry acpi_override_table_entries[] = { ++ { .name = "DSDT", }, ++ {} ++}; ++ ++ ++ssize_t initramfs_read(unsigned int fd, const char * buf, size_t count); ++acpi_status __init ++acpi_load_one_override_table(struct acpi_override_table_entry *entry) ++{ ++ int fd, ret; ++ acpi_status err = AE_OK; ++ char filename[10]; /* /DSDT.aml\0 */ ++ struct kstat stat; ++ ++ snprintf(filename, sizeof(filename), "/%.4s.aml", entry->name); ++ ++ fd = sys_open(filename, O_RDONLY, 0); ++ if (fd < 0) ++ return AE_NOT_FOUND; ++ ++ ret = vfs_fstat(fd, &stat); ++ if (ret < 0) { ++ printk(KERN_ERR "ACPI: fstat failed while trying to read %s\n", ++ filename); ++ err = AE_ERROR; ++ goto out; ++ } ++ ++ entry->table = kmalloc(stat.size, GFP_KERNEL); ++ if (!entry->table) { ++ printk(KERN_ERR "ACPI: Could not allocate memory to " ++ "override %s\n", entry->name); ++ err = AE_NO_MEMORY; ++ goto out; ++ } ++ ++ ret = initramfs_read(fd, (char *)entry->table, stat.size); ++ sys_close(fd); ++ if (ret != stat.size) { ++ printk(KERN_ERR "ACPI: Failed to read %s from initramfs\n", ++ entry->name); ++ err = AE_ERROR; ++ goto out; ++ } ++ ++out: ++ if (err != AE_OK) { ++ kfree(entry->table); ++ entry->table = NULL; ++ } ++ sys_close(fd); ++ return ret; ++} ++ ++static void __init ++acpi_replace_table(struct acpi_table_desc *table, struct acpi_table_header *new) ++{ ++ /* This is the top part of acpi_load_table */ ++ memset(table, 0, sizeof(*table)); ++ table->address = ACPI_PTR_TO_PHYSADDR(new); ++ table->pointer = new; ++ table->length = new->length; ++ table->flags |= ACPI_TABLE_ORIGIN_OVERRIDE; ++ table->flags |= ACPI_TABLE_ORIGIN_ALLOCATED; ++ memcpy(table->signature.ascii, new->signature, ACPI_NAME_SIZE); ++} ++ ++/* This replaces tables already opportunistically loaded, but not used. ++ * If the acpica code provided a table descriptor lookup then we wouldn't ++ * need to open code this. */ ++static void __init ++acpi_override_tables(void) ++{ ++ struct acpi_table_header *new = NULL; ++ struct acpi_table_desc *table; ++ acpi_status status; ++ int i; ++ ++ /* This is early enough that we don't need the mutex yet */ ++ for (i = 0; i < acpi_gbl_root_table_list.count; ++i) { ++ if (acpi_tb_is_table_loaded(i)) ++ continue; ++ ++ table = &acpi_gbl_root_table_list.tables[i]; ++ if (!table->pointer) ++ status = acpi_tb_verify_table(table); ++ ++ if (ACPI_FAILURE(status) || !table->pointer) ++ continue; ++ ++ status = acpi_os_table_override(table->pointer, &new); ++ if (ACPI_SUCCESS(status) && new) { ++ acpi_replace_table(table, new); ++ acpi_tb_print_table_header(table->address, new); ++ } ++ } ++} ++ ++acpi_status __init ++acpi_load_override_tables(void) ++{ ++ struct acpi_override_table_entry *entry = acpi_override_table_entries; ++ while (entry && entry->name) { ++ acpi_load_one_override_table(entry); ++ entry++; ++ } ++ ++ acpi_override_tables(); ++ return AE_OK; ++} ++ ++static struct acpi_table_header * ++acpi_get_override_table(const char *name) ++{ ++ struct acpi_override_table_entry *entry = acpi_override_table_entries; ++ ++ while (entry && entry->name) { ++ if (!memcmp(name, entry->name, ACPI_NAME_SIZE)) ++ return entry->table;; ++ entry++; ++ } ++ ++ return NULL; ++} ++#else ++acpi_status ++acpi_load_override_tables(void) ++{ ++ return AE_OK; ++} ++#endif ++ + acpi_status + acpi_os_table_override(struct acpi_table_header * existing_table, + struct acpi_table_header ** new_table) +@@ -365,6 +532,10 @@ acpi_os_table_override(struct acpi_table + if (strncmp(existing_table->signature, "DSDT", 4) == 0) + *new_table = (struct acpi_table_header *)AmlCode; + #endif ++#ifdef CONFIG_ACPI_CUSTOM_OVERRIDE_INITRAMFS ++ if (!acpi_no_initrd_override) ++ *new_table = acpi_get_override_table(existing_table->signature); ++#endif + if (*new_table != NULL) { + printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], " + "this is unsafe: tainting kernel\n", +--- a/include/acpi/acpiosxf.h ++++ b/include/acpi/acpiosxf.h +@@ -92,6 +92,9 @@ acpi_os_predefined_override(const struct + acpi_string * new_val); + + acpi_status ++acpi_load_override_tables(void); ++ ++acpi_status + acpi_os_table_override(struct acpi_table_header *existing_table, + struct acpi_table_header **new_table); + diff --git a/patches.suse/acpi_osi_sle11_ident.patch b/patches.suse/acpi_osi_sle11_ident.patch new file mode 100644 index 0000000..41731a8 --- /dev/null +++ b/patches.suse/acpi_osi_sle11_ident.patch @@ -0,0 +1,29 @@ +From: Thomas Renninger +Subject: Provide possibility for vendors to fix BIOS issues for SLE11 only +References: none +Patch-Mainline: never + +These BIOS issues generally should be solved in the mainine kernel. +But such ACPI interpreter fixes often are very intrusive and impossible to +add as a maintenance update. +This interface should only be used by vendors in emergency case, e.g. +for important pre-loads. Use cases could be: + - BIOS cannot generally be fixed because it's already validated against + Windows OSes, with this patch a SLE11 specific BIOS fix can be added + - Kernel cannot be fixed, because the fix would be too intrusive +In most cases both above scenarios would be valid. + +--- + drivers/acpi/acpica/uteval.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/acpi/acpica/uteval.c ++++ b/drivers/acpi/acpica/uteval.c +@@ -70,6 +70,7 @@ static char *acpi_interfaces_supported[] + "Windows 2001.1", /* Windows Server 2003 */ + "Windows 2001.1 SP1", /* Windows Server 2003 SP1 - Added 03/2006 */ + "Windows 2006", /* Windows Vista - Added 03/2006 */ ++ "SLE11", /* SLE11 identification */ + + /* Feature Group Strings */ + diff --git a/patches.suse/add-initramfs-file_read_write b/patches.suse/add-initramfs-file_read_write new file mode 100644 index 0000000..c67c7bd --- /dev/null +++ b/patches.suse/add-initramfs-file_read_write @@ -0,0 +1,206 @@ +From: Jeff Mahoney +Subject: initramfs: add initramfs_{read,write} +References: bnc#568120 +Patch-mainline: Probably never + + This patch adds initramfs_read and initramfs_write, which will read and + write to the initramfs without traversing huge chunks of the VFS code. + A previous incarnation of the ACPI dynamic table patches ended up + causing "scheduling while atomic" warnings during boot, resulting in a + whole lot of bug reports. + +Signed-off-by: Jeff Mahoney +--- + init/initramfs.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 147 insertions(+), 3 deletions(-) + +--- a/init/initramfs.c ++++ b/init/initramfs.c +@@ -1,5 +1,6 @@ + #include + #include ++#include + #include + #include + #include +@@ -8,6 +9,8 @@ + #include + #include + #include ++#include ++#include + + static __initdata char *message; + static void __init error(char *x) +@@ -333,10 +335,152 @@ static int __init do_name(void) + return 0; + } + ++ssize_t initramfs_file_read(struct file *file, const char *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct address_space *mapping = file->f_mapping; ++ struct iovec iov = { .iov_base = (void __user *) buf, ++ .iov_len = count }; ++ struct iov_iter i; ++ long status = 0; ++ loff_t pos = *ppos; ++ ssize_t read = 0; ++ ++ iov_iter_init(&i, &iov, 1, count, 0); ++ ++ do { ++ struct page *page; ++ pgoff_t index; ++ unsigned long offset; ++ unsigned long bytes; ++ char *data; ++ ++ offset = (pos & (PAGE_CACHE_SIZE - 1)); ++ index = pos >> PAGE_CACHE_SHIFT; ++ bytes = min_t(unsigned long, PAGE_CACHE_SIZE - offset, ++ iov_iter_count(&i)); ++ ++ page = read_mapping_page(mapping, index, NULL); ++ if (IS_ERR(page)) { ++ status = PTR_ERR(page); ++ break; ++ } ++ ++ data = page_address(page); ++ memcpy(i.iov->iov_base + i.iov_offset, data + offset, bytes); ++ ++ iov_iter_advance(&i, bytes); ++ pos += bytes; ++ read += bytes; ++ } while (iov_iter_count(&i)); ++ ++ *ppos = pos; ++ ++ return read ? read : status; ++} ++ ++ssize_t initramfs_file_write(struct file *file, const char * __user buf, ++ size_t count, loff_t *ppos) ++{ ++ struct address_space *mapping = file->f_mapping; ++ struct iovec iov = { .iov_base = (void __user *) buf, ++ .iov_len = count }; ++ long status = 0; ++ ssize_t written = 0; ++ unsigned int flags = 0; ++ loff_t pos = *ppos; ++ struct iov_iter i; ++ ++ iov_iter_init(&i, &iov, 1, count, 0); ++ ++ /* ++ * Copies from kernel address space cannot fail (NFSD is a big user). ++ */ ++ if (segment_eq(get_fs(), KERNEL_DS)) ++ flags |= AOP_FLAG_UNINTERRUPTIBLE; ++ ++ mutex_lock(&mapping->host->i_mutex); ++ ++ do { ++ struct page *page; ++ pgoff_t index; /* Pagecache index for current page */ ++ unsigned long offset; /* Offset into pagecache page */ ++ unsigned long bytes; /* Bytes to write to page */ ++ size_t copied; /* Bytes copied from user */ ++ void *fsdata; ++ char *data; ++ ++ offset = (pos & (PAGE_CACHE_SIZE - 1)); ++ index = pos >> PAGE_CACHE_SHIFT; ++ bytes = min_t(unsigned long, PAGE_CACHE_SIZE - offset, ++ iov_iter_count(&i)); ++ ++ status = simple_write_begin(file, mapping, pos, bytes, flags, ++ &page, &fsdata); ++ if (unlikely(status)) ++ break; ++ data = page_address(page); ++ ++ memcpy(data + offset, i.iov->iov_base + i.iov_offset, bytes); ++ copied = bytes; ++ ++ status = simple_write_end(file, mapping, pos, bytes, copied, ++ page, fsdata); ++ if (unlikely(status < 0)) ++ break; ++ copied = status; ++ ++ iov_iter_advance(&i, copied); ++ pos += copied; ++ written += copied; ++ ++ } while (iov_iter_count(&i)); ++ ++ mutex_unlock(&mapping->host->i_mutex); ++ ++ *ppos = pos; ++ ++ return written ? written : status; ++} ++ ++ssize_t ++initramfs_read(unsigned int fd, const char * buf, size_t count) ++{ ++ struct file *file; ++ ssize_t ret = 0; ++ ++ file = fget(fd); ++ if (file) { ++ loff_t pos = file->f_pos; ++ ret = initramfs_file_read(file, buf, count, &pos); ++ file->f_pos = pos; ++ fput(file); ++ } ++ ++ return ret; ++} ++ ++ssize_t ++initramfs_write(unsigned int fd, const char * buf, size_t count) ++{ ++ struct file *file; ++ ssize_t ret = 0; ++ ++ file = fget(fd); ++ if (file) { ++ loff_t pos = file->f_pos; ++ ret = initramfs_file_write(file, buf, count, &pos); ++ file->f_pos = pos; ++ fput(file); ++ } ++ ++ return ret; ++} ++ + static int __init do_copy(void) + { + if (count >= body_len) { +- sys_write(wfd, victim, body_len); ++ initramfs_write(wfd, victim, body_len); + sys_close(wfd); + do_utime(vcollected, mtime); + kfree(vcollected); +@@ -344,7 +488,7 @@ static int __init do_copy(void) + state = SkipIt; + return 0; + } else { +- sys_write(wfd, victim, count); ++ initramfs_write(wfd, victim, count); + body_len -= count; + eat(count); + return 1; +@@ -589,7 +733,7 @@ static int __init populate_rootfs(void) + "; looks like an initrd\n", err); + fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700); + if (fd >= 0) { +- sys_write(fd, (char *)initrd_start, ++ initramfs_write(fd, (char *)initrd_start, + initrd_end - initrd_start); + sys_close(fd); + free_initrd(); diff --git a/patches.suse/audit-export-logging.patch b/patches.suse/audit-export-logging.patch new file mode 100644 index 0000000..4748d7c --- /dev/null +++ b/patches.suse/audit-export-logging.patch @@ -0,0 +1,46 @@ +From: Tony Jones +Subject: export audit logging symbols +Patch-mainline: not yet + +In SLE11 the following symbols were exported by patch 'apparmor-audit.diff' +With apparmor now being a built-in these exports were removed for SP1 but +NSS requires that audit_log_untrustedstring be exported. + +Re-export both symbols to be consistent with SLE11. + +Signed-Off-by: Tony Jones + +--- + include/linux/audit.h | 3 +++ + kernel/audit.c | 4 +++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +--- a/include/linux/audit.h ++++ b/include/linux/audit.h +@@ -585,6 +585,9 @@ extern void audit_log(struct audit_ + __attribute__((format(printf,4,5))); + + extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type); ++extern void audit_log_vformat(struct audit_buffer *ab, ++ const char *fmt, va_list args) ++ __attribute__((format(printf,2,0))); + extern void audit_log_format(struct audit_buffer *ab, + const char *fmt, ...) + __attribute__((format(printf,2,3))); +--- a/kernel/audit.c ++++ b/kernel/audit.c +@@ -1235,7 +1235,7 @@ static inline int audit_expand(struct au + * will be called a second time. Currently, we assume that a printk + * can't format message larger than 1024 bytes, so we don't either. + */ +-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, ++void audit_log_vformat(struct audit_buffer *ab, const char *fmt, + va_list args) + { + int len, avail; +@@ -1511,3 +1511,5 @@ EXPORT_SYMBOL(audit_log_start); + EXPORT_SYMBOL(audit_log_end); + EXPORT_SYMBOL(audit_log_format); + EXPORT_SYMBOL(audit_log); ++EXPORT_SYMBOL_GPL(audit_log_vformat); ++EXPORT_SYMBOL_GPL(audit_log_untrustedstring); diff --git a/patches.suse/b43-missing-firmware-info.patch b/patches.suse/b43-missing-firmware-info.patch new file mode 100644 index 0000000..243ce26 --- /dev/null +++ b/patches.suse/b43-missing-firmware-info.patch @@ -0,0 +1,37 @@ +Subject: b43: Change firmware missing message to refer to openSUSE script +From: Larry Finger +Patch-mainline: never + +The error message output by b43 contains instructions for obtaining +firmware; however, this naturally does not take account of the script +/usr/sbin/install_bcm43xx_firmware. Modify the messages to suggest use +of the script. + +Signed-off-by: Larry Finger +Modified-by: Jiri Benc +Signed-off-by: Jiri Benc + +--- + drivers/net/wireless/b43/main.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- linux-2.6.34-master.orig/drivers/net/wireless/b43/main.c ++++ linux-2.6.34-master/drivers/net/wireless/b43/main.c +@@ -1976,10 +1976,13 @@ static void b43_release_firmware(struct + static void b43_print_fw_helptext(struct b43_wl *wl, bool error) + { + const char text[] = +- "You must go to " \ +- "http://wireless.kernel.org/en/users/Drivers/b43#devicefirmware " \ +- "and download the correct firmware for this driver version. " \ +- "Please carefully read all instructions on this website.\n"; ++ "Please open a terminal and enter the command " \ ++ "\"sudo /usr/sbin/install_bcm43xx_firmware\" to download " \ ++ "the correct firmware for this driver version. " \ ++ "For an off-line installation, go to " \ ++ "http://en.opensuse.org/HCL/Network_Adapters_(Wireless)/" \ ++ "Broadcom_BCM43xx and follow the instructions in the " \ ++ "\"Installing firmware from RPM packages\" section.\n"; + + if (error) + b43err(wl, text); diff --git a/patches.suse/bootsplash b/patches.suse/bootsplash new file mode 100644 index 0000000..7eb6cc0 --- /dev/null +++ b/patches.suse/bootsplash @@ -0,0 +1,2875 @@ +From: mls@suse.de +Subject: Bootsplash for current kernel +Patch-mainline: no +References: none + +Better support for other VTs. Don't change percent or silent status +when installing a new jpeg. Provide splash_set_percent function. + +Signed-off-by: mls@suse.de + +--- + drivers/char/keyboard.c | 9 + drivers/char/n_tty.c | 9 + drivers/char/vt.c | 25 + drivers/video/Kconfig | 4 + drivers/video/Makefile | 1 + drivers/video/bootsplash/Kconfig | 17 + drivers/video/bootsplash/Makefile | 5 + drivers/video/bootsplash/bootsplash.c | 1017 ++++++++++++++++++++++++++++++++++ + drivers/video/bootsplash/bootsplash.h | 44 + + drivers/video/bootsplash/decode-jpg.c | 957 +++++++++++++++++++++++++++++++ + drivers/video/bootsplash/decode-jpg.h | 35 + + drivers/video/bootsplash/render.c | 328 ++++++++++ + drivers/video/console/bitblit.c | 39 + + drivers/video/console/fbcon.c | 53 + + drivers/video/console/fbcon.h | 28 + drivers/video/vesafb.c | 8 + include/linux/console_struct.h | 3 + include/linux/fb.h | 8 + kernel/panic.c | 13 + 19 files changed, 2601 insertions(+), 2 deletions(-) + +--- a/drivers/char/keyboard.c ++++ b/drivers/char/keyboard.c +@@ -1190,6 +1190,15 @@ static void kbd_keycode(unsigned int key + if (keycode < BTN_MISC && printk_ratelimit()) + printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", keycode); + ++#ifdef CONFIG_BOOTSPLASH ++ /* This code has to be redone for some non-x86 platforms */ ++ if (down == 1 && (keycode == 0x3c || keycode == 0x01)) { /* F2 and ESC on PC keyboard */ ++ extern int splash_verbose(void); ++ if (splash_verbose()) ++ return; ++ } ++#endif ++ + #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ + if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) { + if (!sysrq_down) { +--- a/drivers/char/n_tty.c ++++ b/drivers/char/n_tty.c +@@ -1779,6 +1779,15 @@ do_it_again: + tty->minimum_to_wake = (minimum - (b - buf)); + + if (!input_available_p(tty, 0)) { ++#ifdef CONFIG_BOOTSPLASH ++ if (file->f_dentry->d_inode->i_rdev == MKDEV(TTY_MAJOR,0) || ++ file->f_dentry->d_inode->i_rdev == MKDEV(TTY_MAJOR,1) || ++ file->f_dentry->d_inode->i_rdev == MKDEV(TTYAUX_MAJOR,0) || ++ file->f_dentry->d_inode->i_rdev == MKDEV(TTYAUX_MAJOR,1)) { ++ extern int splash_verbose(void); ++ (void)splash_verbose(); ++ } ++#endif + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { + retval = -EIO; + break; +--- a/drivers/char/vt.c ++++ b/drivers/char/vt.c +@@ -4101,6 +4101,31 @@ void vcs_scr_writew(struct vc_data *vc, + } + } + ++#ifdef CONFIG_BOOTSPLASH ++void con_remap_def_color(struct vc_data *vc, int new_color) ++{ ++ unsigned short *sbuf = vc->vc_screenbuf; ++ unsigned c, len = vc->vc_screenbuf_size >> 1; ++ int old_color; ++ ++ if (sbuf) { ++ old_color = vc->vc_def_color << 8; ++ new_color <<= 8; ++ while(len--) { ++ c = *sbuf; ++ if (((c ^ old_color) & 0xf000) == 0) ++ *sbuf ^= (old_color ^ new_color) & 0xf000; ++ if (((c ^ old_color) & 0x0f00) == 0) ++ *sbuf ^= (old_color ^ new_color) & 0x0f00; ++ sbuf++; ++ } ++ new_color >>= 8; ++ } ++ vc->vc_def_color = vc->vc_color = new_color; ++ update_attr(vc); ++} ++#endif ++ + /* + * Visible symbols for modules + */ +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -2228,4 +2228,8 @@ if FB || SGI_NEWPORT_CONSOLE + source "drivers/video/logo/Kconfig" + endif + ++if FB ++ source "drivers/video/bootsplash/Kconfig" ++endif ++ + endmenu +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -14,6 +14,7 @@ fb-objs := $(f + obj-$(CONFIG_VT) += console/ + obj-$(CONFIG_LOGO) += logo/ + obj-y += backlight/ display/ ++obj-$(CONFIG_BOOTSPLASH) += bootsplash/ + + obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o + obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o +--- /dev/null ++++ b/drivers/video/bootsplash/Kconfig +@@ -0,0 +1,17 @@ ++# ++# Bootsplash configuration ++# ++ ++menu "Bootsplash configuration" ++ ++config BOOTSPLASH ++ bool "Bootup splash screen" ++ depends on FRAMEBUFFER_CONSOLE && FB_VESA ++ default n ++ ---help--- ++ This option enables the Linux bootsplash screen. For more ++ information on the bootsplash screen have a look at ++ http://www.bootsplash.org/. ++ If you are unsure, say N ++endmenu ++ +--- /dev/null ++++ b/drivers/video/bootsplash/Makefile +@@ -0,0 +1,5 @@ ++# Makefile for the Linux bootsplash ++ ++obj-$(CONFIG_BOOTSPLASH) += bootsplash.o ++obj-$(CONFIG_BOOTSPLASH) += decode-jpg.o ++obj-$(CONFIG_BOOTSPLASH) += render.o +--- /dev/null ++++ b/drivers/video/bootsplash/bootsplash.c +@@ -0,0 +1,1017 @@ ++/* ++ * linux/drivers/video/bootsplash/bootsplash.c - ++ * splash screen handling functions. ++ * ++ * (w) 2001-2004 by Volker Poplawski, , ++ * Stefan Reinauer, , ++ * Steffen Winterfeldt, , ++ * Michael Schroeder ++ * ++ * Ideas & SuSE screen work by Ken Wimer, ++ * ++ * For more information on this code check http://www.bootsplash.org/ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "../console/fbcon.h" ++#include "bootsplash.h" ++#include "decode-jpg.h" ++ ++extern struct fb_ops vesafb_ops; ++extern signed char con2fb_map[MAX_NR_CONSOLES]; ++ ++#define SPLASH_VERSION "3.1.6-2004/03/31" ++ ++/* These errors have to match fbcon-jpegdec.h */ ++static unsigned char *jpg_errors[] = { ++ "no SOI found", ++ "not 8 bit", ++ "height mismatch", ++ "width mismatch", ++ "bad width or height", ++ "too many COMPPs", ++ "illegal HV", ++ "quant table selector", ++ "picture is not YCBCR 221111", ++ "unknow CID in scan", ++ "dct not sequential", ++ "wrong marker", ++ "no EOI", ++ "bad tables", ++ "depth mismatch" ++}; ++ ++static struct jpeg_decdata *decdata = 0; /* private decoder data */ ++ ++static int splash_registered = 0; ++static int splash_usesilent = 0; /* shall we display the silentjpeg? */ ++int splash_default = 0xf01; ++ ++static int splash_check_jpeg(unsigned char *jpeg, int width, int height, int depth); ++ ++static int __init splash_setup(char *options) ++{ ++ if(!strncmp("silent", options, 6)) { ++ printk(KERN_INFO "bootsplash: silent mode.\n"); ++ splash_usesilent = 1; ++ /* skip "silent," */ ++ if (strlen(options) == 6) ++ return 0; ++ options += 7; ++ } ++ if(!strncmp("verbose", options, 7)) { ++ printk(KERN_INFO "bootsplash: verbose mode.\n"); ++ splash_usesilent = 0; ++ return 0; ++ } ++ splash_default = simple_strtoul(options, NULL, 0); ++ return 0; ++} ++ ++__setup("splash=", splash_setup); ++ ++ ++static int splash_hasinter(unsigned char *buf, int num) ++{ ++ unsigned char *bufend = buf + num * 12; ++ while(buf < bufend) { ++ if (buf[1] > 127) /* inter? */ ++ return 1; ++ buf += buf[3] > 127 ? 24 : 12; /* blend? */ ++ } ++ return 0; ++} ++ ++static int boxextract(unsigned char *buf, unsigned short *dp, unsigned char *cols, int *blendp) ++{ ++ dp[0] = buf[0] | buf[1] << 8; ++ dp[1] = buf[2] | buf[3] << 8; ++ dp[2] = buf[4] | buf[5] << 8; ++ dp[3] = buf[6] | buf[7] << 8; ++ *(unsigned int *)(cols + 0) = ++ *(unsigned int *)(cols + 4) = ++ *(unsigned int *)(cols + 8) = ++ *(unsigned int *)(cols + 12) = *(unsigned int *)(buf + 8); ++ if (dp[1] > 32767) { ++ dp[1] = ~dp[1]; ++ *(unsigned int *)(cols + 4) = *(unsigned int *)(buf + 12); ++ *(unsigned int *)(cols + 8) = *(unsigned int *)(buf + 16); ++ *(unsigned int *)(cols + 12) = *(unsigned int *)(buf + 20); ++ *blendp = 1; ++ return 24; ++ } ++ return 12; ++} ++ ++static void boxit(unsigned char *pic, int bytes, unsigned char *buf, int num, int percent, int overpaint) ++{ ++ int x, y, i, p, doblend, r, g, b, a, add; ++ unsigned short data1[4]; ++ unsigned char cols1[16]; ++ unsigned short data2[4]; ++ unsigned char cols2[16]; ++ unsigned char *bufend; ++ unsigned short *picp; ++ unsigned int stipple[32], sti, stin, stinn, stixs, stixe, stiys, stiye; ++ int xs, xe, ys, ye, xo, yo; ++ ++ if (num == 0) ++ return; ++ bufend = buf + num * 12; ++ stipple[0] = 0xffffffff; ++ stin = 1; ++ stinn = 0; ++ stixs = stixe = 0; ++ stiys = stiye = 0; ++ while(buf < bufend) { ++ doblend = 0; ++ buf += boxextract(buf, data1, cols1, &doblend); ++ if (data1[0] == 32767 && data1[1] == 32767) { ++ /* box stipple */ ++ if (stinn == 32) ++ continue; ++ if (stinn == 0) { ++ stixs = data1[2]; ++ stixe = data1[3]; ++ stiys = stiye = 0; ++ } else if (stinn == 4) { ++ stiys = data1[2]; ++ stiye = data1[3]; ++ } ++ stipple[stinn++] = (cols1[ 0] << 24) | (cols1[ 1] << 16) | (cols1[ 2] << 8) | cols1[ 3] ; ++ stipple[stinn++] = (cols1[ 4] << 24) | (cols1[ 5] << 16) | (cols1[ 6] << 8) | cols1[ 7] ; ++ stipple[stinn++] = (cols1[ 8] << 24) | (cols1[ 9] << 16) | (cols1[10] << 8) | cols1[11] ; ++ stipple[stinn++] = (cols1[12] << 24) | (cols1[13] << 16) | (cols1[14] << 8) | cols1[15] ; ++ stin = stinn; ++ continue; ++ } ++ stinn = 0; ++ if (data1[0] > 32767) ++ buf += boxextract(buf, data2, cols2, &doblend); ++ if (data1[0] == 32767 && data1[1] == 32766) { ++ /* box copy */ ++ i = 12 * (short)data1[3]; ++ doblend = 0; ++ i += boxextract(buf + i, data1, cols1, &doblend); ++ if (data1[0] > 32767) ++ boxextract(buf + i, data2, cols2, &doblend); ++ } ++ if (data1[0] == 32767) ++ continue; ++ if (data1[2] > 32767) { ++ if (overpaint) ++ continue; ++ data1[2] = ~data1[2]; ++ } ++ if (data1[3] > 32767) { ++ if (percent == 65536) ++ continue; ++ data1[3] = ~data1[3]; ++ } ++ if (data1[0] > 32767) { ++ data1[0] = ~data1[0]; ++ for (i = 0; i < 4; i++) ++ data1[i] = (data1[i] * (65536 - percent) + data2[i] * percent) >> 16; ++ for (i = 0; i < 16; i++) ++ cols1[i] = (cols1[i] * (65536 - percent) + cols2[i] * percent) >> 16; ++ } ++ *(unsigned int *)cols2 = *(unsigned int *)cols1; ++ a = cols2[3]; ++ if (a == 0 && !doblend) ++ continue; ++ ++ if (stixs >= 32768) { ++ xo = xs = (stixs ^ 65535) + data1[0]; ++ xe = stixe ? stixe + data1[0] : data1[2]; ++ } else if (stixe >= 32768) { ++ xs = stixs ? data1[2] - stixs : data1[0]; ++ xe = data1[2] - (stixe ^ 65535); ++ xo = xe + 1; ++ } else { ++ xo = xs = stixs; ++ xe = stixe ? stixe : data1[2]; ++ } ++ if (stiys >= 32768) { ++ yo = ys = (stiys ^ 65535) + data1[1]; ++ ye = stiye ? stiye + data1[1] : data1[3]; ++ } else if (stiye >= 32768) { ++ ys = stiys ? data1[3] - stiys : data1[1]; ++ ye = data1[3] - (stiye ^ 65535); ++ yo = ye + 1; ++ } else { ++ yo = ys = stiys; ++ ye = stiye ? stiye : data1[3]; ++ } ++ xo = 32 - (xo & 31); ++ yo = stin - (yo % stin); ++ if (xs < data1[0]) ++ xs = data1[0]; ++ if (xe > data1[2]) ++ xe = data1[2]; ++ if (ys < data1[1]) ++ ys = data1[1]; ++ if (ye > data1[3]) ++ ye = data1[3]; ++ ++ for (y = ys; y <= ye; y++) { ++ sti = stipple[(y + yo) % stin]; ++ x = (xs + xo) & 31; ++ if (x) ++ sti = (sti << x) | (sti >> (32 - x)); ++ if (doblend) { ++ if ((p = data1[3] - data1[1]) != 0) ++ p = ((y - data1[1]) << 16) / p; ++ for (i = 0; i < 8; i++) ++ cols2[i + 8] = (cols1[i] * (65536 - p) + cols1[i + 8] * p) >> 16; ++ } ++ add = (xs & 1); ++ add ^= (add ^ y) & 1 ? 1 : 3; /* 2x2 ordered dithering */ ++ picp = (unsigned short *)(pic + xs * 2 + y * bytes); ++ for (x = xs; x <= xe; x++) { ++ if (!(sti & 0x80000000)) { ++ sti <<= 1; ++ picp++; ++ add ^= 3; ++ continue; ++ } ++ sti = (sti << 1) | 1; ++ if (doblend) { ++ if ((p = data1[2] - data1[0]) != 0) ++ p = ((x - data1[0]) << 16) / p; ++ for (i = 0; i < 4; i++) ++ cols2[i] = (cols2[i + 8] * (65536 - p) + cols2[i + 12] * p) >> 16; ++ a = cols2[3]; ++ } ++ r = cols2[0]; ++ g = cols2[1]; ++ b = cols2[2]; ++ if (a != 255) { ++ i = *picp; ++ r = ((i >> 8 & 0xf8) * (255 - a) + r * a) / 255; ++ g = ((i >> 3 & 0xfc) * (255 - a) + g * a) / 255; ++ b = ((i << 3 & 0xf8) * (255 - a) + b * a) / 255; ++ } ++ #define CLAMP(x) ((x) >= 256 ? 255 : (x)) ++ i = ((CLAMP(r + add*2+1) & 0xf8) << 8) | ++ ((CLAMP(g + add ) & 0xfc) << 3) | ++ ((CLAMP(b + add*2+1) ) >> 3); ++ *picp++ = i; ++ add ^= 3; ++ } ++ } ++ } ++} ++ ++static int splash_check_jpeg(unsigned char *jpeg, int width, int height, int depth) ++{ ++ int size, err; ++ unsigned char *mem; ++ ++ size = ((width + 15) & ~15) * ((height + 15) & ~15) * (depth >> 3); ++ mem = vmalloc(size); ++ if (!mem) { ++ printk(KERN_INFO "bootsplash: no memory for decoded picture.\n"); ++ return -1; ++ } ++ if (!decdata) ++ decdata = vmalloc(sizeof(*decdata)); ++ if ((err = jpeg_decode(jpeg, mem, ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) ++ printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d)\n",jpg_errors[err - 1], err); ++ vfree(mem); ++ return err ? -1 : 0; ++} ++ ++static void splash_free(struct vc_data *vc, struct fb_info *info) ++{ ++ if (!vc->vc_splash_data) ++ return; ++ if (info->silent_screen_base) ++ info->screen_base = info->silent_screen_base; ++ info->silent_screen_base = 0; ++ if (vc->vc_splash_data->splash_silentjpeg) ++ vfree(vc->vc_splash_data->splash_sboxes); ++ vfree(vc->vc_splash_data); ++ vc->vc_splash_data = 0; ++ info->splash_data = 0; ++} ++ ++static int splash_mkpenguin(struct splash_data *data, int pxo, int pyo, int pwi, int phe, int pr, int pg, int pb) ++{ ++ unsigned char *buf; ++ int i; ++ ++ if (pwi ==0 || phe == 0) ++ return 0; ++ buf = (unsigned char *)data + sizeof(*data); ++ pwi += pxo - 1; ++ phe += pyo - 1; ++ *buf++ = pxo; ++ *buf++ = pxo >> 8; ++ *buf++ = pyo; ++ *buf++ = pyo >> 8; ++ *buf++ = pwi; ++ *buf++ = pwi >> 8; ++ *buf++ = phe; ++ *buf++ = phe >> 8; ++ *buf++ = pr; ++ *buf++ = pg; ++ *buf++ = pb; ++ *buf++ = 0; ++ for (i = 0; i < 12; i++, buf++) ++ *buf = buf[-12]; ++ buf[-24] ^= 0xff; ++ buf[-23] ^= 0xff; ++ buf[-1] = 0xff; ++ return 2; ++} ++ ++static const int splash_offsets[3][16] = { ++ /* len, unit, size, state, fgcol, col, xo, yo, wi, he ++ boxcnt, ssize, sboxcnt, percent, overok, palcnt */ ++ /* V1 */ ++ { 20, -1, 16, -1, -1, -1, 8, 10, 12, 14, ++ -1, -1, -1, -1, -1, -1 }, ++ /* V2 */ ++ { 35, 8, 12, 9, 10, 11, 16, 18, 20, 22, ++ -1, -1, -1, -1, -1, -1 }, ++ /* V3 */ ++ { 38, 8, 12, 9, 10, 11, 16, 18, 20, 22, ++ 24, 28, 32, 34, 36, 37 }, ++}; ++ ++#define SPLASH_OFF_LEN offsets[0] ++#define SPLASH_OFF_UNIT offsets[1] ++#define SPLASH_OFF_SIZE offsets[2] ++#define SPLASH_OFF_STATE offsets[3] ++#define SPLASH_OFF_FGCOL offsets[4] ++#define SPLASH_OFF_COL offsets[5] ++#define SPLASH_OFF_XO offsets[6] ++#define SPLASH_OFF_YO offsets[7] ++#define SPLASH_OFF_WI offsets[8] ++#define SPLASH_OFF_HE offsets[9] ++#define SPLASH_OFF_BOXCNT offsets[10] ++#define SPLASH_OFF_SSIZE offsets[11] ++#define SPLASH_OFF_SBOXCNT offsets[12] ++#define SPLASH_OFF_PERCENT offsets[13] ++#define SPLASH_OFF_OVEROK offsets[14] ++#define SPLASH_OFF_PALCNT offsets[15] ++ ++static inline int splash_getb(unsigned char *pos, int off) ++{ ++ return off == -1 ? 0 : pos[off]; ++} ++ ++static inline int splash_gets(unsigned char *pos, int off) ++{ ++ return off == -1 ? 0 : pos[off] | pos[off + 1] << 8; ++} ++ ++static inline int splash_geti(unsigned char *pos, int off) ++{ ++ return off == -1 ? 0 : ++ pos[off] | pos[off + 1] << 8 | pos[off + 2] << 16 | pos[off + 3] << 24; ++} ++ ++static int splash_getraw(unsigned char *start, unsigned char *end, int *update) ++{ ++ unsigned char *ndata; ++ int version; ++ int splash_size; ++ int unit; ++ int width, height; ++ int silentsize; ++ int boxcnt; ++ int sboxcnt; ++ int palcnt; ++ int i, len; ++ const int *offsets; ++ struct vc_data *vc; ++ struct fb_info *info; ++ struct splash_data *sd; ++ int oldpercent, oldsilent; ++ ++ if (update) ++ *update = -1; ++ ++ if (!update || start[7] < '2' || start[7] > '3' || splash_geti(start, 12) != (int)0xffffffff) ++ printk(KERN_INFO "bootsplash %s: looking for picture...\n", SPLASH_VERSION); ++ ++ for (ndata = start; ndata < end; ndata++) { ++ if (ndata[0] != 'B' || ndata[1] != 'O' || ndata[2] != 'O' || ndata[3] != 'T') ++ continue; ++ if (ndata[4] != 'S' || ndata[5] != 'P' || ndata[6] != 'L' || ndata[7] < '1' || ndata[7] > '3') ++ continue; ++ version = ndata[7] - '0'; ++ offsets = splash_offsets[version - 1]; ++ len = SPLASH_OFF_LEN; ++ unit = splash_getb(ndata, SPLASH_OFF_UNIT); ++ if (unit >= MAX_NR_CONSOLES) ++ continue; ++ if (unit) { ++ vc_allocate(unit); ++ } ++ vc = vc_cons[unit].d; ++ info = registered_fb[(int)con2fb_map[unit]]; ++ width = info->var.xres; ++ height = info->var.yres; ++ splash_size = splash_geti(ndata, SPLASH_OFF_SIZE); ++ if (splash_size == (int)0xffffffff && version > 1) { ++ if ((sd = vc->vc_splash_data) != 0) { ++ int up = 0; ++ i = splash_getb(ndata, SPLASH_OFF_STATE); ++ if (i != 255) { ++ sd->splash_state = i; ++ up = -1; ++ } ++ i = splash_getb(ndata, SPLASH_OFF_FGCOL); ++ if (i != 255) { ++ sd->splash_fg_color = i; ++ up = -1; ++ } ++ i = splash_getb(ndata, SPLASH_OFF_COL); ++ if (i != 255) { ++ sd->splash_color = i; ++ up = -1; ++ } ++ boxcnt = sboxcnt = 0; ++ if (ndata + len <= end) { ++ boxcnt = splash_gets(ndata, SPLASH_OFF_BOXCNT); ++ sboxcnt = splash_gets(ndata, SPLASH_OFF_SBOXCNT); ++ } ++ if (boxcnt) { ++ i = splash_gets(ndata, len); ++ if (boxcnt + i <= sd->splash_boxcount && ndata + len + 2 + boxcnt * 12 <= end) { ++ ++ if (splash_geti(ndata, len + 2) != 0x7ffd7fff || !memcmp(ndata + len + 2, sd->splash_boxes + i * 12, 8)) { ++ ++ memcpy(sd->splash_boxes + i * 12, ndata + len + 2, boxcnt * 12); ++ up |= 1; ++ } ++ } ++ len += boxcnt * 12 + 2; ++ } ++ if (sboxcnt) { ++ i = splash_gets(ndata, len); ++ if (sboxcnt + i <= sd->splash_sboxcount && ndata + len + 2 + sboxcnt * 12 <= end) { ++ if (splash_geti(ndata, len + 2) != 0x7ffd7fff || !memcmp(ndata + len + 2, sd->splash_sboxes + i * 12, 8)) { ++ memcpy(sd->splash_sboxes + i * 12, ndata + len + 2, sboxcnt * 12); ++ up |= 2; ++ } ++ } ++ } ++ if (update) ++ *update = up; ++ } ++ return unit; ++ } ++ if (splash_size == 0) { ++ printk(KERN_INFO "bootsplash: ...found, freeing memory.\n"); ++ if (vc->vc_splash_data) ++ splash_free(vc, info); ++ return unit; ++ } ++ boxcnt = splash_gets(ndata, SPLASH_OFF_BOXCNT); ++ palcnt = 3 * splash_getb(ndata, SPLASH_OFF_PALCNT); ++ if (ndata + len + splash_size > end) { ++ printk(KERN_ERR "bootsplash: ...found, but truncated!\n"); ++ return -1; ++ } ++ if (!jpeg_check_size(ndata + len + boxcnt * 12 + palcnt, width, height)) { ++ ndata += len + splash_size - 1; ++ continue; ++ } ++ if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, width, height, info->var.bits_per_pixel)) ++ return -1; ++ silentsize = splash_geti(ndata, SPLASH_OFF_SSIZE); ++ if (silentsize) ++ printk(KERN_INFO "bootsplash: silentjpeg size %d bytes\n", silentsize); ++ if (silentsize >= splash_size) { ++ printk(KERN_ERR "bootsplash: bigger than splashsize!\n"); ++ return -1; ++ } ++ splash_size -= silentsize; ++ if (!splash_usesilent) ++ silentsize = 0; ++ else if (height * 2 * info->fix.line_length > info->fix.smem_len) { ++ printk(KERN_WARNING "bootsplash: does not fit into framebuffer.\n"); ++ silentsize = 0; ++ } ++ sboxcnt = splash_gets(ndata, SPLASH_OFF_SBOXCNT); ++ if (silentsize) { ++ unsigned char *simage = ndata + len + splash_size + 12 * sboxcnt; ++ if (!jpeg_check_size(simage, width, height) || ++ splash_check_jpeg(simage, width, height, info->var.bits_per_pixel)) { ++ printk(KERN_WARNING "bootsplash: error in silent jpeg.\n"); ++ silentsize = 0; ++ } ++ } ++ oldpercent = -1; ++ oldsilent = -1; ++ if (vc->vc_splash_data) { ++ oldpercent = vc->vc_splash_data->splash_percent; ++ oldsilent = vc->vc_splash_data->splash_dosilent; ++ splash_free(vc, info); ++ } ++ vc->vc_splash_data = sd = vmalloc(sizeof(*sd) + splash_size + (version < 3 ? 2 * 12 : 0)); ++ if (!sd) ++ break; ++ sd->splash_silentjpeg = 0; ++ sd->splash_sboxes = 0; ++ sd->splash_sboxcount = 0; ++ if (silentsize) { ++ sd->splash_silentjpeg = vmalloc(silentsize); ++ if (sd->splash_silentjpeg) { ++ memcpy(sd->splash_silentjpeg, ndata + len + splash_size, silentsize); ++ sd->splash_sboxes = vc->vc_splash_data->splash_silentjpeg; ++ sd->splash_silentjpeg += 12 * sboxcnt; ++ sd->splash_sboxcount = sboxcnt; ++ } ++ } ++ sd->splash_state = splash_getb(ndata, SPLASH_OFF_STATE); ++ sd->splash_fg_color = splash_getb(ndata, SPLASH_OFF_FGCOL); ++ sd->splash_color = splash_getb(ndata, SPLASH_OFF_COL); ++ sd->splash_overpaintok = splash_getb(ndata, SPLASH_OFF_OVEROK); ++ sd->splash_text_xo = splash_gets(ndata, SPLASH_OFF_XO); ++ sd->splash_text_yo = splash_gets(ndata, SPLASH_OFF_YO); ++ sd->splash_text_wi = splash_gets(ndata, SPLASH_OFF_WI); ++ sd->splash_text_he = splash_gets(ndata, SPLASH_OFF_HE); ++ sd->splash_percent = oldpercent == -1 ? splash_gets(ndata, SPLASH_OFF_PERCENT) : oldpercent; ++ if (version == 1) { ++ sd->splash_text_xo *= 8; ++ sd->splash_text_wi *= 8; ++ sd->splash_text_yo *= 16; ++ sd->splash_text_he *= 16; ++ sd->splash_color = (splash_default >> 8) & 0x0f; ++ sd->splash_fg_color = (splash_default >> 4) & 0x0f; ++ sd->splash_state = splash_default & 1; ++ } ++ if (sd->splash_text_xo + sd->splash_text_wi > width || sd->splash_text_yo + sd->splash_text_he > height) { ++ splash_free(vc, info); ++ printk(KERN_ERR "bootsplash: found, but has oversized text area!\n"); ++ return -1; ++ } ++ if (!vc_cons[unit].d || info->fbops->fb_imageblit != cfb_imageblit) { ++ splash_free(vc, info); ++ printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n"); ++ return -1; ++ } ++ printk(KERN_INFO "bootsplash: ...found (%dx%d, %d bytes, v%d).\n", width, height, splash_size, version); ++ if (version == 1) { ++ printk(KERN_WARNING "bootsplash: Using deprecated v1 header. Updating your splash utility recommended.\n"); ++ printk(KERN_INFO "bootsplash: Find the latest version at http://www.bootsplash.org/\n"); ++ } ++ ++ /* fake penguin box for older formats */ ++ if (version == 1) ++ boxcnt = splash_mkpenguin(sd, sd->splash_text_xo + 10, sd->splash_text_yo + 10, sd->splash_text_wi - 20, sd->splash_text_he - 20, 0xf0, 0xf0, 0xf0); ++ else if (version == 2) ++ boxcnt = splash_mkpenguin(sd, splash_gets(ndata, 24), splash_gets(ndata, 26), splash_gets(ndata, 28), splash_gets(ndata, 30), splash_getb(ndata, 32), splash_getb(ndata, 33), splash_getb(ndata, 34)); ++ ++ memcpy((char *)sd + sizeof(*sd) + (version < 3 ? boxcnt * 12 : 0), ndata + len, splash_size); ++ sd->splash_boxcount = boxcnt; ++ sd->splash_boxes = (unsigned char *)sd + sizeof(*sd); ++ sd->splash_palette = sd->splash_boxes + boxcnt * 12; ++ sd->splash_jpeg = sd->splash_palette + palcnt; ++ sd->splash_palcnt = palcnt / 3; ++ sd->splash_dosilent = sd->splash_silentjpeg != 0 ? (oldsilent == -1 ? 1 : oldsilent) : 0; ++ return unit; ++ } ++ printk(KERN_ERR "bootsplash: ...no good signature found.\n"); ++ return -1; ++} ++ ++int splash_verbose(void) ++{ ++ struct vc_data *vc; ++ struct fb_info *info; ++ ++ if (!splash_usesilent) ++ return 0; ++ ++ vc = vc_cons[0].d; ++ ++ if (!vc || !vc->vc_splash_data || !vc->vc_splash_data->splash_state) ++ return 0; ++ if (fg_console != vc->vc_num) ++ return 0; ++ if (!vc->vc_splash_data->splash_silentjpeg || !vc->vc_splash_data->splash_dosilent) ++ return 0; ++ vc->vc_splash_data->splash_dosilent = 0; ++ info = registered_fb[(int)con2fb_map[0]]; ++ if (!info->silent_screen_base) ++ return 0; ++ splashcopy(info->silent_screen_base, info->screen_base, info->var.yres, info->var.xres, info->fix.line_length, info->fix.line_length); ++ info->screen_base = info->silent_screen_base; ++ info->silent_screen_base = 0; ++ return 1; ++} ++ ++static void splash_off(struct fb_info *info) ++{ ++ if (info->silent_screen_base) ++ info->screen_base = info->silent_screen_base; ++ info->silent_screen_base = 0; ++ info->splash_data = 0; ++ if (info->splash_pic) ++ vfree(info->splash_pic); ++ info->splash_pic = 0; ++ info->splash_pic_size = 0; ++} ++ ++int splash_prepare(struct vc_data *vc, struct fb_info *info) ++{ ++ int err; ++ int width, height, depth, size, sbytes; ++ ++ if (!vc->vc_splash_data || !vc->vc_splash_data->splash_state) { ++ if (decdata) ++ vfree(decdata); ++ decdata = 0; ++ splash_off(info); ++ return -1; ++ } ++ ++ width = info->var.xres; ++ height = info->var.yres; ++ depth = info->var.bits_per_pixel; ++ if (depth != 16) { /* Other targets might need fixing */ ++ splash_off(info); ++ return -2; ++ } ++ ++ sbytes = ((width + 15) & ~15) * (depth >> 3); ++ size = sbytes * ((height + 15) & ~15); ++ if (size != info->splash_pic_size) ++ splash_off(info); ++ if (!info->splash_pic) ++ info->splash_pic = vmalloc(size); ++ ++ if (!info->splash_pic) { ++ printk(KERN_INFO "bootsplash: not enough memory.\n"); ++ splash_off(info); ++ return -3; ++ } ++ ++ if (!decdata) ++ decdata = vmalloc(sizeof(*decdata)); ++ ++ if (vc->vc_splash_data->splash_silentjpeg && vc->vc_splash_data->splash_dosilent) { ++ /* fill area after framebuffer with other jpeg */ ++ if ((err = jpeg_decode(vc->vc_splash_data->splash_silentjpeg, info->splash_pic, ++ ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { ++ printk(KERN_INFO "bootsplash: error while decompressing silent picture: %s (%d)\n", jpg_errors[err - 1], err); ++ if (info->silent_screen_base) ++ info->screen_base = info->silent_screen_base; ++ vc->vc_splash_data->splash_dosilent = 0; ++ } else { ++ if (vc->vc_splash_data->splash_sboxcount) ++ boxit(info->splash_pic, sbytes, vc->vc_splash_data->splash_sboxes, ++ vc->vc_splash_data->splash_sboxcount, vc->vc_splash_data->splash_percent, 0); ++ ++ if (!info->silent_screen_base) ++ info->silent_screen_base = info->screen_base; ++ splashcopy(info->silent_screen_base, info->splash_pic, info->var.yres, info->var.xres, info->fix.line_length, sbytes); ++ info->screen_base = info->silent_screen_base + info->fix.line_length * info->var.yres; ++ } ++ } else if (info->silent_screen_base) ++ info->screen_base = info->silent_screen_base; ++ ++ if ((err = jpeg_decode(vc->vc_splash_data->splash_jpeg, info->splash_pic, ++ ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { ++ printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d) .\n", jpg_errors[err - 1], err); ++ splash_off(info); ++ return -4; ++ } ++ info->splash_pic_size = size; ++ info->splash_bytes = sbytes; ++ if (vc->vc_splash_data->splash_boxcount) ++ boxit(info->splash_pic, sbytes, vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount, vc->vc_splash_data->splash_percent, 0); ++ if (vc->vc_splash_data->splash_state) ++ info->splash_data = vc->vc_splash_data; ++ else ++ splash_off(info); ++ return 0; ++} ++ ++ ++#ifdef CONFIG_PROC_FS ++ ++#include ++ ++static int splash_read_proc(char *buffer, char **start, off_t offset, int size, ++ int *eof, void *data); ++static int splash_write_proc(struct file *file, const char *buffer, ++ unsigned long count, void *data); ++static int splash_status(struct vc_data *vc); ++static int splash_recolor(struct vc_data *vc); ++static int splash_proc_register(void); ++ ++static struct proc_dir_entry *proc_splash; ++ ++static int splash_recolor(struct vc_data *vc) ++{ ++ if (!vc->vc_splash_data) ++ return -1; ++ if (!vc->vc_splash_data->splash_state) ++ return 0; ++ con_remap_def_color(vc, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color); ++ if (fg_console == vc->vc_num) { ++ update_region(vc, ++ vc->vc_origin + vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ } ++ return 0; ++} ++ ++static int splash_status(struct vc_data *vc) ++{ ++ struct fb_info *info; ++ printk(KERN_INFO "bootsplash: status on console %d changed to %s\n", vc->vc_num, vc->vc_splash_data && vc->vc_splash_data->splash_state ? "on" : "off"); ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ if (fg_console == vc->vc_num) ++ splash_prepare(vc, info); ++ if (vc->vc_splash_data && vc->vc_splash_data->splash_state) { ++ con_remap_def_color(vc, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color); ++ /* vc_resize also calls con_switch which resets yscroll */ ++ vc_resize(vc, vc->vc_splash_data->splash_text_wi / vc->vc_font.width, vc->vc_splash_data->splash_text_he / vc->vc_font.height); ++ if (fg_console == vc->vc_num) { ++ update_region(vc, ++ vc->vc_origin + vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ splash_clear_margins(vc->vc_splash_data, vc, info, 0); ++ } ++ } else { ++ /* Switch bootsplash off */ ++ con_remap_def_color(vc, 0x07); ++ vc_resize(vc, info->var.xres / vc->vc_font.width, info->var.yres / vc->vc_font.height); ++ } ++ return 0; ++} ++ ++static int splash_read_proc(char *buffer, char **start, off_t offset, int size, ++ int *eof, void *data) ++{ ++ int len = 0; ++ off_t begin = 0; ++ struct vc_data *vc = vc_cons[0].d; ++ struct fb_info *info = registered_fb[(int)con2fb_map[0]]; ++ int color = vc->vc_splash_data ? vc->vc_splash_data->splash_color << 4 | ++ vc->vc_splash_data->splash_fg_color : splash_default >> 4; ++ int status = vc->vc_splash_data ? vc->vc_splash_data->splash_state & 1 : 0; ++ len += sprintf(buffer + len, "Splash screen v%s (0x%02x, %dx%d%s): %s\n", ++ SPLASH_VERSION, color, info->var.xres, info->var.yres, ++ (vc->vc_splash_data ? vc->vc_splash_data->splash_dosilent : 0)? ", silent" : "", ++ status ? "on" : "off"); ++ if (offset >= begin + len) ++ return 0; ++ ++ *start = buffer + (begin - offset); ++ ++ return (size < begin + len - offset ? size : begin + len - offset); ++} ++ ++void splash_set_percent(struct vc_data *vc, int pe) ++{ ++ struct fb_info *info; ++ struct fbcon_ops *ops; ++ int oldpe; ++ ++ if (pe < 0) ++ pe = 0; ++ if (pe > 65535) ++ pe = 65535; ++ pe += pe > 32767;; ++ ++ if (!vc->vc_splash_data || vc->vc_splash_data->splash_percent == pe) ++ return; ++ ++ oldpe = vc->vc_splash_data->splash_percent; ++ vc->vc_splash_data->splash_percent = pe; ++ if (fg_console != vc->vc_num || !vc->vc_splash_data->splash_state) { ++ return; ++ } ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ops = info->fbcon_par; ++ if (ops->blank_state) ++ return; ++ if (!vc->vc_splash_data->splash_overpaintok || pe == 65536 || pe < oldpe) { ++ if (splash_hasinter(vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount)) ++ splash_status(vc); ++ else ++ splash_prepare(vc, info); ++ } else { ++ if (vc->vc_splash_data->splash_silentjpeg && vc->vc_splash_data->splash_dosilent && info->silent_screen_base) ++ boxit(info->silent_screen_base, info->fix.line_length, vc->vc_splash_data->splash_sboxes, vc->vc_splash_data->splash_sboxcount, vc->vc_splash_data->splash_percent, 1); ++ boxit(info->screen_base, info->fix.line_length, vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount, vc->vc_splash_data->splash_percent, 1); ++ } ++} ++ ++static int splash_write_proc(struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ int new, unit; ++ struct vc_data *vc; ++ ++ if (!buffer || !splash_default) ++ return count; ++ ++ acquire_console_sem(); ++ unit = 0; ++ if (buffer[0] == '@' && buffer[1] >= '0' && buffer[1] <= '9') { ++ unit = buffer[1] - '0'; ++ buffer += 2; ++ if (*buffer >= '0' && *buffer <= '9') { ++ unit = unit * 10 + *buffer - '0'; ++ buffer++; ++ } ++ if (*buffer == ' ') ++ buffer++; ++ if (unit >= MAX_NR_CONSOLES || !vc_cons[unit].d) { ++ release_console_sem(); ++ return count; ++ } ++ } ++ vc = vc_cons[unit].d; ++ if (!strncmp(buffer, "redraw", 6)) { ++ splash_status(vc); ++ release_console_sem(); ++ return count; ++ } ++ if (!strncmp(buffer, "show", 4) || !strncmp(buffer, "hide", 4)) { ++ int pe; ++ ++ if (buffer[4] == ' ' && buffer[5] == 'p') ++ pe = 0; ++ else if (buffer[4] == '\n') ++ pe = 65535; ++ else ++ pe = simple_strtoul(buffer + 5, NULL, 0); ++ if (pe < 0) ++ pe = 0; ++ if (pe > 65535) ++ pe = 65535; ++ if (*buffer == 'h') ++ pe = 65535 - pe; ++ splash_set_percent(vc, pe); ++ release_console_sem(); ++ return count; ++ } ++ if (!strncmp(buffer,"silent\n",7) || !strncmp(buffer,"verbose\n",8)) { ++ if (vc->vc_splash_data && vc->vc_splash_data->splash_silentjpeg) { ++ if (vc->vc_splash_data->splash_dosilent != (buffer[0] == 's')) { ++ vc->vc_splash_data->splash_dosilent = buffer[0] == 's'; ++ splash_status(vc); ++ } ++ } ++ release_console_sem(); ++ return count; ++ } ++ if (!strncmp(buffer,"freesilent\n",11)) { ++ if (vc->vc_splash_data && vc->vc_splash_data->splash_silentjpeg) { ++ printk(KERN_INFO "bootsplash: freeing silent jpeg\n"); ++ vc->vc_splash_data->splash_silentjpeg = 0; ++ vfree(vc->vc_splash_data->splash_sboxes); ++ vc->vc_splash_data->splash_sboxes = 0; ++ vc->vc_splash_data->splash_sboxcount = 0; ++ if (vc->vc_splash_data->splash_dosilent) ++ splash_status(vc); ++ vc->vc_splash_data->splash_dosilent = 0; ++ } ++ release_console_sem(); ++ return count; ++ } ++ ++ if (!strncmp(buffer, "BOOTSPL", 7)) { ++ int up = -1; ++ unit = splash_getraw((unsigned char *)buffer, (unsigned char *)buffer + count, &up); ++ if (unit >= 0) { ++ vc = vc_cons[unit].d; ++ if (up == -1) ++ splash_status(vc); ++ else { ++ struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ if (ops->blank_state) ++ up = 0; ++ if ((up & 2) != 0 && vc->vc_splash_data->splash_silentjpeg && vc->vc_splash_data->splash_dosilent && info->silent_screen_base) ++ boxit(info->silent_screen_base, info->fix.line_length, vc->vc_splash_data->splash_sboxes, vc->vc_splash_data->splash_sboxcount, vc->vc_splash_data->splash_percent, 1); ++ if ((up & 1) != 0) ++ boxit(info->screen_base, info->fix.line_length, vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount, vc->vc_splash_data->splash_percent, 1); ++ } ++ } ++ release_console_sem(); ++ return count; ++ } ++ if (!vc->vc_splash_data) { ++ release_console_sem(); ++ return count; ++ } ++ if (buffer[0] == 't') { ++ vc->vc_splash_data->splash_state ^= 1; ++ splash_status(vc); ++ release_console_sem(); ++ return count; ++ } ++ new = simple_strtoul(buffer, NULL, 0); ++ if (new > 1) { ++ /* expert user */ ++ vc->vc_splash_data->splash_color = new >> 8 & 0xff; ++ vc->vc_splash_data->splash_fg_color = new >> 4 & 0x0f; ++ } ++ if ((new & 1) == vc->vc_splash_data->splash_state) ++ splash_recolor(vc); ++ else { ++ vc->vc_splash_data->splash_state = new & 1; ++ splash_status(vc); ++ } ++ release_console_sem(); ++ return count; ++} ++ ++static int splash_proc_register(void) ++{ ++ if ((proc_splash = create_proc_entry("splash", 0, 0))) { ++ proc_splash->read_proc = splash_read_proc; ++ proc_splash->write_proc = splash_write_proc; ++ return 0; ++ } ++ return 1; ++} ++ ++# if 0 ++static int splash_proc_unregister(void) ++{ ++ if (proc_splash) ++ remove_proc_entry("splash", 0); ++ return 0; ++} ++# endif ++#endif /* CONFIG_PROC_FS */ ++ ++void splash_init(void) ++{ ++ struct fb_info *info; ++ struct vc_data *vc; ++ int isramfs = 1; ++ int fd; ++ int len; ++ int max_len = 1024*1024*2; ++ char *mem; ++ ++ if (splash_registered) ++ return; ++ vc = vc_cons[0].d; ++ info = registered_fb[0]; ++ if (!vc || !info || info->var.bits_per_pixel != 16) ++ return; ++#ifdef CONFIG_PROC_FS ++ splash_proc_register(); ++#endif ++ splash_registered = 1; ++ if (vc->vc_splash_data) ++ return; ++ if ((fd = sys_open("/bootsplash", O_RDONLY, 0)) < 0) { ++ isramfs = 0; ++ fd = sys_open("/initrd.image", O_RDONLY, 0); ++ } ++ if (fd < 0) ++ return; ++ if ((len = (int)sys_lseek(fd, (off_t)0, 2)) <= 0) { ++ sys_close(fd); ++ return; ++ } ++ /* Don't look for more than the last 2MB */ ++ if (len > max_len) { ++ printk( KERN_INFO "bootsplash: scanning last %dMB of initrd for signature\n", ++ max_len>>20); ++ sys_lseek(fd, (off_t)(len - max_len), 0); ++ len = max_len; ++ } else { ++ sys_lseek(fd, (off_t)0, 0); ++ } ++ ++ mem = vmalloc(len); ++ if (mem) { ++ acquire_console_sem(); ++ if ((int)sys_read(fd, mem, len) == len && splash_getraw((unsigned char *)mem, (unsigned char *)mem + len, (int *)0) == 0 && vc->vc_splash_data) ++ vc->vc_splash_data->splash_state = splash_default & 1; ++ release_console_sem(); ++ vfree(mem); ++ } ++ sys_close(fd); ++ if (isramfs) ++ sys_unlink("/bootsplash"); ++ return; ++} ++ +--- /dev/null ++++ b/drivers/video/bootsplash/bootsplash.h +@@ -0,0 +1,44 @@ ++/* ++ * linux/drivers/video/bootsplash/bootsplash.h - splash screen definition. ++ * ++ * (w) 2001-2003 by Volker Poplawski, ++ * Stefan Reinauer, ++ * ++ * ++ * idea and SuSE screen work by Ken Wimer, ++ */ ++ ++#ifndef __BOOTSPLASH_H ++#define __BOOTSPLASH_H ++ ++struct fb_info; ++ ++/* splash.c */ ++extern int splash_prepare(struct vc_data *, struct fb_info *); ++extern void splash_init(void); ++ ++/* splash_render.c */ ++extern void splash_putcs(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++ const unsigned short *s, int count, int ypos, int xpos); ++extern void splash_putc(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++ int c, int ypos, int xpos); ++extern void splashcopy(u8 *dst, u8 *src, int height, int width, int dstbytes, int srcbytes); ++extern void splash_clear(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int height, int width); ++extern void splash_bmove(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width); ++extern void splash_clear_margins(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++ int bottom_only); ++extern int splash_cursor(struct splash_data *sd, struct fb_info *info, struct fb_cursor *cursor); ++extern void splash_bmove_redraw(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++ int y, int sx, int dx, int width); ++extern void splash_blank(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++ int blank); ++ ++/* vt.c */ ++extern void con_remap_def_color(struct vc_data *vc, int new_color); ++ ++extern void acquire_console_sem(void); ++extern void release_console_sem(void); ++ ++#endif +--- /dev/null ++++ b/drivers/video/bootsplash/decode-jpg.c +@@ -0,0 +1,957 @@ ++/* ++ * linux/drivers/video/bootsplash/decode-jpg.c - a tiny jpeg decoder. ++ * ++ * (w) August 2001 by Michael Schroeder, ++ * ++ */ ++ ++#include ++#include ++ ++#include "decode-jpg.h" ++ ++#define ISHIFT 11 ++ ++#define IFIX(a) ((int)((a) * (1 << ISHIFT) + .5)) ++#define IMULT(a, b) (((a) * (b)) >> ISHIFT) ++#define ITOINT(a) ((a) >> ISHIFT) ++ ++#ifndef __P ++# define __P(x) x ++#endif ++ ++/* special markers */ ++#define M_BADHUFF -1 ++#define M_EOF 0x80 ++ ++struct in { ++ unsigned char *p; ++ unsigned int bits; ++ int left; ++ int marker; ++ ++ int (*func) __P((void *)); ++ void *data; ++}; ++ ++/*********************************/ ++struct dec_hufftbl; ++struct enc_hufftbl; ++ ++union hufftblp { ++ struct dec_hufftbl *dhuff; ++ struct enc_hufftbl *ehuff; ++}; ++ ++struct scan { ++ int dc; /* old dc value */ ++ ++ union hufftblp hudc; ++ union hufftblp huac; ++ int next; /* when to switch to next scan */ ++ ++ int cid; /* component id */ ++ int hv; /* horiz/vert, copied from comp */ ++ int tq; /* quant tbl, copied from comp */ ++}; ++ ++/*********************************/ ++ ++#define DECBITS 10 /* seems to be the optimum */ ++ ++struct dec_hufftbl { ++ int maxcode[17]; ++ int valptr[16]; ++ unsigned char vals[256]; ++ unsigned int llvals[1 << DECBITS]; ++}; ++ ++static void decode_mcus __P((struct in *, int *, int, struct scan *, int *)); ++static int dec_readmarker __P((struct in *)); ++static void dec_makehuff __P((struct dec_hufftbl *, int *, unsigned char *)); ++ ++static void setinput __P((struct in *, unsigned char *)); ++/*********************************/ ++ ++#undef PREC ++#define PREC int ++ ++static void idctqtab __P((unsigned char *, PREC *)); ++static void idct __P((int *, int *, PREC *, PREC, int)); ++static void scaleidctqtab __P((PREC *, PREC)); ++ ++/*********************************/ ++ ++static void initcol __P((PREC[][64])); ++ ++static void col221111 __P((int *, unsigned char *, int)); ++static void col221111_16 __P((int *, unsigned char *, int)); ++ ++/*********************************/ ++ ++#define M_SOI 0xd8 ++#define M_APP0 0xe0 ++#define M_DQT 0xdb ++#define M_SOF0 0xc0 ++#define M_DHT 0xc4 ++#define M_DRI 0xdd ++#define M_SOS 0xda ++#define M_RST0 0xd0 ++#define M_EOI 0xd9 ++#define M_COM 0xfe ++ ++static unsigned char *datap; ++ ++static int getbyte(void) ++{ ++ return *datap++; ++} ++ ++static int getword(void) ++{ ++ int c1, c2; ++ c1 = *datap++; ++ c2 = *datap++; ++ return c1 << 8 | c2; ++} ++ ++struct comp { ++ int cid; ++ int hv; ++ int tq; ++}; ++ ++#define MAXCOMP 4 ++struct jpginfo { ++ int nc; /* number of components */ ++ int ns; /* number of scans */ ++ int dri; /* restart interval */ ++ int nm; /* mcus til next marker */ ++ int rm; /* next restart marker */ ++}; ++ ++static struct jpginfo info; ++static struct comp comps[MAXCOMP]; ++ ++static struct scan dscans[MAXCOMP]; ++ ++static unsigned char quant[4][64]; ++ ++static struct dec_hufftbl dhuff[4]; ++ ++#define dec_huffdc (dhuff + 0) ++#define dec_huffac (dhuff + 2) ++ ++static struct in in; ++ ++static int readtables(int till) ++{ ++ int m, l, i, j, lq, pq, tq; ++ int tc, th, tt; ++ ++ for (;;) { ++ if (getbyte() != 0xff) ++ return -1; ++ if ((m = getbyte()) == till) ++ break; ++ ++ switch (m) { ++ case 0xc2: ++ return 0; ++ ++ case M_DQT: ++ lq = getword(); ++ while (lq > 2) { ++ pq = getbyte(); ++ tq = pq & 15; ++ if (tq > 3) ++ return -1; ++ pq >>= 4; ++ if (pq != 0) ++ return -1; ++ for (i = 0; i < 64; i++) ++ quant[tq][i] = getbyte(); ++ lq -= 64 + 1; ++ } ++ break; ++ ++ case M_DHT: ++ l = getword(); ++ while (l > 2) { ++ int hufflen[16], k; ++ unsigned char huffvals[256]; ++ ++ tc = getbyte(); ++ th = tc & 15; ++ tc >>= 4; ++ tt = tc * 2 + th; ++ if (tc > 1 || th > 1) ++ return -1; ++ for (i = 0; i < 16; i++) ++ hufflen[i] = getbyte(); ++ l -= 1 + 16; ++ k = 0; ++ for (i = 0; i < 16; i++) { ++ for (j = 0; j < hufflen[i]; j++) ++ huffvals[k++] = getbyte(); ++ l -= hufflen[i]; ++ } ++ dec_makehuff(dhuff + tt, hufflen, ++ huffvals); ++ } ++ break; ++ ++ case M_DRI: ++ l = getword(); ++ info.dri = getword(); ++ break; ++ ++ default: ++ l = getword(); ++ while (l-- > 2) ++ getbyte(); ++ break; ++ } ++ } ++ return 0; ++} ++ ++static void dec_initscans(void) ++{ ++ int i; ++ ++ info.nm = info.dri + 1; ++ info.rm = M_RST0; ++ for (i = 0; i < info.ns; i++) ++ dscans[i].dc = 0; ++} ++ ++static int dec_checkmarker(void) ++{ ++ int i; ++ ++ if (dec_readmarker(&in) != info.rm) ++ return -1; ++ info.nm = info.dri; ++ info.rm = (info.rm + 1) & ~0x08; ++ for (i = 0; i < info.ns; i++) ++ dscans[i].dc = 0; ++ return 0; ++} ++ ++int jpeg_check_size(unsigned char *buf, int width, int height) ++{ ++ datap = buf; ++ getbyte(); ++ getbyte(); ++ readtables(M_SOF0); ++ getword(); ++ getbyte(); ++ if (height != getword() || width != getword()) ++ return 0; ++ return 1; ++} ++ ++int jpeg_decode(buf, pic, width, height, depth, decdata) ++unsigned char *buf, *pic; ++int width, height, depth; ++struct jpeg_decdata *decdata; ++{ ++ int i, j, m, tac, tdc; ++ int mcusx, mcusy, mx, my; ++ int max[6]; ++ ++ if (!decdata || !buf || !pic) ++ return -1; ++ datap = buf; ++ if (getbyte() != 0xff) ++ return ERR_NO_SOI; ++ if (getbyte() != M_SOI) ++ return ERR_NO_SOI; ++ if (readtables(M_SOF0)) ++ return ERR_BAD_TABLES; ++ getword(); ++ i = getbyte(); ++ if (i != 8) ++ return ERR_NOT_8BIT; ++ if (((getword() + 15) & ~15) != height) ++ return ERR_HEIGHT_MISMATCH; ++ if (((getword() + 15) & ~15) != width) ++ return ERR_WIDTH_MISMATCH; ++ if ((height & 15) || (width & 15)) ++ return ERR_BAD_WIDTH_OR_HEIGHT; ++ info.nc = getbyte(); ++ if (info.nc > MAXCOMP) ++ return ERR_TOO_MANY_COMPPS; ++ for (i = 0; i < info.nc; i++) { ++ int h, v; ++ comps[i].cid = getbyte(); ++ comps[i].hv = getbyte(); ++ v = comps[i].hv & 15; ++ h = comps[i].hv >> 4; ++ comps[i].tq = getbyte(); ++ if (h > 3 || v > 3) ++ return ERR_ILLEGAL_HV; ++ if (comps[i].tq > 3) ++ return ERR_QUANT_TABLE_SELECTOR; ++ } ++ if (readtables(M_SOS)) ++ return ERR_BAD_TABLES; ++ getword(); ++ info.ns = getbyte(); ++ if (info.ns != 3) ++ return ERR_NOT_YCBCR_221111; ++ for (i = 0; i < 3; i++) { ++ dscans[i].cid = getbyte(); ++ tdc = getbyte(); ++ tac = tdc & 15; ++ tdc >>= 4; ++ if (tdc > 1 || tac > 1) ++ return ERR_QUANT_TABLE_SELECTOR; ++ for (j = 0; j < info.nc; j++) ++ if (comps[j].cid == dscans[i].cid) ++ break; ++ if (j == info.nc) ++ return ERR_UNKNOWN_CID_IN_SCAN; ++ dscans[i].hv = comps[j].hv; ++ dscans[i].tq = comps[j].tq; ++ dscans[i].hudc.dhuff = dec_huffdc + tdc; ++ dscans[i].huac.dhuff = dec_huffac + tac; ++ } ++ ++ i = getbyte(); ++ j = getbyte(); ++ m = getbyte(); ++ ++ if (i != 0 || j != 63 || m != 0) ++ return ERR_NOT_SEQUENTIAL_DCT; ++ ++ if (dscans[0].cid != 1 || dscans[1].cid != 2 || dscans[2].cid != 3) ++ return ERR_NOT_YCBCR_221111; ++ ++ if (dscans[0].hv != 0x22 || dscans[1].hv != 0x11 || dscans[2].hv != 0x11) ++ return ERR_NOT_YCBCR_221111; ++ ++ mcusx = width >> 4; ++ mcusy = height >> 4; ++ ++ ++ idctqtab(quant[dscans[0].tq], decdata->dquant[0]); ++ idctqtab(quant[dscans[1].tq], decdata->dquant[1]); ++ idctqtab(quant[dscans[2].tq], decdata->dquant[2]); ++ initcol(decdata->dquant); ++ setinput(&in, datap); ++ ++#if 0 ++ /* landing zone */ ++ img[len] = 0; ++ img[len + 1] = 0xff; ++ img[len + 2] = M_EOF; ++#endif ++ ++ dec_initscans(); ++ ++ dscans[0].next = 6 - 4; ++ dscans[1].next = 6 - 4 - 1; ++ dscans[2].next = 6 - 4 - 1 - 1; /* 411 encoding */ ++ for (my = 0; my < mcusy; my++) { ++ for (mx = 0; mx < mcusx; mx++) { ++ if (info.dri && !--info.nm) ++ if (dec_checkmarker()) ++ return ERR_WRONG_MARKER; ++ ++ decode_mcus(&in, decdata->dcts, 6, dscans, max); ++ idct(decdata->dcts, decdata->out, decdata->dquant[0], IFIX(128.5), max[0]); ++ idct(decdata->dcts + 64, decdata->out + 64, decdata->dquant[0], IFIX(128.5), max[1]); ++ idct(decdata->dcts + 128, decdata->out + 128, decdata->dquant[0], IFIX(128.5), max[2]); ++ idct(decdata->dcts + 192, decdata->out + 192, decdata->dquant[0], IFIX(128.5), max[3]); ++ idct(decdata->dcts + 256, decdata->out + 256, decdata->dquant[1], IFIX(0.5), max[4]); ++ idct(decdata->dcts + 320, decdata->out + 320, decdata->dquant[2], IFIX(0.5), max[5]); ++ ++ switch (depth) { ++ case 24: ++ col221111(decdata->out, pic + (my * 16 * mcusx + mx) * 16 * 3, mcusx * 16 * 3); ++ break; ++ case 16: ++ col221111_16(decdata->out, pic + (my * 16 * mcusx + mx) * (16 * 2), mcusx * (16 * 2)); ++ break; ++ default: ++ return ERR_DEPTH_MISMATCH; ++ break; ++ } ++ } ++ } ++ ++ m = dec_readmarker(&in); ++ if (m != M_EOI) ++ return ERR_NO_EOI; ++ ++ return 0; ++} ++ ++/****************************************************************/ ++/************** huffman decoder ***************/ ++/****************************************************************/ ++ ++static int fillbits __P((struct in *, int, unsigned int)); ++static int dec_rec2 ++__P((struct in *, struct dec_hufftbl *, int *, int, int)); ++ ++static void setinput(in, p) ++struct in *in; ++unsigned char *p; ++{ ++ in->p = p; ++ in->left = 0; ++ in->bits = 0; ++ in->marker = 0; ++} ++ ++static int fillbits(in, le, bi) ++struct in *in; ++int le; ++unsigned int bi; ++{ ++ int b, m; ++ ++ if (in->marker) { ++ if (le <= 16) ++ in->bits = bi << 16, le += 16; ++ return le; ++ } ++ while (le <= 24) { ++ b = *in->p++; ++ if (b == 0xff && (m = *in->p++) != 0) { ++ if (m == M_EOF) { ++ if (in->func && (m = in->func(in->data)) == 0) ++ continue; ++ } ++ in->marker = m; ++ if (le <= 16) ++ bi = bi << 16, le += 16; ++ break; ++ } ++ bi = bi << 8 | b; ++ le += 8; ++ } ++ in->bits = bi; /* tmp... 2 return values needed */ ++ return le; ++} ++ ++static int dec_readmarker(in) ++struct in *in; ++{ ++ int m; ++ ++ in->left = fillbits(in, in->left, in->bits); ++ if ((m = in->marker) == 0) ++ return 0; ++ in->left = 0; ++ in->marker = 0; ++ return m; ++} ++ ++#define LEBI_DCL int le, bi ++#define LEBI_GET(in) (le = in->left, bi = in->bits) ++#define LEBI_PUT(in) (in->left = le, in->bits = bi) ++ ++#define GETBITS(in, n) ( \ ++ (le < (n) ? le = fillbits(in, le, bi), bi = in->bits : 0), \ ++ (le -= (n)), \ ++ bi >> le & ((1 << (n)) - 1) \ ++) ++ ++#define UNGETBITS(in, n) ( \ ++ le += (n) \ ++) ++ ++ ++static int dec_rec2(in, hu, runp, c, i) ++struct in *in; ++struct dec_hufftbl *hu; ++int *runp; ++int c, i; ++{ ++ LEBI_DCL; ++ ++ LEBI_GET(in); ++ if (i) { ++ UNGETBITS(in, i & 127); ++ *runp = i >> 8 & 15; ++ i >>= 16; ++ } else { ++ for (i = DECBITS; (c = ((c << 1) | GETBITS(in, 1))) >= (hu->maxcode[i]); i++); ++ if (i >= 16) { ++ in->marker = M_BADHUFF; ++ return 0; ++ } ++ i = hu->vals[hu->valptr[i] + c - hu->maxcode[i - 1] * 2]; ++ *runp = i >> 4; ++ i &= 15; ++ } ++ if (i == 0) { /* sigh, 0xf0 is 11 bit */ ++ LEBI_PUT(in); ++ return 0; ++ } ++ /* receive part */ ++ c = GETBITS(in, i); ++ if (c < (1 << (i - 1))) ++ c += (-1 << i) + 1; ++ LEBI_PUT(in); ++ return c; ++} ++ ++#define DEC_REC(in, hu, r, i) ( \ ++ r = GETBITS(in, DECBITS), \ ++ i = hu->llvals[r], \ ++ i & 128 ? \ ++ ( \ ++ UNGETBITS(in, i & 127), \ ++ r = i >> 8 & 15, \ ++ i >> 16 \ ++ ) \ ++ : \ ++ ( \ ++ LEBI_PUT(in), \ ++ i = dec_rec2(in, hu, &r, r, i), \ ++ LEBI_GET(in), \ ++ i \ ++ ) \ ++) ++ ++static void decode_mcus(in, dct, n, sc, maxp) ++struct in *in; ++int *dct; ++int n; ++struct scan *sc; ++int *maxp; ++{ ++ struct dec_hufftbl *hu; ++ int i, r, t; ++ LEBI_DCL; ++ ++ memset(dct, 0, n * 64 * sizeof(*dct)); ++ LEBI_GET(in); ++ while (n-- > 0) { ++ hu = sc->hudc.dhuff; ++ *dct++ = (sc->dc += DEC_REC(in, hu, r, t)); ++ ++ hu = sc->huac.dhuff; ++ i = 63; ++ while (i > 0) { ++ t = DEC_REC(in, hu, r, t); ++ if (t == 0 && r == 0) { ++ dct += i; ++ break; ++ } ++ dct += r; ++ *dct++ = t; ++ i -= r + 1; ++ } ++ *maxp++ = 64 - i; ++ if (n == sc->next) ++ sc++; ++ } ++ LEBI_PUT(in); ++} ++ ++static void dec_makehuff(hu, hufflen, huffvals) ++struct dec_hufftbl *hu; ++int *hufflen; ++unsigned char *huffvals; ++{ ++ int code, k, i, j, d, x, c, v; ++ for (i = 0; i < (1 << DECBITS); i++) ++ hu->llvals[i] = 0; ++ ++/* ++ * llvals layout: ++ * ++ * value v already known, run r, backup u bits: ++ * vvvvvvvvvvvvvvvv 0000 rrrr 1 uuuuuuu ++ * value unknown, size b bits, run r, backup u bits: ++ * 000000000000bbbb 0000 rrrr 0 uuuuuuu ++ * value and size unknown: ++ * 0000000000000000 0000 0000 0 0000000 ++ */ ++ code = 0; ++ k = 0; ++ for (i = 0; i < 16; i++, code <<= 1) { /* sizes */ ++ hu->valptr[i] = k; ++ for (j = 0; j < hufflen[i]; j++) { ++ hu->vals[k] = *huffvals++; ++ if (i < DECBITS) { ++ c = code << (DECBITS - 1 - i); ++ v = hu->vals[k] & 0x0f; /* size */ ++ for (d = 1 << (DECBITS - 1 - i); --d >= 0;) { ++ if (v + i < DECBITS) { /* both fit in table */ ++ x = d >> (DECBITS - 1 - v - ++ i); ++ if (v && x < (1 << (v - 1))) ++ x += (-1 << v) + 1; ++ x = x << 16 | (hu-> vals[k] & 0xf0) << 4 | ++ (DECBITS - (i + 1 + v)) | 128; ++ } else ++ x = v << 16 | (hu-> vals[k] & 0xf0) << 4 | ++ (DECBITS - (i + 1)); ++ hu->llvals[c | d] = x; ++ } ++ } ++ code++; ++ k++; ++ } ++ hu->maxcode[i] = code; ++ } ++ hu->maxcode[16] = 0x20000; /* always terminate decode */ ++} ++ ++/****************************************************************/ ++/************** idct ***************/ ++/****************************************************************/ ++ ++#define ONE ((PREC)IFIX(1.)) ++#define S2 ((PREC)IFIX(0.382683432)) ++#define C2 ((PREC)IFIX(0.923879532)) ++#define C4 ((PREC)IFIX(0.707106781)) ++ ++#define S22 ((PREC)IFIX(2 * 0.382683432)) ++#define C22 ((PREC)IFIX(2 * 0.923879532)) ++#define IC4 ((PREC)IFIX(1 / 0.707106781)) ++ ++#define C3IC1 ((PREC)IFIX(0.847759065)) /* c3/c1 */ ++#define C5IC1 ((PREC)IFIX(0.566454497)) /* c5/c1 */ ++#define C7IC1 ((PREC)IFIX(0.198912367)) /* c7/c1 */ ++ ++#define XPP(a,b) (t = a + b, b = a - b, a = t) ++#define XMP(a,b) (t = a - b, b = a + b, a = t) ++#define XPM(a,b) (t = a + b, b = b - a, a = t) ++ ++#define ROT(a,b,s,c) ( t = IMULT(a + b, s), \ ++ a = IMULT(a, c - s) + t, \ ++ b = IMULT(b, c + s) - t) ++ ++#define IDCT \ ++( \ ++ XPP(t0, t1), \ ++ XMP(t2, t3), \ ++ t2 = IMULT(t2, IC4) - t3, \ ++ XPP(t0, t3), \ ++ XPP(t1, t2), \ ++ XMP(t4, t7), \ ++ XPP(t5, t6), \ ++ XMP(t5, t7), \ ++ t5 = IMULT(t5, IC4), \ ++ ROT(t4, t6, S22, C22),\ ++ t6 -= t7, \ ++ t5 -= t6, \ ++ t4 -= t5, \ ++ XPP(t0, t7), \ ++ XPP(t1, t6), \ ++ XPP(t2, t5), \ ++ XPP(t3, t4) \ ++) ++ ++static unsigned char zig2[64] = { ++ 0, 2, 3, 9, 10, 20, 21, 35, ++ 14, 16, 25, 31, 39, 46, 50, 57, ++ 5, 7, 12, 18, 23, 33, 37, 48, ++ 27, 29, 41, 44, 52, 55, 59, 62, ++ 15, 26, 30, 40, 45, 51, 56, 58, ++ 1, 4, 8, 11, 19, 22, 34, 36, ++ 28, 42, 43, 53, 54, 60, 61, 63, ++ 6, 13, 17, 24, 32, 38, 47, 49 ++}; ++ ++void idct(in, out, quant, off, max) ++int *in; ++int *out; ++PREC *quant; ++PREC off; ++int max; ++{ ++ PREC t0, t1, t2, t3, t4, t5, t6, t7, t; ++ PREC tmp[64], *tmpp; ++ int i, j; ++ unsigned char *zig2p; ++ ++ t0 = off; ++ if (max == 1) { ++ t0 += in[0] * quant[0]; ++ for (i = 0; i < 64; i++) ++ out[i] = ITOINT(t0); ++ return; ++ } ++ zig2p = zig2; ++ tmpp = tmp; ++ for (i = 0; i < 8; i++) { ++ j = *zig2p++; ++ t0 += in[j] * quant[j]; ++ j = *zig2p++; ++ t5 = in[j] * quant[j]; ++ j = *zig2p++; ++ t2 = in[j] * quant[j]; ++ j = *zig2p++; ++ t7 = in[j] * quant[j]; ++ j = *zig2p++; ++ t1 = in[j] * quant[j]; ++ j = *zig2p++; ++ t4 = in[j] * quant[j]; ++ j = *zig2p++; ++ t3 = in[j] * quant[j]; ++ j = *zig2p++; ++ t6 = in[j] * quant[j]; ++ IDCT; ++ tmpp[0 * 8] = t0; ++ tmpp[1 * 8] = t1; ++ tmpp[2 * 8] = t2; ++ tmpp[3 * 8] = t3; ++ tmpp[4 * 8] = t4; ++ tmpp[5 * 8] = t5; ++ tmpp[6 * 8] = t6; ++ tmpp[7 * 8] = t7; ++ tmpp++; ++ t0 = 0; ++ } ++ for (i = 0; i < 8; i++) { ++ t0 = tmp[8 * i + 0]; ++ t1 = tmp[8 * i + 1]; ++ t2 = tmp[8 * i + 2]; ++ t3 = tmp[8 * i + 3]; ++ t4 = tmp[8 * i + 4]; ++ t5 = tmp[8 * i + 5]; ++ t6 = tmp[8 * i + 6]; ++ t7 = tmp[8 * i + 7]; ++ IDCT; ++ out[8 * i + 0] = ITOINT(t0); ++ out[8 * i + 1] = ITOINT(t1); ++ out[8 * i + 2] = ITOINT(t2); ++ out[8 * i + 3] = ITOINT(t3); ++ out[8 * i + 4] = ITOINT(t4); ++ out[8 * i + 5] = ITOINT(t5); ++ out[8 * i + 6] = ITOINT(t6); ++ out[8 * i + 7] = ITOINT(t7); ++ } ++} ++ ++static unsigned char zig[64] = { ++ 0, 1, 5, 6, 14, 15, 27, 28, ++ 2, 4, 7, 13, 16, 26, 29, 42, ++ 3, 8, 12, 17, 25, 30, 41, 43, ++ 9, 11, 18, 24, 31, 40, 44, 53, ++ 10, 19, 23, 32, 39, 45, 52, 54, ++ 20, 22, 33, 38, 46, 51, 55, 60, ++ 21, 34, 37, 47, 50, 56, 59, 61, ++ 35, 36, 48, 49, 57, 58, 62, 63 ++}; ++ ++static PREC aaidct[8] = { ++ IFIX(0.3535533906), IFIX(0.4903926402), ++ IFIX(0.4619397663), IFIX(0.4157348062), ++ IFIX(0.3535533906), IFIX(0.2777851165), ++ IFIX(0.1913417162), IFIX(0.0975451610) ++}; ++ ++ ++static void idctqtab(qin, qout) ++unsigned char *qin; ++PREC *qout; ++{ ++ int i, j; ++ ++ for (i = 0; i < 8; i++) ++ for (j = 0; j < 8; j++) ++ qout[zig[i * 8 + j]] = qin[zig[i * 8 + j]] * ++ IMULT(aaidct[i], aaidct[j]); ++} ++ ++static void scaleidctqtab(q, sc) ++PREC *q; ++PREC sc; ++{ ++ int i; ++ ++ for (i = 0; i < 64; i++) ++ q[i] = IMULT(q[i], sc); ++} ++ ++/****************************************************************/ ++/************** color decoder ***************/ ++/****************************************************************/ ++ ++#define ROUND ++ ++/* ++ * YCbCr Color transformation: ++ * ++ * y:0..255 Cb:-128..127 Cr:-128..127 ++ * ++ * R = Y + 1.40200 * Cr ++ * G = Y - 0.34414 * Cb - 0.71414 * Cr ++ * B = Y + 1.77200 * Cb ++ * ++ * => ++ * Cr *= 1.40200; ++ * Cb *= 1.77200; ++ * Cg = 0.19421 * Cb + .50937 * Cr; ++ * R = Y + Cr; ++ * G = Y - Cg; ++ * B = Y + Cb; ++ * ++ * => ++ * Cg = (50 * Cb + 130 * Cr + 128) >> 8; ++ */ ++ ++static void initcol(q) ++PREC q[][64]; ++{ ++ scaleidctqtab(q[1], IFIX(1.77200)); ++ scaleidctqtab(q[2], IFIX(1.40200)); ++} ++ ++/* This is optimized for the stupid sun SUNWspro compiler. */ ++#define STORECLAMP(a,x) \ ++( \ ++ (a) = (x), \ ++ (unsigned int)(x) >= 256 ? \ ++ ((a) = (x) < 0 ? 0 : 255) \ ++ : \ ++ 0 \ ++) ++ ++#define CLAMP(x) ((unsigned int)(x) >= 256 ? ((x) < 0 ? 0 : 255) : (x)) ++ ++#ifdef ROUND ++ ++#define CBCRCG(yin, xin) \ ++( \ ++ cb = outc[0 +yin*8+xin], \ ++ cr = outc[64+yin*8+xin], \ ++ cg = (50 * cb + 130 * cr + 128) >> 8 \ ++) ++ ++#else ++ ++#define CBCRCG(yin, xin) \ ++( \ ++ cb = outc[0 +yin*8+xin], \ ++ cr = outc[64+yin*8+xin], \ ++ cg = (3 * cb + 8 * cr) >> 4 \ ++) ++ ++#endif ++ ++#define PIC(yin, xin, p, xout) \ ++( \ ++ y = outy[(yin) * 8 + xin], \ ++ STORECLAMP(p[(xout) * 3 + 0], y + cr), \ ++ STORECLAMP(p[(xout) * 3 + 1], y - cg), \ ++ STORECLAMP(p[(xout) * 3 + 2], y + cb) \ ++) ++ ++#ifdef __LITTLE_ENDIAN ++#define PIC_16(yin, xin, p, xout, add) \ ++( \ ++ y = outy[(yin) * 8 + xin], \ ++ y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 8) | \ ++ ((CLAMP(y - cg + add ) & 0xfc) << 3) | \ ++ ((CLAMP(y + cb + add*2+1) ) >> 3), \ ++ p[(xout) * 2 + 0] = y & 0xff, \ ++ p[(xout) * 2 + 1] = y >> 8 \ ++) ++#else ++#ifdef CONFIG_PPC ++#define PIC_16(yin, xin, p, xout, add) \ ++( \ ++ y = outy[(yin) * 8 + xin], \ ++ y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 7) | \ ++ ((CLAMP(y - cg + add*2+1) & 0xf8) << 2) | \ ++ ((CLAMP(y + cb + add*2+1) ) >> 3), \ ++ p[(xout) * 2 + 0] = y >> 8, \ ++ p[(xout) * 2 + 1] = y & 0xff \ ++) ++#else ++#define PIC_16(yin, xin, p, xout, add) \ ++( \ ++ y = outy[(yin) * 8 + xin], \ ++ y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 8) | \ ++ ((CLAMP(y - cg + add ) & 0xfc) << 3) | \ ++ ((CLAMP(y + cb + add*2+1) ) >> 3), \ ++ p[(xout) * 2 + 0] = y >> 8, \ ++ p[(xout) * 2 + 1] = y & 0xff \ ++) ++#endif ++#endif ++ ++#define PIC221111(xin) \ ++( \ ++ CBCRCG(0, xin), \ ++ PIC(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0), \ ++ PIC(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1), \ ++ PIC(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0), \ ++ PIC(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1) \ ++) ++ ++#define PIC221111_16(xin) \ ++( \ ++ CBCRCG(0, xin), \ ++ PIC_16(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0, 3), \ ++ PIC_16(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1, 0), \ ++ PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0, 1), \ ++ PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1, 2) \ ++) ++ ++static void col221111(out, pic, width) ++int *out; ++unsigned char *pic; ++int width; ++{ ++ int i, j, k; ++ unsigned char *pic0, *pic1; ++ int *outy, *outc; ++ int cr, cg, cb, y; ++ ++ pic0 = pic; ++ pic1 = pic + width; ++ outy = out; ++ outc = out + 64 * 4; ++ for (i = 2; i > 0; i--) { ++ for (j = 4; j > 0; j--) { ++ for (k = 0; k < 8; k++) { ++ PIC221111(k); ++ } ++ outc += 8; ++ outy += 16; ++ pic0 += 2 * width; ++ pic1 += 2 * width; ++ } ++ outy += 64 * 2 - 16 * 4; ++ } ++} ++ ++static void col221111_16(out, pic, width) ++int *out; ++unsigned char *pic; ++int width; ++{ ++ int i, j, k; ++ unsigned char *pic0, *pic1; ++ int *outy, *outc; ++ int cr, cg, cb, y; ++ ++ pic0 = pic; ++ pic1 = pic + width; ++ outy = out; ++ outc = out + 64 * 4; ++ for (i = 2; i > 0; i--) { ++ for (j = 4; j > 0; j--) { ++ for (k = 0; k < 8; k++) { ++ PIC221111_16(k); ++ } ++ outc += 8; ++ outy += 16; ++ pic0 += 2 * width; ++ pic1 += 2 * width; ++ } ++ outy += 64 * 2 - 16 * 4; ++ } ++} +--- /dev/null ++++ b/drivers/video/bootsplash/decode-jpg.h +@@ -0,0 +1,35 @@ ++/* ++ * linux/drivers/video/bootsplash/decode-jpg.h - a tiny jpeg decoder. ++ * ++ * (w) August 2001 by Michael Schroeder, ++ */ ++ ++#ifndef __DECODE_JPG_H ++#define __DECODE_JPG_H ++ ++#define ERR_NO_SOI 1 ++#define ERR_NOT_8BIT 2 ++#define ERR_HEIGHT_MISMATCH 3 ++#define ERR_WIDTH_MISMATCH 4 ++#define ERR_BAD_WIDTH_OR_HEIGHT 5 ++#define ERR_TOO_MANY_COMPPS 6 ++#define ERR_ILLEGAL_HV 7 ++#define ERR_QUANT_TABLE_SELECTOR 8 ++#define ERR_NOT_YCBCR_221111 9 ++#define ERR_UNKNOWN_CID_IN_SCAN 10 ++#define ERR_NOT_SEQUENTIAL_DCT 11 ++#define ERR_WRONG_MARKER 12 ++#define ERR_NO_EOI 13 ++#define ERR_BAD_TABLES 14 ++#define ERR_DEPTH_MISMATCH 15 ++ ++struct jpeg_decdata { ++ int dcts[6 * 64 + 16]; ++ int out[64 * 6]; ++ int dquant[3][64]; ++}; ++ ++extern int jpeg_decode(unsigned char *, unsigned char *, int, int, int, struct jpeg_decdata *); ++extern int jpeg_check_size(unsigned char *, int, int); ++ ++#endif +--- /dev/null ++++ b/drivers/video/bootsplash/render.c +@@ -0,0 +1,328 @@ ++/* ++ * linux/drivers/video/bootsplash/render.c - splash screen render functions. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../console/fbcon.h" ++#include "bootsplash.h" ++ ++void splash_putcs(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++ const unsigned short *s, int count, int ypos, int xpos) ++{ ++ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; ++ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; ++ u8 *src; ++ u8 *dst, *splashsrc; ++ unsigned int d, x, y; ++ u32 dd, fgx, bgx; ++ u16 c = scr_readw(s); ++ ++ int fg_color, bg_color, transparent; ++ if (console_blanked) ++ return; ++ fg_color = attr_fgcol(fgshift, c); ++ bg_color = attr_bgcol(bgshift, c); ++ transparent = sd->splash_color == bg_color; ++ xpos = xpos * vc->vc_font.width + sd->splash_text_xo; ++ ypos = ypos * vc->vc_font.height + sd->splash_text_yo; ++ splashsrc = (u8 *)(info->splash_pic + ypos * info->splash_bytes + xpos * 2); ++ dst = (u8 *)(info->screen_base + ypos * info->fix.line_length + xpos * 2); ++ ++ fgx = ((u32 *)info->pseudo_palette)[fg_color]; ++ if (transparent && sd->splash_color == 15) { ++ if (fgx == 0xffea) ++ fgx = 0xfe4a; ++ else if (fgx == 0x57ea) ++ fgx = 0x0540; ++ else if (fgx == 0xffff) ++ fgx = 0x52aa; ++ } ++ bgx = ((u32 *)info->pseudo_palette)[bg_color]; ++ d = 0; ++ ++ while (count--) { ++ c = scr_readw(s++); ++ src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * ((vc->vc_font.width + 7) >> 3); ++ ++ for (y = 0; y < vc->vc_font.height; y++) { ++ for (x = 0; x < vc->vc_font.width; x += 2) { ++ if ((x & 7) == 0) ++ d = *src++; ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? *(u16 *)splashsrc : bgx; ++ splashsrc += 2; ++ if (d & 0x40) ++ dd |= fgx << 16; ++ else ++ dd |= (transparent ? *(u16 *)splashsrc : bgx) << 16; ++ splashsrc += 2; ++ d <<= 2; ++ fb_writel(dd, dst); ++ dst += 4; ++ } ++ dst += info->fix.line_length - vc->vc_font.width * 2; ++ splashsrc += info->splash_bytes - vc->vc_font.width * 2; ++ } ++ dst -= info->fix.line_length * vc->vc_font.height - vc->vc_font.width * 2; ++ splashsrc -= info->splash_bytes * vc->vc_font.height - vc->vc_font.width * 2; ++ } ++} ++ ++static void splash_renderc(struct splash_data *sd, struct fb_info *info, int fg_color, int bg_color, u8 *src, int ypos, int xpos, int height, int width) ++{ ++ int transparent = sd->splash_color == bg_color; ++ u32 dd, fgx, bgx; ++ u8 *dst, *splashsrc; ++ unsigned int d, x, y; ++ ++ if (console_blanked) ++ return; ++ splashsrc = (u8 *)(info->splash_pic + ypos * info->splash_bytes + xpos * 2); ++ dst = (u8 *)(info->screen_base + ypos * info->fix.line_length + xpos * 2); ++ fgx = ((u32 *)info->pseudo_palette)[fg_color]; ++ if (transparent && sd->splash_color == 15) { ++ if (fgx == 0xffea) ++ fgx = 0xfe4a; ++ else if (fgx == 0x57ea) ++ fgx = 0x0540; ++ else if (fgx == 0xffff) ++ fgx = 0x52aa; ++ } ++ bgx = ((u32 *)info->pseudo_palette)[bg_color]; ++ d = 0; ++ for (y = 0; y < height; y++) { ++ for (x = 0; x < width; x += 2) { ++ if ((x & 7) == 0) ++ d = *src++; ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? *(u16 *)splashsrc : bgx; ++ splashsrc += 2; ++ if (d & 0x40) ++ dd |= fgx << 16; ++ else ++ dd |= (transparent ? *(u16 *)splashsrc : bgx) << 16; ++ splashsrc += 2; ++ d <<= 2; ++ fb_writel(dd, dst); ++ dst += 4; ++ } ++ dst += info->fix.line_length - width * 2; ++ splashsrc += info->splash_bytes - width * 2; ++ } ++} ++ ++void splash_putc(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++ int c, int ypos, int xpos) ++{ ++ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; ++ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; ++ u8 *src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * ((vc->vc_font.width + 7) >> 3); ++ xpos = xpos * vc->vc_font.width + sd->splash_text_xo; ++ ypos = ypos * vc->vc_font.height + sd->splash_text_yo; ++ splash_renderc(sd, info, attr_fgcol(fgshift, c), attr_bgcol(bgshift, c), src, ypos, xpos, vc->vc_font.height, vc->vc_font.width); ++} ++ ++void splashcopy(u8 *dst, u8 *src, int height, int width, int dstbytes, int srcbytes) ++{ ++ int i; ++ ++ while (height-- > 0) { ++ u32 *p = (u32 *)dst; ++ u32 *q = (u32 *)src; ++ for (i=0; i < width/4; i++) { ++ fb_writel(*q++,p++); ++ fb_writel(*q++,p++); ++ } ++ if (width & 2) ++ fb_writel(*q++,p++); ++ if (width & 1) ++ fb_writew(*(u16*)q,(u16*)p); ++ dst += dstbytes; ++ src += srcbytes; ++ } ++} ++ ++static void splashset(u8 *dst, int height, int width, int dstbytes, u32 bgx) { ++ int i; ++ ++ bgx |= bgx << 16; ++ while (height-- > 0) { ++ u32 *p = (u32 *)dst; ++ for (i=0; i < width/4; i++) { ++ fb_writel(bgx,p++); ++ fb_writel(bgx,p++); ++ } ++ if (width & 2) ++ fb_writel(bgx,p++); ++ if (width & 1) ++ fb_writew(bgx,(u16*)p); ++ dst += dstbytes; ++ } ++} ++ ++static void splashfill(struct fb_info *info, int sy, int sx, int height, int width) { ++ splashcopy((u8 *)(info->screen_base + sy * info->fix.line_length + sx * 2), (u8 *)(info->splash_pic + sy * info->splash_bytes + sx * 2), height, width, info->fix.line_length, info->splash_bytes); ++} ++ ++void splash_clear(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int height, int width) ++{ ++ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ int bg_color = attr_bgcol_ec(bgshift, vc, info); ++ int transparent = sd->splash_color == bg_color; ++ u32 bgx; ++ u8 *dst; ++ ++ if (console_blanked) ++ return; ++ sy = sy * vc->vc_font.height + sd->splash_text_yo; ++ sx = sx * vc->vc_font.width + sd->splash_text_xo; ++ height *= vc->vc_font.height; ++ width *= vc->vc_font.width; ++ if (transparent) { ++ splashfill(info, sy, sx, height, width); ++ return; ++ } ++ dst = (u8 *)(info->screen_base + sy * info->fix.line_length + sx * 2); ++ bgx = ((u32 *)info->pseudo_palette)[bg_color]; ++ splashset(dst, height, width, info->fix.line_length, bgx); ++} ++ ++void splash_bmove(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width) ++{ ++ struct fb_copyarea area; ++ ++ if (console_blanked) ++ return; ++ area.sx = sx * vc->vc_font.width; ++ area.sy = sy * vc->vc_font.height; ++ area.dx = dx * vc->vc_font.width; ++ area.dy = dy * vc->vc_font.height; ++ area.sx += sd->splash_text_xo; ++ area.sy += sd->splash_text_yo; ++ area.dx += sd->splash_text_xo; ++ area.dy += sd->splash_text_yo; ++ area.height = height * vc->vc_font.height; ++ area.width = width * vc->vc_font.width; ++ ++ info->fbops->fb_copyarea(info, &area); ++} ++ ++void splash_clear_margins(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, ++ int bottom_only) ++{ ++ unsigned int tw = vc->vc_cols*vc->vc_font.width; ++ unsigned int th = vc->vc_rows*vc->vc_font.height; ++ ++ if (console_blanked) ++ return; ++ if (!bottom_only) { ++ /* top margin */ ++ splashfill(info, 0, 0, sd->splash_text_yo, info->var.xres); ++ /* left margin */ ++ splashfill(info, sd->splash_text_yo, 0, th, sd->splash_text_xo); ++ /* right margin */ ++ splashfill(info, sd->splash_text_yo, sd->splash_text_xo + tw, th, info->var.xres - sd->splash_text_xo - tw); ++ ++ } ++ splashfill(info, sd->splash_text_yo + th, 0, info->var.yres - sd->splash_text_yo - th, info->var.xres); ++} ++ ++int splash_cursor(struct splash_data *sd, struct fb_info *info, struct fb_cursor *cursor) ++{ ++ int i; ++ unsigned int dsize, s_pitch; ++ ++ if (info->state != FBINFO_STATE_RUNNING) ++ return 0; ++ ++ s_pitch = (cursor->image.width + 7) >> 3; ++ dsize = s_pitch * cursor->image.height; ++ if (cursor->enable) { ++ switch (cursor->rop) { ++ case ROP_XOR: ++ for (i = 0; i < dsize; i++) ++ info->fb_cursordata[i] = cursor->image.data[i] ^ cursor->mask[i]; ++ break; ++ case ROP_COPY: ++ default: ++ for (i = 0; i < dsize; i++) ++ info->fb_cursordata[i] = cursor->image.data[i] & cursor->mask[i]; ++ break; ++ } ++ } else if (info->fb_cursordata != cursor->image.data) ++ memcpy(info->fb_cursordata, cursor->image.data, dsize); ++ cursor->image.data = info->fb_cursordata; ++ splash_renderc(sd, info, cursor->image.fg_color, cursor->image.bg_color, (u8 *)info->fb_cursordata, cursor->image.dy + sd->splash_text_yo, cursor->image.dx + sd->splash_text_xo, cursor->image.height, cursor->image.width); ++ return 0; ++} ++ ++void splash_bmove_redraw(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) ++{ ++ unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * y + dx * 2); ++ unsigned short *s = d + (dx - sx); ++ unsigned short *start = d; ++ unsigned short *ls = d; ++ unsigned short *le = d + width; ++ unsigned short c; ++ int x = dx; ++ unsigned short attr = 1; ++ ++ if (console_blanked) ++ return; ++ do { ++ c = scr_readw(d); ++ if (attr != (c & 0xff00)) { ++ attr = c & 0xff00; ++ if (d > start) { ++ splash_putcs(sd, vc, info, start, d - start, y, x); ++ x += d - start; ++ start = d; ++ } ++ } ++ if (s >= ls && s < le && c == scr_readw(s)) { ++ if (d > start) { ++ splash_putcs(sd, vc, info, start, d - start, y, x); ++ x += d - start + 1; ++ start = d + 1; ++ } else { ++ x++; ++ start++; ++ } ++ } ++ s++; ++ d++; ++ } while (d < le); ++ if (d > start) ++ splash_putcs(sd, vc, info, start, d - start, y, x); ++} ++ ++void splash_blank(struct splash_data *sd, struct vc_data *vc, struct fb_info *info, int blank) ++{ ++ if (blank) { ++ if (info->silent_screen_base) ++ splashset((u8 *)info->silent_screen_base, info->var.yres, info->var.xres, info->fix.line_length, 0); ++ splashset((u8 *)info->screen_base, info->var.yres, info->var.xres, info->fix.line_length, 0); ++ } else { ++ if (info->silent_screen_base) ++ splash_prepare(vc, info); ++ splash_clear_margins(vc->vc_splash_data, vc, info, 0); ++ /* no longer needed, done in fbcon_blank */ ++ /* update_screen(vc->vc_num); */ ++ } ++} ++ +--- a/drivers/video/console/bitblit.c ++++ b/drivers/video/console/bitblit.c +@@ -17,6 +17,9 @@ + #include + #include + #include "fbcon.h" ++#ifdef CONFIG_BOOTSPLASH ++#include "../bootsplash/bootsplash.h" ++#endif + + /* + * Accelerated handlers. +@@ -47,6 +50,13 @@ static void bit_bmove(struct vc_data *vc + { + struct fb_copyarea area; + ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) { ++ splash_bmove(info->splash_data, vc, info, ++ sy, sx, dy, dx, height, width); ++ return; ++ } ++#endif + area.sx = sx * vc->vc_font.width; + area.sy = sy * vc->vc_font.height; + area.dx = dx * vc->vc_font.width; +@@ -63,6 +73,13 @@ static void bit_clear(struct vc_data *vc + int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; + struct fb_fillrect region; + ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) { ++ splash_clear(info->splash_data, vc, info, ++ sy, sx, height, width); ++ return; ++ } ++#endif + region.color = attr_bgcol_ec(bgshift, vc, info); + region.dx = sx * vc->vc_font.width; + region.dy = sy * vc->vc_font.height; +@@ -160,6 +177,13 @@ static void bit_putcs(struct vc_data *vc + image.height = vc->vc_font.height; + image.depth = 1; + ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) { ++ splash_putcs(info->splash_data, vc, info, s, count, yy, xx); ++ return; ++ } ++#endif ++ + if (attribute) { + buf = kmalloc(cellsize, GFP_KERNEL); + if (!buf) +@@ -213,6 +237,13 @@ static void bit_clear_margins(struct vc_ + unsigned int bs = info->var.yres - bh; + struct fb_fillrect region; + ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) { ++ splash_clear_margins(info->splash_data, vc, info, bottom_only); ++ return; ++ } ++#endif ++ + region.color = attr_bgcol_ec(bgshift, vc, info); + region.rop = ROP_COPY; + +@@ -379,6 +410,14 @@ static void bit_cursor(struct vc_data *v + cursor.image.depth = 1; + cursor.rop = ROP_XOR; + ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) { ++ splash_cursor(info->splash_data, info, &cursor); ++ ops->cursor_reset = 0; ++ return; ++ } ++#endif ++ + if (info->fbops->fb_cursor) + err = info->fbops->fb_cursor(info, &cursor); + +--- a/drivers/video/console/fbcon.c ++++ b/drivers/video/console/fbcon.c +@@ -80,6 +80,9 @@ + #include + + #include "fbcon.h" ++#ifdef CONFIG_BOOTSPLASH ++#include "../bootsplash/bootsplash.h" ++#endif + + #ifdef FBCONDEBUG + # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) +@@ -95,7 +98,11 @@ enum { + + static struct display fb_display[MAX_NR_CONSOLES]; + ++#ifdef CONFIG_BOOTSPLASH ++signed char con2fb_map[MAX_NR_CONSOLES]; ++#else + static signed char con2fb_map[MAX_NR_CONSOLES]; ++#endif + static signed char con2fb_map_boot[MAX_NR_CONSOLES]; + + static int logo_lines; +@@ -537,6 +544,10 @@ static int fbcon_takeover(int show_logo) + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = info_idx; + ++#ifdef CONFIG_BOOTSPLASH ++ splash_init(); ++#endif ++ + err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, + fbcon_is_default); + +@@ -1099,6 +1110,16 @@ static void fbcon_init(struct vc_data *v + new_cols /= vc->vc_font.width; + new_rows /= vc->vc_font.height; + ++#ifdef CONFIG_BOOTSPLASH ++ if (vc->vc_splash_data && vc->vc_splash_data->splash_state) { ++ new_cols = vc->vc_splash_data->splash_text_wi / vc->vc_font.width; ++ new_rows = vc->vc_splash_data->splash_text_he / vc->vc_font.height; ++ logo = 0; ++ con_remap_def_color(vc, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color); ++ } ++#endif ++ ++ + /* + * We must always set the mode. The mode of the previous console + * driver could be in the same resolution but we are using different +@@ -1800,6 +1821,10 @@ static int fbcon_scroll(struct vc_data * + fbcon_softback_note(vc, t, count); + if (logo_shown >= 0) + goto redraw_up; ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) ++ goto redraw_up; ++#endif + switch (p->scrollmode) { + case SCROLL_MOVE: + fbcon_redraw_blit(vc, info, p, t, b - t - count, +@@ -1891,6 +1916,10 @@ static int fbcon_scroll(struct vc_data * + count = vc->vc_rows; + if (logo_shown >= 0) + goto redraw_down; ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) ++ goto redraw_down; ++#endif + switch (p->scrollmode) { + case SCROLL_MOVE: + fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, +@@ -2039,6 +2068,14 @@ static void fbcon_bmove_rec(struct vc_da + } + return; + } ++ ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data && sy == dy && height == 1) { ++ /* must use slower redraw bmove to keep background pic intact */ ++ splash_bmove_redraw(info->splash_data, vc, info, sy, sx, dx, width); ++ return; ++ } ++#endif + ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, + height, width); + } +@@ -2147,6 +2184,10 @@ static int fbcon_switch(struct vc_data * + info = registered_fb[con2fb_map[vc->vc_num]]; + ops = info->fbcon_par; + ++#ifdef CONFIG_BOOTSPLASH ++ splash_prepare(vc, info); ++#endif ++ + if (softback_top) { + if (softback_lines) + fbcon_set_origin(vc); +@@ -2280,6 +2321,12 @@ static void fbcon_generic_blank(struct v + { + struct fb_event event; + ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) { ++ splash_blank(info->splash_data, vc, info, blank); ++ return; ++ } ++#endif + if (blank) { + unsigned short charmask = vc->vc_hi_font_mask ? + 0x1ff : 0xff; +@@ -2481,6 +2528,12 @@ static int fbcon_do_set_font(struct vc_d + + cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); ++#ifdef CONFIG_BOOTSPLASH ++ if (info->splash_data) { ++ cols = info->splash_data->splash_text_wi; ++ rows = info->splash_data->splash_text_he; ++ } ++#endif + cols /= w; + rows /= h; + vc_resize(vc, cols, rows); +--- a/drivers/video/console/fbcon.h ++++ b/drivers/video/console/fbcon.h +@@ -25,6 +25,34 @@ + * low-level frame buffer device + */ + ++#ifdef CONFIG_BOOTSPLASH ++struct splash_data { ++ int splash_state; /* show splash? */ ++ int splash_color; /* transparent color */ ++ int splash_fg_color; /* foreground color */ ++ int splash_width; /* width of image */ ++ int splash_height; /* height of image */ ++ int splash_text_xo; /* text area origin */ ++ int splash_text_yo; ++ int splash_text_wi; /* text area size */ ++ int splash_text_he; ++ int splash_showtext; /* silent/verbose mode */ ++ int splash_boxcount; ++ int splash_percent; ++ int splash_overpaintok; /* is it ok to overpaint boxes */ ++ int splash_palcnt; ++ char *oldscreen_base; /* pointer to top of virtual screen */ ++ unsigned char *splash_boxes; ++ unsigned char *splash_jpeg; /* jpeg */ ++ unsigned char *splash_palette; /* palette for 8-bit */ ++ ++ int splash_dosilent; /* show silent jpeg */ ++ unsigned char *splash_silentjpeg; ++ unsigned char *splash_sboxes; ++ int splash_sboxcount; ++}; ++#endif ++ + struct display { + /* Filled in by the low-level console driver */ + const u_char *fontdata; +--- a/drivers/video/vesafb.c ++++ b/drivers/video/vesafb.c +@@ -182,7 +182,10 @@ static void vesafb_destroy(struct fb_inf + framebuffer_release(info); + } + +-static struct fb_ops vesafb_ops = { ++#ifndef CONFIG_BOOTSPLASH ++static ++#endif ++struct fb_ops vesafb_ops = { + .owner = THIS_MODULE, + .fb_destroy = vesafb_destroy, + .fb_setcolreg = vesafb_setcolreg, +@@ -267,6 +270,9 @@ static int __devinit vesafb_probe(struct + * option to simply use size_total as that + * wastes plenty of kernel address space. */ + size_remap = size_vmode * 2; ++#ifdef CONFIG_BOOTSPLASH ++ size_remap *= 2; /* some more for the images */ ++#endif + if (vram_remap) + size_remap = vram_remap * 1024 * 1024; + if (size_remap < size_vmode) +--- a/include/linux/console_struct.h ++++ b/include/linux/console_struct.h +@@ -105,6 +105,9 @@ struct vc_data { + struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */ + unsigned long vc_uni_pagedir; + unsigned long *vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ ++#ifdef CONFIG_BOOTSPLASH ++ struct splash_data *vc_splash_data; ++#endif + /* additional information is in vt_kern.h */ + }; + +--- a/include/linux/fb.h ++++ b/include/linux/fb.h +@@ -859,6 +859,14 @@ struct fb_info { + void *fbcon_par; /* fbcon use-only private area */ + /* From here on everything is device dependent */ + void *par; ++#ifdef CONFIG_BOOTSPLASH ++ struct splash_data *splash_data; ++ unsigned char *splash_pic; ++ int splash_pic_size; ++ int splash_bytes; ++ char *silent_screen_base; /* real screen base */ ++ char fb_cursordata[64]; ++#endif + /* we need the PCI or similiar aperture base/size not + smem_start/size as smem_start may just be an object + allocated inside the aperture so may not actually overlap */ +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -122,7 +122,12 @@ NORET_TYPE void panic(const char * fmt, + * We can't use the "normal" timers since we just panicked. + */ + printk(KERN_EMERG "Rebooting in %d seconds..", panic_timeout); +- ++#ifdef CONFIG_BOOTSPLASH ++ { ++ extern int splash_verbose(void); ++ (void)splash_verbose(); ++ } ++#endif + for (i = 0; i < panic_timeout; i++) { + touch_nmi_watchdog(); + panic_blink_one_second(); +@@ -151,6 +156,12 @@ NORET_TYPE void panic(const char * fmt, + } + #endif + local_irq_enable(); ++#ifdef CONFIG_BOOTSPLASH ++ { ++ extern int splash_verbose(void); ++ (void)splash_verbose(); ++ } ++#endif + while (1) { + touch_softlockup_watchdog(); + panic_blink_one_second(); diff --git a/patches.suse/bootsplash-console-fix b/patches.suse/bootsplash-console-fix new file mode 100644 index 0000000..c5b3e63 --- /dev/null +++ b/patches.suse/bootsplash-console-fix @@ -0,0 +1,65 @@ +From: Takashi Iwai +Subject: Fix rendering on linux console with bootsplash +Patch-mainline: Never +References: bnc#595657,bnc#594209 + +Fix a bug introduced by Cleanup-and-make-boot-splash-work-with-KMS.patch. +The position was wrongly calculated in splash_bmove_redraw(). + +Also, a few clean-ups of render codes. + +Signed-off-by: Takashi Iwai + +--- + drivers/video/bootsplash/render.c | 19 +++++-------------- + 1 file changed, 5 insertions(+), 14 deletions(-) + +--- a/drivers/video/bootsplash/render.c ++++ b/drivers/video/bootsplash/render.c +@@ -210,11 +210,7 @@ + union pt p, q; + p.ul = (u32 *)dst; + q.ul = (u32 *)src; +- for (i=0; i < width/8; i++) { +- fb_writel(*q.ul++,p.ul++); +- fb_writel(*q.ul++,p.ul++); +- } +- if (width & 4) ++ for (i = 0; i < width / 4; i++) + fb_writel(*q.ul++,p.ul++); + if (width & 2) + fb_writew(*q.us++,p.us++); +@@ -234,12 +230,8 @@ + while (height-- > 0) { + union pt p; + p.ul = (u32 *)dst; +- if (octpp != 3) { +- for (i=0; i < width/8; i++) { +- fb_writel(bgx,p.ul++); +- fb_writel(bgx,p.ul++); +- } +- if (width & 4) ++ if (!(octpp & 1)) { ++ for (i = 0; i < width / 4; i++) + fb_writel(bgx,p.ul++); + if (width & 2) + fb_writew(bgx,p.us++); +@@ -248,7 +240,7 @@ + dst += dstbytes; + } else { /* slow! */ + for (i=0; i < width; i++) +- fb_writeb((bgx >> ((i & 0x3) * 8)) && 0xff,p.ub++); ++ fb_writeb((bgx >> ((i % 3) * 8)) && 0xff,p.ub++); + } + } + } +@@ -398,8 +390,7 @@ + void splash_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) + { + struct splash_data *sd; +- int octpp = (info->var.bits_per_pixel + 1) >> 3; +- unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * y + dx * octpp); ++ unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * y + dx * 2); + unsigned short *s = d + (dx - sx); + unsigned short *start = d; + unsigned short *ls = d; diff --git a/patches.suse/bootsplash-keep-multiple-data b/patches.suse/bootsplash-keep-multiple-data new file mode 100644 index 0000000..57dedcd --- /dev/null +++ b/patches.suse/bootsplash-keep-multiple-data @@ -0,0 +1,321 @@ +From: Takashi Iwai +Subject: Keep multiple splash screens for KMS +Patch-mainline: Never +References: bnc#570082 + +Keep multiple splash screens for reloading splash again when KMS is +kicked off. + +Signed-off-by: Takashi Iwai + +--- + drivers/video/bootsplash/bootsplash.c | 148 ++++++++++++++++++++++------------ + drivers/video/bootsplash/decode-jpg.c | 7 - + drivers/video/bootsplash/decode-jpg.h | 2 + drivers/video/console/fbcon.c | 11 ++ + drivers/video/console/fbcon.h | 1 + 5 files changed, 115 insertions(+), 54 deletions(-) + +--- a/drivers/video/bootsplash/bootsplash.c ++++ b/drivers/video/bootsplash/bootsplash.c +@@ -331,12 +331,14 @@ static int splash_check_jpeg(unsigned ch + + static void splash_free(struct vc_data *vc, struct fb_info *info) + { ++ struct splash_data *sd; ++ struct splash_data *next; + SPLASH_DEBUG(); +- if (!vc->vc_splash_data) +- return; +- if (vc->vc_splash_data->splash_silentjpeg) +- vfree(vc->vc_splash_data->splash_sboxes); +- vfree(vc->vc_splash_data); ++ for (sd = vc->vc_splash_data; sd; sd = next) { ++ next = sd->next; ++ vfree(sd->splash_sboxes); ++ vfree(sd); ++ } + vc->vc_splash_data = 0; + info->splash_data = 0; + } +@@ -418,6 +420,32 @@ static inline int splash_geti(unsigned c + pos[off] | pos[off + 1] << 8 | pos[off + 2] << 16 | pos[off + 3] << 24; + } + ++/* move the given splash_data to the current one */ ++static void splash_pivot_current(struct vc_data *vc, struct splash_data *new) ++{ ++ struct splash_data *sd; ++ int state, percent, silent; ++ ++ sd = vc->vc_splash_data; ++ if (!sd || sd == new) ++ return; ++ state = sd->splash_state; ++ percent = sd->splash_percent; ++ silent = sd->splash_dosilent; ++ for (; sd->next; sd = sd->next) { ++ if (sd->next == new) { ++ sd->next = new->next; ++ new->next = vc->vc_splash_data; ++ vc->vc_splash_data = new; ++ /* copy the current states */ ++ new->splash_state = state; ++ new->splash_percent = percent; ++ new->splash_dosilent = silent; ++ return; ++ } ++ } ++} ++ + static int splash_getraw(unsigned char *start, unsigned char *end, int *update) + { + unsigned char *ndata; +@@ -434,6 +462,8 @@ static int splash_getraw(unsigned char * + struct vc_data *vc; + struct fb_info *info; + struct splash_data *sd; ++ struct splash_data *splash_found = NULL; ++ int unit_found = -1; + int oldpercent, oldsilent; + + if (update) +@@ -442,6 +472,8 @@ static int splash_getraw(unsigned char * + if (!update || start[7] < '2' || start[7] > '3' || splash_geti(start, 12) != (int)0xffffffff) + printk(KERN_INFO "bootsplash %s: looking for picture...\n", SPLASH_VERSION); + ++ oldpercent = -1; ++ oldsilent = -1; + for (ndata = start; ndata < end; ndata++) { + if (ndata[0] != 'B' || ndata[1] != 'O' || ndata[2] != 'O' || ndata[3] != 'T') + continue; +@@ -522,12 +554,6 @@ static int splash_getraw(unsigned char * + printk(KERN_ERR "bootsplash: ...found, but truncated!\n"); + return -1; + } +- if (!jpeg_check_size(ndata + len + boxcnt * 12 + palcnt, width, height)) { +- ndata += len + splash_size - 1; +- continue; +- } +- if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, width, height, info->var.bits_per_pixel)) +- return -1; + silentsize = splash_geti(ndata, SPLASH_OFF_SSIZE); + if (silentsize) + printk(KERN_INFO "bootsplash: silentjpeg size %d bytes\n", silentsize); +@@ -543,32 +569,21 @@ static int splash_getraw(unsigned char * + silentsize = 0; + } + sboxcnt = splash_gets(ndata, SPLASH_OFF_SBOXCNT); +- if (silentsize) { +- unsigned char *simage = ndata + len + splash_size + 12 * sboxcnt; +- if (!jpeg_check_size(simage, width, height) || +- splash_check_jpeg(simage, width, height, info->var.bits_per_pixel)) { +- printk(KERN_WARNING "bootsplash: error in silent jpeg.\n"); +- silentsize = 0; +- } +- } +- oldpercent = -1; +- oldsilent = -1; + if (vc->vc_splash_data) { + oldpercent = vc->vc_splash_data->splash_percent; + oldsilent = vc->vc_splash_data->splash_dosilent; +- splash_free(vc, info); + } +- vc->vc_splash_data = sd = vmalloc(sizeof(*sd) + splash_size + (version < 3 ? 2 * 12 : 0)); ++ sd = vmalloc(sizeof(*sd) + splash_size + (version < 3 ? 2 * 12 : 0)); + if (!sd) + break; +- sd->splash_silentjpeg = 0; +- sd->splash_sboxes = 0; +- sd->splash_sboxcount = 0; ++ memset(sd, 0, sizeof(*sd)); ++ jpeg_get_size(ndata + len + boxcnt * 12 + palcnt, ++ &sd->splash_width, &sd->splash_height); + if (silentsize) { + sd->splash_silentjpeg = vmalloc(silentsize); + if (sd->splash_silentjpeg) { + memcpy(sd->splash_silentjpeg, ndata + len + splash_size, silentsize); +- sd->splash_sboxes = vc->vc_splash_data->splash_silentjpeg; ++ sd->splash_sboxes = sd->splash_silentjpeg; + sd->splash_silentjpeg += 12 * sboxcnt; + sd->splash_sboxcount = sboxcnt; + } +@@ -591,22 +606,6 @@ static int splash_getraw(unsigned char * + sd->splash_fg_color = (splash_default >> 4) & 0x0f; + sd->splash_state = splash_default & 1; + } +- if (sd->splash_text_xo + sd->splash_text_wi > width || sd->splash_text_yo + sd->splash_text_he > height) { +- splash_free(vc, info); +- printk(KERN_ERR "bootsplash: found, but has oversized text area!\n"); +- return -1; +- } +- if (!vc_cons[unit].d || info->fbops->fb_imageblit != cfb_imageblit) { +- splash_free(vc, info); +- printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n"); +- return -1; +- } +- printk(KERN_INFO "bootsplash: ...found (%dx%d, %d bytes, v%d).\n", width, height, splash_size, version); +- if (version == 1) { +- printk(KERN_WARNING "bootsplash: Using deprecated v1 header. Updating your splash utility recommended.\n"); +- printk(KERN_INFO "bootsplash: Find the latest version at http://www.bootsplash.org/\n"); +- } +- + /* fake penguin box for older formats */ + if (version == 1) + boxcnt = splash_mkpenguin(sd, sd->splash_text_xo + 10, sd->splash_text_yo + 10, sd->splash_text_wi - 20, sd->splash_text_he - 20, 0xf0, 0xf0, 0xf0); +@@ -620,8 +619,38 @@ static int splash_getraw(unsigned char * + sd->splash_jpeg = sd->splash_palette + palcnt; + sd->splash_palcnt = palcnt / 3; + sd->splash_dosilent = sd->splash_silentjpeg != 0 ? (oldsilent == -1 ? 1 : oldsilent) : 0; +- return unit; ++ ++ sd->next = vc->vc_splash_data; ++ vc->vc_splash_data = sd; ++ ++ if (sd->splash_width != width || sd->splash_height != height) { ++ ndata += len + splash_size - 1; ++ continue; ++ } ++ if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, width, height, info->var.bits_per_pixel)) { ++ ndata += len + splash_size - 1; ++ continue; ++ } ++ if (!vc_cons[unit].d || info->fbops->fb_imageblit != cfb_imageblit) { ++ splash_free(vc, info); ++ printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n"); ++ return -1; ++ } ++ printk(KERN_INFO "bootsplash: ...found (%dx%d, %d bytes, v%d).\n", width, height, splash_size, version); ++ if (version == 1) { ++ printk(KERN_WARNING "bootsplash: Using deprecated v1 header. Updating your splash utility recommended.\n"); ++ printk(KERN_INFO "bootsplash: Find the latest version at http://www.bootsplash.org/\n"); ++ } ++ ++ splash_found = sd; ++ unit_found = unit; ++ } ++ ++ if (splash_found) { ++ splash_pivot_current(vc, splash_found); ++ return unit_found; + } ++ + printk(KERN_ERR "bootsplash: ...no good signature found.\n"); + return -1; + } +@@ -696,6 +725,20 @@ static void splash_off(struct fb_info *i + info->splash_pic_size = 0; + } + ++/* look for the splash with the matching size and set it as the current */ ++static int splash_look_for_jpeg(struct vc_data *vc, int width, int height) ++{ ++ struct splash_data *sd; ++ ++ for (sd = vc->vc_splash_data; sd; sd = sd->next) { ++ if (sd->splash_width == width && sd->splash_height == height) { ++ splash_pivot_current(vc, sd); ++ return 0; ++ } ++ } ++ return -1; ++} ++ + int splash_prepare(struct vc_data *vc, struct fb_info *info) + { + int err; +@@ -719,6 +762,12 @@ int splash_prepare(struct vc_data *vc, s + splash_off(info); + return -2; + } ++ if (splash_look_for_jpeg(vc, width, height) < 0) { ++ printk(KERN_INFO "bootsplash: no matching splash %dx%d\n", ++ width, height); ++ splash_off(info); ++ return -2; ++ } + + sbytes = ((width + 15) & ~15) * octpp; + size = sbytes * ((height + 15) & ~15); +@@ -1007,11 +1056,14 @@ static int splash_write_proc(struct file + if (!strncmp(buffer,"freesilent\n",11)) { + SPLASH_DEBUG( " freesilent"); + if (vc->vc_splash_data && vc->vc_splash_data->splash_silentjpeg) { ++ struct splash_data *sd; + printk(KERN_INFO "bootsplash: freeing silent jpeg\n"); +- vc->vc_splash_data->splash_silentjpeg = 0; +- vfree(vc->vc_splash_data->splash_sboxes); +- vc->vc_splash_data->splash_sboxes = 0; +- vc->vc_splash_data->splash_sboxcount = 0; ++ for (sd = vc->vc_splash_data; sd; sd = sd->next) { ++ sd->splash_silentjpeg = 0; ++ vfree(sd->splash_sboxes); ++ sd->splash_sboxes = 0; ++ sd->splash_sboxcount = 0; ++ } + if (vc->vc_splash_data->splash_dosilent) { + splash_status(vc); + } +--- a/drivers/video/bootsplash/decode-jpg.c ++++ b/drivers/video/bootsplash/decode-jpg.c +@@ -240,7 +240,7 @@ static int dec_checkmarker(void) + return 0; + } + +-int jpeg_check_size(unsigned char *buf, int width, int height) ++void jpeg_get_size(unsigned char *buf, int *width, int *height) + { + datap = buf; + getbyte(); +@@ -248,9 +248,8 @@ int jpeg_check_size(unsigned char *buf, + readtables(M_SOF0); + getword(); + getbyte(); +- if (height != getword() || width != getword()) +- return 0; +- return 1; ++ *height = getword(); ++ *width = getword(); + } + + int jpeg_decode(buf, pic, width, height, depth, decdata) +--- a/drivers/video/bootsplash/decode-jpg.h ++++ b/drivers/video/bootsplash/decode-jpg.h +@@ -30,6 +30,6 @@ struct jpeg_decdata { + }; + + extern int jpeg_decode(unsigned char *, unsigned char *, int, int, int, struct jpeg_decdata *); +-extern int jpeg_check_size(unsigned char *, int, int); ++extern void jpeg_get_size(unsigned char *, int *, int *); + + #endif +--- a/drivers/video/console/fbcon.c ++++ b/drivers/video/console/fbcon.c +@@ -2185,7 +2185,16 @@ static int fbcon_switch(struct vc_data * + ops = info->fbcon_par; + + #ifdef CONFIG_BOOTSPLASH +- splash_prepare(vc, info); ++ { ++ struct splash_data *prev_sd = vc->vc_splash_data; ++ splash_prepare(vc, info); ++ if (vc->vc_splash_data && vc->vc_splash_data->splash_state && ++ vc->vc_splash_data != prev_sd) { ++ vc_resize(vc, vc->vc_splash_data->splash_text_wi / vc->vc_font.width, ++ vc->vc_splash_data->splash_text_he / vc->vc_font.height); ++ con_remap_def_color(vc, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color); ++ } ++ } + #endif + + if (softback_top) { +--- a/drivers/video/console/fbcon.h ++++ b/drivers/video/console/fbcon.h +@@ -50,6 +50,7 @@ struct splash_data { + unsigned char *splash_silentjpeg; + unsigned char *splash_sboxes; + int splash_sboxcount; ++ struct splash_data *next; + }; + #endif + diff --git a/patches.suse/bootsplash-scaler b/patches.suse/bootsplash-scaler new file mode 100644 index 0000000..3c50c3e --- /dev/null +++ b/patches.suse/bootsplash-scaler @@ -0,0 +1,1292 @@ +From: Egbert Eich +Subject: Add bootsplash image scaler +Patch-mainline: Never +References: bnc#570082 + +The initrd most often contains a single fixed size boot image which is of the +size of the VESA framebuffer used for boot. If the size of the framebuffer +changes when KMS is initialized for example the boot splash is turned off. +Takashi Iwai has provided a patch which allows to add multiple images to initrd +so that the kernel can select the appropriate size. This is only feasible for +mobile devices with a build in panel of a fixed resolution in which case one +would have to ship two images at the most: the one of the VESA resolution at +boot time and the one of the panel as used by KMS. +The attached patch adds a boot splash scaler which allows the down scaling of +bootsplash image which is bigger than the screen size. This way by supplying a +single image large enough to accomodate the largest screens possible all +resolutions can be derived from it. + +Acked-by: Michal Marek + +--- + drivers/video/bootsplash/bootsplash.c | 858 +++++++++++++++++++++++++++++++--- + drivers/video/bootsplash/decode-jpg.c | 4 + drivers/video/bootsplash/render.c | 16 + drivers/video/console/fbcon.h | 11 + include/linux/fb.h | 3 + 5 files changed, 810 insertions(+), 82 deletions(-) + +--- a/drivers/video/bootsplash/bootsplash.c ++++ b/drivers/video/bootsplash/bootsplash.c +@@ -6,6 +6,7 @@ + * Stefan Reinauer, , + * Steffen Winterfeldt, , + * Michael Schroeder ++ * 2009, 2010 Egbert Eich + * + * Ideas & SuSE screen work by Ken Wimer, + * +@@ -55,7 +56,9 @@ static unsigned char *jpg_errors[] = { + "wrong marker", + "no EOI", + "bad tables", +- "depth mismatch" ++ "depth mismatch", ++ "scale error", ++ "out of memory" + }; + + static struct jpeg_decdata *decdata = 0; /* private decoder data */ +@@ -64,7 +67,9 @@ static int splash_registered = 0; + static int splash_usesilent = 0; /* shall we display the silentjpeg? */ + int splash_default = 0xf01; + +-static int splash_check_jpeg(unsigned char *jpeg, int width, int height, int depth); ++static int jpeg_get(unsigned char *buf, unsigned char *pic, int width, int height, int depth, ++ struct jpeg_decdata *decdata); ++static int splash_look_for_jpeg(struct vc_data *vc, int width, int height); + + static int __init splash_setup(char *options) + { +@@ -120,7 +125,8 @@ static int boxextract(unsigned char *buf + return 12; + } + +-static void boxit(unsigned char *pic, int bytes, unsigned char *buf, int num, int percent, int overpaint, int octpp) ++static void boxit(unsigned char *pic, int bytes, unsigned char *buf, int num, ++ int percent, int xoff, int yoff, int overpaint, int octpp) + { + int x, y, p, doblend, r, g, b, a, add; + unsigned int i = 0; +@@ -245,7 +251,7 @@ static void boxit(unsigned char *pic, in + } + add = (xs & 1); + add ^= (add ^ y) & 1 ? 1 : 3; /* 2x2 ordered dithering */ +- picp.ub = (pic + xs * octpp + y * bytes); ++ picp.ub = (pic + (xs + xoff) * octpp + (y + yoff) * bytes); + for (x = xs; x <= xe; x++) { + if (!(sti & 0x80000000)) { + sti <<= 1; +@@ -310,19 +316,172 @@ static void boxit(unsigned char *pic, in + } + } + ++static void box_offsets(unsigned char *buf, int num, ++ int screen_w, int screen_h, int pic_w, int pic_h, ++ int *x_off, int *y_off) ++{ ++ int a, doblend; ++ int x_min = pic_w, x_max = 0; ++ int y_min = pic_h, y_max = 0; ++ unsigned int i = 0; ++ unsigned short data1[4]; ++ unsigned char cols1[16]; ++ unsigned short data2[4]; ++ unsigned char cols2[16]; ++ unsigned char *bufend; ++ unsigned int stin, stinn, stixs, stixe, stiys, stiye; ++ int xs, xe, ys, ye; ++ ++ SPLASH_DEBUG(); ++ ++ if ((screen_w == pic_w && screen_h == pic_h) || num == 0) ++ *x_off = *y_off = 0; ++ ++ bufend = buf + num * 12; ++ stin = 1; ++ stinn = 0; ++ stixs = stixe = 0; ++ stiys = stiye = 0; ++ ++ while(buf < bufend) { ++ doblend = 0; ++ buf += boxextract(buf, data1, cols1, &doblend); ++ if (data1[0] == 32767 && data1[1] == 32767) { ++ /* box stipple */ ++ if (stinn == 32) ++ continue; ++ if (stinn == 0) { ++ stixs = data1[2]; ++ stixe = data1[3]; ++ stiys = stiye = 0; ++ } else if (stinn == 4) { ++ stiys = data1[2]; ++ stiye = data1[3]; ++ } ++ stin = stinn; ++ continue; ++ } ++ stinn = 0; ++ if (data1[0] > 32767) ++ buf += boxextract(buf, data2, cols2, &doblend); ++ if (data1[0] == 32767 && data1[1] == 32766) { ++ /* box copy */ ++ i = 12 * (short)data1[3]; ++ doblend = 0; ++ i += boxextract(buf + i, data1, cols1, &doblend); ++ if (data1[0] > 32767) ++ boxextract(buf + i, data2, cols2, &doblend); ++ } ++ if (data1[0] == 32767) ++ continue; ++ if (data1[2] > 32767) { ++ data1[2] = ~data1[2]; ++ } ++ if (data1[3] > 32767) { ++ data1[3] = ~data1[3]; ++ } ++ if (data1[0] > 32767) { ++ data1[0] = ~data1[0]; ++ for (i = 0; i < 4; i++) ++ data1[i] = (data1[i] * (65536 - 1) + data2[i] * 1) >> 16; ++ } ++ *(unsigned int *)cols2 = *(unsigned int *)cols1; ++ a = cols2[3]; ++ if (a == 0 && !doblend) ++ continue; ++ ++ if (stixs >= 32768) { ++ xs = (stixs ^ 65535) + data1[0]; ++ xe = stixe ? stixe + data1[0] : data1[2]; ++ } else if (stixe >= 32768) { ++ xs = stixs ? data1[2] - stixs : data1[0]; ++ xe = data1[2] - (stixe ^ 65535); ++ } else { ++ xs = stixs; ++ xe = stixe ? stixe : data1[2]; ++ } ++ if (stiys >= 32768) { ++ ys = (stiys ^ 65535) + data1[1]; ++ ye = stiye ? stiye + data1[1] : data1[3]; ++ } else if (stiye >= 32768) { ++ ys = stiys ? data1[3] - stiys : data1[1]; ++ ye = data1[3] - (stiye ^ 65535); ++ } else { ++ ys = stiys; ++ ye = stiye ? stiye : data1[3]; ++ } ++ if (xs < data1[0]) ++ xs = data1[0]; ++ if (xe > data1[2]) ++ xe = data1[2]; ++ if (ys < data1[1]) ++ ys = data1[1]; ++ if (ye > data1[3]) ++ ye = data1[3]; ++ ++ if (xs < x_min) ++ x_min = xs; ++ if (xe > x_max) ++ x_max = xe; ++ if (ys < y_min) ++ y_min = ys; ++ if (ye > y_max) ++ y_max = ye; ++ } ++ { ++ int x_center = (x_min + x_max) / 2; ++ int y_center = (y_min + y_max) / 2; ++ ++ if (screen_w == pic_w) ++ *x_off = 0; ++ else { ++ if (x_center < (pic_w + pic_w / 10) >> 1 && x_center > (pic_w - pic_w / 10) >> 1) ++ *x_off = (screen_w - pic_w) >> 1; ++ else { ++ int x = x_center * screen_w / pic_w; ++ *x_off = x - x_center; ++ if (x_min + x_off > 0) ++ *x_off = 0; ++ if (x_max + *x_off > screen_w) ++ *x_off = screen_w - pic_w; ++ } ++ } ++ if (screen_h == pic_h) ++ *y_off = 0; ++ else { ++ if (y_center < (pic_h + pic_h / 10) >> 1 && y_center > (pic_h - pic_h / 10) >> 1) ++ *y_off = (screen_h - pic_h) >> 1; ++ else { ++ int x = y_center * screen_h / pic_h; ++ *y_off = x - y_center; ++ if (y_min + y_off > 0) ++ *y_off = 0; ++ if (y_max + *x_off > screen_h) ++ *y_off = screen_h - pic_h; ++ } ++ } ++ } ++} ++ + static int splash_check_jpeg(unsigned char *jpeg, int width, int height, int depth) + { + int size, err; + unsigned char *mem; + +- size = ((width + 15) & ~15) * ((height + 15) & ~15) * (depth >> 3); ++ size = ((width + 15) & ~15) * ((height + 15) & ~15) * ((depth + 1) >> 3); + mem = vmalloc(size); + if (!mem) { + printk(KERN_INFO "bootsplash: no memory for decoded picture.\n"); + return -1; + } +- if (!decdata) +- decdata = vmalloc(sizeof(*decdata)); ++ if (!decdata) { ++ decdata = vmalloc(sizeof(*decdata)); ++ if (!decdata) { ++ printk(KERN_INFO "bootsplash: not enough memory.\n"); ++ vfree(mem); ++ return -1; ++ } ++ } + if ((err = jpeg_decode(jpeg, mem, ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) + printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d)\n",jpg_errors[err - 1], err); + vfree(mem); +@@ -337,6 +496,7 @@ static void splash_free(struct vc_data * + for (sd = vc->vc_splash_data; sd; sd = next) { + next = sd->next; + vfree(sd->splash_sboxes); ++ vfree(sd->splash_pic); + vfree(sd); + } + vc->vc_splash_data = 0; +@@ -432,6 +592,11 @@ static void splash_pivot_current(struct + state = sd->splash_state; + percent = sd->splash_percent; + silent = sd->splash_dosilent; ++ vfree(sd->splash_pic); ++ sd->splash_pic_size = 0; ++ sd->splash_pic = NULL; ++ sd->splash_text_wi = sd->splash_jpg_text_wi; ++ sd->splash_text_he = sd->splash_jpg_text_he; + for (; sd->next; sd = sd->next) { + if (sd->next == new) { + sd->next = new->next; +@@ -441,6 +606,17 @@ static void splash_pivot_current(struct + new->splash_state = state; + new->splash_percent = percent; + new->splash_dosilent = silent; ++ new->splash_text_wi = new->splash_jpg_text_wi; ++ new->splash_text_he = new->splash_jpg_text_he; ++ ++ vfree(new->splash_pic); ++ new->splash_pic = NULL; ++ new->splash_pic_size = 0; ++ ++ new->splash_boxes_xoff = 0; ++ new->splash_boxes_yoff = 0; ++ new->splash_sboxes_xoff = 0; ++ new->splash_sboxes_yoff = 0; + return; + } + } +@@ -459,7 +635,7 @@ static int splash_getraw(unsigned char * + int palcnt; + int i, len; + const int *offsets; +- struct vc_data *vc; ++ struct vc_data *vc = NULL; + struct fb_info *info; + struct splash_data *sd; + struct splash_data *splash_found = NULL; +@@ -489,7 +665,16 @@ static int splash_getraw(unsigned char * + vc_allocate(unit); + } + vc = vc_cons[unit].d; ++ if (!vc) ++ continue; ++ + info = registered_fb[(int)con2fb_map[unit]]; ++ ++ if (info->fbops->fb_imageblit != cfb_imageblit) { ++ splash_free(vc, info); ++ printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n"); ++ return -1; ++ } + width = info->var.xres; + height = info->var.yres; + splash_size = splash_geti(ndata, SPLASH_OFF_SIZE); +@@ -539,6 +724,9 @@ static int splash_getraw(unsigned char * + } + if (update) + *update = up; ++ vfree(sd->splash_pic); ++ sd->splash_pic = NULL; ++ sd->splash_pic_size = 0; + } + return unit; + } +@@ -579,6 +767,12 @@ static int splash_getraw(unsigned char * + memset(sd, 0, sizeof(*sd)); + jpeg_get_size(ndata + len + boxcnt * 12 + palcnt, + &sd->splash_width, &sd->splash_height); ++ if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, ++ sd->splash_width, sd->splash_height, info->var.bits_per_pixel)) { ++ ndata += len + splash_size - 1; ++ vfree(sd); ++ continue; ++ } + if (silentsize) { + sd->splash_silentjpeg = vmalloc(silentsize); + if (sd->splash_silentjpeg) { +@@ -596,6 +790,8 @@ static int splash_getraw(unsigned char * + sd->splash_text_yo = splash_gets(ndata, SPLASH_OFF_YO); + sd->splash_text_wi = splash_gets(ndata, SPLASH_OFF_WI); + sd->splash_text_he = splash_gets(ndata, SPLASH_OFF_HE); ++ sd->splash_pic = NULL; ++ sd->splash_pic_size = 0; + sd->splash_percent = oldpercent == -1 ? splash_gets(ndata, SPLASH_OFF_PERCENT) : oldpercent; + if (version == 1) { + sd->splash_text_xo *= 8; +@@ -606,6 +802,9 @@ static int splash_getraw(unsigned char * + sd->splash_fg_color = (splash_default >> 4) & 0x0f; + sd->splash_state = splash_default & 1; + } ++ sd->splash_jpg_text_wi = sd->splash_text_wi; ++ sd->splash_jpg_text_he = sd->splash_text_he; ++ + /* fake penguin box for older formats */ + if (version == 1) + boxcnt = splash_mkpenguin(sd, sd->splash_text_xo + 10, sd->splash_text_yo + 10, sd->splash_text_wi - 20, sd->splash_text_he - 20, 0xf0, 0xf0, 0xf0); +@@ -627,15 +826,6 @@ static int splash_getraw(unsigned char * + ndata += len + splash_size - 1; + continue; + } +- if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, width, height, info->var.bits_per_pixel)) { +- ndata += len + splash_size - 1; +- continue; +- } +- if (!vc_cons[unit].d || info->fbops->fb_imageblit != cfb_imageblit) { +- splash_free(vc, info); +- printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n"); +- return -1; +- } + printk(KERN_INFO "bootsplash: ...found (%dx%d, %d bytes, v%d).\n", width, height, splash_size, version); + if (version == 1) { + printk(KERN_WARNING "bootsplash: Using deprecated v1 header. Updating your splash utility recommended.\n"); +@@ -649,6 +839,16 @@ static int splash_getraw(unsigned char * + if (splash_found) { + splash_pivot_current(vc, splash_found); + return unit_found; ++ } else { ++ vc = vc_cons[0].d; ++ if (vc) { ++ info = registered_fb[(int)con2fb_map[0]]; ++ width = info->var.xres; ++ height = info->var.yres; ++ if (!splash_look_for_jpeg(vc, width, height)) ++ return -1; ++ return 0; ++ } + } + + printk(KERN_ERR "bootsplash: ...no good signature found.\n"); +@@ -715,27 +915,71 @@ int splash_verbose(void) + return 0; + } + +-static void splash_off(struct fb_info *info) ++static void splash_off(struct vc_data *vc,struct fb_info *info) + { ++ int rows = info->var.xres / vc->vc_font.width; ++ int cols = info->var.yres / vc->vc_font.height; + SPLASH_DEBUG(); ++ + info->splash_data = 0; +- if (info->splash_pic) +- vfree(info->splash_pic); +- info->splash_pic = 0; +- info->splash_pic_size = 0; ++ if (rows != vc->vc_rows || cols != vc->vc_cols) ++ vc_resize(vc, rows, cols); ++ if (vc->vc_def_color != 0x07) ++ con_remap_def_color(vc, 0x07); + } + + /* look for the splash with the matching size and set it as the current */ + static int splash_look_for_jpeg(struct vc_data *vc, int width, int height) + { +- struct splash_data *sd; ++ struct splash_data *sd, *found = NULL; ++ int found_delta_x = INT_MAX, found_delta_y = INT_MAX; + + for (sd = vc->vc_splash_data; sd; sd = sd->next) { +- if (sd->splash_width == width && sd->splash_height == height) { +- splash_pivot_current(vc, sd); +- return 0; ++ int delta_x = abs(sd->splash_width - width) * height; ++ int delta_y = abs(sd->splash_height - height) * width; ++ if (!found || (found_delta_x + found_delta_y > delta_x + delta_y)) { ++ found = sd; ++ found_delta_x = delta_x; ++ found_delta_y = delta_y; + } + } ++ ++ if (found) { ++ SPLASH_DEBUG("bootsplash: scalable image found (%dx%d scaled to %dx%d).", ++ found->splash_width, found->splash_height, width, height); ++ ++ splash_pivot_current(vc, found); ++ ++ /* textarea margins are constant independent from image size */ ++ if (found->splash_height != height) ++ found->splash_text_he = height - (found->splash_height - found->splash_jpg_text_he); ++ else ++ found->splash_text_he = found->splash_jpg_text_he; ++ if (found->splash_width != width) ++ found->splash_text_wi = width - (found->splash_width - found->splash_jpg_text_wi); ++ else ++ found->splash_text_wi = found->splash_jpg_text_wi; ++ ++ if (found->splash_width != width || found->splash_height != height) { ++ box_offsets(found->splash_boxes, found->splash_boxcount, ++ width, height, found->splash_width, found->splash_height, ++ &found->splash_boxes_xoff, &found->splash_boxes_yoff); ++ SPLASH_DEBUG("bootsplash: offsets for boxes: x=%d y=%d", ++ found->splash_boxes_xoff,found->splash_boxes_yoff); ++ ++ if (found->splash_sboxes) { ++ box_offsets(found->splash_sboxes, found->splash_sboxcount, ++ width, height, found->splash_width, found->splash_height, ++ &found->splash_sboxes_xoff, &found->splash_sboxes_yoff); ++ SPLASH_DEBUG("bootsplash: offsets sboxes: x=%d y=%d", ++ found->splash_sboxes_xoff,found->splash_sboxes_yoff); ++ } ++ } else { ++ found->splash_sboxes_xoff = 0; ++ found->splash_sboxes_yoff = 0; ++ } ++ return 0; ++ } + return -1; + } + +@@ -743,13 +987,14 @@ int splash_prepare(struct vc_data *vc, s + { + int err; + int width, height, depth, octpp, size, sbytes; ++ int pic_update = 0; + + SPLASH_DEBUG("vc_num: %i", vc->vc_num); + if (!vc->vc_splash_data || !vc->vc_splash_data->splash_state) { + if (decdata) + vfree(decdata); + decdata = 0; +- splash_off(info); ++ splash_off(vc,info); + return -1; + } + +@@ -759,52 +1004,62 @@ int splash_prepare(struct vc_data *vc, s + octpp = (depth + 1) >> 3; + + if (depth == 24 || depth < 15) { /* Other targets might need fixing */ +- splash_off(info); ++ splash_off(vc,info); + return -2; + } + if (splash_look_for_jpeg(vc, width, height) < 0) { + printk(KERN_INFO "bootsplash: no matching splash %dx%d\n", + width, height); +- splash_off(info); ++ splash_off(vc,info); + return -2; + } + + sbytes = ((width + 15) & ~15) * octpp; + size = sbytes * ((height + 15) & ~15); +- if (size != info->splash_pic_size) { +- vfree(info->splash_pic); +- info->splash_pic = NULL; +- } +- if (!info->splash_pic) +- info->splash_pic = vmalloc(size); + +- if (!info->splash_pic) { ++ if (size != vc->vc_splash_data->splash_pic_size) { ++ vfree(vc->vc_splash_data->splash_pic); ++ vc->vc_splash_data->splash_pic = NULL; ++ } ++ if (!vc->vc_splash_data->splash_pic) { ++ vc->vc_splash_data->splash_pic = vmalloc(size); ++ pic_update = 1; ++ } ++ if (!vc->vc_splash_data->splash_pic) { + printk(KERN_INFO "bootsplash: not enough memory.\n"); +- splash_off(info); ++ splash_off(vc,info); + return -3; + } + +- if (!decdata) ++ if (!decdata) { + decdata = vmalloc(sizeof(*decdata)); ++ if (!decdata) { ++ printk(KERN_INFO "bootsplash: not enough memory.\n"); ++ splash_off(vc,info); ++ return -3; ++ } ++ } + + if (vc->vc_splash_data->splash_silentjpeg && vc->vc_splash_data->splash_dosilent) { +- /* fill area after framebuffer with other jpeg */ +- if ((err = jpeg_decode(vc->vc_splash_data->splash_silentjpeg, info->splash_pic, +- ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { ++ pic_update = 1; ++ if ((err = jpeg_get(vc->vc_splash_data->splash_silentjpeg, vc->vc_splash_data->splash_pic, ++ width, height, depth, decdata))) { + printk(KERN_INFO "bootsplash: error while decompressing silent picture: %s (%d)\n", + jpg_errors[err - 1], err); + vc->vc_splash_data->splash_dosilent = 0; + } else { + if (vc->vc_splash_data->splash_sboxcount) +- boxit(info->splash_pic, ++ boxit(vc->vc_splash_data->splash_pic, + sbytes, + vc->vc_splash_data->splash_sboxes, + vc->vc_splash_data->splash_sboxcount, + vc->vc_splash_data->splash_percent, ++ vc->vc_splash_data->splash_sboxes_xoff, ++ vc->vc_splash_data->splash_sboxes_yoff, + 0, + octpp); + splashcopy(info->screen_base, +- info->splash_pic, ++ vc->vc_splash_data->splash_pic, + info->var.yres, + info->var.xres, + info->fix.line_length, sbytes, +@@ -813,27 +1068,43 @@ int splash_prepare(struct vc_data *vc, s + } else + vc->vc_splash_data->splash_dosilent = 0; + +- if ((err = jpeg_decode(vc->vc_splash_data->splash_jpeg, info->splash_pic, +- ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { +- printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d) .\n", +- jpg_errors[err - 1], err); +- splash_off(info); +- return -4; ++ if (pic_update) { ++ if ((err = jpeg_get(vc->vc_splash_data->splash_jpeg, vc->vc_splash_data->splash_pic, ++ width, height, depth, decdata))) { ++ printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d) .\n", ++ jpg_errors[err - 1], err); ++ splash_off(vc,info); ++ return -4; ++ } + } +- info->splash_pic_size = size; +- info->splash_pic_stride = sbytes; ++ ++ vc->vc_splash_data->splash_pic_size = size; ++ vc->vc_splash_data->splash_pic_stride = sbytes; ++ + if (vc->vc_splash_data->splash_boxcount) +- boxit(info->splash_pic, ++ boxit(vc->vc_splash_data->splash_pic, + sbytes, + vc->vc_splash_data->splash_boxes, + vc->vc_splash_data->splash_boxcount, + vc->vc_splash_data->splash_percent, ++ vc->vc_splash_data->splash_boxes_xoff, ++ vc->vc_splash_data->splash_boxes_yoff, + 0, + octpp); +- if (vc->vc_splash_data->splash_state) ++ if (vc->vc_splash_data->splash_state) { ++ int cols = vc->vc_splash_data->splash_text_wi / vc->vc_font.width; ++ int rows = vc->vc_splash_data->splash_text_he / vc->vc_font.height; ++ int color = vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color; + info->splash_data = vc->vc_splash_data; +- else { +- splash_off(info); ++ ++ /* vc_resize also calls con_switch which resets yscroll */ ++ if (rows != vc->vc_rows || cols != vc->vc_cols) ++ vc_resize(vc, cols, rows); ++ if (vc->vc_def_color != color) ++ con_remap_def_color(vc, color); ++ ++ } else { ++ splash_off(vc,info); + return -5; + } + return 0; +@@ -856,12 +1127,16 @@ static struct proc_dir_entry *proc_splas + + static int splash_recolor(struct vc_data *vc) + { ++ int color; ++ + SPLASH_DEBUG(); + if (!vc->vc_splash_data) + return -1; + if (!vc->vc_splash_data->splash_state) + return 0; +- con_remap_def_color(vc, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color); ++ color = vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color; ++ if (vc->vc_def_color != color) ++ con_remap_def_color(vc, color); + if (fg_console == vc->vc_num) { + update_region(vc, + vc->vc_origin + vc->vc_size_row * vc->vc_top, +@@ -884,10 +1159,6 @@ static int splash_status(struct vc_data + splash_prepare(vc, info); + if (vc->vc_splash_data && vc->vc_splash_data->splash_state) { + if (info->splash_data) { +- con_remap_def_color(vc, info->splash_data->splash_color << 4 | info->splash_data->splash_fg_color); +- /* vc_resize also calls con_switch which resets yscroll */ +- vc_resize(vc, info->splash_data->splash_text_wi / vc->vc_font.width, +- info->splash_data->splash_text_he / vc->vc_font.height); + if (fg_console == vc->vc_num) { + update_region(vc, + vc->vc_origin + vc->vc_size_row * vc->vc_top, +@@ -895,11 +1166,9 @@ static int splash_status(struct vc_data + splash_clear_margins(vc, info, 0); + } + } +- } else { +- /* Switch bootsplash off */ +- con_remap_def_color(vc, 0x07); +- vc_resize(vc, info->var.xres / vc->vc_font.width, info->var.yres / vc->vc_font.height); +- } ++ } else ++ splash_off(vc,info); ++ + return 0; + } + +@@ -956,10 +1225,9 @@ void splash_set_percent(struct vc_data * + || pe < oldpe) { + if (splash_hasinter(vc->vc_splash_data->splash_boxes, + vc->vc_splash_data->splash_boxcount)) { +- splash_status(vc); +- } +- else +- splash_prepare(vc, info); ++ splash_status(vc); ++ } else ++ splash_prepare(vc, info); + } else { + int octpp = (info->var.bits_per_pixel + 1) >> 3; + if (info->splash_data) { +@@ -970,6 +1238,8 @@ void splash_set_percent(struct vc_data * + info->splash_data->splash_sboxes, + info->splash_data->splash_sboxcount, + info->splash_data->splash_percent, ++ info->splash_data->splash_sboxes_xoff, ++ info->splash_data->splash_sboxes_yoff, + 1, + octpp); + #if 0 +@@ -979,6 +1249,8 @@ void splash_set_percent(struct vc_data * + info->splash_data->splash_boxes, + info->splash_data->splash_boxcount, + info->splash_data->splash_percent, ++ info->splash_data->splash_boxes_xoff, ++ info->splash_data->splash_boxes_yoff, + 1, + octpp); + #endif +@@ -1100,6 +1372,8 @@ static int splash_write_proc(struct file + info->splash_data->splash_sboxes, + info->splash_data->splash_sboxcount, + info->splash_data->splash_percent, ++ info->splash_data->splash_sboxes_xoff, ++ info->splash_data->splash_sboxes_yoff, + 1, + octpp); + } else if ((up & 1) != 0) { +@@ -1108,6 +1382,8 @@ static int splash_write_proc(struct file + info->splash_data->splash_boxes, + info->splash_data->splash_boxcount, + info->splash_data->splash_percent, ++ info->splash_data->splash_boxes_xoff, ++ info->splash_data->splash_boxes_yoff, + 1, + octpp); + } +@@ -1226,3 +1502,447 @@ void splash_init(void) + return; + } + ++#define SPLASH_ALIGN 15 ++ ++static u32 *do_coefficients(u32 from, u32 to, u32 *shift) ++{ ++ u32 *coefficients; ++ u32 left = to; ++ int n = 1; ++ u32 upper = 31; ++ int col_cnt = 0; ++ int row_cnt = 0; ++ int m; ++ u32 rnd = from >> 1; ++ ++ if (from > to) { ++ left = to; ++ rnd = from >> 1; ++ ++ while (upper > 0) { ++ if ((1 << upper) & from) ++ break; ++ upper--; ++ } ++ upper++; ++ ++ *shift = 32 - 8 - 1 - upper; ++ ++ coefficients = vmalloc(sizeof (u32) * (from / to + 2) * from + 1); ++ if (!coefficients) ++ return NULL; ++ ++ n = 1; ++ while (1) { ++ u32 sum = left; ++ col_cnt = 0; ++ m = n++; ++ while (sum < from) { ++ coefficients[n++] = ((left << *shift) + rnd) / from; ++ col_cnt++; ++ left = to; ++ sum += left; ++ } ++ left = sum - from; ++ coefficients[n++] = (((to - left) << *shift) + rnd) / from; ++ col_cnt++; ++ coefficients[m] = col_cnt; ++ row_cnt++; ++ if (!left) { ++ coefficients[0] = row_cnt; ++ return coefficients; ++ } ++ } ++ } else { ++ left = 0; ++ rnd = to >> 1; ++ ++ while (upper > 0) { ++ if ((1 << upper) & to) ++ break; ++ upper--; ++ } ++ upper++; ++ ++ *shift = 32 - 8 - 1 - upper; ++ ++ coefficients = vmalloc(sizeof (u32) * 3 * from + 1); ++ if (!coefficients) ++ return NULL; ++ ++ while (1) { ++ u32 diff; ++ u32 sum = left; ++ col_cnt = 0; ++ row_cnt++; ++ while (sum < to) { ++ col_cnt++; ++ sum += from; ++ } ++ left = sum - to; ++ diff = from - left; ++ if (!left) { ++ coefficients[n] = col_cnt; ++ coefficients[0] = row_cnt; ++ return coefficients; ++ } ++ coefficients[n++] = col_cnt - 1; ++ coefficients[n++] = ((diff << *shift) + rnd) / from; ++ coefficients[n++] = ((left << *shift) + rnd) / from; ++ } ++ } ++} ++ ++ ++struct pixel ++{ ++ u32 red; ++ u32 green; ++ u32 blue; ++}; ++ ++#define put_pixel(pix, buf, depth) \ ++ switch (depth) { \ ++ case 15: \ ++ *(u16 *)(buf) = (u16)((pix).red << 10 | (pix).green << 5 | (pix).blue); \ ++ (buf) += 2; \ ++ break; \ ++ case 16: \ ++ *(u16 *)(buf) = (u16)((pix).red << 11 | (pix).green << 5 | (pix).blue); \ ++ (buf) += 2; \ ++ break; \ ++ case 24: \ ++ *(u16 *)(buf) = (u16)((pix).red << 8 | (pix).green); \ ++ buf += 2; \ ++ *((buf)++) = (pix).blue; \ ++ break; \ ++ case 32: \ ++ *(u32 *)(buf) = (u32)((pix).red << 16 | (pix).green << 8 | (pix).blue); \ ++ (buf) += 4; \ ++ break; \ ++} ++ ++#define get_pixel(pix, buf, depth) \ ++ switch (depth) { \ ++case 15: \ ++ (pix).red = ((*(u16 *)(buf)) >> 10) & 0x1f; \ ++ (pix).green = ((*(u16 *)(buf)) >> 5) & 0x1f; \ ++ (pix).blue = (*(u16 *)(buf)) & 0x1f; \ ++ (buf) += 2; \ ++ break; \ ++case 16: \ ++ (pix).red = ((*(u16 *)(buf)) >> 11) & 0x1f; \ ++ (pix).green = ((*(u16 *)(buf)) >> 5) & 0x3f; \ ++ (pix).blue = (*(u16 *)(buf)) & 0x1f; \ ++ (buf) += 2; \ ++ break; \ ++case 24: \ ++ (pix).blue = *(((buf))++); \ ++ (pix).green = *(((buf))++); \ ++ (pix).red = *(((buf))++); \ ++ break; \ ++case 32: \ ++ (pix).blue = *(((buf))++); \ ++ (pix).green = *(((buf))++); \ ++ (pix).red = *(((buf))++); \ ++ (buf)++; \ ++ break; \ ++} ++ ++static inline void ++scale_x_down(int depth, int src_w, unsigned char **src_p, u32 *x_coeff, u32 x_shift, u32 y_coeff, struct pixel *row_buffer) ++{ ++ u32 curr_x_coeff = 1; ++ struct pixel curr_pixel, tmp_pixel; ++ u32 x_array_size = x_coeff[0]; ++ int x_column_num; ++ int i; ++ int l,m; ++ int k = 0; ++ u32 rnd = (1 << (x_shift - 1)); ++ ++ for (i = 0; i < src_w; ) { ++ curr_x_coeff = 1; ++ get_pixel(tmp_pixel, *src_p, depth); ++ i++; ++ for (l = 0; l < x_array_size; l++) { ++ x_column_num = x_coeff[curr_x_coeff++]; ++ curr_pixel.red = curr_pixel.green = curr_pixel.blue = 0; ++ for (m = 0; m < x_column_num - 1; m++) { ++ curr_pixel.red += tmp_pixel.red * x_coeff[curr_x_coeff]; ++ curr_pixel.green += tmp_pixel.green * x_coeff[curr_x_coeff]; ++ curr_pixel.blue += tmp_pixel.blue * x_coeff[curr_x_coeff]; ++ curr_x_coeff++; ++ get_pixel(tmp_pixel, *src_p, depth); ++ i++; ++ } ++ curr_pixel.red += tmp_pixel.red * x_coeff[curr_x_coeff]; ++ curr_pixel.green += tmp_pixel.green * x_coeff[curr_x_coeff]; ++ curr_pixel.blue += tmp_pixel.blue * x_coeff[curr_x_coeff]; ++ curr_x_coeff++; ++ curr_pixel.red = (curr_pixel.red + rnd) >> x_shift; ++ curr_pixel.green = (curr_pixel.green + rnd) >> x_shift; ++ curr_pixel.blue = (curr_pixel.blue + rnd) >> x_shift; ++ row_buffer[k].red += curr_pixel.red * y_coeff; ++ row_buffer[k].green += curr_pixel.green * y_coeff; ++ row_buffer[k].blue += curr_pixel.blue * y_coeff; ++ k++; ++ } ++ } ++} ++ ++static inline void ++scale_x_up(int depth, int src_w, unsigned char **src_p, u32 *x_coeff, u32 x_shift, u32 y_coeff, struct pixel *row_buffer) ++{ ++ u32 curr_x_coeff = 1; ++ struct pixel curr_pixel, tmp_pixel; ++ u32 x_array_size = x_coeff[0]; ++ int x_column_num; ++ int i; ++ int l,m; ++ int k = 0; ++ u32 rnd = (1 << (x_shift - 1)); ++ ++ for (i = 0; i < src_w;) { ++ curr_x_coeff = 1; ++ get_pixel(tmp_pixel, *src_p, depth); ++ i++; ++ for (l = 0; l < x_array_size - 1; l++) { ++ x_column_num = x_coeff[curr_x_coeff++]; ++ for (m = 0; m < x_column_num; m++) { ++ row_buffer[k].red += tmp_pixel.red * y_coeff; ++ row_buffer[k].green += tmp_pixel.green * y_coeff; ++ row_buffer[k].blue += tmp_pixel.blue * y_coeff; ++ k++; ++ } ++ curr_pixel.red = tmp_pixel.red * x_coeff[curr_x_coeff]; ++ curr_pixel.green = tmp_pixel.green * x_coeff[curr_x_coeff]; ++ curr_pixel.blue = tmp_pixel.blue * x_coeff[curr_x_coeff]; ++ curr_x_coeff++; ++ get_pixel(tmp_pixel, *src_p, depth); ++ i++; ++ row_buffer[k].red += ((curr_pixel.red + tmp_pixel.red * x_coeff[curr_x_coeff] + rnd) >> x_shift) * y_coeff; ++ row_buffer[k].green += ((curr_pixel.green + tmp_pixel.green * x_coeff[curr_x_coeff] + rnd) >> x_shift) * y_coeff; ++ row_buffer[k].blue += ((curr_pixel.blue + tmp_pixel.blue * x_coeff[curr_x_coeff] + rnd) >> x_shift) * y_coeff; ++ k++; ++ curr_x_coeff++; ++ } ++ for (m = 0; m < x_coeff[curr_x_coeff]; m++) { ++ row_buffer[k].red += tmp_pixel.red * y_coeff; ++ row_buffer[k].green += tmp_pixel.green * y_coeff; ++ row_buffer[k].blue += tmp_pixel.blue * y_coeff; ++ k++; ++ } ++ } ++} ++ ++static int scale_y_down(unsigned char *src, unsigned char *dst, int depth, int src_w, int src_h, int dst_w, int dst_h) ++{ ++ int octpp = (depth + 1) >> 3; ++ int src_x_bytes = octpp * ((src_w + SPLASH_ALIGN) & ~SPLASH_ALIGN); ++ int dst_x_bytes = octpp * ((dst_w + SPLASH_ALIGN) & ~SPLASH_ALIGN); ++ int j; ++ struct pixel *row_buffer; ++ u32 x_shift, y_shift; ++ u32 *x_coeff; ++ u32 *y_coeff; ++ u32 curr_y_coeff = 1; ++ unsigned char *src_p; ++ unsigned char *src_p_line = src; ++ char *dst_p_line; ++ int r,s; ++ int y_array_rows; ++ int y_column_num; ++ int k; ++ u32 rnd; ++ int xup; ++ ++ row_buffer = (struct pixel *)vmalloc(sizeof(struct pixel) * (dst_w + 1)); ++ x_coeff = do_coefficients(src_w, dst_w, &x_shift); ++ y_coeff = do_coefficients(src_h, dst_h, &y_shift); ++ if (!row_buffer || !x_coeff || !y_coeff) { ++ vfree(row_buffer); ++ vfree(x_coeff); ++ vfree(y_coeff); ++ return -ENOMEM; ++ } ++ y_array_rows = y_coeff[0]; ++ rnd = (1 << (y_shift - 1)); ++ xup = (src_w <= dst_w) ? 1 : 0; ++ ++ dst_p_line = dst; ++ ++ for (j = 0; j < src_h;) { ++ curr_y_coeff = 1; ++ for (r = 0; r < y_array_rows; r++) { ++ y_column_num = y_coeff[curr_y_coeff++]; ++ for (k = 0; k < dst_w + 1; k++) ++ row_buffer[k].red = row_buffer[k].green = row_buffer[k].blue = 0; ++ src_p = src_p_line; ++ if (xup) ++ scale_x_up(depth, src_w, &src_p, x_coeff, x_shift, y_coeff[curr_y_coeff], row_buffer ); ++ else ++ scale_x_down(depth, src_w, &src_p, x_coeff, x_shift, y_coeff[curr_y_coeff], row_buffer ); ++ curr_y_coeff++; ++ for (s = 1; s < y_column_num; s++) { ++ src_p = src_p_line = src_p_line + src_x_bytes; ++ j++; ++ if (xup) ++ scale_x_up(depth, src_w, &src_p, x_coeff, x_shift, y_coeff[curr_y_coeff], row_buffer ); ++ else ++ scale_x_down(depth, src_w, &src_p, x_coeff, x_shift, y_coeff[curr_y_coeff], row_buffer ); ++ curr_y_coeff++; ++ } ++ for (k = 0; k < dst_w; k++) { ++ row_buffer[k].red = ( row_buffer[k].red + rnd) >> y_shift; ++ row_buffer[k].green = (row_buffer[k].green + rnd) >> y_shift; ++ row_buffer[k].blue = (row_buffer[k].blue + rnd) >> y_shift; ++ put_pixel (row_buffer[k], dst, depth); ++ } ++ dst = dst_p_line = dst_p_line + dst_x_bytes; ++ } ++ src_p_line = src_p_line + src_x_bytes; ++ j++; ++ } ++ vfree(row_buffer); ++ vfree(x_coeff); ++ vfree(y_coeff); ++ return 0; ++} ++ ++static int scale_y_up(unsigned char *src, unsigned char *dst, int depth, int src_w, int src_h, int dst_w, int dst_h) ++{ ++ int octpp = (depth + 1) >> 3; ++ int src_x_bytes = octpp * ((src_w + SPLASH_ALIGN) & ~SPLASH_ALIGN); ++ int dst_x_bytes = octpp * ((dst_w + SPLASH_ALIGN) & ~SPLASH_ALIGN); ++ int j; ++ u32 x_shift, y_shift; ++ u32 *x_coeff; ++ u32 *y_coeff; ++ struct pixel *row_buf_list[2]; ++ struct pixel *row_buffer; ++ u32 curr_y_coeff = 1; ++ unsigned char *src_p; ++ unsigned char *src_p_line = src; ++ char *dst_p_line; ++ int r,s; ++ int y_array_rows; ++ int y_column_num; ++ int k; ++ u32 rnd; ++ int bi; ++ int xup; ++ int writes; ++ ++ x_coeff = do_coefficients(src_w, dst_w, &x_shift); ++ y_coeff = do_coefficients(src_h, dst_h, &y_shift); ++ row_buf_list[0] = (struct pixel *)vmalloc(2 * sizeof(struct pixel) * (dst_w + 1)); ++ if (!row_buf_list[0] || !x_coeff || !y_coeff) { ++ vfree(row_buf_list[0]); ++ vfree(x_coeff); ++ vfree(y_coeff); ++ return -ENOMEM; ++ } ++ row_buf_list[1] = row_buf_list[0] + (dst_w + 1); ++ ++ y_array_rows = y_coeff[0]; ++ rnd = (1 << (y_shift - 1)); ++ bi = 1; ++ xup = (src_w <= dst_w) ? 1 : 0; ++ writes = 0; ++ ++ dst_p_line = dst; ++ src_p = src_p_line; ++ ++ row_buffer = row_buf_list[0]; ++ ++ for (j = 0; j < src_h;) { ++ memset(row_buf_list[0], 0, 2 * sizeof(struct pixel) * (dst_w + 1)); ++ curr_y_coeff = 1; ++ if (xup) ++ scale_x_up(depth, src_w, &src_p, x_coeff, x_shift, 1, row_buffer ); ++ else ++ scale_x_down(depth, src_w, &src_p, x_coeff, x_shift, 1, row_buffer ); ++ src_p = src_p_line = src_p_line + src_x_bytes; ++ j++; ++ for (r = 0; r < y_array_rows - 1; r++) { ++ struct pixel *old_row_buffer = row_buffer; ++ u32 prev_y_coeff_val; ++ ++ y_column_num = y_coeff[curr_y_coeff]; ++ for (s = 0; s < y_column_num; s++) { ++ for (k = 0; k < dst_w; k++) ++ put_pixel(row_buffer[k], dst, depth); ++ dst = dst_p_line = dst_p_line + dst_x_bytes; ++ writes++; ++ } ++ curr_y_coeff++; ++ row_buffer = row_buf_list[(bi++) % 2]; ++ prev_y_coeff_val = y_coeff[curr_y_coeff++]; ++ if (xup) ++ scale_x_up(depth, src_w, &src_p, x_coeff, x_shift, 1, row_buffer ); ++ else ++ scale_x_down(depth, src_w, &src_p, x_coeff, x_shift, 1, row_buffer ); ++ src_p = src_p_line = src_p_line + src_x_bytes; ++ j++; ++ for (k = 0; k > y_shift; ++ pix.green = (old_row_buffer[k].green * prev_y_coeff_val + row_buffer[k].green * y_coeff[curr_y_coeff] + rnd) >> y_shift; ++ pix.blue = (old_row_buffer[k].blue * prev_y_coeff_val + row_buffer[k].blue * y_coeff[curr_y_coeff] + rnd) >> y_shift; ++ old_row_buffer[k].red = old_row_buffer[k].green = old_row_buffer[k].blue = 0; ++ put_pixel(pix, dst, depth); ++ } ++ dst = dst_p_line = dst_p_line + dst_x_bytes; ++ writes++; ++ curr_y_coeff++; ++ } ++ for (r = 0; r < y_coeff[curr_y_coeff]; r++) { ++ for (k = 0; k < dst_w; k++) { ++ put_pixel(row_buffer[k], dst, depth); ++ } ++ dst = dst_p_line = dst_p_line + dst_x_bytes; ++ writes++; ++ } ++ } ++ vfree(row_buf_list[0]); ++ vfree(x_coeff); ++ vfree(y_coeff); ++ ++ return 0; ++} ++ ++static int jpeg_get(unsigned char *buf, unsigned char *pic, ++ int width, int height, int depth, ++ struct jpeg_decdata *decdata) ++{ ++ int my_width, my_height; ++ int err; ++ ++ jpeg_get_size(buf, &my_width, &my_height); ++ ++ if (my_height != height || my_width != width) { ++ int my_size = ((my_width + 15) & ~15) ++ * ((my_height + 15) & ~15) * ((depth + 1) >> 3); ++ unsigned char *mem = vmalloc(my_size); ++ if (!mem) ++ return 17; ++ if ((err = jpeg_decode(buf, mem, ((my_width + 15) & ~15), ++ ((my_height + 15) & ~15), depth, decdata))) { ++ vfree(mem); ++ return err; ++ } ++ printk(KERN_INFO "bootsplash: scaling image from %dx%d to %dx%d\n", my_width, my_height, width, height); ++ if (my_height <= height) ++ err = scale_y_up(mem, pic, depth, my_width, my_height, ((width + 15) & ~15), ((height + 15) & ~15)); ++ else ++ err = scale_y_down(mem, pic, depth, my_width, my_height, ((width + 15) & ~15), ((height + 15) & ~15)); ++ vfree(mem); ++ if (err < 0) ++ return 17; ++ } else { ++ if ((err = jpeg_decode(buf, pic, ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) ++ return err; ++ } ++ return 0; ++} +--- a/drivers/video/bootsplash/decode-jpg.c ++++ b/drivers/video/bootsplash/decode-jpg.c +@@ -888,9 +888,9 @@ PREC q[][64]; + #define PIC_32(yin, xin, p, xout) \ + ( \ + y = outy[(yin) * 8 + xin], \ +- STORECLAMP(p[(xout) * 4 + 0], y + cr), \ ++ STORECLAMP(p[(xout) * 4 + 0], y + cb), \ + STORECLAMP(p[(xout) * 4 + 1], y - cg), \ +- STORECLAMP(p[(xout) * 4 + 2], y + cb), \ ++ STORECLAMP(p[(xout) * 4 + 2], y + cr), \ + p[(xout) * 4 + 3] = 0 \ + ) + +--- a/drivers/video/bootsplash/render.c ++++ b/drivers/video/bootsplash/render.c +@@ -45,7 +45,7 @@ void splash_putcs(struct vc_data *vc, st + transparent = sd->splash_color == bg_color; + xpos = xpos * vc->vc_font.width + sd->splash_text_xo; + ypos = ypos * vc->vc_font.height + sd->splash_text_yo; +- splashsrc.ub = (u8 *)(info->splash_pic + ypos * info->splash_pic_stride + xpos * octpp); ++ splashsrc.ub = (u8 *)(sd->splash_pic + ypos * sd->splash_pic_stride + xpos * octpp); + dst.ub = (u8 *)(info->screen_base + ypos * info->fix.line_length + xpos * octpp); + fgx = ((u32 *)info->pseudo_palette)[fg_color]; + if (transparent && sd->splash_color == 15) { +@@ -109,10 +109,10 @@ void splash_putcs(struct vc_data *vc, st + } + } + dst.ub += info->fix.line_length - vc->vc_font.width * octpp; +- splashsrc.ub += info->splash_pic_stride - vc->vc_font.width * octpp; ++ splashsrc.ub += sd->splash_pic_stride - vc->vc_font.width * octpp; + } + dst.ub -= info->fix.line_length * vc->vc_font.height - vc->vc_font.width * octpp; +- splashsrc.ub -= info->splash_pic_stride * vc->vc_font.height - vc->vc_font.width * octpp; ++ splashsrc.ub -= sd->splash_pic_stride * vc->vc_font.height - vc->vc_font.width * octpp; + } + } + +@@ -136,7 +136,7 @@ static void splash_renderc(struct fb_inf + sd = info->splash_data; + + transparent = sd->splash_color == bg_color; +- splashsrc.ub = (u8*)(info->splash_pic + ypos * info->splash_pic_stride + xpos * octpp); ++ splashsrc.ub = (u8*)(sd->splash_pic + ypos * sd->splash_pic_stride + xpos * octpp); + dst.ub = (u8*)(info->screen_base + ypos * info->fix.line_length + xpos * octpp); + fgx = ((u32 *)info->pseudo_palette)[fg_color]; + if (transparent && sd->splash_color == 15) { +@@ -197,7 +197,7 @@ static void splash_renderc(struct fb_inf + } + } + dst.ub += info->fix.line_length - width * octpp; +- splashsrc.ub += info->splash_pic_stride - width * octpp; ++ splashsrc.ub += sd->splash_pic_stride - width * octpp; + } + } + +@@ -255,10 +255,11 @@ static void splashset(u8 *dst, int heigh + + static void splashfill(struct fb_info *info, int sy, int sx, int height, int width) { + int octpp = (info->var.bits_per_pixel + 1) >> 3; ++ struct splash_data *sd = info->splash_data; + + splashcopy((u8 *)(info->screen_base + sy * info->fix.line_length + sx * octpp), +- (u8 *)(info->splash_pic + sy * info->splash_pic_stride + sx * octpp), +- height, width, info->fix.line_length, info->splash_pic_stride, ++ (u8 *)(sd->splash_pic + sy * sd->splash_pic_stride + sx * octpp), ++ height, width, info->fix.line_length, sd->splash_pic_stride, + octpp); + } + +@@ -442,6 +443,7 @@ void splash_bmove_redraw(struct vc_data + void splash_blank(struct vc_data *vc, struct fb_info *info, int blank) + { + SPLASH_DEBUG(); ++ + if (blank) { + splashset((u8 *)info->screen_base, + info->var.yres, info->var.xres, +--- a/drivers/video/console/fbcon.h ++++ b/drivers/video/console/fbcon.h +@@ -34,8 +34,10 @@ struct splash_data { + int splash_height; /* height of image */ + int splash_text_xo; /* text area origin */ + int splash_text_yo; +- int splash_text_wi; /* text area size */ ++ int splash_text_wi; /* text area size used*/ + int splash_text_he; ++ int splash_jpg_text_wi; /* text area size of jpeg*/ ++ int splash_jpg_text_he; + int splash_showtext; /* silent/verbose mode */ + int splash_boxcount; + int splash_percent; +@@ -45,12 +47,19 @@ struct splash_data { + unsigned char *splash_boxes; + unsigned char *splash_jpeg; /* jpeg */ + unsigned char *splash_palette; /* palette for 8-bit */ ++ int splash_boxes_xoff; ++ int splash_boxes_yoff; + + int splash_dosilent; /* show silent jpeg */ + unsigned char *splash_silentjpeg; + unsigned char *splash_sboxes; + int splash_sboxcount; + struct splash_data *next; ++ int splash_sboxes_xoff; ++ int splash_sboxes_yoff; ++ int splash_pic_stride; ++ unsigned char *splash_pic; ++ int splash_pic_size; + }; + #endif + +--- a/include/linux/fb.h ++++ b/include/linux/fb.h +@@ -861,9 +861,6 @@ struct fb_info { + void *par; + #ifdef CONFIG_BOOTSPLASH + struct splash_data *splash_data; +- unsigned char *splash_pic; +- int splash_pic_size; +- int splash_pic_stride; + char fb_cursordata[64]; + #endif + /* we need the PCI or similiar aperture base/size not diff --git a/patches.suse/cgroup-disable-memory.patch b/patches.suse/cgroup-disable-memory.patch new file mode 100644 index 0000000..18db6f0 --- /dev/null +++ b/patches.suse/cgroup-disable-memory.patch @@ -0,0 +1,89 @@ +From: Balbir Singh +Date: Thu, 01 May 2008 02:48:58 -0700 +Subject: memcg: disable the memory controller by default +Patch-mainline: not yet + +Due to the overhead of the memory controller the memory controller is now +disabled by default. This patch adds cgroup_enable. + +[akpm@linux-foundation.org: `inline __init' doesn't make sense] +Signed-off-by: Balbir Singh +Cc: AMAMOTO Takashi +Acked-by: Paul Menage +Cc: Pavel Emelianov +Acked-by: KAMEZAWA Hiroyuki +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Jiri Slaby +--- + + Documentation/kernel-parameters.txt | 3 +++ + kernel/cgroup.c | 17 +++++++++++++---- + mm/memcontrol.c | 1 + + 3 files changed, 17 insertions(+), 4 deletions(-) + +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -449,8 +449,11 @@ and is between 256 and 4096 characters. + See Documentation/s390/CommonIO for details. + + cgroup_disable= [KNL] Disable a particular controller ++ cgroup_enable= [KNL] Enable a particular controller ++ For both cgroup_enable and cgroup_enable + Format: {name of the controller(s) to disable} + {Currently supported controllers - "memory"} ++ {Memory controller is disabled by default} + + checkreqprot [SELINUX] Set initial checkreqprot flag value. + Format: { "0" | "1" } +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -4394,7 +4394,7 @@ static void cgroup_release_agent(struct + mutex_unlock(&cgroup_mutex); + } + +-static int __init cgroup_disable(char *str) ++static int __init cgroup_turnonoff(char *str, int disable) + { + int i; + char *token; +@@ -4410,17 +4410,26 @@ static int __init cgroup_disable(char *s + struct cgroup_subsys *ss = subsys[i]; + + if (!strcmp(token, ss->name)) { +- ss->disabled = 1; +- printk(KERN_INFO "Disabling %s control group" +- " subsystem\n", ss->name); ++ ss->disabled = disable; + break; + } + } + } + return 1; + } ++ ++static int __init cgroup_disable(char *str) ++{ ++ return cgroup_turnonoff(str, 1); ++} + __setup("cgroup_disable=", cgroup_disable); + ++static int __init cgroup_enable(char *str) ++{ ++ return cgroup_turnonoff(str, 0); ++} ++__setup("cgroup_enable=", cgroup_enable); ++ + /* + * Functons for CSS ID. + */ +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -4343,6 +4343,7 @@ struct cgroup_subsys mem_cgroup_subsys = + .attach = mem_cgroup_move_task, + .early_init = 0, + .use_id = 1, ++ .disabled = 1, + }; + + #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP diff --git a/patches.suse/connector-read-mostly b/patches.suse/connector-read-mostly new file mode 100644 index 0000000..ceaf851 --- /dev/null +++ b/patches.suse/connector-read-mostly @@ -0,0 +1,23 @@ +From: Chris Mason +Subject: Make proc_event_num_listeners __read_mostly +Patch-mainline: not yet + +This will lower the fast path costs of the userland connector code. + +Acked-by: Jeff Mahoney + +--- + drivers/connector/cn_proc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/connector/cn_proc.c ++++ b/drivers/connector/cn_proc.c +@@ -34,7 +34,7 @@ + + #define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event)) + +-static atomic_t proc_event_num_listeners = ATOMIC_INIT(0); ++static atomic_t proc_event_num_listeners __read_mostly = ATOMIC_INIT(0); + static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC }; + + /* proc_event_counts is used as the sequence number of the netlink message */ diff --git a/patches.suse/crasher-26.diff b/patches.suse/crasher-26.diff new file mode 100644 index 0000000..77b5555 --- /dev/null +++ b/patches.suse/crasher-26.diff @@ -0,0 +1,264 @@ +From: Chris Mason +Subject: slab testing module +Patch-mainline: probably never + +--- + drivers/char/Kconfig | 5 + + drivers/char/Makefile | 1 + drivers/char/crasher.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 234 insertions(+) + +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -1113,5 +1113,10 @@ config DEVPORT + + source "drivers/s390/char/Kconfig" + ++config CRASHER ++ tristate "Crasher Module" ++ help ++ Slab cache memory tester. Only use this as a module ++ + endmenu + +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -105,6 +105,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ + + obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o + obj-$(CONFIG_TCG_TPM) += tpm/ ++obj-$(CONFIG_CRASHER) += crasher.o + + obj-$(CONFIG_PS3_FLASH) += ps3flash.o + +--- /dev/null ++++ b/drivers/char/crasher.c +@@ -0,0 +1,228 @@ ++/* ++ * crasher.c, it breaks things ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int module_exiting; ++static struct completion startup = COMPLETION_INITIALIZER(startup); ++static unsigned long rand_seed = 152L; ++static unsigned long seed = 152L; ++static int threads = 1; ++static int call_panic, call_bug, call_warn; ++static int trap_null, call_null, jump_null; ++static long trap_read, trap_write, call_bad, jump_bad; ++ ++module_param(seed, ulong, 0); ++module_param(call_panic, bool, 0); ++module_param(call_bug, bool, 0); ++module_param(call_warn, bool, 0); ++module_param(trap_null, bool, 0); ++module_param(trap_read, long, 0); ++module_param(trap_write, long, 0); ++module_param(call_null, bool, 0); ++module_param(call_bad, long, 0); ++module_param(jump_null, bool, 0); ++module_param(jump_bad, long, 0); ++module_param(threads, int, 0); ++MODULE_PARM_DESC(seed, "random seed for memory tests"); ++MODULE_PARM_DESC(call_panic, "test option. call panic() and render the system unusable."); ++MODULE_PARM_DESC(call_bug, "test option. call BUG() and render the system unusable."); ++MODULE_PARM_DESC(call_warn, "test option. call WARN() and leave the system usable."); ++MODULE_PARM_DESC(trap_null, "test option. dereference a NULL pointer to simulate a crash and render the system unusable."); ++MODULE_PARM_DESC(trap_read, "test option. read from an invalid address to simulate a crash and render the system unusable."); ++MODULE_PARM_DESC(trap_write, "test option. write to an invalid address to simulate a crash and render the system unusable."); ++MODULE_PARM_DESC(call_null, "test option. call a NULL pointer to simulate a crash and render the system unusable."); ++MODULE_PARM_DESC(call_bad, "test option. call an invalid address to simulate a crash and render the system unusable."); ++MODULE_PARM_DESC(jump_null, "test option. jump to a NULL pointer to simulate a crash and render the system unusable."); ++MODULE_PARM_DESC(jump_bad, "test option. jump to an invalid address to simulate a crash and render the system unusable."); ++MODULE_PARM_DESC(threads, "number of threads to run"); ++MODULE_LICENSE("GPL"); ++ ++#define NUM_ALLOC 24 ++#define NUM_SIZES 8 ++static int sizes[] = { 32, 64, 128, 192, 256, 1024, 2048, 4096 }; ++ ++struct mem_buf { ++ char *buf; ++ int size; ++}; ++ ++static unsigned long crasher_random(void) ++{ ++ rand_seed = rand_seed*69069L+1; ++ return rand_seed^jiffies; ++} ++ ++void crasher_srandom(unsigned long entropy) ++{ ++ rand_seed ^= entropy; ++ crasher_random(); ++} ++ ++static char *mem_alloc(int size) { ++ char *p = kmalloc(size, GFP_KERNEL); ++ int i; ++ if (!p) ++ return p; ++ for (i = 0 ; i < size; i++) ++ p[i] = (i % 119) + 8; ++ return p; ++} ++ ++static void mem_check(char *p, int size) { ++ int i; ++ if (!p) ++ return; ++ for (i = 0 ; i < size; i++) { ++ if (p[i] != ((i % 119) + 8)) { ++ printk(KERN_CRIT "verify error at %lX offset %d " ++ " wanted %d found %d size %d\n", ++ (unsigned long)(p + i), i, (i % 119) + 8, ++ p[i], size); ++ } ++ } ++ // try and trigger slab poisoning for people using this buffer ++ // wrong ++ memset(p, 0, size); ++} ++ ++static void mem_verify(void) { ++ struct mem_buf bufs[NUM_ALLOC]; ++ struct mem_buf *b; ++ int index; ++ int size; ++ unsigned long sleep; ++ memset(bufs, 0, sizeof(struct mem_buf) * NUM_ALLOC); ++ while(!module_exiting) { ++ index = crasher_random() % NUM_ALLOC; ++ b = bufs + index; ++ if (b->size) { ++ mem_check(b->buf, b->size); ++ kfree(b->buf); ++ b->buf = NULL; ++ b->size = 0; ++ } else { ++ size = crasher_random() % NUM_SIZES; ++ size = sizes[size]; ++ b->buf = mem_alloc(size); ++ b->size = size; ++ } ++ sleep = crasher_random() % (HZ / 10); ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(sleep); ++ set_current_state(TASK_RUNNING); ++ } ++ for (index = 0 ; index < NUM_ALLOC ; index++) { ++ b = bufs + index; ++ if (b->size) { ++ mem_check(b->buf, b->size); ++ kfree(b->buf); ++ } ++ } ++} ++ ++static int crasher_thread(void *unused) ++{ ++ daemonize("crasher"); ++ complete(&startup); ++ mem_verify(); ++ complete(&startup); ++ return 0; ++} ++ ++static int __init crasher_init(void) ++{ ++ int i; ++ init_completion(&startup); ++ crasher_srandom(seed); ++ ++ if (call_panic) { ++ panic("test panic from crasher module. Good Luck.\n"); ++ return -EFAULT; ++ } ++ if (call_bug) { ++ printk("triggering BUG\n"); ++ BUG_ON(1); ++ return -EFAULT; ++ } ++ if (WARN(call_warn, "triggering WARN\n")) ++ return -EFAULT; ++ ++ if (trap_null) { ++ volatile char *p = NULL; ++ printk("dereferencing NULL pointer.\n"); ++ p[0] = '\n'; ++ return -EFAULT; ++ } ++ if (trap_read) { ++ const volatile char *p = (char *)trap_read; ++ printk("reading from invalid(?) address %p.\n", p); ++ return p[0] ? -EFAULT : -EACCES; ++ } ++ if (trap_write) { ++ volatile char *p = (char *)trap_write; ++ printk("writing to invalid(?) address %p.\n", p); ++ p[0] = ' '; ++ return -EFAULT; ++ } ++ ++ if (call_null) { ++ void(*f)(void) = NULL; ++ printk("calling NULL pointer.\n"); ++ f(); ++ return -EFAULT; ++ } ++ if (call_bad) { ++ void(*f)(void) = (void(*)(void))call_bad; ++ printk("calling invalid(?) address %p.\n", f); ++ f(); ++ return -EFAULT; ++ } ++ ++ /* These two depend on the compiler doing tail call optimization. */ ++ if (jump_null) { ++ int(*f)(void) = NULL; ++ printk("jumping to NULL.\n"); ++ return f(); ++ } ++ if (jump_bad) { ++ int(*f)(void) = (int(*)(void))jump_bad; ++ printk("jumping to invalid(?) address %p.\n", f); ++ return f(); ++ } ++ ++ printk("crasher module (%d threads). Testing sizes: ", threads); ++ for (i = 0 ; i < NUM_SIZES ; i++) ++ printk("%d ", sizes[i]); ++ printk("\n"); ++ ++ for (i = 0 ; i < threads ; i++) ++ kernel_thread(crasher_thread, crasher_thread, ++ CLONE_FS | CLONE_FILES); ++ for (i = 0 ; i < threads ; i++) ++ wait_for_completion(&startup); ++ return 0; ++} ++ ++static void __exit crasher_exit(void) ++{ ++ int i; ++ module_exiting = 1; ++ for (i = 0 ; i < threads ; i++) ++ wait_for_completion(&startup); ++ printk("all crasher threads done\n"); ++ return; ++} ++ ++module_init(crasher_init); ++module_exit(crasher_exit); diff --git a/patches.suse/dm-emulate-blkrrpart-ioctl b/patches.suse/dm-emulate-blkrrpart-ioctl new file mode 100644 index 0000000..16e84ff --- /dev/null +++ b/patches.suse/dm-emulate-blkrrpart-ioctl @@ -0,0 +1,51 @@ +From: Hannes Reinecke +Subject: Emulate BLKRRPART on device-mapper +Patch-mainline: not yet + +Partitions on device-mapper devices are managed by kpartx (if at +all). So if we were just to send out a 'change' event if someone +called BLKRRPART on these devices, kpartx will be triggered via udev +and can manage the partitions accordingly. + +Signed-off-by: Hannes Reinecke + +--- + drivers/md/dm.c | 22 ++++++++++++++-------- + 1 file changed, 14 insertions(+), 8 deletions(-) + +--- a/drivers/md/dm.c ++++ b/drivers/md/dm.c +@@ -409,19 +409,25 @@ static int dm_blk_ioctl(struct block_dev + if (!map || !dm_table_get_size(map)) + goto out; + +- /* We only support devices that have a single target */ +- if (dm_table_get_num_targets(map) != 1) +- goto out; +- +- tgt = dm_table_get_target(map, 0); +- + if (dm_suspended_md(md)) { + r = -EAGAIN; + goto out; + } + +- if (tgt->type->ioctl) +- r = tgt->type->ioctl(tgt, cmd, arg); ++ if (cmd == BLKRRPART) { ++ /* Emulate Re-read partitions table */ ++ kobject_uevent(&disk_to_dev(md->disk)->kobj, KOBJ_CHANGE); ++ r = 0; ++ } else { ++ /* We only support devices that have a single target */ ++ if (dm_table_get_num_targets(map) != 1) ++ goto out; ++ ++ tgt = dm_table_get_target(map, 0); ++ ++ if (tgt->type->ioctl) ++ r = tgt->type->ioctl(tgt, cmd, arg); ++ } + + out: + dm_table_put(map); diff --git a/patches.suse/dm-mpath-accept-failed-paths b/patches.suse/dm-mpath-accept-failed-paths new file mode 100644 index 0000000..7494f1d --- /dev/null +++ b/patches.suse/dm-mpath-accept-failed-paths @@ -0,0 +1,224 @@ +From 950e1951d452d08a5bd95d82d4cad7fa97fa4464 Mon Sep 17 00:00:00 2001 +From: Hannes Reinecke +Date: Thu, 19 Nov 2009 13:54:56 +0100 +Subject: Accept failed paths for multipath maps +References: bnc#458037,bnc#458393 +Patch-mainline: Not yet + +The multipath kernel module is rejecting any map with an invalid +device. However, as the multipathd is processing the events serially +it will try to push a map with invalid devices if more than one +device failed at the same time. +So we can as well accept those maps and make sure to mark the +paths as down. + +Signed-off-by: Hannes Reinecke +--- +--- + drivers/md/dm-mpath.c | 71 ++++++++++++++++++++++++++++++++++++++++---------- + drivers/md/dm-mpath.h | 1 + drivers/md/dm-table.c | 7 +++- + 3 files changed, 64 insertions(+), 15 deletions(-) + +--- a/drivers/md/dm-mpath.c ++++ b/drivers/md/dm-mpath.c +@@ -146,7 +146,8 @@ static void deactivate_path(struct work_ + struct pgpath *pgpath = + container_of(work, struct pgpath, deactivate_path); + +- blk_abort_queue(pgpath->path.dev->bdev->bd_disk->queue); ++ if (pgpath->path.dev) ++ blk_abort_queue(pgpath->path.dev->bdev->bd_disk->queue); + } + + static struct priority_group *alloc_priority_group(void) +@@ -275,6 +276,11 @@ static int __choose_path_in_pg(struct mu + + m->current_pgpath = path_to_pgpath(path); + ++ if (!m->current_pgpath->path.dev) { ++ m->current_pgpath = NULL; ++ return -ENODEV; ++ } ++ + if (m->current_pg != pg) + __switch_pg(m, m->current_pgpath); + +@@ -593,6 +599,7 @@ static struct pgpath *parse_path(struct + { + int r; + struct pgpath *p; ++ char *path; + struct multipath *m = ti->private; + + /* we need at least a path arg */ +@@ -605,14 +612,37 @@ static struct pgpath *parse_path(struct + if (!p) + return ERR_PTR(-ENOMEM); + +- r = dm_get_device(ti, shift(as), dm_table_get_mode(ti->table), ++ path = shift(as); ++ r = dm_get_device(ti, path, dm_table_get_mode(ti->table), + &p->path.dev); + if (r) { +- ti->error = "error getting device"; +- goto bad; ++ unsigned major, minor; ++ ++ /* Try to add a failed device */ ++ if (r == -ENXIO && sscanf(path, "%u:%u", &major, &minor) == 2) { ++ dev_t dev; ++ ++ /* Extract the major/minor numbers */ ++ dev = MKDEV(major, minor); ++ if (MAJOR(dev) != major || MINOR(dev) != minor) { ++ /* Nice try, didn't work */ ++ DMWARN("Invalid device path %s", path); ++ ti->error = "error converting devnum"; ++ goto bad; ++ } ++ DMWARN("adding disabled device %d:%d", major, minor); ++ p->path.dev = NULL; ++ format_dev_t(p->path.pdev, dev); ++ p->is_active = 0; ++ } else { ++ ti->error = "error getting device"; ++ goto bad; ++ } ++ } else { ++ memcpy(p->path.pdev, p->path.dev->name, 16); + } + +- if (m->hw_handler_name) { ++ if (m->hw_handler_name && p->path.dev) { + struct request_queue *q = bdev_get_queue(p->path.dev->bdev); + + r = scsi_dh_attach(q, m->hw_handler_name); +@@ -649,6 +679,11 @@ static struct pgpath *parse_path(struct + goto bad; + } + ++ if (!p->is_active) { ++ ps->type->fail_path(ps, &p->path); ++ p->fail_count++; ++ m->nr_valid_paths--; ++ } + return p; + + bad: +@@ -976,7 +1011,7 @@ static int fail_path(struct pgpath *pgpa + if (!pgpath->is_active) + goto out; + +- DMWARN("Failing path %s.", pgpath->path.dev->name); ++ DMWARN("Failing path %s.", pgpath->path.pdev); + + pgpath->pg->ps.type->fail_path(&pgpath->pg->ps, &pgpath->path); + pgpath->is_active = 0; +@@ -988,7 +1023,7 @@ static int fail_path(struct pgpath *pgpa + m->current_pgpath = NULL; + + dm_path_uevent(DM_UEVENT_PATH_FAILED, m->ti, +- pgpath->path.dev->name, m->nr_valid_paths); ++ pgpath->path.pdev, m->nr_valid_paths); + + schedule_work(&m->trigger_event); + queue_work(kmultipathd, &pgpath->deactivate_path); +@@ -1013,6 +1048,12 @@ static int reinstate_path(struct pgpath + if (pgpath->is_active) + goto out; + ++ if (!pgpath->path.dev) { ++ DMWARN("Cannot reinstate disabled path %s", pgpath->path.pdev); ++ r = -ENODEV; ++ goto out; ++ } ++ + if (!pgpath->pg->ps.type->reinstate_path) { + DMWARN("Reinstate path not supported by path selector %s", + pgpath->pg->ps.type->name); +@@ -1035,7 +1076,7 @@ static int reinstate_path(struct pgpath + } + + dm_path_uevent(DM_UEVENT_PATH_REINSTATED, m->ti, +- pgpath->path.dev->name, m->nr_valid_paths); ++ pgpath->path.pdev, m->nr_valid_paths); + + schedule_work(&m->trigger_event); + +@@ -1055,6 +1096,9 @@ static int action_dev(struct multipath * + struct pgpath *pgpath; + struct priority_group *pg; + ++ if (!dev) ++ return 0; ++ + list_for_each_entry(pg, &m->priority_groups, list) { + list_for_each_entry(pgpath, &pg->pgpaths, list) { + if (pgpath->path.dev == dev) +@@ -1239,8 +1283,9 @@ static void activate_path(struct work_st + struct pgpath *pgpath = + container_of(work, struct pgpath, activate_path); + +- scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev), +- pg_init_done, pgpath); ++ if (pgpath->path.dev) ++ scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev), ++ pg_init_done, pgpath); + } + + /* +@@ -1415,7 +1460,7 @@ static int multipath_status(struct dm_ta + pg->ps.type->info_args); + + list_for_each_entry(p, &pg->pgpaths, list) { +- DMEMIT("%s %s %u ", p->path.dev->name, ++ DMEMIT("%s %s %u ", p->path.pdev, + p->is_active ? "A" : "F", + p->fail_count); + if (pg->ps.type->status) +@@ -1441,7 +1486,7 @@ static int multipath_status(struct dm_ta + pg->ps.type->table_args); + + list_for_each_entry(p, &pg->pgpaths, list) { +- DMEMIT("%s ", p->path.dev->name); ++ DMEMIT("%s ", p->path.pdev); + if (pg->ps.type->status) + sz += pg->ps.type->status(&pg->ps, + &p->path, type, result + sz, +@@ -1533,7 +1578,7 @@ static int multipath_ioctl(struct dm_tar + if (!m->current_pgpath) + __choose_pgpath(m, 0); + +- if (m->current_pgpath) { ++ if (m->current_pgpath && m->current_pgpath->path.dev) { + bdev = m->current_pgpath->path.dev->bdev; + mode = m->current_pgpath->path.dev->mode; + } +--- a/drivers/md/dm-mpath.h ++++ b/drivers/md/dm-mpath.h +@@ -12,6 +12,7 @@ + struct dm_dev; + + struct dm_path { ++ char pdev[16]; /* Requested physical device */ + struct dm_dev *dev; /* Read-only */ + void *pscontext; /* For path-selector use */ + }; +--- a/drivers/md/dm-table.c ++++ b/drivers/md/dm-table.c +@@ -538,9 +538,12 @@ int dm_get_device(struct dm_target *ti, + */ + void dm_put_device(struct dm_target *ti, struct dm_dev *d) + { +- struct dm_dev_internal *dd = container_of(d, struct dm_dev_internal, +- dm_dev); ++ struct dm_dev_internal *dd; + ++ if (!d) ++ return; ++ ++ dd = container_of(d, struct dm_dev_internal, dm_dev); + if (atomic_dec_and_test(&dd->count)) { + close_dev(dd, ti->table->md); + list_del(&dd->list); diff --git a/patches.suse/dm-mpath-detach-existing-hardware-handler b/patches.suse/dm-mpath-detach-existing-hardware-handler new file mode 100644 index 0000000..1429f05 --- /dev/null +++ b/patches.suse/dm-mpath-detach-existing-hardware-handler @@ -0,0 +1,59 @@ +From 27d169318cbd6c8647f689e8dcff08920040408a Mon Sep 17 00:00:00 2001 +From: Hannes Reinecke +Date: Thu, 19 Nov 2009 14:39:24 +0100 +Subject: [PATCH] multipath: detach existing hardware handler if none was specified +Patch-mainline: not yet + +When no hardware handler was specified in the multipath configuration +we should be detaching any existing ones. Otherwise unpredictable +results will happen. + +Signed-off-by: Hannes Reinecke +--- + drivers/md/dm-mpath.c | 32 ++++++++++++++++++-------------- + 1 file changed, 18 insertions(+), 14 deletions(-) + +--- a/drivers/md/dm-mpath.c ++++ b/drivers/md/dm-mpath.c +@@ -642,23 +642,27 @@ static struct pgpath *parse_path(struct + memcpy(p->path.pdev, p->path.dev->name, 16); + } + +- if (m->hw_handler_name && p->path.dev) { ++ if (p->path.dev) { + struct request_queue *q = bdev_get_queue(p->path.dev->bdev); + +- r = scsi_dh_attach(q, m->hw_handler_name); +- if (r == -EBUSY) { +- /* +- * Already attached to different hw_handler, +- * try to reattach with correct one. +- */ +- scsi_dh_detach(q); ++ if (m->hw_handler_name) { + r = scsi_dh_attach(q, m->hw_handler_name); +- } +- +- if (r < 0) { +- ti->error = "error attaching hardware handler"; +- dm_put_device(ti, p->path.dev); +- goto bad; ++ if (r == -EBUSY) { ++ /* ++ * Already attached to different hw_handler, ++ * try to reattach with correct one. ++ */ ++ scsi_dh_detach(q); ++ r = scsi_dh_attach(q, m->hw_handler_name); ++ } ++ if (r < 0) { ++ ti->error = "error attaching hardware handler"; ++ dm_put_device(ti, p->path.dev); ++ goto bad; ++ } ++ } else { ++ /* Play safe and detach hardware handler */ ++ scsi_dh_detach(q); + } + + if (m->hw_handler_params) { diff --git a/patches.suse/dm-mpath-evaluate-request-result-and-sense b/patches.suse/dm-mpath-evaluate-request-result-and-sense new file mode 100644 index 0000000..e518e22 --- /dev/null +++ b/patches.suse/dm-mpath-evaluate-request-result-and-sense @@ -0,0 +1,158 @@ +From: Hannes Reinecke +Subject: multipath: Evaluate request result and sense code +References: FATE#303695,bnc#433920,bnc#442001 +Patch-mainline: not yet + +Currently we're updating the request result upon completion +only for BLK_PC requests. This makes it impossible for the +upper layers to reliable detect the real cause for an +I/O failure. By attaching the result and the sense to all +requests we can update multipathing to make some more elaborate +choices on how to handle I/O errors. +This also solves a potential data corruption with multipathing +and persistent reservations. When queue_if_no_path is active +multipath will queue any I/O failure (including those failed +with RESERVATION CONFLICT) until the reservation status changes. +But by then I/O might have been ongoing on the other paths, +thus the delayed submission will severely corrupt your data. + +Signed-off-by: Hannes Reinecke + +--- + drivers/md/dm-mpath.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/scsi/scsi_lib.c | 28 +++++++++++--------------- + 2 files changed, 63 insertions(+), 16 deletions(-) + +--- a/drivers/md/dm-mpath.c ++++ b/drivers/md/dm-mpath.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + + #define DM_MSG_PREFIX "multipath" +@@ -104,6 +105,7 @@ struct multipath { + struct dm_mpath_io { + struct pgpath *pgpath; + size_t nr_bytes; ++ char sense[SCSI_SENSE_BUFFERSIZE]; + }; + + typedef int (*action_fn) (struct pgpath *pgpath); +@@ -995,6 +997,9 @@ static int multipath_map(struct dm_targe + + map_context->ptr = mpio; + clone->cmd_flags |= REQ_FAILFAST_TRANSPORT; ++ /* Always attach a sense buffer */ ++ if (!clone->sense) ++ clone->sense = mpio->sense; + r = map_io(m, clone, mpio, 0); + if (r < 0 || r == DM_MAPIO_REQUEUE) + mempool_free(mpio, m->mpio_pool); +@@ -1293,6 +1298,44 @@ static void activate_path(struct work_st + } + + /* ++ * Evaluate scsi return code ++ */ ++static int eval_scsi_error(int result, char *sense, int sense_len) ++{ ++ struct scsi_sense_hdr sshdr; ++ int r = DM_ENDIO_REQUEUE; ++ ++ if (host_byte(result) != DID_OK) ++ return r; ++ ++ if (msg_byte(result) != COMMAND_COMPLETE) ++ return r; ++ ++ if (status_byte(result) == RESERVATION_CONFLICT) ++ /* Do not retry here, possible data corruption */ ++ return -EIO; ++ ++#if defined(CONFIG_SCSI) || defined(CONFIG_SCSI_MODULE) ++ if (status_byte(result) == CHECK_CONDITION && ++ !scsi_normalize_sense(sense, sense_len, &sshdr)) { ++ ++ switch (sshdr.sense_key) { ++ case MEDIUM_ERROR: ++ case DATA_PROTECT: ++ case BLANK_CHECK: ++ case COPY_ABORTED: ++ case VOLUME_OVERFLOW: ++ case MISCOMPARE: ++ r = -EIO; ++ break; ++ } ++ } ++#endif ++ ++ return r; ++} ++ ++/* + * end_io handling + */ + static int do_end_io(struct multipath *m, struct request *clone, +@@ -1318,6 +1361,10 @@ static int do_end_io(struct multipath *m + if (error == -EOPNOTSUPP) + return error; + ++ r = eval_scsi_error(clone->errors, clone->sense, clone->sense_len); ++ if (r != DM_ENDIO_REQUEUE) ++ return r; ++ + if (mpio->pgpath) + fail_path(mpio->pgpath); + +@@ -1344,6 +1391,10 @@ static int multipath_end_io(struct dm_ta + if (ps->type->end_io) + ps->type->end_io(ps, &pgpath->path, mpio->nr_bytes); + } ++ if (clone->sense == mpio->sense) { ++ clone->sense = NULL; ++ clone->sense_len = 0; ++ } + mempool_free(mpio, m->mpio_pool); + + return r; +--- a/drivers/scsi/scsi_lib.c ++++ b/drivers/scsi/scsi_lib.c +@@ -722,23 +722,19 @@ void scsi_io_completion(struct scsi_cmnd + sense_deferred = scsi_sense_is_deferred(&sshdr); + } + +- if (blk_pc_request(req)) { /* SG_IO ioctl from block level */ +- req->errors = result; +- if (result) { +- if (sense_valid && req->sense) { +- /* +- * SG_IO wants current and deferred errors +- */ +- int len = 8 + cmd->sense_buffer[7]; ++ req->errors = result; ++ if (sense_valid && req->sense) { ++ int len = 8 + cmd->sense_buffer[7]; ++ ++ if (len > SCSI_SENSE_BUFFERSIZE) ++ len = SCSI_SENSE_BUFFERSIZE; ++ memcpy(req->sense, cmd->sense_buffer, len); ++ req->sense_len = len; ++ } + +- if (len > SCSI_SENSE_BUFFERSIZE) +- len = SCSI_SENSE_BUFFERSIZE; +- memcpy(req->sense, cmd->sense_buffer, len); +- req->sense_len = len; +- } +- if (!sense_deferred) +- error = -EIO; +- } ++ if (blk_pc_request(req)) { /* SG_IO ioctl from block level */ ++ if ((result) && (!sense_deferred)) ++ error = -EIO; + + req->resid_len = scsi_get_resid(cmd); + diff --git a/patches.suse/dm-mpath-leastpending-path-update b/patches.suse/dm-mpath-leastpending-path-update new file mode 100644 index 0000000..78bd6cd --- /dev/null +++ b/patches.suse/dm-mpath-leastpending-path-update @@ -0,0 +1,301 @@ +Subject: Update least-pending-IO dynamic load balancer +From: Hannes Reinecke +Date: Wed Jan 7 09:26:30 2009 +0100: +References: bnc#444199 +Patch-mainline: not yet + +Attached patch provides "Least pending IO" dynamic load balancing policy for +bio based device mapper multipath. This load balancing policy considers the +number of unserviced requests pending on a path and selects the path with least +count for pending service request. + +We find this policy more useful especially when the SAN environment has +heterogeneous components. Ex, when there is one 8GB HBA and one 2GB HBA +connected to the same server, 8GB HBA could be utilized better with this +algorithm. + +This patch includes the update as posted in the bugzilla, +based on the review comments received in the dm-devel mailing list. + +Signed-off-by: Sakshi Chaitanya Veni +Signed-off-by: Vijayakumar Balasubramanian +Signed-off-by: Senthil Kumar V +Signed-off-by: Hannes Reinecke + +--- + drivers/md/Makefile | 2 + drivers/md/dm-least-pending.c | 258 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 259 insertions(+), 1 deletion(-) + +--- a/drivers/md/Makefile ++++ b/drivers/md/Makefile +@@ -37,7 +37,7 @@ obj-$(CONFIG_BLK_DEV_MD) += md-mod.o + obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o + obj-$(CONFIG_DM_CRYPT) += dm-crypt.o + obj-$(CONFIG_DM_DELAY) += dm-delay.o +-obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o ++obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o dm-least-pending.o + obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o + obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o + obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o +--- /dev/null ++++ b/drivers/md/dm-least-pending.c +@@ -0,0 +1,258 @@ ++/* ++ * (C) Copyright 2008 Hewlett-Packard Development Company, L.P ++ * ++ * This file is released under the GPL. ++ */ ++ ++#include "dm-path-selector.h" ++ ++#include ++ ++#define DM_MSG_PREFIX "multipath least-pending" ++ ++/*----------------------------------------------------------------- ++* Path-handling code, paths are held in lists ++*---------------------------------------------------------------*/ ++struct path_info { ++ struct list_head list; ++ struct dm_path *path; ++ unsigned repeat_count; ++ atomic_t io_count; ++}; ++ ++static void free_paths(struct list_head *paths) ++{ ++ struct path_info *pi, *next; ++ ++ list_for_each_entry_safe(pi, next, paths, list) { ++ list_del(&pi->list); ++ kfree(pi); ++ } ++} ++ ++/*----------------------------------------------------------------- ++ * Least-pending selector ++ *---------------------------------------------------------------*/ ++ ++#define LPP_MIN_IO 1 ++ ++struct selector { ++ struct list_head valid_paths; ++ struct list_head invalid_paths; ++}; ++ ++static struct selector *alloc_selector(void) ++{ ++ struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL); ++ ++ if (s) { ++ INIT_LIST_HEAD(&s->valid_paths); ++ INIT_LIST_HEAD(&s->invalid_paths); ++ } ++ ++ return s; ++} ++ ++static int lpp_create(struct path_selector *ps, unsigned argc, char **argv) ++{ ++ struct selector *s; ++ ++ s = alloc_selector(); ++ if (!s) ++ return -ENOMEM; ++ ++ ps->context = s; ++ return 0; ++} ++ ++static void lpp_destroy(struct path_selector *ps) ++{ ++ struct selector *s = ps->context; ++ ++ free_paths(&s->valid_paths); ++ free_paths(&s->invalid_paths); ++ kfree(s); ++ ps->context = NULL; ++} ++ ++static int lpp_status(struct path_selector *ps, struct dm_path *path, ++ status_type_t type, char *result, unsigned int maxlen) ++{ ++ struct path_info *pi; ++ int sz = 0; ++ ++ if (!path) ++ switch (type) { ++ case STATUSTYPE_INFO: ++ DMEMIT("1 "); ++ break; ++ case STATUSTYPE_TABLE: ++ DMEMIT("0 "); ++ break; ++ } ++ else { ++ pi = path->pscontext; ++ switch (type) { ++ case STATUSTYPE_INFO: ++ DMEMIT("%u:%u ", pi->repeat_count, ++ atomic_read(&pi->io_count)); ++ break; ++ case STATUSTYPE_TABLE: ++ break; ++ } ++ } ++ ++ return sz; ++} ++ ++/* ++ * Called during initialisation to register each path with an ++ * optional repeat_count. ++ */ ++static int lpp_add_path(struct path_selector *ps, struct dm_path *path, ++ int argc, char **argv, char **error) ++{ ++ struct selector *s = ps->context; ++ struct path_info *pi; ++ unsigned repeat_count = LPP_MIN_IO; ++ ++ if (argc > 1) { ++ *error = "least-pending ps: incorrect number of arguments"; ++ return -EINVAL; ++ } ++ ++ /* First path argument is number of I/Os before switching path */ ++ if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) { ++ *error = "least-pending ps: invalid repeat count"; ++ return -EINVAL; ++ } ++ ++ /* allocate the path */ ++ pi = kmalloc(sizeof(*pi), GFP_KERNEL); ++ if (!pi) { ++ *error = "least-pending ps: Error allocating path context"; ++ return -ENOMEM; ++ } ++ ++ pi->path = path; ++ pi->repeat_count = repeat_count; ++ atomic_set(&pi->io_count, 0); ++ ++ path->pscontext = pi; ++ ++ list_add(&pi->list, &s->valid_paths); ++ ++ return 0; ++} ++ ++static void lpp_fail_path(struct path_selector *ps, struct dm_path *p) ++{ ++ struct selector *s = ps->context; ++ struct path_info *pi = p->pscontext; ++ ++ if (!pi) ++ return; ++ ++ atomic_set(&pi->io_count, 0); ++ ++ list_move(&pi->list, &s->invalid_paths); ++} ++ ++static int lpp_reinstate_path(struct path_selector *ps, struct dm_path *p) ++{ ++ struct selector *s = ps->context; ++ struct path_info *pi = p->pscontext; ++ ++ if (!pi) ++ return 1; ++ ++ list_move(&pi->list, &s->valid_paths); ++ ++ return 0; ++} ++ ++static struct dm_path *lpp_select_path(struct path_selector *ps, ++ unsigned *repeat_count, ++ size_t nr_bytes) ++{ ++ struct selector *s = ps->context; ++ struct path_info *pi, *next, *least_io_path = NULL; ++ struct list_head *paths; ++ ++ if (list_empty(&s->valid_paths)) ++ return NULL; ++ ++ paths = &s->valid_paths; ++ ++ list_for_each_entry_safe(pi, next, paths, list) { ++ if (!least_io_path || atomic_read(&least_io_path->io_count) < atomic_read(&pi->io_count)) ++ least_io_path = pi; ++ if (!atomic_read(&least_io_path->io_count)) ++ break; ++ } ++ ++ if (!least_io_path) ++ return NULL; ++ ++ atomic_inc(&least_io_path->io_count); ++ *repeat_count = least_io_path->repeat_count; ++ ++ return least_io_path->path; ++} ++ ++static int lpp_end_io(struct path_selector *ps, struct dm_path *path, ++ size_t nr_bytes) ++{ ++ struct path_info *pi = NULL; ++ ++ pi = path->pscontext; ++ if (!pi) ++ return 1; ++ ++ atomic_dec(&pi->io_count); ++ ++ return 0; ++} ++ ++static struct path_selector_type lpp_ps = { ++ .name = "least-pending", ++ .module = THIS_MODULE, ++ .table_args = 1, ++ .info_args = 0, ++ .create = lpp_create, ++ .destroy = lpp_destroy, ++ .status = lpp_status, ++ .add_path = lpp_add_path, ++ .fail_path = lpp_fail_path, ++ .reinstate_path = lpp_reinstate_path, ++ .select_path = lpp_select_path, ++ .end_io = lpp_end_io, ++}; ++ ++static int __init dm_lpp_init(void) ++{ ++ int r = dm_register_path_selector(&lpp_ps); ++ ++ if (r < 0) ++ DMERR("register failed %d", r); ++ ++ DMINFO("version 1.0.0 loaded"); ++ ++ return r; ++} ++ ++static void __exit dm_lpp_exit(void) ++{ ++ int r = dm_unregister_path_selector(&lpp_ps); ++ ++ if (r < 0) ++ DMERR("unregister failed %d", r); ++} ++ ++module_init(dm_lpp_init); ++module_exit(dm_lpp_exit); ++ ++MODULE_DESCRIPTION(DM_NAME " least-pending multipath path selector"); ++MODULE_AUTHOR("Sakshi Chaitanya Veni "); ++MODULE_LICENSE("GPL"); ++ diff --git a/patches.suse/dm-mpath-no-activate-for-offlined-paths b/patches.suse/dm-mpath-no-activate-for-offlined-paths new file mode 100644 index 0000000..58a6486 --- /dev/null +++ b/patches.suse/dm-mpath-no-activate-for-offlined-paths @@ -0,0 +1,85 @@ +From: Hannes Reinecke +Subject: DM-MPIO fails to tresspass LUNs on CLARiiON arrays +Reference: bnc#484529 +Patch-mainline: Not yet + +On Clariion arrays we fail to send the trespass command correctly. +We're trying to send the trespass command to via an disabled path, +causing the device handler to loop trying to send the command on +an invalid path. + +Signed-off-by: Hannes Reinecke + +--- + drivers/md/dm-mpath.c | 12 +++++++++--- + drivers/md/dm-table.c | 12 ++++++++---- + 2 files changed, 17 insertions(+), 7 deletions(-) + +--- a/drivers/md/dm-mpath.c ++++ b/drivers/md/dm-mpath.c +@@ -1228,8 +1228,9 @@ static void pg_init_done(void *data, int + errors = 0; + break; + } +- DMERR("Could not failover the device: Handler scsi_dh_%s " +- "Error %d.", m->hw_handler_name, errors); ++ DMERR("Count not failover device %s: Handler scsi_dh_%s " ++ "was not loaded.", pgpath->path.pdev, ++ m->hw_handler_name); + /* + * Fail path for now, so we do not ping pong + */ +@@ -1242,6 +1243,10 @@ static void pg_init_done(void *data, int + */ + bypass_pg(m, pg, 1); + break; ++ case SCSI_DH_DEV_OFFLINED: ++ DMWARN("Device %s offlined.", pgpath->path.pdev); ++ errors = 0; ++ break; + /* TODO: For SCSI_DH_RETRY we should wait a couple seconds */ + case SCSI_DH_RETRY: + case SCSI_DH_IMM_RETRY: +@@ -1262,7 +1267,8 @@ static void pg_init_done(void *data, int + spin_lock_irqsave(&m->lock, flags); + if (errors) { + if (pgpath == m->current_pgpath) { +- DMERR("Could not failover device. Error %d.", errors); ++ DMERR("Could not failover device %s, error %d.", ++ pgpath->path.pdev, errors); + m->current_pgpath = NULL; + m->current_pg = NULL; + } +--- a/drivers/md/dm-table.c ++++ b/drivers/md/dm-table.c +@@ -411,14 +411,18 @@ static int upgrade_mode(struct dm_dev_in + + dd_new = dd_old = *dd; + +- dd_new.dm_dev.mode |= new_mode; ++ dd_new.dm_dev.mode = new_mode; + dd_new.dm_dev.bdev = NULL; + + r = open_dev(&dd_new, dd->dm_dev.bdev->bd_dev, md); +- if (r) ++ if (r == -EROFS) { ++ dd_new.dm_dev.mode &= ~FMODE_WRITE; ++ r = open_dev(&dd_new, dd->dm_dev.bdev->bd_dev, md); ++ } ++ if (!r) + return r; + +- dd->dm_dev.mode |= new_mode; ++ dd->dm_dev.mode = new_mode; + close_dev(&dd_old, md); + + return 0; +@@ -480,7 +484,7 @@ static int __table_get_device(struct dm_ + atomic_set(&dd->count, 0); + list_add(&dd->list, &t->devices); + +- } else if (dd->dm_dev.mode != (mode | dd->dm_dev.mode)) { ++ } else if (dd->dm_dev.mode != mode) { + r = upgrade_mode(dd, mode, t->md); + if (r) + return r; diff --git a/patches.suse/dm-mpath-no-partitions-feature b/patches.suse/dm-mpath-no-partitions-feature new file mode 100644 index 0000000..2acdfbb --- /dev/null +++ b/patches.suse/dm-mpath-no-partitions-feature @@ -0,0 +1,67 @@ +From: Hannes Reinecke +Subject: Disable partitions scan for multipathed devices +References: bnc#402922,bnc#514767 +Patch-mainline: not yet + +When multipath devices are being used as disks for VM Guests +any partition scanning / setup should be done within the VM Guest, +not from host. So we need to switch off partitions scanning via +kpartx there. +For this I've implemented a new feature 'no_partitions' which +just serves as a notifier to kpartx to _not_ create partitions +on these devices. + +Patch ported to SLES11. + +Signed-off-by: Hannes Reinecke + +--- + drivers/md/dm-mpath.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +--- a/drivers/md/dm-mpath.c ++++ b/drivers/md/dm-mpath.c +@@ -57,6 +57,8 @@ struct priority_group { + struct list_head pgpaths; + }; + ++#define FEATURE_NO_PARTITIONS 1 ++ + /* Multipath context */ + struct multipath { + struct list_head list; +@@ -83,6 +85,7 @@ struct multipath { + unsigned saved_queue_if_no_path;/* Saved state during suspension */ + unsigned pg_init_retries; /* Number of times to retry pg_init */ + unsigned pg_init_count; /* Number of times pg_init called */ ++ unsigned features; /* Additional selected features */ + + struct work_struct process_queued_ios; + struct list_head queued_ios; +@@ -851,6 +854,10 @@ static int parse_features(struct arg_set + continue; + } + ++ if (!strnicmp(param_name, MESG_STR("no_partitions"))) { ++ m->features |= FEATURE_NO_PARTITIONS; ++ continue; ++ } + if (!strnicmp(param_name, MESG_STR("pg_init_retries")) && + (argc >= 1)) { + r = read_param(_params + 1, shift(as), +@@ -1475,11 +1482,14 @@ static int multipath_status(struct dm_ta + DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count); + else { + DMEMIT("%u ", m->queue_if_no_path + +- (m->pg_init_retries > 0) * 2); ++ (m->pg_init_retries > 0) * 2 + ++ (m->features & FEATURE_NO_PARTITIONS)); + if (m->queue_if_no_path) + DMEMIT("queue_if_no_path "); + if (m->pg_init_retries) + DMEMIT("pg_init_retries %u ", m->pg_init_retries); ++ if (m->features & FEATURE_NO_PARTITIONS) ++ DMEMIT("no_partitions "); + } + + if (!m->hw_handler_name || type == STATUSTYPE_INFO) diff --git a/patches.suse/dm-mpath-null-pgs b/patches.suse/dm-mpath-null-pgs new file mode 100644 index 0000000..9de800b --- /dev/null +++ b/patches.suse/dm-mpath-null-pgs @@ -0,0 +1,27 @@ +From: Hannes Reinecke +Subject: Allow zero paths for multipath priority groups +References: bnc#372684 +Patch-mainline: not yet + +For correct handling of the all-paths-down scenario we have to +allow zero paths as a valid argument for priority groups. + +Signed-off-by: Hannes Reinecke + +--- + drivers/md/dm-mpath.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/md/dm-mpath.c ++++ b/drivers/md/dm-mpath.c +@@ -869,8 +869,8 @@ static int multipath_ctr(struct dm_targe + { + /* target parameters */ + static struct param _params[] = { +- {1, 1024, "invalid number of priority groups"}, +- {1, 1024, "invalid initial priority group number"}, ++ {0, 1024, "invalid number of priority groups"}, ++ {0, 1024, "invalid initial priority group number"}, + }; + + int r; diff --git a/patches.suse/dm-raid45_2.6.27_20081027.patch b/patches.suse/dm-raid45_2.6.27_20081027.patch new file mode 100644 index 0000000..f6a7e8c --- /dev/null +++ b/patches.suse/dm-raid45_2.6.27_20081027.patch @@ -0,0 +1,5606 @@ +From: "Heinz Mauelshagen +Subject: DMRAID45 module +X-URL: http://people.redhat.com/~heinzm/sw/dm/dm-raid45/ +Patch-mainline: not yet + + DM-RAID 45 module. + + This driver is used for "Fake RAID" devices. + +Acked-by: Jeff Mahoney + +--- + + drivers/md/Kconfig | 15 + drivers/md/Makefile | 4 + drivers/md/dm-memcache.c | 301 ++ + drivers/md/dm-memcache.h | 68 + drivers/md/dm-message.c | 182 + + drivers/md/dm-message.h | 91 + drivers/md/dm-raid45.c | 4523 +++++++++++++++++++++++++++++++++++++++++ + drivers/md/dm-raid45.h | 28 + drivers/md/dm-region-hash.c | 108 + drivers/md/dm.c | 1 + include/linux/dm-region-hash.h | 109 + 11 files changed, 5314 insertions(+), 116 deletions(-) + +--- a/drivers/md/Kconfig ++++ b/drivers/md/Kconfig +@@ -120,7 +120,6 @@ config MD_RAID10 + + config MD_RAID456 + tristate "RAID-4/RAID-5/RAID-6 mode" +- depends on BLK_DEV_MD + select MD_RAID6_PQ + select ASYNC_MEMCPY + select ASYNC_XOR +@@ -249,9 +248,14 @@ config DM_SNAPSHOT + ---help--- + Allow volume managers to take writable snapshots of a device. + ++config DM_RAID ++ tristate ++ depends on BLK_DEV_DM ++ + config DM_MIRROR + tristate "Mirror target" + depends on BLK_DEV_DM ++ select DM_RAID + ---help--- + Allow volume managers to mirror logical volumes, also + needed for live data migration tools such as 'pvmove'. +@@ -313,6 +317,15 @@ config DM_DELAY + + If unsure, say N. + ++config DM_RAID45 ++ tristate "RAID 4/5 target (EXPERIMENTAL)" ++ depends on DM_RAID ++ depends on BLK_DEV_DM && EXPERIMENTAL ++ ---help--- ++ A target that supports RAID4 and RAID5 mappings. ++ ++ If unsure, say N. ++ + config DM_UEVENT + bool "DM uevents (EXPERIMENTAL)" + depends on BLK_DEV_DM && EXPERIMENTAL +--- a/drivers/md/Makefile ++++ b/drivers/md/Makefile +@@ -41,7 +41,9 @@ obj-$(CONFIG_DM_MULTIPATH) += dm-multipa + obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o + obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o + obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o +-obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o dm-region-hash.o ++obj-$(CONFIG_DM_RAID) += dm-region-hash.o dm-log.o ++obj-$(CONFIG_DM_MIRROR) += dm-mirror.o ++obj-$(CONFIG_DM_RAID45) += dm-raid45.o dm-memcache.o dm-message.o + obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o + obj-$(CONFIG_DM_ZERO) += dm-zero.o + +--- /dev/null ++++ b/drivers/md/dm-memcache.c +@@ -0,0 +1,302 @@ ++/* ++ * Copyright (C) 2006-2008 Red Hat, Inc. All rights reserved. ++ * ++ * Module Author: Heinz Mauelshagen ++ * ++ * Device-mapper memory object handling: ++ * ++ * o allocate/free total_pages in a per client page pool. ++ * ++ * o allocate/free memory objects with chunks (1..n) of ++ * pages_per_chunk pages hanging off. ++ * ++ * This file is released under the GPL. ++ */ ++ ++#define DM_MEM_CACHE_VERSION "0.2" ++ ++#include "dm.h" ++#include "dm-memcache.h" ++#include ++#include ++ ++struct dm_mem_cache_client { ++ spinlock_t lock; ++ mempool_t *objs_pool; ++ struct page_list *free_list; ++ unsigned objects; ++ unsigned chunks; ++ unsigned pages_per_chunk; ++ unsigned free_pages; ++ unsigned total_pages; ++}; ++ ++/* ++ * Free pages and page_list elements of client. ++ */ ++static void free_cache_pages(struct page_list *list) ++{ ++ while (list) { ++ struct page_list *pl = list; ++ ++ list = pl->next; ++ BUG_ON(!pl->page); ++ __free_page(pl->page); ++ kfree(pl); ++ } ++} ++ ++/* ++ * Alloc number of pages and page_list elements as required by client. ++ */ ++static struct page_list *alloc_cache_pages(unsigned pages) ++{ ++ struct page_list *pl, *ret = NULL; ++ struct page *page; ++ ++ while (pages--) { ++ page = alloc_page(GFP_NOIO); ++ if (!page) ++ goto err; ++ ++ pl = kmalloc(sizeof(*pl), GFP_NOIO); ++ if (!pl) { ++ __free_page(page); ++ goto err; ++ } ++ ++ pl->page = page; ++ pl->next = ret; ++ ret = pl; ++ } ++ ++ return ret; ++ ++err: ++ free_cache_pages(ret); ++ return NULL; ++} ++ ++/* ++ * Allocate page_list elements from the pool to chunks of the memory object. ++ */ ++static void alloc_chunks(struct dm_mem_cache_client *cl, ++ struct dm_mem_cache_object *obj) ++{ ++ unsigned chunks = cl->chunks; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ local_irq_disable(); ++ while (chunks--) { ++ unsigned p = cl->pages_per_chunk; ++ ++ obj[chunks].pl = NULL; ++ ++ while (p--) { ++ struct page_list *pl; ++ ++ /* Take next element from free list */ ++ spin_lock(&cl->lock); ++ pl = cl->free_list; ++ BUG_ON(!pl); ++ cl->free_list = pl->next; ++ spin_unlock(&cl->lock); ++ ++ pl->next = obj[chunks].pl; ++ obj[chunks].pl = pl; ++ } ++ } ++ ++ local_irq_restore(flags); ++} ++ ++/* ++ * Free page_list elements putting them back onto free list ++ */ ++static void free_chunks(struct dm_mem_cache_client *cl, ++ struct dm_mem_cache_object *obj) ++{ ++ unsigned chunks = cl->chunks; ++ unsigned long flags; ++ struct page_list *next, *pl; ++ ++ local_irq_save(flags); ++ local_irq_disable(); ++ while (chunks--) { ++ for (pl = obj[chunks].pl; pl; pl = next) { ++ next = pl->next; ++ ++ spin_lock(&cl->lock); ++ pl->next = cl->free_list; ++ cl->free_list = pl; ++ cl->free_pages++; ++ spin_unlock(&cl->lock); ++ } ++ } ++ ++ local_irq_restore(flags); ++} ++ ++/* ++ * Create/destroy dm memory cache client resources. ++ */ ++struct dm_mem_cache_client * ++dm_mem_cache_client_create(unsigned objects, unsigned chunks, ++ unsigned pages_per_chunk) ++{ ++ unsigned total_pages = objects * chunks * pages_per_chunk; ++ struct dm_mem_cache_client *client; ++ ++ BUG_ON(!total_pages); ++ client = kzalloc(sizeof(*client), GFP_KERNEL); ++ if (!client) ++ return ERR_PTR(-ENOMEM); ++ ++ client->objs_pool = mempool_create_kmalloc_pool(objects, ++ chunks * sizeof(struct dm_mem_cache_object)); ++ if (!client->objs_pool) ++ goto err; ++ ++ client->free_list = alloc_cache_pages(total_pages); ++ if (!client->free_list) ++ goto err1; ++ ++ spin_lock_init(&client->lock); ++ client->objects = objects; ++ client->chunks = chunks; ++ client->pages_per_chunk = pages_per_chunk; ++ client->free_pages = client->total_pages = total_pages; ++ return client; ++ ++err1: ++ mempool_destroy(client->objs_pool); ++err: ++ kfree(client); ++ return ERR_PTR(-ENOMEM); ++} ++EXPORT_SYMBOL(dm_mem_cache_client_create); ++ ++void dm_mem_cache_client_destroy(struct dm_mem_cache_client *cl) ++{ ++ BUG_ON(cl->free_pages != cl->total_pages); ++ free_cache_pages(cl->free_list); ++ mempool_destroy(cl->objs_pool); ++ kfree(cl); ++} ++EXPORT_SYMBOL(dm_mem_cache_client_destroy); ++ ++/* ++ * Grow a clients cache by an amount of pages. ++ * ++ * Don't call from interrupt context! ++ */ ++int dm_mem_cache_grow(struct dm_mem_cache_client *cl, unsigned objects) ++{ ++ unsigned pages = objects * cl->chunks * cl->pages_per_chunk; ++ struct page_list *pl, *last; ++ ++ BUG_ON(!pages); ++ pl = alloc_cache_pages(pages); ++ if (!pl) ++ return -ENOMEM; ++ ++ last = pl; ++ while (last->next) ++ last = last->next; ++ ++ spin_lock_irq(&cl->lock); ++ last->next = cl->free_list; ++ cl->free_list = pl; ++ cl->free_pages += pages; ++ cl->total_pages += pages; ++ cl->objects++; ++ spin_unlock_irq(&cl->lock); ++ ++ mempool_resize(cl->objs_pool, cl->objects, GFP_NOIO); ++ return 0; ++} ++EXPORT_SYMBOL(dm_mem_cache_grow); ++ ++/* Shrink a clients cache by an amount of pages */ ++int dm_mem_cache_shrink(struct dm_mem_cache_client *cl, unsigned objects) ++{ ++ int r; ++ unsigned pages = objects * cl->chunks * cl->pages_per_chunk, p = pages; ++ unsigned long flags; ++ struct page_list *last = NULL, *pl, *pos; ++ ++ BUG_ON(!pages); ++ ++ spin_lock_irqsave(&cl->lock, flags); ++ pl = pos = cl->free_list; ++ while (p-- && pos->next) { ++ last = pos; ++ pos = pos->next; ++ } ++ ++ if (++p) ++ r = -ENOMEM; ++ else { ++ r = 0; ++ cl->free_list = pos; ++ cl->free_pages -= pages; ++ cl->total_pages -= pages; ++ cl->objects--; ++ last->next = NULL; ++ } ++ spin_unlock_irqrestore(&cl->lock, flags); ++ ++ if (!r) { ++ free_cache_pages(pl); ++ mempool_resize(cl->objs_pool, cl->objects, GFP_NOIO); ++ } ++ ++ return r; ++} ++EXPORT_SYMBOL(dm_mem_cache_shrink); ++ ++/* ++ * Allocate/free a memory object ++ * ++ * Can be called from interrupt context ++ */ ++struct dm_mem_cache_object *dm_mem_cache_alloc(struct dm_mem_cache_client *cl) ++{ ++ int r = 0; ++ unsigned pages = cl->chunks * cl->pages_per_chunk; ++ unsigned long flags; ++ struct dm_mem_cache_object *obj; ++ ++ obj = mempool_alloc(cl->objs_pool, GFP_NOIO); ++ if (!obj) ++ return ERR_PTR(-ENOMEM); ++ ++ spin_lock_irqsave(&cl->lock, flags); ++ if (pages > cl->free_pages) ++ r = -ENOMEM; ++ else ++ cl->free_pages -= pages; ++ spin_unlock_irqrestore(&cl->lock, flags); ++ ++ if (r) { ++ mempool_free(obj, cl->objs_pool); ++ return ERR_PTR(r); ++ } ++ ++ alloc_chunks(cl, obj); ++ return obj; ++} ++EXPORT_SYMBOL(dm_mem_cache_alloc); ++ ++void dm_mem_cache_free(struct dm_mem_cache_client *cl, ++ struct dm_mem_cache_object *obj) ++{ ++ free_chunks(cl, obj); ++ mempool_free(obj, cl->objs_pool); ++} ++EXPORT_SYMBOL(dm_mem_cache_free); ++ ++MODULE_DESCRIPTION(DM_NAME " dm memory cache"); ++MODULE_AUTHOR("Heinz Mauelshagen "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/md/dm-memcache.h +@@ -0,0 +1,68 @@ ++/* ++ * Copyright (C) 2006-2008 Red Hat, Inc. All rights reserved. ++ * ++ * Module Author: Heinz Mauelshagen ++ * ++ * Device-mapper memory object handling: ++ * ++ * o allocate/free total_pages in a per client page pool. ++ * ++ * o allocate/free memory objects with chunks (1..n) of ++ * pages_per_chunk pages hanging off. ++ * ++ * This file is released under the GPL. ++ */ ++ ++#ifndef _DM_MEM_CACHE_H ++#define _DM_MEM_CACHE_H ++ ++#define DM_MEM_CACHE_H_VERSION "0.1" ++ ++#include "dm.h" ++#include ++ ++static inline struct page_list *pl_elem(struct page_list *pl, unsigned p) ++{ ++ while (pl && p--) ++ pl = pl->next; ++ ++ return pl; ++} ++ ++struct dm_mem_cache_object { ++ struct page_list *pl; /* Dynamically allocated array */ ++ void *private; /* Caller context reference */ ++}; ++ ++struct dm_mem_cache_client; ++ ++/* ++ * Create/destroy dm memory cache client resources. ++ * ++ * On creation, a number of @objects with @chunks of ++ * @pages_per_chunk pages will be allocated. ++ */ ++struct dm_mem_cache_client * ++dm_mem_cache_client_create(unsigned objects, unsigned chunks, ++ unsigned pages_per_chunk); ++void dm_mem_cache_client_destroy(struct dm_mem_cache_client *client); ++ ++/* ++ * Grow/shrink a dm memory cache client resources ++ * by @objetcs amount of objects. ++ */ ++int dm_mem_cache_grow(struct dm_mem_cache_client *client, unsigned objects); ++int dm_mem_cache_shrink(struct dm_mem_cache_client *client, unsigned objects); ++ ++/* ++ * Allocate/free a memory object ++ * ++ * On allocation one object with an amount of chunks and ++ * an amount of pages per chunk will be returned on success. ++ */ ++struct dm_mem_cache_object * ++dm_mem_cache_alloc(struct dm_mem_cache_client *client); ++void dm_mem_cache_free(struct dm_mem_cache_client *client, ++ struct dm_mem_cache_object *object); ++ ++#endif +--- /dev/null ++++ b/drivers/md/dm-message.c +@@ -0,0 +1,182 @@ ++/* ++ * Copyright (C) 2007,2008 Red Hat Inc. All rights reserved. ++ * ++ * Module Author: Heinz Mauelshagen ++ * ++ * General device-mapper message interface argument parser. ++ * ++ * This file is released under the GPL. ++ * ++ * device-mapper message parser. ++ * ++ */ ++ ++#include "dm.h" ++#include "dm-message.h" ++#include ++ ++#define DM_MSG_PREFIX "dm_message" ++ ++/* Basename of a path. */ ++static inline char * ++basename(char *s) ++{ ++ char *p = strrchr(s, '/'); ++ ++ return p ? p + 1 : s; ++} ++ ++/* Get an argument depending on type. */ ++static void ++message_arguments(struct dm_msg *msg, int argc, char **argv) ++{ ++ ++ if (argc) { ++ int i; ++ struct dm_message_argument *args = msg->spec->args; ++ ++ for (i = 0; i < args->num_args; i++) { ++ int r; ++ unsigned long **ptr = args->ptr; ++ enum dm_message_argument_type type = args->types[i]; ++ ++ switch (type) { ++ case dm_msg_base_t: ++ ((char **) ptr)[i] = basename(argv[i]); ++ break; ++ ++ case dm_msg_str_t: ++ ((char **) ptr)[i] = argv[i]; ++ break; ++ ++ case dm_msg_int_t: ++ r = sscanf(argv[i], "%d", ((int **) ptr)[i]); ++ goto check; ++ ++ case dm_msg_uint_t: ++ r = sscanf(argv[i], "%u", ++ ((unsigned **) ptr)[i]); ++ goto check; ++ ++ case dm_msg_uint64_t: ++ r = sscanf(argv[i], "%llu", ++ ((unsigned long long **) ptr)[i]); ++ ++check: ++ if (r != 1) { ++ set_bit(dm_msg_ret_undef, &msg->ret); ++ set_bit(dm_msg_ret_arg, &msg->ret); ++ } ++ } ++ } ++ } ++} ++ ++/* Parse message options. */ ++static void ++message_options_parse(struct dm_msg *msg, int argc, char **argv) ++{ ++ int hit = 0; ++ unsigned long *action; ++ size_t l1 = strlen(*argv), l_hit = 0; ++ struct dm_message_option *o = msg->spec->options; ++ char **option, **option_end = o->options + o->num_options; ++ ++ for (option = o->options, action = o->actions; ++ option < option_end; option++, action++) { ++ size_t l2 = strlen(*option); ++ ++ if (!strnicmp(*argv, *option, min(l1, l2))) { ++ hit++; ++ l_hit = l2; ++ set_bit(*action, &msg->action); ++ } ++ } ++ ++ /* Assume error. */ ++ msg->ret = 0; ++ set_bit(dm_msg_ret_option, &msg->ret); ++ if (!hit || l1 > l_hit) ++ set_bit(dm_msg_ret_undef, &msg->ret); /* Undefined option. */ ++ else if (hit > 1) ++ set_bit(dm_msg_ret_ambiguous, &msg->ret); /* Ambiguous option.*/ ++ else { ++ clear_bit(dm_msg_ret_option, &msg->ret); /* Option OK. */ ++ message_arguments(msg, --argc, ++argv); ++ } ++} ++ ++static inline void ++print_ret(const char *caller, unsigned long ret) ++{ ++ struct { ++ unsigned long err; ++ const char *err_str; ++ } static err_msg[] = { ++ { dm_msg_ret_ambiguous, "message ambiguous" }, ++ { dm_msg_ret_inval, "message invalid" }, ++ { dm_msg_ret_undef, "message undefined" }, ++ { dm_msg_ret_arg, "message argument" }, ++ { dm_msg_ret_argcount, "message argument count" }, ++ { dm_msg_ret_option, "option" }, ++ }, *e = ARRAY_END(err_msg); ++ ++ while (e-- > err_msg) { ++ if (test_bit(e->err, &ret)) ++ DMERR("%s %s", caller, e->err_str); ++ } ++} ++ ++/* Parse a message action. */ ++int ++dm_message_parse(const char *caller, struct dm_msg *msg, void *context, ++ int argc, char **argv) ++{ ++ int hit = 0; ++ size_t l1 = strlen(*argv), l_hit = 0; ++ struct dm_msg_spec *s, *s_hit = NULL, ++ *s_end = msg->specs + msg->num_specs; ++ ++ if (argc < 2) ++ return -EINVAL; ++ ++ for (s = msg->specs; s < s_end; s++) { ++ size_t l2 = strlen(s->cmd); ++ ++ if (!strnicmp(*argv, s->cmd, min(l1, l2))) { ++ hit++; ++ l_hit = l2; ++ s_hit = s; ++ } ++ } ++ ++ msg->ret = 0; ++ if (!hit || l1 > l_hit) /* No hit or message string too long. */ ++ set_bit(dm_msg_ret_undef, &msg->ret); ++ else if (hit > 1) /* Ambiguous message. */ ++ set_bit(dm_msg_ret_ambiguous, &msg->ret); ++ else if (argc - 2 != s_hit->args->num_args) { ++ set_bit(dm_msg_ret_undef, &msg->ret); ++ set_bit(dm_msg_ret_argcount, &msg->ret); ++ } ++ ++ if (msg->ret) ++ goto bad; ++ ++ msg->action = 0; ++ msg->spec = s_hit; ++ set_bit(s_hit->action, &msg->action); ++ message_options_parse(msg, --argc, ++argv); ++ ++ if (!msg->ret) ++ return msg->spec->f(msg, context); ++ ++bad: ++ print_ret(caller, msg->ret); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(dm_message_parse); ++ ++MODULE_DESCRIPTION(DM_NAME " device-mapper target message parser"); ++MODULE_AUTHOR("Heinz Mauelshagen "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/md/dm-message.h +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (C) 2007,2008 Red Hat, Inc. All rights reserved. ++ * ++ * Module Author: Heinz Mauelshagen ++ * ++ * General device-mapper message interface argument parser. ++ * ++ * This file is released under the GPL. ++ * ++ */ ++ ++#ifndef DM_MESSAGE_H ++#define DM_MESSAGE_H ++ ++/* Factor out to dm.h. */ ++/* Reference to array end. */ ++#define ARRAY_END(a) ((a) + ARRAY_SIZE(a)) ++ ++/* Message return bits. */ ++enum dm_message_return { ++ dm_msg_ret_ambiguous, /* Action ambiguous. */ ++ dm_msg_ret_inval, /* Action invalid. */ ++ dm_msg_ret_undef, /* Action undefined. */ ++ ++ dm_msg_ret_option, /* Option error. */ ++ dm_msg_ret_arg, /* Argument error. */ ++ dm_msg_ret_argcount, /* Argument count error. */ ++}; ++ ++/* Message argument type conversions. */ ++enum dm_message_argument_type { ++ dm_msg_base_t, /* Basename string. */ ++ dm_msg_str_t, /* String. */ ++ dm_msg_int_t, /* Signed int. */ ++ dm_msg_uint_t, /* Unsigned int. */ ++ dm_msg_uint64_t, /* Unsigned int 64. */ ++}; ++ ++/* A message option. */ ++struct dm_message_option { ++ unsigned num_options; ++ char **options; ++ unsigned long *actions; ++}; ++ ++/* Message arguments and types. */ ++struct dm_message_argument { ++ unsigned num_args; ++ unsigned long **ptr; ++ enum dm_message_argument_type types[]; ++}; ++ ++/* Client message. */ ++struct dm_msg { ++ unsigned long action; /* Identified action. */ ++ unsigned long ret; /* Return bits. */ ++ unsigned num_specs; /* # of sepcifications listed. */ ++ struct dm_msg_spec *specs; /* Specification list. */ ++ struct dm_msg_spec *spec; /* Specification selected. */ ++}; ++ ++/* Secification of the message. */ ++struct dm_msg_spec { ++ const char *cmd; /* Name of the command (i.e. 'bandwidth'). */ ++ unsigned long action; ++ struct dm_message_option *options; ++ struct dm_message_argument *args; ++ unsigned long parm; /* Parameter to pass through to callback. */ ++ /* Function to process for action. */ ++ int (*f) (struct dm_msg *msg, void *context); ++}; ++ ++/* Parameter access macros. */ ++#define DM_MSG_PARM(msg) ((msg)->spec->parm) ++ ++#define DM_MSG_STR_ARGS(msg, idx) ((char *) *(msg)->spec->args->ptr[idx]) ++#define DM_MSG_INT_ARGS(msg, idx) ((int) *(msg)->spec->args->ptr[idx]) ++#define DM_MSG_UINT_ARGS(msg, idx) ((unsigned) DM_MSG_INT_ARG(msg, idx)) ++#define DM_MSG_UINT64_ARGS(msg, idx) ((uint64_t) *(msg)->spec->args->ptr[idx]) ++ ++#define DM_MSG_STR_ARG(msg) DM_MSG_STR_ARGS(msg, 0) ++#define DM_MSG_INT_ARG(msg) DM_MSG_INT_ARGS(msg, 0) ++#define DM_MSG_UINT_ARG(msg) DM_MSG_UINT_ARGS(msg, 0) ++#define DM_MSG_UINT64_ARG(msg) DM_MSG_UINT64_ARGS(msg, 0) ++ ++ ++/* Parse a message and its options and optionally call a function back. */ ++int dm_message_parse(const char *caller, struct dm_msg *msg, void *context, ++ int argc, char **argv); ++ ++#endif +--- /dev/null ++++ b/drivers/md/dm-raid45.c +@@ -0,0 +1,4524 @@ ++/* ++ * Copyright (C) 2005-2008 Red Hat, Inc. All rights reserved. ++ * ++ * Module Author: Heinz Mauelshagen ++ * ++ * This file is released under the GPL. ++ * ++ * ++ * Linux 2.6 Device Mapper RAID4 and RAID5 target. ++ * ++ * Supports: ++ * o RAID4 with dedicated and selectable parity device ++ * o RAID5 with rotating parity (left+right, symmetric+asymmetric) ++ * o run time optimization of xor algorithm used to calculate parity ++ * ++ * ++ * Thanks to MD for: ++ * o the raid address calculation algorithm ++ * o the base of the biovec <-> page list copier. ++ * ++ * ++ * Uses region hash to keep track of how many writes are in flight to ++ * regions in order to use dirty log to keep state of regions to recover: ++ * ++ * o clean regions (those which are synchronized ++ * and don't have write io in flight) ++ * o dirty regions (those with write io in flight) ++ * ++ * ++ * On startup, any dirty regions are migrated to the 'nosync' state ++ * and are subject to recovery by the daemon. ++ * ++ * See raid_ctr() for table definition. ++ * ++ * ++ * FIXME: ++ * o add virtual interface for locking ++ * o remove instrumentation (REMOVEME:) ++ * ++ */ ++ ++static const char *version = "v0.2431"; ++ ++#include "dm.h" ++#include "dm-memcache.h" ++#include "dm-message.h" ++#include "dm-raid45.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* # of parallel recovered regions */ ++/* FIXME: cope with multiple recovery stripes in raid_set struct. */ ++#define MAX_RECOVER 1 /* needs to be 1! */ ++ ++/* ++ * Configurable parameters ++ */ ++#define INLINE ++ ++/* Default # of stripes if not set in constructor. */ ++#define STRIPES 64 ++ ++/* Minimum/maximum # of selectable stripes. */ ++#define STRIPES_MIN 8 ++#define STRIPES_MAX 16384 ++ ++/* Default chunk size in sectors if not set in constructor. */ ++#define CHUNK_SIZE 64 ++ ++/* Default io size in sectors if not set in constructor. */ ++#define IO_SIZE_MIN SECTORS_PER_PAGE ++#define IO_SIZE IO_SIZE_MIN ++ ++/* Maximum setable chunk size in sectors. */ ++#define CHUNK_SIZE_MAX 16384 ++ ++/* Recover io size default in sectors. */ ++#define RECOVER_IO_SIZE_MIN 64 ++#define RECOVER_IO_SIZE 256 ++ ++/* Default percentage recover io bandwidth. */ ++#define BANDWIDTH 10 ++#define BANDWIDTH_MIN 1 ++#define BANDWIDTH_MAX 100 ++/* ++ * END Configurable parameters ++ */ ++ ++#define TARGET "dm-raid45" ++#define DAEMON "kraid45d" ++#define DM_MSG_PREFIX TARGET ++ ++#define SECTORS_PER_PAGE (PAGE_SIZE >> SECTOR_SHIFT) ++ ++/* Amount/size for __xor(). */ ++#define SECTORS_PER_XOR SECTORS_PER_PAGE ++#define XOR_SIZE PAGE_SIZE ++ ++/* Derive raid_set from stripe_cache pointer. */ ++#define RS(x) container_of(x, struct raid_set, sc) ++ ++/* Check value in range. */ ++#define range_ok(i, min, max) (i >= min && i <= max) ++ ++/* Page reference. */ ++#define PAGE(stripe, p) ((stripe)->obj[p].pl->page) ++ ++/* Bio list reference. */ ++#define BL(stripe, p, rw) (stripe->ss[p].bl + rw) ++ ++/* Page list reference. */ ++#define PL(stripe, p) (stripe->obj[p].pl) ++ ++/* Check argument is power of 2. */ ++#define POWER_OF_2(a) (!(a & (a - 1))) ++ ++/* Factor out to dm-bio-list.h */ ++static inline void bio_list_push(struct bio_list *bl, struct bio *bio) ++{ ++ bio->bi_next = bl->head; ++ bl->head = bio; ++ ++ if (!bl->tail) ++ bl->tail = bio; ++} ++ ++/* Factor out to dm.h */ ++#define TI_ERR_RET(str, ret) \ ++ do { ti->error = DM_MSG_PREFIX ": " str; return ret; } while (0); ++#define TI_ERR(str) TI_ERR_RET(str, -EINVAL) ++ ++/*----------------------------------------------------------------- ++ * Stripe cache ++ * ++ * Cache for all reads and writes to raid sets (operational or degraded) ++ * ++ * We need to run all data to and from a RAID set through this cache, ++ * because parity chunks need to get calculated from data chunks ++ * or, in the degraded/resynchronization case, missing chunks need ++ * to be reconstructed using the other chunks of the stripe. ++ *---------------------------------------------------------------*/ ++/* Protect kmem cache # counter. */ ++static atomic_t _stripe_sc_nr = ATOMIC_INIT(-1); /* kmem cache # counter. */ ++ ++/* A stripe set (holds bios hanging off). */ ++struct stripe_set { ++ struct stripe *stripe; /* Backpointer to stripe for endio(). */ ++ struct bio_list bl[3]; /* Reads, writes, and writes merged. */ ++#define WRITE_MERGED 2 ++}; ++ ++#if READ != 0 || WRITE != 1 ++#error dm-raid45: READ/WRITE != 0/1 used as index!!! ++#endif ++ ++/* ++ * Stripe linked list indexes. Keep order, because the stripe ++ * and the stripe cache rely on the first 3! ++ */ ++enum list_types { ++ LIST_IO = 0, /* Stripes with io pending. */ ++ LIST_ENDIO, /* Stripes to endio. */ ++ LIST_LRU, /* Least recently used stripes. */ ++ LIST_HASH, /* Hashed stripes. */ ++ LIST_RECOVER = LIST_HASH, /* For recovery type stripes only. */ ++ NR_LISTS, /* To size array in struct stripe. */ ++}; ++ ++enum lock_types { ++ LOCK_ENDIO = 0, /* Protect endio list. */ ++ LOCK_LRU, /* Protect lru list. */ ++ NR_LOCKS, /* To size array in struct stripe_cache. */ ++}; ++ ++/* A stripe: the io object to handle all reads and writes to a RAID set. */ ++struct stripe { ++ struct stripe_cache *sc; /* Backpointer to stripe cache. */ ++ ++ sector_t key; /* Hash key. */ ++ region_t region; /* Region stripe is mapped to. */ ++ ++ /* Reference count. */ ++ atomic_t cnt; ++ ++ struct { ++ unsigned long flags; /* flags (see below). */ ++ ++ /* ++ * Pending ios in flight: ++ * ++ * used as a 'lock' to control move of stripe to endio list ++ */ ++ atomic_t pending; /* Pending ios in flight. */ ++ ++ /* Sectors to read and write for multi page stripe sets. */ ++ unsigned size; ++ } io; ++ ++ /* Lock on stripe (for clustering). */ ++ void *lock; ++ ++ /* ++ * 4 linked lists: ++ * o io list to flush io ++ * o endio list ++ * o LRU list to put stripes w/o reference count on ++ * o stripe cache hash ++ */ ++ struct list_head lists[NR_LISTS]; ++ ++ struct { ++ unsigned short parity; /* Parity chunk index. */ ++ short recover; /* Recovery chunk index. */ ++ } idx; ++ ++ /* This sets memory cache object (dm-mem-cache). */ ++ struct dm_mem_cache_object *obj; ++ ++ /* Array of stripe sets (dynamically allocated). */ ++ struct stripe_set ss[0]; ++}; ++ ++/* States stripes can be in (flags field). */ ++enum stripe_states { ++ STRIPE_ACTIVE, /* Active io on stripe. */ ++ STRIPE_ERROR, /* io error on stripe. */ ++ STRIPE_MERGED, /* Writes got merged. */ ++ STRIPE_READ, /* Read. */ ++ STRIPE_RBW, /* Read-before-write. */ ++ STRIPE_RECONSTRUCT, /* reconstruct of a missing chunk required. */ ++ STRIPE_RECOVER, /* Stripe used for RAID set recovery. */ ++}; ++ ++/* ... and macros to access them. */ ++#define BITOPS(name, what, var, flag) \ ++static inline int TestClear ## name ## what(struct var *v) \ ++{ return test_and_clear_bit(flag, &v->io.flags); } \ ++static inline int TestSet ## name ## what(struct var *v) \ ++{ return test_and_set_bit(flag, &v->io.flags); } \ ++static inline void Clear ## name ## what(struct var *v) \ ++{ clear_bit(flag, &v->io.flags); } \ ++static inline void Set ## name ## what(struct var *v) \ ++{ set_bit(flag, &v->io.flags); } \ ++static inline int name ## what(struct var *v) \ ++{ return test_bit(flag, &v->io.flags); } ++ ++ ++BITOPS(Stripe, Active, stripe, STRIPE_ACTIVE) ++BITOPS(Stripe, Merged, stripe, STRIPE_MERGED) ++BITOPS(Stripe, Error, stripe, STRIPE_ERROR) ++BITOPS(Stripe, Read, stripe, STRIPE_READ) ++BITOPS(Stripe, RBW, stripe, STRIPE_RBW) ++BITOPS(Stripe, Reconstruct, stripe, STRIPE_RECONSTRUCT) ++BITOPS(Stripe, Recover, stripe, STRIPE_RECOVER) ++ ++/* A stripe hash. */ ++struct stripe_hash { ++ struct list_head *hash; ++ unsigned buckets; ++ unsigned mask; ++ unsigned prime; ++ unsigned shift; ++}; ++ ++/* A stripe cache. */ ++struct stripe_cache { ++ /* Stripe hash. */ ++ struct stripe_hash hash; ++ ++ /* Stripes with io to flush, stripes to endio and LRU lists. */ ++ struct list_head lists[3]; ++ ++ /* Locks to protect endio and lru lists. */ ++ spinlock_t locks[NR_LOCKS]; ++ ++ /* Slab cache to allocate stripes from. */ ++ struct { ++ struct kmem_cache *cache; /* Cache itself. */ ++ char name[32]; /* Unique name. */ ++ } kc; ++ ++ struct dm_io_client *dm_io_client; /* dm-io client resource context. */ ++ ++ /* dm-mem-cache client resource context. */ ++ struct dm_mem_cache_client *mem_cache_client; ++ ++ int stripes_parm; /* # stripes parameter from constructor. */ ++ atomic_t stripes; /* actual # of stripes in cache. */ ++ atomic_t stripes_to_shrink; /* # of stripes to shrink cache by. */ ++ atomic_t stripes_last; /* last # of stripes in cache. */ ++ atomic_t active_stripes; /* actual # of active stripes in cache. */ ++ ++ /* REMOVEME: */ ++ atomic_t max_active_stripes; /* actual # of active stripes in cache. */ ++}; ++ ++/* Flag specs for raid_dev */ ; ++enum raid_dev_flags { DEVICE_FAILED, IO_QUEUED }; ++ ++/* The raid device in a set. */ ++struct raid_dev { ++ struct dm_dev *dev; ++ unsigned long flags; /* raid_dev_flags. */ ++ sector_t start; /* offset to map to. */ ++}; ++ ++/* Flags spec for raid_set. */ ++enum raid_set_flags { ++ RS_CHECK_OVERWRITE, /* Check for chunk overwrites. */ ++ RS_DEAD, /* RAID set inoperational. */ ++ RS_DEVEL_STATS, /* REMOVEME: display status information. */ ++ RS_IO_ERROR, /* io error on set. */ ++ RS_RECOVER, /* Do recovery. */ ++ RS_RECOVERY_BANDWIDTH, /* Allow recovery bandwidth (delayed bios). */ ++ RS_REGION_GET, /* get a region to recover. */ ++ RS_SC_BUSY, /* stripe cache busy -> send an event. */ ++ RS_SUSPENDED, /* RAID set suspendedn. */ ++}; ++ ++/* REMOVEME: devel stats counters. */ ++enum stats_types { ++ S_BIOS_READ, ++ S_BIOS_ADDED_READ, ++ S_BIOS_ENDIO_READ, ++ S_BIOS_WRITE, ++ S_BIOS_ADDED_WRITE, ++ S_BIOS_ENDIO_WRITE, ++ S_CAN_MERGE, ++ S_CANT_MERGE, ++ S_CONGESTED, ++ S_DM_IO_READ, ++ S_DM_IO_WRITE, ++ S_ACTIVE_READS, ++ S_BANDWIDTH, ++ S_BARRIER, ++ S_BIO_COPY_PL_NEXT, ++ S_DEGRADED, ++ S_DELAYED_BIOS, ++ S_EVICT, ++ S_FLUSHS, ++ S_HITS_1ST, ++ S_IOS_POST, ++ S_INSCACHE, ++ S_MAX_LOOKUP, ++ S_MERGE_PAGE_LOCKED, ++ S_NO_BANDWIDTH, ++ S_NOT_CONGESTED, ++ S_NO_RW, ++ S_NOSYNC, ++ S_PROHIBITPAGEIO, ++ S_RECONSTRUCT_EI, ++ S_RECONSTRUCT_DEV, ++ S_REDO, ++ S_REQUEUE, ++ S_STRIPE_ERROR, ++ S_SUM_DELAYED_BIOS, ++ S_XORS, ++ S_NR_STATS, /* # of stats counters. */ ++}; ++ ++/* Status type -> string mappings. */ ++struct stats_map { ++ const enum stats_types type; ++ const char *str; ++}; ++ ++static struct stats_map stats_map[] = { ++ { S_BIOS_READ, "r=" }, ++ { S_BIOS_ADDED_READ, "/" }, ++ { S_BIOS_ENDIO_READ, "/" }, ++ { S_BIOS_WRITE, " w=" }, ++ { S_BIOS_ADDED_WRITE, "/" }, ++ { S_BIOS_ENDIO_WRITE, "/" }, ++ { S_DM_IO_READ, " rc=" }, ++ { S_DM_IO_WRITE, " wc=" }, ++ { S_ACTIVE_READS, " active_reads=" }, ++ { S_BANDWIDTH, " bandwidth=" }, ++ { S_NO_BANDWIDTH, " no_bandwidth=" }, ++ { S_BARRIER, " barrier=" }, ++ { S_BIO_COPY_PL_NEXT, " bio_copy_pl_next=" }, ++ { S_CAN_MERGE, " can_merge=" }, ++ { S_MERGE_PAGE_LOCKED, "/page_locked=" }, ++ { S_CANT_MERGE, "/cant_merge=" }, ++ { S_CONGESTED, " congested=" }, ++ { S_NOT_CONGESTED, "/not_congested=" }, ++ { S_DEGRADED, " degraded=" }, ++ { S_DELAYED_BIOS, " delayed_bios=" }, ++ { S_SUM_DELAYED_BIOS, "/sum_delayed_bios=" }, ++ { S_EVICT, " evict=" }, ++ { S_FLUSHS, " flushs=" }, ++ { S_HITS_1ST, " hits_1st=" }, ++ { S_IOS_POST, " ios_post=" }, ++ { S_INSCACHE, " inscache=" }, ++ { S_MAX_LOOKUP, " max_lookup=" }, ++ { S_NO_RW, " no_rw=" }, ++ { S_NOSYNC, " nosync=" }, ++ { S_PROHIBITPAGEIO, " ProhibitPageIO=" }, ++ { S_RECONSTRUCT_EI, " reconstruct_ei=" }, ++ { S_RECONSTRUCT_DEV, " reconstruct_dev=" }, ++ { S_REDO, " redo=" }, ++ { S_REQUEUE, " requeue=" }, ++ { S_STRIPE_ERROR, " stripe_error=" }, ++ { S_XORS, " xors=" }, ++}; ++ ++/* ++ * A RAID set. ++ */ ++typedef void (*xor_function_t)(unsigned count, unsigned long **data); ++struct raid_set { ++ struct dm_target *ti; /* Target pointer. */ ++ ++ struct { ++ unsigned long flags; /* State flags. */ ++ spinlock_t in_lock; /* Protects central input list below. */ ++ struct bio_list in; /* Pending ios (central input list). */ ++ struct bio_list work; /* ios work set. */ ++ wait_queue_head_t suspendq; /* suspend synchronization. */ ++ atomic_t in_process; /* counter of queued bios (suspendq). */ ++ atomic_t in_process_max;/* counter of queued bios max. */ ++ ++ /* io work. */ ++ struct workqueue_struct *wq; ++ struct delayed_work dws; ++ } io; ++ ++ /* External locking. */ ++ struct dm_raid45_locking_type *locking; ++ ++ struct stripe_cache sc; /* Stripe cache for this set. */ ++ ++ /* Xor optimization. */ ++ struct { ++ struct xor_func *f; ++ unsigned chunks; ++ unsigned speed; ++ } xor; ++ ++ /* Recovery parameters. */ ++ struct recover { ++ struct dm_dirty_log *dl; /* Dirty log. */ ++ struct dm_region_hash *rh; /* Region hash. */ ++ ++ /* dm-mem-cache client resource context for recovery stripes. */ ++ struct dm_mem_cache_client *mem_cache_client; ++ ++ struct list_head stripes; /* List of recovery stripes. */ ++ ++ region_t nr_regions; ++ region_t nr_regions_to_recover; ++ region_t nr_regions_recovered; ++ unsigned long start_jiffies; ++ unsigned long end_jiffies; ++ ++ unsigned bandwidth; /* Recovery bandwidth [%]. */ ++ unsigned bandwidth_work; /* Recovery bandwidth [factor]. */ ++ unsigned bandwidth_parm; /* " constructor parm. */ ++ unsigned io_size; /* io size <= chunk size. */ ++ unsigned io_size_parm; /* io size ctr parameter. */ ++ ++ /* recovery io throttling. */ ++ atomic_t io_count[2]; /* counter recover/regular io. */ ++ unsigned long last_jiffies; ++ ++ struct dm_region *reg; /* Actual region to recover. */ ++ sector_t pos; /* Position within region to recover. */ ++ sector_t end; /* End of region to recover. */ ++ } recover; ++ ++ /* RAID set parameters. */ ++ struct { ++ struct raid_type *raid_type; /* RAID type (eg, RAID4). */ ++ unsigned raid_parms; /* # variable raid parameters. */ ++ ++ unsigned chunk_size; /* Sectors per chunk. */ ++ unsigned chunk_size_parm; ++ unsigned chunk_mask; /* Mask for amount. */ ++ unsigned chunk_shift; /* rsector chunk size shift. */ ++ ++ unsigned io_size; /* Sectors per io. */ ++ unsigned io_size_parm; ++ unsigned io_mask; /* Mask for amount. */ ++ unsigned io_shift_mask; /* Mask for raid_address(). */ ++ unsigned io_shift; /* rsector io size shift. */ ++ unsigned pages_per_io; /* Pages per io. */ ++ ++ sector_t sectors_per_dev; /* Sectors per device. */ ++ ++ atomic_t failed_devs; /* Amount of devices failed. */ ++ ++ /* Index of device to initialize. */ ++ int dev_to_init; ++ int dev_to_init_parm; ++ ++ /* Raid devices dynamically allocated. */ ++ unsigned raid_devs; /* # of RAID devices below. */ ++ unsigned data_devs; /* # of RAID data devices. */ ++ ++ int ei; /* index of failed RAID device. */ ++ ++ /* index of dedicated parity device (i.e. RAID4). */ ++ int pi; ++ int pi_parm; /* constructor parm for status output. */ ++ } set; ++ ++ /* REMOVEME: devel stats counters. */ ++ atomic_t stats[S_NR_STATS]; ++ ++ /* Dynamically allocated temporary pointers for xor(). */ ++ unsigned long **data; ++ ++ /* Dynamically allocated RAID devices. Alignment? */ ++ struct raid_dev dev[0]; ++}; ++ ++ ++BITOPS(RS, Bandwidth, raid_set, RS_RECOVERY_BANDWIDTH) ++BITOPS(RS, CheckOverwrite, raid_set, RS_CHECK_OVERWRITE) ++BITOPS(RS, Dead, raid_set, RS_DEAD) ++BITOPS(RS, DevelStats, raid_set, RS_DEVEL_STATS) ++BITOPS(RS, IoError, raid_set, RS_IO_ERROR) ++BITOPS(RS, Recover, raid_set, RS_RECOVER) ++BITOPS(RS, RegionGet, raid_set, RS_REGION_GET) ++BITOPS(RS, ScBusy, raid_set, RS_SC_BUSY) ++BITOPS(RS, Suspended, raid_set, RS_SUSPENDED) ++#undef BITOPS ++ ++#define PageIO(page) PageChecked(page) ++#define AllowPageIO(page) SetPageChecked(page) ++#define ProhibitPageIO(page) ClearPageChecked(page) ++ ++/*----------------------------------------------------------------- ++ * Raid-4/5 set structures. ++ *---------------------------------------------------------------*/ ++/* RAID level definitions. */ ++enum raid_level { ++ raid4, ++ raid5, ++}; ++ ++/* Symmetric/Asymmetric, Left/Right parity rotating algorithms. */ ++enum raid_algorithm { ++ none, ++ left_asym, ++ right_asym, ++ left_sym, ++ right_sym, ++}; ++ ++struct raid_type { ++ const char *name; /* RAID algorithm. */ ++ const char *descr; /* Descriptor text for logging. */ ++ const unsigned parity_devs; /* # of parity devices. */ ++ const unsigned minimal_devs; /* minimal # of devices in set. */ ++ const enum raid_level level; /* RAID level. */ ++ const enum raid_algorithm algorithm; /* RAID algorithm. */ ++}; ++ ++/* Supported raid types and properties. */ ++static struct raid_type raid_types[] = { ++ {"raid4", "RAID4 (dedicated parity disk)", 1, 3, raid4, none}, ++ {"raid5_la", "RAID5 (left asymmetric)", 1, 3, raid5, left_asym}, ++ {"raid5_ra", "RAID5 (right asymmetric)", 1, 3, raid5, right_asym}, ++ {"raid5_ls", "RAID5 (left symmetric)", 1, 3, raid5, left_sym}, ++ {"raid5_rs", "RAID5 (right symmetric)", 1, 3, raid5, right_sym}, ++}; ++ ++/* Address as calculated by raid_address(). */ ++struct address { ++ sector_t key; /* Hash key (start address of stripe). */ ++ unsigned di, pi; /* Data and parity disks index. */ ++}; ++ ++/* REMOVEME: reset statistics counters. */ ++static void stats_reset(struct raid_set *rs) ++{ ++ unsigned s = S_NR_STATS; ++ ++ while (s--) ++ atomic_set(rs->stats + s, 0); ++} ++ ++/*---------------------------------------------------------------- ++ * RAID set management routines. ++ *--------------------------------------------------------------*/ ++/* ++ * Begin small helper functions. ++ */ ++/* Queue (optionally delayed) io work. */ ++static void wake_do_raid_delayed(struct raid_set *rs, unsigned long delay) ++{ ++ struct delayed_work *dws = &rs->io.dws; ++ ++ cancel_delayed_work(dws); ++ queue_delayed_work(rs->io.wq, dws, delay); ++} ++ ++/* Queue io work immediately (called from region hash too). */ ++static INLINE void wake_do_raid(void *context) ++{ ++ wake_do_raid_delayed(context, 0); ++} ++ ++/* Wait until all io has been processed. */ ++static INLINE void wait_ios(struct raid_set *rs) ++{ ++ wait_event(rs->io.suspendq, !atomic_read(&rs->io.in_process)); ++} ++ ++/* Declare io queued to device. */ ++static INLINE void io_dev_queued(struct raid_dev *dev) ++{ ++ set_bit(IO_QUEUED, &dev->flags); ++} ++ ++/* Io on device and reset ? */ ++static inline int io_dev_clear(struct raid_dev *dev) ++{ ++ return test_and_clear_bit(IO_QUEUED, &dev->flags); ++} ++ ++/* Get an io reference. */ ++static INLINE void io_get(struct raid_set *rs) ++{ ++ int p = atomic_inc_return(&rs->io.in_process); ++ ++ if (p > atomic_read(&rs->io.in_process_max)) ++ atomic_set(&rs->io.in_process_max, p); /* REMOVEME: max. */ ++} ++ ++/* Put the io reference and conditionally wake io waiters. */ ++static INLINE void io_put(struct raid_set *rs) ++{ ++ /* Intel: rebuild data corrupter? */ ++ if (!atomic_read(&rs->io.in_process)) { ++ DMERR("%s would go negative!!!", __func__); ++ return; ++ } ++ ++ if (atomic_dec_and_test(&rs->io.in_process)) ++ wake_up(&rs->io.suspendq); ++} ++ ++/* Calculate device sector offset. */ ++static INLINE sector_t _sector(struct raid_set *rs, struct bio *bio) ++{ ++ sector_t sector = bio->bi_sector; ++ ++ sector_div(sector, rs->set.data_devs); ++ return sector; ++} ++ ++/* Test device operational. */ ++static INLINE int dev_operational(struct raid_set *rs, unsigned p) ++{ ++ return !test_bit(DEVICE_FAILED, &rs->dev[p].flags); ++} ++ ++/* Return # of active stripes in stripe cache. */ ++static INLINE int sc_active(struct stripe_cache *sc) ++{ ++ return atomic_read(&sc->active_stripes); ++} ++ ++/* Test io pending on stripe. */ ++static INLINE int stripe_io(struct stripe *stripe) ++{ ++ return atomic_read(&stripe->io.pending); ++} ++ ++static INLINE void stripe_io_inc(struct stripe *stripe) ++{ ++ atomic_inc(&stripe->io.pending); ++} ++ ++static INLINE void stripe_io_dec(struct stripe *stripe) ++{ ++ atomic_dec(&stripe->io.pending); ++} ++ ++/* Wrapper needed by for_each_io_dev(). */ ++static void _stripe_io_inc(struct stripe *stripe, unsigned p) ++{ ++ stripe_io_inc(stripe); ++} ++ ++/* Error a stripe. */ ++static INLINE void stripe_error(struct stripe *stripe, struct page *page) ++{ ++ SetStripeError(stripe); ++ SetPageError(page); ++ atomic_inc(RS(stripe->sc)->stats + S_STRIPE_ERROR); ++} ++ ++/* Page IOed ok. */ ++enum dirty_type { CLEAN, DIRTY }; ++static INLINE void page_set(struct page *page, enum dirty_type type) ++{ ++ switch (type) { ++ case DIRTY: ++ SetPageDirty(page); ++ AllowPageIO(page); ++ break; ++ ++ case CLEAN: ++ ClearPageDirty(page); ++ break; ++ ++ default: ++ BUG(); ++ } ++ ++ SetPageUptodate(page); ++ ClearPageError(page); ++} ++ ++/* Return region state for a sector. */ ++static INLINE int ++region_state(struct raid_set *rs, sector_t sector, unsigned long state) ++{ ++ struct dm_region_hash *rh = rs->recover.rh; ++ ++ return RSRecover(rs) ? ++ (dm_rh_get_state(rh, dm_rh_sector_to_region(rh, sector), 1) & ++ state) : 0; ++} ++ ++/* Check maximum devices which may fail in a raid set. */ ++static inline int raid_set_degraded(struct raid_set *rs) ++{ ++ return RSIoError(rs); ++} ++ ++/* Check # of devices which may fail in a raid set. */ ++static INLINE int raid_set_operational(struct raid_set *rs) ++{ ++ /* Too many failed devices -> BAD. */ ++ return atomic_read(&rs->set.failed_devs) <= ++ rs->set.raid_type->parity_devs; ++} ++ ++/* ++ * Return true in case a page_list should be read/written ++ * ++ * Conditions to read/write: ++ * o 1st page in list not uptodate ++ * o 1st page in list dirty ++ * o if we optimized io away, we flag it using the pages checked bit. ++ */ ++static INLINE unsigned page_io(struct page *page) ++{ ++ /* Optimization: page was flagged to need io during first run. */ ++ if (PagePrivate(page)) { ++ ClearPagePrivate(page); ++ return 1; ++ } ++ ++ /* Avoid io if prohibited or a locked page. */ ++ if (!PageIO(page) || PageLocked(page)) ++ return 0; ++ ++ if (!PageUptodate(page) || PageDirty(page)) { ++ /* Flag page needs io for second run optimization. */ ++ SetPagePrivate(page); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* Call a function on each page list needing io. */ ++static INLINE unsigned ++for_each_io_dev(struct raid_set *rs, struct stripe *stripe, ++ void (*f_io)(struct stripe *stripe, unsigned p)) ++{ ++ unsigned p = rs->set.raid_devs, r = 0; ++ ++ while (p--) { ++ if (page_io(PAGE(stripe, p))) { ++ f_io(stripe, p); ++ r++; ++ } ++ } ++ ++ return r; ++} ++ ++/* Reconstruct a particular device ?. */ ++static INLINE int dev_to_init(struct raid_set *rs) ++{ ++ return rs->set.dev_to_init > -1; ++} ++ ++/* ++ * Index of device to calculate parity on. ++ * Either the parity device index *or* the selected device to init ++ * after a spare replacement. ++ */ ++static INLINE unsigned dev_for_parity(struct stripe *stripe) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ ++ return dev_to_init(rs) ? rs->set.dev_to_init : stripe->idx.parity; ++} ++ ++/* Return the index of the device to be recovered. */ ++static int idx_get(struct raid_set *rs) ++{ ++ /* Avoid to read in the pages to be reconstructed anyway. */ ++ if (dev_to_init(rs)) ++ return rs->set.dev_to_init; ++ else if (rs->set.raid_type->level == raid4) ++ return rs->set.pi; ++ ++ return -1; ++} ++ ++/* RAID set congested function. */ ++static int raid_set_congested(void *congested_data, int bdi_bits) ++{ ++ struct raid_set *rs = congested_data; ++ int r = 0; /* Assume uncongested. */ ++ unsigned p = rs->set.raid_devs; ++ ++ /* If any of our component devices are overloaded. */ ++ while (p--) { ++ struct request_queue *q = bdev_get_queue(rs->dev[p].dev->bdev); ++ ++ r |= bdi_congested(&q->backing_dev_info, bdi_bits); ++ } ++ ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + (r ? S_CONGESTED : S_NOT_CONGESTED)); ++ return r; ++} ++ ++/* Display RAID set dead message once. */ ++static void raid_set_dead(struct raid_set *rs) ++{ ++ if (!TestSetRSDead(rs)) { ++ unsigned p; ++ char buf[BDEVNAME_SIZE]; ++ ++ DMERR("FATAL: too many devices failed -> RAID set dead"); ++ ++ for (p = 0; p < rs->set.raid_devs; p++) { ++ if (!dev_operational(rs, p)) ++ DMERR("device /dev/%s failed", ++ bdevname(rs->dev[p].dev->bdev, buf)); ++ } ++ } ++} ++ ++/* RAID set degrade check. */ ++static INLINE int ++raid_set_check_and_degrade(struct raid_set *rs, ++ struct stripe *stripe, unsigned p) ++{ ++ if (test_and_set_bit(DEVICE_FAILED, &rs->dev[p].flags)) ++ return -EPERM; ++ ++ /* Through an event in case of member device errors. */ ++ dm_table_event(rs->ti->table); ++ atomic_inc(&rs->set.failed_devs); ++ ++ /* Only log the first member error. */ ++ if (!TestSetRSIoError(rs)) { ++ char buf[BDEVNAME_SIZE]; ++ ++ /* Store index for recovery. */ ++ mb(); ++ rs->set.ei = p; ++ mb(); ++ ++ DMERR("CRITICAL: %sio error on device /dev/%s " ++ "in region=%llu; DEGRADING RAID set", ++ stripe ? "" : "FAKED ", ++ bdevname(rs->dev[p].dev->bdev, buf), ++ (unsigned long long) (stripe ? stripe->key : 0)); ++ DMERR("further device error messages suppressed"); ++ } ++ ++ return 0; ++} ++ ++static void ++raid_set_check_degrade(struct raid_set *rs, struct stripe *stripe) ++{ ++ unsigned p = rs->set.raid_devs; ++ ++ while (p--) { ++ struct page *page = PAGE(stripe, p); ++ ++ if (PageError(page)) { ++ ClearPageError(page); ++ raid_set_check_and_degrade(rs, stripe, p); ++ } ++ } ++} ++ ++/* RAID set upgrade check. */ ++static int raid_set_check_and_upgrade(struct raid_set *rs, unsigned p) ++{ ++ if (!test_and_clear_bit(DEVICE_FAILED, &rs->dev[p].flags)) ++ return -EPERM; ++ ++ if (atomic_dec_and_test(&rs->set.failed_devs)) { ++ ClearRSIoError(rs); ++ rs->set.ei = -1; ++ } ++ ++ return 0; ++} ++ ++/* Lookup a RAID device by name or by major:minor number. */ ++union dev_lookup { ++ const char *dev_name; ++ struct raid_dev *dev; ++}; ++enum lookup_type { byname, bymajmin, bynumber }; ++static int raid_dev_lookup(struct raid_set *rs, enum lookup_type by, ++ union dev_lookup *dl) ++{ ++ unsigned p; ++ ++ /* ++ * Must be an incremental loop, because the device array ++ * can have empty slots still on calls from raid_ctr() ++ */ ++ for (p = 0; p < rs->set.raid_devs; p++) { ++ char buf[BDEVNAME_SIZE]; ++ struct raid_dev *dev = rs->dev + p; ++ ++ if (!dev->dev) ++ break; ++ ++ /* Format dev string appropriately if necessary. */ ++ if (by == byname) ++ bdevname(dev->dev->bdev, buf); ++ else if (by == bymajmin) ++ format_dev_t(buf, dev->dev->bdev->bd_dev); ++ ++ /* Do the actual check. */ ++ if (by == bynumber) { ++ if (dl->dev->dev->bdev->bd_dev == ++ dev->dev->bdev->bd_dev) ++ return p; ++ } else if (!strcmp(dl->dev_name, buf)) ++ return p; ++ } ++ ++ return -ENODEV; ++} ++ ++/* End io wrapper. */ ++static INLINE void ++_bio_endio(struct raid_set *rs, struct bio *bio, int error) ++{ ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + (bio_data_dir(bio) == WRITE ? ++ S_BIOS_ENDIO_WRITE : S_BIOS_ENDIO_READ)); ++ bio_endio(bio, error); ++ io_put(rs); /* Wake any suspend waiters. */ ++} ++ ++/* ++ * End small helper functions. ++ */ ++ ++ ++/* ++ * Stripe hash functions ++ */ ++/* Initialize/destroy stripe hash. */ ++static int hash_init(struct stripe_hash *hash, unsigned stripes) ++{ ++ unsigned buckets = 2, max_buckets = stripes / 4; ++ unsigned hash_primes[] = { ++ /* Table of primes for hash_fn/table size optimization. */ ++ 3, 7, 13, 27, 53, 97, 193, 389, 769, ++ 1543, 3079, 6151, 12289, 24593, ++ }; ++ ++ /* Calculate number of buckets (2^^n <= stripes / 4). */ ++ while (buckets < max_buckets) ++ buckets <<= 1; ++ ++ /* Allocate stripe hash. */ ++ hash->hash = vmalloc(buckets * sizeof(*hash->hash)); ++ if (!hash->hash) ++ return -ENOMEM; ++ ++ hash->buckets = buckets; ++ hash->mask = buckets - 1; ++ hash->shift = ffs(buckets); ++ if (hash->shift > ARRAY_SIZE(hash_primes) + 1) ++ hash->shift = ARRAY_SIZE(hash_primes) + 1; ++ ++ BUG_ON(hash->shift - 2 > ARRAY_SIZE(hash_primes) + 1); ++ hash->prime = hash_primes[hash->shift - 2]; ++ ++ /* Initialize buckets. */ ++ while (buckets--) ++ INIT_LIST_HEAD(hash->hash + buckets); ++ ++ return 0; ++} ++ ++static INLINE void hash_exit(struct stripe_hash *hash) ++{ ++ if (hash->hash) { ++ vfree(hash->hash); ++ hash->hash = NULL; ++ } ++} ++ ++/* List add (head/tail/locked/unlocked) inlines. */ ++enum list_lock_type { LIST_LOCKED, LIST_UNLOCKED }; ++#define LIST_DEL(name, list) \ ++static void stripe_ ## name ## _del(struct stripe *stripe, \ ++ enum list_lock_type lock) { \ ++ struct list_head *lh = stripe->lists + (list); \ ++ spinlock_t *l = NULL; \ ++\ ++ if (lock == LIST_LOCKED) { \ ++ l = stripe->sc->locks + LOCK_LRU; \ ++ spin_lock_irq(l); \ ++ } \ ++\ ++\ ++ if (!list_empty(lh)) \ ++ list_del_init(lh); \ ++\ ++ if (lock == LIST_LOCKED) \ ++ spin_unlock_irq(l); \ ++} ++ ++LIST_DEL(hash, LIST_HASH) ++LIST_DEL(lru, LIST_LRU) ++#undef LIST_DEL ++ ++enum list_pos_type { POS_HEAD, POS_TAIL }; ++#define LIST_ADD(name, list) \ ++static void stripe_ ## name ## _add(struct stripe *stripe, \ ++ enum list_pos_type pos, \ ++ enum list_lock_type lock) { \ ++ struct list_head *lh = stripe->lists + (list); \ ++ struct stripe_cache *sc = stripe->sc; \ ++ spinlock_t *l = NULL; \ ++\ ++ if (lock == LIST_LOCKED) { \ ++ l = sc->locks + LOCK_LRU; \ ++ spin_lock_irq(l); \ ++ } \ ++\ ++ if (list_empty(lh)) { \ ++ if (pos == POS_HEAD) \ ++ list_add(lh, sc->lists + (list)); \ ++ else \ ++ list_add_tail(lh, sc->lists + (list)); \ ++ } \ ++\ ++ if (lock == LIST_LOCKED) \ ++ spin_unlock_irq(l); \ ++} ++ ++LIST_ADD(endio, LIST_ENDIO) ++LIST_ADD(io, LIST_IO) ++LIST_ADD(lru, LIST_LRU) ++#undef LIST_ADD ++ ++#define POP(list) \ ++ do { \ ++ if (list_empty(sc->lists + list)) \ ++ stripe = NULL; \ ++ else { \ ++ stripe = list_first_entry(&sc->lists[list], \ ++ struct stripe, \ ++ lists[list]); \ ++ list_del_init(&stripe->lists[list]); \ ++ } \ ++ } while (0); ++ ++/* Pop an available stripe off the lru list. */ ++static struct stripe *stripe_lru_pop(struct stripe_cache *sc) ++{ ++ struct stripe *stripe; ++ spinlock_t *lock = sc->locks + LOCK_LRU; ++ ++ spin_lock_irq(lock); ++ POP(LIST_LRU); ++ spin_unlock_irq(lock); ++ ++ if (stripe) ++ /* Remove from hash before reuse. */ ++ stripe_hash_del(stripe, LIST_UNLOCKED); ++ ++ return stripe; ++} ++ ++static inline unsigned hash_fn(struct stripe_hash *hash, sector_t key) ++{ ++ return (unsigned) (((key * hash->prime) >> hash->shift) & hash->mask); ++} ++ ++static inline struct list_head * ++hash_bucket(struct stripe_hash *hash, sector_t key) ++{ ++ return hash->hash + hash_fn(hash, key); ++} ++ ++/* Insert an entry into a hash. */ ++static inline void hash_insert(struct stripe_hash *hash, struct stripe *stripe) ++{ ++ list_add(stripe->lists + LIST_HASH, hash_bucket(hash, stripe->key)); ++} ++ ++/* Insert an entry into the stripe hash. */ ++static inline void ++sc_insert(struct stripe_cache *sc, struct stripe *stripe) ++{ ++ hash_insert(&sc->hash, stripe); ++} ++ ++/* Lookup an entry in the stripe hash. */ ++static inline struct stripe * ++stripe_lookup(struct stripe_cache *sc, sector_t key) ++{ ++ unsigned c = 0; ++ struct stripe *stripe; ++ struct list_head *bucket = hash_bucket(&sc->hash, key); ++ ++ list_for_each_entry(stripe, bucket, lists[LIST_HASH]) { ++ /* REMOVEME: statisics. */ ++ if (++c > atomic_read(RS(sc)->stats + S_MAX_LOOKUP)) ++ atomic_set(RS(sc)->stats + S_MAX_LOOKUP, c); ++ ++ if (stripe->key == key) ++ return stripe; ++ } ++ ++ return NULL; ++} ++ ++/* Resize the stripe cache hash on size changes. */ ++static int hash_resize(struct stripe_cache *sc) ++{ ++ /* Resize threshold reached? */ ++ if (atomic_read(&sc->stripes) > 2 * atomic_read(&sc->stripes_last) ++ || atomic_read(&sc->stripes) < atomic_read(&sc->stripes_last) / 4) { ++ int r; ++ struct stripe_hash hash, hash_tmp; ++ spinlock_t *lock; ++ ++ r = hash_init(&hash, atomic_read(&sc->stripes)); ++ if (r) ++ return r; ++ ++ lock = sc->locks + LOCK_LRU; ++ spin_lock_irq(lock); ++ if (sc->hash.hash) { ++ unsigned b = sc->hash.buckets; ++ struct list_head *pos, *tmp; ++ ++ /* Walk old buckets and insert into new. */ ++ while (b--) { ++ list_for_each_safe(pos, tmp, sc->hash.hash + b) ++ hash_insert(&hash, ++ list_entry(pos, struct stripe, ++ lists[LIST_HASH])); ++ } ++ ++ } ++ ++ memcpy(&hash_tmp, &sc->hash, sizeof(hash_tmp)); ++ memcpy(&sc->hash, &hash, sizeof(sc->hash)); ++ atomic_set(&sc->stripes_last, atomic_read(&sc->stripes)); ++ spin_unlock_irq(lock); ++ ++ hash_exit(&hash_tmp); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Stripe cache locking functions ++ */ ++/* Dummy lock function for local RAID4+5. */ ++static void *no_lock(sector_t key, enum dm_lock_type type) ++{ ++ return &no_lock; ++} ++ ++/* Dummy unlock function for local RAID4+5. */ ++static void no_unlock(void *lock_handle) ++{ ++} ++ ++/* No locking (for local RAID 4+5). */ ++static struct dm_raid45_locking_type locking_none = { ++ .lock = no_lock, ++ .unlock = no_unlock, ++}; ++ ++/* Clustered RAID 4+5. */ ++/* FIXME: code this. */ ++static struct dm_raid45_locking_type locking_cluster = { ++ .lock = no_lock, ++ .unlock = no_unlock, ++}; ++ ++/* Lock a stripe (for clustering). */ ++static int ++stripe_lock(struct raid_set *rs, struct stripe *stripe, int rw, sector_t key) ++{ ++ stripe->lock = rs->locking->lock(key, rw == READ ? DM_RAID45_SHARED : ++ DM_RAID45_EX); ++ return stripe->lock ? 0 : -EPERM; ++} ++ ++/* Unlock a stripe (for clustering). */ ++static void stripe_unlock(struct raid_set *rs, struct stripe *stripe) ++{ ++ rs->locking->unlock(stripe->lock); ++ stripe->lock = NULL; ++} ++ ++/* ++ * Stripe cache functions. ++ */ ++/* ++ * Invalidate all page lists pages of a stripe. ++ * ++ * I only keep state for the whole list in the first page. ++ */ ++static INLINE void ++stripe_pages_invalidate(struct stripe *stripe) ++{ ++ unsigned p = RS(stripe->sc)->set.raid_devs; ++ ++ while (p--) { ++ struct page *page = PAGE(stripe, p); ++ ++ ProhibitPageIO(page); ++ ClearPageChecked(page); ++ ClearPageDirty(page); ++ ClearPageError(page); ++ __clear_page_locked(page); ++ ClearPagePrivate(page); ++ ClearPageUptodate(page); ++ } ++} ++ ++/* Prepare stripe for (re)use. */ ++static INLINE void stripe_invalidate(struct stripe *stripe) ++{ ++ stripe->io.flags = 0; ++ stripe_pages_invalidate(stripe); ++} ++ ++/* Allow io on all chunks of a stripe. */ ++static INLINE void stripe_allow_io(struct stripe *stripe) ++{ ++ unsigned p = RS(stripe->sc)->set.raid_devs; ++ ++ while (p--) ++ AllowPageIO(PAGE(stripe, p)); ++} ++ ++/* Initialize a stripe. */ ++static void ++stripe_init(struct stripe_cache *sc, struct stripe *stripe) ++{ ++ unsigned p = RS(sc)->set.raid_devs; ++ unsigned i; ++ ++ /* Work all io chunks. */ ++ while (p--) { ++ struct stripe_set *ss = stripe->ss + p; ++ ++ stripe->obj[p].private = ss; ++ ss->stripe = stripe; ++ ++ i = ARRAY_SIZE(ss->bl); ++ while (i--) ++ bio_list_init(ss->bl + i); ++ } ++ ++ stripe->sc = sc; ++ ++ i = ARRAY_SIZE(stripe->lists); ++ while (i--) ++ INIT_LIST_HEAD(stripe->lists + i); ++ ++ atomic_set(&stripe->cnt, 0); ++ atomic_set(&stripe->io.pending, 0); ++ ++ stripe_invalidate(stripe); ++} ++ ++/* Number of pages per chunk. */ ++static inline unsigned chunk_pages(unsigned io_size) ++{ ++ return dm_div_up(io_size, SECTORS_PER_PAGE); ++} ++ ++/* Number of pages per stripe. */ ++static inline unsigned stripe_pages(struct raid_set *rs, unsigned io_size) ++{ ++ return chunk_pages(io_size) * rs->set.raid_devs; ++} ++ ++/* Initialize part of page_list (recovery). */ ++static INLINE void stripe_zero_pl_part(struct stripe *stripe, unsigned p, ++ unsigned start, unsigned count) ++{ ++ unsigned pages = chunk_pages(count); ++ /* Get offset into the page_list. */ ++ struct page_list *pl = pl_elem(PL(stripe, p), start / SECTORS_PER_PAGE); ++ ++ BUG_ON(!pl); ++ while (pl && pages--) { ++ BUG_ON(!pl->page); ++ memset(page_address(pl->page), 0, PAGE_SIZE); ++ pl = pl->next; ++ } ++} ++ ++/* Initialize parity chunk of stripe. */ ++static INLINE void stripe_zero_chunk(struct stripe *stripe, unsigned p) ++{ ++ stripe_zero_pl_part(stripe, p, 0, stripe->io.size); ++} ++ ++/* Return dynamic stripe structure size. */ ++static INLINE size_t stripe_size(struct raid_set *rs) ++{ ++ return sizeof(struct stripe) + ++ rs->set.raid_devs * sizeof(struct stripe_set); ++} ++ ++/* Allocate a stripe and its memory object. */ ++/* XXX adjust to cope with stripe cache and recovery stripe caches. */ ++enum grow { SC_GROW, SC_KEEP }; ++static struct stripe *stripe_alloc(struct stripe_cache *sc, ++ struct dm_mem_cache_client *mc, ++ enum grow grow) ++{ ++ int r; ++ struct stripe *stripe; ++ ++ stripe = kmem_cache_zalloc(sc->kc.cache, GFP_KERNEL); ++ if (stripe) { ++ /* Grow the dm-mem-cache by one object. */ ++ if (grow == SC_GROW) { ++ r = dm_mem_cache_grow(mc, 1); ++ if (r) ++ goto err_free; ++ } ++ ++ stripe->obj = dm_mem_cache_alloc(mc); ++ if (!stripe->obj) ++ goto err_shrink; ++ ++ stripe_init(sc, stripe); ++ } ++ ++ return stripe; ++ ++err_shrink: ++ if (grow == SC_GROW) ++ dm_mem_cache_shrink(mc, 1); ++err_free: ++ kmem_cache_free(sc->kc.cache, stripe); ++ return NULL; ++} ++ ++/* ++ * Free a stripes memory object, shrink the ++ * memory cache and free the stripe itself ++ */ ++static void stripe_free(struct stripe *stripe, struct dm_mem_cache_client *mc) ++{ ++ dm_mem_cache_free(mc, stripe->obj); ++ dm_mem_cache_shrink(mc, 1); ++ kmem_cache_free(stripe->sc->kc.cache, stripe); ++} ++ ++/* Free the recovery stripe. */ ++static void stripe_recover_free(struct raid_set *rs) ++{ ++ struct recover *rec = &rs->recover; ++ struct list_head *stripes = &rec->stripes; ++ ++ while (!list_empty(stripes)) { ++ struct stripe *stripe = list_first_entry(stripes, struct stripe, ++ lists[LIST_RECOVER]); ++ list_del(stripe->lists + LIST_RECOVER); ++ stripe_free(stripe, rec->mem_cache_client); ++ } ++} ++ ++/* Push a stripe safely onto the endio list to be handled by do_endios(). */ ++static INLINE void stripe_endio_push(struct stripe *stripe) ++{ ++ int wake; ++ unsigned long flags; ++ struct stripe_cache *sc = stripe->sc; ++ spinlock_t *lock = sc->locks + LOCK_ENDIO; ++ ++ spin_lock_irqsave(lock, flags); ++ wake = list_empty(sc->lists + LIST_ENDIO); ++ stripe_endio_add(stripe, POS_HEAD, LIST_UNLOCKED); ++ spin_unlock_irqrestore(lock, flags); ++ ++ if (wake) ++ wake_do_raid(RS(sc)); ++} ++ ++/* Protected check for stripe cache endio list empty. */ ++static INLINE int stripe_endio_empty(struct stripe_cache *sc) ++{ ++ int r; ++ spinlock_t *lock = sc->locks + LOCK_ENDIO; ++ ++ spin_lock_irq(lock); ++ r = list_empty(sc->lists + LIST_ENDIO); ++ spin_unlock_irq(lock); ++ ++ return r; ++} ++ ++/* Pop a stripe off safely off the endio list. */ ++static struct stripe *stripe_endio_pop(struct stripe_cache *sc) ++{ ++ struct stripe *stripe; ++ spinlock_t *lock = sc->locks + LOCK_ENDIO; ++ ++ /* This runs in parallel with endio(). */ ++ spin_lock_irq(lock); ++ POP(LIST_ENDIO) ++ spin_unlock_irq(lock); ++ return stripe; ++} ++ ++#undef POP ++ ++/* Evict stripe from cache. */ ++static void stripe_evict(struct stripe *stripe) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ stripe_hash_del(stripe, LIST_UNLOCKED); /* Take off hash. */ ++ ++ if (list_empty(stripe->lists + LIST_LRU)) { ++ stripe_lru_add(stripe, POS_TAIL, LIST_LOCKED); ++ atomic_inc(rs->stats + S_EVICT); /* REMOVEME: statistics. */ ++ } ++} ++ ++/* Grow stripe cache. */ ++static int ++sc_grow(struct stripe_cache *sc, unsigned stripes, enum grow grow) ++{ ++ int r = 0; ++ struct raid_set *rs = RS(sc); ++ ++ /* Try to allocate this many (additional) stripes. */ ++ while (stripes--) { ++ struct stripe *stripe = ++ stripe_alloc(sc, sc->mem_cache_client, grow); ++ ++ if (likely(stripe)) { ++ stripe->io.size = rs->set.io_size; ++ stripe_lru_add(stripe, POS_TAIL, LIST_LOCKED); ++ atomic_inc(&sc->stripes); ++ } else { ++ r = -ENOMEM; ++ break; ++ } ++ } ++ ++ ClearRSScBusy(rs); ++ return r ? r : hash_resize(sc); ++} ++ ++/* Shrink stripe cache. */ ++static int sc_shrink(struct stripe_cache *sc, unsigned stripes) ++{ ++ int r = 0; ++ ++ /* Try to get unused stripe from LRU list. */ ++ while (stripes--) { ++ struct stripe *stripe; ++ ++ stripe = stripe_lru_pop(sc); ++ if (stripe) { ++ /* An lru stripe may never have ios pending! */ ++ BUG_ON(stripe_io(stripe)); ++ stripe_free(stripe, sc->mem_cache_client); ++ atomic_dec(&sc->stripes); ++ } else { ++ r = -ENOENT; ++ break; ++ } ++ } ++ ++ /* Check if stats are still sane. */ ++ if (atomic_read(&sc->max_active_stripes) > ++ atomic_read(&sc->stripes)) ++ atomic_set(&sc->max_active_stripes, 0); ++ ++ if (r) ++ return r; ++ ++ ClearRSScBusy(RS(sc)); ++ return hash_resize(sc); ++} ++ ++/* Create stripe cache. */ ++static int sc_init(struct raid_set *rs, unsigned stripes) ++{ ++ unsigned i, nr; ++ struct stripe_cache *sc = &rs->sc; ++ struct stripe *stripe; ++ struct recover *rec = &rs->recover; ++ ++ /* Initialize lists and locks. */ ++ i = ARRAY_SIZE(sc->lists); ++ while (i--) ++ INIT_LIST_HEAD(sc->lists + i); ++ ++ i = NR_LOCKS; ++ while (i--) ++ spin_lock_init(sc->locks + i); ++ ++ /* Initialize atomic variables. */ ++ atomic_set(&sc->stripes, 0); ++ atomic_set(&sc->stripes_last, 0); ++ atomic_set(&sc->stripes_to_shrink, 0); ++ atomic_set(&sc->active_stripes, 0); ++ atomic_set(&sc->max_active_stripes, 0); /* REMOVEME: statistics. */ ++ ++ /* ++ * We need a runtime unique # to suffix the kmem cache name ++ * because we'll have one for each active RAID set. ++ */ ++ nr = atomic_inc_return(&_stripe_sc_nr); ++ sprintf(sc->kc.name, "%s_%d", TARGET, nr); ++ sc->kc.cache = kmem_cache_create(sc->kc.name, stripe_size(rs), ++ 0, 0, NULL); ++ if (!sc->kc.cache) ++ return -ENOMEM; ++ ++ /* Create memory cache client context for RAID stripe cache. */ ++ sc->mem_cache_client = ++ dm_mem_cache_client_create(stripes, rs->set.raid_devs, ++ chunk_pages(rs->set.io_size)); ++ if (IS_ERR(sc->mem_cache_client)) ++ return PTR_ERR(sc->mem_cache_client); ++ ++ /* Create memory cache client context for RAID recovery stripe(s). */ ++ rec->mem_cache_client = ++ dm_mem_cache_client_create(MAX_RECOVER, rs->set.raid_devs, ++ chunk_pages(rec->io_size)); ++ if (IS_ERR(rec->mem_cache_client)) ++ return PTR_ERR(rec->mem_cache_client); ++ ++ /* Allocate stripe for set recovery. */ ++ /* XXX: cope with MAX_RECOVERY. */ ++ INIT_LIST_HEAD(&rec->stripes); ++ for (i = 0; i < MAX_RECOVER; i++) { ++ stripe = stripe_alloc(sc, rec->mem_cache_client, SC_KEEP); ++ if (!stripe) ++ return -ENOMEM; ++ ++ SetStripeRecover(stripe); ++ stripe->io.size = rec->io_size; ++ list_add(stripe->lists + LIST_RECOVER, &rec->stripes); ++ } ++ ++ /* ++ * Allocate the stripe objetcs from the ++ * cache and add them to the LRU list. ++ */ ++ return sc_grow(sc, stripes, SC_KEEP); ++} ++ ++/* Destroy the stripe cache. */ ++static void sc_exit(struct stripe_cache *sc) ++{ ++ if (sc->kc.cache) { ++ BUG_ON(sc_shrink(sc, atomic_read(&sc->stripes))); ++ kmem_cache_destroy(sc->kc.cache); ++ } ++ ++ if (sc->mem_cache_client) ++ dm_mem_cache_client_destroy(sc->mem_cache_client); ++ ++ ClearRSRecover(RS(sc)); ++ stripe_recover_free(RS(sc)); ++ if (RS(sc)->recover.mem_cache_client) ++ dm_mem_cache_client_destroy(RS(sc)->recover.mem_cache_client); ++ ++ hash_exit(&sc->hash); ++} ++ ++/* ++ * Calculate RAID address ++ * ++ * Delivers tuple with the index of the data disk holding the chunk ++ * in the set, the parity disks index and the start of the stripe ++ * within the address space of the set (used as the stripe cache hash key). ++ */ ++/* thx MD. */ ++static struct address * ++raid_address(struct raid_set *rs, sector_t sector, struct address *addr) ++{ ++ unsigned data_devs = rs->set.data_devs, di, pi, ++ raid_devs = rs->set.raid_devs; ++ sector_t stripe, tmp; ++ ++ /* ++ * chunk_number = sector / chunk_size ++ * stripe = chunk_number / data_devs ++ * di = stripe % data_devs; ++ */ ++ stripe = sector >> rs->set.chunk_shift; ++ di = sector_div(stripe, data_devs); ++ ++ switch (rs->set.raid_type->level) { ++ case raid5: ++ tmp = stripe; ++ pi = sector_div(tmp, raid_devs); ++ ++ switch (rs->set.raid_type->algorithm) { ++ case left_asym: /* Left asymmetric. */ ++ pi = data_devs - pi; ++ case right_asym: /* Right asymmetric. */ ++ if (di >= pi) ++ di++; ++ break; ++ ++ case left_sym: /* Left symmetric. */ ++ pi = data_devs - pi; ++ case right_sym: /* Right symmetric. */ ++ di = (pi + di + 1) % raid_devs; ++ break; ++ ++ default: ++ DMERR("Unknown RAID algorithm %d", ++ rs->set.raid_type->algorithm); ++ goto out; ++ } ++ ++ break; ++ ++ case raid4: ++ pi = rs->set.pi; ++ if (di >= pi) ++ di++; ++ break; ++ ++ default: ++ DMERR("Unknown RAID level %d", rs->set.raid_type->level); ++ goto out; ++ } ++ ++ /* ++ * Hash key = start offset on any single device of the RAID set; ++ * adjusted in case io size differs from chunk size. ++ */ ++ addr->key = (stripe << rs->set.chunk_shift) + ++ (sector & rs->set.io_shift_mask); ++ addr->di = di; ++ addr->pi = pi; ++ ++out: ++ return addr; ++} ++ ++/* ++ * Copy data across between stripe pages and bio vectors. ++ * ++ * Pay attention to data alignment in stripe and bio pages. ++ */ ++static void ++bio_copy_page_list(int rw, struct stripe *stripe, ++ struct page_list *pl, struct bio *bio) ++{ ++ unsigned i, page_offset; ++ void *page_addr; ++ struct raid_set *rs = RS(stripe->sc); ++ struct bio_vec *bv; ++ ++ /* Get start page in page list for this sector. */ ++ i = (bio->bi_sector & rs->set.io_mask) / SECTORS_PER_PAGE; ++ pl = pl_elem(pl, i); ++ ++ page_addr = page_address(pl->page); ++ page_offset = to_bytes(bio->bi_sector & (SECTORS_PER_PAGE - 1)); ++ ++ /* Walk all segments and copy data across between bio_vecs and pages. */ ++ bio_for_each_segment(bv, bio, i) { ++ int len = bv->bv_len, size; ++ unsigned bio_offset = 0; ++ void *bio_addr = __bio_kmap_atomic(bio, i, KM_USER0); ++redo: ++ size = (page_offset + len > PAGE_SIZE) ? ++ PAGE_SIZE - page_offset : len; ++ ++ if (rw == READ) ++ memcpy(bio_addr + bio_offset, ++ page_addr + page_offset, size); ++ else ++ memcpy(page_addr + page_offset, ++ bio_addr + bio_offset, size); ++ ++ page_offset += size; ++ if (page_offset == PAGE_SIZE) { ++ /* ++ * We reached the end of the chunk page -> ++ * need refer to the next one to copy more data. ++ */ ++ len -= size; ++ if (len) { ++ /* Get next page. */ ++ pl = pl->next; ++ BUG_ON(!pl); ++ page_addr = page_address(pl->page); ++ page_offset = 0; ++ bio_offset += size; ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_BIO_COPY_PL_NEXT); ++ goto redo; ++ } ++ } ++ ++ __bio_kunmap_atomic(bio_addr, KM_USER0); ++ } ++} ++ ++/* ++ * Xor optimization macros. ++ */ ++/* Xor data pointer declaration and initialization macros. */ ++#define DECLARE_2 unsigned long *d0 = data[0], *d1 = data[1] ++#define DECLARE_3 DECLARE_2, *d2 = data[2] ++#define DECLARE_4 DECLARE_3, *d3 = data[3] ++#define DECLARE_5 DECLARE_4, *d4 = data[4] ++#define DECLARE_6 DECLARE_5, *d5 = data[5] ++#define DECLARE_7 DECLARE_6, *d6 = data[6] ++#define DECLARE_8 DECLARE_7, *d7 = data[7] ++ ++/* Xor unrole macros. */ ++#define D2(n) d0[n] = d0[n] ^ d1[n] ++#define D3(n) D2(n) ^ d2[n] ++#define D4(n) D3(n) ^ d3[n] ++#define D5(n) D4(n) ^ d4[n] ++#define D6(n) D5(n) ^ d5[n] ++#define D7(n) D6(n) ^ d6[n] ++#define D8(n) D7(n) ^ d7[n] ++ ++#define X_2(macro, offset) macro(offset); macro(offset + 1); ++#define X_4(macro, offset) X_2(macro, offset); X_2(macro, offset + 2); ++#define X_8(macro, offset) X_4(macro, offset); X_4(macro, offset + 4); ++#define X_16(macro, offset) X_8(macro, offset); X_8(macro, offset + 8); ++#define X_32(macro, offset) X_16(macro, offset); X_16(macro, offset + 16); ++#define X_64(macro, offset) X_32(macro, offset); X_32(macro, offset + 32); ++ ++/* Define a _xor_#chunks_#xors_per_run() function. */ ++#define _XOR(chunks, xors_per_run) \ ++static void _xor ## chunks ## _ ## xors_per_run(unsigned long **data) \ ++{ \ ++ unsigned end = XOR_SIZE / sizeof(data[0]), i; \ ++ DECLARE_ ## chunks; \ ++\ ++ for (i = 0; i < end; i += xors_per_run) { \ ++ X_ ## xors_per_run(D ## chunks, i); \ ++ } \ ++} ++ ++/* Define xor functions for 2 - 8 chunks. */ ++#define MAKE_XOR_PER_RUN(xors_per_run) \ ++ _XOR(2, xors_per_run); _XOR(3, xors_per_run); \ ++ _XOR(4, xors_per_run); _XOR(5, xors_per_run); \ ++ _XOR(6, xors_per_run); _XOR(7, xors_per_run); \ ++ _XOR(8, xors_per_run); ++ ++MAKE_XOR_PER_RUN(8) /* Define _xor_*_8() functions. */ ++MAKE_XOR_PER_RUN(16) /* Define _xor_*_16() functions. */ ++MAKE_XOR_PER_RUN(32) /* Define _xor_*_32() functions. */ ++MAKE_XOR_PER_RUN(64) /* Define _xor_*_64() functions. */ ++ ++#define MAKE_XOR(xors_per_run) \ ++struct { \ ++ void (*f)(unsigned long **); \ ++} static xor_funcs ## xors_per_run[] = { \ ++ { NULL }, \ ++ { NULL }, \ ++ { _xor2_ ## xors_per_run }, \ ++ { _xor3_ ## xors_per_run }, \ ++ { _xor4_ ## xors_per_run }, \ ++ { _xor5_ ## xors_per_run }, \ ++ { _xor6_ ## xors_per_run }, \ ++ { _xor7_ ## xors_per_run }, \ ++ { _xor8_ ## xors_per_run }, \ ++}; \ ++\ ++static void xor_ ## xors_per_run(unsigned n, unsigned long **data) \ ++{ \ ++ /* Call respective function for amount of chunks. */ \ ++ xor_funcs ## xors_per_run[n].f(data); \ ++} ++ ++/* Define xor_8() - xor_64 functions. */ ++MAKE_XOR(8) ++MAKE_XOR(16) ++MAKE_XOR(32) ++MAKE_XOR(64) ++ ++/* Maximum number of chunks, which can be xor'ed in one go. */ ++#define XOR_CHUNKS_MAX (ARRAY_SIZE(xor_funcs8) - 1) ++ ++struct xor_func { ++ xor_function_t f; ++ const char *name; ++} static xor_funcs[] = { ++ {xor_8, "xor_8"}, ++ {xor_16, "xor_16"}, ++ {xor_32, "xor_32"}, ++ {xor_64, "xor_64"}, ++}; ++ ++/* ++ * Calculate crc. ++ * ++ * This indexes into the page list of the stripe. ++ * ++ * All chunks will be xored into the parity chunk ++ * in maximum groups of xor.chunks. ++ * ++ * FIXME: try mapping the pages on discontiguous memory. ++ */ ++static void xor(struct stripe *stripe, unsigned pi, unsigned sector) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ unsigned max_chunks = rs->xor.chunks, n, p; ++ unsigned o = sector / SECTORS_PER_PAGE; /* Offset into the page_list. */ ++ unsigned long **d = rs->data; ++ xor_function_t xor_f = rs->xor.f->f; ++ ++ /* Address of parity page to xor into. */ ++ d[0] = page_address(pl_elem(PL(stripe, pi), o)->page); ++ ++ /* Preset pointers to data pages. */ ++ for (n = 1, p = rs->set.raid_devs; p--; ) { ++ if (p != pi && PageIO(PAGE(stripe, p))) ++ d[n++] = page_address(pl_elem(PL(stripe, p), o)->page); ++ ++ /* If max chunks -> xor .*/ ++ if (n == max_chunks) { ++ xor_f(n, d); ++ n = 1; ++ } ++ } ++ ++ /* If chunks -> xor. */ ++ if (n > 1) ++ xor_f(n, d); ++ ++ /* Set parity page uptodate and clean. */ ++ page_set(PAGE(stripe, pi), CLEAN); ++} ++ ++/* Common xor loop through all stripe page lists. */ ++static void common_xor(struct stripe *stripe, sector_t count, ++ unsigned off, unsigned p) ++{ ++ unsigned sector; ++ ++ for (sector = off; sector < count; sector += SECTORS_PER_XOR) ++ xor(stripe, p, sector); ++ ++ atomic_inc(RS(stripe->sc)->stats + S_XORS); /* REMOVEME: statistics. */ ++} ++ ++/* ++ * Calculate parity sectors on intact stripes. ++ * ++ * Need to calculate raid address for recover stripe, because its ++ * chunk sizes differs and is typically larger than io chunk size. ++ */ ++static void parity_xor(struct stripe *stripe) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ unsigned chunk_size = rs->set.chunk_size, ++ io_size = stripe->io.size, ++ xor_size = chunk_size > io_size ? io_size : chunk_size; ++ sector_t off; ++ ++ /* This can be the recover stripe with a larger io size. */ ++ for (off = 0; off < io_size; off += xor_size) { ++ unsigned pi; ++ ++ /* ++ * Recover stripe likely is bigger than regular io ++ * ones and has no precalculated parity disk index -> ++ * need to calculate RAID address. ++ */ ++ if (unlikely(StripeRecover(stripe))) { ++ struct address addr; ++ ++ raid_address(rs, ++ (stripe->key + off) * rs->set.data_devs, ++ &addr); ++ pi = addr.pi; ++ stripe_zero_pl_part(stripe, pi, off, ++ rs->set.chunk_size); ++ } else ++ pi = stripe->idx.parity; ++ ++ common_xor(stripe, xor_size, off, pi); ++ page_set(PAGE(stripe, pi), DIRTY); ++ } ++} ++ ++/* Reconstruct missing chunk. */ ++static void reconstruct_xor(struct stripe *stripe) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ int p = stripe->idx.recover; ++ ++ BUG_ON(p < 0); ++ ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + (raid_set_degraded(rs) ? ++ S_RECONSTRUCT_EI : S_RECONSTRUCT_DEV)); ++ ++ /* Zero chunk to be reconstructed. */ ++ stripe_zero_chunk(stripe, p); ++ common_xor(stripe, stripe->io.size, 0, p); ++} ++ ++/* ++ * Try getting a stripe either from the hash or from the lru list ++ */ ++static inline void _stripe_get(struct stripe *stripe) ++{ ++ atomic_inc(&stripe->cnt); ++} ++ ++static struct stripe *stripe_get(struct raid_set *rs, struct address *addr) ++{ ++ struct stripe_cache *sc = &rs->sc; ++ struct stripe *stripe; ++ ++ stripe = stripe_lookup(sc, addr->key); ++ if (stripe) { ++ _stripe_get(stripe); ++ /* Remove from the lru list if on. */ ++ stripe_lru_del(stripe, LIST_LOCKED); ++ atomic_inc(rs->stats + S_HITS_1ST); /* REMOVEME: statistics. */ ++ } else { ++ /* Second try to get an LRU stripe. */ ++ stripe = stripe_lru_pop(sc); ++ if (stripe) { ++ _stripe_get(stripe); ++ /* Invalidate before reinserting with changed key. */ ++ stripe_invalidate(stripe); ++ stripe->key = addr->key; ++ stripe->region = dm_rh_sector_to_region(rs->recover.rh, ++ addr->key); ++ stripe->idx.parity = addr->pi; ++ sc_insert(sc, stripe); ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_INSCACHE); ++ } ++ } ++ ++ return stripe; ++} ++ ++/* ++ * Decrement reference count on a stripe. ++ * ++ * Move it to list of LRU stripes if zero. ++ */ ++static void stripe_put(struct stripe *stripe) ++{ ++ if (atomic_dec_and_test(&stripe->cnt)) { ++ if (TestClearStripeActive(stripe)) ++ atomic_dec(&stripe->sc->active_stripes); ++ ++ /* Put stripe onto the LRU list. */ ++ stripe_lru_add(stripe, POS_TAIL, LIST_LOCKED); ++ } ++ ++ BUG_ON(atomic_read(&stripe->cnt) < 0); ++} ++ ++/* ++ * Process end io ++ * ++ * I need to do it here because I can't in interrupt ++ * ++ * Read and write functions are split in order to avoid ++ * conditionals in the main loop for performamce reasons. ++ */ ++ ++/* Helper read bios on a page list. */ ++static void _bio_copy_page_list(struct stripe *stripe, struct page_list *pl, ++ struct bio *bio) ++{ ++ bio_copy_page_list(READ, stripe, pl, bio); ++} ++ ++/* Helper write bios on a page list. */ ++static void _rh_dec(struct stripe *stripe, struct page_list *pl, ++ struct bio *bio) ++{ ++ dm_rh_dec(RS(stripe->sc)->recover.rh, stripe->region); ++} ++ ++/* End io all bios on a page list. */ ++static inline int ++page_list_endio(int rw, struct stripe *stripe, unsigned p, unsigned *count) ++{ ++ int r = 0; ++ struct bio_list *bl = BL(stripe, p, rw); ++ ++ if (!bio_list_empty(bl)) { ++ struct page_list *pl = PL(stripe, p); ++ struct page *page = pl->page; ++ ++ if (PageLocked(page)) ++ r = -EBUSY; ++ /* ++ * FIXME: PageUptodate() not cleared ++ * properly for missing chunks ? ++ */ ++ else if (PageUptodate(page)) { ++ struct bio *bio; ++ struct raid_set *rs = RS(stripe->sc); ++ void (*h_f)(struct stripe *, struct page_list *, ++ struct bio *) = ++ (rw == READ) ? _bio_copy_page_list : _rh_dec; ++ ++ while ((bio = bio_list_pop(bl))) { ++ h_f(stripe, pl, bio); ++ _bio_endio(rs, bio, 0); ++ stripe_put(stripe); ++ if (count) ++ (*count)++; ++ } ++ } else ++ r = -EAGAIN; ++ } ++ ++ return r; ++} ++ ++/* ++ * End io all reads/writes on a stripe copying ++ * read date accross from stripe to bios. ++ */ ++static int stripe_endio(int rw, struct stripe *stripe, unsigned *count) ++{ ++ int r = 0; ++ unsigned p = RS(stripe->sc)->set.raid_devs; ++ ++ while (p--) { ++ int rr = page_list_endio(rw, stripe, p, count); ++ ++ if (rr && r != -EIO) ++ r = rr; ++ } ++ ++ return r; ++} ++ ++/* Fail all ios on a bio list and return # of bios. */ ++static unsigned ++bio_list_fail(struct raid_set *rs, struct stripe *stripe, struct bio_list *bl) ++{ ++ unsigned r; ++ struct bio *bio; ++ ++ raid_set_dead(rs); ++ ++ /* Update region counters. */ ++ if (stripe) { ++ struct dm_region_hash *rh = rs->recover.rh; ++ ++ bio_list_for_each(bio, bl) { ++ if (bio_data_dir(bio) == WRITE) ++ dm_rh_dec(rh, stripe->region); ++ } ++ } ++ ++ /* Error end io all bios. */ ++ for (r = 0; (bio = bio_list_pop(bl)); r++) ++ _bio_endio(rs, bio, -EIO); ++ ++ return r; ++} ++ ++/* Fail all ios of a bio list of a stripe and drop io pending count. */ ++static void ++stripe_bio_list_fail(struct raid_set *rs, struct stripe *stripe, ++ struct bio_list *bl) ++{ ++ unsigned put = bio_list_fail(rs, stripe, bl); ++ ++ while (put--) ++ stripe_put(stripe); ++} ++ ++/* Fail all ios hanging off all bio lists of a stripe. */ ++static void stripe_fail_io(struct stripe *stripe) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ unsigned p = rs->set.raid_devs; ++ ++ stripe_evict(stripe); ++ ++ while (p--) { ++ struct stripe_set *ss = stripe->ss + p; ++ int i = ARRAY_SIZE(ss->bl); ++ ++ while (i--) ++ stripe_bio_list_fail(rs, stripe, ss->bl + i); ++ } ++} ++ ++/* ++ * Handle all stripes by handing them to the daemon, because we can't ++ * map their pages to copy the data in interrupt context. ++ * ++ * We don't want to handle them here either, while interrupts are disabled. ++ */ ++ ++/* Read/write endio function for dm-io (interrupt context). */ ++static void endio(unsigned long error, void *context) ++{ ++ struct dm_mem_cache_object *obj = context; ++ struct stripe_set *ss = obj->private; ++ struct stripe *stripe = ss->stripe; ++ struct page *page = obj->pl->page; ++ ++ if (unlikely(error)) ++ stripe_error(stripe, page); ++ else ++ page_set(page, CLEAN); ++ ++ __clear_page_locked(page); ++ stripe_io_dec(stripe); ++ ++ /* Add stripe to endio list and wake daemon. */ ++ stripe_endio_push(stripe); ++} ++ ++/* ++ * Recovery io throttling ++ */ ++/* Conditionally reset io counters. */ ++enum count_type { IO_WORK = 0, IO_RECOVER }; ++static int recover_io_reset(struct raid_set *rs) ++{ ++ unsigned long j = jiffies; ++ ++ /* Pay attention to jiffies overflows. */ ++ if (j > rs->recover.last_jiffies + HZ ++ || j < rs->recover.last_jiffies) { ++ rs->recover.last_jiffies = j; ++ atomic_set(rs->recover.io_count + IO_WORK, 0); ++ atomic_set(rs->recover.io_count + IO_RECOVER, 0); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* Count ios. */ ++static INLINE void ++recover_io_count(struct raid_set *rs, struct stripe *stripe) ++{ ++ if (RSRecover(rs)) { ++ recover_io_reset(rs); ++ atomic_inc(rs->recover.io_count + ++ (StripeRecover(stripe) ? IO_RECOVER : IO_WORK)); ++ } ++} ++ ++/* Read/Write a page_list asynchronously. */ ++static void page_list_rw(struct stripe *stripe, unsigned p) ++{ ++ struct stripe_cache *sc = stripe->sc; ++ struct raid_set *rs = RS(sc); ++ struct dm_mem_cache_object *obj = stripe->obj + p; ++ struct page_list *pl = obj->pl; ++ struct page *page = pl->page; ++ struct raid_dev *dev = rs->dev + p; ++ struct dm_io_region io = { ++ .bdev = dev->dev->bdev, ++ .sector = stripe->key, ++ .count = stripe->io.size, ++ }; ++ struct dm_io_request control = { ++ .bi_rw = PageDirty(page) ? WRITE : READ, ++ .mem.type = DM_IO_PAGE_LIST, ++ .mem.ptr.pl = pl, ++ .mem.offset = 0, ++ .notify.fn = endio, ++ .notify.context = obj, ++ .client = sc->dm_io_client, ++ }; ++ ++ BUG_ON(PageLocked(page)); ++ ++ /* ++ * Don't rw past end of device, which can happen, because ++ * typically sectors_per_dev isn't divisable by io_size. ++ */ ++ if (unlikely(io.sector + io.count > rs->set.sectors_per_dev)) ++ io.count = rs->set.sectors_per_dev - io.sector; ++ ++ io.sector += dev->start; /* Add . */ ++ recover_io_count(rs, stripe); /* Recovery io accounting. */ ++ ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + ++ (PageDirty(page) ? S_DM_IO_WRITE : S_DM_IO_READ)); ++ ++ ClearPageError(page); ++ __set_page_locked(page); ++ io_dev_queued(dev); ++ BUG_ON(dm_io(&control, 1, &io, NULL)); ++} ++ ++/* ++ * Write dirty / read not uptodate page lists of a stripe. ++ */ ++static unsigned stripe_page_lists_rw(struct raid_set *rs, struct stripe *stripe) ++{ ++ unsigned r; ++ ++ /* ++ * Increment the pending count on the stripe ++ * first, so that we don't race in endio(). ++ * ++ * An inc (IO) is needed for any page: ++ * ++ * o not uptodate ++ * o dirtied by writes merged ++ * o dirtied by parity calculations ++ */ ++ r = for_each_io_dev(rs, stripe, _stripe_io_inc); ++ if (r) { ++ /* io needed: chunks are not uptodate/dirty. */ ++ int max; /* REMOVEME: */ ++ struct stripe_cache *sc = &rs->sc; ++ ++ if (!TestSetStripeActive(stripe)) ++ atomic_inc(&sc->active_stripes); ++ ++ /* Take off the lru list in case it got added there. */ ++ stripe_lru_del(stripe, LIST_LOCKED); ++ ++ /* Submit actual io. */ ++ for_each_io_dev(rs, stripe, page_list_rw); ++ ++ /* REMOVEME: statistics */ ++ max = sc_active(sc); ++ if (atomic_read(&sc->max_active_stripes) < max) ++ atomic_set(&sc->max_active_stripes, max); ++ ++ atomic_inc(rs->stats + S_FLUSHS); ++ /* END REMOVEME: statistics */ ++ } ++ ++ return r; ++} ++ ++/* Work in all pending writes. */ ++static INLINE void _writes_merge(struct stripe *stripe, unsigned p) ++{ ++ struct bio_list *write = BL(stripe, p, WRITE); ++ ++ if (!bio_list_empty(write)) { ++ struct page_list *pl = stripe->obj[p].pl; ++ struct bio *bio; ++ struct bio_list *write_merged = BL(stripe, p, WRITE_MERGED); ++ ++ /* ++ * We can play with the lists without holding a lock, ++ * because it is just us accessing them anyway. ++ */ ++ bio_list_for_each(bio, write) ++ bio_copy_page_list(WRITE, stripe, pl, bio); ++ ++ bio_list_merge(write_merged, write); ++ bio_list_init(write); ++ page_set(pl->page, DIRTY); ++ } ++} ++ ++/* Merge in all writes hence dirtying respective pages. */ ++static INLINE void writes_merge(struct stripe *stripe) ++{ ++ unsigned p = RS(stripe->sc)->set.raid_devs; ++ ++ while (p--) ++ _writes_merge(stripe, p); ++} ++ ++/* Check, if a chunk gets completely overwritten. */ ++static INLINE int stripe_check_overwrite(struct stripe *stripe, unsigned p) ++{ ++ unsigned sectors = 0; ++ struct bio *bio; ++ struct bio_list *bl = BL(stripe, p, WRITE); ++ ++ bio_list_for_each(bio, bl) ++ sectors += bio_sectors(bio); ++ ++ return sectors == RS(stripe->sc)->set.io_size; ++} ++ ++/* ++ * Prepare stripe to avoid io on broken/reconstructed ++ * drive in order to reconstruct date on endio. ++ */ ++enum prepare_type { IO_ALLOW, IO_PROHIBIT }; ++static void stripe_prepare(struct stripe *stripe, unsigned p, ++ enum prepare_type type) ++{ ++ struct page *page = PAGE(stripe, p); ++ ++ switch (type) { ++ case IO_PROHIBIT: ++ /* ++ * In case we prohibit, we gotta make sure, that ++ * io on all other chunks than the one which failed ++ * or is being reconstructed is allowed and that it ++ * doesn't have state uptodate. ++ */ ++ stripe_allow_io(stripe); ++ ClearPageUptodate(page); ++ ProhibitPageIO(page); ++ ++ /* REMOVEME: statistics. */ ++ atomic_inc(RS(stripe->sc)->stats + S_PROHIBITPAGEIO); ++ stripe->idx.recover = p; ++ SetStripeReconstruct(stripe); ++ break; ++ ++ case IO_ALLOW: ++ AllowPageIO(page); ++ stripe->idx.recover = -1; ++ ClearStripeReconstruct(stripe); ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++ ++/* ++ * Degraded/reconstruction mode. ++ * ++ * Check stripe state to figure which chunks don't need IO. ++ */ ++static INLINE void stripe_check_reconstruct(struct stripe *stripe, ++ int prohibited) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ ++ /* ++ * Degraded mode (device(s) failed) -> ++ * avoid io on the failed device. ++ */ ++ if (unlikely(raid_set_degraded(rs))) { ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_DEGRADED); ++ stripe_prepare(stripe, rs->set.ei, IO_PROHIBIT); ++ return; ++ } else { ++ /* ++ * Reconstruction mode (ie. a particular device or ++ * some (rotating) parity chunk is being resynchronized) -> ++ * o make sure all needed pages are read in ++ * o writes are allowed to go through ++ */ ++ int r = region_state(rs, stripe->key, DM_RH_NOSYNC); ++ ++ if (r) { ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_NOSYNC); ++ stripe_prepare(stripe, dev_for_parity(stripe), ++ IO_PROHIBIT); ++ return; ++ } ++ } ++ ++ /* ++ * All disks good. Avoid reading parity chunk and reconstruct it ++ * unless we have prohibited io to chunk(s). ++ */ ++ if (!prohibited) { ++ if (StripeMerged(stripe)) ++ stripe_prepare(stripe, stripe->idx.parity, IO_ALLOW); ++ else { ++ stripe_prepare(stripe, stripe->idx.parity, IO_PROHIBIT); ++ ++ /* ++ * Overrule stripe_prepare to reconstruct the ++ * parity chunk, because it'll be created new anyway. ++ */ ++ ClearStripeReconstruct(stripe); ++ } ++ } ++} ++ ++/* Check, if stripe is ready to merge writes. */ ++static INLINE int stripe_check_merge(struct stripe *stripe) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ int prohibited = 0; ++ unsigned chunks = 0, p = rs->set.raid_devs; ++ ++ /* Walk all chunks. */ ++ while (p--) { ++ struct page *page = PAGE(stripe, p); ++ ++ /* Can't merge active chunks. */ ++ if (PageLocked(page)) { ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_MERGE_PAGE_LOCKED); ++ break; ++ } ++ ++ /* Can merge uptodate chunks and have to count parity chunk. */ ++ if (PageUptodate(page) || p == stripe->idx.parity) { ++ chunks++; ++ continue; ++ } ++ ++ /* Read before write ordering. */ ++ if (RSCheckOverwrite(rs) && ++ bio_list_empty(BL(stripe, p, READ))) { ++ int r = stripe_check_overwrite(stripe, p); ++ ++ if (r) { ++ chunks++; ++ /* REMOVEME: statistics. */ ++ atomic_inc(RS(stripe->sc)->stats + ++ S_PROHIBITPAGEIO); ++ ProhibitPageIO(page); ++ prohibited = 1; ++ } ++ } ++ } ++ ++ if (chunks == rs->set.raid_devs) { ++ /* All pages are uptodate or get written over or mixture. */ ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_CAN_MERGE); ++ return 0; ++ } else ++ /* REMOVEME: statistics.*/ ++ atomic_inc(rs->stats + S_CANT_MERGE); ++ ++ return prohibited ? 1 : -EPERM; ++} ++ ++/* Check, if stripe is ready to merge writes. */ ++static INLINE int stripe_check_read(struct stripe *stripe) ++{ ++ int r = 0; ++ unsigned p = RS(stripe->sc)->set.raid_devs; ++ ++ /* Walk all chunks. */ ++ while (p--) { ++ struct page *page = PAGE(stripe, p); ++ ++ if (!PageLocked(page) && ++ bio_list_empty(BL(stripe, p, READ))) { ++ ProhibitPageIO(page); ++ r = 1; ++ } ++ } ++ ++ return r; ++} ++ ++/* ++ * Read/write a stripe. ++ * ++ * All stripe read/write activity goes through this function. ++ * ++ * States to cover: ++ * o stripe to read and/or write ++ * o stripe with error to reconstruct ++ */ ++static int stripe_rw(struct stripe *stripe) ++{ ++ struct raid_set *rs = RS(stripe->sc); ++ int prohibited = 0, r; ++ ++ /* ++ * Check the state of the RAID set and if degraded (or ++ * resynchronizing for reads), read in all other chunks but ++ * the one on the dead/resynchronizing device in order to be ++ * able to reconstruct the missing one. ++ * ++ * Merge all writes hanging off uptodate pages of the stripe. ++ */ ++ ++ /* Initially allow io on all chunks and prohibit below, if necessary. */ ++ stripe_allow_io(stripe); ++ ++ if (StripeRBW(stripe)) { ++ r = stripe_check_merge(stripe); ++ if (!r) { ++ /* ++ * If I could rely on valid parity (which would only ++ * be sure in case of a full synchronization), ++ * I could xor a fraction of chunks out of ++ * parity and back in. ++ * ++ * For the time being, I got to redo parity... ++ */ ++ /* parity_xor(stripe); */ /* Xor chunks out. */ ++ stripe_zero_chunk(stripe, stripe->idx.parity); ++ writes_merge(stripe); /* Merge writes in. */ ++ parity_xor(stripe); /* Update parity. */ ++ ClearStripeRBW(stripe); /* Disable RBW. */ ++ SetStripeMerged(stripe); /* Writes merged. */ ++ } ++ ++ if (r > 0) ++ prohibited = 1; ++ } else if (!raid_set_degraded(rs)) ++ /* Only allow for read avoidance if not degraded. */ ++ prohibited = stripe_check_read(stripe); ++ ++ /* ++ * Check, if io needs to be allowed/prohibeted on certain chunks ++ * because of a degraded set or reconstruction on a region. ++ */ ++ stripe_check_reconstruct(stripe, prohibited); ++ ++ /* Now submit any reads/writes. */ ++ r = stripe_page_lists_rw(rs, stripe); ++ if (!r) { ++ /* ++ * No io submitted because of chunk io prohibited or ++ * locked pages -> push to end io list for processing. ++ */ ++ atomic_inc(rs->stats + S_NO_RW); /* REMOVEME: statistics. */ ++ stripe_endio_push(stripe); ++ wake_do_raid(rs); /* Wake myself. */ ++ } ++ ++ return 0; ++} ++ ++/* Flush stripe either via flush list or imeediately. */ ++enum flush_type { FLUSH_DELAY, FLUSH_NOW }; ++static int stripe_flush(struct stripe *stripe, enum flush_type type) ++{ ++ int r = 0; ++ ++ stripe_lru_del(stripe, LIST_LOCKED); ++ ++ /* Immediately flush. */ ++ if (type == FLUSH_NOW) { ++ if (likely(raid_set_operational(RS(stripe->sc)))) ++ r = stripe_rw(stripe); /* Read/write stripe. */ ++ else ++ /* Optimization: Fail early on failed sets. */ ++ stripe_fail_io(stripe); ++ /* Delay flush by putting it on io list for later processing. */ ++ } else if (type == FLUSH_DELAY) ++ stripe_io_add(stripe, POS_TAIL, LIST_UNLOCKED); ++ else ++ BUG(); ++ ++ return r; ++} ++ ++/* ++ * Queue reads and writes to a stripe by hanging ++ * their bios off the stripsets read/write lists. ++ * ++ * Endio reads on uptodate chunks. ++ */ ++static INLINE int stripe_queue_bio(struct raid_set *rs, struct bio *bio, ++ struct bio_list *reject) ++{ ++ int r = 0; ++ struct address addr; ++ struct stripe *stripe = ++ stripe_get(rs, raid_address(rs, bio->bi_sector, &addr)); ++ ++ if (stripe) { ++ int rr, rw = bio_data_dir(bio); ++ ++ rr = stripe_lock(rs, stripe, rw, addr.key); /* Lock stripe */ ++ if (rr) { ++ stripe_put(stripe); ++ goto out; ++ } ++ ++ /* Distinguish read and write cases. */ ++ bio_list_add(BL(stripe, addr.di, rw), bio); ++ ++ /* REMOVEME: statistics */ ++ atomic_inc(rs->stats + (rw == WRITE ? ++ S_BIOS_ADDED_WRITE : S_BIOS_ADDED_READ)); ++ ++ if (rw == READ) ++ SetStripeRead(stripe); ++ else { ++ SetStripeRBW(stripe); ++ ++ /* Inrement pending write count on region. */ ++ dm_rh_inc(rs->recover.rh, stripe->region); ++ r = 1; /* Region hash needs a flush. */ ++ } ++ ++ /* ++ * Optimize stripe flushing: ++ * ++ * o directly start io for read stripes. ++ * ++ * o put stripe onto stripe caches io_list for RBW, ++ * so that do_flush() can belabour it after we put ++ * more bios to the stripe for overwrite optimization. ++ */ ++ stripe_flush(stripe, ++ StripeRead(stripe) ? FLUSH_NOW : FLUSH_DELAY); ++ ++ /* Got no stripe from cache -> reject bio. */ ++ } else { ++out: ++ bio_list_add(reject, bio); ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_IOS_POST); ++ } ++ ++ return r; ++} ++ ++/* ++ * Recovery functions ++ */ ++/* Read a stripe off a raid set for recovery. */ ++static int recover_read(struct raid_set *rs, struct stripe *stripe, int idx) ++{ ++ /* Invalidate all pages so that they get read in. */ ++ stripe_pages_invalidate(stripe); ++ ++ /* Allow io on all recovery chunks. */ ++ stripe_allow_io(stripe); ++ ++ if (idx > -1) ++ ProhibitPageIO(PAGE(stripe, idx)); ++ ++ stripe->key = rs->recover.pos; ++ return stripe_page_lists_rw(rs, stripe); ++} ++ ++/* Write a stripe to a raid set for recovery. */ ++static int recover_write(struct raid_set *rs, struct stripe *stripe, int idx) ++{ ++ /* ++ * If this is a reconstruct of a particular device, then ++ * reconstruct the respective page(s), else create parity page(s). ++ */ ++ if (idx > -1) { ++ struct page *page = PAGE(stripe, idx); ++ ++ AllowPageIO(page); ++ stripe_zero_chunk(stripe, idx); ++ common_xor(stripe, stripe->io.size, 0, idx); ++ page_set(page, DIRTY); ++ } else ++ parity_xor(stripe); ++ ++ return stripe_page_lists_rw(rs, stripe); ++} ++ ++/* Recover bandwidth available ?. */ ++static int recover_bandwidth(struct raid_set *rs) ++{ ++ int r, work; ++ ++ /* On reset -> allow recovery. */ ++ r = recover_io_reset(rs); ++ if (r || RSBandwidth(rs)) ++ goto out; ++ ++ work = atomic_read(rs->recover.io_count + IO_WORK); ++ if (work) { ++ /* Pay attention to larger recover stripe size. */ ++ int recover = ++ atomic_read(rs->recover.io_count + IO_RECOVER) * ++ rs->recover.io_size / ++ rs->set.io_size; ++ ++ /* ++ * Don't use more than given bandwidth of ++ * the work io for recovery. ++ */ ++ if (recover > work / rs->recover.bandwidth_work) { ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_NO_BANDWIDTH); ++ return 0; ++ } ++ } ++ ++out: ++ atomic_inc(rs->stats + S_BANDWIDTH); /* REMOVEME: statistics. */ ++ return 1; ++} ++ ++/* Try to get a region to recover. */ ++static int recover_get_region(struct raid_set *rs) ++{ ++ struct recover *rec = &rs->recover; ++ struct dm_region_hash *rh = rec->rh; ++ ++ /* Start quiescing some regions. */ ++ if (!RSRegionGet(rs)) { ++ int r = recover_bandwidth(rs); /* Enough bandwidth ?. */ ++ ++ if (r) { ++ r = dm_rh_recovery_prepare(rh); ++ if (r < 0) { ++ DMINFO("No %sregions to recover", ++ rec->nr_regions_to_recover ? ++ "more " : ""); ++ return -ENOENT; ++ } ++ } else ++ return -EAGAIN; ++ ++ SetRSRegionGet(rs); ++ } ++ ++ if (!rec->reg) { ++ rec->reg = dm_rh_recovery_start(rh); ++ if (rec->reg) { ++ /* ++ * A reference for the the region I'll ++ * keep till I've completely synced it. ++ */ ++ io_get(rs); ++ rec->pos = dm_rh_region_to_sector(rh, ++ dm_rh_get_region_key(rec->reg)); ++ rec->end = rec->pos + dm_rh_get_region_size(rh); ++ return 1; ++ } else ++ return -EAGAIN; ++ } ++ ++ return 0; ++} ++ ++/* Read/write a recovery stripe. */ ++static INLINE int recover_stripe_rw(struct raid_set *rs, struct stripe *stripe) ++{ ++ /* Read/write flip-flop. */ ++ if (TestClearStripeRBW(stripe)) { ++ SetStripeRead(stripe); ++ return recover_read(rs, stripe, idx_get(rs)); ++ } else if (TestClearStripeRead(stripe)) ++ return recover_write(rs, stripe, idx_get(rs)); ++ ++ return 0; ++} ++ ++/* Reset recovery variables. */ ++static void recovery_region_reset(struct raid_set *rs) ++{ ++ rs->recover.reg = NULL; ++ ClearRSRegionGet(rs); ++} ++ ++/* Update region hash state. */ ++static void recover_rh_update(struct raid_set *rs, int error) ++{ ++ struct recover *rec = &rs->recover; ++ struct dm_region *reg = rec->reg; ++ ++ if (reg) { ++ dm_rh_recovery_end(reg, error); ++ if (!error) ++ rec->nr_regions_recovered++; ++ ++ recovery_region_reset(rs); ++ } ++ ++ dm_rh_update_states(reg->rh, 1); ++ dm_rh_flush(reg->rh); ++ io_put(rs); /* Release the io reference for the region. */ ++} ++ ++/* Called by main io daemon to recover regions. */ ++/* FIXME: cope with MAX_RECOVER > 1. */ ++static INLINE void _do_recovery(struct raid_set *rs, struct stripe *stripe) ++{ ++ int r; ++ struct recover *rec = &rs->recover; ++ ++ /* If recovery is active -> return. */ ++ if (StripeActive(stripe)) ++ return; ++ ++ /* io error is fatal for recovery -> stop it. */ ++ if (unlikely(StripeError(stripe))) ++ goto err; ++ ++ /* Get a region to recover. */ ++ r = recover_get_region(rs); ++ switch (r) { ++ case 1: /* Got a new region. */ ++ /* Flag read before write. */ ++ ClearStripeRead(stripe); ++ SetStripeRBW(stripe); ++ break; ++ ++ case 0: ++ /* Got a region in the works. */ ++ r = recover_bandwidth(rs); ++ if (r) /* Got enough bandwidth. */ ++ break; ++ ++ case -EAGAIN: ++ /* No bandwidth/quiesced region yet, try later. */ ++ wake_do_raid_delayed(rs, HZ / 10); ++ return; ++ ++ case -ENOENT: /* No more regions. */ ++ dm_table_event(rs->ti->table); ++ goto free; ++ } ++ ++ /* Read/write a recover stripe. */ ++ r = recover_stripe_rw(rs, stripe); ++ if (r) { ++ /* IO initiated, get another reference for the IO. */ ++ io_get(rs); ++ return; ++ } ++ ++ /* Update recovery position within region. */ ++ rec->pos += stripe->io.size; ++ ++ /* If we're at end of region, update region hash. */ ++ if (rec->pos >= rec->end || ++ rec->pos >= rs->set.sectors_per_dev) ++ recover_rh_update(rs, 0); ++ else ++ SetStripeRBW(stripe); ++ ++ /* Schedule myself for another round... */ ++ wake_do_raid(rs); ++ return; ++ ++err: ++ raid_set_check_degrade(rs, stripe); ++ ++ { ++ char buf[BDEVNAME_SIZE]; ++ ++ DMERR("stopping recovery due to " ++ "ERROR on /dev/%s, stripe at offset %llu", ++ bdevname(rs->dev[rs->set.ei].dev->bdev, buf), ++ (unsigned long long) stripe->key); ++ ++ } ++ ++ /* Make sure, that all quiesced regions get released. */ ++ do { ++ if (rec->reg) ++ dm_rh_recovery_end(rec->reg, -EIO); ++ ++ rec->reg = dm_rh_recovery_start(rec->rh); ++ } while (rec->reg); ++ ++ recover_rh_update(rs, -EIO); ++free: ++ rs->set.dev_to_init = -1; ++ ++ /* Check for jiffies overrun. */ ++ rs->recover.end_jiffies = jiffies; ++ if (rs->recover.end_jiffies < rs->recover.start_jiffies) ++ rs->recover.end_jiffies = ~0; ++ ++ ClearRSRecover(rs); ++} ++ ++static INLINE void do_recovery(struct raid_set *rs) ++{ ++ struct stripe *stripe; ++ ++ list_for_each_entry(stripe, &rs->recover.stripes, lists[LIST_RECOVER]) ++ _do_recovery(rs, stripe); ++ ++ if (!RSRecover(rs)) ++ stripe_recover_free(rs); ++} ++ ++/* ++ * END recovery functions ++ */ ++ ++/* End io process all stripes handed in by endio() callback. */ ++static void do_endios(struct raid_set *rs) ++{ ++ struct stripe_cache *sc = &rs->sc; ++ struct stripe *stripe; ++ ++ while ((stripe = stripe_endio_pop(sc))) { ++ unsigned count; ++ ++ /* Recovery stripe special case. */ ++ if (unlikely(StripeRecover(stripe))) { ++ if (stripe_io(stripe)) ++ continue; ++ ++ io_put(rs); /* Release region io reference. */ ++ ClearStripeActive(stripe); ++ ++ /* REMOVEME: statistics*/ ++ atomic_dec(&sc->active_stripes); ++ continue; ++ } ++ ++ /* Early end io all reads on any uptodate chunks. */ ++ stripe_endio(READ, stripe, (count = 0, &count)); ++ if (stripe_io(stripe)) { ++ if (count) /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_ACTIVE_READS); ++ ++ continue; ++ } ++ ++ /* Set stripe inactive after all io got processed. */ ++ if (TestClearStripeActive(stripe)) ++ atomic_dec(&sc->active_stripes); ++ ++ /* Unlock stripe (for clustering). */ ++ stripe_unlock(rs, stripe); ++ ++ /* ++ * If an io error on a stripe occured and the RAID set ++ * is still operational, requeue the stripe for io. ++ */ ++ if (TestClearStripeError(stripe)) { ++ raid_set_check_degrade(rs, stripe); ++ ClearStripeReconstruct(stripe); ++ ++ if (!StripeMerged(stripe) && ++ raid_set_operational(rs)) { ++ stripe_pages_invalidate(stripe); ++ stripe_flush(stripe, FLUSH_DELAY); ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_REQUEUE); ++ continue; ++ } ++ } ++ ++ /* Check if the RAID set is inoperational to error ios. */ ++ if (!raid_set_operational(rs)) { ++ ClearStripeReconstruct(stripe); ++ stripe_fail_io(stripe); ++ BUG_ON(atomic_read(&stripe->cnt)); ++ continue; ++ } ++ ++ /* Got to reconstruct a missing chunk. */ ++ if (TestClearStripeReconstruct(stripe)) ++ reconstruct_xor(stripe); ++ ++ /* ++ * Now that we've got a complete stripe, we can ++ * process the rest of the end ios on reads. ++ */ ++ BUG_ON(stripe_endio(READ, stripe, NULL)); ++ ClearStripeRead(stripe); ++ ++ /* ++ * Read-before-write stripes need to be flushed again in ++ * order to work the write data into the pages *after* ++ * they were read in. ++ */ ++ if (TestClearStripeMerged(stripe)) ++ /* End io all bios which got merged already. */ ++ BUG_ON(stripe_endio(WRITE_MERGED, stripe, NULL)); ++ ++ /* Got to put on flush list because of new writes. */ ++ if (StripeRBW(stripe)) ++ stripe_flush(stripe, FLUSH_DELAY); ++ } ++} ++ ++/* ++ * Stripe cache shrinking. ++ */ ++static INLINE void do_sc_shrink(struct raid_set *rs) ++{ ++ unsigned shrink = atomic_read(&rs->sc.stripes_to_shrink); ++ ++ if (shrink) { ++ unsigned cur = atomic_read(&rs->sc.stripes); ++ ++ sc_shrink(&rs->sc, shrink); ++ shrink -= cur - atomic_read(&rs->sc.stripes); ++ atomic_set(&rs->sc.stripes_to_shrink, shrink); ++ ++ /* ++ * Wake myself up in case we failed to shrink the ++ * requested amount in order to try again later. ++ */ ++ if (shrink) ++ wake_do_raid(rs); ++ } ++} ++ ++ ++/* ++ * Process all ios ++ * ++ * We do different things with the io depending on the ++ * state of the region that it's in: ++ * ++ * o reads: hang off stripe cache or postpone if full ++ * ++ * o writes: ++ * ++ * CLEAN/DIRTY/NOSYNC: increment pending and hang io off stripe's stripe set. ++ * In case stripe cache is full or busy, postpone the io. ++ * ++ * RECOVERING: delay the io until recovery of the region completes. ++ * ++ */ ++static INLINE void do_ios(struct raid_set *rs, struct bio_list *ios) ++{ ++ int r; ++ unsigned flush = 0; ++ struct dm_region_hash *rh = rs->recover.rh; ++ struct bio *bio; ++ struct bio_list delay, reject; ++ ++ bio_list_init(&delay); ++ bio_list_init(&reject); ++ ++ /* ++ * Classify each io: ++ * o delay to recovering regions ++ * o queue to all other regions ++ */ ++ while ((bio = bio_list_pop(ios))) { ++ /* ++ * In case we get a barrier bio, push it back onto ++ * the input queue unless all work queues are empty ++ * and the stripe cache is inactive. ++ */ ++ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) { ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + S_BARRIER); ++ if (!list_empty(rs->sc.lists + LIST_IO) || ++ !bio_list_empty(&delay) || ++ !bio_list_empty(&reject) || ++ sc_active(&rs->sc)) { ++ bio_list_push(ios, bio); ++ break; ++ } ++ } ++ ++ r = region_state(rs, _sector(rs, bio), DM_RH_RECOVERING); ++ if (unlikely(r)) { ++ /* Got to wait for recovering regions. */ ++ bio_list_add(&delay, bio); ++ SetRSBandwidth(rs); ++ } else { ++ /* ++ * Process ios to non-recovering regions by queueing ++ * them to stripes (does rh_inc()) for writes). ++ */ ++ flush += stripe_queue_bio(rs, bio, &reject); ++ } ++ } ++ ++ if (flush) { ++ r = dm_rh_flush(rh); /* Writes got queued -> flush dirty log. */ ++ if (r) ++ DMERR("dirty log flush"); ++ } ++ ++ /* Delay ios to regions which are recovering. */ ++ while ((bio = bio_list_pop(&delay))) { ++ /* REMOVEME: statistics.*/ ++ atomic_inc(rs->stats + S_DELAYED_BIOS); ++ atomic_inc(rs->stats + S_SUM_DELAYED_BIOS); ++ dm_rh_delay(rh, bio); ++ ++ } ++ ++ /* Merge any rejected bios back to the head of the input list. */ ++ bio_list_merge_head(ios, &reject); ++} ++ ++/* Flush any stripes on the io list. */ ++static INLINE void do_flush(struct raid_set *rs) ++{ ++ struct list_head *list = rs->sc.lists + LIST_IO, *pos, *tmp; ++ ++ list_for_each_safe(pos, tmp, list) { ++ int r = stripe_flush(list_entry(pos, struct stripe, ++ lists[LIST_IO]), FLUSH_NOW); ++ ++ /* Remove from the list only if the stripe got processed. */ ++ if (!r) ++ list_del_init(pos); ++ } ++} ++ ++/* Send an event in case we're getting too busy. */ ++static INLINE void do_busy_event(struct raid_set *rs) ++{ ++ if ((sc_active(&rs->sc) > atomic_read(&rs->sc.stripes) * 4 / 5)) { ++ if (!TestSetRSScBusy(rs)) ++ dm_table_event(rs->ti->table); ++ } else ++ ClearRSScBusy(rs); ++} ++ ++/* Unplug: let the io role on the sets devices. */ ++static INLINE void do_unplug(struct raid_set *rs) ++{ ++ struct raid_dev *dev = rs->dev + rs->set.raid_devs; ++ ++ while (dev-- > rs->dev) { ++ /* Only call any device unplug function, if io got queued. */ ++ if (io_dev_clear(dev)) ++ blk_unplug(bdev_get_queue(dev->dev->bdev)); ++ } ++} ++ ++/*----------------------------------------------------------------- ++ * RAID daemon ++ *---------------------------------------------------------------*/ ++/* ++ * o belabour all end ios ++ * o optionally shrink the stripe cache ++ * o update the region hash states ++ * o optionally do recovery ++ * o grab the input queue ++ * o work an all requeued or new ios and perform stripe cache flushs ++ * unless the RAID set is inoperational (when we error ios) ++ * o check, if the stripe cache gets too busy and throw an event if so ++ * o unplug any component raid devices with queued bios ++ */ ++static void do_raid(struct work_struct *ws) ++{ ++ struct raid_set *rs = container_of(ws, struct raid_set, io.dws.work); ++ struct bio_list *ios = &rs->io.work, *ios_in = &rs->io.in; ++ spinlock_t *lock = &rs->io.in_lock; ++ ++ /* ++ * We always need to end io, so that ios ++ * can get errored in case the set failed ++ * and the region counters get decremented ++ * before we update the region hash states. ++ */ ++redo: ++ do_endios(rs); ++ ++ /* ++ * Now that we've end io'd, which may have put stripes on ++ * the LRU list, we shrink the stripe cache if requested. ++ */ ++ do_sc_shrink(rs); ++ ++ /* Update region hash states before we go any further. */ ++ dm_rh_update_states(rs->recover.rh, 1); ++ ++ /* Try to recover regions. */ ++ if (RSRecover(rs)) ++ do_recovery(rs); ++ ++ /* More endios -> process. */ ++ if (!stripe_endio_empty(&rs->sc)) { ++ atomic_inc(rs->stats + S_REDO); ++ goto redo; ++ } ++ ++ /* Quickly grab all new ios queued and add them to the work list. */ ++ spin_lock_irq(lock); ++ bio_list_merge(ios, ios_in); ++ bio_list_init(ios_in); ++ spin_unlock_irq(lock); ++ ++ /* Let's assume we're operational most of the time ;-). */ ++ if (likely(raid_set_operational(rs))) { ++ /* If we got ios, work them into the cache. */ ++ if (!bio_list_empty(ios)) { ++ do_ios(rs, ios); ++ do_unplug(rs); /* Unplug the sets device queues. */ ++ } ++ ++ do_flush(rs); /* Flush any stripes on io list. */ ++ do_unplug(rs); /* Unplug the sets device queues. */ ++ do_busy_event(rs); /* Check if we got too busy. */ ++ ++ /* More endios -> process. */ ++ if (!stripe_endio_empty(&rs->sc)) { ++ atomic_inc(rs->stats + S_REDO); ++ goto redo; ++ } ++ } else ++ /* No way to reconstruct data with too many devices failed. */ ++ bio_list_fail(rs, NULL, ios); ++} ++ ++/* ++ * Callback for region hash to dispatch ++ * delayed bios queued to recovered regions ++ * (Gets called via rh_update_states()). ++ */ ++static void dispatch_delayed_bios(void *context, struct bio_list *bl) ++{ ++ struct raid_set *rs = context; ++ struct bio *bio; ++ ++ /* REMOVEME: decrement pending delayed bios counter. */ ++ bio_list_for_each(bio, bl) ++ atomic_dec(rs->stats + S_DELAYED_BIOS); ++ ++ /* Merge region hash private list to work list. */ ++ bio_list_merge_head(&rs->io.work, bl); ++ bio_list_init(bl); ++ ClearRSBandwidth(rs); ++} ++ ++/************************************************************* ++ * Constructor helpers ++ *************************************************************/ ++/* Calculate MB/sec. */ ++static INLINE unsigned mbpers(struct raid_set *rs, unsigned speed) ++{ ++ return to_bytes(speed * rs->set.data_devs * ++ rs->recover.io_size * HZ >> 10) >> 10; ++} ++ ++/* ++ * Discover fastest xor algorithm and # of chunks combination. ++ */ ++/* Calculate speed for algorithm and # of chunks. */ ++static INLINE unsigned xor_speed(struct stripe *stripe) ++{ ++ unsigned r = 0; ++ unsigned long j; ++ ++ /* Wait for next tick. */ ++ for (j = jiffies; j == jiffies;) ++ ; ++ ++ /* Do xors for a full tick. */ ++ for (j = jiffies; j == jiffies;) { ++ mb(); ++ common_xor(stripe, stripe->io.size, 0, 0); ++ mb(); ++ r++; ++ mb(); ++ } ++ ++ return r; ++} ++ ++/* Optimize xor algorithm for this RAID set. */ ++static unsigned xor_optimize(struct raid_set *rs) ++{ ++ unsigned chunks_max = 2, speed_max = 0; ++ struct xor_func *f = ARRAY_END(xor_funcs), *f_max = NULL; ++ struct stripe *stripe; ++ ++ BUG_ON(list_empty(&rs->recover.stripes)); ++ stripe = list_first_entry(&rs->recover.stripes, struct stripe, ++ lists[LIST_RECOVER]); ++ ++ /* ++ * Got to allow io on all chunks, so that ++ * xor() will actually work on them. ++ */ ++ stripe_allow_io(stripe); ++ ++ /* Try all xor functions. */ ++ while (f-- > xor_funcs) { ++ unsigned speed; ++ ++ /* Set actual xor function for common_xor(). */ ++ rs->xor.f = f; ++ rs->xor.chunks = XOR_CHUNKS_MAX + 1; ++ ++ while (rs->xor.chunks-- > 2) { ++ speed = xor_speed(stripe); ++ if (speed > speed_max) { ++ speed_max = speed; ++ chunks_max = rs->xor.chunks; ++ f_max = f; ++ } ++ } ++ } ++ ++ /* Memorize optimum parameters. */ ++ rs->xor.f = f_max; ++ rs->xor.chunks = chunks_max; ++ return speed_max; ++} ++ ++static inline int array_too_big(unsigned long fixed, unsigned long obj, ++ unsigned long num) ++{ ++ return (num > (ULONG_MAX - fixed) / obj); ++} ++ ++static void wakeup_all_recovery_waiters(void *context) ++{ ++} ++ ++/* ++ * Allocate a RAID context (a RAID set) ++ */ ++static int ++context_alloc(struct raid_set **raid_set, struct raid_type *raid_type, ++ unsigned stripes, unsigned chunk_size, unsigned io_size, ++ unsigned recover_io_size, unsigned raid_devs, ++ sector_t sectors_per_dev, ++ struct dm_target *ti, unsigned dl_parms, char **argv) ++{ ++ int r; ++ unsigned p; ++ size_t len; ++ sector_t region_size, ti_len; ++ struct raid_set *rs = NULL; ++ struct dm_dirty_log *dl; ++ struct recover *rec; ++ ++ /* ++ * Create the dirty log ++ * ++ * We need to change length for the dirty log constructor, ++ * because we want an amount of regions for all stripes derived ++ * from the single device size, so that we can keep region ++ * size = 2^^n independant of the number of devices ++ */ ++ ti_len = ti->len; ++ ti->len = sectors_per_dev; ++ dl = dm_dirty_log_create(argv[0], ti, dl_parms, argv + 2); ++ ti->len = ti_len; ++ if (!dl) ++ goto bad_dirty_log; ++ ++ /* Chunk size *must* be smaller than region size. */ ++ region_size = dl->type->get_region_size(dl); ++ if (chunk_size > region_size) ++ goto bad_chunk_size; ++ ++ /* Recover io size *must* be smaller than region size as well. */ ++ if (recover_io_size > region_size) ++ goto bad_recover_io_size; ++ ++ /* Size and allocate the RAID set structure. */ ++ len = sizeof(*rs->data) + sizeof(*rs->dev); ++ if (array_too_big(sizeof(*rs), len, raid_devs)) ++ goto bad_array; ++ ++ len = sizeof(*rs) + raid_devs * len; ++ rs = kzalloc(len, GFP_KERNEL); ++ if (!rs) ++ goto bad_alloc; ++ ++ rec = &rs->recover; ++ atomic_set(&rs->io.in_process, 0); ++ atomic_set(&rs->io.in_process_max, 0); ++ rec->io_size = recover_io_size; ++ ++ /* Pointer to data array. */ ++ rs->data = (unsigned long **) ++ ((void *) rs->dev + raid_devs * sizeof(*rs->dev)); ++ rec->dl = dl; ++ rs->set.raid_devs = p = raid_devs; ++ rs->set.data_devs = raid_devs - raid_type->parity_devs; ++ rs->set.raid_type = raid_type; ++ ++ /* ++ * Set chunk and io size and respective shifts ++ * (used to avoid divisions) ++ */ ++ rs->set.chunk_size = chunk_size; ++ rs->set.chunk_mask = chunk_size - 1; ++ rs->set.chunk_shift = ffs(chunk_size) - 1; ++ ++ rs->set.io_size = io_size; ++ rs->set.io_mask = io_size - 1; ++ rs->set.io_shift = ffs(io_size) - 1; ++ rs->set.io_shift_mask = rs->set.chunk_mask & ~rs->set.io_mask; ++ ++ rs->set.pages_per_io = chunk_pages(io_size); ++ rs->set.sectors_per_dev = sectors_per_dev; ++ ++ rs->set.ei = -1; /* Indicate no failed device. */ ++ atomic_set(&rs->set.failed_devs, 0); ++ ++ rs->ti = ti; ++ ++ atomic_set(rec->io_count + IO_WORK, 0); ++ atomic_set(rec->io_count + IO_RECOVER, 0); ++ ++ /* Initialize io lock and queues. */ ++ spin_lock_init(&rs->io.in_lock); ++ bio_list_init(&rs->io.in); ++ bio_list_init(&rs->io.work); ++ ++ init_waitqueue_head(&rs->io.suspendq); /* Suspend waiters (dm-io). */ ++ ++ rec->nr_regions = dm_sector_div_up(sectors_per_dev, region_size); ++ ++ rec->rh = dm_region_hash_create(rs, dispatch_delayed_bios, wake_do_raid, ++ wakeup_all_recovery_waiters, ++ rs->ti->begin, MAX_RECOVER, dl, ++ region_size, rs->recover.nr_regions); ++ if (IS_ERR(rec->rh)) ++ goto bad_rh; ++ ++ /* Initialize stripe cache. */ ++ r = sc_init(rs, stripes); ++ if (r) ++ goto bad_sc; ++ ++ /* Create dm-io client context. */ ++ rs->sc.dm_io_client = dm_io_client_create(rs->set.raid_devs * ++ rs->set.pages_per_io); ++ if (IS_ERR(rs->sc.dm_io_client)) ++ goto bad_dm_io_client; ++ ++ /* REMOVEME: statistics. */ ++ stats_reset(rs); ++ ClearRSDevelStats(rs); /* Disnable development status. */ ++ ++ *raid_set = rs; ++ return 0; ++ ++bad_dirty_log: ++ TI_ERR_RET("Error creating dirty log", -ENOMEM); ++ ++ ++bad_chunk_size: ++ dm_dirty_log_destroy(dl); ++ TI_ERR("Chunk size larger than region size"); ++ ++bad_recover_io_size: ++ dm_dirty_log_destroy(dl); ++ TI_ERR("Recover stripe io size larger than region size"); ++ ++bad_array: ++ dm_dirty_log_destroy(dl); ++ TI_ERR("Arry too big"); ++ ++bad_alloc: ++ dm_dirty_log_destroy(dl); ++ TI_ERR_RET("Cannot allocate raid context", -ENOMEM); ++ ++bad_rh: ++ dm_dirty_log_destroy(dl); ++ ti->error = DM_MSG_PREFIX "Error creating dirty region hash"; ++ goto free_rs; ++ ++bad_sc: ++ ti->error = DM_MSG_PREFIX "Error creating stripe cache"; ++ goto free; ++ ++bad_dm_io_client: ++ ti->error = DM_MSG_PREFIX "Error allocating dm-io resources"; ++free: ++ dm_region_hash_destroy(rec->rh); ++ sc_exit(&rs->sc); ++ dm_region_hash_destroy(rec->rh); /* Destroys dirty log as well. */ ++free_rs: ++ kfree(rs); ++ return -ENOMEM; ++} ++ ++/* Free a RAID context (a RAID set). */ ++static void ++context_free(struct raid_set *rs, struct dm_target *ti, unsigned r) ++{ ++ while (r--) ++ dm_put_device(ti, rs->dev[r].dev); ++ ++ dm_io_client_destroy(rs->sc.dm_io_client); ++ sc_exit(&rs->sc); ++ dm_region_hash_destroy(rs->recover.rh); ++ dm_dirty_log_destroy(rs->recover.dl); ++ kfree(rs); ++} ++ ++/* Create work queue and initialize work. */ ++static int rs_workqueue_init(struct raid_set *rs) ++{ ++ struct dm_target *ti = rs->ti; ++ ++ rs->io.wq = create_singlethread_workqueue(DAEMON); ++ if (!rs->io.wq) ++ TI_ERR_RET("failed to create " DAEMON, -ENOMEM); ++ ++ INIT_DELAYED_WORK(&rs->io.dws, do_raid); ++ return 0; ++} ++ ++/* Return pointer to raid_type structure for raid name. */ ++static struct raid_type *get_raid_type(char *name) ++{ ++ struct raid_type *r = ARRAY_END(raid_types); ++ ++ while (r-- > raid_types) { ++ if (!strnicmp(STR_LEN(r->name, name))) ++ return r; ++ } ++ ++ return NULL; ++} ++ ++/* FIXME: factor out to dm core. */ ++static int multiple(sector_t a, sector_t b, sector_t *n) ++{ ++ sector_t r = a; ++ ++ sector_div(r, b); ++ *n = r; ++ return a == r * b; ++} ++ ++/* Log RAID set information to kernel log. */ ++static void raid_set_log(struct raid_set *rs, unsigned speed) ++{ ++ unsigned p; ++ char buf[BDEVNAME_SIZE]; ++ ++ for (p = 0; p < rs->set.raid_devs; p++) ++ DMINFO("/dev/%s is raid disk %u", ++ bdevname(rs->dev[p].dev->bdev, buf), p); ++ ++ DMINFO("%d/%d/%d sectors chunk/io/recovery size, %u stripes", ++ rs->set.chunk_size, rs->set.io_size, rs->recover.io_size, ++ atomic_read(&rs->sc.stripes)); ++ DMINFO("algorithm \"%s\", %u chunks with %uMB/s", rs->xor.f->name, ++ rs->xor.chunks, mbpers(rs, speed)); ++ DMINFO("%s set with net %u/%u devices", rs->set.raid_type->descr, ++ rs->set.data_devs, rs->set.raid_devs); ++} ++ ++/* Get all devices and offsets. */ ++static int ++dev_parms(struct dm_target *ti, struct raid_set *rs, ++ char **argv, int *p) ++{ ++ for (*p = 0; *p < rs->set.raid_devs; (*p)++, argv += 2) { ++ int r; ++ unsigned long long tmp; ++ struct raid_dev *dev = rs->dev + *p; ++ union dev_lookup dl = {.dev = dev }; ++ ++ /* Get offset and device. */ ++ r = sscanf(argv[1], "%llu", &tmp); ++ if (r != 1) ++ TI_ERR("Invalid RAID device offset parameter"); ++ ++ dev->start = tmp; ++ r = dm_get_device(ti, argv[0], dev->start, ++ rs->set.sectors_per_dev, ++ dm_table_get_mode(ti->table), &dev->dev); ++ if (r) ++ TI_ERR_RET("RAID device lookup failure", r); ++ ++ r = raid_dev_lookup(rs, bynumber, &dl); ++ if (r != -ENODEV && r < *p) { ++ (*p)++; /* Ensure dm_put_device() on actual device. */ ++ TI_ERR_RET("Duplicate RAID device", -ENXIO); ++ } ++ } ++ ++ return 0; ++} ++ ++/* Set recovery bandwidth. */ ++static INLINE void ++recover_set_bandwidth(struct raid_set *rs, unsigned bandwidth) ++{ ++ rs->recover.bandwidth = bandwidth; ++ rs->recover.bandwidth_work = 100 / bandwidth; ++} ++ ++/* Handle variable number of RAID parameters. */ ++static int ++raid_variable_parms(struct dm_target *ti, char **argv, ++ unsigned i, int *raid_parms, ++ int *chunk_size, int *chunk_size_parm, ++ int *stripes, int *stripes_parm, ++ int *io_size, int *io_size_parm, ++ int *recover_io_size, int *recover_io_size_parm, ++ int *bandwidth, int *bandwidth_parm) ++{ ++ /* Fetch # of variable raid parameters. */ ++ if (sscanf(argv[i++], "%d", raid_parms) != 1 || ++ !range_ok(*raid_parms, 0, 5)) ++ TI_ERR("Bad variable raid parameters number"); ++ ++ if (*raid_parms) { ++ /* ++ * If we've got variable RAID parameters, ++ * chunk size is the first one ++ */ ++ if (sscanf(argv[i++], "%d", chunk_size) != 1 || ++ (*chunk_size != -1 && ++ (!POWER_OF_2(*chunk_size) || ++ !range_ok(*chunk_size, IO_SIZE_MIN, CHUNK_SIZE_MAX)))) ++ TI_ERR("Invalid chunk size; must be 2^^n and <= 16384"); ++ ++ *chunk_size_parm = *chunk_size; ++ if (*chunk_size == -1) ++ *chunk_size = CHUNK_SIZE; ++ ++ /* ++ * In case we've got 2 or more variable raid ++ * parameters, the number of stripes is the second one ++ */ ++ if (*raid_parms > 1) { ++ if (sscanf(argv[i++], "%d", stripes) != 1 || ++ (*stripes != -1 && ++ !range_ok(*stripes, STRIPES_MIN, ++ STRIPES_MAX))) ++ TI_ERR("Invalid number of stripes: must " ++ "be >= 8 and <= 8192"); ++ } ++ ++ *stripes_parm = *stripes; ++ if (*stripes == -1) ++ *stripes = STRIPES; ++ ++ /* ++ * In case we've got 3 or more variable raid ++ * parameters, the io size is the third one. ++ */ ++ if (*raid_parms > 2) { ++ if (sscanf(argv[i++], "%d", io_size) != 1 || ++ (*io_size != -1 && ++ (!POWER_OF_2(*io_size) || ++ !range_ok(*io_size, IO_SIZE_MIN, ++ min(BIO_MAX_SECTORS / 2, ++ *chunk_size))))) ++ TI_ERR("Invalid io size; must " ++ "be 2^^n and less equal " ++ "min(BIO_MAX_SECTORS/2, chunk size)"); ++ } else ++ *io_size = *chunk_size; ++ ++ *io_size_parm = *io_size; ++ if (*io_size == -1) ++ *io_size = *chunk_size; ++ ++ /* ++ * In case we've got 4 variable raid parameters, ++ * the recovery stripe io_size is the fourth one ++ */ ++ if (*raid_parms > 3) { ++ if (sscanf(argv[i++], "%d", recover_io_size) != 1 || ++ (*recover_io_size != -1 && ++ (!POWER_OF_2(*recover_io_size) || ++ !range_ok(*recover_io_size, RECOVER_IO_SIZE_MIN, ++ BIO_MAX_SECTORS / 2)))) ++ TI_ERR("Invalid recovery io size; must be " ++ "2^^n and less equal BIO_MAX_SECTORS/2"); ++ } ++ ++ *recover_io_size_parm = *recover_io_size; ++ if (*recover_io_size == -1) ++ *recover_io_size = RECOVER_IO_SIZE; ++ ++ /* ++ * In case we've got 5 variable raid parameters, ++ * the recovery io bandwidth is the fifth one ++ */ ++ if (*raid_parms > 4) { ++ if (sscanf(argv[i++], "%d", bandwidth) != 1 || ++ (*bandwidth != -1 && ++ !range_ok(*bandwidth, BANDWIDTH_MIN, ++ BANDWIDTH_MAX))) ++ TI_ERR("Invalid recovery bandwidth " ++ "percentage; must be > 0 and <= 100"); ++ } ++ ++ *bandwidth_parm = *bandwidth; ++ if (*bandwidth == -1) ++ *bandwidth = BANDWIDTH; ++ } ++ ++ return 0; ++} ++ ++/* Parse optional locking parameters. */ ++static int ++raid_locking_parms(struct dm_target *ti, char **argv, ++ unsigned i, int *locking_parms, ++ struct dm_raid45_locking_type **locking_type) ++{ ++ *locking_parms = 0; ++ *locking_type = &locking_none; ++ ++ if (!strnicmp(argv[i], "none", strlen(argv[i]))) ++ *locking_parms = 1; ++ else if (!strnicmp(argv[i + 1], "locking", strlen(argv[i + 1]))) { ++ *locking_type = &locking_none; ++ *locking_parms = 2; ++ } else if (!strnicmp(argv[i + 1], "cluster", strlen(argv[i + 1]))) { ++ *locking_type = &locking_cluster; ++ /* FIXME: namespace. */ ++ *locking_parms = 3; ++ } ++ ++ return *locking_parms == 1 ? -EINVAL : 0; ++} ++ ++/* Set backing device information properties of RAID set. */ ++static void rs_set_bdi(struct raid_set *rs, unsigned stripes, unsigned chunks) ++{ ++ unsigned p, ra_pages; ++ struct mapped_device *md = dm_table_get_md(rs->ti->table); ++ struct backing_dev_info *bdi = &dm_disk(md)->queue->backing_dev_info; ++ ++ /* Set read-ahead for the RAID set and the component devices. */ ++ bdi->ra_pages = stripes * stripe_pages(rs, rs->set.io_size); ++ ra_pages = chunks * chunk_pages(rs->set.io_size); ++ for (p = rs->set.raid_devs; p--; ) { ++ struct request_queue *q = bdev_get_queue(rs->dev[p].dev->bdev); ++ ++ q->backing_dev_info.ra_pages = ra_pages; ++ } ++ ++ /* Set congested function and data. */ ++ bdi->congested_fn = raid_set_congested; ++ bdi->congested_data = rs; ++ ++ dm_put(md); ++} ++ ++/* Get backing device information properties of RAID set. */ ++static void rs_get_ra(struct raid_set *rs, unsigned *stripes, unsigned *chunks) ++{ ++ struct mapped_device *md = dm_table_get_md(rs->ti->table); ++ ++ *stripes = dm_disk(md)->queue->backing_dev_info.ra_pages ++ / stripe_pages(rs, rs->set.io_size); ++ *chunks = bdev_get_queue(rs->dev->dev->bdev)->backing_dev_info.ra_pages ++ / chunk_pages(rs->set.io_size); ++ ++ dm_put(md); ++} ++ ++/* ++ * Construct a RAID4/5 mapping: ++ * ++ * log_type #log_params \ ++ * raid_type [#parity_dev] #raid_variable_params \ ++ * [locking "none"/"cluster"] ++ * #raid_devs #dev_to_initialize [ ]{3,} ++ * ++ * log_type = "core"/"disk", ++ * #log_params = 1-3 (1-2 for core dirty log type, 3 for disk dirty log only) ++ * log_params = [dirty_log_path] region_size [[no]sync]) ++ * ++ * raid_type = "raid4", "raid5_la", "raid5_ra", "raid5_ls", "raid5_rs" ++ * ++ * #parity_dev = N if raid_type = "raid4" ++ * o N = -1: pick default = last device ++ * o N >= 0 and < #raid_devs: parity device index ++ * ++ * #raid_variable_params = 0-5; raid_params (-1 = default): ++ * [chunk_size [#stripes [io_size [recover_io_size [%recovery_bandwidth]]]]] ++ * o chunk_size (unit to calculate drive addresses; must be 2^^n, > 8 ++ * and <= CHUNK_SIZE_MAX) ++ * o #stripes is number of stripes allocated to stripe cache ++ * (must be > 1 and < STRIPES_MAX) ++ * o io_size (io unit size per device in sectors; must be 2^^n and > 8) ++ * o recover_io_size (io unit size per device for recovery in sectors; ++ must be 2^^n, > SECTORS_PER_PAGE and <= region_size) ++ * o %recovery_bandwith is the maximum amount spend for recovery during ++ * application io (1-100%) ++ * If raid_variable_params = 0, defaults will be used. ++ * Any raid_variable_param can be set to -1 to apply a default ++ * ++ * #raid_devs = N (N >= 3) ++ * ++ * #dev_to_initialize = N ++ * -1: initialize parity on all devices ++ * >= 0 and < #raid_devs: initialize raid_path; used to force reconstruction ++ * of a failed devices content after replacement ++ * ++ * = device_path (eg, /dev/sdd1) ++ * = begin at offset on ++ * ++ */ ++#define MIN_PARMS 13 ++static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv) ++{ ++ int bandwidth = BANDWIDTH, bandwidth_parm = -1, ++ chunk_size = CHUNK_SIZE, chunk_size_parm = -1, ++ dev_to_init, dl_parms, locking_parms, parity_parm, pi = -1, ++ i, io_size = IO_SIZE, io_size_parm = -1, ++ r, raid_devs, raid_parms, ++ recover_io_size = RECOVER_IO_SIZE, recover_io_size_parm = -1, ++ stripes = STRIPES, stripes_parm = -1; ++ unsigned speed; ++ sector_t tmp, sectors_per_dev; ++ struct dm_raid45_locking_type *locking; ++ struct raid_set *rs; ++ struct raid_type *raid_type; ++ ++ /* Ensure minimum number of parameters. */ ++ if (argc < MIN_PARMS) ++ TI_ERR("Not enough parameters"); ++ ++ /* Fetch # of dirty log parameters. */ ++ if (sscanf(argv[1], "%d", &dl_parms) != 1 ++ || !range_ok(dl_parms, 1, 4711)) ++ TI_ERR("Bad dirty log parameters number"); ++ ++ /* Check raid_type. */ ++ raid_type = get_raid_type(argv[dl_parms + 2]); ++ if (!raid_type) ++ TI_ERR("Bad raid type"); ++ ++ /* In case of RAID4, parity drive is selectable. */ ++ parity_parm = !!(raid_type->level == raid4); ++ ++ /* Handle variable number of RAID parameters. */ ++ r = raid_variable_parms(ti, argv, dl_parms + parity_parm + 3, ++ &raid_parms, ++ &chunk_size, &chunk_size_parm, ++ &stripes, &stripes_parm, ++ &io_size, &io_size_parm, ++ &recover_io_size, &recover_io_size_parm, ++ &bandwidth, &bandwidth_parm); ++ if (r) ++ return r; ++ ++ r = raid_locking_parms(ti, argv, ++ dl_parms + parity_parm + raid_parms + 4, ++ &locking_parms, &locking); ++ if (r) ++ return r; ++ ++ /* # of raid devices. */ ++ i = dl_parms + parity_parm + raid_parms + locking_parms + 4; ++ if (sscanf(argv[i], "%d", &raid_devs) != 1 || ++ raid_devs < raid_type->minimal_devs) ++ TI_ERR("Invalid number of raid devices"); ++ ++ /* In case of RAID4, check parity drive index is in limits. */ ++ if (raid_type->level == raid4) { ++ /* Fetch index of parity device. */ ++ if (sscanf(argv[dl_parms + 3], "%d", &pi) != 1 || ++ !range_ok(pi, 0, raid_devs - 1)) ++ TI_ERR("Invalid RAID4 parity device index"); ++ } ++ ++ /* ++ * Index of device to initialize starts at 0 ++ * ++ * o -1 -> don't initialize a particular device, ++ * o 0..raid_devs-1 -> initialize respective device ++ * (used for reconstruction of a replaced device) ++ */ ++ if (sscanf ++ (argv[dl_parms + parity_parm + raid_parms + locking_parms + 5], ++ "%d", &dev_to_init) != 1 ++ || !range_ok(dev_to_init, -1, raid_devs - 1)) ++ TI_ERR("Invalid number for raid device to initialize"); ++ ++ /* Check # of raid device arguments. */ ++ if (argc - dl_parms - parity_parm - raid_parms - 6 != ++ 2 * raid_devs) ++ TI_ERR("Wrong number of raid device/offset arguments"); ++ ++ /* ++ * Check that the table length is devisable ++ * w/o rest by (raid_devs - parity_devs) ++ */ ++ if (!multiple(ti->len, raid_devs - raid_type->parity_devs, ++ §ors_per_dev)) ++ TI_ERR ++ ("Target length not divisable by number of data devices"); ++ ++ /* ++ * Check that the device size is ++ * devisable w/o rest by chunk size ++ */ ++ if (!multiple(sectors_per_dev, chunk_size, &tmp)) ++ TI_ERR("Device length not divisable by chunk_size"); ++ ++ /**************************************************************** ++ * Now that we checked the constructor arguments -> ++ * let's allocate the RAID set ++ ****************************************************************/ ++ r = context_alloc(&rs, raid_type, stripes, chunk_size, io_size, ++ recover_io_size, raid_devs, sectors_per_dev, ++ ti, dl_parms, argv); ++ if (r) ++ return r; ++ ++ /* ++ * Set these here in order to avoid passing ++ * too many arguments to context_alloc() ++ */ ++ rs->set.dev_to_init_parm = dev_to_init; ++ rs->set.dev_to_init = dev_to_init; ++ rs->set.pi_parm = pi; ++ rs->set.pi = (pi == -1) ? rs->set.data_devs : pi; ++ rs->set.raid_parms = raid_parms; ++ rs->set.chunk_size_parm = chunk_size_parm; ++ rs->set.io_size_parm = io_size_parm; ++ rs->sc.stripes_parm = stripes_parm; ++ rs->recover.io_size_parm = recover_io_size_parm; ++ rs->recover.bandwidth_parm = bandwidth_parm; ++ recover_set_bandwidth(rs, bandwidth); ++ ++ /* Use locking type to lock stripe access. */ ++ rs->locking = locking; ++ ++ /* Get the device/offset tupels. */ ++ argv += dl_parms + 6 + parity_parm + raid_parms; ++ r = dev_parms(ti, rs, argv, &i); ++ if (r) ++ goto err; ++ ++ /* Initialize recovery. */ ++ rs->recover.start_jiffies = jiffies; ++ rs->recover.end_jiffies = 0; ++ recovery_region_reset(rs); ++ ++ /* Allow for recovery of any nosync regions. */ ++ SetRSRecover(rs); ++ ++ /* Set backing device information (eg. read ahead). */ ++ rs_set_bdi(rs, chunk_size * 2, io_size * 4); ++ SetRSCheckOverwrite(rs); /* Allow chunk overwrite checks. */ ++ ++ speed = xor_optimize(rs); /* Select best xor algorithm. */ ++ ++ /* Initialize work queue to handle this RAID set's io. */ ++ r = rs_workqueue_init(rs); ++ if (r) ++ goto err; ++ ++ raid_set_log(rs, speed); /* Log information about RAID set. */ ++ ++ /* ++ * Make sure that dm core only hands maximum io size ++ * length down and pays attention to io boundaries. ++ */ ++ ti->split_io = rs->set.io_size; ++ ti->private = rs; ++ return 0; ++ ++err: ++ context_free(rs, ti, i); ++ return r; ++} ++ ++/* ++ * Destruct a raid mapping ++ */ ++static void raid_dtr(struct dm_target *ti) ++{ ++ struct raid_set *rs = ti->private; ++ ++ /* Indicate recovery end so that ios in flight drain. */ ++ ClearRSRecover(rs); ++ ++ wake_do_raid(rs); /* Wake daemon. */ ++ wait_ios(rs); /* Wait for any io still being processed. */ ++ destroy_workqueue(rs->io.wq); ++ context_free(rs, ti, rs->set.raid_devs); ++} ++ ++/* Queues ios to RAID sets. */ ++static inline void queue_bio(struct raid_set *rs, struct bio *bio) ++{ ++ int wake; ++ struct bio_list *in = &rs->io.in; ++ spinlock_t *in_lock = &rs->io.in_lock; ++ ++ spin_lock_irq(in_lock); ++ wake = bio_list_empty(in); ++ bio_list_add(in, bio); ++ spin_unlock_irq(in_lock); ++ ++ /* Wake daemon if input list was empty. */ ++ if (wake) ++ wake_do_raid(rs); ++} ++ ++/* Raid mapping function. */ ++static int raid_map(struct dm_target *ti, struct bio *bio, ++ union map_info *map_context) ++{ ++ /* I don't want to waste stripe cache capacity. */ ++ if (bio_rw(bio) == READA) ++ return -EIO; ++ else { ++ struct raid_set *rs = ti->private; ++ ++ /* REMOVEME: statistics. */ ++ atomic_inc(rs->stats + ++ (bio_data_dir(bio) == WRITE ? ++ S_BIOS_WRITE : S_BIOS_READ)); ++ ++ /* ++ * Get io reference to be waiting for to drop ++ * to zero on device suspension/destruction. ++ */ ++ io_get(rs); ++ bio->bi_sector -= ti->begin; /* Remap sector. */ ++ queue_bio(rs, bio); /* Queue to the daemon. */ ++ return DM_MAPIO_SUBMITTED; /* Handle later. */ ++ } ++} ++ ++/* Device suspend. */ ++static void raid_postsuspend(struct dm_target *ti) ++{ ++ struct raid_set *rs = ti->private; ++ struct dm_dirty_log *dl = rs->recover.dl; ++ ++ SetRSSuspended(rs); ++ ++ if (RSRecover(rs)) ++ dm_rh_stop_recovery(rs->recover.rh); /* Wakes do_raid(). */ ++ else ++ wake_do_raid(rs); ++ ++ wait_ios(rs); /* Wait for completion of all ios being processed. */ ++ if (dl->type->postsuspend && dl->type->postsuspend(dl)) ++ /* Suspend dirty log. */ ++ /* FIXME: need better error handling. */ ++ DMWARN("log suspend failed"); ++} ++ ++/* Device resume. */ ++static void raid_resume(struct dm_target *ti) ++{ ++ struct raid_set *rs = ti->private; ++ struct recover *rec = &rs->recover; ++ struct dm_dirty_log *dl = rec->dl; ++ ++ if (dl->type->resume && dl->type->resume(dl)) ++ /* Resume dirty log. */ ++ /* FIXME: need better error handling. */ ++ DMWARN("log resume failed"); ++ ++ rec->nr_regions_to_recover = ++ rec->nr_regions - dl->type->get_sync_count(dl); ++ ++ ClearRSSuspended(rs); ++ ++ /* Reset any unfinished recovery. */ ++ if (RSRecover(rs)) { ++ recovery_region_reset(rs); ++ dm_rh_start_recovery(rec->rh);/* Calls wake_do_raid(). */ ++ } else ++ wake_do_raid(rs); ++} ++ ++static INLINE unsigned sc_size(struct raid_set *rs) ++{ ++ return to_sector(atomic_read(&rs->sc.stripes) * ++ (sizeof(struct stripe) + ++ (sizeof(struct stripe_set) + ++ (sizeof(struct page_list) + ++ to_bytes(rs->set.io_size) * ++ rs->set.raid_devs)) + ++ (rs->recover. ++ end_jiffies ? 0 : to_bytes(rs->set.raid_devs * ++ rs->recover. ++ io_size)))); ++} ++ ++/* REMOVEME: status output for development. */ ++static void ++raid_devel_stats(struct dm_target *ti, char *result, ++ unsigned *size, unsigned maxlen) ++{ ++ unsigned chunks, stripes, sz = *size; ++ unsigned long j; ++ char buf[BDEVNAME_SIZE], *p; ++ struct stats_map *sm, *sm_end = ARRAY_END(stats_map); ++ struct raid_set *rs = ti->private; ++ struct recover *rec = &rs->recover; ++ struct timespec ts; ++ ++ DMEMIT("%s ", version); ++ DMEMIT("io_inprocess=%d ", atomic_read(&rs->io.in_process)); ++ DMEMIT("io_inprocess_max=%d ", atomic_read(&rs->io.in_process_max)); ++ ++ for (sm = stats_map; sm < sm_end; sm++) ++ DMEMIT("%s%d", sm->str, atomic_read(rs->stats + sm->type)); ++ ++ DMEMIT(" overwrite=%s ", RSCheckOverwrite(rs) ? "on" : "off"); ++ DMEMIT("sc=%u/%u/%u/%u/%u ", rs->set.chunk_size, rs->set.io_size, ++ atomic_read(&rs->sc.stripes), rs->sc.hash.buckets, ++ sc_size(rs)); ++ ++ j = (rec->end_jiffies ? rec->end_jiffies : jiffies) - ++ rec->start_jiffies; ++ jiffies_to_timespec(j, &ts); ++ sprintf(buf, "%ld.%ld", ts.tv_sec, ts.tv_nsec); ++ p = strchr(buf, '.'); ++ p[3] = 0; ++ ++ DMEMIT("rg=%llu%s/%llu/%llu/%u %s ", ++ (unsigned long long) rec->nr_regions_recovered, ++ RSRegionGet(rs) ? "+" : "", ++ (unsigned long long) rec->nr_regions_to_recover, ++ (unsigned long long) rec->nr_regions, rec->bandwidth, buf); ++ ++ rs_get_ra(rs, &stripes, &chunks); ++ DMEMIT("ra=%u/%u ", stripes, chunks); ++ ++ *size = sz; ++} ++ ++static int ++raid_status(struct dm_target *ti, status_type_t type, ++ char *result, unsigned maxlen) ++{ ++ unsigned i, sz = 0; ++ char buf[BDEVNAME_SIZE]; ++ struct raid_set *rs = ti->private; ++ ++ switch (type) { ++ case STATUSTYPE_INFO: ++ /* REMOVEME: statistics. */ ++ if (RSDevelStats(rs)) ++ raid_devel_stats(ti, result, &sz, maxlen); ++ ++ DMEMIT("%u ", rs->set.raid_devs); ++ ++ for (i = 0; i < rs->set.raid_devs; i++) ++ DMEMIT("%s ", ++ format_dev_t(buf, rs->dev[i].dev->bdev->bd_dev)); ++ ++ DMEMIT("1 "); ++ for (i = 0; i < rs->set.raid_devs; i++) { ++ DMEMIT("%c", dev_operational(rs, i) ? 'A' : 'D'); ++ ++ if (rs->set.raid_type->level == raid4 && ++ i == rs->set.pi) ++ DMEMIT("p"); ++ ++ if (rs->set.dev_to_init == i) ++ DMEMIT("i"); ++ } ++ ++ break; ++ ++ case STATUSTYPE_TABLE: ++ sz = rs->recover.dl->type->status(rs->recover.dl, type, ++ result, maxlen); ++ DMEMIT("%s %u ", rs->set.raid_type->name, ++ rs->set.raid_parms); ++ ++ if (rs->set.raid_type->level == raid4) ++ DMEMIT("%d ", rs->set.pi_parm); ++ ++ if (rs->set.raid_parms) ++ DMEMIT("%d ", rs->set.chunk_size_parm); ++ ++ if (rs->set.raid_parms > 1) ++ DMEMIT("%d ", rs->sc.stripes_parm); ++ ++ if (rs->set.raid_parms > 2) ++ DMEMIT("%d ", rs->set.io_size_parm); ++ ++ if (rs->set.raid_parms > 3) ++ DMEMIT("%d ", rs->recover.io_size_parm); ++ ++ if (rs->set.raid_parms > 4) ++ DMEMIT("%d ", rs->recover.bandwidth_parm); ++ ++ DMEMIT("%u %d ", rs->set.raid_devs, rs->set.dev_to_init); ++ ++ for (i = 0; i < rs->set.raid_devs; i++) ++ DMEMIT("%s %llu ", ++ format_dev_t(buf, ++ rs->dev[i].dev->bdev->bd_dev), ++ (unsigned long long) rs->dev[i].start); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Message interface ++ */ ++enum raid_msg_actions { ++ act_bw, /* Recovery bandwidth switch. */ ++ act_dev, /* Device failure switch. */ ++ act_overwrite, /* Stripe overwrite check. */ ++ act_read_ahead, /* Set read ahead. */ ++ act_stats, /* Development statistics switch. */ ++ act_sc, /* Stripe cache switch. */ ++ ++ act_on, /* Set entity on. */ ++ act_off, /* Set entity off. */ ++ act_reset, /* Reset entity. */ ++ ++ act_set = act_on, /* Set # absolute. */ ++ act_grow = act_off, /* Grow # by an amount. */ ++ act_shrink = act_reset, /* Shrink # by an amount. */ ++}; ++ ++/* Turn a delta to absolute. */ ++static int _absolute(unsigned long action, int act, int r) ++{ ++ /* Make delta absolute. */ ++ if (test_bit(act_set, &action)) ++ ; ++ else if (test_bit(act_grow, &action)) ++ r += act; ++ else if (test_bit(act_shrink, &action)) ++ r = act - r; ++ else ++ r = -EINVAL; ++ ++ return r; ++} ++ ++ /* Change recovery io bandwidth. */ ++static int bandwidth_change(struct dm_msg *msg, void *context) ++{ ++ struct raid_set *rs = context; ++ int act = rs->recover.bandwidth; ++ int bandwidth = DM_MSG_INT_ARG(msg); ++ ++ if (range_ok(bandwidth, BANDWIDTH_MIN, BANDWIDTH_MAX)) { ++ /* Make delta bandwidth absolute. */ ++ bandwidth = _absolute(msg->action, act, bandwidth); ++ ++ /* Check range. */ ++ if (range_ok(bandwidth, BANDWIDTH_MIN, BANDWIDTH_MAX)) { ++ recover_set_bandwidth(rs, bandwidth); ++ return 0; ++ } ++ } ++ ++ set_bit(dm_msg_ret_arg, &msg->ret); ++ set_bit(dm_msg_ret_inval, &msg->ret); ++ return -EINVAL; ++} ++ ++/* Change state of a device (running/offline). */ ++/* FIXME: this only works while recovering!. */ ++static int device_state(struct dm_msg *msg, void *context) ++{ ++ int r; ++ const char *str = "is already "; ++ union dev_lookup dl = { .dev_name = DM_MSG_STR_ARG(msg) }; ++ struct raid_set *rs = context; ++ ++ r = raid_dev_lookup(rs, strchr(dl.dev_name, ':') ? ++ bymajmin : byname, &dl); ++ if (r == -ENODEV) { ++ DMERR("device %s is no member of this set", dl.dev_name); ++ return r; ++ } ++ ++ if (test_bit(act_off, &msg->action)) { ++ if (dev_operational(rs, r)) ++ str = ""; ++ } else if (!dev_operational(rs, r)) ++ str = ""; ++ ++ DMINFO("/dev/%s %s%s", dl.dev_name, str, ++ test_bit(act_off, &msg->action) ? "offline" : "running"); ++ ++ return test_bit(act_off, &msg->action) ? ++ raid_set_check_and_degrade(rs, NULL, r) : ++ raid_set_check_and_upgrade(rs, r); ++} ++ ++/* Set/reset development feature flags. */ ++static int devel_flags(struct dm_msg *msg, void *context) ++{ ++ struct raid_set *rs = context; ++ ++ if (test_bit(act_on, &msg->action)) ++ return test_and_set_bit(msg->spec->parm, ++ &rs->io.flags) ? -EPERM : 0; ++ else if (test_bit(act_off, &msg->action)) ++ return test_and_clear_bit(msg->spec->parm, ++ &rs->io.flags) ? 0 : -EPERM; ++ else if (test_bit(act_reset, &msg->action)) { ++ if (test_bit(act_stats, &msg->action)) { ++ stats_reset(rs); ++ goto on; ++ } else if (test_bit(act_overwrite, &msg->action)) { ++on: ++ set_bit(msg->spec->parm, &rs->io.flags); ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++ /* Set stripe and chunk read ahead pages. */ ++static int read_ahead_set(struct dm_msg *msg, void *context) ++{ ++ int stripes = DM_MSG_INT_ARGS(msg, 0); ++ int chunks = DM_MSG_INT_ARGS(msg, 1); ++ ++ if (range_ok(stripes, 1, 512) && ++ range_ok(chunks, 1, 512)) { ++ rs_set_bdi(context, stripes, chunks); ++ return 0; ++ } ++ ++ set_bit(dm_msg_ret_arg, &msg->ret); ++ set_bit(dm_msg_ret_inval, &msg->ret); ++ return -EINVAL; ++} ++ ++/* Resize the stripe cache. */ ++static int stripecache_resize(struct dm_msg *msg, void *context) ++{ ++ int act, stripes; ++ struct raid_set *rs = context; ++ ++ /* Deny permission in case the daemon is still shrinking!. */ ++ if (atomic_read(&rs->sc.stripes_to_shrink)) ++ return -EPERM; ++ ++ stripes = DM_MSG_INT_ARG(msg); ++ if (stripes > 0) { ++ act = atomic_read(&rs->sc.stripes); ++ ++ /* Make delta stripes absolute. */ ++ stripes = _absolute(msg->action, act, stripes); ++ ++ /* ++ * Check range and that the # of stripes changes. ++ * We can grow from gere but need to leave any ++ * shrinking to the worker for synchronization. ++ */ ++ if (range_ok(stripes, STRIPES_MIN, STRIPES_MAX)) { ++ if (stripes > act) ++ return sc_grow(&rs->sc, stripes - act, SC_GROW); ++ else if (stripes < act) { ++ atomic_set(&rs->sc.stripes_to_shrink, ++ act - stripes); ++ wake_do_raid(rs); ++ } ++ ++ return 0; ++ } ++ } ++ ++ set_bit(dm_msg_ret_arg, &msg->ret); ++ set_bit(dm_msg_ret_inval, &msg->ret); ++ return -EINVAL; ++} ++ ++/* Parse the RAID message action. */ ++/* ++ * 'ba[ndwidth] {se[t],g[row],sh[rink]} #' # e.g 'ba se 50' ++ * 'de{vice] o[ffline]/r[unning] DevName/maj:min' # e.g 'device o /dev/sda' ++ * "o[verwrite] {on,of[f],r[eset]}' # e.g. 'o of' ++ * "r[ead_ahead] set #stripes #chunks # e.g. 'r se 3 2' ++ * 'sta[tistics] {on,of[f],r[eset]}' # e.g. 'stat of' ++ * 'str[ipecache] {se[t],g[row],sh[rink]} #' # e.g. 'stripe set 1024' ++ * ++ */ ++static int ++raid_message(struct dm_target *ti, unsigned argc, char **argv) ++{ ++ /* Variables to store the parsed parameters im. */ ++ static int i[2]; ++ static unsigned long *i_arg[] = { ++ (unsigned long *) i + 0, ++ (unsigned long *) i + 1, ++ }; ++ static char *p; ++ static unsigned long *p_arg[] = { (unsigned long *) &p }; ++ ++ /* Declare all message option strings. */ ++ static char *str_sgs[] = { "set", "grow", "shrink" }; ++ static char *str_dev[] = { "running", "offline" }; ++ static char *str_oor[] = { "on", "off", "reset" }; ++ ++ /* Declare all actions. */ ++ static unsigned long act_sgs[] = { act_set, act_grow, act_shrink }; ++ static unsigned long act_oor[] = { act_on, act_off, act_reset }; ++ ++ /* Bandwidth option. */ ++ static struct dm_message_option bw_opt = { 3, str_sgs, act_sgs }; ++ static struct dm_message_argument bw_args = { ++ 1, i_arg, { dm_msg_int_t } ++ }; ++ ++ /* Device option. */ ++ static struct dm_message_option dev_opt = { 2, str_dev, act_oor }; ++ static struct dm_message_argument dev_args = { ++ 1, p_arg, { dm_msg_base_t } ++ }; ++ ++ /* Read ahead option. */ ++ static struct dm_message_option ra_opt = { 1, str_sgs, act_sgs }; ++ static struct dm_message_argument ra_args = { ++ 2, i_arg, { dm_msg_int_t, dm_msg_int_t } ++ }; ++ ++ static struct dm_message_argument null_args = { ++ 0, NULL, { dm_msg_int_t } ++ }; ++ ++ /* Overwrite and statistics option. */ ++ static struct dm_message_option ovr_stats_opt = { 3, str_oor, act_oor }; ++ ++ /* Sripecache option. */ ++ static struct dm_message_option stripe_opt = { 3, str_sgs, act_sgs }; ++ ++ /* Declare messages. */ ++ static struct dm_msg_spec specs[] = { ++ { "bandwidth", act_bw, &bw_opt, &bw_args, ++ 0, bandwidth_change }, ++ { "device", act_dev, &dev_opt, &dev_args, ++ 0, device_state }, ++ { "overwrite", act_overwrite, &ovr_stats_opt, &null_args, ++ RS_CHECK_OVERWRITE, devel_flags }, ++ { "read_ahead", act_read_ahead, &ra_opt, &ra_args, ++ 0, read_ahead_set }, ++ { "statistics", act_stats, &ovr_stats_opt, &null_args, ++ RS_DEVEL_STATS, devel_flags }, ++ { "stripecache", act_sc, &stripe_opt, &bw_args, ++ 0, stripecache_resize }, ++ }; ++ ++ /* The message for the parser. */ ++ struct dm_msg msg = { ++ .num_specs = ARRAY_SIZE(specs), ++ .specs = specs, ++ }; ++ ++ return dm_message_parse(TARGET, &msg, ti->private, argc, argv); ++} ++/* ++ * END message interface ++ */ ++ ++static struct target_type raid_target = { ++ .name = "raid45", ++ .version = {1, 0, 0}, ++ .module = THIS_MODULE, ++ .ctr = raid_ctr, ++ .dtr = raid_dtr, ++ .map = raid_map, ++ .postsuspend = raid_postsuspend, ++ .resume = raid_resume, ++ .status = raid_status, ++ .message = raid_message, ++}; ++ ++static void init_exit(const char *bad_msg, const char *good_msg, int r) ++{ ++ if (r) ++ DMERR("Failed to %sregister target [%d]", bad_msg, r); ++ else ++ DMINFO("%s %s", good_msg, version); ++} ++ ++static int __init dm_raid_init(void) ++{ ++ int r; ++ ++ r = dm_register_target(&raid_target); ++ init_exit("", "initialized", r); ++ return r; ++} ++ ++static void __exit dm_raid_exit(void) ++{ ++ dm_unregister_target(&raid_target); ++ init_exit("un", "exit", 0); ++} ++ ++/* Module hooks. */ ++module_init(dm_raid_init); ++module_exit(dm_raid_exit); ++ ++MODULE_DESCRIPTION(DM_NAME " raid4/5 target"); ++MODULE_AUTHOR("Heinz Mauelshagen "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/md/dm-raid45.h +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (C) 2006-2008 Red Hat, Inc. All rights reserved. ++ * ++ * Module Author: Heinz Mauelshagen (Mauelshagen@RedHat.com) ++ * ++ * Locking definitions for the device-mapper RAID45 target. ++ * ++ * This file is released under the GPL. ++ * ++ */ ++ ++#ifndef _DM_RAID45_H ++#define _DM_RAID45_H ++ ++/* Factor out to dm.h! */ ++#define STR_LEN(ptr, str) (ptr), (str), strlen((ptr)) ++ ++enum dm_lock_type { DM_RAID45_EX, DM_RAID45_SHARED }; ++ ++struct dm_raid45_locking_type { ++ /* Request a lock on a stripe. */ ++ void* (*lock)(sector_t key, enum dm_lock_type type); ++ ++ /* Release a lock on a stripe. */ ++ void (*unlock)(void *lock_handle); ++}; ++ ++#endif +--- a/drivers/md/dm-region-hash.c ++++ b/drivers/md/dm-region-hash.c +@@ -53,100 +53,6 @@ + * 'delayed_bios' fields of the regions. This is used from irq + * context, so all other uses will have to suspend local irqs. + *---------------------------------------------------------------*/ +-struct dm_region_hash { +- uint32_t region_size; +- unsigned region_shift; +- +- /* holds persistent region state */ +- struct dm_dirty_log *log; +- +- /* hash table */ +- rwlock_t hash_lock; +- mempool_t *region_pool; +- unsigned mask; +- unsigned nr_buckets; +- unsigned prime; +- unsigned shift; +- struct list_head *buckets; +- +- unsigned max_recovery; /* Max # of regions to recover in parallel */ +- +- spinlock_t region_lock; +- atomic_t recovery_in_flight; +- struct semaphore recovery_count; +- struct list_head clean_regions; +- struct list_head quiesced_regions; +- struct list_head recovered_regions; +- struct list_head failed_recovered_regions; +- +- /* +- * If there was a barrier failure no regions can be marked clean. +- */ +- int barrier_failure; +- +- void *context; +- sector_t target_begin; +- +- /* Callback function to schedule bios writes */ +- void (*dispatch_bios)(void *context, struct bio_list *bios); +- +- /* Callback function to wakeup callers worker thread. */ +- void (*wakeup_workers)(void *context); +- +- /* Callback function to wakeup callers recovery waiters. */ +- void (*wakeup_all_recovery_waiters)(void *context); +-}; +- +-struct dm_region { +- struct dm_region_hash *rh; /* FIXME: can we get rid of this ? */ +- region_t key; +- int state; +- +- struct list_head hash_list; +- struct list_head list; +- +- atomic_t pending; +- struct bio_list delayed_bios; +-}; +- +-/* +- * Conversion fns +- */ +-static region_t dm_rh_sector_to_region(struct dm_region_hash *rh, sector_t sector) +-{ +- return sector >> rh->region_shift; +-} +- +-sector_t dm_rh_region_to_sector(struct dm_region_hash *rh, region_t region) +-{ +- return region << rh->region_shift; +-} +-EXPORT_SYMBOL_GPL(dm_rh_region_to_sector); +- +-region_t dm_rh_bio_to_region(struct dm_region_hash *rh, struct bio *bio) +-{ +- return dm_rh_sector_to_region(rh, bio->bi_sector - rh->target_begin); +-} +-EXPORT_SYMBOL_GPL(dm_rh_bio_to_region); +- +-void *dm_rh_region_context(struct dm_region *reg) +-{ +- return reg->rh->context; +-} +-EXPORT_SYMBOL_GPL(dm_rh_region_context); +- +-region_t dm_rh_get_region_key(struct dm_region *reg) +-{ +- return reg->key; +-} +-EXPORT_SYMBOL_GPL(dm_rh_get_region_key); +- +-sector_t dm_rh_get_region_size(struct dm_region_hash *rh) +-{ +- return rh->region_size; +-} +-EXPORT_SYMBOL_GPL(dm_rh_get_region_size); +- + /* + * FIXME: shall we pass in a structure instead of all these args to + * dm_region_hash_create()???? +@@ -495,7 +401,7 @@ void dm_rh_update_states(struct dm_regio + } + EXPORT_SYMBOL_GPL(dm_rh_update_states); + +-static void rh_inc(struct dm_region_hash *rh, region_t region) ++void dm_rh_inc(struct dm_region_hash *rh, region_t region) + { + struct dm_region *reg; + +@@ -517,6 +423,7 @@ static void rh_inc(struct dm_region_hash + + read_unlock(&rh->hash_lock); + } ++EXPORT_SYMBOL_GPL(dm_rh_inc); + + void dm_rh_inc_pending(struct dm_region_hash *rh, struct bio_list *bios) + { +@@ -525,7 +432,7 @@ void dm_rh_inc_pending(struct dm_region_ + for (bio = bios->head; bio; bio = bio->bi_next) { + if (bio_empty_barrier(bio)) + continue; +- rh_inc(rh, dm_rh_bio_to_region(rh, bio)); ++ dm_rh_inc(rh, dm_rh_bio_to_region(rh, bio)); + } + } + EXPORT_SYMBOL_GPL(dm_rh_inc_pending); +@@ -614,8 +521,9 @@ static int __rh_recovery_prepare(struct + return 1; + } + +-void dm_rh_recovery_prepare(struct dm_region_hash *rh) ++int dm_rh_recovery_prepare(struct dm_region_hash *rh) + { ++ int r = 0; + /* Extra reference to avoid race with dm_rh_stop_recovery */ + atomic_inc(&rh->recovery_in_flight); + +@@ -624,13 +532,17 @@ void dm_rh_recovery_prepare(struct dm_re + if (__rh_recovery_prepare(rh) <= 0) { + atomic_dec(&rh->recovery_in_flight); + up(&rh->recovery_count); ++ r = -ENOENT; + break; + } + } + + /* Drop the extra reference */ +- if (atomic_dec_and_test(&rh->recovery_in_flight)) ++ if (atomic_dec_and_test(&rh->recovery_in_flight)) { + rh->wakeup_all_recovery_waiters(rh->context); ++ r = -ESRCH; ++ } ++ return r; + } + EXPORT_SYMBOL_GPL(dm_rh_recovery_prepare); + +--- a/drivers/md/dm.c ++++ b/drivers/md/dm.c +@@ -2673,6 +2673,7 @@ struct gendisk *dm_disk(struct mapped_de + { + return md->disk; + } ++EXPORT_SYMBOL_GPL(dm_disk); + + struct kobject *dm_kobject(struct mapped_device *md) + { +--- a/include/linux/dm-region-hash.h ++++ b/include/linux/dm-region-hash.h +@@ -15,8 +15,62 @@ + /*----------------------------------------------------------------- + * Region hash + *----------------------------------------------------------------*/ +-struct dm_region_hash; +-struct dm_region; ++struct dm_region_hash { ++ uint32_t region_size; ++ unsigned region_shift; ++ ++ /* holds persistent region state */ ++ struct dm_dirty_log *log; ++ ++ /* hash table */ ++ rwlock_t hash_lock; ++ mempool_t *region_pool; ++ unsigned mask; ++ unsigned nr_buckets; ++ unsigned prime; ++ unsigned shift; ++ struct list_head *buckets; ++ ++ unsigned max_recovery; /* Max # of regions to recover in parallel */ ++ ++ spinlock_t region_lock; ++ atomic_t recovery_in_flight; ++ struct semaphore recovery_count; ++ struct list_head clean_regions; ++ struct list_head quiesced_regions; ++ struct list_head recovered_regions; ++ struct list_head failed_recovered_regions; ++ ++ /* ++ * If there was a barrier failure no regions can be marked clean. ++ */ ++ int barrier_failure; ++ ++ void *context; ++ sector_t target_begin; ++ ++ /* Callback function to schedule bios writes */ ++ void (*dispatch_bios)(void *context, struct bio_list *bios); ++ ++ /* Callback function to wakeup callers worker thread. */ ++ void (*wakeup_workers)(void *context); ++ ++ /* Callback function to wakeup callers recovery waiters. */ ++ void (*wakeup_all_recovery_waiters)(void *context); ++}; ++ ++struct dm_region { ++ struct dm_region_hash *rh; /* FIXME: can we get rid of this ? */ ++ region_t key; ++ int state; ++ ++ struct list_head hash_list; ++ struct list_head list; ++ ++ atomic_t pending; ++ struct bio_list delayed_bios; ++}; ++ + + /* + * States a region can have. +@@ -45,19 +99,6 @@ void dm_region_hash_destroy(struct dm_re + struct dm_dirty_log *dm_rh_dirty_log(struct dm_region_hash *rh); + + /* +- * Conversion functions. +- */ +-region_t dm_rh_bio_to_region(struct dm_region_hash *rh, struct bio *bio); +-sector_t dm_rh_region_to_sector(struct dm_region_hash *rh, region_t region); +-void *dm_rh_region_context(struct dm_region *reg); +- +-/* +- * Get region size and key (ie. number of the region). +- */ +-sector_t dm_rh_get_region_size(struct dm_region_hash *rh); +-region_t dm_rh_get_region_key(struct dm_region *reg); +- +-/* + * Get/set/update region state (and dirty log). + * + */ +@@ -73,6 +114,7 @@ int dm_rh_flush(struct dm_region_hash *r + + /* Inc/dec pending count on regions. */ + void dm_rh_inc_pending(struct dm_region_hash *rh, struct bio_list *bios); ++void dm_rh_inc(struct dm_region_hash *rh, region_t region); + void dm_rh_dec(struct dm_region_hash *rh, region_t region); + + /* Delay bios on regions. */ +@@ -85,7 +127,7 @@ void dm_rh_mark_nosync(struct dm_region_ + */ + + /* Prepare some regions for recovery by starting to quiesce them. */ +-void dm_rh_recovery_prepare(struct dm_region_hash *rh); ++int dm_rh_recovery_prepare(struct dm_region_hash *rh); + + /* Try fetching a quiesced region for recovery. */ + struct dm_region *dm_rh_recovery_start(struct dm_region_hash *rh); +@@ -100,4 +142,39 @@ int dm_rh_recovery_in_flight(struct dm_r + void dm_rh_start_recovery(struct dm_region_hash *rh); + void dm_rh_stop_recovery(struct dm_region_hash *rh); + ++/* ++ * Conversion fns ++ */ ++static inline region_t dm_rh_sector_to_region(struct dm_region_hash *rh, ++ sector_t sector) ++{ ++ return sector >> rh->region_shift; ++} ++ ++static inline sector_t dm_rh_region_to_sector(struct dm_region_hash *rh, ++ region_t region) ++{ ++ return region << rh->region_shift; ++} ++ ++static inline region_t dm_rh_bio_to_region(struct dm_region_hash *rh, ++ struct bio *bio) ++{ ++ return dm_rh_sector_to_region(rh, bio->bi_sector - rh->target_begin); ++} ++ ++static inline void *dm_rh_region_context(struct dm_region *reg) ++{ ++ return reg->rh->context; ++} ++ ++static inline region_t dm_rh_get_region_key(struct dm_region *reg) ++{ ++ return reg->key; ++} ++ ++static inline sector_t dm_rh_get_region_size(struct dm_region_hash *rh) ++{ ++ return rh->region_size; ++} + #endif /* DM_REGION_HASH_H */ diff --git a/patches.suse/dmraid45-dm_dirty_log_create-api-fix b/patches.suse/dmraid45-dm_dirty_log_create-api-fix new file mode 100644 index 0000000..f0b1d01 --- /dev/null +++ b/patches.suse/dmraid45-dm_dirty_log_create-api-fix @@ -0,0 +1,25 @@ +From: Jeff Mahoney +Subject: dmraid45: dm_dirty_log_create API fix +Patch-mainline: not yet, depends on patches.suse/dm-raid45_2.6.27_20081027.patch + + 2.6.33 added an optional callback to dm_dirty_log_create for flush + operations. Eventually raid45 should have one but until then, this is + to allow it to build. + +Signed-off-by: Jeff Mahoney + +--- + drivers/md/dm-raid45.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/md/dm-raid45.c ++++ b/drivers/md/dm-raid45.c +@@ -3367,7 +3367,7 @@ context_alloc(struct raid_set **raid_set + */ + ti_len = ti->len; + ti->len = sectors_per_dev; +- dl = dm_dirty_log_create(argv[0], ti, dl_parms, argv + 2); ++ dl = dm_dirty_log_create(argv[0], ti, NULL, dl_parms, argv + 2); + ti->len = ti_len; + if (!dl) + goto bad_dirty_log; diff --git a/patches.suse/dmraid45-dm_get_device-takes-fewer-arguments b/patches.suse/dmraid45-dm_get_device-takes-fewer-arguments new file mode 100644 index 0000000..14b91e1 --- /dev/null +++ b/patches.suse/dmraid45-dm_get_device-takes-fewer-arguments @@ -0,0 +1,26 @@ +From: Jeff Mahoney +Subject: dmraid45: dm_get_device takes fewer arguments +Patch-mainline: Whenever dmraid45 is + + With 2.6.34-rc1, dm_get_device takes 4 args instead of 6. + +Signed-off-by: Jeff Mahoney +Acked-by: Jeff Mahoney +--- + drivers/md/dm-raid45.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/md/dm-raid45.c ++++ b/drivers/md/dm-raid45.c +@@ -3588,9 +3588,8 @@ dev_parms(struct dm_target *ti, struct r + TI_ERR("Invalid RAID device offset parameter"); + + dev->start = tmp; +- r = dm_get_device(ti, argv[0], dev->start, +- rs->set.sectors_per_dev, +- dm_table_get_mode(ti->table), &dev->dev); ++ r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), ++ &dev->dev); + if (r) + TI_ERR_RET("RAID device lookup failure", r); + diff --git a/patches.suse/export-release_open_intent b/patches.suse/export-release_open_intent new file mode 100644 index 0000000..ba8da1c --- /dev/null +++ b/patches.suse/export-release_open_intent @@ -0,0 +1,23 @@ +From: Jeff Mahoney +Subject: Export release_open_intent for NFS branches with aufs +Patch-mainline: never + + aufs requires a way to release an open intent when handling an error + condition after using NFSv4's atomic open. It was using put_filp, + but release_open_intent is more appropriate. + +Signed-off-by: Jeff Mahoney +--- + fs/namei.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -386,6 +386,7 @@ void release_open_intent(struct nameidat + else + fput(nd->intent.open.file); + } ++EXPORT_SYMBOL_GPL(release_open_intent); + + static inline struct dentry * + do_revalidate(struct dentry *dentry, struct nameidata *nd) diff --git a/patches.suse/export-security_inode_permission b/patches.suse/export-security_inode_permission new file mode 100644 index 0000000..2ad17d6 --- /dev/null +++ b/patches.suse/export-security_inode_permission @@ -0,0 +1,21 @@ +From: Jeff Mahoney +Subject: Export security_inode_permission for aufs +Patch-mainline: never + + This patch exports security_inode_permission for aufs. + +Signed-off-by: Jeff Mahoney +--- + security/security.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/security/security.c ++++ b/security/security.c +@@ -564,6 +564,7 @@ int security_inode_permission(struct ino + return 0; + return security_ops->inode_permission(inode, mask); + } ++EXPORT_SYMBOL_GPL(security_inode_permission); + + int security_inode_setattr(struct dentry *dentry, struct iattr *attr) + { diff --git a/patches.suse/export-sync_page_range b/patches.suse/export-sync_page_range new file mode 100644 index 0000000..3b04206 --- /dev/null +++ b/patches.suse/export-sync_page_range @@ -0,0 +1,178 @@ +From 48b8926b6b02382bc774efee2ed2cd6be8770ac0 Mon Sep 17 00:00:00 2001 +From: Michal Marek +Date: Fri, 20 Nov 2009 17:25:21 +0100 +Subject: [PATCH] Revert "vfs: Remove generic_osync_inode() and sync_page_range{_nolock}()" +References: bnc#557231 + +Commit 18f2ee705d98034b0f229a3202d827468d4bffd9 broke iscsitarget, revert +it temporarily. The exports are marged _GPL though. + +Signed-off-by: Michal Marek + +--- + fs/fs-writeback.c | 54 ++++++++++++++++++++++++++++++++++++++ + include/linux/fs.h | 5 +++ + include/linux/writeback.h | 4 ++ + mm/filemap.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 127 insertions(+) + +--- a/fs/fs-writeback.c ++++ b/fs/fs-writeback.c +@@ -1280,3 +1280,57 @@ int sync_inode(struct inode *inode, stru + return ret; + } + EXPORT_SYMBOL(sync_inode); ++ ++/** ++ * generic_osync_inode - flush all dirty data for a given inode to disk ++ * @inode: inode to write ++ * @mapping: the address_space that should be flushed ++ * @what: what to write and wait upon ++ * ++ * This can be called by file_write functions for files which have the ++ * O_SYNC flag set, to flush dirty writes to disk. ++ * ++ * @what is a bitmask, specifying which part of the inode's data should be ++ * written and waited upon. ++ * ++ * OSYNC_DATA: i_mapping's dirty data ++ * OSYNC_METADATA: the buffers at i_mapping->private_list ++ * OSYNC_INODE: the inode itself ++ */ ++ ++int generic_osync_inode(struct inode *inode, struct address_space *mapping, int what) ++{ ++ int err = 0; ++ int need_write_inode_now = 0; ++ int err2; ++ ++ if (what & OSYNC_DATA) ++ err = filemap_fdatawrite(mapping); ++ if (what & (OSYNC_METADATA|OSYNC_DATA)) { ++ err2 = sync_mapping_buffers(mapping); ++ if (!err) ++ err = err2; ++ } ++ if (what & OSYNC_DATA) { ++ err2 = filemap_fdatawait(mapping); ++ if (!err) ++ err = err2; ++ } ++ ++ spin_lock(&inode_lock); ++ if ((inode->i_state & I_DIRTY) && ++ ((what & OSYNC_INODE) || (inode->i_state & I_DIRTY_DATASYNC))) ++ need_write_inode_now = 1; ++ spin_unlock(&inode_lock); ++ ++ if (need_write_inode_now) { ++ err2 = write_inode_now(inode, 1); ++ if (!err) ++ err = err2; ++ } ++ else ++ inode_sync_wait(inode); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(generic_osync_inode); +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1459,6 +1459,11 @@ int fiemap_check_flags(struct fiemap_ext + #define DT_SOCK 12 + #define DT_WHT 14 + ++#define OSYNC_METADATA (1<<0) ++#define OSYNC_DATA (1<<1) ++#define OSYNC_INODE (1<<2) ++int generic_osync_inode(struct inode *, struct address_space *, int); ++ + /* + * This is the "filldir" function type, used by readdir() to let + * the kernel specify what kind of dirent layout it wants to have. +--- a/include/linux/writeback.h ++++ b/include/linux/writeback.h +@@ -148,6 +148,10 @@ int write_cache_pages(struct address_spa + struct writeback_control *wbc, writepage_t writepage, + void *data); + int do_writepages(struct address_space *mapping, struct writeback_control *wbc); ++int sync_page_range(struct inode *inode, struct address_space *mapping, ++ loff_t pos, loff_t count); ++int sync_page_range_nolock(struct inode *inode, struct address_space *mapping, ++ loff_t pos, loff_t count); + void set_page_dirty_balance(struct page *page, int page_mkwrite); + void writeback_set_ratelimit(void); + +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -334,6 +334,70 @@ int filemap_fdatawait_range(struct addre + EXPORT_SYMBOL(filemap_fdatawait_range); + + /** ++ * sync_page_range - write and wait on all pages in the passed range ++ * @inode: target inode ++ * @mapping: target address_space ++ * @pos: beginning offset in pages to write ++ * @count: number of bytes to write ++ * ++ * Write and wait upon all the pages in the passed range. This is a "data ++ * integrity" operation. It waits upon in-flight writeout before starting and ++ * waiting upon new writeout. If there was an IO error, return it. ++ * ++ * We need to re-take i_mutex during the generic_osync_inode list walk because ++ * it is otherwise livelockable. ++ */ ++int sync_page_range(struct inode *inode, struct address_space *mapping, ++ loff_t pos, loff_t count) ++{ ++ pgoff_t start = pos >> PAGE_CACHE_SHIFT; ++ pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT; ++ int ret; ++ ++ if (!mapping_cap_writeback_dirty(mapping) || !count) ++ return 0; ++ ret = filemap_fdatawrite_range(mapping, pos, pos + count - 1); ++ if (ret == 0) { ++ mutex_lock(&inode->i_mutex); ++ ret = generic_osync_inode(inode, mapping, OSYNC_METADATA); ++ mutex_unlock(&inode->i_mutex); ++ } ++ if (ret == 0) ++ ret = wait_on_page_writeback_range(mapping, start, end); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sync_page_range); ++ ++/** ++ * sync_page_range_nolock - write & wait on all pages in the passed range without locking ++ * @inode: target inode ++ * @mapping: target address_space ++ * @pos: beginning offset in pages to write ++ * @count: number of bytes to write ++ * ++ * Note: Holding i_mutex across sync_page_range_nolock() is not a good idea ++ * as it forces O_SYNC writers to different parts of the same file ++ * to be serialised right until io completion. ++ */ ++int sync_page_range_nolock(struct inode *inode, struct address_space *mapping, ++ loff_t pos, loff_t count) ++{ ++ pgoff_t start = pos >> PAGE_CACHE_SHIFT; ++ pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT; ++ int ret; ++ ++ if (!mapping_cap_writeback_dirty(mapping) || !count) ++ return 0; ++ ret = filemap_fdatawrite_range(mapping, pos, pos + count - 1); ++ if (ret == 0) ++ ret = generic_osync_inode(inode, mapping, OSYNC_METADATA); ++ if (ret == 0) ++ ret = wait_on_page_writeback_range(mapping, start, end); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sync_page_range_nolock); ++ ++/** + * filemap_fdatawait - wait for all under-writeback pages to complete + * @mapping: address space structure to wait for + * diff --git a/patches.suse/ext3-barrier-default b/patches.suse/ext3-barrier-default new file mode 100644 index 0000000..5c48c05 --- /dev/null +++ b/patches.suse/ext3-barrier-default @@ -0,0 +1,77 @@ +From: Chris Mason +Subject: make ext3 mount default to barrier=1 +Patch-mainline: probably never + +Hello everyone, + +This patch turns on barriers by default for ext3. mount -o barrier=0 +will turn them off. It also changes the ext3 fsync call to trigger a +barrier when a commit isn't done. + +It should be safe, but some extra review would be appreciated. + +Updated Apr 13 2009 jeffm: +- Added Kconfig option + +Acked-by: Jeff Mahoney + +--- + fs/ext3/Kconfig | 22 ++++++++++++++++++++++ + fs/ext3/fsync.c | 1 + + fs/ext3/super.c | 4 ++++ + 3 files changed, 27 insertions(+) + +--- a/fs/ext3/Kconfig ++++ b/fs/ext3/Kconfig +@@ -49,6 +49,28 @@ config EXT3_DEFAULTS_TO_ORDERED + privacy issues of data=writeback and are willing to make + that trade off, answer 'n'. + ++config EXT3_DEFAULTS_TO_BARRIERS_ENABLED ++ bool "Default to 'barrier=1' in ext3" ++ depends on EXT3_FS ++ help ++ Modern disk drives support write caches that can speed up writeback. ++ Some devices, in order to improve their performance statistics, ++ report that the write has been completed even when it has only ++ been committed to volatile cache memory. This can result in ++ severe corruption in the event of power loss. ++ ++ The -o barrier option enables the file system to direct the block ++ layer to issue a barrier, which ensures that the cache has been ++ flushed before proceeding. This can produce some slowdown in ++ certain environments, but allows higher end storage arrays with ++ battery-backed caches to report completes writes sooner than ++ would be otherwise possible. ++ ++ Without this option, disk write caches should be disabled if ++ you value data integrity over writeback performance. ++ ++ If unsure, say N. ++ + config EXT3_FS_XATTR + bool "Ext3 extended attributes" + depends on EXT3_FS +--- a/fs/ext3/fsync.c ++++ b/fs/ext3/fsync.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + +--- a/fs/ext3/super.c ++++ b/fs/ext3/super.c +@@ -1688,6 +1688,10 @@ static int ext3_fill_super (struct super + sbi->s_resuid = le16_to_cpu(es->s_def_resuid); + sbi->s_resgid = le16_to_cpu(es->s_def_resgid); + ++ /* enable barriers by default */ ++#ifdef CONFIG_EXT3_DEFAULTS_TO_BARRIERS_ENABLED ++ set_opt(sbi->s_mount_opt, BARRIER); ++#endif + set_opt(sbi->s_mount_opt, RESERVATION); + + if (!parse_options ((char *) data, sb, &journal_inum, &journal_devnum, diff --git a/patches.suse/file-capabilities-disable-by-default.diff b/patches.suse/file-capabilities-disable-by-default.diff new file mode 100644 index 0000000..d8930d8 --- /dev/null +++ b/patches.suse/file-capabilities-disable-by-default.diff @@ -0,0 +1,56 @@ +From: Andreas Gruenbacher +Subject: Disable file capabilities by default +Patch-mainline: probably never + +Disable file capabilities by default: we are still lacking documentation +and file capability awareness in system management tools. + +Signed-off-by: Andreas Gruenbacher + +--- + Documentation/kernel-parameters.txt | 8 +++++++- + kernel/capability.c | 9 ++++++++- + 2 files changed, 15 insertions(+), 2 deletions(-) + +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -1697,7 +1697,13 @@ and is between 256 and 4096 characters. + + no_file_caps Tells the kernel not to honor file capabilities. The + only way then for a file to be executed with privilege +- is to be setuid root or executed by root. ++ is to be setuid root or executed by root. They ++ default to disabled. ++ ++ file_caps Tells the kernel to honor file capabilities. The ++ only way then for a file to be executed with privilege ++ is to be setuid root or executed by root. They default ++ to disabled. + + nohalt [IA-64] Tells the kernel not to use the power saving + function PAL_HALT_LIGHT when idle. This increases +--- a/kernel/capability.c ++++ b/kernel/capability.c +@@ -29,7 +29,7 @@ EXPORT_SYMBOL(__cap_empty_set); + EXPORT_SYMBOL(__cap_full_set); + EXPORT_SYMBOL(__cap_init_eff_set); + +-int file_caps_enabled = 1; ++int file_caps_enabled; + + static int __init file_caps_disable(char *str) + { +@@ -38,6 +38,13 @@ static int __init file_caps_disable(char + } + __setup("no_file_caps", file_caps_disable); + ++static int __init file_caps_enable(char *str) ++{ ++ file_caps_enabled = 1; ++ return 1; ++} ++__setup("file_caps", file_caps_enable); ++ + /* + * More recent versions of libcap are available from: + * diff --git a/patches.suse/files-slab-rcu.patch b/patches.suse/files-slab-rcu.patch new file mode 100644 index 0000000..1e54d6d --- /dev/null +++ b/patches.suse/files-slab-rcu.patch @@ -0,0 +1,330 @@ +From: Nick Piggin +Subject: SLAB_DESTROY_BY_RCU for file slab +Patch-mainline: not yet + +Use SLAB_DESTROY_BY_RCU for file slab cache. Ensure we have the correct +object by using a spinlock to protect the refcount rather than having it +atomic (problem with it being atomic is having to release the last ref +on a file we have incorrectly picked up a reference to). + +This improves single threaded repeated open/close performance by 28% by +avoiding the full RCU cycle. + +Signed-off-by: Nick Piggin +--- + drivers/net/ppp_generic.c | 4 +- + drivers/scsi/osst.c | 2 - + drivers/scsi/st.c | 2 - + fs/aio.c | 4 +- + fs/file_table.c | 70 +++++++++++++++++++++++----------------------- + fs/open.c | 2 - + include/linux/fs.h | 25 +++++++++++++--- + kernel/perf_event.c | 4 +- + net/sched/sch_atm.c | 4 +- + net/unix/garbage.c | 2 - + 10 files changed, 69 insertions(+), 50 deletions(-) + +--- a/drivers/net/ppp_generic.c ++++ b/drivers/net/ppp_generic.c +@@ -590,12 +590,12 @@ static long ppp_ioctl(struct file *file, + if (file == ppp->owner) + ppp_shutdown_interface(ppp); + } +- if (atomic_long_read(&file->f_count) <= 2) { ++ if (file->f_count <= 2) { + ppp_release(NULL, file); + err = 0; + } else + printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n", +- atomic_long_read(&file->f_count)); ++ file->f_count); + unlock_kernel(); + return err; + } +--- a/drivers/scsi/osst.c ++++ b/drivers/scsi/osst.c +@@ -4824,7 +4824,7 @@ static int os_scsi_tape_flush(struct fil + struct osst_request * SRpnt = NULL; + char * name = tape_name(STp); + +- if (file_count(filp) > 1) ++ if (filp->f_count > 1) + return 0; + + if ((STps->rw == ST_WRITING || STp->dirty) && !STp->pos_unknown) { +--- a/drivers/scsi/st.c ++++ b/drivers/scsi/st.c +@@ -1272,7 +1272,7 @@ static int st_flush(struct file *filp, f + struct st_partstat *STps = &(STp->ps[STp->partition]); + char *name = tape_name(STp); + +- if (file_count(filp) > 1) ++ if (filp->f_count > 1) + return 0; + + if (STps->rw == ST_WRITING && !STp->pos_unknown) { +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -545,7 +545,7 @@ static void aio_fput_routine(struct work + static int __aio_put_req(struct kioctx *ctx, struct kiocb *req) + { + dprintk(KERN_DEBUG "aio_put(%p): f_count=%ld\n", +- req, atomic_long_read(&req->ki_filp->f_count)); ++ req, req->ki_filp->f_count); + + assert_spin_locked(&ctx->ctx_lock); + +@@ -563,7 +563,7 @@ static int __aio_put_req(struct kioctx * + * we would not be holding the last reference to the file*, so + * this function will be executed w/out any aio kthread wakeup. + */ +- if (unlikely(atomic_long_dec_and_test(&req->ki_filp->f_count))) { ++ if (unlikely(file_dec_and_test(req->ki_filp))) { + get_ioctx(ctx); + spin_lock(&fput_lock); + list_add(&req->ki_list, &fput_head); +--- a/fs/file_table.c ++++ b/fs/file_table.c +@@ -40,19 +40,12 @@ static struct kmem_cache *filp_cachep __ + + static struct percpu_counter nr_files __cacheline_aligned_in_smp; + +-static inline void file_free_rcu(struct rcu_head *head) +-{ +- struct file *f = container_of(head, struct file, f_u.fu_rcuhead); +- +- put_cred(f->f_cred); +- kmem_cache_free(filp_cachep, f); +-} +- + static inline void file_free(struct file *f) + { + percpu_counter_dec(&nr_files); + file_check_state(f); +- call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); ++ put_cred(f->f_cred); ++ kmem_cache_free(filp_cachep, f); + } + + /* +@@ -127,7 +120,7 @@ struct file *get_empty_filp(void) + goto fail_sec; + + INIT_LIST_HEAD(&f->f_u.fu_list); +- atomic_long_set(&f->f_count, 1); ++ f->f_count = 1; + rwlock_init(&f->f_owner.lock); + f->f_cred = get_cred(cred); + spin_lock_init(&f->f_lock); +@@ -196,7 +189,7 @@ EXPORT_SYMBOL(alloc_file); + + void fput(struct file *file) + { +- if (atomic_long_dec_and_test(&file->f_count)) ++ if (unlikely(file_dec_and_test(file))) + __fput(file); + } + +@@ -267,21 +260,38 @@ void __fput(struct file *file) + mntput(mnt); + } + +-struct file *fget(unsigned int fd) ++static inline struct file *get_stable_file(struct files_struct *files, unsigned int fd) + { ++ struct fdtable *fdt; + struct file *file; +- struct files_struct *files = current->files; + + rcu_read_lock(); +- file = fcheck_files(files, fd); +- if (file) { +- if (!atomic_long_inc_not_zero(&file->f_count)) { +- /* File object ref couldn't be taken */ +- rcu_read_unlock(); +- return NULL; ++ fdt = files_fdtable(files); ++ if (likely(fd < fdt->max_fds)) { ++ file = rcu_dereference(fdt->fd[fd]); ++ if (file) { ++ spin_lock(&file->f_lock); ++ if (unlikely(file != fdt->fd[fd] || !file->f_count)) { ++ spin_unlock(&file->f_lock); ++ file = NULL; ++ goto out; ++ } ++ file->f_count++; ++ spin_unlock(&file->f_lock); + } +- } ++ } else ++ file = NULL; ++out: + rcu_read_unlock(); ++ return file; ++} ++ ++struct file *fget(unsigned int fd) ++{ ++ struct file *file; ++ struct files_struct *files = current->files; ++ ++ file = get_stable_file(files, fd); + + return file; + } +@@ -300,20 +310,12 @@ struct file *fget_light(unsigned int fd, + struct file *file; + struct files_struct *files = current->files; + +- *fput_needed = 0; + if (likely((atomic_read(&files->count) == 1))) { ++ *fput_needed = 0; + file = fcheck_files(files, fd); + } else { +- rcu_read_lock(); +- file = fcheck_files(files, fd); +- if (file) { +- if (atomic_long_inc_not_zero(&file->f_count)) +- *fput_needed = 1; +- else +- /* Didn't get the reference, someone's freed */ +- file = NULL; +- } +- rcu_read_unlock(); ++ *fput_needed = 1; ++ file = get_stable_file(files, fd); + } + + return file; +@@ -322,7 +324,7 @@ struct file *fget_light(unsigned int fd, + + void put_filp(struct file *file) + { +- if (atomic_long_dec_and_test(&file->f_count)) { ++ if (unlikely(file_dec_and_test(file))) { + security_file_free(file); + file_kill(file); + file_free(file); +@@ -388,7 +390,7 @@ retry: + struct vfsmount *mnt; + if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) + continue; +- if (!file_count(f)) ++ if (!f->f_count) + continue; + if (!(f->f_mode & FMODE_WRITE)) + continue; +@@ -414,7 +416,7 @@ void __init files_init(unsigned long mem + int n; + + filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, +- SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); ++ SLAB_HWCACHE_ALIGN | SLAB_DESTROY_BY_RCU | SLAB_PANIC, NULL); + + /* + * One file with associated inode and dcache is very roughly 1K. +--- a/fs/open.c ++++ b/fs/open.c +@@ -1114,7 +1114,7 @@ int filp_close(struct file *filp, fl_own + { + int retval = 0; + +- if (!file_count(filp)) { ++ if (unlikely(!filp->f_count)) { + printk(KERN_ERR "VFS: Close: file count is 0\n"); + return 0; + } +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -920,9 +920,10 @@ struct file { + #define f_dentry f_path.dentry + #define f_vfsmnt f_path.mnt + const struct file_operations *f_op; +- spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */ +- atomic_long_t f_count; ++ /* f_lock protects f_count, f_ep_links, f_flags, no IRQ */ ++ spinlock_t f_lock; + unsigned int f_flags; ++ long f_count; + fmode_t f_mode; + loff_t f_pos; + struct fown_struct f_owner; +@@ -949,8 +950,24 @@ extern spinlock_t files_lock; + #define file_list_lock() spin_lock(&files_lock); + #define file_list_unlock() spin_unlock(&files_lock); + +-#define get_file(x) atomic_long_inc(&(x)->f_count) +-#define file_count(x) atomic_long_read(&(x)->f_count) ++static inline void get_file(struct file *f) ++{ ++ spin_lock(&f->f_lock); ++ f->f_count++; ++ spin_unlock(&f->f_lock); ++} ++ ++static inline int file_dec_and_test(struct file *f) ++{ ++ int ret; ++ ++ spin_lock(&f->f_lock); ++ f->f_count--; ++ ret = (f->f_count == 0); ++ spin_unlock(&f->f_lock); ++ ++ return ret; ++} + + #ifdef CONFIG_DEBUG_WRITECOUNT + static inline void file_take_write(struct file *f) +--- a/kernel/perf_event.c ++++ b/kernel/perf_event.c +@@ -4617,7 +4617,7 @@ static int perf_event_set_output(struct + if (event->data) + goto out; + +- atomic_long_inc(&output_file->f_count); ++ get_file(output_file); + + set: + mutex_lock(&event->mmap_mutex); +@@ -4878,7 +4878,7 @@ inherit_event(struct perf_event *parent_ + * we are in the parent and we know that the filp still + * exists and has a nonzero count: + */ +- atomic_long_inc(&parent_event->filp->f_count); ++ get_file(parent_event->filp); + + /* + * Link this into the parent event's child list +--- a/net/sched/sch_atm.c ++++ b/net/sched/sch_atm.c +@@ -164,7 +164,7 @@ static void atm_tc_put(struct Qdisc *sch + tcf_destroy_chain(&flow->filter_list); + if (flow->sock) { + pr_debug("atm_tc_put: f_count %ld\n", +- file_count(flow->sock->file)); ++ flow->sock->file->f_count); + flow->vcc->pop = flow->old_pop; + sockfd_put(flow->sock); + } +@@ -260,7 +260,7 @@ static int atm_tc_change(struct Qdisc *s + sock = sockfd_lookup(fd, &error); + if (!sock) + return error; /* f_count++ */ +- pr_debug("atm_tc_change: f_count %ld\n", file_count(sock->file)); ++ pr_debug("atm_tc_change: f_count %ld\n", sock->file->f_count); + if (sock->ops->family != PF_ATMSVC && sock->ops->family != PF_ATMPVC) { + error = -EPROTOTYPE; + goto err_out; +--- a/net/unix/garbage.c ++++ b/net/unix/garbage.c +@@ -311,7 +311,7 @@ void unix_gc(void) + long total_refs; + long inflight_refs; + +- total_refs = file_count(u->sk.sk_socket->file); ++ total_refs = u->sk.sk_socket->file->f_count; + inflight_refs = atomic_long_read(&u->inflight); + + BUG_ON(inflight_refs < 1); diff --git a/patches.suse/fs-knows-MAY_APPEND.diff b/patches.suse/fs-knows-MAY_APPEND.diff new file mode 100644 index 0000000..f439724 --- /dev/null +++ b/patches.suse/fs-knows-MAY_APPEND.diff @@ -0,0 +1,59 @@ +From: Andreas Gruenbacher +Subject: Allow filesystems to handle MAY_APPEND +Patch-mainline: not yet + +The MS_WITHAPPEND super_block flag tells the vfs that the permission +inode operation understands the MAY_APPEND flag. This is required for +implementing permission models which go beyond the traditional UNIX +semantics. + +If a filesystem does not set the flag, the behavior is unchanged. + +Signed-off-by: Andreas Gruenbacher + +--- + fs/namei.c | 6 +++++- + include/linux/fs.h | 2 ++ + 2 files changed, 7 insertions(+), 1 deletion(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -252,6 +252,7 @@ int generic_permission(struct inode *ino + int inode_permission(struct inode *inode, int mask) + { + int retval; ++ int submask = mask; + + if (mask & MAY_WRITE) { + umode_t mode = inode->i_mode; +@@ -270,8 +271,11 @@ int inode_permission(struct inode *inode + return -EACCES; + } + ++ if (!IS_WITHAPPEND(inode)) ++ submask &= ~MAY_APPEND; ++ + if (inode->i_op->permission) +- retval = inode->i_op->permission(inode, mask); ++ retval = inode->i_op->permission(inode, submask); + else + retval = generic_permission(inode, mask, inode->i_op->check_acl); + +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -209,6 +209,7 @@ struct inodes_stat_t { + #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ + #define MS_I_VERSION (1<<23) /* Update inode I_version field */ + #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ ++#define MS_WITHAPPEND (1<<25) /* iop->permission() understands MAY_APPEND */ + #define MS_ACTIVE (1<<30) + #define MS_NOUSER (1<<31) + +@@ -259,6 +260,7 @@ struct inodes_stat_t { + #define IS_MANDLOCK(inode) __IS_FLG(inode, MS_MANDLOCK) + #define IS_NOATIME(inode) __IS_FLG(inode, MS_RDONLY|MS_NOATIME) + #define IS_I_VERSION(inode) __IS_FLG(inode, MS_I_VERSION) ++#define IS_WITHAPPEND(inode) __IS_FLG(inode, MS_WITHAPPEND) + + #define IS_NOQUOTA(inode) ((inode)->i_flags & S_NOQUOTA) + #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND) diff --git a/patches.suse/fs-may_iops.diff b/patches.suse/fs-may_iops.diff new file mode 100644 index 0000000..b96b1a0 --- /dev/null +++ b/patches.suse/fs-may_iops.diff @@ -0,0 +1,145 @@ +From: Andreas Gruenbacher +Subject: VFS hooks for per-filesystem permission models +Patch-mainline: Not yet + +Add may_create and may_delete inode operations that filesystems can +implement in order to override the vfs provided default behavior. +This is required for implementing permission models which go beyond +the traditional UNIX semantics. + +If a filesystem does not implement these hooks, the behavior is +unchanged. + +Signed-off-by: Andreas Gruenbacher + +--- + fs/namei.c | 48 +++++++++++++++++++++++++++++++++++++----------- + include/linux/fs.h | 2 ++ + 2 files changed, 39 insertions(+), 11 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -1320,13 +1320,24 @@ static int may_delete(struct inode *dir, + BUG_ON(victim->d_parent->d_inode != dir); + audit_inode_child(victim, dir); + +- error = inode_permission(dir, MAY_WRITE | MAY_EXEC); ++ if (dir->i_op->may_delete) { ++ if (IS_RDONLY(dir)) ++ return -EROFS; ++ if (IS_IMMUTABLE(dir)) ++ return -EACCES; ++ error = dir->i_op->may_delete(dir, victim->d_inode); ++ if (!error) ++ error = security_inode_permission(dir, MAY_WRITE | MAY_EXEC); ++ } else { ++ error = inode_permission(dir, MAY_WRITE | MAY_EXEC); ++ if (!error && check_sticky(dir, victim->d_inode)) ++ error = -EPERM; ++ } + if (error) + return error; + if (IS_APPEND(dir)) + return -EPERM; +- if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)|| +- IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode)) ++ if (IS_APPEND(victim->d_inode) || IS_IMMUTABLE(victim->d_inode)) + return -EPERM; + if (isdir) { + if (!S_ISDIR(victim->d_inode->i_mode)) +@@ -1350,13 +1361,28 @@ static int may_delete(struct inode *dir, + * 3. We should have write and exec permissions on dir + * 4. We can't do it if dir is immutable (done in permission()) + */ +-static inline int may_create(struct inode *dir, struct dentry *child) ++static inline int may_create(struct inode *dir, struct dentry *child, ++ int isdir) + { ++ int error; ++ + if (child->d_inode) + return -EEXIST; + if (IS_DEADDIR(dir)) + return -ENOENT; +- return inode_permission(dir, MAY_WRITE | MAY_EXEC); ++ ++ if (dir->i_op->may_create) { ++ if (IS_RDONLY(dir)) ++ return -EROFS; ++ if (IS_IMMUTABLE(dir)) ++ return -EACCES; ++ error = dir->i_op->may_create(dir, isdir); ++ if (!error) ++ error = security_inode_permission(dir, MAY_WRITE | MAY_EXEC); ++ } else ++ error = inode_permission(dir, MAY_WRITE | MAY_EXEC); ++ ++ return error; + } + + /* +@@ -1404,7 +1430,7 @@ void unlock_rename(struct dentry *p1, st + int vfs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) + { +- int error = may_create(dir, dentry); ++ int error = may_create(dir, dentry, 0); + + if (error) + return error; +@@ -1963,7 +1989,7 @@ EXPORT_SYMBOL_GPL(lookup_create); + + int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) + { +- int error = may_create(dir, dentry); ++ int error = may_create(dir, dentry, 0); + + if (error) + return error; +@@ -2067,7 +2093,7 @@ SYSCALL_DEFINE3(mknod, const char __user + + int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) + { +- int error = may_create(dir, dentry); ++ int error = may_create(dir, dentry, 1); + + if (error) + return error; +@@ -2350,7 +2376,7 @@ SYSCALL_DEFINE1(unlink, const char __use + + int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) + { +- int error = may_create(dir, dentry); ++ int error = may_create(dir, dentry, 0); + + if (error) + return error; +@@ -2423,7 +2449,7 @@ int vfs_link(struct dentry *old_dentry, + if (!inode) + return -ENOENT; + +- error = may_create(dir, new_dentry); ++ error = may_create(dir, new_dentry, S_ISDIR(inode->i_mode)); + if (error) + return error; + +@@ -2636,7 +2662,7 @@ int vfs_rename(struct inode *old_dir, st + return error; + + if (!new_dentry->d_inode) +- error = may_create(new_dir, new_dentry); ++ error = may_create(new_dir, new_dentry, is_dir); + else + error = may_delete(new_dir, new_dentry, is_dir); + if (error) +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1531,6 +1531,8 @@ struct inode_operations { + void (*truncate) (struct inode *); + int (*permission) (struct inode *, int); + int (*check_acl)(struct inode *, int); ++ int (*may_create) (struct inode *, int); ++ int (*may_delete) (struct inode *, struct inode *); + int (*setattr) (struct dentry *, struct iattr *); + int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); + int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); diff --git a/patches.suse/genksyms-add-override-flag.diff b/patches.suse/genksyms-add-override-flag.diff new file mode 100644 index 0000000..daad3f7 --- /dev/null +++ b/patches.suse/genksyms-add-override-flag.diff @@ -0,0 +1,116 @@ +From: Andreas Gruenbacher +Subject: genksyms: add --override flag +Patch-mainline: not yet + +Add --override flag to genksyms to allow overriding types with old +definitions using the 'override' keyword. This is similar to -p --preserve, +but it doesn't abort the build if a symtype cannot be preserved + +[mmarek: added KBUILD_OVERRIDE env var to set this globally for the entire + build] +--- + scripts/Makefile.build | 1 + + scripts/genksyms/genksyms.c | 21 +++++++++++++++------ + 2 files changed, 16 insertions(+), 6 deletions(-) + +--- a/scripts/Makefile.build ++++ b/scripts/Makefile.build +@@ -158,6 +158,7 @@ cmd_gensymtypes = + $(CPP) -D__GENKSYMS__ $(c_flags) $< | \ + $(GENKSYMS) $(if $(strip $(1)), -T $(@:.o=.symtypes)) -a $(ARCH) \ + $(if $(KBUILD_PRESERVE),-p) \ ++ $(if $(KBUILD_OVERRIDE),-o) \ + -r $(firstword $(wildcard $(basename $@).symref /dev/null)) + + quiet_cmd_cc_symtypes_c = SYM $(quiet_modtag) $@ +--- a/scripts/genksyms/genksyms.c ++++ b/scripts/genksyms/genksyms.c +@@ -43,7 +43,7 @@ int cur_line = 1; + char *cur_filename; + + static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types, +- flag_preserve, flag_warnings; ++ flag_override, flag_preserve, flag_warnings; + static const char *arch = ""; + static const char *mod_prefix = ""; + +@@ -200,7 +200,7 @@ static struct symbol *__add_symbol(const + sym->is_declared = 1; + return sym; + } else if (!sym->is_declared) { +- if (sym->is_override && flag_preserve) { ++ if (sym->is_override && flag_override) { + print_location(); + fprintf(stderr, "ignoring "); + print_type_name(type, name); +@@ -586,11 +586,13 @@ void export_symbol(const char *name) + struct symbol *n = sym->expansion_trail; + + if (sym->status != STATUS_UNCHANGED) { ++ int fail = sym->is_override && flag_preserve; ++ + if (!has_changed) { + print_location(); + fprintf(stderr, "%s: %s: modversion " + "changed because of changes " +- "in ", flag_preserve ? "error" : ++ "in ", fail ? "error" : + "warning", name); + } else + fprintf(stderr, ", "); +@@ -598,7 +600,7 @@ void export_symbol(const char *name) + if (sym->status == STATUS_DEFINED) + fprintf(stderr, " (became defined)"); + has_changed = 1; +- if (flag_preserve) ++ if (fail) + errors++; + } + sym->expansion_trail = 0; +@@ -655,6 +657,7 @@ static void genksyms_usage(void) + " -D, --dump Dump expanded symbol defs (for debugging only)\n" + " -r, --reference file Read reference symbols from a file\n" + " -T, --dump-types file Dump expanded types into file\n" ++ " -o, --override Allow to override reference modversions\n" + " -p, --preserve Preserve reference modversions or fail\n" + " -w, --warnings Enable warnings\n" + " -q, --quiet Disable warnings (default)\n" +@@ -666,6 +669,7 @@ static void genksyms_usage(void) + " -D Dump expanded symbol defs (for debugging only)\n" + " -r file Read reference symbols from a file\n" + " -T file Dump expanded types into file\n" ++ " -o Allow to override reference modversions\n" + " -p Preserve reference modversions or fail\n" + " -w Enable warnings\n" + " -q Disable warnings (default)\n" +@@ -690,15 +694,16 @@ int main(int argc, char **argv) + {"reference", 1, 0, 'r'}, + {"dump-types", 1, 0, 'T'}, + {"preserve", 0, 0, 'p'}, ++ {"override", 0, 0, 'o'}, + {"version", 0, 0, 'V'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} + }; + +- while ((o = getopt_long(argc, argv, "a:dwqVDr:T:ph", ++ while ((o = getopt_long(argc, argv, "a:dwqVDr:T:oph", + &long_opts[0], NULL)) != EOF) + #else /* __GNU_LIBRARY__ */ +- while ((o = getopt(argc, argv, "a:dwqVDr:T:ph")) != EOF) ++ while ((o = getopt(argc, argv, "a:dwqVDr:T:oph")) != EOF) + #endif /* __GNU_LIBRARY__ */ + switch (o) { + case 'a': +@@ -735,7 +740,11 @@ int main(int argc, char **argv) + return 1; + } + break; ++ case 'o': ++ flag_override = 1; ++ break; + case 'p': ++ flag_override = 1; + flag_preserve = 1; + break; + case 'h': diff --git a/patches.suse/gfs2-ro-mounts-only.patch b/patches.suse/gfs2-ro-mounts-only.patch new file mode 100644 index 0000000..d6f1a3c --- /dev/null +++ b/patches.suse/gfs2-ro-mounts-only.patch @@ -0,0 +1,39 @@ +From: Mark Fasheh +Date: Tue Dec 1 16:15:11 PST 2009 +Subject: gfs2: allow spectator mounts for migration to ocfs2 +Patch-mainline: never +References: FATE#307584 + +Lock out all writeable gfs2 mounts. This allows only spectator mounts, which +should never modify the disks. We do this to support minimal use of GFS2 for +migration of data. No performance bugs will be fixed. Any writeable mounts +are strictly prohibited. + +Signed-off-by: Mark Fasheh + +--- + fs/gfs2/ops_fstype.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/fs/gfs2/ops_fstype.c ++++ b/fs/gfs2/ops_fstype.c +@@ -1134,6 +1134,19 @@ static int fill_super(struct super_block + } + sdp->sd_args = *args; + ++ if (!sdp->sd_args.ar_spectator || !(sb->s_flags & MS_RDONLY) || ++ sdp->sd_args.ar_ignore_local_fs) { ++ printk(KERN_WARNING "Only read-only GFS2 mounts are " ++ "supported.\nPlease mount with the \"spectator\" and " ++ "\"ro\" mount options\n"); ++ goto fail; ++ } ++ ++ printk(KERN_WARNING ++ "WARNING: GFS2 mounts are ONLY supported for single-node " ++ "migration of data!\nNo performance or write bugs will be " ++ "considered.\n"); ++ + if (sdp->sd_args.ar_spectator) { + sb->s_flags |= MS_RDONLY; + set_bit(SDF_NORECOVERY, &sdp->sd_flags); diff --git a/patches.suse/hung_task_timeout-configurable-default b/patches.suse/hung_task_timeout-configurable-default new file mode 100644 index 0000000..cb6418c --- /dev/null +++ b/patches.suse/hung_task_timeout-configurable-default @@ -0,0 +1,54 @@ +From: Jeff Mahoney +Subject: hung_task_timeout: configurable default +References: bnc#552820 +Patch-mainline: not yet + + This patch allows the default value for sysctl_hung_task_timeout_secs + to be set at build time. The feature carries virtually no overhead, + so it makes sense to keep it enabled. On heavily loaded systems, though, + it can end up triggering stack traces when there is no bug other than + the system being underprovisioned. + + The old default of 120 seconds is preserved. + +Signed-off-by: Jeff Mahoney +--- + kernel/hung_task.c | 3 ++- + lib/Kconfig.debug | 14 ++++++++++++++ + 2 files changed, 16 insertions(+), 1 deletion(-) + +--- a/kernel/hung_task.c ++++ b/kernel/hung_task.c +@@ -33,7 +33,8 @@ unsigned long __read_mostly sysctl_hung_ + /* + * Zero means infinite timeout - no checking done: + */ +-unsigned long __read_mostly sysctl_hung_task_timeout_secs = 120; ++unsigned long __read_mostly sysctl_hung_task_timeout_secs = ++ CONFIG_DEFAULT_HUNG_TASK_TIMEOUT; + + unsigned long __read_mostly sysctl_hung_task_warnings = 10; + +--- a/lib/Kconfig.debug ++++ b/lib/Kconfig.debug +@@ -209,6 +209,20 @@ config DETECT_HUNG_TASK + enabled then all held locks will also be reported. This + feature has negligible overhead. + ++config DEFAULT_HUNG_TASK_TIMEOUT ++ int "Default timeout for hung task detection (in seconds)" ++ depends on DETECT_HUNG_TASK ++ default 120 ++ help ++ This option controls the default timeout (in seconds) used ++ to determine when a task has become non-responsive and should ++ be considered hung. ++ ++ It can be adjusted at runtime via the kernel.hung_task_timeout ++ sysctl or by writing a value to /proc/sys/kernel/hung_task_timeout. ++ ++ A timeout of 0 disables the check. The default is 120 seconds. ++ + config BOOTPARAM_HUNG_TASK_PANIC + bool "Panic (Reboot) On Hung Tasks" + depends on DETECT_HUNG_TASK diff --git a/patches.suse/init-move-populate_rootfs-back-to-start_kernel b/patches.suse/init-move-populate_rootfs-back-to-start_kernel new file mode 100644 index 0000000..ca039cc --- /dev/null +++ b/patches.suse/init-move-populate_rootfs-back-to-start_kernel @@ -0,0 +1,111 @@ +From: Jeff Mahoney +Subject: init: move populate_rootfs back to start_kernel +References: bnc#533555 +Patch-mainline: Probably never + + Mainline commit 8d610dd5 introduced the rootfs_initcall and moved + populate_rootfs out of start_kernel. This was because of issues + with userspace helpers being executed way too early. Things like + pipes weren't initialized yet and users were seeing Oopses or + unpredictable behavior in certain circumstances. + + The fix worked by causing the execve to fail because it couldn't lookup + the helper in the file system since the file system wasn't populate yet. + It turns out that's a really late place to fail since the entire + usermodehelper infrastructure depends on a work queue that is already + checked to see if it has been initialized. We can fail earlier without + having to fork threads that will ultimately fail. + + This patch moves populate_rootfs back to start_kernel and avoids the + race against a very early userspace by moving the initialization of + khelper_wq to rootfs_initcall. + + This may seem like a small win, but it is essential for my next patch + which adds the ability to override ACPI tables at boot-time. + +Signed-off-by: Jeff Mahoney +--- + include/linux/init.h | 1 + include/linux/kmod.h | 2 + init/initramfs.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++-- + init/main.c | 10 +++ + kernel/kmod.c | 4 + + 5 files changed, 161 insertions(+), 9 deletions(-) + +--- a/include/linux/init.h ++++ b/include/linux/init.h +@@ -146,6 +146,7 @@ extern unsigned int reset_devices; + /* used by init/main.c */ + void setup_arch(char **); + void prepare_namespace(void); ++int populate_rootfs(void); + + extern void (*late_time_init)(void); + +--- a/include/linux/kmod.h ++++ b/include/linux/kmod.h +@@ -98,8 +98,6 @@ call_usermodehelper_keys(char *path, cha + return call_usermodehelper_exec(info, wait); + } + +-extern void usermodehelper_init(void); +- + struct file; + extern int call_usermodehelper_pipe(char *path, char *argv[], char *envp[], + struct file **filp); +--- a/init/initramfs.c ++++ b/init/initramfs.c +@@ -565,7 +709,7 @@ static void __init clean_rootfs(void) + } + #endif + +-static int __init populate_rootfs(void) ++int __init populate_rootfs(void) + { + char *err = unpack_to_rootfs(__initramfs_start, + __initramfs_end - __initramfs_start); +@@ -605,4 +749,3 @@ static int __init populate_rootfs(void) + } + return 0; + } +-rootfs_initcall(populate_rootfs); +--- a/init/main.c ++++ b/init/main.c +@@ -713,6 +713,15 @@ asmlinkage void __init start_kernel(void + + check_bugs(); + ++ /* ++ * Do this before starting ACPI so we can read-in ++ * override tables before the tables are actually ++ * loaded. The usermode helper won't be initialized ++ * until much later so we don't race against things ++ * calling out to userspace. ++ */ ++ populate_rootfs(); ++ + acpi_early_init(); /* before LAPIC and SMP init */ + sfi_init_late(); + +@@ -810,7 +819,6 @@ static void __init do_basic_setup(void) + { + init_workqueues(); + cpuset_init_smp(); +- usermodehelper_init(); + init_tmpfs(); + driver_init(); + init_irq_proc(); +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -531,8 +531,10 @@ int call_usermodehelper_pipe(char *path, + } + EXPORT_SYMBOL(call_usermodehelper_pipe); + +-void __init usermodehelper_init(void) ++static int __init usermodehelper_init(void) + { + khelper_wq = create_singlethread_workqueue("khelper"); + BUG_ON(!khelper_wq); ++ return 0; + } ++rootfs_initcall(usermodehelper_init); diff --git a/patches.suse/kbd-ignore-gfx.patch b/patches.suse/kbd-ignore-gfx.patch new file mode 100644 index 0000000..d519b18 --- /dev/null +++ b/patches.suse/kbd-ignore-gfx.patch @@ -0,0 +1,37 @@ +From: Dirk Mueller +Subject: setfont breaks first Xserver start +References: 302010 +Patch-Mainline: No + +The patch prevents setfont from accessing vga registers on the card when +the card is in graphics mode KD_GRAPHICS as we assume, that someone else (ie. +the Xserver) is in charge of the HW in which case accessing the vga registers +may (at best) have no effect (not even the desired one) or (at worst) interfer +with settings the graphics driver has made. + +Signed-off-by: Hannes Reinecke + +--- + drivers/video/console/vgacon.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/video/console/vgacon.c ++++ b/drivers/video/console/vgacon.c +@@ -1261,7 +1261,7 @@ static int vgacon_font_set(struct vc_dat + unsigned charcount = font->charcount; + int rc; + +- if (vga_video_type < VIDEO_TYPE_EGAM) ++ if (vga_video_type < VIDEO_TYPE_EGAM || vga_is_gfx) + return -EINVAL; + + if (font->width != VGA_FONTWIDTH || +@@ -1279,7 +1279,7 @@ static int vgacon_font_set(struct vc_dat + + static int vgacon_font_get(struct vc_data *c, struct console_font *font) + { +- if (vga_video_type < VIDEO_TYPE_EGAM) ++ if (vga_video_type < VIDEO_TYPE_EGAM || vga_is_gfx) + return -EINVAL; + + font->width = VGA_FONTWIDTH; diff --git a/patches.suse/kconfig-automate-kernel-desktop b/patches.suse/kconfig-automate-kernel-desktop new file mode 100644 index 0000000..895b795 --- /dev/null +++ b/patches.suse/kconfig-automate-kernel-desktop @@ -0,0 +1,56 @@ +From: Suresh Jayaraman +Subject: [PATCH] automate config options for kernel-desktop +References: FATE#305694 +Patch-mainline: Never + +Automate the desktop only kernel configuration options with the new +CONFIG_KERNEL_DESKTOP. + +Signed-off-by: Suresh Jayaraman +--- + init/Kconfig | 5 ++++- + kernel/Kconfig.hz | 1 + + kernel/Kconfig.preempt | 1 + + 3 files changed, 6 insertions(+), 1 deletion(-) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -498,6 +498,8 @@ config HAVE_UNSTABLE_SCHED_CLOCK + menuconfig CGROUPS + boolean "Control Group support" + depends on EVENTFD ++ default n if KERNEL_DESKTOP ++ default y + help + This option adds support for grouping sets of processes together, for + use with process control subsystems such as Cpusets, CFS, memory +@@ -619,7 +621,8 @@ config CGROUP_MEM_RES_CTLR_SWAP + menuconfig CGROUP_SCHED + bool "Group CPU scheduler" + depends on EXPERIMENTAL && CGROUPS +- default n ++ default n if KERNEL_DESKTOP ++ default y + help + This feature lets CPU scheduler recognize task groups and control CPU + bandwidth allocation to such task groups. It uses cgroups to group +--- a/kernel/Kconfig.hz ++++ b/kernel/Kconfig.hz +@@ -4,6 +4,7 @@ + + choice + prompt "Timer frequency" ++ default HZ_1000 if KERNEL_DESKTOP + default HZ_250 + help + Allows the configuration of the timer frequency. It is customary +--- a/kernel/Kconfig.preempt ++++ b/kernel/Kconfig.preempt +@@ -1,6 +1,7 @@ + + choice + prompt "Preemption Model" ++ default PREEMPT if KERNEL_DESKTOP + default PREEMPT_NONE + + config PREEMPT_NONE diff --git a/patches.suse/kdb-build-fixes b/patches.suse/kdb-build-fixes new file mode 100644 index 0000000..5874f92 --- /dev/null +++ b/patches.suse/kdb-build-fixes @@ -0,0 +1,37 @@ +From: Jeff Mahoney +Subject: kdb: Build fixes +Patch-mainline: not yet, whenever KDB is upstream + + The includes as provided don't work when building out-of-tree, as we do + while packaging. Also, struct file->f_count is an atomic_long_t. + +Signed-off-by: Jeff Mahoney +--- + kdb/modules/kdbm_vm.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/kdb/modules/kdbm_vm.c ++++ b/kdb/modules/kdbm_vm.c +@@ -16,7 +16,9 @@ + #include + #include + +-#include ++#include ++#include ++#include + #include + #include + +@@ -749,9 +751,8 @@ kdbm_filp(int argc, const char **argv) + kdb_printf(" f_dentry = 0x%p f_vfsmnt = 0x%p f_op = 0x%p\n", + f.f_dentry, f.f_vfsmnt, f.f_op); + +- kdb_printf(" f_count = " kdb_f_count_fmt +- " f_flags = 0x%x f_mode = 0x%x\n", +- atomic_long_read(&f.f_count), f.f_flags, f.f_mode); ++ kdb_printf(" f_count = %ld f_flags = 0x%x f_mode = 0x%x\n", ++ f.f_count, f.f_flags, f.f_mode); + + kdb_printf(" f_pos = %Ld\n", f.f_pos); + #ifdef CONFIG_SECURITY diff --git a/patches.suse/kdb-common b/patches.suse/kdb-common new file mode 100644 index 0000000..8e59168 --- /dev/null +++ b/patches.suse/kdb-common @@ -0,0 +1,32572 @@ +From: Martin Hicks +Date: Mon, 07 Dec 2009 11:52:50 -0600 +Subject: kdb-v4.4-2.6.32-common-3 +References: FATE#303971 +X-URL: ftp://oss.sgi.com/www/projects/kdb/download/v4.4/ +Patch-mainline: Probably never + +The KDB common code. + +Differences: +- We remove the binary sysctl. It changes with every release, which makes + it useless. +- Use debugger_syslog_data as provided by + patches/patches.arch/ppc64-xmon-dmesg-printing.patch. It's the same function. + +Acked-by: Jeff Mahoney +--- + + Documentation/kdb/bt_x86 | 1837 ++++++++ + Documentation/kdb/kdb.mm | 492 ++ + Documentation/kdb/kdb_bp.man | 197 + Documentation/kdb/kdb_bt.man | 315 + + Documentation/kdb/kdb_env.man | 46 + Documentation/kdb/kdb_ll.man | 134 + Documentation/kdb/kdb_md.man | 136 + Documentation/kdb/kdb_ps.man | 96 + Documentation/kdb/kdb_rd.man | 170 + Documentation/kdb/kdb_sr.man | 68 + Documentation/kdb/kdb_ss.man | 109 + Documentation/kdb/slides | 1382 ++++++ + Makefile | 1 + drivers/char/keyboard.c | 10 + drivers/hid/usbhid/hid-core.c | 40 + drivers/hid/usbhid/usbkbd.c | 17 + drivers/serial/8250.c | 57 + drivers/serial/8250_early.c | 29 + drivers/serial/sn_console.c | 73 + drivers/usb/core/hcd.c | 71 + drivers/usb/core/hcd.h | 11 + drivers/usb/host/ehci-hcd.c | 42 + drivers/usb/host/ehci-pci.c | 8 + drivers/usb/host/ehci-q.c | 222 + + drivers/usb/host/ohci-hcd.c | 67 + drivers/usb/host/ohci-pci.c | 8 + drivers/usb/host/ohci-q.c | 62 + drivers/usb/host/uhci-hcd.c | 218 + + drivers/usb/host/uhci-q.c | 164 + fs/proc/meminfo.c | 145 + fs/proc/mmu.c | 16 + include/asm-generic/kmap_types.h | 3 + include/linux/console.h | 5 + include/linux/dis-asm.h | 347 + + include/linux/kdb.h | 184 + include/linux/kdbprivate.h | 518 ++ + include/linux/reboot.h | 7 + init/main.c | 32 + kdb/ChangeLog | 2040 +++++++++ + kdb/Makefile | 43 + kdb/kdb_bp.c | 661 +++ + kdb/kdb_bt.c | 180 + kdb/kdb_cmds | 33 + kdb/kdb_id.c | 236 + + kdb/kdb_io.c | 859 ++++ + kdb/kdbdereference.c | 7258 ++++++++++++++++++++++++++++++++++ + kdb/kdbmain.c | 4333 ++++++++++++++++++++ + kdb/kdbsupport.c | 1155 +++++ + kdb/modules/Makefile | 14 + kdb/modules/kdbm_debugtypes.c | 388 + + kdb/modules/kdbm_pg.c | 683 +++ + kdb/modules/kdbm_sched.c | 57 + kdb/modules/kdbm_task.c | 195 + kdb/modules/kdbm_vm.c | 1040 ++++ + kdb/modules/kdbm_x86.c | 1093 +++++ + kdb/modules/lcrash/README | 3 + kdb/modules/lcrash/asm/README | 1 + kdb/modules/lcrash/asm/kl_dump_ia64.h | 199 + kdb/modules/lcrash/asm/kl_types.h | 48 + kdb/modules/lcrash/kl_alloc.h | 124 + kdb/modules/lcrash/kl_bfd.h | 31 + kdb/modules/lcrash/kl_btnode.h | 95 + kdb/modules/lcrash/kl_cmp.h | 102 + kdb/modules/lcrash/kl_copt.h | 29 + kdb/modules/lcrash/kl_debug.h | 168 + kdb/modules/lcrash/kl_dump.h | 511 ++ + kdb/modules/lcrash/kl_dump_arch.h | 124 + kdb/modules/lcrash/kl_dump_ia64.h | 199 + kdb/modules/lcrash/kl_dwarfs.h | 27 + kdb/modules/lcrash/kl_error.h | 266 + + kdb/modules/lcrash/kl_htnode.h | 71 + kdb/modules/lcrash/kl_lib.h | 65 + kdb/modules/lcrash/kl_libutil.h | 40 + kdb/modules/lcrash/kl_mem.h | 104 + kdb/modules/lcrash/kl_mem_ia64.h | 149 + kdb/modules/lcrash/kl_module.h | 69 + kdb/modules/lcrash/kl_queue.h | 89 + kdb/modules/lcrash/kl_stabs.h | 122 + kdb/modules/lcrash/kl_stringtab.h | 68 + kdb/modules/lcrash/kl_sym.h | 131 + kdb/modules/lcrash/kl_task.h | 39 + kdb/modules/lcrash/kl_typeinfo.h | 199 + kdb/modules/lcrash/kl_types.h | 54 + kdb/modules/lcrash/klib.h | 480 ++ + kdb/modules/lcrash/lc_eval.h | 225 + + kernel/exit.c | 3 + kernel/kallsyms.c | 23 + kernel/kexec.c | 15 + kernel/module.c | 19 + kernel/panic.c | 9 + kernel/sched.c | 109 + kernel/signal.c | 49 + lib/bug.c | 8 + mm/hugetlb.c | 22 + mm/mmzone.c | 4 + mm/swapfile.c | 22 + 96 files changed, 31713 insertions(+), 9 deletions(-) + +--- /dev/null ++++ b/Documentation/kdb/bt_x86 +@@ -0,0 +1,1837 @@ ++Copyright Keith Owens, 2007. ++ ++How the KDB backtrace for x86 works, how to diagnose problems and submit a bug ++============================================================================== ++ ++Unlike ia64, x86 architectures do not mandate unwind information in the kernel. ++gcc will include some unwind information for C functions, but not for assembler ++code. Attempts have been made to add unwind information to the assembler code ++by hand, with little success. Eventually Linus rejected the x86 unwind code ++because it was breaking too often and destroying useful debugging data. ++ ++Even if the x86 unwinder worked correctly, it would only give an accurate ++backtrace, it would not give function arguments. Needless to say, function ++arguments are what people really want. To get function arguments requires much ++more support from the compiler than simple unwind data, the compiler has to ++track line by line where each argument is held and make that data available to ++the debugger. Compiling with gcc -g will provide that information, but the ++resulting kernel is several times larger than normal. ++ ++Although the gcc -g data can be stored on another machine, there are constructs ++in the kernel that cannot be tracked by this method. i386 builds with 4K stacks ++and all x86_64 builds have multiple kernel stacks. The compiler knows nothing ++about these extra stacks and cannot backtrace through them correctly. The ++assembler code in arch/{i386,x86_64}/kernel/entry.S is a maze of twisty logic ++paths, some of which eventually jump to common labels. Describing this twisty ++logic to an unwinder is very difficult, expecially when you try to describe ++where arguments and/or registers are held). ++ ++KDB gets an accurate x86 backtrace and extracts the arguments by performing code ++decomposition and analysis at run time. This avoids the need to bloat the ++running kernel to several times its normal size with gcc -g data. KDB (unlike ++gdb) also knows about the additional kernel stacks and twisty assembler code ++paths. ++ ++The x86 backtrace code for i386 is very similar to the x86_64 code, with 80% ++common code and data. Most of the differences between the backtrace for the two ++architectures is due to the assembler code and stack handling. To avoid code ++duplication between KDB patches, the x86 backtrace code is actually stored in ++the kdb common patch, in source kdb/kdba_bt_x86.c. kdb/Makefile only builds ++kdba_bt_x86.o for i386 or x86_64. Most of the code reads as if the architecture ++is x86_64, using register names like rsp and rip. i386 is treated as a subset ++of x86_64, with fewer registers and printing the names as esp and eip. When ++this documentation refers to rsp and rip, read it as esp and eip for i386. The ++20% of code and data that is different in held in two large #ifdef sections, ++scan kdba_bt_x86.c for CONFIG_X86_64. Be careful when changing anything in the ++architecture specific sections, you will need to review the other architecture ++to see if it needs changes as well. ++ ++The idea behind the x86 backtrace is to trace one function at a time, which ++gives us the calling function. Then apply the same algorithm to the calling ++function until you unwind to the first function in the process. The starting ++point for tracing any process is to extract the current stack pointer and ++current instruction pointer (rsp and rip). The way that these values are ++extracted varies between running tasks and blocked tasks, the method is ++described later (Process Starting Point) but ignore it for now, just assume that ++we have a starting rsp and rip. ++ ++Given the instruction pointer (rip), we identify the start and end of the kernel ++or module function it is in, using the kernel symbol table. This is easy for C ++code, it is significantly harder for assembler code because of the twisty code ++paths that branch to common labels. The method of identifying the current ++function is described later (Identifying The Current Function) but ignore it for ++now, just assumes that we have the start and end address of the function plus ++its name. ++ ++After the rip has been mapped to a function name with sensible start and end ++addresses, the next step is to analyse the code paths in that function. KDB ++already has a built in disassembler (copied with slight modifications from ++binutils) which knows how to decode each x86 instruction. Instead of ++duplicating that logic in kdba_bt_x86, it takes advantage of the fact that you ++can override the disassembler's print function, sending the output line to a ++buffer instead of printing it. kdba_bt_x86 stills has to decode the buffer but ++that is a lot easier than decoding the x86 instruction set. ++ ++The code analysis consists of two main passes. There are example below of the ++analysis with basic block (bb) debugging activated (Examples of Basic Block ++Debugging Output). ++ ++The first pass (bb_pass1) identifies all the basic blocks in the function. For ++our purposes, a basic block has a single entry point and one or more exit ++points. The start of the function is the start of basic block 0, all other ++blocks are the target of jump instructions (conditional or unconditional) from ++within the rest of the code. A block ends with an unconditional jump or with a ++terminating instruction such as ret, iret, sysexit, sysret or ud2a (BUG). A ++block can also end because the next instruction is the start of a new block ++(target of jmp or jcc), in this case there is an implied drop through from one ++block to the next. ++ ++Although a call instruction also transfers control, it returns to the next ++instruction so call is not treated as a transfer. Instead call is treated as a ++normal instruction with side effects, the scratch registers are cleared after a ++call. ++ ++At the end of the first pass over the function we have a directed graph that ++starts at bb[0]. The nodes of the graph (bb_list[]) are the basic blocks with ++their start and end address. The vertices are the jmp or jcc instructions ++(bb_jmp_list[]) that transfer control between blocks, plus any implied drop ++through transfers between consecutive blocks. This graph can have cycles, many ++functions have loops in them which transfer control back to earlier in the code ++body. ++ ++The second pass (bb_pass2) runs over the directed graph analysing the effect of ++each instruction on the register and memory state. It is important to ++understand that the analysis in this pass is an abstract one, it does not use ++actual hex values for the register contents, instead it uses symbolic values. ++When the basic block code says that "register rsi contains value rax" it means ++that whatever value was in rax on entry to the function has also been copied to ++register rsi at this point in the logic flow. ++ ++At an abstract level, all C functions start with exactly the same state, each ++register contains its own symbolic value (except for the stack pointer, see ++later) with no local stack variables defined yet. Assembler functions tend to ++have unusual starting points, with some registers and/or memory contents defined ++differently on entry. For example, ret_from_intr on i386 already has a struct ++pt_regs on its stack, ret_from_intr on x86_64 already has a partial struct ++pt_regs plus another two words stacked on top of it. The special starting cases ++are listed in the arch specific bb_special_cases[]. ++ ++Once the input state of bb[0] has been defined (including any special cases), ++bb_pass2_do_changed_blocks() runs over all the nodes in bb_list[]. Each ++instruction in each block is analysed (Tracking the Effects of Instructions) to ++see what effect it has on the abstract register state, the analysis of each ++instruction is done in bb_usage(). An instruction can copy one register to ++another, it can copy a register to stack, move from stack to a register or it ++can invalidate the contents of a register or memory location. A general rule in ++bb_usage() is that any operation whose results cannot be calculated in terms of ++an original input value gives an undefined result. Remember that it is the ++abstract value that becomes undefined, moving a constant to a register gives a ++defined value for the view of the program but it is undefined as far as the ++abstract state is concerned. ++ ++References to data on the stack are a little awkward because the stack pointer ++frequently changes. To overcome this, kdba_bt_x86 defines a pseudo register ++called the 'original stack pointer' (osp). This always represents the stack ++pointer on entry to the function, so on entry rsp contains osp+0x0. As rsp is ++modified, it still points at osp, but its offset from osp changes. Copying rsp ++to another register (e.g. mov %rsp,%rbp) copies the osp offset as well. At the ++point that this function calls the next function down the stack, kdba_bt_x86 ++knows the delta from osp to rsp. Applying that delta to the actual value of the ++stack pointer gives the stack pointer value on input to the current function, ++that location contains the return address so we can go up one stack frame and ++repeat the process. ++ ++After doing basic block analysis on the current function, kdba_bt_x86 knows what ++the abstract register and memory state is at the point this function was ++interrupted or it called the next function down the stack, this is the exit ++state. For an interrupt the actual register values are saved in a struct ++pt_regs, for a call we have unwound from the KDB interrupt back to the called ++function so we have some idea of what the register values are in the called ++function. The abstract exit state is merged with the known actual register ++values to derive the original stack pointer. That in turn gives us any ++registers that were saved on stack. The original stack pointer gives the return ++address from the calling function, go up one stack frame and repeat the ++analysis. ++ ++ ++Process Starting Point ++====================== ++ ++All backtrace code needs a starting point which defines at least the stack ++pointer and instruction pointer, it may define other registers as well. The ++first part of kdba_bt_stack() extracts the starting point. Processes can be in ++one of three states, running (currently on a cpu), blocked (sleeping or ready to ++run but not currently on a cpu) or unknown. ++ ++For running processes, the current rsp and rip are dynamic. Because KDB stops ++the entire machine by sending an interrupt to the other cpus, KDB can save the ++rsp and rip for each cpu at the point where KDB is entered. This data is held ++in array kdb_running_process and is stored by kdb_save_running() and the arch ++specific kdba_save_running() functions. When backtracing a running process, KDB ++uses the data in kdb_running_process as its starting point. ++ ++For blocked processes we always have the saved rsp, it is held in the process's ++thread_info. For i386 blocked processes, thread_info also contains the saved ++rip. For x86_64 blocked processes, rip is no longer saved in thread_info, it is ++assumed that all blocked processes will resume at assembler label thread_return, ++so that rip is used on x86_64. See arch specific kdba_bt_stack_rip(). ++ ++Unknown process state only occurs when the user does 'bt '. ++Unlike other bt commands, 'bt ' does not identify any specific ++process, instead it identifies a kernel stack. must be inside a ++valid kernel stack and must point to a saved rip from a call instruction. ++kdba_bt_x86.c uses the common kdba_get_stack_info() and arch specific ++kdba_get_stack_info_alternate() functions to check that the address falls within ++a valid kernel stack. If the user gives a stack address that does not point to ++a saved rip from a call instruction then the backtrace will be garbage. ++ ++ ++Identifying The Current Function ++================================ ++ ++Given a rip value, KDB uses the kallsyms data to find the start of the function ++(first address <= rip) and the end of the function (next symbol in kallsyms). ++This works for plain C code because gcc only generates one label per function. ++It does not work for assembler code or for assembler code embedded in C ++functions, because the assembler labels appear as global entries in kallsyms. ++For example, arch/i386/kernel/entry.S has function ret_from_exception which ++contains three global labels ret_from_intr, check_userspace and ++resume_userspace. If rip points to any of those global labels, KDB wants the ++start of the real function, i.e. ret_from_exception. In addition, if rip points ++to ret_from_exception, KDB wants the end of the function to be after the last ++global label in that function, i.e. after resume_userspace. ++ ++The simplest way to handle these unwanted global labels is to list the spurious ++assembler labels, which is done in the arch specific array bb_spurious. After ++mapping rip to the nearest start and end labels from kallsyms, kdb_bb() works ++backwards until it finds a non-spurious label then works forwards to the next ++non-spurious label. That gives a real start and end address and a real name for ++the current function. ++ ++Note that this algorithm only applies in kdb_bb() when it maps rip to a suitable ++start and end address. When disassembling the code, you will still see the ++spurious label names, users need to see the extra labels. ret_from_exception on ++i386 disassembles like this (2.6.22) :- ++ ++[0]kdb> id ret_from_exception ++0xc0102554 ret_from_exception: cli ++0xc0102555 ret_from_intr: mov $0xfffff000,%ebp ++0xc010255a ret_from_intr+0x5: and %esp,%ebp ++0xc010255c check_userspace: mov 0x34(%esp),%eax ++0xc0102560 check_userspace+0x4: mov 0x30(%esp),%al ++0xc0102564 check_userspace+0x8: and $0x20003,%eax ++0xc0102569 check_userspace+0xd: cmp $0x3,%eax ++0xc010256c check_userspace+0x10: jb 0xc010258c resume_kernel ++0xc0102572 check_userspace+0x16: mov %esi,%esi ++0xc0102574 resume_userspace: cli ++0xc0102575 resume_userspace+0x1: mov 0x8(%ebp),%ecx ++0xc0102578 resume_userspace+0x4: and $0xfe3e,%ecx ++0xc010257e resume_userspace+0xa: jne 0xc01026f4 work_pending ++0xc0102584 resume_userspace+0x10: jmp 0xc01026a7 restore_all ++0xc0102589 resume_userspace+0x15: lea 0x0(%esi),%esi ++0xc010258c resume_kernel: cli ++ ++For the purposes of kdba_bt_x86.c, any rip from 0xc0102554 to 0xc0102589 needs ++to map to the range 0xc0102554 (start) to 0xc010258c (end) with function name ++ret_from_exception. Therefore ret_from_intr, check_userspace and ++resume_userspace are listed in bb_spurious[] for i386 so those symbols are ++ignored. The comments in bb_spurious[] list the function that encloses each ++spurious label, those comments are only for humans, they do not affect the code. ++ ++Once rip has been mapped to non-spurious labels, the module name, function name, ++start and end address are stored in variables bb_mod_name, bb_func_name, ++bb_func_start, bb_func_end. These variables are used throughout kdba_bt_x86.c ++for processing each function in turn. ++ ++Watch for changes to assembler code, especially in arch/i386/kernel/entry.S, ++arch/x86_64/kernel/entry.S and arch/x86_64/ia32/ia32entry.S. When new labels ++are added you may need to adjust bb_spurious[] for that architecture. Running ++bb_all can help identify assembler labels that have been added or deleted. ++ ++ ++Tracking the Effects of Instructions ++==================================== ++ ++bb_pass2_do_changed_blocks() uses the KDB disassembler to decode the x86 ++instructions to something a human can read. bb_dis_pass2() is used as a print ++routine to store data for a single instruction in a buffer then ++bb_parse_buffer() starts the analysis. Any instruction prefixes like lock or ++rep are stripped out. The opcode string is isolated then up to 3 operands are ++extracted (imul can have 3 operands), these are src, dst and dst2. The operand ++is matched against table bb_opcode_usage_all[] which lists all the instructions ++that actually appear in i386 and x86_64 kernels. A lot of the x86 instrcution ++set is not used by the kernel so instructions such as SSE do not appear in ++bb_opcode_usage_all[]. ++ ++Each operand is decoded by bb_parse_operand() to see whether it has a segment ++prefix, displacement, base, index or scale. An indirect call or jmp is ++identified. Operands consisting only of a register are classified as 'reg' ++type, displacements starting with '$' are immediate values otherwise the operand ++refers to a memory location. Any base or index register name is mapped to the ++abstract register name that contains it, this takes care of mapping (say) %ah to ++rax. ++ ++After decoding the opcode and all its operands, bb_usage() decides what effect ++the instruction will have on the abstract state machine. Some x86 instructions ++list all the affected registers in their operands and these can be handled as ++generic cases. Alas many x86 instructions have side effects and change ++registers that are not listed in the operands, these have to be handled as ++special cases. enum bb_operand_usage lists the generic and special cases. ++ ++bb_usage() is basically one huge switch statement over the special values in ++enum bb_operand_usage. For each special case it tracks the side effects of the ++instruction. Once all the special cases have been handled and converted to ++generic cases then bb_usage() handles the generic cases. ++ ++bb_usage() detects when a register is copied to another register, a register is ++copied to stack or a known stack value is copied to a register and updates the ++state data accordingly. It is particularly important that all stack pointer ++updates and copies of the stack pointer are tracked, much of the saved state is ++on stack and can be accessed via any register that points to the stack, not just ++via rsp. ++ ++i386 built with 4K stacks and all x86_64 builds have multiple kernel stacks. ++bb_usage() knows which instructions or locations are used to switch stacks and ++pretends that these instructions have no effect on the contents of rsp. The ++higher level backtrace code knows how to handle stack switching, it is too ++complicated for basic block analysis. ++ ++ ++Transfer of Control Outside the Current Function ++================================================ ++ ++Ignoring call instructions, most C code does not transfer control outside the ++current function, IOW there are no jump instructions to instructions outside the ++function. There are a few cases that this can occur for C code, inline ++assembler and tail recursion. ++ ++Tail recursion occurs when a function ends by returning the value from a second ++function and that second function has exactly the same arguments and return ++value as the current function. For example, ++ ++ int bar(int i, char *p) ++ { ++ ... do some work and return an int ... ++ } ++ ++ int foo(int i, char *p) ++ { ++ return bar(i, p); ++ } ++ ++If tail recursion is active (gcc -foptimize-sibling-calls) then instead of foo ++calling bar, bar returning to foo then foo returning to its caller, gcc will end ++foo with a direct jmp to bar. The source code says that something called foo ++but the stack trace will show bar is active, with no sign of foo on stack. When ++bar returns it will use the return address from the code that called foo. ++ ++bb_transfer() detects an unconditional jmp to code outside the function body and ++assumes that this represents tail recursion. For tail recursion to work ++correctly, all the preserved registers must contain their original values, ++bb_sanity_check() validates this. Any deviation from the expected state will ++stop basic block analysis and fall back on the old unreliable backtrace code. ++ ++Besides tail recursion in C code, assembler code can jump to labels outside the ++current function. Unfortunately this occurs all the time in the twisty ++assembler code and, to make things worse, many of these transfers are done with ++non-standard register or memory state. bb_special_case() and the arch specific ++bb_special_cases[] handle all the known special cases, including what the ++register and/or memory state should be. Any deviation from the expected state ++will stop basic block analysis and fall back on the old unreliable backtrace ++code. ++ ++ ++Locating Arguments ++================== ++ ++Function arguments can be passed in registers or on stack. The full ABI for ++passing arguments is described in ++ ++ http://www.caldera.com/developers/devspecs/abi386-4.pdf ++ http://www.x86-64.org/documentation/abi.pdf ++ ++The short description, ignoring special cases like passing structures by name ++and floating point arguments which tend not to apply to the kernel, is :- ++ ++i386. With -mpregparm=0, all arguments are passed on stack, except for ++ functions defined as FASTCALL, where the first 3 arguments are passed in ++ registers. ++ ++ With -mregparm=3, the first 3 arguments are passed in registers except ++ for functions defined as asmlinkage or with variable number of ++ arguments, when arguments are still passed on stack. -mpregparm=3 used ++ to be a config option, in recent kernels it is the default. ++ ++ Arguments defined as long long (64 bit) are passed in two registers or ++ in two locations on stack. Being passed in two pieces makes a 64 bit ++ argument look like two 32 bit arguments to KDB, it will be printed as ++ two separate arguments. ++ ++ When compiled with -mregparm=3, if a 64 bit argument is argument number ++ 2 then it will not be split between register and stack, instead it will ++ all be on stack and the third argument register will not be used. This ++ makes it look like there is an extra argument in the list. There is ++ nothing that KDB can do to detect these corner cases with 64 bit ++ arguments on i386, which is a pity because they can confuse users. ++ ++ The preserved registers are ebx, ebp, esp, esi, edi. Arguments are ++ passed in eax, edx, ecx. The return value is in eax. ++ ++x86_64. The first 6 arguments are passed in registers, the 7th and later ++ arguments are passed on stack. Except for functions with a variable ++ number of arguments (e.g. printk) where all arguments are on stack ++ except for rax which contains the number of SSE arguments (always 0 for ++ the kernel). ++ ++ The preserved registers are rbx, rbp, rsp, r12, r13, r14, r15. ++ Arguments are passed in rdi, rsi, rdx, rcx, r8, r9. The return value is ++ in rax. ++ ++For both architectures, kdba_bt detects an argument that is passed in a register ++by the fact that the function code reads from that argument type register while ++it contains its original value. IOW, testing the value of rax, copying rax to ++another register or storing it on stack without first overwriting rax means that ++rax contains a useful input value. Reading from memory which is above the ++original stack pointer means that there is a argument at that location on ++stack. ++ ++There are some functions in the kernel whose definition contains arguments that ++are not actually used. Typically these functions are instantiations of generic ++function definitions where some, but not all, instantiations use all the ++arguments. For example, a filesystem function may take flags that are not used ++by this particular filesystem, but the function definition has to match the ++generic filesystem declarations. If the unused arguments are at the end of the ++list then there is no way of telling from the object code that they exist, the ++function that does not use the trailing aguments will have no code that refers ++to them. KDB will print a truncated argument list for this case. ++ ++If the unused arguments are not at the end of the list then KDB can detect the ++presence of the unused arguments, because there is code that refers to later ++arguments. KDB will print the unused argument, although gcc may have ++overwritten the register it is in, in which case KDB prints "invalid". ++ ++Originally kdba_bt_x86 would detect that there was no reference to arguments in ++registers but there were still references to arguments on stack and assume that ++the function had all its arguments on stack. Unfortunately this did not work ++with the large number of 'pass through' functions in the kernel. A 'pass ++through' function is one which calls another function with roughly the same ++argument list and makes no other reference to the register arguments. For ++example, ipv4_doint_and_flush_strategy() takes 7 arguments, calls ++devinet_conf_sysctl() with those 7 arguments in the same order and has no other ++reference to any of its arguments. ++ ++Pass through functions do not touch the arguments that are passed in registers ++because they are already in the right location for the routine about to be ++called, so the pass through function has no code that references the argument ++registers. No code means that kdba_bt_x86 cannot tell if the function has ++register arguments or not. The arguments passed on stack must always be copied ++to the new stack frame, even for pass through functions, so the arguments on ++stack can always be detected. ++ ++kdba_bt_x86 was changed to assume that if there are any arguments on stack then ++there are always arguments in registers, except for a list of functions that are ++known to be asmlinkage or to have a variable number of arguments. ++bb_assume_pass_through() ignores the known special cases, for other functions ++which have stack arguments but no register arguments it assumes the function is ++pass through and prints a warning about that assumption. ++ ++The above heuristics mean that there is one case that kdba_bt_x86 cannot detect: ++pass through functions where all the arguments are in registers. These have no ++argument references at all in their code, so they are printed with no arguments. ++All they do is call another function so this class of functions never fails, or ++if it does fail then it is due to something that is not argument related. If ++the failure is further down the call stack then the arguments are printed at the ++next function down the stack, so the user still has the arguments. ++ ++This list of limitations on getting the x86 arguments may seem to be a long one, ++but kdba_bt_x86 gives sensible results for most functions. For kernel ++debugging, any arguments are far better than none at all. ++ ++ ++Kernel Stack Switching ++====================== ++ ++Understanding the unusual way that x86 kernel stacks are used is very important ++when diagnosing backtrace problems. Every process has its own normal kernel ++stack, even processes that run entirely within the kernel such as kthread or the ++per cpu migration processes. The normal stacks are 4K or 8K on i386 (depending ++on CONFIG_4KSTACKS) and 8K on x86_64. The normal stacks are global, they are ++not tied to any cpu. ++ ++For i386 with 8K stacks there are no other kernel stacks so there is no stack ++switching to worry about. ++ ++For i386 with 4K process stacks, each cpu also has a 4K soft irq stack and a 4K ++hard irq stack. It is possible for a process to be running on its own process ++stack, for the process to be interrupted by a soft irq which is then interrupted ++by a hard irq. At that point the backtrace is split between the hard irq, the ++soft irq and the normal normal stacks. ++ ++On x86_64, each cpu always has stacks for stackfault, doublefault, nmi, debug, ++mce and interrupts. See Documentation/x86_64/kernel-stacks. ++ ++The arch specific kdba_get_stack_info_alternate() function works out which stack ++the backtrace starts on, how big the stack is and how to switch to the next ++stack. This information is stored in the kdb_activation_record and used by the ++higher level backtrace code to detect a stack switch. ++ ++The normal stack has some padding at the end, this reflects the stack pointer ++when the process was created in the kernel. kdba_bt_x86 cannot backtrace ++through this padding data, mainly because the code that set the nitial stack ++pointer no longer exists after boot. ARCH_NORMAL_PADDING defines how many words ++to ignore at the end of the normal stack. ++ ++ ++Debugging KDB ++============= ++ ++KDB has conditional debugging print statements scattered throughout the code. ++If KDB is not behaving as expected, you can turn on debugging and rerun the ++command. Before debugging KDB, set LINES 10000 and capture the output via a ++serial console. If using minicom, turn on word wrap (control-A W) and capture ++mode (control-A L). If you are using a serial console via a serial to Ethernet ++interface using ssh or telnet, use the 'script' command to start the session. ++ ++The various KDB_DEBUG_FLAG_* flags are listed in include/linux/kdbprivate.h. ++You set debug with 'set KDBDEBUG 0xnn' where nn is the or'd value of the desired ++flags. 'set KDBDEBUG 0' turns off KDB debugging. When diagnosing x86 backtrace ++problems, the most useful debug flags are ++ ++ KDB_DEBUG_FLAG_ARA 0x10 Activation record, arch specific ++ KDB_DEBUG_FLAG_BB_SUMM 0x04 Basic block analysis, summary only ++ KDB_DEBUG_FLAG_BB 0x20 All basic block analysis ++ ++ARA prints information about the different kernel stacks as kdba_bt_x86 unwinds ++through the switched kernel stacks. BB_SUMM prints a summary of the basic block ++analysis for each function, including the abstract exit state and the rollback ++calculations. BB prints a huge amount of basic block debugging, you probably ++only want to turn this for the full backtrace on as a last resort. ++ ++I find 'set KDBDEBUG 0x14' to be best to get an overview of a problem. It gives ++both the kernel stack information plus the abstract state and actual location of ++data for each function. ++ ++Command 'bb1' does a detailed debug session for a single function, bb1 takes a ++single parameter, the address of the exit point from the function, by number, ++not by name. bb1 turns on KDB_DEBUG_FLAG_BB, does basic block analysis for the ++function that contains the exit point then resets the debug flags to their ++previous value. ++ ++Command 'bb_all' runs through every function in the base kernel (not module ++functions) and does a basic block analysis of every function. It also validates ++the various tables in kdba_bt_x86 where possible. bb_all is meant for the KDB ++maintainer to check that all the base kernel function pass the sanity checks, it ++can also be used by end users when reporting a bug. bb_all takes no parameters. ++It prints a '.' for every 100 functions it has analysed and allows for up to 20 ++errors before giving up. The output from bb_all also includes the config ++variables that affect basic block analysis plus any assumptions about 'pass ++through' functions. ++ ++ ++Submitting a Bug Report Against kdba_bt_x86 ++=========================================== ++ ++Capture the KDB output via a serial console. ++ ++set LINES 10000 ++set BTSP 1 ++set KDBDEBUG 0x14 ++Reproduce the problem. ++set KDBDEBUG 0 ++bb_all ++ ++If you can identify the rip/eip where kdba_bt_x86 gets confused, run bb1 with ++that address. ++ ++Find each set of output from kdba_get_stack_info in the trace, extract the last ++two lines and type those lines into KDB. That will give a hex and symbolic dump ++of the raw kernel stacks. For example, if the trace data is ++ ++kdba_get_stack_info: esp=0xc04fbef8 cpu=0 task=c047b3e0 ++kdba_get_stack_info: ar->stack ++ physical_start=0xc04fb000 ++ physical_end=0xc04fc000 ++ logical_start=0xc04fb038 ++ logical_end=0xc04fc000 ++ next=0xc04b4f44 ++ id=hardirq_ctx ++ set MDCOUNT 1024 ++ mds 0xc04fb000 ++ ++then type the last two lines into KDB. Repeat this for each stack listed by ++kdba_get_stack_info on the failing backtrace. ++ ++Send all the console output to the KDB maintainer. ++ ++ ++Examples of Basic Block Debugging Output ++======================================== ++ ++Example of the basic block analysis of fs/namei::getname() on i386. Kernel ++2.6.22, i386, compiled with frame pointers, gcc 4.1.0. ++ ++Basic block debugging is very verbose, so set a high number of output lines. ++You really need a reliable serial console to capture this amount of output. ++ ++ [0]kdb> set LINES 10000 ++ ++A simple disassemble of getname(). This is not required for debugging purposes ++since each instruction is printed as part of basic block debugging, but this can ++be easier to read. ++ ++ [0]kdb> id getname ++ 0xc015cce8 getname: push %ebp ++ 0xc015cce9 getname+0x1: mov %esp,%ebp ++ 0xc015cceb getname+0x3: push %edi ++ 0xc015ccec getname+0x4: push %esi ++ 0xc015cced getname+0x5: push %ebx ++ 0xc015ccee getname+0x6: sub $0x4,%esp ++ 0xc015ccf1 getname+0x9: mov %eax,%edi ++ 0xc015ccf3 getname+0xb: mov $0xd0,%edx ++ 0xc015ccf8 getname+0x10: mov 0xc04b2120,%eax ++ 0xc015ccfd getname+0x15: call 0xc0153009 kmem_cache_alloc ++ 0xc015cd02 getname+0x1a: mov %eax,0xfffffff0(%ebp) ++ 0xc015cd05 getname+0x1d: mov $0xfffffff4,%eax ++ 0xc015cd0a getname+0x22: cmpl $0x0,0xfffffff0(%ebp) ++ 0xc015cd0e getname+0x26: je 0xc015cd7d getname+0x95 ++ 0xc015cd10 getname+0x28: mov %esp,%eax ++ 0xc015cd12 getname+0x2a: and $0xfffff000,%eax ++ 0xc015cd17 getname+0x2f: cmpl $0xffffffff,0x18(%eax) ++ 0xc015cd1b getname+0x33: je 0xc015cd39 getname+0x51 ++ 0xc015cd1d getname+0x35: mov $0xfffffff2,%esi ++ 0xc015cd22 getname+0x3a: cmp $0xbfffffff,%edi ++ 0xc015cd28 getname+0x40: ja 0xc015cd60 getname+0x78 ++ 0xc015cd2a getname+0x42: mov $0xc0000000,%ebx ++ 0xc015cd2f getname+0x47: sub %edi,%ebx ++ 0xc015cd31 getname+0x49: cmp $0xfff,%ebx ++ 0xc015cd37 getname+0x4f: jbe 0xc015cd3e getname+0x56 ++ 0xc015cd39 getname+0x51: mov $0x1000,%ebx ++ 0xc015cd3e getname+0x56: mov %ebx,%ecx ++ 0xc015cd40 getname+0x58: mov %edi,%edx ++ 0xc015cd42 getname+0x5a: mov 0xfffffff0(%ebp),%eax ++ 0xc015cd45 getname+0x5d: call 0xc023dbb4 strncpy_from_user ++ 0xc015cd4a getname+0x62: cmp $0x0,%eax ++ 0xc015cd4d getname+0x65: jle 0xc015cd5a getname+0x72 ++ 0xc015cd4f getname+0x67: mov $0xffffffdc,%esi ++ 0xc015cd54 getname+0x6c: cmp %ebx,%eax ++ 0xc015cd56 getname+0x6e: jae 0xc015cd60 getname+0x78 ++ 0xc015cd58 getname+0x70: jmp 0xc015cd71 getname+0x89 ++ 0xc015cd5a getname+0x72: je 0xc015cd76 getname+0x8e ++ 0xc015cd5c getname+0x74: jge 0xc015cd71 getname+0x89 ++ 0xc015cd5e getname+0x76: mov %eax,%esi ++ 0xc015cd60 getname+0x78: mov 0xfffffff0(%ebp),%edx ++ 0xc015cd63 getname+0x7b: mov 0xc04b2120,%eax ++ 0xc015cd68 getname+0x80: call 0xc01521f1 kmem_cache_free ++ 0xc015cd6d getname+0x85: mov %esi,%eax ++ 0xc015cd6f getname+0x87: jmp 0xc015cd7d getname+0x95 ++ 0xc015cd71 getname+0x89: mov 0xfffffff0(%ebp),%eax ++ 0xc015cd74 getname+0x8c: jmp 0xc015cd7d getname+0x95 ++ 0xc015cd76 getname+0x8e: mov $0xfffffffe,%esi ++ 0xc015cd7b getname+0x93: jmp 0xc015cd60 getname+0x78 ++ 0xc015cd7d getname+0x95: pop %edx ++ 0xc015cd7e getname+0x96: pop %ebx ++ 0xc015cd7f getname+0x97: pop %esi ++ 0xc015cd80 getname+0x98: pop %edi ++ 0xc015cd81 getname+0x99: pop %ebp ++ 0xc015cd82 getname+0x9a: ret ++ ++The bb1 command only one argument which must be an address, not a name. bb1 ++turns on full basic block debugging and analyses the function containing the ++supplied address. Give bb1 the address of the exit point from this function, ++IOW the return address that is stored on stack due to a call from this function ++to the next function down the call stack. Assume that getname() has called ++kmem_cache_free() and something went wrong in kmem_cache_free() or one of the ++functions that it calls. The call to kmem_cache_free is at 0xc015cd68 and the ++return address on stack is the instruction after the call, i.e. 0xc015cd6d, so ++ ++ [0]kdb> bb1 0xc015cd6d ++ bb_pass1: func_name getname func_start 0xc015cce8 func_end 0xc015cd83 ++ ++bb_pass1 has identified the function name and its start and end address. For C ++functions these are just the function start address and the next symbol in ++kallsyms. For Assembler code there may be spurious labels so the function name ++may not match the label prior to the address given to bb1. For an example of ++that on i386, find the address of resume_userspace then pass that address to the ++bb1 KDB command. ++ ++ bb_pass1: end ++ bb[0] start 0xc015cce8 end 0xc015cd38 drop_through 1 ++ bb[1] start 0xc015cd39 end 0xc015cd3d drop_through 1 ++ bb[2] start 0xc015cd3e end 0xc015cd58 drop_through 0 ++ bb[3] start 0xc015cd5a end 0xc015cd5f drop_through 1 ++ bb[4] start 0xc015cd60 end 0xc015cd6f drop_through 0 ++ bb[5] start 0xc015cd71 end 0xc015cd74 drop_through 0 ++ bb[6] start 0xc015cd76 end 0xc015cd7b drop_through 0 ++ bb[7] start 0xc015cd7d end 0xc015cd82 drop_through 0 ++ bb_jmp[0] from 0xc015cd0e to 0xc015cd7d drop_through 0 ++ bb_jmp[1] from 0xc015cd1b to 0xc015cd39 drop_through 0 ++ bb_jmp[2] from 0xc015cd28 to 0xc015cd60 drop_through 0 ++ bb_jmp[3] from 0xc015cd37 to 0xc015cd3e drop_through 0 ++ bb_jmp[4] from 0xc015cd4d to 0xc015cd5a drop_through 0 ++ bb_jmp[5] from 0xc015cd56 to 0xc015cd60 drop_through 0 ++ bb_jmp[6] from 0xc015cd58 to 0xc015cd71 drop_through 0 ++ bb_jmp[7] from 0xc015cd5a to 0xc015cd76 drop_through 0 ++ bb_jmp[8] from 0xc015cd5c to 0xc015cd71 drop_through 0 ++ bb_jmp[9] from 0xc015cd6f to 0xc015cd7d drop_through 0 ++ bb_jmp[10] from 0xc015cd74 to 0xc015cd7d drop_through 0 ++ bb_jmp[11] from 0xc015cd7b to 0xc015cd60 drop_through 0 ++ bb_jmp[12] from 0xc015cd38 to 0xc015cd39 drop_through 1 ++ bb_jmp[13] from 0xc015cd3d to 0xc015cd3e drop_through 1 ++ bb_jmp[14] from 0xc015cd5f to 0xc015cd60 drop_through 1 ++ ++After analysing the logic flow, we can see that getname() consists of 8 basic ++blocks (nodes in bb_list[]). 5 of these blocks end in unconditional jumps, the ++other 3 drop through to the next block. There are 15 transfers of control ++(vertices in bb_jmp_list[]). 12 of these transfers are explicit jmp or jcc ++instructions, the other 3 are implicit transfers when dropping through from one ++block to the next. The node list is sorted by start address, the vertex list is ++not sorted. ++ ++Basic block 0 starts at the function start (0xc015cce8) and ends at 0xc015cd38. ++0xc015cd39 is the target of a jump instruction (0xc015cd1b: je 0xc015cd39) so ++0xc015cd39 starts a new block, which means that 0xc015cd38 becomes the end of ++the previous block. Because bb[0] does not end in an explicit jmp instruction, ++there is a drop through from the end of bb[0] to the start of bb[1], see ++bb_jmp[12]. ++ ++ bb_pass2: start ++ ++To get the most accurate results from pass2, try to scan the directed graph by ++only looking at nodes whose inputs are all defined. Initially only process ++nodes with no missing inputs. ++ ++ bb_pass2_do_changed_blocks: allow_missing 0 ++ ++ bb[0] ++ bb_reg_state c07282e0 ++ rax = rax ++ rbx = rbx ++ rcx = rcx ++ rdx = rdx ++ rdi = rdi ++ rsi = rsi ++ rbp = rbp ++ rsp = osp+0x0 ++ ++The initial state for bb[0] is the same for all C functions. Each register ++contains its own abstract value, except for rsp which is defined in terms of the ++original stack pointer (osp). ++ ++ '0xc015cce8 getname: push %ebp' ++ ++The first instruction of getname() saves the frame pointer. ++ ++ opcode 'push' matched by 'push', usage 44 ++ src R: %ebp base_rc 8 (rbp) ++ ++bb_usage() reports how the instruction was recognised and how its operands were ++decoded. Although this is i386 (ebp), it is reported as rbp. Using the x86_64 ++names for registers throughout makes it easier to create common code for the two ++architecures. ++ ++ rsp osp offset +0x0 -> -0x4 ++ ++A push instruction decrements rsp by 4 (i386) or 8 (x86_64) bytes. rsp ++originally contained the original stack pointer (osp), now it contains the ++original stack pointer - 4. ++ ++ *(rsp+0x0 osp-0x4) = rbp slot 0 ++ ++The stack location pointed to by *rsp now contains the original value of rbp. ++Since rsp contains (osp-0x4), *(osp-0x4) contains rbp. It is slot 0 in the ++memory array associated with the register state. ++ ++ '0xc015cce9 getname+0x1: mov %esp,%ebp' ++ opcode 'mov' matched by 'mov', usage 36 ++ src R: %esp base_rc 9 (rsp) ++ dst R: %ebp base_rc 8 (rbp) ++ rbp = rsp (osp-0x4) ++ ++Copy esp (rsp) to ebp (rbp). rsp contained (osp-0x4) so rbp also contains ++(osp-0x4). Any reference to data via either rbp or rsp will now be tracked as a ++stack location. ++ ++ '0xc015cceb getname+0x3: push %edi' ++ opcode 'push' matched by 'push', usage 44 ++ src R: %edi base_rc 6 (rdi) ++ rsp osp offset -0x4 -> -0x8 ++ *(rsp+0x0 osp-0x8) = rdi slot 1 ++ '0xc015ccec getname+0x4: push %esi' ++ opcode 'push' matched by 'push', usage 44 ++ src R: %esi base_rc 7 (rsi) ++ rsp osp offset -0x8 -> -0xc ++ *(rsp+0x0 osp-0xc) = rsi slot 2 ++ '0xc015cced getname+0x5: push %ebx' ++ opcode 'push' matched by 'push', usage 44 ++ src R: %ebx base_rc 3 (rbx) ++ rsp osp offset -0xc -> -0x10 ++ *(rsp+0x0 osp-0x10) = rbx slot 3 ++ ++Push 3 registers to stack. rsp is adjusted for each push and stack locations ++are assigned to contain the values of edi, esi and ebx. This sequence is very ++common in i386 C functions. edi, esi and ebx are preserved registers on i386, ++but gcc wants to use them for scratch space. The original contents iof these ++registers must be saved on stack and restored before returning to our caller. ++ ++ '0xc015ccee getname+0x6: sub $0x4,%esp' ++ opcode 'sub' matched by 'sub', usage 51 ++ src I: $0x4 ++ dst R: %esp base_rc 9 (rsp) ++ rsp osp offset -0x10 -> -0x14 ++ ++Subtract 4 bytes from esp. This defines the local stack variables. Sorry, ++names for local stack variables are not available to KDB. ++ ++ '0xc015ccf1 getname+0x9: mov %eax,%edi' ++ opcode 'mov' matched by 'mov', usage 36 ++ src R: %eax base_rc 2 (rax) ++ dst R: %edi base_rc 6 (rdi) ++ rdi = rax (rax) ++ ++Having saved edi on stack, gcc now overwrites edi with eax. At this point rax ++still contains its original value, so rdi now contains a copy of rax, as well as ++the original value which is still in rax. This is a common sequence in C code. ++rax contains argument 0 but it is also a scratch register. If the code needs to ++use argument 0 later then its value must be saved somewhere before executing any ++instruction that changes rax. edi is a preserved register so its contents will ++not be changed by any function that we call, or if it is changed then it will be ++restored before returning to this function. ++ ++rax is listed in the arch specific bb_param_reg[] list and the code is reading ++from rax while it still contains its original value. The only way that makes ++any sense is when rax is an input argument to getname(). We note that fact in ++bb_reg_read(). ++ ++ '0xc015ccf3 getname+0xb: mov $0xd0,%edx' ++ opcode 'mov' matched by 'mov', usage 36 ++ src I: $0xd0 ++ dst R: %edx base_rc 5 (rdx) ++ rdx = undefined ++ ++Moving an constant value to edx. Although this is a constant, it does not refer ++to any of the original values that were supplied to this function. Therefore ++rdx becomes undefined for the purposes of the code analysis. ++ ++ '0xc015ccf8 getname+0x10: mov 0xc04b2120,%eax' ++ opcode 'mov' matched by 'mov', usage 36 ++ src M: 0xc04b2120 ++ dst R: %eax base_rc 2 (rax) ++ rax = undefined ++ ++Moving a constant value to eax makes rax undefined. ++ ++ '0xc015ccfd getname+0x15: call 0xc0153009 ' ++ opcode 'call' matched by 'call', usage 17 ++ src M: 0xc0153009 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = rbx ++ rcx = rcx ++ rdx = undefined ++ rdi = rax ++ rsi = rsi ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ ++Basic block debugging prints the register and memory state when transfering ++control between blocks and when issuing call instructions. The call state is ++mainly useful when C code calls assembler routines, especially if you are not ++sure what state the assembler code expects. Not all of our assembler is as well ++documented as it could be :( ++ ++ rax = undefined ++ rcx = undefined ++ rdx = undefined ++ ++The i386 ABI says that some registers are preserved across calls, see the arch ++specific bb_preserved_reg[] list. Any registers not in that list automatically ++become undefined after a call instruction. ++ ++ '0xc015cd02 getname+0x1a: mov %eax,0xfffffff0(%ebp)' ++ opcode 'mov' matched by 'mov', usage 36 ++ src R: %eax base_rc 2 (rax) ++ dst M: 0xfffffff0(%ebp) base_rc 8 (rbp) ++ ++eax is the return value from the call, it is being saved at offset 0xfffffff0 ++(-0x10) from ebp. Since rbp contains (osp-0x4) the return value is being stored ++at (osp-0x14). This is a stack location but we have no record of any data being ++held at that location, it is part of the local stack variables. ++ ++ '0xc015cd05 getname+0x1d: mov $0xfffffff4,%eax' ++ opcode 'mov' matched by 'mov', usage 36 ++ src I: $0xfffffff4 ++ dst R: %eax base_rc 2 (rax) ++ rax = undefined ++ '0xc015cd0a getname+0x22: cmpl $0x0,0xfffffff0(%ebp)' ++ opcode 'cmpl' matched by 'cmp', usage 3 ++ src I: $0x0 ++ dst M: 0xfffffff0(%ebp) base_rc 8 (rbp) ++ '0xc015cd0e getname+0x26: je 0xc015cd7d ' ++ opcode 'je' matched by 'j', usage 28 ++ src M: 0xc015cd7d ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = rbx ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = rsi ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ ++Transfer of control, print the register and memory state. ++ ++ matched: from 0xc015cd0e to 0xc015cd7d drop_through 0 bb_jmp[0] ++ ++Which bb_jmp_list[] entry matches this transfer. ++ ++ new state c07286b8 ++ ++The current abstract register and memory state is cloned at address c07286b8. ++This state becomes one of the inputs to the basic block whose start address is ++0xc015cd7d. ++ ++ '0xc015cd10 getname+0x28: mov %esp,%eax' ++ opcode 'mov' matched by 'mov', usage 36 ++ src R: %esp base_rc 9 (rsp) ++ dst R: %eax base_rc 2 (rax) ++ rax = rsp (osp-0x14) ++ ++Copy rsp which contains (osp-0x14) to rax. rax contains a valid stack pointer. ++ ++ '0xc015cd12 getname+0x2a: and $0xfffff000,%eax' ++ opcode 'and' matched by 'and', usage 11 ++ src I: $0xfffff000 ++ dst R: %eax base_rc 2 (rax) ++ rax = undefined ++ ++But not for long. ++ ++ '0xc015cd17 getname+0x2f: cmpl $0xffffffff,0x18(%eax)' ++ opcode 'cmpl' matched by 'cmp', usage 3 ++ src I: $0xffffffff ++ dst M: 0x18(%eax) base_rc 2 (rax) ++ '0xc015cd1b getname+0x33: je 0xc015cd39 ' ++ opcode 'je' matched by 'j', usage 28 ++ src M: 0xc015cd39 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = rbx ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = rsi ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ ++Another transfer of control, print the state. ++ ++ matched: from 0xc015cd1b to 0xc015cd39 drop_through 0 bb_jmp[1] ++ ++Which bb_jmp_list[] entry was used. ++ ++ reuse bb_jmp[0] ++ ++To save space, we only clone the state if it is different. Otherwise we reuse ++the state from another vertex and bump the reference count. ++ ++ '0xc015cd1d getname+0x35: mov $0xfffffff2,%esi' ++ opcode 'mov' matched by 'mov', usage 36 ++ src I: $0xfffffff2 ++ dst R: %esi base_rc 7 (rsi) ++ rsi = undefined ++ ++Using esi as a scratch register, even though the i386 ABi says that esi is a ++preserved register. Not to worry, the original value of rsi was saved on stack ++on entry and it will be restored before exit. ++ ++ '0xc015cd22 getname+0x3a: cmp $0xbfffffff,%edi' ++ opcode 'cmp' matched by 'cmp', usage 3 ++ src I: $0xbfffffff ++ dst R: %edi base_rc 6 (rdi) ++ '0xc015cd28 getname+0x40: ja 0xc015cd60 ' ++ opcode 'ja' matched by 'j', usage 28 ++ src M: 0xc015cd60 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = rbx ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd28 to 0xc015cd60 drop_through 0 bb_jmp[2] ++ new state c0728710 ++ ++This state is different from any states already saved, clone to a new entry. ++ ++ '0xc015cd2a getname+0x42: mov $0xc0000000,%ebx' ++ opcode 'mov' matched by 'mov', usage 36 ++ src I: $0xc0000000 ++ dst R: %ebx base_rc 3 (rbx) ++ rbx = undefined ++ '0xc015cd2f getname+0x47: sub %edi,%ebx' ++ opcode 'sub' matched by 'sub', usage 51 ++ src R: %edi base_rc 6 (rdi) ++ dst R: %ebx base_rc 3 (rbx) ++ rbx = undefined ++ '0xc015cd31 getname+0x49: cmp $0xfff,%ebx' ++ opcode 'cmp' matched by 'cmp', usage 3 ++ src I: $0xfff ++ dst R: %ebx base_rc 3 (rbx) ++ '0xc015cd37 getname+0x4f: jbe 0xc015cd3e ' ++ opcode 'jbe' matched by 'j', usage 28 ++ src M: 0xc015cd3e ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd37 to 0xc015cd3e drop_through 0 bb_jmp[3] ++ new state c0728768 ++ ++This state is different from any states already saved, clone to a new entry. ++ ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd38 to 0xc015cd39 drop_through 1 bb_jmp[12] ++ reuse bb_jmp[3] ++ ++Basic block 0 drops through to basic block 1, treat it as an implicit transfer ++of control. The state is the same as the previous jump instruction so reuse it ++and bump the reference count. ++ ++That ends basic block 0, now pick the next block in the list that (a) needs to ++be scanned and (b) has all its input states defined. In this case bb[1]. ++ ++ bb[1] ++ ++bb[1] starts at 0xc015cd39 and has two paths that transfer control to it. ++bb_jmp[1] from an explicit jump at 0xc015cd1b and a drop through at bb_jmp[12]. ++Where there is more than one input state we have to merge them and reconcile the ++final value. ++ ++ first state c07286b8 ++ ++The first input state is stored at c07286b8. Looking back through the trace we ++find that entry associated with bb_jmp[0], not bb_jmp[1] as expected. However ++bb_jmp[1] reused the state that was stored for bb_jmp[0] so all is well. ++ ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = rbx ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = rsi ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ ++The first state for bb[1]. ++ ++ merging state c0728768 ++ ++Now merge the second state, which is held at c0728768. ++ ++ rbx = undefined ++ rsi = undefined ++ ++The two states disagree on the values being tracked in rbx and rsi. Compiler ++theory 101 says that if two or more paths to a basic block have different values ++for a register then that register cannot be relied on at the start of the block, ++so make it undefined. The same logic applies to memory locations. ++ ++ final state ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ ++After merging all the input states, this is the final starting state for bb[1]. ++Now track what bb[1] does to the state. ++ ++ '0xc015cd39 getname+0x51: mov $0x1000,%ebx' ++ opcode 'mov' matched by 'mov', usage 36 ++ src I: $0x1000 ++ dst R: %ebx base_rc 3 (rbx) ++ rbx = undefined ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd3d to 0xc015cd3e drop_through 1 bb_jmp[13] ++ reuse bb_jmp[3] ++ ++bb[1] is a single instruction which drops through to bb[2]. ++ ++ bb[2] ++ first state c0728768 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ merging state c0728768 ++ ++bb[2] has two inputs, both vertices are pointing to input state c0728768. ++Merging an entry with itself has no effect. ++ ++ '0xc015cd3e getname+0x56: mov %ebx,%ecx' ++ opcode 'mov' matched by 'mov', usage 36 ++ src R: %ebx base_rc 3 (rbx) ++ dst R: %ecx base_rc 4 (rcx) ++ rcx = rbx (undefined) ++ '0xc015cd40 getname+0x58: mov %edi,%edx' ++ opcode 'mov' matched by 'mov', usage 36 ++ src R: %edi base_rc 6 (rdi) ++ dst R: %edx base_rc 5 (rdx) ++ rdx = rdi (rax) ++ '0xc015cd42 getname+0x5a: mov 0xfffffff0(%ebp),%eax' ++ opcode 'mov' matched by 'mov', usage 36 ++ src M: 0xfffffff0(%ebp) base_rc 8 (rbp) ++ dst R: %eax base_rc 2 (rax) ++ rax = *(rbp-0x10) (osp-0x14) rax = undefined ++ '0xc015cd45 getname+0x5d: call 0xc023dbb4 ' ++ opcode 'call' matched by 'call', usage 17 ++ src M: 0xc023dbb4 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = rax ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ rax = undefined ++ rcx = undefined ++ rdx = undefined ++ '0xc015cd4a getname+0x62: cmp $0x0,%eax' ++ opcode 'cmp' matched by 'cmp', usage 3 ++ src I: $0x0 ++ dst R: %eax base_rc 2 (rax) ++ '0xc015cd4d getname+0x65: jle 0xc015cd5a ' ++ opcode 'jle' matched by 'j', usage 28 ++ src M: 0xc015cd5a ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd4d to 0xc015cd5a drop_through 0 bb_jmp[4] ++ reuse bb_jmp[3] ++ '0xc015cd4f getname+0x67: mov $0xffffffdc,%esi' ++ opcode 'mov' matched by 'mov', usage 36 ++ src I: $0xffffffdc ++ dst R: %esi base_rc 7 (rsi) ++ rsi = undefined ++ '0xc015cd54 getname+0x6c: cmp %ebx,%eax' ++ opcode 'cmp' matched by 'cmp', usage 3 ++ src R: %ebx base_rc 3 (rbx) ++ dst R: %eax base_rc 2 (rax) ++ '0xc015cd56 getname+0x6e: jae 0xc015cd60 ' ++ opcode 'jae' matched by 'j', usage 28 ++ src M: 0xc015cd60 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd56 to 0xc015cd60 drop_through 0 bb_jmp[5] ++ reuse bb_jmp[3] ++ '0xc015cd58 getname+0x70: jmp 0xc015cd71 ' ++ opcode 'jmp' matched by 'j', usage 28 ++ src M: 0xc015cd71 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd58 to 0xc015cd71 drop_through 0 bb_jmp[6] ++ reuse bb_jmp[3] ++ ++ bb[3] ++ first state c0728768 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ ++bb[3] only has one input, nothing to merge. ++ ++ '0xc015cd5a getname+0x72: je 0xc015cd76 ' ++ opcode 'je' matched by 'j', usage 28 ++ src M: 0xc015cd76 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd5a to 0xc015cd76 drop_through 0 bb_jmp[7] ++ reuse bb_jmp[3] ++ '0xc015cd5c getname+0x74: jge 0xc015cd71 ' ++ opcode 'jge' matched by 'j', usage 28 ++ src M: 0xc015cd71 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd5c to 0xc015cd71 drop_through 0 bb_jmp[8] ++ reuse bb_jmp[3] ++ '0xc015cd5e getname+0x76: mov %eax,%esi' ++ opcode 'mov' matched by 'mov', usage 36 ++ src R: %eax base_rc 2 (rax) ++ dst R: %esi base_rc 7 (rsi) ++ rsi = rax (undefined) ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd5f to 0xc015cd60 drop_through 1 bb_jmp[14] ++ reuse bb_jmp[3] ++ ++ bb[5] ++ first state c0728768 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ merging state c0728768 ++ '0xc015cd71 getname+0x89: mov 0xfffffff0(%ebp),%eax' ++ opcode 'mov' matched by 'mov', usage 36 ++ src M: 0xfffffff0(%ebp) base_rc 8 (rbp) ++ dst R: %eax base_rc 2 (rax) ++ rax = *(rbp-0x10) (osp-0x14) rax = undefined ++ '0xc015cd74 getname+0x8c: jmp 0xc015cd7d ' ++ opcode 'jmp' matched by 'j', usage 28 ++ src M: 0xc015cd7d ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd74 to 0xc015cd7d drop_through 0 bb_jmp[10] ++ reuse bb_jmp[3] ++ ++ bb[6] ++ first state c0728768 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ '0xc015cd76 getname+0x8e: mov $0xfffffffe,%esi' ++ opcode 'mov' matched by 'mov', usage 36 ++ src I: $0xfffffffe ++ dst R: %esi base_rc 7 (rsi) ++ rsi = undefined ++ '0xc015cd7b getname+0x93: jmp 0xc015cd60 ' ++ opcode 'jmp' matched by 'j', usage 28 ++ src M: 0xc015cd60 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd7b to 0xc015cd60 drop_through 0 bb_jmp[11] ++ reuse bb_jmp[3] ++ ++ bb[4] ++ first state c0728710 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = rbx ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ merging state c0728768 ++ rbx = undefined ++ merging state c0728768 ++ merging state c0728768 ++ ++bb[4] has 4 inputs, 3 of which have the same state. One one path (state ++c0728710) rbx is defined, on the others (c0728768) rbx is undefined so the final ++state has rbx as undefined. ++ ++ final state ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ '0xc015cd60 getname+0x78: mov 0xfffffff0(%ebp),%edx' ++ opcode 'mov' matched by 'mov', usage 36 ++ src M: 0xfffffff0(%ebp) base_rc 8 (rbp) ++ dst R: %edx base_rc 5 (rdx) ++ rdx = *(rbp-0x10) (osp-0x14) rdx = undefined ++ '0xc015cd63 getname+0x7b: mov 0xc04b2120,%eax' ++ opcode 'mov' matched by 'mov', usage 36 ++ src M: 0xc04b2120 ++ dst R: %eax base_rc 2 (rax) ++ rax = undefined ++ '0xc015cd68 getname+0x80: call 0xc01521f1 ' ++ opcode 'call' matched by 'call', usage 17 ++ src M: 0xc01521f1 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ rax = undefined ++ rcx = undefined ++ rdx = undefined ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ '0xc015cd6d getname+0x85: mov %esi,%eax' ++ opcode 'mov' matched by 'mov', usage 36 ++ src R: %esi base_rc 7 (rsi) ++ dst R: %eax base_rc 2 (rax) ++ rax = rsi (undefined) ++ '0xc015cd6f getname+0x87: jmp 0xc015cd7d ' ++ opcode 'jmp' matched by 'j', usage 28 ++ src M: 0xc015cd7d ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ matched: from 0xc015cd6f to 0xc015cd7d drop_through 0 bb_jmp[9] ++ reuse bb_jmp[3] ++ ++ bb[7] ++ first state c07286b8 ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = rbx ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = rsi ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ merging state c0728768 ++ rbx = undefined ++ rsi = undefined ++ merging state c0728768 ++ final state ++ bb_reg_state c0728658 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ '0xc015cd7d getname+0x95: pop %edx' ++ opcode 'pop' matched by 'pop', usage 42 ++ src R: %edx base_rc 5 (rdx) ++ rdx = *(rsp+0x0) (osp-0x14) rdx = undefined ++ rsp osp offset -0x14 -> -0x10 ++ ++This instruction is a bit misleading. It looks like gcc is restoring a value ++from the stack *(osp-0x14) to edx, but we have no record of any useful data ++being stored at osp-0x14. In fact gcc is just reducing the stack pointer by 4 ++bytes to reverse the effect of 0xc015ccee: sub $0x4,%esp, the value popped into ++edx contains nothing useful. Why gcc does pop instead of add $0x4,%esp is a ++puzzle, probably some micro optimization. ++ ++ '0xc015cd7e getname+0x96: pop %ebx' ++ opcode 'pop' matched by 'pop', usage 42 ++ src R: %ebx base_rc 3 (rbx) ++ rbx = *(rsp+0x0) (osp-0x10) value rbx ++ rsp osp offset -0x10 -> -0xc ++ delete rbx from osp-0x10 slot 3 ++ ++This pop is doing something useful. It is restoring the original value of the ++preserved register ebx from stack, reversing 0xc015cced: push %ebx. Note that ++incrementing rsp from osp-0x10 to osp-0xc invalidates the data held in memory at ++osp-0x10, so we delete our record of it. ++ ++ '0xc015cd7f getname+0x97: pop %esi' ++ opcode 'pop' matched by 'pop', usage 42 ++ src R: %esi base_rc 7 (rsi) ++ rsi = *(rsp+0x0) (osp-0xc) value rsi ++ rsp osp offset -0xc -> -0x8 ++ delete rsi from osp-0xc slot 2 ++ '0xc015cd80 getname+0x98: pop %edi' ++ opcode 'pop' matched by 'pop', usage 42 ++ src R: %edi base_rc 6 (rdi) ++ rdi = *(rsp+0x0) (osp-0x8) value rdi ++ rsp osp offset -0x8 -> -0x4 ++ delete rdi from osp-0x8 slot 1 ++ ++Pop the other preserved registers, in reverse order to the push sequence at the ++start. ++ ++ '0xc015cd81 getname+0x99: pop %ebp' ++ opcode 'pop' matched by 'pop', usage 42 ++ src R: %ebp base_rc 8 (rbp) ++ rbp = *(rsp+0x0) (osp-0x4) value rbp ++ rsp osp offset -0x4 -> +0x0 ++ delete rbp from osp-0x4 slot 0 ++ ++Pop the previous frame pointer. ++ ++ '0xc015cd82 getname+0x9a: ret ' ++ opcode 'ret' matched by 'ret', usage 48 ++ ++When a ret instruction is executed, all the preserved registers must be back to ++their original value and the stack pointer must contain osp+0. ++bb_sanity_check() will complain and abort the backtrace if this is not true. No ++complaints here. ++ ++ bb_pass2: end bb_reg_params 1 bb_memory_params 0 ++ ++We identified one argument passed in a register (the read of rax at 0xc015ccf1) ++and no reference to memory locations above the stack frame. So we have one ++argument being passed in a register and no arguments being passed on stack. ++This matches ++ ++ char * getname(const char __user * filename) ++ ++ bb_pass2: bb_exit_state at 0xc015cd6d ++ bb_reg_state c07287c0 ++ rax = undefined ++ rbx = undefined ++ rcx = undefined ++ rdx = undefined ++ rdi = rax ++ rsi = undefined ++ rbp = osp-0x4 ++ rsp = osp-0x14 ++ slot 0 offset_address -0x4 rbp ++ slot 1 offset_address -0x8 rdi ++ slot 2 offset_address -0xc rsi ++ slot 3 offset_address -0x10 rbx ++ ++We told bb1 that the exit address from this function is 0xc015cd6d. The ++abstract state at this exit point was saved, it defines how we rollback the ++actual register values from the next function down the stack (kmem_cache_free) ++to get the actual register values on entry to this function (getname). See ++bb_actual_rollback() which updates bb_actual[]. ++ ++Looking at the exit state above, we see that rsp contains the abstracte value ++osp-0x14. It is a given that we have the actual value of rsp after the call ++from getname() to kmem_cache_free(), otherwise we would not have found the ++return address on stack and we would not be analysing getname(). Adding 0x14 ++(the delta from osp to rsp) to our current actual rsp gives us the actual value ++of osp on entry to getname(). ++ ++The main aim of doing all this work is to track the function arguments so we can ++print them if possible. getname() only has one argument which was passed in ++eax. According to the abstract exit state, the original value of rax is ++currently in rdi, so by looking at the actual value of rdi from the next stack ++frame down we are able to get the argument to getname(). ++ ++It is not always possible to get register arguments, gcc will only preserve ++input arguments as long as it needs them so there may be no saved copy of ++arguments that are passed in register. In this case, bt_print_one() prints ++"invalid". ++ ++If basic block analysis detected any arguments were passed on stack, their ++contents can now be extracted based on the known value of the stack pointer. ++bt_print_one() prints the arguments, if BT_ARGS is non-zero then any argument ++that might be a kernel address is printed as a symbol. ++ ++Once rsp has been rolled back to osp, we can calculate that actual address of ++the stack locations that contain useful data. The previous values of rbp, rdi, ++rsi and rbx are then copied from those locations into bb_actual[]. That gives ++the values for those registers at the exit point from the function that called ++getname(). Go up one level and repeat the analysis. ++ ++There are two references to rdi in the exit state, which can be confusing. ++ ++ rdi = rax ++ slot 1 offset_address -0x8 rdi ++ ++The first reference says that "register rdi contains the original value of rax", ++the second reference says that "*(osp-0x8) contains the original value of rdi". ++Do not confuse the two, one is by name, the other is by value. ++ ++getname() is a fairly simple function, it has no loops. __follow_mount is more ++complicated, it has loops as well as BUG() statements. ++ ++ [0]kdb> id __follow_mount ++ 0xc015be76 __follow_mount: push %ebp ++ 0xc015be77 __follow_mount+0x1: mov %esp,%ebp ++ 0xc015be79 __follow_mount+0x3: push %edi ++ 0xc015be7a __follow_mount+0x4: push %esi ++ 0xc015be7b __follow_mount+0x5: push %ebx ++ 0xc015be7c __follow_mount+0x6: mov %eax,%esi ++ 0xc015be7e __follow_mount+0x8: xor %edi,%edi ++ 0xc015be80 __follow_mount+0xa: jmp 0xc015beca __follow_mount+0x54 ++ 0xc015be82 __follow_mount+0xc: mov (%esi),%eax ++ 0xc015be84 __follow_mount+0xe: call 0xc0169664 lookup_mnt ++ 0xc015be89 __follow_mount+0x13: mov %eax,%ebx ++ 0xc015be8b __follow_mount+0x15: test %eax,%eax ++ 0xc015be8d __follow_mount+0x17: je 0xc015bed3 __follow_mount+0x5d ++ 0xc015be8f __follow_mount+0x19: mov 0x4(%esi),%eax ++ 0xc015be92 __follow_mount+0x1c: call 0xc0163de2 dput ++ 0xc015be97 __follow_mount+0x21: test %edi,%edi ++ 0xc015be99 __follow_mount+0x23: je 0xc015bead __follow_mount+0x37 ++ 0xc015be9b __follow_mount+0x25: mov (%esi),%eax ++ 0xc015be9d __follow_mount+0x27: test %eax,%eax ++ 0xc015be9f __follow_mount+0x29: je 0xc015bead __follow_mount+0x37 ++ 0xc015bea1 __follow_mount+0x2b: movl $0x0,0x64(%eax) ++ 0xc015bea8 __follow_mount+0x32: call 0xc016835b mntput_no_expire ++ 0xc015bead __follow_mount+0x37: mov %ebx,(%esi) ++ 0xc015beaf __follow_mount+0x39: mov 0x10(%ebx),%eax ++ 0xc015beb2 __follow_mount+0x3c: test %eax,%eax ++ 0xc015beb4 __follow_mount+0x3e: je 0xc015bec2 __follow_mount+0x4c ++ 0xc015beb6 __follow_mount+0x40: cmpl $0x0,(%eax) ++ 0xc015beb9 __follow_mount+0x43: jne 0xc015bebf __follow_mount+0x49 ++ 0xc015bebb __follow_mount+0x45: ud2a ++ 0xc015bebd __follow_mount+0x47: jmp 0xc015bebd __follow_mount+0x47 ++ 0xc015bebf __follow_mount+0x49: lock incl (%eax) ++ 0xc015bec2 __follow_mount+0x4c: mov %eax,0x4(%esi) ++ 0xc015bec5 __follow_mount+0x4f: mov $0x1,%edi ++ 0xc015beca __follow_mount+0x54: mov 0x4(%esi),%edx ++ 0xc015becd __follow_mount+0x57: cmpl $0x0,0x74(%edx) ++ 0xc015bed1 __follow_mount+0x5b: jne 0xc015be82 __follow_mount+0xc ++ 0xc015bed3 __follow_mount+0x5d: mov %edi,%eax ++ 0xc015bed5 __follow_mount+0x5f: pop %ebx ++ 0xc015bed6 __follow_mount+0x60: pop %esi ++ 0xc015bed7 __follow_mount+0x61: pop %edi ++ 0xc015bed8 __follow_mount+0x62: pop %ebp ++ 0xc015bed9 __follow_mount+0x63: ret ++ ++ [0]kdb> bb1 0xc015bed9 ++ bb_pass1: func_name __follow_mount func_start 0xc015be76 func_end 0xc015beda ++ bb_pass1: end ++ bb[0] start 0xc015be76 end 0xc015be80 drop_through 0 ++ bb[1] start 0xc015be82 end 0xc015beac drop_through 1 ++ bb[2] start 0xc015bead end 0xc015bebb drop_through 0 ++ ++Note that the ud2a (BUG) instruction at 0xc015bebb ends bb[2]. ++ ++ bb[3] start 0xc015bebd end 0xc015bebd drop_through 0 ++ ++bb[3] is peculiar, it is a jmp to itself, nothing else refers to 0xc015bebd and ++you cannot drop through from the previous instruction because ud2a kills the ++kernel. The i386 and x86_64 BUG() macros contain for(;;) after ud2a, for no ++good reason that I can see (is there old hardware that does not abort on ud2a?). ++ia64 and the generic versions of BUG() do not contain for(;;). for(;;) after ++ud2a generates a branch to itself than can never be executed. ++ ++ bb[4] start 0xc015bebf end 0xc015bec1 drop_through 1 ++ bb[5] start 0xc015bec2 end 0xc015bec9 drop_through 1 ++ bb[6] start 0xc015beca end 0xc015bed2 drop_through 1 ++ bb[7] start 0xc015bed3 end 0xc015bed9 drop_through 0 ++ bb_jmp[0] from 0xc015be80 to 0xc015beca drop_through 0 ++ bb_jmp[1] from 0xc015be8d to 0xc015bed3 drop_through 0 ++ bb_jmp[2] from 0xc015be99 to 0xc015bead drop_through 0 ++ bb_jmp[3] from 0xc015be9f to 0xc015bead drop_through 0 ++ bb_jmp[4] from 0xc015beb4 to 0xc015bec2 drop_through 0 ++ bb_jmp[5] from 0xc015beb9 to 0xc015bebf drop_through 0 ++ bb_jmp[6] from 0xc015bebd to 0xc015bebd drop_through 0 ++ bb_jmp[7] from 0xc015bed1 to 0xc015be82 drop_through 0 ++ bb_jmp[8] from 0xc015beac to 0xc015bead drop_through 1 ++ bb_jmp[9] from 0xc015bec1 to 0xc015bec2 drop_through 1 ++ bb_jmp[10] from 0xc015bec9 to 0xc015beca drop_through 1 ++ bb_jmp[11] from 0xc015bed2 to 0xc015bed3 drop_through 1 ++ ++Apart from bb[0] and the special case bb[3], all the other blocks are part of a ++cycle. That cycle goes bb[0] -> bb[6]. bb[6] -> {bb[1], bb[7]}. bb[1] -> ++{bb[2], bb[7]}. bb[2] -> {bb[4], bb[5]}. bb[4] -> bb[5]. bb[5] -> bb[6] and ++back to the start. bb[7] ends with 'ret', it does not feed into other blocks. ++ ++ bb_pass2: start ++ ++ bb_pass2_do_changed_blocks: allow_missing 0 ++ ++ bb[0] ++ [ ... detail snipped ... ] ++ matched: from 0xc015be80 to 0xc015beca drop_through 0 bb_jmp[0] ++ new state c07286d8 ++ ++ bb_pass2_do_changed_blocks: allow_missing 1 ++ ++Because of the cycle, only bb[0] can be processed with 0 missing inputs, all the ++other blocks have at least one missing input. Call bb_pass2_do_changed_blocks() ++again, this time allowing one missing input per blocks. ++ ++ bb[6] ++ first state c07286d8 ++ [ ... detail snipped ... ] ++ matched: from 0xc015bed2 to 0xc015bed3 drop_through 1 bb_jmp[11] ++ reuse bb_jmp[7] ++ ++ bb[7] ++ first state c0728730 ++ [ ... detail snipped ... ] ++ ++ bb[1] ++ first state c0728730 ++ [ ... detail snipped ... ] ++ matched: from 0xc015beac to 0xc015bead drop_through 1 bb_jmp[8] ++ reuse bb_jmp[1] ++ ++ bb[2] ++ first state c0728788 ++ [ ... detail snipped ... ] ++ merging state c0728788 ++ merging state c0728788 ++ [ ... detail snipped ... ] ++ matched: from 0xc015beb9 to 0xc015bebf drop_through 0 bb_jmp[5] ++ reuse bb_jmp[1] ++ ++ bb[4] ++ first state c0728788 ++ [ ... detail snipped ... ] ++ matched: from 0xc015bec1 to 0xc015bec2 drop_through 1 bb_jmp[9] ++ reuse bb_jmp[1] ++ ++ bb[5] ++ first state c0728788 ++ [ ... detail snipped ... ] ++ merging state c0728788 ++ [ ... detail snipped ... ] ++ matched: from 0xc015bec9 to 0xc015beca drop_through 1 bb_jmp[10] ++ reuse bb_jmp[1] ++ ++ bb[6] ++ first state c07286d8 ++ [ ... detail snipped ... ] ++ merging state c0728788 ++ matched: from 0xc015bed2 to 0xc015bed3 drop_through 1 bb_jmp[11] ++ reuse bb_jmp[1] ++ ++Note the rescan of bb[6]. The first scan only had one input from bb[0]. After ++traversing the cycle and getting back from bb[5] to bb[6], bb[6] now has more ++inputs so we need to rescan it. With the additional input, the output state ++from bb[6] has changed since the first scan, which means that every block it ++feeds has to be rescanned. bb[6] feeds bb[1] and bb[7]. ++ ++ bb[7] ++ first state c0728788 ++ [ ... detail snipped ... ] ++ merging state c0728788 ++ [ ... detail snipped ... ] ++ ++bb[7] being rescanned, this time it has data for both its inputs. ++ ++ bb[1] ++ first state c0728788 ++ [ ... detail snipped ... ] ++ matched: from 0xc015beac to 0xc015bead drop_through 1 bb_jmp[8] ++ no state change ++ ++bb[1] is being rescanned because the input from bb[6] has changed, however the ++rescan of bb[1] reports 'no state change', the changed input from bb[6] did not ++affect the final output state from bb[1]. Because the output state from bb[1] ++has not changed since the previous scan, there is no need to rescan bb[2], bb[7] ++or bb[4]. Since bb[4] is not being rescanned, there is no need to rescan bb[5] ++or bb[6] and the cycle is closed. +--- /dev/null ++++ b/Documentation/kdb/kdb.mm +@@ -0,0 +1,492 @@ ++.TH KDB 8 "September 21, 2005" ++.hy 0 ++.SH NAME ++Built-in Kernel Debugger for Linux - v4.4 ++.SH "Overview" ++This document describes the built-in kernel debugger available ++for linux. This debugger allows the programmer to interactively ++examine kernel memory, disassemble kernel functions, set breakpoints ++in the kernel code and display and modify register contents. ++.P ++A symbol table is included in the kernel image and in modules which ++enables all non-stack symbols (including static symbols) to be used as ++arguments to the kernel debugger commands. ++.SH "Getting Started" ++To include the kernel debugger in a linux kernel, use a ++configuration mechanism (e.g. xconfig, menuconfig, et. al.) ++to enable the \fBCONFIG_KDB\fP option. Additionally, for accurate ++stack tracebacks, it is recommended that the \fBCONFIG_FRAME_POINTER\fP ++option be enabled (if present). \fBCONFIG_FRAME_POINTER\fP changes the compiler ++flags so that the frame pointer register will be used as a frame ++pointer rather than a general purpose register. ++.P ++After linux has been configured to include the kernel debugger, ++make a new kernel with the new configuration file (a make clean ++is recommended before making the kernel), and install the kernel ++as normal. ++.P ++You can compile a kernel with kdb support but have kdb off by default, ++select \fBCONFIG_KDB_OFF\fR. Then the user has to explicitly activate ++kdb by booting with the 'kdb=on' flag or, after /proc is mounted, by ++.nf ++ echo "1" > /proc/sys/kernel/kdb ++.fi ++You can also do the reverse, compile a kernel with kdb on and ++deactivate kdb with the boot flag 'kdb=off' or, after /proc is mounted, ++by ++.nf ++ echo "0" > /proc/sys/kernel/kdb ++.fi ++.P ++When booting the new kernel, the 'kdb=early' flag ++may be added after the image name on the boot line to ++force the kernel to stop in the kernel debugger early in the ++kernel initialization process. 'kdb=early' implies 'kdb=on'. ++If the 'kdb=early' flag isn't provided, then kdb will automatically be ++invoked upon system panic or when the \fBPAUSE\fP key is used from the ++keyboard, assuming that kdb is on. Older versions of kdb used just a ++boot flag of 'kdb' to activate kdb early, this is no longer supported. ++.P ++KDB can also be used via the serial port. Set up the system to ++have a serial console (see \fIDocumentation/serial-console.txt\fP), you ++must also have a user space program such as agetty set up to read from ++the serial console. ++The control sequence \fBKDB\fP on the serial port will cause the ++kernel debugger to be entered, assuming that kdb is on, that some ++program is reading from the serial console, at least one cpu is ++accepting interrupts and the serial console driver is still usable. ++.P ++\fBNote:\fR\ When the serial console sequence consists of multiple ++characters such as KDB then all but the last character are passed ++through to the application that is reading from the serial console. ++After exiting from kdb, you should use backspace to delete the rest of ++the control sequence. ++.P ++You can boot with kdb activated but without the ability to enter kdb ++via any keyboard sequence. ++In this mode, kdb will only be entered after a system failure. ++Booting with kdb=on-nokey will activate kdb but ignore keyboard ++sequences that would normally drop you into kdb. ++kdb=on-nokey is mainly useful when you are using a PC keyboard and your ++application needs to use the Pause key. ++You can also activate this mode by ++.nf ++ echo "2" > /proc/sys/kernel/kdb ++.fi ++.P ++If the console is sitting on the login prompt when you enter kdb, then ++the login command may switch into upper case mode. ++This is not a kdb bug, it is a "feature" of login - if the userid is ++all upper case then login assumes that you using a TeleType (circa ++1960) which does not have lower case characters. ++Wait 60 seconds for login to timeout and it will switch back to lower ++case mode. ++.P ++\fBNote:\fR\ Your distributor may have chosen a different kdb ++activation sequence for the serial console. ++Consult your distribution documentation. ++.P ++If you have both a keyboard+video and a serial console, you can use ++either for kdb. ++Define both video and serial consoles with boot parameters ++.P ++.nf ++ console=tty0 console=ttyS0,38400 ++.fi ++.P ++Any kdb data entered on the keyboard or the serial console will be echoed ++to both. ++.P ++If you are using a USB keyboard then kdb commands cannot be entered ++until the kernel has initialised the USB subsystem and recognised the ++keyboard. ++Using kdb=early with a USB keyboard will not work, the USB subsystem is ++initialised too late. ++.P ++While kdb is active, the keyboard (not serial console) indicators may strobe. ++The caps lock and scroll lock lights will turn on and off, num lock is not used ++because it can confuse laptop keyboards where the numeric keypad is mapped over ++the normal keys. ++On exit from kdb the keyboard indicators will probably be wrong, they will not match the kernel state. ++Pressing caps lock twice should get the indicators back in sync with ++the kernel. ++.SH "Basic Commands" ++There are several categories of commands available to the ++kernel debugger user including commands providing memory ++display and modification, register display and modification, ++instruction disassemble, breakpoints and stack tracebacks. ++Any command can be prefixed with '-' which will cause kdb to ignore any ++errors on that command, this is useful when packaging commands using ++defcmd. ++A line whose first non-space character is '#' is printed and ignored. ++.P ++The following table shows the currently implemented standard commands, ++these are always available. Other commands can be added by extra ++debugging modules, type '?' at the kdb prompt to get a list of all ++available commands. ++.DS ++.TS ++box, center; ++l | l ++l | l. ++Command Description ++_ ++bc Clear Breakpoint ++bd Disable Breakpoint ++be Enable Breakpoint ++bl Display breakpoints ++bp Set or Display breakpoint ++bph Set or Display hardware breakpoint ++bpa Set or Display breakpoint globally ++bpha Set or Display hardware breakpoint globally ++bt Stack backtrace for current process ++btp Stack backtrace for specific process ++bta Stack backtrace for all processes ++btc Cycle over all live cpus and backtrace each one ++cpu Display or switch cpus ++dmesg Display system messages ++defcmd Define a command as a set of other commands ++ef Print exception frame ++env Show environment ++go Restart execution ++handlers Control the display of IA64 MCA/INIT handlers ++help Display help message ++id Disassemble Instructions ++kill Send a signal to a process ++ll Follow Linked Lists ++lsmod List loaded modules ++md Display memory contents ++mdWcN Display memory contents with width W and count N. ++mdp Display memory based on a physical address ++mdr Display raw memory contents ++mds Display memory contents symbolically ++mm Modify memory contents, words ++mmW Modify memory contents, bytes ++per_cpu Display per_cpu variables ++pid Change the default process context ++ps Display process status ++reboot Reboot the machine ++rd Display register contents ++rm Modify register contents ++rq Display runqueue for one cpu ++rqa Display runqueue for all cpus ++set Add/change environment variable ++sr Invoke SysReq commands ++ss Single step a cpu ++ssb Single step a cpu until a branch instruction ++stackdepth Print the stack depth for selected processes ++summary Summarize the system ++.TE ++.DE ++.P ++Some commands can be abbreviated, such commands are indicated by a ++non-zero \fIminlen\fP parameter to \fBkdb_register\fP; the value of ++\fIminlen\fP being the minimum length to which the command can be ++abbreviated (for example, the \fBgo\fP command can be abbreviated ++legally to \fBg\fP). ++.P ++If an input string does not match a command in the command table, ++it is treated as an address expression and the corresponding address ++value and nearest symbol are shown. ++.P ++Some of the commands are described here. ++Information on the more complicated commands can be found in the ++appropriate manual pages. ++.TP 8 ++cpu ++With no parameters, it lists the available cpus. ++\&'*' after a cpu number indicates a cpu that did not respond to the kdb ++stop signal. ++\&'+' after a cpu number indicates a cpu for which kdb has some data, but ++that cpu is no longer responding to kdb, so you cannot switch to it. ++This could be a cpu that has failed after entering kdb, or the cpu may ++have saved its state for debugging then entered the prom, this is ++normal for an IA64 MCA event. ++\&'I' after a cpu number means that the cpu was idle before it entered ++kdb, it is unlikely to contain any useful data. ++\&'F' after a cpu number means that the cpu is offline. ++There is currenly no way to distinguish between cpus that used to be ++online but are now offline and cpus that were never online, the kernel ++does not maintain the information required to separate those two cases. ++.I cpu ++followed by a number will switch to that cpu, you cannot switch to ++a cpu marked '*', '+' or 'F'. ++This command is only available if the kernel was configured for SMP. ++.TP 8 ++dmesg [lines] [adjust] ++Displays the system messages from the kernel buffer. ++If kdb logging is on, it is disabled by dmesg and is left as disabled. ++With no parameters or a zero value for 'lines', dmesg dumps the entire ++kernel buffer. ++If lines is specified and is positive, dmesg dumps the last 'lines' ++from the buffer. ++If lines is specified and is negative, dmesg dumps the first 'lines' ++from the buffer. ++If adjust is specified, adjust the starting point for the lines that ++are printed. ++When 'lines' is positive, move the starting point back by 'adjust' ++lines, when 'lines' is negative, move the starting point forward by ++\&'adjust' lines. ++.I dmesg -100 ++will dump 100 lines, from the start of the buffer. ++.I dmesg 100 ++will dump 100 lines, starting 100 lines from the end of the buffer, ++.I dmesg 100 100 ++will dump 100 lines, starting 200 lines from the end of the buffer. ++.I dmesg -100 100 ++will dump 100 lines, starting 100 lines from the start of the buffer. ++.TP 8 ++defcmd ++Defines a new command as a set of other commands, all input until ++.I endefcmd ++is saved and executed as a package. ++.I defcmd ++takes three parameters, the command name to be defined and used to ++invoke the package, a quoted string containing the usage text and a ++quoted string containing the help text for the command. ++When using defcmd, it is a good idea to prefix commands that might fail ++with '-', this ignores errors so the following commands are still ++executed. ++For example, ++.P ++.nf ++ defcmd diag "" "Standard diagnostics" ++ set LINES 2000 ++ set BTAPROMPT 0 ++ -id %eip-0x40 ++ -cpu ++ -ps ++ -dmesg 80 ++ -bt ++ -bta ++ endefcmd ++.fi ++.P ++When used with no parameters, defcmd prints all the defined commands. ++.TP 8 ++go ++Continue normal execution. ++Active breakpoints are reestablished and the processor(s) allowed to ++run normally. ++To continue at a specific address, use ++.I rm ++to change the instruction pointer then go. ++.TP 8 ++handlers ++Control the display of IA64 MCA/INIT handlers. ++The IA64 MCA/INIT handlers run on separate tasks. ++During an MCA/INIT event, the active tasks are typically the handlers, ++rather than the original tasks, which is not very useful for debugging. ++By default, KDB hides the MCA/INIT handlers so commands such as ps and ++btc will display the original task. ++You can change this behaviour by using ++.I handlers show ++to display the MCA/INIT handlers instead of the original tasks or use ++.I handlers hide ++(the default) to hide the MCA/INIT handlers and display the original ++tasks. ++.I handlers status ++will list the address of the handler task and the original task for ++each cpu. ++\fBNote:\fR\ If the original task was running in user space or it ++failed any of the MCA/INIT verification tests then there is no original ++task to display. ++In this case, the handler will be displayed even if ++.I handlers hide ++is set and ++.I handlers status ++will not show an original task. ++.TP 8 ++id ++Disassemble instructions starting at an address. ++Environment variable IDCOUNT controls how many lines of disassembly ++output the command produces. ++.TP 8 ++kill ++Internal command to send a signal (like kill(1)) to a process. ++kill -signal pid. ++.TP 8 ++lsmod ++Internal command to list modules. ++This does not use any kernel nor user space services so can be used at any time. ++.TP 8 ++per_cpu [] [] ++Display the values of a per_cpu variable, the variable_name is ++specified without the \fIper_cpu__\fR prefix. ++Length is the length of the variable, 1-8, if omitted or 0 it defaults ++to the size of the machine's register. ++To display the variable on a specific cpu, the third parameter is the ++cpu number. ++When the third parameter is omitted, the variable's value is printed ++from all cpus, except that zero values are suppressed. ++For each cpu, per_cpu prints the cpu number, the address of the ++variable and its value. ++.TP 8 ++pid ++Change the current process context, with no parameters it displays the ++current process. ++The current process is used to display registers, both kernel and user ++space. ++It is also used when dumping user pages. ++.I pid R ++resets to the original process that was running when kdb was entered. ++This command is useful if you have been looking at other processes and/or ++cpus and you want to get back to the original process. ++It does not switch cpus, it only resets the context to the original process. ++.TP 8 ++reboot ++Reboot the system, with no attempt to do a clean close down. ++.TP 8 ++rq ++Display the runqueues for the specified cpu. ++.TP 8 ++rqa ++Display the runqueues for all cpus. ++.TP 8 ++stackdepth ++Print the stack usage for processes using more than the specified ++percentage of their stack. ++If percentage is not supplied, it defaults to 60. ++This command is only implemented on i386 and ia64 architectures, ++patches for other architectures will be gratefully accepted. ++.TP 8 ++summary ++Print a summary of the system, including the time (no timezone is ++applied), uname information and various critical system counters. ++.SH INITIAL KDB COMMANDS ++kdb/kdb_cmds is a plain text file where you can define kdb commands ++which are to be issued during kdb_init(). One command per line, blank ++lines are ignored, lines starting with '#' are ignored. kdb_cmds is ++intended for per user customization of kdb, you can use it to set ++environment variables to suit your hardware or to set standard ++breakpoints for the problem you are debugging. This file is converted ++to a small C object, compiled and linked into the kernel. You must ++rebuild and reinstall the kernel after changing kdb_cmds. This file ++will never be shipped with any useful data so you can always override ++it with your local copy. Sample kdb_cmds: ++.P ++.nf ++# Initial commands for kdb, alter to suit your needs. ++# These commands are executed in kdb_init() context, no SMP, no ++# processes. Commands that require process data (including stack or ++# registers) are not reliable this early. set and bp commands should ++# be safe. Global breakpoint commands affect each cpu as it is booted. ++ ++set LINES=50 ++set MDCOUNT=25 ++set RECURSE=1 ++bp sys_init_module ++.fi ++.SH INTERRUPTS AND KDB ++When a kdb event occurs, one cpu (the initial cpu) enters kdb state. ++It uses a cross system interrupt to interrupt the ++other cpus and bring them all into kdb state. All cpus run with ++interrupts disabled while they are inside kdb, this prevents most ++external events from disturbing the kernel while kdb is running. ++.B Note: ++Disabled interrupts means that any I/O that relies on interrupts cannot ++proceed while kdb is in control, devices can time out. The clock tick ++is also disabled, machines will lose track of time while they are ++inside kdb. ++.P ++Even with interrupts disabled, some non-maskable interrupt events will ++still occur, these can disturb the kernel while you are debugging it. ++The initial cpu will still accept NMI events, assuming that kdb was not ++entered for an NMI event. Any cpu where you use the SS or SSB commands ++will accept NMI events, even after the instruction has finished and the ++cpu is back in kdb. This is an unavoidable side effect of the fact that ++doing SS[B] requires the cpu to drop all the way out of kdb, including ++exiting from the event that brought the cpu into kdb. Under normal ++circumstances the only NMI event is for the NMI oopser and that is kdb ++aware so it does not disturb the kernel while kdb is running. ++.P ++Sometimes doing SS or SSB on ix86 will allow one interrupt to proceed, ++even though the cpu is disabled for interrupts. I have not been able ++to track this one down but I suspect that the interrupt was pending ++when kdb was entered and it runs when kdb exits through IRET even ++though the popped flags are marked as cli(). If any ix86 hardware ++expert can shed some light on this problem, please notify the kdb ++maintainer. ++.SH RECOVERING FROM KDB ERRORS ++If a kdb command breaks and kdb has enough of a recovery environment ++then kdb will abort the command and drop back into mainline kdb code. ++This means that user written kdb commands can follow bad pointers ++without killing kdb. Ideally all code should verify that data areas ++are valid (using kdb_getarea) before accessing it but lots of calls to ++kdb_getarea can be clumsy. ++.P ++The sparc64 port does not currently provide this error recovery. ++If someone would volunteer to write the necessary longjmp/setjmp ++code, their efforts would be greatly appreciated. In the ++meantime, it is possible for kdb to trigger a panic by accessing ++a bad address. ++.SH DEBUGGING THE DEBUGGER ++kdb has limited support for debugging problems within kdb. If you ++suspect that kdb is failing, you can set environment variable KDBDEBUG ++to a bit pattern which will activate kdb_printf statements within kdb. ++See include/linux/kdb.h, KDB_DEBUG_FLAG_xxx defines. For example ++.nf ++ set KDBDEBUG=0x60 ++.fi ++activates the event callbacks into kdb plus state tracing in sections ++of kdb. ++.nf ++ set KDBDEBUG=0x18 ++.fi ++gives lots of tracing as kdb tries to decode the process stack. ++.P ++You can also perform one level of recursion in kdb. If environment ++variable RECURSE is not set or is 0 then kdb will either recover from ++an error (if the recovery environment is satisfactory) or kdb will ++allow the error to percolate, usually resulting in a dead system. When ++RECURSE is 1 then kdb will recover from an error or, if there is no ++satisfactory recovery environment, it will drop into kdb state to let ++you diagnose the problem. When RECURSE is 2 then all errors drop into ++kdb state, kdb does not attempt recovery first. Errors while in ++recursive state all drop through, kdb does not even attempt to recover ++from recursive errors. ++.SH KEYBOARD EDITING ++kdb supports a command history, which can be accessed via keyboard ++sequences. ++It supports the special keys on PC keyboards, control characters and ++vt100 sequences on a serial console or a PC keyboard. ++.P ++.DS ++.TS ++box, center; ++l | l | l l | l ++l | l | l l | l. ++PC Special keys Control VT100 key Codes Action ++_ ++Backspace ctrl-H Backspace 0x7f Delete character to the left of the cursor ++Delete ctrl-D Delete \\e[3~ Delete character to the right of the cursor ++Home ctrl-A Home \\e[1~ Go to start of line ++End ctrl-E End \\e[4~ Go to end of line ++Up arrow ctrl-P Up arrow \\e[A Up one command in history ++Down arrow ctrl-N Down arrow \\e[B Down one command in history ++Left arrow ctrl-B Left arrow \\e[D Left one character in current command ++Right arrow ctrl-F Right arrow \\e[C Right one character in current command ++.TE ++.DE ++.P ++There is no toggle for insert/replace mode, kdb editing is always in ++insert mode. ++Use delete and backspace to delete characters. ++.P ++kdb also supports tab completion for kernel symbols ++Type the start of a kernel symbol and press tab (ctrl-I) to complete ++the name ++If there is more than one possible match, kdb will append any common ++characters and wait for more input, pressing tab a second time will ++display the possible matches ++The number of matches is limited by environment variable DTABCOUNT, ++with a default of 30 if that variable is not set. ++.SH AUTHORS ++Scott Lurndal, Richard Bass, Scott Foehner, Srinivasa Thirumalachar, ++Masahiro Adegawa, Marc Esipovich, Ted Kline, Steve Lord, Andi Kleen, ++Sonic Zhang. ++.br ++Keith Owens - kdb maintainer. ++.SH SEE ALSO ++.P ++linux/Documentation/kdb/kdb_{bp,bt,env,ll,md,ps,rd,sr,ss}.man +--- /dev/null ++++ b/Documentation/kdb/kdb_bp.man +@@ -0,0 +1,197 @@ ++.TH BD 1 "July 12, 2004" ++.SH NAME ++bp, bpa, bph, bpha, bd, bc, be, bl \- breakpoint commands ++.SH SYNOPSIS ++bp \fIaddress-expression\fP ++.LP ++bpa \fIaddress-expression\fP ++.LP ++bph \fIaddress-expression\fP [\f(CWDATAR|DATAW|DATAA|IO\fP [\fIlength\fP]] ++.LP ++bpha \fIaddress-expression\fP [\f(CWDATAR|DATAW|DATAA|IO\fP [\fIlength\fP]] ++.LP ++bd \fIbreakpoint-number\fP ++.LP ++bc \fIbreakpoint-number\fP ++.LP ++be \fIbreakpoint-number\fP ++.LP ++bl ++.SH DESCRIPTION ++.hy 0 ++The ++.B bp ++family of commands are used to establish a breakpoint. ++The \fIaddress-expression\fP may be a numeric value (decimal or ++hexidecimal), a symbol name, a register name preceeded by a ++percent symbol '%', or a simple expression consisting of a ++symbol name, an addition or subtraction character and a numeric ++value (decimal or hexidecimal). ++.P ++\fBbph\fP and \fBbpha\fP will force the use of a hardware register, provided ++the processor architecture supports them. ++.P ++The \fIaddress-expression\fP may also consist of a single ++asterisk '*' symbol which indicates that the command should ++operate on all existing breakpoints (valid only for \fBbc\fP, ++\fBbd\fP and \fBbe\fP). ++.P ++Four different types of ++breakpoints may be set: ++ ++.TP 8 ++Instruction ++Causes the kernel debugger to be invoked from the debug exception ++path when an instruction is fetched from the specified address. This ++is the default if no other type of breakpoint is requested or when ++the \fBbp\fP command is used. ++ ++.TP 8 ++DATAR ++Causes the kernel debugger to be entered when data of length ++\fIlength\fP is read from or written to the specified address. ++This type of breakpoint must use a processor debug register which ++places an architecture dependent limit on the number of data and I/O ++breakpoints that may be established. On arm mode XScale platform ++(thumb mode is not supported yet), ++debugger is triggered by reading from the specified address. ++The \fBbph\fP or \fBbpha\fP commands must be used. ++ ++.TP 8 ++DATAW ++Enters the kernel debugger when data of length \fIlength\fP ++is written to the specified address. \fIlength\fP defaults ++to four bytes if it is not explicitly specified. ++Note that the processor may have already overwritten the prior data at ++the breakpoint location before the kernel debugger is invoked. ++The prior data should be saved before establishing the breakpoint, if ++required. On arm mode XScale platform, the debugger is triggered ++after having overwritten the specified address. ++The \fBbph\fP or \fBbpha\fP commands must be used. ++ ++.TP 8 ++IO ++Enters the kernel debugger when an \fBin\fP or \fBout\fP instruction ++targets the specified I/O address. The \fBbph\fP or \fBbpha\fP ++commands must be used. This type of breakpoint is not valid in ++arm mode XScale platform. This option is not valid in arm ++mode XScale platform. ++ ++.TP 8 ++DATAA ++Enters the kernel debugger after the data in specified address has ++been accessed (read or write), this option is only used in arm ++mode XScale platform. ++ ++.P ++The ++.B bpha ++command will establish a breakpoint on all processors in an ++SMP system. This command is not available in an uniprocessor ++kernel. ++.P ++The ++.B bd ++command will disable a breakpoint without removing it from the kernel ++debugger's breakpoint table. ++This can be used to keep breakpoints in the table without exceeding the ++architecture limit on breakpoint registers. ++A breakpoint-number of \fI*\fR will disable all break points. ++.P ++The ++.B be ++command will re-enable a disabled breakpoint. ++A breakpoint-number of \fI*\fR will enable all break points. ++.P ++The ++.B bc ++command will clear a breakpoint from the breakpoint table. ++A breakpoint-number of \fI*\fR will clear all break points. ++.P ++The ++.B bl ++command will list the existing set of breakpoints. ++.SH LIMITATIONS ++There is a compile time limit of sixteen entries in the ++breakpoint table at any one time. ++.P ++There are architecture dependent limits on the number of hardware ++breakpoints that can be set. ++.IP ix86 8 ++Four. ++.PD 0 ++.IP xscale 8 ++Two for insruction breakpoints and another two for data breakpoint. ++.PD 0 ++.IP ia64 8 ++? ++.PD 0 ++.IP sparc64 8 ++None. ++.PD 1 ++When issuing the "go" command after entering the debugger due to ++a breakpoint, kdb will silently perform a single step in order to ++reapply the breakpoint. The sparc64 port has some limitations on ++single stepping, which may limit where a breakpoint may be safely ++set. Please read the man page for \fBss\fP for more information. ++.SH ENVIRONMENT ++The breakpoint subsystem does not currently use any environment ++variables. ++.SH SMP CONSIDERATIONS ++Using ++.B bc ++is risky on SMP systems. ++If you clear a breakpoint when another cpu has hit that breakpoint but ++has not been processed then it may not be recognised as a kdb ++breakpoint, usually resulting in incorrect program counters and kernel ++panics. ++It is safer to disable the breakpoint with ++.BR bd , ++then ++.B go ++to let any other processors that are waiting on the breakpoint to ++clear. ++After all processors are clear of the disabled breakpoint then it is ++safe to clear it using ++.BR bc . ++.P ++Breakpoints which use the processor breakpoint registers ++are only established on the processor which is ++currently active. If you wish breakpoints to be universal ++use the ++.B bpa ++or ++.B bpha ++commands. ++.SH EXAMPLES ++.TP 8 ++bp schedule ++Sets an instruction breakpoint at the begining of the ++function \fBschedule\fP. ++ ++.TP 8 ++bp schedule+0x12e ++Sets an instruction breakpoint at the instruction located ++at \fBschedule\fP+\fI0x12e\fP. ++ ++.TP 8 ++bph ttybuffer+0x24 dataw ++Sets a data write breakpoint at the location referenced by ++\fBttybuffer\fP+\fI0x24\fP for a length of four bytes. ++ ++.TP 8 ++bph 0xc0254010 datar 1 ++Establishes a data reference breakpoint at address \fB0xc0254010\fP ++for a length of one byte. ++ ++.TP 8 ++bp ++List current breakpoint table. ++ ++.TP 8 ++bd 0 ++Disable breakpoint #0. ++ ++.TP 8 ++bc * ++Clear all breakpoints +--- /dev/null ++++ b/Documentation/kdb/kdb_bt.man +@@ -0,0 +1,315 @@ ++.TH BT 1 "July 20, 2007" ++.SH NAME ++bt \- Stack Traceback command ++.SH SYNOPSIS ++bt [ ] ++.LP ++btp ++.LP ++btt ++.LP ++bta [ DRSTZUIMA ] ++.LP ++btc [] ++.SH DESCRIPTION ++.hy 0 ++The ++.B bt ++command is used to print a stack traceback. It uses the ++current registers (see \fBrd\fP command) to determine ++the starting context and attempts to provide a complete ++stack traceback for the active thread. If \fIstack-frame-address\fP ++is supplied, it is assumed to point to the start of a valid ++stack frame and the stack will be traced back from that ++point. ++On x86 architecture, \fIstack-frame-address\fP must be the stack address of a ++saved \fB%eip\fP (\fB%rip\fP for x86_64) value from a \fBcall\fP instruction. ++.P ++The \fBbtp\fP command will analyze the stack for the given ++process identification (see the \fBps\fP command). ++\fBbtp\fP sets the current process for any following register display or update ++commands. ++.P ++The \fBbtt\fP command will analyze the stack for the given task ++structure. ++It is exactly equivalent to \fBbtp\fR on the pid extracted from the ++task structure. ++\fBbtt\fP sets the current process for any following register display or update ++commands. ++.P ++The \fBbta\fP command lists the stack for all processes in the desired ++state. ++Without any parameters, \fBbta\fP gives a backtrace for all useful processes. ++If a parameter is specified, it is a single string consisting of the ++letters D, R, S, T, Z, U, I, M and A in any order. ++See the kdb \fBps\fR man page for more details. ++\fBbta\fP does not change the current process. ++.P ++The \fBbtc\fP command will analyze the stack for the current process on ++a specified cpu or, if no cpu number is supplied, for the current ++process on all cpus. ++It does not switch to the other cpus, instead it uses the task ++structures to identify and issue \fBbtt\fR against the current task on ++the desired cpus. ++\fBbtc\fP with no arguments does not change the current process. ++\fBbtc\fP with a cpu number sets the current process for any following register ++display or update commands. ++.P ++For each function, the stack trace prints at least two lines. ++The first line contains four or five fields\ :- ++.IP * 3 ++The pointer to the stack frame. ++.PD 0 ++.IP * 3 ++The current address within this frame. ++.IP * 3 ++The address converted to a function name (actually the first non-local ++label which is <= the address). ++.IP * 3 ++The offset of the address within the function. ++.IP * 3 ++Any parameters to the function. ++.PD 1 ++.PP ++If environment variable NOSECT is set to 0 then the next line contains ++five fields which are designed to make it easier to match the trace ++against the kernel code\ :- ++.IP * 3 ++The module name that contains the address, "kernel" if it is in the ++base kernel. ++.PD 0 ++.IP * 3 ++The section name that contains the address (not available on 2.6 kernels). ++.IP * 3 ++The start address of the section (not available on 2.6 kernels). ++.IP * 3 ++The start address of the function. ++.IP * 3 ++The end address of the function (the first non-local label which is > ++the address). ++.PD 1 ++.PP ++If arguments are being converted to symbols, any argument which ++converts to a kernel or module address is printed as\ :- ++.IP * 3 ++Argument address. ++.PD 0 ++.IP * 3 ++The module name that contains the address, "kernel" if it is in the ++base kernel. ++.IP * 3 ++The symbol name the argument maps to. ++.IP * 3 ++The offset of the argument from the symbol, suppressed if 0. ++.PD 1 ++.P ++On architectures that use nested stacks, the backtrace will indicate a ++switch to a new stack by printing a line of equal signs and the type of ++stack. ++.SH MATCHING TRACE TO KERNEL CODE ++The command "objdump\ -S" will disassemble an object and, if the code ++was compiled with debugging (gcc flag -g), objdump will interleave the ++C source lines with the generated object. ++.PP ++A complete objdump of the kernel or a module is too big, normally you ++only want specific functions. ++By default objdump will only print the .text section but Linux uses ++other section names for executable code. ++When objdump prints relocatable objects (modules) it uses an offset of ++0 which is awkward to relate to the stack trace. ++The five fields which are printed for each function are designed to ++make it easier to match the stack trace against the kernel code using ++"objdump\ -S". ++.PP ++If the function is in the kernel then you need the section name, the ++start and end address of the function. The command is ++.PP ++.nf ++ objdump -S -j \\ ++ --start-address= \\ ++ --stop-address= \\ ++ /usr/src/linux/vmlinux ++.fi ++.PP ++If the function is in a module then you need the section name, the ++start address of the section, the start and end address of the ++function, the module name. The command is ++.PP ++.nf ++ objdump -S -j \\ ++ --adjust-vma= \\ ++ --start-address= \\ ++ --stop-address= \\ ++ /path/to/module/.o ++.fi ++.PP ++Unfortunately the 2.6 kernel does not provide the information required ++to locate the start of the section, which makes it very difficult to ++perform a reliable objdump on a module. ++.PP ++All addresses to objdump must be preceded by '0x' if they are in hex, ++objdump does not assume hex. ++The stack trace values are printed with leading '0x' to make it easy to ++run objdump. ++.SH LIMITATIONS ++Some architectures pass parameters in registers; ia64, x86_64 and i386 (with ++gcc flag -mregparm) fall into this category. ++On these architectures, the compiler may reuse input parameter registers as ++scratch space. ++For example, if a function takes a pointer to a structure and only accesses one ++field in that structure, the compiler may calculate the address of the field by ++adding a value to the input register. ++Once the input register has been updated, it no longer points to the ++start of the structure, but to some field within it. ++This also occurs with array pointers, the compiler may update the input pointer ++directly, leaving it pointing to some element of the array instead of the start ++of the array. ++Always treat parameter values that have been passed in registers with extreme ++suspicion, the compiler may have changed the value. ++The x86 backtrace can generally identify register parameters that are no longer ++valid, it prints them as 'invalid' instead of as a misleading number. ++The ia64 backtrace cannot identify parameter registers that have been ++overwritten. ++.P ++x86 architectures do not have full unwind information in the kernel. ++The KDB backtrace on x86 performs code decomposition and analysis to track the ++frames on the call stack (including stack switches) and to locate parameters. ++if this code analysis does not yield a valid result, KDB falls back on the old ++method of scanning the process stack and printing anything that looks like a ++kernel address. ++This old method is unreliable (it produces lots of false positives in the ++trace) and cannot track parameters at all, so no parameters are printed. ++If you get an x86 backtrace that falls back to the old method, read ++Documentation/kdb/bt_x86 and follow the steps listed to get diagnostics and to ++submit a bug report. ++.P ++There are a lot of functions in the kernel which take some arguments then do ++nothing except call another function with the same initial arguments, sometimes ++adding parameters at the end. For example\ :- ++.nf ++.na ++.ft CW ++ ++int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen, ++ void __user *oldval, size_t __user *oldlenp, ++ void __user *newval, size_t newlen) ++{ ++ int ret = devinet_conf_sysctl(table, name, nlen, oldval, oldlenp, ++ newval, newlen); ++ ++ if (ret == 1) ++ rt_cache_flush(0); ++ ++ return ret; ++} ++.ad b ++.fi ++.P ++ipv4_doint_and_flush_strategy() passes all its parameters directly to ++devinet_conf_sysctl() and makes no other use of those parameters, ++so ipv4_doint_and_flush_strategy is a 'pass through' function. ++The x86_64 calling sequence mandates that the first 6 parameters are passed in ++registers, with other parameters being passed on stack. ++The i386 calling sequence with -mregparm=3 (which is the default since about ++2.6.18) passes the first 3 parameters in registers, with other parameters being ++passed on stack. ++The only exceptions to the above calling sequence are for functions declared as ++asmlinkage or functions with a variable number of parameters (e.g. printk). ++.P ++When a pass through function calls another function, the first 3 (i386) or 6 ++(x86) parameters are already in their correct registers so the pass through ++function does not need to access the registers, which means that there are no ++references to these registers in the assembler code for the function. ++Users still want to see those arguments so the x86 backtrace has to assume that ++if\ :- ++.IP * 2 ++There are parameters passed on the stack and ++.IP * ++There are no code references to parameters passed in registers and ++.IP * ++The function is not a known asmlinkage or variadic function, then ++there are pass through register arguments. ++.P ++The x86 backtrace will warn you when it makes this assumption, like this\ :- ++.nf ++.na ++.ft CW ++ ++ has memory parameters but no register parameters. ++ Assuming it is a 'pass through' function that does not refer to its register ++ parameters and setting register parameters ++.ad b ++.fi ++.P ++The above 3 line message is only printed once, any future assumptions will ++print a shorter message. ++.P ++The \fBbt\fP command may print more or less arguments for a function ++than that function accepts. ++For x86, trailing arguments that are passed in but not used by the function ++will not be printed, resulting in fewer arguments than expected. ++For ia64, the hardware does not distinguish between input and local registers, ++some local registers may be printed as function arguments, resulting in more ++arguments than expected. ++.P ++On i386, 64 bit arguments (long long) occupy two adjacent 32 bit fields. ++There is no way for KDB to tell that this has occurred, so 64 bit arguments ++will be printed as two separate 32 bit arguments. ++.SH ENVIRONMENT ++The \fBBTARGS\fP environment variable governs the maximum number ++of arguments that are printed for any single function. ++On IA64 hardware, there is no difference between input and local registers, the ++first \fBBTARGS\fP registers are printed, up to the total limit of input plus ++local registers. ++Use a large value for \fBBTARGS\fP if you want to see the local registers on ++IA64. ++.PP ++If the \fBBTSP\fP environment variable is non-zero then the entire backtrace is ++printed, otherwise only the backtrace to the point of the last interrupt is ++printed. ++Printing the entire backtrace with 'set\ BTSP\ 1' is useful for diagnosing ++problems with the backtrace algorithms. ++In addition, when BTSP is non-zero, each backtrace frame may print extra lines ++giving information about the stack pointers, this is architecture specific. ++.PP ++If the \fBBTSYMARG\fP environment variable is non-zero then any ++arguments that fall within the kernel or modules are converted to symbols. ++.PP ++If the \fBNOSECT\fP environment variable is non-zero then the ++section information is suppressed. ++The default is NOSECT=1 so section data is suppressed; use set\ NOSECT=0 ++to see section information. ++.PP ++The \fBBTAPROMPT\fP environment variable controls the prompt after each ++process is listed by the \fBbta\fP command. If \fBBTAPROMPT\fP is not ++set or is non-zero then \fBbta\fP issues a prompt after each process is ++listed. If \fBBTAPROMPT\fP is set to zero then no prompt is issued and ++all processes are listed without human intervention. ++.PP ++\fBbt\fR with no parameters uses the \fBPS\fR environment variable, see ++the kdb \fBps\fR man page. ++.SH SMP CONSIDERATIONS ++None. ++.SH EXAMPLES ++.nf ++.na ++.ft CW ++[0]kdb> bt ++Stack traceback for pid 2873 ++0xc2efc0f0 2873 2836 1 0 R 0xc2efc2a0 *mount ++esp eip Function (args) ++0xf65a3c88 0xc0201f9f xfs_mount_validate_sb (0xf68bcb08, 0xf68bcb48, 0x0) ++0xf65a3c94 0xc0202f17 xfs_readsb+0x9d (0xf68bcb08, 0x0) ++0xf65a3cc0 0xc020a72e xfs_mount+0x21d (invalid, 0xf68bc2f0, 0x0) ++0xf65a3cf4 0xc021a84a vfs_mount+0x1a (invalid) ++0xf65a3d04 0xc021a721 xfs_fs_fill_super+0x76 (0xf76b6200, invalid, invalid) ++0xf65a3d78 0xc015ad81 get_sb_bdev+0xd4 (invalid, invalid, invalid, 0xf7257000, 0xc021a6ab, 0xf7594b38) ++ xfs_fs_get_sb has memory parameters but no register parameters. ++ Assuming it is a 'pass through' function that does not refer to its register ++ parameters and setting 3 register parameters ++0xf65a3db4 0xc0219a3a xfs_fs_get_sb+0x21 (invalid, invalid, invalid, 0xf7257000, 0xf7594b38) ++0xf65a3dcc 0xc015a992 vfs_kern_mount+0x41 (0xc04847e0, 0x0, 0xf68e9000, 0xf7257000) ++0xf65a3df0 0xc015aa11 do_kern_mount+0x38 (0xf6818000, 0x0, 0xf68e9000, 0xf7257000) ++0xf65a3e10 0xc016c8b0 do_mount+0x5df (0xf68e9000, 0xf65d6000, 0xf6818000, 0xc0ed0000, 0xf7257000) ++0xf65a3f90 0xc016c996 sys_mount+0x6f (0x8069b50, 0x8069b60, 0x8069b70, 0xc0ed0000, 0x8069ba0) ++0xf65a3fb4 0xc0102646 sysenter_past_esp+0x5f (invalid, invalid, invalid, 0x73, 0x246, 0xbfe52f50) +--- /dev/null ++++ b/Documentation/kdb/kdb_env.man +@@ -0,0 +1,46 @@ ++.TH ENV 1 "24 September 2000" ++.SH NAME ++env, set \- Environment manipulation commands ++.SH SYNOPSIS ++env ++.LP ++set \fIenvironment-variable\fP=\fIvalue\fP ++.SH DESCRIPTION ++The kernel debugger contains an environment which contains a series ++of name-value pairs. Some environment variables are known to the ++various kernel debugger commands and have specific meaning to the ++command; such are enumerated on the respective reference material. ++.P ++Arbitrary environment variables may be created and used with ++many commands (those which require an \fIaddress-expression\fP). ++.P ++The ++.B env ++command is used to display the current environment. ++.P ++The ++.B set ++command is used to alter an existing environment variable or ++establish a new environment variable. ++.SH LIMITATIONS ++There is a compile-time limit of 33 environment variables. ++.P ++There is a compile-time limit of 512 bytes (\fBKDB_ENVBUFSIZE\fP) ++of heap space available for new environment variables and for ++environment variables changed from their compile-time values. ++.SH ENVIRONMENT ++These commands explicitly manipulate the environment. ++.SH SMP CONSIDERATIONS ++None. ++.SH USER SETTINGS ++You can include "set" commands in kdb/kdb_cmds (see kdb.mm) to define ++your environment variables at kernel startup. ++.SH EXAMPLES ++.TP 8 ++env ++Display current environment settings. ++ ++.TP 8 ++set IDCOUNT=100 ++Set the number of lines to display for the \fBid\fP command ++to the value \fI100\fP. +--- /dev/null ++++ b/Documentation/kdb/kdb_ll.man +@@ -0,0 +1,134 @@ ++.TH LL 1 "19 April 1999" ++.SH NAME ++ll \- Linked List examination ++.SH SYNOPSIS ++ll ++.SH DESCRIPTION ++The ++.B ll ++command is used to execute a single command repetitively for ++each element of a linked list. ++.P ++The command specified by will be executed with a single ++argument, the address of the current element. ++.SH LIMITATIONS ++Be careful if using this command recursively. ++.SH ENVIRONMENT ++None. ++.SH SMP CONSIDERATIONS ++None. ++.SH EXAMPLES ++.nf ++.na ++.ft CW ++# cd modules ++# insmod kdbm_vm.o ++# Entering kdb on processor 0 due to PAUSE ++kdb> ps ++Task Addr Pid Parent cpu lcpu Tss Command ++0xc03de000 0000000001 0000000000 0000 0000 0xc03de2d4 init ++0xc0090000 0000000002 0000000001 0000 0000 0xc00902d4 kflushd ++0xc000e000 0000000003 0000000001 0000 0000 0xc000e2d4 kpiod ++0xc000c000 0000000004 0000000001 0000 0000 0xc000c2d4 kswapd ++0xc7de2000 0000000056 0000000001 0000 0000 0xc7de22d4 kerneld ++0xc7d3a000 0000000179 0000000001 0000 0000 0xc7d3a2d4 syslogd ++0xc7a7e000 0000000188 0000000001 0000 0000 0xc7a7e2d4 klogd ++0xc7a04000 0000000199 0000000001 0000 0000 0xc7a042d4 atd ++0xc7b84000 0000000210 0000000001 0000 0000 0xc7b842d4 crond ++0xc79d6000 0000000221 0000000001 0000 0000 0xc79d62d4 portmap ++0xc798e000 0000000232 0000000001 0000 0000 0xc798e2d4 snmpd ++0xc7904000 0000000244 0000000001 0000 0000 0xc79042d4 inetd ++0xc78fc000 0000000255 0000000001 0000 0000 0xc78fc2d4 lpd ++0xc77ec000 0000000270 0000000001 0000 0000 0xc77ec2d4 sendmail ++0xc77b8000 0000000282 0000000001 0000 0000 0xc77b82d4 gpm ++0xc7716000 0000000300 0000000001 0000 0000 0xc77162d4 smbd ++0xc7ee2000 0000000322 0000000001 0000 0000 0xc7ee22d4 mingetty ++0xc7d6e000 0000000323 0000000001 0000 0000 0xc7d6e2d4 login ++0xc778c000 0000000324 0000000001 0000 0000 0xc778c2d4 mingetty ++0xc78b6000 0000000325 0000000001 0000 0000 0xc78b62d4 mingetty ++0xc77e8000 0000000326 0000000001 0000 0000 0xc77e82d4 mingetty ++0xc7708000 0000000327 0000000001 0000 0000 0xc77082d4 mingetty ++0xc770e000 0000000328 0000000001 0000 0000 0xc770e2d4 mingetty ++0xc76b0000 0000000330 0000000001 0000 0000 0xc76b02d4 update ++0xc7592000 0000000331 0000000323 0000 0000 0xc75922d4 ksh ++0xc7546000 0000000338 0000000331 0000 0000 0xc75462d4 su ++0xc74dc000 0000000339 0000000338 0000 0000 0xc74dc2d4 ksh ++kdb> md 0xc74dc2d4 ++c74dc2d4: 00000000 c74de000 00000018 00000000 .....`MG........ ++c74dc2e4: 00000000 00000000 00000000 074de000 .............`M. ++c74dc2f4: c01123ff 00000000 00000000 00000000 #.@............ ++c74dc304: 00000000 00000000 c74dded0 00000000 ........P^MG.... ++[omitted] ++c74dc474: 00000000 00000000 00000000 00000000 ................ ++c74dc484: 00000000 c7c15d00 c77b0900 c026fbe0 .....]AG..{G`{&@ ++c74dc494: 00000000 c76c2000 00000000 00000000 ..... lG........ ++c74dc4a4: 00000000 00000000 00000000 c74dc4ac ............,DMG ++kdb> md 0xc026fbe0 ++c026fbe0: c0262b60 00000000 c7594940 c74de000 @HYG....@IYG.`MG ++[omitted] ++kdb> md 0xc0262b60 ++c0262b60: c0266660 08048000 0804c000 c7bec360 `f&@.....@..`C>G ++kdb> ll c0262b60 12 md ++c0262b60: c0266660 08048000 0804c000 c7bec360 `f&@.....@..`C>G ++c7bec360: c0266660 0804c000 0804d000 c7becb20 `f&@.@...P.. K>G ++c7becb20: c0266660 0804d000 08050000 c7bec3a0 `f&@.P...... C>G ++c7bec3a0: c0266660 40000000 40009000 c7bec420 `f&@...@...@ D>G ++c7bec420: c0266660 40009000 4000b000 c7bec4a0 `f&@...@.0.@ D>G ++c7bec4a0: c0266660 4000b000 40010000 c7bec8e0 `f&@.0.@...@`H>G ++c7bec8e0: c0266660 40010000 400a1000 c7becbe0 `f&@...@...@`K>G ++c7becbe0: c0266660 400a1000 400a8000 c7becc60 `f&@...@...@`L>G ++c7becc60: c0266660 400a8000 400b4000 c7952300 `f&@...@.@.@.#.G ++c7952300: c0266660 400b5000 400bc000 c79521c0 `f&@.P.@.@.@@!.G ++c79521c0: c0266660 400bc000 400bd000 c7bec6e0 `f&@.@.@.P.@`F>G ++c7bec6e0: c0266660 bffff000 c0000000 00000000 `f&@.p?...@.... ++kdb> ++kdb> ll c0262b60 12 vm ++struct vm_area_struct at 0xc0262b60 for 56 bytes ++vm_start = 0x8048000 vm_end = 0x804c000 ++page_prot = 0x25 avl_height = 2244 vm_offset = 0x0 ++flags: READ EXEC MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE ++struct vm_area_struct at 0xc7bec360 for 56 bytes ++vm_start = 0x804c000 vm_end = 0x804d000 ++page_prot = 0x25 avl_height = -31808 vm_offset = 0x3000 ++flags: READ WRITE MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE ++struct vm_area_struct at 0xc7becb20 for 56 bytes ++vm_start = 0x804d000 vm_end = 0x8050000 ++page_prot = 0x25 avl_height = -28664 vm_offset = 0x0 ++flags: READ WRITE EXEC MAYREAD MAYWRITE MAYEXEC ++struct vm_area_struct at 0xc7bec3a0 for 56 bytes ++vm_start = 0x40000000 vm_end = 0x40009000 ++page_prot = 0x25 avl_height = 30126 vm_offset = 0x0 ++flags: READ EXEC MAYREAD MAYWRITE MAYEXEC DENYWRITE ++struct vm_area_struct at 0xc7bec420 for 56 bytes ++vm_start = 0x40009000 vm_end = 0x4000b000 ++page_prot = 0x25 avl_height = 30126 vm_offset = 0x8000 ++flags: READ WRITE MAYREAD MAYWRITE MAYEXEC DENYWRITE ++struct vm_area_struct at 0xc7bec4a0 for 56 bytes ++vm_start = 0x4000b000 vm_end = 0x40010000 ++page_prot = 0x25 avl_height = 26853 vm_offset = 0x0 ++flags: READ MAYREAD MAYWRITE MAYEXEC ++struct vm_area_struct at 0xc7bec8e0 for 56 bytes ++vm_start = 0x40010000 vm_end = 0x400a1000 ++page_prot = 0x25 avl_height = 2244 vm_offset = 0x0 ++flags: READ EXEC MAYREAD MAYWRITE MAYEXEC ++struct vm_area_struct at 0xc7becbe0 for 56 bytes ++vm_start = 0x400a1000 vm_end = 0x400a8000 ++page_prot = 0x25 avl_height = 30126 vm_offset = 0x90000 ++flags: READ WRITE MAYREAD MAYWRITE MAYEXEC ++struct vm_area_struct at 0xc7becc60 for 56 bytes ++vm_start = 0x400a8000 vm_end = 0x400b4000 ++page_prot = 0x25 avl_height = 2244 vm_offset = 0x0 ++flags: READ WRITE MAYREAD MAYWRITE MAYEXEC ++struct vm_area_struct at 0xc7952300 for 56 bytes ++vm_start = 0x400b5000 vm_end = 0x400bc000 ++page_prot = 0x25 avl_height = 30126 vm_offset = 0x0 ++flags: READ EXEC MAYREAD MAYWRITE MAYEXEC ++struct vm_area_struct at 0xc79521c0 for 56 bytes ++vm_start = 0x400bc000 vm_end = 0x400bd000 ++page_prot = 0x25 avl_height = -16344 vm_offset = 0x6000 ++flags: READ WRITE MAYREAD MAYWRITE MAYEXEC ++struct vm_area_struct at 0xc7bec6e0 for 56 bytes ++vm_start = 0xbffff000 vm_end = 0xc0000000 ++page_prot = 0x25 avl_height = 2244 vm_offset = 0x0 ++flags: READ WRITE EXEC MAYREAD MAYWRITE MAYEXEC GROWSDOWN ++kdb> +--- /dev/null ++++ b/Documentation/kdb/kdb_md.man +@@ -0,0 +1,136 @@ ++.TH MD 1 "August 4, 2004" ++.SH NAME ++md, mdWcN, mdr, mds, mm, mmW\- Memory manipulation commands ++.SH SYNOPSIS ++md [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] ++.LP ++md\fIW\fRc\fIn\fR [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] ++.LP ++mdp \fIphysical-address-expression\fP,\fIbytes\fP ++.LP ++mdr \fIaddress-expression\fP,\fIbytes\fP ++.LP ++mds [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] ++.LP ++mm \fIaddress-expression\fP \fInew-contents\fP ++.LP ++mm\fIW\fR \fIaddress-expression\fP \fInew-contents\fP ++.SH DESCRIPTION ++The ++.B md ++command is used to display the contents of memory. ++The \fIaddress-expression\fP may be a numeric value (decimal or ++hexidecimal), a symbol name, a register name preceeded by one or more ++percent symbols '%', an environment variable name preceeded by ++a currency symbol '$', or a simple expression consisting of a ++symbol name, an addition or subtraction character and a numeric ++value (decimal or hexidecimal). ++.P ++If an address is specified and the \fIline-count\fP or \fIradix\fP arguments ++are omitted, they default to the values of the \fBMDCOUNT\fP and \fBRADIX\fP ++environment variables respectively. If the \fBMDCOUNT\fP or \fBRADIX\fP ++environment variables are unset, the appropriate defaults will be used [see ++\fBENVIRONMENT\fP below]. If no address is specified then md resumes ++after the last address printed, using the previous values of count and ++radix. The start address is rounded down to a multiple of the ++BYTESPERWORD (md) or width (md\fIW\fR). ++.P ++md uses the current value of environment variable \fBBYTESPERWORD\fP to ++read the data. When reading hardware registers that require special ++widths, it is more convenient to use md\fIW\fRc\fIn\fR where \fIW\fR is ++the width for this command and \fRc\fIn\fR is the number of entries to ++read. For example, md1c20 reads 20 bytes, 1 at a time. To continue ++printing just type md, the width and count apply to following md ++commands with no parameters. \fBNote:\fR The count is the number of ++repeats of the width, unlike MDCOUNT which gives the number of md lines ++to print. ++.P ++The ++.B mdp ++command displays the contents of physical memory, starting at the ++specified physical address for the specified number of bytes. ++The address is preceded by 'phys'. ++.P ++The ++.B mdr ++command displays the raw contents of memory, starting at the specified ++address for the specified number of bytes. ++The data is printed in one line without a leading address and no ++trailing character conversion. ++.B mdr ++is intended for interfacing with external debuggers, it is of little ++use to humans. ++.P ++The ++.B mds ++command displays the contents of memory one word per line and ++attempts to correlate the contents of each word with a symbol ++in the symbol table. If no symbol is found, the ascii representation ++of the word is printed, otherwise the symbol name and offset from ++symbol value are printed. ++By default the section data is printed for kernel symbols. ++.P ++The ++.B mm ++and ++\fBmm\fIW\fR ++commands allow modification of memory. The bytes at the address ++represented by \fIaddress-expression\fP are changed to ++\fInew-contents\fP. \fInew-contents\fP is allowed to be an ++\fIaddress-expression\fP. ++.B mm ++changes a machine word, \fBmm\fIW\fR changes \fIW\fR bytes at that ++address. ++.SH LIMITATIONS ++None. ++.SH ENVIRONMENT ++.TP 8 ++MDCOUNT ++This environment variable (default=8) defines the number of lines ++that will be displayed by each invocation of the \fBmd\fP command. ++ ++.TP 8 ++RADIX ++This environment variable (default=16) defines the radix used to ++print the memory contents. ++ ++.TP 8 ++BYTESPERWORD ++This environment variable (default=4) selects the width of output ++data when printing memory contents. Select the value two to get ++16-bit word output, select the value one to get byte output. ++ ++.TP 8 ++LINES ++This environment variable governs the number of lines of output ++that will be presented before the kernel debugger built-in pager ++pauses the output. This variable only affects the functioning ++of the \fBmd\fP and \fBmds\fP if the \fBMDCOUNT\fP variable ++is set to a value greater than the \fBLINES\fP variable. ++ ++.TP 8 ++NOSECT ++If the \fBNOSECT\fP environment variable is non-zero then the ++section information is suppressed. ++The default is NOSECT=1 so section data is suppressed; use set\ NOSECT=0 ++to see section information. ++.SH SMP CONSIDERATIONS ++None. ++.SH EXAMPLES ++.TP 8 ++md %edx ++Display memory starting at the address contained in register \fB%edx\fP. ++ ++.TP 8 ++mds %esp ++Display stack contents symbolically. This command is quite useful ++in manual stack traceback. ++ ++.TP 8 ++mm 0xc0252110 0x25 ++Change the memory location at 0xc0252110 to the value 0x25. ++ ++.TP 8 ++md chrdev_table 15 ++Display 15 lines (at 16 bytes per line) starting at address ++represented by the symbol \fIchrdev_table\fP. +--- /dev/null ++++ b/Documentation/kdb/kdb_ps.man +@@ -0,0 +1,96 @@ ++.TH PS 1 "September 14, 2004" ++.SH NAME ++ps \- Display processes ++.SH SYNOPSIS ++ps [ DRSTCZEUIMA ] ++.SH DESCRIPTION ++The ++.B ps ++command displays the status of all processes in the desired state. ++This command does not take any locks (all cpus should be frozen while ++kdb is running) so it can safely be used to debug lock problems with ++the process table. ++.P ++Without any parameters, \fBps\fP displays all the interesting ++processes, excluding idle tasks and sleeping system daemons. ++If a parameter is specified, it is a single string consisting of the ++letters D, R, S, T, C, Z, E, U, I and M, in any order. ++Each letter selects processes in a specific state, when multiple ++letters are specified, a process will be displayed if it is in any of ++the specified states. ++The states are\ :- ++.P ++.DS ++.TS ++box, center; ++l | l ++l | l. ++D Uninterruptible sleep ++R Running ++S Interruptible sleep ++T Stopped ++C Traced ++Z Zombie ++E Dead ++U Unrunnable ++I Idle task ++M Sleeping system daemon ++A All ++.TE ++.DE ++.P ++For state R (running), the process may not be on a cpu at the moment, ++but it is ready to run. ++The header line above the backtrace contains '1' in the fourth field if ++the process is actually on a cpu. ++.P ++The idle task is run on each cpu when there is no work for that cpu to do. ++Unless the idle task is servicing an interrupt, there is no point in ++printing the idle task. ++An idle task that is not servicing a interrupt is marked as state I, ++while servicing an interrupt it is in state R. ++By default, idle tasks are not printed, use \fBps\ I\fR to print them. ++If the idle tasks are not being printed, the start of the \fBps\R ++output contains a list of which cpus are idle. ++.P ++Each cpu has one or more system daemons to handle per cpu work such as ++soft irqs. ++A system daemon (idenified by a NULL mm pointer) that is sleeping is ++marked as state M. ++These processes rarely have any useful data and generate a lot of ++output on large machines, so sleeping system daemons are not printed by ++default. ++Use \fBps\ M\fR to print them. ++.P ++At the start of the \fBps\fR output is a line giving the cpu status, ++see the kdb \fBcpu\fR command. ++.SH LIMITATIONS ++None. ++.SH ENVIRONMENT ++.TP 8 ++PS ++This environment variable (default=DRSTCZEU) is used when \fBps\fR ++is issued with no parameters. ++ ++.SH SMP CONSIDERATIONS ++None. ++.SH EXAMPLES ++.TP 8 ++\fBps\fR ++displays the useful tasks, suppressing idle tasks and sleeping ++system daemons. ++ ++.TP 8 ++\fBps\ RD\fR ++displays only tasks that are running or are in an uninterruptible ++sleep. ++ ++.TP 8 ++\fBps\ DRSTCZEUIM\fR ++displays all tasks. ++ ++.TP 8 ++\fBps\ A\fR ++displays all tasks. ++This is easier than remembering DRSTCZEUIM. ++ +--- /dev/null ++++ b/Documentation/kdb/kdb_rd.man +@@ -0,0 +1,170 @@ ++.TH RD 1 "September 20, 2005" ++.SH NAME ++rd, rm\- Register manipulation commands ++.SH SYNOPSIS ++rd [[c [n]]|d|u] ++.LP ++rm \fIregister-name\fP \fInew-contents\fP ++.LP ++ef
++.SH DESCRIPTION ++The ++.B rd ++command is used to display the contents of processor and coprocessor registers. ++Without any arguments, the rd command displays the contents of the general ++register set at the point at which the kernel debugger was entered. ++If the bt* or pid commands have been used to change the current process then ++.B rd ++and ++.B rm ++may not be able to display any registers. ++'n' argument is only used for XScale platform to identify the desired ++coprocessor number, while 'd' option is not valid for XScale platform. ++.P ++On IA32 and IA64, with the 'c' argument, the processor control registers ++%cr0, %cr1, %cr2 and %cr4 are displayed, while with the 'd' argument ++the processor debug registers are displayed. If the 'u' argument ++is supplied, the registers for the current task as of the last ++time the current task entered the kernel are displayed. ++.P ++On XScale, 'c' argument is used to display the ++all coprocessor control registers or specified coprocessor registers by ++argumnet 'n'. Argument 'u' is used to display the ++registers for the current task as of the last time the current task ++entered the kernel. Argument 'd' is not supported. ++.P ++On ix86, the ++.B rm ++command allows modification of a register. The following ++register names are valid: \fB%eax\fP, \fB%ebx\fP, \fB%ecx\fP, ++\fB%edx\fP, \fB%esi\fP, \fB%edi\fP, \fB%esp\fP, \fB%eip\fP, ++and \fB%ebp\fP. Note that if two '%' symbols are used ++consecutively, the register set displayed by the 'u' argument ++to the \fBrd\fP command is modified. ++.P ++The debug registers, \fBdr0\fP through \fBdr3\fP and both ++\fBdr6\fP and \fBdr7\fP can also be modified with the \fBrm\fP ++command. ++.P ++On sparc64, the valid registers are named \fB%g0\fP through ++\fB%g7\fP, \fB%l0\fP through \fB%l7\fP, \fB%o0\fP through ++\fB%o7\fP, and \fB%i0\fP through \fB%i7\fP, with the exceptions ++that \fB%o6\fP is called \fB%sp\fP and that \fB%i6\fP is called ++\fB%fp\fP. The registers \fB%tstate\fP, \fB%tpc\fP, \fB%tnpc\fP, ++\fB%y\fP, and \fB%fprs\fP provide state information at the time ++the system entered kdb. Additionally, when viewing registers, two ++convenience names are provided: \fB%®s\fP shows the ++address on the stack of the current registers, and \fB%csp\fP ++shows the current stack pointer within kdb itself. ++.P ++While on XScale, both the cpu registers and most coprocessor ++registers can be be modified. \fIregister-name\fP can be followings like ++r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, ++r15, cpsr to address cpu registers. For the coprocessor registers in XSacle, ++either alias name or \fICpcc[CRndd[CRmbb[Opaa]]]\fP can be used to address ++the register in coprocessor cc with CRn=dd, CRm=bb and opcode2=aa. All aa, bb, cc, dd can be ++1 or 2 decimal digitals, the default value is 0 when any of them is omitted. Name ++acc0_h and acc0_l are used to identify the high byte and ++low word of accumulator in coprocessor 0. ++.P ++On IA64, the parameter to ++.B rd ++can be d (debug registers), u (user registers at most recent entry to kernel), ++i (interrupt registers), %isr (current interrupt status), s (stacked ++registers), k (kernel registers). You can also specify these individual ++registers - ++psr, ++ifs, ++ip, ++unat, ++pfs, ++rsc, ++rnat, ++bsps, ++pr, ++ldrs, ++ccv, ++fpsr, ++b0, ++b6, ++b7, ++r1, ++r2, ++r3, ++r8, ++r9, ++r10, ++r11, ++r12, ++r13, ++r14, ++r15, ++r16, ++r17, ++r18, ++r19, ++r20, ++r21, ++r22, ++r23, ++r24, ++r25, ++r26, ++r27, ++r28, ++r29, ++r30, ++r31. ++.B rm ++can change any of the individual registers or the stacked registers. ++.P ++The ++.B ef ++command displays an exception frame at the specified address. ++.SH LIMITATIONS ++Currently the \fBrm\fP command will not allow modification of the ++control registers. ++.P ++Currently neither the \fBrd\fP command nor the \fBrm\fP command will ++display or modify the model specific registers on the Pentium ++and Pentium Pro families. ++.SH ENVIRONMENT ++None. ++.SH SMP CONSIDERATIONS ++None. ++.SH EXAMPLES ++.TP 8 ++rd ++Display general register set from kdb's current task. ++ ++.TP 8 ++rd c 0 ++Display coprocessor 0 registers. ++ ++.TP 8 ++rm %eax 0 ++Set the contents of \fB%eax\fP to zero. This will be the ++value of %eax when kdb returns from the condition which ++invoked it. ++ ++.TP 8 ++rm %%eax 0 ++Set the value of the \fB%eax\fP register to zero. This will ++be the value the user-mode application will see upon returning ++from the kernel. ++ ++.TP 8 ++rm %acc0_h 0 ++Set the contents of high byte of accumulator to zero. ++ ++.TP 8 ++rm dr0 0xc1287220 ++Set the value of the \fBdr0\fB register to \f(CW0xc1287220\fP. ++ ++.TP 8 ++rm %InVLD_BTB 0 ++Write 0 to coprocessor 15 register with CRn=7, CRm=5, opcode2=6. ++ ++.TP 8 ++rm %CP15CRn7CRm5Op6 0 ++Same with above. +--- /dev/null ++++ b/Documentation/kdb/kdb_sr.man +@@ -0,0 +1,68 @@ ++.TH SR 1 "7 October 2002" ++.SH NAME ++sr \- invoke sysrq commands from kdb ++.SH SYNOPSIS ++sr \fIx\fP ++.SH DESCRIPTION ++.hy 0 ++The ++.B sr ++command invokes the existing sysrq handler code in the kernel. ++This command takes a single character which is passed to sysrq ++processing, as if you had entered the sysrq key sequence followed by ++that character. ++.P ++.B Caveats: ++.P ++kdb will always call the sysrq code but sysrq may be disabled. ++If you expect to use sysrq functions during debugging then ++.IP "" ++echo "1" > /proc/sys/kernel/sysrq ++.P ++before starting the debug session. ++Alternatively issue ++.IP "" ++mm4 sysrq_enabled 1 ++.P ++during debugging. ++.P ++The sysrq code prints a heading using console loglevel 7 then reverts ++to the original loglevel for the rest of the sysrq processing. ++If the rest of the sysrq output is printed at a level below your ++current loglevel then you will not see the output on the kdb console, ++the output will only appear in the printk buffer. ++It is the user's responsibility to set the loglevel correctly if they ++want to see the sysrq output on the console. ++Issue ++.IP "" ++sr 7 ++.P ++before any other ++.B sr ++commands if you want to see the output on the console. ++You may even have to adjust the default message loglevel in order to ++see any output from ++.BR sr . ++See Documentation/sysctl/kernel.txt for details on setting console ++loglevels via /proc. ++You can also adjust the loglevel variables via kdb ++.BR mm ; ++on older kernels there are variables such as default_message_level, on ++newer kernels all the loglevel variables are in array console_printk, ++see kernel/printk.c for your kernel. ++.P ++Operations that require interrupt driven I/O can be invoked from kdb ++.BR sr , ++but they will not do anything until you type 'go' to exit from kdb ++(interrupts are disabled while in kdb). ++There is no guarantee that these operations will work, if the machine ++entered kdb because of an error then interrupt driven I/O may already ++be dead. ++Do not assume that ++.B sr\ s ++does anything useful. ++.P ++The sysrq handler uses locks and calls printk which also uses locks. ++If the sysrq handler or any of the sysrq functions have to wait for a ++lock then they will never return and kdb will appear to hang. ++Invoking sysrq code from kdb is inherently unsafe. +--- /dev/null ++++ b/Documentation/kdb/kdb_ss.man +@@ -0,0 +1,109 @@ ++.TH SS 1 "17 January 2002" ++.SH NAME ++ss, ssb \- Single Step ++.SH SYNOPSIS ++ss ++.LP ++ssb ++.SH DESCRIPTION ++The ++.B ss ++command is used to execute a single instruction and return ++to the kernel debugger. ++.P ++Both the instruction that was single-stepped and the next ++instruction to execute are printed. ++.P ++The \fBssb\fP command will execute instructions from the ++current value of the instruction pointer. Each instruction ++may be printed as it is executed, depending upon architecture; ++execution will stop at any instruction which would cause the flow ++of control to change (e.g. branch, call, interrupt instruction, ++return, etc.) ++.SH LIMITATIONS ++On sparc64, there are some circumstances where single-stepping ++can be dangerous. Do not single-step across an instruction which ++changes the interrupt-enable bit in %tstate. Do not single step ++through code which is invoked when entering or leaving the ++kernel, particularly any kernel entry code before %tl is set to ++0, or any kernel exit code after %tl is set to 1. ++.SH ENVIRONMENT ++None. ++.SH SMP CONSIDERATIONS ++Other processors are held in the kernel debugger when the instruction ++is traced. Single stepping though code that requires a lock which is ++in use by another processor is an exercise in futility, it will never ++succeed. ++.SH INTERRUPT CONSIDERATIONS ++When a kdb event occurs, one cpu (the initial cpu) enters kdb state. ++It uses a cross system interrupt to interrupt the ++other cpus and bring them all into kdb state. All cpus run with ++interrupts disabled while they are inside kdb, this prevents most ++external events from disturbing the kernel while kdb is running. ++.B Note: ++Disabled interrupts means that any I/O that relies on interrupts cannot ++proceed while kdb is in control, devices can time out. The clock tick ++is also disabled, machines will lose track of time while they are ++inside kdb. ++.P ++Even with interrupts disabled, some non-maskable interrupt events ++will still occur, these can disturb the kernel while you are ++debugging it. The initial cpu will still accept NMI events, ++assuming that kdb was not entered for an NMI event. Any cpu ++where you use the SS or SSB commands will accept NMI events, even ++after the instruction has finished and the cpu is back in kdb. ++This is an unavoidable side effect of the fact that doing SS[B] ++requires the cpu to drop all the way out of kdb, including ++exiting from the NMI event that brought the cpu into kdb. Under ++normal circumstances the only NMI event is for the NMI oopser and ++that is kdb aware so it does not disturb the kernel while kdb is ++running. ++.P ++Sometimes doing SS or SSB on ix86 will allow one interrupt to proceed, ++even though the cpu is disabled for interrupts. I have not been able ++to track this one down but I suspect that the interrupt was pending ++when kdb was entered and it runs when kdb exits through IRET even ++though the popped flags are marked as cli(). If any ix86 hardware ++expert can shed some light on this problem, please notify the kdb ++maintainer. ++.SH EXAMPLES ++.nf ++.na ++.ft CW ++kdb> bp gendisk_head datar 4 ++Data Access Breakpoint #0 at 0xc024ddf4 (gendisk_head) in dr0 is enabled on cpu 0 ++for 4 bytes ++kdb> go ++... ++[root@host /root]# cat /proc/partitions ++Entering kdb on processor 0 due to Debug Exception @ 0xc01845e3 ++Read/Write breakpoint #0 at 0xc024ddf4 ++[0]kdb> ssb ++sd_finish+0x7b: movzbl 0xc02565d4,%edx ++sd_finish+0x82: leal 0xf(%edx),%eax ++sd_finish+0x85: sarl $0x4,%eax ++sd_finish+0x88: movl 0xc0256654,%ecx ++sd_finish+0x8e: leal (%eax,%eax,4),%edx ++sd_finish+0x91: leal (%eax,%edx,2),%edx ++sd_finish+0x94: movl 0xc0251108,%eax ++sd_finish+0x99: movl %eax,0xffffffc(%ecx,%edx,4) ++sd_finish+0x9d: movl %ecx,0xc0251108 ++sd_finish+0xa3: xorl %ebx,%ebx ++sd_finish+0xa5: cmpb $0x0,0xc02565d4 ++[0]kdb> go ++[root@host /root]# ++ ++[0]kdb> ss ++sys_read: pushl %ebp ++SS trap at 0xc01274c1 ++sys_read+0x1: movl %esp,%ebp ++[0]kdb> ss ++sys_read+0x1: movl %esp,%ebp ++SS trap at 0xc01274c3 ++sys_read+0x3: subl $0xc,%esp ++[0]kdb> ss ++sys_read+0x3: subl $0xc,%esp ++SS trap at 0xc01274c6 ++sys_read+0x6: pushl %edi ++[0]kdb> ++ +--- /dev/null ++++ b/Documentation/kdb/slides +@@ -0,0 +1,1382 @@ ++#! /opt/cpg/bin/do-mgp ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%% ++%deffont "standard" tfont "comic.ttf" ++%deffont "thick" tfont "arialb.ttf" ++%deffont "typewriter" xfont "courier new-bold-r" ++%deffont "type2writer" xfont "arial narrow-bold-r" ++%% ++%% Default settings per each line numbers. ++%% ++#%default 1 leftfill, size 2, fore "black", back "LemonChiffon2", font "thick" ++%default 1 leftfill, size 2, fore "black", back "white", font "thick" ++%default 2 size 10, vgap 10, prefix " ", center ++%default 3 size 2, bar "gray70", vgap 10 ++%default 4 size 6, fore "black", vgap 30, prefix " ", font "standard", left ++%% ++%% Default settings that are applied to TAB-indented lines. ++%% ++%tab 1 size 4, vgap 35, prefix " ", icon arc "red" 40 ++%tab 2 size 4, vgap 20, prefix " ", icon delta3 "blue" 40 ++%tab 3 size 4, vgap 20, prefix " ", icon delta3 "green" 40 ++%% ++%% ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++KDB - Kernel Debugger ++ ++ ++ ++%size 7,center, font "thick" ++Introduction ++ ++And ++ ++Demonstration ++ ++ ++%size 3 ++ ++February 5, 2002 IBM Linux Technology Center Paul Dorwin ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++IBM Legal ++ ++ ++ IBM Legal requires this information: ++ ++%size 3 ++ ++ THE INFORMATION IN THE FOLLOWING PRESENTATION IS PREPARED ++ SOLELY FOR THE INFORMATION OF THE READER, AND COMES "AS IS" ++ AND WITHOUT WARRANTY OR REPRESENATION OF ANY KIND. ++ ++ ANY PARTY USING THE MATERIALS IN THIS PRESENTATION DOES SO ++ AT ITS OWN RISK LIABILITY AND THE PROVIDER OF THE MATERIALS ++ ACCEPTS NO RISK OR LIABILITY FOR SUCH USE OR RESULTING FROM ++ DISSEMINATION TO OR USE BY ANY OTHER PARTY ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Agenda ++ ++%size 5 ++ ++ Installing and Configuring KDB ++ ++ KDB Commands ++ ++ Scull Demo ++ ++ Setting Breakpoints ++ ++ Displaying Data Structures ++ ++ Kernel Data structures ++ ++ Take a walk through an IO operation ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Installing Configuring KDB ++ ++ ++ Install KDB patch. ++ Start with a clean source tree ++ Apply architecture specific patches ++ Obtain patch for your kernel version ++ see http://oss.sgi.com/projects/kdb/ ++ Apply the kdb patch ++ patch -p 1 -N -u -i /path/to/patch ++ Apply any other patches ++ Build and reboot on your kdb enabled kernel ++ Man pages can be found at Documentation/kdb ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Configuring KDB ++ ++ ++ Config kernel with the following options: ++ These are documented in Documentation/Configure.help ++ ++ CONFIG_KDB=y ++ Enable compilation of KDB in the kernel.. ++ Setting this also sets CONFIG_KALLSYMS=y. ++ CONFIG_KDB_MODULES=n ++ KDB may be extended, compiling kdb/modules. ++ CONFIG_KDB_OFF=n ++ y = KDB is disabled by default. ++ boot with kdb=on to enable at boot. ++ /proc/sys/kernel/kdb to enable/disable when system is up. ++ CONFIG_KALLSYMS=y ++ This causes all symbols to be exported. ++ CONFIG_FRAME_POINTER=y ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Invoking KDB ++ ++ ++ KDB can be invoked in the following ways: ++ ++ Early init with "kdb=early" lilo flag ++ Hits breakpoint prior to fork_init() (init/main.c) ++ ++ Serial console with CNTRL-A ++ ++ Console with PAUSE key ++ ++ When a pre-set breakpoint is hit ++ ++ On panic ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++KDB Commands ++ ++ ++ KDB environment ++ env Show environment variables ++ set Set environment variables ++ help Display Help Message ++ ? Display Help Message ++ ++ System related ++ sections List kernel and module sections ++ lsmod List loaded kernel modules ++ reboot Reboot the machine immediately ++ cpu Switch to new cpu ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++KDB Commands ++ ++ ++ Memory Manipulation ++ md Display Memory Contents ++ mdr Display Raw Memory ++ mds Display Symbolically ++ mm Modify Memory Contents ++ id Display Instructions ++ ++ Register Manipulation ++ rd Display Registers ++ rm Modify Registers ++ ef Display exception frame ++ ++ Stack ++ bt [] Stack traceback ++ btp Display stack for ++ bta Display all stacks ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++KDB Commands ++ ++ ++ Breakpoint ++ bc Clear Breakpoint ++ bd Disable Breakpoint ++ be Enable Breakpoint ++ bl [] Display breakpoints ++ bp [] Set/Display breakpoints ++ bpa [] Set/Display global breakpoints ++ bph [] Set hardware breakpoint ++ bpha [] Set global hardware breakpoint ++ bp* modifiers: ++ instruction - break on instruction fetch (default) ++ datar - break on read at vaddr ++ dataw - break on write at vaddr ++ IO - break on in or out op at vaddress ++ ++ Execution control ++ go [] Continue Execution ++ ss [<#steps>] Single Step ++ ssb Single step to branch/call ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++KDB Commands ++ ++ ++ Kernel structures ++ ll Traverse list and execute command ++ ps Display active task list ++ vm Display vm_area_struct ++ dentry Display interesting dentry stuff ++ filp Display interesting filp stuff ++ sh Show scsi_host ++ sd Show scsi_device ++ sc Show scsi_cmnd ++ kiobuf Display kiobuf ++ page Display page ++ inode Display inode ++ bh Display buffer head ++ inode_pages Display pages in an inode ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo ++ ++ ++ Objective ++ Find and display the data associated with a scull device ++ ++ The sequence of events ++ Populate the scull device with data ++ Identify the breakpoints ++ Set breakpoint in the device read function ++ Identify the data structure elements ++ Identify device structures used to track data ++ Display data structures containing the data ++ Show the usage of the filp command ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: Populate Device ++ ++ ++ Obtain the code ++ Surf to http://examples.oreilly.com/linuxdrive2/ ++ Download the tarball ++ Untar it to /usr/src ++ ++ Build and install the module ++ cd /usr/src/ldd2-samples-1.0.1/scull ++ make ++ ./scull.init start ++ ++ Populate the scull device ++ cat main.c > /dev/scull0 ++ cat /dev/scull0 ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: Driver Details ++ ++ ++ cat /dev/scull0 ++ fd = ++%fore "blue", cont ++open ++%fore "black", cont ++("/dev/scull0", O_RDONLY); ++ Kernel finds the file_operations structure ++ Kernel then invokes the open function ++%fore "blue" ++ read ++%fore "black", cont ++(fd, buf, size); ++ Kernel finds the file_operations structure ++ Kernel then invokes the read function ++ ++ Scull device file operations structure ++ ++%font "typewriter", size 3 ++ struct file_operations scull_fops = { ++ llseek: scull_llseek, ++%fore "blue" ++ read: scull_read, ++%fore "black" ++ write: scull_write, ++ ioctl: scull_ioctl, ++%fore "blue" ++ open: scull_open, ++%fore "black" ++ release: scull_release, ++ }; ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: Driver Details ++ ++%font "typewriter", size 3 ++ scull_open code ++%font "typewriter", size 3 ++ int ++%fore "blue", cont ++scull_open ++%fore "black", cont ++(struct inode *inode, struct file *filp) ++ { ++ Scull_Dev *dev; /* device information */ ++ int num = NUM(inode->i_rdev); ++ ++ ++ ++ dev = (Scull_Dev *)filp->private_data; ++ if (!dev) { ++ if (num >= scull_nr_devs) return -ENODEV; ++%fore "blue" ++ dev = &scull_devices[num]; ++ filp->private_data = dev; ++%fore "black" ++ } ++ ++ ++ ++ } ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: Driver Details ++ ++%font "typewriter", size 3 ++ scull_read code ++%font "typewriter", size 3 ++ ssize_t ++%fore "blue", cont ++scull_read ++%fore "black", cont ++(struct file *filp, char *buf, size_t count, ++ loff_t *f_pos) ++ { ++ ++%fore "blue", cont ++ Scull_Dev *dev = filp->private_data; ++%fore "black", cont ++ /* the first listitem */ ++%fore "blue" ++ Scull_Dev *dptr; ++%fore "black" ++ int quantum = dev->quantum; ++ int qset = dev->qset; ++ int itemsize = quantum * qset; ++ if (down_interruptible(&dev->sem)) ++ return -ERESTARTSYS; ++ if (*f_pos + count > dev->size) ++ count = dev->size - *f_pos; ++ ++ /* find listitem, qset index, and offset in the quantum */ ++ item = (long)*f_pos / itemsize; ++ rest = (long)*f_pos % itemsize; ++ s_pos = rest / quantum; q_pos = rest % quantum; ++ ++ /* follow the list up to the right position */ ++%fore "blue" ++ dptr = scull_follow(dev, item); ++%fore "black" ++ ++ ++ ++ } ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: Breakpoints ++ ++ ++%font "typewriter", size 3 ++ Determine where to set breakpoint ++%font "typewriter", size 3 ++%fore "blue" ++ dptr = scull_follow(dev, item); ++%fore "black" ++ ++%font "typewriter", size 3 ++ Disassemble scull_read ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++id scull_read ++%fore "black" ++ 0xf8c083b4 scull_read: push %ebp ++ 0xf8c083b5 scull_read+0x1:mov %esp,%ebp ++ 0xf8c083b7 scull_read+0x3:push %edi ++ ++ 0xf8c08465 scull_read+0xb1:sub $0x8,%esp ++%fore "blue" ++ 0xf8c08468 scull_read+0xb4:push %ecx ++ 0xf8c08469 scull_read+0xb5:push %esi ++ 0xf8c0846a scull_read+0xb6:call 0xf8c08364 scull_follow: ++%fore "black" ++ 0xf8c0846f scull_read+0xbb:mov %eax, ++%fore "blue", cont ++ %edx ++%fore "black" ++ 0xf8c08471 ++%fore "blue", cont ++scull_read+0xbd ++%fore "black", cont ++:add $0x10,%esp ++ ++ ++ Set breakpoint in driver read ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue",cont ++bp scull_read+0xbd ++%fore "black" ++ Instruction(i) BP #0 at 0xf8c08471 ([scull]scull_read+0xbd) ++ is enabled globally adjust 1 ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: Breakpoints ++ ++ ++%font "typewriter", size 3 ++ Restart the system ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++go ++%fore "black" ++ ++ Hit the Breakpoint ++%font "typewriter", size 3 ++ [root@elm3b77 root]# ++%fore "blue", cont ++cat /dev/scull0 ++%fore "black" ++ Instruction(i) breakpoint #0 at 0xf8c08471 (adjusted) ++ 0xf8c08471 scull_read+0xbd:int3 ++ Entering kdb (current=0xf73ec000, pid 1249) on processor 2 ++ due to Breakpoint @ 0xf8c08471 ++ ++ Display the registers ++%font "typewriter", size 3 ++ [2]kdb> ++%fore "blue", cont ++rd ++%fore "black" ++ eax = 0xf77d7b60 ebx = 0x00000000 ecx = 0x00000000 edx = ++%fore "blue", cont ++0xf77d7b60 ++%fore "black" ++ esi = ++%fore "blue", cont ++0xf77d7b60 ++%fore "black", cont ++ edi = 0x00001000 esp = 0xf7415f40 eip = 0xf8c08471 ++ ebp = 0xf7415f78 xss = 0x00000018 xcs = 0x00000010 eflags = 0x00000246 ++ xds = 0xf7590018 xes = 0x00000018 origeax = 0xffffffff ®s = 0xf7415f0c ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: Data Structures ++ ++%font "typewriter", size 3 ++ Display the Scull_Dev structure ++%font "typewriter", size 3 ++ [2]kdb> ++%fore "blue", cont ++md 0xf77d7b60 2 ++%fore "black" ++ 0xf77d7b60 ++%fore "blue", cont ++f7400000 ++%fore "black", cont ++ 00000000 00000fa0 000003e8 ..@w.... ...h... ++ 0xf77d7b70 0000534e 00000000 00000000 00000000 NS.............. ++ ++ Scull Device Structure ++%font "typewriter", size 3 ++ typedef struct Scull_Dev { ++%fore "blue" ++ void **data; ++%fore "black" ++ struct Scull_Dev *next; /* next listitem */ ++ int quantum; /* the current quantum size */ ++ int qset; /* the current array size */ ++ unsigned long size; ++ devfs_handle_t handle; /* only used if devfs is there */ ++ unsigned int access_key; /* used by sculluid and scullpriv */ ++ struct semaphore sem; /* mutual exclusion semaphore */ ++ } Scull_Dev; ++%size 6 ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: Data Structures ++ ++ ++%font "typewriter", size 3 ++ Display the quantum set (dev->data) ++%font "typewriter", size 3 ++ [2]kdb> ++%fore "blue", cont ++md f7400000 2 ++%fore "black" ++ 0xf7400000 ++%fore "blue", cont ++f73ea000 ++%fore "black", cont ++ f73f1000 f740c000 f7ab4000 . >w..?w.@@w.@+w ++ 0xf7400010 f73ef000 f755b000 00000000 00000000 .p>w.0Uw........ ++ ++ Display the first quantum (dev->data[0]) ++%font "typewriter", size 3 ++ [2]kdb> ++%fore "blue", cont ++md f73ea000 ++%fore "black" ++ 0xf73ea000 200a2a2f 616d202a 632e6e69 202d2d20 /*. * main.c -- ++ 0xf73ea010 20656874 65726162 75637320 63206c6c the bare scull c ++ 0xf73ea020 20726168 75646f6d 200a656c 2a200a2a har module. *. * ++ 0xf73ea030 706f4320 67697279 28207468 32202943 Copyright (C) 2 ++ 0xf73ea040 20313030 73656c41 646e6173 52206f72 001 Alessandro R ++ 0xf73ea050 6e696275 6e612069 6f4a2064 6874616e ubini and Jonath ++ 0xf73ea060 43206e61 6562726f 2a200a74 706f4320 an Corbet. * Cop ++ 0xf73ea070 67697279 28207468 32202943 20313030 yright (C) 2001 ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: filp command ++ ++ ++%font "typewriter", size 3 ++ Show filp usage - here is the scull_read prototype ++%font "typewriter", size 3 ++ ssize_t scull_read( ++%fore "blue", cont ++struct file *filp ++%fore "black", cont ++, char *buf, ++ size_t count, loff_t *f_pos); ++ Show the stack trace: ++%font "typewriter", size 3 ++[2]kdb> ++%fore "blue", cont ++bt ++%fore "black" ++ EBP EIP Function(args) ++ 0xee9dbf78 0xf8c08471 [scull]scull_read+0xbd ( ++%fore "blue", cont ++0xeaf6c0c0 ++%fore "black", cont ++, 0x804e128, ++ 0x1000, 0xeaf6c0e0, 0x804f000) ++ scull .text 0xf8c08060 0xf8c083b4 0xf8c084dc ++ 0xee9dbfbc 0xc0136278 sys_read+0x98 (0x3, 0x804e128, 0x1000, ... ++ kernel .text 0xc0100000 0xc01361e0 0xc01362b0 ++ 0xc010702b system_call+0x33 ++ kernel .text 0xc0100000 0xc0106ff8 0xc0107030 ++ And show the filp output ++%font "typewriter", size 3 ++ [2]kdb> ++%fore "blue", cont ++filp 0xeaf6c0c0 ++%fore "black" ++ name.name 0xe93889fc name.len 6 ++ File Pointer at 0xeaf6c0c0 ++ f_list.nxt = 0xe42deca0 f_list.prv = 0xf7e69070 ++%fore "blue" ++ f_dentry = 0xe93889a0 ++%fore "black", cont ++ f_op = 0xf8c0a200 ++ f_count = 2 f_flags = 0x8000 f_mode = 0x1 ++ f_pos = 0 f_reada = 0 f_ramax = 0 ++ f_raend = 0 f_ralen = 0 f_rawin = 0 ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Scull Demo: filp command ++ ++ ++%font "typewriter", size 3 ++ filp output - continued ++%font "typewriter", size 3 ++%fore "blue" ++ Directory Entry at 0xe93889a0 ++%fore "black" ++ d_name.len = 6 ++%fore "orange", cont ++d_name.name = 0xe93889fc ++%fore "black", cont ++> ++ d_count = 1 d_flags = 0x0 ++%fore "blue", cont ++d_inode = 0xe827b680 ++%fore "black" ++ d_hash.nxt = 0xc215aec8 d_hash.prv = 0xc215aec8 ++ d_lru.nxt = 0xe93889b8 d_lru.prv = 0xe93889b8 ++ d_child.nxt = 0xe89e1e80 d_child.prv = 0xe9388940 ++ d_subdirs.nxt = 0xe93889c8 d_subdirs.prv = 0xe93889c8 ++ d_alias.nxt = 0xe827b690 d_alias.prv = 0xe827b690 ++ d_op = 0x00000000 d_sb = 0xf7e69000 ++ ++%fore "blue" ++ Inode Entry at 0xe827b680 ++%fore "black" ++ i_mode = 0x21a4 i_nlink = 1 i_rdev = 0xfe00 ++ i_ino = 37182 i_count = 1 i_dev = 0x821 ++ i_hash.nxt = 0xc20e6be8 i_hash.prv = 0xc20e6be8 ++ i_list.nxt = 0xe827b2c8 i_list.prv = 0xe827b868 ++ i_dentry.nxt = 0xe93889d0 i_dentry.prv = 0xe93889d0 ++ ++ Check the filename (display d_name.name) ++%font "typewriter", size 3 ++ [2]kdb> ++%fore "orange", cont ++md 0xe93889fc 1 ++%fore "black" ++ 0xe93889fc 6c756373 0000306c 00000000 00000000 scull0.......... ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Kernel Structures ++ ++ ++ Objective ++ Show output from various kernel related kdb commands ++ ++ Sequence of events ++ Simple Program ++ Write a simple program which allocates memory and hangs ++ Show usage of the ps, vm, and ll commands ++ Walk an IO operation ++ Hit a breakpoint in qlogic driver (isp1020_queuecommand) ++ Show usage of scsi related commands (sc, sh, and sd) ++ Show usage of vm related commands (bh, page, inode, inode_pages) ++ ++ ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Simple program ++ ++%font "typewriter", size 3 ++ simple.c - simple program which allocates memory ++%font "typewriter", size 3 ++%fore "blue" ++ int foo_global[8192]; ++%fore "black" ++ main() ++ { ++ int * ++%fore "blue", cont ++foo_malloc; ++%fore "black" ++ int i; ++ foo_malloc = (int *)malloc(0x8192); ++ for(i = 0; i < 0x100; i++) { ++ foo_global[i] = 0xdead0000 | i; ++ foo_malloc[i] = 0xbeef0000 | i; ++ } ++ printf("foo_global at %x\n", (int)foo_global); ++ printf("foo_malloc at %x\n", (int)foo_malloc); ++ printf("sleep forever\n"); ++ sleep(2000000); ++ } ++ ++ simple output ++%font "typewriter", size 3 ++ [root@elm3b77 scull]# cc -o simple simple.c ++ [root@elm3b77 scull]# ./simple ++ foo_global at ++%fore "blue", cont ++8049780 ++%fore "black" ++ foo_malloc at ++%fore "blue", cont ++8051788 ++%fore "black" ++ sleep forever ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Simple Program ++ ++%font "typewriter", size 3 ++ Show the output of the ps command ++%font "typewriter", size 3 ++ Entering kdb (current=0xc2010000, pid 0) on processor 3 due to ++ Keyboard Entry ++ [3]kdb> ++%fore "blue", cont ++ps ++%fore "black" ++ Task Addr Pid Parent [*] cpu State Thread Command ++ 0xf7efe000 00000001 00000000 0 003 stop 0xf7efe370 init ++ 0xf7ef0000 00000002 00000001 0 001 stop 0xf7ef0370 keventd ++ 0xf7eec000 00000003 00000000 0 000 stop 0xf7eec370 ksoftirqd_CPU0 ++ 0xf7eea000 00000004 00000000 0 001 stop 0xf7eea370 ksoftirqd_CPU1 ++ 0xf7ee8000 00000005 00000000 0 002 stop 0xf7ee8370 ksoftirqd_CPU2 ++ 0xf7ee6000 00000006 00000000 0 003 stop 0xf7ee6370 ksoftirqd_CPU3 ++ ++ ++ ++ 0xf7b46000 00001006 00000737 0 003 stop 0xf7b46370 sshd ++ 0xf7ace000 00001007 00001006 0 000 stop 0xf7ace370 bash ++ 0xef06a000 00001066 00001007 0 003 stop 0xef06a370 su ++ 0xeef88000 00001067 00001066 0 000 stop 0xeef88370 bash ++ 0xeef64000 00001119 00000770 0 001 stop 0xeef64370 in.ftpd ++%fore "blue" ++ 0xeeeac000 ++%fore "black", cont ++ 00001138 00001067 0 001 stop 0xeeeac370 ++%fore "blue", cont ++simple ++%fore "black" ++ [3]kdb> ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Simple Program ++ ++%font "typewriter", size 3 ++ Display the task struct ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++md 0xeeeac000 ++%fore "black" ++ 0xeeeac000 00000001 00000000 00000000 c0000000 ................ ++ 0xeeeac010 c0339880 00000000 00000000 ffffffff ................ ++ 0xeeeac020 0000000a 00000000 00000000 ++%fore "blue", cont ++f7e10f00 ++%fore "black", cont ++ ..............aw ++ 0xeeeac030 00000001 ffffffff ffffffff 00000000 ................ ++ ++%font "typewriter", size 3 ++ Determine offset of mm_struct ptr in task_struct ++%font "typewriter", size 3 ++ struct task_struct { ++ [0] volatile long state; ++ [4] unsigned long flags; ++ [8] int sigpending; ++ [c] mm_segment_t addr_limit; ++ [10] struct exec_domain *exec_domain; ++ [14] volatile long need_resched; ++ [18] unsigned long ptrace; ++ [1c] int lock_depth; ++ [20] long counter; ++ [24] long nice; ++ [28] unsigned long policy; ++%fore "blue" ++ [2c] struct mm_struct *mm; ++%fore "black" ++ [30] int processor; ++ [34] unsigned long cpus_runnable, cpus_allowed; ++ ++ }; ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Simple Program ++ ++ ++%font "typewriter", size 3 ++ Display the mm_struct associated with simple process ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++md f7e10f00 ++%fore "black" ++ 0xf7e10f00 ++%fore "blue", cont ++e8357a80 ++%fore "black", cont ++ e8357978 f7ac77e0 eb15eac0 .z5hxy5h`w,w@j.k ++ 0xf7e10f10 00000001 00000002 0000000b 00000000 ................ ++ 0xf7e10f20 00000001 f7e10f24 f7e10f24 00000001 ................ ++ 0xf7e10f30 f7e35e70 eea7e8f0 08048000 0804862b ................ ++ 0xf7e10f40 0804962c 08049744 08051780 0805a000 ................ ++ 0xf7e10f50 bffffd10 bffffe00 bffffe09 bffffe09 ................ ++ 0xf7e10f60 bffffff3 0000005a 00000168 00000000 ................ ++ 0xf7e10f70 00000000 00000002 00000000 00000001 ................ ++ ++%font "typewriter", size 3 ++ Determine offset of the first vma in the process ++%font "typewriter", size 3 ++ struct mm_struct { ++%fore "blue" ++ struct vm_area_struct * mmap; ++%fore "black" ++ rb_root_t mm_rb; ++ struct vm_area_struct * mmap_cache; ++ ++ }; ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Simple Program ++ ++%font "typewriter", size 3 ++ Display the first vma using md ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++md e8357a80 ++%fore "black" ++ 0xe8357a80 f7e10f00 08048000 08049000 ++%fore "blue", cont ++e8727e00 ++%fore "black",cont ++ ..aw.........~rh ++ 0xe8357a90 00000025 00001875 e8727e18 00000001 %...u....~rh.... ++ ++ Display the first vma using vma ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++vma e8357a80 ++%fore "black" ++ struct vm_area_struct at 0xe8357a80 for 68 bytes ++ vm_start = 0x8048000 vm_end = 0x8049000 ++ page_prot = 0x25 ++ flags: READ EXEC MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE ++%font "typewriter", size 3 ++ ++ Determine the offset to the vma list ++%font "typewriter", size 3 ++ struct vm_area_struct { ++ [0] struct mm_struct * vm_mm; ++ [4] unsigned long vm_start; ++ [8] unsigned long vm_end; ++%fore "blue" ++ [c] struct vm_area_struct *vm_next; ++%fore "black" ++ ++ }; ++ Display the next vma ++%font "typewriter", size 3 ++ [3]kdb> vma e8727e00 ++ struct vm_area_struct at 0xe8727e00 for 68 bytes ++ vm_start = 0x8049000 vm_end = 0x804a000 ++ page_prot = 0x25 ++ flags: READ WRITE MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Simple Program ++ ++%font "typewriter", size 3 ++ Use the ll command to display the list of vma's ++%font "typewriter", size 3 ++ [3]kdb> ll e8357a80 0xc vma ++. ++ struct vm_area_struct at 0xe8357a80 for 68 bytes ++ vm_start = 0x8048000 vm_end = 0x8049000 ++ page_prot = 0x25 ++ flags: READ EXEC MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE ++. ++ struct vm_area_struct at 0xe8727e00 for 68 bytes ++ vm_start = ++%fore "orange", cont ++0x8049000 ++%fore "black", cont ++ vm_end = ++%fore "orange", cont ++0x804a000 ++%fore "black" ++ page_prot = 0x25 ++ flags: READ WRITE MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE ++. ++ struct vm_area_struct at 0xe8727c80 for 68 bytes ++ vm_start = ++%fore "blue", cont ++0x804a000 ++%fore "black", cont ++ vm_end = ++%fore "blue", cont ++0x805a000 ++%fore "black" ++ page_prot = 0x25 ++ flags: READ WRITE EXEC MAYREAD MAYWRITE MAYEXEC ++ ++ struct vm_area_struct at 0xe8357900 for 68 bytes ++ vm_start = 0xbfffe000 vm_end = 0xc0000000 ++ page_prot = 0x25 ++ flags: READ WRITE EXEC MAYREAD MAYWRITE MAYEXEC GROWSDOWN ++ ++ Match the vma to the displayed addresses ++%font "typewriter", size 3 ++ foo_global at ++%fore "orange", cont ++8049780 ++%fore "black" ++ foo_malloc at ++%fore "blue", cont ++8051788 ++%fore "black" ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++ Objective ++ Show usage of various scsi and vm related kdb commands ++ ++ Sequence: ++ Set a breakpoint in the scsi driver ++ Stops when queueing a command to the controller ++ Cause IO on an idle disk ++ Show various IO stack traces ++ Display the IO data structures ++ Display vm information about the data ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Set the breakpoint ++ ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++bp isp1020_queuecommand ++%fore "black" ++ Instruction(i) BP #0 at 0xc01ecfe0 (isp1020_queuecommand) ++ is enabled globally adjust 1 ++ ++%font "typewriter", size 3 ++ Create some activity on a previously unused disk ++ ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++go ++%fore "black" ++ [root@elm3b77 root]# ++%fore "blue", cont ++ls /rh62 ++%fore "black" ++ ++ Instruction(i) breakpoint #0 at 0xc01ecfe0 (adjusted) ++ 0xc01ecfe0 isp1020_queuecommand:int3 ++ ++ Entering kdb (current=0xf75ba000, pid 1181) on processor 3 due to ++ Breakpoint @ 0xc01ecfe0 ++ ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Show the stack. ++ This is a read of the /rh62 directory ++ ++%font "typewriter", size 3 ++ [1]kdb> ++%fore "blue", cont ++bt ++%fore "black" ++ EBP EIP Function(args) ++ 0xf75bbdf4 0xc01ecfe0 isp1020_queuecommand ++ 0xc01e2c77 scsi_dispatch_cmd+0x1f7 ++ 0xf75bbe24 0xc01e99b1 scsi_request_fn+0x2f1 ++ 0xf75bbe34 0xc01c84fd generic_unplug_device+0x2d ++ 0xf75bbe50 0xc011b3af __run_task_queue+0x5f ++ 0xf75bbe6c 0xc013a63c block_sync_page+0x1c ++ 0xf75bbe98 0xc0128127 __lock_page+0x77 ++ 0xf75bbea4 0xc0128178 lock_page+0x18 ++ 0xf75bbec8 0xc012a4b3 read_cache_page+0xc3 ++ 0xf75bbef4 0xc0168e23 ext2_get_page+0x23 ++ 0xf75bbf48 0xc0168fdd ext2_readdir+0xfd ++ 0xf75bbf68 0xc0143d2e vfs_readdir+0x7e ++ 0xf75bbfbc 0xc01442ed ++%fore "blue", cont ++sys_getdents64+0x4d ++%fore "black" ++ 0xc010702b system_call+0x33 ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Allow the operation to complete ++ ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++go ++%fore "black" ++ bench build etc lib mnt oldsys rh72 spv usr ++ bin data h linux mnt1 opt root test var ++ boot dev home lost+found mnt2 proc sbin tmp ++ ++%font "typewriter", size 3 ++ Force some more activity ++ ++%font "typewriter", size 3 ++ [root@elm3b77 root]# ++%fore "blue", cont ++cd /rh62/tmp ++%fore "black" ++ Instruction(i) breakpoint #0 at 0xc01ecfe0 (adjusted) ++ 0xc01ecfe0 isp1020_queuecommand:int3 ++ ++ Entering kdb (current=0xf768a000, pid 981) on processor 3 due to ++ Breakpoint @ 0xc01ecfe0 ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Show the stack. ++ This is an inode read for /rh62/tmp ++ ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++bt ++%fore "black" ++ EBP EIP Function(args) ++ 0xf768bd68 0xc01ecfe0 isp1020_queuecommand ++ 0xc01e2c77 scsi_dispatch_cmd+0x1f7 ++ 0xf768bd98 0xc01e99b1 scsi_request_fn+0x2f1 ++ 0xf768bda8 0xc01c84fd generic_unplug_device+0x2d ++ 0xf768bdc4 0xc011b3af __run_task_queue+0x5f ++ 0xf768bdfc 0xc0137216 __wait_on_buffer+0x56 ++ 0xf768be1c 0xc0138600 bread+0x50 ++ 0xf768be5c 0xc016b684 ext2_read_inode+0x114 ++ 0xf768bf0c 0xc013fbec real_lookup+0x7c ++ 0xf768bf78 0xc014035d link_path_walk+0x5ad ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Create a new file, causing yet more disk activity ++ ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++go ++%fore "black" ++ ++ [root@elm3b77 tmp]# ++%fore "blue", cont ++echo "Hello linux reading group" > j1;sync ++%fore "black" ++ ++ Instruction(i) breakpoint #0 at 0xc01ecfe0 (adjusted) ++ 0xc01ecfe0 isp1020_queuecommand:int3 ++ ++ Entering kdb (current=0xf768a000, pid 981) on processor 3 due to ++ Breakpoint @ 0xc01ecfe0 ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Show the stack ++ This is an inode read in response to the open ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++bt ++%fore "black" ++ EBP EIP Function(args) ++ 0xf768bd78 0xc01ecfe0 isp1020_queuecommand ++ 0xc01e2c77 scsi_dispatch_cmd+0x1f7 ++ 0xf768bda8 0xc01e99b1 scsi_request_fn+0x2f1 ++ 0xf768bdb8 0xc01c84fd generic_unplug_device+0x2d ++ 0xf768bdd4 0xc011b3af __run_task_queue+0x5f ++ 0xf768bdf0 0xc013a63c block_sync_page+0x1c ++ 0xf768be1c 0xc0128127 __lock_page+0x77 ++ 0xf768be28 0xc0128178 lock_page+0x18 ++ 0xf768be4c 0xc012a4b3 read_cache_page+0xc3 ++ 0xf768be78 0xc0168e23 ext2_get_page+0x23 ++ 0xf768beb8 0xc01691ed ext2_find_entry+0x8d ++ 0xf768bed4 0xc016933a ext2_inode_by_name+0x1a ++ 0xf768befc 0xc016c077 ext2_lookup+0x27 ++ 0xf768bf1c 0xc014094a lookup_hash+0x9a ++ 0xf768bf64 0xc0140c4d open_namei+0xfd ++ 0xf768bfa0 0xc0135907 filp_open+0x37 ++ 0xf768bfbc 0xc0135c64 sys_open+0x34 ++ 0xc010702b system_call+0x33 ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Let the operation continue ++%font "typewriter", size 3 ++ [3]kdb> ++%fore "blue", cont ++go ++%fore "black" ++ Instruction(i) breakpoint #0 at 0xc01ecfe0 (adjusted) ++ 0xc01ecfe0 isp1020_queuecommand: int3 ++ Entering kdb (current=0xc0352000, pid 0) on processor 0 due to ++ Breakpoint @ 0xc01ecfe0 ++ Show the stack ++ This is an io completion queuing the next request ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++bt ++%fore "black" ++ EBP EIP Function(args) ++ 0xc0353df4 0xc01ecfe0 isp1020_queuecommand( ++%fore "blue", cont ++0xf7e63a00 ++%fore "black", cont ++,0xc01e7fc0... ++ 0xc01e2c77 scsi_dispatch_cmd+0x1f7 ++ 0xc0353e24 0xc01e99b1 scsi_request_fn+0x2f1 ++ 0xc0353e40 0xc01e8f6a ++%fore "blue", cont ++scsi_queue_next_request+0x4a ++%fore "black" ++ 0xc0353e5c 0xc01e9166 __scsi_end_request+0x116 ++ 0xc0353ea8 0xc01e93e0 ++%fore "blue", cont ++scsi_io_completion+0x170 ++%fore "black" ++ 0xc0353ecc 0xc01f658e rw_intr+0x14e ++ 0xc0353ef8 0xc01e8668 scsi_old_done+0x6a8 ++ 0xc0353fd4 0xc01052c2 cpu_idle+0x52 ++ Function prototype ++%font "typewriter", size 3 ++ int isp1020_queuecommand( ++%fore "blue", cont ++Scsi_Cmnd *Cmnd, ++%fore "black" ++ void (*done)(Scsi_Cmnd *)) ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Show the command being queued ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++sc 0xf7e63a00 ++%fore "black" ++ scsi_cmnd at 0xf7e63a00 ++%fore "blue" ++ host = 0xf7e91400 ++%fore "black", cont ++ state = 4099 owner = 258 ++%fore "blue", cont ++device = 0xf7ed5d80 ++%fore "black" ++ bnext = 0x00000000 reset_chain = 0x00000000 eh_state = 0 ++ done = 0xc01f6440 ++ serial_number = 3402 serial_num_at_to = 0 retries = 0 timeout = 0 ++ id/lun/cmnd = [0/0/0] cmd_len = 10 old_cmd_len = 10 ++ cmnd = [2a/00/00/28/00/3f/00/00/10/00/ef/f7] ++ data_cmnd = [2a/00/00/28/00/3f/00/00/10/00/ef/f7] ++ request_buffer = 0xc03fd000 bh_next = 0x00000000 ++ request_bufflen = 8192 ++ use_sg = 2 old_use_sg = 2 sglist_len = 512 abore_reason = 0 ++ bufflen = 8192 buffer = 0xc03fd000 underflow = 8192 ++ transfersize = 512 ++ tag = 0 pid = 3401 ++ request struct ++ rq_status = RQ_ACTIVE rq_dev = [8/1] errors = 1 cmd = 0 ++ sector = 2621440 nr_sectors = 16 current_nr_sectors = 8 ++ buffer = 0xf7599000 ++%fore "blue", cont ++bh = 0xf75ca300 ++%fore "black", cont ++ bhtail = 0xf75ca3c0 ++ ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Display the host adapter ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++sh 0xf7e91400 ++%fore "black" ++ Scsi_Host at 0xf7e91400 ++ next = 0x00000000 ++%fore "blue", cont ++host_queue = 0xf7ed5d80 ++%fore "black" ++ ehandler = 0x00000000 eh_wait = 0x00000000 en_notify = 0x00000000 ++ eh_action = 0x00000000 ++ h_active = 0x0 host_wait = 0xc0353ac4 hostt = 0xc034bce0 ++ host_busy = 1 ++ host_failed = 0 extra_bytes = 524 host_no = 0 resetting = 0 ++ max id/lun/channel = [16/8/0] this_id = 7 ++ can_queue = 64 cmd_per_lun = 1 sg_tablesize = 427 u_isa_dma = 0 ++ host_blocked = 0 reverse_ordering = 0 ++ ++%font "typewriter", size 3 ++ Display the scsi device ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++sd 0xf7ed5d80 ++%fore "black" ++ scsi_device at 0xf7ed5d80 ++ next = 0xf7ed5c80 prev = 0x00000000 host = 0xf7e91400 ++ device_busy = 1 ++%fore "blue", cont ++device_queue 0xf7e63a00 ++%fore "black" ++ id/lun/chan = [0/0/0] single_lun = 0 device_blocked = 0 ++ queue_depth = 1 current_tag = 0 scsi_level = 4 ++ IBM DGHS18X 0360 ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Display the Buffer header associated with the command ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++bh 0xf75ca300 ++%fore "black" ++ buffer_head at 0xf75ca300 ++ next 0x00000000 bno 327680 rsec 2621440 size 4096 ++ dev 0x801 rdev 0x801 ++ count 2 state 0x1d [Uptodate Lock Req Mapped] ftime 0x7695e ++ b_list 1 b_reqnext 0xf75ca3c0 b_data 0xf7599000 ++%fore "blue" ++ b_page 0xc1dd6640 ++%fore "black", cont ++ b_this_page 0xf75ca300 b_private 0x00000000 ++ ++ Display the associated page structure ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++page 0xc1dd6640 ++%fore "black" ++ struct page at 0xc1dd6640 ++ next 0xc1dd7300 prev 0xc1dd6240 ++%fore "blue", cont ++addr space 0xf7af04d0 ++%fore "black" ++ index 327680 (offset 0x50000000) ++ count 2 flags PG_referenced PG_lru virtual 0xf7599000 ++ buffers 0xf75ca300 ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Display the Address space associated with the page ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++md 0xf7af04d0 ++%fore "black" ++ 0xf7af04d0 c1dd6240 c1dea740 f7af04d8 f7af04d8 @b]A@'^AX./wX./w ++ 0xf7af04e0 f7af04e0 f7af04e0 00000007 c033b700 `./w`./w.....73@ ++ 0xf7af04f0 ++%fore "blue", cont ++f7af0420 ++%fore "black", cont ++ 00000000 00000000 00000001 ./w............ ++ 0xf7af0500 000001d0 00000000 00000000 f7af050c P............./w ++ 0xf7af0510 f7af050c 00000000 f7a8afa0 00000000 ../w.... /(w.... ++ ++ The structure looks like: ++%size 3 ++ struct address_space { ++ struct list_head clean_pages; /* list of clean pages */ ++ struct list_head dirty_pages; /* list of dirty pages */ ++ struct list_head locked_pages;/* list of locked pages */ ++ unsigned long nrpages; /* number of total pages */ ++ spinlock_t page_lock; /* spinlock protecting them*/ ++ struct address_space_operations *a_ops; /* methods */ ++%fore "blue" ++ struct inode *host; /* owner: inode, block_dev */ ++%fore "black" ++ ++ }; ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Display the inode associated with the address space ++ I think htis is the inode for the block device. ++ ++%font "typewriter", size 3 ++ [1]kdb> ++%fore "blue", cont ++inode f7af0420 ++%fore "black" ++ struct inode at 0xf7af0420 ++ i_ino = 289 i_count = 1 i_dev = 0x801 i_size 4301789184 ++ i_mode = 0x8000 i_nlink = 1 i_rdev = 0x801 ++ i_hash.nxt = 0xf7af0420 i_hash.prv = 0xf7af0420 ++ i_list.nxt = 0xf7af0608 i_list.prv = 0xf7af0068 ++ i_dentry.nxt = 0xf7af0430 i_dentry.prv = 0xf7af0430 ++ i_dirty_buffers.nxt = 0xf7af0438 i_dirty_buffers.prv = 0xf7af0438 ++ i_sb = 0xc201f200 i_op = 0xc03cfdc0 i_data = 0xf7af04d0 nrpages = 6 ++ i_mapping = 0xf7af04d0 ++ i_flags 0x0 i_state 0x0 [] fs specific info @ 0xf7af0540 ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++%page ++ ++Walking IO structures ++ ++ ++%font "typewriter", size 3 ++ Display the page list associated with the inode ++%font "typewriter", size 3 ++ [0]kdb> ++%fore "blue", cont ++inode_pages f7af0420 ++%fore "black" ++CLEAN page_struct index cnt flags ++ 0xc1dd6240 327735 2 0x44 bh 0xf75caae0 bno 327735 ++ [Lock Req Mapped] ++%fore "blue" ++ 0xc1dd6640 327680 2 0x44 bh 0xf75ca300 bno 327680 ++ [Uptodate Lock Req Mapped] ++%fore "black" ++ 0xc1dd7300 327681 2 0x44 bh 0xf75ca3c0 bno 327681 ++ [Uptodate Lock Req Mapped] ++ 0xc1dd6e00 327684 2 0x44 bh 0xf75ca420 bno 327684 ++ [Uptodate Req Mapped] ++ 0xc1de8fc0 4 2 0xc0 bh 0xf7b5ade0 bno 4 ++ [Uptodate Req Mapped] ++ 0xc1dea700 1 2 0x44 bh 0xf7e02740 bno 1 ++ [Uptodate Req Mapped] ++ 0xc1dea740 0 2 0x44 bh 0xf7e028c0 bno 0 ++ [Uptodate Req Mapped] ++DIRTY page_struct index cnt flags ++LOCKED page_struct index cnt flags +--- a/Makefile ++++ b/Makefile +@@ -672,6 +672,7 @@ export mod_strip_cmd + + ifeq ($(KBUILD_EXTMOD),) + core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ ++core-$(CONFIG_KDB) += kdb/ + + vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ + $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ +--- a/drivers/char/keyboard.c ++++ b/drivers/char/keyboard.c +@@ -43,6 +43,9 @@ + #include + #include + #include ++#ifdef CONFIG_KDB ++#include ++#endif /* CONFIG_KDB */ + + extern void ctrl_alt_del(void); + +@@ -1199,6 +1202,13 @@ static void kbd_keycode(unsigned int key + } + #endif + ++#ifdef CONFIG_KDB ++ if (down && !rep && keycode == KEY_PAUSE && kdb_on == 1) { ++ kdb(KDB_REASON_KEYBOARD, 0, get_irq_regs()); ++ return; ++ } ++#endif /* CONFIG_KDB */ ++ + #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ + if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) { + if (!sysrq_down) { +--- a/drivers/hid/usbhid/hid-core.c ++++ b/drivers/hid/usbhid/hid-core.c +@@ -44,6 +44,10 @@ + #define DRIVER_DESC "USB HID core driver" + #define DRIVER_LICENSE "GPL" + ++#ifdef CONFIG_KDB_USB ++#include ++#endif ++ + /* + * Module parameters. + */ +@@ -1032,6 +1036,34 @@ static int usbhid_start(struct hid_devic + USB_INTERFACE_PROTOCOL_KEYBOARD) + usbhid_set_leds(hid); + ++#ifdef CONFIG_KDB_USB ++ /* Attach USB keyboards to kdb */ ++ if (intf->cur_altsetting->desc.bInterfaceProtocol == ++ USB_INTERFACE_PROTOCOL_KEYBOARD) { ++ int ret; ++ struct usbhid_device *usbhid = hid->driver_data; ++ extern void *usb_hcd_get_kdb_poll_func(struct usb_device *udev); ++ extern void * usb_hcd_get_kdb_completion_func(struct usb_device *udev); ++ extern int usb_hcd_check_uhci(struct usb_device *udev); ++ extern kdb_hc_keyboard_attach_t ++ usb_hcd_get_hc_keyboard_attach(struct usb_device *udev); ++ extern kdb_hc_keyboard_detach_t ++ usb_hcd_get_hc_keyboard_detach(struct usb_device *udev); ++ ++ ret = kdb_usb_keyboard_attach(usbhid->urbin, usbhid->inbuf, ++ usb_hcd_get_kdb_poll_func(interface_to_usbdev(intf)), ++ usb_hcd_get_kdb_completion_func(interface_to_usbdev(intf)), ++ usb_hcd_get_hc_keyboard_attach(interface_to_usbdev(intf)), ++ usb_hcd_get_hc_keyboard_detach(interface_to_usbdev(intf)), ++ usbhid->bufsize, ++ NULL); ++ ++ if (ret == -1) ++ printk(": FAILED to register keyboard (%s) " ++ "with KDB\n", hid->phys); ++ } ++#endif /* CONFIG_KDB_USB */ ++ + return 0; + + fail: +@@ -1051,6 +1083,14 @@ static void usbhid_stop(struct hid_devic + + if (WARN_ON(!usbhid)) + return; ++#ifdef CONFIG_KDB_USB ++ /* ++ * If the URB was for a Keyboard, detach it from kdb. ++ * If the URB was for another type of device, just ++ * allow kdb_usb_keyboard_detach() to silently fail. ++ */ ++ kdb_usb_keyboard_detach(usbhid->urbin); ++#endif + + clear_bit(HID_STARTED, &usbhid->iofl); + spin_lock_irq(&usbhid->lock); /* Sync with error handler */ +--- a/drivers/hid/usbhid/usbkbd.c ++++ b/drivers/hid/usbhid/usbkbd.c +@@ -30,6 +30,9 @@ + #include + #include + #include ++#ifdef CONFIG_KDB_USB ++#include ++#endif + + /* + * Version Information +@@ -292,6 +295,16 @@ static int usb_kbd_probe(struct usb_inte + usb_fill_int_urb(kbd->irq, dev, pipe, + kbd->new, (maxp > 8 ? 8 : maxp), + usb_kbd_irq, kbd, endpoint->bInterval); ++ ++#ifdef CONFIG_KDB_USB ++ /* Attach keyboard to kdb */ ++ extern void * usb_hcd_get_kdb_poll_func(struct usb_device *udev); ++ ++ kdb_usb_keyboard_attach(kbd->irq, kbd->new, ++ usb_hcd_get_kdb_poll_func(dev)); ++ ++#endif /* CONFIG_KDB_USB */ ++ + kbd->irq->transfer_dma = kbd->new_dma; + kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + +@@ -329,6 +342,10 @@ static void usb_kbd_disconnect(struct us + + usb_set_intfdata(intf, NULL); + if (kbd) { ++#ifdef CONFIG_KDB_USB ++ /* Detach the keyboard from kdb */ ++ kdb_usb_keyboard_detach(kbd->irq); ++#endif /* CONFIG_KDB_USB */ + usb_kill_urb(kbd->irq); + input_unregister_device(kbd->dev); + usb_kbd_free_mem(interface_to_usbdev(intf), kbd); +--- a/drivers/serial/8250.c ++++ b/drivers/serial/8250.c +@@ -44,11 +44,26 @@ + #include + + #include "8250.h" +- + #ifdef CONFIG_SPARC + #include "suncore.h" + #endif + ++#ifdef CONFIG_KDB ++#include ++/* ++ * kdb_serial_line records the serial line number of the first serial console. ++ * NOTE: The kernel ignores characters on the serial line unless a user space ++ * program has opened the line first. To enter kdb before user space has opened ++ * the serial line, you can use the 'kdb=early' flag to lilo and set the ++ * appropriate breakpoints. ++ */ ++ ++static int kdb_serial_line = -1; ++static const char *kdb_serial_ptr = kdb_serial_str; ++#else ++#define KDB_8250() 0 ++#endif /* CONFIG_KDB */ ++ + /* + * Configuration: + * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option +@@ -1403,6 +1418,20 @@ receive_chars(struct uart_8250_port *up, + * just force the read character to be 0 + */ + ch = 0; ++#ifdef CONFIG_KDB ++ if ((up->port.line == kdb_serial_line) && kdb_on == 1) { ++ if (ch == *kdb_serial_ptr) { ++ if (!(*++kdb_serial_ptr)) { ++ atomic_inc(&kdb_8250); ++ kdb(KDB_REASON_KEYBOARD, 0, get_irq_regs()); ++ atomic_dec(&kdb_8250); ++ kdb_serial_ptr = kdb_serial_str; ++ break; ++ } ++ } else ++ kdb_serial_ptr = kdb_serial_str; ++ } ++#endif /* CONFIG_KDB */ + + flag = TTY_NORMAL; + up->port.icount.rx++; +@@ -2776,7 +2805,7 @@ serial8250_console_write(struct console + if (up->port.sysrq) { + /* serial8250_handle_port() already took the lock */ + locked = 0; +- } else if (oops_in_progress) { ++ } else if (oops_in_progress || KDB_8250()) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); +@@ -2834,6 +2863,30 @@ static int __init serial8250_console_set + if (!port->iobase && !port->membase) + return -ENODEV; + ++#ifdef CONFIG_KDB ++ /* ++ * Remember the line number of the first serial ++ * console. We'll make this the kdb serial console too. ++ */ ++ if (co && kdb_serial_line == -1) { ++ kdb_serial_line = co->index; ++ kdb_serial.io_type = port->iotype; ++ switch (port->iotype) { ++ case SERIAL_IO_MEM: ++#ifdef SERIAL_IO_MEM32 ++ case SERIAL_IO_MEM32: ++#endif ++ kdb_serial.iobase = (unsigned long)(port->membase); ++ kdb_serial.ioreg_shift = port->regshift; ++ break; ++ default: ++ kdb_serial.iobase = port->iobase; ++ kdb_serial.ioreg_shift = 0; ++ break; ++ } ++ } ++#endif /* CONFIG_KDB */ ++ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + +--- a/drivers/serial/8250_early.c ++++ b/drivers/serial/8250_early.c +@@ -38,6 +38,11 @@ + #include + #endif + ++#ifdef CONFIG_KDB ++#include ++static int kdb_serial_line = -1; ++#endif /* CONFIG_KDB */ ++ + struct early_serial8250_device { + struct uart_port port; + char options[16]; /* e.g., 115200n8 */ +@@ -231,6 +236,30 @@ int __init setup_early_serial8250_consol + + register_console(&early_serial8250_console); + ++#ifdef CONFIG_KDB ++ /* ++ * Remember the line number of the first serial ++ * console. We'll make this the kdb serial console too. ++ */ ++ if (kdb_serial_line == -1) { ++ kdb_serial_line = early_serial8250_console.index; ++ kdb_serial.io_type = early_device.port.iotype; ++ switch (early_device.port.iotype) { ++ case SERIAL_IO_MEM: ++#ifdef SERIAL_IO_MEM32 ++ case SERIAL_IO_MEM32: ++#endif ++ kdb_serial.iobase = (unsigned long)(early_device.port.membase); ++ kdb_serial.ioreg_shift = early_device.port.regshift; ++ break; ++ default: ++ kdb_serial.iobase = early_device.port.iobase; ++ kdb_serial.ioreg_shift = 0; ++ break; ++ } ++ } ++#endif /* CONFIG_KDB */ ++ + return 0; + } + +--- a/drivers/serial/sn_console.c ++++ b/drivers/serial/sn_console.c +@@ -48,6 +48,22 @@ + #include /* for mdelay */ + #include + #include ++#ifdef CONFIG_KDB ++#include ++#include ++#include ++/* ++ * kdb_serial_line records the serial line number of the first serial console. ++ * NOTE: The kernel ignores characters on the serial line unless a user space ++ * program has opened the line first. To enter kdb before user space has opened ++ * the serial line, you can use the 'kdb=early' flag to lilo and set the ++ * appropriate breakpoints. ++ */ ++ ++static int kdb_serial_line = -1; ++static char *kdb_serial_ptr = (char *)kdb_serial_str; ++#endif /* CONFIG_KDB */ ++ + + #include + #include +@@ -485,6 +501,26 @@ sn_receive_chars(struct sn_cons_port *po + "obtaining data from the console (0x%0x)\n", ch); + break; + } ++#ifdef CONFIG_KDB ++ if (kdb_on == 1) { ++ if (ch == *kdb_serial_ptr) { ++ if (!(*++kdb_serial_ptr)) { ++ spin_unlock_irqrestore(&port->sc_port.lock, flags); ++ if (!get_irq_regs()) { ++ KDB_STATE_SET(KEYBOARD); ++ KDB_ENTER(); /* to get some registers */ ++ } else ++ kdb(KDB_REASON_KEYBOARD, 0, get_irq_regs()); ++ kdb_serial_ptr = (char *)kdb_serial_str; ++ spin_lock_irqsave(&port->sc_port.lock, flags); ++ break; ++ } ++ } ++ else ++ kdb_serial_ptr = (char *)kdb_serial_str; ++ } ++#endif /* CONFIG_KDB */ ++ + #ifdef CONFIG_MAGIC_SYSRQ + if (sysrq_requested) { + unsigned long sysrq_timeout = sysrq_requested + HZ*5; +@@ -1008,6 +1044,15 @@ sn_sal_console_write(struct console *co, + */ + static int sn_sal_console_setup(struct console *co, char *options) + { ++#ifdef CONFIG_KDB ++ /* ++ * Remember the line number of the first serial ++ * console. We'll make this the kdb serial console too. ++ */ ++ if (kdb_serial_line == -1) { ++ kdb_serial_line = co->index; ++ } ++#endif /* CONFIG_KDB */ + return 0; + } + +@@ -1083,3 +1128,31 @@ static int __init sn_sal_serial_console_ + } + + console_initcall(sn_sal_serial_console_init); ++ ++#ifdef CONFIG_KDB ++int ++l1_control_in_polled(int offset) ++{ ++ int sal_call_status = 0, input; ++ int ret = 0; ++ if (offset == UART_LSR) { ++ ret = (UART_LSR_THRE | UART_LSR_TEMT); /* can send anytime */ ++ sal_call_status = ia64_sn_console_check(&input); ++ if (!sal_call_status && input) { ++ /* input pending */ ++ ret |= UART_LSR_DR; ++ } ++ } ++ return ret; ++} ++ ++int ++l1_serial_in_polled(void) ++{ ++ int ch; ++ if (!ia64_sn_console_getc(&ch)) ++ return ch; ++ else ++ return 0; ++} ++#endif /* CONFIG_KDB */ +--- a/drivers/usb/core/hcd.c ++++ b/drivers/usb/core/hcd.c +@@ -40,6 +40,9 @@ + #include + #include + #include ++#ifdef CONFIG_KDB_USB ++#include ++#endif + + #include + +@@ -2271,6 +2274,74 @@ usb_hcd_platform_shutdown(struct platfor + } + EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown); + ++#ifdef CONFIG_KDB_USB ++void * ++usb_hcd_get_kdb_poll_func(struct usb_device *udev) ++{ ++ struct usb_hcd *hcd = bus_to_hcd(udev->bus); ++ ++ if (hcd && hcd->driver) ++ return (void *)(hcd->driver->kdb_poll_char); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL (usb_hcd_get_kdb_poll_func); ++ ++void * ++usb_hcd_get_kdb_completion_func(struct usb_device *udev) ++{ ++ struct usb_hcd *hcd = bus_to_hcd(udev->bus); ++ ++ if (hcd && hcd->driver) ++ return (void *)(hcd->driver->kdb_completion); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL (usb_hcd_get_kdb_completion_func); ++ ++int ++usb_hcd_check_uhci(struct usb_device *udev) ++{ ++ struct usb_hcd *hcd = bus_to_hcd(udev->bus); ++ ++ if (hcd && hcd->driver){ ++ if (!(strcmp(hcd->driver->description, "uhci_hcd"))) ++ return 1; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL (usb_hcd_check_uhci); ++ ++kdb_hc_keyboard_attach_t ++usb_hcd_get_hc_keyboard_attach(struct usb_device *udev) ++{ ++ struct usb_hcd *hcd = bus_to_hcd(udev->bus); ++ ++ if (hcd && hcd->driver){ ++ return hcd->driver->kdb_hc_keyboard_attach; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL (usb_hcd_get_hc_keyboard_attach); ++ ++kdb_hc_keyboard_detach_t ++usb_hcd_get_hc_keyboard_detach(struct usb_device *udev) ++{ ++ struct usb_hcd *hcd = bus_to_hcd(udev->bus); ++ ++ if (hcd && hcd->driver){ ++ return hcd->driver->kdb_hc_keyboard_detach; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL (usb_hcd_get_hc_keyboard_detach); ++ ++ ++#endif /* CONFIG_KDB_USB */ ++ + /*-------------------------------------------------------------------------*/ + + #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) +--- a/drivers/usb/core/hcd.h ++++ b/drivers/usb/core/hcd.h +@@ -22,6 +22,9 @@ + #ifdef __KERNEL__ + + #include ++#ifdef CONFIG_KDB_USB ++#include ++#endif + + #define MAX_TOPO_LEVEL 6 + +@@ -287,6 +290,14 @@ struct hc_driver { + int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); + int (*reset_device)(struct usb_hcd *, struct usb_device *); ++ ++#ifdef CONFIG_KDB_USB ++ /* KDB poll function for this HC */ ++ int (*kdb_poll_char)(struct urb *urb); ++ void (*kdb_completion)(struct urb *urb); ++ kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach; ++ kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach; ++#endif /* CONFIG_KDB_USB */ + }; + + extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -1092,6 +1092,48 @@ static int ehci_get_frame (struct usb_hc + ehci->periodic_size; + } + ++#ifdef CONFIG_KDB_USB ++ ++int ++ehci_kdb_poll_char(struct urb *urb) ++{ ++ struct ehci_hcd *ehci; ++ ++ /* just to make sure */ ++ if (!urb || !urb->dev || !urb->dev->bus) ++ return -1; ++ ++ ehci = (struct ehci_hcd *) hcd_to_ehci(bus_to_hcd(urb->dev->bus)); ++ ++ /* make sure */ ++ if (!ehci) ++ return -1; ++ ++ if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) ++ return -1; ++ ++ /* ++ * If ehci->lock is held coming into this routine, it could ++ * mean KDB was entered while the HC driver was in the midst ++ * of processing URBs. Therefore it could be dangerous to ++ * processes URBs from this poll routine. And, we can't wait on ++ * the lock since we are in KDB and kernel threads (including the ++ * one holding the lock) are suspended. ++ * So, we punt and return an error. Keyboards attached to this ++ * HC will not be useable from KDB at this time. ++ */ ++ if (spin_is_locked(&ehci->lock)) ++ return -EBUSY; ++ ++ /* processes the URB */ ++ if (qh_completions_kdb(ehci, urb->hcpriv, urb)) ++ return 0; ++ ++ return -1; ++} ++ ++#endif /* CONFIG_KDB_USB */ ++ + /*-------------------------------------------------------------------------*/ + + MODULE_DESCRIPTION(DRIVER_DESC); +--- a/drivers/usb/host/ehci-pci.c ++++ b/drivers/usb/host/ehci-pci.c +@@ -22,6 +22,10 @@ + #error "This file is PCI bus glue. CONFIG_PCI must be defined." + #endif + ++#ifdef CONFIG_KDB_USB ++#include ++#endif ++ + /*-------------------------------------------------------------------------*/ + + /* called after powerup, by probe or system-pm "wakeup" */ +@@ -412,6 +416,10 @@ static const struct hc_driver ehci_pci_h + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, ++ ++#ifdef CONFIG_KDB_USB ++ .kdb_poll_char = ehci_kdb_poll_char, ++#endif + }; + + /*-------------------------------------------------------------------------*/ +--- a/drivers/usb/host/ehci-q.c ++++ b/drivers/usb/host/ehci-q.c +@@ -577,6 +577,228 @@ halt: + return count; + } + ++#ifdef CONFIG_KDB_USB ++/* ++ * This routine is basically a copy of qh_completions() for use by KDB. ++ * It is modified to only work on qtds which are associated ++ * with 'kdburb'. Also, there are some fixups related to locking. ++ */ ++unsigned ++qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh *qh, struct urb *kdburb) ++{ ++ struct ehci_qtd *last = NULL, *end = qh->dummy; ++ struct list_head *entry, *tmp; ++ int last_status = -EINPROGRESS; ++ int stopped; ++ unsigned count = 0; ++ int do_status = 0; ++ u8 state; ++ u32 halt = HALT_BIT(ehci); ++ ++ /* verify params are valid */ ++ if (!qh || !kdburb) ++ return 0; ++ ++ if (unlikely (list_empty (&qh->qtd_list))) ++ return count; ++ ++ /* completions (or tasks on other cpus) must never clobber HALT ++ * till we've gone through and cleaned everything up, even when ++ * they add urbs to this qh's queue or mark them for unlinking. ++ * ++ * NOTE: unlinking expects to be done in queue order. ++ */ ++ state = qh->qh_state; ++ qh->qh_state = QH_STATE_COMPLETING; ++ stopped = (state == QH_STATE_IDLE); ++ ++ /* remove de-activated QTDs from front of queue. ++ * after faults (including short reads), cleanup this urb ++ * then let the queue advance. ++ * if queue is stopped, handles unlinks. ++ */ ++ list_for_each_safe (entry, tmp, &qh->qtd_list) { ++ struct ehci_qtd *qtd; ++ struct urb *urb; ++ u32 token = 0; ++ int qtd_status; ++ ++ qtd = list_entry (entry, struct ehci_qtd, qtd_list); ++ urb = qtd->urb; ++ ++ if (urb != kdburb) ++ continue; ++ ++ /* clean up any state from previous QTD ...*/ ++ if (last) { ++ if (likely (last->urb != urb)) { ++ /* ++ * Lock hackery here... ++ * ehci_urb_done() makes the assumption ++ * that it's called with ehci->lock held. ++ * So, lock it if it isn't already. ++ */ ++ if (!spin_is_locked(&ehci->lock)) ++ spin_lock(&ehci->lock); ++ ++ ehci_urb_done(ehci, last->urb, last_status); ++ ++ /* ++ * ehci_urb_done() releases and reacquires ++ * ehci->lock, so release it here. ++ */ ++ if (spin_is_locked(&ehci->lock)) ++ spin_unlock (&ehci->lock); ++ ++ count++; ++ } ++ ehci_qtd_free (ehci, last); ++ last = NULL; ++ last_status = -EINPROGRESS; ++ } ++ ++ /* ignore urbs submitted during completions we reported */ ++ if (qtd == end) ++ break; ++ ++ /* hardware copies qtd out of qh overlay */ ++ rmb (); ++ token = hc32_to_cpu(ehci, qtd->hw_token); ++ ++ /* always clean up qtds the hc de-activated */ ++ if ((token & QTD_STS_ACTIVE) == 0) { ++ ++ if ((token & QTD_STS_HALT) != 0) { ++ stopped = 1; ++ ++ /* magic dummy for some short reads; qh won't advance. ++ * that silicon quirk can kick in with this dummy too. ++ */ ++ } else if (IS_SHORT_READ (token) ++ && !(qtd->hw_alt_next ++ & EHCI_LIST_END(ehci))) { ++ stopped = 1; ++ goto halt; ++ } ++ ++ /* stop scanning when we reach qtds the hc is using */ ++ } else if (likely (!stopped ++ && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) { ++ break; ++ ++ } else { ++ stopped = 1; ++ ++ if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) ++ last_status = -ESHUTDOWN; ++ ++ /* ignore active urbs unless some previous qtd ++ * for the urb faulted (including short read) or ++ * its urb was canceled. we may patch qh or qtds. ++ */ ++ if (likely(last_status == -EINPROGRESS && ++ !urb->unlinked)) ++ continue; ++ ++ /* issue status after short control reads */ ++ if (unlikely (do_status != 0) ++ && QTD_PID (token) == 0 /* OUT */) { ++ do_status = 0; ++ continue; ++ } ++ ++ /* token in overlay may be most current */ ++ if (state == QH_STATE_IDLE ++ && cpu_to_hc32(ehci, qtd->qtd_dma) ++ == qh->hw_current) ++ token = hc32_to_cpu(ehci, qh->hw_token); ++ ++ /* force halt for unlinked or blocked qh, so we'll ++ * patch the qh later and so that completions can't ++ * activate it while we "know" it's stopped. ++ */ ++ if ((halt & qh->hw_token) == 0) { ++halt: ++ qh->hw_token |= halt; ++ wmb (); ++ } ++ } ++ ++ /* remove it from the queue */ ++ qtd_status = qtd_copy_status(ehci, urb, qtd->length, token); ++ if (unlikely(qtd_status == -EREMOTEIO)) { ++ do_status = (!urb->unlinked && ++ usb_pipecontrol(urb->pipe)); ++ qtd_status = 0; ++ } ++ if (likely(last_status == -EINPROGRESS)) ++ last_status = qtd_status; ++ ++ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { ++ last = list_entry (qtd->qtd_list.prev, ++ struct ehci_qtd, qtd_list); ++ last->hw_next = qtd->hw_next; ++ } ++ list_del (&qtd->qtd_list); ++ last = qtd; ++ } ++ ++ /* last urb's completion might still need calling */ ++ if (likely (last != NULL)) { ++ /* ++ * Lock hackery here... ++ * ehci_urb_done() makes the assumption ++ * that it's called with ehci->lock held. ++ * So, lock it if it isn't already. ++ */ ++ if (!spin_is_locked(&ehci->lock)) ++ spin_lock(&ehci->lock); ++ ++ ehci_urb_done(ehci, last->urb, last_status); ++ ++ /* ++ * ehci_urb_done() releases and reacquires ++ * ehci->lock, so release it here. ++ */ ++ if (spin_is_locked(&ehci->lock)) ++ spin_unlock (&ehci->lock); ++ ++ count++; ++ ehci_qtd_free (ehci, last); ++ } ++ ++ /* restore original state; caller must unlink or relink */ ++ qh->qh_state = state; ++ ++ /* be sure the hardware's done with the qh before refreshing ++ * it after fault cleanup, or recovering from silicon wrongly ++ * overlaying the dummy qtd (which reduces DMA chatter). ++ */ ++ if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) { ++ switch (state) { ++ case QH_STATE_IDLE: ++ qh_refresh(ehci, qh); ++ break; ++ case QH_STATE_LINKED: ++ /* should be rare for periodic transfers, ++ * except maybe high bandwidth ... ++ */ ++ if ((cpu_to_hc32(ehci, QH_SMASK) ++ & qh->hw_info2) != 0) { ++ intr_deschedule (ehci, qh); ++ (void) qh_schedule (ehci, qh); ++ } else ++ unlink_async (ehci, qh); ++ break; ++ /* otherwise, unlink already started */ ++ } ++ } ++ ++ return count; ++} ++ ++#endif /* CONFIG_KDB_USB */ ++ + /*-------------------------------------------------------------------------*/ + + // high bandwidth multiplier, as encoded in highspeed endpoint descriptors +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -987,6 +987,73 @@ static int ohci_restart (struct ohci_hcd + + /*-------------------------------------------------------------------------*/ + ++#ifdef CONFIG_KDB_USB ++ ++int ++ohci_kdb_poll_char(struct urb *urb) ++{ ++ struct ohci_hcd *ohci; ++ struct ohci_regs * regs; ++ ++ /* just to make sure */ ++ if (!urb || !urb->dev || !urb->dev->bus) ++ return -1; ++ ++ ohci = (struct ohci_hcd *) hcd_to_ohci(bus_to_hcd(urb->dev->bus)); ++ ++ /* make sure */ ++ if (!ohci || !ohci->hcca) ++ return -1; ++ ++ if (!HC_IS_RUNNING (ohci_to_hcd(ohci)->state)) ++ return -1; ++ ++ /* ++ * If ohci->lock is held coming into this routine, it could ++ * mean KDB was entered while the HC driver was in the midst ++ * of processing URBs. Therefore it could be dangerous to ++ * processes URBs from this poll routine. And, we can't wait on ++ * the lock since we are in KDB and kernel threads (including the ++ * one holding the lock) are suspended. ++ * So, we punt and return an error. Keyboards attached to this ++ * HC will not be useable from KDB at this time. ++ */ ++ if (spin_is_locked(&ohci->lock)) ++ return -EBUSY; ++ ++ regs = ohci->regs; ++ ++ /* if the urb is not currently in progress resubmit it */ ++ if (urb->status != -EINPROGRESS) { ++ ++ if (usb_submit_urb (urb, GFP_ATOMIC)) ++ return -1; ++ ++ /* make sure the HC registers are set correctly */ ++ ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrenable); ++ ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrstatus); ++ ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable); ++ ++ // flush those pci writes ++ (void) ohci_readl (ohci, &ohci->regs->control); ++ } ++ ++ if (ohci->hcca->done_head) { ++ dl_done_list_kdb (ohci, urb); ++ ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrstatus); ++ // flush the pci write ++ (void) ohci_readl (ohci, &ohci->regs->control); ++ ++ return 0; ++ } ++ ++ return -1; ++} ++ ++#endif /* CONFIG_KDB_USB */ ++ ++/*-------------------------------------------------------------------------*/ ++ + MODULE_AUTHOR (DRIVER_AUTHOR); + MODULE_DESCRIPTION(DRIVER_DESC); + MODULE_LICENSE ("GPL"); +--- a/drivers/usb/host/ohci-pci.c ++++ b/drivers/usb/host/ohci-pci.c +@@ -21,6 +21,10 @@ + #include + #include + ++#ifdef CONFIG_KDB_USB ++#include ++#endif ++ + + /* constants used to work around PM-related transfer + * glitches in some AMD 700 series southbridges +@@ -387,6 +391,7 @@ static int __devinit ohci_pci_start (str + ohci_err (ohci, "can't start\n"); + ohci_stop (hcd); + } ++ + return ret; + } + +@@ -485,6 +490,9 @@ static const struct hc_driver ohci_pci_h + .bus_resume = ohci_bus_resume, + #endif + .start_port_reset = ohci_start_port_reset, ++#ifdef CONFIG_KDB_USB ++ .kdb_poll_char = ohci_kdb_poll_char, ++#endif + }; + + /*-------------------------------------------------------------------------*/ +--- a/drivers/usb/host/ohci-q.c ++++ b/drivers/usb/host/ohci-q.c +@@ -1134,3 +1134,65 @@ dl_done_list (struct ohci_hcd *ohci) + td = td_next; + } + } ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_KDB_USB ++static void ++dl_done_list_kdb (struct ohci_hcd *ohci, struct urb *kdburb) ++{ ++ struct td *td = dl_reverse_done_list (ohci); ++ ++ while (td) { ++ struct td *td_next = td->next_dl_td; ++ struct urb *urb = td->urb; ++ urb_priv_t *urb_priv = urb->hcpriv; ++ struct ed *ed = td->ed; ++ ++ if (urb != kdburb) { ++ td = td_next; ++ continue; ++ } ++ ++ /* update URB's length and status from TD */ ++ td_done (ohci, urb, td); ++ urb_priv->td_cnt++; ++ ++ /* If all this urb's TDs are done, just resubmit it */ ++ if (urb_priv->td_cnt == urb_priv->length) { ++ urb->actual_length = 0; ++ urb->status = -EINPROGRESS; ++ td_submit_urb (ohci, urb); ++ } ++ ++ /* clean schedule: unlink EDs that are no longer busy */ ++ if (list_empty (&ed->td_list)) { ++ if (ed->state == ED_OPER) ++ start_ed_unlink (ohci, ed); ++ ++ /* ... reenabling halted EDs only after fault cleanup */ ++ } else if ((ed->hwINFO & cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE)) ++ == cpu_to_hc32 (ohci, ED_SKIP)) { ++ td = list_entry (ed->td_list.next, struct td, td_list); ++ if (!(td->hwINFO & cpu_to_hc32 (ohci, TD_DONE))) { ++ ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP); ++ /* ... hc may need waking-up */ ++ switch (ed->type) { ++ case PIPE_CONTROL: ++ ohci_writel (ohci, OHCI_CLF, ++ &ohci->regs->cmdstatus); ++ break; ++ case PIPE_BULK: ++ ohci_writel (ohci, OHCI_BLF, ++ &ohci->regs->cmdstatus); ++ break; ++ } ++ } ++ } ++ ++ td = td_next; ++ } ++} ++ ++#endif /* CONFIG_KDB_USB */ +--- a/drivers/usb/host/uhci-hcd.c ++++ b/drivers/usb/host/uhci-hcd.c +@@ -50,6 +50,11 @@ + #include "uhci-hcd.h" + #include "pci-quirks.h" + ++#ifdef CONFIG_KDB_USB ++#include ++#include ++#endif ++ + /* + * Version Information + */ +@@ -461,6 +466,213 @@ static irqreturn_t uhci_irq(struct usb_h + return IRQ_HANDLED; + } + ++#ifdef CONFIG_KDB_USB ++/* Unlink KDB QH from hardware and software scheduler */ ++static void kdb_unlink_uhci_qh(struct urb *urb, struct uhci_qh *qh) ++{ ++ unsigned long flags; ++ struct uhci_hcd *uhci; ++ ++ uhci = (struct uhci_hcd *) hcd_to_uhci(bus_to_hcd(urb->dev->bus)); ++ ++ spin_lock_irqsave(&uhci->lock, flags); ++ unlink_interrupt(NULL, qh); ++ list_del(&(qh->node)); ++ spin_unlock_irqrestore(&uhci->lock, flags); ++ ++} ++ ++static int uhci_kdb_poll_char(struct urb *urb) ++{ ++ if (!urb) /* can happen if no keyboard attached */ ++ return -1; ++ ++ return uhci_check_kdb_uhci_qh(kdb_uhci_keyboard_get_qh(urb)); ++} ++ ++/* Only 1 UHCI Keyboard supported */ ++static inline void kdb_usb_fill_int_urb (struct urb *urb, ++ struct usb_device *dev, ++ unsigned int pipe, ++ void *transfer_buffer, ++ int buffer_length, ++ usb_complete_t complete_fn, ++ void *context, ++ int interval) ++{ ++ urb->dev = dev; ++ urb->pipe = pipe; ++ urb->transfer_buffer = transfer_buffer; ++ urb->transfer_buffer_length = buffer_length; ++ urb->complete = complete_fn; ++ urb->context = context; ++ urb->interval = interval; ++ urb->start_frame = -1; ++} ++ ++static int kdb_uhci_keyboard_attach(int i, unsigned int usbhid_bufsize) ++{ ++ struct urb *kdb_urb; ++ unsigned char *kdb_buffer; ++ dma_addr_t uhci_inbuf_dma; ++ struct urb *hid_inurb = kdb_usb_kbds[i].urb; ++ int ret = -1; ++ ++ kdb_usb_kbds[i].hid_urb = hid_inurb; ++ ++ kdb_urb = NULL; ++ kdb_buffer = NULL; ++ if (!(kdb_buffer = usb_buffer_alloc(hid_inurb->dev, ++ usbhid_bufsize, GFP_ATOMIC, ++ &uhci_inbuf_dma))) ++ goto out; ++ ++ if (!(kdb_urb = usb_alloc_urb(0, GFP_KERNEL))) ++ goto out; ++ ++ kdb_usb_fill_int_urb(kdb_urb, ++ hid_inurb->dev, ++ hid_inurb->pipe, ++ kdb_buffer, ++ hid_inurb->transfer_buffer_length, ++ hid_inurb->complete, ++ hid_inurb->context, ++ hid_inurb->interval ++ ); ++ ++ (kdb_urb)->transfer_dma = uhci_inbuf_dma; ++ (kdb_urb)->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ kdb_usb_kbds[i].urb = kdb_urb; ++ kdb_usb_kbds[i].buffer = kdb_buffer; ++ ++ if (usb_submit_urb(kdb_urb, GFP_ATOMIC)){ ++ kdb_usb_keyboard_detach(hid_inurb); ++ goto out; ++ } ++ /* Remove KDB special URB from endpoin queue to ++ * prevent hang during hid_disconnect(). ++ */ ++ list_del(&(kdb_urb->urb_list)); ++ ++ ret = 0; ++ return ret; ++out: ++ /* Some Error Cleanup */ ++ ret = -1; ++ printk("KDB: Error, UHCI Keyboard HID won't work!\n"); ++ ++ if (kdb_buffer) ++ usb_buffer_free(hid_inurb->dev, ++ usbhid_bufsize, kdb_buffer, ++ uhci_inbuf_dma); ++ ++ if (kdb_urb) ++ usb_free_urb(kdb_urb); ++ ++ return ret; ++} ++ ++static int kdb_uhci_keyboard_detach(struct urb *urb, int i) ++{ ++ int ret; ++ ++ if (kdb_usb_kbds[i].qh && (kdb_usb_kbds[i].hid_urb == urb)) { ++ /* UHCI keyboard */ ++ kdb_unlink_uhci_qh(kdb_usb_kbds[i].urb, kdb_usb_kbds[i].qh); ++ ret = 0; ++ } ++ ret = -1; ++ ++ return ret; ++} ++ ++/* Check if URB is managed by KDB code */ ++static int kdb_uhci_keyboard_urb(struct urb *urb) ++{ ++ int i; ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].urb == urb) ++ return i; ++ } ++ return -1; ++} ++ ++/* Check if UHCI QH is managed by KDB code */ ++static int kdb_uhci_keyboard_check_uhci_qh(struct uhci_qh *qh) ++{ ++ int i; ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].qh == qh) ++ return i; ++ } ++ return -1; ++} ++ ++/* Set UHCI QH using URB pointer */ ++static int kdb_uhci_keyboard_set_qh(struct urb *urb, struct uhci_qh *qh) ++{ ++ int i; ++ ++ i = kdb_uhci_keyboard_urb(urb); ++ if (i != -1) ++ kdb_usb_kbds[i].qh = qh; ++ ++ return 0; ++} ++ ++/* Get UHCI QH using URB pointer */ ++static struct uhci_qh *kdb_uhci_keyboard_get_qh(struct urb *urb) ++{ ++ int i; ++ ++ i = kdb_uhci_keyboard_urb(urb); ++ if (i != -1) ++ return kdb_usb_kbds[i].qh; ++ ++ return NULL; ++} ++ ++/* Set UHCI hid_event using URB pointer */ ++static int kdb_uhci_keyboard_set_hid_event(struct urb *urb, int hid_event) ++{ ++ int i; ++ ++ i = kdb_uhci_keyboard_urb(urb); ++ if (i != -1) ++ kdb_usb_kbds[i].kdb_hid_event = hid_event; ++ ++ return 0; ++} ++/* Get UHCI hid_event using URB pointer */ ++static int kdb_uhci_keyboard_get_hid_event(struct urb *urb) ++{ ++ int i; ++ ++ i = kdb_uhci_keyboard_urb(urb); ++ if (i != -1) ++ return kdb_usb_kbds[i].kdb_hid_event; ++ ++ return 0; ++} ++ ++/* Set UHCI hid_event using UHCI QH pointer */ ++static int kdb_uhci_keyboard_set_hid_event_qh(struct uhci_qh *qh, int hid_event) ++{ ++ int i; ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].qh == qh){ ++ kdb_usb_kbds[i].kdb_hid_event = hid_event; ++ return i; ++ } ++ } ++ return -1; ++} ++#endif ++ + /* + * Store the current frame number in uhci->frame_number if the controller + * is runnning. Expand from 11 bits (of which we use only 10) to a +@@ -935,6 +1147,12 @@ static const struct hc_driver uhci_drive + + .hub_status_data = uhci_hub_status_data, + .hub_control = uhci_hub_control, ++#ifdef CONFIG_KDB_USB ++ .kdb_poll_char = uhci_kdb_poll_char, ++ .kdb_completion = kdb_uhci_urb_complete, ++ .kdb_hc_keyboard_attach = kdb_uhci_keyboard_attach, ++ .kdb_hc_keyboard_detach = kdb_uhci_keyboard_detach, ++#endif + }; + + static const struct pci_device_id uhci_pci_ids[] = { { +--- a/drivers/usb/host/uhci-q.c ++++ b/drivers/usb/host/uhci-q.c +@@ -25,6 +25,17 @@ + * games with the FSBR code to make sure we get the correct order in all + * the cases. I don't think it's worth the effort + */ ++#ifdef CONFIG_KDB_USB ++/* KDB HID QH, managed by KDB code */ ++static int kdb_uhci_keyboard_check_uhci_qh(struct uhci_qh *qh); ++static int kdb_uhci_keyboard_set_qh(struct urb *urb, struct uhci_qh *qh); ++static struct uhci_qh *kdb_uhci_keyboard_get_qh(struct urb *urb); ++static int kdb_uhci_keyboard_set_hid_event(struct urb *urb, int hid_event); ++static int kdb_uhci_keyboard_get_hid_event(struct urb *urb); ++static int kdb_uhci_keyboard_set_hid_event_qh(struct uhci_qh *qh, int hid_event); ++static int kdb_uhci_keyboard_urb(struct urb *urb); ++#endif ++ + static void uhci_set_next_interrupt(struct uhci_hcd *uhci) + { + if (uhci->is_stopped) +@@ -288,6 +299,58 @@ static struct uhci_qh *uhci_alloc_qh(str + return qh; + } + ++#ifdef CONFIG_KDB_USB ++/* ++ * Same as uhci_alloc_qh execpt it doesn't change to hep->hcpriv ++ */ ++static struct uhci_qh *kdb_uhci_alloc_qh(struct uhci_hcd *uhci, ++ struct usb_device *udev, struct usb_host_endpoint *hep) ++{ ++ dma_addr_t dma_handle; ++ struct uhci_qh *qh; ++ ++ qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle); ++ if (!qh) ++ return NULL; ++ ++ memset(qh, 0, sizeof(*qh)); ++ qh->dma_handle = dma_handle; ++ ++ qh->element = UHCI_PTR_TERM; ++ qh->link = UHCI_PTR_TERM; ++ ++ INIT_LIST_HEAD(&qh->queue); ++ INIT_LIST_HEAD(&qh->node); ++ ++ if (udev) { /* Normal QH */ ++ qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++ if (qh->type != USB_ENDPOINT_XFER_ISOC) { ++ qh->dummy_td = uhci_alloc_td(uhci); ++ if (!qh->dummy_td) { ++ dma_pool_free(uhci->qh_pool, qh, dma_handle); ++ return NULL; ++ } ++ } ++ qh->state = QH_STATE_IDLE; ++ qh->hep = hep; ++ qh->udev = udev; ++ ++ if (qh->type == USB_ENDPOINT_XFER_INT || ++ qh->type == USB_ENDPOINT_XFER_ISOC) ++ qh->load = usb_calc_bus_time(udev->speed, ++ usb_endpoint_dir_in(&hep->desc), ++ qh->type == USB_ENDPOINT_XFER_ISOC, ++ le16_to_cpu(hep->desc.wMaxPacketSize)) ++ / 1000 + 1; ++ ++ } else { /* Skeleton QH */ ++ qh->state = QH_STATE_ACTIVE; ++ qh->type = -1; ++ } ++ return qh; ++} ++#endif ++ + static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) + { + WARN_ON(qh->state != QH_STATE_IDLE && qh->udev); +@@ -1394,6 +1457,21 @@ static int uhci_urb_enqueue(struct usb_h + if (!urbp) + goto done; + ++#ifdef CONFIG_KDB_USB ++ /* Always allocate new QH for KDB URB. ++ * KDB HQ will be managed by KDB poll code not by ++ * UHCI HCD Driver. ++ */ ++ if (kdb_uhci_keyboard_urb(urb) != -1){ ++ /* KDB urb will be enqued only once */ ++ kdb_uhci_keyboard_set_qh(urb, NULL); ++ qh = kdb_uhci_alloc_qh(uhci, urb->dev, urb->ep); ++ if (!qh) ++ goto err_no_qh; ++ kdb_uhci_keyboard_set_qh(urb, qh); ++ } else ++#endif ++ + if (urb->ep->hcpriv) + qh = urb->ep->hcpriv; + else { +@@ -1641,6 +1719,14 @@ static int uhci_advance_check(struct uhc + int ret = 1; + unsigned status; + ++#ifdef CONFIG_KDB_USB ++ /* Don't manage KDB QH */ ++ if(kdb_uhci_keyboard_check_uhci_qh(qh) != -1){ ++ ret = 0; ++ goto done; ++ } ++#endif ++ + if (qh->type == USB_ENDPOINT_XFER_ISOC) + goto done; + +@@ -1733,6 +1819,11 @@ rescan: + uhci->next_qh = list_entry(qh->node.next, + struct uhci_qh, node); + ++#ifdef CONFIG_KDB_USB ++ /* Don't manage KDB QH */ ++ if(kdb_uhci_keyboard_check_uhci_qh(qh) != -1) ++ continue; ++#endif + if (uhci_advance_check(uhci, qh)) { + uhci_scan_qh(uhci, qh); + if (qh->state == QH_STATE_ACTIVE) { +@@ -1759,3 +1850,76 @@ rescan: + else + uhci_set_next_interrupt(uhci); + } ++ ++#ifdef CONFIG_KDB_USB ++/* ++ * Activate KDB UHCI QH, called by KDB poll code. ++ */ ++static void kdb_activate_uhci_qh(struct uhci_qh *qh) ++{ ++ struct urb_priv *urbp; ++ struct uhci_td *td; ++ __le32 status, token; ++ ++ urbp = list_entry(qh->queue.next, struct urb_priv, node); ++ ++ list_for_each_entry(td, &urbp->td_list, list){ ++ status = td->status; ++ token = td->token; ++ barrier(); ++ /* Clear Status and ActLen */ ++ status &= cpu_to_le32(0xff000000); ++ /* Make TD Active */ ++ status |= cpu_to_le32(TD_CTRL_ACTIVE); ++ /* Clear TD Interrupt */ ++ status &= cpu_to_le32(~TD_CTRL_IOC); ++ /* Toggle Data Sycronization Bit */ ++ if (token & cpu_to_le32(TD_TOKEN_TOGGLE)) ++ token &= cpu_to_le32(~TD_TOKEN_TOGGLE); ++ else ++ token |= cpu_to_le32(TD_TOKEN_TOGGLE); ++ ++ td->token = token; ++ td->status = status; ++ barrier(); ++ } ++ /* Activate KDB UHCI Keyboard HID QH */ ++ td = list_entry(urbp->td_list.next, struct uhci_td, list); ++ qh->element = LINK_TO_TD(td); ++ barrier(); ++} ++ ++/* ++ * Called when KDB finishes process key press/release event. ++ */ ++static void ++kdb_uhci_urb_complete (struct urb *urb) ++{ ++ if (!kdb_uhci_keyboard_get_hid_event(urb)) ++ return; ++ ++ /* Activate KDB TD */ ++ kdb_activate_uhci_qh(kdb_uhci_keyboard_get_qh(urb)); ++ kdb_uhci_keyboard_set_hid_event(urb, 0); ++} ++ ++/* ++ * Check if state of KDB URB changed (key was pressed/released). ++ */ ++static int uhci_check_kdb_uhci_qh(struct uhci_qh *qh) ++{ ++ struct urb_priv *urbp = NULL; ++ struct uhci_td *td; ++ unsigned status; ++ ++ urbp = list_entry(qh->queue.next, struct urb_priv, node); ++ td = list_entry(urbp->td_list.next, struct uhci_td, list); ++ status = td_status(td); ++ if (!(status & TD_CTRL_ACTIVE)){ ++ /* We're okay, the queue has advanced */ ++ kdb_uhci_keyboard_set_hid_event_qh(qh, 1); ++ return 0; ++ } ++ return -1; ++} ++#endif +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -161,6 +161,151 @@ static int meminfo_proc_show(struct seq_ + #undef K + } + ++#ifdef CONFIG_KDB ++#include ++#include ++/* Like meminfo_proc_show() but without the locks and using kdb_printf() */ ++void ++kdb_meminfo_proc_show(void) ++{ ++ struct sysinfo i; ++ unsigned long committed; ++ unsigned long allowed; ++ struct vmalloc_info vmi; ++ long cached; ++ unsigned long pages[NR_LRU_LISTS]; ++ int lru; ++ ++/* ++ * display in kilobytes. ++ */ ++#define K(x) ((x) << (PAGE_SHIFT - 10)) ++ si_meminfo(&i); ++ kdb_si_swapinfo(&i); ++ committed = percpu_counter_read_positive(&vm_committed_as); ++ allowed = ((totalram_pages - hugetlb_total_pages()) ++ * sysctl_overcommit_ratio / 100) + total_swap_pages; ++ ++ cached = global_page_state(NR_FILE_PAGES) - ++ total_swapcache_pages - i.bufferram; ++ if (cached < 0) ++ cached = 0; ++ ++ get_vmalloc_info(&vmi); ++ ++ for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++) ++ pages[lru] = global_page_state(NR_LRU_BASE + lru); ++ ++ kdb_printf( ++ "MemTotal: %8lu kB\n" ++ "MemFree: %8lu kB\n" ++ "Buffers: %8lu kB\n", ++ K(i.totalram), ++ K(i.freeram), ++ K(i.bufferram) ++ ); ++ kdb_printf( ++ "Cached: %8lu kB\n" ++ "SwapCached: %8lu kB\n" ++ "Active: %8lu kB\n" ++ "Inactive: %8lu kB\n", ++ K(cached), ++ K(total_swapcache_pages), ++ K(pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]), ++ K(pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE]) ++ ); ++ kdb_printf( ++ "Active(anon): %8lu kB\n" ++ "Inactive(anon): %8lu kB\n" ++ "Active(file): %8lu kB\n" ++ "Inactive(file): %8lu kB\n", ++ K(pages[LRU_ACTIVE_ANON]), ++ K(pages[LRU_INACTIVE_ANON]), ++ K(pages[LRU_ACTIVE_FILE]), ++ K(pages[LRU_INACTIVE_FILE]) ++ ); ++#ifdef CONFIG_UNEVICTABLE_LRU ++ kdb_printf( ++ "Unevictable: %8lu kB\n" ++ "Mlocked: %8lu kB\n", ++ K(pages[LRU_UNEVICTABLE]), ++ K(global_page_state(NR_MLOCK)) ++ ); ++#endif ++#ifdef CONFIG_HIGHMEM ++ kdb_printf( ++ "HighTotal: %8lu kB\n" ++ "HighFree: %8lu kB\n" ++ "LowTotal: %8lu kB\n" ++ "LowFree: %8lu kB\n", ++ K(i.totalhigh), ++ K(i.freehigh), ++ K(i.totalram-i.totalhigh), ++ K(i.freeram-i.freehigh) ++ ); ++#endif ++ kdb_printf( ++ "SwapTotal: %8lu kB\n" ++ "SwapFree: %8lu kB\n" ++ "Dirty: %8lu kB\n", ++ K(i.totalswap), ++ K(i.freeswap), ++ K(global_page_state(NR_FILE_DIRTY)) ++ ); ++ kdb_printf( ++ "Writeback: %8lu kB\n" ++ "AnonPages: %8lu kB\n" ++ "Mapped: %8lu kB\n", ++ K(global_page_state(NR_WRITEBACK)), ++ K(global_page_state(NR_ANON_PAGES)), ++ K(global_page_state(NR_FILE_MAPPED)) ++ ); ++ kdb_printf( ++ "Slab: %8lu kB\n" ++ "SReclaimable: %8lu kB\n" ++ "SUnreclaim: %8lu kB\n", ++ K(global_page_state(NR_SLAB_RECLAIMABLE) + ++ global_page_state(NR_SLAB_UNRECLAIMABLE)), ++ K(global_page_state(NR_SLAB_RECLAIMABLE)), ++ K(global_page_state(NR_SLAB_UNRECLAIMABLE)) ++ ); ++ kdb_printf( ++ "PageTables: %8lu kB\n" ++#ifdef CONFIG_QUICKLIST ++ "Quicklists: %8lu kB\n" ++#endif ++ "NFS_Unstable: %8lu kB\n" ++ "Bounce: %8lu kB\n", ++ K(global_page_state(NR_PAGETABLE)), ++#ifdef CONFIG_QUICKLIST ++ K(quicklist_total_size()), ++#endif ++ K(global_page_state(NR_UNSTABLE_NFS)), ++ K(global_page_state(NR_BOUNCE)) ++ ); ++ kdb_printf( ++ "WritebackTmp: %8lu kB\n" ++ "CommitLimit: %8lu kB\n" ++ "Committed_AS: %8lu kB\n", ++ K(global_page_state(NR_WRITEBACK_TEMP)), ++ K(allowed), ++ K(committed) ++ ); ++ kdb_printf( ++ "VmallocTotal: %8lu kB\n" ++ "VmallocUsed: %8lu kB\n" ++ "VmallocChunk: %8lu kB\n", ++ (unsigned long)VMALLOC_TOTAL >> 10, ++ vmi.used >> 10, ++ vmi.largest_chunk >> 10 ++ ); ++ ++#ifdef CONFIG_HUGETLBFS ++ kdb_hugetlb_report_meminfo(); ++#endif ++} ++#endif /* CONFIG_KDB */ ++ + static int meminfo_proc_open(struct inode *inode, struct file *file) + { + return single_open(file, meminfo_proc_show, NULL); +--- a/fs/proc/mmu.c ++++ b/fs/proc/mmu.c +@@ -14,11 +14,21 @@ + #include + #include "internal.h" + ++#ifdef CONFIG_KDB ++#include ++#endif ++ + void get_vmalloc_info(struct vmalloc_info *vmi) + { + struct vm_struct *vma; + unsigned long free_area_size; + unsigned long prev_end; ++#ifdef CONFIG_KDB ++ int get_lock = !KDB_IS_RUNNING(); ++#else ++#define get_lock 1 ++#endif ++ + + vmi->used = 0; + +@@ -30,7 +40,8 @@ void get_vmalloc_info(struct vmalloc_inf + + prev_end = VMALLOC_START; + +- read_lock(&vmlist_lock); ++ if (get_lock) ++ read_lock(&vmlist_lock); + + for (vma = vmlist; vma; vma = vma->next) { + unsigned long addr = (unsigned long) vma->addr; +@@ -55,6 +66,7 @@ void get_vmalloc_info(struct vmalloc_inf + if (VMALLOC_END - prev_end > vmi->largest_chunk) + vmi->largest_chunk = VMALLOC_END - prev_end; + +- read_unlock(&vmlist_lock); ++ if (get_lock) ++ read_unlock(&vmlist_lock); + } + } +--- a/include/asm-generic/kmap_types.h ++++ b/include/asm-generic/kmap_types.h +@@ -28,7 +28,8 @@ KMAP_D(15) KM_UML_USERCOPY, + KMAP_D(16) KM_IRQ_PTE, + KMAP_D(17) KM_NMI, + KMAP_D(18) KM_NMI_PTE, +-KMAP_D(19) KM_TYPE_NR ++KMAP_D(19) KM_KDB, ++KMAP_D(20) KM_TYPE_NR + }; + + #undef KMAP_D +--- a/include/linux/console.h ++++ b/include/linux/console.h +@@ -142,7 +142,12 @@ void vcs_remove_sysfs(int index); + + /* Some debug stub to catch some of the obvious races in the VT code */ + #if 1 ++#ifdef CONFIG_KDB ++#include ++#define WARN_CONSOLE_UNLOCKED() WARN_ON(!is_console_locked() && !oops_in_progress && !atomic_read(&kdb_event)) ++#else /* !CONFIG_KDB */ + #define WARN_CONSOLE_UNLOCKED() WARN_ON(!is_console_locked() && !oops_in_progress) ++#endif /* CONFIG_KDB */ + #else + #define WARN_CONSOLE_UNLOCKED() + #endif +--- /dev/null ++++ b/include/linux/dis-asm.h +@@ -0,0 +1,347 @@ ++/* Interface between the opcode library and its callers. ++ ++ Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005 ++ Free Software Foundation, Inc. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2, or (at your option) ++ any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street - Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++ Written by Cygnus Support, 1993. ++ ++ The opcode library (libopcodes.a) provides instruction decoders for ++ a large variety of instruction sets, callable with an identical ++ interface, for making instruction-processing programs more independent ++ of the instruction set being processed. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifndef DIS_ASM_H ++#define DIS_ASM_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifdef __KERNEL__ ++#include ++#include ++typedef void FILE; ++#else /* __KERNEL__ */ ++#include ++#include "bfd.h" ++#endif /* __KERNEL__ */ ++ ++typedef int (*fprintf_ftype) (void *, const char*, ...) ATTRIBUTE_FPTR_PRINTF_2; ++ ++enum dis_insn_type { ++ dis_noninsn, /* Not a valid instruction */ ++ dis_nonbranch, /* Not a branch instruction */ ++ dis_branch, /* Unconditional branch */ ++ dis_condbranch, /* Conditional branch */ ++ dis_jsr, /* Jump to subroutine */ ++ dis_condjsr, /* Conditional jump to subroutine */ ++ dis_dref, /* Data reference instruction */ ++ dis_dref2 /* Two data references in instruction */ ++}; ++ ++/* This struct is passed into the instruction decoding routine, ++ and is passed back out into each callback. The various fields are used ++ for conveying information from your main routine into your callbacks, ++ for passing information into the instruction decoders (such as the ++ addresses of the callback functions), or for passing information ++ back from the instruction decoders to their callers. ++ ++ It must be initialized before it is first passed; this can be done ++ by hand, or using one of the initialization macros below. */ ++ ++typedef struct disassemble_info { ++ fprintf_ftype fprintf_func; ++ void *stream; ++ void *application_data; ++ ++ /* Target description. We could replace this with a pointer to the bfd, ++ but that would require one. There currently isn't any such requirement ++ so to avoid introducing one we record these explicitly. */ ++ /* The bfd_flavour. This can be bfd_target_unknown_flavour. */ ++ enum bfd_flavour flavour; ++ /* The bfd_arch value. */ ++ enum bfd_architecture arch; ++ /* The bfd_mach value. */ ++ unsigned long mach; ++ /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */ ++ enum bfd_endian endian; ++ /* An arch/mach-specific bitmask of selected instruction subsets, mainly ++ for processors with run-time-switchable instruction sets. The default, ++ zero, means that there is no constraint. CGEN-based opcodes ports ++ may use ISA_foo masks. */ ++ unsigned long insn_sets; ++ ++ /* Some targets need information about the current section to accurately ++ display insns. If this is NULL, the target disassembler function ++ will have to make its best guess. */ ++ asection *section; ++ ++ /* An array of pointers to symbols either at the location being disassembled ++ or at the start of the function being disassembled. The array is sorted ++ so that the first symbol is intended to be the one used. The others are ++ present for any misc. purposes. This is not set reliably, but if it is ++ not NULL, it is correct. */ ++ asymbol **symbols; ++ /* Number of symbols in array. */ ++ int num_symbols; ++ ++ /* For use by the disassembler. ++ The top 16 bits are reserved for public use (and are documented here). ++ The bottom 16 bits are for the internal use of the disassembler. */ ++ unsigned long flags; ++#define INSN_HAS_RELOC 0x80000000 ++ void *private_data; ++ ++ /* Function used to get bytes to disassemble. MEMADDR is the ++ address of the stuff to be disassembled, MYADDR is the address to ++ put the bytes in, and LENGTH is the number of bytes to read. ++ INFO is a pointer to this struct. ++ Returns an errno value or 0 for success. */ ++ int (*read_memory_func) ++ (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, ++ struct disassemble_info *info); ++ ++ /* Function which should be called if we get an error that we can't ++ recover from. STATUS is the errno value from read_memory_func and ++ MEMADDR is the address that we were trying to read. INFO is a ++ pointer to this struct. */ ++ void (*memory_error_func) ++ (int status, bfd_vma memaddr, struct disassemble_info *info); ++ ++ /* Function called to print ADDR. */ ++ void (*print_address_func) ++ (bfd_vma addr, struct disassemble_info *info); ++ ++ /* Function called to determine if there is a symbol at the given ADDR. ++ If there is, the function returns 1, otherwise it returns 0. ++ This is used by ports which support an overlay manager where ++ the overlay number is held in the top part of an address. In ++ some circumstances we want to include the overlay number in the ++ address, (normally because there is a symbol associated with ++ that address), but sometimes we want to mask out the overlay bits. */ ++ int (* symbol_at_address_func) ++ (bfd_vma addr, struct disassemble_info * info); ++ ++ /* Function called to check if a SYMBOL is can be displayed to the user. ++ This is used by some ports that want to hide special symbols when ++ displaying debugging outout. */ ++ bfd_boolean (* symbol_is_valid) ++ (asymbol *, struct disassemble_info * info); ++ ++ /* These are for buffer_read_memory. */ ++ bfd_byte *buffer; ++ bfd_vma buffer_vma; ++ unsigned int buffer_length; ++ ++ /* This variable may be set by the instruction decoder. It suggests ++ the number of bytes objdump should display on a single line. If ++ the instruction decoder sets this, it should always set it to ++ the same value in order to get reasonable looking output. */ ++ int bytes_per_line; ++ ++ /* The next two variables control the way objdump displays the raw data. */ ++ /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */ ++ /* output will look like this: ++ 00: 00000000 00000000 ++ with the chunks displayed according to "display_endian". */ ++ int bytes_per_chunk; ++ enum bfd_endian display_endian; ++ ++ /* Number of octets per incremented target address ++ Normally one, but some DSPs have byte sizes of 16 or 32 bits. */ ++ unsigned int octets_per_byte; ++ ++ /* The number of zeroes we want to see at the end of a section before we ++ start skipping them. */ ++ unsigned int skip_zeroes; ++ ++ /* The number of zeroes to skip at the end of a section. If the number ++ of zeroes at the end is between SKIP_ZEROES_AT_END and SKIP_ZEROES, ++ they will be disassembled. If there are fewer than ++ SKIP_ZEROES_AT_END, they will be skipped. This is a heuristic ++ attempt to avoid disassembling zeroes inserted by section ++ alignment. */ ++ unsigned int skip_zeroes_at_end; ++ ++ /* Results from instruction decoders. Not all decoders yet support ++ this information. This info is set each time an instruction is ++ decoded, and is only valid for the last such instruction. ++ ++ To determine whether this decoder supports this information, set ++ insn_info_valid to 0, decode an instruction, then check it. */ ++ ++ char insn_info_valid; /* Branch info has been set. */ ++ char branch_delay_insns; /* How many sequential insn's will run before ++ a branch takes effect. (0 = normal) */ ++ char data_size; /* Size of data reference in insn, in bytes */ ++ enum dis_insn_type insn_type; /* Type of instruction */ ++ bfd_vma target; /* Target address of branch or dref, if known; ++ zero if unknown. */ ++ bfd_vma target2; /* Second target address for dref2 */ ++ ++ /* Command line options specific to the target disassembler. */ ++ char * disassembler_options; ++ ++} disassemble_info; ++ ++ ++/* Standard disassemblers. Disassemble one instruction at the given ++ target address. Return number of octets processed. */ ++typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); ++ ++extern int print_insn_big_mips (bfd_vma, disassemble_info *); ++extern int print_insn_little_mips (bfd_vma, disassemble_info *); ++extern int print_insn_i386 (bfd_vma, disassemble_info *); ++extern int print_insn_i386_att (bfd_vma, disassemble_info *); ++extern int print_insn_i386_intel (bfd_vma, disassemble_info *); ++extern int print_insn_ia64 (bfd_vma, disassemble_info *); ++extern int print_insn_i370 (bfd_vma, disassemble_info *); ++extern int print_insn_m68hc11 (bfd_vma, disassemble_info *); ++extern int print_insn_m68hc12 (bfd_vma, disassemble_info *); ++extern int print_insn_m68k (bfd_vma, disassemble_info *); ++extern int print_insn_z8001 (bfd_vma, disassemble_info *); ++extern int print_insn_z8002 (bfd_vma, disassemble_info *); ++extern int print_insn_h8300 (bfd_vma, disassemble_info *); ++extern int print_insn_h8300h (bfd_vma, disassemble_info *); ++extern int print_insn_h8300s (bfd_vma, disassemble_info *); ++extern int print_insn_h8500 (bfd_vma, disassemble_info *); ++extern int print_insn_alpha (bfd_vma, disassemble_info *); ++extern int print_insn_big_arm (bfd_vma, disassemble_info *); ++extern int print_insn_little_arm (bfd_vma, disassemble_info *); ++extern int print_insn_sparc (bfd_vma, disassemble_info *); ++extern int print_insn_big_a29k (bfd_vma, disassemble_info *); ++extern int print_insn_little_a29k (bfd_vma, disassemble_info *); ++extern int print_insn_avr (bfd_vma, disassemble_info *); ++extern int print_insn_d10v (bfd_vma, disassemble_info *); ++extern int print_insn_d30v (bfd_vma, disassemble_info *); ++extern int print_insn_dlx (bfd_vma, disassemble_info *); ++extern int print_insn_fr30 (bfd_vma, disassemble_info *); ++extern int print_insn_hppa (bfd_vma, disassemble_info *); ++extern int print_insn_i860 (bfd_vma, disassemble_info *); ++extern int print_insn_i960 (bfd_vma, disassemble_info *); ++extern int print_insn_ip2k (bfd_vma, disassemble_info *); ++extern int print_insn_m32r (bfd_vma, disassemble_info *); ++extern int print_insn_m88k (bfd_vma, disassemble_info *); ++extern int print_insn_maxq_little (bfd_vma, disassemble_info *); ++extern int print_insn_maxq_big (bfd_vma, disassemble_info *); ++extern int print_insn_mcore (bfd_vma, disassemble_info *); ++extern int print_insn_mmix (bfd_vma, disassemble_info *); ++extern int print_insn_mn10200 (bfd_vma, disassemble_info *); ++extern int print_insn_mn10300 (bfd_vma, disassemble_info *); ++extern int print_insn_ms1 (bfd_vma, disassemble_info *); ++extern int print_insn_msp430 (bfd_vma, disassemble_info *); ++extern int print_insn_ns32k (bfd_vma, disassemble_info *); ++extern int print_insn_crx (bfd_vma, disassemble_info *); ++extern int print_insn_openrisc (bfd_vma, disassemble_info *); ++extern int print_insn_big_or32 (bfd_vma, disassemble_info *); ++extern int print_insn_little_or32 (bfd_vma, disassemble_info *); ++extern int print_insn_pdp11 (bfd_vma, disassemble_info *); ++extern int print_insn_pj (bfd_vma, disassemble_info *); ++extern int print_insn_big_powerpc (bfd_vma, disassemble_info *); ++extern int print_insn_little_powerpc (bfd_vma, disassemble_info *); ++extern int print_insn_rs6000 (bfd_vma, disassemble_info *); ++extern int print_insn_s390 (bfd_vma, disassemble_info *); ++extern int print_insn_sh (bfd_vma, disassemble_info *); ++extern int print_insn_tic30 (bfd_vma, disassemble_info *); ++extern int print_insn_tic4x (bfd_vma, disassemble_info *); ++extern int print_insn_tic54x (bfd_vma, disassemble_info *); ++extern int print_insn_tic80 (bfd_vma, disassemble_info *); ++extern int print_insn_v850 (bfd_vma, disassemble_info *); ++extern int print_insn_vax (bfd_vma, disassemble_info *); ++extern int print_insn_w65 (bfd_vma, disassemble_info *); ++extern int print_insn_xstormy16 (bfd_vma, disassemble_info *); ++extern int print_insn_xtensa (bfd_vma, disassemble_info *); ++extern int print_insn_sh64 (bfd_vma, disassemble_info *); ++extern int print_insn_sh64x_media (bfd_vma, disassemble_info *); ++extern int print_insn_frv (bfd_vma, disassemble_info *); ++extern int print_insn_iq2000 (bfd_vma, disassemble_info *); ++extern int print_insn_m32c (bfd_vma, disassemble_info *); ++ ++extern disassembler_ftype arc_get_disassembler (void *); ++extern disassembler_ftype cris_get_disassembler (bfd *); ++ ++extern void print_mips_disassembler_options (FILE *); ++extern void print_ppc_disassembler_options (FILE *); ++extern void print_arm_disassembler_options (FILE *); ++extern void parse_arm_disassembler_option (char *); ++extern int get_arm_regname_num_options (void); ++extern int set_arm_regname_option (int); ++extern int get_arm_regnames (int, const char **, const char **, const char *const **); ++extern bfd_boolean arm_symbol_is_valid (asymbol *, struct disassemble_info *); ++ ++/* Fetch the disassembler for a given BFD, if that support is available. */ ++extern disassembler_ftype disassembler (bfd *); ++ ++/* Amend the disassemble_info structure as necessary for the target architecture. ++ Should only be called after initialising the info->arch field. */ ++extern void disassemble_init_for_target (struct disassemble_info * info); ++ ++/* Document any target specific options available from the disassembler. */ ++extern void disassembler_usage (FILE *); ++ ++ ++/* This block of definitions is for particular callers who read instructions ++ into a buffer before calling the instruction decoder. */ ++ ++/* Here is a function which callers may wish to use for read_memory_func. ++ It gets bytes from a buffer. */ ++extern int buffer_read_memory ++ (bfd_vma, bfd_byte *, unsigned int, struct disassemble_info *); ++ ++/* This function goes with buffer_read_memory. ++ It prints a message using info->fprintf_func and info->stream. */ ++extern void perror_memory (int, bfd_vma, struct disassemble_info *); ++ ++ ++/* Just print the address in hex. This is included for completeness even ++ though both GDB and objdump provide their own (to print symbolic ++ addresses). */ ++extern void generic_print_address ++ (bfd_vma, struct disassemble_info *); ++ ++/* Always true. */ ++extern int generic_symbol_at_address ++ (bfd_vma, struct disassemble_info *); ++ ++/* Also always true. */ ++extern bfd_boolean generic_symbol_is_valid ++ (asymbol *, struct disassemble_info *); ++ ++/* Method to initialize a disassemble_info struct. This should be ++ called by all applications creating such a struct. */ ++extern void init_disassemble_info (struct disassemble_info *info, void *stream, ++ fprintf_ftype fprintf_func); ++ ++/* For compatibility with existing code. */ ++#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \ ++ init_disassemble_info (&(INFO), (STREAM), (fprintf_ftype) (FPRINTF_FUNC)) ++#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ ++ init_disassemble_info (&(INFO), (STREAM), (fprintf_ftype) (FPRINTF_FUNC)) ++ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* ! defined (DIS_ASM_H) */ +--- /dev/null ++++ b/include/linux/kdb.h +@@ -0,0 +1,184 @@ ++#ifndef _KDB_H ++#define _KDB_H ++ ++/* ++ * Kernel Debugger Architecture Independent Global Headers ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 2000-2007 Silicon Graphics, Inc. All Rights Reserved. ++ * Copyright (C) 2000 Stephane Eranian ++ */ ++ ++#include ++#include ++#include ++ ++#ifdef CONFIG_KDB ++/* These are really private, but they must be defined before including ++ * asm-$(ARCH)/kdb.h, so make them public and put them here. ++ */ ++extern int kdb_getuserarea_size(void *, unsigned long, size_t); ++extern int kdb_putuserarea_size(unsigned long, void *, size_t); ++ ++#include ++#endif ++ ++#define KDB_MAJOR_VERSION 4 ++#define KDB_MINOR_VERSION 4 ++#define KDB_TEST_VERSION "" ++ ++/* ++ * kdb_initial_cpu is initialized to -1, and is set to the cpu ++ * number whenever the kernel debugger is entered. ++ */ ++extern volatile int kdb_initial_cpu; ++extern atomic_t kdb_event; ++extern atomic_t kdb_8250; ++#ifdef CONFIG_KDB ++#define KDB_IS_RUNNING() (kdb_initial_cpu != -1) ++#define KDB_8250() (atomic_read(&kdb_8250) != 0) ++#else ++#define KDB_IS_RUNNING() (0) ++#define KDB_8250() (0) ++#endif /* CONFIG_KDB */ ++ ++/* ++ * kdb_on ++ * ++ * Defines whether kdb is on or not. Default value ++ * is set by CONFIG_KDB_OFF. Boot with kdb=on/off/on-nokey ++ * or echo "[012]" > /proc/sys/kernel/kdb to change it. ++ */ ++extern int kdb_on; ++ ++#if defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_SGI_L1_CONSOLE) ++/* ++ * kdb_serial.iobase is initialized to zero, and is set to the I/O ++ * address of the serial port when the console is setup in ++ * serial_console_setup. ++ */ ++extern struct kdb_serial { ++ int io_type; ++ unsigned long iobase; ++ unsigned long ioreg_shift; ++} kdb_serial; ++#endif ++ ++/* ++ * kdb_diemsg ++ * ++ * Contains a pointer to the last string supplied to the ++ * kernel 'die' panic function. ++ */ ++extern const char *kdb_diemsg; ++ ++#define KDB_FLAG_EARLYKDB (1 << 0) /* set from boot parameter kdb=early */ ++#define KDB_FLAG_CATASTROPHIC (1 << 1) /* A catastrophic event has occurred */ ++#define KDB_FLAG_CMD_INTERRUPT (1 << 2) /* Previous command was interrupted */ ++#define KDB_FLAG_NOIPI (1 << 3) /* Do not send IPIs */ ++#define KDB_FLAG_ONLY_DO_DUMP (1 << 4) /* Only do a dump, used when kdb is off */ ++#define KDB_FLAG_NO_CONSOLE (1 << 5) /* No console is available, kdb is disabled */ ++#define KDB_FLAG_NO_VT_CONSOLE (1 << 6) /* No VT console is available, do not use keyboard */ ++#define KDB_FLAG_NO_I8042 (1 << 7) /* No i8042 chip is available, do not use keyboard */ ++#define KDB_FLAG_RECOVERY (1 << 8) /* kdb is being entered for an error which has been recovered */ ++ ++extern volatile int kdb_flags; /* Global flags, see kdb_state for per cpu state */ ++ ++extern void kdb_save_flags(void); ++extern void kdb_restore_flags(void); ++ ++#define KDB_FLAG(flag) (kdb_flags & KDB_FLAG_##flag) ++#define KDB_FLAG_SET(flag) ((void)(kdb_flags |= KDB_FLAG_##flag)) ++#define KDB_FLAG_CLEAR(flag) ((void)(kdb_flags &= ~KDB_FLAG_##flag)) ++ ++/* ++ * External entry point for the kernel debugger. The pt_regs ++ * at the time of entry are supplied along with the reason for ++ * entry to the kernel debugger. ++ */ ++ ++typedef enum { ++ KDB_REASON_ENTER=1, /* KDB_ENTER() trap/fault - regs valid */ ++ KDB_REASON_ENTER_SLAVE, /* KDB_ENTER_SLAVE() trap/fault - regs valid */ ++ KDB_REASON_BREAK, /* Breakpoint inst. - regs valid */ ++ KDB_REASON_DEBUG, /* Debug Fault - regs valid */ ++ KDB_REASON_OOPS, /* Kernel Oops - regs valid */ ++ KDB_REASON_SWITCH, /* CPU switch - regs valid*/ ++ KDB_REASON_KEYBOARD, /* Keyboard entry - regs valid */ ++ KDB_REASON_NMI, /* Non-maskable interrupt; regs valid */ ++ KDB_REASON_RECURSE, /* Recursive entry to kdb; regs probably valid */ ++ KDB_REASON_CPU_UP, /* Add one cpu to kdb; regs invalid */ ++ KDB_REASON_SILENT, /* Silent entry/exit to kdb; regs invalid - internal only */ ++} kdb_reason_t; ++ ++#ifdef CONFIG_KDB ++extern int kdb(kdb_reason_t, int, struct pt_regs *); ++#else ++#define kdb(reason,error_code,frame) (0) ++#endif ++ ++/* Mainly used by kdb code, but this function is sometimes used ++ * by hacked debug code so make it generally available, not private. ++ */ ++extern void kdb_printf(const char *,...) ++ __attribute__ ((format (printf, 1, 2))); ++typedef void (*kdb_printf_t)(const char *, ...) ++ __attribute__ ((format (printf, 1, 2))); ++extern void kdb_init(void); ++ ++#if defined(CONFIG_SMP) ++/* ++ * Kernel debugger non-maskable IPI handler. ++ */ ++extern int kdb_ipi(struct pt_regs *, void (*ack_interrupt)(void)); ++extern void smp_kdb_stop(void); ++#else /* CONFIG_SMP */ ++#define smp_kdb_stop() ++#endif /* CONFIG_SMP */ ++ ++#ifdef CONFIG_KDB_USB ++ ++#include ++ ++typedef int (*kdb_hc_keyboard_attach_t)(int i, unsigned int bufsize); ++typedef int (*kdb_hc_keyboard_detach_t)(struct urb *urb, int i); ++ ++extern int kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, ++ void *poll_func, void *compl_func, ++ kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach, ++ kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach, ++ unsigned int bufsize, ++ struct urb *hid_urb); ++ ++extern int kdb_usb_keyboard_detach(struct urb *urb); ++ ++#endif /* CONFIG_KDB_USB */ ++ ++static inline ++int kdb_process_cpu(const struct task_struct *p) ++{ ++ unsigned int cpu = task_thread_info(p)->cpu; ++ if (cpu > NR_CPUS) ++ cpu = 0; ++ return cpu; ++} ++ ++extern const char kdb_serial_str[]; ++ ++#ifdef CONFIG_KDB_KDUMP ++/* Define values for kdb_kdump_state */ ++extern int kdb_kdump_state; /* KDB kdump state */ ++#define KDB_KDUMP_RESET 0 ++#define KDB_KDUMP_KDUMP 1 ++ ++void kdba_kdump_prepare(struct pt_regs *); ++void machine_crash_shutdown(struct pt_regs *); ++void machine_crash_shutdown_begin(void); ++void machine_crash_shutdown_end(struct pt_regs *); ++ ++#endif /* CONFIG_KDB_KDUMP */ ++ ++#endif /* !_KDB_H */ +--- /dev/null ++++ b/include/linux/kdbprivate.h +@@ -0,0 +1,518 @@ ++#ifndef _KDBPRIVATE_H ++#define _KDBPRIVATE_H ++ ++/* ++ * Kernel Debugger Architecture Independent Private Headers ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++ ++#include ++#include ++#include ++ ++ /* ++ * Kernel Debugger Error codes. Must not overlap with command codes. ++ */ ++ ++#define KDB_NOTFOUND (-1) ++#define KDB_ARGCOUNT (-2) ++#define KDB_BADWIDTH (-3) ++#define KDB_BADRADIX (-4) ++#define KDB_NOTENV (-5) ++#define KDB_NOENVVALUE (-6) ++#define KDB_NOTIMP (-7) ++#define KDB_ENVFULL (-8) ++#define KDB_ENVBUFFULL (-9 ) ++#define KDB_TOOMANYBPT (-10) ++#define KDB_TOOMANYDBREGS (-11) ++#define KDB_DUPBPT (-12) ++#define KDB_BPTNOTFOUND (-13) ++#define KDB_BADMODE (-14) ++#define KDB_BADINT (-15) ++#define KDB_INVADDRFMT (-16) ++#define KDB_BADREG (-17) ++#define KDB_BADCPUNUM (-18) ++#define KDB_BADLENGTH (-19) ++#define KDB_NOBP (-20) ++#define KDB_BADADDR (-21) ++ ++ /* ++ * Kernel Debugger Command codes. Must not overlap with error codes. ++ */ ++#define KDB_CMD_GO (-1001) ++#define KDB_CMD_CPU (-1002) ++#define KDB_CMD_SS (-1003) ++#define KDB_CMD_SSB (-1004) ++ ++ /* ++ * Internal debug flags ++ */ ++/* KDB_DEBUG_FLAG_BT 0x0001 Was Stack traceback debug */ ++#define KDB_DEBUG_FLAG_BP 0x0002 /* Breakpoint subsystem debug */ ++#define KDB_DEBUG_FLAG_BB_SUMM 0x0004 /* Basic block analysis, summary only */ ++#define KDB_DEBUG_FLAG_AR 0x0008 /* Activation record, generic */ ++#define KDB_DEBUG_FLAG_ARA 0x0010 /* Activation record, arch specific */ ++#define KDB_DEBUG_FLAG_BB 0x0020 /* All basic block analysis */ ++#define KDB_DEBUG_FLAG_STATE 0x0040 /* State flags */ ++#define KDB_DEBUG_FLAG_MASK 0xffff /* All debug flags */ ++#define KDB_DEBUG_FLAG_SHIFT 16 /* Shift factor for dbflags */ ++ ++#define KDB_DEBUG(flag) (kdb_flags & (KDB_DEBUG_FLAG_##flag << KDB_DEBUG_FLAG_SHIFT)) ++#define KDB_DEBUG_STATE(text,value) if (KDB_DEBUG(STATE)) kdb_print_state(text, value) ++ ++typedef enum { ++ KDB_REPEAT_NONE = 0, /* Do not repeat this command */ ++ KDB_REPEAT_NO_ARGS, /* Repeat the command without arguments */ ++ KDB_REPEAT_WITH_ARGS, /* Repeat the command including its arguments */ ++} kdb_repeat_t; ++ ++typedef int (*kdb_func_t)(int, const char **); ++ ++ /* ++ * Symbol table format returned by kallsyms. ++ */ ++ ++typedef struct __ksymtab { ++ unsigned long value; /* Address of symbol */ ++ const char *mod_name; /* Module containing symbol or "kernel" */ ++ unsigned long mod_start; ++ unsigned long mod_end; ++ const char *sec_name; /* Section containing symbol */ ++ unsigned long sec_start; ++ unsigned long sec_end; ++ const char *sym_name; /* Full symbol name, including any version */ ++ unsigned long sym_start; ++ unsigned long sym_end; ++ } kdb_symtab_t; ++extern int kallsyms_symbol_next(char *prefix_name, int flag); ++extern int kallsyms_symbol_complete(char *prefix_name, int max_len); ++ ++ /* ++ * Exported Symbols for kernel loadable modules to use. ++ */ ++extern int kdb_register(char *, kdb_func_t, char *, char *, short); ++extern int kdb_register_repeat(char *, kdb_func_t, char *, char *, short, kdb_repeat_t); ++extern int kdb_unregister(char *); ++ ++extern int kdb_getarea_size(void *, unsigned long, size_t); ++extern int kdb_putarea_size(unsigned long, void *, size_t); ++ ++/* Like get_user and put_user, kdb_getarea and kdb_putarea take variable ++ * names, not pointers. The underlying *_size functions take pointers. ++ */ ++#define kdb_getarea(x,addr) kdb_getarea_size(&(x), addr, sizeof((x))) ++#define kdb_putarea(addr,x) kdb_putarea_size(addr, &(x), sizeof((x))) ++ ++extern int kdb_getphysword(unsigned long *word, ++ unsigned long addr, size_t size); ++extern int kdb_getword(unsigned long *, unsigned long, size_t); ++extern int kdb_putword(unsigned long, unsigned long, size_t); ++ ++extern int kdbgetularg(const char *, unsigned long *); ++extern char *kdbgetenv(const char *); ++extern int kdbgetintenv(const char *, int *); ++extern int kdbgetaddrarg(int, const char**, int*, unsigned long *, ++ long *, char **); ++extern int kdbgetsymval(const char *, kdb_symtab_t *); ++extern int kdbnearsym(unsigned long, kdb_symtab_t *); ++extern void kdbnearsym_cleanup(void); ++extern char *kdb_read(char *buffer, size_t bufsize); ++extern char *kdb_strdup(const char *str, gfp_t type); ++extern void kdb_symbol_print(kdb_machreg_t, const kdb_symtab_t *, unsigned int); ++ ++ /* ++ * Do we have a set of registers? ++ */ ++ ++#define KDB_NULL_REGS(regs) \ ++ (regs == (struct pt_regs *)NULL ? kdb_printf("%s: null regs - should never happen\n", __FUNCTION__), 1 : 0) ++ ++ /* ++ * Routine for debugging the debugger state. ++ */ ++ ++extern void kdb_print_state(const char *, int); ++ ++ /* ++ * Per cpu kdb state. A cpu can be under kdb control but outside kdb, ++ * for example when doing single step. ++ */ ++volatile extern int kdb_state[ /*NR_CPUS*/ ]; ++#define KDB_STATE_KDB 0x00000001 /* Cpu is inside kdb */ ++#define KDB_STATE_LEAVING 0x00000002 /* Cpu is leaving kdb */ ++#define KDB_STATE_CMD 0x00000004 /* Running a kdb command */ ++#define KDB_STATE_KDB_CONTROL 0x00000008 /* This cpu is under kdb control */ ++#define KDB_STATE_HOLD_CPU 0x00000010 /* Hold this cpu inside kdb */ ++#define KDB_STATE_DOING_SS 0x00000020 /* Doing ss command */ ++#define KDB_STATE_DOING_SSB 0x00000040 /* Doing ssb command, DOING_SS is also set */ ++#define KDB_STATE_SSBPT 0x00000080 /* Install breakpoint after one ss, independent of DOING_SS */ ++#define KDB_STATE_REENTRY 0x00000100 /* Valid re-entry into kdb */ ++#define KDB_STATE_SUPPRESS 0x00000200 /* Suppress error messages */ ++#define KDB_STATE_LONGJMP 0x00000400 /* longjmp() data is available */ ++#define KDB_STATE_GO_SWITCH 0x00000800 /* go is switching back to initial cpu */ ++#define KDB_STATE_PRINTF_LOCK 0x00001000 /* Holds kdb_printf lock */ ++#define KDB_STATE_WAIT_IPI 0x00002000 /* Waiting for kdb_ipi() NMI */ ++#define KDB_STATE_RECURSE 0x00004000 /* Recursive entry to kdb */ ++#define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been adjusted */ ++#define KDB_STATE_GO1 0x00010000 /* go only releases one cpu */ ++#define KDB_STATE_KEYBOARD 0x00020000 /* kdb entered via keyboard on this cpu */ ++#define KDB_STATE_KEXEC 0x00040000 /* kexec issued */ ++#define KDB_STATE_ARCH 0xff000000 /* Reserved for arch specific use */ ++ ++#define KDB_STATE_CPU(flag,cpu) (kdb_state[cpu] & KDB_STATE_##flag) ++#define KDB_STATE_SET_CPU(flag,cpu) ((void)(kdb_state[cpu] |= KDB_STATE_##flag)) ++#define KDB_STATE_CLEAR_CPU(flag,cpu) ((void)(kdb_state[cpu] &= ~KDB_STATE_##flag)) ++ ++#define KDB_STATE(flag) KDB_STATE_CPU(flag,smp_processor_id()) ++#define KDB_STATE_SET(flag) KDB_STATE_SET_CPU(flag,smp_processor_id()) ++#define KDB_STATE_CLEAR(flag) KDB_STATE_CLEAR_CPU(flag,smp_processor_id()) ++ ++ /* ++ * kdb_nextline ++ * ++ * Contains the current line number on the screen. Used ++ * to handle the built-in pager (LINES env variable) ++ */ ++extern volatile int kdb_nextline; ++ ++ /* ++ * Breakpoint state ++ * ++ * Each active and inactive breakpoint is represented by ++ * an instance of the following data structure. ++ */ ++ ++typedef struct _kdb_bp { ++ bfd_vma bp_addr; /* Address breakpoint is present at */ ++ kdb_machinst_t bp_inst; /* Replaced instruction */ ++ ++ unsigned int bp_free:1; /* This entry is available */ ++ ++ unsigned int bp_enabled:1; /* Breakpoint is active in register */ ++ unsigned int bp_global:1; /* Global to all processors */ ++ ++ unsigned int bp_hardtype:1; /* Uses hardware register */ ++ unsigned int bp_forcehw:1; /* Force hardware register */ ++ unsigned int bp_installed:1; /* Breakpoint is installed */ ++ unsigned int bp_delay:1; /* Do delayed bp handling */ ++ unsigned int bp_delayed:1; /* Delayed breakpoint */ ++ ++ int bp_cpu; /* Cpu # (if bp_global == 0) */ ++ kdbhard_bp_t bp_template; /* Hardware breakpoint template */ ++ kdbhard_bp_t *bp_hard[NR_CPUS]; /* Hardware breakpoint structure */ ++ int bp_adjust; /* Adjustment to PC for real instruction */ ++} kdb_bp_t; ++ ++ /* ++ * Breakpoint handling subsystem global variables ++ */ ++extern kdb_bp_t kdb_breakpoints[/* KDB_MAXBPT */]; ++ ++ /* ++ * Breakpoint architecture dependent functions. Must be provided ++ * in some form for all architectures. ++ */ ++extern void kdba_initbp(void); ++extern void kdba_printbp(kdb_bp_t *); ++extern void kdba_alloc_hwbp(kdb_bp_t *bp, int *diagp); ++extern void kdba_free_hwbp(kdb_bp_t *bp); ++extern int kdba_parsebp(int, const char**, int *, kdb_bp_t*); ++extern char *kdba_bptype(kdbhard_bp_t *); ++extern void kdba_setsinglestep(struct pt_regs *); ++extern void kdba_clearsinglestep(struct pt_regs *); ++ ++ /* ++ * Adjust instruction pointer architecture dependent function. Must be ++ * provided in some form for all architectures. ++ */ ++extern void kdba_adjust_ip(kdb_reason_t, int, struct pt_regs *); ++ ++ /* ++ * KDB-only global function prototypes. ++ */ ++extern void kdb_id1(unsigned long); ++extern void kdb_id_init(void); ++ ++ /* ++ * Initialization functions. ++ */ ++extern void kdba_init(void); ++extern void kdb_io_init(void); ++ ++ /* ++ * Architecture specific function to read a string. ++ */ ++typedef int (*get_char_func)(void); ++extern get_char_func poll_funcs[]; ++ ++#ifndef CONFIG_IA64 ++ /* ++ * Data for a single activation record on stack. ++ */ ++ ++struct kdb_stack_info { ++ kdb_machreg_t physical_start; ++ kdb_machreg_t physical_end; ++ kdb_machreg_t logical_start; ++ kdb_machreg_t logical_end; ++ kdb_machreg_t next; ++ const char * id; ++}; ++ ++typedef struct { DECLARE_BITMAP(bits, KDBA_MAXARGS); } valid_t; ++ ++struct kdb_activation_record { ++ struct kdb_stack_info stack; /* information about current stack */ ++ int args; /* number of arguments detected */ ++ kdb_machreg_t arg[KDBA_MAXARGS]; /* -> arguments */ ++ valid_t valid; /* is argument n valid? */ ++}; ++#endif ++ ++ /* ++ * Architecture specific Stack Traceback functions. ++ */ ++ ++struct task_struct; ++ ++extern int kdba_bt_address(kdb_machreg_t, int); ++extern int kdba_bt_process(const struct task_struct *, int); ++ ++ /* ++ * KDB Command Table ++ */ ++ ++typedef struct _kdbtab { ++ char *cmd_name; /* Command name */ ++ kdb_func_t cmd_func; /* Function to execute command */ ++ char *cmd_usage; /* Usage String for this command */ ++ char *cmd_help; /* Help message for this command */ ++ short cmd_flags; /* Parsing flags */ ++ short cmd_minlen; /* Minimum legal # command chars required */ ++ kdb_repeat_t cmd_repeat; /* Does command auto repeat on enter? */ ++} kdbtab_t; ++ ++ /* ++ * External command function declarations ++ */ ++ ++extern int kdb_id(int, const char **); ++extern int kdb_bt(int, const char **); ++ ++ /* ++ * External utility function declarations ++ */ ++extern char* kdb_getstr(char *, size_t, char *); ++ ++ /* ++ * Register contents manipulation ++ */ ++extern int kdba_getregcontents(const char *, struct pt_regs *, kdb_machreg_t *); ++extern int kdba_setregcontents(const char *, struct pt_regs *, kdb_machreg_t); ++extern int kdba_dumpregs(struct pt_regs *, const char *, const char *); ++extern int kdba_setpc(struct pt_regs *, kdb_machreg_t); ++extern kdb_machreg_t kdba_getpc(struct pt_regs *); ++ ++ /* ++ * Debug register handling. ++ */ ++extern void kdba_installdbreg(kdb_bp_t*); ++extern void kdba_removedbreg(kdb_bp_t*); ++ ++ /* ++ * Breakpoint handling - External interfaces ++ */ ++extern void kdb_initbptab(void); ++extern void kdb_bp_install_global(struct pt_regs *); ++extern void kdb_bp_install_local(struct pt_regs *); ++extern void kdb_bp_remove_global(void); ++extern void kdb_bp_remove_local(void); ++ ++ /* ++ * Breakpoint handling - Internal to kdb_bp.c/kdba_bp.c ++ */ ++extern int kdba_installbp(struct pt_regs *regs, kdb_bp_t *); ++extern int kdba_removebp(kdb_bp_t *); ++ ++ ++typedef enum { ++ KDB_DB_BPT, /* Breakpoint */ ++ KDB_DB_SS, /* Single-step trap */ ++ KDB_DB_SSB, /* Single step to branch */ ++ KDB_DB_SSBPT, /* Single step over breakpoint */ ++ KDB_DB_NOBPT /* Spurious breakpoint */ ++} kdb_dbtrap_t; ++ ++extern kdb_dbtrap_t kdba_db_trap(struct pt_regs *, int); /* DEBUG trap/fault handler */ ++extern kdb_dbtrap_t kdba_bp_trap(struct pt_regs *, int); /* Breakpoint trap/fault hdlr */ ++ ++ /* ++ * Interrupt Handling ++ */ ++typedef unsigned long kdb_intstate_t; ++ ++extern void kdba_disableint(kdb_intstate_t *); ++extern void kdba_restoreint(kdb_intstate_t *); ++ ++ /* ++ * SMP and process stack manipulation routines. ++ */ ++extern int kdba_ipi(struct pt_regs *, void (*)(void)); ++extern int kdba_main_loop(kdb_reason_t, kdb_reason_t, int, kdb_dbtrap_t, struct pt_regs *); ++extern int kdb_main_loop(kdb_reason_t, kdb_reason_t, int, kdb_dbtrap_t, struct pt_regs *); ++ ++ /* ++ * General Disassembler interfaces ++ */ ++extern int kdb_dis_fprintf(PTR, const char *, ...) __attribute__ ((format (printf, 2, 3))); ++extern int kdb_dis_fprintf_dummy(PTR, const char *, ...) __attribute__ ((format (printf, 2, 3))); ++extern disassemble_info kdb_di; ++ ++ /* ++ * Architecture Dependent Disassembler interfaces ++ */ ++extern int kdba_id_printinsn(kdb_machreg_t, disassemble_info *); ++extern int kdba_id_parsemode(const char *, disassemble_info*); ++extern void kdba_id_init(disassemble_info *); ++extern void kdba_check_pc(kdb_machreg_t *); ++ ++ /* ++ * Miscellaneous functions and data areas ++ */ ++extern char *kdb_cmds[]; ++extern void debugger_syslog_data(char *syslog_data[]); ++extern unsigned long kdb_task_state_string(const char *); ++extern char kdb_task_state_char (const struct task_struct *); ++extern unsigned long kdb_task_state(const struct task_struct *p, unsigned long mask); ++extern void kdb_ps_suppressed(void); ++extern void kdb_ps1(const struct task_struct *p); ++extern int kdb_parse(const char *cmdstr); ++extern void kdb_print_nameval(const char *name, unsigned long val); ++extern void kdb_send_sig_info(struct task_struct *p, struct siginfo *info, int seqno); ++#ifdef CONFIG_SWAP ++extern void kdb_si_swapinfo(struct sysinfo *); ++#else ++#include ++#define kdb_si_swapinfo(x) si_swapinfo(x) ++#endif ++extern void kdb_meminfo_proc_show(void); ++#ifdef CONFIG_HUGETLB_PAGE ++extern void kdb_hugetlb_report_meminfo(void); ++#endif /* CONFIG_HUGETLB_PAGE */ ++extern const char *kdb_walk_kallsyms(loff_t *pos); ++ ++ /* ++ * Architecture Dependant Local Processor setup & cleanup interfaces ++ */ ++extern void kdba_local_arch_setup(void); ++extern void kdba_local_arch_cleanup(void); ++ ++ /* ++ * Defines for kdb_symbol_print. ++ */ ++#define KDB_SP_SPACEB 0x0001 /* Space before string */ ++#define KDB_SP_SPACEA 0x0002 /* Space after string */ ++#define KDB_SP_PAREN 0x0004 /* Parenthesis around string */ ++#define KDB_SP_VALUE 0x0008 /* Print the value of the address */ ++#define KDB_SP_SYMSIZE 0x0010 /* Print the size of the symbol */ ++#define KDB_SP_NEWLINE 0x0020 /* Newline after string */ ++#define KDB_SP_DEFAULT (KDB_SP_VALUE|KDB_SP_PAREN) ++ ++/* Save data about running processes */ ++ ++struct kdb_running_process { ++ struct task_struct *p; ++ struct pt_regs *regs; ++ int seqno; /* kdb sequence number */ ++ int irq_depth; /* irq count */ ++ struct kdba_running_process arch; /* arch dependent save data */ ++}; ++ ++extern struct kdb_running_process kdb_running_process[/* NR_CPUS */]; ++ ++extern int kdb_save_running(struct pt_regs *, kdb_reason_t, kdb_reason_t, int, kdb_dbtrap_t); ++extern void kdb_unsave_running(struct pt_regs *); ++extern struct task_struct *kdb_curr_task(int); ++ ++/* Incremented each time the main kdb loop is entered on the initial cpu, ++ * it gives some indication of how old the saved data is. ++ */ ++extern int kdb_seqno; ++ ++#define kdb_task_has_cpu(p) (task_curr(p)) ++extern void kdb_runqueue(unsigned long cpu, kdb_printf_t xxx_printf); ++ ++/* Simplify coexistence with NPTL */ ++#define kdb_do_each_thread(g, p) do_each_thread(g, p) ++#define kdb_while_each_thread(g, p) while_each_thread(g, p) ++ ++#define GFP_KDB (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) ++ ++extern void *debug_kmalloc(size_t size, gfp_t flags); ++extern void debug_kfree(void *); ++extern void debug_kusage(void); ++ ++extern void kdba_set_current_task(const struct task_struct *); ++extern const struct task_struct *kdb_current_task; ++extern struct pt_regs *kdb_current_regs; ++ ++/* Functions to safely read and write kernel areas. The {to,from}_xxx ++ * addresses are not necessarily valid, these functions must check for ++ * validity. If the arch already supports get and put routines with suitable ++ * validation and/or recovery on invalid addresses then use those routines, ++ * otherwise check it yourself. ++ */ ++ ++extern int kdba_putarea_size(unsigned long to_xxx, void *from, size_t size); ++extern int kdba_getarea_size(void *to, unsigned long from_xxx, size_t size); ++extern int kdba_verify_rw(unsigned long addr, size_t size); ++ ++#ifndef KDB_RUNNING_PROCESS_ORIGINAL ++#define KDB_RUNNING_PROCESS_ORIGINAL kdb_running_process ++#endif ++ ++extern int kdb_wait_for_cpus_secs; ++extern void kdba_cpu_up(void); ++extern char kdb_prompt_str[]; ++ ++#define KDB_WORD_SIZE ((int)sizeof(kdb_machreg_t)) ++ ++#ifdef CONFIG_KDB_USB ++#include ++ ++/* support up to 8 USB keyboards (probably excessive, but...) */ ++#define KDB_USB_NUM_KEYBOARDS 8 ++ ++struct kdb_usb_kbd_info { ++ struct urb *urb; /* pointer to the URB */ ++ unsigned char *buffer; /* pointer to the kbd char buffer */ ++ int (*poll_func)(struct urb *urb); /* poll function to retrieve chars */ ++ int poll_ret; /* return val from poll_func */ ++ int caps_lock; /* state of the caps lock for this keyboard */ ++ struct uhci_qh *qh; ++ int kdb_hid_event; ++ struct urb *hid_urb; /* pointer to the HID URB */ ++ /* USB Host Controller specific callbacks */ ++ kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach; ++ kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach; ++ int (*kdb_hc_urb_complete)(struct urb *urb); /* called when URB int is ++ processed */ ++ ++}; ++ ++extern struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS]; ++ ++#endif /* CONFIG_KDB_USB */ ++ ++#ifdef CONFIG_KDB_KDUMP ++#define KDUMP_REASON_RESET 0 ++extern void kdba_kdump_shutdown_slave(struct pt_regs *); ++#endif /* CONFIG_KDB_KDUMP */ ++ ++#endif /* !_KDBPRIVATE_H */ +--- a/include/linux/reboot.h ++++ b/include/linux/reboot.h +@@ -53,7 +53,14 @@ extern void machine_power_off(void); + + extern void machine_shutdown(void); + struct pt_regs; ++#ifdef CONFIG_KDB_KDUMP + extern void machine_crash_shutdown(struct pt_regs *); ++extern void machine_crash_shutdown_begin(void); ++extern void machine_crash_shutdown_other_cpu(struct pt_regs *); ++extern void machine_crash_shutdown_end(struct pt_regs *); ++#else ++extern void machine_crash_shutdown(struct pt_regs *); ++#endif /* !CONFIG_KDB_KDUMP */ + + /* + * Architecture independent implemenations of sys_reboot commands. +--- a/init/main.c ++++ b/init/main.c +@@ -101,6 +101,10 @@ extern void tc_init(void); + enum system_states system_state __read_mostly; + EXPORT_SYMBOL(system_state); + ++#ifdef CONFIG_KDB ++#include ++#endif /* CONFIG_KDB */ ++ + /* + * Boot command-line arguments + */ +@@ -203,6 +207,26 @@ static const char *panic_later, *panic_p + + extern struct obs_kernel_param __setup_start[], __setup_end[]; + ++#ifdef CONFIG_KDB ++static int __init kdb_setup(char *str) ++{ ++ if (strcmp(str, "on") == 0) { ++ kdb_on = 1; ++ } else if (strcmp(str, "on-nokey") == 0) { ++ kdb_on = 2; ++ } else if (strcmp(str, "off") == 0) { ++ kdb_on = 0; ++ } else if (strcmp(str, "early") == 0) { ++ kdb_on = 1; ++ kdb_flags |= KDB_FLAG_EARLYKDB; ++ } else ++ printk("kdb flag %s not recognised\n", str); ++ return 0; ++} ++ ++__setup("kdb=", kdb_setup); ++#endif /* CONFIG_KDB */ ++ + static int __init obsolete_checksetup(char *line) + { + struct obs_kernel_param *p; +@@ -664,6 +688,14 @@ asmlinkage void __init start_kernel(void + calibrate_delay(); + pidmap_init(); + anon_vma_init(); ++ ++#ifdef CONFIG_KDB ++ kdb_init(); ++ if (KDB_FLAG(EARLYKDB)) { ++ KDB_ENTER(); ++ } ++#endif /* CONFIG_KDB */ ++ + #ifdef CONFIG_X86 + if (efi_enabled) + efi_enter_virtual_mode(); +--- /dev/null ++++ b/kdb/ChangeLog +@@ -0,0 +1,2040 @@ ++2008-11-26 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc6-common-1. ++ ++2008-11-12 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc4-common-1. ++ ++2008-11-04 Jay Lan ++ ++ * medusa needs kdb to handle '\n' in kdb_read(), ++ Cliff Wickman ++ * kdb-v4.4-2.6.28-rc3-common-1. ++ ++2008-10-29 Jay Lan ++ ++ * "Commandeer vector 0xfe for KDB_VECTOR", version 2. ++ Cliff Wickman ++ * kdb-v4.4-2.6.28-rc2-common-2. ++ ++2008-10-27 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc2-common-1. ++ ++2008-10-20 Jay Lan ++ ++ * kdb-v4.4-2.6.27-common-1. ++ ++2008-09-30 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc8-common-1. ++ ++2008-09-22 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc7-common-1. ++ ++2008-09-03 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc5-common-1. ++ ++2008-08-19 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc3-common-1. ++ ++2008-08-15 Jay Lan ++ ++ * mm_online_pgdat_export_symbol, Jay Lan ++ - Fix compilation error by exporting first_online_pgdat & ++ next_online_pgdat for 'pgdat' command. ++ * kdb-v4.4-2.6.27-rc2-common-2.1. ++ ++2008-08-14 Jay Lan ++ ++ * Support 'kdump' command to take a kdump vmcore from KDB, ++ Dan Aloni (da-x@monatomic.org), ++ Jason Xiao (jidong.xiao@gmail.com), ++ Jay Lan (jlan@sgi.com) ++ * kdb-v4.4-2.6.27-rc2-common-2. ++ ++2008-08-06 Jay Lan ++ ++ * Fix up the NULL pointer deference issue in ohci_kdb_poll_char, ++ Jason Xiao ++ * kdb-v4.4-2.6.27-rc2-common-1. ++ ++2008-07-18 Jay Lan ++ ++ * support Hardware Breakpoint (bph/bpha) commands ++ IA64: Greg Banks ++ X86: Konstantin Baydarov ++ * kdb-v4.4-2.6.26-common-2. ++ ++2008-07-14 Jay Lan ++ ++ * kdb-v4.4-2.6.26-common-1. ++ ++2008-07-11 Jay Lan ++ ++ * New commands and some fixups and enhancements, ++ Joe Korty ++ John Blackwood ++ Jim Houston ++ - Use the non-sleeping copy_from_user_atomic. ++ - Enhance kdb_cmderror diagnostic output. ++ - Expand the KDB 'duplicate command' error message. ++ - Touch NMI watchdog in various KDB busy-loops. ++ - Support IMB HS20 Blade 8843 platform. ++ - Display exactly which cpus needed an NMI to get them into kdb. ++ - Better document that kdb's 'ps A' command can be used to show ++ _all_ processes and threads ++ - Suppress KDB boottime INFO messages if quiet boot. ++ - Add a KDB breakpoint to the OOPs path. ++ - Add CONFIG_DISCONTIGMEM support to kdbm_memmap. ++ - Extend the KDB task command to handle CONFIG_NUMA fields. ++ - Extend the KDB vm command to support NUMA stuff. ++ - Create the KDB mempolicy command. ++ - Create a pgdat command for KDB. ++ - Fix a hang on boot on some i386 systems. ++ * kdb-v4.4-2.6.26-rc9-common-1. ++ ++2008-06-30 Jay Lan ++ ++ * compilation warning cleanup, Cliff Wickman ++ * kdb-v4.4-2.6.26-rc8-common-1. ++ ++2008-06-25 Jay Lan ++ ++ * Added John Blackwood to the authors of ++ kdb-v4.4-2.6.26-rc4-common-2. ++ * kdb-v4.4-2.6.26-rc7-common-1. ++ ++2008-06-24 Jay Lan ++ ++ * support lcrash style debug_info file: Cliff Wickman ++ - It adds to kdb the ability to symbolically dereference structure ++ pointers through a lcrash-style debug_info file. ++ - Implements "print", "px", and "pd" print commands. ++ - Implements "walk" command to follow linked lists. ++ - Implements "whatis" to display a structure (with offsets). ++ - Implements "sizeof" for types (structures, typedefs, etc.). ++ * kdb-v4.4-2.6.26-rc5-common-2. ++ ++2008-06-06 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc5-common-1. ++ ++2008-06-05 Jay Lan ++ ++ * fixed 'rq/rqa' command runs off the end of runqueue's rt.active ++ priority bitmap array, John Blackwood & ++ Lachlan McIlroy ++ * kdb-v4.4-2.6.26-rc4-common-2. ++ ++2008-05-30 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc4-common-1. ++ ++2008-05-20 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc3-common-1. ++ ++2008-05-13 Jay Lan ++ ++ * XPC support is removed from KDB due to XPC changes in 2.6.26-rc1. ++ * kdb-v4.4-2.6.26-rc1-common-1. ++ ++2008-04-17 Jay Lan ++ ++ * kdb-v4.4-2.6.25-common-1. ++ ++2008-03-16 Jay Lan ++ ++ * kdb-v4.4-2.6.25-rc6-common-1. ++ ++2008-03-03 Jay Lan ++ ++ * kdb-v4.4-2.6.25-rc3-common-1. ++ ++2008-02-26 Jay Lan ++ ++ * remove 'fastcall' from kdb code. ++ * kdb-v4.4-2.6.25-rc2-common-1. ++ ++2008-02-19 Jay Lan ++ ++ * kdb-v4.4-2.6.25-rc1-common-1. ++ ++2008-02-06 Jay Lan ++ ++ * Backed out USB UHCI support since it caused dropped characters and ++ broke OHCI. ++ * Restored "archkdbcommon" commands for x86. It was lost at the x86 ++ merge. ++ * Detecting if the HC was "busy", Aaron Young ++ * kdb-v4.4-2.6.24-common-2. ++ ++2008-01-29 Jay Lan ++ ++ * kdb-v4.4-2.6.24-common-1. ++ ++2008-01-22 Jay Lan ++ ++ * USB UHCI kdb support, Konstantin Baydarov ++ * kdb-v4.4-2.6.24-rc8-common-3. ++ ++2008-01-18 Jay Lan ++ ++ * USB EHCI kdb support, Aaron Young ++ * kdb-v4.4-2.6.24-rc8-common-2. ++ ++2008-01-18 Jay Lan ++ ++ * kdb-v4.4-2.6.24-rc8-common-1. ++ ++2008-01-07 Jay Lan ++ ++ * kdb-v4.4-2.6.24-rc7-common-1. ++ ++2007-12-21 Jay Lan ++ ++ * Renamed kdb/kdba_bt_x86.c to arch/x86/kdba_bt.c. And thus, the x86 ++ backtrace code is now moved into the kdb x86 patch. ++ * kdb v4.4-2.6.24-rc6-common-1. ++ ++2007-12-12 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc5-common-1. ++ ++2007-12-05 Jay Lan ++ ++ * Fixed a 'sysctl table check failed' problem. ++ * kdb v4.4-2.6.24-rc4-common-1. ++ ++2007-11-26 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc3-common-1. ++ ++2007-11-13 Jay Lan ++ ++ * Back ported "New KDB USB interface" from Aaron Young in ++ v4.4-2.6.23-common-2 to 2.6.24 kdb patchset. ++ * kdb v4.4-2.6.24-rc2-common-2. ++ ++2007-11-12 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc2-common-1. ++ ++2007-11-09 Jay Lan ++ ++ * Rebase to 2.6.24-rc1 kernel ++ * - merged kdb-v4.4-2.6.23-i386-1 and kdb-v4.4-2.6.23-x86_64-1 ++ * into kdb-v4.4-2.6.24-rc1-x86-1 ++ * - Fields "done", "sglist_len", and "pid" are removed from ++ * struct scsi_cmnd. Thus, these fields are no longer displayed ++ * on "sc" command. ++ * kdb v4.4-2.6.24-rc1-common-1. ++ ++2007-11-08 Jay Lan ++ ++ * New KDB USB interface, Aaron Young ++ * 1. This patch allows KDB to work with any Host Contoller driver ++ * and call the correct HC driver poll routine (as long as the ++ * HC driver provides a .kdb_poll_char routine via it's ++ * associated hc_driver struct). ++ * 2. Hotplugged keyboards are now recognized by KDB. ++ * 3. Currently KDB can only make use of 1 USB type keyboard. ++ * New code can handle up to 8 attached keyboards - input is ++ * multiplexed from all of them while in kdb. ++ * kdb v4.4-2.6.23-common-2. ++ ++2007-10-24 Jay Lan ++ ++ * kdb v4.4-2.6.23-common-1. ++ ++2007-09-26 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc8-common-1. ++ ++2007-09-21 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc7-common-1. ++ ++2007-09-12 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc6-common-1. ++ ++2007-09-06 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc5-common-1. ++ ++2007-08-30 Keith Owens ++ ++ * New i386/x86_64 backtrace requires that kdb_save_running() does not ++ exit until after kdb_main_loop() has completed. ++ * List more noret functions in i386/x86_64 backtrace code. ++ * Call to a noret function ends a basic block. ++ * After a call to a noret function, eip/rip may be pointing at the next ++ function or not, depending on function alignment. Jay Lan. ++ * kdb v4.4-2.6.23-rc4-common-2. ++ ++2007-08-30 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc4-common-1. ++ ++2007-08-28 Keith Owens ++ ++ * kdb/kdba_bt_x86.c: ++ * Handle the variable amount of stack data that is pushed by x86_64 ++ * hardware on an interrupt. ++ * Add instruction vmsave. ++ * Handle pop to %rsp. ++ * Cope with return address for functions defined as ATTRIB_NORET. ++ * Include CONFIG_DEBUG_INFO in the summary line of bb_all. ++ * Check for an interrupt that was delivered while user space was in ++ * control. ++ * A return to child_rip ends a backtrace. ++ * Ignore level2_kernel_pgt and level3_kernel_pgt data areas if they ++ * occur within the text segment. ++ * kdb v4.4-2.6.23-rc3-common-2. ++ ++2007-08-24 Keith Owens ++ ++ * kdb v4.4-2.6.23-rc3-common-1. ++ ++2007-08-24 Jay Lan ++ ++ * kdb/kdba_bt_x86.c: ++ * retint_kernel is only defined for CONFIG_PREEMPT. ++ * Handle assembler code for CONFIG_HIBERNATION=y. ++ * Handle assembler code for CONFIG_MATH_EMULATION=y. ++ * Handle assembler code for CONFIG_XEN=y. ++ * Handle assembler code for CONFIG_KPROBES=y. ++ * Add CC version to the bb_all header. ++ * Handle spurious label in jprobe_return. ++ * Handle stack switch in jprobe_return. ++ * Prefix register name with '%' in xadd/xchg temporary variable. ++ * Require bb_usage_mov() to handle all the special cases internally. ++ * Handle stack manipulation for kexec. ++ * Handle spurious label in kretprobe_trampoline_holder. ++ * Add instructions clgi, invlpga, rcl, rdpmc, stgi, vmclear, ++ * vmlaunch, vmload, vmptrld, vmread, vmresume, vmrun, vmwrite, ++ * xstore-rng. ++ * Exclude more 16 bit and/or real mode acpi functions from bb_all. ++ * Handle assembler stack switching code in i386 do_softirq. ++ * kdb/kdbmain.c: ++ * Add CC version to the summary output. ++ * Bump debug_kmalloc pool from 128K to 256K, some of the kernel ++ * functions have huge numbers of basic blocks and jumps between them. ++ * Correct reinstallation of breakpoints when exiting KDB. ++ * Keith Owens. ++ * kdb v4.4-2.6.23-rc2-common-2. ++ ++2007-08-07 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc2-common-1. ++ ++2007-08-03 Keith Owens ++ ++ * kdba_bt_x86.c: Rename some variables to make the code more readable. ++ Print more debug information when merging register states and when ++ calculating the new stack pointer. ++ * kdb v4.4-2.6.23-rc1-common-2. ++ ++2007-07-30 Keith Owens ++ ++ * kdb v4.4-2.6.23-rc1-common-1. ++ ++2007-07-26 Keith Owens ++ ++ * New x86 backtrace code. ++ * kdb v4.4-2.6.22-common-4. ++ ++2007-07-17 Keith Owens ++ ++ * Make kdb_printf_lock an irq lock to keep lockdep happy. ++ * kdb v4.4-2.6.22-common-3. ++ ++2007-07-13 Keith Owens ++ ++ * Increase the size of the debug_alloc pool. ++ * Add the caller that obtained each entry in the debug_alloc pool. ++ * Poison entries in the debug_alloc pool. ++ * Track current and maximum usage in debug_alloc pool. ++ * Print the debug_alloc entries that are still in use when kdb exits ++ (memory leaks). ++ * Increase the default value of BTARGS to 9. ++ * kdb v4.4-2.6.22-common-2. ++ ++2007-07-09 Keith Owens ++ ++ * kdb v4.4-2.6.22-common-1. ++ ++2007-07-02 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc7-common-1. ++ ++2007-06-20 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc5-common-1. ++ ++2007-06-15 Keith Owens ++ ++ * Do not include asm/kdb.h unless CONFIG_KDB is on. Dave Jiang. ++ * kdb v4.4-2.6.22-rc4-common-2. ++ ++2007-06-08 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc4-common-1. ++ ++2007-05-28 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc3-common-1. ++ ++2007-05-22 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc2-common-1. ++ ++2007-05-22 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc1-common-1. ++ ++2007-05-17 Keith Owens ++ ++ * Add rdmsr and wrmsr commands for i386 and x86_64. Original patch by ++ Bernardo Innocenti for i386, reworked by Keith Owens to make it safe ++ on all cpu models and to handle both i386 and x86_64. ++ * kdb v4.4-2.6.21-common-3. ++ ++2007-05-15 Keith Owens ++ ++ * Correct alignment of debug_alloc_header. ++ * kdb v4.4-2.6.21-common-2. ++ ++2007-04-29 Keith Owens ++ ++ * kdb v4.4-2.6.21-common-1. ++ ++2007-04-16 Keith Owens ++ ++ * Remove dead symbol declarations. ++ * kdb v4.4-2.6.21-rc7-common-2. ++ ++2007-04-16 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc7-common-1. ++ ++2007-04-10 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc6-common-1. ++ ++2007-04-02 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc5-common-1. ++ ++2007-03-19 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc4-common-1. ++ ++2007-03-14 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc3-common-1. ++ ++2007-03-14 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc2-common-1. ++ ++2007-03-01 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc1-common-1. ++ ++2007-03-01 Keith Owens ++ ++ * Remove sparse warnings. ++ * kdb v4.4-2.6.20-common-6. ++ ++2007-02-27 Keith Owens ++ ++ * set_irq_regs() on entry to kdb() if they are not already set. ++ * kdb v4.4-2.6.20-common-5. ++ ++2007-02-22 Keith Owens ++ ++ * Initialise struct disassemble_info in kdb_id1(). ++ * kdb v4.4-2.6.20-common-4. ++ ++2007-02-16 Keith Owens ++ ++ * Clean up debug_alloc_pool code. ++ * kdb v4.4-2.6.20-common-3. ++ ++2007-02-16 Keith Owens ++ ++ * Initialise variable bits of struct disassemble_info each time. ++ * kdb v4.4-2.6.20-common-2. ++ ++2007-02-06 Keith Owens ++ ++ * kdb v4.4-2.6.20-common-1. ++ ++2007-02-01 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc7-common-1. ++ ++2007-01-08 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc4-common-1. ++ ++2007-01-02 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc3-common-1. ++ ++2006-12-21 Keith Owens ++ ++ * Initialize the debug_kmalloc pool on the first call, so it can be ++ used at any time. ++ * kdb v4.4-2.6.20-rc1-common-2. ++ ++2006-12-20 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc1-common-1. ++ ++2006-11-30 Keith Owens ++ ++ * kdb v4.4-2.6.19-common-1. ++ ++2006-11-30 Keith Owens ++ ++ * Do not access registers if kdb_current_regs is NULL. ++ * kdb v4.4-2.6.19-rc6-common-3. ++ ++2006-11-27 Keith Owens ++ ++ * Only use VT keyboard if the command line allows it and ACPI indicates ++ that there is an i8042. ++ * Optimize kdb_read() to reduce the risk of dropping input characters. ++ * Print cpumasks as lists instead of hex, also cope with long lists. ++ * kdb v4.4-2.6.19-rc6-common-2. ++ ++2006-11-20 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc6-common-1. ++ ++2006-11-09 Keith Owens ++ ++ * Change kdb() to fastcall. ++ * Correct loop in kdb_help(). Georg Nikodym. ++ * Only use VT console if the command line allows it. ++ * kdb v4.4-2.6.19-rc5-common-2. ++ ++2006-11-08 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc5-common-1. ++ ++2006-11-01 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc4-common-1. ++ ++2006-10-24 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc3-common-1. ++ ++2006-10-24 Keith Owens ++ ++ * Remove redundant regs and envp parameters. ++ * kdb v4.4-2.6.19-rc2-common-2. ++ ++2006-10-18 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc2-common-1. ++ ++2006-10-11 Keith Owens ++ ++ * Move kdbm_x86.c from the i386 to the common KDB patch. ++ * Expand kdbm_x86.c to work on x86_64 as well as i386. ++ * kdb v4.4-2.6.19-rc1-common-2. ++ ++2006-10-09 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc1-common-1. ++ ++2006-10-06 Keith Owens ++ ++ * Remove #include ++ * kdb v4.4-2.6.18-common-2. ++ ++2006-09-20 Keith Owens ++ ++ * kdb v4.4-2.6.18-common-1. ++ ++2006-09-15 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc7-common-1. ++ ++2006-08-29 Keith Owens ++ ++ * Rewrite all backtrace code. ++ * kdb v4.4-2.6.18-rc5-common-2. ++ ++2006-08-28 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc5-common-1. ++ ++2006-08-08 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc4-common-1. ++ ++2006-08-04 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc3-common-1. ++ ++2006-07-18 Keith Owens ++ ++ * 8250.c locking has been fixed so there is no need to break spinlocks ++ for keyboard entry. ++ * kdb v4.4-2.6.18-rc2-common-2. ++ ++2006-07-18 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc2-common-1. ++ ++2006-07-12 Keith Owens ++ ++ * Remove dead KDB_REASON codes. ++ * The main kdb() function is now always entered with interrupts ++ disabled, so there is no need to disable bottom halves. ++ * sparse cleanups. ++ * kdb v4.4-2.6.18-rc1-common-2. ++ ++2006-07-07 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc1-common-1. ++ ++2006-07-04 Keith Owens ++ ++ * Add KDB_REASON_CPU_UP and callbacks for cpus coming online. ++ * Relegate KDB_REASON_SILENT to KDB internal use only. ++ * Backout the v4.4-2.6.15-common-3 change that made KDB_REASON_SILENT ++ wait for cpus, the Dell Xeon problem has been fixed. ++ * notify_die() is not called for KDB_REASON_SILENT nor ++ KDB_REASON_CPU_UP, these events do not stay in KDB. ++ * Export kdb_current_task for kdbm_x86. SuSE patch ++ kdb-missing-export.diff ++ * Scale kdb_wait_for_cpus_secs by the number of online cpus. ++ * Delete kdb_enablehwfault, architectures now do their own setup. ++ * Delete kdba_enable_mce, architectures now do their own setup. ++ * Delete kdba_enable_lbr, kdba_disable_lbr, kdba_print_lbr, ++ page_fault_mca. Only ever implemented on x86, difficult to maintain ++ and rarely used in the field. ++ * Replace #ifdef KDB_HAVE_LONGJMP with #ifdef kdba_setjmp. ++ * kdb v4.4-2.6.17-common-2. ++ ++2006-06-19 Keith Owens ++ ++ * kdb v4.4-2.6.17-common-1. ++ ++2006-05-31 Keith Owens ++ ++ * Break spinlocks for keyboard entry. Hopefully a temporary hack while ++ I track down why keyboard entry to KDB is hanging. ++ * kdb v4.4-2.6.17-rc5-common-2. ++ ++2006-05-25 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc5-common-1. ++ ++2006-05-15 Keith Owens ++ ++ * Refresh bfd related files from binutils 2.16.91.0.2. ++ * kdb v4.4-2.6.17-rc4-common-2. ++ ++2006-05-12 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc4-common-1. ++ ++2006-04-28 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc3-common-1. ++ ++2006-04-22 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc2-common-1. ++ ++2006-04-11 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc1-common-1. ++ ++2006-04-05 Keith Owens ++ ++ * More fixes for the timing race with KDB_ENTER_SLAVE. ++ * kdb v4.4-2.6.16-common-5. ++ ++2006-03-30 Keith Owens ++ ++ * Some code was testing KDB_IS_RUNNING() twice, which left it open to ++ races. Cache the result instead. ++ * kdb v4.4-2.6.16-common-4. ++ ++2006-03-30 Keith Owens ++ ++ * Change CONFIG_LKCD to CONFIG_LKCD_DUMP. ++ * kdb v4.4-2.6.16-common-3. ++ ++2006-03-22 Keith Owens ++ ++ * Add some more xpc flags. Dean Nelson, SGI. ++ * Replace open coded counter references with atomic_read(). ++ * Pass early_uart_console to early_uart_setup(). Francois ++ Wellenreiter, Bull. ++ * Replace open code with for_each_online_cpu(). ++ * If cpus do not come into kdb after a few seconds then let ++ architectures send a more forceful interrupt. ++ * Close a timing race with KDB_ENTER_SLAVE. ++ * kdb v4.4-2.6.16-common-2. ++ ++2006-03-21 Keith Owens ++ ++ * kdb v4.4-2.6.16-common-1. ++ ++2006-03-14 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc6-common-1. ++ ++2006-02-28 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc5-common-1. ++ ++2006-02-20 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc4-common-1. ++ ++2006-02-06 Keith Owens ++ ++ * Change CONFIG_CRASH_DUMP to CONFIG_LKCD. ++ * Remove obsolete kdb_notifier_list. ++ * kdb v4.4-2.6.16-rc2-common-2. ++ ++2006-02-06 Keith Owens ++ ++ * Add xpcusers command. Dean Nelson, SGI. ++ * kdb v4.4-2.6.16-rc2-common-1. ++ ++2006-02-02 Keith Owens ++ ++ * Check if we have a console before using it for KDB. ++ * kdb v4.4-2.6.16-rc1-common-3. ++ ++2006-02-01 Keith Owens ++ ++ * Add option 'R' to the pid command to reset to the original task. ++ * Include 'pid R' in archkdb* commands to reset up the original failing ++ task. Users may have switched to other cpus and/or tasks before ++ issuing archkdb. ++ * Compile fix for kdbm_pg.c on i386. ++ * kdb v4.4-2.6.16-rc1-common-2. ++ ++2006-01-18 Keith Owens ++ ++ * kdb v4.4-2.6.16-rc1-common-1. ++ ++2006-01-11 Keith Owens ++ ++ * Plug a timing race between KDB_ENTER_SLAVE and KDB_ENTER, and allow ++ the cpu command to switch to a slave cpu. ++ * KDB_REASON_SILENT now waits for other cpus, to avoid spurious NMI ++ events that were seen on some Xeon systems. ++ * kdb v4.4-2.6.15-common-3. ++ ++2006-01-08 Keith Owens ++ ++ * kdb mainline invokes DIE_KDEBUG_ENTER and DIE_KDEBUG_LEAVE via ++ notify_die. ++ * Move xpc debug support from xpc to mainline kdb. ++ * kdbm_cm.c: check if file_lock_operations or lock_manager_operations ++ are set before dereferencing them. Felix Blyakher, SGI. ++ * kdb v4.4-2.6.15-common-2. ++ ++2006-01-04 Keith Owens ++ ++ * Print all buffers on a page in inode pages and update formatting to be ++ legible, too. David Chinner, SGI. ++ * Update page flags in kdbm_pg. ++ * Remove inline from *.c files. ++ * kdb v4.4-2.6.15-common-1. ++ ++2005-12-25 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc7-common-1. ++ ++2005-12-20 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc6-common-1. ++ ++2005-12-10 Keith Owens ++ ++ * Update mapping of flags to strings in kdbm_pg.c and kdbm_vm.c. ++ * kdb v4.4-2.6.15-rc5-common-3. ++ ++2005-12-06 Keith Owens ++ ++ * Add RECOVERY flag to global KDB flags. ++ * Add kdb_{save,restore}_flags. ++ * kdb v4.4-2.6.15-rc5-common-2. ++ ++2005-12-05 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc5-common-1. ++ ++2005-12-02 Keith Owens ++ ++ * kdbm_vm.c: offsets of page macros should be unsigned long. Reported ++ by Dean Nelson, SGI. ++ * kdb v4.4-2.6.15-rc4-common-1. ++ ++2005-11-30 Keith Owens ++ ++ * New follow_page() API. ++ * kdb v4.4-2.6.15-rc3-common-1. ++ ++2005-11-21 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc2-common-1. ++ ++2005-11-15 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc1-common-1. ++ ++2005-11-15 Keith Owens ++ ++ * Allow kdb_printf() to be used outside kdb, in preemptible context. ++ * Build with CONFIG_SWAP=n. Reported by Leo Yuriev. ++ * kdb v4.4-2.6.14-common-2. ++ ++2005-10-28 Keith Owens ++ ++ * kdb v4.4-2.6.14-common-1. ++ ++2005-10-21 Keith Owens ++ ++ * kdb v4.4-2.6.14-rc5-common-1. ++ ++2005-10-11 Keith Owens ++ ++ * Handle removal of USB keyboard. Aaron Young, SGI. ++ * kdb v4.4-2.6.14-rc4-common-1. ++ ++2005-10-05 Keith Owens ++ ++ * Extend kdb_notifier_list() codes to include dumping. ++ * Use emergency_restart() for reboot, it can be called from interrupt ++ context, unlike machine_restart(). ++ * kdb v4.4-2.6.14-rc3-common-1. ++ ++2005-09-21 Keith Owens ++ ++ * Support kdb_current_task in register display and modify commands. ++ * Document what changes kdb's notion of the current task. ++ * Update rd documentation for IA64. ++ * Move some definictions to kdbprivate.h and remove some unused symbol ++ exports. ++ * kdb v4.4-2.6.14-rc2-common-1. ++ ++2005-09-20 Keith Owens ++ ++ * Document IA64 handlers command. ++ * Add more fields to the task command. ++ * Cope with MCA/INIT handlers in the ps command. ++ * Namespace cleanup, delete unused exports, make some functions static. ++ * Add a kdb_notifier_list callback when kdb is about to reboot the ++ system. ++ * kdb v4.4-2.6.14-rc1-common-1. ++ ++2005-08-29 Keith Owens ++ ++ * kdb v4.4-2.6.13-common-1. ++ ++2005-08-24 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc7-common-1. ++ ++2005-08-08 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc6-common-1. ++ ++2005-08-02 Keith Owens ++ ++ * Print more fields from filp, dentry. ++ * Add kdb=on-nokey to suppress kdb entry from the keyboard. ++ * kdb v4.4-2.6.13-rc5-common-1. ++ ++2005-07-30 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc4-common-1. ++ ++2005-07-26 Keith Owens ++ ++ * Fix compile problem with CONFIG_USB_KBD. ++ * kdb v4.4-2.6.13-rc3-common-3. ++ ++2005-07-22 Keith Owens ++ ++ * The asmlinkage kdb() patch was lost during packaging. Reinstate it. ++ * kdb v4.4-2.6.13-rc3-common-2. ++ ++2005-07-19 Keith Owens ++ ++ * Add support for USB keyboard (OHCI only). Aaron Young, SGI. ++ * kdb v4.4-2.6.13-rc3-common-1. ++ ++2005-07-08 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc2-common-1. ++ ++2005-07-01 Keith Owens ++ ++ * Make kdb() asmlinkage to avoid problems with CONFIG_REGPARM. ++ * Change some uses of smp_processor_id() to be preempt safe. ++ * Use DEFINE_SPINLOCK(). ++ * kdb v4.4-2.6.13-rc1-common-1. ++ ++2005-06-18 Keith Owens ++ ++ * kdb v4.4-2.6.12-common-1. ++ ++2005-06-08 Keith Owens ++ ++ * Correct early exit from bd *. ++ * kdb v4.4-2.6.12-rc6-common-1. ++ ++2005-05-25 Keith Owens ++ ++ * Delete Documentation/kdb/dump.txt. lkcd now has reasonable ++ integration with kdb. ++ * kdb v4.4-2.6.12-rc5-common-1. ++ ++2005-05-08 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc4-common-1. ++ ++2005-04-21 Keith Owens ++ ++ * Add rpte command (find the pte for a physical page). ++ * kdb v4.4-2.6.12-rc3-common-1. ++ ++2005-04-06 Keith Owens ++ ++ * Add rq and rqa commands. John Hawkes, SGI. ++ * kdb v4.4-2.6.12-rc2-common-1. ++ ++2005-03-29 Keith Owens ++ ++ * Use register_sysctl_table() instead of patching kernel/sysctl.c. ++ * Non-ASCII characters are not printable. ++ * kdb v4.4-2.6.12-rc1-common-1. ++ ++2005-03-15 Keith Owens ++ ++ * More coexistence patches for lkcd. Jason Uhlenkott, SGI. ++ * kdb v4.4-2.6.11-common-3. ++ ++2005-03-08 Keith Owens ++ ++ * Coexistence patches for lkcd. Jason Uhlenkott, SGI. ++ * kdb v4.4-2.6.11-common-2. ++ ++2005-03-03 Keith Owens ++ ++ * Add kdb to drivers/serial/8250_early.c. Francois Wellenreiter, Bull. ++ * kdb v4.4-2.6.11-common-1. ++ ++2005-02-14 Keith Owens ++ ++ * kdb v4.4-2.6.11-rc4-common-1. ++ ++2005-02-08 Keith Owens ++ ++ * kdb v4.4-2.6.11-rc3-bk4-common-1. ++ ++2005-02-03 Keith Owens ++ ++ * Print more superblock fields. Nathan Scott, SGI. ++ * Remove kallsyms correction for modules, Linus took it. ++ * kdb v4.4-2.6.11-rc3-common-1. ++ ++2005-01-27 Keith Owens ++ ++ * Add bio command. Nathan Scott, SGI. ++ * kdb v4.4-2.6.11-rc2-common-1. ++ ++2005-01-20 Keith Owens ++ ++ * Include kallsyms correction for modules until Linus takes it. ++ * kdb v4.4-2.6.11-rc1-bk7-common-1. ++ ++2005-01-12 Keith Owens ++ ++ * kallsyms now supports all symbols properly, remove kdb patch. ++ * Add last ditch allocator for debugging. ++ * Update kdb_meminfo_read_proc() for vmalloc changes. ++ * Update kdbm_vm.c for 4 level page tables. ++ * kdb v4.4-2.6.11-rc1-common-1. ++ ++2004-12-25 Keith Owens ++ ++ * Add kobject command. ++ * Ignore low addresses and large offsets in kdbnearsym(). ++ * Console updates for sn2 simulator. ++ * kdb v4.4-2.6.10-common-1. ++ ++2004-12-07 Keith Owens ++ ++ * kdb v4.4-2.6.10-rc3-common-1. ++ ++2004-11-23 Keith Owens ++ ++ * Remove warning message from kdb_get_one_user_page(), it was too noisy. ++ * kdb v4.4-2.6.10-rc2-common-1. ++ ++2004-11-02 Keith Owens ++ ++ * Build with kdb patch applied but CONFIG_KDB=n. ++ * kdb v4.4-2.6.10-rc1-common-2. ++ ++2004-10-29 Keith Owens ++ ++ * Handle new compression scheme for kallsyms. ++ * Handle move of DEAD and ZOMBIE for task->state to task->exit_state. ++ * Tweak the concept of a valid kernel address to get all symbols, ++ including the symbols in the ia64 gate page. ++ * kdb v4.4-2.6.10-rc1-common-1. ++ ++2004-10-21 Keith Owens ++ ++ * Handle variable size for the kernel log buffer. ++ * kdb v4.4-2.6.9-common-2. ++ ++2004-10-19 Keith Owens ++ ++ * kdb v4.4-2.6.9-common-1. ++ ++2004-10-12 Keith Owens ++ ++ * kdb v4.4-2.6.9-rc4-common-1. ++ ++2004-10-01 Keith Owens ++ ++ * kdb v4.4-2.6.9-rc3-common-1. ++ ++2004-09-30 Keith Owens ++ ++ * Add stackdepth command to Documentation/kdb/kdb.mm. stackdepth is ++ only supported on i386 and ia64 at the moment. ++ * Skip kdbm_pg memmap build on x86_64. Scott Lurndal, 3leafnetworks. ++ * Export kdb_serial_str for modular I/O. Bryan Cardillo, UPenn. ++ * Reinstate tab completion for symbols. ++ * kdb v4.4-2.6.9-rc2-common-2. ++ ++2004-09-14 Keith Owens ++ ++ * Add task states C (traCed) and E (dEad). ++ * kdb v4.4-2.6.9-rc2-common-1. ++ ++2004-08-27 Keith Owens ++ ++ * kdb v4.4-2.6.9-rc1-common-1. ++ ++2004-08-14 Keith Owens ++ ++ * kdb v4.4-2.6.8-common-1. ++ ++2004-08-12 Keith Owens ++ ++ * kdb v4.4-2.6.8-rc4-common-1. ++ ++2004-08-05 Keith Owens ++ ++ * Mark kdb_initcall as __attribute_used__ for newer gcc. ++ * kdb v4.4-2.6.8-rc3-common-2. ++ ++2004-08-04 Keith Owens ++ ++ * Add mdp (memory display physical) comnmand. ++ Ananth N Mavinakayanahalli, IBM. ++ * kdb v4.4-2.6.8-rc3-common-1. ++ ++2004-07-18 Keith Owens ++ ++ * Patch for new sn_console. Erik Jacobson. SGI. ++ * kdb v4.4-2.6.8-rc2-common-1. ++ ++2004-07-12 Keith Owens ++ ++ * Convert kdbm_task to standard cpumask_t. ++ * Document '*' (all breakpoints) option on bd/be/bc commands. ++ * kdb v4.4-2.6.8-rc1-common-1. ++ ++2004-06-30 Keith Owens ++ ++ * Common changes to help the x86-64 port. ++ * kdb v4.4-2.6.7-common-3. ++ ++2004-06-20 Keith Owens ++ ++ * Move kdb includes in mm/swapfile.c to reduce conflicts with other ++ SGI patches. ++ * kdb v4.4-2.6.7-common-2. ++ ++2004-06-16 Keith Owens ++ ++ * kdb v4.4-2.6.7-common-1. ++ ++2004-06-09 Keith Owens ++ ++ * kdb v4.4-2.6.7-rc3-common-1. ++ ++2004-06-09 Keith Owens ++ ++ * Namespace clean up. Mark code/variables as static when it is only ++ used in one file, delete dead code/variables. ++ * Saved interrupt state requires long, not int. ++ * kdb v4.4-2.6.7-rc2-common-3. ++ ++2004-06-08 Keith Owens ++ ++ * Whitespace clean up, no code changes. ++ * kdb v4.4-2.6.7-rc2-common-2. ++ ++2004-06-07 Keith Owens ++ ++ * kdb v4.4-2.6.7-rc2-common-1. ++ ++2004-06-06 Keith Owens ++ ++ * Avoid recursion problems in kdb_init(). ++ * Add standard archkdb commands. ++ * Add per_cpu command. ++ * Move kdb_{get,put}userarea_size definitions to linux/kdb.h. ++ * kdb v4.4-2.6.6-common-2. ++ ++2004-05-23 Keith Owens ++ ++ * Shrink the output from the cpu command. ++ * Add cpu state 'I', the cpu is idle. ++ * Add cpu state '+', some kdb data is available but the cpu is not ++ responding. ++ * Do not print tasks in state I or M by default in ps and bta commands. ++ * Add states I (idle task) and M (sleeping system daemon) to ps and ++ bta commands. ++ * Delete unused variables. ++ * Move private kdb fields from kdb.h to kdbprivate.h. ++ * Print 'for keyboard entry' for the special cases when KDB_ENTER() is ++ used to get registers. ++ * Move bfd.h and ansidecl.h from arch/$(ARCH)/kdb to include/asm-$(ARCH) ++ and remove -I arch/$(ARCH)/kdb. ++ * dmesg command now prints from either the start or end of dmesg, or at ++ an arbitrary point in the middle of the kernel log buffer. ++ * Sensible string dump for multi byte md commands. ++ * 'page' command handles ia64 correctly. ++ * Show some activity when waiting for cpus to enter kdb. ++ * Change the KDB entry code to KDB. ++ * Allow comment commands, starting with '#'. ++ * Commands defined using defcmd from kdb_cmds are not printed as they ++ are entered, use defcmd with no parameters to print all the defined ++ commands. ++ * Add summary command. ++ * Update copyright notices. ++ * Zero suppression on md command. ++ * Make set NOSECT=1 the default. ++ * PPC64 uses OF-stdout instead of console. Ananth N Mavinakayanahalli. ++ * kdb v4.4-2.6.6-common-1. ++ ++2004-05-10 Keith Owens ++ ++ * kdb v4.3-2.6.6-common-1. ++ ++2004-05-06 Keith Owens ++ ++ * kdb v4.3-2.6.6-rc3-common-1. ++ ++2004-05-06 Keith Owens ++ ++ * kdb v4.3-2.6.6-rc2-common-1. ++ ++2004-04-30 Keith Owens ++ ++ * Rewrite inode_pages command for new radix code in struct page. ++ * kdb v4.3-2.6.6-rc1-common-1. ++ ++2004-04-11 Keith Owens ++ ++ * Unlock sn_sal_lock before entering kdb from sn_serial. ++ * kdb v4.3-2.6.5-common-2. ++ ++2004-04-05 Keith Owens ++ ++ * kdb v4.3-2.6.5-common-1. ++ ++2004-03-22 Keith Owens ++ ++ * kdb v4.3-2.6.5-rc2-common-1. ++ ++2004-03-12 Keith Owens ++ ++ * More work to avoid spurious messages from WARN_CONSOLE_UNLOCKED(). ++ * bh command bug fixes. Nathan Scott. ++ * kdb v4.3-2.6.4-common-1. ++ ++2004-03-06 Keith Owens ++ ++ * Set KDB_IS_RUNNING() during kdb_init to avoid spurious messages from ++ WARN_CONSOLE_UNLOCKED(). ++ * Correct loss of symbol names in kdbnearsym. ++ * kdb v4.3-2.6.4-rc2-common-1. ++ ++2004-02-29 Keith Owens ++ ++ * kdb v4.3-2.6.4-rc1-common-1. ++ ++2004-02-21 Keith Owens ++ ++ * Correct build of kdb_cmds when using a separate object directory and ++ make it quiet. j-nomura (NEC), Keith Owens. ++ * kdb v4.3-2.6.3-common-2. ++ ++2004-02-18 Keith Owens ++ ++ * kdb v4.3-2.6.3-common-1. ++ ++2004-02-17 Keith Owens ++ ++ * Remove WAR for incorrect console registration patch. ++ * kdb v4.3-2.6.3-rc4-common-1. ++ ++2004-02-17 Keith Owens ++ ++ * Convert longjmp buffers from static to dynamic allocation, for large ++ cpu counts. ++ * Tweak kdbm_task for SMP/UP. ++ * Reconcile with kdb-v4.3 2.4.25-rc1-common-1. ++ * Simplify coexistence with NPTL patches. ++ * Support kill command on new scheduler. ++ * Do not refetch data when printing a value as characters. ++ * Document the pid command. ++ * Work around 2.6 kallsyms 'feature'. ++ * Upgrade to 2.6.3-rc3. ++ * WAR for incorrect console registration patch. ++ * kdb v4.3-2.6.3-rc3-common-1. ++ ++2003-12-03 Keith Owens ++ ++ * Reconcile 2.6-test versions from Xavier Bru (Bull), Greg Banks (SGI), ++ Jim Houston (Concurrent Computer Corp). ++ * Reconcile with kdb v4.3-2.4.23-common-2. ++ * Clean up CONFIG_KDB changes to {scripts,kernel}/kallsyms.c. ++ * Correct handling of kdb command line arguments. ++ * Make hooks into module code less intrusive. ++ * Delete kdb_active_task, not required with O(1) scheduler. ++ * Port kdbm_task.c from 2.4. ++ * Disable debug check in exit.c::next_thread() when kdb is running. ++ * Remove "only bh_disable when interrupts are set". BH must be disabled ++ in kdb to prevent deadlock on breakpoints in interrupt handlers. ++ * Add kdb to drivers/char/sn_serial.c. ++ * kdb v4.3-2.6.0-test11-common-1. ++ ++2003-11-11 Xavier Bru ++ * Merge to 2.6.0-test9 ++2003-10-17 Xavier Bru ++ * fix NUll ptr in kdb_ps at early prompt. ++2003-10-14 Xavier Bru ++ * fix NUll ptr in kdb_ps when cpu not present. ++2003-10-06 Xavier Bru ++ * Merge to 2.6.0-test5 ++ * fix compile error with CONFIG_MODULES not set. ++ ++2003-09-08 Xavier Bru ++ * Merge to 2.6.0-test4 ++ ++2003-07-10 Xavier Bru ++ ++ * Merge kdb v4.3 to 2.5.72 ia64 ++ * don't call local_bh_enable() with interrupts masked. ++ ++2003-04-07 Xavier Bru ++ ++ * Merge kdb v4.1 to 2.5.64 ia64 ++ * new kernel parameters support ++ * new module format ++ * new kallsyms support ++ ++2003-12-02 Keith Owens ++ ++ * Use correct page alignment in kdb_get_one_user_page(). ++ Prasanna S Panchamukhi, IBM. ++ * Split pte command into pte -m and pte -p. Dean Roe, SGI. ++ * kdb v4.3-2.4.23-common-2. ++ ++2003-12-01 Keith Owens ++ ++ * kdb v4.3-2.4.23-common-1. ++ ++2003-11-11 Keith Owens ++ ++ * Make KDB for USB keyboards build. Peter T. Breuer. ++ * Do not use USB keyboard if it has not been probed. ++ * kdb v4.3-2.4.23-rc1-common-1. ++ ++2003-10-10 Keith Owens ++ ++ * Sync with XFS 2.4.22 tree. ++ * kdb v4.3-2.4.22-common-2. ++ ++2003-08-29 Keith Owens ++ ++ * kdb v4.3-2.4.22-common-1. ++ ++2003-07-27 Keith Owens ++ ++ * kdb v4.3-2.4.22-pre8-common-8. ++ ++2003-07-20 Keith Owens ++ ++ * Make kdb_serial_str a common constant, the same for all consoles. ++ * Support SGI L1 console. ++ * kdb v4.3-2.4.21-common-8. ++ ++2003-07-14 Keith Owens ++ ++ * Correct ll command. ++ * kdb v4.3-2.4.21-common-7. ++ ++2003-07-08 Keith Owens ++ ++ * Export more kdb symbols. Vamsi Krishna S., IBM. ++ * kdb v4.3-2.4.21-common-6. ++ ++2003-07-07 Keith Owens ++ ++ * Tweak 'waiting for cpus' message. ++ * kdb v4.3-2.4.21-common-5. ++ ++2003-07-07 Keith Owens ++ ++ * 2.4.21-ia64-030702 patches common code that affects kdb. Workaround ++ this nuisance. ++ * kdb v4.3-2.4.21-common-4. ++ ++2003-06-24 Keith Owens ++ ++ * Add task and sigset commands. Mark Goodwin, SGI. ++ * kdb v4.3-2.4.21-common-3. ++ ++2003-06-23 Keith Owens ++ ++ * Sync with XFS 2.4.21 tree. ++ * kdb v4.3-2.4.21-common-2. ++ ++2003-06-20 Keith Owens ++ ++ * kdb v4.3-2.4.21-common-1. ++ ++2003-06-20 Keith Owens ++ ++ * More details on vm command, add vmp and pte commands. ++ Dean Nelson, Dean Roe, SGI. ++ * YAO1SCF (Yet Another O(1) Scheduler Coexistence Fix). ++ * Changes to common code to build on sparc. Tom Duffy. ++ * Move Tom Duffy's changes to drivers/sbus from the sparc64 ++ patch to the common patch to keep all the serial changes ++ together. ++ * Changes to common code to build on Xscale. Eddie Dong, Intel. ++ * Remove CROSS_COMPILE_INC. ++ * Remove obsolete boot parameter 'kdb', long since replaced by ++ 'kdb=on'. ++ * Remove obsolete kdb_eframe_t casts. ++ * Add CONFIG_KDB_CONTINUE_CATASTROPHIC. ++ * Wait a short interval for cpus to join kdb before proceeding. ++ * Automatically enable sysrq for sr command. ++ * Correct double free of kdb_printf lock, spotted by Richard Sanders. ++ * Add optional cpu parameter to btc command. ++ * kdb v4.3-2.4.20-common-1. ++ ++2003-05-02 Keith Owens ++ ++ * Some architectures have problems with the initial empty kallsyms ++ section so revert to three kallsyms passes. ++ * Flush buffered input at startup and at 'more' prompt. ++ * Only print 'more' prompt when longjmp data is available. ++ * Print more data for buffers and inodes. ++ * Disable kill command when O(1) scheduler is installed, the code ++ needs to be redone for O(1). ++ * The kernel has an undocumented assumption that enable_bh() is ++ always called with interrupts enabled, make it so. ++ * Print trailing punctuation even for symbols that are not in kernel. ++ * Add read/write access to user pages. Vamsi Krishna S., IBM ++ * Rename cpu_is_online to cpu_online, as in 2.5. ++ * O(1) scheduler removes init_task so kdb maintains its own list of ++ active tasks. ++ * Delete btp 0 option, it needed init_tasks. ++ * Clean up USB keyboard support. Steven Dake. ++ * Sync with XFS 2.4.20 tree. ++ * kdb v4.2-2.4.20-common-1. ++ ++2003-04-04 Keith Owens ++ ++ * Remove one kallsyms pass. ++ * Automatic detection of O(1) scheduler. ++ * Rename cpu_online to cpu_is_online. ++ * Workarounds for scheduler bugs. ++ * Tweak algorithm for detecting if cpu process data is available. ++ * Add 'kill' command. Sonic Zhang, Keith Owens. ++ * kdb v4.1-2.4.20-common-1. ++ ++2003-03-16 Keith Owens ++ ++ * Each cpu saves its state as it enters kdb or before it enters code ++ which cannot call kdb. ++ * Allow btp on process 0 for a specified cpu. ++ * Add btt command, backtrace given a struct task address. ++ * btc command no longer switches cpus, instead it uses the saved data. ++ * bta shows the idle task on each cpu as well as real tasks, the idle ++ task could be handling an interrupt. ++ * ps command shows the idle task on each cpu. ++ * ps checks that the saved data for a cpu matches the process running on ++ that cpu and warns about stale saved data or no saved data at all. ++ * Remove special cases for i386 backtrace from common code and simplify ++ common bt code. ++ * Clean up kdb interaction with CONFIG_SERIAL_CONSOLE. ++ * Do not automatically repeat commands after the user typed 'q'. ++ * O(1) scheduler patch changes the process cpu field but does not set ++ any indicator that O(1) is being used. Adjust kdb_process_cpu() by ++ hand after applying O(1). ++ * Add kdb_print_nameval() to common code. ++ * Convert tests of cpu_online_map to cpu_online() macro. ++ * module.h needs errno.h when compiling with CONFIG_MODULES=n. ++ * Correct duplicate breakpoint handling. ++ * Do not try to send IPI during a catastrophic error, send_ipi can hang ++ and take kdb with it. ++ * kdb memmap command is i386 only, restrict it. ++ * Add large block device (LBD) support from XFS tree. Eric Sandeen. ++ * kdb v4.0-2.4.20-common-1. ++ ++2003-02-03 Keith Owens ++ ++ * Register kdb commands early. ++ * Decode oops via kallsyms if it is available. ++ * Update copyright notices to 2003. ++ * Add defcmd/endefcmd to allow users to package their own macros. ++ * kdb commands that fail are ignored when prefixed with '-'. ++ * Add selection options to bta command. ++ * Add btc command (switch to each cpu and backtrace). ++ * Do real time detection of dead cpus. ++ * Clear ip adjusted flag when leaving kdb. ++ * Clean up ps command. ++ * Print ps output for each task when backtracing. ++ * Bump to version v3.0 to reduce confusion between kdb and kernel ++ version numbers. ++ * Add kdba_local_arch_setup/kdba_local_arch_cleanup to correct ++ keyboard freeze. Ashish Kalra. ++ * Refuse multiple breakpoints at the same address. ++ * Add fl (file_lock) command, from XFS development tree. ++ * Correct inode_pages, from XFS development tree. ++ * Add command history and editing. Sonic Zhang. ++ * Extend command history and editing to handle vt100 escape sequences. ++ * Allow tab completion at start of line. ++ * Touch nmi watchdog on long running bta and btc commands. ++ * Clean up ps output and standardize with bta codes. ++ * Correctly handle escaped characters in commands. ++ * Update man pages for btc and command history/editing. ++ * kdb v3.0-2.4.20-common-1. ++ ++2002-11-29 Keith Owens ++ ++ * Upgrade to 2.4.20. ++ * Correct Documentation/kdb/kdb_sr.man. ++ * Remove leading zeroes from pids, they are decimal, not octal. ++ * kdb v2.5-2.4.20-common-1. ++ ++2002-11-14 Keith Owens ++ ++ * Upgrade to 2.4.20-rc1. ++ * kdb v2.5-2.4.20-rc1-common-1. ++ ++2002-11-14 Keith Owens ++ ++ * Fix processing with O(1) scheduler. ++ * 'go' switches back to initial cpu first. ++ * 'go
' only allowed on initial cpu. ++ * 'go' installs the global breakpoints from the initial cpu before ++ releasing the other cpus. ++ * If 'go' has to single step over a breakpoint then it single steps just ++ the initial cpu, installs the global breakpoints then releases the ++ other cpus. ++ * General clean up of handling for breakpoints and single stepping over ++ software breakpoints. ++ * Add kdb_notifier_block so other code can tell when kdb is in control. ++ * kdb v2.5-2.4.19-common-1. ++ ++2002-11-02 Keith Owens ++ ++ * Correct build without CONFIG_KDB. ++ * kdb v2.4-2.4.19-common-3. ++ ++2002-11-01 Keith Owens ++ ++ * Minimize differences from 2.5.44. ++ * kdb v2.4-2.4.19-common-2. ++ ++2002-10-31 Keith Owens ++ ++ * Add defcmd/endefcmd feature. ++ * Remove kdb_eframe_t. ++ * Clear bp data before using. ++ * Sanity check if we have pt_regs. ++ * Force LINES > 1. ++ * Remove special case for KDB_REASON_PANIC, use KDB_ENTER() instead. ++ * Remove kdba_getcurrentframe(). ++ * Coexist with O(1) scheduler. ++ * Add lines option to dmesg, speed up dmesg. ++ * kdb v2.4-2.4.19-common-1. ++ ++2002-10-17 Keith Owens ++ ++ * Add selection critera to ps and bta commands. ++ * kdb v2.3-2.4.19-common-4. ++ ++2002-10-07 Keith Owens ++ ++ * New man page, Documentation/kdb/kdb_sr.man. ++ ++2002-10-04 Keith Owens ++ ++ * Minimize differences between patches for 2.4 and 2.5 kernels. ++ * Add Configure.help for CONFIG_KDB_USB. ++ * Reduce stack usage. ++ * kdb v2.3-2.4.19-common-3. ++ ++2002-08-10 Keith Owens ++ ++ * Replace kdb_port with kdb_serial to support memory mapped I/O. ++ David Mosberger. ++ * kdb v2.3-2.4.19-common-2. ++ ++2002-08-07 Keith Owens ++ ++ * Upgrade to 2.4.19. ++ * Remove individual SGI copyrights, the general SGI copyright applies. ++ * Handle md0. Reported by Hugh Dickins, different fix by Keith Owens. ++ * Use page_address() in kdbm_pg.c. Hugh Dickins. ++ * Remove debugging printk from kdbm_pg.c. Hugh Dickins. ++ * Move breakpoint address verification into arch dependent code. ++ * Dynamically resize kdb command table as required. ++ * Common code to support USB keyboard. Sebastien Lelarge. ++ * kdb v2.3-2.4.19-common-1. ++ ++2002-07-09 Keith Owens ++ ++ * Upgrade to 2.4.19-rc1. ++ * Add dmesg command. ++ * Clean up copyrights, Eric Sandeen. ++ * kdb v2.2-2.4.19-rc1-common-1. ++ ++2002-06-14 Keith Owens ++ ++ * Upgrade to 2.4.19-pre10. ++ * Sync with XFS. ++ * kdb v2.1-2.4.19-pre10-common-1. ++ ++2002-04-09 Keith Owens ++ ++ * Upgrade to 2.4.19-pre6. ++ * kdb v2.1-2.4.19-pre6-common-1. ++ ++2002-03-18 Keith Owens ++ ++ * Syntax check mdWcN commands. ++ ++2002-03-01 Keith Owens ++ ++ * Sync with XFS 2.4.18. ++ * kdb v2.1-2.4.18-common-2. ++ ++2002-02-26 Keith Owens ++ ++ * Upgrade to 2.4.18. ++ * Add Paul Dorwin (IBM) magicpoint slides on using kdb as ++ Documentation/kdb/slides. ++ * kdb v2.1-2.4.18-common-1. ++ ++2002-01-23 Keith Owens ++ ++ * Sync with XFS pagebuf changes. ++ * kdb v2.1-2.4.17-common-2. ++ ++2002-01-18 Keith Owens ++ ++ * Ignore single stepping during panic. ++ * Remove kdba_getword, kdba_putword. Replace with kdb_getword, ++ kdb_putword that rely on copy_xx_user. The new functions return ++ an error code, like copy_xx_user. ++ * New functions kdb_getarea, kdb_putarea for copying areas of data ++ such as structures. These functions also return an error code. ++ * Change all common code to use the new functions. ++ * bp command checks that it can read and write the word at the ++ breakpoint before accepting the address. ++ * Break points are now set FIFO and cleared LIFO so overlapping ++ entries give sensible results. ++ * Verify address before disassembling code. ++ * Common changes for sparc64. Ethan Solomita, Tom Duffy. ++ * Remove ss , never supported. ++ * Remove kallsyms entries from arch vmlinux.lds files. ++ * Specify which commands auto repeat. ++ * kdb v2.1-2.4.17-common-1. ++ ++2002-01-07 Keith Owens ++ ++ * Remove console semaphore code, not good in interrupt. ++ * Remove fragment of ia64 patch that had crept into kdb. ++ * Release as kdb v2.0-2.4.17-common-3. ++ ++2002-01-04 Keith Owens ++ ++ * Sync xfs <-> kdb common code. ++ ++2001-12-22 Keith Owens ++ ++ * Upgrade to 2.4.17. ++ * Clean up ifdef CONFIG_KDB. ++ * Add ifdef CONFIG_KDB around include kdb.h. ++ * Delete dummy kdb.h files for unsupported architectures. ++ * Delete arch i386 and ia64 specific files. This changelog now ++ applies to kdb common code only. ++ * Release as kdb v2.0-2.4.17-common-1. ++ ++2001-12-03 Keith Owens ++ ++ * Upgrade to 2.4.16. ++ * Add include/asm-um/kdb.h stub to allow XFS to be tested under UML. ++ * Check if an interrupt frame on i386 came from user space. ++ * Out of scope bug fix in kdb_id.c. Ethan Solomita. ++ * Changes to common code to support sparc64. Ethan Solomita. ++ * Change GFP_KERNEL to GFP_ATOMIC in disasm. Ethan Solomita. ++ ++2001-11-16 Keith Owens ++ ++ * Upgrade to 2.4.15-pre5. ++ * Wrap () around #define expressions with unary operators. ++ ++2001-11-13 Keith Owens ++ ++ * Upgrade to 2.4.15-pre4. ++ * kbdm_pg.c patch from Hugh Dickins. ++ ++2001-11-07 Keith Owens ++ ++ * Upgrade to 2.4.14-ia64-011105. ++ * Change name of l1 serial I/O routine, add ia64 init command. SGI. ++ * Sync kdbm_pg with XFS. ++ ++2001-11-06 Keith Owens ++ ++ * Upgrade to kernel 2.4.14. ++ ++2001-11-02 Keith Owens ++ ++ * Sync kdbm_pg.c with XFS. ++ ++2001-10-24 Keith Owens ++ ++ * Upgrade to kernel 2.4.13. ++ ++2001-10-14 Keith Owens ++ ++ * More use of TMPPREFIX in top level Makefile to speed up NFS compiles. ++ ++ * Correct repeat calculations in md/mds commands. ++ ++2001-10-10 Keith Owens ++ ++ * Copy bfd.h and ansidecl.h to arch/$(ARCH)/kdb, remove dependecies on ++ user space includes. ++ ++ * Update kdb v1.9 to kernel 2.4.11. ++ ++2001-10-01 Keith Owens ++ ++ * Update kdb v1.9 to kernel 2.4.11-pre1 and 2.4.10-ac1. ++ ++ * Correct loop in kdb_parse, reported by Tachino Nobuhiro. ++ ++2001-09-25 Keith Owens ++ ++ * Update kdb v1.8 to kernel 2.4.10. ++ ++ * kdbm_pg patch from Hugh Dickens. ++ ++ * DProbes patch from Bharata B Rao. ++ ++ * mdWcn and mmW patch from Vamsi Krishna S. ++ ++ * i386 disasm layout patch from Jean-Marc Saffroy. ++ ++ * Work around for 64 bit binutils, Simon Munton. ++ ++ * kdb.mm doc correction by Chris Pascoe. ++ ++ * Enter repeats the last command, IA64 disasm only prints one ++ instruction. Don Dugger. ++ ++ * Allow kdb/modules to be linked into vmlinux. ++ ++ * Remove obsolete code from kdb/modules/kdbm_{pg,vm}.c. ++ ++ * Warn when commands are entered at more prompt. ++ ++ * Add MODULE_AUTHOR, DESCRIPTION, LICENSE. ++ ++ * Release as kdb v1.9. ++ ++2001-02-27 Keith Owens ++ ++ * Update kdb v1.8 to kernel 2.4.2, sync kdb/modules with XFS. ++ ++ * Hook into panic() call. ++ ++2000-12-18 Keith Owens ++ ++ * Update kdb v1.7 to kernel 2.4.0-test13-pre3, sync kdb/modules with ++ XFS. ++ ++2000-11-18 Keith Owens ++ ++ * Update to kernel 2.4.0-test11-pre7, including forward port of ++ bug fixes from WIP 2.4.0-test9 tree. ++ ++ * Update to Cygnus CVS trees for disassembly code. ++ ++ * Bump to kdb v1.6. ++ ++2000-10-19 Keith Owens ++ ++ * Update to kernel 2.4.0-test10-pre4. ++ ++2000-10-15 Keith Owens ++ ++ * kdb/kdbmain.c (kdb_parse): Correctly handle blank input. ++ ++ * kdb/kdbmain.c (kdb_local, kdb): Reason SILENT can have NULL regs. ++ ++2000-10-13 Keith Owens ++ ++ * kdb/kdbmain.c: Reduce CMD_LEN to avoid overflowing kdb_printf buffer. ++ ++2000-10-11 Keith Owens ++ ++ * kdb/kdbmain.c (kdb): Test for userspace breakpoints before driving ++ other cpus into kdb. Speeds up gdb and avoids SMP race. ++ ++ * arch/i386/kdb/kdba_io.c (get_serial_char, get_kbd_char): Ignore ++ unprintable characters. ++ ++ * arch/i386/kdb/kdba_io.c (kdba_read): Better handling of buffer size. ++ ++2000-10-04 Keith Owens ++ ++ * arch/i386/kdb/kdba_bt.c (kdba_bt_process): Verify that esp is inside ++ task_struct. Original patch by Mike Galbraith. ++ ++ * kdb/kdb_io.c (kdb_getstr): Reset output line counter, remove ++ unnecessary prompts. ++ ++ * arch/i386/kdb/kdbasupport.c (kdb_getregcontents): Change " cs" to ++ "xcs", ditto ss, ds, es. gdb2kdb does not like leading spaces. ++ ++ * include/asm-xxx/kdb.h: Add dummy kdb.h for all architectures except ++ ix86. This allows #include to appear in arch independent ++ code without causing compile errors. ++ ++ * kdb/modules/kdbm_pg: Sync with XFS. ++ ++2000-10-03 Keith Owens ++ ++ * kdb/kdb_io.c (kdb_read): Ignore NMI while waiting for input. ++ ++ * kdb/kdb_io.c, kdb/Makefile: Export kdb_read. ++ ++2000-10-02 Keith Owens ++ ++ * arch/i386/kernel/smpboot.c (do_boot_cpu): Set nmi_watchdog_source to 2 ++ to avoid premature NMI oops during cpu bring up. We have to assume that ++ a box with more than 1 cpu has a working IO-APIC. ++ ++ * Documentation/kdb/{kdb.mm,kdb_md.man}: Add mdr command. ++ ++ * kdb/kdbmain.c (kdb_md): Add mdr command. ++ ++ * Release as kdb v1.5 against 2.4.0-test9-pre8. ++ ++ * arch/i386/kdb/kdba_io.c, arch/i386/kdb/kdbasupport.c, kdb/kdbmain.c, ++ kdb/kdb_io.c, kdb/kdb_id.c: Remove zero initializers for static ++ variables. ++ ++2000-09-28 Keith Owens ++ ++ * various: Add nmi_watchdog_source, 1 local APIC, 2 IO-APIC. ++ Test nmi_watchdog_source instead of nr_ioapics so UP works on SMP hardware. ++ ++ * arch/i386/kernel/io_apic.c: Rename setup_nmi to setup_nmi_io for clarity. ++ ++ * kdb/kdbmain.c (kdb_parse): Only set NO_WATCHDOG if it was already set. ++ ++ * kdb/kdbmain.c (kdb): Clear NO_WATCHDOG on all exit paths. ++ ++ * include/linux/kdb.h: Add KDB_REASON_SILENT. ++ ++ * kdb/kdbmain.c (kdb_local): Treat reason SILENT as immediate 'go'. ++ ++ * kdb/kdbmain.c (kdb_init): Invoke kdb with reason SILENT to instantiate ++ any breakpoints on boot cpu. ++ ++ * arch/i386/kernel/smpboot.c (smp_callin): Invoke kdb with reason SILENT ++ to instantiate any global breakpoints on this cpu. ++ ++ * kdb/kdb_cmds: Remove comment that said initial commands only worked on ++ boot cpu. ++ ++2000-09-27 Keith Owens ++ ++ * arch/i386/kernel/msr.c: Move {rd,wr}msr_eio to include/asm-i386/apic.h. ++ ++ * include/asm-i386/apic.h: Define NMI interfaces. ++ ++ * kernel/sysctl.c (kern_table): ++ * kernel/sysctl.c (do_proc_set_nmi_watchdog): ++ Add /proc/sys/kernel/nmi_watchdog. ++ ++ * arch/i386/kernel/apic.c: New routines set_nmi_counter_local, ++ setup_apic_nmi_watchdog. ++ ++ * arch/i386/kernel/traps.c: New routine set_nmi_watchdog(). Call apic ++ routines to set/clear local apic timer. ++ ++2000-09-26 Keith Owens ++ ++ * include/linux/sysctl.h (enum): Add NMI_WATCHDOG. ++ ++ * arch/i386/kernel/traps.c (nmi_watchdog_tick): Check nmi_watchdog is ++ still on. ++ ++ * arch/i386/config.in: Add CONFIG_UP_NMI_WATCHDOG. ++ ++ * Documentation/Configure.help: Add CONFIG_UP_NMI_WATCHDOG. ++ ++ * Documentation/nmi_watchdog.txt: Update for UP NMI watchdog. ++ ++2000-09-25 Keith Owens ++ ++ * arch/i386/kernel/apic.c (init_apic_mappings): ++ * arch/i386/kernel/io_apic.c (IO_APIC_init_uniprocessor): ++ Merge Keir Fraser's local APIC for uniprocessors patch. ++ ++2000-09-24 Keith Owens ++ ++ * Various: Declare initialization routines as __init. ++ ++ * Makefile: Define and export AWK. ++ ++ * kdb/Makefile: Generate gen-kdb_cmds.c from kdb/kdb_cmds. ++ ++ * kdb/kdbmain.c (kdb_init): Call new routine kdb_cmds_init to execute ++ whatever the user put in kdb/kdb_cmds. ++ ++ * arch/i386/kdb/kdba_bt.c (kdba_bt_stack): New parameter to ++ indicate if esp in regs is known to be valid or not. ++ ++ * kdb/kdb_bp.c, arch/i386/kdb/kdba_bp.c: More trace prints for ++ breakpoint handling. ++ ++ * arch/i386/kdb/kdba_bp.c (kdba_installbp): Finally found and fixed the ++ annoying breakpoint bug where breakpoints where not always installed ++ after 'go'. ++ ++ * Documentation/kdb: Update man pages kdb.mm, kdb_env.man, kdb_ss.man. ++ ++ * Released as kdb-v1.5-beta1-2.4.0-test8. ++ ++ * Sync to 2.4.0-test9-pre6 and release as kdb-v1.5-beta1-2.4.0-test9-pre6. ++ ++2000-09-23 Keith Owens ++ ++ * arch/i386/kdb/kdbasupport.c (kdba_getregcontents): New pseudo ++ registers cesp and ceflags to help with debugging the debugger. ++ ++ * kdb/kdbmain.c (kdb_local, kdb): Add KDB_REASON_RECURSE. Add ++ environment variable RECURSE. Add code to cope with some types of ++ recursion. ++ ++ * kdb/kdbmain.c (kdb), arch/i386/kdba/kdba_bp.c: Add ++ kdba_clearsinglestep. ++ ++2000-09-22 Keith Owens ++ ++ * drivers/video/vgacon.c (write_vga): No cli() if kdb is running, avoid ++ console deadlock. ++ ++ * arch/i386/kernel/irq.c (get_irqlock): Warn if kdb is running, may hang. ++ ++ * include/linux/kdb.h: Define KDB_IS_RUNNING as (0) if no CONFIG_KDB. ++ ++ * arch/i386/kdb/kdba_bt.c (kdba_bt_stack): Do not attempt a backtrace if ++ the code segment is not in the kernel. ++ ++ * kdb/modules: Change modules from MX_OBJS to M_OBJS. Remove EXPORT_NOSYMBOLS. ++ ++2000-09-21 Keith Owens ++ ++ * arch/i386/kernel/i386_ksyms.c: Move EXPORT_SYMBOLS for kdb to kdb/kdbmain.c. ++ ++ * kdb/Makefile: Change kdb/kdbmain.o from O_OBJS to OX_OBJS. ++ ++ * arch/i386/kernel/smp.c: Remove some #ifdef CONFIG_KDB. Remove kdbprivate.h. ++ ++ * include/linux/kdb.h: Add kdb_print_state. Add KDB_STATE_WAIT_IPI. ++ ++ * kdb/kdbmain.c (kdb): Only mark cpu as leaving if it is in KDB state. Maintain ++ WAIT_IPI state so a cpu is only driven through NMI once. ++ ++ * arch/i386/kernel/smp.c (smp_kdb_stop): All state fiddling moved to kdb(). ++ ++2000-09-20 Keith Owens ++ ++ * include/linux/kdb.h: #define kdb() as (0) if kdb is not configured. ++ ++ * arch/i386/kernel/traps.c: Remove some #ifdef CONFIG_KDB. ++ ++ * include/linux/kdbprivate.h: Move per cpu state to kdb.h. ++ ++ * include/linux/kdb.h: Add KDB_STATE_NO_WATCHDOG, KDB_STATE_PRINTF_LOCK. ++ Rename KDB_DEBUG_xxx to KDB_DEBUG_FLAG_xxx. Clean up debug flag ++ definitions. ++ ++ * arch/i386/kernel/traps.c (nmi_watchdog_tick): Check no watchdog. ++ ++ * kdb/kdbmain.c (kdb): Set no watchdog in normal kdb code. ++ ++ * kdb/kdbmain.c (kdb_parse): Allow watchdog in commands. ++ ++ * kdb/kdb_io.c (kdb_printf): No watchdog during printing. Clean up lock handling. ++ ++ * kdb/kdbmain.c (kdb_set): Clean up debug flag handling. ++ ++2000-09-19 Juan J. Quintela ++ ++ * kdb/arch/i386/kdb/kdba_io.c: Allow kdb to compile without CONFIG_VT and/or ++ serial console. ++ ++2000-09-19 Keith Owens ++ ++ * include/linux/kdb.h: Define KDB_DEBUG_STATE(). ++ ++ * kdb/kdbmain.c (kdb): Add kdb_print_state(), calls to KDB_DEBUG_STATE(). ++ ++2000-09-16 Keith Owens ++ ++ * Move to finer grained control over individual processors in kdb with ++ per cpu kdb state. Needed to allow ss[b] to only release one processor, ++ previously ss[b] released all processors. Also need to recover from ++ errors inside kdb commands, e.g. oops in kdbm_pg code. ++ ++ * various: ++ Move global flags KDB_FLAG_SSB, KDB_FLAG_SUPRESS, KDB_FLAG_FAULT, ++ KDB_FLAG_SS, KDB_FLAG_SSBPT, kdb_active, to per cpu state and macros ++ KDB_STATE(xxx). ++ Replace kdb_flags & KDB_FLAG_xxx with KDB_FLAG(xxx). ++ Replace kdb_flags & KDB_DEBUG_xxx with KDB_DEBUG(xxx). ++ Replace specific tests with wrapper KDB_IS_RUNNING(). ++ ++ * various: Remove #ifdef CONFIG_SMP from kdb code wherever ++ possible. Simplifies the code and makes it much more readable. ++ ++ * arch/i386/kdb/kdbasupport.c (kdb_setjmp): Record if we have reliable ++ longjmp data instead of assuming it is always set. ++ ++ * various: Replace smp_kdb_wait with per cpu state, HOLD_CPU. ++ ++ * init/main.c : Replace #ifdef KDB_DEBUG with KDB_DEBUG(CALLBACK). ++ ++ * include/linux/kdbprivate.h: Separate command return codes from error ++ codes. Add more detailed command codes. ++ ++ * arch/i386/kernel/traps.c (die): Change spin_lock_irq to ++ spin_lock_irqsave. Why did I do this? ++ ++ * kdb/kdbmain.c (kdb_parse): Set per cpu flag CMD before executing kdb ++ command. More detailed return codes for commands that affect ++ processors. ++ ++ * kdb/kdbmain.c (kdb_previous_event): New, check if any processors are ++ still executing the previous kdb event. Removes a race window where a ++ second event could enter kdb before the first had completely ended. ++ ++ * kdb/kdbmain.c (kdb): Document all the concurrency conditions and how ++ kdb handles them. ss[b] now releases only the current cpu. Do not set ++ breakpoints when releasing for ss[b]. Recover from errors in kdb ++ commands. Check that we have reliable longjmp data before using it. ++ ++ * various: Update return code documentation. ++ ++ * kdb/kdb_bp.c (kdb_ss): Separate ss and ssb return codes. ++ ++ * kdb/kdbsupport.c (kdb_ipi): Finer grained algorithm for deciding ++ whether to call send a stop signal to a cpu. ++ ++ * arch/i386/kdb/kdba_bp.c (kdba_db_trap): Separate ss and ssb return ++ codes. Reinstall delayed software breakpoints per cpu instead of ++ globally. Changed algorithm for handling ss[b]. ++ ++ * arch/i386/kdb/kdba_bp.c (kdba_bp_trap): Match software breakpoints per ++ cpu instead of globally. ++ ++ * include/linux/kdb.h: Bump version to kdb v1.5. ++ ++2000-09-16 Keith Owens ++ ++ * kernel/sysctl.c (kern_table): add /proc/sys/kernel/kdb. ++ ++ * init/main.c (parse_options): add boot flags kdb=on, kdb=off, ++ kdb=early. ++ ++ * include/linux/sysctl.h (enum): add KERN_KDB. ++ ++ * drivers/char/serial.c (receive_chars): check kdb_on. ++ ++ * drivers/char/keyboard.c (handle_scancode): check kdb_on. ++ ++ * arch/i386/kernel/traps.c (nmi_watchdog_tick): check kdb_on. ++ ++ * arch/i386/config.in: add CONFIG_KDB_OFF. ++ ++ * Documentation/Configure.help: add CONFIG_KDB_OFF. ++ ++ * kdb/kdbmain.c: add kdb_initial_cpu, kdb_on. ++ ++ * kdb/kdbmain.c (kdb): check kdb_on, set kdb_initial_cpu. ++ ++ * kdb/kdbmain.c (kdb_init): add Keith Owens to kdb banner. ++ ++ * kdb/kdb_io.c (kdb_printf): serialize kdb_printf output. ++ ++ * kdb/kdb_bt.c (kdb_bt): check environment variable BTAPROMPT. ++ ++ * kdb/kdbsupport.c (kdb_ipi): ignore NMI for kdb_initial_cpu. ++ ++ * kdb/modules/kdbm_pg.c (kdbm_page): merge updates from 2.4.0-test5-xfs. ++ ++ * kdb/kdb_bt.man: add btp, bta, BTAPROMPT. ++ ++ * kdb/kdb.mm: add CONFIG_KDB_OFF, boot flags, btp, bta. ++ ++ * include/linux/kdbprivate.h: add kdb_initial_cpu. ++ ++ * include/linux/kdb.h: add kdb_on, bump version to kdb v1.4. +--- /dev/null ++++ b/kdb/Makefile +@@ -0,0 +1,43 @@ ++# ++# This file is subject to the terms and conditions of the GNU General Public ++# License. See the file "COPYING" in the main directory of this archive ++# for more details. ++# ++# Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++# ++ ++CCVERSION := $(shell $(CC) -v 2>&1 | sed -ne '$$p') ++obj-y := kdb_bt.o kdb_bp.o kdb_id.o kdbsupport.o gen-kdb_cmds.o kdbmain.o kdb_io.o kdbdereference.o ++CFLAGS_kdbmain.o += -DCCVERSION="$(CCVERSION)" ++ ++subdir-$(CONFIG_KDB_MODULES) := modules ++obj-y += $(addsuffix /built-in.o, $(subdir-y)) ++ ++clean-files := gen-kdb_cmds.c ++ ++override CFLAGS := $(CFLAGS:%-pg=% ) ++ ++# define architecture dependent kdb_cmds ++ifeq ($(CONFIG_IA64),y) ++ KDB_CMDS = ia64/kdb/kdb_cmds ++else ++ ifeq ($(CONFIG_X86_64),y) ++ KDB_CMDS = x86/kdb/kdb_cmds_64 ++ else ++ ifeq ($(CONFIG_X86_32),y) ++ KDB_CMDS = x86/kdb/kdb_cmds_32 ++ endif ++ endif ++endif ++ ++quiet_cmd_gen-kdb = GENKDB $@ ++ cmd_gen-kdb = $(AWK) 'BEGIN {print "\#include "; print "\#include "} \ ++ /^\#/{next} \ ++ /^[ \t]*$$/{next} \ ++ {gsub(/"/, "\\\"", $$0); \ ++ print "static __initdata char kdb_cmd" cmds++ "[] = \"" $$0 "\\n\";"} \ ++ END {print "extern char *kdb_cmds[]; char __initdata *kdb_cmds[] = {"; for (i = 0; i < cmds; ++i) {print " kdb_cmd" i ","}; print(" NULL\n};");}' \ ++ $(filter-out %/Makefile,$^) > $@ ++ ++$(obj)/gen-kdb_cmds.c: $(src)/kdb_cmds $(wildcard $(TOPDIR)/arch/$(KDB_CMDS)) $(src)/Makefile ++ $(call cmd,gen-kdb) +--- /dev/null ++++ b/kdb/kdb_bp.c +@@ -0,0 +1,661 @@ ++/* ++ * Kernel Debugger Architecture Independent Breakpoint Handler ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Table of kdb_breakpoints ++ */ ++kdb_bp_t kdb_breakpoints[KDB_MAXBPT]; ++ ++/* ++ * Predicate to test whether a breakpoint should be installed ++ * on this CPU. ++ * ++ * Note that for purposes of installation, hardware breakpoints ++ * are treated as local (even if the global flag is set), on ++ * the assumption that the require per-cpu registers to be set. ++ */ ++ ++static inline int kdb_is_installable_global_bp(const kdb_bp_t *bp) ++{ ++ return (bp->bp_enabled && ++ bp->bp_global && ++ !bp->bp_forcehw); ++} ++ ++static int kdb_is_installable_local_bp(const kdb_bp_t *bp) ++{ ++ if (!bp->bp_enabled) ++ return 0; ++ ++ if (bp->bp_forcehw) { ++ if (bp->bp_cpu == smp_processor_id() || bp->bp_global) ++ return 1; ++ } else { ++ if (bp->bp_cpu == smp_processor_id() && !bp->bp_global) ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * kdb_bp_install_global ++ * ++ * Install global kdb_breakpoints prior to returning from the ++ * kernel debugger. This allows the kdb_breakpoints to be set ++ * upon functions that are used internally by kdb, such as ++ * printk(). ++ * ++ * Parameters: ++ * regs Execution frame. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * This function is only called once per kdb session. ++ */ ++ ++void ++kdb_bp_install_global(struct pt_regs *regs) ++{ ++ int i; ++ ++ for(i=0; ibp_enabled, bp->bp_global); ++ } ++ /* HW BP local or global are installed in kdb_bp_install_local*/ ++ if (kdb_is_installable_global_bp(bp)) ++ kdba_installbp(regs, bp); ++ } ++} ++ ++/* ++ * kdb_bp_install_local ++ * ++ * Install local kdb_breakpoints prior to returning from the ++ * kernel debugger. This allows the kdb_breakpoints to be set ++ * upon functions that are used internally by kdb, such as ++ * printk(). ++ * ++ * Parameters: ++ * regs Execution frame. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * This function is called once per processor. ++ */ ++ ++void ++kdb_bp_install_local(struct pt_regs *regs) ++{ ++ int i; ++ ++ for(i=0; ibp_enabled, bp->bp_global, ++ smp_processor_id(), bp->bp_cpu); ++ } ++ if (kdb_is_installable_local_bp(bp)) ++ kdba_installbp(regs, bp); ++ } ++} ++ ++/* ++ * kdb_bp_remove_global ++ * ++ * Remove global kdb_breakpoints upon entry to the kernel debugger. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++void ++kdb_bp_remove_global(void) ++{ ++ int i; ++ ++ for(i=KDB_MAXBPT-1; i>=0; i--) { ++ kdb_bp_t *bp = &kdb_breakpoints[i]; ++ ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdb_bp_remove_global bp %d bp_enabled %d bp_global %d\n", ++ i, bp->bp_enabled, bp->bp_global); ++ } ++ if (kdb_is_installable_global_bp(bp)) ++ kdba_removebp(bp); ++ } ++} ++ ++ ++/* ++ * kdb_bp_remove_local ++ * ++ * Remove local kdb_breakpoints upon entry to the kernel debugger. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++void ++kdb_bp_remove_local(void) ++{ ++ int i; ++ ++ for(i=KDB_MAXBPT-1; i>=0; i--) { ++ kdb_bp_t *bp = &kdb_breakpoints[i]; ++ ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdb_bp_remove_local bp %d bp_enabled %d bp_global %d cpu %d bp_cpu %d\n", ++ i, bp->bp_enabled, bp->bp_global, ++ smp_processor_id(), bp->bp_cpu); ++ } ++ if (kdb_is_installable_local_bp(bp)) ++ kdba_removebp(bp); ++ } ++} ++ ++/* ++ * kdb_printbp ++ * ++ * Internal function to format and print a breakpoint entry. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++static void ++kdb_printbp(kdb_bp_t *bp, int i) ++{ ++ if (bp->bp_forcehw) { ++ kdb_printf("Forced "); ++ } ++ ++ if (!bp->bp_template.bph_free) { ++ kdb_printf("%s ", kdba_bptype(&bp->bp_template)); ++ } else { ++ kdb_printf("Instruction(i) "); ++ } ++ ++ kdb_printf("BP #%d at ", i); ++ kdb_symbol_print(bp->bp_addr, NULL, KDB_SP_DEFAULT); ++ ++ if (bp->bp_enabled) { ++ kdba_printbp(bp); ++ if (bp->bp_global) ++ kdb_printf(" globally"); ++ else ++ kdb_printf(" on cpu %d", bp->bp_cpu); ++ if (bp->bp_adjust) ++ kdb_printf(" adjust %d", bp->bp_adjust); ++ } else { ++ kdb_printf("\n is disabled"); ++ } ++ ++ kdb_printf("\taddr at %016lx, hardtype=%d, forcehw=%d, installed=%d, hard=%p\n", ++ bp->bp_addr, bp->bp_hardtype, bp->bp_forcehw, ++ bp->bp_installed, bp->bp_hard); ++ ++ kdb_printf("\n"); ++} ++ ++/* ++ * kdb_bp ++ * ++ * Handle the bp, and bpa commands. ++ * ++ * [bp|bpa|bph] [DATAR|DATAW|IO [length]] ++ * ++ * Parameters: ++ * argc Count of arguments in argv ++ * argv Space delimited command line arguments ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic if failure. ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * bp Set breakpoint. Only use hardware assist if necessary. ++ * bpa Set breakpoint on all cpus, only use hardware regs if necessary ++ * bph Set breakpoint - force hardware register ++ * bpha Set breakpoint on all cpus, force hardware register ++ */ ++ ++static int ++kdb_bp(int argc, const char **argv) ++{ ++ int i, bpno; ++ kdb_bp_t *bp, *bp_check; ++ int diag; ++ int free; ++ char *symname = NULL; ++ long offset = 0ul; ++ int nextarg; ++ static kdb_bp_t kdb_bp_template; ++ ++ if (argc == 0) { ++ /* ++ * Display breakpoint table ++ */ ++ for(bpno=0,bp=kdb_breakpoints; bpnobp_free) continue; ++ ++ kdb_printbp(bp, bpno); ++ } ++ ++ return 0; ++ } ++ ++ memset(&kdb_bp_template, 0, sizeof(kdb_bp_template)); ++ ++ kdb_bp_template.bp_global = ((strcmp(argv[0], "bpa") == 0) ++ || (strcmp(argv[0], "bpha") == 0)); ++ kdb_bp_template.bp_forcehw = ((strcmp(argv[0], "bph") == 0) ++ || (strcmp(argv[0], "bpha") == 0)); ++ ++ /* Fix me: "bp" is treated as "bpa" to avoid system freeze. -jlan */ ++ if (strcmp(argv[0], "bp") == 0) ++ kdb_bp_template.bp_global = 1; ++ ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &kdb_bp_template.bp_addr, ++ &offset, &symname); ++ if (diag) ++ return diag; ++ if (!kdb_bp_template.bp_addr) ++ return KDB_BADINT; ++ ++ /* ++ * Find an empty bp structure, to allocate ++ */ ++ free = KDB_MAXBPT; ++ for(bpno=0,bp=kdb_breakpoints; bpnobp_free) { ++ break; ++ } ++ } ++ ++ if (bpno == KDB_MAXBPT) ++ return KDB_TOOMANYBPT; ++ ++ /* ++ * Handle architecture dependent parsing ++ */ ++ diag = kdba_parsebp(argc, argv, &nextarg, &kdb_bp_template); ++ if (diag) { ++ return diag; ++ } ++ ++ ++ /* ++ * Check for clashing breakpoints. ++ * ++ * Note, in this design we can't have hardware breakpoints ++ * enabled for both read and write on the same address, even ++ * though ia64 allows this. ++ */ ++ for(i=0,bp_check=kdb_breakpoints; ibp_free && ++ bp_check->bp_addr == kdb_bp_template.bp_addr && ++ (bp_check->bp_global || ++ bp_check->bp_cpu == kdb_bp_template.bp_cpu)) { ++ kdb_printf("You already have a breakpoint at " ++ kdb_bfd_vma_fmt0 "\n", kdb_bp_template.bp_addr); ++ return KDB_DUPBPT; ++ } ++ } ++ ++ kdb_bp_template.bp_enabled = 1; ++ ++ /* ++ * Actually allocate the breakpoint found earlier ++ */ ++ *bp = kdb_bp_template; ++ bp->bp_free = 0; ++ ++ if (!bp->bp_global) { ++ bp->bp_cpu = smp_processor_id(); ++ } ++ ++ /* ++ * Allocate a hardware breakpoint. If one is not available, ++ * disable the breakpoint, but leave it in the breakpoint ++ * table. When the breakpoint is re-enabled (via 'be'), we'll ++ * attempt to allocate a hardware register for it. ++ */ ++ if (!bp->bp_template.bph_free) { ++ kdba_alloc_hwbp(bp, &diag); ++ if (diag) { ++ bp->bp_enabled = 0; ++ bp->bp_hardtype = 0; ++ kdba_free_hwbp(bp); ++ return diag; ++ } ++ } ++ ++ kdb_printbp(bp, bpno); ++ ++ return 0; ++} ++ ++/* ++ * kdb_bc ++ * ++ * Handles the 'bc', 'be', and 'bd' commands ++ * ++ * [bd|bc|be] ++ * [bd|bc|be] * ++ * ++ * Parameters: ++ * argc Count of arguments in argv ++ * argv Space delimited command line arguments ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic for failure ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++#define KDBCMD_BC 0 ++#define KDBCMD_BE 1 ++#define KDBCMD_BD 2 ++ ++static int ++kdb_bc(int argc, const char **argv) ++{ ++ kdb_machreg_t addr; ++ kdb_bp_t *bp = NULL; ++ int lowbp = KDB_MAXBPT; ++ int highbp = 0; ++ int done = 0; ++ int i; ++ int diag; ++ int cmd; /* KDBCMD_B? */ ++ ++ if (strcmp(argv[0], "be") == 0) { ++ cmd = KDBCMD_BE; ++ } else if (strcmp(argv[0], "bd") == 0) { ++ cmd = KDBCMD_BD; ++ } else ++ cmd = KDBCMD_BC; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ if (strcmp(argv[1], "*") == 0) { ++ lowbp = 0; ++ highbp = KDB_MAXBPT; ++ } else { ++ diag = kdbgetularg(argv[1], &addr); ++ if (diag) ++ return diag; ++ ++ /* ++ * For addresses less than the maximum breakpoint number, ++ * assume that the breakpoint number is desired. ++ */ ++ if (addr < KDB_MAXBPT) { ++ bp = &kdb_breakpoints[addr]; ++ lowbp = highbp = addr; ++ highbp++; ++ } else { ++ for(i=0, bp=kdb_breakpoints; ibp_addr == addr) { ++ lowbp = highbp = i; ++ highbp++; ++ break; ++ } ++ } ++ } ++ } ++ ++ /* ++ * Now operate on the set of breakpoints matching the input ++ * criteria (either '*' for all, or an individual breakpoint). ++ */ ++ for(bp=&kdb_breakpoints[lowbp], i=lowbp; ++ i < highbp; ++ i++, bp++) { ++ if (bp->bp_free) ++ continue; ++ ++ done++; ++ ++ switch (cmd) { ++ case KDBCMD_BC: ++ if (bp->bp_hardtype) ++ kdba_free_hwbp(bp); ++ ++ bp->bp_enabled = 0; ++ bp->bp_global = 0; ++ ++ kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " cleared\n", ++ i, bp->bp_addr); ++ ++ bp->bp_addr = 0; ++ bp->bp_free = 1; ++ ++ break; ++ case KDBCMD_BE: ++ /* ++ * Allocate a hardware breakpoint. If one is not ++ * available, don't enable the breakpoint. ++ */ ++ if (!bp->bp_template.bph_free ++ && !bp->bp_hardtype) { ++ kdba_alloc_hwbp(bp, &diag); ++ if (diag) { ++ bp->bp_enabled = 0; ++ bp->bp_hardtype = 0; ++ kdba_free_hwbp(bp); ++ return diag; ++ } ++ } ++ ++ bp->bp_enabled = 1; ++ ++ kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " enabled", ++ i, bp->bp_addr); ++ ++ kdb_printf("\n"); ++ break; ++ case KDBCMD_BD: ++ if (!bp->bp_enabled) ++ break; ++ ++ /* ++ * Since this breakpoint is now disabled, we can ++ * give up the hardware register which is allocated ++ * to it. ++ */ ++ if (bp->bp_hardtype) ++ kdba_free_hwbp(bp); ++ ++ bp->bp_enabled = 0; ++ ++ kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " disabled\n", ++ i, bp->bp_addr); ++ ++ break; ++ } ++ if (bp->bp_delay && (cmd == KDBCMD_BC || cmd == KDBCMD_BD)) { ++ bp->bp_delay = 0; ++ KDB_STATE_CLEAR(SSBPT); ++ } ++ } ++ ++ return (!done)?KDB_BPTNOTFOUND:0; ++} ++ ++/* ++ * kdb_ss ++ * ++ * Process the 'ss' (Single Step) and 'ssb' (Single Step to Branch) ++ * commands. ++ * ++ * ss ++ * ssb ++ * ++ * Parameters: ++ * argc Argument count ++ * argv Argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * KDB_CMD_SS[B] for success, a kdb error if failure. ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * Set the arch specific option to trigger a debug trap after the next ++ * instruction. ++ * ++ * For 'ssb', set the trace flag in the debug trap handler ++ * after printing the current insn and return directly without ++ * invoking the kdb command processor, until a branch instruction ++ * is encountered. ++ */ ++ ++static int ++kdb_ss(int argc, const char **argv) ++{ ++ int ssb = 0; ++ struct pt_regs *regs = get_irq_regs(); ++ ++ ssb = (strcmp(argv[0], "ssb") == 0); ++ if (argc != 0) ++ return KDB_ARGCOUNT; ++ ++ if (!regs) { ++ kdb_printf("%s: pt_regs not available\n", __FUNCTION__); ++ return KDB_BADREG; ++ } ++ ++ /* ++ * Set trace flag and go. ++ */ ++ KDB_STATE_SET(DOING_SS); ++ if (ssb) ++ KDB_STATE_SET(DOING_SSB); ++ ++ kdba_setsinglestep(regs); /* Enable single step */ ++ ++ if (ssb) ++ return KDB_CMD_SSB; ++ return KDB_CMD_SS; ++} ++ ++/* ++ * kdb_initbptab ++ * ++ * Initialize the breakpoint table. Register breakpoint commands. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++void __init ++kdb_initbptab(void) ++{ ++ int i; ++ kdb_bp_t *bp; ++ ++ /* ++ * First time initialization. ++ */ ++ memset(&kdb_breakpoints, '\0', sizeof(kdb_breakpoints)); ++ ++ for (i=0, bp=kdb_breakpoints; ibp_free = 1; ++ /* ++ * The bph_free flag is architecturally required. It ++ * is set by architecture-dependent code to false (zero) ++ * in the event a hardware breakpoint register is required ++ * for this breakpoint. ++ * ++ * The rest of the template is reserved to the architecture ++ * dependent code and _must_ not be touched by the architecture ++ * independent code. ++ */ ++ bp->bp_template.bph_free = 1; ++ } ++ ++ kdb_register_repeat("bp", kdb_bp, "[]", "Set/Display breakpoints", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("bl", kdb_bp, "[]", "Display breakpoints", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("bpa", kdb_bp, "[]", "Set/Display global breakpoints", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("bph", kdb_bp, "[]", "Set hardware breakpoint", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("bpha", kdb_bp, "[]", "Set global hardware breakpoint", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("bc", kdb_bc, "", "Clear Breakpoint", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("be", kdb_bc, "", "Enable Breakpoint", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("bd", kdb_bc, "", "Disable Breakpoint", 0, KDB_REPEAT_NONE); ++ ++ kdb_register_repeat("ss", kdb_ss, "", "Single Step", 1, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("ssb", kdb_ss, "", "Single step to branch/call", 0, KDB_REPEAT_NO_ARGS); ++ /* ++ * Architecture dependent initialization. ++ */ ++ kdba_initbp(); ++} +--- /dev/null ++++ b/kdb/kdb_bt.c +@@ -0,0 +1,180 @@ ++/* ++ * Kernel Debugger Architecture Independent Stack Traceback ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * kdb_bt ++ * ++ * This function implements the 'bt' command. Print a stack ++ * traceback. ++ * ++ * bt [] (addr-exp is for alternate stacks) ++ * btp Kernel stack for ++ * btt Kernel stack for task structure at ++ * bta [DRSTCZEUIMA] All useful processes, optionally filtered by state ++ * btc [] The current process on one cpu, default is all cpus ++ * ++ * bt refers to a address on the stack, that location ++ * is assumed to contain a return address. ++ * ++ * btt refers to the address of a struct task. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * Backtrack works best when the code uses frame pointers. But even ++ * without frame pointers we should get a reasonable trace. ++ * ++ * mds comes in handy when examining the stack to do a manual traceback or ++ * to get a starting point for bt . ++ */ ++ ++static int ++kdb_bt1(const struct task_struct *p, unsigned long mask, int argcount, int btaprompt) ++{ ++ int diag; ++ char buffer[2]; ++ if (kdb_getarea(buffer[0], (unsigned long)p) || ++ kdb_getarea(buffer[0], (unsigned long)(p+1)-1)) ++ return KDB_BADADDR; ++ if (!kdb_task_state(p, mask)) ++ return 0; ++ kdb_printf("Stack traceback for pid %d\n", p->pid); ++ kdb_ps1(p); ++ diag = kdba_bt_process(p, argcount); ++ if (btaprompt) { ++ kdb_getstr(buffer, sizeof(buffer), "Enter to end, to continue:"); ++ if (buffer[0] == 'q') { ++ kdb_printf("\n"); ++ return 1; ++ } ++ } ++ touch_nmi_watchdog(); ++ return 0; ++} ++ ++int ++kdb_bt(int argc, const char **argv) ++{ ++ int diag; ++ int argcount = 5; ++ int btaprompt = 1; ++ int nextarg; ++ unsigned long addr; ++ long offset; ++ ++ kdbgetintenv("BTARGS", &argcount); /* Arguments to print */ ++ kdbgetintenv("BTAPROMPT", &btaprompt); /* Prompt after each proc in bta */ ++ ++ if (strcmp(argv[0], "bta") == 0) { ++ struct task_struct *g, *p; ++ unsigned long cpu; ++ unsigned long mask = kdb_task_state_string(argc ? argv[1] : NULL); ++ if (argc == 0) ++ kdb_ps_suppressed(); ++ /* Run the active tasks first */ ++ for (cpu = 0; cpu < NR_CPUS; ++cpu) { ++ if (!cpu_online(cpu)) ++ continue; ++ p = kdb_curr_task(cpu); ++ if (kdb_bt1(p, mask, argcount, btaprompt)) ++ return 0; ++ } ++ /* Now the inactive tasks */ ++ kdb_do_each_thread(g, p) { ++ if (task_curr(p)) ++ continue; ++ if (kdb_bt1(p, mask, argcount, btaprompt)) ++ return 0; ++ } kdb_while_each_thread(g, p); ++ } else if (strcmp(argv[0], "btp") == 0) { ++ struct task_struct *p; ++ unsigned long pid; ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ if ((diag = kdbgetularg((char *)argv[1], &pid))) ++ return diag; ++ if ((p = find_task_by_pid_ns(pid, &init_pid_ns))) { ++ kdba_set_current_task(p); ++ return kdb_bt1(p, ~0UL, argcount, 0); ++ } ++ kdb_printf("No process with pid == %ld found\n", pid); ++ return 0; ++ } else if (strcmp(argv[0], "btt") == 0) { ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ if ((diag = kdbgetularg((char *)argv[1], &addr))) ++ return diag; ++ kdba_set_current_task((struct task_struct *)addr); ++ return kdb_bt1((struct task_struct *)addr, ~0UL, argcount, 0); ++ } else if (strcmp(argv[0], "btc") == 0) { ++ unsigned long cpu = ~0; ++ struct kdb_running_process *krp; ++ const struct task_struct *save_current_task = kdb_current_task; ++ char buf[80]; ++ if (argc > 1) ++ return KDB_ARGCOUNT; ++ if (argc == 1 && (diag = kdbgetularg((char *)argv[1], &cpu))) ++ return diag; ++ /* Recursive use of kdb_parse, do not use argv after this point */ ++ argv = NULL; ++ if (cpu != ~0) { ++ krp = kdb_running_process + cpu; ++ if (cpu >= NR_CPUS || !krp->seqno || !cpu_online(cpu)) { ++ kdb_printf("no process for cpu %ld\n", cpu); ++ return 0; ++ } ++ sprintf(buf, "btt 0x%p\n", krp->p); ++ kdb_parse(buf); ++ return 0; ++ } ++ kdb_printf("btc: cpu status: "); ++ kdb_parse("cpu\n"); ++ for (cpu = 0, krp = kdb_running_process; cpu < NR_CPUS; ++cpu, ++krp) { ++ if (!cpu_online(cpu) || !krp->seqno) ++ continue; ++ sprintf(buf, "btt 0x%p\n", krp->p); ++ kdb_parse(buf); ++ touch_nmi_watchdog(); ++ } ++ kdba_set_current_task(save_current_task); ++ return 0; ++ } else { ++ if (argc) { ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, ++ &offset, NULL); ++ if (diag) ++ return diag; ++ return kdba_bt_address(addr, argcount); ++ } else { ++ return kdb_bt1(kdb_current_task, ~0UL, argcount, 0); ++ } ++ } ++ ++ /* NOTREACHED */ ++ return 0; ++} +--- /dev/null ++++ b/kdb/kdb_cmds +@@ -0,0 +1,33 @@ ++# Initial commands for kdb, alter to suit your needs. ++# These commands are executed in kdb_init() context, no SMP, no ++# processes. Commands that require process data (including stack or ++# registers) are not reliable this early. set and bp commands should ++# be safe. Global breakpoint commands affect each cpu as it is booted. ++ ++# Standard debugging information for first level support, just type archkdb ++# or archkdbcpu or archkdbshort at the kdb prompt. ++ ++defcmd archkdb "" "First line arch debugging" ++ set BTSYMARG 1 ++ set BTARGS 9 ++ pid R ++ -archkdbcommon ++ r ++ -bta ++endefcmd ++ ++defcmd archkdbcpu "" "archkdb with only tasks on cpus" ++ set BTSYMARG 1 ++ set BTARGS 9 ++ pid R ++ -archkdbcommon ++ -btc ++endefcmd ++ ++defcmd archkdbshort "" "archkdb with less detailed backtrace" ++ set BTSYMARG 0 ++ set BTARGS 0 ++ pid R ++ -archkdbcommon ++ -bta ++endefcmd +--- /dev/null ++++ b/kdb/kdb_id.c +@@ -0,0 +1,236 @@ ++/* ++ * Kernel Debugger Architecture Independent Instruction Disassembly ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++disassemble_info kdb_di; ++ ++/* ++ * kdb_id ++ * ++ * Handle the id (instruction display) command. ++ * ++ * id [] ++ * ++ * Parameters: ++ * argc Count of arguments in argv ++ * argv Space delimited command line arguments ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic if failure. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++int ++kdb_id(int argc, const char **argv) ++{ ++ kdb_machreg_t pc; ++ int icount; ++ int diag; ++ int i; ++ char *mode; ++ int nextarg; ++ long offset = 0; ++ static kdb_machreg_t lastpc; ++ struct disassemble_info *dip = &kdb_di; ++ char lastbuf[50]; ++ unsigned long word; ++ ++ kdb_di.fprintf_func = kdb_dis_fprintf; ++ kdba_id_init(&kdb_di); ++ ++ if (argc != 1) { ++ if (lastpc == 0) { ++ return KDB_ARGCOUNT; ++ } else { ++ sprintf(lastbuf, "0x%lx", lastpc); ++ argv[1] = lastbuf; ++ argc = 1; ++ } ++ } ++ ++ ++ /* ++ * Fetch PC. First, check to see if it is a symbol, if not, ++ * try address. ++ */ ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &pc, &offset, NULL); ++ if (diag) ++ return diag; ++ kdba_check_pc(&pc); ++ if (kdb_getarea(word, pc)) ++ return(0); ++ ++ /* ++ * Number of lines to display ++ */ ++ diag = kdbgetintenv("IDCOUNT", &icount); ++ if (diag) ++ return diag; ++ ++ mode = kdbgetenv("IDMODE"); ++ diag = kdba_id_parsemode(mode, dip); ++ if (diag) { ++ return diag; ++ } ++ ++ for(i=0; i ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static struct console *kdbcons; ++ ++#ifdef CONFIG_PPC64 ++#include ++#endif ++ ++#define CMD_BUFLEN 256 ++char kdb_prompt_str[CMD_BUFLEN]; ++ ++extern int kdb_grepping_flag; ++extern char kdb_grep_string[]; ++extern int kdb_grep_leading; ++extern int kdb_grep_trailing; ++ ++/* ++ * kdb_read ++ * ++ * This function reads a string of characters, terminated by ++ * a newline, or by reaching the end of the supplied buffer, ++ * from the current kernel debugger console device. ++ * Parameters: ++ * buffer - Address of character buffer to receive input characters. ++ * bufsize - size, in bytes, of the character buffer ++ * Returns: ++ * Returns a pointer to the buffer containing the received ++ * character string. This string will be terminated by a ++ * newline character. ++ * Locking: ++ * No locks are required to be held upon entry to this ++ * function. It is not reentrant - it relies on the fact ++ * that while kdb is running on any one processor all other ++ * processors will be spinning at the kdb barrier. ++ * Remarks: ++ * ++ * Davidm asks, why doesn't kdb use the console abstraction; ++ * here are some reasons: ++ * - you cannot debug the console abstraction with kdb if ++ * kdb uses it. ++ * - you rely on the correct functioning of the abstraction ++ * in the presence of general system failures. ++ * - You must acquire the console spinlock thus restricting ++ * the usability - what if the kernel fails with the spinlock ++ * held - one still wishes to debug such situations. ++ * - How about debugging before the console(s) are registered? ++ * - None of the current consoles (sercons, vt_console_driver) ++ * have read functions defined. ++ * - The standard pc keyboard and terminal drivers are interrupt ++ * driven. We cannot enable interrupts while kdb is active, ++ * so the standard input functions cannot be used by kdb. ++ * ++ * An implementation could be improved by removing the need for ++ * lock acquisition - just keep a 'struct console *kdbconsole;' global ++ * variable which refers to the preferred kdb console. ++ * ++ * The bulk of this function is architecture dependent. ++ * ++ * The buffer size must be >= 2. A buffer size of 2 means that the caller only ++ * wants a single key. ++ * ++ * An escape key could be the start of a vt100 control sequence such as \e[D ++ * (left arrow) or it could be a character in its own right. The standard ++ * method for detecting the difference is to wait for 2 seconds to see if there ++ * are any other characters. kdb is complicated by the lack of a timer service ++ * (interrupts are off), by multiple input sources and by the need to sometimes ++ * return after just one key. Escape sequence processing has to be done as ++ * states in the polling loop. ++ */ ++ ++char * ++kdb_read(char *buffer, size_t bufsize) ++{ ++ char *cp = buffer; ++ char *bufend = buffer+bufsize-2; /* Reserve space for newline and null byte */ ++ ++ char *lastchar; ++ char *p_tmp; ++ char tmp; ++ static char tmpbuffer[CMD_BUFLEN]; ++ int len = strlen(buffer); ++ int len_tmp; ++ int tab=0; ++ int count; ++ int i; ++ int diag, dtab_count; ++ ++#define ESCAPE_UDELAY 1000 ++#define ESCAPE_DELAY 2*1000000/ESCAPE_UDELAY /* 2 seconds worth of udelays */ ++ char escape_data[5]; /* longest vt100 escape sequence is 4 bytes */ ++ char *ped = escape_data; ++ int escape_delay = 0; ++ get_char_func *f, *f_escape = NULL; ++ ++ diag = kdbgetintenv("DTABCOUNT",&dtab_count); ++ if (diag) ++ dtab_count = 30; ++ ++ if (len > 0 ) { ++ cp += len; ++ if (*(buffer+len-1) == '\n') ++ cp--; ++ } ++ ++ lastchar = cp; ++ *cp = '\0'; ++ kdb_printf("%s", buffer); ++ ++ for (;;) { ++ int key; ++ for (f = &poll_funcs[0]; ; ++f) { ++ if (*f == NULL) { ++ /* Reset NMI watchdog once per poll loop */ ++ touch_nmi_watchdog(); ++ f = &poll_funcs[0]; ++ } ++ if (escape_delay == 2) { ++ *ped = '\0'; ++ ped = escape_data; ++ --escape_delay; ++ } ++ if (escape_delay == 1) { ++ key = *ped++; ++ if (!*ped) ++ --escape_delay; ++ break; ++ } ++ key = (*f)(); ++ if (key == -1) { ++ if (escape_delay) { ++ udelay(ESCAPE_UDELAY); ++ --escape_delay; ++ } ++ continue; ++ } ++ if (bufsize <= 2) { ++ if (key == '\r') ++ key = '\n'; ++ kdb_printf("%c", key); ++ *buffer++ = key; ++ *buffer = '\0'; ++ return buffer; ++ } ++ if (escape_delay == 0 && key == '\e') { ++ escape_delay = ESCAPE_DELAY; ++ ped = escape_data; ++ f_escape = f; ++ } ++ if (escape_delay) { ++ *ped++ = key; ++ if (f_escape != f) { ++ escape_delay = 2; ++ continue; ++ } ++ if (ped - escape_data == 1) { ++ /* \e */ ++ continue; ++ } ++ else if (ped - escape_data == 2) { ++ /* \e */ ++ if (key != '[') ++ escape_delay = 2; ++ continue; ++ } else if (ped - escape_data == 3) { ++ /* \e[ */ ++ int mapkey = 0; ++ switch (key) { ++ case 'A': mapkey = 16; break; /* \e[A, up arrow */ ++ case 'B': mapkey = 14; break; /* \e[B, down arrow */ ++ case 'C': mapkey = 6; break; /* \e[C, right arrow */ ++ case 'D': mapkey = 2; break; /* \e[D, left arrow */ ++ case '1': /* dropthrough */ ++ case '3': /* dropthrough */ ++ case '4': mapkey = -1; break; /* \e[<1,3,4>], may be home, del, end */ ++ } ++ if (mapkey != -1) { ++ if (mapkey > 0) { ++ escape_data[0] = mapkey; ++ escape_data[1] = '\0'; ++ } ++ escape_delay = 2; ++ } ++ continue; ++ } else if (ped - escape_data == 4) { ++ /* \e[<1,3,4> */ ++ int mapkey = 0; ++ if (key == '~') { ++ switch (escape_data[2]) { ++ case '1': mapkey = 1; break; /* \e[1~, home */ ++ case '3': mapkey = 4; break; /* \e[3~, del */ ++ case '4': mapkey = 5; break; /* \e[4~, end */ ++ } ++ } ++ if (mapkey > 0) { ++ escape_data[0] = mapkey; ++ escape_data[1] = '\0'; ++ } ++ escape_delay = 2; ++ continue; ++ } ++ } ++ break; /* A key to process */ ++ } ++ ++ if (key != 9) ++ tab = 0; ++ switch (key) { ++ case 8: /* backspace */ ++ if (cp > buffer) { ++ if (cp < lastchar) { ++ memcpy(tmpbuffer, cp, lastchar - cp); ++ memcpy(cp-1, tmpbuffer, lastchar - cp); ++ } ++ *(--lastchar) = '\0'; ++ --cp; ++ kdb_printf("\b%s \r", cp); ++ tmp = *cp; ++ *cp = '\0'; ++ kdb_printf(kdb_prompt_str); ++ kdb_printf("%s", buffer); ++ *cp = tmp; ++ } ++ break; ++ case 13: /* enter \r */ ++ case 10: /* enter \n */ ++ *lastchar++ = '\n'; ++ *lastchar++ = '\0'; ++ kdb_printf("\n"); ++ return buffer; ++ case 4: /* Del */ ++ if(cp < lastchar) { ++ memcpy(tmpbuffer, cp+1, lastchar - cp -1); ++ memcpy(cp, tmpbuffer, lastchar - cp -1); ++ *(--lastchar) = '\0'; ++ kdb_printf("%s \r", cp); ++ tmp = *cp; ++ *cp = '\0'; ++ kdb_printf(kdb_prompt_str); ++ kdb_printf("%s", buffer); ++ *cp = tmp; ++ } ++ break; ++ case 1: /* Home */ ++ if(cp > buffer) { ++ kdb_printf("\r"); ++ kdb_printf(kdb_prompt_str); ++ cp = buffer; ++ } ++ break; ++ case 5: /* End */ ++ if(cp < lastchar) { ++ kdb_printf("%s", cp); ++ cp = lastchar; ++ } ++ break; ++ case 2: /* Left */ ++ if (cp > buffer) { ++ kdb_printf("\b"); ++ --cp; ++ } ++ break; ++ case 14: /* Down */ ++ memset(tmpbuffer, ' ', strlen(kdb_prompt_str)+(lastchar-buffer)); ++ *(tmpbuffer+strlen(kdb_prompt_str)+(lastchar-buffer)) = '\0'; ++ kdb_printf("\r%s\r", tmpbuffer); ++ *lastchar = (char)key; ++ *(lastchar+1) = '\0'; ++ return lastchar; ++ case 6: /* Right */ ++ if (cp < lastchar) { ++ kdb_printf("%c", *cp); ++ ++cp; ++ } ++ break; ++ case 16: /* Up */ ++ memset(tmpbuffer, ' ', strlen(kdb_prompt_str)+(lastchar-buffer)); ++ *(tmpbuffer+strlen(kdb_prompt_str)+(lastchar-buffer)) = '\0'; ++ kdb_printf("\r%s\r", tmpbuffer); ++ *lastchar = (char)key; ++ *(lastchar+1) = '\0'; ++ return lastchar; ++ case 9: /* Tab */ ++ if (tab < 2) ++ ++tab; ++ p_tmp = buffer; ++ while(*p_tmp==' ') p_tmp++; ++ if (p_tmp<=cp) { ++ memcpy(tmpbuffer, p_tmp, cp-p_tmp); ++ *(tmpbuffer + (cp-p_tmp)) = '\0'; ++ p_tmp = strrchr(tmpbuffer, ' '); ++ if (p_tmp) ++ ++p_tmp; ++ else ++ p_tmp = tmpbuffer; ++ len = strlen(p_tmp); ++ count = kallsyms_symbol_complete(p_tmp, sizeof(tmpbuffer) - (p_tmp - tmpbuffer)); ++ if (tab == 2) { ++ if (count > 0) { ++ kdb_printf("\n%d symbols are found.", count); ++ if(count>dtab_count) { ++ count=dtab_count; ++ kdb_printf(" But only first %d symbols will be printed.\nYou can change the environment variable DTABCOUNT.", count); ++ } ++ kdb_printf("\n"); ++ for(i=0;i=dtab_count)kdb_printf("..."); ++ kdb_printf("\n"); ++ kdb_printf(kdb_prompt_str); ++ kdb_printf("%s", buffer); ++ } ++ } ++ else { ++ if (count > 0) { ++ len_tmp = strlen(p_tmp); ++ strncpy(p_tmp+len_tmp,cp, lastchar-cp+1); ++ len_tmp = strlen(p_tmp); ++ strncpy(cp, p_tmp+len, len_tmp-len+1); ++ len = len_tmp - len; ++ kdb_printf("%s", cp); ++ cp+=len; ++ lastchar+=len; ++ } ++ } ++ kdb_nextline = 1; /* reset output line number */ ++ } ++ break; ++ default: ++ if (key >= 32 &&lastchar < bufend) { ++ if (cp < lastchar) { ++ memcpy(tmpbuffer, cp, lastchar - cp); ++ memcpy(cp+1, tmpbuffer, lastchar - cp); ++ *++lastchar = '\0'; ++ *cp = key; ++ kdb_printf("%s\r", cp); ++ ++cp; ++ tmp = *cp; ++ *cp = '\0'; ++ kdb_printf(kdb_prompt_str); ++ kdb_printf("%s", buffer); ++ *cp = tmp; ++ } else { ++ *++lastchar = '\0'; ++ *cp++ = key; ++ kdb_printf("%c", key); ++ } ++ } ++ break; ++ } ++ } ++} ++ ++/* ++ * kdb_getstr ++ * ++ * Print the prompt string and read a command from the ++ * input device. ++ * ++ * Parameters: ++ * buffer Address of buffer to receive command ++ * bufsize Size of buffer in bytes ++ * prompt Pointer to string to use as prompt string ++ * Returns: ++ * Pointer to command buffer. ++ * Locking: ++ * None. ++ * Remarks: ++ * For SMP kernels, the processor number will be ++ * substituted for %d, %x or %o in the prompt. ++ */ ++ ++char * ++kdb_getstr(char *buffer, size_t bufsize, char *prompt) ++{ ++ if(prompt && kdb_prompt_str!=prompt) ++ strncpy(kdb_prompt_str, prompt, CMD_BUFLEN); ++ kdb_printf(kdb_prompt_str); ++ kdb_nextline = 1; /* Prompt and input resets line number */ ++ return kdb_read(buffer, bufsize); ++} ++ ++/* ++ * kdb_input_flush ++ * ++ * Get rid of any buffered console input. ++ * ++ * Parameters: ++ * none ++ * Returns: ++ * nothing ++ * Locking: ++ * none ++ * Remarks: ++ * Call this function whenever you want to flush input. If there is any ++ * outstanding input, it ignores all characters until there has been no ++ * data for approximately half a second. ++ */ ++ ++#define FLUSH_UDELAY 100 ++#define FLUSH_DELAY 500000/FLUSH_UDELAY /* 0.5 seconds worth of udelays */ ++ ++static void ++kdb_input_flush(void) ++{ ++ get_char_func *f; ++ int flush_delay = 1; ++ while (flush_delay--) { ++ touch_nmi_watchdog(); ++ for (f = &poll_funcs[0]; *f; ++f) { ++ if ((*f)() != -1) { ++ flush_delay = FLUSH_DELAY; ++ break; ++ } ++ } ++ if (flush_delay) ++ udelay(FLUSH_UDELAY); ++ } ++} ++ ++/* ++ * kdb_printf ++ * ++ * Print a string to the output device(s). ++ * ++ * Parameters: ++ * printf-like format and optional args. ++ * Returns: ++ * 0 ++ * Locking: ++ * None. ++ * Remarks: ++ * use 'kdbcons->write()' to avoid polluting 'log_buf' with ++ * kdb output. ++ * ++ * If the user is doing a cmd args | grep srch ++ * then kdb_grepping_flag is set. ++ * In that case we need to accumulate full lines (ending in \n) before ++ * searching for the pattern. ++ */ ++ ++static char kdb_buffer[256]; /* A bit too big to go on stack */ ++static char *next_avail=kdb_buffer; ++static int size_avail; ++static int suspend_grep=0; ++ ++/* ++ * search arg1 to see if it contains arg2 ++ * (kdmain.c provides flags for ^pat and pat$) ++ * ++ * return 1 for found, 0 for not found ++ */ ++int ++kdb_search_string(char *searched, char *searchfor) ++{ ++ char firstchar, *cp; ++ int len1, len2; ++ ++ /* not counting the newline at the end of "searched" */ ++ len1 = strlen(searched)-1; ++ len2 = strlen(searchfor); ++ if (len1 < len2) return 0; ++ if (kdb_grep_leading && kdb_grep_trailing && len1 != len2) return 0; ++ ++ if (kdb_grep_leading) { ++ if (!strncmp(searched, searchfor, len2)) { ++ return 1; ++ } ++ } else if (kdb_grep_trailing) { ++ if (!strncmp(searched+len1-len2, searchfor, len2)) { ++ return 1; ++ } ++ } else { ++ firstchar = *searchfor; ++ cp = searched; ++ while ((cp = strchr(cp,firstchar))) { ++ if (!strncmp(cp, searchfor, len2)) { ++ return 1; ++ } ++ cp++; ++ } ++ } ++ return 0; ++} ++ ++void ++kdb_printf(const char *fmt, ...) ++{ ++ va_list ap; ++ int diag; ++ int linecount; ++ int logging, saved_loglevel = 0; ++ int do_longjmp = 0; ++ int got_printf_lock = 0; ++ int fnd, len; ++ char *cp, *cp2, *cphold = NULL, replaced_byte = ' '; ++ char *moreprompt = "more> "; ++ struct console *c = console_drivers; ++ static DEFINE_SPINLOCK(kdb_printf_lock); ++ unsigned long uninitialized_var(flags); ++ ++ preempt_disable(); ++ /* Serialize kdb_printf if multiple cpus try to write at once. ++ * But if any cpu goes recursive in kdb, just print the output, ++ * even if it is interleaved with any other text. ++ */ ++ if (!KDB_STATE(PRINTF_LOCK)) { ++ KDB_STATE_SET(PRINTF_LOCK); ++ spin_lock_irqsave(&kdb_printf_lock, flags); ++ got_printf_lock = 1; ++ atomic_inc(&kdb_event); ++ } else { ++ __acquire(kdb_printf_lock); ++ } ++ atomic_inc(&kdb_8250); ++ ++ diag = kdbgetintenv("LINES", &linecount); ++ if (diag || linecount <= 1) ++ linecount = 22; ++ ++ diag = kdbgetintenv("LOGGING", &logging); ++ if (diag) ++ logging = 0; ++ ++ if (!kdb_grepping_flag || suspend_grep) { ++ /* normally, every vsnprintf starts a new buffer */ ++ next_avail = kdb_buffer; ++ size_avail = sizeof(kdb_buffer); ++ } ++ va_start(ap, fmt); ++ vsnprintf(next_avail, size_avail, fmt, ap); ++ va_end(ap); ++ ++ /* ++ * If kdb_parse() found that the command was cmd xxx | grep yyy ++ * then kdb_grepping_flag is set, and kdb_grep_string contains yyy ++ * ++ * Accumulate the print data up to a newline before searching it. ++ * (vsnprintf does null-terminate the string that it generates) ++ */ ++ ++ /* skip the search if prints are temporarily unconditional */ ++ if (! suspend_grep) { ++ ++ if (kdb_grepping_flag) { ++ cp = strchr(kdb_buffer, '\n'); ++ if (!cp) { ++ /* ++ * Special cases that don't end with newlines ++ * but should be written without one: ++ * The "[nn]kdb> " prompt should ++ * appear at the front of the buffer. ++ * ++ * The "[nn]more " prompt should also be ++ * (MOREPROMPT -> moreprompt) ++ * written * but we print that ourselves, ++ * we set the suspend_grep flag to make ++ * it unconditional. ++ * ++ */ ++ if (next_avail == kdb_buffer) { ++ /* ++ * these should occur after a newline, ++ * so they will be at the front of ++ * the buffer ++ */ ++ cp2 = kdb_buffer; ++ len = strlen(kdb_prompt_str); ++ if (!strncmp(cp2,kdb_prompt_str, len)) { ++ /* ++ * We're about to start a new ++ * command, so we can go back ++ * to normal mode. ++ */ ++ kdb_grepping_flag = 0; ++ goto kdb_printit; ++ } ++ } ++ /* no newline; don't search/write the buffer ++ until one is there */ ++ len = strlen(kdb_buffer); ++ next_avail = kdb_buffer + len; ++ size_avail = sizeof(kdb_buffer) - len; ++ goto kdb_print_out; ++ } ++ ++ /* ++ * The newline is present; print through it or discard ++ * it, depending on the results of the search. ++ */ ++ cp++; /* to byte after the newline */ ++ replaced_byte = *cp; /* remember what/where it was */ ++ cphold = cp; ++ *cp = '\0'; /* end the string for our search */ ++ ++ /* ++ * We now have a newline at the end of the string ++ * Only continue with this output if it contains the ++ * search string. ++ */ ++ fnd = kdb_search_string(kdb_buffer, kdb_grep_string); ++ if (!fnd) { ++ /* ++ * At this point the complete line at the start ++ * of kdb_buffer can be discarded, as it does ++ * not contain what the user is looking for. ++ * Shift the buffer left. ++ */ ++ *cphold = replaced_byte; ++ strcpy(kdb_buffer, cphold); ++ len = strlen(kdb_buffer); ++ next_avail = kdb_buffer + len; ++ size_avail = sizeof(kdb_buffer) - len; ++ goto kdb_print_out; ++ } ++ /* ++ * at this point the string is a full line and ++ * should be printed, up to the null. ++ */ ++ } ++ } ++kdb_printit: ++ ++ /* ++ * Write to all consoles. ++ */ ++#ifdef CONFIG_SPARC64 ++ if (c == NULL) ++ prom_printf("%s", kdb_buffer); ++ else ++#endif ++ ++#ifdef CONFIG_PPC64 ++ if (udbg_write) ++ udbg_write(kdb_buffer, strlen(kdb_buffer)); ++ else ++#endif ++ ++ while (c) { ++ c->write(c, kdb_buffer, strlen(kdb_buffer)); ++ touch_nmi_watchdog(); ++ c = c->next; ++ } ++ if (logging) { ++ saved_loglevel = console_loglevel; ++ console_loglevel = 0; ++ printk("%s", kdb_buffer); ++ } ++ ++ if (KDB_STATE(LONGJMP) && strchr(kdb_buffer, '\n')) ++ kdb_nextline++; ++ ++ /* check for having reached the LINES number of printed lines */ ++ if (kdb_nextline == linecount) { ++ char buf1[16]=""; ++#if defined(CONFIG_SMP) ++ char buf2[32]; ++#endif ++ ++ /* Watch out for recursion here. Any routine that calls ++ * kdb_printf will come back through here. And kdb_read ++ * uses kdb_printf to echo on serial consoles ... ++ */ ++ kdb_nextline = 1; /* In case of recursion */ ++ ++ /* ++ * Pause until cr. ++ */ ++ moreprompt = kdbgetenv("MOREPROMPT"); ++ if (moreprompt == NULL) { ++ moreprompt = "more> "; ++ } ++ ++#if defined(CONFIG_SMP) ++ if (strchr(moreprompt, '%')) { ++ sprintf(buf2, moreprompt, get_cpu()); ++ put_cpu(); ++ moreprompt = buf2; ++ } ++#endif ++ ++ kdb_input_flush(); ++ c = console_drivers; ++#ifdef CONFIG_SPARC64 ++ if (c == NULL) ++ prom_printf("%s", moreprompt); ++ else ++#endif ++ ++#ifdef CONFIG_PPC64 ++ if (udbg_write) ++ udbg_write(moreprompt, strlen(moreprompt)); ++ else ++#endif ++ ++ while (c) { ++ c->write(c, moreprompt, strlen(moreprompt)); ++ touch_nmi_watchdog(); ++ c = c->next; ++ } ++ ++ if (logging) ++ printk("%s", moreprompt); ++ ++ kdb_read(buf1, 2); /* '2' indicates to return immediately after getting one key. */ ++ kdb_nextline = 1; /* Really set output line 1 */ ++ ++ /* empty and reset the buffer: */ ++ kdb_buffer[0] = '\0'; ++ next_avail = kdb_buffer; ++ size_avail = sizeof(kdb_buffer); ++ if ((buf1[0] == 'q') || (buf1[0] == 'Q')) { ++ /* user hit q or Q */ ++ do_longjmp = 1; ++ KDB_FLAG_SET(CMD_INTERRUPT); /* command was interrupted */ ++ /* end of command output; back to normal mode */ ++ kdb_grepping_flag = 0; ++ kdb_printf("\n"); ++ } else if (buf1[0] && buf1[0] != '\n') { ++ /* user hit something other than enter */ ++ suspend_grep = 1; /* for this recursion */ ++ kdb_printf("\nOnly 'q' or 'Q' are processed at more prompt, input ignored\n"); ++ } else if (kdb_grepping_flag) { ++ /* user hit enter */ ++ suspend_grep = 1; /* for this recursion */ ++ kdb_printf("\n"); ++ } ++ kdb_input_flush(); ++ } ++ ++ /* ++ * For grep searches, shift the printed string left. ++ * replaced_byte contains the character that was overwritten with ++ * the terminating null, and cphold points to the null. ++ * Then adjust the notion of available space in the buffer. ++ */ ++ if (kdb_grepping_flag && !suspend_grep) { ++ *cphold = replaced_byte; ++ strcpy(kdb_buffer, cphold); ++ len = strlen(kdb_buffer); ++ next_avail = kdb_buffer + len; ++ size_avail = sizeof(kdb_buffer) - len; ++ } ++ ++kdb_print_out: ++ suspend_grep = 0; /* end of what may have been a recursive call */ ++ if (logging) { ++ console_loglevel = saved_loglevel; ++ } ++ atomic_dec(&kdb_8250); ++ if (KDB_STATE(PRINTF_LOCK) && got_printf_lock) { ++ got_printf_lock = 0; ++ spin_unlock_irqrestore(&kdb_printf_lock, flags); ++ KDB_STATE_CLEAR(PRINTF_LOCK); ++ atomic_dec(&kdb_event); ++ } else { ++ __release(kdb_printf_lock); ++ } ++ preempt_enable(); ++ if (do_longjmp) ++#ifdef kdba_setjmp ++ kdba_longjmp(&kdbjmpbuf[smp_processor_id()], 1) ++#endif /* kdba_setjmp */ ++ ; ++} ++ ++/* ++ * kdb_io_init ++ * ++ * Initialize kernel debugger output environment. ++ * ++ * Parameters: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * Select a console device. Only use a VT console if the user specified ++ * or defaulted console= /^tty[0-9]*$/ ++ */ ++ ++void __init ++kdb_io_init(void) ++{ ++ /* ++ * Select a console. ++ */ ++ struct console *c = console_drivers; ++ int vt_console = 0; ++ ++ while (c) { ++ if ((c->flags & CON_CONSDEV) && !kdbcons) ++ kdbcons = c; ++ if ((c->flags & CON_ENABLED) && ++ strncmp(c->name, "tty", 3) == 0) { ++ char *p = c->name + 3; ++ while (isdigit(*p)) ++ ++p; ++ if (*p == '\0') ++ vt_console = 1; ++ } ++ c = c->next; ++ } ++ ++ if (kdbcons == NULL) { ++ printk(KERN_ERR "kdb: Initialization failed - no console. kdb is disabled.\n"); ++ KDB_FLAG_SET(NO_CONSOLE); ++ kdb_on = 0; ++ } ++ if (!vt_console) ++ KDB_FLAG_SET(NO_VT_CONSOLE); ++ kdb_input_flush(); ++ return; ++} ++ ++#ifdef CONFIG_KDB_USB ++ ++int kdb_no_usb = 0; ++ ++static int __init opt_kdbnousb(char *str) ++{ ++ kdb_no_usb = 1; ++ return 0; ++} ++ ++early_param("kdbnousb", opt_kdbnousb); ++ ++#endif ++ ++EXPORT_SYMBOL(kdb_read); +--- /dev/null ++++ b/kdb/kdbdereference.c +@@ -0,0 +1,7258 @@ ++/* ++ * ++ * Most of this code is borrowed and adapted from the lkcd command "lcrash" ++ * and its supporting libarary. ++ * ++ * This kdb commands for casting memory structures. ++ * It provides ++ * "print" "px", "pd" * ++ * ++ * Careful of porting the klib KL_XXX functions (they call thru a jump table ++ * that we don't use here) ++ * ++ * The kernel type information is added be insmod'g the kdb debuginfo module ++ * It loads symbolic debugging info (provided from lcrash -o), ++ * (this information originally comes from the lcrash "kerntypes" file) ++ * ++ */ ++ ++#define VMALLOC_START_IA64 0xa000000200000000 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "modules/lcrash/klib.h" ++#include "modules/lcrash/kl_stringtab.h" ++#include "modules/lcrash/kl_btnode.h" ++#include "modules/lcrash/lc_eval.h" ++ ++#undef next_node /* collision with nodemask.h */ ++int have_debug_file = 0; ++dbg_sym_t *types_tree_head; ++dbg_sym_t *typedefs_tree_head; ++kltype_t *kltype_array; ++dbg_sym_t *dsym_types_array; ++ ++ ++EXPORT_SYMBOL(types_tree_head); ++EXPORT_SYMBOL(typedefs_tree_head); ++EXPORT_SYMBOL(kltype_array); ++EXPORT_SYMBOL(dsym_types_array); ++ ++#define C_HEX 0x0002 ++#define C_WHATIS 0x0004 ++#define C_NOVARS 0x0008 ++#define C_SIZEOF 0x0010 ++#define C_SHOWOFFSET 0x0020 ++#define C_LISTHEAD 0x0040 ++#define C_LISTHEAD_N 0x0080 /* walk using list_head.next */ ++#define C_LISTHEAD_P 0x0100 /* walk using list_head.prev */ ++#define C_BINARY 0x0200 ++#define MAX_LONG_LONG 0xffffffffffffffffULL ++klib_t kdb_klib; ++klib_t *KLP = &kdb_klib; ++k_error_t klib_error = 0; ++dbg_sym_t *type_tree = (dbg_sym_t *)NULL; ++dbg_sym_t *typedef_tree = (dbg_sym_t *)NULL; ++dbg_sym_t *func_tree = (dbg_sym_t *)NULL; ++dbg_sym_t *srcfile_tree = (dbg_sym_t *)NULL; ++dbg_sym_t *var_tree = (dbg_sym_t *)NULL; ++dbg_sym_t *xtype_tree = (dbg_sym_t *)NULL; ++dbg_hashrec_t *dbg_hash[TYPE_NUM_SLOTS]; ++int all_count, deall_count; ++void single_type(char *str); ++void sizeof_type(char *str); ++typedef struct chunk_s { ++ struct chunk_s *next; /* Must be first */ ++ struct chunk_s *prev; /* Must be second */ ++ void *addr; ++ struct bucket_s *bucketp; ++ uint32_t chunksz; /* size of memory chunk (via malloc()) */ ++ uint32_t blksz; /* Not including header */ ++ short blkcount; /* Number of blksz blocks in chunk */ ++} chunk_t; ++ ++typedef struct blkhdr_s { ++ struct blkhdr_s *next; ++ union { ++ struct blkhdr_s *prev; ++ chunk_t *chunkp; ++ } b_un; ++ int flg; ++ int size; ++} blkhdr_t; ++ ++int ptrsz64 = ((int)sizeof(void *) == 8); ++alloc_functions_t alloc_functions; ++ ++/* ++ * return 1 if addr is invalid ++ */ ++static int ++invalid_address(kaddr_t addr, int count) ++{ ++ unsigned char c; ++ unsigned long lcount; ++ /* FIXME: untested? */ ++ lcount = count; ++ /* FIXME: use kdb_verify_area */ ++ while (count--) { ++ if (kdb_getarea(c, addr)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * wrappers for calls to kernel-style allocation/deallocation ++ */ ++static void * ++kl_alloc_block(int size) ++{ ++ void *vp; ++ ++ vp = kmalloc(size, GFP_KERNEL); ++ if (!vp) { ++ kdb_printf ("kmalloc of %d bytes failed\n", size); ++ } ++ /* important: the lcrash code sometimes assumes that the ++ * allocation is zeroed out ++ */ ++ memset(vp, 0, size); ++ all_count++; ++ return vp; ++} ++static void ++kl_free_block(void *vp) ++{ ++ kfree(vp); ++ deall_count++; ++ return; ++} ++ ++int ++get_value(char *s, uint64_t *value) ++{ ++ return kl_get_value(s, NULL, 0, value); ++} ++ ++/* ++ * kl_get_block() ++ * ++ * Read a size block from virtual address addr in the system memory image. ++ */ ++k_error_t ++kl_get_block(kaddr_t addr, unsigned size, void *bp, void *mmap) ++{ ++ if (!bp) { ++ return(KLE_NULL_BUFF); ++ } else if (!size) { ++ return(KLE_ZERO_SIZE); ++ } ++ ++ memcpy(bp, (void *)addr, size); ++ ++ return(0); ++} ++ ++/* ++ * print_value() ++ */ ++void ++print_value(char *ldstr, uint64_t value, int width) ++{ ++ int w = 0; ++ char fmtstr[12], f, s[2]="\000\000"; ++ ++ if (ldstr) { ++ kdb_printf("%s", ldstr); ++ } ++ s[0] = '#'; ++ f = 'x'; ++ if (width) { ++ if (ptrsz64) { ++ w = 18; /* due to leading "0x" */ ++ } else { ++ w = 10; /* due to leading "0x" */ ++ } ++ } ++ if (w) { ++ sprintf(fmtstr, "%%%s%d"FMT64"%c", s, w, f); ++ } else { ++ sprintf(fmtstr, "%%%s"FMT64"%c", s, f); ++ } ++ kdb_printf(fmtstr, value); ++} ++ ++/* ++ * print_list_head() ++ */ ++void ++print_list_head(kaddr_t saddr) ++{ ++ print_value("STRUCT ADDR: ", (uint64_t)saddr, 8); ++ kdb_printf("\n"); ++} ++ ++/* ++ * check_prev_ptr() ++ */ ++void ++check_prev_ptr(kaddr_t ptr, kaddr_t prev) ++{ ++ if(ptr != prev) { ++ kdb_printf("\nWARNING: Pointer broken. %#"FMTPTR"x," ++ " SHOULD BE: %#"FMTPTR"x\n", prev, ptr); ++ } ++} ++ ++/* ++ * kl_kaddr() -- Return a kernel virtual address stored in a structure ++ * ++ * Pointer 'p' points to a kernel structure ++ * of type 's.' Get the kernel address located in member 'm.' ++ */ ++kaddr_t ++kl_kaddr(void *p, char *s, char *m) ++{ ++ uint64_t *u64p; ++ int offset; ++ ++ offset = kl_member_offset(s, m); ++ u64p = (uint64_t *)(p + offset); ++ return((kaddr_t)*u64p); ++} ++ ++/* ++ * walk_structs() -- walk linked lists of kernel data structures ++ */ ++int ++walk_structs(char *s, char *f, char *member, kaddr_t addr, int flags) ++{ ++ int size, offset, mem_offset=0; ++ kaddr_t last = 0, next; ++ kltype_t *klt = (kltype_t *)NULL, *memklt=(kltype_t *)NULL; ++ unsigned long long iter_threshold = 10000; ++ ++ int counter = 0; ++ kaddr_t head=0, head_next=0, head_prev=0, entry=0; ++ kaddr_t entry_next=0, entry_prev; ++ ++ /* field name of link pointer, determine its offset in the struct. */ ++ if ((offset = kl_member_offset(s, f)) == -1) { ++ kdb_printf("Could not determine offset for member %s of %s.\n", ++ f, s); ++ return 0; ++ } ++ ++ /* Get the type of the enclosing structure */ ++ if (!(klt = kl_find_type(s, (KLT_STRUCT|KLT_UNION)))) { ++ kdb_printf("Could not find the type of %s\n", s); ++ return(1); ++ } ++ ++ /* Get the struct size */ ++ if ((size = kl_struct_len(s)) == 0) { ++ kdb_printf ("could not get the length of %s\n", s); ++ return(1); ++ } ++ ++ /* test for a named member of the structure that should be displayed */ ++ if (member) { ++ memklt = kl_get_member(klt, member); ++ if (!memklt) { ++ kdb_printf ("%s has no member %s\n", s, member); ++ return 1; ++ } ++ mem_offset = kl_get_member_offset(klt, member); ++ } ++ ++ if ((next = addr)) { ++ /* get head of list (anchor) when struct list_head is used */ ++ if (flags & C_LISTHEAD) { ++ head = next; ++ if (invalid_address(head, sizeof(head))) { ++ kdb_printf ("invalid address %#lx\n", ++ head); ++ return 1; ++ } ++ /* get contents of addr struct member */ ++ head_next = kl_kaddr((void *)head, "list_head", "next"); ++ if (invalid_address(head, sizeof(head_next))) { ++ kdb_printf ("invalid address %#lx\n", ++ head_next); ++ return 1; ++ } ++ /* get prev field of anchor */ ++ head_prev = kl_kaddr((void *)head, "list_head", "prev"); ++ if (invalid_address(head, sizeof(head_prev))) { ++ kdb_printf ("invalid address %#lx\n", ++ head_prev); ++ return 1; ++ } ++ entry = 0; ++ } ++ } ++ ++ while(next && counter < iter_threshold) { ++ counter++; ++ if (counter > iter_threshold) { ++ kdb_printf("\nWARNING: Iteration threshold reached.\n"); ++ kdb_printf("Current threshold: %lld\n", iter_threshold); ++ break; ++ } ++ if(flags & C_LISTHEAD) { ++ if(!(entry)){ ++ if(flags & C_LISTHEAD_N){ ++ entry = head_next; ++ } else { ++ entry = head_prev; ++ } ++ last = head; ++ } ++ ++ if(head == entry) { ++ if(flags & C_LISTHEAD_N){ ++ check_prev_ptr(last, head_prev); ++ } else { ++ check_prev_ptr(last, head_next); ++ } ++ break; ++ } ++ ++ next = entry - offset; /* next structure */ ++ /* check that the whole structure can be addressed */ ++ if (invalid_address(next, size)) { ++ kdb_printf( ++ "invalid struct address %#lx\n", next); ++ return 1; ++ } ++ /* and validate that it points to valid addresses */ ++ entry_next = kl_kaddr((void *)entry,"list_head","next"); ++ if (invalid_address(entry_next, sizeof(entry_next))) { ++ kdb_printf("invalid address %#lx\n", ++ entry_next); ++ return 1; ++ } ++ entry_prev = kl_kaddr((void *)entry,"list_head","prev"); ++ if (invalid_address(entry_prev, sizeof(entry_prev))) { ++ kdb_printf("invalid address %#lx\n", ++ entry_prev); ++ return 1; ++ } ++ if(flags & C_LISTHEAD_N){ ++ check_prev_ptr(last, entry_prev); ++ } else { ++ check_prev_ptr(last, entry_next); ++ } ++ print_list_head(next); ++ last = entry; ++ if(flags & C_LISTHEAD_N){ ++ entry = entry_next; /* next list_head */ ++ } else { ++ entry = entry_prev; /* next list_head */ ++ } ++ } ++ ++ if (memklt) { ++ /* print named sub-structure in C-like struct format. */ ++ kl_print_member( ++ (void *)((unsigned long)next+mem_offset), ++ memklt, 0, C_HEX); ++ } else { ++ /* print entire structure in C-like struct format. */ ++ kl_print_type((void *)next, klt, 0, C_HEX); ++ } ++ ++ if(!(flags & C_LISTHEAD)) { ++ last = next; ++ next = (kaddr_t) (*(uint64_t*)(next + offset)); ++ } ++ } ++ ++ return(0); ++} ++ ++/* ++ * Implement the lcrash walk -s command ++ * see lcrash cmd_walk.c ++ */ ++int ++kdb_walk(int argc, const char **argv) ++{ ++ int i, nonoptc=0, optc=0, flags=0, init_len=0; ++ char *cmd, *arg, *structp=NULL, *forwp=NULL, *memberp=NULL; ++ char *addrp=NULL; ++ uint64_t value; ++ kaddr_t start_addr; ++ ++ all_count=0; ++ deall_count=0; ++ if (!have_debug_file) { ++ kdb_printf("no debuginfo file\n"); ++ return 0; ++ } ++ /* If there is nothing to evaluate, just return */ ++ if (argc == 0) { ++ return 0; ++ } ++ cmd = (char *)*argv; /* s/b "walk" */ ++ if (strcmp(cmd,"walk")) { ++ kdb_printf("got %s, not \"walk\"\n", cmd); ++ return 0; ++ } ++ ++ for (i=1; i<=argc; i++) { ++ arg = (char *)*(argv+i); ++ if (*arg == '-') { ++ optc++; ++ if (optc > 2) { ++ kdb_printf("too many options\n"); ++ kdb_printf("see 'walkhelp'\n"); ++ return 0; ++ } ++ if (*(arg+1) == 's') { ++ continue; /* ignore -s */ ++ } else if (*(arg+1) == 'h') { ++ if ((init_len=kl_struct_len("list_head")) ++ == 0) { ++ kdb_printf( ++ "could not find list_head\n"); ++ return 0; ++ } ++ if (*(arg+2) == 'p') { ++ flags = C_LISTHEAD; ++ flags |= C_LISTHEAD_P; ++ } else if (*(arg+2) == 'n') { ++ flags = C_LISTHEAD; ++ flags |= C_LISTHEAD_N; ++ } else { ++ kdb_printf("invalid -h option <%s>\n", ++ arg); ++ kdb_printf("see 'walkhelp'\n"); ++ return 0; ++ } ++ } else { ++ kdb_printf("invalid option <%s>\n", arg); ++ kdb_printf("see 'walkhelp'\n"); ++ return 0; ++ } ++ } else { ++ nonoptc++; ++ if (nonoptc > 4) { ++ kdb_printf("too many arguments\n"); ++ kdb_printf("see 'walkhelp'\n"); ++ return 0; ++ } ++ if (nonoptc == 1) { ++ structp = arg; ++ } else if (nonoptc == 2) { ++ forwp = arg; ++ } else if (nonoptc == 3) { ++ addrp = arg; ++ } else if (nonoptc == 4) { ++ /* the member is optional; if we get ++ a fourth, the previous was the member */ ++ memberp = addrp; ++ addrp = arg; ++ } else { ++ kdb_printf("invalid argument <%s>\n", arg); ++ kdb_printf("see 'walkhelp'\n"); ++ return 0; ++ } ++ } ++ } ++ if (nonoptc < 3) { ++ kdb_printf("too few arguments\n"); ++ kdb_printf("see 'walkhelp'\n"); ++ return 0; ++ } ++ if (!(flags & C_LISTHEAD)) { ++ if ((init_len=kl_struct_len(structp)) == 0) { ++ kdb_printf("could not find %s\n", structp); ++ return 0; ++ } ++ } ++ ++ /* Get the start address of the structure */ ++ if (get_value(addrp, &value)) { ++ kdb_printf ("address %s invalid\n", addrp); ++ return 0; ++ } ++ start_addr = (kaddr_t)value; ++ if (invalid_address(start_addr, init_len)) { ++ kdb_printf ("address %#lx invalid\n", start_addr); ++ return 0; ++ } ++ ++ if (memberp) { ++ } ++ ++ if (walk_structs(structp, forwp, memberp, start_addr, flags)) { ++ kdb_printf ("walk_structs failed\n"); ++ return 0; ++ } ++ /* kdb_printf("ptc allocated:%d deallocated:%d\n", ++ all_count, deall_count); */ ++ return 0; ++} ++ ++/* ++ * Implement the lcrash px (print, pd) command ++ * see lcrash cmd_print.c ++ * ++ * px ++ * e.g. px *(task_struct *)
++ */ ++int ++kdb_debuginfo_print(int argc, const char **argv) ++{ ++ /* argc does not count the command itself, which is argv[0] */ ++ char *cmd, *next, *end, *exp, *cp; ++ unsigned char *buf; ++ int i, j, iflags; ++ node_t *np; ++ uint64_t flags = 0; ++ ++ /* If there is nothing to evaluate, just return */ ++ if (argc == 0) { ++ return 0; ++ } ++ all_count=0; ++ deall_count=0; ++ ++ cmd = (char *)*argv; ++ ++ /* Set up the flags value. If this command was invoked via ++ * "pd" or "px", then make sure the appropriate flag is set. ++ */ ++ flags = 0; ++ if (!strcmp(cmd, "pd") || !strcmp(cmd, "print")) { ++ flags = 0; ++ } else if (!strcmp(cmd, "px")) { ++ flags |= C_HEX; ++ } else if (!strcmp(cmd, "whatis")) { ++ if (argc != 1) { ++ kdb_printf("usage: whatis \n"); ++ return 0; ++ } ++ cp = (char *)*(argv+1); ++ single_type(cp); ++ /* kdb_printf("allocated:%d deallocated:%d\n", ++ all_count, deall_count); */ ++ return 0; ++ } else if (!strcmp(cmd, "sizeof")) { ++ if (!have_debug_file) { ++ kdb_printf("no debuginfo file\n"); ++ return 0; ++ } ++ if (argc != 1) { ++ kdb_printf("usage: sizeof type\n"); ++ return 0; ++ } ++ cp = (char *)*(argv+1); ++ sizeof_type(cp); ++ return 0; ++ } else { ++ kdb_printf("command error: %s\n", cmd); ++ return 0; ++ } ++ ++ /* ++ * Count the number of bytes necessary to hold the entire expression ++ * string. ++ */ ++ for (i=1, j=0; i <= argc; i++) { ++ j += (strlen(*(argv+i)) + 1); ++ } ++ ++ /* ++ * Allocate space for the expression string and copy the individual ++ * arguments into it. ++ */ ++ buf = kl_alloc_block(j); ++ if (!buf) { ++ return 0; ++ } ++ ++ for (i=1; i <= argc; i++) { ++ strcat(buf, *(argv+i)); ++ /* put spaces between arguments */ ++ if (i < argc) { ++ strcat(buf, " "); ++ } ++ } ++ ++ /* Walk through the expression string, expression by expression. ++ * Note that a comma (',') is the delimiting character between ++ * expressions. ++ */ ++ next = buf; ++ while (next) { ++ if ((end = strchr(next, ','))) { ++ *end = (char)0; ++ } ++ ++ /* Copy the next expression to a separate expression string. ++ * A separate expresison string is necessary because it is ++ * likely to get freed up in eval() when variables get expanded. ++ */ ++ i = strlen(next)+1; ++ exp = (char *)kl_alloc_block(i); ++ if (!exp) { ++ return 0; ++ } ++ strcpy(exp, next); ++ ++ /* Evaluate the expression */ ++ np = eval(&exp, 0); ++ if (!np || eval_error) { ++ print_eval_error(cmd, exp, ++ (error_token ? error_token : (char*)NULL), ++ eval_error, CMD_NAME_FLG); ++ if (np) { ++ free_nodes(np); ++ } ++ kl_free_block(buf); ++ kl_free_block(exp); ++ free_eval_memory(); ++ return 0; ++ } ++ iflags = flags; ++ if (print_eval_results(np, iflags)) { ++ free_nodes(np); ++ kl_free_block(buf); ++ free_eval_memory(); ++ return 0; ++ } ++ kl_free_block(exp); ++ ++ if (end) { ++ next = end + 1; ++ kdb_printf(" "); ++ } else { ++ next = (char*)NULL; ++ kdb_printf("\n"); ++ } ++ free_nodes(np); ++ } ++ free_eval_memory(); ++ kl_free_block(buf); ++ /* kdb_printf("allocated:%d deallocated:%d\n", ++ all_count, deall_count); */ ++ return 0; ++} ++ ++/* ++ * Display help for the px command ++ */ ++int ++kdb_pxhelp(int argc, const char **argv) ++{ ++ if (have_debug_file) { ++ kdb_printf ("Some examples of using the px command:\n"); ++ kdb_printf (" the whole structure:\n"); ++ kdb_printf (" px *(task_struct *)0xe0000...\n"); ++ kdb_printf (" one member:\n"); ++ kdb_printf (" px (*(task_struct *)0xe0000...)->comm\n"); ++ kdb_printf (" the address of a member\n"); ++ kdb_printf (" px &((task_struct *)0xe0000...)->children\n"); ++ kdb_printf (" a structure pointed to by a member:\n"); ++ kdb_printf (" px ((*(class_device *)0xe0000...)->class)->name\n"); ++ kdb_printf (" array element:\n"); ++ kdb_printf (" px (cache_sizes *)0xa0000...[0]\n"); ++ kdb_printf (" px (task_struct *)(0xe0000...)->cpus_allowed.bits[0]\n"); ++ } else { ++ kdb_printf ("There is no debug info file.\n"); ++ kdb_printf ("The px/pd/print commands can only evaluate "); ++ kdb_printf ("arithmetic expressions.\n"); ++ } ++ return 0; ++} ++ ++/* ++ * Display help for the walk command ++ */ ++int ++kdb_walkhelp(int argc, const char **argv) ++{ ++ if (!have_debug_file) { ++ kdb_printf("no debuginfo file\n"); ++ return 0; ++ } ++ kdb_printf ("Using the walk command:\n"); ++ kdb_printf (" (only the -s (symbolic) form is supported, so -s is ignored)\n"); ++ kdb_printf ("\n"); ++ kdb_printf (" If the list is not linked with list_head structures:\n"); ++ kdb_printf (" walk [-s] struct name-of-forward-pointer address\n"); ++ kdb_printf (" example: walk xyz_struct next 0xe00....\n"); ++ kdb_printf ("\n"); ++ kdb_printf (" If the list is linked with list_head structures, use -hn\n"); ++ kdb_printf (" to walk the 'next' list, -hp for the 'prev' list\n"); ++ kdb_printf (" walk -h[n|p] struct name-of-forward-pointer [member-to-show] address-of-list-head\n"); ++ kdb_printf (" example, to show the entire task_struct:\n"); ++ kdb_printf (" walk -hn task_struct tasks 0xe000....\n"); ++ kdb_printf (" example, to show the task_struct member comm:\n"); ++ kdb_printf (" walk -hn task_struct tasks comm 0xe000....\n"); ++ kdb_printf (" (address is not the address of first member's list_head, "); ++ kdb_printf ("but of the anchoring list_head\n"); ++ return 0; ++} ++ ++/* ++ * dup_block() ++ */ ++void * ++dup_block(void *b, int len) ++{ ++ void *b2; ++ ++ if ((b2 = kl_alloc_block(len))) { ++ memcpy(b2, b, len); /* dst, src, sz */ ++ } ++ return(b2); ++} ++ ++/* ++ * kl_reset_error() ++ */ ++void ++kl_reset_error(void) ++{ ++ klib_error = 0; ++} ++ ++/* ++ * given a symbol name, look up its address ++ * ++ * in lcrash, this would return a pointer to the syment_t in ++ * a binary tree of them ++ * ++ * In this one, look up the symbol in the standard kdb way, ++ * which fills in the kdb_symtab_t. ++ * Then fill in the global syment_t "lkup_syment" -- assuming ++ * we'll only need one at a time! ++ * ++ * kl_lkup_symname returns the address of syment_t if the symbol is ++ * found, else null. ++ * ++ * Note: we allocate a syment_t the caller should kfree it ++ */ ++syment_t * ++kl_lkup_symname (char *cp) ++{ ++ syment_t *sp; ++ kdb_symtab_t kdb_symtab; ++ ++ if (kdbgetsymval(cp, &kdb_symtab)) { ++ sp = (syment_t *)kl_alloc_block(sizeof(syment_t)); ++ sp->s_addr = (kaddr_t)kdb_symtab.sym_start; ++ KL_ERROR = 0; ++ return (sp); ++ } else { ++ /* returns 0 if the symbol is not found */ ++ KL_ERROR = KLE_INVALID_VALUE; ++ return ((syment_t *)0); ++ } ++} ++ ++/* ++ * kl_get_ra() ++ * ++ * This function returns its own return address. ++ * Usefule when trying to capture where we came from. ++ */ ++void* ++kl_get_ra(void) ++{ ++ return (__builtin_return_address(0)); ++} ++ ++/* start kl_util.c */ ++/* ++ * Definitions for the do_math() routine. ++ */ ++#define M_ADD '+' ++#define M_SUBTRACT '-' ++#define M_MULTIPLY '*' ++#define M_DIVIDE '/' ++ ++/* ++ * do_math() -- Calculate some math values based on a string argument ++ * passed into the function. For example, if you use: ++ * ++ * 0xffffc000*2+6/5-3*19-8 ++ * ++ * And you will get the value 0xffff7fc0 back. I could ++ * probably optimize this a bit more, but right now, it ++ * works, which is good enough for me. ++ */ ++static uint64_t ++do_math(char *str) ++{ ++ int i = 0; ++ char *buf, *loc; ++ uint64_t value1, value2; ++ syment_t *sp; ++ ++ buf = (char *)kl_alloc_block((strlen(str) + 1)); ++ sprintf(buf, "%s", str); ++ for (i = strlen(str); i >= 0; i--) { ++ if ((str[i] == M_ADD) || (str[i] == M_SUBTRACT)) { ++ buf[i] = '\0'; ++ value1 = do_math(buf); ++ value2 = do_math(&str[i+1]); ++ kl_free_block((void *)buf); ++ if (str[i] == M_SUBTRACT) { ++ return value1 - value2; ++ } else { ++ return value1 + value2; ++ } ++ } ++ } ++ ++ for (i = strlen(str); i >= 0; i--) { ++ if ((str[i] == M_MULTIPLY) || (str[i] == M_DIVIDE)) { ++ buf[i] = '\0'; ++ value1 = do_math(buf); ++ value2 = do_math(&str[i+1]); ++ kl_free_block((void *)buf); ++ if (str[i] == M_MULTIPLY) { ++ return (value1 * value2); ++ } else { ++ if (value2 == 0) { ++ /* handle divide by zero */ ++ /* XXX -- set proper error code */ ++ klib_error = 1; ++ return (0); ++ } else { ++ return (value1 / value2); ++ } ++ } ++ } ++ } ++ ++ /* ++ * Otherwise, just process the value, and return it. ++ */ ++ sp = kl_lkup_symname(buf); ++ if (KL_ERROR) { ++ KL_ERROR = 0; ++ value2 = kl_strtoull(buf, &loc, 10); ++ if (((!value2) && (buf[0] != '0')) || (*loc) || ++ (!strncmp(buf, "0x", 2)) || (!strncmp(buf, "0X", 2))) { ++ value1 = (kaddr_t)kl_strtoull(buf, (char**)NULL, 16); ++ } else { ++ value1 = (unsigned)kl_strtoull(buf, (char**)NULL, 10); ++ } ++ } else { ++ value1 = (kaddr_t)sp->s_addr; ++ kl_free_block((void *)sp); ++ } ++ kl_free_block((void *)buf); ++ return (value1); ++} ++/* ++ * kl_get_value() -- Translate numeric input strings ++ * ++ * A generic routine for translating an input string (param) in a ++ * number of dfferent ways. If the input string is an equation ++ * (contains the characters '+', '-', '/', and '*'), then perform ++ * the math evaluation and return one of the following modes (if ++ * mode is passed): ++ * ++ * 0 -- if the resulting value is <= elements, if elements (number ++ * of elements in a table) is passed. ++ * ++ * 1 -- if the first character in param is a pound sign ('#'). ++ * ++ * 3 -- the numeric result of an equation. ++ * ++ * If the input string is NOT an equation, mode (if passed) will be ++ * set in one of the following ways (depending on the contents of ++ * param and elements). ++ * ++ * o When the first character of param is a pound sign ('#'), mode ++ * is set equal to one and the trailing numeric value (assumed to ++ * be decimal) is returned. ++ * ++ * o When the first two characters in param are "0x" or "0X," or ++ * when when param contains one of the characers "abcdef," or when ++ * the length of the input value is eight characters. mode is set ++ * equal to two and the numeric value contained in param is ++ * translated as hexadecimal and returned. ++ * ++ * o The value contained in param is translated as decimal and mode ++ * is set equal to zero. The resulting value is then tested to see ++ * if it exceeds elements (if passed). If it does, then value is ++ * translated as hexadecimal and mode is set equal to two. ++ * ++ * Note that mode is only set when a pointer is passed in the mode ++ * paramater. Also note that when elements is set equal to zero, any ++ * non-hex (as determined above) value not starting with a pound sign ++ * will be translated as hexadecimal (mode will be set equal to two) -- ++ * IF the length of the string of characters is less than 16 (kaddr_t). ++ * ++ */ ++int ++kl_get_value(char *param, int *mode, int elements, uint64_t *value) ++{ ++ char *loc; ++ uint64_t v; ++ ++ kl_reset_error(); ++ ++ /* Check to see if we are going to need to do any math ++ */ ++ if (strpbrk(param, "+-/*")) { ++ if (!strncmp(param, "#", 1)) { ++ v = do_math(¶m[1]); ++ if (mode) { ++ *mode = 1; ++ } ++ } else { ++ v = do_math(param); ++ if (mode) { ++ if (elements && (*value <= elements)) { ++ *mode = 0; ++ } else { ++ *mode = 3; ++ } ++ } ++ } ++ } else { ++ if (!strncmp(param, "#", 1)) { ++ if (!strncmp(param, "0x", 2) ++ || !strncmp(param, "0X", 2) ++ || strpbrk(param, "abcdef")) { ++ v = kl_strtoull(¶m[1], &loc, 16); ++ } else { ++ v = kl_strtoull(¶m[1], &loc, 10); ++ } ++ if (loc) { ++ KL_ERROR = KLE_INVALID_VALUE; ++ return (1); ++ } ++ if (mode) { ++ *mode = 1; ++ } ++ } else if (!strncmp(param, "0x", 2) || !strncmp(param, "0X", 2) ++ || strpbrk(param, "abcdef")) { ++ v = kl_strtoull(param, &loc, 16); ++ if (loc) { ++ KL_ERROR = KLE_INVALID_VALUE; ++ return (1); ++ } ++ if (mode) { ++ *mode = 2; /* HEX VALUE */ ++ } ++ } else if (elements || (strlen(param) < 16) || ++ (strlen(param) > 16)) { ++ v = kl_strtoull(param, &loc, 10); ++ if (loc) { ++ KL_ERROR = KLE_INVALID_VALUE; ++ return (1); ++ } ++ if (elements && (v >= elements)) { ++ v = (kaddr_t)kl_strtoull(param, ++ (char**)NULL, 16); ++ if (mode) { ++ *mode = 2; /* HEX VALUE */ ++ } ++ } else if (mode) { ++ *mode = 0; ++ } ++ } else { ++ v = kl_strtoull(param, &loc, 16); ++ if (loc) { ++ KL_ERROR = KLE_INVALID_VALUE; ++ return (1); ++ } ++ if (mode) { ++ *mode = 2; /* ASSUME HEX VALUE */ ++ } ++ } ++ } ++ *value = v; ++ return (0); ++} ++/* end kl_util.c */ ++ ++/* start kl_libutil.c */ ++static int ++valid_digit(char c, int base) ++{ ++ switch(base) { ++ case 2: ++ if ((c >= '0') && (c <= '1')) { ++ return(1); ++ } else { ++ return(0); ++ } ++ case 8: ++ if ((c >= '0') && (c <= '7')) { ++ return(1); ++ } else { ++ return(0); ++ } ++ case 10: ++ if ((c >= '0') && (c <= '9')) { ++ return(1); ++ } else { ++ return(0); ++ } ++ case 16: ++ if (((c >= '0') && (c <= '9')) ++ || ((c >= 'a') && (c <= 'f')) ++ || ((c >= 'A') && (c <= 'F'))) { ++ return(1); ++ } else { ++ return(0); ++ } ++ } ++ return(0); ++} ++ ++static int ++digit_value(char c, int base, int *val) ++{ ++ if (!valid_digit(c, base)) { ++ return(1); ++ } ++ switch (base) { ++ case 2: ++ case 8: ++ case 10: ++ *val = (int)((int)(c - 48)); ++ break; ++ case 16: ++ if ((c >= 'a') && (c <= 'f')) { ++ *val = ((int)(c - 87)); ++ } else if ((c >= 'A') && (c <= 'F')) { ++ *val = ((int)(c - 55)); ++ } else { ++ *val = ((int)(c - 48)); ++ } ++ } ++ return(0); ++} ++ ++uint64_t ++kl_strtoull(char *str, char **loc, int base) ++{ ++ int dval; ++ uint64_t i = 1, v, value = 0; ++ char *c, *cp = str; ++ ++ *loc = (char *)NULL; ++ if (base == 0) { ++ if (!strncmp(cp, "0x", 2) || !strncmp(cp, "0X", 2)) { ++ base = 16; ++ } else if (cp[0] == '0') { ++ if (cp[1] == 'b') { ++ base = 2; ++ } else { ++ base = 8; ++ } ++ } else if (strpbrk(cp, "abcdefABCDEF")) { ++ base = 16; ++ } else { ++ base = 10; ++ } ++ } ++ if ((base == 8) && (*cp == '0')) { ++ cp += 1; ++ } else if ((base == 2) && !strncmp(cp, "0b", 2)) { ++ cp += 2; ++ } else if ((base == 16) && ++ (!strncmp(cp, "0x", 2) || !strncmp(cp, "0X", 2))) { ++ cp += 2; ++ } ++ c = &cp[strlen(cp) - 1]; ++ while (c >= cp) { ++ ++ if (digit_value(*c, base, &dval)) { ++ if (loc) { ++ *loc = c; ++ } ++ return(value); ++ } ++ v = dval * i; ++ if ((MAX_LONG_LONG - value) < v) { ++ return(MAX_LONG_LONG); ++ } ++ value += v; ++ i *= (uint64_t)base; ++ c--; ++ } ++ return(value); ++} ++/* end kl_libutil.c */ ++ ++/* ++ * dbg_hash_sym() ++ */ ++void ++dbg_hash_sym(uint64_t typenum, dbg_sym_t *stp) ++{ ++ dbg_hashrec_t *shp, *hshp; ++ ++ if ((typenum == 0) || (!stp)) { ++ return; ++ } ++ shp = (dbg_hashrec_t *)kl_alloc_block(sizeof(dbg_hashrec_t)); ++ shp->h_typenum = typenum; ++ shp->h_ptr = stp; ++ shp->h_next = (dbg_hashrec_t *)NULL; ++ if ((hshp = dbg_hash[TYPE_NUM_HASH(typenum)])) { ++ while (hshp->h_next) { ++ hshp = hshp->h_next; ++ } ++ hshp->h_next = shp; ++ } else { ++ dbg_hash[TYPE_NUM_HASH(typenum)] = shp; ++ } ++} ++ ++/* ++ * dbg_find_sym() ++ */ ++dbg_sym_t * ++dbg_find_sym(char *name, int type, uint64_t typenum) ++{ ++ dbg_sym_t *stp = (dbg_sym_t *)NULL; ++ ++ if (name && strlen(name)) { ++ /* Cycle through the type flags and see if any records are ++ * present. Note that if multiple type flags or DBG_ALL is ++ * passed in, only the first occurance of 'name' will be ++ * found and returned. If name exists in multiple trees, ++ * then multiple searches are necessary to find them. ++ */ ++ if (type & DBG_TYPE) { ++ if ((stp = (dbg_sym_t *)kl_find_btnode((btnode_t *) ++ type_tree, name, (int *)NULL))) { ++ goto found_sym; ++ } ++ } ++ if (type & DBG_TYPEDEF) { ++ if ((stp = (dbg_sym_t *)kl_find_btnode((btnode_t *) ++ typedef_tree, name, (int *)NULL))) { ++ goto found_sym; ++ } ++ } ++ if (!stp) { ++ return((dbg_sym_t*)NULL); ++ } ++ } ++found_sym: ++ if (typenum) { ++ dbg_hashrec_t *hshp; ++ ++ if (stp) { ++ if (stp->sym_typenum == typenum) { ++ return(stp); ++ } ++ } else if ((hshp = dbg_hash[TYPE_NUM_HASH(typenum)])) { ++ while (hshp) { ++ if (hshp->h_typenum == typenum) { ++ return(hshp->h_ptr); ++ } ++ hshp = hshp->h_next; ++ } ++ } ++ } ++ return(stp); ++} ++ ++/* ++ * kl_find_type() -- find a KLT type by name. ++ */ ++kltype_t * ++kl_find_type(char *name, int tnum) ++{ ++ dbg_sym_t *stp; ++ kltype_t *kltp = (kltype_t *)NULL; ++ ++ if (!have_debug_file) { ++ kdb_printf("no debuginfo file\n"); ++ return kltp; ++ } ++ ++ if (!tnum || IS_TYPE(tnum)) { ++ if ((stp = dbg_find_sym(name, DBG_TYPE, 0))) { ++ kltp = (kltype_t *)stp->sym_kltype; ++ if (tnum && !(kltp->kl_type & tnum)) { ++ /* We have found a type by this name ++ * but it does not have the right ++ * type number (e.g., we're looking ++ * for a struct and we don't find ++ * a KLT_STRUCT type by this name). ++ */ ++ return((kltype_t *)NULL); ++ } ++ } ++ } ++ if (!tnum || IS_TYPEDEF(tnum)) { ++ if ((stp = dbg_find_sym(name, DBG_TYPEDEF, 0))) { ++ kltp = (kltype_t *)stp->sym_kltype; ++ } ++ } ++ return(kltp); ++} ++ ++/* ++ * kl_first_btnode() -- non-recursive implementation. ++ */ ++btnode_t * ++kl_first_btnode(btnode_t *np) ++{ ++ if (!np) { ++ return((btnode_t *)NULL); ++ } ++ ++ /* Walk down the left side 'til the end... ++ */ ++ while (np->bt_left) { ++ np = np->bt_left; ++ } ++ return(np); ++} ++ ++/* ++ * kl_next_btnode() -- non-recursive implementation. ++ */ ++btnode_t * ++kl_next_btnode(btnode_t *node) ++{ ++ btnode_t *np = node, *parent; ++ ++ if (np) { ++ if (np->bt_right) { ++ return(kl_first_btnode(np->bt_right)); ++ } else { ++ parent = np->bt_parent; ++next: ++ if (parent) { ++ if (parent->bt_left == np) { ++ return(parent); ++ } ++ np = parent; ++ parent = parent->bt_parent; ++ goto next; ++ } ++ } ++ } ++ return((btnode_t *)NULL); ++} ++ ++/* ++ * dbg_next_sym() ++ */ ++dbg_sym_t * ++dbg_next_sym(dbg_sym_t *stp) ++{ ++ dbg_sym_t *next_stp; ++ ++ next_stp = (dbg_sym_t *)kl_next_btnode((btnode_t *)stp); ++ return(next_stp); ++} ++ ++/* ++ * kl_prev_btnode() -- non-recursive implementation. ++ */ ++btnode_t * ++kl_prev_btnode(btnode_t *node) ++{ ++ btnode_t *np = node, *parent; ++ ++ if (np) { ++ if (np->bt_left) { ++ np = np->bt_left; ++ while (np->bt_right) { ++ np = np->bt_right; ++ } ++ return(np); ++ } ++ parent = np->bt_parent; ++next: ++ if (parent) { ++ if (parent->bt_right == np) { ++ return(parent); ++ } ++ np = parent; ++ parent = parent->bt_parent; ++ goto next; ++ } ++ } ++ return((btnode_t *)NULL); ++} ++ ++/* ++ * dbg_prev_sym() ++ */ ++dbg_sym_t * ++dbg_prev_sym(dbg_sym_t *stp) ++{ ++ dbg_sym_t *prev_stp; ++ ++ prev_stp = (dbg_sym_t *)kl_prev_btnode((btnode_t *)stp); ++ return(prev_stp); ++} ++ ++/* ++ * kl_find_next_type() -- find next KLT type ++ */ ++kltype_t * ++kl_find_next_type(kltype_t *kltp, int type) ++{ ++ kltype_t *nkltp = NULL; ++ dbg_sym_t *nstp; ++ ++ if (kltp && kltp->kl_ptr) { ++ nstp = (dbg_sym_t *)kltp->kl_ptr; ++ nkltp = (kltype_t *)nstp->sym_kltype; ++ if (type) { ++ while(nkltp && !(nkltp->kl_type & type)) { ++ if ((nstp = dbg_next_sym(nstp))) { ++ nkltp = (kltype_t *)nstp->sym_kltype; ++ } else { ++ nkltp = (kltype_t *)NULL; ++ } ++ } ++ } ++ } ++ return(nkltp); ++} ++ ++/* ++ * dbg_first_sym() ++ */ ++dbg_sym_t * ++dbg_first_sym(int type) ++{ ++ dbg_sym_t *stp = (dbg_sym_t *)NULL; ++ ++ switch(type) { ++ case DBG_TYPE: ++ stp = (dbg_sym_t *) ++ kl_first_btnode((btnode_t *)type_tree); ++ break; ++ case DBG_TYPEDEF: ++ stp = (dbg_sym_t *) ++ kl_first_btnode((btnode_t *)typedef_tree); ++ break; ++ } ++ return(stp); ++} ++ ++/* ++ * kl_first_type() ++ */ ++kltype_t * ++kl_first_type(int tnum) ++{ ++ kltype_t *kltp = NULL; ++ dbg_sym_t *stp; ++ ++ if (IS_TYPE(tnum)) { ++ /* If (tnum == KLT_TYPE), then return the first type ++ * record, regardless of the type. Otherwise, search ++ * for the frst type that mapps into tnum. ++ */ ++ if ((stp = dbg_first_sym(DBG_TYPE))) { ++ kltp = (kltype_t *)stp->sym_kltype; ++ if (tnum != KLT_TYPE) { ++ while (kltp && !(kltp->kl_type & tnum)) { ++ if ((stp = dbg_next_sym(stp))) { ++ kltp = (kltype_t *)stp->sym_kltype; ++ } else { ++ kltp = (kltype_t *)NULL; ++ } ++ } ++ } ++ } ++ } else if (IS_TYPEDEF(tnum)) { ++ if ((stp = dbg_first_sym(DBG_TYPEDEF))) { ++ kltp = (kltype_t *)stp->sym_kltype; ++ } ++ } ++ return(kltp); ++} ++ ++/* ++ * kl_next_type() ++ */ ++kltype_t * ++kl_next_type(kltype_t *kltp) ++{ ++ dbg_sym_t *stp, *nstp; ++ kltype_t *nkltp = (kltype_t *)NULL; ++ ++ if (!kltp) { ++ return((kltype_t *)NULL); ++ } ++ stp = (dbg_sym_t *)kltp->kl_ptr; ++ if ((nstp = dbg_next_sym(stp))) { ++ nkltp = (kltype_t *)nstp->sym_kltype; ++ } ++ return(nkltp); ++} ++ ++/* ++ * kl_prev_type() ++ */ ++kltype_t * ++kl_prev_type(kltype_t *kltp) ++{ ++ dbg_sym_t *stp, *pstp; ++ kltype_t *pkltp = (kltype_t *)NULL; ++ ++ if (!kltp) { ++ return((kltype_t *)NULL); ++ } ++ stp = (dbg_sym_t *)kltp->kl_ptr; ++ if ((pstp = dbg_prev_sym(stp))) { ++ pkltp = (kltype_t *)pstp->sym_kltype; ++ } ++ return(pkltp); ++} ++ ++/* ++ * kl_realtype() ++ */ ++kltype_t * ++kl_realtype(kltype_t *kltp, int tnum) ++{ ++ kltype_t *rkltp = kltp; ++ ++ while (rkltp) { ++ if (tnum && (rkltp->kl_type == tnum)) { ++ break; ++ } ++ if (!rkltp->kl_realtype) { ++ break; ++ } ++ if (rkltp->kl_realtype == rkltp) { ++ break; ++ } ++ rkltp = rkltp->kl_realtype; ++ if (rkltp == kltp) { ++ break; ++ } ++ } ++ return(rkltp); ++} ++ ++/* ++ * dbg_find_typenum() ++ */ ++dbg_type_t * ++dbg_find_typenum(uint64_t typenum) ++{ ++ dbg_sym_t *stp; ++ dbg_type_t *sp = (dbg_type_t *)NULL; ++ ++ if ((stp = dbg_find_sym(0, DBG_TYPE, typenum))) { ++ sp = (dbg_type_t *)stp->sym_kltype; ++ } ++ return(sp); ++} ++ ++/* ++ * find type by typenum ++ */ ++kltype_t * ++kl_find_typenum(uint64_t typenum) ++{ ++ kltype_t *kltp; ++ ++ kltp = (kltype_t *)dbg_find_typenum(typenum); ++ return(kltp); ++} ++ ++/* ++ * kl_find_btnode() -- non-recursive implementation. ++ */ ++btnode_t * ++_kl_find_btnode(btnode_t *np, char *key, int *max_depth, size_t len) ++{ ++ int ret; ++ btnode_t *next, *prev; ++ ++ if (np) { ++ if (max_depth) { ++ (*max_depth)++; ++ } ++ next = np; ++again: ++ if (len) { ++ ret = strncmp(key, next->bt_key, len); ++ } else { ++ ret = strcmp(key, next->bt_key); ++ } ++ if (ret == 0) { ++ if ((prev = kl_prev_btnode(next))) { ++ if (len) { ++ ret = strncmp(key, prev->bt_key, len); ++ } else { ++ ret = strcmp(key, prev->bt_key); ++ } ++ if (ret == 0) { ++ next = prev; ++ goto again; ++ } ++ } ++ return(next); ++ } else if (ret < 0) { ++ if ((next = next->bt_left)) { ++ goto again; ++ } ++ } else { ++ if ((next = next->bt_right)) { ++ goto again; ++ } ++ } ++ } ++ return((btnode_t *)NULL); ++} ++ ++/* ++ * kl_type_size() ++ */ ++int ++kl_type_size(kltype_t *kltp) ++{ ++ kltype_t *rkltp; ++ ++ if (!kltp) { ++ return(0); ++ } ++ if (!(rkltp = kl_realtype(kltp, 0))) { ++ return(0); ++ } ++ return(rkltp->kl_size); ++} ++ ++/* ++ * kl_struct_len() ++ */ ++int ++kl_struct_len(char *s) ++{ ++ kltype_t *kltp; ++ ++ if ((kltp = kl_find_type(s, (KLT_TYPES)))) { ++ return kl_type_size(kltp); ++ } ++ return(0); ++} ++ ++/* ++ * kl_get_member() ++ */ ++kltype_t * ++kl_get_member(kltype_t *kltp, char *f) ++{ ++ kltype_t *mp; ++ ++ if ((mp = kltp->kl_member)) { ++ while (mp) { ++ if (mp->kl_flags & TYP_ANONYMOUS_FLG) { ++ kltype_t *amp; ++ ++ if ((amp = kl_get_member(mp->kl_realtype, f))) { ++ return(amp); ++ } ++ } else if (!strcmp(mp->kl_name, f)) { ++ break; ++ } ++ mp = mp->kl_member; ++ } ++ } ++ return(mp); ++} ++ ++/* ++ * kl_member() ++ */ ++kltype_t * ++kl_member(char *s, char *f) ++{ ++ kltype_t *kltp, *mp = NULL; ++ ++ if (!(kltp = kl_find_type(s, (KLT_STRUCT|KLT_UNION)))) { ++ if ((kltp = kl_find_type(s, KLT_TYPEDEF))) { ++ kltp = kl_realtype(kltp, 0); ++ } ++ } ++ if (kltp) { ++ mp = kl_get_member(kltp, f); ++ } ++ return(mp); ++} ++ ++ ++/* ++ * kl_get_member_offset() ++ */ ++int ++kl_get_member_offset(kltype_t *kltp, char *f) ++{ ++ kltype_t *mp; ++ ++ if ((mp = kltp->kl_member)) { ++ while (mp) { ++ if (mp->kl_flags & TYP_ANONYMOUS_FLG) { ++ int off; ++ ++ /* Drill down to see if the member we are looking for is in ++ * an anonymous union or struct. Since this call is recursive, ++ * the drill down may actually be multi-layer. ++ */ ++ off = kl_get_member_offset(mp->kl_realtype, f); ++ if (off >= 0) { ++ return(mp->kl_offset + off); ++ } ++ } else if (!strcmp(mp->kl_name, f)) { ++ return(mp->kl_offset); ++ } ++ mp = mp->kl_member; ++ } ++ } ++ return(-1); ++} ++ ++/* ++ * kl_member_offset() ++ */ ++int ++kl_member_offset(char *s, char *f) ++{ ++ int off = -1; ++ kltype_t *kltp; ++ ++ if (!(kltp = kl_find_type(s, (KLT_STRUCT|KLT_UNION)))) { ++ if ((kltp = kl_find_type(s, KLT_TYPEDEF))) { ++ kltp = kl_realtype(kltp, 0); ++ } ++ } ++ if (kltp) { ++ off = kl_get_member_offset(kltp, f); ++ } ++ return(off); ++} ++ ++/* ++ * kl_is_member() ++ */ ++int ++kl_is_member(char *s, char *f) ++{ ++ kltype_t *mp; ++ ++ if ((mp = kl_member(s, f))) { ++ return(1); ++ } ++ return(0); ++} ++ ++/* ++ * kl_member_size() ++ */ ++int ++kl_member_size(char *s, char *f) ++{ ++ kltype_t *mp; ++ ++ if ((mp = kl_member(s, f))) { ++ return(mp->kl_size); ++ } ++ return(0); ++} ++ ++#define TAB_SPACES 8 ++#define LEVEL_INDENT(level, flags) {\ ++ int i, j; \ ++ if (!(flags & NO_INDENT)) { \ ++ for (i = 0; i < level; i++) { \ ++ for (j = 0; j < TAB_SPACES; j++) { \ ++ kdb_printf(" "); \ ++ } \ ++ }\ ++ } \ ++} ++#define PRINT_NL(flags) \ ++ if (!(flags & SUPPRESS_NL)) { \ ++ kdb_printf("\n"); \ ++ } ++#define PRINT_SEMI_COLON(level, flags) \ ++ if (level && (!(flags & SUPPRESS_SEMI_COLON))) { \ ++ kdb_printf(";"); \ ++ } ++ ++/* ++ * print_realtype() ++ */ ++static void ++print_realtype(kltype_t *kltp) ++{ ++ kltype_t *rkltp; ++ ++ if ((rkltp = kltp->kl_realtype)) { ++ while (rkltp && rkltp->kl_realtype) { ++ rkltp = rkltp->kl_realtype; ++ } ++ if (rkltp->kl_type == KLT_BASE) { ++ kdb_printf(" (%s)", rkltp->kl_name); ++ } ++ } ++} ++ ++int align_chk = 0; ++/* ++ * kl_print_uint16() ++ * ++ */ ++void ++kl_print_uint16(void *ptr, int flags) ++{ ++ unsigned long long a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * * dump core) ++ * */ ++ if (align_chk && (uaddr_t)ptr % 16) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(unsigned long long *) ptr; ++ if (flags & C_HEX) { ++ kdb_printf("%#llx", a); ++ } else if (flags & C_BINARY) { ++ kdb_printf("0b"); ++ kl_binary_print(a); ++ } else { ++ kdb_printf("%llu", a); ++ } ++} ++ ++#if 0 ++/* ++ * kl_print_float16() ++ * ++ */ ++void ++kl_print_float16(void *ptr, int flags) ++{ ++ double a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * * dump core) ++ * */ ++ if (align_chk && (uaddr_t)ptr % 16) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(double*) ptr; ++ kdb_printf("%f", a); ++} ++#endif ++ ++/* ++ * kl_print_int16() ++ * ++ */ ++void ++kl_print_int16(void *ptr, int flags) ++{ ++ long long a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * * dump core) ++ * */ ++ if (align_chk && (uaddr_t)ptr % 16) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(long long *) ptr; ++ if (flags & C_HEX) { ++ kdb_printf("%#llx", a); ++ } else if (flags & C_BINARY) { ++ kdb_printf("0b"); ++ kl_binary_print(a); ++ } else { ++ kdb_printf("%lld", a); ++ } ++} ++ ++/* ++ * kl_print_int8() ++ */ ++void ++kl_print_int8(void *ptr, int flags) ++{ ++ long long a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * dump core) ++ */ ++ if (align_chk && (uaddr_t)ptr % 8) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(long long *) ptr; ++ if (flags & C_HEX) { ++ kdb_printf("%#llx", a); ++ } else if (flags & C_BINARY) { ++ kdb_printf("0b"); ++ kl_binary_print(a); ++ } else { ++ kdb_printf("%lld", a); ++ } ++} ++ ++#if 0 ++/* ++ * kl_print_float8() ++ */ ++void ++kl_print_float8(void *ptr, int flags) ++{ ++ double a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * dump core) ++ */ ++ if (align_chk && (uaddr_t)ptr % 8) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(double*) ptr; ++ kdb_printf("%f", a); ++} ++#endif ++ ++/* ++ * kl_print_uint8() ++ */ ++void ++kl_print_uint8(void *ptr, int flags) ++{ ++ unsigned long long a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * dump core) ++ */ ++ if (align_chk && (uaddr_t)ptr % 8) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(unsigned long long *) ptr; ++ if (flags & C_HEX) { ++ kdb_printf("%#llx", a); ++ } else if (flags & C_BINARY) { ++ kdb_printf("0b"); ++ kl_binary_print(a); ++ } else { ++ kdb_printf("%llu", a); ++ } ++} ++ ++/* ++ * kl_print_int4() ++ */ ++void ++kl_print_int4(void *ptr, int flags) ++{ ++ int32_t a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * dump core ++ */ ++ if (align_chk && (uaddr_t)ptr % 4) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(int32_t*) ptr; ++ if (flags & C_HEX) { ++ kdb_printf("0x%x", a); ++ } else if (flags & C_BINARY) { ++ uint64_t value = a & 0xffffffff; ++ kdb_printf("0b"); ++ kl_binary_print(value); ++ } else { ++ kdb_printf("%d", a); ++ } ++} ++ ++#if 0 ++/* ++ * kl_print_float4() ++ */ ++void ++kl_print_float4(void *ptr, int flags) ++{ ++ float a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * dump core) ++ */ ++ if (align_chk && (uaddr_t)ptr % 4) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(float*) ptr; ++ kdb_printf("%f", a); ++} ++#endif ++ ++/* ++ * kl_print_uint4() ++ */ ++void ++kl_print_uint4(void *ptr, int flags) ++{ ++ uint32_t a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * dump core) ++ */ ++ if (align_chk && (uaddr_t)ptr % 4) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(uint32_t*) ptr; ++ if (flags & C_HEX) { ++ kdb_printf("0x%x", a); ++ } else if (flags & C_BINARY) { ++ uint64_t value = a & 0xffffffff; ++ kdb_printf("0b"); ++ kl_binary_print(value); ++ } else { ++ kdb_printf("%u", a); ++ } ++} ++ ++/* ++ * kl_print_int2() ++ */ ++void ++kl_print_int2(void *ptr, int flags) ++{ ++ int16_t a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * dump core ++ */ ++ if (align_chk && (uaddr_t)ptr % 2) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(int16_t*) ptr; ++ if (flags & C_HEX) { ++ kdb_printf("0x%hx", a); ++ } else if (flags & C_BINARY) { ++ uint64_t value = a & 0xffff; ++ kdb_printf("0b"); ++ kl_binary_print(value); ++ } else { ++ kdb_printf("%hd", a); ++ } ++} ++ ++/* ++ * kl_print_uint2() ++ */ ++void ++kl_print_uint2(void *ptr, int flags) ++{ ++ uint16_t a; ++ ++ /* Make sure the pointer is properly aligned (or we will ++ * dump core ++ */ ++ if (align_chk && (uaddr_t)ptr % 2) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ a = *(uint16_t*) ptr; ++ if (flags & C_HEX) { ++ kdb_printf("0x%hx", a); ++ } else if (flags & C_BINARY) { ++ uint64_t value = a & 0xffff; ++ kdb_printf("0b"); ++ kl_binary_print(value); ++ } else { ++ kdb_printf("%hu", a); ++ } ++} ++ ++/* ++ * kl_print_char() ++ */ ++void ++kl_print_char(void *ptr, int flags) ++{ ++ char c; ++ ++ if (flags & C_HEX) { ++ kdb_printf("0x%x", (*(char *)ptr) & 0xff); ++ } else if (flags & C_BINARY) { ++ uint64_t value = (*(char *)ptr) & 0xff; ++ kdb_printf("0b"); ++ kl_binary_print(value); ++ } else { ++ c = *(char *)ptr; ++ ++ kdb_printf("\'\\%03o\'", (unsigned char)c); ++ switch (c) { ++ case '\a' : ++ kdb_printf(" = \'\\a\'"); ++ break; ++ case '\b' : ++ kdb_printf(" = \'\\b\'"); ++ break; ++ case '\t' : ++ kdb_printf(" = \'\\t\'"); ++ break; ++ case '\n' : ++ kdb_printf(" = \'\\n\'"); ++ break; ++ case '\f' : ++ kdb_printf(" = \'\\f\'"); ++ break; ++ case '\r' : ++ kdb_printf(" = \'\\r\'"); ++ break; ++ case '\e' : ++ kdb_printf(" = \'\\e\'"); ++ break; ++ default : ++ if( !iscntrl((unsigned char) c) ) { ++ kdb_printf(" = \'%c\'", c); ++ } ++ break; ++ } ++ } ++} ++ ++/* ++ * kl_print_uchar() ++ */ ++void ++kl_print_uchar(void *ptr, int flags) ++{ ++ if (flags & C_HEX) { ++ kdb_printf("0x%x", *(unsigned char *)ptr); ++ } else if (flags & C_BINARY) { ++ uint64_t value = (*(unsigned char *)ptr) & 0xff; ++ kdb_printf("0b"); ++ kl_binary_print(value); ++ } else { ++ kdb_printf("%u", *(unsigned char *)ptr); ++ } ++} ++ ++/* ++ * kl_print_base() ++ */ ++void ++kl_print_base(void *ptr, int size, int encoding, int flags) ++{ ++ /* FIXME: untested */ ++ if (invalid_address((kaddr_t)ptr, size)) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); ++ return; ++ } ++ switch (size) { ++ ++ case 1: ++ if (encoding == ENC_UNSIGNED) { ++ kl_print_uchar(ptr, flags); ++ } else { ++ kl_print_char(ptr, flags); ++ } ++ break; ++ ++ case 2: ++ if (encoding == ENC_UNSIGNED) { ++ kl_print_uint2(ptr, flags); ++ } else { ++ kl_print_int2(ptr, flags); ++ } ++ break; ++ ++ case 4: ++ if (encoding == ENC_UNSIGNED) { ++ kl_print_uint4(ptr, flags); ++ } else if (encoding == ENC_FLOAT) { ++ printk("error: print of 4-byte float\n"); ++ /* kl_print_float4(ptr, flags); */ ++ } else { ++ kl_print_int4(ptr, flags); ++ } ++ break; ++ ++ case 8: ++ if (encoding == ENC_UNSIGNED) { ++ kl_print_uint8(ptr, flags); ++ } else if (encoding == ENC_FLOAT) { ++ printk("error: print of 8-byte float\n"); ++ /* kl_print_float8(ptr, flags); */ ++ } else { ++ kl_print_int8(ptr, flags); ++ } ++ break; ++ ++ case 16: ++ if (encoding == ENC_UNSIGNED) { ++ /* Ex: unsigned long long */ ++ kl_print_uint16(ptr, flags); ++ } else if (encoding == ENC_FLOAT) { ++ printk("error: print of 16-byte float\n"); ++ /* Ex: long double */ ++ /* kl_print_float16(ptr, flags); */ ++ } else { ++ /* Ex: long long */ ++ kl_print_int16(ptr, flags); ++ } ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++/* ++ * kl_print_base_value() ++ */ ++void ++kl_print_base_value(void *ptr, kltype_t *kltp, int flags) ++{ ++ kltype_t *rkltp=NULL; ++ ++ if (kltp->kl_type != KLT_BASE) { ++ if (!(rkltp = kltp->kl_realtype)) { ++ return; ++ } ++ if (rkltp->kl_type != KLT_BASE) { ++ return; ++ } ++ } else { ++ rkltp = kltp; ++ } ++ kl_print_base(ptr, rkltp->kl_size, rkltp->kl_encoding, flags); ++} ++ ++/* ++ * kl_print_typedef_type() ++ */ ++void ++kl_print_typedef_type( ++ void *ptr, ++ kltype_t *kltp, ++ int level, ++ int flags) ++{ ++ char *name; ++ kltype_t *rkltp; ++ ++ if (ptr) { ++ rkltp = kltp->kl_realtype; ++ while (rkltp->kl_type == KLT_TYPEDEF) { ++ if (rkltp->kl_realtype) { ++ rkltp = rkltp->kl_realtype; ++ } ++ } ++ if (rkltp->kl_type == KLT_POINTER) { ++ kl_print_pointer_type(ptr, kltp, level, flags); ++ return; ++ } ++ switch (rkltp->kl_type) { ++ case KLT_BASE: ++ kl_print_base_type(ptr, kltp, ++ level, flags); ++ break; ++ ++ case KLT_UNION: ++ case KLT_STRUCT: ++ kl_print_struct_type(ptr, kltp, ++ level, flags); ++ break; ++ ++ case KLT_ARRAY: ++ kl_print_array_type(ptr, kltp, ++ level, flags); ++ break; ++ ++ case KLT_ENUMERATION: ++ kl_print_enumeration_type(ptr, ++ kltp, level, flags); ++ break; ++ ++ default: ++ kl_print_base_type(ptr, kltp, ++ level, flags); ++ break; ++ } ++ } else { ++ LEVEL_INDENT(level, flags); ++ if (flags & NO_REALTYPE) { ++ rkltp = kltp; ++ } else { ++ rkltp = kltp->kl_realtype; ++ while (rkltp && rkltp->kl_type == KLT_POINTER) { ++ rkltp = rkltp->kl_realtype; ++ } ++ } ++ if (!rkltp) { ++ if (SUPPRESS_NAME) { ++ kdb_printf(""); ++ } else { ++ kdb_printf( "typedef %s;", ++ kltp->kl_name); ++ } ++ return; ++ } ++ if (rkltp->kl_type == KLT_FUNCTION) { ++ if (kltp->kl_realtype->kl_type == KLT_POINTER) { ++ kdb_printf("typedef %s(*%s)();", ++ kltp->kl_typestr, kltp->kl_name); ++ } else { ++ kdb_printf( "typedef %s(%s)();", ++ kltp->kl_typestr, kltp->kl_name); ++ } ++ } else if (rkltp->kl_type == KLT_ARRAY) { ++ kl_print_array_type(ptr, rkltp, level, flags); ++ } else if (rkltp->kl_type == KLT_TYPEDEF) { ++ if (!(name = rkltp->kl_name)) { ++ name = rkltp->kl_typestr; ++ } ++ ++ if (SUPPRESS_NAME) { ++ kdb_printf("%s", name); ++ } else { ++ kdb_printf("typedef %s%s;", ++ name, kltp->kl_name); ++ } ++ print_realtype(rkltp); ++ } else { ++ kl_print_type(ptr, rkltp, level, flags); ++ } ++ PRINT_NL(flags); ++ } ++} ++ ++/* ++ * kl_print_pointer_type() ++ */ ++void ++kl_print_pointer_type( ++ void *ptr, ++ kltype_t *kltp, ++ int level, ++ int flags) ++{ ++ kltype_t *itp; ++ ++ if (kltp->kl_type == KLT_MEMBER) { ++ itp = kltp->kl_realtype; ++ } else { ++ itp = kltp; ++ } ++ ++ /* See if this is a pointer to a function. If it is, then it ++ * has to be handled differently... ++ */ ++ while (itp->kl_type == KLT_POINTER) { ++ if ((itp = itp->kl_realtype)) { ++ if (itp->kl_type == KLT_FUNCTION) { ++ kl_print_function_type(ptr, ++ kltp, level, flags); ++ return; ++ } ++ } else { ++ LEVEL_INDENT(level, flags); ++ kdb_printf("%s%s;\n", ++ kltp->kl_typestr, kltp->kl_name); ++ return; ++ } ++ } ++ ++ LEVEL_INDENT(level, flags); ++ if (ptr) { ++ kaddr_t tmp; ++ tmp = *(kaddr_t *)ptr; ++ flags |= SUPPRESS_SEMI_COLON; ++ if(kltp->kl_name){ ++ if (*(kaddr_t *)ptr) { ++ kdb_printf("%s = 0x%"FMTPTR"x", ++ kltp->kl_name, tmp); ++ } else { ++ kdb_printf("%s = (nil)", kltp->kl_name); ++ } ++ } else { ++ if (tmp != 0) { ++ kdb_printf("0x%"FMTPTR"x", tmp); ++ } else { ++ kdb_printf( "(nil)"); ++ } ++ } ++ } else { ++ if (kltp->kl_typestr) { ++ if (kltp->kl_name && !(flags & SUPPRESS_NAME)) { ++ kdb_printf("%s%s", ++ kltp->kl_typestr, kltp->kl_name); ++ } else { ++ kdb_printf("%s", kltp->kl_typestr); ++ } ++ } else { ++ kdb_printf(""); ++ } ++ } ++ PRINT_SEMI_COLON(level, flags); ++ PRINT_NL(flags); ++} ++ ++/* ++ * kl_print_function_type() ++ */ ++void ++kl_print_function_type( ++ void *ptr, ++ kltype_t *kltp, ++ int level, ++ int flags) ++{ ++ LEVEL_INDENT(level, flags); ++ if (ptr) { ++ kaddr_t a; ++ ++ a = *(kaddr_t *)ptr; ++ kdb_printf("%s = 0x%"FMTPTR"x", kltp->kl_name, a); ++ } else { ++ if (flags & SUPPRESS_NAME) { ++ kdb_printf("%s(*)()", kltp->kl_typestr); ++ } else { ++ kdb_printf("%s(*%s)();", ++ kltp->kl_typestr, kltp->kl_name); ++ } ++ } ++ PRINT_NL(flags); ++} ++ ++/* ++ * kl_print_array_type() ++ */ ++void ++kl_print_array_type(void *ptr, kltype_t *kltp, int level, int flags) ++{ ++ int i, count = 0, anon = 0, size, low, high, multi = 0; ++ char typestr[128], *name, *p; ++ kltype_t *rkltp, *etp, *retp; ++ ++ if (kltp->kl_type != KLT_ARRAY) { ++ if ((rkltp = kltp->kl_realtype)) { ++ while (rkltp->kl_type != KLT_ARRAY) { ++ if (!(rkltp = rkltp->kl_realtype)) { ++ break; ++ } ++ } ++ } ++ if (!rkltp) { ++ LEVEL_INDENT(level, flags); ++ kdb_printf(""); ++ PRINT_SEMI_COLON(level, flags); ++ PRINT_NL(flags); ++ return; ++ } ++ } else { ++ rkltp = kltp; ++ } ++ ++ etp = rkltp->kl_elementtype; ++ if (!etp) { ++ LEVEL_INDENT(level, flags); ++ kdb_printf(" %s", rkltp->kl_name); ++ PRINT_SEMI_COLON(level, flags); ++ PRINT_NL(flags); ++ return; ++ } ++ ++ /* Set retp to point to the actual element type. This is necessary ++ * for multi-dimensional arrays, which link using the kl_elementtype ++ * member. ++ */ ++ retp = etp; ++ while (retp->kl_type == KLT_ARRAY) { ++ retp = retp->kl_elementtype; ++ } ++ low = rkltp->kl_low_bounds + 1; ++ high = rkltp->kl_high_bounds; ++ ++ if (ptr) { ++ ++ p = ptr; ++ ++ if ((retp->kl_size == 1) && (retp->kl_encoding == ENC_CHAR)) { ++ if (kltp->kl_type == KLT_MEMBER) { ++ LEVEL_INDENT(level, flags); ++ } ++ if (flags & SUPPRESS_NAME) { ++ kdb_printf("\""); ++ flags &= ~SUPPRESS_NAME; ++ } else { ++ kdb_printf("%s = \"", kltp->kl_name); ++ } ++ for (i = 0; i < high; i++) { ++ if (*(char*)p == 0) { ++ break; ++ } ++ kdb_printf("%c", *(char *)p); ++ p++; ++ } ++ kdb_printf("\""); ++ PRINT_NL(flags); ++ } else { ++ if (kltp->kl_type == KLT_MEMBER) { ++ LEVEL_INDENT(level, flags); ++ } ++ ++ if (flags & SUPPRESS_NAME) { ++ kdb_printf("{\n"); ++ flags &= ~SUPPRESS_NAME; ++ } else { ++ kdb_printf("%s = {\n", kltp->kl_name); ++ } ++ ++ if (retp->kl_type == KLT_POINTER) { ++ size = sizeof(void *); ++ } else { ++ while (retp->kl_realtype) { ++ retp = retp->kl_realtype; ++ } ++ size = retp->kl_size; ++ } ++ if ((retp->kl_type != KLT_STRUCT) && ++ (retp->kl_type != KLT_UNION)) { ++ /* Turn off the printing of names for all ++ * but structs and unions. ++ */ ++ flags |= SUPPRESS_NAME; ++ } ++ for (i = low; i <= high; i++) { ++ ++ LEVEL_INDENT(level + 1, flags); ++ kdb_printf("[%d] ", i); ++ ++ switch (retp->kl_type) { ++ case KLT_POINTER : ++ kl_print_pointer_type( ++ p, retp, level, ++ flags|NO_INDENT); ++ break; ++ ++ case KLT_TYPEDEF: ++ kl_print_typedef_type( ++ p, retp, level, ++ flags|NO_INDENT); ++ break; ++ ++ case KLT_BASE: ++ kl_print_base_value(p, ++ retp, flags|NO_INDENT); ++ kdb_printf("\n"); ++ break; ++ ++ case KLT_ARRAY: ++ kl_print_array_type(p, retp, ++ level + 1, ++ flags|SUPPRESS_NAME); ++ break; ++ ++ case KLT_STRUCT: ++ case KLT_UNION: ++ kl_print_struct_type(p, ++ retp, level + 1, ++ flags|NO_INDENT); ++ break; ++ ++ default: ++ kl_print_base_value( ++ p, retp, ++ flags|NO_INDENT); ++ kdb_printf("\n"); ++ break; ++ } ++ p = (void *)((uaddr_t)p + size); ++ } ++ LEVEL_INDENT(level, flags); ++ kdb_printf("}"); ++ PRINT_SEMI_COLON(level, flags); ++ PRINT_NL(flags); ++ } ++ } else { ++ if (rkltp) { ++ count = (rkltp->kl_high_bounds - ++ rkltp->kl_low_bounds) + 1; ++ } else { ++ count = 1; ++ } ++ ++ if (!strcmp(retp->kl_typestr, "struct ") || ++ !strcmp(retp->kl_typestr, "union ")) { ++ anon = 1; ++ } ++next_dimension: ++ switch (retp->kl_type) { ++ ++ case KLT_UNION: ++ case KLT_STRUCT: ++ if (anon) { ++ if (multi) { ++ kdb_printf("[%d]", count); ++ break; ++ } ++ kl_print_struct_type(ptr, retp, level, ++ flags| ++ SUPPRESS_NL| ++ SUPPRESS_SEMI_COLON); ++ if (kltp->kl_type == KLT_MEMBER) { ++ kdb_printf(" %s[%d]", ++ kltp->kl_name, count); ++ } else { ++ kdb_printf(" [%d]", count); ++ } ++ break; ++ } ++ /* else drop through */ ++ ++ default: ++ LEVEL_INDENT(level, flags); ++ if (multi) { ++ kdb_printf("[%d]", count); ++ break; ++ } ++ name = kltp->kl_name; ++ if (retp->kl_type == KLT_TYPEDEF) { ++ strcpy(typestr, retp->kl_name); ++ strcat(typestr, " "); ++ } else { ++ strcpy(typestr, retp->kl_typestr); ++ } ++ if (!name || (flags & SUPPRESS_NAME)) { ++ kdb_printf("%s[%d]", typestr, count); ++ } else { ++ kdb_printf("%s%s[%d]", ++ typestr, name, count); ++ } ++ } ++ if (etp->kl_type == KLT_ARRAY) { ++ count = etp->kl_high_bounds - etp->kl_low_bounds + 1; ++ etp = etp->kl_elementtype; ++ multi++; ++ goto next_dimension; ++ } ++ PRINT_SEMI_COLON(level, flags); ++ PRINT_NL(flags); ++ } ++} ++ ++/* ++ * kl_print_enumeration_type() ++ */ ++void ++kl_print_enumeration_type( ++ void *ptr, ++ kltype_t *kltp, ++ int level, ++ int flags) ++{ ++ unsigned long long val = 0; ++ kltype_t *mp, *rkltp; ++ ++ rkltp = kl_realtype(kltp, KLT_ENUMERATION); ++ if (ptr) { ++ switch (kltp->kl_size) { ++ case 1: ++ val = *(unsigned long long *)ptr; ++ break; ++ ++ case 2: ++ val = *(uint16_t *)ptr; ++ break; ++ ++ case 4: ++ val = *(uint32_t *)ptr; ++ break; ++ ++ case 8: ++ val = *(uint64_t *)ptr; ++ break; ++ } ++ mp = rkltp->kl_member; ++ while (mp) { ++ if (mp->kl_value == val) { ++ break; ++ } ++ mp = mp->kl_member; ++ } ++ LEVEL_INDENT(level, flags); ++ if (mp) { ++ kdb_printf("%s = (%s=%lld)", ++ kltp->kl_name, mp->kl_name, val); ++ } else { ++ kdb_printf("%s = %lld", kltp->kl_name, val); ++ } ++ PRINT_NL(flags); ++ } else { ++ LEVEL_INDENT(level, flags); ++ kdb_printf ("%s {", kltp->kl_typestr); ++ mp = rkltp->kl_member; ++ while (mp) { ++ kdb_printf("%s = %d", mp->kl_name, mp->kl_value); ++ if ((mp = mp->kl_member)) { ++ kdb_printf(", "); ++ } ++ } ++ mp = kltp; ++ if (level) { ++ kdb_printf("} %s;", mp->kl_name); ++ } else { ++ kdb_printf("};"); ++ } ++ PRINT_NL(flags); ++ } ++} ++ ++/* ++ * kl_binary_print() ++ */ ++void ++kl_binary_print(uint64_t num) ++{ ++ int i, pre = 1; ++ ++ for (i = 63; i >= 0; i--) { ++ if (num & ((uint64_t)1 << i)) { ++ kdb_printf("1"); ++ if (pre) { ++ pre = 0; ++ } ++ } else { ++ if (!pre) { ++ kdb_printf("0"); ++ } ++ } ++ } ++ if (pre) { ++ kdb_printf("0"); ++ } ++} ++ ++/* ++ * kl_get_bit_value() ++ * ++ * x = byte_size, y = bit_size, z = bit_offset ++ */ ++uint64_t ++kl_get_bit_value(void *ptr, unsigned int x, unsigned int y, unsigned int z) ++{ ++ uint64_t value=0, mask; ++ ++ /* handle x bytes of buffer -- doing just memcpy won't work ++ * on big endian architectures ++ */ ++ switch (x) { ++ case 5: ++ case 6: ++ case 7: ++ case 8: ++ x = 8; ++ value = *(uint64_t*) ptr; ++ break; ++ case 3: ++ case 4: ++ x = 4; ++ value = *(uint32_t*) ptr; ++ break; ++ case 2: ++ value = *(uint16_t*) ptr; ++ break; ++ case 1: ++ value = *(uint8_t *)ptr; ++ break; ++ default: ++ /* FIXME: set KL_ERROR */ ++ return(0); ++ } ++ /* ++ o FIXME: correct handling of overlapping fields ++ */ ++ ++ /* goto bit offset */ ++ value = value >> z; ++ ++ /* mask bit size bits */ ++ mask = (((uint64_t)1 << y) - 1); ++ return (value & mask); ++} ++ ++/* ++ * kl_print_bit_value() ++ * ++ * x = byte_size, y = bit_size, z = bit_offset ++ */ ++void ++kl_print_bit_value(void *ptr, int x, int y, int z, int flags) ++{ ++ unsigned long long value; ++ ++ value = kl_get_bit_value(ptr, x, y, z); ++ if (flags & C_HEX) { ++ kdb_printf("%#llx", value); ++ } else if (flags & C_BINARY) { ++ kdb_printf("0b"); ++ kl_binary_print(value); ++ } else { ++ kdb_printf("%lld", value); ++ } ++} ++ ++/* ++ * kl_print_base_type() ++ */ ++void ++kl_print_base_type(void *ptr, kltype_t *kltp, int level, int flags) ++{ ++ LEVEL_INDENT(level, flags); ++ if (ptr) { ++ if (!(flags & SUPPRESS_NAME)) { ++ kdb_printf ("%s = ", kltp->kl_name); ++ } ++ } ++ if (kltp->kl_type == KLT_MEMBER) { ++ if (kltp->kl_bit_size < (kltp->kl_size * 8)) { ++ if (ptr) { ++ kl_print_bit_value(ptr, kltp->kl_size, ++ kltp->kl_bit_size, ++ kltp->kl_bit_offset, flags); ++ } else { ++ if (kltp->kl_name) { ++ kdb_printf ("%s%s :%d;", ++ kltp->kl_typestr, ++ kltp->kl_name, ++ kltp->kl_bit_size); ++ } else { ++ kdb_printf ("%s :%d;", ++ kltp->kl_typestr, ++ kltp->kl_bit_size); ++ } ++ } ++ PRINT_NL(flags); ++ return; ++ } ++ } ++ if (ptr) { ++ kltype_t *rkltp; ++ ++ rkltp = kl_realtype(kltp, 0); ++ if (rkltp->kl_encoding == ENC_UNDEFINED) { ++ /* This is a void value ++ */ ++ kdb_printf(""); ++ } else { ++ kl_print_base(ptr, kltp->kl_size, ++ rkltp->kl_encoding, flags); ++ } ++ } else { ++ if (kltp->kl_type == KLT_MEMBER) { ++ if (flags & SUPPRESS_NAME) { ++ kdb_printf ("%s", kltp->kl_typestr); ++ } else { ++ if (kltp->kl_name) { ++ kdb_printf("%s%s;", kltp->kl_typestr, ++ kltp->kl_name); ++ } else { ++ kdb_printf ("%s :%d;", ++ kltp->kl_typestr, ++ kltp->kl_bit_size); ++ } ++ } ++ } else { ++ if (SUPPRESS_NAME) { ++ kdb_printf("%s", kltp->kl_name); ++ } else { ++ kdb_printf("%s;", kltp->kl_name); ++ } ++ } ++ } ++ PRINT_NL(flags); ++} ++ ++/* ++ * kl_print_member() ++ */ ++void ++kl_print_member(void *ptr, kltype_t *mp, int level, int flags) ++{ ++ int kl_type = 0; ++ kltype_t *rkltp; ++ ++ if (flags & C_SHOWOFFSET) { ++ kdb_printf("%#x ", mp->kl_offset); ++ } ++ ++ if ((rkltp = mp->kl_realtype)) { ++ kl_type = rkltp->kl_type; ++ } else ++ kl_type = mp->kl_type; ++ switch (kl_type) { ++ case KLT_STRUCT: ++ case KLT_UNION: ++ kl_print_struct_type(ptr, mp, level, flags); ++ break; ++ case KLT_ARRAY: ++ kl_print_array_type(ptr, mp, level, flags); ++ break; ++ case KLT_POINTER: ++ kl_print_pointer_type(ptr, mp, level, flags); ++ break; ++ case KLT_FUNCTION: ++ kl_print_function_type(ptr, mp, level, flags); ++ break; ++ case KLT_BASE: ++ kl_print_base_type(ptr, mp, level, flags); ++ break; ++ case KLT_ENUMERATION: ++ kl_print_enumeration_type(ptr, mp, level, flags); ++ break; ++ case KLT_TYPEDEF: ++ while (rkltp && rkltp->kl_realtype) { ++ if (rkltp->kl_realtype == rkltp) { ++ break; ++ } ++ rkltp = rkltp->kl_realtype; ++ } ++ if (ptr) { ++ kl_print_typedef_type(ptr, mp, ++ level, flags); ++ break; ++ } ++ LEVEL_INDENT(level, flags); ++ if (flags & SUPPRESS_NAME) { ++ if (rkltp && (mp->kl_bit_size < ++ (rkltp->kl_size * 8))) { ++ kdb_printf ("%s :%d", ++ mp->kl_typestr, ++ mp->kl_bit_size); ++ } else { ++ kdb_printf("%s", ++ mp->kl_realtype->kl_name); ++ } ++ print_realtype(mp->kl_realtype); ++ } else { ++ if (rkltp && (mp->kl_bit_size < ++ (rkltp->kl_size * 8))) { ++ if (mp->kl_name) { ++ kdb_printf ("%s%s :%d;", ++ mp->kl_typestr, ++ mp->kl_name, ++ mp->kl_bit_size); ++ } else { ++ kdb_printf ("%s :%d;", ++ mp->kl_typestr, ++ mp->kl_bit_size); ++ } ++ } else { ++ kdb_printf("%s %s;", ++ mp->kl_realtype->kl_name, ++ mp->kl_name); ++ } ++ } ++ PRINT_NL(flags); ++ break; ++ ++ default: ++ LEVEL_INDENT(level, flags); ++ if (mp->kl_typestr) { ++ kdb_printf("%s%s;", ++ mp->kl_typestr, mp->kl_name); ++ } else { ++ kdb_printf("<\?\?\? kl_type:%d> %s;", ++ kl_type, mp->kl_name); ++ } ++ PRINT_NL(flags); ++ break; ++ } ++} ++ ++/* ++ * kl_print_struct_type() ++ */ ++void ++kl_print_struct_type(void *buf, kltype_t *kltp, int level, int flags) ++{ ++ int orig_flags = flags; ++ void *ptr = NULL; ++ kltype_t *mp, *rkltp; ++ ++ /* If we are printing out an actual struct, then don't print any ++ * semi colons. ++ */ ++ if (buf) { ++ flags |= SUPPRESS_SEMI_COLON; ++ } ++ ++ LEVEL_INDENT(level, flags); ++ if ((level == 0) || (flags & NO_INDENT)) { ++ kdb_printf("%s{\n", kltp->kl_typestr); ++ } else { ++ if (buf) { ++ if (level && !(kltp->kl_flags & TYP_ANONYMOUS_FLG)) { ++ kdb_printf("%s = %s{\n", ++ kltp->kl_name, kltp->kl_typestr); ++ } else { ++ kdb_printf("%s{\n", kltp->kl_typestr); ++ } ++ flags &= (~SUPPRESS_NL); ++ } else { ++ if (kltp->kl_typestr) { ++ kdb_printf("%s{\n", kltp->kl_typestr); ++ } else { ++ kdb_printf(" {\n"); ++ } ++ } ++ } ++ ++ /* If the SUPPRESS_NL, SUPPRESS_SEMI_COLON, and SUPPRESS_NAME flags ++ * are set and buf is NULL, then turn them off as they only apply ++ * at the end of the struct. We save the original flags for that ++ * purpose. ++ */ ++ if (!buf) { ++ flags &= ~(SUPPRESS_NL|SUPPRESS_SEMI_COLON|SUPPRESS_NAME); ++ } ++ ++ /* If the NO_INDENT is set, we need to turn it off at this ++ * point -- just in case we come across a member of this struct ++ * that is also a struct. ++ */ ++ if (flags & NO_INDENT) { ++ flags &= ~(NO_INDENT); ++ } ++ ++ if (kltp->kl_type == KLT_MEMBER) { ++ rkltp = kl_realtype(kltp, 0); ++ } else { ++ rkltp = kltp; ++ } ++ level++; ++ if ((mp = rkltp->kl_member)) { ++ while (mp) { ++ if (buf) { ++ ptr = buf + mp->kl_offset; ++ } ++ kl_print_member(ptr, mp, level, flags); ++ mp = mp->kl_member; ++ } ++ } else { ++ if (kltp->kl_flags & TYP_INCOMPLETE_FLG) { ++ LEVEL_INDENT(level, flags); ++ kdb_printf("\n"); ++ } ++ } ++ level--; ++ LEVEL_INDENT(level, flags); ++ ++ /* kl_size = 0 for empty structs */ ++ if (ptr || ((kltp->kl_size == 0) && buf)) { ++ kdb_printf("}"); ++ } else if ((kltp->kl_type == KLT_MEMBER) && ++ !(orig_flags & SUPPRESS_NAME) && ++ !(kltp->kl_flags & TYP_ANONYMOUS_FLG)) { ++ kdb_printf("} %s", kltp->kl_name); ++ } else { ++ kdb_printf("}"); ++ } ++ PRINT_SEMI_COLON(level, orig_flags); ++ PRINT_NL(orig_flags); ++} ++ ++/* ++ * kl_print_type() ++ */ ++void ++kl_print_type(void *buf, kltype_t *kltp, int level, int flags) ++{ ++ void *ptr; ++ ++ if (buf) { ++ if (kltp->kl_offset) { ++ ptr = (void *)((uaddr_t)buf + kltp->kl_offset); ++ } else { ++ ptr = buf; ++ } ++ } else { ++ ptr = 0; ++ } ++ ++ /* Only allow binary printing for base types ++ */ ++ if (kltp->kl_type != KLT_BASE) { ++ flags &= (~C_BINARY); ++ } ++ switch (kltp->kl_type) { ++ ++ case KLT_TYPEDEF: ++ kl_print_typedef_type(ptr, kltp, level, flags); ++ break; ++ ++ case KLT_STRUCT: ++ case KLT_UNION: ++ kl_print_struct_type(ptr, kltp, level, flags); ++ break; ++ ++ case KLT_MEMBER: ++ kl_print_member(ptr, kltp, level, flags); ++ break; ++ ++ case KLT_POINTER: ++ kl_print_pointer_type(ptr, kltp, level, flags); ++ break; ++ ++ case KLT_FUNCTION: ++ LEVEL_INDENT(level, flags); ++ kl_print_function_type(ptr, kltp, level, flags); ++ break; ++ ++ case KLT_ARRAY: ++ kl_print_array_type(ptr, kltp, level, flags); ++ break; ++ ++ case KLT_ENUMERATION: ++ kl_print_enumeration_type(ptr, ++ kltp, level, flags); ++ break; ++ ++ case KLT_BASE: ++ kl_print_base_type(ptr, kltp, level, flags); ++ break; ++ ++ default: ++ LEVEL_INDENT(level, flags); ++ if (flags & SUPPRESS_NAME) { ++ kdb_printf ("%s", kltp->kl_name); ++ } else { ++ kdb_printf ("%s %s;", ++ kltp->kl_name, kltp->kl_name); ++ } ++ PRINT_NL(flags); ++ } ++} ++ ++/* ++ * eval is from lcrash eval.c ++ */ ++ ++/* Forward declarations */ ++static void free_node(node_t *); ++static node_t *make_node(token_t *, int); ++static node_t *get_node_list(token_t *, int); ++static node_t *do_eval(int); ++static int is_unary(int); ++static int is_binary(int); ++static int precedence(int); ++static node_t *get_sizeof(void); ++static int replace_cast(node_t *, int); ++static int replace_unary(node_t *, int); ++static node_t *replace(node_t *, int); ++static void array_to_element(node_t*, node_t*); ++static int type_to_number(node_t *); ++kltype_t *number_to_type(node_t *); ++static type_t *eval_type(node_t *); ++static type_t *get_type(char *, int); ++static int add_rchild(node_t *, node_t *); ++static void free_nodelist(node_t *); ++ ++/* Global variables ++ */ ++static int logical_flag; ++static node_t *node_list = (node_t *)NULL; ++uint64_t eval_error; ++char *error_token; ++ ++/* ++ * set_eval_error() ++ */ ++static void ++set_eval_error(uint64_t ecode) ++{ ++ eval_error = ecode; ++} ++ ++/* ++ * is_typestr() ++ * ++ * We check for "struct", "union", etc. separately because they ++ * would not be an actual part of the type name. We also assume ++ * that the string passed in ++ * ++ * - does not have any leading blanks or tabs ++ * - is NULL terminated ++ * - contains only one type name to check ++ * - does not contain any '*' characters ++ */ ++static int ++is_typestr(char *str) ++{ ++ int len; ++ ++ len = strlen(str); ++ if ((len >= 6) && !strncmp(str, "struct", 6)) { ++ return(1); ++ } else if ((len >= 5) &&!strncmp(str, "union", 5)) { ++ return(1); ++ } else if ((len >= 5) &&!strncmp(str, "short", 5)) { ++ return(1); ++ } else if ((len >= 8) &&!strncmp(str, "unsigned", 8)) { ++ return(1); ++ } else if ((len >= 6) &&!strncmp(str, "signed", 6)) { ++ return(1); ++ } else if ((len >= 4) &&!strncmp(str, "long", 4)) { ++ return(1); ++ } ++ /* Strip off any trailing blanks ++ */ ++ while(*str && ((str[strlen(str) - 1] == ' ') ++ || (str[strlen(str) - 1] == '\t'))) { ++ str[strlen(str) - 1] = 0; ++ } ++ if (kl_find_type(str, KLT_TYPES)) { ++ return (1); ++ } ++ return(0); ++} ++ ++/* ++ * free_tokens() ++ */ ++static void ++free_tokens(token_t *tp) ++{ ++ token_t *t, *tnext; ++ ++ t = tp; ++ while (t) { ++ tnext = t->next; ++ if (t->string) { ++ kl_free_block((void *)t->string); ++ } ++ kl_free_block((void *)t); ++ t = tnext; ++ } ++} ++ ++/* ++ * process_text() ++ */ ++static int ++process_text(char **str, token_t *tok) ++{ ++ char *cp = *str; ++ char *s = NULL; ++ int len = 0; ++ ++ /* Check and see if this token is a STRING or CHARACTER ++ * type (beginning with a single or double quote). ++ */ ++ if (*cp == '\'') { ++ /* make sure that only a single character is between ++ * the single quotes (it can be an escaped character ++ * too). ++ */ ++ s = strpbrk((cp + 1), "\'"); ++ if (!s) { ++ set_eval_error(E_SINGLE_QUOTE); ++ error_token = tok->ptr; ++ return(1); ++ } ++ len = (uaddr_t)s - (uaddr_t)cp; ++ if ((*(cp+1) == '\\')) { ++ if (*(cp+2) == '0') { ++ long int val; ++ unsigned long uval; ++ char *ep; ++ ++ uval = kl_strtoull((char*)(cp+2), ++ (char **)&ep, 8); ++ val = uval; ++ if ((val > 255) || (*ep != '\'')) { ++ set_eval_error(E_BAD_CHAR); ++ error_token = tok->ptr; ++ return(1); ++ } ++ } else if (*(cp+3) != '\'') { ++ set_eval_error(E_BAD_CHAR); ++ error_token = tok->ptr; ++ return(1); ++ } ++ tok->type = CHARACTER; ++ } else if (len == 2) { ++ tok->type = CHARACTER; ++ } else { ++ ++ /* Treat as a single token entry. It's possible ++ * that what's between the single quotes is a ++ * type name. That will be determined later on. ++ */ ++ tok->type = STRING; ++ } ++ *str = cp + len; ++ } else if (*cp == '\"') { ++ s = strpbrk((cp + 1), "\""); ++ if (!s) { ++ set_eval_error(E_BAD_STRING); ++ error_token = tok->ptr; ++ return(1); ++ } ++ len = (uaddr_t)s - (uaddr_t)cp; ++ tok->type = TEXT; ++ *str = cp + len; ++ } ++ if ((tok->type == STRING) || (tok->type == TEXT)) { ++ ++ if ((tok->type == TEXT) && (strlen(cp) > (len + 1))) { ++ ++ /* Check to see if there is a comma or semi-colon ++ * directly following the string. If there is, ++ * then the string is OK (the following characters ++ * are part of the next expression). Also, it's OK ++ * to have trailing blanks as long as that's all ++ * threre is. ++ */ ++ char *c; ++ ++ c = s + 1; ++ while (*c) { ++ if ((*c == ',') || (*c == ';')) { ++ break; ++ } else if (*c != ' ') { ++ set_eval_error(E_END_EXPECTED); ++ tok->ptr = c; ++ error_token = tok->ptr; ++ return(1); ++ } ++ c++; ++ } ++ /* Truncate the trailing blanks (they are not ++ * part of the string). ++ */ ++ if (c != (s + 1)) { ++ *(s + 1) = 0; ++ } ++ } ++ tok->string = (char *)kl_alloc_block(len); ++ memcpy(tok->string, (cp + 1), len - 1); ++ tok->string[len - 1] = 0; ++ } ++ return(0); ++} ++ ++/* ++ * get_token_list() ++ */ ++static token_t * ++get_token_list(char *str) ++{ ++ int paren_count = 0; ++ char *cp; ++ token_t *tok = (token_t*)NULL, *tok_head = (token_t*)NULL; ++ token_t *tok_last = (token_t*)NULL; ++ ++ cp = str; ++ eval_error = 0; ++ ++ while (*cp) { ++ ++ /* Skip past any "white space" (spaces and tabs). ++ */ ++ switch (*cp) { ++ case ' ' : ++ case '\t' : ++ case '`' : ++ cp++; ++ continue; ++ default : ++ break; ++ } ++ ++ /* Allocate space for the next token */ ++ tok = (token_t *)kl_alloc_block(sizeof(token_t)); ++ tok->ptr = cp; ++ ++ switch(*cp) { ++ ++ /* Check for operators ++ */ ++ case '+' : ++ if (*((char*)cp + 1) == '+') { ++ ++ /* We aren't doing asignment here, ++ * so the ++ operator is not ++ * considered valid. ++ */ ++ set_eval_error(E_BAD_OPERATOR); ++ error_token = tok_last->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return ((token_t*)NULL); ++ } else if (!tok_last || ++ (tok_last->operator && ++ (tok_last->operator != CLOSE_PAREN))) { ++ tok->operator = UNARY_PLUS; ++ } else { ++ tok->operator = ADD; ++ } ++ break; ++ ++ case '-' : ++ if (*((char*)cp + 1) == '-') { ++ ++ /* We aren't doing asignment here, so ++ * the -- operator is not considered ++ * valid. ++ */ ++ set_eval_error(E_BAD_OPERATOR); ++ error_token = tok_last->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return ((token_t*)NULL); ++ } else if (*((char*)cp + 1) == '>') { ++ tok->operator = RIGHT_ARROW; ++ cp++; ++ } else if (!tok_last || (tok_last->operator && ++ (tok_last->operator != CLOSE_PAREN))) { ++ tok->operator = UNARY_MINUS; ++ } else { ++ tok->operator = SUBTRACT; ++ } ++ break; ++ ++ case '.' : ++ /* XXX - need to check to see if this is a ++ * decimal point in the middle fo a floating ++ * point value. ++ */ ++ tok->operator = DOT; ++ break; ++ ++ case '*' : ++ /* XXX - need a better way to tell if this is ++ * an INDIRECTION. perhaps check the next ++ * token? ++ */ ++ if (!tok_last || (tok_last->operator && ++ ((tok_last->operator != CLOSE_PAREN) && ++ (tok_last->operator != CAST)))) { ++ tok->operator = INDIRECTION; ++ } else { ++ tok->operator = MULTIPLY; ++ } ++ break; ++ ++ case '/' : ++ tok->operator = DIVIDE; ++ break; ++ ++ case '%' : ++ tok->operator = MODULUS; ++ break; ++ ++ case '(' : { ++ char *s, *s1, *s2; ++ int len; ++ ++ /* Make sure the previous token is an operator ++ */ ++ if (tok_last && !tok_last->operator) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = tok_last->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return ((token_t*)NULL); ++ } ++ ++ if (tok_last && ++ ((tok_last->operator == RIGHT_ARROW) || ++ (tok_last->operator == DOT))) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = tok_last->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return ((token_t*)NULL); ++ } ++ ++ /* Check here to see if following tokens ++ * constitute a cast. ++ */ ++ ++ /* Skip past any "white space" (spaces ++ * and tabs) ++ */ ++ while ((*(cp+1) == ' ') || (*(cp+1) == '\t')) { ++ cp++; ++ } ++ if ((*(cp+1) == '(') || isdigit(*(cp+1)) || ++ (*(cp+1) == '+') || (*(cp+1) == '-') || ++ (*(cp+1) == '*') || (*(cp+1) == '&') || ++ (*(cp+1) == ')')){ ++ tok->operator = OPEN_PAREN; ++ paren_count++; ++ break; ++ } ++ ++ /* Make sure we have a CLOSE_PAREN. ++ */ ++ if (!(s1 = strchr(cp+1, ')'))) { ++ set_eval_error(E_OPEN_PAREN); ++ error_token = tok->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return ((token_t*)NULL); ++ } ++ /* Check to see if this is NOT a simple ++ * typecast. ++ */ ++ if (!(s2 = strchr(cp+1, '.'))) { ++ s2 = strstr(cp+1, "->"); ++ } ++ if (s2 && (s2 < s1)) { ++ tok->operator = OPEN_PAREN; ++ paren_count++; ++ break; ++ } ++ ++ if ((s = strpbrk(cp+1, "*)"))) { ++ char str[128]; ++ ++ len = (uaddr_t)s - (uaddr_t)(cp+1); ++ strncpy(str, cp+1, len); ++ str[len] = 0; ++ if (!is_typestr(str)) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = tok->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return ((token_t*)NULL); ++ } ++ if (!(s = strpbrk((cp+1), ")"))) { ++ set_eval_error(E_OPEN_PAREN); ++ error_token = tok->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return ((token_t*)NULL); ++ } ++ len = (uaddr_t)s - (uaddr_t)(cp+1); ++ tok->string = (char *) ++ kl_alloc_block(len + 1); ++ memcpy(tok->string, (cp+1), len); ++ tok->string[len] = 0; ++ tok->operator = CAST; ++ cp = (char *)((uaddr_t)(cp+1) + len); ++ break; ++ } ++ tok->operator = OPEN_PAREN; ++ paren_count++; ++ break; ++ } ++ ++ case ')' : ++ if (tok_last && ((tok_last->operator == ++ RIGHT_ARROW) || ++ (tok_last->operator == DOT))) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = tok_last->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return ((token_t*)NULL); ++ } ++ tok->operator = CLOSE_PAREN; ++ paren_count--; ++ break; ++ ++ case '&' : ++ if (*((char*)cp + 1) == '&') { ++ tok->operator = LOGICAL_AND; ++ cp++; ++ } else if (!tok_last || (tok_last && ++ (tok_last->operator && ++ tok_last->operator != ++ CLOSE_PAREN))) { ++ tok->operator = ADDRESS; ++ } else { ++ tok->operator = BITWISE_AND; ++ } ++ break; ++ ++ case '|' : ++ if (*((char*)cp + 1) == '|') { ++ tok->operator = LOGICAL_OR; ++ cp++; ++ } else { ++ tok->operator = BITWISE_OR; ++ } ++ break; ++ ++ case '=' : ++ if (*((char*)cp + 1) == '=') { ++ tok->operator = EQUAL; ++ cp++; ++ } else { ++ /* ASIGNMENT -- NOT IMPLEMENTED ++ */ ++ tok->operator = NOT_YET; ++ } ++ break; ++ ++ case '<' : ++ if (*((char*)cp + 1) == '<') { ++ tok->operator = LEFT_SHIFT; ++ cp++; ++ } else if (*((char*)cp + 1) == '=') { ++ tok->operator = LESS_THAN_OR_EQUAL; ++ cp++; ++ } else { ++ tok->operator = LESS_THAN; ++ } ++ break; ++ ++ case '>' : ++ if (*((char*)(cp + 1)) == '>') { ++ tok->operator = RIGHT_SHIFT; ++ cp++; ++ } else if (*((char*)cp + 1) == '=') { ++ tok->operator = GREATER_THAN_OR_EQUAL; ++ cp++; ++ } else { ++ tok->operator = GREATER_THAN; ++ } ++ break; ++ ++ case '!' : ++ if (*((char*)cp + 1) == '=') { ++ tok->operator = NOT_EQUAL; ++ cp++; ++ } else { ++ tok->operator = LOGICAL_NEGATION; ++ } ++ break; ++ ++ case '$' : ++ set_eval_error(E_NOT_IMPLEMENTED); ++ error_token = tok->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return((token_t*)NULL); ++ case '~' : ++ tok->operator = ONES_COMPLEMENT; ++ break; ++ ++ case '^' : ++ tok->operator = BITWISE_EXCLUSIVE_OR; ++ break; ++ ++ case '?' : ++ set_eval_error(E_NOT_IMPLEMENTED); ++ error_token = tok->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return((token_t*)NULL); ++ case ':' : ++ set_eval_error(E_NOT_IMPLEMENTED); ++ error_token = tok->ptr; ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return((token_t*)NULL); ++ case '[' : ++ tok->operator = OPEN_SQUARE_BRACKET;; ++ break; ++ ++ case ']' : ++ tok->operator = CLOSE_SQUARE_BRACKET;; ++ break; ++ ++ default: { ++ ++ char *s; ++ int len; ++ ++ /* See if the last token is a RIGHT_ARROW ++ * or a DOT. If it is, then this token must ++ * be the name of a struct/union member. ++ */ ++ if (tok_last && ++ ((tok_last->operator == RIGHT_ARROW) || ++ (tok_last->operator == DOT))) { ++ tok->type = MEMBER; ++ } else if (process_text(&cp, tok)) { ++ free_tokens(tok_head); ++ free_tokens(tok); ++ return((token_t*)NULL); ++ } ++ if (tok->type == TEXT) { ++ return(tok); ++ } else if (tok->type == STRING) { ++ if (is_typestr(tok->string)) { ++ tok->type = TYPE_DEF; ++ } else { ++ tok->operator = TEXT; ++ return(tok); ++ } ++ break; ++ } else if (tok->type == CHARACTER) { ++ break; ++ } ++ ++ /* Check and See if the entire string is ++ * a typename (valid only for whatis case). ++ */ ++ s = strpbrk(cp, ++ ".\t+-*/()[]|~!$&%^<>?:&=^\"\'"); ++ if (!s && !tok->type && is_typestr(cp)) { ++ tok->type = TYPE_DEF; ++ len = strlen(cp) + 1; ++ tok->string = (char *) ++ kl_alloc_block(len); ++ memcpy(tok->string, cp, len - 1); ++ tok->string[len - 1] = 0; ++ cp = (char *)((uaddr_t)cp + len - 2); ++ break; ++ } ++ ++ /* Now check for everything else ++ */ ++ if ((s = strpbrk(cp, ++ " .\t+-*/()[]|~!$&%^<>?:&=^\"\'"))) { ++ len = (uaddr_t)s - (uaddr_t)cp + 1; ++ } else { ++ len = strlen(cp) + 1; ++ } ++ ++ tok->string = ++ (char *)kl_alloc_block(len); ++ memcpy(tok->string, cp, len - 1); ++ tok->string[len - 1] = 0; ++ ++ cp = (char *)((uaddr_t)cp + len - 2); ++ ++ /* Check to see if this is the keyword ++ * "sizeof". If not, then check to see if ++ * the string is a member name. ++ */ ++ if (!strcmp(tok->string, "sizeof")) { ++ tok->operator = SIZEOF; ++ kl_free_block((void *)tok->string); ++ tok->string = 0; ++ } else if (tok_last && ++ ((tok_last->operator == RIGHT_ARROW) || ++ (tok_last->operator == DOT))) { ++ tok->type = MEMBER; ++ } else { ++ tok->type = STRING; ++ } ++ break; ++ } ++ } ++ if (!(tok->type)) { ++ tok->type = OPERATOR; ++ } ++ if (!tok_head) { ++ tok_head = tok_last = tok; ++ } else { ++ tok_last->next = tok; ++ tok_last = tok; ++ } ++ cp++; ++ } ++ if (paren_count < 0) { ++ set_eval_error(E_CLOSE_PAREN); ++ error_token = tok->ptr; ++ free_tokens(tok_head); ++ return((token_t*)NULL); ++ } else if (paren_count > 0) { ++ set_eval_error(E_OPEN_PAREN); ++ error_token = tok->ptr; ++ free_tokens(tok_head); ++ return((token_t*)NULL); ++ } ++ return(tok_head); ++} ++ ++/* ++ * valid_binary_args() ++ */ ++int ++valid_binary_args(node_t *np, node_t *left, node_t *right) ++{ ++ int op = np->operator; ++ ++ if ((op == RIGHT_ARROW) || (op == DOT)) { ++ if (!left) { ++ set_eval_error(E_MISSING_STRUCTURE); ++ error_token = np->tok_ptr; ++ return(0); ++ } else if (!(left->node_type == TYPE_DEF) && ++ !(left->node_type == MEMBER) && ++ !(left->operator == CLOSE_PAREN) && ++ !(left->operator == CLOSE_SQUARE_BRACKET)) { ++ set_eval_error(E_BAD_STRUCTURE); ++ error_token = left->tok_ptr; ++ return(0); ++ } ++ if (!right || (!(right->node_type == MEMBER))) { ++ set_eval_error(E_BAD_MEMBER); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ return(1); ++ } ++ if (!left || !right) { ++ set_eval_error(E_MISSING_OPERAND); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ switch (left->operator) { ++ case CLOSE_PAREN: ++ case CLOSE_SQUARE_BRACKET: ++ break; ++ default: ++ switch(left->node_type) { ++ case NUMBER: ++ case STRING: ++ case TEXT: ++ case CHARACTER: ++ case EVAL_VAR: ++ case MEMBER: ++ break; ++ default: ++ set_eval_error(E_BAD_OPERAND); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ } ++ switch (right->operator) { ++ case OPEN_PAREN: ++ break; ++ default: ++ switch(right->node_type) { ++ case NUMBER: ++ case STRING: ++ case TEXT: ++ case CHARACTER: ++ case EVAL_VAR: ++ case MEMBER: ++ break; ++ default: ++ set_eval_error(E_BAD_OPERAND); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ } ++ return(1); ++} ++ ++/* ++ * get_node_list() ++ */ ++static node_t * ++get_node_list(token_t *tp, int flags) ++{ ++ node_t *root = (node_t *)NULL; ++ node_t *np = (node_t *)NULL; ++ node_t *last = (node_t *)NULL; ++ ++ /* Loop through the tokens and convert them to nodes. ++ */ ++ while (tp) { ++ np = make_node(tp, flags); ++ if (eval_error) { ++ return((node_t *)NULL); ++ } ++ if (root) { ++ last->next = np; ++ last = np; ++ } else { ++ root = last = np; ++ } ++ tp = tp->next; ++ } ++ last->next = (node_t *)NULL; /* cpw patch */ ++ last = (node_t *)NULL; ++ for (np = root; np; np = np->next) { ++ if (is_binary(np->operator)) { ++ if (!valid_binary_args(np, last, np->next)) { ++ free_nodelist(root); ++ return((node_t *)NULL); ++ } ++ } ++ last = np; ++ } ++ return(root); ++} ++ ++/* ++ * next_node() ++ */ ++static node_t * ++next_node(void) ++{ ++ node_t *np; ++ if ((np = node_list)) { ++ node_list = node_list->next; ++ np->next = (node_t*)NULL; ++ } ++ return(np); ++} ++ ++/* ++ * eval_unary() ++ */ ++static node_t * ++eval_unary(node_t *curnp, int flags) ++{ ++ node_t *n0, *n1; ++ ++ n0 = curnp; ++ ++ /* Peek ahead and make sure there is a next node. ++ * Also check to see if the next node requires ++ * a recursive call to do_eval(). If it does, we'll ++ * let the do_eval() call take care of pulling it ++ * off the list. ++ */ ++ if (!node_list) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = n0->tok_ptr; ++ free_nodes(n0); ++ return((node_t*)NULL); ++ } ++ if (n0->operator == CAST) { ++ if (node_list->operator == CLOSE_PAREN) { ++ ++ /* Free the CLOSE_PAREN and return ++ */ ++ free_node(next_node()); ++ return(n0); ++ } ++ if (!(node_list->node_type == NUMBER) && ++ !(node_list->node_type == VADDR) && ++ !((node_list->operator == ADDRESS) || ++ (node_list->operator == CAST) || ++ (node_list->operator == UNARY_MINUS) || ++ (node_list->operator == UNARY_PLUS) || ++ (node_list->operator == INDIRECTION) || ++ (node_list->operator == OPEN_PAREN))) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = node_list->tok_ptr; ++ free_nodes(n0); ++ return((node_t*)NULL); ++ } ++ } ++ if ((n0->operator == INDIRECTION) || ++ (n0->operator == ADDRESS) || ++ (n0->operator == OPEN_PAREN) || ++ is_unary(node_list->operator)) { ++ n1 = do_eval(flags); ++ if (eval_error) { ++ free_nodes(n0); ++ free_nodes(n1); ++ return((node_t*)NULL); ++ } ++ } else { ++ n1 = next_node(); ++ } ++ ++ if (n1->operator == OPEN_PAREN) { ++ /* Get the value contained within the parenthesis. ++ * If there was an error, just return. ++ */ ++ free_node(n1); ++ n1 = do_eval(flags); ++ if (eval_error) { ++ free_nodes(n1); ++ free_nodes(n0); ++ return((node_t*)NULL); ++ } ++ } ++ ++ n0->right = n1; ++ if (replace_unary(n0, flags) == -1) { ++ if (!eval_error) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = n0->tok_ptr; ++ } ++ free_nodes(n0); ++ return((node_t*)NULL); ++ } ++ return(n0); ++} ++ ++/* ++ * do_eval() -- Reduces an equation to a single value. ++ * ++ * Any parenthesis (and nested parenthesis) within the equation will ++ * be solved first via recursive calls to do_eval(). ++ */ ++static node_t * ++do_eval(int flags) ++{ ++ node_t *root = (node_t*)NULL, *curnp, *n0, *n1; ++ ++ /* Loop through the list of nodes until we run out of nodes ++ * or we hit a CLOSE_PAREN. If we hit an OPEN_PAREN, make a ++ * recursive call to do_eval(). ++ */ ++ curnp = next_node(); ++ while (curnp) { ++ n0 = n1 = (node_t *)NULL; ++ ++ if (curnp->operator == OPEN_PAREN) { ++ /* Get the value contained within the parenthesis. ++ * If there was an error, just return. ++ */ ++ free_node(curnp); ++ n0 = do_eval(flags); ++ if (eval_error) { ++ free_nodes(n0); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ ++ } else if (curnp->operator == SIZEOF) { ++ /* Free the SIZEOF node and then make a call ++ * to the get_sizeof() function (which will ++ * get the next node off the list). ++ */ ++ n0 = get_sizeof(); ++ if (eval_error) { ++ if (!error_token) { ++ error_token = curnp->tok_ptr; ++ } ++ free_node(curnp); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ free_node(curnp); ++ curnp = (node_t *)NULL; ++ } else if (is_unary(curnp->operator)) { ++ n0 = eval_unary(curnp, flags); ++ } else { ++ n0 = curnp; ++ curnp = (node_t *)NULL; ++ } ++ if (eval_error) { ++ free_nodes(n0); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ ++ /* n0 should now contain a non-operator node. Check to see if ++ * there is a next token. If there isn't, just add the last ++ * rchild and return. ++ */ ++ if (!node_list) { ++ if (root) { ++ add_rchild(root, n0); ++ } else { ++ root = n0; ++ } ++ replace(root, flags); ++ if (eval_error) { ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ return(root); ++ } ++ ++ /* Make sure the next token is an operator. ++ */ ++ if (!node_list->operator) { ++ free_nodes(root); ++ free_node(n0); ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = node_list->tok_ptr; ++ return((node_t *)NULL); ++ } else if ((node_list->operator == CLOSE_PAREN) || ++ (node_list->operator == CLOSE_SQUARE_BRACKET)) { ++ ++ if (root) { ++ add_rchild(root, n0); ++ } else { ++ root = n0; ++ } ++ ++ /* Reduce the resulting tree to a single value ++ */ ++ replace(root, flags); ++ if (eval_error) { ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ ++ /* Step over the CLOSE_PAREN or CLOSE_SQUARE_BRACKET ++ * and then return. ++ */ ++ free_node(next_node()); ++ return(root); ++ } else if (node_list->operator == OPEN_SQUARE_BRACKET) { ++next_dimension1: ++ /* skip over the OPEN_SQUARE_BRACKET token ++ */ ++ free_node(next_node()); ++ ++ /* Get the value contained within the brackets. This ++ * value must represent an array index (value or ++ * equation). ++ */ ++ n1 = do_eval(0); ++ if (eval_error) { ++ free_nodes(root); ++ free_node(n0); ++ free_node(n1); ++ return((node_t *)NULL); ++ } ++ ++ /* Convert the array (or pointer type) to an ++ * element type using the index value obtained ++ * above. Make sure that n0 contains some sort ++ * of type definition first, however. ++ */ ++ if (n0->node_type != TYPE_DEF) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n0->tok_ptr; ++ free_nodes(n0); ++ free_nodes(n1); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ array_to_element(n0, n1); ++ free_node(n1); ++ if (eval_error) { ++ free_nodes(root); ++ free_nodes(n0); ++ return((node_t *)NULL); ++ } ++ ++ /* If there aren't any more nodes, just ++ * return. ++ */ ++ if (!node_list) { ++ return(n0); ++ } ++ if (node_list->operator == OPEN_SQUARE_BRACKET) { ++ goto next_dimension1; ++ } ++ } else if (!is_binary(node_list->operator)) { ++ set_eval_error(E_BAD_OPERATOR); ++ error_token = node_list->tok_ptr; ++ free_nodes(root); ++ free_nodes(n0); ++ return((node_t *)NULL); ++ } ++ ++ /* Now get the operator node ++ */ ++ if (!(n1 = next_node())) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = n0->tok_ptr; ++ free_nodes(n0); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ ++ /* Check to see if this binary operator is RIGHT_ARROW or DOT. ++ * If it is, we need to reduce it to a single value node now. ++ */ ++ while ((n1->operator == RIGHT_ARROW) || (n1->operator == DOT)) { ++ ++ /* The next node must contain the name of the ++ * struct|union member. ++ */ ++ if (!node_list || (node_list->node_type != MEMBER)) { ++ set_eval_error(E_BAD_MEMBER); ++ error_token = n1->tok_ptr; ++ free_nodes(n0); ++ free_nodes(n1); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ n1->left = n0; ++ ++ /* Now get the next node and link it as the ++ * right child. ++ */ ++ if (!(n0 = next_node())) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = n1->tok_ptr; ++ free_nodes(n1); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ n1->right = n0; ++ if (!(n0 = replace(n1, flags))) { ++ if (!(eval_error)) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = n1->tok_ptr; ++ } ++ free_nodes(n1); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ n1 = (node_t *)NULL; ++ ++ /* Check to see if there is a next node. If there ++ * is, check to see if it is the operator CLOSE_PAREN. ++ * If it is, then return (skipping over the ++ * CLOSE_PAREN first). ++ */ ++ if (node_list && ((node_list->operator == CLOSE_PAREN) ++ || (node_list->operator == ++ CLOSE_SQUARE_BRACKET))) { ++ if (root) { ++ add_rchild(root, n0); ++ } else { ++ root = n0; ++ } ++ ++ /* Reduce the resulting tree to a single ++ * value ++ */ ++ replace(root, flags); ++ if (eval_error) { ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ ++ /* Advance the token pointer past the ++ * CLOSE_PAREN and then return. ++ */ ++ free_node(next_node()); ++ return(root); ++ } ++ ++ /* Check to see if the next node is an ++ * OPEN_SQUARE_BRACKET. If it is, then we have to ++ * reduce the contents of the square brackets to ++ * an index array. ++ */ ++ if (node_list && (node_list->operator ++ == OPEN_SQUARE_BRACKET)) { ++ ++ /* Advance the token pointer and call ++ * do_eval() again. ++ */ ++ free_node(next_node()); ++next_dimension2: ++ n1 = do_eval(0); ++ if (eval_error) { ++ free_node(n0); ++ free_node(n1); ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ ++ /* Convert the array (or pointer type) to ++ * an element type using the index value ++ * obtained above. Make sure that n0 ++ * contains some sort of type definition ++ * first, however. ++ */ ++ if (n0->node_type != TYPE_DEF) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n0->tok_ptr; ++ free_node(n0); ++ free_node(n1); ++ free_node(root); ++ return((node_t *)NULL); ++ } ++ array_to_element(n0, n1); ++ free_node(n1); ++ if (eval_error) { ++ free_node(n0); ++ free_node(root); ++ return((node_t *)NULL); ++ } ++ } ++ ++ /* Now get the next operator node (if there is one). ++ */ ++ if (!node_list) { ++ if (root) { ++ add_rchild(root, n0); ++ } else { ++ root = n0; ++ } ++ return(root); ++ } ++ n1 = next_node(); ++ if (n1->operator == OPEN_SQUARE_BRACKET) { ++ goto next_dimension2; ++ } ++ } ++ ++ if (n1 && ((n1->operator == CLOSE_PAREN) || ++ (n1->operator == CLOSE_SQUARE_BRACKET))) { ++ free_node(n1); ++ if (root) { ++ add_rchild(root, n0); ++ } else { ++ root = n0; ++ } ++ replace(root, flags); ++ if (eval_error) { ++ free_nodes(root); ++ return((node_t *)NULL); ++ } ++ return(root); ++ } ++ ++ if (!root) { ++ root = n1; ++ n1->left = n0; ++ } else if (precedence(root->operator) ++ >= precedence(n1->operator)) { ++ add_rchild(root, n0); ++ n1->left = root; ++ root = n1; ++ } else { ++ if (!root->right) { ++ n1->left = n0; ++ root->right = n1; ++ } else { ++ add_rchild(root, n0); ++ n1->left = root->right; ++ root->right = n1; ++ } ++ } ++ curnp = next_node(); ++ } /* while(curnp) */ ++ return(root); ++} ++ ++/* ++ * is_unary() ++ */ ++static int ++is_unary(int op) ++{ ++ switch (op) { ++ case LOGICAL_NEGATION : ++ case ADDRESS : ++ case INDIRECTION : ++ case UNARY_MINUS : ++ case UNARY_PLUS : ++ case ONES_COMPLEMENT : ++ case CAST : ++ return(1); ++ ++ default : ++ return(0); ++ } ++} ++ ++ ++/* ++ * is_binary() ++ */ ++static int ++is_binary(int op) ++{ ++ switch (op) { ++ ++ case BITWISE_OR : ++ case BITWISE_EXCLUSIVE_OR : ++ case BITWISE_AND : ++ case RIGHT_SHIFT : ++ case LEFT_SHIFT : ++ case ADD : ++ case SUBTRACT : ++ case MULTIPLY : ++ case DIVIDE : ++ case MODULUS : ++ case LOGICAL_OR : ++ case LOGICAL_AND : ++ case EQUAL : ++ case NOT_EQUAL : ++ case LESS_THAN : ++ case GREATER_THAN : ++ case LESS_THAN_OR_EQUAL : ++ case GREATER_THAN_OR_EQUAL : ++ case RIGHT_ARROW : ++ case DOT : ++ return(1); ++ ++ default : ++ return(0); ++ } ++} ++ ++/* ++ * precedence() ++ */ ++static int ++precedence(int a) ++{ ++ if ((a >= CONDITIONAL) && (a <= CONDITIONAL_ELSE)) { ++ return(1); ++ } else if (a == LOGICAL_OR) { ++ return(2); ++ } else if (a == LOGICAL_AND) { ++ return(3); ++ } else if (a == BITWISE_OR) { ++ return(4); ++ } else if (a == BITWISE_EXCLUSIVE_OR) { ++ return(5); ++ } else if (a == BITWISE_AND) { ++ return(6); ++ } else if ((a >= EQUAL) && (a <= NOT_EQUAL)) { ++ return(7); ++ } else if ((a >= LESS_THAN) && (a <= GREATER_THAN_OR_EQUAL)) { ++ return(8); ++ } else if ((a >= RIGHT_SHIFT) && (a <= LEFT_SHIFT)) { ++ return(9); ++ } else if ((a >= ADD) && (a <= SUBTRACT)) { ++ return(10); ++ } else if ((a >= MULTIPLY) && (a <= MODULUS)) { ++ return(11); ++ } else if ((a >= LOGICAL_NEGATION) && (a <= SIZEOF)) { ++ return(12); ++ } else if ((a >= RIGHT_ARROW) && (a <= DOT)) { ++ return(13); ++ } else { ++ return(0); ++ } ++} ++ ++/* ++ * esc_char() ++ */ ++char ++esc_char(char *str) ++{ ++ long int val; ++ unsigned long uval; ++ char ch; ++ ++ if (strlen(str) > 1) { ++ uval = kl_strtoull(str, (char **)NULL, 8); ++ val = uval; ++ ch = (char)val; ++ } else { ++ ch = str[0]; ++ } ++ switch (ch) { ++ case 'a' : ++ return((char)7); ++ case 'b' : ++ return((char)8); ++ case 't' : ++ return((char)9); ++ case 'n' : ++ return((char)10); ++ case 'f' : ++ return((char)12); ++ case 'r' : ++ return((char)13); ++ case 'e' : ++ return((char)27); ++ default: ++ return(ch); ++ } ++} ++ ++/* ++ * make_node() ++ */ ++static node_t * ++make_node(token_t *t, int flags) ++{ ++ node_t *np; ++ ++ set_eval_error(0); ++ np = (node_t*)kl_alloc_block(sizeof(*np)); ++ ++ if (t->type == OPERATOR) { ++ ++ /* Check to see if this token represents a typecast ++ */ ++ if (t->operator == CAST) { ++ type_t *tp; ++ ++ if (!(np->type = get_type(t->string, flags))) { ++ set_eval_error(E_BAD_CAST); ++ error_token = t->ptr; ++ free_nodes(np); ++ return((node_t*)NULL); ++ } ++ ++ /* Determin if this is a pointer to a type ++ */ ++ tp = np->type; ++ if (tp->flag == POINTER_FLAG) { ++ np->flags = POINTER_FLAG; ++ tp = tp->t_next; ++ while (tp->flag == POINTER_FLAG) { ++ tp = tp->t_next; ++ } ++ } ++ switch(tp->flag) { ++ case KLTYPE_FLAG: ++ np->flags |= KLTYPE_FLAG; ++ break; ++ ++ default: ++ free_nodes(np); ++ set_eval_error(E_BAD_CAST); ++ error_token = t->ptr; ++ return((node_t*)NULL); ++ } ++ if (!t->next) { ++ if (flags & C_WHATIS) { ++ np->node_type = TYPE_DEF; ++ } else { ++ set_eval_error(E_BAD_CAST); ++ error_token = t->ptr; ++ return((node_t*)NULL); ++ } ++ } else { ++ np->node_type = OPERATOR; ++ np->operator = CAST; ++ } ++ } else { ++ np->node_type = OPERATOR; ++ np->operator = t->operator; ++ } ++ } else if (t->type == MEMBER) { ++ np->name = (char *)dup_block((void *)t->string, strlen(t->string)+1); ++ np->node_type = MEMBER; ++ } else if ((t->type == STRING) || (t->type == TYPE_DEF)) { ++ syment_t *sp; ++ dbg_sym_t *stp; ++ dbg_type_t *sttp; ++ ++ if ((sp = kl_lkup_symname(t->string))) { ++ if (!(flags & C_NOVARS)) { ++ int has_type = 0; ++ ++ /* The string is a symbol name. We'll treat it as ++ * a global kernel variable and, at least, gather in ++ * the address of the symbol and the value it points ++ * to. ++ */ ++ np->address = sp->s_addr; ++ np->flags |= ADDRESS_FLAG; ++ np->name = t->string; ++ t->string = (char*)NULL; ++ ++ /* Need to see if there is type information available ++ * for this variable. Since this mapping is not ++ * available yet, we will just attach a type struct ++ * for either uint32_t or uint64_t (depending on the ++ * size of a kernel pointer). That will at least let ++ * us do something and will prevent the scenario where ++ * we have a type node with out a pointer to a type ++ * struct! ++ */ ++ np->node_type = TYPE_DEF; ++ np->flags |= KLTYPE_FLAG; ++ np->value = *((kaddr_t *)np->address); ++ /* try to get the actual type info for the variable */ ++ if(((stp = dbg_find_sym(sp->s_name, DBG_VAR, ++ (uint64_t)0)) != NULL)){ ++ if((sttp = (dbg_type_t *) ++ kl_find_typenum(stp->sym_typenum)) ++ != NULL){ ++ /* kl_get_typestring(sttp); */ ++ has_type = 1; ++ if(sttp->st_klt.kl_type == KLT_POINTER){ ++ np->flags ^= KLTYPE_FLAG; ++ np->flags |= POINTER_FLAG; ++ np->type = ++ get_type(sttp->st_typestr, ++ flags); ++ } else { ++ np->type = ++ kl_alloc_block(sizeof(type_t)); ++ np->type->un.kltp = ++ &sttp->st_klt; ++ } ++ } ++ } ++ /* no type info for the variable found */ ++ if(!has_type){ ++ if (ptrsz64) { ++ np->type = get_type("uint64_t", flags); ++ } else { ++ np->type = get_type("uint32_t", flags); ++ } ++ } ++ } ++ kl_free_block((void *)sp); ++ } else if (flags & (C_WHATIS|C_SIZEOF)) { ++ ++ kltype_t *kltp; ++ ++ if ((kltp = kl_find_type(t->string, KLT_TYPES))) { ++ ++ np->node_type = TYPE_DEF; ++ np->flags = KLTYPE_FLAG; ++ np->type = (type_t*) ++ kl_alloc_block(sizeof(type_t)); ++ np->type->flag = KLTYPE_FLAG; ++ np->type->t_kltp = kltp; ++ } else { ++ if (get_value(t->string, ++ (uint64_t *)&np->value)) { ++ set_eval_error(E_BAD_VALUE); ++ error_token = t->ptr; ++ free_nodes(np); ++ return((node_t*)NULL); ++ } ++ if (!strncmp(t->string, "0x", 2) || ++ !strncmp(t->string, "0X", 2)) { ++ np->flags |= UNSIGNED_FLAG; ++ } ++ np->node_type = NUMBER; ++ } ++ np->tok_ptr = t->ptr; ++ return(np); ++ } else { ++ if (get_value(t->string, (uint64_t *)&np->value)) { ++ set_eval_error(E_BAD_VALUE); ++ error_token = t->ptr; ++ free_nodes(np); ++ return((node_t*)NULL); ++ } ++ if (np->value > 0xffffffff) { ++ np->byte_size = 8; ++ } else { ++ np->byte_size = 4; ++ } ++ if (!strncmp(t->string, "0x", 2) || ++ !strncmp(t->string, "0X", 2)) { ++ np->flags |= UNSIGNED_FLAG; ++ } ++ np->node_type = NUMBER; ++ } ++ } else if (t->type == CHARACTER) { ++ char *cp; ++ ++ /* Step over the single quote ++ */ ++ cp = (t->ptr + 1); ++ if (*cp == '\\') { ++ int i = 0; ++ char str[16]; ++ ++ /* Step over the back slash ++ */ ++ cp++; ++ while (*cp != '\'') { ++ str[i++] = *cp++; ++ } ++ str[i] = 0; ++ np->value = esc_char(str); ++ } else { ++ np->value = *cp; ++ } ++ np->type = get_type("char", flags); ++ np->node_type = TYPE_DEF; ++ np->flags |= KLTYPE_FLAG; ++ } else if (t->type == TEXT) { ++ np->node_type = TEXT; ++ np->name = t->string; ++ /* So the block doesn't get freed twice */ ++ t->string = (char*)NULL; ++ } else { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = t->ptr; ++ return((node_t*)NULL); ++ } ++ np->tok_ptr = t->ptr; ++ return(np); ++} ++ ++/* ++ * add_node() ++ */ ++static int ++add_node(node_t *root, node_t *new_node) ++{ ++ node_t *n = root; ++ ++ /* Find the most lower-right node ++ */ ++ while (n->right) { ++ n = n->right; ++ } ++ ++ /* If the node we found is a leaf node, return an error (we will ++ * have to insert the node instead). ++ */ ++ if (n->node_type == NUMBER) { ++ return(-1); ++ } else { ++ n->right = new_node; ++ } ++ return(0); ++} ++ ++/* ++ * add_rchild() ++ */ ++static int ++add_rchild(node_t *root, node_t *new_node) ++{ ++ if (add_node(root, new_node) == -1) { ++ return(-1); ++ } ++ return(0); ++} ++ ++/* ++ * free_type() ++ */ ++static void ++free_type(type_t *head) ++{ ++ type_t *t0, *t1; ++ ++ t0 = head; ++ while(t0) { ++ if (t0->flag == POINTER_FLAG) { ++ t1 = t0->t_next; ++ kl_free_block((void *)t0); ++ t0 = t1; ++ } else { ++ if (t0->flag != KLTYPE_FLAG) { ++ kl_free_block((void *)t0->t_kltp); ++ } ++ kl_free_block((void *)t0); ++ t0 = (type_t *)NULL; ++ } ++ } ++ return; ++} ++ ++/* ++ * get_type() -- Convert a typecast string into a type. ++ * ++ * Returns a pointer to a struct containing type information. ++ * The type of struct returned is indicated by the contents ++ * of type. If the typecast contains an asterisk, set ptr_type ++ * equal to one, otherwise set it equal to zero. ++ */ ++static type_t * ++get_type(char *s, int flags) ++{ ++ int len, type = 0; ++ char *cp, typename[128]; ++ type_t *t, *head, *last; ++ kltype_t *kltp; ++ ++ head = last = (type_t *)NULL; ++ ++ /* Get the type string ++ */ ++ if (!strncmp(s, "struct", 6)) { ++ if ((cp = strpbrk(s + 7, " \t*"))) { ++ len = cp - (s + 7); ++ } else { ++ len = strlen(s + 7); ++ } ++ memcpy(typename, s + 7, len); ++ } else if (!strncmp(s, "union", 5)) { ++ if ((cp = strpbrk(s + 6, " \t*"))) { ++ len = cp - (s + 6); ++ } else { ++ len = strlen(s + 6); ++ } ++ memcpy(typename, s + 6, len); ++ } else { ++ if ((cp = strpbrk(s, "*)"))) { ++ len = cp - s; ++ } else { ++ len = strlen(s); ++ } ++ memcpy(typename, s, len); ++ } ++ ++ /* Strip off any trailing spaces ++ */ ++ while (len && ((typename[len - 1] == ' ') || ++ (typename[len - 1] == '\t'))) { ++ len--; ++ } ++ typename[len] = 0; ++ ++ if (!(kltp = kl_find_type(typename, KLT_TYPES))) { ++ return ((type_t *)NULL); ++ } ++ type = KLTYPE_FLAG; ++ ++ /* check to see if this cast is a pointer to a type, a pointer ++ * to a pointer to a type, etc. ++ */ ++ cp = s; ++ while ((cp = strpbrk(cp, "*"))) { ++ t = (type_t *)kl_alloc_block(sizeof(type_t)); ++ t->flag = POINTER_FLAG; ++ if (last) { ++ last->t_next = t; ++ last = t; ++ } else { ++ head = last = t; ++ } ++ cp++; ++ } ++ ++ /* Allocate a type block that will point to the type specific ++ * record. ++ */ ++ t = (type_t *)kl_alloc_block(sizeof(type_t)); ++ t->flag = type; ++ ++ switch (t->flag) { ++ ++ case KLTYPE_FLAG: ++ t->t_kltp = kltp; ++ break; ++ ++ default: ++ free_type(head); ++ return((type_t*)NULL); ++ } ++ if (last) { ++ last->t_next = t; ++ } else { ++ head = t; ++ } ++ return(head); ++} ++ ++/* ++ * free_node() ++ */ ++static void ++free_node(node_t *np) ++{ ++ /* If there is nothing to free, just return. ++ */ ++ if (!np) { ++ return; ++ } ++ if (np->name) { ++ kl_free_block((void *)np->name); ++ } ++ free_type(np->type); ++ kl_free_block((void *)np); ++} ++ ++/* ++ * free_nodes() ++ */ ++void ++free_nodes(node_t *np) ++{ ++ node_t *q; ++ ++ /* If there is nothing to free, just return. ++ */ ++ if (!np) { ++ return; ++ } ++ if ((q = np->left)) { ++ free_nodes(q); ++ } ++ if ((q = np->right)) { ++ free_nodes(q); ++ } ++ if (np->name) { ++ kl_free_block((void *)np->name); ++ } ++ free_type(np->type); ++ kl_free_block((void *)np); ++} ++ ++/* ++ * free_nodelist() ++ */ ++static void ++free_nodelist(node_t *np) ++{ ++ node_t *nnp; ++ ++ while(np) { ++ nnp = np->next; ++ free_node(np); ++ np = nnp; ++ } ++} ++ ++extern int alloc_debug; ++ ++/* ++ * free_eval_memory() ++ */ ++void ++free_eval_memory(void) ++{ ++ free_nodelist(node_list); ++ node_list = (node_t*)NULL; ++} ++ ++/* ++ * get_sizeof() ++ */ ++static node_t * ++get_sizeof() ++{ ++ node_t *curnp, *n0 = NULL; ++ ++ if (!(curnp = next_node())) { ++ set_eval_error(E_SYNTAX_ERROR); ++ return((node_t*)NULL); ++ } ++ ++ /* The next token should be a CAST or an open paren. ++ * If it's something else, then return an error. ++ */ ++ if (curnp->operator == OPEN_PAREN) { ++ free_nodes(curnp); ++ n0 = do_eval(C_SIZEOF); ++ if (eval_error) { ++ error_token = n0->tok_ptr; ++ free_nodes(n0); ++ return((node_t*)NULL); ++ } ++ } else if (curnp->operator == CAST) { ++ n0 = curnp; ++ } else { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n0->tok_ptr; ++ free_nodes(n0); ++ return((node_t*)NULL); ++ } ++ ++ if (!n0->type) { ++ set_eval_error(E_NOTYPE); ++ error_token = n0->tok_ptr; ++ free_nodes(n0); ++ return((node_t*)NULL); ++ } ++ ++ if (n0->type->flag & POINTER_FLAG) { ++ n0->value = sizeof(void *); ++ } else if (n0->type->flag & KLTYPE_FLAG) { ++ kltype_t *kltp; ++ ++ kltp = kl_realtype(n0->type->t_kltp, 0); ++ ++ if (kltp->kl_bit_size) { ++ n0->value = kltp->kl_bit_size / 8; ++ if (kltp->kl_bit_size % 8) { ++ n0->value += 1; ++ } ++ } else { ++ n0->value = kltp->kl_size; ++ } ++ } else { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n0->tok_ptr; ++ free_nodes(n0); ++ return((node_t*)NULL); ++ } ++ n0->node_type = NUMBER; ++ n0->flags = 0; ++ n0->operator = 0; ++ n0->byte_size = 0; ++ n0->address = 0; ++ if (n0->type) { ++ free_type(n0->type); ++ n0->type = 0; ++ } ++ return(n0); ++} ++ ++/* ++ * apply_unary() ++ */ ++static int ++apply_unary(node_t *n, uint64_t *value) ++{ ++ if (!n || !n->right) { ++ return(-1); ++ } ++ ++ switch (n->operator) { ++ ++ case UNARY_MINUS : ++ *value = (0 - n->right->value); ++ break; ++ ++ case UNARY_PLUS : ++ *value = (n->right->value); ++ break; ++ ++ case ONES_COMPLEMENT : ++ *value = ~(n->right->value); ++ break; ++ ++ case LOGICAL_NEGATION : ++ if (n->right->value) { ++ *value = 0; ++ } else { ++ *value = 1; ++ } ++ logical_flag++; ++ break; ++ ++ default : ++ break; ++ } ++ return(0); ++} ++ ++/* ++ * pointer_math() ++ */ ++static int ++pointer_math(node_t *np, uint64_t *value, int type, int flags) ++{ ++ int size; ++ uint64_t lvalue, rvalue; ++ type_t *tp = NULL, *tp1; ++ ++ if (type < 0) { ++ if (np->left->flags & POINTER_FLAG) { ++ ++ /* Since we only allow pointer math, ++ * anything other than a pointer causes ++ * failure. ++ */ ++ tp = (type_t*)np->left->type; ++ if (tp->flag != POINTER_FLAG) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = np->left->tok_ptr; ++ return(-1); ++ } ++ ++ tp = tp->t_next; ++ ++ switch (tp->flag) { ++ ++ case POINTER_FLAG : ++ size = sizeof(void *); ++ break; ++ ++ case KLTYPE_FLAG : { ++ /* Get the size of the real type, ++ * not just the size of a pointer ++ * If there isn't any type info, ++ * then just set size equal to the ++ * size of a pointer. ++ */ ++ kltype_t *kltp, *rkltp; ++ ++ kltp = tp->t_kltp; ++ rkltp = kl_realtype(kltp, 0); ++ if (!(size = rkltp->kl_size)) { ++ if (kltp != rkltp) { ++ size = kltp->kl_size; ++ } else { ++ size = sizeof(void *); ++ } ++ } ++ break; ++ } ++ ++ default : ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = np->left->tok_ptr; ++ return(-1); ++ } ++ lvalue = np->left->value; ++ } else { ++ size = sizeof(void *); ++ lvalue = np->left->address; ++ } ++ switch (np->operator) { ++ case ADD : ++ *value = lvalue + (np->right->value * size); ++ break; ++ ++ case SUBTRACT : ++ *value = lvalue - (np->right->value * size); ++ break; ++ ++ default : ++ set_eval_error(E_BAD_OPERATOR); ++ error_token = np->tok_ptr; ++ return(-1); ++ } ++ } else if (type > 0) { ++ if (np->right->flags & POINTER_FLAG) { ++ ++ /* Since we only allow pointer math, ++ * anything other than a pointer causes ++ * failure. ++ */ ++ tp = (type_t*)np->right->type; ++ if (tp->flag != POINTER_FLAG) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = np->right->tok_ptr; ++ return(-1); ++ } ++ ++ tp = tp->t_next; ++ ++ switch (tp->flag) { ++ ++ case POINTER_FLAG : ++ size = sizeof(void *); ++ break; ++ ++ case KLTYPE_FLAG : ++ size = tp->t_kltp->kl_size; ++ break; ++ ++ default : ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = np->right->tok_ptr; ++ return(-1); ++ } ++ rvalue = np->right->value; ++ } else { ++ size = sizeof(void *); ++ rvalue = np->right->address; ++ } ++ switch (np->operator) { ++ case ADD : ++ *value = rvalue + (np->left->value * size); ++ break; ++ ++ case SUBTRACT : ++ *value = rvalue - (np->left->value * size); ++ break; ++ ++ default : ++ set_eval_error(E_BAD_OPERATOR); ++ error_token = np->tok_ptr; ++ return(-1); ++ } ++ } else { ++ return(-1); ++ } ++ tp1 = (type_t *)kl_alloc_block(sizeof(type_t)); ++ tp1->flag = POINTER_FLAG; ++ np->type = tp1; ++ while (tp->flag == POINTER_FLAG) { ++ tp1->t_next = (type_t *)kl_alloc_block(sizeof(type_t)); ++ tp1->flag = POINTER_FLAG; ++ tp1 = tp1->t_next; ++ tp = tp->t_next; ++ } ++ if (tp) { ++ tp1->t_next = (type_t *)kl_alloc_block(sizeof(type_t)); ++ tp1 = tp1->t_next; ++ tp1->flag = KLTYPE_FLAG; ++ tp1->t_kltp = tp->t_kltp; ++ if (type < 0) { ++ if (np->left->flags & POINTER_FLAG) { ++ np->flags |= POINTER_FLAG; ++ } else { ++ np->flags |= VADDR; ++ } ++ } else { ++ if (np->right->flags & POINTER_FLAG) { ++ np->flags |= POINTER_FLAG; ++ } else { ++ np->flags |= VADDR; ++ } ++ } ++ } ++ return(0); ++} ++ ++/* ++ * check_unsigned() ++ */ ++int ++check_unsigned(node_t *np) ++{ ++ kltype_t *kltp, *rkltp; ++ ++ if (np->flags & UNSIGNED_FLAG) { ++ return(1); ++ } ++ if (!np->type) { ++ return(0); ++ } ++ if (np->type->flag == POINTER_FLAG) { ++ return(0); ++ } ++ kltp = np->type->t_kltp; ++ if ((rkltp = kl_realtype(kltp, 0))) { ++ if (rkltp->kl_encoding == ENC_UNSIGNED) { ++ np->flags |= UNSIGNED_FLAG; ++ return(1); ++ } ++ } ++ return(0); ++} ++ ++/* ++ * apply() ++ */ ++static int ++apply(node_t *np, uint64_t *value, int flags) ++{ ++ int ltype, rtype, do_signed = 0; ++ ++ /* There must be two operands ++ */ ++ if (!np->right || !np->left) { ++ set_eval_error(E_MISSING_OPERAND); ++ error_token = np->tok_ptr; ++ return(-1); ++ } ++ ++ if (np->right->node_type == OPERATOR) { ++ replace(np->right, flags); ++ if (eval_error) { ++ return(-1); ++ } ++ } ++ ++ ltype = np->left->node_type; ++ rtype = np->right->node_type; ++ if ((ltype == TYPE_DEF) || (ltype == VADDR)) { ++ if ((rtype == TYPE_DEF) || (rtype == VADDR)) { ++ set_eval_error(E_NO_VALUE); ++ error_token = np->tok_ptr; ++ return(-1); ++ } ++ if (check_unsigned(np->left)) { ++ np->flags |= UNSIGNED_FLAG; ++ } else { ++ do_signed++; ++ } ++ if (!type_to_number(np->left)) { ++ return(pointer_math(np, value, -1, flags)); ++ } ++ np->byte_size = np->left->byte_size; ++ } else if ((rtype == TYPE_DEF) || (rtype == VADDR)) { ++ if ((ltype == TYPE_DEF) || (ltype == VADDR)) { ++ error_token = np->tok_ptr; ++ set_eval_error(E_NO_VALUE); ++ return(-1); ++ } ++ if (check_unsigned(np->right)) { ++ np->flags |= UNSIGNED_FLAG; ++ } else { ++ do_signed++; ++ } ++ if (!type_to_number(np->right)) { ++ return(pointer_math(np, value, 1, flags)); ++ } ++ np->byte_size = np->right->byte_size; ++ } else if ((np->left->flags & UNSIGNED_FLAG) || ++ (np->right->flags & UNSIGNED_FLAG)) { ++ np->flags |= UNSIGNED_FLAG; ++ } else { ++ do_signed++; ++ } ++ ++ if (do_signed) { ++ switch (np->operator) { ++ case ADD : ++ *value = (int64_t)np->left->value + ++ (int64_t)np->right->value; ++ break; ++ ++ case SUBTRACT : ++ *value = (int64_t)np->left->value - ++ (int64_t)np->right->value; ++ break; ++ ++ case MULTIPLY : ++ *value = (int64_t)np->left->value * ++ (int64_t)np->right->value; ++ break; ++ ++ case DIVIDE : ++ if ((int64_t)np->right->value == 0) { ++ set_eval_error(E_DIVIDE_BY_ZERO); ++ error_token = np->right->tok_ptr; ++ return(-1); ++ } ++ *value = (int64_t)np->left->value / ++ (int64_t)np->right->value; ++ break; ++ ++ case BITWISE_OR : ++ *value = (int64_t)np->left->value | ++ (int64_t)np->right->value; ++ break; ++ ++ case BITWISE_AND : ++ *value = (int64_t)np->left->value & ++ (int64_t)np->right->value; ++ break; ++ ++ case MODULUS : ++ if ((int64_t)np->right->value == 0) { ++ set_eval_error(E_DIVIDE_BY_ZERO); ++ error_token = np->right->tok_ptr; ++ return(-1); ++ } ++ *value = (int64_t)np->left->value % ++ (int64_t)np->right->value; ++ break; ++ ++ case RIGHT_SHIFT : ++ *value = ++ (int64_t)np->left->value >> ++ (int64_t)np->right->value; ++ break; ++ ++ case LEFT_SHIFT : ++ *value = ++ (int64_t)np->left->value << ++ (int64_t)np->right->value; ++ break; ++ ++ case LOGICAL_OR : ++ if ((int64_t)np->left->value || ++ (int64_t)np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case LOGICAL_AND : ++ if ((int64_t)np->left->value && ++ (int64_t)np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case EQUAL : ++ if ((int64_t)np->left->value == ++ (int64_t)np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case NOT_EQUAL : ++ if ((int64_t)np->left->value != ++ (int64_t)np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case LESS_THAN : ++ if ((int64_t)np->left->value < ++ (int64_t)np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case GREATER_THAN : ++ if ((int64_t)np->left->value > ++ (int64_t)np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case LESS_THAN_OR_EQUAL : ++ if ((int64_t)np->left->value <= ++ (int64_t)np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case GREATER_THAN_OR_EQUAL : ++ if ((int64_t)np->left->value >= ++ (int64_t)np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ default : ++ break; ++ } ++ } else { ++ switch (np->operator) { ++ case ADD : ++ *value = np->left->value + np->right->value; ++ break; ++ ++ case SUBTRACT : ++ *value = np->left->value - np->right->value; ++ break; ++ ++ case MULTIPLY : ++ *value = np->left->value * np->right->value; ++ break; ++ ++ case DIVIDE : ++ *value = np->left->value / np->right->value; ++ break; ++ ++ case BITWISE_OR : ++ *value = np->left->value | np->right->value; ++ break; ++ ++ case BITWISE_AND : ++ *value = np->left->value & np->right->value; ++ break; ++ ++ case MODULUS : ++ *value = np->left->value % np->right->value; ++ break; ++ ++ case RIGHT_SHIFT : ++ *value = np->left->value >> np->right->value; ++ break; ++ ++ case LEFT_SHIFT : ++ *value = np->left->value << np->right->value; ++ break; ++ ++ case LOGICAL_OR : ++ if (np->left->value || np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case LOGICAL_AND : ++ if (np->left->value && np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case EQUAL : ++ if (np->left->value == np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case NOT_EQUAL : ++ if (np->left->value != np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case LESS_THAN : ++ if (np->left->value < np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case GREATER_THAN : ++ if (np->left->value > np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case LESS_THAN_OR_EQUAL : ++ if (np->left->value <= np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ case GREATER_THAN_OR_EQUAL : ++ if (np->left->value >= np->right->value) { ++ *value = 1; ++ } else { ++ *value = 0; ++ } ++ logical_flag++; ++ break; ++ ++ default : ++ break; ++ } ++ } ++ return(0); ++} ++ ++/* ++ * member_to_type() ++ */ ++static type_t * ++member_to_type(kltype_t *kltp, int flags) ++{ ++ kltype_t *rkltp; ++ type_t *tp, *head = (type_t *)NULL, *last = (type_t *)NULL; ++ ++ /* Make sure this is a member ++ */ ++ if (kltp->kl_type != KLT_MEMBER) { ++ return((type_t *)NULL); ++ } ++ ++ rkltp = kltp->kl_realtype; ++ while (rkltp && rkltp->kl_type == KLT_POINTER) { ++ tp = (type_t *)kl_alloc_block(sizeof(type_t)); ++ tp->flag = POINTER_FLAG; ++ if (last) { ++ last->t_next = tp; ++ last = tp; ++ } else { ++ head = last = tp; ++ } ++ rkltp = rkltp->kl_realtype; ++ } ++ ++ /* If We step past all the pointer records and don't point ++ * at anything, this must be a void pointer. Setup a VOID ++ * type struct so that we can maintain a pointer to some ++ * type info. ++ */ ++ if (!rkltp) { ++ tp = (type_t *)kl_alloc_block(sizeof(type_t)); ++ tp->flag = VOID_FLAG; ++ tp->t_kltp = kltp; ++ if (last) { ++ last->t_next = tp; ++ last = tp; ++ } else { ++ head = last = tp; ++ } ++ return(head); ++ } ++ ++ tp = (type_t *)kl_alloc_block(sizeof(type_t)); ++ tp->flag = KLTYPE_FLAG; ++ tp->t_kltp = kltp; ++ if (last) { ++ last->t_next = tp; ++ } else { ++ head = tp; ++ } ++ return(head); ++} ++ ++/* ++ * replace() -- ++ * ++ * Replace the tree with a node containing the numerical result of ++ * the equation. If pointer math is performed, the result will have ++ * the same type as the pointer. ++ */ ++static node_t * ++replace(node_t *np, int flags) ++{ ++ int offset; ++ uint64_t value; ++ node_t *q; ++ ++ if (!np) { ++ return((node_t *)NULL); ++ } ++ ++ if (np->node_type == OPERATOR) { ++ if (!(q = np->left)) { ++ return((node_t *)NULL); ++ } ++ while (q) { ++ if (!replace(q, flags)) { ++ return((node_t *)NULL); ++ } ++ q = q->right; ++ } ++ ++ if ((np->operator == RIGHT_ARROW) || (np->operator == DOT)) { ++ kaddr_t addr = 0; ++ type_t *tp; ++ ++ if (!have_debug_file) { ++ kdb_printf("no debuginfo file\n"); ++ return 0; ++ } ++ ++ /* The left node must point to a TYPE_DEF ++ */ ++ if (np->left->node_type != TYPE_DEF) { ++ if (np->left->flags & NOTYPE_FLAG) { ++ set_eval_error(E_NOTYPE); ++ error_token = np->left->tok_ptr; ++ } else { ++ set_eval_error(E_BAD_TYPE); ++ error_token = np->left->tok_ptr; ++ } ++ return((node_t *)NULL); ++ } ++ ++ /* Get the type information. Check to see if we ++ * have a pointer to a type. If we do, we need ++ * to strip off the pointer and get the type info. ++ */ ++ if (np->left->type->flag == POINTER_FLAG) { ++ tp = np->left->type->t_next; ++ kl_free_block((void *)np->left->type); ++ } else { ++ tp = np->left->type; ++ } ++ ++ /* We need to zero out the left child's type pointer ++ * to prevent the type structs from being prematurely ++ * freed (upon success). We have to remember, however, ++ * to the free the type information before we return. ++ */ ++ np->left->type = (type_t*)NULL; ++ ++ /* tp should now point at a type_t struct that ++ * references a kltype_t struct. If it points ++ * to anything else, return failure. ++ * ++ */ ++ if (tp->flag != KLTYPE_FLAG) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = np->left->tok_ptr; ++ free_type(tp); ++ return((node_t *)NULL); ++ } ++ ++ switch (tp->flag) { ++ case KLTYPE_FLAG: { ++ /* Make sure that the type referenced ++ * is a struct, union, or pointer to ++ * a struct or union. If it isn't one ++ * of these, then return failure. ++ */ ++ kltype_t *kltp, *kltmp; ++ ++ kltp = kl_realtype(tp->t_kltp, 0); ++ if ((kltp->kl_type != KLT_STRUCT) && ++ (kltp->kl_type != KLT_UNION)) { ++ error_token = ++ np->left->tok_ptr; ++ set_eval_error(E_BAD_TYPE); ++ free_type(tp); ++ return((node_t *)NULL); ++ } ++ ++ /* Get type information for member. ++ * If member is a pointer to a type, ++ * get the pointer address and load ++ * it into value. In any event, load ++ * the struct/union address plus the ++ * offset of the member. ++ */ ++ kltmp = kl_get_member(kltp, ++ np->right->name); ++ if (!kltmp) { ++ set_eval_error(E_BAD_MEMBER); ++ error_token = ++ np->right->tok_ptr; ++ free_type(tp); ++ return((node_t *)NULL); ++ } ++ ++ /* We can't just use the offset value ++ * for the member. That's because it ++ * may be from an anonymous struct or ++ * union within another struct ++ * definition. ++ */ ++ offset = kl_get_member_offset(kltp, ++ np->right->name); ++ np->type = member_to_type(kltmp, flags); ++ if (!np->type) { ++ set_eval_error(E_BAD_MEMBER); ++ error_token = ++ np->right->tok_ptr; ++ free_type(tp); ++ return((node_t *)NULL); ++ } ++ ++ /* Now free the struct type information ++ */ ++ free_type(tp); ++ np->node_type = TYPE_DEF; ++ np->flags |= KLTYPE_FLAG; ++ np->operator = 0; ++ addr = 0; ++ if (np->left->flags & POINTER_FLAG) { ++ addr = np->left->value + ++ offset; ++ } else if (np->left->flags & ++ ADDRESS_FLAG) { ++ addr = np->left->address + ++ offset; ++ } ++ if (addr) { ++ np->address = addr; ++ np->flags |= ADDRESS_FLAG; ++ } ++ ++ if (np->type->flag == POINTER_FLAG) { ++ np->flags |= POINTER_FLAG; ++ np->value = *((kaddr_t *)addr); ++ } else { ++ np->value = addr; ++ } ++ break; ++ } ++ } ++ free_nodes(np->left); ++ free_nodes(np->right); ++ np->left = np->right = (node_t*)NULL; ++ return(np); ++ } else { ++ if (!np->left || !np->right) { ++ set_eval_error(E_MISSING_OPERAND); ++ error_token = np->tok_ptr; ++ return((node_t *)NULL); ++ } ++ if (np->left->byte_size && np->right->byte_size) { ++ if (np->left->byte_size > ++ np->right->byte_size) { ++ ++ /* Left byte_size is greater than right ++ */ ++ np->byte_size = np->left->byte_size; ++ np->type = np->left->type; ++ np->flags = np->left->flags; ++ free_type(np->right->type); ++ } else if (np->left->byte_size < ++ np->right->byte_size) { ++ ++ /* Right byte_size is greater than left ++ */ ++ np->byte_size = np->right->byte_size; ++ np->type = np->right->type; ++ np->flags = np->right->flags; ++ free_type(np->left->type); ++ } else { ++ ++ /* Left and right byte_size is equal ++ */ ++ if (np->left->flags & UNSIGNED_FLAG) { ++ np->byte_size = ++ np->left->byte_size; ++ np->type = np->left->type; ++ np->flags = np->left->flags; ++ free_type(np->right->type); ++ } else if (np->right->flags & ++ UNSIGNED_FLAG) { ++ np->byte_size = ++ np->right->byte_size; ++ np->type = np->right->type; ++ np->flags = np->right->flags; ++ free_type(np->left->type); ++ } else { ++ np->byte_size = ++ np->left->byte_size; ++ np->type = np->left->type; ++ np->flags = np->left->flags; ++ free_type(np->right->type); ++ } ++ } ++ } else if (np->left->byte_size) { ++ np->byte_size = np->left->byte_size; ++ np->type = np->left->type; ++ np->flags = np->left->flags; ++ free_type(np->right->type); ++ } else if (np->right->byte_size) { ++ np->byte_size = np->right->byte_size; ++ np->type = np->right->type; ++ np->flags = np->right->flags; ++ } else { ++ /* XXX - No byte sizes ++ */ ++ } ++ ++ if (apply(np, &value, flags)) { ++ return((node_t *)NULL); ++ } ++ } ++ np->right->type = np->left->type = (type_t*)NULL; ++ ++ /* Flesh out the rest of the node struct. ++ */ ++ if (np->type) { ++ np->node_type = TYPE_DEF; ++ np->flags |= KLTYPE_FLAG; ++ } else { ++ np->node_type = NUMBER; ++ np->flags &= ~(KLTYPE_FLAG); ++ } ++ np->operator = 0; ++ np->value = value; ++ kl_free_block((void *)np->left); ++ kl_free_block((void *)np->right); ++ np->left = np->right = (node_t*)NULL; ++ } ++ return(np); ++} ++ ++/* ++ * replace_cast() ++ */ ++static int ++replace_cast(node_t *n, int flags) ++{ ++ type_t *t; ++ ++ if (!n) { ++ set_eval_error(E_SYNTAX_ERROR); ++ return(-1); ++ } else if (!n->right) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = n->tok_ptr; ++ return(-1); ++ } ++ if (n->flags & POINTER_FLAG) { ++ if (n->right->node_type == VADDR) { ++ if (n->right->flags & ADDRESS_FLAG) { ++ n->value = n->right->address; ++ } else { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ ++ } else { ++ n->value = n->right->value; ++ n->address = 0; ++ } ++ } else if (n->right->flags & ADDRESS_FLAG) { ++ n->flags |= ADDRESS_FLAG; ++ n->address = n->right->address; ++ n->value = n->right->value; ++ } else { ++ kltype_t *kltp; ++ ++ if (!(t = eval_type(n))) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->tok_ptr; ++ return(-1); ++ } ++ if (t->t_kltp->kl_type != KLT_BASE) { ++ ++ kltp = kl_realtype(t->t_kltp, 0); ++ if (kltp->kl_type != KLT_BASE) { ++ set_eval_error(E_BAD_CAST); ++ error_token = n->tok_ptr; ++ return(-1); ++ } ++ } ++ n->value = n->right->value; ++ n->type = t; ++ } ++ n->node_type = TYPE_DEF; ++ n->operator = 0; ++ free_node(n->right); ++ n->right = (node_t *)NULL; ++ return(0); ++} ++ ++/* ++ * replace_indirection() ++ */ ++static int ++replace_indirection(node_t *n, int flags) ++{ ++ kaddr_t addr; ++ type_t *t, *tp, *rtp; ++ ++ /* Make sure there is a right child and that it is a TYPE_DEF. ++ */ ++ if (!n->right) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->tok_ptr; ++ return(-1); ++ } else if (n->right->node_type != TYPE_DEF) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ ++ /* Make sure the right node contains a pointer or address value. ++ * Note that it's possible for the whatis command to generate ++ * this case without any actual pointer/address value. ++ */ ++ if (!(n->right->flags & (POINTER_FLAG|ADDRESS_FLAG))) { ++ set_eval_error(E_BAD_POINTER); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ ++ /* Get the pointer to the first type struct and make sure ++ * it's a pointer. ++ */ ++ if (!(tp = n->right->type) || (tp->flag != POINTER_FLAG)) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ ++ /* Make sure we have a pointer to a type structure. ++ */ ++ if (!(n->right->flags & KLTYPE_FLAG)) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ ++ n->node_type = TYPE_DEF; ++ n->flags = KLTYPE_FLAG; ++ n->operator = 0; ++ ++ if (!(t = tp->t_next)) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ ++ if (!(rtp = eval_type(n->right))) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ ++ /* Zero out the type field in the right child so ++ * it wont accidently be freed when the right child ++ * is freed (upon success). ++ */ ++ n->right->type = (type_t*)NULL; ++ ++ n->type = t; ++ ++ /* Free the pointer struct ++ */ ++ kl_free_block((void *)tp); ++ ++ /* Get the pointer address ++ */ ++ addr = n->address = n->right->value; ++ n->flags |= ADDRESS_FLAG; ++ ++ if (rtp->t_kltp->kl_type == KLT_MEMBER) { ++ /* If this is a member, we have to step over the KLT_MEMBER ++ * struct and then make sure we have a KLT_POINTER struct. ++ * If we do, we step over it too...otherwise return an ++ * error. ++ */ ++ if (rtp->t_kltp->kl_realtype->kl_type != KLT_POINTER) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ rtp->t_kltp = rtp->t_kltp->kl_realtype; ++ } ++ ++ if (rtp->t_kltp->kl_type == KLT_POINTER) { ++ /* Strip off the pointer type record so that ++ * we pick up the actual type definition with ++ * our indirection. ++ */ ++ rtp->t_kltp = rtp->t_kltp->kl_realtype; ++ if (rtp->t_kltp->kl_name && ++ !strcmp(rtp->t_kltp->kl_name, "char")) { ++ n->flags |= STRING_FLAG; ++ } ++ } ++ ++ ++ /* If this is a pointer to a pointer, get the next ++ * pointer value. ++ */ ++ if (n->type->flag == POINTER_FLAG) { ++ n->value = *((kaddr_t *)addr); ++ ++ /* Set the appropriate node flag values ++ */ ++ n->flags |= POINTER_FLAG; ++ free_node(n->right); ++ n->left = n->right = (node_t *)NULL; ++ return(0); ++ } ++ /* Zero out the type field in the right child so it doesn't ++ * accidently get freed up when the right child is freed ++ * (upon success). ++ */ ++ n->right->type = (type_t*)NULL; ++ free_node(n->right); ++ n->left = n->right = (node_t *)NULL; ++ return(0); ++} ++ ++/* ++ * replace_unary() ++ * ++ * Convert a unary operator node that contains a pointer to a value ++ * with a node containing the numerical result. Free the node that ++ * originally contained the value. ++ */ ++static int ++replace_unary(node_t *n, int flags) ++{ ++ uint64_t value; ++ ++ if (!n->right) { ++ set_eval_error(E_MISSING_OPERAND); ++ error_token = n->tok_ptr; ++ return(-1); ++ } ++ if (is_unary(n->right->operator)) { ++ if (replace_unary(n->right, flags) == -1) { ++ return(-1); ++ } ++ } ++ if (n->operator == CAST) { ++ return(replace_cast(n, flags)); ++ } else if (n->operator == INDIRECTION) { ++ return(replace_indirection(n, flags)); ++ } else if (n->operator == ADDRESS) { ++ type_t *t; ++ ++ if (n->right->node_type == TYPE_DEF) { ++ if (!(n->right->flags & ADDRESS_FLAG)) { ++ set_eval_error(E_NO_ADDRESS); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ t = n->right->type; ++ } else { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->right->tok_ptr; ++ return(-1); ++ } ++ n->type = (type_t*)kl_alloc_block(sizeof(type_t)); ++ n->type->flag = POINTER_FLAG; ++ n->type->t_next = t; ++ n->node_type = TYPE_DEF; ++ n->operator = 0; ++ n->value = n->right->address; ++ n->flags = POINTER_FLAG; ++ if (!(t = eval_type(n))) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = n->tok_ptr; ++ return(-1); ++ } ++ n->flags |= t->flag; ++ n->right->type = 0; ++ free_nodes(n->right); ++ n->left = n->right = (node_t *)NULL; ++ return(0); ++ } else if (apply_unary(n, &value) == -1) { ++ return(-1); ++ } ++ free_nodes(n->right); ++ n->node_type = NUMBER; ++ n->operator = 0; ++ n->left = n->right = (node_t *)NULL; ++ memcpy(&n->value, &value, sizeof(uint64_t)); ++ return(0); ++} ++ ++/* ++ * pointer_to_element() ++ */ ++static void ++pointer_to_element(node_t *n0, node_t *n1) ++{ ++ int size; ++ kltype_t *kltp, *rkltp; ++ type_t *tp; ++ ++ if (!(tp = n0->type)) { ++ set_eval_error(E_BAD_INDEX); ++ error_token = n0->tok_ptr; ++ return; ++ } ++ if (tp->t_next->flag == POINTER_FLAG) { ++ size = sizeof(void *); ++ } else { ++ kltp = tp->t_next->t_kltp; ++ if (!(rkltp = kl_realtype(kltp, 0))) { ++ set_eval_error(E_BAD_INDEX); ++ error_token = n0->tok_ptr; ++ return; ++ } ++ size = rkltp->kl_size; ++ } ++ ++ /* Get the details on the array element ++ */ ++ n0->flags |= ADDRESS_FLAG; ++ n0->address = n0->value + (n1->value * size); ++ n0->type = tp->t_next; ++ kl_free_block((char *)tp); ++ if (tp->t_next->flag == POINTER_FLAG) { ++ n0->flags |= POINTER_FLAG; ++ n0->value = *((kaddr_t *)n0->address); ++ } else { ++ n0->flags &= (~POINTER_FLAG); ++ n0->value = 0; ++ } ++} ++ ++/* ++ * array_to_element() ++ */ ++static void ++array_to_element(node_t *n0, node_t *n1) ++{ ++ kltype_t *kltp, *rkltp, *ip, *ep; ++ type_t *tp, *troot = (type_t *)NULL; ++ ++ if (!(tp = n0->type)) { ++ set_eval_error(E_BAD_INDEX); ++ error_token = n0->tok_ptr; ++ return; ++ } ++ ++ /* If we are indexing a pointer, then make a call to the ++ * pointer_to_element() and return. ++ */ ++ if (tp->flag == POINTER_FLAG) { ++ return(pointer_to_element(n0, n1)); ++ } ++ ++ if (!(kltp = n0->type->t_kltp)) { ++ set_eval_error(E_BAD_INDEX); ++ error_token = n0->tok_ptr; ++ return; ++ } ++ if (!(rkltp = kl_realtype(kltp, KLT_ARRAY))) { ++ set_eval_error(E_BAD_INDEX); ++ error_token = n0->tok_ptr; ++ return; ++ } ++ ip = rkltp->kl_indextype; ++ ep = rkltp->kl_elementtype; ++ if (!ip || !ep) { ++ set_eval_error(E_BAD_INDEX); ++ error_token = n1->tok_ptr; ++ return; ++ } ++ /* Get the details on the array element ++ */ ++ n0->address = n0->address + (n1->value * ep->kl_size); ++ if (ep->kl_type == KLT_POINTER) { ++ n0->flags |= POINTER_FLAG; ++ n0->value = *((kaddr_t *)n0->address); ++ } else { ++ n0->value = 0; ++ } ++ n0->flags |= ADDRESS_FLAG; ++ kltp = ep; ++ while (kltp->kl_type == KLT_POINTER) { ++ if (troot) { ++ tp->t_next = (type_t*)kl_alloc_block(sizeof(type_t)); ++ tp = tp->t_next; ++ } else { ++ tp = (type_t*)kl_alloc_block(sizeof(type_t)); ++ troot = tp; ++ } ++ tp->flag = POINTER_FLAG; ++ kltp = kltp->kl_realtype; ++ } ++ if (troot) { ++ tp->t_next = (type_t*)kl_alloc_block(sizeof(type_t)); ++ tp = tp->t_next; ++ n0->type = troot; ++ } else { ++ tp = (type_t*)kl_alloc_block(sizeof(type_t)); ++ n0->type = tp; ++ } ++ tp->flag = KLTYPE_FLAG; ++ tp->t_kltp = ep; ++} ++ ++/* ++ * number_to_size() ++ */ ++int ++number_to_size(node_t *np) ++{ ++ int unsigned_flag = 0; ++ ++ if (np->node_type != NUMBER) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ if (np->flags & UNSIGNED_FLAG) { ++ unsigned_flag = 1; ++ } ++ if ((np->value >= 0) && (np->value <= 0xffffffff)) { ++ return(4); ++ } else if (((np->value >> 32) & 0xffffffff) == 0xffffffff) { ++ if (unsigned_flag) { ++ return(8); ++ } else if (sizeof(void *) == 4) { ++ return(4); ++ } else { ++ return(8); ++ } ++ } ++ return(8); ++} ++ ++/* ++ * number_to_type() ++ */ ++kltype_t * ++number_to_type(node_t *np) ++{ ++ int unsigned_flag = 0; ++ kltype_t *kltp, *rkltp = (kltype_t *)NULL; ++ ++ if (np->node_type != NUMBER) { ++ set_eval_error(E_BAD_TYPE); ++ error_token = np->tok_ptr; ++ return((kltype_t *)NULL); ++ } ++ if (np->flags & UNSIGNED_FLAG) { ++ unsigned_flag = 1; ++ } ++ if ((np->value >= 0) && (np->value <= 0xffffffff)) { ++ if (unsigned_flag) { ++ kltp = kl_find_type("uint32_t", KLT_TYPEDEF); ++ } else { ++ kltp = kl_find_type("int32_t", KLT_TYPEDEF); ++ } ++ } else if (((np->value >> 32) & 0xffffffff) == 0xffffffff) { ++ if (unsigned_flag) { ++ kltp = kl_find_type("uint64_t", KLT_TYPEDEF); ++ } else if (sizeof(void *) == 4) { ++ kltp = kl_find_type("int32_t", KLT_TYPEDEF); ++ } else { ++ kltp = kl_find_type("int64_t", KLT_TYPEDEF); ++ } ++ } else { ++ if (unsigned_flag) { ++ kltp = kl_find_type("uint64_t", KLT_TYPEDEF); ++ } else { ++ kltp = kl_find_type("int64_t", KLT_TYPEDEF); ++ } ++ } ++ if (kltp) { ++ if (!(rkltp = kl_realtype(kltp, 0))) { ++ rkltp = kltp; ++ } ++ } else { ++ set_eval_error(E_BAD_TYPE); ++ error_token = np->tok_ptr; ++ } ++ return(rkltp); ++} ++ ++/* ++ * type_to_number() ++ * ++ * Convert a base type to a numeric value. Return 1 on successful ++ * conversion, 0 if nothing was done. ++ */ ++static int ++type_to_number(node_t *np) ++{ ++ int byte_size, bit_offset, bit_size, encoding; ++ uint64_t value, value1; ++ kltype_t *kltp, *rkltp; ++ ++ /* Sanity check... ++ */ ++ if (np->node_type != TYPE_DEF) { ++ set_eval_error(E_NOTYPE); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ if (!np->type) { ++ set_eval_error(E_NOTYPE); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ if (np->type->flag == POINTER_FLAG) { ++ return(0); ++ } ++ ++ /* Get the real type record and make sure that it is ++ * for a base type. ++ */ ++ kltp = np->type->t_kltp; ++ rkltp = kl_realtype(kltp, 0); ++ if (rkltp->kl_type != KLT_BASE) { ++ set_eval_error(E_NOTYPE); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ ++ byte_size = rkltp->kl_size; ++ bit_offset = rkltp->kl_bit_offset; ++ if (!(bit_size = rkltp->kl_bit_size)) { ++ bit_size = byte_size * 8; ++ } ++ encoding = rkltp->kl_encoding; ++ if (np->flags & ADDRESS_FLAG) { ++ /* FIXME: untested */ ++ if (invalid_address(np->address, byte_size)) { ++ kdb_printf("ILLEGAL ADDRESS (%lx)", ++ (uaddr_t)np->address); ++ return (0); ++ } ++ kl_get_block(np->address, byte_size,(void *)&value1,(void *)0); ++ } else { ++ value1 = np->value; ++ } ++ value = kl_get_bit_value(&value1, byte_size, bit_size, bit_offset); ++ switch (byte_size) { ++ ++ case 1 : ++ if (encoding == ENC_UNSIGNED) { ++ np->value = (unsigned char)value; ++ np->flags |= UNSIGNED_FLAG; ++ } else if (encoding == ENC_SIGNED) { ++ np->value = (signed char)value; ++ } else { ++ np->value = (char)value; ++ } ++ break; ++ ++ case 2 : ++ if (encoding == ENC_UNSIGNED) { ++ np->value = (uint16_t)value; ++ np->flags |= UNSIGNED_FLAG; ++ } else { ++ np->value = (int16_t)value; ++ } ++ break; ++ ++ case 4 : ++ if (encoding == ENC_UNSIGNED) { ++ np->value = (uint32_t)value; ++ np->flags |= UNSIGNED_FLAG; ++ } else { ++ np->value = (int32_t)value; ++ } ++ break; ++ ++ case 8 : ++ if (encoding == ENC_UNSIGNED) { ++ np->value = (uint64_t)value; ++ np->flags |= UNSIGNED_FLAG; ++ } else { ++ np->value = (int64_t)value; ++ } ++ break; ++ ++ default : ++ set_eval_error(E_BAD_TYPE); ++ error_token = np->tok_ptr; ++ return(0); ++ } ++ np->byte_size = byte_size; ++ np->node_type = NUMBER; ++ return(1); ++} ++ ++/* ++ * eval_type() ++ */ ++static type_t * ++eval_type(node_t *n) ++{ ++ type_t *t; ++ ++ if (!(t = n->type)) { ++ return((type_t*)NULL); ++ } ++ while (t->flag == POINTER_FLAG) { ++ t = t->t_next; ++ ++ /* If for some reason, there is no type pointer (this shouldn't ++ * happen but...), we have to make sure that we don't try to ++ * reference a NULL pointer and get a SEGV. Return an error if ++ * 't' is NULL. ++ */ ++ if (!t) { ++ return((type_t*)NULL); ++ } ++ } ++ if (t->flag == KLTYPE_FLAG) { ++ return (t); ++ } ++ return((type_t*)NULL); ++} ++ ++/* ++ * expand_variables() ++ */ ++static char * ++expand_variables(char *exp, int flags) ++{ ++ return((char *)NULL); ++} ++ ++/* ++ * eval() ++ */ ++node_t * ++eval(char **exp, int flags) ++{ ++ token_t *tok; ++ node_t *n, *root; ++ char *e, *s; ++ ++ eval_error = 0; ++ logical_flag = 0; ++ ++ /* Make sure there is an expression to evaluate ++ */ ++ if (!(*exp)) { ++ return ((node_t*)NULL); ++ } ++ ++ /* Expand any variables that are in the expression string. If ++ * a new string is allocated by the expand_variables() function, ++ * we need to make sure the original expression string gets ++ * freed. In any event, point s at the current expression string ++ * so that it gets freed up when we are done. ++ */ ++ if ((e = expand_variables(*exp, 0))) { ++ kl_free_block((void *)*exp); ++ *exp = e; ++ } else if (eval_error) { ++ eval_error |= E_BAD_EVAR; ++ error_token = *exp; ++ } ++ s = *exp; ++ tok = get_token_list(s); ++ if (eval_error) { ++ return((node_t*)NULL); ++ } ++ ++ /* Get the node_list and evaluate the expression. ++ */ ++ node_list = get_node_list(tok, flags); ++ if (eval_error) { ++ free_nodelist(node_list); ++ node_list = (node_t*)NULL; ++ free_tokens(tok); ++ return((node_t*)NULL); ++ } ++ if (!(n = do_eval(flags))) { ++ if (!eval_error) { ++ set_eval_error(E_SYNTAX_ERROR); ++ error_token = s + strlen(s) - 1; ++ } ++ free_nodes(n); ++ free_tokens(tok); ++ return((node_t*)NULL); ++ } ++ ++ if (!(root = replace(n, flags))) { ++ if (eval_error) { ++ free_nodes(n); ++ free_tokens(tok); ++ return((node_t*)NULL); ++ } ++ root = n; ++ } ++ ++ /* Check to see if the the result should ++ * be interpreted as 'true' or 'false' ++ */ ++ if (logical_flag && ((root->value == 0) || (root->value == 1))) { ++ root->flags |= BOOLIAN_FLAG; ++ } ++ free_tokens(tok); ++ return(root); ++} ++ ++/* ++ * print_number() ++ */ ++void ++print_number(node_t *np, int flags) ++{ ++ int size; ++ unsigned long long value; ++ ++ if ((size = number_to_size(np)) && (size != sizeof(uint64_t))) { ++ value = np->value & (((uint64_t)1 << (uint64_t)(size*8))-1); ++ } else { ++ value = np->value; ++ } ++ if (flags & C_HEX) { ++ kdb_printf("0x%llx", value); ++ } else if (flags & C_BINARY) { ++ kdb_printf("0b"); ++ kl_binary_print(value); ++ } else { ++ if (np->flags & UNSIGNED_FLAG) { ++ kdb_printf("%llu", value); ++ } else { ++ kdb_printf("%lld", np->value); ++ } ++ } ++} ++ ++/* ++ * print_string() ++ */ ++void ++print_string(kaddr_t addr, int size) ++{ ++ int i; ++ char *str; ++ ++ if (!size) { ++ size = 255; ++ } ++ /* FIXME: untested */ ++ if (invalid_address(addr, size)) { ++ klib_error = KLE_INVALID_PADDR; ++ return; ++ } ++ str = (char*)kl_alloc_block(size); ++ kl_get_block(addr, size, (void *)str, (void *)0); ++ kdb_printf("\"%s", str); ++ for (i = 0; i < size; i++) { ++ if (!str[i]) { ++ break; ++ } ++ } ++ if (KL_ERROR || (i == size)) { ++ kdb_printf("..."); ++ } ++ kdb_printf("\""); ++ kl_free_block(str); ++} ++ ++/* ++ * kl_print_error() ++ */ ++void ++kl_print_error(void) ++{ ++ int ecode; ++ ++ ecode = klib_error & 0xffffffff; ++ switch(ecode) { ++ ++ /** General klib error codes ++ **/ ++ case KLE_NO_MEMORY: ++ kdb_printf("insufficient memory"); ++ break; ++ case KLE_OPEN_ERROR: ++ kdb_printf("unable to open file"); ++ break; ++ case KLE_ZERO_BLOCK: ++ kdb_printf("tried to allocate a zero-sized block"); ++ break; ++ case KLE_INVALID_VALUE: ++ kdb_printf("invalid input value"); ++ break; ++ case KLE_NULL_BUFF: ++ kdb_printf( "NULL buffer pointer"); ++ break; ++ case KLE_ZERO_SIZE: ++ kdb_printf("zero sized block requested"); ++ break; ++ case KLE_ACTIVE: ++ kdb_printf("operation not supported on a live system"); ++ break; ++ case KLE_UNSUPPORTED_ARCH: ++ kdb_printf("unsupported architecture"); ++ break; ++ case KLE_MISC_ERROR: ++ kdb_printf("KLIB error"); ++ break; ++ case KLE_NOT_SUPPORTED: ++ kdb_printf("operation not supported"); ++ break; ++ case KLE_UNKNOWN_ERROR: ++ kdb_printf("unknown error"); ++ break; ++ ++ /** memory error codes ++ **/ ++ case KLE_BAD_MAP_FILE: ++ kdb_printf("bad map file"); ++ break; ++ case KLE_BAD_DUMP: ++ kdb_printf("bad dump file"); ++ break; ++ case KLE_BAD_DUMPTYPE: ++ kdb_printf("bad dumptype"); ++ break; ++ case KLE_INVALID_LSEEK: ++ kdb_printf("lseek error"); ++ break; ++ case KLE_INVALID_READ: ++ kdb_printf("not found in dump file"); ++ break; ++ case KLE_BAD_KERNINFO: ++ kdb_printf("bad kerninfo struct"); ++ break; ++ case KLE_INVALID_PADDR: ++ kdb_printf("invalid physical address"); ++ break; ++ case KLE_INVALID_VADDR: ++ kdb_printf("invalid virtual address"); ++ break; ++ case KLE_INVALID_VADDR_ALIGN: ++ kdb_printf("invalid vaddr alignment"); ++ break; ++ case KLE_INVALID_MAPPING: ++ kdb_printf("invalid address mapping"); ++ break; ++ case KLE_PAGE_NOT_PRESENT: ++ kdb_printf("page not present"); ++ break; ++ case KLE_BAD_ELF_FILE: ++ kdb_printf("bad elf file"); ++ break; ++ case KLE_ARCHIVE_FILE: ++ kdb_printf("archive file"); ++ break; ++ case KLE_MAP_FILE_PRESENT: ++ kdb_printf("map file present"); ++ break; ++ case KLE_BAD_MAP_FILENAME: ++ kdb_printf("bad map filename"); ++ break; ++ case KLE_BAD_DUMP_FILENAME: ++ kdb_printf("bad dump filename"); ++ break; ++ case KLE_BAD_NAMELIST_FILE: ++ kdb_printf("bad namelist file"); ++ break; ++ case KLE_BAD_NAMELIST_FILENAME: ++ kdb_printf("bad namelist filename"); ++ break; ++ ++ /** symbol error codes ++ **/ ++ case KLE_NO_SYMTAB: ++ kdb_printf("no symtab"); ++ break; ++ case KLE_NO_SYMBOLS: ++ kdb_printf("no symbol information"); ++ break; ++ case KLE_NO_MODULE_LIST: ++ kdb_printf("kernel without module support"); ++ break; ++ ++ /** kernel data error codes ++ **/ ++ case KLE_INVALID_KERNELSTACK: ++ kdb_printf("invalid kernel stack"); ++ break; ++ case KLE_INVALID_STRUCT_SIZE: ++ kdb_printf("invalid struct size"); ++ break; ++ case KLE_BEFORE_RAM_OFFSET: ++ kdb_printf("physical address proceeds start of RAM"); ++ break; ++ case KLE_AFTER_MAXPFN: ++ kdb_printf("PFN exceeds maximum PFN"); ++ break; ++ case KLE_AFTER_PHYSMEM: ++ kdb_printf("address exceeds physical memory"); ++ break; ++ case KLE_AFTER_MAXMEM: ++ kdb_printf("address exceeds maximum physical address"); ++ break; ++ case KLE_PHYSMEM_NOT_INSTALLED: ++ kdb_printf("physical memory not installed"); ++ break; ++ case KLE_NO_DEFTASK: ++ kdb_printf("default task not set"); ++ break; ++ case KLE_PID_NOT_FOUND: ++ kdb_printf("PID not found"); ++ break; ++ case KLE_DEFTASK_NOT_ON_CPU: ++ kdb_printf("default task not running on a cpu"); ++ break; ++ case KLE_NO_CURCPU: ++ kdb_printf("current cpu could not be determined"); ++ break; ++ ++ case KLE_KERNEL_MAGIC_MISMATCH: ++ kdb_printf("kernel_magic mismatch " ++ "of map and memory image"); ++ break; ++ ++ case KLE_INVALID_DUMP_HEADER: ++ kdb_printf("invalid dump header in dump"); ++ break; ++ ++ case KLE_DUMP_INDEX_CREATION: ++ kdb_printf("cannot create index file"); ++ break; ++ ++ case KLE_DUMP_HEADER_ONLY: ++ kdb_printf("dump only has a dump header"); ++ break; ++ ++ case KLE_NO_END_SYMBOL: ++ kdb_printf("no _end symbol in kernel"); ++ break; ++ ++ case KLE_NO_CPU: ++ kdb_printf("CPU not installed"); ++ break; ++ ++ default: ++ break; ++ } ++ kdb_printf("\n"); ++} ++ ++/* ++ * kl_print_string() ++ * ++ * print out a string, translating all embeded control characters ++ * (e.g., '\n' for newline, '\t' for tab, etc.) ++ */ ++void ++kl_print_string(char *s) ++{ ++ char *sp, *cp; ++ ++ kl_reset_error(); ++ ++ if (!(sp = s)) { ++ klib_error = KLE_BAD_STRING; ++ return; ++ } ++ /* FIXME: untested */ ++ if (invalid_address((kaddr_t)sp, 1)) { ++ klib_error = KLE_INVALID_PADDR; ++ return; ++ } ++ ++ while (sp) { ++ if ((cp = strchr(sp, '\\'))) { ++ switch (*(cp + 1)) { ++ ++ case 'n' : ++ *cp++ = '\n'; ++ *cp++ = 0; ++ break; ++ ++ case 't' : ++ *cp++ = '\t'; ++ *cp++ = 0; ++ break; ++ ++ default : ++ if (*(cp + 1) == 0) { ++ klib_error = KLE_BAD_STRING; ++ return; ++ } ++ /* Change the '\' character to a zero ++ * and then print the string (the rest ++ * of the string will be picked ++ * up on the next pass). ++ */ ++ *cp++ = 0; ++ break; ++ } ++ kdb_printf("%s", sp); ++ sp = cp; ++ } else { ++ kdb_printf("%s", sp); ++ sp = 0; ++ } ++ } ++} ++ ++/* ++ * print_eval_results() ++ */ ++int ++print_eval_results(node_t *np, int flags) ++{ ++ int size, i, count, ptr_cnt = 0; ++ kaddr_t addr; ++ char *typestr; ++ kltype_t *kltp, *rkltp = NULL, *nkltp; ++ type_t *tp; ++ ++ /* Print the results ++ */ ++ switch (np->node_type) { ++ ++ case NUMBER: ++ print_number(np, flags); ++ break; ++ ++ case TYPE_DEF: { ++ ++ /* First, determine the number of levels of indirection ++ * by determining the number of pointer type records. ++ */ ++ if ((tp = np->type)) { ++ while (tp && (tp->flag == POINTER_FLAG)) { ++ ptr_cnt++; ++ tp = tp->t_next; ++ } ++ if (tp) { ++ rkltp = tp->t_kltp; ++ } ++ } ++ if (!rkltp) { ++ kdb_printf("Type information not available\n"); ++ return(1); ++ } ++ ++ if (ptr_cnt) { ++ ++ /* If this is a member, we need to get the ++ * first type record. ++ */ ++ if (rkltp->kl_type == KLT_MEMBER) { ++ /* We need to get down to the first ++ * real type record... ++ */ ++ rkltp = rkltp->kl_realtype; ++ } ++ ++ /* step over any KLT_POINTER type records. ++ */ ++ while (rkltp && rkltp->kl_type == KLT_POINTER) { ++ rkltp = rkltp->kl_realtype; ++ } ++ if (!rkltp) { ++ kdb_printf("Bad type information\n"); ++ return(1); ++ } ++ typestr = rkltp->kl_typestr; ++ if (rkltp->kl_type == KLT_FUNCTION) { ++ kdb_printf("%s(", typestr); ++ } else if (rkltp->kl_type == KLT_ARRAY) { ++ kdb_printf("(%s(", typestr); ++ } else { ++ kdb_printf("(%s", typestr); ++ } ++ for (i = 0; i < ptr_cnt; i++) { ++ kdb_printf("*"); ++ } ++ if (rkltp->kl_type == KLT_FUNCTION) { ++ kdb_printf(")("); ++ } else if (rkltp->kl_type == KLT_ARRAY) { ++ kdb_printf(")"); ++ ++ nkltp = rkltp; ++ while (nkltp->kl_type == KLT_ARRAY) { ++ count = nkltp->kl_high_bounds - ++ nkltp->kl_low_bounds + 1; ++ kdb_printf("[%d]", count); ++ nkltp = nkltp->kl_elementtype; ++ } ++ } ++ kdb_printf(") "); ++ kdb_printf("0x%llx", np->value); ++ ++ if (ptr_cnt > 1) { ++ break; ++ } ++ ++ if ((rkltp->kl_type == KLT_BASE) && ++ rkltp->kl_encoding == ENC_CHAR) { ++ kdb_printf(" = "); ++ print_string(np->value, 0); ++ } ++ break; ++ } ++ if (np->flags & KLTYPE_FLAG) { ++ void * ptr; ++ ++ /* Get the type information. It's possible ++ * that the type is a member. In which case, ++ * the size may only be from this record ++ * (which would be the casse if this is an ++ * array). We must check the original type ++ * record first, and try the realtype record ++ * if the value is zero. ++ */ ++ kltp = np->type->t_kltp; ++ ++ if (kltp->kl_type == KLT_MEMBER) { ++ rkltp = kltp->kl_realtype; ++ } else { ++ rkltp = kltp; ++ } ++ ++ /* Check to see if this is a typedef. If ++ * it is, then it might be a typedef for ++ * a pointer type. Don't walk to the last ++ * type record. ++ */ ++ while (rkltp->kl_type == KLT_TYPEDEF) { ++ rkltp = rkltp->kl_realtype; ++ } ++ ++ if (rkltp->kl_type == KLT_POINTER) { ++ kdb_printf("0x%llx", np->value); ++ break; ++ } ++ if((rkltp->kl_name != 0) && ++ !(strcmp(rkltp->kl_name, "void"))) { ++ /* we are about to dereference ++ * a void pointer. ++ */ ++ kdb_printf("Can't dereference a " ++ "generic pointer.\n"); ++ return(1); ++ } ++ ++ size = rkltp->kl_size; ++ if (!size || (size < 0)) { ++ size = kltp->kl_size; ++ } ++ ++ if(rkltp->kl_type==KLT_ARRAY) { ++ size = rkltp->kl_high_bounds - ++ rkltp->kl_low_bounds + 1; ++ if(rkltp->kl_elementtype == NULL){ ++ kdb_printf("Incomplete array" ++ " type.\n"); ++ return(1); ++ } ++ if(rkltp->kl_elementtype->kl_type == ++ KLT_POINTER){ ++ size *= sizeof(void *); ++ } else { ++ size *= rkltp->kl_elementtype->kl_size; ++ } ++ } ++ if(size){ ++ ptr = kl_alloc_block(size); ++ } else { ++ ptr = NULL; ++ } ++ if ((rkltp->kl_type == KLT_BASE) && ++ !(np->flags & ADDRESS_FLAG)) { ++ switch (size) { ++ case 1: ++ *(unsigned char *)ptr = ++ np->value; ++ break; ++ ++ case 2: ++ *(unsigned short *)ptr = ++ np->value; ++ break; ++ ++ case 4: ++ *(unsigned int *)ptr = ++ np->value; ++ break; ++ ++ case 8: ++ *(unsigned long long *) ++ ptr = np->value; ++ break; ++ } ++ kl_print_type(ptr, rkltp, 0, ++ flags|SUPPRESS_NAME); ++ kl_free_block(ptr); ++ return(1); ++ } ++ ++ if(size){ ++ addr = np->address; ++ if (invalid_address(addr, size)) { ++ kdb_printf ( ++ "invalid address %#lx\n", ++ addr); ++ return 1; ++ } ++ kl_get_block(addr, size, (void *)ptr, ++ (void *)0); ++ if (KL_ERROR) { ++ kl_print_error(); ++ kl_free_block(ptr); ++ return(1); ++ } ++ } ++ /* Print out the actual type ++ */ ++ switch (rkltp->kl_type) { ++ case KLT_STRUCT: ++ case KLT_UNION: ++ kl_print_type(ptr, rkltp, 0, ++ flags); ++ break; ++ ++ case KLT_ARRAY: ++ kl_print_type(ptr, rkltp, 0, ++ flags| SUPPRESS_NAME); ++ break; ++ ++ default: ++ kl_print_type(ptr, rkltp, 0, ++ (flags| ++ SUPPRESS_NAME| ++ SUPPRESS_NL)); ++ break; ++ } ++ if(ptr){ ++ kl_free_block(ptr); ++ } ++ } ++ break; ++ } ++ ++ case VADDR: ++ /* If we get here, there was no type info available. ++ * The ADDRESS_FLAG should be set (otherwise we ++ * would have returned an error). So, print out ++ * the address. ++ */ ++ kdb_printf("0x%lx", np->address); ++ break; ++ ++ default: ++ if (np->node_type == TEXT) { ++ kl_print_string(np->name); ++ if (KL_ERROR) { ++ kl_print_error(); ++ return(1); ++ } ++ } else if (np->node_type == CHARACTER) { ++ kdb_printf("\'%c\'", (char)np->value); ++ } ++ break; ++ } ++ return(0); ++} ++ ++/* ++ * print_eval_error() ++ */ ++void ++print_eval_error( ++ char *cmdname, ++ char *s, ++ char *bad_ptr, ++ uint64_t error, ++ int flags) ++{ ++ int i, cmd_len; ++ ++ kdb_printf("%s %s\n", cmdname, s); ++ cmd_len = strlen(cmdname); ++ ++ if (!bad_ptr) { ++ for (i = 0; i < (strlen(s) + cmd_len); i++) { ++ kdb_printf(" "); ++ } ++ } else { ++ for (i = 0; i < (bad_ptr - s + 1 + cmd_len); i++) { ++ kdb_printf(" "); ++ } ++ } ++ kdb_printf("^ "); ++ switch (error) { ++ case E_OPEN_PAREN : ++ kdb_printf("Too many open parenthesis\n"); ++ break; ++ ++ case E_CLOSE_PAREN : ++ kdb_printf("Too many close parenthesis\n"); ++ break; ++ ++ case E_BAD_STRUCTURE : ++ kdb_printf("Invalid structure\n"); ++ break; ++ ++ case E_MISSING_STRUCTURE : ++ kdb_printf("Missing structure\n"); ++ break; ++ ++ case E_BAD_MEMBER : ++ kdb_printf("No such member\n"); ++ break; ++ ++ case E_BAD_OPERATOR : ++ kdb_printf("Invalid operator\n"); ++ break; ++ ++ case E_MISSING_OPERAND : ++ kdb_printf("Missing operand\n"); ++ break; ++ ++ case E_BAD_OPERAND : ++ kdb_printf("Invalid operand\n"); ++ break; ++ ++ case E_BAD_TYPE : ++ kdb_printf("Invalid type\n"); ++ if (!have_debug_file) { ++ kdb_printf("no debuginfo file\n"); ++ return; ++ } ++ break; ++ ++ case E_NOTYPE : ++ kdb_printf("Could not find type information\n"); ++ break; ++ ++ case E_BAD_POINTER : ++ kdb_printf("Invalid pointer\n"); ++ break; ++ ++ case E_BAD_INDEX : ++ kdb_printf("Invalid array index\n"); ++ break; ++ ++ case E_BAD_CHAR : ++ kdb_printf("Invalid character value\n"); ++ break; ++ ++ case E_BAD_STRING : ++ kdb_printf("Non-termining string\n"); ++ break; ++ ++ case E_END_EXPECTED : ++ kdb_printf( ++ "Expected end of print statement\n"); ++ break; ++ ++ case E_BAD_EVAR : ++ kdb_printf("Invalid eval variable\n"); ++ break; ++ ++ case E_BAD_VALUE : ++ kdb_printf("Invalid value\n"); ++ break; ++ ++ case E_NO_VALUE : ++ kdb_printf("No value supplied\n"); ++ break; ++ ++ case E_DIVIDE_BY_ZERO : ++ kdb_printf("Divide by zero\n"); ++ break; ++ ++ case E_BAD_CAST : ++ kdb_printf("Invalid cast\n"); ++ break; ++ ++ case E_NO_ADDRESS : ++ kdb_printf("Not an address\n"); ++ break; ++ ++ case E_SINGLE_QUOTE : ++ kdb_printf("Missing single quote\n"); ++ break; ++ ++ case E_BAD_WHATIS : ++ kdb_printf("Invalid whatis Operation\n"); ++ break; ++ ++ case E_NOT_IMPLEMENTED : ++ kdb_printf("Not implemented\n"); ++ break; ++ ++ default : ++ kdb_printf("Syntax error\n"); ++ break; ++ } ++} ++ ++/* ++ * single_type() ++ */ ++void ++single_type(char *str) ++{ ++ char buffer[256], *type_name; ++ kltype_t *kltp; ++ syment_t *sp; ++ ++ type_name = buffer; ++ strcpy(type_name, str); ++ ++ if (have_debug_file) { ++ if ((kltp = kl_find_type(type_name, KLT_TYPE))) { ++ kl_print_type((void *)NULL, kltp, 0, C_SHOWOFFSET); ++ return; ++ } ++ if ((kltp = kl_find_type(type_name, KLT_TYPEDEF))) { ++ kdb_printf ("typedef %s:\n", type_name); ++ kl_print_type((void *)NULL, kltp, 0, C_SHOWOFFSET); ++ return; ++ } ++ } ++ if ((sp = kl_lkup_symname(type_name))) { ++ kdb_printf ("symbol %s value: %#lx\n", str, sp->s_addr); ++ kl_free_block((void *)sp); ++ return; ++ } ++ kdb_printf("could not find type or symbol information for %s\n", ++ type_name); ++ return; ++} ++ ++/* ++ * sizeof_type() ++ */ ++void ++sizeof_type(char *str) ++{ ++ char buffer[256], *type_name; ++ kltype_t *kltp; ++ ++ type_name = buffer; ++ strcpy(type_name, str); ++ ++ if ((kltp = kl_find_type(type_name, KLT_TYPE))) { ++ kdb_printf ("%s %d %#x\n", kltp->kl_typestr, ++ kltp->kl_size, kltp->kl_size); ++ return; ++ } ++ if ((kltp = kl_find_type(type_name, KLT_TYPEDEF))) { ++ kdb_printf ("%s %d %#x\n", kltp->kl_typestr, ++ kltp->kl_size, kltp->kl_size); ++ return; ++ } ++ kdb_printf("could not find type information for %s\n", type_name); ++} ++ ++EXPORT_SYMBOL(have_debug_file); ++EXPORT_SYMBOL(type_tree); ++EXPORT_SYMBOL(typedef_tree); ++ ++#if defined(CONFIG_X86_32) ++/* needed for i386: */ ++#include ++#include ++/* ++ * Generic C version of full 64 bit by 64 bit division ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * Code generated for this function might be very inefficient ++ * for some CPUs, can be overridden by linking arch-specific ++ * assembly versions such as arch/sparc/lib/udivdi.S ++ */ ++uint64_t ++__udivdi3(uint64_t dividend, uint64_t divisor) ++{ ++ uint32_t d = divisor; ++ /* Scale divisor to 32 bits */ ++ if (divisor > 0xffffffffULL) { ++ unsigned int shift = fls(divisor >> 32); ++ d = divisor >> shift; ++ dividend >>= shift; ++ } ++ /* avoid 64 bit division if possible */ ++ if (dividend >> 32) ++ do_div(dividend, d); ++ else ++ dividend = (uint32_t) dividend / d; ++ return dividend; ++} ++ ++int64_t ++__divdi3(int64_t dividend, int64_t divisor) ++{ ++ int32_t d = divisor; ++ /* Scale divisor to 32 bits */ ++ if (divisor > 0xffffffffLL) { ++ unsigned int shift = fls(divisor >> 32); ++ d = divisor >> shift; ++ dividend >>= shift; ++ } ++ /* avoid 64 bit division if possible */ ++ if (dividend >> 32) ++ do_div(dividend, d); ++ else ++ dividend = (int32_t) dividend / d; ++ return dividend; ++} ++ ++uint64_t ++__umoddi3(uint64_t dividend, uint64_t divisor) ++{ ++ return dividend - (__udivdi3(dividend, divisor) * divisor); ++} ++ ++int64_t ++__moddi3(int64_t dividend, int64_t divisor) ++{ ++ return dividend - (__divdi3(dividend, divisor) * divisor); ++} ++#endif /* CONFIG_x86_32 */ +--- /dev/null ++++ b/kdb/kdbmain.c +@@ -0,0 +1,4333 @@ ++/* ++ * Kernel Debugger Architecture Independent Main Code ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ * Copyright (C) 2000 Stephane Eranian ++ * Xscale (R) modifications copyright (C) 2003 Intel Corporation. ++ */ ++ ++/* ++ * Updated for Xscale (R) architecture support ++ * Eddie Dong 8 Jan 03 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(CONFIG_LKCD_DUMP) || defined(CONFIG_LKCD_DUMP_MODULE) ++#include ++#endif ++#include ++#include ++#ifdef CONFIG_KDB_KDUMP ++#include ++#endif ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++char kdb_debug_info_filename[256] = {""}; ++EXPORT_SYMBOL(kdb_debug_info_filename); ++#define GREP_LEN 256 ++char kdb_grep_string[GREP_LEN]; ++int kdb_grepping_flag; ++EXPORT_SYMBOL(kdb_grepping_flag); ++int kdb_grep_leading; ++int kdb_grep_trailing; ++ ++/* ++ * Kernel debugger state flags ++ */ ++volatile int kdb_flags; ++atomic_t kdb_event; ++atomic_t kdb_8250; ++ ++/* ++ * kdb_lock protects updates to kdb_initial_cpu. Used to ++ * single thread processors through the kernel debugger. ++ */ ++static DEFINE_SPINLOCK(kdb_lock); ++volatile int kdb_initial_cpu = -1; /* cpu number that owns kdb */ ++int kdb_seqno = 2; /* how many times kdb has been entered */ ++ ++volatile int kdb_nextline = 1; ++static volatile int kdb_new_cpu; /* Which cpu to switch to */ ++ ++volatile int kdb_state[NR_CPUS]; /* Per cpu state */ ++ ++const struct task_struct *kdb_current_task; ++EXPORT_SYMBOL(kdb_current_task); ++struct pt_regs *kdb_current_regs; ++ ++#ifdef CONFIG_KDB_OFF ++int kdb_on = 0; /* Default is off */ ++#else ++int kdb_on = 1; /* Default is on */ ++#endif /* CONFIG_KDB_OFF */ ++ ++const char *kdb_diemsg; ++static int kdb_go_count; ++#ifdef CONFIG_KDB_CONTINUE_CATASTROPHIC ++static unsigned int kdb_continue_catastrophic = CONFIG_KDB_CONTINUE_CATASTROPHIC; ++#else ++static unsigned int kdb_continue_catastrophic = 0; ++#endif ++ ++#ifdef kdba_setjmp ++ /* ++ * Must have a setjmp buffer per CPU. Switching cpus will ++ * cause the jump buffer to be setup for the new cpu, and ++ * subsequent switches (and pager aborts) will use the ++ * appropriate per-processor values. ++ */ ++kdb_jmp_buf *kdbjmpbuf; ++#endif /* kdba_setjmp */ ++ ++ /* ++ * kdb_commands describes the available commands. ++ */ ++static kdbtab_t *kdb_commands; ++static int kdb_max_commands; ++ ++typedef struct _kdbmsg { ++ int km_diag; /* kdb diagnostic */ ++ char *km_msg; /* Corresponding message text */ ++} kdbmsg_t; ++ ++#define KDBMSG(msgnum, text) \ ++ { KDB_##msgnum, text } ++ ++static kdbmsg_t kdbmsgs[] = { ++ KDBMSG(NOTFOUND,"Command Not Found"), ++ KDBMSG(ARGCOUNT, "Improper argument count, see usage."), ++ KDBMSG(BADWIDTH, "Illegal value for BYTESPERWORD use 1, 2, 4 or 8, 8 is only allowed on 64 bit systems"), ++ KDBMSG(BADRADIX, "Illegal value for RADIX use 8, 10 or 16"), ++ KDBMSG(NOTENV, "Cannot find environment variable"), ++ KDBMSG(NOENVVALUE, "Environment variable should have value"), ++ KDBMSG(NOTIMP, "Command not implemented"), ++ KDBMSG(ENVFULL, "Environment full"), ++ KDBMSG(ENVBUFFULL, "Environment buffer full"), ++ KDBMSG(TOOMANYBPT, "Too many breakpoints defined"), ++#ifdef CONFIG_CPU_XSCALE ++ KDBMSG(TOOMANYDBREGS, "More breakpoints than ibcr registers defined"), ++#else ++ KDBMSG(TOOMANYDBREGS, "More breakpoints than db registers defined"), ++#endif ++ KDBMSG(DUPBPT, "Duplicate breakpoint address"), ++ KDBMSG(BPTNOTFOUND, "Breakpoint not found"), ++ KDBMSG(BADMODE, "Invalid IDMODE"), ++ KDBMSG(BADINT, "Illegal numeric value"), ++ KDBMSG(INVADDRFMT, "Invalid symbolic address format"), ++ KDBMSG(BADREG, "Invalid register name"), ++ KDBMSG(BADCPUNUM, "Invalid cpu number"), ++ KDBMSG(BADLENGTH, "Invalid length field"), ++ KDBMSG(NOBP, "No Breakpoint exists"), ++ KDBMSG(BADADDR, "Invalid address"), ++}; ++#undef KDBMSG ++ ++static const int __nkdb_err = sizeof(kdbmsgs) / sizeof(kdbmsg_t); ++ ++ ++/* ++ * Initial environment. This is all kept static and local to ++ * this file. We don't want to rely on the memory allocation ++ * mechanisms in the kernel, so we use a very limited allocate-only ++ * heap for new and altered environment variables. The entire ++ * environment is limited to a fixed number of entries (add more ++ * to __env[] if required) and a fixed amount of heap (add more to ++ * KDB_ENVBUFSIZE if required). ++ */ ++ ++static char *__env[] = { ++#if defined(CONFIG_SMP) ++ "PROMPT=[%d]kdb> ", ++ "MOREPROMPT=[%d]more> ", ++#else ++ "PROMPT=kdb> ", ++ "MOREPROMPT=more> ", ++#endif ++ "RADIX=16", ++ "LINES=24", ++ "COLUMNS=80", ++ "MDCOUNT=8", /* lines of md output */ ++ "BTARGS=9", /* 9 possible args in bt */ ++ KDB_PLATFORM_ENV, ++ "DTABCOUNT=30", ++ "NOSECT=1", ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++ (char *)0, ++}; ++ ++static const int __nenv = (sizeof(__env) / sizeof(char *)); ++ ++/* external commands: */ ++int kdb_debuginfo_print(int argc, const char **argv); ++int kdb_pxhelp(int argc, const char **argv); ++int kdb_walkhelp(int argc, const char **argv); ++int kdb_walk(int argc, const char **argv); ++ ++/* ++ * kdb_serial_str is the sequence that the user must enter on a serial ++ * console to invoke kdb. It can be a single character such as "\001" ++ * (control-A) or multiple characters such as "\eKDB". NOTE: All except the ++ * last character are passed through to the application reading from the serial ++ * console. ++ * ++ * I tried to make the sequence a CONFIG_ option but most of CML1 cannot cope ++ * with '\' in strings. CML2 would have been able to do it but we lost CML2. ++ * KAO. ++ */ ++const char kdb_serial_str[] = "\eKDB"; ++EXPORT_SYMBOL(kdb_serial_str); ++ ++struct task_struct * ++kdb_curr_task(int cpu) ++{ ++ struct task_struct *p = curr_task(cpu); ++#ifdef _TIF_MCA_INIT ++ struct kdb_running_process *krp = kdb_running_process + cpu; ++ if ((task_thread_info(p)->flags & _TIF_MCA_INIT) && krp->p) ++ p = krp->p; ++#endif ++ return p; ++} ++ ++/* ++ * kdbgetenv ++ * ++ * This function will return the character string value of ++ * an environment variable. ++ * ++ * Parameters: ++ * match A character string representing an environment variable. ++ * Outputs: ++ * None. ++ * Returns: ++ * NULL No environment variable matches 'match' ++ * char* Pointer to string value of environment variable. ++ * Locking: ++ * No locking considerations required. ++ * Remarks: ++ */ ++char * ++kdbgetenv(const char *match) ++{ ++ char **ep = __env; ++ int matchlen = strlen(match); ++ int i; ++ ++ for(i=0; i<__nenv; i++) { ++ char *e = *ep++; ++ ++ if (!e) continue; ++ ++ if ((strncmp(match, e, matchlen) == 0) ++ && ((e[matchlen] == '\0') ++ ||(e[matchlen] == '='))) { ++ char *cp = strchr(e, '='); ++ return (cp ? ++cp :""); ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * kdballocenv ++ * ++ * This function is used to allocate bytes for environment entries. ++ * ++ * Parameters: ++ * match A character string representing a numeric value ++ * Outputs: ++ * *value the unsigned long represntation of the env variable 'match' ++ * Returns: ++ * Zero on success, a kdb diagnostic on failure. ++ * Locking: ++ * No locking considerations required. Must be called with all ++ * processors halted. ++ * Remarks: ++ * We use a static environment buffer (envbuffer) to hold the values ++ * of dynamically generated environment variables (see kdb_set). Buffer ++ * space once allocated is never free'd, so over time, the amount of space ++ * (currently 512 bytes) will be exhausted if env variables are changed ++ * frequently. ++ */ ++static char * ++kdballocenv(size_t bytes) ++{ ++#define KDB_ENVBUFSIZE 512 ++ static char envbuffer[KDB_ENVBUFSIZE]; ++ static int envbufsize; ++ char *ep = NULL; ++ ++ if ((KDB_ENVBUFSIZE - envbufsize) >= bytes) { ++ ep = &envbuffer[envbufsize]; ++ envbufsize += bytes; ++ } ++ return ep; ++} ++ ++/* ++ * kdbgetulenv ++ * ++ * This function will return the value of an unsigned long-valued ++ * environment variable. ++ * ++ * Parameters: ++ * match A character string representing a numeric value ++ * Outputs: ++ * *value the unsigned long represntation of the env variable 'match' ++ * Returns: ++ * Zero on success, a kdb diagnostic on failure. ++ * Locking: ++ * No locking considerations required. ++ * Remarks: ++ */ ++ ++static int ++kdbgetulenv(const char *match, unsigned long *value) ++{ ++ char *ep; ++ ++ ep = kdbgetenv(match); ++ if (!ep) return KDB_NOTENV; ++ if (strlen(ep) == 0) return KDB_NOENVVALUE; ++ ++ *value = simple_strtoul(ep, NULL, 0); ++ ++ return 0; ++} ++ ++/* ++ * kdbgetintenv ++ * ++ * This function will return the value of an integer-valued ++ * environment variable. ++ * ++ * Parameters: ++ * match A character string representing an integer-valued env variable ++ * Outputs: ++ * *value the integer representation of the environment variable 'match' ++ * Returns: ++ * Zero on success, a kdb diagnostic on failure. ++ * Locking: ++ * No locking considerations required. ++ * Remarks: ++ */ ++ ++int ++kdbgetintenv(const char *match, int *value) { ++ unsigned long val; ++ int diag; ++ ++ diag = kdbgetulenv(match, &val); ++ if (!diag) { ++ *value = (int) val; ++ } ++ return diag; ++} ++ ++/* ++ * kdbgetularg ++ * ++ * This function will convert a numeric string ++ * into an unsigned long value. ++ * ++ * Parameters: ++ * arg A character string representing a numeric value ++ * Outputs: ++ * *value the unsigned long represntation of arg. ++ * Returns: ++ * Zero on success, a kdb diagnostic on failure. ++ * Locking: ++ * No locking considerations required. ++ * Remarks: ++ */ ++ ++int ++kdbgetularg(const char *arg, unsigned long *value) ++{ ++ char *endp; ++ unsigned long val; ++ ++ val = simple_strtoul(arg, &endp, 0); ++ ++ if (endp == arg) { ++ /* ++ * Try base 16, for us folks too lazy to type the ++ * leading 0x... ++ */ ++ val = simple_strtoul(arg, &endp, 16); ++ if (endp == arg) ++ return KDB_BADINT; ++ } ++ ++ *value = val; ++ ++ return 0; ++} ++ ++/* ++ * kdb_set ++ * ++ * This function implements the 'set' command. Alter an existing ++ * environment variable or create a new one. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_set(int argc, const char **argv) ++{ ++ int i; ++ char *ep; ++ size_t varlen, vallen; ++ ++ /* ++ * we can be invoked two ways: ++ * set var=value argv[1]="var", argv[2]="value" ++ * set var = value argv[1]="var", argv[2]="=", argv[3]="value" ++ * - if the latter, shift 'em down. ++ */ ++ if (argc == 3) { ++ argv[2] = argv[3]; ++ argc--; ++ } ++ ++ if (argc != 2) ++ return KDB_ARGCOUNT; ++ ++ /* ++ * Check for internal variables ++ */ ++ if (strcmp(argv[1], "KDBDEBUG") == 0) { ++ unsigned int debugflags; ++ char *cp; ++ ++ debugflags = simple_strtoul(argv[2], &cp, 0); ++ if (cp == argv[2] || debugflags & ~KDB_DEBUG_FLAG_MASK) { ++ kdb_printf("kdb: illegal debug flags '%s'\n", ++ argv[2]); ++ return 0; ++ } ++ kdb_flags = (kdb_flags & ~(KDB_DEBUG_FLAG_MASK << KDB_DEBUG_FLAG_SHIFT)) ++ | (debugflags << KDB_DEBUG_FLAG_SHIFT); ++ ++ return 0; ++ } ++ ++ /* ++ * Tokenizer squashed the '=' sign. argv[1] is variable ++ * name, argv[2] = value. ++ */ ++ varlen = strlen(argv[1]); ++ vallen = strlen(argv[2]); ++ ep = kdballocenv(varlen + vallen + 2); ++ if (ep == (char *)0) ++ return KDB_ENVBUFFULL; ++ ++ sprintf(ep, "%s=%s", argv[1], argv[2]); ++ ++ ep[varlen+vallen+1]='\0'; ++ ++ for(i=0; i<__nenv; i++) { ++ if (__env[i] ++ && ((strncmp(__env[i], argv[1], varlen)==0) ++ && ((__env[i][varlen] == '\0') ++ || (__env[i][varlen] == '=')))) { ++ __env[i] = ep; ++ return 0; ++ } ++ } ++ ++ /* ++ * Wasn't existing variable. Fit into slot. ++ */ ++ for(i=0; i<__nenv-1; i++) { ++ if (__env[i] == (char *)0) { ++ __env[i] = ep; ++ return 0; ++ } ++ } ++ ++ return KDB_ENVFULL; ++} ++ ++static int ++kdb_check_regs(void) ++{ ++ if (!kdb_current_regs) { ++ kdb_printf("No current kdb registers." ++ " You may need to select another task\n"); ++ return KDB_BADREG; ++ } ++ return 0; ++} ++ ++/* ++ * kdbgetaddrarg ++ * ++ * This function is responsible for parsing an ++ * address-expression and returning the value of ++ * the expression, symbol name, and offset to the caller. ++ * ++ * The argument may consist of a numeric value (decimal or ++ * hexidecimal), a symbol name, a register name (preceeded ++ * by the percent sign), an environment variable with a numeric ++ * value (preceeded by a dollar sign) or a simple arithmetic ++ * expression consisting of a symbol name, +/-, and a numeric ++ * constant value (offset). ++ * ++ * Parameters: ++ * argc - count of arguments in argv ++ * argv - argument vector ++ * *nextarg - index to next unparsed argument in argv[] ++ * regs - Register state at time of KDB entry ++ * Outputs: ++ * *value - receives the value of the address-expression ++ * *offset - receives the offset specified, if any ++ * *name - receives the symbol name, if any ++ * *nextarg - index to next unparsed argument in argv[] ++ * ++ * Returns: ++ * zero is returned on success, a kdb diagnostic code is ++ * returned on error. ++ * ++ * Locking: ++ * No locking requirements. ++ * ++ * Remarks: ++ * ++ */ ++ ++int ++kdbgetaddrarg(int argc, const char **argv, int *nextarg, ++ kdb_machreg_t *value, long *offset, ++ char **name) ++{ ++ kdb_machreg_t addr; ++ unsigned long off = 0; ++ int positive; ++ int diag; ++ int found = 0; ++ char *symname; ++ char symbol = '\0'; ++ char *cp; ++ kdb_symtab_t symtab; ++ ++ /* ++ * Process arguments which follow the following syntax: ++ * ++ * symbol | numeric-address [+/- numeric-offset] ++ * %register ++ * $environment-variable ++ */ ++ ++ if (*nextarg > argc) { ++ return KDB_ARGCOUNT; ++ } ++ ++ symname = (char *)argv[*nextarg]; ++ ++ /* ++ * If there is no whitespace between the symbol ++ * or address and the '+' or '-' symbols, we ++ * remember the character and replace it with a ++ * null so the symbol/value can be properly parsed ++ */ ++ if ((cp = strpbrk(symname, "+-")) != NULL) { ++ symbol = *cp; ++ *cp++ = '\0'; ++ } ++ ++ if (symname[0] == '$') { ++ diag = kdbgetulenv(&symname[1], &addr); ++ if (diag) ++ return diag; ++ } else if (symname[0] == '%') { ++ if ((diag = kdb_check_regs())) ++ return diag; ++ diag = kdba_getregcontents(&symname[1], kdb_current_regs, &addr); ++ if (diag) ++ return diag; ++ } else { ++ found = kdbgetsymval(symname, &symtab); ++ if (found) { ++ addr = symtab.sym_start; ++ } else { ++ diag = kdbgetularg(argv[*nextarg], &addr); ++ if (diag) ++ return diag; ++ } ++ } ++ ++ if (!found) ++ found = kdbnearsym(addr, &symtab); ++ ++ (*nextarg)++; ++ ++ if (name) ++ *name = symname; ++ if (value) ++ *value = addr; ++ if (offset && name && *name) ++ *offset = addr - symtab.sym_start; ++ ++ if ((*nextarg > argc) ++ && (symbol == '\0')) ++ return 0; ++ ++ /* ++ * check for +/- and offset ++ */ ++ ++ if (symbol == '\0') { ++ if ((argv[*nextarg][0] != '+') ++ && (argv[*nextarg][0] != '-')) { ++ /* ++ * Not our argument. Return. ++ */ ++ return 0; ++ } else { ++ positive = (argv[*nextarg][0] == '+'); ++ (*nextarg)++; ++ } ++ } else ++ positive = (symbol == '+'); ++ ++ /* ++ * Now there must be an offset! ++ */ ++ if ((*nextarg > argc) ++ && (symbol == '\0')) { ++ return KDB_INVADDRFMT; ++ } ++ ++ if (!symbol) { ++ cp = (char *)argv[*nextarg]; ++ (*nextarg)++; ++ } ++ ++ diag = kdbgetularg(cp, &off); ++ if (diag) ++ return diag; ++ ++ if (!positive) ++ off = -off; ++ ++ if (offset) ++ *offset += off; ++ ++ if (value) ++ *value += off; ++ ++ return 0; ++} ++ ++static void ++kdb_cmderror(int diag) ++{ ++ int i; ++ ++ if (diag >= 0) { ++ kdb_printf("no error detected (diagnostic is %d)\n", diag); ++ return; ++ } ++ ++ for(i=0; i<__nkdb_err; i++) { ++ if (kdbmsgs[i].km_diag == diag) { ++ kdb_printf("diag: %d: %s\n", diag, kdbmsgs[i].km_msg); ++ return; ++ } ++ } ++ ++ kdb_printf("Unknown diag %d\n", -diag); ++} ++ ++/* ++ * kdb_defcmd, kdb_defcmd2 ++ * ++ * This function implements the 'defcmd' command which defines one ++ * command as a set of other commands, terminated by endefcmd. ++ * kdb_defcmd processes the initial 'defcmd' command, kdb_defcmd2 ++ * is invoked from kdb_parse for the following commands until ++ * 'endefcmd'. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++struct defcmd_set { ++ int count; ++ int usable; ++ char *name; ++ char *usage; ++ char *help; ++ char **command; ++}; ++static struct defcmd_set *defcmd_set; ++static int defcmd_set_count; ++static int defcmd_in_progress; ++ ++/* Forward references */ ++static int kdb_exec_defcmd(int argc, const char **argv); ++ ++static int ++kdb_defcmd2(const char *cmdstr, const char *argv0) ++{ ++ struct defcmd_set *s = defcmd_set + defcmd_set_count - 1; ++ char **save_command = s->command; ++ if (strcmp(argv0, "endefcmd") == 0) { ++ defcmd_in_progress = 0; ++ if (!s->count) ++ s->usable = 0; ++ if (s->usable) ++ kdb_register(s->name, kdb_exec_defcmd, s->usage, s->help, 0); ++ return 0; ++ } ++ if (!s->usable) ++ return KDB_NOTIMP; ++ s->command = kmalloc((s->count + 1) * sizeof(*(s->command)), GFP_KDB); ++ if (!s->command) { ++ kdb_printf("Could not allocate new kdb_defcmd table for %s\n", cmdstr); ++ s->usable = 0; ++ return KDB_NOTIMP; ++ } ++ memcpy(s->command, save_command, s->count * sizeof(*(s->command))); ++ s->command[s->count++] = kdb_strdup(cmdstr, GFP_KDB); ++ kfree(save_command); ++ return 0; ++} ++ ++static int ++kdb_defcmd(int argc, const char **argv) ++{ ++ struct defcmd_set *save_defcmd_set = defcmd_set, *s; ++ if (defcmd_in_progress) { ++ kdb_printf("kdb: nested defcmd detected, assuming missing endefcmd\n"); ++ kdb_defcmd2("endefcmd", "endefcmd"); ++ } ++ if (argc == 0) { ++ int i; ++ for (s = defcmd_set; s < defcmd_set + defcmd_set_count; ++s) { ++ kdb_printf("defcmd %s \"%s\" \"%s\"\n", s->name, s->usage, s->help); ++ for (i = 0; i < s->count; ++i) ++ kdb_printf("%s", s->command[i]); ++ kdb_printf("endefcmd\n"); ++ } ++ return 0; ++ } ++ if (argc != 3) ++ return KDB_ARGCOUNT; ++ defcmd_set = kmalloc((defcmd_set_count + 1) * sizeof(*defcmd_set), GFP_KDB); ++ if (!defcmd_set) { ++ kdb_printf("Could not allocate new defcmd_set entry for %s\n", argv[1]); ++ defcmd_set = save_defcmd_set; ++ return KDB_NOTIMP; ++ } ++ memcpy(defcmd_set, save_defcmd_set, defcmd_set_count * sizeof(*defcmd_set)); ++ kfree(save_defcmd_set); ++ s = defcmd_set + defcmd_set_count; ++ memset(s, 0, sizeof(*s)); ++ s->usable = 1; ++ s->name = kdb_strdup(argv[1], GFP_KDB); ++ s->usage = kdb_strdup(argv[2], GFP_KDB); ++ s->help = kdb_strdup(argv[3], GFP_KDB); ++ if (s->usage[0] == '"') { ++ strcpy(s->usage, s->usage+1); ++ s->usage[strlen(s->usage)-1] = '\0'; ++ } ++ if (s->help[0] == '"') { ++ strcpy(s->help, s->help+1); ++ s->help[strlen(s->help)-1] = '\0'; ++ } ++ ++defcmd_set_count; ++ defcmd_in_progress = 1; ++ return 0; ++} ++ ++/* ++ * kdb_exec_defcmd ++ * ++ * Execute the set of commands associated with this defcmd name. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_exec_defcmd(int argc, const char **argv) ++{ ++ int i, ret; ++ struct defcmd_set *s; ++ if (argc != 0) ++ return KDB_ARGCOUNT; ++ for (s = defcmd_set, i = 0; i < defcmd_set_count; ++i, ++s) { ++ if (strcmp(s->name, argv[0]) == 0) ++ break; ++ } ++ if (i == defcmd_set_count) { ++ kdb_printf("kdb_exec_defcmd: could not find commands for %s\n", argv[0]); ++ return KDB_NOTIMP; ++ } ++ for (i = 0; i < s->count; ++i) { ++ /* Recursive use of kdb_parse, do not use argv after this point */ ++ argv = NULL; ++ kdb_printf("[%s]kdb> %s\n", s->name, s->command[i]); ++ if ((ret = kdb_parse(s->command[i]))) ++ return ret; ++ } ++ return 0; ++} ++ ++/* Command history */ ++#define KDB_CMD_HISTORY_COUNT 32 ++#define CMD_BUFLEN 200 /* kdb_printf: max printline size == 256 */ ++static unsigned int cmd_head=0, cmd_tail=0; ++static unsigned int cmdptr; ++static char cmd_hist[KDB_CMD_HISTORY_COUNT][CMD_BUFLEN]; ++static char cmd_cur[CMD_BUFLEN]; ++ ++/* ++ * The "str" argument may point to something like | grep xyz ++ * ++ */ ++static void ++parse_grep(const char *str) ++{ ++ int len; ++ char *cp = (char *)str, *cp2; ++ ++ /* sanity check: we should have been called with the \ first */ ++ if (*cp != '|') ++ return; ++ cp++; ++ while (isspace(*cp)) cp++; ++ if (strncmp(cp,"grep ",5)) { ++ kdb_printf ("invalid 'pipe', see grephelp\n"); ++ return; ++ } ++ cp += 5; ++ while (isspace(*cp)) cp++; ++ cp2 = strchr(cp, '\n'); ++ if (cp2) ++ *cp2 = '\0'; /* remove the trailing newline */ ++ len = strlen(cp); ++ if (len == 0) { ++ kdb_printf ("invalid 'pipe', see grephelp\n"); ++ return; ++ } ++ /* now cp points to a nonzero length search string */ ++ if (*cp == '"') { ++ /* allow it be "x y z" by removing the "'s - there must ++ be two of them */ ++ cp++; ++ cp2 = strchr(cp, '"'); ++ if (!cp2) { ++ kdb_printf ("invalid quoted string, see grephelp\n"); ++ return; ++ } ++ *cp2 = '\0'; /* end the string where the 2nd " was */ ++ } ++ kdb_grep_leading = 0; ++ if (*cp == '^') { ++ kdb_grep_leading = 1; ++ cp++; ++ } ++ len = strlen(cp); ++ kdb_grep_trailing = 0; ++ if (*(cp+len-1) == '$') { ++ kdb_grep_trailing = 1; ++ *(cp+len-1) = '\0'; ++ } ++ len = strlen(cp); ++ if (!len) return; ++ if (len >= GREP_LEN) { ++ kdb_printf ("search string too long\n"); ++ return; ++ } ++ strcpy(kdb_grep_string, cp); ++ kdb_grepping_flag++; ++ return; ++} ++ ++/* ++ * kdb_parse ++ * ++ * Parse the command line, search the command table for a ++ * matching command and invoke the command function. ++ * This function may be called recursively, if it is, the second call ++ * will overwrite argv and cbuf. It is the caller's responsibility to ++ * save their argv if they recursively call kdb_parse(). ++ * ++ * Parameters: ++ * cmdstr The input command line to be parsed. ++ * regs The registers at the time kdb was entered. ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic if failure. ++ * Locking: ++ * None. ++ * Remarks: ++ * Limited to 20 tokens. ++ * ++ * Real rudimentary tokenization. Basically only whitespace ++ * is considered a token delimeter (but special consideration ++ * is taken of the '=' sign as used by the 'set' command). ++ * ++ * The algorithm used to tokenize the input string relies on ++ * there being at least one whitespace (or otherwise useless) ++ * character between tokens as the character immediately following ++ * the token is altered in-place to a null-byte to terminate the ++ * token string. ++ */ ++ ++#define MAXARGC 20 ++ ++int ++kdb_parse(const char *cmdstr) ++{ ++ static char *argv[MAXARGC]; ++ static int argc = 0; ++ static char cbuf[CMD_BUFLEN+2]; ++ char *cp; ++ char *cpp, quoted; ++ kdbtab_t *tp; ++ int i, escaped, ignore_errors = 0, check_grep; ++ ++ /* ++ * First tokenize the command string. ++ */ ++ cp = (char *)cmdstr; ++ kdb_grepping_flag = check_grep = 0; ++ ++ if (KDB_FLAG(CMD_INTERRUPT)) { ++ /* Previous command was interrupted, newline must not repeat the command */ ++ KDB_FLAG_CLEAR(CMD_INTERRUPT); ++ argc = 0; /* no repeat */ ++ } ++ ++ if (*cp != '\n' && *cp != '\0') { ++ argc = 0; ++ cpp = cbuf; ++ while (*cp) { ++ /* skip whitespace */ ++ while (isspace(*cp)) cp++; ++ if ((*cp == '\0') || (*cp == '\n') || (*cp == '#' && !defcmd_in_progress)) ++ break; ++ /* special case: check for | grep pattern */ ++ if (*cp == '|') { ++ check_grep++; ++ break; ++ } ++ if (cpp >= cbuf + CMD_BUFLEN) { ++ kdb_printf("kdb_parse: command buffer overflow, command ignored\n%s\n", cmdstr); ++ return KDB_NOTFOUND; ++ } ++ if (argc >= MAXARGC - 1) { ++ kdb_printf("kdb_parse: too many arguments, command ignored\n%s\n", cmdstr); ++ return KDB_NOTFOUND; ++ } ++ argv[argc++] = cpp; ++ escaped = 0; ++ quoted = '\0'; ++ /* Copy to next unquoted and unescaped whitespace or '=' */ ++ while (*cp && *cp != '\n' && (escaped || quoted || !isspace(*cp))) { ++ if (cpp >= cbuf + CMD_BUFLEN) ++ break; ++ if (escaped) { ++ escaped = 0; ++ *cpp++ = *cp++; ++ continue; ++ } ++ if (*cp == '\\') { ++ escaped = 1; ++ ++cp; ++ continue; ++ } ++ if (*cp == quoted) { ++ quoted = '\0'; ++ } else if (*cp == '\'' || *cp == '"') { ++ quoted = *cp; ++ } ++ if ((*cpp = *cp++) == '=' && !quoted) ++ break; ++ ++cpp; ++ } ++ *cpp++ = '\0'; /* Squash a ws or '=' character */ ++ } ++ } ++ if (!argc) ++ return 0; ++ if (check_grep) ++ parse_grep(cp); ++ if (defcmd_in_progress) { ++ int result = kdb_defcmd2(cmdstr, argv[0]); ++ if (!defcmd_in_progress) { ++ argc = 0; /* avoid repeat on endefcmd */ ++ *(argv[0]) = '\0'; ++ } ++ return result; ++ } ++ if (argv[0][0] == '-' && argv[0][1] && (argv[0][1] < '0' || argv[0][1] > '9')) { ++ ignore_errors = 1; ++ ++argv[0]; ++ } ++ ++ for(tp=kdb_commands, i=0; i < kdb_max_commands; i++,tp++) { ++ if (tp->cmd_name) { ++ /* ++ * If this command is allowed to be abbreviated, ++ * check to see if this is it. ++ */ ++ ++ if (tp->cmd_minlen ++ && (strlen(argv[0]) <= tp->cmd_minlen)) { ++ if (strncmp(argv[0], ++ tp->cmd_name, ++ tp->cmd_minlen) == 0) { ++ break; ++ } ++ } ++ ++ if (strcmp(argv[0], tp->cmd_name)==0) { ++ break; ++ } ++ } ++ } ++ ++ /* ++ * If we don't find a command by this name, see if the first ++ * few characters of this match any of the known commands. ++ * e.g., md1c20 should match md. ++ */ ++ if (i == kdb_max_commands) { ++ for(tp=kdb_commands, i=0; i < kdb_max_commands; i++,tp++) { ++ if (tp->cmd_name) { ++ if (strncmp(argv[0], ++ tp->cmd_name, ++ strlen(tp->cmd_name))==0) { ++ break; ++ } ++ } ++ } ++ } ++ ++ if (i < kdb_max_commands) { ++ int result; ++ KDB_STATE_SET(CMD); ++ result = (*tp->cmd_func)(argc-1, ++ (const char**)argv); ++ if (result && ignore_errors && result > KDB_CMD_GO) ++ result = 0; ++ KDB_STATE_CLEAR(CMD); ++ switch (tp->cmd_repeat) { ++ case KDB_REPEAT_NONE: ++ argc = 0; ++ if (argv[0]) ++ *(argv[0]) = '\0'; ++ break; ++ case KDB_REPEAT_NO_ARGS: ++ argc = 1; ++ if (argv[1]) ++ *(argv[1]) = '\0'; ++ break; ++ case KDB_REPEAT_WITH_ARGS: ++ break; ++ } ++ return result; ++ } ++ ++ /* ++ * If the input with which we were presented does not ++ * map to an existing command, attempt to parse it as an ++ * address argument and display the result. Useful for ++ * obtaining the address of a variable, or the nearest symbol ++ * to an address contained in a register. ++ */ ++ { ++ kdb_machreg_t value; ++ char *name = NULL; ++ long offset; ++ int nextarg = 0; ++ ++ if (kdbgetaddrarg(0, (const char **)argv, &nextarg, ++ &value, &offset, &name)) { ++ return KDB_NOTFOUND; ++ } ++ ++ kdb_printf("%s = ", argv[0]); ++ kdb_symbol_print(value, NULL, KDB_SP_DEFAULT); ++ kdb_printf("\n"); ++ return 0; ++ } ++} ++ ++ ++static int ++handle_ctrl_cmd(char *cmd) ++{ ++#define CTRL_P 16 ++#define CTRL_N 14 ++ ++ /* initial situation */ ++ if (cmd_head == cmd_tail) return 0; ++ ++ switch(*cmd) { ++ case CTRL_P: ++ if (cmdptr != cmd_tail) ++ cmdptr = (cmdptr-1) % KDB_CMD_HISTORY_COUNT; ++ strncpy(cmd_cur, cmd_hist[cmdptr], CMD_BUFLEN); ++ return 1; ++ case CTRL_N: ++ if (cmdptr != cmd_head) ++ cmdptr = (cmdptr+1) % KDB_CMD_HISTORY_COUNT; ++ strncpy(cmd_cur, cmd_hist[cmdptr], CMD_BUFLEN); ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * kdb_do_dump ++ * ++ * Call the dump() function if the kernel is configured for LKCD. ++ * Inputs: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. dump() may or may not return. ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static void ++kdb_do_dump(void) ++{ ++#if defined(CONFIG_LKCD_DUMP) || defined(CONFIG_LKCD_DUMP_MODULE) ++ kdb_printf("Forcing dump (if configured)\n"); ++ console_loglevel = 8; /* to see the dump messages */ ++ dump("kdb_do_dump"); ++#endif ++} ++ ++/* ++ * kdb_reboot ++ * ++ * This function implements the 'reboot' command. Reboot the system ++ * immediately. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * Shouldn't return from this function. ++ */ ++ ++static int ++kdb_reboot(int argc, const char **argv) ++{ ++ emergency_restart(); ++ kdb_printf("Hmm, kdb_reboot did not reboot, spinning here\n"); ++ while (1) {}; ++ /* NOTREACHED */ ++ return 0; ++} ++ ++#ifdef CONFIG_KDB_KDUMP ++ ++int kdb_kdump_state = KDB_KDUMP_RESET; /* KDB kdump state */ ++ ++static int kdb_cpu(int argc, const char **argv); ++ ++/* ++ * kdb_kdump_check ++ * ++ * This is where the kdump on monarch cpu is handled. ++ * ++ */ ++void kdb_kdump_check(struct pt_regs *regs) ++{ ++ if (kdb_kdump_state != KDB_KDUMP_RESET) { ++ crash_kexec(regs); ++ ++ /* If the call above returned then something ++ didn't work */ ++ kdb_printf("kdb_kdump_check: crash_kexec failed!\n"); ++ kdb_printf(" Please check if the kdump kernel has been properly loaded\n"); ++ kdb_kdump_state = KDB_KDUMP_RESET; ++ } ++} ++ ++ ++/* ++ * kdb_kdump ++ * ++ * This function implements the 'kdump' command. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * envp environment vector ++ * regs registers at time kdb was entered. ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * Shouldn't return from this function. ++ */ ++ ++static int ++kdb_kdump(int argc, const char **argv) ++{ ++ char cpu_id[6]; /* up to 99,999 cpus */ ++ const char *cpu_argv[] = {NULL, cpu_id, NULL}; ++ int ret; ++ ++ kdb_kdump_state = KDB_KDUMP_KDUMP; ++ /* Switch back to the initial cpu before process kdump command */ ++ if (smp_processor_id() != kdb_initial_cpu) { ++ sprintf(cpu_id, "%d", kdb_initial_cpu); ++ ret = kdb_cpu(1, cpu_argv); ++ if (ret != KDB_CMD_CPU) { ++ kdb_printf("kdump: Failed to switch to initial cpu %d;" ++ " aborted\n", kdb_initial_cpu); ++ kdb_kdump_state = KDB_KDUMP_RESET; ++ } ++ } else ++ ret = KDB_CMD_CPU; ++ ++ return ret; ++} ++ ++#endif /* CONFIG_KDB_KDUMP */ ++ ++static int ++kdb_quiet(int reason) ++{ ++ return (reason == KDB_REASON_CPU_UP || reason == KDB_REASON_SILENT); ++} ++ ++/* ++ * kdb_local ++ * ++ * The main code for kdb. This routine is invoked on a specific ++ * processor, it is not global. The main kdb() routine ensures ++ * that only one processor at a time is in this routine. This ++ * code is called with the real reason code on the first entry ++ * to a kdb session, thereafter it is called with reason SWITCH, ++ * even if the user goes back to the original cpu. ++ * ++ * Inputs: ++ * reason The reason KDB was invoked ++ * error The hardware-defined error code ++ * regs The exception frame at time of fault/breakpoint. NULL ++ * for reason SILENT or CPU_UP, otherwise valid. ++ * db_result Result code from the break or debug point. ++ * Returns: ++ * 0 KDB was invoked for an event which it wasn't responsible ++ * 1 KDB handled the event for which it was invoked. ++ * KDB_CMD_GO User typed 'go'. ++ * KDB_CMD_CPU User switched to another cpu. ++ * KDB_CMD_SS Single step. ++ * KDB_CMD_SSB Single step until branch. ++ * Locking: ++ * none ++ * Remarks: ++ * none ++ */ ++ ++static int ++kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs, kdb_dbtrap_t db_result) ++{ ++ char *cmdbuf; ++ int diag; ++ struct task_struct *kdb_current = kdb_curr_task(smp_processor_id()); ++ ++#ifdef CONFIG_KDB_KDUMP ++ kdb_kdump_check(regs); ++#endif ++ ++ /* If kdb has been entered for an event which has been/will be ++ * recovered then silently return. We have to get this far into kdb in ++ * order to synchronize all the cpus, typically only one cpu (monarch) ++ * knows that the event is recoverable but the other cpus (slaves) may ++ * also be driven into kdb before that decision is made by the monarch. ++ * ++ * To pause in kdb even for recoverable events, 'set RECOVERY_PAUSE 1' ++ */ ++ KDB_DEBUG_STATE("kdb_local 1", reason); ++ if (reason == KDB_REASON_ENTER ++ && KDB_FLAG(RECOVERY) ++ && !KDB_FLAG(CATASTROPHIC)) { ++ int recovery_pause = 0; ++ kdbgetintenv("RECOVERY_PAUSE", &recovery_pause); ++ if (recovery_pause == 0) ++ reason = KDB_REASON_SILENT; ++ else ++ kdb_printf("%s: Recoverable error detected but" ++ " RECOVERY_PAUSE is set, staying in KDB\n", ++ __FUNCTION__); ++ } ++ ++ KDB_DEBUG_STATE("kdb_local 2", reason); ++ kdb_go_count = 0; ++ if (kdb_quiet(reason)) { ++ /* no message */ ++ } else if (reason == KDB_REASON_DEBUG) { ++ /* special case below */ ++ } else { ++ kdb_printf("\nEntering kdb (current=0x%p, pid %d) ", kdb_current, kdb_current->pid); ++#if defined(CONFIG_SMP) ++ kdb_printf("on processor %d ", smp_processor_id()); ++#endif ++ } ++ ++ switch (reason) { ++ case KDB_REASON_DEBUG: ++ { ++ /* ++ * If re-entering kdb after a single step ++ * command, don't print the message. ++ */ ++ switch(db_result) { ++ case KDB_DB_BPT: ++ kdb_printf("\nEntering kdb (0x%p, pid %d) ", kdb_current, kdb_current->pid); ++#if defined(CONFIG_SMP) ++ kdb_printf("on processor %d ", smp_processor_id()); ++#endif ++ kdb_printf("due to Debug @ " kdb_machreg_fmt "\n", kdba_getpc(regs)); ++ break; ++ case KDB_DB_SSB: ++ /* ++ * In the midst of ssb command. Just return. ++ */ ++ KDB_DEBUG_STATE("kdb_local 3", reason); ++ return KDB_CMD_SSB; /* Continue with SSB command */ ++ ++ break; ++ case KDB_DB_SS: ++ break; ++ case KDB_DB_SSBPT: ++ KDB_DEBUG_STATE("kdb_local 4", reason); ++ return 1; /* kdba_db_trap did the work */ ++ default: ++ kdb_printf("kdb: Bad result from kdba_db_trap: %d\n", ++ db_result); ++ break; ++ } ++ ++ } ++ break; ++ case KDB_REASON_ENTER: ++ if (KDB_STATE(KEYBOARD)) ++ kdb_printf("due to Keyboard Entry\n"); ++ else { ++ kdb_printf("due to KDB_ENTER()\n"); ++ } ++ break; ++ case KDB_REASON_KEYBOARD: ++ KDB_STATE_SET(KEYBOARD); ++ kdb_printf("due to Keyboard Entry\n"); ++ break; ++ case KDB_REASON_ENTER_SLAVE: /* drop through, slaves only get released via cpu switch */ ++ case KDB_REASON_SWITCH: ++ kdb_printf("due to cpu switch\n"); ++ if (KDB_STATE(GO_SWITCH)) { ++ KDB_STATE_CLEAR(GO_SWITCH); ++ KDB_DEBUG_STATE("kdb_local 5", reason); ++ return KDB_CMD_GO; ++ } ++ break; ++ case KDB_REASON_OOPS: ++ kdb_printf("Oops: %s\n", kdb_diemsg); ++ kdb_printf("due to oops @ " kdb_machreg_fmt "\n", kdba_getpc(regs)); ++ kdba_dumpregs(regs, NULL, NULL); ++ break; ++ case KDB_REASON_NMI: ++ kdb_printf("due to NonMaskable Interrupt @ " kdb_machreg_fmt "\n", ++ kdba_getpc(regs)); ++ kdba_dumpregs(regs, NULL, NULL); ++ break; ++ case KDB_REASON_BREAK: ++ kdb_printf("due to Breakpoint @ " kdb_machreg_fmt "\n", kdba_getpc(regs)); ++ /* ++ * Determine if this breakpoint is one that we ++ * are interested in. ++ */ ++ if (db_result != KDB_DB_BPT) { ++ kdb_printf("kdb: error return from kdba_bp_trap: %d\n", db_result); ++ KDB_DEBUG_STATE("kdb_local 6", reason); ++ return 0; /* Not for us, dismiss it */ ++ } ++ break; ++ case KDB_REASON_RECURSE: ++ kdb_printf("due to Recursion @ " kdb_machreg_fmt "\n", kdba_getpc(regs)); ++ break; ++ case KDB_REASON_CPU_UP: ++ case KDB_REASON_SILENT: ++ KDB_DEBUG_STATE("kdb_local 7", reason); ++ if (reason == KDB_REASON_CPU_UP) ++ kdba_cpu_up(); ++ return KDB_CMD_GO; /* Silent entry, silent exit */ ++ break; ++ default: ++ kdb_printf("kdb: unexpected reason code: %d\n", reason); ++ KDB_DEBUG_STATE("kdb_local 8", reason); ++ return 0; /* Not for us, dismiss it */ ++ } ++ ++ kdba_local_arch_setup(); ++ ++ kdba_set_current_task(kdb_current); ++ ++ while (1) { ++ /* ++ * Initialize pager context. ++ */ ++ kdb_nextline = 1; ++ KDB_STATE_CLEAR(SUPPRESS); ++#ifdef kdba_setjmp ++ /* ++ * Use kdba_setjmp/kdba_longjmp to break out of ++ * the pager early and to attempt to recover from kdb errors. ++ */ ++ KDB_STATE_CLEAR(LONGJMP); ++ if (kdbjmpbuf) { ++ if (kdba_setjmp(&kdbjmpbuf[smp_processor_id()])) { ++ /* Command aborted (usually in pager) */ ++ continue; ++ } ++ else ++ KDB_STATE_SET(LONGJMP); ++ } ++#endif /* kdba_setjmp */ ++ ++ cmdbuf = cmd_cur; ++ *cmdbuf = '\0'; ++ *(cmd_hist[cmd_head])='\0'; ++ ++ if (KDB_FLAG(ONLY_DO_DUMP)) { ++ /* kdb is off but a catastrophic error requires a dump. ++ * Take the dump and reboot. ++ * Turn on logging so the kdb output appears in the log ++ * buffer in the dump. ++ */ ++ const char *setargs[] = { "set", "LOGGING", "1" }; ++ kdb_set(2, setargs); ++ kdb_do_dump(); ++ kdb_reboot(0, NULL); ++ /*NOTREACHED*/ ++ } ++ ++do_full_getstr: ++#if defined(CONFIG_SMP) ++ snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT"), smp_processor_id()); ++#else ++ snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT")); ++#endif ++ if (defcmd_in_progress) ++ strncat(kdb_prompt_str, "[defcmd]", CMD_BUFLEN); ++ ++ /* ++ * Fetch command from keyboard ++ */ ++ cmdbuf = kdb_getstr(cmdbuf, CMD_BUFLEN, kdb_prompt_str); ++ if (*cmdbuf != '\n') { ++ if (*cmdbuf < 32) { ++ if(cmdptr == cmd_head) { ++ strncpy(cmd_hist[cmd_head], cmd_cur, CMD_BUFLEN); ++ *(cmd_hist[cmd_head]+strlen(cmd_hist[cmd_head])-1) = '\0'; ++ } ++ if(!handle_ctrl_cmd(cmdbuf)) ++ *(cmd_cur+strlen(cmd_cur)-1) = '\0'; ++ cmdbuf = cmd_cur; ++ goto do_full_getstr; ++ } ++ else ++ strncpy(cmd_hist[cmd_head], cmd_cur, CMD_BUFLEN); ++ ++ cmd_head = (cmd_head+1) % KDB_CMD_HISTORY_COUNT; ++ if (cmd_head == cmd_tail) cmd_tail = (cmd_tail+1) % KDB_CMD_HISTORY_COUNT; ++ ++ } ++ ++ cmdptr = cmd_head; ++ diag = kdb_parse(cmdbuf); ++ if (diag == KDB_NOTFOUND) { ++ kdb_printf("Unknown kdb command: '%s'\n", cmdbuf); ++ diag = 0; ++ } ++ if (diag == KDB_CMD_GO ++ || diag == KDB_CMD_CPU ++ || diag == KDB_CMD_SS ++ || diag == KDB_CMD_SSB) ++ break; ++ ++ if (diag) ++ kdb_cmderror(diag); ++ } ++ ++ kdba_local_arch_cleanup(); ++ ++ KDB_DEBUG_STATE("kdb_local 9", diag); ++ return diag; ++} ++ ++ ++/* ++ * kdb_print_state ++ * ++ * Print the state data for the current processor for debugging. ++ * ++ * Inputs: ++ * text Identifies the debug point ++ * value Any integer value to be printed, e.g. reason code. ++ * Returns: ++ * None. ++ * Locking: ++ * none ++ * Remarks: ++ * none ++ */ ++ ++void kdb_print_state(const char *text, int value) ++{ ++ kdb_printf("state: %s cpu %d value %d initial %d state %x\n", ++ text, smp_processor_id(), value, kdb_initial_cpu, kdb_state[smp_processor_id()]); ++} ++ ++/* ++ * kdb_previous_event ++ * ++ * Return a count of cpus that are leaving kdb, i.e. the number ++ * of processors that are still handling the previous kdb event. ++ * ++ * Inputs: ++ * None. ++ * Returns: ++ * Count of cpus in previous event. ++ * Locking: ++ * none ++ * Remarks: ++ * none ++ */ ++ ++static int ++kdb_previous_event(void) ++{ ++ int i, leaving = 0; ++ for (i = 0; i < NR_CPUS; ++i) { ++ if (KDB_STATE_CPU(LEAVING, i)) ++ ++leaving; ++ } ++ return leaving; ++} ++ ++/* ++ * kdb_wait_for_cpus ++ * ++ * Invoked once at the start of a kdb event, from the controlling cpu. Wait a ++ * short period for the other cpus to enter kdb state. ++ * ++ * Inputs: ++ * none ++ * Returns: ++ * none ++ * Locking: ++ * none ++ * Remarks: ++ * none ++ */ ++ ++int kdb_wait_for_cpus_secs; ++ ++static void ++kdb_wait_for_cpus(void) ++{ ++#ifdef CONFIG_SMP ++ int online = 0, kdb_data = 0, prev_kdb_data = 0, c, time; ++ mdelay(100); ++ for (time = 0; time < kdb_wait_for_cpus_secs; ++time) { ++ online = 0; ++ kdb_data = 0; ++ for_each_online_cpu(c) { ++ ++online; ++ if (kdb_running_process[c].seqno >= kdb_seqno - 1) ++ ++kdb_data; ++ } ++ if (online == kdb_data) ++ break; ++ if (prev_kdb_data != kdb_data) { ++ kdb_nextline = 0; /* no prompt yet */ ++ kdb_printf(" %d out of %d cpus in kdb, waiting for the rest, timeout in %d second(s)\n", ++ kdb_data, online, kdb_wait_for_cpus_secs - time); ++ prev_kdb_data = kdb_data; ++ } ++ touch_nmi_watchdog(); ++ mdelay(1000); ++ /* Architectures may want to send a more forceful interrupt */ ++ if (time == min(kdb_wait_for_cpus_secs / 2, 5)) ++ kdba_wait_for_cpus(); ++ if (time % 4 == 0) ++ kdb_printf("."); ++ } ++ if (time) { ++ int wait = online - kdb_data; ++ if (wait == 0) ++ kdb_printf("All cpus are now in kdb\n"); ++ else ++ kdb_printf("%d cpu%s not in kdb, %s state is unknown\n", ++ wait, ++ wait == 1 ? " is" : "s are", ++ wait == 1 ? "its" : "their"); ++ } ++ /* give back the vector we took over in smp_kdb_stop */ ++ kdba_giveback_vector(KDB_VECTOR); ++#endif /* CONFIG_SMP */ ++} ++ ++/* ++ * kdb_main_loop ++ * ++ * The main kdb loop. After initial setup and assignment of the controlling ++ * cpu, all cpus are in this loop. One cpu is in control and will issue the kdb ++ * prompt, the others will spin until 'go' or cpu switch. ++ * ++ * To get a consistent view of the kernel stacks for all processes, this routine ++ * is invoked from the main kdb code via an architecture specific routine. ++ * kdba_main_loop is responsible for making the kernel stacks consistent for all ++ * processes, there should be no difference between a blocked process and a ++ * running process as far as kdb is concerned. ++ * ++ * Inputs: ++ * reason The reason KDB was invoked ++ * error The hardware-defined error code ++ * reason2 kdb's current reason code. Initially error but can change ++ * acording to kdb state. ++ * db_result Result code from break or debug point. ++ * regs The exception frame at time of fault/breakpoint. If reason ++ * is SILENT or CPU_UP then regs is NULL, otherwise it ++ * should always be valid. ++ * Returns: ++ * 0 KDB was invoked for an event which it wasn't responsible ++ * 1 KDB handled the event for which it was invoked. ++ * Locking: ++ * none ++ * Remarks: ++ * none ++ */ ++ ++int ++kdb_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, ++ kdb_dbtrap_t db_result, struct pt_regs *regs) ++{ ++ int result = 1; ++ /* Stay in kdb() until 'go', 'ss[b]' or an error */ ++ while (1) { ++ /* ++ * All processors except the one that is in control ++ * will spin here. ++ */ ++ KDB_DEBUG_STATE("kdb_main_loop 1", reason); ++ while (KDB_STATE(HOLD_CPU)) { ++ /* state KDB is turned off by kdb_cpu to see if the ++ * other cpus are still live, each cpu in this loop ++ * turns it back on. ++ */ ++ if (!KDB_STATE(KDB)) { ++ KDB_STATE_SET(KDB); ++ } ++ ++#if defined(CONFIG_KDB_KDUMP) ++ if (KDB_STATE(KEXEC)) { ++ struct pt_regs r; ++ if (regs == NULL) ++ regs = &r; ++ ++ kdba_kdump_shutdown_slave(regs); ++ return 0; ++ } ++#endif ++ } ++ ++ KDB_STATE_CLEAR(SUPPRESS); ++ KDB_DEBUG_STATE("kdb_main_loop 2", reason); ++ if (KDB_STATE(LEAVING)) ++ break; /* Another cpu said 'go' */ ++ ++ if (!kdb_quiet(reason)) ++ kdb_wait_for_cpus(); ++ /* Still using kdb, this processor is in control */ ++ result = kdb_local(reason2, error, regs, db_result); ++ KDB_DEBUG_STATE("kdb_main_loop 3", result); ++ ++ if (result == KDB_CMD_CPU) { ++ /* Cpu switch, hold the current cpu, release the target one. */ ++ reason2 = KDB_REASON_SWITCH; ++ KDB_STATE_SET(HOLD_CPU); ++ KDB_STATE_CLEAR_CPU(HOLD_CPU, kdb_new_cpu); ++ continue; ++ } ++ ++ if (result == KDB_CMD_SS) { ++ KDB_STATE_SET(DOING_SS); ++ break; ++ } ++ ++ if (result == KDB_CMD_SSB) { ++ KDB_STATE_SET(DOING_SS); ++ KDB_STATE_SET(DOING_SSB); ++ break; ++ } ++ ++ if (result && result != 1 && result != KDB_CMD_GO) ++ kdb_printf("\nUnexpected kdb_local return code %d\n", result); ++ ++ KDB_DEBUG_STATE("kdb_main_loop 4", reason); ++ break; ++ } ++ if (KDB_STATE(DOING_SS)) ++ KDB_STATE_CLEAR(SSBPT); ++ return result; ++} ++ ++/* iapc_boot_arch was defined in ACPI 2.0, FADT revision 3 onwards. For any ++ * FADT prior to revision 3, we have to assume that we have an i8042 I/O ++ * device. ACPI initialises after KDB initialises but before using KDB, so ++ * check iapc_boot_arch on each entry to KDB. ++ */ ++static void ++kdb_check_i8042(void) ++{ ++ KDB_FLAG_CLEAR(NO_I8042); ++#ifdef CONFIG_ACPI ++ if (acpi_gbl_FADT.header.revision >= 3 && ++ (acpi_gbl_FADT.boot_flags & ACPI_FADT_8042) == 0) ++ KDB_FLAG_SET(NO_I8042); ++#endif /* CONFIG_ACPI */ ++} ++ ++/* ++ * kdb ++ * ++ * This function is the entry point for the kernel debugger. It ++ * provides a command parser and associated support functions to ++ * allow examination and control of an active kernel. ++ * ++ * The breakpoint trap code should invoke this function with ++ * one of KDB_REASON_BREAK (int 03) or KDB_REASON_DEBUG (debug register) ++ * ++ * the die_if_kernel function should invoke this function with ++ * KDB_REASON_OOPS. ++ * ++ * In single step mode, one cpu is released to run without ++ * breakpoints. Interrupts and NMI are reset to their original values, ++ * the cpu is allowed to do one instruction which causes a trap ++ * into kdb with KDB_REASON_DEBUG. ++ * ++ * Inputs: ++ * reason The reason KDB was invoked ++ * error The hardware-defined error code ++ * regs The exception frame at time of fault/breakpoint. If reason ++ * is SILENT or CPU_UP then regs is NULL, otherwise it ++ * should always be valid. ++ * Returns: ++ * 0 KDB was invoked for an event which it wasn't responsible ++ * 1 KDB handled the event for which it was invoked. ++ * Locking: ++ * none ++ * Remarks: ++ * No assumptions of system state. This function may be invoked ++ * with arbitrary locks held. It will stop all other processors ++ * in an SMP environment, disable all interrupts and does not use ++ * the operating systems keyboard driver. ++ * ++ * This code is reentrant but only for cpu switch. Any other ++ * reentrancy is an error, although kdb will attempt to recover. ++ * ++ * At the start of a kdb session the initial processor is running ++ * kdb() and the other processors can be doing anything. When the ++ * initial processor calls smp_kdb_stop() the other processors are ++ * driven through kdb_ipi which calls kdb() with reason SWITCH. ++ * That brings all processors into this routine, one with a "real" ++ * reason code, the other with SWITCH. ++ * ++ * Because the other processors are driven via smp_kdb_stop(), ++ * they enter here from the NMI handler. Until the other ++ * processors exit from here and exit from kdb_ipi, they will not ++ * take any more NMI requests. The initial cpu will still take NMI. ++ * ++ * Multiple race and reentrancy conditions, each with different ++ * advoidance mechanisms. ++ * ++ * Two cpus hit debug points at the same time. ++ * ++ * kdb_lock and kdb_initial_cpu ensure that only one cpu gets ++ * control of kdb. The others spin on kdb_initial_cpu until ++ * they are driven through NMI into kdb_ipi. When the initial ++ * cpu releases the others from NMI, they resume trying to get ++ * kdb_initial_cpu to start a new event. ++ * ++ * A cpu is released from kdb and starts a new event before the ++ * original event has completely ended. ++ * ++ * kdb_previous_event() prevents any cpu from entering ++ * kdb_initial_cpu state until the previous event has completely ++ * ended on all cpus. ++ * ++ * An exception occurs inside kdb. ++ * ++ * kdb_initial_cpu detects recursive entry to kdb and attempts ++ * to recover. The recovery uses longjmp() which means that ++ * recursive calls to kdb never return. Beware of assumptions ++ * like ++ * ++ * ++depth; ++ * kdb(); ++ * --depth; ++ * ++ * If the kdb call is recursive then longjmp takes over and ++ * --depth is never executed. ++ * ++ * NMI handling. ++ * ++ * NMI handling is tricky. The initial cpu is invoked by some kdb event, ++ * this event could be NMI driven but usually is not. The other cpus are ++ * driven into kdb() via kdb_ipi which uses NMI so at the start the other ++ * cpus will not accept NMI. Some operations such as SS release one cpu ++ * but hold all the others. Releasing a cpu means it drops back to ++ * whatever it was doing before the kdb event, this means it drops out of ++ * kdb_ipi and hence out of NMI status. But the software watchdog uses ++ * NMI and we do not want spurious watchdog calls into kdb. kdba_read() ++ * resets the watchdog counters in its input polling loop, when a kdb ++ * command is running it is subject to NMI watchdog events. ++ * ++ * Another problem with NMI handling is the NMI used to drive the other ++ * cpus into kdb cannot be distinguished from the watchdog NMI. State ++ * flag WAIT_IPI indicates that a cpu is waiting for NMI via kdb_ipi, ++ * if not set then software NMI is ignored by kdb_ipi. ++ * ++ * Cpu switching. ++ * ++ * All cpus are in kdb (or they should be), all but one are ++ * spinning on KDB_STATE(HOLD_CPU). Only one cpu is not in ++ * HOLD_CPU state, only that cpu can handle commands. ++ * ++ * Go command entered. ++ * ++ * If necessary, go will switch to the initial cpu first. If the event ++ * was caused by a software breakpoint (assumed to be global) that ++ * requires single-step to get over the breakpoint then only release the ++ * initial cpu, after the initial cpu has single-stepped the breakpoint ++ * then release the rest of the cpus. If SSBPT is not required then ++ * release all the cpus at once. ++ */ ++ ++int ++kdb(kdb_reason_t reason, int error, struct pt_regs *regs) ++{ ++ kdb_intstate_t int_state; /* Interrupt state */ ++ kdb_reason_t reason2 = reason; ++ int result = 0; /* Default is kdb did not handle it */ ++ int ss_event, old_regs_saved = 0; ++ struct pt_regs *old_regs = NULL; ++ kdb_dbtrap_t db_result=KDB_DB_NOBPT; ++ preempt_disable(); ++ atomic_inc(&kdb_event); ++ ++ switch(reason) { ++ case KDB_REASON_OOPS: ++ case KDB_REASON_NMI: ++ KDB_FLAG_SET(CATASTROPHIC); /* kernel state is dubious now */ ++ break; ++ default: ++ break; ++ } ++ switch(reason) { ++ case KDB_REASON_ENTER: ++ case KDB_REASON_ENTER_SLAVE: ++ case KDB_REASON_BREAK: ++ case KDB_REASON_DEBUG: ++ case KDB_REASON_OOPS: ++ case KDB_REASON_SWITCH: ++ case KDB_REASON_KEYBOARD: ++ case KDB_REASON_NMI: ++ if (regs && regs != get_irq_regs()) { ++ old_regs = set_irq_regs(regs); ++ old_regs_saved = 1; ++ } ++ break; ++ default: ++ break; ++ } ++ if (kdb_continue_catastrophic > 2) { ++ kdb_printf("kdb_continue_catastrophic is out of range, setting to 2\n"); ++ kdb_continue_catastrophic = 2; ++ } ++ if (!kdb_on && KDB_FLAG(CATASTROPHIC) && kdb_continue_catastrophic == 2) { ++ KDB_FLAG_SET(ONLY_DO_DUMP); ++ } ++ if (!kdb_on && !KDB_FLAG(ONLY_DO_DUMP)) ++ goto out; ++ ++ KDB_DEBUG_STATE("kdb 1", reason); ++ KDB_STATE_CLEAR(SUPPRESS); ++ ++ /* Filter out userspace breakpoints first, no point in doing all ++ * the kdb smp fiddling when it is really a gdb trap. ++ * Save the single step status first, kdba_db_trap clears ss status. ++ * kdba_b[dp]_trap sets SSBPT if required. ++ */ ++ ss_event = KDB_STATE(DOING_SS) || KDB_STATE(SSBPT); ++#ifdef CONFIG_CPU_XSCALE ++ if ( KDB_STATE(A_XSC_ICH) ) { ++ /* restore changed I_BIT */ ++ KDB_STATE_CLEAR(A_XSC_ICH); ++ kdba_restore_retirq(regs, KDB_STATE(A_XSC_IRQ)); ++ if ( !ss_event ) { ++ kdb_printf("Stranger!!! Why IRQ bit is changed====\n"); ++ } ++ } ++#endif ++ if (reason == KDB_REASON_BREAK) { ++ db_result = kdba_bp_trap(regs, error); /* Only call this once */ ++ } ++ if (reason == KDB_REASON_DEBUG) { ++ db_result = kdba_db_trap(regs, error); /* Only call this once */ ++ } ++ ++ if ((reason == KDB_REASON_BREAK || reason == KDB_REASON_DEBUG) ++ && db_result == KDB_DB_NOBPT) { ++ KDB_DEBUG_STATE("kdb 2", reason); ++ goto out; /* Not one of mine */ ++ } ++ ++ /* Turn off single step if it was being used */ ++ if (ss_event) { ++ kdba_clearsinglestep(regs); ++ /* Single step after a breakpoint removes the need for a delayed reinstall */ ++ if (reason == KDB_REASON_BREAK || reason == KDB_REASON_DEBUG) ++ KDB_STATE_CLEAR(SSBPT); ++ } ++ ++ /* kdb can validly reenter but only for certain well defined conditions */ ++ if (reason == KDB_REASON_DEBUG ++ && !KDB_STATE(HOLD_CPU) ++ && ss_event) ++ KDB_STATE_SET(REENTRY); ++ else ++ KDB_STATE_CLEAR(REENTRY); ++ ++ /* Wait for previous kdb event to completely exit before starting ++ * a new event. ++ */ ++ while (kdb_previous_event()) ++ ; ++ KDB_DEBUG_STATE("kdb 3", reason); ++ ++ /* ++ * If kdb is already active, print a message and try to recover. ++ * If recovery is not possible and recursion is allowed or ++ * forced recursion without recovery is set then try to recurse ++ * in kdb. Not guaranteed to work but it makes an attempt at ++ * debugging the debugger. ++ */ ++ if (reason != KDB_REASON_SWITCH && ++ reason != KDB_REASON_ENTER_SLAVE) { ++ if (KDB_IS_RUNNING() && !KDB_STATE(REENTRY)) { ++ int recover = 1; ++ unsigned long recurse = 0; ++ kdb_printf("kdb: Debugger re-entered on cpu %d, new reason = %d\n", ++ smp_processor_id(), reason); ++ /* Should only re-enter from released cpu */ ++ ++ if (KDB_STATE(HOLD_CPU)) { ++ kdb_printf(" Strange, cpu %d should not be running\n", smp_processor_id()); ++ recover = 0; ++ } ++ if (!KDB_STATE(CMD)) { ++ kdb_printf(" Not executing a kdb command\n"); ++ recover = 0; ++ } ++ if (!KDB_STATE(LONGJMP)) { ++ kdb_printf(" No longjmp available for recovery\n"); ++ recover = 0; ++ } ++ kdbgetulenv("RECURSE", &recurse); ++ if (recurse > 1) { ++ kdb_printf(" Forced recursion is set\n"); ++ recover = 0; ++ } ++ if (recover) { ++ kdb_printf(" Attempting to abort command and recover\n"); ++#ifdef kdba_setjmp ++ kdba_longjmp(&kdbjmpbuf[smp_processor_id()], 0); ++#endif /* kdba_setjmp */ ++ } ++ if (recurse) { ++ if (KDB_STATE(RECURSE)) { ++ kdb_printf(" Already in recursive mode\n"); ++ } else { ++ kdb_printf(" Attempting recursive mode\n"); ++ KDB_STATE_SET(RECURSE); ++ KDB_STATE_SET(REENTRY); ++ reason2 = KDB_REASON_RECURSE; ++ recover = 1; ++ } ++ } ++ if (!recover) { ++ kdb_printf(" Cannot recover, allowing event to proceed\n"); ++ /*temp*/ ++ while (KDB_IS_RUNNING()) ++ cpu_relax(); ++ goto out; ++ } ++ } ++ } else if (reason == KDB_REASON_SWITCH && !KDB_IS_RUNNING()) { ++ kdb_printf("kdb: CPU switch without kdb running, I'm confused\n"); ++ goto out; ++ } ++ ++ /* ++ * Disable interrupts, breakpoints etc. on this processor ++ * during kdb command processing ++ */ ++ KDB_STATE_SET(KDB); ++ kdba_disableint(&int_state); ++ if (!KDB_STATE(KDB_CONTROL)) { ++ kdb_bp_remove_local(); ++ KDB_STATE_SET(KDB_CONTROL); ++ } ++ ++ /* ++ * If not entering the debugger due to CPU switch or single step ++ * reentry, serialize access here. ++ * The processors may race getting to this point - if, ++ * for example, more than one processor hits a breakpoint ++ * at the same time. We'll serialize access to kdb here - ++ * other processors will loop here, and the NMI from the stop ++ * IPI will take them into kdb as switch candidates. Once ++ * the initial processor releases the debugger, the rest of ++ * the processors will race for it. ++ * ++ * The above describes the normal state of affairs, where two or more ++ * cpus that are entering kdb at the "same" time are assumed to be for ++ * separate events. However some processes such as ia64 MCA/INIT will ++ * drive all the cpus into error processing at the same time. For that ++ * case, all of the cpus entering kdb at the "same" time are really a ++ * single event. ++ * ++ * That case is handled by the use of KDB_ENTER by one cpu (the ++ * monarch) and KDB_ENTER_SLAVE on the other cpus (the slaves). ++ * KDB_ENTER_SLAVE maps to KDB_REASON_ENTER_SLAVE. The slave events ++ * will be treated as if they had just responded to the kdb IPI, i.e. ++ * as if they were KDB_REASON_SWITCH. ++ * ++ * Because of races across multiple cpus, ENTER_SLAVE can occur before ++ * the main ENTER. Hold up ENTER_SLAVE here until the main ENTER ++ * arrives. ++ */ ++ ++ if (reason == KDB_REASON_ENTER_SLAVE) { ++ spin_lock(&kdb_lock); ++ while (!KDB_IS_RUNNING()) { ++ spin_unlock(&kdb_lock); ++ while (!KDB_IS_RUNNING()) ++ cpu_relax(); ++ spin_lock(&kdb_lock); ++ } ++ reason = KDB_REASON_SWITCH; ++ KDB_STATE_SET(HOLD_CPU); ++ spin_unlock(&kdb_lock); ++ } ++ ++ if (reason == KDB_REASON_SWITCH || KDB_STATE(REENTRY)) ++ ; /* drop through */ ++ else { ++ KDB_DEBUG_STATE("kdb 4", reason); ++ spin_lock(&kdb_lock); ++ while (KDB_IS_RUNNING() || kdb_previous_event()) { ++ spin_unlock(&kdb_lock); ++ while (KDB_IS_RUNNING() || kdb_previous_event()) ++ cpu_relax(); ++ spin_lock(&kdb_lock); ++ } ++ KDB_DEBUG_STATE("kdb 5", reason); ++ ++ kdb_initial_cpu = smp_processor_id(); ++ ++kdb_seqno; ++ spin_unlock(&kdb_lock); ++ if (!kdb_quiet(reason)) ++ notify_die(DIE_KDEBUG_ENTER, "KDEBUG ENTER", regs, error, 0, 0); ++ } ++ ++ if (smp_processor_id() == kdb_initial_cpu ++ && !KDB_STATE(REENTRY)) { ++ KDB_STATE_CLEAR(HOLD_CPU); ++ KDB_STATE_CLEAR(WAIT_IPI); ++ kdb_check_i8042(); ++ /* ++ * Remove the global breakpoints. This is only done ++ * once from the initial processor on initial entry. ++ */ ++ if (!kdb_quiet(reason) || smp_processor_id() == 0) ++ kdb_bp_remove_global(); ++ ++ /* ++ * If SMP, stop other processors. The other processors ++ * will enter kdb() with KDB_REASON_SWITCH and spin in ++ * kdb_main_loop(). ++ */ ++ KDB_DEBUG_STATE("kdb 6", reason); ++ if (NR_CPUS > 1 && !kdb_quiet(reason)) { ++ int i; ++ for (i = 0; i < NR_CPUS; ++i) { ++ if (!cpu_online(i)) ++ continue; ++ if (i != kdb_initial_cpu) { ++ KDB_STATE_SET_CPU(HOLD_CPU, i); ++ KDB_STATE_SET_CPU(WAIT_IPI, i); ++ } ++ } ++ KDB_DEBUG_STATE("kdb 7", reason); ++ smp_kdb_stop(); ++ KDB_DEBUG_STATE("kdb 8", reason); ++ } ++ } ++ ++ if (KDB_STATE(GO1)) { ++ kdb_bp_remove_global(); /* They were set for single-step purposes */ ++ KDB_STATE_CLEAR(GO1); ++ reason = KDB_REASON_SILENT; /* Now silently go */ ++ } ++ ++ /* Set up a consistent set of process stacks before talking to the user */ ++ KDB_DEBUG_STATE("kdb 9", result); ++ result = kdba_main_loop(reason, reason2, error, db_result, regs); ++ reason = reason2; /* back to original event type */ ++ ++ KDB_DEBUG_STATE("kdb 10", result); ++ kdba_adjust_ip(reason, error, regs); ++ KDB_STATE_CLEAR(LONGJMP); ++ KDB_DEBUG_STATE("kdb 11", result); ++ /* go which requires single-step over a breakpoint must only release ++ * one cpu. ++ */ ++ if (result == KDB_CMD_GO && KDB_STATE(SSBPT)) ++ KDB_STATE_SET(GO1); ++ ++ if (smp_processor_id() == kdb_initial_cpu && ++ !KDB_STATE(DOING_SS) && ++ !KDB_STATE(RECURSE)) { ++ /* ++ * (Re)install the global breakpoints and cleanup the cached ++ * symbol table. This is only done once from the initial ++ * processor on go. ++ */ ++ KDB_DEBUG_STATE("kdb 12", reason); ++ if (!kdb_quiet(reason) || smp_processor_id() == 0) { ++ kdb_bp_install_global(regs); ++ kdbnearsym_cleanup(); ++ debug_kusage(); ++ } ++ if (!KDB_STATE(GO1)) { ++ /* ++ * Release all other cpus which will see KDB_STATE(LEAVING) is set. ++ */ ++ int i; ++ for (i = 0; i < NR_CPUS; ++i) { ++ if (KDB_STATE_CPU(KDB, i)) ++ KDB_STATE_SET_CPU(LEAVING, i); ++ KDB_STATE_CLEAR_CPU(WAIT_IPI, i); ++ KDB_STATE_CLEAR_CPU(HOLD_CPU, i); ++ } ++ /* Wait until all the other processors leave kdb */ ++ while (kdb_previous_event() != 1) ++ ; ++ if (!kdb_quiet(reason)) ++ notify_die(DIE_KDEBUG_LEAVE, "KDEBUG LEAVE", regs, error, 0, 0); ++ kdb_initial_cpu = -1; /* release kdb control */ ++ KDB_DEBUG_STATE("kdb 13", reason); ++ } ++ } ++ ++ KDB_DEBUG_STATE("kdb 14", result); ++ kdba_restoreint(&int_state); ++#ifdef CONFIG_CPU_XSCALE ++ if ( smp_processor_id() == kdb_initial_cpu && ++ ( KDB_STATE(SSBPT) | KDB_STATE(DOING_SS) ) ++ ) { ++ kdba_setsinglestep(regs); ++ // disable IRQ in stack frame ++ KDB_STATE_SET(A_XSC_ICH); ++ if ( kdba_disable_retirq(regs) ) { ++ KDB_STATE_SET(A_XSC_IRQ); ++ } ++ else { ++ KDB_STATE_CLEAR(A_XSC_IRQ); ++ } ++ } ++#endif ++ ++ /* Only do this work if we are really leaving kdb */ ++ if (!(KDB_STATE(DOING_SS) || KDB_STATE(SSBPT) || KDB_STATE(RECURSE))) { ++ KDB_DEBUG_STATE("kdb 15", result); ++ kdb_bp_install_local(regs); ++ if (old_regs_saved) ++ set_irq_regs(old_regs); ++ KDB_STATE_CLEAR(KDB_CONTROL); ++ } ++ ++ KDB_DEBUG_STATE("kdb 16", result); ++ KDB_FLAG_CLEAR(CATASTROPHIC); ++ KDB_STATE_CLEAR(IP_ADJUSTED); /* Re-adjust ip next time in */ ++ KDB_STATE_CLEAR(KEYBOARD); ++ KDB_STATE_CLEAR(KDB); /* Main kdb state has been cleared */ ++ KDB_STATE_CLEAR(RECURSE); ++ KDB_STATE_CLEAR(LEAVING); /* No more kdb work after this */ ++ KDB_DEBUG_STATE("kdb 17", reason); ++out: ++ atomic_dec(&kdb_event); ++ preempt_enable(); ++ return result != 0; ++} ++ ++/* ++ * kdb_mdr ++ * ++ * This function implements the guts of the 'mdr' command. ++ * ++ * mdr , ++ * ++ * Inputs: ++ * addr Start address ++ * count Number of bytes ++ * Outputs: ++ * None. ++ * Returns: ++ * Always 0. Any errors are detected and printed by kdb_getarea. ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_mdr(kdb_machreg_t addr, unsigned int count) ++{ ++ unsigned char c; ++ while (count--) { ++ if (kdb_getarea(c, addr)) ++ return 0; ++ kdb_printf("%02x", c); ++ addr++; ++ } ++ kdb_printf("\n"); ++ return 0; ++} ++ ++/* ++ * kdb_md ++ * ++ * This function implements the 'md', 'md1', 'md2', 'md4', 'md8' ++ * 'mdr' and 'mds' commands. ++ * ++ * md|mds [ [ []]] ++ * mdWcN [ [ []]] ++ * where W = is the width (1, 2, 4 or 8) and N is the count. ++ * for eg., md1c20 reads 20 bytes, 1 at a time. ++ * mdr , ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static void ++kdb_md_line(const char *fmtstr, kdb_machreg_t addr, ++ int symbolic, int nosect, int bytesperword, ++ int num, int repeat, int phys) ++{ ++ /* print just one line of data */ ++ kdb_symtab_t symtab; ++ char cbuf[32]; ++ char *c = cbuf; ++ int i; ++ unsigned long word; ++ ++ memset(cbuf, '\0', sizeof(cbuf)); ++ if (phys) ++ kdb_printf("phys " kdb_machreg_fmt0 " ", addr); ++ else ++ kdb_printf(kdb_machreg_fmt0 " ", addr); ++ ++ for (i = 0; i < num && repeat--; i++) { ++ if (phys) { ++ if (kdb_getphysword(&word, addr, bytesperword)) ++ break; ++ } else if (kdb_getword(&word, addr, bytesperword)) ++ break; ++ kdb_printf(fmtstr, word); ++ if (symbolic) ++ kdbnearsym(word, &symtab); ++ else ++ memset(&symtab, 0, sizeof(symtab)); ++ if (symtab.sym_name) { ++ kdb_symbol_print(word, &symtab, 0); ++ if (!nosect) { ++ kdb_printf("\n"); ++ kdb_printf(" %s %s " ++ kdb_machreg_fmt " " kdb_machreg_fmt " " kdb_machreg_fmt, ++ symtab.mod_name, ++ symtab.sec_name, ++ symtab.sec_start, ++ symtab.sym_start, ++ symtab.sym_end); ++ } ++ addr += bytesperword; ++ } else { ++ union { ++ u64 word; ++ unsigned char c[8]; ++ } wc; ++ unsigned char *cp; ++#ifdef __BIG_ENDIAN ++ cp = wc.c + 8 - bytesperword; ++#else ++ cp = wc.c; ++#endif ++ wc.word = word; ++#define printable_char(c) ({unsigned char __c = c; isascii(__c) && isprint(__c) ? __c : '.';}) ++ switch (bytesperword) { ++ case 8: ++ *c++ = printable_char(*cp++); ++ *c++ = printable_char(*cp++); ++ *c++ = printable_char(*cp++); ++ *c++ = printable_char(*cp++); ++ addr += 4; ++ case 4: ++ *c++ = printable_char(*cp++); ++ *c++ = printable_char(*cp++); ++ addr += 2; ++ case 2: ++ *c++ = printable_char(*cp++); ++ addr++; ++ case 1: ++ *c++ = printable_char(*cp++); ++ addr++; ++ break; ++ } ++#undef printable_char ++ } ++ } ++ kdb_printf("%*s %s\n", (int)((num-i)*(2*bytesperword + 1)+1), " ", cbuf); ++} ++ ++static int ++kdb_md(int argc, const char **argv) ++{ ++ static kdb_machreg_t last_addr; ++ static int last_radix, last_bytesperword, last_repeat; ++ int radix = 16, mdcount = 8, bytesperword = KDB_WORD_SIZE, repeat; ++ int nosect = 0; ++ char fmtchar, fmtstr[64]; ++ kdb_machreg_t addr; ++ unsigned long word; ++ long offset = 0; ++ int symbolic = 0; ++ int valid = 0; ++ int phys = 0; ++ ++ kdbgetintenv("MDCOUNT", &mdcount); ++ kdbgetintenv("RADIX", &radix); ++ kdbgetintenv("BYTESPERWORD", &bytesperword); ++ ++ /* Assume 'md ' and start with environment values */ ++ repeat = mdcount * 16 / bytesperword; ++ ++ if (strcmp(argv[0], "mdr") == 0) { ++ if (argc != 2) ++ return KDB_ARGCOUNT; ++ valid = 1; ++ } else if (isdigit(argv[0][2])) { ++ bytesperword = (int)(argv[0][2] - '0'); ++ if (bytesperword == 0) { ++ bytesperword = last_bytesperword; ++ if (bytesperword == 0) { ++ bytesperword = 4; ++ } ++ } ++ last_bytesperword = bytesperword; ++ repeat = mdcount * 16 / bytesperword; ++ if (!argv[0][3]) ++ valid = 1; ++ else if (argv[0][3] == 'c' && argv[0][4]) { ++ char *p; ++ repeat = simple_strtoul(argv[0]+4, &p, 10); ++ mdcount = ((repeat * bytesperword) + 15) / 16; ++ valid = !*p; ++ } ++ last_repeat = repeat; ++ } else if (strcmp(argv[0], "md") == 0) ++ valid = 1; ++ else if (strcmp(argv[0], "mds") == 0) ++ valid = 1; ++ else if (strcmp(argv[0], "mdp") == 0) { ++ phys = valid = 1; ++ } ++ if (!valid) ++ return KDB_NOTFOUND; ++ ++ if (argc == 0) { ++ if (last_addr == 0) ++ return KDB_ARGCOUNT; ++ addr = last_addr; ++ radix = last_radix; ++ bytesperword = last_bytesperword; ++ repeat = last_repeat; ++ mdcount = ((repeat * bytesperword) + 15) / 16; ++ } ++ ++ if (argc) { ++ kdb_machreg_t val; ++ int diag, nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ if (argc > nextarg+2) ++ return KDB_ARGCOUNT; ++ ++ if (argc >= nextarg) { ++ diag = kdbgetularg(argv[nextarg], &val); ++ if (!diag) { ++ mdcount = (int) val; ++ repeat = mdcount * 16 / bytesperword; ++ } ++ } ++ if (argc >= nextarg+1) { ++ diag = kdbgetularg(argv[nextarg+1], &val); ++ if (!diag) ++ radix = (int) val; ++ } ++ } ++ ++ if (strcmp(argv[0], "mdr") == 0) { ++ return kdb_mdr(addr, mdcount); ++ } ++ ++ switch (radix) { ++ case 10: ++ fmtchar = 'd'; ++ break; ++ case 16: ++ fmtchar = 'x'; ++ break; ++ case 8: ++ fmtchar = 'o'; ++ break; ++ default: ++ return KDB_BADRADIX; ++ } ++ ++ last_radix = radix; ++ ++ if (bytesperword > KDB_WORD_SIZE) ++ return KDB_BADWIDTH; ++ ++ switch (bytesperword) { ++ case 8: ++ sprintf(fmtstr, "%%16.16l%c ", fmtchar); ++ break; ++ case 4: ++ sprintf(fmtstr, "%%8.8l%c ", fmtchar); ++ break; ++ case 2: ++ sprintf(fmtstr, "%%4.4l%c ", fmtchar); ++ break; ++ case 1: ++ sprintf(fmtstr, "%%2.2l%c ", fmtchar); ++ break; ++ default: ++ return KDB_BADWIDTH; ++ } ++ ++ last_repeat = repeat; ++ last_bytesperword = bytesperword; ++ ++ if (strcmp(argv[0], "mds") == 0) { ++ symbolic = 1; ++ /* Do not save these changes as last_*, they are temporary mds ++ * overrides. ++ */ ++ bytesperword = KDB_WORD_SIZE; ++ repeat = mdcount; ++ kdbgetintenv("NOSECT", &nosect); ++ } ++ ++ /* Round address down modulo BYTESPERWORD */ ++ ++ addr &= ~(bytesperword-1); ++ ++ while (repeat > 0) { ++ unsigned long a; ++ int n, z, num = (symbolic ? 1 : (16 / bytesperword)); ++ ++ for (a = addr, z = 0; z < repeat; a += bytesperword, ++z) { ++ if (phys) { ++ if (kdb_getphysword(&word, a, bytesperword) ++ || word) ++ break; ++ } else if (kdb_getword(&word, a, bytesperword) || word) ++ break; ++ } ++ n = min(num, repeat); ++ kdb_md_line(fmtstr, addr, symbolic, nosect, bytesperword, num, repeat, phys); ++ addr += bytesperword * n; ++ repeat -= n; ++ z = (z + num - 1) / num; ++ if (z > 2) { ++ int s = num * (z-2); ++ kdb_printf(kdb_machreg_fmt0 "-" kdb_machreg_fmt0 " zero suppressed\n", ++ addr, addr + bytesperword * s - 1); ++ addr += bytesperword * s; ++ repeat -= s; ++ } ++ } ++ last_addr = addr; ++ ++ return 0; ++} ++ ++/* ++ * kdb_mm ++ * ++ * This function implements the 'mm' command. ++ * ++ * mm address-expression new-value ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * mm works on machine words, mmW works on bytes. ++ */ ++ ++static int ++kdb_mm(int argc, const char **argv) ++{ ++ int diag; ++ kdb_machreg_t addr; ++ long offset = 0; ++ unsigned long contents; ++ int nextarg; ++ int width; ++ ++ if (argv[0][2] && !isdigit(argv[0][2])) ++ return KDB_NOTFOUND; ++ ++ if (argc < 2) { ++ return KDB_ARGCOUNT; ++ } ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) ++ return diag; ++ ++ if (nextarg > argc) ++ return KDB_ARGCOUNT; ++ ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &contents, NULL, NULL))) ++ return diag; ++ ++ if (nextarg != argc + 1) ++ return KDB_ARGCOUNT; ++ ++ width = argv[0][2] ? (argv[0][2] - '0') : (KDB_WORD_SIZE); ++ if ((diag = kdb_putword(addr, contents, width))) ++ return diag; ++ ++ kdb_printf(kdb_machreg_fmt " = " kdb_machreg_fmt "\n", addr, contents); ++ ++ return 0; ++} ++ ++/* ++ * kdb_go ++ * ++ * This function implements the 'go' command. ++ * ++ * go [address-expression] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * KDB_CMD_GO for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_go(int argc, const char **argv) ++{ ++ kdb_machreg_t addr; ++ int diag; ++ int nextarg; ++ long offset; ++ struct pt_regs *regs = get_irq_regs(); ++ ++ if (argc == 1) { ++ if (smp_processor_id() != kdb_initial_cpu) { ++ kdb_printf("go
must be issued from the initial cpu, do cpu %d first\n", kdb_initial_cpu); ++ return KDB_ARGCOUNT; ++ } ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, ++ &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ ++ kdba_setpc(regs, addr); ++ } else if (argc) ++ return KDB_ARGCOUNT; ++ ++ diag = KDB_CMD_GO; ++ if (KDB_FLAG(CATASTROPHIC)) { ++ kdb_printf("Catastrophic error detected\n"); ++ kdb_printf("kdb_continue_catastrophic=%d, ", ++ kdb_continue_catastrophic); ++ if (kdb_continue_catastrophic == 0 && kdb_go_count++ == 0) { ++ kdb_printf("type go a second time if you really want to continue\n"); ++ return 0; ++ } ++ if (kdb_continue_catastrophic == 2) { ++ kdb_do_dump(); ++ kdb_printf("forcing reboot\n"); ++ kdb_reboot(0, NULL); ++ } ++ kdb_printf("attempting to continue\n"); ++ } ++ if (smp_processor_id() != kdb_initial_cpu) { ++ char buf[80]; ++ kdb_printf("go was not issued from initial cpu, switching back to cpu %d\n", kdb_initial_cpu); ++ sprintf(buf, "cpu %d\n", kdb_initial_cpu); ++ /* Recursive use of kdb_parse, do not use argv after this point */ ++ argv = NULL; ++ diag = kdb_parse(buf); ++ if (diag == KDB_CMD_CPU) ++ KDB_STATE_SET_CPU(GO_SWITCH, kdb_initial_cpu); ++ } ++ return diag; ++} ++ ++/* ++ * kdb_rd ++ * ++ * This function implements the 'rd' command. ++ * ++ * rd display all general registers. ++ * rd c display all control registers. ++ * rd d display all debug registers. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_rd(int argc, const char **argv) ++{ ++ int diag; ++ if (argc == 0) { ++ if ((diag = kdb_check_regs())) ++ return diag; ++ return kdba_dumpregs(kdb_current_regs, NULL, NULL); ++ } ++ ++ if (argc > 2) { ++ return KDB_ARGCOUNT; ++ } ++ ++ if ((diag = kdb_check_regs())) ++ return diag; ++ return kdba_dumpregs(kdb_current_regs, argv[1], argc==2 ? argv[2]: NULL); ++} ++ ++/* ++ * kdb_rm ++ * ++ * This function implements the 'rm' (register modify) command. ++ * ++ * rm register-name new-contents ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * Currently doesn't allow modification of control or ++ * debug registers. ++ */ ++ ++static int ++kdb_rm(int argc, const char **argv) ++{ ++ int diag; ++ int ind = 0; ++ kdb_machreg_t contents; ++ ++ if (argc != 2) { ++ return KDB_ARGCOUNT; ++ } ++ ++ /* ++ * Allow presence or absence of leading '%' symbol. ++ */ ++ ++ if (argv[1][0] == '%') ++ ind = 1; ++ ++ diag = kdbgetularg(argv[2], &contents); ++ if (diag) ++ return diag; ++ ++ if ((diag = kdb_check_regs())) ++ return diag; ++ diag = kdba_setregcontents(&argv[1][ind], kdb_current_regs, contents); ++ if (diag) ++ return diag; ++ ++ return 0; ++} ++ ++#if defined(CONFIG_MAGIC_SYSRQ) ++/* ++ * kdb_sr ++ * ++ * This function implements the 'sr' (SYSRQ key) command which ++ * interfaces to the soi-disant MAGIC SYSRQ functionality. ++ * ++ * sr ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * None. ++ */ ++static int ++kdb_sr(int argc, const char **argv) ++{ ++ extern int __sysrq_enabled; ++ if (argc != 1) { ++ return KDB_ARGCOUNT; ++ } ++ if (!__sysrq_enabled) { ++ kdb_printf("Auto activating sysrq\n"); ++ __sysrq_enabled = 1; ++ } ++ ++ handle_sysrq(*argv[1], NULL); ++ ++ return 0; ++} ++#endif /* CONFIG_MAGIC_SYSRQ */ ++ ++/* ++ * kdb_ef ++ * ++ * This function implements the 'regs' (display exception frame) ++ * command. This command takes an address and expects to find ++ * an exception frame at that address, formats and prints it. ++ * ++ * regs address-expression ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * Not done yet. ++ */ ++ ++static int ++kdb_ef(int argc, const char **argv) ++{ ++ int diag; ++ kdb_machreg_t addr; ++ long offset; ++ int nextarg; ++ ++ if (argc == 1) { ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ ++ return kdba_dumpregs((struct pt_regs *)addr, NULL, NULL); ++ } ++ ++ return KDB_ARGCOUNT; ++} ++ ++#if defined(CONFIG_MODULES) ++extern struct list_head *kdb_modules; ++extern void free_module(struct module *); ++ ++/* modules using other modules */ ++struct module_use ++{ ++ struct list_head list; ++ struct module *module_which_uses; ++}; ++ ++/* ++ * kdb_lsmod ++ * ++ * This function implements the 'lsmod' command. Lists currently ++ * loaded kernel modules. ++ * ++ * Mostly taken from userland lsmod. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * ++ */ ++ ++static int ++kdb_lsmod(int argc, const char **argv) ++{ ++ struct module *mod; ++ ++ if (argc != 0) ++ return KDB_ARGCOUNT; ++ ++ kdb_printf("Module Size modstruct Used by\n"); ++ list_for_each_entry(mod, kdb_modules, list) { ++ ++ kdb_printf("%-20s%8u 0x%p ", mod->name, ++ mod->core_size, (void *)mod); ++#ifdef CONFIG_MODULE_UNLOAD ++ kdb_printf("%4d ", module_refcount(mod)); ++#endif ++ if (mod->state == MODULE_STATE_GOING) ++ kdb_printf(" (Unloading)"); ++ else if (mod->state == MODULE_STATE_COMING) ++ kdb_printf(" (Loading)"); ++ else ++ kdb_printf(" (Live)"); ++ ++#ifdef CONFIG_MODULE_UNLOAD ++ { ++ struct module_use *use; ++ kdb_printf(" [ "); ++ list_for_each_entry(use, &mod->modules_which_use_me, list) ++ kdb_printf("%s ", use->module_which_uses->name); ++ kdb_printf("]\n"); ++ } ++#endif ++ } ++ ++ return 0; ++} ++ ++#endif /* CONFIG_MODULES */ ++ ++/* ++ * kdb_env ++ * ++ * This function implements the 'env' command. Display the current ++ * environment variables. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_env(int argc, const char **argv) ++{ ++ int i; ++ ++ for(i=0; i<__nenv; i++) { ++ if (__env[i]) { ++ kdb_printf("%s\n", __env[i]); ++ } ++ } ++ ++ if (KDB_DEBUG(MASK)) ++ kdb_printf("KDBFLAGS=0x%x\n", kdb_flags); ++ ++ return 0; ++} ++ ++/* ++ * kdb_dmesg ++ * ++ * This function implements the 'dmesg' command to display the contents ++ * of the syslog buffer. ++ * ++ * dmesg [lines] [adjust] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * None. ++ */ ++ ++static int ++kdb_dmesg(int argc, const char **argv) ++{ ++ char *syslog_data[4], *start, *end, c = '\0', *p; ++ int diag, logging, logsize, lines = 0, adjust = 0, n; ++ ++ if (argc > 2) ++ return KDB_ARGCOUNT; ++ if (argc) { ++ char *cp; ++ lines = simple_strtol(argv[1], &cp, 0); ++ if (*cp) ++ lines = 0; ++ if (argc > 1) { ++ adjust = simple_strtoul(argv[2], &cp, 0); ++ if (*cp || adjust < 0) ++ adjust = 0; ++ } ++ } ++ ++ /* disable LOGGING if set */ ++ diag = kdbgetintenv("LOGGING", &logging); ++ if (!diag && logging) { ++ const char *setargs[] = { "set", "LOGGING", "0" }; ++ kdb_set(2, setargs); ++ } ++ ++ /* syslog_data[0,1] physical start, end+1. syslog_data[2,3] logical start, end+1. */ ++ debugger_syslog_data(syslog_data); ++ if (syslog_data[2] == syslog_data[3]) ++ return 0; ++ logsize = syslog_data[1] - syslog_data[0]; ++ start = syslog_data[2]; ++ end = syslog_data[3]; ++#define KDB_WRAP(p) (((p - syslog_data[0]) % logsize) + syslog_data[0]) ++ for (n = 0, p = start; p < end; ++p) { ++ if ((c = *KDB_WRAP(p)) == '\n') ++ ++n; ++ } ++ if (c != '\n') ++ ++n; ++ if (lines < 0) { ++ if (adjust >= n) ++ kdb_printf("buffer only contains %d lines, nothing printed\n", n); ++ else if (adjust - lines >= n) ++ kdb_printf("buffer only contains %d lines, last %d lines printed\n", ++ n, n - adjust); ++ if (adjust) { ++ for (; start < end && adjust; ++start) { ++ if (*KDB_WRAP(start) == '\n') ++ --adjust; ++ } ++ if (start < end) ++ ++start; ++ } ++ for (p = start; p < end && lines; ++p) { ++ if (*KDB_WRAP(p) == '\n') ++ ++lines; ++ } ++ end = p; ++ } else if (lines > 0) { ++ int skip = n - (adjust + lines); ++ if (adjust >= n) { ++ kdb_printf("buffer only contains %d lines, nothing printed\n", n); ++ skip = n; ++ } else if (skip < 0) { ++ lines += skip; ++ skip = 0; ++ kdb_printf("buffer only contains %d lines, first %d lines printed\n", ++ n, lines); ++ } ++ for (; start < end && skip; ++start) { ++ if (*KDB_WRAP(start) == '\n') ++ --skip; ++ } ++ for (p = start; p < end && lines; ++p) { ++ if (*KDB_WRAP(p) == '\n') ++ --lines; ++ } ++ end = p; ++ } ++ /* Do a line at a time (max 200 chars) to reduce protocol overhead */ ++ c = '\n'; ++ while (start != end) { ++ char buf[201]; ++ p = buf; ++ while (start < end && (c = *KDB_WRAP(start)) && (p - buf) < sizeof(buf)-1) { ++ ++start; ++ *p++ = c; ++ if (c == '\n') ++ break; ++ } ++ *p = '\0'; ++ kdb_printf("%s", buf); ++ } ++ if (c != '\n') ++ kdb_printf("\n"); ++ ++ return 0; ++} ++ ++/* ++ * kdb_cpu ++ * ++ * This function implements the 'cpu' command. ++ * ++ * cpu [] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * KDB_CMD_CPU for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * All cpu's should be spinning in kdb(). However just in case ++ * a cpu did not take the smp_kdb_stop NMI, check that a cpu ++ * entered kdb() before passing control to it. ++ */ ++ ++static void ++kdb_cpu_status(void) ++{ ++ int i, start_cpu, first_print = 1; ++ char state, prev_state = '?'; ++ ++ kdb_printf("Currently on cpu %d\n", smp_processor_id()); ++ kdb_printf("Available cpus: "); ++ for (start_cpu = -1, i = 0; i < NR_CPUS; i++) { ++ if (!cpu_online(i)) ++ state = 'F'; /* cpu is offline */ ++ else { ++ struct kdb_running_process *krp = kdb_running_process+i; ++ if (KDB_STATE_CPU(KDB, i)) { ++ state = ' '; /* cpu is responding to kdb */ ++ if (kdb_task_state_char(krp->p) == 'I') ++ state = 'I'; /* running the idle task */ ++ } else if (krp->seqno && krp->p && krp->seqno >= kdb_seqno - 1) ++ state = '+'; /* some kdb data, but not responding */ ++ else ++ state = '*'; /* no kdb data */ ++ } ++ if (state != prev_state) { ++ if (prev_state != '?') { ++ if (!first_print) ++ kdb_printf(", "); ++ first_print = 0; ++ kdb_printf("%d", start_cpu); ++ if (start_cpu < i-1) ++ kdb_printf("-%d", i-1); ++ if (prev_state != ' ') ++ kdb_printf("(%c)", prev_state); ++ } ++ prev_state = state; ++ start_cpu = i; ++ } ++ } ++ /* print the trailing cpus, ignoring them if they are all offline */ ++ if (prev_state != 'F') { ++ if (!first_print) ++ kdb_printf(", "); ++ kdb_printf("%d", start_cpu); ++ if (start_cpu < i-1) ++ kdb_printf("-%d", i-1); ++ if (prev_state != ' ') ++ kdb_printf("(%c)", prev_state); ++ } ++ kdb_printf("\n"); ++} ++ ++static int ++kdb_cpu(int argc, const char **argv) ++{ ++ unsigned long cpunum; ++ int diag, i; ++ ++ /* ask the other cpus if they are still active */ ++ for (i=0; i NR_CPUS) ++ || !cpu_online(cpunum) ++ || !KDB_STATE_CPU(KDB, cpunum)) ++ return KDB_BADCPUNUM; ++ ++ kdb_new_cpu = cpunum; ++ ++ /* ++ * Switch to other cpu ++ */ ++ return KDB_CMD_CPU; ++} ++ ++/* The user may not realize that ps/bta with no parameters does not print idle ++ * or sleeping system daemon processes, so tell them how many were suppressed. ++ */ ++void ++kdb_ps_suppressed(void) ++{ ++ int idle = 0, daemon = 0; ++ unsigned long mask_I = kdb_task_state_string("I"), ++ mask_M = kdb_task_state_string("M"); ++ unsigned long cpu; ++ const struct task_struct *p, *g; ++ for (cpu = 0; cpu < NR_CPUS; ++cpu) { ++ if (!cpu_online(cpu)) ++ continue; ++ p = kdb_curr_task(cpu); ++ if (kdb_task_state(p, mask_I)) ++ ++idle; ++ } ++ kdb_do_each_thread(g, p) { ++ if (kdb_task_state(p, mask_M)) ++ ++daemon; ++ } kdb_while_each_thread(g, p); ++ if (idle || daemon) { ++ if (idle) ++ kdb_printf("%d idle process%s (state I)%s\n", ++ idle, idle == 1 ? "" : "es", ++ daemon ? " and " : ""); ++ if (daemon) ++ kdb_printf("%d sleeping system daemon (state M) process%s", ++ daemon, daemon == 1 ? "" : "es"); ++ kdb_printf(" suppressed,\nuse 'ps A' to see all.\n"); ++ } ++} ++ ++/* ++ * kdb_ps ++ * ++ * This function implements the 'ps' command which shows ++ * a list of the active processes. ++ * ++ * ps [DRSTCZEUIMA] All processes, optionally filtered by state ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++void ++kdb_ps1(const struct task_struct *p) ++{ ++ struct kdb_running_process *krp = kdb_running_process + kdb_process_cpu(p); ++ kdb_printf("0x%p %8d %8d %d %4d %c 0x%p %c%s\n", ++ (void *)p, p->pid, p->parent->pid, ++ kdb_task_has_cpu(p), kdb_process_cpu(p), ++ kdb_task_state_char(p), ++ (void *)(&p->thread), ++ p == kdb_curr_task(smp_processor_id()) ? '*': ' ', ++ p->comm); ++ if (kdb_task_has_cpu(p)) { ++ if (!krp->seqno || !krp->p) ++ kdb_printf(" Error: no saved data for this cpu\n"); ++ else { ++ if (krp->seqno < kdb_seqno - 1) ++ kdb_printf(" Warning: process state is stale\n"); ++ if (krp->p != p) ++ kdb_printf(" Error: does not match running process table (0x%p)\n", krp->p); ++ } ++ } ++} ++ ++static int ++kdb_ps(int argc, const char **argv) ++{ ++ struct task_struct *g, *p; ++ unsigned long mask, cpu; ++ ++ if (argc == 0) ++ kdb_ps_suppressed(); ++ kdb_printf("%-*s Pid Parent [*] cpu State %-*s Command\n", ++ (int)(2*sizeof(void *))+2, "Task Addr", ++ (int)(2*sizeof(void *))+2, "Thread"); ++ mask = kdb_task_state_string(argc ? argv[1] : NULL); ++ /* Run the active tasks first */ ++ for (cpu = 0; cpu < NR_CPUS; ++cpu) { ++ if (!cpu_online(cpu)) ++ continue; ++ p = kdb_curr_task(cpu); ++ if (kdb_task_state(p, mask)) ++ kdb_ps1(p); ++ } ++ kdb_printf("\n"); ++ /* Now the real tasks */ ++ kdb_do_each_thread(g, p) { ++ if (kdb_task_state(p, mask)) ++ kdb_ps1(p); ++ } kdb_while_each_thread(g, p); ++ ++ return 0; ++} ++ ++/* ++ * kdb_pid ++ * ++ * This function implements the 'pid' command which switches ++ * the currently active process. ++ * ++ * pid [ | R] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++ ++static int ++kdb_pid(int argc, const char **argv) ++{ ++ struct task_struct *p; ++ unsigned long val; ++ int diag; ++ ++ if (argc > 1) ++ return KDB_ARGCOUNT; ++ ++ if (argc) { ++ if (strcmp(argv[1], "R") == 0) { ++ p = KDB_RUNNING_PROCESS_ORIGINAL[kdb_initial_cpu].p; ++ } else { ++ diag = kdbgetularg(argv[1], &val); ++ if (diag) ++ return KDB_BADINT; ++ ++ p = find_task_by_pid_ns((pid_t)val, &init_pid_ns); ++ if (!p) { ++ kdb_printf("No task with pid=%d\n", (pid_t)val); ++ return 0; ++ } ++ } ++ ++ kdba_set_current_task(p); ++ } ++ ++ kdb_printf("KDB current process is %s(pid=%d)\n", kdb_current_task->comm, ++ kdb_current_task->pid); ++ ++ return 0; ++} ++ ++/* ++ * kdb_ll ++ * ++ * This function implements the 'll' command which follows a linked ++ * list and executes an arbitrary command for each element. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_ll(int argc, const char **argv) ++{ ++ int diag; ++ kdb_machreg_t addr; ++ long offset = 0; ++ kdb_machreg_t va; ++ unsigned long linkoffset; ++ int nextarg; ++ const char *command; ++ ++ if (argc != 3) { ++ return KDB_ARGCOUNT; ++ } ++ ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ ++ diag = kdbgetularg(argv[2], &linkoffset); ++ if (diag) ++ return diag; ++ ++ /* ++ * Using the starting address as ++ * the first element in the list, and assuming that ++ * the list ends with a null pointer. ++ */ ++ ++ va = addr; ++ if (!(command = kdb_strdup(argv[3], GFP_KDB))) { ++ kdb_printf("%s: cannot duplicate command\n", __FUNCTION__); ++ return 0; ++ } ++ /* Recursive use of kdb_parse, do not use argv after this point */ ++ argv = NULL; ++ ++ while (va) { ++ char buf[80]; ++ ++ sprintf(buf, "%s " kdb_machreg_fmt "\n", command, va); ++ diag = kdb_parse(buf); ++ if (diag) ++ return diag; ++ ++ addr = va + linkoffset; ++ if (kdb_getword(&va, addr, sizeof(va))) ++ return 0; ++ } ++ kfree(command); ++ ++ return 0; ++} ++ ++/* ++ * kdb_help ++ * ++ * This function implements the 'help' and '?' commands. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_help(int argc, const char **argv) ++{ ++ kdbtab_t *kt; ++ int i; ++ ++ kdb_printf("%-15.15s %-20.20s %s\n", "Command", "Usage", "Description"); ++ kdb_printf("----------------------------------------------------------\n"); ++ for(i=0, kt=kdb_commands; icmd_name) ++ kdb_printf("%-15.15s %-20.20s %s\n", kt->cmd_name, ++ kt->cmd_usage, kt->cmd_help); ++ } ++ return 0; ++} ++ ++extern int kdb_wake_up_process(struct task_struct * p); ++ ++/* ++ * kdb_kill ++ * ++ * This function implements the 'kill' commands. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_kill(int argc, const char **argv) ++{ ++ long sig, pid; ++ char *endp; ++ struct task_struct *p; ++ struct siginfo info; ++ ++ if (argc!=2) ++ return KDB_ARGCOUNT; ++ ++ sig = simple_strtol(argv[1], &endp, 0); ++ if (*endp) ++ return KDB_BADINT; ++ if (sig >= 0 ) { ++ kdb_printf("Invalid signal parameter.<-signal>\n"); ++ return 0; ++ } ++ sig=-sig; ++ ++ pid = simple_strtol(argv[2], &endp, 0); ++ if (*endp) ++ return KDB_BADINT; ++ if (pid <=0 ) { ++ kdb_printf("Process ID must be large than 0.\n"); ++ return 0; ++ } ++ ++ /* Find the process. */ ++ if (!(p = find_task_by_pid_ns(pid, &init_pid_ns))) { ++ kdb_printf("The specified process isn't found.\n"); ++ return 0; ++ } ++ p = p->group_leader; ++ info.si_signo = sig; ++ info.si_errno = 0; ++ info.si_code = SI_USER; ++ info.si_pid = pid; /* use same capabilities as process being signalled */ ++ info.si_uid = 0; /* kdb has root authority */ ++ kdb_send_sig_info(p, &info, kdb_seqno); ++ return 0; ++} ++ ++struct kdb_tm { ++ int tm_sec; /* seconds */ ++ int tm_min; /* minutes */ ++ int tm_hour; /* hours */ ++ int tm_mday; /* day of the month */ ++ int tm_mon; /* month */ ++ int tm_year; /* year */ ++}; ++ ++static void ++kdb_gmtime(struct timespec *tv, struct kdb_tm *tm) ++{ ++ /* This will work from 1970-2099, 2100 is not a leap year */ ++ static int mon_day[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; ++ memset(tm, 0, sizeof(*tm)); ++ tm->tm_sec = tv->tv_sec % (24 * 60 * 60); ++ tm->tm_mday = tv->tv_sec / (24 * 60 * 60) + (2 * 365 + 1); /* shift base from 1970 to 1968 */ ++ tm->tm_min = tm->tm_sec / 60 % 60; ++ tm->tm_hour = tm->tm_sec / 60 / 60; ++ tm->tm_sec = tm->tm_sec % 60; ++ tm->tm_year = 68 + 4*(tm->tm_mday / (4*365+1)); ++ tm->tm_mday %= (4*365+1); ++ mon_day[1] = 29; ++ while (tm->tm_mday >= mon_day[tm->tm_mon]) { ++ tm->tm_mday -= mon_day[tm->tm_mon]; ++ if (++tm->tm_mon == 12) { ++ tm->tm_mon = 0; ++ ++tm->tm_year; ++ mon_day[1] = 28; ++ } ++ } ++ ++tm->tm_mday; ++} ++ ++/* ++ * Most of this code has been lifted from kernel/timer.c::sys_sysinfo(). ++ * I cannot call that code directly from kdb, it has an unconditional ++ * cli()/sti() and calls routines that take locks which can stop the debugger. ++ */ ++ ++static void ++kdb_sysinfo(struct sysinfo *val) ++{ ++ struct timespec uptime; ++ do_posix_clock_monotonic_gettime(&uptime); ++ memset(val, 0, sizeof(*val)); ++ val->uptime = uptime.tv_sec; ++ val->loads[0] = avenrun[0]; ++ val->loads[1] = avenrun[1]; ++ val->loads[2] = avenrun[2]; ++ val->procs = nr_threads-1; ++ si_meminfo(val); ++ kdb_si_swapinfo(val); ++ ++ return; ++} ++ ++/* ++ * kdb_summary ++ * ++ * This function implements the 'summary' command. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_summary(int argc, const char **argv) ++{ ++ extern struct timespec xtime; ++ extern struct timezone sys_tz; ++ struct kdb_tm tm; ++ struct sysinfo val; ++ ++ if (argc) ++ return KDB_ARGCOUNT; ++ ++ kdb_printf("sysname %s\n", init_uts_ns.name.sysname); ++ kdb_printf("release %s\n", init_uts_ns.name.release); ++ kdb_printf("version %s\n", init_uts_ns.name.version); ++ kdb_printf("machine %s\n", init_uts_ns.name.machine); ++ kdb_printf("nodename %s\n", init_uts_ns.name.nodename); ++ kdb_printf("domainname %s\n", init_uts_ns.name.domainname); ++ kdb_printf("ccversion %s\n", __stringify(CCVERSION)); ++ ++ kdb_gmtime(&xtime, &tm); ++ kdb_printf("date %04d-%02d-%02d %02d:%02d:%02d tz_minuteswest %d\n", ++ 1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday, ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ++ sys_tz.tz_minuteswest); ++ ++ kdb_sysinfo(&val); ++ kdb_printf("uptime "); ++ if (val.uptime > (24*60*60)) { ++ int days = val.uptime / (24*60*60); ++ val.uptime %= (24*60*60); ++ kdb_printf("%d day%s ", days, days == 1 ? "" : "s"); ++ } ++ kdb_printf("%02ld:%02ld\n", val.uptime/(60*60), (val.uptime/60)%60); ++ ++ /* lifted from fs/proc/proc_misc.c::loadavg_read_proc() */ ++ ++#define LOAD_INT(x) ((x) >> FSHIFT) ++#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) ++ kdb_printf("load avg %ld.%02ld %ld.%02ld %ld.%02ld\n", ++ LOAD_INT(val.loads[0]), LOAD_FRAC(val.loads[0]), ++ LOAD_INT(val.loads[1]), LOAD_FRAC(val.loads[1]), ++ LOAD_INT(val.loads[2]), LOAD_FRAC(val.loads[2])); ++ kdb_printf("\n"); ++#undef LOAD_INT ++#undef LOAD_FRAC ++ ++ kdb_meminfo_proc_show(); /* in fs/proc/meminfo.c */ ++ ++ return 0; ++} ++ ++/* ++ * kdb_per_cpu ++ * ++ * This function implements the 'per_cpu' command. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdb_per_cpu(int argc, const char **argv) ++{ ++ char buf[256], fmtstr[64]; ++ kdb_symtab_t symtab; ++ cpumask_t suppress; ++ int cpu, diag; ++ unsigned long addr, val, bytesperword = 0, whichcpu = ~0UL; ++ ++ if (argc < 1 || argc > 3) ++ return KDB_ARGCOUNT; ++ ++ cpus_clear(suppress); ++ snprintf(buf, sizeof(buf), "per_cpu__%s", argv[1]); ++ if (!kdbgetsymval(buf, &symtab)) { ++ kdb_printf("%s is not a per_cpu variable\n", argv[1]); ++ return KDB_BADADDR; ++ } ++ if (argc >=2 && (diag = kdbgetularg(argv[2], &bytesperword))) ++ return diag; ++ if (!bytesperword) ++ bytesperword = KDB_WORD_SIZE; ++ else if (bytesperword > KDB_WORD_SIZE) ++ return KDB_BADWIDTH; ++ sprintf(fmtstr, "%%0%dlx ", (int)(2*bytesperword)); ++ if (argc >= 3) { ++ if ((diag = kdbgetularg(argv[3], &whichcpu))) ++ return diag; ++ if (!cpu_online(whichcpu)) { ++ kdb_printf("cpu %ld is not online\n", whichcpu); ++ return KDB_BADCPUNUM; ++ } ++ } ++ ++ /* Most architectures use __per_cpu_offset[cpu], some use ++ * __per_cpu_offset(cpu), smp has no __per_cpu_offset. ++ */ ++#ifdef __per_cpu_offset ++#define KDB_PCU(cpu) __per_cpu_offset(cpu) ++#else ++#ifdef CONFIG_SMP ++#define KDB_PCU(cpu) __per_cpu_offset[cpu] ++#else ++#define KDB_PCU(cpu) 0 ++#endif ++#endif ++ ++ for_each_online_cpu(cpu) { ++ if (whichcpu != ~0UL && whichcpu != cpu) ++ continue; ++ addr = symtab.sym_start + KDB_PCU(cpu); ++ if ((diag = kdb_getword(&val, addr, bytesperword))) { ++ kdb_printf("%5d " kdb_bfd_vma_fmt0 " - unable to read, diag=%d\n", ++ cpu, addr, diag); ++ continue; ++ } ++#ifdef CONFIG_SMP ++ if (!val) { ++ cpu_set(cpu, suppress); ++ continue; ++ } ++#endif /* CONFIG_SMP */ ++ kdb_printf("%5d ", cpu); ++ kdb_md_line(fmtstr, addr, ++ bytesperword == KDB_WORD_SIZE, ++ 1, bytesperword, 1, 1, 0); ++ } ++ if (cpus_weight(suppress) == 0) ++ return 0; ++ kdb_printf("Zero suppressed cpu(s):"); ++ for_each_cpu_mask(cpu, suppress) { ++ kdb_printf(" %d", cpu); ++ if (cpu == NR_CPUS-1 || next_cpu(cpu, suppress) != cpu + 1) ++ continue; ++ while (cpu < NR_CPUS && next_cpu(cpu, suppress) == cpu + 1) ++ ++cpu; ++ kdb_printf("-%d", cpu); ++ } ++ kdb_printf("\n"); ++ ++#undef KDB_PCU ++ ++ return 0; ++} ++ ++/* ++ * display help for the use of cmd | grep pattern ++ */ ++static int ++kdb_grep_help(int argc, const char **argv) ++{ ++ kdb_printf ("Usage of cmd args | grep pattern:\n"); ++ kdb_printf (" Any command's output may be filtered through an "); ++ kdb_printf ("emulated 'pipe'.\n"); ++ kdb_printf (" 'grep' is just a key word.\n"); ++ kdb_printf ++ (" The pattern may include a very limited set of metacharacters:\n"); ++ kdb_printf (" pattern or ^pattern or pattern$ or ^pattern$\n"); ++ kdb_printf ++ (" And if there are spaces in the pattern, you may quote it:\n"); ++ kdb_printf ++ (" \"pat tern\" or \"^pat tern\" or \"pat tern$\" or \"^pat tern$\"\n"); ++ return 0; ++} ++ ++/* ++ * kdb_register_repeat ++ * ++ * This function is used to register a kernel debugger command. ++ * ++ * Inputs: ++ * cmd Command name ++ * func Function to execute the command ++ * usage A simple usage string showing arguments ++ * help A simple help string describing command ++ * repeat Does the command auto repeat on enter? ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, one if a duplicate command. ++ * Locking: ++ * none. ++ * Remarks: ++ * ++ */ ++ ++#define kdb_command_extend 50 /* arbitrary */ ++int ++kdb_register_repeat(char *cmd, ++ kdb_func_t func, ++ char *usage, ++ char *help, ++ short minlen, ++ kdb_repeat_t repeat) ++{ ++ int i; ++ kdbtab_t *kp; ++ ++ /* ++ * Brute force method to determine duplicates ++ */ ++ for (i=0, kp=kdb_commands; icmd_name && (strcmp(kp->cmd_name, cmd)==0)) { ++ kdb_printf("Duplicate kdb command registered: " ++ "%s, func %p help %s\n", cmd, func, help); ++ return 1; ++ } ++ } ++ ++ /* ++ * Insert command into first available location in table ++ */ ++ for (i=0, kp=kdb_commands; icmd_name == NULL) { ++ break; ++ } ++ } ++ ++ if (i >= kdb_max_commands) { ++ kdbtab_t *new = kmalloc((kdb_max_commands + kdb_command_extend) * sizeof(*new), GFP_KDB); ++ if (!new) { ++ kdb_printf("Could not allocate new kdb_command table\n"); ++ return 1; ++ } ++ if (kdb_commands) { ++ memcpy(new, kdb_commands, kdb_max_commands * sizeof(*new)); ++ kfree(kdb_commands); ++ } ++ memset(new + kdb_max_commands, 0, kdb_command_extend * sizeof(*new)); ++ kdb_commands = new; ++ kp = kdb_commands + kdb_max_commands; ++ kdb_max_commands += kdb_command_extend; ++ } ++ ++ kp->cmd_name = cmd; ++ kp->cmd_func = func; ++ kp->cmd_usage = usage; ++ kp->cmd_help = help; ++ kp->cmd_flags = 0; ++ kp->cmd_minlen = minlen; ++ kp->cmd_repeat = repeat; ++ ++ return 0; ++} ++ ++/* ++ * kdb_register ++ * ++ * Compatibility register function for commands that do not need to ++ * specify a repeat state. Equivalent to kdb_register_repeat with ++ * KDB_REPEAT_NONE. ++ * ++ * Inputs: ++ * cmd Command name ++ * func Function to execute the command ++ * usage A simple usage string showing arguments ++ * help A simple help string describing command ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, one if a duplicate command. ++ * Locking: ++ * none. ++ * Remarks: ++ * ++ */ ++ ++int ++kdb_register(char *cmd, ++ kdb_func_t func, ++ char *usage, ++ char *help, ++ short minlen) ++{ ++ return kdb_register_repeat(cmd, func, usage, help, minlen, KDB_REPEAT_NONE); ++} ++ ++/* ++ * kdb_unregister ++ * ++ * This function is used to unregister a kernel debugger command. ++ * It is generally called when a module which implements kdb ++ * commands is unloaded. ++ * ++ * Inputs: ++ * cmd Command name ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, one command not registered. ++ * Locking: ++ * none. ++ * Remarks: ++ * ++ */ ++ ++int ++kdb_unregister(char *cmd) ++{ ++ int i; ++ kdbtab_t *kp; ++ ++ /* ++ * find the command. ++ */ ++ for (i=0, kp=kdb_commands; icmd_name && (strcmp(kp->cmd_name, cmd)==0)) { ++ kp->cmd_name = NULL; ++ return 0; ++ } ++ } ++ ++ /* ++ * Couldn't find it. ++ */ ++ return 1; ++} ++ ++/* ++ * kdb_inittab ++ * ++ * This function is called by the kdb_init function to initialize ++ * the kdb command table. It must be called prior to any other ++ * call to kdb_register_repeat. ++ * ++ * Inputs: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ */ ++ ++static void __init ++kdb_inittab(void) ++{ ++ int i; ++ kdbtab_t *kp; ++ ++ for(i=0, kp=kdb_commands; i < kdb_max_commands; i++,kp++) { ++ kp->cmd_name = NULL; ++ } ++ ++ kdb_register_repeat("md", kdb_md, "", "Display Memory Contents, also mdWcN, e.g. md8c1", 1, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("mdr", kdb_md, " ", "Display Raw Memory", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("mdp", kdb_md, " ", "Display Physical Memory", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("mds", kdb_md, "", "Display Memory Symbolically", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("mm", kdb_mm, " ", "Modify Memory Contents", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("id", kdb_id, "", "Display Instructions", 1, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("go", kdb_go, "[]", "Continue Execution", 1, KDB_REPEAT_NONE); ++ kdb_register_repeat("rd", kdb_rd, "", "Display Registers", 1, KDB_REPEAT_NONE); ++ kdb_register_repeat("rm", kdb_rm, " ", "Modify Registers", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("ef", kdb_ef, "", "Display exception frame", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("bt", kdb_bt, "[]", "Stack traceback", 1, KDB_REPEAT_NONE); ++ kdb_register_repeat("btp", kdb_bt, "", "Display stack for process ", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("bta", kdb_bt, "[DRSTCZEUIMA]", "Display stack all processes", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("btc", kdb_bt, "", "Backtrace current process on each cpu", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("btt", kdb_bt, "", "Backtrace process given its struct task address", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("ll", kdb_ll, " ", "Execute cmd for each element in linked list", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("env", kdb_env, "", "Show environment variables", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("set", kdb_set, "", "Set environment variables", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("help", kdb_help, "", "Display Help Message", 1, KDB_REPEAT_NONE); ++ kdb_register_repeat("?", kdb_help, "", "Display Help Message", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("cpu", kdb_cpu, "","Switch to new cpu", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("ps", kdb_ps, "[|A]", "Display active task list", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("pid", kdb_pid, "", "Switch to another task", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("reboot", kdb_reboot, "", "Reboot the machine immediately", 0, KDB_REPEAT_NONE); ++#if defined(CONFIG_KDB_KDUMP) ++ kdb_register_repeat("kdump", kdb_kdump, "", "Calls kdump mode", 0, KDB_REPEAT_NONE); ++#endif ++#if defined(CONFIG_MODULES) ++ kdb_register_repeat("lsmod", kdb_lsmod, "", "List loaded kernel modules", 0, KDB_REPEAT_NONE); ++#endif ++#if defined(CONFIG_MAGIC_SYSRQ) ++ kdb_register_repeat("sr", kdb_sr, "", "Magic SysRq key", 0, KDB_REPEAT_NONE); ++#endif ++ kdb_register_repeat("dmesg", kdb_dmesg, "[lines]", "Display syslog buffer", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("defcmd", kdb_defcmd, "name \"usage\" \"help\"", "Define a set of commands, down to endefcmd", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("kill", kdb_kill, "<-signal> ", "Send a signal to a process", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("summary", kdb_summary, "", "Summarize the system", 4, KDB_REPEAT_NONE); ++ kdb_register_repeat("per_cpu", kdb_per_cpu, "", "Display per_cpu variables", 3, KDB_REPEAT_NONE); ++ kdb_register_repeat("grephelp", kdb_grep_help, "", ++ "Display help on | grep", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("print", kdb_debuginfo_print, "", ++ "Type casting, as in lcrash", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("px", kdb_debuginfo_print, "", ++ "Print in hex (type casting) (see 'pxhelp')", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("pxhelp", kdb_pxhelp, "", ++ "Display help for the px command", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("pd", kdb_debuginfo_print, "", ++ "Print in decimal (type casting)", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("whatis", kdb_debuginfo_print,"", ++ "Display the type, or the address for a symbol", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("sizeof", kdb_debuginfo_print, "", ++ "Display the size of a structure, typedef, etc.", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("walk", kdb_walk, "", ++ "Walk a linked list (see 'walkhelp')", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("walkhelp", kdb_walkhelp, "", ++ "Display help for the walk command", 0, KDB_REPEAT_NONE); ++} ++ ++/* ++ * The user has written to our "file" ++ * file: the /proc file ++ * buffer: user address of the data he is writing ++ * count: number of bytes in the user's buffer ++ */ ++static int ++kdb_write_proc_filename(struct file *file, const char __user *buffer, ++ unsigned long count, void *data) ++{ ++ int ret_count; ++ ++ /* our buffer is kdb_debug_info_filename[256] */ ++ if (count > 256) { ++ return 0; ++ } ++ if (copy_from_user(kdb_debug_info_filename, buffer, count)) { ++ return 0; ++ } ++ ret_count = count; /* actual count */ ++ /* remove any newline from the end of the file name */ ++ if (kdb_debug_info_filename[count-1] == '\n') count--; ++ kdb_debug_info_filename[count] = '\0'; ++ ++ return ret_count; ++} ++ ++/* ++ * The user is reading from our "file" ++ * page: the beginning of the user's buffer ++ * start: pointer to the user's pointer (tells him where we put the data) ++ * off: offset into the resource to be read ++ * count: length of the read ++ */ ++static int ++kdb_read_proc_filename(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ /* give him kdb_debug_info_filename[]; */ ++ return snprintf(page, count, "%s\n", kdb_debug_info_filename); ++} ++ ++/* ++ * kdb_proc_filename ++ * ++ * create /proc/kdb/debug_info_name ++ */ ++static void ++kdb_proc_filename(void) ++{ ++ struct proc_dir_entry *kdb_dir_entry, *kdb_file_entry; ++ ++ /* create /proc/kdb */ ++ kdb_dir_entry = proc_mkdir("kdb", NULL); ++ if (!kdb_dir_entry) { ++ printk ("kdb could not create /proc/kdb\n"); ++ return; ++ } ++ ++ /* read/write by owner (root) only */ ++ kdb_file_entry = create_proc_entry("debug_info_name", ++ S_IRUSR | S_IWUSR, kdb_dir_entry); ++ if (!kdb_file_entry) { ++ printk ("kdb could not create /proc/kdb/kdb_dir_entry\n"); ++ return; ++ } ++ kdb_file_entry->nlink = 1; ++ kdb_file_entry->data = (void *)NULL; ++ kdb_file_entry->read_proc = kdb_read_proc_filename; ++ kdb_file_entry->write_proc = kdb_write_proc_filename; ++ return; ++} ++ ++/* ++ * kdb_cmd_init ++ * ++ * This function is called by the kdb_init function to execute any ++ * commands defined in kdb_cmds. ++ * ++ * Inputs: ++ * Commands in *kdb_cmds[]; ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ */ ++ ++static void __init ++kdb_cmd_init(void) ++{ ++ int i, diag; ++ for (i = 0; kdb_cmds[i]; ++i) { ++ if (!defcmd_in_progress) ++ if (console_loglevel >= 6 /* KERN_INFO */) ++ kdb_printf("kdb_cmd[%d]: %s", i, kdb_cmds[i]); ++ diag = kdb_parse(kdb_cmds[i]); ++ if (diag) ++ kdb_printf("kdb command %s failed, kdb diag %d\n", ++ kdb_cmds[i], diag); ++ } ++ if (defcmd_in_progress) { ++ kdb_printf("Incomplete 'defcmd' set, forcing endefcmd\n"); ++ kdb_parse("endefcmd"); ++ } ++} ++ ++/* ++ * kdb_panic ++ * ++ * Invoked via the panic_notifier_list. ++ * ++ * Inputs: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero. ++ * Locking: ++ * None. ++ * Remarks: ++ * When this function is called from panic(), the other cpus have already ++ * been stopped. ++ * ++ */ ++ ++static int ++kdb_panic(struct notifier_block *self, unsigned long command, void *ptr) ++{ ++ KDB_FLAG_SET(CATASTROPHIC); /* kernel state is dubious now */ ++ KDB_ENTER(); ++ return 0; ++} ++ ++static struct notifier_block kdb_block = { kdb_panic, NULL, 0 }; ++ ++#ifdef CONFIG_SYSCTL ++static int proc_do_kdb(ctl_table *table, int write, void __user *buffer, ++ size_t *lenp, loff_t *ppos) ++{ ++ if (KDB_FLAG(NO_CONSOLE) && write) { ++ printk(KERN_ERR "kdb has no working console and has switched itself off\n"); ++ return -EINVAL; ++ } ++ return proc_dointvec(table, write, buffer, lenp, ppos); ++} ++ ++static ctl_table kdb_kern_table[] = { ++ { ++ .procname = "kdb", ++ .data = &kdb_on, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = proc_do_kdb, ++ }, ++ {} ++}; ++ ++static ctl_table kdb_root_table[] = { ++ { ++ .procname = "kernel", ++ .mode = 0555, ++ .child = kdb_kern_table, ++ }, ++ {} ++}; ++#endif /* CONFIG_SYSCTL */ ++ ++static int ++kdb_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) ++{ ++ if (action == CPU_ONLINE) { ++ int cpu =(unsigned long)hcpu; ++ cpumask_t save_cpus_allowed = current->cpus_allowed; ++ set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); ++ kdb(KDB_REASON_CPU_UP, 0, NULL); /* do kdb setup on this cpu */ ++ set_cpus_allowed_ptr(current, &save_cpus_allowed); ++ } ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block kdb_cpu_nfb = { ++ .notifier_call = kdb_cpu_callback ++}; ++ ++/* ++ * kdb_init ++ * ++ * Initialize the kernel debugger environment. ++ * ++ * Parameters: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * None. ++ */ ++ ++void __init ++kdb_init(void) ++{ ++ kdb_initial_cpu = smp_processor_id(); ++ /* ++ * This must be called before any calls to kdb_printf. ++ */ ++ kdb_io_init(); ++ ++ kdb_inittab(); /* Initialize Command Table */ ++ kdb_initbptab(); /* Initialize Breakpoint Table */ ++ kdb_id_init(); /* Initialize Disassembler */ ++ kdba_init(); /* Architecture Dependent Initialization */ ++ ++ /* ++ * Use printk() to get message in log_buf[]; ++ */ ++ printk("kdb version %d.%d%s by Keith Owens, Scott Lurndal. "\ ++ "Copyright SGI, All Rights Reserved\n", ++ KDB_MAJOR_VERSION, KDB_MINOR_VERSION, KDB_TEST_VERSION); ++ ++ kdb_cmd_init(); /* Preset commands from kdb_cmds */ ++ kdb_initial_cpu = -1; /* Avoid recursion problems */ ++ kdb(KDB_REASON_CPU_UP, 0, NULL); /* do kdb setup on boot cpu */ ++ kdb_initial_cpu = smp_processor_id(); ++ atomic_notifier_chain_register(&panic_notifier_list, &kdb_block); ++ register_cpu_notifier(&kdb_cpu_nfb); ++ ++#ifdef kdba_setjmp ++ kdbjmpbuf = vmalloc(NR_CPUS * sizeof(*kdbjmpbuf)); ++ if (!kdbjmpbuf) ++ printk(KERN_ERR "Cannot allocate kdbjmpbuf, no kdb recovery will be possible\n"); ++#endif /* kdba_setjmp */ ++ ++ kdb_initial_cpu = -1; ++ kdb_wait_for_cpus_secs = 2*num_online_cpus(); ++ kdb_wait_for_cpus_secs = max(kdb_wait_for_cpus_secs, 10); ++} ++ ++#ifdef CONFIG_SYSCTL ++static int __init ++kdb_late_init(void) ++{ ++ register_sysctl_table(kdb_root_table); ++ /* seems that we cannot allocate with kmalloc until now */ ++ kdb_proc_filename(); ++ return 0; ++} ++ ++__initcall(kdb_late_init); ++#endif ++ ++EXPORT_SYMBOL(kdb_register); ++EXPORT_SYMBOL(kdb_register_repeat); ++EXPORT_SYMBOL(kdb_unregister); ++EXPORT_SYMBOL(kdb_getarea_size); ++EXPORT_SYMBOL(kdb_putarea_size); ++EXPORT_SYMBOL(kdb_getuserarea_size); ++EXPORT_SYMBOL(kdb_putuserarea_size); ++EXPORT_SYMBOL(kdbgetularg); ++EXPORT_SYMBOL(kdbgetenv); ++EXPORT_SYMBOL(kdbgetintenv); ++EXPORT_SYMBOL(kdbgetaddrarg); ++EXPORT_SYMBOL(kdb); ++EXPORT_SYMBOL(kdb_on); ++EXPORT_SYMBOL(kdb_seqno); ++EXPORT_SYMBOL(kdb_initial_cpu); ++EXPORT_SYMBOL(kdbnearsym); ++EXPORT_SYMBOL(kdb_printf); ++EXPORT_SYMBOL(kdb_symbol_print); ++EXPORT_SYMBOL(kdb_running_process); +--- /dev/null ++++ b/kdb/kdbsupport.c +@@ -0,0 +1,1155 @@ ++/* ++ * Kernel Debugger Architecture Independent Support Functions ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ * 03/02/13 added new 2.5 kallsyms ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++/* ++ * Symbol table functions. ++ */ ++ ++/* ++ * kdbgetsymval ++ * ++ * Return the address of the given symbol. ++ * ++ * Parameters: ++ * symname Character string containing symbol name ++ * symtab Structure to receive results ++ * Outputs: ++ * Returns: ++ * 0 Symbol not found, symtab zero filled ++ * 1 Symbol mapped to module/symbol/section, data in symtab ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++int ++kdbgetsymval(const char *symname, kdb_symtab_t *symtab) ++{ ++ if (KDB_DEBUG(AR)) ++ kdb_printf("kdbgetsymval: symname=%s, symtab=%p\n", symname, symtab); ++ memset(symtab, 0, sizeof(*symtab)); ++ ++ if ((symtab->sym_start = kallsyms_lookup_name(symname))) { ++ if (KDB_DEBUG(AR)) ++ kdb_printf("kdbgetsymval: returns 1, symtab->sym_start=0x%lx\n", symtab->sym_start); ++ return 1; ++ } ++ if (KDB_DEBUG(AR)) ++ kdb_printf("kdbgetsymval: returns 0\n"); ++ return 0; ++} ++EXPORT_SYMBOL(kdbgetsymval); ++ ++/* ++ * kdbnearsym ++ * ++ * Return the name of the symbol with the nearest address ++ * less than 'addr'. ++ * ++ * Parameters: ++ * addr Address to check for symbol near ++ * symtab Structure to receive results ++ * Outputs: ++ * Returns: ++ * 0 No sections contain this address, symtab zero filled ++ * 1 Address mapped to module/symbol/section, data in symtab ++ * Locking: ++ * None. ++ * Remarks: ++ * 2.6 kallsyms has a "feature" where it unpacks the name into a string. ++ * If that string is reused before the caller expects it then the caller ++ * sees its string change without warning. To avoid cluttering up the ++ * main kdb code with lots of kdb_strdup, tests and kfree calls, kdbnearsym ++ * maintains an LRU list of the last few unique strings. The list is sized ++ * large enough to hold active strings, no kdb caller of kdbnearsym makes ++ * more than ~20 later calls before using a saved value. ++ */ ++ ++static char *kdb_name_table[100]; /* arbitrary size */ ++ ++int ++kdbnearsym(unsigned long addr, kdb_symtab_t *symtab) ++{ ++ int ret = 0; ++ unsigned long symbolsize; ++ unsigned long offset; ++#define knt1_size 128 /* must be >= kallsyms table size */ ++ char *knt1 = NULL; ++ ++ if (KDB_DEBUG(AR)) ++ kdb_printf("kdbnearsym: addr=0x%lx, symtab=%p\n", addr, symtab); ++ memset(symtab, 0, sizeof(*symtab)); ++ ++ if (addr < 4096) ++ goto out; ++ knt1 = debug_kmalloc(knt1_size, GFP_ATOMIC); ++ if (!knt1) { ++ kdb_printf("kdbnearsym: addr=0x%lx cannot kmalloc knt1\n", addr); ++ goto out; ++ } ++ symtab->sym_name = kallsyms_lookup(addr, &symbolsize , &offset, (char **)(&symtab->mod_name), knt1); ++ if (offset > 8*1024*1024) { ++ symtab->sym_name = NULL; ++ addr = offset = symbolsize = 0; ++ } ++ symtab->sym_start = addr - offset; ++ symtab->sym_end = symtab->sym_start + symbolsize; ++ ret = symtab->sym_name != NULL && *(symtab->sym_name) != '\0'; ++ ++ if (ret) { ++ int i; ++ /* Another 2.6 kallsyms "feature". Sometimes the sym_name is ++ * set but the buffer passed into kallsyms_lookup is not used, ++ * so it contains garbage. The caller has to work out which ++ * buffer needs to be saved. ++ * ++ * What was Rusty smoking when he wrote that code? ++ */ ++ if (symtab->sym_name != knt1) { ++ strncpy(knt1, symtab->sym_name, knt1_size); ++ knt1[knt1_size-1] = '\0'; ++ } ++ for (i = 0; i < ARRAY_SIZE(kdb_name_table); ++i) { ++ if (kdb_name_table[i] && strcmp(kdb_name_table[i], knt1) == 0) ++ break; ++ } ++ if (i >= ARRAY_SIZE(kdb_name_table)) { ++ debug_kfree(kdb_name_table[0]); ++ memcpy(kdb_name_table, kdb_name_table+1, ++ sizeof(kdb_name_table[0])*(ARRAY_SIZE(kdb_name_table)-1)); ++ } else { ++ debug_kfree(knt1); ++ knt1 = kdb_name_table[i]; ++ memcpy(kdb_name_table+i, kdb_name_table+i+1, ++ sizeof(kdb_name_table[0])*(ARRAY_SIZE(kdb_name_table)-i-1)); ++ } ++ i = ARRAY_SIZE(kdb_name_table) - 1; ++ kdb_name_table[i] = knt1; ++ symtab->sym_name = kdb_name_table[i]; ++ knt1 = NULL; ++ } ++ ++ if (symtab->mod_name == NULL) ++ symtab->mod_name = "kernel"; ++ if (KDB_DEBUG(AR)) ++ kdb_printf("kdbnearsym: returns %d symtab->sym_start=0x%lx, symtab->mod_name=%p, symtab->sym_name=%p (%s)\n", ret, symtab->sym_start, symtab->mod_name, symtab->sym_name, symtab->sym_name); ++ ++out: ++ debug_kfree(knt1); ++ return ret; ++} ++ ++void ++kdbnearsym_cleanup(void) ++{ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(kdb_name_table); ++i) { ++ if (kdb_name_table[i]) { ++ debug_kfree(kdb_name_table[i]); ++ kdb_name_table[i] = NULL; ++ } ++ } ++} ++ ++/* ++ * kallsyms_symbol_complete ++ * ++ * Parameters: ++ * prefix_name prefix of a symbol name to lookup ++ * max_len maximum length that can be returned ++ * Returns: ++ * Number of symbols which match the given prefix. ++ * Notes: ++ * prefix_name is changed to contain the longest unique prefix that ++ * starts with this prefix (tab completion). ++ */ ++ ++static char ks_namebuf[KSYM_NAME_LEN+1], ks_namebuf_prev[KSYM_NAME_LEN+1]; ++ ++int kallsyms_symbol_complete(char *prefix_name, int max_len) ++{ ++ loff_t pos = 0; ++ int prefix_len = strlen(prefix_name), prev_len = 0; ++ int i, number = 0; ++ const char *name; ++ ++ while ((name = kdb_walk_kallsyms(&pos))) { ++ if (strncmp(name, prefix_name, prefix_len) == 0) { ++ strcpy(ks_namebuf, name); ++ /* Work out the longest name that matches the prefix */ ++ if (++number == 1) { ++ prev_len = min_t(int, max_len-1, strlen(ks_namebuf)); ++ memcpy(ks_namebuf_prev, ks_namebuf, prev_len); ++ ks_namebuf_prev[prev_len] = '\0'; ++ } else for (i = 0; i < prev_len; ++i) { ++ if (ks_namebuf[i] != ks_namebuf_prev[i]) { ++ prev_len = i; ++ ks_namebuf_prev[i] = '\0'; ++ break; ++ } ++ } ++ } ++ } ++ if (prev_len > prefix_len) ++ memcpy(prefix_name, ks_namebuf_prev, prev_len+1); ++ return number; ++} ++ ++/* ++ * kallsyms_symbol_next ++ * ++ * Parameters: ++ * prefix_name prefix of a symbol name to lookup ++ * flag 0 means search from the head, 1 means continue search. ++ * Returns: ++ * 1 if a symbol matches the given prefix. ++ * 0 if no string found ++ */ ++ ++int kallsyms_symbol_next(char *prefix_name, int flag) ++{ ++ int prefix_len = strlen(prefix_name); ++ static loff_t pos; ++ const char *name; ++ ++ if (!flag) ++ pos = 0; ++ ++ while ((name = kdb_walk_kallsyms(&pos))) { ++ if (strncmp(name, prefix_name, prefix_len) == 0) { ++ strncpy(prefix_name, name, strlen(name)+1); ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++#if defined(CONFIG_SMP) ++/* ++ * kdb_ipi ++ * ++ * This function is called from the non-maskable interrupt ++ * handler to handle a kdb IPI instruction. ++ * ++ * Inputs: ++ * regs = Exception frame pointer ++ * Outputs: ++ * None. ++ * Returns: ++ * 0 - Did not handle NMI ++ * 1 - Handled NMI ++ * Locking: ++ * None. ++ * Remarks: ++ * Initially one processor is invoked in the kdb() code. That ++ * processor sends an ipi which drives this routine on the other ++ * processors. All this does is call kdb() with reason SWITCH. ++ * This puts all processors into the kdb() routine and all the ++ * code for breakpoints etc. is in one place. ++ * One problem with the way the kdb NMI is sent, the NMI has no ++ * identification that says it came from kdb. If the cpu's kdb state is ++ * marked as "waiting for kdb_ipi" then the NMI is treated as coming from ++ * kdb, otherwise it is assumed to be for another reason and is ignored. ++ */ ++ ++int ++kdb_ipi(struct pt_regs *regs, void (*ack_interrupt)(void)) ++{ ++ /* Do not print before checking and clearing WAIT_IPI, IPIs are ++ * going all the time. ++ */ ++ if (KDB_STATE(WAIT_IPI)) { ++ /* ++ * Stopping other processors via smp_kdb_stop(). ++ */ ++ if (ack_interrupt) ++ (*ack_interrupt)(); /* Acknowledge the interrupt */ ++ KDB_STATE_CLEAR(WAIT_IPI); ++ KDB_DEBUG_STATE("kdb_ipi 1", 0); ++ kdb(KDB_REASON_SWITCH, 0, regs); /* Spin in kdb() */ ++ KDB_DEBUG_STATE("kdb_ipi 2", 0); ++ return 1; ++ } ++ return 0; ++} ++#endif /* CONFIG_SMP */ ++ ++/* ++ * kdb_symbol_print ++ * ++ * Standard method for printing a symbol name and offset. ++ * Inputs: ++ * addr Address to be printed. ++ * symtab Address of symbol data, if NULL this routine does its ++ * own lookup. ++ * punc Punctuation for string, bit field. ++ * Outputs: ++ * None. ++ * Returns: ++ * Always 0. ++ * Locking: ++ * none. ++ * Remarks: ++ * The string and its punctuation is only printed if the address ++ * is inside the kernel, except that the value is always printed ++ * when requested. ++ */ ++ ++void ++kdb_symbol_print(kdb_machreg_t addr, const kdb_symtab_t *symtab_p, unsigned int punc) ++{ ++ kdb_symtab_t symtab, *symtab_p2; ++ if (symtab_p) { ++ symtab_p2 = (kdb_symtab_t *)symtab_p; ++ } ++ else { ++ symtab_p2 = &symtab; ++ kdbnearsym(addr, symtab_p2); ++ } ++ if (symtab_p2->sym_name || (punc & KDB_SP_VALUE)) { ++ ; /* drop through */ ++ } ++ else { ++ return; ++ } ++ if (punc & KDB_SP_SPACEB) { ++ kdb_printf(" "); ++ } ++ if (punc & KDB_SP_VALUE) { ++ kdb_printf(kdb_machreg_fmt0, addr); ++ } ++ if (symtab_p2->sym_name) { ++ if (punc & KDB_SP_VALUE) { ++ kdb_printf(" "); ++ } ++ if (punc & KDB_SP_PAREN) { ++ kdb_printf("("); ++ } ++ if (strcmp(symtab_p2->mod_name, "kernel")) { ++ kdb_printf("[%s]", symtab_p2->mod_name); ++ } ++ kdb_printf("%s", symtab_p2->sym_name); ++ if (addr != symtab_p2->sym_start) { ++ kdb_printf("+0x%lx", addr - symtab_p2->sym_start); ++ } ++ if (punc & KDB_SP_SYMSIZE) { ++ kdb_printf("/0x%lx", symtab_p2->sym_end - symtab_p2->sym_start); ++ } ++ if (punc & KDB_SP_PAREN) { ++ kdb_printf(")"); ++ } ++ } ++ if (punc & KDB_SP_SPACEA) { ++ kdb_printf(" "); ++ } ++ if (punc & KDB_SP_NEWLINE) { ++ kdb_printf("\n"); ++ } ++} ++ ++/* ++ * kdb_strdup ++ * ++ * kdb equivalent of strdup, for disasm code. ++ * Inputs: ++ * str The string to duplicate. ++ * type Flags to kmalloc for the new string. ++ * Outputs: ++ * None. ++ * Returns: ++ * Address of the new string, NULL if storage could not be allocated. ++ * Locking: ++ * none. ++ * Remarks: ++ * This is not in lib/string.c because it uses kmalloc which is not ++ * available when string.o is used in boot loaders. ++ */ ++ ++char *kdb_strdup(const char *str, gfp_t type) ++{ ++ int n = strlen(str)+1; ++ char *s = kmalloc(n, type); ++ if (!s) return NULL; ++ return strcpy(s, str); ++} ++ ++/* ++ * kdb_getarea_size ++ * ++ * Read an area of data. The kdb equivalent of copy_from_user, with ++ * kdb messages for invalid addresses. ++ * Inputs: ++ * res Pointer to the area to receive the result. ++ * addr Address of the area to copy. ++ * size Size of the area. ++ * Outputs: ++ * none. ++ * Returns: ++ * 0 for success, < 0 for error. ++ * Locking: ++ * none. ++ */ ++ ++int kdb_getarea_size(void *res, unsigned long addr, size_t size) ++{ ++ int ret = kdba_getarea_size(res, addr, size); ++ if (ret) { ++ if (!KDB_STATE(SUPPRESS)) { ++ kdb_printf("kdb_getarea: Bad address 0x%lx\n", addr); ++ KDB_STATE_SET(SUPPRESS); ++ } ++ ret = KDB_BADADDR; ++ } ++ else { ++ KDB_STATE_CLEAR(SUPPRESS); ++ } ++ return(ret); ++} ++ ++/* ++ * kdb_putarea_size ++ * ++ * Write an area of data. The kdb equivalent of copy_to_user, with ++ * kdb messages for invalid addresses. ++ * Inputs: ++ * addr Address of the area to write to. ++ * res Pointer to the area holding the data. ++ * size Size of the area. ++ * Outputs: ++ * none. ++ * Returns: ++ * 0 for success, < 0 for error. ++ * Locking: ++ * none. ++ */ ++ ++int kdb_putarea_size(unsigned long addr, void *res, size_t size) ++{ ++ int ret = kdba_putarea_size(addr, res, size); ++ if (ret) { ++ if (!KDB_STATE(SUPPRESS)) { ++ kdb_printf("kdb_putarea: Bad address 0x%lx\n", addr); ++ KDB_STATE_SET(SUPPRESS); ++ } ++ ret = KDB_BADADDR; ++ } ++ else { ++ KDB_STATE_CLEAR(SUPPRESS); ++ } ++ return(ret); ++} ++ ++/* ++ * kdb_getphys ++ * ++ * Read data from a physical address. Validate the address is in range, ++ * use kmap_atomic() to get data ++ * ++ * Similar to kdb_getarea() - but for phys addresses ++ * ++ * Inputs: ++ * res Pointer to the word to receive the result ++ * addr Physical address of the area to copy ++ * size Size of the area ++ * Outputs: ++ * none. ++ * Returns: ++ * 0 for success, < 0 for error. ++ * Locking: ++ * none. ++ */ ++static int kdb_getphys(void *res, unsigned long addr, size_t size) ++{ ++ unsigned long pfn; ++ void *vaddr; ++ struct page *page; ++ ++ pfn = (addr >> PAGE_SHIFT); ++ if (!pfn_valid(pfn)) ++ return 1; ++ page = pfn_to_page(pfn); ++ vaddr = kmap_atomic(page, KM_KDB); ++ memcpy(res, vaddr + (addr & (PAGE_SIZE -1)), size); ++ kunmap_atomic(vaddr, KM_KDB); ++ ++ return 0; ++} ++ ++/* ++ * kdb_getphysword ++ * ++ * Inputs: ++ * word Pointer to the word to receive the result. ++ * addr Address of the area to copy. ++ * size Size of the area. ++ * Outputs: ++ * none. ++ * Returns: ++ * 0 for success, < 0 for error. ++ * Locking: ++ * none. ++ */ ++int kdb_getphysword(unsigned long *word, unsigned long addr, size_t size) ++{ ++ int diag; ++ __u8 w1; ++ __u16 w2; ++ __u32 w4; ++ __u64 w8; ++ *word = 0; /* Default value if addr or size is invalid */ ++ ++ switch (size) { ++ case 1: ++ if (!(diag = kdb_getphys(&w1, addr, sizeof(w1)))) ++ *word = w1; ++ break; ++ case 2: ++ if (!(diag = kdb_getphys(&w2, addr, sizeof(w2)))) ++ *word = w2; ++ break; ++ case 4: ++ if (!(diag = kdb_getphys(&w4, addr, sizeof(w4)))) ++ *word = w4; ++ break; ++ case 8: ++ if (size <= sizeof(*word)) { ++ if (!(diag = kdb_getphys(&w8, addr, sizeof(w8)))) ++ *word = w8; ++ break; ++ } ++ /* drop through */ ++ default: ++ diag = KDB_BADWIDTH; ++ kdb_printf("kdb_getphysword: bad width %ld\n", (long) size); ++ } ++ return(diag); ++} ++ ++/* ++ * kdb_getword ++ * ++ * Read a binary value. Unlike kdb_getarea, this treats data as numbers. ++ * Inputs: ++ * word Pointer to the word to receive the result. ++ * addr Address of the area to copy. ++ * size Size of the area. ++ * Outputs: ++ * none. ++ * Returns: ++ * 0 for success, < 0 for error. ++ * Locking: ++ * none. ++ */ ++ ++int kdb_getword(unsigned long *word, unsigned long addr, size_t size) ++{ ++ int diag; ++ __u8 w1; ++ __u16 w2; ++ __u32 w4; ++ __u64 w8; ++ *word = 0; /* Default value if addr or size is invalid */ ++ switch (size) { ++ case 1: ++ if (!(diag = kdb_getarea(w1, addr))) ++ *word = w1; ++ break; ++ case 2: ++ if (!(diag = kdb_getarea(w2, addr))) ++ *word = w2; ++ break; ++ case 4: ++ if (!(diag = kdb_getarea(w4, addr))) ++ *word = w4; ++ break; ++ case 8: ++ if (size <= sizeof(*word)) { ++ if (!(diag = kdb_getarea(w8, addr))) ++ *word = w8; ++ break; ++ } ++ /* drop through */ ++ default: ++ diag = KDB_BADWIDTH; ++ kdb_printf("kdb_getword: bad width %ld\n", (long) size); ++ } ++ return(diag); ++} ++ ++/* ++ * kdb_putword ++ * ++ * Write a binary value. Unlike kdb_putarea, this treats data as numbers. ++ * Inputs: ++ * addr Address of the area to write to.. ++ * word The value to set. ++ * size Size of the area. ++ * Outputs: ++ * none. ++ * Returns: ++ * 0 for success, < 0 for error. ++ * Locking: ++ * none. ++ */ ++ ++int kdb_putword(unsigned long addr, unsigned long word, size_t size) ++{ ++ int diag; ++ __u8 w1; ++ __u16 w2; ++ __u32 w4; ++ __u64 w8; ++ switch (size) { ++ case 1: ++ w1 = word; ++ diag = kdb_putarea(addr, w1); ++ break; ++ case 2: ++ w2 = word; ++ diag = kdb_putarea(addr, w2); ++ break; ++ case 4: ++ w4 = word; ++ diag = kdb_putarea(addr, w4); ++ break; ++ case 8: ++ if (size <= sizeof(word)) { ++ w8 = word; ++ diag = kdb_putarea(addr, w8); ++ break; ++ } ++ /* drop through */ ++ default: ++ diag = KDB_BADWIDTH; ++ kdb_printf("kdb_putword: bad width %ld\n", (long) size); ++ } ++ return(diag); ++} ++ ++/* ++ * kdb_task_state_string ++ * ++ * Convert a string containing any of the letters DRSTCZEUIMA to a mask ++ * for the process state field and return the value. If no argument is ++ * supplied, return the mask that corresponds to environment variable PS, ++ * DRSTCZEU by default. ++ * Inputs: ++ * s String to convert ++ * Outputs: ++ * none. ++ * Returns: ++ * Mask for process state. ++ * Locking: ++ * none. ++ * Notes: ++ * The mask folds data from several sources into a single long value, so ++ * be carefull not to overlap the bits. TASK_* bits are in the LSB, ++ * special cases like UNRUNNABLE are in the MSB. As of 2.6.10-rc1 there ++ * is no overlap between TASK_* and EXIT_* but that may not always be ++ * true, so EXIT_* bits are shifted left 16 bits before being stored in ++ * the mask. ++ */ ++ ++#define UNRUNNABLE (1UL << (8*sizeof(unsigned long) - 1)) /* unrunnable is < 0 */ ++#define RUNNING (1UL << (8*sizeof(unsigned long) - 2)) ++#define IDLE (1UL << (8*sizeof(unsigned long) - 3)) ++#define DAEMON (1UL << (8*sizeof(unsigned long) - 4)) ++ ++unsigned long ++kdb_task_state_string(const char *s) ++{ ++ long res = 0; ++ if (!s && !(s = kdbgetenv("PS"))) { ++ s = "DRSTCZEU"; /* default value for ps */ ++ } ++ while (*s) { ++ switch (*s) { ++ case 'D': res |= TASK_UNINTERRUPTIBLE; break; ++ case 'R': res |= RUNNING; break; ++ case 'S': res |= TASK_INTERRUPTIBLE; break; ++ case 'T': res |= TASK_STOPPED; break; ++ case 'C': res |= TASK_TRACED; break; ++ case 'Z': res |= EXIT_ZOMBIE << 16; break; ++ case 'E': res |= EXIT_DEAD << 16; break; ++ case 'U': res |= UNRUNNABLE; break; ++ case 'I': res |= IDLE; break; ++ case 'M': res |= DAEMON; break; ++ case 'A': res = ~0UL; break; ++ default: ++ kdb_printf("%s: unknown flag '%c' ignored\n", __FUNCTION__, *s); ++ break; ++ } ++ ++s; ++ } ++ return res; ++} ++ ++/* ++ * kdb_task_state_char ++ * ++ * Return the character that represents the task state. ++ * Inputs: ++ * p struct task for the process ++ * Outputs: ++ * none. ++ * Returns: ++ * One character to represent the task state. ++ * Locking: ++ * none. ++ */ ++ ++char ++kdb_task_state_char (const struct task_struct *p) ++{ ++ int cpu = kdb_process_cpu(p); ++ struct kdb_running_process *krp = kdb_running_process + cpu; ++ char state = (p->state == 0) ? 'R' : ++ (p->state < 0) ? 'U' : ++ (p->state & TASK_UNINTERRUPTIBLE) ? 'D' : ++ (p->state & TASK_STOPPED) ? 'T' : ++ (p->state & TASK_TRACED) ? 'C' : ++ (p->exit_state & EXIT_ZOMBIE) ? 'Z' : ++ (p->exit_state & EXIT_DEAD) ? 'E' : ++ (p->state & TASK_INTERRUPTIBLE) ? 'S' : '?'; ++ if (p->pid == 0) { ++ /* Idle task. Is it really idle, apart from the kdb interrupt? */ ++ if (!kdb_task_has_cpu(p) || krp->irq_depth == 1) { ++ /* There is a corner case when the idle task takes an ++ * interrupt and dies in the interrupt code. It has an ++ * interrupt count of 1 but that did not come from kdb. ++ * This corner case can only occur on the initial cpu, ++ * all the others were entered via the kdb IPI. ++ */ ++ if (cpu != kdb_initial_cpu || KDB_STATE_CPU(KEYBOARD, cpu)) ++ state = 'I'; /* idle task */ ++ } ++ } ++ else if (!p->mm && state == 'S') { ++ state = 'M'; /* sleeping system daemon */ ++ } ++ return state; ++} ++ ++/* ++ * kdb_task_state ++ * ++ * Return true if a process has the desired state given by the mask. ++ * Inputs: ++ * p struct task for the process ++ * mask mask from kdb_task_state_string to select processes ++ * Outputs: ++ * none. ++ * Returns: ++ * True if the process matches at least one criteria defined by the mask. ++ * Locking: ++ * none. ++ */ ++ ++unsigned long ++kdb_task_state(const struct task_struct *p, unsigned long mask) ++{ ++ char state[] = { kdb_task_state_char(p), '\0' }; ++ return (mask & kdb_task_state_string(state)) != 0; ++} ++ ++struct kdb_running_process kdb_running_process[NR_CPUS]; ++ ++/* Save the state of a running process and invoke kdb_main_loop. This is ++ * invoked on the current process on each cpu (assuming the cpu is responding). ++ */ ++ ++int ++kdb_save_running(struct pt_regs *regs, kdb_reason_t reason, ++ kdb_reason_t reason2, int error, kdb_dbtrap_t db_result) ++{ ++ struct kdb_running_process *krp = kdb_running_process + smp_processor_id(); ++ krp->p = current; ++ krp->regs = regs; ++ krp->seqno = kdb_seqno; ++ krp->irq_depth = hardirq_count() >> HARDIRQ_SHIFT; ++ kdba_save_running(&(krp->arch), regs); ++ return kdb_main_loop(reason, reason2, error, db_result, regs); ++} ++ ++/* ++ * kdb_unsave_running ++ * ++ * Reverse the effect of kdb_save_running. ++ * Inputs: ++ * regs struct pt_regs for the process ++ * Outputs: ++ * Updates kdb_running_process[] for this cpu. ++ * Returns: ++ * none. ++ * Locking: ++ * none. ++ */ ++ ++void ++kdb_unsave_running(struct pt_regs *regs) ++{ ++ struct kdb_running_process *krp = kdb_running_process + smp_processor_id(); ++ kdba_unsave_running(&(krp->arch), regs); ++ krp->seqno = 0; ++} ++ ++ ++/* ++ * kdb_print_nameval ++ * ++ * Print a name and its value, converting the value to a symbol lookup ++ * if possible. ++ * Inputs: ++ * name field name to print ++ * val value of field ++ * Outputs: ++ * none. ++ * Returns: ++ * none. ++ * Locking: ++ * none. ++ */ ++ ++void ++kdb_print_nameval(const char *name, unsigned long val) ++{ ++ kdb_symtab_t symtab; ++ kdb_printf(" %-11.11s ", name); ++ if (kdbnearsym(val, &symtab)) ++ kdb_symbol_print(val, &symtab, KDB_SP_VALUE|KDB_SP_SYMSIZE|KDB_SP_NEWLINE); ++ else ++ kdb_printf("0x%lx\n", val); ++} ++ ++static struct page * kdb_get_one_user_page(const struct task_struct *tsk, unsigned long start, ++ int len, int write) ++{ ++ struct mm_struct *mm = tsk->mm; ++ unsigned int flags; ++ struct vm_area_struct * vma; ++ ++ /* shouldn't cross a page boundary. */ ++ if ((start & PAGE_MASK) != ((start+len) & PAGE_MASK)) ++ return NULL; ++ ++ /* we need to align start address to the current page boundy, PAGE_ALIGN ++ * aligns to next page boundry. ++ * FIXME: What about hugetlb? ++ */ ++ start = start & PAGE_MASK; ++ flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD); ++ ++ vma = find_extend_vma(mm, start); ++ ++ /* may be we can allow access to VM_IO pages inside KDB? */ ++ if (!vma || (vma->vm_flags & VM_IO) || !(flags & vma->vm_flags)) ++ return NULL; ++ ++ return follow_page(vma, start, write ? FOLL_WRITE : 0); ++} ++ ++int kdb_getuserarea_size(void *to, unsigned long from, size_t size) ++{ ++ struct page *page; ++ void *vaddr; ++ ++ page = kdb_get_one_user_page(kdb_current_task, from, size, 0); ++ if (!page) ++ return size; ++ ++ vaddr = kmap_atomic(page, KM_KDB); ++ memcpy(to, vaddr+ (from & (PAGE_SIZE - 1)), size); ++ kunmap_atomic(vaddr, KM_KDB); ++ ++ return 0; ++} ++ ++int kdb_putuserarea_size(unsigned long to, void *from, size_t size) ++{ ++ struct page *page; ++ void *vaddr; ++ ++ page = kdb_get_one_user_page(kdb_current_task, to, size, 1); ++ if (!page) ++ return size; ++ ++ vaddr = kmap_atomic(page, KM_KDB); ++ memcpy(vaddr+ (to & (PAGE_SIZE - 1)), from, size); ++ kunmap_atomic(vaddr, KM_KDB); ++ ++ return 0; ++} ++ ++/* Last ditch allocator for debugging, so we can still debug even when the ++ * GFP_ATOMIC pool has been exhausted. The algorithms are tuned for space ++ * usage, not for speed. One smallish memory pool, the free chain is always in ++ * ascending address order to allow coalescing, allocations are done in brute ++ * force best fit. ++ */ ++ ++struct debug_alloc_header { ++ u32 next; /* offset of next header from start of pool */ ++ u32 size; ++ void *caller; ++}; ++ ++/* The memory returned by this allocator must be aligned, which means so must ++ * the header size. Do not assume that sizeof(struct debug_alloc_header) is a ++ * multiple of the alignment, explicitly calculate the overhead of this header, ++ * including the alignment. The rest of this code must not use sizeof() on any ++ * header or pointer to a header. ++ */ ++#define dah_align 8 ++#define dah_overhead ALIGN(sizeof(struct debug_alloc_header), dah_align) ++ ++static u64 debug_alloc_pool_aligned[256*1024/dah_align]; /* 256K pool */ ++static char *debug_alloc_pool = (char *)debug_alloc_pool_aligned; ++static u32 dah_first, dah_first_call = 1, dah_used = 0, dah_used_max = 0; ++ ++/* Locking is awkward. The debug code is called from all contexts, including ++ * non maskable interrupts. A normal spinlock is not safe in NMI context. Try ++ * to get the debug allocator lock, if it cannot be obtained after a second ++ * then give up. If the lock could not be previously obtained on this cpu then ++ * only try once. ++ * ++ * sparse has no annotation for "this function _sometimes_ acquires a lock", so ++ * fudge the acquire/release notation. ++ */ ++static DEFINE_SPINLOCK(dap_lock); ++static int ++get_dap_lock(void) ++ __acquires(dap_lock) ++{ ++ static int dap_locked = -1; ++ int count; ++ if (dap_locked == smp_processor_id()) ++ count = 1; ++ else ++ count = 1000; ++ while (1) { ++ if (spin_trylock(&dap_lock)) { ++ dap_locked = -1; ++ return 1; ++ } ++ if (!count--) ++ break; ++ udelay(1000); ++ } ++ dap_locked = smp_processor_id(); ++ __acquire(dap_lock); ++ return 0; ++} ++ ++void ++*debug_kmalloc(size_t size, gfp_t flags) ++{ ++ unsigned int rem, h_offset; ++ struct debug_alloc_header *best, *bestprev, *prev, *h; ++ void *p = NULL; ++ if (!get_dap_lock()) { ++ __release(dap_lock); /* we never actually got it */ ++ return NULL; ++ } ++ h = (struct debug_alloc_header *)(debug_alloc_pool + dah_first); ++ if (dah_first_call) { ++ h->size = sizeof(debug_alloc_pool_aligned) - dah_overhead; ++ dah_first_call = 0; ++ } ++ size = ALIGN(size, dah_align); ++ prev = best = bestprev = NULL; ++ while (1) { ++ if (h->size >= size && (!best || h->size < best->size)) { ++ best = h; ++ bestprev = prev; ++ if (h->size == size) ++ break; ++ } ++ if (!h->next) ++ break; ++ prev = h; ++ h = (struct debug_alloc_header *)(debug_alloc_pool + h->next); ++ } ++ if (!best) ++ goto out; ++ rem = best->size - size; ++ /* The pool must always contain at least one header */ ++ if (best->next == 0 && bestprev == NULL && rem < dah_overhead) ++ goto out; ++ if (rem >= dah_overhead) { ++ best->size = size; ++ h_offset = ((char *)best - debug_alloc_pool) + ++ dah_overhead + best->size; ++ h = (struct debug_alloc_header *)(debug_alloc_pool + h_offset); ++ h->size = rem - dah_overhead; ++ h->next = best->next; ++ } else ++ h_offset = best->next; ++ best->caller = __builtin_return_address(0); ++ dah_used += best->size; ++ dah_used_max = max(dah_used, dah_used_max); ++ if (bestprev) ++ bestprev->next = h_offset; ++ else ++ dah_first = h_offset; ++ p = (char *)best + dah_overhead; ++ memset(p, POISON_INUSE, best->size - 1); ++ *((char *)p + best->size - 1) = POISON_END; ++out: ++ spin_unlock(&dap_lock); ++ return p; ++} ++ ++void ++debug_kfree(void *p) ++{ ++ struct debug_alloc_header *h; ++ unsigned int h_offset; ++ if (!p) ++ return; ++ if ((char *)p < debug_alloc_pool || ++ (char *)p >= debug_alloc_pool + sizeof(debug_alloc_pool_aligned)) { ++ kfree(p); ++ return; ++ } ++ if (!get_dap_lock()) { ++ __release(dap_lock); /* we never actually got it */ ++ return; /* memory leak, cannot be helped */ ++ } ++ h = (struct debug_alloc_header *)((char *)p - dah_overhead); ++ memset(p, POISON_FREE, h->size - 1); ++ *((char *)p + h->size - 1) = POISON_END; ++ h->caller = NULL; ++ dah_used -= h->size; ++ h_offset = (char *)h - debug_alloc_pool; ++ if (h_offset < dah_first) { ++ h->next = dah_first; ++ dah_first = h_offset; ++ } else { ++ struct debug_alloc_header *prev; ++ unsigned int prev_offset; ++ prev = (struct debug_alloc_header *)(debug_alloc_pool + dah_first); ++ while (1) { ++ if (!prev->next || prev->next > h_offset) ++ break; ++ prev = (struct debug_alloc_header *) ++ (debug_alloc_pool + prev->next); ++ } ++ prev_offset = (char *)prev - debug_alloc_pool; ++ if (prev_offset + dah_overhead + prev->size == h_offset) { ++ prev->size += dah_overhead + h->size; ++ memset(h, POISON_FREE, dah_overhead - 1); ++ *((char *)h + dah_overhead - 1) = POISON_END; ++ h = prev; ++ h_offset = prev_offset; ++ } else { ++ h->next = prev->next; ++ prev->next = h_offset; ++ } ++ } ++ if (h_offset + dah_overhead + h->size == h->next) { ++ struct debug_alloc_header *next; ++ next = (struct debug_alloc_header *) ++ (debug_alloc_pool + h->next); ++ h->size += dah_overhead + next->size; ++ h->next = next->next; ++ memset(next, POISON_FREE, dah_overhead - 1); ++ *((char *)next + dah_overhead - 1) = POISON_END; ++ } ++ spin_unlock(&dap_lock); ++} ++ ++void ++debug_kusage(void) ++{ ++ struct debug_alloc_header *h_free, *h_used; ++#ifdef CONFIG_IA64 ++ /* FIXME: using dah for ia64 unwind always results in a memory leak. ++ * Fix that memory leak first, then set debug_kusage_one_time = 1 for ++ * all architectures. ++ */ ++ static int debug_kusage_one_time = 0; ++#else ++ static int debug_kusage_one_time = 1; ++#endif ++ if (!get_dap_lock()) { ++ __release(dap_lock); /* we never actually got it */ ++ return; ++ } ++ h_free = (struct debug_alloc_header *)(debug_alloc_pool + dah_first); ++ if (dah_first == 0 && ++ (h_free->size == sizeof(debug_alloc_pool_aligned) - dah_overhead || ++ dah_first_call)) ++ goto out; ++ if (!debug_kusage_one_time) ++ goto out; ++ debug_kusage_one_time = 0; ++ kdb_printf("%s: debug_kmalloc memory leak dah_first %d\n", ++ __FUNCTION__, dah_first); ++ if (dah_first) { ++ h_used = (struct debug_alloc_header *)debug_alloc_pool; ++ kdb_printf("%s: h_used %p size %d\n", __FUNCTION__, h_used, h_used->size); ++ } ++ do { ++ h_used = (struct debug_alloc_header *) ++ ((char *)h_free + dah_overhead + h_free->size); ++ kdb_printf("%s: h_used %p size %d caller %p\n", ++ __FUNCTION__, h_used, h_used->size, h_used->caller); ++ h_free = (struct debug_alloc_header *) ++ (debug_alloc_pool + h_free->next); ++ } while (h_free->next); ++ h_used = (struct debug_alloc_header *) ++ ((char *)h_free + dah_overhead + h_free->size); ++ if ((char *)h_used - debug_alloc_pool != ++ sizeof(debug_alloc_pool_aligned)) ++ kdb_printf("%s: h_used %p size %d caller %p\n", ++ __FUNCTION__, h_used, h_used->size, h_used->caller); ++out: ++ spin_unlock(&dap_lock); ++} ++ ++/* Maintain a small stack of kdb_flags to allow recursion without disturbing ++ * the global kdb state. ++ */ ++ ++static int kdb_flags_stack[4], kdb_flags_index; ++ ++void ++kdb_save_flags(void) ++{ ++ BUG_ON(kdb_flags_index >= ARRAY_SIZE(kdb_flags_stack)); ++ kdb_flags_stack[kdb_flags_index++] = kdb_flags; ++} ++ ++void ++kdb_restore_flags(void) ++{ ++ BUG_ON(kdb_flags_index <= 0); ++ kdb_flags = kdb_flags_stack[--kdb_flags_index]; ++} +--- /dev/null ++++ b/kdb/modules/Makefile +@@ -0,0 +1,14 @@ ++# ++# This file is subject to the terms and conditions of the GNU General Public ++# License. See the file "COPYING" in the main directory of this archive ++# for more details. ++# ++# Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. ++# ++ ++obj-$(CONFIG_KDB_MODULES) += kdbm_pg.o kdbm_task.o kdbm_vm.o kdbm_sched.o ++obj-m += kdbm_debugtypes.o ++ifdef CONFIG_X86 ++obj-$(CONFIG_KDB_MODULES) += kdbm_x86.o ++endif ++CFLAGS_kdbm_vm.o += -I $(srctree)/drivers/scsi +--- /dev/null ++++ b/kdb/modules/kdbm_debugtypes.c +@@ -0,0 +1,388 @@ ++/* this one has some additional address validation - untested */ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * ++ * Most of this code is borrowed and adapted from the lkcd command "lcrash" ++ * and its supporting libarary. ++ * ++ * This module provides kdb commands for casting memory structures. ++ * It loads symbolic debugging info (provided from lcrash -o), and provides ++ * "print" "px", "pd" ++ * (this information originally comes from the lcrash "kerntypes" file) ++ * ++ * A key here is tacking a file of debug info onto this module, for ++ * load with it at insmod time. ++ * ++ * Careful of porting the klib KL_XXX functions (they call thru a jump table ++ * that we don't use here) ++ * ++ * Usage: ++ * in order for the insmod kdbm_debugtypes.ko to succeed in loading types ++ * you must first use lcrash -t kerntypes.xxxx -o debug_info ++ * and echo debug_info > /proc/kdb/debug_info_name ++ */ ++ ++#define VMALLOC_START_IA64 0xa000000200000000 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "lcrash/klib.h" ++#include "lcrash/kl_stringtab.h" ++#include "lcrash/kl_btnode.h" ++#include "lcrash/lc_eval.h" ++ ++MODULE_AUTHOR("SGI"); ++MODULE_DESCRIPTION("Load symbolic debugging information"); ++MODULE_LICENSE("GPL"); ++ ++#undef next_node /* collision with nodemask.h */ ++static char *stringstorage, **stringp_array; ++static void *filestorage; ++static long num_strings, num_kltypes, num_dsyms, stringstorage_size; ++extern int have_debug_file; ++extern dbg_sym_t *types_tree_head; ++extern dbg_sym_t *typedefs_tree_head; ++extern kltype_t *kltype_array; ++extern dbg_sym_t *dsym_types_array; ++extern dbg_sym_t *type_tree; ++extern dbg_sym_t *typedef_tree; ++ ++/* ++ * use a pointer's value as an index in the stringp_array (num_strings) and ++ * translate it to string address ++ * ++ * Return 0 for success, 1 for failure ++ */ ++static int ++index_to_char_ptr(char **ptrp) ++{ ++ long i; ++ ++ i = (long)*ptrp; ++ /* we use a value of -1 to mean this was a null pointer */ ++ if (i == -1) { ++ *ptrp = NULL; ++ return 0; ++ } ++ if (i > num_strings-1) { ++ printk("Could not translate character string index %#lx\n", i); ++ return 1; ++ } ++ *ptrp = *(stringp_array+i); ++ return 0; ++} ++ ++/* ++ * use a pointer's value as an index in the kltype_array (num_kltypes) and ++ * translate it to the kltype_t address ++ * ++ * return 0 for success, 1 for failure ++ */ ++static int ++index_to_kltype_ptr(kltype_t **ptrp) ++{ ++ long i; ++ ++ i = (long)*ptrp; ++ /* we use a value of -1 to mean this was a null pointer */ ++ if (i == -1) { ++ *ptrp = NULL; ++ return 0; ++ } ++ if (i > num_kltypes-1) { ++ printk("Could not translate kl_type string index %#lx\n", i); ++ return 1; ++ } ++ *ptrp = kltype_array+i; ++ return 0; ++} ++ ++/* ++ * look up a pointer in the dsym_types_array (num_dsyms) and ++ * translate it to the index in the array ++ * ++ * return 0 for success, 1 for failure ++ */ ++static int ++index_to_dbg_ptr(dbg_sym_t **ptrp) ++{ ++ long i; ++ ++ i = (long)*ptrp; ++ /* we use a value of -1 to mean this was a null pointer */ ++ if (i == -1) { ++ *ptrp = NULL; ++ return 0; ++ } ++ if (i > num_dsyms-1) { ++ printk("Could not translate dbg_sym_t index %#lx\n", i); ++ return 1; ++ } ++ *ptrp = dsym_types_array+i; ++ return 0; ++} ++ ++ ++/* ++ * Work on the image of the file built by lcrash. ++ * Unpack the strings, and resolve the pointers in the arrays of kltype_t's ++ * and dbg_sym_t's to pointers. ++ * ++ * see lcrash's lib/libklib/kl_debug.c, which generates this file ++ * ++ * Return the pointers to the heads of the two binary trees by means of ++ * pointer arguments. ++ * ++ * Return 0 for sucess, 1 for any error. ++ */ ++static int ++trans_file_image(void *file_storage, long file_size, dbg_sym_t **type_treepp, ++ dbg_sym_t **typedef_treepp) ++{ ++ int len; ++ long i, section_size, *lp, element_size; ++ long head_types_tree, head_typedefs_tree; ++ char *ptr, *stringsection, *kltypesection, *dbgsection; ++ void *kltypestorage, *dbgstorage; ++ kltype_t *klp; ++ dbg_sym_t *dbgp; ++ ++ /* 1) the strings */ ++ lp = (long *)file_storage; ++ stringsection = (char *)lp; ++ section_size = *lp++; ++ num_strings = *lp++; ++ lp++; /* element size does not apply the strings section */ ++ ++ stringstorage_size = section_size - (3*sizeof(long)); ++ stringstorage = (char *)lp; ++ ++ stringp_array = (char **)vmalloc(num_strings * sizeof(char *)); ++ if (! stringp_array) { ++ printk("vmalloc of %ld string pointers failed\n", num_strings); ++ return 1; ++ } ++ ptr = stringstorage; ++ for (i=0; ikl_name)) ++ goto bad; ++ if (index_to_char_ptr(&klp->kl_typestr)) ++ goto bad; ++ if (index_to_kltype_ptr(&klp->kl_member)) ++ goto bad; ++ if (index_to_kltype_ptr(&klp->kl_next)) ++ goto bad; ++ if (index_to_kltype_ptr(&klp->kl_realtype)) ++ goto bad; ++ if (index_to_kltype_ptr(&klp->kl_indextype)) ++ goto bad; ++ if (index_to_kltype_ptr(&klp->kl_elementtype)) ++ goto bad; ++ if (index_to_dbg_ptr((dbg_sym_t **)&klp->kl_ptr)) ++ goto bad; ++ } ++ ++ /* translate the indices in our our array of dbg_sym_t's to pointers */ ++ /* (see write_dbgtype() for the fields that can be translated) */ ++ dbgp = dsym_types_array; ++ for (i=0; isym_bt.bt_key)) ++ goto bad; ++ if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_left)) ++ goto bad; ++ if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_right)) ++ goto bad; ++ if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_parent)) ++ goto bad; ++ if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_next)) ++ goto bad; ++ if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_link)) ++ goto bad; ++ if (index_to_kltype_ptr(&dbgp->sym_kltype)) ++ goto bad; ++ } ++ ++ vfree(stringp_array); ++ return 0; ++bad: ++ printk("trans_file_image() returning an error\n"); ++ vfree(stringp_array); ++ return 1; ++} ++ ++/* there is /proc interface to this string */ ++extern char kdb_debug_info_filename[]; ++/* ++ * This is the module initialization function. ++ */ ++static int __init ++kdbm_debuginfo_init(void) ++{ ++ int len; ++ long ret, file_size; ++ ssize_t sizeread; ++ mm_segment_t fs; ++ struct file *file; ++ loff_t inode_size, pos; ++ ++ len = strlen(kdb_debug_info_filename); ++ if (!len) { ++ printk("kdb: no file name in /proc/kdb/debug_info_name\n"); ++ return -ENODEV; ++ } ++ ++ fs = get_fs(); /* save previous value of address limits */ ++ set_fs (get_ds()); /* use kernel limit */ ++ ++ file = filp_open(kdb_debug_info_filename, O_RDONLY, 0); ++ if (IS_ERR(file)) { ++ set_fs(fs); ++ printk ( ++ "kdb: open of %s (from /proc/kdb/debug_info_name) failed\n", ++ kdb_debug_info_filename); ++ return -ENODEV; ++ } ++ if (!file->f_op || (!file->f_op->read && !file->f_op->llseek)) { ++ printk ("file has no operation for read or seek\n"); ++ set_fs(fs); ++ return -ENODEV; ++ } ++ inode_size = file->f_dentry->d_inode->i_size; ++ ++ /* ++ * File has a header word on it that contains the size of the ++ * file. We don't need it, but can use it as a sanity check. ++ */ ++ pos = 0; ++ sizeread = file->f_op->read(file, (char *)&file_size, ++ sizeof(file_size), &pos); ++ if (sizeread != sizeof(file_size)) { ++ printk("could not read %d bytes from %s\n", ++ (int)sizeof(file_size), kdb_debug_info_filename); ++ ret = filp_close(file, NULL); ++ set_fs(fs); ++ return -ENODEV; ++ } ++ if (inode_size != file_size) { ++ printk("file says %ld, inode says %lld\n", ++ file_size, inode_size); ++ ret = filp_close(file, NULL); ++ set_fs(fs); ++ return -ENODEV; ++ } ++ ++ /* space for the rest of the file: */ ++ file_size -= sizeof(long); ++ filestorage = (void *)vmalloc(file_size); ++ ++ pos = sizeof(file_size); /* position after the header word */ ++ sizeread = file->f_op->read(file, (char *)filestorage, ++ file_size, &pos); ++ if (sizeread != file_size) { ++ printk("could not read %ld bytes from %s\n", ++ file_size, kdb_debug_info_filename); ++ ret = filp_close(file, NULL); ++ set_fs(fs); ++ vfree (filestorage); ++ return -ENODEV; ++ } ++ ++ ret = filp_close(file, NULL); ++ set_fs(fs); /* restore address limits before returning to user space */ ++ ++ if (trans_file_image(filestorage, file_size, &types_tree_head, ++ &typedefs_tree_head)){ ++ vfree (filestorage); ++ return -ENODEV; ++ } ++ printk("kdbm_debuginfo loaded %s\n", kdb_debug_info_filename); ++ /* set the lcrash code's binary tree head nodes */ ++ type_tree = types_tree_head; ++ typedef_tree = typedefs_tree_head; ++ ++ have_debug_file = 1; ++ ++ return 0; ++} ++ ++/* ++ * This is the module exit function. ++ */ ++static void __exit ++kdbm_debuginfo_exit(void) ++{ ++ printk("kdbm_debuginfo unloaded %s\n", kdb_debug_info_filename); ++ vfree (filestorage); ++ have_debug_file = 0; ++ return; ++} ++ ++module_init(kdbm_debuginfo_init); ++module_exit(kdbm_debuginfo_exit); +--- /dev/null ++++ b/kdb/modules/kdbm_pg.c +@@ -0,0 +1,684 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("SGI"); ++MODULE_DESCRIPTION("Debug page information"); ++MODULE_LICENSE("GPL"); ++ ++/* Standard Linux page stuff */ ++ ++#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) ++/* From include/linux/page-flags.h */ ++static char *pg_flag_vals[] = { ++ "PG_locked", "PG_error", "PG_referenced", "PG_uptodate", ++ "PG_dirty", "PG_lru", "PG_active", "PG_slab", ++ "PG_owner_priv_1", "PG_arch_1", "PG_reserved", "PG_private", ++ "PG_writeback", ++#ifdef CONFIG_PAGEFLAGS_EXTENDED ++ "PG_head", "PG_tail", ++#else ++ "PG_compound", ++#endif ++ "PG_swapcache", "PG_mappedtodisk", "PG_reclaim", "PG_buddy", ++#ifdef CONFIG_IA64_UNCACHED_ALLOCATOR ++ "PG_uncached", ++#endif ++ NULL }; ++#endif ++ ++/* From include/linux/buffer_head.h */ ++static char *bh_state_vals[] = { ++ "Uptodate", "Dirty", "Lock", "Req", ++ "Uptodate_Lock", "Mapped", "New", "Async_read", ++ "Async_write", "Delay", "Boundary", "Write_EIO", ++ "Ordered", "Eopnotsupp", "Unwritten", "PriavateStart", ++ NULL }; ++ ++/* From include/linux/bio.h */ ++static char *bio_flag_vals[] = { ++ "Uptodate", "RW_block", "EOF", "Seg_valid", ++ "Cloned", "Bounced", "User_mapped", "Eopnotsupp", ++ NULL }; ++ ++/* From include/linux/fs.h */ ++static char *inode_flag_vals[] = { ++ "I_DIRTY_SYNC", "I_DIRTY_DATASYNC", "I_DIRTY_PAGES", "I_NEW", ++ "I_WILL_FREE", "I_FREEING", "I_CLEAR", "I_LOCK", ++ "I_SYNC", NULL }; ++ ++static char *map_flags(unsigned long flags, char *mapping[]) ++{ ++ static char buffer[256]; ++ int index; ++ int offset = 12; ++ ++ buffer[0] = '\0'; ++ ++ for (index = 0; flags && mapping[index]; flags >>= 1, index++) { ++ if (flags & 1) { ++ if ((offset + strlen(mapping[index]) + 1) >= 80) { ++ strcat(buffer, "\n "); ++ offset = 12; ++ } else if (offset > 12) { ++ strcat(buffer, " "); ++ offset++; ++ } ++ strcat(buffer, mapping[index]); ++ offset += strlen(mapping[index]); ++ } ++ } ++ ++ return (buffer); ++} ++ ++static int ++kdbm_buffers(int argc, const char **argv) ++{ ++ struct buffer_head bh; ++ unsigned long addr; ++ long offset = 0; ++ int nextarg; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || ++ (diag = kdb_getarea(bh, addr))) ++ return(diag); ++ ++ kdb_printf("buffer_head at 0x%lx\n", addr); ++ kdb_printf(" bno %llu size %llu dev 0x%x\n", ++ (unsigned long long)bh.b_blocknr, ++ (unsigned long long)bh.b_size, ++ bh.b_bdev ? bh.b_bdev->bd_dev : 0); ++ kdb_printf(" count %d state 0x%lx [%s]\n", ++ bh.b_count.counter, bh.b_state, ++ map_flags(bh.b_state, bh_state_vals)); ++ kdb_printf(" b_data 0x%p\n", ++ bh.b_data); ++ kdb_printf(" b_page 0x%p b_this_page 0x%p b_private 0x%p\n", ++ bh.b_page, bh.b_this_page, bh.b_private); ++ kdb_printf(" b_end_io "); ++ if (bh.b_end_io) ++ kdb_symbol_print(kdba_funcptr_value(bh.b_end_io), NULL, KDB_SP_VALUE); ++ else ++ kdb_printf("(NULL)"); ++ kdb_printf("\n"); ++ ++ return 0; ++} ++ ++static int ++print_biovec(struct bio_vec *vec, int vcount) ++{ ++ struct bio_vec bvec; ++ unsigned long addr; ++ int diag; ++ int i; ++ ++ if (vcount < 1 || vcount > BIO_MAX_PAGES) { ++ kdb_printf(" [skipped iovecs, vcnt is %d]\n", vcount); ++ return 0; ++ } ++ ++ addr = (unsigned long)vec; ++ for (i = 0; i < vcount; i++) { ++ if ((diag = kdb_getarea(bvec, addr))) ++ return(diag); ++ addr += sizeof(bvec); ++ kdb_printf(" [%d] page 0x%p length=%u offset=%u\n", ++ i, bvec.bv_page, bvec.bv_len, bvec.bv_offset); ++ } ++ return 0; ++} ++ ++static int ++kdbm_bio(int argc, const char **argv) ++{ ++ struct bio bio; ++ unsigned long addr; ++ long offset = 0; ++ int nextarg; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || ++ (diag = kdb_getarea(bio, addr))) ++ return(diag); ++ ++ kdb_printf("bio at 0x%lx\n", addr); ++ kdb_printf(" bno %llu next 0x%p dev 0x%x\n", ++ (unsigned long long)bio.bi_sector, ++ bio.bi_next, bio.bi_bdev ? bio.bi_bdev->bd_dev : 0); ++ kdb_printf(" vcnt %u vec 0x%p rw 0x%lx flags 0x%lx [%s]\n", ++ bio.bi_vcnt, bio.bi_io_vec, bio.bi_rw, bio.bi_flags, ++ map_flags(bio.bi_flags, bio_flag_vals)); ++ print_biovec(bio.bi_io_vec, bio.bi_vcnt); ++ kdb_printf(" count %d private 0x%p\n", ++ atomic_read(&bio.bi_cnt), bio.bi_private); ++ kdb_printf(" bi_end_io "); ++ if (bio.bi_end_io) ++ kdb_symbol_print(kdba_funcptr_value(bio.bi_end_io), NULL, KDB_SP_VALUE); ++ else ++ kdb_printf("(NULL)"); ++ kdb_printf("\n"); ++ ++ return 0; ++} ++ ++#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) ++static char *page_flags(unsigned long flags) ++{ ++ return(map_flags(flags, pg_flag_vals)); ++} ++ ++static int ++kdbm_page(int argc, const char **argv) ++{ ++ struct page page; ++ unsigned long addr; ++ long offset = 0; ++ int nextarg; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ ++#ifdef __ia64__ ++ if (rgn_index(addr) == 0) ++ addr = (unsigned long) &mem_map[addr]; /* assume region 0 is a page index, not an address */ ++#else ++ if (addr < PAGE_OFFSET) ++ addr = (unsigned long) &mem_map[addr]; ++#endif ++ ++ if ((diag = kdb_getarea(page, addr))) ++ return(diag); ++ ++ kdb_printf("struct page at 0x%lx\n", addr); ++ kdb_printf(" addr space 0x%p index %lu (offset 0x%llx)\n", ++ page.mapping, page.index, ++ (unsigned long long)page.index << PAGE_CACHE_SHIFT); ++ kdb_printf(" count %d flags %s\n", ++ page._count.counter, page_flags(page.flags)); ++ kdb_printf(" virtual 0x%p\n", page_address((struct page *)addr)); ++ if (page_has_buffers(&page)) ++ kdb_printf(" buffers 0x%p\n", page_buffers(&page)); ++ else ++ kdb_printf(" private 0x%lx\n", page_private(&page)); ++ ++ return 0; ++} ++#endif /* !CONFIG_DISCONTIGMEM && !NUMA */ ++ ++static unsigned long ++print_request(unsigned long addr) ++{ ++ struct request rq; ++ ++ if (kdb_getarea(rq, addr)) ++ return(0); ++ ++ kdb_printf("struct request at 0x%lx\n", addr); ++ kdb_printf(" errors %d sector %llu nr_sectors %llu\n", ++ rq.errors, ++ (unsigned long long)blk_rq_pos(&rq), ++ (unsigned long long)blk_rq_sectors(&rq)); ++ ++ return (unsigned long) rq.queuelist.next; ++} ++ ++static int ++kdbm_request(int argc, const char **argv) ++{ ++ long offset = 0; ++ unsigned long addr; ++ int nextarg; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ ++ print_request(addr); ++ return 0; ++} ++ ++ ++static int ++kdbm_rqueue(int argc, const char **argv) ++{ ++ struct request_queue rq; ++ unsigned long addr, head_addr, next; ++ long offset = 0; ++ int nextarg; ++ int i, diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || ++ (diag = kdb_getarea(rq, addr))) ++ return(diag); ++ ++ kdb_printf("struct request_queue at 0x%lx\n", addr); ++ i = 0; ++ next = (unsigned long)rq.queue_head.next; ++ head_addr = addr + offsetof(struct request_queue, queue_head); ++ kdb_printf(" request queue: %s\n", next == head_addr ? ++ "empty" : ""); ++ while (next != head_addr) { ++ i++; ++ next = print_request(next); ++ } ++ ++ if (i) ++ kdb_printf("%d requests found\n", i); ++ ++ return 0; ++} ++ ++ ++static void ++do_buffer(unsigned long addr) ++{ ++ struct buffer_head bh; ++ ++ if (kdb_getarea(bh, addr)) ++ return; ++ ++ kdb_printf("\tbh 0x%lx bno %8llu [%s]\n", addr, ++ (unsigned long long)bh.b_blocknr, ++ map_flags(bh.b_state, bh_state_vals)); ++} ++ ++static void ++kdbm_show_page(struct page *page, int first) ++{ ++ if (first) ++ kdb_printf("page_struct index cnt zone nid flags\n"); ++ kdb_printf("%p%s %6lu %5d %3d %3d 0x%lx", ++ page_address(page), sizeof(void *) == 4 ? " " : "", ++ page->index, atomic_read(&(page->_count)), ++ page_zonenum(page), page_to_nid(page), ++ page->flags & (~0UL >> ZONES_SHIFT)); ++#define kdb_page_flags(page, type) if (Page ## type(page)) kdb_printf(" " #type); ++ kdb_page_flags(page, Locked); ++ kdb_page_flags(page, Error); ++ kdb_page_flags(page, Referenced); ++ kdb_page_flags(page, Uptodate); ++ kdb_page_flags(page, Dirty); ++ kdb_page_flags(page, LRU); ++ kdb_page_flags(page, Active); ++ kdb_page_flags(page, Slab); ++ kdb_page_flags(page, Checked); ++ if (page->flags & (1UL << PG_arch_1)) ++ kdb_printf(" arch_1"); ++ kdb_page_flags(page, Reserved); ++ kdb_page_flags(page, Private); ++ kdb_page_flags(page, Writeback); ++ kdb_page_flags(page, Compound); ++ kdb_page_flags(page, SwapCache); ++ kdb_page_flags(page, MappedToDisk); ++ kdb_page_flags(page, Reclaim); ++ kdb_page_flags(page, Buddy); ++ ++ /* PageHighMem is not a flag any more, but treat it as one */ ++ kdb_page_flags(page, HighMem); ++ ++ if (page_has_buffers(page)) { ++ struct buffer_head *head, *bh; ++ kdb_printf("\n"); ++ head = bh = page_buffers(page); ++ do { ++ do_buffer((unsigned long) bh); ++ } while ((bh = bh->b_this_page) != head); ++ } else if (page_private(page)) { ++ kdb_printf(" private= 0x%lx", page_private(page)); ++ } ++ /* Cannot use page_mapping(page) here, it needs swapper_space which is ++ * not exported. ++ */ ++ if (page->mapping) ++ kdb_printf(" mapping= %p", page->mapping); ++ kdb_printf("\n"); ++#undef kdb_page_flags ++} ++ ++static int ++kdbm_inode_pages(int argc, const char **argv) ++{ ++ struct inode *inode = NULL; ++ struct address_space *ap = NULL; ++ unsigned long addr, addr1 = 0; ++ long offset = 0; ++ int nextarg; ++ int diag; ++ pgoff_t next = 0; ++ struct page *page; ++ int first; ++ ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ goto out; ++ ++ if (argc == 2) { ++ nextarg = 2; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr1, ++ &offset, NULL); ++ if (diag) ++ goto out; ++ kdb_printf("Looking for page index 0x%lx ... \n", addr1); ++ next = addr1; ++ } ++ ++ if (!(inode = kmalloc(sizeof(*inode), GFP_ATOMIC))) { ++ kdb_printf("kdbm_inode_pages: cannot kmalloc inode\n"); ++ goto out; ++ } ++ if (!(ap = kmalloc(sizeof(*ap), GFP_ATOMIC))) { ++ kdb_printf("kdbm_inode_pages: cannot kmalloc ap\n"); ++ goto out; ++ } ++ if ((diag = kdb_getarea(*inode, addr))) ++ goto out; ++ if (!inode->i_mapping) { ++ kdb_printf("inode has no mapping\n"); ++ goto out; ++ } ++ if ((diag = kdb_getarea(*ap, (unsigned long) inode->i_mapping))) ++ goto out; ++ ++ /* Run the pages in the radix tree, printing the state of each page */ ++ first = 1; ++ while (radix_tree_gang_lookup(&ap->page_tree, (void **)&page, next, 1)) { ++ kdbm_show_page(page, first); ++ if (addr1) ++ break; ++ first = 0; ++ next = page->index + 1; ++ } ++ ++out: ++ if (inode) ++ kfree(inode); ++ if (ap) ++ kfree(ap); ++ return diag; ++} ++ ++static int ++kdbm_inode(int argc, const char **argv) ++{ ++ struct inode *inode = NULL; ++ unsigned long addr; ++ unsigned char *iaddr; ++ long offset = 0; ++ int nextarg; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) ++ goto out; ++ if (!(inode = kmalloc(sizeof(*inode), GFP_ATOMIC))) { ++ kdb_printf("kdbm_inode: cannot kmalloc inode\n"); ++ goto out; ++ } ++ if ((diag = kdb_getarea(*inode, addr))) ++ goto out; ++ ++ kdb_printf("struct inode at 0x%lx\n", addr); ++ ++ kdb_printf(" i_ino = %lu i_count = %u i_size %Ld\n", ++ inode->i_ino, atomic_read(&inode->i_count), ++ inode->i_size); ++ ++ kdb_printf(" i_mode = 0%o i_nlink = %d i_rdev = 0x%x\n", ++ inode->i_mode, inode->i_nlink, ++ inode->i_rdev); ++ ++ kdb_printf(" i_hash.nxt = 0x%p i_hash.pprev = 0x%p\n", ++ inode->i_hash.next, ++ inode->i_hash.pprev); ++ ++ kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n", ++ list_entry(inode->i_list.next, struct inode, i_list), ++ list_entry(inode->i_list.prev, struct inode, i_list)); ++ ++ kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n", ++ list_entry(inode->i_dentry.next, struct dentry, d_alias), ++ list_entry(inode->i_dentry.prev, struct dentry, d_alias)); ++ ++ kdb_printf(" i_sb = 0x%p i_op = 0x%p i_data = 0x%lx nrpages = %lu\n", ++ inode->i_sb, inode->i_op, ++ addr + offsetof(struct inode, i_data), ++ inode->i_data.nrpages); ++ kdb_printf(" i_fop= 0x%p i_flock = 0x%p i_mapping = 0x%p\n", ++ inode->i_fop, inode->i_flock, inode->i_mapping); ++ ++ kdb_printf(" i_flags 0x%x i_state 0x%lx [%s]", ++ inode->i_flags, inode->i_state, ++ map_flags(inode->i_state, inode_flag_vals)); ++ ++ iaddr = (char *)addr; ++ iaddr += offsetof(struct inode, i_private); ++ ++ kdb_printf(" fs specific info @ 0x%p\n", iaddr); ++out: ++ if (inode) ++ kfree(inode); ++ return diag; ++} ++ ++static int ++kdbm_sb(int argc, const char **argv) ++{ ++ struct super_block *sb = NULL; ++ unsigned long addr; ++ long offset = 0; ++ int nextarg; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) ++ goto out; ++ if (!(sb = kmalloc(sizeof(*sb), GFP_ATOMIC))) { ++ kdb_printf("kdbm_sb: cannot kmalloc sb\n"); ++ goto out; ++ } ++ if ((diag = kdb_getarea(*sb, addr))) ++ goto out; ++ ++ kdb_printf("struct super_block at 0x%lx\n", addr); ++ kdb_printf(" s_dev 0x%x blocksize 0x%lx\n", sb->s_dev, sb->s_blocksize); ++ kdb_printf(" s_flags 0x%lx s_root 0x%p\n", sb->s_flags, sb->s_root); ++ kdb_printf(" s_frozen %d s_id [%s]\n", sb->s_frozen, sb->s_id); ++out: ++ if (sb) ++ kfree(sb); ++ return diag; ++} ++ ++ ++#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) ++/* According to Steve Lord, this code is ix86 specific. Patches to extend it to ++ * other architectures will be greatefully accepted. ++ */ ++static int ++kdbm_memmap(int argc, const char **argv) ++{ ++ struct page page; ++ int i, page_count; ++ int slab_count = 0; ++ int dirty_count = 0; ++ int locked_count = 0; ++ int page_counts[10]; /* [8] = large counts, [9] = -1 counts */ ++ int buffered_count = 0; ++#ifdef buffer_delay ++ int delay_count = 0; ++#endif ++ int diag; ++ unsigned long addr; ++#ifdef CONFIG_DISCONTIGMEM ++ int node_id = -1, found_node = 0; ++ int tot_page_count = 0; ++ unsigned long unode_id; ++ pg_data_t *pgdat; ++ ++ if (argc == 1) { /* node_id was specified */ ++ diag = kdbgetularg(argv[argc], &unode_id); ++ if (diag) ++ return diag; ++ node_id = (int)unode_id; ++ } ++ else if (argc) ++ return KDB_ARGCOUNT; ++ ++ tot_page_count = 0; ++ memset(page_counts, 0, sizeof(page_counts)); ++ ++ for_each_online_pgdat(pgdat) { ++ if ((node_id != -1) && (pgdat->node_id != node_id)) ++ continue; ++ found_node = 1; ++ addr = (unsigned long)pgdat->node_mem_map; ++ page_count = pgdat->node_spanned_pages; ++ tot_page_count += page_count; ++#else ++ addr = (unsigned long)mem_map; ++ page_count = max_mapnr; ++ memset(page_counts, 0, sizeof(page_counts)); ++#endif ++ for (i = 0; i < page_count; i++) { ++ if ((diag = kdb_getarea(page, addr))) ++ return(diag); ++ addr += sizeof(page); ++ ++ if (PageSlab(&page)) ++ slab_count++; ++ if (PageDirty(&page)) ++ dirty_count++; ++ if (PageLocked(&page)) ++ locked_count++; ++ if (page._count.counter == -1) ++ page_counts[9]++; ++ else if (page._count.counter < 8) ++ page_counts[page._count.counter]++; ++ else ++ page_counts[8]++; ++ if (page_has_buffers(&page)) { ++ buffered_count++; ++#ifdef buffer_delay ++ if (buffer_delay(page.buffers)) ++ delay_count++; ++#endif ++ } ++ } ++#ifdef CONFIG_DISCONTIGMEM ++ } ++ page_count = tot_page_count; ++ if (node_id != -1) { ++ if (!found_node) { ++ kdb_printf("Node %d does not exist.\n", node_id); ++ return 0; ++ } ++ kdb_printf("Node %d pages:\n", node_id); ++ } ++#endif ++ kdb_printf(" Total pages: %6d\n", page_count); ++ kdb_printf(" Slab pages: %6d\n", slab_count); ++ kdb_printf(" Dirty pages: %6d\n", dirty_count); ++ kdb_printf(" Locked pages: %6d\n", locked_count); ++ kdb_printf(" Buffer pages: %6d\n", buffered_count); ++#ifdef buffer_delay ++ kdb_printf(" Delalloc pages: %6d\n", delay_count); ++#endif ++ kdb_printf(" -1 page count: %6d\n", page_counts[9]); ++ for (i = 0; i < 8; i++) { ++ kdb_printf(" %d page count: %6d\n", ++ i, page_counts[i]); ++ } ++ kdb_printf(" high page count: %6d\n", page_counts[8]); ++ return 0; ++} ++#endif /* !CONFIG_DISCONTIGMEM && !NUMA */ ++ ++static int __init kdbm_pg_init(void) ++{ ++#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) ++ kdb_register("page", kdbm_page, "", "Display page", 0); ++#endif ++ kdb_register("inode", kdbm_inode, "", "Display inode", 0); ++ kdb_register("sb", kdbm_sb, "", "Display super_block", 0); ++ kdb_register("bh", kdbm_buffers, "", "Display buffer", 0); ++ kdb_register("bio", kdbm_bio, "", "Display bio", 0); ++ kdb_register("inode_pages", kdbm_inode_pages, "", "Display pages in an inode", 0); ++ kdb_register("req", kdbm_request, "", "dump request struct", 0); ++ kdb_register("rqueue", kdbm_rqueue, "", "dump request queue", 0); ++#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) ++ kdb_register("memmap", kdbm_memmap, "", "page table summary", 0); ++#endif ++ ++ return 0; ++} ++ ++ ++static void __exit kdbm_pg_exit(void) ++{ ++#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) ++ kdb_unregister("page"); ++#endif ++ kdb_unregister("inode"); ++ kdb_unregister("sb"); ++ kdb_unregister("bh"); ++ kdb_unregister("bio"); ++ kdb_unregister("inode_pages"); ++ kdb_unregister("req"); ++ kdb_unregister("rqueue"); ++#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) ++ kdb_unregister("memmap"); ++#endif ++} ++ ++module_init(kdbm_pg_init) ++module_exit(kdbm_pg_exit) +--- /dev/null ++++ b/kdb/modules/kdbm_sched.c +@@ -0,0 +1,57 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("SGI"); ++MODULE_DESCRIPTION("Debug scheduler information"); ++MODULE_LICENSE("GPL"); ++ ++static int ++kdbm_runqueues(int argc, const char **argv) ++{ ++ unsigned long cpu; ++ int ret = 0; ++ ++ if (argc == 1) { ++ ret = kdbgetularg((char *)argv[1], &cpu); ++ if (!ret) { ++ if (!cpu_online(cpu)) { ++ kdb_printf("Invalid cpu number\n"); ++ } else ++ kdb_runqueue(cpu, kdb_printf); ++ } ++ } else if (argc == 0) { ++ for_each_online_cpu(cpu) ++ kdb_runqueue(cpu, kdb_printf); ++ } else { ++ /* More than one arg */ ++ kdb_printf("Specify one cpu number\n"); ++ } ++ return ret; ++} ++ ++static int __init kdbm_sched_init(void) ++{ ++ kdb_register("rq", kdbm_runqueues, "", "Display runqueue for ", 0); ++ kdb_register("rqa", kdbm_runqueues, "", "Display all runqueues", 0); ++ return 0; ++} ++ ++static void __exit kdbm_sched_exit(void) ++{ ++ kdb_unregister("rq"); ++ kdb_unregister("rqa"); ++} ++ ++module_init(kdbm_sched_init) ++module_exit(kdbm_sched_exit) +--- /dev/null ++++ b/kdb/modules/kdbm_task.c +@@ -0,0 +1,196 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("SGI"); ++MODULE_DESCRIPTION("Debug struct task and sigset information"); ++MODULE_LICENSE("GPL"); ++ ++static char * ++kdb_cpus_allowed_string(struct task_struct *tp) ++{ ++ static char maskbuf[NR_CPUS * 8]; ++ if (cpus_equal(tp->cpus_allowed, cpu_online_map)) ++ strcpy(maskbuf, "ALL"); ++ else if (cpus_empty(tp->cpus_allowed)) ++ strcpy(maskbuf, "NONE"); ++ else if (cpus_weight(tp->cpus_allowed) == 1) ++ snprintf(maskbuf, sizeof(maskbuf), "ONLY(%d)", first_cpu(tp->cpus_allowed)); ++ else ++ cpulist_scnprintf(maskbuf, sizeof(maskbuf), &tp->cpus_allowed); ++ return maskbuf; ++} ++ ++static int ++kdbm_task(int argc, const char **argv) ++{ ++ unsigned long addr; ++ long offset=0; ++ int nextarg; ++ int e = 0; ++ struct task_struct *tp = NULL, *tp1; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((e = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) != 0) ++ return(e); ++ ++ if (!(tp = kmalloc(sizeof(*tp), GFP_ATOMIC))) { ++ kdb_printf("%s: cannot kmalloc tp\n", __FUNCTION__); ++ goto out; ++ } ++ if ((e = kdb_getarea(*tp, addr))) { ++ kdb_printf("%s: invalid task address\n", __FUNCTION__); ++ goto out; ++ } ++ ++ tp1 = (struct task_struct *)addr; ++ kdb_printf( ++ "struct task at 0x%lx, pid=%d flags=0x%x state=%ld comm=\"%s\"\n", ++ addr, tp->pid, tp->flags, tp->state, tp->comm); ++ ++ kdb_printf(" cpu=%d policy=%u ", kdb_process_cpu(tp), tp->policy); ++ kdb_printf( ++ "prio=%d static_prio=%d cpus_allowed=", ++ tp->prio, tp->static_prio); ++ { ++ /* The cpus allowed string may be longer than kdb_printf() can ++ * handle. Print it in chunks. ++ */ ++ char c, *p; ++ p = kdb_cpus_allowed_string(tp); ++ while (1) { ++ if (strlen(p) < 100) { ++ kdb_printf("%s", p); ++ break; ++ } ++ c = p[100]; ++ p[100] = '\0'; ++ kdb_printf("%s", p); ++ p[100] = c; ++ p += 100; ++ } ++ } ++ kdb_printf(" &thread=0x%p\n", &tp1->thread); ++ ++ kdb_printf(" need_resched=%d ", ++ test_tsk_thread_flag(tp, TIF_NEED_RESCHED)); ++ kdb_printf( ++ "time_slice=%u", ++ tp->rt.time_slice); ++ kdb_printf(" lock_depth=%d\n", tp->lock_depth); ++ ++ kdb_printf( ++ " fs=0x%p files=0x%p mm=0x%p\n", ++ tp->fs, tp->files, tp->mm); ++ ++ if (tp->sysvsem.undo_list) ++ kdb_printf( ++ " sysvsem.sem_undo refcnt %d list_proc=0x%p\n", ++ atomic_read(&tp->sysvsem.undo_list->refcnt), ++ &tp->sysvsem.undo_list->list_proc); ++ ++ kdb_printf( ++ " signal=0x%p &blocked=0x%p &pending=0x%p\n", ++ tp->signal, &tp1->blocked, &tp1->pending); ++ ++ kdb_printf( ++ " utime=%ld stime=%ld cutime=%ld cstime=%ld\n", ++ tp->utime, tp->stime, ++ tp->signal ? tp->signal->cutime : 0L, ++ tp->signal ? tp->signal->cstime : 0L); ++ ++ kdb_printf(" thread_info=0x%p\n", task_thread_info(tp)); ++ kdb_printf(" ti flags=0x%lx\n", (unsigned long)task_thread_info(tp)->flags); ++ ++#ifdef CONFIG_NUMA ++ kdb_printf( ++ " mempolicy=0x%p il_next=%d\n", ++ tp->mempolicy, tp->il_next); ++#endif ++ ++out: ++ if (tp) ++ kfree(tp); ++ return e; ++} ++ ++static int ++kdbm_sigset(int argc, const char **argv) ++{ ++ sigset_t *sp = NULL; ++ unsigned long addr; ++ long offset=0; ++ int nextarg; ++ int e = 0; ++ int i; ++ char fmt[32]; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++#ifndef _NSIG_WORDS ++ kdb_printf("unavailable on this platform, _NSIG_WORDS not defined.\n"); ++#else ++ nextarg = 1; ++ if ((e = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) != 0) ++ return(e); ++ ++ if (!(sp = kmalloc(sizeof(*sp), GFP_ATOMIC))) { ++ kdb_printf("%s: cannot kmalloc sp\n", __FUNCTION__); ++ goto out; ++ } ++ if ((e = kdb_getarea(*sp, addr))) { ++ kdb_printf("%s: invalid sigset address\n", __FUNCTION__); ++ goto out; ++ } ++ ++ sprintf(fmt, "[%%d]=0x%%0%dlx ", (int)sizeof(sp->sig[0])*2); ++ kdb_printf("sigset at 0x%p : ", sp); ++ for (i=_NSIG_WORDS-1; i >= 0; i--) { ++ if (i == 0 || sp->sig[i]) { ++ kdb_printf(fmt, i, sp->sig[i]); ++ } ++ } ++ kdb_printf("\n"); ++#endif /* _NSIG_WORDS */ ++ ++out: ++ if (sp) ++ kfree(sp); ++ return e; ++} ++ ++static int __init kdbm_task_init(void) ++{ ++ kdb_register("task", kdbm_task, "", "Display task_struct", 0); ++ kdb_register("sigset", kdbm_sigset, "", "Display sigset_t", 0); ++ ++ return 0; ++} ++ ++static void __exit kdbm_task_exit(void) ++{ ++ kdb_unregister("task"); ++ kdb_unregister("sigset"); ++} ++ ++module_init(kdbm_task_init) ++module_exit(kdbm_task_exit) +--- /dev/null ++++ b/kdb/modules/kdbm_vm.c +@@ -0,0 +1,1041 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++MODULE_AUTHOR("SGI"); ++MODULE_DESCRIPTION("Debug VM information"); ++MODULE_LICENSE("GPL"); ++ ++struct __vmflags { ++ unsigned long mask; ++ char *name; ++}; ++ ++static struct __vmflags vmflags[] = { ++ { VM_READ, "VM_READ " }, ++ { VM_WRITE, "VM_WRITE " }, ++ { VM_EXEC, "VM_EXEC " }, ++ { VM_SHARED, "VM_SHARED " }, ++ { VM_MAYREAD, "VM_MAYREAD " }, ++ { VM_MAYWRITE, "VM_MAYWRITE " }, ++ { VM_MAYEXEC, "VM_MAYEXEC " }, ++ { VM_MAYSHARE, "VM_MAYSHARE " }, ++ { VM_GROWSDOWN, "VM_GROWSDOWN " }, ++ { VM_GROWSUP, "VM_GROWSUP " }, ++ { VM_PFNMAP, "VM_PFNMAP " }, ++ { VM_DENYWRITE, "VM_DENYWRITE " }, ++ { VM_EXECUTABLE, "VM_EXECUTABLE " }, ++ { VM_LOCKED, "VM_LOCKED " }, ++ { VM_IO, "VM_IO " }, ++ { VM_SEQ_READ, "VM_SEQ_READ " }, ++ { VM_RAND_READ, "VM_RAND_READ " }, ++ { VM_DONTCOPY, "VM_DONTCOPY " }, ++ { VM_DONTEXPAND, "VM_DONTEXPAND " }, ++ { VM_RESERVED, "VM_RESERVED " }, ++ { VM_ACCOUNT, "VM_ACCOUNT " }, ++ { VM_HUGETLB, "VM_HUGETLB " }, ++ { VM_NONLINEAR, "VM_NONLINEAR " }, ++ { VM_MAPPED_COPY, "VM_MAPPED_COPY " }, ++ { VM_INSERTPAGE, "VM_INSERTPAGE " }, ++ { 0, "" } ++}; ++ ++static int ++kdbm_print_vm(struct vm_area_struct *vp, unsigned long addr, int verbose_flg) ++{ ++ struct __vmflags *tp; ++ ++ kdb_printf("struct vm_area_struct at 0x%lx for %d bytes\n", ++ addr, (int) sizeof (struct vm_area_struct)); ++ ++ kdb_printf("vm_start = 0x%p vm_end = 0x%p\n", (void *) vp->vm_start, ++ (void *) vp->vm_end); ++ kdb_printf("vm_page_prot = 0x%llx\n", ++ (unsigned long long)pgprot_val(vp->vm_page_prot)); ++ ++ kdb_printf("vm_flags: "); ++ for (tp = vmflags; tp->mask; tp++) { ++ if (vp->vm_flags & tp->mask) { ++ kdb_printf(" %s", tp->name); ++ } ++ } ++ kdb_printf("\n"); ++ ++ if (!verbose_flg) ++ return 0; ++ ++ kdb_printf("vm_mm = 0x%p\n", (void *) vp->vm_mm); ++ kdb_printf("vm_next = 0x%p\n", (void *) vp->vm_next); ++ kdb_printf("shared.vm_set.list.next = 0x%p\n", (void *) vp->shared.vm_set.list.next); ++ kdb_printf("shared.vm_set.list.prev = 0x%p\n", (void *) vp->shared.vm_set.list.prev); ++ kdb_printf("shared.vm_set.parent = 0x%p\n", (void *) vp->shared.vm_set.parent); ++ kdb_printf("shared.vm_set.head = 0x%p\n", (void *) vp->shared.vm_set.head); ++ kdb_printf("anon_vma_node.next = 0x%p\n", (void *) vp->anon_vma_node.next); ++ kdb_printf("anon_vma_node.prev = 0x%p\n", (void *) vp->anon_vma_node.prev); ++ kdb_printf("vm_ops = 0x%p\n", (void *) vp->vm_ops); ++ if (vp->vm_ops != NULL) { ++ kdb_printf("vm_ops->open = 0x%p\n", vp->vm_ops->open); ++ kdb_printf("vm_ops->close = 0x%p\n", vp->vm_ops->close); ++ kdb_printf("vm_ops->fault = 0x%p\n", vp->vm_ops->fault); ++#ifdef HAVE_VMOP_MPROTECT ++ kdb_printf("vm_ops->mprotect = 0x%p\n", vp->vm_ops->mprotect); ++#endif ++#ifdef CONFIG_NUMA ++ kdb_printf("vm_ops->set_policy = 0x%p\n", vp->vm_ops->set_policy); ++ kdb_printf("vm_ops->get_policy = 0x%p\n", vp->vm_ops->get_policy); ++#endif ++ } ++ kdb_printf("vm_pgoff = 0x%lx\n", vp->vm_pgoff); ++ kdb_printf("vm_file = 0x%p\n", (void *) vp->vm_file); ++ kdb_printf("vm_private_data = 0x%p\n", vp->vm_private_data); ++#ifdef CONFIG_NUMA ++ kdb_printf("vm_policy = 0x%p\n", vp->vm_policy); ++#endif ++ ++ return 0; ++} ++ ++static int ++kdbm_print_vmp(struct vm_area_struct *vp, int verbose_flg) ++{ ++ struct __vmflags *tp; ++ ++ if (verbose_flg) { ++ kdb_printf("0x%lx: ", (unsigned long) vp); ++ } ++ ++ kdb_printf("0x%p 0x%p ", (void *) vp->vm_start, (void *) vp->vm_end); ++ ++ for (tp = vmflags; tp->mask; tp++) { ++ if (vp->vm_flags & tp->mask) { ++ kdb_printf(" %s", tp->name); ++ } ++ } ++ kdb_printf("\n"); ++ ++ return 0; ++} ++ ++ ++#ifdef CONFIG_NUMA ++#include ++ ++/* ++ * kdbm_mpol ++ * ++ * This function implements the 'mempolicy' command. ++ * Print a struct mempolicy. ++ * ++ * mempolicy
Print struct mempolicy at
++ */ ++static int ++kdbm_mpol(int argc, const char **argv) ++{ ++ unsigned long addr; ++ long offset = 0; ++ int nextarg; ++ int err = 0; ++ struct mempolicy *mp = NULL; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((err = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, ++ NULL)) != 0) ++ return(err); ++ ++ if (!(mp = kmalloc(sizeof(*mp), GFP_ATOMIC))) { ++ kdb_printf("%s: cannot kmalloc mp\n", __FUNCTION__); ++ goto out; ++ } ++ ++ if ((err = kdb_getarea(*mp, addr))) { ++ kdb_printf("%s: invalid mempolicy address\n", __FUNCTION__); ++ goto out; ++ } ++ ++ kdb_printf("struct mempolicy at 0x%p\n", (struct mempolicy *)addr); ++ kdb_printf(" refcnt %d\n", atomic_read(&mp->refcnt)); ++ ++ switch (mp->mode) { ++ case MPOL_DEFAULT: ++ kdb_printf(" mode %d (MPOL_DEFAULT)\n", mp->mode); ++ break; ++ ++ case MPOL_PREFERRED: ++ kdb_printf(" mode %d (MPOL_PREFERRED)\n", mp->mode); ++ if (mp->flags & MPOL_F_LOCAL) ++ kdb_printf(" preferred_node local\n"); ++ else ++ kdb_printf(" preferred_node %d\n", mp->v.preferred_node); ++ break; ++ ++ case MPOL_BIND: ++ case MPOL_INTERLEAVE: ++ { ++ int i, nlongs; ++ unsigned long *longp; ++ ++ kdb_printf(" mode %d (%s)\n", mp->mode, ++ mp->mode == MPOL_INTERLEAVE ++ ? "MPOL_INTERLEAVE" ++ : "MPOL_BIND"); ++ nlongs = (int)BITS_TO_LONGS(MAX_NUMNODES); ++ kdb_printf(" nodes:"); ++ longp = mp->v.nodes.bits; ++ for (i = 0; i < nlongs; i++, longp++) ++ kdb_printf(" 0x%lx ", *longp); ++ kdb_printf("\n"); ++ break; ++ } ++ ++ default: ++ kdb_printf(" mode %d (unknown)\n", mp->mode); ++ break; ++ } ++out: ++ if (mp) ++ kfree(mp); ++ return err; ++} ++ ++#endif /* CONFIG_NUMA */ ++ ++/* ++ * kdbm_pgdat ++ * ++ * This function implements the 'pgdat' command. ++ * Print a struct pglist_data (pg_dat_t). ++ * ++ * pgdat Print struct pglist_data for node . ++ * ++ * Print pglist_data for node 0 if node_id not specified, ++ * or print the one pglist_data structure if !CONFIG_NUMA. ++ */ ++static int ++kdbm_pgdat(int argc, const char **argv) ++{ ++ int err = 0, node_id = 0, i; ++ pg_data_t *pgdatp = NULL; ++ ++#ifdef CONFIG_NUMA ++ if (argc > 1) ++ return KDB_ARGCOUNT; ++ if (argc == 1) { ++ int nextarg; ++ long offset = 0; ++ unsigned long node_id_ul; ++ ++ nextarg = 1; ++ if ((err = kdbgetaddrarg(argc, argv, &nextarg, &node_id_ul, ++ &offset, NULL)) != 0) { ++ return(err); ++ } ++ node_id = (int)node_id_ul; ++ } ++#endif ++ for_each_online_pgdat(pgdatp) { ++ if (pgdatp->node_id == node_id) ++ break; ++ } ++ if (!pgdatp) { ++ kdb_printf("%s: specified node not found\n", __FUNCTION__); ++ return 0; ++ } ++ kdb_printf("struct pglist_data at 0x%p node_id = %d\n", ++ pgdatp, pgdatp->node_id); ++ ++ for (i = 0; i < MAX_ZONELISTS; i++) { ++ int zr; ++ struct zoneref *zonerefp; ++ struct zone *zonep; ++ ++ zonerefp = pgdatp->node_zonelists[i]._zonerefs; ++ kdb_printf(" _zonerefs[%d] at 0x%p\n", i, zonerefp); ++ ++ for (zr = 0; zr <= MAX_ZONES_PER_ZONELIST; zr++, zonerefp++) { ++ int z; ++ pg_data_t *tmp_pgdatp; ++ ++ zonep = zonelist_zone(zonerefp); ++ if (!zonep) ++ break; ++ ++ kdb_printf(" 0x%p", zonep); ++ ++ for_each_online_pgdat(tmp_pgdatp) { ++ for (z = 0; z < MAX_NR_ZONES; z++) { ++ if (zonep == &tmp_pgdatp->node_zones[z]) { ++ kdb_printf (" (node %d node_zones[%d])", ++ tmp_pgdatp->node_id, z); ++ break; ++ } ++ } ++ if (z != MAX_NR_ZONES) ++ break; /* found it */ ++ } ++ kdb_printf("\n"); ++ } ++ } ++ ++ kdb_printf(" nr_zones = %d", pgdatp->nr_zones); ++#ifdef CONFIG_FLAT_NODE_MEM_MAP ++ kdb_printf(" node_mem_map = 0x%p\n", pgdatp->node_mem_map); ++#endif ++ kdb_printf(" bdata = 0x%p", pgdatp->bdata); ++ kdb_printf(" node_start_pfn = 0x%lx\n", pgdatp->node_start_pfn); ++ kdb_printf(" node_present_pages = %ld (0x%lx)\n", ++ pgdatp->node_present_pages, pgdatp->node_present_pages); ++ kdb_printf(" node_spanned_pages = %ld (0x%lx)\n", ++ pgdatp->node_spanned_pages, pgdatp->node_spanned_pages); ++ kdb_printf(" kswapd = 0x%p\n", pgdatp->kswapd); ++ ++ return err; ++} ++ ++/* ++ * kdbm_vm ++ * ++ * This function implements the 'vm' command. Print a vm_area_struct. ++ * ++ * vm [-v]
Print vm_area_struct at
++ * vmp [-v] Print all vm_area_structs for ++ */ ++ ++static int ++kdbm_vm(int argc, const char **argv) ++{ ++ unsigned long addr; ++ long offset = 0; ++ int nextarg; ++ int diag; ++ int verbose_flg = 0; ++ ++ if (argc == 2) { ++ if (strcmp(argv[1], "-v") != 0) { ++ return KDB_ARGCOUNT; ++ } ++ verbose_flg = 1; ++ } else if (argc != 1) { ++ return KDB_ARGCOUNT; ++ } ++ ++ if (strcmp(argv[0], "vmp") == 0) { ++ struct task_struct *g, *tp; ++ struct vm_area_struct *vp; ++ pid_t pid; ++ ++ if ((diag = kdbgetularg(argv[argc], (unsigned long *) &pid))) ++ return diag; ++ ++ kdb_do_each_thread(g, tp) { ++ if (tp->pid == pid) { ++ if (tp->mm != NULL) { ++ if (verbose_flg) ++ kdb_printf ++ ("vm_area_struct "); ++ kdb_printf ++ ("vm_start vm_end vm_flags\n"); ++ vp = tp->mm->mmap; ++ while (vp != NULL) { ++ kdbm_print_vmp(vp, verbose_flg); ++ vp = vp->vm_next; ++ } ++ } ++ return 0; ++ } ++ } kdb_while_each_thread(g, tp); ++ ++ kdb_printf("No process with pid == %d found\n", pid); ++ ++ } else { ++ struct vm_area_struct v; ++ ++ nextarg = argc; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, ++ NULL)) ++ || (diag = kdb_getarea(v, addr))) ++ return (diag); ++ ++ kdbm_print_vm(&v, addr, verbose_flg); ++ } ++ ++ return 0; ++} ++ ++static int ++kdbm_print_pte(pte_t * pte) ++{ ++ kdb_printf("0x%lx (", (unsigned long) pte_val(*pte)); ++ ++ if (pte_present(*pte)) { ++#ifdef pte_exec ++ if (pte_exec(*pte)) ++ kdb_printf("X"); ++#endif ++ if (pte_write(*pte)) ++ kdb_printf("W"); ++#ifdef pte_read ++ if (pte_read(*pte)) ++ kdb_printf("R"); ++#endif ++ if (pte_young(*pte)) ++ kdb_printf("A"); ++ if (pte_dirty(*pte)) ++ kdb_printf("D"); ++ ++ } else { ++ kdb_printf("OFFSET=0x%lx ", swp_offset(pte_to_swp_entry(*pte))); ++ kdb_printf("TYPE=0x%ulx", swp_type(pte_to_swp_entry(*pte))); ++ } ++ ++ kdb_printf(")"); ++ ++ /* final newline is output by caller of kdbm_print_pte() */ ++ ++ return 0; ++} ++ ++/* ++ * kdbm_pte ++ * ++ * This function implements the 'pte' command. Print all pte_t structures ++ * that map to the given virtual address range (
through
++ * plus ) for the given process. The default value for nbytes is ++ * one. ++ * ++ * pte -m
[] Print all pte_t structures for ++ * virtual
in address space ++ * of which is a pointer to a ++ * mm_struct ++ * pte -p
[] Print all pte_t structures for ++ * virtual
in address space ++ * of ++ */ ++ ++static int ++kdbm_pte(int argc, const char **argv) ++{ ++ unsigned long addr; ++ long offset = 0; ++ int nextarg; ++ unsigned long nbytes = 1; ++ long npgs; ++ int diag; ++ int found; ++ pid_t pid; ++ struct task_struct *tp; ++ struct mm_struct *mm, copy_of_mm; ++ pgd_t *pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ pte_t *pte; ++ ++ if (argc < 3 || argc > 4) { ++ return KDB_ARGCOUNT; ++ } ++ ++ if (strcmp(argv[1], "-p") == 0) { ++ if ((diag = kdbgetularg(argv[2], (unsigned long *) &pid))) { ++ return diag; ++ } ++ ++ found = 0; ++ for_each_process(tp) { ++ if (tp->pid == pid) { ++ if (tp->mm != NULL) { ++ found = 1; ++ break; ++ } ++ kdb_printf("task structure's mm field is NULL\n"); ++ return 0; ++ } ++ } ++ ++ if (!found) { ++ kdb_printf("No process with pid == %d found\n", pid); ++ return 0; ++ } ++ mm = tp->mm; ++ } else if (strcmp(argv[1], "-m") == 0) { ++ ++ ++ nextarg = 2; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, ++ NULL)) ++ || (diag = kdb_getarea(copy_of_mm, addr))) ++ return (diag); ++ mm = ©_of_mm; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ ++ if ((diag = kdbgetularg(argv[3], &addr))) { ++ return diag; ++ } ++ ++ if (argc == 4) { ++ if ((diag = kdbgetularg(argv[4], &nbytes))) { ++ return diag; ++ } ++ } ++ ++ kdb_printf("vaddr pte\n"); ++ ++ npgs = ((((addr & ~PAGE_MASK) + nbytes) + ~PAGE_MASK) >> PAGE_SHIFT); ++ while (npgs-- > 0) { ++ ++ kdb_printf("0x%p ", (void *) (addr & PAGE_MASK)); ++ ++ pgd = pgd_offset(mm, addr); ++ if (pgd_present(*pgd)) { ++ pud = pud_offset(pgd, addr); ++ if (pud_present(*pud)) { ++ pmd = pmd_offset(pud, addr); ++ if (pmd_present(*pmd)) { ++ pte = pte_offset_map(pmd, addr); ++ if (pte_present(*pte)) { ++ kdbm_print_pte(pte); ++ } ++ } ++ } ++ } ++ ++ kdb_printf("\n"); ++ addr += PAGE_SIZE; ++ } ++ ++ return 0; ++} ++ ++/* ++ * kdbm_rpte ++ * ++ * This function implements the 'rpte' command. Print all pte_t structures ++ * that contain the given physical page range ( through ++ * plus ) for the given process. The default value for npages is ++ * one. ++ * ++ * rpte -m [] Print all pte_t structures for ++ * physical page in address space ++ * of which is a pointer to a ++ * mm_struct ++ * rpte -p [] Print all pte_t structures for ++ * physical page in address space ++ * of ++ */ ++ ++static int ++kdbm_rpte(int argc, const char **argv) ++{ ++ unsigned long addr; ++ unsigned long pfn; ++ long offset = 0; ++ int nextarg; ++ unsigned long npages = 1; ++ int diag; ++ int found; ++ pid_t pid; ++ struct task_struct *tp; ++ struct mm_struct *mm, copy_of_mm; ++ pgd_t *pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ pte_t *pte; ++ unsigned long g, u, m, t; ++ ++ if (argc < 3 || argc > 4) { ++ return KDB_ARGCOUNT; ++ } ++ ++ if (strcmp(argv[1], "-p") == 0) { ++ if ((diag = kdbgetularg(argv[2], (unsigned long *) &pid))) { ++ return diag; ++ } ++ ++ found = 0; ++ for_each_process(tp) { ++ if (tp->pid == pid) { ++ if (tp->mm != NULL) { ++ found = 1; ++ break; ++ } ++ kdb_printf("task structure's mm field is NULL\n"); ++ return 0; ++ } ++ } ++ ++ if (!found) { ++ kdb_printf("No process with pid == %d found\n", pid); ++ return 0; ++ } ++ mm = tp->mm; ++ } else if (strcmp(argv[1], "-m") == 0) { ++ ++ ++ nextarg = 2; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, ++ NULL)) ++ || (diag = kdb_getarea(copy_of_mm, addr))) ++ return (diag); ++ mm = ©_of_mm; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ ++ if ((diag = kdbgetularg(argv[3], &pfn))) { ++ return diag; ++ } ++ ++ if (argc == 4) { ++ if ((diag = kdbgetularg(argv[4], &npages))) { ++ return diag; ++ } ++ } ++ ++ /* spaces after vaddr depends on sizeof(unsigned long) */ ++ kdb_printf("pfn vaddr%*s pte\n", ++ (int)(2*sizeof(unsigned long) + 2 - 5), " "); ++ ++ for (g = 0, pgd = pgd_offset(mm, 0UL); g < PTRS_PER_PGD; ++g, ++pgd) { ++ if (pgd_none(*pgd) || pgd_bad(*pgd)) ++ continue; ++ for (u = 0, pud = pud_offset(pgd, 0UL); u < PTRS_PER_PUD; ++u, ++pud) { ++ if (pud_none(*pud) || pud_bad(*pud)) ++ continue; ++ for (m = 0, pmd = pmd_offset(pud, 0UL); m < PTRS_PER_PMD; ++m, ++pmd) { ++ if (pmd_none(*pmd) || pmd_bad(*pmd)) ++ continue; ++ for (t = 0, pte = pte_offset_map(pmd, 0UL); t < PTRS_PER_PTE; ++t, ++pte) { ++ if (pte_none(*pte)) ++ continue; ++ if (pte_pfn(*pte) < pfn || pte_pfn(*pte) >= (pfn + npages)) ++ continue; ++ addr = g << PGDIR_SHIFT; ++#ifdef __ia64__ ++ /* IA64 plays tricks with the pgd mapping to save space. ++ * This reverses pgd_index(). ++ */ ++ { ++ unsigned long region = g >> (PAGE_SHIFT - 6); ++ unsigned long l1index = g - (region << (PAGE_SHIFT - 6)); ++ addr = (region << 61) + (l1index << PGDIR_SHIFT); ++ } ++#endif ++ addr += (m << PMD_SHIFT) + (t << PAGE_SHIFT); ++ kdb_printf("0x%-14lx " kdb_bfd_vma_fmt0 " ", ++ pte_pfn(*pte), addr); ++ kdbm_print_pte(pte); ++ kdb_printf("\n"); ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++kdbm_print_dentry(unsigned long daddr) ++{ ++ struct dentry d; ++ int diag; ++ char buf[256]; ++ ++ kdb_printf("Dentry at 0x%lx\n", daddr); ++ if ((diag = kdb_getarea(d, (unsigned long)daddr))) ++ return diag; ++ ++ if ((d.d_name.len > sizeof(buf)) || (diag = kdb_getarea_size(buf, (unsigned long)(d.d_name.name), d.d_name.len))) ++ kdb_printf(" d_name.len = %d d_name.name = 0x%p\n", ++ d.d_name.len, d.d_name.name); ++ else ++ kdb_printf(" d_name.len = %d d_name.name = 0x%p <%.*s>\n", ++ d.d_name.len, d.d_name.name, ++ (int)(d.d_name.len), d.d_name.name); ++ ++ kdb_printf(" d_count = %d d_flags = 0x%x d_inode = 0x%p\n", ++ atomic_read(&d.d_count), d.d_flags, d.d_inode); ++ ++ kdb_printf(" d_parent = 0x%p\n", d.d_parent); ++ ++ kdb_printf(" d_hash.nxt = 0x%p d_hash.prv = 0x%p\n", ++ d.d_hash.next, d.d_hash.pprev); ++ ++ kdb_printf(" d_lru.nxt = 0x%p d_lru.prv = 0x%p\n", ++ d.d_lru.next, d.d_lru.prev); ++ ++ kdb_printf(" d_child.nxt = 0x%p d_child.prv = 0x%p\n", ++ d.d_u.d_child.next, d.d_u.d_child.prev); ++ ++ kdb_printf(" d_subdirs.nxt = 0x%p d_subdirs.prv = 0x%p\n", ++ d.d_subdirs.next, d.d_subdirs.prev); ++ ++ kdb_printf(" d_alias.nxt = 0x%p d_alias.prv = 0x%p\n", ++ d.d_alias.next, d.d_alias.prev); ++ ++ kdb_printf(" d_op = 0x%p d_sb = 0x%p d_fsdata = 0x%p\n", ++ d.d_op, d.d_sb, d.d_fsdata); ++ ++ kdb_printf(" d_iname = %s\n", ++ d.d_iname); ++ ++ if (d.d_inode) { ++ struct inode i; ++ kdb_printf("\nInode Entry at 0x%p\n", d.d_inode); ++ if ((diag = kdb_getarea(i, (unsigned long)d.d_inode))) ++ return diag; ++ kdb_printf(" i_mode = 0%o i_nlink = %d i_rdev = 0x%x\n", ++ i.i_mode, i.i_nlink, i.i_rdev); ++ ++ kdb_printf(" i_ino = %ld i_count = %d\n", ++ i.i_ino, atomic_read(&i.i_count)); ++ ++ kdb_printf(" i_hash.nxt = 0x%p i_hash.prv = 0x%p\n", ++ i.i_hash.next, i.i_hash.pprev); ++ ++ kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n", ++ i.i_list.next, i.i_list.prev); ++ ++ kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n", ++ i.i_dentry.next, i.i_dentry.prev); ++ ++ } ++ kdb_printf("\n"); ++ return 0; ++} ++ ++static int ++kdbm_filp(int argc, const char **argv) ++{ ++ struct file f; ++ int nextarg; ++ unsigned long addr; ++ long offset; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || ++ (diag = kdb_getarea(f, addr))) ++ return diag; ++ ++ kdb_printf("File Pointer at 0x%lx\n", addr); ++ ++ kdb_printf(" fu_list.nxt = 0x%p fu_list.prv = 0x%p\n", ++ f.f_u.fu_list.next, f.f_u.fu_list.prev); ++ ++ kdb_printf(" f_dentry = 0x%p f_vfsmnt = 0x%p f_op = 0x%p\n", ++ f.f_dentry, f.f_vfsmnt, f.f_op); ++ ++ kdb_printf(" f_count = " kdb_f_count_fmt ++ " f_flags = 0x%x f_mode = 0x%x\n", ++ atomic_long_read(&f.f_count), f.f_flags, f.f_mode); ++ ++ kdb_printf(" f_pos = %Ld\n", f.f_pos); ++#ifdef CONFIG_SECURITY ++ kdb_printf(" security = 0x%p\n", f.f_security); ++#endif ++ ++ kdb_printf(" private_data = 0x%p f_mapping = 0x%p\n\n", ++ f.private_data, f.f_mapping); ++ ++ return kdbm_print_dentry((unsigned long)f.f_dentry); ++} ++ ++static int ++kdbm_fl(int argc, const char **argv) ++{ ++ struct file_lock fl; ++ int nextarg; ++ unsigned long addr; ++ long offset; ++ int diag; ++ ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || ++ (diag = kdb_getarea(fl, addr))) ++ return diag; ++ ++ kdb_printf("File_lock at 0x%lx\n", addr); ++ ++ kdb_printf(" fl_next = 0x%p fl_link.nxt = 0x%p fl_link.prv = 0x%p\n", ++ fl.fl_next, fl.fl_link.next, fl.fl_link.prev); ++ kdb_printf(" fl_block.nxt = 0x%p fl_block.prv = 0x%p\n", ++ fl.fl_block.next, fl.fl_block.prev); ++ kdb_printf(" fl_owner = 0x%p fl_pid = %d fl_wait = 0x%p\n", ++ fl.fl_owner, fl.fl_pid, &fl.fl_wait); ++ kdb_printf(" fl_file = 0x%p fl_flags = 0x%x\n", ++ fl.fl_file, fl.fl_flags); ++ kdb_printf(" fl_type = %d fl_start = 0x%llx fl_end = 0x%llx\n", ++ fl.fl_type, fl.fl_start, fl.fl_end); ++ ++ kdb_printf(" file_lock_operations"); ++ if (fl.fl_ops) ++ kdb_printf("\n fl_copy_lock = 0x%p fl_release_private = 0x%p\n", ++ fl.fl_ops->fl_copy_lock, fl.fl_ops->fl_release_private); ++ else ++ kdb_printf(" empty\n"); ++ ++ kdb_printf(" lock_manager_operations"); ++ if (fl.fl_lmops) ++ kdb_printf("\n fl_compare_owner = 0x%p fl_notify = 0x%p\n", ++ fl.fl_lmops->fl_compare_owner, fl.fl_lmops->fl_notify); ++ else ++ kdb_printf(" empty\n"); ++ ++ kdb_printf(" fl_fasync = 0x%p fl_break 0x%lx\n", ++ fl.fl_fasync, fl.fl_break_time); ++ ++ return 0; ++} ++ ++ ++static int ++kdbm_dentry(int argc, const char **argv) ++{ ++ int nextarg; ++ unsigned long addr; ++ long offset; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) ++ return diag; ++ ++ return kdbm_print_dentry(addr); ++} ++ ++static int ++kdbm_kobject(int argc, const char **argv) ++{ ++ struct kobject k; ++ int nextarg; ++ unsigned long addr; ++ long offset; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || ++ (diag = kdb_getarea(k, addr))) ++ return diag; ++ ++ ++ kdb_printf("kobject at 0x%lx\n", addr); ++ ++ if (k.name) { ++ char c; ++ kdb_printf(" name 0x%p", k.name); ++ if (kdb_getarea(c, (unsigned long)k.name) == 0) ++ kdb_printf(" '%s'", k.name); ++ kdb_printf("\n"); ++ } ++ ++ if (k.name != kobject_name((struct kobject *)addr)) ++ kdb_printf(" name '%.20s'\n", k.name); ++ ++ kdb_printf(" kref.refcount %d'\n", atomic_read(&k.kref.refcount)); ++ ++ kdb_printf(" entry.next = 0x%p entry.prev = 0x%p\n", ++ k.entry.next, k.entry.prev); ++ ++ kdb_printf(" parent = 0x%p kset = 0x%p ktype = 0x%p sd = 0x%p\n", ++ k.parent, k.kset, k.ktype, k.sd); ++ ++ return 0; ++} ++ ++static int ++kdbm_sh(int argc, const char **argv) ++{ ++ int diag; ++ int nextarg; ++ unsigned long addr; ++ long offset = 0L; ++ struct Scsi_Host sh; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || ++ (diag = kdb_getarea(sh, addr))) ++ return diag; ++ ++ kdb_printf("Scsi_Host at 0x%lx\n", addr); ++ kdb_printf("host_queue = 0x%p\n", sh.__devices.next); ++ kdb_printf("ehandler = 0x%p eh_action = 0x%p\n", ++ sh.ehandler, sh.eh_action); ++ kdb_printf("host_wait = 0x%p hostt = 0x%p\n", ++ &sh.host_wait, sh.hostt); ++ kdb_printf("host_failed = %d host_no = %d resetting = %d\n", ++ sh.host_failed, sh.host_no, sh.resetting); ++ kdb_printf("max id/lun/channel = [%d/%d/%d] this_id = %d\n", ++ sh.max_id, sh.max_lun, sh.max_channel, sh.this_id); ++ kdb_printf("can_queue = %d cmd_per_lun = %d sg_tablesize = %d u_isa_dma = %d\n", ++ sh.can_queue, sh.cmd_per_lun, sh.sg_tablesize, sh.unchecked_isa_dma); ++ kdb_printf("host_blocked = %d reverse_ordering = %d \n", ++ sh.host_blocked, sh.reverse_ordering); ++ ++ return 0; ++} ++ ++static int ++kdbm_sd(int argc, const char **argv) ++{ ++ int diag; ++ int nextarg; ++ unsigned long addr; ++ long offset = 0L; ++ struct scsi_device *sd = NULL; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) ++ goto out; ++ if (!(sd = kmalloc(sizeof(*sd), GFP_ATOMIC))) { ++ kdb_printf("kdbm_sd: cannot kmalloc sd\n"); ++ goto out; ++ } ++ if ((diag = kdb_getarea(*sd, addr))) ++ goto out; ++ ++ kdb_printf("scsi_device at 0x%lx\n", addr); ++ kdb_printf("next = 0x%p prev = 0x%p host = 0x%p\n", ++ sd->siblings.next, sd->siblings.prev, sd->host); ++ kdb_printf("device_busy = %d current_cmnd 0x%p\n", ++ sd->device_busy, sd->current_cmnd); ++ kdb_printf("id/lun/chan = [%d/%d/%d] single_lun = %d device_blocked = %d\n", ++ sd->id, sd->lun, sd->channel, sd->sdev_target->single_lun, sd->device_blocked); ++ kdb_printf("queue_depth = %d current_tag = %d scsi_level = %d\n", ++ sd->queue_depth, sd->current_tag, sd->scsi_level); ++ kdb_printf("%8.8s %16.16s %4.4s\n", sd->vendor, sd->model, sd->rev); ++out: ++ if (sd) ++ kfree(sd); ++ return diag; ++} ++ ++static int ++kdbm_sc(int argc, const char **argv) ++{ ++ int diag; ++ int nextarg; ++ unsigned long addr; ++ long offset = 0L; ++ struct scsi_cmnd *sc = NULL; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ nextarg = 1; ++ if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) ++ goto out; ++ if (!(sc = kmalloc(sizeof(*sc), GFP_ATOMIC))) { ++ kdb_printf("kdbm_sc: cannot kmalloc sc\n"); ++ goto out; ++ } ++ if ((diag = kdb_getarea(*sc, addr))) ++ goto out; ++ ++ kdb_printf("scsi_cmnd at 0x%lx\n", addr); ++ kdb_printf("device = 0x%p next = 0x%p\n", ++ sc->device, sc->list.next); ++ kdb_printf("serial_number = %ld retries = %d\n", ++ sc->serial_number, sc->retries); ++ kdb_printf("cmd_len = %d\n", sc->cmd_len); ++ kdb_printf("cmnd = [%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x]\n", ++ sc->cmnd[0], sc->cmnd[1], sc->cmnd[2], sc->cmnd[3], sc->cmnd[4], ++ sc->cmnd[5], sc->cmnd[6], sc->cmnd[7], sc->cmnd[8], sc->cmnd[9], ++ sc->cmnd[10], sc->cmnd[11]); ++ kdb_printf("request_buffer = 0x%p request_bufflen = %d\n", ++ scsi_sglist(sc), scsi_bufflen(sc)); ++ kdb_printf("use_sg = %d\n", scsi_sg_count(sc)); ++ kdb_printf("underflow = %d transfersize = %d\n", ++ sc->underflow, sc->transfersize); ++ kdb_printf("tag = %d\n", sc->tag); ++ ++out: ++ if (sc) ++ kfree(sc); ++ return diag; ++} ++ ++static int __init kdbm_vm_init(void) ++{ ++ kdb_register("vm", kdbm_vm, "[-v] ", "Display vm_area_struct", 0); ++ kdb_register("vmp", kdbm_vm, "[-v] ", "Display all vm_area_struct for ", 0); ++#ifdef CONFIG_NUMA ++ kdb_register("mempolicy", kdbm_mpol, "", "Display mempolicy structure", 0); ++ kdb_register("pgdat", kdbm_pgdat, "", "Display pglist_data node structure", 0); ++#else ++ kdb_register("pgdat", kdbm_pgdat, "", "Display pglist_data node structure", 0); ++#endif ++ kdb_register("pte", kdbm_pte, "( -m | -p ) []", "Display pte_t for mm_struct or pid", 0); ++ kdb_register("rpte", kdbm_rpte, "( -m | -p ) []", "Find pte_t containing pfn for mm_struct or pid", 0); ++ kdb_register("dentry", kdbm_dentry, "", "Display interesting dentry stuff", 0); ++ kdb_register("kobject", kdbm_kobject, "", "Display interesting kobject stuff", 0); ++ kdb_register("filp", kdbm_filp, "", "Display interesting filp stuff", 0); ++ kdb_register("fl", kdbm_fl, "", "Display interesting file_lock stuff", 0); ++ kdb_register("sh", kdbm_sh, "", "Show scsi_host", 0); ++ kdb_register("sd", kdbm_sd, "", "Show scsi_device", 0); ++ kdb_register("sc", kdbm_sc, "", "Show scsi_cmnd", 0); ++ ++ return 0; ++} ++ ++static void __exit kdbm_vm_exit(void) ++{ ++ kdb_unregister("vm"); ++ kdb_unregister("vmp"); ++#ifdef CONFIG_NUMA ++ kdb_unregister("mempolicy"); ++#endif ++ kdb_unregister("pgdat"); ++ kdb_unregister("pte"); ++ kdb_unregister("rpte"); ++ kdb_unregister("dentry"); ++ kdb_unregister("kobject"); ++ kdb_unregister("filp"); ++ kdb_unregister("fl"); ++ kdb_unregister("sh"); ++ kdb_unregister("sd"); ++ kdb_unregister("sc"); ++} ++ ++module_init(kdbm_vm_init) ++module_exit(kdbm_vm_exit) +--- /dev/null ++++ b/kdb/modules/kdbm_x86.c +@@ -0,0 +1,1093 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Author: Vamsi Krishna S. ++ * (C) 2003 IBM Corporation. ++ * 2006-10-10 Keith Owens ++ * Reworked to include x86_64 support ++ * Copyright (c) 2006 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#if 0 ++#include ++#endif ++ ++MODULE_AUTHOR("Vamsi Krishna S./IBM"); ++MODULE_DESCRIPTION("x86 specific information (gdt/idt/ldt/page tables)"); ++MODULE_LICENSE("GPL"); ++ ++/* Isolate as many of the i386/x86_64 differences as possible in one spot */ ++ ++#ifdef CONFIG_X86_64 ++ ++#define KDB_X86_64 1 ++#define MOVLQ "movq" ++ ++typedef struct desc_struct kdb_desc_t; ++typedef struct gate_struct64 kdb_gate_desc_t; ++ ++#define KDB_SYS_DESC_OFFSET(d) ((unsigned long)d->offset_high << 32 | d->offset_middle << 16 | d->offset_low) ++#define KDB_SYS_DESC_CALLG_COUNT(d) 0 ++ ++#else /* !CONFIG_X86_64 */ ++ ++#define KDB_X86_64 0 ++#define MOVLQ "movl" ++ ++/* i386 has no detailed mapping for the 8 byte segment descriptor, copy the ++ * x86_64 one and merge the l and avl bits. ++ */ ++struct kdb_desc { ++ u16 limit0; ++ u16 base0; ++ unsigned base1 : 8, type : 4, s : 1, dpl : 2, p : 1; ++ unsigned limit : 4, avl : 2, d : 1, g : 1, base2 : 8; ++} __attribute__((packed)); ++typedef struct kdb_desc kdb_desc_t; ++ ++/* i386 has no detailed mapping for the 8 byte gate descriptor, base it on the ++ * x86_64 one. ++ */ ++struct kdb_gate_desc { ++ u16 offset_low; ++ u16 segment; ++ unsigned res : 8, type : 4, s : 1, dpl : 2, p : 1; ++ u16 offset_middle; ++} __attribute__((packed)); ++typedef struct kdb_gate_desc kdb_gate_desc_t; ++ ++#define KDB_SYS_DESC_OFFSET(d) ((unsigned long)(d->offset_middle << 16 | d->offset_low)) ++#define KDB_SYS_DESC_CALLG_COUNT(d) ((unsigned int)(d->res & 0x0F)) ++ ++#endif /* CONFIG_X86_64 */ ++ ++#define KDB_SEL_MAX 0x2000 ++#define KDB_IDT_MAX 0x100 ++#define KDB_SYS_DESC_TYPE_TSS16 0x01 ++#define KDB_SYS_DESC_TYPE_LDT 0x02 ++#define KDB_SYS_DESC_TYPE_TSSB16 0x03 ++#define KDB_SYS_DESC_TYPE_CALLG16 0x04 ++#define KDB_SYS_DESC_TYPE_TASKG 0x05 ++#define KDB_SYS_DESC_TYPE_INTG16 0x06 ++#define KDB_SYS_DESC_TYPE_TRAP16 0x07 ++ ++#define KDB_SYS_DESC_TYPE_TSS 0x09 ++#define KDB_SYS_DESC_TYPE_TSSB 0x0b ++#define KDB_SYS_DESC_TYPE_CALLG 0x0c ++#define KDB_SYS_DESC_TYPE_INTG 0x0e ++#define KDB_SYS_DESC_TYPE_TRAPG 0x0f ++ ++#define KDB_SEG_DESC_TYPE_CODE 0x08 ++#define KDB_SEG_DESC_TYPE_CODE_R 0x02 ++#define KDB_SEG_DESC_TYPE_DATA_W 0x02 ++#define KDB_SEG_DESC_TYPE_CODE_C 0x02 /* conforming */ ++#define KDB_SEG_DESC_TYPE_DATA_D 0x02 /* expand-down */ ++#define KDB_SEG_DESC_TYPE_A 0x01 /* accessed */ ++ ++#define _LIMIT(d) ((unsigned long)((d)->limit << 16 | (d)->limit0)) ++#define KDB_SEG_DESC_LIMIT(d) ((d)->g ? ((_LIMIT(d)+1) << 12) -1 : _LIMIT(d)) ++ ++static unsigned long kdb_seg_desc_base(kdb_desc_t *d) ++{ ++ unsigned long base = d->base2 << 24 | d->base1 << 16 | d->base0; ++#ifdef CONFIG_X86_64 ++ switch (d->type) { ++ case KDB_SYS_DESC_TYPE_TSS: ++ case KDB_SYS_DESC_TYPE_TSSB: ++ case KDB_SYS_DESC_TYPE_LDT: ++ base += (unsigned long)(((struct ldttss_desc64 *)d)->base3) << 32; ++ break; ++ } ++#endif ++ return base; ++} ++ ++/* helper functions to display system registers in verbose mode */ ++static void display_gdtr(void) ++{ ++ struct desc_ptr gdtr; ++ ++ __asm__ __volatile__ ("sgdt %0\n\t" : "=m"(gdtr)); ++ kdb_printf("gdtr.address = " kdb_machreg_fmt0 ", gdtr.size = 0x%x\n", ++ gdtr.address, gdtr.size); ++ ++ return; ++} ++ ++static void display_ldtr(void) ++{ ++ struct desc_ptr gdtr; ++ unsigned long ldtr; ++ ++ __asm__ __volatile__ ("sgdt %0\n\t" : "=m"(gdtr)); ++ __asm__ __volatile__ ("sldt %0\n\t" : "=m"(ldtr)); ++ ldtr &= 0xfff8; /* extract the index */ ++ ++ kdb_printf("ldtr = " kdb_machreg_fmt0 " ", ldtr); ++ ++ if (ldtr < gdtr.size) { ++ kdb_desc_t *ldt_desc = ++ (kdb_desc_t *)(gdtr.address + ldtr); ++ kdb_printf("base=" kdb_machreg_fmt0 ++ ", limit=" kdb_machreg_fmt "\n", ++ kdb_seg_desc_base(ldt_desc), ++ KDB_SEG_DESC_LIMIT(ldt_desc)); ++ } else { ++ kdb_printf("invalid\n"); ++ } ++ ++ return; ++} ++ ++static void display_idtr(void) ++{ ++ struct desc_ptr idtr; ++ __asm__ __volatile__ ("sidt %0\n\t" : "=m"(idtr)); ++ kdb_printf("idtr.address = " kdb_machreg_fmt0 ", idtr.size = 0x%x\n", ++ idtr.address, idtr.size); ++ return; ++} ++ ++static const char *cr0_flags[] = { ++ "pe", "mp", "em", "ts", "et", "ne", NULL, NULL, ++ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ++ "wp", NULL, "am", NULL, NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, NULL, "nw", "cd", "pg"}; ++ ++static void display_cr0(void) ++{ ++ kdb_machreg_t cr0; ++ int i; ++ __asm__ (MOVLQ " %%cr0,%0\n\t":"=r"(cr0)); ++ kdb_printf("cr0 = " kdb_machreg_fmt0, cr0); ++ for (i = 0; i < ARRAY_SIZE(cr0_flags); i++) { ++ if (test_bit(i, &cr0) && cr0_flags[i]) ++ kdb_printf(" %s", cr0_flags[i]); ++ } ++ kdb_printf("\n"); ++ return; ++} ++ ++static void display_cr3(void) ++{ ++ kdb_machreg_t cr3; ++ __asm__ (MOVLQ " %%cr3,%0\n\t":"=r"(cr3)); ++ kdb_printf("cr3 = " kdb_machreg_fmt0 " ", cr3); ++ if (cr3 & 0x08) ++ kdb_printf("pwt "); ++ if (cr3 & 0x10) ++ kdb_printf("pcd "); ++ kdb_printf("%s=" kdb_machreg_fmt0 "\n", ++ KDB_X86_64 ? "pml4" : "pgdir", cr3 & PAGE_MASK); ++ return; ++} ++ ++static const char *cr4_flags[] = { ++ "vme", "pvi", "tsd", "de", ++ "pse", "pae", "mce", "pge", ++ "pce", "osfxsr" "osxmmexcpt"}; ++ ++static void display_cr4(void) ++{ ++ kdb_machreg_t cr4; ++ int i; ++ __asm__ (MOVLQ " %%cr4,%0\n\t":"=r"(cr4)); ++ kdb_printf("cr4 = " kdb_machreg_fmt0, cr4); ++ for (i = 0; i < ARRAY_SIZE(cr4_flags); i++) { ++ if (test_bit(i, &cr4)) ++ kdb_printf(" %s", cr4_flags[i]); ++ } ++ kdb_printf("\n"); ++ return; ++} ++ ++static void display_cr8(void) ++{ ++#ifdef CONFIG_X86_64 ++ kdb_machreg_t cr8; ++ __asm__ (MOVLQ " %%cr8,%0\n\t":"=r"(cr8)); ++ kdb_printf("cr8 = " kdb_machreg_fmt0 "\n", cr8); ++ return; ++#endif /* CONFIG_X86_64 */ ++} ++ ++static char *dr_type_name[] = { "exec", "write", "io", "rw" }; ++ ++static void display_dr_status(int nr, int enabled, int local, int len, int type) ++{ ++ if (!enabled) { ++ kdb_printf("\tdebug register %d: not enabled\n", nr); ++ return; ++ } ++ ++ kdb_printf(" debug register %d: %s, len = %d, type = %s\n", ++ nr, ++ local? " local":"global", ++ len, ++ dr_type_name[type]); ++} ++ ++static void display_dr(void) ++{ ++ kdb_machreg_t dr0, dr1, dr2, dr3, dr6, dr7; ++ int dbnr, set; ++ ++ __asm__ (MOVLQ " %%db0,%0\n\t":"=r"(dr0)); ++ __asm__ (MOVLQ " %%db1,%0\n\t":"=r"(dr1)); ++ __asm__ (MOVLQ " %%db2,%0\n\t":"=r"(dr2)); ++ __asm__ (MOVLQ " %%db3,%0\n\t":"=r"(dr3)); ++ __asm__ (MOVLQ " %%db6,%0\n\t":"=r"(dr6)); ++ __asm__ (MOVLQ " %%db7,%0\n\t":"=r"(dr7)); ++ ++ kdb_printf("dr0 = " kdb_machreg_fmt0 " dr1 = " kdb_machreg_fmt0 ++ " dr2 = " kdb_machreg_fmt0 " dr3 = " kdb_machreg_fmt0 "\n", ++ dr0, dr1, dr2, dr3); ++ kdb_printf("dr6 = " kdb_machreg_fmt0 " ", dr6); ++ dbnr = dr6 & DR6_DR_MASK; ++ if (dbnr) { ++ int nr; ++ switch(dbnr) { ++ case 1: ++ nr = 0; break; ++ case 2: ++ nr = 1; break; ++ case 4: ++ nr = 2; break; ++ default: ++ nr = 3; break; ++ } ++ kdb_printf("debug register hit = %d", nr); ++ } else if (dr6 & DR_STEP) { ++ kdb_printf("single step"); ++ } else if (dr6 & DR_SWITCH) { ++ kdb_printf("task switch"); ++ } ++ kdb_printf("\n"); ++ ++ kdb_printf("dr7 = " kdb_machreg_fmt0 "\n", dr7); ++ set = DR7_L0(dr7) || DR7_G0(dr7); ++ display_dr_status(0, set, DR7_L0(dr7), DR7_LEN0(dr7), DR7_RW0(dr7)); ++ set = DR7_L1(dr7) || DR7_G1(dr7); ++ display_dr_status(1, set, DR7_L1(dr7), DR7_LEN1(dr7), DR7_RW1(dr7)); ++ set = DR7_L2(dr7) || DR7_G2(dr7); ++ display_dr_status(2, set, DR7_L2(dr7), DR7_LEN2(dr7), DR7_RW2(dr7)); ++ set = DR7_L3(dr7) || DR7_G3(dr7); ++ display_dr_status(3, set, DR7_L3(dr7), DR7_LEN3(dr7), DR7_RW3(dr7)); ++} ++ ++static char *set_eflags[] = { ++ "carry", NULL, "parity", NULL, "adjust", NULL, "zero", "sign", ++ "trace", "intr-on", "dir", "overflow", NULL, NULL, "nestedtask", NULL, ++ "resume", "vm", "align", "vif", "vip", "id"}; ++ ++static void display_eflags(unsigned long ef) ++{ ++ int i, iopl; ++ kdb_printf("eflags = " kdb_machreg_fmt0 " ", ef); ++ for (i = 0; i < ARRAY_SIZE(set_eflags); i++) { ++ if (test_bit(i, &ef) && set_eflags[i]) ++ kdb_printf("%s ", set_eflags[i]); ++ } ++ ++ iopl = (ef & 0x00003000) >> 12; ++ kdb_printf("iopl=%d\n", iopl); ++ return; ++} ++ ++static void display_tss(struct tss_struct *t) ++{ ++#ifdef CONFIG_X86_64 ++ int i; ++ kdb_printf(" sp0 = 0x%016Lx, sp1 = 0x%016Lx\n", ++ t->x86_tss.sp0, t->x86_tss.sp1); ++ kdb_printf(" sp2 = 0x%016Lx\n", t->x86_tss.sp2); ++ for (i = 0; i < ARRAY_SIZE(t->x86_tss.ist); ++i) ++ kdb_printf(" ist[%d] = 0x%016Lx\n", ++ i, t->x86_tss.ist[i]); ++ kdb_printf(" iomap = 0x%04x\n", t->x86_tss.io_bitmap_base); ++#else /* !CONFIG_X86_64 */ ++ kdb_printf(" cs = %04x, ip = " kdb_machreg_fmt0 "\n", ++ t->x86_tss.es, t->x86_tss.ip); ++ kdb_printf(" ss = %04x, sp = " kdb_machreg_fmt0 "\n", ++ t->x86_tss.ss, t->x86_tss.sp); ++ kdb_printf(" ss0 = %04x, sp0 = " kdb_machreg_fmt0 "\n", ++ t->x86_tss.ss0, t->x86_tss.sp0); ++ kdb_printf(" ss1 = %04x, sp1 = " kdb_machreg_fmt0 "\n", ++ t->x86_tss.ss1, t->x86_tss.sp1); ++ kdb_printf(" ss2 = %04x, sp2 = " kdb_machreg_fmt0 "\n", ++ t->x86_tss.ss2, t->x86_tss.sp2); ++ kdb_printf(" ldt = %04x, cr3 = " kdb_machreg_fmt0 "\n", ++ t->x86_tss.ldt, t->x86_tss.__cr3); ++ kdb_printf(" ds = %04x, es = %04x fs = %04x gs = %04x\n", ++ t->x86_tss.ds, t->x86_tss.es, t->x86_tss.fs, t->x86_tss.gs); ++ kdb_printf(" ax = " kdb_machreg_fmt0 ", bx = " kdb_machreg_fmt0 ++ " cx = " kdb_machreg_fmt0 " dx = " kdb_machreg_fmt0 "\n", ++ t->x86_tss.ax, t->x86_tss.bx, t->x86_tss.cx, t->x86_tss.dx); ++ kdb_printf(" si = " kdb_machreg_fmt0 ", di = " kdb_machreg_fmt0 ++ " bp = " kdb_machreg_fmt0 "\n", ++ t->x86_tss.si, t->x86_tss.di, t->x86_tss.bp); ++ kdb_printf(" trace = %d, iomap = 0x%04x\n", t->x86_tss.trace, t->x86_tss.io_bitmap_base); ++#endif /* CONFIG_X86_64 */ ++} ++ ++static char *gate_desc_types[] = { ++#ifdef CONFIG_X86_64 ++ "reserved-0", "reserved-1", "ldt", "reserved-3", ++ "reserved-4", "reserved-5", "reserved-6", "reserved-7", ++ "reserved-8", "tss-avlb", "reserved-10", "tss-busy", ++ "callgate", "reserved-13", "intgate", "trapgate", ++#else /* CONFIG_X86_64 */ ++ "reserved-0", "tss16-avlb", "ldt", "tss16-busy", ++ "callgate16", "taskgate", "intgate16", "trapgate16", ++ "reserved-8", "tss-avlb", "reserved-10", "tss-busy", ++ "callgate", "reserved-13", "intgate", "trapgate", ++#endif /* CONFIG_X86_64 */ ++}; ++ ++static void ++display_gate_desc(kdb_gate_desc_t *d) ++{ ++ kdb_printf("%-11s ", gate_desc_types[d->type]); ++ ++ switch(d->type) { ++ case KDB_SYS_DESC_TYPE_LDT: ++ kdb_printf("base="); ++ kdb_symbol_print(kdb_seg_desc_base((kdb_desc_t *)d), NULL, ++ KDB_SP_DEFAULT); ++ kdb_printf(" limit=" kdb_machreg_fmt " dpl=%d\n", ++ KDB_SEG_DESC_LIMIT((kdb_desc_t *)d), d->dpl); ++ break; ++ case KDB_SYS_DESC_TYPE_TSS: ++ case KDB_SYS_DESC_TYPE_TSS16: ++ case KDB_SYS_DESC_TYPE_TSSB: ++ case KDB_SYS_DESC_TYPE_TSSB16: ++ { ++ struct tss_struct *tss = ++ (struct tss_struct *) ++ kdb_seg_desc_base((kdb_desc_t *)d); ++ kdb_printf("base="); ++ kdb_symbol_print((unsigned long)tss, NULL, KDB_SP_DEFAULT); ++ kdb_printf(" limit=" kdb_machreg_fmt " dpl=%d\n", ++ KDB_SEG_DESC_LIMIT((kdb_desc_t *)d), d->dpl); ++ display_tss(tss); ++ break; ++ } ++ case KDB_SYS_DESC_TYPE_CALLG16: ++ kdb_printf("segment=0x%4.4x off=", d->segment); ++ kdb_symbol_print(KDB_SYS_DESC_OFFSET(d), NULL, KDB_SP_DEFAULT); ++ kdb_printf(" dpl=%d wc=%d\n", ++ d->dpl, KDB_SYS_DESC_CALLG_COUNT(d)); ++ break; ++ case KDB_SYS_DESC_TYPE_CALLG: ++ kdb_printf("segment=0x%4.4x off=", d->segment); ++ kdb_symbol_print(KDB_SYS_DESC_OFFSET(d), NULL, KDB_SP_DEFAULT); ++ kdb_printf(" dpl=%d\n", d->dpl); ++ break; ++ default: ++ kdb_printf("segment=0x%4.4x off=", d->segment); ++ if (KDB_SYS_DESC_OFFSET(d)) ++ kdb_symbol_print(KDB_SYS_DESC_OFFSET(d), NULL, ++ KDB_SP_DEFAULT); ++ else ++ kdb_printf(kdb_machreg_fmt0, KDB_SYS_DESC_OFFSET(d)); ++ kdb_printf(" dpl=%d", d->dpl); ++#ifdef CONFIG_X86_64 ++ if (d->ist) ++ kdb_printf(" ist=%d", d->ist); ++#endif /* CONFIG_X86_64 */ ++ kdb_printf("\n"); ++ break; ++ } ++} ++ ++static void ++display_seg_desc(kdb_desc_t *d) ++{ ++ unsigned char type = d->type; ++ ++ if (type & KDB_SEG_DESC_TYPE_CODE) { ++ kdb_printf("%-11s base=" kdb_machreg_fmt0 " limit=" ++ kdb_machreg_fmt " dpl=%d %c%c%c %s %s %s \n", ++ "code", ++ kdb_seg_desc_base(d), KDB_SEG_DESC_LIMIT(d), ++ d->dpl, ++ (type & KDB_SEG_DESC_TYPE_CODE_R)?'r':'-', ++ '-', 'x', ++#ifdef CONFIG_X86_64 ++ d->l ? "64b" : d->d ? "32b" : "16b", ++#else /* !CONFIG_X86_64 */ ++ d->d ? "32b" : "16b", ++#endif /* CONFIG_X86_64 */ ++ (type & KDB_SEG_DESC_TYPE_A)?"ac":"", ++ (type & KDB_SEG_DESC_TYPE_CODE_C)?"conf":""); ++ } else { ++ kdb_printf("%-11s base=" kdb_machreg_fmt0 " limit=" ++ kdb_machreg_fmt " dpl=%d %c%c%c %s %s %s \n", ++ "data", ++ kdb_seg_desc_base(d), KDB_SEG_DESC_LIMIT(d), ++ d->dpl, ++ 'r', ++ (type & KDB_SEG_DESC_TYPE_DATA_W)?'w':'-', ++ '-', ++ d->d ? "32b" : "16b", ++ (type & KDB_SEG_DESC_TYPE_A)?"ac":"", ++ (type & KDB_SEG_DESC_TYPE_DATA_D)?"down":""); ++ } ++} ++ ++static int ++kdb_parse_two_numbers(int argc, const char **argv, int *sel, int *count, ++ int *last_sel, int *last_count) ++{ ++ int diag; ++ ++ if (argc > 2) ++ return KDB_ARGCOUNT; ++ ++ kdbgetintenv("MDCOUNT", count); ++ ++ if (argc == 0) { ++ *sel = *last_sel; ++ if (*last_count) ++ *count = *last_count; ++ } else { ++ unsigned long val; ++ ++ if (argc >= 1) { ++ diag = kdbgetularg(argv[1], &val); ++ if (diag) ++ return diag; ++ *sel = val; ++ } ++ if (argc >= 2) { ++ diag = kdbgetularg(argv[2], &val); ++ if (diag) ++ return diag; ++ *count = (int) val; ++ *last_count = (int) val; ++ } else if (*last_count) { ++ *count = *last_count; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * kdb_gdt ++ * ++ * This function implements the 'gdt' command. ++ * ++ * gdt [ []] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++static int ++kdb_gdt(int argc, const char **argv) ++{ ++ int sel = 0; ++ struct desc_ptr gdtr; ++ int diag, count = 8; ++ kdb_desc_t *gdt; ++ unsigned int max_sel; ++ static int last_sel = 0, last_count = 0; ++ ++ diag = kdb_parse_two_numbers(argc, argv, &sel, &count, ++ &last_sel, &last_count); ++ if (diag) ++ return diag; ++ ++ __asm__ __volatile__ ("sgdt %0\n\t" : "=m"(gdtr)); ++ gdt = (kdb_desc_t *) gdtr.address; ++ ++ max_sel = (gdtr.size + 1) / sizeof(kdb_desc_t); ++ if (sel >= max_sel) { ++ kdb_printf("Maximum selector (%d) reached\n", max_sel); ++ return 0; ++ } ++ ++ if (sel + count > max_sel) ++ count = max_sel - sel; ++ ++ while (count--) { ++ kdb_desc_t *d = &gdt[sel]; ++ kdb_printf("0x%4.4x ", sel++); ++ ++ if (!d->p) { ++ kdb_printf("not present\n"); ++ continue; ++ } ++ if (d->s) { ++ display_seg_desc(d); ++ } else { ++ display_gate_desc((kdb_gate_desc_t *)d); ++ if (KDB_X86_64 && count) { ++ ++sel; /* this descriptor occupies two slots */ ++ --count; ++ } ++ } ++ } ++ ++ last_sel = sel; ++ return 0; ++} ++ ++/* ++ * kdb_ldt ++ * ++ * This function implements the 'ldt' command. ++ * ++ * ldt [ []] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++static int ++kdb_ldt(int argc, const char **argv) ++{ ++ int sel = 0; ++ struct desc_ptr gdtr; ++ unsigned long ldtr = 0; ++ int diag, count = 8; ++ kdb_desc_t *ldt, *ldt_desc; ++ unsigned int max_sel; ++ static int last_sel = 0, last_count = 0; ++ ++ diag = kdb_parse_two_numbers(argc, argv, &sel, &count, ++ &last_sel, &last_count); ++ if (diag) ++ return diag; ++ ++ if (strcmp(argv[0], "ldtp") == 0) { ++ kdb_printf("pid=%d, process=%s\n", ++ kdb_current_task->pid, kdb_current_task->comm); ++ if (!kdb_current_task->mm || ++ !kdb_current_task->mm->context.ldt) { ++ kdb_printf("no special LDT for this process\n"); ++ return 0; ++ } ++ ldt = kdb_current_task->mm->context.ldt; ++ max_sel = kdb_current_task->mm->context.size; ++ } else { ++ ++ /* sldt gives the GDT selector for the segment containing LDT */ ++ __asm__ __volatile__ ("sgdt %0\n\t" : "=m"(gdtr)); ++ __asm__ __volatile__ ("sldt %0\n\t" : "=m"(ldtr)); ++ ldtr &= 0xfff8; /* extract the index */ ++ ++ if (ldtr > gdtr.size+1) { ++ kdb_printf("invalid ldtr\n"); ++ return 0; ++ } ++ ++ ldt_desc = (kdb_desc_t *)(gdtr.address + ldtr); ++ ldt = (kdb_desc_t *)kdb_seg_desc_base(ldt_desc); ++ max_sel = (KDB_SEG_DESC_LIMIT(ldt_desc)+1) / sizeof(kdb_desc_t); ++ } ++ ++ if (sel >= max_sel) { ++ kdb_printf("Maximum selector (%d) reached\n", max_sel); ++ return 0; ++ } ++ ++ if (sel + count > max_sel) ++ count = max_sel - sel; ++ ++ while (count--) { ++ kdb_desc_t *d = &ldt[sel]; ++ kdb_printf("0x%4.4x ", sel++); ++ ++ if (!d->p) { ++ kdb_printf("not present\n"); ++ continue; ++ } ++ if (d->s) { ++ display_seg_desc(d); ++ } else { ++ display_gate_desc((kdb_gate_desc_t *)d); ++ if (KDB_X86_64 && count) { ++ ++sel; /* this descriptor occupies two slots */ ++ --count; ++ } ++ } ++ } ++ ++ last_sel = sel; ++ return 0; ++} ++ ++/* ++ * kdb_idt ++ * ++ * This function implements the 'idt' command. ++ * ++ * idt [ []] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++static int ++kdb_idt(int argc, const char **argv) ++{ ++ int vec = 0; ++ struct desc_ptr idtr; ++ int diag, count = 8; ++ kdb_gate_desc_t *idt; ++ unsigned int max_entries; ++ static int last_vec = 0, last_count = 0; ++ ++ diag = kdb_parse_two_numbers(argc, argv, &vec, &count, ++ &last_vec, &last_count); ++ if (diag) ++ return diag; ++ ++ __asm__ __volatile__ ("sidt %0\n\t" : "=m"(idtr)); ++ idt = (kdb_gate_desc_t *)idtr.address; ++ ++ max_entries = (idtr.size+1) / sizeof(kdb_gate_desc_t); ++ if (vec >= max_entries) { ++ kdb_printf("Maximum vector (%d) reached\n", max_entries); ++ return 0; ++ } ++ ++ if (vec + count > max_entries) ++ count = max_entries - vec; ++ ++ while (count--) { ++ kdb_gate_desc_t *d = &idt[vec]; ++ kdb_printf("0x%4.4x ", vec++); ++ if (!d->p) { ++ kdb_printf("not present\n"); ++ continue; ++ } ++#ifndef CONFIG_X86_64 ++ if (d->s) { ++ kdb_printf("invalid\n"); ++ continue; ++ } ++#endif /* CONFIG_X86_64 */ ++ display_gate_desc(d); ++ } ++ ++ last_vec = vec; ++ ++ return 0; ++} ++ ++#if 0 ++static int ++get_pagetables(unsigned long addr, pgd_t **pgdir, pmd_t **pgmiddle, pte_t **pte) ++{ ++ pgd_t *d; ++ pmd_t *m; ++ pte_t *t; ++ ++ if (addr > PAGE_OFFSET) { ++ d = pgd_offset_k(addr); ++ } else { ++ kdb_printf("pid=%d, process=%s\n", kdb_current_task->pid, kdb_current_task->comm); ++ d = pgd_offset(kdb_current_task->mm, addr); ++ } ++ ++ if (pgd_none(*d) || pgd_bad(*d)) { ++ *pgdir = NULL; ++ *pgmiddle = NULL; ++ *pte = NULL; ++ return 0; ++ } else { ++ *pgdir = d; ++ } ++ ++ /* if _PAGE_PSE is set, pgdir points directly to the page. */ ++ if (pgd_val(*d) & _PAGE_PSE) { ++ *pgmiddle = NULL; ++ *pte = NULL; ++ return 0; ++ } ++ ++ m = pmd_offset(d, addr); ++ if (pmd_none(*m) || pmd_bad(*m)) { ++ *pgmiddle = NULL; ++ *pte = NULL; ++ return 0; ++ } else { ++ *pgmiddle = m; ++ } ++ ++ t = pte_offset(m, addr); ++ if (pte_none(*t)) { ++ *pte = NULL; ++ return 0; ++ } else { ++ *pte = t; ++ } ++ kdb_printf("\naddr=%08lx, pgd=%08lx, pmd=%08lx, pte=%08lx\n", ++ addr, ++ (unsigned long) pgd_val(*d), ++ (unsigned long) pmd_val(*m), ++ (unsigned long) pte_val(*t)); ++ return 0; ++} ++#endif ++ ++#define FORMAT_PGDIR(entry) \ ++ kdb_printf("frame=%05lx %c %s %c %c %c %s %c %s %s \n",\ ++ (entry >> PAGE_SHIFT), \ ++ (entry & _PAGE_PRESENT)?'p':'n', \ ++ (entry & _PAGE_RW)?"rw":"ro", \ ++ (entry & _PAGE_USER)?'u':'s', \ ++ (entry & _PAGE_ACCESSED)?'a':' ', \ ++ ' ', \ ++ (entry & _PAGE_PSE)?"4M":"4K", \ ++ (entry & _PAGE_GLOBAL)?'g':' ', \ ++ (entry & _PAGE_PWT)?"wt":"wb", \ ++ (entry & _PAGE_PCD)?"cd":" "); ++ ++#define FORMAT_PTE(p, entry) \ ++ kdb_printf("frame=%05lx %c%c%c %c %c %c %s %c %s %s\n", \ ++ (entry >> PAGE_SHIFT), \ ++ (pte_read(p))? 'r':'-', \ ++ (pte_write(p))? 'w':'-', \ ++ (pte_exec(p))? 'x':'-', \ ++ (pte_dirty(p))? 'd':' ', \ ++ (pte_young(p))? 'a':' ', \ ++ (entry & _PAGE_USER)? 'u':'s', \ ++ " ", \ ++ (entry & _PAGE_GLOBAL)? 'g':' ', \ ++ (entry & _PAGE_PWT)? "wt":"wb", \ ++ (entry & _PAGE_PCD)? "cd":" "); ++#if 0 ++static int ++display_pgdir(unsigned long addr, pgd_t *pgdir, int count) ++{ ++ unsigned long entry; ++ int i; ++ int index = pgdir - ((pgd_t *)(((unsigned long)pgdir) & PAGE_MASK)); ++ ++ count = min(count, PTRS_PER_PGD - index); ++ addr &= ~(PGDIR_SIZE-1); ++ ++ for (i = 0; i < count; i++, pgdir++) { ++ entry = pgd_val(*pgdir); ++ kdb_printf("pgd: addr=%08lx ", addr); ++ if (pgd_none(*pgdir)) { ++ kdb_printf("pgdir not present\n"); ++ } else { ++ FORMAT_PGDIR(entry); ++ } ++ addr += PGDIR_SIZE; ++ } ++ return i; ++} ++#endif ++ ++#if 0 /* for now, let's not print pgmiddle. */ ++static int ++display_pgmiddle(unsigned long addr, pmd_t *pgmiddle, int count) ++{ ++ unsigned long entry; ++ int i; ++ int index = pgmiddle - ((pmd_t *)(((unsigned long)pgmiddle) & PAGE_MASK)); ++ ++ count = min(count, PTRS_PER_PMD - index); ++ addr &= ~(PMD_SIZE-1); ++ ++ for (i = 0; i < count; i++, pgmiddle++) { ++ entry = pmd_val(*pgmiddle); ++ kdb_printf("pmd: addr=%08lx ", addr); ++ if (pmd_none(*pgmiddle)) { ++ kdb_printf("pgmiddle not present\n"); ++ } else { ++ FORMAT_PGDIR(entry); ++ } ++ addr += PMD_SIZE; ++ } ++ return i; ++} ++#endif ++ ++#if 0 ++static int ++display_pte(unsigned long addr, pte_t *pte, int count) ++{ ++ unsigned long entry; ++ int i; ++ int index = pte - ((pte_t *)(((unsigned long)pte) & PAGE_MASK)); ++ ++ count = min(count, PTRS_PER_PTE - index); ++ addr &= PAGE_MASK; ++ ++ for (i = 0; i < count; i++, pte++) { ++ entry = pte_val(*pte); ++ kdb_printf("pte: addr=%08lx ", addr); ++ if (pte_none(*pte)) { ++ kdb_printf("pte not present\n"); ++ } else if (!pte_present(*pte)) { ++ kdb_printf("page swapped out. swp_offset=%08lx ", SWP_OFFSET(pte_to_swp_entry(*pte))); ++ kdb_printf("swp_type=%8lx", SWP_TYPE(pte_to_swp_entry(*pte))); ++ } else { ++ FORMAT_PTE(*pte, entry); ++ } ++ addr += PAGE_SIZE; ++ } ++ return i; ++} ++ ++ ++/* ++ * kdb_pte ++ * ++ * This function implements the 'pte' command. ++ * ++ * pte [] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++static int ++kdb_pte(int argc, const char **argv) ++{ ++ static unsigned long last_addr = 0, last_count = 0; ++ int count = 8; ++ unsigned long addr; ++ long offset = 0; ++ pgd_t *pgdir; ++ pmd_t *pgmiddle; ++ pte_t *pte; ++ ++#ifdef CONFIG_X86_PAE ++ kdb_printf("This kernel is compiled with PAE support."); ++ return KDB_NOTIMP; ++#endif ++ kdbgetintenv("MDCOUNT", &count); ++ ++ if (argc == 0) { ++ if (last_addr == 0) ++ return KDB_ARGCOUNT; ++ addr = last_addr; ++ if (last_count) ++ count = last_count; ++ } else { ++ kdb_machreg_t val; ++ int diag, nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ if (argc > nextarg+1) ++ return KDB_ARGCOUNT; ++ ++ if (argc >= nextarg) { ++ diag = kdbgetularg(argv[nextarg], &val); ++ if (!diag) { ++ count = (int) val; ++ last_count = count; ++ } else if (last_count) { ++ count = last_count; ++ } ++ } ++ } ++ ++ /* ++ * round off the addr to a page boundary. ++ */ ++ addr &= PAGE_MASK; ++ ++ get_pagetables(addr, &pgdir, &pgmiddle, &pte); ++ ++ if (pgdir) ++ display_pgdir(addr, pgdir, 1); ++#if 0 /* for now, let's not print pgmiddle. */ ++ if (pgmiddle) ++ display_pgmiddle(addr, pgmiddle, 1); ++#endif ++ if (pte) { ++ int displayed; ++ displayed = display_pte(addr, pte, count); ++ addr += (displayed << PAGE_SHIFT); ++ } ++ last_addr = addr; ++ return 0; ++} ++#else ++/* ++ * Todo - In 2.5 the pte_offset macro in asm/pgtable.h seems to be ++ * renamed to pte_offset_kernel. ++ */ ++static int ++kdb_pte(int argc, const char **argv) ++{ ++ kdb_printf("not supported."); ++ return KDB_NOTIMP; ++} ++#endif ++ ++/* ++ * kdb_rdv ++ * ++ * This function implements the 'rdv' command. ++ * It displays all registers of the current processor ++ * included control registers in verbose mode. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * This should have been an option to rd command say "rd v", ++ * but it is here as it is a non-essential x86-only command, ++ * that need not clutter arch/i386/kdb/kdbasupport.c. ++ */ ++static int ++kdb_rdv(int argc, const char **argv) ++{ ++ struct pt_regs *regs = get_irq_regs(); ++ kdba_dumpregs(regs, NULL, NULL); ++ kdb_printf("\n"); ++ display_eflags(regs->flags); ++ kdb_printf("\n"); ++ display_gdtr(); ++ display_idtr(); ++ display_ldtr(); ++ kdb_printf("\n"); ++ display_cr0(); ++ display_cr3(); ++ display_cr4(); ++ display_cr8(); ++ kdb_printf("\n"); ++ display_dr(); ++ return 0; ++} ++ ++static int ++kdb_rdmsr(int argc, const char **argv) ++{ ++ unsigned long addr; ++ uint32_t l, h; ++ int diag; ++ struct cpuinfo_x86 *c = &cpu_data(smp_processor_id()); ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ if ((diag = kdbgetularg(argv[1], &addr))) ++ return diag; ++ ++ if (!cpu_has(c, X86_FEATURE_MSR)) ++ return KDB_NOTIMP; ++ ++ kdb_printf("msr(0x%lx) = ", addr); ++ if ((diag = rdmsr_safe(addr, &l, &h))) { ++ kdb_printf("error %d\n", diag); ++ return KDB_BADINT; ++ } else { ++ kdb_printf("0x%08x_%08x\n", h, l); ++ } ++ ++ return 0; ++} ++ ++static int ++kdb_wrmsr(int argc, const char **argv) ++{ ++ unsigned long addr; ++ unsigned long l, h; ++ int diag; ++ struct cpuinfo_x86 *c = &cpu_data(smp_processor_id()); ++ ++ if (argc != 3) ++ return KDB_ARGCOUNT; ++ ++ if ((diag = kdbgetularg(argv[1], &addr)) ++ || (diag = kdbgetularg(argv[2], &h)) ++ || (diag = kdbgetularg(argv[3], &l))) ++ return diag; ++ ++ if (!cpu_has(c, X86_FEATURE_MSR)) ++ return KDB_NOTIMP; ++ ++ if ((diag = wrmsr_safe(addr, l, h))) { ++ kdb_printf("error %d\n", diag); ++ return KDB_BADINT; ++ } ++ ++ return 0; ++} ++ ++static int __init kdbm_x86_init(void) ++{ ++ kdb_register("rdv", kdb_rdv, NULL, "Display registers in verbose mode", 0); ++ kdb_register_repeat("gdt", kdb_gdt, " []", "Display GDT", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("idt", kdb_idt, " []", "Display IDT", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("ldt", kdb_ldt, " []", "Display LDT", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("ptex", kdb_pte, " []", "Display pagetables", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register_repeat("ldtp", kdb_ldt, " []", "Display Process LDT", 0, KDB_REPEAT_NO_ARGS); ++ kdb_register("rdmsr", kdb_rdmsr, "", "Display Model Specific Register", 0); ++ kdb_register("wrmsr", kdb_wrmsr, " ", "Modify Model Specific Register", 0); ++ return 0; ++} ++ ++static void __exit kdbm_x86_exit(void) ++{ ++ kdb_unregister("rdv"); ++ kdb_unregister("gdt"); ++ kdb_unregister("ldt"); ++ kdb_unregister("idt"); ++ kdb_unregister("ptex"); ++ kdb_unregister("ldtp"); ++ kdb_unregister("rdmsr"); ++ kdb_unregister("wrmsr"); ++} ++ ++module_init(kdbm_x86_init) ++module_exit(kdbm_x86_exit) +--- /dev/null ++++ b/kdb/modules/lcrash/README +@@ -0,0 +1,3 @@ ++ ++ These files are copied from lcrash. ++ The only changes are flagged with "cpw". +--- /dev/null ++++ b/kdb/modules/lcrash/asm/README +@@ -0,0 +1 @@ ++This kl_types.h is asm-ia64 version. +--- /dev/null ++++ b/kdb/modules/lcrash/asm/kl_dump_ia64.h +@@ -0,0 +1,199 @@ ++/* ++ * $Id: kl_dump_ia64.h 1151 2005-02-23 01:09:12Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++/* This header file holds the architecture specific crash dump header */ ++#ifndef __KL_DUMP_IA64_H ++#define __KL_DUMP_IA64_H ++ ++/* definitions */ ++#ifndef KL_NR_CPUS ++# define KL_NR_CPUS 128 /* max number CPUs */ ++#endif ++ ++#define KL_DUMP_MAGIC_NUMBER_IA64 0xdeaddeadULL /* magic number */ ++#define KL_DUMP_VERSION_NUMBER_IA64 0x4 /* version number */ ++ ++ ++/* ++ * mkswap.c calls getpagesize() to get the system page size, ++ * which is not necessarily the same as the hardware page size. ++ * ++ * For ia64 the kernel PAGE_SIZE can be configured from 4KB ... 16KB. ++ * ++ * The physical memory is layed out out in the hardware/minimal pages. ++ * This is the size we need to use for dumping physical pages. ++ * ++ * Note ths hardware/minimal page size being use in; ++ * arch/ia64/kernel/efi.c`efi_memmap_walk(): ++ * curr.end = curr.start + (md->num_pages << 12); ++ * ++ * Since the system page size could change between the kernel we boot ++ * on the the kernel that cause the core dume we may want to have something ++ * more constant like the maximum system page size (See include/asm-ia64/page.h). ++ */ ++#define DUMP_MIN_PAGE_SHIFT 12 ++#define DUMP_MIN_PAGE_SIZE (1UL << DUMP_MIN_PAGE_SHIFT) ++#define DUMP_MIN_PAGE_MASK (~(DUMP_MIN_PAGE_SIZE - 1)) ++#define DUMP_MIN_PAGE_ALIGN(addr) (((addr) + DUMP_MIN_PAGE_SIZE - 1) & DUMP_MIN_PAGE_MASK) ++ ++#define DUMP_MAX_PAGE_SHIFT 16 ++#define DUMP_MAX_PAGE_SIZE (1UL << DUMP_MAX_PAGE_SHIFT) ++#define DUMP_MAX_PAGE_MASK (~(DUMP_MAX_PAGE_SIZE - 1)) ++#define DUMP_MAX_PAGE_ALIGN(addr) (((addr) + DUMP_MAX_PAGE_SIZE - 1) & DUMP_MAX_PAGE_MASK) ++ ++#define DUMP_HEADER_OFFSET DUMP_MAX_PAGE_SIZE ++ ++#define DUMP_EF_PAGE_SHIFT DUMP_MIN_PAGE_SHIFT ++ ++#define DUMP_PAGE_SHIFT DUMP_MIN_PAGE_SHIFT ++#define DUMP_PAGE_SIZE DUMP_MIN_PAGE_SIZE ++#define DUMP_PAGE_MASK DUMP_MIN_PAGE_MASK ++#define DUMP_PAGE_ALIGN(addr) DUMP_MIN_PAGE_ALIGN(addr) ++ ++struct kl_ia64_fpreg { ++ union { ++ unsigned long bits[2]; ++ long double __dummy; /* force 16-byte alignment */ ++ } u; ++}; ++ ++struct kl_pt_regs_ia64 { ++ /* for 2.6 kernels only. This structure was totally different in 2.4 kernels */ ++ unsigned long b6; /* scratch */ ++ unsigned long b7; /* scratch */ ++ ++ unsigned long ar_csd; /* used by cmp8xchg16 (scratch) */ ++ unsigned long ar_ssd; /* reserved for future use (scratch) */ ++ ++ unsigned long r8; /* scratch (return value register 0) */ ++ unsigned long r9; /* scratch (return value register 1) */ ++ unsigned long r10; /* scratch (return value register 2) */ ++ unsigned long r11; /* scratch (return value register 3) */ ++ ++ unsigned long cr_ipsr; /* interrupted task's psr */ ++ unsigned long cr_iip; /* interrupted task's instruction pointer */ ++ unsigned long cr_ifs; /* interrupted task's function state */ ++ ++ unsigned long ar_unat; /* interrupted task's NaT register (preserved) */ ++ unsigned long ar_pfs; /* prev function state */ ++ unsigned long ar_rsc; /* RSE configuration */ ++ /* The following two are valid only if cr_ipsr.cpl > 0: */ ++ unsigned long ar_rnat; /* RSE NaT */ ++ unsigned long ar_bspstore; /* RSE bspstore */ ++ ++ unsigned long pr; /* 64 predicate registers (1 bit each) */ ++ unsigned long b0; /* return pointer (bp) */ ++ unsigned long loadrs; /* size of dirty partition << 16 */ ++ ++ unsigned long r1; /* the gp pointer */ ++ unsigned long r12; /* interrupted task's memory stack pointer */ ++ unsigned long r13; /* thread pointer */ ++ ++ unsigned long ar_fpsr; /* floating point status (preserved) */ ++ unsigned long r15; /* scratch */ ++ ++ /* The remaining registers are NOT saved for system calls. */ ++ ++ unsigned long r14; /* scratch */ ++ unsigned long r2; /* scratch */ ++ unsigned long r3; /* scratch */ ++ ++ /* The following registers are saved by SAVE_REST: */ ++ unsigned long r16; /* scratch */ ++ unsigned long r17; /* scratch */ ++ unsigned long r18; /* scratch */ ++ unsigned long r19; /* scratch */ ++ unsigned long r20; /* scratch */ ++ unsigned long r21; /* scratch */ ++ unsigned long r22; /* scratch */ ++ unsigned long r23; /* scratch */ ++ unsigned long r24; /* scratch */ ++ unsigned long r25; /* scratch */ ++ unsigned long r26; /* scratch */ ++ unsigned long r27; /* scratch */ ++ unsigned long r28; /* scratch */ ++ unsigned long r29; /* scratch */ ++ unsigned long r30; /* scratch */ ++ unsigned long r31; /* scratch */ ++ ++ unsigned long ar_ccv; /* compare/exchange value (scratch) */ ++ ++ /* ++ * * Floating point registers that the kernel considers scratch: ++ * */ ++ struct kl_ia64_fpreg f6; /* scratch */ ++ struct kl_ia64_fpreg f7; /* scratch */ ++ struct kl_ia64_fpreg f8; /* scratch */ ++ struct kl_ia64_fpreg f9; /* scratch */ ++ struct kl_ia64_fpreg f10; /* scratch */ ++ struct kl_ia64_fpreg f11; /* scratch */ ++} __attribute__((packed)); ++ ++/* ++ * Structure: dump_header_asm_t ++ * Function: This is the header for architecture-specific stuff. It ++ * follows right after the dump header. ++ */ ++typedef struct kl_dump_header_ia64_s { ++ /* the dump magic number -- unique to verify dump is valid */ ++ uint64_t magic_number; ++ /* the version number of this dump */ ++ uint32_t version; ++ /* the size of this header (in case we can't read it) */ ++ uint32_t header_size; ++ /* pointer to pt_regs */ ++ uint64_t pt_regs; ++ /* the dump registers */ ++ struct kl_pt_regs_ia64 regs; ++ /* the rnat register saved after flushrs */ ++ uint64_t rnat; ++ /* the pfs register saved after flushrs */ ++ uint64_t pfs; ++ /* the bspstore register saved after flushrs */ ++ uint64_t bspstore; ++ ++ /* smp specific */ ++ uint32_t smp_num_cpus; ++ uint32_t dumping_cpu; ++ struct kl_pt_regs_ia64 smp_regs[KL_NR_CPUS]; ++ uint64_t smp_current_task[KL_NR_CPUS]; ++ uint64_t stack[KL_NR_CPUS]; ++} __attribute__((packed)) kl_dump_header_ia64_t; ++ ++/* The following struct is used just to calculate the size needed ++ * to store per CPU info. (Make sure it is sync with the above struct) ++ */ ++struct kl_dump_CPU_info_ia64 { ++ struct kl_pt_regs_ia64 smp_regs; ++ uint64_t smp_current_task; ++ uint64_t stack; ++} __attribute__((packed)); ++ ++/* function declarations ++ */ ++int kl_set_dumparch_ia64(void); ++uint32_t dha_num_cpus_ia64(void); ++kaddr_t dha_current_task_ia64(int cpuid); ++int dha_cpuid_ia64(kaddr_t); ++kaddr_t dha_stack_ia64(int); ++kaddr_t dha_stack_ptr_ia64(int); ++int kl_read_dump_header_ia64(void); ++ ++#endif /* __KL_DUMP_IA64_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/asm/kl_types.h +@@ -0,0 +1,48 @@ ++/* ++ * $Id: kl_types.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __ASMIA64_KL_TYPES_H ++#define __ASMIA64_KL_TYPES_H ++ ++/* cpw */ ++/* was #include */ ++#include "kl_dump_ia64.h" ++ ++#define HOST_ARCH_IA64 ++/* cpw: add this, as otherwise comes from makefile */ ++#define DUMP_ARCH_IA64 ++ ++/* Format string that allows a single fprintf() call to work for both ++ * 32-bit and 64-bit pointer values (architecture specific). ++ */ ++#ifdef CONFIG_X86_32 ++#define FMT64 "ll" ++#else ++#define FMT64 "l" ++#endif ++#define FMTPTR "l" ++ ++/* for usage in common code where host architecture ++ * specific type/macro is needed ++ */ ++typedef kl_dump_header_ia64_t kl_dump_header_asm_t; ++#define KL_DUMP_ASM_MAGIC_NUMBER KL_DUMP_MAGIC_NUMBER_IA64 ++ ++#endif /* __ASMIA64_KL_TYPES_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_alloc.h +@@ -0,0 +1,124 @@ ++/* ++ * $Id: kl_alloc.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libutil. ++ * A library which provides auxiliary functions. ++ * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_ALLOC_H ++#define __KL_ALLOC_H ++ ++/** ++ ** Header file for kl_alloc.c module ++ ** ++ **/ ++ ++#define K_TEMP 1 ++#define K_PERM 2 ++ ++/** function prototypes for register functions ++ **/ ++ ++/* Memory block allocator. Returns a pointer to an allocated block ++ * of size bytes. In case of error, a NULL pointer will be returned ++ * and errno will be set to indicate exactly what error occurred. ++ * Note that the flag value will determine if the block allocated is ++ * temporary (can be freed via a call to kl_free_temp_blks()) or ++ * permenant (must be freed with a call to kl_free_block()).. ++ */ ++typedef void * (*klib_block_alloc_func) ( ++ int /* size of block required */, ++ int /* flag value */, ++ void * /* return address */); ++ ++/* Memory block reallocator. Returns a pointer to a block of new_size ++ * bytes. In case of error, a NULL pointer will be returned and ++ * errno will be set to indicate exactly what error occurred. ++ * Note that the flag value will determine if the block allocated is ++ * temporary (can be free via a call to kl_free_temp_blks()) or ++ * permenant. ++ */ ++typedef void * (*klib_block_realloc_func) ( ++ void * /* pointer to block to realloc */, ++ int /* size of new block required */, ++ int /* flag value */, ++ void * /* return address */); ++ ++/* Memory block duplicator. Returns a pointer to a block that is ++ * a copy of the block passed in via pointer. In case of error, a ++ * NULL pointer will be returned and errno will be set to indicate ++ * exactly what error occurred. Note that the flag value will ++ * determine if the block allocated is temporary (will be freed ++ * via a call to kl_free_temp_blks()) or permenant. Note that this ++ * function is only supported when liballoc is used (there is no ++ * way to tell the size of a malloced block. ++ */ ++typedef void * (*klib_block_dup_func) ( ++ void * /* pointer to block to dup */, ++ int /* flag value */, ++ void * /* return address */); ++ ++/* Allocates a block large enough to hold a string (plus the terminating ++ * NULL character). ++ */ ++typedef void * (*klib_str_to_block_func) ( ++ char * /* pointer to character string */, ++ int /* flag value */, ++ void * /* return address */); ++ ++/* Frees blocks that were previously allocated. ++ */ ++typedef void (*klib_block_free_func) ( ++ void * /* pointer to block */); ++ ++/* alloc block wrapper function table structure ++ */ ++typedef struct alloc_functions_s { ++ int flag; /* Functions initialized? */ ++ klib_block_alloc_func block_alloc; /* Returns ptr to block */ ++ klib_block_realloc_func block_realloc; /* Returns ptr to new blk */ ++ klib_block_dup_func block_dup; /* Returns ptr to new blk */ ++ klib_str_to_block_func str_to_block; /* Returns ptr to new blk */ ++ klib_block_free_func block_free; /* Frees memory block */ ++} alloc_functions_t; ++ ++extern alloc_functions_t alloc_functions; ++ ++/* Macros for accessing functions in alloc_functions table ++ */ ++#define KL_BLOCK_ALLOC() (alloc_functions.block_alloc) ++#define KL_BLOCK_REALLOC() (alloc_functions.block_realloc) ++#define KL_BLOCK_DUP() (alloc_functions.block_dup) ++#define KL_STR_TO_BLOCK() (alloc_functions.str_to_block) ++#define KL_BLOCK_FREE() (alloc_functions.block_free) ++ ++void *_kl_alloc_block(int, int, void *); ++void *_kl_realloc_block(void *, int, int, void *); ++void *_kl_dup_block(void *, int, void *); ++void *_kl_str_to_block(char *, int, void *); ++#if 0 ++cpw: we create a new wrappers for these: ++void kl_free_block(void *); ++ ++#define kl_alloc_block(size, flags) _kl_alloc_block(size, flags, kl_get_ra()) ++#endif ++#define kl_realloc_block(b, new_size, flags) \ ++ _kl_realloc_block(b, new_size, flags, kl_get_ra()) ++#define kl_dup_block(b, flags) _kl_dup_block(b, flags, kl_get_ra()) ++#define kl_str_to_block(s, flags) _kl_str_to_block(s, flags, kl_get_ra()) ++ ++#endif /* __KL_ALLOC_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_bfd.h +@@ -0,0 +1,31 @@ ++/* ++ * $Id: kl_bfd.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_BFD_H ++#define __KL_BFD_H ++ ++/* cpw: " " form: */ ++#include "klib.h" ++ ++int kl_check_bfd_error(bfd_error_type); ++int kl_open_elf(char*, bfd**, bfd**); ++int kl_read_bfd_syminfo(maplist_t*); ++ ++#endif /* __KL_BFD_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_btnode.h +@@ -0,0 +1,95 @@ ++/* ++ * $Id: kl_btnode.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libutil. ++ * A library which provides auxiliary functions. ++ * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_BTNODE_H ++#define __KL_BTNODE_H ++ ++/* ++ * Node header struct for use in binary search tree routines ++ */ ++typedef struct btnode_s { ++ struct btnode_s *bt_left; ++ struct btnode_s *bt_right; ++ struct btnode_s *bt_parent; ++ char *bt_key; ++ int bt_height; ++} btnode_t; ++ ++#define DUPLICATES_OK 1 ++ ++/** ++ ** btnode operation function prototypes ++ **/ ++ ++/* Return the hight of a given btnode_s struct in a tree. In the ++ * event of an error (a NULL btnode_s pointer was passed in), a ++ * value of -1 will be returned. ++ */ ++int kl_btnode_height( ++ btnode_t* /* pointer to btnode_s struct */); ++ ++/* Insert a btnode_s struct into a tree. After the insertion, the ++ * tree will be left in a reasonibly ballanced state. Note that, if ++ * the DUPLICATES_OK flag is set, duplicate keys will be inserted ++ * into the tree (otherwise return an error). In the event of an ++ * error, a value of -1 will be returned. ++ */ ++int kl_insert_btnode( ++ btnode_t** /* pointer to root of tree */, ++ btnode_t* /* pointer to btnode_s struct to insert */, ++ int /* flags (DUPLICATES_OK) */); ++ ++/* Finds a btnode in a tree and removes it, making sure to keep ++ * the tree in a reasonably balanced state. As part of the ++ * delete_btnode() operation, a call will be made to the free ++ * function (passed in as a parameter) to free any application ++ * specific data. ++ */ ++int kl_delete_btnode( ++ btnode_t** /* pointer to the root of the btree */, ++ btnode_t* /* pointer to btnode_s struct to delete */, ++ void(*)(void*) /* pointer to function to actually free the node */, ++ int /* flags */); ++ ++/* Traverse a tree looking for a particular key. In the event that ++ * duplicate keys are allowed in the tree, returns the first occurance ++ * of the search key found. A pointer to an int should be passed in ++ * to hold the maximum depth reached in the search. Upon success, ++ * returns a pointer to a btnode_s struct. Otherwise, a NULL pointer ++ * will be returned. ++ */ ++btnode_t *_kl_find_btnode( ++ btnode_t* /* pointer to btnode_s struct to start search with */, ++ char* /* key we are looking for */, ++ int* /* pointer to where max depth vlaue will be placed */, ++ size_t /* if nonzero compare only first n chars of key */); ++#define kl_find_btnode(A, B, C) _kl_find_btnode(A, B, C, 0) ++ ++btnode_t *kl_first_btnode( ++ btnode_t * /* pointer to any btnode in a btree */); ++ ++btnode_t *kl_next_btnode( ++ btnode_t * /* pointer to current btnode */); ++ ++btnode_t *kl_prev_btnode( ++ btnode_t * /* Pointer to current btnode */); ++ ++#endif /* __KL_BTNODE_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_cmp.h +@@ -0,0 +1,102 @@ ++/* ++ * $Id: kl_cmp.h 1216 2005-07-06 10:03:13Z holzheu $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_CMP_H ++#define __KL_CMP_H ++ ++#define DUMP_INDEX_MAGIC 0xdeadbeef ++#define DUMP_INDEX_VERSION 31900 ++#define NUM_BUCKETS 65535 ++ ++/* ++ * Definitions for compressed cached reads. I've recently lowered ++ * these ... If they need to be increased later, I'll do so. ++ */ ++#define CMP_HIGH_WATER_MARK 25 ++#define CMP_LOW_WATER_MARK 10 ++ ++#define CMP_VM_CACHED 0x01 ++#define CMP_VM_UNCACHED 0x02 ++ ++ ++/* ++ * This structure defines a page table entry, what each value will ++ * contain. Since these can be cached or uncached, we have a flags ++ * variable to specify this. ++ */ ++typedef struct _ptableentry { ++ int flags; /* flags for page in cache */ ++ int length; /* length of page */ ++ int cached; /* cached (1 = yes, cached) */ ++ kaddr_t addr; /* addr of page */ ++ char *data; /* data in page */ ++ struct _ptableentry *next; /* ptr to next dump page */ ++ struct _ptableentry *prev; /* ptr to prev dump page */ ++ struct _ptableentry *nextcache; /* ptr to next cached page */ ++ struct _ptableentry *prevcache; /* ptr to prev cached page */ ++} ptableentry; ++ ++/* ++ * This is for the page table index from the compressed core dump. ++ * This is separate from the page table entries because these are ++ * simply addresses off of the compressed core dump, and not the ++ * actual data from the core dump. If we hash these values, we gain ++ * a lot of performance because we only have 1 to search for the ++ * page data, 1 to search for the index, and return if both searches ++ * failed. ++ */ ++typedef struct _ptableindex { ++ kl_dump_page_t dir; /* directory entry of page */ ++ kaddr_t addr; /* address of page offset */ ++ kaddr_t coreaddr; /* address of page in core */ ++ unsigned int hash; /* hash value for this index item */ ++ struct _ptableindex *next; /* next pointer */ ++} ptableindex; ++ ++typedef struct dump_index_s { ++ unsigned int magic_number; /* dump index magic number */ ++ unsigned int version_number; /* dump index version number */ ++ /* struct timeval depends on machine, use two long values here */ ++ struct {uint64_t tv_sec; ++ uint64_t tv_usec; ++ } timebuf; /* the time of the dump */ ++} __attribute__((packed)) dump_index_t; ++ ++/* Compression function */ ++typedef int (*kl_compress_fn_t)(const unsigned char *old, uint32_t old_size, unsigned char *new, uint32_t size); ++ ++/* function declarations ++ */ ++int kl_cmpreadmem(int, kaddr_t, char*, unsigned int, unsigned int); ++int kl_cmpinit( ++ int /* fd */, ++ char * /* indexname */, ++ int /* flags */); ++ ++/* Compression routine: No compression */ ++int kl_compress_none(const char *old, uint32_t old_size, char *new, uint32_t new_size); ++ ++/* Compression routine: Run length encoding */ ++int kl_compress_rle(const char *old, uint32_t old_size, char *new, uint32_t new_size); ++ ++/* Compression routine: GZIP */ ++int kl_compress_gzip(const unsigned char *old, uint32_t old_size, unsigned char *new, uint32_t new_size); ++ ++#endif /* __KL_CMP_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_copt.h +@@ -0,0 +1,29 @@ ++/* ++ * $Id: kl_copt.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libutil. ++ * A library which provides auxiliary functions. ++ * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * ++ * Copyright (C) 2003, 2004 Silicon Graphics, Inc. All rights reserved. ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++#ifndef __KL_COPT_H ++#define __KL_COPT_H ++ ++extern int copt_ind; ++extern char *copt_arg; ++extern int copt_error; ++ ++void reset_copt(void); ++int is_copt(char *); ++int get_copt(int, char **, const char *, char **); ++ ++#endif /* __KL_COPT_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_debug.h +@@ -0,0 +1,168 @@ ++/* ++ * $Id: kl_debug.h 1196 2005-05-17 18:34:12Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_DEBUG_H ++#define __KL_DEBUG_H ++ ++/* generic functions for reading kerntypes in stabs and dwarf2 formats */ ++ ++#define DBG_NONE 0 ++#define DBG_STABS 1 ++#define DBG_DWARF2 2 ++ ++extern int debug_format; ++ ++#define TYPE_NUM(X) ((uint64_t)(X) & 0xffffffff) ++#define SRC_FILE(X) (((uint64_t)(X) >> 48) & 0xfff) ++#define TYPE_NUM_SLOTS (255) ++#define TYPE_NUM_HASH(X) \ ++ (((SRC_FILE(X)<<1)+TYPE_NUM(X)) % (TYPE_NUM_SLOTS - 1)) ++ ++typedef struct dbg_type_s { ++ kltype_t st_klt; /* must be first */ ++ ++ int st_bit_offset; /* from start of struct/union */ ++ uint64_t st_type_num; /* DBG type_num */ ++ uint64_t st_real_type; /* real type type_num */ ++ uint64_t st_index_type; /* type_num of array index */ ++ uint64_t st_element_type; /* type_num of array element */ ++} dbg_type_t; ++ ++#define st_name st_klt.kl_name ++#define st_type st_klt.kl_type ++#define st_ptr st_klt.kl_ptr ++#define st_flags st_klt.kl_flags ++#define st_typestr st_klt.kl_typestr ++#define st_size st_klt.kl_size ++#define st_offset st_klt.kl_offset ++#define st_low_bounds st_klt.kl_low_bounds ++#define st_high_bounds st_klt.kl_high_bounds ++#define st_value st_klt.kl_value ++#define st_bit_size st_klt.kl_bit_size ++#define st_next st_klt.kl_next ++#define st_member st_klt.kl_member ++#define st_realtype st_klt.kl_realtype ++#define st_indextype st_klt.kl_indextype ++#define st_elementtype st_klt.kl_elementtype ++#define st_encoding st_klt.kl_encoding ++ ++/* Structure containing information about a symbol entry ++ */ ++/* this must match the definition in lkcd's libklib/include/kl_debug.h */ ++typedef struct dbg_sym_s { ++ btnode_t sym_bt; /* must be first */ ++ short sym_dbgtyp; /* STABS, DWARF2, ... */ ++ short sym_state; /* current state */ ++ short sym_flag; /* current flag value */ ++ short sym_type; /* symbol type */ ++ short sym_pvttype; /* private type */ ++ short sym_nmlist; /* namelist index */ ++ short sym_srcfile; /* source file index */ ++ short sym_incfile; /* include file index */ ++ int sym_num; /* symbol number */ ++ int sym_off; /* symbol table offset */ ++ int sym_stroff; /* symbol offset in string table */ ++ uint64_t sym_typenum; /* arbitrary type number */ ++ kltype_t *sym_kltype; /* Full type information */ ++ struct dbg_sym_s *sym_next; /* next pointer for chaining */ ++ struct dbg_sym_s *sym_link; /* another pointer for chaining */ ++ int sym_dup; /* duplicate symbol */ ++} dbg_sym_t; ++#define sym_name sym_bt.bt_key ++ ++extern dbg_sym_t *type_tree; ++extern dbg_sym_t *typedef_tree; ++extern dbg_sym_t *func_tree; ++extern dbg_sym_t *srcfile_tree; ++extern dbg_sym_t *var_tree; ++extern dbg_sym_t *xtype_tree; ++extern dbg_sym_t *symlist; ++extern dbg_sym_t *symlist_end; ++ ++/* State flags ++ */ ++#define DBG_SETUP 0x1 ++#define DBG_SETUP_DONE 0x2 ++#define DBG_SETUP_FAILED 0x4 ++ ++/* Flags for identifying individual symbol types ++ */ ++#define DBG_SRCFILE 0x0001 ++#define DBG_TYPE 0x0002 ++#define DBG_TYPEDEF 0x0004 ++#define DBG_FUNC 0x0008 ++#define DBG_PARAM 0x0010 ++#define DBG_LINE 0x0020 ++#define DBG_VAR 0x0040 ++#define DBG_XTYPE 0x0100 ++#define DBG_ALL 0xffff ++ ++/* Structure for cross referencing one type number to another ++ */ ++typedef struct dbg_hashrec_s { ++ uint64_t h_typenum; /* type number */ ++ dbg_sym_t *h_ptr; /* pointer to actual type */ ++ struct dbg_hashrec_s *h_next; /* next pointer (for hashing) */ ++} dbg_hashrec_t; ++ ++extern dbg_hashrec_t *dbg_hash[]; ++ ++#define HASH_SYM 1 ++#define HASH_XREF 2 ++ ++/* DBG function prototypes ++ */ ++dbg_sym_t *dbg_alloc_sym( ++ int /* format */); ++ ++void dbg_free_sym( ++ dbg_sym_t * /* dbg_sym_s pointer */); ++ ++int dbg_setup_typeinfo( ++ dbg_sym_t * /* dbg_sym_s pointer */); ++ ++int dbg_insert_sym( ++ dbg_sym_t * /* dbg_sym_s pointer */); ++ ++void dbg_hash_sym( ++ uint64_t /* typenum */, ++ dbg_sym_t * /* dbg_sym_s pointer */); ++ ++dbg_type_t *dbg_walk_hash( ++ int * /* pointer to hash index */, ++ void ** /* pointer to hash record pointer */); ++ ++dbg_sym_t *dbg_find_sym( ++ char * /* name */, ++ int /* type number */, ++ uint64_t /* typenum */); ++ ++dbg_sym_t *dbg_first_sym( ++ int /* type number */); ++ ++dbg_sym_t *dbg_next_sym( ++ dbg_sym_t * /* dbg_sym_s pointer */); ++ ++dbg_sym_t *dbg_prev_sym( ++ dbg_sym_t * /* dbg_sym_s pointer */); ++ ++dbg_type_t *dbg_find_typenum( ++ uint64_t /* typenum */); ++ ++#endif /* __KL_DEBUG_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_dump.h +@@ -0,0 +1,511 @@ ++/* ++ * $Id: kl_dump.h 1336 2006-10-23 23:27:06Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_DUMP_H ++#define __KL_DUMP_H ++ ++#if 0 ++cpw: dont need: ++#include ++#include ++#endif ++ ++/* ++ * DUMP_DEBUG: a debug level for the kernel dump code and ++ * the supporting lkcd libraries in user space. ++ * ++ * 0: FALSE: No Debug Added ++ * 1: TRUE: Break Points ++ * . ++ * . ++ * . ++ * 6: Add Debug Data to Structures ++ * . ++ * . ++ * 9: Max ++ */ ++#define DUMP_DEBUG FALSE ++ ++#if DUMP_DEBUG ++void dump_bp(void); /* Called when something exceptional occures */ ++# define DUMP_BP() dump_bp() /* BreakPoint */ ++#else ++# define DUMP_BP() ++#endif ++ ++ ++#define KL_UTS_LEN 65 /* do not change ... */ ++ ++extern int SN2_24X; ++ ++/* ++ * Size of the buffer that's used to hold: ++ * ++ * 1. the dump header (paded to fill the complete buffer) ++ * 2. the possibly compressed page headers and data ++ */ ++extern uint64_t KL_DUMP_BUFFER_SIZE; ++extern uint64_t KL_DUMP_HEADER_SIZE; ++ ++#if 0 ++/* Variables that contain page size, mask etc. used in dump format ++ * (this is not the system page size stored in the dump header) ++ */ ++uint64_t KL_DUMP_PAGE_SIZE; ++uint64_t KL_DUMP_PAGE_MASK; ++uint64_t KL_DUMP_PAGE_SHIFT; ++#endif ++ ++/* Dump header offset changed from 4k to 64k to support multiple page sizes */ ++#define KL_DUMP_HEADER_OFFSET (1ULL << 16) ++ ++ ++/* header definitions for dumps from s390 standalone dump tools */ ++#define KL_DUMP_MAGIC_S390SA 0xa8190173618f23fdULL /* s390sa magic number */ ++#define KL_DUMP_HEADER_SZ_S390SA 4096 ++ ++/* standard header definitions */ ++#define KL_DUMP_MAGIC_NUMBER 0xa8190173618f23edULL /* dump magic number */ ++#define KL_DUMP_MAGIC_LIVE 0xa8190173618f23cdULL /* live magic number */ ++#define KL_DUMP_MAGIC_ASM 0xdeaddeadULL /* generic arch magic number */ ++#define KL_DUMP_VERSION_NUMBER 0x8 /* dump version number */ ++#define KL_DUMP_PANIC_LEN 0x100 /* dump panic string length */ ++ ++/* dump levels - type specific stuff added later -- add as necessary */ ++#define KL_DUMP_LEVEL_NONE 0x0 /* no dumping at all -- just bail */ ++#define KL_DUMP_LEVEL_HEADER 0x1 /* kernel dump header only */ ++#define KL_DUMP_LEVEL_KERN 0x2 /* dump header and kernel pages */ ++#define KL_DUMP_LEVEL_USED 0x4 /* dump header, kernel/user pages */ ++#define KL_DUMP_LEVEL_ALL_RAM 0x8 /* dump header, all RAM pages */ ++#define KL_DUMP_LEVEL_ALL 0x10 /* dump all memory RAM and firmware */ ++ ++/* dump compression options -- add as necessary */ ++#define KL_DUMP_COMPRESS_NONE 0x0 /* don't compress this dump */ ++#define KL_DUMP_COMPRESS_RLE 0x1 /* use RLE compression */ ++#define KL_DUMP_COMPRESS_GZIP 0x2 /* use GZIP compression */ ++ ++/* dump flags - any dump-type specific flags -- add as necessary */ ++#define KL_DUMP_FLAGS_NONE 0x0 /* no flags are set for this dump */ ++#define KL_DUMP_FLAGS_NONDISRUPT 0x1 /* try to keep running after dump */ ++#define KL_DUMP_FLAGS_DISKDUMP 0x80000000 /* dump to local disk */ ++#define KL_DUMP_FLAGS_NETDUMP 0x40000000 /* dump to network device */ ++ ++/* dump header flags -- add as necessary */ ++#define KL_DUMP_DH_FLAGS_NONE 0x0 /* no flags set (error condition!) */ ++#define KL_DUMP_DH_RAW 0x1 /* raw page (no compression) */ ++#define KL_DUMP_DH_COMPRESSED 0x2 /* page is compressed */ ++#define KL_DUMP_DH_END 0x4 /* end marker on a full dump */ ++#define KL_DUMP_DH_TRUNCATED 0x8 /* dump is incomplete */ ++#define KL_DUMP_DH_TEST_PATTERN 0x10 /* dump page is a test pattern */ ++#define KL_DUMP_DH_NOT_USED 0x20 /* 1st bit not used in flags */ ++ ++/* dump ioctl() control options */ ++#ifdef IOCTL26 ++#define DIOSDUMPDEV _IOW('p', 0xA0, unsigned int) /* set the dump device */ ++#define DIOGDUMPDEV _IOR('p', 0xA1, unsigned int) /* get the dump device */ ++#define DIOSDUMPLEVEL _IOW('p', 0xA2, unsigned int) /* set the dump level */ ++#define DIOGDUMPLEVEL _IOR('p', 0xA3, unsigned int) /* get the dump level */ ++#define DIOSDUMPFLAGS _IOW('p', 0xA4, unsigned int) /* set the dump flag parameters */ ++#define DIOGDUMPFLAGS _IOR('p', 0xA5, unsigned int) /* get the dump flag parameters */ ++#define DIOSDUMPCOMPRESS _IOW('p', 0xA6, unsigned int) /* set the dump compress level */ ++#define DIOGDUMPCOMPRESS _IOR('p', 0xA7, unsigned int) /* get the dump compress level */ ++ ++/* these ioctls are used only by netdump module */ ++#define DIOSTARGETIP _IOW('p', 0xA8, unsigned int) /* set the target m/c's ip */ ++#define DIOGTARGETIP _IOR('p', 0xA9, unsigned int) /* get the target m/c's ip */ ++#define DIOSTARGETPORT _IOW('p', 0xAA, unsigned int) /* set the target m/c's port */ ++#define DIOGTARGETPORT _IOR('p', 0xAB, unsigned int) /* get the target m/c's port */ ++#define DIOSSOURCEPORT _IOW('p', 0xAC, unsigned int) /* set the source m/c's port */ ++#define DIOGSOURCEPORT _IOR('p', 0xAD, unsigned int) /* get the source m/c's port */ ++#define DIOSETHADDR _IOW('p', 0xAE, unsigned int) /* set ethernet address */ ++#define DIOGETHADDR _IOR('p', 0xAF, unsigned int) /* get ethernet address */ ++#define DIOGDUMPOKAY _IOR('p', 0xB0, unsigned int) /* check if dump is configured */ ++#define DIOSDUMPTAKE _IOW('p', 0xB1, unsigned int) /* take a manual dump */ ++#else ++#define DIOSDUMPDEV 1 /* set the dump device */ ++#define DIOGDUMPDEV 2 /* get the dump device */ ++#define DIOSDUMPLEVEL 3 /* set the dump level */ ++#define DIOGDUMPLEVEL 4 /* get the dump level */ ++#define DIOSDUMPFLAGS 5 /* set the dump flag parameters */ ++#define DIOGDUMPFLAGS 6 /* get the dump flag parameters */ ++#define DIOSDUMPCOMPRESS 7 /* set the dump compress level */ ++#define DIOGDUMPCOMPRESS 8 /* get the dump compress level */ ++#define DIOSTARGETIP 9 /* set the target m/c's ip */ ++#define DIOGTARGETIP 10 /* get the target m/c's ip */ ++#define DIOSTARGETPORT 11 /* set the target m/c's port */ ++#define DIOGTARGETPORT 12 /* get the target m/c's port */ ++#define DIOSSOURCEPORT 13 /* set the source m/c's port */ ++#define DIOGSOURCEPORT 14 /* get the source m/c's port */ ++#define DIOSETHADDR 15 /* set ethernet address */ ++#define DIOGETHADDR 16 /* get ethernet address */ ++#define DIOGDUMPOKAY 17 /* check if dump is configured */ ++#define DIOSDUMPTAKE 18 /* take a manual dump */ ++#endif ++ ++/* ++ * structures ++ */ ++ ++/* This is the header dumped at the top of every valid crash dump. ++ */ ++typedef struct kl_dump_header_s { ++ uint64_t magic_number; /* dump magic number, unique to verify dump */ ++ uint32_t version; /* version number of this dump */ ++ uint32_t header_size; /* size of this header */ ++ uint32_t dump_level; /* level of this dump */ ++ /* FIXME: rename page_size to dump_page_size ++ * The size of a hardware/physical memory page (DUMP_PAGE_SIZE). ++ * NB: Not the configurable system page (PAGE_SIZE) (4K, 8K, 16K, etc.) ++ */ ++/* uint32_t dh_dump_page_size; */ ++ uint32_t page_size; /* page size (e.g. 4K, 8K, 16K, etc.) */ ++ uint64_t memory_size; /* size of entire physical memory */ ++ uint64_t memory_start; /* start of physical memory */ ++ uint64_t memory_end; /* end of physical memory */ ++#if DUMP_DEBUG >= 6 ++ uint64_t num_bytes; /* number of bytes in this dump */ ++#endif ++ /* the number of dump pages in this dump specifically */ ++ uint32_t num_dump_pages; ++ char panic_string[KL_DUMP_PANIC_LEN]; /* panic string, if available*/ ++ ++ /* timeval depends on machine, two long values */ ++ struct {uint64_t tv_sec; ++ uint64_t tv_usec; ++ } time; /* the time of the system crash */ ++ ++ /* the NEW utsname (uname) information -- in character form */ ++ /* we do this so we don't have to include utsname.h */ ++ /* plus it helps us be more architecture independent */ ++ char utsname_sysname[KL_UTS_LEN]; ++ char utsname_nodename[KL_UTS_LEN]; ++ char utsname_release[KL_UTS_LEN]; ++ char utsname_version[KL_UTS_LEN]; ++ char utsname_machine[KL_UTS_LEN]; ++ char utsname_domainname[KL_UTS_LEN]; ++ ++ uint64_t current_task; /* fixme: better use uint64_t here */ ++ uint32_t dump_compress; /* compression type used in this dump */ ++ uint32_t dump_flags; /* any additional flags */ ++ uint32_t dump_device; /* any additional flags */ ++ uint64_t dump_buffer_size; /* version >= 9 */ ++} __attribute__((packed)) kl_dump_header_t; ++ ++/* This is the header used by the s390 standalone dump tools ++ */ ++typedef struct kl_dump_header_s390sa_s { ++ uint64_t magic_number; /* magic number for this dump (unique)*/ ++ uint32_t version; /* version number of this dump */ ++ uint32_t header_size; /* size of this header */ ++ uint32_t dump_level; /* the level of this dump (just a header?) */ ++ uint32_t page_size; /* page size of dumped Linux (4K,8K,16K etc.) */ ++ uint64_t memory_size; /* the size of all physical memory */ ++ uint64_t memory_start; /* the start of physical memory */ ++ uint64_t memory_end; /* the end of physical memory */ ++ uint32_t num_pages; /* number of pages in this dump */ ++ uint32_t pad; /* ensure 8 byte alignment for tod and cpu_id */ ++ uint64_t tod; /* the time of the dump generation */ ++ uint64_t cpu_id; /* cpu id */ ++ uint32_t arch_id; ++ uint32_t build_arch_id; ++#define KL_DH_ARCH_ID_S390X 2 ++#define KL_DH_ARCH_ID_S390 1 ++} __attribute__((packed)) kl_dump_header_s390sa_t; ++ ++/* Header associated to each physical page of memory saved in the system ++ * crash dump. ++ */ ++typedef struct kl_dump_page_s { ++#if DUMP_DEBUG >= 6 ++ uint64_t byte_offset; /* byte offset */ ++ uint64_t page_index; /* page index */ ++#endif ++ uint64_t address; /* the address of this dump page */ ++ uint32_t size; /* the size of this dump page */ ++ uint32_t flags; /* flags (DUMP_COMPRESSED, DUMP_RAW or DUMP_END) */ ++} __attribute__((packed)) kl_dump_page_t; ++ ++/* CORE_TYPE indicating type of dump ++ */ ++typedef enum { ++ dev_kmem, /* image of /dev/kmem, a running kernel */ ++ reg_core, /* Regular (uncompressed) core file */ ++ s390_core, /* s390 core file */ ++ cmp_core, /* compressed core file */ ++ unk_core /* unknown core type */ ++} CORE_TYPE; ++ ++/* function to determine kernel stack for task */ ++typedef kaddr_t(*kl_kernelstack_t) (kaddr_t); ++/* map virtual address to physical one */ ++typedef int(*kl_virtop_t)(kaddr_t, void*, kaddr_t*); ++/* function to perform page-table traversal */ ++typedef kaddr_t(*kl_mmap_virtop_t)(kaddr_t, void*); ++/* XXX description */ ++typedef int(*kl_valid_physmem_t)(kaddr_t, int); ++/* XXX description */ ++typedef kaddr_t(*kl_next_valid_physaddr_t)(kaddr_t); ++/* write a dump-header-asm, if analyzing a live system */ ++typedef int(*kl_write_dump_header_asm_t)(void*); ++/* redirect addresses pointing into task_union areas for running tasks */ ++typedef kaddr_t(*kl_fix_vaddr_t)(kaddr_t, size_t); ++/* initialize mapping of virtual to physical addresses */ ++typedef int (*kl_init_virtop_t)(void); ++ ++/* struct storing dump architecture specific values ++ */ ++typedef struct kl_dumparch_s { ++ int arch; /* KL_ARCH_ */ ++ int ptrsz; /* 32 or 64 bit */ ++ int byteorder; /* KL_LITTLE_ENDIAN or KL_BIG_ENDIAN */ ++ uint64_t pageoffset; /* PAGE_OFFSET */ ++ uint64_t kstacksize; /* size of kernel stack */ ++ uint64_t pgdshift; /* PGDIR_SHIFT */ ++ uint64_t pgdsize; /* PGDIR_SIZE */ ++ uint64_t pgdmask; /* PGDIR_MASK */ ++ uint64_t pmdshift; /* PMD_SHIFT */ ++ uint64_t pmdsize; /* PMD_SIZE */ ++ uint64_t pmdmask; /* PMD_MASK */ ++ uint64_t pageshift; /* PAGE_SHIFT */ ++ uint64_t pagesize; /* PAGE_SIZE */ ++ uint64_t pagemask; /* PAGE_MASK */ ++ uint32_t ptrsperpgd; /* PTRS_PER_PGD */ ++ uint32_t ptrsperpmd; /* PTRS_PER_PMD */ ++ uint32_t ptrsperpte; /* PTRS_PER_PTE */ ++ kl_kernelstack_t kernelstack; /* determine kernel stack for task */ ++ kl_virtop_t virtop; /* map virtual address to physical */ ++ kl_mmap_virtop_t mmap_virtop; /* traverse page table */ ++ kl_valid_physmem_t valid_physmem; /* XXX description */ ++ kl_next_valid_physaddr_t next_valid_physaddr; /* XXX description */ ++ kl_fix_vaddr_t fix_vaddr; /* XXX description */ ++ uint32_t dha_size; /* size of kl_dump_header_xxx_t */ ++ kl_write_dump_header_asm_t write_dha; /* XXX description */ ++ kl_init_virtop_t init_virtop; /* init address translation */ ++} kl_dumparch_t; ++ ++/* function types for dumpaccess */ ++typedef kaddr_t (*kl_get_ptr_t) (void*); ++typedef uint8_t (*kl_get_uint8_t) (void*); ++typedef uint16_t(*kl_get_uint16_t)(void*); ++typedef uint32_t(*kl_get_uint32_t)(void*); ++typedef uint64_t(*kl_get_uint64_t)(void*); ++/* function types for dumpaccess */ ++typedef kaddr_t (*kl_read_ptr_t) (kaddr_t); ++typedef uint8_t (*kl_read_uint8_t) (kaddr_t); ++typedef uint16_t (*kl_read_uint16_t)(kaddr_t); ++typedef uint32_t (*kl_read_uint32_t)(kaddr_t); ++typedef uint64_t (*kl_read_uint64_t)(kaddr_t); ++ ++/* struct to store dump architecture specific functions ++ */ ++typedef struct kl_dumpaccess_s { ++ /* get integer value from memory, previously read from dump */ ++ kl_get_ptr_t get_ptr; ++ kl_get_uint8_t get_uint8; ++ kl_get_uint16_t get_uint16; ++ kl_get_uint32_t get_uint32; ++ kl_get_uint64_t get_uint64; ++ /* read integer value from dump (from physical address) */ ++ kl_read_ptr_t read_ptr; ++ kl_read_uint8_t read_uint8; ++ kl_read_uint16_t read_uint16; ++ kl_read_uint32_t read_uint32; ++ kl_read_uint64_t read_uint64; ++ /* read integer value from dump (from virtual address) */ ++ kl_read_ptr_t vread_ptr; ++ kl_read_uint8_t vread_uint8; ++ kl_read_uint16_t vread_uint16; ++ kl_read_uint32_t vread_uint32; ++ kl_read_uint64_t vread_uint64; ++} kl_dumpaccess_t; ++ ++/* Struct containing sizes of frequently used kernel structures. ++ */ ++typedef struct struct_sizes_s { ++ int task_struct_sz; ++ int mm_struct_sz; ++ int page_sz; ++ int module_sz; ++ int new_utsname_sz; ++ int switch_stack_sz; ++ int pt_regs_sz; ++ int pglist_data_sz; ++ int runqueue_sz; ++} struct_sizes_t; ++ ++/* struct storing memory specifc values of the dumped Linux system ++ */ ++typedef struct kl_kerninfo_s{ ++ kaddr_t num_physpages; /* number of physical pages */ ++ kaddr_t mem_map; /* XXX description */ ++ kaddr_t high_memory; /* physical memory size */ ++ kaddr_t init_mm; /* address of mm_struct init_mm */ ++ uint64_t kernel_flags; /* to indicate kernel features ++ * e.g. KL_IS_PAE_I386 on i386 */ ++ int num_cpus; /* number of cpus */ ++ kaddr_t pgdat_list; /* pgdat_list value. used as MEM_MAP */ ++ /* not defined for DISCONTIG memory */ ++ int linux_release; /* kernel release of dump */ ++ struct_sizes_t struct_sizes; /* frequently needed struct sizes */ ++} kl_kerninfo_t; ++ ++/* various flags to indicate Linux kernel compile switches */ ++#define KL_IS_PAE_I386 0x0020 /* i386 kernel with PAE support */ ++ ++/* struct where to keep whole information about the dump ++ */ ++typedef struct kl_dumpinfo_s { ++ CORE_TYPE core_type; /* type of core file */ ++ char *dump; /* pathname for dump */ ++ char *map; /* pathname for map file */ ++ int core_fd; /* file descriptor for dump file */ ++ int rw_flag; /* O_RDONLY/O_RDWR (/dev/kmem only) */ ++ kl_dumparch_t arch; /* dump arch info */ ++ kl_dumpaccess_t func; /* dump access functions */ ++ kl_kerninfo_t mem; /* mem info for dump */ ++} kl_dumpinfo_t; ++ ++/* External declarations ++ */ ++extern char *dh_typename; ++extern char *dha_typename; ++extern void *G_dump_header; ++extern void *G_dump_header_asm; ++extern kl_dump_header_t *KL_DUMP_HEADER; ++extern void *KL_DUMP_HEADER_ASM; ++ ++/* function declarations ++ */ ++ ++/* open dump */ ++int kl_open_dump(void); ++ ++/* init sizes for some structures */ ++void kl_init_struct_sizes(void); ++ ++/* init host architecture information */ ++int kl_setup_hostinfo(void); ++ ++/* init dumpinfo structure */ ++int kl_setup_dumpinfo(char * /* map file */, ++ char * /* dump */, ++ int /* rwflag */); ++ ++ ++/* init dumpinfo structure */ ++int kl_set_dumpinfo(char * /* map file */, ++ char * /* dump */, ++ int /* arch of dump */, ++ int /* rwflag */); ++ ++/* free dumpinfo structure */ ++void kl_free_dumpinfo(kl_dumpinfo_t *); ++ ++/* set memory related characteristics of dump */ ++int kl_set_kerninfo(void); ++ ++/* set function pointers for dump access (depends on host and dump arch) */ ++int kl_set_dumpaccess(void); ++ ++/* print contents of kl_dumpinfo_t etc. */ ++int kl_print_dumpinfo(int); ++#define KL_INFO_ALL 0 ++#define KL_INFO_ENDIAN 1 ++#define KL_INFO_ARCH 2 ++#define KL_INFO_PTRSZ 3 ++#define KL_INFO_KRELEASE 4 ++#define KL_INFO_MEMSIZE 5 ++#define KL_INFO_NUMCPUS 6 ++ ++/* Functions that read data from generic dump_header */ ++int kl_valid_dump_magic(uint64_t); ++int kl_header_swap(void *); ++uint64_t kl_header_magic(void *); ++int kl_valid_header(void *); ++uint32_t kl_header_version(void *); ++int kl_header_size(void *); ++void *kl_read_header(int fd, void *); ++ ++/* init common lkcd dump header from dump */ ++void kl_init_dump_header(int); ++ ++/* try to evalutate arch from lkcd 4.1 (version <= 7) dump header */ ++int kl_dump_arch_4_1(void *); ++ ++/* swap dump header values if necessary */ ++void kl_swap_dump_header_reg(kl_dump_header_t* dh); ++void kl_swap_dump_header_s390sa(kl_dump_header_s390sa_t* dh); ++ ++/* Read dump header in from dump */ ++int kl_read_dump_header(void); ++int kl_read_dump_header_asm(void); ++ ++/* Determine the architecure of dump */ ++int kl_set_dumparch(int); ++ ++/* Finish setting up for access to dump */ ++int kl_setup_dumpaccess(int); ++ ++/* get the raw dump header */ ++int kl_get_raw_dh(int); ++int kl_get_raw_asm_dh(int); ++ ++/* get common lkcd dump header */ ++int kl_get_dump_header(kl_dump_header_t*); ++ ++/* get older style dump headers */ ++kl_dump_header_t *get_dump_header_4_1(void *); ++kl_dump_header_t *get_dump_header_SN2_24X(void *); ++ ++/* get task that was running when dump was started */ ++kaddr_t kl_dumptask(void); ++ ++/* Print dump header */ ++int kl_print_dump_header(const char* dump); ++ ++/* Print dump regular header */ ++void kl_print_dump_header_reg(kl_dump_header_t *); ++ ++/* Print s390 dump header */ ++void kl_print_dump_header_s390(char*); ++ ++/* Convert s390 to reg header */ ++void kl_s390sa_to_reg_header(kl_dump_header_s390sa_t*, kl_dump_header_t*); ++ ++/* Byte swapping functions needed for Xclrash */ ++/* get integer value from buffer and swap bytes */ ++kaddr_t kl_get_swap_ptr(void*); ++uint16_t kl_get_swap_uint16(void*); ++uint32_t kl_get_swap_uint32(void*); ++uint64_t kl_get_swap_uint64(void*); ++ ++/* read integer value from dump (physical address) and swap bytes */ ++kaddr_t kl_read_swap_ptr(kaddr_t); ++uint16_t kl_read_swap_uint16(kaddr_t); ++uint32_t kl_read_swap_uint32(kaddr_t); ++uint64_t kl_read_swap_uint64(kaddr_t); ++ ++/* read integer value from dump (virtual address) and swap bytes */ ++kaddr_t kl_vread_swap_ptr(kaddr_t); ++uint16_t kl_vread_swap_uint16(kaddr_t); ++uint32_t kl_vread_swap_uint32(kaddr_t); ++uint64_t kl_vread_swap_uint64(kaddr_t); ++ ++#endif /* __KL_DUMP_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_dump_arch.h +@@ -0,0 +1,124 @@ ++/* ++ * $Id: kl_dump_arch.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_DUMP_ARCH_H ++#define __KL_DUMP_ARCH_H ++ ++/* check for valid configuration ++ */ ++#if !(defined(HOST_ARCH_ALPHA) || defined(HOST_ARCH_I386) || \ ++ defined(HOST_ARCH_IA64) || defined(HOST_ARCH_S390) || \ ++ defined(HOST_ARCH_S390X) || defined(HOST_ARCH_ARM) || \ ++ defined(HOST_ARCH_PPC64) || defined(HOST_ARCH_X86_64)) ++# error "No valid host architecture defined." ++#endif ++#if ((defined(HOST_ARCH_ALPHA) && \ ++ (defined(HOST_ARCH_I386) || defined(HOST_ARCH_IA64) || \ ++ defined(HOST_ARCH_S390) || defined(HOST_ARCH_S390X) || \ ++ defined(HOST_ARCH_ARM) || defined(HOST_ARCH_PPC64) || \ ++ defined(HOST_ARCH_X86_64))) || \ ++ (defined(HOST_ARCH_I386) && \ ++ (defined(HOST_ARCH_IA64) || defined(HOST_ARCH_S390) || \ ++ defined(HOST_ARCH_S390X)|| defined(HOST_ARCH_ARM) || \ ++ defined(HOST_ARCH_PPC64)|| defined(HOST_ARCH_X86_64))) || \ ++ (defined(HOST_ARCH_IA64) && \ ++ (defined(HOST_ARCH_S390)|| defined(HOST_ARCH_S390X) || \ ++ defined(HOST_ARCH_ARM) || defined(HOST_ARCH_PPC64) || \ ++ defined(HOST_ARCH_X86_64))) || \ ++ (defined(HOST_ARCH_S390) && \ ++ (defined(HOST_ARCH_S390X) || defined(HOST_ARCH_ARM) || \ ++ defined(HOST_ARCH_PPC64) || defined(HOST_ARCH_X86_64))) || \ ++ (defined(HOST_ARCH_S390X) && \ ++ (defined(HOST_ARCH_ARM) || defined(HOST_ARCH_PPC64) || \ ++ defined(HOST_ARCH_X86_64))) || \ ++ (defined(HOST_ARCH_ARM) && \ ++ (defined(HOST_ARCH_PPC64) || defined(HOST_ARCH_X86_64))) || \ ++ (defined(HOST_ARCH_PPC64) && defined(HOST_ARCH_X86_64))) ++# error "More than one valid host architectures defined." ++#endif ++#if !(defined(DUMP_ARCH_ALPHA) || defined(DUMP_ARCH_I386) || \ ++ defined(DUMP_ARCH_IA64) || defined(DUMP_ARCH_S390) || \ ++ defined(DUMP_ARCH_S390X) || defined(DUMP_ARCH_ARM) || \ ++ defined(DUMP_ARCH_PPC64) || defined(DUMP_ARCH_X86_64)) ++# error "No valid dump architecture defined." ++#endif ++ ++/* optional: check that host arch equals one supported dump arch ++ */ ++#ifdef SUPPORT_HOST_ARCH ++# if (defined(HOST_ARCH_ALPHA) && !defined(DUMP_ARCH_ALPHA)) || \ ++ (defined(HOST_ARCH_I386) && !defined(DUMP_ARCH_I386)) || \ ++ (defined(HOST_ARCH_IA64) && !defined(DUMP_ARCH_IA64)) || \ ++ (defined(HOST_ARCH_S390) && !defined(DUMP_ARCH_S390)) || \ ++ (defined(HOST_ARCH_S390X) && !defined(DUMP_ARCH_S390X)) || \ ++ (defined(HOST_ARCH_ARM) && !defined(DUMP_ARCH_ARM)) || \ ++ (defined(HOST_ARCH_PPC64) && !defined(DUMP_ARCH_PPC64)) || \ ++ (defined(HOST_ARCH_X86_64) && !defined(DUMP_ARCH_X86_64)) ++# error "Host architecture not supported as dump architecture." ++# endif ++#endif ++ ++/* include dump architecture specific stuff ++ */ ++#ifdef DUMP_ARCH_ALPHA ++# include ++# include ++#endif ++/* cpw: use the " " form: */ ++#ifdef DUMP_ARCH_IA64 ++# include "kl_mem_ia64.h" ++# include "kl_dump_ia64.h" ++#endif ++#ifdef DUMP_ARCH_I386 ++# include ++# include ++#endif ++#ifdef DUMP_ARCH_S390 ++# include ++# include ++#endif ++#ifdef DUMP_ARCH_S390X ++# include ++# include ++#endif ++#ifdef DUMP_ARCH_ARM ++# include ++# include ++#endif ++#ifdef DUMP_ARCH_PPC64 ++#include ++#include ++#endif ++#ifdef DUMP_ARCH_X86_64 ++#include ++#include ++#endif ++ ++/** Function prototypes ++ **/ ++int kl_init_kern_info(void); ++ ++int kl_get_struct( ++ kaddr_t /* address */, ++ int /* size of struct */, ++ void * /* ptr to buffer */, ++ char * /* name of struct */); ++ ++#endif /* __KL_DUMP_ARCH_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_dump_ia64.h +@@ -0,0 +1,199 @@ ++/* ++ * $Id: kl_dump_ia64.h 1151 2005-02-23 01:09:12Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++/* This header file holds the architecture specific crash dump header */ ++#ifndef __KL_DUMP_IA64_H ++#define __KL_DUMP_IA64_H ++ ++/* definitions */ ++#ifndef KL_NR_CPUS ++# define KL_NR_CPUS 128 /* max number CPUs */ ++#endif ++ ++#define KL_DUMP_MAGIC_NUMBER_IA64 0xdeaddeadULL /* magic number */ ++#define KL_DUMP_VERSION_NUMBER_IA64 0x4 /* version number */ ++ ++ ++/* ++ * mkswap.c calls getpagesize() to get the system page size, ++ * which is not necessarily the same as the hardware page size. ++ * ++ * For ia64 the kernel PAGE_SIZE can be configured from 4KB ... 16KB. ++ * ++ * The physical memory is layed out out in the hardware/minimal pages. ++ * This is the size we need to use for dumping physical pages. ++ * ++ * Note ths hardware/minimal page size being use in; ++ * arch/ia64/kernel/efi.c`efi_memmap_walk(): ++ * curr.end = curr.start + (md->num_pages << 12); ++ * ++ * Since the system page size could change between the kernel we boot ++ * on the the kernel that cause the core dume we may want to have something ++ * more constant like the maximum system page size (See include/asm-ia64/page.h). ++ */ ++#define DUMP_MIN_PAGE_SHIFT 12 ++#define DUMP_MIN_PAGE_SIZE (1UL << DUMP_MIN_PAGE_SHIFT) ++#define DUMP_MIN_PAGE_MASK (~(DUMP_MIN_PAGE_SIZE - 1)) ++#define DUMP_MIN_PAGE_ALIGN(addr) (((addr) + DUMP_MIN_PAGE_SIZE - 1) & DUMP_MIN_PAGE_MASK) ++ ++#define DUMP_MAX_PAGE_SHIFT 16 ++#define DUMP_MAX_PAGE_SIZE (1UL << DUMP_MAX_PAGE_SHIFT) ++#define DUMP_MAX_PAGE_MASK (~(DUMP_MAX_PAGE_SIZE - 1)) ++#define DUMP_MAX_PAGE_ALIGN(addr) (((addr) + DUMP_MAX_PAGE_SIZE - 1) & DUMP_MAX_PAGE_MASK) ++ ++#define DUMP_HEADER_OFFSET DUMP_MAX_PAGE_SIZE ++ ++#define DUMP_EF_PAGE_SHIFT DUMP_MIN_PAGE_SHIFT ++ ++#define DUMP_PAGE_SHIFT DUMP_MIN_PAGE_SHIFT ++#define DUMP_PAGE_SIZE DUMP_MIN_PAGE_SIZE ++#define DUMP_PAGE_MASK DUMP_MIN_PAGE_MASK ++#define DUMP_PAGE_ALIGN(addr) DUMP_MIN_PAGE_ALIGN(addr) ++ ++struct kl_ia64_fpreg { ++ union { ++ unsigned long bits[2]; ++ long double __dummy; /* force 16-byte alignment */ ++ } u; ++}; ++ ++struct kl_pt_regs_ia64 { ++ /* for 2.6 kernels only. This structure was totally different in 2.4 kernels */ ++ unsigned long b6; /* scratch */ ++ unsigned long b7; /* scratch */ ++ ++ unsigned long ar_csd; /* used by cmp8xchg16 (scratch) */ ++ unsigned long ar_ssd; /* reserved for future use (scratch) */ ++ ++ unsigned long r8; /* scratch (return value register 0) */ ++ unsigned long r9; /* scratch (return value register 1) */ ++ unsigned long r10; /* scratch (return value register 2) */ ++ unsigned long r11; /* scratch (return value register 3) */ ++ ++ unsigned long cr_ipsr; /* interrupted task's psr */ ++ unsigned long cr_iip; /* interrupted task's instruction pointer */ ++ unsigned long cr_ifs; /* interrupted task's function state */ ++ ++ unsigned long ar_unat; /* interrupted task's NaT register (preserved) */ ++ unsigned long ar_pfs; /* prev function state */ ++ unsigned long ar_rsc; /* RSE configuration */ ++ /* The following two are valid only if cr_ipsr.cpl > 0: */ ++ unsigned long ar_rnat; /* RSE NaT */ ++ unsigned long ar_bspstore; /* RSE bspstore */ ++ ++ unsigned long pr; /* 64 predicate registers (1 bit each) */ ++ unsigned long b0; /* return pointer (bp) */ ++ unsigned long loadrs; /* size of dirty partition << 16 */ ++ ++ unsigned long r1; /* the gp pointer */ ++ unsigned long r12; /* interrupted task's memory stack pointer */ ++ unsigned long r13; /* thread pointer */ ++ ++ unsigned long ar_fpsr; /* floating point status (preserved) */ ++ unsigned long r15; /* scratch */ ++ ++ /* The remaining registers are NOT saved for system calls. */ ++ ++ unsigned long r14; /* scratch */ ++ unsigned long r2; /* scratch */ ++ unsigned long r3; /* scratch */ ++ ++ /* The following registers are saved by SAVE_REST: */ ++ unsigned long r16; /* scratch */ ++ unsigned long r17; /* scratch */ ++ unsigned long r18; /* scratch */ ++ unsigned long r19; /* scratch */ ++ unsigned long r20; /* scratch */ ++ unsigned long r21; /* scratch */ ++ unsigned long r22; /* scratch */ ++ unsigned long r23; /* scratch */ ++ unsigned long r24; /* scratch */ ++ unsigned long r25; /* scratch */ ++ unsigned long r26; /* scratch */ ++ unsigned long r27; /* scratch */ ++ unsigned long r28; /* scratch */ ++ unsigned long r29; /* scratch */ ++ unsigned long r30; /* scratch */ ++ unsigned long r31; /* scratch */ ++ ++ unsigned long ar_ccv; /* compare/exchange value (scratch) */ ++ ++ /* ++ * * Floating point registers that the kernel considers scratch: ++ * */ ++ struct kl_ia64_fpreg f6; /* scratch */ ++ struct kl_ia64_fpreg f7; /* scratch */ ++ struct kl_ia64_fpreg f8; /* scratch */ ++ struct kl_ia64_fpreg f9; /* scratch */ ++ struct kl_ia64_fpreg f10; /* scratch */ ++ struct kl_ia64_fpreg f11; /* scratch */ ++} __attribute__((packed)); ++ ++/* ++ * Structure: dump_header_asm_t ++ * Function: This is the header for architecture-specific stuff. It ++ * follows right after the dump header. ++ */ ++typedef struct kl_dump_header_ia64_s { ++ /* the dump magic number -- unique to verify dump is valid */ ++ uint64_t magic_number; ++ /* the version number of this dump */ ++ uint32_t version; ++ /* the size of this header (in case we can't read it) */ ++ uint32_t header_size; ++ /* pointer to pt_regs */ ++ uint64_t pt_regs; ++ /* the dump registers */ ++ struct kl_pt_regs_ia64 regs; ++ /* the rnat register saved after flushrs */ ++ uint64_t rnat; ++ /* the pfs register saved after flushrs */ ++ uint64_t pfs; ++ /* the bspstore register saved after flushrs */ ++ uint64_t bspstore; ++ ++ /* smp specific */ ++ uint32_t smp_num_cpus; ++ uint32_t dumping_cpu; ++ struct kl_pt_regs_ia64 smp_regs[KL_NR_CPUS]; ++ uint64_t smp_current_task[KL_NR_CPUS]; ++ uint64_t stack[KL_NR_CPUS]; ++} __attribute__((packed)) kl_dump_header_ia64_t; ++ ++/* The following struct is used just to calculate the size needed ++ * to store per CPU info. (Make sure it is sync with the above struct) ++ */ ++struct kl_dump_CPU_info_ia64 { ++ struct kl_pt_regs_ia64 smp_regs; ++ uint64_t smp_current_task; ++ uint64_t stack; ++} __attribute__((packed)); ++ ++/* function declarations ++ */ ++int kl_set_dumparch_ia64(void); ++uint32_t dha_num_cpus_ia64(void); ++kaddr_t dha_current_task_ia64(int cpuid); ++int dha_cpuid_ia64(kaddr_t); ++kaddr_t dha_stack_ia64(int); ++kaddr_t dha_stack_ptr_ia64(int); ++int kl_read_dump_header_ia64(void); ++ ++#endif /* __KL_DUMP_IA64_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_dwarfs.h +@@ -0,0 +1,27 @@ ++/* ++ * $Id: kl_dwarfs.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by: Prashanth Tamraparni (prasht@in.ibm.com) ++ * Contributions by SGI ++ * ++ * Copyright (C) 2004 International Business Machines Corp. ++ * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++#ifndef __KL_DWARFS_H ++#define __KL_DWARFS_H ++ ++/* Dwarf function declarations */ ++ ++int dw_open_namelist(char*, int); ++int dw_setup_typeinfo(void); ++ ++#endif /* __KL_DWARFS_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_error.h +@@ -0,0 +1,266 @@ ++/* ++ * $Id: kl_error.h 1169 2005-03-02 21:38:01Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_ERROR_H ++#define __KL_ERROR_H ++ ++extern uint64_t klib_error; ++extern FILE *kl_stdout; ++extern FILE *kl_stderr; ++ ++/* Error Classes ++ */ ++#define KLEC_APP 0 ++#define KLEC_KLIB 1 ++#define KLEC_MEM 2 ++#define KLEC_SYM 3 ++#define KLEC_KERN 4 ++ ++#define KLEC_CLASS_MASK 0x00000000ff000000ULL ++#define KLEC_CLASS_SHIFT 24 ++#define KLEC_ECODE_MASK 0x0000000000ffffffULL ++#define KLEC_TYPE_MASK 0xffffffff00000000ULL ++#define KLEC_TYPE_SHIFT 32 ++#define KLEC_CLASS(e) ((e & KLEC_CLASS_MASK) >> KLEC_CLASS_SHIFT) ++#define KLEC_ECODE(e) (e & KLEC_ECODE_MASK) ++#define KLEC_TYPE(e) ((e & KLEC_TYPE_MASK) >> KLEC_TYPE_SHIFT) ++ ++void kl_reset_error(void); /* reset klib_error */ ++void kl_print_error(void); /* print warning/error messages */ ++void kl_check_error(char*); /* check for/handle errors, generate messages */ ++ ++/* FIXME: not used yet -- for changes in future, improve error handling ++ */ ++typedef struct klib_error_s{ ++ uint32_t code; /* error code */ ++ uint16_t class; /* error class */ ++ uint16_t severity; /* severity of error: e.g. warning or fatal error */ ++ uint32_t datadesc; /* description of data which caused the error */ ++ FILE *fp; /* fp where to place warning and error messages */ ++} klib_error_t; ++ ++/* ++ * Some macros for accessing data in klib_error ++ */ ++#define KL_ERROR klib_error ++#define KL_ERRORFP kl_stderr ++ ++/* Error codes ++ * ++ * There are basically two types of error codes -- with each type ++ * residing in a single word in a two word error code value. The lower ++ * 32-bits contains an error class and code that represents exactly ++ * WHAT error occurred (e.g., non-numeric text in a numeric value ++ * entered by a user, bad virtual address, etc.). ++ * ++ * The upper 32-bits represents what type of data was being referenced ++ * when the error occurred (e.g., bad proc struct). Having two tiers of ++ * error codes makes it easier to generate useful and specific error ++ * messages. Note that is possible to have situations where one or the ++ * other type of error codes is not set. This is OK as long as at least ++ * one type s set. ++ */ ++ ++/* General klib error codes ++ */ ++#define KLE_KLIB (KLEC_KLIB << KLEC_CLASS_SHIFT) ++#define KLE_NO_MEMORY (KLE_KLIB|1) ++#define KLE_OPEN_ERROR (KLE_KLIB|2) ++#define KLE_ZERO_BLOCK (KLE_KLIB|3) ++#define KLE_INVALID_VALUE (KLE_KLIB|4) ++#define KLE_NULL_BUFF (KLE_KLIB|5) ++#define KLE_ZERO_SIZE (KLE_KLIB|6) ++#define KLE_ACTIVE (KLE_KLIB|7) ++#define KLE_NULL_POINTER (KLE_KLIB|8) ++#define KLE_UNSUPPORTED_ARCH (KLE_KLIB|9) ++ ++#define KLE_MISC_ERROR (KLE_KLIB|97) ++#define KLE_NOT_SUPPORTED (KLE_KLIB|98) ++#define KLE_UNKNOWN_ERROR (KLE_KLIB|99) ++ ++/* memory error codes ++ */ ++#define KLE_MEM (KLEC_MEM << KLEC_CLASS_SHIFT) ++#define KLE_BAD_MAP_FILE (KLE_MEM|1) ++#define KLE_BAD_DUMP (KLE_MEM|2) ++#define KLE_BAD_DUMPTYPE (KLE_MEM|3) ++#define KLE_INVALID_LSEEK (KLE_MEM|4) ++#define KLE_INVALID_READ (KLE_MEM|5) ++#define KLE_BAD_KERNINFO (KLE_MEM|6) ++#define KLE_INVALID_PADDR (KLE_MEM|7) ++#define KLE_INVALID_VADDR (KLE_MEM|8) ++#define KLE_INVALID_VADDR_ALIGN (KLE_MEM|9) ++#define KLE_INVALID_MAPPING (KLE_MEM|10) ++#define KLE_CMP_ERROR (KLE_MEM|11) ++#define KLE_INVALID_DUMP_MAGIC (KLE_MEM|12) ++#define KLE_KERNEL_MAGIC_MISMATCH (KLE_MEM|13) ++#define KLE_NO_END_SYMBOL (KLE_MEM|14) ++#define KLE_INVALID_DUMP_HEADER (KLE_MEM|15) ++#define KLE_DUMP_INDEX_CREATION (KLE_MEM|16) ++#define KLE_DUMP_HEADER_ONLY (KLE_MEM|17) ++#define KLE_PAGE_NOT_PRESENT (KLE_MEM|18) ++#define KLE_BAD_ELF_FILE (KLE_MEM|19) ++#define KLE_ARCHIVE_FILE (KLE_MEM|20) ++#define KLE_MAP_FILE_PRESENT (KLE_MEM|21) ++#define KLE_BAD_MAP_FILENAME (KLE_MEM|22) ++#define KLE_BAD_DUMP_FILENAME (KLE_MEM|23) ++#define KLE_BAD_NAMELIST_FILE (KLE_MEM|24) ++#define KLE_BAD_NAMELIST_FILENAME (KLE_MEM|25) ++#define KLE_LIVE_SYSTEM (KLE_MEM|26) ++#define KLE_NOT_INITIALIZED (KLE_MEM|27) ++ ++/* symbol error codes ++ */ ++#define KLE_SYM (KLEC_SYM << KLEC_CLASS_SHIFT) ++#define KLE_NO_SYMTAB (KLE_SYM|1) ++#define KLE_NO_SYMBOLS (KLE_SYM|2) ++#define KLE_INVALID_TYPE (KLE_SYM|3) ++#define KLE_NO_MODULE_LIST (KLE_SYM|4) ++ ++/* kernel data error codes ++ */ ++#define KLE_KERN (KLEC_KERN << KLEC_CLASS_SHIFT) ++#define KLE_INVALID_KERNELSTACK (KLE_KERN|1) ++#define KLE_INVALID_STRUCT_SIZE (KLE_KERN|2) ++#define KLE_BEFORE_RAM_OFFSET (KLE_KERN|3) ++#define KLE_AFTER_MAXPFN (KLE_KERN|4) ++#define KLE_AFTER_PHYSMEM (KLE_KERN|5) ++#define KLE_AFTER_MAXMEM (KLE_KERN|6) ++#define KLE_PHYSMEM_NOT_INSTALLED (KLE_KERN|7) ++#define KLE_NO_DEFTASK (KLE_KERN|8) ++#define KLE_PID_NOT_FOUND (KLE_KERN|9) ++#define KLE_DEFTASK_NOT_ON_CPU (KLE_KERN|10) ++#define KLE_NO_CURCPU (KLE_KERN|11) ++#define KLE_NO_CPU (KLE_KERN|12) ++#define KLE_SIG_ERROR (KLE_KERN|13) ++#define KLE_TASK_RUNNING (KLE_KERN|14) ++#define KLE_NO_SWITCH_STACK (KLE_KERN|15) ++ ++/* Error codes that indicate what type of data was bad. These are ++ * placed in the upper 32-bits of klib_error. ++ */ ++#define KLE_BAD_TASK_STRUCT (((uint64_t)1)<<32) ++#define KLE_BAD_SYMNAME (((uint64_t)2)<<32) ++#define KLE_BAD_SYMADDR (((uint64_t)3)<<32) ++#define KLE_BAD_FUNCADDR (((uint64_t)4)<<32) ++#define KLE_BAD_STRUCT (((uint64_t)5)<<32) ++#define KLE_BAD_FIELD (((uint64_t)6)<<32) ++#define KLE_BAD_PC (((uint64_t)7)<<32) ++#define KLE_BAD_RA (((uint64_t)8)<<32) ++#define KLE_BAD_SP (((uint64_t)9)<<32) ++#define KLE_BAD_EP (((uint64_t)10)<<32) ++#define KLE_BAD_SADDR (((uint64_t)11)<<32) ++#define KLE_BAD_KERNELSTACK (((uint64_t)12)<<32) ++#define KLE_BAD_LINENO (((uint64_t)13)<<32) ++#define KLE_MAP_FILE (((uint64_t)14)<<32) ++#define KLE_DUMP (((uint64_t)15)<<32) ++#define KLE_BAD_STRING (((uint64_t)16)<<32) ++#define KLE_ELF_FILE (((uint64_t)17)<<32) ++ ++/* flags for function kl_msg() ++ * First 3 bits define trace levels. Minimum trace threshold is trace level 1. ++ * So maximal 7 trace levels are possible. We are using only KLE_TRACELEVEL_MAX. ++ * If no trace level bits are set, it is normal output. ++ */ ++#define _KLE_TRACEBIT1 0x00000001 /* trace bit 1 */ ++#define _KLE_TRACEBIT2 0x00000002 /* trace bit 2 */ ++#define _KLE_TRACEBIT3 0x00000004 /* trace bit 3 */ ++#define _KLE_TRACENUM 8 /* used in _KLE_TRACENUM */ ++#define _KLE_TRACEMASK (_KLE_TRACENUM-1) /* mask for trace bits */ ++/* further flags */ ++#define KLE_F_NOORIGIN 0x00001000 /* do not print origin for this msg */ ++#define KLE_F_ERRORMSG 0x00002000 /* treat message as error message */ ++/* trace levels := predefined combinations of trace bits */ ++#define KLE_F_TRACELEVEL1 (_KLE_TRACEBIT1) ++#define KLE_F_TRACELEVEL2 (_KLE_TRACEBIT2) ++#define KLE_F_TRACELEVEL3 (_KLE_TRACEBIT1|_KLE_TRACEBIT2) ++#define KLE_F_TRACELEVEL4 (_KLE_TRACEBIT3) ++#define KLE_TRACELEVELMAX 4 ++#define KLE_TRACELEVEL(flg) (flg & _KLE_TRACEMASK) ++#define KLE_GETTRACELEVEL(flg) \ ++ ((KLE_TRACELEVEL(flg) > KLE_TRACELEVELMAX) ? KLE_TRACELEVELMAX : \ ++ KLE_TRACELEVEL(flg)) ++ ++/* define debug components of libklib (64 components possible) ++ * used by kl_msg() ++ */ ++#define KL_DBGCOMP_ALLOC 0x0000000001 /* liballoc */ ++#define KL_DBGCOMP_BFD 0x0000000002 /* general bfd support */ ++#define KL_DBGCOMP_BTREE 0x0000000004 /* btree implementation */ ++#define KL_DBGCOMP_COMPRESS 0x0000000008 /* gzip/rle (de)compression */ ++#define KL_DBGCOMP_INIT 0x0000000010 /* klib initialization */ ++#define KL_DBGCOMP_MEMMAP 0x0000000020 /* memory mapping */ ++#define KL_DBGCOMP_MODULE 0x0000000040 /* kernel module handling */ ++#define KL_DBGCOMP_SIGNAL 0x0000000080 /* signal handling */ ++#define KL_DBGCOMP_STABS 0x0000000100 /* stabs format support */ ++#define KL_DBGCOMP_SYMBOL 0x0000000200 /* symbol handling */ ++#define KL_DBGCOMP_TYPE 0x0000000400 /* type information handling */ ++#define KL_DBGCOMP_ALL ((uint64_t) -1) /* all components */ ++ ++/* central output routine, shouldn't be used directly, but ++ * by following macros ++ */ ++void kl_msg(uint64_t, uint32_t, const char*, const char*, int, ++ const char*, ...); ++ ++/* vararg macros that should be used instead of kl_msg() ++ */ ++/* used within libklib to print non-error messages (e.g. progress indication) ++ */ ++#define KL_MSG(fmt, args...) \ ++kl_msg(0, 0, NULL, NULL, 0, fmt, ## args) ++/* Can be used by application to print error messages; ++ * not used by libklib itself. ++ */ ++#define kl_error(fmt, args...) \ ++kl_msg(0, KLE_F_ERRORMSG, __FUNCTION__, __FILE__, __LINE__, fmt, ## args) ++/* Generate trace messages. Used for libklib debugging. Might be used ++ * by an application, too. ++ * A macro _DBG_COMPONENT has to be defined locally in the module where ++ * any trace macro is used. See above debug components. ++ * Trace messages are only printed iff _DBG_COMPONENT was set before with a ++ * call to kl_set_dbg_component(). ++ */ ++#define kl_trace1(flg, fmt, args...) \ ++kl_msg(_DBG_COMPONENT, KLE_F_TRACELEVEL1|(flg), \ ++ __FUNCTION__, __FILE__, __LINE__, fmt, ## args) ++#define kl_trace2(flg, fmt, args...) \ ++kl_msg(_DBG_COMPONENT, KLE_F_TRACELEVEL2|(flg), \ ++ __FUNCTION__, __FILE__, __LINE__, fmt, ## args) ++#define kl_trace3(flg, fmt, args...) \ ++kl_msg(_DBG_COMPONENT, KLE_F_TRACELEVEL3|(flg), \ ++ __FUNCTION__, __FILE__, __LINE__, fmt, ## args) ++#define kl_trace4(flg, fmt, args...) \ ++kl_msg(_DBG_COMPONENT, KLE_F_TRACELEVEL4|(flg), \ ++ __FUNCTION__, __FILE__, __LINE__, fmt, ## args) ++ ++/* functions to set some global variables for libklib debugging ++ */ ++int kl_set_trace_threshold(uint32_t); ++void kl_set_dbg_component(uint64_t); ++void kl_set_stdout(FILE *); ++void kl_set_stderr(FILE *); ++ ++/* functions to get contents of global variables for libklib debugging ++ */ ++uint32_t kl_get_trace_threshold(void); ++uint64_t kl_get_dbg_component(void); ++ ++#endif /* __KL_ERROR_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_htnode.h +@@ -0,0 +1,71 @@ ++/* ++ * $Id: kl_htnode.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libutil. ++ * A library which provides auxiliary functions. ++ * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_HTNODE_H ++#define __KL_HTNODE_H ++ ++/* Node structure for use in hierarchical trees (htrees). ++ */ ++typedef struct htnode_s { ++ struct htnode_s *next; ++ struct htnode_s *prev; ++ struct htnode_s *parent; ++ struct htnode_s *children; ++ int seq; ++ int level; ++ int key; ++} htnode_t; ++ ++/* Flag values ++ */ ++#define HT_BEFORE 0x1 ++#define HT_AFTER 0x2 ++#define HT_CHILD 0x4 ++#define HT_PEER 0x8 ++ ++/* Function prototypes ++ */ ++htnode_t *kl_next_htnode( ++ htnode_t * /* htnode pointer */); ++ ++htnode_t *kl_prev_htnode( ++ htnode_t * /* htnode pointer */); ++ ++void ht_insert_peer( ++ htnode_t * /* htnode pointer */, ++ htnode_t * /* new htnode pointer*/, ++ int /* flags */); ++ ++void ht_insert_child( ++ htnode_t * /* htnode pointer */, ++ htnode_t * /* new htnode pointer*/, ++ int /* flags */); ++ ++int ht_insert( ++ htnode_t * /* htnode pointer */, ++ htnode_t * /* new htnode pointer*/, ++ int /* flags */); ++ ++void ht_insert_next_htnode( ++ htnode_t * /* htnode pointer */, ++ htnode_t * /* new htnode pointer*/); ++ ++#endif /* __KL_HTNODE_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_lib.h +@@ -0,0 +1,65 @@ ++/* ++ * $Id: kl_lib.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libutil. ++ * A library which provides auxiliary functions. ++ * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_LIB_H ++#define __KL_LIB_H ++ ++/* Include system header files ++ */ ++ ++#if 0 ++/* cpw: we don't need this userland stuff: */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#endif ++ ++/* Include lkcd library header files ++ */ ++/* cpw: change these from the < > form to the " " form: */ ++#include "kl_types.h" ++#include "kl_alloc.h" ++#include "kl_libutil.h" ++#include "kl_btnode.h" ++#include "kl_htnode.h" ++#include "kl_queue.h" ++#include "kl_stringtab.h" ++ ++#endif /* __KL_LIB_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_libutil.h +@@ -0,0 +1,40 @@ ++/* ++ * $Id: kl_libutil.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libutil. ++ * A library which provides auxiliary functions. ++ * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2004 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_LIBUTIL_H ++#define __KL_LIBUTIL_H ++ ++/* cpw: change all these from the < > form to the " " form: */ ++#include "kl_alloc.h" ++#include "kl_btnode.h" ++#include "kl_copt.h" ++#include "kl_htnode.h" ++#include "kl_queue.h" ++#include "kl_stringtab.h" ++ ++int kl_shift_value(uint64_t ); ++int kl_string_compare(char *, char *); ++int kl_string_match(char *, char *); ++uint64_t kl_strtoull(char *, char **, int); ++time_t kl_str_to_ctime(char *); ++void *kl_get_ra(void); ++ ++#endif /* __KL_LIBUTIL_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_mem.h +@@ -0,0 +1,104 @@ ++/* ++ * $Id: kl_mem.h 1157 2005-02-25 22:04:05Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_MEM_H ++#define __KL_MEM_H ++ ++#if 0 ++cpw: skip: ++extern kaddr_t VMALLOC_START; ++extern kaddr_t VMALLOC_END; ++#endif ++ ++/* ++ * Function prototypes ++ */ ++ ++int kl_linux_release(void); ++ ++k_error_t kl_readmem( ++ kaddr_t /* physical address to start reading from */, ++ unsigned /* number of bytes to read */, ++ void * /* pointer to buffer */); ++ ++k_error_t kl_readkmem( ++ kaddr_t /* virtual address to start reading from */, ++ unsigned /* number of bytes to read */, ++ void * /* pointer to buffer */); ++ ++int kl_virtop( ++ kaddr_t /* virtual address to translate */, ++ void * /* pointer to mem_map for address translation */, ++ kaddr_t * /* pointer to physical address to return */); ++ ++k_error_t kl_get_block( ++ kaddr_t /* virtual address */, ++ unsigned /* size of block to read in */, ++ void * /* pointer to buffer */, ++ void * /* pointer to mmap */); ++ ++/* Wrapper that eliminates the mmap parameter ++ */ ++#define GET_BLOCK(a, s, b) kl_get_block(a, s, (void *)b, (void *)0) ++ ++uint64_t kl_uint( ++ void * /* pointer to buffer containing struct */, ++ char * /* name of struct */, ++ char * /* name of member */, ++ unsigned /* offset */); ++ ++int64_t kl_int( ++ void * /* pointer to buffer containing struct */, ++ char * /* name of struct */, ++ char * /* name of member */, ++ unsigned /* offset */); ++ ++kaddr_t kl_kaddr( ++ void * /* pointer to buffer containing struct */, ++ char * /* name of struct */, ++ char * /* name of member */); ++ ++/* XXX deprecated use KL_READ_PTR() instead */ ++kaddr_t kl_kaddr_to_ptr( ++ kaddr_t /* Address to dereference */); ++ ++int kl_is_valid_kaddr( ++ kaddr_t /* Address to test */, ++ void * /* pointer to mmap */, ++ int /* flags */); ++ ++/* REMIND: ++ * Likely not right for ia64 ++ */ ++#define KL_KADDR_IS_PHYSICAL(vaddr) ((vaddr >= KL_PAGE_OFFSET) && \ ++ (vaddr <= KL_HIGH_MEMORY)) ++ ++#define PGNO_TO_PADDR(pgno) (pgno << KL_PAGE_SHIFT) ++ ++/* ++ * declaration of some defaults that are used in kl_set_dumparch() ++ */ ++int kl_valid_physaddr(kaddr_t); ++int kl_valid_physmem(kaddr_t, int); ++kaddr_t kl_next_valid_physaddr(kaddr_t); ++kaddr_t kl_fix_vaddr(kaddr_t, size_t); ++int kl_init_virtop(void); ++ ++#endif /* __KL_MEM_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_mem_ia64.h +@@ -0,0 +1,149 @@ ++/* ++ * $Id: kl_mem_ia64.h 1250 2006-04-18 18:23:44Z cliffpwickman $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_MEM_IA64_H ++#define __KL_MEM_IA64_H ++ ++/* XXX - the following macros are used by functions in kl_page.c and in */ ++/* function kl_virtop, they still have to be defined properly, */ ++/* all the following macros have first to be set with correct values. */ ++/* I don't have a clue what values to use for ia64 architecture!!! */ ++ ++/* KSTACK_SIZE depends on page size (see kernel headers ptrace.h and page.h) ++ * #define IA64_STK_OFFSET ((1 << IA64_TASK_STRUCT_LOG_NUM_PAGES)*PAGE_SIZE) ++ * and ++ * #define PAGE_SIZE 1UL<= KL_HIGH_MEMORY))) ++ ++uint32_t dha_num_cpus_ia64(void); ++kaddr_t dha_current_task_ia64(int); ++int dha_cpuid_ia64(kaddr_t); ++kaddr_t dha_stack_ia64(int); ++kaddr_t dha_stack_ptr_ia64(int); ++kaddr_t kl_kernelstack_ia64(kaddr_t); ++kaddr_t kl_mmap_virtop_ia64(kaddr_t, void*); ++int kl_init_virtop_ia64(void); ++int kl_virtop_ia64(kaddr_t, void*, kaddr_t*); ++int kl_vtop_ia64(kaddr_t, kaddr_t*); ++int kl_valid_physmem_ia64(kaddr_t, int); ++kaddr_t kl_next_valid_physaddr_ia64(kaddr_t); ++kaddr_t kl_fix_vaddr_ia64(kaddr_t, size_t); ++ ++/* Structure containing key data for ia64 virtual memory mapping. ++ * Note that a number of fields are SN system specific. ++ */ ++typedef struct ia64_vminfo_s { ++ int flags; ++ kaddr_t vpernode_base; ++ kaddr_t vglobal_base; ++ kaddr_t to_phys_mask; ++ kaddr_t kernphysbase; ++ int nasid_shift; /* SN specific */ ++ int nasid_mask; /* SN specific */ ++} ia64_vminfo_t; ++ ++extern ia64_vminfo_t ia64_vminfo; ++ ++/* Some vminfo flags ++ */ ++#define MAPPED_KERN_FLAG 0x1 ++#define SN2_FLAG 0x2 ++ ++/* Some vminfo macros ++ */ ++#define IS_MAPPED_KERN (ia64_vminfo.flags & MAPPED_KERN_FLAG) ++#define IS_SN2 (ia64_vminfo.flags & SN2_FLAG) ++#define KL_VPERNODE_BASE ia64_vminfo.vpernode_base ++#define KL_VGLOBAL_BASE ia64_vminfo.vglobal_base ++#define KL_TO_PHYS_MASK ia64_vminfo.to_phys_mask ++#define KL_KERNPHYSBASE ia64_vminfo.kernphysbase ++#define KL_NASID_SHIFT ia64_vminfo.nasid_shift ++#define KL_NASID_MASK ia64_vminfo.nasid_mask ++ ++#define ADDR_TO_NASID(A) (((A) >> (long)(KL_NASID_SHIFT)) & KL_NASID_MASK) ++ ++#endif /* __KL_MEM_IA64_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_module.h +@@ -0,0 +1,69 @@ ++/* ++ * $Id: kl_module.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_MODULE_H ++#define __KL_MODULE_H ++ ++/* ++ * insmod generates ksymoops ++ * ++ */ ++ ++typedef struct kl_modinfo_s { ++ char *modname; /* name of module as loaded in dump */ ++ /* store ksym info for all modules in a linked list */ ++ struct kl_modinfo_s *next; ++ char *object_file; /* name of file that module was loaded from*/ ++ /* ? possibly store modtime and version here ? */ ++ uint64_t header; /* address of module header */ ++ uint64_t mtime; /* time of last modification of object_file */ ++ uint32_t version; /* kernel version that module was compiled for */ ++ uint64_t text_sec; /* address of text section */ ++ uint64_t text_len; /* length of text section */ ++ uint64_t data_sec; /* address of data section */ ++ uint64_t data_len; /* length of data section */ ++ uint64_t rodata_sec; /* address of rodata section */ ++ uint64_t rodata_len; /* length of rodata section */ ++ uint64_t bss_sec; /* address of rodata section */ ++ uint64_t bss_len; /* length of rodata section */ ++ char *ksym_object; /* ksym for object */ ++ char *ksym_text_sec; /* ksym for its text section */ ++ char *ksym_data_sec; /* ksym for its data section */ ++ char *ksym_rodata_sec; /* ksym for its rodata section */ ++ char *ksym_bss_sec; /* ksym for its bss sectio */ ++} kl_modinfo_t; ++ ++int kl_get_module(char*, kaddr_t*, void**); ++int kl_get_module_2_6(char*, kaddr_t*, void**); ++int kl_get_modname(char**, void*); ++int kl_new_get_modname(char**, void*); ++void kl_free_modinfo(kl_modinfo_t**); ++int kl_new_modinfo(kl_modinfo_t**, void*); ++int kl_set_modinfo(kaddr_t, char*, kl_modinfo_t*); ++int kl_complete_modinfo(kl_modinfo_t*); ++int kl_load_ksyms(int); ++int kl_load_ksyms_2_6(int); ++int kl_unload_ksyms(void); ++int kl_load_module_sym(char*, char*, char*); ++int kl_unload_module_sym(char*); ++int kl_autoload_module_info(char*); ++kl_modinfo_t * kl_lkup_modinfo(char*); ++ ++#endif /* __KL_MODULE_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_queue.h +@@ -0,0 +1,89 @@ ++/* ++ * $Id: kl_queue.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libutil. ++ * A library which provides auxiliary functions. ++ * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_QUEUE_H ++#define __KL_QUEUE_H ++ ++/* List element header ++ */ ++typedef struct element_s { ++ struct element_s *next; ++ struct element_s *prev; ++} element_t; ++ ++/* Some useful macros ++ */ ++#define ENQUEUE(list, elem) \ ++ kl_enqueue((element_t **)list, (element_t *)elem) ++#define DEQUEUE(list) kl_dequeue((element_t **)list) ++#define FINDQUEUE(list, elem) \ ++ kl_findqueue((element_t **)list, (element_t *)elem) ++#define REMQUEUE(list, elem) kl_remqueue((element_t **)list, (element_t *)elem) ++ ++typedef struct list_of_ptrs { ++ element_t elem; ++ unsigned long long val64; ++} list_of_ptrs_t; ++ ++#define FINDLIST_QUEUE(list, elem, compare) \ ++ kl_findlist_queue((list_of_ptrs_t **)list, \ ++ (list_of_ptrs_t *)elem, compare) ++ ++/** ++ ** Function prototypes ++ **/ ++ ++/* Add a new element to the tail of a doubly linked list. ++ */ ++void kl_enqueue( ++ element_t** /* ptr to head of list */, ++ element_t* /* ptr to element to add to the list */); ++ ++/* Remove an element from the head of a doubly linked list. A pointer ++ * to the element will be returned. In the event that the list is ++ * empty, a NULL pointer will be returned. ++ */ ++element_t *kl_dequeue( ++ element_t** /* ptr to list head (first item removed) */); ++ ++/* Checks to see if a particular element is in a list. If it is, a ++ * value of one (1) will be returned. Otherwise, a value of zero (0) ++ * will be returned. ++ */ ++int kl_findqueue( ++ element_t** /* ptr to head of list */, ++ element_t* /* ptr to element to find on list */); ++ ++/* Walks through a list of pointers to queues and looks for a ++ * particular list. ++ */ ++int kl_findlist_queue( ++ list_of_ptrs_t** /* ptr to list of lists */, ++ list_of_ptrs_t* /* ptr to list to look for */, ++ int(*)(void *, void *) /* ptr to compare function */); ++ ++/* Remove specified element from doubly linked list. ++ */ ++void kl_remqueue( ++ element_t** /* ptr to head of list */, ++ element_t* /* ptr to element to remove from list */); ++ ++#endif /* __KL_QUEUE_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_stabs.h +@@ -0,0 +1,122 @@ ++/* ++ * $Id: kl_stabs.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2004 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_STABS_H ++#define __KL_STABS_H ++ ++/* STABS specific types ++ */ ++#define STAB_XSTRUCT 100 /* Cross referense to STAB_STRUCT */ ++#define STAB_XUNION 101 /* Cross referense to STAB_UNIONB */ ++#define STAB_XENUM 102 /* Cross referense to STAB_ENUM */ ++ ++/* Structure allocated for every namelist. A namelist can be either an ++ * object file (.o or executible), or it can be an archive (.a). ++ */ ++typedef struct st_nmlist_s { ++ char *sts_filename; /* disk file name */ ++ short sts_type; /* ST_OBJ or ST_AR */ ++ short sts_nfiles; /* number of source/object files */ ++} st_nmlist_t; ++ ++/* Values for type field ++ */ ++#define ST_OBJ 1 /* object file (.o or executible) */ ++#define ST_AR 2 /* archive */ ++ ++/* Stab entry type Flags. For determining which stab entries to ++ * capture from the symbol table. ++ */ ++#define ST_UNDF 0x0001 ++#define ST_SO 0x0002 ++#define ST_LSYM 0x0004 ++#define ST_GSYM 0x0008 ++#define ST_PSYM 0x0010 ++#define ST_STSYM 0x0020 ++#define ST_LCSYM 0x0040 ++#define ST_FUN 0x0080 ++#define ST_BINCL 0x0100 ++#define ST_EINCL 0x0200 ++#define ST_EXCL 0x0400 ++#define ST_SLINE 0x0800 ++#define ST_RSYM 0x2000 ++#define ST_ALL 0xffff ++#define ST_DEFAULT (ST_LSYM|ST_GSYM|ST_FUN) ++ ++#define N_UNDF 0 ++ ++/* Structures that allow us to selectively cycle through only those BFD ++ * sections containing STAB data. ++ */ ++typedef struct stab_sect_s { ++ char *stabsect_name; ++ char *strsect_name; ++} stab_sect_t; ++ ++/* Local structure that contains the current type string (which may be ++ * just a part of the complete type defenition string) and the character ++ * index (current) pointer. ++ */ ++typedef struct stab_str_s { ++ char *str; ++ char *ptr; ++} stab_str_t; ++ ++/* Local structure containing global values that allow us to cycle ++ * through multiple object files without reinitializing. ++ */ ++typedef struct st_global_s { ++ bfd *abfd; /* current bfd pointer */ ++ int type; /* symbol entry type */ ++ int flags; /* want flags */ ++ int flag; /* current ST flag */ ++ int nmlist; /* current namelist index */ ++ int srcfile; /* current source file number */ ++ int incfile; /* current include file */ ++ int symnum; /* symbol entry number */ ++ bfd_byte *stabp; /* beg of current string table */ ++ bfd_byte *stabs_end; /* end of current string table */ ++ int staboff; /* current stab table offset */ ++ unsigned int value; /* value (e.g., function addr) */ ++ int stroffset; /* offset in stab string table */ ++ short desc; /* desc value (e.g, line number) */ ++ stab_str_t stab_str; /* current stab string */ ++} st_global_t; ++ ++/* Macros for accessing the current global values ++ */ ++#define G_abfd G_values.abfd ++#define G_type G_values.type ++#define G_flags G_values.flags ++#define G_flag G_values.flag ++#define G_nmlist G_values.nmlist ++#define G_srcfile G_values.srcfile ++#define G_incfile G_values.incfile ++#define G_symnum G_values.symnum ++#define G_stabp G_values.stabp ++#define G_stabs_end G_values.stabs_end ++#define G_staboff G_values.staboff ++#define G_value G_values.value ++#define G_stroffset G_values.stroffset ++#define G_desc G_values.desc ++#define G_stab_str G_values.stab_str ++#define CUR_CHAR G_stab_str.ptr ++ ++#endif /* __KL_STABS_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_stringtab.h +@@ -0,0 +1,68 @@ ++/* ++ * $Id: kl_stringtab.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libutil. ++ * A library which provides auxiliary functions. ++ * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_STRINGTAB_H ++#define __KL_STRINGTAB_H ++ ++/* The string table structure ++ * ++ * String space is allocated from 4K blocks which are allocated ++ * as needed. The first four bytes of each block are reserved so ++ * that the blocks can be chained together (to make it easy to free ++ * them when the string table is no longer necessary). ++ */ ++typedef struct string_table_s { ++ int num_strings; ++ void *block_list; ++} string_table_t; ++ ++#define NO_STRINGTAB 0 ++#define USE_STRINGTAB 1 ++ ++/** ++ ** Function prototypes ++ **/ ++ ++/* Initialize a string table. Depending on the value of the flag ++ * parameter, either temporary or permenent blocks will be used. ++ * Upon success, a pointer to a string table will be returned. ++ * Otherwise, a NULL pointer will be returned. ++ */ ++string_table_t *kl_init_string_table( ++ int /* flag (K_TEMP/K_PERM)*/); ++ ++/* Free all memory blocks allocated for a particular string table ++ * and then free the table itself. ++ */ ++void kl_free_string_table( ++ string_table_t* /* pointer to string table */); ++ ++/* Search for a string in a string table. If the string does not ++ * exist, allocate space from the string table and add the string. ++ * In either event, a pointer to the string (from the table) will ++ * be returned. ++ */ ++char *kl_get_string( ++ string_table_t* /* pointer to string table */, ++ char* /* string to get/add from/to string table */, ++ int /* flag (K_TEMP/K_PERM)*/); ++ ++#endif /* __KL_STRINGTAB_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_sym.h +@@ -0,0 +1,131 @@ ++/* ++ * $Id: kl_sym.h 1233 2005-09-10 08:01:11Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_SYM_H ++#define __KL_SYM_H ++ ++/* The syment struct contains information about kernel symbols (text, ++ * data, etc.). The first field in syment_t is a btnode_s sruct. This ++ * allows the generic binary search tree routines, insert_tnode() and ++ * find_tnode(), to be used. ++ */ ++typedef struct syment_s { ++ btnode_t s_bt; /* Must be first */ ++ struct syment_s *s_next; /* For linked lists */ ++ struct syment_s *s_prev; /* For linked lists */ ++ kaddr_t s_addr; /* vaddr of symbol */ ++ kaddr_t s_end; /* end address of symbol */ ++ int s_type; /* text, data */ ++ struct syment_s *s_forward; /* For linked lists */ ++} syment_t; ++ ++#define s_name s_bt.bt_key ++ ++#define SYM_GLOBAL_TEXT 1 ++#define SYM_LOCAL_TEXT 2 ++#define SYM_LOCORE_TEXT 3 ++#define SYM_GLOBAL_DATA 4 ++#define SYM_LOCAL_DATA 5 ++#define SYM_ABS 6 ++#define SYM_UNK 9 ++#define SYM_KSYM 10 ++#define SYM_KSYM_TEXT 11 ++#define SYM_KALLSYMS 12 ++ ++#define SYM_MAP_ANY 0 ++#define SYM_MAP_FILE 1 ++#define SYM_MAP_KSYM 2 ++#define SYM_MAP_MODULE 3 ++#define SYM_MAP_KALLSYMS 4 ++ ++#define KL_KERNEL_MODULE "kernel_module" ++#define KL_S_BSS ".bss.start" ++#define KL_E_BSS ".bss.end" ++#define KL_S_DATA ".data.start" ++#define KL_E_DATA ".data.end" ++#define KL_S_RODATA ".rodata.start" ++#define KL_E_RODATA ".rodata.end" ++#define KL_S_TEXT ".text.start" ++#define KL_E_TEXT ".text.end" ++#define KL_SYM_END "__end__" ++ ++ ++#define KL_SYMBOL_NAME_LEN 256 ++ ++/* ++ * Struct containing symbol table information ++ */ ++typedef struct symtab_s { ++ int symcnt; /* Number of symbols */ ++ int symaddrcnt; /* Number of symbol addrs to track */ ++ syment_t **symaddrs; /* Table of symbols by address */ ++ btnode_t *symnames; /* tree of symbols by name */ ++ syment_t *text_list; /* Linked list of text symbols */ ++ syment_t *data_list; /* Linked list of data symbols */ ++} symtab_t; ++ ++ ++/* support of further mapfiles besides System.map */ ++typedef struct maplist_s { ++ struct maplist_s *next; ++ int maplist_type; /* type of maplist */ ++ char *mapfile; /* name of mapfile */ ++ char *modname; /* set if map belongs to a module */ ++ symtab_t *syminfo; ++} maplist_t; ++ ++ ++/* API Function prototypes ++ */ ++int kl_read_syminfo(maplist_t*); ++int kl_free_syminfo(char*); ++void kl_free_symtab(symtab_t*); ++void kl_free_syment_list(syment_t*); ++void kl_free_maplist(maplist_t*); ++syment_t *kl_get_similar_name(char*, char*, int*, int*); ++syment_t *kl_lkup_symname(char*); ++syment_t *_kl_lkup_symname(char*, int, size_t len); ++#define KL_LKUP_SYMNAME(NAME, TYPE, LEN) _kl_lkup_symname(NAME, TYPE, LEN) ++syment_t *kl_lkup_funcaddr(kaddr_t); ++syment_t *kl_lkup_symaddr(kaddr_t); ++syment_t *kl_lkup_symaddr_text(kaddr_t); ++syment_t *_kl_lkup_symaddr(kaddr_t, int); ++#define KL_LKUP_SYMADDR(KADDR, TYPE) _kl_lkup_symaddr(KADDR, TYPE) ++kaddr_t kl_symaddr(char * /* symbol name */); ++kaddr_t kl_symptr(char * /* symbol name */); ++kaddr_t kl_funcaddr(kaddr_t /* pc value */); ++char *kl_funcname(kaddr_t /* pc value */); ++int kl_funcsize(kaddr_t /* pc value */); ++int kl_symsize(syment_t*); ++syment_t *kl_alloc_syment(kaddr_t, kaddr_t, int, const char*); ++void kl_insert_symbols(symtab_t*, syment_t*); ++int kl_insert_artificial_symbols(symtab_t*, syment_t**, kl_modinfo_t*); ++int kl_convert_symbol(kaddr_t*, int*, char, kl_modinfo_t*); ++int kl_load_sym(char*); ++int kl_print_symtables(char*, char*, int, int); ++void kl_print_symbol(kaddr_t, syment_t*, int); ++ ++/* flag for use by kl_print_symbol() and kl_print_syminfo() ++ */ ++#define KL_SYMWOFFSET (0x01) /* with offset field */ ++#define KL_SYMFULL (0x02) /* print detailed syminfo */ ++#define KL_SYMBYNAME (0x04) /* print symbol sorted by name */ ++ ++#endif /* __KL_SYM_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_task.h +@@ -0,0 +1,39 @@ ++/* ++ * $Id: kl_task.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002, 2004 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_TASK_H ++#define __KL_TASK_H ++ ++extern kaddr_t deftask; ++ ++/* Function prototypes ++ */ ++k_error_t kl_set_deftask(kaddr_t); ++int kl_parent_pid(void *); ++kaddr_t kl_pid_to_task(kaddr_t); ++k_error_t kl_get_task_struct(kaddr_t, int, void *); ++kaddr_t kl_kernelstack(kaddr_t); ++kaddr_t kl_first_task(void); ++kaddr_t kl_next_task(void *); ++kaddr_t kl_prev_task(void *); ++kaddr_t kl_pid_to_task(kaddr_t); ++int kl_task_size(kaddr_t); ++ ++#endif /* __KL_TASK_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_typeinfo.h +@@ -0,0 +1,199 @@ ++/* ++ * $Id: kl_typeinfo.h 1259 2006-04-25 18:33:20Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2006 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_TYPEINFO_H ++#define __KL_TYPEINFO_H ++ ++#define KLT_BASE 0x001 ++#define KLT_STRUCT 0x002 ++#define KLT_UNION 0x004 ++#define KLT_ENUMERATION 0x008 ++#define KLT_MEMBER 0x010 ++#define KLT_ARRAY 0x020 ++#define KLT_POINTER 0x040 ++#define KLT_TYPEDEF 0x080 ++#define KLT_FUNCTION 0x100 ++#define KLT_VARIABLE 0x200 ++#define KLT_SRCFILE 0x400 ++#define KLT_SUBRANGE 0x800 ++#define KLT_INCOMPLETE 0x4000 ++#define KLT_UNKNOWN 0x8000 ++#define KLT_TYPE (KLT_BASE|KLT_STRUCT|KLT_UNION|KLT_ENUMERATION) ++#define KLT_TYPES (KLT_BASE|KLT_STRUCT|KLT_UNION|KLT_ENUMERATION|KLT_TYPEDEF) ++ ++#define IS_TYPE(T) ((T) & KLT_TYPE) ++#define IS_STRUCT(T) ((T) & KLT_STRUCT) ++#define IS_UNION(T) ((T) & KLT_UNION) ++#define IS_ENUM(T) ((T) & KLT_ENUM) ++#define IS_MEMBER(T) ((T) & KLT_MEMBER) ++#define IS_POINTER(T) ((T) & KLT_POINTER) ++#define IS_TYPEDEF(T) ((T) & KLT_TYPEDEF) ++ ++#define TYP_SETUP_FLG 0x01 ++#define TYP_TYPESTRING_FLG 0x02 ++#define TYP_INCOMPLETE_FLG 0x04 ++#define TYP_XREFERENCE_FLG 0x08 ++#define TYP_ANONYMOUS_FLG 0x10 /* Denotes anonymous union or struct */ ++ ++#define NO_INDENT 0x01000000 ++#define SUPPRESS_NAME 0x02000000 ++#define SUPPRESS_NL 0x04000000 ++#define SUPPRESS_SEMI_COLON 0x08000000 ++#define NO_REALTYPE 0x10000000 ++ ++extern int numnmlist; ++ ++#define KL_TYPEINFO() (numnmlist) ++ ++typedef struct kltype_s { ++ char *kl_name; /* type name */ ++ char *kl_typestr; /* 'typecast' string */ ++ void *kl_ptr; /* ptr to arch typeinfo */ ++ int kl_flags; /* (e.g., STAB_FLG) */ ++ int kl_type; /* (e.g., KLT_TYPEDEF) */ ++ int kl_offset; /* offset to 1st byte */ ++ int kl_size; /* number of bytes */ ++ int kl_bit_offset; /* offset to 1st data bit */ ++ int kl_bit_size; /* total num of data bits */ ++ int kl_encoding; /* for base value types */ ++ int kl_low_bounds; /* for arrays */ ++ int kl_high_bounds; /* for arrays */ ++ unsigned int kl_value; /* enum value, etc. */ ++ struct kltype_s *kl_member; /* struct/union member list */ ++ struct kltype_s *kl_next; /* hash lists, etc. */ ++ struct kltype_s *kl_realtype; /* pointer to real type */ ++ struct kltype_s *kl_indextype; /* pointer to index_type */ ++ struct kltype_s *kl_elementtype; /* pointer to element_type */ ++} kltype_t; ++ ++/* Flag values ++ */ ++#define K_HEX 0x1 ++#define K_OCTAL 0x2 ++#define K_BINARY 0x4 ++#define K_NO_SWAP 0x8 ++ ++/* Base type encoding values ++ */ ++#define ENC_CHAR 0x01 ++#define ENC_SIGNED 0x02 ++#define ENC_UNSIGNED 0x04 ++#define ENC_FLOAT 0x08 ++#define ENC_ADDRESS 0x10 ++#define ENC_UNDEFINED 0x20 ++ ++/* Maximum number of open namelists ++ */ ++#define MAXNMLIST 10 ++ ++typedef struct nmlist_s { ++ int index; ++ char *namelist; ++ void *private; /* pointer to private control struct */ ++ string_table_t *stringtab; ++} nmlist_t; ++ ++extern nmlist_t nmlist[]; ++extern int numnmlist; ++extern int curnmlist; ++ ++#define KL_TYPESTR_STRUCT "struct" ++#define KL_TYPESTR_UNION "union" ++#define KL_TYPESTR_ENUM "enum" ++#define KL_TYPESTR_VOID "void" ++ ++/* Function prototypes ++ */ ++kltype_t *kl_find_type( ++ char * /* type name */, ++ int /* type number */); ++ ++kltype_t *kl_find_next_type( ++ kltype_t * /* kltype_t pointer */, ++ int /* type number */); ++ ++kltype_t *kl_first_type( ++ int /* type number */); ++ ++kltype_t *kl_next_type( ++ kltype_t * /* kltype_t pointer */); ++ ++kltype_t *kl_prev_type( ++ kltype_t * /* kltype_t pointer */); ++ ++kltype_t *kl_realtype( ++ kltype_t * /* kltype_t pointer */, ++ int /* type number */); ++ ++kltype_t *kl_find_typenum( ++ uint64_t /* private typenumber */); ++ ++int kl_get_first_similar_typedef( ++ char * /* type name */, ++ char * /* fullname */); ++ ++int kl_type_size( ++ kltype_t * /* kltype_t pointer */); ++ ++int kl_struct_len( ++ char * /* struct name */); ++ ++kltype_t *kl_get_member( ++ kltype_t * /* kltype_t pointer */, ++ char * /* member name */); ++ ++int kl_get_member_offset( ++ kltype_t * /* kltype_t pointer */, ++ char * /* member name */); ++ ++int kl_is_member( ++ char * /* struct name */, ++ char * /* member name */); ++ ++kltype_t *kl_member( ++ char * /* struct name */, ++ char * /* member name */); ++ ++int kl_member_offset( ++ char * /* struct name */, ++ char * /* member name */); ++ ++int kl_member_size( ++ char * /* struct name */, ++ char * /* member name */); ++ ++/* cpw: get rid of last arguent FILE * */ ++void kl_print_member(void *, kltype_t *, int, int); ++void kl_print_pointer_type(void *, kltype_t *, int, int); ++void kl_print_function_type(void *, kltype_t *, int, int); ++void kl_print_array_type(void *, kltype_t *, int, int); ++void kl_print_enumeration_type(void *, kltype_t *, int, int); ++void kl_print_base_type(void *, kltype_t *, int, int); ++void kl_print_type(void *, kltype_t *, int, int); ++void kl_print_struct_type(void *, kltype_t *, int, int); ++void kl_print_base_value(void *, kltype_t *, int); ++ ++void kl_print_type( ++ void * /* pointer to data */, ++ kltype_t * /* pointer to type information */, ++ int /* indent level */, ++ int /* flags */); ++ ++#endif /* __KL_TYPEINFO_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/kl_types.h +@@ -0,0 +1,54 @@ ++/* ++ * $Id: kl_types.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __KL_TYPES_H ++#define __KL_TYPES_H ++ ++/* The following typedef should be used for variables or return values ++ * that contain kernel virtual or physical addresses. It should be sized ++ * such that it can hold both pointers of 64 bit architectures as well as ++ * pointers from 32 bit architectures. ++ */ ++typedef unsigned long kaddr_t; ++ ++/* The following typedef should be used when converting a pointer value ++ * (either kernel or application) to an unsigned value for pointer ++ * calculations. ++ */ ++typedef unsigned long uaddr_t; ++ ++/* KLIB error type ++ */ ++typedef uint64_t k_error_t; ++ ++/* Typedef that allows a single fprintf() call to work for both ++ * 32-bit and 64-bit pointer values. ++ */ ++#define UADDR(X) ((kaddr_t)X) ++#define UADDR64(X) ((kaddr_t)X)) ++/* #define UADDR(X) ((uaddr_t)X) */ ++/* #define UADDR64(X) ((uint64_t)((uaddr_t)X)) */ ++ ++ ++/* cpw */ ++/* was: #include */ ++#include "asm/kl_types.h" ++ ++#endif /* __KL_TYPES_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/klib.h +@@ -0,0 +1,480 @@ ++/* ++ * $Id: klib.h 1336 2006-10-23 23:27:06Z tjm $ ++ * ++ * This file is part of libklib. ++ * A library which provides access to Linux system kernel dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, NEC, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * Copyright 2000 Junichi Nomura, NEC Solutions ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++/* ++ * klib.h -- Interface of the klib library, a library for access to ++ * Linux system memory dumps. ++ */ ++ ++#ifndef __KLIB_H ++#define __KLIB_H ++ ++/* Include header files ++ */ ++#if 0 ++ /* cpw: don't include all this: */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#endif ++ ++/* cpw: change all the below includes form the < > form to " " */ ++ ++/* Include libutil header ++ */ ++#include "kl_lib.h" ++ ++/* Include libklib header files ++ */ ++#include "kl_types.h" ++#include "kl_error.h" ++#include "kl_dump.h" ++#include "kl_mem.h" ++#include "kl_cmp.h" ++#include "kl_typeinfo.h" ++#include "kl_module.h" ++#include "kl_sym.h" ++#include "kl_bfd.h" ++#include "kl_debug.h" ++#include "kl_stabs.h" ++#include "kl_dwarfs.h" ++#include "kl_task.h" ++#include "kl_dump_arch.h" ++ ++ ++#ifndef TRUE ++# define TRUE 1 ++#endif ++#ifndef FALSE ++# define FALSE 0 ++#endif ++ ++#ifndef MIN ++#define MIN(x,y) (((x)<(y))?(x):(y)) ++#endif ++#ifndef MAX ++#define MAX(x,y) (((x)>(y))?(x):(y)) ++#endif ++ ++#define KL_NR_CPUS 128 ++ ++/* We have to distinc between HOST_ARCH_* and DUMP_ARCH_*. These two classes of ++ * macros are used througout the code for conditional compilation. ++ * Additional we have following macros for comparison and switch statements. ++ */ ++#define KL_ARCH_UNKNOWN 0 ++#define KL_ARCH_ALPHA 1 ++#define KL_ARCH_ARM 2 ++#define KL_ARCH_I386 3 ++#define KL_ARCH_IA64 4 ++#define KL_ARCH_M68K 5 ++#define KL_ARCH_MIPS 6 ++#define KL_ARCH_MIPS64 7 ++#define KL_ARCH_PPC 8 ++#define KL_ARCH_S390 9 ++#define KL_ARCH_SH 10 ++#define KL_ARCH_SPARK 11 ++#define KL_ARCH_SPARK64 12 ++#define KL_ARCH_S390X 13 ++#define KL_ARCH_PPC64 14 ++#define KL_ARCH_X86_64 15 ++#define KL_ARCH_IA64_SN2 16 ++#define KL_ARCH_IA64_DIG 17 ++#define KL_ARCH_IA64_HPSIM 18 ++#define KL_ARCH_IA64_HPZX1 19 ++#define KL_ARCH_S390SA 20 ++ ++#define KL_LIVE_SYSTEM 1000 ++ ++#define ARCH_IS_IA64(A) \ ++ ((A==KL_ARCH_IA64)|| \ ++ (A==KL_ARCH_IA64_SN2)|| \ ++ (A==KL_ARCH_IA64_DIG)|| \ ++ (A==KL_ARCH_IA64_HPSIM)|| \ ++ (A==KL_ARCH_IA64_HPZX1)) ++ ++#ifdef HOST_ARCH_ALPHA ++# define KL_HOST_ARCH KL_ARCH_ALPHA ++#endif ++#ifdef HOST_ARCH_ARM ++# define KL_HOST_ARCH KL_ARCH_ARM ++#endif ++#ifdef HOST_ARCH_I386 ++# define KL_HOST_ARCH KL_ARCH_I386 ++#endif ++#ifdef HOST_ARCH_IA64 ++# define KL_HOST_ARCH KL_ARCH_IA64 ++#endif ++#ifdef HOST_ARCH_S390 ++# define KL_HOST_ARCH KL_ARCH_S390 ++#endif ++#ifdef HOST_ARCH_S390X ++# define KL_HOST_ARCH KL_ARCH_S390X ++#endif ++#ifdef HOST_ARCH_PPC64 ++#define KL_HOST_ARCH KL_ARCH_PPC64 ++#endif ++#ifdef HOST_ARCH_X86_64 ++#define KL_HOST_ARCH KL_ARCH_X86_64 ++#endif ++ ++#define KL_ARCH_STR_ALPHA "alpha" ++#define KL_ARCH_STR_ARM "arm" ++#define KL_ARCH_STR_I386 "i386" ++#define KL_ARCH_STR_IA64 "ia64" ++#define KL_ARCH_STR_S390 "s390" ++#define KL_ARCH_STR_S390X "s390x" ++#define KL_ARCH_STR_PPC64 "ppc64" ++#define KL_ARCH_STR_X86_64 "x86_64" ++#define KL_ARCH_STR_IA64_SN2 "sn2" ++#define KL_ARCH_STR_UNKNOWN "unknown" ++ ++/* for endianess of dump and host arch ++ */ ++#define KL_UNKNOWN_ENDIAN 0x00 ++#define KL_LITTLE_ENDIAN 0x01 ++#define KL_BIG_ENDIAN 0x02 ++ ++/* macros for handling of different Kernel versions ++ */ ++#define LINUX_2_2_X(R) (((R) & 0xffff00) == 0x020200) ++#define LINUX_2_2_16 0x020210 ++#define LINUX_2_2_17 0x020211 ++#define LINUX_2_4_X(R) (((R) & 0xffff00) == 0x020400) ++#define LINUX_2_4_0 0x020400 ++#define LINUX_2_4_4 0x020404 ++#define LINUX_2_4_15 0x02040f ++#define LINUX_2_6_X(R) (((R) & 0xffff00) == 0x020600) ++#define LINUX_2_6_0 0x020600 ++ ++/* libklib flags ++ */ ++#define KL_FAILSAFE_FLG 0x0001 ++#define KL_NOVERIFY_FLG 0x0002 ++#define KL_SILENT_FLG 0x0004 ++#define KL_SAVETYPES_FLG 0x0008 ++#define KL_USETYPES_FLG 0x0010 ++ ++/* macros for backward compatibility ++ */ ++#define NUM_PHYSPAGES KLP->dump->mem.num_physpages ++#define MEM_MAP KLP->dump->mem.mem_map ++#define KL_HIGH_MEMORY KLP->dump->mem.high_memory ++#define KL_INIT_MM KLP->dump->mem.init_mm ++#define KL_NUM_CPUS KLP->dump->mem.num_cpus ++#define KL_PGDAT_LIST KLP->dump->mem.pgdat_list ++ ++/* macros for better use of dump architecture dependent functions ++ */ ++ ++/* read integer value from buffer */ ++#define KL_GET_PTR(ptr) (*KLP->dump->func.get_ptr)(ptr) ++#define KL_GET_LONG(ptr) ((int64_t) KL_GET_PTR(ptr)) ++#define KL_GET_ULONG(ptr) KL_GET_PTR(ptr) ++#define KL_GET_UINT8(ptr) (*KLP->dump->func.get_uint8)(ptr) ++#define KL_GET_UINT16(ptr) (*KLP->dump->func.get_uint16)(ptr) ++#define KL_GET_UINT32(ptr) (*KLP->dump->func.get_uint32)(ptr) ++#define KL_GET_UINT64(ptr) (*KLP->dump->func.get_uint64)(ptr) ++#define KL_GET_INT8(ptr) ((int8_t) KL_GET_UINT8(ptr)) ++#define KL_GET_INT16(ptr) ((int16_t) KL_GET_UINT16(ptr)) ++#define KL_GET_INT32(ptr) ((int32_t) KL_GET_UINT32(ptr)) ++#define KL_GET_INT64(ptr) ((int64_t) KL_GET_UINT64(ptr)) ++ ++/* read integer value from dump (without address mapping) ++ * Use these functions sparsely, e.g. before address translation ++ * is properly set up. ++ */ ++#define KL_READ_PTR(addr) (*KLP->dump->func.read_ptr)(addr) ++#define KL_READ_LONG(addr) ((int64_t) KL_READ_PTR(addr)) ++#define KL_READ_ULONG(addr) KL_READ_PTR(addr) ++#define KL_READ_UINT8(addr) (*KLP->dump->func.read_uint8)(addr) ++#define KL_READ_UINT16(addr) (*KLP->dump->func.read_uint16)(addr) ++#define KL_READ_UINT32(addr) (*KLP->dump->func.read_uint32)(addr) ++#define KL_READ_UINT64(addr) (*KLP->dump->func.read_uint64)(addr) ++#define KL_READ_INT8(addr) ((int8_t) KL_READ_UINT8(addr)) ++#define KL_READ_INT16(addr) ((int16_t) KL_READ_UINT16(addr)) ++#define KL_READ_INT32(addr) ((int32_t) KL_READ_UINT32(addr)) ++#define KL_READ_INT64(addr) ((int64_t) KL_READ_UINT64(addr)) ++ ++/* read integer value from dump (from virtual address) doing address mapping */ ++#define KL_VREAD_PTR(addr) (*KLP->dump->func.vread_ptr)(addr) ++#define KL_VREAD_LONG(addr) ((int64_t) KL_VREAD_PTR(addr)) ++#define KL_VREAD_ULONG(addr) KL_VREAD_PTR(addr) ++#define KL_VREAD_UINT8(addr) (*KLP->dump->func.vread_uint8)(addr) ++#define KL_VREAD_UINT16(addr) (*KLP->dump->func.vread_uint16)(addr) ++#define KL_VREAD_UINT32(addr) (*KLP->dump->func.vread_uint32)(addr) ++#define KL_VREAD_UINT64(addr) (*KLP->dump->func.vread_uint64)(addr) ++#define KL_VREAD_INT8(addr) ((int8_t) KL_VREAD_UINT8(addr)) ++#define KL_VREAD_INT16(addr) ((int16_t) KL_VREAD_UINT16(addr)) ++#define KL_VREAD_INT32(addr) ((int32_t) KL_VREAD_UINT32(addr)) ++#define KL_VREAD_INT64(addr) ((int64_t) KL_VREAD_UINT64(addr)) ++ ++/* determine start of stack */ ++#define KL_KERNELSTACK_UINT64 (*KLP->dump->arch.kernelstack) ++/* map virtual adress to physical one */ ++#define KL_VIRTOP (*KLP->dump->arch.virtop) ++/* travers page table */ ++#define KL_MMAP_VIRTOP (*KLP->dump->arch.mmap_virtop) ++/* check whether address points to valid physical memory */ ++#define KL_VALID_PHYSMEM (*KLP->dump->arch.valid_physmem) ++/* determine next valid physical address */ ++#define KL_NEXT_VALID_PHYSADDR (*KLP->dump->arch.next_valid_physaddr) ++/* XXX */ ++#define KL_FIX_VADDR (*KLP->dump->arch.fix_vaddr) ++/* write dump_header_asm_t */ ++#define KL_WRITE_DHA (*KLP->dump->arch.write_dha) ++/* size of dump_header_asm_t */ ++#define KL_DHA_SIZE (KLP->dump->arch.dha_size) ++/* init virtual to physical address mapping */ ++#define KL_INIT_VIRTOP (KLP->dump->arch.init_virtop) ++ ++ ++/* macros for easier access to dump specific values */ ++#define KL_CORE_TYPE KLP->dump->core_type ++#define KL_CORE_FD KLP->dump->core_fd ++#define KL_ARCH KLP->dump->arch.arch ++#define KL_PTRSZ KLP->dump->arch.ptrsz ++#define KL_NBPW (KL_PTRSZ/8) ++#define KL_BYTE_ORDER KLP->dump->arch.byteorder ++#define KL_PAGE_SHIFT KLP->dump->arch.pageshift ++#define KL_PAGE_SIZE KLP->dump->arch.pagesize ++#define KL_PAGE_MASK KLP->dump->arch.pagemask ++#define KL_PAGE_OFFSET KLP->dump->arch.pageoffset ++#define KL_STACK_OFFSET KLP->dump->arch.kstacksize ++#define IS_BIG_ENDIAN() (KL_BYTE_ORDER == KL_BIG_ENDIAN) ++#define IS_LITTLE_ENDIAN() (KL_BYTE_ORDER == KL_LITTLE_ENDIAN) ++#define KL_LINUX_RELEASE KLP->dump->mem.linux_release ++#define KL_KERNEL_FLAGS KLP->dump->mem.kernel_flags ++ ++#if 0 ++/* cpw: don't need all this dump file stuff: */ ++/* macros to access input files */ ++#define KL_MAP_FILE KLP->dump->map ++#define KL_DUMP_FILE KLP->dump->dump ++#define KL_KERNTYPES_FILE KLP->kerntypes ++ ++#define CORE_IS_KMEM (KL_CORE_TYPE == dev_kmem) ++#define CORE_IS_DUMP ((KL_CORE_TYPE > dev_kmem) && (KL_CORE_TYPE <= unk_core)) ++ ++ ++/* Generic dump header structure (the first three members of ++ * dump_header and dump_header_asm are the same). ++ */ ++typedef struct generic_dump_header_s { ++ uint64_t magic_number; ++ uint32_t version; ++ uint32_t header_size; ++} generic_dump_header_t; ++ ++/* Some macros for making it easier to access the generic header ++ * information in a dump_header or dump_header_asm stuct. ++ */ ++#define DHP(dh) ((generic_dump_header_t*)(dh)) ++#define DH_MAGIC(dh) DHP(dh)->magic_number ++#define DH_VERSION(dh) DHP(dh)->version ++#define DH_HEADER_SIZE(dh) DHP(dh)->header_size ++ ++extern kl_dump_header_t *DUMP_HEADER; ++extern void *DUMP_HEADER_ASM; ++#endif ++ ++/* Struct to store some host architecture specific values ++ */ ++typedef struct kl_hostarch_s { ++ int arch; /* KL_ARCH_ */ ++ int ptrsz; /* 32 or 64 bit */ ++ int byteorder; /* KL_LITTLE_ENDIAN or KL_BIG_ENDIAN */ ++} kl_hostarch_t; ++ ++/* Struct klib_s, contains all the information necessary for accessing ++ * information in the kernel. A pointer to a klib_t struct will be ++ * returned from libkern_init() if core dump analysis (or live system ++ * analysis) is possible. ++ * ++ */ ++typedef struct klib_s { ++ int k_flags; /* Flags pertaining to klib_s struct */ ++ kl_hostarch_t *host; /* host arch info */ ++ kl_dumpinfo_t *dump; /* dump information */ ++ maplist_t *k_symmap; /* symbol information */ ++ kltype_t *k_typeinfo; /* type information */ ++ char *kerntypes; /* pathname for kerntypes file */ ++} klib_t; ++ ++/* Structure to accomodate all debug formats */ ++struct namelist_format_opns { ++ /* to open/setup the namelist file */ ++ int (*open_namelist) (char *filename , int flags); ++ int (*setup_typeinfo)(void); ++}; ++ ++/* ++ * global variables ++ */ ++ ++/* Here we store almost everything, we need to know about a dump. */ ++extern klib_t *KLP; ++ ++/* macros to make live easier */ ++#define MIP KLP->dump ++#define STP KLP->k_symmap ++#define TASK_STRUCT_SZ (KLP->dump->mem.struct_sizes.task_struct_sz) ++#define MM_STRUCT_SZ (KLP->dump->mem.struct_sizes.mm_struct_sz) ++#define PAGE_SZ (KLP->dump->mem.struct_sizes.page_sz) ++#define MODULE_SZ (KLP->dump->mem.struct_sizes.module_sz) ++#define NEW_UTSNAME_SZ (KLP->dump->mem.struct_sizes.new_utsname_sz) ++#define SWITCH_STACK_SZ (KLP->dump->mem.struct_sizes.switch_stack_sz) ++#define PT_REGS_SZ (KLP->dump->mem.struct_sizes.pt_regs_sz) ++#define PGLIST_DATA_SZ (KLP->dump->mem.struct_sizes.pglist_data_sz) ++#define RUNQUEUE_SZ (KLP->dump->mem.struct_sizes.runqueue_sz) ++ ++#if 0 ++cpw: used for sial? ++/* klib_jbuf has to be defined outside libklib. ++ * Make sure to call setjmp(klib_jbuf) BEFORE kl_sig_setup() is called! */ ++extern jmp_buf klib_jbuf; ++#endif ++ ++/* Macros that eliminate the offset paramaters to the kl_uint() and kl_int() ++ * functions (just makes things cleaner looking) ++ */ ++#define KL_UINT(p, s, m) kl_uint(p, s, m, 0) ++#define KL_INT(p, s, m) kl_int(p, s, m, 0) ++ ++/* Macros for translating strings into long numeric values depending ++ * on the base of 's'. ++ */ ++#define GET_VALUE(s, value) kl_get_value(s, NULL, 0, value) ++#define GET_HEX_VALUE(s) (kaddr_t)strtoull(s, (char**)NULL, 16) ++#define GET_DEC_VALUE(s) (unsigned)strtoull(s, (char**)NULL, 10) ++#define GET_OCT_VALUE(s) (unsigned)strtoull(s, (char**)NULL, 8) ++ ++#define KL_SIGFLG_CORE 0x1 ++#define KL_SIGFLG_SILENT 0x2 ++#define KL_SIGFLG_LNGJMP 0x4 ++ ++/* Flag that tells kl_is_valid_kaddr() to perform a word aligned check ++ */ ++#define WORD_ALIGN_FLAG 1 ++ ++#define ADDR_TO_PGNO(addr) ((addr - KL_PAGE_OFFSET) >> KL_PAGE_SHIFT); ++ ++/* Generalized macros for pointing at different data types at particular ++ * offsets in kernel structs. ++ */ ++/* #define K_ADDR(p, s, f) ((uaddr_t)(p) + kl_member_offset(s, f)) */ ++#define K_ADDR(p, s, f) ((p) + kl_member_offset(s, f)) ++#define K_PTR(p, s, f) (K_ADDR((void*)p, s, f)) ++#define CHAR(p, s, f) (K_ADDR((char*)p, s, f)) ++ ++#define PTRSZ32 ((KL_PTRSZ == 32) ? 1 : 0) ++#define PTRSZ64 ((KL_PTRSZ == 64) ? 1 : 0) ++ ++/* Function prototypes ++ */ ++/* cpw: remove the last argument FILE * */ ++void kl_binary_print(uint64_t); ++void kl_print_bit_value(void *, int, int, int, int); ++void kl_print_char(void *, int); ++void kl_print_uchar(void *, int); ++void kl_print_int2(void *, int); ++void kl_print_uint2(void *, int); ++void kl_print_int4(void *, int); ++void kl_print_uint4(void *, int); ++void kl_print_float4(void *, int); ++void kl_print_int8(void *, int); ++void kl_print_uint8(void *, int); ++void kl_print_float8(void *, int); ++void kl_print_base(void *, int, int, int); ++void kl_print_string(char *); ++ ++int kl_get_live_filenames( ++ char * /* pointer to buffer for map filename */, ++ char * /* pointer to buffer for dump filename */, ++ char * /* pointer to buffer for namelist filename */); ++ ++int kl_init_klib( ++ char * /* map file name */, ++ char * /* dump file name */, ++ char * /* namelist file name */, ++ int /* system arch of memory in dump */, ++ int /* rwflag flag (/dev/mem only) */, ++ int /* Linux release */); ++ ++void kl_free_klib( ++ klib_t * /* Pointer to klib_s struct */); ++ ++ ++int kl_dump_retrieve( ++ char * /* dumpdev name */, ++ char * /* dumpdir name */, ++ int /* progress flag (zero or non-zero) */, ++ int /* debug flag (zero or non-zero) */); ++ ++int kl_dump_erase( ++ char * /* dumpdev name */); ++ ++uint64_t kl_strtoull( ++ char * /* string containing numeric value */, ++ char ** /* pointer to pointer to bad char */, ++ int /* base */); ++ ++int kl_get_value( ++ char * /* param */, ++ int * /* mode pointer */, ++ int /* number of elements */, ++ uint64_t * /* pointer to value */); ++ ++/* Functions for working with list_head structs ++ */ ++kaddr_t kl_list_entry(kaddr_t, char *, char *); ++kaddr_t kl_list_next(kaddr_t); ++kaddr_t kl_list_prev(kaddr_t); ++ ++int kl_sig_setup(int); ++ ++void kl_set_curnmlist( ++ int /* index of namelist */); ++ ++int kl_open_namelist( ++ char * /* name of namelist */, ++ int /* flags */, ++ int /* kl_flags */); ++ ++int kl_get_structure(kaddr_t, char*, size_t*, void**); ++uint64_t kl_get_bit_value(void*, unsigned int, unsigned int, unsigned int); ++void kl_s390tod_to_timeval(uint64_t, struct timeval*); ++ ++#endif /* __KLIB_H */ +--- /dev/null ++++ b/kdb/modules/lcrash/lc_eval.h +@@ -0,0 +1,225 @@ ++/* ++ * $Id: lc_eval.h 1122 2004-12-21 23:26:23Z tjm $ ++ * ++ * This file is part of lcrash, an analysis tool for Linux memory dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, and others ++ * ++ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. See the file COPYING for more ++ * information. ++ */ ++ ++#ifndef __LC_EVAL_H ++#define __LC_EVAL_H ++ ++typedef struct type_s { ++ int flag; ++ union { ++ struct type_s *next; ++ kltype_t *kltp; ++ } un; ++} type_t; ++ ++#define t_next un.next ++#define t_kltp un.kltp ++ ++/* Structure to hold info on "tokens" extracted from eval and print ++ * command input strings. ++ */ ++typedef struct token_s { ++ short type; ++ short operator; /* if token is an operator */ ++ char *string; /* string holding value or identifier */ ++ char *ptr; /* pointer to start of token */ ++ struct token_s *next; /* next token in the chain */ ++} token_t; ++ ++/* Structure returned by the eval() function containing the result ++ * of an expression evaluation. This struct is also used to build the ++ * parse tree for the expression. ++ */ ++typedef struct node_s { ++ struct node_s *next; /* linked list pointer */ ++ unsigned char node_type; /* type of node */ ++ unsigned short flags; /* see below */ ++ unsigned char operator; /* operator if node is type OPERATOR */ ++ unsigned char byte_size; /* byte_size of base_type values */ ++ char *name; /* name of variable or struct member */ ++ /* value and address are uint64_t in lcrash, but for ia32 ... */ ++ unsigned long long value; /* numeric value or pointer */ ++ unsigned long address; /* address (could be same as pointer) */ ++ type_t *type; /* pointer to type related info */ ++ char *tok_ptr; /* pointer to token in cmd string */ ++ struct node_s *left; /* pointer to left child */ ++ struct node_s *right; /* pointer to right child */ ++} node_t; ++ ++/* Token and Node types ++ */ ++#define OPERATOR 1 ++#define NUMBER 2 ++#define INDEX 3 ++#define TYPE_DEF 4 ++#define VADDR 5 ++#define MEMBER 6 ++#define STRING 7 ++#define TEXT 8 ++#define CHARACTER 9 ++#define EVAL_VAR 10 ++ ++/* Flag values ++ */ ++#define STRING_FLAG 0x001 ++#define ADDRESS_FLAG 0x002 ++#define INDIRECTION_FLAG 0x004 ++#define POINTER_FLAG 0x008 ++#define MEMBER_FLAG 0x010 ++#define BOOLIAN_FLAG 0x020 ++#define KLTYPE_FLAG 0x040 ++#define NOTYPE_FLAG 0x080 ++#define UNSIGNED_FLAG 0x100 ++#define VOID_FLAG 0x200 ++ ++/* Flag value for print_eval_error() function ++ */ ++#define CMD_NAME_FLG 1 /* cmdname is not name of a command */ ++#define CMD_STRING_FLG 2 /* cmdname is not name of a command */ ++ ++/* Expression operators in order of precedence. ++ */ ++#define CONDITIONAL 1 ++#define CONDITIONAL_ELSE 2 ++#define LOGICAL_OR 3 ++#define LOGICAL_AND 4 ++#define BITWISE_OR 5 ++#define BITWISE_EXCLUSIVE_OR 6 ++#define BITWISE_AND 7 ++#define EQUAL 8 ++#define NOT_EQUAL 9 ++#define LESS_THAN 10 ++#define GREATER_THAN 11 ++#define LESS_THAN_OR_EQUAL 12 ++#define GREATER_THAN_OR_EQUAL 13 ++#define RIGHT_SHIFT 14 ++#define LEFT_SHIFT 15 ++#define ADD 16 ++#define SUBTRACT 17 ++#define MULTIPLY 18 ++#define DIVIDE 19 ++#define MODULUS 20 ++#define LOGICAL_NEGATION 21 ++#define ONES_COMPLEMENT 22 ++#define PREFIX_INCREMENT 23 ++#define PREFIX_DECREMENT 24 ++#define POSTFIX_INCREMENT 25 ++#define POSTFIX_DECREMENT 26 ++#define CAST 27 ++#define UNARY_MINUS 28 ++#define UNARY_PLUS 29 ++#define INDIRECTION 30 ++#define ADDRESS 31 ++#define SIZEOF 32 ++#define RIGHT_ARROW 33 ++#define DOT 34 ++#define OPEN_PAREN 100 ++#define CLOSE_PAREN 101 ++#define OPEN_SQUARE_BRACKET 102 ++#define CLOSE_SQUARE_BRACKET 103 ++#define SEMI_COLON 104 ++#define NOT_YET -1 ++ ++/* Errors codes primarily for use with eval (print) functions ++ */ ++#define E_OPEN_PAREN 1100 ++#define E_CLOSE_PAREN 1101 ++#define E_BAD_STRUCTURE 1102 ++#define E_MISSING_STRUCTURE 1103 ++#define E_BAD_MEMBER 1104 ++#define E_BAD_OPERATOR 1105 ++#define E_BAD_OPERAND 1106 ++#define E_MISSING_OPERAND 1107 ++#define E_BAD_TYPE 1108 ++#define E_NOTYPE 1109 ++#define E_BAD_POINTER 1110 ++#define E_BAD_INDEX 1111 ++#define E_BAD_CHAR 1112 ++#define E_BAD_STRING 1113 ++#define E_END_EXPECTED 1114 ++#define E_BAD_EVAR 1115 /* Bad eval variable */ ++#define E_BAD_VALUE 1116 ++#define E_NO_VALUE 1117 ++#define E_DIVIDE_BY_ZERO 1118 ++#define E_BAD_CAST 1119 ++#define E_NO_ADDRESS 1120 ++#define E_SINGLE_QUOTE 1121 ++ ++#define E_BAD_WHATIS 1197 ++#define E_NOT_IMPLEMENTED 1198 ++#define E_SYNTAX_ERROR 1199 ++ ++extern uint64_t eval_error; ++extern char *error_token; ++ ++/* Function prototypes ++ */ ++node_t *eval(char **, int); ++void print_eval_error(char *, char *, char *, uint64_t, int); ++void free_nodes(node_t *); ++ ++/* Struct to hold information about eval variables ++ */ ++typedef struct variable_s { ++ btnode_t v_bt; /* Must be first */ ++ int v_flags; ++ char *v_exp; /* What was entered on command line */ ++ char *v_typestr; /* Actual type string after eval() call */ ++ node_t *v_node; ++} variable_t; ++ ++#define v_left v_bt.bt_left ++#define v_right v_bt.bt_right ++#define v_name v_bt.bt_key ++ ++/* Flag values ++ */ ++#define V_PERM 0x001 /* can't be unset - can be modified */ ++#define V_DEFAULT 0x002 /* set at startup */ ++#define V_NOMOD 0x004 /* cannot be modified */ ++#define V_TYPEDEF 0x008 /* contains typed data */ ++#define V_REC_STRUCT 0x010 /* direct ref to struct/member (not pointer) */ ++#define V_STRING 0x020 /* contains ASCII string (no type) */ ++#define V_COMMAND 0x040 /* contains command string (no type) */ ++#define V_OPTION 0x080 /* contains option flag (e.g., $hexints) */ ++#define V_PERM_NODE 0x100 /* Don't free node after setting variable */ ++ ++/* Variable table struct ++ */ ++typedef struct vtab_s { ++ variable_t *vt_root; ++ int vt_count; ++} vtab_t; ++ ++extern vtab_t *vtab; /* Pointer to table of eval variable info */ ++ ++/* Function Prototypes ++ */ ++variable_t *make_variable(char *, char *, node_t *, int); ++void clean_variable(variable_t *); ++void free_variable(variable_t *); ++void init_variables(vtab_t *); ++int set_variable(vtab_t *, char *, char *, node_t *, int); ++int unset_variable(vtab_t *, variable_t *); ++variable_t *find_variable(vtab_t *, char *, int); ++kltype_t *number_to_type(node_t *); ++void free_eval_memory(void); ++/* cpw: was int print_eval_results(node_t *, FILE *, int); */ ++int print_eval_results(node_t *, int); ++ ++#endif /* __LC_EVAL_H */ +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -4,6 +4,9 @@ + * Copyright (C) 1991, 1992 Linus Torvalds + */ + ++#ifdef CONFIG_KDB ++#include ++#endif + #include + #include + #include +--- a/kernel/kallsyms.c ++++ b/kernel/kallsyms.c +@@ -529,3 +529,26 @@ static int __init kallsyms_init(void) + return 0; + } + device_initcall(kallsyms_init); ++ ++ ++#ifdef CONFIG_KDB ++#include ++#include ++ ++const char *kdb_walk_kallsyms(loff_t *pos) ++{ ++ static struct kallsym_iter kdb_walk_kallsyms_iter; ++ if (*pos == 0) { ++ memset(&kdb_walk_kallsyms_iter, 0, sizeof(kdb_walk_kallsyms_iter)); ++ reset_iter(&kdb_walk_kallsyms_iter, 0); ++ } ++ while (1) { ++ if (!update_iter(&kdb_walk_kallsyms_iter, *pos)) ++ return NULL; ++ ++*pos; ++ /* Some debugging symbols have no name. Ignore them. */ ++ if (kdb_walk_kallsyms_iter.name[0]) ++ return kdb_walk_kallsyms_iter.name; ++ } ++} ++#endif /* CONFIG_KDB */ +--- a/kernel/kexec.c ++++ b/kernel/kexec.c +@@ -40,6 +40,12 @@ + #include + #include + ++#ifdef CONFIG_KDB_KDUMP ++#include ++#include ++#include ++#endif ++ + /* Per cpu memory for storing cpu states in case of system crash. */ + note_buf_t __percpu *crash_notes; + +@@ -1080,7 +1086,16 @@ void crash_kexec(struct pt_regs *regs) + + crash_setup_regs(&fixed_regs, regs); + crash_save_vmcoreinfo(); ++ /* ++ * If we enabled KDB, we don't want to automatically ++ * perform a kdump since KDB will be responsible for ++ * executing kdb through a special 'kdump' command. ++ */ ++#ifdef CONFIG_KDB_KDUMP ++ kdba_kdump_prepare(&fixed_regs); ++#else + machine_crash_shutdown(&fixed_regs); ++#endif + machine_kexec(kexec_crash_image); + } + mutex_unlock(&kexec_mutex); +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -2786,12 +2786,23 @@ out: + return -ERANGE; + } + ++#ifdef CONFIG_KDB ++#include ++struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ ++#endif /* CONFIG_KDB */ ++ + int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, + char *name, char *module_name, int *exported) + { + struct module *mod; ++#ifdef CONFIG_KDB ++ int get_lock = !KDB_IS_RUNNING(); ++#else ++#define get_lock 1 ++#endif + +- preempt_disable(); ++ if (get_lock) ++ preempt_disable(); + list_for_each_entry_rcu(mod, &modules, list) { + if (symnum < mod->num_symtab) { + *value = mod->symtab[symnum].st_value; +@@ -2800,12 +2811,14 @@ int module_get_kallsym(unsigned int symn + KSYM_NAME_LEN); + strlcpy(module_name, mod->name, MODULE_NAME_LEN); + *exported = is_exported(name, *value, mod); +- preempt_enable(); ++ if (get_lock) ++ preempt_enable(); + return 0; + } + symnum -= mod->num_symtab; + } +- preempt_enable(); ++ if (get_lock) ++ preempt_enable(); + return -ERANGE; + } + +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -23,6 +23,9 @@ + #include + #include + #include ++#ifdef CONFIG_KDB_KDUMP ++#include ++#endif + + int panic_on_oops; + static unsigned long tainted_mask; +@@ -96,6 +99,12 @@ NORET_TYPE void panic(const char * fmt, + dump_stack(); + #endif + ++ ++#ifdef CONFIG_KDB_KDUMP ++ if (kdb_kdump_state == KDB_KDUMP_RESET) { ++ (void)kdb(KDB_REASON_OOPS, 999, get_irq_regs()); ++ } ++#endif + /* + * If we have crashed and we have a crash kernel loaded let it handle + * everything else. +--- a/kernel/sched.c ++++ b/kernel/sched.c +@@ -7948,7 +7948,7 @@ void normalize_rt_tasks(void) + + #endif /* CONFIG_MAGIC_SYSRQ */ + +-#ifdef CONFIG_IA64 ++#if defined(CONFIG_IA64) || defined(CONFIG_KDB) + /* + * These functions are only useful for the IA64 MCA handling. + * +@@ -9196,3 +9196,110 @@ void synchronize_sched_expedited(void) + EXPORT_SYMBOL_GPL(synchronize_sched_expedited); + + #endif /* #else #ifndef CONFIG_SMP */ ++ ++ ++#ifdef CONFIG_KDB ++#include ++ ++static void ++kdb_prio(char *name, struct rt_prio_array *array, kdb_printf_t xxx_printf, ++ unsigned int cpu) ++{ ++ int pri, printed_header = 0; ++ struct task_struct *p; ++ ++ xxx_printf(" %s rt bitmap: 0x%lx 0x%lx 0x%lx\n", ++ name, ++ array->bitmap[0], array->bitmap[1], array->bitmap[2]); ++ ++ pri = sched_find_first_bit(array->bitmap); ++ if (pri < MAX_RT_PRIO) { ++ xxx_printf(" rt bitmap priorities:"); ++ while (pri < MAX_RT_PRIO) { ++ xxx_printf(" %d", pri); ++ pri++; ++ pri = find_next_bit(array->bitmap, MAX_RT_PRIO, pri); ++ } ++ xxx_printf("\n"); ++ } ++ ++ for (pri = 0; pri < MAX_RT_PRIO; pri++) { ++ int printed_hdr = 0; ++ struct list_head *head, *curr; ++ ++ head = array->queue + pri; ++ curr = head->next; ++ while(curr != head) { ++ struct task_struct *task; ++ if (!printed_hdr) { ++ xxx_printf(" queue at priority=%d\n", pri); ++ printed_hdr = 1; ++ } ++ task = list_entry(curr, struct task_struct, rt.run_list); ++ if (task) ++ xxx_printf(" 0x%p %d %s time_slice:%d\n", ++ task, task->pid, task->comm, ++ task->rt.time_slice); ++ curr = curr->next; ++ } ++ } ++ for_each_process(p) { ++ if (p->se.on_rq && (task_cpu(p) == cpu) && ++ (p->policy == SCHED_NORMAL)) { ++ if (!printed_header) { ++ xxx_printf(" sched_normal queue:\n"); ++ printed_header = 1; ++ } ++ xxx_printf(" 0x%p %d %s pri:%d spri:%d npri:%d\n", ++ p, p->pid, p->comm, p->prio, ++ p->static_prio, p->normal_prio); ++ } ++ } ++} ++ ++/* This code must be in sched.c because struct rq is only defined in this ++ * source. To allow most of kdb to be modular, this code cannot call any kdb ++ * functions directly, any external functions that it needs must be passed in ++ * as parameters. ++ */ ++ ++void ++kdb_runqueue(unsigned long cpu, kdb_printf_t xxx_printf) ++{ ++ int i; ++ struct rq *rq; ++ ++ rq = cpu_rq(cpu); ++ ++ xxx_printf("CPU%ld lock:%s curr:0x%p(%d)(%s)", ++ cpu, (raw_spin_is_locked(&rq->lock))?"LOCKED":"free", ++ rq->curr, rq->curr->pid, rq->curr->comm); ++ if (rq->curr == rq->idle) ++ xxx_printf(" is idle"); ++ xxx_printf("\n"); ++ ++ xxx_printf(" nr_running:%ld ", rq->nr_running); ++ xxx_printf(" nr_uninterruptible:%ld ", rq->nr_uninterruptible); ++ ++ xxx_printf(" nr_switches:%llu ", (long long)rq->nr_switches); ++ xxx_printf(" nr_iowait:%u ", atomic_read(&rq->nr_iowait)); ++ xxx_printf(" next_balance:%lu\n", rq->next_balance); ++ ++#ifdef CONFIG_SMP ++ xxx_printf(" active_balance:%u ", rq->active_balance); ++ xxx_printf(" idle_at_tick:%u\n", rq->idle_at_tick); ++ ++ xxx_printf(" push_cpu:%u ", rq->push_cpu); ++ xxx_printf(" cpu:%u ", rq->cpu); ++ xxx_printf(" online:%u\n", rq->online); ++#endif ++ ++ xxx_printf(" cpu_load:"); ++ for (i=0; icpu_load[i]); ++ xxx_printf("\n"); ++ kdb_prio("active", &rq->rt.active, xxx_printf, (unsigned int)cpu); ++} ++EXPORT_SYMBOL(kdb_runqueue); ++ ++#endif /* CONFIG_KDB */ +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -2735,3 +2735,52 @@ void __init signals_init(void) + { + sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC); + } ++ ++#ifdef CONFIG_KDB ++#include ++/* ++ * kdb_send_sig_info ++ * ++ * Allows kdb to send signals without exposing signal internals. ++ * ++ * Inputs: ++ * t task ++ * siginfo signal information ++ * seqno current kdb sequence number (avoid including kdbprivate.h) ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * Checks if the required locks are available before calling the main ++ * signal code, to avoid kdb deadlocks. ++ * Remarks: ++ */ ++void ++kdb_send_sig_info(struct task_struct *t, struct siginfo *info, int seqno) ++{ ++ static struct task_struct *kdb_prev_t; ++ static int kdb_prev_seqno; ++ int sig, new_t; ++ if (!spin_trylock(&t->sighand->siglock)) { ++ kdb_printf("Can't do kill command now.\n" ++ "The sigmask lock is held somewhere else in kernel, try again later\n"); ++ return; ++ } ++ spin_unlock(&t->sighand->siglock); ++ new_t = kdb_prev_t != t || kdb_prev_seqno != seqno; ++ kdb_prev_t = t; ++ kdb_prev_seqno = seqno; ++ if (t->state != TASK_RUNNING && new_t) { ++ kdb_printf("Process is not RUNNING, sending a signal from kdb risks deadlock\n" ++ "on the run queue locks. The signal has _not_ been sent.\n" ++ "Reissue the kill command if you want to risk the deadlock.\n"); ++ return; ++ } ++ sig = info->si_signo; ++ if (send_sig_info(sig, info, t)) ++ kdb_printf("Fail to deliver Signal %d to process %d.\n", sig, t->pid); ++ else ++ kdb_printf("Signal %d is sent to process %d.\n", sig, t->pid); ++} ++#endif /* CONFIG_KDB */ +--- a/lib/bug.c ++++ b/lib/bug.c +@@ -43,6 +43,10 @@ + #include + #include + ++#ifdef CONFIG_KDB ++#include ++#endif ++ + extern const struct bug_entry __start___bug_table[], __stop___bug_table[]; + + static inline unsigned long bug_addr(const struct bug_entry *bug) +@@ -177,5 +181,9 @@ enum bug_trap_type report_bug(unsigned l + "[verbose debug info unavailable]\n", + (void *)bugaddr); + ++#ifdef CONFIG_KDB ++ kdb(KDB_REASON_ENTER, 0, regs); ++#endif ++ + return BUG_TRAP_TYPE_BUG; + } +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -1929,6 +1929,28 @@ int hugetlb_overcommit_handler(struct ct + + #endif /* CONFIG_SYSCTL */ + ++#ifdef CONFIG_KDB ++#include ++#include ++/* Like hugetlb_report_meminfo() but using kdb_printf() */ ++void ++kdb_hugetlb_report_meminfo(void) ++{ ++ struct hstate *h = &default_hstate; ++ kdb_printf( ++ "HugePages_Total: %5lu\n" ++ "HugePages_Free: %5lu\n" ++ "HugePages_Rsvd: %5lu\n" ++ "HugePages_Surp: %5lu\n" ++ "Hugepagesize: %5lu kB\n", ++ h->nr_huge_pages, ++ h->free_huge_pages, ++ h->resv_huge_pages, ++ h->surplus_huge_pages, ++ 1UL << (huge_page_order(h) + PAGE_SHIFT - 10)); ++} ++#endif /* CONFIG_KDB */ ++ + void hugetlb_report_meminfo(struct seq_file *m) + { + struct hstate *h = &default_hstate; +--- a/mm/mmzone.c ++++ b/mm/mmzone.c +@@ -23,6 +23,10 @@ struct pglist_data *next_online_pgdat(st + return NULL; + return NODE_DATA(nid); + } ++#ifdef CONFIG_KDB ++EXPORT_SYMBOL(first_online_pgdat); ++EXPORT_SYMBOL(next_online_pgdat); ++#endif + + /* + * next_zone - helper magic for for_each_zone() +--- a/mm/swapfile.c ++++ b/mm/swapfile.c +@@ -13,6 +13,10 @@ + #include + #include + #include ++#ifdef CONFIG_KDB ++#include ++#include ++#endif /* CONFIG_KDB */ + #include + #include + #include +@@ -2129,6 +2133,24 @@ void si_swapinfo(struct sysinfo *val) + spin_unlock(&swap_lock); + } + ++#ifdef CONFIG_KDB ++/* Like si_swapinfo() but without the locks */ ++void kdb_si_swapinfo(struct sysinfo *val) ++{ ++ unsigned int i; ++ unsigned long nr_to_be_unused = 0; ++ ++ for (i = 0; i < nr_swapfiles; i++) { ++ if (!(swap_info[i]->flags & SWP_USED) || ++ (swap_info[i]->flags & SWP_WRITEOK)) ++ continue; ++ nr_to_be_unused += swap_info[i]->inuse_pages; ++ } ++ val->freeswap = nr_swap_pages + nr_to_be_unused; ++ val->totalswap = total_swap_pages + nr_to_be_unused; ++} ++#endif /* CONFIG_KDB */ ++ + /* + * Verify that a swap entry is valid and increment its swap map count. + * diff --git a/patches.suse/kdb-fix-assignment-from-incompatible-pointer-warnings b/patches.suse/kdb-fix-assignment-from-incompatible-pointer-warnings new file mode 100644 index 0000000..16ba925 --- /dev/null +++ b/patches.suse/kdb-fix-assignment-from-incompatible-pointer-warnings @@ -0,0 +1,31 @@ +From: Jeff Mahoney +Subject: kdb: Fix assignment from incompatible pointer warnings +Patch-mainline: not yet, whenever KDB is upstream + + info->pfs_loc is an unsigned long *, not a u64 *. + +Signed-off-by: Jeff Mahoney +--- + arch/ia64/kdb/kdba_bt.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/ia64/kdb/kdba_bt.c ++++ b/arch/ia64/kdb/kdba_bt.c +@@ -112,7 +112,7 @@ bt_print_one(kdb_machreg_t ip, + } + if (btsp) + kdb_printf(" sp 0x%016lx bsp 0x%016lx cfm 0x%016lx info->pfs_loc 0x%016lx 0x%016lx\n", +- sp, bsp, cfm, (u64) info->pfs_loc, info->pfs_loc ? *(info->pfs_loc) : 0); ++ sp, bsp, cfm, info->pfs_loc, info->pfs_loc ? *(info->pfs_loc) : 0); + } + + /* +@@ -142,7 +142,7 @@ kdba_bt_stack(int argcount, const struct + struct pt_regs *regs = NULL; + int count = 0; + int btsp = 0; /* Backtrace the kdb code as well */ +- u64 *prev_pfs_loc = NULL; ++ unsigned long *prev_pfs_loc = NULL; + extern char __attribute__ ((weak)) ia64_spinlock_contention_pre3_4[]; + extern char __attribute__ ((weak)) ia64_spinlock_contention_pre3_4_end[]; + diff --git a/patches.suse/kdb-fix-kdb_cmds-to-include-the-arch-common-macro b/patches.suse/kdb-fix-kdb_cmds-to-include-the-arch-common-macro new file mode 100644 index 0000000..b28c41e --- /dev/null +++ b/patches.suse/kdb-fix-kdb_cmds-to-include-the-arch-common-macro @@ -0,0 +1,26 @@ +From 8290f9ee66352a04b2858db63e20229ccd9395fb Mon Sep 17 00:00:00 2001 +From: Martin Hicks +Date: Mon, 8 Feb 2010 13:48:48 -0600 +Subject: [PATCH] kdb: fix kdb_cmds to include the arch common macro +References: bnc#578421 +Patch-mainline: Whenever kdb is accepted + +kbuild must have changed at one point and nobody noticed that +the "archkdb" type macros, which use the archkdbcommon +macro, were not working + +Signed-off-by: Martin Hicks +Acked-by: Jeff Mahoney +--- + kdb/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/kdb/Makefile ++++ b/kdb/Makefile +@@ -39,5 +39,5 @@ quiet_cmd_gen-kdb = GENKDB $@ + END {print "extern char *kdb_cmds[]; char __initdata *kdb_cmds[] = {"; for (i = 0; i < cmds; ++i) {print " kdb_cmd" i ","}; print(" NULL\n};");}' \ + $(filter-out %/Makefile,$^) > $@ + +-$(obj)/gen-kdb_cmds.c: $(src)/kdb_cmds $(wildcard $(TOPDIR)/arch/$(KDB_CMDS)) $(src)/Makefile ++$(obj)/gen-kdb_cmds.c: $(src)/kdb_cmds $(wildcard $(srctree)/arch/$(KDB_CMDS)) $(src)/Makefile + $(call cmd,gen-kdb) diff --git a/patches.suse/kdb-handle-nonexistance-keyboard-controller b/patches.suse/kdb-handle-nonexistance-keyboard-controller new file mode 100644 index 0000000..46cd3e8 --- /dev/null +++ b/patches.suse/kdb-handle-nonexistance-keyboard-controller @@ -0,0 +1,87 @@ +From: Martin Hicks +Subject: kdb: handle nonexistance keyboard controller +References: bnc#578051 +Patch-mainline: When kdb is accepted + + On UV, we have no keyboard controller and during the kdba_io polling + routines kdb attempts to disable the interrupts on the keyboard + controller to go into polling mode. + + These non-existant port addresses return 0xff all the time, which + appears to lock up KDB during entry. + +Acked-by: Jeff Mahoney +--- + arch/x86/kdb/kdba_io.c | 32 +++++++++++++++++++++++++------- + 1 file changed, 25 insertions(+), 7 deletions(-) + +--- a/arch/x86/kdb/kdba_io.c ++++ b/arch/x86/kdb/kdba_io.c +@@ -366,7 +366,23 @@ static int get_serial_char(void) + + #ifdef CONFIG_VT_CONSOLE + +-static int kbd_exists; ++static int kdb_check_kbd_exists(void) ++{ ++ static int kbd_exists = -1; ++ ++ /* One time init */ ++ if (kbd_exists == -1) { ++ if (KDB_FLAG(NO_I8042) || KDB_FLAG(NO_VT_CONSOLE) || ++ (kbd_read_status() == 0xff && ++ kbd_read_input() == 0xff)) ++ kbd_exists = 0; ++ else ++ kbd_exists = 1; ++ } ++ ++ return kbd_exists; ++} ++ + + /* + * Check if the keyboard controller has a keypress for us. +@@ -382,12 +398,8 @@ static int get_kbd_char(void) + u_short keychar; + extern u_short plain_map[], shift_map[], ctrl_map[]; + +- if (KDB_FLAG(NO_I8042) || KDB_FLAG(NO_VT_CONSOLE) || +- (inb(KBD_STATUS_REG) == 0xff && inb(KBD_DATA_REG) == 0xff)) { +- kbd_exists = 0; ++ if (!kdb_check_kbd_exists()) + return -1; +- } +- kbd_exists = 1; + + if ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) + return -1; +@@ -571,7 +583,7 @@ static int blink_led(void) + { + static long delay; + +- if (kbd_exists == 0) ++ if (!kdb_check_kbd_exists()) + return -1; + + if (--delay < 0) { +@@ -621,6 +633,9 @@ void kdba_local_arch_setup(void) + int timeout; + unsigned char c; + ++ if (!kdb_check_kbd_exists()) ++ return; ++ + while (kbd_read_status() & KBD_STAT_IBF); + kbd_write_command(KBD_CCMD_READ_MODE); + mdelay(1); +@@ -646,6 +661,9 @@ void kdba_local_arch_cleanup(void) + int timeout; + unsigned char c; + ++ if (!kdb_check_kbd_exists()) ++ return; ++ + while (kbd_read_status() & KBD_STAT_IBF); + kbd_write_command(KBD_CCMD_READ_MODE); + mdelay(1); diff --git a/patches.suse/kdb-ia64 b/patches.suse/kdb-ia64 new file mode 100644 index 0000000..f5c8ab2 --- /dev/null +++ b/patches.suse/kdb-ia64 @@ -0,0 +1,22912 @@ +From: Martin Hicks +Date: Mon, 07 Dec 2009 11:52:50 -0600 +Subject: kdb-v4.4-2.6.32-ia64-3 +References: FATE#303971 +X-URL: ftp://oss.sgi.com/www/projects/kdb/download/v4.4/ +Patch-mainline: not yet + +The KDB IA64 code. + +Acked-by: Jeff Mahoney +--- + + arch/ia64/Kconfig.debug | 97 + arch/ia64/Makefile | 1 + arch/ia64/include/asm/ansidecl.h | 383 + + arch/ia64/include/asm/bfd.h | 5089 +++++++++++++++++++++ + arch/ia64/include/asm/kdb.h | 50 + arch/ia64/include/asm/kdb_break.h | 24 + arch/ia64/include/asm/kdbprivate.h | 124 + arch/ia64/include/asm/kregs.h | 2 + arch/ia64/kdb/ChangeLog | 1111 ++++ + arch/ia64/kdb/Makefile | 21 + arch/ia64/kdb/cpu-ia64-opc.c | 598 ++ + arch/ia64/kdb/ia64-asmtab.c | 8585 +++++++++++++++++++++++++++++++++++++ + arch/ia64/kdb/ia64-asmtab.h | 158 + arch/ia64/kdb/ia64-dis.c | 312 + + arch/ia64/kdb/ia64-opc.c | 749 +++ + arch/ia64/kdb/ia64-opc.h | 141 + arch/ia64/kdb/ia64.h | 402 + + arch/ia64/kdb/kdb_cmds | 17 + arch/ia64/kdb/kdba_bp.c | 841 +++ + arch/ia64/kdb/kdba_bt.c | 285 + + arch/ia64/kdb/kdba_fru.c | 65 + arch/ia64/kdb/kdba_id.c | 529 ++ + arch/ia64/kdb/kdba_io.c | 661 ++ + arch/ia64/kdb/kdba_jmp.S | 394 + + arch/ia64/kdb/kdba_pod.c | 64 + arch/ia64/kdb/kdba_support.c | 1720 +++++++ + arch/ia64/kernel/head.S | 7 + arch/ia64/kernel/mca.c | 72 + arch/ia64/kernel/smp.c | 23 + arch/ia64/kernel/traps.c | 22 + arch/ia64/kernel/unwind.c | 33 + 31 files changed, 22568 insertions(+), 12 deletions(-) + +--- a/arch/ia64/Kconfig.debug ++++ b/arch/ia64/Kconfig.debug +@@ -56,9 +56,106 @@ config IA64_DEBUG_IRQ + and restore instructions. It's useful for tracking down spinlock + problems, but slow! If you're unsure, select N. + ++config KDB ++ bool "Built-in Kernel Debugger support" ++ depends on DEBUG_KERNEL ++ select KALLSYMS ++ select KALLSYMS_ALL ++ help ++ This option provides a built-in kernel debugger. The built-in ++ kernel debugger contains commands which allow memory to be examined, ++ instructions to be disassembled and breakpoints to be set. For details, ++ see Documentation/kdb/kdb.mm and the manual pages kdb_bt, kdb_ss, etc. ++ Kdb can also be used via the serial port. Set up the system to ++ have a serial console (see Documentation/serial-console.txt). ++ The key sequence KDB on the serial port will cause the ++ kernel debugger to be entered with input from the serial port and ++ output to the serial console. If unsure, say N. ++ ++config KDB_MODULES ++ tristate "KDB modules" ++ depends on KDB ++ help ++ KDB can be extended by adding your own modules, in directory ++ kdb/modules. This option selects the way that these modules should ++ be compiled, as free standing modules (select M) or built into the ++ kernel (select Y). If unsure say M. ++ ++config KDB_OFF ++ bool "KDB off by default" ++ depends on KDB ++ help ++ Normally kdb is activated by default, as long as CONFIG_KDB is set. ++ If you want to ship a kernel with kdb support but only have kdb ++ turned on when the user requests it then select this option. When ++ compiled with CONFIG_KDB_OFF, kdb ignores all events unless you boot ++ with kdb=on or you echo "1" > /proc/sys/kernel/kdb. This option also ++ works in reverse, if kdb is normally activated, you can boot with ++ kdb=off or echo "0" > /proc/sys/kernel/kdb to deactivate kdb. If ++ unsure, say N. ++ ++config KDB_CONTINUE_CATASTROPHIC ++ int "KDB continues after catastrophic errors" ++ depends on KDB ++ default "0" ++ help ++ This integer controls the behaviour of kdb when the kernel gets a ++ catastrophic error, i.e. for a panic, oops, NMI or other watchdog ++ tripping. CONFIG_KDB_CONTINUE_CATASTROPHIC interacts with ++ /proc/sys/kernel/kdb and CONFIG_LKCD_DUMP (if your kernel has the ++ LKCD patch). ++ When KDB is active (/proc/sys/kernel/kdb == 1) and a catastrophic ++ error occurs, nothing extra happens until you type 'go'. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 0 (default). The first time ++ you type 'go', kdb warns you. The second time you type 'go', KDB ++ tries to continue - no guarantees that the kernel is still usable. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 1. KDB tries to continue - no ++ guarantees that the kernel is still usable. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 2. If your kernel has the LKCD ++ patch and LKCD is configured to take a dump then KDB forces a dump. ++ Whether or not a dump is taken, KDB forces a reboot. ++ When KDB is not active (/proc/sys/kernel/kdb == 0) and a catastrophic ++ error occurs, the following steps are automatic, no human ++ intervention is required. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 0 (default) or 1. KDB attempts ++ to continue - no guarantees that the kernel is still usable. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 2. If your kernel has the LKCD ++ patch and LKCD is configured to take a dump then KDB automatically ++ forces a dump. Whether or not a dump is taken, KDB forces a ++ reboot. ++ If you are not sure, say 0. Read Documentation/kdb/dump.txt before ++ setting to 2. ++ ++config KDB_USB ++ bool "Support for USB Keyboard in KDB (OHCI and/or EHCI only)" ++ depends on KDB && (USB_OHCI_HCD || USB_UHCI_HCD) ++ help ++ If you want to use kdb from USB keyboards then say Y here. If you ++ say N then kdb can only be used from a PC (AT) keyboard or a serial ++ console. ++ ++config KDB_HARDWARE_BREAKPOINTS ++ bool "Enable hardware breakpoints in KDB" ++ depends on KDB ++ default y ++ help ++ If you say Y here, KDB will allow you to use the IA64 ++ hardware watchpoint feature (via the bph and bpha ++ commands). Currently, only data breakpoints are ++ implemented. ++ + config SYSVIPC_COMPAT + bool + depends on COMPAT && SYSVIPC + default y + ++config KDB_KDUMP ++ bool "Support for Kdump in KDB" ++ depends on KDB ++ select KEXEC ++ default N ++ help ++ If you want to take Kdump kernel vmcore from KDB then say Y here. ++ Of imsire. say N. ++ + endmenu +--- a/arch/ia64/Makefile ++++ b/arch/ia64/Makefile +@@ -57,6 +57,7 @@ core-$(CONFIG_IA64_SGI_UV) += arch/ia64/ + core-$(CONFIG_KVM) += arch/ia64/kvm/ + core-$(CONFIG_XEN) += arch/ia64/xen/ + ++drivers-$(CONFIG_KDB) += arch/$(ARCH)/kdb/ + drivers-$(CONFIG_PCI) += arch/ia64/pci/ + drivers-$(CONFIG_IA64_HP_SIM) += arch/ia64/hp/sim/ + drivers-$(CONFIG_IA64_HP_ZX1) += arch/ia64/hp/common/ arch/ia64/hp/zx1/ +--- /dev/null ++++ b/arch/ia64/include/asm/ansidecl.h +@@ -0,0 +1,383 @@ ++/* ANSI and traditional C compatability macros ++ Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 ++ Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, write to the Free Software ++Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++/* ANSI and traditional C compatibility macros ++ ++ ANSI C is assumed if __STDC__ is #defined. ++ ++ Macro ANSI C definition Traditional C definition ++ ----- ---- - ---------- ----------- - ---------- ++ ANSI_PROTOTYPES 1 not defined ++ PTR `void *' `char *' ++ PTRCONST `void *const' `char *' ++ LONG_DOUBLE `long double' `double' ++ const not defined `' ++ volatile not defined `' ++ signed not defined `' ++ VA_START(ap, var) va_start(ap, var) va_start(ap) ++ ++ Note that it is safe to write "void foo();" indicating a function ++ with no return value, in all K+R compilers we have been able to test. ++ ++ For declaring functions with prototypes, we also provide these: ++ ++ PARAMS ((prototype)) ++ -- for functions which take a fixed number of arguments. Use this ++ when declaring the function. When defining the function, write a ++ K+R style argument list. For example: ++ ++ char *strcpy PARAMS ((char *dest, char *source)); ++ ... ++ char * ++ strcpy (dest, source) ++ char *dest; ++ char *source; ++ { ... } ++ ++ ++ VPARAMS ((prototype, ...)) ++ -- for functions which take a variable number of arguments. Use ++ PARAMS to declare the function, VPARAMS to define it. For example: ++ ++ int printf PARAMS ((const char *format, ...)); ++ ... ++ int ++ printf VPARAMS ((const char *format, ...)) ++ { ++ ... ++ } ++ ++ For writing functions which take variable numbers of arguments, we ++ also provide the VA_OPEN, VA_CLOSE, and VA_FIXEDARG macros. These ++ hide the differences between K+R and C89 more ++ thoroughly than the simple VA_START() macro mentioned above. ++ ++ VA_OPEN and VA_CLOSE are used *instead of* va_start and va_end. ++ Immediately after VA_OPEN, put a sequence of VA_FIXEDARG calls ++ corresponding to the list of fixed arguments. Then use va_arg ++ normally to get the variable arguments, or pass your va_list object ++ around. You do not declare the va_list yourself; VA_OPEN does it ++ for you. ++ ++ Here is a complete example: ++ ++ int ++ printf VPARAMS ((const char *format, ...)) ++ { ++ int result; ++ ++ VA_OPEN (ap, format); ++ VA_FIXEDARG (ap, const char *, format); ++ ++ result = vfprintf (stdout, format, ap); ++ VA_CLOSE (ap); ++ ++ return result; ++ } ++ ++ ++ You can declare variables either before or after the VA_OPEN, ++ VA_FIXEDARG sequence. Also, VA_OPEN and VA_CLOSE are the beginning ++ and end of a block. They must appear at the same nesting level, ++ and any variables declared after VA_OPEN go out of scope at ++ VA_CLOSE. Unfortunately, with a K+R compiler, that includes the ++ argument list. You can have multiple instances of VA_OPEN/VA_CLOSE ++ pairs in a single function in case you need to traverse the ++ argument list more than once. ++ ++ For ease of writing code which uses GCC extensions but needs to be ++ portable to other compilers, we provide the GCC_VERSION macro that ++ simplifies testing __GNUC__ and __GNUC_MINOR__ together, and various ++ wrappers around __attribute__. Also, __extension__ will be #defined ++ to nothing if it doesn't work. See below. ++ ++ This header also defines a lot of obsolete macros: ++ CONST, VOLATILE, SIGNED, PROTO, EXFUN, DEFUN, DEFUN_VOID, ++ AND, DOTS, NOARGS. Don't use them. */ ++ ++#ifndef _ANSIDECL_H ++#define _ANSIDECL_H 1 ++ ++/* Every source file includes this file, ++ so they will all get the switch for lint. */ ++/* LINTLIBRARY */ ++ ++/* Using MACRO(x,y) in cpp #if conditionals does not work with some ++ older preprocessors. Thus we can't define something like this: ++ ++#define HAVE_GCC_VERSION(MAJOR, MINOR) \ ++ (__GNUC__ > (MAJOR) || (__GNUC__ == (MAJOR) && __GNUC_MINOR__ >= (MINOR))) ++ ++and then test "#if HAVE_GCC_VERSION(2,7)". ++ ++So instead we use the macro below and test it against specific values. */ ++ ++/* This macro simplifies testing whether we are using gcc, and if it ++ is of a particular minimum version. (Both major & minor numbers are ++ significant.) This macro will evaluate to 0 if we are not using ++ gcc at all. */ ++#ifndef GCC_VERSION ++#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) ++#endif /* GCC_VERSION */ ++ ++#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(_WIN32) || (defined(__alpha) && defined(__cplusplus)) ++/* All known AIX compilers implement these things (but don't always ++ define __STDC__). The RISC/OS MIPS compiler defines these things ++ in SVR4 mode, but does not define __STDC__. */ ++/* eraxxon@alumni.rice.edu: The Compaq C++ compiler, unlike many other ++ C++ compilers, does not define __STDC__, though it acts as if this ++ was so. (Verified versions: 5.7, 6.2, 6.3, 6.5) */ ++ ++#define ANSI_PROTOTYPES 1 ++#define PTR void * ++#define PTRCONST void *const ++#define LONG_DOUBLE long double ++ ++/* PARAMS is often defined elsewhere (e.g. by libintl.h), so wrap it in ++ a #ifndef. */ ++#ifndef PARAMS ++#define PARAMS(ARGS) ARGS ++#endif ++ ++#define VPARAMS(ARGS) ARGS ++#define VA_START(VA_LIST, VAR) va_start(VA_LIST, VAR) ++ ++/* variadic function helper macros */ ++/* "struct Qdmy" swallows the semicolon after VA_OPEN/VA_FIXEDARG's ++ use without inhibiting further decls and without declaring an ++ actual variable. */ ++#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP, VAR); { struct Qdmy ++#define VA_CLOSE(AP) } va_end(AP); } ++#define VA_FIXEDARG(AP, T, N) struct Qdmy ++ ++#undef const ++#undef volatile ++#undef signed ++ ++#ifdef __KERNEL__ ++#ifndef __STDC_VERSION__ ++#define __STDC_VERSION__ 0 ++#endif ++#endif /* __KERNEL__ */ ++ ++/* inline requires special treatment; it's in C99, and GCC >=2.7 supports ++ it too, but it's not in C89. */ ++#undef inline ++#if __STDC_VERSION__ > 199901L ++/* it's a keyword */ ++#else ++# if GCC_VERSION >= 2007 ++# define inline __inline__ /* __inline__ prevents -pedantic warnings */ ++# else ++# define inline /* nothing */ ++# endif ++#endif ++ ++/* These are obsolete. Do not use. */ ++#ifndef IN_GCC ++#define CONST const ++#define VOLATILE volatile ++#define SIGNED signed ++ ++#define PROTO(type, name, arglist) type name arglist ++#define EXFUN(name, proto) name proto ++#define DEFUN(name, arglist, args) name(args) ++#define DEFUN_VOID(name) name(void) ++#define AND , ++#define DOTS , ... ++#define NOARGS void ++#endif /* ! IN_GCC */ ++ ++#else /* Not ANSI C. */ ++ ++#undef ANSI_PROTOTYPES ++#define PTR char * ++#define PTRCONST PTR ++#define LONG_DOUBLE double ++ ++#define PARAMS(args) () ++#define VPARAMS(args) (va_alist) va_dcl ++#define VA_START(va_list, var) va_start(va_list) ++ ++#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP); { struct Qdmy ++#define VA_CLOSE(AP) } va_end(AP); } ++#define VA_FIXEDARG(AP, TYPE, NAME) TYPE NAME = va_arg(AP, TYPE) ++ ++/* some systems define these in header files for non-ansi mode */ ++#undef const ++#undef volatile ++#undef signed ++#undef inline ++#define const ++#define volatile ++#define signed ++#define inline ++ ++#ifndef IN_GCC ++#define CONST ++#define VOLATILE ++#define SIGNED ++ ++#define PROTO(type, name, arglist) type name () ++#define EXFUN(name, proto) name() ++#define DEFUN(name, arglist, args) name arglist args; ++#define DEFUN_VOID(name) name() ++#define AND ; ++#define DOTS ++#define NOARGS ++#endif /* ! IN_GCC */ ++ ++#endif /* ANSI C. */ ++ ++/* Define macros for some gcc attributes. This permits us to use the ++ macros freely, and know that they will come into play for the ++ version of gcc in which they are supported. */ ++ ++#if (GCC_VERSION < 2007) ++# define __attribute__(x) ++#endif ++ ++/* Attribute __malloc__ on functions was valid as of gcc 2.96. */ ++#ifndef ATTRIBUTE_MALLOC ++# if (GCC_VERSION >= 2096) ++# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) ++# else ++# define ATTRIBUTE_MALLOC ++# endif /* GNUC >= 2.96 */ ++#endif /* ATTRIBUTE_MALLOC */ ++ ++/* Attributes on labels were valid as of gcc 2.93. */ ++#ifndef ATTRIBUTE_UNUSED_LABEL ++# if (!defined (__cplusplus) && GCC_VERSION >= 2093) ++# define ATTRIBUTE_UNUSED_LABEL ATTRIBUTE_UNUSED ++# else ++# define ATTRIBUTE_UNUSED_LABEL ++# endif /* !__cplusplus && GNUC >= 2.93 */ ++#endif /* ATTRIBUTE_UNUSED_LABEL */ ++ ++#ifndef ATTRIBUTE_UNUSED ++#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) ++#endif /* ATTRIBUTE_UNUSED */ ++ ++/* Before GCC 3.4, the C++ frontend couldn't parse attributes placed after the ++ identifier name. */ ++#if ! defined(__cplusplus) || (GCC_VERSION >= 3004) ++# define ARG_UNUSED(NAME) NAME ATTRIBUTE_UNUSED ++#else /* !__cplusplus || GNUC >= 3.4 */ ++# define ARG_UNUSED(NAME) NAME ++#endif /* !__cplusplus || GNUC >= 3.4 */ ++ ++#ifndef ATTRIBUTE_NORETURN ++#define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) ++#endif /* ATTRIBUTE_NORETURN */ ++ ++/* Attribute `nonnull' was valid as of gcc 3.3. */ ++#ifndef ATTRIBUTE_NONNULL ++# if (GCC_VERSION >= 3003) ++# define ATTRIBUTE_NONNULL(m) __attribute__ ((__nonnull__ (m))) ++# else ++# define ATTRIBUTE_NONNULL(m) ++# endif /* GNUC >= 3.3 */ ++#endif /* ATTRIBUTE_NONNULL */ ++ ++/* Attribute `pure' was valid as of gcc 3.0. */ ++#ifndef ATTRIBUTE_PURE ++# if (GCC_VERSION >= 3000) ++# define ATTRIBUTE_PURE __attribute__ ((__pure__)) ++# else ++# define ATTRIBUTE_PURE ++# endif /* GNUC >= 3.0 */ ++#endif /* ATTRIBUTE_PURE */ ++ ++/* Use ATTRIBUTE_PRINTF when the format specifier must not be NULL. ++ This was the case for the `printf' format attribute by itself ++ before GCC 3.3, but as of 3.3 we need to add the `nonnull' ++ attribute to retain this behavior. */ ++#ifndef ATTRIBUTE_PRINTF ++#define ATTRIBUTE_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) ATTRIBUTE_NONNULL(m) ++#define ATTRIBUTE_PRINTF_1 ATTRIBUTE_PRINTF(1, 2) ++#define ATTRIBUTE_PRINTF_2 ATTRIBUTE_PRINTF(2, 3) ++#define ATTRIBUTE_PRINTF_3 ATTRIBUTE_PRINTF(3, 4) ++#define ATTRIBUTE_PRINTF_4 ATTRIBUTE_PRINTF(4, 5) ++#define ATTRIBUTE_PRINTF_5 ATTRIBUTE_PRINTF(5, 6) ++#endif /* ATTRIBUTE_PRINTF */ ++ ++/* Use ATTRIBUTE_FPTR_PRINTF when the format attribute is to be set on ++ a function pointer. Format attributes were allowed on function ++ pointers as of gcc 3.1. */ ++#ifndef ATTRIBUTE_FPTR_PRINTF ++# if (GCC_VERSION >= 3001) ++# define ATTRIBUTE_FPTR_PRINTF(m, n) ATTRIBUTE_PRINTF(m, n) ++# else ++# define ATTRIBUTE_FPTR_PRINTF(m, n) ++# endif /* GNUC >= 3.1 */ ++# define ATTRIBUTE_FPTR_PRINTF_1 ATTRIBUTE_FPTR_PRINTF(1, 2) ++# define ATTRIBUTE_FPTR_PRINTF_2 ATTRIBUTE_FPTR_PRINTF(2, 3) ++# define ATTRIBUTE_FPTR_PRINTF_3 ATTRIBUTE_FPTR_PRINTF(3, 4) ++# define ATTRIBUTE_FPTR_PRINTF_4 ATTRIBUTE_FPTR_PRINTF(4, 5) ++# define ATTRIBUTE_FPTR_PRINTF_5 ATTRIBUTE_FPTR_PRINTF(5, 6) ++#endif /* ATTRIBUTE_FPTR_PRINTF */ ++ ++/* Use ATTRIBUTE_NULL_PRINTF when the format specifier may be NULL. A ++ NULL format specifier was allowed as of gcc 3.3. */ ++#ifndef ATTRIBUTE_NULL_PRINTF ++# if (GCC_VERSION >= 3003) ++# define ATTRIBUTE_NULL_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) ++# else ++# define ATTRIBUTE_NULL_PRINTF(m, n) ++# endif /* GNUC >= 3.3 */ ++# define ATTRIBUTE_NULL_PRINTF_1 ATTRIBUTE_NULL_PRINTF(1, 2) ++# define ATTRIBUTE_NULL_PRINTF_2 ATTRIBUTE_NULL_PRINTF(2, 3) ++# define ATTRIBUTE_NULL_PRINTF_3 ATTRIBUTE_NULL_PRINTF(3, 4) ++# define ATTRIBUTE_NULL_PRINTF_4 ATTRIBUTE_NULL_PRINTF(4, 5) ++# define ATTRIBUTE_NULL_PRINTF_5 ATTRIBUTE_NULL_PRINTF(5, 6) ++#endif /* ATTRIBUTE_NULL_PRINTF */ ++ ++/* Attribute `sentinel' was valid as of gcc 3.5. */ ++#ifndef ATTRIBUTE_SENTINEL ++# if (GCC_VERSION >= 3005) ++# define ATTRIBUTE_SENTINEL __attribute__ ((__sentinel__)) ++# else ++# define ATTRIBUTE_SENTINEL ++# endif /* GNUC >= 3.5 */ ++#endif /* ATTRIBUTE_SENTINEL */ ++ ++ ++#ifndef ATTRIBUTE_ALIGNED_ALIGNOF ++# if (GCC_VERSION >= 3000) ++# define ATTRIBUTE_ALIGNED_ALIGNOF(m) __attribute__ ((__aligned__ (__alignof__ (m)))) ++# else ++# define ATTRIBUTE_ALIGNED_ALIGNOF(m) ++# endif /* GNUC >= 3.0 */ ++#endif /* ATTRIBUTE_ALIGNED_ALIGNOF */ ++ ++/* We use __extension__ in some places to suppress -pedantic warnings ++ about GCC extensions. This feature didn't work properly before ++ gcc 2.8. */ ++#if GCC_VERSION < 2008 ++#define __extension__ ++#endif ++ ++#endif /* ansidecl.h */ +--- /dev/null ++++ b/arch/ia64/include/asm/bfd.h +@@ -0,0 +1,5089 @@ ++/* DO NOT EDIT! -*- buffer-read-only: t -*- This file is automatically ++ generated from "bfd-in.h", "init.c", "opncls.c", "libbfd.c", ++ "bfdio.c", "bfdwin.c", "section.c", "archures.c", "reloc.c", ++ "syms.c", "bfd.c", "archive.c", "corefile.c", "targets.c", "format.c", ++ "linker.c" and "simple.c". ++ Run "make headers" in your build bfd/ to regenerate. */ ++ ++/* Main header file for the bfd library -- portable access to object files. ++ ++ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, ++ 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. ++ ++ Contributed by Cygnus Support. ++ ++ This file is part of BFD, the Binary File Descriptor library. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifndef __BFD_H_SEEN__ ++#define __BFD_H_SEEN__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifdef __KERNEL__ ++#include ++#else /* __KERNEL__ */ ++#include "ansidecl.h" ++#include "symcat.h" ++#endif /* __KERNEL__ */ ++#if defined (__STDC__) || defined (ALMOST_STDC) || defined (HAVE_STRINGIZE) ++#ifndef SABER ++/* This hack is to avoid a problem with some strict ANSI C preprocessors. ++ The problem is, "32_" is not a valid preprocessing token, and we don't ++ want extra underscores (e.g., "nlm_32_"). The XCONCAT2 macro will ++ cause the inner CONCAT2 macros to be evaluated first, producing ++ still-valid pp-tokens. Then the final concatenation can be done. */ ++#undef CONCAT4 ++#define CONCAT4(a,b,c,d) XCONCAT2(CONCAT2(a,b),CONCAT2(c,d)) ++#endif ++#endif ++ ++/* The word size used by BFD on the host. This may be 64 with a 32 ++ bit target if the host is 64 bit, or if other 64 bit targets have ++ been selected with --enable-targets, or if --enable-64-bit-bfd. */ ++#define BFD_ARCH_SIZE 64 ++ ++/* The word size of the default bfd target. */ ++#define BFD_DEFAULT_TARGET_SIZE 64 ++ ++#define BFD_HOST_64BIT_LONG 1 ++#define BFD_HOST_LONG_LONG 1 ++#if 1 ++#define BFD_HOST_64_BIT long ++#define BFD_HOST_U_64_BIT unsigned long ++typedef BFD_HOST_64_BIT bfd_int64_t; ++typedef BFD_HOST_U_64_BIT bfd_uint64_t; ++#endif ++ ++#if BFD_ARCH_SIZE >= 64 ++#define BFD64 ++#endif ++ ++#ifndef INLINE ++#if __GNUC__ >= 2 ++#define INLINE __inline__ ++#else ++#define INLINE ++#endif ++#endif ++ ++/* Forward declaration. */ ++typedef struct bfd bfd; ++ ++/* Boolean type used in bfd. Too many systems define their own ++ versions of "boolean" for us to safely typedef a "boolean" of ++ our own. Using an enum for "bfd_boolean" has its own set of ++ problems, with strange looking casts required to avoid warnings ++ on some older compilers. Thus we just use an int. ++ ++ General rule: Functions which are bfd_boolean return TRUE on ++ success and FALSE on failure (unless they're a predicate). */ ++ ++typedef int bfd_boolean; ++#undef FALSE ++#undef TRUE ++#define FALSE 0 ++#define TRUE 1 ++ ++#ifdef BFD64 ++ ++#ifndef BFD_HOST_64_BIT ++ #error No 64 bit integer type available ++#endif /* ! defined (BFD_HOST_64_BIT) */ ++ ++typedef BFD_HOST_U_64_BIT bfd_vma; ++typedef BFD_HOST_64_BIT bfd_signed_vma; ++typedef BFD_HOST_U_64_BIT bfd_size_type; ++typedef BFD_HOST_U_64_BIT symvalue; ++ ++#ifndef fprintf_vma ++#if BFD_HOST_64BIT_LONG ++#define sprintf_vma(s,x) sprintf (s, "%016lx", x) ++#define fprintf_vma(f,x) fprintf (f, "%016lx", x) ++#else ++#define _bfd_int64_low(x) ((unsigned long) (((x) & 0xffffffff))) ++#define _bfd_int64_high(x) ((unsigned long) (((x) >> 32) & 0xffffffff)) ++#define fprintf_vma(s,x) \ ++ fprintf ((s), "%08lx%08lx", _bfd_int64_high (x), _bfd_int64_low (x)) ++#define sprintf_vma(s,x) \ ++ sprintf ((s), "%08lx%08lx", _bfd_int64_high (x), _bfd_int64_low (x)) ++#endif ++#endif ++ ++#else /* not BFD64 */ ++ ++/* Represent a target address. Also used as a generic unsigned type ++ which is guaranteed to be big enough to hold any arithmetic types ++ we need to deal with. */ ++typedef unsigned long bfd_vma; ++ ++/* A generic signed type which is guaranteed to be big enough to hold any ++ arithmetic types we need to deal with. Can be assumed to be compatible ++ with bfd_vma in the same way that signed and unsigned ints are compatible ++ (as parameters, in assignment, etc). */ ++typedef long bfd_signed_vma; ++ ++typedef unsigned long symvalue; ++typedef unsigned long bfd_size_type; ++ ++/* Print a bfd_vma x on stream s. */ ++#define fprintf_vma(s,x) fprintf (s, "%08lx", x) ++#define sprintf_vma(s,x) sprintf (s, "%08lx", x) ++ ++#endif /* not BFD64 */ ++ ++#define HALF_BFD_SIZE_TYPE \ ++ (((bfd_size_type) 1) << (8 * sizeof (bfd_size_type) / 2)) ++ ++#ifndef BFD_HOST_64_BIT ++/* Fall back on a 32 bit type. The idea is to make these types always ++ available for function return types, but in the case that ++ BFD_HOST_64_BIT is undefined such a function should abort or ++ otherwise signal an error. */ ++typedef bfd_signed_vma bfd_int64_t; ++typedef bfd_vma bfd_uint64_t; ++#endif ++ ++/* An offset into a file. BFD always uses the largest possible offset ++ based on the build time availability of fseek, fseeko, or fseeko64. */ ++typedef BFD_HOST_64_BIT file_ptr; ++typedef unsigned BFD_HOST_64_BIT ufile_ptr; ++ ++extern void bfd_sprintf_vma (bfd *, char *, bfd_vma); ++extern void bfd_fprintf_vma (bfd *, void *, bfd_vma); ++ ++#define printf_vma(x) fprintf_vma(stdout,x) ++#define bfd_printf_vma(abfd,x) bfd_fprintf_vma (abfd,stdout,x) ++ ++typedef unsigned int flagword; /* 32 bits of flags */ ++typedef unsigned char bfd_byte; ++ ++typedef int (*bfd_qsort_closure_func) (const void *, const void *, const void *); ++extern void bfd_qsort (void *base, bfd_size_type nmemb, bfd_size_type size, ++ bfd_qsort_closure_func cmp, void *closure); ++ ++/* File formats. */ ++ ++typedef enum bfd_format ++{ ++ bfd_unknown = 0, /* File format is unknown. */ ++ bfd_object, /* Linker/assembler/compiler output. */ ++ bfd_archive, /* Object archive file. */ ++ bfd_core, /* Core dump. */ ++ bfd_type_end /* Marks the end; don't use it! */ ++} ++bfd_format; ++ ++/* Values that may appear in the flags field of a BFD. These also ++ appear in the object_flags field of the bfd_target structure, where ++ they indicate the set of flags used by that backend (not all flags ++ are meaningful for all object file formats) (FIXME: at the moment, ++ the object_flags values have mostly just been copied from backend ++ to another, and are not necessarily correct). */ ++ ++/* No flags. */ ++#define BFD_NO_FLAGS 0x00 ++ ++/* BFD contains relocation entries. */ ++#define HAS_RELOC 0x01 ++ ++/* BFD is directly executable. */ ++#define EXEC_P 0x02 ++ ++/* BFD has line number information (basically used for F_LNNO in a ++ COFF header). */ ++#define HAS_LINENO 0x04 ++ ++/* BFD has debugging information. */ ++#define HAS_DEBUG 0x08 ++ ++/* BFD has symbols. */ ++#define HAS_SYMS 0x10 ++ ++/* BFD has local symbols (basically used for F_LSYMS in a COFF ++ header). */ ++#define HAS_LOCALS 0x20 ++ ++/* BFD is a dynamic object. */ ++#define DYNAMIC 0x40 ++ ++/* Text section is write protected (if D_PAGED is not set, this is ++ like an a.out NMAGIC file) (the linker sets this by default, but ++ clears it for -r or -N). */ ++#define WP_TEXT 0x80 ++ ++/* BFD is dynamically paged (this is like an a.out ZMAGIC file) (the ++ linker sets this by default, but clears it for -r or -n or -N). */ ++#define D_PAGED 0x100 ++ ++/* BFD is relaxable (this means that bfd_relax_section may be able to ++ do something) (sometimes bfd_relax_section can do something even if ++ this is not set). */ ++#define BFD_IS_RELAXABLE 0x200 ++ ++/* This may be set before writing out a BFD to request using a ++ traditional format. For example, this is used to request that when ++ writing out an a.out object the symbols not be hashed to eliminate ++ duplicates. */ ++#define BFD_TRADITIONAL_FORMAT 0x400 ++ ++/* This flag indicates that the BFD contents are actually cached in ++ memory. If this is set, iostream points to a bfd_in_memory struct. */ ++#define BFD_IN_MEMORY 0x800 ++ ++/* The sections in this BFD specify a memory page. */ ++#define HAS_LOAD_PAGE 0x1000 ++ ++/* This BFD has been created by the linker and doesn't correspond ++ to any input file. */ ++#define BFD_LINKER_CREATED 0x2000 ++ ++/* Symbols and relocation. */ ++ ++/* A count of carsyms (canonical archive symbols). */ ++typedef unsigned long symindex; ++ ++/* How to perform a relocation. */ ++typedef const struct reloc_howto_struct reloc_howto_type; ++ ++#define BFD_NO_MORE_SYMBOLS ((symindex) ~0) ++ ++/* General purpose part of a symbol X; ++ target specific parts are in libcoff.h, libaout.h, etc. */ ++ ++#define bfd_get_section(x) ((x)->section) ++#define bfd_get_output_section(x) ((x)->section->output_section) ++#define bfd_set_section(x,y) ((x)->section) = (y) ++#define bfd_asymbol_base(x) ((x)->section->vma) ++#define bfd_asymbol_value(x) (bfd_asymbol_base(x) + (x)->value) ++#define bfd_asymbol_name(x) ((x)->name) ++/*Perhaps future: #define bfd_asymbol_bfd(x) ((x)->section->owner)*/ ++#define bfd_asymbol_bfd(x) ((x)->the_bfd) ++#define bfd_asymbol_flavour(x) (bfd_asymbol_bfd(x)->xvec->flavour) ++ ++/* A canonical archive symbol. */ ++/* This is a type pun with struct ranlib on purpose! */ ++typedef struct carsym ++{ ++ char *name; ++ file_ptr file_offset; /* Look here to find the file. */ ++} ++carsym; /* To make these you call a carsymogen. */ ++ ++/* Used in generating armaps (archive tables of contents). ++ Perhaps just a forward definition would do? */ ++struct orl /* Output ranlib. */ ++{ ++ char **name; /* Symbol name. */ ++ union ++ { ++ file_ptr pos; ++ bfd *abfd; ++ } u; /* bfd* or file position. */ ++ int namidx; /* Index into string table. */ ++}; ++ ++/* Linenumber stuff. */ ++typedef struct lineno_cache_entry ++{ ++ unsigned int line_number; /* Linenumber from start of function. */ ++ union ++ { ++ struct bfd_symbol *sym; /* Function name. */ ++ bfd_vma offset; /* Offset into section. */ ++ } u; ++} ++alent; ++ ++/* Object and core file sections. */ ++ ++#define align_power(addr, align) \ ++ (((addr) + ((bfd_vma) 1 << (align)) - 1) & ((bfd_vma) -1 << (align))) ++ ++typedef struct bfd_section *sec_ptr; ++ ++#define bfd_get_section_name(bfd, ptr) ((ptr)->name + 0) ++#define bfd_get_section_vma(bfd, ptr) ((ptr)->vma + 0) ++#define bfd_get_section_lma(bfd, ptr) ((ptr)->lma + 0) ++#define bfd_get_section_alignment(bfd, ptr) ((ptr)->alignment_power + 0) ++#define bfd_section_name(bfd, ptr) ((ptr)->name) ++#define bfd_section_size(bfd, ptr) ((ptr)->size) ++#define bfd_get_section_size(ptr) ((ptr)->size) ++#define bfd_section_vma(bfd, ptr) ((ptr)->vma) ++#define bfd_section_lma(bfd, ptr) ((ptr)->lma) ++#define bfd_section_alignment(bfd, ptr) ((ptr)->alignment_power) ++#define bfd_get_section_flags(bfd, ptr) ((ptr)->flags + 0) ++#define bfd_get_section_userdata(bfd, ptr) ((ptr)->userdata) ++ ++#define bfd_is_com_section(ptr) (((ptr)->flags & SEC_IS_COMMON) != 0) ++ ++#define bfd_set_section_vma(bfd, ptr, val) (((ptr)->vma = (ptr)->lma = (val)), ((ptr)->user_set_vma = TRUE), TRUE) ++#define bfd_set_section_alignment(bfd, ptr, val) (((ptr)->alignment_power = (val)),TRUE) ++#define bfd_set_section_userdata(bfd, ptr, val) (((ptr)->userdata = (val)),TRUE) ++/* Find the address one past the end of SEC. */ ++#define bfd_get_section_limit(bfd, sec) \ ++ (((sec)->rawsize ? (sec)->rawsize : (sec)->size) \ ++ / bfd_octets_per_byte (bfd)) ++ ++typedef struct stat stat_type; ++ ++typedef enum bfd_print_symbol ++{ ++ bfd_print_symbol_name, ++ bfd_print_symbol_more, ++ bfd_print_symbol_all ++} bfd_print_symbol_type; ++ ++/* Information about a symbol that nm needs. */ ++ ++typedef struct _symbol_info ++{ ++ symvalue value; ++ char type; ++ const char *name; /* Symbol name. */ ++ unsigned char stab_type; /* Stab type. */ ++ char stab_other; /* Stab other. */ ++ short stab_desc; /* Stab desc. */ ++ const char *stab_name; /* String for stab type. */ ++} symbol_info; ++ ++/* Get the name of a stabs type code. */ ++ ++extern const char *bfd_get_stab_name (int); ++ ++/* Hash table routines. There is no way to free up a hash table. */ ++ ++/* An element in the hash table. Most uses will actually use a larger ++ structure, and an instance of this will be the first field. */ ++ ++struct bfd_hash_entry ++{ ++ /* Next entry for this hash code. */ ++ struct bfd_hash_entry *next; ++ /* String being hashed. */ ++ const char *string; ++ /* Hash code. This is the full hash code, not the index into the ++ table. */ ++ unsigned long hash; ++}; ++ ++/* A hash table. */ ++ ++struct bfd_hash_table ++{ ++ /* The hash array. */ ++ struct bfd_hash_entry **table; ++ /* The number of slots in the hash table. */ ++ unsigned int size; ++ /* A function used to create new elements in the hash table. The ++ first entry is itself a pointer to an element. When this ++ function is first invoked, this pointer will be NULL. However, ++ having the pointer permits a hierarchy of method functions to be ++ built each of which calls the function in the superclass. Thus ++ each function should be written to allocate a new block of memory ++ only if the argument is NULL. */ ++ struct bfd_hash_entry *(*newfunc) ++ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *); ++ /* An objalloc for this hash table. This is a struct objalloc *, ++ but we use void * to avoid requiring the inclusion of objalloc.h. */ ++ void *memory; ++}; ++ ++/* Initialize a hash table. */ ++extern bfd_boolean bfd_hash_table_init ++ (struct bfd_hash_table *, ++ struct bfd_hash_entry *(*) (struct bfd_hash_entry *, ++ struct bfd_hash_table *, ++ const char *)); ++ ++/* Initialize a hash table specifying a size. */ ++extern bfd_boolean bfd_hash_table_init_n ++ (struct bfd_hash_table *, ++ struct bfd_hash_entry *(*) (struct bfd_hash_entry *, ++ struct bfd_hash_table *, ++ const char *), ++ unsigned int size); ++ ++/* Free up a hash table. */ ++extern void bfd_hash_table_free ++ (struct bfd_hash_table *); ++ ++/* Look up a string in a hash table. If CREATE is TRUE, a new entry ++ will be created for this string if one does not already exist. The ++ COPY argument must be TRUE if this routine should copy the string ++ into newly allocated memory when adding an entry. */ ++extern struct bfd_hash_entry *bfd_hash_lookup ++ (struct bfd_hash_table *, const char *, bfd_boolean create, ++ bfd_boolean copy); ++ ++/* Replace an entry in a hash table. */ ++extern void bfd_hash_replace ++ (struct bfd_hash_table *, struct bfd_hash_entry *old, ++ struct bfd_hash_entry *nw); ++ ++/* Base method for creating a hash table entry. */ ++extern struct bfd_hash_entry *bfd_hash_newfunc ++ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *); ++ ++/* Grab some space for a hash table entry. */ ++extern void *bfd_hash_allocate ++ (struct bfd_hash_table *, unsigned int); ++ ++/* Traverse a hash table in a random order, calling a function on each ++ element. If the function returns FALSE, the traversal stops. The ++ INFO argument is passed to the function. */ ++extern void bfd_hash_traverse ++ (struct bfd_hash_table *, ++ bfd_boolean (*) (struct bfd_hash_entry *, void *), ++ void *info); ++ ++/* Allows the default size of a hash table to be configured. New hash ++ tables allocated using bfd_hash_table_init will be created with ++ this size. */ ++extern void bfd_hash_set_default_size (bfd_size_type); ++ ++/* This structure is used to keep track of stabs in sections ++ information while linking. */ ++ ++struct stab_info ++{ ++ /* A hash table used to hold stabs strings. */ ++ struct bfd_strtab_hash *strings; ++ /* The header file hash table. */ ++ struct bfd_hash_table includes; ++ /* The first .stabstr section. */ ++ struct bfd_section *stabstr; ++}; ++ ++#define COFF_SWAP_TABLE (void *) &bfd_coff_std_swap_table ++ ++/* User program access to BFD facilities. */ ++ ++/* Direct I/O routines, for programs which know more about the object ++ file than BFD does. Use higher level routines if possible. */ ++ ++extern bfd_size_type bfd_bread (void *, bfd_size_type, bfd *); ++extern bfd_size_type bfd_bwrite (const void *, bfd_size_type, bfd *); ++extern int bfd_seek (bfd *, file_ptr, int); ++extern file_ptr bfd_tell (bfd *); ++extern int bfd_flush (bfd *); ++extern int bfd_stat (bfd *, struct stat *); ++ ++/* Deprecated old routines. */ ++#if __GNUC__ ++#define bfd_read(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_read", __FILE__, __LINE__, __FUNCTION__), \ ++ bfd_bread ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#define bfd_write(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_write", __FILE__, __LINE__, __FUNCTION__), \ ++ bfd_bwrite ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#else ++#define bfd_read(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_read", (const char *) 0, 0, (const char *) 0), \ ++ bfd_bread ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#define bfd_write(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_write", (const char *) 0, 0, (const char *) 0),\ ++ bfd_bwrite ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#endif ++extern void warn_deprecated (const char *, const char *, int, const char *); ++ ++/* Cast from const char * to char * so that caller can assign to ++ a char * without a warning. */ ++#define bfd_get_filename(abfd) ((char *) (abfd)->filename) ++#define bfd_get_cacheable(abfd) ((abfd)->cacheable) ++#define bfd_get_format(abfd) ((abfd)->format) ++#define bfd_get_target(abfd) ((abfd)->xvec->name) ++#define bfd_get_flavour(abfd) ((abfd)->xvec->flavour) ++#define bfd_family_coff(abfd) \ ++ (bfd_get_flavour (abfd) == bfd_target_coff_flavour || \ ++ bfd_get_flavour (abfd) == bfd_target_xcoff_flavour) ++#define bfd_big_endian(abfd) ((abfd)->xvec->byteorder == BFD_ENDIAN_BIG) ++#define bfd_little_endian(abfd) ((abfd)->xvec->byteorder == BFD_ENDIAN_LITTLE) ++#define bfd_header_big_endian(abfd) \ ++ ((abfd)->xvec->header_byteorder == BFD_ENDIAN_BIG) ++#define bfd_header_little_endian(abfd) \ ++ ((abfd)->xvec->header_byteorder == BFD_ENDIAN_LITTLE) ++#define bfd_get_file_flags(abfd) ((abfd)->flags) ++#define bfd_applicable_file_flags(abfd) ((abfd)->xvec->object_flags) ++#define bfd_applicable_section_flags(abfd) ((abfd)->xvec->section_flags) ++#define bfd_my_archive(abfd) ((abfd)->my_archive) ++#define bfd_has_map(abfd) ((abfd)->has_armap) ++ ++#define bfd_valid_reloc_types(abfd) ((abfd)->xvec->valid_reloc_types) ++#define bfd_usrdata(abfd) ((abfd)->usrdata) ++ ++#define bfd_get_start_address(abfd) ((abfd)->start_address) ++#define bfd_get_symcount(abfd) ((abfd)->symcount) ++#define bfd_get_outsymbols(abfd) ((abfd)->outsymbols) ++#define bfd_count_sections(abfd) ((abfd)->section_count) ++ ++#define bfd_get_dynamic_symcount(abfd) ((abfd)->dynsymcount) ++ ++#define bfd_get_symbol_leading_char(abfd) ((abfd)->xvec->symbol_leading_char) ++ ++#define bfd_set_cacheable(abfd,bool) (((abfd)->cacheable = bool), TRUE) ++ ++extern bfd_boolean bfd_cache_close ++ (bfd *abfd); ++/* NB: This declaration should match the autogenerated one in libbfd.h. */ ++ ++extern bfd_boolean bfd_cache_close_all (void); ++ ++extern bfd_boolean bfd_record_phdr ++ (bfd *, unsigned long, bfd_boolean, flagword, bfd_boolean, bfd_vma, ++ bfd_boolean, bfd_boolean, unsigned int, struct bfd_section **); ++ ++/* Byte swapping routines. */ ++ ++bfd_uint64_t bfd_getb64 (const void *); ++bfd_uint64_t bfd_getl64 (const void *); ++bfd_int64_t bfd_getb_signed_64 (const void *); ++bfd_int64_t bfd_getl_signed_64 (const void *); ++bfd_vma bfd_getb32 (const void *); ++bfd_vma bfd_getl32 (const void *); ++bfd_signed_vma bfd_getb_signed_32 (const void *); ++bfd_signed_vma bfd_getl_signed_32 (const void *); ++bfd_vma bfd_getb16 (const void *); ++bfd_vma bfd_getl16 (const void *); ++bfd_signed_vma bfd_getb_signed_16 (const void *); ++bfd_signed_vma bfd_getl_signed_16 (const void *); ++void bfd_putb64 (bfd_uint64_t, void *); ++void bfd_putl64 (bfd_uint64_t, void *); ++void bfd_putb32 (bfd_vma, void *); ++void bfd_putl32 (bfd_vma, void *); ++void bfd_putb16 (bfd_vma, void *); ++void bfd_putl16 (bfd_vma, void *); ++ ++/* Byte swapping routines which take size and endiannes as arguments. */ ++ ++bfd_uint64_t bfd_get_bits (const void *, int, bfd_boolean); ++void bfd_put_bits (bfd_uint64_t, void *, int, bfd_boolean); ++ ++extern bfd_boolean bfd_section_already_linked_table_init (void); ++extern void bfd_section_already_linked_table_free (void); ++ ++/* Externally visible ECOFF routines. */ ++ ++#if defined(__STDC__) || defined(ALMOST_STDC) ++struct ecoff_debug_info; ++struct ecoff_debug_swap; ++struct ecoff_extr; ++struct bfd_symbol; ++struct bfd_link_info; ++struct bfd_link_hash_entry; ++struct bfd_elf_version_tree; ++#endif ++extern bfd_vma bfd_ecoff_get_gp_value ++ (bfd * abfd); ++extern bfd_boolean bfd_ecoff_set_gp_value ++ (bfd *abfd, bfd_vma gp_value); ++extern bfd_boolean bfd_ecoff_set_regmasks ++ (bfd *abfd, unsigned long gprmask, unsigned long fprmask, ++ unsigned long *cprmask); ++extern void *bfd_ecoff_debug_init ++ (bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, struct bfd_link_info *); ++extern void bfd_ecoff_debug_free ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_accumulate ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, bfd *input_bfd, ++ struct ecoff_debug_info *input_debug, ++ const struct ecoff_debug_swap *input_swap, struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_accumulate_other ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, bfd *input_bfd, ++ struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_externals ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, bfd_boolean relocatable, ++ bfd_boolean (*get_extr) (struct bfd_symbol *, struct ecoff_extr *), ++ void (*set_index) (struct bfd_symbol *, bfd_size_type)); ++extern bfd_boolean bfd_ecoff_debug_one_external ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, const char *name, ++ struct ecoff_extr *esym); ++extern bfd_size_type bfd_ecoff_debug_size ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap); ++extern bfd_boolean bfd_ecoff_write_debug ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, file_ptr where); ++extern bfd_boolean bfd_ecoff_write_accumulated_debug ++ (void *handle, bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, ++ struct bfd_link_info *info, file_ptr where); ++ ++/* Externally visible ELF routines. */ ++ ++struct bfd_link_needed_list ++{ ++ struct bfd_link_needed_list *next; ++ bfd *by; ++ const char *name; ++}; ++ ++enum dynamic_lib_link_class { ++ DYN_NORMAL = 0, ++ DYN_AS_NEEDED = 1, ++ DYN_DT_NEEDED = 2, ++ DYN_NO_ADD_NEEDED = 4, ++ DYN_NO_NEEDED = 8 ++}; ++ ++extern bfd_boolean bfd_elf_record_link_assignment ++ (bfd *, struct bfd_link_info *, const char *, bfd_boolean, ++ bfd_boolean); ++extern struct bfd_link_needed_list *bfd_elf_get_needed_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_elf_get_bfd_needed_list ++ (bfd *, struct bfd_link_needed_list **); ++extern bfd_boolean bfd_elf_size_dynamic_sections ++ (bfd *, const char *, const char *, const char *, const char * const *, ++ struct bfd_link_info *, struct bfd_section **, ++ struct bfd_elf_version_tree *); ++extern bfd_boolean bfd_elf_size_dynsym_hash_dynstr ++ (bfd *, struct bfd_link_info *); ++extern void bfd_elf_set_dt_needed_name ++ (bfd *, const char *); ++extern const char *bfd_elf_get_dt_soname ++ (bfd *); ++extern void bfd_elf_set_dyn_lib_class ++ (bfd *, int); ++extern int bfd_elf_get_dyn_lib_class ++ (bfd *); ++extern struct bfd_link_needed_list *bfd_elf_get_runpath_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_elf_discard_info ++ (bfd *, struct bfd_link_info *); ++extern unsigned int _bfd_elf_default_action_discarded ++ (struct bfd_section *); ++ ++/* Return an upper bound on the number of bytes required to store a ++ copy of ABFD's program header table entries. Return -1 if an error ++ occurs; bfd_get_error will return an appropriate code. */ ++extern long bfd_get_elf_phdr_upper_bound ++ (bfd *abfd); ++ ++/* Copy ABFD's program header table entries to *PHDRS. The entries ++ will be stored as an array of Elf_Internal_Phdr structures, as ++ defined in include/elf/internal.h. To find out how large the ++ buffer needs to be, call bfd_get_elf_phdr_upper_bound. ++ ++ Return the number of program header table entries read, or -1 if an ++ error occurs; bfd_get_error will return an appropriate code. */ ++extern int bfd_get_elf_phdrs ++ (bfd *abfd, void *phdrs); ++ ++/* Create a new BFD as if by bfd_openr. Rather than opening a file, ++ reconstruct an ELF file by reading the segments out of remote memory ++ based on the ELF file header at EHDR_VMA and the ELF program headers it ++ points to. If not null, *LOADBASEP is filled in with the difference ++ between the VMAs from which the segments were read, and the VMAs the ++ file headers (and hence BFD's idea of each section's VMA) put them at. ++ ++ The function TARGET_READ_MEMORY is called to copy LEN bytes from the ++ remote memory at target address VMA into the local buffer at MYADDR; it ++ should return zero on success or an `errno' code on failure. TEMPL must ++ be a BFD for an ELF target with the word size and byte order found in ++ the remote memory. */ ++extern bfd *bfd_elf_bfd_from_remote_memory ++ (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep, ++ int (*target_read_memory) (bfd_vma vma, bfd_byte *myaddr, int len)); ++ ++/* Return the arch_size field of an elf bfd, or -1 if not elf. */ ++extern int bfd_get_arch_size ++ (bfd *); ++ ++/* Return TRUE if address "naturally" sign extends, or -1 if not elf. */ ++extern int bfd_get_sign_extend_vma ++ (bfd *); ++ ++extern struct bfd_section *_bfd_elf_tls_setup ++ (bfd *, struct bfd_link_info *); ++ ++extern void _bfd_fix_excluded_sec_syms ++ (bfd *, struct bfd_link_info *); ++ ++extern bfd_boolean bfd_m68k_elf32_create_embedded_relocs ++ (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, ++ char **); ++ ++extern bfd_boolean bfd_bfin_elf32_create_embedded_relocs ++ (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, ++ char **); ++ ++/* SunOS shared library support routines for the linker. */ ++ ++extern struct bfd_link_needed_list *bfd_sunos_get_needed_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_sunos_record_link_assignment ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_sunos_size_dynamic_sections ++ (bfd *, struct bfd_link_info *, struct bfd_section **, ++ struct bfd_section **, struct bfd_section **); ++ ++/* Linux shared library support routines for the linker. */ ++ ++extern bfd_boolean bfd_i386linux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_m68klinux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_sparclinux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++ ++/* mmap hacks */ ++ ++struct _bfd_window_internal; ++typedef struct _bfd_window_internal bfd_window_internal; ++ ++typedef struct _bfd_window ++{ ++ /* What the user asked for. */ ++ void *data; ++ bfd_size_type size; ++ /* The actual window used by BFD. Small user-requested read-only ++ regions sharing a page may share a single window into the object ++ file. Read-write versions shouldn't until I've fixed things to ++ keep track of which portions have been claimed by the ++ application; don't want to give the same region back when the ++ application wants two writable copies! */ ++ struct _bfd_window_internal *i; ++} ++bfd_window; ++ ++extern void bfd_init_window ++ (bfd_window *); ++extern void bfd_free_window ++ (bfd_window *); ++extern bfd_boolean bfd_get_file_window ++ (bfd *, file_ptr, bfd_size_type, bfd_window *, bfd_boolean); ++ ++/* XCOFF support routines for the linker. */ ++ ++extern bfd_boolean bfd_xcoff_link_record_set ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *, bfd_size_type); ++extern bfd_boolean bfd_xcoff_import_symbol ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *, bfd_vma, ++ const char *, const char *, const char *, unsigned int); ++extern bfd_boolean bfd_xcoff_export_symbol ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *); ++extern bfd_boolean bfd_xcoff_link_count_reloc ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_xcoff_record_link_assignment ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_xcoff_size_dynamic_sections ++ (bfd *, struct bfd_link_info *, const char *, const char *, ++ unsigned long, unsigned long, unsigned long, bfd_boolean, ++ int, bfd_boolean, bfd_boolean, struct bfd_section **, bfd_boolean); ++extern bfd_boolean bfd_xcoff_link_generate_rtinit ++ (bfd *, const char *, const char *, bfd_boolean); ++ ++/* XCOFF support routines for ar. */ ++extern bfd_boolean bfd_xcoff_ar_archive_set_magic ++ (bfd *, char *); ++ ++/* Externally visible COFF routines. */ ++ ++#if defined(__STDC__) || defined(ALMOST_STDC) ++struct internal_syment; ++union internal_auxent; ++#endif ++ ++extern bfd_boolean bfd_coff_get_syment ++ (bfd *, struct bfd_symbol *, struct internal_syment *); ++ ++extern bfd_boolean bfd_coff_get_auxent ++ (bfd *, struct bfd_symbol *, int, union internal_auxent *); ++ ++extern bfd_boolean bfd_coff_set_symbol_class ++ (bfd *, struct bfd_symbol *, unsigned int); ++ ++extern bfd_boolean bfd_m68k_coff_create_embedded_relocs ++ (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, char **); ++ ++/* ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_arm_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_arm_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++extern bfd_boolean bfd_arm_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++/* PE ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_arm_pe_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_arm_pe_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++extern bfd_boolean bfd_arm_pe_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++/* ELF ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_elf32_arm_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_elf32_arm_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++void bfd_elf32_arm_set_target_relocs ++ (struct bfd_link_info *, int, char *, int, int); ++ ++extern bfd_boolean bfd_elf32_arm_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++extern bfd_boolean bfd_elf32_arm_add_glue_sections_to_bfd ++ (bfd *, struct bfd_link_info *); ++ ++/* ELF ARM mapping symbol support */ ++extern bfd_boolean bfd_is_arm_mapping_symbol_name ++ (const char * name); ++ ++/* ARM Note section processing. */ ++extern bfd_boolean bfd_arm_merge_machines ++ (bfd *, bfd *); ++ ++extern bfd_boolean bfd_arm_update_notes ++ (bfd *, const char *); ++ ++extern unsigned int bfd_arm_get_mach_from_notes ++ (bfd *, const char *); ++ ++/* TI COFF load page support. */ ++extern void bfd_ticoff_set_section_load_page ++ (struct bfd_section *, int); ++ ++extern int bfd_ticoff_get_section_load_page ++ (struct bfd_section *); ++ ++/* H8/300 functions. */ ++extern bfd_vma bfd_h8300_pad_address ++ (bfd *, bfd_vma); ++ ++/* IA64 Itanium code generation. Called from linker. */ ++extern void bfd_elf32_ia64_after_parse ++ (int); ++ ++extern void bfd_elf64_ia64_after_parse ++ (int); ++ ++/* This structure is used for a comdat section, as in PE. A comdat ++ section is associated with a particular symbol. When the linker ++ sees a comdat section, it keeps only one of the sections with a ++ given name and associated with a given symbol. */ ++ ++struct coff_comdat_info ++{ ++ /* The name of the symbol associated with a comdat section. */ ++ const char *name; ++ ++ /* The local symbol table index of the symbol associated with a ++ comdat section. This is only meaningful to the object file format ++ specific code; it is not an index into the list returned by ++ bfd_canonicalize_symtab. */ ++ long symbol; ++}; ++ ++extern struct coff_comdat_info *bfd_coff_get_comdat_section ++ (bfd *, struct bfd_section *); ++ ++/* Extracted from init.c. */ ++void bfd_init (void); ++ ++/* Extracted from opncls.c. */ ++bfd *bfd_fopen (const char *filename, const char *target, ++ const char *mode, int fd); ++ ++bfd *bfd_openr (const char *filename, const char *target); ++ ++bfd *bfd_fdopenr (const char *filename, const char *target, int fd); ++ ++bfd *bfd_openstreamr (const char *, const char *, void *); ++ ++bfd *bfd_openr_iovec (const char *filename, const char *target, ++ void *(*open) (struct bfd *nbfd, ++ void *open_closure), ++ void *open_closure, ++ file_ptr (*pread) (struct bfd *nbfd, ++ void *stream, ++ void *buf, ++ file_ptr nbytes, ++ file_ptr offset), ++ int (*close) (struct bfd *nbfd, ++ void *stream)); ++ ++bfd *bfd_openw (const char *filename, const char *target); ++ ++bfd_boolean bfd_close (bfd *abfd); ++ ++bfd_boolean bfd_close_all_done (bfd *); ++ ++bfd *bfd_create (const char *filename, bfd *templ); ++ ++bfd_boolean bfd_make_writable (bfd *abfd); ++ ++bfd_boolean bfd_make_readable (bfd *abfd); ++ ++unsigned long bfd_calc_gnu_debuglink_crc32 ++ (unsigned long crc, const unsigned char *buf, bfd_size_type len); ++ ++char *bfd_follow_gnu_debuglink (bfd *abfd, const char *dir); ++ ++struct bfd_section *bfd_create_gnu_debuglink_section ++ (bfd *abfd, const char *filename); ++ ++bfd_boolean bfd_fill_in_gnu_debuglink_section ++ (bfd *abfd, struct bfd_section *sect, const char *filename); ++ ++/* Extracted from libbfd.c. */ ++ ++/* Byte swapping macros for user section data. */ ++ ++#define bfd_put_8(abfd, val, ptr) \ ++ ((void) (*((unsigned char *) (ptr)) = (val) & 0xff)) ++#define bfd_put_signed_8 \ ++ bfd_put_8 ++#define bfd_get_8(abfd, ptr) \ ++ (*(unsigned char *) (ptr) & 0xff) ++#define bfd_get_signed_8(abfd, ptr) \ ++ (((*(unsigned char *) (ptr) & 0xff) ^ 0x80) - 0x80) ++ ++#define bfd_put_16(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx16, ((val),(ptr))) ++#define bfd_put_signed_16 \ ++ bfd_put_16 ++#define bfd_get_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx16, (ptr)) ++#define bfd_get_signed_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_16, (ptr)) ++ ++#define bfd_put_32(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx32, ((val),(ptr))) ++#define bfd_put_signed_32 \ ++ bfd_put_32 ++#define bfd_get_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx32, (ptr)) ++#define bfd_get_signed_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_32, (ptr)) ++ ++#define bfd_put_64(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx64, ((val), (ptr))) ++#define bfd_put_signed_64 \ ++ bfd_put_64 ++#define bfd_get_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx64, (ptr)) ++#define bfd_get_signed_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_64, (ptr)) ++ ++#define bfd_get(bits, abfd, ptr) \ ++ ((bits) == 8 ? (bfd_vma) bfd_get_8 (abfd, ptr) \ ++ : (bits) == 16 ? bfd_get_16 (abfd, ptr) \ ++ : (bits) == 32 ? bfd_get_32 (abfd, ptr) \ ++ : (bits) == 64 ? bfd_get_64 (abfd, ptr) \ ++ : (abort (), (bfd_vma) - 1)) ++ ++#define bfd_put(bits, abfd, val, ptr) \ ++ ((bits) == 8 ? bfd_put_8 (abfd, val, ptr) \ ++ : (bits) == 16 ? bfd_put_16 (abfd, val, ptr) \ ++ : (bits) == 32 ? bfd_put_32 (abfd, val, ptr) \ ++ : (bits) == 64 ? bfd_put_64 (abfd, val, ptr) \ ++ : (abort (), (void) 0)) ++ ++ ++/* Byte swapping macros for file header data. */ ++ ++#define bfd_h_put_8(abfd, val, ptr) \ ++ bfd_put_8 (abfd, val, ptr) ++#define bfd_h_put_signed_8(abfd, val, ptr) \ ++ bfd_put_8 (abfd, val, ptr) ++#define bfd_h_get_8(abfd, ptr) \ ++ bfd_get_8 (abfd, ptr) ++#define bfd_h_get_signed_8(abfd, ptr) \ ++ bfd_get_signed_8 (abfd, ptr) ++ ++#define bfd_h_put_16(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx16, (val, ptr)) ++#define bfd_h_put_signed_16 \ ++ bfd_h_put_16 ++#define bfd_h_get_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx16, (ptr)) ++#define bfd_h_get_signed_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_16, (ptr)) ++ ++#define bfd_h_put_32(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx32, (val, ptr)) ++#define bfd_h_put_signed_32 \ ++ bfd_h_put_32 ++#define bfd_h_get_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx32, (ptr)) ++#define bfd_h_get_signed_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_32, (ptr)) ++ ++#define bfd_h_put_64(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx64, (val, ptr)) ++#define bfd_h_put_signed_64 \ ++ bfd_h_put_64 ++#define bfd_h_get_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx64, (ptr)) ++#define bfd_h_get_signed_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_64, (ptr)) ++ ++/* Aliases for the above, which should eventually go away. */ ++ ++#define H_PUT_64 bfd_h_put_64 ++#define H_PUT_32 bfd_h_put_32 ++#define H_PUT_16 bfd_h_put_16 ++#define H_PUT_8 bfd_h_put_8 ++#define H_PUT_S64 bfd_h_put_signed_64 ++#define H_PUT_S32 bfd_h_put_signed_32 ++#define H_PUT_S16 bfd_h_put_signed_16 ++#define H_PUT_S8 bfd_h_put_signed_8 ++#define H_GET_64 bfd_h_get_64 ++#define H_GET_32 bfd_h_get_32 ++#define H_GET_16 bfd_h_get_16 ++#define H_GET_8 bfd_h_get_8 ++#define H_GET_S64 bfd_h_get_signed_64 ++#define H_GET_S32 bfd_h_get_signed_32 ++#define H_GET_S16 bfd_h_get_signed_16 ++#define H_GET_S8 bfd_h_get_signed_8 ++ ++ ++/* Extracted from bfdio.c. */ ++long bfd_get_mtime (bfd *abfd); ++ ++long bfd_get_size (bfd *abfd); ++ ++/* Extracted from bfdwin.c. */ ++/* Extracted from section.c. */ ++typedef struct bfd_section ++{ ++ /* The name of the section; the name isn't a copy, the pointer is ++ the same as that passed to bfd_make_section. */ ++ const char *name; ++ ++ /* A unique sequence number. */ ++ int id; ++ ++ /* Which section in the bfd; 0..n-1 as sections are created in a bfd. */ ++ int index; ++ ++ /* The next section in the list belonging to the BFD, or NULL. */ ++ struct bfd_section *next; ++ ++ /* The previous section in the list belonging to the BFD, or NULL. */ ++ struct bfd_section *prev; ++ ++ /* The field flags contains attributes of the section. Some ++ flags are read in from the object file, and some are ++ synthesized from other information. */ ++ flagword flags; ++ ++#define SEC_NO_FLAGS 0x000 ++ ++ /* Tells the OS to allocate space for this section when loading. ++ This is clear for a section containing debug information only. */ ++#define SEC_ALLOC 0x001 ++ ++ /* Tells the OS to load the section from the file when loading. ++ This is clear for a .bss section. */ ++#define SEC_LOAD 0x002 ++ ++ /* The section contains data still to be relocated, so there is ++ some relocation information too. */ ++#define SEC_RELOC 0x004 ++ ++ /* A signal to the OS that the section contains read only data. */ ++#define SEC_READONLY 0x008 ++ ++ /* The section contains code only. */ ++#define SEC_CODE 0x010 ++ ++ /* The section contains data only. */ ++#define SEC_DATA 0x020 ++ ++ /* The section will reside in ROM. */ ++#define SEC_ROM 0x040 ++ ++ /* The section contains constructor information. This section ++ type is used by the linker to create lists of constructors and ++ destructors used by <>. When a back end sees a symbol ++ which should be used in a constructor list, it creates a new ++ section for the type of name (e.g., <<__CTOR_LIST__>>), attaches ++ the symbol to it, and builds a relocation. To build the lists ++ of constructors, all the linker has to do is catenate all the ++ sections called <<__CTOR_LIST__>> and relocate the data ++ contained within - exactly the operations it would peform on ++ standard data. */ ++#define SEC_CONSTRUCTOR 0x080 ++ ++ /* The section has contents - a data section could be ++ <> | <>; a debug section could be ++ <> */ ++#define SEC_HAS_CONTENTS 0x100 ++ ++ /* An instruction to the linker to not output the section ++ even if it has information which would normally be written. */ ++#define SEC_NEVER_LOAD 0x200 ++ ++ /* The section contains thread local data. */ ++#define SEC_THREAD_LOCAL 0x400 ++ ++ /* The section has GOT references. This flag is only for the ++ linker, and is currently only used by the elf32-hppa back end. ++ It will be set if global offset table references were detected ++ in this section, which indicate to the linker that the section ++ contains PIC code, and must be handled specially when doing a ++ static link. */ ++#define SEC_HAS_GOT_REF 0x800 ++ ++ /* The section contains common symbols (symbols may be defined ++ multiple times, the value of a symbol is the amount of ++ space it requires, and the largest symbol value is the one ++ used). Most targets have exactly one of these (which we ++ translate to bfd_com_section_ptr), but ECOFF has two. */ ++#define SEC_IS_COMMON 0x1000 ++ ++ /* The section contains only debugging information. For ++ example, this is set for ELF .debug and .stab sections. ++ strip tests this flag to see if a section can be ++ discarded. */ ++#define SEC_DEBUGGING 0x2000 ++ ++ /* The contents of this section are held in memory pointed to ++ by the contents field. This is checked by bfd_get_section_contents, ++ and the data is retrieved from memory if appropriate. */ ++#define SEC_IN_MEMORY 0x4000 ++ ++ /* The contents of this section are to be excluded by the ++ linker for executable and shared objects unless those ++ objects are to be further relocated. */ ++#define SEC_EXCLUDE 0x8000 ++ ++ /* The contents of this section are to be sorted based on the sum of ++ the symbol and addend values specified by the associated relocation ++ entries. Entries without associated relocation entries will be ++ appended to the end of the section in an unspecified order. */ ++#define SEC_SORT_ENTRIES 0x10000 ++ ++ /* When linking, duplicate sections of the same name should be ++ discarded, rather than being combined into a single section as ++ is usually done. This is similar to how common symbols are ++ handled. See SEC_LINK_DUPLICATES below. */ ++#define SEC_LINK_ONCE 0x20000 ++ ++ /* If SEC_LINK_ONCE is set, this bitfield describes how the linker ++ should handle duplicate sections. */ ++#define SEC_LINK_DUPLICATES 0x40000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that duplicate ++ sections with the same name should simply be discarded. */ ++#define SEC_LINK_DUPLICATES_DISCARD 0x0 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if there are any duplicate sections, although ++ it should still only link one copy. */ ++#define SEC_LINK_DUPLICATES_ONE_ONLY 0x80000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if any duplicate sections are a different size. */ ++#define SEC_LINK_DUPLICATES_SAME_SIZE 0x100000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if any duplicate sections contain different ++ contents. */ ++#define SEC_LINK_DUPLICATES_SAME_CONTENTS \ ++ (SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE) ++ ++ /* This section was created by the linker as part of dynamic ++ relocation or other arcane processing. It is skipped when ++ going through the first-pass output, trusting that someone ++ else up the line will take care of it later. */ ++#define SEC_LINKER_CREATED 0x200000 ++ ++ /* This section should not be subject to garbage collection. */ ++#define SEC_KEEP 0x400000 ++ ++ /* This section contains "short" data, and should be placed ++ "near" the GP. */ ++#define SEC_SMALL_DATA 0x800000 ++ ++ /* Attempt to merge identical entities in the section. ++ Entity size is given in the entsize field. */ ++#define SEC_MERGE 0x1000000 ++ ++ /* If given with SEC_MERGE, entities to merge are zero terminated ++ strings where entsize specifies character size instead of fixed ++ size entries. */ ++#define SEC_STRINGS 0x2000000 ++ ++ /* This section contains data about section groups. */ ++#define SEC_GROUP 0x4000000 ++ ++ /* The section is a COFF shared library section. This flag is ++ only for the linker. If this type of section appears in ++ the input file, the linker must copy it to the output file ++ without changing the vma or size. FIXME: Although this ++ was originally intended to be general, it really is COFF ++ specific (and the flag was renamed to indicate this). It ++ might be cleaner to have some more general mechanism to ++ allow the back end to control what the linker does with ++ sections. */ ++#define SEC_COFF_SHARED_LIBRARY 0x10000000 ++ ++ /* This section contains data which may be shared with other ++ executables or shared objects. This is for COFF only. */ ++#define SEC_COFF_SHARED 0x20000000 ++ ++ /* When a section with this flag is being linked, then if the size of ++ the input section is less than a page, it should not cross a page ++ boundary. If the size of the input section is one page or more, ++ it should be aligned on a page boundary. This is for TI ++ TMS320C54X only. */ ++#define SEC_TIC54X_BLOCK 0x40000000 ++ ++ /* Conditionally link this section; do not link if there are no ++ references found to any symbol in the section. This is for TI ++ TMS320C54X only. */ ++#define SEC_TIC54X_CLINK 0x80000000 ++ ++ /* End of section flags. */ ++ ++ /* Some internal packed boolean fields. */ ++ ++ /* See the vma field. */ ++ unsigned int user_set_vma : 1; ++ ++ /* A mark flag used by some of the linker backends. */ ++ unsigned int linker_mark : 1; ++ ++ /* Another mark flag used by some of the linker backends. Set for ++ output sections that have an input section. */ ++ unsigned int linker_has_input : 1; ++ ++ /* Mark flags used by some linker backends for garbage collection. */ ++ unsigned int gc_mark : 1; ++ unsigned int gc_mark_from_eh : 1; ++ ++ /* The following flags are used by the ELF linker. */ ++ ++ /* Mark sections which have been allocated to segments. */ ++ unsigned int segment_mark : 1; ++ ++ /* Type of sec_info information. */ ++ unsigned int sec_info_type:3; ++#define ELF_INFO_TYPE_NONE 0 ++#define ELF_INFO_TYPE_STABS 1 ++#define ELF_INFO_TYPE_MERGE 2 ++#define ELF_INFO_TYPE_EH_FRAME 3 ++#define ELF_INFO_TYPE_JUST_SYMS 4 ++ ++ /* Nonzero if this section uses RELA relocations, rather than REL. */ ++ unsigned int use_rela_p:1; ++ ++ /* Bits used by various backends. The generic code doesn't touch ++ these fields. */ ++ ++ /* Nonzero if this section has TLS related relocations. */ ++ unsigned int has_tls_reloc:1; ++ ++ /* Nonzero if this section has a gp reloc. */ ++ unsigned int has_gp_reloc:1; ++ ++ /* Nonzero if this section needs the relax finalize pass. */ ++ unsigned int need_finalize_relax:1; ++ ++ /* Whether relocations have been processed. */ ++ unsigned int reloc_done : 1; ++ ++ /* End of internal packed boolean fields. */ ++ ++ /* The virtual memory address of the section - where it will be ++ at run time. The symbols are relocated against this. The ++ user_set_vma flag is maintained by bfd; if it's not set, the ++ backend can assign addresses (for example, in <>, where ++ the default address for <<.data>> is dependent on the specific ++ target and various flags). */ ++ bfd_vma vma; ++ ++ /* The load address of the section - where it would be in a ++ rom image; really only used for writing section header ++ information. */ ++ bfd_vma lma; ++ ++ /* The size of the section in octets, as it will be output. ++ Contains a value even if the section has no contents (e.g., the ++ size of <<.bss>>). */ ++ bfd_size_type size; ++ ++ /* For input sections, the original size on disk of the section, in ++ octets. This field is used by the linker relaxation code. It is ++ currently only set for sections where the linker relaxation scheme ++ doesn't cache altered section and reloc contents (stabs, eh_frame, ++ SEC_MERGE, some coff relaxing targets), and thus the original size ++ needs to be kept to read the section multiple times. ++ For output sections, rawsize holds the section size calculated on ++ a previous linker relaxation pass. */ ++ bfd_size_type rawsize; ++ ++ /* If this section is going to be output, then this value is the ++ offset in *bytes* into the output section of the first byte in the ++ input section (byte ==> smallest addressable unit on the ++ target). In most cases, if this was going to start at the ++ 100th octet (8-bit quantity) in the output section, this value ++ would be 100. However, if the target byte size is 16 bits ++ (bfd_octets_per_byte is "2"), this value would be 50. */ ++ bfd_vma output_offset; ++ ++ /* The output section through which to map on output. */ ++ struct bfd_section *output_section; ++ ++ /* The alignment requirement of the section, as an exponent of 2 - ++ e.g., 3 aligns to 2^3 (or 8). */ ++ unsigned int alignment_power; ++ ++ /* If an input section, a pointer to a vector of relocation ++ records for the data in this section. */ ++ struct reloc_cache_entry *relocation; ++ ++ /* If an output section, a pointer to a vector of pointers to ++ relocation records for the data in this section. */ ++ struct reloc_cache_entry **orelocation; ++ ++ /* The number of relocation records in one of the above. */ ++ unsigned reloc_count; ++ ++ /* Information below is back end specific - and not always used ++ or updated. */ ++ ++ /* File position of section data. */ ++ file_ptr filepos; ++ ++ /* File position of relocation info. */ ++ file_ptr rel_filepos; ++ ++ /* File position of line data. */ ++ file_ptr line_filepos; ++ ++ /* Pointer to data for applications. */ ++ void *userdata; ++ ++ /* If the SEC_IN_MEMORY flag is set, this points to the actual ++ contents. */ ++ unsigned char *contents; ++ ++ /* Attached line number information. */ ++ alent *lineno; ++ ++ /* Number of line number records. */ ++ unsigned int lineno_count; ++ ++ /* Entity size for merging purposes. */ ++ unsigned int entsize; ++ ++ /* Points to the kept section if this section is a link-once section, ++ and is discarded. */ ++ struct bfd_section *kept_section; ++ ++ /* When a section is being output, this value changes as more ++ linenumbers are written out. */ ++ file_ptr moving_line_filepos; ++ ++ /* What the section number is in the target world. */ ++ int target_index; ++ ++ void *used_by_bfd; ++ ++ /* If this is a constructor section then here is a list of the ++ relocations created to relocate items within it. */ ++ struct relent_chain *constructor_chain; ++ ++ /* The BFD which owns the section. */ ++ bfd *owner; ++ ++ /* A symbol which points at this section only. */ ++ struct bfd_symbol *symbol; ++ struct bfd_symbol **symbol_ptr_ptr; ++ ++ /* Early in the link process, map_head and map_tail are used to build ++ a list of input sections attached to an output section. Later, ++ output sections use these fields for a list of bfd_link_order ++ structs. */ ++ union { ++ struct bfd_link_order *link_order; ++ struct bfd_section *s; ++ } map_head, map_tail; ++} asection; ++ ++/* These sections are global, and are managed by BFD. The application ++ and target back end are not permitted to change the values in ++ these sections. New code should use the section_ptr macros rather ++ than referring directly to the const sections. The const sections ++ may eventually vanish. */ ++#define BFD_ABS_SECTION_NAME "*ABS*" ++#define BFD_UND_SECTION_NAME "*UND*" ++#define BFD_COM_SECTION_NAME "*COM*" ++#define BFD_IND_SECTION_NAME "*IND*" ++ ++/* The absolute section. */ ++extern asection bfd_abs_section; ++#define bfd_abs_section_ptr ((asection *) &bfd_abs_section) ++#define bfd_is_abs_section(sec) ((sec) == bfd_abs_section_ptr) ++/* Pointer to the undefined section. */ ++extern asection bfd_und_section; ++#define bfd_und_section_ptr ((asection *) &bfd_und_section) ++#define bfd_is_und_section(sec) ((sec) == bfd_und_section_ptr) ++/* Pointer to the common section. */ ++extern asection bfd_com_section; ++#define bfd_com_section_ptr ((asection *) &bfd_com_section) ++/* Pointer to the indirect section. */ ++extern asection bfd_ind_section; ++#define bfd_ind_section_ptr ((asection *) &bfd_ind_section) ++#define bfd_is_ind_section(sec) ((sec) == bfd_ind_section_ptr) ++ ++#define bfd_is_const_section(SEC) \ ++ ( ((SEC) == bfd_abs_section_ptr) \ ++ || ((SEC) == bfd_und_section_ptr) \ ++ || ((SEC) == bfd_com_section_ptr) \ ++ || ((SEC) == bfd_ind_section_ptr)) ++ ++extern const struct bfd_symbol * const bfd_abs_symbol; ++extern const struct bfd_symbol * const bfd_com_symbol; ++extern const struct bfd_symbol * const bfd_und_symbol; ++extern const struct bfd_symbol * const bfd_ind_symbol; ++ ++/* Macros to handle insertion and deletion of a bfd's sections. These ++ only handle the list pointers, ie. do not adjust section_count, ++ target_index etc. */ ++#define bfd_section_list_remove(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ asection *_next = _s->next; \ ++ asection *_prev = _s->prev; \ ++ if (_prev) \ ++ _prev->next = _next; \ ++ else \ ++ (ABFD)->sections = _next; \ ++ if (_next) \ ++ _next->prev = _prev; \ ++ else \ ++ (ABFD)->section_last = _prev; \ ++ } \ ++ while (0) ++#define bfd_section_list_append(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ bfd *_abfd = ABFD; \ ++ _s->next = NULL; \ ++ if (_abfd->section_last) \ ++ { \ ++ _s->prev = _abfd->section_last; \ ++ _abfd->section_last->next = _s; \ ++ } \ ++ else \ ++ { \ ++ _s->prev = NULL; \ ++ _abfd->sections = _s; \ ++ } \ ++ _abfd->section_last = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_prepend(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ bfd *_abfd = ABFD; \ ++ _s->prev = NULL; \ ++ if (_abfd->sections) \ ++ { \ ++ _s->next = _abfd->sections; \ ++ _abfd->sections->prev = _s; \ ++ } \ ++ else \ ++ { \ ++ _s->next = NULL; \ ++ _abfd->section_last = _s; \ ++ } \ ++ _abfd->sections = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_insert_after(ABFD, A, S) \ ++ do \ ++ { \ ++ asection *_a = A; \ ++ asection *_s = S; \ ++ asection *_next = _a->next; \ ++ _s->next = _next; \ ++ _s->prev = _a; \ ++ _a->next = _s; \ ++ if (_next) \ ++ _next->prev = _s; \ ++ else \ ++ (ABFD)->section_last = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_insert_before(ABFD, B, S) \ ++ do \ ++ { \ ++ asection *_b = B; \ ++ asection *_s = S; \ ++ asection *_prev = _b->prev; \ ++ _s->prev = _prev; \ ++ _s->next = _b; \ ++ _b->prev = _s; \ ++ if (_prev) \ ++ _prev->next = _s; \ ++ else \ ++ (ABFD)->sections = _s; \ ++ } \ ++ while (0) ++#define bfd_section_removed_from_list(ABFD, S) \ ++ ((S)->next == NULL ? (ABFD)->section_last != (S) : (S)->next->prev != (S)) ++ ++#define BFD_FAKE_SECTION(SEC, FLAGS, SYM, SYM_PTR, NAME, IDX) \ ++ /* name, id, index, next, prev, flags, user_set_vma, */ \ ++ { NAME, IDX, 0, NULL, NULL, FLAGS, 0, \ ++ \ ++ /* linker_mark, linker_has_input, gc_mark, gc_mark_from_eh, */ \ ++ 0, 0, 1, 0, \ ++ \ ++ /* segment_mark, sec_info_type, use_rela_p, has_tls_reloc, */ \ ++ 0, 0, 0, 0, \ ++ \ ++ /* has_gp_reloc, need_finalize_relax, reloc_done, */ \ ++ 0, 0, 0, \ ++ \ ++ /* vma, lma, size, rawsize */ \ ++ 0, 0, 0, 0, \ ++ \ ++ /* output_offset, output_section, alignment_power, */ \ ++ 0, (struct bfd_section *) &SEC, 0, \ ++ \ ++ /* relocation, orelocation, reloc_count, filepos, rel_filepos, */ \ ++ NULL, NULL, 0, 0, 0, \ ++ \ ++ /* line_filepos, userdata, contents, lineno, lineno_count, */ \ ++ 0, NULL, NULL, NULL, 0, \ ++ \ ++ /* entsize, kept_section, moving_line_filepos, */ \ ++ 0, NULL, 0, \ ++ \ ++ /* target_index, used_by_bfd, constructor_chain, owner, */ \ ++ 0, NULL, NULL, NULL, \ ++ \ ++ /* symbol, */ \ ++ (struct bfd_symbol *) SYM, \ ++ \ ++ /* symbol_ptr_ptr, */ \ ++ (struct bfd_symbol **) SYM_PTR, \ ++ \ ++ /* map_head, map_tail */ \ ++ { NULL }, { NULL } \ ++ } ++ ++void bfd_section_list_clear (bfd *); ++ ++asection *bfd_get_section_by_name (bfd *abfd, const char *name); ++ ++asection *bfd_get_section_by_name_if ++ (bfd *abfd, ++ const char *name, ++ bfd_boolean (*func) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++char *bfd_get_unique_section_name ++ (bfd *abfd, const char *templat, int *count); ++ ++asection *bfd_make_section_old_way (bfd *abfd, const char *name); ++ ++asection *bfd_make_section_anyway_with_flags ++ (bfd *abfd, const char *name, flagword flags); ++ ++asection *bfd_make_section_anyway (bfd *abfd, const char *name); ++ ++asection *bfd_make_section_with_flags ++ (bfd *, const char *name, flagword flags); ++ ++asection *bfd_make_section (bfd *, const char *name); ++ ++bfd_boolean bfd_set_section_flags ++ (bfd *abfd, asection *sec, flagword flags); ++ ++void bfd_map_over_sections ++ (bfd *abfd, ++ void (*func) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++asection *bfd_sections_find_if ++ (bfd *abfd, ++ bfd_boolean (*operation) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++bfd_boolean bfd_set_section_size ++ (bfd *abfd, asection *sec, bfd_size_type val); ++ ++bfd_boolean bfd_set_section_contents ++ (bfd *abfd, asection *section, const void *data, ++ file_ptr offset, bfd_size_type count); ++ ++bfd_boolean bfd_get_section_contents ++ (bfd *abfd, asection *section, void *location, file_ptr offset, ++ bfd_size_type count); ++ ++bfd_boolean bfd_malloc_and_get_section ++ (bfd *abfd, asection *section, bfd_byte **buf); ++ ++bfd_boolean bfd_copy_private_section_data ++ (bfd *ibfd, asection *isec, bfd *obfd, asection *osec); ++ ++#define bfd_copy_private_section_data(ibfd, isection, obfd, osection) \ ++ BFD_SEND (obfd, _bfd_copy_private_section_data, \ ++ (ibfd, isection, obfd, osection)) ++bfd_boolean bfd_generic_is_group_section (bfd *, const asection *sec); ++ ++bfd_boolean bfd_generic_discard_group (bfd *abfd, asection *group); ++ ++/* Extracted from archures.c. */ ++enum bfd_architecture ++{ ++ bfd_arch_unknown, /* File arch not known. */ ++ bfd_arch_obscure, /* Arch known, not one of these. */ ++ bfd_arch_m68k, /* Motorola 68xxx */ ++#define bfd_mach_m68000 1 ++#define bfd_mach_m68008 2 ++#define bfd_mach_m68010 3 ++#define bfd_mach_m68020 4 ++#define bfd_mach_m68030 5 ++#define bfd_mach_m68040 6 ++#define bfd_mach_m68060 7 ++#define bfd_mach_cpu32 8 ++#define bfd_mach_mcf5200 9 ++#define bfd_mach_mcf5206e 10 ++#define bfd_mach_mcf5307 11 ++#define bfd_mach_mcf5407 12 ++#define bfd_mach_mcf528x 13 ++#define bfd_mach_mcfv4e 14 ++#define bfd_mach_mcf521x 15 ++#define bfd_mach_mcf5249 16 ++#define bfd_mach_mcf547x 17 ++#define bfd_mach_mcf548x 18 ++ bfd_arch_vax, /* DEC Vax */ ++ bfd_arch_i960, /* Intel 960 */ ++ /* The order of the following is important. ++ lower number indicates a machine type that ++ only accepts a subset of the instructions ++ available to machines with higher numbers. ++ The exception is the "ca", which is ++ incompatible with all other machines except ++ "core". */ ++ ++#define bfd_mach_i960_core 1 ++#define bfd_mach_i960_ka_sa 2 ++#define bfd_mach_i960_kb_sb 3 ++#define bfd_mach_i960_mc 4 ++#define bfd_mach_i960_xa 5 ++#define bfd_mach_i960_ca 6 ++#define bfd_mach_i960_jx 7 ++#define bfd_mach_i960_hx 8 ++ ++ bfd_arch_or32, /* OpenRISC 32 */ ++ ++ bfd_arch_sparc, /* SPARC */ ++#define bfd_mach_sparc 1 ++/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */ ++#define bfd_mach_sparc_sparclet 2 ++#define bfd_mach_sparc_sparclite 3 ++#define bfd_mach_sparc_v8plus 4 ++#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */ ++#define bfd_mach_sparc_sparclite_le 6 ++#define bfd_mach_sparc_v9 7 ++#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */ ++#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */ ++#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */ ++/* Nonzero if MACH has the v9 instruction set. */ ++#define bfd_mach_sparc_v9_p(mach) \ ++ ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \ ++ && (mach) != bfd_mach_sparc_sparclite_le) ++/* Nonzero if MACH is a 64 bit sparc architecture. */ ++#define bfd_mach_sparc_64bit_p(mach) \ ++ ((mach) >= bfd_mach_sparc_v9 && (mach) != bfd_mach_sparc_v8plusb) ++ bfd_arch_mips, /* MIPS Rxxxx */ ++#define bfd_mach_mips3000 3000 ++#define bfd_mach_mips3900 3900 ++#define bfd_mach_mips4000 4000 ++#define bfd_mach_mips4010 4010 ++#define bfd_mach_mips4100 4100 ++#define bfd_mach_mips4111 4111 ++#define bfd_mach_mips4120 4120 ++#define bfd_mach_mips4300 4300 ++#define bfd_mach_mips4400 4400 ++#define bfd_mach_mips4600 4600 ++#define bfd_mach_mips4650 4650 ++#define bfd_mach_mips5000 5000 ++#define bfd_mach_mips5400 5400 ++#define bfd_mach_mips5500 5500 ++#define bfd_mach_mips6000 6000 ++#define bfd_mach_mips7000 7000 ++#define bfd_mach_mips8000 8000 ++#define bfd_mach_mips9000 9000 ++#define bfd_mach_mips10000 10000 ++#define bfd_mach_mips12000 12000 ++#define bfd_mach_mips16 16 ++#define bfd_mach_mips5 5 ++#define bfd_mach_mips_sb1 12310201 /* octal 'SB', 01 */ ++#define bfd_mach_mipsisa32 32 ++#define bfd_mach_mipsisa32r2 33 ++#define bfd_mach_mipsisa64 64 ++#define bfd_mach_mipsisa64r2 65 ++ bfd_arch_i386, /* Intel 386 */ ++#define bfd_mach_i386_i386 1 ++#define bfd_mach_i386_i8086 2 ++#define bfd_mach_i386_i386_intel_syntax 3 ++#define bfd_mach_x86_64 64 ++#define bfd_mach_x86_64_intel_syntax 65 ++ bfd_arch_we32k, /* AT&T WE32xxx */ ++ bfd_arch_tahoe, /* CCI/Harris Tahoe */ ++ bfd_arch_i860, /* Intel 860 */ ++ bfd_arch_i370, /* IBM 360/370 Mainframes */ ++ bfd_arch_romp, /* IBM ROMP PC/RT */ ++ bfd_arch_convex, /* Convex */ ++ bfd_arch_m88k, /* Motorola 88xxx */ ++ bfd_arch_m98k, /* Motorola 98xxx */ ++ bfd_arch_pyramid, /* Pyramid Technology */ ++ bfd_arch_h8300, /* Renesas H8/300 (formerly Hitachi H8/300) */ ++#define bfd_mach_h8300 1 ++#define bfd_mach_h8300h 2 ++#define bfd_mach_h8300s 3 ++#define bfd_mach_h8300hn 4 ++#define bfd_mach_h8300sn 5 ++#define bfd_mach_h8300sx 6 ++#define bfd_mach_h8300sxn 7 ++ bfd_arch_pdp11, /* DEC PDP-11 */ ++ bfd_arch_powerpc, /* PowerPC */ ++#define bfd_mach_ppc 32 ++#define bfd_mach_ppc64 64 ++#define bfd_mach_ppc_403 403 ++#define bfd_mach_ppc_403gc 4030 ++#define bfd_mach_ppc_505 505 ++#define bfd_mach_ppc_601 601 ++#define bfd_mach_ppc_602 602 ++#define bfd_mach_ppc_603 603 ++#define bfd_mach_ppc_ec603e 6031 ++#define bfd_mach_ppc_604 604 ++#define bfd_mach_ppc_620 620 ++#define bfd_mach_ppc_630 630 ++#define bfd_mach_ppc_750 750 ++#define bfd_mach_ppc_860 860 ++#define bfd_mach_ppc_a35 35 ++#define bfd_mach_ppc_rs64ii 642 ++#define bfd_mach_ppc_rs64iii 643 ++#define bfd_mach_ppc_7400 7400 ++#define bfd_mach_ppc_e500 500 ++ bfd_arch_rs6000, /* IBM RS/6000 */ ++#define bfd_mach_rs6k 6000 ++#define bfd_mach_rs6k_rs1 6001 ++#define bfd_mach_rs6k_rsc 6003 ++#define bfd_mach_rs6k_rs2 6002 ++ bfd_arch_hppa, /* HP PA RISC */ ++#define bfd_mach_hppa10 10 ++#define bfd_mach_hppa11 11 ++#define bfd_mach_hppa20 20 ++#define bfd_mach_hppa20w 25 ++ bfd_arch_d10v, /* Mitsubishi D10V */ ++#define bfd_mach_d10v 1 ++#define bfd_mach_d10v_ts2 2 ++#define bfd_mach_d10v_ts3 3 ++ bfd_arch_d30v, /* Mitsubishi D30V */ ++ bfd_arch_dlx, /* DLX */ ++ bfd_arch_m68hc11, /* Motorola 68HC11 */ ++ bfd_arch_m68hc12, /* Motorola 68HC12 */ ++#define bfd_mach_m6812_default 0 ++#define bfd_mach_m6812 1 ++#define bfd_mach_m6812s 2 ++ bfd_arch_z8k, /* Zilog Z8000 */ ++#define bfd_mach_z8001 1 ++#define bfd_mach_z8002 2 ++ bfd_arch_h8500, /* Renesas H8/500 (formerly Hitachi H8/500) */ ++ bfd_arch_sh, /* Renesas / SuperH SH (formerly Hitachi SH) */ ++#define bfd_mach_sh 1 ++#define bfd_mach_sh2 0x20 ++#define bfd_mach_sh_dsp 0x2d ++#define bfd_mach_sh2a 0x2a ++#define bfd_mach_sh2a_nofpu 0x2b ++#define bfd_mach_sh2a_nofpu_or_sh4_nommu_nofpu 0x2a1 ++#define bfd_mach_sh2a_nofpu_or_sh3_nommu 0x2a2 ++#define bfd_mach_sh2a_or_sh4 0x2a3 ++#define bfd_mach_sh2a_or_sh3e 0x2a4 ++#define bfd_mach_sh2e 0x2e ++#define bfd_mach_sh3 0x30 ++#define bfd_mach_sh3_nommu 0x31 ++#define bfd_mach_sh3_dsp 0x3d ++#define bfd_mach_sh3e 0x3e ++#define bfd_mach_sh4 0x40 ++#define bfd_mach_sh4_nofpu 0x41 ++#define bfd_mach_sh4_nommu_nofpu 0x42 ++#define bfd_mach_sh4a 0x4a ++#define bfd_mach_sh4a_nofpu 0x4b ++#define bfd_mach_sh4al_dsp 0x4d ++#define bfd_mach_sh5 0x50 ++ bfd_arch_alpha, /* Dec Alpha */ ++#define bfd_mach_alpha_ev4 0x10 ++#define bfd_mach_alpha_ev5 0x20 ++#define bfd_mach_alpha_ev6 0x30 ++ bfd_arch_arm, /* Advanced Risc Machines ARM. */ ++#define bfd_mach_arm_unknown 0 ++#define bfd_mach_arm_2 1 ++#define bfd_mach_arm_2a 2 ++#define bfd_mach_arm_3 3 ++#define bfd_mach_arm_3M 4 ++#define bfd_mach_arm_4 5 ++#define bfd_mach_arm_4T 6 ++#define bfd_mach_arm_5 7 ++#define bfd_mach_arm_5T 8 ++#define bfd_mach_arm_5TE 9 ++#define bfd_mach_arm_XScale 10 ++#define bfd_mach_arm_ep9312 11 ++#define bfd_mach_arm_iWMMXt 12 ++ bfd_arch_ns32k, /* National Semiconductors ns32000 */ ++ bfd_arch_w65, /* WDC 65816 */ ++ bfd_arch_tic30, /* Texas Instruments TMS320C30 */ ++ bfd_arch_tic4x, /* Texas Instruments TMS320C3X/4X */ ++#define bfd_mach_tic3x 30 ++#define bfd_mach_tic4x 40 ++ bfd_arch_tic54x, /* Texas Instruments TMS320C54X */ ++ bfd_arch_tic80, /* TI TMS320c80 (MVP) */ ++ bfd_arch_v850, /* NEC V850 */ ++#define bfd_mach_v850 1 ++#define bfd_mach_v850e 'E' ++#define bfd_mach_v850e1 '1' ++ bfd_arch_arc, /* ARC Cores */ ++#define bfd_mach_arc_5 5 ++#define bfd_mach_arc_6 6 ++#define bfd_mach_arc_7 7 ++#define bfd_mach_arc_8 8 ++ bfd_arch_m32c, /* Renesas M16C/M32C. */ ++#define bfd_mach_m16c 0x75 ++#define bfd_mach_m32c 0x78 ++ bfd_arch_m32r, /* Renesas M32R (formerly Mitsubishi M32R/D) */ ++#define bfd_mach_m32r 1 /* For backwards compatibility. */ ++#define bfd_mach_m32rx 'x' ++#define bfd_mach_m32r2 '2' ++ bfd_arch_mn10200, /* Matsushita MN10200 */ ++ bfd_arch_mn10300, /* Matsushita MN10300 */ ++#define bfd_mach_mn10300 300 ++#define bfd_mach_am33 330 ++#define bfd_mach_am33_2 332 ++ bfd_arch_fr30, ++#define bfd_mach_fr30 0x46523330 ++ bfd_arch_frv, ++#define bfd_mach_frv 1 ++#define bfd_mach_frvsimple 2 ++#define bfd_mach_fr300 300 ++#define bfd_mach_fr400 400 ++#define bfd_mach_fr450 450 ++#define bfd_mach_frvtomcat 499 /* fr500 prototype */ ++#define bfd_mach_fr500 500 ++#define bfd_mach_fr550 550 ++ bfd_arch_mcore, ++ bfd_arch_ia64, /* HP/Intel ia64 */ ++#define bfd_mach_ia64_elf64 64 ++#define bfd_mach_ia64_elf32 32 ++ bfd_arch_ip2k, /* Ubicom IP2K microcontrollers. */ ++#define bfd_mach_ip2022 1 ++#define bfd_mach_ip2022ext 2 ++ bfd_arch_iq2000, /* Vitesse IQ2000. */ ++#define bfd_mach_iq2000 1 ++#define bfd_mach_iq10 2 ++ bfd_arch_mt, ++#define bfd_mach_ms1 1 ++#define bfd_mach_mrisc2 2 ++#define bfd_mach_ms2 3 ++ bfd_arch_pj, ++ bfd_arch_avr, /* Atmel AVR microcontrollers. */ ++#define bfd_mach_avr1 1 ++#define bfd_mach_avr2 2 ++#define bfd_mach_avr3 3 ++#define bfd_mach_avr4 4 ++#define bfd_mach_avr5 5 ++ bfd_arch_bfin, /* ADI Blackfin */ ++#define bfd_mach_bfin 1 ++ bfd_arch_cr16c, /* National Semiconductor CompactRISC. */ ++#define bfd_mach_cr16c 1 ++ bfd_arch_crx, /* National Semiconductor CRX. */ ++#define bfd_mach_crx 1 ++ bfd_arch_cris, /* Axis CRIS */ ++#define bfd_mach_cris_v0_v10 255 ++#define bfd_mach_cris_v32 32 ++#define bfd_mach_cris_v10_v32 1032 ++ bfd_arch_s390, /* IBM s390 */ ++#define bfd_mach_s390_31 31 ++#define bfd_mach_s390_64 64 ++ bfd_arch_openrisc, /* OpenRISC */ ++ bfd_arch_mmix, /* Donald Knuth's educational processor. */ ++ bfd_arch_xstormy16, ++#define bfd_mach_xstormy16 1 ++ bfd_arch_msp430, /* Texas Instruments MSP430 architecture. */ ++#define bfd_mach_msp11 11 ++#define bfd_mach_msp110 110 ++#define bfd_mach_msp12 12 ++#define bfd_mach_msp13 13 ++#define bfd_mach_msp14 14 ++#define bfd_mach_msp15 15 ++#define bfd_mach_msp16 16 ++#define bfd_mach_msp21 21 ++#define bfd_mach_msp31 31 ++#define bfd_mach_msp32 32 ++#define bfd_mach_msp33 33 ++#define bfd_mach_msp41 41 ++#define bfd_mach_msp42 42 ++#define bfd_mach_msp43 43 ++#define bfd_mach_msp44 44 ++ bfd_arch_xtensa, /* Tensilica's Xtensa cores. */ ++#define bfd_mach_xtensa 1 ++ bfd_arch_maxq, /* Dallas MAXQ 10/20 */ ++#define bfd_mach_maxq10 10 ++#define bfd_mach_maxq20 20 ++ bfd_arch_z80, ++#define bfd_mach_z80strict 1 /* No undocumented opcodes. */ ++#define bfd_mach_z80 3 /* With ixl, ixh, iyl, and iyh. */ ++#define bfd_mach_z80full 7 /* All undocumented instructions. */ ++#define bfd_mach_r800 11 /* R800: successor with multiplication. */ ++ bfd_arch_last ++ }; ++ ++typedef struct bfd_arch_info ++{ ++ int bits_per_word; ++ int bits_per_address; ++ int bits_per_byte; ++ enum bfd_architecture arch; ++ unsigned long mach; ++ const char *arch_name; ++ const char *printable_name; ++ unsigned int section_align_power; ++ /* TRUE if this is the default machine for the architecture. ++ The default arch should be the first entry for an arch so that ++ all the entries for that arch can be accessed via <>. */ ++ bfd_boolean the_default; ++ const struct bfd_arch_info * (*compatible) ++ (const struct bfd_arch_info *a, const struct bfd_arch_info *b); ++ ++ bfd_boolean (*scan) (const struct bfd_arch_info *, const char *); ++ ++ const struct bfd_arch_info *next; ++} ++bfd_arch_info_type; ++ ++const char *bfd_printable_name (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_scan_arch (const char *string); ++ ++const char **bfd_arch_list (void); ++ ++const bfd_arch_info_type *bfd_arch_get_compatible ++ (const bfd *abfd, const bfd *bbfd, bfd_boolean accept_unknowns); ++ ++void bfd_set_arch_info (bfd *abfd, const bfd_arch_info_type *arg); ++ ++enum bfd_architecture bfd_get_arch (bfd *abfd); ++ ++unsigned long bfd_get_mach (bfd *abfd); ++ ++unsigned int bfd_arch_bits_per_byte (bfd *abfd); ++ ++unsigned int bfd_arch_bits_per_address (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_get_arch_info (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_lookup_arch ++ (enum bfd_architecture arch, unsigned long machine); ++ ++const char *bfd_printable_arch_mach ++ (enum bfd_architecture arch, unsigned long machine); ++ ++unsigned int bfd_octets_per_byte (bfd *abfd); ++ ++unsigned int bfd_arch_mach_octets_per_byte ++ (enum bfd_architecture arch, unsigned long machine); ++ ++/* Extracted from reloc.c. */ ++typedef enum bfd_reloc_status ++{ ++ /* No errors detected. */ ++ bfd_reloc_ok, ++ ++ /* The relocation was performed, but there was an overflow. */ ++ bfd_reloc_overflow, ++ ++ /* The address to relocate was not within the section supplied. */ ++ bfd_reloc_outofrange, ++ ++ /* Used by special functions. */ ++ bfd_reloc_continue, ++ ++ /* Unsupported relocation size requested. */ ++ bfd_reloc_notsupported, ++ ++ /* Unused. */ ++ bfd_reloc_other, ++ ++ /* The symbol to relocate against was undefined. */ ++ bfd_reloc_undefined, ++ ++ /* The relocation was performed, but may not be ok - presently ++ generated only when linking i960 coff files with i960 b.out ++ symbols. If this type is returned, the error_message argument ++ to bfd_perform_relocation will be set. */ ++ bfd_reloc_dangerous ++ } ++ bfd_reloc_status_type; ++ ++ ++typedef struct reloc_cache_entry ++{ ++ /* A pointer into the canonical table of pointers. */ ++ struct bfd_symbol **sym_ptr_ptr; ++ ++ /* offset in section. */ ++ bfd_size_type address; ++ ++ /* addend for relocation value. */ ++ bfd_vma addend; ++ ++ /* Pointer to how to perform the required relocation. */ ++ reloc_howto_type *howto; ++ ++} ++arelent; ++ ++enum complain_overflow ++{ ++ /* Do not complain on overflow. */ ++ complain_overflow_dont, ++ ++ /* Complain if the value overflows when considered as a signed ++ number one bit larger than the field. ie. A bitfield of N bits ++ is allowed to represent -2**n to 2**n-1. */ ++ complain_overflow_bitfield, ++ ++ /* Complain if the value overflows when considered as a signed ++ number. */ ++ complain_overflow_signed, ++ ++ /* Complain if the value overflows when considered as an ++ unsigned number. */ ++ complain_overflow_unsigned ++}; ++ ++struct reloc_howto_struct ++{ ++ /* The type field has mainly a documentary use - the back end can ++ do what it wants with it, though normally the back end's ++ external idea of what a reloc number is stored ++ in this field. For example, a PC relative word relocation ++ in a coff environment has the type 023 - because that's ++ what the outside world calls a R_PCRWORD reloc. */ ++ unsigned int type; ++ ++ /* The value the final relocation is shifted right by. This drops ++ unwanted data from the relocation. */ ++ unsigned int rightshift; ++ ++ /* The size of the item to be relocated. This is *not* a ++ power-of-two measure. To get the number of bytes operated ++ on by a type of relocation, use bfd_get_reloc_size. */ ++ int size; ++ ++ /* The number of bits in the item to be relocated. This is used ++ when doing overflow checking. */ ++ unsigned int bitsize; ++ ++ /* Notes that the relocation is relative to the location in the ++ data section of the addend. The relocation function will ++ subtract from the relocation value the address of the location ++ being relocated. */ ++ bfd_boolean pc_relative; ++ ++ /* The bit position of the reloc value in the destination. ++ The relocated value is left shifted by this amount. */ ++ unsigned int bitpos; ++ ++ /* What type of overflow error should be checked for when ++ relocating. */ ++ enum complain_overflow complain_on_overflow; ++ ++ /* If this field is non null, then the supplied function is ++ called rather than the normal function. This allows really ++ strange relocation methods to be accommodated (e.g., i960 callj ++ instructions). */ ++ bfd_reloc_status_type (*special_function) ++ (bfd *, arelent *, struct bfd_symbol *, void *, asection *, ++ bfd *, char **); ++ ++ /* The textual name of the relocation type. */ ++ char *name; ++ ++ /* Some formats record a relocation addend in the section contents ++ rather than with the relocation. For ELF formats this is the ++ distinction between USE_REL and USE_RELA (though the code checks ++ for USE_REL == 1/0). The value of this field is TRUE if the ++ addend is recorded with the section contents; when performing a ++ partial link (ld -r) the section contents (the data) will be ++ modified. The value of this field is FALSE if addends are ++ recorded with the relocation (in arelent.addend); when performing ++ a partial link the relocation will be modified. ++ All relocations for all ELF USE_RELA targets should set this field ++ to FALSE (values of TRUE should be looked on with suspicion). ++ However, the converse is not true: not all relocations of all ELF ++ USE_REL targets set this field to TRUE. Why this is so is peculiar ++ to each particular target. For relocs that aren't used in partial ++ links (e.g. GOT stuff) it doesn't matter what this is set to. */ ++ bfd_boolean partial_inplace; ++ ++ /* src_mask selects the part of the instruction (or data) to be used ++ in the relocation sum. If the target relocations don't have an ++ addend in the reloc, eg. ELF USE_REL, src_mask will normally equal ++ dst_mask to extract the addend from the section contents. If ++ relocations do have an addend in the reloc, eg. ELF USE_RELA, this ++ field should be zero. Non-zero values for ELF USE_RELA targets are ++ bogus as in those cases the value in the dst_mask part of the ++ section contents should be treated as garbage. */ ++ bfd_vma src_mask; ++ ++ /* dst_mask selects which parts of the instruction (or data) are ++ replaced with a relocated value. */ ++ bfd_vma dst_mask; ++ ++ /* When some formats create PC relative instructions, they leave ++ the value of the pc of the place being relocated in the offset ++ slot of the instruction, so that a PC relative relocation can ++ be made just by adding in an ordinary offset (e.g., sun3 a.out). ++ Some formats leave the displacement part of an instruction ++ empty (e.g., m88k bcs); this flag signals the fact. */ ++ bfd_boolean pcrel_offset; ++}; ++ ++#define HOWTO(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \ ++ { (unsigned) C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC } ++#define NEWHOWTO(FUNCTION, NAME, SIZE, REL, IN) \ ++ HOWTO (0, 0, SIZE, 0, REL, 0, complain_overflow_dont, FUNCTION, \ ++ NAME, FALSE, 0, 0, IN) ++ ++#define EMPTY_HOWTO(C) \ ++ HOWTO ((C), 0, 0, 0, FALSE, 0, complain_overflow_dont, NULL, \ ++ NULL, FALSE, 0, 0, FALSE) ++ ++#define HOWTO_PREPARE(relocation, symbol) \ ++ { \ ++ if (symbol != NULL) \ ++ { \ ++ if (bfd_is_com_section (symbol->section)) \ ++ { \ ++ relocation = 0; \ ++ } \ ++ else \ ++ { \ ++ relocation = symbol->value; \ ++ } \ ++ } \ ++ } ++ ++unsigned int bfd_get_reloc_size (reloc_howto_type *); ++ ++typedef struct relent_chain ++{ ++ arelent relent; ++ struct relent_chain *next; ++} ++arelent_chain; ++ ++bfd_reloc_status_type bfd_check_overflow ++ (enum complain_overflow how, ++ unsigned int bitsize, ++ unsigned int rightshift, ++ unsigned int addrsize, ++ bfd_vma relocation); ++ ++bfd_reloc_status_type bfd_perform_relocation ++ (bfd *abfd, ++ arelent *reloc_entry, ++ void *data, ++ asection *input_section, ++ bfd *output_bfd, ++ char **error_message); ++ ++bfd_reloc_status_type bfd_install_relocation ++ (bfd *abfd, ++ arelent *reloc_entry, ++ void *data, bfd_vma data_start, ++ asection *input_section, ++ char **error_message); ++ ++enum bfd_reloc_code_real { ++ _dummy_first_bfd_reloc_code_real, ++ ++ ++/* Basic absolute relocations of N bits. */ ++ BFD_RELOC_64, ++ BFD_RELOC_32, ++ BFD_RELOC_26, ++ BFD_RELOC_24, ++ BFD_RELOC_16, ++ BFD_RELOC_14, ++ BFD_RELOC_8, ++ ++/* PC-relative relocations. Sometimes these are relative to the address ++of the relocation itself; sometimes they are relative to the start of ++the section containing the relocation. It depends on the specific target. ++ ++The 24-bit relocation is used in some Intel 960 configurations. */ ++ BFD_RELOC_64_PCREL, ++ BFD_RELOC_32_PCREL, ++ BFD_RELOC_24_PCREL, ++ BFD_RELOC_16_PCREL, ++ BFD_RELOC_12_PCREL, ++ BFD_RELOC_8_PCREL, ++ ++/* Section relative relocations. Some targets need this for DWARF2. */ ++ BFD_RELOC_32_SECREL, ++ ++/* For ELF. */ ++ BFD_RELOC_32_GOT_PCREL, ++ BFD_RELOC_16_GOT_PCREL, ++ BFD_RELOC_8_GOT_PCREL, ++ BFD_RELOC_32_GOTOFF, ++ BFD_RELOC_16_GOTOFF, ++ BFD_RELOC_LO16_GOTOFF, ++ BFD_RELOC_HI16_GOTOFF, ++ BFD_RELOC_HI16_S_GOTOFF, ++ BFD_RELOC_8_GOTOFF, ++ BFD_RELOC_64_PLT_PCREL, ++ BFD_RELOC_32_PLT_PCREL, ++ BFD_RELOC_24_PLT_PCREL, ++ BFD_RELOC_16_PLT_PCREL, ++ BFD_RELOC_8_PLT_PCREL, ++ BFD_RELOC_64_PLTOFF, ++ BFD_RELOC_32_PLTOFF, ++ BFD_RELOC_16_PLTOFF, ++ BFD_RELOC_LO16_PLTOFF, ++ BFD_RELOC_HI16_PLTOFF, ++ BFD_RELOC_HI16_S_PLTOFF, ++ BFD_RELOC_8_PLTOFF, ++ ++/* Relocations used by 68K ELF. */ ++ BFD_RELOC_68K_GLOB_DAT, ++ BFD_RELOC_68K_JMP_SLOT, ++ BFD_RELOC_68K_RELATIVE, ++ ++/* Linkage-table relative. */ ++ BFD_RELOC_32_BASEREL, ++ BFD_RELOC_16_BASEREL, ++ BFD_RELOC_LO16_BASEREL, ++ BFD_RELOC_HI16_BASEREL, ++ BFD_RELOC_HI16_S_BASEREL, ++ BFD_RELOC_8_BASEREL, ++ BFD_RELOC_RVA, ++ ++/* Absolute 8-bit relocation, but used to form an address like 0xFFnn. */ ++ BFD_RELOC_8_FFnn, ++ ++/* These PC-relative relocations are stored as word displacements -- ++i.e., byte displacements shifted right two bits. The 30-bit word ++displacement (<<32_PCREL_S2>> -- 32 bits, shifted 2) is used on the ++SPARC. (SPARC tools generally refer to this as <>.) The ++signed 16-bit displacement is used on the MIPS, and the 23-bit ++displacement is used on the Alpha. */ ++ BFD_RELOC_32_PCREL_S2, ++ BFD_RELOC_16_PCREL_S2, ++ BFD_RELOC_23_PCREL_S2, ++ ++/* High 22 bits and low 10 bits of 32-bit value, placed into lower bits of ++the target word. These are used on the SPARC. */ ++ BFD_RELOC_HI22, ++ BFD_RELOC_LO10, ++ ++/* For systems that allocate a Global Pointer register, these are ++displacements off that register. These relocation types are ++handled specially, because the value the register will have is ++decided relatively late. */ ++ BFD_RELOC_GPREL16, ++ BFD_RELOC_GPREL32, ++ ++/* Reloc types used for i960/b.out. */ ++ BFD_RELOC_I960_CALLJ, ++ ++/* SPARC ELF relocations. There is probably some overlap with other ++relocation types already defined. */ ++ BFD_RELOC_NONE, ++ BFD_RELOC_SPARC_WDISP22, ++ BFD_RELOC_SPARC22, ++ BFD_RELOC_SPARC13, ++ BFD_RELOC_SPARC_GOT10, ++ BFD_RELOC_SPARC_GOT13, ++ BFD_RELOC_SPARC_GOT22, ++ BFD_RELOC_SPARC_PC10, ++ BFD_RELOC_SPARC_PC22, ++ BFD_RELOC_SPARC_WPLT30, ++ BFD_RELOC_SPARC_COPY, ++ BFD_RELOC_SPARC_GLOB_DAT, ++ BFD_RELOC_SPARC_JMP_SLOT, ++ BFD_RELOC_SPARC_RELATIVE, ++ BFD_RELOC_SPARC_UA16, ++ BFD_RELOC_SPARC_UA32, ++ BFD_RELOC_SPARC_UA64, ++ ++/* I think these are specific to SPARC a.out (e.g., Sun 4). */ ++ BFD_RELOC_SPARC_BASE13, ++ BFD_RELOC_SPARC_BASE22, ++ ++/* SPARC64 relocations */ ++#define BFD_RELOC_SPARC_64 BFD_RELOC_64 ++ BFD_RELOC_SPARC_10, ++ BFD_RELOC_SPARC_11, ++ BFD_RELOC_SPARC_OLO10, ++ BFD_RELOC_SPARC_HH22, ++ BFD_RELOC_SPARC_HM10, ++ BFD_RELOC_SPARC_LM22, ++ BFD_RELOC_SPARC_PC_HH22, ++ BFD_RELOC_SPARC_PC_HM10, ++ BFD_RELOC_SPARC_PC_LM22, ++ BFD_RELOC_SPARC_WDISP16, ++ BFD_RELOC_SPARC_WDISP19, ++ BFD_RELOC_SPARC_7, ++ BFD_RELOC_SPARC_6, ++ BFD_RELOC_SPARC_5, ++#define BFD_RELOC_SPARC_DISP64 BFD_RELOC_64_PCREL ++ BFD_RELOC_SPARC_PLT32, ++ BFD_RELOC_SPARC_PLT64, ++ BFD_RELOC_SPARC_HIX22, ++ BFD_RELOC_SPARC_LOX10, ++ BFD_RELOC_SPARC_H44, ++ BFD_RELOC_SPARC_M44, ++ BFD_RELOC_SPARC_L44, ++ BFD_RELOC_SPARC_REGISTER, ++ ++/* SPARC little endian relocation */ ++ BFD_RELOC_SPARC_REV32, ++ ++/* SPARC TLS relocations */ ++ BFD_RELOC_SPARC_TLS_GD_HI22, ++ BFD_RELOC_SPARC_TLS_GD_LO10, ++ BFD_RELOC_SPARC_TLS_GD_ADD, ++ BFD_RELOC_SPARC_TLS_GD_CALL, ++ BFD_RELOC_SPARC_TLS_LDM_HI22, ++ BFD_RELOC_SPARC_TLS_LDM_LO10, ++ BFD_RELOC_SPARC_TLS_LDM_ADD, ++ BFD_RELOC_SPARC_TLS_LDM_CALL, ++ BFD_RELOC_SPARC_TLS_LDO_HIX22, ++ BFD_RELOC_SPARC_TLS_LDO_LOX10, ++ BFD_RELOC_SPARC_TLS_LDO_ADD, ++ BFD_RELOC_SPARC_TLS_IE_HI22, ++ BFD_RELOC_SPARC_TLS_IE_LO10, ++ BFD_RELOC_SPARC_TLS_IE_LD, ++ BFD_RELOC_SPARC_TLS_IE_LDX, ++ BFD_RELOC_SPARC_TLS_IE_ADD, ++ BFD_RELOC_SPARC_TLS_LE_HIX22, ++ BFD_RELOC_SPARC_TLS_LE_LOX10, ++ BFD_RELOC_SPARC_TLS_DTPMOD32, ++ BFD_RELOC_SPARC_TLS_DTPMOD64, ++ BFD_RELOC_SPARC_TLS_DTPOFF32, ++ BFD_RELOC_SPARC_TLS_DTPOFF64, ++ BFD_RELOC_SPARC_TLS_TPOFF32, ++ BFD_RELOC_SPARC_TLS_TPOFF64, ++ ++/* Alpha ECOFF and ELF relocations. Some of these treat the symbol or ++"addend" in some special way. ++For GPDISP_HI16 ("gpdisp") relocations, the symbol is ignored when ++writing; when reading, it will be the absolute section symbol. The ++addend is the displacement in bytes of the "lda" instruction from ++the "ldah" instruction (which is at the address of this reloc). */ ++ BFD_RELOC_ALPHA_GPDISP_HI16, ++ ++/* For GPDISP_LO16 ("ignore") relocations, the symbol is handled as ++with GPDISP_HI16 relocs. The addend is ignored when writing the ++relocations out, and is filled in with the file's GP value on ++reading, for convenience. */ ++ BFD_RELOC_ALPHA_GPDISP_LO16, ++ ++/* The ELF GPDISP relocation is exactly the same as the GPDISP_HI16 ++relocation except that there is no accompanying GPDISP_LO16 ++relocation. */ ++ BFD_RELOC_ALPHA_GPDISP, ++ ++/* The Alpha LITERAL/LITUSE relocs are produced by a symbol reference; ++the assembler turns it into a LDQ instruction to load the address of ++the symbol, and then fills in a register in the real instruction. ++ ++The LITERAL reloc, at the LDQ instruction, refers to the .lita ++section symbol. The addend is ignored when writing, but is filled ++in with the file's GP value on reading, for convenience, as with the ++GPDISP_LO16 reloc. ++ ++The ELF_LITERAL reloc is somewhere between 16_GOTOFF and GPDISP_LO16. ++It should refer to the symbol to be referenced, as with 16_GOTOFF, ++but it generates output not based on the position within the .got ++section, but relative to the GP value chosen for the file during the ++final link stage. ++ ++The LITUSE reloc, on the instruction using the loaded address, gives ++information to the linker that it might be able to use to optimize ++away some literal section references. The symbol is ignored (read ++as the absolute section symbol), and the "addend" indicates the type ++of instruction using the register: ++1 - "memory" fmt insn ++2 - byte-manipulation (byte offset reg) ++3 - jsr (target of branch) */ ++ BFD_RELOC_ALPHA_LITERAL, ++ BFD_RELOC_ALPHA_ELF_LITERAL, ++ BFD_RELOC_ALPHA_LITUSE, ++ ++/* The HINT relocation indicates a value that should be filled into the ++"hint" field of a jmp/jsr/ret instruction, for possible branch- ++prediction logic which may be provided on some processors. */ ++ BFD_RELOC_ALPHA_HINT, ++ ++/* The LINKAGE relocation outputs a linkage pair in the object file, ++which is filled by the linker. */ ++ BFD_RELOC_ALPHA_LINKAGE, ++ ++/* The CODEADDR relocation outputs a STO_CA in the object file, ++which is filled by the linker. */ ++ BFD_RELOC_ALPHA_CODEADDR, ++ ++/* The GPREL_HI/LO relocations together form a 32-bit offset from the ++GP register. */ ++ BFD_RELOC_ALPHA_GPREL_HI16, ++ BFD_RELOC_ALPHA_GPREL_LO16, ++ ++/* Like BFD_RELOC_23_PCREL_S2, except that the source and target must ++share a common GP, and the target address is adjusted for ++STO_ALPHA_STD_GPLOAD. */ ++ BFD_RELOC_ALPHA_BRSGP, ++ ++/* Alpha thread-local storage relocations. */ ++ BFD_RELOC_ALPHA_TLSGD, ++ BFD_RELOC_ALPHA_TLSLDM, ++ BFD_RELOC_ALPHA_DTPMOD64, ++ BFD_RELOC_ALPHA_GOTDTPREL16, ++ BFD_RELOC_ALPHA_DTPREL64, ++ BFD_RELOC_ALPHA_DTPREL_HI16, ++ BFD_RELOC_ALPHA_DTPREL_LO16, ++ BFD_RELOC_ALPHA_DTPREL16, ++ BFD_RELOC_ALPHA_GOTTPREL16, ++ BFD_RELOC_ALPHA_TPREL64, ++ BFD_RELOC_ALPHA_TPREL_HI16, ++ BFD_RELOC_ALPHA_TPREL_LO16, ++ BFD_RELOC_ALPHA_TPREL16, ++ ++/* Bits 27..2 of the relocation address shifted right 2 bits; ++simple reloc otherwise. */ ++ BFD_RELOC_MIPS_JMP, ++ ++/* The MIPS16 jump instruction. */ ++ BFD_RELOC_MIPS16_JMP, ++ ++/* MIPS16 GP relative reloc. */ ++ BFD_RELOC_MIPS16_GPREL, ++ ++/* High 16 bits of 32-bit value; simple reloc. */ ++ BFD_RELOC_HI16, ++ ++/* High 16 bits of 32-bit value but the low 16 bits will be sign ++extended and added to form the final result. If the low 16 ++bits form a negative number, we need to add one to the high value ++to compensate for the borrow when the low bits are added. */ ++ BFD_RELOC_HI16_S, ++ ++/* Low 16 bits. */ ++ BFD_RELOC_LO16, ++ ++/* High 16 bits of 32-bit pc-relative value */ ++ BFD_RELOC_HI16_PCREL, ++ ++/* High 16 bits of 32-bit pc-relative value, adjusted */ ++ BFD_RELOC_HI16_S_PCREL, ++ ++/* Low 16 bits of pc-relative value */ ++ BFD_RELOC_LO16_PCREL, ++ ++/* MIPS16 high 16 bits of 32-bit value. */ ++ BFD_RELOC_MIPS16_HI16, ++ ++/* MIPS16 high 16 bits of 32-bit value but the low 16 bits will be sign ++extended and added to form the final result. If the low 16 ++bits form a negative number, we need to add one to the high value ++to compensate for the borrow when the low bits are added. */ ++ BFD_RELOC_MIPS16_HI16_S, ++ ++/* MIPS16 low 16 bits. */ ++ BFD_RELOC_MIPS16_LO16, ++ ++/* Relocation against a MIPS literal section. */ ++ BFD_RELOC_MIPS_LITERAL, ++ ++/* MIPS ELF relocations. */ ++ BFD_RELOC_MIPS_GOT16, ++ BFD_RELOC_MIPS_CALL16, ++ BFD_RELOC_MIPS_GOT_HI16, ++ BFD_RELOC_MIPS_GOT_LO16, ++ BFD_RELOC_MIPS_CALL_HI16, ++ BFD_RELOC_MIPS_CALL_LO16, ++ BFD_RELOC_MIPS_SUB, ++ BFD_RELOC_MIPS_GOT_PAGE, ++ BFD_RELOC_MIPS_GOT_OFST, ++ BFD_RELOC_MIPS_GOT_DISP, ++ BFD_RELOC_MIPS_SHIFT5, ++ BFD_RELOC_MIPS_SHIFT6, ++ BFD_RELOC_MIPS_INSERT_A, ++ BFD_RELOC_MIPS_INSERT_B, ++ BFD_RELOC_MIPS_DELETE, ++ BFD_RELOC_MIPS_HIGHEST, ++ BFD_RELOC_MIPS_HIGHER, ++ BFD_RELOC_MIPS_SCN_DISP, ++ BFD_RELOC_MIPS_REL16, ++ BFD_RELOC_MIPS_RELGOT, ++ BFD_RELOC_MIPS_JALR, ++ BFD_RELOC_MIPS_TLS_DTPMOD32, ++ BFD_RELOC_MIPS_TLS_DTPREL32, ++ BFD_RELOC_MIPS_TLS_DTPMOD64, ++ BFD_RELOC_MIPS_TLS_DTPREL64, ++ BFD_RELOC_MIPS_TLS_GD, ++ BFD_RELOC_MIPS_TLS_LDM, ++ BFD_RELOC_MIPS_TLS_DTPREL_HI16, ++ BFD_RELOC_MIPS_TLS_DTPREL_LO16, ++ BFD_RELOC_MIPS_TLS_GOTTPREL, ++ BFD_RELOC_MIPS_TLS_TPREL32, ++ BFD_RELOC_MIPS_TLS_TPREL64, ++ BFD_RELOC_MIPS_TLS_TPREL_HI16, ++ BFD_RELOC_MIPS_TLS_TPREL_LO16, ++ ++ ++/* Fujitsu Frv Relocations. */ ++ BFD_RELOC_FRV_LABEL16, ++ BFD_RELOC_FRV_LABEL24, ++ BFD_RELOC_FRV_LO16, ++ BFD_RELOC_FRV_HI16, ++ BFD_RELOC_FRV_GPREL12, ++ BFD_RELOC_FRV_GPRELU12, ++ BFD_RELOC_FRV_GPREL32, ++ BFD_RELOC_FRV_GPRELHI, ++ BFD_RELOC_FRV_GPRELLO, ++ BFD_RELOC_FRV_GOT12, ++ BFD_RELOC_FRV_GOTHI, ++ BFD_RELOC_FRV_GOTLO, ++ BFD_RELOC_FRV_FUNCDESC, ++ BFD_RELOC_FRV_FUNCDESC_GOT12, ++ BFD_RELOC_FRV_FUNCDESC_GOTHI, ++ BFD_RELOC_FRV_FUNCDESC_GOTLO, ++ BFD_RELOC_FRV_FUNCDESC_VALUE, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFF12, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFFHI, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFFLO, ++ BFD_RELOC_FRV_GOTOFF12, ++ BFD_RELOC_FRV_GOTOFFHI, ++ BFD_RELOC_FRV_GOTOFFLO, ++ BFD_RELOC_FRV_GETTLSOFF, ++ BFD_RELOC_FRV_TLSDESC_VALUE, ++ BFD_RELOC_FRV_GOTTLSDESC12, ++ BFD_RELOC_FRV_GOTTLSDESCHI, ++ BFD_RELOC_FRV_GOTTLSDESCLO, ++ BFD_RELOC_FRV_TLSMOFF12, ++ BFD_RELOC_FRV_TLSMOFFHI, ++ BFD_RELOC_FRV_TLSMOFFLO, ++ BFD_RELOC_FRV_GOTTLSOFF12, ++ BFD_RELOC_FRV_GOTTLSOFFHI, ++ BFD_RELOC_FRV_GOTTLSOFFLO, ++ BFD_RELOC_FRV_TLSOFF, ++ BFD_RELOC_FRV_TLSDESC_RELAX, ++ BFD_RELOC_FRV_GETTLSOFF_RELAX, ++ BFD_RELOC_FRV_TLSOFF_RELAX, ++ BFD_RELOC_FRV_TLSMOFF, ++ ++ ++/* This is a 24bit GOT-relative reloc for the mn10300. */ ++ BFD_RELOC_MN10300_GOTOFF24, ++ ++/* This is a 32bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT32, ++ ++/* This is a 24bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT24, ++ ++/* This is a 16bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT16, ++ ++/* Copy symbol at runtime. */ ++ BFD_RELOC_MN10300_COPY, ++ ++/* Create GOT entry. */ ++ BFD_RELOC_MN10300_GLOB_DAT, ++ ++/* Create PLT entry. */ ++ BFD_RELOC_MN10300_JMP_SLOT, ++ ++/* Adjust by program base. */ ++ BFD_RELOC_MN10300_RELATIVE, ++ ++ ++/* i386/elf relocations */ ++ BFD_RELOC_386_GOT32, ++ BFD_RELOC_386_PLT32, ++ BFD_RELOC_386_COPY, ++ BFD_RELOC_386_GLOB_DAT, ++ BFD_RELOC_386_JUMP_SLOT, ++ BFD_RELOC_386_RELATIVE, ++ BFD_RELOC_386_GOTOFF, ++ BFD_RELOC_386_GOTPC, ++ BFD_RELOC_386_TLS_TPOFF, ++ BFD_RELOC_386_TLS_IE, ++ BFD_RELOC_386_TLS_GOTIE, ++ BFD_RELOC_386_TLS_LE, ++ BFD_RELOC_386_TLS_GD, ++ BFD_RELOC_386_TLS_LDM, ++ BFD_RELOC_386_TLS_LDO_32, ++ BFD_RELOC_386_TLS_IE_32, ++ BFD_RELOC_386_TLS_LE_32, ++ BFD_RELOC_386_TLS_DTPMOD32, ++ BFD_RELOC_386_TLS_DTPOFF32, ++ BFD_RELOC_386_TLS_TPOFF32, ++ ++/* x86-64/elf relocations */ ++ BFD_RELOC_X86_64_GOT32, ++ BFD_RELOC_X86_64_PLT32, ++ BFD_RELOC_X86_64_COPY, ++ BFD_RELOC_X86_64_GLOB_DAT, ++ BFD_RELOC_X86_64_JUMP_SLOT, ++ BFD_RELOC_X86_64_RELATIVE, ++ BFD_RELOC_X86_64_GOTPCREL, ++ BFD_RELOC_X86_64_32S, ++ BFD_RELOC_X86_64_DTPMOD64, ++ BFD_RELOC_X86_64_DTPOFF64, ++ BFD_RELOC_X86_64_TPOFF64, ++ BFD_RELOC_X86_64_TLSGD, ++ BFD_RELOC_X86_64_TLSLD, ++ BFD_RELOC_X86_64_DTPOFF32, ++ BFD_RELOC_X86_64_GOTTPOFF, ++ BFD_RELOC_X86_64_TPOFF32, ++ BFD_RELOC_X86_64_GOTOFF64, ++ BFD_RELOC_X86_64_GOTPC32, ++ BFD_RELOC_X86_64_GOT64, ++ BFD_RELOC_X86_64_GOTPCREL64, ++ BFD_RELOC_X86_64_GOTPC64, ++ BFD_RELOC_X86_64_GOTPLT64, ++ BFD_RELOC_X86_64_PLTOFF64, ++ ++/* ns32k relocations */ ++ BFD_RELOC_NS32K_IMM_8, ++ BFD_RELOC_NS32K_IMM_16, ++ BFD_RELOC_NS32K_IMM_32, ++ BFD_RELOC_NS32K_IMM_8_PCREL, ++ BFD_RELOC_NS32K_IMM_16_PCREL, ++ BFD_RELOC_NS32K_IMM_32_PCREL, ++ BFD_RELOC_NS32K_DISP_8, ++ BFD_RELOC_NS32K_DISP_16, ++ BFD_RELOC_NS32K_DISP_32, ++ BFD_RELOC_NS32K_DISP_8_PCREL, ++ BFD_RELOC_NS32K_DISP_16_PCREL, ++ BFD_RELOC_NS32K_DISP_32_PCREL, ++ ++/* PDP11 relocations */ ++ BFD_RELOC_PDP11_DISP_8_PCREL, ++ BFD_RELOC_PDP11_DISP_6_PCREL, ++ ++/* Picojava relocs. Not all of these appear in object files. */ ++ BFD_RELOC_PJ_CODE_HI16, ++ BFD_RELOC_PJ_CODE_LO16, ++ BFD_RELOC_PJ_CODE_DIR16, ++ BFD_RELOC_PJ_CODE_DIR32, ++ BFD_RELOC_PJ_CODE_REL16, ++ BFD_RELOC_PJ_CODE_REL32, ++ ++/* Power(rs6000) and PowerPC relocations. */ ++ BFD_RELOC_PPC_B26, ++ BFD_RELOC_PPC_BA26, ++ BFD_RELOC_PPC_TOC16, ++ BFD_RELOC_PPC_B16, ++ BFD_RELOC_PPC_B16_BRTAKEN, ++ BFD_RELOC_PPC_B16_BRNTAKEN, ++ BFD_RELOC_PPC_BA16, ++ BFD_RELOC_PPC_BA16_BRTAKEN, ++ BFD_RELOC_PPC_BA16_BRNTAKEN, ++ BFD_RELOC_PPC_COPY, ++ BFD_RELOC_PPC_GLOB_DAT, ++ BFD_RELOC_PPC_JMP_SLOT, ++ BFD_RELOC_PPC_RELATIVE, ++ BFD_RELOC_PPC_LOCAL24PC, ++ BFD_RELOC_PPC_EMB_NADDR32, ++ BFD_RELOC_PPC_EMB_NADDR16, ++ BFD_RELOC_PPC_EMB_NADDR16_LO, ++ BFD_RELOC_PPC_EMB_NADDR16_HI, ++ BFD_RELOC_PPC_EMB_NADDR16_HA, ++ BFD_RELOC_PPC_EMB_SDAI16, ++ BFD_RELOC_PPC_EMB_SDA2I16, ++ BFD_RELOC_PPC_EMB_SDA2REL, ++ BFD_RELOC_PPC_EMB_SDA21, ++ BFD_RELOC_PPC_EMB_MRKREF, ++ BFD_RELOC_PPC_EMB_RELSEC16, ++ BFD_RELOC_PPC_EMB_RELST_LO, ++ BFD_RELOC_PPC_EMB_RELST_HI, ++ BFD_RELOC_PPC_EMB_RELST_HA, ++ BFD_RELOC_PPC_EMB_BIT_FLD, ++ BFD_RELOC_PPC_EMB_RELSDA, ++ BFD_RELOC_PPC64_HIGHER, ++ BFD_RELOC_PPC64_HIGHER_S, ++ BFD_RELOC_PPC64_HIGHEST, ++ BFD_RELOC_PPC64_HIGHEST_S, ++ BFD_RELOC_PPC64_TOC16_LO, ++ BFD_RELOC_PPC64_TOC16_HI, ++ BFD_RELOC_PPC64_TOC16_HA, ++ BFD_RELOC_PPC64_TOC, ++ BFD_RELOC_PPC64_PLTGOT16, ++ BFD_RELOC_PPC64_PLTGOT16_LO, ++ BFD_RELOC_PPC64_PLTGOT16_HI, ++ BFD_RELOC_PPC64_PLTGOT16_HA, ++ BFD_RELOC_PPC64_ADDR16_DS, ++ BFD_RELOC_PPC64_ADDR16_LO_DS, ++ BFD_RELOC_PPC64_GOT16_DS, ++ BFD_RELOC_PPC64_GOT16_LO_DS, ++ BFD_RELOC_PPC64_PLT16_LO_DS, ++ BFD_RELOC_PPC64_SECTOFF_DS, ++ BFD_RELOC_PPC64_SECTOFF_LO_DS, ++ BFD_RELOC_PPC64_TOC16_DS, ++ BFD_RELOC_PPC64_TOC16_LO_DS, ++ BFD_RELOC_PPC64_PLTGOT16_DS, ++ BFD_RELOC_PPC64_PLTGOT16_LO_DS, ++ ++/* PowerPC and PowerPC64 thread-local storage relocations. */ ++ BFD_RELOC_PPC_TLS, ++ BFD_RELOC_PPC_DTPMOD, ++ BFD_RELOC_PPC_TPREL16, ++ BFD_RELOC_PPC_TPREL16_LO, ++ BFD_RELOC_PPC_TPREL16_HI, ++ BFD_RELOC_PPC_TPREL16_HA, ++ BFD_RELOC_PPC_TPREL, ++ BFD_RELOC_PPC_DTPREL16, ++ BFD_RELOC_PPC_DTPREL16_LO, ++ BFD_RELOC_PPC_DTPREL16_HI, ++ BFD_RELOC_PPC_DTPREL16_HA, ++ BFD_RELOC_PPC_DTPREL, ++ BFD_RELOC_PPC_GOT_TLSGD16, ++ BFD_RELOC_PPC_GOT_TLSGD16_LO, ++ BFD_RELOC_PPC_GOT_TLSGD16_HI, ++ BFD_RELOC_PPC_GOT_TLSGD16_HA, ++ BFD_RELOC_PPC_GOT_TLSLD16, ++ BFD_RELOC_PPC_GOT_TLSLD16_LO, ++ BFD_RELOC_PPC_GOT_TLSLD16_HI, ++ BFD_RELOC_PPC_GOT_TLSLD16_HA, ++ BFD_RELOC_PPC_GOT_TPREL16, ++ BFD_RELOC_PPC_GOT_TPREL16_LO, ++ BFD_RELOC_PPC_GOT_TPREL16_HI, ++ BFD_RELOC_PPC_GOT_TPREL16_HA, ++ BFD_RELOC_PPC_GOT_DTPREL16, ++ BFD_RELOC_PPC_GOT_DTPREL16_LO, ++ BFD_RELOC_PPC_GOT_DTPREL16_HI, ++ BFD_RELOC_PPC_GOT_DTPREL16_HA, ++ BFD_RELOC_PPC64_TPREL16_DS, ++ BFD_RELOC_PPC64_TPREL16_LO_DS, ++ BFD_RELOC_PPC64_TPREL16_HIGHER, ++ BFD_RELOC_PPC64_TPREL16_HIGHERA, ++ BFD_RELOC_PPC64_TPREL16_HIGHEST, ++ BFD_RELOC_PPC64_TPREL16_HIGHESTA, ++ BFD_RELOC_PPC64_DTPREL16_DS, ++ BFD_RELOC_PPC64_DTPREL16_LO_DS, ++ BFD_RELOC_PPC64_DTPREL16_HIGHER, ++ BFD_RELOC_PPC64_DTPREL16_HIGHERA, ++ BFD_RELOC_PPC64_DTPREL16_HIGHEST, ++ BFD_RELOC_PPC64_DTPREL16_HIGHESTA, ++ ++/* IBM 370/390 relocations */ ++ BFD_RELOC_I370_D12, ++ ++/* The type of reloc used to build a constructor table - at the moment ++probably a 32 bit wide absolute relocation, but the target can choose. ++It generally does map to one of the other relocation types. */ ++ BFD_RELOC_CTOR, ++ ++/* ARM 26 bit pc-relative branch. The lowest two bits must be zero and are ++not stored in the instruction. */ ++ BFD_RELOC_ARM_PCREL_BRANCH, ++ ++/* ARM 26 bit pc-relative branch. The lowest bit must be zero and is ++not stored in the instruction. The 2nd lowest bit comes from a 1 bit ++field in the instruction. */ ++ BFD_RELOC_ARM_PCREL_BLX, ++ ++/* Thumb 22 bit pc-relative branch. The lowest bit must be zero and is ++not stored in the instruction. The 2nd lowest bit comes from a 1 bit ++field in the instruction. */ ++ BFD_RELOC_THUMB_PCREL_BLX, ++ ++/* ARM 26-bit pc-relative branch for an unconditional BL or BLX instruction. */ ++ BFD_RELOC_ARM_PCREL_CALL, ++ ++/* ARM 26-bit pc-relative branch for B or conditional BL instruction. */ ++ BFD_RELOC_ARM_PCREL_JUMP, ++ ++/* Thumb 7-, 9-, 12-, 20-, 23-, and 25-bit pc-relative branches. ++The lowest bit must be zero and is not stored in the instruction. ++Note that the corresponding ELF R_ARM_THM_JUMPnn constant has an ++"nn" one smaller in all cases. Note further that BRANCH23 ++corresponds to R_ARM_THM_CALL. */ ++ BFD_RELOC_THUMB_PCREL_BRANCH7, ++ BFD_RELOC_THUMB_PCREL_BRANCH9, ++ BFD_RELOC_THUMB_PCREL_BRANCH12, ++ BFD_RELOC_THUMB_PCREL_BRANCH20, ++ BFD_RELOC_THUMB_PCREL_BRANCH23, ++ BFD_RELOC_THUMB_PCREL_BRANCH25, ++ ++/* 12-bit immediate offset, used in ARM-format ldr and str instructions. */ ++ BFD_RELOC_ARM_OFFSET_IMM, ++ ++/* 5-bit immediate offset, used in Thumb-format ldr and str instructions. */ ++ BFD_RELOC_ARM_THUMB_OFFSET, ++ ++/* Pc-relative or absolute relocation depending on target. Used for ++entries in .init_array sections. */ ++ BFD_RELOC_ARM_TARGET1, ++ ++/* Read-only segment base relative address. */ ++ BFD_RELOC_ARM_ROSEGREL32, ++ ++/* Data segment base relative address. */ ++ BFD_RELOC_ARM_SBREL32, ++ ++/* This reloc is used for references to RTTI data from exception handling ++tables. The actual definition depends on the target. It may be a ++pc-relative or some form of GOT-indirect relocation. */ ++ BFD_RELOC_ARM_TARGET2, ++ ++/* 31-bit PC relative address. */ ++ BFD_RELOC_ARM_PREL31, ++ ++/* Relocations for setting up GOTs and PLTs for shared libraries. */ ++ BFD_RELOC_ARM_JUMP_SLOT, ++ BFD_RELOC_ARM_GLOB_DAT, ++ BFD_RELOC_ARM_GOT32, ++ BFD_RELOC_ARM_PLT32, ++ BFD_RELOC_ARM_RELATIVE, ++ BFD_RELOC_ARM_GOTOFF, ++ BFD_RELOC_ARM_GOTPC, ++ ++/* ARM thread-local storage relocations. */ ++ BFD_RELOC_ARM_TLS_GD32, ++ BFD_RELOC_ARM_TLS_LDO32, ++ BFD_RELOC_ARM_TLS_LDM32, ++ BFD_RELOC_ARM_TLS_DTPOFF32, ++ BFD_RELOC_ARM_TLS_DTPMOD32, ++ BFD_RELOC_ARM_TLS_TPOFF32, ++ BFD_RELOC_ARM_TLS_IE32, ++ BFD_RELOC_ARM_TLS_LE32, ++ ++/* These relocs are only used within the ARM assembler. They are not ++(at present) written to any object files. */ ++ BFD_RELOC_ARM_IMMEDIATE, ++ BFD_RELOC_ARM_ADRL_IMMEDIATE, ++ BFD_RELOC_ARM_T32_IMMEDIATE, ++ BFD_RELOC_ARM_T32_IMM12, ++ BFD_RELOC_ARM_T32_ADD_PC12, ++ BFD_RELOC_ARM_SHIFT_IMM, ++ BFD_RELOC_ARM_SMC, ++ BFD_RELOC_ARM_SWI, ++ BFD_RELOC_ARM_MULTI, ++ BFD_RELOC_ARM_CP_OFF_IMM, ++ BFD_RELOC_ARM_CP_OFF_IMM_S2, ++ BFD_RELOC_ARM_T32_CP_OFF_IMM, ++ BFD_RELOC_ARM_T32_CP_OFF_IMM_S2, ++ BFD_RELOC_ARM_ADR_IMM, ++ BFD_RELOC_ARM_LDR_IMM, ++ BFD_RELOC_ARM_LITERAL, ++ BFD_RELOC_ARM_IN_POOL, ++ BFD_RELOC_ARM_OFFSET_IMM8, ++ BFD_RELOC_ARM_T32_OFFSET_U8, ++ BFD_RELOC_ARM_T32_OFFSET_IMM, ++ BFD_RELOC_ARM_HWLITERAL, ++ BFD_RELOC_ARM_THUMB_ADD, ++ BFD_RELOC_ARM_THUMB_IMM, ++ BFD_RELOC_ARM_THUMB_SHIFT, ++ ++/* Renesas / SuperH SH relocs. Not all of these appear in object files. */ ++ BFD_RELOC_SH_PCDISP8BY2, ++ BFD_RELOC_SH_PCDISP12BY2, ++ BFD_RELOC_SH_IMM3, ++ BFD_RELOC_SH_IMM3U, ++ BFD_RELOC_SH_DISP12, ++ BFD_RELOC_SH_DISP12BY2, ++ BFD_RELOC_SH_DISP12BY4, ++ BFD_RELOC_SH_DISP12BY8, ++ BFD_RELOC_SH_DISP20, ++ BFD_RELOC_SH_DISP20BY8, ++ BFD_RELOC_SH_IMM4, ++ BFD_RELOC_SH_IMM4BY2, ++ BFD_RELOC_SH_IMM4BY4, ++ BFD_RELOC_SH_IMM8, ++ BFD_RELOC_SH_IMM8BY2, ++ BFD_RELOC_SH_IMM8BY4, ++ BFD_RELOC_SH_PCRELIMM8BY2, ++ BFD_RELOC_SH_PCRELIMM8BY4, ++ BFD_RELOC_SH_SWITCH16, ++ BFD_RELOC_SH_SWITCH32, ++ BFD_RELOC_SH_USES, ++ BFD_RELOC_SH_COUNT, ++ BFD_RELOC_SH_ALIGN, ++ BFD_RELOC_SH_CODE, ++ BFD_RELOC_SH_DATA, ++ BFD_RELOC_SH_LABEL, ++ BFD_RELOC_SH_LOOP_START, ++ BFD_RELOC_SH_LOOP_END, ++ BFD_RELOC_SH_COPY, ++ BFD_RELOC_SH_GLOB_DAT, ++ BFD_RELOC_SH_JMP_SLOT, ++ BFD_RELOC_SH_RELATIVE, ++ BFD_RELOC_SH_GOTPC, ++ BFD_RELOC_SH_GOT_LOW16, ++ BFD_RELOC_SH_GOT_MEDLOW16, ++ BFD_RELOC_SH_GOT_MEDHI16, ++ BFD_RELOC_SH_GOT_HI16, ++ BFD_RELOC_SH_GOTPLT_LOW16, ++ BFD_RELOC_SH_GOTPLT_MEDLOW16, ++ BFD_RELOC_SH_GOTPLT_MEDHI16, ++ BFD_RELOC_SH_GOTPLT_HI16, ++ BFD_RELOC_SH_PLT_LOW16, ++ BFD_RELOC_SH_PLT_MEDLOW16, ++ BFD_RELOC_SH_PLT_MEDHI16, ++ BFD_RELOC_SH_PLT_HI16, ++ BFD_RELOC_SH_GOTOFF_LOW16, ++ BFD_RELOC_SH_GOTOFF_MEDLOW16, ++ BFD_RELOC_SH_GOTOFF_MEDHI16, ++ BFD_RELOC_SH_GOTOFF_HI16, ++ BFD_RELOC_SH_GOTPC_LOW16, ++ BFD_RELOC_SH_GOTPC_MEDLOW16, ++ BFD_RELOC_SH_GOTPC_MEDHI16, ++ BFD_RELOC_SH_GOTPC_HI16, ++ BFD_RELOC_SH_COPY64, ++ BFD_RELOC_SH_GLOB_DAT64, ++ BFD_RELOC_SH_JMP_SLOT64, ++ BFD_RELOC_SH_RELATIVE64, ++ BFD_RELOC_SH_GOT10BY4, ++ BFD_RELOC_SH_GOT10BY8, ++ BFD_RELOC_SH_GOTPLT10BY4, ++ BFD_RELOC_SH_GOTPLT10BY8, ++ BFD_RELOC_SH_GOTPLT32, ++ BFD_RELOC_SH_SHMEDIA_CODE, ++ BFD_RELOC_SH_IMMU5, ++ BFD_RELOC_SH_IMMS6, ++ BFD_RELOC_SH_IMMS6BY32, ++ BFD_RELOC_SH_IMMU6, ++ BFD_RELOC_SH_IMMS10, ++ BFD_RELOC_SH_IMMS10BY2, ++ BFD_RELOC_SH_IMMS10BY4, ++ BFD_RELOC_SH_IMMS10BY8, ++ BFD_RELOC_SH_IMMS16, ++ BFD_RELOC_SH_IMMU16, ++ BFD_RELOC_SH_IMM_LOW16, ++ BFD_RELOC_SH_IMM_LOW16_PCREL, ++ BFD_RELOC_SH_IMM_MEDLOW16, ++ BFD_RELOC_SH_IMM_MEDLOW16_PCREL, ++ BFD_RELOC_SH_IMM_MEDHI16, ++ BFD_RELOC_SH_IMM_MEDHI16_PCREL, ++ BFD_RELOC_SH_IMM_HI16, ++ BFD_RELOC_SH_IMM_HI16_PCREL, ++ BFD_RELOC_SH_PT_16, ++ BFD_RELOC_SH_TLS_GD_32, ++ BFD_RELOC_SH_TLS_LD_32, ++ BFD_RELOC_SH_TLS_LDO_32, ++ BFD_RELOC_SH_TLS_IE_32, ++ BFD_RELOC_SH_TLS_LE_32, ++ BFD_RELOC_SH_TLS_DTPMOD32, ++ BFD_RELOC_SH_TLS_DTPOFF32, ++ BFD_RELOC_SH_TLS_TPOFF32, ++ ++/* ARC Cores relocs. ++ARC 22 bit pc-relative branch. The lowest two bits must be zero and are ++not stored in the instruction. The high 20 bits are installed in bits 26 ++through 7 of the instruction. */ ++ BFD_RELOC_ARC_B22_PCREL, ++ ++/* ARC 26 bit absolute branch. The lowest two bits must be zero and are not ++stored in the instruction. The high 24 bits are installed in bits 23 ++through 0. */ ++ BFD_RELOC_ARC_B26, ++ ++/* ADI Blackfin 16 bit immediate absolute reloc. */ ++ BFD_RELOC_BFIN_16_IMM, ++ ++/* ADI Blackfin 16 bit immediate absolute reloc higher 16 bits. */ ++ BFD_RELOC_BFIN_16_HIGH, ++ ++/* ADI Blackfin 'a' part of LSETUP. */ ++ BFD_RELOC_BFIN_4_PCREL, ++ ++/* ADI Blackfin. */ ++ BFD_RELOC_BFIN_5_PCREL, ++ ++/* ADI Blackfin 16 bit immediate absolute reloc lower 16 bits. */ ++ BFD_RELOC_BFIN_16_LOW, ++ ++/* ADI Blackfin. */ ++ BFD_RELOC_BFIN_10_PCREL, ++ ++/* ADI Blackfin 'b' part of LSETUP. */ ++ BFD_RELOC_BFIN_11_PCREL, ++ ++/* ADI Blackfin. */ ++ BFD_RELOC_BFIN_12_PCREL_JUMP, ++ ++/* ADI Blackfin Short jump, pcrel. */ ++ BFD_RELOC_BFIN_12_PCREL_JUMP_S, ++ ++/* ADI Blackfin Call.x not implemented. */ ++ BFD_RELOC_BFIN_24_PCREL_CALL_X, ++ ++/* ADI Blackfin Long Jump pcrel. */ ++ BFD_RELOC_BFIN_24_PCREL_JUMP_L, ++ ++/* ADI Blackfin GOT relocation. */ ++ BFD_RELOC_BFIN_GOT, ++ ++/* ADI Blackfin PLTPC relocation. */ ++ BFD_RELOC_BFIN_PLTPC, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_PUSH, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_CONST, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_ADD, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_SUB, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_MULT, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_DIV, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_MOD, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_LSHIFT, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_RSHIFT, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_AND, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_OR, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_XOR, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_LAND, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_LOR, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_LEN, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_NEG, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_COMP, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_PAGE, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_HWPAGE, ++ ++/* ADI Blackfin arithmetic relocation. */ ++ BFD_ARELOC_BFIN_ADDR, ++ ++/* Mitsubishi D10V relocs. ++This is a 10-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_10_PCREL_R, ++ ++/* Mitsubishi D10V relocs. ++This is a 10-bit reloc with the right 2 bits ++assumed to be 0. This is the same as the previous reloc ++except it is in the left container, i.e., ++shifted left 15 bits. */ ++ BFD_RELOC_D10V_10_PCREL_L, ++ ++/* This is an 18-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_18, ++ ++/* This is an 18-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_18_PCREL, ++ ++/* Mitsubishi D30V relocs. ++This is a 6-bit absolute reloc. */ ++ BFD_RELOC_D30V_6, ++ ++/* This is a 6-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_9_PCREL, ++ ++/* This is a 6-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_9_PCREL_R, ++ ++/* This is a 12-bit absolute reloc with the ++right 3 bitsassumed to be 0. */ ++ BFD_RELOC_D30V_15, ++ ++/* This is a 12-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_15_PCREL, ++ ++/* This is a 12-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_15_PCREL_R, ++ ++/* This is an 18-bit absolute reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_21, ++ ++/* This is an 18-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_21_PCREL, ++ ++/* This is an 18-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_21_PCREL_R, ++ ++/* This is a 32-bit absolute reloc. */ ++ BFD_RELOC_D30V_32, ++ ++/* This is a 32-bit pc-relative reloc. */ ++ BFD_RELOC_D30V_32_PCREL, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_HI16_S, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_LO16, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_JMP26, ++ ++/* Renesas M16C/M32C Relocations. */ ++ BFD_RELOC_M32C_HI8, ++ ++/* Renesas M32R (formerly Mitsubishi M32R) relocs. ++This is a 24 bit absolute address. */ ++ BFD_RELOC_M32R_24, ++ ++/* This is a 10-bit pc-relative reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_10_PCREL, ++ ++/* This is an 18-bit reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_18_PCREL, ++ ++/* This is a 26-bit reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_26_PCREL, ++ ++/* This is a 16-bit reloc containing the high 16 bits of an address ++used when the lower 16 bits are treated as unsigned. */ ++ BFD_RELOC_M32R_HI16_ULO, ++ ++/* This is a 16-bit reloc containing the high 16 bits of an address ++used when the lower 16 bits are treated as signed. */ ++ BFD_RELOC_M32R_HI16_SLO, ++ ++/* This is a 16-bit reloc containing the lower 16 bits of an address. */ ++ BFD_RELOC_M32R_LO16, ++ ++/* This is a 16-bit reloc containing the small data area offset for use in ++add3, load, and store instructions. */ ++ BFD_RELOC_M32R_SDA16, ++ ++/* For PIC. */ ++ BFD_RELOC_M32R_GOT24, ++ BFD_RELOC_M32R_26_PLTREL, ++ BFD_RELOC_M32R_COPY, ++ BFD_RELOC_M32R_GLOB_DAT, ++ BFD_RELOC_M32R_JMP_SLOT, ++ BFD_RELOC_M32R_RELATIVE, ++ BFD_RELOC_M32R_GOTOFF, ++ BFD_RELOC_M32R_GOTOFF_HI_ULO, ++ BFD_RELOC_M32R_GOTOFF_HI_SLO, ++ BFD_RELOC_M32R_GOTOFF_LO, ++ BFD_RELOC_M32R_GOTPC24, ++ BFD_RELOC_M32R_GOT16_HI_ULO, ++ BFD_RELOC_M32R_GOT16_HI_SLO, ++ BFD_RELOC_M32R_GOT16_LO, ++ BFD_RELOC_M32R_GOTPC_HI_ULO, ++ BFD_RELOC_M32R_GOTPC_HI_SLO, ++ BFD_RELOC_M32R_GOTPC_LO, ++ ++/* This is a 9-bit reloc */ ++ BFD_RELOC_V850_9_PCREL, ++ ++/* This is a 22-bit reloc */ ++ BFD_RELOC_V850_22_PCREL, ++ ++/* This is a 16 bit offset from the short data area pointer. */ ++ BFD_RELOC_V850_SDA_16_16_OFFSET, ++ ++/* This is a 16 bit offset (of which only 15 bits are used) from the ++short data area pointer. */ ++ BFD_RELOC_V850_SDA_15_16_OFFSET, ++ ++/* This is a 16 bit offset from the zero data area pointer. */ ++ BFD_RELOC_V850_ZDA_16_16_OFFSET, ++ ++/* This is a 16 bit offset (of which only 15 bits are used) from the ++zero data area pointer. */ ++ BFD_RELOC_V850_ZDA_15_16_OFFSET, ++ ++/* This is an 8 bit offset (of which only 6 bits are used) from the ++tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_6_8_OFFSET, ++ ++/* This is an 8bit offset (of which only 7 bits are used) from the tiny ++data area pointer. */ ++ BFD_RELOC_V850_TDA_7_8_OFFSET, ++ ++/* This is a 7 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_7_7_OFFSET, ++ ++/* This is a 16 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_16_16_OFFSET, ++ ++/* This is a 5 bit offset (of which only 4 bits are used) from the tiny ++data area pointer. */ ++ BFD_RELOC_V850_TDA_4_5_OFFSET, ++ ++/* This is a 4 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_4_4_OFFSET, ++ ++/* This is a 16 bit offset from the short data area pointer, with the ++bits placed non-contiguously in the instruction. */ ++ BFD_RELOC_V850_SDA_16_16_SPLIT_OFFSET, ++ ++/* This is a 16 bit offset from the zero data area pointer, with the ++bits placed non-contiguously in the instruction. */ ++ BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET, ++ ++/* This is a 6 bit offset from the call table base pointer. */ ++ BFD_RELOC_V850_CALLT_6_7_OFFSET, ++ ++/* This is a 16 bit offset from the call table base pointer. */ ++ BFD_RELOC_V850_CALLT_16_16_OFFSET, ++ ++/* Used for relaxing indirect function calls. */ ++ BFD_RELOC_V850_LONGCALL, ++ ++/* Used for relaxing indirect jumps. */ ++ BFD_RELOC_V850_LONGJUMP, ++ ++/* Used to maintain alignment whilst relaxing. */ ++ BFD_RELOC_V850_ALIGN, ++ ++/* This is a variation of BFD_RELOC_LO16 that can be used in v850e ld.bu ++instructions. */ ++ BFD_RELOC_V850_LO16_SPLIT_OFFSET, ++ ++/* This is a 32bit pcrel reloc for the mn10300, offset by two bytes in the ++instruction. */ ++ BFD_RELOC_MN10300_32_PCREL, ++ ++/* This is a 16bit pcrel reloc for the mn10300, offset by two bytes in the ++instruction. */ ++ BFD_RELOC_MN10300_16_PCREL, ++ ++/* This is a 8bit DP reloc for the tms320c30, where the most ++significant 8 bits of a 24 bit word are placed into the least ++significant 8 bits of the opcode. */ ++ BFD_RELOC_TIC30_LDP, ++ ++/* This is a 7bit reloc for the tms320c54x, where the least ++significant 7 bits of a 16 bit word are placed into the least ++significant 7 bits of the opcode. */ ++ BFD_RELOC_TIC54X_PARTLS7, ++ ++/* This is a 9bit DP reloc for the tms320c54x, where the most ++significant 9 bits of a 16 bit word are placed into the least ++significant 9 bits of the opcode. */ ++ BFD_RELOC_TIC54X_PARTMS9, ++ ++/* This is an extended address 23-bit reloc for the tms320c54x. */ ++ BFD_RELOC_TIC54X_23, ++ ++/* This is a 16-bit reloc for the tms320c54x, where the least ++significant 16 bits of a 23-bit extended address are placed into ++the opcode. */ ++ BFD_RELOC_TIC54X_16_OF_23, ++ ++/* This is a reloc for the tms320c54x, where the most ++significant 7 bits of a 23-bit extended address are placed into ++the opcode. */ ++ BFD_RELOC_TIC54X_MS7_OF_23, ++ ++/* This is a 48 bit reloc for the FR30 that stores 32 bits. */ ++ BFD_RELOC_FR30_48, ++ ++/* This is a 32 bit reloc for the FR30 that stores 20 bits split up into ++two sections. */ ++ BFD_RELOC_FR30_20, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 6 bit word offset in ++4 bits. */ ++ BFD_RELOC_FR30_6_IN_4, ++ ++/* This is a 16 bit reloc for the FR30 that stores an 8 bit byte offset ++into 8 bits. */ ++ BFD_RELOC_FR30_8_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 9 bit short offset ++into 8 bits. */ ++ BFD_RELOC_FR30_9_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 10 bit word offset ++into 8 bits. */ ++ BFD_RELOC_FR30_10_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 9 bit pc relative ++short offset into 8 bits. */ ++ BFD_RELOC_FR30_9_PCREL, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 12 bit pc relative ++short offset into 11 bits. */ ++ BFD_RELOC_FR30_12_PCREL, ++ ++/* Motorola Mcore relocations. */ ++ BFD_RELOC_MCORE_PCREL_IMM8BY4, ++ BFD_RELOC_MCORE_PCREL_IMM11BY2, ++ BFD_RELOC_MCORE_PCREL_IMM4BY2, ++ BFD_RELOC_MCORE_PCREL_32, ++ BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2, ++ BFD_RELOC_MCORE_RVA, ++ ++/* These are relocations for the GETA instruction. */ ++ BFD_RELOC_MMIX_GETA, ++ BFD_RELOC_MMIX_GETA_1, ++ BFD_RELOC_MMIX_GETA_2, ++ BFD_RELOC_MMIX_GETA_3, ++ ++/* These are relocations for a conditional branch instruction. */ ++ BFD_RELOC_MMIX_CBRANCH, ++ BFD_RELOC_MMIX_CBRANCH_J, ++ BFD_RELOC_MMIX_CBRANCH_1, ++ BFD_RELOC_MMIX_CBRANCH_2, ++ BFD_RELOC_MMIX_CBRANCH_3, ++ ++/* These are relocations for the PUSHJ instruction. */ ++ BFD_RELOC_MMIX_PUSHJ, ++ BFD_RELOC_MMIX_PUSHJ_1, ++ BFD_RELOC_MMIX_PUSHJ_2, ++ BFD_RELOC_MMIX_PUSHJ_3, ++ BFD_RELOC_MMIX_PUSHJ_STUBBABLE, ++ ++/* These are relocations for the JMP instruction. */ ++ BFD_RELOC_MMIX_JMP, ++ BFD_RELOC_MMIX_JMP_1, ++ BFD_RELOC_MMIX_JMP_2, ++ BFD_RELOC_MMIX_JMP_3, ++ ++/* This is a relocation for a relative address as in a GETA instruction or ++a branch. */ ++ BFD_RELOC_MMIX_ADDR19, ++ ++/* This is a relocation for a relative address as in a JMP instruction. */ ++ BFD_RELOC_MMIX_ADDR27, ++ ++/* This is a relocation for an instruction field that may be a general ++register or a value 0..255. */ ++ BFD_RELOC_MMIX_REG_OR_BYTE, ++ ++/* This is a relocation for an instruction field that may be a general ++register. */ ++ BFD_RELOC_MMIX_REG, ++ ++/* This is a relocation for two instruction fields holding a register and ++an offset, the equivalent of the relocation. */ ++ BFD_RELOC_MMIX_BASE_PLUS_OFFSET, ++ ++/* This relocation is an assertion that the expression is not allocated as ++a global register. It does not modify contents. */ ++ BFD_RELOC_MMIX_LOCAL, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit pc relative ++short offset into 7 bits. */ ++ BFD_RELOC_AVR_7_PCREL, ++ ++/* This is a 16 bit reloc for the AVR that stores 13 bit pc relative ++short offset into 12 bits. */ ++ BFD_RELOC_AVR_13_PCREL, ++ ++/* This is a 16 bit reloc for the AVR that stores 17 bit value (usually ++program memory address) into 16 bits. */ ++ BFD_RELOC_AVR_16_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (usually ++data memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_LO8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit ++of data memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HI8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit ++of program memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HH8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(usually data memory address) into 8 bit immediate value of SUBI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 8 bit of data memory address) into 8 bit immediate value of ++SUBI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(most high 8 bit of program memory address) into 8 bit immediate value ++of LDI or SUBI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (usually ++command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit ++of command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit ++of command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(usually command address) into 8 bit immediate value of SUBI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_PM_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 8 bit of 16 bit command address) into 8 bit immediate value ++of SUBI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_PM_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 6 bit of 22 bit command address) into 8 bit immediate ++value of SUBI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_PM_NEG, ++ ++/* This is a 32 bit reloc for the AVR that stores 23 bit value ++into 22 bits. */ ++ BFD_RELOC_AVR_CALL, ++ ++/* This is a 16 bit reloc for the AVR that stores all needed bits ++for absolute addressing with ldi with overflow check to linktime */ ++ BFD_RELOC_AVR_LDI, ++ ++/* This is a 6 bit reloc for the AVR that stores offset for ldd/std ++instructions */ ++ BFD_RELOC_AVR_6, ++ ++/* This is a 6 bit reloc for the AVR that stores offset for adiw/sbiw ++instructions */ ++ BFD_RELOC_AVR_6_ADIW, ++ ++/* Direct 12 bit. */ ++ BFD_RELOC_390_12, ++ ++/* 12 bit GOT offset. */ ++ BFD_RELOC_390_GOT12, ++ ++/* 32 bit PC relative PLT address. */ ++ BFD_RELOC_390_PLT32, ++ ++/* Copy symbol at runtime. */ ++ BFD_RELOC_390_COPY, ++ ++/* Create GOT entry. */ ++ BFD_RELOC_390_GLOB_DAT, ++ ++/* Create PLT entry. */ ++ BFD_RELOC_390_JMP_SLOT, ++ ++/* Adjust by program base. */ ++ BFD_RELOC_390_RELATIVE, ++ ++/* 32 bit PC relative offset to GOT. */ ++ BFD_RELOC_390_GOTPC, ++ ++/* 16 bit GOT offset. */ ++ BFD_RELOC_390_GOT16, ++ ++/* PC relative 16 bit shifted by 1. */ ++ BFD_RELOC_390_PC16DBL, ++ ++/* 16 bit PC rel. PLT shifted by 1. */ ++ BFD_RELOC_390_PLT16DBL, ++ ++/* PC relative 32 bit shifted by 1. */ ++ BFD_RELOC_390_PC32DBL, ++ ++/* 32 bit PC rel. PLT shifted by 1. */ ++ BFD_RELOC_390_PLT32DBL, ++ ++/* 32 bit PC rel. GOT shifted by 1. */ ++ BFD_RELOC_390_GOTPCDBL, ++ ++/* 64 bit GOT offset. */ ++ BFD_RELOC_390_GOT64, ++ ++/* 64 bit PC relative PLT address. */ ++ BFD_RELOC_390_PLT64, ++ ++/* 32 bit rel. offset to GOT entry. */ ++ BFD_RELOC_390_GOTENT, ++ ++/* 64 bit offset to GOT. */ ++ BFD_RELOC_390_GOTOFF64, ++ ++/* 12-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT12, ++ ++/* 16-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT16, ++ ++/* 32-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT32, ++ ++/* 64-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT64, ++ ++/* 32-bit rel. offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLTENT, ++ ++/* 16-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF16, ++ ++/* 32-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF32, ++ ++/* 64-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF64, ++ ++/* s390 tls relocations. */ ++ BFD_RELOC_390_TLS_LOAD, ++ BFD_RELOC_390_TLS_GDCALL, ++ BFD_RELOC_390_TLS_LDCALL, ++ BFD_RELOC_390_TLS_GD32, ++ BFD_RELOC_390_TLS_GD64, ++ BFD_RELOC_390_TLS_GOTIE12, ++ BFD_RELOC_390_TLS_GOTIE32, ++ BFD_RELOC_390_TLS_GOTIE64, ++ BFD_RELOC_390_TLS_LDM32, ++ BFD_RELOC_390_TLS_LDM64, ++ BFD_RELOC_390_TLS_IE32, ++ BFD_RELOC_390_TLS_IE64, ++ BFD_RELOC_390_TLS_IEENT, ++ BFD_RELOC_390_TLS_LE32, ++ BFD_RELOC_390_TLS_LE64, ++ BFD_RELOC_390_TLS_LDO32, ++ BFD_RELOC_390_TLS_LDO64, ++ BFD_RELOC_390_TLS_DTPMOD, ++ BFD_RELOC_390_TLS_DTPOFF, ++ BFD_RELOC_390_TLS_TPOFF, ++ ++/* Long displacement extension. */ ++ BFD_RELOC_390_20, ++ BFD_RELOC_390_GOT20, ++ BFD_RELOC_390_GOTPLT20, ++ BFD_RELOC_390_TLS_GOTIE20, ++ ++/* Scenix IP2K - 9-bit register number / data address */ ++ BFD_RELOC_IP2K_FR9, ++ ++/* Scenix IP2K - 4-bit register/data bank number */ ++ BFD_RELOC_IP2K_BANK, ++ ++/* Scenix IP2K - low 13 bits of instruction word address */ ++ BFD_RELOC_IP2K_ADDR16CJP, ++ ++/* Scenix IP2K - high 3 bits of instruction word address */ ++ BFD_RELOC_IP2K_PAGE3, ++ ++/* Scenix IP2K - ext/low/high 8 bits of data address */ ++ BFD_RELOC_IP2K_LO8DATA, ++ BFD_RELOC_IP2K_HI8DATA, ++ BFD_RELOC_IP2K_EX8DATA, ++ ++/* Scenix IP2K - low/high 8 bits of instruction word address */ ++ BFD_RELOC_IP2K_LO8INSN, ++ BFD_RELOC_IP2K_HI8INSN, ++ ++/* Scenix IP2K - even/odd PC modifier to modify snb pcl.0 */ ++ BFD_RELOC_IP2K_PC_SKIP, ++ ++/* Scenix IP2K - 16 bit word address in text section. */ ++ BFD_RELOC_IP2K_TEXT, ++ ++/* Scenix IP2K - 7-bit sp or dp offset */ ++ BFD_RELOC_IP2K_FR_OFFSET, ++ ++/* Scenix VPE4K coprocessor - data/insn-space addressing */ ++ BFD_RELOC_VPE4KMATH_DATA, ++ BFD_RELOC_VPE4KMATH_INSN, ++ ++/* These two relocations are used by the linker to determine which of ++the entries in a C++ virtual function table are actually used. When ++the --gc-sections option is given, the linker will zero out the entries ++that are not used, so that the code for those functions need not be ++included in the output. ++ ++VTABLE_INHERIT is a zero-space relocation used to describe to the ++linker the inheritance tree of a C++ virtual function table. The ++relocation's symbol should be the parent class' vtable, and the ++relocation should be located at the child vtable. ++ ++VTABLE_ENTRY is a zero-space relocation that describes the use of a ++virtual function table entry. The reloc's symbol should refer to the ++table of the class mentioned in the code. Off of that base, an offset ++describes the entry that is being used. For Rela hosts, this offset ++is stored in the reloc's addend. For Rel hosts, we are forced to put ++this offset in the reloc's section offset. */ ++ BFD_RELOC_VTABLE_INHERIT, ++ BFD_RELOC_VTABLE_ENTRY, ++ ++/* Intel IA64 Relocations. */ ++ BFD_RELOC_IA64_IMM14, ++ BFD_RELOC_IA64_IMM22, ++ BFD_RELOC_IA64_IMM64, ++ BFD_RELOC_IA64_DIR32MSB, ++ BFD_RELOC_IA64_DIR32LSB, ++ BFD_RELOC_IA64_DIR64MSB, ++ BFD_RELOC_IA64_DIR64LSB, ++ BFD_RELOC_IA64_GPREL22, ++ BFD_RELOC_IA64_GPREL64I, ++ BFD_RELOC_IA64_GPREL32MSB, ++ BFD_RELOC_IA64_GPREL32LSB, ++ BFD_RELOC_IA64_GPREL64MSB, ++ BFD_RELOC_IA64_GPREL64LSB, ++ BFD_RELOC_IA64_LTOFF22, ++ BFD_RELOC_IA64_LTOFF64I, ++ BFD_RELOC_IA64_PLTOFF22, ++ BFD_RELOC_IA64_PLTOFF64I, ++ BFD_RELOC_IA64_PLTOFF64MSB, ++ BFD_RELOC_IA64_PLTOFF64LSB, ++ BFD_RELOC_IA64_FPTR64I, ++ BFD_RELOC_IA64_FPTR32MSB, ++ BFD_RELOC_IA64_FPTR32LSB, ++ BFD_RELOC_IA64_FPTR64MSB, ++ BFD_RELOC_IA64_FPTR64LSB, ++ BFD_RELOC_IA64_PCREL21B, ++ BFD_RELOC_IA64_PCREL21BI, ++ BFD_RELOC_IA64_PCREL21M, ++ BFD_RELOC_IA64_PCREL21F, ++ BFD_RELOC_IA64_PCREL22, ++ BFD_RELOC_IA64_PCREL60B, ++ BFD_RELOC_IA64_PCREL64I, ++ BFD_RELOC_IA64_PCREL32MSB, ++ BFD_RELOC_IA64_PCREL32LSB, ++ BFD_RELOC_IA64_PCREL64MSB, ++ BFD_RELOC_IA64_PCREL64LSB, ++ BFD_RELOC_IA64_LTOFF_FPTR22, ++ BFD_RELOC_IA64_LTOFF_FPTR64I, ++ BFD_RELOC_IA64_LTOFF_FPTR32MSB, ++ BFD_RELOC_IA64_LTOFF_FPTR32LSB, ++ BFD_RELOC_IA64_LTOFF_FPTR64MSB, ++ BFD_RELOC_IA64_LTOFF_FPTR64LSB, ++ BFD_RELOC_IA64_SEGREL32MSB, ++ BFD_RELOC_IA64_SEGREL32LSB, ++ BFD_RELOC_IA64_SEGREL64MSB, ++ BFD_RELOC_IA64_SEGREL64LSB, ++ BFD_RELOC_IA64_SECREL32MSB, ++ BFD_RELOC_IA64_SECREL32LSB, ++ BFD_RELOC_IA64_SECREL64MSB, ++ BFD_RELOC_IA64_SECREL64LSB, ++ BFD_RELOC_IA64_REL32MSB, ++ BFD_RELOC_IA64_REL32LSB, ++ BFD_RELOC_IA64_REL64MSB, ++ BFD_RELOC_IA64_REL64LSB, ++ BFD_RELOC_IA64_LTV32MSB, ++ BFD_RELOC_IA64_LTV32LSB, ++ BFD_RELOC_IA64_LTV64MSB, ++ BFD_RELOC_IA64_LTV64LSB, ++ BFD_RELOC_IA64_IPLTMSB, ++ BFD_RELOC_IA64_IPLTLSB, ++ BFD_RELOC_IA64_COPY, ++ BFD_RELOC_IA64_LTOFF22X, ++ BFD_RELOC_IA64_LDXMOV, ++ BFD_RELOC_IA64_TPREL14, ++ BFD_RELOC_IA64_TPREL22, ++ BFD_RELOC_IA64_TPREL64I, ++ BFD_RELOC_IA64_TPREL64MSB, ++ BFD_RELOC_IA64_TPREL64LSB, ++ BFD_RELOC_IA64_LTOFF_TPREL22, ++ BFD_RELOC_IA64_DTPMOD64MSB, ++ BFD_RELOC_IA64_DTPMOD64LSB, ++ BFD_RELOC_IA64_LTOFF_DTPMOD22, ++ BFD_RELOC_IA64_DTPREL14, ++ BFD_RELOC_IA64_DTPREL22, ++ BFD_RELOC_IA64_DTPREL64I, ++ BFD_RELOC_IA64_DTPREL32MSB, ++ BFD_RELOC_IA64_DTPREL32LSB, ++ BFD_RELOC_IA64_DTPREL64MSB, ++ BFD_RELOC_IA64_DTPREL64LSB, ++ BFD_RELOC_IA64_LTOFF_DTPREL22, ++ ++/* Motorola 68HC11 reloc. ++This is the 8 bit high part of an absolute address. */ ++ BFD_RELOC_M68HC11_HI8, ++ ++/* Motorola 68HC11 reloc. ++This is the 8 bit low part of an absolute address. */ ++ BFD_RELOC_M68HC11_LO8, ++ ++/* Motorola 68HC11 reloc. ++This is the 3 bit of a value. */ ++ BFD_RELOC_M68HC11_3B, ++ ++/* Motorola 68HC11 reloc. ++This reloc marks the beginning of a jump/call instruction. ++It is used for linker relaxation to correctly identify beginning ++of instruction and change some branches to use PC-relative ++addressing mode. */ ++ BFD_RELOC_M68HC11_RL_JUMP, ++ ++/* Motorola 68HC11 reloc. ++This reloc marks a group of several instructions that gcc generates ++and for which the linker relaxation pass can modify and/or remove ++some of them. */ ++ BFD_RELOC_M68HC11_RL_GROUP, ++ ++/* Motorola 68HC11 reloc. ++This is the 16-bit lower part of an address. It is used for 'call' ++instruction to specify the symbol address without any special ++transformation (due to memory bank window). */ ++ BFD_RELOC_M68HC11_LO16, ++ ++/* Motorola 68HC11 reloc. ++This is a 8-bit reloc that specifies the page number of an address. ++It is used by 'call' instruction to specify the page number of ++the symbol. */ ++ BFD_RELOC_M68HC11_PAGE, ++ ++/* Motorola 68HC11 reloc. ++This is a 24-bit reloc that represents the address with a 16-bit ++value and a 8-bit page number. The symbol address is transformed ++to follow the 16K memory bank of 68HC12 (seen as mapped in the window). */ ++ BFD_RELOC_M68HC11_24, ++ ++/* Motorola 68HC12 reloc. ++This is the 5 bits of a value. */ ++ BFD_RELOC_M68HC12_5B, ++ ++/* NS CR16C Relocations. */ ++ BFD_RELOC_16C_NUM08, ++ BFD_RELOC_16C_NUM08_C, ++ BFD_RELOC_16C_NUM16, ++ BFD_RELOC_16C_NUM16_C, ++ BFD_RELOC_16C_NUM32, ++ BFD_RELOC_16C_NUM32_C, ++ BFD_RELOC_16C_DISP04, ++ BFD_RELOC_16C_DISP04_C, ++ BFD_RELOC_16C_DISP08, ++ BFD_RELOC_16C_DISP08_C, ++ BFD_RELOC_16C_DISP16, ++ BFD_RELOC_16C_DISP16_C, ++ BFD_RELOC_16C_DISP24, ++ BFD_RELOC_16C_DISP24_C, ++ BFD_RELOC_16C_DISP24a, ++ BFD_RELOC_16C_DISP24a_C, ++ BFD_RELOC_16C_REG04, ++ BFD_RELOC_16C_REG04_C, ++ BFD_RELOC_16C_REG04a, ++ BFD_RELOC_16C_REG04a_C, ++ BFD_RELOC_16C_REG14, ++ BFD_RELOC_16C_REG14_C, ++ BFD_RELOC_16C_REG16, ++ BFD_RELOC_16C_REG16_C, ++ BFD_RELOC_16C_REG20, ++ BFD_RELOC_16C_REG20_C, ++ BFD_RELOC_16C_ABS20, ++ BFD_RELOC_16C_ABS20_C, ++ BFD_RELOC_16C_ABS24, ++ BFD_RELOC_16C_ABS24_C, ++ BFD_RELOC_16C_IMM04, ++ BFD_RELOC_16C_IMM04_C, ++ BFD_RELOC_16C_IMM16, ++ BFD_RELOC_16C_IMM16_C, ++ BFD_RELOC_16C_IMM20, ++ BFD_RELOC_16C_IMM20_C, ++ BFD_RELOC_16C_IMM24, ++ BFD_RELOC_16C_IMM24_C, ++ BFD_RELOC_16C_IMM32, ++ BFD_RELOC_16C_IMM32_C, ++ ++/* NS CRX Relocations. */ ++ BFD_RELOC_CRX_REL4, ++ BFD_RELOC_CRX_REL8, ++ BFD_RELOC_CRX_REL8_CMP, ++ BFD_RELOC_CRX_REL16, ++ BFD_RELOC_CRX_REL24, ++ BFD_RELOC_CRX_REL32, ++ BFD_RELOC_CRX_REGREL12, ++ BFD_RELOC_CRX_REGREL22, ++ BFD_RELOC_CRX_REGREL28, ++ BFD_RELOC_CRX_REGREL32, ++ BFD_RELOC_CRX_ABS16, ++ BFD_RELOC_CRX_ABS32, ++ BFD_RELOC_CRX_NUM8, ++ BFD_RELOC_CRX_NUM16, ++ BFD_RELOC_CRX_NUM32, ++ BFD_RELOC_CRX_IMM16, ++ BFD_RELOC_CRX_IMM32, ++ BFD_RELOC_CRX_SWITCH8, ++ BFD_RELOC_CRX_SWITCH16, ++ BFD_RELOC_CRX_SWITCH32, ++ ++/* These relocs are only used within the CRIS assembler. They are not ++(at present) written to any object files. */ ++ BFD_RELOC_CRIS_BDISP8, ++ BFD_RELOC_CRIS_UNSIGNED_5, ++ BFD_RELOC_CRIS_SIGNED_6, ++ BFD_RELOC_CRIS_UNSIGNED_6, ++ BFD_RELOC_CRIS_SIGNED_8, ++ BFD_RELOC_CRIS_UNSIGNED_8, ++ BFD_RELOC_CRIS_SIGNED_16, ++ BFD_RELOC_CRIS_UNSIGNED_16, ++ BFD_RELOC_CRIS_LAPCQ_OFFSET, ++ BFD_RELOC_CRIS_UNSIGNED_4, ++ ++/* Relocs used in ELF shared libraries for CRIS. */ ++ BFD_RELOC_CRIS_COPY, ++ BFD_RELOC_CRIS_GLOB_DAT, ++ BFD_RELOC_CRIS_JUMP_SLOT, ++ BFD_RELOC_CRIS_RELATIVE, ++ ++/* 32-bit offset to symbol-entry within GOT. */ ++ BFD_RELOC_CRIS_32_GOT, ++ ++/* 16-bit offset to symbol-entry within GOT. */ ++ BFD_RELOC_CRIS_16_GOT, ++ ++/* 32-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_CRIS_32_GOTPLT, ++ ++/* 16-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_CRIS_16_GOTPLT, ++ ++/* 32-bit offset to symbol, relative to GOT. */ ++ BFD_RELOC_CRIS_32_GOTREL, ++ ++/* 32-bit offset to symbol with PLT entry, relative to GOT. */ ++ BFD_RELOC_CRIS_32_PLT_GOTREL, ++ ++/* 32-bit offset to symbol with PLT entry, relative to this relocation. */ ++ BFD_RELOC_CRIS_32_PLT_PCREL, ++ ++/* Intel i860 Relocations. */ ++ BFD_RELOC_860_COPY, ++ BFD_RELOC_860_GLOB_DAT, ++ BFD_RELOC_860_JUMP_SLOT, ++ BFD_RELOC_860_RELATIVE, ++ BFD_RELOC_860_PC26, ++ BFD_RELOC_860_PLT26, ++ BFD_RELOC_860_PC16, ++ BFD_RELOC_860_LOW0, ++ BFD_RELOC_860_SPLIT0, ++ BFD_RELOC_860_LOW1, ++ BFD_RELOC_860_SPLIT1, ++ BFD_RELOC_860_LOW2, ++ BFD_RELOC_860_SPLIT2, ++ BFD_RELOC_860_LOW3, ++ BFD_RELOC_860_LOGOT0, ++ BFD_RELOC_860_SPGOT0, ++ BFD_RELOC_860_LOGOT1, ++ BFD_RELOC_860_SPGOT1, ++ BFD_RELOC_860_LOGOTOFF0, ++ BFD_RELOC_860_SPGOTOFF0, ++ BFD_RELOC_860_LOGOTOFF1, ++ BFD_RELOC_860_SPGOTOFF1, ++ BFD_RELOC_860_LOGOTOFF2, ++ BFD_RELOC_860_LOGOTOFF3, ++ BFD_RELOC_860_LOPC, ++ BFD_RELOC_860_HIGHADJ, ++ BFD_RELOC_860_HAGOT, ++ BFD_RELOC_860_HAGOTOFF, ++ BFD_RELOC_860_HAPC, ++ BFD_RELOC_860_HIGH, ++ BFD_RELOC_860_HIGOT, ++ BFD_RELOC_860_HIGOTOFF, ++ ++/* OpenRISC Relocations. */ ++ BFD_RELOC_OPENRISC_ABS_26, ++ BFD_RELOC_OPENRISC_REL_26, ++ ++/* H8 elf Relocations. */ ++ BFD_RELOC_H8_DIR16A8, ++ BFD_RELOC_H8_DIR16R8, ++ BFD_RELOC_H8_DIR24A8, ++ BFD_RELOC_H8_DIR24R8, ++ BFD_RELOC_H8_DIR32A16, ++ ++/* Sony Xstormy16 Relocations. */ ++ BFD_RELOC_XSTORMY16_REL_12, ++ BFD_RELOC_XSTORMY16_12, ++ BFD_RELOC_XSTORMY16_24, ++ BFD_RELOC_XSTORMY16_FPTR16, ++ ++/* Relocations used by VAX ELF. */ ++ BFD_RELOC_VAX_GLOB_DAT, ++ BFD_RELOC_VAX_JMP_SLOT, ++ BFD_RELOC_VAX_RELATIVE, ++ ++/* Morpho MT - 16 bit immediate relocation. */ ++ BFD_RELOC_MT_PC16, ++ ++/* Morpho MT - Hi 16 bits of an address. */ ++ BFD_RELOC_MT_HI16, ++ ++/* Morpho MT - Low 16 bits of an address. */ ++ BFD_RELOC_MT_LO16, ++ ++/* Morpho MT - Used to tell the linker which vtable entries are used. */ ++ BFD_RELOC_MT_GNU_VTINHERIT, ++ ++/* Morpho MT - Used to tell the linker which vtable entries are used. */ ++ BFD_RELOC_MT_GNU_VTENTRY, ++ ++/* Morpho MT - 8 bit immediate relocation. */ ++ BFD_RELOC_MT_PCINSN8, ++ ++/* msp430 specific relocation codes */ ++ BFD_RELOC_MSP430_10_PCREL, ++ BFD_RELOC_MSP430_16_PCREL, ++ BFD_RELOC_MSP430_16, ++ BFD_RELOC_MSP430_16_PCREL_BYTE, ++ BFD_RELOC_MSP430_16_BYTE, ++ BFD_RELOC_MSP430_2X_PCREL, ++ BFD_RELOC_MSP430_RL_PCREL, ++ ++/* IQ2000 Relocations. */ ++ BFD_RELOC_IQ2000_OFFSET_16, ++ BFD_RELOC_IQ2000_OFFSET_21, ++ BFD_RELOC_IQ2000_UHI16, ++ ++/* Special Xtensa relocation used only by PLT entries in ELF shared ++objects to indicate that the runtime linker should set the value ++to one of its own internal functions or data structures. */ ++ BFD_RELOC_XTENSA_RTLD, ++ ++/* Xtensa relocations for ELF shared objects. */ ++ BFD_RELOC_XTENSA_GLOB_DAT, ++ BFD_RELOC_XTENSA_JMP_SLOT, ++ BFD_RELOC_XTENSA_RELATIVE, ++ ++/* Xtensa relocation used in ELF object files for symbols that may require ++PLT entries. Otherwise, this is just a generic 32-bit relocation. */ ++ BFD_RELOC_XTENSA_PLT, ++ ++/* Xtensa relocations to mark the difference of two local symbols. ++These are only needed to support linker relaxation and can be ignored ++when not relaxing. The field is set to the value of the difference ++assuming no relaxation. The relocation encodes the position of the ++first symbol so the linker can determine whether to adjust the field ++value. */ ++ BFD_RELOC_XTENSA_DIFF8, ++ BFD_RELOC_XTENSA_DIFF16, ++ BFD_RELOC_XTENSA_DIFF32, ++ ++/* Generic Xtensa relocations for instruction operands. Only the slot ++number is encoded in the relocation. The relocation applies to the ++last PC-relative immediate operand, or if there are no PC-relative ++immediates, to the last immediate operand. */ ++ BFD_RELOC_XTENSA_SLOT0_OP, ++ BFD_RELOC_XTENSA_SLOT1_OP, ++ BFD_RELOC_XTENSA_SLOT2_OP, ++ BFD_RELOC_XTENSA_SLOT3_OP, ++ BFD_RELOC_XTENSA_SLOT4_OP, ++ BFD_RELOC_XTENSA_SLOT5_OP, ++ BFD_RELOC_XTENSA_SLOT6_OP, ++ BFD_RELOC_XTENSA_SLOT7_OP, ++ BFD_RELOC_XTENSA_SLOT8_OP, ++ BFD_RELOC_XTENSA_SLOT9_OP, ++ BFD_RELOC_XTENSA_SLOT10_OP, ++ BFD_RELOC_XTENSA_SLOT11_OP, ++ BFD_RELOC_XTENSA_SLOT12_OP, ++ BFD_RELOC_XTENSA_SLOT13_OP, ++ BFD_RELOC_XTENSA_SLOT14_OP, ++ ++/* Alternate Xtensa relocations. Only the slot is encoded in the ++relocation. The meaning of these relocations is opcode-specific. */ ++ BFD_RELOC_XTENSA_SLOT0_ALT, ++ BFD_RELOC_XTENSA_SLOT1_ALT, ++ BFD_RELOC_XTENSA_SLOT2_ALT, ++ BFD_RELOC_XTENSA_SLOT3_ALT, ++ BFD_RELOC_XTENSA_SLOT4_ALT, ++ BFD_RELOC_XTENSA_SLOT5_ALT, ++ BFD_RELOC_XTENSA_SLOT6_ALT, ++ BFD_RELOC_XTENSA_SLOT7_ALT, ++ BFD_RELOC_XTENSA_SLOT8_ALT, ++ BFD_RELOC_XTENSA_SLOT9_ALT, ++ BFD_RELOC_XTENSA_SLOT10_ALT, ++ BFD_RELOC_XTENSA_SLOT11_ALT, ++ BFD_RELOC_XTENSA_SLOT12_ALT, ++ BFD_RELOC_XTENSA_SLOT13_ALT, ++ BFD_RELOC_XTENSA_SLOT14_ALT, ++ ++/* Xtensa relocations for backward compatibility. These have all been ++replaced by BFD_RELOC_XTENSA_SLOT0_OP. */ ++ BFD_RELOC_XTENSA_OP0, ++ BFD_RELOC_XTENSA_OP1, ++ BFD_RELOC_XTENSA_OP2, ++ ++/* Xtensa relocation to mark that the assembler expanded the ++instructions from an original target. The expansion size is ++encoded in the reloc size. */ ++ BFD_RELOC_XTENSA_ASM_EXPAND, ++ ++/* Xtensa relocation to mark that the linker should simplify ++assembler-expanded instructions. This is commonly used ++internally by the linker after analysis of a ++BFD_RELOC_XTENSA_ASM_EXPAND. */ ++ BFD_RELOC_XTENSA_ASM_SIMPLIFY, ++ ++/* 8 bit signed offset in (ix+d) or (iy+d). */ ++ BFD_RELOC_Z80_DISP8, ++ ++/* DJNZ offset. */ ++ BFD_RELOC_Z8K_DISP7, ++ ++/* CALR offset. */ ++ BFD_RELOC_Z8K_CALLR, ++ ++/* 4 bit value. */ ++ BFD_RELOC_Z8K_IMM4L, ++ BFD_RELOC_UNUSED }; ++typedef enum bfd_reloc_code_real bfd_reloc_code_real_type; ++reloc_howto_type *bfd_reloc_type_lookup ++ (bfd *abfd, bfd_reloc_code_real_type code); ++ ++const char *bfd_get_reloc_code_name (bfd_reloc_code_real_type code); ++ ++/* Extracted from syms.c. */ ++ ++typedef struct bfd_symbol ++{ ++ /* A pointer to the BFD which owns the symbol. This information ++ is necessary so that a back end can work out what additional ++ information (invisible to the application writer) is carried ++ with the symbol. ++ ++ This field is *almost* redundant, since you can use section->owner ++ instead, except that some symbols point to the global sections ++ bfd_{abs,com,und}_section. This could be fixed by making ++ these globals be per-bfd (or per-target-flavor). FIXME. */ ++ struct bfd *the_bfd; /* Use bfd_asymbol_bfd(sym) to access this field. */ ++ ++ /* The text of the symbol. The name is left alone, and not copied; the ++ application may not alter it. */ ++ const char *name; ++ ++ /* The value of the symbol. This really should be a union of a ++ numeric value with a pointer, since some flags indicate that ++ a pointer to another symbol is stored here. */ ++ symvalue value; ++ ++ /* Attributes of a symbol. */ ++#define BSF_NO_FLAGS 0x00 ++ ++ /* The symbol has local scope; <> in <>. The value ++ is the offset into the section of the data. */ ++#define BSF_LOCAL 0x01 ++ ++ /* The symbol has global scope; initialized data in <>. The ++ value is the offset into the section of the data. */ ++#define BSF_GLOBAL 0x02 ++ ++ /* The symbol has global scope and is exported. The value is ++ the offset into the section of the data. */ ++#define BSF_EXPORT BSF_GLOBAL /* No real difference. */ ++ ++ /* A normal C symbol would be one of: ++ <>, <>, <> or ++ <>. */ ++ ++ /* The symbol is a debugging record. The value has an arbitrary ++ meaning, unless BSF_DEBUGGING_RELOC is also set. */ ++#define BSF_DEBUGGING 0x08 ++ ++ /* The symbol denotes a function entry point. Used in ELF, ++ perhaps others someday. */ ++#define BSF_FUNCTION 0x10 ++ ++ /* Used by the linker. */ ++#define BSF_KEEP 0x20 ++#define BSF_KEEP_G 0x40 ++ ++ /* A weak global symbol, overridable without warnings by ++ a regular global symbol of the same name. */ ++#define BSF_WEAK 0x80 ++ ++ /* This symbol was created to point to a section, e.g. ELF's ++ STT_SECTION symbols. */ ++#define BSF_SECTION_SYM 0x100 ++ ++ /* The symbol used to be a common symbol, but now it is ++ allocated. */ ++#define BSF_OLD_COMMON 0x200 ++ ++ /* The default value for common data. */ ++#define BFD_FORT_COMM_DEFAULT_VALUE 0 ++ ++ /* In some files the type of a symbol sometimes alters its ++ location in an output file - ie in coff a <> symbol ++ which is also <> symbol appears where it was ++ declared and not at the end of a section. This bit is set ++ by the target BFD part to convey this information. */ ++#define BSF_NOT_AT_END 0x400 ++ ++ /* Signal that the symbol is the label of constructor section. */ ++#define BSF_CONSTRUCTOR 0x800 ++ ++ /* Signal that the symbol is a warning symbol. The name is a ++ warning. The name of the next symbol is the one to warn about; ++ if a reference is made to a symbol with the same name as the next ++ symbol, a warning is issued by the linker. */ ++#define BSF_WARNING 0x1000 ++ ++ /* Signal that the symbol is indirect. This symbol is an indirect ++ pointer to the symbol with the same name as the next symbol. */ ++#define BSF_INDIRECT 0x2000 ++ ++ /* BSF_FILE marks symbols that contain a file name. This is used ++ for ELF STT_FILE symbols. */ ++#define BSF_FILE 0x4000 ++ ++ /* Symbol is from dynamic linking information. */ ++#define BSF_DYNAMIC 0x8000 ++ ++ /* The symbol denotes a data object. Used in ELF, and perhaps ++ others someday. */ ++#define BSF_OBJECT 0x10000 ++ ++ /* This symbol is a debugging symbol. The value is the offset ++ into the section of the data. BSF_DEBUGGING should be set ++ as well. */ ++#define BSF_DEBUGGING_RELOC 0x20000 ++ ++ /* This symbol is thread local. Used in ELF. */ ++#define BSF_THREAD_LOCAL 0x40000 ++ ++ flagword flags; ++ ++ /* A pointer to the section to which this symbol is ++ relative. This will always be non NULL, there are special ++ sections for undefined and absolute symbols. */ ++ struct bfd_section *section; ++ ++ /* Back end special data. */ ++ union ++ { ++ void *p; ++ bfd_vma i; ++ } ++ udata; ++} ++asymbol; ++ ++#define bfd_get_symtab_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_symtab_upper_bound, (abfd)) ++ ++bfd_boolean bfd_is_local_label (bfd *abfd, asymbol *sym); ++ ++bfd_boolean bfd_is_local_label_name (bfd *abfd, const char *name); ++ ++#define bfd_is_local_label_name(abfd, name) \ ++ BFD_SEND (abfd, _bfd_is_local_label_name, (abfd, name)) ++ ++bfd_boolean bfd_is_target_special_symbol (bfd *abfd, asymbol *sym); ++ ++#define bfd_is_target_special_symbol(abfd, sym) \ ++ BFD_SEND (abfd, _bfd_is_target_special_symbol, (abfd, sym)) ++ ++#define bfd_canonicalize_symtab(abfd, location) \ ++ BFD_SEND (abfd, _bfd_canonicalize_symtab, (abfd, location)) ++ ++bfd_boolean bfd_set_symtab ++ (bfd *abfd, asymbol **location, unsigned int count); ++ ++void bfd_print_symbol_vandf (bfd *abfd, void *file, asymbol *symbol); ++ ++#define bfd_make_empty_symbol(abfd) \ ++ BFD_SEND (abfd, _bfd_make_empty_symbol, (abfd)) ++ ++asymbol *_bfd_generic_make_empty_symbol (bfd *); ++ ++#define bfd_make_debug_symbol(abfd,ptr,size) \ ++ BFD_SEND (abfd, _bfd_make_debug_symbol, (abfd, ptr, size)) ++ ++int bfd_decode_symclass (asymbol *symbol); ++ ++bfd_boolean bfd_is_undefined_symclass (int symclass); ++ ++void bfd_symbol_info (asymbol *symbol, symbol_info *ret); ++ ++bfd_boolean bfd_copy_private_symbol_data ++ (bfd *ibfd, asymbol *isym, bfd *obfd, asymbol *osym); ++ ++#define bfd_copy_private_symbol_data(ibfd, isymbol, obfd, osymbol) \ ++ BFD_SEND (obfd, _bfd_copy_private_symbol_data, \ ++ (ibfd, isymbol, obfd, osymbol)) ++ ++/* Extracted from bfd.c. */ ++struct bfd ++{ ++ /* A unique identifier of the BFD */ ++ unsigned int id; ++ ++ /* The filename the application opened the BFD with. */ ++ const char *filename; ++ ++ /* A pointer to the target jump table. */ ++ const struct bfd_target *xvec; ++ ++ /* The IOSTREAM, and corresponding IO vector that provide access ++ to the file backing the BFD. */ ++ void *iostream; ++ const struct bfd_iovec *iovec; ++ ++ /* Is the file descriptor being cached? That is, can it be closed as ++ needed, and re-opened when accessed later? */ ++ bfd_boolean cacheable; ++ ++ /* Marks whether there was a default target specified when the ++ BFD was opened. This is used to select which matching algorithm ++ to use to choose the back end. */ ++ bfd_boolean target_defaulted; ++ ++ /* The caching routines use these to maintain a ++ least-recently-used list of BFDs. */ ++ struct bfd *lru_prev, *lru_next; ++ ++ /* When a file is closed by the caching routines, BFD retains ++ state information on the file here... */ ++ ufile_ptr where; ++ ++ /* ... and here: (``once'' means at least once). */ ++ bfd_boolean opened_once; ++ ++ /* Set if we have a locally maintained mtime value, rather than ++ getting it from the file each time. */ ++ bfd_boolean mtime_set; ++ ++ /* File modified time, if mtime_set is TRUE. */ ++ long mtime; ++ ++ /* Reserved for an unimplemented file locking extension. */ ++ int ifd; ++ ++ /* The format which belongs to the BFD. (object, core, etc.) */ ++ bfd_format format; ++ ++ /* The direction with which the BFD was opened. */ ++ enum bfd_direction ++ { ++ no_direction = 0, ++ read_direction = 1, ++ write_direction = 2, ++ both_direction = 3 ++ } ++ direction; ++ ++ /* Format_specific flags. */ ++ flagword flags; ++ ++ /* Currently my_archive is tested before adding origin to ++ anything. I believe that this can become always an add of ++ origin, with origin set to 0 for non archive files. */ ++ ufile_ptr origin; ++ ++ /* Remember when output has begun, to stop strange things ++ from happening. */ ++ bfd_boolean output_has_begun; ++ ++ /* A hash table for section names. */ ++ struct bfd_hash_table section_htab; ++ ++ /* Pointer to linked list of sections. */ ++ struct bfd_section *sections; ++ ++ /* The last section on the section list. */ ++ struct bfd_section *section_last; ++ ++ /* The number of sections. */ ++ unsigned int section_count; ++ ++ /* Stuff only useful for object files: ++ The start address. */ ++ bfd_vma start_address; ++ ++ /* Used for input and output. */ ++ unsigned int symcount; ++ ++ /* Symbol table for output BFD (with symcount entries). */ ++ struct bfd_symbol **outsymbols; ++ ++ /* Used for slurped dynamic symbol tables. */ ++ unsigned int dynsymcount; ++ ++ /* Pointer to structure which contains architecture information. */ ++ const struct bfd_arch_info *arch_info; ++ ++ /* Flag set if symbols from this BFD should not be exported. */ ++ bfd_boolean no_export; ++ ++ /* Stuff only useful for archives. */ ++ void *arelt_data; ++ struct bfd *my_archive; /* The containing archive BFD. */ ++ struct bfd *next; /* The next BFD in the archive. */ ++ struct bfd *archive_head; /* The first BFD in the archive. */ ++ bfd_boolean has_armap; ++ ++ /* A chain of BFD structures involved in a link. */ ++ struct bfd *link_next; ++ ++ /* A field used by _bfd_generic_link_add_archive_symbols. This will ++ be used only for archive elements. */ ++ int archive_pass; ++ ++ /* Used by the back end to hold private data. */ ++ union ++ { ++ struct aout_data_struct *aout_data; ++ struct artdata *aout_ar_data; ++ struct _oasys_data *oasys_obj_data; ++ struct _oasys_ar_data *oasys_ar_data; ++ struct coff_tdata *coff_obj_data; ++ struct pe_tdata *pe_obj_data; ++ struct xcoff_tdata *xcoff_obj_data; ++ struct ecoff_tdata *ecoff_obj_data; ++ struct ieee_data_struct *ieee_data; ++ struct ieee_ar_data_struct *ieee_ar_data; ++ struct srec_data_struct *srec_data; ++ struct ihex_data_struct *ihex_data; ++ struct tekhex_data_struct *tekhex_data; ++ struct elf_obj_tdata *elf_obj_data; ++ struct nlm_obj_tdata *nlm_obj_data; ++ struct bout_data_struct *bout_data; ++ struct mmo_data_struct *mmo_data; ++ struct sun_core_struct *sun_core_data; ++ struct sco5_core_struct *sco5_core_data; ++ struct trad_core_struct *trad_core_data; ++ struct som_data_struct *som_data; ++ struct hpux_core_struct *hpux_core_data; ++ struct hppabsd_core_struct *hppabsd_core_data; ++ struct sgi_core_struct *sgi_core_data; ++ struct lynx_core_struct *lynx_core_data; ++ struct osf_core_struct *osf_core_data; ++ struct cisco_core_struct *cisco_core_data; ++ struct versados_data_struct *versados_data; ++ struct netbsd_core_struct *netbsd_core_data; ++ struct mach_o_data_struct *mach_o_data; ++ struct mach_o_fat_data_struct *mach_o_fat_data; ++ struct bfd_pef_data_struct *pef_data; ++ struct bfd_pef_xlib_data_struct *pef_xlib_data; ++ struct bfd_sym_data_struct *sym_data; ++ void *any; ++ } ++ tdata; ++ ++ /* Used by the application to hold private data. */ ++ void *usrdata; ++ ++ /* Where all the allocated stuff under this BFD goes. This is a ++ struct objalloc *, but we use void * to avoid requiring the inclusion ++ of objalloc.h. */ ++ void *memory; ++}; ++ ++typedef enum bfd_error ++{ ++ bfd_error_no_error = 0, ++ bfd_error_system_call, ++ bfd_error_invalid_target, ++ bfd_error_wrong_format, ++ bfd_error_wrong_object_format, ++ bfd_error_invalid_operation, ++ bfd_error_no_memory, ++ bfd_error_no_symbols, ++ bfd_error_no_armap, ++ bfd_error_no_more_archived_files, ++ bfd_error_malformed_archive, ++ bfd_error_file_not_recognized, ++ bfd_error_file_ambiguously_recognized, ++ bfd_error_no_contents, ++ bfd_error_nonrepresentable_section, ++ bfd_error_no_debug_section, ++ bfd_error_bad_value, ++ bfd_error_file_truncated, ++ bfd_error_file_too_big, ++ bfd_error_invalid_error_code ++} ++bfd_error_type; ++ ++bfd_error_type bfd_get_error (void); ++ ++void bfd_set_error (bfd_error_type error_tag); ++ ++const char *bfd_errmsg (bfd_error_type error_tag); ++ ++void bfd_perror (const char *message); ++ ++typedef void (*bfd_error_handler_type) (const char *, ...); ++ ++bfd_error_handler_type bfd_set_error_handler (bfd_error_handler_type); ++ ++void bfd_set_error_program_name (const char *); ++ ++bfd_error_handler_type bfd_get_error_handler (void); ++ ++long bfd_get_reloc_upper_bound (bfd *abfd, asection *sect); ++ ++long bfd_canonicalize_reloc ++ (bfd *abfd, asection *sec, arelent **loc, asymbol **syms); ++ ++void bfd_set_reloc ++ (bfd *abfd, asection *sec, arelent **rel, unsigned int count); ++ ++bfd_boolean bfd_set_file_flags (bfd *abfd, flagword flags); ++ ++int bfd_get_arch_size (bfd *abfd); ++ ++int bfd_get_sign_extend_vma (bfd *abfd); ++ ++bfd_boolean bfd_set_start_address (bfd *abfd, bfd_vma vma); ++ ++unsigned int bfd_get_gp_size (bfd *abfd); ++ ++void bfd_set_gp_size (bfd *abfd, unsigned int i); ++ ++bfd_vma bfd_scan_vma (const char *string, const char **end, int base); ++ ++bfd_boolean bfd_copy_private_header_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_copy_private_header_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_copy_private_header_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_copy_private_bfd_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_copy_private_bfd_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_copy_private_bfd_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_merge_private_bfd_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_merge_private_bfd_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_merge_private_bfd_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_set_private_flags (bfd *abfd, flagword flags); ++ ++#define bfd_set_private_flags(abfd, flags) \ ++ BFD_SEND (abfd, _bfd_set_private_flags, (abfd, flags)) ++#define bfd_sizeof_headers(abfd, reloc) \ ++ BFD_SEND (abfd, _bfd_sizeof_headers, (abfd, reloc)) ++ ++#define bfd_find_nearest_line(abfd, sec, syms, off, file, func, line) \ ++ BFD_SEND (abfd, _bfd_find_nearest_line, \ ++ (abfd, sec, syms, off, file, func, line)) ++ ++#define bfd_find_line(abfd, syms, sym, file, line) \ ++ BFD_SEND (abfd, _bfd_find_line, \ ++ (abfd, syms, sym, file, line)) ++ ++#define bfd_find_inliner_info(abfd, file, func, line) \ ++ BFD_SEND (abfd, _bfd_find_inliner_info, \ ++ (abfd, file, func, line)) ++ ++#define bfd_debug_info_start(abfd) \ ++ BFD_SEND (abfd, _bfd_debug_info_start, (abfd)) ++ ++#define bfd_debug_info_end(abfd) \ ++ BFD_SEND (abfd, _bfd_debug_info_end, (abfd)) ++ ++#define bfd_debug_info_accumulate(abfd, section) \ ++ BFD_SEND (abfd, _bfd_debug_info_accumulate, (abfd, section)) ++ ++#define bfd_stat_arch_elt(abfd, stat) \ ++ BFD_SEND (abfd, _bfd_stat_arch_elt,(abfd, stat)) ++ ++#define bfd_update_armap_timestamp(abfd) \ ++ BFD_SEND (abfd, _bfd_update_armap_timestamp, (abfd)) ++ ++#define bfd_set_arch_mach(abfd, arch, mach)\ ++ BFD_SEND ( abfd, _bfd_set_arch_mach, (abfd, arch, mach)) ++ ++#define bfd_relax_section(abfd, section, link_info, again) \ ++ BFD_SEND (abfd, _bfd_relax_section, (abfd, section, link_info, again)) ++ ++#define bfd_gc_sections(abfd, link_info) \ ++ BFD_SEND (abfd, _bfd_gc_sections, (abfd, link_info)) ++ ++#define bfd_merge_sections(abfd, link_info) \ ++ BFD_SEND (abfd, _bfd_merge_sections, (abfd, link_info)) ++ ++#define bfd_is_group_section(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_is_group_section, (abfd, sec)) ++ ++#define bfd_discard_group(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_discard_group, (abfd, sec)) ++ ++#define bfd_link_hash_table_create(abfd) \ ++ BFD_SEND (abfd, _bfd_link_hash_table_create, (abfd)) ++ ++#define bfd_link_hash_table_free(abfd, hash) \ ++ BFD_SEND (abfd, _bfd_link_hash_table_free, (hash)) ++ ++#define bfd_link_add_symbols(abfd, info) \ ++ BFD_SEND (abfd, _bfd_link_add_symbols, (abfd, info)) ++ ++#define bfd_link_just_syms(abfd, sec, info) \ ++ BFD_SEND (abfd, _bfd_link_just_syms, (sec, info)) ++ ++#define bfd_final_link(abfd, info) \ ++ BFD_SEND (abfd, _bfd_final_link, (abfd, info)) ++ ++#define bfd_free_cached_info(abfd) \ ++ BFD_SEND (abfd, _bfd_free_cached_info, (abfd)) ++ ++#define bfd_get_dynamic_symtab_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_dynamic_symtab_upper_bound, (abfd)) ++ ++#define bfd_print_private_bfd_data(abfd, file)\ ++ BFD_SEND (abfd, _bfd_print_private_bfd_data, (abfd, file)) ++ ++#define bfd_canonicalize_dynamic_symtab(abfd, asymbols) \ ++ BFD_SEND (abfd, _bfd_canonicalize_dynamic_symtab, (abfd, asymbols)) ++ ++#define bfd_get_synthetic_symtab(abfd, count, syms, dyncount, dynsyms, ret) \ ++ BFD_SEND (abfd, _bfd_get_synthetic_symtab, (abfd, count, syms, \ ++ dyncount, dynsyms, ret)) ++ ++#define bfd_get_dynamic_reloc_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_dynamic_reloc_upper_bound, (abfd)) ++ ++#define bfd_canonicalize_dynamic_reloc(abfd, arels, asyms) \ ++ BFD_SEND (abfd, _bfd_canonicalize_dynamic_reloc, (abfd, arels, asyms)) ++ ++extern bfd_byte *bfd_get_relocated_section_contents ++ (bfd *, struct bfd_link_info *, struct bfd_link_order *, bfd_byte *, ++ bfd_boolean, asymbol **); ++ ++bfd_boolean bfd_alt_mach_code (bfd *abfd, int alternative); ++ ++struct bfd_preserve ++{ ++ void *marker; ++ void *tdata; ++ flagword flags; ++ const struct bfd_arch_info *arch_info; ++ struct bfd_section *sections; ++ struct bfd_section *section_last; ++ unsigned int section_count; ++ struct bfd_hash_table section_htab; ++}; ++ ++bfd_boolean bfd_preserve_save (bfd *, struct bfd_preserve *); ++ ++void bfd_preserve_restore (bfd *, struct bfd_preserve *); ++ ++void bfd_preserve_finish (bfd *, struct bfd_preserve *); ++ ++/* Extracted from archive.c. */ ++symindex bfd_get_next_mapent ++ (bfd *abfd, symindex previous, carsym **sym); ++ ++bfd_boolean bfd_set_archive_head (bfd *output, bfd *new_head); ++ ++bfd *bfd_openr_next_archived_file (bfd *archive, bfd *previous); ++ ++/* Extracted from corefile.c. */ ++const char *bfd_core_file_failing_command (bfd *abfd); ++ ++int bfd_core_file_failing_signal (bfd *abfd); ++ ++bfd_boolean core_file_matches_executable_p ++ (bfd *core_bfd, bfd *exec_bfd); ++ ++/* Extracted from targets.c. */ ++#define BFD_SEND(bfd, message, arglist) \ ++ ((*((bfd)->xvec->message)) arglist) ++ ++#ifdef DEBUG_BFD_SEND ++#undef BFD_SEND ++#define BFD_SEND(bfd, message, arglist) \ ++ (((bfd) && (bfd)->xvec && (bfd)->xvec->message) ? \ ++ ((*((bfd)->xvec->message)) arglist) : \ ++ (bfd_assert (__FILE__,__LINE__), NULL)) ++#endif ++#define BFD_SEND_FMT(bfd, message, arglist) \ ++ (((bfd)->xvec->message[(int) ((bfd)->format)]) arglist) ++ ++#ifdef DEBUG_BFD_SEND ++#undef BFD_SEND_FMT ++#define BFD_SEND_FMT(bfd, message, arglist) \ ++ (((bfd) && (bfd)->xvec && (bfd)->xvec->message) ? \ ++ (((bfd)->xvec->message[(int) ((bfd)->format)]) arglist) : \ ++ (bfd_assert (__FILE__,__LINE__), NULL)) ++#endif ++ ++enum bfd_flavour ++{ ++ bfd_target_unknown_flavour, ++ bfd_target_aout_flavour, ++ bfd_target_coff_flavour, ++ bfd_target_ecoff_flavour, ++ bfd_target_xcoff_flavour, ++ bfd_target_elf_flavour, ++ bfd_target_ieee_flavour, ++ bfd_target_nlm_flavour, ++ bfd_target_oasys_flavour, ++ bfd_target_tekhex_flavour, ++ bfd_target_srec_flavour, ++ bfd_target_ihex_flavour, ++ bfd_target_som_flavour, ++ bfd_target_os9k_flavour, ++ bfd_target_versados_flavour, ++ bfd_target_msdos_flavour, ++ bfd_target_ovax_flavour, ++ bfd_target_evax_flavour, ++ bfd_target_mmo_flavour, ++ bfd_target_mach_o_flavour, ++ bfd_target_pef_flavour, ++ bfd_target_pef_xlib_flavour, ++ bfd_target_sym_flavour ++}; ++ ++enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN }; ++ ++/* Forward declaration. */ ++typedef struct bfd_link_info _bfd_link_info; ++ ++typedef struct bfd_target ++{ ++ /* Identifies the kind of target, e.g., SunOS4, Ultrix, etc. */ ++ char *name; ++ ++ /* The "flavour" of a back end is a general indication about ++ the contents of a file. */ ++ enum bfd_flavour flavour; ++ ++ /* The order of bytes within the data area of a file. */ ++ enum bfd_endian byteorder; ++ ++ /* The order of bytes within the header parts of a file. */ ++ enum bfd_endian header_byteorder; ++ ++ /* A mask of all the flags which an executable may have set - ++ from the set <>, <>, ...<>. */ ++ flagword object_flags; ++ ++ /* A mask of all the flags which a section may have set - from ++ the set <>, <>, ...<>. */ ++ flagword section_flags; ++ ++ /* The character normally found at the front of a symbol. ++ (if any), perhaps `_'. */ ++ char symbol_leading_char; ++ ++ /* The pad character for file names within an archive header. */ ++ char ar_pad_char; ++ ++ /* The maximum number of characters in an archive header. */ ++ unsigned short ar_max_namelen; ++ ++ /* Entries for byte swapping for data. These are different from the ++ other entry points, since they don't take a BFD as the first argument. ++ Certain other handlers could do the same. */ ++ bfd_uint64_t (*bfd_getx64) (const void *); ++ bfd_int64_t (*bfd_getx_signed_64) (const void *); ++ void (*bfd_putx64) (bfd_uint64_t, void *); ++ bfd_vma (*bfd_getx32) (const void *); ++ bfd_signed_vma (*bfd_getx_signed_32) (const void *); ++ void (*bfd_putx32) (bfd_vma, void *); ++ bfd_vma (*bfd_getx16) (const void *); ++ bfd_signed_vma (*bfd_getx_signed_16) (const void *); ++ void (*bfd_putx16) (bfd_vma, void *); ++ ++ /* Byte swapping for the headers. */ ++ bfd_uint64_t (*bfd_h_getx64) (const void *); ++ bfd_int64_t (*bfd_h_getx_signed_64) (const void *); ++ void (*bfd_h_putx64) (bfd_uint64_t, void *); ++ bfd_vma (*bfd_h_getx32) (const void *); ++ bfd_signed_vma (*bfd_h_getx_signed_32) (const void *); ++ void (*bfd_h_putx32) (bfd_vma, void *); ++ bfd_vma (*bfd_h_getx16) (const void *); ++ bfd_signed_vma (*bfd_h_getx_signed_16) (const void *); ++ void (*bfd_h_putx16) (bfd_vma, void *); ++ ++ /* Format dependent routines: these are vectors of entry points ++ within the target vector structure, one for each format to check. */ ++ ++ /* Check the format of a file being read. Return a <> or zero. */ ++ const struct bfd_target *(*_bfd_check_format[bfd_type_end]) (bfd *); ++ ++ /* Set the format of a file being written. */ ++ bfd_boolean (*_bfd_set_format[bfd_type_end]) (bfd *); ++ ++ /* Write cached information into a file being written, at <>. */ ++ bfd_boolean (*_bfd_write_contents[bfd_type_end]) (bfd *); ++ ++ ++ /* Generic entry points. */ ++#define BFD_JUMP_TABLE_GENERIC(NAME) \ ++ NAME##_close_and_cleanup, \ ++ NAME##_bfd_free_cached_info, \ ++ NAME##_new_section_hook, \ ++ NAME##_get_section_contents, \ ++ NAME##_get_section_contents_in_window ++ ++ /* Called when the BFD is being closed to do any necessary cleanup. */ ++ bfd_boolean (*_close_and_cleanup) (bfd *); ++ /* Ask the BFD to free all cached information. */ ++ bfd_boolean (*_bfd_free_cached_info) (bfd *); ++ /* Called when a new section is created. */ ++ bfd_boolean (*_new_section_hook) (bfd *, sec_ptr); ++ /* Read the contents of a section. */ ++ bfd_boolean (*_bfd_get_section_contents) ++ (bfd *, sec_ptr, void *, file_ptr, bfd_size_type); ++ bfd_boolean (*_bfd_get_section_contents_in_window) ++ (bfd *, sec_ptr, bfd_window *, file_ptr, bfd_size_type); ++ ++ /* Entry points to copy private data. */ ++#define BFD_JUMP_TABLE_COPY(NAME) \ ++ NAME##_bfd_copy_private_bfd_data, \ ++ NAME##_bfd_merge_private_bfd_data, \ ++ _bfd_generic_init_private_section_data, \ ++ NAME##_bfd_copy_private_section_data, \ ++ NAME##_bfd_copy_private_symbol_data, \ ++ NAME##_bfd_copy_private_header_data, \ ++ NAME##_bfd_set_private_flags, \ ++ NAME##_bfd_print_private_bfd_data ++ ++ /* Called to copy BFD general private data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_bfd_data) (bfd *, bfd *); ++ /* Called to merge BFD general private data from one object file ++ to a common output file when linking. */ ++ bfd_boolean (*_bfd_merge_private_bfd_data) (bfd *, bfd *); ++ /* Called to initialize BFD private section data from one object file ++ to another. */ ++#define bfd_init_private_section_data(ibfd, isec, obfd, osec, link_info) \ ++ BFD_SEND (obfd, _bfd_init_private_section_data, (ibfd, isec, obfd, osec, link_info)) ++ bfd_boolean (*_bfd_init_private_section_data) ++ (bfd *, sec_ptr, bfd *, sec_ptr, struct bfd_link_info *); ++ /* Called to copy BFD private section data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_section_data) ++ (bfd *, sec_ptr, bfd *, sec_ptr); ++ /* Called to copy BFD private symbol data from one symbol ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_symbol_data) ++ (bfd *, asymbol *, bfd *, asymbol *); ++ /* Called to copy BFD private header data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_header_data) ++ (bfd *, bfd *); ++ /* Called to set private backend flags. */ ++ bfd_boolean (*_bfd_set_private_flags) (bfd *, flagword); ++ ++ /* Called to print private BFD data. */ ++ bfd_boolean (*_bfd_print_private_bfd_data) (bfd *, void *); ++ ++ /* Core file entry points. */ ++#define BFD_JUMP_TABLE_CORE(NAME) \ ++ NAME##_core_file_failing_command, \ ++ NAME##_core_file_failing_signal, \ ++ NAME##_core_file_matches_executable_p ++ ++ char * (*_core_file_failing_command) (bfd *); ++ int (*_core_file_failing_signal) (bfd *); ++ bfd_boolean (*_core_file_matches_executable_p) (bfd *, bfd *); ++ ++ /* Archive entry points. */ ++#define BFD_JUMP_TABLE_ARCHIVE(NAME) \ ++ NAME##_slurp_armap, \ ++ NAME##_slurp_extended_name_table, \ ++ NAME##_construct_extended_name_table, \ ++ NAME##_truncate_arname, \ ++ NAME##_write_armap, \ ++ NAME##_read_ar_hdr, \ ++ NAME##_openr_next_archived_file, \ ++ NAME##_get_elt_at_index, \ ++ NAME##_generic_stat_arch_elt, \ ++ NAME##_update_armap_timestamp ++ ++ bfd_boolean (*_bfd_slurp_armap) (bfd *); ++ bfd_boolean (*_bfd_slurp_extended_name_table) (bfd *); ++ bfd_boolean (*_bfd_construct_extended_name_table) ++ (bfd *, char **, bfd_size_type *, const char **); ++ void (*_bfd_truncate_arname) (bfd *, const char *, char *); ++ bfd_boolean (*write_armap) ++ (bfd *, unsigned int, struct orl *, unsigned int, int); ++ void * (*_bfd_read_ar_hdr_fn) (bfd *); ++ bfd * (*openr_next_archived_file) (bfd *, bfd *); ++#define bfd_get_elt_at_index(b,i) BFD_SEND (b, _bfd_get_elt_at_index, (b,i)) ++ bfd * (*_bfd_get_elt_at_index) (bfd *, symindex); ++ int (*_bfd_stat_arch_elt) (bfd *, struct stat *); ++ bfd_boolean (*_bfd_update_armap_timestamp) (bfd *); ++ ++ /* Entry points used for symbols. */ ++#define BFD_JUMP_TABLE_SYMBOLS(NAME) \ ++ NAME##_get_symtab_upper_bound, \ ++ NAME##_canonicalize_symtab, \ ++ NAME##_make_empty_symbol, \ ++ NAME##_print_symbol, \ ++ NAME##_get_symbol_info, \ ++ NAME##_bfd_is_local_label_name, \ ++ NAME##_bfd_is_target_special_symbol, \ ++ NAME##_get_lineno, \ ++ NAME##_find_nearest_line, \ ++ _bfd_generic_find_line, \ ++ NAME##_find_inliner_info, \ ++ NAME##_bfd_make_debug_symbol, \ ++ NAME##_read_minisymbols, \ ++ NAME##_minisymbol_to_symbol ++ ++ long (*_bfd_get_symtab_upper_bound) (bfd *); ++ long (*_bfd_canonicalize_symtab) ++ (bfd *, struct bfd_symbol **); ++ struct bfd_symbol * ++ (*_bfd_make_empty_symbol) (bfd *); ++ void (*_bfd_print_symbol) ++ (bfd *, void *, struct bfd_symbol *, bfd_print_symbol_type); ++#define bfd_print_symbol(b,p,s,e) BFD_SEND (b, _bfd_print_symbol, (b,p,s,e)) ++ void (*_bfd_get_symbol_info) ++ (bfd *, struct bfd_symbol *, symbol_info *); ++#define bfd_get_symbol_info(b,p,e) BFD_SEND (b, _bfd_get_symbol_info, (b,p,e)) ++ bfd_boolean (*_bfd_is_local_label_name) (bfd *, const char *); ++ bfd_boolean (*_bfd_is_target_special_symbol) (bfd *, asymbol *); ++ alent * (*_get_lineno) (bfd *, struct bfd_symbol *); ++ bfd_boolean (*_bfd_find_nearest_line) ++ (bfd *, struct bfd_section *, struct bfd_symbol **, bfd_vma, ++ const char **, const char **, unsigned int *); ++ bfd_boolean (*_bfd_find_line) ++ (bfd *, struct bfd_symbol **, struct bfd_symbol *, ++ const char **, unsigned int *); ++ bfd_boolean (*_bfd_find_inliner_info) ++ (bfd *, const char **, const char **, unsigned int *); ++ /* Back-door to allow format-aware applications to create debug symbols ++ while using BFD for everything else. Currently used by the assembler ++ when creating COFF files. */ ++ asymbol * (*_bfd_make_debug_symbol) ++ (bfd *, void *, unsigned long size); ++#define bfd_read_minisymbols(b, d, m, s) \ ++ BFD_SEND (b, _read_minisymbols, (b, d, m, s)) ++ long (*_read_minisymbols) ++ (bfd *, bfd_boolean, void **, unsigned int *); ++#define bfd_minisymbol_to_symbol(b, d, m, f) \ ++ BFD_SEND (b, _minisymbol_to_symbol, (b, d, m, f)) ++ asymbol * (*_minisymbol_to_symbol) ++ (bfd *, bfd_boolean, const void *, asymbol *); ++ ++ /* Routines for relocs. */ ++#define BFD_JUMP_TABLE_RELOCS(NAME) \ ++ NAME##_get_reloc_upper_bound, \ ++ NAME##_canonicalize_reloc, \ ++ NAME##_bfd_reloc_type_lookup ++ ++ long (*_get_reloc_upper_bound) (bfd *, sec_ptr); ++ long (*_bfd_canonicalize_reloc) ++ (bfd *, sec_ptr, arelent **, struct bfd_symbol **); ++ /* See documentation on reloc types. */ ++ reloc_howto_type * ++ (*reloc_type_lookup) (bfd *, bfd_reloc_code_real_type); ++ ++ /* Routines used when writing an object file. */ ++#define BFD_JUMP_TABLE_WRITE(NAME) \ ++ NAME##_set_arch_mach, \ ++ NAME##_set_section_contents ++ ++ bfd_boolean (*_bfd_set_arch_mach) ++ (bfd *, enum bfd_architecture, unsigned long); ++ bfd_boolean (*_bfd_set_section_contents) ++ (bfd *, sec_ptr, const void *, file_ptr, bfd_size_type); ++ ++ /* Routines used by the linker. */ ++#define BFD_JUMP_TABLE_LINK(NAME) \ ++ NAME##_sizeof_headers, \ ++ NAME##_bfd_get_relocated_section_contents, \ ++ NAME##_bfd_relax_section, \ ++ NAME##_bfd_link_hash_table_create, \ ++ NAME##_bfd_link_hash_table_free, \ ++ NAME##_bfd_link_add_symbols, \ ++ NAME##_bfd_link_just_syms, \ ++ NAME##_bfd_final_link, \ ++ NAME##_bfd_link_split_section, \ ++ NAME##_bfd_gc_sections, \ ++ NAME##_bfd_merge_sections, \ ++ NAME##_bfd_is_group_section, \ ++ NAME##_bfd_discard_group, \ ++ NAME##_section_already_linked \ ++ ++ int (*_bfd_sizeof_headers) (bfd *, bfd_boolean); ++ bfd_byte * (*_bfd_get_relocated_section_contents) ++ (bfd *, struct bfd_link_info *, struct bfd_link_order *, ++ bfd_byte *, bfd_boolean, struct bfd_symbol **); ++ ++ bfd_boolean (*_bfd_relax_section) ++ (bfd *, struct bfd_section *, struct bfd_link_info *, bfd_boolean *); ++ ++ /* Create a hash table for the linker. Different backends store ++ different information in this table. */ ++ struct bfd_link_hash_table * ++ (*_bfd_link_hash_table_create) (bfd *); ++ ++ /* Release the memory associated with the linker hash table. */ ++ void (*_bfd_link_hash_table_free) (struct bfd_link_hash_table *); ++ ++ /* Add symbols from this object file into the hash table. */ ++ bfd_boolean (*_bfd_link_add_symbols) (bfd *, struct bfd_link_info *); ++ ++ /* Indicate that we are only retrieving symbol values from this section. */ ++ void (*_bfd_link_just_syms) (asection *, struct bfd_link_info *); ++ ++ /* Do a link based on the link_order structures attached to each ++ section of the BFD. */ ++ bfd_boolean (*_bfd_final_link) (bfd *, struct bfd_link_info *); ++ ++ /* Should this section be split up into smaller pieces during linking. */ ++ bfd_boolean (*_bfd_link_split_section) (bfd *, struct bfd_section *); ++ ++ /* Remove sections that are not referenced from the output. */ ++ bfd_boolean (*_bfd_gc_sections) (bfd *, struct bfd_link_info *); ++ ++ /* Attempt to merge SEC_MERGE sections. */ ++ bfd_boolean (*_bfd_merge_sections) (bfd *, struct bfd_link_info *); ++ ++ /* Is this section a member of a group? */ ++ bfd_boolean (*_bfd_is_group_section) (bfd *, const struct bfd_section *); ++ ++ /* Discard members of a group. */ ++ bfd_boolean (*_bfd_discard_group) (bfd *, struct bfd_section *); ++ ++ /* Check if SEC has been already linked during a reloceatable or ++ final link. */ ++ void (*_section_already_linked) (bfd *, struct bfd_section *); ++ ++ /* Routines to handle dynamic symbols and relocs. */ ++#define BFD_JUMP_TABLE_DYNAMIC(NAME) \ ++ NAME##_get_dynamic_symtab_upper_bound, \ ++ NAME##_canonicalize_dynamic_symtab, \ ++ NAME##_get_synthetic_symtab, \ ++ NAME##_get_dynamic_reloc_upper_bound, \ ++ NAME##_canonicalize_dynamic_reloc ++ ++ /* Get the amount of memory required to hold the dynamic symbols. */ ++ long (*_bfd_get_dynamic_symtab_upper_bound) (bfd *); ++ /* Read in the dynamic symbols. */ ++ long (*_bfd_canonicalize_dynamic_symtab) ++ (bfd *, struct bfd_symbol **); ++ /* Create synthetized symbols. */ ++ long (*_bfd_get_synthetic_symtab) ++ (bfd *, long, struct bfd_symbol **, long, struct bfd_symbol **, ++ struct bfd_symbol **); ++ /* Get the amount of memory required to hold the dynamic relocs. */ ++ long (*_bfd_get_dynamic_reloc_upper_bound) (bfd *); ++ /* Read in the dynamic relocs. */ ++ long (*_bfd_canonicalize_dynamic_reloc) ++ (bfd *, arelent **, struct bfd_symbol **); ++ ++ /* Opposite endian version of this target. */ ++ const struct bfd_target * alternative_target; ++ ++ /* Data for use by back-end routines, which isn't ++ generic enough to belong in this structure. */ ++ const void *backend_data; ++ ++} bfd_target; ++ ++bfd_boolean bfd_set_default_target (const char *name); ++ ++const bfd_target *bfd_find_target (const char *target_name, bfd *abfd); ++ ++const char ** bfd_target_list (void); ++ ++const bfd_target *bfd_search_for_target ++ (int (*search_func) (const bfd_target *, void *), ++ void *); ++ ++/* Extracted from format.c. */ ++bfd_boolean bfd_check_format (bfd *abfd, bfd_format format); ++ ++bfd_boolean bfd_check_format_matches ++ (bfd *abfd, bfd_format format, char ***matching); ++ ++bfd_boolean bfd_set_format (bfd *abfd, bfd_format format); ++ ++const char *bfd_format_string (bfd_format format); ++ ++/* Extracted from linker.c. */ ++bfd_boolean bfd_link_split_section (bfd *abfd, asection *sec); ++ ++#define bfd_link_split_section(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_link_split_section, (abfd, sec)) ++ ++void bfd_section_already_linked (bfd *abfd, asection *sec); ++ ++#define bfd_section_already_linked(abfd, sec) \ ++ BFD_SEND (abfd, _section_already_linked, (abfd, sec)) ++ ++/* Extracted from simple.c. */ ++bfd_byte *bfd_simple_get_relocated_section_contents ++ (bfd *abfd, asection *sec, bfd_byte *outbuf, asymbol **symbol_table); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +--- /dev/null ++++ b/arch/ia64/include/asm/kdb.h +@@ -0,0 +1,50 @@ ++#ifndef _ASM_KDB_H ++#define _ASM_KDB_H ++ ++/* ++ * Kernel Debugger Architecture Dependent Global Headers ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2008 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * KDB_ENTER() is a macro which causes entry into the kernel ++ * debugger from any point in the kernel code stream. If it ++ * is intended to be used from interrupt level, it must use ++ * a non-maskable entry method. ++ */ ++#include /* break numbers are separated for CONFIG_KDB_LOCK */ ++#define __KDB_ENTER2(b) asm("\tbreak.m "#b"\n") ++#define __KDB_ENTER1(b) __KDB_ENTER2(b) ++#define KDB_ENTER() do {if (kdb_on && !KDB_IS_RUNNING()) { __KDB_ENTER1(KDB_BREAK_ENTER); }} while(0) ++#define KDB_ENTER_SLAVE() do {if (kdb_on) { __KDB_ENTER1(KDB_BREAK_ENTER_SLAVE); }} while(0) ++ ++ /* ++ * Needed for exported symbols. ++ */ ++typedef unsigned long kdb_machreg_t; ++ ++#define kdb_machreg_fmt "0x%lx" ++#define kdb_machreg_fmt0 "0x%016lx" ++#define kdb_bfd_vma_fmt "0x%lx" ++#define kdb_bfd_vma_fmt0 "0x%016lx" ++#define kdb_elfw_addr_fmt "0x%lx" ++#define kdb_elfw_addr_fmt0 "0x%016lx" ++#define kdb_f_count_fmt "%ld" ++ ++static inline unsigned long ++kdba_funcptr_value(void *fp) ++{ ++ /* ia64 function descriptor, first word is address of code */ ++ return *(unsigned long *)fp; ++} ++ ++#ifdef CONFIG_SMP ++#define kdba_giveback_vector(vector) (0) ++#endif ++ ++#endif /* !_ASM_KDB_H */ +--- /dev/null ++++ b/arch/ia64/include/asm/kdb_break.h +@@ -0,0 +1,24 @@ ++#ifndef _ASM_KDB_BREAK_H ++#define _ASM_KDB_BREAK_H ++ ++/* ++ * Kernel Debugger Architecture Dependent Global Headers ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * Break numbers are used by CONFIG_KDB_LOCK code. They need to be seperated ++ * from asm/kdb.h to let spinlock code build without pulling in all of the kdb ++ * headers. ++ */ ++ ++#define KDB_BREAK_BREAK 0x80100 /* kdb breakpoint in kernel */ ++#define KDB_BREAK_ENTER 0x80101 /* KDB_ENTER(), single event or monarch */ ++#define KDB_BREAK_ENTER_SLAVE 0x80102 /* KDB_ENTER_SLAVE(), concurrent slave events */ ++ ++#endif /* !_ASM_KDB_BREAK_H */ +--- /dev/null ++++ b/arch/ia64/include/asm/kdbprivate.h +@@ -0,0 +1,124 @@ ++#ifndef _ASM_KDBPRIVATE_H ++#define _ASM_KDBPRIVATE_H ++ ++/* ++ * Kernel Debugger Architecture Dependent Private Headers ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++/* Definition of an machine instruction. ++ * Takes care of VLIW processors like Itanium ++ */ ++ ++typedef struct { ++ unsigned long inst[2]; ++} kdb_machinst_t; ++ ++/* ++ * KDB_MAXBPT describes the total number of breakpoints ++ * supported by this architecure. ++ */ ++#define KDB_MAXBPT 16 ++ ++/* ++ * KDB_MAXHARDBPT describes the total number of hardware ++ * breakpoint registers that exist. ++ */ ++#define KDB_MAXHARDBPT 4 ++ ++/* ++ * Platform specific environment entries ++ */ ++#define KDB_PLATFORM_ENV "IDMODE=ia64", "BYTESPERWORD=4", "IDCOUNT=8" ++ ++/* ++ * Support for IA64 debug registers ++ */ ++typedef struct _kdbhard_bp { ++ kdb_machreg_t bph_reg; /* Register this breakpoint uses */ ++ ++ unsigned int bph_free:1; /* Register available for use */ ++ unsigned int bph_data:1; /* Data Access breakpoint */ ++ ++ unsigned int bph_write:1; /* Write Data breakpoint */ ++ unsigned int bph_mode:2; /* 0=inst, 1=write, 2=io, 3=read */ ++ unsigned int bph_length:2; /* 0=1, 1=2, 2=BAD, 3=4 (bytes) */ ++} kdbhard_bp_t; ++ ++#define getprsregs(regs) ((struct switch_stack *)regs -1) ++ ++/* bkpt support using break inst instead of IBP reg */ ++ ++/* ++ * Define certain specific instructions ++ */ ++#define BREAK_INSTR (long)(KDB_BREAK_BREAK << (5+6)) ++#define INST_SLOT0_MASK (0x1ffffffffffL << 5) ++ ++#define BKPTMODE_DATAR 3 ++#define BKPTMODE_IO 2 ++#define BKPTMODE_DATAW 1 ++#define BKPTMODE_INST 0 ++ ++/* Some of the fault registers needed by kdb but not passed with ++ * regs or switch stack. ++ */ ++typedef struct fault_regs { ++ unsigned long isr ; ++ unsigned long ifa ; ++ unsigned long iim ; ++ unsigned long itir ; ++} fault_regs_t ; ++ ++/* ++ * Support for setjmp/longjmp ++ */ ++ ++/* __jmp_buf definition copied from libc/sysdeps/unix/sysv/linux/ia64/bits/setjmp.h */ ++ ++#define _JBLEN 70 ++ ++typedef struct __kdb_jmp_buf { ++ unsigned long __jmp_buf[_JBLEN]; ++} kdb_jmp_buf __attribute__ ((aligned (16))); ++ ++extern int kdba_setjmp(kdb_jmp_buf *); ++extern void kdba_longjmp(kdb_jmp_buf *, int); ++#define kdba_setjmp kdba_setjmp ++ ++extern kdb_jmp_buf *kdbjmpbuf; ++ ++/* Arch specific data saved for running processes */ ++ ++struct kdba_running_process { ++ struct switch_stack *sw; ++}; ++ ++extern void kdba_save_running(struct kdba_running_process *, struct pt_regs *); ++extern void kdba_unsave_running(struct kdba_running_process *, struct pt_regs *); ++ ++/* kdba wrappers which want to save switch stack will call unw_init_running(). ++ * That routine only takes a void* so pack the interrupt data into a structure. ++ */ ++ ++#include /* for irqreturn_t */ ++ ++enum kdba_serial_console { ++ KDBA_SC_NONE = 0, ++ KDBA_SC_STANDARD, ++ KDBA_SC_SGI_L1, ++}; ++ ++extern enum kdba_serial_console kdba_serial_console; ++ ++#define KDB_RUNNING_PROCESS_ORIGINAL kdb_running_process_save ++extern struct kdb_running_process *kdb_running_process_save; /* [NR_CPUS] */ ++ ++extern void kdba_wait_for_cpus(void); ++ ++#endif /* !_ASM_KDBPRIVATE_H */ +--- a/arch/ia64/include/asm/kregs.h ++++ b/arch/ia64/include/asm/kregs.h +@@ -72,7 +72,7 @@ + /* A mask of PSR bits that we generally don't want to inherit across a clone2() or an + execve(). Only list flags here that need to be cleared/set for BOTH clone2() and + execve(). */ +-#define IA64_PSR_BITS_TO_CLEAR (IA64_PSR_MFL | IA64_PSR_MFH | IA64_PSR_DB | IA64_PSR_LP | \ ++#define IA64_PSR_BITS_TO_CLEAR (IA64_PSR_MFL | IA64_PSR_MFH | IA64_PSR_LP | \ + IA64_PSR_TB | IA64_PSR_ID | IA64_PSR_DA | IA64_PSR_DD | \ + IA64_PSR_SS | IA64_PSR_ED | IA64_PSR_IA) + #define IA64_PSR_BITS_TO_SET (IA64_PSR_DFH | IA64_PSR_SP) +--- /dev/null ++++ b/arch/ia64/kdb/ChangeLog +@@ -0,0 +1,1111 @@ ++2008-11-26 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc6-ia64-1. ++ ++2008-11-12 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc4-ia64-1. ++ ++2008-11-04 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc3-ia64-1. ++ ++2008-10-29 Jay Lan ++ ++ * "Commandeer vector 0xfe for KDB_VECTOR", version 2. ++ Cliff Wickman ++ * kdb-v4.4-2.6.28-rc2-ia64-2. ++ ++2008-10-27 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc2-ia64-1. ++ ++2008-10-20 Jay Lan ++ ++ * kdb-v4.4-2.6.27-ia64-1. ++ ++2008-09-30 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc8-ia64-1. ++ ++2008-09-22 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc7-ia64-1. ++ ++2008-09-03 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc5-ia64-1. ++ ++2008-08-19 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc3-ia64-1. ++ ++2008-08-15 Jay Lan ++ ++ * Fix a problem that slave cpus panic'ed during NMI. ++ Jay Lan ++ * kdb-v4.4-2.6.27-rc2-ia64-2.1. ++ ++2008-08-14 Jay Lan ++ ++ * Support 'kdump' command to take a kdump vmcore from KDB, ++ Dan Aloni (da-x@monatomic.org), ++ Jason Xiao (jidong.xiao@gmail.com), ++ Jay Lan (jlan@sgi.com) ++ * kdb-v4.4-2.6.27-rc2-ia64-2. ++ ++2008-08-06 Jay Lan ++ ++ * Fix up the NULL pointer deference issue in ohci_kdb_poll_char, ++ Jason Xiao ++ * kdb-v4.4-2.6.27-rc2-ia64-1. ++ ++2008-07-18 Jay Lan ++ ++ * support Hardware Breakpoint (bph/bpha) commands ++ IA64: Greg Banks ++ X86: Konstantin Baydarov ++ * kdb-v4.4-2.6.26-ia64-2. ++ ++2008-07-14 Jay Lan ++ ++ * kdb-v4.4-2.6.26-ia64-1. ++ ++2008-07-11 Jay Lan ++ ++ * New commands and some fixups and enhancements, ++ Joe Korty ++ John Blackwood ++ Jim Houston ++ - Use the non-sleeping copy_from_user_atomic. ++ - Enhance kdb_cmderror diagnostic output. ++ - Expand the KDB 'duplicate command' error message. ++ - Touch NMI watchdog in various KDB busy-loops. ++ - Support IMB HS20 Blade 8843 platform. ++ - Display exactly which cpus needed an NMI to get them into kdb. ++ - Better document that kdb's 'ps A' command can be used to show ++ _all_ processes and threads ++ - Suppress KDB boottime INFO messages if quiet boot. ++ - Add a KDB breakpoint to the OOPs path. ++ - Add CONFIG_DISCONTIGMEM support to kdbm_memmap. ++ - Extend the KDB task command to handle CONFIG_NUMA fields. ++ - Extend the KDB vm command to support NUMA stuff. ++ - Create the KDB mempolicy command. ++ - Create a pgdat command for KDB. ++ - Fix a hang on boot on some i386 systems. ++ * kdb-v4.4-2.6.26-rc9-ia64-1. ++ ++2008-06-30 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc8-ia64-1. ++ ++2008-06-25 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc7-ia64-1. ++ ++2008-06-06 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc5-ia64-1. ++ ++2008-05-30 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc4-ia64-1. ++ ++2008-05-20 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc3-ia64-1. ++ ++2008-05-13 Jay Lan ++ ++ * XPC support removed from KDB due to XPC changes to 2.6.26-rc1. ++ * kdb-v4.4-2.6.26-rc1-ia64-1. ++ ++2008-04-17 Jay Lan ++ ++ * kdb-v4.4-2.6.25-ia64-1. ++ ++2008-03-16 Jay Lan ++ ++ * kdb-v4.4-2.6.25-rc6-ia64-1. ++ ++2008-03-03 Jay Lan ++ ++ * kdb-v4.4-2.6.25-rc3-ia64-1. ++ ++2008-02-26 Jay Lan ++ ++ * kdb-v4.4-2.6.25-rc2-ia64-1. ++ ++2008-02-19 Jay Lan ++ ++ * kdb-v4.4-2.6.25-rc1-ia64-1. ++ ++2008-02-01 Jay Lan ++ ++ * Backed out USB UHCI support since it caused dropped characters and ++ broke OHCI. ++ * Restored "archkdbcommon" commands for x86. It was lost at the x86 ++ merge. ++ * Detecting if the HC was "busy", Aaron Young ++ * kdb-v4.4-2.6.24-ia64-2. ++ ++2008-01-29 Jay Lan ++ ++ * kdb-v4.4-2.6.24-ia64-1. ++ ++2008-01-22 Jay Lan ++ ++ * USB UHCI kdb support, Konstantin Baydarov ++ * kdb-v4.4-2.6.24-rc8-ia64-3. ++ ++2008-01-18 Jay Lan ++ ++ * USB EHCI kdb support, Aaron Young ++ * kdb-v4.4-2.6.24-rc8-ia64-2. ++ ++2008-01-18 Jay Lan ++ ++ * kdb-v4.4-2.6.24-rc8-ia64-1. ++ ++2008-01-07 Jay Lan ++ ++ * kdb-v4.4-2.6.24-rc7-ia64-1. ++ ++2007-12-21 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc6-ia64-1. ++ ++2007-12-12 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc5-ia64-1. ++ ++2007-12-05 Jay Lan ++ ++ * Fixed a 'sysctl table check failed' problem. ++ * kdb v4.4-2.6.24-rc4-ia64-1. ++ ++2007-11-26 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc3-ia64-1. ++ ++2007-11-13 Jay Lan ++ ++ * Back ported "New KDB USB interface" from Aaron Young in ++ v4.4-2.6.23-ia64-2 to 2.6.24 kdb patchset. ++ * kdb v4.4-2.6.24-rc2-ia64-2. ++ ++2007-11-12 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc2-ia64-1. ++ ++2007-11-09 Jay Lan ++ ++ * Rebase to 2.6.24-rc1 kernel ++ * - merged kdb-v4.4-2.6.23-i386-1 and kdb-v4.4-2.6.23-x86_64-1 ++ * into kdb-v4.4-2.6.24-rc1-x86-1 ++ * - Fields "done", "sglist_len", and "pid" are removed from ++ * struct scsi_cmnd. Thus, these fields are no longer displayed ++ * on "sc" command. ++ * kdb v4.4-2.6.24-rc1-ia64-1. ++ ++2007-11-08 Jay Lan ++ ++ * New KDB USB interface, Aaron Young ++ * 1. This patch allows KDB to work with any Host Contoller driver ++ * and call the correct HC driver poll routine (as long as the ++ * HC driver provides a .kdb_poll_char routine via it's ++ * associated hc_driver struct). ++ * 2. Hotplugged keyboards are now recognized by KDB. ++ * 3. Currently KDB can only make use of 1 USB type keyboard. ++ * New code can handle up to 8 attached keyboards - input is ++ * multiplexed from all of them while in kdb. ++ * kdb v4.4-2.6.23-ia64-2. ++ ++2007-10-24 Jay Lan ++ ++ * kdb v4.4-2.6.23-ia64-1. ++ ++2007-09-26 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc8-ia64-1. ++ ++2007-09-21 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc7-ia64-1. ++ ++2007-09-19 Jay Lan ++ ++ * Get into KDB successfully if multiple cpus are in MCA. ++ * kdb v4.4-2.6.23-rc6-ia64-2. ++ ++2007-09-12 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc6-ia64-1. ++ ++2007-09-06 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc5-ia64-1. ++ ++2007-08-30 Keith Owens ++ ++ * New i386/x86_64 backtrace requires that kdb_save_running() does not ++ exit until after kdb_main_loop() has completed. ++ * kdb v4.4-2.6.23-rc4-ia64-2. ++ ++2007-08-30 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc4-ia64-1. ++ ++2007-08-24 Keith Owens ++ ++ * kdb v4.4-2.6.23-rc3-ia64-1. ++ ++2007-08-07 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc2-ia64-1. ++ ++2007-07-30 Keith Owens ++ ++ * kdb v4.4-2.6.23-rc1-ia64-1. ++ ++2007-07-09 Keith Owens ++ ++ * kdb v4.4-2.6.22-ia64-1. ++ ++2007-07-02 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc7-ia64-1. ++ ++2007-06-20 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc5-ia64-1. ++ ++2007-06-08 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc4-ia64-1. ++ ++2007-05-28 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc3-ia64-1. ++ ++2007-05-22 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc2-ia64-1. ++ ++2007-05-22 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc1-ia64-1. ++ ++2007-04-29 Keith Owens ++ ++ * kdb v4.4-2.6.21-ia64-1. ++ ++2007-04-16 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc7-ia64-1. ++ ++2007-04-10 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc6-ia64-1. ++ ++2007-04-02 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc5-ia64-1. ++ ++2007-03-19 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc4-ia64-1. ++ ++2007-03-14 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc3-ia64-1. ++ ++2007-03-14 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc2-ia64-1. ++ ++2007-03-01 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc1-ia64-1. ++ ++2007-03-01 Keith Owens ++ ++ * Remove sparse warnings. ++ * kdb v4.4-2.6.20-ia64-3. ++ ++2007-02-16 Keith Owens ++ ++ * Initialise variable bits of struct disassemble_info each time. ++ * kdb v4.4-2.6.20-ia64-2. ++ ++2007-02-06 Keith Owens ++ ++ * kdb v4.4-2.6.20-ia64-1. ++ ++2007-02-01 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc7-ia64-1. ++ ++2007-01-08 Keith Owens ++ ++ * Detect calls via PLT and decode the target address. ++ * kdb v4.4-2.6.20-rc4-ia64-2. ++ ++2007-01-08 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc4-ia64-1. ++ ++2007-01-02 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc3-ia64-1. ++ ++2006-12-20 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc1-ia64-1. ++ ++2006-12-07 Keith Owens ++ ++ * Export kdba_dumpregs. ++ * kdb v4.4-2.6.19-ia64-2. ++ ++2006-11-30 Keith Owens ++ ++ * kdb v4.4-2.6.19-ia64-1. ++ ++2006-11-27 Keith Owens ++ ++ * Only use VT keyboard if the command line allows it and ACPI indicates ++ that there is an i8042. ++ * kdb v4.4-2.6.19-rc6-ia64-2. ++ ++2006-11-20 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc6-ia64-1. ++ ++2006-11-09 Keith Owens ++ ++ * Only use VT console if the command line allows it. ++ * kdb v4.4-2.6.19-rc5-ia64-2. ++ ++2006-11-08 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc5-ia64-1. ++ ++2006-11-01 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc4-ia64-1. ++ ++2006-10-24 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc3-ia64-1. ++ ++2006-10-24 Keith Owens ++ ++ * Remove redundant regs and envp parameters. ++ * kdb v4.4-2.6.19-rc2-ia64-2. ++ ++2006-10-18 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc2-ia64-1. ++ ++2006-10-09 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc1-ia64-1. ++ ++2006-10-06 Keith Owens ++ ++ * Remove #include ++ * kdb v4.4-2.6.18-ia64-2. ++ ++2006-09-20 Keith Owens ++ ++ * kdb v4.4-2.6.18-ia64-1. ++ ++2006-09-15 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc7-ia64-1. ++ ++2006-08-29 Keith Owens ++ ++ * Rewrite all backtrace code. ++ * kdb v4.4-2.6.18-rc5-ia64-2. ++ ++2006-08-28 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc5-ia64-1. ++ ++2006-08-08 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc4-ia64-1. ++ ++2006-08-04 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc3-ia64-1. ++ ++2006-07-18 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc2-ia64-1. ++ ++2006-07-12 Keith Owens ++ ++ * Remove dead KDB_REASON codes. ++ * sparse cleanups. ++ * kdb v4.4-2.6.18-rc1-ia64-2. ++ ++2006-07-07 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc1-ia64-1. ++ ++2006-07-04 Keith Owens ++ ++ * Delete kdba_enable_lbr, kdba_disable_lbr, kdba_print_lbr, ++ page_fault_mca. Only ever implemented on x86, difficult to maintain ++ and rarely used in the field. ++ * Replace #ifdef KDB_HAVE_LONGJMP with #ifdef kdba_setjmp. ++ * kdb v4.4-2.6.17-ia64-2. ++ ++2006-06-19 Keith Owens ++ ++ * kdb v4.4-2.6.17-ia64-1. ++ ++2006-05-25 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc5-ia64-1. ++ ++2006-05-15 Keith Owens ++ ++ * Refresh bfd related files from binutils 2.16.91.0.2. ++ * kdb v4.4-2.6.17-rc4-ia64-2. ++ ++2006-05-12 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc4-ia64-1. ++ ++2006-04-28 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc3-ia64-1. ++ ++2006-04-22 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc2-ia64-1. ++ ++2006-04-11 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc1-ia64-1. ++ ++2006-03-30 Keith Owens ++ ++ * Change CONFIG_LKCD to CONFIG_LKCD_DUMP. ++ * kdb v4.4-2.6.16-ia64-3. ++ ++2006-03-24 Keith Owens ++ ++ * Use INIT to interrupt cpus that do not respond to a normal kdb IPI. ++ * Remove KDBA_MCA_TRACE from arch/ia64/kernel/mca.c. ++ * kdb v4.4-2.6.16-ia64-2. ++ ++2006-03-21 Keith Owens ++ ++ * kdb v4.4-2.6.16-ia64-1. ++ ++2006-03-14 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc6-ia64-1. ++ ++2006-02-28 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc5-ia64-1. ++ ++2006-02-20 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc4-ia64-1. ++ ++2006-02-07 Keith Owens ++ ++ * Change kdb_running_process_save from a static array to a pointer. ++ gcc 4.0 objects to forward declarations for arrays with an incomplete ++ type. ++ * kdb v4.4-2.6.16-rc2-ia64-3. ++ ++2006-02-06 Keith Owens ++ ++ * Change CONFIG_CRASH_DUMP to CONFIG_LKCD. ++ * kdb v4.4-2.6.16-rc2-ia64-2. ++ ++2006-02-06 Keith Owens ++ ++ * kdb v4.4-2.6.16-rc2-ia64-1. ++ ++2006-02-01 Keith Owens ++ ++ * Handlers: check that the task is in kernel space before looking at ++ the thread_info bits. ++ * Expose kdb_running_process_save[] so 'pid R' can get the original ++ process, even when the MCA/INIT handlers are being used. ++ * kdb v4.4-2.6.16-rc1-ia64-3. ++ ++2006-01-19 Keith Owens ++ ++ * Add back some kdb changes to xpc_main that were lost due to a patch ++ conflict. ++ * kdb v4.4-2.6.16-rc1-ia64-2. ++ ++2006-01-18 Keith Owens ++ ++ * kdb v4.4-2.6.16-rc1-ia64-1. ++ ++2006-01-10 Keith Owens ++ ++ * Build kdba_pod for generic as well as sn2 kernels and test at run ++ time if the platform is sn2. ++ * kdb v4.4-2.6.15-ia64-3. ++ ++2006-01-08 Keith Owens ++ ++ * Convert xpc to use DIE_KDEBUG_ENTER and DIE_KDEBUG_LEAVE. ++ * Add debug option for xpc. ++ * break.b always sets a debug trap number of 0 , so pass that to kdb as ++ well as the normal kdb traaps. ++ * kdb v4.4-2.6.15-ia64-2. ++ ++2006-01-04 Keith Owens ++ ++ * Remove some inlines and the last vestige of CONFIG_NUMA_REPLICATE. ++ * Read the keyboard acknowledgment after sending a character. SuSE ++ Bugzilla 60240. ++ * kdb v4.4-2.6.15-ia64-1. ++ ++2005-12-25 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc7-ia64-1. ++ ++2005-12-20 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc6-ia64-1. ++ ++2005-12-06 Keith Owens ++ ++ * Use RECOVERY flag in MCA handler. ++ * kdb v4.4-2.6.15-rc5-ia64-2. ++ ++2005-12-05 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc5-ia64-1. ++ ++2005-12-02 Keith Owens ++ ++ * Reinstate hook for debug trap, the patch chunk was accidentally ++ dropped in 2.6.15-rc1. ++ * kdb v4.4-2.6.15-rc4-ia64-1. ++ ++2005-11-30 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc3-ia64-1. ++ ++2005-11-21 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc2-ia64-1. ++ ++2005-11-15 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc1-ia64-1. ++ ++2005-10-28 Keith Owens ++ ++ * kdb v4.4-2.6.14-ia64-1. ++ ++2005-10-21 Keith Owens ++ ++ * kdb v4.4-2.6.14-rc5-ia64-1. ++ ++2005-10-11 Keith Owens ++ ++ * Handle removal of USB keyboard. Aaron Young, SGI ++ * kdb v4.4-2.6.14-rc4-ia64-1. ++ ++2005-10-04 Keith Owens ++ ++ * kdb v4.4-2.6.14-rc3-ia64-1. ++ ++2005-09-21 Keith Owens ++ ++ * Support kdb_current_task in register display and modify commands. ++ * kdb v4.4-2.6.14-rc2-ia64-1. ++ ++2005-09-20 Keith Owens ++ ++ * Coexist with kprobes. ++ * Coexist with MCA/INIT rewrite. ++ * Add KDB_ENTER_SLAVE to handle concurrent entry to kdb from multiple ++ cpus. ++ * Add handlers command to control whether the MCA/INIT task or the ++ original task is displayed. ++ * Namespace clean up, remove unused kdba_sw_interrupt. ++ * kdb v4.4-2.6.14-rc1-ia64-1. ++ ++2005-08-29 Keith Owens ++ ++ * kdb v4.4-2.6.13-ia64-1. ++ ++2005-08-24 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc7-ia64-1. ++ ++2005-08-08 Keith Owens ++ ++ * Add minstate command. ++ * kdb v4.4-2.6.13-rc6-ia64-1. ++ ++2005-08-02 Keith Owens ++ ++ * Replace hard coded kdb declarations with #include . ++ * kdb v4.4-2.6.13-rc5-ia64-1. ++ ++2005-07-30 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc4-ia64-1. ++ ++2005-07-22 Keith Owens ++ ++ * Handle INIT delivered while in physical mode. ++ * kdb v4.4-2.6.13-rc3-ia64-2. ++ ++2005-07-19 Keith Owens ++ ++ * Add support for USB keyboard (OHCI only). Aaron Young, SGI. ++ * kdb v4.4-2.6.13-rc3-ia64-1. ++ ++2005-07-08 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc2-ia64-1. ++ ++2005-07-01 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc1-ia64-1. ++ ++2005-06-18 Keith Owens ++ ++ * Standard IA64 code now works around break.b setting cr.iim to 0 ++ instead of the break number. Remove the kdb workaround. ++ * kdb v4.4-2.6.12-ia64-1. ++ ++2005-06-08 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc6-ia64-1. ++ ++2005-05-25 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc5-ia64-1. ++ ++2005-05-24 Keith Owens ++ ++ * break.b sets cr.iim to 0 instead of the break number. Deal with it. ++ * kdb v4.4-2.6.12-rc4-ia64-3. ++ ++2005-05-14 Keith Owens ++ ++ * Correct MCA path after calling kdba_mca_bspstore_fixup(). ++ Mark Larson, SGI. ++ * Tell the user that MCA/INIT is recoverable so kdb is not entered. ++ * kdb v4.4-2.6.12-rc4-ia64-2. ++ ++2005-05-08 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc4-ia64-1. ++ ++2005-04-21 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc3-ia64-1. ++ ++2005-04-06 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc2-ia64-1. ++ ++2005-04-04 Keith Owens ++ ++ * More tweaks to cope with invalid old bspstore in MCA handler. ++ * kdb v4.4-2.6.12-rc1-ia64-2. ++ ++2005-03-29 Keith Owens ++ ++ * Replace __copy_to_user with __copy_to_user_inatomic. ++ * MCA handler, do not use old_bspstore if it is in region 4 or below. ++ * kdb v4.4-2.6.12-rc1-ia64-1. ++ ++2005-03-08 Keith Owens ++ ++ * Coexistence patches for lkcd. Jason Uhlenkott, SGI. ++ * kdb v4.4-2.6.11-ia64-2. ++ ++2005-03-03 Keith Owens ++ ++ * kdb-v4.4-2.6.11-ia64-1. ++ ++2005-02-14 Keith Owens ++ ++ * kdb-v4.4-2.6.11-rc4-ia64-1. ++ ++2005-02-08 Keith Owens ++ ++ * kdb-v4.4-2.6.11-rc3-bk4-ia64-1. ++ ++2005-02-03 Keith Owens ++ ++ * kdb-v4.4-2.6.11-rc3-ia64-1. ++ ++2005-01-27 Keith Owens ++ ++ * kdb-v4.4-2.6.11-rc2-ia64-1. ++ ++2005-01-20 Keith Owens ++ ++ * MCA and INIT stacks moved to per-cpu area. ++ * kdb-v4.4-2.6.11-rc1-bk7-ia64-1. ++ ++2005-01-12 Keith Owens ++ ++ * ia64_spinlock_contention_pre3_4_end is in base kernel, remove from kdb. ++ * Use last ditch allocator if unwind cannot allocate memory. ++ * kdb-v4.4-2.6.11-rc1-ia64-1. ++ ++2004-12-25 Keith Owens ++ ++ * Add cpuinfo command. ++ * kdb-v4.4-2.6.10-ia64-1. ++ ++2004-12-07 Keith Owens ++ ++ * Clean up error path in kdba_mca_init. ++ * kdb-v4.4-2.6.10-rc3-ia64-1. ++ ++2004-11-15 Keith Owens ++ ++ * kdb-v4.4-2.6.10-rc2-ia64-1. ++ ++2004-10-29 Keith Owens ++ ++ * kdb-v4.4-2.6.10-rc1-ia64-1. ++ ++2004-10-19 Keith Owens ++ ++ * kdb-v4.4-2.6.9-ia64-1. ++ ++2004-10-12 Keith Owens ++ ++ * kdb-v4.4-2.6.9-rc4-ia64-1. ++ ++2004-10-01 Keith Owens ++ ++ * kdb-v4.4-2.6.9-rc3-ia64-1. ++ ++2004-09-30 Keith Owens ++ ++ * Add stackdepth command. ++ * kdb-v4.4-2.6.9-rc2-ia64-3. ++ ++2004-09-16 Keith Owens ++ ++ * Fixes for current in region 5 instead of 7 (idle task on cpu 0). ++ * kdb-v4.4-2.6.9-rc2-ia64-2. ++ ++2004-09-14 Keith Owens ++ ++ * kdb-v4.4-2.6.9-rc2-ia64-1. ++ ++2004-08-27 Keith Owens ++ ++ * kdb-v4.4-2.6.9-rc1-ia64-1. ++ ++2004-08-14 Keith Owens ++ ++ * kdb-v4.4-2.6.8-ia64-1. ++ ++2004-08-12 Keith Owens ++ ++ * kdb-v4.4-2.6.8-rc4-ia64-1. ++ ++2004-08-04 Keith Owens ++ ++ * kdb-v4.4-2.6.8-rc3-ia64-1. ++ ++2004-07-18 Keith Owens ++ ++ * New config name for SN serial console. ++ * kdb-v4.4-2.6.8-rc2-ia64-1. ++ ++2004-07-12 Keith Owens ++ ++ * kdb-v4.4-2.6.8-rc1-ia64-1. ++ ++2004-06-30 Keith Owens ++ ++ * kdb-v4.4-2.6.7-ia64-040629-1. ++ ++2004-06-16 Keith Owens ++ ++ * Coexist with 2.6.7-ia64-040619. ++ * kdb-v4.4-2.6.7-ia64-040619-1. ++ ++2004-06-16 Keith Owens ++ ++ * kdb v4.4-2.6.7-ia64-1. ++ ++2004-06-10 Keith Owens ++ ++ * kdb v4.4-2.6.7-rc3-ia64-1. ++ ++2004-06-09 Keith Owens ++ ++ * Namespace clean up. Mark code/variables as static when it is only ++ used in one file, delete dead code/variables. ++ * Saved interrupt state requires long, not int. ++ * kdb v4.4-2.6.7-rc2-ia64-3. ++ ++2004-06-08 Keith Owens ++ ++ * Whitespace clean up, no code changes. ++ * kdb v4.4-2.6.7-rc2-2. ++ ++2004-06-07 Keith Owens ++ ++ * Force KALLSYMS and KALLSYMS_ALL for CONFIG_KDB. ++ * kdb v4.4-2.6.7-rc2-1. ++ ++2004-06-06 Keith Owens ++ ++ * Add standard archkdb commands. ++ * Move kdb_{get,put}userarea_size definitions to linux/kdb.h. ++ * kdb v4.4-2.6.6-ia64-040521-2. ++ ++2004-05-25 Keith Owens ++ ++ * Update Kconfig text. ++ * kdb v4.4-2.6.6-ia64-040521-1. ++ ++2004-05-23 Keith Owens ++ ++ * Move bfd.h and ansidecl.h from arch/$(ARCH)/kdb to include/asm-$(ARCH). ++ * ia64-opc.c needs kdbprivate.h after common reorganisation. ++ * Update copyright notices. ++ * kdb v4.4-2.6.6-ia64-1. ++ ++2004-05-60 Keith Owens ++ ++ * kdb v4.3-2.6.6-rc3-ia64-1. ++ ++2004-05-60 Keith Owens ++ ++ * Tweak WAR for backtrace through contended spinlocks. ++ * kdb v4.3-2.6.6-rc2-ia64-1. ++ ++2004-04-30 Keith Owens ++ ++ * kdb v4.3-2.6.6-rc1-ia64-1. ++ ++2004-04-15 Keith Owens ++ ++ * kdb v4.3-2.6.5-ia64-040413-1. ++ ++2004-03-06 Keith Owens ++ ++ * Use kdb_print for unwind debugging. ++ * kdb v4.3-2.6.4-rc2-ia64-1. ++ ++2004-02-29 Keith Owens ++ ++ * kdb v4.3-2.6.4-rc1-ia64-1. ++ ++2004-02-18 Keith Owens ++ ++ * kdb v4.3-2.6.3-ia64-1. ++ ++2004-02-17 Keith Owens ++ ++ * Reconcile 2.6-test versions from Xavier Bru (Bull), Greg Banks (SGI), ++ Jim Houston (Concurrent Computer Corp). ++ * Reconcile with kdb v4.3-2.4.23-ia64-0312??-1. ++ * Reconcile with salinfo changes. ++ * Port WAR for backtrace from spinlock contention from 2.4 to 2.6. ++ * Merge PGS FIFO tweak with SERIAL_IO_MEM and concurrent support for ++ multiple consoles (no USB consoles yet). ++ * Update pt_regs output to match the order of struct pt_regs. ++ * KDB wrappers for interrupts handlers now return the handler's return code. ++ * tpa and tpav commands from Anonymous. ++ * Reconcile with mca changes. ++ * Upgrade to 2.6.3-rc3. ++ * kdb v4.3-2.6.3-rc3-ia64-1. ++ ++2003-10-22 Xavier Bru ++ * Merge to 2.6.0-test7 ++2003-10-20 Philippe Garrigues ++ * Enable FIFO in UART ++2003-09-08 Xavier Bru ++ * Merge to 2.6.0-test4 ++2003-03-21 Xavier Bru ++ * Merge kdb v4.0 on 2.5.64 ia64 ++ * new kernel parameters support ++ * new kallsyms support ++ ++2003-10-24 Keith Owens ++ ++ * kdb v4.3-2.4.23-pre8-cset-1.1069.1.143-to-1.1108-ia64-1. ++ ++2003-10-03 Keith Owens ++ ++ * After MCA, copy the saved RSE registers from ia64_mca_bspstore to the ++ stack of the failing process. ++ * Abort backtrace when we hit IVT, no unwind data which confuses ++ unw_unwind(). ++ * Workaround for backtrace through spinlock contention called from leaf ++ functions. ++ * kdb v4.3-2.4.22-ia64-030909-1. ++ ++2003-07-20 Keith Owens ++ ++ * MCA rendezvous timeout affects kdb_wait_for_cpus_secs. ++ * Support SGI L1 console. ++ * kdb v4.3-2.4.21-ia64-030702-2. ++ ++2003-07-08 Keith Owens ++ ++ * print_symbol() in mca.c does something useful when kdb is installed. ++ * Unwind and SAL changes removed from kdb, they are in the base kernel. ++ * kdb v4.3-2.4.21-ia64-030702-1. ++ ++2003-06-20 Keith Owens ++ ++ * Add CONFIG_KDB_CONTINUE_CATASTROPHIC. ++ * Do not send IPI if the machine state does not require them. ++ * Correct definition of KDB_ENTER(). ++ * Workaround for broken init monarch handler. ++ * Monarch cpu must get to kdb, even if it was interrupted in user space. ++ * Unwind fixes. ++ * Generalize ia64_spinlock_contention name. ++ * Add kdba_fru for SN machines. ++ * Correct test for cpu number. ++ * kdb v4.3-2.4.20-ia64-020821-1. ++ ++2003-05-02 Keith Owens ++ ++ * Add kdba_fp_value(). ++ * Limit backtrace size to catch loops. ++ * Print spinlock name in ia64_spinlock_contention. ++ * Tweak INIT slave stack lock and handler. ++ * Add read/write access to user pages. Vamsi Krishna S., IBM ++ * Rename cpu_is_online to cpu_online, as in 2.5. ++ * Clean up USB keyboard support. ++ * Clean up serial console support. ++ * kdb v4.2-2.4.20-ia64-020821-1. ++ ++2003-04-04 Keith Owens ++ ++ * Add support for INIT slave interrupts. ++ * Tell SAL to always rendezvous on MCA. ++ * No lock on SAL rendezvous call. ++ * Include unwind.c from 2.4.21-pre5. ++ * Rename cpu_online to cpu_is_online. ++ * Workarounds for scheduler bugs. ++ * kdb v4.1-2.4.20-ia64-020821-1. ++ ++2003-03-16 Keith Owens ++ ++ * Each cpu saves its state as it enters kdb or before it enters code ++ which cannot call kdb, converting kdb from a pull to a push model. ++ * Clean up kdb interaction with CONFIG_SERIAL_CONSOLE. ++ * Removal of special cases for i386 backtrace from common code ++ simplifies the architecture code. ++ * Add support for MCA events (both main and rendezvous) plus INIT ++ monarch event. ++ * Correct decode of brl. ++ * Move kdba_print_nameval to common code. ++ * Generalize kdba unwind handlers. ++ * Fix decode of sal records (fix included in later ia64 kernels). ++ * Handle multiple pt_regs in stack (fix included in later ia64 kernels). ++ * Clean up debug code in unwind (fix included in later ia64 kernels). ++ * Move kdb break numbers to their own file so it can be used in asm. ++ * kdb v4.0-2.4.20-ia64-021210-1. ++ ++2003-02-03 Keith Owens ++ ++ * Register kdb commands early. ++ * Handle KDB_ENTER() when kdb=off. ++ * Optimize __kdba_getarea_size when width is a constant. ++ * Decode oops via kallsyms if it is available. ++ * Update copyright notices to 2003. ++ * Add commands to dump struct pt_regs and switch_stack. ++ * Handle padding from unw_init_running for switch_stack. ++ * Add dummy kdba_local_arch_setup/kdba_local_arch_cleanup. ++ * Warning for pod mode. ++ * Add command history and editing. Sonic Zhang. ++ * kdb_toggleled is conditional on KDB_BLINK_LED. Bernhard Fischer. ++ * Allow tab on serial line for symbol completion. ++ * Ignore KDB_ENTER() when kdb is already running. ++ * kdb v3.0-2.4.20-ia64-021210-1. ++ ++2003-01-23 Keith Owens ++ ++ * Upgrade to 2.4.20-ia64-021210. ++ * kdb v2.5-2.4.20-ia64-021210-1. ++ ++2002-11-14 Keith Owens ++ ++ * General clean up of handling for breakpoints and single stepping over ++ software breakpoints. ++ * kdb v2.5-2.4.19-ia64-020821-1. ++ ++2002-10-31 Keith Owens ++ ++ * Remove kdb_eframe_t. ++ * Sanity check if we have pt_regs. ++ * Remove kdba_getcurrentframe(). ++ * Comments for coexistence with O(1) scheduler. ++ * kdb v2.4-2.4.19-ia64-020821-1. ++ ++2002-10-15 Keith Owens ++ ++ * Minimize differences between patches for 2.4 and 2.5 kernels. ++ * kdb v2.3-2.4.19-ia64-020821-2. ++ ++2002-08-10 Keith Owens ++ ++ * Verify rw address for instruction breakpoint. ++ * Replace kdb_port with kdb_serial to support memory mapped I/O. ++ David Mosberger. ++ Note: This needs kdb v2.3-2.4.18-common-2 or later. ++ * kdb v2.3-2.4.18-ia64-020722-2. ++ ++2002-08-07 Keith Owens ++ ++ * Upgrade to 2.4.18-ia64-020722. ++ * Remove individual SGI copyrights, the general SGI copyright applies. ++ * Clean up disassembly layout. Hugh Dickins, Keith Owens. ++ * Remove fixed KDB_MAX_COMMANDS size. ++ * Add set_fs() around __copy_to_user on kernel addresses. ++ Randolph Chung. ++ * Position ia64 for CONFIG_NUMA_REPLICATE. ++ * Stacked registers modification support. Sebastien Lelarge. ++ * USB keyboard support. Sebastien Lelarge. ++ * kdb v2.3-2.4.18-ia64-020722-1. ++ ++2002-03-20 Keith Owens ++ ++ * Sync with 2.4.17-sn2. ++ * Add pod command. ++ ++2002-02-20 Keith Owens ++ ++ * Call kdb from mca handler. Jenna S. Hall, Intel. ++ * kdb v2.1-2.4.17-ia64-011226-2. ++ ++2002-01-18 Keith Owens ++ ++ * Replace kdb_get/putword with kdb_get/putarea functions. ++ * Wrap kdb references in #ifdef CONFIG_KDB. ++ * Delete sample i386 code. ++ * Refuse to update kernel text on NUMA systems. ++ * Reject hardware breakpoints, not supported yet. ++ * kdb v2.1-2.4.17-ia64-011226-1. ++ ++2002-01-07 Keith Owens ++ ++ * Split kdb for ia64 as kdb v2.0-2.4.17-ia64-011226-1. +--- /dev/null ++++ b/arch/ia64/kdb/Makefile +@@ -0,0 +1,21 @@ ++# ++# This file is subject to the terms and conditions of the GNU General Public ++# License. See the file "COPYING" in the main directory of this archive ++# for more details. ++# ++# Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++# ++ ++obj-y := kdba_bt.o kdba_bp.o kdba_io.o kdba_support.o \ ++ cpu-ia64-opc.o ia64-dis.o ia64-opc.o kdba_id.o kdba_jmp.o ++ ++# fru does not compile on 2.6. ++# obj-$(CONFIG_IA64_SGI_SN2) += kdba_fru.o ++obj-$(CONFIG_IA64_SGI_SN2) += kdba_pod.o ++obj-$(CONFIG_IA64_GENERIC) += kdba_pod.o ++ ++override CFLAGS := $(CFLAGS:%-pg=% ) ++ ++AFLAGS_kdba_jmp.o += $(AFLAGS_KERNEL) ++ ++USE_STANDARD_AS_RULE := true +--- /dev/null ++++ b/arch/ia64/kdb/cpu-ia64-opc.c +@@ -0,0 +1,598 @@ ++/* Copyright 1998, 1999, 2000, 2001, 2002, 2003 ++ Free Software Foundation, Inc. ++ Contributed by David Mosberger-Tang ++ ++This file is part of BFD, the Binary File Descriptor library. ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, write to the Free Software ++Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++/* Logically, this code should be part of libopcode but since some of ++ the operand insertion/extraction functions help bfd to implement ++ relocations, this code is included as part of cpu-ia64.c. This ++ avoids circular dependencies between libopcode and libbfd and also ++ obviates the need for applications to link in libopcode when all ++ they really want is libbfd. ++ ++ --davidm Mon Apr 13 22:14:02 1998 */ ++ ++#ifdef __KERNEL__ ++#include "ia64-opc.h" ++#else /* __KERNEL__ */ ++#include "../opcodes/ia64-opc.h" ++#endif /* __KERNEL__ */ ++ ++#define NELEMS(a) ((int) (sizeof (a) / sizeof ((a)[0]))) ++ ++static const char* ++ins_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED, ++ ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED) ++{ ++ return "internal error---this shouldn't happen"; ++} ++ ++static const char* ++ext_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED, ++ ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED) ++{ ++ return "internal error---this shouldn't happen"; ++} ++ ++static const char* ++ins_const (const struct ia64_operand *self ATTRIBUTE_UNUSED, ++ ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED) ++{ ++ return 0; ++} ++ ++static const char* ++ext_const (const struct ia64_operand *self ATTRIBUTE_UNUSED, ++ ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED) ++{ ++ return 0; ++} ++ ++static const char* ++ins_reg (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ if (value >= 1u << self->field[0].bits) ++ return "register number out of range"; ++ ++ *code |= value << self->field[0].shift; ++ return 0; ++} ++ ++static const char* ++ext_reg (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ *valuep = ((code >> self->field[0].shift) ++ & ((1u << self->field[0].bits) - 1)); ++ return 0; ++} ++ ++static const char* ++ins_immu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ ia64_insn new = 0; ++ int i; ++ ++ for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) ++ { ++ new |= ((value & ((((ia64_insn) 1) << self->field[i].bits) - 1)) ++ << self->field[i].shift); ++ value >>= self->field[i].bits; ++ } ++ if (value) ++ return "integer operand out of range"; ++ ++ *code |= new; ++ return 0; ++} ++ ++static const char* ++ext_immu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ BFD_HOST_U_64_BIT value = 0; ++ int i, bits = 0, total = 0; ++ ++ for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) ++ { ++ bits = self->field[i].bits; ++ value |= ((code >> self->field[i].shift) ++ & ((((BFD_HOST_U_64_BIT) 1) << bits) - 1)) << total; ++ total += bits; ++ } ++ *valuep = value; ++ return 0; ++} ++ ++static const char* ++ins_immus8 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ if (value & 0x7) ++ return "value not an integer multiple of 8"; ++ return ins_immu (self, value >> 3, code); ++} ++ ++static const char* ++ext_immus8 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ const char *result; ++ ++ result = ext_immu (self, code, valuep); ++ if (result) ++ return result; ++ ++ *valuep = *valuep << 3; ++ return 0; ++} ++ ++static const char* ++ins_imms_scaled (const struct ia64_operand *self, ia64_insn value, ++ ia64_insn *code, int scale) ++{ ++ BFD_HOST_64_BIT svalue = value, sign_bit = 0; ++ ia64_insn new = 0; ++ int i; ++ ++ svalue >>= scale; ++ ++ for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) ++ { ++ new |= ((svalue & ((((ia64_insn) 1) << self->field[i].bits) - 1)) ++ << self->field[i].shift); ++ sign_bit = (svalue >> (self->field[i].bits - 1)) & 1; ++ svalue >>= self->field[i].bits; ++ } ++ if ((!sign_bit && svalue != 0) || (sign_bit && svalue != -1)) ++ return "integer operand out of range"; ++ ++ *code |= new; ++ return 0; ++} ++ ++static const char* ++ext_imms_scaled (const struct ia64_operand *self, ia64_insn code, ++ ia64_insn *valuep, int scale) ++{ ++ int i, bits = 0, total = 0; ++ BFD_HOST_64_BIT val = 0, sign; ++ ++ for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) ++ { ++ bits = self->field[i].bits; ++ val |= ((code >> self->field[i].shift) ++ & ((((BFD_HOST_U_64_BIT) 1) << bits) - 1)) << total; ++ total += bits; ++ } ++ /* sign extend: */ ++ sign = (BFD_HOST_64_BIT) 1 << (total - 1); ++ val = (val ^ sign) - sign; ++ ++ *valuep = (val << scale); ++ return 0; ++} ++ ++static const char* ++ins_imms (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ return ins_imms_scaled (self, value, code, 0); ++} ++ ++static const char* ++ins_immsu4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; ++ ++ return ins_imms_scaled (self, value, code, 0); ++} ++ ++static const char* ++ext_imms (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ return ext_imms_scaled (self, code, valuep, 0); ++} ++ ++static const char* ++ins_immsm1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ --value; ++ return ins_imms_scaled (self, value, code, 0); ++} ++ ++static const char* ++ins_immsm1u4 (const struct ia64_operand *self, ia64_insn value, ++ ia64_insn *code) ++{ ++ value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; ++ ++ --value; ++ return ins_imms_scaled (self, value, code, 0); ++} ++ ++static const char* ++ext_immsm1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ const char *res = ext_imms_scaled (self, code, valuep, 0); ++ ++ ++*valuep; ++ return res; ++} ++ ++static const char* ++ins_imms1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ return ins_imms_scaled (self, value, code, 1); ++} ++ ++static const char* ++ext_imms1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ return ext_imms_scaled (self, code, valuep, 1); ++} ++ ++static const char* ++ins_imms4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ return ins_imms_scaled (self, value, code, 4); ++} ++ ++static const char* ++ext_imms4 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ return ext_imms_scaled (self, code, valuep, 4); ++} ++ ++static const char* ++ins_imms16 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ return ins_imms_scaled (self, value, code, 16); ++} ++ ++static const char* ++ext_imms16 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ return ext_imms_scaled (self, code, valuep, 16); ++} ++ ++static const char* ++ins_cimmu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ ia64_insn mask = (((ia64_insn) 1) << self->field[0].bits) - 1; ++ return ins_immu (self, value ^ mask, code); ++} ++ ++static const char* ++ext_cimmu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ const char *result; ++ ia64_insn mask; ++ ++ mask = (((ia64_insn) 1) << self->field[0].bits) - 1; ++ result = ext_immu (self, code, valuep); ++ if (!result) ++ { ++ mask = (((ia64_insn) 1) << self->field[0].bits) - 1; ++ *valuep ^= mask; ++ } ++ return result; ++} ++ ++static const char* ++ins_cnt (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ --value; ++ if (value >= ((BFD_HOST_U_64_BIT) 1) << self->field[0].bits) ++ return "count out of range"; ++ ++ *code |= value << self->field[0].shift; ++ return 0; ++} ++ ++static const char* ++ext_cnt (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ *valuep = ((code >> self->field[0].shift) ++ & ((((BFD_HOST_U_64_BIT) 1) << self->field[0].bits) - 1)) + 1; ++ return 0; ++} ++ ++static const char* ++ins_cnt2b (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ --value; ++ ++ if (value > 2) ++ return "count must be in range 1..3"; ++ ++ *code |= value << self->field[0].shift; ++ return 0; ++} ++ ++static const char* ++ext_cnt2b (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ *valuep = ((code >> self->field[0].shift) & 0x3) + 1; ++ return 0; ++} ++ ++static const char* ++ins_cnt2c (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ switch (value) ++ { ++ case 0: value = 0; break; ++ case 7: value = 1; break; ++ case 15: value = 2; break; ++ case 16: value = 3; break; ++ default: return "count must be 0, 7, 15, or 16"; ++ } ++ *code |= value << self->field[0].shift; ++ return 0; ++} ++ ++static const char* ++ext_cnt2c (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ ia64_insn value; ++ ++ value = (code >> self->field[0].shift) & 0x3; ++ switch (value) ++ { ++ case 0: value = 0; break; ++ case 1: value = 7; break; ++ case 2: value = 15; break; ++ case 3: value = 16; break; ++ } ++ *valuep = value; ++ return 0; ++} ++ ++static const char* ++ins_inc3 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) ++{ ++ BFD_HOST_64_BIT val = value; ++ BFD_HOST_U_64_BIT sign = 0; ++ ++ if (val < 0) ++ { ++ sign = 0x4; ++ value = -value; ++ } ++ switch (value) ++ { ++ case 1: value = 3; break; ++ case 4: value = 2; break; ++ case 8: value = 1; break; ++ case 16: value = 0; break; ++ default: return "count must be +/- 1, 4, 8, or 16"; ++ } ++ *code |= (sign | value) << self->field[0].shift; ++ return 0; ++} ++ ++static const char* ++ext_inc3 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) ++{ ++ BFD_HOST_64_BIT val; ++ int negate; ++ ++ val = (code >> self->field[0].shift) & 0x7; ++ negate = val & 0x4; ++ switch (val & 0x3) ++ { ++ case 0: val = 16; break; ++ case 1: val = 8; break; ++ case 2: val = 4; break; ++ case 3: val = 1; break; ++ } ++ if (negate) ++ val = -val; ++ ++ *valuep = val; ++ return 0; ++} ++ ++#define CST IA64_OPND_CLASS_CST ++#define REG IA64_OPND_CLASS_REG ++#define IND IA64_OPND_CLASS_IND ++#define ABS IA64_OPND_CLASS_ABS ++#define REL IA64_OPND_CLASS_REL ++ ++#define SDEC IA64_OPND_FLAG_DECIMAL_SIGNED ++#define UDEC IA64_OPND_FLAG_DECIMAL_UNSIGNED ++ ++const struct ia64_operand elf64_ia64_operands[IA64_OPND_COUNT] = ++ { ++ /* constants: */ ++ { CST, ins_const, ext_const, "NIL", {{ 0, 0}}, 0, "" }, ++ { CST, ins_const, ext_const, "ar.csd", {{ 0, 0}}, 0, "ar.csd" }, ++ { CST, ins_const, ext_const, "ar.ccv", {{ 0, 0}}, 0, "ar.ccv" }, ++ { CST, ins_const, ext_const, "ar.pfs", {{ 0, 0}}, 0, "ar.pfs" }, ++ { CST, ins_const, ext_const, "1", {{ 0, 0}}, 0, "1" }, ++ { CST, ins_const, ext_const, "8", {{ 0, 0}}, 0, "8" }, ++ { CST, ins_const, ext_const, "16", {{ 0, 0}}, 0, "16" }, ++ { CST, ins_const, ext_const, "r0", {{ 0, 0}}, 0, "r0" }, ++ { CST, ins_const, ext_const, "ip", {{ 0, 0}}, 0, "ip" }, ++ { CST, ins_const, ext_const, "pr", {{ 0, 0}}, 0, "pr" }, ++ { CST, ins_const, ext_const, "pr.rot", {{ 0, 0}}, 0, "pr.rot" }, ++ { CST, ins_const, ext_const, "psr", {{ 0, 0}}, 0, "psr" }, ++ { CST, ins_const, ext_const, "psr.l", {{ 0, 0}}, 0, "psr.l" }, ++ { CST, ins_const, ext_const, "psr.um", {{ 0, 0}}, 0, "psr.um" }, ++ ++ /* register operands: */ ++ { REG, ins_reg, ext_reg, "ar", {{ 7, 20}}, 0, /* AR3 */ ++ "an application register" }, ++ { REG, ins_reg, ext_reg, "b", {{ 3, 6}}, 0, /* B1 */ ++ "a branch register" }, ++ { REG, ins_reg, ext_reg, "b", {{ 3, 13}}, 0, /* B2 */ ++ "a branch register"}, ++ { REG, ins_reg, ext_reg, "cr", {{ 7, 20}}, 0, /* CR */ ++ "a control register"}, ++ { REG, ins_reg, ext_reg, "f", {{ 7, 6}}, 0, /* F1 */ ++ "a floating-point register" }, ++ { REG, ins_reg, ext_reg, "f", {{ 7, 13}}, 0, /* F2 */ ++ "a floating-point register" }, ++ { REG, ins_reg, ext_reg, "f", {{ 7, 20}}, 0, /* F3 */ ++ "a floating-point register" }, ++ { REG, ins_reg, ext_reg, "f", {{ 7, 27}}, 0, /* F4 */ ++ "a floating-point register" }, ++ { REG, ins_reg, ext_reg, "p", {{ 6, 6}}, 0, /* P1 */ ++ "a predicate register" }, ++ { REG, ins_reg, ext_reg, "p", {{ 6, 27}}, 0, /* P2 */ ++ "a predicate register" }, ++ { REG, ins_reg, ext_reg, "r", {{ 7, 6}}, 0, /* R1 */ ++ "a general register" }, ++ { REG, ins_reg, ext_reg, "r", {{ 7, 13}}, 0, /* R2 */ ++ "a general register" }, ++ { REG, ins_reg, ext_reg, "r", {{ 7, 20}}, 0, /* R3 */ ++ "a general register" }, ++ { REG, ins_reg, ext_reg, "r", {{ 2, 20}}, 0, /* R3_2 */ ++ "a general register r0-r3" }, ++ ++ /* indirect operands: */ ++ { IND, ins_reg, ext_reg, "cpuid", {{7, 20}}, 0, /* CPUID_R3 */ ++ "a cpuid register" }, ++ { IND, ins_reg, ext_reg, "dbr", {{7, 20}}, 0, /* DBR_R3 */ ++ "a dbr register" }, ++ { IND, ins_reg, ext_reg, "dtr", {{7, 20}}, 0, /* DTR_R3 */ ++ "a dtr register" }, ++ { IND, ins_reg, ext_reg, "itr", {{7, 20}}, 0, /* ITR_R3 */ ++ "an itr register" }, ++ { IND, ins_reg, ext_reg, "ibr", {{7, 20}}, 0, /* IBR_R3 */ ++ "an ibr register" }, ++ { IND, ins_reg, ext_reg, "", {{7, 20}}, 0, /* MR3 */ ++ "an indirect memory address" }, ++ { IND, ins_reg, ext_reg, "msr", {{7, 20}}, 0, /* MSR_R3 */ ++ "an msr register" }, ++ { IND, ins_reg, ext_reg, "pkr", {{7, 20}}, 0, /* PKR_R3 */ ++ "a pkr register" }, ++ { IND, ins_reg, ext_reg, "pmc", {{7, 20}}, 0, /* PMC_R3 */ ++ "a pmc register" }, ++ { IND, ins_reg, ext_reg, "pmd", {{7, 20}}, 0, /* PMD_R3 */ ++ "a pmd register" }, ++ { IND, ins_reg, ext_reg, "rr", {{7, 20}}, 0, /* RR_R3 */ ++ "an rr register" }, ++ ++ /* immediate operands: */ ++ { ABS, ins_cimmu, ext_cimmu, 0, {{ 5, 20 }}, UDEC, /* CCNT5 */ ++ "a 5-bit count (0-31)" }, ++ { ABS, ins_cnt, ext_cnt, 0, {{ 2, 27 }}, UDEC, /* CNT2a */ ++ "a 2-bit count (1-4)" }, ++ { ABS, ins_cnt2b, ext_cnt2b, 0, {{ 2, 27 }}, UDEC, /* CNT2b */ ++ "a 2-bit count (1-3)" }, ++ { ABS, ins_cnt2c, ext_cnt2c, 0, {{ 2, 30 }}, UDEC, /* CNT2c */ ++ "a count (0, 7, 15, or 16)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 5, 14}}, UDEC, /* CNT5 */ ++ "a 5-bit count (0-31)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 6, 27}}, UDEC, /* CNT6 */ ++ "a 6-bit count (0-63)" }, ++ { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 20}}, UDEC, /* CPOS6a */ ++ "a 6-bit bit pos (0-63)" }, ++ { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 14}}, UDEC, /* CPOS6b */ ++ "a 6-bit bit pos (0-63)" }, ++ { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 31}}, UDEC, /* CPOS6c */ ++ "a 6-bit bit pos (0-63)" }, ++ { ABS, ins_imms, ext_imms, 0, {{ 1, 36}}, SDEC, /* IMM1 */ ++ "a 1-bit integer (-1, 0)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 2, 13}}, UDEC, /* IMMU2 */ ++ "a 2-bit unsigned (0-3)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, 0, /* IMMU7a */ ++ "a 7-bit unsigned (0-127)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, 0, /* IMMU7b */ ++ "a 7-bit unsigned (0-127)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, UDEC, /* SOF */ ++ "a frame size (register count)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, UDEC, /* SOL */ ++ "a local register count" }, ++ { ABS, ins_immus8,ext_immus8,0, {{ 4, 27}}, UDEC, /* SOR */ ++ "a rotating register count (integer multiple of 8)" }, ++ { ABS, ins_imms, ext_imms, 0, /* IMM8 */ ++ {{ 7, 13}, { 1, 36}}, SDEC, ++ "an 8-bit integer (-128-127)" }, ++ { ABS, ins_immsu4, ext_imms, 0, /* IMM8U4 */ ++ {{ 7, 13}, { 1, 36}}, SDEC, ++ "an 8-bit signed integer for 32-bit unsigned compare (-128-127)" }, ++ { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1 */ ++ {{ 7, 13}, { 1, 36}}, SDEC, ++ "an 8-bit integer (-127-128)" }, ++ { ABS, ins_immsm1u4, ext_immsm1, 0, /* IMM8M1U4 */ ++ {{ 7, 13}, { 1, 36}}, SDEC, ++ "an 8-bit integer for 32-bit unsigned compare (-127-(-1),1-128,0x100000000)" }, ++ { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1U8 */ ++ {{ 7, 13}, { 1, 36}}, SDEC, ++ "an 8-bit integer for 64-bit unsigned compare (-127-(-1),1-128,0x10000000000000000)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 2, 33}, { 7, 20}}, 0, /* IMMU9 */ ++ "a 9-bit unsigned (0-511)" }, ++ { ABS, ins_imms, ext_imms, 0, /* IMM9a */ ++ {{ 7, 6}, { 1, 27}, { 1, 36}}, SDEC, ++ "a 9-bit integer (-256-255)" }, ++ { ABS, ins_imms, ext_imms, 0, /* IMM9b */ ++ {{ 7, 13}, { 1, 27}, { 1, 36}}, SDEC, ++ "a 9-bit integer (-256-255)" }, ++ { ABS, ins_imms, ext_imms, 0, /* IMM14 */ ++ {{ 7, 13}, { 6, 27}, { 1, 36}}, SDEC, ++ "a 14-bit integer (-8192-8191)" }, ++ { ABS, ins_imms1, ext_imms1, 0, /* IMM17 */ ++ {{ 7, 6}, { 8, 24}, { 1, 36}}, 0, ++ "a 17-bit integer (-65536-65535)" }, ++ { ABS, ins_immu, ext_immu, 0, {{20, 6}, { 1, 36}}, 0, /* IMMU21 */ ++ "a 21-bit unsigned" }, ++ { ABS, ins_imms, ext_imms, 0, /* IMM22 */ ++ {{ 7, 13}, { 9, 27}, { 5, 22}, { 1, 36}}, SDEC, ++ "a 22-bit signed integer" }, ++ { ABS, ins_immu, ext_immu, 0, /* IMMU24 */ ++ {{21, 6}, { 2, 31}, { 1, 36}}, 0, ++ "a 24-bit unsigned" }, ++ { ABS, ins_imms16,ext_imms16,0, {{27, 6}, { 1, 36}}, 0, /* IMM44 */ ++ "a 44-bit unsigned (least 16 bits ignored/zeroes)" }, ++ { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU62 */ ++ "a 62-bit unsigned" }, ++ { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU64 */ ++ "a 64-bit unsigned" }, ++ { ABS, ins_inc3, ext_inc3, 0, {{ 3, 13}}, SDEC, /* INC3 */ ++ "an increment (+/- 1, 4, 8, or 16)" }, ++ { ABS, ins_cnt, ext_cnt, 0, {{ 4, 27}}, UDEC, /* LEN4 */ ++ "a 4-bit length (1-16)" }, ++ { ABS, ins_cnt, ext_cnt, 0, {{ 6, 27}}, UDEC, /* LEN6 */ ++ "a 6-bit length (1-64)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 4, 20}}, 0, /* MBTYPE4 */ ++ "a mix type (@rev, @mix, @shuf, @alt, or @brcst)" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 8, 20}}, 0, /* MBTYPE8 */ ++ "an 8-bit mix type" }, ++ { ABS, ins_immu, ext_immu, 0, {{ 6, 14}}, UDEC, /* POS6 */ ++ "a 6-bit bit pos (0-63)" }, ++ { REL, ins_imms4, ext_imms4, 0, {{ 7, 6}, { 2, 33}}, 0, /* TAG13 */ ++ "a branch tag" }, ++ { REL, ins_imms4, ext_imms4, 0, {{ 9, 24}}, 0, /* TAG13b */ ++ "a branch tag" }, ++ { REL, ins_imms4, ext_imms4, 0, {{20, 6}, { 1, 36}}, 0, /* TGT25 */ ++ "a branch target" }, ++ { REL, ins_imms4, ext_imms4, 0, /* TGT25b */ ++ {{ 7, 6}, {13, 20}, { 1, 36}}, 0, ++ "a branch target" }, ++ { REL, ins_imms4, ext_imms4, 0, {{20, 13}, { 1, 36}}, 0, /* TGT25c */ ++ "a branch target" }, ++ { REL, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* TGT64 */ ++ "a branch target" }, ++ ++ { ABS, ins_const, ext_const, 0, {{0, 0}}, 0, /* LDXMOV */ ++ "ldxmov target" }, ++ }; +--- /dev/null ++++ b/arch/ia64/kdb/ia64-asmtab.c +@@ -0,0 +1,8585 @@ ++/* This file is automatically generated by ia64-gen. Do not edit! */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++static const char * const ia64_strings[] = { ++ "", "0", "1", "a", "acq", "add", "addl", "addp4", "adds", "alloc", "and", ++ "andcm", "b", "bias", "br", "break", "brl", "brp", "bsw", "c", "call", ++ "cexit", "chk", "cloop", "clr", "clrrrb", "cmp", "cmp4", "cmp8xchg16", ++ "cmpxchg1", "cmpxchg2", "cmpxchg4", "cmpxchg8", "cond", "cover", "ctop", ++ "czx1", "czx2", "d", "dep", "dpnt", "dptk", "e", "epc", "eq", "excl", ++ "exit", "exp", "extr", "f", "fabs", "fadd", "famax", "famin", "fand", ++ "fandcm", "fault", "fc", "fchkf", "fclass", "fclrf", "fcmp", "fcvt", ++ "fetchadd4", "fetchadd8", "few", "fill", "flushrs", "fma", "fmax", ++ "fmerge", "fmin", "fmix", "fmpy", "fms", "fneg", "fnegabs", "fnma", ++ "fnmpy", "fnorm", "for", "fpabs", "fpack", "fpamax", "fpamin", "fpcmp", ++ "fpcvt", "fpma", "fpmax", "fpmerge", "fpmin", "fpmpy", "fpms", "fpneg", ++ "fpnegabs", "fpnma", "fpnmpy", "fprcpa", "fprsqrta", "frcpa", "frsqrta", ++ "fselect", "fsetc", "fsub", "fswap", "fsxt", "fwb", "fx", "fxor", "fxu", ++ "g", "ga", "ge", "getf", "geu", "gt", "gtu", "h", "hint", "hu", "i", "ia", ++ "imp", "invala", "itc", "itr", "l", "ld1", "ld16", "ld2", "ld4", "ld8", ++ "ldf", "ldf8", "ldfd", "ldfe", "ldfp8", "ldfpd", "ldfps", "ldfs", "le", ++ "leu", "lfetch", "loadrs", "loop", "lr", "lt", "ltu", "lu", "m", "many", ++ "mf", "mix1", "mix2", "mix4", "mov", "movl", "mux1", "mux2", "nc", "ne", ++ "neq", "nge", "ngt", "nl", "nle", "nlt", "nm", "nop", "nr", "ns", "nt1", ++ "nt2", "nta", "nz", "or", "orcm", "ord", "pack2", "pack4", "padd1", ++ "padd2", "padd4", "pavg1", "pavg2", "pavgsub1", "pavgsub2", "pcmp1", ++ "pcmp2", "pcmp4", "pmax1", "pmax2", "pmin1", "pmin2", "pmpy2", "pmpyshr2", ++ "popcnt", "pr", "probe", "psad1", "pshl2", "pshl4", "pshladd2", "pshr2", ++ "pshr4", "pshradd2", "psub1", "psub2", "psub4", "ptc", "ptr", "r", "raz", ++ "rel", "ret", "rfi", "rsm", "rum", "rw", "s", "s0", "s1", "s2", "s3", ++ "sa", "se", "setf", "shl", "shladd", "shladdp4", "shr", "shrp", "sig", ++ "spill", "spnt", "sptk", "srlz", "ssm", "sss", "st1", "st16", "st2", ++ "st4", "st8", "stf", "stf8", "stfd", "stfe", "stfs", "sub", "sum", "sxt1", ++ "sxt2", "sxt4", "sync", "tak", "tbit", "thash", "tnat", "tpa", "trunc", ++ "ttag", "u", "unc", "unord", "unpack1", "unpack2", "unpack4", "uss", ++ "uus", "uuu", "w", "wexit", "wtop", "x", "xchg1", "xchg2", "xchg4", ++ "xchg8", "xf", "xma", "xmpy", "xor", "xuf", "z", "zxt1", "zxt2", "zxt4", ++}; ++ ++static const struct ia64_dependency ++dependencies[] = { ++ { "ALAT", 0, 0, 0, -1, NULL, }, ++ { "AR[BSP]", 26, 0, 2, 17, NULL, }, ++ { "AR[BSPSTORE]", 26, 0, 2, 18, NULL, }, ++ { "AR[CFLG]", 26, 0, 2, 27, NULL, }, ++ { "AR[CCV]", 26, 0, 2, 32, NULL, }, ++ { "AR[CSD]", 26, 0, 2, 25, NULL, }, ++ { "AR[EC]", 26, 0, 2, 66, NULL, }, ++ { "AR[EFLAG]", 26, 0, 2, 24, NULL, }, ++ { "AR[FCR]", 26, 0, 2, 21, NULL, }, ++ { "AR[FDR]", 26, 0, 2, 30, NULL, }, ++ { "AR[FIR]", 26, 0, 2, 29, NULL, }, ++ { "AR[FPSR].sf0.controls", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].sf1.controls", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].sf2.controls", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].sf3.controls", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].sf0.flags", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].sf1.flags", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].sf2.flags", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].sf3.flags", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].traps", 30, 0, 2, -1, NULL, }, ++ { "AR[FPSR].rv", 30, 0, 2, -1, NULL, }, ++ { "AR[FSR]", 26, 0, 2, 28, NULL, }, ++ { "AR[ITC]", 26, 0, 2, 44, NULL, }, ++ { "AR[K%], % in 0 - 7", 1, 0, 2, -1, NULL, }, ++ { "AR[LC]", 26, 0, 2, 65, NULL, }, ++ { "AR[PFS]", 26, 0, 2, 64, NULL, }, ++ { "AR[PFS]", 26, 0, 2, 64, NULL, }, ++ { "AR[PFS]", 26, 0, 0, 64, NULL, }, ++ { "AR[RNAT]", 26, 0, 2, 19, NULL, }, ++ { "AR[RSC]", 26, 0, 2, 16, NULL, }, ++ { "AR[SSD]", 26, 0, 2, 26, NULL, }, ++ { "AR[UNAT]{%}, % in 0 - 63", 2, 0, 2, -1, NULL, }, ++ { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 0, 0, -1, NULL, }, ++ { "AR%, % in 48-63, 112-127", 4, 0, 2, -1, NULL, }, ++ { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, }, ++ { "BR%, % in 0 - 7", 5, 0, 0, -1, NULL, }, ++ { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, }, ++ { "CFM", 6, 0, 2, -1, NULL, }, ++ { "CFM", 6, 0, 2, -1, NULL, }, ++ { "CFM", 6, 0, 2, -1, NULL, }, ++ { "CFM", 6, 0, 2, -1, NULL, }, ++ { "CFM", 6, 0, 0, -1, NULL, }, ++ { "CPUID#", 7, 0, 5, -1, NULL, }, ++ { "CR[CMCV]", 27, 0, 3, 74, NULL, }, ++ { "CR[DCR]", 27, 0, 3, 0, NULL, }, ++ { "CR[EOI]", 27, 0, 7, 67, "SC Section 10.8.3.4", }, ++ { "CR[GPTA]", 27, 0, 3, 9, NULL, }, ++ { "CR[IFA]", 27, 0, 1, 20, NULL, }, ++ { "CR[IFA]", 27, 0, 3, 20, NULL, }, ++ { "CR[IFS]", 27, 0, 3, 23, NULL, }, ++ { "CR[IFS]", 27, 0, 1, 23, NULL, }, ++ { "CR[IFS]", 27, 0, 1, 23, NULL, }, ++ { "CR[IHA]", 27, 0, 3, 25, NULL, }, ++ { "CR[IIM]", 27, 0, 3, 24, NULL, }, ++ { "CR[IIP]", 27, 0, 3, 19, NULL, }, ++ { "CR[IIP]", 27, 0, 1, 19, NULL, }, ++ { "CR[IIPA]", 27, 0, 3, 22, NULL, }, ++ { "CR[IPSR]", 27, 0, 3, 16, NULL, }, ++ { "CR[IPSR]", 27, 0, 1, 16, NULL, }, ++ { "CR[IRR%], % in 0 - 3", 8, 0, 3, -1, NULL, }, ++ { "CR[ISR]", 27, 0, 3, 17, NULL, }, ++ { "CR[ITIR]", 27, 0, 3, 21, NULL, }, ++ { "CR[ITIR]", 27, 0, 1, 21, NULL, }, ++ { "CR[ITM]", 27, 0, 3, 1, NULL, }, ++ { "CR[ITV]", 27, 0, 3, 72, NULL, }, ++ { "CR[IVA]", 27, 0, 4, 2, NULL, }, ++ { "CR[IVR]", 27, 0, 7, 65, "SC Section 10.8.3.2", }, ++ { "CR[LID]", 27, 0, 7, 64, "SC Section 10.8.3.1", }, ++ { "CR[LRR%], % in 0 - 1", 9, 0, 3, -1, NULL, }, ++ { "CR[PMV]", 27, 0, 3, 73, NULL, }, ++ { "CR[PTA]", 27, 0, 3, 8, NULL, }, ++ { "CR[TPR]", 27, 0, 3, 66, NULL, }, ++ { "CR[TPR]", 27, 0, 7, 66, "SC Section 10.8.3.3", }, ++ { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 0, 0, -1, NULL, }, ++ { "DBR#", 11, 0, 2, -1, NULL, }, ++ { "DBR#", 11, 0, 3, -1, NULL, }, ++ { "DTC", 0, 0, 3, -1, NULL, }, ++ { "DTC", 0, 0, 2, -1, NULL, }, ++ { "DTC", 0, 0, 0, -1, NULL, }, ++ { "DTC", 0, 0, 2, -1, NULL, }, ++ { "DTC_LIMIT*", 0, 0, 2, -1, NULL, }, ++ { "DTR", 0, 0, 3, -1, NULL, }, ++ { "DTR", 0, 0, 2, -1, NULL, }, ++ { "DTR", 0, 0, 3, -1, NULL, }, ++ { "DTR", 0, 0, 0, -1, NULL, }, ++ { "DTR", 0, 0, 2, -1, NULL, }, ++ { "FR%, % in 0 - 1", 12, 0, 0, -1, NULL, }, ++ { "FR%, % in 2 - 127", 13, 0, 2, -1, NULL, }, ++ { "FR%, % in 2 - 127", 13, 0, 0, -1, NULL, }, ++ { "GR0", 14, 0, 0, -1, NULL, }, ++ { "GR%, % in 1 - 127", 15, 0, 0, -1, NULL, }, ++ { "GR%, % in 1 - 127", 15, 0, 2, -1, NULL, }, ++ { "IBR#", 16, 0, 2, -1, NULL, }, ++ { "InService*", 17, 0, 3, -1, NULL, }, ++ { "InService*", 17, 0, 2, -1, NULL, }, ++ { "InService*", 17, 0, 2, -1, NULL, }, ++ { "IP", 0, 0, 0, -1, NULL, }, ++ { "ITC", 0, 0, 4, -1, NULL, }, ++ { "ITC", 0, 0, 2, -1, NULL, }, ++ { "ITC", 0, 0, 0, -1, NULL, }, ++ { "ITC", 0, 0, 4, -1, NULL, }, ++ { "ITC", 0, 0, 2, -1, NULL, }, ++ { "ITC_LIMIT*", 0, 0, 2, -1, NULL, }, ++ { "ITR", 0, 0, 2, -1, NULL, }, ++ { "ITR", 0, 0, 4, -1, NULL, }, ++ { "ITR", 0, 0, 2, -1, NULL, }, ++ { "ITR", 0, 0, 0, -1, NULL, }, ++ { "ITR", 0, 0, 4, -1, NULL, }, ++ { "memory", 0, 0, 0, -1, NULL, }, ++ { "MSR#", 18, 0, 5, -1, NULL, }, ++ { "PKR#", 19, 0, 3, -1, NULL, }, ++ { "PKR#", 19, 0, 0, -1, NULL, }, ++ { "PKR#", 19, 0, 2, -1, NULL, }, ++ { "PKR#", 19, 0, 2, -1, NULL, }, ++ { "PMC#", 20, 0, 2, -1, NULL, }, ++ { "PMC#", 20, 0, 7, -1, "SC+3 Section 12.1.1", }, ++ { "PMD#", 21, 0, 2, -1, NULL, }, ++ { "PR0", 0, 0, 0, -1, NULL, }, ++ { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, }, ++ { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, }, ++ { "PR%, % in 1 - 15", 22, 0, 0, -1, NULL, }, ++ { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, }, ++ { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, }, ++ { "PR%, % in 16 - 62", 23, 0, 0, -1, NULL, }, ++ { "PR63", 24, 0, 2, -1, NULL, }, ++ { "PR63", 24, 0, 2, -1, NULL, }, ++ { "PR63", 24, 0, 0, -1, NULL, }, ++ { "PSR.ac", 28, 0, 1, 3, NULL, }, ++ { "PSR.ac", 28, 0, 3, 3, NULL, }, ++ { "PSR.ac", 28, 0, 2, 3, NULL, }, ++ { "PSR.be", 28, 0, 1, 1, NULL, }, ++ { "PSR.be", 28, 0, 3, 1, NULL, }, ++ { "PSR.be", 28, 0, 2, 1, NULL, }, ++ { "PSR.bn", 28, 0, 2, 44, NULL, }, ++ { "PSR.cpl", 28, 0, 1, 32, NULL, }, ++ { "PSR.da", 28, 0, 3, 38, NULL, }, ++ { "PSR.db", 28, 0, 3, 24, NULL, }, ++ { "PSR.db", 28, 0, 2, 24, NULL, }, ++ { "PSR.db", 28, 0, 3, 24, NULL, }, ++ { "PSR.dd", 28, 0, 3, 39, NULL, }, ++ { "PSR.dfh", 28, 0, 3, 19, NULL, }, ++ { "PSR.dfh", 28, 0, 2, 19, NULL, }, ++ { "PSR.dfl", 28, 0, 3, 18, NULL, }, ++ { "PSR.dfl", 28, 0, 2, 18, NULL, }, ++ { "PSR.di", 28, 0, 3, 22, NULL, }, ++ { "PSR.di", 28, 0, 2, 22, NULL, }, ++ { "PSR.dt", 28, 0, 3, 17, NULL, }, ++ { "PSR.dt", 28, 0, 2, 17, NULL, }, ++ { "PSR.ed", 28, 0, 3, 43, NULL, }, ++ { "PSR.i", 28, 0, 2, 14, NULL, }, ++ { "PSR.i", 28, 0, 3, 14, NULL, }, ++ { "PSR.ia", 28, 0, 0, 14, NULL, }, ++ { "PSR.ic", 28, 0, 2, 13, NULL, }, ++ { "PSR.ic", 28, 0, 3, 13, NULL, }, ++ { "PSR.id", 28, 0, 0, 14, NULL, }, ++ { "PSR.is", 28, 0, 0, 14, NULL, }, ++ { "PSR.it", 28, 0, 3, 14, NULL, }, ++ { "PSR.lp", 28, 0, 2, 25, NULL, }, ++ { "PSR.lp", 28, 0, 3, 25, NULL, }, ++ { "PSR.lp", 28, 0, 3, 25, NULL, }, ++ { "PSR.mc", 28, 0, 0, 35, NULL, }, ++ { "PSR.mfh", 28, 0, 2, 5, NULL, }, ++ { "PSR.mfl", 28, 0, 2, 4, NULL, }, ++ { "PSR.pk", 28, 0, 3, 15, NULL, }, ++ { "PSR.pk", 28, 0, 2, 15, NULL, }, ++ { "PSR.pp", 28, 0, 2, 21, NULL, }, ++ { "PSR.ri", 28, 0, 0, 41, NULL, }, ++ { "PSR.rt", 28, 0, 2, 27, NULL, }, ++ { "PSR.rt", 28, 0, 3, 27, NULL, }, ++ { "PSR.rt", 28, 0, 3, 27, NULL, }, ++ { "PSR.si", 28, 0, 2, 23, NULL, }, ++ { "PSR.si", 28, 0, 3, 23, NULL, }, ++ { "PSR.sp", 28, 0, 2, 20, NULL, }, ++ { "PSR.sp", 28, 0, 3, 20, NULL, }, ++ { "PSR.ss", 28, 0, 3, 40, NULL, }, ++ { "PSR.tb", 28, 0, 3, 26, NULL, }, ++ { "PSR.tb", 28, 0, 2, 26, NULL, }, ++ { "PSR.up", 28, 0, 2, 2, NULL, }, ++ { "RR#", 25, 0, 3, -1, NULL, }, ++ { "RR#", 25, 0, 2, -1, NULL, }, ++ { "RSE", 29, 0, 2, -1, NULL, }, ++ { "ALAT", 0, 1, 0, -1, NULL, }, ++ { "AR[BSP]", 26, 1, 2, 17, NULL, }, ++ { "AR[BSPSTORE]", 26, 1, 2, 18, NULL, }, ++ { "AR[CCV]", 26, 1, 2, 32, NULL, }, ++ { "AR[CFLG]", 26, 1, 2, 27, NULL, }, ++ { "AR[CSD]", 26, 1, 2, 25, NULL, }, ++ { "AR[EC]", 26, 1, 2, 66, NULL, }, ++ { "AR[EFLAG]", 26, 1, 2, 24, NULL, }, ++ { "AR[FCR]", 26, 1, 2, 21, NULL, }, ++ { "AR[FDR]", 26, 1, 2, 30, NULL, }, ++ { "AR[FIR]", 26, 1, 2, 29, NULL, }, ++ { "AR[FPSR].sf0.controls", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf1.controls", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf2.controls", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf3.controls", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf0.flags", 30, 1, 0, -1, NULL, }, ++ { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf1.flags", 30, 1, 0, -1, NULL, }, ++ { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf2.flags", 30, 1, 0, -1, NULL, }, ++ { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf3.flags", 30, 1, 0, -1, NULL, }, ++ { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].rv", 30, 1, 2, -1, NULL, }, ++ { "AR[FPSR].traps", 30, 1, 2, -1, NULL, }, ++ { "AR[FSR]", 26, 1, 2, 28, NULL, }, ++ { "AR[ITC]", 26, 1, 2, 44, NULL, }, ++ { "AR[K%], % in 0 - 7", 1, 1, 2, -1, NULL, }, ++ { "AR[LC]", 26, 1, 2, 65, NULL, }, ++ { "AR[PFS]", 26, 1, 0, 64, NULL, }, ++ { "AR[PFS]", 26, 1, 2, 64, NULL, }, ++ { "AR[PFS]", 26, 1, 2, 64, NULL, }, ++ { "AR[RNAT]", 26, 1, 2, 19, NULL, }, ++ { "AR[RSC]", 26, 1, 2, 16, NULL, }, ++ { "AR[UNAT]{%}, % in 0 - 63", 2, 1, 2, -1, NULL, }, ++ { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 1, 0, -1, NULL, }, ++ { "AR%, % in 48 - 63, 112-127", 4, 1, 2, -1, NULL, }, ++ { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, ++ { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, ++ { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, ++ { "BR%, % in 0 - 7", 5, 1, 0, -1, NULL, }, ++ { "CFM", 6, 1, 2, -1, NULL, }, ++ { "CPUID#", 7, 1, 0, -1, NULL, }, ++ { "CR[CMCV]", 27, 1, 2, 74, NULL, }, ++ { "CR[DCR]", 27, 1, 2, 0, NULL, }, ++ { "CR[EOI]", 27, 1, 7, 67, "SC Section 10.8.3.4", }, ++ { "CR[GPTA]", 27, 1, 2, 9, NULL, }, ++ { "CR[IFA]", 27, 1, 2, 20, NULL, }, ++ { "CR[IFS]", 27, 1, 2, 23, NULL, }, ++ { "CR[IHA]", 27, 1, 2, 25, NULL, }, ++ { "CR[IIM]", 27, 1, 2, 24, NULL, }, ++ { "CR[IIP]", 27, 1, 2, 19, NULL, }, ++ { "CR[IIPA]", 27, 1, 2, 22, NULL, }, ++ { "CR[IPSR]", 27, 1, 2, 16, NULL, }, ++ { "CR[IRR%], % in 0 - 3", 8, 1, 2, -1, NULL, }, ++ { "CR[ISR]", 27, 1, 2, 17, NULL, }, ++ { "CR[ITIR]", 27, 1, 2, 21, NULL, }, ++ { "CR[ITM]", 27, 1, 2, 1, NULL, }, ++ { "CR[ITV]", 27, 1, 2, 72, NULL, }, ++ { "CR[IVA]", 27, 1, 2, 2, NULL, }, ++ { "CR[IVR]", 27, 1, 7, 65, "SC", }, ++ { "CR[LID]", 27, 1, 7, 64, "SC", }, ++ { "CR[LRR%], % in 0 - 1", 9, 1, 2, -1, NULL, }, ++ { "CR[PMV]", 27, 1, 2, 73, NULL, }, ++ { "CR[PTA]", 27, 1, 2, 8, NULL, }, ++ { "CR[TPR]", 27, 1, 2, 66, NULL, }, ++ { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 1, 0, -1, NULL, }, ++ { "DBR#", 11, 1, 2, -1, NULL, }, ++ { "DTC", 0, 1, 0, -1, NULL, }, ++ { "DTC", 0, 1, 2, -1, NULL, }, ++ { "DTC", 0, 1, 2, -1, NULL, }, ++ { "DTC_LIMIT*", 0, 1, 2, -1, NULL, }, ++ { "DTR", 0, 1, 2, -1, NULL, }, ++ { "DTR", 0, 1, 2, -1, NULL, }, ++ { "DTR", 0, 1, 2, -1, NULL, }, ++ { "DTR", 0, 1, 0, -1, NULL, }, ++ { "FR%, % in 0 - 1", 12, 1, 0, -1, NULL, }, ++ { "FR%, % in 2 - 127", 13, 1, 2, -1, NULL, }, ++ { "GR0", 14, 1, 0, -1, NULL, }, ++ { "GR%, % in 1 - 127", 15, 1, 2, -1, NULL, }, ++ { "IBR#", 16, 1, 2, -1, NULL, }, ++ { "InService*", 17, 1, 7, -1, "SC", }, ++ { "IP", 0, 1, 0, -1, NULL, }, ++ { "ITC", 0, 1, 0, -1, NULL, }, ++ { "ITC", 0, 1, 2, -1, NULL, }, ++ { "ITC", 0, 1, 2, -1, NULL, }, ++ { "ITR", 0, 1, 2, -1, NULL, }, ++ { "ITR", 0, 1, 2, -1, NULL, }, ++ { "ITR", 0, 1, 0, -1, NULL, }, ++ { "memory", 0, 1, 0, -1, NULL, }, ++ { "MSR#", 18, 1, 7, -1, "SC", }, ++ { "PKR#", 19, 1, 0, -1, NULL, }, ++ { "PKR#", 19, 1, 0, -1, NULL, }, ++ { "PKR#", 19, 1, 2, -1, NULL, }, ++ { "PMC#", 20, 1, 2, -1, NULL, }, ++ { "PMD#", 21, 1, 2, -1, NULL, }, ++ { "PR0", 0, 1, 0, -1, NULL, }, ++ { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, }, ++ { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, }, ++ { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, }, ++ { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, }, ++ { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, }, ++ { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, }, ++ { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, }, ++ { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, }, ++ { "PR63", 24, 1, 0, -1, NULL, }, ++ { "PR63", 24, 1, 0, -1, NULL, }, ++ { "PR63", 24, 1, 2, -1, NULL, }, ++ { "PR63", 24, 1, 2, -1, NULL, }, ++ { "PSR.ac", 28, 1, 2, 3, NULL, }, ++ { "PSR.be", 28, 1, 2, 1, NULL, }, ++ { "PSR.bn", 28, 1, 2, 44, NULL, }, ++ { "PSR.cpl", 28, 1, 2, 32, NULL, }, ++ { "PSR.da", 28, 1, 2, 38, NULL, }, ++ { "PSR.db", 28, 1, 2, 24, NULL, }, ++ { "PSR.dd", 28, 1, 2, 39, NULL, }, ++ { "PSR.dfh", 28, 1, 2, 19, NULL, }, ++ { "PSR.dfl", 28, 1, 2, 18, NULL, }, ++ { "PSR.di", 28, 1, 2, 22, NULL, }, ++ { "PSR.dt", 28, 1, 2, 17, NULL, }, ++ { "PSR.ed", 28, 1, 2, 43, NULL, }, ++ { "PSR.i", 28, 1, 2, 14, NULL, }, ++ { "PSR.ia", 28, 1, 2, 14, NULL, }, ++ { "PSR.ic", 28, 1, 2, 13, NULL, }, ++ { "PSR.id", 28, 1, 2, 14, NULL, }, ++ { "PSR.is", 28, 1, 2, 14, NULL, }, ++ { "PSR.it", 28, 1, 2, 14, NULL, }, ++ { "PSR.lp", 28, 1, 2, 25, NULL, }, ++ { "PSR.mc", 28, 1, 2, 35, NULL, }, ++ { "PSR.mfh", 28, 1, 0, 5, NULL, }, ++ { "PSR.mfh", 28, 1, 2, 5, NULL, }, ++ { "PSR.mfh", 28, 1, 2, 5, NULL, }, ++ { "PSR.mfl", 28, 1, 0, 4, NULL, }, ++ { "PSR.mfl", 28, 1, 2, 4, NULL, }, ++ { "PSR.mfl", 28, 1, 2, 4, NULL, }, ++ { "PSR.pk", 28, 1, 2, 15, NULL, }, ++ { "PSR.pp", 28, 1, 2, 21, NULL, }, ++ { "PSR.ri", 28, 1, 2, 41, NULL, }, ++ { "PSR.rt", 28, 1, 2, 27, NULL, }, ++ { "PSR.si", 28, 1, 2, 23, NULL, }, ++ { "PSR.sp", 28, 1, 2, 20, NULL, }, ++ { "PSR.ss", 28, 1, 2, 40, NULL, }, ++ { "PSR.tb", 28, 1, 2, 26, NULL, }, ++ { "PSR.up", 28, 1, 2, 2, NULL, }, ++ { "RR#", 25, 1, 2, -1, NULL, }, ++ { "RSE", 29, 1, 2, -1, NULL, }, ++ { "PR63", 24, 2, 6, -1, NULL, }, ++}; ++ ++static const unsigned short dep0[] = { ++ 96, 267, 2139, 2312, ++}; ++ ++static const unsigned short dep1[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, 4135, ++ 20613, ++}; ++ ++static const unsigned short dep2[] = { ++ 96, 267, 2165, 2166, 2168, 2169, 2171, 2172, 2174, 2329, 2332, 2333, 2336, ++ 2337, 2340, 2341, ++}; ++ ++static const unsigned short dep3[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2329, 2332, ++ 2333, 2336, 2337, 2340, 2341, 4135, 20613, ++}; ++ ++static const unsigned short dep4[] = { ++ 96, 267, 22645, 22646, 22648, 22649, 22651, 22652, 22654, 22809, 22812, 22813, ++ 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep5[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++ 22809, 22812, 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep6[] = { ++ 96, 267, 2165, 2166, 2168, 2169, 2171, 2172, 2174, 2329, 2330, 2332, 2334, ++ 2336, 2338, 2340, ++}; ++ ++static const unsigned short dep7[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2329, 2330, ++ 2333, 2334, 2337, 2338, 2341, 4135, 20613, ++}; ++ ++static const unsigned short dep8[] = { ++ 96, 267, 2165, 2166, 2168, 2169, 2171, 2172, 2174, 2329, 2331, 2333, 2335, ++ 2337, 2339, 2341, ++}; ++ ++static const unsigned short dep9[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2329, 2331, ++ 2332, 2335, 2336, 2339, 2340, 4135, 20613, ++}; ++ ++static const unsigned short dep10[] = { ++ 96, 267, 2165, 2166, 2168, 2169, 2171, 2172, 2174, 2329, 2330, 2331, 2332, ++ 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, ++}; ++ ++static const unsigned short dep11[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2329, 2330, ++ 2331, 2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 4135, 20613, ++ ++}; ++ ++static const unsigned short dep12[] = { ++ 96, 267, 2379, ++}; ++ ++static const unsigned short dep13[] = { ++ 40, 41, 96, 156, 174, 175, 267, 2082, 2083, 2165, 2167, 2168, 2170, 2171, ++ 2173, 2174, 4135, ++}; ++ ++static const unsigned short dep14[] = { ++ 96, 155, 267, 310, 2379, 28852, 29002, ++}; ++ ++static const unsigned short dep15[] = { ++ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, ++ 22, 23, 24, 25, 26, 28, 29, 30, 31, 32, 33, 40, 41, 96, 144, 156, 174, 175, ++ 267, 310, 2082, 2083, 2165, 2167, 2168, 2170, 2171, 2173, 2174, 4135, 28852, ++ 29002, ++}; ++ ++static const unsigned short dep16[] = { ++ 1, 6, 40, 96, 134, 182, 187, 226, 267, 297, 2379, 28852, 29002, ++}; ++ ++static const unsigned short dep17[] = { ++ 1, 25, 27, 38, 40, 41, 96, 156, 158, 159, 174, 175, 182, 187, 226, 267, 297, ++ 2082, 2083, 2165, 2167, 2168, 2170, 2171, 2173, 2174, 4135, 28852, 29002, ++ ++}; ++ ++static const unsigned short dep18[] = { ++ 1, 40, 51, 96, 182, 226, 233, 267, 28852, 29002, ++}; ++ ++static const unsigned short dep19[] = { ++ 1, 38, 40, 41, 96, 153, 174, 182, 226, 233, 267, 4135, 28852, 29002, ++}; ++ ++static const unsigned short dep20[] = { ++ 40, 96, 226, 267, ++}; ++ ++static const unsigned short dep21[] = { ++ 96, 174, 226, 267, ++}; ++ ++static const unsigned short dep22[] = { ++ 1, 40, 96, 128, 129, 131, 132, 133, 134, 135, 138, 139, 140, 141, 142, 143, ++ 144, 145, 146, 147, 148, 150, 151, 152, 153, 154, 155, 156, 159, 160, 161, ++ 162, 163, 164, 165, 166, 169, 170, 171, 172, 173, 174, 175, 176, 177, 182, ++ 226, 267, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, ++ 307, 308, 309, 310, 311, 312, 313, 315, 316, 318, 319, 320, 321, 322, 323, ++ 324, 325, 326, 327, 328, 28852, 29002, ++}; ++ ++static const unsigned short dep23[] = { ++ 1, 38, 40, 41, 50, 51, 55, 58, 72, 96, 134, 174, 182, 226, 267, 294, 295, ++ 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, ++ 311, 312, 313, 315, 316, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, ++ 328, 4135, 28852, 29002, ++}; ++ ++static const unsigned short dep24[] = { ++ 96, 133, 267, 296, ++}; ++ ++static const unsigned short dep25[] = { ++ 96, 134, 174, 267, 296, ++}; ++ ++static const unsigned short dep26[] = { ++ 96, 134, 267, 297, ++}; ++ ++static const unsigned short dep27[] = { ++ 25, 26, 96, 97, 100, 104, 107, 134, 156, 174, 267, 297, ++}; ++ ++static const unsigned short dep28[] = { ++ 40, 41, 96, 174, 267, 2165, 2167, 2168, 2170, 2171, 2173, 2174, 4135, ++}; ++ ++static const unsigned short dep29[] = { ++ 1, 25, 40, 96, 182, 214, 215, 226, 267, 2082, 2270, 2273, 2379, 28852, 29002, ++ ++}; ++ ++static const unsigned short dep30[] = { ++ 1, 6, 38, 40, 41, 96, 134, 156, 174, 175, 182, 214, 216, 226, 267, 2082, 2083, ++ 2165, 2167, 2168, 2170, 2171, 2173, 2174, 2271, 2273, 4135, 28852, 29002, ++ ++}; ++ ++static const unsigned short dep31[] = { ++ 96, 267, ++}; ++ ++static const unsigned short dep32[] = { ++ 96, 174, 267, 2082, 2084, ++}; ++ ++static const unsigned short dep33[] = { ++ 40, 41, 96, 156, 174, 175, 267, 2165, 2167, 2168, 2170, 2171, 2173, 2174, ++ 4135, ++}; ++ ++static const unsigned short dep34[] = { ++ 6, 37, 38, 39, 96, 124, 125, 187, 226, 267, 292, 293, 2379, ++}; ++ ++static const unsigned short dep35[] = { ++ 6, 37, 40, 41, 96, 156, 174, 175, 187, 226, 267, 292, 293, 331, 2165, 2167, ++ 2168, 2170, 2171, 2173, 2174, 4135, ++}; ++ ++static const unsigned short dep36[] = { ++ 24, 96, 213, 267, 2379, ++}; ++ ++static const unsigned short dep37[] = { ++ 24, 40, 41, 96, 156, 174, 175, 213, 267, 2165, 2167, 2168, 2170, 2171, 2173, ++ 2174, 4135, ++}; ++ ++static const unsigned short dep38[] = { ++ 6, 24, 37, 38, 39, 96, 124, 125, 187, 213, 226, 267, 292, 293, 2379, ++}; ++ ++static const unsigned short dep39[] = { ++ 6, 24, 37, 40, 41, 96, 156, 174, 175, 187, 213, 226, 267, 292, 293, 331, 2165, ++ 2167, 2168, 2170, 2171, 2173, 2174, 4135, ++}; ++ ++static const unsigned short dep40[] = { ++ 1, 6, 38, 40, 41, 96, 134, 156, 174, 175, 182, 214, 216, 226, 267, 2165, 2167, ++ 2168, 2170, 2171, 2173, 2174, 2271, 2273, 4135, 28852, 29002, ++}; ++ ++static const unsigned short dep41[] = { ++ 96, 174, 267, ++}; ++ ++static const unsigned short dep42[] = { ++ 15, 96, 196, 197, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++ 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, 22812, 22813, 22816, ++ 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep43[] = { ++ 11, 19, 20, 40, 41, 96, 174, 196, 198, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 22809, 22812, ++ 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep44[] = { ++ 15, 16, 17, 18, 96, 196, 197, 199, 200, 202, 203, 205, 206, 267, 2135, 2310, ++ 18593, 18594, 18746, 18747, 18749, 18750, 22645, 22646, 22647, 22649, 22650, ++ 22652, 22653, 22809, 22812, 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep45[] = { ++ 11, 12, 13, 14, 19, 20, 40, 41, 96, 174, 196, 198, 199, 201, 202, 204, 205, ++ 207, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 2310, 4135, 16524, 16526, ++ 18746, 18748, 18749, 18751, 22809, 22812, 22813, 22816, 22817, 22820, 22821, ++ ++}; ++ ++static const unsigned short dep46[] = { ++ 16, 96, 199, 200, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++ 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, 22812, 22813, 22816, ++ 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep47[] = { ++ 12, 19, 20, 40, 41, 96, 174, 199, 201, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 22809, 22812, ++ 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep48[] = { ++ 17, 96, 202, 203, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++ 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, 22812, 22813, 22816, ++ 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep49[] = { ++ 13, 19, 20, 40, 41, 96, 174, 202, 204, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 22809, 22812, ++ 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep50[] = { ++ 18, 96, 205, 206, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++ 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, 22812, 22813, 22816, ++ 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep51[] = { ++ 14, 19, 20, 40, 41, 96, 174, 205, 207, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 22809, 22812, ++ 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep52[] = { ++ 15, 96, 196, 197, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++ ++}; ++ ++static const unsigned short dep53[] = { ++ 11, 19, 20, 40, 41, 96, 174, 196, 198, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, ++}; ++ ++static const unsigned short dep54[] = { ++ 15, 16, 17, 18, 96, 196, 197, 199, 200, 202, 203, 205, 206, 267, 2135, 2310, ++ 18593, 18594, 18746, 18747, 18749, 18750, ++}; ++ ++static const unsigned short dep55[] = { ++ 11, 12, 13, 14, 19, 20, 40, 41, 96, 174, 196, 198, 199, 201, 202, 204, 205, ++ 207, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 2310, 4135, 16524, 16526, ++ 18746, 18748, 18749, 18751, ++}; ++ ++static const unsigned short dep56[] = { ++ 16, 96, 199, 200, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++ ++}; ++ ++static const unsigned short dep57[] = { ++ 12, 19, 20, 40, 41, 96, 174, 199, 201, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, ++}; ++ ++static const unsigned short dep58[] = { ++ 17, 96, 202, 203, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++ ++}; ++ ++static const unsigned short dep59[] = { ++ 13, 19, 20, 40, 41, 96, 174, 202, 204, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, ++}; ++ ++static const unsigned short dep60[] = { ++ 18, 96, 205, 206, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++ ++}; ++ ++static const unsigned short dep61[] = { ++ 14, 19, 20, 40, 41, 96, 174, 205, 207, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, ++}; ++ ++static const unsigned short dep62[] = { ++ 96, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++}; ++ ++static const unsigned short dep63[] = { ++ 40, 41, 96, 174, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 2310, 4135, ++ 16524, 16526, 18746, 18748, 18749, 18751, ++}; ++ ++static const unsigned short dep64[] = { ++ 11, 96, 192, 267, ++}; ++ ++static const unsigned short dep65[] = { ++ 11, 40, 41, 96, 174, 192, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep66[] = { ++ 11, 40, 41, 96, 174, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep67[] = { ++ 12, 96, 193, 267, ++}; ++ ++static const unsigned short dep68[] = { ++ 11, 40, 41, 96, 174, 193, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep69[] = { ++ 13, 96, 194, 267, ++}; ++ ++static const unsigned short dep70[] = { ++ 11, 40, 41, 96, 174, 194, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep71[] = { ++ 14, 96, 195, 267, ++}; ++ ++static const unsigned short dep72[] = { ++ 11, 40, 41, 96, 174, 195, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep73[] = { ++ 15, 96, 197, 198, 267, ++}; ++ ++static const unsigned short dep74[] = { ++ 40, 41, 96, 174, 197, 198, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep75[] = { ++ 40, 41, 96, 174, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep76[] = { ++ 16, 96, 200, 201, 267, ++}; ++ ++static const unsigned short dep77[] = { ++ 40, 41, 96, 174, 200, 201, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep78[] = { ++ 17, 96, 203, 204, 267, ++}; ++ ++static const unsigned short dep79[] = { ++ 40, 41, 96, 174, 203, 204, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep80[] = { ++ 18, 96, 206, 207, 267, ++}; ++ ++static const unsigned short dep81[] = { ++ 40, 41, 96, 174, 206, 207, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep82[] = { ++ 15, 19, 20, 40, 41, 96, 156, 174, 175, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep83[] = { ++ 15, 16, 19, 20, 40, 41, 96, 156, 174, 175, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep84[] = { ++ 15, 17, 19, 20, 40, 41, 96, 156, 174, 175, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep85[] = { ++ 15, 18, 19, 20, 40, 41, 96, 156, 174, 175, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep86[] = { ++ 15, 96, 196, 197, 267, ++}; ++ ++static const unsigned short dep87[] = { ++ 11, 19, 20, 40, 41, 96, 174, 196, 198, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep88[] = { ++ 15, 16, 17, 18, 96, 196, 197, 199, 200, 202, 203, 205, 206, 267, ++}; ++ ++static const unsigned short dep89[] = { ++ 11, 12, 13, 14, 19, 20, 40, 41, 96, 174, 196, 198, 199, 201, 202, 204, 205, ++ 207, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep90[] = { ++ 16, 96, 199, 200, 267, ++}; ++ ++static const unsigned short dep91[] = { ++ 12, 19, 20, 40, 41, 96, 174, 199, 201, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep92[] = { ++ 17, 96, 202, 203, 267, ++}; ++ ++static const unsigned short dep93[] = { ++ 13, 19, 20, 40, 41, 96, 174, 202, 204, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep94[] = { ++ 18, 96, 205, 206, 267, ++}; ++ ++static const unsigned short dep95[] = { ++ 14, 19, 20, 40, 41, 96, 174, 205, 207, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep96[] = { ++ 15, 96, 196, 197, 267, 2165, 2166, 2167, 2169, 2170, 2172, 2173, 2329, 2332, ++ 2333, 2336, 2337, 2340, 2341, ++}; ++ ++static const unsigned short dep97[] = { ++ 11, 19, 20, 40, 41, 96, 174, 196, 198, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2329, 2332, 2333, 2336, 2337, 2340, 2341, 4135, 16524, 16526, ++ ++}; ++ ++static const unsigned short dep98[] = { ++ 15, 16, 17, 18, 96, 196, 197, 199, 200, 202, 203, 205, 206, 267, 2165, 2166, ++ 2167, 2169, 2170, 2172, 2173, 2329, 2332, 2333, 2336, 2337, 2340, 2341, ++}; ++ ++static const unsigned short dep99[] = { ++ 11, 12, 13, 14, 19, 20, 40, 41, 96, 174, 196, 198, 199, 201, 202, 204, 205, ++ 207, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 2329, 2332, 2333, 2336, ++ 2337, 2340, 2341, 4135, 16524, 16526, ++}; ++ ++static const unsigned short dep100[] = { ++ 16, 96, 199, 200, 267, 2165, 2166, 2167, 2169, 2170, 2172, 2173, 2329, 2332, ++ 2333, 2336, 2337, 2340, 2341, ++}; ++ ++static const unsigned short dep101[] = { ++ 12, 19, 20, 40, 41, 96, 174, 199, 201, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2329, 2332, 2333, 2336, 2337, 2340, 2341, 4135, 16524, 16526, ++ ++}; ++ ++static const unsigned short dep102[] = { ++ 17, 96, 202, 203, 267, 2165, 2166, 2167, 2169, 2170, 2172, 2173, 2329, 2332, ++ 2333, 2336, 2337, 2340, 2341, ++}; ++ ++static const unsigned short dep103[] = { ++ 13, 19, 20, 40, 41, 96, 174, 202, 204, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2329, 2332, 2333, 2336, 2337, 2340, 2341, 4135, 16524, 16526, ++ ++}; ++ ++static const unsigned short dep104[] = { ++ 18, 96, 205, 206, 267, 2165, 2166, 2167, 2169, 2170, 2172, 2173, 2329, 2332, ++ 2333, 2336, 2337, 2340, 2341, ++}; ++ ++static const unsigned short dep105[] = { ++ 14, 19, 20, 40, 41, 96, 174, 205, 207, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 2329, 2332, 2333, 2336, 2337, 2340, 2341, 4135, 16524, 16526, ++ ++}; ++ ++static const unsigned short dep106[] = { ++ 15, 96, 196, 197, 267, 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, ++ 22812, 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep107[] = { ++ 11, 19, 20, 40, 41, 96, 174, 196, 198, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 4135, 16524, 16526, 22809, 22812, 22813, 22816, 22817, 22820, ++ 22821, ++}; ++ ++static const unsigned short dep108[] = { ++ 15, 16, 17, 18, 96, 196, 197, 199, 200, 202, 203, 205, 206, 267, 22645, 22646, ++ 22647, 22649, 22650, 22652, 22653, 22809, 22812, 22813, 22816, 22817, 22820, ++ 22821, ++}; ++ ++static const unsigned short dep109[] = { ++ 11, 12, 13, 14, 19, 20, 40, 41, 96, 174, 196, 198, 199, 201, 202, 204, 205, ++ 207, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 4135, 16524, 16526, 22809, ++ 22812, 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep110[] = { ++ 16, 96, 199, 200, 267, 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, ++ 22812, 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep111[] = { ++ 12, 19, 20, 40, 41, 96, 174, 199, 201, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 4135, 16524, 16526, 22809, 22812, 22813, 22816, 22817, 22820, ++ 22821, ++}; ++ ++static const unsigned short dep112[] = { ++ 17, 96, 202, 203, 267, 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, ++ 22812, 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep113[] = { ++ 13, 19, 20, 40, 41, 96, 174, 202, 204, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 4135, 16524, 16526, 22809, 22812, 22813, 22816, 22817, 22820, ++ 22821, ++}; ++ ++static const unsigned short dep114[] = { ++ 18, 96, 205, 206, 267, 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, ++ 22812, 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep115[] = { ++ 14, 19, 20, 40, 41, 96, 174, 205, 207, 267, 2134, 2135, 2136, 2165, 2166, ++ 2169, 2172, 4135, 16524, 16526, 22809, 22812, 22813, 22816, 22817, 22820, ++ 22821, ++}; ++ ++static const unsigned short dep116[] = { ++ 96, 267, 2165, 2166, 2167, 2169, 2170, 2172, 2173, 2329, 2332, 2333, 2336, ++ 2337, 2340, 2341, ++}; ++ ++static const unsigned short dep117[] = { ++ 40, 41, 96, 174, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 2329, 2332, ++ 2333, 2336, 2337, 2340, 2341, 4135, 16524, 16526, ++}; ++ ++static const unsigned short dep118[] = { ++ 96, 267, 22645, 22646, 22647, 22649, 22650, 22652, 22653, 22809, 22812, 22813, ++ 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep119[] = { ++ 40, 41, 96, 174, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 4135, 16524, ++ 16526, 22809, 22812, 22813, 22816, 22817, 22820, 22821, ++}; ++ ++static const unsigned short dep120[] = { ++ 19, 20, 40, 41, 96, 174, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 2310, ++ 4135, 16524, 16526, 18746, 18748, 18749, 18751, ++}; ++ ++static const unsigned short dep121[] = { ++ 40, 41, 96, 156, 174, 175, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, ++ 4135, 20613, ++}; ++ ++static const unsigned short dep122[] = { ++ 96, 267, 2083, 2084, 2271, 2272, ++}; ++ ++static const unsigned short dep123[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2270, 2272, ++ 4135, 20613, ++}; ++ ++static const unsigned short dep124[] = { ++ 40, 41, 96, 174, 267, 2082, 2084, 2165, 2166, 2169, 2172, 2312, 4135, 20613, ++ ++}; ++ ++static const unsigned short dep125[] = { ++ 96, 267, 14454, 14456, 14457, 14459, 14460, 14462, 14620, 14621, 14624, 14625, ++ 14628, 14629, ++}; ++ ++static const unsigned short dep126[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 4135, 14620, 14621, 14624, 14625, ++ 14628, 14629, 20613, 24693, 24694, 24697, 24700, ++}; ++ ++static const unsigned short dep127[] = { ++ 96, 121, 123, 124, 126, 267, 288, 289, 292, 293, ++}; ++ ++static const unsigned short dep128[] = { ++ 40, 41, 96, 174, 267, 288, 289, 292, 293, 4135, 24693, 24694, 24697, 24700, ++ ++}; ++ ++static const unsigned short dep129[] = { ++ 40, 41, 96, 174, 267, 2165, 2166, 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep130[] = { ++ 40, 41, 96, 118, 121, 124, 174, 267, 2312, 4135, 20613, 24693, ++}; ++ ++static const unsigned short dep131[] = { ++ 6, 24, 26, 27, 96, 187, 213, 216, 267, 2081, 2269, ++}; ++ ++static const unsigned short dep132[] = { ++ 40, 41, 96, 174, 187, 213, 215, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, ++ 2269, 4135, 20613, ++}; ++ ++static const unsigned short dep133[] = { ++ 6, 24, 25, 26, 40, 41, 96, 174, 267, 2081, 2165, 2166, 2169, 2172, 2312, 4135, ++ 20613, ++}; ++ ++static const unsigned short dep134[] = { ++ 0, 40, 41, 96, 156, 174, 175, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep135[] = { ++ 0, 96, 181, 267, ++}; ++ ++static const unsigned short dep136[] = { ++ 0, 40, 41, 96, 156, 174, 175, 181, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep137[] = { ++ 40, 41, 96, 174, 181, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep138[] = { ++ 2, 28, 96, 183, 217, 267, 28852, 29002, ++}; ++ ++static const unsigned short dep139[] = { ++ 1, 2, 28, 29, 96, 168, 169, 174, 183, 217, 267, 28852, 29002, ++}; ++ ++static const unsigned short dep140[] = { ++ 1, 28, 29, 38, 40, 41, 96, 168, 169, 174, 183, 217, 267, 4135, 28852, 29002, ++ ++}; ++ ++static const unsigned short dep141[] = { ++ 0, 40, 41, 96, 174, 181, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep142[] = { ++ 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, ++ 28, 29, 30, 31, 96, 182, 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, ++ 194, 195, 197, 198, 200, 201, 203, 204, 206, 207, 208, 209, 210, 211, 217, ++ 218, 219, 267, 2071, 2081, 2260, 2269, 28852, 29002, ++}; ++ ++static const unsigned short dep143[] = { ++ 29, 40, 41, 96, 134, 174, 182, 183, 184, 185, 186, 188, 189, 190, 191, 192, ++ 193, 194, 195, 197, 198, 200, 201, 203, 204, 206, 207, 208, 209, 210, 211, ++ 217, 218, 219, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2260, 2269, ++ 4135, 20613, 28852, 29002, ++}; ++ ++static const unsigned short dep144[] = { ++ 96, 267, 14463, 14465, 14466, 14468, 14497, 14498, 14513, 14630, 14631, 14651, ++ 14652, 14654, 14655, 14664, ++}; ++ ++static const unsigned short dep145[] = { ++ 40, 41, 96, 173, 174, 267, 2165, 2166, 2169, 2172, 4135, 14630, 14631, 14651, ++ 14652, 14654, 14655, 14664, ++}; ++ ++static const unsigned short dep146[] = { ++ 14463, 14465, 14466, 14468, 14497, 14498, 14513, 14630, 14631, 14651, 14652, ++ 14654, 14655, 14664, ++}; ++ ++static const unsigned short dep147[] = { ++ 173, 14630, 14631, 14651, 14652, 14654, 14655, 14664, ++}; ++ ++static const unsigned short dep148[] = { ++ 96, 267, 14464, 14465, 14467, 14468, 14476, 14477, 14478, 14479, 14480, 14481, ++ 14482, 14483, 14485, 14488, 14489, 14497, 14498, 14499, 14500, 14501, 14506, ++ 14507, 14508, 14509, 14513, 14630, 14631, 14637, 14638, 14639, 14640, 14642, ++ 14644, 14651, 14652, 14654, 14655, 14656, 14657, 14660, 14661, 14664, ++}; ++ ++static const unsigned short dep149[] = { ++ 40, 41, 72, 96, 134, 174, 267, 2165, 2166, 2169, 2172, 4135, 14630, 14631, ++ 14637, 14638, 14639, 14640, 14642, 14644, 14651, 14652, 14654, 14655, 14656, ++ 14657, 14660, 14661, 14664, ++}; ++ ++static const unsigned short dep150[] = { ++ 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, ++ 28, 29, 30, 31, 40, 41, 96, 134, 171, 174, 267, 2071, 2081, 2165, 2166, 2169, ++ 2172, 2312, 4135, 20613, 28852, ++}; ++ ++static const unsigned short dep151[] = { ++ 43, 44, 45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63, ++ 64, 65, 67, 69, 70, 71, 72, 93, 95, 96, 228, 229, 230, 231, 232, 233, 234, ++ 235, 236, 237, 238, 240, 241, 242, 243, 244, 246, 248, 249, 250, 266, 267, ++ 2116, 2295, ++}; ++ ++static const unsigned short dep152[] = { ++ 40, 41, 95, 96, 134, 153, 174, 228, 229, 230, 231, 232, 233, 234, 235, 236, ++ 237, 238, 240, 241, 242, 243, 244, 246, 248, 249, 250, 266, 267, 2137, 2138, ++ 2139, 2165, 2166, 2169, 2172, 2295, 4135, 20613, ++}; ++ ++static const unsigned short dep153[] = { ++ 59, 94, 96, 239, 266, 267, 2139, 2312, ++}; ++ ++static const unsigned short dep154[] = { ++ 40, 41, 43, 44, 46, 48, 49, 51, 52, 53, 54, 56, 57, 60, 61, 63, 64, 65, 66, ++ 67, 69, 70, 71, 93, 94, 96, 134, 153, 174, 239, 266, 267, 2107, 2116, 2165, ++ 2166, 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep155[] = { ++ 2, 28, 41, 96, 183, 217, 226, 267, 2139, 2312, 28852, 29002, ++}; ++ ++static const unsigned short dep156[] = { ++ 2, 25, 26, 28, 29, 38, 40, 41, 96, 168, 169, 174, 183, 217, 226, 267, 2312, ++ 4135, 20613, 28852, 29002, ++}; ++ ++static const unsigned short dep157[] = { ++ 96, 128, 129, 131, 132, 136, 137, 140, 141, 142, 143, 144, 145, 146, 147, ++ 149, 152, 153, 157, 158, 161, 162, 163, 164, 165, 167, 168, 170, 171, 172, ++ 173, 175, 176, 177, 267, 294, 295, 299, 301, 302, 303, 304, 306, 308, 312, ++ 315, 316, 318, 319, 320, 321, 323, 324, 325, 327, 328, ++}; ++ ++static const unsigned short dep158[] = { ++ 40, 41, 72, 96, 134, 174, 267, 294, 295, 299, 301, 302, 303, 304, 306, 308, ++ 312, 315, 316, 318, 319, 320, 321, 323, 324, 325, 327, 328, 2137, 2138, 2139, ++ 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep159[] = { ++ 96, 127, 129, 130, 132, 161, 162, 177, 267, 294, 295, 315, 316, 318, 319, ++ 328, ++}; ++ ++static const unsigned short dep160[] = { ++ 40, 41, 96, 173, 174, 267, 294, 295, 315, 316, 318, 319, 328, 2137, 2138, ++ 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep161[] = { ++ 40, 41, 96, 129, 132, 134, 137, 138, 141, 143, 145, 147, 149, 150, 152, 156, ++ 157, 159, 160, 161, 162, 164, 165, 167, 169, 170, 172, 174, 176, 177, 267, ++ 2165, 2166, 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep162[] = { ++ 40, 41, 96, 129, 132, 161, 162, 174, 177, 267, 2165, 2166, 2169, 2172, 2312, ++ 4135, 20613, ++}; ++ ++static const unsigned short dep163[] = { ++ 40, 41, 75, 76, 81, 83, 96, 110, 134, 163, 174, 178, 267, 2137, 2138, 2139, ++ 2165, 2166, 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep164[] = { ++ 40, 41, 75, 76, 81, 83, 96, 110, 134, 135, 136, 138, 139, 163, 174, 178, 267, ++ 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep165[] = { ++ 76, 77, 96, 100, 101, 254, 255, 267, 269, 270, ++}; ++ ++static const unsigned short dep166[] = { ++ 40, 41, 47, 62, 77, 79, 85, 96, 98, 101, 134, 153, 174, 178, 254, 255, 267, ++ 269, 270, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep167[] = { ++ 40, 41, 47, 62, 77, 79, 96, 98, 101, 103, 105, 134, 153, 174, 178, 254, 255, ++ 267, 269, 270, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep168[] = { ++ 96, 267, 12466, 12467, 12617, ++}; ++ ++static const unsigned short dep169[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, ++ 12617, 20613, ++}; ++ ++static const unsigned short dep170[] = { ++ 96, 267, 6218, 6219, 6396, ++}; ++ ++static const unsigned short dep171[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, ++ 6396, 20613, ++}; ++ ++static const unsigned short dep172[] = { ++ 96, 267, 6236, 6409, ++}; ++ ++static const unsigned short dep173[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, ++ 6409, 20613, ++}; ++ ++static const unsigned short dep174[] = { ++ 96, 267, 6254, 6255, 6256, 6257, 6420, 6422, 8469, ++}; ++ ++static const unsigned short dep175[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, ++ 6257, 6421, 6422, 8303, 8468, 20613, ++}; ++ ++static const unsigned short dep176[] = { ++ 96, 267, 6258, 6259, 6423, ++}; ++ ++static const unsigned short dep177[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, ++ 6423, 20613, ++}; ++ ++static const unsigned short dep178[] = { ++ 96, 267, 6260, 6424, ++}; ++ ++static const unsigned short dep179[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, ++ 6424, 20613, ++}; ++ ++static const unsigned short dep180[] = { ++ 96, 267, 10349, 10515, ++}; ++ ++static const unsigned short dep181[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, ++ 10515, 20613, ++}; ++ ++static const unsigned short dep182[] = { ++ 76, 77, 81, 82, 96, 100, 101, 254, 255, 257, 258, 267, 269, 270, ++}; ++ ++static const unsigned short dep183[] = { ++ 40, 41, 47, 62, 77, 79, 82, 85, 96, 98, 101, 134, 153, 174, 178, 254, 255, ++ 257, 259, 267, 269, 270, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++ ++}; ++ ++static const unsigned short dep184[] = { ++ 76, 77, 96, 100, 101, 103, 104, 254, 255, 267, 269, 270, 271, 272, ++}; ++ ++static const unsigned short dep185[] = { ++ 40, 41, 47, 62, 77, 79, 96, 98, 101, 103, 105, 134, 153, 174, 178, 254, 255, ++ 267, 269, 270, 271, 272, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++ ++}; ++ ++static const unsigned short dep186[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, ++ 4135, 12467, 20613, ++}; ++ ++static const unsigned short dep187[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, ++ 4135, 6218, 20613, ++}; ++ ++static const unsigned short dep188[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, ++ 4135, 6236, 20613, ++}; ++ ++static const unsigned short dep189[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, ++ 4135, 6256, 8302, 20613, ++}; ++ ++static const unsigned short dep190[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, ++ 4135, 6258, 20613, ++}; ++ ++static const unsigned short dep191[] = { ++ 40, 41, 96, 134, 173, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, ++ 2312, 4135, 6259, 6260, 20613, ++}; ++ ++static const unsigned short dep192[] = { ++ 40, 41, 96, 134, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, ++ 4135, 10349, 20613, ++}; ++ ++static const unsigned short dep193[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, 4135, ++ 6186, 20613, ++}; ++ ++static const unsigned short dep194[] = { ++ 76, 78, 79, 96, 97, 98, 99, 253, 254, 267, 268, 269, ++}; ++ ++static const unsigned short dep195[] = { ++ 40, 41, 77, 78, 82, 84, 96, 99, 101, 103, 106, 134, 174, 178, 253, 255, 267, ++ 268, 270, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep196[] = { ++ 76, 78, 79, 80, 96, 97, 98, 99, 102, 253, 254, 256, 267, 268, 269, ++}; ++ ++static const unsigned short dep197[] = { ++ 40, 41, 77, 78, 80, 82, 84, 96, 99, 101, 102, 103, 106, 134, 174, 178, 253, ++ 255, 256, 267, 268, 270, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++ ++}; ++ ++static const unsigned short dep198[] = { ++ 76, 78, 79, 83, 84, 85, 96, 97, 98, 99, 253, 254, 259, 260, 267, 268, 269, ++ ++}; ++ ++static const unsigned short dep199[] = { ++ 40, 41, 77, 78, 82, 84, 96, 99, 101, 134, 174, 178, 253, 255, 258, 260, 267, ++ 268, 270, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep200[] = { ++ 76, 78, 79, 96, 97, 98, 99, 105, 106, 107, 253, 254, 267, 268, 269, 272, 273, ++ ++}; ++ ++static const unsigned short dep201[] = { ++ 40, 41, 77, 78, 96, 99, 101, 103, 106, 134, 174, 178, 253, 255, 267, 268, ++ 270, 271, 273, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep202[] = { ++ 40, 41, 46, 70, 96, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, ++ 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep203[] = { ++ 40, 41, 96, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, ++ 4135, 20613, ++}; ++ ++static const unsigned short dep204[] = { ++ 40, 41, 76, 81, 83, 96, 134, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, ++ 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep205[] = { ++ 40, 41, 96, 156, 174, 175, 267, 2134, 2135, 2136, 2137, 2138, 2139, 2165, ++ 2166, 2169, 2172, 4135, 16524, 16526, 20613, ++}; ++ ++static const unsigned short dep206[] = { ++ 40, 41, 76, 81, 83, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, ++ 4135, 20613, ++}; ++ ++static const unsigned short dep207[] = { ++ 40, 41, 77, 78, 96, 99, 134, 174, 253, 255, 267, 268, 270, 2137, 2138, 2139, ++ 2165, 2166, 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep208[] = { ++ 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2312, ++ 4135, 20613, ++}; ++ ++static const unsigned short dep209[] = { ++ 5, 96, 186, 267, 2139, 2312, ++}; ++ ++static const unsigned short dep210[] = { ++ 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 186, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, ++ 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep211[] = { ++ 40, 41, 44, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 148, 163, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, ++ 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep212[] = { ++ 0, 96, 181, 267, 2139, 2312, ++}; ++ ++static const unsigned short dep213[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 163, 174, 178, 181, 267, 2137, 2138, 2139, 2165, 2166, 2169, ++ 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep214[] = { ++ 0, 40, 41, 44, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, ++ 136, 138, 139, 146, 148, 163, 174, 178, 181, 267, 2137, 2138, 2139, 2165, ++ 2166, 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep215[] = { ++ 31, 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 163, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, ++ 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep216[] = { ++ 0, 96, 181, 267, 2312, 26714, ++}; ++ ++static const unsigned short dep217[] = { ++ 0, 96, 108, 181, 267, 274, ++}; ++ ++static const unsigned short dep218[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 181, 267, 274, 2137, 2138, 2139, 2165, 2166, 2169, ++ 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep219[] = { ++ 0, 5, 40, 41, 75, 76, 81, 83, 96, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 163, 174, 178, 181, 267, 274, 2137, 2138, 2139, 2165, 2166, ++ 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep220[] = { ++ 0, 31, 96, 108, 181, 219, 267, 274, ++}; ++ ++static const unsigned short dep221[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 181, 219, 267, 274, 2137, 2138, 2139, 2165, 2166, ++ 2169, 2172, 4135, 20613, ++}; ++ ++static const unsigned short dep222[] = { ++ 0, 96, 108, 181, 267, 274, 2139, 2312, ++}; ++ ++static const unsigned short dep223[] = { ++ 0, 4, 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, ++ 136, 138, 139, 146, 163, 174, 178, 181, 267, 274, 2137, 2138, 2139, 2165, ++ 2166, 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep224[] = { ++ 0, 4, 5, 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, ++ 136, 138, 139, 146, 163, 174, 178, 181, 267, 274, 2137, 2138, 2139, 2165, ++ 2166, 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep225[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 163, 174, 178, 181, 267, 274, 2137, 2138, 2139, 2165, 2166, ++ 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep226[] = { ++ 40, 41, 96, 174, 267, 2134, 2135, 2136, 2165, 2166, 2169, 2172, 2312, 4135, ++ 16524, 16526, 20613, ++}; ++ ++static const unsigned short dep227[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 181, 267, 274, 2137, 2138, 2139, 2165, 2166, 2169, ++ 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep228[] = { ++ 0, 31, 96, 108, 181, 219, 267, 274, 2139, 2312, ++}; ++ ++static const unsigned short dep229[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 181, 219, 267, 274, 2137, 2138, 2139, 2165, 2166, ++ 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep230[] = { ++ 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2310, ++ 4135, 16524, 16526, 18746, 18748, 18749, 18751, 20613, ++}; ++ ++static const unsigned short dep231[] = { ++ 40, 41, 44, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 148, 163, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, ++ 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 20613, ++}; ++ ++static const unsigned short dep232[] = { ++ 0, 96, 181, 267, 2135, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++}; ++ ++static const unsigned short dep233[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 163, 174, 178, 181, 267, 2137, 2138, 2139, 2165, 2166, 2169, ++ 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 20613, ++}; ++ ++static const unsigned short dep234[] = { ++ 0, 40, 41, 44, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, ++ 136, 138, 139, 146, 148, 163, 174, 178, 181, 267, 2137, 2138, 2139, 2165, ++ 2166, 2169, 2172, 2310, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 20613, ++ ++}; ++ ++static const unsigned short dep235[] = { ++ 0, 96, 181, 267, 2136, 2310, 18593, 18594, 18746, 18747, 18749, 18750, ++}; ++ ++static const unsigned short dep236[] = { ++ 96, 267, 2135, 2139, 2310, 2312, 18593, 18594, 18746, 18747, 18749, 18750, ++ ++}; ++ ++static const unsigned short dep237[] = { ++ 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2310, ++ 2312, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 20613, ++}; ++ ++static const unsigned short dep238[] = { ++ 40, 41, 44, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 148, 163, 174, 178, 267, 2137, 2138, 2139, 2165, 2166, 2169, ++ 2172, 2310, 2312, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 20613, ++}; ++ ++static const unsigned short dep239[] = { ++ 0, 96, 181, 267, 2135, 2139, 2310, 2312, 18593, 18594, 18746, 18747, 18749, ++ 18750, ++}; ++ ++static const unsigned short dep240[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, 136, ++ 138, 139, 146, 163, 174, 178, 181, 267, 2137, 2138, 2139, 2165, 2166, 2169, ++ 2172, 2310, 2312, 4135, 16524, 16526, 18746, 18748, 18749, 18751, 20613, ++}; ++ ++static const unsigned short dep241[] = { ++ 0, 40, 41, 44, 75, 76, 81, 83, 96, 108, 110, 127, 128, 130, 131, 134, 135, ++ 136, 138, 139, 146, 148, 163, 174, 178, 181, 267, 2137, 2138, 2139, 2165, ++ 2166, 2169, 2172, 2310, 2312, 4135, 16524, 16526, 18746, 18748, 18749, 18751, ++ 20613, ++}; ++ ++static const unsigned short dep242[] = { ++ 0, 96, 181, 267, 2136, 2139, 2310, 2312, 18593, 18594, 18746, 18747, 18749, ++ 18750, ++}; ++ ++static const unsigned short dep243[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 181, 267, 274, 2134, 2135, 2136, 2137, 2138, 2139, ++ 2165, 2166, 2169, 2172, 4135, 16524, 16526, 20613, ++}; ++ ++static const unsigned short dep244[] = { ++ 40, 41, 75, 96, 134, 148, 174, 267, 2165, 2166, 2169, 2172, 4135, ++}; ++ ++static const unsigned short dep245[] = { ++ 40, 41, 75, 96, 134, 135, 139, 148, 174, 267, 2165, 2166, 2169, 2172, 4135, ++ ++}; ++ ++static const unsigned short dep246[] = { ++ 40, 41, 75, 96, 134, 148, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, ++ 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep247[] = { ++ 40, 41, 75, 96, 134, 135, 139, 148, 174, 267, 2137, 2138, 2139, 2165, 2166, ++ 2169, 2172, 2312, 4135, 20613, ++}; ++ ++static const unsigned short dep248[] = { ++ 40, 41, 96, 174, 267, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2310, 4135, ++ 16524, 16526, 18746, 18748, 18749, 18751, 20613, ++}; ++ ++static const unsigned short dep249[] = { ++ 0, 40, 41, 75, 76, 81, 83, 96, 110, 127, 128, 130, 131, 134, 135, 136, 138, ++ 139, 146, 163, 174, 178, 181, 267, 274, 2134, 2135, 2136, 2137, 2138, 2139, ++ 2165, 2166, 2169, 2172, 2312, 4135, 16524, 16526, 20613, ++}; ++ ++static const unsigned short dep250[] = { ++ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, ++ 22, 24, 26, 27, 28, 29, 30, 31, 96, 182, 183, 184, 185, 186, 187, 188, 189, ++ 190, 191, 192, 193, 194, 195, 197, 198, 200, 201, 203, 204, 206, 207, 208, ++ 209, 210, 211, 213, 216, 217, 218, 219, 267, 2071, 2081, 2139, 2260, 2269, ++ 2312, 28852, 29002, ++}; ++ ++static const unsigned short dep251[] = { ++ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, ++ 22, 24, 25, 26, 28, 29, 30, 31, 40, 41, 96, 134, 171, 174, 182, 183, 184, ++ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 197, 198, 200, 201, ++ 203, 204, 206, 207, 208, 209, 210, 211, 213, 215, 217, 218, 219, 267, 2071, ++ 2081, 2137, 2138, 2139, 2165, 2166, 2169, 2172, 2260, 2269, 2312, 4135, 20613, ++ 28852, 29002, ++}; ++ ++#define NELS(X) (sizeof(X)/sizeof(X[0])) ++static const struct ia64_opcode_dependency ++op_dependencies[] = { ++ { NELS(dep1), dep1, NELS(dep0), dep0, }, ++ { NELS(dep3), dep3, NELS(dep2), dep2, }, ++ { NELS(dep5), dep5, NELS(dep4), dep4, }, ++ { NELS(dep7), dep7, NELS(dep6), dep6, }, ++ { NELS(dep9), dep9, NELS(dep8), dep8, }, ++ { NELS(dep11), dep11, NELS(dep10), dep10, }, ++ { NELS(dep13), dep13, NELS(dep12), dep12, }, ++ { NELS(dep15), dep15, NELS(dep14), dep14, }, ++ { NELS(dep17), dep17, NELS(dep16), dep16, }, ++ { NELS(dep19), dep19, NELS(dep18), dep18, }, ++ { NELS(dep21), dep21, NELS(dep20), dep20, }, ++ { NELS(dep23), dep23, NELS(dep22), dep22, }, ++ { NELS(dep25), dep25, NELS(dep24), dep24, }, ++ { NELS(dep27), dep27, NELS(dep26), dep26, }, ++ { NELS(dep28), dep28, NELS(dep12), dep12, }, ++ { NELS(dep30), dep30, NELS(dep29), dep29, }, ++ { NELS(dep32), dep32, NELS(dep31), dep31, }, ++ { NELS(dep33), dep33, NELS(dep12), dep12, }, ++ { NELS(dep35), dep35, NELS(dep34), dep34, }, ++ { NELS(dep37), dep37, NELS(dep36), dep36, }, ++ { NELS(dep39), dep39, NELS(dep38), dep38, }, ++ { NELS(dep40), dep40, NELS(dep29), dep29, }, ++ { NELS(dep41), dep41, NELS(dep31), dep31, }, ++ { NELS(dep43), dep43, NELS(dep42), dep42, }, ++ { NELS(dep45), dep45, NELS(dep44), dep44, }, ++ { NELS(dep47), dep47, NELS(dep46), dep46, }, ++ { NELS(dep49), dep49, NELS(dep48), dep48, }, ++ { NELS(dep51), dep51, NELS(dep50), dep50, }, ++ { NELS(dep53), dep53, NELS(dep52), dep52, }, ++ { NELS(dep55), dep55, NELS(dep54), dep54, }, ++ { NELS(dep57), dep57, NELS(dep56), dep56, }, ++ { NELS(dep59), dep59, NELS(dep58), dep58, }, ++ { NELS(dep61), dep61, NELS(dep60), dep60, }, ++ { NELS(dep63), dep63, NELS(dep62), dep62, }, ++ { NELS(dep65), dep65, NELS(dep64), dep64, }, ++ { NELS(dep66), dep66, NELS(dep31), dep31, }, ++ { NELS(dep68), dep68, NELS(dep67), dep67, }, ++ { NELS(dep70), dep70, NELS(dep69), dep69, }, ++ { NELS(dep72), dep72, NELS(dep71), dep71, }, ++ { NELS(dep74), dep74, NELS(dep73), dep73, }, ++ { NELS(dep75), dep75, NELS(dep31), dep31, }, ++ { NELS(dep77), dep77, NELS(dep76), dep76, }, ++ { NELS(dep79), dep79, NELS(dep78), dep78, }, ++ { NELS(dep81), dep81, NELS(dep80), dep80, }, ++ { NELS(dep82), dep82, NELS(dep31), dep31, }, ++ { NELS(dep83), dep83, NELS(dep31), dep31, }, ++ { NELS(dep84), dep84, NELS(dep31), dep31, }, ++ { NELS(dep85), dep85, NELS(dep31), dep31, }, ++ { NELS(dep87), dep87, NELS(dep86), dep86, }, ++ { NELS(dep89), dep89, NELS(dep88), dep88, }, ++ { NELS(dep91), dep91, NELS(dep90), dep90, }, ++ { NELS(dep93), dep93, NELS(dep92), dep92, }, ++ { NELS(dep95), dep95, NELS(dep94), dep94, }, ++ { NELS(dep97), dep97, NELS(dep96), dep96, }, ++ { NELS(dep99), dep99, NELS(dep98), dep98, }, ++ { NELS(dep101), dep101, NELS(dep100), dep100, }, ++ { NELS(dep103), dep103, NELS(dep102), dep102, }, ++ { NELS(dep105), dep105, NELS(dep104), dep104, }, ++ { NELS(dep107), dep107, NELS(dep106), dep106, }, ++ { NELS(dep109), dep109, NELS(dep108), dep108, }, ++ { NELS(dep111), dep111, NELS(dep110), dep110, }, ++ { NELS(dep113), dep113, NELS(dep112), dep112, }, ++ { NELS(dep115), dep115, NELS(dep114), dep114, }, ++ { NELS(dep117), dep117, NELS(dep116), dep116, }, ++ { NELS(dep119), dep119, NELS(dep118), dep118, }, ++ { NELS(dep120), dep120, NELS(dep62), dep62, }, ++ { NELS(dep121), dep121, NELS(dep31), dep31, }, ++ { NELS(dep123), dep123, NELS(dep122), dep122, }, ++ { NELS(dep124), dep124, NELS(dep0), dep0, }, ++ { NELS(dep126), dep126, NELS(dep125), dep125, }, ++ { NELS(dep128), dep128, NELS(dep127), dep127, }, ++ { NELS(dep129), dep129, NELS(dep0), dep0, }, ++ { NELS(dep130), dep130, NELS(dep0), dep0, }, ++ { NELS(dep132), dep132, NELS(dep131), dep131, }, ++ { NELS(dep133), dep133, NELS(dep0), dep0, }, ++ { NELS(dep134), dep134, NELS(dep31), dep31, }, ++ { NELS(dep136), dep136, NELS(dep135), dep135, }, ++ { NELS(dep137), dep137, NELS(dep135), dep135, }, ++ { NELS(dep139), dep139, NELS(dep138), dep138, }, ++ { NELS(dep140), dep140, NELS(dep138), dep138, }, ++ { NELS(dep141), dep141, NELS(dep135), dep135, }, ++ { NELS(dep143), dep143, NELS(dep142), dep142, }, ++ { NELS(dep145), dep145, NELS(dep144), dep144, }, ++ { NELS(dep147), dep147, NELS(dep146), dep146, }, ++ { NELS(dep149), dep149, NELS(dep148), dep148, }, ++ { NELS(dep150), dep150, NELS(dep0), dep0, }, ++ { NELS(dep152), dep152, NELS(dep151), dep151, }, ++ { NELS(dep154), dep154, NELS(dep153), dep153, }, ++ { NELS(dep156), dep156, NELS(dep155), dep155, }, ++ { NELS(dep158), dep158, NELS(dep157), dep157, }, ++ { NELS(dep160), dep160, NELS(dep159), dep159, }, ++ { NELS(dep161), dep161, NELS(dep0), dep0, }, ++ { NELS(dep162), dep162, NELS(dep0), dep0, }, ++ { NELS(dep163), dep163, NELS(dep0), dep0, }, ++ { NELS(dep164), dep164, NELS(dep31), dep31, }, ++ { NELS(dep166), dep166, NELS(dep165), dep165, }, ++ { NELS(dep167), dep167, NELS(dep165), dep165, }, ++ { NELS(dep169), dep169, NELS(dep168), dep168, }, ++ { NELS(dep171), dep171, NELS(dep170), dep170, }, ++ { NELS(dep173), dep173, NELS(dep172), dep172, }, ++ { NELS(dep175), dep175, NELS(dep174), dep174, }, ++ { NELS(dep177), dep177, NELS(dep176), dep176, }, ++ { NELS(dep179), dep179, NELS(dep178), dep178, }, ++ { NELS(dep181), dep181, NELS(dep180), dep180, }, ++ { NELS(dep183), dep183, NELS(dep182), dep182, }, ++ { NELS(dep185), dep185, NELS(dep184), dep184, }, ++ { NELS(dep186), dep186, NELS(dep0), dep0, }, ++ { NELS(dep187), dep187, NELS(dep0), dep0, }, ++ { NELS(dep188), dep188, NELS(dep0), dep0, }, ++ { NELS(dep189), dep189, NELS(dep0), dep0, }, ++ { NELS(dep190), dep190, NELS(dep0), dep0, }, ++ { NELS(dep191), dep191, NELS(dep0), dep0, }, ++ { NELS(dep192), dep192, NELS(dep0), dep0, }, ++ { NELS(dep193), dep193, NELS(dep0), dep0, }, ++ { NELS(dep195), dep195, NELS(dep194), dep194, }, ++ { NELS(dep197), dep197, NELS(dep196), dep196, }, ++ { NELS(dep199), dep199, NELS(dep198), dep198, }, ++ { NELS(dep201), dep201, NELS(dep200), dep200, }, ++ { NELS(dep202), dep202, NELS(dep0), dep0, }, ++ { NELS(dep203), dep203, NELS(dep0), dep0, }, ++ { NELS(dep204), dep204, NELS(dep0), dep0, }, ++ { NELS(dep205), dep205, NELS(dep31), dep31, }, ++ { NELS(dep206), dep206, NELS(dep31), dep31, }, ++ { NELS(dep207), dep207, NELS(dep194), dep194, }, ++ { NELS(dep208), dep208, NELS(dep0), dep0, }, ++ { NELS(dep210), dep210, NELS(dep209), dep209, }, ++ { NELS(dep211), dep211, NELS(dep0), dep0, }, ++ { NELS(dep213), dep213, NELS(dep212), dep212, }, ++ { NELS(dep214), dep214, NELS(dep212), dep212, }, ++ { NELS(dep215), dep215, NELS(dep0), dep0, }, ++ { NELS(dep213), dep213, NELS(dep216), dep216, }, ++ { NELS(dep218), dep218, NELS(dep217), dep217, }, ++ { NELS(dep219), dep219, NELS(dep217), dep217, }, ++ { NELS(dep221), dep221, NELS(dep220), dep220, }, ++ { NELS(dep223), dep223, NELS(dep222), dep222, }, ++ { NELS(dep224), dep224, NELS(dep222), dep222, }, ++ { NELS(dep225), dep225, NELS(dep222), dep222, }, ++ { NELS(dep226), dep226, NELS(dep0), dep0, }, ++ { NELS(dep227), dep227, NELS(dep222), dep222, }, ++ { NELS(dep229), dep229, NELS(dep228), dep228, }, ++ { NELS(dep230), dep230, NELS(dep62), dep62, }, ++ { NELS(dep231), dep231, NELS(dep62), dep62, }, ++ { NELS(dep233), dep233, NELS(dep232), dep232, }, ++ { NELS(dep234), dep234, NELS(dep232), dep232, }, ++ { NELS(dep233), dep233, NELS(dep235), dep235, }, ++ { NELS(dep237), dep237, NELS(dep236), dep236, }, ++ { NELS(dep238), dep238, NELS(dep236), dep236, }, ++ { NELS(dep240), dep240, NELS(dep239), dep239, }, ++ { NELS(dep241), dep241, NELS(dep239), dep239, }, ++ { NELS(dep240), dep240, NELS(dep242), dep242, }, ++ { NELS(dep243), dep243, NELS(dep217), dep217, }, ++ { NELS(dep244), dep244, NELS(dep31), dep31, }, ++ { NELS(dep245), dep245, NELS(dep31), dep31, }, ++ { NELS(dep246), dep246, NELS(dep0), dep0, }, ++ { NELS(dep247), dep247, NELS(dep0), dep0, }, ++ { NELS(dep248), dep248, NELS(dep62), dep62, }, ++ { NELS(dep249), dep249, NELS(dep222), dep222, }, ++ { 0, NULL, 0, NULL, }, ++ { NELS(dep251), dep251, NELS(dep250), dep250, }, ++}; ++ ++static const struct ia64_completer_table ++completer_table[] = { ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 88 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 88 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 576, -1, 0, 1, 6 }, ++ { 0x0, 0x0, 0, 639, -1, 0, 1, 17 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 157 }, ++ { 0x0, 0x0, 0, 738, -1, 0, 1, 17 }, ++ { 0x0, 0x0, 0, 2164, -1, 0, 1, 10 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 9 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 13 }, ++ { 0x1, 0x1, 0, -1, -1, 13, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, 2372, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, 1122, -1, 0, 1, 122 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 44 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 78 }, ++ { 0x0, 0x0, 0, 2212, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, 2439, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, 2216, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, 2218, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, 2448, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, 2451, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, 2473, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, 2476, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 24 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 24 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 24 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 24 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 35 }, ++ { 0x0, 0x0, 0, 2484, -1, 0, 1, 29 }, ++ { 0x0, 0x0, 0, 1391, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 157 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 77 }, ++ { 0x0, 0x0, 0, 1439, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1448, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1457, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1459, -1, 0, 1, 125 }, ++ { 0x0, 0x0, 0, 1461, -1, 0, 1, 125 }, ++ { 0x0, 0x0, 0, 1470, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1479, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1488, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1497, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1506, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1515, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1525, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1535, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1545, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 0, 1554, -1, 0, 1, 140 }, ++ { 0x0, 0x0, 0, 1560, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1566, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1572, -1, 0, 1, 140 }, ++ { 0x0, 0x0, 0, 1578, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1584, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1590, -1, 0, 1, 140 }, ++ { 0x0, 0x0, 0, 1596, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1602, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1608, -1, 0, 1, 140 }, ++ { 0x0, 0x0, 0, 1614, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1620, -1, 0, 1, 140 }, ++ { 0x0, 0x0, 0, 1626, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1632, -1, 0, 1, 140 }, ++ { 0x0, 0x0, 0, 1638, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1644, -1, 0, 1, 140 }, ++ { 0x0, 0x0, 0, 1650, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1656, -1, 0, 1, 145 }, ++ { 0x0, 0x0, 0, 1660, -1, 0, 1, 151 }, ++ { 0x0, 0x0, 0, 1664, -1, 0, 1, 153 }, ++ { 0x0, 0x0, 0, 1668, -1, 0, 1, 153 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 79 }, ++ { 0x0, 0x0, 0, 256, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 67 }, ++ { 0x1, 0x1, 0, 1148, -1, 20, 1, 67 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 68 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 69 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 69 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 70 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 71 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 72 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 86 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 87 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 89 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 90 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 91 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 92 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 97 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 98 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 99 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 100 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 101 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 102 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 103 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 106 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 107 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 108 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 109 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 110 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 111 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 112 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 113 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 158 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 158 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 158 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 71 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 157 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2824, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2825, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2176, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2177, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2839, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2840, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2841, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2842, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2843, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2826, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, 2827, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 11 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 84 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 83 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x1, 0x1, 0, -1, -1, 13, 1, 0 }, ++ { 0x0, 0x0, 0, 2845, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 84 }, ++ { 0x0, 0x0, 0, 1948, -1, 0, 1, 131 }, ++ { 0x0, 0x0, 0, 1950, -1, 0, 1, 138 }, ++ { 0x0, 0x0, 0, 1952, -1, 0, 1, 132 }, ++ { 0x0, 0x0, 0, 1954, -1, 0, 1, 132 }, ++ { 0x0, 0x0, 0, 1956, -1, 0, 1, 131 }, ++ { 0x0, 0x0, 0, 1958, -1, 0, 1, 138 }, ++ { 0x0, 0x0, 0, 1960, -1, 0, 1, 131 }, ++ { 0x0, 0x0, 0, 1962, -1, 0, 1, 138 }, ++ { 0x0, 0x0, 0, 1965, -1, 0, 1, 131 }, ++ { 0x0, 0x0, 0, 1968, -1, 0, 1, 138 }, ++ { 0x0, 0x0, 0, 1971, -1, 0, 1, 150 }, ++ { 0x0, 0x0, 0, 1972, -1, 0, 1, 156 }, ++ { 0x0, 0x0, 0, 1973, -1, 0, 1, 150 }, ++ { 0x0, 0x0, 0, 1974, -1, 0, 1, 156 }, ++ { 0x0, 0x0, 0, 1975, -1, 0, 1, 150 }, ++ { 0x0, 0x0, 0, 1976, -1, 0, 1, 156 }, ++ { 0x0, 0x0, 0, 1977, -1, 0, 1, 150 }, ++ { 0x0, 0x0, 0, 1978, -1, 0, 1, 156 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 82 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 120 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 118 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 120 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 119 }, ++ { 0x0, 0x0, 0, 1669, -1, 0, 1, 136 }, ++ { 0x0, 0x0, 0, 1670, -1, 0, 1, 136 }, ++ { 0x0, 0x0, 0, 1671, -1, 0, 1, 136 }, ++ { 0x0, 0x0, 0, 1672, -1, 0, 1, 136 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 1, 223, -1, 0, 1, 12 }, ++ { 0x1, 0x1, 2, -1, -1, 27, 1, 12 }, ++ { 0x0, 0x0, 3, -1, 1322, 0, 0, -1 }, ++ { 0x0, 0x0, 3, -1, 1323, 0, 0, -1 }, ++ { 0x1, 0x1, 3, 2715, 1432, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2716, 1441, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2717, 1450, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2718, 1463, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2719, 1472, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2720, 1481, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2721, 1490, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2722, 1499, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2723, 1508, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2724, 1517, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2725, 1527, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2726, 1537, 33, 1, 127 }, ++ { 0x1, 0x1, 3, 2727, 1550, 33, 1, 142 }, ++ { 0x1, 0x1, 3, 2728, 1556, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2729, 1562, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2730, 1568, 33, 1, 142 }, ++ { 0x1, 0x1, 3, 2731, 1574, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2732, 1580, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2733, 1586, 33, 1, 142 }, ++ { 0x1, 0x1, 3, 2734, 1592, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2735, 1598, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2736, 1604, 33, 1, 142 }, ++ { 0x1, 0x1, 3, 2737, 1610, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2738, 1616, 33, 1, 142 }, ++ { 0x1, 0x1, 3, 2739, 1622, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2740, 1628, 33, 1, 142 }, ++ { 0x1, 0x1, 3, 2741, 1634, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2742, 1640, 33, 1, 142 }, ++ { 0x1, 0x1, 3, 2743, 1646, 33, 1, 147 }, ++ { 0x1, 0x1, 3, 2744, 1652, 33, 1, 147 }, ++ { 0x1, 0x1, 3, -1, -1, 27, 1, 40 }, ++ { 0x0, 0x0, 4, 2178, 1407, 0, 1, 135 }, ++ { 0x0, 0x0, 4, 2179, 1409, 0, 1, 135 }, ++ { 0x0, 0x0, 4, 2180, 1411, 0, 1, 134 }, ++ { 0x0, 0x0, 4, 2181, 1413, 0, 1, 134 }, ++ { 0x0, 0x0, 4, 2182, 1415, 0, 1, 134 }, ++ { 0x0, 0x0, 4, 2183, 1417, 0, 1, 134 }, ++ { 0x0, 0x0, 4, 2184, 1419, 0, 1, 134 }, ++ { 0x0, 0x0, 4, 2185, 1421, 0, 1, 134 }, ++ { 0x0, 0x0, 4, 2186, 1423, 0, 1, 134 }, ++ { 0x0, 0x0, 4, 2187, 1425, 0, 1, 134 }, ++ { 0x0, 0x0, 4, 2188, 1427, 0, 1, 136 }, ++ { 0x0, 0x0, 4, 2189, 1429, 0, 1, 136 }, ++ { 0x1, 0x1, 4, -1, 1436, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 534, 1435, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1445, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 535, 1444, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1454, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 536, 1453, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1458, 32, 1, 125 }, ++ { 0x1, 0x1, 4, -1, 1460, 32, 1, 125 }, ++ { 0x1, 0x1, 4, -1, 1467, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 537, 1466, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1476, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 538, 1475, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1485, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 539, 1484, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1494, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 540, 1493, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1503, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 541, 1502, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1512, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 542, 1511, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1522, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 1018, 1520, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1532, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 1019, 1530, 32, 1, 124 }, ++ { 0x1, 0x1, 4, -1, 1542, 33, 1, 130 }, ++ { 0x5, 0x5, 4, 1020, 1540, 32, 1, 124 }, ++ { 0x1, 0x21, 10, 1991, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 1992, -1, 12, 1, 3 }, ++ { 0x1, 0x21, 10, 410, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2048, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, -1, 2049, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2050, 0, 0, -1 }, ++ { 0x0, 0x0, 10, 1995, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 10, 1996, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 1997, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 1998, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, 420, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 10, 2054, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 424, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2056, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, 428, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 10, 2058, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 432, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2060, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, 436, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 10, 2062, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 440, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2064, -1, 12, 1, 3 }, ++ { 0x1, 0x21, 10, 2011, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2012, -1, 12, 1, 3 }, ++ { 0x1, 0x21, 10, 450, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2070, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, -1, 2071, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2072, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2075, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2076, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2077, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2078, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2079, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2080, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2081, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2082, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2083, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2084, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2085, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2086, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2087, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2088, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2089, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2090, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2091, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2092, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2093, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2094, 0, 0, -1 }, ++ { 0x1, 0x21, 10, 2015, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2016, -1, 12, 1, 3 }, ++ { 0x1, 0x21, 10, 458, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2096, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, -1, 2097, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2098, 0, 0, -1 }, ++ { 0x0, 0x0, 10, 2019, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 10, 2020, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 2021, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2022, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, 468, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 10, 2102, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 472, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2104, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, 476, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 10, 2106, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 480, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2108, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, 484, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 10, 2110, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 488, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2112, -1, 12, 1, 3 }, ++ { 0x1, 0x21, 10, 2035, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2036, -1, 12, 1, 3 }, ++ { 0x1, 0x21, 10, 498, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 10, 2118, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, -1, 2119, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2120, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2123, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2124, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2125, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2126, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2127, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2128, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2129, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2130, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2131, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2132, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2133, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2134, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2135, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2136, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2137, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2138, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2139, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2140, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2141, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2142, 0, 0, -1 }, ++ { 0x1, 0x1, 10, 2039, -1, 36, 1, 3 }, ++ { 0x1000001, 0x1000001, 10, 2040, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 2041, -1, 36, 1, 3 }, ++ { 0x1000001, 0x1000001, 10, 2042, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, -1, 2143, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2145, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2147, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2149, 0, 0, -1 }, ++ { 0x1, 0x1, 10, 2043, -1, 36, 1, 3 }, ++ { 0x1000001, 0x1000001, 10, 2044, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 10, 2045, -1, 36, 1, 3 }, ++ { 0x1000001, 0x1000001, 10, 2046, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 10, -1, 2151, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2153, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2155, 0, 0, -1 }, ++ { 0x0, 0x0, 10, -1, 2157, 0, 0, -1 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x4200001, 11, 1993, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 298, -1, 33, 1, 3 }, ++ { 0x0, 0x0, 11, 2051, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 11, 2052, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 1999, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x0, 0x0, 11, 306, -1, 0, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x200001, 11, 2001, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 308, -1, 33, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 2003, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x0, 0x0, 11, 310, -1, 0, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x200001, 11, 2005, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 312, -1, 33, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 2007, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x0, 0x0, 11, 314, -1, 0, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x200001, 11, 2009, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 316, -1, 33, 1, 3 }, ++ { 0x0, 0x0, 11, 2065, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 11, 2066, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 11, 2067, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 11, 2068, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x4200001, 11, 2013, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 320, -1, 33, 1, 3 }, ++ { 0x0, 0x0, 11, 2073, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 11, 2074, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x4200001, 11, 2017, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 346, -1, 33, 1, 3 }, ++ { 0x0, 0x0, 11, 2099, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 11, 2100, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 2023, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x0, 0x0, 11, 354, -1, 0, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x200001, 11, 2025, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 356, -1, 33, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 2027, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x0, 0x0, 11, 358, -1, 0, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x200001, 11, 2029, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 360, -1, 33, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 2031, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x0, 0x0, 11, 362, -1, 0, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x200001, 11, 2033, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 364, -1, 33, 1, 3 }, ++ { 0x0, 0x0, 11, 2113, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 11, 2114, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 11, 2115, -1, 33, 1, 3 }, ++ { 0x200001, 0x200001, 11, 2116, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x200001, 0x4200001, 11, 2037, -1, 12, 1, 3 }, ++ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, ++ { 0x1, 0x1, 11, 368, -1, 33, 1, 3 }, ++ { 0x0, 0x0, 11, 2121, -1, 0, 1, 3 }, ++ { 0x1, 0x1, 11, 2122, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, ++ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, ++ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, ++ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, ++ { 0x1, 0x1, 11, 2144, -1, 36, 1, 3 }, ++ { 0x1000001, 0x1000001, 11, 2146, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 11, 2148, -1, 36, 1, 3 }, ++ { 0x1000001, 0x1000001, 11, 2150, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, ++ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, ++ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, ++ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, ++ { 0x1, 0x1, 11, 2152, -1, 36, 1, 3 }, ++ { 0x1000001, 0x1000001, 11, 2154, -1, 12, 1, 3 }, ++ { 0x1, 0x1, 11, 2156, -1, 36, 1, 3 }, ++ { 0x1000001, 0x1000001, 11, 2158, -1, 12, 1, 3 }, ++ { 0x0, 0x0, 12, -1, -1, 0, 1, 14 }, ++ { 0x0, 0x0, 12, -1, -1, 0, 1, 14 }, ++ { 0x0, 0x0, 12, -1, -1, 0, 1, 14 }, ++ { 0x1, 0x1, 13, 270, 1434, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 272, 1443, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 274, 1452, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 278, 1465, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 280, 1474, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 282, 1483, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 284, 1492, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 286, 1501, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 288, 1510, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 290, 1519, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 292, 1529, 34, 1, 124 }, ++ { 0x1, 0x1, 13, 294, 1539, 34, 1, 124 }, ++ { 0x0, 0x0, 19, -1, 777, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 778, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 779, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 780, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 781, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 782, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 783, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 784, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 785, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 786, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 787, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 788, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 789, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 790, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 791, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 792, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 793, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 794, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 795, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 796, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 797, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 798, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 799, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 800, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 801, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 802, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 803, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 804, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 805, 0, 0, -1 }, ++ { 0x0, 0x0, 19, -1, 806, 0, 0, -1 }, ++ { 0x0, 0x0, 20, -1, 2793, 0, 0, -1 }, ++ { 0x0, 0x0, 20, -1, 2794, 0, 0, -1 }, ++ { 0x0, 0x0, 20, -1, 2809, 0, 0, -1 }, ++ { 0x0, 0x0, 20, -1, 2810, 0, 0, -1 }, ++ { 0x0, 0x0, 20, -1, 2815, 0, 0, -1 }, ++ { 0x0, 0x0, 20, -1, 2816, 0, 0, -1 }, ++ { 0x0, 0x0, 21, 813, 2805, 0, 0, -1 }, ++ { 0x0, 0x0, 21, 814, 2807, 0, 0, -1 }, ++ { 0x0, 0x0, 23, -1, 2803, 0, 0, -1 }, ++ { 0x0, 0x0, 23, -1, 2804, 0, 0, -1 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, 1254, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 15 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, 1275, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, 1308, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 17 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, ++ { 0x1, 0x1, 24, -1, -1, 33, 1, 76 }, ++ { 0x1, 0x1, 24, -1, -1, 33, 1, 76 }, ++ { 0x1, 0x1, 24, 1324, 1437, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1325, 1446, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1326, 1455, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1327, 1468, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1328, 1477, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1329, 1486, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1330, 1495, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1331, 1504, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1332, 1513, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1333, 1523, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1334, 1533, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1335, 1543, 35, 1, 130 }, ++ { 0x1, 0x1, 24, 1336, 1552, 35, 1, 144 }, ++ { 0x1, 0x1, 24, 1337, 1558, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1338, 1564, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1339, 1570, 35, 1, 144 }, ++ { 0x1, 0x1, 24, 1340, 1576, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1341, 1582, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1342, 1588, 35, 1, 144 }, ++ { 0x1, 0x1, 24, 1343, 1594, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1344, 1600, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1345, 1606, 35, 1, 144 }, ++ { 0x1, 0x1, 24, 1346, 1612, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1347, 1618, 35, 1, 144 }, ++ { 0x1, 0x1, 24, 1348, 1624, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1349, 1630, 35, 1, 144 }, ++ { 0x1, 0x1, 24, 1350, 1636, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1351, 1642, 35, 1, 144 }, ++ { 0x1, 0x1, 24, 1352, 1648, 35, 1, 149 }, ++ { 0x1, 0x1, 24, 1353, 1654, 35, 1, 149 }, ++ { 0x0, 0x0, 33, 2787, 2785, 0, 0, -1 }, ++ { 0x0, 0x0, 33, 2790, 2788, 0, 0, -1 }, ++ { 0x0, 0x0, 33, 2796, 2795, 0, 0, -1 }, ++ { 0x0, 0x0, 33, 2798, 2797, 0, 0, -1 }, ++ { 0x0, 0x0, 33, 2812, 2811, 0, 0, -1 }, ++ { 0x0, 0x0, 33, 2814, 2813, 0, 0, -1 }, ++ { 0x0, 0x0, 35, -1, 2806, 0, 0, -1 }, ++ { 0x0, 0x0, 35, -1, 2808, 0, 0, -1 }, ++ { 0x1, 0x1, 38, -1, 2256, 37, 1, 29 }, ++ { 0x1, 0x1, 38, -1, 2315, 37, 1, 29 }, ++ { 0x0, 0x0, 38, -1, 2318, 0, 0, -1 }, ++ { 0x1, 0x1, 38, -1, -1, 37, 1, 29 }, ++ { 0x1, 0x1, 38, -1, 2323, 37, 1, 29 }, ++ { 0x0, 0x0, 38, -1, 2326, 0, 0, -1 }, ++ { 0x1, 0x1, 38, -1, -1, 37, 1, 29 }, ++ { 0x0, 0x0, 38, -1, 2329, 0, 0, -1 }, ++ { 0x1, 0x1, 38, -1, -1, 37, 1, 29 }, ++ { 0x1, 0x1, 38, -1, 2332, 37, 1, 29 }, ++ { 0x1, 0x1, 38, -1, 2335, 37, 1, 29 }, ++ { 0x1, 0x1, 38, -1, 2368, 37, 1, 29 }, ++ { 0x3, 0x3, 38, -1, -1, 30, 1, 137 }, ++ { 0x0, 0x0, 38, 1124, -1, 0, 1, 95 }, ++ { 0x0, 0x0, 38, -1, -1, 0, 1, 104 }, ++ { 0x0, 0x0, 38, 1130, -1, 0, 1, 116 }, ++ { 0x3, 0x3, 38, -1, -1, 30, 1, 155 }, ++ { 0x0, 0x0, 38, 1131, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 40, -1, 955, 0, 0, -1 }, ++ { 0x0, 0x0, 40, -1, 963, 0, 0, -1 }, ++ { 0x0, 0x0, 40, 1133, 959, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 604, 33, 1, 6 }, ++ { 0x18000001, 0x18000001, 40, -1, 612, 6, 1, 7 }, ++ { 0x3, 0x3, 40, 1134, 608, 33, 1, 6 }, ++ { 0x0, 0x0, 40, -1, 967, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 624, 33, 1, 8 }, ++ { 0x0, 0x0, 40, -1, 971, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 636, 33, 1, 15 }, ++ { 0x0, 0x0, 40, -1, 976, 0, 0, -1 }, ++ { 0x0, 0x0, 40, -1, 980, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 659, 33, 1, 17 }, ++ { 0x3, 0x3, 40, -1, 663, 33, 1, 17 }, ++ { 0x0, 0x0, 40, -1, 984, 0, 0, -1 }, ++ { 0x0, 0x0, 40, -1, 988, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 683, 33, 1, 18 }, ++ { 0x18000001, 0x18000001, 40, -1, 687, 6, 1, 18 }, ++ { 0x0, 0x0, 40, -1, 992, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 699, 33, 1, 19 }, ++ { 0x0, 0x0, 40, -1, 996, 0, 0, -1 }, ++ { 0x0, 0x0, 40, -1, 1000, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 719, 33, 1, 20 }, ++ { 0x18000001, 0x18000001, 40, -1, 723, 6, 1, 20 }, ++ { 0x0, 0x0, 40, -1, 1004, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 735, 33, 1, 21 }, ++ { 0x0, 0x0, 40, -1, 1009, 0, 0, -1 }, ++ { 0x0, 0x0, 40, -1, 1013, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 758, 33, 1, 17 }, ++ { 0x3, 0x3, 40, -1, 762, 33, 1, 17 }, ++ { 0x0, 0x0, 40, -1, 1017, 0, 0, -1 }, ++ { 0x3, 0x3, 40, -1, 774, 33, 1, 21 }, ++ { 0x0, 0x0, 41, 833, 954, 0, 0, -1 }, ++ { 0x0, 0x0, 41, 834, 962, 0, 0, -1 }, ++ { 0x0, 0x0, 41, 835, 958, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 836, 603, 34, 1, 6 }, ++ { 0x10000001, 0x10000001, 41, 837, 611, 6, 1, 7 }, ++ { 0x1, 0x1, 41, 838, 607, 34, 1, 6 }, ++ { 0x0, 0x0, 41, 839, 966, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 840, 623, 34, 1, 8 }, ++ { 0x0, 0x0, 41, 841, 970, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 842, 635, 34, 1, 15 }, ++ { 0x0, 0x0, 41, 843, 975, 0, 0, -1 }, ++ { 0x0, 0x0, 41, 844, 979, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 845, 658, 34, 1, 17 }, ++ { 0x1, 0x1, 41, 846, 662, 34, 1, 17 }, ++ { 0x0, 0x0, 41, 847, 983, 0, 0, -1 }, ++ { 0x0, 0x0, 41, 848, 987, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 849, 682, 34, 1, 18 }, ++ { 0x10000001, 0x10000001, 41, 850, 686, 6, 1, 18 }, ++ { 0x0, 0x0, 41, 851, 991, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 852, 698, 34, 1, 19 }, ++ { 0x0, 0x0, 41, 853, 995, 0, 0, -1 }, ++ { 0x0, 0x0, 41, 854, 999, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 855, 718, 34, 1, 20 }, ++ { 0x10000001, 0x10000001, 41, 856, 722, 6, 1, 20 }, ++ { 0x0, 0x0, 41, 857, 1003, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 858, 734, 34, 1, 21 }, ++ { 0x0, 0x0, 41, 859, 1008, 0, 0, -1 }, ++ { 0x0, 0x0, 41, 860, 1012, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 861, 757, 34, 1, 17 }, ++ { 0x1, 0x1, 41, 862, 761, 34, 1, 17 }, ++ { 0x0, 0x0, 41, 863, 1016, 0, 0, -1 }, ++ { 0x1, 0x1, 41, 864, 773, 34, 1, 21 }, ++ { 0x800001, 0x800001, 41, -1, 1138, 4, 1, 16 }, ++ { 0x1, 0x1, 41, 2202, 1136, 4, 1, 16 }, ++ { 0x1, 0x1, 41, 939, 1141, 4, 1, 22 }, ++ { 0x2, 0x3, 41, -1, 1146, 20, 1, 67 }, ++ { 0x1, 0x1, 41, 2203, 1144, 21, 1, 67 }, ++ { 0x0, 0x0, 42, -1, -1, 0, 1, 80 }, ++ { 0x0, 0x0, 42, -1, -1, 0, 1, 80 }, ++ { 0x0, 0x0, 42, -1, -1, 0, 1, 123 }, ++ { 0x1, 0x1, 44, 1354, 295, 38, 1, 1 }, ++ { 0x1, 0x1, 44, 1355, 297, 38, 1, 1 }, ++ { 0x0, 0x0, 44, -1, 300, 0, 0, -1 }, ++ { 0x0, 0x0, 44, -1, 414, 0, 0, -1 }, ++ { 0x1, 0x1, 44, 1359, 317, 38, 1, 1 }, ++ { 0x1, 0x1, 44, 1360, 319, 38, 1, 1 }, ++ { 0x0, 0x0, 44, -1, 322, 0, 0, -1 }, ++ { 0x0, 0x0, 44, -1, 454, 0, 0, -1 }, ++ { 0x0, 0x0, 44, -1, 324, 0, 0, -1 }, ++ { 0x0, 0x0, 44, -1, 342, 0, 0, -1 }, ++ { 0x1, 0x1, 44, 1366, 343, 38, 1, 1 }, ++ { 0x1, 0x1, 44, 1367, 345, 38, 1, 1 }, ++ { 0x0, 0x0, 44, -1, 348, 0, 0, -1 }, ++ { 0x0, 0x0, 44, -1, 462, 0, 0, -1 }, ++ { 0x1, 0x1, 44, 1371, 365, 38, 1, 1 }, ++ { 0x1, 0x1, 44, 1372, 367, 38, 1, 1 }, ++ { 0x0, 0x0, 44, -1, 370, 0, 0, -1 }, ++ { 0x0, 0x0, 44, -1, 502, 0, 0, -1 }, ++ { 0x0, 0x0, 44, -1, 372, 0, 0, -1 }, ++ { 0x0, 0x0, 44, -1, 390, 0, 0, -1 }, ++ { 0x0, 0x0, 44, 1230, 2263, 0, 0, -1 }, ++ { 0x0, 0x0, 44, 1231, 2271, 0, 1, 54 }, ++ { 0x0, 0x0, 44, 1232, 2938, 0, 1, 54 }, ++ { 0x0, 0x0, 44, 1233, 2339, 0, 0, -1 }, ++ { 0x0, 0x0, 44, 1234, -1, 0, 1, 49 }, ++ { 0x0, 0x0, 44, 1102, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 44, 1103, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 44, 1104, -1, 0, 1, 0 }, ++ { 0x1, 0x1, 45, -1, 1658, 30, 1, 152 }, ++ { 0x1, 0x1, 45, 945, 1657, 30, 1, 151 }, ++ { 0x1, 0x1, 45, -1, 1662, 30, 1, 154 }, ++ { 0x1, 0x1, 45, 946, 1661, 30, 1, 153 }, ++ { 0x1, 0x1, 45, -1, 1666, 30, 1, 154 }, ++ { 0x1, 0x1, 45, 947, 1665, 30, 1, 153 }, ++ { 0x3, 0x3, 46, -1, 1142, 3, 1, 22 }, ++ { 0x1, 0x1, 47, 2223, -1, 30, 1, 137 }, ++ { 0x1, 0x1, 47, 2254, -1, 30, 1, 155 }, ++ { 0x0, 0x0, 49, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 49, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 49, -1, -1, 0, 1, 40 }, ++ { 0x1, 0x1, 56, -1, 1659, 31, 1, 152 }, ++ { 0x1, 0x1, 56, -1, 1663, 31, 1, 154 }, ++ { 0x1, 0x1, 56, -1, 1667, 31, 1, 154 }, ++ { 0x0, 0x0, 56, -1, -1, 0, 1, 94 }, ++ { 0x2, 0x3, 56, -1, -1, 27, 1, 94 }, ++ { 0x1, 0x1, 56, -1, -1, 28, 1, 94 }, ++ { 0x0, 0x0, 65, 14, 574, 0, 1, 6 }, ++ { 0x0, 0x0, 65, 1255, 577, 0, 1, 6 }, ++ { 0x1, 0x1, 65, 1256, 579, 33, 1, 6 }, ++ { 0x1, 0x1, 65, 1257, 581, 34, 1, 6 }, ++ { 0x3, 0x3, 65, 1258, 583, 33, 1, 6 }, ++ { 0x0, 0x0, 65, 1259, 585, 0, 1, 6 }, ++ { 0x1, 0x1, 65, 1260, 587, 33, 1, 6 }, ++ { 0x1, 0x1, 65, 1261, 589, 34, 1, 6 }, ++ { 0x3, 0x3, 65, 1262, 591, 33, 1, 6 }, ++ { 0x1, 0x1, 65, 1263, 593, 6, 1, 7 }, ++ { 0x8000001, 0x8000001, 65, 1264, 595, 6, 1, 7 }, ++ { 0x10000001, 0x10000001, 65, 1265, 597, 6, 1, 7 }, ++ { 0x18000001, 0x18000001, 65, 1266, 599, 6, 1, 7 }, ++ { 0x0, 0x0, 65, 1267, 613, 0, 1, 8 }, ++ { 0x1, 0x1, 65, 1268, 615, 33, 1, 8 }, ++ { 0x1, 0x1, 65, 1269, 617, 34, 1, 8 }, ++ { 0x3, 0x3, 65, 1270, 619, 33, 1, 8 }, ++ { 0x0, 0x0, 65, 1271, 625, 0, 1, 15 }, ++ { 0x1, 0x1, 65, 1272, 627, 33, 1, 15 }, ++ { 0x1, 0x1, 65, 1273, 629, 34, 1, 15 }, ++ { 0x3, 0x3, 65, 1274, 631, 33, 1, 15 }, ++ { 0x0, 0x0, 65, 15, 637, 0, 1, 17 }, ++ { 0x0, 0x0, 65, 1276, 640, 0, 1, 17 }, ++ { 0x1, 0x1, 65, 1277, 642, 33, 1, 17 }, ++ { 0x1, 0x1, 65, 1278, 644, 34, 1, 17 }, ++ { 0x3, 0x3, 65, 1279, 646, 33, 1, 17 }, ++ { 0x0, 0x0, 65, 1280, 648, 0, 1, 17 }, ++ { 0x1, 0x1, 65, 1281, 650, 33, 1, 17 }, ++ { 0x1, 0x1, 65, 1282, 652, 34, 1, 17 }, ++ { 0x3, 0x3, 65, 1283, 654, 33, 1, 17 }, ++ { 0x0, 0x0, 65, 1284, 664, 0, 1, 18 }, ++ { 0x1, 0x1, 65, 1285, 666, 33, 1, 18 }, ++ { 0x1, 0x1, 65, 1286, 668, 34, 1, 18 }, ++ { 0x3, 0x3, 65, 1287, 670, 33, 1, 18 }, ++ { 0x1, 0x1, 65, 1288, 672, 6, 1, 18 }, ++ { 0x8000001, 0x8000001, 65, 1289, 674, 6, 1, 18 }, ++ { 0x10000001, 0x10000001, 65, 1290, 676, 6, 1, 18 }, ++ { 0x18000001, 0x18000001, 65, 1291, 678, 6, 1, 18 }, ++ { 0x0, 0x0, 65, 1292, 688, 0, 1, 19 }, ++ { 0x1, 0x1, 65, 1293, 690, 33, 1, 19 }, ++ { 0x1, 0x1, 65, 1294, 692, 34, 1, 19 }, ++ { 0x3, 0x3, 65, 1295, 694, 33, 1, 19 }, ++ { 0x0, 0x0, 65, 1296, 700, 0, 1, 20 }, ++ { 0x1, 0x1, 65, 1297, 702, 33, 1, 20 }, ++ { 0x1, 0x1, 65, 1298, 704, 34, 1, 20 }, ++ { 0x3, 0x3, 65, 1299, 706, 33, 1, 20 }, ++ { 0x1, 0x1, 65, 1300, 708, 6, 1, 20 }, ++ { 0x8000001, 0x8000001, 65, 1301, 710, 6, 1, 20 }, ++ { 0x10000001, 0x10000001, 65, 1302, 712, 6, 1, 20 }, ++ { 0x18000001, 0x18000001, 65, 1303, 714, 6, 1, 20 }, ++ { 0x0, 0x0, 65, 1304, 724, 0, 1, 21 }, ++ { 0x1, 0x1, 65, 1305, 726, 33, 1, 21 }, ++ { 0x1, 0x1, 65, 1306, 728, 34, 1, 21 }, ++ { 0x3, 0x3, 65, 1307, 730, 33, 1, 21 }, ++ { 0x0, 0x0, 65, 17, 736, 0, 1, 17 }, ++ { 0x0, 0x0, 65, 1309, 739, 0, 1, 17 }, ++ { 0x1, 0x1, 65, 1310, 741, 33, 1, 17 }, ++ { 0x1, 0x1, 65, 1311, 743, 34, 1, 17 }, ++ { 0x3, 0x3, 65, 1312, 745, 33, 1, 17 }, ++ { 0x0, 0x0, 65, 1313, 747, 0, 1, 17 }, ++ { 0x1, 0x1, 65, 1314, 749, 33, 1, 17 }, ++ { 0x1, 0x1, 65, 1315, 751, 34, 1, 17 }, ++ { 0x3, 0x3, 65, 1316, 753, 33, 1, 17 }, ++ { 0x0, 0x0, 65, 1317, 763, 0, 1, 21 }, ++ { 0x1, 0x1, 65, 1318, 765, 33, 1, 21 }, ++ { 0x1, 0x1, 65, 1319, 767, 34, 1, 21 }, ++ { 0x3, 0x3, 65, 1320, 769, 33, 1, 21 }, ++ { 0x3, 0x3, 66, 543, 1521, 33, 1, 129 }, ++ { 0x3, 0x3, 66, 544, 1531, 33, 1, 129 }, ++ { 0x3, 0x3, 66, 545, 1541, 33, 1, 129 }, ++ { 0x0, 0x0, 66, -1, 1546, 0, 1, 140 }, ++ { 0x0, 0x0, 66, -1, 1547, 0, 1, 145 }, ++ { 0x0, 0x0, 66, -1, 1548, 0, 1, 145 }, ++ { 0x0, 0x0, 107, 1028, 2311, 0, 0, -1 }, ++ { 0x0, 0x0, 107, 1029, 2830, 0, 1, 29 }, ++ { 0x0, 0x0, 107, 1030, 2352, 0, 0, -1 }, ++ { 0x0, 0x0, 107, 1031, 2834, 0, 1, 29 }, ++ { 0x0, 0x0, 109, -1, 2313, 0, 0, -1 }, ++ { 0x1, 0x1, 109, -1, 2831, 27, 1, 29 }, ++ { 0x0, 0x0, 109, -1, 2354, 0, 0, -1 }, ++ { 0x1, 0x1, 109, -1, 2835, 27, 1, 29 }, ++ { 0x0, 0x0, 110, 1033, -1, 0, 1, 115 }, ++ { 0x1, 0x1, 111, -1, -1, 27, 1, 115 }, ++ { 0x0, 0x0, 112, 1064, 2860, 0, 1, 1 }, ++ { 0x0, 0x0, 112, 1065, 2863, 0, 1, 1 }, ++ { 0x0, 0x0, 112, 1206, 303, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1207, 307, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1167, 430, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1168, 438, 0, 0, -1 }, ++ { 0x0, 0x0, 112, -1, 446, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1066, 2876, 0, 1, 1 }, ++ { 0x0, 0x0, 112, 1067, 2879, 0, 1, 1 }, ++ { 0x0, 0x0, 112, -1, 328, 0, 0, -1 }, ++ { 0x0, 0x0, 112, -1, 332, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1215, 333, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1216, 337, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1068, 2900, 0, 1, 1 }, ++ { 0x0, 0x0, 112, 1069, 2903, 0, 1, 1 }, ++ { 0x0, 0x0, 112, 1219, 351, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1220, 355, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1180, 478, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1181, 486, 0, 0, -1 }, ++ { 0x0, 0x0, 112, -1, 494, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1373, 2914, 0, 1, 1 }, ++ { 0x0, 0x0, 112, 1374, 2916, 0, 1, 1 }, ++ { 0x0, 0x0, 112, -1, 376, 0, 0, -1 }, ++ { 0x0, 0x0, 112, -1, 380, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1228, 381, 0, 0, -1 }, ++ { 0x0, 0x0, 112, 1229, 385, 0, 0, -1 }, ++ { 0x0, 0x0, 112, -1, 2281, 0, 0, -1 }, ++ { 0x1, 0x9, 112, -1, 2285, 33, 1, 54 }, ++ { 0x1, 0x9, 112, -1, 2947, 33, 1, 54 }, ++ { 0x2, 0x3, 112, 1390, 2348, 27, 1, 49 }, ++ { 0x1, 0x1, 114, 1356, 2861, 37, 1, 1 }, ++ { 0x1, 0x1, 114, 1357, 2864, 37, 1, 1 }, ++ { 0x1, 0x1, 114, 1361, 2877, 37, 1, 1 }, ++ { 0x1, 0x1, 114, 1362, 2880, 37, 1, 1 }, ++ { 0x1, 0x1, 114, 1368, 2901, 37, 1, 1 }, ++ { 0x1, 0x1, 114, 1369, 2904, 37, 1, 1 }, ++ { 0x0, 0x0, 114, -1, 2924, 0, 1, 1 }, ++ { 0x0, 0x0, 114, -1, 2925, 0, 1, 1 }, ++ { 0x0, 0x0, 115, 1105, 2856, 0, 1, 1 }, ++ { 0x0, 0x0, 115, 1106, 2858, 0, 1, 1 }, ++ { 0x0, 0x0, 115, 1165, 301, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1166, 305, 0, 0, -1 }, ++ { 0x0, 0x0, 115, -1, 434, 0, 0, -1 }, ++ { 0x0, 0x0, 115, -1, 442, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1210, 444, 0, 0, -1 }, ++ { 0x0, 0x0, 115, -1, 2874, 0, 1, 1 }, ++ { 0x0, 0x0, 115, -1, 2875, 0, 1, 1 }, ++ { 0x0, 0x0, 115, 1213, 326, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1214, 330, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1174, 335, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1175, 339, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1109, 2896, 0, 1, 1 }, ++ { 0x0, 0x0, 115, 1110, 2898, 0, 1, 1 }, ++ { 0x0, 0x0, 115, 1178, 349, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1179, 353, 0, 0, -1 }, ++ { 0x0, 0x0, 115, -1, 482, 0, 0, -1 }, ++ { 0x0, 0x0, 115, -1, 490, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1223, 492, 0, 0, -1 }, ++ { 0x0, 0x0, 115, -1, 2912, 0, 1, 1 }, ++ { 0x0, 0x0, 115, -1, 2913, 0, 1, 1 }, ++ { 0x0, 0x0, 115, 1226, 374, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1227, 378, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1187, 383, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1188, 387, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1060, 2279, 0, 0, -1 }, ++ { 0x0, 0x0, 115, 1061, 2283, 0, 1, 54 }, ++ { 0x0, 0x0, 115, 1062, 2946, 0, 1, 54 }, ++ { 0x0, 0x0, 115, 1063, 2347, 0, 1, 49 }, ++ { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 116, -1, 2857, 37, 1, 1 }, ++ { 0x1, 0x1, 116, -1, 2859, 37, 1, 1 }, ++ { 0x0, 0x0, 116, -1, 2884, 0, 1, 1 }, ++ { 0x0, 0x0, 116, -1, 2885, 0, 1, 1 }, ++ { 0x1, 0x1, 116, -1, 2897, 37, 1, 1 }, ++ { 0x1, 0x1, 116, -1, 2899, 37, 1, 1 }, ++ { 0x0, 0x0, 116, -1, 2922, 0, 1, 1 }, ++ { 0x0, 0x0, 116, -1, 2923, 0, 1, 1 }, ++ { 0x0, 0x0, 117, 1158, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 117, 1159, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 117, 1160, -1, 0, 1, 0 }, ++ { 0x3, 0x3, 117, 1118, -1, 34, 1, 33 }, ++ { 0x3, 0x3, 117, 1119, -1, 34, 1, 40 }, ++ { 0x1, 0x1, 119, -1, -1, 35, 1, 33 }, ++ { 0x1, 0x1, 119, -1, -1, 35, 1, 40 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 66 }, ++ { 0x1, 0x1, 120, -1, -1, 36, 1, 122 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 40 }, ++ { 0x1, 0x1, 120, -1, -1, 27, 1, 96 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 105 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 73 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 73 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 74 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 40 }, ++ { 0x1, 0x1, 120, -1, -1, 27, 1, 117 }, ++ { 0x1, 0x1, 120, -1, -1, 27, 1, 40 }, ++ { 0x0, 0x0, 120, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 121, -1, 2786, 0, 0, -1 }, ++ { 0x0, 0x0, 121, -1, 2789, 0, 0, -1 }, ++ { 0x1, 0x1, 122, -1, -1, 35, 1, 16 }, ++ { 0x1, 0x1, 122, -1, -1, 35, 1, 16 }, ++ { 0x1, 0x1, 122, -1, -1, 35, 1, 16 }, ++ { 0x1, 0x1, 122, -1, -1, 35, 1, 16 }, ++ { 0x1, 0x1, 122, -1, -1, 35, 1, 22 }, ++ { 0x1, 0x1, 122, -1, -1, 35, 1, 22 }, ++ { 0x1, 0x1, 122, -1, -1, 35, 1, 22 }, ++ { 0x1, 0x1, 122, -1, -1, 35, 1, 22 }, ++ { 0x1, 0x1, 122, -1, -1, 23, 1, 67 }, ++ { 0x1, 0x1, 122, -1, -1, 23, 1, 67 }, ++ { 0x1, 0x1, 122, -1, -1, 23, 1, 67 }, ++ { 0x1, 0x1, 122, -1, -1, 23, 1, 67 }, ++ { 0x1, 0x1, 122, 900, -1, 23, 1, 67 }, ++ { 0x9, 0x9, 122, 901, -1, 20, 1, 67 }, ++ { 0x0, 0x0, 126, 2165, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 126, 2166, -1, 0, 1, 0 }, ++ { 0x1, 0x1, 126, -1, -1, 28, 1, 33 }, ++ { 0x1, 0x1, 126, -1, -1, 27, 1, 33 }, ++ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, ++ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, ++ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, ++ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, ++ { 0x0, 0x0, 126, -1, -1, 0, 1, 114 }, ++ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, ++ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, ++ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, ++ { 0x0, 0x0, 126, 1116, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 126, 1244, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 140, 1194, 2852, 0, 1, 1 }, ++ { 0x0, 0x0, 140, 1195, 2854, 0, 1, 1 }, ++ { 0x0, 0x0, 140, 1036, 302, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1037, 422, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1076, 311, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1077, 315, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1078, 443, 0, 0, -1 }, ++ { 0x0, 0x0, 140, -1, 2872, 0, 1, 1 }, ++ { 0x0, 0x0, 140, -1, 2873, 0, 1, 1 }, ++ { 0x0, 0x0, 140, 1081, 325, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1082, 329, 0, 0, -1 }, ++ { 0x0, 0x0, 140, -1, 336, 0, 0, -1 }, ++ { 0x0, 0x0, 140, -1, 340, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1198, 2892, 0, 1, 1 }, ++ { 0x0, 0x0, 140, 1199, 2894, 0, 1, 1 }, ++ { 0x0, 0x0, 140, 1049, 350, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1050, 470, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1089, 359, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1090, 363, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1091, 491, 0, 0, -1 }, ++ { 0x0, 0x0, 140, -1, 2910, 0, 1, 1 }, ++ { 0x0, 0x0, 140, -1, 2911, 0, 1, 1 }, ++ { 0x0, 0x0, 140, 1094, 373, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 1095, 377, 0, 0, -1 }, ++ { 0x0, 0x0, 140, -1, 384, 0, 0, -1 }, ++ { 0x0, 0x0, 140, -1, 388, 0, 0, -1 }, ++ { 0x0, 0x0, 140, 2974, 2267, 0, 0, -1 }, ++ { 0x1, 0x1, 140, 2975, 2275, 33, 1, 54 }, ++ { 0x1, 0x1, 140, 2976, 2940, 33, 1, 54 }, ++ { 0x0, 0x0, 140, 2977, 2341, 0, 0, -1 }, ++ { 0x1, 0x1, 140, 2978, -1, 28, 1, 49 }, ++ { 0x1, 0x1, 141, -1, 2853, 37, 1, 1 }, ++ { 0x1, 0x1, 141, -1, 2855, 37, 1, 1 }, ++ { 0x0, 0x0, 141, -1, 2882, 0, 1, 1 }, ++ { 0x0, 0x0, 141, -1, 2883, 0, 1, 1 }, ++ { 0x1, 0x1, 141, -1, 2893, 37, 1, 1 }, ++ { 0x1, 0x1, 141, -1, 2895, 37, 1, 1 }, ++ { 0x0, 0x0, 141, -1, 2920, 0, 1, 1 }, ++ { 0x0, 0x0, 141, -1, 2921, 0, 1, 1 }, ++ { 0x1, 0x1, 144, 899, 1140, 3, 1, 22 }, ++ { 0x0, 0x0, 145, 2167, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 146, 905, 2846, 0, 1, 1 }, ++ { 0x0, 0x0, 146, 906, 2849, 0, 1, 1 }, ++ { 0x0, 0x0, 146, -1, 304, 0, 0, -1 }, ++ { 0x0, 0x0, 146, -1, 426, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1038, 309, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1039, 313, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1040, 445, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 909, 2866, 0, 1, 1 }, ++ { 0x0, 0x0, 146, 910, 2869, 0, 1, 1 }, ++ { 0x0, 0x0, 146, 1043, 327, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1044, 331, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1083, 334, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1084, 338, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 915, 2886, 0, 1, 1 }, ++ { 0x0, 0x0, 146, 916, 2889, 0, 1, 1 }, ++ { 0x0, 0x0, 146, -1, 352, 0, 0, -1 }, ++ { 0x0, 0x0, 146, -1, 474, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1051, 357, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1052, 361, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1053, 493, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 919, 2906, 0, 1, 1 }, ++ { 0x0, 0x0, 146, 920, 2908, 0, 1, 1 }, ++ { 0x0, 0x0, 146, 1056, 375, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1057, 379, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1096, 382, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1097, 386, 0, 0, -1 }, ++ { 0x0, 0x0, 146, 1189, 2265, 0, 0, -1 }, ++ { 0x1, 0x1, 146, 1190, 2273, 36, 1, 54 }, ++ { 0x1, 0x1, 146, 1191, 2939, 36, 1, 54 }, ++ { 0x0, 0x0, 146, 1192, 2340, 0, 0, -1 }, ++ { 0x1, 0x1, 146, 1193, -1, 27, 1, 49 }, ++ { 0x1, 0x1, 147, -1, 2848, 37, 1, 1 }, ++ { 0x1, 0x1, 147, -1, 2851, 37, 1, 1 }, ++ { 0x1, 0x1, 147, -1, 2868, 37, 1, 1 }, ++ { 0x1, 0x1, 147, -1, 2871, 37, 1, 1 }, ++ { 0x1, 0x1, 147, -1, 2888, 37, 1, 1 }, ++ { 0x1, 0x1, 147, -1, 2891, 37, 1, 1 }, ++ { 0x0, 0x0, 147, -1, 2918, 0, 1, 1 }, ++ { 0x0, 0x0, 147, -1, 2919, 0, 1, 1 }, ++ { 0x0, 0x0, 148, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 148, 1117, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 149, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 149, -1, -1, 0, 1, 66 }, ++ { 0x0, 0x0, 149, -1, 2926, 0, 1, 63 }, ++ { 0x0, 0x0, 149, -1, 2927, 0, 1, 63 }, ++ { 0x0, 0x0, 149, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 149, -1, -1, 0, 1, 81 }, ++ { 0x0, 0x0, 149, -1, -1, 0, 1, 81 }, ++ { 0x0, 0x0, 149, -1, -1, 0, 1, 85 }, ++ { 0x0, 0x0, 149, -1, -1, 0, 1, 40 }, ++ { 0x1, 0x1, 150, -1, 575, 12, 1, 6 }, ++ { 0x1, 0x1, 150, -1, 578, 12, 1, 6 }, ++ { 0x200001, 0x200001, 150, -1, 580, 12, 1, 6 }, ++ { 0x400001, 0x400001, 150, -1, 582, 12, 1, 6 }, ++ { 0x600001, 0x600001, 150, -1, 584, 12, 1, 6 }, ++ { 0x1, 0x1, 150, -1, 586, 12, 1, 6 }, ++ { 0x200001, 0x200001, 150, -1, 588, 12, 1, 6 }, ++ { 0x400001, 0x400001, 150, -1, 590, 12, 1, 6 }, ++ { 0x600001, 0x600001, 150, -1, 592, 12, 1, 6 }, ++ { 0x41, 0x41, 150, -1, 594, 6, 1, 7 }, ++ { 0x8000041, 0x8000041, 150, -1, 596, 6, 1, 7 }, ++ { 0x10000041, 0x10000041, 150, -1, 598, 6, 1, 7 }, ++ { 0x18000041, 0x18000041, 150, -1, 600, 6, 1, 7 }, ++ { 0x1, 0x1, 150, -1, 614, 12, 1, 8 }, ++ { 0x200001, 0x200001, 150, -1, 616, 12, 1, 8 }, ++ { 0x400001, 0x400001, 150, -1, 618, 12, 1, 8 }, ++ { 0x600001, 0x600001, 150, -1, 620, 12, 1, 8 }, ++ { 0x1, 0x1, 150, -1, 626, 12, 1, 15 }, ++ { 0x200001, 0x200001, 150, -1, 628, 12, 1, 15 }, ++ { 0x400001, 0x400001, 150, -1, 630, 12, 1, 15 }, ++ { 0x600001, 0x600001, 150, -1, 632, 12, 1, 15 }, ++ { 0x1, 0x1, 150, -1, 638, 12, 1, 17 }, ++ { 0x1, 0x1, 150, -1, 641, 12, 1, 17 }, ++ { 0x200001, 0x200001, 150, -1, 643, 12, 1, 17 }, ++ { 0x400001, 0x400001, 150, -1, 645, 12, 1, 17 }, ++ { 0x600001, 0x600001, 150, -1, 647, 12, 1, 17 }, ++ { 0x1, 0x1, 150, -1, 649, 12, 1, 17 }, ++ { 0x200001, 0x200001, 150, -1, 651, 12, 1, 17 }, ++ { 0x400001, 0x400001, 150, -1, 653, 12, 1, 17 }, ++ { 0x600001, 0x600001, 150, -1, 655, 12, 1, 17 }, ++ { 0x1, 0x1, 150, -1, 665, 12, 1, 18 }, ++ { 0x200001, 0x200001, 150, -1, 667, 12, 1, 18 }, ++ { 0x400001, 0x400001, 150, -1, 669, 12, 1, 18 }, ++ { 0x600001, 0x600001, 150, -1, 671, 12, 1, 18 }, ++ { 0x41, 0x41, 150, -1, 673, 6, 1, 18 }, ++ { 0x8000041, 0x8000041, 150, -1, 675, 6, 1, 18 }, ++ { 0x10000041, 0x10000041, 150, -1, 677, 6, 1, 18 }, ++ { 0x18000041, 0x18000041, 150, -1, 679, 6, 1, 18 }, ++ { 0x1, 0x1, 150, -1, 689, 12, 1, 19 }, ++ { 0x200001, 0x200001, 150, -1, 691, 12, 1, 19 }, ++ { 0x400001, 0x400001, 150, -1, 693, 12, 1, 19 }, ++ { 0x600001, 0x600001, 150, -1, 695, 12, 1, 19 }, ++ { 0x1, 0x1, 150, -1, 701, 12, 1, 20 }, ++ { 0x200001, 0x200001, 150, -1, 703, 12, 1, 20 }, ++ { 0x400001, 0x400001, 150, -1, 705, 12, 1, 20 }, ++ { 0x600001, 0x600001, 150, -1, 707, 12, 1, 20 }, ++ { 0x41, 0x41, 150, -1, 709, 6, 1, 20 }, ++ { 0x8000041, 0x8000041, 150, -1, 711, 6, 1, 20 }, ++ { 0x10000041, 0x10000041, 150, -1, 713, 6, 1, 20 }, ++ { 0x18000041, 0x18000041, 150, -1, 715, 6, 1, 20 }, ++ { 0x1, 0x1, 150, -1, 725, 12, 1, 21 }, ++ { 0x200001, 0x200001, 150, -1, 727, 12, 1, 21 }, ++ { 0x400001, 0x400001, 150, -1, 729, 12, 1, 21 }, ++ { 0x600001, 0x600001, 150, -1, 731, 12, 1, 21 }, ++ { 0x1, 0x1, 150, -1, 737, 12, 1, 17 }, ++ { 0x1, 0x1, 150, -1, 740, 12, 1, 17 }, ++ { 0x200001, 0x200001, 150, -1, 742, 12, 1, 17 }, ++ { 0x400001, 0x400001, 150, -1, 744, 12, 1, 17 }, ++ { 0x600001, 0x600001, 150, -1, 746, 12, 1, 17 }, ++ { 0x1, 0x1, 150, -1, 748, 12, 1, 17 }, ++ { 0x200001, 0x200001, 150, -1, 750, 12, 1, 17 }, ++ { 0x400001, 0x400001, 150, -1, 752, 12, 1, 17 }, ++ { 0x600001, 0x600001, 150, -1, 754, 12, 1, 17 }, ++ { 0x1, 0x1, 150, -1, 764, 12, 1, 21 }, ++ { 0x200001, 0x200001, 150, -1, 766, 12, 1, 21 }, ++ { 0x400001, 0x400001, 150, -1, 768, 12, 1, 21 }, ++ { 0x600001, 0x600001, 150, -1, 770, 12, 1, 21 }, ++ { 0x0, 0x0, 155, -1, -1, 0, 1, 124 }, ++ { 0x0, 0x0, 159, 775, -1, 0, 1, 75 }, ++ { 0x0, 0x0, 159, 776, -1, 0, 1, 75 }, ++ { 0x9, 0x9, 159, -1, 1438, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1447, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1456, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1469, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1478, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1487, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1496, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1505, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1514, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1524, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1534, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1544, 32, 1, 130 }, ++ { 0x9, 0x9, 159, -1, 1553, 32, 1, 144 }, ++ { 0x9, 0x9, 159, -1, 1559, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1565, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1571, 32, 1, 144 }, ++ { 0x9, 0x9, 159, -1, 1577, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1583, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1589, 32, 1, 144 }, ++ { 0x9, 0x9, 159, -1, 1595, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1601, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1607, 32, 1, 144 }, ++ { 0x9, 0x9, 159, -1, 1613, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1619, 32, 1, 144 }, ++ { 0x9, 0x9, 159, -1, 1625, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1631, 32, 1, 144 }, ++ { 0x9, 0x9, 159, -1, 1637, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1643, 32, 1, 144 }, ++ { 0x9, 0x9, 159, -1, 1649, 32, 1, 149 }, ++ { 0x9, 0x9, 159, -1, 1655, 32, 1, 149 }, ++ { 0x0, 0x0, 160, 1235, 296, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 1236, 412, 0, 0, -1 }, ++ { 0x1, 0x1, 160, -1, 2862, 38, 1, 1 }, ++ { 0x1, 0x1, 160, 907, 2865, 38, 1, 1 }, ++ { 0x0, 0x0, 160, 908, 413, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 1237, 318, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 1238, 452, 0, 0, -1 }, ++ { 0x1, 0x1, 160, -1, 2878, 38, 1, 1 }, ++ { 0x1, 0x1, 160, 911, 2881, 38, 1, 1 }, ++ { 0x0, 0x0, 160, 912, 453, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 913, 323, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 914, 341, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 1239, 344, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 1240, 460, 0, 0, -1 }, ++ { 0x1, 0x1, 160, -1, 2902, 38, 1, 1 }, ++ { 0x1, 0x1, 160, 917, 2905, 38, 1, 1 }, ++ { 0x0, 0x0, 160, 918, 461, 0, 0, -1 }, ++ { 0x0, 0x0, 160, -1, 366, 0, 0, -1 }, ++ { 0x0, 0x0, 160, -1, 500, 0, 0, -1 }, ++ { 0x1, 0x1, 160, -1, 2915, 38, 1, 1 }, ++ { 0x1, 0x1, 160, 921, 2917, 38, 1, 1 }, ++ { 0x0, 0x0, 160, 922, 501, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 923, 371, 0, 0, -1 }, ++ { 0x0, 0x0, 160, 924, 389, 0, 0, -1 }, ++ { 0x0, 0x0, 161, 1397, 2287, 0, 0, -1 }, ++ { 0x0, 0x0, 161, 1398, 2295, 0, 1, 54 }, ++ { 0x0, 0x0, 161, 1399, 2956, 0, 1, 54 }, ++ { 0x0, 0x0, 161, 1400, 2343, 0, 0, -1 }, ++ { 0x1, 0x1, 161, 1401, -1, 29, 1, 49 }, ++ { 0x0, 0x0, 162, -1, 2305, 0, 0, -1 }, ++ { 0x1, 0x9, 162, -1, 2309, 33, 1, 54 }, ++ { 0x1, 0x9, 162, -1, 2965, 33, 1, 54 }, ++ { 0x6, 0x7, 162, -1, 2350, 27, 1, 49 }, ++ { 0x0, 0x0, 163, 1383, 2303, 0, 0, -1 }, ++ { 0x0, 0x0, 163, 1384, 2307, 0, 1, 54 }, ++ { 0x0, 0x0, 163, 1385, 2964, 0, 1, 54 }, ++ { 0x1, 0x1, 163, 1386, 2349, 29, 1, 49 }, ++ { 0x1, 0x1, 164, 1404, -1, 27, 1, 33 }, ++ { 0x0, 0x0, 165, 2159, 2291, 0, 0, -1 }, ++ { 0x1, 0x1, 165, 2160, 2299, 33, 1, 54 }, ++ { 0x1, 0x1, 165, 2161, 2958, 33, 1, 54 }, ++ { 0x0, 0x0, 165, 2162, 2345, 0, 0, -1 }, ++ { 0x3, 0x3, 165, 2163, -1, 28, 1, 49 }, ++ { 0x0, 0x0, 166, 1392, 2289, 0, 0, -1 }, ++ { 0x1, 0x1, 166, 1393, 2297, 36, 1, 54 }, ++ { 0x1, 0x1, 166, 1394, 2957, 36, 1, 54 }, ++ { 0x0, 0x0, 166, 1395, 2344, 0, 0, -1 }, ++ { 0x5, 0x5, 166, 1396, -1, 27, 1, 49 }, ++ { 0x0, 0x0, 167, -1, 2928, 0, 1, 63 }, ++ { 0x0, 0x0, 167, -1, 2929, 0, 1, 63 }, ++ { 0x1, 0x1, 169, -1, -1, 28, 1, 33 }, ++ { 0x1, 0x1, 170, 2745, -1, 27, 1, 33 }, ++ { 0x1, 0x1, 170, 2746, -1, 27, 1, 33 }, ++ { 0x1, 0x1, 171, 1685, -1, 28, 1, 135 }, ++ { 0x1, 0x1, 171, 1686, -1, 28, 1, 135 }, ++ { 0x1, 0x1, 171, 1687, -1, 28, 1, 135 }, ++ { 0x1, 0x1, 171, 1688, -1, 28, 1, 135 }, ++ { 0x1, 0x1, 171, 1689, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1690, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1691, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1692, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1693, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1694, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1695, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1696, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1697, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1698, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1699, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1700, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1701, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1702, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1703, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1704, -1, 28, 1, 134 }, ++ { 0x1, 0x1, 171, 1705, -1, 28, 1, 136 }, ++ { 0x1, 0x1, 171, 1706, -1, 28, 1, 136 }, ++ { 0x1, 0x1, 171, 1707, -1, 28, 1, 136 }, ++ { 0x1, 0x1, 171, 1708, -1, 28, 1, 136 }, ++ { 0x1, 0x1, 171, 1709, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1710, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1711, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1712, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1713, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1714, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1715, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1716, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1717, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1718, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1719, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1720, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1721, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1722, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1723, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1724, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1725, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1726, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1727, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1728, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1729, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1730, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1731, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1732, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1733, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1734, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1735, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1736, -1, 28, 1, 125 }, ++ { 0x1, 0x1, 171, 1737, -1, 28, 1, 125 }, ++ { 0x1, 0x1, 171, 1738, -1, 28, 1, 125 }, ++ { 0x1, 0x1, 171, 1739, -1, 28, 1, 125 }, ++ { 0x1, 0x1, 171, 1740, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1741, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1742, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1743, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1744, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1745, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1746, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1747, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1748, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1749, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1750, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1751, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1752, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1753, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1754, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1755, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1756, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1757, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1758, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1759, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1760, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1761, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1762, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1763, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1764, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1765, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1766, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1767, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1768, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1769, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1770, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1771, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1772, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1773, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1774, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1775, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1776, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1777, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1778, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1779, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1780, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1781, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1782, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1783, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1784, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1785, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1786, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1787, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1788, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1789, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1790, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1791, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1792, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1793, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1794, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1795, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1796, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1797, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1798, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1799, -1, 28, 1, 129 }, ++ { 0x1, 0x1, 171, 1800, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1801, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1802, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1803, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1804, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1805, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1806, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1807, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1808, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1809, -1, 28, 1, 129 }, ++ { 0x1, 0x1, 171, 1810, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1811, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1812, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1813, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1814, -1, 28, 1, 126 }, ++ { 0x1, 0x1, 171, 1815, -1, 28, 1, 127 }, ++ { 0x1, 0x1, 171, 1816, -1, 28, 1, 128 }, ++ { 0x1, 0x1, 171, 1817, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1818, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1819, -1, 28, 1, 129 }, ++ { 0x1, 0x1, 171, 1820, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1821, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1822, -1, 28, 1, 130 }, ++ { 0x1, 0x1, 171, 1823, -1, 28, 1, 124 }, ++ { 0x1, 0x1, 171, 1824, -1, 28, 1, 140 }, ++ { 0x1, 0x1, 171, 1825, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1826, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1827, -1, 28, 1, 141 }, ++ { 0x1, 0x1, 171, 1828, -1, 28, 1, 142 }, ++ { 0x1, 0x1, 171, 1829, -1, 28, 1, 143 }, ++ { 0x1, 0x1, 171, 1830, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1831, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1832, -1, 28, 1, 140 }, ++ { 0x1, 0x1, 171, 1833, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1834, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1835, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1836, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1837, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1838, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1839, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1840, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1841, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1842, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1843, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1844, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1845, -1, 28, 1, 141 }, ++ { 0x1, 0x1, 171, 1846, -1, 28, 1, 142 }, ++ { 0x1, 0x1, 171, 1847, -1, 28, 1, 143 }, ++ { 0x1, 0x1, 171, 1848, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1849, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1850, -1, 28, 1, 140 }, ++ { 0x1, 0x1, 171, 1851, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1852, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1853, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1854, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1855, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1856, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1857, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1858, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1859, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1860, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1861, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1862, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1863, -1, 28, 1, 141 }, ++ { 0x1, 0x1, 171, 1864, -1, 28, 1, 142 }, ++ { 0x1, 0x1, 171, 1865, -1, 28, 1, 143 }, ++ { 0x1, 0x1, 171, 1866, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1867, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1868, -1, 28, 1, 140 }, ++ { 0x1, 0x1, 171, 1869, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1870, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1871, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1872, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1873, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1874, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1875, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1876, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1877, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1878, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1879, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1880, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1881, -1, 28, 1, 141 }, ++ { 0x1, 0x1, 171, 1882, -1, 28, 1, 142 }, ++ { 0x1, 0x1, 171, 1883, -1, 28, 1, 143 }, ++ { 0x1, 0x1, 171, 1884, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1885, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1886, -1, 28, 1, 140 }, ++ { 0x1, 0x1, 171, 1887, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1888, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1889, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1890, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1891, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1892, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1893, -1, 28, 1, 141 }, ++ { 0x1, 0x1, 171, 1894, -1, 28, 1, 142 }, ++ { 0x1, 0x1, 171, 1895, -1, 28, 1, 143 }, ++ { 0x1, 0x1, 171, 1896, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1897, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1898, -1, 28, 1, 140 }, ++ { 0x1, 0x1, 171, 1899, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1900, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1901, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1902, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1903, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1904, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1905, -1, 28, 1, 141 }, ++ { 0x1, 0x1, 171, 1906, -1, 28, 1, 142 }, ++ { 0x1, 0x1, 171, 1907, -1, 28, 1, 143 }, ++ { 0x1, 0x1, 171, 1908, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1909, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1910, -1, 28, 1, 140 }, ++ { 0x1, 0x1, 171, 1911, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1912, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1913, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1914, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1915, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1916, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1917, -1, 28, 1, 141 }, ++ { 0x1, 0x1, 171, 1918, -1, 28, 1, 142 }, ++ { 0x1, 0x1, 171, 1919, -1, 28, 1, 143 }, ++ { 0x1, 0x1, 171, 1920, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1921, -1, 28, 1, 144 }, ++ { 0x1, 0x1, 171, 1922, -1, 28, 1, 140 }, ++ { 0x1, 0x1, 171, 1923, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1924, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1925, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1926, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1927, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1928, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1929, -1, 28, 1, 146 }, ++ { 0x1, 0x1, 171, 1930, -1, 28, 1, 147 }, ++ { 0x1, 0x1, 171, 1931, -1, 28, 1, 148 }, ++ { 0x1, 0x1, 171, 1932, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1933, -1, 28, 1, 149 }, ++ { 0x1, 0x1, 171, 1934, -1, 28, 1, 145 }, ++ { 0x1, 0x1, 171, 1673, -1, 28, 1, 151 }, ++ { 0x1, 0x1, 171, 1674, -1, 28, 1, 152 }, ++ { 0x1, 0x1, 171, 1675, -1, 28, 1, 152 }, ++ { 0x1, 0x1, 171, 1676, -1, 28, 1, 151 }, ++ { 0x1, 0x1, 171, 1677, -1, 28, 1, 153 }, ++ { 0x1, 0x1, 171, 1678, -1, 28, 1, 154 }, ++ { 0x1, 0x1, 171, 1679, -1, 28, 1, 154 }, ++ { 0x1, 0x1, 171, 1680, -1, 28, 1, 153 }, ++ { 0x1, 0x1, 171, 1681, -1, 28, 1, 153 }, ++ { 0x1, 0x1, 171, 1682, -1, 28, 1, 154 }, ++ { 0x1, 0x1, 171, 1683, -1, 28, 1, 154 }, ++ { 0x1, 0x1, 171, 1684, -1, 28, 1, 153 }, ++ { 0x1, 0x1, 171, 1979, -1, 28, 1, 136 }, ++ { 0x1, 0x1, 171, 1980, -1, 28, 1, 136 }, ++ { 0x1, 0x1, 171, 1981, -1, 28, 1, 136 }, ++ { 0x1, 0x1, 171, 1982, -1, 28, 1, 136 }, ++ { 0x1, 0x1, 172, 1935, -1, 29, 1, 151 }, ++ { 0x1, 0x1, 172, 1936, -1, 29, 1, 152 }, ++ { 0x1, 0x1, 172, 1937, -1, 29, 1, 152 }, ++ { 0x1, 0x1, 172, 1938, -1, 29, 1, 151 }, ++ { 0x1, 0x1, 172, 1939, -1, 29, 1, 153 }, ++ { 0x1, 0x1, 172, 1940, -1, 29, 1, 154 }, ++ { 0x1, 0x1, 172, 1941, -1, 29, 1, 154 }, ++ { 0x1, 0x1, 172, 1942, -1, 29, 1, 153 }, ++ { 0x1, 0x1, 172, 1943, -1, 29, 1, 153 }, ++ { 0x1, 0x1, 172, 1944, -1, 29, 1, 154 }, ++ { 0x1, 0x1, 172, 1945, -1, 29, 1, 154 }, ++ { 0x1, 0x1, 172, 1946, -1, 29, 1, 153 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 269, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2224, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 271, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2225, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 273, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2226, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 125 }, ++ { 0x3, 0x3, 173, 275, -1, 28, 1, 125 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 125 }, ++ { 0x3, 0x3, 173, 276, -1, 28, 1, 125 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 277, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2227, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 279, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2228, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 281, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2229, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 283, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2230, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 285, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2231, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 287, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2232, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 129 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 289, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2233, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 129 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 291, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2234, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 126 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 127 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 128 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 129 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 293, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 130 }, ++ { 0x3, 0x3, 173, 2235, -1, 28, 1, 124 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 140 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, 2236, -1, 28, 1, 140 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2237, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2238, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, 2239, -1, 28, 1, 140 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2240, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2241, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, 2242, -1, 28, 1, 140 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2243, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2244, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, 2245, -1, 28, 1, 140 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2246, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, 2247, -1, 28, 1, 140 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2248, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, 2249, -1, 28, 1, 140 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2250, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 144 }, ++ { 0x3, 0x3, 173, 2251, -1, 28, 1, 140 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2252, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, ++ { 0x3, 0x3, 173, 2253, -1, 28, 1, 145 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 152 }, ++ { 0x3, 0x3, 173, 933, -1, 28, 1, 152 }, ++ { 0x3, 0x3, 173, 934, -1, 28, 1, 151 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, ++ { 0x3, 0x3, 173, 935, -1, 28, 1, 154 }, ++ { 0x3, 0x3, 173, 936, -1, 28, 1, 153 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, ++ { 0x3, 0x3, 173, 937, -1, 28, 1, 154 }, ++ { 0x3, 0x3, 173, 938, -1, 28, 1, 153 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, ++ { 0x3, 0x3, 173, 2190, -1, 28, 1, 131 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, ++ { 0x3, 0x3, 173, 2191, -1, 28, 1, 138 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 132 }, ++ { 0x3, 0x3, 173, 2192, -1, 28, 1, 132 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 132 }, ++ { 0x3, 0x3, 173, 2193, -1, 28, 1, 132 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, ++ { 0x3, 0x3, 173, 2194, -1, 28, 1, 131 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, ++ { 0x3, 0x3, 173, 2195, -1, 28, 1, 138 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, ++ { 0x3, 0x3, 173, 2196, -1, 28, 1, 131 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, ++ { 0x3, 0x3, 173, 2197, -1, 28, 1, 138 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, ++ { 0x3, 0x3, 173, 2198, -1, 28, 1, 131 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 139 }, ++ { 0x3, 0x3, 173, 2199, -1, 28, 1, 138 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, ++ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, ++ { 0x0, 0x0, 174, -1, 392, 0, 0, -1 }, ++ { 0x0, 0x0, 174, -1, 394, 0, 0, -1 }, ++ { 0x0, 0x0, 174, 3004, 2968, 0, 1, 1 }, ++ { 0x0, 0x0, 174, 3005, 2969, 0, 1, 1 }, ++ { 0x0, 0x0, 174, -1, 400, 0, 0, -1 }, ++ { 0x0, 0x0, 174, -1, 402, 0, 0, -1 }, ++ { 0x0, 0x0, 174, 3008, 2972, 0, 1, 1 }, ++ { 0x0, 0x0, 174, 3009, 2973, 0, 1, 1 }, ++ { 0x11, 0x31, 175, 2847, 407, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 408, 12, 1, 4 }, ++ { 0x11, 0x31, 175, 2047, 409, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 411, 12, 1, 4 }, ++ { 0x1, 0x1, 175, -1, 415, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 175, -1, 416, 12, 1, 4 }, ++ { 0x11, 0x11, 175, -1, 417, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 418, 12, 1, 4 }, ++ { 0x1, 0x1, 175, 2053, 419, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 175, -1, 421, 12, 1, 4 }, ++ { 0x11, 0x11, 175, 2055, 423, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 425, 12, 1, 4 }, ++ { 0x1, 0x1, 175, 2057, 427, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 175, -1, 429, 12, 1, 4 }, ++ { 0x11, 0x11, 175, 2059, 431, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 433, 12, 1, 4 }, ++ { 0x1, 0x1, 175, 2061, 435, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 175, -1, 437, 12, 1, 4 }, ++ { 0x11, 0x11, 175, 2063, 439, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 441, 12, 1, 4 }, ++ { 0x11, 0x31, 175, 2867, 447, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 448, 12, 1, 4 }, ++ { 0x11, 0x31, 175, 2069, 449, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 451, 12, 1, 4 }, ++ { 0x11, 0x31, 175, 2887, 455, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 456, 12, 1, 4 }, ++ { 0x11, 0x31, 175, 2095, 457, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 459, 12, 1, 4 }, ++ { 0x1, 0x1, 175, -1, 463, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 175, -1, 464, 12, 1, 4 }, ++ { 0x11, 0x11, 175, -1, 465, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 466, 12, 1, 4 }, ++ { 0x1, 0x1, 175, 2101, 467, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 175, -1, 469, 12, 1, 4 }, ++ { 0x11, 0x11, 175, 2103, 471, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 473, 12, 1, 4 }, ++ { 0x1, 0x1, 175, 2105, 475, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 175, -1, 477, 12, 1, 4 }, ++ { 0x11, 0x11, 175, 2107, 479, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 481, 12, 1, 4 }, ++ { 0x1, 0x1, 175, 2109, 483, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 175, -1, 485, 12, 1, 4 }, ++ { 0x11, 0x11, 175, 2111, 487, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 489, 12, 1, 4 }, ++ { 0x11, 0x31, 175, 2907, 495, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 496, 12, 1, 4 }, ++ { 0x11, 0x31, 175, 2117, 497, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 175, -1, 499, 12, 1, 4 }, ++ { 0x1, 0x1, 175, -1, 503, 33, 1, 4 }, ++ { 0x200001, 0x200001, 175, -1, 504, 12, 1, 4 }, ++ { 0x1, 0x1, 175, -1, 505, 33, 1, 4 }, ++ { 0x200001, 0x200001, 175, -1, 506, 12, 1, 4 }, ++ { 0x1, 0x1, 175, -1, 511, 33, 1, 4 }, ++ { 0x200001, 0x200001, 175, -1, 512, 12, 1, 4 }, ++ { 0x1, 0x1, 175, -1, 513, 33, 1, 4 }, ++ { 0x200001, 0x200001, 175, -1, 514, 12, 1, 4 }, ++ { 0x2200001, 0x6200001, 176, 2850, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 1994, -1, 33, 1, 4 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x1, 0x1, 176, 2000, -1, 37, 1, 4 }, ++ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2002, -1, 33, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x1, 0x1, 176, 2004, -1, 37, 1, 4 }, ++ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2006, -1, 33, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x1, 0x1, 176, 2008, -1, 37, 1, 4 }, ++ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2010, -1, 33, 1, 4 }, ++ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, -1, -1, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, ++ { 0x2200001, 0x6200001, 176, 2870, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2014, -1, 33, 1, 4 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x2200001, 0x6200001, 176, 2890, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2018, -1, 33, 1, 4 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x1, 0x1, 176, 2024, -1, 37, 1, 4 }, ++ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2026, -1, 33, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x1, 0x1, 176, 2028, -1, 37, 1, 4 }, ++ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2030, -1, 33, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x1, 0x1, 176, 2032, -1, 37, 1, 4 }, ++ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2034, -1, 33, 1, 4 }, ++ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, -1, -1, 33, 1, 4 }, ++ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, ++ { 0x2200001, 0x6200001, 176, 2909, -1, 12, 1, 4 }, ++ { 0x11, 0x11, 176, 2038, -1, 33, 1, 4 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, ++ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, ++ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, ++ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, ++ { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, ++ { 0x1, 0x1, 176, 395, -1, 33, 1, 4 }, ++ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x200001, 0x200001, 176, 396, -1, 12, 1, 4 }, ++ { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, ++ { 0x1, 0x1, 176, 397, -1, 33, 1, 4 }, ++ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x200001, 0x200001, 176, 398, -1, 12, 1, 4 }, ++ { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, ++ { 0x1, 0x1, 176, 403, -1, 33, 1, 4 }, ++ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x200001, 0x200001, 176, 404, -1, 12, 1, 4 }, ++ { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, ++ { 0x1, 0x1, 176, 405, -1, 33, 1, 4 }, ++ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, ++ { 0x200001, 0x200001, 176, 406, -1, 12, 1, 4 }, ++ { 0x0, 0x0, 177, -1, 2293, 0, 0, -1 }, ++ { 0x9, 0x9, 177, -1, 2301, 33, 1, 49 }, ++ { 0x9, 0x9, 177, -1, 2959, 33, 1, 49 }, ++ { 0x0, 0x0, 177, -1, 2346, 0, 0, -1 }, ++ { 0x7, 0x7, 177, -1, -1, 27, 1, 49 }, ++ { 0x1, 0x1, 197, -1, -1, 27, 1, 10 }, ++ { 0x1, 0x1, 211, -1, -1, 29, 1, 0 }, ++ { 0x1, 0x1, 211, -1, -1, 29, 1, 0 }, ++ { 0x2, 0x3, 211, 1151, -1, 27, 1, 33 }, ++ { 0x0, 0x0, 211, 1152, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 211, 1153, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 211, 1154, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 211, 1155, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 211, 1156, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 211, 2988, -1, 0, 1, 93 }, ++ { 0x0, 0x0, 211, 2989, -1, 0, 1, 93 }, ++ { 0x0, 0x0, 211, 2990, 949, 0, 0, -1 }, ++ { 0x1, 0x1, 212, -1, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 212, -1, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 213, -1, 1408, 32, 1, 135 }, ++ { 0x1, 0x1, 213, -1, 1410, 32, 1, 135 }, ++ { 0x1, 0x1, 213, -1, 1412, 32, 1, 134 }, ++ { 0x1, 0x1, 213, -1, 1414, 32, 1, 134 }, ++ { 0x1, 0x1, 213, -1, 1416, 32, 1, 134 }, ++ { 0x1, 0x1, 213, -1, 1418, 32, 1, 134 }, ++ { 0x1, 0x1, 213, -1, 1420, 32, 1, 134 }, ++ { 0x1, 0x1, 213, -1, 1422, 32, 1, 134 }, ++ { 0x1, 0x1, 213, -1, 1424, 32, 1, 134 }, ++ { 0x1, 0x1, 213, -1, 1426, 32, 1, 134 }, ++ { 0x1, 0x1, 213, -1, 1428, 32, 1, 136 }, ++ { 0x1, 0x1, 213, -1, 1430, 32, 1, 136 }, ++ { 0x1, 0x1, 213, -1, 1947, 32, 1, 131 }, ++ { 0x1, 0x1, 213, -1, 1949, 32, 1, 138 }, ++ { 0x1, 0x1, 213, -1, 1951, 32, 1, 132 }, ++ { 0x1, 0x1, 213, -1, 1953, 32, 1, 132 }, ++ { 0x1, 0x1, 213, -1, 1955, 32, 1, 131 }, ++ { 0x1, 0x1, 213, -1, 1957, 32, 1, 138 }, ++ { 0x1, 0x1, 213, -1, 1959, 32, 1, 131 }, ++ { 0x1, 0x1, 213, -1, 1961, 32, 1, 138 }, ++ { 0x1, 0x1, 213, 2749, 1963, 32, 1, 131 }, ++ { 0x1, 0x1, 213, 2750, 1966, 32, 1, 138 }, ++ { 0x0, 0x0, 214, -1, 2791, 0, 0, -1 }, ++ { 0x0, 0x0, 214, -1, 2792, 0, 0, -1 }, ++ { 0x0, 0x0, 214, -1, 2817, 0, 0, -1 }, ++ { 0x5, 0x5, 214, -1, 2820, 20, 1, 67 }, ++ { 0x0, 0x0, 218, 2175, 948, 0, 0, -1 }, ++ { 0x0, 0x0, 219, -1, 1121, 0, 0, -1 }, ++ { 0x0, 0x0, 219, -1, 1246, 0, 0, -1 }, ++ { 0x0, 0x0, 219, -1, -1, 0, 1, 121 }, ++ { 0x0, 0x0, 219, -1, -1, 0, 1, 66 }, ++ { 0x1, 0x1, 219, 815, 2255, 36, 1, 65 }, ++ { 0x1, 0x1, 219, 816, 2314, 36, 1, 65 }, ++ { 0x0, 0x0, 219, 817, 2317, 0, 0, -1 }, ++ { 0x1, 0x1, 219, 818, -1, 36, 1, 65 }, ++ { 0x0, 0x0, 219, 1405, -1, 0, 1, 33 }, ++ { 0x1, 0x1, 219, 819, 2322, 36, 1, 65 }, ++ { 0x0, 0x0, 219, 820, 2325, 0, 0, -1 }, ++ { 0x1, 0x1, 219, 821, -1, 36, 1, 65 }, ++ { 0x0, 0x0, 219, 822, 2328, 0, 0, -1 }, ++ { 0x1, 0x1, 219, 823, -1, 36, 1, 65 }, ++ { 0x1, 0x1, 219, 824, 2331, 36, 1, 65 }, ++ { 0x1, 0x1, 219, 825, 2334, 36, 1, 65 }, ++ { 0x0, 0x0, 219, 1406, -1, 0, 1, 33 }, ++ { 0x1, 0x1, 219, 826, 2367, 36, 1, 65 }, ++ { 0x1, 0x1, 219, 827, -1, 31, 1, 137 }, ++ { 0x1, 0x1, 219, 226, 1431, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 227, 1440, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 228, 1449, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 229, 1462, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 230, 1471, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 231, 1480, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 232, 1489, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 233, 1498, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 234, 1507, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 235, 1516, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 236, 1526, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 237, 1536, 32, 1, 126 }, ++ { 0x1, 0x1, 219, 238, 1549, 32, 1, 141 }, ++ { 0x1, 0x1, 219, 239, 1555, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 240, 1561, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 241, 1567, 32, 1, 141 }, ++ { 0x1, 0x1, 219, 242, 1573, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 243, 1579, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 244, 1585, 32, 1, 141 }, ++ { 0x1, 0x1, 219, 245, 1591, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 246, 1597, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 247, 1603, 32, 1, 141 }, ++ { 0x1, 0x1, 219, 248, 1609, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 249, 1615, 32, 1, 141 }, ++ { 0x1, 0x1, 219, 250, 1621, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 251, 1627, 32, 1, 141 }, ++ { 0x1, 0x1, 219, 252, 1633, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 253, 1639, 32, 1, 141 }, ++ { 0x1, 0x1, 219, 254, 1645, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 255, 1651, 32, 1, 146 }, ++ { 0x1, 0x1, 219, 831, -1, 31, 1, 155 }, ++ { 0x0, 0x0, 220, 2370, -1, 0, 1, 65 }, ++ { 0x0, 0x0, 220, 2371, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 25, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2373, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2374, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2375, -1, 0, 1, 44 }, ++ { 0x0, 0x0, 220, 2376, -1, 0, 1, 39 }, ++ { 0x1, 0x1, 220, 2377, -1, 12, 1, 58 }, ++ { 0x0, 0x0, 220, 2378, -1, 0, 1, 53 }, ++ { 0x1000001, 0x1000001, 220, 2379, -1, 12, 1, 58 }, ++ { 0x1, 0x1, 220, 2380, -1, 36, 1, 53 }, ++ { 0x200001, 0x200001, 220, 2381, -1, 12, 1, 58 }, ++ { 0x1, 0x1, 220, 2382, -1, 33, 1, 53 }, ++ { 0x1200001, 0x1200001, 220, 2383, -1, 12, 1, 48 }, ++ { 0x9, 0x9, 220, 2384, -1, 33, 1, 48 }, ++ { 0x0, 0x0, 220, 2385, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2386, -1, 0, 1, 53 }, ++ { 0x0, 0x0, 220, 2387, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2388, -1, 0, 1, 53 }, ++ { 0x0, 0x0, 220, 2389, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2390, -1, 0, 1, 53 }, ++ { 0x0, 0x0, 220, 2391, -1, 0, 1, 48 }, ++ { 0x0, 0x0, 220, 2392, -1, 0, 1, 48 }, ++ { 0x1, 0x1, 220, 2393, -1, 12, 1, 58 }, ++ { 0x0, 0x0, 220, 2394, -1, 0, 1, 53 }, ++ { 0x200001, 0x1200001, 220, 2395, -1, 12, 1, 58 }, ++ { 0x1, 0x9, 220, 2396, -1, 33, 1, 53 }, ++ { 0x0, 0x0, 220, 2397, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2398, -1, 0, 1, 53 }, ++ { 0x0, 0x0, 220, 2399, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2400, -1, 0, 1, 53 }, ++ { 0x1, 0x1, 220, 2401, -1, 12, 1, 58 }, ++ { 0x0, 0x0, 220, 2402, -1, 0, 1, 53 }, ++ { 0x1000001, 0x1000001, 220, 2403, -1, 12, 1, 58 }, ++ { 0x1, 0x1, 220, 2404, -1, 36, 1, 53 }, ++ { 0x200001, 0x200001, 220, 2405, -1, 12, 1, 58 }, ++ { 0x1, 0x1, 220, 2406, -1, 33, 1, 53 }, ++ { 0x1200001, 0x1200001, 220, 2407, -1, 12, 1, 48 }, ++ { 0x9, 0x9, 220, 2408, -1, 33, 1, 48 }, ++ { 0x0, 0x0, 220, 2409, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2410, -1, 0, 1, 53 }, ++ { 0x0, 0x0, 220, 2411, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2412, -1, 0, 1, 53 }, ++ { 0x0, 0x0, 220, 2413, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2414, -1, 0, 1, 53 }, ++ { 0x0, 0x0, 220, 2415, -1, 0, 1, 48 }, ++ { 0x0, 0x0, 220, 2416, -1, 0, 1, 48 }, ++ { 0x1, 0x1, 220, 2417, -1, 12, 1, 58 }, ++ { 0x0, 0x0, 220, 2418, -1, 0, 1, 53 }, ++ { 0x200001, 0x1200001, 220, 2419, -1, 12, 1, 58 }, ++ { 0x1, 0x9, 220, 2420, -1, 33, 1, 53 }, ++ { 0x0, 0x0, 220, 2421, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2422, -1, 0, 1, 53 }, ++ { 0x0, 0x0, 220, 2423, -1, 0, 1, 58 }, ++ { 0x0, 0x0, 220, 2424, -1, 0, 1, 53 }, ++ { 0x1, 0x1, 220, 2425, -1, 28, 1, 28 }, ++ { 0x0, 0x0, 220, 2426, -1, 0, 1, 28 }, ++ { 0x3, 0x3, 220, 2427, -1, 27, 1, 28 }, ++ { 0x1, 0x1, 220, 2428, -1, 27, 1, 28 }, ++ { 0x0, 0x0, 220, 2429, -1, 0, 1, 65 }, ++ { 0x0, 0x0, 220, 2430, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2431, -1, 0, 1, 28 }, ++ { 0x1, 0x1, 220, 2432, -1, 36, 1, 65 }, ++ { 0x1, 0x1, 220, 2433, -1, 37, 1, 28 }, ++ { 0x0, 0x0, 220, 2434, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2435, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2436, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2437, -1, 0, 1, 65 }, ++ { 0x0, 0x0, 220, 2438, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 37, -1, 0, 1, 28 }, ++ { 0x1, 0x1, 220, 2440, -1, 36, 1, 65 }, ++ { 0x1, 0x1, 220, 2441, -1, 37, 1, 28 }, ++ { 0x0, 0x0, 220, 2442, -1, 0, 1, 28 }, ++ { 0x1, 0x1, 220, 2443, -1, 36, 1, 65 }, ++ { 0x1, 0x1, 220, 2444, -1, 37, 1, 28 }, ++ { 0x0, 0x0, 220, 2445, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2446, -1, 0, 1, 65 }, ++ { 0x0, 0x0, 220, 2447, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 42, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2449, -1, 0, 1, 65 }, ++ { 0x0, 0x0, 220, 2450, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 43, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2452, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2453, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2454, -1, 0, 1, 48 }, ++ { 0x1, 0x1, 220, 2455, -1, 27, 1, 48 }, ++ { 0x1, 0x1, 220, 2456, -1, 28, 1, 48 }, ++ { 0x3, 0x3, 220, 2457, -1, 27, 1, 48 }, ++ { 0x1, 0x1, 220, 2458, -1, 29, 1, 48 }, ++ { 0x5, 0x5, 220, 2459, -1, 27, 1, 48 }, ++ { 0x3, 0x3, 220, 2460, -1, 28, 1, 48 }, ++ { 0x7, 0x7, 220, 2461, -1, 27, 1, 48 }, ++ { 0x0, 0x0, 220, 2462, -1, 0, 1, 48 }, ++ { 0x0, 0x0, 220, 2463, -1, 0, 1, 48 }, ++ { 0x0, 0x0, 220, 2464, -1, 0, 1, 48 }, ++ { 0x0, 0x0, 220, 2465, -1, 0, 1, 48 }, ++ { 0x1, 0x1, 220, 2466, -1, 28, 1, 28 }, ++ { 0x0, 0x0, 220, 2467, -1, 0, 1, 28 }, ++ { 0x3, 0x3, 220, 2468, -1, 27, 1, 28 }, ++ { 0x1, 0x1, 220, 2469, -1, 27, 1, 28 }, ++ { 0x0, 0x0, 220, 2470, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2471, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2472, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 52, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2474, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2475, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 57, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 2477, -1, 0, 1, 23 }, ++ { 0x0, 0x0, 220, 2478, -1, 0, 1, 23 }, ++ { 0x0, 0x0, 220, 2479, -1, 0, 1, 23 }, ++ { 0x0, 0x0, 220, 2480, -1, 0, 1, 23 }, ++ { 0x0, 0x0, 220, 2481, -1, 0, 1, 34 }, ++ { 0x0, 0x0, 220, 2482, -1, 0, 1, 65 }, ++ { 0x0, 0x0, 220, 2483, -1, 0, 1, 28 }, ++ { 0x0, 0x0, 220, 64, -1, 0, 1, 28 }, ++ { 0x1, 0x1, 221, 2485, -1, 34, 1, 65 }, ++ { 0x1, 0x1, 221, 2486, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2487, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2488, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2489, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2490, -1, 34, 1, 45 }, ++ { 0x1, 0x1, 221, 2491, -1, 34, 1, 41 }, ++ { 0x400001, 0x400001, 221, 2492, -1, 12, 1, 60 }, ++ { 0x1, 0x1, 221, 2493, -1, 34, 1, 55 }, ++ { 0x1400001, 0x1400001, 221, 2494, -1, 12, 1, 60 }, ++ { 0x5, 0x5, 221, 2495, -1, 34, 1, 55 }, ++ { 0x600001, 0x600001, 221, 2496, -1, 12, 1, 60 }, ++ { 0x3, 0x3, 221, 2497, -1, 33, 1, 55 }, ++ { 0x1600001, 0x1600001, 221, 2498, -1, 12, 1, 50 }, ++ { 0xb, 0xb, 221, 2499, -1, 33, 1, 50 }, ++ { 0x1, 0x1, 221, 2500, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2501, -1, 34, 1, 55 }, ++ { 0x1, 0x1, 221, 2502, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2503, -1, 34, 1, 55 }, ++ { 0x1, 0x1, 221, 2504, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2505, -1, 34, 1, 55 }, ++ { 0x1, 0x1, 221, 2506, -1, 34, 1, 50 }, ++ { 0x1, 0x1, 221, 2507, -1, 34, 1, 50 }, ++ { 0x400001, 0x400001, 221, 2508, -1, 12, 1, 60 }, ++ { 0x1, 0x1, 221, 2509, -1, 34, 1, 55 }, ++ { 0x600001, 0x1600001, 221, 2510, -1, 12, 1, 60 }, ++ { 0x3, 0xb, 221, 2511, -1, 33, 1, 55 }, ++ { 0x1, 0x1, 221, 2512, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2513, -1, 34, 1, 55 }, ++ { 0x1, 0x1, 221, 2514, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2515, -1, 34, 1, 55 }, ++ { 0x400001, 0x400001, 221, 2516, -1, 12, 1, 60 }, ++ { 0x1, 0x1, 221, 2517, -1, 34, 1, 55 }, ++ { 0x1400001, 0x1400001, 221, 2518, -1, 12, 1, 60 }, ++ { 0x5, 0x5, 221, 2519, -1, 34, 1, 55 }, ++ { 0x600001, 0x600001, 221, 2520, -1, 12, 1, 60 }, ++ { 0x3, 0x3, 221, 2521, -1, 33, 1, 55 }, ++ { 0x1600001, 0x1600001, 221, 2522, -1, 12, 1, 50 }, ++ { 0xb, 0xb, 221, 2523, -1, 33, 1, 50 }, ++ { 0x1, 0x1, 221, 2524, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2525, -1, 34, 1, 55 }, ++ { 0x1, 0x1, 221, 2526, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2527, -1, 34, 1, 55 }, ++ { 0x1, 0x1, 221, 2528, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2529, -1, 34, 1, 55 }, ++ { 0x1, 0x1, 221, 2530, -1, 34, 1, 50 }, ++ { 0x1, 0x1, 221, 2531, -1, 34, 1, 50 }, ++ { 0x400001, 0x400001, 221, 2532, -1, 12, 1, 60 }, ++ { 0x1, 0x1, 221, 2533, -1, 34, 1, 55 }, ++ { 0x600001, 0x1600001, 221, 2534, -1, 12, 1, 60 }, ++ { 0x3, 0xb, 221, 2535, -1, 33, 1, 55 }, ++ { 0x1, 0x1, 221, 2536, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2537, -1, 34, 1, 55 }, ++ { 0x1, 0x1, 221, 2538, -1, 34, 1, 60 }, ++ { 0x1, 0x1, 221, 2539, -1, 34, 1, 55 }, ++ { 0x41, 0x41, 221, 2540, -1, 28, 1, 30 }, ++ { 0x1, 0x1, 221, 2541, -1, 34, 1, 30 }, ++ { 0x83, 0x83, 221, 2542, -1, 27, 1, 30 }, ++ { 0x81, 0x81, 221, 2543, -1, 27, 1, 30 }, ++ { 0x1, 0x1, 221, 2544, -1, 34, 1, 65 }, ++ { 0x1, 0x1, 221, 2545, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2546, -1, 34, 1, 30 }, ++ { 0x5, 0x5, 221, 2547, -1, 34, 1, 65 }, ++ { 0x9, 0x9, 221, 2548, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2549, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2550, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2551, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2552, -1, 34, 1, 65 }, ++ { 0x1, 0x1, 221, 2553, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2554, -1, 34, 1, 30 }, ++ { 0x5, 0x5, 221, 2555, -1, 34, 1, 65 }, ++ { 0x9, 0x9, 221, 2556, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2557, -1, 34, 1, 30 }, ++ { 0x5, 0x5, 221, 2558, -1, 34, 1, 65 }, ++ { 0x9, 0x9, 221, 2559, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2560, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2561, -1, 34, 1, 65 }, ++ { 0x1, 0x1, 221, 2562, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2563, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2564, -1, 34, 1, 65 }, ++ { 0x1, 0x1, 221, 2565, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2566, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2567, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2568, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2569, -1, 34, 1, 50 }, ++ { 0x81, 0x81, 221, 2570, -1, 27, 1, 50 }, ++ { 0x41, 0x41, 221, 2571, -1, 28, 1, 50 }, ++ { 0x83, 0x83, 221, 2572, -1, 27, 1, 50 }, ++ { 0x21, 0x21, 221, 2573, -1, 29, 1, 50 }, ++ { 0x85, 0x85, 221, 2574, -1, 27, 1, 50 }, ++ { 0x43, 0x43, 221, 2575, -1, 28, 1, 50 }, ++ { 0x87, 0x87, 221, 2576, -1, 27, 1, 50 }, ++ { 0x1, 0x1, 221, 2577, -1, 34, 1, 50 }, ++ { 0x1, 0x1, 221, 2578, -1, 34, 1, 50 }, ++ { 0x1, 0x1, 221, 2579, -1, 34, 1, 50 }, ++ { 0x1, 0x1, 221, 2580, -1, 34, 1, 50 }, ++ { 0x41, 0x41, 221, 2581, -1, 28, 1, 30 }, ++ { 0x1, 0x1, 221, 2582, -1, 34, 1, 30 }, ++ { 0x83, 0x83, 221, 2583, -1, 27, 1, 30 }, ++ { 0x81, 0x81, 221, 2584, -1, 27, 1, 30 }, ++ { 0x1, 0x1, 221, 2585, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2586, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2587, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2588, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2589, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2590, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2591, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2592, -1, 34, 1, 25 }, ++ { 0x1, 0x1, 221, 2593, -1, 34, 1, 25 }, ++ { 0x1, 0x1, 221, 2594, -1, 34, 1, 25 }, ++ { 0x1, 0x1, 221, 2595, -1, 34, 1, 25 }, ++ { 0x1, 0x1, 221, 2596, -1, 34, 1, 36 }, ++ { 0x1, 0x1, 221, 2597, -1, 34, 1, 65 }, ++ { 0x1, 0x1, 221, 2598, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 221, 2599, -1, 34, 1, 30 }, ++ { 0x1, 0x1, 222, 2600, -1, 35, 1, 65 }, ++ { 0x1, 0x1, 222, 2601, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2602, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2603, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2604, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2605, -1, 35, 1, 46 }, ++ { 0x1, 0x1, 222, 2606, -1, 35, 1, 42 }, ++ { 0x800001, 0x800001, 222, 2607, -1, 12, 1, 61 }, ++ { 0x1, 0x1, 222, 2608, -1, 35, 1, 56 }, ++ { 0x1800001, 0x1800001, 222, 2609, -1, 12, 1, 61 }, ++ { 0x3, 0x3, 222, 2610, -1, 35, 1, 56 }, ++ { 0xa00001, 0xa00001, 222, 2611, -1, 12, 1, 61 }, ++ { 0x5, 0x5, 222, 2612, -1, 33, 1, 56 }, ++ { 0x1a00001, 0x1a00001, 222, 2613, -1, 12, 1, 51 }, ++ { 0xd, 0xd, 222, 2614, -1, 33, 1, 51 }, ++ { 0x1, 0x1, 222, 2615, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2616, -1, 35, 1, 56 }, ++ { 0x1, 0x1, 222, 2617, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2618, -1, 35, 1, 56 }, ++ { 0x1, 0x1, 222, 2619, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2620, -1, 35, 1, 56 }, ++ { 0x1, 0x1, 222, 2621, -1, 35, 1, 51 }, ++ { 0x1, 0x1, 222, 2622, -1, 35, 1, 51 }, ++ { 0x800001, 0x800001, 222, 2623, -1, 12, 1, 61 }, ++ { 0x1, 0x1, 222, 2624, -1, 35, 1, 56 }, ++ { 0xa00001, 0x1a00001, 222, 2625, -1, 12, 1, 61 }, ++ { 0x5, 0xd, 222, 2626, -1, 33, 1, 56 }, ++ { 0x1, 0x1, 222, 2627, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2628, -1, 35, 1, 56 }, ++ { 0x1, 0x1, 222, 2629, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2630, -1, 35, 1, 56 }, ++ { 0x800001, 0x800001, 222, 2631, -1, 12, 1, 61 }, ++ { 0x1, 0x1, 222, 2632, -1, 35, 1, 56 }, ++ { 0x1800001, 0x1800001, 222, 2633, -1, 12, 1, 61 }, ++ { 0x3, 0x3, 222, 2634, -1, 35, 1, 56 }, ++ { 0xa00001, 0xa00001, 222, 2635, -1, 12, 1, 61 }, ++ { 0x5, 0x5, 222, 2636, -1, 33, 1, 56 }, ++ { 0x1a00001, 0x1a00001, 222, 2637, -1, 12, 1, 51 }, ++ { 0xd, 0xd, 222, 2638, -1, 33, 1, 51 }, ++ { 0x1, 0x1, 222, 2639, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2640, -1, 35, 1, 56 }, ++ { 0x1, 0x1, 222, 2641, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2642, -1, 35, 1, 56 }, ++ { 0x1, 0x1, 222, 2643, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2644, -1, 35, 1, 56 }, ++ { 0x1, 0x1, 222, 2645, -1, 35, 1, 51 }, ++ { 0x1, 0x1, 222, 2646, -1, 35, 1, 51 }, ++ { 0x800001, 0x800001, 222, 2647, -1, 12, 1, 61 }, ++ { 0x1, 0x1, 222, 2648, -1, 35, 1, 56 }, ++ { 0xa00001, 0x1a00001, 222, 2649, -1, 12, 1, 61 }, ++ { 0x5, 0xd, 222, 2650, -1, 33, 1, 56 }, ++ { 0x1, 0x1, 222, 2651, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2652, -1, 35, 1, 56 }, ++ { 0x1, 0x1, 222, 2653, -1, 35, 1, 61 }, ++ { 0x1, 0x1, 222, 2654, -1, 35, 1, 56 }, ++ { 0x81, 0x81, 222, 2655, -1, 28, 1, 31 }, ++ { 0x1, 0x1, 222, 2656, -1, 35, 1, 31 }, ++ { 0x103, 0x103, 222, 2657, -1, 27, 1, 31 }, ++ { 0x101, 0x101, 222, 2658, -1, 27, 1, 31 }, ++ { 0x1, 0x1, 222, 2659, -1, 35, 1, 65 }, ++ { 0x1, 0x1, 222, 2660, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2661, -1, 35, 1, 31 }, ++ { 0x3, 0x3, 222, 2662, -1, 35, 1, 65 }, ++ { 0x5, 0x5, 222, 2663, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2664, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2665, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2666, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2667, -1, 35, 1, 65 }, ++ { 0x1, 0x1, 222, 2668, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2669, -1, 35, 1, 31 }, ++ { 0x3, 0x3, 222, 2670, -1, 35, 1, 65 }, ++ { 0x5, 0x5, 222, 2671, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2672, -1, 35, 1, 31 }, ++ { 0x3, 0x3, 222, 2673, -1, 35, 1, 65 }, ++ { 0x5, 0x5, 222, 2674, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2675, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2676, -1, 35, 1, 65 }, ++ { 0x1, 0x1, 222, 2677, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2678, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2679, -1, 35, 1, 65 }, ++ { 0x1, 0x1, 222, 2680, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2681, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2682, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2683, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2684, -1, 35, 1, 51 }, ++ { 0x101, 0x101, 222, 2685, -1, 27, 1, 51 }, ++ { 0x81, 0x81, 222, 2686, -1, 28, 1, 51 }, ++ { 0x103, 0x103, 222, 2687, -1, 27, 1, 51 }, ++ { 0x41, 0x41, 222, 2688, -1, 29, 1, 51 }, ++ { 0x105, 0x105, 222, 2689, -1, 27, 1, 51 }, ++ { 0x83, 0x83, 222, 2690, -1, 28, 1, 51 }, ++ { 0x107, 0x107, 222, 2691, -1, 27, 1, 51 }, ++ { 0x1, 0x1, 222, 2692, -1, 35, 1, 51 }, ++ { 0x1, 0x1, 222, 2693, -1, 35, 1, 51 }, ++ { 0x1, 0x1, 222, 2694, -1, 35, 1, 51 }, ++ { 0x1, 0x1, 222, 2695, -1, 35, 1, 51 }, ++ { 0x81, 0x81, 222, 2696, -1, 28, 1, 31 }, ++ { 0x1, 0x1, 222, 2697, -1, 35, 1, 31 }, ++ { 0x103, 0x103, 222, 2698, -1, 27, 1, 31 }, ++ { 0x101, 0x101, 222, 2699, -1, 27, 1, 31 }, ++ { 0x1, 0x1, 222, 2700, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2701, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2702, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2703, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2704, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2705, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2706, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2707, -1, 35, 1, 26 }, ++ { 0x1, 0x1, 222, 2708, -1, 35, 1, 26 }, ++ { 0x1, 0x1, 222, 2709, -1, 35, 1, 26 }, ++ { 0x1, 0x1, 222, 2710, -1, 35, 1, 26 }, ++ { 0x1, 0x1, 222, 2711, -1, 35, 1, 37 }, ++ { 0x1, 0x1, 222, 2712, -1, 35, 1, 65 }, ++ { 0x1, 0x1, 222, 2713, -1, 35, 1, 31 }, ++ { 0x1, 0x1, 222, 2714, -1, 35, 1, 31 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 65 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2209, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 47 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 43 }, ++ { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x3, 0x3, 223, 2930, -1, 34, 1, 57 }, ++ { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x7, 0x7, 223, 2931, -1, 34, 1, 57 }, ++ { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x7, 0x7, 223, 2932, -1, 33, 1, 57 }, ++ { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 52 }, ++ { 0xf, 0xf, 223, 2933, -1, 33, 1, 52 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2934, -1, 34, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2935, -1, 34, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2936, -1, 34, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 52 }, ++ { 0x3, 0x3, 223, 2937, -1, 34, 1, 52 }, ++ { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x3, 0x3, 223, 2942, -1, 34, 1, 57 }, ++ { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x7, 0xf, 223, 2943, -1, 33, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2944, -1, 34, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2945, -1, 34, 1, 57 }, ++ { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x3, 0x3, 223, 2948, -1, 34, 1, 57 }, ++ { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x7, 0x7, 223, 2949, -1, 34, 1, 57 }, ++ { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x7, 0x7, 223, 2950, -1, 33, 1, 57 }, ++ { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 52 }, ++ { 0xf, 0xf, 223, 2951, -1, 33, 1, 52 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2952, -1, 34, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2953, -1, 34, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2954, -1, 34, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 52 }, ++ { 0x3, 0x3, 223, 2955, -1, 34, 1, 52 }, ++ { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x3, 0x3, 223, 2960, -1, 34, 1, 57 }, ++ { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 62 }, ++ { 0x7, 0xf, 223, 2961, -1, 33, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2962, -1, 34, 1, 57 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 62 }, ++ { 0x3, 0x3, 223, 2963, -1, 34, 1, 57 }, ++ { 0xc1, 0xc1, 223, -1, -1, 28, 1, 32 }, ++ { 0x3, 0x3, 223, 2828, -1, 34, 1, 32 }, ++ { 0x183, 0x183, 223, -1, -1, 27, 1, 32 }, ++ { 0x181, 0x181, 223, 2829, -1, 27, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 65 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2210, -1, 34, 1, 32 }, ++ { 0x7, 0x7, 223, -1, -1, 34, 1, 65 }, ++ { 0xb, 0xb, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2211, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 65 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2214, -1, 34, 1, 32 }, ++ { 0x7, 0x7, 223, -1, -1, 34, 1, 65 }, ++ { 0xb, 0xb, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2215, -1, 34, 1, 32 }, ++ { 0x7, 0x7, 223, -1, -1, 34, 1, 65 }, ++ { 0xb, 0xb, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2217, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 65 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2219, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 65 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2220, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 52 }, ++ { 0x181, 0x181, 223, -1, -1, 27, 1, 52 }, ++ { 0xc1, 0xc1, 223, -1, -1, 28, 1, 52 }, ++ { 0x183, 0x183, 223, -1, -1, 27, 1, 52 }, ++ { 0x61, 0x61, 223, -1, -1, 29, 1, 52 }, ++ { 0x185, 0x185, 223, -1, -1, 27, 1, 52 }, ++ { 0xc3, 0xc3, 223, -1, -1, 28, 1, 52 }, ++ { 0x187, 0x187, 223, -1, -1, 27, 1, 52 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 52 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 52 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 52 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 52 }, ++ { 0xc1, 0xc1, 223, -1, -1, 28, 1, 32 }, ++ { 0x3, 0x3, 223, 2832, -1, 34, 1, 32 }, ++ { 0x183, 0x183, 223, -1, -1, 27, 1, 32 }, ++ { 0x181, 0x181, 223, 2833, -1, 27, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 27 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 27 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 27 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 27 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 38 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 65 }, ++ { 0x3, 0x3, 223, -1, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 223, 2222, -1, 34, 1, 32 }, ++ { 0x3, 0x3, 224, 522, 1433, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 523, 1442, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 524, 1451, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 525, 1464, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 526, 1473, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 527, 1482, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 528, 1491, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 529, 1500, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 530, 1509, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 531, 1518, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 532, 1528, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 533, 1538, 32, 1, 128 }, ++ { 0x3, 0x3, 224, 546, 1551, 32, 1, 143 }, ++ { 0x3, 0x3, 224, 547, 1557, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 548, 1563, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 549, 1569, 32, 1, 143 }, ++ { 0x3, 0x3, 224, 550, 1575, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 551, 1581, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 552, 1587, 32, 1, 143 }, ++ { 0x3, 0x3, 224, 553, 1593, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 554, 1599, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 555, 1605, 32, 1, 143 }, ++ { 0x3, 0x3, 224, 556, 1611, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 557, 1617, 32, 1, 143 }, ++ { 0x3, 0x3, 224, 558, 1623, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 559, 1629, 32, 1, 143 }, ++ { 0x3, 0x3, 224, 560, 1635, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 561, 1641, 32, 1, 143 }, ++ { 0x3, 0x3, 224, 562, 1647, 32, 1, 148 }, ++ { 0x3, 0x3, 224, 563, 1653, 32, 1, 148 }, ++ { 0x1, 0x1, 225, -1, -1, 28, 1, 33 }, ++ { 0x1, 0x1, 225, -1, -1, 28, 1, 33 }, ++ { 0x0, 0x0, 232, 940, -1, 0, 1, 137 }, ++ { 0x0, 0x0, 232, 941, -1, 0, 1, 155 }, ++ { 0x1, 0x1, 233, -1, 1964, 33, 1, 133 }, ++ { 0x1, 0x1, 233, -1, 1967, 33, 1, 139 }, ++ { 0x0, 0x0, 233, -1, 1969, 0, 1, 150 }, ++ { 0x0, 0x0, 233, -1, 1970, 0, 1, 156 }, ++ { 0x0, 0x0, 234, 865, 953, 0, 0, -1 }, ++ { 0x0, 0x0, 234, 866, 961, 0, 0, -1 }, ++ { 0x0, 0x0, 234, 867, 957, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 868, 602, 33, 1, 6 }, ++ { 0x8000001, 0x8000001, 234, 869, 610, 6, 1, 7 }, ++ { 0x1, 0x1, 234, 870, 606, 33, 1, 6 }, ++ { 0x0, 0x0, 234, 871, 965, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 872, 622, 33, 1, 8 }, ++ { 0x0, 0x0, 234, 873, 969, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 874, 634, 33, 1, 15 }, ++ { 0x0, 0x0, 234, 875, 974, 0, 0, -1 }, ++ { 0x0, 0x0, 234, 876, 978, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 877, 657, 33, 1, 17 }, ++ { 0x1, 0x1, 234, 878, 661, 33, 1, 17 }, ++ { 0x0, 0x0, 234, 879, 982, 0, 0, -1 }, ++ { 0x0, 0x0, 234, 880, 986, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 881, 681, 33, 1, 18 }, ++ { 0x8000001, 0x8000001, 234, 882, 685, 6, 1, 18 }, ++ { 0x0, 0x0, 234, 883, 990, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 884, 697, 33, 1, 19 }, ++ { 0x0, 0x0, 234, 885, 994, 0, 0, -1 }, ++ { 0x0, 0x0, 234, 886, 998, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 887, 717, 33, 1, 20 }, ++ { 0x8000001, 0x8000001, 234, 888, 721, 6, 1, 20 }, ++ { 0x0, 0x0, 234, 889, 1002, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 890, 733, 33, 1, 21 }, ++ { 0x0, 0x0, 234, 891, 1007, 0, 0, -1 }, ++ { 0x0, 0x0, 234, 892, 1011, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 893, 756, 33, 1, 17 }, ++ { 0x1, 0x1, 234, 894, 760, 33, 1, 17 }, ++ { 0x0, 0x0, 234, 895, 1015, 0, 0, -1 }, ++ { 0x1, 0x1, 234, 896, 772, 33, 1, 21 }, ++ { 0x0, 0x0, 235, 2753, 952, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2754, 960, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2755, 956, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2756, 601, 0, 1, 6 }, ++ { 0x1, 0x1, 235, 2757, 609, 6, 1, 7 }, ++ { 0x0, 0x0, 235, 2758, 605, 0, 1, 6 }, ++ { 0x0, 0x0, 235, 2759, 964, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2760, 621, 0, 1, 8 }, ++ { 0x0, 0x0, 235, 2761, 968, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2762, 633, 0, 1, 15 }, ++ { 0x0, 0x0, 235, 2763, 973, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2764, 977, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2765, 656, 0, 1, 17 }, ++ { 0x0, 0x0, 235, 2766, 660, 0, 1, 17 }, ++ { 0x0, 0x0, 235, 2767, 981, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2768, 985, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2769, 680, 0, 1, 18 }, ++ { 0x1, 0x1, 235, 2770, 684, 6, 1, 18 }, ++ { 0x0, 0x0, 235, 2771, 989, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2772, 696, 0, 1, 19 }, ++ { 0x0, 0x0, 235, 2773, 993, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2774, 997, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2775, 716, 0, 1, 20 }, ++ { 0x1, 0x1, 235, 2776, 720, 6, 1, 20 }, ++ { 0x0, 0x0, 235, 2777, 1001, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2778, 732, 0, 1, 21 }, ++ { 0x0, 0x0, 235, 2779, 1006, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2780, 1010, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2781, 755, 0, 1, 17 }, ++ { 0x0, 0x0, 235, 2782, 759, 0, 1, 17 }, ++ { 0x0, 0x0, 235, 2783, 1014, 0, 0, -1 }, ++ { 0x0, 0x0, 235, 2784, 771, 0, 1, 21 }, ++ { 0x1, 0x1, 235, 897, 1137, 27, 1, 16 }, ++ { 0x0, 0x0, 235, 898, 1135, 0, 1, 16 }, ++ { 0x0, 0x0, 235, 1202, 1139, 0, 1, 22 }, ++ { 0x0, 0x1, 235, 1147, 1145, 20, 1, 67 }, ++ { 0x0, 0x0, 235, 111, 1143, 0, 1, 67 }, ++ { 0x1, 0x1, 238, -1, -1, 29, 1, 0 }, ++ { 0x0, 0x0, 238, -1, -1, 0, 1, 0 }, ++ { 0x1, 0x1, 238, 2984, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 238, 2985, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 238, 2986, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 238, 2987, -1, 27, 1, 0 }, ++ { 0x0, 0x0, 260, -1, 2310, 0, 0, -1 }, ++ { 0x0, 0x0, 260, -1, 2312, 0, 0, -1 }, ++ { 0x1, 0x1, 260, -1, -1, 28, 1, 29 }, ++ { 0x1, 0x1, 260, -1, -1, 28, 1, 29 }, ++ { 0x0, 0x0, 260, -1, 2351, 0, 0, -1 }, ++ { 0x0, 0x0, 260, -1, 2353, 0, 0, -1 }, ++ { 0x1, 0x1, 260, -1, -1, 28, 1, 29 }, ++ { 0x1, 0x1, 260, -1, -1, 28, 1, 29 }, ++ { 0x0, 0x0, 262, 23, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 262, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 262, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x1, 262, -1, -1, 29, 1, 0 }, ++ { 0x0, 0x1, 262, -1, -1, 29, 1, 0 }, ++ { 0x0, 0x1, 262, -1, -1, 29, 1, 0 }, ++ { 0x0, 0x1, 262, -1, -1, 29, 1, 0 }, ++ { 0x0, 0x1, 262, -1, -1, 29, 1, 0 }, ++ { 0x0, 0x0, 262, 180, -1, 0, 1, 0 }, ++ { 0x0, 0x1, 262, -1, -1, 29, 1, 0 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 299, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 321, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 347, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 369, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 64 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 64 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 64 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 64 }, ++ { 0x0, 0x0, 263, -1, 2262, 0, 0, -1 }, ++ { 0x0, 0x0, 263, -1, 2264, 0, 0, -1 }, ++ { 0x0, 0x0, 263, -1, 2266, 0, 0, -1 }, ++ { 0x0, 0x0, 263, -1, 2268, 0, 0, -1 }, ++ { 0x1, 0x1, 263, -1, 2270, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, 2272, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, 2274, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, 2276, 12, 1, 49 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 49 }, ++ { 0x0, 0x0, 263, -1, 2278, 0, 0, -1 }, ++ { 0x0, 0x0, 263, -1, 2280, 0, 0, -1 }, ++ { 0x1, 0x1, 263, -1, 2282, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, 2284, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x0, 0x0, 263, -1, 2286, 0, 0, -1 }, ++ { 0x0, 0x0, 263, -1, 2288, 0, 0, -1 }, ++ { 0x0, 0x0, 263, -1, 2290, 0, 0, -1 }, ++ { 0x0, 0x0, 263, -1, 2292, 0, 0, -1 }, ++ { 0x1, 0x1, 263, -1, 2294, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, 2296, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, 2298, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, 2300, 12, 1, 49 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 49 }, ++ { 0x0, 0x0, 263, -1, 2302, 0, 0, -1 }, ++ { 0x0, 0x0, 263, -1, 2304, 0, 0, -1 }, ++ { 0x1, 0x1, 263, -1, 2306, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, 2308, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, -1, -1, 12, 1, 59 }, ++ { 0x1, 0x1, 263, 391, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 393, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 507, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 509, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 399, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 401, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 515, -1, 12, 1, 2 }, ++ { 0x1, 0x1, 263, 517, -1, 12, 1, 2 }, ++ { 0x0, 0x0, 264, -1, 2269, 0, 0, -1 }, ++ { 0x9, 0x9, 264, -1, 2277, 33, 1, 49 }, ++ { 0x9, 0x9, 264, -1, 2941, 33, 1, 49 }, ++ { 0x0, 0x0, 264, 1381, 2342, 0, 0, -1 }, ++ { 0x3, 0x3, 264, 1382, -1, 27, 1, 49 }, ++ { 0x0, 0x0, 268, 2822, -1, 0, 1, 0 }, ++ { 0x3, 0x3, 269, -1, -1, 27, 1, 0 }, ++ { 0x3, 0x3, 269, -1, -1, 27, 1, 0 }, ++ { 0x3, 0x3, 269, -1, -1, 27, 1, 0 }, ++ { 0x3, 0x3, 269, -1, -1, 27, 1, 0 }, ++ { 0x1, 0x1, 270, 2980, -1, 28, 1, 0 }, ++ { 0x1, 0x1, 270, 2981, -1, 28, 1, 0 }, ++ { 0x1, 0x1, 270, 2982, -1, 28, 1, 0 }, ++ { 0x1, 0x1, 270, 2983, -1, 28, 1, 0 }, ++ { 0x1, 0x1, 271, -1, -1, 27, 1, 93 }, ++ { 0x1, 0x1, 271, -1, -1, 27, 1, 93 }, ++ { 0x0, 0x0, 271, -1, 950, 0, 0, -1 }, ++ { 0x0, 0x0, 272, 2993, 2799, 0, 0, -1 }, ++ { 0x0, 0x0, 272, 2994, 2801, 0, 0, -1 }, ++ { 0x0, 0x0, 273, -1, 2800, 0, 0, -1 }, ++ { 0x0, 0x0, 273, -1, 2802, 0, 0, -1 }, ++ { 0x0, 0x0, 274, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 274, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 274, -1, -1, 0, 1, 40 }, ++ { 0x0, 0x0, 279, -1, -1, 0, 1, 33 }, ++ { 0x0, 0x0, 283, -1, 2316, 0, 1, 29 }, ++ { 0x0, 0x0, 284, -1, -1, 0, 1, 0 }, ++ { 0x0, 0x0, 284, -1, -1, 0, 1, 71 }, ++ { 0x0, 0x0, 284, 1983, 2966, 0, 1, 1 }, ++ { 0x0, 0x0, 284, 1984, 2967, 0, 1, 1 }, ++ { 0x0, 0x0, 284, -1, 508, 0, 0, -1 }, ++ { 0x0, 0x0, 284, -1, 510, 0, 0, -1 }, ++ { 0x0, 0x0, 284, 1987, 2970, 0, 1, 1 }, ++ { 0x0, 0x0, 284, 1988, 2971, 0, 1, 1 }, ++ { 0x0, 0x0, 284, -1, 516, 0, 0, -1 }, ++ { 0x0, 0x0, 284, -1, 518, 0, 0, -1 }, ++}; ++ ++static const struct ia64_main_table ++main_table[] = { ++ { 5, 1, 1, 0x0000010000000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 0, }, ++ { 5, 1, 1, 0x0000010008000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 1, }, ++ { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 66, 27, 0, 0 }, 0x0, 2, }, ++ { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 63, 26, 0, 0 }, 0x0, 3, }, ++ { 6, 1, 1, 0x0000012000000000ull, 0x000001e000000000ull, { 24, 66, 27, 0, 0 }, 0x0, 4, }, ++ { 7, 1, 1, 0x0000010040000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 5, }, ++ { 7, 1, 1, 0x0000010c00000000ull, 0x000001ee00000000ull, { 24, 63, 26, 0, 0 }, 0x0, 6, }, ++ { 8, 1, 1, 0x0000010800000000ull, 0x000001ee00000000ull, { 24, 63, 26, 0, 0 }, 0x0, 7, }, ++ { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 3, 52, 53, 54 }, 0x221, 8, }, ++ { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 52, 53, 54, 0 }, 0x261, 9, }, ++ { 10, 1, 1, 0x0000010060000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 10, }, ++ { 10, 1, 1, 0x0000010160000000ull, 0x000001eff8000000ull, { 24, 55, 26, 0, 0 }, 0x0, 11, }, ++ { 11, 1, 1, 0x0000010068000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 12, }, ++ { 11, 1, 1, 0x0000010168000000ull, 0x000001eff8000000ull, { 24, 55, 26, 0, 0 }, 0x0, 13, }, ++ { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011ffull, { 16, 0, 0, 0, 0 }, 0x40, 951, }, ++ { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x0, 807, }, ++ { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x40, 808, }, ++ { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x200, 2200, }, ++ { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x240, 2201, }, ++ { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x0, 564, }, ++ { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x40, 565, }, ++ { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011ffull, { 81, 0, 0, 0, 0 }, 0x40, 972, }, ++ { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 81, 0, 0, 0, 0 }, 0x0, 809, }, ++ { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 81, 0, 0, 0, 0 }, 0x40, 810, }, ++ { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 81, 0, 0, 0, 0 }, 0x210, 2991, }, ++ { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 81, 0, 0, 0, 0 }, 0x250, 2992, }, ++ { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 81, 0, 0, 0, 0 }, 0x30, 572, }, ++ { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 81, 0, 0, 0, 0 }, 0x70, 573, }, ++ { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 81, 0, 0, 0, 0 }, 0x230, 570, }, ++ { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 81, 0, 0, 0, 0 }, 0x270, 571, }, ++ { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 81, 0, 0, 0 }, 0x0, 566, }, ++ { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 81, 0, 0, 0 }, 0x40, 567, }, ++ { 15, 4, 0, 0x0000000000000000ull, 0x000001e1f8000000ull, { 65, 0, 0, 0, 0 }, 0x0, 519, }, ++ { 15, 5, 0, 0x0000000000000000ull, 0x000001e3f8000000ull, { 65, 0, 0, 0, 0 }, 0x0, 942, }, ++ { 15, 2, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 65, 0, 0, 0, 0 }, 0x2, 1120, }, ++ { 15, 3, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 65, 0, 0, 0, 0 }, 0x0, 1245, }, ++ { 15, 6, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 69, 0, 0, 0, 0 }, 0x0, 2995, }, ++ { 15, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 65, 0, 0, 0, 0 }, 0x0, 16, }, ++ { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011ffull, { 82, 0, 0, 0, 0 }, 0x40, 1005, }, ++ { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x0, 811, }, ++ { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x40, 812, }, ++ { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x0, 568, }, ++ { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x40, 569, }, ++ { 17, 4, 0, 0x0000004080000000ull, 0x000001e9f8000018ull, { 16, 77, 0, 0, 0 }, 0x20, 2818, }, ++ { 17, 4, 0, 0x000000e000000000ull, 0x000001e800000018ull, { 81, 77, 0, 0, 0 }, 0x20, 2819, }, ++ { 18, 4, 0, 0x0000000060000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x2c, 222, }, ++ { 22, 2, 0, 0x0000000200000000ull, 0x000001ee00000000ull, { 25, 80, 0, 0, 0 }, 0x0, 2205, }, ++ { 22, 3, 0, 0x0000000800000000ull, 0x000001ee00000000ull, { 24, 81, 0, 0, 0 }, 0x0, 224, }, ++ { 22, 3, 0, 0x0000000c00000000ull, 0x000001ee00000000ull, { 18, 81, 0, 0, 0 }, 0x0, 225, }, ++ { 22, 3, 0, 0x0000002200000000ull, 0x000001ee00000000ull, { 25, 80, 0, 0, 0 }, 0x0, 2206, }, ++ { 22, 3, 0, 0x0000002600000000ull, 0x000001ee00000000ull, { 19, 80, 0, 0, 0 }, 0x0, 2207, }, ++ { 22, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 25, 80, 0, 0, 0 }, 0x0, 2208, }, ++ { 25, 4, 0, 0x0000000020000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 18, }, ++ { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1204, }, ++ { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1205, }, ++ { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1163, }, ++ { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1164, }, ++ { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1072, }, ++ { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1073, }, ++ { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1034, }, ++ { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1035, }, ++ { 26, 1, 2, 0x0000018200000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1358, }, ++ { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1074, }, ++ { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1075, }, ++ { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1208, }, ++ { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1209, }, ++ { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1169, }, ++ { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 55, 26, 0 }, 0x0, 1211, }, ++ { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 55, 26, 0, 0 }, 0x40, 1212, }, ++ { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 57, 26, 0 }, 0x0, 1170, }, ++ { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 57, 26, 0, 0 }, 0x40, 1171, }, ++ { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 57, 26, 0 }, 0x0, 1079, }, ++ { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 57, 26, 0, 0 }, 0x40, 1080, }, ++ { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 55, 26, 0 }, 0x0, 1041, }, ++ { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 55, 26, 0, 0 }, 0x40, 1042, }, ++ { 26, 1, 2, 0x0000018a00000000ull, 0x000001ee00001000ull, { 22, 23, 55, 26, 0 }, 0x40, 1363, }, ++ { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 23, 59, 26, 0 }, 0x0, 1196, }, ++ { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 59, 26, 0, 0 }, 0x40, 1197, }, ++ { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 22, 59, 26, 0 }, 0x0, 1107, }, ++ { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 59, 26, 0, 0 }, 0x40, 1108, }, ++ { 26, 1, 2, 0x000001c200000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1364, }, ++ { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1172, }, ++ { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1173, }, ++ { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1045, }, ++ { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1046, }, ++ { 26, 1, 2, 0x000001ca00000000ull, 0x000001ee00001000ull, { 23, 22, 55, 26, 0 }, 0x40, 1365, }, ++ { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1217, }, ++ { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1218, }, ++ { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1176, }, ++ { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1177, }, ++ { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1085, }, ++ { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1086, }, ++ { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1047, }, ++ { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1048, }, ++ { 27, 1, 2, 0x0000018600000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1370, }, ++ { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1087, }, ++ { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1088, }, ++ { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1221, }, ++ { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1222, }, ++ { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1182, }, ++ { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 55, 26, 0 }, 0x0, 1224, }, ++ { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 55, 26, 0, 0 }, 0x40, 1225, }, ++ { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 57, 26, 0 }, 0x0, 1183, }, ++ { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 57, 26, 0, 0 }, 0x40, 1184, }, ++ { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 57, 26, 0 }, 0x0, 1092, }, ++ { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 57, 26, 0, 0 }, 0x40, 1093, }, ++ { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 55, 26, 0 }, 0x0, 1054, }, ++ { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 55, 26, 0, 0 }, 0x40, 1055, }, ++ { 27, 1, 2, 0x0000018e00000000ull, 0x000001ee00001000ull, { 22, 23, 55, 26, 0 }, 0x40, 1375, }, ++ { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1241, }, ++ { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1242, }, ++ { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1200, }, ++ { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1201, }, ++ { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1111, }, ++ { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1112, }, ++ { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1070, }, ++ { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1071, }, ++ { 27, 1, 2, 0x000001c600000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1376, }, ++ { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1185, }, ++ { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1186, }, ++ { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1058, }, ++ { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1059, }, ++ { 27, 1, 2, 0x000001ce00000000ull, 0x000001ee00001000ull, { 23, 22, 55, 26, 0 }, 0x40, 1377, }, ++ { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 33, 25, 1, 2 }, 0x0, 257, }, ++ { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x40, 258, }, ++ { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 33, 25, 2, 0 }, 0x0, 259, }, ++ { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x40, 260, }, ++ { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 33, 25, 2, 0 }, 0x0, 261, }, ++ { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x40, 262, }, ++ { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 33, 25, 2, 0 }, 0x0, 263, }, ++ { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x40, 264, }, ++ { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 33, 25, 2, 0 }, 0x0, 265, }, ++ { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x40, 266, }, ++ { 34, 4, 0, 0x0000000010000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 19, }, ++ { 36, 2, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1149, }, ++ { 37, 2, 1, 0x00000000c8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1150, }, ++ { 39, 2, 1, 0x0000008000000000ull, 0x000001e000000000ull, { 24, 25, 26, 47, 72 }, 0x0, 20, }, ++ { 39, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 73, 0 }, 0x0, 3000, }, ++ { 39, 2, 1, 0x000000a604000000ull, 0x000001ee04000000ull, { 24, 55, 45, 73, 0 }, 0x0, 3001, }, ++ { 39, 2, 1, 0x000000ae00000000ull, 0x000001ee00000000ull, { 24, 48, 26, 46, 73 }, 0x0, 21, }, ++ { 43, 4, 0, 0x0000000080000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x20, 22, }, ++ { 48, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 76, 73, 0 }, 0x0, 2836, }, ++ { 50, 5, 1, 0x0000000080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 24, }, ++ { 51, 5, 1, 0x0000010008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2257, }, ++ { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2258, }, ++ { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 26, }, ++ { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2259, }, ++ { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 27, }, ++ { 54, 5, 1, 0x0000000160000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 28, }, ++ { 55, 5, 1, 0x0000000168000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 29, }, ++ { 57, 3, 0, 0x0000002180000000ull, 0x000001fff8000000ull, { 26, 0, 0, 0, 0 }, 0x0, 30, }, ++ { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 79, 0, 0, 0, 0 }, 0x0, 2260, }, ++ { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 79, 0, 0, 0, 0 }, 0x40, 31, }, ++ { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 23, 19, 60, 0 }, 0x0, 1247, }, ++ { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 19, 60, 0, 0 }, 0x40, 1248, }, ++ { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 22, 19, 60, 0 }, 0x40, 1402, }, ++ { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 19, 60, 0, 0 }, 0x40, 1403, }, ++ { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 2261, }, ++ { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x40, 32, }, ++ { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x0, 925, }, ++ { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 19, 20, 0, 0 }, 0x40, 926, }, ++ { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x40, 927, }, ++ { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x0, 1098, }, ++ { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 20, 19, 0, 0 }, 0x40, 1099, }, ++ { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x40, 1100, }, ++ { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x0, 1378, }, ++ { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 19, 20, 0, 0 }, 0x40, 1379, }, ++ { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x40, 1380, }, ++ { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x0, 1387, }, ++ { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 20, 19, 0, 0 }, 0x40, 1388, }, ++ { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x40, 1389, }, ++ { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1024, }, ++ { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1025, }, ++ { 62, 5, 1, 0x00000000e0000000ull, 0x000001e3f8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 2998, }, ++ { 62, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 2999, }, ++ { 63, 3, 1, 0x0000008488000000ull, 0x000001fff8000000ull, { 24, 33, 71, 0, 0 }, 0x0, 267, }, ++ { 64, 3, 1, 0x00000084c8000000ull, 0x000001fff8000000ull, { 24, 33, 71, 0, 0 }, 0x0, 268, }, ++ { 67, 3, 0, 0x0000000060000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 33, }, ++ { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2319, }, ++ { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 34, }, ++ { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2320, }, ++ { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 35, }, ++ { 70, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2213, }, ++ { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2321, }, ++ { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 36, }, ++ { 72, 5, 1, 0x00000001c8000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 1203, }, ++ { 73, 5, 1, 0x0000010000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2324, }, ++ { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2327, }, ++ { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 38, }, ++ { 75, 5, 1, 0x0000000088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 39, }, ++ { 76, 5, 1, 0x0000000088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 40, }, ++ { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2330, }, ++ { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 41, }, ++ { 78, 5, 1, 0x0000018000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2333, }, ++ { 79, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 2336, }, ++ { 80, 5, 1, 0x0000000170000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 44, }, ++ { 81, 5, 1, 0x0000002080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 45, }, ++ { 82, 5, 1, 0x0000000140000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 46, }, ++ { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2337, }, ++ { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 47, }, ++ { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2338, }, ++ { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 48, }, ++ { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 928, }, ++ { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 929, }, ++ { 85, 5, 1, 0x0000002188000000ull, 0x000001eff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 1101, }, ++ { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1026, }, ++ { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1027, }, ++ { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2355, }, ++ { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 49, }, ++ { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2356, }, ++ { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 50, }, ++ { 89, 5, 1, 0x0000002080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2221, }, ++ { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2357, }, ++ { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 51, }, ++ { 91, 5, 1, 0x0000013000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2358, }, ++ { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2359, }, ++ { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 53, }, ++ { 93, 5, 1, 0x0000002088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 54, }, ++ { 94, 5, 1, 0x0000002088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 55, }, ++ { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2360, }, ++ { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 56, }, ++ { 96, 5, 1, 0x000001b000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2361, }, ++ { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2362, }, ++ { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 58, }, ++ { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2363, }, ++ { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 59, }, ++ { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2364, }, ++ { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 60, }, ++ { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2365, }, ++ { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 61, }, ++ { 101, 5, 1, 0x000001c000000000ull, 0x000001f000000000ull, { 18, 20, 21, 19, 0 }, 0x0, 62, }, ++ { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 50, 51, 0, 0, 0 }, 0x0, 2366, }, ++ { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 50, 51, 0, 0, 0 }, 0x40, 63, }, ++ { 103, 5, 1, 0x0000014008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2369, }, ++ { 104, 5, 1, 0x00000001a0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 65, }, ++ { 105, 5, 1, 0x00000001e0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2168, }, ++ { 106, 3, 0, 0x0000000100000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 66, }, ++ { 108, 5, 1, 0x0000000178000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 67, }, ++ { 113, 3, 1, 0x0000008708000000ull, 0x000001ffc8000000ull, { 24, 19, 0, 0, 0 }, 0x0, 2747, }, ++ { 118, 4, 0, 0x0000004008000000ull, 0x000001e1f8000000ull, { 65, 0, 0, 0, 0 }, 0x0, 520, }, ++ { 118, 5, 0, 0x000000000c000000ull, 0x000001e3fc000000ull, { 65, 0, 0, 0, 0 }, 0x0, 943, }, ++ { 118, 2, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 65, 0, 0, 0, 0 }, 0x2, 1123, }, ++ { 118, 3, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 65, 0, 0, 0, 0 }, 0x0, 1249, }, ++ { 118, 6, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 69, 0, 0, 0, 0 }, 0x0, 2996, }, ++ { 118, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 65, 0, 0, 0, 0 }, 0x0, 68, }, ++ { 123, 3, 0, 0x0000000080000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 69, }, ++ { 123, 3, 0, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 0, 0, 0, 0 }, 0x0, 902, }, ++ { 123, 3, 0, 0x0000000098000000ull, 0x000001eff8000000ull, { 18, 0, 0, 0, 0 }, 0x0, 903, }, ++ { 124, 3, 0, 0x0000002170000000ull, 0x000001eff8000000ull, { 25, 0, 0, 0, 0 }, 0xc, 828, }, ++ { 125, 3, 1, 0x0000002070000000ull, 0x000001eff8000000ull, { 30, 25, 0, 0, 0 }, 0x8, 829, }, ++ { 125, 3, 1, 0x0000002078000000ull, 0x000001eff8000000ull, { 31, 25, 0, 0, 0 }, 0x8, 1125, }, ++ { 127, 3, 1, 0x0000008000000000ull, 0x000001fff8000000ull, { 24, 33, 0, 0, 0 }, 0x0, 70, }, ++ { 127, 3, 1, 0x0000009000000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x400, 71, }, ++ { 127, 3, 1, 0x000000a000000000ull, 0x000001eff0000000ull, { 24, 33, 62, 0, 0 }, 0x400, 72, }, ++ { 128, 3, 2, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 1, 33, 0, 0 }, 0x0, 73, }, ++ { 128, 3, 1, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 33, 0, 0, 0 }, 0x40, 74, }, ++ { 129, 3, 1, 0x0000008040000000ull, 0x000001fff8000000ull, { 24, 33, 0, 0, 0 }, 0x0, 75, }, ++ { 129, 3, 1, 0x0000009040000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x400, 76, }, ++ { 129, 3, 1, 0x000000a040000000ull, 0x000001eff0000000ull, { 24, 33, 62, 0, 0 }, 0x400, 77, }, ++ { 130, 3, 1, 0x0000008080000000ull, 0x000001fff8000000ull, { 24, 33, 0, 0, 0 }, 0x0, 78, }, ++ { 130, 3, 1, 0x0000009080000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x400, 79, }, ++ { 130, 3, 1, 0x000000a080000000ull, 0x000001eff0000000ull, { 24, 33, 62, 0, 0 }, 0x400, 80, }, ++ { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 33, 0, 0, 0 }, 0x0, 81, }, ++ { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 33, 83, 0, 0 }, 0x0, 1321, }, ++ { 131, 3, 1, 0x00000090c0000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x400, 82, }, ++ { 131, 3, 1, 0x000000a0c0000000ull, 0x000001eff0000000ull, { 24, 33, 62, 0, 0 }, 0x400, 83, }, ++ { 132, 3, 1, 0x000000c6c0000000ull, 0x000001fff8000000ull, { 18, 33, 0, 0, 0 }, 0x0, 1021, }, ++ { 132, 3, 1, 0x000000d6c0000000ull, 0x000001fff8000000ull, { 18, 33, 25, 0, 0 }, 0x400, 1022, }, ++ { 132, 3, 1, 0x000000e6c0000000ull, 0x000001eff0000000ull, { 18, 33, 62, 0, 0 }, 0x400, 1023, }, ++ { 133, 3, 1, 0x000000c040000000ull, 0x000001fff8000000ull, { 18, 33, 0, 0, 0 }, 0x0, 84, }, ++ { 133, 3, 1, 0x000000d040000000ull, 0x000001fff8000000ull, { 18, 33, 25, 0, 0 }, 0x400, 85, }, ++ { 133, 3, 1, 0x000000e040000000ull, 0x000001eff0000000ull, { 18, 33, 62, 0, 0 }, 0x400, 86, }, ++ { 134, 3, 1, 0x000000c0c0000000ull, 0x000001fff8000000ull, { 18, 33, 0, 0, 0 }, 0x0, 87, }, ++ { 134, 3, 1, 0x000000d0c0000000ull, 0x000001fff8000000ull, { 18, 33, 25, 0, 0 }, 0x400, 88, }, ++ { 134, 3, 1, 0x000000e0c0000000ull, 0x000001eff0000000ull, { 18, 33, 62, 0, 0 }, 0x400, 89, }, ++ { 135, 3, 1, 0x000000c000000000ull, 0x000001fff8000000ull, { 18, 33, 0, 0, 0 }, 0x0, 90, }, ++ { 135, 3, 1, 0x000000d000000000ull, 0x000001fff8000000ull, { 18, 33, 25, 0, 0 }, 0x400, 91, }, ++ { 135, 3, 1, 0x000000e000000000ull, 0x000001eff0000000ull, { 18, 33, 62, 0, 0 }, 0x400, 92, }, ++ { 136, 3, 2, 0x000000c048000000ull, 0x000001fff8000000ull, { 18, 19, 33, 0, 0 }, 0x0, 93, }, ++ { 136, 3, 2, 0x000000d048000000ull, 0x000001fff8000000ull, { 18, 19, 33, 6, 0 }, 0x400, 94, }, ++ { 137, 3, 2, 0x000000c0c8000000ull, 0x000001fff8000000ull, { 18, 19, 33, 0, 0 }, 0x0, 95, }, ++ { 137, 3, 2, 0x000000d0c8000000ull, 0x000001fff8000000ull, { 18, 19, 33, 6, 0 }, 0x400, 96, }, ++ { 138, 3, 2, 0x000000c088000000ull, 0x000001fff8000000ull, { 18, 19, 33, 0, 0 }, 0x0, 97, }, ++ { 138, 3, 2, 0x000000d088000000ull, 0x000001fff8000000ull, { 18, 19, 33, 5, 0 }, 0x400, 98, }, ++ { 139, 3, 1, 0x000000c080000000ull, 0x000001fff8000000ull, { 18, 33, 0, 0, 0 }, 0x0, 99, }, ++ { 139, 3, 1, 0x000000d080000000ull, 0x000001fff8000000ull, { 18, 33, 25, 0, 0 }, 0x400, 100, }, ++ { 139, 3, 1, 0x000000e080000000ull, 0x000001eff0000000ull, { 18, 33, 62, 0, 0 }, 0x400, 101, }, ++ { 142, 3, 0, 0x000000cb00000000ull, 0x000001fff8000000ull, { 33, 0, 0, 0, 0 }, 0x0, 102, }, ++ { 142, 3, 0, 0x000000db00000000ull, 0x000001fff8000000ull, { 33, 25, 0, 0, 0 }, 0x400, 103, }, ++ { 142, 3, 0, 0x000000eb00000000ull, 0x000001eff0000000ull, { 33, 62, 0, 0, 0 }, 0x400, 104, }, ++ { 143, 3, 0, 0x0000000050000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 105, }, ++ { 151, 3, 0, 0x0000000110000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 106, }, ++ { 152, 2, 1, 0x000000e880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2169, }, ++ { 153, 2, 1, 0x000000ea80000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2170, }, ++ { 154, 2, 1, 0x000000f880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2171, }, ++ { 155, 1, 1, 0x0000010800000000ull, 0x000001fff80fe000ull, { 24, 26, 0, 0, 0 }, 0x0, 107, }, ++ { 155, 1, 1, 0x0000012000000000ull, 0x000001e000300000ull, { 24, 66, 0, 0, 0 }, 0x40, 108, }, ++ { 155, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 109, }, ++ { 155, 2, 1, 0x0000000e00100000ull, 0x000001ee00f00000ull, { 15, 25, 0, 0, 0 }, 0x40, 110, }, ++ { 155, 2, 1, 0x0000000e00000000ull, 0x000001ee00f00000ull, { 15, 25, 78, 0, 0 }, 0x0, 2821, }, ++ { 155, 2, 1, 0x0000000188000000ull, 0x000001eff8000000ull, { 24, 16, 0, 0, 0 }, 0x0, 112, }, ++ { 155, 2, 1, 0x0000000600000000ull, 0x000001ee00000000ull, { 9, 25, 64, 0, 0 }, 0x0, 113, }, ++ { 155, 2, 1, 0x00000016ff001fc0ull, 0x000001feff001fc0ull, { 9, 25, 0, 0, 0 }, 0x40, 114, }, ++ { 155, 2, 1, 0x0000000400000000ull, 0x000001ee00000000ull, { 10, 68, 0, 0, 0 }, 0x0, 115, }, ++ { 155, 2, 1, 0x0000000180000000ull, 0x000001eff8000000ull, { 24, 8, 0, 0, 0 }, 0x0, 116, }, ++ { 155, 2, 1, 0x0000000198000000ull, 0x000001eff8000000ull, { 24, 9, 0, 0, 0 }, 0x0, 117, }, ++ { 155, 2, 1, 0x0000000150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1126, }, ++ { 155, 2, 1, 0x0000000050000000ull, 0x000001eff8000000ull, { 14, 55, 0, 0, 0 }, 0x0, 1127, }, ++ { 155, 2, 1, 0x0000000190000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1128, }, ++ { 155, 3, 1, 0x0000000140000000ull, 0x000001eff8000000ull, { 14, 55, 0, 0, 0 }, 0x0, 1250, }, ++ { 155, 3, 1, 0x0000002150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1251, }, ++ { 155, 3, 1, 0x0000002110000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1252, }, ++ { 155, 3, 1, 0x0000002160000000ull, 0x000001eff8000000ull, { 17, 25, 0, 0, 0 }, 0x8, 118, }, ++ { 155, 3, 1, 0x0000002120000000ull, 0x000001eff8000000ull, { 24, 17, 0, 0, 0 }, 0x8, 119, }, ++ { 155, 3, 1, 0x0000002168000000ull, 0x000001eff8000000ull, { 12, 25, 0, 0, 0 }, 0x8, 120, }, ++ { 155, 3, 1, 0x0000002148000000ull, 0x000001eff8000000ull, { 13, 25, 0, 0, 0 }, 0x0, 121, }, ++ { 155, 3, 1, 0x0000002128000000ull, 0x000001eff8000000ull, { 24, 11, 0, 0, 0 }, 0x8, 122, }, ++ { 155, 3, 1, 0x0000002108000000ull, 0x000001eff8000000ull, { 24, 13, 0, 0, 0 }, 0x0, 123, }, ++ { 155, 3, 1, 0x0000002000000000ull, 0x000001eff8000000ull, { 38, 25, 0, 0, 0 }, 0x8, 124, }, ++ { 155, 3, 1, 0x0000002008000000ull, 0x000001eff8000000ull, { 29, 25, 0, 0, 0 }, 0x8, 125, }, ++ { 155, 3, 1, 0x0000002010000000ull, 0x000001eff8000000ull, { 32, 25, 0, 0, 0 }, 0x8, 126, }, ++ { 155, 3, 1, 0x0000002018000000ull, 0x000001eff8000000ull, { 35, 25, 0, 0, 0 }, 0x8, 127, }, ++ { 155, 3, 1, 0x0000002020000000ull, 0x000001eff8000000ull, { 36, 25, 0, 0, 0 }, 0x8, 128, }, ++ { 155, 3, 1, 0x0000002028000000ull, 0x000001eff8000000ull, { 37, 25, 0, 0, 0 }, 0x8, 129, }, ++ { 155, 3, 1, 0x0000002030000000ull, 0x000001eff8000000ull, { 34, 25, 0, 0, 0 }, 0x8, 130, }, ++ { 155, 3, 1, 0x0000002080000000ull, 0x000001eff8000000ull, { 24, 38, 0, 0, 0 }, 0x8, 131, }, ++ { 155, 3, 1, 0x0000002088000000ull, 0x000001eff8000000ull, { 24, 29, 0, 0, 0 }, 0x8, 132, }, ++ { 155, 3, 1, 0x0000002090000000ull, 0x000001eff8000000ull, { 24, 32, 0, 0, 0 }, 0x8, 133, }, ++ { 155, 3, 1, 0x0000002098000000ull, 0x000001eff8000000ull, { 24, 35, 0, 0, 0 }, 0x8, 134, }, ++ { 155, 3, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 24, 36, 0, 0, 0 }, 0x8, 135, }, ++ { 155, 3, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 24, 37, 0, 0, 0 }, 0x0, 136, }, ++ { 155, 3, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 24, 34, 0, 0, 0 }, 0x8, 137, }, ++ { 155, 3, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 138, }, ++ { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 14, 0, 0, 0 }, 0x0, 139, }, ++ { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 55, 0, 0, 0 }, 0x0, 140, }, ++ { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 25, 0, 0, 0 }, 0x0, 141, }, ++ { 156, 6, 1, 0x000000c000000000ull, 0x000001e000100000ull, { 24, 70, 0, 0, 0 }, 0x0, 142, }, ++ { 157, 2, 1, 0x000000eca0000000ull, 0x000001fff0000000ull, { 24, 25, 74, 0, 0 }, 0x0, 143, }, ++ { 158, 2, 1, 0x000000eea0000000ull, 0x000001fff0000000ull, { 24, 25, 75, 0, 0 }, 0x0, 144, }, ++ { 168, 4, 0, 0x0000004000000000ull, 0x000001e1f8000000ull, { 65, 0, 0, 0, 0 }, 0x0, 521, }, ++ { 168, 5, 0, 0x0000000008000000ull, 0x000001e3fc000000ull, { 65, 0, 0, 0, 0 }, 0x0, 944, }, ++ { 168, 2, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 65, 0, 0, 0, 0 }, 0x2, 1129, }, ++ { 168, 3, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 65, 0, 0, 0, 0 }, 0x0, 1253, }, ++ { 168, 6, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 69, 0, 0, 0, 0 }, 0x0, 2997, }, ++ { 168, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 65, 0, 0, 0, 0 }, 0x0, 145, }, ++ { 175, 1, 1, 0x0000010070000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 146, }, ++ { 175, 1, 1, 0x0000010170000000ull, 0x000001eff8000000ull, { 24, 55, 26, 0, 0 }, 0x0, 147, }, ++ { 178, 2, 1, 0x000000ea00000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2979, }, ++ { 179, 2, 1, 0x000000f820000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2823, }, ++ { 180, 1, 1, 0x0000010400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 148, }, ++ { 181, 1, 1, 0x0000010600000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 149, }, ++ { 182, 1, 1, 0x0000011400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 150, }, ++ { 183, 1, 1, 0x0000010450000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 151, }, ++ { 184, 1, 1, 0x0000010650000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 152, }, ++ { 185, 1, 1, 0x0000010470000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 153, }, ++ { 186, 1, 1, 0x0000010670000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 154, }, ++ { 187, 1, 1, 0x0000010520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 930, }, ++ { 188, 1, 1, 0x0000010720000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 931, }, ++ { 189, 1, 1, 0x0000011520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 932, }, ++ { 190, 2, 1, 0x000000e850000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2837, }, ++ { 191, 2, 1, 0x000000ea70000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 155, }, ++ { 192, 2, 1, 0x000000e810000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2838, }, ++ { 193, 2, 1, 0x000000ea30000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 156, }, ++ { 194, 2, 1, 0x000000ead0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2172, }, ++ { 195, 2, 1, 0x000000e230000000ull, 0x000001ff30000000ull, { 24, 25, 26, 42, 0 }, 0x0, 157, }, ++ { 196, 2, 1, 0x000000e690000000ull, 0x000001fff0000000ull, { 24, 26, 0, 0, 0 }, 0x0, 158, }, ++ { 198, 3, 1, 0x00000021c0000000ull, 0x000001eff8000000ull, { 24, 26, 25, 0, 0 }, 0x0, 2173, }, ++ { 198, 3, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 24, 26, 49, 0, 0 }, 0x0, 2174, }, ++ { 198, 3, 0, 0x0000002188000000ull, 0x000001eff8000000ull, { 26, 49, 0, 0, 0 }, 0x0, 2204, }, ++ { 199, 2, 1, 0x000000e8b0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 159, }, ++ { 200, 2, 1, 0x000000e240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 160, }, ++ { 200, 2, 1, 0x000000ee50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 161, }, ++ { 201, 2, 1, 0x000000f040000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 162, }, ++ { 201, 2, 1, 0x000000fc50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 163, }, ++ { 202, 1, 1, 0x0000010680000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 164, }, ++ { 203, 2, 1, 0x000000e220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 165, }, ++ { 203, 2, 1, 0x000000e630000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 166, }, ++ { 204, 2, 1, 0x000000f020000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 167, }, ++ { 204, 2, 1, 0x000000f430000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 168, }, ++ { 205, 1, 1, 0x00000106c0000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 169, }, ++ { 206, 1, 1, 0x0000010420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 170, }, ++ { 207, 1, 1, 0x0000010620000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 171, }, ++ { 208, 1, 1, 0x0000011420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 172, }, ++ { 209, 3, 0, 0x0000002048000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 1157, }, ++ { 209, 3, 0, 0x0000002050000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0xc, 1032, }, ++ { 209, 3, 0, 0x00000021a0000000ull, 0x000001eff8000000ull, { 26, 0, 0, 0, 0 }, 0x8, 904, }, ++ { 210, 3, 0, 0x0000002060000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 830, }, ++ { 215, 4, 0, 0x0000000040000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x22c, 173, }, ++ { 216, 3, 0, 0x0000000038000000ull, 0x000001ee78000000ull, { 67, 0, 0, 0, 0 }, 0x8, 174, }, ++ { 217, 3, 0, 0x0000000028000000ull, 0x000001ee78000000ull, { 67, 0, 0, 0, 0 }, 0x0, 175, }, ++ { 226, 3, 1, 0x000000c708000000ull, 0x000001ffc8000000ull, { 18, 25, 0, 0, 0 }, 0x0, 2748, }, ++ { 227, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 0, 0 }, 0x140, 176, }, ++ { 227, 2, 1, 0x000000f240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 177, }, ++ { 228, 1, 1, 0x0000010080000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 178, }, ++ { 229, 1, 1, 0x00000100c0000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 179, }, ++ { 230, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 76, 0, 0 }, 0x140, 2844, }, ++ { 230, 2, 1, 0x000000f220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 181, }, ++ { 231, 2, 1, 0x000000ac00000000ull, 0x000001ee00000000ull, { 24, 25, 26, 44, 0 }, 0x0, 182, }, ++ { 236, 3, 0, 0x0000000180000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 832, }, ++ { 237, 3, 0, 0x0000000030000000ull, 0x000001ee78000000ull, { 67, 0, 0, 0, 0 }, 0x8, 183, }, ++ { 239, 3, 1, 0x0000008c00000000ull, 0x000001fff8000000ull, { 33, 25, 0, 0, 0 }, 0x0, 184, }, ++ { 239, 3, 1, 0x000000ac00000000ull, 0x000001eff0000000ull, { 33, 25, 61, 0, 0 }, 0x400, 185, }, ++ { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 33, 25, 1, 0, 0 }, 0x0, 186, }, ++ { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 33, 25, 0, 0, 0 }, 0x40, 187, }, ++ { 241, 3, 1, 0x0000008c40000000ull, 0x000001fff8000000ull, { 33, 25, 0, 0, 0 }, 0x0, 188, }, ++ { 241, 3, 1, 0x000000ac40000000ull, 0x000001eff0000000ull, { 33, 25, 61, 0, 0 }, 0x400, 189, }, ++ { 242, 3, 1, 0x0000008c80000000ull, 0x000001fff8000000ull, { 33, 25, 0, 0, 0 }, 0x0, 190, }, ++ { 242, 3, 1, 0x000000ac80000000ull, 0x000001eff0000000ull, { 33, 25, 61, 0, 0 }, 0x400, 191, }, ++ { 243, 3, 1, 0x0000008cc0000000ull, 0x000001fff8000000ull, { 33, 25, 0, 0, 0 }, 0x0, 192, }, ++ { 243, 3, 1, 0x000000acc0000000ull, 0x000001eff0000000ull, { 33, 25, 61, 0, 0 }, 0x400, 193, }, ++ { 244, 3, 1, 0x000000cec0000000ull, 0x000001fff8000000ull, { 33, 19, 0, 0, 0 }, 0x0, 2751, }, ++ { 244, 3, 1, 0x000000eec0000000ull, 0x000001eff0000000ull, { 33, 19, 61, 0, 0 }, 0x400, 2752, }, ++ { 245, 3, 1, 0x000000cc40000000ull, 0x000001fff8000000ull, { 33, 19, 0, 0, 0 }, 0x0, 194, }, ++ { 245, 3, 1, 0x000000ec40000000ull, 0x000001eff0000000ull, { 33, 19, 61, 0, 0 }, 0x400, 195, }, ++ { 246, 3, 1, 0x000000ccc0000000ull, 0x000001fff8000000ull, { 33, 19, 0, 0, 0 }, 0x0, 196, }, ++ { 246, 3, 1, 0x000000ecc0000000ull, 0x000001eff0000000ull, { 33, 19, 61, 0, 0 }, 0x400, 197, }, ++ { 247, 3, 1, 0x000000cc00000000ull, 0x000001fff8000000ull, { 33, 19, 0, 0, 0 }, 0x0, 198, }, ++ { 247, 3, 1, 0x000000ec00000000ull, 0x000001eff0000000ull, { 33, 19, 61, 0, 0 }, 0x400, 199, }, ++ { 248, 3, 1, 0x000000cc80000000ull, 0x000001fff8000000ull, { 33, 19, 0, 0, 0 }, 0x0, 200, }, ++ { 248, 3, 1, 0x000000ec80000000ull, 0x000001eff0000000ull, { 33, 19, 61, 0, 0 }, 0x400, 201, }, ++ { 249, 1, 1, 0x0000010028000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 202, }, ++ { 249, 1, 1, 0x0000010020000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 203, }, ++ { 249, 1, 1, 0x0000010128000000ull, 0x000001eff8000000ull, { 24, 55, 26, 0, 0 }, 0x0, 204, }, ++ { 250, 3, 0, 0x0000000020000000ull, 0x000001ee78000000ull, { 67, 0, 0, 0, 0 }, 0x0, 205, }, ++ { 251, 2, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 206, }, ++ { 252, 2, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 207, }, ++ { 253, 2, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 208, }, ++ { 254, 3, 0, 0x0000000198000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 1132, }, ++ { 255, 3, 1, 0x00000020f8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 209, }, ++ { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 23, 26, 76, 0 }, 0x0, 3002, }, ++ { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 26, 76, 0, 0 }, 0x40, 3003, }, ++ { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 22, 26, 76, 0 }, 0x40, 1985, }, ++ { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 26, 76, 0, 0 }, 0x40, 1986, }, ++ { 257, 3, 1, 0x00000020d0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 210, }, ++ { 258, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 23, 26, 0, 0 }, 0x0, 3006, }, ++ { 258, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 26, 0, 0, 0 }, 0x40, 3007, }, ++ { 258, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 22, 26, 0, 0 }, 0x40, 1989, }, ++ { 258, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 26, 0, 0, 0 }, 0x40, 1990, }, ++ { 259, 3, 1, 0x00000020f0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 211, }, ++ { 261, 3, 1, 0x00000020d8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 212, }, ++ { 265, 2, 1, 0x000000e840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1113, }, ++ { 266, 2, 1, 0x000000ea40000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1114, }, ++ { 267, 2, 1, 0x000000f840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1115, }, ++ { 275, 3, 1, 0x0000008208000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x0, 213, }, ++ { 276, 3, 1, 0x0000008248000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x0, 214, }, ++ { 277, 3, 1, 0x0000008288000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x0, 215, }, ++ { 278, 3, 1, 0x00000082c8000000ull, 0x000001fff8000000ull, { 24, 33, 25, 0, 0 }, 0x0, 216, }, ++ { 280, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 1161, }, ++ { 280, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 1243, }, ++ { 281, 5, 1, 0x000001d000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 1162, }, ++ { 282, 1, 1, 0x0000010078000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 217, }, ++ { 282, 1, 1, 0x0000010178000000ull, 0x000001eff8000000ull, { 24, 55, 26, 0, 0 }, 0x0, 218, }, ++ { 285, 2, 1, 0x0000000080000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 219, }, ++ { 286, 2, 1, 0x0000000088000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 220, }, ++ { 287, 2, 1, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 221, }, ++}; ++ ++static const char dis_table[] = { ++0xa0, 0xc5, 0xe8, 0xa0, 0x2e, 0x98, 0xa0, 0x2c, 0x80, 0xa0, 0x1b, 0xc0, ++0x98, 0xb0, 0x02, 0x50, 0x90, 0x50, 0x90, 0x28, 0x24, 0x38, 0x28, 0x24, ++0x38, 0x20, 0x90, 0x28, 0x24, 0x38, 0x18, 0x24, 0x38, 0x10, 0x91, 0x60, ++0x90, 0x28, 0x24, 0x38, 0x00, 0x10, 0x10, 0x58, 0x41, 0x61, 0xbf, 0xc0, ++0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, ++0x10, 0x10, 0x52, 0xc0, 0xc0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, ++0x10, 0x10, 0x10, 0x24, 0x23, 0x70, 0x90, 0x28, 0x24, 0x37, 0xf0, 0x24, ++0x37, 0xe8, 0xa8, 0x0b, 0x48, 0x15, 0x20, 0x97, 0x20, 0x95, 0xc8, 0x9a, ++0xb8, 0x05, 0x38, 0x91, 0x18, 0x90, 0xa0, 0x90, 0x60, 0x80, 0x90, 0x20, ++0x34, 0x86, 0xa4, 0x24, 0x00, 0x34, 0x83, 0x80, 0xa4, 0x35, 0xa0, 0x36, ++0xb9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x36, 0xaf, 0x80, 0x34, 0x66, 0x81, ++0x33, 0xe2, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x23, 0x10, 0x34, ++0x63, 0xa4, 0x1f, 0x08, 0x34, 0x60, 0x90, 0x38, 0xa4, 0x37, 0xa0, 0x36, ++0xfa, 0xa4, 0x37, 0x48, 0x36, 0xee, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x36, ++0x20, 0x36, 0xcf, 0xa4, 0x35, 0xf8, 0x36, 0xca, 0x80, 0xa4, 0x22, 0xf0, ++0x34, 0x5f, 0x92, 0x18, 0x91, 0xc0, 0x80, 0x91, 0x80, 0x90, 0xf8, 0xdb, ++0x84, 0x60, 0xf9, 0x40, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0x58, 0x8c, 0x42, ++0xb8, 0x84, 0x38, 0x61, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0x48, 0x8c, 0x42, ++0x98, 0x84, 0x38, 0x5f, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, ++0x13, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x11, 0xa4, 0x1f, 0x18, 0x33, 0xe4, ++0x80, 0x90, 0x28, 0x80, 0x33, 0xe0, 0x80, 0x34, 0x68, 0x81, 0x90, 0x38, ++0xa4, 0x23, 0x80, 0x34, 0x6b, 0xa4, 0x23, 0x48, 0x34, 0x65, 0xc0, 0x40, ++0x10, 0x10, 0x90, 0x38, 0xa4, 0x1e, 0xf0, 0x33, 0xdf, 0xa4, 0x1e, 0xe0, ++0x33, 0xdd, 0x18, 0x24, 0x23, 0xf8, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, ++0xc0, 0xc0, 0x80, 0xa4, 0x41, 0x28, 0x38, 0x4b, 0xc0, 0xc0, 0x80, 0xa4, ++0x41, 0x18, 0x38, 0x47, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, ++0x0d, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x0b, 0x92, 0xb8, 0x99, 0x84, 0x23, ++0x68, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x35, 0x98, 0x36, ++0xb8, 0x82, 0x36, 0xae, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x37, ++0x98, 0x36, 0xf9, 0xa4, 0x37, 0x40, 0x36, 0xed, 0x80, 0x90, 0x38, 0xa4, ++0x36, 0x18, 0x36, 0xce, 0xa4, 0x35, 0xf0, 0x36, 0xc9, 0x83, 0x90, 0xa8, ++0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x40, 0xf8, 0x38, 0x3f, 0xc0, ++0xc0, 0x80, 0xa4, 0x40, 0xe8, 0x38, 0x3b, 0xd3, 0x82, 0x40, 0x50, 0xc0, ++0xc0, 0x81, 0x38, 0x07, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x05, 0x18, 0x24, ++0x23, 0x78, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, ++0x40, 0xc8, 0x38, 0x33, 0xc0, 0xc0, 0x80, 0xa4, 0x40, 0xb8, 0x38, 0x2f, ++0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x01, 0x50, 0xc0, 0xc0, ++0x81, 0x37, 0xff, 0x94, 0x50, 0x92, 0xf8, 0x99, 0x84, 0x1f, 0x48, 0x90, ++0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x35, 0x90, 0x36, 0xb7, 0x82, ++0x36, 0xad, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x37, 0x90, 0x36, ++0xf8, 0xa4, 0x37, 0x38, 0x36, 0xec, 0x80, 0x90, 0x38, 0xa4, 0x36, 0x10, ++0x36, 0xcd, 0xa4, 0x35, 0xe8, 0x36, 0xc8, 0x83, 0x90, 0xe8, 0xd3, 0x83, ++0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0x68, 0x8c, 0x42, 0xd8, 0x84, 0x38, ++0x63, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0x50, 0x8c, 0x42, 0xa8, 0x84, 0x38, ++0x60, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x15, 0x50, 0xc0, ++0xc0, 0x81, 0x38, 0x12, 0x18, 0x24, 0x1f, 0x40, 0x83, 0x90, 0xa8, 0xd3, ++0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0x38, 0x38, 0x4f, 0xc0, 0xc0, ++0x80, 0xa4, 0x41, 0x20, 0x38, 0x49, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, ++0x81, 0x38, 0x0f, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x0c, 0x92, 0xb8, 0x99, ++0x84, 0x1f, 0x38, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x35, ++0x88, 0x36, 0xb6, 0x82, 0x36, 0xac, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, ++0xa4, 0x37, 0x88, 0x36, 0xf7, 0xa4, 0x37, 0x30, 0x36, 0xeb, 0x80, 0x90, ++0x38, 0xa4, 0x36, 0x08, 0x36, 0xcc, 0xa4, 0x35, 0xe0, 0x36, 0xc7, 0x83, ++0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0x08, 0x38, ++0x43, 0xc0, 0xc0, 0x80, 0xa4, 0x40, 0xf0, 0x38, 0x3d, 0xd3, 0x82, 0x40, ++0x50, 0xc0, 0xc0, 0x81, 0x38, 0x09, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x06, ++0x18, 0x20, 0x01, 0x48, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, ++0x80, 0xa4, 0x40, 0xd8, 0x38, 0x37, 0xc0, 0xc0, 0x80, 0xa4, 0x40, 0xc0, ++0x38, 0x31, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x03, 0x50, ++0xc0, 0xc0, 0x81, 0x38, 0x00, 0xda, 0x06, 0xe0, 0xf9, 0x80, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x23, 0xe8, 0x34, 0x7b, 0x80, 0x34, 0x78, 0x90, 0x38, ++0xa4, 0x23, 0x90, 0x34, 0x76, 0x80, 0x34, 0x73, 0x90, 0x60, 0x90, 0x38, ++0xa4, 0x23, 0xd0, 0x34, 0x7c, 0x80, 0x34, 0x79, 0x90, 0x38, 0xa4, 0x23, ++0xa8, 0x34, 0x77, 0x80, 0x34, 0x74, 0xc8, 0x40, 0x19, 0x00, 0x91, 0x58, ++0x90, 0x60, 0x82, 0x90, 0x20, 0x36, 0xab, 0xa4, 0x35, 0x48, 0x36, 0xaa, ++0x90, 0xc0, 0x80, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0xb9, 0x00, 0x85, ++0x36, 0xe3, 0xc9, 0xe1, 0xb8, 0x40, 0x85, 0x36, 0xe0, 0x80, 0x36, 0xdf, ++0x10, 0x10, 0x81, 0x36, 0xbb, 0x90, 0xa8, 0x10, 0x10, 0x90, 0x28, 0x81, ++0x36, 0xd9, 0x90, 0x38, 0xa4, 0x36, 0xa0, 0x36, 0xd5, 0xa4, 0x36, 0x90, ++0x36, 0xd3, 0x90, 0x70, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x36, 0xb8, 0x36, ++0xd8, 0x80, 0x36, 0xd6, 0x90, 0x60, 0x90, 0x28, 0x24, 0x36, 0xf0, 0xa4, ++0x36, 0xe0, 0x36, 0xdd, 0x80, 0xa4, 0x36, 0xd0, 0x36, 0xdb, 0x80, 0x90, ++0xf8, 0x90, 0x90, 0x90, 0x50, 0x90, 0x28, 0x80, 0x37, 0xf7, 0x80, 0x37, ++0xfe, 0x80, 0xa4, 0x3f, 0xe0, 0x37, 0xfd, 0x90, 0x28, 0x81, 0x37, 0xfb, ++0x80, 0xa4, 0x3f, 0xc8, 0x37, 0xfa, 0x83, 0x37, 0xf8, 0x98, 0xe8, 0x01, ++0xb0, 0x90, 0x88, 0x90, 0x60, 0xa4, 0x35, 0x38, 0x10, 0x10, 0x10, 0x10, ++0x83, 0x33, 0xb7, 0x24, 0x35, 0x30, 0x90, 0x28, 0x24, 0x35, 0x28, 0x24, ++0x35, 0x20, 0x90, 0x88, 0x90, 0x60, 0xa4, 0x35, 0x10, 0x10, 0x10, 0x10, ++0x10, 0x83, 0x33, 0xb6, 0x24, 0x35, 0x08, 0x90, 0x28, 0x24, 0x35, 0x00, ++0x24, 0x34, 0xf8, 0xa8, 0x09, 0x00, 0x0e, 0x20, 0x96, 0x48, 0x95, 0xe8, ++0x93, 0x38, 0x91, 0xa0, 0x90, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, ++0x60, 0x33, 0xcd, 0xa4, 0x1e, 0x50, 0x33, 0xcb, 0x90, 0x38, 0xa4, 0x1e, ++0x40, 0x33, 0xc9, 0x80, 0x33, 0xc7, 0x90, 0x60, 0x90, 0x28, 0x24, 0x1e, ++0x00, 0xa4, 0x1d, 0xf0, 0x33, 0xbf, 0x90, 0x38, 0xa4, 0x1d, 0xe0, 0x33, ++0xbd, 0xa4, 0x1e, 0x28, 0x33, 0xc6, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x1e, 0x18, 0x33, 0xc4, 0xa4, 0x1e, 0x08, 0x33, 0xc2, 0x90, 0x38, ++0xa4, 0x34, 0xb0, 0x36, 0x9c, 0xa4, 0x34, 0x50, 0x36, 0x90, 0x90, 0x70, ++0x90, 0x38, 0xa4, 0x31, 0x90, 0x36, 0x3e, 0xa4, 0x31, 0x60, 0x36, 0x38, ++0x10, 0x10, 0xa4, 0x1d, 0xd0, 0x33, 0xbb, 0x99, 0x60, 0x02, 0x70, 0x90, ++0x90, 0x90, 0x50, 0x90, 0x28, 0x24, 0x1e, 0x90, 0x80, 0x33, 0xda, 0x80, ++0xa4, 0x1e, 0x98, 0x33, 0xd8, 0x90, 0x50, 0x90, 0x28, 0x24, 0x1e, 0xa0, ++0x80, 0x33, 0xdb, 0x90, 0x38, 0xa4, 0x1e, 0xa8, 0x33, 0xd9, 0xa4, 0x1e, ++0x70, 0x33, 0xcf, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x33, 0xe8, ++0x36, 0x85, 0xa4, 0x33, 0x48, 0x36, 0x72, 0x90, 0x38, 0xa4, 0x32, 0xe0, ++0x36, 0x63, 0xa4, 0x32, 0x50, 0x36, 0x52, 0x81, 0xa4, 0x1e, 0x80, 0x33, ++0xd1, 0xe4, 0xa1, 0xfc, 0x40, 0x37, 0xf3, 0x18, 0x24, 0x1d, 0xc8, 0xe4, ++0xe1, 0xfa, 0xc0, 0x37, 0xed, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, ++0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x34, 0xa8, 0x36, 0x9b, 0xa4, 0x34, ++0x48, 0x36, 0x8f, 0x80, 0x90, 0x38, 0xa4, 0x31, 0x88, 0x36, 0x3d, 0xa4, ++0x31, 0x58, 0x36, 0x37, 0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, ++0x38, 0xa4, 0x33, 0xd8, 0x36, 0x84, 0xa4, 0x33, 0x40, 0x36, 0x70, 0x90, ++0x38, 0xa4, 0x32, 0xd0, 0x36, 0x62, 0xa4, 0x32, 0x48, 0x36, 0x50, 0xe4, ++0xa1, 0xf9, 0x40, 0x37, 0xe7, 0x18, 0x24, 0x1d, 0xc0, 0xe4, 0xe1, 0xf7, ++0xc0, 0x37, 0xe1, 0x92, 0x90, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, ++0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x34, 0xa0, 0x36, 0x9a, 0xa4, 0x34, ++0x40, 0x36, 0x8e, 0x80, 0x90, 0x38, 0xa4, 0x31, 0x80, 0x36, 0x3c, 0xa4, ++0x31, 0x50, 0x36, 0x36, 0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, ++0x38, 0xa4, 0x33, 0xc8, 0x36, 0x83, 0xa4, 0x33, 0x38, 0x36, 0x6e, 0x90, ++0x38, 0xa4, 0x32, 0xc0, 0x36, 0x61, 0xa4, 0x32, 0x40, 0x36, 0x4e, 0xe4, ++0xa1, 0xfc, 0x80, 0x37, 0xf5, 0x10, 0x10, 0xe4, 0xe1, 0xfb, 0x00, 0x37, ++0xef, 0x92, 0x50, 0x99, 0x1c, 0x1e, 0xb0, 0x10, 0x10, 0x90, 0x80, 0x10, ++0x10, 0x90, 0x38, 0xa4, 0x34, 0x98, 0x36, 0x99, 0xa4, 0x34, 0x38, 0x36, ++0x8d, 0x80, 0x90, 0x38, 0xa4, 0x31, 0x78, 0x36, 0x3b, 0xa4, 0x31, 0x48, ++0x36, 0x35, 0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, ++0x33, 0xb8, 0x36, 0x82, 0xa4, 0x33, 0x30, 0x36, 0x6c, 0x90, 0x38, 0xa4, ++0x32, 0xb0, 0x36, 0x60, 0xa4, 0x32, 0x38, 0x36, 0x4c, 0xe4, 0xa1, 0xf9, ++0x80, 0x37, 0xe9, 0x10, 0x10, 0xe4, 0xe1, 0xf8, 0x00, 0x37, 0xe3, 0xc0, ++0x40, 0x80, 0x10, 0x10, 0x81, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0x90, ++0x80, 0x85, 0x36, 0x46, 0xc9, 0xe1, 0x91, 0x00, 0x85, 0x36, 0x43, 0x80, ++0x36, 0x41, 0x80, 0xd8, 0x47, 0x80, 0x0d, 0xc0, 0xc0, 0x80, 0x10, 0x10, ++0x82, 0x90, 0x58, 0xd5, 0x81, 0x80, 0x80, 0x37, 0xdd, 0x80, 0x37, 0xdb, ++0xd5, 0x81, 0x80, 0x80, 0x37, 0xd9, 0x80, 0x37, 0xd7, 0xc0, 0x80, 0x10, ++0x10, 0x82, 0x90, 0x58, 0xd5, 0x81, 0x80, 0x80, 0x37, 0xde, 0x80, 0x37, ++0xdc, 0xd5, 0x81, 0x80, 0x80, 0x37, 0xda, 0x80, 0x37, 0xd8, 0xc0, 0x80, ++0x83, 0xa4, 0x3e, 0xa8, 0x37, 0xd6, 0xa0, 0x57, 0xc0, 0xa0, 0x41, 0xe0, ++0xa8, 0x1e, 0xb0, 0x34, 0x88, 0xa0, 0x12, 0x38, 0xa0, 0x0b, 0x48, 0x96, ++0x00, 0x9a, 0xf0, 0x05, 0xc0, 0x91, 0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, ++0x38, 0xa4, 0x15, 0x58, 0x33, 0xb5, 0xa4, 0x15, 0x78, 0x33, 0xb4, 0x10, ++0x10, 0xa4, 0x15, 0x68, 0x33, 0xb3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, ++0xf8, 0x33, 0x9a, 0xa4, 0x15, 0x18, 0x33, 0x99, 0x10, 0x10, 0xa4, 0x15, ++0x08, 0x33, 0x98, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x98, ++0x33, 0x7f, 0xa4, 0x14, 0xb8, 0x33, 0x7e, 0x10, 0x10, 0xa4, 0x14, 0xa8, ++0x33, 0x7d, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x38, 0x33, 0x63, 0xa4, ++0x14, 0x58, 0x33, 0x62, 0x10, 0x10, 0xa4, 0x14, 0x48, 0x33, 0x61, 0x91, ++0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x15, 0x28, 0x33, 0xb0, ++0xa4, 0x15, 0x48, 0x33, 0xb2, 0x10, 0x10, 0xa4, 0x15, 0x38, 0x33, 0xb1, ++0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0xc8, 0x33, 0x95, 0xa4, 0x14, 0xe8, ++0x33, 0x97, 0x10, 0x10, 0xa4, 0x14, 0xd8, 0x33, 0x96, 0x90, 0xb8, 0x90, ++0x70, 0x90, 0x38, 0xa4, 0x14, 0x68, 0x33, 0x7a, 0xa4, 0x14, 0x88, 0x33, ++0x7c, 0x10, 0x10, 0xa4, 0x14, 0x78, 0x33, 0x7b, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x14, 0x08, 0x33, 0x5e, 0xa4, 0x14, 0x28, 0x33, 0x60, 0x10, 0x10, ++0xa4, 0x14, 0x18, 0x33, 0x5f, 0xe4, 0xe1, 0x83, 0x40, 0x36, 0x21, 0x9a, ++0xf0, 0x05, 0x00, 0x91, 0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, ++0x13, 0xa0, 0x33, 0xad, 0xa4, 0x13, 0x98, 0x33, 0xaf, 0x10, 0x10, 0xa4, ++0x13, 0x90, 0x33, 0xae, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x88, 0x33, ++0x92, 0xa4, 0x13, 0x80, 0x33, 0x94, 0x10, 0x10, 0xa4, 0x13, 0x78, 0x33, ++0x93, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x70, 0x33, 0x77, ++0xa4, 0x13, 0x68, 0x33, 0x79, 0x10, 0x10, 0xa4, 0x13, 0x60, 0x33, 0x78, ++0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x58, 0x33, 0x5b, 0xa4, 0x13, 0x50, ++0x33, 0x5d, 0x10, 0x10, 0xa4, 0x13, 0x48, 0x33, 0x5c, 0x91, 0x10, 0x90, ++0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xaa, 0x80, 0x33, 0xac, 0x10, ++0x10, 0x80, 0x33, 0xab, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x8f, 0x80, ++0x33, 0x91, 0x10, 0x10, 0x80, 0x33, 0x90, 0x90, 0x88, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x33, 0x74, 0x80, 0x33, 0x76, 0x10, 0x10, 0x80, 0x33, 0x75, ++0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x58, 0x80, 0x33, 0x5a, 0x10, 0x10, ++0x80, 0x33, 0x59, 0xe4, 0xe1, 0x5e, 0x40, 0x35, 0xa1, 0x95, 0x40, 0x9a, ++0x90, 0x05, 0x00, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, ++0x33, 0xa7, 0x80, 0x33, 0xa9, 0x10, 0x10, 0x80, 0x33, 0xa8, 0x90, 0x50, ++0x90, 0x28, 0x80, 0x33, 0x8c, 0x80, 0x33, 0x8e, 0x10, 0x10, 0x80, 0x33, ++0x8d, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x30, 0x33, 0x71, ++0xa4, 0x13, 0x40, 0x33, 0x73, 0x10, 0x10, 0xa4, 0x13, 0x38, 0x33, 0x72, ++0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x00, 0x33, 0x55, 0xa4, 0x13, 0x10, ++0x33, 0x57, 0x10, 0x10, 0xa4, 0x13, 0x08, 0x33, 0x56, 0x91, 0x10, 0x90, ++0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xa4, 0x80, 0x33, 0xa6, 0x10, ++0x10, 0x80, 0x33, 0xa5, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x89, 0x80, ++0x33, 0x8b, 0x10, 0x10, 0x80, 0x33, 0x8a, 0x90, 0xb8, 0x90, 0x70, 0x90, ++0x38, 0xa4, 0x13, 0x18, 0x33, 0x6e, 0xa4, 0x13, 0x28, 0x33, 0x70, 0x10, ++0x10, 0xa4, 0x13, 0x20, 0x33, 0x6f, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x12, ++0xe8, 0x33, 0x52, 0xa4, 0x12, 0xf8, 0x33, 0x54, 0x10, 0x10, 0xa4, 0x12, ++0xf0, 0x33, 0x53, 0xe4, 0xe1, 0x82, 0x40, 0x36, 0x1d, 0x98, 0xb8, 0x01, ++0x68, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4f, ++0x80, 0x33, 0x51, 0x10, 0x10, 0x80, 0x33, 0x50, 0x90, 0x60, 0x90, 0x30, ++0x60, 0xa0, 0x97, 0x00, 0x60, 0xa0, 0x96, 0xc0, 0x90, 0x30, 0x60, 0xa0, ++0x96, 0x80, 0x60, 0xa0, 0x96, 0x40, 0xe4, 0xe1, 0x5c, 0x40, 0x35, 0x99, ++0xa0, 0x08, 0x08, 0x94, 0xe0, 0x9a, 0x60, 0x04, 0xa0, 0x91, 0x40, 0x90, ++0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0xd8, 0x33, 0x9e, 0xa4, 0x13, ++0xf8, 0x33, 0xa3, 0x10, 0x10, 0xa4, 0x13, 0xe8, 0x33, 0xa2, 0x90, 0x50, ++0x90, 0x28, 0x80, 0x33, 0x83, 0x80, 0x33, 0x88, 0x10, 0x10, 0x80, 0x33, ++0x87, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x68, 0x80, 0x33, ++0x6d, 0x10, 0x10, 0x80, 0x33, 0x6c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, ++0x49, 0x80, 0x33, 0x4e, 0x10, 0x10, 0x80, 0x33, 0x4d, 0x91, 0x40, 0x90, ++0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0xa8, 0x33, 0x9b, 0xa4, 0x13, ++0xc8, 0x33, 0x9d, 0x10, 0x10, 0xa4, 0x13, 0xb8, 0x33, 0x9c, 0x90, 0x50, ++0x90, 0x28, 0x80, 0x33, 0x80, 0x80, 0x33, 0x82, 0x10, 0x10, 0x80, 0x33, ++0x81, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x65, 0x80, 0x33, ++0x67, 0x10, 0x10, 0x80, 0x33, 0x66, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, ++0x46, 0x80, 0x33, 0x48, 0x10, 0x10, 0x80, 0x33, 0x47, 0xe4, 0xe1, 0x81, ++0x40, 0x36, 0x19, 0x9a, 0x60, 0x02, 0xe0, 0x91, 0x40, 0x90, 0xb8, 0x90, ++0x70, 0x90, 0x38, 0xa4, 0x1a, 0x20, 0x33, 0x9f, 0xa4, 0x1a, 0x10, 0x33, ++0xa1, 0x10, 0x10, 0xa4, 0x1a, 0x00, 0x33, 0xa0, 0x90, 0x50, 0x90, 0x28, ++0x80, 0x33, 0x84, 0x80, 0x33, 0x86, 0x10, 0x10, 0x80, 0x33, 0x85, 0x90, ++0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x69, 0x80, 0x33, 0x6b, 0x10, ++0x10, 0x80, 0x33, 0x6a, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4a, 0x80, ++0x33, 0x4c, 0x10, 0x10, 0x80, 0x33, 0x4b, 0x81, 0x90, 0x50, 0x90, 0x28, ++0x24, 0x19, 0xd0, 0x24, 0x19, 0xf0, 0x10, 0x10, 0x24, 0x19, 0xe0, 0xe4, ++0xe1, 0x5a, 0x40, 0x35, 0x91, 0x93, 0x90, 0x99, 0xb8, 0x03, 0x50, 0x90, ++0xe8, 0x90, 0x88, 0x90, 0x40, 0x80, 0xa4, 0x15, 0xb8, 0x32, 0xca, 0x10, ++0x10, 0xa4, 0x15, 0xa8, 0x32, 0xc9, 0x90, 0x28, 0x81, 0x32, 0xc6, 0x10, ++0x10, 0x80, 0x32, 0xc5, 0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xc2, 0x10, ++0x10, 0x80, 0x32, 0xc1, 0x90, 0x28, 0x81, 0x32, 0xbe, 0x10, 0x10, 0x80, ++0x32, 0xbd, 0x90, 0xe8, 0x90, 0x88, 0x90, 0x40, 0x80, 0xa4, 0x15, 0x88, ++0x32, 0xc7, 0x10, 0x10, 0xa4, 0x15, 0x98, 0x32, 0xc8, 0x90, 0x28, 0x81, ++0x32, 0xc3, 0x10, 0x10, 0x80, 0x32, 0xc4, 0x90, 0x60, 0x90, 0x28, 0x81, ++0x32, 0xbf, 0x10, 0x10, 0x80, 0x32, 0xc0, 0x90, 0x28, 0x81, 0x32, 0xbb, ++0x10, 0x10, 0x80, 0x32, 0xbc, 0xe4, 0xe1, 0x80, 0x40, 0x36, 0x15, 0x88, ++0x00, 0x88, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x81, 0x32, 0xb9, 0x10, ++0x10, 0x80, 0x32, 0xba, 0xe4, 0xe1, 0x58, 0x40, 0x35, 0x89, 0xa0, 0x0e, ++0x80, 0xa0, 0x09, 0x08, 0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, ++0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x39, 0x80, 0x33, 0x38, ++0x10, 0x10, 0x80, 0x33, 0x37, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x1e, ++0x80, 0x33, 0x1d, 0x10, 0x10, 0x80, 0x33, 0x1c, 0x90, 0x88, 0x90, 0x50, ++0x90, 0x28, 0x80, 0x33, 0x03, 0x80, 0x33, 0x02, 0x10, 0x10, 0x80, 0x33, ++0x01, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe8, 0x80, 0x32, 0xe7, 0x10, ++0x10, 0x80, 0x32, 0xe6, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, ++0x80, 0x33, 0x34, 0x80, 0x33, 0x36, 0x10, 0x10, 0x80, 0x33, 0x35, 0x90, ++0x50, 0x90, 0x28, 0x80, 0x33, 0x19, 0x80, 0x33, 0x1b, 0x10, 0x10, 0x80, ++0x33, 0x1a, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfe, 0x80, ++0x33, 0x00, 0x10, 0x10, 0x80, 0x32, 0xff, 0x90, 0x50, 0x90, 0x28, 0x80, ++0x32, 0xe3, 0x80, 0x32, 0xe5, 0x10, 0x10, 0x80, 0x32, 0xe4, 0xe4, 0xe1, ++0x72, 0x40, 0x35, 0xf1, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, ++0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x31, 0x80, 0x33, 0x33, 0x10, 0x10, ++0x80, 0x33, 0x32, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x16, 0x80, 0x33, ++0x18, 0x10, 0x10, 0x80, 0x33, 0x17, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, ++0x80, 0x32, 0xfb, 0x80, 0x32, 0xfd, 0x10, 0x10, 0x80, 0x32, 0xfc, 0x90, ++0x50, 0x90, 0x28, 0x80, 0x32, 0xe0, 0x80, 0x32, 0xe2, 0x10, 0x10, 0x80, ++0x32, 0xe1, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, ++0x2e, 0x80, 0x33, 0x30, 0x10, 0x10, 0x80, 0x33, 0x2f, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x33, 0x13, 0x80, 0x33, 0x15, 0x10, 0x10, 0x80, 0x33, 0x14, ++0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xf8, 0x80, 0x32, 0xfa, ++0x10, 0x10, 0x80, 0x32, 0xf9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xdd, ++0x80, 0x32, 0xdf, 0x10, 0x10, 0x80, 0x32, 0xde, 0xe4, 0xe1, 0x51, 0x40, ++0x35, 0x59, 0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, ++0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x2b, 0x80, 0x33, 0x2d, 0x10, 0x10, ++0x80, 0x33, 0x2c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x10, 0x80, 0x33, ++0x12, 0x10, 0x10, 0x80, 0x33, 0x11, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, ++0x80, 0x32, 0xf5, 0x80, 0x32, 0xf7, 0x10, 0x10, 0x80, 0x32, 0xf6, 0x90, ++0x50, 0x90, 0x28, 0x80, 0x32, 0xda, 0x80, 0x32, 0xdc, 0x10, 0x10, 0x80, ++0x32, 0xdb, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, ++0x28, 0x80, 0x33, 0x2a, 0x10, 0x10, 0x80, 0x33, 0x29, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x33, 0x0d, 0x80, 0x33, 0x0f, 0x10, 0x10, 0x80, 0x33, 0x0e, ++0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xf2, 0x80, 0x32, 0xf4, ++0x10, 0x10, 0x80, 0x32, 0xf3, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd7, ++0x80, 0x32, 0xd9, 0x10, 0x10, 0x80, 0x32, 0xd8, 0xe4, 0xe1, 0x70, 0x40, ++0x35, 0xe9, 0x88, 0x00, 0xb0, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x32, 0xd4, 0x80, 0x32, 0xd6, 0x10, 0x10, 0x80, 0x32, 0xd5, ++0xe4, 0xe1, 0x50, 0x40, 0x35, 0x55, 0x96, 0xe8, 0x94, 0x80, 0x9a, 0x30, ++0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, ++0x22, 0x80, 0x33, 0x27, 0x10, 0x10, 0x80, 0x33, 0x26, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x33, 0x07, 0x80, 0x33, 0x0c, 0x10, 0x10, 0x80, 0x33, 0x0b, ++0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xec, 0x80, 0x32, 0xf1, ++0x10, 0x10, 0x80, 0x32, 0xf0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xce, ++0x80, 0x32, 0xd3, 0x10, 0x10, 0x80, 0x32, 0xd2, 0x91, 0x10, 0x90, 0x88, ++0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x1f, 0x80, 0x33, 0x21, 0x10, 0x10, ++0x80, 0x33, 0x20, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x04, 0x80, 0x33, ++0x06, 0x10, 0x10, 0x80, 0x33, 0x05, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, ++0x80, 0x32, 0xe9, 0x80, 0x32, 0xeb, 0x10, 0x10, 0x80, 0x32, 0xea, 0x90, ++0x50, 0x90, 0x28, 0x80, 0x32, 0xcb, 0x80, 0x32, 0xcd, 0x10, 0x10, 0x80, ++0x32, 0xcc, 0xe4, 0xe1, 0x6e, 0x40, 0x35, 0xe1, 0x88, 0x02, 0x28, 0x91, ++0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x23, 0x80, 0x33, ++0x25, 0x10, 0x10, 0x80, 0x33, 0x24, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, ++0x08, 0x80, 0x33, 0x0a, 0x10, 0x10, 0x80, 0x33, 0x09, 0x90, 0x88, 0x90, ++0x50, 0x90, 0x28, 0x80, 0x32, 0xed, 0x80, 0x32, 0xef, 0x10, 0x10, 0x80, ++0x32, 0xee, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xcf, 0x80, 0x32, 0xd1, ++0x10, 0x10, 0x80, 0x32, 0xd0, 0xe4, 0xe1, 0x4f, 0x40, 0x35, 0x51, 0x90, ++0x40, 0xe5, 0x21, 0x6c, 0x40, 0x35, 0xd9, 0xe5, 0x21, 0x4e, 0x40, 0x35, ++0x4d, 0x9e, 0xb4, 0x22, 0xe8, 0x93, 0x70, 0x91, 0xd8, 0xd5, 0x07, 0x80, ++0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3e, 0x38, 0x84, 0x37, 0xd1, ++0xa4, 0x3c, 0x18, 0x37, 0x9b, 0x90, 0x28, 0x24, 0x3b, 0x58, 0xa4, 0x39, ++0xd8, 0x37, 0x53, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3e, 0x18, ++0x84, 0x37, 0xcf, 0xa4, 0x3c, 0x08, 0x37, 0x99, 0x90, 0x28, 0x24, 0x3b, ++0x48, 0xa4, 0x39, 0xc8, 0x37, 0x51, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, ++0x90, 0x28, 0x80, 0x37, 0xbb, 0xa4, 0x3b, 0xe8, 0x37, 0x95, 0x90, 0x28, ++0x24, 0x3b, 0x28, 0xa4, 0x39, 0xa8, 0x37, 0x4d, 0xd0, 0xc3, 0x40, 0x90, ++0x28, 0x80, 0x37, 0xb7, 0xa4, 0x3b, 0xd8, 0x37, 0x93, 0x90, 0x28, 0x24, ++0x3b, 0x18, 0xa4, 0x39, 0x98, 0x37, 0x4b, 0x91, 0x98, 0xd5, 0x06, 0x80, ++0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xaf, 0xa4, 0x3b, 0xb8, 0x37, ++0x8f, 0x90, 0x28, 0x24, 0x3a, 0xf8, 0xa4, 0x39, 0x78, 0x37, 0x47, 0xd0, ++0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xab, 0xa4, 0x3b, 0xa8, 0x37, 0x8d, ++0x90, 0x28, 0x24, 0x3a, 0xe8, 0xa4, 0x39, 0x68, 0x37, 0x45, 0xd5, 0x06, ++0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xa3, 0xa4, 0x3b, 0x88, ++0x37, 0x89, 0x90, 0x28, 0x24, 0x3a, 0xc8, 0xa4, 0x39, 0x48, 0x37, 0x41, ++0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0x9f, 0xa4, 0x3b, 0x78, 0x37, ++0x87, 0x90, 0x28, 0x24, 0x3a, 0xb8, 0xa4, 0x39, 0x38, 0x37, 0x3f, 0x93, ++0x70, 0x91, 0xd8, 0xd5, 0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, ++0x8c, 0x3e, 0x58, 0x84, 0x37, 0xd3, 0xa4, 0x3c, 0x28, 0x37, 0x9d, 0x90, ++0x28, 0x24, 0x3b, 0x68, 0xa4, 0x39, 0xe8, 0x37, 0x55, 0xd0, 0xc4, 0x40, ++0x90, 0x48, 0x80, 0x8c, 0x3e, 0x28, 0x84, 0x37, 0xd0, 0xa4, 0x3c, 0x10, ++0x37, 0x9a, 0x90, 0x28, 0x24, 0x3b, 0x50, 0xa4, 0x39, 0xd0, 0x37, 0x52, ++0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xbf, 0xa4, ++0x3b, 0xf8, 0x37, 0x97, 0x90, 0x28, 0x24, 0x3b, 0x38, 0xa4, 0x39, 0xb8, ++0x37, 0x4f, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xb9, 0xa4, 0x3b, ++0xe0, 0x37, 0x94, 0x90, 0x28, 0x24, 0x3b, 0x20, 0xa4, 0x39, 0xa0, 0x37, ++0x4c, 0x91, 0x98, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, ++0x37, 0xb3, 0xa4, 0x3b, 0xc8, 0x37, 0x91, 0x90, 0x28, 0x24, 0x3b, 0x08, ++0xa4, 0x39, 0x88, 0x37, 0x49, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, ++0xad, 0xa4, 0x3b, 0xb0, 0x37, 0x8e, 0x90, 0x28, 0x24, 0x3a, 0xf0, 0xa4, ++0x39, 0x70, 0x37, 0x46, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, ++0x80, 0x37, 0xa7, 0xa4, 0x3b, 0x98, 0x37, 0x8b, 0x90, 0x28, 0x24, 0x3a, ++0xd8, 0xa4, 0x39, 0x58, 0x37, 0x43, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, ++0x37, 0xa1, 0xa4, 0x3b, 0x80, 0x37, 0x88, 0x90, 0x28, 0x24, 0x3a, 0xc0, ++0xa4, 0x39, 0x40, 0x37, 0x40, 0x99, 0x08, 0x01, 0xf0, 0x81, 0x90, 0x78, ++0xd4, 0xc2, 0x00, 0xa4, 0x22, 0x80, 0x34, 0x40, 0xa4, 0x21, 0x80, 0x34, ++0x20, 0xd4, 0xc2, 0x00, 0xa4, 0x21, 0xa0, 0x34, 0x44, 0xa4, 0x20, 0xa0, ++0x34, 0x24, 0x81, 0x90, 0x78, 0xd4, 0xc2, 0x00, 0xa4, 0x21, 0xe0, 0x34, ++0x4c, 0xa4, 0x20, 0xe0, 0x34, 0x2c, 0xd4, 0xc2, 0x00, 0xa4, 0x21, 0xc0, ++0x34, 0x48, 0xa4, 0x20, 0xc0, 0x34, 0x28, 0xa8, 0x0b, 0x18, 0x13, 0xa8, ++0x96, 0x80, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x12, 0xb8, 0x32, 0x58, 0x24, 0x12, 0xb0, 0x90, 0x38, ++0xa4, 0x11, 0xe0, 0x32, 0x3d, 0x24, 0x11, 0xd8, 0x90, 0x60, 0x90, 0x38, ++0xa4, 0x11, 0x08, 0x32, 0x22, 0x24, 0x11, 0x00, 0x90, 0x38, 0xa4, 0x10, ++0x30, 0x32, 0x07, 0x24, 0x10, 0x28, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, ++0xa4, 0x12, 0xa8, 0x32, 0x53, 0x24, 0x12, 0xa0, 0x90, 0x38, 0xa4, 0x11, ++0xd0, 0x32, 0x38, 0x24, 0x11, 0xc8, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, ++0xf8, 0x32, 0x1d, 0x24, 0x10, 0xf0, 0x90, 0x38, 0xa4, 0x10, 0x20, 0x32, ++0x02, 0x24, 0x10, 0x18, 0xe4, 0xe1, 0xc8, 0x40, 0x37, 0x23, 0x99, 0x90, ++0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x90, 0x32, ++0x50, 0x24, 0x12, 0x88, 0x90, 0x38, 0xa4, 0x11, 0xb8, 0x32, 0x35, 0x24, ++0x11, 0xb0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xe0, 0x32, 0x1a, 0x24, ++0x10, 0xd8, 0x90, 0x38, 0xa4, 0x10, 0x08, 0x31, 0xff, 0x24, 0x10, 0x00, ++0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x78, 0x32, 0x4d, 0x24, ++0x12, 0x70, 0x90, 0x38, 0xa4, 0x11, 0xa0, 0x32, 0x32, 0x24, 0x11, 0x98, ++0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xc8, 0x32, 0x17, 0x24, 0x10, 0xc0, ++0x90, 0x38, 0xa4, 0x0f, 0xf0, 0x31, 0xfc, 0x24, 0x0f, 0xe8, 0xe4, 0xe1, ++0xc6, 0xc0, 0x37, 0x1d, 0x93, 0x78, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, ++0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x60, 0x32, 0x4a, 0x24, 0x12, 0x58, ++0x90, 0x38, 0xa4, 0x11, 0x88, 0x32, 0x2f, 0x24, 0x11, 0x80, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x10, 0xb0, 0x32, 0x14, 0x24, 0x10, 0xa8, 0x90, 0x38, ++0xa4, 0x0f, 0xd8, 0x31, 0xf9, 0x24, 0x0f, 0xd0, 0x90, 0xc0, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x12, 0x48, 0x32, 0x47, 0x24, 0x12, 0x40, 0x90, 0x38, ++0xa4, 0x11, 0x70, 0x32, 0x2c, 0x24, 0x11, 0x68, 0x90, 0x60, 0x90, 0x38, ++0xa4, 0x10, 0x98, 0x32, 0x11, 0x24, 0x10, 0x90, 0x90, 0x38, 0xa4, 0x0f, ++0xc0, 0x31, 0xf6, 0x24, 0x0f, 0xb8, 0xec, 0xa1, 0x16, 0x00, 0x02, 0x00, ++0x34, 0x5a, 0xa4, 0x38, 0xa8, 0x37, 0x17, 0x88, 0x00, 0x88, 0x10, 0x10, ++0x10, 0x10, 0x90, 0x38, 0xa4, 0x0f, 0xa8, 0x31, 0xf3, 0x24, 0x0f, 0xa0, ++0xe9, 0x61, 0x15, 0x40, 0x02, 0x00, 0x34, 0x56, 0xe3, 0x61, 0xc3, 0xc0, ++0x37, 0x11, 0x95, 0x08, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, ++0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x30, 0x32, 0x41, 0x24, 0x12, 0x28, ++0x90, 0x38, 0xa4, 0x11, 0x58, 0x32, 0x26, 0x24, 0x11, 0x50, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x10, 0x80, 0x32, 0x0b, 0x24, 0x10, 0x78, 0x90, 0x38, ++0xa4, 0x0f, 0x90, 0x31, 0xed, 0x24, 0x0f, 0x88, 0x90, 0xc0, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x12, 0x00, 0x32, 0x3e, 0x24, 0x11, 0xf8, 0x90, 0x38, ++0xa4, 0x11, 0x28, 0x32, 0x23, 0x24, 0x11, 0x20, 0x90, 0x60, 0x90, 0x38, ++0xa4, 0x10, 0x50, 0x32, 0x08, 0x24, 0x10, 0x48, 0x90, 0x38, 0xa4, 0x0f, ++0x60, 0x31, 0xea, 0x24, 0x0f, 0x58, 0xe4, 0xe1, 0xc8, 0x80, 0x37, 0x25, ++0x88, 0x01, 0x88, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x20, ++0x32, 0x42, 0x24, 0x12, 0x18, 0x90, 0x38, 0xa4, 0x11, 0x48, 0x32, 0x27, ++0x24, 0x11, 0x40, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0x70, 0x32, 0x0c, ++0x24, 0x10, 0x68, 0x90, 0x38, 0xa4, 0x0f, 0x80, 0x31, 0xee, 0x24, 0x0f, ++0x78, 0xe4, 0xe1, 0xc7, 0x00, 0x37, 0x1f, 0x92, 0xd0, 0x99, 0x50, 0x02, ++0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe9, 0x24, 0x0f, ++0x40, 0x90, 0x28, 0x80, 0x31, 0xe5, 0x24, 0x0f, 0x20, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x31, 0xe1, 0x24, 0x0f, 0x00, 0x90, 0x28, 0x80, 0x31, 0xdd, ++0x24, 0x0e, 0xe0, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe6, ++0x24, 0x0f, 0x38, 0x90, 0x28, 0x80, 0x31, 0xe2, 0x24, 0x0f, 0x18, 0x90, ++0x50, 0x90, 0x28, 0x80, 0x31, 0xde, 0x24, 0x0e, 0xf8, 0x90, 0x28, 0x80, ++0x31, 0xda, 0x24, 0x0e, 0xd8, 0xec, 0xe1, 0xc5, 0xa1, 0x17, 0x00, 0x37, ++0x19, 0x88, 0x00, 0x78, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x80, 0x31, ++0xd8, 0x24, 0x0e, 0xc8, 0xec, 0xe1, 0xc4, 0x21, 0x15, 0x00, 0x37, 0x13, ++0xe5, 0xa1, 0x4d, 0x40, 0x35, 0x31, 0xa0, 0x2a, 0x10, 0xa8, 0x16, 0x60, ++0x29, 0xd8, 0xa0, 0x0c, 0x48, 0xa0, 0x0a, 0xc8, 0x95, 0x60, 0x92, 0xb0, ++0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xa1, 0x80, ++0x31, 0xa0, 0x10, 0x10, 0x80, 0x31, 0x9f, 0x90, 0x70, 0x90, 0x38, 0xa4, ++0x08, 0x98, 0x31, 0xb3, 0xa4, 0x08, 0x90, 0x31, 0xb2, 0x10, 0x10, 0xa4, ++0x08, 0x88, 0x31, 0xb1, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, ++0xb8, 0x31, 0xd7, 0xa4, 0x09, 0xb0, 0x31, 0xd6, 0x10, 0x10, 0xa4, 0x09, ++0xa8, 0x31, 0xd5, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x28, 0x31, 0xc5, ++0xa4, 0x09, 0x20, 0x31, 0xc4, 0x10, 0x10, 0xa4, 0x09, 0x18, 0x31, 0xc3, ++0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x9c, 0x80, ++0x31, 0x9e, 0x10, 0x10, 0x80, 0x31, 0x9d, 0x90, 0x70, 0x90, 0x38, 0xa4, ++0x08, 0x70, 0x31, 0xae, 0xa4, 0x08, 0x80, 0x31, 0xb0, 0x10, 0x10, 0xa4, ++0x08, 0x78, 0x31, 0xaf, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, ++0x90, 0x31, 0xd2, 0xa4, 0x09, 0xa0, 0x31, 0xd4, 0x10, 0x10, 0xa4, 0x09, ++0x98, 0x31, 0xd3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x00, 0x31, 0xc0, ++0xa4, 0x09, 0x10, 0x31, 0xc2, 0x10, 0x10, 0xa4, 0x09, 0x08, 0x31, 0xc1, ++0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, ++0x99, 0x80, 0x31, 0x9b, 0x10, 0x10, 0x80, 0x31, 0x9a, 0x90, 0x70, 0x90, ++0x38, 0xa4, 0x08, 0x58, 0x31, 0xab, 0xa4, 0x08, 0x68, 0x31, 0xad, 0x10, ++0x10, 0xa4, 0x08, 0x60, 0x31, 0xac, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x09, 0x78, 0x31, 0xcf, 0xa4, 0x09, 0x88, 0x31, 0xd1, 0x10, 0x10, ++0xa4, 0x09, 0x80, 0x31, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xe8, ++0x31, 0xbd, 0xa4, 0x08, 0xf8, 0x31, 0xbf, 0x10, 0x10, 0xa4, 0x08, 0xf0, ++0x31, 0xbe, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, ++0x96, 0x80, 0x31, 0x98, 0x10, 0x10, 0x80, 0x31, 0x97, 0x90, 0x70, 0x90, ++0x38, 0xa4, 0x08, 0x40, 0x31, 0xa8, 0xa4, 0x08, 0x50, 0x31, 0xaa, 0x10, ++0x10, 0xa4, 0x08, 0x48, 0x31, 0xa9, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x09, 0x60, 0x31, 0xcc, 0xa4, 0x09, 0x70, 0x31, 0xce, 0x10, 0x10, ++0xa4, 0x09, 0x68, 0x31, 0xcd, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xd0, ++0x31, 0xba, 0xa4, 0x08, 0xe0, 0x31, 0xbc, 0x10, 0x10, 0xa4, 0x08, 0xd8, ++0x31, 0xbb, 0x10, 0x10, 0x90, 0xa8, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, ++0x90, 0x28, 0x80, 0x31, 0x8d, 0x80, 0x31, 0x8f, 0x10, 0x10, 0x80, 0x31, ++0x8e, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0xc0, 0x60, 0xa0, 0x2a, ++0x80, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0x40, 0x60, 0xa0, 0x2a, 0x00, 0x97, ++0xf0, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x31, 0x93, 0x80, 0x31, 0x95, 0x10, 0x10, 0x80, 0x31, 0x94, ++0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x28, 0x31, 0xa5, 0xa4, 0x08, 0x38, ++0x31, 0xa7, 0x10, 0x10, 0xa4, 0x08, 0x30, 0x31, 0xa6, 0x90, 0xb8, 0x90, ++0x70, 0x90, 0x38, 0xa4, 0x09, 0x48, 0x31, 0xc9, 0xa4, 0x09, 0x58, 0x31, ++0xcb, 0x10, 0x10, 0xa4, 0x09, 0x50, 0x31, 0xca, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x08, 0xb8, 0x31, 0xb7, 0xa4, 0x08, 0xc8, 0x31, 0xb9, 0x10, 0x10, ++0xa4, 0x08, 0xc0, 0x31, 0xb8, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x31, 0x90, 0x80, 0x31, 0x92, 0x10, 0x10, 0x80, 0x31, 0x91, ++0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x10, 0x31, 0xa2, 0xa4, 0x08, 0x20, ++0x31, 0xa4, 0x10, 0x10, 0xa4, 0x08, 0x18, 0x31, 0xa3, 0x90, 0xb8, 0x90, ++0x70, 0x90, 0x38, 0xa4, 0x09, 0x30, 0x31, 0xc6, 0xa4, 0x09, 0x40, 0x31, ++0xc8, 0x10, 0x10, 0xa4, 0x09, 0x38, 0x31, 0xc7, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x08, 0xa0, 0x31, 0xb4, 0xa4, 0x08, 0xb0, 0x31, 0xb6, 0x10, 0x10, ++0xa4, 0x08, 0xa8, 0x31, 0xb5, 0x10, 0x10, 0x91, 0x40, 0x90, 0xa0, 0x90, ++0x50, 0x90, 0x28, 0x80, 0x30, 0xcb, 0x80, 0x30, 0xca, 0x90, 0x28, 0x80, ++0x30, 0xc9, 0x80, 0x30, 0xc8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xc4, ++0x80, 0x30, 0xc7, 0x90, 0x28, 0x80, 0x30, 0xc6, 0x80, 0x30, 0xc5, 0x90, ++0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbc, 0x80, 0x30, 0xc3, 0x90, ++0x28, 0x80, 0x30, 0xc2, 0x80, 0x30, 0xc1, 0x90, 0x50, 0x90, 0x28, 0x80, ++0x30, 0xbd, 0x80, 0x30, 0xc0, 0x90, 0x28, 0x80, 0x30, 0xbf, 0x80, 0x30, ++0xbe, 0x91, 0x88, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, ++0x3b, 0x10, 0x10, 0x80, 0x31, 0x3a, 0x90, 0x28, 0x81, 0x31, 0x3d, 0x10, ++0x10, 0x80, 0x31, 0x3c, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, 0x41, 0x10, ++0x10, 0x80, 0x31, 0x40, 0x90, 0x28, 0x81, 0x31, 0x3f, 0x10, 0x10, 0x80, ++0x31, 0x3e, 0x80, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x81, 0x31, 0x38, ++0x10, 0x10, 0x80, 0x31, 0x39, 0xa0, 0x0b, 0x90, 0xa0, 0x0a, 0xc8, 0x95, ++0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, ++0x31, 0x56, 0x80, 0x31, 0x55, 0x10, 0x10, 0x80, 0x31, 0x54, 0x90, 0x70, ++0x90, 0x38, 0xa4, 0x06, 0xe8, 0x31, 0x68, 0xa4, 0x06, 0xe0, 0x31, 0x67, ++0x10, 0x10, 0xa4, 0x06, 0xd8, 0x31, 0x66, 0x90, 0xb8, 0x90, 0x70, 0x90, ++0x38, 0xa4, 0x08, 0x08, 0x31, 0x8c, 0xa4, 0x08, 0x00, 0x31, 0x8b, 0x10, ++0x10, 0xa4, 0x07, 0xf8, 0x31, 0x8a, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, ++0x78, 0x31, 0x7a, 0xa4, 0x07, 0x70, 0x31, 0x79, 0x10, 0x10, 0xa4, 0x07, ++0x68, 0x31, 0x78, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, ++0x31, 0x51, 0x80, 0x31, 0x53, 0x10, 0x10, 0x80, 0x31, 0x52, 0x90, 0x70, ++0x90, 0x38, 0xa4, 0x06, 0xc0, 0x31, 0x63, 0xa4, 0x06, 0xd0, 0x31, 0x65, ++0x10, 0x10, 0xa4, 0x06, 0xc8, 0x31, 0x64, 0x90, 0xb8, 0x90, 0x70, 0x90, ++0x38, 0xa4, 0x07, 0xe0, 0x31, 0x87, 0xa4, 0x07, 0xf0, 0x31, 0x89, 0x10, ++0x10, 0xa4, 0x07, 0xe8, 0x31, 0x88, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, ++0x50, 0x31, 0x75, 0xa4, 0x07, 0x60, 0x31, 0x77, 0x10, 0x10, 0xa4, 0x07, ++0x58, 0x31, 0x76, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x31, 0x4e, 0x80, 0x31, 0x50, 0x10, 0x10, 0x80, 0x31, 0x4f, ++0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0xa8, 0x31, 0x60, 0xa4, 0x06, 0xb8, ++0x31, 0x62, 0x10, 0x10, 0xa4, 0x06, 0xb0, 0x31, 0x61, 0x90, 0xb8, 0x90, ++0x70, 0x90, 0x38, 0xa4, 0x07, 0xc8, 0x31, 0x84, 0xa4, 0x07, 0xd8, 0x31, ++0x86, 0x10, 0x10, 0xa4, 0x07, 0xd0, 0x31, 0x85, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x07, 0x38, 0x31, 0x72, 0xa4, 0x07, 0x48, 0x31, 0x74, 0x10, 0x10, ++0xa4, 0x07, 0x40, 0x31, 0x73, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, ++0x28, 0x80, 0x31, 0x4b, 0x80, 0x31, 0x4d, 0x10, 0x10, 0x80, 0x31, 0x4c, ++0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x90, 0x31, 0x5d, 0xa4, 0x06, 0xa0, ++0x31, 0x5f, 0x10, 0x10, 0xa4, 0x06, 0x98, 0x31, 0x5e, 0x90, 0xb8, 0x90, ++0x70, 0x90, 0x38, 0xa4, 0x07, 0xb0, 0x31, 0x81, 0xa4, 0x07, 0xc0, 0x31, ++0x83, 0x10, 0x10, 0xa4, 0x07, 0xb8, 0x31, 0x82, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x07, 0x20, 0x31, 0x6f, 0xa4, 0x07, 0x30, 0x31, 0x71, 0x10, 0x10, ++0xa4, 0x07, 0x28, 0x31, 0x70, 0x10, 0x10, 0x80, 0x10, 0x10, 0x10, 0x10, ++0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x42, 0x80, 0x31, 0x44, 0x10, 0x10, ++0x80, 0x31, 0x43, 0x80, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, ++0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x48, 0x80, 0x31, 0x4a, 0x10, 0x10, ++0x80, 0x31, 0x49, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x78, 0x31, 0x5a, ++0xa4, 0x06, 0x88, 0x31, 0x5c, 0x10, 0x10, 0xa4, 0x06, 0x80, 0x31, 0x5b, ++0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x98, 0x31, 0x7e, 0xa4, ++0x07, 0xa8, 0x31, 0x80, 0x10, 0x10, 0xa4, 0x07, 0xa0, 0x31, 0x7f, 0x90, ++0x70, 0x90, 0x38, 0xa4, 0x07, 0x08, 0x31, 0x6c, 0xa4, 0x07, 0x18, 0x31, ++0x6e, 0x10, 0x10, 0xa4, 0x07, 0x10, 0x31, 0x6d, 0x91, 0x40, 0x90, 0x88, ++0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x45, 0x80, 0x31, 0x47, 0x10, 0x10, ++0x80, 0x31, 0x46, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x60, 0x31, 0x57, ++0xa4, 0x06, 0x70, 0x31, 0x59, 0x10, 0x10, 0xa4, 0x06, 0x68, 0x31, 0x58, ++0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x80, 0x31, 0x7b, 0xa4, ++0x07, 0x90, 0x31, 0x7d, 0x10, 0x10, 0xa4, 0x07, 0x88, 0x31, 0x7c, 0x90, ++0x70, 0x90, 0x38, 0xa4, 0x06, 0xf0, 0x31, 0x69, 0xa4, 0x07, 0x00, 0x31, ++0x6b, 0x10, 0x10, 0xa4, 0x06, 0xf8, 0x31, 0x6a, 0x10, 0x10, 0x91, 0x40, ++0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbb, 0x80, 0x30, 0xba, ++0x90, 0x28, 0x80, 0x30, 0xb9, 0x80, 0x30, 0xb8, 0x90, 0x50, 0x90, 0x28, ++0x80, 0x30, 0xb4, 0x80, 0x30, 0xb7, 0x90, 0x28, 0x80, 0x30, 0xb6, 0x80, ++0x30, 0xb5, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xac, 0x80, ++0x30, 0xb3, 0x90, 0x28, 0x80, 0x30, 0xb2, 0x80, 0x30, 0xb1, 0x90, 0x50, ++0x90, 0x28, 0x80, 0x30, 0xad, 0x80, 0x30, 0xb0, 0x90, 0x28, 0x80, 0x30, ++0xaf, 0x80, 0x30, 0xae, 0xc3, 0xc0, 0x30, 0x42, 0x9c, 0xe8, 0x07, 0x60, ++0x91, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x80, 0x88, 0x00, 0x80, 0x90, 0x50, ++0x90, 0x28, 0x80, 0x33, 0xf8, 0x80, 0x33, 0xf9, 0x81, 0x33, 0xef, 0xd0, ++0x41, 0x80, 0x24, 0x20, 0x90, 0x24, 0x20, 0x98, 0x10, 0x10, 0x80, 0x90, ++0x58, 0x80, 0x90, 0x28, 0x24, 0x1f, 0x90, 0x24, 0x1f, 0x98, 0x81, 0x24, ++0x1f, 0x50, 0x92, 0x68, 0x91, 0x00, 0x80, 0x90, 0x90, 0x90, 0x30, 0x80, ++0x24, 0x20, 0x00, 0x90, 0x38, 0xa4, 0x1f, 0xf8, 0x34, 0x06, 0x80, 0x34, ++0x05, 0x80, 0x90, 0x28, 0x80, 0x34, 0x0f, 0xa4, 0x1f, 0xe0, 0x34, 0x0e, ++0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x80, 0x34, 0x09, 0xa4, 0x1f, ++0xf0, 0x34, 0x08, 0x90, 0x28, 0x80, 0x34, 0x04, 0xa4, 0x1f, 0xe8, 0x34, ++0x03, 0x90, 0x50, 0x90, 0x28, 0x80, 0x34, 0x0d, 0x80, 0x34, 0x0c, 0x90, ++0x28, 0x24, 0x20, 0x88, 0x24, 0x20, 0x80, 0x90, 0x58, 0x80, 0x10, 0x10, ++0x80, 0x10, 0x10, 0x80, 0x33, 0xfb, 0x80, 0x90, 0x40, 0x10, 0x10, 0x80, ++0x24, 0x1f, 0x60, 0x80, 0x10, 0x10, 0x80, 0x33, 0xfa, 0x91, 0x58, 0x91, ++0x00, 0x90, 0x80, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xf6, 0x80, ++0x33, 0xf7, 0x81, 0x33, 0xee, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, ++0xf4, 0x80, 0x33, 0xf5, 0x81, 0x33, 0xed, 0x83, 0x90, 0x28, 0x24, 0x1f, ++0x80, 0x24, 0x1f, 0x88, 0x90, 0xe8, 0x81, 0x90, 0x88, 0x90, 0x38, 0x10, ++0x10, 0x80, 0x34, 0x07, 0x90, 0x28, 0x80, 0x34, 0x02, 0x80, 0x34, 0x01, ++0x80, 0x90, 0x28, 0x80, 0x34, 0x0b, 0x80, 0x34, 0x0a, 0x82, 0x10, 0x10, ++0x80, 0x24, 0x1f, 0x58, 0x97, 0x10, 0x9e, 0x10, 0x06, 0x98, 0x93, 0x00, ++0x91, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x80, 0x30, ++0x71, 0x24, 0x03, 0x78, 0x90, 0x38, 0xa4, 0x04, 0x10, 0x30, 0x83, 0x24, ++0x04, 0x08, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x30, 0x30, 0xa7, 0x24, ++0x05, 0x28, 0x90, 0x38, 0xa4, 0x04, 0xa0, 0x30, 0x95, 0x24, 0x04, 0x98, ++0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x70, 0x30, 0x6c, 0x24, ++0x03, 0x68, 0x90, 0x38, 0xa4, 0x04, 0x00, 0x30, 0x7e, 0x24, 0x03, 0xf8, ++0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x20, 0x30, 0xa2, 0x24, 0x05, 0x18, ++0x90, 0x38, 0xa4, 0x04, 0x90, 0x30, 0x90, 0x24, 0x04, 0x88, 0x91, 0x80, ++0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x58, 0x30, 0x69, 0x24, ++0x03, 0x50, 0x90, 0x38, 0xa4, 0x03, 0xe8, 0x30, 0x7b, 0x24, 0x03, 0xe0, ++0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x08, 0x30, 0x9f, 0x24, 0x05, 0x00, ++0x90, 0x38, 0xa4, 0x04, 0x78, 0x30, 0x8d, 0x24, 0x04, 0x70, 0x90, 0xc0, ++0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x40, 0x30, 0x66, 0x24, 0x03, 0x38, ++0x90, 0x38, 0xa4, 0x03, 0xd0, 0x30, 0x78, 0x24, 0x03, 0xc8, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x04, 0xf0, 0x30, 0x9c, 0x24, 0x04, 0xe8, 0x90, 0x38, ++0xa4, 0x04, 0x60, 0x30, 0x8a, 0x24, 0x04, 0x58, 0x10, 0x10, 0x80, 0x10, ++0x10, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x02, 0xf8, 0x30, 0x5d, 0x24, 0x02, ++0xf0, 0xd7, 0x42, 0x00, 0xa4, 0x38, 0x58, 0x37, 0x0d, 0xa4, 0x38, 0x38, ++0x37, 0x09, 0x9c, 0xe0, 0x06, 0x90, 0x93, 0x00, 0x91, 0x80, 0x90, 0xc0, ++0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x28, 0x30, 0x63, 0x24, 0x03, 0x20, ++0x90, 0x38, 0xa4, 0x03, 0xb8, 0x30, 0x75, 0x24, 0x03, 0xb0, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x04, 0xd8, 0x30, 0x99, 0x24, 0x04, 0xd0, 0x90, 0x38, ++0xa4, 0x04, 0x48, 0x30, 0x87, 0x24, 0x04, 0x40, 0x90, 0xc0, 0x90, 0x60, ++0x90, 0x38, 0xa4, 0x03, 0x10, 0x30, 0x60, 0x24, 0x03, 0x08, 0x90, 0x38, ++0xa4, 0x03, 0xa0, 0x30, 0x72, 0x24, 0x03, 0x98, 0x90, 0x60, 0x90, 0x38, ++0xa4, 0x04, 0xc0, 0x30, 0x96, 0x24, 0x04, 0xb8, 0x90, 0x38, 0xa4, 0x04, ++0x30, 0x30, 0x84, 0x24, 0x04, 0x28, 0x10, 0x10, 0x90, 0xe0, 0x90, 0x70, ++0x90, 0x38, 0xa4, 0x02, 0x88, 0x30, 0x52, 0xa4, 0x02, 0x78, 0x30, 0x50, ++0x90, 0x38, 0xa4, 0x02, 0x70, 0x30, 0x4b, 0xa4, 0x02, 0x60, 0x30, 0x4d, ++0x90, 0x70, 0x90, 0x38, 0xa4, 0x02, 0x50, 0x30, 0x43, 0xa4, 0x02, 0x40, ++0x30, 0x49, 0x90, 0x38, 0xa4, 0x02, 0x38, 0x30, 0x44, 0xa4, 0x02, 0x28, ++0x30, 0x46, 0x91, 0x48, 0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, ++0x30, 0x56, 0x24, 0x02, 0xa8, 0x90, 0x28, 0x80, 0x30, 0x58, 0x24, 0x02, ++0xb8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0x5c, 0x24, 0x02, 0xd8, 0x90, ++0x28, 0x80, 0x30, 0x5a, 0x24, 0x02, 0xc8, 0x80, 0x10, 0x10, 0x10, 0x10, ++0x90, 0x28, 0x80, 0x30, 0x53, 0x24, 0x02, 0xa0, 0xd7, 0x42, 0x00, 0xa4, ++0x38, 0x60, 0x37, 0x0e, 0xa4, 0x38, 0x40, 0x37, 0x0a, 0xa0, 0x14, 0x68, ++0xa0, 0x10, 0x90, 0xa0, 0x0c, 0x60, 0x9e, 0x88, 0x09, 0xd0, 0x94, 0xf0, ++0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x44, 0x40, ++0x85, 0x35, 0x2d, 0xcb, 0x61, 0x3d, 0x00, 0x85, 0x35, 0x03, 0x9a, 0x00, ++0x03, 0xf8, 0x91, 0x98, 0x80, 0x91, 0x10, 0x90, 0xa0, 0x90, 0x68, 0x90, ++0x20, 0x3a, 0x53, 0xc9, 0xe2, 0x94, 0x40, 0x85, 0x35, 0x2b, 0xa4, 0x52, ++0x78, 0x3a, 0x50, 0x90, 0x38, 0xa4, 0x52, 0x40, 0x3a, 0x49, 0xa4, 0x52, ++0x30, 0x3a, 0x47, 0x90, 0x48, 0x10, 0x10, 0xa4, 0x51, 0xf8, 0x3a, 0x40, ++0x10, 0x10, 0x80, 0x3a, 0x3c, 0x81, 0x10, 0x10, 0x80, 0xa4, 0x51, 0xc8, ++0x3a, 0x3a, 0x91, 0xb0, 0x91, 0x60, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, ++0xa4, 0x52, 0x68, 0x3a, 0x4e, 0xa4, 0x52, 0x58, 0x3a, 0x4c, 0x90, 0x38, ++0xa4, 0x52, 0x20, 0x3a, 0x45, 0xa4, 0x52, 0x10, 0x3a, 0x43, 0x90, 0x48, ++0x10, 0x10, 0xa4, 0x51, 0xe8, 0x3a, 0x3e, 0x10, 0x10, 0x80, 0x3a, 0x3b, ++0x90, 0x28, 0x80, 0x3a, 0x34, 0x80, 0x3a, 0x33, 0x81, 0x10, 0x10, 0x80, ++0xa4, 0x51, 0xb8, 0x3a, 0x38, 0xcb, 0x61, 0x3c, 0xc0, 0x85, 0x35, 0x02, ++0x90, 0xd8, 0x88, 0x00, 0x90, 0x84, 0x90, 0x38, 0xc1, 0xc0, 0x85, 0x3a, ++0x56, 0xc9, 0xe1, 0x44, 0x00, 0x85, 0x35, 0x29, 0xcb, 0x61, 0x3c, 0x80, ++0x85, 0x35, 0x01, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x43, ++0xc0, 0x85, 0x35, 0x27, 0xcb, 0x61, 0x3c, 0x40, 0x85, 0x35, 0x00, 0x91, ++0xf8, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x43, ++0x40, 0x85, 0x35, 0x23, 0xcb, 0x61, 0x3b, 0xc0, 0x85, 0x34, 0xfe, 0x88, ++0x01, 0x00, 0x90, 0xa0, 0x81, 0x90, 0x70, 0x80, 0x90, 0x20, 0x3a, 0x4a, ++0xc9, 0xe1, 0x43, 0x00, 0x85, 0x35, 0x21, 0x81, 0x3a, 0x41, 0x81, 0x10, ++0x10, 0x80, 0xa4, 0x51, 0xa8, 0x3a, 0x36, 0xcb, 0x61, 0x3b, 0x80, 0x85, ++0x34, 0xfd, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, ++0x42, 0xc0, 0x85, 0x35, 0x1f, 0xcb, 0x61, 0x3b, 0x40, 0x85, 0x34, 0xfc, ++0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x42, 0x80, 0x85, 0x35, ++0x1d, 0xcb, 0x61, 0x3b, 0x00, 0x85, 0x34, 0xfb, 0x92, 0x38, 0x81, 0x91, ++0x68, 0x91, 0x18, 0x90, 0x80, 0x90, 0x40, 0x80, 0xa4, 0x53, 0x28, 0x3a, ++0x66, 0x80, 0xa4, 0x53, 0x20, 0x3a, 0x63, 0x90, 0x28, 0x81, 0x3a, 0x62, ++0x90, 0x38, 0xa4, 0x53, 0x00, 0x3a, 0x61, 0xa4, 0x52, 0xf0, 0x3a, 0x5f, ++0x90, 0x28, 0x80, 0x3a, 0x5d, 0x80, 0x3a, 0x5c, 0x80, 0x90, 0x40, 0x10, ++0x10, 0x80, 0x24, 0x52, 0xd8, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x52, 0xc8, ++0x3a, 0x5a, 0xa4, 0x52, 0xb8, 0x3a, 0x58, 0x90, 0x28, 0x80, 0x3a, 0x55, ++0x80, 0x3a, 0x54, 0x9a, 0xd0, 0x03, 0xe0, 0x91, 0x60, 0x90, 0xb0, 0x88, ++0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x42, 0x00, 0x85, 0x35, 0x19, ++0xcb, 0x61, 0x3a, 0x80, 0x85, 0x34, 0xf9, 0x88, 0x00, 0x68, 0x84, 0x10, ++0x10, 0xc9, 0xe1, 0x41, 0xc0, 0x85, 0x35, 0x17, 0xcb, 0x61, 0x3a, 0x40, ++0x85, 0x34, 0xf8, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, ++0xe1, 0x41, 0x80, 0x85, 0x35, 0x15, 0xcb, 0x61, 0x3a, 0x00, 0x85, 0x34, ++0xf7, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x41, 0x40, 0x85, ++0x35, 0x13, 0xcb, 0x61, 0x39, 0xc0, 0x85, 0x34, 0xf6, 0x90, 0x90, 0x90, ++0x48, 0xcb, 0xa1, 0x38, 0x00, 0x85, 0x34, 0xe5, 0xcb, 0xa1, 0x37, 0xc0, ++0x85, 0x34, 0xe4, 0x90, 0x48, 0xcb, 0xa1, 0x37, 0x80, 0x85, 0x34, 0xe3, ++0xcb, 0xa1, 0x37, 0x40, 0x85, 0x34, 0xe2, 0xcb, 0xa2, 0x8c, 0x40, 0x80, ++0x3a, 0x32, 0x92, 0x40, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x26, ++0x60, 0x84, 0x24, 0x26, 0xd8, 0x8c, 0x26, 0x58, 0x84, 0x24, 0x26, 0xd0, ++0x90, 0x48, 0x8c, 0x26, 0x50, 0x84, 0x24, 0x26, 0xc8, 0x8c, 0x26, 0x48, ++0x84, 0x24, 0x26, 0xc0, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x26, 0x38, 0x84, ++0x24, 0x26, 0xb0, 0x8c, 0x26, 0x30, 0x84, 0x24, 0x26, 0xa8, 0x90, 0x48, ++0x8c, 0x26, 0x28, 0x84, 0x24, 0x26, 0xa0, 0x8c, 0x26, 0x20, 0x84, 0x24, ++0x26, 0x98, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x26, 0x10, 0x84, ++0x24, 0x26, 0x88, 0x8c, 0x26, 0x08, 0x84, 0x24, 0x26, 0x80, 0x90, 0x48, ++0x8c, 0x26, 0x00, 0x84, 0x24, 0x26, 0x78, 0x8c, 0x25, 0xf8, 0x84, 0x24, ++0x26, 0x70, 0x90, 0x38, 0xa4, 0x25, 0xe0, 0x34, 0xbd, 0xa4, 0x25, 0xd0, ++0x34, 0xbb, 0xa0, 0x0f, 0x50, 0xa0, 0x09, 0x08, 0x9a, 0x30, 0x04, 0x40, ++0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x8a, 0x40, ++0x3a, 0x21, 0xe5, 0x22, 0x82, 0x40, 0x3a, 0x1d, 0xcb, 0x61, 0x2a, 0x40, ++0x85, 0x34, 0xb8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x7a, 0x40, 0x39, ++0xe1, 0xe5, 0x22, 0x72, 0x40, 0x39, 0xdd, 0xcb, 0x61, 0x2a, 0x00, 0x85, ++0x34, 0xb7, 0x90, 0x48, 0xcb, 0xa1, 0x29, 0xc0, 0x85, 0x34, 0xb6, 0xcb, ++0xa1, 0x29, 0x80, 0x85, 0x34, 0xb5, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, ++0x00, 0x80, 0xe5, 0x22, 0x64, 0x40, 0x39, 0xa9, 0xe5, 0x22, 0x58, 0x40, ++0x39, 0x79, 0xcb, 0x61, 0x29, 0x00, 0x85, 0x34, 0xb3, 0x98, 0x50, 0x00, ++0x80, 0xe5, 0x22, 0x4c, 0x40, 0x39, 0x49, 0xe5, 0x22, 0x40, 0x40, 0x39, ++0x19, 0xcb, 0x61, 0x28, 0xc0, 0x85, 0x34, 0xb2, 0x90, 0x48, 0xcb, 0xa1, ++0x28, 0x80, 0x85, 0x34, 0xb1, 0xcb, 0xa1, 0x28, 0x40, 0x85, 0x34, 0xb0, ++0x92, 0x20, 0x91, 0x30, 0x90, 0xb8, 0xd5, 0x03, 0x00, 0xc0, 0xc0, 0x81, ++0x8c, 0x01, 0xa0, 0x84, 0x30, 0x3e, 0xc0, 0xc0, 0x81, 0x8c, 0x01, 0x80, ++0x84, 0x30, 0x3c, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, 0x30, 0x28, 0xc0, ++0xc0, 0x81, 0x30, 0x24, 0x90, 0x78, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, ++0x30, 0x1c, 0xc0, 0xc0, 0x81, 0x30, 0x18, 0xd5, 0x02, 0x00, 0xc0, 0xc0, ++0x81, 0x30, 0x10, 0xc0, 0xc0, 0x81, 0x30, 0x0c, 0x91, 0x70, 0x90, 0xd8, ++0xd5, 0x03, 0x80, 0xc8, 0xe2, 0x38, 0x40, 0x81, 0x8c, 0x01, 0xc0, 0x84, ++0x30, 0x40, 0xc8, 0xe2, 0x3a, 0x40, 0x81, 0x8c, 0x01, 0x90, 0x84, 0x30, ++0x3d, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x37, 0x40, 0x81, 0x30, 0x2c, 0xc8, ++0xe2, 0x31, 0xc0, 0x81, 0x30, 0x26, 0x90, 0x98, 0xd5, 0x02, 0x80, 0xc8, ++0xe2, 0x26, 0xc0, 0x81, 0x30, 0x20, 0xc8, 0xe2, 0x28, 0xc0, 0x81, 0x30, ++0x1a, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x25, 0xc0, 0x81, 0x30, 0x14, 0xc8, ++0xe2, 0x20, 0x40, 0x81, 0x30, 0x0e, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x90, ++0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x7e, 0x40, 0x39, 0xf1, ++0xe5, 0x22, 0x80, 0x40, 0x3a, 0x15, 0xcb, 0x61, 0x27, 0xc0, 0x85, 0x34, ++0xae, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x6e, 0x40, 0x39, 0xb1, 0xe5, ++0x22, 0x70, 0x40, 0x39, 0xd5, 0xcb, 0x61, 0x27, 0x80, 0x85, 0x34, 0xad, ++0x90, 0x48, 0xcb, 0xa1, 0x27, 0x40, 0x85, 0x34, 0xac, 0xcb, 0xa1, 0x27, ++0x00, 0x85, 0x34, 0xab, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, ++0xe5, 0x22, 0x60, 0x40, 0x39, 0x99, 0xe5, 0x22, 0x54, 0x40, 0x39, 0x69, ++0xcb, 0x61, 0x25, 0x40, 0x85, 0x34, 0x9a, 0x98, 0x50, 0x00, 0x80, 0xe5, ++0x22, 0x48, 0x40, 0x39, 0x39, 0xe5, 0x22, 0x3c, 0x40, 0x39, 0x09, 0xcb, ++0x61, 0x25, 0x00, 0x85, 0x34, 0x99, 0x90, 0x48, 0xcb, 0xa1, 0x24, 0xc0, ++0x85, 0x34, 0x98, 0xcb, 0xa1, 0x24, 0x80, 0x85, 0x34, 0x97, 0x91, 0x00, ++0x90, 0x80, 0x90, 0x40, 0xe5, 0x20, 0x02, 0x40, 0x30, 0x0a, 0xe5, 0x20, ++0x01, 0x80, 0x30, 0x07, 0x90, 0x40, 0xe5, 0x20, 0x00, 0xc0, 0x30, 0x04, ++0xe5, 0x20, 0x00, 0x00, 0x30, 0x01, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, ++0x2d, 0x40, 0x38, 0xab, 0xe5, 0x22, 0x2f, 0x80, 0x38, 0xd3, 0x90, 0x40, ++0xe5, 0x22, 0x1b, 0xc0, 0x38, 0x65, 0xe5, 0x22, 0x1e, 0x00, 0x38, 0x8d, ++0x80, 0x99, 0x28, 0x02, 0xf0, 0x8c, 0x24, 0x48, 0x90, 0x80, 0x90, 0x40, ++0xe5, 0x22, 0x84, 0x40, 0x3a, 0x0d, 0xe5, 0x22, 0x81, 0x40, 0x3a, 0x19, ++0x90, 0x40, 0xe5, 0x22, 0x74, 0x40, 0x39, 0xcd, 0xe5, 0x22, 0x71, 0x40, ++0x39, 0xd9, 0x91, 0x48, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, ++0x62, 0x40, 0x39, 0xa1, 0xe5, 0x22, 0x56, 0x40, 0x39, 0x71, 0xcb, 0x61, ++0x23, 0x00, 0x85, 0x34, 0x90, 0x90, 0x40, 0xe5, 0x22, 0x4a, 0x40, 0x39, ++0x41, 0xe5, 0x22, 0x3e, 0x40, 0x39, 0x11, 0x90, 0x48, 0xcb, 0xa1, 0x22, ++0x80, 0x85, 0x34, 0x8e, 0xcb, 0xa1, 0x22, 0xc0, 0x85, 0x34, 0x8f, 0x10, ++0x10, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, 0x33, 0xc0, 0x38, 0xcb, 0xe5, ++0x22, 0x30, 0xc0, 0x38, 0xd9, 0x90, 0x40, 0xe5, 0x22, 0x22, 0x40, 0x38, ++0x85, 0xe5, 0x22, 0x1f, 0x40, 0x38, 0x93, ++}; ++ ++static const struct ia64_dis_names ia64_dis_names[] = { ++{ 0x51, 41, 0, 10 }, ++{ 0x31, 41, 1, 20 }, ++{ 0x11, 42, 0, 19 }, ++{ 0x29, 41, 0, 12 }, ++{ 0x19, 41, 1, 24 }, ++{ 0x9, 42, 0, 23 }, ++{ 0x15, 41, 0, 14 }, ++{ 0xd, 41, 1, 28 }, ++{ 0x5, 42, 0, 27 }, ++{ 0xb, 41, 0, 16 }, ++{ 0x7, 41, 1, 32 }, ++{ 0x3, 42, 0, 31 }, ++{ 0x51, 39, 1, 58 }, ++{ 0x50, 39, 0, 34 }, ++{ 0xd1, 39, 1, 57 }, ++{ 0xd0, 39, 0, 33 }, ++{ 0x31, 39, 1, 68 }, ++{ 0x30, 39, 1, 44 }, ++{ 0x11, 40, 1, 67 }, ++{ 0x10, 40, 0, 43 }, ++{ 0x71, 39, 1, 66 }, ++{ 0x70, 39, 1, 42 }, ++{ 0x31, 40, 1, 65 }, ++{ 0x30, 40, 0, 41 }, ++{ 0x29, 39, 1, 60 }, ++{ 0x28, 39, 0, 36 }, ++{ 0x69, 39, 1, 59 }, ++{ 0x68, 39, 0, 35 }, ++{ 0x19, 39, 1, 72 }, ++{ 0x18, 39, 1, 48 }, ++{ 0x9, 40, 1, 71 }, ++{ 0x8, 40, 0, 47 }, ++{ 0x39, 39, 1, 70 }, ++{ 0x38, 39, 1, 46 }, ++{ 0x19, 40, 1, 69 }, ++{ 0x18, 40, 0, 45 }, ++{ 0x15, 39, 1, 62 }, ++{ 0x14, 39, 0, 38 }, ++{ 0x35, 39, 1, 61 }, ++{ 0x34, 39, 0, 37 }, ++{ 0xd, 39, 1, 76 }, ++{ 0xc, 39, 1, 52 }, ++{ 0x5, 40, 1, 75 }, ++{ 0x4, 40, 0, 51 }, ++{ 0x1d, 39, 1, 74 }, ++{ 0x1c, 39, 1, 50 }, ++{ 0xd, 40, 1, 73 }, ++{ 0xc, 40, 0, 49 }, ++{ 0xb, 39, 1, 64 }, ++{ 0xa, 39, 0, 40 }, ++{ 0x1b, 39, 1, 63 }, ++{ 0x1a, 39, 0, 39 }, ++{ 0x7, 39, 1, 80 }, ++{ 0x6, 39, 1, 56 }, ++{ 0x3, 40, 1, 79 }, ++{ 0x2, 40, 0, 55 }, ++{ 0xf, 39, 1, 78 }, ++{ 0xe, 39, 1, 54 }, ++{ 0x7, 40, 1, 77 }, ++{ 0x6, 40, 0, 53 }, ++{ 0x8, 38, 0, 82 }, ++{ 0x18, 38, 0, 81 }, ++{ 0x1, 38, 1, 86 }, ++{ 0x2, 38, 0, 85 }, ++{ 0x3, 38, 1, 84 }, ++{ 0x4, 38, 0, 83 }, ++{ 0x1, 336, 0, 87 }, ++{ 0x20, 289, 0, 98 }, ++{ 0x220, 289, 0, 94 }, ++{ 0x1220, 289, 0, 91 }, ++{ 0xa20, 289, 0, 92 }, ++{ 0x620, 289, 0, 93 }, ++{ 0x120, 289, 0, 95 }, ++{ 0xa0, 289, 0, 96 }, ++{ 0x60, 289, 0, 97 }, ++{ 0x10, 289, 0, 102 }, ++{ 0x90, 289, 0, 99 }, ++{ 0x50, 289, 0, 100 }, ++{ 0x30, 289, 0, 101 }, ++{ 0x8, 289, 0, 103 }, ++{ 0x4, 289, 0, 104 }, ++{ 0x2, 289, 0, 105 }, ++{ 0x1, 289, 0, 106 }, ++{ 0x1, 411, 0, 108 }, ++{ 0x3, 411, 0, 107 }, ++{ 0x2, 417, 0, 109 }, ++{ 0x1, 417, 0, 110 }, ++{ 0x2, 413, 0, 111 }, ++{ 0x1, 413, 0, 112 }, ++{ 0x2, 415, 0, 113 }, ++{ 0x1, 415, 0, 114 }, ++{ 0x2, 419, 0, 115 }, ++{ 0x1, 419, 0, 116 }, ++{ 0x1, 268, 0, 143 }, ++{ 0x5, 268, 0, 141 }, ++{ 0x3, 268, 0, 142 }, ++{ 0x140, 277, 0, 119 }, ++{ 0x540, 277, 0, 117 }, ++{ 0x340, 277, 0, 118 }, ++{ 0xc0, 277, 0, 131 }, ++{ 0x2c0, 277, 0, 129 }, ++{ 0x1c0, 277, 0, 130 }, ++{ 0x20, 277, 0, 146 }, ++{ 0xa0, 277, 0, 144 }, ++{ 0x60, 277, 0, 145 }, ++{ 0x10, 277, 0, 158 }, ++{ 0x50, 277, 0, 156 }, ++{ 0x30, 277, 0, 157 }, ++{ 0x8, 277, 0, 170 }, ++{ 0x28, 277, 0, 168 }, ++{ 0x18, 277, 0, 169 }, ++{ 0x4, 277, 0, 180 }, ++{ 0x2, 277, 0, 181 }, ++{ 0x1, 277, 0, 182 }, ++{ 0x140, 271, 0, 122 }, ++{ 0x540, 271, 0, 120 }, ++{ 0x340, 271, 0, 121 }, ++{ 0xc0, 271, 0, 134 }, ++{ 0x2c0, 271, 0, 132 }, ++{ 0x1c0, 271, 0, 133 }, ++{ 0x20, 271, 0, 149 }, ++{ 0xa0, 271, 0, 147 }, ++{ 0x60, 271, 0, 148 }, ++{ 0x10, 271, 0, 161 }, ++{ 0x50, 271, 0, 159 }, ++{ 0x30, 271, 0, 160 }, ++{ 0x8, 271, 0, 173 }, ++{ 0x28, 271, 0, 171 }, ++{ 0x18, 271, 0, 172 }, ++{ 0x4, 271, 0, 183 }, ++{ 0x2, 271, 0, 184 }, ++{ 0x1, 271, 0, 185 }, ++{ 0x140, 274, 0, 125 }, ++{ 0x540, 274, 0, 123 }, ++{ 0x340, 274, 0, 124 }, ++{ 0xc0, 274, 0, 137 }, ++{ 0x2c0, 274, 0, 135 }, ++{ 0x1c0, 274, 0, 136 }, ++{ 0x20, 274, 0, 152 }, ++{ 0xa0, 274, 0, 150 }, ++{ 0x60, 274, 0, 151 }, ++{ 0x10, 274, 0, 164 }, ++{ 0x50, 274, 0, 162 }, ++{ 0x30, 274, 0, 163 }, ++{ 0x8, 274, 0, 176 }, ++{ 0x28, 274, 0, 174 }, ++{ 0x18, 274, 0, 175 }, ++{ 0x4, 274, 0, 186 }, ++{ 0x2, 274, 0, 187 }, ++{ 0x1, 274, 0, 188 }, ++{ 0x140, 286, 0, 128 }, ++{ 0x540, 286, 0, 126 }, ++{ 0x340, 286, 0, 127 }, ++{ 0xc0, 286, 0, 140 }, ++{ 0x2c0, 286, 0, 138 }, ++{ 0x1c0, 286, 0, 139 }, ++{ 0x20, 286, 0, 155 }, ++{ 0xa0, 286, 0, 153 }, ++{ 0x60, 286, 0, 154 }, ++{ 0x10, 286, 0, 167 }, ++{ 0x50, 286, 0, 165 }, ++{ 0x30, 286, 0, 166 }, ++{ 0x8, 286, 0, 179 }, ++{ 0x28, 286, 0, 177 }, ++{ 0x18, 286, 0, 178 }, ++{ 0x4, 286, 0, 189 }, ++{ 0x2, 286, 0, 190 }, ++{ 0x1, 286, 0, 191 }, ++{ 0x8, 390, 0, 192 }, ++{ 0x4, 390, 0, 193 }, ++{ 0x2, 390, 0, 194 }, ++{ 0x1, 390, 0, 195 }, ++{ 0x20, 288, 0, 203 }, ++{ 0x220, 288, 0, 199 }, ++{ 0x1220, 288, 0, 196 }, ++{ 0xa20, 288, 0, 197 }, ++{ 0x620, 288, 0, 198 }, ++{ 0x120, 288, 0, 200 }, ++{ 0xa0, 288, 0, 201 }, ++{ 0x60, 288, 0, 202 }, ++{ 0x10, 288, 0, 207 }, ++{ 0x90, 288, 0, 204 }, ++{ 0x50, 288, 0, 205 }, ++{ 0x30, 288, 0, 206 }, ++{ 0x8, 288, 0, 208 }, ++{ 0x4, 288, 0, 209 }, ++{ 0x2, 288, 0, 210 }, ++{ 0x1, 288, 0, 211 }, ++{ 0x20, 287, 0, 219 }, ++{ 0x220, 287, 0, 215 }, ++{ 0x1220, 287, 0, 212 }, ++{ 0xa20, 287, 0, 213 }, ++{ 0x620, 287, 0, 214 }, ++{ 0x120, 287, 0, 216 }, ++{ 0xa0, 287, 0, 217 }, ++{ 0x60, 287, 0, 218 }, ++{ 0x10, 287, 0, 223 }, ++{ 0x90, 287, 0, 220 }, ++{ 0x50, 287, 0, 221 }, ++{ 0x30, 287, 0, 222 }, ++{ 0x8, 287, 0, 224 }, ++{ 0x4, 287, 0, 225 }, ++{ 0x2, 287, 0, 226 }, ++{ 0x1, 287, 0, 227 }, ++{ 0x140, 279, 0, 230 }, ++{ 0x540, 279, 0, 228 }, ++{ 0x340, 279, 0, 229 }, ++{ 0xc0, 279, 0, 239 }, ++{ 0x2c0, 279, 0, 237 }, ++{ 0x1c0, 279, 0, 238 }, ++{ 0x20, 279, 0, 248 }, ++{ 0xa0, 279, 0, 246 }, ++{ 0x60, 279, 0, 247 }, ++{ 0x10, 279, 0, 257 }, ++{ 0x50, 279, 0, 255 }, ++{ 0x30, 279, 0, 256 }, ++{ 0x8, 279, 0, 266 }, ++{ 0x28, 279, 0, 264 }, ++{ 0x18, 279, 0, 265 }, ++{ 0x4, 279, 0, 273 }, ++{ 0x2, 279, 0, 274 }, ++{ 0x1, 279, 0, 275 }, ++{ 0x140, 281, 0, 233 }, ++{ 0x540, 281, 0, 231 }, ++{ 0x340, 281, 0, 232 }, ++{ 0xc0, 281, 0, 242 }, ++{ 0x2c0, 281, 0, 240 }, ++{ 0x1c0, 281, 0, 241 }, ++{ 0x20, 281, 0, 251 }, ++{ 0xa0, 281, 0, 249 }, ++{ 0x60, 281, 0, 250 }, ++{ 0x10, 281, 0, 260 }, ++{ 0x50, 281, 0, 258 }, ++{ 0x30, 281, 0, 259 }, ++{ 0x8, 281, 0, 269 }, ++{ 0x28, 281, 0, 267 }, ++{ 0x18, 281, 0, 268 }, ++{ 0x4, 281, 0, 276 }, ++{ 0x2, 281, 0, 277 }, ++{ 0x1, 281, 0, 278 }, ++{ 0x140, 283, 0, 236 }, ++{ 0x540, 283, 0, 234 }, ++{ 0x340, 283, 0, 235 }, ++{ 0xc0, 283, 0, 245 }, ++{ 0x2c0, 283, 0, 243 }, ++{ 0x1c0, 283, 0, 244 }, ++{ 0x20, 283, 0, 254 }, ++{ 0xa0, 283, 0, 252 }, ++{ 0x60, 283, 0, 253 }, ++{ 0x10, 283, 0, 263 }, ++{ 0x50, 283, 0, 261 }, ++{ 0x30, 283, 0, 262 }, ++{ 0x8, 283, 0, 272 }, ++{ 0x28, 283, 0, 270 }, ++{ 0x18, 283, 0, 271 }, ++{ 0x4, 283, 0, 279 }, ++{ 0x2, 283, 0, 280 }, ++{ 0x1, 283, 0, 281 }, ++{ 0x140, 278, 0, 284 }, ++{ 0x540, 278, 0, 282 }, ++{ 0x340, 278, 0, 283 }, ++{ 0xc0, 278, 0, 293 }, ++{ 0x2c0, 278, 0, 291 }, ++{ 0x1c0, 278, 0, 292 }, ++{ 0x20, 278, 0, 302 }, ++{ 0xa0, 278, 0, 300 }, ++{ 0x60, 278, 0, 301 }, ++{ 0x10, 278, 0, 311 }, ++{ 0x50, 278, 0, 309 }, ++{ 0x30, 278, 0, 310 }, ++{ 0x8, 278, 0, 320 }, ++{ 0x28, 278, 0, 318 }, ++{ 0x18, 278, 0, 319 }, ++{ 0x4, 278, 0, 327 }, ++{ 0x2, 278, 0, 328 }, ++{ 0x1, 278, 0, 329 }, ++{ 0x140, 280, 0, 287 }, ++{ 0x540, 280, 0, 285 }, ++{ 0x340, 280, 0, 286 }, ++{ 0xc0, 280, 0, 296 }, ++{ 0x2c0, 280, 0, 294 }, ++{ 0x1c0, 280, 0, 295 }, ++{ 0x20, 280, 0, 305 }, ++{ 0xa0, 280, 0, 303 }, ++{ 0x60, 280, 0, 304 }, ++{ 0x10, 280, 0, 314 }, ++{ 0x50, 280, 0, 312 }, ++{ 0x30, 280, 0, 313 }, ++{ 0x8, 280, 0, 323 }, ++{ 0x28, 280, 0, 321 }, ++{ 0x18, 280, 0, 322 }, ++{ 0x4, 280, 0, 330 }, ++{ 0x2, 280, 0, 331 }, ++{ 0x1, 280, 0, 332 }, ++{ 0x140, 282, 0, 290 }, ++{ 0x540, 282, 0, 288 }, ++{ 0x340, 282, 0, 289 }, ++{ 0xc0, 282, 0, 299 }, ++{ 0x2c0, 282, 0, 297 }, ++{ 0x1c0, 282, 0, 298 }, ++{ 0x20, 282, 0, 308 }, ++{ 0xa0, 282, 0, 306 }, ++{ 0x60, 282, 0, 307 }, ++{ 0x10, 282, 0, 317 }, ++{ 0x50, 282, 0, 315 }, ++{ 0x30, 282, 0, 316 }, ++{ 0x8, 282, 0, 326 }, ++{ 0x28, 282, 0, 324 }, ++{ 0x18, 282, 0, 325 }, ++{ 0x4, 282, 0, 333 }, ++{ 0x2, 282, 0, 334 }, ++{ 0x1, 282, 0, 335 }, ++{ 0x1, 410, 0, 337 }, ++{ 0x3, 410, 0, 336 }, ++{ 0x2, 416, 0, 338 }, ++{ 0x1, 416, 0, 339 }, ++{ 0x2, 412, 0, 340 }, ++{ 0x1, 412, 0, 341 }, ++{ 0x2, 414, 0, 342 }, ++{ 0x1, 414, 0, 343 }, ++{ 0x2, 418, 0, 344 }, ++{ 0x1, 418, 0, 345 }, ++{ 0x1, 267, 0, 372 }, ++{ 0x5, 267, 0, 370 }, ++{ 0x3, 267, 0, 371 }, ++{ 0x140, 276, 0, 348 }, ++{ 0x540, 276, 0, 346 }, ++{ 0x340, 276, 0, 347 }, ++{ 0xc0, 276, 0, 360 }, ++{ 0x2c0, 276, 0, 358 }, ++{ 0x1c0, 276, 0, 359 }, ++{ 0x20, 276, 0, 375 }, ++{ 0xa0, 276, 0, 373 }, ++{ 0x60, 276, 0, 374 }, ++{ 0x10, 276, 0, 387 }, ++{ 0x50, 276, 0, 385 }, ++{ 0x30, 276, 0, 386 }, ++{ 0x8, 276, 0, 399 }, ++{ 0x28, 276, 0, 397 }, ++{ 0x18, 276, 0, 398 }, ++{ 0x4, 276, 0, 409 }, ++{ 0x2, 276, 0, 410 }, ++{ 0x1, 276, 0, 411 }, ++{ 0x140, 270, 0, 351 }, ++{ 0x540, 270, 0, 349 }, ++{ 0x340, 270, 0, 350 }, ++{ 0xc0, 270, 0, 363 }, ++{ 0x2c0, 270, 0, 361 }, ++{ 0x1c0, 270, 0, 362 }, ++{ 0x20, 270, 0, 378 }, ++{ 0xa0, 270, 0, 376 }, ++{ 0x60, 270, 0, 377 }, ++{ 0x10, 270, 0, 390 }, ++{ 0x50, 270, 0, 388 }, ++{ 0x30, 270, 0, 389 }, ++{ 0x8, 270, 0, 402 }, ++{ 0x28, 270, 0, 400 }, ++{ 0x18, 270, 0, 401 }, ++{ 0x4, 270, 0, 412 }, ++{ 0x2, 270, 0, 413 }, ++{ 0x1, 270, 0, 414 }, ++{ 0x140, 273, 0, 354 }, ++{ 0x540, 273, 0, 352 }, ++{ 0x340, 273, 0, 353 }, ++{ 0xc0, 273, 0, 366 }, ++{ 0x2c0, 273, 0, 364 }, ++{ 0x1c0, 273, 0, 365 }, ++{ 0x20, 273, 0, 381 }, ++{ 0xa0, 273, 0, 379 }, ++{ 0x60, 273, 0, 380 }, ++{ 0x10, 273, 0, 393 }, ++{ 0x50, 273, 0, 391 }, ++{ 0x30, 273, 0, 392 }, ++{ 0x8, 273, 0, 405 }, ++{ 0x28, 273, 0, 403 }, ++{ 0x18, 273, 0, 404 }, ++{ 0x4, 273, 0, 415 }, ++{ 0x2, 273, 0, 416 }, ++{ 0x1, 273, 0, 417 }, ++{ 0x140, 285, 0, 357 }, ++{ 0x540, 285, 0, 355 }, ++{ 0x340, 285, 0, 356 }, ++{ 0xc0, 285, 0, 369 }, ++{ 0x2c0, 285, 0, 367 }, ++{ 0x1c0, 285, 0, 368 }, ++{ 0x20, 285, 0, 384 }, ++{ 0xa0, 285, 0, 382 }, ++{ 0x60, 285, 0, 383 }, ++{ 0x10, 285, 0, 396 }, ++{ 0x50, 285, 0, 394 }, ++{ 0x30, 285, 0, 395 }, ++{ 0x8, 285, 0, 408 }, ++{ 0x28, 285, 0, 406 }, ++{ 0x18, 285, 0, 407 }, ++{ 0x4, 285, 0, 418 }, ++{ 0x2, 285, 0, 419 }, ++{ 0x1, 285, 0, 420 }, ++{ 0x1, 266, 0, 447 }, ++{ 0x5, 266, 0, 445 }, ++{ 0x3, 266, 0, 446 }, ++{ 0x140, 275, 0, 423 }, ++{ 0x540, 275, 0, 421 }, ++{ 0x340, 275, 0, 422 }, ++{ 0xc0, 275, 0, 435 }, ++{ 0x2c0, 275, 0, 433 }, ++{ 0x1c0, 275, 0, 434 }, ++{ 0x20, 275, 0, 450 }, ++{ 0xa0, 275, 0, 448 }, ++{ 0x60, 275, 0, 449 }, ++{ 0x10, 275, 0, 462 }, ++{ 0x50, 275, 0, 460 }, ++{ 0x30, 275, 0, 461 }, ++{ 0x8, 275, 0, 474 }, ++{ 0x28, 275, 0, 472 }, ++{ 0x18, 275, 0, 473 }, ++{ 0x4, 275, 0, 484 }, ++{ 0x2, 275, 0, 485 }, ++{ 0x1, 275, 0, 486 }, ++{ 0x140, 269, 0, 426 }, ++{ 0x540, 269, 0, 424 }, ++{ 0x340, 269, 0, 425 }, ++{ 0xc0, 269, 0, 438 }, ++{ 0x2c0, 269, 0, 436 }, ++{ 0x1c0, 269, 0, 437 }, ++{ 0x20, 269, 0, 453 }, ++{ 0xa0, 269, 0, 451 }, ++{ 0x60, 269, 0, 452 }, ++{ 0x10, 269, 0, 465 }, ++{ 0x50, 269, 0, 463 }, ++{ 0x30, 269, 0, 464 }, ++{ 0x8, 269, 0, 477 }, ++{ 0x28, 269, 0, 475 }, ++{ 0x18, 269, 0, 476 }, ++{ 0x4, 269, 0, 487 }, ++{ 0x2, 269, 0, 488 }, ++{ 0x1, 269, 0, 489 }, ++{ 0x140, 272, 0, 429 }, ++{ 0x540, 272, 0, 427 }, ++{ 0x340, 272, 0, 428 }, ++{ 0xc0, 272, 0, 441 }, ++{ 0x2c0, 272, 0, 439 }, ++{ 0x1c0, 272, 0, 440 }, ++{ 0x20, 272, 0, 456 }, ++{ 0xa0, 272, 0, 454 }, ++{ 0x60, 272, 0, 455 }, ++{ 0x10, 272, 0, 468 }, ++{ 0x50, 272, 0, 466 }, ++{ 0x30, 272, 0, 467 }, ++{ 0x8, 272, 0, 480 }, ++{ 0x28, 272, 0, 478 }, ++{ 0x18, 272, 0, 479 }, ++{ 0x4, 272, 0, 490 }, ++{ 0x2, 272, 0, 491 }, ++{ 0x1, 272, 0, 492 }, ++{ 0x140, 284, 0, 432 }, ++{ 0x540, 284, 0, 430 }, ++{ 0x340, 284, 0, 431 }, ++{ 0xc0, 284, 0, 444 }, ++{ 0x2c0, 284, 0, 442 }, ++{ 0x1c0, 284, 0, 443 }, ++{ 0x20, 284, 0, 459 }, ++{ 0xa0, 284, 0, 457 }, ++{ 0x60, 284, 0, 458 }, ++{ 0x10, 284, 0, 471 }, ++{ 0x50, 284, 0, 469 }, ++{ 0x30, 284, 0, 470 }, ++{ 0x8, 284, 0, 483 }, ++{ 0x28, 284, 0, 481 }, ++{ 0x18, 284, 0, 482 }, ++{ 0x4, 284, 0, 493 }, ++{ 0x2, 284, 0, 494 }, ++{ 0x1, 284, 0, 495 }, ++{ 0x8, 409, 0, 497 }, ++{ 0x18, 409, 0, 496 }, ++{ 0x4, 409, 0, 499 }, ++{ 0xc, 409, 0, 498 }, ++{ 0x2, 409, 0, 506 }, ++{ 0x1, 409, 0, 507 }, ++{ 0x4, 407, 0, 501 }, ++{ 0xc, 407, 0, 500 }, ++{ 0x2, 407, 0, 508 }, ++{ 0x1, 407, 0, 509 }, ++{ 0x4, 405, 0, 503 }, ++{ 0xc, 405, 0, 502 }, ++{ 0x2, 405, 0, 510 }, ++{ 0x1, 405, 0, 511 }, ++{ 0x4, 401, 0, 505 }, ++{ 0xc, 401, 0, 504 }, ++{ 0x2, 401, 0, 512 }, ++{ 0x1, 401, 0, 513 }, ++{ 0xa00, 265, 0, 528 }, ++{ 0x2a00, 265, 0, 526 }, ++{ 0x1a00, 265, 0, 527 }, ++{ 0x600, 265, 0, 540 }, ++{ 0x2600, 265, 0, 516 }, ++{ 0xa600, 265, 0, 514 }, ++{ 0x6600, 265, 0, 515 }, ++{ 0x1600, 265, 0, 538 }, ++{ 0xe00, 265, 0, 539 }, ++{ 0x100, 265, 0, 552 }, ++{ 0x500, 265, 0, 550 }, ++{ 0x300, 265, 0, 551 }, ++{ 0x80, 265, 0, 555 }, ++{ 0x280, 265, 0, 553 }, ++{ 0x180, 265, 0, 554 }, ++{ 0x40, 265, 0, 567 }, ++{ 0x140, 265, 0, 565 }, ++{ 0xc0, 265, 0, 566 }, ++{ 0x20, 265, 0, 579 }, ++{ 0xa0, 265, 0, 577 }, ++{ 0x60, 265, 0, 578 }, ++{ 0x10, 265, 0, 591 }, ++{ 0x50, 265, 0, 589 }, ++{ 0x30, 265, 0, 590 }, ++{ 0x8, 265, 0, 603 }, ++{ 0x28, 265, 0, 601 }, ++{ 0x18, 265, 0, 602 }, ++{ 0x4, 265, 0, 613 }, ++{ 0x2, 265, 0, 614 }, ++{ 0x1, 265, 0, 615 }, ++{ 0x500, 261, 0, 531 }, ++{ 0x1500, 261, 0, 529 }, ++{ 0xd00, 261, 0, 530 }, ++{ 0x300, 261, 0, 543 }, ++{ 0x1300, 261, 0, 519 }, ++{ 0x5300, 261, 0, 517 }, ++{ 0x3300, 261, 0, 518 }, ++{ 0xb00, 261, 0, 541 }, ++{ 0x700, 261, 0, 542 }, ++{ 0x80, 261, 0, 558 }, ++{ 0x280, 261, 0, 556 }, ++{ 0x180, 261, 0, 557 }, ++{ 0x40, 261, 0, 570 }, ++{ 0x140, 261, 0, 568 }, ++{ 0xc0, 261, 0, 569 }, ++{ 0x20, 261, 0, 582 }, ++{ 0xa0, 261, 0, 580 }, ++{ 0x60, 261, 0, 581 }, ++{ 0x10, 261, 0, 594 }, ++{ 0x50, 261, 0, 592 }, ++{ 0x30, 261, 0, 593 }, ++{ 0x8, 261, 0, 606 }, ++{ 0x28, 261, 0, 604 }, ++{ 0x18, 261, 0, 605 }, ++{ 0x4, 261, 0, 616 }, ++{ 0x2, 261, 0, 617 }, ++{ 0x1, 261, 0, 618 }, ++{ 0x500, 258, 0, 534 }, ++{ 0x1500, 258, 0, 532 }, ++{ 0xd00, 258, 0, 533 }, ++{ 0x300, 258, 0, 546 }, ++{ 0x1300, 258, 0, 522 }, ++{ 0x5300, 258, 0, 520 }, ++{ 0x3300, 258, 0, 521 }, ++{ 0xb00, 258, 0, 544 }, ++{ 0x700, 258, 0, 545 }, ++{ 0x80, 258, 0, 561 }, ++{ 0x280, 258, 0, 559 }, ++{ 0x180, 258, 0, 560 }, ++{ 0x40, 258, 0, 573 }, ++{ 0x140, 258, 0, 571 }, ++{ 0xc0, 258, 0, 572 }, ++{ 0x20, 258, 0, 585 }, ++{ 0xa0, 258, 0, 583 }, ++{ 0x60, 258, 0, 584 }, ++{ 0x10, 258, 0, 597 }, ++{ 0x50, 258, 0, 595 }, ++{ 0x30, 258, 0, 596 }, ++{ 0x8, 258, 0, 609 }, ++{ 0x28, 258, 0, 607 }, ++{ 0x18, 258, 0, 608 }, ++{ 0x4, 258, 0, 619 }, ++{ 0x2, 258, 0, 620 }, ++{ 0x1, 258, 0, 621 }, ++{ 0x500, 253, 0, 537 }, ++{ 0x1500, 253, 0, 535 }, ++{ 0xd00, 253, 0, 536 }, ++{ 0x300, 253, 0, 549 }, ++{ 0x1300, 253, 0, 525 }, ++{ 0x5300, 253, 0, 523 }, ++{ 0x3300, 253, 0, 524 }, ++{ 0xb00, 253, 0, 547 }, ++{ 0x700, 253, 0, 548 }, ++{ 0x80, 253, 0, 564 }, ++{ 0x280, 253, 0, 562 }, ++{ 0x180, 253, 0, 563 }, ++{ 0x40, 253, 0, 576 }, ++{ 0x140, 253, 0, 574 }, ++{ 0xc0, 253, 0, 575 }, ++{ 0x20, 253, 0, 588 }, ++{ 0xa0, 253, 0, 586 }, ++{ 0x60, 253, 0, 587 }, ++{ 0x10, 253, 0, 600 }, ++{ 0x50, 253, 0, 598 }, ++{ 0x30, 253, 0, 599 }, ++{ 0x8, 253, 0, 612 }, ++{ 0x28, 253, 0, 610 }, ++{ 0x18, 253, 0, 611 }, ++{ 0x4, 253, 0, 622 }, ++{ 0x2, 253, 0, 623 }, ++{ 0x1, 253, 0, 624 }, ++{ 0x8, 238, 0, 625 }, ++{ 0x4, 238, 0, 626 }, ++{ 0x2, 238, 0, 627 }, ++{ 0x1, 238, 0, 628 }, ++{ 0x2, 176, 0, 631 }, ++{ 0xa, 176, 0, 629 }, ++{ 0x6, 176, 0, 630 }, ++{ 0x1, 176, 0, 637 }, ++{ 0x5, 176, 0, 635 }, ++{ 0x3, 176, 0, 636 }, ++{ 0x2, 175, 0, 634 }, ++{ 0xa, 175, 0, 632 }, ++{ 0x6, 175, 0, 633 }, ++{ 0x1, 175, 0, 640 }, ++{ 0x5, 175, 0, 638 }, ++{ 0x3, 175, 0, 639 }, ++{ 0x4, 446, 0, 641 }, ++{ 0x2, 446, 0, 642 }, ++{ 0x1, 446, 0, 643 }, ++{ 0x4, 445, 0, 644 }, ++{ 0x2, 445, 0, 645 }, ++{ 0x1, 445, 0, 646 }, ++{ 0x4, 444, 0, 647 }, ++{ 0x2, 444, 0, 648 }, ++{ 0x1, 444, 0, 649 }, ++{ 0x4, 443, 0, 650 }, ++{ 0x2, 443, 0, 651 }, ++{ 0x1, 443, 0, 652 }, ++{ 0x2, 123, 1, 658 }, ++{ 0x2, 124, 0, 657 }, ++{ 0xa, 123, 1, 654 }, ++{ 0xa, 124, 0, 653 }, ++{ 0x6, 123, 1, 656 }, ++{ 0x6, 124, 0, 655 }, ++{ 0x1, 123, 1, 688 }, ++{ 0x1, 124, 0, 687 }, ++{ 0x5, 123, 1, 684 }, ++{ 0x5, 124, 0, 683 }, ++{ 0x3, 123, 1, 686 }, ++{ 0x3, 124, 0, 685 }, ++{ 0x2, 131, 1, 664 }, ++{ 0x2, 132, 0, 663 }, ++{ 0xa, 131, 1, 660 }, ++{ 0xa, 132, 0, 659 }, ++{ 0x6, 131, 1, 662 }, ++{ 0x6, 132, 0, 661 }, ++{ 0x1, 131, 1, 694 }, ++{ 0x1, 132, 0, 693 }, ++{ 0x5, 131, 1, 690 }, ++{ 0x5, 132, 0, 689 }, ++{ 0x3, 131, 1, 692 }, ++{ 0x3, 132, 0, 691 }, ++{ 0x2, 129, 1, 670 }, ++{ 0x2, 130, 0, 669 }, ++{ 0xa, 129, 1, 666 }, ++{ 0xa, 130, 0, 665 }, ++{ 0x6, 129, 1, 668 }, ++{ 0x6, 130, 0, 667 }, ++{ 0x1, 129, 1, 700 }, ++{ 0x1, 130, 0, 699 }, ++{ 0x5, 129, 1, 696 }, ++{ 0x5, 130, 0, 695 }, ++{ 0x3, 129, 1, 698 }, ++{ 0x3, 130, 0, 697 }, ++{ 0x2, 127, 1, 676 }, ++{ 0x2, 128, 0, 675 }, ++{ 0xa, 127, 1, 672 }, ++{ 0xa, 128, 0, 671 }, ++{ 0x6, 127, 1, 674 }, ++{ 0x6, 128, 0, 673 }, ++{ 0x1, 127, 1, 706 }, ++{ 0x1, 128, 0, 705 }, ++{ 0x5, 127, 1, 702 }, ++{ 0x5, 128, 0, 701 }, ++{ 0x3, 127, 1, 704 }, ++{ 0x3, 128, 0, 703 }, ++{ 0x2, 125, 1, 682 }, ++{ 0x2, 126, 0, 681 }, ++{ 0xa, 125, 1, 678 }, ++{ 0xa, 126, 0, 677 }, ++{ 0x6, 125, 1, 680 }, ++{ 0x6, 126, 0, 679 }, ++{ 0x1, 125, 1, 712 }, ++{ 0x1, 126, 0, 711 }, ++{ 0x5, 125, 1, 708 }, ++{ 0x5, 126, 0, 707 }, ++{ 0x3, 125, 1, 710 }, ++{ 0x3, 126, 0, 709 }, ++{ 0x4, 402, 1, 718 }, ++{ 0x4, 403, 0, 717 }, ++{ 0xc, 402, 1, 716 }, ++{ 0xc, 403, 0, 715 }, ++{ 0x2, 402, 1, 728 }, ++{ 0x2, 403, 0, 727 }, ++{ 0x1, 402, 1, 730 }, ++{ 0x1, 403, 0, 729 }, ++{ 0x8, 408, 0, 714 }, ++{ 0x18, 408, 0, 713 }, ++{ 0x4, 408, 0, 720 }, ++{ 0xc, 408, 0, 719 }, ++{ 0x2, 408, 0, 731 }, ++{ 0x1, 408, 0, 732 }, ++{ 0x4, 406, 0, 722 }, ++{ 0xc, 406, 0, 721 }, ++{ 0x2, 406, 0, 733 }, ++{ 0x1, 406, 0, 734 }, ++{ 0x4, 404, 0, 724 }, ++{ 0xc, 404, 0, 723 }, ++{ 0x2, 404, 0, 735 }, ++{ 0x1, 404, 0, 736 }, ++{ 0x4, 400, 0, 726 }, ++{ 0xc, 400, 0, 725 }, ++{ 0x2, 400, 0, 737 }, ++{ 0x1, 400, 0, 738 }, ++{ 0xa00, 264, 0, 753 }, ++{ 0x2a00, 264, 0, 751 }, ++{ 0x1a00, 264, 0, 752 }, ++{ 0x600, 264, 0, 765 }, ++{ 0x2600, 264, 0, 741 }, ++{ 0xa600, 264, 0, 739 }, ++{ 0x6600, 264, 0, 740 }, ++{ 0x1600, 264, 0, 763 }, ++{ 0xe00, 264, 0, 764 }, ++{ 0x100, 264, 0, 777 }, ++{ 0x500, 264, 0, 775 }, ++{ 0x300, 264, 0, 776 }, ++{ 0x80, 264, 0, 780 }, ++{ 0x280, 264, 0, 778 }, ++{ 0x180, 264, 0, 779 }, ++{ 0x40, 264, 0, 792 }, ++{ 0x140, 264, 0, 790 }, ++{ 0xc0, 264, 0, 791 }, ++{ 0x20, 264, 0, 804 }, ++{ 0xa0, 264, 0, 802 }, ++{ 0x60, 264, 0, 803 }, ++{ 0x10, 264, 0, 816 }, ++{ 0x50, 264, 0, 814 }, ++{ 0x30, 264, 0, 815 }, ++{ 0x8, 264, 0, 828 }, ++{ 0x28, 264, 0, 826 }, ++{ 0x18, 264, 0, 827 }, ++{ 0x4, 264, 0, 838 }, ++{ 0x2, 264, 0, 839 }, ++{ 0x1, 264, 0, 840 }, ++{ 0x500, 260, 0, 756 }, ++{ 0x1500, 260, 0, 754 }, ++{ 0xd00, 260, 0, 755 }, ++{ 0x300, 260, 0, 768 }, ++{ 0x1300, 260, 0, 744 }, ++{ 0x5300, 260, 0, 742 }, ++{ 0x3300, 260, 0, 743 }, ++{ 0xb00, 260, 0, 766 }, ++{ 0x700, 260, 0, 767 }, ++{ 0x80, 260, 0, 783 }, ++{ 0x280, 260, 0, 781 }, ++{ 0x180, 260, 0, 782 }, ++{ 0x40, 260, 0, 795 }, ++{ 0x140, 260, 0, 793 }, ++{ 0xc0, 260, 0, 794 }, ++{ 0x20, 260, 0, 807 }, ++{ 0xa0, 260, 0, 805 }, ++{ 0x60, 260, 0, 806 }, ++{ 0x10, 260, 0, 819 }, ++{ 0x50, 260, 0, 817 }, ++{ 0x30, 260, 0, 818 }, ++{ 0x8, 260, 0, 831 }, ++{ 0x28, 260, 0, 829 }, ++{ 0x18, 260, 0, 830 }, ++{ 0x4, 260, 0, 841 }, ++{ 0x2, 260, 0, 842 }, ++{ 0x1, 260, 0, 843 }, ++{ 0x500, 257, 0, 759 }, ++{ 0x1500, 257, 0, 757 }, ++{ 0xd00, 257, 0, 758 }, ++{ 0x300, 257, 0, 771 }, ++{ 0x1300, 257, 0, 747 }, ++{ 0x5300, 257, 0, 745 }, ++{ 0x3300, 257, 0, 746 }, ++{ 0xb00, 257, 0, 769 }, ++{ 0x700, 257, 0, 770 }, ++{ 0x80, 257, 0, 786 }, ++{ 0x280, 257, 0, 784 }, ++{ 0x180, 257, 0, 785 }, ++{ 0x40, 257, 0, 798 }, ++{ 0x140, 257, 0, 796 }, ++{ 0xc0, 257, 0, 797 }, ++{ 0x20, 257, 0, 810 }, ++{ 0xa0, 257, 0, 808 }, ++{ 0x60, 257, 0, 809 }, ++{ 0x10, 257, 0, 822 }, ++{ 0x50, 257, 0, 820 }, ++{ 0x30, 257, 0, 821 }, ++{ 0x8, 257, 0, 834 }, ++{ 0x28, 257, 0, 832 }, ++{ 0x18, 257, 0, 833 }, ++{ 0x4, 257, 0, 844 }, ++{ 0x2, 257, 0, 845 }, ++{ 0x1, 257, 0, 846 }, ++{ 0x500, 252, 0, 762 }, ++{ 0x1500, 252, 0, 760 }, ++{ 0xd00, 252, 0, 761 }, ++{ 0x300, 252, 0, 774 }, ++{ 0x1300, 252, 0, 750 }, ++{ 0x5300, 252, 0, 748 }, ++{ 0x3300, 252, 0, 749 }, ++{ 0xb00, 252, 0, 772 }, ++{ 0x700, 252, 0, 773 }, ++{ 0x80, 252, 0, 789 }, ++{ 0x280, 252, 0, 787 }, ++{ 0x180, 252, 0, 788 }, ++{ 0x40, 252, 0, 801 }, ++{ 0x140, 252, 0, 799 }, ++{ 0xc0, 252, 0, 800 }, ++{ 0x20, 252, 0, 813 }, ++{ 0xa0, 252, 0, 811 }, ++{ 0x60, 252, 0, 812 }, ++{ 0x10, 252, 0, 825 }, ++{ 0x50, 252, 0, 823 }, ++{ 0x30, 252, 0, 824 }, ++{ 0x8, 252, 0, 837 }, ++{ 0x28, 252, 0, 835 }, ++{ 0x18, 252, 0, 836 }, ++{ 0x4, 252, 0, 847 }, ++{ 0x2, 252, 0, 848 }, ++{ 0x1, 252, 0, 849 }, ++{ 0x8, 254, 1, 895 }, ++{ 0x8, 255, 0, 894 }, ++{ 0x28, 254, 1, 891 }, ++{ 0x28, 255, 0, 890 }, ++{ 0x18, 254, 1, 893 }, ++{ 0x18, 255, 0, 892 }, ++{ 0x4, 254, 1, 957 }, ++{ 0x4, 255, 0, 956 }, ++{ 0x2, 254, 1, 959 }, ++{ 0x2, 255, 0, 958 }, ++{ 0x1, 254, 1, 961 }, ++{ 0x1, 255, 0, 960 }, ++{ 0xa00, 262, 0, 865 }, ++{ 0x2a00, 262, 0, 863 }, ++{ 0x1a00, 262, 0, 864 }, ++{ 0x600, 262, 0, 877 }, ++{ 0x2600, 262, 0, 853 }, ++{ 0xa600, 262, 0, 851 }, ++{ 0x6600, 262, 0, 852 }, ++{ 0x1600, 262, 0, 875 }, ++{ 0xe00, 262, 0, 876 }, ++{ 0x100, 262, 0, 889 }, ++{ 0x500, 262, 0, 887 }, ++{ 0x300, 262, 0, 888 }, ++{ 0x80, 262, 0, 898 }, ++{ 0x280, 262, 0, 896 }, ++{ 0x180, 262, 0, 897 }, ++{ 0x40, 262, 0, 910 }, ++{ 0x140, 262, 0, 908 }, ++{ 0xc0, 262, 0, 909 }, ++{ 0x20, 262, 0, 922 }, ++{ 0xa0, 262, 0, 920 }, ++{ 0x60, 262, 0, 921 }, ++{ 0x10, 262, 0, 934 }, ++{ 0x50, 262, 0, 932 }, ++{ 0x30, 262, 0, 933 }, ++{ 0x8, 262, 0, 946 }, ++{ 0x28, 262, 0, 944 }, ++{ 0x18, 262, 0, 945 }, ++{ 0x4, 262, 0, 962 }, ++{ 0x2, 262, 0, 963 }, ++{ 0x1, 262, 1, 964 }, ++{ 0x1, 263, 0, 850 }, ++{ 0x500, 259, 0, 868 }, ++{ 0x1500, 259, 0, 866 }, ++{ 0xd00, 259, 0, 867 }, ++{ 0x300, 259, 0, 880 }, ++{ 0x1300, 259, 0, 856 }, ++{ 0x5300, 259, 0, 854 }, ++{ 0x3300, 259, 0, 855 }, ++{ 0xb00, 259, 0, 878 }, ++{ 0x700, 259, 0, 879 }, ++{ 0x80, 259, 0, 901 }, ++{ 0x280, 259, 0, 899 }, ++{ 0x180, 259, 0, 900 }, ++{ 0x40, 259, 0, 913 }, ++{ 0x140, 259, 0, 911 }, ++{ 0xc0, 259, 0, 912 }, ++{ 0x20, 259, 0, 925 }, ++{ 0xa0, 259, 0, 923 }, ++{ 0x60, 259, 0, 924 }, ++{ 0x10, 259, 0, 937 }, ++{ 0x50, 259, 0, 935 }, ++{ 0x30, 259, 0, 936 }, ++{ 0x8, 259, 0, 949 }, ++{ 0x28, 259, 0, 947 }, ++{ 0x18, 259, 0, 948 }, ++{ 0x4, 259, 0, 965 }, ++{ 0x2, 259, 0, 966 }, ++{ 0x1, 259, 0, 967 }, ++{ 0x500, 256, 0, 871 }, ++{ 0x1500, 256, 0, 869 }, ++{ 0xd00, 256, 0, 870 }, ++{ 0x300, 256, 0, 883 }, ++{ 0x1300, 256, 0, 859 }, ++{ 0x5300, 256, 0, 857 }, ++{ 0x3300, 256, 0, 858 }, ++{ 0xb00, 256, 0, 881 }, ++{ 0x700, 256, 0, 882 }, ++{ 0x80, 256, 0, 904 }, ++{ 0x280, 256, 0, 902 }, ++{ 0x180, 256, 0, 903 }, ++{ 0x40, 256, 0, 916 }, ++{ 0x140, 256, 0, 914 }, ++{ 0xc0, 256, 0, 915 }, ++{ 0x20, 256, 0, 928 }, ++{ 0xa0, 256, 0, 926 }, ++{ 0x60, 256, 0, 927 }, ++{ 0x10, 256, 0, 940 }, ++{ 0x50, 256, 0, 938 }, ++{ 0x30, 256, 0, 939 }, ++{ 0x8, 256, 0, 952 }, ++{ 0x28, 256, 0, 950 }, ++{ 0x18, 256, 0, 951 }, ++{ 0x4, 256, 0, 968 }, ++{ 0x2, 256, 0, 969 }, ++{ 0x1, 256, 0, 970 }, ++{ 0x500, 251, 0, 874 }, ++{ 0x1500, 251, 0, 872 }, ++{ 0xd00, 251, 0, 873 }, ++{ 0x300, 251, 0, 886 }, ++{ 0x1300, 251, 0, 862 }, ++{ 0x5300, 251, 0, 860 }, ++{ 0x3300, 251, 0, 861 }, ++{ 0xb00, 251, 0, 884 }, ++{ 0x700, 251, 0, 885 }, ++{ 0x80, 251, 0, 907 }, ++{ 0x280, 251, 0, 905 }, ++{ 0x180, 251, 0, 906 }, ++{ 0x40, 251, 0, 919 }, ++{ 0x140, 251, 0, 917 }, ++{ 0xc0, 251, 0, 918 }, ++{ 0x20, 251, 0, 931 }, ++{ 0xa0, 251, 0, 929 }, ++{ 0x60, 251, 0, 930 }, ++{ 0x10, 251, 0, 943 }, ++{ 0x50, 251, 0, 941 }, ++{ 0x30, 251, 0, 942 }, ++{ 0x8, 251, 0, 955 }, ++{ 0x28, 251, 0, 953 }, ++{ 0x18, 251, 0, 954 }, ++{ 0x4, 251, 0, 971 }, ++{ 0x2, 251, 0, 972 }, ++{ 0x1, 251, 0, 973 }, ++{ 0x2, 150, 0, 975 }, ++{ 0x1, 150, 0, 976 }, ++{ 0x1, 50, 0, 977 }, ++{ 0x3, 49, 0, 978 }, ++{ 0x1, 428, 0, 979 }, ++{ 0x1, 438, 0, 980 }, ++{ 0x2, 386, 0, 983 }, ++{ 0x1, 386, 0, 984 }, ++{ 0x2, 384, 0, 985 }, ++{ 0x1, 384, 0, 986 }, ++{ 0x1, 383, 0, 987 }, ++{ 0x1, 328, 0, 992 }, ++{ 0x1, 327, 0, 993 }, ++{ 0x1, 326, 0, 994 }, ++{ 0x1, 325, 0, 995 }, ++{ 0x1, 250, 0, 996 }, ++{ 0x1, 249, 0, 997 }, ++{ 0x1, 324, 0, 998 }, ++{ 0x1, 323, 0, 999 }, ++{ 0x1, 322, 0, 1000 }, ++{ 0x1, 321, 0, 1001 }, ++{ 0x1, 320, 0, 1002 }, ++{ 0x1, 319, 0, 1003 }, ++{ 0x1, 318, 0, 1004 }, ++{ 0x2, 248, 0, 1005 }, ++{ 0x1, 248, 0, 1006 }, ++{ 0x2, 366, 0, 1012 }, ++{ 0x1, 366, 0, 1013 }, ++{ 0x1, 317, 0, 1014 }, ++{ 0x1, 316, 0, 1015 }, ++{ 0x1, 315, 0, 1016 }, ++{ 0x1, 314, 0, 1017 }, ++{ 0x1, 8, 1, 1019 }, ++{ 0x1, 9, 0, 1018 }, ++{ 0x1, 313, 0, 1020 }, ++{ 0x1, 312, 0, 1021 }, ++{ 0x1, 311, 0, 1022 }, ++{ 0x1, 310, 0, 1023 }, ++{ 0x1, 388, 0, 1024 }, ++{ 0x1, 399, 0, 1025 }, ++{ 0x1, 389, 0, 1026 }, ++{ 0x1, 423, 0, 1027 }, ++{ 0x1, 309, 0, 1031 }, ++{ 0x1, 247, 0, 1032 }, ++{ 0x1, 177, 0, 1035 }, ++{ 0x2, 291, 0, 1039 }, ++{ 0x1, 291, 0, 1040 }, ++{ 0x1, 236, 0, 1041 }, ++{ 0x5, 48, 0, 1043 }, ++{ 0x3, 48, 0, 1044 }, ++{ 0x5, 47, 0, 1045 }, ++{ 0x3, 47, 0, 1046 }, ++{ 0x1, 365, 0, 1047 }, ++{ 0x1, 373, 0, 1048 }, ++{ 0x1, 371, 0, 1049 }, ++{ 0x1, 392, 0, 1050 }, ++{ 0x1, 372, 0, 1051 }, ++{ 0x1, 370, 0, 1052 }, ++{ 0x2, 378, 0, 1053 }, ++{ 0x1, 378, 0, 1055 }, ++{ 0x2, 376, 0, 1054 }, ++{ 0x1, 376, 0, 1056 }, ++{ 0x2, 396, 0, 1057 }, ++{ 0x1, 396, 0, 1060 }, ++{ 0x2, 377, 0, 1058 }, ++{ 0x1, 377, 0, 1061 }, ++{ 0x2, 375, 0, 1059 }, ++{ 0x1, 375, 0, 1062 }, ++{ 0x1, 338, 0, 1063 }, ++{ 0x1, 337, 0, 1064 }, ++{ 0x1, 369, 0, 1065 }, ++{ 0x1, 360, 0, 1066 }, ++{ 0x1, 362, 0, 1067 }, ++{ 0x1, 359, 0, 1068 }, ++{ 0x1, 361, 0, 1069 }, ++{ 0x2, 442, 0, 1070 }, ++{ 0x1, 442, 0, 1073 }, ++{ 0x2, 441, 0, 1071 }, ++{ 0x1, 441, 0, 1074 }, ++{ 0x2, 440, 0, 1072 }, ++{ 0x1, 440, 0, 1075 }, ++{ 0x1, 348, 0, 1076 }, ++{ 0x2, 347, 0, 1077 }, ++{ 0x1, 347, 0, 1078 }, ++{ 0x2, 294, 0, 1079 }, ++{ 0x1, 294, 0, 1082 }, ++{ 0x2, 293, 0, 1080 }, ++{ 0x1, 293, 0, 1083 }, ++{ 0x2, 292, 0, 1081 }, ++{ 0x1, 292, 0, 1084 }, ++{ 0x2, 363, 0, 1085 }, ++{ 0x1, 363, 0, 1086 }, ++{ 0x2, 364, 0, 1087 }, ++{ 0x1, 364, 0, 1088 }, ++{ 0xa, 434, 1, 1100 }, ++{ 0xa, 435, 1, 1099 }, ++{ 0xa, 436, 1, 1098 }, ++{ 0xa, 437, 0, 1097 }, ++{ 0x1a, 434, 1, 1092 }, ++{ 0x1a, 435, 1, 1091 }, ++{ 0x32, 436, 1, 1090 }, ++{ 0x32, 437, 0, 1089 }, ++{ 0x6, 434, 1, 1108 }, ++{ 0x6, 435, 1, 1107 }, ++{ 0x6, 436, 1, 1106 }, ++{ 0x6, 437, 0, 1105 }, ++{ 0x1, 434, 1, 1120 }, ++{ 0x1, 435, 1, 1119 }, ++{ 0x1, 436, 1, 1118 }, ++{ 0x1, 437, 0, 1117 }, ++{ 0x9, 434, 1, 1104 }, ++{ 0x9, 435, 1, 1103 }, ++{ 0x9, 436, 1, 1102 }, ++{ 0x9, 437, 0, 1101 }, ++{ 0x19, 434, 1, 1096 }, ++{ 0x19, 435, 1, 1095 }, ++{ 0x31, 436, 1, 1094 }, ++{ 0x31, 437, 0, 1093 }, ++{ 0x5, 434, 1, 1112 }, ++{ 0x5, 435, 1, 1111 }, ++{ 0x5, 436, 1, 1110 }, ++{ 0x5, 437, 0, 1109 }, ++{ 0x3, 434, 1, 1116 }, ++{ 0x3, 435, 1, 1115 }, ++{ 0x3, 436, 1, 1114 }, ++{ 0x3, 437, 0, 1113 }, ++{ 0xa, 429, 1, 1132 }, ++{ 0xa, 430, 1, 1131 }, ++{ 0xa, 431, 1, 1130 }, ++{ 0xa, 432, 0, 1129 }, ++{ 0x1a, 429, 1, 1124 }, ++{ 0x1a, 430, 1, 1123 }, ++{ 0x32, 431, 1, 1122 }, ++{ 0x32, 432, 0, 1121 }, ++{ 0x6, 429, 1, 1140 }, ++{ 0x6, 430, 1, 1139 }, ++{ 0x6, 431, 1, 1138 }, ++{ 0x6, 432, 0, 1137 }, ++{ 0x1, 429, 1, 1152 }, ++{ 0x1, 430, 1, 1151 }, ++{ 0x1, 431, 1, 1150 }, ++{ 0x1, 432, 0, 1149 }, ++{ 0x9, 429, 1, 1136 }, ++{ 0x9, 430, 1, 1135 }, ++{ 0x9, 431, 1, 1134 }, ++{ 0x9, 432, 0, 1133 }, ++{ 0x19, 429, 1, 1128 }, ++{ 0x19, 430, 1, 1127 }, ++{ 0x31, 431, 1, 1126 }, ++{ 0x31, 432, 0, 1125 }, ++{ 0x5, 429, 1, 1144 }, ++{ 0x5, 430, 1, 1143 }, ++{ 0x5, 431, 1, 1142 }, ++{ 0x5, 432, 0, 1141 }, ++{ 0x3, 429, 1, 1148 }, ++{ 0x3, 430, 1, 1147 }, ++{ 0x3, 431, 1, 1146 }, ++{ 0x3, 432, 0, 1145 }, ++{ 0x1, 139, 0, 1153 }, ++{ 0x1, 138, 0, 1154 }, ++{ 0x1, 391, 1, 1156 }, ++{ 0x1, 137, 0, 1155 }, ++{ 0x2, 395, 1, 1158 }, ++{ 0x2, 141, 0, 1157 }, ++{ 0x1, 395, 1, 1160 }, ++{ 0x1, 141, 0, 1159 }, ++{ 0x1, 397, 0, 1161 }, ++{ 0x1, 136, 0, 1162 }, ++{ 0x2, 135, 0, 1163 }, ++{ 0x2, 134, 0, 1164 }, ++{ 0x1, 454, 1, 1170 }, ++{ 0x1, 246, 0, 1033 }, ++{ 0x1, 453, 0, 1171 }, ++{ 0x1, 452, 1, 1172 }, ++{ 0x1, 245, 0, 1042 }, ++{ 0x1, 308, 0, 1173 }, ++{ 0x1, 307, 1, 1174 }, ++{ 0x1, 290, 0, 1034 }, ++{ 0x1, 306, 0, 1175 }, ++{ 0x1, 305, 1, 1176 }, ++{ 0x1, 427, 0, 1036 }, ++{ 0x1, 304, 1, 1177 }, ++{ 0x1, 398, 0, 1038 }, ++{ 0x1, 303, 0, 1178 }, ++{ 0x1, 302, 0, 1179 }, ++{ 0x1, 301, 0, 1180 }, ++{ 0x1, 300, 1, 1181 }, ++{ 0x2, 398, 0, 1037 }, ++{ 0x10, 299, 0, 1185 }, ++{ 0x90, 299, 0, 1183 }, ++{ 0x190, 299, 0, 1182 }, ++{ 0x50, 299, 0, 1184 }, ++{ 0x30, 299, 0, 1187 }, ++{ 0x70, 299, 0, 1186 }, ++{ 0x8, 299, 0, 1189 }, ++{ 0x18, 299, 0, 1188 }, ++{ 0x4, 299, 0, 1190 }, ++{ 0x1, 299, 0, 1193 }, ++{ 0x3, 299, 0, 1192 }, ++{ 0x1, 298, 1, 1194 }, ++{ 0x2, 299, 0, 1191 }, ++{ 0x3, 46, 0, 1195 }, ++{ 0x1, 241, 1, 1196 }, ++{ 0x1, 242, 1, 1028 }, ++{ 0x1, 243, 0, 88 }, ++{ 0x1, 341, 1, 1197 }, ++{ 0x1, 342, 1, 1029 }, ++{ 0x1, 343, 0, 89 }, ++{ 0x1, 34, 1, 1198 }, ++{ 0x1, 35, 1, 1030 }, ++{ 0x1, 36, 0, 90 }, ++{ 0x1, 230, 0, 1199 }, ++{ 0x4, 447, 0, 1200 }, ++{ 0x2, 447, 0, 1201 }, ++{ 0x1, 447, 1, 1203 }, ++{ 0x1, 448, 0, 1202 }, ++{ 0x8, 449, 0, 1204 }, ++{ 0x4, 449, 0, 1205 }, ++{ 0x1, 449, 1, 1207 }, ++{ 0x2, 449, 0, 1206 }, ++{ 0x8, 219, 0, 1208 }, ++{ 0x4, 219, 0, 1209 }, ++{ 0x2, 219, 0, 1210 }, ++{ 0x1, 219, 1, 1212 }, ++{ 0x1, 220, 0, 1211 }, ++{ 0x10, 221, 0, 1213 }, ++{ 0x8, 221, 0, 1214 }, ++{ 0x4, 221, 0, 1215 }, ++{ 0x1, 221, 1, 1217 }, ++{ 0x2, 221, 0, 1216 }, ++{ 0x220, 191, 0, 1218 }, ++{ 0x120, 191, 0, 1219 }, ++{ 0xa0, 191, 0, 1220 }, ++{ 0x60, 191, 1, 1222 }, ++{ 0x4, 192, 0, 1221 }, ++{ 0x110, 191, 0, 1228 }, ++{ 0x90, 191, 0, 1229 }, ++{ 0x50, 191, 0, 1230 }, ++{ 0x30, 191, 1, 1232 }, ++{ 0x2, 192, 0, 1231 }, ++{ 0x8, 191, 0, 1233 }, ++{ 0x4, 191, 0, 1234 }, ++{ 0x2, 191, 0, 1235 }, ++{ 0x1, 191, 1, 1237 }, ++{ 0x1, 192, 0, 1236 }, ++{ 0x440, 193, 0, 1223 }, ++{ 0x240, 193, 0, 1224 }, ++{ 0x140, 193, 0, 1225 }, ++{ 0xc0, 193, 1, 1227 }, ++{ 0x40, 193, 0, 1226 }, ++{ 0x220, 193, 0, 1238 }, ++{ 0x120, 193, 0, 1239 }, ++{ 0xa0, 193, 0, 1240 }, ++{ 0x60, 193, 1, 1242 }, ++{ 0x20, 193, 0, 1241 }, ++{ 0x10, 193, 0, 1243 }, ++{ 0x8, 193, 0, 1244 }, ++{ 0x4, 193, 0, 1245 }, ++{ 0x1, 193, 1, 1247 }, ++{ 0x2, 193, 0, 1246 }, ++{ 0x8, 215, 0, 1248 }, ++{ 0x4, 215, 0, 1249 }, ++{ 0x2, 215, 0, 1250 }, ++{ 0x1, 215, 1, 1252 }, ++{ 0x1, 216, 0, 1251 }, ++{ 0x220, 187, 0, 1253 }, ++{ 0x120, 187, 0, 1254 }, ++{ 0xa0, 187, 0, 1255 }, ++{ 0x60, 187, 1, 1257 }, ++{ 0x4, 188, 0, 1256 }, ++{ 0x110, 187, 0, 1263 }, ++{ 0x90, 187, 0, 1264 }, ++{ 0x50, 187, 0, 1265 }, ++{ 0x30, 187, 1, 1267 }, ++{ 0x2, 188, 0, 1266 }, ++{ 0x8, 187, 0, 1268 }, ++{ 0x4, 187, 0, 1269 }, ++{ 0x2, 187, 0, 1270 }, ++{ 0x1, 187, 1, 1272 }, ++{ 0x1, 188, 0, 1271 }, ++{ 0x440, 233, 0, 1258 }, ++{ 0x240, 233, 0, 1259 }, ++{ 0x140, 233, 0, 1260 }, ++{ 0xc0, 233, 1, 1262 }, ++{ 0x40, 233, 0, 1261 }, ++{ 0x220, 233, 0, 1273 }, ++{ 0x120, 233, 0, 1274 }, ++{ 0xa0, 233, 0, 1275 }, ++{ 0x60, 233, 1, 1277 }, ++{ 0x20, 233, 0, 1276 }, ++{ 0x10, 233, 0, 1278 }, ++{ 0x8, 233, 0, 1279 }, ++{ 0x4, 233, 0, 1280 }, ++{ 0x1, 233, 1, 1282 }, ++{ 0x2, 233, 0, 1281 }, ++{ 0x8, 207, 0, 1283 }, ++{ 0x4, 207, 0, 1284 }, ++{ 0x2, 207, 0, 1285 }, ++{ 0x1, 207, 1, 1287 }, ++{ 0x1, 208, 0, 1286 }, ++{ 0x10, 214, 0, 1288 }, ++{ 0x8, 214, 0, 1289 }, ++{ 0x4, 214, 0, 1290 }, ++{ 0x1, 214, 1, 1292 }, ++{ 0x2, 214, 0, 1291 }, ++{ 0x220, 178, 0, 1293 }, ++{ 0x120, 178, 0, 1294 }, ++{ 0xa0, 178, 0, 1295 }, ++{ 0x60, 178, 1, 1297 }, ++{ 0x4, 179, 0, 1296 }, ++{ 0x110, 178, 0, 1318 }, ++{ 0x90, 178, 0, 1319 }, ++{ 0x50, 178, 0, 1320 }, ++{ 0x30, 178, 1, 1322 }, ++{ 0x2, 179, 0, 1321 }, ++{ 0x8, 178, 0, 1323 }, ++{ 0x4, 178, 0, 1324 }, ++{ 0x2, 178, 0, 1325 }, ++{ 0x1, 178, 1, 1327 }, ++{ 0x1, 179, 0, 1326 }, ++{ 0x440, 186, 0, 1298 }, ++{ 0x240, 186, 0, 1299 }, ++{ 0x140, 186, 0, 1300 }, ++{ 0xc0, 186, 1, 1302 }, ++{ 0x40, 186, 0, 1301 }, ++{ 0x220, 186, 0, 1328 }, ++{ 0x120, 186, 0, 1329 }, ++{ 0xa0, 186, 0, 1330 }, ++{ 0x60, 186, 1, 1332 }, ++{ 0x20, 186, 0, 1331 }, ++{ 0x10, 186, 0, 1333 }, ++{ 0x8, 186, 0, 1334 }, ++{ 0x4, 186, 0, 1335 }, ++{ 0x1, 186, 1, 1337 }, ++{ 0x2, 186, 0, 1336 }, ++{ 0x440, 143, 0, 1303 }, ++{ 0x240, 143, 0, 1304 }, ++{ 0x140, 143, 0, 1305 }, ++{ 0xc0, 143, 1, 1307 }, ++{ 0x40, 143, 0, 1306 }, ++{ 0x220, 143, 0, 1338 }, ++{ 0x120, 143, 0, 1339 }, ++{ 0xa0, 143, 0, 1340 }, ++{ 0x60, 143, 1, 1342 }, ++{ 0x20, 143, 0, 1341 }, ++{ 0x10, 143, 0, 1343 }, ++{ 0x8, 143, 0, 1344 }, ++{ 0x1, 143, 1, 1347 }, ++{ 0x2, 143, 0, 1346 }, ++{ 0x440, 194, 1, 1313 }, ++{ 0x441, 174, 0, 1308 }, ++{ 0x240, 194, 1, 1314 }, ++{ 0x241, 174, 0, 1309 }, ++{ 0x140, 194, 1, 1315 }, ++{ 0x141, 174, 0, 1310 }, ++{ 0xc0, 194, 1, 1317 }, ++{ 0x40, 194, 1, 1316 }, ++{ 0xc1, 174, 1, 1312 }, ++{ 0x41, 174, 0, 1311 }, ++{ 0x220, 194, 1, 1358 }, ++{ 0x221, 174, 0, 1348 }, ++{ 0x120, 194, 1, 1359 }, ++{ 0x121, 174, 0, 1349 }, ++{ 0xa0, 194, 1, 1360 }, ++{ 0xa1, 174, 0, 1350 }, ++{ 0x60, 194, 1, 1362 }, ++{ 0x20, 194, 1, 1361 }, ++{ 0x61, 174, 1, 1352 }, ++{ 0x21, 174, 0, 1351 }, ++{ 0x10, 194, 1, 1363 }, ++{ 0x11, 174, 0, 1353 }, ++{ 0x8, 194, 1, 1364 }, ++{ 0x9, 174, 0, 1354 }, ++{ 0x4, 194, 1, 1365 }, ++{ 0x5, 174, 0, 1355 }, ++{ 0x1, 194, 1, 1367 }, ++{ 0x2, 194, 1, 1366 }, ++{ 0x3, 174, 1, 1357 }, ++{ 0x1, 174, 0, 1356 }, ++{ 0x1, 153, 1, 1375 }, ++{ 0x1, 154, 1, 1374 }, ++{ 0x1, 155, 1, 1373 }, ++{ 0x1, 156, 0, 1372 }, ++{ 0x3, 153, 1, 1371 }, ++{ 0x3, 154, 1, 1370 }, ++{ 0x3, 155, 1, 1369 }, ++{ 0x3, 156, 0, 1368 }, ++{ 0x1108, 159, 1, 1537 }, ++{ 0x1108, 160, 1, 1536 }, ++{ 0x1108, 165, 1, 1377 }, ++{ 0x1108, 166, 0, 1376 }, ++{ 0x908, 159, 1, 1539 }, ++{ 0x908, 160, 1, 1538 }, ++{ 0x908, 165, 1, 1379 }, ++{ 0x908, 166, 0, 1378 }, ++{ 0x508, 159, 1, 1541 }, ++{ 0x508, 160, 1, 1540 }, ++{ 0x508, 165, 1, 1381 }, ++{ 0x508, 166, 0, 1380 }, ++{ 0x308, 159, 1, 1545 }, ++{ 0x308, 160, 1, 1544 }, ++{ 0x108, 160, 1, 1542 }, ++{ 0x18, 161, 1, 1543 }, ++{ 0x308, 165, 1, 1385 }, ++{ 0x308, 166, 1, 1384 }, ++{ 0x108, 166, 1, 1382 }, ++{ 0x18, 167, 0, 1383 }, ++{ 0x88, 159, 1, 1577 }, ++{ 0x88, 160, 1, 1576 }, ++{ 0x88, 165, 1, 1457 }, ++{ 0x88, 166, 0, 1456 }, ++{ 0x48, 159, 1, 1579 }, ++{ 0x48, 160, 1, 1578 }, ++{ 0x48, 165, 1, 1459 }, ++{ 0x48, 166, 0, 1458 }, ++{ 0x28, 159, 1, 1581 }, ++{ 0x28, 160, 1, 1580 }, ++{ 0x28, 165, 1, 1461 }, ++{ 0x28, 166, 0, 1460 }, ++{ 0x18, 159, 1, 1585 }, ++{ 0x18, 160, 1, 1584 }, ++{ 0x8, 160, 1, 1582 }, ++{ 0x8, 161, 1, 1583 }, ++{ 0x18, 165, 1, 1465 }, ++{ 0x18, 166, 1, 1464 }, ++{ 0x8, 166, 1, 1462 }, ++{ 0x8, 167, 0, 1463 }, ++{ 0x884, 159, 1, 1547 }, ++{ 0x884, 160, 1, 1546 }, ++{ 0x442, 162, 1, 1437 }, ++{ 0x442, 163, 1, 1436 }, ++{ 0x884, 165, 1, 1407 }, ++{ 0x884, 166, 1, 1406 }, ++{ 0x442, 168, 1, 1387 }, ++{ 0x442, 169, 0, 1386 }, ++{ 0x484, 159, 1, 1549 }, ++{ 0x484, 160, 1, 1548 }, ++{ 0x242, 162, 1, 1439 }, ++{ 0x242, 163, 1, 1438 }, ++{ 0x484, 165, 1, 1409 }, ++{ 0x484, 166, 1, 1408 }, ++{ 0x242, 168, 1, 1389 }, ++{ 0x242, 169, 0, 1388 }, ++{ 0x284, 159, 1, 1551 }, ++{ 0x284, 160, 1, 1550 }, ++{ 0x142, 162, 1, 1441 }, ++{ 0x142, 163, 1, 1440 }, ++{ 0x284, 165, 1, 1411 }, ++{ 0x284, 166, 1, 1410 }, ++{ 0x142, 168, 1, 1391 }, ++{ 0x142, 169, 0, 1390 }, ++{ 0x184, 159, 1, 1555 }, ++{ 0x184, 160, 1, 1554 }, ++{ 0x84, 160, 1, 1552 }, ++{ 0xc, 161, 1, 1553 }, ++{ 0xc2, 162, 1, 1445 }, ++{ 0xc2, 163, 1, 1444 }, ++{ 0x42, 163, 1, 1442 }, ++{ 0x6, 164, 1, 1443 }, ++{ 0x184, 165, 1, 1415 }, ++{ 0x184, 166, 1, 1414 }, ++{ 0x84, 166, 1, 1412 }, ++{ 0xc, 167, 1, 1413 }, ++{ 0xc2, 168, 1, 1395 }, ++{ 0xc2, 169, 1, 1394 }, ++{ 0x42, 169, 1, 1392 }, ++{ 0x6, 170, 0, 1393 }, ++{ 0x44, 159, 1, 1587 }, ++{ 0x44, 160, 1, 1586 }, ++{ 0x22, 162, 1, 1517 }, ++{ 0x22, 163, 1, 1516 }, ++{ 0x44, 165, 1, 1487 }, ++{ 0x44, 166, 1, 1486 }, ++{ 0x22, 168, 1, 1467 }, ++{ 0x22, 169, 0, 1466 }, ++{ 0x24, 159, 1, 1589 }, ++{ 0x24, 160, 1, 1588 }, ++{ 0x12, 162, 1, 1519 }, ++{ 0x12, 163, 1, 1518 }, ++{ 0x24, 165, 1, 1489 }, ++{ 0x24, 166, 1, 1488 }, ++{ 0x12, 168, 1, 1469 }, ++{ 0x12, 169, 0, 1468 }, ++{ 0x14, 159, 1, 1591 }, ++{ 0x14, 160, 1, 1590 }, ++{ 0xa, 162, 1, 1521 }, ++{ 0xa, 163, 1, 1520 }, ++{ 0x14, 165, 1, 1491 }, ++{ 0x14, 166, 1, 1490 }, ++{ 0xa, 168, 1, 1471 }, ++{ 0xa, 169, 0, 1470 }, ++{ 0xc, 159, 1, 1595 }, ++{ 0xc, 160, 1, 1594 }, ++{ 0x4, 160, 1, 1592 }, ++{ 0x4, 161, 1, 1593 }, ++{ 0x6, 162, 1, 1525 }, ++{ 0x6, 163, 1, 1524 }, ++{ 0x2, 163, 1, 1522 }, ++{ 0x2, 164, 1, 1523 }, ++{ 0xc, 165, 1, 1495 }, ++{ 0xc, 166, 1, 1494 }, ++{ 0x4, 166, 1, 1492 }, ++{ 0x4, 167, 1, 1493 }, ++{ 0x6, 168, 1, 1475 }, ++{ 0x6, 169, 1, 1474 }, ++{ 0x2, 169, 1, 1472 }, ++{ 0x2, 170, 0, 1473 }, ++{ 0x442, 159, 1, 1557 }, ++{ 0x442, 160, 1, 1556 }, ++{ 0x221, 162, 1, 1447 }, ++{ 0x221, 163, 1, 1446 }, ++{ 0x442, 165, 1, 1417 }, ++{ 0x442, 166, 1, 1416 }, ++{ 0x221, 168, 1, 1397 }, ++{ 0x221, 169, 0, 1396 }, ++{ 0x242, 159, 1, 1559 }, ++{ 0x242, 160, 1, 1558 }, ++{ 0x121, 162, 1, 1449 }, ++{ 0x121, 163, 1, 1448 }, ++{ 0x242, 165, 1, 1419 }, ++{ 0x242, 166, 1, 1418 }, ++{ 0x121, 168, 1, 1399 }, ++{ 0x121, 169, 0, 1398 }, ++{ 0x142, 159, 1, 1561 }, ++{ 0x142, 160, 1, 1560 }, ++{ 0xa1, 162, 1, 1451 }, ++{ 0xa1, 163, 1, 1450 }, ++{ 0x142, 165, 1, 1421 }, ++{ 0x142, 166, 1, 1420 }, ++{ 0xa1, 168, 1, 1401 }, ++{ 0xa1, 169, 0, 1400 }, ++{ 0xc2, 159, 1, 1565 }, ++{ 0xc2, 160, 1, 1564 }, ++{ 0x42, 160, 1, 1562 }, ++{ 0x6, 161, 1, 1563 }, ++{ 0x61, 162, 1, 1455 }, ++{ 0x61, 163, 1, 1454 }, ++{ 0x21, 163, 1, 1452 }, ++{ 0x3, 164, 1, 1453 }, ++{ 0xc2, 165, 1, 1425 }, ++{ 0xc2, 166, 1, 1424 }, ++{ 0x42, 166, 1, 1422 }, ++{ 0x6, 167, 1, 1423 }, ++{ 0x61, 168, 1, 1405 }, ++{ 0x61, 169, 1, 1404 }, ++{ 0x21, 169, 1, 1402 }, ++{ 0x3, 170, 0, 1403 }, ++{ 0x22, 159, 1, 1597 }, ++{ 0x22, 160, 1, 1596 }, ++{ 0x11, 162, 1, 1527 }, ++{ 0x11, 163, 1, 1526 }, ++{ 0x22, 165, 1, 1497 }, ++{ 0x22, 166, 1, 1496 }, ++{ 0x11, 168, 1, 1477 }, ++{ 0x11, 169, 0, 1476 }, ++{ 0x12, 159, 1, 1599 }, ++{ 0x12, 160, 1, 1598 }, ++{ 0x9, 162, 1, 1529 }, ++{ 0x9, 163, 1, 1528 }, ++{ 0x12, 165, 1, 1499 }, ++{ 0x12, 166, 1, 1498 }, ++{ 0x9, 168, 1, 1479 }, ++{ 0x9, 169, 0, 1478 }, ++{ 0xa, 159, 1, 1601 }, ++{ 0xa, 160, 1, 1600 }, ++{ 0x5, 162, 1, 1531 }, ++{ 0x5, 163, 1, 1530 }, ++{ 0xa, 165, 1, 1501 }, ++{ 0xa, 166, 1, 1500 }, ++{ 0x5, 168, 1, 1481 }, ++{ 0x5, 169, 0, 1480 }, ++{ 0x6, 159, 1, 1605 }, ++{ 0x6, 160, 1, 1604 }, ++{ 0x2, 160, 1, 1602 }, ++{ 0x2, 161, 1, 1603 }, ++{ 0x3, 162, 1, 1535 }, ++{ 0x3, 163, 1, 1534 }, ++{ 0x1, 163, 1, 1532 }, ++{ 0x1, 164, 1, 1533 }, ++{ 0x6, 165, 1, 1505 }, ++{ 0x6, 166, 1, 1504 }, ++{ 0x2, 166, 1, 1502 }, ++{ 0x2, 167, 1, 1503 }, ++{ 0x3, 168, 1, 1485 }, ++{ 0x3, 169, 1, 1484 }, ++{ 0x1, 169, 1, 1482 }, ++{ 0x1, 170, 0, 1483 }, ++{ 0x221, 159, 1, 1567 }, ++{ 0x221, 160, 1, 1566 }, ++{ 0x221, 165, 1, 1427 }, ++{ 0x221, 166, 0, 1426 }, ++{ 0x121, 159, 1, 1569 }, ++{ 0x121, 160, 1, 1568 }, ++{ 0x121, 165, 1, 1429 }, ++{ 0x121, 166, 0, 1428 }, ++{ 0xa1, 159, 1, 1571 }, ++{ 0xa1, 160, 1, 1570 }, ++{ 0xa1, 165, 1, 1431 }, ++{ 0xa1, 166, 0, 1430 }, ++{ 0x61, 159, 1, 1575 }, ++{ 0x61, 160, 1, 1574 }, ++{ 0x21, 160, 1, 1572 }, ++{ 0x3, 161, 1, 1573 }, ++{ 0x61, 165, 1, 1435 }, ++{ 0x61, 166, 1, 1434 }, ++{ 0x21, 166, 1, 1432 }, ++{ 0x3, 167, 0, 1433 }, ++{ 0x11, 159, 1, 1607 }, ++{ 0x11, 160, 1, 1606 }, ++{ 0x11, 165, 1, 1507 }, ++{ 0x11, 166, 0, 1506 }, ++{ 0x9, 159, 1, 1609 }, ++{ 0x9, 160, 1, 1608 }, ++{ 0x9, 165, 1, 1509 }, ++{ 0x9, 166, 0, 1508 }, ++{ 0x5, 159, 1, 1611 }, ++{ 0x5, 160, 1, 1610 }, ++{ 0x5, 165, 1, 1511 }, ++{ 0x5, 166, 0, 1510 }, ++{ 0x3, 159, 1, 1615 }, ++{ 0x3, 160, 1, 1614 }, ++{ 0x1, 160, 1, 1612 }, ++{ 0x1, 161, 1, 1613 }, ++{ 0x3, 165, 1, 1515 }, ++{ 0x3, 166, 1, 1514 }, ++{ 0x1, 166, 1, 1512 }, ++{ 0x1, 167, 0, 1513 }, ++{ 0x442, 205, 0, 1616 }, ++{ 0x242, 205, 0, 1617 }, ++{ 0x142, 205, 0, 1618 }, ++{ 0xc2, 205, 1, 1620 }, ++{ 0x6, 206, 1, 1619 }, ++{ 0x1, 439, 0, 981 }, ++{ 0x22, 205, 0, 1626 }, ++{ 0x12, 205, 0, 1627 }, ++{ 0xa, 205, 0, 1628 }, ++{ 0x6, 205, 1, 1630 }, ++{ 0x2, 206, 1, 1629 }, ++{ 0x2, 367, 0, 1010 }, ++{ 0x221, 205, 0, 1621 }, ++{ 0x121, 205, 0, 1622 }, ++{ 0xa1, 205, 0, 1623 }, ++{ 0x61, 205, 1, 1625 }, ++{ 0x3, 206, 1, 1624 }, ++{ 0x1, 433, 0, 982 }, ++{ 0x11, 205, 0, 1631 }, ++{ 0x9, 205, 0, 1632 }, ++{ 0x5, 205, 0, 1633 }, ++{ 0x3, 205, 1, 1635 }, ++{ 0x1, 206, 1, 1634 }, ++{ 0x1, 367, 0, 1011 }, ++{ 0x4, 211, 0, 1636 }, ++{ 0x1, 211, 0, 1638 }, ++{ 0x1, 218, 0, 1639 }, ++{ 0x1, 217, 1, 1640 }, ++{ 0x2, 211, 0, 1637 }, ++{ 0x1, 196, 0, 1641 }, ++{ 0x880, 202, 0, 1642 }, ++{ 0x480, 202, 0, 1643 }, ++{ 0x280, 202, 0, 1644 }, ++{ 0x180, 202, 1, 1646 }, ++{ 0x80, 203, 0, 1645 }, ++{ 0x440, 202, 1, 1657 }, ++{ 0x88, 204, 0, 1647 }, ++{ 0x240, 202, 1, 1658 }, ++{ 0x48, 204, 0, 1648 }, ++{ 0x140, 202, 1, 1659 }, ++{ 0x28, 204, 0, 1649 }, ++{ 0xc0, 202, 1, 1661 }, ++{ 0x40, 203, 1, 1660 }, ++{ 0x18, 204, 1, 1651 }, ++{ 0x8, 204, 0, 1650 }, ++{ 0x220, 202, 1, 1662 }, ++{ 0x44, 204, 0, 1652 }, ++{ 0x120, 202, 1, 1663 }, ++{ 0x24, 204, 0, 1653 }, ++{ 0xa0, 202, 1, 1664 }, ++{ 0x14, 204, 0, 1654 }, ++{ 0x60, 202, 1, 1666 }, ++{ 0x20, 203, 1, 1665 }, ++{ 0xc, 204, 1, 1656 }, ++{ 0x4, 204, 0, 1655 }, ++{ 0x110, 202, 0, 1667 }, ++{ 0x90, 202, 0, 1668 }, ++{ 0x50, 202, 0, 1669 }, ++{ 0x30, 202, 1, 1671 }, ++{ 0x10, 203, 1, 1670 }, ++{ 0x1, 385, 0, 974 }, ++{ 0x88, 202, 0, 1672 }, ++{ 0x48, 202, 0, 1673 }, ++{ 0x28, 202, 0, 1674 }, ++{ 0x18, 202, 1, 1676 }, ++{ 0x8, 203, 1, 1675 }, ++{ 0xc, 368, 0, 1007 }, ++{ 0x44, 202, 1, 1687 }, ++{ 0x22, 204, 0, 1677 }, ++{ 0x24, 202, 1, 1688 }, ++{ 0x12, 204, 0, 1678 }, ++{ 0x14, 202, 1, 1689 }, ++{ 0xa, 204, 0, 1679 }, ++{ 0xc, 202, 1, 1691 }, ++{ 0x4, 203, 1, 1690 }, ++{ 0x6, 204, 1, 1681 }, ++{ 0x2, 204, 1, 1680 }, ++{ 0x6, 368, 0, 1008 }, ++{ 0x22, 202, 1, 1692 }, ++{ 0x11, 204, 0, 1682 }, ++{ 0x12, 202, 1, 1693 }, ++{ 0x9, 204, 0, 1683 }, ++{ 0xa, 202, 1, 1694 }, ++{ 0x5, 204, 0, 1684 }, ++{ 0x6, 202, 1, 1696 }, ++{ 0x2, 203, 1, 1695 }, ++{ 0x3, 204, 1, 1686 }, ++{ 0x1, 204, 1, 1685 }, ++{ 0x3, 368, 0, 1009 }, ++{ 0x11, 202, 0, 1697 }, ++{ 0x9, 202, 0, 1698 }, ++{ 0x5, 202, 0, 1699 }, ++{ 0x3, 202, 1, 1701 }, ++{ 0x1, 203, 0, 1700 }, ++{ 0x8, 198, 0, 1702 }, ++{ 0x4, 198, 0, 1703 }, ++{ 0x2, 198, 0, 1704 }, ++{ 0x1, 198, 1, 1706 }, ++{ 0x1, 199, 1, 1705 }, ++{ 0x1, 332, 0, 988 }, ++{ 0x8, 200, 0, 1707 }, ++{ 0x4, 200, 0, 1708 }, ++{ 0x2, 200, 0, 1709 }, ++{ 0x1, 200, 1, 1711 }, ++{ 0x1, 201, 1, 1710 }, ++{ 0x1, 331, 0, 989 }, ++{ 0x8, 209, 0, 1712 }, ++{ 0x4, 209, 0, 1713 }, ++{ 0x2, 209, 0, 1714 }, ++{ 0x1, 209, 1, 1716 }, ++{ 0x1, 210, 1, 1715 }, ++{ 0x1, 330, 0, 990 }, ++{ 0x8, 212, 0, 1717 }, ++{ 0x4, 212, 0, 1718 }, ++{ 0x2, 212, 0, 1719 }, ++{ 0x1, 212, 1, 1721 }, ++{ 0x1, 213, 1, 1720 }, ++{ 0x1, 329, 0, 991 }, ++{ 0x8, 224, 0, 1722 }, ++{ 0x4, 224, 0, 1723 }, ++{ 0x2, 224, 0, 1724 }, ++{ 0x1, 224, 1, 1726 }, ++{ 0x1, 225, 0, 1725 }, ++{ 0x8, 222, 0, 1727 }, ++{ 0x4, 222, 0, 1728 }, ++{ 0x2, 222, 0, 1729 }, ++{ 0x1, 222, 1, 1731 }, ++{ 0x1, 223, 0, 1730 }, ++{ 0x1, 240, 0, 1732 }, ++{ 0x1, 340, 0, 1733 }, ++{ 0x1, 33, 0, 1734 }, ++{ 0x8, 151, 0, 1735 }, ++{ 0x4, 151, 0, 1736 }, ++{ 0x2, 151, 0, 1737 }, ++{ 0x1, 151, 1, 1739 }, ++{ 0x1, 152, 0, 1738 }, ++{ 0x8, 157, 0, 1740 }, ++{ 0x4, 157, 0, 1741 }, ++{ 0x2, 157, 0, 1742 }, ++{ 0x1, 157, 1, 1744 }, ++{ 0x1, 158, 0, 1743 }, ++{ 0x8, 231, 0, 1745 }, ++{ 0x4, 231, 0, 1746 }, ++{ 0x2, 231, 0, 1747 }, ++{ 0x1, 231, 1, 1749 }, ++{ 0x1, 232, 0, 1748 }, ++{ 0x1, 173, 0, 1750 }, ++{ 0x442, 171, 0, 1751 }, ++{ 0x242, 171, 0, 1752 }, ++{ 0x142, 171, 0, 1753 }, ++{ 0xc2, 171, 1, 1755 }, ++{ 0x6, 172, 0, 1754 }, ++{ 0x22, 171, 0, 1761 }, ++{ 0x12, 171, 0, 1762 }, ++{ 0xa, 171, 0, 1763 }, ++{ 0x6, 171, 1, 1765 }, ++{ 0x2, 172, 1, 1764 }, ++{ 0x1, 135, 0, 1165 }, ++{ 0x221, 171, 0, 1756 }, ++{ 0x121, 171, 0, 1757 }, ++{ 0xa1, 171, 0, 1758 }, ++{ 0x61, 171, 1, 1760 }, ++{ 0x3, 172, 0, 1759 }, ++{ 0x11, 171, 0, 1766 }, ++{ 0x9, 171, 0, 1767 }, ++{ 0x5, 171, 0, 1768 }, ++{ 0x3, 171, 1, 1770 }, ++{ 0x1, 172, 1, 1769 }, ++{ 0x1, 134, 0, 1166 }, ++{ 0x1, 237, 0, 1771 }, ++{ 0x1, 195, 0, 1772 }, ++{ 0x1, 149, 0, 1773 }, ++{ 0x1, 148, 0, 1774 }, ++{ 0x4, 234, 0, 1775 }, ++{ 0x2, 234, 0, 1776 }, ++{ 0x1, 234, 0, 1777 }, ++{ 0x1, 197, 0, 1778 }, ++{ 0x2, 235, 0, 1779 }, ++{ 0x1, 235, 0, 1780 }, ++{ 0x4, 185, 0, 1781 }, ++{ 0x2, 185, 0, 1782 }, ++{ 0x1, 185, 0, 1783 }, ++{ 0x4, 182, 0, 1784 }, ++{ 0x1, 190, 0, 1787 }, ++{ 0x1, 189, 1, 1788 }, ++{ 0x2, 182, 0, 1785 }, ++{ 0x1, 142, 0, 1789 }, ++{ 0x1, 297, 1, 1790 }, ++{ 0x1, 182, 0, 1786 }, ++{ 0x8, 144, 0, 1791 }, ++{ 0x4, 144, 0, 1792 }, ++{ 0x2, 144, 0, 1793 }, ++{ 0x1, 144, 1, 1795 }, ++{ 0x1, 145, 0, 1794 }, ++{ 0x8, 146, 0, 1796 }, ++{ 0x4, 146, 0, 1797 }, ++{ 0x2, 146, 0, 1798 }, ++{ 0x1, 146, 1, 1800 }, ++{ 0x1, 147, 1, 1799 }, ++{ 0x1, 426, 0, 1167 }, ++{ 0x8, 180, 0, 1801 }, ++{ 0x4, 180, 0, 1802 }, ++{ 0x2, 180, 0, 1803 }, ++{ 0x1, 180, 1, 1805 }, ++{ 0x1, 181, 1, 1804 }, ++{ 0x1, 425, 0, 1168 }, ++{ 0x8, 183, 0, 1806 }, ++{ 0x4, 183, 0, 1807 }, ++{ 0x2, 183, 0, 1808 }, ++{ 0x1, 183, 1, 1810 }, ++{ 0x1, 184, 1, 1809 }, ++{ 0x1, 424, 0, 1169 }, ++{ 0x8, 228, 0, 1811 }, ++{ 0x4, 228, 0, 1812 }, ++{ 0x2, 228, 0, 1813 }, ++{ 0x1, 228, 1, 1815 }, ++{ 0x1, 229, 0, 1814 }, ++{ 0x8, 226, 0, 1816 }, ++{ 0x4, 226, 0, 1817 }, ++{ 0x2, 226, 0, 1818 }, ++{ 0x1, 226, 1, 1820 }, ++{ 0x1, 227, 0, 1819 }, ++{ 0x8, 44, 0, 1825 }, ++{ 0x18, 44, 0, 1821 }, ++{ 0x4, 44, 0, 1826 }, ++{ 0xc, 44, 0, 1822 }, ++{ 0x2, 44, 0, 1827 }, ++{ 0x6, 44, 0, 1823 }, ++{ 0x1, 44, 0, 1828 }, ++{ 0x3, 44, 0, 1824 }, ++{ 0x51, 30, 0, 1830 }, ++{ 0xd1, 30, 0, 1829 }, ++{ 0x31, 30, 1, 1840 }, ++{ 0x11, 31, 0, 1839 }, ++{ 0x71, 30, 1, 1838 }, ++{ 0x31, 31, 0, 1837 }, ++{ 0x29, 30, 0, 1832 }, ++{ 0x69, 30, 0, 1831 }, ++{ 0x19, 30, 1, 1844 }, ++{ 0x9, 31, 0, 1843 }, ++{ 0x39, 30, 1, 1842 }, ++{ 0x19, 31, 0, 1841 }, ++{ 0x15, 30, 0, 1834 }, ++{ 0x35, 30, 0, 1833 }, ++{ 0xd, 30, 1, 1848 }, ++{ 0x5, 31, 0, 1847 }, ++{ 0x1d, 30, 1, 1846 }, ++{ 0xd, 31, 0, 1845 }, ++{ 0xb, 30, 0, 1836 }, ++{ 0x1b, 30, 0, 1835 }, ++{ 0x7, 30, 1, 1852 }, ++{ 0x3, 31, 0, 1851 }, ++{ 0xf, 30, 1, 1850 }, ++{ 0x7, 31, 0, 1849 }, ++{ 0xa2, 28, 0, 1854 }, ++{ 0x1a2, 28, 0, 1853 }, ++{ 0x62, 28, 1, 1864 }, ++{ 0x22, 29, 0, 1863 }, ++{ 0xe2, 28, 1, 1862 }, ++{ 0x62, 29, 0, 1861 }, ++{ 0x52, 28, 0, 1856 }, ++{ 0xd2, 28, 0, 1855 }, ++{ 0x32, 28, 1, 1868 }, ++{ 0x12, 29, 0, 1867 }, ++{ 0x72, 28, 1, 1866 }, ++{ 0x32, 29, 0, 1865 }, ++{ 0x2a, 28, 0, 1858 }, ++{ 0x6a, 28, 0, 1857 }, ++{ 0x1a, 28, 1, 1872 }, ++{ 0xa, 29, 0, 1871 }, ++{ 0x3a, 28, 1, 1870 }, ++{ 0x1a, 29, 0, 1869 }, ++{ 0x16, 28, 0, 1860 }, ++{ 0x36, 28, 0, 1859 }, ++{ 0xe, 28, 1, 1876 }, ++{ 0x6, 29, 0, 1875 }, ++{ 0x1e, 28, 1, 1874 }, ++{ 0xe, 29, 0, 1873 }, ++{ 0x51, 28, 0, 1878 }, ++{ 0xd1, 28, 0, 1877 }, ++{ 0x31, 28, 1, 1888 }, ++{ 0x11, 29, 0, 1887 }, ++{ 0x71, 28, 1, 1886 }, ++{ 0x31, 29, 0, 1885 }, ++{ 0x29, 28, 0, 1880 }, ++{ 0x69, 28, 0, 1879 }, ++{ 0x19, 28, 1, 1892 }, ++{ 0x9, 29, 0, 1891 }, ++{ 0x39, 28, 1, 1890 }, ++{ 0x19, 29, 0, 1889 }, ++{ 0x15, 28, 0, 1882 }, ++{ 0x35, 28, 0, 1881 }, ++{ 0xd, 28, 1, 1896 }, ++{ 0x5, 29, 0, 1895 }, ++{ 0x1d, 28, 1, 1894 }, ++{ 0xd, 29, 0, 1893 }, ++{ 0xb, 28, 0, 1884 }, ++{ 0x1b, 28, 0, 1883 }, ++{ 0x7, 28, 1, 1900 }, ++{ 0x3, 29, 0, 1899 }, ++{ 0xf, 28, 1, 1898 }, ++{ 0x7, 29, 0, 1897 }, ++{ 0x51, 26, 0, 1902 }, ++{ 0xd1, 26, 0, 1901 }, ++{ 0x31, 26, 1, 1912 }, ++{ 0x11, 27, 0, 1911 }, ++{ 0x71, 26, 1, 1910 }, ++{ 0x31, 27, 0, 1909 }, ++{ 0x29, 26, 0, 1904 }, ++{ 0x69, 26, 0, 1903 }, ++{ 0x19, 26, 1, 1916 }, ++{ 0x9, 27, 0, 1915 }, ++{ 0x39, 26, 1, 1914 }, ++{ 0x19, 27, 0, 1913 }, ++{ 0x15, 26, 0, 1906 }, ++{ 0x35, 26, 0, 1905 }, ++{ 0xd, 26, 1, 1920 }, ++{ 0x5, 27, 0, 1919 }, ++{ 0x1d, 26, 1, 1918 }, ++{ 0xd, 27, 0, 1917 }, ++{ 0xb, 26, 0, 1908 }, ++{ 0x1b, 26, 0, 1907 }, ++{ 0x7, 26, 1, 1924 }, ++{ 0x3, 27, 0, 1923 }, ++{ 0xf, 26, 1, 1922 }, ++{ 0x7, 27, 0, 1921 }, ++{ 0xa2, 24, 0, 1926 }, ++{ 0x1a2, 24, 0, 1925 }, ++{ 0x62, 24, 1, 1936 }, ++{ 0x22, 25, 0, 1935 }, ++{ 0xe2, 24, 1, 1934 }, ++{ 0x62, 25, 0, 1933 }, ++{ 0x52, 24, 0, 1928 }, ++{ 0xd2, 24, 0, 1927 }, ++{ 0x32, 24, 1, 1940 }, ++{ 0x12, 25, 0, 1939 }, ++{ 0x72, 24, 1, 1938 }, ++{ 0x32, 25, 0, 1937 }, ++{ 0x2a, 24, 0, 1930 }, ++{ 0x6a, 24, 0, 1929 }, ++{ 0x1a, 24, 1, 1944 }, ++{ 0xa, 25, 0, 1943 }, ++{ 0x3a, 24, 1, 1942 }, ++{ 0x1a, 25, 0, 1941 }, ++{ 0x16, 24, 0, 1932 }, ++{ 0x36, 24, 0, 1931 }, ++{ 0xe, 24, 1, 1948 }, ++{ 0x6, 25, 0, 1947 }, ++{ 0x1e, 24, 1, 1946 }, ++{ 0xe, 25, 0, 1945 }, ++{ 0x51, 24, 0, 1950 }, ++{ 0xd1, 24, 0, 1949 }, ++{ 0x31, 24, 1, 1960 }, ++{ 0x11, 25, 0, 1959 }, ++{ 0x71, 24, 1, 1958 }, ++{ 0x31, 25, 0, 1957 }, ++{ 0x29, 24, 0, 1952 }, ++{ 0x69, 24, 0, 1951 }, ++{ 0x19, 24, 1, 1964 }, ++{ 0x9, 25, 0, 1963 }, ++{ 0x39, 24, 1, 1962 }, ++{ 0x19, 25, 0, 1961 }, ++{ 0x15, 24, 0, 1954 }, ++{ 0x35, 24, 0, 1953 }, ++{ 0xd, 24, 1, 1968 }, ++{ 0x5, 25, 0, 1967 }, ++{ 0x1d, 24, 1, 1966 }, ++{ 0xd, 25, 0, 1965 }, ++{ 0xb, 24, 0, 1956 }, ++{ 0x1b, 24, 0, 1955 }, ++{ 0x7, 24, 1, 1972 }, ++{ 0x3, 25, 0, 1971 }, ++{ 0xf, 24, 1, 1970 }, ++{ 0x7, 25, 0, 1969 }, ++{ 0x51, 22, 1, 1998 }, ++{ 0x50, 22, 0, 1974 }, ++{ 0xd1, 22, 1, 1997 }, ++{ 0xd0, 22, 0, 1973 }, ++{ 0x31, 22, 1, 2008 }, ++{ 0x30, 22, 1, 1984 }, ++{ 0x11, 23, 1, 2007 }, ++{ 0x10, 23, 0, 1983 }, ++{ 0x71, 22, 1, 2006 }, ++{ 0x70, 22, 1, 1982 }, ++{ 0x31, 23, 1, 2005 }, ++{ 0x30, 23, 0, 1981 }, ++{ 0x29, 22, 1, 2000 }, ++{ 0x28, 22, 0, 1976 }, ++{ 0x69, 22, 1, 1999 }, ++{ 0x68, 22, 0, 1975 }, ++{ 0x19, 22, 1, 2012 }, ++{ 0x18, 22, 1, 1988 }, ++{ 0x9, 23, 1, 2011 }, ++{ 0x8, 23, 0, 1987 }, ++{ 0x39, 22, 1, 2010 }, ++{ 0x38, 22, 1, 1986 }, ++{ 0x19, 23, 1, 2009 }, ++{ 0x18, 23, 0, 1985 }, ++{ 0x15, 22, 1, 2002 }, ++{ 0x14, 22, 0, 1978 }, ++{ 0x35, 22, 1, 2001 }, ++{ 0x34, 22, 0, 1977 }, ++{ 0xd, 22, 1, 2016 }, ++{ 0xc, 22, 1, 1992 }, ++{ 0x5, 23, 1, 2015 }, ++{ 0x4, 23, 0, 1991 }, ++{ 0x1d, 22, 1, 2014 }, ++{ 0x1c, 22, 1, 1990 }, ++{ 0xd, 23, 1, 2013 }, ++{ 0xc, 23, 0, 1989 }, ++{ 0xb, 22, 1, 2004 }, ++{ 0xa, 22, 0, 1980 }, ++{ 0x1b, 22, 1, 2003 }, ++{ 0x1a, 22, 0, 1979 }, ++{ 0x7, 22, 1, 2020 }, ++{ 0x6, 22, 1, 1996 }, ++{ 0x3, 23, 1, 2019 }, ++{ 0x2, 23, 0, 1995 }, ++{ 0xf, 22, 1, 2018 }, ++{ 0xe, 22, 1, 1994 }, ++{ 0x7, 23, 1, 2017 }, ++{ 0x6, 23, 0, 1993 }, ++{ 0x8, 21, 0, 2022 }, ++{ 0x18, 21, 0, 2021 }, ++{ 0x1, 21, 1, 2026 }, ++{ 0x2, 21, 0, 2025 }, ++{ 0x3, 21, 1, 2024 }, ++{ 0x4, 21, 0, 2023 }, ++{ 0x1, 239, 0, 2027 }, ++{ 0x1, 339, 0, 2028 }, ++{ 0x14, 43, 0, 2031 }, ++{ 0x34, 43, 0, 2029 }, ++{ 0xc, 43, 0, 2032 }, ++{ 0x1c, 43, 0, 2030 }, ++{ 0x2, 43, 0, 2035 }, ++{ 0x6, 43, 0, 2033 }, ++{ 0x1, 43, 0, 2036 }, ++{ 0x3, 43, 0, 2034 }, ++{ 0x51, 19, 0, 2038 }, ++{ 0xd1, 19, 0, 2037 }, ++{ 0x31, 19, 1, 2048 }, ++{ 0x11, 20, 0, 2047 }, ++{ 0x71, 19, 1, 2046 }, ++{ 0x31, 20, 0, 2045 }, ++{ 0x29, 19, 0, 2040 }, ++{ 0x69, 19, 0, 2039 }, ++{ 0x19, 19, 1, 2052 }, ++{ 0x9, 20, 0, 2051 }, ++{ 0x39, 19, 1, 2050 }, ++{ 0x19, 20, 0, 2049 }, ++{ 0x15, 19, 0, 2042 }, ++{ 0x35, 19, 0, 2041 }, ++{ 0xd, 19, 1, 2056 }, ++{ 0x5, 20, 0, 2055 }, ++{ 0x1d, 19, 1, 2054 }, ++{ 0xd, 20, 0, 2053 }, ++{ 0xb, 19, 0, 2044 }, ++{ 0x1b, 19, 0, 2043 }, ++{ 0x7, 19, 1, 2060 }, ++{ 0x3, 20, 0, 2059 }, ++{ 0xf, 19, 1, 2058 }, ++{ 0x7, 20, 0, 2057 }, ++{ 0x1, 32, 0, 2061 }, ++{ 0x1, 140, 0, 2062 }, ++{ 0x2, 45, 0, 2063 }, ++{ 0x1, 45, 0, 2064 }, ++{ 0x1, 387, 0, 2065 }, ++{ 0x2, 52, 0, 2066 }, ++{ 0x1, 52, 0, 2067 }, ++{ 0x1, 133, 0, 2068 }, ++{ 0x51, 17, 0, 2070 }, ++{ 0xd1, 17, 0, 2069 }, ++{ 0x31, 17, 1, 2080 }, ++{ 0x11, 18, 0, 2079 }, ++{ 0x71, 17, 1, 2078 }, ++{ 0x31, 18, 0, 2077 }, ++{ 0x29, 17, 0, 2072 }, ++{ 0x69, 17, 0, 2071 }, ++{ 0x19, 17, 1, 2084 }, ++{ 0x9, 18, 0, 2083 }, ++{ 0x39, 17, 1, 2082 }, ++{ 0x19, 18, 0, 2081 }, ++{ 0x15, 17, 0, 2074 }, ++{ 0x35, 17, 0, 2073 }, ++{ 0xd, 17, 1, 2088 }, ++{ 0x5, 18, 0, 2087 }, ++{ 0x1d, 17, 1, 2086 }, ++{ 0xd, 18, 0, 2085 }, ++{ 0xb, 17, 0, 2076 }, ++{ 0x1b, 17, 0, 2075 }, ++{ 0x7, 17, 1, 2092 }, ++{ 0x3, 18, 0, 2091 }, ++{ 0xf, 17, 1, 2090 }, ++{ 0x7, 18, 0, 2089 }, ++{ 0xa20, 15, 0, 2094 }, ++{ 0x1a20, 15, 0, 2093 }, ++{ 0x620, 15, 1, 2104 }, ++{ 0x220, 16, 0, 2103 }, ++{ 0xe20, 15, 1, 2102 }, ++{ 0x620, 16, 0, 2101 }, ++{ 0x520, 15, 0, 2096 }, ++{ 0xd20, 15, 0, 2095 }, ++{ 0x320, 15, 1, 2108 }, ++{ 0x120, 16, 0, 2107 }, ++{ 0x720, 15, 1, 2106 }, ++{ 0x320, 16, 0, 2105 }, ++{ 0x2a0, 15, 0, 2098 }, ++{ 0x6a0, 15, 0, 2097 }, ++{ 0x1a0, 15, 1, 2112 }, ++{ 0xa0, 16, 0, 2111 }, ++{ 0x3a0, 15, 1, 2110 }, ++{ 0x1a0, 16, 0, 2109 }, ++{ 0x160, 15, 0, 2100 }, ++{ 0x360, 15, 0, 2099 }, ++{ 0xe0, 15, 1, 2116 }, ++{ 0x60, 16, 0, 2115 }, ++{ 0x1e0, 15, 1, 2114 }, ++{ 0xe0, 16, 0, 2113 }, ++{ 0x51, 15, 1, 2142 }, ++{ 0x50, 15, 0, 2118 }, ++{ 0xd1, 15, 1, 2141 }, ++{ 0xd0, 15, 0, 2117 }, ++{ 0x31, 15, 1, 2152 }, ++{ 0x30, 15, 1, 2128 }, ++{ 0x11, 16, 1, 2151 }, ++{ 0x10, 16, 0, 2127 }, ++{ 0x71, 15, 1, 2150 }, ++{ 0x70, 15, 1, 2126 }, ++{ 0x31, 16, 1, 2149 }, ++{ 0x30, 16, 0, 2125 }, ++{ 0x29, 15, 1, 2144 }, ++{ 0x28, 15, 0, 2120 }, ++{ 0x69, 15, 1, 2143 }, ++{ 0x68, 15, 0, 2119 }, ++{ 0x19, 15, 1, 2156 }, ++{ 0x18, 15, 1, 2132 }, ++{ 0x9, 16, 1, 2155 }, ++{ 0x8, 16, 0, 2131 }, ++{ 0x39, 15, 1, 2154 }, ++{ 0x38, 15, 1, 2130 }, ++{ 0x19, 16, 1, 2153 }, ++{ 0x18, 16, 0, 2129 }, ++{ 0x15, 15, 1, 2146 }, ++{ 0x14, 15, 0, 2122 }, ++{ 0x35, 15, 1, 2145 }, ++{ 0x34, 15, 0, 2121 }, ++{ 0xd, 15, 1, 2160 }, ++{ 0xc, 15, 1, 2136 }, ++{ 0x5, 16, 1, 2159 }, ++{ 0x4, 16, 0, 2135 }, ++{ 0x1d, 15, 1, 2158 }, ++{ 0x1c, 15, 1, 2134 }, ++{ 0xd, 16, 1, 2157 }, ++{ 0xc, 16, 0, 2133 }, ++{ 0xb, 15, 1, 2148 }, ++{ 0xa, 15, 0, 2124 }, ++{ 0x1b, 15, 1, 2147 }, ++{ 0x1a, 15, 0, 2123 }, ++{ 0x7, 15, 1, 2164 }, ++{ 0x6, 15, 1, 2140 }, ++{ 0x3, 16, 1, 2163 }, ++{ 0x2, 16, 0, 2139 }, ++{ 0xf, 15, 1, 2162 }, ++{ 0xe, 15, 1, 2138 }, ++{ 0x7, 16, 1, 2161 }, ++{ 0x6, 16, 0, 2137 }, ++{ 0x8, 14, 0, 2166 }, ++{ 0x18, 14, 0, 2165 }, ++{ 0x1, 14, 1, 2170 }, ++{ 0x2, 14, 0, 2169 }, ++{ 0x3, 14, 1, 2168 }, ++{ 0x4, 14, 0, 2167 }, ++{ 0x1, 109, 1, 2322 }, ++{ 0x1, 110, 1, 2321 }, ++{ 0x1, 111, 1, 2320 }, ++{ 0x1, 112, 1, 2319 }, ++{ 0x1, 113, 1, 2318 }, ++{ 0x1, 114, 1, 2317 }, ++{ 0x1, 115, 1, 2316 }, ++{ 0x1, 116, 1, 2315 }, ++{ 0x39, 41, 1, 22 }, ++{ 0x19, 42, 0, 21 }, ++{ 0x3, 109, 1, 2314 }, ++{ 0x3, 110, 1, 2313 }, ++{ 0x3, 111, 1, 2312 }, ++{ 0x3, 112, 1, 2311 }, ++{ 0x3, 113, 1, 2310 }, ++{ 0x3, 114, 1, 2309 }, ++{ 0x3, 115, 1, 2308 }, ++{ 0x3, 116, 1, 2307 }, ++{ 0x69, 41, 0, 11 }, ++{ 0x14, 100, 1, 2302 }, ++{ 0x22, 101, 1, 2299 }, ++{ 0x44, 101, 1, 2301 }, ++{ 0xa, 108, 1, 2300 }, ++{ 0xd1, 41, 0, 9 }, ++{ 0x34, 100, 1, 2174 }, ++{ 0xc4, 101, 1, 2173 }, ++{ 0x1c, 107, 1, 2171 }, ++{ 0xe, 122, 0, 2172 }, ++{ 0xc, 100, 1, 2462 }, ++{ 0xa, 101, 1, 2459 }, ++{ 0x14, 101, 1, 2461 }, ++{ 0x6, 108, 0, 2460 }, ++{ 0x2, 100, 1, 2186 }, ++{ 0x2, 101, 1, 2185 }, ++{ 0x2, 106, 1, 2184 }, ++{ 0x2, 107, 0, 2183 }, ++{ 0x12, 100, 1, 2182 }, ++{ 0x42, 101, 1, 2181 }, ++{ 0x6, 106, 1, 2180 }, ++{ 0x6, 107, 0, 2179 }, ++{ 0xa, 100, 1, 2306 }, ++{ 0x12, 101, 1, 2305 }, ++{ 0x24, 101, 1, 2303 }, ++{ 0x5, 108, 1, 2304 }, ++{ 0x71, 41, 1, 18 }, ++{ 0x31, 42, 0, 17 }, ++{ 0x1a, 100, 1, 2178 }, ++{ 0x32, 101, 1, 2177 }, ++{ 0x1a, 107, 1, 2175 }, ++{ 0x7, 122, 0, 2176 }, ++{ 0x6, 100, 1, 2466 }, ++{ 0x6, 101, 1, 2465 }, ++{ 0xc, 101, 1, 2463 }, ++{ 0x3, 108, 0, 2464 }, ++{ 0x1, 100, 1, 2482 }, ++{ 0x1, 101, 1, 2481 }, ++{ 0x1, 102, 1, 2480 }, ++{ 0x1, 103, 1, 2479 }, ++{ 0x1, 104, 1, 2478 }, ++{ 0x1, 105, 1, 2477 }, ++{ 0x1, 106, 1, 2476 }, ++{ 0x1, 107, 0, 2475 }, ++{ 0x3, 100, 1, 2474 }, ++{ 0x3, 101, 1, 2473 }, ++{ 0x3, 102, 1, 2472 }, ++{ 0x3, 103, 1, 2471 }, ++{ 0x3, 104, 1, 2470 }, ++{ 0x3, 105, 1, 2469 }, ++{ 0x3, 106, 1, 2468 }, ++{ 0x3, 107, 0, 2467 }, ++{ 0x8, 67, 1, 2346 }, ++{ 0x8, 68, 1, 2345 }, ++{ 0x2, 73, 1, 2340 }, ++{ 0x2, 74, 1, 2339 }, ++{ 0x1, 76, 1, 2344 }, ++{ 0x1, 77, 1, 2343 }, ++{ 0x1, 78, 1, 2342 }, ++{ 0x1, 79, 1, 2341 }, ++{ 0xf, 41, 1, 30 }, ++{ 0x7, 42, 0, 29 }, ++{ 0x18, 67, 1, 2338 }, ++{ 0x18, 68, 1, 2337 }, ++{ 0x6, 73, 1, 2332 }, ++{ 0x6, 74, 1, 2331 }, ++{ 0x3, 76, 1, 2336 }, ++{ 0x3, 77, 1, 2335 }, ++{ 0x3, 78, 1, 2334 }, ++{ 0x3, 79, 1, 2333 }, ++{ 0x1b, 41, 0, 15 }, ++{ 0x14, 67, 1, 2326 }, ++{ 0x22, 68, 1, 2323 }, ++{ 0x44, 68, 1, 2325 }, ++{ 0xa, 75, 1, 2324 }, ++{ 0x35, 41, 0, 13 }, ++{ 0x34, 67, 1, 2190 }, ++{ 0xc4, 68, 1, 2189 }, ++{ 0x38, 74, 1, 2187 }, ++{ 0xe, 85, 0, 2188 }, ++{ 0xc, 67, 1, 2486 }, ++{ 0xa, 68, 1, 2483 }, ++{ 0x14, 68, 1, 2485 }, ++{ 0x6, 75, 0, 2484 }, ++{ 0x2, 67, 1, 2202 }, ++{ 0x2, 68, 1, 2201 }, ++{ 0x4, 73, 1, 2200 }, ++{ 0x4, 74, 0, 2199 }, ++{ 0x12, 67, 1, 2198 }, ++{ 0x42, 68, 1, 2197 }, ++{ 0xc, 73, 1, 2196 }, ++{ 0xc, 74, 0, 2195 }, ++{ 0xa, 67, 1, 2330 }, ++{ 0x12, 68, 1, 2329 }, ++{ 0x24, 68, 1, 2327 }, ++{ 0x5, 75, 1, 2328 }, ++{ 0x1d, 41, 1, 26 }, ++{ 0xd, 42, 0, 25 }, ++{ 0x1a, 67, 1, 2194 }, ++{ 0x32, 68, 1, 2193 }, ++{ 0x34, 74, 1, 2191 }, ++{ 0x7, 85, 0, 2192 }, ++{ 0x6, 67, 1, 2490 }, ++{ 0x6, 68, 1, 2489 }, ++{ 0xc, 68, 1, 2487 }, ++{ 0x3, 75, 0, 2488 }, ++{ 0x1, 67, 1, 2506 }, ++{ 0x1, 68, 1, 2505 }, ++{ 0x1, 69, 1, 2504 }, ++{ 0x1, 70, 1, 2503 }, ++{ 0x1, 71, 1, 2502 }, ++{ 0x1, 72, 1, 2501 }, ++{ 0x1, 73, 1, 2500 }, ++{ 0x1, 74, 0, 2499 }, ++{ 0x3, 67, 1, 2498 }, ++{ 0x3, 68, 1, 2497 }, ++{ 0x3, 69, 1, 2496 }, ++{ 0x3, 70, 1, 2495 }, ++{ 0x3, 71, 1, 2494 }, ++{ 0x3, 72, 1, 2493 }, ++{ 0x3, 73, 1, 2492 }, ++{ 0x3, 74, 0, 2491 }, ++{ 0x28, 95, 1, 2354 }, ++{ 0x44, 96, 1, 2349 }, ++{ 0x88, 96, 1, 2353 }, ++{ 0x44, 97, 1, 2348 }, ++{ 0x88, 97, 1, 2352 }, ++{ 0x44, 98, 1, 2347 }, ++{ 0x88, 98, 1, 2351 }, ++{ 0x28, 99, 0, 2350 }, ++{ 0x68, 95, 1, 2210 }, ++{ 0x188, 96, 1, 2209 }, ++{ 0x188, 97, 1, 2208 }, ++{ 0x188, 98, 1, 2207 }, ++{ 0x38, 118, 1, 2206 }, ++{ 0x38, 119, 1, 2205 }, ++{ 0x38, 120, 1, 2204 }, ++{ 0x38, 121, 0, 2203 }, ++{ 0x18, 95, 1, 2514 }, ++{ 0x14, 96, 1, 2509 }, ++{ 0x28, 96, 1, 2513 }, ++{ 0x14, 97, 1, 2508 }, ++{ 0x28, 97, 1, 2512 }, ++{ 0x14, 98, 1, 2507 }, ++{ 0x28, 98, 1, 2511 }, ++{ 0x18, 99, 0, 2510 }, ++{ 0x14, 95, 1, 2362 }, ++{ 0x24, 96, 1, 2361 }, ++{ 0x48, 96, 1, 2357 }, ++{ 0x24, 97, 1, 2360 }, ++{ 0x48, 97, 1, 2356 }, ++{ 0x24, 98, 1, 2359 }, ++{ 0x48, 98, 1, 2355 }, ++{ 0x14, 99, 0, 2358 }, ++{ 0x34, 95, 1, 2218 }, ++{ 0x64, 96, 1, 2217 }, ++{ 0x64, 97, 1, 2216 }, ++{ 0x64, 98, 1, 2215 }, ++{ 0x1c, 118, 1, 2214 }, ++{ 0x1c, 119, 1, 2213 }, ++{ 0x1c, 120, 1, 2212 }, ++{ 0x1c, 121, 0, 2211 }, ++{ 0xc, 95, 1, 2522 }, ++{ 0xc, 96, 1, 2521 }, ++{ 0x18, 96, 1, 2517 }, ++{ 0xc, 97, 1, 2520 }, ++{ 0x18, 97, 1, 2516 }, ++{ 0xc, 98, 1, 2519 }, ++{ 0x18, 98, 1, 2515 }, ++{ 0xc, 99, 0, 2518 }, ++{ 0xa, 95, 1, 2370 }, ++{ 0x11, 96, 1, 2365 }, ++{ 0x22, 96, 1, 2369 }, ++{ 0x11, 97, 1, 2364 }, ++{ 0x22, 97, 1, 2368 }, ++{ 0x11, 98, 1, 2363 }, ++{ 0x22, 98, 1, 2367 }, ++{ 0xa, 99, 0, 2366 }, ++{ 0x1a, 95, 1, 2226 }, ++{ 0x62, 96, 1, 2225 }, ++{ 0x62, 97, 1, 2224 }, ++{ 0x62, 98, 1, 2223 }, ++{ 0xe, 118, 1, 2222 }, ++{ 0xe, 119, 1, 2221 }, ++{ 0xe, 120, 1, 2220 }, ++{ 0xe, 121, 0, 2219 }, ++{ 0x6, 95, 1, 2530 }, ++{ 0x5, 96, 1, 2525 }, ++{ 0xa, 96, 1, 2529 }, ++{ 0x5, 97, 1, 2524 }, ++{ 0xa, 97, 1, 2528 }, ++{ 0x5, 98, 1, 2523 }, ++{ 0xa, 98, 1, 2527 }, ++{ 0x6, 99, 0, 2526 }, ++{ 0x5, 95, 1, 2378 }, ++{ 0x9, 96, 1, 2377 }, ++{ 0x12, 96, 1, 2373 }, ++{ 0x9, 97, 1, 2376 }, ++{ 0x12, 97, 1, 2372 }, ++{ 0x9, 98, 1, 2375 }, ++{ 0x12, 98, 1, 2371 }, ++{ 0x5, 99, 0, 2374 }, ++{ 0xd, 95, 1, 2234 }, ++{ 0x19, 96, 1, 2233 }, ++{ 0x19, 97, 1, 2232 }, ++{ 0x19, 98, 1, 2231 }, ++{ 0x7, 118, 1, 2230 }, ++{ 0x7, 119, 1, 2229 }, ++{ 0x7, 120, 1, 2228 }, ++{ 0x7, 121, 0, 2227 }, ++{ 0x3, 95, 1, 2538 }, ++{ 0x3, 96, 1, 2537 }, ++{ 0x6, 96, 1, 2533 }, ++{ 0x3, 97, 1, 2536 }, ++{ 0x6, 97, 1, 2532 }, ++{ 0x3, 98, 1, 2535 }, ++{ 0x6, 98, 1, 2531 }, ++{ 0x3, 99, 0, 2534 }, ++{ 0x28, 62, 1, 2386 }, ++{ 0x44, 63, 1, 2381 }, ++{ 0x88, 63, 1, 2385 }, ++{ 0x44, 64, 1, 2380 }, ++{ 0x88, 64, 1, 2384 }, ++{ 0x44, 65, 1, 2379 }, ++{ 0x88, 65, 1, 2383 }, ++{ 0x28, 66, 0, 2382 }, ++{ 0x68, 62, 1, 2242 }, ++{ 0x188, 63, 1, 2241 }, ++{ 0x188, 64, 1, 2240 }, ++{ 0x188, 65, 1, 2239 }, ++{ 0x38, 81, 1, 2238 }, ++{ 0x38, 82, 1, 2237 }, ++{ 0x38, 83, 1, 2236 }, ++{ 0x38, 84, 0, 2235 }, ++{ 0x18, 62, 1, 2546 }, ++{ 0x14, 63, 1, 2541 }, ++{ 0x28, 63, 1, 2545 }, ++{ 0x14, 64, 1, 2540 }, ++{ 0x28, 64, 1, 2544 }, ++{ 0x14, 65, 1, 2539 }, ++{ 0x28, 65, 1, 2543 }, ++{ 0x18, 66, 0, 2542 }, ++{ 0x14, 62, 1, 2394 }, ++{ 0x24, 63, 1, 2393 }, ++{ 0x48, 63, 1, 2389 }, ++{ 0x24, 64, 1, 2392 }, ++{ 0x48, 64, 1, 2388 }, ++{ 0x24, 65, 1, 2391 }, ++{ 0x48, 65, 1, 2387 }, ++{ 0x14, 66, 0, 2390 }, ++{ 0x34, 62, 1, 2250 }, ++{ 0x64, 63, 1, 2249 }, ++{ 0x64, 64, 1, 2248 }, ++{ 0x64, 65, 1, 2247 }, ++{ 0x1c, 81, 1, 2246 }, ++{ 0x1c, 82, 1, 2245 }, ++{ 0x1c, 83, 1, 2244 }, ++{ 0x1c, 84, 0, 2243 }, ++{ 0xc, 62, 1, 2554 }, ++{ 0xc, 63, 1, 2553 }, ++{ 0x18, 63, 1, 2549 }, ++{ 0xc, 64, 1, 2552 }, ++{ 0x18, 64, 1, 2548 }, ++{ 0xc, 65, 1, 2551 }, ++{ 0x18, 65, 1, 2547 }, ++{ 0xc, 66, 0, 2550 }, ++{ 0xa, 62, 1, 2402 }, ++{ 0x11, 63, 1, 2397 }, ++{ 0x22, 63, 1, 2401 }, ++{ 0x11, 64, 1, 2396 }, ++{ 0x22, 64, 1, 2400 }, ++{ 0x11, 65, 1, 2395 }, ++{ 0x22, 65, 1, 2399 }, ++{ 0xa, 66, 0, 2398 }, ++{ 0x1a, 62, 1, 2258 }, ++{ 0x62, 63, 1, 2257 }, ++{ 0x62, 64, 1, 2256 }, ++{ 0x62, 65, 1, 2255 }, ++{ 0xe, 81, 1, 2254 }, ++{ 0xe, 82, 1, 2253 }, ++{ 0xe, 83, 1, 2252 }, ++{ 0xe, 84, 0, 2251 }, ++{ 0x6, 62, 1, 2562 }, ++{ 0x5, 63, 1, 2557 }, ++{ 0xa, 63, 1, 2561 }, ++{ 0x5, 64, 1, 2556 }, ++{ 0xa, 64, 1, 2560 }, ++{ 0x5, 65, 1, 2555 }, ++{ 0xa, 65, 1, 2559 }, ++{ 0x6, 66, 0, 2558 }, ++{ 0x5, 62, 1, 2410 }, ++{ 0x9, 63, 1, 2409 }, ++{ 0x12, 63, 1, 2405 }, ++{ 0x9, 64, 1, 2408 }, ++{ 0x12, 64, 1, 2404 }, ++{ 0x9, 65, 1, 2407 }, ++{ 0x12, 65, 1, 2403 }, ++{ 0x5, 66, 0, 2406 }, ++{ 0xd, 62, 1, 2266 }, ++{ 0x19, 63, 1, 2265 }, ++{ 0x19, 64, 1, 2264 }, ++{ 0x19, 65, 1, 2263 }, ++{ 0x7, 81, 1, 2262 }, ++{ 0x7, 82, 1, 2261 }, ++{ 0x7, 83, 1, 2260 }, ++{ 0x7, 84, 0, 2259 }, ++{ 0x3, 62, 1, 2570 }, ++{ 0x3, 63, 1, 2569 }, ++{ 0x6, 63, 1, 2565 }, ++{ 0x3, 64, 1, 2568 }, ++{ 0x6, 64, 1, 2564 }, ++{ 0x3, 65, 1, 2567 }, ++{ 0x6, 65, 1, 2563 }, ++{ 0x3, 66, 0, 2566 }, ++{ 0x8, 86, 1, 2434 }, ++{ 0x8, 87, 1, 2433 }, ++{ 0x2, 88, 1, 2432 }, ++{ 0x2, 89, 1, 2431 }, ++{ 0x2, 90, 1, 2430 }, ++{ 0x2, 91, 1, 2429 }, ++{ 0x2, 92, 1, 2428 }, ++{ 0x2, 93, 0, 2427 }, ++{ 0x18, 86, 1, 2426 }, ++{ 0x18, 87, 1, 2425 }, ++{ 0x6, 88, 1, 2424 }, ++{ 0x6, 89, 1, 2423 }, ++{ 0x6, 90, 1, 2422 }, ++{ 0x6, 91, 1, 2421 }, ++{ 0x6, 92, 1, 2420 }, ++{ 0x6, 93, 0, 2419 }, ++{ 0x14, 86, 1, 2414 }, ++{ 0x22, 87, 1, 2411 }, ++{ 0x44, 87, 1, 2413 }, ++{ 0xa, 94, 0, 2412 }, ++{ 0x34, 86, 1, 2270 }, ++{ 0xc4, 87, 1, 2269 }, ++{ 0x38, 93, 1, 2267 }, ++{ 0xe, 117, 0, 2268 }, ++{ 0xc, 86, 1, 2574 }, ++{ 0xa, 87, 1, 2571 }, ++{ 0x14, 87, 1, 2573 }, ++{ 0x6, 94, 0, 2572 }, ++{ 0x2, 86, 1, 2282 }, ++{ 0x2, 87, 1, 2281 }, ++{ 0x4, 92, 1, 2280 }, ++{ 0x4, 93, 0, 2279 }, ++{ 0x12, 86, 1, 2278 }, ++{ 0x42, 87, 1, 2277 }, ++{ 0xc, 92, 1, 2276 }, ++{ 0xc, 93, 0, 2275 }, ++{ 0xa, 86, 1, 2418 }, ++{ 0x12, 87, 1, 2417 }, ++{ 0x24, 87, 1, 2415 }, ++{ 0x5, 94, 0, 2416 }, ++{ 0x1a, 86, 1, 2274 }, ++{ 0x32, 87, 1, 2273 }, ++{ 0x34, 93, 1, 2271 }, ++{ 0x7, 117, 0, 2272 }, ++{ 0x6, 86, 1, 2578 }, ++{ 0x6, 87, 1, 2577 }, ++{ 0xc, 87, 1, 2575 }, ++{ 0x3, 94, 0, 2576 }, ++{ 0x1, 86, 1, 2594 }, ++{ 0x1, 87, 1, 2593 }, ++{ 0x1, 88, 1, 2592 }, ++{ 0x1, 89, 1, 2591 }, ++{ 0x1, 90, 1, 2590 }, ++{ 0x1, 91, 1, 2589 }, ++{ 0x1, 92, 1, 2588 }, ++{ 0x1, 93, 0, 2587 }, ++{ 0x3, 86, 1, 2586 }, ++{ 0x3, 87, 1, 2585 }, ++{ 0x3, 88, 1, 2584 }, ++{ 0x3, 89, 1, 2583 }, ++{ 0x3, 90, 1, 2582 }, ++{ 0x3, 91, 1, 2581 }, ++{ 0x3, 92, 1, 2580 }, ++{ 0x3, 93, 0, 2579 }, ++{ 0x8, 53, 1, 2458 }, ++{ 0x8, 54, 1, 2457 }, ++{ 0x2, 55, 1, 2456 }, ++{ 0x2, 56, 1, 2455 }, ++{ 0x2, 57, 1, 2454 }, ++{ 0x2, 58, 1, 2453 }, ++{ 0x2, 59, 1, 2452 }, ++{ 0x2, 60, 0, 2451 }, ++{ 0x18, 53, 1, 2450 }, ++{ 0x18, 54, 1, 2449 }, ++{ 0x6, 55, 1, 2448 }, ++{ 0x6, 56, 1, 2447 }, ++{ 0x6, 57, 1, 2446 }, ++{ 0x6, 58, 1, 2445 }, ++{ 0x6, 59, 1, 2444 }, ++{ 0x6, 60, 0, 2443 }, ++{ 0x14, 53, 1, 2438 }, ++{ 0x22, 54, 1, 2435 }, ++{ 0x44, 54, 1, 2437 }, ++{ 0xa, 61, 0, 2436 }, ++{ 0x34, 53, 1, 2286 }, ++{ 0xc4, 54, 1, 2285 }, ++{ 0x38, 60, 1, 2283 }, ++{ 0xe, 80, 0, 2284 }, ++{ 0xc, 53, 1, 2598 }, ++{ 0xa, 54, 1, 2595 }, ++{ 0x14, 54, 1, 2597 }, ++{ 0x6, 61, 0, 2596 }, ++{ 0x2, 53, 1, 2298 }, ++{ 0x2, 54, 1, 2297 }, ++{ 0x4, 59, 1, 2296 }, ++{ 0x4, 60, 0, 2295 }, ++{ 0x12, 53, 1, 2294 }, ++{ 0x42, 54, 1, 2293 }, ++{ 0xc, 59, 1, 2292 }, ++{ 0xc, 60, 0, 2291 }, ++{ 0xa, 53, 1, 2442 }, ++{ 0x12, 54, 1, 2441 }, ++{ 0x24, 54, 1, 2439 }, ++{ 0x5, 61, 0, 2440 }, ++{ 0x1a, 53, 1, 2290 }, ++{ 0x32, 54, 1, 2289 }, ++{ 0x34, 60, 1, 2287 }, ++{ 0x7, 80, 0, 2288 }, ++{ 0x6, 53, 1, 2602 }, ++{ 0x6, 54, 1, 2601 }, ++{ 0xc, 54, 1, 2599 }, ++{ 0x3, 61, 0, 2600 }, ++{ 0x1, 53, 1, 2618 }, ++{ 0x1, 54, 1, 2617 }, ++{ 0x1, 55, 1, 2616 }, ++{ 0x1, 56, 1, 2615 }, ++{ 0x1, 57, 1, 2614 }, ++{ 0x1, 58, 1, 2613 }, ++{ 0x1, 59, 1, 2612 }, ++{ 0x1, 60, 0, 2611 }, ++{ 0x3, 53, 1, 2610 }, ++{ 0x3, 54, 1, 2609 }, ++{ 0x3, 55, 1, 2608 }, ++{ 0x3, 56, 1, 2607 }, ++{ 0x3, 57, 1, 2606 }, ++{ 0x3, 58, 1, 2605 }, ++{ 0x3, 59, 1, 2604 }, ++{ 0x3, 60, 0, 2603 }, ++{ 0x1, 4, 0, 2619 }, ++{ 0x1, 296, 0, 2620 }, ++{ 0x1, 379, 0, 2621 }, ++{ 0x1, 374, 0, 2622 }, ++{ 0x2, 358, 0, 2623 }, ++{ 0x1, 358, 0, 2626 }, ++{ 0x2, 357, 0, 2624 }, ++{ 0x1, 357, 0, 2627 }, ++{ 0x2, 356, 0, 2625 }, ++{ 0x1, 356, 0, 2628 }, ++{ 0x1, 355, 0, 2629 }, ++{ 0x1, 354, 0, 2630 }, ++{ 0x2, 353, 0, 2631 }, ++{ 0x1, 353, 0, 2633 }, ++{ 0x2, 352, 0, 2632 }, ++{ 0x1, 352, 0, 2634 }, ++{ 0x1, 382, 0, 2641 }, ++{ 0x8, 381, 0, 2635 }, ++{ 0x4, 381, 0, 2637 }, ++{ 0x2, 381, 0, 2639 }, ++{ 0x1, 381, 0, 2642 }, ++{ 0x8, 380, 0, 2636 }, ++{ 0x4, 380, 0, 2638 }, ++{ 0x2, 380, 0, 2640 }, ++{ 0x1, 380, 0, 2643 }, ++{ 0x1, 351, 0, 2650 }, ++{ 0x8, 350, 0, 2644 }, ++{ 0x4, 350, 0, 2646 }, ++{ 0x2, 350, 0, 2648 }, ++{ 0x1, 350, 0, 2651 }, ++{ 0x8, 349, 0, 2645 }, ++{ 0x4, 349, 0, 2647 }, ++{ 0x2, 349, 1, 2649 }, ++{ 0x4, 143, 0, 1345 }, ++{ 0x1, 349, 0, 2652 }, ++{ 0x1, 6, 0, 2653 }, ++{ 0x1, 7, 0, 2654 }, ++{ 0x1, 295, 0, 2655 }, ++{ 0x1, 451, 0, 2656 }, ++{ 0x1, 346, 0, 2657 }, ++{ 0x1, 13, 0, 2658 }, ++{ 0x1, 11, 0, 2659 }, ++{ 0x1, 422, 0, 2660 }, ++{ 0x1, 394, 0, 2661 }, ++{ 0x1, 393, 0, 2662 }, ++{ 0x1, 450, 0, 2663 }, ++{ 0x1, 345, 0, 2664 }, ++{ 0x1, 12, 0, 2665 }, ++{ 0x1, 10, 0, 2666 }, ++{ 0x1, 5, 0, 2667 }, ++{ 0x1, 421, 0, 2668 }, ++{ 0x1, 420, 0, 2669 }, ++{ 0x1, 1, 0, 2670 }, ++{ 0x1, 0, 0, 2671 }, ++}; ++ +--- /dev/null ++++ b/arch/ia64/kdb/ia64-asmtab.h +@@ -0,0 +1,158 @@ ++/* ia64-asmtab.h -- Header for compacted IA-64 opcode tables. ++ Copyright 1999, 2000 Free Software Foundation, Inc. ++ Contributed by Bob Manson of Cygnus Support ++ ++ This file is part of GDB, GAS, and the GNU binutils. ++ ++ GDB, GAS, and the GNU binutils are free software; you can redistribute ++ them and/or modify them under the terms of the GNU General Public ++ License as published by the Free Software Foundation; either version ++ 2, or (at your option) any later version. ++ ++ GDB, GAS, and the GNU binutils are distributed in the hope that they ++ will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ the GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this file; see the file COPYING. If not, write to the ++ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA ++ 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifndef IA64_ASMTAB_H ++#define IA64_ASMTAB_H ++ ++#ifdef __KERNEL__ ++#include "ia64.h" ++#else /* __KERNEL__ */ ++#include "opcode/ia64.h" ++#endif /* __KERNEL__ */ ++ ++/* The primary opcode table is made up of the following: */ ++struct ia64_main_table ++{ ++ /* The entry in the string table that corresponds to the name of this ++ opcode. */ ++ unsigned short name_index; ++ ++ /* The type of opcode; corresponds to the TYPE field in ++ struct ia64_opcode. */ ++ unsigned char opcode_type; ++ ++ /* The number of outputs for this opcode. */ ++ unsigned char num_outputs; ++ ++ /* The base insn value for this opcode. It may be modified by completers. */ ++ ia64_insn opcode; ++ ++ /* The mask of valid bits in OPCODE. Zeros indicate operand fields. */ ++ ia64_insn mask; ++ ++ /* The operands of this instruction. Corresponds to the OPERANDS field ++ in struct ia64_opcode. */ ++ unsigned char operands[5]; ++ ++ /* The flags for this instruction. Corresponds to the FLAGS field in ++ struct ia64_opcode. */ ++ short flags; ++ ++ /* The tree of completers for this instruction; this is an offset into ++ completer_table. */ ++ short completers; ++}; ++ ++/* Each instruction has a set of possible "completers", or additional ++ suffixes that can alter the instruction's behavior, and which has ++ potentially different dependencies. ++ ++ The completer entries modify certain bits in the instruction opcode. ++ Which bits are to be modified are marked by the BITS, MASK and ++ OFFSET fields. The completer entry may also note dependencies for the ++ opcode. ++ ++ These completers are arranged in a DAG; the pointers are indexes ++ into the completer_table array. The completer DAG is searched by ++ find_completer () and ia64_find_matching_opcode (). ++ ++ Note that each completer needs to be applied in turn, so that if we ++ have the instruction ++ cmp.lt.unc ++ the completer entries for both "lt" and "unc" would need to be applied ++ to the opcode's value. ++ ++ Some instructions do not require any completers; these contain an ++ empty completer entry. Instructions that require a completer do ++ not contain an empty entry. ++ ++ Terminal completers (those completers that validly complete an ++ instruction) are marked by having the TERMINAL_COMPLETER flag set. ++ ++ Only dependencies listed in the terminal completer for an opcode are ++ considered to apply to that opcode instance. */ ++ ++struct ia64_completer_table ++{ ++ /* The bit value that this completer sets. */ ++ unsigned int bits; ++ ++ /* And its mask. 1s are bits that are to be modified in the ++ instruction. */ ++ unsigned int mask; ++ ++ /* The entry in the string table that corresponds to the name of this ++ completer. */ ++ unsigned short name_index; ++ ++ /* An alternative completer, or -1 if this is the end of the chain. */ ++ short alternative; ++ ++ /* A pointer to the DAG of completers that can potentially follow ++ this one, or -1. */ ++ short subentries; ++ ++ /* The bit offset in the instruction where BITS and MASK should be ++ applied. */ ++ unsigned char offset : 7; ++ ++ unsigned char terminal_completer : 1; ++ ++ /* Index into the dependency list table */ ++ short dependencies; ++}; ++ ++/* This contains sufficient information for the disassembler to resolve ++ the complete name of the original instruction. */ ++struct ia64_dis_names ++{ ++ /* COMPLETER_INDEX represents the tree of completers that make up ++ the instruction. The LSB represents the top of the tree for the ++ specified instruction. ++ ++ A 0 bit indicates to go to the next alternate completer via the ++ alternative field; a 1 bit indicates that the current completer ++ is part of the instruction, and to go down the subentries index. ++ We know we've reached the final completer when we run out of 1 ++ bits. ++ ++ There is always at least one 1 bit. */ ++ unsigned int completer_index : 20; ++ ++ /* The index in the main_table[] array for the instruction. */ ++ unsigned short insn_index : 11; ++ ++ /* If set, the next entry in this table is an alternate possibility ++ for this instruction encoding. Which one to use is determined by ++ the instruction type and other factors (see opcode_verify ()). */ ++ unsigned int next_flag : 1; ++ ++ /* The disassembly priority of this entry among instructions. */ ++ unsigned short priority; ++}; ++ ++#endif +--- /dev/null ++++ b/arch/ia64/kdb/ia64-dis.c +@@ -0,0 +1,312 @@ ++/* ia64-dis.c -- Disassemble ia64 instructions ++ Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc. ++ Contributed by David Mosberger-Tang ++ ++ This file is part of GDB, GAS, and the GNU binutils. ++ ++ GDB, GAS, and the GNU binutils are free software; you can redistribute ++ them and/or modify them under the terms of the GNU General Public ++ License as published by the Free Software Foundation; either version ++ 2, or (at your option) any later version. ++ ++ GDB, GAS, and the GNU binutils are distributed in the hope that they ++ will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ the GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this file; see the file COPYING. If not, write to the ++ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA ++ 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++#include "ia64.h" ++ ++/* imported from bfd/libbfd.c for kernel */ ++bfd_uint64_t ++bfd_getl64 (const void *p ATTRIBUTE_UNUSED) ++{ ++#ifdef BFD_HOST_64_BIT ++ const bfd_byte *addr = p; ++ bfd_uint64_t v; ++ ++ v = addr[7]; v <<= 8; ++ v |= addr[6]; v <<= 8; ++ v |= addr[5]; v <<= 8; ++ v |= addr[4]; v <<= 8; ++ v |= addr[3]; v <<= 8; ++ v |= addr[2]; v <<= 8; ++ v |= addr[1]; v <<= 8; ++ v |= addr[0]; ++ ++ return v; ++#else ++ BFD_FAIL(); ++ return 0; ++#endif ++ ++} ++ ++#else /* __KERNEL__ */ ++#include ++#include ++ ++#include "dis-asm.h" ++#include "opcode/ia64.h" ++#endif /* __KERNEL__ */ ++ ++#define NELEMS(a) ((int) (sizeof (a) / sizeof (a[0]))) ++ ++/* Disassemble ia64 instruction. */ ++ ++/* Return the instruction type for OPCODE found in unit UNIT. */ ++ ++static enum ia64_insn_type ++unit_to_type (ia64_insn opcode, enum ia64_unit unit) ++{ ++ enum ia64_insn_type type; ++ int op; ++ ++ op = IA64_OP (opcode); ++ ++ if (op >= 8 && (unit == IA64_UNIT_I || unit == IA64_UNIT_M)) ++ { ++ type = IA64_TYPE_A; ++ } ++ else ++ { ++ switch (unit) ++ { ++ case IA64_UNIT_I: ++ type = IA64_TYPE_I; break; ++ case IA64_UNIT_M: ++ type = IA64_TYPE_M; break; ++ case IA64_UNIT_B: ++ type = IA64_TYPE_B; break; ++ case IA64_UNIT_F: ++ type = IA64_TYPE_F; break; ++ case IA64_UNIT_L: ++ case IA64_UNIT_X: ++ type = IA64_TYPE_X; break; ++ default: ++ type = -1; ++ } ++ } ++ return type; ++} ++ ++int ++print_insn_ia64 (bfd_vma memaddr, struct disassemble_info *info) ++{ ++ ia64_insn t0, t1, slot[3], template, s_bit, insn; ++ int slotnum, j, status, need_comma, retval, slot_multiplier; ++ const struct ia64_operand *odesc; ++ const struct ia64_opcode *idesc; ++ const char *err, *str, *tname; ++ BFD_HOST_U_64_BIT value; ++ bfd_byte bundle[16]; ++ enum ia64_unit unit; ++ char regname[16]; ++ ++ if (info->bytes_per_line == 0) ++ info->bytes_per_line = 6; ++ info->display_endian = info->endian; ++ ++ slot_multiplier = info->bytes_per_line; ++ retval = slot_multiplier; ++ ++ slotnum = (((long) memaddr) & 0xf) / slot_multiplier; ++ if (slotnum > 2) ++ return -1; ++ ++ memaddr -= (memaddr & 0xf); ++ status = (*info->read_memory_func) (memaddr, bundle, sizeof (bundle), info); ++ if (status != 0) ++ { ++ (*info->memory_error_func) (status, memaddr, info); ++ return -1; ++ } ++ /* bundles are always in little-endian byte order */ ++ t0 = bfd_getl64 (bundle); ++ t1 = bfd_getl64 (bundle + 8); ++ s_bit = t0 & 1; ++ template = (t0 >> 1) & 0xf; ++ slot[0] = (t0 >> 5) & 0x1ffffffffffLL; ++ slot[1] = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18); ++ slot[2] = (t1 >> 23) & 0x1ffffffffffLL; ++ ++ tname = ia64_templ_desc[template].name; ++ if (slotnum == 0) ++ (*info->fprintf_func) (info->stream, "[%s] ", tname); ++ else ++ (*info->fprintf_func) (info->stream, " "); ++ ++ unit = ia64_templ_desc[template].exec_unit[slotnum]; ++ ++ if (template == 2 && slotnum == 1) ++ { ++ /* skip L slot in MLI template: */ ++ slotnum = 2; ++ retval += slot_multiplier; ++ } ++ ++ insn = slot[slotnum]; ++ ++ if (unit == IA64_UNIT_NIL) ++ goto decoding_failed; ++ ++ idesc = ia64_dis_opcode (insn, unit_to_type (insn, unit)); ++ if (idesc == NULL) ++ goto decoding_failed; ++ ++ /* print predicate, if any: */ ++ ++ if ((idesc->flags & IA64_OPCODE_NO_PRED) ++ || (insn & 0x3f) == 0) ++ (*info->fprintf_func) (info->stream, " "); ++ else ++ (*info->fprintf_func) (info->stream, "(p%02d) ", (int)(insn & 0x3f)); ++ ++ /* now the actual instruction: */ ++ ++ (*info->fprintf_func) (info->stream, "%s", idesc->name); ++ if (idesc->operands[0]) ++ (*info->fprintf_func) (info->stream, " "); ++ ++ need_comma = 0; ++ for (j = 0; j < NELEMS (idesc->operands) && idesc->operands[j]; ++j) ++ { ++ odesc = elf64_ia64_operands + idesc->operands[j]; ++ ++ if (need_comma) ++ (*info->fprintf_func) (info->stream, ","); ++ ++ if (odesc - elf64_ia64_operands == IA64_OPND_IMMU64) ++ { ++ /* special case of 64 bit immediate load: */ ++ value = ((insn >> 13) & 0x7f) | (((insn >> 27) & 0x1ff) << 7) ++ | (((insn >> 22) & 0x1f) << 16) | (((insn >> 21) & 0x1) << 21) ++ | (slot[1] << 22) | (((insn >> 36) & 0x1) << 63); ++ } ++ else if (odesc - elf64_ia64_operands == IA64_OPND_IMMU62) ++ { ++ /* 62-bit immediate for nop.x/break.x */ ++ value = ((slot[1] & 0x1ffffffffffLL) << 21) ++ | (((insn >> 36) & 0x1) << 20) ++ | ((insn >> 6) & 0xfffff); ++ } ++ else if (odesc - elf64_ia64_operands == IA64_OPND_TGT64) ++ { ++ /* 60-bit immediate for long branches. */ ++ value = (((insn >> 13) & 0xfffff) ++ | (((insn >> 36) & 1) << 59) ++ | (((slot[1] >> 2) & 0x7fffffffffLL) << 20)) << 4; ++ } ++ else ++ { ++ err = (*odesc->extract) (odesc, insn, &value); ++ if (err) ++ { ++ (*info->fprintf_func) (info->stream, "%s", err); ++ goto done; ++ } ++ } ++ ++ switch (odesc->class) ++ { ++ case IA64_OPND_CLASS_CST: ++ (*info->fprintf_func) (info->stream, "%s", odesc->str); ++ break; ++ ++ case IA64_OPND_CLASS_REG: ++ if (odesc->str[0] == 'a' && odesc->str[1] == 'r') ++ { ++ switch (value) ++ { ++ case 0: case 1: case 2: case 3: ++ case 4: case 5: case 6: case 7: ++ sprintf (regname, "ar.k%u", (unsigned int) value); ++ break; ++ case 16: strcpy (regname, "ar.rsc"); break; ++ case 17: strcpy (regname, "ar.bsp"); break; ++ case 18: strcpy (regname, "ar.bspstore"); break; ++ case 19: strcpy (regname, "ar.rnat"); break; ++ case 32: strcpy (regname, "ar.ccv"); break; ++ case 36: strcpy (regname, "ar.unat"); break; ++ case 40: strcpy (regname, "ar.fpsr"); break; ++ case 44: strcpy (regname, "ar.itc"); break; ++ case 64: strcpy (regname, "ar.pfs"); break; ++ case 65: strcpy (regname, "ar.lc"); break; ++ case 66: strcpy (regname, "ar.ec"); break; ++ default: ++ sprintf (regname, "ar%u", (unsigned int) value); ++ break; ++ } ++ (*info->fprintf_func) (info->stream, "%s", regname); ++ } ++ else ++ (*info->fprintf_func) (info->stream, "%s%d", odesc->str, (int)value); ++ break; ++ ++ case IA64_OPND_CLASS_IND: ++ (*info->fprintf_func) (info->stream, "%s[r%d]", odesc->str, (int)value); ++ break; ++ ++ case IA64_OPND_CLASS_ABS: ++ str = 0; ++ if (odesc - elf64_ia64_operands == IA64_OPND_MBTYPE4) ++ switch (value) ++ { ++ case 0x0: str = "@brcst"; break; ++ case 0x8: str = "@mix"; break; ++ case 0x9: str = "@shuf"; break; ++ case 0xa: str = "@alt"; break; ++ case 0xb: str = "@rev"; break; ++ } ++ ++ if (str) ++ (*info->fprintf_func) (info->stream, "%s", str); ++ else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_SIGNED) ++ (*info->fprintf_func) (info->stream, "%lld", (long long) value); ++ else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_UNSIGNED) ++ (*info->fprintf_func) (info->stream, "%llu", (long long) value); ++ else ++ (*info->fprintf_func) (info->stream, "0x%llx", (long long) value); ++ break; ++ ++ case IA64_OPND_CLASS_REL: ++ (*info->print_address_func) (memaddr + value, info); ++ break; ++ } ++ ++ need_comma = 1; ++ if (j + 1 == idesc->num_outputs) ++ { ++ (*info->fprintf_func) (info->stream, "="); ++ need_comma = 0; ++ } ++ } ++ if (slotnum + 1 == ia64_templ_desc[template].group_boundary ++ || ((slotnum == 2) && s_bit)) ++ (*info->fprintf_func) (info->stream, ";;"); ++ ++ done: ++ ia64_free_opcode ((struct ia64_opcode *)idesc); ++ failed: ++ if (slotnum == 2) ++ retval += 16 - 3*slot_multiplier; ++ return retval; ++ ++ decoding_failed: ++ (*info->fprintf_func) (info->stream, " data8 %#011llx", (long long) insn); ++ goto failed; ++} +--- /dev/null ++++ b/arch/ia64/kdb/ia64-opc.c +@@ -0,0 +1,749 @@ ++/* ia64-opc.c -- Functions to access the compacted opcode table ++ Copyright 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. ++ Written by Bob Manson of Cygnus Solutions, ++ ++ This file is part of GDB, GAS, and the GNU binutils. ++ ++ GDB, GAS, and the GNU binutils are free software; you can redistribute ++ them and/or modify them under the terms of the GNU General Public ++ License as published by the Free Software Foundation; either version ++ 2, or (at your option) any later version. ++ ++ GDB, GAS, and the GNU binutils are distributed in the hope that they ++ will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ the GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this file; see the file COPYING. If not, write to the ++ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA ++ 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++#include ++ ++#define xstrdup(string) ({ char *res = kdb_strdup(string, GFP_ATOMIC); if (!res) BUG(); res; }) ++#define xmalloc(size) ({ void *res = debug_kmalloc(size, GFP_ATOMIC); if (!res) BUG(); res; }) ++#define free(address) debug_kfree(address) ++#define abort() BUG() ++ ++#else /* __KERNEL__ */ ++#include "ansidecl.h" ++#include "sysdep.h" ++#include "libiberty.h" ++#endif /* __KERNEL__ */ ++#include "ia64-asmtab.h" ++#include "ia64-asmtab.c" ++ ++static void get_opc_prefix (const char **, char *); ++static short int find_string_ent (const char *); ++static short int find_main_ent (short int); ++static short int find_completer (short int, short int, const char *); ++static ia64_insn apply_completer (ia64_insn, int); ++static int extract_op_bits (int, int, int); ++static int extract_op (int, int *, unsigned int *); ++static int opcode_verify (ia64_insn, int, enum ia64_insn_type); ++static int locate_opcode_ent (ia64_insn, enum ia64_insn_type); ++static struct ia64_opcode *make_ia64_opcode ++ (ia64_insn, const char *, int, int); ++static struct ia64_opcode *ia64_find_matching_opcode ++ (const char *, short int); ++ ++const struct ia64_templ_desc ia64_templ_desc[16] = ++ { ++ { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, /* 0 */ ++ { 2, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, ++ { 0, { IA64_UNIT_M, IA64_UNIT_L, IA64_UNIT_X }, "MLX" }, ++ { 0, { 0, }, "-3-" }, ++ { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, /* 4 */ ++ { 1, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, ++ { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_I }, "MFI" }, ++ { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_F }, "MMF" }, ++ { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_B }, "MIB" }, /* 8 */ ++ { 0, { IA64_UNIT_M, IA64_UNIT_B, IA64_UNIT_B }, "MBB" }, ++ { 0, { 0, }, "-a-" }, ++ { 0, { IA64_UNIT_B, IA64_UNIT_B, IA64_UNIT_B }, "BBB" }, ++ { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_B }, "MMB" }, /* c */ ++ { 0, { 0, }, "-d-" }, ++ { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_B }, "MFB" }, ++ { 0, { 0, }, "-f-" }, ++ }; ++ ++ ++/* Copy the prefix contained in *PTR (up to a '.' or a NUL) to DEST. ++ PTR will be adjusted to point to the start of the next portion ++ of the opcode, or at the NUL character. */ ++ ++static void ++get_opc_prefix (const char **ptr, char *dest) ++{ ++ char *c = strchr (*ptr, '.'); ++ if (c != NULL) ++ { ++ memcpy (dest, *ptr, c - *ptr); ++ dest[c - *ptr] = '\0'; ++ *ptr = c + 1; ++ } ++ else ++ { ++ int l = strlen (*ptr); ++ memcpy (dest, *ptr, l); ++ dest[l] = '\0'; ++ *ptr += l; ++ } ++} ++ ++/* Find the index of the entry in the string table corresponding to ++ STR; return -1 if one does not exist. */ ++ ++static short ++find_string_ent (const char *str) ++{ ++ short start = 0; ++ short end = sizeof (ia64_strings) / sizeof (const char *); ++ short i = (start + end) / 2; ++ ++ if (strcmp (str, ia64_strings[end - 1]) > 0) ++ { ++ return -1; ++ } ++ while (start <= end) ++ { ++ int c = strcmp (str, ia64_strings[i]); ++ if (c < 0) ++ { ++ end = i - 1; ++ } ++ else if (c == 0) ++ { ++ return i; ++ } ++ else ++ { ++ start = i + 1; ++ } ++ i = (start + end) / 2; ++ } ++ return -1; ++} ++ ++/* Find the opcode in the main opcode table whose name is STRINGINDEX, or ++ return -1 if one does not exist. */ ++ ++static short ++find_main_ent (short nameindex) ++{ ++ short start = 0; ++ short end = sizeof (main_table) / sizeof (struct ia64_main_table); ++ short i = (start + end) / 2; ++ ++ if (nameindex < main_table[0].name_index ++ || nameindex > main_table[end - 1].name_index) ++ { ++ return -1; ++ } ++ while (start <= end) ++ { ++ if (nameindex < main_table[i].name_index) ++ { ++ end = i - 1; ++ } ++ else if (nameindex == main_table[i].name_index) ++ { ++ while (i > 0 && main_table[i - 1].name_index == nameindex) ++ { ++ i--; ++ } ++ return i; ++ } ++ else ++ { ++ start = i + 1; ++ } ++ i = (start + end) / 2; ++ } ++ return -1; ++} ++ ++/* Find the index of the entry in the completer table that is part of ++ MAIN_ENT (starting from PREV_COMPLETER) that matches NAME, or ++ return -1 if one does not exist. */ ++ ++static short ++find_completer (short main_ent, short prev_completer, const char *name) ++{ ++ short name_index = find_string_ent (name); ++ ++ if (name_index < 0) ++ { ++ return -1; ++ } ++ ++ if (prev_completer == -1) ++ { ++ prev_completer = main_table[main_ent].completers; ++ } ++ else ++ { ++ prev_completer = completer_table[prev_completer].subentries; ++ } ++ ++ while (prev_completer != -1) ++ { ++ if (completer_table[prev_completer].name_index == name_index) ++ { ++ return prev_completer; ++ } ++ prev_completer = completer_table[prev_completer].alternative; ++ } ++ return -1; ++} ++ ++/* Apply the completer referred to by COMPLETER_INDEX to OPCODE, and ++ return the result. */ ++ ++static ia64_insn ++apply_completer (ia64_insn opcode, int completer_index) ++{ ++ ia64_insn mask = completer_table[completer_index].mask; ++ ia64_insn bits = completer_table[completer_index].bits; ++ int shiftamt = (completer_table[completer_index].offset & 63); ++ ++ mask = mask << shiftamt; ++ bits = bits << shiftamt; ++ opcode = (opcode & ~mask) | bits; ++ return opcode; ++} ++ ++/* Extract BITS number of bits starting from OP_POINTER + BITOFFSET in ++ the dis_table array, and return its value. (BITOFFSET is numbered ++ starting from MSB to LSB, so a BITOFFSET of 0 indicates the MSB of the ++ first byte in OP_POINTER.) */ ++ ++static int ++extract_op_bits (int op_pointer, int bitoffset, int bits) ++{ ++ int res = 0; ++ ++ op_pointer += (bitoffset / 8); ++ ++ if (bitoffset % 8) ++ { ++ unsigned int op = dis_table[op_pointer++]; ++ int numb = 8 - (bitoffset % 8); ++ int mask = (1 << numb) - 1; ++ int bata = (bits < numb) ? bits : numb; ++ int delta = numb - bata; ++ ++ res = (res << bata) | ((op & mask) >> delta); ++ bitoffset += bata; ++ bits -= bata; ++ } ++ while (bits >= 8) ++ { ++ res = (res << 8) | (dis_table[op_pointer++] & 255); ++ bits -= 8; ++ } ++ if (bits > 0) ++ { ++ unsigned int op = (dis_table[op_pointer++] & 255); ++ res = (res << bits) | (op >> (8 - bits)); ++ } ++ return res; ++} ++ ++/* Examine the state machine entry at OP_POINTER in the dis_table ++ array, and extract its values into OPVAL and OP. The length of the ++ state entry in bits is returned. */ ++ ++static int ++extract_op (int op_pointer, int *opval, unsigned int *op) ++{ ++ int oplen = 5; ++ ++ *op = dis_table[op_pointer]; ++ ++ if ((*op) & 0x40) ++ { ++ opval[0] = extract_op_bits (op_pointer, oplen, 5); ++ oplen += 5; ++ } ++ switch ((*op) & 0x30) ++ { ++ case 0x10: ++ { ++ opval[1] = extract_op_bits (op_pointer, oplen, 8); ++ oplen += 8; ++ opval[1] += op_pointer; ++ break; ++ } ++ case 0x20: ++ { ++ opval[1] = extract_op_bits (op_pointer, oplen, 16); ++ if (! (opval[1] & 32768)) ++ { ++ opval[1] += op_pointer; ++ } ++ oplen += 16; ++ break; ++ } ++ case 0x30: ++ { ++ oplen--; ++ opval[2] = extract_op_bits (op_pointer, oplen, 12); ++ oplen += 12; ++ opval[2] |= 32768; ++ break; ++ } ++ } ++ if (((*op) & 0x08) && (((*op) & 0x30) != 0x30)) ++ { ++ opval[2] = extract_op_bits (op_pointer, oplen, 16); ++ oplen += 16; ++ if (! (opval[2] & 32768)) ++ { ++ opval[2] += op_pointer; ++ } ++ } ++ return oplen; ++} ++ ++/* Returns a non-zero value if the opcode in the main_table list at ++ PLACE matches OPCODE and is of type TYPE. */ ++ ++static int ++opcode_verify (ia64_insn opcode, int place, enum ia64_insn_type type) ++{ ++ if (main_table[place].opcode_type != type) ++ { ++ return 0; ++ } ++ if (main_table[place].flags ++ & (IA64_OPCODE_F2_EQ_F3 | IA64_OPCODE_LEN_EQ_64MCNT)) ++ { ++ const struct ia64_operand *o1, *o2; ++ ia64_insn f2, f3; ++ ++ if (main_table[place].flags & IA64_OPCODE_F2_EQ_F3) ++ { ++ o1 = elf64_ia64_operands + IA64_OPND_F2; ++ o2 = elf64_ia64_operands + IA64_OPND_F3; ++ (*o1->extract) (o1, opcode, &f2); ++ (*o2->extract) (o2, opcode, &f3); ++ if (f2 != f3) ++ return 0; ++ } ++ else ++ { ++ ia64_insn len, count; ++ ++ /* length must equal 64-count: */ ++ o1 = elf64_ia64_operands + IA64_OPND_LEN6; ++ o2 = elf64_ia64_operands + main_table[place].operands[2]; ++ (*o1->extract) (o1, opcode, &len); ++ (*o2->extract) (o2, opcode, &count); ++ if (len != 64 - count) ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++/* Find an instruction entry in the ia64_dis_names array that matches ++ opcode OPCODE and is of type TYPE. Returns either a positive index ++ into the array, or a negative value if an entry for OPCODE could ++ not be found. Checks all matches and returns the one with the highest ++ priority. */ ++ ++static int ++locate_opcode_ent (ia64_insn opcode, enum ia64_insn_type type) ++{ ++ int currtest[41]; ++ int bitpos[41]; ++ int op_ptr[41]; ++ int currstatenum = 0; ++ short found_disent = -1; ++ short found_priority = -1; ++ ++ currtest[currstatenum] = 0; ++ op_ptr[currstatenum] = 0; ++ bitpos[currstatenum] = 40; ++ ++ while (1) ++ { ++ int op_pointer = op_ptr[currstatenum]; ++ unsigned int op; ++ int currbitnum = bitpos[currstatenum]; ++ int oplen; ++ int opval[3] = {0}; ++ int next_op; ++ int currbit; ++ ++ oplen = extract_op (op_pointer, opval, &op); ++ ++ bitpos[currstatenum] = currbitnum; ++ ++ /* Skip opval[0] bits in the instruction. */ ++ if (op & 0x40) ++ { ++ currbitnum -= opval[0]; ++ } ++ ++ /* The value of the current bit being tested. */ ++ currbit = opcode & (((ia64_insn) 1) << currbitnum) ? 1 : 0; ++ next_op = -1; ++ ++ /* We always perform the tests specified in the current state in ++ a particular order, falling through to the next test if the ++ previous one failed. */ ++ switch (currtest[currstatenum]) ++ { ++ case 0: ++ currtest[currstatenum]++; ++ if (currbit == 0 && (op & 0x80)) ++ { ++ /* Check for a zero bit. If this test solely checks for ++ a zero bit, we can check for up to 8 consecutive zero ++ bits (the number to check is specified by the lower 3 ++ bits in the state code.) ++ ++ If the state instruction matches, we go to the very ++ next state instruction; otherwise, try the next test. */ ++ ++ if ((op & 0xf8) == 0x80) ++ { ++ int count = op & 0x7; ++ int x; ++ ++ for (x = 0; x <= count; x++) ++ { ++ int i = ++ opcode & (((ia64_insn) 1) << (currbitnum - x)) ? 1 : 0; ++ if (i) ++ { ++ break; ++ } ++ } ++ if (x > count) ++ { ++ next_op = op_pointer + ((oplen + 7) / 8); ++ currbitnum -= count; ++ break; ++ } ++ } ++ else if (! currbit) ++ { ++ next_op = op_pointer + ((oplen + 7) / 8); ++ break; ++ } ++ } ++ /* FALLTHROUGH */ ++ case 1: ++ /* If the bit in the instruction is one, go to the state ++ instruction specified by opval[1]. */ ++ currtest[currstatenum]++; ++ if (currbit && (op & 0x30) != 0 && ((op & 0x30) != 0x30)) ++ { ++ next_op = opval[1]; ++ break; ++ } ++ /* FALLTHROUGH */ ++ case 2: ++ /* Don't care. Skip the current bit and go to the state ++ instruction specified by opval[2]. ++ ++ An encoding of 0x30 is special; this means that a 12-bit ++ offset into the ia64_dis_names[] array is specified. */ ++ currtest[currstatenum]++; ++ if ((op & 0x08) || ((op & 0x30) == 0x30)) ++ { ++ next_op = opval[2]; ++ break; ++ } ++ } ++ ++ /* If bit 15 is set in the address of the next state, an offset ++ in the ia64_dis_names array was specified instead. We then ++ check to see if an entry in the list of opcodes matches the ++ opcode we were given; if so, we have succeeded. */ ++ ++ if ((next_op >= 0) && (next_op & 32768)) ++ { ++ short disent = next_op & 32767; ++ short priority = -1; ++ ++ if (next_op > 65535) ++ { ++ abort (); ++ } ++ ++ /* Run through the list of opcodes to check, trying to find ++ one that matches. */ ++ while (disent >= 0) ++ { ++ int place = ia64_dis_names[disent].insn_index; ++ ++ priority = ia64_dis_names[disent].priority; ++ ++ if (opcode_verify (opcode, place, type) ++ && priority > found_priority) ++ { ++ break; ++ } ++ if (ia64_dis_names[disent].next_flag) ++ { ++ disent++; ++ } ++ else ++ { ++ disent = -1; ++ } ++ } ++ ++ if (disent >= 0) ++ { ++ found_disent = disent; ++ found_priority = priority; ++ } ++ /* Try the next test in this state, regardless of whether a match ++ was found. */ ++ next_op = -2; ++ } ++ ++ /* next_op == -1 is "back up to the previous state". ++ next_op == -2 is "stay in this state and try the next test". ++ Otherwise, transition to the state indicated by next_op. */ ++ ++ if (next_op == -1) ++ { ++ currstatenum--; ++ if (currstatenum < 0) ++ { ++ return found_disent; ++ } ++ } ++ else if (next_op >= 0) ++ { ++ currstatenum++; ++ bitpos[currstatenum] = currbitnum - 1; ++ op_ptr[currstatenum] = next_op; ++ currtest[currstatenum] = 0; ++ } ++ } ++} ++ ++/* Construct an ia64_opcode entry based on OPCODE, NAME and PLACE. */ ++ ++static struct ia64_opcode * ++make_ia64_opcode (ia64_insn opcode, const char *name, int place, int depind) ++{ ++ struct ia64_opcode *res = ++ (struct ia64_opcode *) xmalloc (sizeof (struct ia64_opcode)); ++ res->name = xstrdup (name); ++ res->type = main_table[place].opcode_type; ++ res->num_outputs = main_table[place].num_outputs; ++ res->opcode = opcode; ++ res->mask = main_table[place].mask; ++ res->operands[0] = main_table[place].operands[0]; ++ res->operands[1] = main_table[place].operands[1]; ++ res->operands[2] = main_table[place].operands[2]; ++ res->operands[3] = main_table[place].operands[3]; ++ res->operands[4] = main_table[place].operands[4]; ++ res->flags = main_table[place].flags; ++ res->ent_index = place; ++ res->dependencies = &op_dependencies[depind]; ++ return res; ++} ++ ++/* Determine the ia64_opcode entry for the opcode specified by INSN ++ and TYPE. If a valid entry is not found, return NULL. */ ++struct ia64_opcode * ++ia64_dis_opcode (ia64_insn insn, enum ia64_insn_type type) ++{ ++ int disent = locate_opcode_ent (insn, type); ++ ++ if (disent < 0) ++ { ++ return NULL; ++ } ++ else ++ { ++ unsigned int cb = ia64_dis_names[disent].completer_index; ++ static char name[128]; ++ int place = ia64_dis_names[disent].insn_index; ++ int ci = main_table[place].completers; ++ ia64_insn tinsn = main_table[place].opcode; ++ ++ strcpy (name, ia64_strings [main_table[place].name_index]); ++ ++ while (cb) ++ { ++ if (cb & 1) ++ { ++ int cname = completer_table[ci].name_index; ++ ++ tinsn = apply_completer (tinsn, ci); ++ ++ if (ia64_strings[cname][0] != '\0') ++ { ++ strcat (name, "."); ++ strcat (name, ia64_strings[cname]); ++ } ++ if (cb != 1) ++ { ++ ci = completer_table[ci].subentries; ++ } ++ } ++ else ++ { ++ ci = completer_table[ci].alternative; ++ } ++ if (ci < 0) ++ { ++ abort (); ++ } ++ cb = cb >> 1; ++ } ++ if (tinsn != (insn & main_table[place].mask)) ++ { ++ abort (); ++ } ++ return make_ia64_opcode (insn, name, place, ++ completer_table[ci].dependencies); ++ } ++} ++ ++/* Search the main_opcode table starting from PLACE for an opcode that ++ matches NAME. Return NULL if one is not found. */ ++ ++static struct ia64_opcode * ++ia64_find_matching_opcode (const char *name, short place) ++{ ++ char op[129]; ++ const char *suffix; ++ short name_index; ++ ++ if (strlen (name) > 128) ++ { ++ return NULL; ++ } ++ suffix = name; ++ get_opc_prefix (&suffix, op); ++ name_index = find_string_ent (op); ++ if (name_index < 0) ++ { ++ return NULL; ++ } ++ ++ while (main_table[place].name_index == name_index) ++ { ++ const char *curr_suffix = suffix; ++ ia64_insn curr_insn = main_table[place].opcode; ++ short completer = -1; ++ ++ do { ++ if (suffix[0] == '\0') ++ { ++ completer = find_completer (place, completer, suffix); ++ } ++ else ++ { ++ get_opc_prefix (&curr_suffix, op); ++ completer = find_completer (place, completer, op); ++ } ++ if (completer != -1) ++ { ++ curr_insn = apply_completer (curr_insn, completer); ++ } ++ } while (completer != -1 && curr_suffix[0] != '\0'); ++ ++ if (completer != -1 && curr_suffix[0] == '\0' ++ && completer_table[completer].terminal_completer) ++ { ++ int depind = completer_table[completer].dependencies; ++ return make_ia64_opcode (curr_insn, name, place, depind); ++ } ++ else ++ { ++ place++; ++ } ++ } ++ return NULL; ++} ++ ++/* Find the next opcode after PREV_ENT that matches PREV_ENT, or return NULL ++ if one does not exist. ++ ++ It is the caller's responsibility to invoke ia64_free_opcode () to ++ release any resources used by the returned entry. */ ++ ++struct ia64_opcode * ++ia64_find_next_opcode (struct ia64_opcode *prev_ent) ++{ ++ return ia64_find_matching_opcode (prev_ent->name, ++ prev_ent->ent_index + 1); ++} ++ ++/* Find the first opcode that matches NAME, or return NULL if it does ++ not exist. ++ ++ It is the caller's responsibility to invoke ia64_free_opcode () to ++ release any resources used by the returned entry. */ ++ ++struct ia64_opcode * ++ia64_find_opcode (const char *name) ++{ ++ char op[129]; ++ const char *suffix; ++ short place; ++ short name_index; ++ ++ if (strlen (name) > 128) ++ { ++ return NULL; ++ } ++ suffix = name; ++ get_opc_prefix (&suffix, op); ++ name_index = find_string_ent (op); ++ if (name_index < 0) ++ { ++ return NULL; ++ } ++ ++ place = find_main_ent (name_index); ++ ++ if (place < 0) ++ { ++ return NULL; ++ } ++ return ia64_find_matching_opcode (name, place); ++} ++ ++/* Free any resources used by ENT. */ ++void ++ia64_free_opcode (struct ia64_opcode *ent) ++{ ++ free ((void *)ent->name); ++ free (ent); ++} ++ ++const struct ia64_dependency * ++ia64_find_dependency (int index) ++{ ++ index = DEP(index); ++ ++ if (index < 0 ++ || index >= (int)(sizeof(dependencies) / sizeof(dependencies[0]))) ++ return NULL; ++ ++ return &dependencies[index]; ++} +--- /dev/null ++++ b/arch/ia64/kdb/ia64-opc.h +@@ -0,0 +1,141 @@ ++/* ia64-opc.h -- IA-64 opcode table. ++ Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc. ++ Contributed by David Mosberger-Tang ++ ++ This file is part of GDB, GAS, and the GNU binutils. ++ ++ GDB, GAS, and the GNU binutils are free software; you can redistribute ++ them and/or modify them under the terms of the GNU General Public ++ License as published by the Free Software Foundation; either version ++ 2, or (at your option) any later version. ++ ++ GDB, GAS, and the GNU binutils are distributed in the hope that they ++ will be useful, but WITHOUT ANY WARRANTY; without even the implied ++ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ the GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this file; see the file COPYING. If not, write to the ++ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA ++ 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifndef IA64_OPC_H ++#define IA64_OPC_H ++ ++#ifdef __KERNEL__ ++#include "ia64.h" ++#else /* __KERNEL__ */ ++#include "opcode/ia64.h" ++#endif /* __KERNEL__ */ ++ ++/* define a couple of abbreviations: */ ++ ++#define bOp(x) (((ia64_insn) ((x) & 0xf)) << 37) ++#define mOp bOp (-1) ++#define Op(x) bOp (x), mOp ++ ++#define FIRST IA64_OPCODE_FIRST ++#define X_IN_MLX IA64_OPCODE_X_IN_MLX ++#define LAST IA64_OPCODE_LAST ++#define PRIV IA64_OPCODE_PRIV ++#define NO_PRED IA64_OPCODE_NO_PRED ++#define SLOT2 IA64_OPCODE_SLOT2 ++#define PSEUDO IA64_OPCODE_PSEUDO ++#define F2_EQ_F3 IA64_OPCODE_F2_EQ_F3 ++#define LEN_EQ_64MCNT IA64_OPCODE_LEN_EQ_64MCNT ++#define MOD_RRBS IA64_OPCODE_MOD_RRBS ++#define POSTINC IA64_OPCODE_POSTINC ++ ++#define AR_CCV IA64_OPND_AR_CCV ++#define AR_PFS IA64_OPND_AR_PFS ++#define AR_CSD IA64_OPND_AR_CSD ++#define C1 IA64_OPND_C1 ++#define C8 IA64_OPND_C8 ++#define C16 IA64_OPND_C16 ++#define GR0 IA64_OPND_GR0 ++#define IP IA64_OPND_IP ++#define PR IA64_OPND_PR ++#define PR_ROT IA64_OPND_PR_ROT ++#define PSR IA64_OPND_PSR ++#define PSR_L IA64_OPND_PSR_L ++#define PSR_UM IA64_OPND_PSR_UM ++ ++#define AR3 IA64_OPND_AR3 ++#define B1 IA64_OPND_B1 ++#define B2 IA64_OPND_B2 ++#define CR3 IA64_OPND_CR3 ++#define F1 IA64_OPND_F1 ++#define F2 IA64_OPND_F2 ++#define F3 IA64_OPND_F3 ++#define F4 IA64_OPND_F4 ++#define P1 IA64_OPND_P1 ++#define P2 IA64_OPND_P2 ++#define R1 IA64_OPND_R1 ++#define R2 IA64_OPND_R2 ++#define R3 IA64_OPND_R3 ++#define R3_2 IA64_OPND_R3_2 ++ ++#define CPUID_R3 IA64_OPND_CPUID_R3 ++#define DBR_R3 IA64_OPND_DBR_R3 ++#define DTR_R3 IA64_OPND_DTR_R3 ++#define ITR_R3 IA64_OPND_ITR_R3 ++#define IBR_R3 IA64_OPND_IBR_R3 ++#define MR3 IA64_OPND_MR3 ++#define MSR_R3 IA64_OPND_MSR_R3 ++#define PKR_R3 IA64_OPND_PKR_R3 ++#define PMC_R3 IA64_OPND_PMC_R3 ++#define PMD_R3 IA64_OPND_PMD_R3 ++#define RR_R3 IA64_OPND_RR_R3 ++ ++#define CCNT5 IA64_OPND_CCNT5 ++#define CNT2a IA64_OPND_CNT2a ++#define CNT2b IA64_OPND_CNT2b ++#define CNT2c IA64_OPND_CNT2c ++#define CNT5 IA64_OPND_CNT5 ++#define CNT6 IA64_OPND_CNT6 ++#define CPOS6a IA64_OPND_CPOS6a ++#define CPOS6b IA64_OPND_CPOS6b ++#define CPOS6c IA64_OPND_CPOS6c ++#define IMM1 IA64_OPND_IMM1 ++#define IMM14 IA64_OPND_IMM14 ++#define IMM17 IA64_OPND_IMM17 ++#define IMM22 IA64_OPND_IMM22 ++#define IMM44 IA64_OPND_IMM44 ++#define SOF IA64_OPND_SOF ++#define SOL IA64_OPND_SOL ++#define SOR IA64_OPND_SOR ++#define IMM8 IA64_OPND_IMM8 ++#define IMM8U4 IA64_OPND_IMM8U4 ++#define IMM8M1 IA64_OPND_IMM8M1 ++#define IMM8M1U4 IA64_OPND_IMM8M1U4 ++#define IMM8M1U8 IA64_OPND_IMM8M1U8 ++#define IMM9a IA64_OPND_IMM9a ++#define IMM9b IA64_OPND_IMM9b ++#define IMMU2 IA64_OPND_IMMU2 ++#define IMMU21 IA64_OPND_IMMU21 ++#define IMMU24 IA64_OPND_IMMU24 ++#define IMMU62 IA64_OPND_IMMU62 ++#define IMMU64 IA64_OPND_IMMU64 ++#define IMMU7a IA64_OPND_IMMU7a ++#define IMMU7b IA64_OPND_IMMU7b ++#define IMMU9 IA64_OPND_IMMU9 ++#define INC3 IA64_OPND_INC3 ++#define LEN4 IA64_OPND_LEN4 ++#define LEN6 IA64_OPND_LEN6 ++#define MBTYPE4 IA64_OPND_MBTYPE4 ++#define MHTYPE8 IA64_OPND_MHTYPE8 ++#define POS6 IA64_OPND_POS6 ++#define TAG13 IA64_OPND_TAG13 ++#define TAG13b IA64_OPND_TAG13b ++#define TGT25 IA64_OPND_TGT25 ++#define TGT25b IA64_OPND_TGT25b ++#define TGT25c IA64_OPND_TGT25c ++#define TGT64 IA64_OPND_TGT64 ++ ++#endif +--- /dev/null ++++ b/arch/ia64/kdb/ia64.h +@@ -0,0 +1,402 @@ ++/* ia64.h -- Header file for ia64 opcode table ++ Copyright (C) 1998, 1999, 2000, 2002 Free Software Foundation, Inc. ++ Contributed by David Mosberger-Tang */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifndef opcode_ia64_h ++#define opcode_ia64_h ++ ++#ifdef __KERNEL__ ++#include ++#else /* __KERNEL__ */ ++#include ++ ++#include "bfd.h" ++#endif /* __KERNEL__ */ ++ ++ ++typedef BFD_HOST_U_64_BIT ia64_insn; ++ ++enum ia64_insn_type ++ { ++ IA64_TYPE_NIL = 0, /* illegal type */ ++ IA64_TYPE_A, /* integer alu (I- or M-unit) */ ++ IA64_TYPE_I, /* non-alu integer (I-unit) */ ++ IA64_TYPE_M, /* memory (M-unit) */ ++ IA64_TYPE_B, /* branch (B-unit) */ ++ IA64_TYPE_F, /* floating-point (F-unit) */ ++ IA64_TYPE_X, /* long encoding (X-unit) */ ++ IA64_TYPE_DYN, /* Dynamic opcode */ ++ IA64_NUM_TYPES ++ }; ++ ++enum ia64_unit ++ { ++ IA64_UNIT_NIL = 0, /* illegal unit */ ++ IA64_UNIT_I, /* integer unit */ ++ IA64_UNIT_M, /* memory unit */ ++ IA64_UNIT_B, /* branching unit */ ++ IA64_UNIT_F, /* floating-point unit */ ++ IA64_UNIT_L, /* long "unit" */ ++ IA64_UNIT_X, /* may be integer or branch unit */ ++ IA64_NUM_UNITS ++ }; ++ ++/* Changes to this enumeration must be propagated to the operand table in ++ bfd/cpu-ia64-opc.c ++ */ ++enum ia64_opnd ++ { ++ IA64_OPND_NIL, /* no operand---MUST BE FIRST!*/ ++ ++ /* constants */ ++ IA64_OPND_AR_CSD, /* application register csd (ar.csd) */ ++ IA64_OPND_AR_CCV, /* application register ccv (ar.ccv) */ ++ IA64_OPND_AR_PFS, /* application register pfs (ar.pfs) */ ++ IA64_OPND_C1, /* the constant 1 */ ++ IA64_OPND_C8, /* the constant 8 */ ++ IA64_OPND_C16, /* the constant 16 */ ++ IA64_OPND_GR0, /* gr0 */ ++ IA64_OPND_IP, /* instruction pointer (ip) */ ++ IA64_OPND_PR, /* predicate register (pr) */ ++ IA64_OPND_PR_ROT, /* rotating predicate register (pr.rot) */ ++ IA64_OPND_PSR, /* processor status register (psr) */ ++ IA64_OPND_PSR_L, /* processor status register L (psr.l) */ ++ IA64_OPND_PSR_UM, /* processor status register UM (psr.um) */ ++ ++ /* register operands: */ ++ IA64_OPND_AR3, /* third application register # (bits 20-26) */ ++ IA64_OPND_B1, /* branch register # (bits 6-8) */ ++ IA64_OPND_B2, /* branch register # (bits 13-15) */ ++ IA64_OPND_CR3, /* third control register # (bits 20-26) */ ++ IA64_OPND_F1, /* first floating-point register # */ ++ IA64_OPND_F2, /* second floating-point register # */ ++ IA64_OPND_F3, /* third floating-point register # */ ++ IA64_OPND_F4, /* fourth floating-point register # */ ++ IA64_OPND_P1, /* first predicate # */ ++ IA64_OPND_P2, /* second predicate # */ ++ IA64_OPND_R1, /* first register # */ ++ IA64_OPND_R2, /* second register # */ ++ IA64_OPND_R3, /* third register # */ ++ IA64_OPND_R3_2, /* third register # (limited to gr0-gr3) */ ++ ++ /* indirect operands: */ ++ IA64_OPND_CPUID_R3, /* cpuid[reg] */ ++ IA64_OPND_DBR_R3, /* dbr[reg] */ ++ IA64_OPND_DTR_R3, /* dtr[reg] */ ++ IA64_OPND_ITR_R3, /* itr[reg] */ ++ IA64_OPND_IBR_R3, /* ibr[reg] */ ++ IA64_OPND_MR3, /* memory at addr of third register # */ ++ IA64_OPND_MSR_R3, /* msr[reg] */ ++ IA64_OPND_PKR_R3, /* pkr[reg] */ ++ IA64_OPND_PMC_R3, /* pmc[reg] */ ++ IA64_OPND_PMD_R3, /* pmd[reg] */ ++ IA64_OPND_RR_R3, /* rr[reg] */ ++ ++ /* immediate operands: */ ++ IA64_OPND_CCNT5, /* 5-bit count (31 - bits 20-24) */ ++ IA64_OPND_CNT2a, /* 2-bit count (1 + bits 27-28) */ ++ IA64_OPND_CNT2b, /* 2-bit count (bits 27-28): 1, 2, 3 */ ++ IA64_OPND_CNT2c, /* 2-bit count (bits 30-31): 0, 7, 15, or 16 */ ++ IA64_OPND_CNT5, /* 5-bit count (bits 14-18) */ ++ IA64_OPND_CNT6, /* 6-bit count (bits 27-32) */ ++ IA64_OPND_CPOS6a, /* 6-bit count (63 - bits 20-25) */ ++ IA64_OPND_CPOS6b, /* 6-bit count (63 - bits 14-19) */ ++ IA64_OPND_CPOS6c, /* 6-bit count (63 - bits 31-36) */ ++ IA64_OPND_IMM1, /* signed 1-bit immediate (bit 36) */ ++ IA64_OPND_IMMU2, /* unsigned 2-bit immediate (bits 13-14) */ ++ IA64_OPND_IMMU7a, /* unsigned 7-bit immediate (bits 13-19) */ ++ IA64_OPND_IMMU7b, /* unsigned 7-bit immediate (bits 20-26) */ ++ IA64_OPND_SOF, /* 8-bit stack frame size */ ++ IA64_OPND_SOL, /* 8-bit size of locals */ ++ IA64_OPND_SOR, /* 6-bit number of rotating registers (scaled by 8) */ ++ IA64_OPND_IMM8, /* signed 8-bit immediate (bits 13-19 & 36) */ ++ IA64_OPND_IMM8U4, /* cmp4*u signed 8-bit immediate (bits 13-19 & 36) */ ++ IA64_OPND_IMM8M1, /* signed 8-bit immediate -1 (bits 13-19 & 36) */ ++ IA64_OPND_IMM8M1U4, /* cmp4*u signed 8-bit immediate -1 (bits 13-19 & 36)*/ ++ IA64_OPND_IMM8M1U8, /* cmp*u signed 8-bit immediate -1 (bits 13-19 & 36) */ ++ IA64_OPND_IMMU9, /* unsigned 9-bit immediate (bits 33-34, 20-26) */ ++ IA64_OPND_IMM9a, /* signed 9-bit immediate (bits 6-12, 27, 36) */ ++ IA64_OPND_IMM9b, /* signed 9-bit immediate (bits 13-19, 27, 36) */ ++ IA64_OPND_IMM14, /* signed 14-bit immediate (bits 13-19, 27-32, 36) */ ++ IA64_OPND_IMM17, /* signed 17-bit immediate (2*bits 6-12, 24-31, 36) */ ++ IA64_OPND_IMMU21, /* unsigned 21-bit immediate (bits 6-25, 36) */ ++ IA64_OPND_IMM22, /* signed 22-bit immediate (bits 13-19, 22-36) */ ++ IA64_OPND_IMMU24, /* unsigned 24-bit immediate (bits 6-26, 31-32, 36) */ ++ IA64_OPND_IMM44, /* signed 44-bit immediate (2^16*bits 6-32, 36) */ ++ IA64_OPND_IMMU62, /* unsigned 62-bit immediate */ ++ IA64_OPND_IMMU64, /* unsigned 64-bit immediate (lotsa bits...) */ ++ IA64_OPND_INC3, /* signed 3-bit (bits 13-15): +/-1, 4, 8, 16 */ ++ IA64_OPND_LEN4, /* 4-bit count (bits 27-30 + 1) */ ++ IA64_OPND_LEN6, /* 6-bit count (bits 27-32 + 1) */ ++ IA64_OPND_MBTYPE4, /* 4-bit mux type (bits 20-23) */ ++ IA64_OPND_MHTYPE8, /* 8-bit mux type (bits 20-27) */ ++ IA64_OPND_POS6, /* 6-bit count (bits 14-19) */ ++ IA64_OPND_TAG13, /* signed 13-bit tag (ip + 16*bits 6-12, 33-34) */ ++ IA64_OPND_TAG13b, /* signed 13-bit tag (ip + 16*bits 24-32) */ ++ IA64_OPND_TGT25, /* signed 25-bit (ip + 16*bits 6-25, 36) */ ++ IA64_OPND_TGT25b, /* signed 25-bit (ip + 16*bits 6-12, 20-32, 36) */ ++ IA64_OPND_TGT25c, /* signed 25-bit (ip + 16*bits 13-32, 36) */ ++ IA64_OPND_TGT64, /* 64-bit (ip + 16*bits 13-32, 36, 2-40(L)) */ ++ IA64_OPND_LDXMOV, /* any symbol, generates R_IA64_LDXMOV. */ ++ ++ IA64_OPND_COUNT /* # of operand types (MUST BE LAST!) */ ++ }; ++ ++enum ia64_dependency_mode ++{ ++ IA64_DV_RAW, ++ IA64_DV_WAW, ++ IA64_DV_WAR, ++}; ++ ++enum ia64_dependency_semantics ++{ ++ IA64_DVS_NONE, ++ IA64_DVS_IMPLIED, ++ IA64_DVS_IMPLIEDF, ++ IA64_DVS_DATA, ++ IA64_DVS_INSTR, ++ IA64_DVS_SPECIFIC, ++ IA64_DVS_STOP, ++ IA64_DVS_OTHER, ++}; ++ ++enum ia64_resource_specifier ++{ ++ IA64_RS_ANY, ++ IA64_RS_AR_K, ++ IA64_RS_AR_UNAT, ++ IA64_RS_AR, /* 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111 */ ++ IA64_RS_ARb, /* 48-63, 112-127 */ ++ IA64_RS_BR, ++ IA64_RS_CFM, ++ IA64_RS_CPUID, ++ IA64_RS_CR_IRR, ++ IA64_RS_CR_LRR, ++ IA64_RS_CR, /* 3-7,10-15,18,26-63,75-79,82-127 */ ++ IA64_RS_DBR, ++ IA64_RS_FR, ++ IA64_RS_FRb, ++ IA64_RS_GR0, ++ IA64_RS_GR, ++ IA64_RS_IBR, ++ IA64_RS_INSERVICE, /* CR[EOI] or CR[IVR] */ ++ IA64_RS_MSR, ++ IA64_RS_PKR, ++ IA64_RS_PMC, ++ IA64_RS_PMD, ++ IA64_RS_PR, /* non-rotating, 1-15 */ ++ IA64_RS_PRr, /* rotating, 16-62 */ ++ IA64_RS_PR63, ++ IA64_RS_RR, ++ ++ IA64_RS_ARX, /* ARs not in RS_AR or RS_ARb */ ++ IA64_RS_CRX, /* CRs not in RS_CR */ ++ IA64_RS_PSR, /* PSR bits */ ++ IA64_RS_RSE, /* implementation-specific RSE resources */ ++ IA64_RS_AR_FPSR, ++}; ++ ++enum ia64_rse_resource ++{ ++ IA64_RSE_N_STACKED_PHYS, ++ IA64_RSE_BOF, ++ IA64_RSE_STORE_REG, ++ IA64_RSE_LOAD_REG, ++ IA64_RSE_BSPLOAD, ++ IA64_RSE_RNATBITINDEX, ++ IA64_RSE_CFLE, ++ IA64_RSE_NDIRTY, ++}; ++ ++/* Information about a given resource dependency */ ++struct ia64_dependency ++{ ++ /* Name of the resource */ ++ const char *name; ++ /* Does this dependency need further specification? */ ++ enum ia64_resource_specifier specifier; ++ /* Mode of dependency */ ++ enum ia64_dependency_mode mode; ++ /* Dependency semantics */ ++ enum ia64_dependency_semantics semantics; ++ /* Register index, if applicable (distinguishes AR, CR, and PSR deps) */ ++#define REG_NONE (-1) ++ int regindex; ++ /* Special info on semantics */ ++ const char *info; ++}; ++ ++/* Two arrays of indexes into the ia64_dependency table. ++ chks are dependencies to check for conflicts when an opcode is ++ encountered; regs are dependencies to register (mark as used) when an ++ opcode is used. chks correspond to readers (RAW) or writers (WAW or ++ WAR) of a resource, while regs correspond to writers (RAW or WAW) and ++ readers (WAR) of a resource. */ ++struct ia64_opcode_dependency ++{ ++ int nchks; ++ const unsigned short *chks; ++ int nregs; ++ const unsigned short *regs; ++}; ++ ++/* encode/extract the note/index for a dependency */ ++#define RDEP(N,X) (((N)<<11)|(X)) ++#define NOTE(X) (((X)>>11)&0x1F) ++#define DEP(X) ((X)&0x7FF) ++ ++/* A template descriptor describes the execution units that are active ++ for each of the three slots. It also specifies the location of ++ instruction group boundaries that may be present between two slots. */ ++struct ia64_templ_desc ++ { ++ int group_boundary; /* 0=no boundary, 1=between slot 0 & 1, etc. */ ++ enum ia64_unit exec_unit[3]; ++ const char *name; ++ }; ++ ++/* The opcode table is an array of struct ia64_opcode. */ ++ ++struct ia64_opcode ++ { ++ /* The opcode name. */ ++ const char *name; ++ ++ /* The type of the instruction: */ ++ enum ia64_insn_type type; ++ ++ /* Number of output operands: */ ++ int num_outputs; ++ ++ /* The opcode itself. Those bits which will be filled in with ++ operands are zeroes. */ ++ ia64_insn opcode; ++ ++ /* The opcode mask. This is used by the disassembler. This is a ++ mask containing ones indicating those bits which must match the ++ opcode field, and zeroes indicating those bits which need not ++ match (and are presumably filled in by operands). */ ++ ia64_insn mask; ++ ++ /* An array of operand codes. Each code is an index into the ++ operand table. They appear in the order which the operands must ++ appear in assembly code, and are terminated by a zero. */ ++ enum ia64_opnd operands[5]; ++ ++ /* One bit flags for the opcode. These are primarily used to ++ indicate specific processors and environments support the ++ instructions. The defined values are listed below. */ ++ unsigned int flags; ++ ++ /* Used by ia64_find_next_opcode (). */ ++ short ent_index; ++ ++ /* Opcode dependencies. */ ++ const struct ia64_opcode_dependency *dependencies; ++ }; ++ ++/* Values defined for the flags field of a struct ia64_opcode. */ ++ ++#define IA64_OPCODE_FIRST (1<<0) /* must be first in an insn group */ ++#define IA64_OPCODE_X_IN_MLX (1<<1) /* insn is allowed in X slot of MLX */ ++#define IA64_OPCODE_LAST (1<<2) /* must be last in an insn group */ ++#define IA64_OPCODE_PRIV (1<<3) /* privileged instruct */ ++#define IA64_OPCODE_SLOT2 (1<<4) /* insn allowed in slot 2 only */ ++#define IA64_OPCODE_NO_PRED (1<<5) /* insn cannot be predicated */ ++#define IA64_OPCODE_PSEUDO (1<<6) /* insn is a pseudo-op */ ++#define IA64_OPCODE_F2_EQ_F3 (1<<7) /* constraint: F2 == F3 */ ++#define IA64_OPCODE_LEN_EQ_64MCNT (1<<8) /* constraint: LEN == 64-CNT */ ++#define IA64_OPCODE_MOD_RRBS (1<<9) /* modifies all rrbs in CFM */ ++#define IA64_OPCODE_POSTINC (1<<10) /* postincrement MR3 operand */ ++ ++/* A macro to extract the major opcode from an instruction. */ ++#define IA64_OP(i) (((i) >> 37) & 0xf) ++ ++enum ia64_operand_class ++ { ++ IA64_OPND_CLASS_CST, /* constant */ ++ IA64_OPND_CLASS_REG, /* register */ ++ IA64_OPND_CLASS_IND, /* indirect register */ ++ IA64_OPND_CLASS_ABS, /* absolute value */ ++ IA64_OPND_CLASS_REL, /* IP-relative value */ ++ }; ++ ++/* The operands table is an array of struct ia64_operand. */ ++ ++struct ia64_operand ++{ ++ enum ia64_operand_class class; ++ ++ /* Set VALUE as the operand bits for the operand of type SELF in the ++ instruction pointed to by CODE. If an error occurs, *CODE is not ++ modified and the returned string describes the cause of the ++ error. If no error occurs, NULL is returned. */ ++ const char *(*insert) (const struct ia64_operand *self, ia64_insn value, ++ ia64_insn *code); ++ ++ /* Extract the operand bits for an operand of type SELF from ++ instruction CODE store them in *VALUE. If an error occurs, the ++ cause of the error is described by the string returned. If no ++ error occurs, NULL is returned. */ ++ const char *(*extract) (const struct ia64_operand *self, ia64_insn code, ++ ia64_insn *value); ++ ++ /* A string whose meaning depends on the operand class. */ ++ ++ const char *str; ++ ++ struct bit_field ++ { ++ /* The number of bits in the operand. */ ++ int bits; ++ ++ /* How far the operand is left shifted in the instruction. */ ++ int shift; ++ } ++ field[4]; /* no operand has more than this many bit-fields */ ++ ++ unsigned int flags; ++ ++ const char *desc; /* brief description */ ++}; ++ ++/* Values defined for the flags field of a struct ia64_operand. */ ++ ++/* Disassemble as signed decimal (instead of hex): */ ++#define IA64_OPND_FLAG_DECIMAL_SIGNED (1<<0) ++/* Disassemble as unsigned decimal (instead of hex): */ ++#define IA64_OPND_FLAG_DECIMAL_UNSIGNED (1<<1) ++ ++extern const struct ia64_templ_desc ia64_templ_desc[16]; ++ ++/* The tables are sorted by major opcode number and are otherwise in ++ the order in which the disassembler should consider instructions. */ ++extern struct ia64_opcode ia64_opcodes_a[]; ++extern struct ia64_opcode ia64_opcodes_i[]; ++extern struct ia64_opcode ia64_opcodes_m[]; ++extern struct ia64_opcode ia64_opcodes_b[]; ++extern struct ia64_opcode ia64_opcodes_f[]; ++extern struct ia64_opcode ia64_opcodes_d[]; ++ ++ ++extern struct ia64_opcode *ia64_find_opcode (const char *name); ++extern struct ia64_opcode *ia64_find_next_opcode (struct ia64_opcode *ent); ++ ++extern struct ia64_opcode *ia64_dis_opcode (ia64_insn insn, ++ enum ia64_insn_type type); ++ ++extern void ia64_free_opcode (struct ia64_opcode *ent); ++extern const struct ia64_dependency *ia64_find_dependency (int index); ++ ++/* To avoid circular library dependencies, this array is implemented ++ in bfd/cpu-ia64-opc.c: */ ++extern const struct ia64_operand elf64_ia64_operands[IA64_OPND_COUNT]; ++ ++#endif /* opcode_ia64_h */ +--- /dev/null ++++ b/arch/ia64/kdb/kdb_cmds +@@ -0,0 +1,17 @@ ++# Standard architecture specific commands for kdb. ++# These commands are appended to those in kdb/kdb_cmds, see that file for ++# restrictions. ++ ++# Standard debugging information for first level support, invoked from archkdb* ++# commands that are defined in kdb/kdb_cmds. ++ ++defcmd archkdbcommon "" "Common arch debugging" ++ set LINES 2000000 ++ set BTAPROMPT 0 ++ -summary ++ -id %ip-0x40 ++ -cpu ++ -ps ++ -dmesg 600 ++ -bt ++endefcmd +--- /dev/null ++++ b/arch/ia64/kdb/kdba_bp.c +@@ -0,0 +1,841 @@ ++/* ++ * Kernel Debugger Architecture Dependent Breakpoint Handling ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++static char *kdba_rwtypes[] = { "Instruction(Register)", "Data Write", ++ "I/O", "Data Access"}; ++ ++/* ++ * Table describing processor architecture hardware ++ * breakpoint registers. ++ */ ++ ++static kdbhard_bp_t kdb_hardbreaks[KDB_MAXHARDBPT]; ++ ++#ifdef CONFIG_KDB_HARDWARE_BREAKPOINTS ++/* ++ * Counters for number of debug registers used on each CPU. ++ * Used to detect when to enable and disable debug traps. ++ */ ++static unsigned char kdb_dbrs_used[NR_CPUS]; ++#endif /* CONFIG_KDB_HARDWARE_BREAKPOINTS */ ++ ++/* ++ * kdba_db_trap ++ * ++ * Perform breakpoint processing upon entry to the ++ * processor debugger fault. Determine and print ++ * the active breakpoint. ++ * ++ * Parameters: ++ * regs Exception frame containing machine register state ++ * error Error number passed to kdb. ++ * Outputs: ++ * None. ++ * Returns: ++ * KDB_DB_BPT Standard instruction or data breakpoint encountered ++ * KDB_DB_SS Single Step fault ('ss' command or end of 'ssb' command) ++ * KDB_DB_SSB Single Step fault, caller should continue ('ssb' command) ++ * KDB_DB_SSBPT Single step over breakpoint ++ * KDB_DB_NOBPT No existing kdb breakpoint matches this debug exception ++ * Locking: ++ * None. ++ * Remarks: ++ * Yup, there be goto's here. ++ * ++ * If multiple processors receive debug exceptions simultaneously, ++ * one may be waiting at the kdb fence in kdb() while the user ++ * issues a 'bc' command to clear the breakpoint the processor ++ * which is waiting has already encountered. If this is the case, ++ * the debug registers will no longer match any entry in the ++ * breakpoint table, and we'll return the value KDB_DB_NOBPT. ++ * This can cause a panic in die_if_kernel(). It is safer to ++ * disable the breakpoint (bd), go until all processors are past ++ * the breakpoint then clear the breakpoint (bc). This code ++ * recognises a breakpoint even when disabled but not when it has ++ * been cleared. ++ * ++ * WARNING: This routine clears the debug state. It should be called ++ * once per debug and the result cached. ++ */ ++ ++kdb_dbtrap_t ++kdba_db_trap(struct pt_regs *regs, int error) ++{ ++ int i; ++ kdb_dbtrap_t rv = KDB_DB_BPT; ++ kdb_bp_t *bp; ++ ++ if (KDB_NULL_REGS(regs)) ++ return KDB_DB_NOBPT; ++ ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_db_trap: error %d\n", error); ++ ++ if (error == 36) { ++ /* Single step */ ++ if (KDB_STATE(SSBPT)) { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("ssbpt\n"); ++ KDB_STATE_CLEAR(SSBPT); ++ for(i=0,bp=kdb_breakpoints; ++ i < KDB_MAXBPT; ++ i++, bp++) { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("bp 0x%p enabled %d delayed %d global %d cpu %d\n", ++ bp, bp->bp_enabled, bp->bp_delayed, bp->bp_global, bp->bp_cpu); ++ if (!bp->bp_enabled) ++ continue; ++ if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) ++ continue; ++ if (KDB_DEBUG(BP)) ++ kdb_printf("bp for this cpu\n"); ++ if (bp->bp_delayed) { ++ bp->bp_delayed = 0; ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_installbp\n"); ++ kdba_installbp(regs, bp); ++ if (!KDB_STATE(DOING_SS)) { ++ kdba_clearsinglestep(regs); ++ return(KDB_DB_SSBPT); ++ } ++ break; ++ } ++ } ++ if (i == KDB_MAXBPT) { ++ kdb_printf("kdb: Unable to find delayed breakpoint\n"); ++ } ++ if (!KDB_STATE(DOING_SS)) { ++ kdba_clearsinglestep(regs); ++ return(KDB_DB_NOBPT); ++ } ++ /* FALLTHROUGH */ ++ } ++ ++ /* ++ * KDB_STATE_DOING_SS is set when the kernel debugger is using ++ * the processor trap flag to single-step a processor. If a ++ * single step trap occurs and this flag is clear, the SS trap ++ * will be ignored by KDB and the kernel will be allowed to deal ++ * with it as necessary (e.g. for ptrace). ++ */ ++ if (!KDB_STATE(DOING_SS)) ++ return(KDB_DB_NOBPT); ++ ++ /* single step */ ++ rv = KDB_DB_SS; /* Indicate single step */ ++ if (KDB_STATE(DOING_SSB)) /* No ia64 ssb support yet */ ++ KDB_STATE_CLEAR(DOING_SSB); /* No ia64 ssb support yet */ ++ if (KDB_STATE(DOING_SSB)) { ++ /* No IA64 ssb support yet */ ++ } else { ++ /* ++ * Print current insn ++ */ ++ kdb_machreg_t pc = regs->cr_iip + ia64_psr(regs)->ri * 6; ++ kdb_printf("SS trap at "); ++ kdb_symbol_print(pc, NULL, KDB_SP_DEFAULT|KDB_SP_NEWLINE); ++ kdb_id1(pc); ++ KDB_STATE_CLEAR(DOING_SS); ++ } ++ ++ if (rv != KDB_DB_SSB) ++ kdba_clearsinglestep(regs); ++ } ++ ++ return(rv); ++} ++ ++/* ++ * kdba_bp_trap ++ * ++ * Perform breakpoint processing upon entry to the ++ * processor breakpoint instruction fault. Determine and print ++ * the active breakpoint. ++ * ++ * Parameters: ++ * regs Exception frame containing machine register state ++ * error Error number passed to kdb. ++ * Outputs: ++ * None. ++ * Returns: ++ * 0 Standard instruction or data breakpoint encountered ++ * 1 Single Step fault ('ss' command) ++ * 2 Single Step fault, caller should continue ('ssb' command) ++ * 3 No existing kdb breakpoint matches this debug exception ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * If multiple processors receive debug exceptions simultaneously, ++ * one may be waiting at the kdb fence in kdb() while the user ++ * issues a 'bc' command to clear the breakpoint the processor which ++ * is waiting has already encountered. If this is the case, the ++ * debug registers will no longer match any entry in the breakpoint ++ * table, and we'll return the value '3'. This can cause a panic ++ * in die_if_kernel(). It is safer to disable the breakpoint (bd), ++ * 'go' until all processors are past the breakpoint then clear the ++ * breakpoint (bc). This code recognises a breakpoint even when ++ * disabled but not when it has been cleared. ++ * ++ * WARNING: This routine resets the ip. It should be called ++ * once per breakpoint and the result cached. ++ */ ++ ++kdb_dbtrap_t ++kdba_bp_trap(struct pt_regs *regs, int error) ++{ ++ int i; ++ kdb_dbtrap_t rv; ++ kdb_bp_t *bp; ++ ++ if (KDB_NULL_REGS(regs)) ++ return KDB_DB_NOBPT; ++ ++ /* ++ * Determine which breakpoint was encountered. ++ */ ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_bp_trap: ip=0x%lx " ++ "regs=0x%p sp=0x%lx\n", ++ regs->cr_iip, regs, regs->r12); ++ ++ rv = KDB_DB_NOBPT; /* Cause kdb() to return */ ++ ++ for(i=0, bp=kdb_breakpoints; ibp_free) ++ continue; ++ if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) ++ continue; ++ if (bp->bp_addr == regs->cr_iip) { ++ /* Hit this breakpoint. */ ++ kdb_printf("Instruction(i) breakpoint #%d at 0x%lx\n", ++ i, regs->cr_iip); ++ kdb_id1(regs->cr_iip); ++ rv = KDB_DB_BPT; ++ bp->bp_delay = 1; ++ /* SSBPT is set when the kernel debugger must single ++ * step a task in order to re-establish an instruction ++ * breakpoint which uses the instruction replacement ++ * mechanism. It is cleared by any action that removes ++ * the need to single-step the breakpoint. ++ */ ++ KDB_STATE_SET(SSBPT); ++ break; ++ } ++ } ++ ++ return rv; ++} ++ ++/* ++ * kdba_handle_bp ++ * ++ * Handle an instruction-breakpoint trap. Called when re-installing ++ * an enabled breakpoint which has has the bp_delay bit set. ++ * ++ * Parameters: ++ * Returns: ++ * Locking: ++ * Remarks: ++ * ++ * Ok, we really need to: ++ * 1) Restore the original instruction byte(s) ++ * 2) Single Step ++ * 3) Restore breakpoint instruction ++ * 4) Continue. ++ * ++ * ++ */ ++ ++static void ++kdba_handle_bp(struct pt_regs *regs, kdb_bp_t *bp) ++{ ++ if (KDB_NULL_REGS(regs)) ++ return; ++ ++ if (KDB_DEBUG(BP)) ++ kdb_printf("regs->cr_iip = 0x%lx\n", regs->cr_iip); ++ ++ /* ++ * Setup single step ++ */ ++ kdba_setsinglestep(regs); ++ ++ /* ++ * Reset delay attribute ++ */ ++ bp->bp_delay = 0; ++ bp->bp_delayed = 1; ++} ++ ++ ++/* ++ * kdba_bptype ++ * ++ * Return a string describing type of breakpoint. ++ * ++ * Parameters: ++ * bph Pointer to hardware breakpoint description ++ * Outputs: ++ * None. ++ * Returns: ++ * Character string. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++char * ++kdba_bptype(kdbhard_bp_t *bph) ++{ ++ char *mode; ++ ++ mode = kdba_rwtypes[bph->bph_mode]; ++ ++ return mode; ++} ++ ++/* ++ * kdba_printbpreg ++ * ++ * Print register name assigned to breakpoint ++ * ++ * Parameters: ++ * bph Pointer hardware breakpoint structure ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++static void ++kdba_printbpreg(kdbhard_bp_t *bph) ++{ ++ kdb_printf(" in dr%ld", bph->bph_reg); ++} ++ ++/* ++ * kdba_printbp ++ * ++ * Print string describing hardware breakpoint. ++ * ++ * Parameters: ++ * bph Pointer to hardware breakpoint description ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++void ++kdba_printbp(kdb_bp_t *bp) ++{ ++ kdb_printf("\n is enabled"); ++ if (bp->bp_hardtype) { ++ /* Note that bp->bp_hard[NR_CPU] is for x86. ++ * The ia64 uses bp->bp_hard[0] only. ++ */ ++ kdba_printbpreg(bp->bp_hard[0]); ++ if (bp->bp_hard[0]->bph_mode != 0) { ++ kdb_printf(" for %d bytes", ++ bp->bp_hard[0]->bph_length+1); ++ } ++ } ++} ++ ++/* ++ * kdba_parsebp ++ * ++ * Parse architecture dependent portion of the ++ * breakpoint command. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic for failure ++ * Locking: ++ * None. ++ * Remarks: ++ * for IA64 architure, data access, data write and ++ * I/O breakpoints are supported in addition to instruction ++ * breakpoints. ++ * ++ * {datar|dataw|io|inst} [length] ++ */ ++ ++int ++kdba_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp) ++{ ++ int nextarg = *nextargp; ++ int diag; ++ kdbhard_bp_t *bph = &bp->bp_template; ++ ++ bph->bph_mode = 0; /* Default to instruction breakpoint */ ++ bph->bph_length = 0; /* Length must be zero for insn bp */ ++ if ((argc + 1) != nextarg) { ++ if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0) { ++ bph->bph_mode = 3; ++ } else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) { ++ bph->bph_mode = 1; ++ } else if (strnicmp(argv[nextarg], "io", sizeof("io")) == 0) { ++ bph->bph_mode = 2; ++ } else if (strnicmp(argv[nextarg], "inst", sizeof("inst")) == 0) { ++ bph->bph_mode = 0; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ ++ if (bph->bph_mode == 0) ++ kdba_check_pc(&bp->bp_addr); ++ ++ bph->bph_length = 3; /* Default to 4 byte */ ++ ++ nextarg++; ++ ++ if ((argc + 1) != nextarg) { ++ unsigned long len; ++ ++ diag = kdbgetularg((char *)argv[nextarg], ++ &len); ++ if (diag) ++ return diag; ++ ++ ++ if ((len > 4) || (len == 3)) ++ return KDB_BADLENGTH; ++ ++ bph->bph_length = len; ++ bph->bph_length--; /* Normalize for debug register */ ++ nextarg++; ++ } ++ ++ if ((argc + 1) != nextarg) ++ return KDB_ARGCOUNT; ++ ++ /* ++ * Indicate to architecture independent level that ++ * a hardware register assignment is required to enable ++ * this breakpoint. ++ */ ++ ++ bph->bph_free = 0; ++ } else { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_bp: no args, forcehw is %d\n", bp->bp_forcehw); ++ if (bp->bp_forcehw) { ++ /* ++ * We are forced to use a hardware register for this ++ * breakpoint because either the bph or bpha ++ * commands were used to establish this breakpoint. ++ */ ++ bph->bph_free = 0; ++ } else { ++ /* ++ * Indicate to architecture dependent level that ++ * the instruction replacement breakpoint technique ++ * should be used for this breakpoint. ++ */ ++ bph->bph_free = 1; ++ bp->bp_adjust = 0; /* software, break is fault, not trap */ ++ } ++ } ++ ++ if (bph->bph_mode == 0 && kdba_verify_rw(bp->bp_addr, bph->bph_length+1)) { ++ kdb_printf("Invalid address for breakpoint, ignoring bp command\n"); ++ return KDB_BADADDR; ++ } ++ ++ *nextargp = nextarg; ++#ifndef CONFIG_KDB_HARDWARE_BREAKPOINTS ++ if (!bph->bph_free) { ++ kdb_printf("kdba_parsebp hardware breakpoints are not supported yet\n"); ++ return KDB_NOTIMP; ++ } ++#endif /* CONFIG_KDB_HARDWARE_BREAKPOINTS */ ++ return 0; ++} ++ ++/* ++ * kdba_allocbp ++ * ++ * Associate a hardware register with a breakpoint. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * A pointer to the allocated register kdbhard_bp_t structure for ++ * success, Null and a non-zero diagnostic for failure. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++static kdbhard_bp_t * ++kdba_allocbp(kdbhard_bp_t *bph, int *diagp) ++{ ++ int i; ++ kdbhard_bp_t *newbph; ++ ++ for(i=0,newbph=kdb_hardbreaks; i < KDB_MAXHARDBPT; i++, newbph++) { ++ if (newbph->bph_free) { ++ break; ++ } ++ } ++ ++ if (i == KDB_MAXHARDBPT) { ++ *diagp = KDB_TOOMANYDBREGS; ++ return NULL; ++ } ++ ++ *diagp = 0; ++ ++ /* ++ * Copy data from template. Can't just copy the entire template ++ * here because the register number in kdb_hardbreaks must be ++ * preserved. ++ */ ++ newbph->bph_data = bph->bph_data; ++ newbph->bph_write = bph->bph_write; ++ newbph->bph_mode = bph->bph_mode; ++ newbph->bph_length = bph->bph_length; ++ ++ /* ++ * Mark entry allocated. ++ */ ++ newbph->bph_free = 0; ++ ++ return newbph; ++} ++ ++/* ++ * kdba_alloc_hwbp ++ * ++ * Associate a hardware registers with a breakpoint. ++ * If hw bp is global hw registers descriptor will be allocated ++ * on every CPU. ++ * ++ * Parameters: ++ * bp - hardware bp ++ * diagp - pointer to variable that will store error when ++ * function complete ++ * Outputs: ++ * None. ++ * Returns: ++ * None ++ * Locking: ++ * None. ++ * Remarks: ++ * Should be called with correct bp->bp_template. ++ */ ++ ++void ++kdba_alloc_hwbp(kdb_bp_t *bp, int *diagp) ++{ ++ /* Note that bp->bp_hard[NR_CPU] is for x86. ++ * The ia64 uses bp->bp_hard[0] only. ++ */ ++ bp->bp_hard[0] = kdba_allocbp(&bp->bp_template, diagp); ++ bp->bp_hardtype = 1; ++} ++ ++ ++ ++/* ++ * kdba_freebp ++ * ++ * Deallocate a hardware breakpoint ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic for failure ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++static void ++kdba_freebp(kdbhard_bp_t *bph) ++{ ++ bph->bph_free = 1; ++} ++ ++/* ++ * kdba_free_hwbp ++ * ++ * Frees allocated hw registers descriptors for bp. ++ * If hw bp is global, hw registers descriptors will be freed ++ * on every CPU. ++ * ++ * Parameters: ++ * bp - hardware bp ++ * Outputs: ++ * None. ++ * Returns: ++ * None ++ * Locking: ++ * None. ++ * Remarks: ++ * Should be called with correct bp->bp_template ++ */ ++ ++void ++kdba_free_hwbp(kdb_bp_t *bp) ++{ ++ /* When kernel enters KDB, first, all local bps ++ * are removed, so here we don't need to clear ++ * debug registers. ++ */ ++ ++ kdba_freebp(bp->bp_hard[0]); ++ bp->bp_hard[0] = NULL; ++ bp->bp_hardtype = 0; ++} ++ ++ ++/* ++ * kdba_initbp ++ * ++ * Initialize the breakpoint table for the hardware breakpoint ++ * register. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic for failure ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * There is one entry per register. On the ia64 architecture ++ * all the registers are interchangeable, so no special allocation ++ * criteria are required. ++ */ ++ ++void ++kdba_initbp(void) ++{ ++ int i; ++ kdbhard_bp_t *bph; ++ ++ /* ++ * Clear the hardware breakpoint table ++ */ ++ ++ memset(kdb_hardbreaks, '\0', sizeof(kdb_hardbreaks)); ++ ++ for(i=0,bph=kdb_hardbreaks; ibph_reg = i; ++ bph->bph_free = 1; ++ } ++} ++ ++#ifdef CONFIG_KDB_HARDWARE_BREAKPOINTS ++/* ++ * Enable Instruction Debug & Data Debug faults on this CPU now. ++ */ ++static inline void kdba_enable_debug_faults(void) ++{ ++ unsigned long tmp; ++ ++ tmp = ia64_getreg(_IA64_REG_PSR); ++ ia64_stop(); ++ tmp |= IA64_PSR_DB; ++ ia64_stop(); ++ ia64_setreg(_IA64_REG_PSR_L, tmp); ++ ia64_srlz_i(); ++ if (KDB_DEBUG(BP)) ++ kdb_printf("enabling debug faults: [%d]PSR.L=%08x\n", ++ smp_processor_id(), (unsigned int)tmp); ++} ++ ++/* ++ * Disable Instruction Debug & Data Debug faults on this CPU now. ++ */ ++static inline void kdba_disable_debug_faults(void) ++{ ++ unsigned long tmp; ++ ++ tmp = ia64_getreg(_IA64_REG_PSR); ++ ia64_stop(); ++ tmp &= ~IA64_PSR_DB; ++ ia64_stop(); ++ ia64_setreg(_IA64_REG_PSR_L, tmp); ++ ia64_srlz_i(); ++ if (KDB_DEBUG(BP)) ++ kdb_printf("disabling debug faults: [%d]PSR.L=%08x\n", ++ smp_processor_id(), (unsigned int)tmp); ++} ++#endif /* CONFIG_KDB_HARDWARE_BREAKPOINTS */ ++ ++/* ++ * kdba_installbp ++ * ++ * Install a breakpoint ++ * ++ * Parameters: ++ * regs Exception frame ++ * bp Breakpoint structure for the breakpoint to be installed ++ * Outputs: ++ * None. ++ * Returns: ++ * 0 if breakpoint set, otherwise error. ++ * Locking: ++ * None. ++ * Remarks: ++ * For hardware breakpoints, a debug register is allocated ++ * and assigned to the breakpoint. If no debug register is ++ * available, a warning message is printed and the breakpoint ++ * is disabled. ++ * ++ * For instruction replacement breakpoints, we must single-step ++ * over the replaced instruction at this point so we can re-install ++ * the breakpoint instruction after the single-step. SSBPT is set ++ * when the breakpoint is initially hit and is cleared by any action ++ * that removes the need for single-step over the breakpoint. ++ */ ++ ++int ++kdba_installbp(struct pt_regs *regs, kdb_bp_t *bp) ++{ ++ /* ++ * Install the breakpoint, if it is not already installed. ++ */ ++ ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdba_installbp bp_installed %d\n", bp->bp_installed); ++ } ++ if (!KDB_STATE(SSBPT)) ++ bp->bp_delay = 0; ++ ++ if (bp->bp_hardtype) { ++#ifdef CONFIG_KDB_HARDWARE_BREAKPOINTS ++ /* ++ * Hardware breakpoints are always local for the ++ * purposes of installation (i.e. they use per-cpu ++ * registers), so we don't need to check bp_installed ++ */ ++ kdba_installdbreg(bp); ++ if (++kdb_dbrs_used[smp_processor_id()] == 1) ++ kdba_enable_debug_faults(); ++ bp->bp_installed = 1; ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt0 "\n", ++ bp->bp_hard[0]->bph_reg, bp->bp_addr); ++ } ++#endif /* CONFIG_KDB_HARDWARE_BREAKPOINTS */ ++ ++ } else if (bp->bp_delay) { ++ if (!bp->bp_installed) { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_installbp delayed bp\n"); ++ kdba_handle_bp(regs, bp); ++ } ++ } else { ++ if (!bp->bp_installed) { ++ /* Software breakpoints always use slot 0 in the 128 bit ++ * bundle. The template type does not matter, slot 0 ++ * can only be M or B and the encodings for break.m and ++ * break.b are the same. ++ */ ++ unsigned long break_inst; ++ if (kdb_getarea_size(bp->bp_inst.inst, bp->bp_addr, sizeof(bp->bp_inst.inst))) { ++ kdb_printf("kdba_installbp failed to read software breakpoint at 0x%lx\n", bp->bp_addr); ++ return(1); ++ } ++ break_inst = (bp->bp_inst.inst[0] & ~INST_SLOT0_MASK) | BREAK_INSTR; ++ if (kdb_putarea_size(bp->bp_addr, &break_inst, sizeof(break_inst))) { ++ kdb_printf("kdba_installbp failed to set software breakpoint at 0x%lx\n", bp->bp_addr); ++ return(1); ++ } ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_installbp instruction 0x%lx at " kdb_bfd_vma_fmt0 "\n", ++ BREAK_INSTR, bp->bp_addr); ++ bp->bp_installed = 1; ++ flush_icache_range(bp->bp_addr, bp->bp_addr+16); ++ } ++ } ++ return(0); ++} ++ ++/* ++ * kdba_removebp ++ * ++ * Make a breakpoint ineffective. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * 0 if breakpoint removed, otherwise error. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++int ++kdba_removebp(kdb_bp_t *bp) ++{ ++ /* ++ * For hardware breakpoints, remove it from the active register, ++ * for software breakpoints, restore the instruction stream. ++ */ ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdba_removebp bp_installed %d\n", bp->bp_installed); ++ } ++ ++ if (bp->bp_hardtype) { ++#ifdef CONFIG_KDB_HARDWARE_BREAKPOINTS ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt0 "\n", ++ bp->bp_hard[0]->bph_reg, bp->bp_addr); ++ } ++ if (--kdb_dbrs_used[smp_processor_id()] == 0) ++ kdba_disable_debug_faults(); ++ kdba_removedbreg(bp); ++#endif /* CONFIG_KDB_HARDWARE_BREAKPOINTS */ ++ } else { ++ if (bp->bp_installed) { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdb: restoring instruction 0x%016lx%016lx at " kdb_bfd_vma_fmt0 "\n", ++ bp->bp_inst.inst[0], bp->bp_inst.inst[1], bp->bp_addr); ++ if (kdba_putarea_size(bp->bp_addr, bp->bp_inst.inst, sizeof(bp->bp_inst.inst))) ++ return(1); ++ } ++ bp->bp_installed = 0; ++ flush_icache_range(bp->bp_addr, bp->bp_addr+16); ++ } ++ return(0); ++} +--- /dev/null ++++ b/arch/ia64/kdb/kdba_bt.c +@@ -0,0 +1,285 @@ ++/* ++ * Kernel Debugger Architecture Dependent Stack Traceback ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * bt_print_one ++ * ++ * Print one back trace entry. ++ * ++ * Inputs: ++ * ip Current program counter. ++ * symtab Information about symbol that ip falls within. ++ * argcount Maximum number of arguments to print. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * None. ++ */ ++ ++static void ++bt_print_one(kdb_machreg_t ip, ++ const kdb_symtab_t *symtab, int argcount, ++ struct unw_frame_info *info) ++{ ++ int btsymarg = 0; /* Convert arguments to symbols */ ++ int btsp = 0; /* Print stack and backing store pointers */ ++ int nosect = 0; /* Suppress section data */ ++ int args; ++ kdb_machreg_t sp, bsp, cfm; ++ ++ kdbgetintenv("BTSYMARG", &btsymarg); ++ kdbgetintenv("BTSP", &btsp); ++ kdbgetintenv("NOSECT", &nosect); ++ ++ unw_get_sp(info, &sp); ++ unw_get_bsp(info, &bsp); ++ unw_get_cfm(info, &cfm); ++ kdb_symbol_print(ip, symtab, KDB_SP_VALUE|KDB_SP_NEWLINE); ++ args = (cfm >> 7) & 0x7f; /* sol */ ++ if (!args) ++ args = cfm & 0x7f; /* no in/local, use sof instead */ ++ if (argcount && args) { ++ int i, argc = args; ++ ++ kdb_printf(" args ("); ++ if (argc > argcount) ++ argc = argcount; ++ ++ for(i = 0; i < argc; i++){ ++ kdb_machreg_t arg; ++ char nat; ++ if (unw_access_gr(info, i+32, &arg, &nat, 0)) ++ arg = 0; ++ ++ if (i) ++ kdb_printf(", "); ++ kdb_printf("0x%lx", arg); ++ } ++ kdb_printf(")\n"); ++ if (btsymarg) { ++ kdb_symtab_t arg_symtab; ++ kdb_machreg_t arg; ++ for(i = 0; i < argc; i++){ ++ char nat; ++ if (unw_access_gr(info, i+32, &arg, &nat, 0)) ++ arg = 0; ++ if (kdbnearsym(arg, &arg_symtab)) { ++ kdb_printf(" arg %d ", i); ++ kdb_symbol_print(arg, &arg_symtab, KDB_SP_DEFAULT|KDB_SP_NEWLINE); ++ } ++ } ++ } ++ } ++ if (symtab->sym_name) { ++ if (!nosect) { ++ kdb_printf(" %s", symtab->mod_name); ++ if (symtab->sec_name) ++ kdb_printf(" %s 0x%lx", symtab->sec_name, symtab->sec_start); ++ kdb_printf(" 0x%lx", symtab->sym_start); ++ if (symtab->sym_end) ++ kdb_printf(" 0x%lx", symtab->sym_end); ++ kdb_printf("\n"); ++ } ++ if (strncmp(symtab->sym_name, "ia64_spinlock_contention", 24) == 0) { ++ kdb_machreg_t r31; ++ char nat; ++ kdb_printf(" r31 (spinlock address) "); ++ if (unw_access_gr(info, 31, &r31, &nat, 0)) ++ r31 = 0; ++ kdb_symbol_print(r31, NULL, KDB_SP_VALUE|KDB_SP_NEWLINE); ++ } ++ } ++ if (btsp) ++ kdb_printf(" sp 0x%016lx bsp 0x%016lx cfm 0x%016lx info->pfs_loc 0x%016lx 0x%016lx\n", ++ sp, bsp, cfm, (u64) info->pfs_loc, info->pfs_loc ? *(info->pfs_loc) : 0); ++} ++ ++/* ++ * kdba_bt_stack ++ * ++ * Unwind the ia64 backtrace for a specified process. ++ * ++ * Inputs: ++ * argcount ++ * p Pointer to task structure to unwind. ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * none. ++ */ ++ ++static int ++kdba_bt_stack(int argcount, const struct task_struct *p) ++{ ++ kdb_symtab_t symtab; ++ struct unw_frame_info info; ++ struct switch_stack *sw; ++ struct pt_regs *regs = NULL; ++ int count = 0; ++ int btsp = 0; /* Backtrace the kdb code as well */ ++ u64 *prev_pfs_loc = NULL; ++ extern char __attribute__ ((weak)) ia64_spinlock_contention_pre3_4[]; ++ extern char __attribute__ ((weak)) ia64_spinlock_contention_pre3_4_end[]; ++ ++ /* ++ * Upon entering kdb_main_loop, the stack frame looks like this: ++ * ++ * +---------------------+ ++ * | struct pt_regs | ++ * +---------------------+ ++ * | | ++ * | kernel stack | ++ * | | ++ * +=====================+ <--- top of stack upon entering kdb ++ * | struct pt_regs | ++ * +---------------------+ ++ * | | ++ * | kdb stack | ++ * | | ++ * +---------------------+ ++ * | struct switch_stack | ++ * +=====================+ <--- kdb_running_process[cpu].arch.sw from do_kdba_main_loop ++ * ++ * When looking at another process, we do not have the address of the ++ * current pt_regs, it is NULL. If the process has saved its state, use ++ * that pt_regs instead. ++ */ ++ ++ kdbgetintenv("BTSP", &btsp); ++ ++ if (kdb_task_has_cpu(p)) { ++ struct kdb_running_process *krp = kdb_running_process + kdb_process_cpu(p); ++ if (krp->seqno) { ++ sw = krp->arch.sw; ++ regs = krp->regs; ++ } ++ else ++ sw = NULL; ++ } ++ else { ++ /* Not running, assume blocked */ ++ sw = (struct switch_stack *) (p->thread.ksp + 16); ++ } ++ if (!sw) { ++ kdb_printf("Process does not have a switch_stack, cannot backtrace\n"); ++ kdb_ps1(p); ++ return 0; ++ } ++ ++ unw_init_frame_info(&info, (struct task_struct *)p, sw); ++ ++ /* If we have the address of pt_regs, suppress backtrace on the frames below ++ * pt_regs. No point in displaying kdb itself, unless the user is debugging ++ * the unwinder using set BTSP=1. ++ */ ++ if (regs && !btsp) { ++ kdb_machreg_t sp; ++ if (user_mode(regs)) { ++ kdb_printf("Process was interrupted in user mode, no backtrace available\n"); ++ return 0; ++ } ++ do { ++ unw_get_sp(&info, &sp); ++ if (sp >= (kdb_machreg_t)regs) ++ break; ++ } while (unw_unwind(&info) >= 0 && count++ < 200); ++ } ++ ++ do { ++ kdb_machreg_t ip; ++ ++ /* Avoid unsightly console message from unw_unwind() when attempting ++ * to unwind through the Interrupt Vector Table which has no unwind ++ * information. dispatch_illegal_op_fault() is an exception, it sits ++ * in the 0x3c00 slot. ++ */ ++ if (info.ip >= (u64)__start_ivt_text && info.ip < (u64)__end_ivt_text) { ++ if (info.ip < (u64)__start_ivt_text + 0x3c00 || ++ info.ip >= (u64)__start_ivt_text + 0x4000) ++ return 0; ++ } ++ ++ /* WAR for spinlock contention from leaf functions. ia64_spinlock_contention_pre3_4 ++ * has ar.pfs == r0. Leaf functions do not modify ar.pfs so ar.pfs remains ++ * as 0, stopping the backtrace. Record the previous ar.pfs when the current ++ * IP is in ia64_spinlock_contention_pre3_4 then unwind, if pfs_loc has not changed ++ * after unwind then use pt_regs.ar_pfs which is where the real ar.pfs is for ++ * leaf functions. ++ */ ++ if (prev_pfs_loc && regs && info.pfs_loc == prev_pfs_loc) ++ info.pfs_loc = ®s->ar_pfs; ++ prev_pfs_loc = (info.ip >= (u64)ia64_spinlock_contention_pre3_4 && ++ info.ip < (u64)ia64_spinlock_contention_pre3_4_end) ? ++ info.pfs_loc : NULL; ++ ++ unw_get_ip(&info, &ip); ++ if (ip == 0) ++ break; ++ ++ kdbnearsym(ip, &symtab); ++ if (!symtab.sym_name) { ++ kdb_printf("0x%0*lx - No name. May be an area that has no unwind data\n", ++ (int)(2*sizeof(ip)), ip); ++ return 0; ++ } ++ bt_print_one(ip, &symtab, argcount, &info); ++ } while (unw_unwind(&info) >= 0 && count++ < 200); ++ if (count >= 200) ++ kdb_printf("bt truncated, count limit reached\n"); ++ ++ return 0; ++} ++ ++int ++kdba_bt_address(kdb_machreg_t addr, int argcount) ++{ ++ kdb_printf("Backtrace from a stack address is not supported on ia64\n"); ++ return KDB_NOTIMP; ++} ++ ++/* ++ * kdba_bt_process ++ * ++ * Do a backtrace for a specified process. ++ * ++ * Inputs: ++ * p Struct task pointer extracted by 'bt' command. ++ * argcount ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ */ ++ ++int ++kdba_bt_process(const struct task_struct *p, int argcount) ++{ ++ return kdba_bt_stack(argcount, p); ++} +--- /dev/null ++++ b/arch/ia64/kdb/kdba_fru.c +@@ -0,0 +1,65 @@ ++/* ++ * Kernel Debugger Architecture Dependent FRU functions. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Jesse Barnes"); ++MODULE_DESCRIPTION("Capture FRU data"); ++MODULE_LICENSE("GPL"); ++ ++/** ++ * kdba_fru - capture FRU data ++ * @argc: arg count ++ * @argv: arg values ++ * ++ * Tell the system contollers to capture FRU data ++ */ ++static int ++kdba_fru(int argc, const char **argv) ++{ ++ u64 ret; ++ ++ kdb_printf("Capturing FRU data..."); ++ ret = ia64_sn_fru_capture(); ++ kdb_printf("done.\n"); ++ return ret; ++} ++ ++/** ++ * kdba_fru_init - register 'fru' command with kdb ++ * ++ * Register the 'fru' command with kdb at load time. ++ */ ++static int __init ++kdba_fru_init(void) ++{ ++ kdb_register("fru", kdba_fru, 0, "Capture FRU data", 0); ++ ++ return 0; ++} ++ ++/** ++ * kdba_fru_exit - unregister the 'fru' command ++ * ++ * Tell kdb that the 'fru' command is no longer available. ++ */ ++static void __exit ++kdba_fru_exit(void) ++{ ++ kdb_unregister("fru"); ++} ++ ++module_init(kdba_fru_init) ++module_exit(kdba_fru_exit) +--- /dev/null ++++ b/arch/ia64/kdb/kdba_id.c +@@ -0,0 +1,529 @@ ++/* ++ * Kernel Debugger Architecture Dependent Instruction Disassembly ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define KDBA_PRINTBUF_LEN 64 /* buffer len to print a single instr */ ++#define KDBA_READBUFFER_LEN 256 /* buffer for BFD disassembler */ ++ ++#define BUNDLE_MULTIPLIER 3 /* how many instr/bundle */ ++#define BUNDLE_SIZE 16 /* how many bytes/bundle */ ++#define KDBA_DEFAULT_IDLEN 3 /* default number of bundles to disassemble */ ++ ++/* ++ * kdba_dis_getsym ++ * ++ * Get a symbol for the disassembler. ++ * ++ * Parameters: ++ * addr Address for which to get symbol ++ * dip Pointer to disassemble_info ++ * Returns: ++ * 0 ++ * Locking: ++ * Remarks: ++ * Not used for kdb. ++ */ ++ ++/* ARGSUSED */ ++static int ++kdba_dis_getsym(bfd_vma addr, disassemble_info *dip) ++{ ++ ++ return 0; ++} ++ ++/* ++ * kdba_printaddress ++ * ++ * Print (symbolically) an address. ++ * ++ * Parameters: ++ * addr Address for which to get symbol ++ * dip Pointer to disassemble_info ++ * flag True if a ":" sequence should follow the address ++ * Returns: ++ * 0 ++ * Locking: ++ * Remarks: ++ * ++ */ ++ ++/* ARGSUSED */ ++static void ++kdba_printaddress(kdb_machreg_t addr, disassemble_info *dip, int flag) ++{ ++ kdb_symtab_t symtab; ++ int spaces = 5; ++ unsigned int offset; ++ int slot; ++ ++ /* Some code prints slot number, some prints "byte" offset ++ * from start of bundle. Standardise on "byte" offset. ++ */ ++ slot = addr & 0x0f; ++ if (slot < 3) ++ slot *= 6; ++ addr = (addr & ~0x0f) + slot; ++ ++ /* ++ * Print a symbol name or address as necessary. ++ */ ++ dip->fprintf_func(dip->stream, "0x%0*lx ", (int)(2*sizeof(addr)), addr); ++ kdbnearsym(addr, &symtab); ++ if (symtab.sym_name) { ++ /* Do not use kdb_symbol_print here, it always does ++ * kdb_printf but we want dip->fprintf_func. ++ */ ++ dip->fprintf_func(dip->stream, "%s", symtab.sym_name); ++ if ((offset = addr - symtab.sym_start) == 0) { ++ spaces += 4; ++ } ++ else { ++ unsigned int o = offset; ++ while (o >>= 4) ++ --spaces; ++ dip->fprintf_func(dip->stream, "+0x%x", offset); ++ } ++ } ++ ++ if (flag) { ++ if (spaces < 1) { ++ spaces = 1; ++ } ++ dip->fprintf_func(dip->stream, ":%*s", spaces, " "); ++ } ++} ++ ++/* Calls outside the current kernel module use a PLT */ ++ ++static int addr_maybe_plt; ++ ++/* The templates below were extracted from arch/ia64/kernel/module.c. The ++ * masks were generated by this quick and dirty program: ++ */ ++ ++#if 0 /* mask program */ ++#include ++ ++#define u64 unsigned long ++ ++/* routines copied from arch/ia64/kernel/patch.c */ ++ ++static void ++ia64_patch (u64 insn_addr, u64 mask, u64 val) ++{ ++ u64 m0, m1, v0, v1, b0, b1, *b = (u64 *) (insn_addr & -16); ++# define insn_mask ((1UL << 41) - 1) ++ unsigned long shift; ++ ++ b0 = b[0]; b1 = b[1]; ++ shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */ ++ if (shift >= 64) { ++ m1 = mask << (shift - 64); ++ v1 = val << (shift - 64); ++ } else { ++ m0 = mask << shift; m1 = mask >> (64 - shift); ++ v0 = val << shift; v1 = val >> (64 - shift); ++ b[0] = (b0 & ~m0) | (v0 & m0); ++ } ++ b[1] = (b1 & ~m1) | (v1 & m1); ++} ++ ++static void ++ia64_patch_imm64 (u64 insn_addr, u64 val) ++{ ++ /* The assembler may generate offset pointing to either slot 1 ++ or slot 2 for a long (2-slot) instruction, occupying slots 1 ++ and 2. */ ++ insn_addr &= -16UL; ++ ia64_patch(insn_addr + 2, ++ 0x01fffefe000UL, ( ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */ ++ | ((val & 0x0000000000200000UL) << 0) /* bit 21 -> 21 */ ++ | ((val & 0x00000000001f0000UL) << 6) /* bit 16 -> 22 */ ++ | ((val & 0x000000000000ff80UL) << 20) /* bit 7 -> 27 */ ++ | ((val & 0x000000000000007fUL) << 13) /* bit 0 -> 13 */)); ++ ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22); ++} ++ ++static void ++ia64_patch_imm60 (u64 insn_addr, u64 val) ++{ ++ /* The assembler may generate offset pointing to either slot 1 ++ or slot 2 for a long (2-slot) instruction, occupying slots 1 ++ and 2. */ ++ insn_addr &= -16UL; ++ ia64_patch(insn_addr + 2, ++ 0x011ffffe000UL, ( ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */ ++ | ((val & 0x00000000000fffffUL) << 13) /* bit 0 -> 13 */)); ++ ia64_patch(insn_addr + 1, 0x1fffffffffcUL, val >> 18); ++} ++ ++struct plt_entry { ++ unsigned char bundle[3][16]; ++}; ++static struct plt_entry ia64_plt_mask; ++ ++int main(void) ++{ ++ int i, j; ++ printf("2 bundle\n"); ++ for (i = 0; i < 2; ++i) ++ for (j = 0; j < 16; ++j) ++ ia64_plt_mask.bundle[i][j] = 0xff; ++ ia64_patch_imm64((u64) (ia64_plt_mask.bundle + 0), 0); ++ ia64_patch_imm60((u64) (ia64_plt_mask.bundle + 1), 0); ++ for (i = 0; i < 2; ++i) { ++ for (j = 0; j < 16; ++j) { ++ printf("0x%02x", ia64_plt_mask.bundle[i][j]); ++ if (j != 15) ++ printf(", "); ++ if (j % 6 == 5 || j == 15) ++ printf("\n"); ++ } ++ } ++ printf("\n3 bundle\n"); ++ for (i = 0; i < 3; ++i) ++ for (j = 0; j < 16; ++j) ++ ia64_plt_mask.bundle[i][j] = 0xff; ++ ia64_patch_imm64((u64) (ia64_plt_mask.bundle + 0), 0); ++ ia64_patch_imm64((u64) (ia64_plt_mask.bundle + 1), 0); ++ for (i = 0; i < 3; ++i) { ++ for (j = 0; j < 16; ++j) { ++ printf("0x%02x", ia64_plt_mask.bundle[i][j]); ++ if (j != 15) ++ printf(", "); ++ if (j % 6 == 5 || j == 15) ++ printf("\n"); ++ } ++ } ++ return 0; ++} ++#endif /* mask program */ ++ ++#ifdef CONFIG_IA64_BRL_EMU ++#define PLT_BUNDLES 3 ++struct plt_entry { ++ unsigned char bundle[PLT_BUNDLES][16]; ++}; ++static const struct plt_entry ia64_plt_template = { ++ { ++ { ++ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* movl r16=TARGET_IP */ ++ 0x02, 0x00, 0x00, 0x60 ++ }, ++ { ++ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, /* movl gp=TARGET_GP */ ++ 0x00, 0x00, 0x00, 0x60 ++ }, ++ { ++ 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MIB] nop.m 0 */ ++ 0x60, 0x80, 0x04, 0x80, 0x03, 0x00, /* mov b6=r16 */ ++ 0x60, 0x00, 0x80, 0x00 /* br.few b6 */ ++ } ++ } ++}; ++static const struct plt_entry ia64_plt_mask = { ++ { ++ { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, /* [MLX] nop.m 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, /* movl r16=TARGET_IP */ ++ 0x0f, 0x08, 0x00, 0xf0 ++ }, ++ { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, /* [MLX] nop.m 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, /* movl gp=TARGET_GP */ ++ 0x0f, 0x08, 0x00, 0xf0 ++ }, ++ { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* [MIB] nop.m 0 */ ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* mov b6=r16 */ ++ 0xff, 0xff, 0xff, 0xff /* br.few b6 */ ++ } ++}; ++ ++#else /* !CONFIG_IA64_BRL_EMU */ ++ ++#define PLT_BUNDLES 2 ++struct plt_entry { ++ unsigned char bundle[PLT_BUNDLES][16]; ++}; ++static const struct plt_entry ia64_plt_template = { ++ { ++ { ++ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, /* movl gp=TARGET_GP */ ++ 0x00, 0x00, 0x00, 0x60 ++ }, ++ { ++ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* brl.many TARGET_IP */ ++ 0x08, 0x00, 0x00, 0xc0 ++ } ++ } ++}; ++static const struct plt_entry ia64_plt_mask = { ++ { ++ { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, /* [MLX] nop.m 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, /* movl gp=TARGET_GP */ ++ 0x0f, 0x08, 0x00, 0xf0 ++ }, ++ { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* [MLX] nop.m 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, /* brl.many TARGET_IP */ ++ 0x0f, 0x00, 0x00, 0xf7 ++ } ++ } ++}; ++#endif /* CONFIG_IA64_BRL_EMU */ ++ ++static inline u64 ++get_slot(u64 bundle, int slot) ++{ ++ switch (slot) { ++ case 0: ++ return (((u64 *)bundle)[0] >> 5) & 0x1ffffffffffLL; ++ case 1: ++ return ((((u64 *)bundle)[0] >> 46) & 0x3ffff) | ++ ((((u64 *)bundle)[1] & 0x7fffff) << 18); ++ default: ++ return (((u64 *)bundle)[1] >> 23) & 0x1ffffffffffLL; ++ } ++} ++ ++static inline u64 ++get_movl (u64 addr) ++{ ++ /* X2 format */ ++ u64 slot1 = get_slot(addr, 1), slot2 = get_slot(addr, 2); ++ u64 i, imm9d, imm5c, ic, imm7b, imm41; ++ i = (slot2 >> 36) & 0x1UL; ++ imm9d = (slot2 >> 27) & 0xfffUL; ++ imm5c = (slot2 >> 22) & 0x1fUL; ++ ic = (slot2 >> 21) & 0x1UL; ++ imm7b = (slot2 >> 13) & 0x7fUL; ++ imm41 = slot1; ++ return (i << 63) | (imm41 << 22) | (ic << 21) | (imm5c << 16) | ++ (imm9d << 7) | imm7b; ++} ++ ++static inline u64 ++get_brl (u64 addr) ++{ ++ /* X3 format */ ++ u64 slot1 = get_slot(addr, 1), slot2 = get_slot(addr, 2); ++ u64 i, imm20b, imm39; ++ i = (slot2 >> 36) & 0x1UL; ++ imm20b = (slot2 >> 13) & 0xfffffUL; ++ imm39 = slot1 >> 2; ++ return ((i << 59) | (imm39 << 20) | imm20b) << 4; ++} ++ ++static bfd_vma ++is_plt(bfd_vma addr) { ++ int i, j; ++ u64 target; ++ struct plt_entry plt; ++ if (kdb_getarea_size(&plt, addr, sizeof(plt))) ++ return 0; ++ for (i = 0; i < PLT_BUNDLES; ++i) { ++ for (j = 0; j < 16; ++j) { ++ if ((plt.bundle[i][j] & ia64_plt_mask.bundle[i][j]) != ++ ia64_plt_template.bundle[i][j]) ++ return 0; ++ } ++ } ++ if (PLT_BUNDLES == 2) { ++ /* brl is IP relative, in second bundle */ ++ target = get_brl(addr + 16) + addr + 16; ++ } else { ++ /* movl is absolute, in first bundle */ ++ target = get_movl(addr); ++ } ++ return target; ++} ++ ++/* ++ * kdba_dis_printaddr ++ * ++ * Print (symbolically) an address. Called by GNU disassembly ++ * code via disassemble_info structure. ++ * ++ * Parameters: ++ * addr Address for which to get symbol ++ * dip Pointer to disassemble_info ++ * Returns: ++ * 0 ++ * Locking: ++ * Remarks: ++ * This function will never append ":" to the printed ++ * symbolic address. If the address may be a PLT entry then try to decode ++ * the PLT information. ++ */ ++ ++static void ++kdba_dis_printaddr(bfd_vma addr, disassemble_info *dip) ++{ ++ bfd_vma target; ++ kdba_printaddress(addr, dip, 0); ++ if (!addr_maybe_plt) ++ return; ++ if (!(target = is_plt(addr))) ++ return; ++ kdb_printf(" PLT --> "); ++ kdba_printaddress(target, dip, 0); ++} ++ ++/* ++ * kdba_dis_getmem ++ * ++ * Fetch 'length' bytes from 'addr' into 'buf'. ++ * ++ * Parameters: ++ * addr Address for which to get symbol ++ * buf Address of buffer to fill with bytes from 'addr' ++ * length Number of bytes to fetch ++ * dip Pointer to disassemble_info ++ * Returns: ++ * 0 ++ * Locking: ++ * Remarks: ++ * ++ */ ++ ++/* ARGSUSED */ ++static int ++kdba_dis_getmem(bfd_vma addr, bfd_byte *buf, unsigned int length, disassemble_info *dip) ++{ ++ return kdb_getarea_size(buf, addr, length); ++} ++ ++/* ++ * kdba_id_parsemode ++ * ++ * Parse IDMODE environment variable string and ++ * set appropriate value into "disassemble_info" structure. ++ * ++ * Parameters: ++ * mode Mode string ++ * dip Disassemble_info structure pointer ++ * Returns: ++ * Locking: ++ * Remarks: ++ * No mode supported yet. ++ */ ++ ++int ++kdba_id_parsemode(const char *mode, disassemble_info *dip) ++{ ++ if (mode && strcmp(mode, "ia64")) ++ return KDB_BADMODE; ++ return 0; ++} ++ ++/* ++ * kdba_check_pc ++ * ++ * Check that the pc is satisfactory. ++ * ++ * Parameters: ++ * pc Program Counter Value. ++ * Returns: ++ * None ++ * Locking: ++ * None. ++ * Remarks: ++ * Can change pc. ++ */ ++ ++void ++kdba_check_pc(kdb_machreg_t *pc) ++{ ++ (*pc) &= ~0xf; /* pc must be 16 byte aligned */ ++} ++ ++/* ++ * kdba_id_printinsn ++ * ++ * Format and print a single bundle at 'pc'. Return the ++ * length of the bundle. ++ * ++ * Parameters: ++ * pc Program Counter Value. ++ * dip Disassemble_info structure pointer ++ * Returns: ++ * Length of instruction, -1 for error. ++ * Locking: ++ * None. ++ * Remarks: ++ * None. ++ */ ++ ++int ++kdba_id_printinsn(kdb_machreg_t pc, disassemble_info *dip) ++{ ++ int ret; ++ int byte = 0; ++ ++ kdba_check_pc(&pc); ++ while (byte < 16) { ++ kdba_dis_printaddr(pc+byte, dip); ++ addr_maybe_plt = 1; ++ ret = print_insn_ia64((kdb_machreg_t)(pc+byte), dip); ++ addr_maybe_plt = 0; ++ dip->fprintf_func(dip->stream, "\n"); ++ if (ret < 0) ++ break; ++ byte += ret; ++ } ++ return(byte); ++} ++ ++/* ++ * kdba_id_init ++ * ++ * Initialize the architecture dependent elements of ++ * the disassembly information structure ++ * for the GNU disassembler. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++void ++kdba_id_init(disassemble_info *dip) ++{ ++ dip->read_memory_func = kdba_dis_getmem; ++ dip->print_address_func = kdba_dis_printaddr; ++ dip->symbol_at_address_func = kdba_dis_getsym; ++ ++ dip->flavour = bfd_target_elf_flavour; ++ dip->arch = bfd_arch_ia64; ++ dip->endian = BFD_ENDIAN_LITTLE; ++ ++ dip->display_endian = BFD_ENDIAN_LITTLE; ++} +--- /dev/null ++++ b/arch/ia64/kdb/kdba_io.c +@@ -0,0 +1,661 @@ ++/* ++ * Kernel Debugger Architecture Dependent Console I/O handler ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#if defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_SGI_L1_CONSOLE) ++#define HAVE_KDBA_SERIAL_CONSOLE ++#endif ++ ++/* from include/linux/pc_keyb.h on 2.4 */ ++#define KBD_STATUS_REG 0x64 /* Status register (R) */ ++#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */ ++#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ ++#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ ++#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ ++#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ ++ ++#ifdef CONFIG_VT_CONSOLE ++#define KDB_BLINK_LED 1 ++#else ++#undef KDB_BLINK_LED ++#endif ++ ++#ifdef CONFIG_KDB_USB ++ ++/* support up to 8 USB keyboards (probably excessive, but...) */ ++#define KDB_USB_NUM_KEYBOARDS 8 ++struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS]; ++ ++extern int kdb_no_usb; ++ ++static unsigned char kdb_usb_keycode[256] = { ++ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, ++ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, ++ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, ++ 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, ++ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, ++ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, ++ 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95, ++ 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, ++ 115,114, 0, 0, 0,124, 0,181,182,183,184,185,186,187,188,189, ++ 190,191,192,193,194,195,196,197,198, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, ++ 150,158,159,128,136,177,178,176,142,152,173,140 ++}; ++ ++/* ++ * kdb_usb_keyboard_attach() ++ * Attach a USB keyboard to kdb. ++ */ ++int ++kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, void *poll_func) ++{ ++ int i; ++ int rc = -1; ++ ++ if (kdb_no_usb) ++ return 0; ++ ++ /* ++ * Search through the array of KDB USB keyboards (kdb_usb_kbds) ++ * looking for a free index. If found, assign the keyboard to ++ * the array index. ++ */ ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ if (kdb_usb_kbds[i].urb) /* index is already assigned */ ++ continue; ++ ++ /* found a free array index */ ++ kdb_usb_kbds[i].urb = urb; ++ kdb_usb_kbds[i].buffer = buffer; ++ kdb_usb_kbds[i].poll_func = poll_func; ++ ++ rc = 0; /* success */ ++ ++ break; ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL (kdb_usb_keyboard_attach); ++ ++/* ++ * kdb_usb_keyboard_detach() ++ * Detach a USB keyboard from kdb. ++ */ ++int ++kdb_usb_keyboard_detach(struct urb *urb) ++{ ++ int i; ++ int rc = -1; ++ ++ if (kdb_no_usb) ++ return 0; ++ ++ /* ++ * Search through the array of KDB USB keyboards (kdb_usb_kbds) ++ * looking for the index with the matching URB. If found, ++ * clear the array index. ++ */ ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ if (kdb_usb_kbds[i].urb != urb) ++ continue; ++ ++ /* found it, clear the index */ ++ kdb_usb_kbds[i].urb = NULL; ++ kdb_usb_kbds[i].buffer = NULL; ++ kdb_usb_kbds[i].poll_func = NULL; ++ kdb_usb_kbds[i].caps_lock = 0; ++ ++ rc = 0; /* success */ ++ ++ break; ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL (kdb_usb_keyboard_detach); ++ ++/* ++ * get_usb_char ++ * This function drives the USB attached keyboards. ++ * Fetch the USB scancode and decode it. ++ */ ++static int ++get_usb_char(void) ++{ ++ int i; ++ int ret; ++ unsigned char keycode, spec; ++ extern u_short plain_map[], shift_map[], ctrl_map[]; ++ ++ if (kdb_no_usb) ++ return -1; ++ ++ /* ++ * Loop through all the USB keyboard(s) and return ++ * the first character obtained from them. ++ */ ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ /* skip uninitialized keyboard array entries */ ++ if (!kdb_usb_kbds[i].urb || !kdb_usb_kbds[i].buffer || ++ !kdb_usb_kbds[i].poll_func) ++ continue; ++ ++ /* Transfer char */ ++ ret = (*kdb_usb_kbds[i].poll_func)(kdb_usb_kbds[i].urb); ++ ++ if (ret == -EBUSY && kdb_usb_kbds[i].poll_ret != -EBUSY) ++ kdb_printf("NOTICE: USB HC driver BUSY. " ++ "USB keyboard has been disabled.\n"); ++ ++ kdb_usb_kbds[i].poll_ret = ret; ++ ++ if (ret < 0) /* error or no characters, try the next kbd */ ++ continue; ++ ++ spec = kdb_usb_kbds[i].buffer[0]; ++ keycode = kdb_usb_kbds[i].buffer[2]; ++ kdb_usb_kbds[i].buffer[0] = (char)0; ++ kdb_usb_kbds[i].buffer[2] = (char)0; ++ ++ if(kdb_usb_kbds[i].buffer[3]) { ++ kdb_usb_kbds[i].buffer[3] = (char)0; ++ continue; ++ } ++ ++ /* A normal key is pressed, decode it */ ++ if(keycode) ++ keycode = kdb_usb_keycode[keycode]; ++ ++ /* 2 Keys pressed at one time ? */ ++ if (spec && keycode) { ++ switch(spec) ++ { ++ case 0x2: ++ case 0x20: /* Shift */ ++ return shift_map[keycode]; ++ case 0x1: ++ case 0x10: /* Ctrl */ ++ return ctrl_map[keycode]; ++ case 0x4: ++ case 0x40: /* Alt */ ++ break; ++ } ++ } else if (keycode) { /* If only one key pressed */ ++ switch(keycode) ++ { ++ case 0x1C: /* Enter */ ++ return 13; ++ ++ case 0x3A: /* Capslock */ ++ kdb_usb_kbds[i].caps_lock = !(kdb_usb_kbds[i].caps_lock); ++ break; ++ case 0x0E: /* Backspace */ ++ return 8; ++ case 0x0F: /* TAB */ ++ return 9; ++ case 0x77: /* Pause */ ++ break ; ++ default: ++ if(!kdb_usb_kbds[i].caps_lock) { ++ return plain_map[keycode]; ++ } ++ else { ++ return shift_map[keycode]; ++ } ++ } ++ } ++ } ++ ++ /* no chars were returned from any of the USB keyboards */ ++ ++ return -1; ++} ++#endif /* CONFIG_KDB_USB */ ++ ++/* ++ * This module contains code to read characters from the keyboard or a serial ++ * port. ++ * ++ * It is used by the kernel debugger, and is polled, not interrupt driven. ++ * ++ */ ++ ++#ifdef KDB_BLINK_LED ++/* ++ * send: Send a byte to the keyboard controller. Used primarily to ++ * alter LED settings. ++ */ ++ ++static void ++kdb_kbdsend(unsigned char byte) ++{ ++ int timeout; ++ for (timeout = 200 * 1000; timeout && (inb(KBD_STATUS_REG) & KBD_STAT_IBF); timeout--); ++ outb(byte, KBD_DATA_REG); ++ udelay(40); ++ for (timeout = 200 * 1000; timeout && (~inb(KBD_STATUS_REG) & KBD_STAT_OBF); timeout--); ++ inb(KBD_DATA_REG); ++ udelay(40); ++} ++ ++static void ++kdb_toggleled(int led) ++{ ++ static int leds; ++ ++ leds ^= led; ++ ++ kdb_kbdsend(KBD_CMD_SET_LEDS); ++ kdb_kbdsend((unsigned char)leds); ++} ++#endif /* KDB_BLINK_LED */ ++ ++#ifdef HAVE_KDBA_SERIAL_CONSOLE ++ ++struct kdb_serial kdb_serial; ++enum kdba_serial_console kdba_serial_console; ++static int get_serial_char(void); ++ ++/* There must be a serial_inp_xxx() and get_serial_char_xxx() for each type ++ * of console. See enum kdba_serial_console in include/asm-$(ARCH)/kdbprivate.h. ++ */ ++ ++#ifdef CONFIG_SERIAL_8250_CONSOLE ++ ++static unsigned int ++serial_inp_standard(const struct kdb_serial *kdb_serial, int offset) ++{ ++ offset <<= kdb_serial->ioreg_shift; ++ ++ switch (kdb_serial->io_type) { ++ case SERIAL_IO_MEM: ++ return readb((void __iomem *)(kdb_serial->iobase + offset)); ++ break; ++ default: ++ return inb(kdb_serial->iobase + offset); ++ break; ++ } ++} ++ ++/* Check if there is a byte ready at the serial port */ ++static int ++get_serial_char_standard(void) ++{ ++ unsigned char ch; ++ static unsigned long fifon; ++ if (fifon == 0) { ++ /* try to set the FIFO */ ++ fifon = kdb_serial.iobase + ++ (UART_FCR << kdb_serial.ioreg_shift); ++ switch (kdb_serial.io_type) { ++ case SERIAL_IO_MEM: ++ writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | ++ UART_FCR_CLEAR_XMIT), (void __iomem *)fifon); ++ break; ++ default: ++ outb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | ++ UART_FCR_CLEAR_XMIT), fifon); ++ break; ++ } ++ } ++ ++ if (kdb_serial.iobase == 0) ++ return -1; ++ ++ if (serial_inp_standard(&kdb_serial, UART_LSR) & UART_LSR_DR) { ++ ch = serial_inp_standard(&kdb_serial, UART_RX); ++ if (ch == 0x7f) ++ ch = 8; ++ return ch; ++ } ++ return -1; ++} ++ ++#else /* !CONFIG_SERIAL_8250_CONSOLE */ ++ ++#define get_serial_char_standard() -1 ++ ++#endif /* CONFIG_SERIAL_8250_CONSOLE */ ++ ++#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE ++ ++extern u64 master_node_bedrock_address; ++ ++/* UART registers on the Bedrock start at 0x80 */ ++ ++extern int l1_serial_in_polled(void); ++extern int l1_control_in_polled(int); ++ ++/* Read a byte from the L1 port. kdb_serial is ignored */ ++static unsigned int ++serial_inp_sgi_l1(const struct kdb_serial *kdb_serial, int offset) ++{ ++ if (offset & 0x80) { ++ int counter = 10000; ++ unsigned int value; ++ while ( counter-- ) { ++ value = l1_serial_in_polled(); ++ /* Gobble up the 0's */ ++ if ( value ) ++ return(value); ++ } ++ return(0); ++ } ++ else { ++ return l1_control_in_polled(offset); ++ } ++} ++ ++/* Check if there is a byte ready at the L1 port. */ ++static int ++get_serial_char_sgi_l1(void) ++{ ++ unsigned char ch; ++ int status; ++ ++ if ((status = serial_inp_sgi_l1(&kdb_serial, UART_LSR)) & UART_LSR_DR) { ++ ch = serial_inp_sgi_l1(&kdb_serial, UART_RX | 0x80); /* bedrock offset */ ++ if (ch == 0x7f) ++ ch = 8; ++ return ch; ++ } ++ return -1; ++} ++ ++#else /* !CONFIG_SERIAL_SGI_L1_CONSOLE */ ++ ++#define get_serial_char_sgi_l1() -1 ++ ++#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE */ ++ ++/* Select the serial console input at run time, to handle generic kernels */ ++ ++static int ++get_serial_char(void) ++{ ++ switch (kdba_serial_console) { ++ case KDBA_SC_NONE: ++ return -1; ++ case KDBA_SC_STANDARD: ++ return get_serial_char_standard(); ++ case KDBA_SC_SGI_L1: ++ return get_serial_char_sgi_l1(); ++ } ++ /* gcc is not smart enough to realize that all paths return before here :( */ ++ return -1; ++} ++ ++#endif /* HAVE_KDBA_SERIAL_CONSOLE */ ++ ++#ifdef CONFIG_VT_CONSOLE ++ ++static int kbd_exists; ++ ++/* ++ * Check if the keyboard controller has a keypress for us. ++ * Some parts (Enter Release, LED change) are still blocking polled here, ++ * but hopefully they are all short. ++ */ ++static int get_kbd_char(void) ++{ ++ int scancode, scanstatus; ++ static int shift_lock; /* CAPS LOCK state (0-off, 1-on) */ ++ static int shift_key; /* Shift next keypress */ ++ static int ctrl_key; ++ u_short keychar; ++ extern u_short plain_map[], shift_map[], ctrl_map[]; ++ ++ if (KDB_FLAG(NO_I8042) || KDB_FLAG(NO_VT_CONSOLE) || ++ (inb(KBD_STATUS_REG) == 0xff && inb(KBD_DATA_REG) == 0xff)) { ++ kbd_exists = 0; ++ return -1; ++ } ++ kbd_exists = 1; ++ ++ if ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) ++ return -1; ++ ++ /* ++ * Fetch the scancode ++ */ ++ scancode = inb(KBD_DATA_REG); ++ scanstatus = inb(KBD_STATUS_REG); ++ ++ /* ++ * Ignore mouse events. ++ */ ++ if (scanstatus & KBD_STAT_MOUSE_OBF) ++ return -1; ++ ++ /* ++ * Ignore release, trigger on make ++ * (except for shift keys, where we want to ++ * keep the shift state so long as the key is ++ * held down). ++ */ ++ ++ if (((scancode&0x7f) == 0x2a) || ((scancode&0x7f) == 0x36)) { ++ /* ++ * Next key may use shift table ++ */ ++ if ((scancode & 0x80) == 0) { ++ shift_key=1; ++ } else { ++ shift_key=0; ++ } ++ return -1; ++ } ++ ++ if ((scancode&0x7f) == 0x1d) { ++ /* ++ * Left ctrl key ++ */ ++ if ((scancode & 0x80) == 0) { ++ ctrl_key = 1; ++ } else { ++ ctrl_key = 0; ++ } ++ return -1; ++ } ++ ++ if ((scancode & 0x80) != 0) ++ return -1; ++ ++ scancode &= 0x7f; ++ ++ /* ++ * Translate scancode ++ */ ++ ++ if (scancode == 0x3a) { ++ /* ++ * Toggle caps lock ++ */ ++ shift_lock ^= 1; ++ ++#ifdef KDB_BLINK_LED ++ kdb_toggleled(0x4); ++#endif ++ return -1; ++ } ++ ++ if (scancode == 0x0e) { ++ /* ++ * Backspace ++ */ ++ return 8; ++ } ++ ++ /* Special Key */ ++ switch (scancode) { ++ case 0xF: /* Tab */ ++ return 9; ++ case 0x53: /* Del */ ++ return 4; ++ case 0x47: /* Home */ ++ return 1; ++ case 0x4F: /* End */ ++ return 5; ++ case 0x4B: /* Left */ ++ return 2; ++ case 0x48: /* Up */ ++ return 16; ++ case 0x50: /* Down */ ++ return 14; ++ case 0x4D: /* Right */ ++ return 6; ++ } ++ ++ if (scancode == 0xe0) { ++ return -1; ++ } ++ ++ /* ++ * For Japanese 86/106 keyboards ++ * See comment in drivers/char/pc_keyb.c. ++ * - Masahiro Adegawa ++ */ ++ if (scancode == 0x73) { ++ scancode = 0x59; ++ } else if (scancode == 0x7d) { ++ scancode = 0x7c; ++ } ++ ++ if (!shift_lock && !shift_key && !ctrl_key) { ++ keychar = plain_map[scancode]; ++ } else if (shift_lock || shift_key) { ++ keychar = shift_map[scancode]; ++ } else if (ctrl_key) { ++ keychar = ctrl_map[scancode]; ++ } else { ++ keychar = 0x0020; ++ kdb_printf("Unknown state/scancode (%d)\n", scancode); ++ } ++ keychar &= 0x0fff; ++ switch (KTYP(keychar)) { ++ case KT_LETTER: ++ case KT_LATIN: ++ if (isprint(keychar)) ++ break; /* printable characters */ ++ /* drop through */ ++ case KT_SPEC: ++ if (keychar == K_ENTER) ++ break; ++ /* drop through */ ++ default: ++ return(-1); /* ignore unprintables */ ++ } ++ ++ if ((scancode & 0x7f) == 0x1c) { ++ /* ++ * enter key. All done. Absorb the release scancode. ++ */ ++ while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) ++ ; ++ ++ /* ++ * Fetch the scancode ++ */ ++ scancode = inb(KBD_DATA_REG); ++ scanstatus = inb(KBD_STATUS_REG); ++ ++ while (scanstatus & KBD_STAT_MOUSE_OBF) { ++ scancode = inb(KBD_DATA_REG); ++ scanstatus = inb(KBD_STATUS_REG); ++ } ++ ++ if (scancode != 0x9c) { ++ /* ++ * Wasn't an enter-release, why not? ++ */ ++ kdb_printf("kdb: expected enter got 0x%x status 0x%x\n", ++ scancode, scanstatus); ++ } ++ ++ kdb_printf("\n"); ++ return 13; ++ } ++ ++ return keychar & 0xff; ++} ++#endif /* CONFIG_VT_CONSOLE */ ++ ++#ifdef KDB_BLINK_LED ++ ++/* Leave numlock alone, setting it messes up laptop keyboards with the keypad ++ * mapped over normal keys. ++ */ ++static int kdba_blink_mask = 0x1 | 0x4; ++ ++#ifdef CONFIG_SMP ++#define BOGOMIPS (local_cpu_data->loops_per_jiffy/(500000/HZ)) ++#else ++#define BOGOMIPS (loops_per_jiffy/(500000/HZ)) ++#endif ++static int blink_led(void) ++{ ++ static long delay; ++ ++ if (kbd_exists == 0) ++ return -1; ++ ++ if (--delay < 0) { ++ if (BOGOMIPS == 0) /* early kdb */ ++ delay = 150000000/1000; /* arbitrary bogomips */ ++ else ++ delay = 150000000/BOGOMIPS; /* Roughly 1 second when polling */ ++ kdb_toggleled(kdba_blink_mask); ++ } ++ return -1; ++} ++#endif ++ ++get_char_func poll_funcs[] = { ++#if defined(CONFIG_VT_CONSOLE) ++ get_kbd_char, ++#endif ++#ifdef HAVE_KDBA_SERIAL_CONSOLE ++ get_serial_char, ++#endif ++#ifdef KDB_BLINK_LED ++ blink_led, ++#endif ++#ifdef CONFIG_KDB_USB ++ get_usb_char, ++#endif ++ NULL ++}; ++ ++/* Dummy versions of kdba_local_arch_setup, kdba_local_arch_cleanup. ++ * FIXME: ia64 with legacy keyboard might need the same code as i386. ++ */ ++ ++void kdba_local_arch_setup(void) {} ++void kdba_local_arch_cleanup(void) {} +--- /dev/null ++++ b/arch/ia64/kdb/kdba_jmp.S +@@ -0,0 +1,394 @@ ++/* ++ * Kernel Debugger Architecture Dependent Longjump Support. ++ */ ++ ++/* setjmp() and longjmp() assembler support for kdb on ia64. ++ ++ This code was copied from glibc CVS as of 2001-06-27 and modified where ++ necessary to fit the kernel. No glibc lines were changed or deleted, all ++ adjustments are wrapped in #ifdef __KERNEL__, except for the added ++ .mem.offset lines, they work in or out of the kenrel. The original code is ++ in sysdeps/unix/sysv/linux/ia64/{setjmp.S,__longjmp.S}. ++ ++ glibc has setjmp (save signals) and _setjmp (do not save signals). Kernel ++ code does not have signals, only kdba_setjmp_asm() is used. ++ ++ Keith Owens 2001-06-27 ++ */ ++ ++/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. ++ Contributed by David Mosberger-Tang . ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the GNU C Library; see the file COPYING.LIB. If ++ not, write to the Free Software Foundation, Inc., ++ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ ++ The layout of the jmp_buf is as follows. This is subject to change ++ and user-code should never depend on the particular layout of ++ jmp_buf! ++ ++ ++ offset: description: ++ ------- ------------ ++ 0x000 stack pointer (r12) ; unchangeable (see _JMPBUF_UNWINDS) ++ 0x008 r1 (gp) ++ 0x010 caller's unat ++ 0x018 fpsr ++ 0x020 r4 ++ 0x028 r5 ++ 0x030 r6 ++ 0x038 r7 ++ 0x040 rp (b0) ++ 0x048 b1 ++ 0x050 b2 ++ 0x058 b3 ++ 0x060 b4 ++ 0x068 b5 ++ 0x070 ar.pfs ++ 0x078 ar.lc ++ 0x080 pr ++ 0x088 ar.bsp ; unchangeable (see __longjmp.S) ++ 0x090 ar.unat ++ 0x098 &__jmp_buf ; address of the jmpbuf (needed to locate NaT bits in unat) ++ 0x0a0 f2 ++ 0x0b0 f3 ++ 0x0c0 f4 ++ 0x0d0 f5 ++ 0x0e0 f16 ++ 0x0f0 f17 ++ 0x100 f18 ++ 0x110 f19 ++ 0x120 f20 ++ 0x130 f21 ++ 0x130 f22 ++ 0x140 f23 ++ 0x150 f24 ++ 0x160 f25 ++ 0x170 f26 ++ 0x180 f27 ++ 0x190 f28 ++ 0x1a0 f29 ++ 0x1b0 f30 ++ 0x1c0 f31 */ ++ ++#ifndef __KERNEL__ ++ ++#include ++#include ++ ++ /* The following two entry points are the traditional entry points: */ ++ ++LEAF(setjmp) ++ alloc r8=ar.pfs,2,0,0,0 ++ mov in1=1 ++ br.cond.sptk.many __sigsetjmp ++END(setjmp) ++ ++LEAF(_setjmp) ++ alloc r8=ar.pfs,2,0,0,0 ++ mov in1=0 ++ br.cond.sptk.many __sigsetjmp ++END(_setjmp) ++ ++ /* __sigsetjmp(__jmp_buf buf, int savemask) */ ++ ++ENTRY(__sigsetjmp) ++ ++#else /* __KERNEL __ */ ++#include ++#define ret br.ret.sptk.few rp ++GLOBAL_ENTRY(kdba_setjmp) ++#endif /* !__KERNEL__ */ ++ ++ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) ++ alloc loc1=ar.pfs,2,2,2,0 ++ mov r16=ar.unat ++ ;; ++ mov r17=ar.fpsr ++ mov r2=in0 ++ add r3=8,in0 ++ ;; ++.mem.offset 0,0; ++ st8.spill.nta [r2]=sp,16 // r12 (sp) ++.mem.offset 8,0; ++ st8.spill.nta [r3]=gp,16 // r1 (gp) ++ ;; ++ st8.nta [r2]=r16,16 // save caller's unat ++ st8.nta [r3]=r17,16 // save fpsr ++ add r8=0xa0,in0 ++ ;; ++.mem.offset 160,0; ++ st8.spill.nta [r2]=r4,16 // r4 ++.mem.offset 168,0; ++ st8.spill.nta [r3]=r5,16 // r5 ++ add r9=0xb0,in0 ++ ;; ++ stf.spill.nta [r8]=f2,32 ++ stf.spill.nta [r9]=f3,32 ++ mov loc0=rp ++ .body ++ ;; ++ stf.spill.nta [r8]=f4,32 ++ stf.spill.nta [r9]=f5,32 ++ mov r17=b1 ++ ;; ++ stf.spill.nta [r8]=f16,32 ++ stf.spill.nta [r9]=f17,32 ++ mov r18=b2 ++ ;; ++ stf.spill.nta [r8]=f18,32 ++ stf.spill.nta [r9]=f19,32 ++ mov r19=b3 ++ ;; ++ stf.spill.nta [r8]=f20,32 ++ stf.spill.nta [r9]=f21,32 ++ mov r20=b4 ++ ;; ++ stf.spill.nta [r8]=f22,32 ++ stf.spill.nta [r9]=f23,32 ++ mov r21=b5 ++ ;; ++ stf.spill.nta [r8]=f24,32 ++ stf.spill.nta [r9]=f25,32 ++ mov r22=ar.lc ++ ;; ++ stf.spill.nta [r8]=f26,32 ++ stf.spill.nta [r9]=f27,32 ++ mov r24=pr ++ ;; ++ stf.spill.nta [r8]=f28,32 ++ stf.spill.nta [r9]=f29,32 ++ ;; ++ stf.spill.nta [r8]=f30 ++ stf.spill.nta [r9]=f31 ++ ++.mem.offset 0,0; ++ st8.spill.nta [r2]=r6,16 // r6 ++.mem.offset 8,0; ++ st8.spill.nta [r3]=r7,16 // r7 ++ ;; ++ mov r23=ar.bsp ++ mov r25=ar.unat ++#ifndef __KERNEL__ ++ mov out0=in0 ++#endif /* !__KERNEL__ */ ++ ++ st8.nta [r2]=loc0,16 // b0 ++ st8.nta [r3]=r17,16 // b1 ++#ifndef __KERNEL__ ++ mov out1=in1 ++#endif /* !__KERNEL__ */ ++ ;; ++ st8.nta [r2]=r18,16 // b2 ++ st8.nta [r3]=r19,16 // b3 ++ ;; ++ st8.nta [r2]=r20,16 // b4 ++ st8.nta [r3]=r21,16 // b5 ++ ;; ++ st8.nta [r2]=loc1,16 // ar.pfs ++ st8.nta [r3]=r22,16 // ar.lc ++ ;; ++ st8.nta [r2]=r24,16 // pr ++ st8.nta [r3]=r23,16 // ar.bsp ++ ;; ++ st8.nta [r2]=r25 // ar.unat ++ st8.nta [r3]=in0 // &__jmp_buf ++#ifndef __KERNEL__ ++ br.call.dpnt.few rp=__sigjmp_save ++.ret0: // force a new bundle ::q ++#endif /* !_KERNEL__ */ ++ mov r8=0 ++ mov rp=loc0 ++ mov ar.pfs=loc1 ++ ret ++#ifndef __KERNEL__ ++END(__sigsetjmp) ++ ++weak_extern(_setjmp) ++weak_extern(setjmp) ++ ++#else /* __KERNEL__ */ ++END(kdba_setjmp) ++#endif /* !_KERNEL__ */ ++ ++/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. ++ Contributed by David Mosberger-Tang . ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the GNU C Library; see the file COPYING.LIB. If ++ not, write to the Free Software Foundation, Inc., ++ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ ++ Note that __sigsetjmp() did NOT flush the register stack. Instead, ++ we do it here since __longjmp() is usually much less frequently ++ invoked than __sigsetjmp(). The only difficulty is that __sigsetjmp() ++ didn't (and wouldn't be able to) save ar.rnat either. This is a problem ++ because if we're not careful, we could end up loading random NaT bits. ++ There are two cases: ++ ++ (i) ar.bsp < ia64_rse_rnat_addr(jmpbuf.ar_bsp) ++ ar.rnat contains the desired bits---preserve ar.rnat ++ across loadrs and write to ar.bspstore ++ ++ (ii) ar.bsp >= ia64_rse_rnat_addr(jmpbuf.ar_bsp) ++ The desired ar.rnat is stored in ++ ia64_rse_rnat_addr(jmpbuf.ar_bsp). Load those ++ bits into ar.rnat after setting ar.bspstore. */ ++ ++#ifndef __KERNEL__ ++#include ++#include ++#endif /* !__KERNEL__ */ ++ ++# define pPos p6 /* is rotate count positive? */ ++# define pNeg p7 /* is rotate count negative? */ ++ ++ ++ /* __longjmp(__jmp_buf buf, int val) */ ++ ++#ifndef __KERNEL__ ++LEAF(__longjmp) ++#else /* __KERNEL__ */ ++GLOBAL_ENTRY(kdba_longjmp) ++#endif /* !__KERNEL__ */ ++ alloc r8=ar.pfs,2,1,0,0 ++ mov r27=ar.rsc ++ add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr ++ ;; ++ ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr ++ mov r10=ar.bsp ++ and r11=~0x3,r27 // clear ar.rsc.mode ++ ;; ++ flushrs // flush dirty regs to backing store (must be first in insn grp) ++ ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp ++ sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf ++ ;; ++ ld8 r25=[r2] // r25 <- jmpbuf.ar_unat ++ extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f ++ ;; ++ cmp.lt pNeg,pPos=r8,r0 ++ mov r2=in0 ++ ;; ++(pPos) mov r16=r8 ++(pNeg) add r16=64,r8 ++(pPos) sub r17=64,r8 ++(pNeg) sub r17=r0,r8 ++ ;; ++ mov ar.rsc=r11 // put RSE in enforced lazy mode ++ shr.u r8=r25,r16 ++ add r3=8,in0 // r3 <- &jmpbuf.r1 ++ shl r9=r25,r17 ++ ;; ++ or r25=r8,r9 ++ ;; ++ mov r26=ar.rnat ++ mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12) ++ ;; ++ ld8.fill.nta sp=[r2],16 // r12 (sp) ++ ld8.fill.nta gp=[r3],16 // r1 (gp) ++ dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp) ++ ;; ++ ld8.nta r16=[r2],16 // caller's unat ++ ld8.nta r17=[r3],16 // fpsr ++ ;; ++ ld8.fill.nta r4=[r2],16 // r4 ++ ld8.fill.nta r5=[r3],16 // r5 (gp) ++ cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp) ++ ;; ++ ld8.fill.nta r6=[r2],16 // r6 ++ ld8.fill.nta r7=[r3],16 // r7 ++ ;; ++ mov ar.unat=r16 // restore caller's unat ++ mov ar.fpsr=r17 // restore fpsr ++ ;; ++ ld8.nta r16=[r2],16 // b0 ++ ld8.nta r17=[r3],16 // b1 ++ ;; ++(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp) ++ mov ar.bspstore=r23 // restore ar.bspstore ++ ;; ++ ld8.nta r18=[r2],16 // b2 ++ ld8.nta r19=[r3],16 // b3 ++ ;; ++ ld8.nta r20=[r2],16 // b4 ++ ld8.nta r21=[r3],16 // b5 ++ ;; ++ ld8.nta r11=[r2],16 // ar.pfs ++ ld8.nta r22=[r3],56 // ar.lc ++ ;; ++ ld8.nta r24=[r2],32 // pr ++ mov b0=r16 ++ ;; ++ ldf.fill.nta f2=[r2],32 ++ ldf.fill.nta f3=[r3],32 ++ mov b1=r17 ++ ;; ++ ldf.fill.nta f4=[r2],32 ++ ldf.fill.nta f5=[r3],32 ++ mov b2=r18 ++ ;; ++ ldf.fill.nta f16=[r2],32 ++ ldf.fill.nta f17=[r3],32 ++ mov b3=r19 ++ ;; ++ ldf.fill.nta f18=[r2],32 ++ ldf.fill.nta f19=[r3],32 ++ mov b4=r20 ++ ;; ++ ldf.fill.nta f20=[r2],32 ++ ldf.fill.nta f21=[r3],32 ++ mov b5=r21 ++ ;; ++ ldf.fill.nta f22=[r2],32 ++ ldf.fill.nta f23=[r3],32 ++ mov ar.lc=r22 ++ ;; ++ ldf.fill.nta f24=[r2],32 ++ ldf.fill.nta f25=[r3],32 ++ cmp.eq p8,p9=0,in1 ++ ;; ++ ldf.fill.nta f26=[r2],32 ++ ldf.fill.nta f27=[r3],32 ++ mov ar.pfs=r11 ++ ;; ++ ldf.fill.nta f28=[r2],32 ++ ldf.fill.nta f29=[r3],32 ++ ;; ++ ldf.fill.nta f30=[r2] ++ ldf.fill.nta f31=[r3] ++(p8) mov r8=1 ++ ++ mov ar.rnat=r26 // restore ar.rnat ++ ;; ++ mov ar.rsc=r27 // restore ar.rsc ++(p9) mov r8=in1 ++ ++ invala // virt. -> phys. regnum mapping may change ++ mov pr=r24,-1 ++ ret ++#ifndef __KERNEL__ ++END(__longjmp) ++#else /* __KERNEL__ */ ++END(kdba_longjmp) ++#endif /* !_KERNEL__ */ +--- /dev/null ++++ b/arch/ia64/kdb/kdba_pod.c +@@ -0,0 +1,64 @@ ++/* ++ * Kernel Debugger Architecture Dependent POD functions. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Jesse Barnes"); ++MODULE_DESCRIPTION("Enter POD through KDB"); ++MODULE_LICENSE("GPL"); ++ ++/** ++ * kdba_pod - enter POD mode from kdb ++ * @argc: arg count ++ * @argv: arg values ++ * ++ * Enter POD mode from kdb using SGI SN specific SAL function call. ++ */ ++static int ++kdba_pod(int argc, const char **argv) ++{ ++ kdb_printf("WARNING: pod commands are dangerous unless you know exactly\n" ++ "what you are doing. If in doubt, type exit immediately.\n"); ++ return ia64_sn_pod_mode(); ++} ++ ++/** ++ * kdba_pod_init - register 'pod' command with kdb ++ * ++ * Register the 'pod' command with kdb at load time. ++ */ ++static int __init ++kdba_pod_init(void) ++{ ++ if (ia64_platform_is("sn2")) ++ kdb_register("pod", kdba_pod, NULL, "Enter POD", 0); ++ ++ return 0; ++} ++ ++/** ++ * kdba_pod_exit - unregister the 'pod' command ++ * ++ * Tell kdb that the 'pod' command is no longer available. ++ */ ++static void __exit ++kdba_pod_exit(void) ++{ ++ if (ia64_platform_is("sn2")) ++ kdb_unregister("pod"); ++} ++ ++module_init(kdba_pod_init) ++module_exit(kdba_pod_exit) +--- /dev/null ++++ b/arch/ia64/kdb/kdba_support.c +@@ -0,0 +1,1720 @@ ++/* ++ * Kernel Debugger Architecture Independent Support Functions ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. ++ * Copyright (C) David Mosberger-Tang ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_KDB_KDUMP ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#ifdef CONFIG_SMP ++#include ++#endif ++#include ++ ++struct kdb_running_process *kdb_running_process_save; /* [NR_CPUS] */ ++static int kdba_show_handlers = 0; ++ ++static int ++kdba_itm (int argc, const char **argv) ++{ ++ int diag; ++ unsigned long val; ++ ++ diag = kdbgetularg(argv[1], &val); ++ if (diag) ++ return diag; ++ kdb_printf("new itm=" kdb_machreg_fmt "\n", val); ++ ++ ia64_set_itm(val); ++ return 0; ++} ++ ++static void ++kdba_show_intregs(void) ++{ ++ u64 lid, tpr, lrr0, lrr1, itv, pmv, cmcv; ++ ++ asm ("mov %0=cr.lid" : "=r"(lid)); ++ asm ("mov %0=cr.tpr" : "=r"(tpr)); ++ asm ("mov %0=cr.lrr0" : "=r"(lrr0)); ++ asm ("mov %0=cr.lrr1" : "=r"(lrr1)); ++ kdb_printf("lid=" kdb_machreg_fmt ", tpr=" kdb_machreg_fmt ", lrr0=" kdb_machreg_fmt ", llr1=" kdb_machreg_fmt "\n", lid, tpr, lrr0, lrr1); ++ ++ asm ("mov %0=cr.itv" : "=r"(itv)); ++ asm ("mov %0=cr.pmv" : "=r"(pmv)); ++ asm ("mov %0=cr.cmcv" : "=r"(cmcv)); ++ kdb_printf("itv=" kdb_machreg_fmt ", pmv=" kdb_machreg_fmt ", cmcv=" kdb_machreg_fmt "\n", itv, pmv, cmcv); ++ ++ kdb_printf("irr=0x%016lx,0x%016lx,0x%016lx,0x%016lx\n", ++ ia64_getreg(_IA64_REG_CR_IRR0), ia64_getreg(_IA64_REG_CR_IRR1), ia64_getreg(_IA64_REG_CR_IRR2), ia64_getreg(_IA64_REG_CR_IRR3)); ++ ++ kdb_printf("itc=0x%016lx, itm=0x%016lx\n", ia64_get_itc(), ia64_get_itm()); ++} ++ ++static int ++kdba_sir (int argc, const char **argv) ++{ ++ kdba_show_intregs(); ++ ++ return 0; ++} ++ ++/* ++ * kdba_pt_regs ++ * ++ * Format a struct pt_regs ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * If no address is supplied, it uses the current irq pt_regs. ++ */ ++ ++static int ++kdba_pt_regs(int argc, const char **argv) ++{ ++ int diag; ++ kdb_machreg_t addr; ++ long offset = 0; ++ int nextarg; ++ struct pt_regs *p; ++ ++ if (argc == 0) { ++ addr = (kdb_machreg_t) get_irq_regs(); ++ } else if (argc == 1) { ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ ++ p = (struct pt_regs *) addr; ++ kdb_printf("struct pt_regs %p-%p\n", p, (unsigned char *)p + sizeof(*p) - 1); ++ kdb_print_nameval("b6", p->b6); ++ kdb_print_nameval("b7", p->b7); ++ kdb_printf(" ar_csd 0x%lx\n", p->ar_csd); ++ kdb_printf(" ar_ssd 0x%lx\n", p->ar_ssd); ++ kdb_print_nameval("r8", p->r8); ++ kdb_print_nameval("r9", p->r9); ++ kdb_print_nameval("r10", p->r10); ++ kdb_print_nameval("r11", p->r11); ++ kdb_printf(" cr_ipsr 0x%lx\n", p->cr_ipsr); ++ kdb_print_nameval("cr_iip", p->cr_iip); ++ kdb_printf(" cr_ifs 0x%lx\n", p->cr_ifs); ++ kdb_printf(" ar_unat 0x%lx\n", p->ar_unat); ++ kdb_printf(" ar_pfs 0x%lx\n", p->ar_pfs); ++ kdb_printf(" ar_rsc 0x%lx\n", p->ar_rsc); ++ kdb_printf(" ar_rnat 0x%lx\n", p->ar_rnat); ++ kdb_printf(" ar_bspstore 0x%lx\n", p->ar_bspstore); ++ kdb_printf(" pr 0x%lx\n", p->pr); ++ kdb_print_nameval("b0", p->b0); ++ kdb_printf(" loadrs 0x%lx\n", p->loadrs); ++ kdb_print_nameval("r1", p->r1); ++ kdb_print_nameval("r12", p->r12); ++ kdb_print_nameval("r13", p->r13); ++ kdb_printf(" ar_fpsr 0x%lx\n", p->ar_fpsr); ++ kdb_print_nameval("r15", p->r15); ++ kdb_print_nameval("r14", p->r14); ++ kdb_print_nameval("r2", p->r2); ++ kdb_print_nameval("r3", p->r3); ++ kdb_print_nameval("r16", p->r16); ++ kdb_print_nameval("r17", p->r17); ++ kdb_print_nameval("r18", p->r18); ++ kdb_print_nameval("r19", p->r19); ++ kdb_print_nameval("r20", p->r20); ++ kdb_print_nameval("r21", p->r21); ++ kdb_print_nameval("r22", p->r22); ++ kdb_print_nameval("r23", p->r23); ++ kdb_print_nameval("r24", p->r24); ++ kdb_print_nameval("r25", p->r25); ++ kdb_print_nameval("r26", p->r26); ++ kdb_print_nameval("r27", p->r27); ++ kdb_print_nameval("r28", p->r28); ++ kdb_print_nameval("r29", p->r29); ++ kdb_print_nameval("r30", p->r30); ++ kdb_print_nameval("r31", p->r31); ++ kdb_printf(" ar_ccv 0x%lx\n", p->ar_ccv); ++ kdb_printf(" f6 0x%lx 0x%lx\n", p->f6.u.bits[0], p->f6.u.bits[1]); ++ kdb_printf(" f7 0x%lx 0x%lx\n", p->f7.u.bits[0], p->f7.u.bits[1]); ++ kdb_printf(" f8 0x%lx 0x%lx\n", p->f8.u.bits[0], p->f8.u.bits[1]); ++ kdb_printf(" f9 0x%lx 0x%lx\n", p->f9.u.bits[0], p->f9.u.bits[1]); ++ kdb_printf(" f10 0x%lx 0x%lx\n", p->f10.u.bits[0], p->f10.u.bits[1]); ++ kdb_printf(" f11 0x%lx 0x%lx\n", p->f11.u.bits[0], p->f11.u.bits[1]); ++ ++ return 0; ++} ++ ++/* ++ * kdba_stackdepth ++ * ++ * Print processes that are using more than a specific percentage of their ++ * stack. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * If no percentage is supplied, it uses 60. ++ */ ++ ++static int ++kdba_stackdepth(int argc, const char **argv) ++{ ++ int diag, threshold, used; ++ unsigned long percentage; ++ unsigned long esp; ++ long offset = 0; ++ int nextarg; ++ struct task_struct *p, *g; ++ struct switch_stack *sw; ++ ++ if (argc == 0) { ++ percentage = 60; ++ } else if (argc == 1) { ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &percentage, &offset, NULL); ++ if (diag) ++ return diag; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ percentage = max_t(int, percentage, 1); ++ percentage = min_t(int, percentage, 100); ++ threshold = ((2 * THREAD_SIZE * percentage) / 100 + 1) >> 1; ++ kdb_printf("stackdepth: processes using more than %ld%% (%d bytes) of stack\n", ++ percentage, threshold); ++ kdb_do_each_thread(g, p) { ++ if (kdb_task_has_cpu(p)) { ++ struct kdb_running_process *krp = kdb_running_process + kdb_process_cpu(p); ++ if (krp->seqno) ++ sw = krp->arch.sw; ++ else ++ sw = NULL; ++ } else ++ sw = (struct switch_stack *) (p->thread.ksp + 16); ++ if (!sw) ++ continue; ++ esp = (unsigned long) sw; ++ used = THREAD_SIZE - (esp - sw->ar_bspstore); ++ if (used >= threshold) { ++ kdb_ps1(p); ++ kdb_printf(" esp %lx bsp %lx used %d\n", esp, sw->ar_bspstore, used); ++ } ++ } kdb_while_each_thread(g, p); ++ ++ return 0; ++} ++ ++/* ++ * kdb_switch_stack ++ * ++ * Format a struct switch_stack ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * If no address is supplied, it uses kdb_running_process[smp_processor_id()].arch.sw. ++ */ ++ ++static int ++kdba_switch_stack(int argc, const char **argv) ++{ ++ int diag; ++ kdb_machreg_t addr; ++ long offset = 0; ++ int nextarg; ++ struct switch_stack *p; ++ ++ if (argc == 0) { ++ addr = (kdb_machreg_t) kdb_running_process[smp_processor_id()].arch.sw; ++ } else if (argc == 1) { ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ ++ p = (struct switch_stack *) addr; ++ kdb_printf("struct switch_stack %p-%p\n", p, (unsigned char *)p + sizeof(*p) - 1); ++ kdb_printf(" caller_unat 0x%lx\n", p->caller_unat); ++ kdb_printf(" ar_fpsr 0x%lx\n", p->ar_fpsr); ++ kdb_printf(" f2 0x%lx 0x%lx\n", p->f2.u.bits[0], p->f2.u.bits[1]); ++ kdb_printf(" f3 0x%lx 0x%lx\n", p->f3.u.bits[0], p->f3.u.bits[1]); ++ kdb_printf(" f4 0x%lx 0x%lx\n", p->f4.u.bits[0], p->f4.u.bits[1]); ++ kdb_printf(" f5 0x%lx 0x%lx\n", p->f5.u.bits[0], p->f5.u.bits[1]); ++ kdb_printf(" f12 0x%lx 0x%lx\n", p->f12.u.bits[0], p->f12.u.bits[1]); ++ kdb_printf(" f13 0x%lx 0x%lx\n", p->f13.u.bits[0], p->f13.u.bits[1]); ++ kdb_printf(" f14 0x%lx 0x%lx\n", p->f14.u.bits[0], p->f14.u.bits[1]); ++ kdb_printf(" f15 0x%lx 0x%lx\n", p->f15.u.bits[0], p->f15.u.bits[1]); ++ kdb_printf(" f16 0x%lx 0x%lx\n", p->f16.u.bits[0], p->f16.u.bits[1]); ++ kdb_printf(" f17 0x%lx 0x%lx\n", p->f17.u.bits[0], p->f17.u.bits[1]); ++ kdb_printf(" f18 0x%lx 0x%lx\n", p->f18.u.bits[0], p->f18.u.bits[1]); ++ kdb_printf(" f19 0x%lx 0x%lx\n", p->f19.u.bits[0], p->f19.u.bits[1]); ++ kdb_printf(" f20 0x%lx 0x%lx\n", p->f20.u.bits[0], p->f20.u.bits[1]); ++ kdb_printf(" f21 0x%lx 0x%lx\n", p->f21.u.bits[0], p->f21.u.bits[1]); ++ kdb_printf(" f22 0x%lx 0x%lx\n", p->f22.u.bits[0], p->f22.u.bits[1]); ++ kdb_printf(" f23 0x%lx 0x%lx\n", p->f23.u.bits[0], p->f23.u.bits[1]); ++ kdb_printf(" f24 0x%lx 0x%lx\n", p->f24.u.bits[0], p->f24.u.bits[1]); ++ kdb_printf(" f25 0x%lx 0x%lx\n", p->f25.u.bits[0], p->f25.u.bits[1]); ++ kdb_printf(" f26 0x%lx 0x%lx\n", p->f26.u.bits[0], p->f26.u.bits[1]); ++ kdb_printf(" f27 0x%lx 0x%lx\n", p->f27.u.bits[0], p->f27.u.bits[1]); ++ kdb_printf(" f28 0x%lx 0x%lx\n", p->f28.u.bits[0], p->f28.u.bits[1]); ++ kdb_printf(" f29 0x%lx 0x%lx\n", p->f29.u.bits[0], p->f29.u.bits[1]); ++ kdb_printf(" f30 0x%lx 0x%lx\n", p->f30.u.bits[0], p->f30.u.bits[1]); ++ kdb_printf(" f31 0x%lx 0x%lx\n", p->f31.u.bits[0], p->f31.u.bits[1]); ++ kdb_print_nameval("r4", p->r4); ++ kdb_print_nameval("r5", p->r5); ++ kdb_print_nameval("r6", p->r6); ++ kdb_print_nameval("r7", p->r7); ++ kdb_print_nameval("b0", p->b0); ++ kdb_print_nameval("b1", p->b1); ++ kdb_print_nameval("b2", p->b2); ++ kdb_print_nameval("b3", p->b3); ++ kdb_print_nameval("b4", p->b4); ++ kdb_print_nameval("b5", p->b5); ++ kdb_printf(" ar_pfs 0x%lx\n", p->ar_pfs); ++ kdb_printf(" ar_lc 0x%lx\n", p->ar_lc); ++ kdb_printf(" ar_unat 0x%lx\n", p->ar_unat); ++ kdb_printf(" ar_rnat 0x%lx\n", p->ar_rnat); ++ kdb_printf(" ar_bspstore 0x%lx\n", p->ar_bspstore); ++ kdb_printf(" pr 0x%lx\n", p->pr); ++ ++ return 0; ++} ++ ++/* ++ * kdb_minstate ++ * ++ * Format the PAL minstate area. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * None. ++ */ ++ ++static int ++kdba_minstate(int argc, const char **argv) ++{ ++ int diag; ++ kdb_machreg_t addr; ++ long offset = 0; ++ int nextarg; ++ pal_min_state_area_t *p; ++ ++ if (argc == 1) { ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ ++ p = (pal_min_state_area_t *) addr; ++ kdb_printf("PAL minstate %p-%p\n", p, (unsigned char *)p + sizeof(*p) - 1); ++ kdb_printf(" pmsa_nat_bits 0x%lx\n", p->pmsa_nat_bits); ++ kdb_print_nameval("r1", p->pmsa_gr[1-1]); ++ kdb_print_nameval("r2", p->pmsa_gr[2-1]); ++ kdb_print_nameval("r3", p->pmsa_gr[3-1]); ++ kdb_print_nameval("r4", p->pmsa_gr[4-1]); ++ kdb_print_nameval("r5", p->pmsa_gr[5-1]); ++ kdb_print_nameval("r6", p->pmsa_gr[6-1]); ++ kdb_print_nameval("r7", p->pmsa_gr[7-1]); ++ kdb_print_nameval("r8", p->pmsa_gr[8-1]); ++ kdb_print_nameval("r9", p->pmsa_gr[9-1]); ++ kdb_print_nameval("r10", p->pmsa_gr[10-1]); ++ kdb_print_nameval("r11", p->pmsa_gr[11-1]); ++ kdb_print_nameval("r12", p->pmsa_gr[12-1]); ++ kdb_print_nameval("r13", p->pmsa_gr[13-1]); ++ kdb_print_nameval("r14", p->pmsa_gr[14-1]); ++ kdb_print_nameval("r15", p->pmsa_gr[15-1]); ++ kdb_printf(" Bank 0\n"); ++ kdb_print_nameval("r16", p->pmsa_bank0_gr[16-16]); ++ kdb_print_nameval("r17", p->pmsa_bank0_gr[17-16]); ++ kdb_print_nameval("r18", p->pmsa_bank0_gr[18-16]); ++ kdb_print_nameval("r19", p->pmsa_bank0_gr[19-16]); ++ kdb_print_nameval("r20", p->pmsa_bank0_gr[20-16]); ++ kdb_print_nameval("r21", p->pmsa_bank0_gr[21-16]); ++ kdb_print_nameval("r22", p->pmsa_bank0_gr[22-16]); ++ kdb_print_nameval("r23", p->pmsa_bank0_gr[23-16]); ++ kdb_print_nameval("r24", p->pmsa_bank0_gr[24-16]); ++ kdb_print_nameval("r25", p->pmsa_bank0_gr[25-16]); ++ kdb_print_nameval("r26", p->pmsa_bank0_gr[26-16]); ++ kdb_print_nameval("r27", p->pmsa_bank0_gr[27-16]); ++ kdb_print_nameval("r28", p->pmsa_bank0_gr[28-16]); ++ kdb_print_nameval("r29", p->pmsa_bank0_gr[29-16]); ++ kdb_print_nameval("r30", p->pmsa_bank0_gr[30-16]); ++ kdb_print_nameval("r31", p->pmsa_bank0_gr[31-16]); ++ kdb_printf(" Bank 1\n"); ++ kdb_print_nameval("r16", p->pmsa_bank1_gr[16-16]); ++ kdb_print_nameval("r17", p->pmsa_bank1_gr[17-16]); ++ kdb_print_nameval("r18", p->pmsa_bank1_gr[18-16]); ++ kdb_print_nameval("r19", p->pmsa_bank1_gr[19-16]); ++ kdb_print_nameval("r20", p->pmsa_bank1_gr[20-16]); ++ kdb_print_nameval("r21", p->pmsa_bank1_gr[21-16]); ++ kdb_print_nameval("r22", p->pmsa_bank1_gr[22-16]); ++ kdb_print_nameval("r23", p->pmsa_bank1_gr[23-16]); ++ kdb_print_nameval("r24", p->pmsa_bank1_gr[24-16]); ++ kdb_print_nameval("r25", p->pmsa_bank1_gr[25-16]); ++ kdb_print_nameval("r26", p->pmsa_bank1_gr[26-16]); ++ kdb_print_nameval("r27", p->pmsa_bank1_gr[27-16]); ++ kdb_print_nameval("r28", p->pmsa_bank1_gr[28-16]); ++ kdb_print_nameval("r29", p->pmsa_bank1_gr[29-16]); ++ kdb_print_nameval("r30", p->pmsa_bank1_gr[30-16]); ++ kdb_print_nameval("r31", p->pmsa_bank1_gr[31-16]); ++ kdb_printf(" pr 0x%lx\n", p->pmsa_pr); ++ kdb_print_nameval("b0", p->pmsa_br0); ++ kdb_printf(" ar.rsc 0x%lx\n", p->pmsa_rsc); ++ kdb_print_nameval("cr.iip", p->pmsa_iip); ++ kdb_printf(" cr.ipsr 0x%lx\n", p->pmsa_ipsr); ++ kdb_printf(" cr.ifs 0x%lx\n", p->pmsa_ifs); ++ kdb_print_nameval("cr.xip", p->pmsa_xip); ++ kdb_printf(" cr.xpsr 0x%lx\n", p->pmsa_xpsr); ++ kdb_printf(" cr.xfs 0x%lx\n", p->pmsa_xfs); ++ kdb_print_nameval("b1", p->pmsa_br1); ++ ++ return 0; ++} ++ ++/* ++ * kdba_cpuinfo ++ * ++ * Format struct cpuinfo_ia64. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * If no cpu is supplied, it prints cpuinfo for all online cpus. ++ */ ++ ++static int ++kdba_cpuinfo(int argc, const char **argv) ++{ ++ int diag; ++ unsigned long cpunum = -1; ++ long offset = 0; ++ int nextarg, c, i; ++ struct cpuinfo_ia64 *cpuinfo; ++ ++ if (argc == 1) { ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &cpunum, &offset, NULL); ++ if (diag) ++ return diag; ++ if (cpunum >= NR_CPUS || !cpu_online(cpunum)) ++ return KDB_BADCPUNUM; ++ } else if (argc > 1) { ++ return KDB_ARGCOUNT; ++ } ++ ++ for (c = (cpunum == -1 ? 0 : cpunum); ++ c < (cpunum == -1 ? NR_CPUS : cpunum+1); ++ ++c) { ++ if (!cpu_online(c)) ++ continue; ++ cpuinfo = cpu_data(c); ++ kdb_printf("struct cpuinfo_ia64 for cpu %d is at 0x%p\n", c, cpuinfo); ++ kdb_printf(" softirq_pending 0x%x\n", cpuinfo->softirq_pending); ++ kdb_printf(" itm_delta %ld\n", cpuinfo->itm_delta); ++ kdb_printf(" itm_next %ld\n", cpuinfo->itm_next); ++ kdb_printf(" nsec_per_cyc %ld\n", cpuinfo->nsec_per_cyc); ++ kdb_printf(" unimpl_va_mask 0x%lx\n", cpuinfo->unimpl_va_mask); ++ kdb_printf(" unimpl_pa_mask 0x%lx\n", cpuinfo->unimpl_pa_mask); ++ kdb_printf(" itc_freq %ld\n", cpuinfo->itc_freq); ++ kdb_printf(" proc_freq %ld\n", cpuinfo->proc_freq); ++ kdb_printf(" cyc_per_usec %ld\n", cpuinfo->cyc_per_usec); ++ kdb_printf(" cyc_per_usec %ld\n", cpuinfo->cyc_per_usec); ++#if 0 /* RJA per-cpu MCA */ ++ kdb_printf(" percpu_paddr 0x%lx\n", cpuinfo->percpu_paddr); ++#endif ++ kdb_printf(" ptce_base 0x%lx\n", cpuinfo->ptce_base); ++ kdb_printf(" ptce_count %d %d\n", cpuinfo->ptce_count[0], cpuinfo->ptce_count[1]); ++ kdb_printf(" ptce_stride %d %d\n", cpuinfo->ptce_stride[0], cpuinfo->ptce_stride[1]); ++#if 0 /* RJA per-cpu MCA */ ++ kdb_printf(" pal_paddr 0x%lx\n", cpuinfo->pal_paddr); ++ kdb_printf(" pal_base 0x%lx\n", cpuinfo->pal_base); ++#endif ++ kdb_printf(" ksoftirqd 0x%p\n", cpuinfo->ksoftirqd); ++#ifdef CONFIG_SMP ++ kdb_printf(" loops_per_jiffy %ld\n", cpuinfo->loops_per_jiffy); ++ kdb_printf(" cpu %d\n", cpuinfo->cpu); ++ kdb_printf(" socket_id %d\n", cpuinfo->socket_id); ++ kdb_printf(" core_id %d\n", cpuinfo->core_id); ++ kdb_printf(" thread_id %d\n", cpuinfo->thread_id); ++ kdb_printf(" num_log %d\n", cpuinfo->num_log); ++ kdb_printf(" cores_per_socket %d\n", cpuinfo->cores_per_socket); ++ kdb_printf(" threads_per_core %d\n", cpuinfo->threads_per_core); ++#endif ++ kdb_printf(" ppn 0x%lx\n", cpuinfo->ppn); ++ kdb_printf(" features 0x%lx\n", cpuinfo->features); ++ kdb_printf(" number %d\n", cpuinfo->number); ++ kdb_printf(" revision %d\n", cpuinfo->revision); ++ kdb_printf(" model %d\n", cpuinfo->model); ++ kdb_printf(" family %d\n", cpuinfo->family); ++ kdb_printf(" archrev %d\n", cpuinfo->archrev); ++ kdb_printf(" vendor "); ++ for (i = 0; i < sizeof(cpuinfo->vendor); ++i) ++ kdb_printf(" 0x%02x", cpuinfo->vendor[i]); ++ kdb_printf("\n"); ++#ifdef CONFIG_NUMA ++ kdb_printf(" node_data 0x%p\n", cpuinfo->node_data); ++#endif ++#if 0 /* RJA per-cpu MCA */ ++ kdb_printf(" ia64_pa_mca_data 0x%p\n", cpuinfo->ia64_pa_mca_data); ++#endif ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_KDB_HARDWARE_BREAKPOINTS ++void ++kdba_installdbreg(kdb_bp_t *bp) ++{ ++ unsigned long mask; ++ unsigned int regbase; ++ static unsigned long masks[] = { ++ 0x00FFFFFFFFFFFFFFUL, // 1 byte long ++ 0x00FFFFFFFFFFFFFEUL, // 2 bytes long ++ 0x0000000000000000UL, // invalid ++ 0x00FFFFFFFFFFFFFCUL // 4 bytes long ++ }; ++ static unsigned char modes[] = { ++ 0x81, // instruction => x, plm=priv level 0 only ++ 0x41, // write => w, plm=priv level 0 only ++ 0x00, // io ++ 0x81 // read => r, plm=priv level 0 only ++ }; ++ ++ /* Note that bp->bp_hard[NR_CPU] is for x86. ++ * The ia64 uses bp->bp_hard[0] only. ++ */ ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_installdbreg:\n"); ++ mask = masks[bp->bp_hard[0]->bph_length] | ++ (((unsigned long)(modes[bp->bp_hard[0]->bph_mode])) << 56); ++ regbase = 2*bp->bp_hard[0]->bph_reg; ++ ++ switch (bp->bp_hard[0]->bph_mode) ++ { ++ case 1: ++ case 3: ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdba_installdbreg: dbr[%u]=%016lx\n", ++ regbase, bp->bp_addr); ++ kdb_printf("kdba_installdbreg: dbr[%u]=%016lx\n", ++ regbase+1, mask); ++ } ++ ++ ia64_set_dbr(regbase, bp->bp_addr); ++ ia64_set_dbr(regbase+1, mask); ++ ia64_srlz_d(); ++ break; ++ ++ case 0: /* instruction */ ++#if 0 ++ ia64_set_ibr(regbase, bp->bp_addr); ++ ia64_set_ibr(regbase+1, mask); ++ ia64_srlz_d(); ++#else ++ kdb_printf("\"instr\" mode not implemented\n"); ++#endif ++ break; ++ ++ case 2: /* io */ ++ kdb_printf("\"io\" mode not implemented\n"); ++ break; ++ } ++} ++ ++void ++kdba_removedbreg(kdb_bp_t *bp) ++{ ++ unsigned int regbase = 2*bp->bp_hard[0]->bph_reg; ++ ++ /* Note that bp->bp_hard[NR_CPU] is for x86. ++ * The ia64 uses bp->bp_hard[0] only. ++ */ ++ switch (bp->bp_hard[0]->bph_mode) ++ { ++ case 1: ++ case 3: ++ ia64_set_dbr(regbase, 0); ++ ia64_set_dbr(regbase+1, 0); ++ ia64_srlz_d(); ++ break; ++ ++ case 0: /* instruction */ ++#if 0 ++ ia64_set_ibr(regbase, 0); ++ ia64_set_ibr(regbase+1, 0); ++ ia64_srlz_d(); ++#else ++ kdb_printf("\"instr\" mode not implemented\n"); ++#endif ++ break; ++ ++ case 2: /* io */ ++ kdb_printf("\"io\" mode not implemented\n"); ++ break; ++ } ++} ++#endif /* CONFIG_KDB_HARDWARE_BREAKPOINTS */ ++ ++ ++static kdb_machreg_t ++kdba_getdr(int regnum) ++{ ++ kdb_machreg_t contents = 0; ++ unsigned long reg = (unsigned long)regnum; ++ ++ __asm__ ("mov %0=ibr[%1]"::"r"(contents),"r"(reg)); ++// __asm__ ("mov ibr[%0]=%1"::"r"(dbreg_cond),"r"(value)); ++ ++ return contents; ++} ++ ++ ++static void ++get_fault_regs(fault_regs_t *fr) ++{ ++ fr->ifa = 0 ; ++ fr->isr = 0 ; ++ ++ __asm__ ("rsm psr.ic;;") ; ++ ia64_srlz_d(); ++ __asm__ ("mov %0=cr.ifa" : "=r"(fr->ifa)); ++ __asm__ ("mov %0=cr.isr" : "=r"(fr->isr)); ++ __asm__ ("ssm psr.ic;;") ; ++ ia64_srlz_d(); ++} ++ ++static void ++show_kernel_regs (void) ++{ ++ unsigned long kr[8]; ++ int i; ++ ++ asm ("mov %0=ar.k0" : "=r"(kr[0])); asm ("mov %0=ar.k1" : "=r"(kr[1])); ++ asm ("mov %0=ar.k2" : "=r"(kr[2])); asm ("mov %0=ar.k3" : "=r"(kr[3])); ++ asm ("mov %0=ar.k4" : "=r"(kr[4])); asm ("mov %0=ar.k5" : "=r"(kr[5])); ++ asm ("mov %0=ar.k6" : "=r"(kr[6])); asm ("mov %0=ar.k7" : "=r"(kr[7])); ++ ++ for (i = 0; i < 4; ++i) ++ kdb_printf(" kr%d: %016lx kr%d: %016lx\n", 2*i, kr[2*i], 2*i+1, kr[2*i+1]); ++ kdb_printf("\n"); ++} ++ ++static int ++change_cur_stack_frame(int regno, unsigned long *contents) ++{ ++ unsigned long sof, i, cfm, sp, *bsp, __user *ubsp; ++ struct unw_frame_info info; ++ mm_segment_t old_fs; ++ int cpu = kdb_process_cpu(kdb_current_task); ++ struct kdb_running_process *krp = kdb_running_process + cpu; ++ ++ if (kdb_current_task != krp->p) { ++ kdb_printf("Stacked registers are not available for tasks that are not running.\n"); ++ kdb_printf("Use bt with a large BTARGS value instead\n"); ++ return 0; ++ } ++ unw_init_frame_info(&info, krp->p, krp->arch.sw); ++ do { ++ if (unw_unwind(&info) < 0) { ++ kdb_printf("Failed to unwind\n"); ++ return 0; ++ } ++ unw_get_sp(&info, &sp); ++ } while (sp <= (unsigned long) kdb_current_regs); ++ unw_get_bsp(&info, (unsigned long *) &bsp); ++ unw_get_cfm(&info, &cfm); ++ ++ if (!bsp) { ++ kdb_printf("Unable to get Current Stack Frame\n"); ++ return 0; ++ } ++ ++ sof = (cfm & 0x7f); ++ ++ if(((unsigned long)regno - 32) >= (sof - 2)) return 1; ++ ++ old_fs = set_fs(KERNEL_DS); ++ for (i = 0; i < (regno - 32); ++i) ++ bsp = ia64_rse_skip_regs(bsp, 1); ++ ubsp = (unsigned long __user *) bsp; ++ put_user(*contents, ubsp); ++ set_fs(old_fs); ++ ++ return 0 ; ++} ++ ++static int ++show_cur_stack_frame(int regno, unsigned long *contents) ++{ ++ unsigned long sof, i, cfm, val, sp, *bsp, __user *ubsp; ++ struct unw_frame_info info; ++ mm_segment_t old_fs; ++ int cpu = kdb_process_cpu(kdb_current_task); ++ struct kdb_running_process *krp = kdb_running_process + cpu; ++ ++ if (kdb_current_task != krp->p) { ++ kdb_printf("Stacked registers are not available for tasks that are not running.\n"); ++ kdb_printf("Use bt with a large BTARGS value instead\n"); ++ return 0; ++ } ++ unw_init_frame_info(&info, krp->p, krp->arch.sw); ++ do { ++ if (unw_unwind(&info) < 0) { ++ kdb_printf("Failed to unwind\n"); ++ return 0; ++ } ++ unw_get_sp(&info, &sp); ++ } while (sp <= (unsigned long) kdb_current_regs); ++ unw_get_bsp(&info, (unsigned long *) &bsp); ++ unw_get_cfm(&info, &cfm); ++ ++ if (!bsp) { ++ kdb_printf("Unable to display Current Stack Frame\n"); ++ return 0; ++ } ++ ++ sof = (cfm & 0x7f); ++ ++ if (regno) { ++ if ((unsigned) regno - 32 >= sof) ++ return 0; ++ bsp = ia64_rse_skip_regs(bsp, regno - 32); ++ old_fs = set_fs(KERNEL_DS); ++ ubsp = (unsigned long __user *) bsp; ++ get_user(val, ubsp); ++ set_fs(old_fs); ++ *contents = val; ++ return 1; ++ } ++ ++ old_fs = set_fs(KERNEL_DS); ++ for (i = 0; i < sof; ++i) { ++ ubsp = (unsigned long __user *) bsp; ++ get_user(val, ubsp); ++ kdb_printf(" r%lu: %016lx ", 32 + i, val); ++ if (!((i + 1) % 3)) ++ kdb_printf("\n"); ++ bsp = ia64_rse_skip_regs(bsp, 1); ++ } ++ kdb_printf("\n"); ++ set_fs(old_fs); ++ ++ return 0 ; ++} ++ ++/* ++ * kdba_getregcontents ++ * ++ * Return the contents of the register specified by the ++ * input string argument. Return an error if the string ++ * does not match a machine register. ++ * ++ * The following pseudo register names are supported: ++ * ®s - Prints address of exception frame ++ * kesp - Prints kernel stack pointer at time of fault ++ * sstk - Prints switch stack for ia64 ++ * % - Uses the value of the registers at the ++ * last time the user process entered kernel ++ * mode, instead of the registers at the time ++ * kdb was entered. ++ * ++ * Parameters: ++ * regname Pointer to string naming register ++ * regs Pointer to structure containing registers. ++ * Outputs: ++ * *contents Pointer to unsigned long to recieve register contents ++ * Returns: ++ * 0 Success ++ * KDB_BADREG Invalid register name ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * Note that this function is really machine independent. The kdb ++ * register list is not, however. ++ */ ++ ++static struct kdbregs { ++ char *reg_name; ++ size_t reg_offset; ++} kdbreglist[] = { ++ { "psr", offsetof(struct pt_regs, cr_ipsr) }, ++ { "ifs", offsetof(struct pt_regs, cr_ifs) }, ++ { "ip", offsetof(struct pt_regs, cr_iip) }, ++ ++ { "unat", offsetof(struct pt_regs, ar_unat) }, ++ { "pfs", offsetof(struct pt_regs, ar_pfs) }, ++ { "rsc", offsetof(struct pt_regs, ar_rsc) }, ++ ++ { "rnat", offsetof(struct pt_regs, ar_rnat) }, ++ { "bsps", offsetof(struct pt_regs, ar_bspstore) }, ++ { "pr", offsetof(struct pt_regs, pr) }, ++ ++ { "ldrs", offsetof(struct pt_regs, loadrs) }, ++ { "ccv", offsetof(struct pt_regs, ar_ccv) }, ++ { "fpsr", offsetof(struct pt_regs, ar_fpsr) }, ++ ++ { "b0", offsetof(struct pt_regs, b0) }, ++ { "b6", offsetof(struct pt_regs, b6) }, ++ { "b7", offsetof(struct pt_regs, b7) }, ++ ++ { "r1",offsetof(struct pt_regs, r1) }, ++ { "r2",offsetof(struct pt_regs, r2) }, ++ { "r3",offsetof(struct pt_regs, r3) }, ++ ++ { "r8",offsetof(struct pt_regs, r8) }, ++ { "r9",offsetof(struct pt_regs, r9) }, ++ { "r10",offsetof(struct pt_regs, r10) }, ++ ++ { "r11",offsetof(struct pt_regs, r11) }, ++ { "r12",offsetof(struct pt_regs, r12) }, ++ { "r13",offsetof(struct pt_regs, r13) }, ++ ++ { "r14",offsetof(struct pt_regs, r14) }, ++ { "r15",offsetof(struct pt_regs, r15) }, ++ { "r16",offsetof(struct pt_regs, r16) }, ++ ++ { "r17",offsetof(struct pt_regs, r17) }, ++ { "r18",offsetof(struct pt_regs, r18) }, ++ { "r19",offsetof(struct pt_regs, r19) }, ++ ++ { "r20",offsetof(struct pt_regs, r20) }, ++ { "r21",offsetof(struct pt_regs, r21) }, ++ { "r22",offsetof(struct pt_regs, r22) }, ++ ++ { "r23",offsetof(struct pt_regs, r23) }, ++ { "r24",offsetof(struct pt_regs, r24) }, ++ { "r25",offsetof(struct pt_regs, r25) }, ++ ++ { "r26",offsetof(struct pt_regs, r26) }, ++ { "r27",offsetof(struct pt_regs, r27) }, ++ { "r28",offsetof(struct pt_regs, r28) }, ++ ++ { "r29",offsetof(struct pt_regs, r29) }, ++ { "r30",offsetof(struct pt_regs, r30) }, ++ { "r31",offsetof(struct pt_regs, r31) }, ++ ++}; ++ ++static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs); ++ ++int ++kdba_getregcontents(const char *regname, struct pt_regs *regs, unsigned long *contents) ++{ ++ int i; ++ ++ if (strcmp(regname, "isr") == 0) { ++ fault_regs_t fr ; ++ get_fault_regs(&fr) ; ++ *contents = fr.isr ; ++ return 0 ; ++ } ++ ++ if (!regs) { ++ kdb_printf("%s: pt_regs not available, use bt* or pid to select a different task\n", __FUNCTION__); ++ return KDB_BADREG; ++ } ++ ++ if (strcmp(regname, "®s") == 0) { ++ *contents = (unsigned long)regs; ++ return 0; ++ } ++ ++ if (strcmp(regname, "sstk") == 0) { ++ *contents = (unsigned long)getprsregs(regs) ; ++ return 0; ++ } ++ ++ if (strcmp(regname, "ksp") == 0) { ++ *contents = (unsigned long) (regs + 1); ++ return 0; ++ } ++ ++ for (i=0; i ++ * ++ * Parameters: ++ * regname Pointer to string naming register ++ * regs Pointer to structure containing registers. ++ * contents Unsigned long containing new register contents ++ * Outputs: ++ * Returns: ++ * 0 Success ++ * KDB_BADREG Invalid register name ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++int ++kdba_setregcontents(const char *regname, ++ struct pt_regs *regs, ++ unsigned long contents) ++{ ++ int i, ret = 0, fixed = 0; ++ char *endp; ++ unsigned long regno; ++ ++ if (regname[0] == '%') { ++ regname++; ++ regs = (struct pt_regs *) ++ (kdb_current_task->thread.ksp - sizeof(struct pt_regs)); ++ } ++ ++ if (!regs) { ++ kdb_printf("%s: pt_regs not available, use bt* or pid to select a different task\n", __FUNCTION__); ++ return KDB_BADREG; ++ } ++ ++ /* fixed registers */ ++ for (i=0; i (unsigned long)31) { ++ ret = change_cur_stack_frame(regno, &contents); ++ if(!ret) return 0; ++ } ++ } ++ ++ if ((i == nkdbreglist) ++ || (strlen(kdbreglist[i].reg_name) != strlen(regname)) ++ || ret) { ++ return KDB_BADREG; ++ } ++ ++ /* just in case of "standard" register */ ++ *(unsigned long *)((unsigned long)regs + kdbreglist[i].reg_offset) = ++ contents; ++ ++ return 0; ++} ++ ++/* ++ * kdba_dumpregs ++ * ++ * Dump the specified register set to the display. ++ * ++ * Parameters: ++ * regs Pointer to structure containing registers. ++ * type Character string identifying register set to dump ++ * extra string further identifying register (optional) ++ * Outputs: ++ * Returns: ++ * 0 Success ++ * Locking: ++ * None. ++ * Remarks: ++ * This function will dump the general register set if the type ++ * argument is NULL (struct pt_regs). The alternate register ++ * set types supported by this function: ++ * ++ * d Debug registers ++ * c Control registers ++ * u User registers at most recent entry to kernel ++ * i Interrupt registers -- same as "irr" command ++ * Following not yet implemented: ++ * m Model Specific Registers (extra defines register #) ++ * r Memory Type Range Registers (extra defines register) ++ * ++ * For now, all registers are covered as follows: ++ * ++ * rd - dumps all regs ++ * rd %isr - current interrupt status reg, read freshly ++ * rd s - valid stacked regs ++ * rd %sstk - gets switch stack addr. dump memory and search ++ * rd d - debug regs, may not be too useful ++ * rd k - dump kernel regs ++ * ++ * ARs TB Done ++ * OTHERS TB Decided ?? ++ * ++ * Intel wish list ++ * These will be implemented later - Srinivasa ++ * ++ * type action ++ * ---- ------ ++ * g dump all General static registers ++ * s dump all general Stacked registers ++ * f dump all Floating Point registers ++ * p dump all Predicate registers ++ * b dump all Branch registers ++ * a dump all Application registers ++ * c dump all Control registers ++ * ++ */ ++ ++int ++kdba_dumpregs(struct pt_regs *regs, ++ const char *type, ++ const char *extra) ++ ++{ ++ int i; ++ int count = 0; ++ ++ if (type ++ && (type[0] == 'u')) { ++ type = NULL; ++ regs = (struct pt_regs *) ++ (kdb_current_task->thread.ksp - sizeof(struct pt_regs)); ++ } ++ ++ if (type == NULL) { ++ if (!regs) { ++ kdb_printf("%s: pt_regs not available, use bt* or pid to select a different task\n", __FUNCTION__); ++ return KDB_BADREG; ++ } ++ for (i=0; icr_iip + ia64_psr(regs)->ri : 0; ++} ++ ++int ++kdba_setpc(struct pt_regs *regs, kdb_machreg_t newpc) ++{ ++ if (KDB_NULL_REGS(regs)) ++ return KDB_BADREG; ++ regs->cr_iip = newpc & ~0xf; ++ ia64_psr(regs)->ri = newpc & 0x3; ++ KDB_STATE_SET(IP_ADJUSTED); ++ return 0; ++} ++ ++struct kdba_main_loop_data { ++ kdb_reason_t reason; ++ kdb_reason_t reason2; ++ int error; ++ kdb_dbtrap_t db_result; ++ struct pt_regs *regs; ++ int ret; ++}; ++ ++/* ++ * do_kdba_main_loop ++ * ++ * Invoked from kdba_main_loop via unw_init_running() after that routine ++ * has pushed a struct switch_stack. ++ * ++ * Inputs: ++ * info Unwind information. ++ * data kdb data passed as void * to unw_init_running. ++ * Returns: ++ * none (unw_init_running requires void). vdata->ret is set to ++ * 0 KDB was invoked for an event which it wasn't responsible ++ * 1 KDB handled the event for which it was invoked. ++ * Outputs: ++ * none ++ * Locking: ++ * None. ++ * Remarks: ++ * unw_init_running() creates struct switch_stack then struct ++ * unw_frame_info. We get the address of the info so step over ++ * that to get switch_stack. Just hope that unw_init_running ++ * does not change its stack usage. unw_init_running adds padding ++ * to put switch_stack on a 16 byte boundary. ++ */ ++ ++static void ++do_kdba_main_loop(struct unw_frame_info *info, void *vdata) ++{ ++ struct kdba_main_loop_data *data = vdata; ++ struct switch_stack *sw, *prev_sw; ++ struct pt_regs *prev_regs; ++ struct kdb_running_process *krp = ++ kdb_running_process + smp_processor_id(); ++ KDB_DEBUG_STATE(__FUNCTION__, data->reason); ++ prev_sw = krp->arch.sw; ++ sw = (struct switch_stack *)(info+1); ++ /* padding from unw_init_running */ ++ sw = (struct switch_stack *)(((unsigned long)sw + 15) & ~15); ++ krp->arch.sw = sw; ++ prev_regs = krp->regs; ++ data->ret = kdb_save_running(data->regs, data->reason, data->reason2, ++ data->error, data->db_result); ++ kdb_unsave_running(data->regs); ++ krp->regs = prev_regs; ++ krp->arch.sw = prev_sw; ++} ++ ++/* ++ * kdba_main_loop ++ * ++ * Do any architecture specific set up before entering the main kdb loop. ++ * The primary function of this routine is to make all processes look the ++ * same to kdb, kdb must be able to list a process without worrying if the ++ * process is running or blocked, so make all processes look as though they ++ * are blocked. ++ * ++ * Inputs: ++ * reason The reason KDB was invoked ++ * error The hardware-defined error code ++ * error2 kdb's current reason code. Initially error but can change ++ * acording to kdb state. ++ * db_result Result from break or debug point. ++ * regs The exception frame at time of fault/breakpoint. If reason ++ * is SILENT or CPU_UP then regs is NULL, otherwise it should ++ * always be valid. ++ * Returns: ++ * 0 KDB was invoked for an event which it wasn't responsible ++ * 1 KDB handled the event for which it was invoked. ++ * Outputs: ++ * Builds a switch_stack structure before calling the main loop. ++ * Locking: ++ * None. ++ * Remarks: ++ * none. ++ */ ++ ++int ++kdba_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, ++ kdb_dbtrap_t db_result, struct pt_regs *regs) ++{ ++ struct kdba_main_loop_data data; ++ KDB_DEBUG_STATE("kdba_main_loop", reason); ++ data.reason = reason; ++ data.reason2 = reason2; ++ data.error = error; ++ data.db_result = db_result; ++ data.regs = regs; ++ unw_init_running(do_kdba_main_loop, &data); ++ return(data.ret); ++} ++ ++void ++kdba_disableint(kdb_intstate_t *state) ++{ ++ unsigned long *fp = (unsigned long *)state; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ *fp = flags; ++} ++ ++void ++kdba_restoreint(kdb_intstate_t *state) ++{ ++ unsigned long flags = *(unsigned long *)state; ++ local_irq_restore(flags); ++} ++ ++void ++kdba_setsinglestep(struct pt_regs *regs) ++{ ++ if (KDB_NULL_REGS(regs)) ++ return; ++ ia64_psr(regs)->ss = 1; ++} ++ ++void ++kdba_clearsinglestep(struct pt_regs *regs) ++{ ++ if (KDB_NULL_REGS(regs)) ++ return; ++ ia64_psr(regs)->ss = 0; ++} ++ ++/* ++ * kdb_tpa ++ * ++ * Virtual to Physical address translation command. ++ * ++ * tpa ++ * ++ * Parameters: ++ * argc Count of arguments in argv ++ * argv Space delimited command line arguments ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic if failure. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++#define __xtpa(x) ({ia64_va _v; asm("tpa %0=%1" : "=r"(_v.l) : "r"(x)); _v.l;}) ++static int ++kdba_tpa(int argc, const char **argv) ++{ ++ kdb_machreg_t addr; ++ int diag; ++ long offset = 0; ++ int nextarg; ++ char c; ++ ++ nextarg = 1; ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ if (kdb_getarea(c, addr)) ++ return(0); ++ kdb_printf("vaddr: 0x%lx , paddr: 0x%lx\n", addr, __xtpa(addr)); ++ return(0); ++} ++#if defined(CONFIG_NUMA) ++static int ++kdba_tpav(int argc, const char **argv) ++{ ++ kdb_machreg_t addr, end, paddr; ++ int diag; ++ long offset = 0; ++ int nextarg, nid, nid_old; ++ char c; ++ ++ nextarg = 1; ++ if (argc != 2) ++ return KDB_ARGCOUNT; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &end, &offset, NULL); ++ if (diag) ++ return diag; ++ if (kdb_getarea(c, addr)) ++ return(0); ++ if (kdb_getarea(c, end)) ++ return(0); ++ paddr=__xtpa(addr); ++ nid = paddr_to_nid(paddr); ++ kdb_printf("begin: 0x%lx , paddr: 0x%lx , nid: %d\n", addr, __xtpa(addr), nid); ++ for(;addr] ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ */ ++ ++static int ++kdba_sendinit(int argc, const char **argv) ++{ ++ unsigned long cpunum; ++ int diag; ++ ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ ++ diag = kdbgetularg(argv[1], &cpunum); ++ if (diag) ++ return diag; ++ ++ if (cpunum >= NR_CPUS || !cpu_online(cpunum)) ++ return KDB_BADCPUNUM; ++ ++ platform_send_ipi(cpunum, 0, IA64_IPI_DM_INIT, 0); ++ return 0; ++} ++ ++/* Invoked once from kdb_wait_for_cpus when waiting for cpus. For those cpus ++ * that have not responded to the normal KDB interrupt yet, hit them with an ++ * INIT event. ++ */ ++void ++kdba_wait_for_cpus(void) ++{ ++ int c; ++ if (KDB_FLAG(CATASTROPHIC)) ++ return; ++ kdb_printf(" Sending INIT to cpus that have not responded yet\n"); ++ for_each_online_cpu(c) ++ if (kdb_running_process[c].seqno < kdb_seqno - 1) ++ platform_send_ipi(c, 0, IA64_IPI_DM_INIT, 0); ++} ++ ++#endif /* CONFIG_SMP */ ++ ++/* This code is sensitive to the layout of the MCA/INIT stack (see mca_asm.h) ++ * and to the stack layout that ia64_mca_modify_original_stack() creates when ++ * it makes the original task look blocked. ++ */ ++static void ++kdba_handlers_modify(struct task_struct *task, int cpu) ++{ ++ struct kdb_running_process *work, *save; ++ work = kdb_running_process + cpu; ++ save = kdb_running_process_save + cpu; ++ *work = *save; ++ if (!kdba_show_handlers && REGION_NUMBER(task) >= RGN_GATE && ++ (task_thread_info(task)->flags & _TIF_MCA_INIT)) { ++ struct ia64_sal_os_state *sos = (struct ia64_sal_os_state *) ++ ((unsigned long)save->p + MCA_SOS_OFFSET); ++ char *p; ++ if (!sos->prev_task) ++ return; ++ work->p = sos->prev_task; ++ p = (char *)sos->prev_task->thread.ksp; ++ p += 16; ++ work->arch.sw = (struct switch_stack *)p; ++ p += sizeof(struct switch_stack); ++ work->regs = (struct pt_regs *)p; ++ work->irq_depth = 2; /* any value >1 will do */ ++ } ++} ++ ++/* Turn the display of the MCA/INIT handlers on or off, or display the status ++ * of the MCA/INIT handlers. ++ */ ++static int ++kdba_handlers(int argc, const char **argv) ++{ ++ int cpu; ++ struct kdb_running_process *krp; ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ if (strcmp(argv[1], "show") == 0) ++ kdba_show_handlers = 1; ++ else if (strcmp(argv[1], "hide") == 0) ++ kdba_show_handlers = 0; ++ else if (strcmp(argv[1], "status") != 0) { ++ kdb_printf("handlers \n"); ++ return 0; ++ } ++ for (cpu = 0, krp = kdb_running_process_save; cpu < NR_CPUS; ++cpu, ++krp) { ++ if (krp->p) ++ kdba_handlers_modify(krp->p, cpu); ++ } ++ if (strcmp(argv[1], "status") != 0) ++ return 0; ++ kdb_printf("handlers status is %s\n", kdba_show_handlers ? "'show'" : "'hide'"); ++ kdb_printf(" cpu handler task command original task command\n"); ++ for (cpu = 0, krp = kdb_running_process_save; cpu < NR_CPUS; ++cpu, ++krp) { ++ struct task_struct *p = krp->p; ++ if (!p) ++ continue; ++ kdb_printf("%4d", cpu); ++ if (task_thread_info(p)->flags & _TIF_MCA_INIT) { ++ struct ia64_sal_os_state *sos; ++ kdb_printf(" " kdb_machreg_fmt0 " %-*s ", ++ (unsigned long)p, (int)sizeof(p->comm), p->comm); ++ sos = (struct ia64_sal_os_state *)((unsigned long)p + MCA_SOS_OFFSET); ++ p = sos->prev_task; ++ } else ++ kdb_printf("%*s", (int)(1+2+16+1+sizeof(p->comm)+2), " "); ++ if (p) ++ kdb_printf(" " kdb_machreg_fmt0 " %-*s", ++ (unsigned long)p, (int)sizeof(p->comm), p->comm); ++ kdb_printf("\n"); ++ } ++ return 0; ++} ++ ++/* Executed once on each cpu at startup. */ ++void ++kdba_cpu_up(void) ++{ ++} ++ ++/* ++ * kdba_init ++ * ++ * Architecture specific initialization. ++ * ++ * Parameters: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * None. ++ */ ++ ++void ++kdba_init(void) ++{ ++ kdb_running_process_save = kzalloc( ++ sizeof(*kdb_running_process_save) * NR_CPUS, GFP_KERNEL); ++ BUG_ON(!kdb_running_process_save); ++ kdb_register("irr", kdba_sir, "", "Show interrupt registers", 0); ++ kdb_register("itm", kdba_itm, "", "Set new ITM value", 0); ++#if defined(CONFIG_SMP) ++ kdb_register("init", kdba_sendinit, "", "Send INIT to cpu", 0); ++#endif ++ kdb_register("pt_regs", kdba_pt_regs, "address", "Format struct pt_regs", 0); ++ kdb_register("switch_stack", kdba_switch_stack, "address", "Format struct switch_stack", 0); ++ kdb_register("minstate", kdba_minstate, "address", "Format PAL minstate", 0); ++ kdb_register("tpa", kdba_tpa, "", "Translate virtual to physical address", 0); ++#if defined(CONFIG_NUMA) ++ kdb_register("tpav", kdba_tpav, " ", "Verify that physical addresses corresponding to virtual addresses from to are in same node", 0); ++#endif ++ kdb_register("stackdepth", kdba_stackdepth, "[percentage]", "Print processes using >= stack percentage", 0); ++ kdb_register("cpuinfo", kdba_cpuinfo, "[cpu]", "Print struct cpuinfo_ia64", 0); ++ kdb_register("handlers", kdba_handlers, "", "Control the display of MCA/INIT handlers", 0); ++ ++#ifdef CONFIG_SERIAL_8250_CONSOLE ++ kdba_serial_console = KDBA_SC_STANDARD; ++#endif ++#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE ++ if (ia64_platform_is("sn2")) ++ kdba_serial_console = KDBA_SC_SGI_L1; ++#endif ++ return; ++} ++ ++/* ++ * kdba_adjust_ip ++ * ++ * Architecture specific adjustment of instruction pointer before leaving ++ * kdb. ++ * ++ * Parameters: ++ * reason The reason KDB was invoked ++ * error The hardware-defined error code ++ * regs The exception frame at time of fault/breakpoint. If reason ++ * is SILENT or CPU_UP then regs is NULL, otherwise it should ++ * always be valid. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * On IA64, KDB_ENTER() and KDB_ENTER_SLAVE() use break which is a fault, ++ * not a trap. The instruction pointer must be stepped before leaving ++ * kdb, otherwise we get a loop. ++ */ ++ ++void ++kdba_adjust_ip(kdb_reason_t reason, int error, struct pt_regs *regs) ++{ ++ if ((reason == KDB_REASON_ENTER || reason == KDB_REASON_ENTER_SLAVE) && ++ !KDB_STATE(IP_ADJUSTED)) { ++ if (KDB_NULL_REGS(regs)) ++ return; ++ if (ia64_psr(regs)->ri < 2) ++ kdba_setpc(regs, regs->cr_iip + ia64_psr(regs)->ri + 1); ++ else ++ kdba_setpc(regs, regs->cr_iip + 16); ++ } ++} ++ ++void ++kdba_save_running(struct kdba_running_process *k, struct pt_regs *regs) ++{ ++ struct kdb_running_process *work, *save; ++ int cpu = smp_processor_id(); ++ work = kdb_running_process + cpu; ++ save = kdb_running_process_save + cpu; ++ *save = *work; ++ if (!regs) ++ return; ++ kdba_handlers_modify((struct task_struct *)regs->r13, cpu); ++} ++ ++void ++kdba_unsave_running(struct kdba_running_process *k, struct pt_regs *regs) ++{ ++ memset(kdb_running_process_save + smp_processor_id(), 0, ++ sizeof(*kdb_running_process_save)); ++} ++ ++void ++kdba_set_current_task(const struct task_struct *p) ++{ ++ int cpu = kdb_process_cpu(p); ++ struct kdb_running_process *work, *save; ++ work = kdb_running_process + cpu; ++ save = kdb_running_process_save + cpu; ++ kdb_current_task = p; ++ if (kdb_task_has_cpu(p)) { ++ kdb_current_regs = work->regs; ++ return; ++ } ++ kdb_current_regs = NULL; ++ /* For most blocked tasks we cannot get the pt_regs without doing an ++ * unwind, which is not worth doing. For tasks interrupted by ++ * MCA/INIT, when the user is not working on the handlers, we must use ++ * the registers at the time of interrupt. ++ */ ++ if (work->p == save->p || work->p != p) ++ return; ++ kdb_current_regs = (struct pt_regs *)(work->p->thread.ksp + 16 + ++ sizeof(struct switch_stack)); ++} ++ ++/* ++ * asm-ia64 uaccess.h supplies __copy_to_user which relies on MMU to ++ * trap invalid addresses in the _xxx fields. Verify the other address ++ * of the pair is valid by accessing the first and last byte ourselves, ++ * then any access violations should only be caused by the _xxx ++ * addresses, ++ */ ++ ++int ++kdba_putarea_size(unsigned long to_xxx, void *from, size_t size) ++{ ++ mm_segment_t oldfs = get_fs(); ++ int r; ++ char c; ++ c = *((volatile char *)from); ++ c = *((volatile char *)from + size - 1); ++ ++ if (to_xxx >> 61 <= 4) { ++ return kdb_putuserarea_size(to_xxx, from, size); ++ } ++ ++ set_fs(KERNEL_DS); ++ r = __copy_to_user_inatomic((void __user *)to_xxx, from, size); ++ set_fs(oldfs); ++ return r; ++} ++ ++int ++kdba_getarea_size(void *to, unsigned long from_xxx, size_t size) ++{ ++ mm_segment_t oldfs = get_fs(); ++ int r; ++ *((volatile char *)to) = '\0'; ++ *((volatile char *)to + size - 1) = '\0'; ++ ++ if (from_xxx >> 61 <= 4) ++ return kdb_getuserarea_size(to, from_xxx, size); ++ ++ set_fs(KERNEL_DS); ++ switch (size) { ++ case 1: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, 1); ++ break; ++ case 2: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, 2); ++ break; ++ case 4: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, 4); ++ break; ++ case 8: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, 8); ++ break; ++ default: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, size); ++ break; ++ } ++ set_fs(oldfs); ++ return r; ++} ++ ++int ++kdba_verify_rw(unsigned long addr, size_t size) ++{ ++ unsigned char data[(__force size_t) size]; ++ return(kdba_getarea_size(data, addr, size) || kdba_putarea_size(addr, data, size)); ++} ++ ++#ifdef CONFIG_KDB_KDUMP ++void ++kdba_kdump_prepare(struct pt_regs *fixed_regs) ++{ ++ int i; ++ ++ /* Set on KEXEC bit on all onlinr cpus */ ++ for (i = 1; i < NR_CPUS; ++i) { ++ if (!cpu_online(i)) ++ continue; ++ ++ KDB_STATE_SET_CPU(KEXEC, i); ++ } ++ ++ machine_crash_shutdown(fixed_regs); ++} ++ ++void kdba_kdump_shutdown_slave(struct pt_regs *regs) ++{ ++ if (kdb_kdump_state != KDB_KDUMP_RESET) { ++ unw_init_running(kdump_cpu_freeze, NULL); ++ } ++} ++#endif +--- a/arch/ia64/kernel/head.S ++++ b/arch/ia64/kernel/head.S +@@ -259,8 +259,13 @@ start_ap: + /* + * Switch into virtual mode: + */ ++#ifdef CONFIG_KDB_HARDWARE_BREAKPOINTS ++#define IA64_PSR_KDB_FLAGS IA64_PSR_DB ++#else ++#define IA64_PSR_KDB_FLAGS 0 ++#endif + movl r16=(IA64_PSR_IT|IA64_PSR_IC|IA64_PSR_DT|IA64_PSR_RT|IA64_PSR_DFH|IA64_PSR_BN \ +- |IA64_PSR_DI|IA64_PSR_AC) ++ |IA64_PSR_DI|IA64_PSR_AC|IA64_PSR_KDB_FLAGS) + ;; + mov cr.ipsr=r16 + movl r17=1f +--- a/arch/ia64/kernel/mca.c ++++ b/arch/ia64/kernel/mca.c +@@ -88,6 +88,10 @@ + #include + #include + #include ++#ifdef CONFIG_KDB ++#include ++#include /* for switch state wrappers */ ++#endif /* CONFIG_KDB */ + + #include + #include +@@ -824,6 +828,14 @@ ia64_mca_rendez_int_handler(int rendez_i + */ + ia64_sal_mc_rendez(); + ++#ifdef CONFIG_KDB ++ /* We get here when the MCA monarch has entered and has woken up the ++ * slaves. Do a KDB rendezvous to meet the monarch cpu. ++ */ ++ if (monarch_cpu != -1) ++ KDB_ENTER_SLAVE(); ++#endif ++ + NOTIFY_MCA(DIE_MCA_RENDZVOUS_PROCESS, get_irq_regs(), (long)&nd, 1); + + /* Wait for the monarch cpu to exit. */ +@@ -1379,6 +1391,19 @@ ia64_mca_handler(struct pt_regs *regs, s + mca_insert_tr(0x2); /*Reload dynamic itrs*/ + } + ++#ifdef CONFIG_KDB ++ kdb_save_flags(); ++ KDB_FLAG_CLEAR(CATASTROPHIC); ++ KDB_FLAG_CLEAR(RECOVERY); ++ if (recover) ++ KDB_FLAG_SET(RECOVERY); ++ else ++ KDB_FLAG_SET(CATASTROPHIC); ++ KDB_FLAG_SET(NOIPI); /* do not send IPI for MCA/INIT events */ ++ KDB_ENTER(); ++ kdb_restore_flags(); ++#endif /* CONFIG_KDB */ ++ + NOTIFY_MCA(DIE_MCA_MONARCH_LEAVE, regs, (long)&nd, 1); + + if (atomic_dec_return(&mca_count) > 0) { +@@ -1391,6 +1416,12 @@ ia64_mca_handler(struct pt_regs *regs, s + if (cpu_isset(i, mca_cpu)) { + monarch_cpu = i; + cpu_clear(i, mca_cpu); /* wake next cpu */ ++#ifdef CONFIG_KDB ++ /* ++ * No longer a monarch, report in as a slave. ++ */ ++ KDB_ENTER_SLAVE(); ++#endif + while (monarch_cpu != -1) + cpu_relax(); /* spin until last cpu leaves */ + set_curr_task(cpu, previous_current); +@@ -1400,6 +1431,7 @@ ia64_mca_handler(struct pt_regs *regs, s + } + } + } ++ + set_curr_task(cpu, previous_current); + ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE; + monarch_cpu = -1; /* This frees the slaves and previous monarchs */ +@@ -1660,6 +1692,11 @@ default_monarch_init_process(struct noti + } + } + printk("\n\n"); ++#ifdef CONFIG_KDB ++ KDB_FLAG_SET(NOIPI); /* do not send IPI for MCA/INIT events */ ++ KDB_ENTER(); ++ KDB_FLAG_CLEAR(NOIPI); ++#else /* !CONFIG_KDB */ + if (read_trylock(&tasklist_lock)) { + do_each_thread (g, t) { + printk("\nBacktrace of pid %d (%s)\n", t->pid, t->comm); +@@ -1667,6 +1704,7 @@ default_monarch_init_process(struct noti + } while_each_thread (g, t); + read_unlock(&tasklist_lock); + } ++#endif /* CONFIG_KDB */ + /* FIXME: This will not restore zapped printk locks. */ + RESTORE_LOGLEVEL(console_loglevel); + return NOTIFY_DONE; +@@ -1699,6 +1737,20 @@ ia64_init_handler(struct pt_regs *regs, + int cpu = smp_processor_id(); + struct ia64_mca_notify_die nd = + { .sos = sos, .monarch_cpu = &monarch_cpu }; ++#ifdef CONFIG_KDB ++ int kdba_recalcitrant = 0; ++ /* kdba_wait_for_cpus() sends INIT to recalcitrant cpus which ends up ++ * calling this routine. If KDB is waiting for the IPI to be processed ++ * then treat all INIT events as slaves, kdb_initial_cpu is the ++ * monarch. ++ */ ++ if (KDB_STATE(WAIT_IPI)) { ++ monarch_cpu = kdb_initial_cpu; ++ sos->monarch = 0; ++ KDB_STATE_CLEAR(WAIT_IPI); ++ kdba_recalcitrant = 1; ++ } ++#endif /* CONFIG_KDB */ + + NOTIFY_INIT(DIE_INIT_ENTER, regs, (long)&nd, 0); + +@@ -1742,6 +1794,11 @@ ia64_init_handler(struct pt_regs *regs, + #else + while (monarch_cpu == -1) + cpu_relax(); /* spin until monarch enters */ ++#ifdef CONFIG_KDB ++ KDB_ENTER_SLAVE(); ++ if (kdba_recalcitrant) ++ monarch_cpu = -1; ++#endif /* CONFIG_KDB */ + #endif + + NOTIFY_INIT(DIE_INIT_SLAVE_ENTER, regs, (long)&nd, 1); +@@ -1776,6 +1833,14 @@ ia64_init_handler(struct pt_regs *regs, + mprintk("Delaying for 5 seconds...\n"); + udelay(5*1000000); + ia64_wait_for_slaves(cpu, "INIT"); ++ ++#ifdef CONFIG_KDB ++ kdb_save_flags(); ++ KDB_FLAG_SET(NOIPI); /* do not send IPI for MCA/INIT events */ ++ KDB_ENTER(); ++ kdb_restore_flags(); ++#endif /* CONFIG_KDB */ ++ + /* If nobody intercepts DIE_INIT_MONARCH_PROCESS then we drop through + * to default_monarch_init_process() above and just print all the + * tasks. +@@ -2014,6 +2079,13 @@ ia64_mca_init(void) + printk(KERN_INFO "Increasing MCA rendezvous timeout from " + "%ld to %ld milliseconds\n", timeout, isrv.v0); + timeout = isrv.v0; ++#ifdef CONFIG_KDB ++ /* kdb must wait long enough for the MCA timeout to trip ++ * and process. The MCA timeout is in milliseconds. ++ */ ++ kdb_wait_for_cpus_secs = max(kdb_wait_for_cpus_secs, ++ (int)(timeout/1000) + 10); ++#endif /* CONFIG_KDB */ + NOTIFY_MCA(DIE_MCA_NEW_TIMEOUT, NULL, timeout, 0); + continue; + } +--- a/arch/ia64/kernel/smp.c ++++ b/arch/ia64/kernel/smp.c +@@ -36,6 +36,11 @@ + #include + #include + #include ++ ++#ifdef CONFIG_KDB ++#include ++#endif /* CONFIG_KDB */ ++ + #include + #include + #include +@@ -65,6 +70,9 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(uns + #define IPI_CPU_STOP 1 + #define IPI_CALL_FUNC_SINGLE 2 + #define IPI_KDUMP_CPU_STOP 3 ++#ifdef CONFIG_KDB ++#define IPI_KDB_INTERRUPT 4 ++#endif /* CONFIG_KDB */ + + /* This needs to be cacheline aligned because it is written to by *other* CPUs. */ + static DEFINE_PER_CPU_SHARED_ALIGNED(unsigned long, ipi_operation); +@@ -125,6 +133,12 @@ handle_IPI (int irq, void *dev_id) + unw_init_running(kdump_cpu_freeze, NULL); + break; + #endif ++#ifdef CONFIG_KDB ++ case IPI_KDB_INTERRUPT: ++ if (!kdb_ipi(get_irq_regs(), NULL)) ++ printk(KERN_ERR "kdb_ipi() rejected IPI_KDB_INTERRUPT\n"); ++ break; ++#endif + default: + printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", + this_cpu, which); +@@ -334,3 +348,12 @@ setup_profiling_timer (unsigned int mult + { + return -EINVAL; + } ++ ++#if defined(CONFIG_KDB) ++void ++smp_kdb_stop(void) ++{ ++ if (!KDB_FLAG(NOIPI)) ++ send_IPI_allbutself(IPI_KDB_INTERRUPT); ++} ++#endif /* CONFIG_KDB */ +--- a/arch/ia64/kernel/traps.c ++++ b/arch/ia64/kernel/traps.c +@@ -13,6 +13,9 @@ + #include + #include /* For unblank_screen() */ + #include /* for EXPORT_SYMBOL */ ++#ifdef CONFIG_KDB ++#include ++#endif /* CONFIG_KDB */ + #include + #include + #include /* for ssleep() */ +@@ -77,6 +80,10 @@ die (const char *str, struct pt_regs *re + if (!regs) + return 1; + ++#ifdef CONFIG_KDB ++ (void)kdb(KDB_REASON_OOPS, err, regs); ++#endif /* CONFIG_KDB */ ++ + if (panic_on_oops) + panic("Fatal exception"); + +@@ -170,6 +177,17 @@ __kprobes ia64_bad_break (unsigned long + if (break_num < 0x80000) { + sig = SIGILL; code = __ILL_BREAK; + } else { ++#ifdef CONFIG_KDB ++ if (break_num == KDB_BREAK_ENTER && ++ kdb(KDB_REASON_ENTER, break_num, regs)) ++ return; /* kdb handled it */ ++ if (break_num == KDB_BREAK_ENTER_SLAVE && ++ kdb(KDB_REASON_ENTER_SLAVE, break_num, regs)) ++ return; /* kdb handled it */ ++ if (break_num == KDB_BREAK_BREAK && ++ kdb(KDB_REASON_BREAK, break_num, regs)) ++ return; /* kdb handled it */ ++#endif /* CONFIG_KDB */ + if (notify_die(DIE_BREAK, "bad break", regs, break_num, TRAP_BRKPT, SIGTRAP) + == NOTIFY_STOP) + return; +@@ -564,6 +582,10 @@ ia64_fault (unsigned long vector, unsign + if (notify_die(DIE_FAULT, "ia64_fault", ®s, vector, siginfo.si_code, SIGTRAP) + == NOTIFY_STOP) + return; ++#ifdef CONFIG_KDB ++ if (!user_mode(®s) && kdb(KDB_REASON_DEBUG, vector, ®s)) ++ return; /* kdb handled this */ ++#endif /* CONFIG_KDB */ + siginfo.si_signo = SIGTRAP; + siginfo.si_errno = 0; + siginfo.si_addr = (void __user *) ifa; +--- a/arch/ia64/kernel/unwind.c ++++ b/arch/ia64/kernel/unwind.c +@@ -57,14 +57,27 @@ + + #ifdef UNW_DEBUG + static unsigned int unw_debug_level = UNW_DEBUG; +-# define UNW_DEBUG_ON(n) unw_debug_level >= n +- /* Do not code a printk level, not all debug lines end in newline */ +-# define UNW_DPRINT(n, ...) if (UNW_DEBUG_ON(n)) printk(__VA_ARGS__) ++# ifdef CONFIG_KDB ++# include ++# include ++# define UNW_KMALLOC(s, f) debug_kmalloc(s, f) ++# define UNW_KFREE(p) debug_kfree(p) ++# define UNW_DEBUG_ON(n) (unw_debug_level >= n && !KDB_IS_RUNNING()) ++# define UNW_DPRINT(n, ...) if (UNW_DEBUG_ON(n)) kdb_printf(__VA_ARGS__) ++# else /* !CONFIG_KDB */ ++# define UNW_DEBUG_ON(n) unw_debug_level >= n ++ /* Do not code a printk level, not all debug lines end in newline */ ++# define UNW_DPRINT(n, ...) if (UNW_DEBUG_ON(n)) printk(__VA_ARGS__) ++# define UNW_KMALLOC(s, f) kmalloc(s, f) ++# define UNW_KFREE(p) kfree(p) ++# endif /* CONFIG_KDB */ + # undef inline + # define inline + #else /* !UNW_DEBUG */ + # define UNW_DEBUG_ON(n) 0 + # define UNW_DPRINT(n, ...) ++# define UNW_KMALLOC(s, f) kmalloc(s, f) ++# define UNW_KFREE(p) kfree(p) + #endif /* UNW_DEBUG */ + + #if UNW_STATS +@@ -73,10 +86,10 @@ + # define STAT(x...) + #endif + +-#define alloc_reg_state() kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC) +-#define free_reg_state(usr) kfree(usr) +-#define alloc_labeled_state() kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC) +-#define free_labeled_state(usr) kfree(usr) ++#define alloc_reg_state() UNW_KMALLOC(sizeof(struct unw_reg_state), GFP_ATOMIC) ++#define free_reg_state(usr) UNW_KFREE(usr) ++#define alloc_labeled_state() UNW_KMALLOC(sizeof(struct unw_labeled_state), GFP_ATOMIC) ++#define free_labeled_state(usr) UNW_KFREE(usr) + + typedef unsigned long unw_word; + typedef unsigned char unw_hash_index_t; +@@ -2092,7 +2105,7 @@ unw_add_unwind_table (const char *name, + return NULL; + } + +- table = kmalloc(sizeof(*table), GFP_USER); ++ table = UNW_KMALLOC(sizeof(*table), GFP_USER); + if (!table) + return NULL; + +@@ -2165,7 +2178,7 @@ unw_remove_unwind_table (void *handle) + write_unlock(&tmp->lock); + } + +- kfree(table); ++ UNW_KFREE(table); + } + + static int __init +@@ -2199,7 +2212,7 @@ create_gate_table (void) + size += 3*8 + 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset)); + size += 8; /* reserve space for "end of table" marker */ + +- unw.gate_table = kmalloc(size, GFP_KERNEL); ++ unw.gate_table = UNW_KMALLOC(size, GFP_KERNEL); + if (!unw.gate_table) { + unw.gate_table_size = 0; + printk(KERN_ERR "%s: unable to create unwind data for gate page!\n", __func__); diff --git a/patches.suse/kdb-usb-rework b/patches.suse/kdb-usb-rework new file mode 100644 index 0000000..94b70b1 --- /dev/null +++ b/patches.suse/kdb-usb-rework @@ -0,0 +1,398 @@ +From: Jeff Mahoney +Subject: kdb: Cleanup KDB_USB +Patch-mainline: Who knows? + + kdb-common adds a KDB interface for USB keyboards. Unfortunately it + ends up duplicating some core USB functions wholesale. As these functions + change, the KDB implementation lags behind. + + This patch avoids the duplication of functionality and hooks into the + existing functions. + +Signed-off-by: Jeff Mahoney +--- + arch/ia64/kdb/kdba_io.c | 25 ++++ + drivers/usb/host/ehci-q.c | 276 +++++++++------------------------------------- + 2 files changed, 79 insertions(+), 222 deletions(-) + +--- a/arch/ia64/kdb/kdba_io.c ++++ b/arch/ia64/kdb/kdba_io.c +@@ -70,7 +70,11 @@ static unsigned char kdb_usb_keycode[256 + * Attach a USB keyboard to kdb. + */ + int +-kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, void *poll_func) ++kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, ++ void *poll_func, void *compl_func, ++ kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach, ++ kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach, ++ unsigned int bufsize, struct urb *hid_urb) + { + int i; + int rc = -1; +@@ -93,6 +97,17 @@ kdb_usb_keyboard_attach(struct urb *urb, + kdb_usb_kbds[i].buffer = buffer; + kdb_usb_kbds[i].poll_func = poll_func; + ++ kdb_usb_kbds[i].kdb_hc_urb_complete = compl_func; ++ kdb_usb_kbds[i].kdb_hc_keyboard_attach = kdb_hc_keyboard_attach; ++ kdb_usb_kbds[i].kdb_hc_keyboard_detach = kdb_hc_keyboard_detach; ++ ++ /* USB Host Controller specific Keyboadr attach callback. ++ * Currently only UHCI has this callback. ++ */ ++ if (kdb_usb_kbds[i].kdb_hc_keyboard_attach) ++ kdb_usb_kbds[i].kdb_hc_keyboard_attach(i, bufsize); ++ ++ + rc = 0; /* success */ + + break; +@@ -125,11 +140,19 @@ kdb_usb_keyboard_detach(struct urb *urb) + if (kdb_usb_kbds[i].urb != urb) + continue; + ++ /* USB Host Controller specific Keyboard detach callback. ++ * Currently only UHCI has this callback. ++ */ ++ if (kdb_usb_kbds[i].kdb_hc_keyboard_detach) ++ kdb_usb_kbds[i].kdb_hc_keyboard_detach(urb, i); ++ ++ + /* found it, clear the index */ + kdb_usb_kbds[i].urb = NULL; + kdb_usb_kbds[i].buffer = NULL; + kdb_usb_kbds[i].poll_func = NULL; + kdb_usb_kbds[i].caps_lock = 0; ++ kdb_usb_kbds[i].hid_urb = NULL; + + rc = 0; /* success */ + +--- a/drivers/usb/host/ehci-q.c ++++ b/drivers/usb/host/ehci-q.c +@@ -296,6 +296,28 @@ __acquires(ehci->lock) + spin_lock (&ehci->lock); + } + ++/* ++ * Lock hackery here... ++ * ehci_urb_done() makes the assumption that it's called with ehci->lock held. ++ * So, lock it if it isn't already. ++ */ ++static void ++kdb_ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status) ++__acquires(ehci->lock) ++__releases(ehci->lock) ++{ ++#ifdef CONFIG_KDB_USB ++ int locked; ++ if (!spin_is_locked(&ehci->lock)) { ++ spin_lock(&ehci->lock); ++ locked = 1; ++ } ++ ehci_urb_done(ehci, urb, status); ++ if (locked) ++ spin_unlock(&ehci->lock); ++#endif ++} ++ + static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); + static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); + +@@ -305,9 +327,16 @@ static int qh_schedule (struct ehci_hcd + * Process and free completed qtds for a qh, returning URBs to drivers. + * Chases up to qh->hw_current. Returns number of completions called, + * indicating how much "real" work we did. ++ * ++ * The KDB part is ugly but KDB wants its own copy and it keeps getting ++ * out of sync. The difference with kdb=1 is that we will only process ++ * qtds that are associated with kdburb. ehci_urb_done also releases ++ * and retakes ehci->lock. We may not have that lock while KDB is ++ * running. + */ + static unsigned +-qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) ++__qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, int kdb, ++ struct urb *kdburb) + { + struct ehci_qtd *last, *end = qh->dummy; + struct list_head *entry, *tmp; +@@ -318,6 +347,9 @@ qh_completions (struct ehci_hcd *ehci, s + const __le32 halt = HALT_BIT(ehci); + struct ehci_qh_hw *hw = qh->hw; + ++ if (kdb && !kdburb) ++ return 0; ++ + if (unlikely (list_empty (&qh->qtd_list))) + return count; + +@@ -353,10 +385,18 @@ qh_completions (struct ehci_hcd *ehci, s + qtd = list_entry (entry, struct ehci_qtd, qtd_list); + urb = qtd->urb; + ++ if (kdburb && urb != kdburb) ++ continue; ++ + /* clean up any state from previous QTD ...*/ + if (last) { + if (likely (last->urb != urb)) { +- ehci_urb_done(ehci, last->urb, last_status); ++ if (kdb) ++ kdb_ehci_urb_done(ehci, last->urb, ++ last_status); ++ else ++ ehci_urb_done(ehci, last->urb, ++ last_status); + count++; + last_status = -EINPROGRESS; + } +@@ -522,7 +562,10 @@ halt: + + /* last urb's completion might still need calling */ + if (likely (last != NULL)) { +- ehci_urb_done(ehci, last->urb, last_status); ++ if (kdb) ++ kdb_ehci_urb_done(ehci, last->urb, last_status); ++ else ++ ehci_urb_done(ehci, last->urb, last_status); + count++; + ehci_qtd_free (ehci, last); + } +@@ -577,227 +620,18 @@ halt: + return count; + } + +-#ifdef CONFIG_KDB_USB +-/* +- * This routine is basically a copy of qh_completions() for use by KDB. +- * It is modified to only work on qtds which are associated +- * with 'kdburb'. Also, there are some fixups related to locking. +- */ +-unsigned +-qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh *qh, struct urb *kdburb) ++static unsigned ++qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) + { +- struct ehci_qtd *last = NULL, *end = qh->dummy; +- struct list_head *entry, *tmp; +- int last_status = -EINPROGRESS; +- int stopped; +- unsigned count = 0; +- int do_status = 0; +- u8 state; +- u32 halt = HALT_BIT(ehci); +- +- /* verify params are valid */ +- if (!qh || !kdburb) +- return 0; +- +- if (unlikely (list_empty (&qh->qtd_list))) +- return count; +- +- /* completions (or tasks on other cpus) must never clobber HALT +- * till we've gone through and cleaned everything up, even when +- * they add urbs to this qh's queue or mark them for unlinking. +- * +- * NOTE: unlinking expects to be done in queue order. +- */ +- state = qh->qh_state; +- qh->qh_state = QH_STATE_COMPLETING; +- stopped = (state == QH_STATE_IDLE); +- +- /* remove de-activated QTDs from front of queue. +- * after faults (including short reads), cleanup this urb +- * then let the queue advance. +- * if queue is stopped, handles unlinks. +- */ +- list_for_each_safe (entry, tmp, &qh->qtd_list) { +- struct ehci_qtd *qtd; +- struct urb *urb; +- u32 token = 0; +- int qtd_status; +- +- qtd = list_entry (entry, struct ehci_qtd, qtd_list); +- urb = qtd->urb; +- +- if (urb != kdburb) +- continue; +- +- /* clean up any state from previous QTD ...*/ +- if (last) { +- if (likely (last->urb != urb)) { +- /* +- * Lock hackery here... +- * ehci_urb_done() makes the assumption +- * that it's called with ehci->lock held. +- * So, lock it if it isn't already. +- */ +- if (!spin_is_locked(&ehci->lock)) +- spin_lock(&ehci->lock); +- +- ehci_urb_done(ehci, last->urb, last_status); +- +- /* +- * ehci_urb_done() releases and reacquires +- * ehci->lock, so release it here. +- */ +- if (spin_is_locked(&ehci->lock)) +- spin_unlock (&ehci->lock); +- +- count++; +- } +- ehci_qtd_free (ehci, last); +- last = NULL; +- last_status = -EINPROGRESS; +- } +- +- /* ignore urbs submitted during completions we reported */ +- if (qtd == end) +- break; +- +- /* hardware copies qtd out of qh overlay */ +- rmb (); +- token = hc32_to_cpu(ehci, qtd->hw_token); +- +- /* always clean up qtds the hc de-activated */ +- if ((token & QTD_STS_ACTIVE) == 0) { +- +- if ((token & QTD_STS_HALT) != 0) { +- stopped = 1; +- +- /* magic dummy for some short reads; qh won't advance. +- * that silicon quirk can kick in with this dummy too. +- */ +- } else if (IS_SHORT_READ (token) +- && !(qtd->hw_alt_next +- & EHCI_LIST_END(ehci))) { +- stopped = 1; +- goto halt; +- } +- +- /* stop scanning when we reach qtds the hc is using */ +- } else if (likely (!stopped +- && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) { +- break; +- +- } else { +- stopped = 1; +- +- if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) +- last_status = -ESHUTDOWN; +- +- /* ignore active urbs unless some previous qtd +- * for the urb faulted (including short read) or +- * its urb was canceled. we may patch qh or qtds. +- */ +- if (likely(last_status == -EINPROGRESS && +- !urb->unlinked)) +- continue; +- +- /* issue status after short control reads */ +- if (unlikely (do_status != 0) +- && QTD_PID (token) == 0 /* OUT */) { +- do_status = 0; +- continue; +- } +- +- /* token in overlay may be most current */ +- if (state == QH_STATE_IDLE +- && cpu_to_hc32(ehci, qtd->qtd_dma) +- == qh->hw_current) +- token = hc32_to_cpu(ehci, qh->hw_token); +- +- /* force halt for unlinked or blocked qh, so we'll +- * patch the qh later and so that completions can't +- * activate it while we "know" it's stopped. +- */ +- if ((halt & qh->hw_token) == 0) { +-halt: +- qh->hw_token |= halt; +- wmb (); +- } +- } +- +- /* remove it from the queue */ +- qtd_status = qtd_copy_status(ehci, urb, qtd->length, token); +- if (unlikely(qtd_status == -EREMOTEIO)) { +- do_status = (!urb->unlinked && +- usb_pipecontrol(urb->pipe)); +- qtd_status = 0; +- } +- if (likely(last_status == -EINPROGRESS)) +- last_status = qtd_status; +- +- if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { +- last = list_entry (qtd->qtd_list.prev, +- struct ehci_qtd, qtd_list); +- last->hw_next = qtd->hw_next; +- } +- list_del (&qtd->qtd_list); +- last = qtd; +- } +- +- /* last urb's completion might still need calling */ +- if (likely (last != NULL)) { +- /* +- * Lock hackery here... +- * ehci_urb_done() makes the assumption +- * that it's called with ehci->lock held. +- * So, lock it if it isn't already. +- */ +- if (!spin_is_locked(&ehci->lock)) +- spin_lock(&ehci->lock); +- +- ehci_urb_done(ehci, last->urb, last_status); +- +- /* +- * ehci_urb_done() releases and reacquires +- * ehci->lock, so release it here. +- */ +- if (spin_is_locked(&ehci->lock)) +- spin_unlock (&ehci->lock); +- +- count++; +- ehci_qtd_free (ehci, last); +- } +- +- /* restore original state; caller must unlink or relink */ +- qh->qh_state = state; +- +- /* be sure the hardware's done with the qh before refreshing +- * it after fault cleanup, or recovering from silicon wrongly +- * overlaying the dummy qtd (which reduces DMA chatter). +- */ +- if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) { +- switch (state) { +- case QH_STATE_IDLE: +- qh_refresh(ehci, qh); +- break; +- case QH_STATE_LINKED: +- /* should be rare for periodic transfers, +- * except maybe high bandwidth ... +- */ +- if ((cpu_to_hc32(ehci, QH_SMASK) +- & qh->hw_info2) != 0) { +- intr_deschedule (ehci, qh); +- (void) qh_schedule (ehci, qh); +- } else +- unlink_async (ehci, qh); +- break; +- /* otherwise, unlink already started */ +- } +- } +- +- return count; ++ return __qh_completions(ehci, qh, 0, NULL); + } + +-#endif /* CONFIG_KDB_USB */ ++unsigned ++qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh *qh, ++ struct urb *kdburb) ++{ ++ return __qh_completions(ehci, qh, 1, kdburb); ++} + + /*-------------------------------------------------------------------------*/ + diff --git a/patches.suse/kdb-vm-api-changes-for-2-6-34 b/patches.suse/kdb-vm-api-changes-for-2-6-34 new file mode 100644 index 0000000..b59489f --- /dev/null +++ b/patches.suse/kdb-vm-api-changes-for-2-6-34 @@ -0,0 +1,46 @@ +From: Jeff Mahoney +Subject: kdb: VM API changes for 2.6.34 +Patch-mainline: Whenever KBB is + + vm_area_struct->anon_vma_node was renamed to anon_vma_chain + + Bootmem is now optional + +Signed-off-by: Jeff Mahoney +Acked-by: Jeff Mahoney +--- + kdb/modules/kdbm_vm.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/kdb/modules/kdbm_vm.c ++++ b/kdb/modules/kdbm_vm.c +@@ -90,8 +90,8 @@ kdbm_print_vm(struct vm_area_struct *vp, + kdb_printf("shared.vm_set.list.prev = 0x%p\n", (void *) vp->shared.vm_set.list.prev); + kdb_printf("shared.vm_set.parent = 0x%p\n", (void *) vp->shared.vm_set.parent); + kdb_printf("shared.vm_set.head = 0x%p\n", (void *) vp->shared.vm_set.head); +- kdb_printf("anon_vma_node.next = 0x%p\n", (void *) vp->anon_vma_node.next); +- kdb_printf("anon_vma_node.prev = 0x%p\n", (void *) vp->anon_vma_node.prev); ++ kdb_printf("anon_vma_chain.next = 0x%p\n", (void *) vp->anon_vma_chain.next); ++ kdb_printf("anon_vma_chain.prev = 0x%p\n", (void *) vp->anon_vma_chain.prev); + kdb_printf("vm_ops = 0x%p\n", (void *) vp->vm_ops); + if (vp->vm_ops != NULL) { + kdb_printf("vm_ops->open = 0x%p\n", vp->vm_ops->open); +@@ -303,7 +303,9 @@ kdbm_pgdat(int argc, const char **argv) + #ifdef CONFIG_FLAT_NODE_MEM_MAP + kdb_printf(" node_mem_map = 0x%p\n", pgdatp->node_mem_map); + #endif ++#ifndef CONFIG_NO_BOOTMEM + kdb_printf(" bdata = 0x%p", pgdatp->bdata); ++#endif + kdb_printf(" node_start_pfn = 0x%lx\n", pgdatp->node_start_pfn); + kdb_printf(" node_present_pages = %ld (0x%lx)\n", + pgdatp->node_present_pages, pgdatp->node_present_pages); +@@ -752,7 +754,7 @@ kdbm_filp(int argc, const char **argv) + f.f_dentry, f.f_vfsmnt, f.f_op); + + kdb_printf(" f_count = %ld f_flags = 0x%x f_mode = 0x%x\n", +- f.f_count, f.f_flags, f.f_mode); ++ atomic_long_read(&f.f_count), f.f_flags, f.f_mode); + + kdb_printf(" f_pos = %Ld\n", f.f_pos); + #ifdef CONFIG_SECURITY diff --git a/patches.suse/kdb-x86 b/patches.suse/kdb-x86 new file mode 100644 index 0000000..72fc62e --- /dev/null +++ b/patches.suse/kdb-x86 @@ -0,0 +1,27127 @@ +From: Martin Hicks +Date: Mon, 07 Dec 2009 11:52:50 -0600 +Subject: kdb-v4.4-2.6.32-x86-3 +References: FATE#303971 +X-URL: ftp://oss.sgi.com/www/projects/kdb/download/v4.4/ +Patch-mainline: Not yet + +The KDB x86 code. + +Acked-by: Jeff Mahoney +--- + + arch/x86/Kconfig.debug | 87 + arch/x86/Makefile | 3 + arch/x86/include/asm/ansidecl.h | 5 + arch/x86/include/asm/ansidecl_32.h | 383 ++ + arch/x86/include/asm/ansidecl_64.h | 383 ++ + arch/x86/include/asm/bfd.h | 5 + arch/x86/include/asm/bfd_32.h | 4921 +++++++++++++++++++++++++++++++ + arch/x86/include/asm/bfd_64.h | 4917 +++++++++++++++++++++++++++++++ + arch/x86/include/asm/irq_vectors.h | 7 + arch/x86/include/asm/kdb.h | 140 + arch/x86/include/asm/kdbprivate.h | 241 + + arch/x86/include/asm/kdebug.h | 2 + arch/x86/include/asm/ptrace.h | 23 + arch/x86/kdb/ChangeLog | 262 + + arch/x86/kdb/ChangeLog_32 | 865 +++++ + arch/x86/kdb/ChangeLog_64 | 447 ++ + arch/x86/kdb/Makefile | 29 + arch/x86/kdb/kdb_cmds_32 | 17 + arch/x86/kdb/kdb_cmds_64 | 18 + arch/x86/kdb/kdba_bp.c | 914 +++++ + arch/x86/kdb/kdba_bt.c | 5757 +++++++++++++++++++++++++++++++++++++ + arch/x86/kdb/kdba_id.c | 261 + + arch/x86/kdb/kdba_io.c | 666 ++++ + arch/x86/kdb/kdba_support.c | 1536 +++++++++ + arch/x86/kdb/pc_keyb.h | 137 + arch/x86/kdb/x86-dis.c | 4688 ++++++++++++++++++++++++++++++ + arch/x86/kernel/apic/io_apic.c | 8 + arch/x86/kernel/dumpstack.c | 12 + arch/x86/kernel/entry_32.S | 20 + arch/x86/kernel/entry_64.S | 27 + arch/x86/kernel/reboot.c | 35 + arch/x86/kernel/traps.c | 27 + 32 files changed, 26843 insertions(+) + +--- a/arch/x86/Kconfig.debug ++++ b/arch/x86/Kconfig.debug +@@ -310,4 +310,91 @@ config DEBUG_STRICT_USER_COPY_CHECKS + + If unsure, or if you run an older (pre 4.4) gcc, say N. + ++config KDB ++ bool "Built-in Kernel Debugger support" ++ depends on DEBUG_KERNEL ++ select KALLSYMS ++ select KALLSYMS_ALL ++ help ++ This option provides a built-in kernel debugger. The built-in ++ kernel debugger contains commands which allow memory to be examined, ++ instructions to be disassembled and breakpoints to be set. For details, ++ see Documentation/kdb/kdb.mm and the manual pages kdb_bt, kdb_ss, etc. ++ Kdb can also be used via the serial port. Set up the system to ++ have a serial console (see Documentation/serial-console.txt). ++ The key sequence KDB on the serial port will cause the ++ kernel debugger to be entered with input from the serial port and ++ output to the serial console. If unsure, say N. ++ ++config KDB_MODULES ++ tristate "KDB modules" ++ depends on KDB ++ help ++ KDB can be extended by adding your own modules, in directory ++ kdb/modules. This option selects the way that these modules should ++ be compiled, as free standing modules (select M) or built into the ++ kernel (select Y). If unsure say M. ++ ++config KDB_OFF ++ bool "KDB off by default" ++ depends on KDB ++ help ++ Normally kdb is activated by default, as long as CONFIG_KDB is set. ++ If you want to ship a kernel with kdb support but only have kdb ++ turned on when the user requests it then select this option. When ++ compiled with CONFIG_KDB_OFF, kdb ignores all events unless you boot ++ with kdb=on or you echo "1" > /proc/sys/kernel/kdb. This option also ++ works in reverse, if kdb is normally activated, you can boot with ++ kdb=off or echo "0" > /proc/sys/kernel/kdb to deactivate kdb. If ++ unsure, say N. ++ ++config KDB_CONTINUE_CATASTROPHIC ++ int "KDB continues after catastrophic errors" ++ depends on KDB ++ default "0" ++ help ++ This integer controls the behaviour of kdb when the kernel gets a ++ catastrophic error, i.e. for a panic, oops, NMI or other watchdog ++ tripping. CONFIG_KDB_CONTINUE_CATASTROPHIC interacts with ++ /proc/sys/kernel/kdb and CONFIG_LKCD_DUMP (if your kernel has the ++ LKCD patch). ++ When KDB is active (/proc/sys/kernel/kdb == 1) and a catastrophic ++ error occurs, nothing extra happens until you type 'go'. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 0 (default). The first time ++ you type 'go', kdb warns you. The second time you type 'go', KDB ++ tries to continue - no guarantees that the kernel is still usable. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 1. KDB tries to continue - no ++ guarantees that the kernel is still usable. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 2. If your kernel has the LKCD ++ patch and LKCD is configured to take a dump then KDB forces a dump. ++ Whether or not a dump is taken, KDB forces a reboot. ++ When KDB is not active (/proc/sys/kernel/kdb == 0) and a catastrophic ++ error occurs, the following steps are automatic, no human ++ intervention is required. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 0 (default) or 1. KDB attempts ++ to continue - no guarantees that the kernel is still usable. ++ CONFIG_KDB_CONTINUE_CATASTROPHIC == 2. If your kernel has the LKCD ++ patch and LKCD is configured to take a dump then KDB automatically ++ forces a dump. Whether or not a dump is taken, KDB forces a ++ reboot. ++ If you are not sure, say 0. Read Documentation/kdb/dump.txt before ++ setting to 2. ++ ++config KDB_USB ++ bool "Support for USB Keyboard in KDB" ++ depends on KDB && (USB_OHCI_HCD || USB_EHCI_HCD || USB_UHCI_HCD) ++ help ++ If you want to use kdb from USB keyboards then say Y here. If you ++ say N then kdb can only be used from a PC (AT) keyboard or a serial ++ console. ++ ++config KDB_KDUMP ++ bool "Support for Kdump in KDB" ++ depends on KDB ++ select KEXEC ++ default N ++ help ++ If you want to take Kdump kernel vmcore from KDB then say Y here. ++ If unsure, say N. ++ + endmenu +--- a/arch/x86/Makefile ++++ b/arch/x86/Makefile +@@ -137,6 +137,9 @@ drivers-$(CONFIG_PM) += arch/x86/power/ + + drivers-$(CONFIG_FB) += arch/x86/video/ + ++# KDB support ++drivers-$(CONFIG_KDB) += arch/x86/kdb/ ++ + #### + # boot loader support. Several targets are kept for legacy purposes + +--- /dev/null ++++ b/arch/x86/include/asm/ansidecl.h +@@ -0,0 +1,5 @@ ++#ifdef CONFIG_X86_32 ++# include "ansidecl_32.h" ++#else ++# include "ansidecl_64.h" ++#endif +--- /dev/null ++++ b/arch/x86/include/asm/ansidecl_32.h +@@ -0,0 +1,383 @@ ++/* ANSI and traditional C compatability macros ++ Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 ++ Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, write to the Free Software ++Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++/* ANSI and traditional C compatibility macros ++ ++ ANSI C is assumed if __STDC__ is #defined. ++ ++ Macro ANSI C definition Traditional C definition ++ ----- ---- - ---------- ----------- - ---------- ++ ANSI_PROTOTYPES 1 not defined ++ PTR `void *' `char *' ++ PTRCONST `void *const' `char *' ++ LONG_DOUBLE `long double' `double' ++ const not defined `' ++ volatile not defined `' ++ signed not defined `' ++ VA_START(ap, var) va_start(ap, var) va_start(ap) ++ ++ Note that it is safe to write "void foo();" indicating a function ++ with no return value, in all K+R compilers we have been able to test. ++ ++ For declaring functions with prototypes, we also provide these: ++ ++ PARAMS ((prototype)) ++ -- for functions which take a fixed number of arguments. Use this ++ when declaring the function. When defining the function, write a ++ K+R style argument list. For example: ++ ++ char *strcpy PARAMS ((char *dest, char *source)); ++ ... ++ char * ++ strcpy (dest, source) ++ char *dest; ++ char *source; ++ { ... } ++ ++ ++ VPARAMS ((prototype, ...)) ++ -- for functions which take a variable number of arguments. Use ++ PARAMS to declare the function, VPARAMS to define it. For example: ++ ++ int printf PARAMS ((const char *format, ...)); ++ ... ++ int ++ printf VPARAMS ((const char *format, ...)) ++ { ++ ... ++ } ++ ++ For writing functions which take variable numbers of arguments, we ++ also provide the VA_OPEN, VA_CLOSE, and VA_FIXEDARG macros. These ++ hide the differences between K+R and C89 more ++ thoroughly than the simple VA_START() macro mentioned above. ++ ++ VA_OPEN and VA_CLOSE are used *instead of* va_start and va_end. ++ Immediately after VA_OPEN, put a sequence of VA_FIXEDARG calls ++ corresponding to the list of fixed arguments. Then use va_arg ++ normally to get the variable arguments, or pass your va_list object ++ around. You do not declare the va_list yourself; VA_OPEN does it ++ for you. ++ ++ Here is a complete example: ++ ++ int ++ printf VPARAMS ((const char *format, ...)) ++ { ++ int result; ++ ++ VA_OPEN (ap, format); ++ VA_FIXEDARG (ap, const char *, format); ++ ++ result = vfprintf (stdout, format, ap); ++ VA_CLOSE (ap); ++ ++ return result; ++ } ++ ++ ++ You can declare variables either before or after the VA_OPEN, ++ VA_FIXEDARG sequence. Also, VA_OPEN and VA_CLOSE are the beginning ++ and end of a block. They must appear at the same nesting level, ++ and any variables declared after VA_OPEN go out of scope at ++ VA_CLOSE. Unfortunately, with a K+R compiler, that includes the ++ argument list. You can have multiple instances of VA_OPEN/VA_CLOSE ++ pairs in a single function in case you need to traverse the ++ argument list more than once. ++ ++ For ease of writing code which uses GCC extensions but needs to be ++ portable to other compilers, we provide the GCC_VERSION macro that ++ simplifies testing __GNUC__ and __GNUC_MINOR__ together, and various ++ wrappers around __attribute__. Also, __extension__ will be #defined ++ to nothing if it doesn't work. See below. ++ ++ This header also defines a lot of obsolete macros: ++ CONST, VOLATILE, SIGNED, PROTO, EXFUN, DEFUN, DEFUN_VOID, ++ AND, DOTS, NOARGS. Don't use them. */ ++ ++#ifndef _ANSIDECL_H ++#define _ANSIDECL_H 1 ++ ++/* Every source file includes this file, ++ so they will all get the switch for lint. */ ++/* LINTLIBRARY */ ++ ++/* Using MACRO(x,y) in cpp #if conditionals does not work with some ++ older preprocessors. Thus we can't define something like this: ++ ++#define HAVE_GCC_VERSION(MAJOR, MINOR) \ ++ (__GNUC__ > (MAJOR) || (__GNUC__ == (MAJOR) && __GNUC_MINOR__ >= (MINOR))) ++ ++and then test "#if HAVE_GCC_VERSION(2,7)". ++ ++So instead we use the macro below and test it against specific values. */ ++ ++/* This macro simplifies testing whether we are using gcc, and if it ++ is of a particular minimum version. (Both major & minor numbers are ++ significant.) This macro will evaluate to 0 if we are not using ++ gcc at all. */ ++#ifndef GCC_VERSION ++#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) ++#endif /* GCC_VERSION */ ++ ++#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(_WIN32) || (defined(__alpha) && defined(__cplusplus)) ++/* All known AIX compilers implement these things (but don't always ++ define __STDC__). The RISC/OS MIPS compiler defines these things ++ in SVR4 mode, but does not define __STDC__. */ ++/* eraxxon@alumni.rice.edu: The Compaq C++ compiler, unlike many other ++ C++ compilers, does not define __STDC__, though it acts as if this ++ was so. (Verified versions: 5.7, 6.2, 6.3, 6.5) */ ++ ++#define ANSI_PROTOTYPES 1 ++#define PTR void * ++#define PTRCONST void *const ++#define LONG_DOUBLE long double ++ ++/* PARAMS is often defined elsewhere (e.g. by libintl.h), so wrap it in ++ a #ifndef. */ ++//#ifndef PARAMS ++//#define PARAMS(ARGS) ARGS ++//#endif ++ ++#define VPARAMS(ARGS) ARGS ++#define VA_START(VA_LIST, VAR) va_start(VA_LIST, VAR) ++ ++/* variadic function helper macros */ ++/* "struct Qdmy" swallows the semicolon after VA_OPEN/VA_FIXEDARG's ++ use without inhibiting further decls and without declaring an ++ actual variable. */ ++#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP, VAR); { struct Qdmy ++#define VA_CLOSE(AP) } va_end(AP); } ++#define VA_FIXEDARG(AP, T, N) struct Qdmy ++ ++#undef const ++#undef volatile ++#undef signed ++ ++#ifdef __KERNEL__ ++#ifndef __STDC_VERSION__ ++#define __STDC_VERSION__ 0 ++#endif ++#endif /* __KERNEL__ */ ++ ++/* inline requires special treatment; it's in C99, and GCC >=2.7 supports ++ it too, but it's not in C89. */ ++#undef inline ++#if __STDC_VERSION__ > 199901L ++/* it's a keyword */ ++#else ++# if GCC_VERSION >= 2007 ++# define inline __inline__ /* __inline__ prevents -pedantic warnings */ ++# else ++# define inline /* nothing */ ++# endif ++#endif ++ ++/* These are obsolete. Do not use. */ ++#ifndef IN_GCC ++#define CONST const ++#define VOLATILE volatile ++#define SIGNED signed ++ ++#define PROTO(type, name, arglist) type name arglist ++#define EXFUN(name, proto) name proto ++#define DEFUN(name, arglist, args) name(args) ++#define DEFUN_VOID(name) name(void) ++#define AND , ++#define DOTS , ... ++#define NOARGS void ++#endif /* ! IN_GCC */ ++ ++#else /* Not ANSI C. */ ++ ++#undef ANSI_PROTOTYPES ++#define PTR char * ++#define PTRCONST PTR ++#define LONG_DOUBLE double ++ ++//#define PARAMS(args) () ++#define VPARAMS(args) (va_alist) va_dcl ++#define VA_START(va_list, var) va_start(va_list) ++ ++#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP); { struct Qdmy ++#define VA_CLOSE(AP) } va_end(AP); } ++#define VA_FIXEDARG(AP, TYPE, NAME) TYPE NAME = va_arg(AP, TYPE) ++ ++/* some systems define these in header files for non-ansi mode */ ++#undef const ++#undef volatile ++#undef signed ++#undef inline ++#define const ++#define volatile ++#define signed ++#define inline ++ ++#ifndef IN_GCC ++#define CONST ++#define VOLATILE ++#define SIGNED ++ ++#define PROTO(type, name, arglist) type name () ++#define EXFUN(name, proto) name() ++#define DEFUN(name, arglist, args) name arglist args; ++#define DEFUN_VOID(name) name() ++#define AND ; ++#define DOTS ++#define NOARGS ++#endif /* ! IN_GCC */ ++ ++#endif /* ANSI C. */ ++ ++/* Define macros for some gcc attributes. This permits us to use the ++ macros freely, and know that they will come into play for the ++ version of gcc in which they are supported. */ ++ ++#if (GCC_VERSION < 2007) ++# define __attribute__(x) ++#endif ++ ++/* Attribute __malloc__ on functions was valid as of gcc 2.96. */ ++#ifndef ATTRIBUTE_MALLOC ++# if (GCC_VERSION >= 2096) ++# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) ++# else ++# define ATTRIBUTE_MALLOC ++# endif /* GNUC >= 2.96 */ ++#endif /* ATTRIBUTE_MALLOC */ ++ ++/* Attributes on labels were valid as of gcc 2.93. */ ++#ifndef ATTRIBUTE_UNUSED_LABEL ++# if (!defined (__cplusplus) && GCC_VERSION >= 2093) ++# define ATTRIBUTE_UNUSED_LABEL ATTRIBUTE_UNUSED ++# else ++# define ATTRIBUTE_UNUSED_LABEL ++# endif /* !__cplusplus && GNUC >= 2.93 */ ++#endif /* ATTRIBUTE_UNUSED_LABEL */ ++ ++#ifndef ATTRIBUTE_UNUSED ++#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) ++#endif /* ATTRIBUTE_UNUSED */ ++ ++/* Before GCC 3.4, the C++ frontend couldn't parse attributes placed after the ++ identifier name. */ ++#if ! defined(__cplusplus) || (GCC_VERSION >= 3004) ++# define ARG_UNUSED(NAME) NAME ATTRIBUTE_UNUSED ++#else /* !__cplusplus || GNUC >= 3.4 */ ++# define ARG_UNUSED(NAME) NAME ++#endif /* !__cplusplus || GNUC >= 3.4 */ ++ ++#ifndef ATTRIBUTE_NORETURN ++#define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) ++#endif /* ATTRIBUTE_NORETURN */ ++ ++/* Attribute `nonnull' was valid as of gcc 3.3. */ ++#ifndef ATTRIBUTE_NONNULL ++# if (GCC_VERSION >= 3003) ++# define ATTRIBUTE_NONNULL(m) __attribute__ ((__nonnull__ (m))) ++# else ++# define ATTRIBUTE_NONNULL(m) ++# endif /* GNUC >= 3.3 */ ++#endif /* ATTRIBUTE_NONNULL */ ++ ++/* Attribute `pure' was valid as of gcc 3.0. */ ++#ifndef ATTRIBUTE_PURE ++# if (GCC_VERSION >= 3000) ++# define ATTRIBUTE_PURE __attribute__ ((__pure__)) ++# else ++# define ATTRIBUTE_PURE ++# endif /* GNUC >= 3.0 */ ++#endif /* ATTRIBUTE_PURE */ ++ ++/* Use ATTRIBUTE_PRINTF when the format specifier must not be NULL. ++ This was the case for the `printf' format attribute by itself ++ before GCC 3.3, but as of 3.3 we need to add the `nonnull' ++ attribute to retain this behavior. */ ++#ifndef ATTRIBUTE_PRINTF ++#define ATTRIBUTE_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) ATTRIBUTE_NONNULL(m) ++#define ATTRIBUTE_PRINTF_1 ATTRIBUTE_PRINTF(1, 2) ++#define ATTRIBUTE_PRINTF_2 ATTRIBUTE_PRINTF(2, 3) ++#define ATTRIBUTE_PRINTF_3 ATTRIBUTE_PRINTF(3, 4) ++#define ATTRIBUTE_PRINTF_4 ATTRIBUTE_PRINTF(4, 5) ++#define ATTRIBUTE_PRINTF_5 ATTRIBUTE_PRINTF(5, 6) ++#endif /* ATTRIBUTE_PRINTF */ ++ ++/* Use ATTRIBUTE_FPTR_PRINTF when the format attribute is to be set on ++ a function pointer. Format attributes were allowed on function ++ pointers as of gcc 3.1. */ ++#ifndef ATTRIBUTE_FPTR_PRINTF ++# if (GCC_VERSION >= 3001) ++# define ATTRIBUTE_FPTR_PRINTF(m, n) ATTRIBUTE_PRINTF(m, n) ++# else ++# define ATTRIBUTE_FPTR_PRINTF(m, n) ++# endif /* GNUC >= 3.1 */ ++# define ATTRIBUTE_FPTR_PRINTF_1 ATTRIBUTE_FPTR_PRINTF(1, 2) ++# define ATTRIBUTE_FPTR_PRINTF_2 ATTRIBUTE_FPTR_PRINTF(2, 3) ++# define ATTRIBUTE_FPTR_PRINTF_3 ATTRIBUTE_FPTR_PRINTF(3, 4) ++# define ATTRIBUTE_FPTR_PRINTF_4 ATTRIBUTE_FPTR_PRINTF(4, 5) ++# define ATTRIBUTE_FPTR_PRINTF_5 ATTRIBUTE_FPTR_PRINTF(5, 6) ++#endif /* ATTRIBUTE_FPTR_PRINTF */ ++ ++/* Use ATTRIBUTE_NULL_PRINTF when the format specifier may be NULL. A ++ NULL format specifier was allowed as of gcc 3.3. */ ++#ifndef ATTRIBUTE_NULL_PRINTF ++# if (GCC_VERSION >= 3003) ++# define ATTRIBUTE_NULL_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) ++# else ++# define ATTRIBUTE_NULL_PRINTF(m, n) ++# endif /* GNUC >= 3.3 */ ++# define ATTRIBUTE_NULL_PRINTF_1 ATTRIBUTE_NULL_PRINTF(1, 2) ++# define ATTRIBUTE_NULL_PRINTF_2 ATTRIBUTE_NULL_PRINTF(2, 3) ++# define ATTRIBUTE_NULL_PRINTF_3 ATTRIBUTE_NULL_PRINTF(3, 4) ++# define ATTRIBUTE_NULL_PRINTF_4 ATTRIBUTE_NULL_PRINTF(4, 5) ++# define ATTRIBUTE_NULL_PRINTF_5 ATTRIBUTE_NULL_PRINTF(5, 6) ++#endif /* ATTRIBUTE_NULL_PRINTF */ ++ ++/* Attribute `sentinel' was valid as of gcc 3.5. */ ++#ifndef ATTRIBUTE_SENTINEL ++# if (GCC_VERSION >= 3005) ++# define ATTRIBUTE_SENTINEL __attribute__ ((__sentinel__)) ++# else ++# define ATTRIBUTE_SENTINEL ++# endif /* GNUC >= 3.5 */ ++#endif /* ATTRIBUTE_SENTINEL */ ++ ++ ++#ifndef ATTRIBUTE_ALIGNED_ALIGNOF ++# if (GCC_VERSION >= 3000) ++# define ATTRIBUTE_ALIGNED_ALIGNOF(m) __attribute__ ((__aligned__ (__alignof__ (m)))) ++# else ++# define ATTRIBUTE_ALIGNED_ALIGNOF(m) ++# endif /* GNUC >= 3.0 */ ++#endif /* ATTRIBUTE_ALIGNED_ALIGNOF */ ++ ++/* We use __extension__ in some places to suppress -pedantic warnings ++ about GCC extensions. This feature didn't work properly before ++ gcc 2.8. */ ++#if GCC_VERSION < 2008 ++#define __extension__ ++#endif ++ ++#endif /* ansidecl.h */ +--- /dev/null ++++ b/arch/x86/include/asm/ansidecl_64.h +@@ -0,0 +1,383 @@ ++/* ANSI and traditional C compatability macros ++ Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 ++ Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, write to the Free Software ++Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++/* ANSI and traditional C compatibility macros ++ ++ ANSI C is assumed if __STDC__ is #defined. ++ ++ Macro ANSI C definition Traditional C definition ++ ----- ---- - ---------- ----------- - ---------- ++ ANSI_PROTOTYPES 1 not defined ++ PTR `void *' `char *' ++ PTRCONST `void *const' `char *' ++ LONG_DOUBLE `long double' `double' ++ const not defined `' ++ volatile not defined `' ++ signed not defined `' ++ VA_START(ap, var) va_start(ap, var) va_start(ap) ++ ++ Note that it is safe to write "void foo();" indicating a function ++ with no return value, in all K+R compilers we have been able to test. ++ ++ For declaring functions with prototypes, we also provide these: ++ ++ PARAMS ((prototype)) ++ -- for functions which take a fixed number of arguments. Use this ++ when declaring the function. When defining the function, write a ++ K+R style argument list. For example: ++ ++ char *strcpy PARAMS ((char *dest, char *source)); ++ ... ++ char * ++ strcpy (dest, source) ++ char *dest; ++ char *source; ++ { ... } ++ ++ ++ VPARAMS ((prototype, ...)) ++ -- for functions which take a variable number of arguments. Use ++ PARAMS to declare the function, VPARAMS to define it. For example: ++ ++ int printf PARAMS ((const char *format, ...)); ++ ... ++ int ++ printf VPARAMS ((const char *format, ...)) ++ { ++ ... ++ } ++ ++ For writing functions which take variable numbers of arguments, we ++ also provide the VA_OPEN, VA_CLOSE, and VA_FIXEDARG macros. These ++ hide the differences between K+R and C89 more ++ thoroughly than the simple VA_START() macro mentioned above. ++ ++ VA_OPEN and VA_CLOSE are used *instead of* va_start and va_end. ++ Immediately after VA_OPEN, put a sequence of VA_FIXEDARG calls ++ corresponding to the list of fixed arguments. Then use va_arg ++ normally to get the variable arguments, or pass your va_list object ++ around. You do not declare the va_list yourself; VA_OPEN does it ++ for you. ++ ++ Here is a complete example: ++ ++ int ++ printf VPARAMS ((const char *format, ...)) ++ { ++ int result; ++ ++ VA_OPEN (ap, format); ++ VA_FIXEDARG (ap, const char *, format); ++ ++ result = vfprintf (stdout, format, ap); ++ VA_CLOSE (ap); ++ ++ return result; ++ } ++ ++ ++ You can declare variables either before or after the VA_OPEN, ++ VA_FIXEDARG sequence. Also, VA_OPEN and VA_CLOSE are the beginning ++ and end of a block. They must appear at the same nesting level, ++ and any variables declared after VA_OPEN go out of scope at ++ VA_CLOSE. Unfortunately, with a K+R compiler, that includes the ++ argument list. You can have multiple instances of VA_OPEN/VA_CLOSE ++ pairs in a single function in case you need to traverse the ++ argument list more than once. ++ ++ For ease of writing code which uses GCC extensions but needs to be ++ portable to other compilers, we provide the GCC_VERSION macro that ++ simplifies testing __GNUC__ and __GNUC_MINOR__ together, and various ++ wrappers around __attribute__. Also, __extension__ will be #defined ++ to nothing if it doesn't work. See below. ++ ++ This header also defines a lot of obsolete macros: ++ CONST, VOLATILE, SIGNED, PROTO, EXFUN, DEFUN, DEFUN_VOID, ++ AND, DOTS, NOARGS. Don't use them. */ ++ ++#ifndef _ANSIDECL_H ++#define _ANSIDECL_H 1 ++ ++/* Every source file includes this file, ++ so they will all get the switch for lint. */ ++/* LINTLIBRARY */ ++ ++/* Using MACRO(x,y) in cpp #if conditionals does not work with some ++ older preprocessors. Thus we can't define something like this: ++ ++#define HAVE_GCC_VERSION(MAJOR, MINOR) \ ++ (__GNUC__ > (MAJOR) || (__GNUC__ == (MAJOR) && __GNUC_MINOR__ >= (MINOR))) ++ ++and then test "#if HAVE_GCC_VERSION(2,7)". ++ ++So instead we use the macro below and test it against specific values. */ ++ ++/* This macro simplifies testing whether we are using gcc, and if it ++ is of a particular minimum version. (Both major & minor numbers are ++ significant.) This macro will evaluate to 0 if we are not using ++ gcc at all. */ ++#ifndef GCC_VERSION ++#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) ++#endif /* GCC_VERSION */ ++ ++#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(_WIN32) || (defined(__alpha) && defined(__cplusplus)) ++/* All known AIX compilers implement these things (but don't always ++ define __STDC__). The RISC/OS MIPS compiler defines these things ++ in SVR4 mode, but does not define __STDC__. */ ++/* eraxxon@alumni.rice.edu: The Compaq C++ compiler, unlike many other ++ C++ compilers, does not define __STDC__, though it acts as if this ++ was so. (Verified versions: 5.7, 6.2, 6.3, 6.5) */ ++ ++#define ANSI_PROTOTYPES 1 ++#define PTR void * ++#define PTRCONST void *const ++#define LONG_DOUBLE long double ++ ++/* PARAMS is often defined elsewhere (e.g. by libintl.h), so wrap it in ++ a #ifndef. */ ++//#ifndef PARAMS ++//#define PARAMS(ARGS) ARGS ++//#endif ++ ++#define VPARAMS(ARGS) ARGS ++#define VA_START(VA_LIST, VAR) va_start(VA_LIST, VAR) ++ ++/* variadic function helper macros */ ++/* "struct Qdmy" swallows the semicolon after VA_OPEN/VA_FIXEDARG's ++ use without inhibiting further decls and without declaring an ++ actual variable. */ ++#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP, VAR); { struct Qdmy ++#define VA_CLOSE(AP) } va_end(AP); } ++#define VA_FIXEDARG(AP, T, N) struct Qdmy ++ ++#undef const ++#undef volatile ++#undef signed ++ ++#ifdef __KERNEL__ ++#ifndef __STDC_VERSION__ ++#define __STDC_VERSION__ 0 ++#endif ++#endif /* __KERNEL__ */ ++ ++/* inline requires special treatment; it's in C99, and GCC >=2.7 supports ++ it too, but it's not in C89. */ ++#undef inline ++#if __STDC_VERSION__ > 199901L ++/* it's a keyword */ ++#else ++# if GCC_VERSION >= 2007 ++# define inline __inline__ /* __inline__ prevents -pedantic warnings */ ++# else ++# define inline /* nothing */ ++# endif ++#endif ++ ++/* These are obsolete. Do not use. */ ++#ifndef IN_GCC ++#define CONST const ++#define VOLATILE volatile ++#define SIGNED signed ++ ++#define PROTO(type, name, arglist) type name arglist ++#define EXFUN(name, proto) name proto ++#define DEFUN(name, arglist, args) name(args) ++#define DEFUN_VOID(name) name(void) ++#define AND , ++#define DOTS , ... ++#define NOARGS void ++#endif /* ! IN_GCC */ ++ ++#else /* Not ANSI C. */ ++ ++#undef ANSI_PROTOTYPES ++#define PTR char * ++#define PTRCONST PTR ++#define LONG_DOUBLE double ++ ++//#define PARAMS(args) () ++#define VPARAMS(args) (va_alist) va_dcl ++#define VA_START(va_list, var) va_start(va_list) ++ ++#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP); { struct Qdmy ++#define VA_CLOSE(AP) } va_end(AP); } ++#define VA_FIXEDARG(AP, TYPE, NAME) TYPE NAME = va_arg(AP, TYPE) ++ ++/* some systems define these in header files for non-ansi mode */ ++#undef const ++#undef volatile ++#undef signed ++#undef inline ++#define const ++#define volatile ++#define signed ++#define inline ++ ++#ifndef IN_GCC ++#define CONST ++#define VOLATILE ++#define SIGNED ++ ++#define PROTO(type, name, arglist) type name () ++#define EXFUN(name, proto) name() ++#define DEFUN(name, arglist, args) name arglist args; ++#define DEFUN_VOID(name) name() ++#define AND ; ++#define DOTS ++#define NOARGS ++#endif /* ! IN_GCC */ ++ ++#endif /* ANSI C. */ ++ ++/* Define macros for some gcc attributes. This permits us to use the ++ macros freely, and know that they will come into play for the ++ version of gcc in which they are supported. */ ++ ++#if (GCC_VERSION < 2007) ++# define __attribute__(x) ++#endif ++ ++/* Attribute __malloc__ on functions was valid as of gcc 2.96. */ ++#ifndef ATTRIBUTE_MALLOC ++# if (GCC_VERSION >= 2096) ++# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) ++# else ++# define ATTRIBUTE_MALLOC ++# endif /* GNUC >= 2.96 */ ++#endif /* ATTRIBUTE_MALLOC */ ++ ++/* Attributes on labels were valid as of gcc 2.93. */ ++#ifndef ATTRIBUTE_UNUSED_LABEL ++# if (!defined (__cplusplus) && GCC_VERSION >= 2093) ++# define ATTRIBUTE_UNUSED_LABEL ATTRIBUTE_UNUSED ++# else ++# define ATTRIBUTE_UNUSED_LABEL ++# endif /* !__cplusplus && GNUC >= 2.93 */ ++#endif /* ATTRIBUTE_UNUSED_LABEL */ ++ ++#ifndef ATTRIBUTE_UNUSED ++#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) ++#endif /* ATTRIBUTE_UNUSED */ ++ ++/* Before GCC 3.4, the C++ frontend couldn't parse attributes placed after the ++ identifier name. */ ++#if ! defined(__cplusplus) || (GCC_VERSION >= 3004) ++# define ARG_UNUSED(NAME) NAME ATTRIBUTE_UNUSED ++#else /* !__cplusplus || GNUC >= 3.4 */ ++# define ARG_UNUSED(NAME) NAME ++#endif /* !__cplusplus || GNUC >= 3.4 */ ++ ++#ifndef ATTRIBUTE_NORETURN ++#define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) ++#endif /* ATTRIBUTE_NORETURN */ ++ ++/* Attribute `nonnull' was valid as of gcc 3.3. */ ++#ifndef ATTRIBUTE_NONNULL ++# if (GCC_VERSION >= 3003) ++# define ATTRIBUTE_NONNULL(m) __attribute__ ((__nonnull__ (m))) ++# else ++# define ATTRIBUTE_NONNULL(m) ++# endif /* GNUC >= 3.3 */ ++#endif /* ATTRIBUTE_NONNULL */ ++ ++/* Attribute `pure' was valid as of gcc 3.0. */ ++#ifndef ATTRIBUTE_PURE ++# if (GCC_VERSION >= 3000) ++# define ATTRIBUTE_PURE __attribute__ ((__pure__)) ++# else ++# define ATTRIBUTE_PURE ++# endif /* GNUC >= 3.0 */ ++#endif /* ATTRIBUTE_PURE */ ++ ++/* Use ATTRIBUTE_PRINTF when the format specifier must not be NULL. ++ This was the case for the `printf' format attribute by itself ++ before GCC 3.3, but as of 3.3 we need to add the `nonnull' ++ attribute to retain this behavior. */ ++#ifndef ATTRIBUTE_PRINTF ++#define ATTRIBUTE_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) ATTRIBUTE_NONNULL(m) ++#define ATTRIBUTE_PRINTF_1 ATTRIBUTE_PRINTF(1, 2) ++#define ATTRIBUTE_PRINTF_2 ATTRIBUTE_PRINTF(2, 3) ++#define ATTRIBUTE_PRINTF_3 ATTRIBUTE_PRINTF(3, 4) ++#define ATTRIBUTE_PRINTF_4 ATTRIBUTE_PRINTF(4, 5) ++#define ATTRIBUTE_PRINTF_5 ATTRIBUTE_PRINTF(5, 6) ++#endif /* ATTRIBUTE_PRINTF */ ++ ++/* Use ATTRIBUTE_FPTR_PRINTF when the format attribute is to be set on ++ a function pointer. Format attributes were allowed on function ++ pointers as of gcc 3.1. */ ++#ifndef ATTRIBUTE_FPTR_PRINTF ++# if (GCC_VERSION >= 3001) ++# define ATTRIBUTE_FPTR_PRINTF(m, n) ATTRIBUTE_PRINTF(m, n) ++# else ++# define ATTRIBUTE_FPTR_PRINTF(m, n) ++# endif /* GNUC >= 3.1 */ ++# define ATTRIBUTE_FPTR_PRINTF_1 ATTRIBUTE_FPTR_PRINTF(1, 2) ++# define ATTRIBUTE_FPTR_PRINTF_2 ATTRIBUTE_FPTR_PRINTF(2, 3) ++# define ATTRIBUTE_FPTR_PRINTF_3 ATTRIBUTE_FPTR_PRINTF(3, 4) ++# define ATTRIBUTE_FPTR_PRINTF_4 ATTRIBUTE_FPTR_PRINTF(4, 5) ++# define ATTRIBUTE_FPTR_PRINTF_5 ATTRIBUTE_FPTR_PRINTF(5, 6) ++#endif /* ATTRIBUTE_FPTR_PRINTF */ ++ ++/* Use ATTRIBUTE_NULL_PRINTF when the format specifier may be NULL. A ++ NULL format specifier was allowed as of gcc 3.3. */ ++#ifndef ATTRIBUTE_NULL_PRINTF ++# if (GCC_VERSION >= 3003) ++# define ATTRIBUTE_NULL_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) ++# else ++# define ATTRIBUTE_NULL_PRINTF(m, n) ++# endif /* GNUC >= 3.3 */ ++# define ATTRIBUTE_NULL_PRINTF_1 ATTRIBUTE_NULL_PRINTF(1, 2) ++# define ATTRIBUTE_NULL_PRINTF_2 ATTRIBUTE_NULL_PRINTF(2, 3) ++# define ATTRIBUTE_NULL_PRINTF_3 ATTRIBUTE_NULL_PRINTF(3, 4) ++# define ATTRIBUTE_NULL_PRINTF_4 ATTRIBUTE_NULL_PRINTF(4, 5) ++# define ATTRIBUTE_NULL_PRINTF_5 ATTRIBUTE_NULL_PRINTF(5, 6) ++#endif /* ATTRIBUTE_NULL_PRINTF */ ++ ++/* Attribute `sentinel' was valid as of gcc 3.5. */ ++#ifndef ATTRIBUTE_SENTINEL ++# if (GCC_VERSION >= 3005) ++# define ATTRIBUTE_SENTINEL __attribute__ ((__sentinel__)) ++# else ++# define ATTRIBUTE_SENTINEL ++# endif /* GNUC >= 3.5 */ ++#endif /* ATTRIBUTE_SENTINEL */ ++ ++ ++#ifndef ATTRIBUTE_ALIGNED_ALIGNOF ++# if (GCC_VERSION >= 3000) ++# define ATTRIBUTE_ALIGNED_ALIGNOF(m) __attribute__ ((__aligned__ (__alignof__ (m)))) ++# else ++# define ATTRIBUTE_ALIGNED_ALIGNOF(m) ++# endif /* GNUC >= 3.0 */ ++#endif /* ATTRIBUTE_ALIGNED_ALIGNOF */ ++ ++/* We use __extension__ in some places to suppress -pedantic warnings ++ about GCC extensions. This feature didn't work properly before ++ gcc 2.8. */ ++#if GCC_VERSION < 2008 ++#define __extension__ ++#endif ++ ++#endif /* ansidecl.h */ +--- /dev/null ++++ b/arch/x86/include/asm/bfd.h +@@ -0,0 +1,5 @@ ++#ifdef CONFIG_X86_32 ++# include "bfd_32.h" ++#else ++# include "bfd_64.h" ++#endif +--- /dev/null ++++ b/arch/x86/include/asm/bfd_32.h +@@ -0,0 +1,4921 @@ ++/* DO NOT EDIT! -*- buffer-read-only: t -*- This file is automatically ++ generated from "bfd-in.h", "init.c", "opncls.c", "libbfd.c", ++ "bfdio.c", "bfdwin.c", "section.c", "archures.c", "reloc.c", ++ "syms.c", "bfd.c", "archive.c", "corefile.c", "targets.c", "format.c", ++ "linker.c" and "simple.c". ++ Run "make headers" in your build bfd/ to regenerate. */ ++ ++/* Main header file for the bfd library -- portable access to object files. ++ ++ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, ++ 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. ++ ++ Contributed by Cygnus Support. ++ ++ This file is part of BFD, the Binary File Descriptor library. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifndef __BFD_H_SEEN__ ++#define __BFD_H_SEEN__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifdef __KERNEL__ ++#include ++#else /* __KERNEL__ */ ++#include "ansidecl.h" ++#include "symcat.h" ++#endif /* __KERNEL__ */ ++#if defined (__STDC__) || defined (ALMOST_STDC) || defined (HAVE_STRINGIZE) ++#ifndef SABER ++/* This hack is to avoid a problem with some strict ANSI C preprocessors. ++ The problem is, "32_" is not a valid preprocessing token, and we don't ++ want extra underscores (e.g., "nlm_32_"). The XCONCAT2 macro will ++ cause the inner CONCAT2 macros to be evaluated first, producing ++ still-valid pp-tokens. Then the final concatenation can be done. */ ++#undef CONCAT4 ++#define CONCAT4(a,b,c,d) XCONCAT2(CONCAT2(a,b),CONCAT2(c,d)) ++#endif ++#endif ++ ++/* The word size used by BFD on the host. This may be 64 with a 32 ++ bit target if the host is 64 bit, or if other 64 bit targets have ++ been selected with --enable-targets, or if --enable-64-bit-bfd. */ ++#ifdef __KERNEL__ ++#define BFD_ARCH_SIZE 32 ++#else /* __KERNEL__ */ ++#define BFD_ARCH_SIZE 64 ++#endif /* __KERNEL__ */ ++ ++/* The word size of the default bfd target. */ ++#define BFD_DEFAULT_TARGET_SIZE 32 ++ ++#define BFD_HOST_64BIT_LONG 0 ++#define BFD_HOST_LONG_LONG 1 ++#if 1 ++#define BFD_HOST_64_BIT long long ++#define BFD_HOST_U_64_BIT unsigned long long ++typedef BFD_HOST_64_BIT bfd_int64_t; ++typedef BFD_HOST_U_64_BIT bfd_uint64_t; ++#endif ++ ++#if BFD_ARCH_SIZE >= 64 ++#define BFD64 ++#endif ++ ++#ifndef INLINE ++#if __GNUC__ >= 2 ++#define INLINE __inline__ ++#else ++#define INLINE ++#endif ++#endif ++ ++/* Forward declaration. */ ++typedef struct bfd bfd; ++ ++/* Boolean type used in bfd. Too many systems define their own ++ versions of "boolean" for us to safely typedef a "boolean" of ++ our own. Using an enum for "bfd_boolean" has its own set of ++ problems, with strange looking casts required to avoid warnings ++ on some older compilers. Thus we just use an int. ++ ++ General rule: Functions which are bfd_boolean return TRUE on ++ success and FALSE on failure (unless they're a predicate). */ ++ ++typedef int bfd_boolean; ++#undef FALSE ++#undef TRUE ++#define FALSE 0 ++#define TRUE 1 ++ ++#ifdef BFD64 ++ ++#ifndef BFD_HOST_64_BIT ++ #error No 64 bit integer type available ++#endif /* ! defined (BFD_HOST_64_BIT) */ ++ ++typedef BFD_HOST_U_64_BIT bfd_vma; ++typedef BFD_HOST_64_BIT bfd_signed_vma; ++typedef BFD_HOST_U_64_BIT bfd_size_type; ++typedef BFD_HOST_U_64_BIT symvalue; ++ ++#ifndef fprintf_vma ++#if BFD_HOST_64BIT_LONG ++#define sprintf_vma(s,x) sprintf (s, "%016lx", x) ++#define fprintf_vma(f,x) fprintf (f, "%016lx", x) ++#else ++#define _bfd_int64_low(x) ((unsigned long) (((x) & 0xffffffff))) ++#define _bfd_int64_high(x) ((unsigned long) (((x) >> 32) & 0xffffffff)) ++#define fprintf_vma(s,x) \ ++ fprintf ((s), "%08lx%08lx", _bfd_int64_high (x), _bfd_int64_low (x)) ++#define sprintf_vma(s,x) \ ++ sprintf ((s), "%08lx%08lx", _bfd_int64_high (x), _bfd_int64_low (x)) ++#endif ++#endif ++ ++#else /* not BFD64 */ ++ ++/* Represent a target address. Also used as a generic unsigned type ++ which is guaranteed to be big enough to hold any arithmetic types ++ we need to deal with. */ ++typedef unsigned long bfd_vma; ++ ++/* A generic signed type which is guaranteed to be big enough to hold any ++ arithmetic types we need to deal with. Can be assumed to be compatible ++ with bfd_vma in the same way that signed and unsigned ints are compatible ++ (as parameters, in assignment, etc). */ ++typedef long bfd_signed_vma; ++ ++typedef unsigned long symvalue; ++typedef unsigned long bfd_size_type; ++ ++/* Print a bfd_vma x on stream s. */ ++#define fprintf_vma(s,x) fprintf (s, "%08lx", x) ++#define sprintf_vma(s,x) sprintf (s, "%08lx", x) ++ ++#endif /* not BFD64 */ ++ ++#define HALF_BFD_SIZE_TYPE \ ++ (((bfd_size_type) 1) << (8 * sizeof (bfd_size_type) / 2)) ++ ++#ifndef BFD_HOST_64_BIT ++/* Fall back on a 32 bit type. The idea is to make these types always ++ available for function return types, but in the case that ++ BFD_HOST_64_BIT is undefined such a function should abort or ++ otherwise signal an error. */ ++typedef bfd_signed_vma bfd_int64_t; ++typedef bfd_vma bfd_uint64_t; ++#endif ++ ++/* An offset into a file. BFD always uses the largest possible offset ++ based on the build time availability of fseek, fseeko, or fseeko64. */ ++typedef BFD_HOST_64_BIT file_ptr; ++typedef unsigned BFD_HOST_64_BIT ufile_ptr; ++ ++extern void bfd_sprintf_vma (bfd *, char *, bfd_vma); ++extern void bfd_fprintf_vma (bfd *, void *, bfd_vma); ++ ++#define printf_vma(x) fprintf_vma(stdout,x) ++#define bfd_printf_vma(abfd,x) bfd_fprintf_vma (abfd,stdout,x) ++ ++typedef unsigned int flagword; /* 32 bits of flags */ ++typedef unsigned char bfd_byte; ++ ++/* File formats. */ ++ ++typedef enum bfd_format ++{ ++ bfd_unknown = 0, /* File format is unknown. */ ++ bfd_object, /* Linker/assembler/compiler output. */ ++ bfd_archive, /* Object archive file. */ ++ bfd_core, /* Core dump. */ ++ bfd_type_end /* Marks the end; don't use it! */ ++} ++bfd_format; ++ ++/* Values that may appear in the flags field of a BFD. These also ++ appear in the object_flags field of the bfd_target structure, where ++ they indicate the set of flags used by that backend (not all flags ++ are meaningful for all object file formats) (FIXME: at the moment, ++ the object_flags values have mostly just been copied from backend ++ to another, and are not necessarily correct). */ ++ ++/* No flags. */ ++#define BFD_NO_FLAGS 0x00 ++ ++/* BFD contains relocation entries. */ ++#define HAS_RELOC 0x01 ++ ++/* BFD is directly executable. */ ++#define EXEC_P 0x02 ++ ++/* BFD has line number information (basically used for F_LNNO in a ++ COFF header). */ ++#define HAS_LINENO 0x04 ++ ++/* BFD has debugging information. */ ++#define HAS_DEBUG 0x08 ++ ++/* BFD has symbols. */ ++#define HAS_SYMS 0x10 ++ ++/* BFD has local symbols (basically used for F_LSYMS in a COFF ++ header). */ ++#define HAS_LOCALS 0x20 ++ ++/* BFD is a dynamic object. */ ++#define DYNAMIC 0x40 ++ ++/* Text section is write protected (if D_PAGED is not set, this is ++ like an a.out NMAGIC file) (the linker sets this by default, but ++ clears it for -r or -N). */ ++#define WP_TEXT 0x80 ++ ++/* BFD is dynamically paged (this is like an a.out ZMAGIC file) (the ++ linker sets this by default, but clears it for -r or -n or -N). */ ++#define D_PAGED 0x100 ++ ++/* BFD is relaxable (this means that bfd_relax_section may be able to ++ do something) (sometimes bfd_relax_section can do something even if ++ this is not set). */ ++#define BFD_IS_RELAXABLE 0x200 ++ ++/* This may be set before writing out a BFD to request using a ++ traditional format. For example, this is used to request that when ++ writing out an a.out object the symbols not be hashed to eliminate ++ duplicates. */ ++#define BFD_TRADITIONAL_FORMAT 0x400 ++ ++/* This flag indicates that the BFD contents are actually cached in ++ memory. If this is set, iostream points to a bfd_in_memory struct. */ ++#define BFD_IN_MEMORY 0x800 ++ ++/* The sections in this BFD specify a memory page. */ ++#define HAS_LOAD_PAGE 0x1000 ++ ++/* This BFD has been created by the linker and doesn't correspond ++ to any input file. */ ++#define BFD_LINKER_CREATED 0x2000 ++ ++/* Symbols and relocation. */ ++ ++/* A count of carsyms (canonical archive symbols). */ ++typedef unsigned long symindex; ++ ++/* How to perform a relocation. */ ++typedef const struct reloc_howto_struct reloc_howto_type; ++ ++#define BFD_NO_MORE_SYMBOLS ((symindex) ~0) ++ ++/* General purpose part of a symbol X; ++ target specific parts are in libcoff.h, libaout.h, etc. */ ++ ++#define bfd_get_section(x) ((x)->section) ++#define bfd_get_output_section(x) ((x)->section->output_section) ++#define bfd_set_section(x,y) ((x)->section) = (y) ++#define bfd_asymbol_base(x) ((x)->section->vma) ++#define bfd_asymbol_value(x) (bfd_asymbol_base(x) + (x)->value) ++#define bfd_asymbol_name(x) ((x)->name) ++/*Perhaps future: #define bfd_asymbol_bfd(x) ((x)->section->owner)*/ ++#define bfd_asymbol_bfd(x) ((x)->the_bfd) ++#define bfd_asymbol_flavour(x) (bfd_asymbol_bfd(x)->xvec->flavour) ++ ++/* A canonical archive symbol. */ ++/* This is a type pun with struct ranlib on purpose! */ ++typedef struct carsym ++{ ++ char *name; ++ file_ptr file_offset; /* Look here to find the file. */ ++} ++carsym; /* To make these you call a carsymogen. */ ++ ++/* Used in generating armaps (archive tables of contents). ++ Perhaps just a forward definition would do? */ ++struct orl /* Output ranlib. */ ++{ ++ char **name; /* Symbol name. */ ++ union ++ { ++ file_ptr pos; ++ bfd *abfd; ++ } u; /* bfd* or file position. */ ++ int namidx; /* Index into string table. */ ++}; ++ ++/* Linenumber stuff. */ ++typedef struct lineno_cache_entry ++{ ++ unsigned int line_number; /* Linenumber from start of function. */ ++ union ++ { ++ struct bfd_symbol *sym; /* Function name. */ ++ bfd_vma offset; /* Offset into section. */ ++ } u; ++} ++alent; ++ ++/* Object and core file sections. */ ++ ++#define align_power(addr, align) \ ++ (((addr) + ((bfd_vma) 1 << (align)) - 1) & ((bfd_vma) -1 << (align))) ++ ++typedef struct bfd_section *sec_ptr; ++ ++#define bfd_get_section_name(bfd, ptr) ((ptr)->name + 0) ++#define bfd_get_section_vma(bfd, ptr) ((ptr)->vma + 0) ++#define bfd_get_section_lma(bfd, ptr) ((ptr)->lma + 0) ++#define bfd_get_section_alignment(bfd, ptr) ((ptr)->alignment_power + 0) ++#define bfd_section_name(bfd, ptr) ((ptr)->name) ++#define bfd_section_size(bfd, ptr) ((ptr)->size) ++#define bfd_get_section_size(ptr) ((ptr)->size) ++#define bfd_section_vma(bfd, ptr) ((ptr)->vma) ++#define bfd_section_lma(bfd, ptr) ((ptr)->lma) ++#define bfd_section_alignment(bfd, ptr) ((ptr)->alignment_power) ++#define bfd_get_section_flags(bfd, ptr) ((ptr)->flags + 0) ++#define bfd_get_section_userdata(bfd, ptr) ((ptr)->userdata) ++ ++#define bfd_is_com_section(ptr) (((ptr)->flags & SEC_IS_COMMON) != 0) ++ ++#define bfd_set_section_vma(bfd, ptr, val) (((ptr)->vma = (ptr)->lma = (val)), ((ptr)->user_set_vma = TRUE), TRUE) ++#define bfd_set_section_alignment(bfd, ptr, val) (((ptr)->alignment_power = (val)),TRUE) ++#define bfd_set_section_userdata(bfd, ptr, val) (((ptr)->userdata = (val)),TRUE) ++/* Find the address one past the end of SEC. */ ++#define bfd_get_section_limit(bfd, sec) \ ++ (((sec)->rawsize ? (sec)->rawsize : (sec)->size) \ ++ / bfd_octets_per_byte (bfd)) ++ ++typedef struct stat stat_type; ++ ++typedef enum bfd_print_symbol ++{ ++ bfd_print_symbol_name, ++ bfd_print_symbol_more, ++ bfd_print_symbol_all ++} bfd_print_symbol_type; ++ ++/* Information about a symbol that nm needs. */ ++ ++typedef struct _symbol_info ++{ ++ symvalue value; ++ char type; ++ const char *name; /* Symbol name. */ ++ unsigned char stab_type; /* Stab type. */ ++ char stab_other; /* Stab other. */ ++ short stab_desc; /* Stab desc. */ ++ const char *stab_name; /* String for stab type. */ ++} symbol_info; ++ ++/* Get the name of a stabs type code. */ ++ ++extern const char *bfd_get_stab_name (int); ++ ++/* Hash table routines. There is no way to free up a hash table. */ ++ ++/* An element in the hash table. Most uses will actually use a larger ++ structure, and an instance of this will be the first field. */ ++ ++struct bfd_hash_entry ++{ ++ /* Next entry for this hash code. */ ++ struct bfd_hash_entry *next; ++ /* String being hashed. */ ++ const char *string; ++ /* Hash code. This is the full hash code, not the index into the ++ table. */ ++ unsigned long hash; ++}; ++ ++/* A hash table. */ ++ ++struct bfd_hash_table ++{ ++ /* The hash array. */ ++ struct bfd_hash_entry **table; ++ /* The number of slots in the hash table. */ ++ unsigned int size; ++ /* A function used to create new elements in the hash table. The ++ first entry is itself a pointer to an element. When this ++ function is first invoked, this pointer will be NULL. However, ++ having the pointer permits a hierarchy of method functions to be ++ built each of which calls the function in the superclass. Thus ++ each function should be written to allocate a new block of memory ++ only if the argument is NULL. */ ++ struct bfd_hash_entry *(*newfunc) ++ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *); ++ /* An objalloc for this hash table. This is a struct objalloc *, ++ but we use void * to avoid requiring the inclusion of objalloc.h. */ ++ void *memory; ++}; ++ ++/* Initialize a hash table. */ ++extern bfd_boolean bfd_hash_table_init ++ (struct bfd_hash_table *, ++ struct bfd_hash_entry *(*) (struct bfd_hash_entry *, ++ struct bfd_hash_table *, ++ const char *)); ++ ++/* Initialize a hash table specifying a size. */ ++extern bfd_boolean bfd_hash_table_init_n ++ (struct bfd_hash_table *, ++ struct bfd_hash_entry *(*) (struct bfd_hash_entry *, ++ struct bfd_hash_table *, ++ const char *), ++ unsigned int size); ++ ++/* Free up a hash table. */ ++extern void bfd_hash_table_free ++ (struct bfd_hash_table *); ++ ++/* Look up a string in a hash table. If CREATE is TRUE, a new entry ++ will be created for this string if one does not already exist. The ++ COPY argument must be TRUE if this routine should copy the string ++ into newly allocated memory when adding an entry. */ ++extern struct bfd_hash_entry *bfd_hash_lookup ++ (struct bfd_hash_table *, const char *, bfd_boolean create, ++ bfd_boolean copy); ++ ++/* Replace an entry in a hash table. */ ++extern void bfd_hash_replace ++ (struct bfd_hash_table *, struct bfd_hash_entry *old, ++ struct bfd_hash_entry *nw); ++ ++/* Base method for creating a hash table entry. */ ++extern struct bfd_hash_entry *bfd_hash_newfunc ++ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *); ++ ++/* Grab some space for a hash table entry. */ ++extern void *bfd_hash_allocate ++ (struct bfd_hash_table *, unsigned int); ++ ++/* Traverse a hash table in a random order, calling a function on each ++ element. If the function returns FALSE, the traversal stops. The ++ INFO argument is passed to the function. */ ++extern void bfd_hash_traverse ++ (struct bfd_hash_table *, ++ bfd_boolean (*) (struct bfd_hash_entry *, void *), ++ void *info); ++ ++/* Allows the default size of a hash table to be configured. New hash ++ tables allocated using bfd_hash_table_init will be created with ++ this size. */ ++extern void bfd_hash_set_default_size (bfd_size_type); ++ ++/* This structure is used to keep track of stabs in sections ++ information while linking. */ ++ ++struct stab_info ++{ ++ /* A hash table used to hold stabs strings. */ ++ struct bfd_strtab_hash *strings; ++ /* The header file hash table. */ ++ struct bfd_hash_table includes; ++ /* The first .stabstr section. */ ++ struct bfd_section *stabstr; ++}; ++ ++#define COFF_SWAP_TABLE (void *) &bfd_coff_std_swap_table ++ ++/* User program access to BFD facilities. */ ++ ++/* Direct I/O routines, for programs which know more about the object ++ file than BFD does. Use higher level routines if possible. */ ++ ++extern bfd_size_type bfd_bread (void *, bfd_size_type, bfd *); ++extern bfd_size_type bfd_bwrite (const void *, bfd_size_type, bfd *); ++extern int bfd_seek (bfd *, file_ptr, int); ++extern file_ptr bfd_tell (bfd *); ++extern int bfd_flush (bfd *); ++extern int bfd_stat (bfd *, struct stat *); ++ ++/* Deprecated old routines. */ ++#if __GNUC__ ++#define bfd_read(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_read", __FILE__, __LINE__, __FUNCTION__), \ ++ bfd_bread ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#define bfd_write(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_write", __FILE__, __LINE__, __FUNCTION__), \ ++ bfd_bwrite ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#else ++#define bfd_read(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_read", (const char *) 0, 0, (const char *) 0), \ ++ bfd_bread ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#define bfd_write(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_write", (const char *) 0, 0, (const char *) 0),\ ++ bfd_bwrite ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#endif ++extern void warn_deprecated (const char *, const char *, int, const char *); ++ ++/* Cast from const char * to char * so that caller can assign to ++ a char * without a warning. */ ++#define bfd_get_filename(abfd) ((char *) (abfd)->filename) ++#define bfd_get_cacheable(abfd) ((abfd)->cacheable) ++#define bfd_get_format(abfd) ((abfd)->format) ++#define bfd_get_target(abfd) ((abfd)->xvec->name) ++#define bfd_get_flavour(abfd) ((abfd)->xvec->flavour) ++#define bfd_family_coff(abfd) \ ++ (bfd_get_flavour (abfd) == bfd_target_coff_flavour || \ ++ bfd_get_flavour (abfd) == bfd_target_xcoff_flavour) ++#define bfd_big_endian(abfd) ((abfd)->xvec->byteorder == BFD_ENDIAN_BIG) ++#define bfd_little_endian(abfd) ((abfd)->xvec->byteorder == BFD_ENDIAN_LITTLE) ++#define bfd_header_big_endian(abfd) \ ++ ((abfd)->xvec->header_byteorder == BFD_ENDIAN_BIG) ++#define bfd_header_little_endian(abfd) \ ++ ((abfd)->xvec->header_byteorder == BFD_ENDIAN_LITTLE) ++#define bfd_get_file_flags(abfd) ((abfd)->flags) ++#define bfd_applicable_file_flags(abfd) ((abfd)->xvec->object_flags) ++#define bfd_applicable_section_flags(abfd) ((abfd)->xvec->section_flags) ++#define bfd_my_archive(abfd) ((abfd)->my_archive) ++#define bfd_has_map(abfd) ((abfd)->has_armap) ++ ++#define bfd_valid_reloc_types(abfd) ((abfd)->xvec->valid_reloc_types) ++#define bfd_usrdata(abfd) ((abfd)->usrdata) ++ ++#define bfd_get_start_address(abfd) ((abfd)->start_address) ++#define bfd_get_symcount(abfd) ((abfd)->symcount) ++#define bfd_get_outsymbols(abfd) ((abfd)->outsymbols) ++#define bfd_count_sections(abfd) ((abfd)->section_count) ++ ++#define bfd_get_dynamic_symcount(abfd) ((abfd)->dynsymcount) ++ ++#define bfd_get_symbol_leading_char(abfd) ((abfd)->xvec->symbol_leading_char) ++ ++#define bfd_set_cacheable(abfd,bool) (((abfd)->cacheable = bool), TRUE) ++ ++extern bfd_boolean bfd_cache_close ++ (bfd *abfd); ++/* NB: This declaration should match the autogenerated one in libbfd.h. */ ++ ++extern bfd_boolean bfd_cache_close_all (void); ++ ++extern bfd_boolean bfd_record_phdr ++ (bfd *, unsigned long, bfd_boolean, flagword, bfd_boolean, bfd_vma, ++ bfd_boolean, bfd_boolean, unsigned int, struct bfd_section **); ++ ++/* Byte swapping routines. */ ++ ++bfd_uint64_t bfd_getb64 (const void *); ++bfd_uint64_t bfd_getl64 (const void *); ++bfd_int64_t bfd_getb_signed_64 (const void *); ++bfd_int64_t bfd_getl_signed_64 (const void *); ++bfd_vma bfd_getb32 (const void *); ++bfd_vma bfd_getl32 (const void *); ++bfd_signed_vma bfd_getb_signed_32 (const void *); ++bfd_signed_vma bfd_getl_signed_32 (const void *); ++bfd_vma bfd_getb16 (const void *); ++bfd_vma bfd_getl16 (const void *); ++bfd_signed_vma bfd_getb_signed_16 (const void *); ++bfd_signed_vma bfd_getl_signed_16 (const void *); ++void bfd_putb64 (bfd_uint64_t, void *); ++void bfd_putl64 (bfd_uint64_t, void *); ++void bfd_putb32 (bfd_vma, void *); ++void bfd_putl32 (bfd_vma, void *); ++void bfd_putb16 (bfd_vma, void *); ++void bfd_putl16 (bfd_vma, void *); ++ ++/* Byte swapping routines which take size and endiannes as arguments. */ ++ ++bfd_uint64_t bfd_get_bits (const void *, int, bfd_boolean); ++void bfd_put_bits (bfd_uint64_t, void *, int, bfd_boolean); ++ ++extern bfd_boolean bfd_section_already_linked_table_init (void); ++extern void bfd_section_already_linked_table_free (void); ++ ++/* Externally visible ECOFF routines. */ ++ ++#if defined(__STDC__) || defined(ALMOST_STDC) ++struct ecoff_debug_info; ++struct ecoff_debug_swap; ++struct ecoff_extr; ++struct bfd_symbol; ++struct bfd_link_info; ++struct bfd_link_hash_entry; ++struct bfd_elf_version_tree; ++#endif ++extern bfd_vma bfd_ecoff_get_gp_value ++ (bfd * abfd); ++extern bfd_boolean bfd_ecoff_set_gp_value ++ (bfd *abfd, bfd_vma gp_value); ++extern bfd_boolean bfd_ecoff_set_regmasks ++ (bfd *abfd, unsigned long gprmask, unsigned long fprmask, ++ unsigned long *cprmask); ++extern void *bfd_ecoff_debug_init ++ (bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, struct bfd_link_info *); ++extern void bfd_ecoff_debug_free ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_accumulate ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, bfd *input_bfd, ++ struct ecoff_debug_info *input_debug, ++ const struct ecoff_debug_swap *input_swap, struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_accumulate_other ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, bfd *input_bfd, ++ struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_externals ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, bfd_boolean relocatable, ++ bfd_boolean (*get_extr) (struct bfd_symbol *, struct ecoff_extr *), ++ void (*set_index) (struct bfd_symbol *, bfd_size_type)); ++extern bfd_boolean bfd_ecoff_debug_one_external ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, const char *name, ++ struct ecoff_extr *esym); ++extern bfd_size_type bfd_ecoff_debug_size ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap); ++extern bfd_boolean bfd_ecoff_write_debug ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, file_ptr where); ++extern bfd_boolean bfd_ecoff_write_accumulated_debug ++ (void *handle, bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, ++ struct bfd_link_info *info, file_ptr where); ++ ++/* Externally visible ELF routines. */ ++ ++struct bfd_link_needed_list ++{ ++ struct bfd_link_needed_list *next; ++ bfd *by; ++ const char *name; ++}; ++ ++enum dynamic_lib_link_class { ++ DYN_NORMAL = 0, ++ DYN_AS_NEEDED = 1, ++ DYN_DT_NEEDED = 2, ++ DYN_NO_ADD_NEEDED = 4, ++ DYN_NO_NEEDED = 8 ++}; ++ ++extern bfd_boolean bfd_elf_record_link_assignment ++ (struct bfd_link_info *, const char *, bfd_boolean); ++extern struct bfd_link_needed_list *bfd_elf_get_needed_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_elf_get_bfd_needed_list ++ (bfd *, struct bfd_link_needed_list **); ++extern bfd_boolean bfd_elf_size_dynamic_sections ++ (bfd *, const char *, const char *, const char *, const char * const *, ++ struct bfd_link_info *, struct bfd_section **, ++ struct bfd_elf_version_tree *); ++extern bfd_boolean bfd_elf_size_dynsym_hash_dynstr ++ (bfd *, struct bfd_link_info *); ++extern void bfd_elf_set_dt_needed_name ++ (bfd *, const char *); ++extern const char *bfd_elf_get_dt_soname ++ (bfd *); ++extern void bfd_elf_set_dyn_lib_class ++ (bfd *, int); ++extern int bfd_elf_get_dyn_lib_class ++ (bfd *); ++extern struct bfd_link_needed_list *bfd_elf_get_runpath_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_elf_discard_info ++ (bfd *, struct bfd_link_info *); ++extern unsigned int _bfd_elf_default_action_discarded ++ (struct bfd_section *); ++ ++/* Return an upper bound on the number of bytes required to store a ++ copy of ABFD's program header table entries. Return -1 if an error ++ occurs; bfd_get_error will return an appropriate code. */ ++extern long bfd_get_elf_phdr_upper_bound ++ (bfd *abfd); ++ ++/* Copy ABFD's program header table entries to *PHDRS. The entries ++ will be stored as an array of Elf_Internal_Phdr structures, as ++ defined in include/elf/internal.h. To find out how large the ++ buffer needs to be, call bfd_get_elf_phdr_upper_bound. ++ ++ Return the number of program header table entries read, or -1 if an ++ error occurs; bfd_get_error will return an appropriate code. */ ++extern int bfd_get_elf_phdrs ++ (bfd *abfd, void *phdrs); ++ ++/* Create a new BFD as if by bfd_openr. Rather than opening a file, ++ reconstruct an ELF file by reading the segments out of remote memory ++ based on the ELF file header at EHDR_VMA and the ELF program headers it ++ points to. If not null, *LOADBASEP is filled in with the difference ++ between the VMAs from which the segments were read, and the VMAs the ++ file headers (and hence BFD's idea of each section's VMA) put them at. ++ ++ The function TARGET_READ_MEMORY is called to copy LEN bytes from the ++ remote memory at target address VMA into the local buffer at MYADDR; it ++ should return zero on success or an `errno' code on failure. TEMPL must ++ be a BFD for an ELF target with the word size and byte order found in ++ the remote memory. */ ++extern bfd *bfd_elf_bfd_from_remote_memory ++ (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep, ++ int (*target_read_memory) (bfd_vma vma, bfd_byte *myaddr, int len)); ++ ++/* Return the arch_size field of an elf bfd, or -1 if not elf. */ ++extern int bfd_get_arch_size ++ (bfd *); ++ ++/* Return TRUE if address "naturally" sign extends, or -1 if not elf. */ ++extern int bfd_get_sign_extend_vma ++ (bfd *); ++ ++extern struct bfd_section *_bfd_elf_tls_setup ++ (bfd *, struct bfd_link_info *); ++ ++extern void _bfd_elf_provide_symbol ++ (struct bfd_link_info *, const char *, bfd_vma, struct bfd_section *); ++ ++extern void _bfd_elf_provide_section_bound_symbols ++ (struct bfd_link_info *, struct bfd_section *, const char *, const char *); ++ ++extern void _bfd_elf_fix_excluded_sec_syms ++ (bfd *, struct bfd_link_info *); ++ ++extern bfd_boolean bfd_m68k_elf32_create_embedded_relocs ++ (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, ++ char **); ++ ++/* SunOS shared library support routines for the linker. */ ++ ++extern struct bfd_link_needed_list *bfd_sunos_get_needed_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_sunos_record_link_assignment ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_sunos_size_dynamic_sections ++ (bfd *, struct bfd_link_info *, struct bfd_section **, ++ struct bfd_section **, struct bfd_section **); ++ ++/* Linux shared library support routines for the linker. */ ++ ++extern bfd_boolean bfd_i386linux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_m68klinux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_sparclinux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++ ++/* mmap hacks */ ++ ++struct _bfd_window_internal; ++typedef struct _bfd_window_internal bfd_window_internal; ++ ++typedef struct _bfd_window ++{ ++ /* What the user asked for. */ ++ void *data; ++ bfd_size_type size; ++ /* The actual window used by BFD. Small user-requested read-only ++ regions sharing a page may share a single window into the object ++ file. Read-write versions shouldn't until I've fixed things to ++ keep track of which portions have been claimed by the ++ application; don't want to give the same region back when the ++ application wants two writable copies! */ ++ struct _bfd_window_internal *i; ++} ++bfd_window; ++ ++extern void bfd_init_window ++ (bfd_window *); ++extern void bfd_free_window ++ (bfd_window *); ++extern bfd_boolean bfd_get_file_window ++ (bfd *, file_ptr, bfd_size_type, bfd_window *, bfd_boolean); ++ ++/* XCOFF support routines for the linker. */ ++ ++extern bfd_boolean bfd_xcoff_link_record_set ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *, bfd_size_type); ++extern bfd_boolean bfd_xcoff_import_symbol ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *, bfd_vma, ++ const char *, const char *, const char *, unsigned int); ++extern bfd_boolean bfd_xcoff_export_symbol ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *); ++extern bfd_boolean bfd_xcoff_link_count_reloc ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_xcoff_record_link_assignment ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_xcoff_size_dynamic_sections ++ (bfd *, struct bfd_link_info *, const char *, const char *, ++ unsigned long, unsigned long, unsigned long, bfd_boolean, ++ int, bfd_boolean, bfd_boolean, struct bfd_section **, bfd_boolean); ++extern bfd_boolean bfd_xcoff_link_generate_rtinit ++ (bfd *, const char *, const char *, bfd_boolean); ++ ++/* XCOFF support routines for ar. */ ++extern bfd_boolean bfd_xcoff_ar_archive_set_magic ++ (bfd *, char *); ++ ++/* Externally visible COFF routines. */ ++ ++#if defined(__STDC__) || defined(ALMOST_STDC) ++struct internal_syment; ++union internal_auxent; ++#endif ++ ++extern bfd_boolean bfd_coff_get_syment ++ (bfd *, struct bfd_symbol *, struct internal_syment *); ++ ++extern bfd_boolean bfd_coff_get_auxent ++ (bfd *, struct bfd_symbol *, int, union internal_auxent *); ++ ++extern bfd_boolean bfd_coff_set_symbol_class ++ (bfd *, struct bfd_symbol *, unsigned int); ++ ++extern bfd_boolean bfd_m68k_coff_create_embedded_relocs ++ (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, char **); ++ ++/* ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_arm_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_arm_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++extern bfd_boolean bfd_arm_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++/* PE ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_arm_pe_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_arm_pe_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++extern bfd_boolean bfd_arm_pe_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++/* ELF ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_elf32_arm_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_elf32_arm_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++void bfd_elf32_arm_set_target_relocs ++ (struct bfd_link_info *, int, char *, int, int); ++ ++extern bfd_boolean bfd_elf32_arm_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++extern bfd_boolean bfd_elf32_arm_add_glue_sections_to_bfd ++ (bfd *, struct bfd_link_info *); ++ ++/* ELF ARM mapping symbol support */ ++extern bfd_boolean bfd_is_arm_mapping_symbol_name ++ (const char * name); ++ ++/* ARM Note section processing. */ ++extern bfd_boolean bfd_arm_merge_machines ++ (bfd *, bfd *); ++ ++extern bfd_boolean bfd_arm_update_notes ++ (bfd *, const char *); ++ ++extern unsigned int bfd_arm_get_mach_from_notes ++ (bfd *, const char *); ++ ++/* TI COFF load page support. */ ++extern void bfd_ticoff_set_section_load_page ++ (struct bfd_section *, int); ++ ++extern int bfd_ticoff_get_section_load_page ++ (struct bfd_section *); ++ ++/* H8/300 functions. */ ++extern bfd_vma bfd_h8300_pad_address ++ (bfd *, bfd_vma); ++ ++/* IA64 Itanium code generation. Called from linker. */ ++extern void bfd_elf32_ia64_after_parse ++ (int); ++ ++extern void bfd_elf64_ia64_after_parse ++ (int); ++ ++/* This structure is used for a comdat section, as in PE. A comdat ++ section is associated with a particular symbol. When the linker ++ sees a comdat section, it keeps only one of the sections with a ++ given name and associated with a given symbol. */ ++ ++struct coff_comdat_info ++{ ++ /* The name of the symbol associated with a comdat section. */ ++ const char *name; ++ ++ /* The local symbol table index of the symbol associated with a ++ comdat section. This is only meaningful to the object file format ++ specific code; it is not an index into the list returned by ++ bfd_canonicalize_symtab. */ ++ long symbol; ++}; ++ ++extern struct coff_comdat_info *bfd_coff_get_comdat_section ++ (bfd *, struct bfd_section *); ++ ++/* Extracted from init.c. */ ++void bfd_init (void); ++ ++/* Extracted from opncls.c. */ ++bfd *bfd_fopen (const char *filename, const char *target, ++ const char *mode, int fd); ++ ++bfd *bfd_openr (const char *filename, const char *target); ++ ++bfd *bfd_fdopenr (const char *filename, const char *target, int fd); ++ ++bfd *bfd_openstreamr (const char *, const char *, void *); ++ ++bfd *bfd_openr_iovec (const char *filename, const char *target, ++ void *(*open) (struct bfd *nbfd, ++ void *open_closure), ++ void *open_closure, ++ file_ptr (*pread) (struct bfd *nbfd, ++ void *stream, ++ void *buf, ++ file_ptr nbytes, ++ file_ptr offset), ++ int (*close) (struct bfd *nbfd, ++ void *stream)); ++ ++bfd *bfd_openw (const char *filename, const char *target); ++ ++bfd_boolean bfd_close (bfd *abfd); ++ ++bfd_boolean bfd_close_all_done (bfd *); ++ ++bfd *bfd_create (const char *filename, bfd *templ); ++ ++bfd_boolean bfd_make_writable (bfd *abfd); ++ ++bfd_boolean bfd_make_readable (bfd *abfd); ++ ++unsigned long bfd_calc_gnu_debuglink_crc32 ++ (unsigned long crc, const unsigned char *buf, bfd_size_type len); ++ ++char *bfd_follow_gnu_debuglink (bfd *abfd, const char *dir); ++ ++struct bfd_section *bfd_create_gnu_debuglink_section ++ (bfd *abfd, const char *filename); ++ ++bfd_boolean bfd_fill_in_gnu_debuglink_section ++ (bfd *abfd, struct bfd_section *sect, const char *filename); ++ ++/* Extracted from libbfd.c. */ ++ ++/* Byte swapping macros for user section data. */ ++ ++#define bfd_put_8(abfd, val, ptr) \ ++ ((void) (*((unsigned char *) (ptr)) = (val) & 0xff)) ++#define bfd_put_signed_8 \ ++ bfd_put_8 ++#define bfd_get_8(abfd, ptr) \ ++ (*(unsigned char *) (ptr) & 0xff) ++#define bfd_get_signed_8(abfd, ptr) \ ++ (((*(unsigned char *) (ptr) & 0xff) ^ 0x80) - 0x80) ++ ++#define bfd_put_16(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx16, ((val),(ptr))) ++#define bfd_put_signed_16 \ ++ bfd_put_16 ++#define bfd_get_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx16, (ptr)) ++#define bfd_get_signed_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_16, (ptr)) ++ ++#define bfd_put_32(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx32, ((val),(ptr))) ++#define bfd_put_signed_32 \ ++ bfd_put_32 ++#define bfd_get_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx32, (ptr)) ++#define bfd_get_signed_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_32, (ptr)) ++ ++#define bfd_put_64(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx64, ((val), (ptr))) ++#define bfd_put_signed_64 \ ++ bfd_put_64 ++#define bfd_get_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx64, (ptr)) ++#define bfd_get_signed_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_64, (ptr)) ++ ++#define bfd_get(bits, abfd, ptr) \ ++ ((bits) == 8 ? (bfd_vma) bfd_get_8 (abfd, ptr) \ ++ : (bits) == 16 ? bfd_get_16 (abfd, ptr) \ ++ : (bits) == 32 ? bfd_get_32 (abfd, ptr) \ ++ : (bits) == 64 ? bfd_get_64 (abfd, ptr) \ ++ : (abort (), (bfd_vma) - 1)) ++ ++#define bfd_put(bits, abfd, val, ptr) \ ++ ((bits) == 8 ? bfd_put_8 (abfd, val, ptr) \ ++ : (bits) == 16 ? bfd_put_16 (abfd, val, ptr) \ ++ : (bits) == 32 ? bfd_put_32 (abfd, val, ptr) \ ++ : (bits) == 64 ? bfd_put_64 (abfd, val, ptr) \ ++ : (abort (), (void) 0)) ++ ++ ++/* Byte swapping macros for file header data. */ ++ ++#define bfd_h_put_8(abfd, val, ptr) \ ++ bfd_put_8 (abfd, val, ptr) ++#define bfd_h_put_signed_8(abfd, val, ptr) \ ++ bfd_put_8 (abfd, val, ptr) ++#define bfd_h_get_8(abfd, ptr) \ ++ bfd_get_8 (abfd, ptr) ++#define bfd_h_get_signed_8(abfd, ptr) \ ++ bfd_get_signed_8 (abfd, ptr) ++ ++#define bfd_h_put_16(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx16, (val, ptr)) ++#define bfd_h_put_signed_16 \ ++ bfd_h_put_16 ++#define bfd_h_get_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx16, (ptr)) ++#define bfd_h_get_signed_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_16, (ptr)) ++ ++#define bfd_h_put_32(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx32, (val, ptr)) ++#define bfd_h_put_signed_32 \ ++ bfd_h_put_32 ++#define bfd_h_get_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx32, (ptr)) ++#define bfd_h_get_signed_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_32, (ptr)) ++ ++#define bfd_h_put_64(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx64, (val, ptr)) ++#define bfd_h_put_signed_64 \ ++ bfd_h_put_64 ++#define bfd_h_get_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx64, (ptr)) ++#define bfd_h_get_signed_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_64, (ptr)) ++ ++/* Aliases for the above, which should eventually go away. */ ++ ++#define H_PUT_64 bfd_h_put_64 ++#define H_PUT_32 bfd_h_put_32 ++#define H_PUT_16 bfd_h_put_16 ++#define H_PUT_8 bfd_h_put_8 ++#define H_PUT_S64 bfd_h_put_signed_64 ++#define H_PUT_S32 bfd_h_put_signed_32 ++#define H_PUT_S16 bfd_h_put_signed_16 ++#define H_PUT_S8 bfd_h_put_signed_8 ++#define H_GET_64 bfd_h_get_64 ++#define H_GET_32 bfd_h_get_32 ++#define H_GET_16 bfd_h_get_16 ++#define H_GET_8 bfd_h_get_8 ++#define H_GET_S64 bfd_h_get_signed_64 ++#define H_GET_S32 bfd_h_get_signed_32 ++#define H_GET_S16 bfd_h_get_signed_16 ++#define H_GET_S8 bfd_h_get_signed_8 ++ ++ ++/* Extracted from bfdio.c. */ ++long bfd_get_mtime (bfd *abfd); ++ ++long bfd_get_size (bfd *abfd); ++ ++/* Extracted from bfdwin.c. */ ++/* Extracted from section.c. */ ++typedef struct bfd_section ++{ ++ /* The name of the section; the name isn't a copy, the pointer is ++ the same as that passed to bfd_make_section. */ ++ const char *name; ++ ++ /* A unique sequence number. */ ++ int id; ++ ++ /* Which section in the bfd; 0..n-1 as sections are created in a bfd. */ ++ int index; ++ ++ /* The next section in the list belonging to the BFD, or NULL. */ ++ struct bfd_section *next; ++ ++ /* The previous section in the list belonging to the BFD, or NULL. */ ++ struct bfd_section *prev; ++ ++ /* The field flags contains attributes of the section. Some ++ flags are read in from the object file, and some are ++ synthesized from other information. */ ++ flagword flags; ++ ++#define SEC_NO_FLAGS 0x000 ++ ++ /* Tells the OS to allocate space for this section when loading. ++ This is clear for a section containing debug information only. */ ++#define SEC_ALLOC 0x001 ++ ++ /* Tells the OS to load the section from the file when loading. ++ This is clear for a .bss section. */ ++#define SEC_LOAD 0x002 ++ ++ /* The section contains data still to be relocated, so there is ++ some relocation information too. */ ++#define SEC_RELOC 0x004 ++ ++ /* A signal to the OS that the section contains read only data. */ ++#define SEC_READONLY 0x008 ++ ++ /* The section contains code only. */ ++#define SEC_CODE 0x010 ++ ++ /* The section contains data only. */ ++#define SEC_DATA 0x020 ++ ++ /* The section will reside in ROM. */ ++#define SEC_ROM 0x040 ++ ++ /* The section contains constructor information. This section ++ type is used by the linker to create lists of constructors and ++ destructors used by <>. When a back end sees a symbol ++ which should be used in a constructor list, it creates a new ++ section for the type of name (e.g., <<__CTOR_LIST__>>), attaches ++ the symbol to it, and builds a relocation. To build the lists ++ of constructors, all the linker has to do is catenate all the ++ sections called <<__CTOR_LIST__>> and relocate the data ++ contained within - exactly the operations it would peform on ++ standard data. */ ++#define SEC_CONSTRUCTOR 0x080 ++ ++ /* The section has contents - a data section could be ++ <> | <>; a debug section could be ++ <> */ ++#define SEC_HAS_CONTENTS 0x100 ++ ++ /* An instruction to the linker to not output the section ++ even if it has information which would normally be written. */ ++#define SEC_NEVER_LOAD 0x200 ++ ++ /* The section contains thread local data. */ ++#define SEC_THREAD_LOCAL 0x400 ++ ++ /* The section has GOT references. This flag is only for the ++ linker, and is currently only used by the elf32-hppa back end. ++ It will be set if global offset table references were detected ++ in this section, which indicate to the linker that the section ++ contains PIC code, and must be handled specially when doing a ++ static link. */ ++#define SEC_HAS_GOT_REF 0x800 ++ ++ /* The section contains common symbols (symbols may be defined ++ multiple times, the value of a symbol is the amount of ++ space it requires, and the largest symbol value is the one ++ used). Most targets have exactly one of these (which we ++ translate to bfd_com_section_ptr), but ECOFF has two. */ ++#define SEC_IS_COMMON 0x1000 ++ ++ /* The section contains only debugging information. For ++ example, this is set for ELF .debug and .stab sections. ++ strip tests this flag to see if a section can be ++ discarded. */ ++#define SEC_DEBUGGING 0x2000 ++ ++ /* The contents of this section are held in memory pointed to ++ by the contents field. This is checked by bfd_get_section_contents, ++ and the data is retrieved from memory if appropriate. */ ++#define SEC_IN_MEMORY 0x4000 ++ ++ /* The contents of this section are to be excluded by the ++ linker for executable and shared objects unless those ++ objects are to be further relocated. */ ++#define SEC_EXCLUDE 0x8000 ++ ++ /* The contents of this section are to be sorted based on the sum of ++ the symbol and addend values specified by the associated relocation ++ entries. Entries without associated relocation entries will be ++ appended to the end of the section in an unspecified order. */ ++#define SEC_SORT_ENTRIES 0x10000 ++ ++ /* When linking, duplicate sections of the same name should be ++ discarded, rather than being combined into a single section as ++ is usually done. This is similar to how common symbols are ++ handled. See SEC_LINK_DUPLICATES below. */ ++#define SEC_LINK_ONCE 0x20000 ++ ++ /* If SEC_LINK_ONCE is set, this bitfield describes how the linker ++ should handle duplicate sections. */ ++#define SEC_LINK_DUPLICATES 0x40000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that duplicate ++ sections with the same name should simply be discarded. */ ++#define SEC_LINK_DUPLICATES_DISCARD 0x0 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if there are any duplicate sections, although ++ it should still only link one copy. */ ++#define SEC_LINK_DUPLICATES_ONE_ONLY 0x80000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if any duplicate sections are a different size. */ ++#define SEC_LINK_DUPLICATES_SAME_SIZE 0x100000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if any duplicate sections contain different ++ contents. */ ++#define SEC_LINK_DUPLICATES_SAME_CONTENTS \ ++ (SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE) ++ ++ /* This section was created by the linker as part of dynamic ++ relocation or other arcane processing. It is skipped when ++ going through the first-pass output, trusting that someone ++ else up the line will take care of it later. */ ++#define SEC_LINKER_CREATED 0x200000 ++ ++ /* This section should not be subject to garbage collection. */ ++#define SEC_KEEP 0x400000 ++ ++ /* This section contains "short" data, and should be placed ++ "near" the GP. */ ++#define SEC_SMALL_DATA 0x800000 ++ ++ /* Attempt to merge identical entities in the section. ++ Entity size is given in the entsize field. */ ++#define SEC_MERGE 0x1000000 ++ ++ /* If given with SEC_MERGE, entities to merge are zero terminated ++ strings where entsize specifies character size instead of fixed ++ size entries. */ ++#define SEC_STRINGS 0x2000000 ++ ++ /* This section contains data about section groups. */ ++#define SEC_GROUP 0x4000000 ++ ++ /* The section is a COFF shared library section. This flag is ++ only for the linker. If this type of section appears in ++ the input file, the linker must copy it to the output file ++ without changing the vma or size. FIXME: Although this ++ was originally intended to be general, it really is COFF ++ specific (and the flag was renamed to indicate this). It ++ might be cleaner to have some more general mechanism to ++ allow the back end to control what the linker does with ++ sections. */ ++#define SEC_COFF_SHARED_LIBRARY 0x10000000 ++ ++ /* This section contains data which may be shared with other ++ executables or shared objects. This is for COFF only. */ ++#define SEC_COFF_SHARED 0x20000000 ++ ++ /* When a section with this flag is being linked, then if the size of ++ the input section is less than a page, it should not cross a page ++ boundary. If the size of the input section is one page or more, ++ it should be aligned on a page boundary. This is for TI ++ TMS320C54X only. */ ++#define SEC_TIC54X_BLOCK 0x40000000 ++ ++ /* Conditionally link this section; do not link if there are no ++ references found to any symbol in the section. This is for TI ++ TMS320C54X only. */ ++#define SEC_TIC54X_CLINK 0x80000000 ++ ++ /* End of section flags. */ ++ ++ /* Some internal packed boolean fields. */ ++ ++ /* See the vma field. */ ++ unsigned int user_set_vma : 1; ++ ++ /* A mark flag used by some of the linker backends. */ ++ unsigned int linker_mark : 1; ++ ++ /* Another mark flag used by some of the linker backends. Set for ++ output sections that have an input section. */ ++ unsigned int linker_has_input : 1; ++ ++ /* Mark flags used by some linker backends for garbage collection. */ ++ unsigned int gc_mark : 1; ++ unsigned int gc_mark_from_eh : 1; ++ ++ /* The following flags are used by the ELF linker. */ ++ ++ /* Mark sections which have been allocated to segments. */ ++ unsigned int segment_mark : 1; ++ ++ /* Type of sec_info information. */ ++ unsigned int sec_info_type:3; ++#define ELF_INFO_TYPE_NONE 0 ++#define ELF_INFO_TYPE_STABS 1 ++#define ELF_INFO_TYPE_MERGE 2 ++#define ELF_INFO_TYPE_EH_FRAME 3 ++#define ELF_INFO_TYPE_JUST_SYMS 4 ++ ++ /* Nonzero if this section uses RELA relocations, rather than REL. */ ++ unsigned int use_rela_p:1; ++ ++ /* Bits used by various backends. The generic code doesn't touch ++ these fields. */ ++ ++ /* Nonzero if this section has TLS related relocations. */ ++ unsigned int has_tls_reloc:1; ++ ++ /* Nonzero if this section has a gp reloc. */ ++ unsigned int has_gp_reloc:1; ++ ++ /* Nonzero if this section needs the relax finalize pass. */ ++ unsigned int need_finalize_relax:1; ++ ++ /* Whether relocations have been processed. */ ++ unsigned int reloc_done : 1; ++ ++ /* End of internal packed boolean fields. */ ++ ++ /* The virtual memory address of the section - where it will be ++ at run time. The symbols are relocated against this. The ++ user_set_vma flag is maintained by bfd; if it's not set, the ++ backend can assign addresses (for example, in <>, where ++ the default address for <<.data>> is dependent on the specific ++ target and various flags). */ ++ bfd_vma vma; ++ ++ /* The load address of the section - where it would be in a ++ rom image; really only used for writing section header ++ information. */ ++ bfd_vma lma; ++ ++ /* The size of the section in octets, as it will be output. ++ Contains a value even if the section has no contents (e.g., the ++ size of <<.bss>>). */ ++ bfd_size_type size; ++ ++ /* For input sections, the original size on disk of the section, in ++ octets. This field is used by the linker relaxation code. It is ++ currently only set for sections where the linker relaxation scheme ++ doesn't cache altered section and reloc contents (stabs, eh_frame, ++ SEC_MERGE, some coff relaxing targets), and thus the original size ++ needs to be kept to read the section multiple times. ++ For output sections, rawsize holds the section size calculated on ++ a previous linker relaxation pass. */ ++ bfd_size_type rawsize; ++ ++ /* If this section is going to be output, then this value is the ++ offset in *bytes* into the output section of the first byte in the ++ input section (byte ==> smallest addressable unit on the ++ target). In most cases, if this was going to start at the ++ 100th octet (8-bit quantity) in the output section, this value ++ would be 100. However, if the target byte size is 16 bits ++ (bfd_octets_per_byte is "2"), this value would be 50. */ ++ bfd_vma output_offset; ++ ++ /* The output section through which to map on output. */ ++ struct bfd_section *output_section; ++ ++ /* The alignment requirement of the section, as an exponent of 2 - ++ e.g., 3 aligns to 2^3 (or 8). */ ++ unsigned int alignment_power; ++ ++ /* If an input section, a pointer to a vector of relocation ++ records for the data in this section. */ ++ struct reloc_cache_entry *relocation; ++ ++ /* If an output section, a pointer to a vector of pointers to ++ relocation records for the data in this section. */ ++ struct reloc_cache_entry **orelocation; ++ ++ /* The number of relocation records in one of the above. */ ++ unsigned reloc_count; ++ ++ /* Information below is back end specific - and not always used ++ or updated. */ ++ ++ /* File position of section data. */ ++ file_ptr filepos; ++ ++ /* File position of relocation info. */ ++ file_ptr rel_filepos; ++ ++ /* File position of line data. */ ++ file_ptr line_filepos; ++ ++ /* Pointer to data for applications. */ ++ void *userdata; ++ ++ /* If the SEC_IN_MEMORY flag is set, this points to the actual ++ contents. */ ++ unsigned char *contents; ++ ++ /* Attached line number information. */ ++ alent *lineno; ++ ++ /* Number of line number records. */ ++ unsigned int lineno_count; ++ ++ /* Entity size for merging purposes. */ ++ unsigned int entsize; ++ ++ /* Points to the kept section if this section is a link-once section, ++ and is discarded. */ ++ struct bfd_section *kept_section; ++ ++ /* When a section is being output, this value changes as more ++ linenumbers are written out. */ ++ file_ptr moving_line_filepos; ++ ++ /* What the section number is in the target world. */ ++ int target_index; ++ ++ void *used_by_bfd; ++ ++ /* If this is a constructor section then here is a list of the ++ relocations created to relocate items within it. */ ++ struct relent_chain *constructor_chain; ++ ++ /* The BFD which owns the section. */ ++ bfd *owner; ++ ++ /* A symbol which points at this section only. */ ++ struct bfd_symbol *symbol; ++ struct bfd_symbol **symbol_ptr_ptr; ++ ++ /* Early in the link process, map_head and map_tail are used to build ++ a list of input sections attached to an output section. Later, ++ output sections use these fields for a list of bfd_link_order ++ structs. */ ++ union { ++ struct bfd_link_order *link_order; ++ struct bfd_section *s; ++ } map_head, map_tail; ++} asection; ++ ++/* These sections are global, and are managed by BFD. The application ++ and target back end are not permitted to change the values in ++ these sections. New code should use the section_ptr macros rather ++ than referring directly to the const sections. The const sections ++ may eventually vanish. */ ++#define BFD_ABS_SECTION_NAME "*ABS*" ++#define BFD_UND_SECTION_NAME "*UND*" ++#define BFD_COM_SECTION_NAME "*COM*" ++#define BFD_IND_SECTION_NAME "*IND*" ++ ++/* The absolute section. */ ++extern asection bfd_abs_section; ++#define bfd_abs_section_ptr ((asection *) &bfd_abs_section) ++#define bfd_is_abs_section(sec) ((sec) == bfd_abs_section_ptr) ++/* Pointer to the undefined section. */ ++extern asection bfd_und_section; ++#define bfd_und_section_ptr ((asection *) &bfd_und_section) ++#define bfd_is_und_section(sec) ((sec) == bfd_und_section_ptr) ++/* Pointer to the common section. */ ++extern asection bfd_com_section; ++#define bfd_com_section_ptr ((asection *) &bfd_com_section) ++/* Pointer to the indirect section. */ ++extern asection bfd_ind_section; ++#define bfd_ind_section_ptr ((asection *) &bfd_ind_section) ++#define bfd_is_ind_section(sec) ((sec) == bfd_ind_section_ptr) ++ ++#define bfd_is_const_section(SEC) \ ++ ( ((SEC) == bfd_abs_section_ptr) \ ++ || ((SEC) == bfd_und_section_ptr) \ ++ || ((SEC) == bfd_com_section_ptr) \ ++ || ((SEC) == bfd_ind_section_ptr)) ++ ++extern const struct bfd_symbol * const bfd_abs_symbol; ++extern const struct bfd_symbol * const bfd_com_symbol; ++extern const struct bfd_symbol * const bfd_und_symbol; ++extern const struct bfd_symbol * const bfd_ind_symbol; ++ ++/* Macros to handle insertion and deletion of a bfd's sections. These ++ only handle the list pointers, ie. do not adjust section_count, ++ target_index etc. */ ++#define bfd_section_list_remove(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ asection *_next = _s->next; \ ++ asection *_prev = _s->prev; \ ++ if (_prev) \ ++ _prev->next = _next; \ ++ else \ ++ (ABFD)->sections = _next; \ ++ if (_next) \ ++ _next->prev = _prev; \ ++ else \ ++ (ABFD)->section_last = _prev; \ ++ } \ ++ while (0) ++#define bfd_section_list_append(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ bfd *_abfd = ABFD; \ ++ _s->next = NULL; \ ++ if (_abfd->section_last) \ ++ { \ ++ _s->prev = _abfd->section_last; \ ++ _abfd->section_last->next = _s; \ ++ } \ ++ else \ ++ { \ ++ _s->prev = NULL; \ ++ _abfd->sections = _s; \ ++ } \ ++ _abfd->section_last = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_prepend(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ bfd *_abfd = ABFD; \ ++ _s->prev = NULL; \ ++ if (_abfd->sections) \ ++ { \ ++ _s->next = _abfd->sections; \ ++ _abfd->sections->prev = _s; \ ++ } \ ++ else \ ++ { \ ++ _s->next = NULL; \ ++ _abfd->section_last = _s; \ ++ } \ ++ _abfd->sections = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_insert_after(ABFD, A, S) \ ++ do \ ++ { \ ++ asection *_a = A; \ ++ asection *_s = S; \ ++ asection *_next = _a->next; \ ++ _s->next = _next; \ ++ _s->prev = _a; \ ++ _a->next = _s; \ ++ if (_next) \ ++ _next->prev = _s; \ ++ else \ ++ (ABFD)->section_last = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_insert_before(ABFD, B, S) \ ++ do \ ++ { \ ++ asection *_b = B; \ ++ asection *_s = S; \ ++ asection *_prev = _b->prev; \ ++ _s->prev = _prev; \ ++ _s->next = _b; \ ++ _b->prev = _s; \ ++ if (_prev) \ ++ _prev->next = _s; \ ++ else \ ++ (ABFD)->sections = _s; \ ++ } \ ++ while (0) ++#define bfd_section_removed_from_list(ABFD, S) \ ++ ((S)->next == NULL ? (ABFD)->section_last != (S) : (S)->next->prev != (S)) ++ ++void bfd_section_list_clear (bfd *); ++ ++asection *bfd_get_section_by_name (bfd *abfd, const char *name); ++ ++asection *bfd_get_section_by_name_if ++ (bfd *abfd, ++ const char *name, ++ bfd_boolean (*func) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++char *bfd_get_unique_section_name ++ (bfd *abfd, const char *templat, int *count); ++ ++asection *bfd_make_section_old_way (bfd *abfd, const char *name); ++ ++asection *bfd_make_section_anyway_with_flags ++ (bfd *abfd, const char *name, flagword flags); ++ ++asection *bfd_make_section_anyway (bfd *abfd, const char *name); ++ ++asection *bfd_make_section_with_flags ++ (bfd *, const char *name, flagword flags); ++ ++asection *bfd_make_section (bfd *, const char *name); ++ ++bfd_boolean bfd_set_section_flags ++ (bfd *abfd, asection *sec, flagword flags); ++ ++void bfd_map_over_sections ++ (bfd *abfd, ++ void (*func) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++asection *bfd_sections_find_if ++ (bfd *abfd, ++ bfd_boolean (*operation) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++bfd_boolean bfd_set_section_size ++ (bfd *abfd, asection *sec, bfd_size_type val); ++ ++bfd_boolean bfd_set_section_contents ++ (bfd *abfd, asection *section, const void *data, ++ file_ptr offset, bfd_size_type count); ++ ++bfd_boolean bfd_get_section_contents ++ (bfd *abfd, asection *section, void *location, file_ptr offset, ++ bfd_size_type count); ++ ++bfd_boolean bfd_malloc_and_get_section ++ (bfd *abfd, asection *section, bfd_byte **buf); ++ ++bfd_boolean bfd_copy_private_section_data ++ (bfd *ibfd, asection *isec, bfd *obfd, asection *osec); ++ ++#define bfd_copy_private_section_data(ibfd, isection, obfd, osection) \ ++ BFD_SEND (obfd, _bfd_copy_private_section_data, \ ++ (ibfd, isection, obfd, osection)) ++bfd_boolean bfd_generic_is_group_section (bfd *, const asection *sec); ++ ++bfd_boolean bfd_generic_discard_group (bfd *abfd, asection *group); ++ ++/* Extracted from archures.c. */ ++enum bfd_architecture ++{ ++ bfd_arch_unknown, /* File arch not known. */ ++ bfd_arch_obscure, /* Arch known, not one of these. */ ++ bfd_arch_m68k, /* Motorola 68xxx */ ++#define bfd_mach_m68000 1 ++#define bfd_mach_m68008 2 ++#define bfd_mach_m68010 3 ++#define bfd_mach_m68020 4 ++#define bfd_mach_m68030 5 ++#define bfd_mach_m68040 6 ++#define bfd_mach_m68060 7 ++#define bfd_mach_cpu32 8 ++#define bfd_mach_mcf5200 9 ++#define bfd_mach_mcf5206e 10 ++#define bfd_mach_mcf5307 11 ++#define bfd_mach_mcf5407 12 ++#define bfd_mach_mcf528x 13 ++#define bfd_mach_mcfv4e 14 ++#define bfd_mach_mcf521x 15 ++#define bfd_mach_mcf5249 16 ++#define bfd_mach_mcf547x 17 ++#define bfd_mach_mcf548x 18 ++ bfd_arch_vax, /* DEC Vax */ ++ bfd_arch_i960, /* Intel 960 */ ++ /* The order of the following is important. ++ lower number indicates a machine type that ++ only accepts a subset of the instructions ++ available to machines with higher numbers. ++ The exception is the "ca", which is ++ incompatible with all other machines except ++ "core". */ ++ ++#define bfd_mach_i960_core 1 ++#define bfd_mach_i960_ka_sa 2 ++#define bfd_mach_i960_kb_sb 3 ++#define bfd_mach_i960_mc 4 ++#define bfd_mach_i960_xa 5 ++#define bfd_mach_i960_ca 6 ++#define bfd_mach_i960_jx 7 ++#define bfd_mach_i960_hx 8 ++ ++ bfd_arch_or32, /* OpenRISC 32 */ ++ ++ bfd_arch_a29k, /* AMD 29000 */ ++ bfd_arch_sparc, /* SPARC */ ++#define bfd_mach_sparc 1 ++/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */ ++#define bfd_mach_sparc_sparclet 2 ++#define bfd_mach_sparc_sparclite 3 ++#define bfd_mach_sparc_v8plus 4 ++#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */ ++#define bfd_mach_sparc_sparclite_le 6 ++#define bfd_mach_sparc_v9 7 ++#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */ ++#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */ ++#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */ ++/* Nonzero if MACH has the v9 instruction set. */ ++#define bfd_mach_sparc_v9_p(mach) \ ++ ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \ ++ && (mach) != bfd_mach_sparc_sparclite_le) ++/* Nonzero if MACH is a 64 bit sparc architecture. */ ++#define bfd_mach_sparc_64bit_p(mach) \ ++ ((mach) >= bfd_mach_sparc_v9 && (mach) != bfd_mach_sparc_v8plusb) ++ bfd_arch_mips, /* MIPS Rxxxx */ ++#define bfd_mach_mips3000 3000 ++#define bfd_mach_mips3900 3900 ++#define bfd_mach_mips4000 4000 ++#define bfd_mach_mips4010 4010 ++#define bfd_mach_mips4100 4100 ++#define bfd_mach_mips4111 4111 ++#define bfd_mach_mips4120 4120 ++#define bfd_mach_mips4300 4300 ++#define bfd_mach_mips4400 4400 ++#define bfd_mach_mips4600 4600 ++#define bfd_mach_mips4650 4650 ++#define bfd_mach_mips5000 5000 ++#define bfd_mach_mips5400 5400 ++#define bfd_mach_mips5500 5500 ++#define bfd_mach_mips6000 6000 ++#define bfd_mach_mips7000 7000 ++#define bfd_mach_mips8000 8000 ++#define bfd_mach_mips9000 9000 ++#define bfd_mach_mips10000 10000 ++#define bfd_mach_mips12000 12000 ++#define bfd_mach_mips16 16 ++#define bfd_mach_mips5 5 ++#define bfd_mach_mips_sb1 12310201 /* octal 'SB', 01 */ ++#define bfd_mach_mipsisa32 32 ++#define bfd_mach_mipsisa32r2 33 ++#define bfd_mach_mipsisa64 64 ++#define bfd_mach_mipsisa64r2 65 ++ bfd_arch_i386, /* Intel 386 */ ++#define bfd_mach_i386_i386 1 ++#define bfd_mach_i386_i8086 2 ++#define bfd_mach_i386_i386_intel_syntax 3 ++#define bfd_mach_x86_64 64 ++#define bfd_mach_x86_64_intel_syntax 65 ++ bfd_arch_we32k, /* AT&T WE32xxx */ ++ bfd_arch_tahoe, /* CCI/Harris Tahoe */ ++ bfd_arch_i860, /* Intel 860 */ ++ bfd_arch_i370, /* IBM 360/370 Mainframes */ ++ bfd_arch_romp, /* IBM ROMP PC/RT */ ++ bfd_arch_alliant, /* Alliant */ ++ bfd_arch_convex, /* Convex */ ++ bfd_arch_m88k, /* Motorola 88xxx */ ++ bfd_arch_m98k, /* Motorola 98xxx */ ++ bfd_arch_pyramid, /* Pyramid Technology */ ++ bfd_arch_h8300, /* Renesas H8/300 (formerly Hitachi H8/300) */ ++#define bfd_mach_h8300 1 ++#define bfd_mach_h8300h 2 ++#define bfd_mach_h8300s 3 ++#define bfd_mach_h8300hn 4 ++#define bfd_mach_h8300sn 5 ++#define bfd_mach_h8300sx 6 ++#define bfd_mach_h8300sxn 7 ++ bfd_arch_pdp11, /* DEC PDP-11 */ ++ bfd_arch_powerpc, /* PowerPC */ ++#define bfd_mach_ppc 32 ++#define bfd_mach_ppc64 64 ++#define bfd_mach_ppc_403 403 ++#define bfd_mach_ppc_403gc 4030 ++#define bfd_mach_ppc_505 505 ++#define bfd_mach_ppc_601 601 ++#define bfd_mach_ppc_602 602 ++#define bfd_mach_ppc_603 603 ++#define bfd_mach_ppc_ec603e 6031 ++#define bfd_mach_ppc_604 604 ++#define bfd_mach_ppc_620 620 ++#define bfd_mach_ppc_630 630 ++#define bfd_mach_ppc_750 750 ++#define bfd_mach_ppc_860 860 ++#define bfd_mach_ppc_a35 35 ++#define bfd_mach_ppc_rs64ii 642 ++#define bfd_mach_ppc_rs64iii 643 ++#define bfd_mach_ppc_7400 7400 ++#define bfd_mach_ppc_e500 500 ++ bfd_arch_rs6000, /* IBM RS/6000 */ ++#define bfd_mach_rs6k 6000 ++#define bfd_mach_rs6k_rs1 6001 ++#define bfd_mach_rs6k_rsc 6003 ++#define bfd_mach_rs6k_rs2 6002 ++ bfd_arch_hppa, /* HP PA RISC */ ++#define bfd_mach_hppa10 10 ++#define bfd_mach_hppa11 11 ++#define bfd_mach_hppa20 20 ++#define bfd_mach_hppa20w 25 ++ bfd_arch_d10v, /* Mitsubishi D10V */ ++#define bfd_mach_d10v 1 ++#define bfd_mach_d10v_ts2 2 ++#define bfd_mach_d10v_ts3 3 ++ bfd_arch_d30v, /* Mitsubishi D30V */ ++ bfd_arch_dlx, /* DLX */ ++ bfd_arch_m68hc11, /* Motorola 68HC11 */ ++ bfd_arch_m68hc12, /* Motorola 68HC12 */ ++#define bfd_mach_m6812_default 0 ++#define bfd_mach_m6812 1 ++#define bfd_mach_m6812s 2 ++ bfd_arch_z8k, /* Zilog Z8000 */ ++#define bfd_mach_z8001 1 ++#define bfd_mach_z8002 2 ++ bfd_arch_h8500, /* Renesas H8/500 (formerly Hitachi H8/500) */ ++ bfd_arch_sh, /* Renesas / SuperH SH (formerly Hitachi SH) */ ++#define bfd_mach_sh 1 ++#define bfd_mach_sh2 0x20 ++#define bfd_mach_sh_dsp 0x2d ++#define bfd_mach_sh2a 0x2a ++#define bfd_mach_sh2a_nofpu 0x2b ++#define bfd_mach_sh2a_nofpu_or_sh4_nommu_nofpu 0x2a1 ++#define bfd_mach_sh2a_nofpu_or_sh3_nommu 0x2a2 ++#define bfd_mach_sh2a_or_sh4 0x2a3 ++#define bfd_mach_sh2a_or_sh3e 0x2a4 ++#define bfd_mach_sh2e 0x2e ++#define bfd_mach_sh3 0x30 ++#define bfd_mach_sh3_nommu 0x31 ++#define bfd_mach_sh3_dsp 0x3d ++#define bfd_mach_sh3e 0x3e ++#define bfd_mach_sh4 0x40 ++#define bfd_mach_sh4_nofpu 0x41 ++#define bfd_mach_sh4_nommu_nofpu 0x42 ++#define bfd_mach_sh4a 0x4a ++#define bfd_mach_sh4a_nofpu 0x4b ++#define bfd_mach_sh4al_dsp 0x4d ++#define bfd_mach_sh5 0x50 ++ bfd_arch_alpha, /* Dec Alpha */ ++#define bfd_mach_alpha_ev4 0x10 ++#define bfd_mach_alpha_ev5 0x20 ++#define bfd_mach_alpha_ev6 0x30 ++ bfd_arch_arm, /* Advanced Risc Machines ARM. */ ++#define bfd_mach_arm_unknown 0 ++#define bfd_mach_arm_2 1 ++#define bfd_mach_arm_2a 2 ++#define bfd_mach_arm_3 3 ++#define bfd_mach_arm_3M 4 ++#define bfd_mach_arm_4 5 ++#define bfd_mach_arm_4T 6 ++#define bfd_mach_arm_5 7 ++#define bfd_mach_arm_5T 8 ++#define bfd_mach_arm_5TE 9 ++#define bfd_mach_arm_XScale 10 ++#define bfd_mach_arm_ep9312 11 ++#define bfd_mach_arm_iWMMXt 12 ++ bfd_arch_ns32k, /* National Semiconductors ns32000 */ ++ bfd_arch_w65, /* WDC 65816 */ ++ bfd_arch_tic30, /* Texas Instruments TMS320C30 */ ++ bfd_arch_tic4x, /* Texas Instruments TMS320C3X/4X */ ++#define bfd_mach_tic3x 30 ++#define bfd_mach_tic4x 40 ++ bfd_arch_tic54x, /* Texas Instruments TMS320C54X */ ++ bfd_arch_tic80, /* TI TMS320c80 (MVP) */ ++ bfd_arch_v850, /* NEC V850 */ ++#define bfd_mach_v850 1 ++#define bfd_mach_v850e 'E' ++#define bfd_mach_v850e1 '1' ++ bfd_arch_arc, /* ARC Cores */ ++#define bfd_mach_arc_5 5 ++#define bfd_mach_arc_6 6 ++#define bfd_mach_arc_7 7 ++#define bfd_mach_arc_8 8 ++ bfd_arch_m32c, /* Renesas M16C/M32C. */ ++#define bfd_mach_m16c 0x75 ++#define bfd_mach_m32c 0x78 ++ bfd_arch_m32r, /* Renesas M32R (formerly Mitsubishi M32R/D) */ ++#define bfd_mach_m32r 1 /* For backwards compatibility. */ ++#define bfd_mach_m32rx 'x' ++#define bfd_mach_m32r2 '2' ++ bfd_arch_mn10200, /* Matsushita MN10200 */ ++ bfd_arch_mn10300, /* Matsushita MN10300 */ ++#define bfd_mach_mn10300 300 ++#define bfd_mach_am33 330 ++#define bfd_mach_am33_2 332 ++ bfd_arch_fr30, ++#define bfd_mach_fr30 0x46523330 ++ bfd_arch_frv, ++#define bfd_mach_frv 1 ++#define bfd_mach_frvsimple 2 ++#define bfd_mach_fr300 300 ++#define bfd_mach_fr400 400 ++#define bfd_mach_fr450 450 ++#define bfd_mach_frvtomcat 499 /* fr500 prototype */ ++#define bfd_mach_fr500 500 ++#define bfd_mach_fr550 550 ++ bfd_arch_mcore, ++ bfd_arch_ia64, /* HP/Intel ia64 */ ++#define bfd_mach_ia64_elf64 64 ++#define bfd_mach_ia64_elf32 32 ++ bfd_arch_ip2k, /* Ubicom IP2K microcontrollers. */ ++#define bfd_mach_ip2022 1 ++#define bfd_mach_ip2022ext 2 ++ bfd_arch_iq2000, /* Vitesse IQ2000. */ ++#define bfd_mach_iq2000 1 ++#define bfd_mach_iq10 2 ++ bfd_arch_ms1, ++#define bfd_mach_ms1 1 ++#define bfd_mach_mrisc2 2 ++ bfd_arch_pj, ++ bfd_arch_avr, /* Atmel AVR microcontrollers. */ ++#define bfd_mach_avr1 1 ++#define bfd_mach_avr2 2 ++#define bfd_mach_avr3 3 ++#define bfd_mach_avr4 4 ++#define bfd_mach_avr5 5 ++ bfd_arch_cr16c, /* National Semiconductor CompactRISC. */ ++#define bfd_mach_cr16c 1 ++ bfd_arch_crx, /* National Semiconductor CRX. */ ++#define bfd_mach_crx 1 ++ bfd_arch_cris, /* Axis CRIS */ ++#define bfd_mach_cris_v0_v10 255 ++#define bfd_mach_cris_v32 32 ++#define bfd_mach_cris_v10_v32 1032 ++ bfd_arch_s390, /* IBM s390 */ ++#define bfd_mach_s390_31 31 ++#define bfd_mach_s390_64 64 ++ bfd_arch_openrisc, /* OpenRISC */ ++ bfd_arch_mmix, /* Donald Knuth's educational processor. */ ++ bfd_arch_xstormy16, ++#define bfd_mach_xstormy16 1 ++ bfd_arch_msp430, /* Texas Instruments MSP430 architecture. */ ++#define bfd_mach_msp11 11 ++#define bfd_mach_msp110 110 ++#define bfd_mach_msp12 12 ++#define bfd_mach_msp13 13 ++#define bfd_mach_msp14 14 ++#define bfd_mach_msp15 15 ++#define bfd_mach_msp16 16 ++#define bfd_mach_msp31 31 ++#define bfd_mach_msp32 32 ++#define bfd_mach_msp33 33 ++#define bfd_mach_msp41 41 ++#define bfd_mach_msp42 42 ++#define bfd_mach_msp43 43 ++#define bfd_mach_msp44 44 ++ bfd_arch_xtensa, /* Tensilica's Xtensa cores. */ ++#define bfd_mach_xtensa 1 ++ bfd_arch_maxq, /* Dallas MAXQ 10/20 */ ++#define bfd_mach_maxq10 10 ++#define bfd_mach_maxq20 20 ++ bfd_arch_last ++ }; ++ ++typedef struct bfd_arch_info ++{ ++ int bits_per_word; ++ int bits_per_address; ++ int bits_per_byte; ++ enum bfd_architecture arch; ++ unsigned long mach; ++ const char *arch_name; ++ const char *printable_name; ++ unsigned int section_align_power; ++ /* TRUE if this is the default machine for the architecture. ++ The default arch should be the first entry for an arch so that ++ all the entries for that arch can be accessed via <>. */ ++ bfd_boolean the_default; ++ const struct bfd_arch_info * (*compatible) ++ (const struct bfd_arch_info *a, const struct bfd_arch_info *b); ++ ++ bfd_boolean (*scan) (const struct bfd_arch_info *, const char *); ++ ++ const struct bfd_arch_info *next; ++} ++bfd_arch_info_type; ++ ++const char *bfd_printable_name (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_scan_arch (const char *string); ++ ++const char **bfd_arch_list (void); ++ ++const bfd_arch_info_type *bfd_arch_get_compatible ++ (const bfd *abfd, const bfd *bbfd, bfd_boolean accept_unknowns); ++ ++void bfd_set_arch_info (bfd *abfd, const bfd_arch_info_type *arg); ++ ++enum bfd_architecture bfd_get_arch (bfd *abfd); ++ ++unsigned long bfd_get_mach (bfd *abfd); ++ ++unsigned int bfd_arch_bits_per_byte (bfd *abfd); ++ ++unsigned int bfd_arch_bits_per_address (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_get_arch_info (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_lookup_arch ++ (enum bfd_architecture arch, unsigned long machine); ++ ++const char *bfd_printable_arch_mach ++ (enum bfd_architecture arch, unsigned long machine); ++ ++unsigned int bfd_octets_per_byte (bfd *abfd); ++ ++unsigned int bfd_arch_mach_octets_per_byte ++ (enum bfd_architecture arch, unsigned long machine); ++ ++/* Extracted from reloc.c. */ ++typedef enum bfd_reloc_status ++{ ++ /* No errors detected. */ ++ bfd_reloc_ok, ++ ++ /* The relocation was performed, but there was an overflow. */ ++ bfd_reloc_overflow, ++ ++ /* The address to relocate was not within the section supplied. */ ++ bfd_reloc_outofrange, ++ ++ /* Used by special functions. */ ++ bfd_reloc_continue, ++ ++ /* Unsupported relocation size requested. */ ++ bfd_reloc_notsupported, ++ ++ /* Unused. */ ++ bfd_reloc_other, ++ ++ /* The symbol to relocate against was undefined. */ ++ bfd_reloc_undefined, ++ ++ /* The relocation was performed, but may not be ok - presently ++ generated only when linking i960 coff files with i960 b.out ++ symbols. If this type is returned, the error_message argument ++ to bfd_perform_relocation will be set. */ ++ bfd_reloc_dangerous ++ } ++ bfd_reloc_status_type; ++ ++ ++typedef struct reloc_cache_entry ++{ ++ /* A pointer into the canonical table of pointers. */ ++ struct bfd_symbol **sym_ptr_ptr; ++ ++ /* offset in section. */ ++ bfd_size_type address; ++ ++ /* addend for relocation value. */ ++ bfd_vma addend; ++ ++ /* Pointer to how to perform the required relocation. */ ++ reloc_howto_type *howto; ++ ++} ++arelent; ++ ++enum complain_overflow ++{ ++ /* Do not complain on overflow. */ ++ complain_overflow_dont, ++ ++ /* Complain if the bitfield overflows, whether it is considered ++ as signed or unsigned. */ ++ complain_overflow_bitfield, ++ ++ /* Complain if the value overflows when considered as signed ++ number. */ ++ complain_overflow_signed, ++ ++ /* Complain if the value overflows when considered as an ++ unsigned number. */ ++ complain_overflow_unsigned ++}; ++ ++struct reloc_howto_struct ++{ ++ /* The type field has mainly a documentary use - the back end can ++ do what it wants with it, though normally the back end's ++ external idea of what a reloc number is stored ++ in this field. For example, a PC relative word relocation ++ in a coff environment has the type 023 - because that's ++ what the outside world calls a R_PCRWORD reloc. */ ++ unsigned int type; ++ ++ /* The value the final relocation is shifted right by. This drops ++ unwanted data from the relocation. */ ++ unsigned int rightshift; ++ ++ /* The size of the item to be relocated. This is *not* a ++ power-of-two measure. To get the number of bytes operated ++ on by a type of relocation, use bfd_get_reloc_size. */ ++ int size; ++ ++ /* The number of bits in the item to be relocated. This is used ++ when doing overflow checking. */ ++ unsigned int bitsize; ++ ++ /* Notes that the relocation is relative to the location in the ++ data section of the addend. The relocation function will ++ subtract from the relocation value the address of the location ++ being relocated. */ ++ bfd_boolean pc_relative; ++ ++ /* The bit position of the reloc value in the destination. ++ The relocated value is left shifted by this amount. */ ++ unsigned int bitpos; ++ ++ /* What type of overflow error should be checked for when ++ relocating. */ ++ enum complain_overflow complain_on_overflow; ++ ++ /* If this field is non null, then the supplied function is ++ called rather than the normal function. This allows really ++ strange relocation methods to be accommodated (e.g., i960 callj ++ instructions). */ ++ bfd_reloc_status_type (*special_function) ++ (bfd *, arelent *, struct bfd_symbol *, void *, asection *, ++ bfd *, char **); ++ ++ /* The textual name of the relocation type. */ ++ char *name; ++ ++ /* Some formats record a relocation addend in the section contents ++ rather than with the relocation. For ELF formats this is the ++ distinction between USE_REL and USE_RELA (though the code checks ++ for USE_REL == 1/0). The value of this field is TRUE if the ++ addend is recorded with the section contents; when performing a ++ partial link (ld -r) the section contents (the data) will be ++ modified. The value of this field is FALSE if addends are ++ recorded with the relocation (in arelent.addend); when performing ++ a partial link the relocation will be modified. ++ All relocations for all ELF USE_RELA targets should set this field ++ to FALSE (values of TRUE should be looked on with suspicion). ++ However, the converse is not true: not all relocations of all ELF ++ USE_REL targets set this field to TRUE. Why this is so is peculiar ++ to each particular target. For relocs that aren't used in partial ++ links (e.g. GOT stuff) it doesn't matter what this is set to. */ ++ bfd_boolean partial_inplace; ++ ++ /* src_mask selects the part of the instruction (or data) to be used ++ in the relocation sum. If the target relocations don't have an ++ addend in the reloc, eg. ELF USE_REL, src_mask will normally equal ++ dst_mask to extract the addend from the section contents. If ++ relocations do have an addend in the reloc, eg. ELF USE_RELA, this ++ field should be zero. Non-zero values for ELF USE_RELA targets are ++ bogus as in those cases the value in the dst_mask part of the ++ section contents should be treated as garbage. */ ++ bfd_vma src_mask; ++ ++ /* dst_mask selects which parts of the instruction (or data) are ++ replaced with a relocated value. */ ++ bfd_vma dst_mask; ++ ++ /* When some formats create PC relative instructions, they leave ++ the value of the pc of the place being relocated in the offset ++ slot of the instruction, so that a PC relative relocation can ++ be made just by adding in an ordinary offset (e.g., sun3 a.out). ++ Some formats leave the displacement part of an instruction ++ empty (e.g., m88k bcs); this flag signals the fact. */ ++ bfd_boolean pcrel_offset; ++}; ++ ++#define HOWTO(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \ ++ { (unsigned) C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC } ++#define NEWHOWTO(FUNCTION, NAME, SIZE, REL, IN) \ ++ HOWTO (0, 0, SIZE, 0, REL, 0, complain_overflow_dont, FUNCTION, \ ++ NAME, FALSE, 0, 0, IN) ++ ++#define EMPTY_HOWTO(C) \ ++ HOWTO ((C), 0, 0, 0, FALSE, 0, complain_overflow_dont, NULL, \ ++ NULL, FALSE, 0, 0, FALSE) ++ ++#define HOWTO_PREPARE(relocation, symbol) \ ++ { \ ++ if (symbol != NULL) \ ++ { \ ++ if (bfd_is_com_section (symbol->section)) \ ++ { \ ++ relocation = 0; \ ++ } \ ++ else \ ++ { \ ++ relocation = symbol->value; \ ++ } \ ++ } \ ++ } ++ ++unsigned int bfd_get_reloc_size (reloc_howto_type *); ++ ++typedef struct relent_chain ++{ ++ arelent relent; ++ struct relent_chain *next; ++} ++arelent_chain; ++ ++bfd_reloc_status_type bfd_check_overflow ++ (enum complain_overflow how, ++ unsigned int bitsize, ++ unsigned int rightshift, ++ unsigned int addrsize, ++ bfd_vma relocation); ++ ++bfd_reloc_status_type bfd_perform_relocation ++ (bfd *abfd, ++ arelent *reloc_entry, ++ void *data, ++ asection *input_section, ++ bfd *output_bfd, ++ char **error_message); ++ ++bfd_reloc_status_type bfd_install_relocation ++ (bfd *abfd, ++ arelent *reloc_entry, ++ void *data, bfd_vma data_start, ++ asection *input_section, ++ char **error_message); ++ ++enum bfd_reloc_code_real { ++ _dummy_first_bfd_reloc_code_real, ++ ++ ++/* Basic absolute relocations of N bits. */ ++ BFD_RELOC_64, ++ BFD_RELOC_32, ++ BFD_RELOC_26, ++ BFD_RELOC_24, ++ BFD_RELOC_16, ++ BFD_RELOC_14, ++ BFD_RELOC_8, ++ ++/* PC-relative relocations. Sometimes these are relative to the address ++of the relocation itself; sometimes they are relative to the start of ++the section containing the relocation. It depends on the specific target. ++ ++The 24-bit relocation is used in some Intel 960 configurations. */ ++ BFD_RELOC_64_PCREL, ++ BFD_RELOC_32_PCREL, ++ BFD_RELOC_24_PCREL, ++ BFD_RELOC_16_PCREL, ++ BFD_RELOC_12_PCREL, ++ BFD_RELOC_8_PCREL, ++ ++/* Section relative relocations. Some targets need this for DWARF2. */ ++ BFD_RELOC_32_SECREL, ++ ++/* For ELF. */ ++ BFD_RELOC_32_GOT_PCREL, ++ BFD_RELOC_16_GOT_PCREL, ++ BFD_RELOC_8_GOT_PCREL, ++ BFD_RELOC_32_GOTOFF, ++ BFD_RELOC_16_GOTOFF, ++ BFD_RELOC_LO16_GOTOFF, ++ BFD_RELOC_HI16_GOTOFF, ++ BFD_RELOC_HI16_S_GOTOFF, ++ BFD_RELOC_8_GOTOFF, ++ BFD_RELOC_64_PLT_PCREL, ++ BFD_RELOC_32_PLT_PCREL, ++ BFD_RELOC_24_PLT_PCREL, ++ BFD_RELOC_16_PLT_PCREL, ++ BFD_RELOC_8_PLT_PCREL, ++ BFD_RELOC_64_PLTOFF, ++ BFD_RELOC_32_PLTOFF, ++ BFD_RELOC_16_PLTOFF, ++ BFD_RELOC_LO16_PLTOFF, ++ BFD_RELOC_HI16_PLTOFF, ++ BFD_RELOC_HI16_S_PLTOFF, ++ BFD_RELOC_8_PLTOFF, ++ ++/* Relocations used by 68K ELF. */ ++ BFD_RELOC_68K_GLOB_DAT, ++ BFD_RELOC_68K_JMP_SLOT, ++ BFD_RELOC_68K_RELATIVE, ++ ++/* Linkage-table relative. */ ++ BFD_RELOC_32_BASEREL, ++ BFD_RELOC_16_BASEREL, ++ BFD_RELOC_LO16_BASEREL, ++ BFD_RELOC_HI16_BASEREL, ++ BFD_RELOC_HI16_S_BASEREL, ++ BFD_RELOC_8_BASEREL, ++ BFD_RELOC_RVA, ++ ++/* Absolute 8-bit relocation, but used to form an address like 0xFFnn. */ ++ BFD_RELOC_8_FFnn, ++ ++/* These PC-relative relocations are stored as word displacements -- ++i.e., byte displacements shifted right two bits. The 30-bit word ++displacement (<<32_PCREL_S2>> -- 32 bits, shifted 2) is used on the ++SPARC. (SPARC tools generally refer to this as <>.) The ++signed 16-bit displacement is used on the MIPS, and the 23-bit ++displacement is used on the Alpha. */ ++ BFD_RELOC_32_PCREL_S2, ++ BFD_RELOC_16_PCREL_S2, ++ BFD_RELOC_23_PCREL_S2, ++ ++/* High 22 bits and low 10 bits of 32-bit value, placed into lower bits of ++the target word. These are used on the SPARC. */ ++ BFD_RELOC_HI22, ++ BFD_RELOC_LO10, ++ ++/* For systems that allocate a Global Pointer register, these are ++displacements off that register. These relocation types are ++handled specially, because the value the register will have is ++decided relatively late. */ ++ BFD_RELOC_GPREL16, ++ BFD_RELOC_GPREL32, ++ ++/* Reloc types used for i960/b.out. */ ++ BFD_RELOC_I960_CALLJ, ++ ++/* SPARC ELF relocations. There is probably some overlap with other ++relocation types already defined. */ ++ BFD_RELOC_NONE, ++ BFD_RELOC_SPARC_WDISP22, ++ BFD_RELOC_SPARC22, ++ BFD_RELOC_SPARC13, ++ BFD_RELOC_SPARC_GOT10, ++ BFD_RELOC_SPARC_GOT13, ++ BFD_RELOC_SPARC_GOT22, ++ BFD_RELOC_SPARC_PC10, ++ BFD_RELOC_SPARC_PC22, ++ BFD_RELOC_SPARC_WPLT30, ++ BFD_RELOC_SPARC_COPY, ++ BFD_RELOC_SPARC_GLOB_DAT, ++ BFD_RELOC_SPARC_JMP_SLOT, ++ BFD_RELOC_SPARC_RELATIVE, ++ BFD_RELOC_SPARC_UA16, ++ BFD_RELOC_SPARC_UA32, ++ BFD_RELOC_SPARC_UA64, ++ ++/* I think these are specific to SPARC a.out (e.g., Sun 4). */ ++ BFD_RELOC_SPARC_BASE13, ++ BFD_RELOC_SPARC_BASE22, ++ ++/* SPARC64 relocations */ ++#define BFD_RELOC_SPARC_64 BFD_RELOC_64 ++ BFD_RELOC_SPARC_10, ++ BFD_RELOC_SPARC_11, ++ BFD_RELOC_SPARC_OLO10, ++ BFD_RELOC_SPARC_HH22, ++ BFD_RELOC_SPARC_HM10, ++ BFD_RELOC_SPARC_LM22, ++ BFD_RELOC_SPARC_PC_HH22, ++ BFD_RELOC_SPARC_PC_HM10, ++ BFD_RELOC_SPARC_PC_LM22, ++ BFD_RELOC_SPARC_WDISP16, ++ BFD_RELOC_SPARC_WDISP19, ++ BFD_RELOC_SPARC_7, ++ BFD_RELOC_SPARC_6, ++ BFD_RELOC_SPARC_5, ++#define BFD_RELOC_SPARC_DISP64 BFD_RELOC_64_PCREL ++ BFD_RELOC_SPARC_PLT32, ++ BFD_RELOC_SPARC_PLT64, ++ BFD_RELOC_SPARC_HIX22, ++ BFD_RELOC_SPARC_LOX10, ++ BFD_RELOC_SPARC_H44, ++ BFD_RELOC_SPARC_M44, ++ BFD_RELOC_SPARC_L44, ++ BFD_RELOC_SPARC_REGISTER, ++ ++/* SPARC little endian relocation */ ++ BFD_RELOC_SPARC_REV32, ++ ++/* SPARC TLS relocations */ ++ BFD_RELOC_SPARC_TLS_GD_HI22, ++ BFD_RELOC_SPARC_TLS_GD_LO10, ++ BFD_RELOC_SPARC_TLS_GD_ADD, ++ BFD_RELOC_SPARC_TLS_GD_CALL, ++ BFD_RELOC_SPARC_TLS_LDM_HI22, ++ BFD_RELOC_SPARC_TLS_LDM_LO10, ++ BFD_RELOC_SPARC_TLS_LDM_ADD, ++ BFD_RELOC_SPARC_TLS_LDM_CALL, ++ BFD_RELOC_SPARC_TLS_LDO_HIX22, ++ BFD_RELOC_SPARC_TLS_LDO_LOX10, ++ BFD_RELOC_SPARC_TLS_LDO_ADD, ++ BFD_RELOC_SPARC_TLS_IE_HI22, ++ BFD_RELOC_SPARC_TLS_IE_LO10, ++ BFD_RELOC_SPARC_TLS_IE_LD, ++ BFD_RELOC_SPARC_TLS_IE_LDX, ++ BFD_RELOC_SPARC_TLS_IE_ADD, ++ BFD_RELOC_SPARC_TLS_LE_HIX22, ++ BFD_RELOC_SPARC_TLS_LE_LOX10, ++ BFD_RELOC_SPARC_TLS_DTPMOD32, ++ BFD_RELOC_SPARC_TLS_DTPMOD64, ++ BFD_RELOC_SPARC_TLS_DTPOFF32, ++ BFD_RELOC_SPARC_TLS_DTPOFF64, ++ BFD_RELOC_SPARC_TLS_TPOFF32, ++ BFD_RELOC_SPARC_TLS_TPOFF64, ++ ++/* Alpha ECOFF and ELF relocations. Some of these treat the symbol or ++"addend" in some special way. ++For GPDISP_HI16 ("gpdisp") relocations, the symbol is ignored when ++writing; when reading, it will be the absolute section symbol. The ++addend is the displacement in bytes of the "lda" instruction from ++the "ldah" instruction (which is at the address of this reloc). */ ++ BFD_RELOC_ALPHA_GPDISP_HI16, ++ ++/* For GPDISP_LO16 ("ignore") relocations, the symbol is handled as ++with GPDISP_HI16 relocs. The addend is ignored when writing the ++relocations out, and is filled in with the file's GP value on ++reading, for convenience. */ ++ BFD_RELOC_ALPHA_GPDISP_LO16, ++ ++/* The ELF GPDISP relocation is exactly the same as the GPDISP_HI16 ++relocation except that there is no accompanying GPDISP_LO16 ++relocation. */ ++ BFD_RELOC_ALPHA_GPDISP, ++ ++/* The Alpha LITERAL/LITUSE relocs are produced by a symbol reference; ++the assembler turns it into a LDQ instruction to load the address of ++the symbol, and then fills in a register in the real instruction. ++ ++The LITERAL reloc, at the LDQ instruction, refers to the .lita ++section symbol. The addend is ignored when writing, but is filled ++in with the file's GP value on reading, for convenience, as with the ++GPDISP_LO16 reloc. ++ ++The ELF_LITERAL reloc is somewhere between 16_GOTOFF and GPDISP_LO16. ++It should refer to the symbol to be referenced, as with 16_GOTOFF, ++but it generates output not based on the position within the .got ++section, but relative to the GP value chosen for the file during the ++final link stage. ++ ++The LITUSE reloc, on the instruction using the loaded address, gives ++information to the linker that it might be able to use to optimize ++away some literal section references. The symbol is ignored (read ++as the absolute section symbol), and the "addend" indicates the type ++of instruction using the register: ++1 - "memory" fmt insn ++2 - byte-manipulation (byte offset reg) ++3 - jsr (target of branch) */ ++ BFD_RELOC_ALPHA_LITERAL, ++ BFD_RELOC_ALPHA_ELF_LITERAL, ++ BFD_RELOC_ALPHA_LITUSE, ++ ++/* The HINT relocation indicates a value that should be filled into the ++"hint" field of a jmp/jsr/ret instruction, for possible branch- ++prediction logic which may be provided on some processors. */ ++ BFD_RELOC_ALPHA_HINT, ++ ++/* The LINKAGE relocation outputs a linkage pair in the object file, ++which is filled by the linker. */ ++ BFD_RELOC_ALPHA_LINKAGE, ++ ++/* The CODEADDR relocation outputs a STO_CA in the object file, ++which is filled by the linker. */ ++ BFD_RELOC_ALPHA_CODEADDR, ++ ++/* The GPREL_HI/LO relocations together form a 32-bit offset from the ++GP register. */ ++ BFD_RELOC_ALPHA_GPREL_HI16, ++ BFD_RELOC_ALPHA_GPREL_LO16, ++ ++/* Like BFD_RELOC_23_PCREL_S2, except that the source and target must ++share a common GP, and the target address is adjusted for ++STO_ALPHA_STD_GPLOAD. */ ++ BFD_RELOC_ALPHA_BRSGP, ++ ++/* Alpha thread-local storage relocations. */ ++ BFD_RELOC_ALPHA_TLSGD, ++ BFD_RELOC_ALPHA_TLSLDM, ++ BFD_RELOC_ALPHA_DTPMOD64, ++ BFD_RELOC_ALPHA_GOTDTPREL16, ++ BFD_RELOC_ALPHA_DTPREL64, ++ BFD_RELOC_ALPHA_DTPREL_HI16, ++ BFD_RELOC_ALPHA_DTPREL_LO16, ++ BFD_RELOC_ALPHA_DTPREL16, ++ BFD_RELOC_ALPHA_GOTTPREL16, ++ BFD_RELOC_ALPHA_TPREL64, ++ BFD_RELOC_ALPHA_TPREL_HI16, ++ BFD_RELOC_ALPHA_TPREL_LO16, ++ BFD_RELOC_ALPHA_TPREL16, ++ ++/* Bits 27..2 of the relocation address shifted right 2 bits; ++simple reloc otherwise. */ ++ BFD_RELOC_MIPS_JMP, ++ ++/* The MIPS16 jump instruction. */ ++ BFD_RELOC_MIPS16_JMP, ++ ++/* MIPS16 GP relative reloc. */ ++ BFD_RELOC_MIPS16_GPREL, ++ ++/* High 16 bits of 32-bit value; simple reloc. */ ++ BFD_RELOC_HI16, ++ ++/* High 16 bits of 32-bit value but the low 16 bits will be sign ++extended and added to form the final result. If the low 16 ++bits form a negative number, we need to add one to the high value ++to compensate for the borrow when the low bits are added. */ ++ BFD_RELOC_HI16_S, ++ ++/* Low 16 bits. */ ++ BFD_RELOC_LO16, ++ ++/* High 16 bits of 32-bit pc-relative value */ ++ BFD_RELOC_HI16_PCREL, ++ ++/* High 16 bits of 32-bit pc-relative value, adjusted */ ++ BFD_RELOC_HI16_S_PCREL, ++ ++/* Low 16 bits of pc-relative value */ ++ BFD_RELOC_LO16_PCREL, ++ ++/* MIPS16 high 16 bits of 32-bit value. */ ++ BFD_RELOC_MIPS16_HI16, ++ ++/* MIPS16 high 16 bits of 32-bit value but the low 16 bits will be sign ++extended and added to form the final result. If the low 16 ++bits form a negative number, we need to add one to the high value ++to compensate for the borrow when the low bits are added. */ ++ BFD_RELOC_MIPS16_HI16_S, ++ ++/* MIPS16 low 16 bits. */ ++ BFD_RELOC_MIPS16_LO16, ++ ++/* Relocation against a MIPS literal section. */ ++ BFD_RELOC_MIPS_LITERAL, ++ ++/* MIPS ELF relocations. */ ++ BFD_RELOC_MIPS_GOT16, ++ BFD_RELOC_MIPS_CALL16, ++ BFD_RELOC_MIPS_GOT_HI16, ++ BFD_RELOC_MIPS_GOT_LO16, ++ BFD_RELOC_MIPS_CALL_HI16, ++ BFD_RELOC_MIPS_CALL_LO16, ++ BFD_RELOC_MIPS_SUB, ++ BFD_RELOC_MIPS_GOT_PAGE, ++ BFD_RELOC_MIPS_GOT_OFST, ++ BFD_RELOC_MIPS_GOT_DISP, ++ BFD_RELOC_MIPS_SHIFT5, ++ BFD_RELOC_MIPS_SHIFT6, ++ BFD_RELOC_MIPS_INSERT_A, ++ BFD_RELOC_MIPS_INSERT_B, ++ BFD_RELOC_MIPS_DELETE, ++ BFD_RELOC_MIPS_HIGHEST, ++ BFD_RELOC_MIPS_HIGHER, ++ BFD_RELOC_MIPS_SCN_DISP, ++ BFD_RELOC_MIPS_REL16, ++ BFD_RELOC_MIPS_RELGOT, ++ BFD_RELOC_MIPS_JALR, ++ BFD_RELOC_MIPS_TLS_DTPMOD32, ++ BFD_RELOC_MIPS_TLS_DTPREL32, ++ BFD_RELOC_MIPS_TLS_DTPMOD64, ++ BFD_RELOC_MIPS_TLS_DTPREL64, ++ BFD_RELOC_MIPS_TLS_GD, ++ BFD_RELOC_MIPS_TLS_LDM, ++ BFD_RELOC_MIPS_TLS_DTPREL_HI16, ++ BFD_RELOC_MIPS_TLS_DTPREL_LO16, ++ BFD_RELOC_MIPS_TLS_GOTTPREL, ++ BFD_RELOC_MIPS_TLS_TPREL32, ++ BFD_RELOC_MIPS_TLS_TPREL64, ++ BFD_RELOC_MIPS_TLS_TPREL_HI16, ++ BFD_RELOC_MIPS_TLS_TPREL_LO16, ++ ++ ++/* Fujitsu Frv Relocations. */ ++ BFD_RELOC_FRV_LABEL16, ++ BFD_RELOC_FRV_LABEL24, ++ BFD_RELOC_FRV_LO16, ++ BFD_RELOC_FRV_HI16, ++ BFD_RELOC_FRV_GPREL12, ++ BFD_RELOC_FRV_GPRELU12, ++ BFD_RELOC_FRV_GPREL32, ++ BFD_RELOC_FRV_GPRELHI, ++ BFD_RELOC_FRV_GPRELLO, ++ BFD_RELOC_FRV_GOT12, ++ BFD_RELOC_FRV_GOTHI, ++ BFD_RELOC_FRV_GOTLO, ++ BFD_RELOC_FRV_FUNCDESC, ++ BFD_RELOC_FRV_FUNCDESC_GOT12, ++ BFD_RELOC_FRV_FUNCDESC_GOTHI, ++ BFD_RELOC_FRV_FUNCDESC_GOTLO, ++ BFD_RELOC_FRV_FUNCDESC_VALUE, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFF12, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFFHI, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFFLO, ++ BFD_RELOC_FRV_GOTOFF12, ++ BFD_RELOC_FRV_GOTOFFHI, ++ BFD_RELOC_FRV_GOTOFFLO, ++ BFD_RELOC_FRV_GETTLSOFF, ++ BFD_RELOC_FRV_TLSDESC_VALUE, ++ BFD_RELOC_FRV_GOTTLSDESC12, ++ BFD_RELOC_FRV_GOTTLSDESCHI, ++ BFD_RELOC_FRV_GOTTLSDESCLO, ++ BFD_RELOC_FRV_TLSMOFF12, ++ BFD_RELOC_FRV_TLSMOFFHI, ++ BFD_RELOC_FRV_TLSMOFFLO, ++ BFD_RELOC_FRV_GOTTLSOFF12, ++ BFD_RELOC_FRV_GOTTLSOFFHI, ++ BFD_RELOC_FRV_GOTTLSOFFLO, ++ BFD_RELOC_FRV_TLSOFF, ++ BFD_RELOC_FRV_TLSDESC_RELAX, ++ BFD_RELOC_FRV_GETTLSOFF_RELAX, ++ BFD_RELOC_FRV_TLSOFF_RELAX, ++ BFD_RELOC_FRV_TLSMOFF, ++ ++ ++/* This is a 24bit GOT-relative reloc for the mn10300. */ ++ BFD_RELOC_MN10300_GOTOFF24, ++ ++/* This is a 32bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT32, ++ ++/* This is a 24bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT24, ++ ++/* This is a 16bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT16, ++ ++/* Copy symbol at runtime. */ ++ BFD_RELOC_MN10300_COPY, ++ ++/* Create GOT entry. */ ++ BFD_RELOC_MN10300_GLOB_DAT, ++ ++/* Create PLT entry. */ ++ BFD_RELOC_MN10300_JMP_SLOT, ++ ++/* Adjust by program base. */ ++ BFD_RELOC_MN10300_RELATIVE, ++ ++ ++/* i386/elf relocations */ ++ BFD_RELOC_386_GOT32, ++ BFD_RELOC_386_PLT32, ++ BFD_RELOC_386_COPY, ++ BFD_RELOC_386_GLOB_DAT, ++ BFD_RELOC_386_JUMP_SLOT, ++ BFD_RELOC_386_RELATIVE, ++ BFD_RELOC_386_GOTOFF, ++ BFD_RELOC_386_GOTPC, ++ BFD_RELOC_386_TLS_TPOFF, ++ BFD_RELOC_386_TLS_IE, ++ BFD_RELOC_386_TLS_GOTIE, ++ BFD_RELOC_386_TLS_LE, ++ BFD_RELOC_386_TLS_GD, ++ BFD_RELOC_386_TLS_LDM, ++ BFD_RELOC_386_TLS_LDO_32, ++ BFD_RELOC_386_TLS_IE_32, ++ BFD_RELOC_386_TLS_LE_32, ++ BFD_RELOC_386_TLS_DTPMOD32, ++ BFD_RELOC_386_TLS_DTPOFF32, ++ BFD_RELOC_386_TLS_TPOFF32, ++ ++/* x86-64/elf relocations */ ++ BFD_RELOC_X86_64_GOT32, ++ BFD_RELOC_X86_64_PLT32, ++ BFD_RELOC_X86_64_COPY, ++ BFD_RELOC_X86_64_GLOB_DAT, ++ BFD_RELOC_X86_64_JUMP_SLOT, ++ BFD_RELOC_X86_64_RELATIVE, ++ BFD_RELOC_X86_64_GOTPCREL, ++ BFD_RELOC_X86_64_32S, ++ BFD_RELOC_X86_64_DTPMOD64, ++ BFD_RELOC_X86_64_DTPOFF64, ++ BFD_RELOC_X86_64_TPOFF64, ++ BFD_RELOC_X86_64_TLSGD, ++ BFD_RELOC_X86_64_TLSLD, ++ BFD_RELOC_X86_64_DTPOFF32, ++ BFD_RELOC_X86_64_GOTTPOFF, ++ BFD_RELOC_X86_64_TPOFF32, ++ BFD_RELOC_X86_64_GOTOFF64, ++ BFD_RELOC_X86_64_GOTPC32, ++ ++/* ns32k relocations */ ++ BFD_RELOC_NS32K_IMM_8, ++ BFD_RELOC_NS32K_IMM_16, ++ BFD_RELOC_NS32K_IMM_32, ++ BFD_RELOC_NS32K_IMM_8_PCREL, ++ BFD_RELOC_NS32K_IMM_16_PCREL, ++ BFD_RELOC_NS32K_IMM_32_PCREL, ++ BFD_RELOC_NS32K_DISP_8, ++ BFD_RELOC_NS32K_DISP_16, ++ BFD_RELOC_NS32K_DISP_32, ++ BFD_RELOC_NS32K_DISP_8_PCREL, ++ BFD_RELOC_NS32K_DISP_16_PCREL, ++ BFD_RELOC_NS32K_DISP_32_PCREL, ++ ++/* PDP11 relocations */ ++ BFD_RELOC_PDP11_DISP_8_PCREL, ++ BFD_RELOC_PDP11_DISP_6_PCREL, ++ ++/* Picojava relocs. Not all of these appear in object files. */ ++ BFD_RELOC_PJ_CODE_HI16, ++ BFD_RELOC_PJ_CODE_LO16, ++ BFD_RELOC_PJ_CODE_DIR16, ++ BFD_RELOC_PJ_CODE_DIR32, ++ BFD_RELOC_PJ_CODE_REL16, ++ BFD_RELOC_PJ_CODE_REL32, ++ ++/* Power(rs6000) and PowerPC relocations. */ ++ BFD_RELOC_PPC_B26, ++ BFD_RELOC_PPC_BA26, ++ BFD_RELOC_PPC_TOC16, ++ BFD_RELOC_PPC_B16, ++ BFD_RELOC_PPC_B16_BRTAKEN, ++ BFD_RELOC_PPC_B16_BRNTAKEN, ++ BFD_RELOC_PPC_BA16, ++ BFD_RELOC_PPC_BA16_BRTAKEN, ++ BFD_RELOC_PPC_BA16_BRNTAKEN, ++ BFD_RELOC_PPC_COPY, ++ BFD_RELOC_PPC_GLOB_DAT, ++ BFD_RELOC_PPC_JMP_SLOT, ++ BFD_RELOC_PPC_RELATIVE, ++ BFD_RELOC_PPC_LOCAL24PC, ++ BFD_RELOC_PPC_EMB_NADDR32, ++ BFD_RELOC_PPC_EMB_NADDR16, ++ BFD_RELOC_PPC_EMB_NADDR16_LO, ++ BFD_RELOC_PPC_EMB_NADDR16_HI, ++ BFD_RELOC_PPC_EMB_NADDR16_HA, ++ BFD_RELOC_PPC_EMB_SDAI16, ++ BFD_RELOC_PPC_EMB_SDA2I16, ++ BFD_RELOC_PPC_EMB_SDA2REL, ++ BFD_RELOC_PPC_EMB_SDA21, ++ BFD_RELOC_PPC_EMB_MRKREF, ++ BFD_RELOC_PPC_EMB_RELSEC16, ++ BFD_RELOC_PPC_EMB_RELST_LO, ++ BFD_RELOC_PPC_EMB_RELST_HI, ++ BFD_RELOC_PPC_EMB_RELST_HA, ++ BFD_RELOC_PPC_EMB_BIT_FLD, ++ BFD_RELOC_PPC_EMB_RELSDA, ++ BFD_RELOC_PPC64_HIGHER, ++ BFD_RELOC_PPC64_HIGHER_S, ++ BFD_RELOC_PPC64_HIGHEST, ++ BFD_RELOC_PPC64_HIGHEST_S, ++ BFD_RELOC_PPC64_TOC16_LO, ++ BFD_RELOC_PPC64_TOC16_HI, ++ BFD_RELOC_PPC64_TOC16_HA, ++ BFD_RELOC_PPC64_TOC, ++ BFD_RELOC_PPC64_PLTGOT16, ++ BFD_RELOC_PPC64_PLTGOT16_LO, ++ BFD_RELOC_PPC64_PLTGOT16_HI, ++ BFD_RELOC_PPC64_PLTGOT16_HA, ++ BFD_RELOC_PPC64_ADDR16_DS, ++ BFD_RELOC_PPC64_ADDR16_LO_DS, ++ BFD_RELOC_PPC64_GOT16_DS, ++ BFD_RELOC_PPC64_GOT16_LO_DS, ++ BFD_RELOC_PPC64_PLT16_LO_DS, ++ BFD_RELOC_PPC64_SECTOFF_DS, ++ BFD_RELOC_PPC64_SECTOFF_LO_DS, ++ BFD_RELOC_PPC64_TOC16_DS, ++ BFD_RELOC_PPC64_TOC16_LO_DS, ++ BFD_RELOC_PPC64_PLTGOT16_DS, ++ BFD_RELOC_PPC64_PLTGOT16_LO_DS, ++ ++/* PowerPC and PowerPC64 thread-local storage relocations. */ ++ BFD_RELOC_PPC_TLS, ++ BFD_RELOC_PPC_DTPMOD, ++ BFD_RELOC_PPC_TPREL16, ++ BFD_RELOC_PPC_TPREL16_LO, ++ BFD_RELOC_PPC_TPREL16_HI, ++ BFD_RELOC_PPC_TPREL16_HA, ++ BFD_RELOC_PPC_TPREL, ++ BFD_RELOC_PPC_DTPREL16, ++ BFD_RELOC_PPC_DTPREL16_LO, ++ BFD_RELOC_PPC_DTPREL16_HI, ++ BFD_RELOC_PPC_DTPREL16_HA, ++ BFD_RELOC_PPC_DTPREL, ++ BFD_RELOC_PPC_GOT_TLSGD16, ++ BFD_RELOC_PPC_GOT_TLSGD16_LO, ++ BFD_RELOC_PPC_GOT_TLSGD16_HI, ++ BFD_RELOC_PPC_GOT_TLSGD16_HA, ++ BFD_RELOC_PPC_GOT_TLSLD16, ++ BFD_RELOC_PPC_GOT_TLSLD16_LO, ++ BFD_RELOC_PPC_GOT_TLSLD16_HI, ++ BFD_RELOC_PPC_GOT_TLSLD16_HA, ++ BFD_RELOC_PPC_GOT_TPREL16, ++ BFD_RELOC_PPC_GOT_TPREL16_LO, ++ BFD_RELOC_PPC_GOT_TPREL16_HI, ++ BFD_RELOC_PPC_GOT_TPREL16_HA, ++ BFD_RELOC_PPC_GOT_DTPREL16, ++ BFD_RELOC_PPC_GOT_DTPREL16_LO, ++ BFD_RELOC_PPC_GOT_DTPREL16_HI, ++ BFD_RELOC_PPC_GOT_DTPREL16_HA, ++ BFD_RELOC_PPC64_TPREL16_DS, ++ BFD_RELOC_PPC64_TPREL16_LO_DS, ++ BFD_RELOC_PPC64_TPREL16_HIGHER, ++ BFD_RELOC_PPC64_TPREL16_HIGHERA, ++ BFD_RELOC_PPC64_TPREL16_HIGHEST, ++ BFD_RELOC_PPC64_TPREL16_HIGHESTA, ++ BFD_RELOC_PPC64_DTPREL16_DS, ++ BFD_RELOC_PPC64_DTPREL16_LO_DS, ++ BFD_RELOC_PPC64_DTPREL16_HIGHER, ++ BFD_RELOC_PPC64_DTPREL16_HIGHERA, ++ BFD_RELOC_PPC64_DTPREL16_HIGHEST, ++ BFD_RELOC_PPC64_DTPREL16_HIGHESTA, ++ ++/* IBM 370/390 relocations */ ++ BFD_RELOC_I370_D12, ++ ++/* The type of reloc used to build a constructor table - at the moment ++probably a 32 bit wide absolute relocation, but the target can choose. ++It generally does map to one of the other relocation types. */ ++ BFD_RELOC_CTOR, ++ ++/* ARM 26 bit pc-relative branch. The lowest two bits must be zero and are ++not stored in the instruction. */ ++ BFD_RELOC_ARM_PCREL_BRANCH, ++ ++/* ARM 26 bit pc-relative branch. The lowest bit must be zero and is ++not stored in the instruction. The 2nd lowest bit comes from a 1 bit ++field in the instruction. */ ++ BFD_RELOC_ARM_PCREL_BLX, ++ ++/* Thumb 22 bit pc-relative branch. The lowest bit must be zero and is ++not stored in the instruction. The 2nd lowest bit comes from a 1 bit ++field in the instruction. */ ++ BFD_RELOC_THUMB_PCREL_BLX, ++ ++/* Thumb 7-, 9-, 12-, 20-, 23-, and 25-bit pc-relative branches. ++The lowest bit must be zero and is not stored in the instruction. ++Note that the corresponding ELF R_ARM_THM_JUMPnn constant has an ++"nn" one smaller in all cases. Note further that BRANCH23 ++corresponds to R_ARM_THM_CALL. */ ++ BFD_RELOC_THUMB_PCREL_BRANCH7, ++ BFD_RELOC_THUMB_PCREL_BRANCH9, ++ BFD_RELOC_THUMB_PCREL_BRANCH12, ++ BFD_RELOC_THUMB_PCREL_BRANCH20, ++ BFD_RELOC_THUMB_PCREL_BRANCH23, ++ BFD_RELOC_THUMB_PCREL_BRANCH25, ++ ++/* 12-bit immediate offset, used in ARM-format ldr and str instructions. */ ++ BFD_RELOC_ARM_OFFSET_IMM, ++ ++/* 5-bit immediate offset, used in Thumb-format ldr and str instructions. */ ++ BFD_RELOC_ARM_THUMB_OFFSET, ++ ++/* Pc-relative or absolute relocation depending on target. Used for ++entries in .init_array sections. */ ++ BFD_RELOC_ARM_TARGET1, ++ ++/* Read-only segment base relative address. */ ++ BFD_RELOC_ARM_ROSEGREL32, ++ ++/* Data segment base relative address. */ ++ BFD_RELOC_ARM_SBREL32, ++ ++/* This reloc is used for references to RTTI data from exception handling ++tables. The actual definition depends on the target. It may be a ++pc-relative or some form of GOT-indirect relocation. */ ++ BFD_RELOC_ARM_TARGET2, ++ ++/* 31-bit PC relative address. */ ++ BFD_RELOC_ARM_PREL31, ++ ++/* Relocations for setting up GOTs and PLTs for shared libraries. */ ++ BFD_RELOC_ARM_JUMP_SLOT, ++ BFD_RELOC_ARM_GLOB_DAT, ++ BFD_RELOC_ARM_GOT32, ++ BFD_RELOC_ARM_PLT32, ++ BFD_RELOC_ARM_RELATIVE, ++ BFD_RELOC_ARM_GOTOFF, ++ BFD_RELOC_ARM_GOTPC, ++ ++/* ARM thread-local storage relocations. */ ++ BFD_RELOC_ARM_TLS_GD32, ++ BFD_RELOC_ARM_TLS_LDO32, ++ BFD_RELOC_ARM_TLS_LDM32, ++ BFD_RELOC_ARM_TLS_DTPOFF32, ++ BFD_RELOC_ARM_TLS_DTPMOD32, ++ BFD_RELOC_ARM_TLS_TPOFF32, ++ BFD_RELOC_ARM_TLS_IE32, ++ BFD_RELOC_ARM_TLS_LE32, ++ ++/* These relocs are only used within the ARM assembler. They are not ++(at present) written to any object files. */ ++ BFD_RELOC_ARM_IMMEDIATE, ++ BFD_RELOC_ARM_ADRL_IMMEDIATE, ++ BFD_RELOC_ARM_T32_IMMEDIATE, ++ BFD_RELOC_ARM_SHIFT_IMM, ++ BFD_RELOC_ARM_SMI, ++ BFD_RELOC_ARM_SWI, ++ BFD_RELOC_ARM_MULTI, ++ BFD_RELOC_ARM_CP_OFF_IMM, ++ BFD_RELOC_ARM_CP_OFF_IMM_S2, ++ BFD_RELOC_ARM_ADR_IMM, ++ BFD_RELOC_ARM_LDR_IMM, ++ BFD_RELOC_ARM_LITERAL, ++ BFD_RELOC_ARM_IN_POOL, ++ BFD_RELOC_ARM_OFFSET_IMM8, ++ BFD_RELOC_ARM_T32_OFFSET_U8, ++ BFD_RELOC_ARM_T32_OFFSET_IMM, ++ BFD_RELOC_ARM_HWLITERAL, ++ BFD_RELOC_ARM_THUMB_ADD, ++ BFD_RELOC_ARM_THUMB_IMM, ++ BFD_RELOC_ARM_THUMB_SHIFT, ++ ++/* Renesas / SuperH SH relocs. Not all of these appear in object files. */ ++ BFD_RELOC_SH_PCDISP8BY2, ++ BFD_RELOC_SH_PCDISP12BY2, ++ BFD_RELOC_SH_IMM3, ++ BFD_RELOC_SH_IMM3U, ++ BFD_RELOC_SH_DISP12, ++ BFD_RELOC_SH_DISP12BY2, ++ BFD_RELOC_SH_DISP12BY4, ++ BFD_RELOC_SH_DISP12BY8, ++ BFD_RELOC_SH_DISP20, ++ BFD_RELOC_SH_DISP20BY8, ++ BFD_RELOC_SH_IMM4, ++ BFD_RELOC_SH_IMM4BY2, ++ BFD_RELOC_SH_IMM4BY4, ++ BFD_RELOC_SH_IMM8, ++ BFD_RELOC_SH_IMM8BY2, ++ BFD_RELOC_SH_IMM8BY4, ++ BFD_RELOC_SH_PCRELIMM8BY2, ++ BFD_RELOC_SH_PCRELIMM8BY4, ++ BFD_RELOC_SH_SWITCH16, ++ BFD_RELOC_SH_SWITCH32, ++ BFD_RELOC_SH_USES, ++ BFD_RELOC_SH_COUNT, ++ BFD_RELOC_SH_ALIGN, ++ BFD_RELOC_SH_CODE, ++ BFD_RELOC_SH_DATA, ++ BFD_RELOC_SH_LABEL, ++ BFD_RELOC_SH_LOOP_START, ++ BFD_RELOC_SH_LOOP_END, ++ BFD_RELOC_SH_COPY, ++ BFD_RELOC_SH_GLOB_DAT, ++ BFD_RELOC_SH_JMP_SLOT, ++ BFD_RELOC_SH_RELATIVE, ++ BFD_RELOC_SH_GOTPC, ++ BFD_RELOC_SH_GOT_LOW16, ++ BFD_RELOC_SH_GOT_MEDLOW16, ++ BFD_RELOC_SH_GOT_MEDHI16, ++ BFD_RELOC_SH_GOT_HI16, ++ BFD_RELOC_SH_GOTPLT_LOW16, ++ BFD_RELOC_SH_GOTPLT_MEDLOW16, ++ BFD_RELOC_SH_GOTPLT_MEDHI16, ++ BFD_RELOC_SH_GOTPLT_HI16, ++ BFD_RELOC_SH_PLT_LOW16, ++ BFD_RELOC_SH_PLT_MEDLOW16, ++ BFD_RELOC_SH_PLT_MEDHI16, ++ BFD_RELOC_SH_PLT_HI16, ++ BFD_RELOC_SH_GOTOFF_LOW16, ++ BFD_RELOC_SH_GOTOFF_MEDLOW16, ++ BFD_RELOC_SH_GOTOFF_MEDHI16, ++ BFD_RELOC_SH_GOTOFF_HI16, ++ BFD_RELOC_SH_GOTPC_LOW16, ++ BFD_RELOC_SH_GOTPC_MEDLOW16, ++ BFD_RELOC_SH_GOTPC_MEDHI16, ++ BFD_RELOC_SH_GOTPC_HI16, ++ BFD_RELOC_SH_COPY64, ++ BFD_RELOC_SH_GLOB_DAT64, ++ BFD_RELOC_SH_JMP_SLOT64, ++ BFD_RELOC_SH_RELATIVE64, ++ BFD_RELOC_SH_GOT10BY4, ++ BFD_RELOC_SH_GOT10BY8, ++ BFD_RELOC_SH_GOTPLT10BY4, ++ BFD_RELOC_SH_GOTPLT10BY8, ++ BFD_RELOC_SH_GOTPLT32, ++ BFD_RELOC_SH_SHMEDIA_CODE, ++ BFD_RELOC_SH_IMMU5, ++ BFD_RELOC_SH_IMMS6, ++ BFD_RELOC_SH_IMMS6BY32, ++ BFD_RELOC_SH_IMMU6, ++ BFD_RELOC_SH_IMMS10, ++ BFD_RELOC_SH_IMMS10BY2, ++ BFD_RELOC_SH_IMMS10BY4, ++ BFD_RELOC_SH_IMMS10BY8, ++ BFD_RELOC_SH_IMMS16, ++ BFD_RELOC_SH_IMMU16, ++ BFD_RELOC_SH_IMM_LOW16, ++ BFD_RELOC_SH_IMM_LOW16_PCREL, ++ BFD_RELOC_SH_IMM_MEDLOW16, ++ BFD_RELOC_SH_IMM_MEDLOW16_PCREL, ++ BFD_RELOC_SH_IMM_MEDHI16, ++ BFD_RELOC_SH_IMM_MEDHI16_PCREL, ++ BFD_RELOC_SH_IMM_HI16, ++ BFD_RELOC_SH_IMM_HI16_PCREL, ++ BFD_RELOC_SH_PT_16, ++ BFD_RELOC_SH_TLS_GD_32, ++ BFD_RELOC_SH_TLS_LD_32, ++ BFD_RELOC_SH_TLS_LDO_32, ++ BFD_RELOC_SH_TLS_IE_32, ++ BFD_RELOC_SH_TLS_LE_32, ++ BFD_RELOC_SH_TLS_DTPMOD32, ++ BFD_RELOC_SH_TLS_DTPOFF32, ++ BFD_RELOC_SH_TLS_TPOFF32, ++ ++/* ARC Cores relocs. ++ARC 22 bit pc-relative branch. The lowest two bits must be zero and are ++not stored in the instruction. The high 20 bits are installed in bits 26 ++through 7 of the instruction. */ ++ BFD_RELOC_ARC_B22_PCREL, ++ ++/* ARC 26 bit absolute branch. The lowest two bits must be zero and are not ++stored in the instruction. The high 24 bits are installed in bits 23 ++through 0. */ ++ BFD_RELOC_ARC_B26, ++ ++/* Mitsubishi D10V relocs. ++This is a 10-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_10_PCREL_R, ++ ++/* Mitsubishi D10V relocs. ++This is a 10-bit reloc with the right 2 bits ++assumed to be 0. This is the same as the previous reloc ++except it is in the left container, i.e., ++shifted left 15 bits. */ ++ BFD_RELOC_D10V_10_PCREL_L, ++ ++/* This is an 18-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_18, ++ ++/* This is an 18-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_18_PCREL, ++ ++/* Mitsubishi D30V relocs. ++This is a 6-bit absolute reloc. */ ++ BFD_RELOC_D30V_6, ++ ++/* This is a 6-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_9_PCREL, ++ ++/* This is a 6-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_9_PCREL_R, ++ ++/* This is a 12-bit absolute reloc with the ++right 3 bitsassumed to be 0. */ ++ BFD_RELOC_D30V_15, ++ ++/* This is a 12-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_15_PCREL, ++ ++/* This is a 12-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_15_PCREL_R, ++ ++/* This is an 18-bit absolute reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_21, ++ ++/* This is an 18-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_21_PCREL, ++ ++/* This is an 18-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_21_PCREL_R, ++ ++/* This is a 32-bit absolute reloc. */ ++ BFD_RELOC_D30V_32, ++ ++/* This is a 32-bit pc-relative reloc. */ ++ BFD_RELOC_D30V_32_PCREL, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_HI16_S, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_LO16, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_JMP26, ++ ++/* Renesas M16C/M32C Relocations. */ ++ BFD_RELOC_M16C_8_PCREL8, ++ BFD_RELOC_M16C_16_PCREL8, ++ BFD_RELOC_M16C_8_PCREL16, ++ BFD_RELOC_M16C_8_ELABEL24, ++ BFD_RELOC_M16C_8_ABS16, ++ BFD_RELOC_M16C_16_ABS16, ++ BFD_RELOC_M16C_16_ABS24, ++ BFD_RELOC_M16C_16_ABS32, ++ BFD_RELOC_M16C_24_ABS16, ++ BFD_RELOC_M16C_24_ABS24, ++ BFD_RELOC_M16C_24_ABS32, ++ BFD_RELOC_M16C_32_ABS16, ++ BFD_RELOC_M16C_32_ABS24, ++ BFD_RELOC_M16C_32_ABS32, ++ BFD_RELOC_M16C_40_ABS16, ++ BFD_RELOC_M16C_40_ABS24, ++ BFD_RELOC_M16C_40_ABS32, ++ ++/* Renesas M32R (formerly Mitsubishi M32R) relocs. ++This is a 24 bit absolute address. */ ++ BFD_RELOC_M32R_24, ++ ++/* This is a 10-bit pc-relative reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_10_PCREL, ++ ++/* This is an 18-bit reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_18_PCREL, ++ ++/* This is a 26-bit reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_26_PCREL, ++ ++/* This is a 16-bit reloc containing the high 16 bits of an address ++used when the lower 16 bits are treated as unsigned. */ ++ BFD_RELOC_M32R_HI16_ULO, ++ ++/* This is a 16-bit reloc containing the high 16 bits of an address ++used when the lower 16 bits are treated as signed. */ ++ BFD_RELOC_M32R_HI16_SLO, ++ ++/* This is a 16-bit reloc containing the lower 16 bits of an address. */ ++ BFD_RELOC_M32R_LO16, ++ ++/* This is a 16-bit reloc containing the small data area offset for use in ++add3, load, and store instructions. */ ++ BFD_RELOC_M32R_SDA16, ++ ++/* For PIC. */ ++ BFD_RELOC_M32R_GOT24, ++ BFD_RELOC_M32R_26_PLTREL, ++ BFD_RELOC_M32R_COPY, ++ BFD_RELOC_M32R_GLOB_DAT, ++ BFD_RELOC_M32R_JMP_SLOT, ++ BFD_RELOC_M32R_RELATIVE, ++ BFD_RELOC_M32R_GOTOFF, ++ BFD_RELOC_M32R_GOTOFF_HI_ULO, ++ BFD_RELOC_M32R_GOTOFF_HI_SLO, ++ BFD_RELOC_M32R_GOTOFF_LO, ++ BFD_RELOC_M32R_GOTPC24, ++ BFD_RELOC_M32R_GOT16_HI_ULO, ++ BFD_RELOC_M32R_GOT16_HI_SLO, ++ BFD_RELOC_M32R_GOT16_LO, ++ BFD_RELOC_M32R_GOTPC_HI_ULO, ++ BFD_RELOC_M32R_GOTPC_HI_SLO, ++ BFD_RELOC_M32R_GOTPC_LO, ++ ++/* This is a 9-bit reloc */ ++ BFD_RELOC_V850_9_PCREL, ++ ++/* This is a 22-bit reloc */ ++ BFD_RELOC_V850_22_PCREL, ++ ++/* This is a 16 bit offset from the short data area pointer. */ ++ BFD_RELOC_V850_SDA_16_16_OFFSET, ++ ++/* This is a 16 bit offset (of which only 15 bits are used) from the ++short data area pointer. */ ++ BFD_RELOC_V850_SDA_15_16_OFFSET, ++ ++/* This is a 16 bit offset from the zero data area pointer. */ ++ BFD_RELOC_V850_ZDA_16_16_OFFSET, ++ ++/* This is a 16 bit offset (of which only 15 bits are used) from the ++zero data area pointer. */ ++ BFD_RELOC_V850_ZDA_15_16_OFFSET, ++ ++/* This is an 8 bit offset (of which only 6 bits are used) from the ++tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_6_8_OFFSET, ++ ++/* This is an 8bit offset (of which only 7 bits are used) from the tiny ++data area pointer. */ ++ BFD_RELOC_V850_TDA_7_8_OFFSET, ++ ++/* This is a 7 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_7_7_OFFSET, ++ ++/* This is a 16 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_16_16_OFFSET, ++ ++/* This is a 5 bit offset (of which only 4 bits are used) from the tiny ++data area pointer. */ ++ BFD_RELOC_V850_TDA_4_5_OFFSET, ++ ++/* This is a 4 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_4_4_OFFSET, ++ ++/* This is a 16 bit offset from the short data area pointer, with the ++bits placed non-contiguously in the instruction. */ ++ BFD_RELOC_V850_SDA_16_16_SPLIT_OFFSET, ++ ++/* This is a 16 bit offset from the zero data area pointer, with the ++bits placed non-contiguously in the instruction. */ ++ BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET, ++ ++/* This is a 6 bit offset from the call table base pointer. */ ++ BFD_RELOC_V850_CALLT_6_7_OFFSET, ++ ++/* This is a 16 bit offset from the call table base pointer. */ ++ BFD_RELOC_V850_CALLT_16_16_OFFSET, ++ ++/* Used for relaxing indirect function calls. */ ++ BFD_RELOC_V850_LONGCALL, ++ ++/* Used for relaxing indirect jumps. */ ++ BFD_RELOC_V850_LONGJUMP, ++ ++/* Used to maintain alignment whilst relaxing. */ ++ BFD_RELOC_V850_ALIGN, ++ ++/* This is a variation of BFD_RELOC_LO16 that can be used in v850e ld.bu ++instructions. */ ++ BFD_RELOC_V850_LO16_SPLIT_OFFSET, ++ ++/* This is a 32bit pcrel reloc for the mn10300, offset by two bytes in the ++instruction. */ ++ BFD_RELOC_MN10300_32_PCREL, ++ ++/* This is a 16bit pcrel reloc for the mn10300, offset by two bytes in the ++instruction. */ ++ BFD_RELOC_MN10300_16_PCREL, ++ ++/* This is a 8bit DP reloc for the tms320c30, where the most ++significant 8 bits of a 24 bit word are placed into the least ++significant 8 bits of the opcode. */ ++ BFD_RELOC_TIC30_LDP, ++ ++/* This is a 7bit reloc for the tms320c54x, where the least ++significant 7 bits of a 16 bit word are placed into the least ++significant 7 bits of the opcode. */ ++ BFD_RELOC_TIC54X_PARTLS7, ++ ++/* This is a 9bit DP reloc for the tms320c54x, where the most ++significant 9 bits of a 16 bit word are placed into the least ++significant 9 bits of the opcode. */ ++ BFD_RELOC_TIC54X_PARTMS9, ++ ++/* This is an extended address 23-bit reloc for the tms320c54x. */ ++ BFD_RELOC_TIC54X_23, ++ ++/* This is a 16-bit reloc for the tms320c54x, where the least ++significant 16 bits of a 23-bit extended address are placed into ++the opcode. */ ++ BFD_RELOC_TIC54X_16_OF_23, ++ ++/* This is a reloc for the tms320c54x, where the most ++significant 7 bits of a 23-bit extended address are placed into ++the opcode. */ ++ BFD_RELOC_TIC54X_MS7_OF_23, ++ ++/* This is a 48 bit reloc for the FR30 that stores 32 bits. */ ++ BFD_RELOC_FR30_48, ++ ++/* This is a 32 bit reloc for the FR30 that stores 20 bits split up into ++two sections. */ ++ BFD_RELOC_FR30_20, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 6 bit word offset in ++4 bits. */ ++ BFD_RELOC_FR30_6_IN_4, ++ ++/* This is a 16 bit reloc for the FR30 that stores an 8 bit byte offset ++into 8 bits. */ ++ BFD_RELOC_FR30_8_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 9 bit short offset ++into 8 bits. */ ++ BFD_RELOC_FR30_9_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 10 bit word offset ++into 8 bits. */ ++ BFD_RELOC_FR30_10_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 9 bit pc relative ++short offset into 8 bits. */ ++ BFD_RELOC_FR30_9_PCREL, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 12 bit pc relative ++short offset into 11 bits. */ ++ BFD_RELOC_FR30_12_PCREL, ++ ++/* Motorola Mcore relocations. */ ++ BFD_RELOC_MCORE_PCREL_IMM8BY4, ++ BFD_RELOC_MCORE_PCREL_IMM11BY2, ++ BFD_RELOC_MCORE_PCREL_IMM4BY2, ++ BFD_RELOC_MCORE_PCREL_32, ++ BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2, ++ BFD_RELOC_MCORE_RVA, ++ ++/* These are relocations for the GETA instruction. */ ++ BFD_RELOC_MMIX_GETA, ++ BFD_RELOC_MMIX_GETA_1, ++ BFD_RELOC_MMIX_GETA_2, ++ BFD_RELOC_MMIX_GETA_3, ++ ++/* These are relocations for a conditional branch instruction. */ ++ BFD_RELOC_MMIX_CBRANCH, ++ BFD_RELOC_MMIX_CBRANCH_J, ++ BFD_RELOC_MMIX_CBRANCH_1, ++ BFD_RELOC_MMIX_CBRANCH_2, ++ BFD_RELOC_MMIX_CBRANCH_3, ++ ++/* These are relocations for the PUSHJ instruction. */ ++ BFD_RELOC_MMIX_PUSHJ, ++ BFD_RELOC_MMIX_PUSHJ_1, ++ BFD_RELOC_MMIX_PUSHJ_2, ++ BFD_RELOC_MMIX_PUSHJ_3, ++ BFD_RELOC_MMIX_PUSHJ_STUBBABLE, ++ ++/* These are relocations for the JMP instruction. */ ++ BFD_RELOC_MMIX_JMP, ++ BFD_RELOC_MMIX_JMP_1, ++ BFD_RELOC_MMIX_JMP_2, ++ BFD_RELOC_MMIX_JMP_3, ++ ++/* This is a relocation for a relative address as in a GETA instruction or ++a branch. */ ++ BFD_RELOC_MMIX_ADDR19, ++ ++/* This is a relocation for a relative address as in a JMP instruction. */ ++ BFD_RELOC_MMIX_ADDR27, ++ ++/* This is a relocation for an instruction field that may be a general ++register or a value 0..255. */ ++ BFD_RELOC_MMIX_REG_OR_BYTE, ++ ++/* This is a relocation for an instruction field that may be a general ++register. */ ++ BFD_RELOC_MMIX_REG, ++ ++/* This is a relocation for two instruction fields holding a register and ++an offset, the equivalent of the relocation. */ ++ BFD_RELOC_MMIX_BASE_PLUS_OFFSET, ++ ++/* This relocation is an assertion that the expression is not allocated as ++a global register. It does not modify contents. */ ++ BFD_RELOC_MMIX_LOCAL, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit pc relative ++short offset into 7 bits. */ ++ BFD_RELOC_AVR_7_PCREL, ++ ++/* This is a 16 bit reloc for the AVR that stores 13 bit pc relative ++short offset into 12 bits. */ ++ BFD_RELOC_AVR_13_PCREL, ++ ++/* This is a 16 bit reloc for the AVR that stores 17 bit value (usually ++program memory address) into 16 bits. */ ++ BFD_RELOC_AVR_16_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (usually ++data memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_LO8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit ++of data memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HI8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit ++of program memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HH8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(usually data memory address) into 8 bit immediate value of SUBI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 8 bit of data memory address) into 8 bit immediate value of ++SUBI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(most high 8 bit of program memory address) into 8 bit immediate value ++of LDI or SUBI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (usually ++command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit ++of command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit ++of command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(usually command address) into 8 bit immediate value of SUBI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_PM_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 8 bit of 16 bit command address) into 8 bit immediate value ++of SUBI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_PM_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 6 bit of 22 bit command address) into 8 bit immediate ++value of SUBI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_PM_NEG, ++ ++/* This is a 32 bit reloc for the AVR that stores 23 bit value ++into 22 bits. */ ++ BFD_RELOC_AVR_CALL, ++ ++/* This is a 16 bit reloc for the AVR that stores all needed bits ++for absolute addressing with ldi with overflow check to linktime */ ++ BFD_RELOC_AVR_LDI, ++ ++/* This is a 6 bit reloc for the AVR that stores offset for ldd/std ++instructions */ ++ BFD_RELOC_AVR_6, ++ ++/* This is a 6 bit reloc for the AVR that stores offset for adiw/sbiw ++instructions */ ++ BFD_RELOC_AVR_6_ADIW, ++ ++/* Direct 12 bit. */ ++ BFD_RELOC_390_12, ++ ++/* 12 bit GOT offset. */ ++ BFD_RELOC_390_GOT12, ++ ++/* 32 bit PC relative PLT address. */ ++ BFD_RELOC_390_PLT32, ++ ++/* Copy symbol at runtime. */ ++ BFD_RELOC_390_COPY, ++ ++/* Create GOT entry. */ ++ BFD_RELOC_390_GLOB_DAT, ++ ++/* Create PLT entry. */ ++ BFD_RELOC_390_JMP_SLOT, ++ ++/* Adjust by program base. */ ++ BFD_RELOC_390_RELATIVE, ++ ++/* 32 bit PC relative offset to GOT. */ ++ BFD_RELOC_390_GOTPC, ++ ++/* 16 bit GOT offset. */ ++ BFD_RELOC_390_GOT16, ++ ++/* PC relative 16 bit shifted by 1. */ ++ BFD_RELOC_390_PC16DBL, ++ ++/* 16 bit PC rel. PLT shifted by 1. */ ++ BFD_RELOC_390_PLT16DBL, ++ ++/* PC relative 32 bit shifted by 1. */ ++ BFD_RELOC_390_PC32DBL, ++ ++/* 32 bit PC rel. PLT shifted by 1. */ ++ BFD_RELOC_390_PLT32DBL, ++ ++/* 32 bit PC rel. GOT shifted by 1. */ ++ BFD_RELOC_390_GOTPCDBL, ++ ++/* 64 bit GOT offset. */ ++ BFD_RELOC_390_GOT64, ++ ++/* 64 bit PC relative PLT address. */ ++ BFD_RELOC_390_PLT64, ++ ++/* 32 bit rel. offset to GOT entry. */ ++ BFD_RELOC_390_GOTENT, ++ ++/* 64 bit offset to GOT. */ ++ BFD_RELOC_390_GOTOFF64, ++ ++/* 12-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT12, ++ ++/* 16-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT16, ++ ++/* 32-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT32, ++ ++/* 64-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT64, ++ ++/* 32-bit rel. offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLTENT, ++ ++/* 16-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF16, ++ ++/* 32-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF32, ++ ++/* 64-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF64, ++ ++/* s390 tls relocations. */ ++ BFD_RELOC_390_TLS_LOAD, ++ BFD_RELOC_390_TLS_GDCALL, ++ BFD_RELOC_390_TLS_LDCALL, ++ BFD_RELOC_390_TLS_GD32, ++ BFD_RELOC_390_TLS_GD64, ++ BFD_RELOC_390_TLS_GOTIE12, ++ BFD_RELOC_390_TLS_GOTIE32, ++ BFD_RELOC_390_TLS_GOTIE64, ++ BFD_RELOC_390_TLS_LDM32, ++ BFD_RELOC_390_TLS_LDM64, ++ BFD_RELOC_390_TLS_IE32, ++ BFD_RELOC_390_TLS_IE64, ++ BFD_RELOC_390_TLS_IEENT, ++ BFD_RELOC_390_TLS_LE32, ++ BFD_RELOC_390_TLS_LE64, ++ BFD_RELOC_390_TLS_LDO32, ++ BFD_RELOC_390_TLS_LDO64, ++ BFD_RELOC_390_TLS_DTPMOD, ++ BFD_RELOC_390_TLS_DTPOFF, ++ BFD_RELOC_390_TLS_TPOFF, ++ ++/* Long displacement extension. */ ++ BFD_RELOC_390_20, ++ BFD_RELOC_390_GOT20, ++ BFD_RELOC_390_GOTPLT20, ++ BFD_RELOC_390_TLS_GOTIE20, ++ ++/* Scenix IP2K - 9-bit register number / data address */ ++ BFD_RELOC_IP2K_FR9, ++ ++/* Scenix IP2K - 4-bit register/data bank number */ ++ BFD_RELOC_IP2K_BANK, ++ ++/* Scenix IP2K - low 13 bits of instruction word address */ ++ BFD_RELOC_IP2K_ADDR16CJP, ++ ++/* Scenix IP2K - high 3 bits of instruction word address */ ++ BFD_RELOC_IP2K_PAGE3, ++ ++/* Scenix IP2K - ext/low/high 8 bits of data address */ ++ BFD_RELOC_IP2K_LO8DATA, ++ BFD_RELOC_IP2K_HI8DATA, ++ BFD_RELOC_IP2K_EX8DATA, ++ ++/* Scenix IP2K - low/high 8 bits of instruction word address */ ++ BFD_RELOC_IP2K_LO8INSN, ++ BFD_RELOC_IP2K_HI8INSN, ++ ++/* Scenix IP2K - even/odd PC modifier to modify snb pcl.0 */ ++ BFD_RELOC_IP2K_PC_SKIP, ++ ++/* Scenix IP2K - 16 bit word address in text section. */ ++ BFD_RELOC_IP2K_TEXT, ++ ++/* Scenix IP2K - 7-bit sp or dp offset */ ++ BFD_RELOC_IP2K_FR_OFFSET, ++ ++/* Scenix VPE4K coprocessor - data/insn-space addressing */ ++ BFD_RELOC_VPE4KMATH_DATA, ++ BFD_RELOC_VPE4KMATH_INSN, ++ ++/* These two relocations are used by the linker to determine which of ++the entries in a C++ virtual function table are actually used. When ++the --gc-sections option is given, the linker will zero out the entries ++that are not used, so that the code for those functions need not be ++included in the output. ++ ++VTABLE_INHERIT is a zero-space relocation used to describe to the ++linker the inheritance tree of a C++ virtual function table. The ++relocation's symbol should be the parent class' vtable, and the ++relocation should be located at the child vtable. ++ ++VTABLE_ENTRY is a zero-space relocation that describes the use of a ++virtual function table entry. The reloc's symbol should refer to the ++table of the class mentioned in the code. Off of that base, an offset ++describes the entry that is being used. For Rela hosts, this offset ++is stored in the reloc's addend. For Rel hosts, we are forced to put ++this offset in the reloc's section offset. */ ++ BFD_RELOC_VTABLE_INHERIT, ++ BFD_RELOC_VTABLE_ENTRY, ++ ++/* Intel IA64 Relocations. */ ++ BFD_RELOC_IA64_IMM14, ++ BFD_RELOC_IA64_IMM22, ++ BFD_RELOC_IA64_IMM64, ++ BFD_RELOC_IA64_DIR32MSB, ++ BFD_RELOC_IA64_DIR32LSB, ++ BFD_RELOC_IA64_DIR64MSB, ++ BFD_RELOC_IA64_DIR64LSB, ++ BFD_RELOC_IA64_GPREL22, ++ BFD_RELOC_IA64_GPREL64I, ++ BFD_RELOC_IA64_GPREL32MSB, ++ BFD_RELOC_IA64_GPREL32LSB, ++ BFD_RELOC_IA64_GPREL64MSB, ++ BFD_RELOC_IA64_GPREL64LSB, ++ BFD_RELOC_IA64_LTOFF22, ++ BFD_RELOC_IA64_LTOFF64I, ++ BFD_RELOC_IA64_PLTOFF22, ++ BFD_RELOC_IA64_PLTOFF64I, ++ BFD_RELOC_IA64_PLTOFF64MSB, ++ BFD_RELOC_IA64_PLTOFF64LSB, ++ BFD_RELOC_IA64_FPTR64I, ++ BFD_RELOC_IA64_FPTR32MSB, ++ BFD_RELOC_IA64_FPTR32LSB, ++ BFD_RELOC_IA64_FPTR64MSB, ++ BFD_RELOC_IA64_FPTR64LSB, ++ BFD_RELOC_IA64_PCREL21B, ++ BFD_RELOC_IA64_PCREL21BI, ++ BFD_RELOC_IA64_PCREL21M, ++ BFD_RELOC_IA64_PCREL21F, ++ BFD_RELOC_IA64_PCREL22, ++ BFD_RELOC_IA64_PCREL60B, ++ BFD_RELOC_IA64_PCREL64I, ++ BFD_RELOC_IA64_PCREL32MSB, ++ BFD_RELOC_IA64_PCREL32LSB, ++ BFD_RELOC_IA64_PCREL64MSB, ++ BFD_RELOC_IA64_PCREL64LSB, ++ BFD_RELOC_IA64_LTOFF_FPTR22, ++ BFD_RELOC_IA64_LTOFF_FPTR64I, ++ BFD_RELOC_IA64_LTOFF_FPTR32MSB, ++ BFD_RELOC_IA64_LTOFF_FPTR32LSB, ++ BFD_RELOC_IA64_LTOFF_FPTR64MSB, ++ BFD_RELOC_IA64_LTOFF_FPTR64LSB, ++ BFD_RELOC_IA64_SEGREL32MSB, ++ BFD_RELOC_IA64_SEGREL32LSB, ++ BFD_RELOC_IA64_SEGREL64MSB, ++ BFD_RELOC_IA64_SEGREL64LSB, ++ BFD_RELOC_IA64_SECREL32MSB, ++ BFD_RELOC_IA64_SECREL32LSB, ++ BFD_RELOC_IA64_SECREL64MSB, ++ BFD_RELOC_IA64_SECREL64LSB, ++ BFD_RELOC_IA64_REL32MSB, ++ BFD_RELOC_IA64_REL32LSB, ++ BFD_RELOC_IA64_REL64MSB, ++ BFD_RELOC_IA64_REL64LSB, ++ BFD_RELOC_IA64_LTV32MSB, ++ BFD_RELOC_IA64_LTV32LSB, ++ BFD_RELOC_IA64_LTV64MSB, ++ BFD_RELOC_IA64_LTV64LSB, ++ BFD_RELOC_IA64_IPLTMSB, ++ BFD_RELOC_IA64_IPLTLSB, ++ BFD_RELOC_IA64_COPY, ++ BFD_RELOC_IA64_LTOFF22X, ++ BFD_RELOC_IA64_LDXMOV, ++ BFD_RELOC_IA64_TPREL14, ++ BFD_RELOC_IA64_TPREL22, ++ BFD_RELOC_IA64_TPREL64I, ++ BFD_RELOC_IA64_TPREL64MSB, ++ BFD_RELOC_IA64_TPREL64LSB, ++ BFD_RELOC_IA64_LTOFF_TPREL22, ++ BFD_RELOC_IA64_DTPMOD64MSB, ++ BFD_RELOC_IA64_DTPMOD64LSB, ++ BFD_RELOC_IA64_LTOFF_DTPMOD22, ++ BFD_RELOC_IA64_DTPREL14, ++ BFD_RELOC_IA64_DTPREL22, ++ BFD_RELOC_IA64_DTPREL64I, ++ BFD_RELOC_IA64_DTPREL32MSB, ++ BFD_RELOC_IA64_DTPREL32LSB, ++ BFD_RELOC_IA64_DTPREL64MSB, ++ BFD_RELOC_IA64_DTPREL64LSB, ++ BFD_RELOC_IA64_LTOFF_DTPREL22, ++ ++/* Motorola 68HC11 reloc. ++This is the 8 bit high part of an absolute address. */ ++ BFD_RELOC_M68HC11_HI8, ++ ++/* Motorola 68HC11 reloc. ++This is the 8 bit low part of an absolute address. */ ++ BFD_RELOC_M68HC11_LO8, ++ ++/* Motorola 68HC11 reloc. ++This is the 3 bit of a value. */ ++ BFD_RELOC_M68HC11_3B, ++ ++/* Motorola 68HC11 reloc. ++This reloc marks the beginning of a jump/call instruction. ++It is used for linker relaxation to correctly identify beginning ++of instruction and change some branches to use PC-relative ++addressing mode. */ ++ BFD_RELOC_M68HC11_RL_JUMP, ++ ++/* Motorola 68HC11 reloc. ++This reloc marks a group of several instructions that gcc generates ++and for which the linker relaxation pass can modify and/or remove ++some of them. */ ++ BFD_RELOC_M68HC11_RL_GROUP, ++ ++/* Motorola 68HC11 reloc. ++This is the 16-bit lower part of an address. It is used for 'call' ++instruction to specify the symbol address without any special ++transformation (due to memory bank window). */ ++ BFD_RELOC_M68HC11_LO16, ++ ++/* Motorola 68HC11 reloc. ++This is a 8-bit reloc that specifies the page number of an address. ++It is used by 'call' instruction to specify the page number of ++the symbol. */ ++ BFD_RELOC_M68HC11_PAGE, ++ ++/* Motorola 68HC11 reloc. ++This is a 24-bit reloc that represents the address with a 16-bit ++value and a 8-bit page number. The symbol address is transformed ++to follow the 16K memory bank of 68HC12 (seen as mapped in the window). */ ++ BFD_RELOC_M68HC11_24, ++ ++/* Motorola 68HC12 reloc. ++This is the 5 bits of a value. */ ++ BFD_RELOC_M68HC12_5B, ++ ++/* NS CR16C Relocations. */ ++ BFD_RELOC_16C_NUM08, ++ BFD_RELOC_16C_NUM08_C, ++ BFD_RELOC_16C_NUM16, ++ BFD_RELOC_16C_NUM16_C, ++ BFD_RELOC_16C_NUM32, ++ BFD_RELOC_16C_NUM32_C, ++ BFD_RELOC_16C_DISP04, ++ BFD_RELOC_16C_DISP04_C, ++ BFD_RELOC_16C_DISP08, ++ BFD_RELOC_16C_DISP08_C, ++ BFD_RELOC_16C_DISP16, ++ BFD_RELOC_16C_DISP16_C, ++ BFD_RELOC_16C_DISP24, ++ BFD_RELOC_16C_DISP24_C, ++ BFD_RELOC_16C_DISP24a, ++ BFD_RELOC_16C_DISP24a_C, ++ BFD_RELOC_16C_REG04, ++ BFD_RELOC_16C_REG04_C, ++ BFD_RELOC_16C_REG04a, ++ BFD_RELOC_16C_REG04a_C, ++ BFD_RELOC_16C_REG14, ++ BFD_RELOC_16C_REG14_C, ++ BFD_RELOC_16C_REG16, ++ BFD_RELOC_16C_REG16_C, ++ BFD_RELOC_16C_REG20, ++ BFD_RELOC_16C_REG20_C, ++ BFD_RELOC_16C_ABS20, ++ BFD_RELOC_16C_ABS20_C, ++ BFD_RELOC_16C_ABS24, ++ BFD_RELOC_16C_ABS24_C, ++ BFD_RELOC_16C_IMM04, ++ BFD_RELOC_16C_IMM04_C, ++ BFD_RELOC_16C_IMM16, ++ BFD_RELOC_16C_IMM16_C, ++ BFD_RELOC_16C_IMM20, ++ BFD_RELOC_16C_IMM20_C, ++ BFD_RELOC_16C_IMM24, ++ BFD_RELOC_16C_IMM24_C, ++ BFD_RELOC_16C_IMM32, ++ BFD_RELOC_16C_IMM32_C, ++ ++/* NS CRX Relocations. */ ++ BFD_RELOC_CRX_REL4, ++ BFD_RELOC_CRX_REL8, ++ BFD_RELOC_CRX_REL8_CMP, ++ BFD_RELOC_CRX_REL16, ++ BFD_RELOC_CRX_REL24, ++ BFD_RELOC_CRX_REL32, ++ BFD_RELOC_CRX_REGREL12, ++ BFD_RELOC_CRX_REGREL22, ++ BFD_RELOC_CRX_REGREL28, ++ BFD_RELOC_CRX_REGREL32, ++ BFD_RELOC_CRX_ABS16, ++ BFD_RELOC_CRX_ABS32, ++ BFD_RELOC_CRX_NUM8, ++ BFD_RELOC_CRX_NUM16, ++ BFD_RELOC_CRX_NUM32, ++ BFD_RELOC_CRX_IMM16, ++ BFD_RELOC_CRX_IMM32, ++ BFD_RELOC_CRX_SWITCH8, ++ BFD_RELOC_CRX_SWITCH16, ++ BFD_RELOC_CRX_SWITCH32, ++ ++/* These relocs are only used within the CRIS assembler. They are not ++(at present) written to any object files. */ ++ BFD_RELOC_CRIS_BDISP8, ++ BFD_RELOC_CRIS_UNSIGNED_5, ++ BFD_RELOC_CRIS_SIGNED_6, ++ BFD_RELOC_CRIS_UNSIGNED_6, ++ BFD_RELOC_CRIS_SIGNED_8, ++ BFD_RELOC_CRIS_UNSIGNED_8, ++ BFD_RELOC_CRIS_SIGNED_16, ++ BFD_RELOC_CRIS_UNSIGNED_16, ++ BFD_RELOC_CRIS_LAPCQ_OFFSET, ++ BFD_RELOC_CRIS_UNSIGNED_4, ++ ++/* Relocs used in ELF shared libraries for CRIS. */ ++ BFD_RELOC_CRIS_COPY, ++ BFD_RELOC_CRIS_GLOB_DAT, ++ BFD_RELOC_CRIS_JUMP_SLOT, ++ BFD_RELOC_CRIS_RELATIVE, ++ ++/* 32-bit offset to symbol-entry within GOT. */ ++ BFD_RELOC_CRIS_32_GOT, ++ ++/* 16-bit offset to symbol-entry within GOT. */ ++ BFD_RELOC_CRIS_16_GOT, ++ ++/* 32-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_CRIS_32_GOTPLT, ++ ++/* 16-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_CRIS_16_GOTPLT, ++ ++/* 32-bit offset to symbol, relative to GOT. */ ++ BFD_RELOC_CRIS_32_GOTREL, ++ ++/* 32-bit offset to symbol with PLT entry, relative to GOT. */ ++ BFD_RELOC_CRIS_32_PLT_GOTREL, ++ ++/* 32-bit offset to symbol with PLT entry, relative to this relocation. */ ++ BFD_RELOC_CRIS_32_PLT_PCREL, ++ ++/* Intel i860 Relocations. */ ++ BFD_RELOC_860_COPY, ++ BFD_RELOC_860_GLOB_DAT, ++ BFD_RELOC_860_JUMP_SLOT, ++ BFD_RELOC_860_RELATIVE, ++ BFD_RELOC_860_PC26, ++ BFD_RELOC_860_PLT26, ++ BFD_RELOC_860_PC16, ++ BFD_RELOC_860_LOW0, ++ BFD_RELOC_860_SPLIT0, ++ BFD_RELOC_860_LOW1, ++ BFD_RELOC_860_SPLIT1, ++ BFD_RELOC_860_LOW2, ++ BFD_RELOC_860_SPLIT2, ++ BFD_RELOC_860_LOW3, ++ BFD_RELOC_860_LOGOT0, ++ BFD_RELOC_860_SPGOT0, ++ BFD_RELOC_860_LOGOT1, ++ BFD_RELOC_860_SPGOT1, ++ BFD_RELOC_860_LOGOTOFF0, ++ BFD_RELOC_860_SPGOTOFF0, ++ BFD_RELOC_860_LOGOTOFF1, ++ BFD_RELOC_860_SPGOTOFF1, ++ BFD_RELOC_860_LOGOTOFF2, ++ BFD_RELOC_860_LOGOTOFF3, ++ BFD_RELOC_860_LOPC, ++ BFD_RELOC_860_HIGHADJ, ++ BFD_RELOC_860_HAGOT, ++ BFD_RELOC_860_HAGOTOFF, ++ BFD_RELOC_860_HAPC, ++ BFD_RELOC_860_HIGH, ++ BFD_RELOC_860_HIGOT, ++ BFD_RELOC_860_HIGOTOFF, ++ ++/* OpenRISC Relocations. */ ++ BFD_RELOC_OPENRISC_ABS_26, ++ BFD_RELOC_OPENRISC_REL_26, ++ ++/* H8 elf Relocations. */ ++ BFD_RELOC_H8_DIR16A8, ++ BFD_RELOC_H8_DIR16R8, ++ BFD_RELOC_H8_DIR24A8, ++ BFD_RELOC_H8_DIR24R8, ++ BFD_RELOC_H8_DIR32A16, ++ ++/* Sony Xstormy16 Relocations. */ ++ BFD_RELOC_XSTORMY16_REL_12, ++ BFD_RELOC_XSTORMY16_12, ++ BFD_RELOC_XSTORMY16_24, ++ BFD_RELOC_XSTORMY16_FPTR16, ++ ++/* Relocations used by VAX ELF. */ ++ BFD_RELOC_VAX_GLOB_DAT, ++ BFD_RELOC_VAX_JMP_SLOT, ++ BFD_RELOC_VAX_RELATIVE, ++ ++/* Morpho MS1 - 16 bit immediate relocation. */ ++ BFD_RELOC_MS1_PC16, ++ ++/* Morpho MS1 - Hi 16 bits of an address. */ ++ BFD_RELOC_MS1_HI16, ++ ++/* Morpho MS1 - Low 16 bits of an address. */ ++ BFD_RELOC_MS1_LO16, ++ ++/* Morpho MS1 - Used to tell the linker which vtable entries are used. */ ++ BFD_RELOC_MS1_GNU_VTINHERIT, ++ ++/* Morpho MS1 - Used to tell the linker which vtable entries are used. */ ++ BFD_RELOC_MS1_GNU_VTENTRY, ++ ++/* msp430 specific relocation codes */ ++ BFD_RELOC_MSP430_10_PCREL, ++ BFD_RELOC_MSP430_16_PCREL, ++ BFD_RELOC_MSP430_16, ++ BFD_RELOC_MSP430_16_PCREL_BYTE, ++ BFD_RELOC_MSP430_16_BYTE, ++ BFD_RELOC_MSP430_2X_PCREL, ++ BFD_RELOC_MSP430_RL_PCREL, ++ ++/* IQ2000 Relocations. */ ++ BFD_RELOC_IQ2000_OFFSET_16, ++ BFD_RELOC_IQ2000_OFFSET_21, ++ BFD_RELOC_IQ2000_UHI16, ++ ++/* Special Xtensa relocation used only by PLT entries in ELF shared ++objects to indicate that the runtime linker should set the value ++to one of its own internal functions or data structures. */ ++ BFD_RELOC_XTENSA_RTLD, ++ ++/* Xtensa relocations for ELF shared objects. */ ++ BFD_RELOC_XTENSA_GLOB_DAT, ++ BFD_RELOC_XTENSA_JMP_SLOT, ++ BFD_RELOC_XTENSA_RELATIVE, ++ ++/* Xtensa relocation used in ELF object files for symbols that may require ++PLT entries. Otherwise, this is just a generic 32-bit relocation. */ ++ BFD_RELOC_XTENSA_PLT, ++ ++/* Xtensa relocations to mark the difference of two local symbols. ++These are only needed to support linker relaxation and can be ignored ++when not relaxing. The field is set to the value of the difference ++assuming no relaxation. The relocation encodes the position of the ++first symbol so the linker can determine whether to adjust the field ++value. */ ++ BFD_RELOC_XTENSA_DIFF8, ++ BFD_RELOC_XTENSA_DIFF16, ++ BFD_RELOC_XTENSA_DIFF32, ++ ++/* Generic Xtensa relocations for instruction operands. Only the slot ++number is encoded in the relocation. The relocation applies to the ++last PC-relative immediate operand, or if there are no PC-relative ++immediates, to the last immediate operand. */ ++ BFD_RELOC_XTENSA_SLOT0_OP, ++ BFD_RELOC_XTENSA_SLOT1_OP, ++ BFD_RELOC_XTENSA_SLOT2_OP, ++ BFD_RELOC_XTENSA_SLOT3_OP, ++ BFD_RELOC_XTENSA_SLOT4_OP, ++ BFD_RELOC_XTENSA_SLOT5_OP, ++ BFD_RELOC_XTENSA_SLOT6_OP, ++ BFD_RELOC_XTENSA_SLOT7_OP, ++ BFD_RELOC_XTENSA_SLOT8_OP, ++ BFD_RELOC_XTENSA_SLOT9_OP, ++ BFD_RELOC_XTENSA_SLOT10_OP, ++ BFD_RELOC_XTENSA_SLOT11_OP, ++ BFD_RELOC_XTENSA_SLOT12_OP, ++ BFD_RELOC_XTENSA_SLOT13_OP, ++ BFD_RELOC_XTENSA_SLOT14_OP, ++ ++/* Alternate Xtensa relocations. Only the slot is encoded in the ++relocation. The meaning of these relocations is opcode-specific. */ ++ BFD_RELOC_XTENSA_SLOT0_ALT, ++ BFD_RELOC_XTENSA_SLOT1_ALT, ++ BFD_RELOC_XTENSA_SLOT2_ALT, ++ BFD_RELOC_XTENSA_SLOT3_ALT, ++ BFD_RELOC_XTENSA_SLOT4_ALT, ++ BFD_RELOC_XTENSA_SLOT5_ALT, ++ BFD_RELOC_XTENSA_SLOT6_ALT, ++ BFD_RELOC_XTENSA_SLOT7_ALT, ++ BFD_RELOC_XTENSA_SLOT8_ALT, ++ BFD_RELOC_XTENSA_SLOT9_ALT, ++ BFD_RELOC_XTENSA_SLOT10_ALT, ++ BFD_RELOC_XTENSA_SLOT11_ALT, ++ BFD_RELOC_XTENSA_SLOT12_ALT, ++ BFD_RELOC_XTENSA_SLOT13_ALT, ++ BFD_RELOC_XTENSA_SLOT14_ALT, ++ ++/* Xtensa relocations for backward compatibility. These have all been ++replaced by BFD_RELOC_XTENSA_SLOT0_OP. */ ++ BFD_RELOC_XTENSA_OP0, ++ BFD_RELOC_XTENSA_OP1, ++ BFD_RELOC_XTENSA_OP2, ++ ++/* Xtensa relocation to mark that the assembler expanded the ++instructions from an original target. The expansion size is ++encoded in the reloc size. */ ++ BFD_RELOC_XTENSA_ASM_EXPAND, ++ ++/* Xtensa relocation to mark that the linker should simplify ++assembler-expanded instructions. This is commonly used ++internally by the linker after analysis of a ++BFD_RELOC_XTENSA_ASM_EXPAND. */ ++ BFD_RELOC_XTENSA_ASM_SIMPLIFY, ++ BFD_RELOC_UNUSED }; ++typedef enum bfd_reloc_code_real bfd_reloc_code_real_type; ++reloc_howto_type *bfd_reloc_type_lookup ++ (bfd *abfd, bfd_reloc_code_real_type code); ++ ++const char *bfd_get_reloc_code_name (bfd_reloc_code_real_type code); ++ ++/* Extracted from syms.c. */ ++ ++typedef struct bfd_symbol ++{ ++ /* A pointer to the BFD which owns the symbol. This information ++ is necessary so that a back end can work out what additional ++ information (invisible to the application writer) is carried ++ with the symbol. ++ ++ This field is *almost* redundant, since you can use section->owner ++ instead, except that some symbols point to the global sections ++ bfd_{abs,com,und}_section. This could be fixed by making ++ these globals be per-bfd (or per-target-flavor). FIXME. */ ++ struct bfd *the_bfd; /* Use bfd_asymbol_bfd(sym) to access this field. */ ++ ++ /* The text of the symbol. The name is left alone, and not copied; the ++ application may not alter it. */ ++ const char *name; ++ ++ /* The value of the symbol. This really should be a union of a ++ numeric value with a pointer, since some flags indicate that ++ a pointer to another symbol is stored here. */ ++ symvalue value; ++ ++ /* Attributes of a symbol. */ ++#define BSF_NO_FLAGS 0x00 ++ ++ /* The symbol has local scope; <> in <>. The value ++ is the offset into the section of the data. */ ++#define BSF_LOCAL 0x01 ++ ++ /* The symbol has global scope; initialized data in <>. The ++ value is the offset into the section of the data. */ ++#define BSF_GLOBAL 0x02 ++ ++ /* The symbol has global scope and is exported. The value is ++ the offset into the section of the data. */ ++#define BSF_EXPORT BSF_GLOBAL /* No real difference. */ ++ ++ /* A normal C symbol would be one of: ++ <>, <>, <> or ++ <>. */ ++ ++ /* The symbol is a debugging record. The value has an arbitrary ++ meaning, unless BSF_DEBUGGING_RELOC is also set. */ ++#define BSF_DEBUGGING 0x08 ++ ++ /* The symbol denotes a function entry point. Used in ELF, ++ perhaps others someday. */ ++#define BSF_FUNCTION 0x10 ++ ++ /* Used by the linker. */ ++#define BSF_KEEP 0x20 ++#define BSF_KEEP_G 0x40 ++ ++ /* A weak global symbol, overridable without warnings by ++ a regular global symbol of the same name. */ ++#define BSF_WEAK 0x80 ++ ++ /* This symbol was created to point to a section, e.g. ELF's ++ STT_SECTION symbols. */ ++#define BSF_SECTION_SYM 0x100 ++ ++ /* The symbol used to be a common symbol, but now it is ++ allocated. */ ++#define BSF_OLD_COMMON 0x200 ++ ++ /* The default value for common data. */ ++#define BFD_FORT_COMM_DEFAULT_VALUE 0 ++ ++ /* In some files the type of a symbol sometimes alters its ++ location in an output file - ie in coff a <> symbol ++ which is also <> symbol appears where it was ++ declared and not at the end of a section. This bit is set ++ by the target BFD part to convey this information. */ ++#define BSF_NOT_AT_END 0x400 ++ ++ /* Signal that the symbol is the label of constructor section. */ ++#define BSF_CONSTRUCTOR 0x800 ++ ++ /* Signal that the symbol is a warning symbol. The name is a ++ warning. The name of the next symbol is the one to warn about; ++ if a reference is made to a symbol with the same name as the next ++ symbol, a warning is issued by the linker. */ ++#define BSF_WARNING 0x1000 ++ ++ /* Signal that the symbol is indirect. This symbol is an indirect ++ pointer to the symbol with the same name as the next symbol. */ ++#define BSF_INDIRECT 0x2000 ++ ++ /* BSF_FILE marks symbols that contain a file name. This is used ++ for ELF STT_FILE symbols. */ ++#define BSF_FILE 0x4000 ++ ++ /* Symbol is from dynamic linking information. */ ++#define BSF_DYNAMIC 0x8000 ++ ++ /* The symbol denotes a data object. Used in ELF, and perhaps ++ others someday. */ ++#define BSF_OBJECT 0x10000 ++ ++ /* This symbol is a debugging symbol. The value is the offset ++ into the section of the data. BSF_DEBUGGING should be set ++ as well. */ ++#define BSF_DEBUGGING_RELOC 0x20000 ++ ++ /* This symbol is thread local. Used in ELF. */ ++#define BSF_THREAD_LOCAL 0x40000 ++ ++ flagword flags; ++ ++ /* A pointer to the section to which this symbol is ++ relative. This will always be non NULL, there are special ++ sections for undefined and absolute symbols. */ ++ struct bfd_section *section; ++ ++ /* Back end special data. */ ++ union ++ { ++ void *p; ++ bfd_vma i; ++ } ++ udata; ++} ++asymbol; ++ ++#define bfd_get_symtab_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_symtab_upper_bound, (abfd)) ++ ++bfd_boolean bfd_is_local_label (bfd *abfd, asymbol *sym); ++ ++bfd_boolean bfd_is_local_label_name (bfd *abfd, const char *name); ++ ++#define bfd_is_local_label_name(abfd, name) \ ++ BFD_SEND (abfd, _bfd_is_local_label_name, (abfd, name)) ++ ++bfd_boolean bfd_is_target_special_symbol (bfd *abfd, asymbol *sym); ++ ++#define bfd_is_target_special_symbol(abfd, sym) \ ++ BFD_SEND (abfd, _bfd_is_target_special_symbol, (abfd, sym)) ++ ++#define bfd_canonicalize_symtab(abfd, location) \ ++ BFD_SEND (abfd, _bfd_canonicalize_symtab, (abfd, location)) ++ ++bfd_boolean bfd_set_symtab ++ (bfd *abfd, asymbol **location, unsigned int count); ++ ++void bfd_print_symbol_vandf (bfd *abfd, void *file, asymbol *symbol); ++ ++#define bfd_make_empty_symbol(abfd) \ ++ BFD_SEND (abfd, _bfd_make_empty_symbol, (abfd)) ++ ++asymbol *_bfd_generic_make_empty_symbol (bfd *); ++ ++#define bfd_make_debug_symbol(abfd,ptr,size) \ ++ BFD_SEND (abfd, _bfd_make_debug_symbol, (abfd, ptr, size)) ++ ++int bfd_decode_symclass (asymbol *symbol); ++ ++bfd_boolean bfd_is_undefined_symclass (int symclass); ++ ++void bfd_symbol_info (asymbol *symbol, symbol_info *ret); ++ ++bfd_boolean bfd_copy_private_symbol_data ++ (bfd *ibfd, asymbol *isym, bfd *obfd, asymbol *osym); ++ ++#define bfd_copy_private_symbol_data(ibfd, isymbol, obfd, osymbol) \ ++ BFD_SEND (obfd, _bfd_copy_private_symbol_data, \ ++ (ibfd, isymbol, obfd, osymbol)) ++ ++/* Extracted from bfd.c. */ ++struct bfd ++{ ++ /* A unique identifier of the BFD */ ++ unsigned int id; ++ ++ /* The filename the application opened the BFD with. */ ++ const char *filename; ++ ++ /* A pointer to the target jump table. */ ++ const struct bfd_target *xvec; ++ ++ /* The IOSTREAM, and corresponding IO vector that provide access ++ to the file backing the BFD. */ ++ void *iostream; ++ const struct bfd_iovec *iovec; ++ ++ /* Is the file descriptor being cached? That is, can it be closed as ++ needed, and re-opened when accessed later? */ ++ bfd_boolean cacheable; ++ ++ /* Marks whether there was a default target specified when the ++ BFD was opened. This is used to select which matching algorithm ++ to use to choose the back end. */ ++ bfd_boolean target_defaulted; ++ ++ /* The caching routines use these to maintain a ++ least-recently-used list of BFDs. */ ++ struct bfd *lru_prev, *lru_next; ++ ++ /* When a file is closed by the caching routines, BFD retains ++ state information on the file here... */ ++ ufile_ptr where; ++ ++ /* ... and here: (``once'' means at least once). */ ++ bfd_boolean opened_once; ++ ++ /* Set if we have a locally maintained mtime value, rather than ++ getting it from the file each time. */ ++ bfd_boolean mtime_set; ++ ++ /* File modified time, if mtime_set is TRUE. */ ++ long mtime; ++ ++ /* Reserved for an unimplemented file locking extension. */ ++ int ifd; ++ ++ /* The format which belongs to the BFD. (object, core, etc.) */ ++ bfd_format format; ++ ++ /* The direction with which the BFD was opened. */ ++ enum bfd_direction ++ { ++ no_direction = 0, ++ read_direction = 1, ++ write_direction = 2, ++ both_direction = 3 ++ } ++ direction; ++ ++ /* Format_specific flags. */ ++ flagword flags; ++ ++ /* Currently my_archive is tested before adding origin to ++ anything. I believe that this can become always an add of ++ origin, with origin set to 0 for non archive files. */ ++ ufile_ptr origin; ++ ++ /* Remember when output has begun, to stop strange things ++ from happening. */ ++ bfd_boolean output_has_begun; ++ ++ /* A hash table for section names. */ ++ struct bfd_hash_table section_htab; ++ ++ /* Pointer to linked list of sections. */ ++ struct bfd_section *sections; ++ ++ /* The last section on the section list. */ ++ struct bfd_section *section_last; ++ ++ /* The number of sections. */ ++ unsigned int section_count; ++ ++ /* Stuff only useful for object files: ++ The start address. */ ++ bfd_vma start_address; ++ ++ /* Used for input and output. */ ++ unsigned int symcount; ++ ++ /* Symbol table for output BFD (with symcount entries). */ ++ struct bfd_symbol **outsymbols; ++ ++ /* Used for slurped dynamic symbol tables. */ ++ unsigned int dynsymcount; ++ ++ /* Pointer to structure which contains architecture information. */ ++ const struct bfd_arch_info *arch_info; ++ ++ /* Flag set if symbols from this BFD should not be exported. */ ++ bfd_boolean no_export; ++ ++ /* Stuff only useful for archives. */ ++ void *arelt_data; ++ struct bfd *my_archive; /* The containing archive BFD. */ ++ struct bfd *next; /* The next BFD in the archive. */ ++ struct bfd *archive_head; /* The first BFD in the archive. */ ++ bfd_boolean has_armap; ++ ++ /* A chain of BFD structures involved in a link. */ ++ struct bfd *link_next; ++ ++ /* A field used by _bfd_generic_link_add_archive_symbols. This will ++ be used only for archive elements. */ ++ int archive_pass; ++ ++ /* Used by the back end to hold private data. */ ++ union ++ { ++ struct aout_data_struct *aout_data; ++ struct artdata *aout_ar_data; ++ struct _oasys_data *oasys_obj_data; ++ struct _oasys_ar_data *oasys_ar_data; ++ struct coff_tdata *coff_obj_data; ++ struct pe_tdata *pe_obj_data; ++ struct xcoff_tdata *xcoff_obj_data; ++ struct ecoff_tdata *ecoff_obj_data; ++ struct ieee_data_struct *ieee_data; ++ struct ieee_ar_data_struct *ieee_ar_data; ++ struct srec_data_struct *srec_data; ++ struct ihex_data_struct *ihex_data; ++ struct tekhex_data_struct *tekhex_data; ++ struct elf_obj_tdata *elf_obj_data; ++ struct nlm_obj_tdata *nlm_obj_data; ++ struct bout_data_struct *bout_data; ++ struct mmo_data_struct *mmo_data; ++ struct sun_core_struct *sun_core_data; ++ struct sco5_core_struct *sco5_core_data; ++ struct trad_core_struct *trad_core_data; ++ struct som_data_struct *som_data; ++ struct hpux_core_struct *hpux_core_data; ++ struct hppabsd_core_struct *hppabsd_core_data; ++ struct sgi_core_struct *sgi_core_data; ++ struct lynx_core_struct *lynx_core_data; ++ struct osf_core_struct *osf_core_data; ++ struct cisco_core_struct *cisco_core_data; ++ struct versados_data_struct *versados_data; ++ struct netbsd_core_struct *netbsd_core_data; ++ struct mach_o_data_struct *mach_o_data; ++ struct mach_o_fat_data_struct *mach_o_fat_data; ++ struct bfd_pef_data_struct *pef_data; ++ struct bfd_pef_xlib_data_struct *pef_xlib_data; ++ struct bfd_sym_data_struct *sym_data; ++ void *any; ++ } ++ tdata; ++ ++ /* Used by the application to hold private data. */ ++ void *usrdata; ++ ++ /* Where all the allocated stuff under this BFD goes. This is a ++ struct objalloc *, but we use void * to avoid requiring the inclusion ++ of objalloc.h. */ ++ void *memory; ++}; ++ ++typedef enum bfd_error ++{ ++ bfd_error_no_error = 0, ++ bfd_error_system_call, ++ bfd_error_invalid_target, ++ bfd_error_wrong_format, ++ bfd_error_wrong_object_format, ++ bfd_error_invalid_operation, ++ bfd_error_no_memory, ++ bfd_error_no_symbols, ++ bfd_error_no_armap, ++ bfd_error_no_more_archived_files, ++ bfd_error_malformed_archive, ++ bfd_error_file_not_recognized, ++ bfd_error_file_ambiguously_recognized, ++ bfd_error_no_contents, ++ bfd_error_nonrepresentable_section, ++ bfd_error_no_debug_section, ++ bfd_error_bad_value, ++ bfd_error_file_truncated, ++ bfd_error_file_too_big, ++ bfd_error_invalid_error_code ++} ++bfd_error_type; ++ ++bfd_error_type bfd_get_error (void); ++ ++void bfd_set_error (bfd_error_type error_tag); ++ ++const char *bfd_errmsg (bfd_error_type error_tag); ++ ++void bfd_perror (const char *message); ++ ++typedef void (*bfd_error_handler_type) (const char *, ...); ++ ++bfd_error_handler_type bfd_set_error_handler (bfd_error_handler_type); ++ ++void bfd_set_error_program_name (const char *); ++ ++bfd_error_handler_type bfd_get_error_handler (void); ++ ++long bfd_get_reloc_upper_bound (bfd *abfd, asection *sect); ++ ++long bfd_canonicalize_reloc ++ (bfd *abfd, asection *sec, arelent **loc, asymbol **syms); ++ ++void bfd_set_reloc ++ (bfd *abfd, asection *sec, arelent **rel, unsigned int count); ++ ++bfd_boolean bfd_set_file_flags (bfd *abfd, flagword flags); ++ ++int bfd_get_arch_size (bfd *abfd); ++ ++int bfd_get_sign_extend_vma (bfd *abfd); ++ ++bfd_boolean bfd_set_start_address (bfd *abfd, bfd_vma vma); ++ ++unsigned int bfd_get_gp_size (bfd *abfd); ++ ++void bfd_set_gp_size (bfd *abfd, unsigned int i); ++ ++bfd_vma bfd_scan_vma (const char *string, const char **end, int base); ++ ++bfd_boolean bfd_copy_private_header_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_copy_private_header_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_copy_private_header_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_copy_private_bfd_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_copy_private_bfd_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_copy_private_bfd_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_merge_private_bfd_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_merge_private_bfd_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_merge_private_bfd_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_set_private_flags (bfd *abfd, flagword flags); ++ ++#define bfd_set_private_flags(abfd, flags) \ ++ BFD_SEND (abfd, _bfd_set_private_flags, (abfd, flags)) ++#define bfd_sizeof_headers(abfd, reloc) \ ++ BFD_SEND (abfd, _bfd_sizeof_headers, (abfd, reloc)) ++ ++#define bfd_find_nearest_line(abfd, sec, syms, off, file, func, line) \ ++ BFD_SEND (abfd, _bfd_find_nearest_line, \ ++ (abfd, sec, syms, off, file, func, line)) ++ ++#define bfd_find_line(abfd, syms, sym, file, line) \ ++ BFD_SEND (abfd, _bfd_find_line, \ ++ (abfd, syms, sym, file, line)) ++ ++#define bfd_find_inliner_info(abfd, file, func, line) \ ++ BFD_SEND (abfd, _bfd_find_inliner_info, \ ++ (abfd, file, func, line)) ++ ++#define bfd_debug_info_start(abfd) \ ++ BFD_SEND (abfd, _bfd_debug_info_start, (abfd)) ++ ++#define bfd_debug_info_end(abfd) \ ++ BFD_SEND (abfd, _bfd_debug_info_end, (abfd)) ++ ++#define bfd_debug_info_accumulate(abfd, section) \ ++ BFD_SEND (abfd, _bfd_debug_info_accumulate, (abfd, section)) ++ ++#define bfd_stat_arch_elt(abfd, stat) \ ++ BFD_SEND (abfd, _bfd_stat_arch_elt,(abfd, stat)) ++ ++#define bfd_update_armap_timestamp(abfd) \ ++ BFD_SEND (abfd, _bfd_update_armap_timestamp, (abfd)) ++ ++#define bfd_set_arch_mach(abfd, arch, mach)\ ++ BFD_SEND ( abfd, _bfd_set_arch_mach, (abfd, arch, mach)) ++ ++#define bfd_relax_section(abfd, section, link_info, again) \ ++ BFD_SEND (abfd, _bfd_relax_section, (abfd, section, link_info, again)) ++ ++#define bfd_gc_sections(abfd, link_info) \ ++ BFD_SEND (abfd, _bfd_gc_sections, (abfd, link_info)) ++ ++#define bfd_merge_sections(abfd, link_info) \ ++ BFD_SEND (abfd, _bfd_merge_sections, (abfd, link_info)) ++ ++#define bfd_is_group_section(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_is_group_section, (abfd, sec)) ++ ++#define bfd_discard_group(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_discard_group, (abfd, sec)) ++ ++#define bfd_link_hash_table_create(abfd) \ ++ BFD_SEND (abfd, _bfd_link_hash_table_create, (abfd)) ++ ++#define bfd_link_hash_table_free(abfd, hash) \ ++ BFD_SEND (abfd, _bfd_link_hash_table_free, (hash)) ++ ++#define bfd_link_add_symbols(abfd, info) \ ++ BFD_SEND (abfd, _bfd_link_add_symbols, (abfd, info)) ++ ++#define bfd_link_just_syms(abfd, sec, info) \ ++ BFD_SEND (abfd, _bfd_link_just_syms, (sec, info)) ++ ++#define bfd_final_link(abfd, info) \ ++ BFD_SEND (abfd, _bfd_final_link, (abfd, info)) ++ ++#define bfd_free_cached_info(abfd) \ ++ BFD_SEND (abfd, _bfd_free_cached_info, (abfd)) ++ ++#define bfd_get_dynamic_symtab_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_dynamic_symtab_upper_bound, (abfd)) ++ ++#define bfd_print_private_bfd_data(abfd, file)\ ++ BFD_SEND (abfd, _bfd_print_private_bfd_data, (abfd, file)) ++ ++#define bfd_canonicalize_dynamic_symtab(abfd, asymbols) \ ++ BFD_SEND (abfd, _bfd_canonicalize_dynamic_symtab, (abfd, asymbols)) ++ ++#define bfd_get_synthetic_symtab(abfd, count, syms, dyncount, dynsyms, ret) \ ++ BFD_SEND (abfd, _bfd_get_synthetic_symtab, (abfd, count, syms, \ ++ dyncount, dynsyms, ret)) ++ ++#define bfd_get_dynamic_reloc_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_dynamic_reloc_upper_bound, (abfd)) ++ ++#define bfd_canonicalize_dynamic_reloc(abfd, arels, asyms) \ ++ BFD_SEND (abfd, _bfd_canonicalize_dynamic_reloc, (abfd, arels, asyms)) ++ ++extern bfd_byte *bfd_get_relocated_section_contents ++ (bfd *, struct bfd_link_info *, struct bfd_link_order *, bfd_byte *, ++ bfd_boolean, asymbol **); ++ ++bfd_boolean bfd_alt_mach_code (bfd *abfd, int alternative); ++ ++struct bfd_preserve ++{ ++ void *marker; ++ void *tdata; ++ flagword flags; ++ const struct bfd_arch_info *arch_info; ++ struct bfd_section *sections; ++ struct bfd_section *section_last; ++ unsigned int section_count; ++ struct bfd_hash_table section_htab; ++}; ++ ++bfd_boolean bfd_preserve_save (bfd *, struct bfd_preserve *); ++ ++void bfd_preserve_restore (bfd *, struct bfd_preserve *); ++ ++void bfd_preserve_finish (bfd *, struct bfd_preserve *); ++ ++/* Extracted from archive.c. */ ++symindex bfd_get_next_mapent ++ (bfd *abfd, symindex previous, carsym **sym); ++ ++bfd_boolean bfd_set_archive_head (bfd *output, bfd *new_head); ++ ++bfd *bfd_openr_next_archived_file (bfd *archive, bfd *previous); ++ ++/* Extracted from corefile.c. */ ++const char *bfd_core_file_failing_command (bfd *abfd); ++ ++int bfd_core_file_failing_signal (bfd *abfd); ++ ++bfd_boolean core_file_matches_executable_p ++ (bfd *core_bfd, bfd *exec_bfd); ++ ++/* Extracted from targets.c. */ ++#define BFD_SEND(bfd, message, arglist) \ ++ ((*((bfd)->xvec->message)) arglist) ++ ++#ifdef DEBUG_BFD_SEND ++#undef BFD_SEND ++#define BFD_SEND(bfd, message, arglist) \ ++ (((bfd) && (bfd)->xvec && (bfd)->xvec->message) ? \ ++ ((*((bfd)->xvec->message)) arglist) : \ ++ (bfd_assert (__FILE__,__LINE__), NULL)) ++#endif ++#define BFD_SEND_FMT(bfd, message, arglist) \ ++ (((bfd)->xvec->message[(int) ((bfd)->format)]) arglist) ++ ++#ifdef DEBUG_BFD_SEND ++#undef BFD_SEND_FMT ++#define BFD_SEND_FMT(bfd, message, arglist) \ ++ (((bfd) && (bfd)->xvec && (bfd)->xvec->message) ? \ ++ (((bfd)->xvec->message[(int) ((bfd)->format)]) arglist) : \ ++ (bfd_assert (__FILE__,__LINE__), NULL)) ++#endif ++ ++enum bfd_flavour ++{ ++ bfd_target_unknown_flavour, ++ bfd_target_aout_flavour, ++ bfd_target_coff_flavour, ++ bfd_target_ecoff_flavour, ++ bfd_target_xcoff_flavour, ++ bfd_target_elf_flavour, ++ bfd_target_ieee_flavour, ++ bfd_target_nlm_flavour, ++ bfd_target_oasys_flavour, ++ bfd_target_tekhex_flavour, ++ bfd_target_srec_flavour, ++ bfd_target_ihex_flavour, ++ bfd_target_som_flavour, ++ bfd_target_os9k_flavour, ++ bfd_target_versados_flavour, ++ bfd_target_msdos_flavour, ++ bfd_target_ovax_flavour, ++ bfd_target_evax_flavour, ++ bfd_target_mmo_flavour, ++ bfd_target_mach_o_flavour, ++ bfd_target_pef_flavour, ++ bfd_target_pef_xlib_flavour, ++ bfd_target_sym_flavour ++}; ++ ++enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN }; ++ ++/* Forward declaration. */ ++typedef struct bfd_link_info _bfd_link_info; ++ ++typedef struct bfd_target ++{ ++ /* Identifies the kind of target, e.g., SunOS4, Ultrix, etc. */ ++ char *name; ++ ++ /* The "flavour" of a back end is a general indication about ++ the contents of a file. */ ++ enum bfd_flavour flavour; ++ ++ /* The order of bytes within the data area of a file. */ ++ enum bfd_endian byteorder; ++ ++ /* The order of bytes within the header parts of a file. */ ++ enum bfd_endian header_byteorder; ++ ++ /* A mask of all the flags which an executable may have set - ++ from the set <>, <>, ...<>. */ ++ flagword object_flags; ++ ++ /* A mask of all the flags which a section may have set - from ++ the set <>, <>, ...<>. */ ++ flagword section_flags; ++ ++ /* The character normally found at the front of a symbol. ++ (if any), perhaps `_'. */ ++ char symbol_leading_char; ++ ++ /* The pad character for file names within an archive header. */ ++ char ar_pad_char; ++ ++ /* The maximum number of characters in an archive header. */ ++ unsigned short ar_max_namelen; ++ ++ /* Entries for byte swapping for data. These are different from the ++ other entry points, since they don't take a BFD as the first argument. ++ Certain other handlers could do the same. */ ++ bfd_uint64_t (*bfd_getx64) (const void *); ++ bfd_int64_t (*bfd_getx_signed_64) (const void *); ++ void (*bfd_putx64) (bfd_uint64_t, void *); ++ bfd_vma (*bfd_getx32) (const void *); ++ bfd_signed_vma (*bfd_getx_signed_32) (const void *); ++ void (*bfd_putx32) (bfd_vma, void *); ++ bfd_vma (*bfd_getx16) (const void *); ++ bfd_signed_vma (*bfd_getx_signed_16) (const void *); ++ void (*bfd_putx16) (bfd_vma, void *); ++ ++ /* Byte swapping for the headers. */ ++ bfd_uint64_t (*bfd_h_getx64) (const void *); ++ bfd_int64_t (*bfd_h_getx_signed_64) (const void *); ++ void (*bfd_h_putx64) (bfd_uint64_t, void *); ++ bfd_vma (*bfd_h_getx32) (const void *); ++ bfd_signed_vma (*bfd_h_getx_signed_32) (const void *); ++ void (*bfd_h_putx32) (bfd_vma, void *); ++ bfd_vma (*bfd_h_getx16) (const void *); ++ bfd_signed_vma (*bfd_h_getx_signed_16) (const void *); ++ void (*bfd_h_putx16) (bfd_vma, void *); ++ ++ /* Format dependent routines: these are vectors of entry points ++ within the target vector structure, one for each format to check. */ ++ ++ /* Check the format of a file being read. Return a <> or zero. */ ++ const struct bfd_target *(*_bfd_check_format[bfd_type_end]) (bfd *); ++ ++ /* Set the format of a file being written. */ ++ bfd_boolean (*_bfd_set_format[bfd_type_end]) (bfd *); ++ ++ /* Write cached information into a file being written, at <>. */ ++ bfd_boolean (*_bfd_write_contents[bfd_type_end]) (bfd *); ++ ++ ++ /* Generic entry points. */ ++#define BFD_JUMP_TABLE_GENERIC(NAME) \ ++ NAME##_close_and_cleanup, \ ++ NAME##_bfd_free_cached_info, \ ++ NAME##_new_section_hook, \ ++ NAME##_get_section_contents, \ ++ NAME##_get_section_contents_in_window ++ ++ /* Called when the BFD is being closed to do any necessary cleanup. */ ++ bfd_boolean (*_close_and_cleanup) (bfd *); ++ /* Ask the BFD to free all cached information. */ ++ bfd_boolean (*_bfd_free_cached_info) (bfd *); ++ /* Called when a new section is created. */ ++ bfd_boolean (*_new_section_hook) (bfd *, sec_ptr); ++ /* Read the contents of a section. */ ++ bfd_boolean (*_bfd_get_section_contents) ++ (bfd *, sec_ptr, void *, file_ptr, bfd_size_type); ++ bfd_boolean (*_bfd_get_section_contents_in_window) ++ (bfd *, sec_ptr, bfd_window *, file_ptr, bfd_size_type); ++ ++ /* Entry points to copy private data. */ ++#define BFD_JUMP_TABLE_COPY(NAME) \ ++ NAME##_bfd_copy_private_bfd_data, \ ++ NAME##_bfd_merge_private_bfd_data, \ ++ NAME##_bfd_copy_private_section_data, \ ++ NAME##_bfd_copy_private_symbol_data, \ ++ NAME##_bfd_copy_private_header_data, \ ++ NAME##_bfd_set_private_flags, \ ++ NAME##_bfd_print_private_bfd_data ++ ++ /* Called to copy BFD general private data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_bfd_data) (bfd *, bfd *); ++ /* Called to merge BFD general private data from one object file ++ to a common output file when linking. */ ++ bfd_boolean (*_bfd_merge_private_bfd_data) (bfd *, bfd *); ++ /* Called to copy BFD private section data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_section_data) ++ (bfd *, sec_ptr, bfd *, sec_ptr); ++ /* Called to copy BFD private symbol data from one symbol ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_symbol_data) ++ (bfd *, asymbol *, bfd *, asymbol *); ++ /* Called to copy BFD private header data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_header_data) ++ (bfd *, bfd *); ++ /* Called to set private backend flags. */ ++ bfd_boolean (*_bfd_set_private_flags) (bfd *, flagword); ++ ++ /* Called to print private BFD data. */ ++ bfd_boolean (*_bfd_print_private_bfd_data) (bfd *, void *); ++ ++ /* Core file entry points. */ ++#define BFD_JUMP_TABLE_CORE(NAME) \ ++ NAME##_core_file_failing_command, \ ++ NAME##_core_file_failing_signal, \ ++ NAME##_core_file_matches_executable_p ++ ++ char * (*_core_file_failing_command) (bfd *); ++ int (*_core_file_failing_signal) (bfd *); ++ bfd_boolean (*_core_file_matches_executable_p) (bfd *, bfd *); ++ ++ /* Archive entry points. */ ++#define BFD_JUMP_TABLE_ARCHIVE(NAME) \ ++ NAME##_slurp_armap, \ ++ NAME##_slurp_extended_name_table, \ ++ NAME##_construct_extended_name_table, \ ++ NAME##_truncate_arname, \ ++ NAME##_write_armap, \ ++ NAME##_read_ar_hdr, \ ++ NAME##_openr_next_archived_file, \ ++ NAME##_get_elt_at_index, \ ++ NAME##_generic_stat_arch_elt, \ ++ NAME##_update_armap_timestamp ++ ++ bfd_boolean (*_bfd_slurp_armap) (bfd *); ++ bfd_boolean (*_bfd_slurp_extended_name_table) (bfd *); ++ bfd_boolean (*_bfd_construct_extended_name_table) ++ (bfd *, char **, bfd_size_type *, const char **); ++ void (*_bfd_truncate_arname) (bfd *, const char *, char *); ++ bfd_boolean (*write_armap) ++ (bfd *, unsigned int, struct orl *, unsigned int, int); ++ void * (*_bfd_read_ar_hdr_fn) (bfd *); ++ bfd * (*openr_next_archived_file) (bfd *, bfd *); ++#define bfd_get_elt_at_index(b,i) BFD_SEND (b, _bfd_get_elt_at_index, (b,i)) ++ bfd * (*_bfd_get_elt_at_index) (bfd *, symindex); ++ int (*_bfd_stat_arch_elt) (bfd *, struct stat *); ++ bfd_boolean (*_bfd_update_armap_timestamp) (bfd *); ++ ++ /* Entry points used for symbols. */ ++#define BFD_JUMP_TABLE_SYMBOLS(NAME) \ ++ NAME##_get_symtab_upper_bound, \ ++ NAME##_canonicalize_symtab, \ ++ NAME##_make_empty_symbol, \ ++ NAME##_print_symbol, \ ++ NAME##_get_symbol_info, \ ++ NAME##_bfd_is_local_label_name, \ ++ NAME##_bfd_is_target_special_symbol, \ ++ NAME##_get_lineno, \ ++ NAME##_find_nearest_line, \ ++ _bfd_generic_find_line, \ ++ NAME##_find_inliner_info, \ ++ NAME##_bfd_make_debug_symbol, \ ++ NAME##_read_minisymbols, \ ++ NAME##_minisymbol_to_symbol ++ ++ long (*_bfd_get_symtab_upper_bound) (bfd *); ++ long (*_bfd_canonicalize_symtab) ++ (bfd *, struct bfd_symbol **); ++ struct bfd_symbol * ++ (*_bfd_make_empty_symbol) (bfd *); ++ void (*_bfd_print_symbol) ++ (bfd *, void *, struct bfd_symbol *, bfd_print_symbol_type); ++#define bfd_print_symbol(b,p,s,e) BFD_SEND (b, _bfd_print_symbol, (b,p,s,e)) ++ void (*_bfd_get_symbol_info) ++ (bfd *, struct bfd_symbol *, symbol_info *); ++#define bfd_get_symbol_info(b,p,e) BFD_SEND (b, _bfd_get_symbol_info, (b,p,e)) ++ bfd_boolean (*_bfd_is_local_label_name) (bfd *, const char *); ++ bfd_boolean (*_bfd_is_target_special_symbol) (bfd *, asymbol *); ++ alent * (*_get_lineno) (bfd *, struct bfd_symbol *); ++ bfd_boolean (*_bfd_find_nearest_line) ++ (bfd *, struct bfd_section *, struct bfd_symbol **, bfd_vma, ++ const char **, const char **, unsigned int *); ++ bfd_boolean (*_bfd_find_line) ++ (bfd *, struct bfd_symbol **, struct bfd_symbol *, ++ const char **, unsigned int *); ++ bfd_boolean (*_bfd_find_inliner_info) ++ (bfd *, const char **, const char **, unsigned int *); ++ /* Back-door to allow format-aware applications to create debug symbols ++ while using BFD for everything else. Currently used by the assembler ++ when creating COFF files. */ ++ asymbol * (*_bfd_make_debug_symbol) ++ (bfd *, void *, unsigned long size); ++#define bfd_read_minisymbols(b, d, m, s) \ ++ BFD_SEND (b, _read_minisymbols, (b, d, m, s)) ++ long (*_read_minisymbols) ++ (bfd *, bfd_boolean, void **, unsigned int *); ++#define bfd_minisymbol_to_symbol(b, d, m, f) \ ++ BFD_SEND (b, _minisymbol_to_symbol, (b, d, m, f)) ++ asymbol * (*_minisymbol_to_symbol) ++ (bfd *, bfd_boolean, const void *, asymbol *); ++ ++ /* Routines for relocs. */ ++#define BFD_JUMP_TABLE_RELOCS(NAME) \ ++ NAME##_get_reloc_upper_bound, \ ++ NAME##_canonicalize_reloc, \ ++ NAME##_bfd_reloc_type_lookup ++ ++ long (*_get_reloc_upper_bound) (bfd *, sec_ptr); ++ long (*_bfd_canonicalize_reloc) ++ (bfd *, sec_ptr, arelent **, struct bfd_symbol **); ++ /* See documentation on reloc types. */ ++ reloc_howto_type * ++ (*reloc_type_lookup) (bfd *, bfd_reloc_code_real_type); ++ ++ /* Routines used when writing an object file. */ ++#define BFD_JUMP_TABLE_WRITE(NAME) \ ++ NAME##_set_arch_mach, \ ++ NAME##_set_section_contents ++ ++ bfd_boolean (*_bfd_set_arch_mach) ++ (bfd *, enum bfd_architecture, unsigned long); ++ bfd_boolean (*_bfd_set_section_contents) ++ (bfd *, sec_ptr, const void *, file_ptr, bfd_size_type); ++ ++ /* Routines used by the linker. */ ++#define BFD_JUMP_TABLE_LINK(NAME) \ ++ NAME##_sizeof_headers, \ ++ NAME##_bfd_get_relocated_section_contents, \ ++ NAME##_bfd_relax_section, \ ++ NAME##_bfd_link_hash_table_create, \ ++ NAME##_bfd_link_hash_table_free, \ ++ NAME##_bfd_link_add_symbols, \ ++ NAME##_bfd_link_just_syms, \ ++ NAME##_bfd_final_link, \ ++ NAME##_bfd_link_split_section, \ ++ NAME##_bfd_gc_sections, \ ++ NAME##_bfd_merge_sections, \ ++ NAME##_bfd_is_group_section, \ ++ NAME##_bfd_discard_group, \ ++ NAME##_section_already_linked \ ++ ++ int (*_bfd_sizeof_headers) (bfd *, bfd_boolean); ++ bfd_byte * (*_bfd_get_relocated_section_contents) ++ (bfd *, struct bfd_link_info *, struct bfd_link_order *, ++ bfd_byte *, bfd_boolean, struct bfd_symbol **); ++ ++ bfd_boolean (*_bfd_relax_section) ++ (bfd *, struct bfd_section *, struct bfd_link_info *, bfd_boolean *); ++ ++ /* Create a hash table for the linker. Different backends store ++ different information in this table. */ ++ struct bfd_link_hash_table * ++ (*_bfd_link_hash_table_create) (bfd *); ++ ++ /* Release the memory associated with the linker hash table. */ ++ void (*_bfd_link_hash_table_free) (struct bfd_link_hash_table *); ++ ++ /* Add symbols from this object file into the hash table. */ ++ bfd_boolean (*_bfd_link_add_symbols) (bfd *, struct bfd_link_info *); ++ ++ /* Indicate that we are only retrieving symbol values from this section. */ ++ void (*_bfd_link_just_syms) (asection *, struct bfd_link_info *); ++ ++ /* Do a link based on the link_order structures attached to each ++ section of the BFD. */ ++ bfd_boolean (*_bfd_final_link) (bfd *, struct bfd_link_info *); ++ ++ /* Should this section be split up into smaller pieces during linking. */ ++ bfd_boolean (*_bfd_link_split_section) (bfd *, struct bfd_section *); ++ ++ /* Remove sections that are not referenced from the output. */ ++ bfd_boolean (*_bfd_gc_sections) (bfd *, struct bfd_link_info *); ++ ++ /* Attempt to merge SEC_MERGE sections. */ ++ bfd_boolean (*_bfd_merge_sections) (bfd *, struct bfd_link_info *); ++ ++ /* Is this section a member of a group? */ ++ bfd_boolean (*_bfd_is_group_section) (bfd *, const struct bfd_section *); ++ ++ /* Discard members of a group. */ ++ bfd_boolean (*_bfd_discard_group) (bfd *, struct bfd_section *); ++ ++ /* Check if SEC has been already linked during a reloceatable or ++ final link. */ ++ void (*_section_already_linked) (bfd *, struct bfd_section *); ++ ++ /* Routines to handle dynamic symbols and relocs. */ ++#define BFD_JUMP_TABLE_DYNAMIC(NAME) \ ++ NAME##_get_dynamic_symtab_upper_bound, \ ++ NAME##_canonicalize_dynamic_symtab, \ ++ NAME##_get_synthetic_symtab, \ ++ NAME##_get_dynamic_reloc_upper_bound, \ ++ NAME##_canonicalize_dynamic_reloc ++ ++ /* Get the amount of memory required to hold the dynamic symbols. */ ++ long (*_bfd_get_dynamic_symtab_upper_bound) (bfd *); ++ /* Read in the dynamic symbols. */ ++ long (*_bfd_canonicalize_dynamic_symtab) ++ (bfd *, struct bfd_symbol **); ++ /* Create synthetized symbols. */ ++ long (*_bfd_get_synthetic_symtab) ++ (bfd *, long, struct bfd_symbol **, long, struct bfd_symbol **, ++ struct bfd_symbol **); ++ /* Get the amount of memory required to hold the dynamic relocs. */ ++ long (*_bfd_get_dynamic_reloc_upper_bound) (bfd *); ++ /* Read in the dynamic relocs. */ ++ long (*_bfd_canonicalize_dynamic_reloc) ++ (bfd *, arelent **, struct bfd_symbol **); ++ ++ /* Opposite endian version of this target. */ ++ const struct bfd_target * alternative_target; ++ ++ /* Data for use by back-end routines, which isn't ++ generic enough to belong in this structure. */ ++ const void *backend_data; ++ ++} bfd_target; ++ ++bfd_boolean bfd_set_default_target (const char *name); ++ ++const bfd_target *bfd_find_target (const char *target_name, bfd *abfd); ++ ++const char ** bfd_target_list (void); ++ ++const bfd_target *bfd_search_for_target ++ (int (*search_func) (const bfd_target *, void *), ++ void *); ++ ++/* Extracted from format.c. */ ++bfd_boolean bfd_check_format (bfd *abfd, bfd_format format); ++ ++bfd_boolean bfd_check_format_matches ++ (bfd *abfd, bfd_format format, char ***matching); ++ ++bfd_boolean bfd_set_format (bfd *abfd, bfd_format format); ++ ++const char *bfd_format_string (bfd_format format); ++ ++/* Extracted from linker.c. */ ++bfd_boolean bfd_link_split_section (bfd *abfd, asection *sec); ++ ++#define bfd_link_split_section(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_link_split_section, (abfd, sec)) ++ ++void bfd_section_already_linked (bfd *abfd, asection *sec); ++ ++#define bfd_section_already_linked(abfd, sec) \ ++ BFD_SEND (abfd, _section_already_linked, (abfd, sec)) ++ ++/* Extracted from simple.c. */ ++bfd_byte *bfd_simple_get_relocated_section_contents ++ (bfd *abfd, asection *sec, bfd_byte *outbuf, asymbol **symbol_table); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +--- /dev/null ++++ b/arch/x86/include/asm/bfd_64.h +@@ -0,0 +1,4917 @@ ++/* DO NOT EDIT! -*- buffer-read-only: t -*- This file is automatically ++ generated from "bfd-in.h", "init.c", "opncls.c", "libbfd.c", ++ "bfdio.c", "bfdwin.c", "section.c", "archures.c", "reloc.c", ++ "syms.c", "bfd.c", "archive.c", "corefile.c", "targets.c", "format.c", ++ "linker.c" and "simple.c". ++ Run "make headers" in your build bfd/ to regenerate. */ ++ ++/* Main header file for the bfd library -- portable access to object files. ++ ++ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, ++ 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. ++ ++ Contributed by Cygnus Support. ++ ++ This file is part of BFD, the Binary File Descriptor library. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as ++ * required. ++ * Keith Owens 15 May 2006 ++ */ ++ ++#ifndef __BFD_H_SEEN__ ++#define __BFD_H_SEEN__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifdef __KERNEL__ ++#include ++#else /* __KERNEL__ */ ++#include "ansidecl.h" ++#include "symcat.h" ++#endif /* __KERNEL__ */ ++#if defined (__STDC__) || defined (ALMOST_STDC) || defined (HAVE_STRINGIZE) ++#ifndef SABER ++/* This hack is to avoid a problem with some strict ANSI C preprocessors. ++ The problem is, "32_" is not a valid preprocessing token, and we don't ++ want extra underscores (e.g., "nlm_32_"). The XCONCAT2 macro will ++ cause the inner CONCAT2 macros to be evaluated first, producing ++ still-valid pp-tokens. Then the final concatenation can be done. */ ++#undef CONCAT4 ++#define CONCAT4(a,b,c,d) XCONCAT2(CONCAT2(a,b),CONCAT2(c,d)) ++#endif ++#endif ++ ++/* The word size used by BFD on the host. This may be 64 with a 32 ++ bit target if the host is 64 bit, or if other 64 bit targets have ++ been selected with --enable-targets, or if --enable-64-bit-bfd. */ ++#define BFD_ARCH_SIZE 64 ++ ++/* The word size of the default bfd target. */ ++#define BFD_DEFAULT_TARGET_SIZE 64 ++ ++#define BFD_HOST_64BIT_LONG 1 ++#define BFD_HOST_LONG_LONG 1 ++#if 1 ++#define BFD_HOST_64_BIT long ++#define BFD_HOST_U_64_BIT unsigned long ++typedef BFD_HOST_64_BIT bfd_int64_t; ++typedef BFD_HOST_U_64_BIT bfd_uint64_t; ++#endif ++ ++#if BFD_ARCH_SIZE >= 64 ++#define BFD64 ++#endif ++ ++#ifndef INLINE ++#if __GNUC__ >= 2 ++#define INLINE __inline__ ++#else ++#define INLINE ++#endif ++#endif ++ ++/* Forward declaration. */ ++typedef struct bfd bfd; ++ ++/* Boolean type used in bfd. Too many systems define their own ++ versions of "boolean" for us to safely typedef a "boolean" of ++ our own. Using an enum for "bfd_boolean" has its own set of ++ problems, with strange looking casts required to avoid warnings ++ on some older compilers. Thus we just use an int. ++ ++ General rule: Functions which are bfd_boolean return TRUE on ++ success and FALSE on failure (unless they're a predicate). */ ++ ++typedef int bfd_boolean; ++#undef FALSE ++#undef TRUE ++#define FALSE 0 ++#define TRUE 1 ++ ++#ifdef BFD64 ++ ++#ifndef BFD_HOST_64_BIT ++ #error No 64 bit integer type available ++#endif /* ! defined (BFD_HOST_64_BIT) */ ++ ++typedef BFD_HOST_U_64_BIT bfd_vma; ++typedef BFD_HOST_64_BIT bfd_signed_vma; ++typedef BFD_HOST_U_64_BIT bfd_size_type; ++typedef BFD_HOST_U_64_BIT symvalue; ++ ++#ifndef fprintf_vma ++#if BFD_HOST_64BIT_LONG ++#define sprintf_vma(s,x) sprintf (s, "%016lx", x) ++#define fprintf_vma(f,x) fprintf (f, "%016lx", x) ++#else ++#define _bfd_int64_low(x) ((unsigned long) (((x) & 0xffffffff))) ++#define _bfd_int64_high(x) ((unsigned long) (((x) >> 32) & 0xffffffff)) ++#define fprintf_vma(s,x) \ ++ fprintf ((s), "%08lx%08lx", _bfd_int64_high (x), _bfd_int64_low (x)) ++#define sprintf_vma(s,x) \ ++ sprintf ((s), "%08lx%08lx", _bfd_int64_high (x), _bfd_int64_low (x)) ++#endif ++#endif ++ ++#else /* not BFD64 */ ++ ++/* Represent a target address. Also used as a generic unsigned type ++ which is guaranteed to be big enough to hold any arithmetic types ++ we need to deal with. */ ++typedef unsigned long bfd_vma; ++ ++/* A generic signed type which is guaranteed to be big enough to hold any ++ arithmetic types we need to deal with. Can be assumed to be compatible ++ with bfd_vma in the same way that signed and unsigned ints are compatible ++ (as parameters, in assignment, etc). */ ++typedef long bfd_signed_vma; ++ ++typedef unsigned long symvalue; ++typedef unsigned long bfd_size_type; ++ ++/* Print a bfd_vma x on stream s. */ ++#define fprintf_vma(s,x) fprintf (s, "%08lx", x) ++#define sprintf_vma(s,x) sprintf (s, "%08lx", x) ++ ++#endif /* not BFD64 */ ++ ++#define HALF_BFD_SIZE_TYPE \ ++ (((bfd_size_type) 1) << (8 * sizeof (bfd_size_type) / 2)) ++ ++#ifndef BFD_HOST_64_BIT ++/* Fall back on a 32 bit type. The idea is to make these types always ++ available for function return types, but in the case that ++ BFD_HOST_64_BIT is undefined such a function should abort or ++ otherwise signal an error. */ ++typedef bfd_signed_vma bfd_int64_t; ++typedef bfd_vma bfd_uint64_t; ++#endif ++ ++/* An offset into a file. BFD always uses the largest possible offset ++ based on the build time availability of fseek, fseeko, or fseeko64. */ ++typedef BFD_HOST_64_BIT file_ptr; ++typedef unsigned BFD_HOST_64_BIT ufile_ptr; ++ ++extern void bfd_sprintf_vma (bfd *, char *, bfd_vma); ++extern void bfd_fprintf_vma (bfd *, void *, bfd_vma); ++ ++#define printf_vma(x) fprintf_vma(stdout,x) ++#define bfd_printf_vma(abfd,x) bfd_fprintf_vma (abfd,stdout,x) ++ ++typedef unsigned int flagword; /* 32 bits of flags */ ++typedef unsigned char bfd_byte; ++ ++/* File formats. */ ++ ++typedef enum bfd_format ++{ ++ bfd_unknown = 0, /* File format is unknown. */ ++ bfd_object, /* Linker/assembler/compiler output. */ ++ bfd_archive, /* Object archive file. */ ++ bfd_core, /* Core dump. */ ++ bfd_type_end /* Marks the end; don't use it! */ ++} ++bfd_format; ++ ++/* Values that may appear in the flags field of a BFD. These also ++ appear in the object_flags field of the bfd_target structure, where ++ they indicate the set of flags used by that backend (not all flags ++ are meaningful for all object file formats) (FIXME: at the moment, ++ the object_flags values have mostly just been copied from backend ++ to another, and are not necessarily correct). */ ++ ++/* No flags. */ ++#define BFD_NO_FLAGS 0x00 ++ ++/* BFD contains relocation entries. */ ++#define HAS_RELOC 0x01 ++ ++/* BFD is directly executable. */ ++#define EXEC_P 0x02 ++ ++/* BFD has line number information (basically used for F_LNNO in a ++ COFF header). */ ++#define HAS_LINENO 0x04 ++ ++/* BFD has debugging information. */ ++#define HAS_DEBUG 0x08 ++ ++/* BFD has symbols. */ ++#define HAS_SYMS 0x10 ++ ++/* BFD has local symbols (basically used for F_LSYMS in a COFF ++ header). */ ++#define HAS_LOCALS 0x20 ++ ++/* BFD is a dynamic object. */ ++#define DYNAMIC 0x40 ++ ++/* Text section is write protected (if D_PAGED is not set, this is ++ like an a.out NMAGIC file) (the linker sets this by default, but ++ clears it for -r or -N). */ ++#define WP_TEXT 0x80 ++ ++/* BFD is dynamically paged (this is like an a.out ZMAGIC file) (the ++ linker sets this by default, but clears it for -r or -n or -N). */ ++#define D_PAGED 0x100 ++ ++/* BFD is relaxable (this means that bfd_relax_section may be able to ++ do something) (sometimes bfd_relax_section can do something even if ++ this is not set). */ ++#define BFD_IS_RELAXABLE 0x200 ++ ++/* This may be set before writing out a BFD to request using a ++ traditional format. For example, this is used to request that when ++ writing out an a.out object the symbols not be hashed to eliminate ++ duplicates. */ ++#define BFD_TRADITIONAL_FORMAT 0x400 ++ ++/* This flag indicates that the BFD contents are actually cached in ++ memory. If this is set, iostream points to a bfd_in_memory struct. */ ++#define BFD_IN_MEMORY 0x800 ++ ++/* The sections in this BFD specify a memory page. */ ++#define HAS_LOAD_PAGE 0x1000 ++ ++/* This BFD has been created by the linker and doesn't correspond ++ to any input file. */ ++#define BFD_LINKER_CREATED 0x2000 ++ ++/* Symbols and relocation. */ ++ ++/* A count of carsyms (canonical archive symbols). */ ++typedef unsigned long symindex; ++ ++/* How to perform a relocation. */ ++typedef const struct reloc_howto_struct reloc_howto_type; ++ ++#define BFD_NO_MORE_SYMBOLS ((symindex) ~0) ++ ++/* General purpose part of a symbol X; ++ target specific parts are in libcoff.h, libaout.h, etc. */ ++ ++#define bfd_get_section(x) ((x)->section) ++#define bfd_get_output_section(x) ((x)->section->output_section) ++#define bfd_set_section(x,y) ((x)->section) = (y) ++#define bfd_asymbol_base(x) ((x)->section->vma) ++#define bfd_asymbol_value(x) (bfd_asymbol_base(x) + (x)->value) ++#define bfd_asymbol_name(x) ((x)->name) ++/*Perhaps future: #define bfd_asymbol_bfd(x) ((x)->section->owner)*/ ++#define bfd_asymbol_bfd(x) ((x)->the_bfd) ++#define bfd_asymbol_flavour(x) (bfd_asymbol_bfd(x)->xvec->flavour) ++ ++/* A canonical archive symbol. */ ++/* This is a type pun with struct ranlib on purpose! */ ++typedef struct carsym ++{ ++ char *name; ++ file_ptr file_offset; /* Look here to find the file. */ ++} ++carsym; /* To make these you call a carsymogen. */ ++ ++/* Used in generating armaps (archive tables of contents). ++ Perhaps just a forward definition would do? */ ++struct orl /* Output ranlib. */ ++{ ++ char **name; /* Symbol name. */ ++ union ++ { ++ file_ptr pos; ++ bfd *abfd; ++ } u; /* bfd* or file position. */ ++ int namidx; /* Index into string table. */ ++}; ++ ++/* Linenumber stuff. */ ++typedef struct lineno_cache_entry ++{ ++ unsigned int line_number; /* Linenumber from start of function. */ ++ union ++ { ++ struct bfd_symbol *sym; /* Function name. */ ++ bfd_vma offset; /* Offset into section. */ ++ } u; ++} ++alent; ++ ++/* Object and core file sections. */ ++ ++#define align_power(addr, align) \ ++ (((addr) + ((bfd_vma) 1 << (align)) - 1) & ((bfd_vma) -1 << (align))) ++ ++typedef struct bfd_section *sec_ptr; ++ ++#define bfd_get_section_name(bfd, ptr) ((ptr)->name + 0) ++#define bfd_get_section_vma(bfd, ptr) ((ptr)->vma + 0) ++#define bfd_get_section_lma(bfd, ptr) ((ptr)->lma + 0) ++#define bfd_get_section_alignment(bfd, ptr) ((ptr)->alignment_power + 0) ++#define bfd_section_name(bfd, ptr) ((ptr)->name) ++#define bfd_section_size(bfd, ptr) ((ptr)->size) ++#define bfd_get_section_size(ptr) ((ptr)->size) ++#define bfd_section_vma(bfd, ptr) ((ptr)->vma) ++#define bfd_section_lma(bfd, ptr) ((ptr)->lma) ++#define bfd_section_alignment(bfd, ptr) ((ptr)->alignment_power) ++#define bfd_get_section_flags(bfd, ptr) ((ptr)->flags + 0) ++#define bfd_get_section_userdata(bfd, ptr) ((ptr)->userdata) ++ ++#define bfd_is_com_section(ptr) (((ptr)->flags & SEC_IS_COMMON) != 0) ++ ++#define bfd_set_section_vma(bfd, ptr, val) (((ptr)->vma = (ptr)->lma = (val)), ((ptr)->user_set_vma = TRUE), TRUE) ++#define bfd_set_section_alignment(bfd, ptr, val) (((ptr)->alignment_power = (val)),TRUE) ++#define bfd_set_section_userdata(bfd, ptr, val) (((ptr)->userdata = (val)),TRUE) ++/* Find the address one past the end of SEC. */ ++#define bfd_get_section_limit(bfd, sec) \ ++ (((sec)->rawsize ? (sec)->rawsize : (sec)->size) \ ++ / bfd_octets_per_byte (bfd)) ++ ++typedef struct stat stat_type; ++ ++typedef enum bfd_print_symbol ++{ ++ bfd_print_symbol_name, ++ bfd_print_symbol_more, ++ bfd_print_symbol_all ++} bfd_print_symbol_type; ++ ++/* Information about a symbol that nm needs. */ ++ ++typedef struct _symbol_info ++{ ++ symvalue value; ++ char type; ++ const char *name; /* Symbol name. */ ++ unsigned char stab_type; /* Stab type. */ ++ char stab_other; /* Stab other. */ ++ short stab_desc; /* Stab desc. */ ++ const char *stab_name; /* String for stab type. */ ++} symbol_info; ++ ++/* Get the name of a stabs type code. */ ++ ++extern const char *bfd_get_stab_name (int); ++ ++/* Hash table routines. There is no way to free up a hash table. */ ++ ++/* An element in the hash table. Most uses will actually use a larger ++ structure, and an instance of this will be the first field. */ ++ ++struct bfd_hash_entry ++{ ++ /* Next entry for this hash code. */ ++ struct bfd_hash_entry *next; ++ /* String being hashed. */ ++ const char *string; ++ /* Hash code. This is the full hash code, not the index into the ++ table. */ ++ unsigned long hash; ++}; ++ ++/* A hash table. */ ++ ++struct bfd_hash_table ++{ ++ /* The hash array. */ ++ struct bfd_hash_entry **table; ++ /* The number of slots in the hash table. */ ++ unsigned int size; ++ /* A function used to create new elements in the hash table. The ++ first entry is itself a pointer to an element. When this ++ function is first invoked, this pointer will be NULL. However, ++ having the pointer permits a hierarchy of method functions to be ++ built each of which calls the function in the superclass. Thus ++ each function should be written to allocate a new block of memory ++ only if the argument is NULL. */ ++ struct bfd_hash_entry *(*newfunc) ++ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *); ++ /* An objalloc for this hash table. This is a struct objalloc *, ++ but we use void * to avoid requiring the inclusion of objalloc.h. */ ++ void *memory; ++}; ++ ++/* Initialize a hash table. */ ++extern bfd_boolean bfd_hash_table_init ++ (struct bfd_hash_table *, ++ struct bfd_hash_entry *(*) (struct bfd_hash_entry *, ++ struct bfd_hash_table *, ++ const char *)); ++ ++/* Initialize a hash table specifying a size. */ ++extern bfd_boolean bfd_hash_table_init_n ++ (struct bfd_hash_table *, ++ struct bfd_hash_entry *(*) (struct bfd_hash_entry *, ++ struct bfd_hash_table *, ++ const char *), ++ unsigned int size); ++ ++/* Free up a hash table. */ ++extern void bfd_hash_table_free ++ (struct bfd_hash_table *); ++ ++/* Look up a string in a hash table. If CREATE is TRUE, a new entry ++ will be created for this string if one does not already exist. The ++ COPY argument must be TRUE if this routine should copy the string ++ into newly allocated memory when adding an entry. */ ++extern struct bfd_hash_entry *bfd_hash_lookup ++ (struct bfd_hash_table *, const char *, bfd_boolean create, ++ bfd_boolean copy); ++ ++/* Replace an entry in a hash table. */ ++extern void bfd_hash_replace ++ (struct bfd_hash_table *, struct bfd_hash_entry *old, ++ struct bfd_hash_entry *nw); ++ ++/* Base method for creating a hash table entry. */ ++extern struct bfd_hash_entry *bfd_hash_newfunc ++ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *); ++ ++/* Grab some space for a hash table entry. */ ++extern void *bfd_hash_allocate ++ (struct bfd_hash_table *, unsigned int); ++ ++/* Traverse a hash table in a random order, calling a function on each ++ element. If the function returns FALSE, the traversal stops. The ++ INFO argument is passed to the function. */ ++extern void bfd_hash_traverse ++ (struct bfd_hash_table *, ++ bfd_boolean (*) (struct bfd_hash_entry *, void *), ++ void *info); ++ ++/* Allows the default size of a hash table to be configured. New hash ++ tables allocated using bfd_hash_table_init will be created with ++ this size. */ ++extern void bfd_hash_set_default_size (bfd_size_type); ++ ++/* This structure is used to keep track of stabs in sections ++ information while linking. */ ++ ++struct stab_info ++{ ++ /* A hash table used to hold stabs strings. */ ++ struct bfd_strtab_hash *strings; ++ /* The header file hash table. */ ++ struct bfd_hash_table includes; ++ /* The first .stabstr section. */ ++ struct bfd_section *stabstr; ++}; ++ ++#define COFF_SWAP_TABLE (void *) &bfd_coff_std_swap_table ++ ++/* User program access to BFD facilities. */ ++ ++/* Direct I/O routines, for programs which know more about the object ++ file than BFD does. Use higher level routines if possible. */ ++ ++extern bfd_size_type bfd_bread (void *, bfd_size_type, bfd *); ++extern bfd_size_type bfd_bwrite (const void *, bfd_size_type, bfd *); ++extern int bfd_seek (bfd *, file_ptr, int); ++extern file_ptr bfd_tell (bfd *); ++extern int bfd_flush (bfd *); ++extern int bfd_stat (bfd *, struct stat *); ++ ++/* Deprecated old routines. */ ++#if __GNUC__ ++#define bfd_read(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_read", __FILE__, __LINE__, __FUNCTION__), \ ++ bfd_bread ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#define bfd_write(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_write", __FILE__, __LINE__, __FUNCTION__), \ ++ bfd_bwrite ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#else ++#define bfd_read(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_read", (const char *) 0, 0, (const char *) 0), \ ++ bfd_bread ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#define bfd_write(BUF, ELTSIZE, NITEMS, ABFD) \ ++ (warn_deprecated ("bfd_write", (const char *) 0, 0, (const char *) 0),\ ++ bfd_bwrite ((BUF), (ELTSIZE) * (NITEMS), (ABFD))) ++#endif ++extern void warn_deprecated (const char *, const char *, int, const char *); ++ ++/* Cast from const char * to char * so that caller can assign to ++ a char * without a warning. */ ++#define bfd_get_filename(abfd) ((char *) (abfd)->filename) ++#define bfd_get_cacheable(abfd) ((abfd)->cacheable) ++#define bfd_get_format(abfd) ((abfd)->format) ++#define bfd_get_target(abfd) ((abfd)->xvec->name) ++#define bfd_get_flavour(abfd) ((abfd)->xvec->flavour) ++#define bfd_family_coff(abfd) \ ++ (bfd_get_flavour (abfd) == bfd_target_coff_flavour || \ ++ bfd_get_flavour (abfd) == bfd_target_xcoff_flavour) ++#define bfd_big_endian(abfd) ((abfd)->xvec->byteorder == BFD_ENDIAN_BIG) ++#define bfd_little_endian(abfd) ((abfd)->xvec->byteorder == BFD_ENDIAN_LITTLE) ++#define bfd_header_big_endian(abfd) \ ++ ((abfd)->xvec->header_byteorder == BFD_ENDIAN_BIG) ++#define bfd_header_little_endian(abfd) \ ++ ((abfd)->xvec->header_byteorder == BFD_ENDIAN_LITTLE) ++#define bfd_get_file_flags(abfd) ((abfd)->flags) ++#define bfd_applicable_file_flags(abfd) ((abfd)->xvec->object_flags) ++#define bfd_applicable_section_flags(abfd) ((abfd)->xvec->section_flags) ++#define bfd_my_archive(abfd) ((abfd)->my_archive) ++#define bfd_has_map(abfd) ((abfd)->has_armap) ++ ++#define bfd_valid_reloc_types(abfd) ((abfd)->xvec->valid_reloc_types) ++#define bfd_usrdata(abfd) ((abfd)->usrdata) ++ ++#define bfd_get_start_address(abfd) ((abfd)->start_address) ++#define bfd_get_symcount(abfd) ((abfd)->symcount) ++#define bfd_get_outsymbols(abfd) ((abfd)->outsymbols) ++#define bfd_count_sections(abfd) ((abfd)->section_count) ++ ++#define bfd_get_dynamic_symcount(abfd) ((abfd)->dynsymcount) ++ ++#define bfd_get_symbol_leading_char(abfd) ((abfd)->xvec->symbol_leading_char) ++ ++#define bfd_set_cacheable(abfd,bool) (((abfd)->cacheable = bool), TRUE) ++ ++extern bfd_boolean bfd_cache_close ++ (bfd *abfd); ++/* NB: This declaration should match the autogenerated one in libbfd.h. */ ++ ++extern bfd_boolean bfd_cache_close_all (void); ++ ++extern bfd_boolean bfd_record_phdr ++ (bfd *, unsigned long, bfd_boolean, flagword, bfd_boolean, bfd_vma, ++ bfd_boolean, bfd_boolean, unsigned int, struct bfd_section **); ++ ++/* Byte swapping routines. */ ++ ++bfd_uint64_t bfd_getb64 (const void *); ++bfd_uint64_t bfd_getl64 (const void *); ++bfd_int64_t bfd_getb_signed_64 (const void *); ++bfd_int64_t bfd_getl_signed_64 (const void *); ++bfd_vma bfd_getb32 (const void *); ++bfd_vma bfd_getl32 (const void *); ++bfd_signed_vma bfd_getb_signed_32 (const void *); ++bfd_signed_vma bfd_getl_signed_32 (const void *); ++bfd_vma bfd_getb16 (const void *); ++bfd_vma bfd_getl16 (const void *); ++bfd_signed_vma bfd_getb_signed_16 (const void *); ++bfd_signed_vma bfd_getl_signed_16 (const void *); ++void bfd_putb64 (bfd_uint64_t, void *); ++void bfd_putl64 (bfd_uint64_t, void *); ++void bfd_putb32 (bfd_vma, void *); ++void bfd_putl32 (bfd_vma, void *); ++void bfd_putb16 (bfd_vma, void *); ++void bfd_putl16 (bfd_vma, void *); ++ ++/* Byte swapping routines which take size and endiannes as arguments. */ ++ ++bfd_uint64_t bfd_get_bits (const void *, int, bfd_boolean); ++void bfd_put_bits (bfd_uint64_t, void *, int, bfd_boolean); ++ ++extern bfd_boolean bfd_section_already_linked_table_init (void); ++extern void bfd_section_already_linked_table_free (void); ++ ++/* Externally visible ECOFF routines. */ ++ ++#if defined(__STDC__) || defined(ALMOST_STDC) ++struct ecoff_debug_info; ++struct ecoff_debug_swap; ++struct ecoff_extr; ++struct bfd_symbol; ++struct bfd_link_info; ++struct bfd_link_hash_entry; ++struct bfd_elf_version_tree; ++#endif ++extern bfd_vma bfd_ecoff_get_gp_value ++ (bfd * abfd); ++extern bfd_boolean bfd_ecoff_set_gp_value ++ (bfd *abfd, bfd_vma gp_value); ++extern bfd_boolean bfd_ecoff_set_regmasks ++ (bfd *abfd, unsigned long gprmask, unsigned long fprmask, ++ unsigned long *cprmask); ++extern void *bfd_ecoff_debug_init ++ (bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, struct bfd_link_info *); ++extern void bfd_ecoff_debug_free ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_accumulate ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, bfd *input_bfd, ++ struct ecoff_debug_info *input_debug, ++ const struct ecoff_debug_swap *input_swap, struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_accumulate_other ++ (void *handle, bfd *output_bfd, struct ecoff_debug_info *output_debug, ++ const struct ecoff_debug_swap *output_swap, bfd *input_bfd, ++ struct bfd_link_info *); ++extern bfd_boolean bfd_ecoff_debug_externals ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, bfd_boolean relocatable, ++ bfd_boolean (*get_extr) (struct bfd_symbol *, struct ecoff_extr *), ++ void (*set_index) (struct bfd_symbol *, bfd_size_type)); ++extern bfd_boolean bfd_ecoff_debug_one_external ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, const char *name, ++ struct ecoff_extr *esym); ++extern bfd_size_type bfd_ecoff_debug_size ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap); ++extern bfd_boolean bfd_ecoff_write_debug ++ (bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, file_ptr where); ++extern bfd_boolean bfd_ecoff_write_accumulated_debug ++ (void *handle, bfd *abfd, struct ecoff_debug_info *debug, ++ const struct ecoff_debug_swap *swap, ++ struct bfd_link_info *info, file_ptr where); ++ ++/* Externally visible ELF routines. */ ++ ++struct bfd_link_needed_list ++{ ++ struct bfd_link_needed_list *next; ++ bfd *by; ++ const char *name; ++}; ++ ++enum dynamic_lib_link_class { ++ DYN_NORMAL = 0, ++ DYN_AS_NEEDED = 1, ++ DYN_DT_NEEDED = 2, ++ DYN_NO_ADD_NEEDED = 4, ++ DYN_NO_NEEDED = 8 ++}; ++ ++extern bfd_boolean bfd_elf_record_link_assignment ++ (struct bfd_link_info *, const char *, bfd_boolean); ++extern struct bfd_link_needed_list *bfd_elf_get_needed_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_elf_get_bfd_needed_list ++ (bfd *, struct bfd_link_needed_list **); ++extern bfd_boolean bfd_elf_size_dynamic_sections ++ (bfd *, const char *, const char *, const char *, const char * const *, ++ struct bfd_link_info *, struct bfd_section **, ++ struct bfd_elf_version_tree *); ++extern bfd_boolean bfd_elf_size_dynsym_hash_dynstr ++ (bfd *, struct bfd_link_info *); ++extern void bfd_elf_set_dt_needed_name ++ (bfd *, const char *); ++extern const char *bfd_elf_get_dt_soname ++ (bfd *); ++extern void bfd_elf_set_dyn_lib_class ++ (bfd *, int); ++extern int bfd_elf_get_dyn_lib_class ++ (bfd *); ++extern struct bfd_link_needed_list *bfd_elf_get_runpath_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_elf_discard_info ++ (bfd *, struct bfd_link_info *); ++extern unsigned int _bfd_elf_default_action_discarded ++ (struct bfd_section *); ++ ++/* Return an upper bound on the number of bytes required to store a ++ copy of ABFD's program header table entries. Return -1 if an error ++ occurs; bfd_get_error will return an appropriate code. */ ++extern long bfd_get_elf_phdr_upper_bound ++ (bfd *abfd); ++ ++/* Copy ABFD's program header table entries to *PHDRS. The entries ++ will be stored as an array of Elf_Internal_Phdr structures, as ++ defined in include/elf/internal.h. To find out how large the ++ buffer needs to be, call bfd_get_elf_phdr_upper_bound. ++ ++ Return the number of program header table entries read, or -1 if an ++ error occurs; bfd_get_error will return an appropriate code. */ ++extern int bfd_get_elf_phdrs ++ (bfd *abfd, void *phdrs); ++ ++/* Create a new BFD as if by bfd_openr. Rather than opening a file, ++ reconstruct an ELF file by reading the segments out of remote memory ++ based on the ELF file header at EHDR_VMA and the ELF program headers it ++ points to. If not null, *LOADBASEP is filled in with the difference ++ between the VMAs from which the segments were read, and the VMAs the ++ file headers (and hence BFD's idea of each section's VMA) put them at. ++ ++ The function TARGET_READ_MEMORY is called to copy LEN bytes from the ++ remote memory at target address VMA into the local buffer at MYADDR; it ++ should return zero on success or an `errno' code on failure. TEMPL must ++ be a BFD for an ELF target with the word size and byte order found in ++ the remote memory. */ ++extern bfd *bfd_elf_bfd_from_remote_memory ++ (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep, ++ int (*target_read_memory) (bfd_vma vma, bfd_byte *myaddr, int len)); ++ ++/* Return the arch_size field of an elf bfd, or -1 if not elf. */ ++extern int bfd_get_arch_size ++ (bfd *); ++ ++/* Return TRUE if address "naturally" sign extends, or -1 if not elf. */ ++extern int bfd_get_sign_extend_vma ++ (bfd *); ++ ++extern struct bfd_section *_bfd_elf_tls_setup ++ (bfd *, struct bfd_link_info *); ++ ++extern void _bfd_elf_provide_symbol ++ (struct bfd_link_info *, const char *, bfd_vma, struct bfd_section *); ++ ++extern void _bfd_elf_provide_section_bound_symbols ++ (struct bfd_link_info *, struct bfd_section *, const char *, const char *); ++ ++extern void _bfd_elf_fix_excluded_sec_syms ++ (bfd *, struct bfd_link_info *); ++ ++extern bfd_boolean bfd_m68k_elf32_create_embedded_relocs ++ (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, ++ char **); ++ ++/* SunOS shared library support routines for the linker. */ ++ ++extern struct bfd_link_needed_list *bfd_sunos_get_needed_list ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_sunos_record_link_assignment ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_sunos_size_dynamic_sections ++ (bfd *, struct bfd_link_info *, struct bfd_section **, ++ struct bfd_section **, struct bfd_section **); ++ ++/* Linux shared library support routines for the linker. */ ++ ++extern bfd_boolean bfd_i386linux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_m68klinux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++extern bfd_boolean bfd_sparclinux_size_dynamic_sections ++ (bfd *, struct bfd_link_info *); ++ ++/* mmap hacks */ ++ ++struct _bfd_window_internal; ++typedef struct _bfd_window_internal bfd_window_internal; ++ ++typedef struct _bfd_window ++{ ++ /* What the user asked for. */ ++ void *data; ++ bfd_size_type size; ++ /* The actual window used by BFD. Small user-requested read-only ++ regions sharing a page may share a single window into the object ++ file. Read-write versions shouldn't until I've fixed things to ++ keep track of which portions have been claimed by the ++ application; don't want to give the same region back when the ++ application wants two writable copies! */ ++ struct _bfd_window_internal *i; ++} ++bfd_window; ++ ++extern void bfd_init_window ++ (bfd_window *); ++extern void bfd_free_window ++ (bfd_window *); ++extern bfd_boolean bfd_get_file_window ++ (bfd *, file_ptr, bfd_size_type, bfd_window *, bfd_boolean); ++ ++/* XCOFF support routines for the linker. */ ++ ++extern bfd_boolean bfd_xcoff_link_record_set ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *, bfd_size_type); ++extern bfd_boolean bfd_xcoff_import_symbol ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *, bfd_vma, ++ const char *, const char *, const char *, unsigned int); ++extern bfd_boolean bfd_xcoff_export_symbol ++ (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *); ++extern bfd_boolean bfd_xcoff_link_count_reloc ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_xcoff_record_link_assignment ++ (bfd *, struct bfd_link_info *, const char *); ++extern bfd_boolean bfd_xcoff_size_dynamic_sections ++ (bfd *, struct bfd_link_info *, const char *, const char *, ++ unsigned long, unsigned long, unsigned long, bfd_boolean, ++ int, bfd_boolean, bfd_boolean, struct bfd_section **, bfd_boolean); ++extern bfd_boolean bfd_xcoff_link_generate_rtinit ++ (bfd *, const char *, const char *, bfd_boolean); ++ ++/* XCOFF support routines for ar. */ ++extern bfd_boolean bfd_xcoff_ar_archive_set_magic ++ (bfd *, char *); ++ ++/* Externally visible COFF routines. */ ++ ++#if defined(__STDC__) || defined(ALMOST_STDC) ++struct internal_syment; ++union internal_auxent; ++#endif ++ ++extern bfd_boolean bfd_coff_get_syment ++ (bfd *, struct bfd_symbol *, struct internal_syment *); ++ ++extern bfd_boolean bfd_coff_get_auxent ++ (bfd *, struct bfd_symbol *, int, union internal_auxent *); ++ ++extern bfd_boolean bfd_coff_set_symbol_class ++ (bfd *, struct bfd_symbol *, unsigned int); ++ ++extern bfd_boolean bfd_m68k_coff_create_embedded_relocs ++ (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, char **); ++ ++/* ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_arm_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_arm_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++extern bfd_boolean bfd_arm_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++/* PE ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_arm_pe_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_arm_pe_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++extern bfd_boolean bfd_arm_pe_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++/* ELF ARM Interworking support. Called from linker. */ ++extern bfd_boolean bfd_elf32_arm_allocate_interworking_sections ++ (struct bfd_link_info *); ++ ++extern bfd_boolean bfd_elf32_arm_process_before_allocation ++ (bfd *, struct bfd_link_info *, int); ++ ++void bfd_elf32_arm_set_target_relocs ++ (struct bfd_link_info *, int, char *, int, int); ++ ++extern bfd_boolean bfd_elf32_arm_get_bfd_for_interworking ++ (bfd *, struct bfd_link_info *); ++ ++extern bfd_boolean bfd_elf32_arm_add_glue_sections_to_bfd ++ (bfd *, struct bfd_link_info *); ++ ++/* ELF ARM mapping symbol support */ ++extern bfd_boolean bfd_is_arm_mapping_symbol_name ++ (const char * name); ++ ++/* ARM Note section processing. */ ++extern bfd_boolean bfd_arm_merge_machines ++ (bfd *, bfd *); ++ ++extern bfd_boolean bfd_arm_update_notes ++ (bfd *, const char *); ++ ++extern unsigned int bfd_arm_get_mach_from_notes ++ (bfd *, const char *); ++ ++/* TI COFF load page support. */ ++extern void bfd_ticoff_set_section_load_page ++ (struct bfd_section *, int); ++ ++extern int bfd_ticoff_get_section_load_page ++ (struct bfd_section *); ++ ++/* H8/300 functions. */ ++extern bfd_vma bfd_h8300_pad_address ++ (bfd *, bfd_vma); ++ ++/* IA64 Itanium code generation. Called from linker. */ ++extern void bfd_elf32_ia64_after_parse ++ (int); ++ ++extern void bfd_elf64_ia64_after_parse ++ (int); ++ ++/* This structure is used for a comdat section, as in PE. A comdat ++ section is associated with a particular symbol. When the linker ++ sees a comdat section, it keeps only one of the sections with a ++ given name and associated with a given symbol. */ ++ ++struct coff_comdat_info ++{ ++ /* The name of the symbol associated with a comdat section. */ ++ const char *name; ++ ++ /* The local symbol table index of the symbol associated with a ++ comdat section. This is only meaningful to the object file format ++ specific code; it is not an index into the list returned by ++ bfd_canonicalize_symtab. */ ++ long symbol; ++}; ++ ++extern struct coff_comdat_info *bfd_coff_get_comdat_section ++ (bfd *, struct bfd_section *); ++ ++/* Extracted from init.c. */ ++void bfd_init (void); ++ ++/* Extracted from opncls.c. */ ++bfd *bfd_fopen (const char *filename, const char *target, ++ const char *mode, int fd); ++ ++bfd *bfd_openr (const char *filename, const char *target); ++ ++bfd *bfd_fdopenr (const char *filename, const char *target, int fd); ++ ++bfd *bfd_openstreamr (const char *, const char *, void *); ++ ++bfd *bfd_openr_iovec (const char *filename, const char *target, ++ void *(*open) (struct bfd *nbfd, ++ void *open_closure), ++ void *open_closure, ++ file_ptr (*pread) (struct bfd *nbfd, ++ void *stream, ++ void *buf, ++ file_ptr nbytes, ++ file_ptr offset), ++ int (*close) (struct bfd *nbfd, ++ void *stream)); ++ ++bfd *bfd_openw (const char *filename, const char *target); ++ ++bfd_boolean bfd_close (bfd *abfd); ++ ++bfd_boolean bfd_close_all_done (bfd *); ++ ++bfd *bfd_create (const char *filename, bfd *templ); ++ ++bfd_boolean bfd_make_writable (bfd *abfd); ++ ++bfd_boolean bfd_make_readable (bfd *abfd); ++ ++unsigned long bfd_calc_gnu_debuglink_crc32 ++ (unsigned long crc, const unsigned char *buf, bfd_size_type len); ++ ++char *bfd_follow_gnu_debuglink (bfd *abfd, const char *dir); ++ ++struct bfd_section *bfd_create_gnu_debuglink_section ++ (bfd *abfd, const char *filename); ++ ++bfd_boolean bfd_fill_in_gnu_debuglink_section ++ (bfd *abfd, struct bfd_section *sect, const char *filename); ++ ++/* Extracted from libbfd.c. */ ++ ++/* Byte swapping macros for user section data. */ ++ ++#define bfd_put_8(abfd, val, ptr) \ ++ ((void) (*((unsigned char *) (ptr)) = (val) & 0xff)) ++#define bfd_put_signed_8 \ ++ bfd_put_8 ++#define bfd_get_8(abfd, ptr) \ ++ (*(unsigned char *) (ptr) & 0xff) ++#define bfd_get_signed_8(abfd, ptr) \ ++ (((*(unsigned char *) (ptr) & 0xff) ^ 0x80) - 0x80) ++ ++#define bfd_put_16(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx16, ((val),(ptr))) ++#define bfd_put_signed_16 \ ++ bfd_put_16 ++#define bfd_get_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx16, (ptr)) ++#define bfd_get_signed_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_16, (ptr)) ++ ++#define bfd_put_32(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx32, ((val),(ptr))) ++#define bfd_put_signed_32 \ ++ bfd_put_32 ++#define bfd_get_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx32, (ptr)) ++#define bfd_get_signed_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_32, (ptr)) ++ ++#define bfd_put_64(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_putx64, ((val), (ptr))) ++#define bfd_put_signed_64 \ ++ bfd_put_64 ++#define bfd_get_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx64, (ptr)) ++#define bfd_get_signed_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_getx_signed_64, (ptr)) ++ ++#define bfd_get(bits, abfd, ptr) \ ++ ((bits) == 8 ? (bfd_vma) bfd_get_8 (abfd, ptr) \ ++ : (bits) == 16 ? bfd_get_16 (abfd, ptr) \ ++ : (bits) == 32 ? bfd_get_32 (abfd, ptr) \ ++ : (bits) == 64 ? bfd_get_64 (abfd, ptr) \ ++ : (abort (), (bfd_vma) - 1)) ++ ++#define bfd_put(bits, abfd, val, ptr) \ ++ ((bits) == 8 ? bfd_put_8 (abfd, val, ptr) \ ++ : (bits) == 16 ? bfd_put_16 (abfd, val, ptr) \ ++ : (bits) == 32 ? bfd_put_32 (abfd, val, ptr) \ ++ : (bits) == 64 ? bfd_put_64 (abfd, val, ptr) \ ++ : (abort (), (void) 0)) ++ ++ ++/* Byte swapping macros for file header data. */ ++ ++#define bfd_h_put_8(abfd, val, ptr) \ ++ bfd_put_8 (abfd, val, ptr) ++#define bfd_h_put_signed_8(abfd, val, ptr) \ ++ bfd_put_8 (abfd, val, ptr) ++#define bfd_h_get_8(abfd, ptr) \ ++ bfd_get_8 (abfd, ptr) ++#define bfd_h_get_signed_8(abfd, ptr) \ ++ bfd_get_signed_8 (abfd, ptr) ++ ++#define bfd_h_put_16(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx16, (val, ptr)) ++#define bfd_h_put_signed_16 \ ++ bfd_h_put_16 ++#define bfd_h_get_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx16, (ptr)) ++#define bfd_h_get_signed_16(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_16, (ptr)) ++ ++#define bfd_h_put_32(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx32, (val, ptr)) ++#define bfd_h_put_signed_32 \ ++ bfd_h_put_32 ++#define bfd_h_get_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx32, (ptr)) ++#define bfd_h_get_signed_32(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_32, (ptr)) ++ ++#define bfd_h_put_64(abfd, val, ptr) \ ++ BFD_SEND (abfd, bfd_h_putx64, (val, ptr)) ++#define bfd_h_put_signed_64 \ ++ bfd_h_put_64 ++#define bfd_h_get_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx64, (ptr)) ++#define bfd_h_get_signed_64(abfd, ptr) \ ++ BFD_SEND (abfd, bfd_h_getx_signed_64, (ptr)) ++ ++/* Aliases for the above, which should eventually go away. */ ++ ++#define H_PUT_64 bfd_h_put_64 ++#define H_PUT_32 bfd_h_put_32 ++#define H_PUT_16 bfd_h_put_16 ++#define H_PUT_8 bfd_h_put_8 ++#define H_PUT_S64 bfd_h_put_signed_64 ++#define H_PUT_S32 bfd_h_put_signed_32 ++#define H_PUT_S16 bfd_h_put_signed_16 ++#define H_PUT_S8 bfd_h_put_signed_8 ++#define H_GET_64 bfd_h_get_64 ++#define H_GET_32 bfd_h_get_32 ++#define H_GET_16 bfd_h_get_16 ++#define H_GET_8 bfd_h_get_8 ++#define H_GET_S64 bfd_h_get_signed_64 ++#define H_GET_S32 bfd_h_get_signed_32 ++#define H_GET_S16 bfd_h_get_signed_16 ++#define H_GET_S8 bfd_h_get_signed_8 ++ ++ ++/* Extracted from bfdio.c. */ ++long bfd_get_mtime (bfd *abfd); ++ ++long bfd_get_size (bfd *abfd); ++ ++/* Extracted from bfdwin.c. */ ++/* Extracted from section.c. */ ++typedef struct bfd_section ++{ ++ /* The name of the section; the name isn't a copy, the pointer is ++ the same as that passed to bfd_make_section. */ ++ const char *name; ++ ++ /* A unique sequence number. */ ++ int id; ++ ++ /* Which section in the bfd; 0..n-1 as sections are created in a bfd. */ ++ int index; ++ ++ /* The next section in the list belonging to the BFD, or NULL. */ ++ struct bfd_section *next; ++ ++ /* The previous section in the list belonging to the BFD, or NULL. */ ++ struct bfd_section *prev; ++ ++ /* The field flags contains attributes of the section. Some ++ flags are read in from the object file, and some are ++ synthesized from other information. */ ++ flagword flags; ++ ++#define SEC_NO_FLAGS 0x000 ++ ++ /* Tells the OS to allocate space for this section when loading. ++ This is clear for a section containing debug information only. */ ++#define SEC_ALLOC 0x001 ++ ++ /* Tells the OS to load the section from the file when loading. ++ This is clear for a .bss section. */ ++#define SEC_LOAD 0x002 ++ ++ /* The section contains data still to be relocated, so there is ++ some relocation information too. */ ++#define SEC_RELOC 0x004 ++ ++ /* A signal to the OS that the section contains read only data. */ ++#define SEC_READONLY 0x008 ++ ++ /* The section contains code only. */ ++#define SEC_CODE 0x010 ++ ++ /* The section contains data only. */ ++#define SEC_DATA 0x020 ++ ++ /* The section will reside in ROM. */ ++#define SEC_ROM 0x040 ++ ++ /* The section contains constructor information. This section ++ type is used by the linker to create lists of constructors and ++ destructors used by <>. When a back end sees a symbol ++ which should be used in a constructor list, it creates a new ++ section for the type of name (e.g., <<__CTOR_LIST__>>), attaches ++ the symbol to it, and builds a relocation. To build the lists ++ of constructors, all the linker has to do is catenate all the ++ sections called <<__CTOR_LIST__>> and relocate the data ++ contained within - exactly the operations it would peform on ++ standard data. */ ++#define SEC_CONSTRUCTOR 0x080 ++ ++ /* The section has contents - a data section could be ++ <> | <>; a debug section could be ++ <> */ ++#define SEC_HAS_CONTENTS 0x100 ++ ++ /* An instruction to the linker to not output the section ++ even if it has information which would normally be written. */ ++#define SEC_NEVER_LOAD 0x200 ++ ++ /* The section contains thread local data. */ ++#define SEC_THREAD_LOCAL 0x400 ++ ++ /* The section has GOT references. This flag is only for the ++ linker, and is currently only used by the elf32-hppa back end. ++ It will be set if global offset table references were detected ++ in this section, which indicate to the linker that the section ++ contains PIC code, and must be handled specially when doing a ++ static link. */ ++#define SEC_HAS_GOT_REF 0x800 ++ ++ /* The section contains common symbols (symbols may be defined ++ multiple times, the value of a symbol is the amount of ++ space it requires, and the largest symbol value is the one ++ used). Most targets have exactly one of these (which we ++ translate to bfd_com_section_ptr), but ECOFF has two. */ ++#define SEC_IS_COMMON 0x1000 ++ ++ /* The section contains only debugging information. For ++ example, this is set for ELF .debug and .stab sections. ++ strip tests this flag to see if a section can be ++ discarded. */ ++#define SEC_DEBUGGING 0x2000 ++ ++ /* The contents of this section are held in memory pointed to ++ by the contents field. This is checked by bfd_get_section_contents, ++ and the data is retrieved from memory if appropriate. */ ++#define SEC_IN_MEMORY 0x4000 ++ ++ /* The contents of this section are to be excluded by the ++ linker for executable and shared objects unless those ++ objects are to be further relocated. */ ++#define SEC_EXCLUDE 0x8000 ++ ++ /* The contents of this section are to be sorted based on the sum of ++ the symbol and addend values specified by the associated relocation ++ entries. Entries without associated relocation entries will be ++ appended to the end of the section in an unspecified order. */ ++#define SEC_SORT_ENTRIES 0x10000 ++ ++ /* When linking, duplicate sections of the same name should be ++ discarded, rather than being combined into a single section as ++ is usually done. This is similar to how common symbols are ++ handled. See SEC_LINK_DUPLICATES below. */ ++#define SEC_LINK_ONCE 0x20000 ++ ++ /* If SEC_LINK_ONCE is set, this bitfield describes how the linker ++ should handle duplicate sections. */ ++#define SEC_LINK_DUPLICATES 0x40000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that duplicate ++ sections with the same name should simply be discarded. */ ++#define SEC_LINK_DUPLICATES_DISCARD 0x0 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if there are any duplicate sections, although ++ it should still only link one copy. */ ++#define SEC_LINK_DUPLICATES_ONE_ONLY 0x80000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if any duplicate sections are a different size. */ ++#define SEC_LINK_DUPLICATES_SAME_SIZE 0x100000 ++ ++ /* This value for SEC_LINK_DUPLICATES means that the linker ++ should warn if any duplicate sections contain different ++ contents. */ ++#define SEC_LINK_DUPLICATES_SAME_CONTENTS \ ++ (SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE) ++ ++ /* This section was created by the linker as part of dynamic ++ relocation or other arcane processing. It is skipped when ++ going through the first-pass output, trusting that someone ++ else up the line will take care of it later. */ ++#define SEC_LINKER_CREATED 0x200000 ++ ++ /* This section should not be subject to garbage collection. */ ++#define SEC_KEEP 0x400000 ++ ++ /* This section contains "short" data, and should be placed ++ "near" the GP. */ ++#define SEC_SMALL_DATA 0x800000 ++ ++ /* Attempt to merge identical entities in the section. ++ Entity size is given in the entsize field. */ ++#define SEC_MERGE 0x1000000 ++ ++ /* If given with SEC_MERGE, entities to merge are zero terminated ++ strings where entsize specifies character size instead of fixed ++ size entries. */ ++#define SEC_STRINGS 0x2000000 ++ ++ /* This section contains data about section groups. */ ++#define SEC_GROUP 0x4000000 ++ ++ /* The section is a COFF shared library section. This flag is ++ only for the linker. If this type of section appears in ++ the input file, the linker must copy it to the output file ++ without changing the vma or size. FIXME: Although this ++ was originally intended to be general, it really is COFF ++ specific (and the flag was renamed to indicate this). It ++ might be cleaner to have some more general mechanism to ++ allow the back end to control what the linker does with ++ sections. */ ++#define SEC_COFF_SHARED_LIBRARY 0x10000000 ++ ++ /* This section contains data which may be shared with other ++ executables or shared objects. This is for COFF only. */ ++#define SEC_COFF_SHARED 0x20000000 ++ ++ /* When a section with this flag is being linked, then if the size of ++ the input section is less than a page, it should not cross a page ++ boundary. If the size of the input section is one page or more, ++ it should be aligned on a page boundary. This is for TI ++ TMS320C54X only. */ ++#define SEC_TIC54X_BLOCK 0x40000000 ++ ++ /* Conditionally link this section; do not link if there are no ++ references found to any symbol in the section. This is for TI ++ TMS320C54X only. */ ++#define SEC_TIC54X_CLINK 0x80000000 ++ ++ /* End of section flags. */ ++ ++ /* Some internal packed boolean fields. */ ++ ++ /* See the vma field. */ ++ unsigned int user_set_vma : 1; ++ ++ /* A mark flag used by some of the linker backends. */ ++ unsigned int linker_mark : 1; ++ ++ /* Another mark flag used by some of the linker backends. Set for ++ output sections that have an input section. */ ++ unsigned int linker_has_input : 1; ++ ++ /* Mark flags used by some linker backends for garbage collection. */ ++ unsigned int gc_mark : 1; ++ unsigned int gc_mark_from_eh : 1; ++ ++ /* The following flags are used by the ELF linker. */ ++ ++ /* Mark sections which have been allocated to segments. */ ++ unsigned int segment_mark : 1; ++ ++ /* Type of sec_info information. */ ++ unsigned int sec_info_type:3; ++#define ELF_INFO_TYPE_NONE 0 ++#define ELF_INFO_TYPE_STABS 1 ++#define ELF_INFO_TYPE_MERGE 2 ++#define ELF_INFO_TYPE_EH_FRAME 3 ++#define ELF_INFO_TYPE_JUST_SYMS 4 ++ ++ /* Nonzero if this section uses RELA relocations, rather than REL. */ ++ unsigned int use_rela_p:1; ++ ++ /* Bits used by various backends. The generic code doesn't touch ++ these fields. */ ++ ++ /* Nonzero if this section has TLS related relocations. */ ++ unsigned int has_tls_reloc:1; ++ ++ /* Nonzero if this section has a gp reloc. */ ++ unsigned int has_gp_reloc:1; ++ ++ /* Nonzero if this section needs the relax finalize pass. */ ++ unsigned int need_finalize_relax:1; ++ ++ /* Whether relocations have been processed. */ ++ unsigned int reloc_done : 1; ++ ++ /* End of internal packed boolean fields. */ ++ ++ /* The virtual memory address of the section - where it will be ++ at run time. The symbols are relocated against this. The ++ user_set_vma flag is maintained by bfd; if it's not set, the ++ backend can assign addresses (for example, in <>, where ++ the default address for <<.data>> is dependent on the specific ++ target and various flags). */ ++ bfd_vma vma; ++ ++ /* The load address of the section - where it would be in a ++ rom image; really only used for writing section header ++ information. */ ++ bfd_vma lma; ++ ++ /* The size of the section in octets, as it will be output. ++ Contains a value even if the section has no contents (e.g., the ++ size of <<.bss>>). */ ++ bfd_size_type size; ++ ++ /* For input sections, the original size on disk of the section, in ++ octets. This field is used by the linker relaxation code. It is ++ currently only set for sections where the linker relaxation scheme ++ doesn't cache altered section and reloc contents (stabs, eh_frame, ++ SEC_MERGE, some coff relaxing targets), and thus the original size ++ needs to be kept to read the section multiple times. ++ For output sections, rawsize holds the section size calculated on ++ a previous linker relaxation pass. */ ++ bfd_size_type rawsize; ++ ++ /* If this section is going to be output, then this value is the ++ offset in *bytes* into the output section of the first byte in the ++ input section (byte ==> smallest addressable unit on the ++ target). In most cases, if this was going to start at the ++ 100th octet (8-bit quantity) in the output section, this value ++ would be 100. However, if the target byte size is 16 bits ++ (bfd_octets_per_byte is "2"), this value would be 50. */ ++ bfd_vma output_offset; ++ ++ /* The output section through which to map on output. */ ++ struct bfd_section *output_section; ++ ++ /* The alignment requirement of the section, as an exponent of 2 - ++ e.g., 3 aligns to 2^3 (or 8). */ ++ unsigned int alignment_power; ++ ++ /* If an input section, a pointer to a vector of relocation ++ records for the data in this section. */ ++ struct reloc_cache_entry *relocation; ++ ++ /* If an output section, a pointer to a vector of pointers to ++ relocation records for the data in this section. */ ++ struct reloc_cache_entry **orelocation; ++ ++ /* The number of relocation records in one of the above. */ ++ unsigned reloc_count; ++ ++ /* Information below is back end specific - and not always used ++ or updated. */ ++ ++ /* File position of section data. */ ++ file_ptr filepos; ++ ++ /* File position of relocation info. */ ++ file_ptr rel_filepos; ++ ++ /* File position of line data. */ ++ file_ptr line_filepos; ++ ++ /* Pointer to data for applications. */ ++ void *userdata; ++ ++ /* If the SEC_IN_MEMORY flag is set, this points to the actual ++ contents. */ ++ unsigned char *contents; ++ ++ /* Attached line number information. */ ++ alent *lineno; ++ ++ /* Number of line number records. */ ++ unsigned int lineno_count; ++ ++ /* Entity size for merging purposes. */ ++ unsigned int entsize; ++ ++ /* Points to the kept section if this section is a link-once section, ++ and is discarded. */ ++ struct bfd_section *kept_section; ++ ++ /* When a section is being output, this value changes as more ++ linenumbers are written out. */ ++ file_ptr moving_line_filepos; ++ ++ /* What the section number is in the target world. */ ++ int target_index; ++ ++ void *used_by_bfd; ++ ++ /* If this is a constructor section then here is a list of the ++ relocations created to relocate items within it. */ ++ struct relent_chain *constructor_chain; ++ ++ /* The BFD which owns the section. */ ++ bfd *owner; ++ ++ /* A symbol which points at this section only. */ ++ struct bfd_symbol *symbol; ++ struct bfd_symbol **symbol_ptr_ptr; ++ ++ /* Early in the link process, map_head and map_tail are used to build ++ a list of input sections attached to an output section. Later, ++ output sections use these fields for a list of bfd_link_order ++ structs. */ ++ union { ++ struct bfd_link_order *link_order; ++ struct bfd_section *s; ++ } map_head, map_tail; ++} asection; ++ ++/* These sections are global, and are managed by BFD. The application ++ and target back end are not permitted to change the values in ++ these sections. New code should use the section_ptr macros rather ++ than referring directly to the const sections. The const sections ++ may eventually vanish. */ ++#define BFD_ABS_SECTION_NAME "*ABS*" ++#define BFD_UND_SECTION_NAME "*UND*" ++#define BFD_COM_SECTION_NAME "*COM*" ++#define BFD_IND_SECTION_NAME "*IND*" ++ ++/* The absolute section. */ ++extern asection bfd_abs_section; ++#define bfd_abs_section_ptr ((asection *) &bfd_abs_section) ++#define bfd_is_abs_section(sec) ((sec) == bfd_abs_section_ptr) ++/* Pointer to the undefined section. */ ++extern asection bfd_und_section; ++#define bfd_und_section_ptr ((asection *) &bfd_und_section) ++#define bfd_is_und_section(sec) ((sec) == bfd_und_section_ptr) ++/* Pointer to the common section. */ ++extern asection bfd_com_section; ++#define bfd_com_section_ptr ((asection *) &bfd_com_section) ++/* Pointer to the indirect section. */ ++extern asection bfd_ind_section; ++#define bfd_ind_section_ptr ((asection *) &bfd_ind_section) ++#define bfd_is_ind_section(sec) ((sec) == bfd_ind_section_ptr) ++ ++#define bfd_is_const_section(SEC) \ ++ ( ((SEC) == bfd_abs_section_ptr) \ ++ || ((SEC) == bfd_und_section_ptr) \ ++ || ((SEC) == bfd_com_section_ptr) \ ++ || ((SEC) == bfd_ind_section_ptr)) ++ ++extern const struct bfd_symbol * const bfd_abs_symbol; ++extern const struct bfd_symbol * const bfd_com_symbol; ++extern const struct bfd_symbol * const bfd_und_symbol; ++extern const struct bfd_symbol * const bfd_ind_symbol; ++ ++/* Macros to handle insertion and deletion of a bfd's sections. These ++ only handle the list pointers, ie. do not adjust section_count, ++ target_index etc. */ ++#define bfd_section_list_remove(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ asection *_next = _s->next; \ ++ asection *_prev = _s->prev; \ ++ if (_prev) \ ++ _prev->next = _next; \ ++ else \ ++ (ABFD)->sections = _next; \ ++ if (_next) \ ++ _next->prev = _prev; \ ++ else \ ++ (ABFD)->section_last = _prev; \ ++ } \ ++ while (0) ++#define bfd_section_list_append(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ bfd *_abfd = ABFD; \ ++ _s->next = NULL; \ ++ if (_abfd->section_last) \ ++ { \ ++ _s->prev = _abfd->section_last; \ ++ _abfd->section_last->next = _s; \ ++ } \ ++ else \ ++ { \ ++ _s->prev = NULL; \ ++ _abfd->sections = _s; \ ++ } \ ++ _abfd->section_last = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_prepend(ABFD, S) \ ++ do \ ++ { \ ++ asection *_s = S; \ ++ bfd *_abfd = ABFD; \ ++ _s->prev = NULL; \ ++ if (_abfd->sections) \ ++ { \ ++ _s->next = _abfd->sections; \ ++ _abfd->sections->prev = _s; \ ++ } \ ++ else \ ++ { \ ++ _s->next = NULL; \ ++ _abfd->section_last = _s; \ ++ } \ ++ _abfd->sections = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_insert_after(ABFD, A, S) \ ++ do \ ++ { \ ++ asection *_a = A; \ ++ asection *_s = S; \ ++ asection *_next = _a->next; \ ++ _s->next = _next; \ ++ _s->prev = _a; \ ++ _a->next = _s; \ ++ if (_next) \ ++ _next->prev = _s; \ ++ else \ ++ (ABFD)->section_last = _s; \ ++ } \ ++ while (0) ++#define bfd_section_list_insert_before(ABFD, B, S) \ ++ do \ ++ { \ ++ asection *_b = B; \ ++ asection *_s = S; \ ++ asection *_prev = _b->prev; \ ++ _s->prev = _prev; \ ++ _s->next = _b; \ ++ _b->prev = _s; \ ++ if (_prev) \ ++ _prev->next = _s; \ ++ else \ ++ (ABFD)->sections = _s; \ ++ } \ ++ while (0) ++#define bfd_section_removed_from_list(ABFD, S) \ ++ ((S)->next == NULL ? (ABFD)->section_last != (S) : (S)->next->prev != (S)) ++ ++void bfd_section_list_clear (bfd *); ++ ++asection *bfd_get_section_by_name (bfd *abfd, const char *name); ++ ++asection *bfd_get_section_by_name_if ++ (bfd *abfd, ++ const char *name, ++ bfd_boolean (*func) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++char *bfd_get_unique_section_name ++ (bfd *abfd, const char *templat, int *count); ++ ++asection *bfd_make_section_old_way (bfd *abfd, const char *name); ++ ++asection *bfd_make_section_anyway_with_flags ++ (bfd *abfd, const char *name, flagword flags); ++ ++asection *bfd_make_section_anyway (bfd *abfd, const char *name); ++ ++asection *bfd_make_section_with_flags ++ (bfd *, const char *name, flagword flags); ++ ++asection *bfd_make_section (bfd *, const char *name); ++ ++bfd_boolean bfd_set_section_flags ++ (bfd *abfd, asection *sec, flagword flags); ++ ++void bfd_map_over_sections ++ (bfd *abfd, ++ void (*func) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++asection *bfd_sections_find_if ++ (bfd *abfd, ++ bfd_boolean (*operation) (bfd *abfd, asection *sect, void *obj), ++ void *obj); ++ ++bfd_boolean bfd_set_section_size ++ (bfd *abfd, asection *sec, bfd_size_type val); ++ ++bfd_boolean bfd_set_section_contents ++ (bfd *abfd, asection *section, const void *data, ++ file_ptr offset, bfd_size_type count); ++ ++bfd_boolean bfd_get_section_contents ++ (bfd *abfd, asection *section, void *location, file_ptr offset, ++ bfd_size_type count); ++ ++bfd_boolean bfd_malloc_and_get_section ++ (bfd *abfd, asection *section, bfd_byte **buf); ++ ++bfd_boolean bfd_copy_private_section_data ++ (bfd *ibfd, asection *isec, bfd *obfd, asection *osec); ++ ++#define bfd_copy_private_section_data(ibfd, isection, obfd, osection) \ ++ BFD_SEND (obfd, _bfd_copy_private_section_data, \ ++ (ibfd, isection, obfd, osection)) ++bfd_boolean bfd_generic_is_group_section (bfd *, const asection *sec); ++ ++bfd_boolean bfd_generic_discard_group (bfd *abfd, asection *group); ++ ++/* Extracted from archures.c. */ ++enum bfd_architecture ++{ ++ bfd_arch_unknown, /* File arch not known. */ ++ bfd_arch_obscure, /* Arch known, not one of these. */ ++ bfd_arch_m68k, /* Motorola 68xxx */ ++#define bfd_mach_m68000 1 ++#define bfd_mach_m68008 2 ++#define bfd_mach_m68010 3 ++#define bfd_mach_m68020 4 ++#define bfd_mach_m68030 5 ++#define bfd_mach_m68040 6 ++#define bfd_mach_m68060 7 ++#define bfd_mach_cpu32 8 ++#define bfd_mach_mcf5200 9 ++#define bfd_mach_mcf5206e 10 ++#define bfd_mach_mcf5307 11 ++#define bfd_mach_mcf5407 12 ++#define bfd_mach_mcf528x 13 ++#define bfd_mach_mcfv4e 14 ++#define bfd_mach_mcf521x 15 ++#define bfd_mach_mcf5249 16 ++#define bfd_mach_mcf547x 17 ++#define bfd_mach_mcf548x 18 ++ bfd_arch_vax, /* DEC Vax */ ++ bfd_arch_i960, /* Intel 960 */ ++ /* The order of the following is important. ++ lower number indicates a machine type that ++ only accepts a subset of the instructions ++ available to machines with higher numbers. ++ The exception is the "ca", which is ++ incompatible with all other machines except ++ "core". */ ++ ++#define bfd_mach_i960_core 1 ++#define bfd_mach_i960_ka_sa 2 ++#define bfd_mach_i960_kb_sb 3 ++#define bfd_mach_i960_mc 4 ++#define bfd_mach_i960_xa 5 ++#define bfd_mach_i960_ca 6 ++#define bfd_mach_i960_jx 7 ++#define bfd_mach_i960_hx 8 ++ ++ bfd_arch_or32, /* OpenRISC 32 */ ++ ++ bfd_arch_a29k, /* AMD 29000 */ ++ bfd_arch_sparc, /* SPARC */ ++#define bfd_mach_sparc 1 ++/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */ ++#define bfd_mach_sparc_sparclet 2 ++#define bfd_mach_sparc_sparclite 3 ++#define bfd_mach_sparc_v8plus 4 ++#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */ ++#define bfd_mach_sparc_sparclite_le 6 ++#define bfd_mach_sparc_v9 7 ++#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */ ++#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */ ++#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */ ++/* Nonzero if MACH has the v9 instruction set. */ ++#define bfd_mach_sparc_v9_p(mach) \ ++ ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \ ++ && (mach) != bfd_mach_sparc_sparclite_le) ++/* Nonzero if MACH is a 64 bit sparc architecture. */ ++#define bfd_mach_sparc_64bit_p(mach) \ ++ ((mach) >= bfd_mach_sparc_v9 && (mach) != bfd_mach_sparc_v8plusb) ++ bfd_arch_mips, /* MIPS Rxxxx */ ++#define bfd_mach_mips3000 3000 ++#define bfd_mach_mips3900 3900 ++#define bfd_mach_mips4000 4000 ++#define bfd_mach_mips4010 4010 ++#define bfd_mach_mips4100 4100 ++#define bfd_mach_mips4111 4111 ++#define bfd_mach_mips4120 4120 ++#define bfd_mach_mips4300 4300 ++#define bfd_mach_mips4400 4400 ++#define bfd_mach_mips4600 4600 ++#define bfd_mach_mips4650 4650 ++#define bfd_mach_mips5000 5000 ++#define bfd_mach_mips5400 5400 ++#define bfd_mach_mips5500 5500 ++#define bfd_mach_mips6000 6000 ++#define bfd_mach_mips7000 7000 ++#define bfd_mach_mips8000 8000 ++#define bfd_mach_mips9000 9000 ++#define bfd_mach_mips10000 10000 ++#define bfd_mach_mips12000 12000 ++#define bfd_mach_mips16 16 ++#define bfd_mach_mips5 5 ++#define bfd_mach_mips_sb1 12310201 /* octal 'SB', 01 */ ++#define bfd_mach_mipsisa32 32 ++#define bfd_mach_mipsisa32r2 33 ++#define bfd_mach_mipsisa64 64 ++#define bfd_mach_mipsisa64r2 65 ++ bfd_arch_i386, /* Intel 386 */ ++#define bfd_mach_i386_i386 1 ++#define bfd_mach_i386_i8086 2 ++#define bfd_mach_i386_i386_intel_syntax 3 ++#define bfd_mach_x86_64 64 ++#define bfd_mach_x86_64_intel_syntax 65 ++ bfd_arch_we32k, /* AT&T WE32xxx */ ++ bfd_arch_tahoe, /* CCI/Harris Tahoe */ ++ bfd_arch_i860, /* Intel 860 */ ++ bfd_arch_i370, /* IBM 360/370 Mainframes */ ++ bfd_arch_romp, /* IBM ROMP PC/RT */ ++ bfd_arch_alliant, /* Alliant */ ++ bfd_arch_convex, /* Convex */ ++ bfd_arch_m88k, /* Motorola 88xxx */ ++ bfd_arch_m98k, /* Motorola 98xxx */ ++ bfd_arch_pyramid, /* Pyramid Technology */ ++ bfd_arch_h8300, /* Renesas H8/300 (formerly Hitachi H8/300) */ ++#define bfd_mach_h8300 1 ++#define bfd_mach_h8300h 2 ++#define bfd_mach_h8300s 3 ++#define bfd_mach_h8300hn 4 ++#define bfd_mach_h8300sn 5 ++#define bfd_mach_h8300sx 6 ++#define bfd_mach_h8300sxn 7 ++ bfd_arch_pdp11, /* DEC PDP-11 */ ++ bfd_arch_powerpc, /* PowerPC */ ++#define bfd_mach_ppc 32 ++#define bfd_mach_ppc64 64 ++#define bfd_mach_ppc_403 403 ++#define bfd_mach_ppc_403gc 4030 ++#define bfd_mach_ppc_505 505 ++#define bfd_mach_ppc_601 601 ++#define bfd_mach_ppc_602 602 ++#define bfd_mach_ppc_603 603 ++#define bfd_mach_ppc_ec603e 6031 ++#define bfd_mach_ppc_604 604 ++#define bfd_mach_ppc_620 620 ++#define bfd_mach_ppc_630 630 ++#define bfd_mach_ppc_750 750 ++#define bfd_mach_ppc_860 860 ++#define bfd_mach_ppc_a35 35 ++#define bfd_mach_ppc_rs64ii 642 ++#define bfd_mach_ppc_rs64iii 643 ++#define bfd_mach_ppc_7400 7400 ++#define bfd_mach_ppc_e500 500 ++ bfd_arch_rs6000, /* IBM RS/6000 */ ++#define bfd_mach_rs6k 6000 ++#define bfd_mach_rs6k_rs1 6001 ++#define bfd_mach_rs6k_rsc 6003 ++#define bfd_mach_rs6k_rs2 6002 ++ bfd_arch_hppa, /* HP PA RISC */ ++#define bfd_mach_hppa10 10 ++#define bfd_mach_hppa11 11 ++#define bfd_mach_hppa20 20 ++#define bfd_mach_hppa20w 25 ++ bfd_arch_d10v, /* Mitsubishi D10V */ ++#define bfd_mach_d10v 1 ++#define bfd_mach_d10v_ts2 2 ++#define bfd_mach_d10v_ts3 3 ++ bfd_arch_d30v, /* Mitsubishi D30V */ ++ bfd_arch_dlx, /* DLX */ ++ bfd_arch_m68hc11, /* Motorola 68HC11 */ ++ bfd_arch_m68hc12, /* Motorola 68HC12 */ ++#define bfd_mach_m6812_default 0 ++#define bfd_mach_m6812 1 ++#define bfd_mach_m6812s 2 ++ bfd_arch_z8k, /* Zilog Z8000 */ ++#define bfd_mach_z8001 1 ++#define bfd_mach_z8002 2 ++ bfd_arch_h8500, /* Renesas H8/500 (formerly Hitachi H8/500) */ ++ bfd_arch_sh, /* Renesas / SuperH SH (formerly Hitachi SH) */ ++#define bfd_mach_sh 1 ++#define bfd_mach_sh2 0x20 ++#define bfd_mach_sh_dsp 0x2d ++#define bfd_mach_sh2a 0x2a ++#define bfd_mach_sh2a_nofpu 0x2b ++#define bfd_mach_sh2a_nofpu_or_sh4_nommu_nofpu 0x2a1 ++#define bfd_mach_sh2a_nofpu_or_sh3_nommu 0x2a2 ++#define bfd_mach_sh2a_or_sh4 0x2a3 ++#define bfd_mach_sh2a_or_sh3e 0x2a4 ++#define bfd_mach_sh2e 0x2e ++#define bfd_mach_sh3 0x30 ++#define bfd_mach_sh3_nommu 0x31 ++#define bfd_mach_sh3_dsp 0x3d ++#define bfd_mach_sh3e 0x3e ++#define bfd_mach_sh4 0x40 ++#define bfd_mach_sh4_nofpu 0x41 ++#define bfd_mach_sh4_nommu_nofpu 0x42 ++#define bfd_mach_sh4a 0x4a ++#define bfd_mach_sh4a_nofpu 0x4b ++#define bfd_mach_sh4al_dsp 0x4d ++#define bfd_mach_sh5 0x50 ++ bfd_arch_alpha, /* Dec Alpha */ ++#define bfd_mach_alpha_ev4 0x10 ++#define bfd_mach_alpha_ev5 0x20 ++#define bfd_mach_alpha_ev6 0x30 ++ bfd_arch_arm, /* Advanced Risc Machines ARM. */ ++#define bfd_mach_arm_unknown 0 ++#define bfd_mach_arm_2 1 ++#define bfd_mach_arm_2a 2 ++#define bfd_mach_arm_3 3 ++#define bfd_mach_arm_3M 4 ++#define bfd_mach_arm_4 5 ++#define bfd_mach_arm_4T 6 ++#define bfd_mach_arm_5 7 ++#define bfd_mach_arm_5T 8 ++#define bfd_mach_arm_5TE 9 ++#define bfd_mach_arm_XScale 10 ++#define bfd_mach_arm_ep9312 11 ++#define bfd_mach_arm_iWMMXt 12 ++ bfd_arch_ns32k, /* National Semiconductors ns32000 */ ++ bfd_arch_w65, /* WDC 65816 */ ++ bfd_arch_tic30, /* Texas Instruments TMS320C30 */ ++ bfd_arch_tic4x, /* Texas Instruments TMS320C3X/4X */ ++#define bfd_mach_tic3x 30 ++#define bfd_mach_tic4x 40 ++ bfd_arch_tic54x, /* Texas Instruments TMS320C54X */ ++ bfd_arch_tic80, /* TI TMS320c80 (MVP) */ ++ bfd_arch_v850, /* NEC V850 */ ++#define bfd_mach_v850 1 ++#define bfd_mach_v850e 'E' ++#define bfd_mach_v850e1 '1' ++ bfd_arch_arc, /* ARC Cores */ ++#define bfd_mach_arc_5 5 ++#define bfd_mach_arc_6 6 ++#define bfd_mach_arc_7 7 ++#define bfd_mach_arc_8 8 ++ bfd_arch_m32c, /* Renesas M16C/M32C. */ ++#define bfd_mach_m16c 0x75 ++#define bfd_mach_m32c 0x78 ++ bfd_arch_m32r, /* Renesas M32R (formerly Mitsubishi M32R/D) */ ++#define bfd_mach_m32r 1 /* For backwards compatibility. */ ++#define bfd_mach_m32rx 'x' ++#define bfd_mach_m32r2 '2' ++ bfd_arch_mn10200, /* Matsushita MN10200 */ ++ bfd_arch_mn10300, /* Matsushita MN10300 */ ++#define bfd_mach_mn10300 300 ++#define bfd_mach_am33 330 ++#define bfd_mach_am33_2 332 ++ bfd_arch_fr30, ++#define bfd_mach_fr30 0x46523330 ++ bfd_arch_frv, ++#define bfd_mach_frv 1 ++#define bfd_mach_frvsimple 2 ++#define bfd_mach_fr300 300 ++#define bfd_mach_fr400 400 ++#define bfd_mach_fr450 450 ++#define bfd_mach_frvtomcat 499 /* fr500 prototype */ ++#define bfd_mach_fr500 500 ++#define bfd_mach_fr550 550 ++ bfd_arch_mcore, ++ bfd_arch_ia64, /* HP/Intel ia64 */ ++#define bfd_mach_ia64_elf64 64 ++#define bfd_mach_ia64_elf32 32 ++ bfd_arch_ip2k, /* Ubicom IP2K microcontrollers. */ ++#define bfd_mach_ip2022 1 ++#define bfd_mach_ip2022ext 2 ++ bfd_arch_iq2000, /* Vitesse IQ2000. */ ++#define bfd_mach_iq2000 1 ++#define bfd_mach_iq10 2 ++ bfd_arch_ms1, ++#define bfd_mach_ms1 1 ++#define bfd_mach_mrisc2 2 ++ bfd_arch_pj, ++ bfd_arch_avr, /* Atmel AVR microcontrollers. */ ++#define bfd_mach_avr1 1 ++#define bfd_mach_avr2 2 ++#define bfd_mach_avr3 3 ++#define bfd_mach_avr4 4 ++#define bfd_mach_avr5 5 ++ bfd_arch_cr16c, /* National Semiconductor CompactRISC. */ ++#define bfd_mach_cr16c 1 ++ bfd_arch_crx, /* National Semiconductor CRX. */ ++#define bfd_mach_crx 1 ++ bfd_arch_cris, /* Axis CRIS */ ++#define bfd_mach_cris_v0_v10 255 ++#define bfd_mach_cris_v32 32 ++#define bfd_mach_cris_v10_v32 1032 ++ bfd_arch_s390, /* IBM s390 */ ++#define bfd_mach_s390_31 31 ++#define bfd_mach_s390_64 64 ++ bfd_arch_openrisc, /* OpenRISC */ ++ bfd_arch_mmix, /* Donald Knuth's educational processor. */ ++ bfd_arch_xstormy16, ++#define bfd_mach_xstormy16 1 ++ bfd_arch_msp430, /* Texas Instruments MSP430 architecture. */ ++#define bfd_mach_msp11 11 ++#define bfd_mach_msp110 110 ++#define bfd_mach_msp12 12 ++#define bfd_mach_msp13 13 ++#define bfd_mach_msp14 14 ++#define bfd_mach_msp15 15 ++#define bfd_mach_msp16 16 ++#define bfd_mach_msp31 31 ++#define bfd_mach_msp32 32 ++#define bfd_mach_msp33 33 ++#define bfd_mach_msp41 41 ++#define bfd_mach_msp42 42 ++#define bfd_mach_msp43 43 ++#define bfd_mach_msp44 44 ++ bfd_arch_xtensa, /* Tensilica's Xtensa cores. */ ++#define bfd_mach_xtensa 1 ++ bfd_arch_maxq, /* Dallas MAXQ 10/20 */ ++#define bfd_mach_maxq10 10 ++#define bfd_mach_maxq20 20 ++ bfd_arch_last ++ }; ++ ++typedef struct bfd_arch_info ++{ ++ int bits_per_word; ++ int bits_per_address; ++ int bits_per_byte; ++ enum bfd_architecture arch; ++ unsigned long mach; ++ const char *arch_name; ++ const char *printable_name; ++ unsigned int section_align_power; ++ /* TRUE if this is the default machine for the architecture. ++ The default arch should be the first entry for an arch so that ++ all the entries for that arch can be accessed via <>. */ ++ bfd_boolean the_default; ++ const struct bfd_arch_info * (*compatible) ++ (const struct bfd_arch_info *a, const struct bfd_arch_info *b); ++ ++ bfd_boolean (*scan) (const struct bfd_arch_info *, const char *); ++ ++ const struct bfd_arch_info *next; ++} ++bfd_arch_info_type; ++ ++const char *bfd_printable_name (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_scan_arch (const char *string); ++ ++const char **bfd_arch_list (void); ++ ++const bfd_arch_info_type *bfd_arch_get_compatible ++ (const bfd *abfd, const bfd *bbfd, bfd_boolean accept_unknowns); ++ ++void bfd_set_arch_info (bfd *abfd, const bfd_arch_info_type *arg); ++ ++enum bfd_architecture bfd_get_arch (bfd *abfd); ++ ++unsigned long bfd_get_mach (bfd *abfd); ++ ++unsigned int bfd_arch_bits_per_byte (bfd *abfd); ++ ++unsigned int bfd_arch_bits_per_address (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_get_arch_info (bfd *abfd); ++ ++const bfd_arch_info_type *bfd_lookup_arch ++ (enum bfd_architecture arch, unsigned long machine); ++ ++const char *bfd_printable_arch_mach ++ (enum bfd_architecture arch, unsigned long machine); ++ ++unsigned int bfd_octets_per_byte (bfd *abfd); ++ ++unsigned int bfd_arch_mach_octets_per_byte ++ (enum bfd_architecture arch, unsigned long machine); ++ ++/* Extracted from reloc.c. */ ++typedef enum bfd_reloc_status ++{ ++ /* No errors detected. */ ++ bfd_reloc_ok, ++ ++ /* The relocation was performed, but there was an overflow. */ ++ bfd_reloc_overflow, ++ ++ /* The address to relocate was not within the section supplied. */ ++ bfd_reloc_outofrange, ++ ++ /* Used by special functions. */ ++ bfd_reloc_continue, ++ ++ /* Unsupported relocation size requested. */ ++ bfd_reloc_notsupported, ++ ++ /* Unused. */ ++ bfd_reloc_other, ++ ++ /* The symbol to relocate against was undefined. */ ++ bfd_reloc_undefined, ++ ++ /* The relocation was performed, but may not be ok - presently ++ generated only when linking i960 coff files with i960 b.out ++ symbols. If this type is returned, the error_message argument ++ to bfd_perform_relocation will be set. */ ++ bfd_reloc_dangerous ++ } ++ bfd_reloc_status_type; ++ ++ ++typedef struct reloc_cache_entry ++{ ++ /* A pointer into the canonical table of pointers. */ ++ struct bfd_symbol **sym_ptr_ptr; ++ ++ /* offset in section. */ ++ bfd_size_type address; ++ ++ /* addend for relocation value. */ ++ bfd_vma addend; ++ ++ /* Pointer to how to perform the required relocation. */ ++ reloc_howto_type *howto; ++ ++} ++arelent; ++ ++enum complain_overflow ++{ ++ /* Do not complain on overflow. */ ++ complain_overflow_dont, ++ ++ /* Complain if the bitfield overflows, whether it is considered ++ as signed or unsigned. */ ++ complain_overflow_bitfield, ++ ++ /* Complain if the value overflows when considered as signed ++ number. */ ++ complain_overflow_signed, ++ ++ /* Complain if the value overflows when considered as an ++ unsigned number. */ ++ complain_overflow_unsigned ++}; ++ ++struct reloc_howto_struct ++{ ++ /* The type field has mainly a documentary use - the back end can ++ do what it wants with it, though normally the back end's ++ external idea of what a reloc number is stored ++ in this field. For example, a PC relative word relocation ++ in a coff environment has the type 023 - because that's ++ what the outside world calls a R_PCRWORD reloc. */ ++ unsigned int type; ++ ++ /* The value the final relocation is shifted right by. This drops ++ unwanted data from the relocation. */ ++ unsigned int rightshift; ++ ++ /* The size of the item to be relocated. This is *not* a ++ power-of-two measure. To get the number of bytes operated ++ on by a type of relocation, use bfd_get_reloc_size. */ ++ int size; ++ ++ /* The number of bits in the item to be relocated. This is used ++ when doing overflow checking. */ ++ unsigned int bitsize; ++ ++ /* Notes that the relocation is relative to the location in the ++ data section of the addend. The relocation function will ++ subtract from the relocation value the address of the location ++ being relocated. */ ++ bfd_boolean pc_relative; ++ ++ /* The bit position of the reloc value in the destination. ++ The relocated value is left shifted by this amount. */ ++ unsigned int bitpos; ++ ++ /* What type of overflow error should be checked for when ++ relocating. */ ++ enum complain_overflow complain_on_overflow; ++ ++ /* If this field is non null, then the supplied function is ++ called rather than the normal function. This allows really ++ strange relocation methods to be accommodated (e.g., i960 callj ++ instructions). */ ++ bfd_reloc_status_type (*special_function) ++ (bfd *, arelent *, struct bfd_symbol *, void *, asection *, ++ bfd *, char **); ++ ++ /* The textual name of the relocation type. */ ++ char *name; ++ ++ /* Some formats record a relocation addend in the section contents ++ rather than with the relocation. For ELF formats this is the ++ distinction between USE_REL and USE_RELA (though the code checks ++ for USE_REL == 1/0). The value of this field is TRUE if the ++ addend is recorded with the section contents; when performing a ++ partial link (ld -r) the section contents (the data) will be ++ modified. The value of this field is FALSE if addends are ++ recorded with the relocation (in arelent.addend); when performing ++ a partial link the relocation will be modified. ++ All relocations for all ELF USE_RELA targets should set this field ++ to FALSE (values of TRUE should be looked on with suspicion). ++ However, the converse is not true: not all relocations of all ELF ++ USE_REL targets set this field to TRUE. Why this is so is peculiar ++ to each particular target. For relocs that aren't used in partial ++ links (e.g. GOT stuff) it doesn't matter what this is set to. */ ++ bfd_boolean partial_inplace; ++ ++ /* src_mask selects the part of the instruction (or data) to be used ++ in the relocation sum. If the target relocations don't have an ++ addend in the reloc, eg. ELF USE_REL, src_mask will normally equal ++ dst_mask to extract the addend from the section contents. If ++ relocations do have an addend in the reloc, eg. ELF USE_RELA, this ++ field should be zero. Non-zero values for ELF USE_RELA targets are ++ bogus as in those cases the value in the dst_mask part of the ++ section contents should be treated as garbage. */ ++ bfd_vma src_mask; ++ ++ /* dst_mask selects which parts of the instruction (or data) are ++ replaced with a relocated value. */ ++ bfd_vma dst_mask; ++ ++ /* When some formats create PC relative instructions, they leave ++ the value of the pc of the place being relocated in the offset ++ slot of the instruction, so that a PC relative relocation can ++ be made just by adding in an ordinary offset (e.g., sun3 a.out). ++ Some formats leave the displacement part of an instruction ++ empty (e.g., m88k bcs); this flag signals the fact. */ ++ bfd_boolean pcrel_offset; ++}; ++ ++#define HOWTO(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \ ++ { (unsigned) C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC } ++#define NEWHOWTO(FUNCTION, NAME, SIZE, REL, IN) \ ++ HOWTO (0, 0, SIZE, 0, REL, 0, complain_overflow_dont, FUNCTION, \ ++ NAME, FALSE, 0, 0, IN) ++ ++#define EMPTY_HOWTO(C) \ ++ HOWTO ((C), 0, 0, 0, FALSE, 0, complain_overflow_dont, NULL, \ ++ NULL, FALSE, 0, 0, FALSE) ++ ++#define HOWTO_PREPARE(relocation, symbol) \ ++ { \ ++ if (symbol != NULL) \ ++ { \ ++ if (bfd_is_com_section (symbol->section)) \ ++ { \ ++ relocation = 0; \ ++ } \ ++ else \ ++ { \ ++ relocation = symbol->value; \ ++ } \ ++ } \ ++ } ++ ++unsigned int bfd_get_reloc_size (reloc_howto_type *); ++ ++typedef struct relent_chain ++{ ++ arelent relent; ++ struct relent_chain *next; ++} ++arelent_chain; ++ ++bfd_reloc_status_type bfd_check_overflow ++ (enum complain_overflow how, ++ unsigned int bitsize, ++ unsigned int rightshift, ++ unsigned int addrsize, ++ bfd_vma relocation); ++ ++bfd_reloc_status_type bfd_perform_relocation ++ (bfd *abfd, ++ arelent *reloc_entry, ++ void *data, ++ asection *input_section, ++ bfd *output_bfd, ++ char **error_message); ++ ++bfd_reloc_status_type bfd_install_relocation ++ (bfd *abfd, ++ arelent *reloc_entry, ++ void *data, bfd_vma data_start, ++ asection *input_section, ++ char **error_message); ++ ++enum bfd_reloc_code_real { ++ _dummy_first_bfd_reloc_code_real, ++ ++ ++/* Basic absolute relocations of N bits. */ ++ BFD_RELOC_64, ++ BFD_RELOC_32, ++ BFD_RELOC_26, ++ BFD_RELOC_24, ++ BFD_RELOC_16, ++ BFD_RELOC_14, ++ BFD_RELOC_8, ++ ++/* PC-relative relocations. Sometimes these are relative to the address ++of the relocation itself; sometimes they are relative to the start of ++the section containing the relocation. It depends on the specific target. ++ ++The 24-bit relocation is used in some Intel 960 configurations. */ ++ BFD_RELOC_64_PCREL, ++ BFD_RELOC_32_PCREL, ++ BFD_RELOC_24_PCREL, ++ BFD_RELOC_16_PCREL, ++ BFD_RELOC_12_PCREL, ++ BFD_RELOC_8_PCREL, ++ ++/* Section relative relocations. Some targets need this for DWARF2. */ ++ BFD_RELOC_32_SECREL, ++ ++/* For ELF. */ ++ BFD_RELOC_32_GOT_PCREL, ++ BFD_RELOC_16_GOT_PCREL, ++ BFD_RELOC_8_GOT_PCREL, ++ BFD_RELOC_32_GOTOFF, ++ BFD_RELOC_16_GOTOFF, ++ BFD_RELOC_LO16_GOTOFF, ++ BFD_RELOC_HI16_GOTOFF, ++ BFD_RELOC_HI16_S_GOTOFF, ++ BFD_RELOC_8_GOTOFF, ++ BFD_RELOC_64_PLT_PCREL, ++ BFD_RELOC_32_PLT_PCREL, ++ BFD_RELOC_24_PLT_PCREL, ++ BFD_RELOC_16_PLT_PCREL, ++ BFD_RELOC_8_PLT_PCREL, ++ BFD_RELOC_64_PLTOFF, ++ BFD_RELOC_32_PLTOFF, ++ BFD_RELOC_16_PLTOFF, ++ BFD_RELOC_LO16_PLTOFF, ++ BFD_RELOC_HI16_PLTOFF, ++ BFD_RELOC_HI16_S_PLTOFF, ++ BFD_RELOC_8_PLTOFF, ++ ++/* Relocations used by 68K ELF. */ ++ BFD_RELOC_68K_GLOB_DAT, ++ BFD_RELOC_68K_JMP_SLOT, ++ BFD_RELOC_68K_RELATIVE, ++ ++/* Linkage-table relative. */ ++ BFD_RELOC_32_BASEREL, ++ BFD_RELOC_16_BASEREL, ++ BFD_RELOC_LO16_BASEREL, ++ BFD_RELOC_HI16_BASEREL, ++ BFD_RELOC_HI16_S_BASEREL, ++ BFD_RELOC_8_BASEREL, ++ BFD_RELOC_RVA, ++ ++/* Absolute 8-bit relocation, but used to form an address like 0xFFnn. */ ++ BFD_RELOC_8_FFnn, ++ ++/* These PC-relative relocations are stored as word displacements -- ++i.e., byte displacements shifted right two bits. The 30-bit word ++displacement (<<32_PCREL_S2>> -- 32 bits, shifted 2) is used on the ++SPARC. (SPARC tools generally refer to this as <>.) The ++signed 16-bit displacement is used on the MIPS, and the 23-bit ++displacement is used on the Alpha. */ ++ BFD_RELOC_32_PCREL_S2, ++ BFD_RELOC_16_PCREL_S2, ++ BFD_RELOC_23_PCREL_S2, ++ ++/* High 22 bits and low 10 bits of 32-bit value, placed into lower bits of ++the target word. These are used on the SPARC. */ ++ BFD_RELOC_HI22, ++ BFD_RELOC_LO10, ++ ++/* For systems that allocate a Global Pointer register, these are ++displacements off that register. These relocation types are ++handled specially, because the value the register will have is ++decided relatively late. */ ++ BFD_RELOC_GPREL16, ++ BFD_RELOC_GPREL32, ++ ++/* Reloc types used for i960/b.out. */ ++ BFD_RELOC_I960_CALLJ, ++ ++/* SPARC ELF relocations. There is probably some overlap with other ++relocation types already defined. */ ++ BFD_RELOC_NONE, ++ BFD_RELOC_SPARC_WDISP22, ++ BFD_RELOC_SPARC22, ++ BFD_RELOC_SPARC13, ++ BFD_RELOC_SPARC_GOT10, ++ BFD_RELOC_SPARC_GOT13, ++ BFD_RELOC_SPARC_GOT22, ++ BFD_RELOC_SPARC_PC10, ++ BFD_RELOC_SPARC_PC22, ++ BFD_RELOC_SPARC_WPLT30, ++ BFD_RELOC_SPARC_COPY, ++ BFD_RELOC_SPARC_GLOB_DAT, ++ BFD_RELOC_SPARC_JMP_SLOT, ++ BFD_RELOC_SPARC_RELATIVE, ++ BFD_RELOC_SPARC_UA16, ++ BFD_RELOC_SPARC_UA32, ++ BFD_RELOC_SPARC_UA64, ++ ++/* I think these are specific to SPARC a.out (e.g., Sun 4). */ ++ BFD_RELOC_SPARC_BASE13, ++ BFD_RELOC_SPARC_BASE22, ++ ++/* SPARC64 relocations */ ++#define BFD_RELOC_SPARC_64 BFD_RELOC_64 ++ BFD_RELOC_SPARC_10, ++ BFD_RELOC_SPARC_11, ++ BFD_RELOC_SPARC_OLO10, ++ BFD_RELOC_SPARC_HH22, ++ BFD_RELOC_SPARC_HM10, ++ BFD_RELOC_SPARC_LM22, ++ BFD_RELOC_SPARC_PC_HH22, ++ BFD_RELOC_SPARC_PC_HM10, ++ BFD_RELOC_SPARC_PC_LM22, ++ BFD_RELOC_SPARC_WDISP16, ++ BFD_RELOC_SPARC_WDISP19, ++ BFD_RELOC_SPARC_7, ++ BFD_RELOC_SPARC_6, ++ BFD_RELOC_SPARC_5, ++#define BFD_RELOC_SPARC_DISP64 BFD_RELOC_64_PCREL ++ BFD_RELOC_SPARC_PLT32, ++ BFD_RELOC_SPARC_PLT64, ++ BFD_RELOC_SPARC_HIX22, ++ BFD_RELOC_SPARC_LOX10, ++ BFD_RELOC_SPARC_H44, ++ BFD_RELOC_SPARC_M44, ++ BFD_RELOC_SPARC_L44, ++ BFD_RELOC_SPARC_REGISTER, ++ ++/* SPARC little endian relocation */ ++ BFD_RELOC_SPARC_REV32, ++ ++/* SPARC TLS relocations */ ++ BFD_RELOC_SPARC_TLS_GD_HI22, ++ BFD_RELOC_SPARC_TLS_GD_LO10, ++ BFD_RELOC_SPARC_TLS_GD_ADD, ++ BFD_RELOC_SPARC_TLS_GD_CALL, ++ BFD_RELOC_SPARC_TLS_LDM_HI22, ++ BFD_RELOC_SPARC_TLS_LDM_LO10, ++ BFD_RELOC_SPARC_TLS_LDM_ADD, ++ BFD_RELOC_SPARC_TLS_LDM_CALL, ++ BFD_RELOC_SPARC_TLS_LDO_HIX22, ++ BFD_RELOC_SPARC_TLS_LDO_LOX10, ++ BFD_RELOC_SPARC_TLS_LDO_ADD, ++ BFD_RELOC_SPARC_TLS_IE_HI22, ++ BFD_RELOC_SPARC_TLS_IE_LO10, ++ BFD_RELOC_SPARC_TLS_IE_LD, ++ BFD_RELOC_SPARC_TLS_IE_LDX, ++ BFD_RELOC_SPARC_TLS_IE_ADD, ++ BFD_RELOC_SPARC_TLS_LE_HIX22, ++ BFD_RELOC_SPARC_TLS_LE_LOX10, ++ BFD_RELOC_SPARC_TLS_DTPMOD32, ++ BFD_RELOC_SPARC_TLS_DTPMOD64, ++ BFD_RELOC_SPARC_TLS_DTPOFF32, ++ BFD_RELOC_SPARC_TLS_DTPOFF64, ++ BFD_RELOC_SPARC_TLS_TPOFF32, ++ BFD_RELOC_SPARC_TLS_TPOFF64, ++ ++/* Alpha ECOFF and ELF relocations. Some of these treat the symbol or ++"addend" in some special way. ++For GPDISP_HI16 ("gpdisp") relocations, the symbol is ignored when ++writing; when reading, it will be the absolute section symbol. The ++addend is the displacement in bytes of the "lda" instruction from ++the "ldah" instruction (which is at the address of this reloc). */ ++ BFD_RELOC_ALPHA_GPDISP_HI16, ++ ++/* For GPDISP_LO16 ("ignore") relocations, the symbol is handled as ++with GPDISP_HI16 relocs. The addend is ignored when writing the ++relocations out, and is filled in with the file's GP value on ++reading, for convenience. */ ++ BFD_RELOC_ALPHA_GPDISP_LO16, ++ ++/* The ELF GPDISP relocation is exactly the same as the GPDISP_HI16 ++relocation except that there is no accompanying GPDISP_LO16 ++relocation. */ ++ BFD_RELOC_ALPHA_GPDISP, ++ ++/* The Alpha LITERAL/LITUSE relocs are produced by a symbol reference; ++the assembler turns it into a LDQ instruction to load the address of ++the symbol, and then fills in a register in the real instruction. ++ ++The LITERAL reloc, at the LDQ instruction, refers to the .lita ++section symbol. The addend is ignored when writing, but is filled ++in with the file's GP value on reading, for convenience, as with the ++GPDISP_LO16 reloc. ++ ++The ELF_LITERAL reloc is somewhere between 16_GOTOFF and GPDISP_LO16. ++It should refer to the symbol to be referenced, as with 16_GOTOFF, ++but it generates output not based on the position within the .got ++section, but relative to the GP value chosen for the file during the ++final link stage. ++ ++The LITUSE reloc, on the instruction using the loaded address, gives ++information to the linker that it might be able to use to optimize ++away some literal section references. The symbol is ignored (read ++as the absolute section symbol), and the "addend" indicates the type ++of instruction using the register: ++1 - "memory" fmt insn ++2 - byte-manipulation (byte offset reg) ++3 - jsr (target of branch) */ ++ BFD_RELOC_ALPHA_LITERAL, ++ BFD_RELOC_ALPHA_ELF_LITERAL, ++ BFD_RELOC_ALPHA_LITUSE, ++ ++/* The HINT relocation indicates a value that should be filled into the ++"hint" field of a jmp/jsr/ret instruction, for possible branch- ++prediction logic which may be provided on some processors. */ ++ BFD_RELOC_ALPHA_HINT, ++ ++/* The LINKAGE relocation outputs a linkage pair in the object file, ++which is filled by the linker. */ ++ BFD_RELOC_ALPHA_LINKAGE, ++ ++/* The CODEADDR relocation outputs a STO_CA in the object file, ++which is filled by the linker. */ ++ BFD_RELOC_ALPHA_CODEADDR, ++ ++/* The GPREL_HI/LO relocations together form a 32-bit offset from the ++GP register. */ ++ BFD_RELOC_ALPHA_GPREL_HI16, ++ BFD_RELOC_ALPHA_GPREL_LO16, ++ ++/* Like BFD_RELOC_23_PCREL_S2, except that the source and target must ++share a common GP, and the target address is adjusted for ++STO_ALPHA_STD_GPLOAD. */ ++ BFD_RELOC_ALPHA_BRSGP, ++ ++/* Alpha thread-local storage relocations. */ ++ BFD_RELOC_ALPHA_TLSGD, ++ BFD_RELOC_ALPHA_TLSLDM, ++ BFD_RELOC_ALPHA_DTPMOD64, ++ BFD_RELOC_ALPHA_GOTDTPREL16, ++ BFD_RELOC_ALPHA_DTPREL64, ++ BFD_RELOC_ALPHA_DTPREL_HI16, ++ BFD_RELOC_ALPHA_DTPREL_LO16, ++ BFD_RELOC_ALPHA_DTPREL16, ++ BFD_RELOC_ALPHA_GOTTPREL16, ++ BFD_RELOC_ALPHA_TPREL64, ++ BFD_RELOC_ALPHA_TPREL_HI16, ++ BFD_RELOC_ALPHA_TPREL_LO16, ++ BFD_RELOC_ALPHA_TPREL16, ++ ++/* Bits 27..2 of the relocation address shifted right 2 bits; ++simple reloc otherwise. */ ++ BFD_RELOC_MIPS_JMP, ++ ++/* The MIPS16 jump instruction. */ ++ BFD_RELOC_MIPS16_JMP, ++ ++/* MIPS16 GP relative reloc. */ ++ BFD_RELOC_MIPS16_GPREL, ++ ++/* High 16 bits of 32-bit value; simple reloc. */ ++ BFD_RELOC_HI16, ++ ++/* High 16 bits of 32-bit value but the low 16 bits will be sign ++extended and added to form the final result. If the low 16 ++bits form a negative number, we need to add one to the high value ++to compensate for the borrow when the low bits are added. */ ++ BFD_RELOC_HI16_S, ++ ++/* Low 16 bits. */ ++ BFD_RELOC_LO16, ++ ++/* High 16 bits of 32-bit pc-relative value */ ++ BFD_RELOC_HI16_PCREL, ++ ++/* High 16 bits of 32-bit pc-relative value, adjusted */ ++ BFD_RELOC_HI16_S_PCREL, ++ ++/* Low 16 bits of pc-relative value */ ++ BFD_RELOC_LO16_PCREL, ++ ++/* MIPS16 high 16 bits of 32-bit value. */ ++ BFD_RELOC_MIPS16_HI16, ++ ++/* MIPS16 high 16 bits of 32-bit value but the low 16 bits will be sign ++extended and added to form the final result. If the low 16 ++bits form a negative number, we need to add one to the high value ++to compensate for the borrow when the low bits are added. */ ++ BFD_RELOC_MIPS16_HI16_S, ++ ++/* MIPS16 low 16 bits. */ ++ BFD_RELOC_MIPS16_LO16, ++ ++/* Relocation against a MIPS literal section. */ ++ BFD_RELOC_MIPS_LITERAL, ++ ++/* MIPS ELF relocations. */ ++ BFD_RELOC_MIPS_GOT16, ++ BFD_RELOC_MIPS_CALL16, ++ BFD_RELOC_MIPS_GOT_HI16, ++ BFD_RELOC_MIPS_GOT_LO16, ++ BFD_RELOC_MIPS_CALL_HI16, ++ BFD_RELOC_MIPS_CALL_LO16, ++ BFD_RELOC_MIPS_SUB, ++ BFD_RELOC_MIPS_GOT_PAGE, ++ BFD_RELOC_MIPS_GOT_OFST, ++ BFD_RELOC_MIPS_GOT_DISP, ++ BFD_RELOC_MIPS_SHIFT5, ++ BFD_RELOC_MIPS_SHIFT6, ++ BFD_RELOC_MIPS_INSERT_A, ++ BFD_RELOC_MIPS_INSERT_B, ++ BFD_RELOC_MIPS_DELETE, ++ BFD_RELOC_MIPS_HIGHEST, ++ BFD_RELOC_MIPS_HIGHER, ++ BFD_RELOC_MIPS_SCN_DISP, ++ BFD_RELOC_MIPS_REL16, ++ BFD_RELOC_MIPS_RELGOT, ++ BFD_RELOC_MIPS_JALR, ++ BFD_RELOC_MIPS_TLS_DTPMOD32, ++ BFD_RELOC_MIPS_TLS_DTPREL32, ++ BFD_RELOC_MIPS_TLS_DTPMOD64, ++ BFD_RELOC_MIPS_TLS_DTPREL64, ++ BFD_RELOC_MIPS_TLS_GD, ++ BFD_RELOC_MIPS_TLS_LDM, ++ BFD_RELOC_MIPS_TLS_DTPREL_HI16, ++ BFD_RELOC_MIPS_TLS_DTPREL_LO16, ++ BFD_RELOC_MIPS_TLS_GOTTPREL, ++ BFD_RELOC_MIPS_TLS_TPREL32, ++ BFD_RELOC_MIPS_TLS_TPREL64, ++ BFD_RELOC_MIPS_TLS_TPREL_HI16, ++ BFD_RELOC_MIPS_TLS_TPREL_LO16, ++ ++ ++/* Fujitsu Frv Relocations. */ ++ BFD_RELOC_FRV_LABEL16, ++ BFD_RELOC_FRV_LABEL24, ++ BFD_RELOC_FRV_LO16, ++ BFD_RELOC_FRV_HI16, ++ BFD_RELOC_FRV_GPREL12, ++ BFD_RELOC_FRV_GPRELU12, ++ BFD_RELOC_FRV_GPREL32, ++ BFD_RELOC_FRV_GPRELHI, ++ BFD_RELOC_FRV_GPRELLO, ++ BFD_RELOC_FRV_GOT12, ++ BFD_RELOC_FRV_GOTHI, ++ BFD_RELOC_FRV_GOTLO, ++ BFD_RELOC_FRV_FUNCDESC, ++ BFD_RELOC_FRV_FUNCDESC_GOT12, ++ BFD_RELOC_FRV_FUNCDESC_GOTHI, ++ BFD_RELOC_FRV_FUNCDESC_GOTLO, ++ BFD_RELOC_FRV_FUNCDESC_VALUE, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFF12, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFFHI, ++ BFD_RELOC_FRV_FUNCDESC_GOTOFFLO, ++ BFD_RELOC_FRV_GOTOFF12, ++ BFD_RELOC_FRV_GOTOFFHI, ++ BFD_RELOC_FRV_GOTOFFLO, ++ BFD_RELOC_FRV_GETTLSOFF, ++ BFD_RELOC_FRV_TLSDESC_VALUE, ++ BFD_RELOC_FRV_GOTTLSDESC12, ++ BFD_RELOC_FRV_GOTTLSDESCHI, ++ BFD_RELOC_FRV_GOTTLSDESCLO, ++ BFD_RELOC_FRV_TLSMOFF12, ++ BFD_RELOC_FRV_TLSMOFFHI, ++ BFD_RELOC_FRV_TLSMOFFLO, ++ BFD_RELOC_FRV_GOTTLSOFF12, ++ BFD_RELOC_FRV_GOTTLSOFFHI, ++ BFD_RELOC_FRV_GOTTLSOFFLO, ++ BFD_RELOC_FRV_TLSOFF, ++ BFD_RELOC_FRV_TLSDESC_RELAX, ++ BFD_RELOC_FRV_GETTLSOFF_RELAX, ++ BFD_RELOC_FRV_TLSOFF_RELAX, ++ BFD_RELOC_FRV_TLSMOFF, ++ ++ ++/* This is a 24bit GOT-relative reloc for the mn10300. */ ++ BFD_RELOC_MN10300_GOTOFF24, ++ ++/* This is a 32bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT32, ++ ++/* This is a 24bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT24, ++ ++/* This is a 16bit GOT-relative reloc for the mn10300, offset by two bytes ++in the instruction. */ ++ BFD_RELOC_MN10300_GOT16, ++ ++/* Copy symbol at runtime. */ ++ BFD_RELOC_MN10300_COPY, ++ ++/* Create GOT entry. */ ++ BFD_RELOC_MN10300_GLOB_DAT, ++ ++/* Create PLT entry. */ ++ BFD_RELOC_MN10300_JMP_SLOT, ++ ++/* Adjust by program base. */ ++ BFD_RELOC_MN10300_RELATIVE, ++ ++ ++/* i386/elf relocations */ ++ BFD_RELOC_386_GOT32, ++ BFD_RELOC_386_PLT32, ++ BFD_RELOC_386_COPY, ++ BFD_RELOC_386_GLOB_DAT, ++ BFD_RELOC_386_JUMP_SLOT, ++ BFD_RELOC_386_RELATIVE, ++ BFD_RELOC_386_GOTOFF, ++ BFD_RELOC_386_GOTPC, ++ BFD_RELOC_386_TLS_TPOFF, ++ BFD_RELOC_386_TLS_IE, ++ BFD_RELOC_386_TLS_GOTIE, ++ BFD_RELOC_386_TLS_LE, ++ BFD_RELOC_386_TLS_GD, ++ BFD_RELOC_386_TLS_LDM, ++ BFD_RELOC_386_TLS_LDO_32, ++ BFD_RELOC_386_TLS_IE_32, ++ BFD_RELOC_386_TLS_LE_32, ++ BFD_RELOC_386_TLS_DTPMOD32, ++ BFD_RELOC_386_TLS_DTPOFF32, ++ BFD_RELOC_386_TLS_TPOFF32, ++ ++/* x86-64/elf relocations */ ++ BFD_RELOC_X86_64_GOT32, ++ BFD_RELOC_X86_64_PLT32, ++ BFD_RELOC_X86_64_COPY, ++ BFD_RELOC_X86_64_GLOB_DAT, ++ BFD_RELOC_X86_64_JUMP_SLOT, ++ BFD_RELOC_X86_64_RELATIVE, ++ BFD_RELOC_X86_64_GOTPCREL, ++ BFD_RELOC_X86_64_32S, ++ BFD_RELOC_X86_64_DTPMOD64, ++ BFD_RELOC_X86_64_DTPOFF64, ++ BFD_RELOC_X86_64_TPOFF64, ++ BFD_RELOC_X86_64_TLSGD, ++ BFD_RELOC_X86_64_TLSLD, ++ BFD_RELOC_X86_64_DTPOFF32, ++ BFD_RELOC_X86_64_GOTTPOFF, ++ BFD_RELOC_X86_64_TPOFF32, ++ BFD_RELOC_X86_64_GOTOFF64, ++ BFD_RELOC_X86_64_GOTPC32, ++ ++/* ns32k relocations */ ++ BFD_RELOC_NS32K_IMM_8, ++ BFD_RELOC_NS32K_IMM_16, ++ BFD_RELOC_NS32K_IMM_32, ++ BFD_RELOC_NS32K_IMM_8_PCREL, ++ BFD_RELOC_NS32K_IMM_16_PCREL, ++ BFD_RELOC_NS32K_IMM_32_PCREL, ++ BFD_RELOC_NS32K_DISP_8, ++ BFD_RELOC_NS32K_DISP_16, ++ BFD_RELOC_NS32K_DISP_32, ++ BFD_RELOC_NS32K_DISP_8_PCREL, ++ BFD_RELOC_NS32K_DISP_16_PCREL, ++ BFD_RELOC_NS32K_DISP_32_PCREL, ++ ++/* PDP11 relocations */ ++ BFD_RELOC_PDP11_DISP_8_PCREL, ++ BFD_RELOC_PDP11_DISP_6_PCREL, ++ ++/* Picojava relocs. Not all of these appear in object files. */ ++ BFD_RELOC_PJ_CODE_HI16, ++ BFD_RELOC_PJ_CODE_LO16, ++ BFD_RELOC_PJ_CODE_DIR16, ++ BFD_RELOC_PJ_CODE_DIR32, ++ BFD_RELOC_PJ_CODE_REL16, ++ BFD_RELOC_PJ_CODE_REL32, ++ ++/* Power(rs6000) and PowerPC relocations. */ ++ BFD_RELOC_PPC_B26, ++ BFD_RELOC_PPC_BA26, ++ BFD_RELOC_PPC_TOC16, ++ BFD_RELOC_PPC_B16, ++ BFD_RELOC_PPC_B16_BRTAKEN, ++ BFD_RELOC_PPC_B16_BRNTAKEN, ++ BFD_RELOC_PPC_BA16, ++ BFD_RELOC_PPC_BA16_BRTAKEN, ++ BFD_RELOC_PPC_BA16_BRNTAKEN, ++ BFD_RELOC_PPC_COPY, ++ BFD_RELOC_PPC_GLOB_DAT, ++ BFD_RELOC_PPC_JMP_SLOT, ++ BFD_RELOC_PPC_RELATIVE, ++ BFD_RELOC_PPC_LOCAL24PC, ++ BFD_RELOC_PPC_EMB_NADDR32, ++ BFD_RELOC_PPC_EMB_NADDR16, ++ BFD_RELOC_PPC_EMB_NADDR16_LO, ++ BFD_RELOC_PPC_EMB_NADDR16_HI, ++ BFD_RELOC_PPC_EMB_NADDR16_HA, ++ BFD_RELOC_PPC_EMB_SDAI16, ++ BFD_RELOC_PPC_EMB_SDA2I16, ++ BFD_RELOC_PPC_EMB_SDA2REL, ++ BFD_RELOC_PPC_EMB_SDA21, ++ BFD_RELOC_PPC_EMB_MRKREF, ++ BFD_RELOC_PPC_EMB_RELSEC16, ++ BFD_RELOC_PPC_EMB_RELST_LO, ++ BFD_RELOC_PPC_EMB_RELST_HI, ++ BFD_RELOC_PPC_EMB_RELST_HA, ++ BFD_RELOC_PPC_EMB_BIT_FLD, ++ BFD_RELOC_PPC_EMB_RELSDA, ++ BFD_RELOC_PPC64_HIGHER, ++ BFD_RELOC_PPC64_HIGHER_S, ++ BFD_RELOC_PPC64_HIGHEST, ++ BFD_RELOC_PPC64_HIGHEST_S, ++ BFD_RELOC_PPC64_TOC16_LO, ++ BFD_RELOC_PPC64_TOC16_HI, ++ BFD_RELOC_PPC64_TOC16_HA, ++ BFD_RELOC_PPC64_TOC, ++ BFD_RELOC_PPC64_PLTGOT16, ++ BFD_RELOC_PPC64_PLTGOT16_LO, ++ BFD_RELOC_PPC64_PLTGOT16_HI, ++ BFD_RELOC_PPC64_PLTGOT16_HA, ++ BFD_RELOC_PPC64_ADDR16_DS, ++ BFD_RELOC_PPC64_ADDR16_LO_DS, ++ BFD_RELOC_PPC64_GOT16_DS, ++ BFD_RELOC_PPC64_GOT16_LO_DS, ++ BFD_RELOC_PPC64_PLT16_LO_DS, ++ BFD_RELOC_PPC64_SECTOFF_DS, ++ BFD_RELOC_PPC64_SECTOFF_LO_DS, ++ BFD_RELOC_PPC64_TOC16_DS, ++ BFD_RELOC_PPC64_TOC16_LO_DS, ++ BFD_RELOC_PPC64_PLTGOT16_DS, ++ BFD_RELOC_PPC64_PLTGOT16_LO_DS, ++ ++/* PowerPC and PowerPC64 thread-local storage relocations. */ ++ BFD_RELOC_PPC_TLS, ++ BFD_RELOC_PPC_DTPMOD, ++ BFD_RELOC_PPC_TPREL16, ++ BFD_RELOC_PPC_TPREL16_LO, ++ BFD_RELOC_PPC_TPREL16_HI, ++ BFD_RELOC_PPC_TPREL16_HA, ++ BFD_RELOC_PPC_TPREL, ++ BFD_RELOC_PPC_DTPREL16, ++ BFD_RELOC_PPC_DTPREL16_LO, ++ BFD_RELOC_PPC_DTPREL16_HI, ++ BFD_RELOC_PPC_DTPREL16_HA, ++ BFD_RELOC_PPC_DTPREL, ++ BFD_RELOC_PPC_GOT_TLSGD16, ++ BFD_RELOC_PPC_GOT_TLSGD16_LO, ++ BFD_RELOC_PPC_GOT_TLSGD16_HI, ++ BFD_RELOC_PPC_GOT_TLSGD16_HA, ++ BFD_RELOC_PPC_GOT_TLSLD16, ++ BFD_RELOC_PPC_GOT_TLSLD16_LO, ++ BFD_RELOC_PPC_GOT_TLSLD16_HI, ++ BFD_RELOC_PPC_GOT_TLSLD16_HA, ++ BFD_RELOC_PPC_GOT_TPREL16, ++ BFD_RELOC_PPC_GOT_TPREL16_LO, ++ BFD_RELOC_PPC_GOT_TPREL16_HI, ++ BFD_RELOC_PPC_GOT_TPREL16_HA, ++ BFD_RELOC_PPC_GOT_DTPREL16, ++ BFD_RELOC_PPC_GOT_DTPREL16_LO, ++ BFD_RELOC_PPC_GOT_DTPREL16_HI, ++ BFD_RELOC_PPC_GOT_DTPREL16_HA, ++ BFD_RELOC_PPC64_TPREL16_DS, ++ BFD_RELOC_PPC64_TPREL16_LO_DS, ++ BFD_RELOC_PPC64_TPREL16_HIGHER, ++ BFD_RELOC_PPC64_TPREL16_HIGHERA, ++ BFD_RELOC_PPC64_TPREL16_HIGHEST, ++ BFD_RELOC_PPC64_TPREL16_HIGHESTA, ++ BFD_RELOC_PPC64_DTPREL16_DS, ++ BFD_RELOC_PPC64_DTPREL16_LO_DS, ++ BFD_RELOC_PPC64_DTPREL16_HIGHER, ++ BFD_RELOC_PPC64_DTPREL16_HIGHERA, ++ BFD_RELOC_PPC64_DTPREL16_HIGHEST, ++ BFD_RELOC_PPC64_DTPREL16_HIGHESTA, ++ ++/* IBM 370/390 relocations */ ++ BFD_RELOC_I370_D12, ++ ++/* The type of reloc used to build a constructor table - at the moment ++probably a 32 bit wide absolute relocation, but the target can choose. ++It generally does map to one of the other relocation types. */ ++ BFD_RELOC_CTOR, ++ ++/* ARM 26 bit pc-relative branch. The lowest two bits must be zero and are ++not stored in the instruction. */ ++ BFD_RELOC_ARM_PCREL_BRANCH, ++ ++/* ARM 26 bit pc-relative branch. The lowest bit must be zero and is ++not stored in the instruction. The 2nd lowest bit comes from a 1 bit ++field in the instruction. */ ++ BFD_RELOC_ARM_PCREL_BLX, ++ ++/* Thumb 22 bit pc-relative branch. The lowest bit must be zero and is ++not stored in the instruction. The 2nd lowest bit comes from a 1 bit ++field in the instruction. */ ++ BFD_RELOC_THUMB_PCREL_BLX, ++ ++/* Thumb 7-, 9-, 12-, 20-, 23-, and 25-bit pc-relative branches. ++The lowest bit must be zero and is not stored in the instruction. ++Note that the corresponding ELF R_ARM_THM_JUMPnn constant has an ++"nn" one smaller in all cases. Note further that BRANCH23 ++corresponds to R_ARM_THM_CALL. */ ++ BFD_RELOC_THUMB_PCREL_BRANCH7, ++ BFD_RELOC_THUMB_PCREL_BRANCH9, ++ BFD_RELOC_THUMB_PCREL_BRANCH12, ++ BFD_RELOC_THUMB_PCREL_BRANCH20, ++ BFD_RELOC_THUMB_PCREL_BRANCH23, ++ BFD_RELOC_THUMB_PCREL_BRANCH25, ++ ++/* 12-bit immediate offset, used in ARM-format ldr and str instructions. */ ++ BFD_RELOC_ARM_OFFSET_IMM, ++ ++/* 5-bit immediate offset, used in Thumb-format ldr and str instructions. */ ++ BFD_RELOC_ARM_THUMB_OFFSET, ++ ++/* Pc-relative or absolute relocation depending on target. Used for ++entries in .init_array sections. */ ++ BFD_RELOC_ARM_TARGET1, ++ ++/* Read-only segment base relative address. */ ++ BFD_RELOC_ARM_ROSEGREL32, ++ ++/* Data segment base relative address. */ ++ BFD_RELOC_ARM_SBREL32, ++ ++/* This reloc is used for references to RTTI data from exception handling ++tables. The actual definition depends on the target. It may be a ++pc-relative or some form of GOT-indirect relocation. */ ++ BFD_RELOC_ARM_TARGET2, ++ ++/* 31-bit PC relative address. */ ++ BFD_RELOC_ARM_PREL31, ++ ++/* Relocations for setting up GOTs and PLTs for shared libraries. */ ++ BFD_RELOC_ARM_JUMP_SLOT, ++ BFD_RELOC_ARM_GLOB_DAT, ++ BFD_RELOC_ARM_GOT32, ++ BFD_RELOC_ARM_PLT32, ++ BFD_RELOC_ARM_RELATIVE, ++ BFD_RELOC_ARM_GOTOFF, ++ BFD_RELOC_ARM_GOTPC, ++ ++/* ARM thread-local storage relocations. */ ++ BFD_RELOC_ARM_TLS_GD32, ++ BFD_RELOC_ARM_TLS_LDO32, ++ BFD_RELOC_ARM_TLS_LDM32, ++ BFD_RELOC_ARM_TLS_DTPOFF32, ++ BFD_RELOC_ARM_TLS_DTPMOD32, ++ BFD_RELOC_ARM_TLS_TPOFF32, ++ BFD_RELOC_ARM_TLS_IE32, ++ BFD_RELOC_ARM_TLS_LE32, ++ ++/* These relocs are only used within the ARM assembler. They are not ++(at present) written to any object files. */ ++ BFD_RELOC_ARM_IMMEDIATE, ++ BFD_RELOC_ARM_ADRL_IMMEDIATE, ++ BFD_RELOC_ARM_T32_IMMEDIATE, ++ BFD_RELOC_ARM_SHIFT_IMM, ++ BFD_RELOC_ARM_SMI, ++ BFD_RELOC_ARM_SWI, ++ BFD_RELOC_ARM_MULTI, ++ BFD_RELOC_ARM_CP_OFF_IMM, ++ BFD_RELOC_ARM_CP_OFF_IMM_S2, ++ BFD_RELOC_ARM_ADR_IMM, ++ BFD_RELOC_ARM_LDR_IMM, ++ BFD_RELOC_ARM_LITERAL, ++ BFD_RELOC_ARM_IN_POOL, ++ BFD_RELOC_ARM_OFFSET_IMM8, ++ BFD_RELOC_ARM_T32_OFFSET_U8, ++ BFD_RELOC_ARM_T32_OFFSET_IMM, ++ BFD_RELOC_ARM_HWLITERAL, ++ BFD_RELOC_ARM_THUMB_ADD, ++ BFD_RELOC_ARM_THUMB_IMM, ++ BFD_RELOC_ARM_THUMB_SHIFT, ++ ++/* Renesas / SuperH SH relocs. Not all of these appear in object files. */ ++ BFD_RELOC_SH_PCDISP8BY2, ++ BFD_RELOC_SH_PCDISP12BY2, ++ BFD_RELOC_SH_IMM3, ++ BFD_RELOC_SH_IMM3U, ++ BFD_RELOC_SH_DISP12, ++ BFD_RELOC_SH_DISP12BY2, ++ BFD_RELOC_SH_DISP12BY4, ++ BFD_RELOC_SH_DISP12BY8, ++ BFD_RELOC_SH_DISP20, ++ BFD_RELOC_SH_DISP20BY8, ++ BFD_RELOC_SH_IMM4, ++ BFD_RELOC_SH_IMM4BY2, ++ BFD_RELOC_SH_IMM4BY4, ++ BFD_RELOC_SH_IMM8, ++ BFD_RELOC_SH_IMM8BY2, ++ BFD_RELOC_SH_IMM8BY4, ++ BFD_RELOC_SH_PCRELIMM8BY2, ++ BFD_RELOC_SH_PCRELIMM8BY4, ++ BFD_RELOC_SH_SWITCH16, ++ BFD_RELOC_SH_SWITCH32, ++ BFD_RELOC_SH_USES, ++ BFD_RELOC_SH_COUNT, ++ BFD_RELOC_SH_ALIGN, ++ BFD_RELOC_SH_CODE, ++ BFD_RELOC_SH_DATA, ++ BFD_RELOC_SH_LABEL, ++ BFD_RELOC_SH_LOOP_START, ++ BFD_RELOC_SH_LOOP_END, ++ BFD_RELOC_SH_COPY, ++ BFD_RELOC_SH_GLOB_DAT, ++ BFD_RELOC_SH_JMP_SLOT, ++ BFD_RELOC_SH_RELATIVE, ++ BFD_RELOC_SH_GOTPC, ++ BFD_RELOC_SH_GOT_LOW16, ++ BFD_RELOC_SH_GOT_MEDLOW16, ++ BFD_RELOC_SH_GOT_MEDHI16, ++ BFD_RELOC_SH_GOT_HI16, ++ BFD_RELOC_SH_GOTPLT_LOW16, ++ BFD_RELOC_SH_GOTPLT_MEDLOW16, ++ BFD_RELOC_SH_GOTPLT_MEDHI16, ++ BFD_RELOC_SH_GOTPLT_HI16, ++ BFD_RELOC_SH_PLT_LOW16, ++ BFD_RELOC_SH_PLT_MEDLOW16, ++ BFD_RELOC_SH_PLT_MEDHI16, ++ BFD_RELOC_SH_PLT_HI16, ++ BFD_RELOC_SH_GOTOFF_LOW16, ++ BFD_RELOC_SH_GOTOFF_MEDLOW16, ++ BFD_RELOC_SH_GOTOFF_MEDHI16, ++ BFD_RELOC_SH_GOTOFF_HI16, ++ BFD_RELOC_SH_GOTPC_LOW16, ++ BFD_RELOC_SH_GOTPC_MEDLOW16, ++ BFD_RELOC_SH_GOTPC_MEDHI16, ++ BFD_RELOC_SH_GOTPC_HI16, ++ BFD_RELOC_SH_COPY64, ++ BFD_RELOC_SH_GLOB_DAT64, ++ BFD_RELOC_SH_JMP_SLOT64, ++ BFD_RELOC_SH_RELATIVE64, ++ BFD_RELOC_SH_GOT10BY4, ++ BFD_RELOC_SH_GOT10BY8, ++ BFD_RELOC_SH_GOTPLT10BY4, ++ BFD_RELOC_SH_GOTPLT10BY8, ++ BFD_RELOC_SH_GOTPLT32, ++ BFD_RELOC_SH_SHMEDIA_CODE, ++ BFD_RELOC_SH_IMMU5, ++ BFD_RELOC_SH_IMMS6, ++ BFD_RELOC_SH_IMMS6BY32, ++ BFD_RELOC_SH_IMMU6, ++ BFD_RELOC_SH_IMMS10, ++ BFD_RELOC_SH_IMMS10BY2, ++ BFD_RELOC_SH_IMMS10BY4, ++ BFD_RELOC_SH_IMMS10BY8, ++ BFD_RELOC_SH_IMMS16, ++ BFD_RELOC_SH_IMMU16, ++ BFD_RELOC_SH_IMM_LOW16, ++ BFD_RELOC_SH_IMM_LOW16_PCREL, ++ BFD_RELOC_SH_IMM_MEDLOW16, ++ BFD_RELOC_SH_IMM_MEDLOW16_PCREL, ++ BFD_RELOC_SH_IMM_MEDHI16, ++ BFD_RELOC_SH_IMM_MEDHI16_PCREL, ++ BFD_RELOC_SH_IMM_HI16, ++ BFD_RELOC_SH_IMM_HI16_PCREL, ++ BFD_RELOC_SH_PT_16, ++ BFD_RELOC_SH_TLS_GD_32, ++ BFD_RELOC_SH_TLS_LD_32, ++ BFD_RELOC_SH_TLS_LDO_32, ++ BFD_RELOC_SH_TLS_IE_32, ++ BFD_RELOC_SH_TLS_LE_32, ++ BFD_RELOC_SH_TLS_DTPMOD32, ++ BFD_RELOC_SH_TLS_DTPOFF32, ++ BFD_RELOC_SH_TLS_TPOFF32, ++ ++/* ARC Cores relocs. ++ARC 22 bit pc-relative branch. The lowest two bits must be zero and are ++not stored in the instruction. The high 20 bits are installed in bits 26 ++through 7 of the instruction. */ ++ BFD_RELOC_ARC_B22_PCREL, ++ ++/* ARC 26 bit absolute branch. The lowest two bits must be zero and are not ++stored in the instruction. The high 24 bits are installed in bits 23 ++through 0. */ ++ BFD_RELOC_ARC_B26, ++ ++/* Mitsubishi D10V relocs. ++This is a 10-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_10_PCREL_R, ++ ++/* Mitsubishi D10V relocs. ++This is a 10-bit reloc with the right 2 bits ++assumed to be 0. This is the same as the previous reloc ++except it is in the left container, i.e., ++shifted left 15 bits. */ ++ BFD_RELOC_D10V_10_PCREL_L, ++ ++/* This is an 18-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_18, ++ ++/* This is an 18-bit reloc with the right 2 bits ++assumed to be 0. */ ++ BFD_RELOC_D10V_18_PCREL, ++ ++/* Mitsubishi D30V relocs. ++This is a 6-bit absolute reloc. */ ++ BFD_RELOC_D30V_6, ++ ++/* This is a 6-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_9_PCREL, ++ ++/* This is a 6-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_9_PCREL_R, ++ ++/* This is a 12-bit absolute reloc with the ++right 3 bitsassumed to be 0. */ ++ BFD_RELOC_D30V_15, ++ ++/* This is a 12-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_15_PCREL, ++ ++/* This is a 12-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_15_PCREL_R, ++ ++/* This is an 18-bit absolute reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_21, ++ ++/* This is an 18-bit pc-relative reloc with ++the right 3 bits assumed to be 0. */ ++ BFD_RELOC_D30V_21_PCREL, ++ ++/* This is an 18-bit pc-relative reloc with ++the right 3 bits assumed to be 0. Same ++as the previous reloc but on the right side ++of the container. */ ++ BFD_RELOC_D30V_21_PCREL_R, ++ ++/* This is a 32-bit absolute reloc. */ ++ BFD_RELOC_D30V_32, ++ ++/* This is a 32-bit pc-relative reloc. */ ++ BFD_RELOC_D30V_32_PCREL, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_HI16_S, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_LO16, ++ ++/* DLX relocs */ ++ BFD_RELOC_DLX_JMP26, ++ ++/* Renesas M16C/M32C Relocations. */ ++ BFD_RELOC_M16C_8_PCREL8, ++ BFD_RELOC_M16C_16_PCREL8, ++ BFD_RELOC_M16C_8_PCREL16, ++ BFD_RELOC_M16C_8_ELABEL24, ++ BFD_RELOC_M16C_8_ABS16, ++ BFD_RELOC_M16C_16_ABS16, ++ BFD_RELOC_M16C_16_ABS24, ++ BFD_RELOC_M16C_16_ABS32, ++ BFD_RELOC_M16C_24_ABS16, ++ BFD_RELOC_M16C_24_ABS24, ++ BFD_RELOC_M16C_24_ABS32, ++ BFD_RELOC_M16C_32_ABS16, ++ BFD_RELOC_M16C_32_ABS24, ++ BFD_RELOC_M16C_32_ABS32, ++ BFD_RELOC_M16C_40_ABS16, ++ BFD_RELOC_M16C_40_ABS24, ++ BFD_RELOC_M16C_40_ABS32, ++ ++/* Renesas M32R (formerly Mitsubishi M32R) relocs. ++This is a 24 bit absolute address. */ ++ BFD_RELOC_M32R_24, ++ ++/* This is a 10-bit pc-relative reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_10_PCREL, ++ ++/* This is an 18-bit reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_18_PCREL, ++ ++/* This is a 26-bit reloc with the right 2 bits assumed to be 0. */ ++ BFD_RELOC_M32R_26_PCREL, ++ ++/* This is a 16-bit reloc containing the high 16 bits of an address ++used when the lower 16 bits are treated as unsigned. */ ++ BFD_RELOC_M32R_HI16_ULO, ++ ++/* This is a 16-bit reloc containing the high 16 bits of an address ++used when the lower 16 bits are treated as signed. */ ++ BFD_RELOC_M32R_HI16_SLO, ++ ++/* This is a 16-bit reloc containing the lower 16 bits of an address. */ ++ BFD_RELOC_M32R_LO16, ++ ++/* This is a 16-bit reloc containing the small data area offset for use in ++add3, load, and store instructions. */ ++ BFD_RELOC_M32R_SDA16, ++ ++/* For PIC. */ ++ BFD_RELOC_M32R_GOT24, ++ BFD_RELOC_M32R_26_PLTREL, ++ BFD_RELOC_M32R_COPY, ++ BFD_RELOC_M32R_GLOB_DAT, ++ BFD_RELOC_M32R_JMP_SLOT, ++ BFD_RELOC_M32R_RELATIVE, ++ BFD_RELOC_M32R_GOTOFF, ++ BFD_RELOC_M32R_GOTOFF_HI_ULO, ++ BFD_RELOC_M32R_GOTOFF_HI_SLO, ++ BFD_RELOC_M32R_GOTOFF_LO, ++ BFD_RELOC_M32R_GOTPC24, ++ BFD_RELOC_M32R_GOT16_HI_ULO, ++ BFD_RELOC_M32R_GOT16_HI_SLO, ++ BFD_RELOC_M32R_GOT16_LO, ++ BFD_RELOC_M32R_GOTPC_HI_ULO, ++ BFD_RELOC_M32R_GOTPC_HI_SLO, ++ BFD_RELOC_M32R_GOTPC_LO, ++ ++/* This is a 9-bit reloc */ ++ BFD_RELOC_V850_9_PCREL, ++ ++/* This is a 22-bit reloc */ ++ BFD_RELOC_V850_22_PCREL, ++ ++/* This is a 16 bit offset from the short data area pointer. */ ++ BFD_RELOC_V850_SDA_16_16_OFFSET, ++ ++/* This is a 16 bit offset (of which only 15 bits are used) from the ++short data area pointer. */ ++ BFD_RELOC_V850_SDA_15_16_OFFSET, ++ ++/* This is a 16 bit offset from the zero data area pointer. */ ++ BFD_RELOC_V850_ZDA_16_16_OFFSET, ++ ++/* This is a 16 bit offset (of which only 15 bits are used) from the ++zero data area pointer. */ ++ BFD_RELOC_V850_ZDA_15_16_OFFSET, ++ ++/* This is an 8 bit offset (of which only 6 bits are used) from the ++tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_6_8_OFFSET, ++ ++/* This is an 8bit offset (of which only 7 bits are used) from the tiny ++data area pointer. */ ++ BFD_RELOC_V850_TDA_7_8_OFFSET, ++ ++/* This is a 7 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_7_7_OFFSET, ++ ++/* This is a 16 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_16_16_OFFSET, ++ ++/* This is a 5 bit offset (of which only 4 bits are used) from the tiny ++data area pointer. */ ++ BFD_RELOC_V850_TDA_4_5_OFFSET, ++ ++/* This is a 4 bit offset from the tiny data area pointer. */ ++ BFD_RELOC_V850_TDA_4_4_OFFSET, ++ ++/* This is a 16 bit offset from the short data area pointer, with the ++bits placed non-contiguously in the instruction. */ ++ BFD_RELOC_V850_SDA_16_16_SPLIT_OFFSET, ++ ++/* This is a 16 bit offset from the zero data area pointer, with the ++bits placed non-contiguously in the instruction. */ ++ BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET, ++ ++/* This is a 6 bit offset from the call table base pointer. */ ++ BFD_RELOC_V850_CALLT_6_7_OFFSET, ++ ++/* This is a 16 bit offset from the call table base pointer. */ ++ BFD_RELOC_V850_CALLT_16_16_OFFSET, ++ ++/* Used for relaxing indirect function calls. */ ++ BFD_RELOC_V850_LONGCALL, ++ ++/* Used for relaxing indirect jumps. */ ++ BFD_RELOC_V850_LONGJUMP, ++ ++/* Used to maintain alignment whilst relaxing. */ ++ BFD_RELOC_V850_ALIGN, ++ ++/* This is a variation of BFD_RELOC_LO16 that can be used in v850e ld.bu ++instructions. */ ++ BFD_RELOC_V850_LO16_SPLIT_OFFSET, ++ ++/* This is a 32bit pcrel reloc for the mn10300, offset by two bytes in the ++instruction. */ ++ BFD_RELOC_MN10300_32_PCREL, ++ ++/* This is a 16bit pcrel reloc for the mn10300, offset by two bytes in the ++instruction. */ ++ BFD_RELOC_MN10300_16_PCREL, ++ ++/* This is a 8bit DP reloc for the tms320c30, where the most ++significant 8 bits of a 24 bit word are placed into the least ++significant 8 bits of the opcode. */ ++ BFD_RELOC_TIC30_LDP, ++ ++/* This is a 7bit reloc for the tms320c54x, where the least ++significant 7 bits of a 16 bit word are placed into the least ++significant 7 bits of the opcode. */ ++ BFD_RELOC_TIC54X_PARTLS7, ++ ++/* This is a 9bit DP reloc for the tms320c54x, where the most ++significant 9 bits of a 16 bit word are placed into the least ++significant 9 bits of the opcode. */ ++ BFD_RELOC_TIC54X_PARTMS9, ++ ++/* This is an extended address 23-bit reloc for the tms320c54x. */ ++ BFD_RELOC_TIC54X_23, ++ ++/* This is a 16-bit reloc for the tms320c54x, where the least ++significant 16 bits of a 23-bit extended address are placed into ++the opcode. */ ++ BFD_RELOC_TIC54X_16_OF_23, ++ ++/* This is a reloc for the tms320c54x, where the most ++significant 7 bits of a 23-bit extended address are placed into ++the opcode. */ ++ BFD_RELOC_TIC54X_MS7_OF_23, ++ ++/* This is a 48 bit reloc for the FR30 that stores 32 bits. */ ++ BFD_RELOC_FR30_48, ++ ++/* This is a 32 bit reloc for the FR30 that stores 20 bits split up into ++two sections. */ ++ BFD_RELOC_FR30_20, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 6 bit word offset in ++4 bits. */ ++ BFD_RELOC_FR30_6_IN_4, ++ ++/* This is a 16 bit reloc for the FR30 that stores an 8 bit byte offset ++into 8 bits. */ ++ BFD_RELOC_FR30_8_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 9 bit short offset ++into 8 bits. */ ++ BFD_RELOC_FR30_9_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 10 bit word offset ++into 8 bits. */ ++ BFD_RELOC_FR30_10_IN_8, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 9 bit pc relative ++short offset into 8 bits. */ ++ BFD_RELOC_FR30_9_PCREL, ++ ++/* This is a 16 bit reloc for the FR30 that stores a 12 bit pc relative ++short offset into 11 bits. */ ++ BFD_RELOC_FR30_12_PCREL, ++ ++/* Motorola Mcore relocations. */ ++ BFD_RELOC_MCORE_PCREL_IMM8BY4, ++ BFD_RELOC_MCORE_PCREL_IMM11BY2, ++ BFD_RELOC_MCORE_PCREL_IMM4BY2, ++ BFD_RELOC_MCORE_PCREL_32, ++ BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2, ++ BFD_RELOC_MCORE_RVA, ++ ++/* These are relocations for the GETA instruction. */ ++ BFD_RELOC_MMIX_GETA, ++ BFD_RELOC_MMIX_GETA_1, ++ BFD_RELOC_MMIX_GETA_2, ++ BFD_RELOC_MMIX_GETA_3, ++ ++/* These are relocations for a conditional branch instruction. */ ++ BFD_RELOC_MMIX_CBRANCH, ++ BFD_RELOC_MMIX_CBRANCH_J, ++ BFD_RELOC_MMIX_CBRANCH_1, ++ BFD_RELOC_MMIX_CBRANCH_2, ++ BFD_RELOC_MMIX_CBRANCH_3, ++ ++/* These are relocations for the PUSHJ instruction. */ ++ BFD_RELOC_MMIX_PUSHJ, ++ BFD_RELOC_MMIX_PUSHJ_1, ++ BFD_RELOC_MMIX_PUSHJ_2, ++ BFD_RELOC_MMIX_PUSHJ_3, ++ BFD_RELOC_MMIX_PUSHJ_STUBBABLE, ++ ++/* These are relocations for the JMP instruction. */ ++ BFD_RELOC_MMIX_JMP, ++ BFD_RELOC_MMIX_JMP_1, ++ BFD_RELOC_MMIX_JMP_2, ++ BFD_RELOC_MMIX_JMP_3, ++ ++/* This is a relocation for a relative address as in a GETA instruction or ++a branch. */ ++ BFD_RELOC_MMIX_ADDR19, ++ ++/* This is a relocation for a relative address as in a JMP instruction. */ ++ BFD_RELOC_MMIX_ADDR27, ++ ++/* This is a relocation for an instruction field that may be a general ++register or a value 0..255. */ ++ BFD_RELOC_MMIX_REG_OR_BYTE, ++ ++/* This is a relocation for an instruction field that may be a general ++register. */ ++ BFD_RELOC_MMIX_REG, ++ ++/* This is a relocation for two instruction fields holding a register and ++an offset, the equivalent of the relocation. */ ++ BFD_RELOC_MMIX_BASE_PLUS_OFFSET, ++ ++/* This relocation is an assertion that the expression is not allocated as ++a global register. It does not modify contents. */ ++ BFD_RELOC_MMIX_LOCAL, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit pc relative ++short offset into 7 bits. */ ++ BFD_RELOC_AVR_7_PCREL, ++ ++/* This is a 16 bit reloc for the AVR that stores 13 bit pc relative ++short offset into 12 bits. */ ++ BFD_RELOC_AVR_13_PCREL, ++ ++/* This is a 16 bit reloc for the AVR that stores 17 bit value (usually ++program memory address) into 16 bits. */ ++ BFD_RELOC_AVR_16_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (usually ++data memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_LO8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit ++of data memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HI8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit ++of program memory address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HH8_LDI, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(usually data memory address) into 8 bit immediate value of SUBI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 8 bit of data memory address) into 8 bit immediate value of ++SUBI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(most high 8 bit of program memory address) into 8 bit immediate value ++of LDI or SUBI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (usually ++command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit ++of command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit ++of command address) into 8 bit immediate value of LDI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_PM, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(usually command address) into 8 bit immediate value of SUBI insn. */ ++ BFD_RELOC_AVR_LO8_LDI_PM_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 8 bit of 16 bit command address) into 8 bit immediate value ++of SUBI insn. */ ++ BFD_RELOC_AVR_HI8_LDI_PM_NEG, ++ ++/* This is a 16 bit reloc for the AVR that stores negated 8 bit value ++(high 6 bit of 22 bit command address) into 8 bit immediate ++value of SUBI insn. */ ++ BFD_RELOC_AVR_HH8_LDI_PM_NEG, ++ ++/* This is a 32 bit reloc for the AVR that stores 23 bit value ++into 22 bits. */ ++ BFD_RELOC_AVR_CALL, ++ ++/* This is a 16 bit reloc for the AVR that stores all needed bits ++for absolute addressing with ldi with overflow check to linktime */ ++ BFD_RELOC_AVR_LDI, ++ ++/* This is a 6 bit reloc for the AVR that stores offset for ldd/std ++instructions */ ++ BFD_RELOC_AVR_6, ++ ++/* This is a 6 bit reloc for the AVR that stores offset for adiw/sbiw ++instructions */ ++ BFD_RELOC_AVR_6_ADIW, ++ ++/* Direct 12 bit. */ ++ BFD_RELOC_390_12, ++ ++/* 12 bit GOT offset. */ ++ BFD_RELOC_390_GOT12, ++ ++/* 32 bit PC relative PLT address. */ ++ BFD_RELOC_390_PLT32, ++ ++/* Copy symbol at runtime. */ ++ BFD_RELOC_390_COPY, ++ ++/* Create GOT entry. */ ++ BFD_RELOC_390_GLOB_DAT, ++ ++/* Create PLT entry. */ ++ BFD_RELOC_390_JMP_SLOT, ++ ++/* Adjust by program base. */ ++ BFD_RELOC_390_RELATIVE, ++ ++/* 32 bit PC relative offset to GOT. */ ++ BFD_RELOC_390_GOTPC, ++ ++/* 16 bit GOT offset. */ ++ BFD_RELOC_390_GOT16, ++ ++/* PC relative 16 bit shifted by 1. */ ++ BFD_RELOC_390_PC16DBL, ++ ++/* 16 bit PC rel. PLT shifted by 1. */ ++ BFD_RELOC_390_PLT16DBL, ++ ++/* PC relative 32 bit shifted by 1. */ ++ BFD_RELOC_390_PC32DBL, ++ ++/* 32 bit PC rel. PLT shifted by 1. */ ++ BFD_RELOC_390_PLT32DBL, ++ ++/* 32 bit PC rel. GOT shifted by 1. */ ++ BFD_RELOC_390_GOTPCDBL, ++ ++/* 64 bit GOT offset. */ ++ BFD_RELOC_390_GOT64, ++ ++/* 64 bit PC relative PLT address. */ ++ BFD_RELOC_390_PLT64, ++ ++/* 32 bit rel. offset to GOT entry. */ ++ BFD_RELOC_390_GOTENT, ++ ++/* 64 bit offset to GOT. */ ++ BFD_RELOC_390_GOTOFF64, ++ ++/* 12-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT12, ++ ++/* 16-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT16, ++ ++/* 32-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT32, ++ ++/* 64-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLT64, ++ ++/* 32-bit rel. offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_390_GOTPLTENT, ++ ++/* 16-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF16, ++ ++/* 32-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF32, ++ ++/* 64-bit rel. offset from the GOT to a PLT entry. */ ++ BFD_RELOC_390_PLTOFF64, ++ ++/* s390 tls relocations. */ ++ BFD_RELOC_390_TLS_LOAD, ++ BFD_RELOC_390_TLS_GDCALL, ++ BFD_RELOC_390_TLS_LDCALL, ++ BFD_RELOC_390_TLS_GD32, ++ BFD_RELOC_390_TLS_GD64, ++ BFD_RELOC_390_TLS_GOTIE12, ++ BFD_RELOC_390_TLS_GOTIE32, ++ BFD_RELOC_390_TLS_GOTIE64, ++ BFD_RELOC_390_TLS_LDM32, ++ BFD_RELOC_390_TLS_LDM64, ++ BFD_RELOC_390_TLS_IE32, ++ BFD_RELOC_390_TLS_IE64, ++ BFD_RELOC_390_TLS_IEENT, ++ BFD_RELOC_390_TLS_LE32, ++ BFD_RELOC_390_TLS_LE64, ++ BFD_RELOC_390_TLS_LDO32, ++ BFD_RELOC_390_TLS_LDO64, ++ BFD_RELOC_390_TLS_DTPMOD, ++ BFD_RELOC_390_TLS_DTPOFF, ++ BFD_RELOC_390_TLS_TPOFF, ++ ++/* Long displacement extension. */ ++ BFD_RELOC_390_20, ++ BFD_RELOC_390_GOT20, ++ BFD_RELOC_390_GOTPLT20, ++ BFD_RELOC_390_TLS_GOTIE20, ++ ++/* Scenix IP2K - 9-bit register number / data address */ ++ BFD_RELOC_IP2K_FR9, ++ ++/* Scenix IP2K - 4-bit register/data bank number */ ++ BFD_RELOC_IP2K_BANK, ++ ++/* Scenix IP2K - low 13 bits of instruction word address */ ++ BFD_RELOC_IP2K_ADDR16CJP, ++ ++/* Scenix IP2K - high 3 bits of instruction word address */ ++ BFD_RELOC_IP2K_PAGE3, ++ ++/* Scenix IP2K - ext/low/high 8 bits of data address */ ++ BFD_RELOC_IP2K_LO8DATA, ++ BFD_RELOC_IP2K_HI8DATA, ++ BFD_RELOC_IP2K_EX8DATA, ++ ++/* Scenix IP2K - low/high 8 bits of instruction word address */ ++ BFD_RELOC_IP2K_LO8INSN, ++ BFD_RELOC_IP2K_HI8INSN, ++ ++/* Scenix IP2K - even/odd PC modifier to modify snb pcl.0 */ ++ BFD_RELOC_IP2K_PC_SKIP, ++ ++/* Scenix IP2K - 16 bit word address in text section. */ ++ BFD_RELOC_IP2K_TEXT, ++ ++/* Scenix IP2K - 7-bit sp or dp offset */ ++ BFD_RELOC_IP2K_FR_OFFSET, ++ ++/* Scenix VPE4K coprocessor - data/insn-space addressing */ ++ BFD_RELOC_VPE4KMATH_DATA, ++ BFD_RELOC_VPE4KMATH_INSN, ++ ++/* These two relocations are used by the linker to determine which of ++the entries in a C++ virtual function table are actually used. When ++the --gc-sections option is given, the linker will zero out the entries ++that are not used, so that the code for those functions need not be ++included in the output. ++ ++VTABLE_INHERIT is a zero-space relocation used to describe to the ++linker the inheritance tree of a C++ virtual function table. The ++relocation's symbol should be the parent class' vtable, and the ++relocation should be located at the child vtable. ++ ++VTABLE_ENTRY is a zero-space relocation that describes the use of a ++virtual function table entry. The reloc's symbol should refer to the ++table of the class mentioned in the code. Off of that base, an offset ++describes the entry that is being used. For Rela hosts, this offset ++is stored in the reloc's addend. For Rel hosts, we are forced to put ++this offset in the reloc's section offset. */ ++ BFD_RELOC_VTABLE_INHERIT, ++ BFD_RELOC_VTABLE_ENTRY, ++ ++/* Intel IA64 Relocations. */ ++ BFD_RELOC_IA64_IMM14, ++ BFD_RELOC_IA64_IMM22, ++ BFD_RELOC_IA64_IMM64, ++ BFD_RELOC_IA64_DIR32MSB, ++ BFD_RELOC_IA64_DIR32LSB, ++ BFD_RELOC_IA64_DIR64MSB, ++ BFD_RELOC_IA64_DIR64LSB, ++ BFD_RELOC_IA64_GPREL22, ++ BFD_RELOC_IA64_GPREL64I, ++ BFD_RELOC_IA64_GPREL32MSB, ++ BFD_RELOC_IA64_GPREL32LSB, ++ BFD_RELOC_IA64_GPREL64MSB, ++ BFD_RELOC_IA64_GPREL64LSB, ++ BFD_RELOC_IA64_LTOFF22, ++ BFD_RELOC_IA64_LTOFF64I, ++ BFD_RELOC_IA64_PLTOFF22, ++ BFD_RELOC_IA64_PLTOFF64I, ++ BFD_RELOC_IA64_PLTOFF64MSB, ++ BFD_RELOC_IA64_PLTOFF64LSB, ++ BFD_RELOC_IA64_FPTR64I, ++ BFD_RELOC_IA64_FPTR32MSB, ++ BFD_RELOC_IA64_FPTR32LSB, ++ BFD_RELOC_IA64_FPTR64MSB, ++ BFD_RELOC_IA64_FPTR64LSB, ++ BFD_RELOC_IA64_PCREL21B, ++ BFD_RELOC_IA64_PCREL21BI, ++ BFD_RELOC_IA64_PCREL21M, ++ BFD_RELOC_IA64_PCREL21F, ++ BFD_RELOC_IA64_PCREL22, ++ BFD_RELOC_IA64_PCREL60B, ++ BFD_RELOC_IA64_PCREL64I, ++ BFD_RELOC_IA64_PCREL32MSB, ++ BFD_RELOC_IA64_PCREL32LSB, ++ BFD_RELOC_IA64_PCREL64MSB, ++ BFD_RELOC_IA64_PCREL64LSB, ++ BFD_RELOC_IA64_LTOFF_FPTR22, ++ BFD_RELOC_IA64_LTOFF_FPTR64I, ++ BFD_RELOC_IA64_LTOFF_FPTR32MSB, ++ BFD_RELOC_IA64_LTOFF_FPTR32LSB, ++ BFD_RELOC_IA64_LTOFF_FPTR64MSB, ++ BFD_RELOC_IA64_LTOFF_FPTR64LSB, ++ BFD_RELOC_IA64_SEGREL32MSB, ++ BFD_RELOC_IA64_SEGREL32LSB, ++ BFD_RELOC_IA64_SEGREL64MSB, ++ BFD_RELOC_IA64_SEGREL64LSB, ++ BFD_RELOC_IA64_SECREL32MSB, ++ BFD_RELOC_IA64_SECREL32LSB, ++ BFD_RELOC_IA64_SECREL64MSB, ++ BFD_RELOC_IA64_SECREL64LSB, ++ BFD_RELOC_IA64_REL32MSB, ++ BFD_RELOC_IA64_REL32LSB, ++ BFD_RELOC_IA64_REL64MSB, ++ BFD_RELOC_IA64_REL64LSB, ++ BFD_RELOC_IA64_LTV32MSB, ++ BFD_RELOC_IA64_LTV32LSB, ++ BFD_RELOC_IA64_LTV64MSB, ++ BFD_RELOC_IA64_LTV64LSB, ++ BFD_RELOC_IA64_IPLTMSB, ++ BFD_RELOC_IA64_IPLTLSB, ++ BFD_RELOC_IA64_COPY, ++ BFD_RELOC_IA64_LTOFF22X, ++ BFD_RELOC_IA64_LDXMOV, ++ BFD_RELOC_IA64_TPREL14, ++ BFD_RELOC_IA64_TPREL22, ++ BFD_RELOC_IA64_TPREL64I, ++ BFD_RELOC_IA64_TPREL64MSB, ++ BFD_RELOC_IA64_TPREL64LSB, ++ BFD_RELOC_IA64_LTOFF_TPREL22, ++ BFD_RELOC_IA64_DTPMOD64MSB, ++ BFD_RELOC_IA64_DTPMOD64LSB, ++ BFD_RELOC_IA64_LTOFF_DTPMOD22, ++ BFD_RELOC_IA64_DTPREL14, ++ BFD_RELOC_IA64_DTPREL22, ++ BFD_RELOC_IA64_DTPREL64I, ++ BFD_RELOC_IA64_DTPREL32MSB, ++ BFD_RELOC_IA64_DTPREL32LSB, ++ BFD_RELOC_IA64_DTPREL64MSB, ++ BFD_RELOC_IA64_DTPREL64LSB, ++ BFD_RELOC_IA64_LTOFF_DTPREL22, ++ ++/* Motorola 68HC11 reloc. ++This is the 8 bit high part of an absolute address. */ ++ BFD_RELOC_M68HC11_HI8, ++ ++/* Motorola 68HC11 reloc. ++This is the 8 bit low part of an absolute address. */ ++ BFD_RELOC_M68HC11_LO8, ++ ++/* Motorola 68HC11 reloc. ++This is the 3 bit of a value. */ ++ BFD_RELOC_M68HC11_3B, ++ ++/* Motorola 68HC11 reloc. ++This reloc marks the beginning of a jump/call instruction. ++It is used for linker relaxation to correctly identify beginning ++of instruction and change some branches to use PC-relative ++addressing mode. */ ++ BFD_RELOC_M68HC11_RL_JUMP, ++ ++/* Motorola 68HC11 reloc. ++This reloc marks a group of several instructions that gcc generates ++and for which the linker relaxation pass can modify and/or remove ++some of them. */ ++ BFD_RELOC_M68HC11_RL_GROUP, ++ ++/* Motorola 68HC11 reloc. ++This is the 16-bit lower part of an address. It is used for 'call' ++instruction to specify the symbol address without any special ++transformation (due to memory bank window). */ ++ BFD_RELOC_M68HC11_LO16, ++ ++/* Motorola 68HC11 reloc. ++This is a 8-bit reloc that specifies the page number of an address. ++It is used by 'call' instruction to specify the page number of ++the symbol. */ ++ BFD_RELOC_M68HC11_PAGE, ++ ++/* Motorola 68HC11 reloc. ++This is a 24-bit reloc that represents the address with a 16-bit ++value and a 8-bit page number. The symbol address is transformed ++to follow the 16K memory bank of 68HC12 (seen as mapped in the window). */ ++ BFD_RELOC_M68HC11_24, ++ ++/* Motorola 68HC12 reloc. ++This is the 5 bits of a value. */ ++ BFD_RELOC_M68HC12_5B, ++ ++/* NS CR16C Relocations. */ ++ BFD_RELOC_16C_NUM08, ++ BFD_RELOC_16C_NUM08_C, ++ BFD_RELOC_16C_NUM16, ++ BFD_RELOC_16C_NUM16_C, ++ BFD_RELOC_16C_NUM32, ++ BFD_RELOC_16C_NUM32_C, ++ BFD_RELOC_16C_DISP04, ++ BFD_RELOC_16C_DISP04_C, ++ BFD_RELOC_16C_DISP08, ++ BFD_RELOC_16C_DISP08_C, ++ BFD_RELOC_16C_DISP16, ++ BFD_RELOC_16C_DISP16_C, ++ BFD_RELOC_16C_DISP24, ++ BFD_RELOC_16C_DISP24_C, ++ BFD_RELOC_16C_DISP24a, ++ BFD_RELOC_16C_DISP24a_C, ++ BFD_RELOC_16C_REG04, ++ BFD_RELOC_16C_REG04_C, ++ BFD_RELOC_16C_REG04a, ++ BFD_RELOC_16C_REG04a_C, ++ BFD_RELOC_16C_REG14, ++ BFD_RELOC_16C_REG14_C, ++ BFD_RELOC_16C_REG16, ++ BFD_RELOC_16C_REG16_C, ++ BFD_RELOC_16C_REG20, ++ BFD_RELOC_16C_REG20_C, ++ BFD_RELOC_16C_ABS20, ++ BFD_RELOC_16C_ABS20_C, ++ BFD_RELOC_16C_ABS24, ++ BFD_RELOC_16C_ABS24_C, ++ BFD_RELOC_16C_IMM04, ++ BFD_RELOC_16C_IMM04_C, ++ BFD_RELOC_16C_IMM16, ++ BFD_RELOC_16C_IMM16_C, ++ BFD_RELOC_16C_IMM20, ++ BFD_RELOC_16C_IMM20_C, ++ BFD_RELOC_16C_IMM24, ++ BFD_RELOC_16C_IMM24_C, ++ BFD_RELOC_16C_IMM32, ++ BFD_RELOC_16C_IMM32_C, ++ ++/* NS CRX Relocations. */ ++ BFD_RELOC_CRX_REL4, ++ BFD_RELOC_CRX_REL8, ++ BFD_RELOC_CRX_REL8_CMP, ++ BFD_RELOC_CRX_REL16, ++ BFD_RELOC_CRX_REL24, ++ BFD_RELOC_CRX_REL32, ++ BFD_RELOC_CRX_REGREL12, ++ BFD_RELOC_CRX_REGREL22, ++ BFD_RELOC_CRX_REGREL28, ++ BFD_RELOC_CRX_REGREL32, ++ BFD_RELOC_CRX_ABS16, ++ BFD_RELOC_CRX_ABS32, ++ BFD_RELOC_CRX_NUM8, ++ BFD_RELOC_CRX_NUM16, ++ BFD_RELOC_CRX_NUM32, ++ BFD_RELOC_CRX_IMM16, ++ BFD_RELOC_CRX_IMM32, ++ BFD_RELOC_CRX_SWITCH8, ++ BFD_RELOC_CRX_SWITCH16, ++ BFD_RELOC_CRX_SWITCH32, ++ ++/* These relocs are only used within the CRIS assembler. They are not ++(at present) written to any object files. */ ++ BFD_RELOC_CRIS_BDISP8, ++ BFD_RELOC_CRIS_UNSIGNED_5, ++ BFD_RELOC_CRIS_SIGNED_6, ++ BFD_RELOC_CRIS_UNSIGNED_6, ++ BFD_RELOC_CRIS_SIGNED_8, ++ BFD_RELOC_CRIS_UNSIGNED_8, ++ BFD_RELOC_CRIS_SIGNED_16, ++ BFD_RELOC_CRIS_UNSIGNED_16, ++ BFD_RELOC_CRIS_LAPCQ_OFFSET, ++ BFD_RELOC_CRIS_UNSIGNED_4, ++ ++/* Relocs used in ELF shared libraries for CRIS. */ ++ BFD_RELOC_CRIS_COPY, ++ BFD_RELOC_CRIS_GLOB_DAT, ++ BFD_RELOC_CRIS_JUMP_SLOT, ++ BFD_RELOC_CRIS_RELATIVE, ++ ++/* 32-bit offset to symbol-entry within GOT. */ ++ BFD_RELOC_CRIS_32_GOT, ++ ++/* 16-bit offset to symbol-entry within GOT. */ ++ BFD_RELOC_CRIS_16_GOT, ++ ++/* 32-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_CRIS_32_GOTPLT, ++ ++/* 16-bit offset to symbol-entry within GOT, with PLT handling. */ ++ BFD_RELOC_CRIS_16_GOTPLT, ++ ++/* 32-bit offset to symbol, relative to GOT. */ ++ BFD_RELOC_CRIS_32_GOTREL, ++ ++/* 32-bit offset to symbol with PLT entry, relative to GOT. */ ++ BFD_RELOC_CRIS_32_PLT_GOTREL, ++ ++/* 32-bit offset to symbol with PLT entry, relative to this relocation. */ ++ BFD_RELOC_CRIS_32_PLT_PCREL, ++ ++/* Intel i860 Relocations. */ ++ BFD_RELOC_860_COPY, ++ BFD_RELOC_860_GLOB_DAT, ++ BFD_RELOC_860_JUMP_SLOT, ++ BFD_RELOC_860_RELATIVE, ++ BFD_RELOC_860_PC26, ++ BFD_RELOC_860_PLT26, ++ BFD_RELOC_860_PC16, ++ BFD_RELOC_860_LOW0, ++ BFD_RELOC_860_SPLIT0, ++ BFD_RELOC_860_LOW1, ++ BFD_RELOC_860_SPLIT1, ++ BFD_RELOC_860_LOW2, ++ BFD_RELOC_860_SPLIT2, ++ BFD_RELOC_860_LOW3, ++ BFD_RELOC_860_LOGOT0, ++ BFD_RELOC_860_SPGOT0, ++ BFD_RELOC_860_LOGOT1, ++ BFD_RELOC_860_SPGOT1, ++ BFD_RELOC_860_LOGOTOFF0, ++ BFD_RELOC_860_SPGOTOFF0, ++ BFD_RELOC_860_LOGOTOFF1, ++ BFD_RELOC_860_SPGOTOFF1, ++ BFD_RELOC_860_LOGOTOFF2, ++ BFD_RELOC_860_LOGOTOFF3, ++ BFD_RELOC_860_LOPC, ++ BFD_RELOC_860_HIGHADJ, ++ BFD_RELOC_860_HAGOT, ++ BFD_RELOC_860_HAGOTOFF, ++ BFD_RELOC_860_HAPC, ++ BFD_RELOC_860_HIGH, ++ BFD_RELOC_860_HIGOT, ++ BFD_RELOC_860_HIGOTOFF, ++ ++/* OpenRISC Relocations. */ ++ BFD_RELOC_OPENRISC_ABS_26, ++ BFD_RELOC_OPENRISC_REL_26, ++ ++/* H8 elf Relocations. */ ++ BFD_RELOC_H8_DIR16A8, ++ BFD_RELOC_H8_DIR16R8, ++ BFD_RELOC_H8_DIR24A8, ++ BFD_RELOC_H8_DIR24R8, ++ BFD_RELOC_H8_DIR32A16, ++ ++/* Sony Xstormy16 Relocations. */ ++ BFD_RELOC_XSTORMY16_REL_12, ++ BFD_RELOC_XSTORMY16_12, ++ BFD_RELOC_XSTORMY16_24, ++ BFD_RELOC_XSTORMY16_FPTR16, ++ ++/* Relocations used by VAX ELF. */ ++ BFD_RELOC_VAX_GLOB_DAT, ++ BFD_RELOC_VAX_JMP_SLOT, ++ BFD_RELOC_VAX_RELATIVE, ++ ++/* Morpho MS1 - 16 bit immediate relocation. */ ++ BFD_RELOC_MS1_PC16, ++ ++/* Morpho MS1 - Hi 16 bits of an address. */ ++ BFD_RELOC_MS1_HI16, ++ ++/* Morpho MS1 - Low 16 bits of an address. */ ++ BFD_RELOC_MS1_LO16, ++ ++/* Morpho MS1 - Used to tell the linker which vtable entries are used. */ ++ BFD_RELOC_MS1_GNU_VTINHERIT, ++ ++/* Morpho MS1 - Used to tell the linker which vtable entries are used. */ ++ BFD_RELOC_MS1_GNU_VTENTRY, ++ ++/* msp430 specific relocation codes */ ++ BFD_RELOC_MSP430_10_PCREL, ++ BFD_RELOC_MSP430_16_PCREL, ++ BFD_RELOC_MSP430_16, ++ BFD_RELOC_MSP430_16_PCREL_BYTE, ++ BFD_RELOC_MSP430_16_BYTE, ++ BFD_RELOC_MSP430_2X_PCREL, ++ BFD_RELOC_MSP430_RL_PCREL, ++ ++/* IQ2000 Relocations. */ ++ BFD_RELOC_IQ2000_OFFSET_16, ++ BFD_RELOC_IQ2000_OFFSET_21, ++ BFD_RELOC_IQ2000_UHI16, ++ ++/* Special Xtensa relocation used only by PLT entries in ELF shared ++objects to indicate that the runtime linker should set the value ++to one of its own internal functions or data structures. */ ++ BFD_RELOC_XTENSA_RTLD, ++ ++/* Xtensa relocations for ELF shared objects. */ ++ BFD_RELOC_XTENSA_GLOB_DAT, ++ BFD_RELOC_XTENSA_JMP_SLOT, ++ BFD_RELOC_XTENSA_RELATIVE, ++ ++/* Xtensa relocation used in ELF object files for symbols that may require ++PLT entries. Otherwise, this is just a generic 32-bit relocation. */ ++ BFD_RELOC_XTENSA_PLT, ++ ++/* Xtensa relocations to mark the difference of two local symbols. ++These are only needed to support linker relaxation and can be ignored ++when not relaxing. The field is set to the value of the difference ++assuming no relaxation. The relocation encodes the position of the ++first symbol so the linker can determine whether to adjust the field ++value. */ ++ BFD_RELOC_XTENSA_DIFF8, ++ BFD_RELOC_XTENSA_DIFF16, ++ BFD_RELOC_XTENSA_DIFF32, ++ ++/* Generic Xtensa relocations for instruction operands. Only the slot ++number is encoded in the relocation. The relocation applies to the ++last PC-relative immediate operand, or if there are no PC-relative ++immediates, to the last immediate operand. */ ++ BFD_RELOC_XTENSA_SLOT0_OP, ++ BFD_RELOC_XTENSA_SLOT1_OP, ++ BFD_RELOC_XTENSA_SLOT2_OP, ++ BFD_RELOC_XTENSA_SLOT3_OP, ++ BFD_RELOC_XTENSA_SLOT4_OP, ++ BFD_RELOC_XTENSA_SLOT5_OP, ++ BFD_RELOC_XTENSA_SLOT6_OP, ++ BFD_RELOC_XTENSA_SLOT7_OP, ++ BFD_RELOC_XTENSA_SLOT8_OP, ++ BFD_RELOC_XTENSA_SLOT9_OP, ++ BFD_RELOC_XTENSA_SLOT10_OP, ++ BFD_RELOC_XTENSA_SLOT11_OP, ++ BFD_RELOC_XTENSA_SLOT12_OP, ++ BFD_RELOC_XTENSA_SLOT13_OP, ++ BFD_RELOC_XTENSA_SLOT14_OP, ++ ++/* Alternate Xtensa relocations. Only the slot is encoded in the ++relocation. The meaning of these relocations is opcode-specific. */ ++ BFD_RELOC_XTENSA_SLOT0_ALT, ++ BFD_RELOC_XTENSA_SLOT1_ALT, ++ BFD_RELOC_XTENSA_SLOT2_ALT, ++ BFD_RELOC_XTENSA_SLOT3_ALT, ++ BFD_RELOC_XTENSA_SLOT4_ALT, ++ BFD_RELOC_XTENSA_SLOT5_ALT, ++ BFD_RELOC_XTENSA_SLOT6_ALT, ++ BFD_RELOC_XTENSA_SLOT7_ALT, ++ BFD_RELOC_XTENSA_SLOT8_ALT, ++ BFD_RELOC_XTENSA_SLOT9_ALT, ++ BFD_RELOC_XTENSA_SLOT10_ALT, ++ BFD_RELOC_XTENSA_SLOT11_ALT, ++ BFD_RELOC_XTENSA_SLOT12_ALT, ++ BFD_RELOC_XTENSA_SLOT13_ALT, ++ BFD_RELOC_XTENSA_SLOT14_ALT, ++ ++/* Xtensa relocations for backward compatibility. These have all been ++replaced by BFD_RELOC_XTENSA_SLOT0_OP. */ ++ BFD_RELOC_XTENSA_OP0, ++ BFD_RELOC_XTENSA_OP1, ++ BFD_RELOC_XTENSA_OP2, ++ ++/* Xtensa relocation to mark that the assembler expanded the ++instructions from an original target. The expansion size is ++encoded in the reloc size. */ ++ BFD_RELOC_XTENSA_ASM_EXPAND, ++ ++/* Xtensa relocation to mark that the linker should simplify ++assembler-expanded instructions. This is commonly used ++internally by the linker after analysis of a ++BFD_RELOC_XTENSA_ASM_EXPAND. */ ++ BFD_RELOC_XTENSA_ASM_SIMPLIFY, ++ BFD_RELOC_UNUSED }; ++typedef enum bfd_reloc_code_real bfd_reloc_code_real_type; ++reloc_howto_type *bfd_reloc_type_lookup ++ (bfd *abfd, bfd_reloc_code_real_type code); ++ ++const char *bfd_get_reloc_code_name (bfd_reloc_code_real_type code); ++ ++/* Extracted from syms.c. */ ++ ++typedef struct bfd_symbol ++{ ++ /* A pointer to the BFD which owns the symbol. This information ++ is necessary so that a back end can work out what additional ++ information (invisible to the application writer) is carried ++ with the symbol. ++ ++ This field is *almost* redundant, since you can use section->owner ++ instead, except that some symbols point to the global sections ++ bfd_{abs,com,und}_section. This could be fixed by making ++ these globals be per-bfd (or per-target-flavor). FIXME. */ ++ struct bfd *the_bfd; /* Use bfd_asymbol_bfd(sym) to access this field. */ ++ ++ /* The text of the symbol. The name is left alone, and not copied; the ++ application may not alter it. */ ++ const char *name; ++ ++ /* The value of the symbol. This really should be a union of a ++ numeric value with a pointer, since some flags indicate that ++ a pointer to another symbol is stored here. */ ++ symvalue value; ++ ++ /* Attributes of a symbol. */ ++#define BSF_NO_FLAGS 0x00 ++ ++ /* The symbol has local scope; <> in <>. The value ++ is the offset into the section of the data. */ ++#define BSF_LOCAL 0x01 ++ ++ /* The symbol has global scope; initialized data in <>. The ++ value is the offset into the section of the data. */ ++#define BSF_GLOBAL 0x02 ++ ++ /* The symbol has global scope and is exported. The value is ++ the offset into the section of the data. */ ++#define BSF_EXPORT BSF_GLOBAL /* No real difference. */ ++ ++ /* A normal C symbol would be one of: ++ <>, <>, <> or ++ <>. */ ++ ++ /* The symbol is a debugging record. The value has an arbitrary ++ meaning, unless BSF_DEBUGGING_RELOC is also set. */ ++#define BSF_DEBUGGING 0x08 ++ ++ /* The symbol denotes a function entry point. Used in ELF, ++ perhaps others someday. */ ++#define BSF_FUNCTION 0x10 ++ ++ /* Used by the linker. */ ++#define BSF_KEEP 0x20 ++#define BSF_KEEP_G 0x40 ++ ++ /* A weak global symbol, overridable without warnings by ++ a regular global symbol of the same name. */ ++#define BSF_WEAK 0x80 ++ ++ /* This symbol was created to point to a section, e.g. ELF's ++ STT_SECTION symbols. */ ++#define BSF_SECTION_SYM 0x100 ++ ++ /* The symbol used to be a common symbol, but now it is ++ allocated. */ ++#define BSF_OLD_COMMON 0x200 ++ ++ /* The default value for common data. */ ++#define BFD_FORT_COMM_DEFAULT_VALUE 0 ++ ++ /* In some files the type of a symbol sometimes alters its ++ location in an output file - ie in coff a <> symbol ++ which is also <> symbol appears where it was ++ declared and not at the end of a section. This bit is set ++ by the target BFD part to convey this information. */ ++#define BSF_NOT_AT_END 0x400 ++ ++ /* Signal that the symbol is the label of constructor section. */ ++#define BSF_CONSTRUCTOR 0x800 ++ ++ /* Signal that the symbol is a warning symbol. The name is a ++ warning. The name of the next symbol is the one to warn about; ++ if a reference is made to a symbol with the same name as the next ++ symbol, a warning is issued by the linker. */ ++#define BSF_WARNING 0x1000 ++ ++ /* Signal that the symbol is indirect. This symbol is an indirect ++ pointer to the symbol with the same name as the next symbol. */ ++#define BSF_INDIRECT 0x2000 ++ ++ /* BSF_FILE marks symbols that contain a file name. This is used ++ for ELF STT_FILE symbols. */ ++#define BSF_FILE 0x4000 ++ ++ /* Symbol is from dynamic linking information. */ ++#define BSF_DYNAMIC 0x8000 ++ ++ /* The symbol denotes a data object. Used in ELF, and perhaps ++ others someday. */ ++#define BSF_OBJECT 0x10000 ++ ++ /* This symbol is a debugging symbol. The value is the offset ++ into the section of the data. BSF_DEBUGGING should be set ++ as well. */ ++#define BSF_DEBUGGING_RELOC 0x20000 ++ ++ /* This symbol is thread local. Used in ELF. */ ++#define BSF_THREAD_LOCAL 0x40000 ++ ++ flagword flags; ++ ++ /* A pointer to the section to which this symbol is ++ relative. This will always be non NULL, there are special ++ sections for undefined and absolute symbols. */ ++ struct bfd_section *section; ++ ++ /* Back end special data. */ ++ union ++ { ++ void *p; ++ bfd_vma i; ++ } ++ udata; ++} ++asymbol; ++ ++#define bfd_get_symtab_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_symtab_upper_bound, (abfd)) ++ ++bfd_boolean bfd_is_local_label (bfd *abfd, asymbol *sym); ++ ++bfd_boolean bfd_is_local_label_name (bfd *abfd, const char *name); ++ ++#define bfd_is_local_label_name(abfd, name) \ ++ BFD_SEND (abfd, _bfd_is_local_label_name, (abfd, name)) ++ ++bfd_boolean bfd_is_target_special_symbol (bfd *abfd, asymbol *sym); ++ ++#define bfd_is_target_special_symbol(abfd, sym) \ ++ BFD_SEND (abfd, _bfd_is_target_special_symbol, (abfd, sym)) ++ ++#define bfd_canonicalize_symtab(abfd, location) \ ++ BFD_SEND (abfd, _bfd_canonicalize_symtab, (abfd, location)) ++ ++bfd_boolean bfd_set_symtab ++ (bfd *abfd, asymbol **location, unsigned int count); ++ ++void bfd_print_symbol_vandf (bfd *abfd, void *file, asymbol *symbol); ++ ++#define bfd_make_empty_symbol(abfd) \ ++ BFD_SEND (abfd, _bfd_make_empty_symbol, (abfd)) ++ ++asymbol *_bfd_generic_make_empty_symbol (bfd *); ++ ++#define bfd_make_debug_symbol(abfd,ptr,size) \ ++ BFD_SEND (abfd, _bfd_make_debug_symbol, (abfd, ptr, size)) ++ ++int bfd_decode_symclass (asymbol *symbol); ++ ++bfd_boolean bfd_is_undefined_symclass (int symclass); ++ ++void bfd_symbol_info (asymbol *symbol, symbol_info *ret); ++ ++bfd_boolean bfd_copy_private_symbol_data ++ (bfd *ibfd, asymbol *isym, bfd *obfd, asymbol *osym); ++ ++#define bfd_copy_private_symbol_data(ibfd, isymbol, obfd, osymbol) \ ++ BFD_SEND (obfd, _bfd_copy_private_symbol_data, \ ++ (ibfd, isymbol, obfd, osymbol)) ++ ++/* Extracted from bfd.c. */ ++struct bfd ++{ ++ /* A unique identifier of the BFD */ ++ unsigned int id; ++ ++ /* The filename the application opened the BFD with. */ ++ const char *filename; ++ ++ /* A pointer to the target jump table. */ ++ const struct bfd_target *xvec; ++ ++ /* The IOSTREAM, and corresponding IO vector that provide access ++ to the file backing the BFD. */ ++ void *iostream; ++ const struct bfd_iovec *iovec; ++ ++ /* Is the file descriptor being cached? That is, can it be closed as ++ needed, and re-opened when accessed later? */ ++ bfd_boolean cacheable; ++ ++ /* Marks whether there was a default target specified when the ++ BFD was opened. This is used to select which matching algorithm ++ to use to choose the back end. */ ++ bfd_boolean target_defaulted; ++ ++ /* The caching routines use these to maintain a ++ least-recently-used list of BFDs. */ ++ struct bfd *lru_prev, *lru_next; ++ ++ /* When a file is closed by the caching routines, BFD retains ++ state information on the file here... */ ++ ufile_ptr where; ++ ++ /* ... and here: (``once'' means at least once). */ ++ bfd_boolean opened_once; ++ ++ /* Set if we have a locally maintained mtime value, rather than ++ getting it from the file each time. */ ++ bfd_boolean mtime_set; ++ ++ /* File modified time, if mtime_set is TRUE. */ ++ long mtime; ++ ++ /* Reserved for an unimplemented file locking extension. */ ++ int ifd; ++ ++ /* The format which belongs to the BFD. (object, core, etc.) */ ++ bfd_format format; ++ ++ /* The direction with which the BFD was opened. */ ++ enum bfd_direction ++ { ++ no_direction = 0, ++ read_direction = 1, ++ write_direction = 2, ++ both_direction = 3 ++ } ++ direction; ++ ++ /* Format_specific flags. */ ++ flagword flags; ++ ++ /* Currently my_archive is tested before adding origin to ++ anything. I believe that this can become always an add of ++ origin, with origin set to 0 for non archive files. */ ++ ufile_ptr origin; ++ ++ /* Remember when output has begun, to stop strange things ++ from happening. */ ++ bfd_boolean output_has_begun; ++ ++ /* A hash table for section names. */ ++ struct bfd_hash_table section_htab; ++ ++ /* Pointer to linked list of sections. */ ++ struct bfd_section *sections; ++ ++ /* The last section on the section list. */ ++ struct bfd_section *section_last; ++ ++ /* The number of sections. */ ++ unsigned int section_count; ++ ++ /* Stuff only useful for object files: ++ The start address. */ ++ bfd_vma start_address; ++ ++ /* Used for input and output. */ ++ unsigned int symcount; ++ ++ /* Symbol table for output BFD (with symcount entries). */ ++ struct bfd_symbol **outsymbols; ++ ++ /* Used for slurped dynamic symbol tables. */ ++ unsigned int dynsymcount; ++ ++ /* Pointer to structure which contains architecture information. */ ++ const struct bfd_arch_info *arch_info; ++ ++ /* Flag set if symbols from this BFD should not be exported. */ ++ bfd_boolean no_export; ++ ++ /* Stuff only useful for archives. */ ++ void *arelt_data; ++ struct bfd *my_archive; /* The containing archive BFD. */ ++ struct bfd *next; /* The next BFD in the archive. */ ++ struct bfd *archive_head; /* The first BFD in the archive. */ ++ bfd_boolean has_armap; ++ ++ /* A chain of BFD structures involved in a link. */ ++ struct bfd *link_next; ++ ++ /* A field used by _bfd_generic_link_add_archive_symbols. This will ++ be used only for archive elements. */ ++ int archive_pass; ++ ++ /* Used by the back end to hold private data. */ ++ union ++ { ++ struct aout_data_struct *aout_data; ++ struct artdata *aout_ar_data; ++ struct _oasys_data *oasys_obj_data; ++ struct _oasys_ar_data *oasys_ar_data; ++ struct coff_tdata *coff_obj_data; ++ struct pe_tdata *pe_obj_data; ++ struct xcoff_tdata *xcoff_obj_data; ++ struct ecoff_tdata *ecoff_obj_data; ++ struct ieee_data_struct *ieee_data; ++ struct ieee_ar_data_struct *ieee_ar_data; ++ struct srec_data_struct *srec_data; ++ struct ihex_data_struct *ihex_data; ++ struct tekhex_data_struct *tekhex_data; ++ struct elf_obj_tdata *elf_obj_data; ++ struct nlm_obj_tdata *nlm_obj_data; ++ struct bout_data_struct *bout_data; ++ struct mmo_data_struct *mmo_data; ++ struct sun_core_struct *sun_core_data; ++ struct sco5_core_struct *sco5_core_data; ++ struct trad_core_struct *trad_core_data; ++ struct som_data_struct *som_data; ++ struct hpux_core_struct *hpux_core_data; ++ struct hppabsd_core_struct *hppabsd_core_data; ++ struct sgi_core_struct *sgi_core_data; ++ struct lynx_core_struct *lynx_core_data; ++ struct osf_core_struct *osf_core_data; ++ struct cisco_core_struct *cisco_core_data; ++ struct versados_data_struct *versados_data; ++ struct netbsd_core_struct *netbsd_core_data; ++ struct mach_o_data_struct *mach_o_data; ++ struct mach_o_fat_data_struct *mach_o_fat_data; ++ struct bfd_pef_data_struct *pef_data; ++ struct bfd_pef_xlib_data_struct *pef_xlib_data; ++ struct bfd_sym_data_struct *sym_data; ++ void *any; ++ } ++ tdata; ++ ++ /* Used by the application to hold private data. */ ++ void *usrdata; ++ ++ /* Where all the allocated stuff under this BFD goes. This is a ++ struct objalloc *, but we use void * to avoid requiring the inclusion ++ of objalloc.h. */ ++ void *memory; ++}; ++ ++typedef enum bfd_error ++{ ++ bfd_error_no_error = 0, ++ bfd_error_system_call, ++ bfd_error_invalid_target, ++ bfd_error_wrong_format, ++ bfd_error_wrong_object_format, ++ bfd_error_invalid_operation, ++ bfd_error_no_memory, ++ bfd_error_no_symbols, ++ bfd_error_no_armap, ++ bfd_error_no_more_archived_files, ++ bfd_error_malformed_archive, ++ bfd_error_file_not_recognized, ++ bfd_error_file_ambiguously_recognized, ++ bfd_error_no_contents, ++ bfd_error_nonrepresentable_section, ++ bfd_error_no_debug_section, ++ bfd_error_bad_value, ++ bfd_error_file_truncated, ++ bfd_error_file_too_big, ++ bfd_error_invalid_error_code ++} ++bfd_error_type; ++ ++bfd_error_type bfd_get_error (void); ++ ++void bfd_set_error (bfd_error_type error_tag); ++ ++const char *bfd_errmsg (bfd_error_type error_tag); ++ ++void bfd_perror (const char *message); ++ ++typedef void (*bfd_error_handler_type) (const char *, ...); ++ ++bfd_error_handler_type bfd_set_error_handler (bfd_error_handler_type); ++ ++void bfd_set_error_program_name (const char *); ++ ++bfd_error_handler_type bfd_get_error_handler (void); ++ ++long bfd_get_reloc_upper_bound (bfd *abfd, asection *sect); ++ ++long bfd_canonicalize_reloc ++ (bfd *abfd, asection *sec, arelent **loc, asymbol **syms); ++ ++void bfd_set_reloc ++ (bfd *abfd, asection *sec, arelent **rel, unsigned int count); ++ ++bfd_boolean bfd_set_file_flags (bfd *abfd, flagword flags); ++ ++int bfd_get_arch_size (bfd *abfd); ++ ++int bfd_get_sign_extend_vma (bfd *abfd); ++ ++bfd_boolean bfd_set_start_address (bfd *abfd, bfd_vma vma); ++ ++unsigned int bfd_get_gp_size (bfd *abfd); ++ ++void bfd_set_gp_size (bfd *abfd, unsigned int i); ++ ++bfd_vma bfd_scan_vma (const char *string, const char **end, int base); ++ ++bfd_boolean bfd_copy_private_header_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_copy_private_header_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_copy_private_header_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_copy_private_bfd_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_copy_private_bfd_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_copy_private_bfd_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_merge_private_bfd_data (bfd *ibfd, bfd *obfd); ++ ++#define bfd_merge_private_bfd_data(ibfd, obfd) \ ++ BFD_SEND (obfd, _bfd_merge_private_bfd_data, \ ++ (ibfd, obfd)) ++bfd_boolean bfd_set_private_flags (bfd *abfd, flagword flags); ++ ++#define bfd_set_private_flags(abfd, flags) \ ++ BFD_SEND (abfd, _bfd_set_private_flags, (abfd, flags)) ++#define bfd_sizeof_headers(abfd, reloc) \ ++ BFD_SEND (abfd, _bfd_sizeof_headers, (abfd, reloc)) ++ ++#define bfd_find_nearest_line(abfd, sec, syms, off, file, func, line) \ ++ BFD_SEND (abfd, _bfd_find_nearest_line, \ ++ (abfd, sec, syms, off, file, func, line)) ++ ++#define bfd_find_line(abfd, syms, sym, file, line) \ ++ BFD_SEND (abfd, _bfd_find_line, \ ++ (abfd, syms, sym, file, line)) ++ ++#define bfd_find_inliner_info(abfd, file, func, line) \ ++ BFD_SEND (abfd, _bfd_find_inliner_info, \ ++ (abfd, file, func, line)) ++ ++#define bfd_debug_info_start(abfd) \ ++ BFD_SEND (abfd, _bfd_debug_info_start, (abfd)) ++ ++#define bfd_debug_info_end(abfd) \ ++ BFD_SEND (abfd, _bfd_debug_info_end, (abfd)) ++ ++#define bfd_debug_info_accumulate(abfd, section) \ ++ BFD_SEND (abfd, _bfd_debug_info_accumulate, (abfd, section)) ++ ++#define bfd_stat_arch_elt(abfd, stat) \ ++ BFD_SEND (abfd, _bfd_stat_arch_elt,(abfd, stat)) ++ ++#define bfd_update_armap_timestamp(abfd) \ ++ BFD_SEND (abfd, _bfd_update_armap_timestamp, (abfd)) ++ ++#define bfd_set_arch_mach(abfd, arch, mach)\ ++ BFD_SEND ( abfd, _bfd_set_arch_mach, (abfd, arch, mach)) ++ ++#define bfd_relax_section(abfd, section, link_info, again) \ ++ BFD_SEND (abfd, _bfd_relax_section, (abfd, section, link_info, again)) ++ ++#define bfd_gc_sections(abfd, link_info) \ ++ BFD_SEND (abfd, _bfd_gc_sections, (abfd, link_info)) ++ ++#define bfd_merge_sections(abfd, link_info) \ ++ BFD_SEND (abfd, _bfd_merge_sections, (abfd, link_info)) ++ ++#define bfd_is_group_section(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_is_group_section, (abfd, sec)) ++ ++#define bfd_discard_group(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_discard_group, (abfd, sec)) ++ ++#define bfd_link_hash_table_create(abfd) \ ++ BFD_SEND (abfd, _bfd_link_hash_table_create, (abfd)) ++ ++#define bfd_link_hash_table_free(abfd, hash) \ ++ BFD_SEND (abfd, _bfd_link_hash_table_free, (hash)) ++ ++#define bfd_link_add_symbols(abfd, info) \ ++ BFD_SEND (abfd, _bfd_link_add_symbols, (abfd, info)) ++ ++#define bfd_link_just_syms(abfd, sec, info) \ ++ BFD_SEND (abfd, _bfd_link_just_syms, (sec, info)) ++ ++#define bfd_final_link(abfd, info) \ ++ BFD_SEND (abfd, _bfd_final_link, (abfd, info)) ++ ++#define bfd_free_cached_info(abfd) \ ++ BFD_SEND (abfd, _bfd_free_cached_info, (abfd)) ++ ++#define bfd_get_dynamic_symtab_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_dynamic_symtab_upper_bound, (abfd)) ++ ++#define bfd_print_private_bfd_data(abfd, file)\ ++ BFD_SEND (abfd, _bfd_print_private_bfd_data, (abfd, file)) ++ ++#define bfd_canonicalize_dynamic_symtab(abfd, asymbols) \ ++ BFD_SEND (abfd, _bfd_canonicalize_dynamic_symtab, (abfd, asymbols)) ++ ++#define bfd_get_synthetic_symtab(abfd, count, syms, dyncount, dynsyms, ret) \ ++ BFD_SEND (abfd, _bfd_get_synthetic_symtab, (abfd, count, syms, \ ++ dyncount, dynsyms, ret)) ++ ++#define bfd_get_dynamic_reloc_upper_bound(abfd) \ ++ BFD_SEND (abfd, _bfd_get_dynamic_reloc_upper_bound, (abfd)) ++ ++#define bfd_canonicalize_dynamic_reloc(abfd, arels, asyms) \ ++ BFD_SEND (abfd, _bfd_canonicalize_dynamic_reloc, (abfd, arels, asyms)) ++ ++extern bfd_byte *bfd_get_relocated_section_contents ++ (bfd *, struct bfd_link_info *, struct bfd_link_order *, bfd_byte *, ++ bfd_boolean, asymbol **); ++ ++bfd_boolean bfd_alt_mach_code (bfd *abfd, int alternative); ++ ++struct bfd_preserve ++{ ++ void *marker; ++ void *tdata; ++ flagword flags; ++ const struct bfd_arch_info *arch_info; ++ struct bfd_section *sections; ++ struct bfd_section *section_last; ++ unsigned int section_count; ++ struct bfd_hash_table section_htab; ++}; ++ ++bfd_boolean bfd_preserve_save (bfd *, struct bfd_preserve *); ++ ++void bfd_preserve_restore (bfd *, struct bfd_preserve *); ++ ++void bfd_preserve_finish (bfd *, struct bfd_preserve *); ++ ++/* Extracted from archive.c. */ ++symindex bfd_get_next_mapent ++ (bfd *abfd, symindex previous, carsym **sym); ++ ++bfd_boolean bfd_set_archive_head (bfd *output, bfd *new_head); ++ ++bfd *bfd_openr_next_archived_file (bfd *archive, bfd *previous); ++ ++/* Extracted from corefile.c. */ ++const char *bfd_core_file_failing_command (bfd *abfd); ++ ++int bfd_core_file_failing_signal (bfd *abfd); ++ ++bfd_boolean core_file_matches_executable_p ++ (bfd *core_bfd, bfd *exec_bfd); ++ ++/* Extracted from targets.c. */ ++#define BFD_SEND(bfd, message, arglist) \ ++ ((*((bfd)->xvec->message)) arglist) ++ ++#ifdef DEBUG_BFD_SEND ++#undef BFD_SEND ++#define BFD_SEND(bfd, message, arglist) \ ++ (((bfd) && (bfd)->xvec && (bfd)->xvec->message) ? \ ++ ((*((bfd)->xvec->message)) arglist) : \ ++ (bfd_assert (__FILE__,__LINE__), NULL)) ++#endif ++#define BFD_SEND_FMT(bfd, message, arglist) \ ++ (((bfd)->xvec->message[(int) ((bfd)->format)]) arglist) ++ ++#ifdef DEBUG_BFD_SEND ++#undef BFD_SEND_FMT ++#define BFD_SEND_FMT(bfd, message, arglist) \ ++ (((bfd) && (bfd)->xvec && (bfd)->xvec->message) ? \ ++ (((bfd)->xvec->message[(int) ((bfd)->format)]) arglist) : \ ++ (bfd_assert (__FILE__,__LINE__), NULL)) ++#endif ++ ++enum bfd_flavour ++{ ++ bfd_target_unknown_flavour, ++ bfd_target_aout_flavour, ++ bfd_target_coff_flavour, ++ bfd_target_ecoff_flavour, ++ bfd_target_xcoff_flavour, ++ bfd_target_elf_flavour, ++ bfd_target_ieee_flavour, ++ bfd_target_nlm_flavour, ++ bfd_target_oasys_flavour, ++ bfd_target_tekhex_flavour, ++ bfd_target_srec_flavour, ++ bfd_target_ihex_flavour, ++ bfd_target_som_flavour, ++ bfd_target_os9k_flavour, ++ bfd_target_versados_flavour, ++ bfd_target_msdos_flavour, ++ bfd_target_ovax_flavour, ++ bfd_target_evax_flavour, ++ bfd_target_mmo_flavour, ++ bfd_target_mach_o_flavour, ++ bfd_target_pef_flavour, ++ bfd_target_pef_xlib_flavour, ++ bfd_target_sym_flavour ++}; ++ ++enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN }; ++ ++/* Forward declaration. */ ++typedef struct bfd_link_info _bfd_link_info; ++ ++typedef struct bfd_target ++{ ++ /* Identifies the kind of target, e.g., SunOS4, Ultrix, etc. */ ++ char *name; ++ ++ /* The "flavour" of a back end is a general indication about ++ the contents of a file. */ ++ enum bfd_flavour flavour; ++ ++ /* The order of bytes within the data area of a file. */ ++ enum bfd_endian byteorder; ++ ++ /* The order of bytes within the header parts of a file. */ ++ enum bfd_endian header_byteorder; ++ ++ /* A mask of all the flags which an executable may have set - ++ from the set <>, <>, ...<>. */ ++ flagword object_flags; ++ ++ /* A mask of all the flags which a section may have set - from ++ the set <>, <>, ...<>. */ ++ flagword section_flags; ++ ++ /* The character normally found at the front of a symbol. ++ (if any), perhaps `_'. */ ++ char symbol_leading_char; ++ ++ /* The pad character for file names within an archive header. */ ++ char ar_pad_char; ++ ++ /* The maximum number of characters in an archive header. */ ++ unsigned short ar_max_namelen; ++ ++ /* Entries for byte swapping for data. These are different from the ++ other entry points, since they don't take a BFD as the first argument. ++ Certain other handlers could do the same. */ ++ bfd_uint64_t (*bfd_getx64) (const void *); ++ bfd_int64_t (*bfd_getx_signed_64) (const void *); ++ void (*bfd_putx64) (bfd_uint64_t, void *); ++ bfd_vma (*bfd_getx32) (const void *); ++ bfd_signed_vma (*bfd_getx_signed_32) (const void *); ++ void (*bfd_putx32) (bfd_vma, void *); ++ bfd_vma (*bfd_getx16) (const void *); ++ bfd_signed_vma (*bfd_getx_signed_16) (const void *); ++ void (*bfd_putx16) (bfd_vma, void *); ++ ++ /* Byte swapping for the headers. */ ++ bfd_uint64_t (*bfd_h_getx64) (const void *); ++ bfd_int64_t (*bfd_h_getx_signed_64) (const void *); ++ void (*bfd_h_putx64) (bfd_uint64_t, void *); ++ bfd_vma (*bfd_h_getx32) (const void *); ++ bfd_signed_vma (*bfd_h_getx_signed_32) (const void *); ++ void (*bfd_h_putx32) (bfd_vma, void *); ++ bfd_vma (*bfd_h_getx16) (const void *); ++ bfd_signed_vma (*bfd_h_getx_signed_16) (const void *); ++ void (*bfd_h_putx16) (bfd_vma, void *); ++ ++ /* Format dependent routines: these are vectors of entry points ++ within the target vector structure, one for each format to check. */ ++ ++ /* Check the format of a file being read. Return a <> or zero. */ ++ const struct bfd_target *(*_bfd_check_format[bfd_type_end]) (bfd *); ++ ++ /* Set the format of a file being written. */ ++ bfd_boolean (*_bfd_set_format[bfd_type_end]) (bfd *); ++ ++ /* Write cached information into a file being written, at <>. */ ++ bfd_boolean (*_bfd_write_contents[bfd_type_end]) (bfd *); ++ ++ ++ /* Generic entry points. */ ++#define BFD_JUMP_TABLE_GENERIC(NAME) \ ++ NAME##_close_and_cleanup, \ ++ NAME##_bfd_free_cached_info, \ ++ NAME##_new_section_hook, \ ++ NAME##_get_section_contents, \ ++ NAME##_get_section_contents_in_window ++ ++ /* Called when the BFD is being closed to do any necessary cleanup. */ ++ bfd_boolean (*_close_and_cleanup) (bfd *); ++ /* Ask the BFD to free all cached information. */ ++ bfd_boolean (*_bfd_free_cached_info) (bfd *); ++ /* Called when a new section is created. */ ++ bfd_boolean (*_new_section_hook) (bfd *, sec_ptr); ++ /* Read the contents of a section. */ ++ bfd_boolean (*_bfd_get_section_contents) ++ (bfd *, sec_ptr, void *, file_ptr, bfd_size_type); ++ bfd_boolean (*_bfd_get_section_contents_in_window) ++ (bfd *, sec_ptr, bfd_window *, file_ptr, bfd_size_type); ++ ++ /* Entry points to copy private data. */ ++#define BFD_JUMP_TABLE_COPY(NAME) \ ++ NAME##_bfd_copy_private_bfd_data, \ ++ NAME##_bfd_merge_private_bfd_data, \ ++ NAME##_bfd_copy_private_section_data, \ ++ NAME##_bfd_copy_private_symbol_data, \ ++ NAME##_bfd_copy_private_header_data, \ ++ NAME##_bfd_set_private_flags, \ ++ NAME##_bfd_print_private_bfd_data ++ ++ /* Called to copy BFD general private data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_bfd_data) (bfd *, bfd *); ++ /* Called to merge BFD general private data from one object file ++ to a common output file when linking. */ ++ bfd_boolean (*_bfd_merge_private_bfd_data) (bfd *, bfd *); ++ /* Called to copy BFD private section data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_section_data) ++ (bfd *, sec_ptr, bfd *, sec_ptr); ++ /* Called to copy BFD private symbol data from one symbol ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_symbol_data) ++ (bfd *, asymbol *, bfd *, asymbol *); ++ /* Called to copy BFD private header data from one object file ++ to another. */ ++ bfd_boolean (*_bfd_copy_private_header_data) ++ (bfd *, bfd *); ++ /* Called to set private backend flags. */ ++ bfd_boolean (*_bfd_set_private_flags) (bfd *, flagword); ++ ++ /* Called to print private BFD data. */ ++ bfd_boolean (*_bfd_print_private_bfd_data) (bfd *, void *); ++ ++ /* Core file entry points. */ ++#define BFD_JUMP_TABLE_CORE(NAME) \ ++ NAME##_core_file_failing_command, \ ++ NAME##_core_file_failing_signal, \ ++ NAME##_core_file_matches_executable_p ++ ++ char * (*_core_file_failing_command) (bfd *); ++ int (*_core_file_failing_signal) (bfd *); ++ bfd_boolean (*_core_file_matches_executable_p) (bfd *, bfd *); ++ ++ /* Archive entry points. */ ++#define BFD_JUMP_TABLE_ARCHIVE(NAME) \ ++ NAME##_slurp_armap, \ ++ NAME##_slurp_extended_name_table, \ ++ NAME##_construct_extended_name_table, \ ++ NAME##_truncate_arname, \ ++ NAME##_write_armap, \ ++ NAME##_read_ar_hdr, \ ++ NAME##_openr_next_archived_file, \ ++ NAME##_get_elt_at_index, \ ++ NAME##_generic_stat_arch_elt, \ ++ NAME##_update_armap_timestamp ++ ++ bfd_boolean (*_bfd_slurp_armap) (bfd *); ++ bfd_boolean (*_bfd_slurp_extended_name_table) (bfd *); ++ bfd_boolean (*_bfd_construct_extended_name_table) ++ (bfd *, char **, bfd_size_type *, const char **); ++ void (*_bfd_truncate_arname) (bfd *, const char *, char *); ++ bfd_boolean (*write_armap) ++ (bfd *, unsigned int, struct orl *, unsigned int, int); ++ void * (*_bfd_read_ar_hdr_fn) (bfd *); ++ bfd * (*openr_next_archived_file) (bfd *, bfd *); ++#define bfd_get_elt_at_index(b,i) BFD_SEND (b, _bfd_get_elt_at_index, (b,i)) ++ bfd * (*_bfd_get_elt_at_index) (bfd *, symindex); ++ int (*_bfd_stat_arch_elt) (bfd *, struct stat *); ++ bfd_boolean (*_bfd_update_armap_timestamp) (bfd *); ++ ++ /* Entry points used for symbols. */ ++#define BFD_JUMP_TABLE_SYMBOLS(NAME) \ ++ NAME##_get_symtab_upper_bound, \ ++ NAME##_canonicalize_symtab, \ ++ NAME##_make_empty_symbol, \ ++ NAME##_print_symbol, \ ++ NAME##_get_symbol_info, \ ++ NAME##_bfd_is_local_label_name, \ ++ NAME##_bfd_is_target_special_symbol, \ ++ NAME##_get_lineno, \ ++ NAME##_find_nearest_line, \ ++ _bfd_generic_find_line, \ ++ NAME##_find_inliner_info, \ ++ NAME##_bfd_make_debug_symbol, \ ++ NAME##_read_minisymbols, \ ++ NAME##_minisymbol_to_symbol ++ ++ long (*_bfd_get_symtab_upper_bound) (bfd *); ++ long (*_bfd_canonicalize_symtab) ++ (bfd *, struct bfd_symbol **); ++ struct bfd_symbol * ++ (*_bfd_make_empty_symbol) (bfd *); ++ void (*_bfd_print_symbol) ++ (bfd *, void *, struct bfd_symbol *, bfd_print_symbol_type); ++#define bfd_print_symbol(b,p,s,e) BFD_SEND (b, _bfd_print_symbol, (b,p,s,e)) ++ void (*_bfd_get_symbol_info) ++ (bfd *, struct bfd_symbol *, symbol_info *); ++#define bfd_get_symbol_info(b,p,e) BFD_SEND (b, _bfd_get_symbol_info, (b,p,e)) ++ bfd_boolean (*_bfd_is_local_label_name) (bfd *, const char *); ++ bfd_boolean (*_bfd_is_target_special_symbol) (bfd *, asymbol *); ++ alent * (*_get_lineno) (bfd *, struct bfd_symbol *); ++ bfd_boolean (*_bfd_find_nearest_line) ++ (bfd *, struct bfd_section *, struct bfd_symbol **, bfd_vma, ++ const char **, const char **, unsigned int *); ++ bfd_boolean (*_bfd_find_line) ++ (bfd *, struct bfd_symbol **, struct bfd_symbol *, ++ const char **, unsigned int *); ++ bfd_boolean (*_bfd_find_inliner_info) ++ (bfd *, const char **, const char **, unsigned int *); ++ /* Back-door to allow format-aware applications to create debug symbols ++ while using BFD for everything else. Currently used by the assembler ++ when creating COFF files. */ ++ asymbol * (*_bfd_make_debug_symbol) ++ (bfd *, void *, unsigned long size); ++#define bfd_read_minisymbols(b, d, m, s) \ ++ BFD_SEND (b, _read_minisymbols, (b, d, m, s)) ++ long (*_read_minisymbols) ++ (bfd *, bfd_boolean, void **, unsigned int *); ++#define bfd_minisymbol_to_symbol(b, d, m, f) \ ++ BFD_SEND (b, _minisymbol_to_symbol, (b, d, m, f)) ++ asymbol * (*_minisymbol_to_symbol) ++ (bfd *, bfd_boolean, const void *, asymbol *); ++ ++ /* Routines for relocs. */ ++#define BFD_JUMP_TABLE_RELOCS(NAME) \ ++ NAME##_get_reloc_upper_bound, \ ++ NAME##_canonicalize_reloc, \ ++ NAME##_bfd_reloc_type_lookup ++ ++ long (*_get_reloc_upper_bound) (bfd *, sec_ptr); ++ long (*_bfd_canonicalize_reloc) ++ (bfd *, sec_ptr, arelent **, struct bfd_symbol **); ++ /* See documentation on reloc types. */ ++ reloc_howto_type * ++ (*reloc_type_lookup) (bfd *, bfd_reloc_code_real_type); ++ ++ /* Routines used when writing an object file. */ ++#define BFD_JUMP_TABLE_WRITE(NAME) \ ++ NAME##_set_arch_mach, \ ++ NAME##_set_section_contents ++ ++ bfd_boolean (*_bfd_set_arch_mach) ++ (bfd *, enum bfd_architecture, unsigned long); ++ bfd_boolean (*_bfd_set_section_contents) ++ (bfd *, sec_ptr, const void *, file_ptr, bfd_size_type); ++ ++ /* Routines used by the linker. */ ++#define BFD_JUMP_TABLE_LINK(NAME) \ ++ NAME##_sizeof_headers, \ ++ NAME##_bfd_get_relocated_section_contents, \ ++ NAME##_bfd_relax_section, \ ++ NAME##_bfd_link_hash_table_create, \ ++ NAME##_bfd_link_hash_table_free, \ ++ NAME##_bfd_link_add_symbols, \ ++ NAME##_bfd_link_just_syms, \ ++ NAME##_bfd_final_link, \ ++ NAME##_bfd_link_split_section, \ ++ NAME##_bfd_gc_sections, \ ++ NAME##_bfd_merge_sections, \ ++ NAME##_bfd_is_group_section, \ ++ NAME##_bfd_discard_group, \ ++ NAME##_section_already_linked \ ++ ++ int (*_bfd_sizeof_headers) (bfd *, bfd_boolean); ++ bfd_byte * (*_bfd_get_relocated_section_contents) ++ (bfd *, struct bfd_link_info *, struct bfd_link_order *, ++ bfd_byte *, bfd_boolean, struct bfd_symbol **); ++ ++ bfd_boolean (*_bfd_relax_section) ++ (bfd *, struct bfd_section *, struct bfd_link_info *, bfd_boolean *); ++ ++ /* Create a hash table for the linker. Different backends store ++ different information in this table. */ ++ struct bfd_link_hash_table * ++ (*_bfd_link_hash_table_create) (bfd *); ++ ++ /* Release the memory associated with the linker hash table. */ ++ void (*_bfd_link_hash_table_free) (struct bfd_link_hash_table *); ++ ++ /* Add symbols from this object file into the hash table. */ ++ bfd_boolean (*_bfd_link_add_symbols) (bfd *, struct bfd_link_info *); ++ ++ /* Indicate that we are only retrieving symbol values from this section. */ ++ void (*_bfd_link_just_syms) (asection *, struct bfd_link_info *); ++ ++ /* Do a link based on the link_order structures attached to each ++ section of the BFD. */ ++ bfd_boolean (*_bfd_final_link) (bfd *, struct bfd_link_info *); ++ ++ /* Should this section be split up into smaller pieces during linking. */ ++ bfd_boolean (*_bfd_link_split_section) (bfd *, struct bfd_section *); ++ ++ /* Remove sections that are not referenced from the output. */ ++ bfd_boolean (*_bfd_gc_sections) (bfd *, struct bfd_link_info *); ++ ++ /* Attempt to merge SEC_MERGE sections. */ ++ bfd_boolean (*_bfd_merge_sections) (bfd *, struct bfd_link_info *); ++ ++ /* Is this section a member of a group? */ ++ bfd_boolean (*_bfd_is_group_section) (bfd *, const struct bfd_section *); ++ ++ /* Discard members of a group. */ ++ bfd_boolean (*_bfd_discard_group) (bfd *, struct bfd_section *); ++ ++ /* Check if SEC has been already linked during a reloceatable or ++ final link. */ ++ void (*_section_already_linked) (bfd *, struct bfd_section *); ++ ++ /* Routines to handle dynamic symbols and relocs. */ ++#define BFD_JUMP_TABLE_DYNAMIC(NAME) \ ++ NAME##_get_dynamic_symtab_upper_bound, \ ++ NAME##_canonicalize_dynamic_symtab, \ ++ NAME##_get_synthetic_symtab, \ ++ NAME##_get_dynamic_reloc_upper_bound, \ ++ NAME##_canonicalize_dynamic_reloc ++ ++ /* Get the amount of memory required to hold the dynamic symbols. */ ++ long (*_bfd_get_dynamic_symtab_upper_bound) (bfd *); ++ /* Read in the dynamic symbols. */ ++ long (*_bfd_canonicalize_dynamic_symtab) ++ (bfd *, struct bfd_symbol **); ++ /* Create synthetized symbols. */ ++ long (*_bfd_get_synthetic_symtab) ++ (bfd *, long, struct bfd_symbol **, long, struct bfd_symbol **, ++ struct bfd_symbol **); ++ /* Get the amount of memory required to hold the dynamic relocs. */ ++ long (*_bfd_get_dynamic_reloc_upper_bound) (bfd *); ++ /* Read in the dynamic relocs. */ ++ long (*_bfd_canonicalize_dynamic_reloc) ++ (bfd *, arelent **, struct bfd_symbol **); ++ ++ /* Opposite endian version of this target. */ ++ const struct bfd_target * alternative_target; ++ ++ /* Data for use by back-end routines, which isn't ++ generic enough to belong in this structure. */ ++ const void *backend_data; ++ ++} bfd_target; ++ ++bfd_boolean bfd_set_default_target (const char *name); ++ ++const bfd_target *bfd_find_target (const char *target_name, bfd *abfd); ++ ++const char ** bfd_target_list (void); ++ ++const bfd_target *bfd_search_for_target ++ (int (*search_func) (const bfd_target *, void *), ++ void *); ++ ++/* Extracted from format.c. */ ++bfd_boolean bfd_check_format (bfd *abfd, bfd_format format); ++ ++bfd_boolean bfd_check_format_matches ++ (bfd *abfd, bfd_format format, char ***matching); ++ ++bfd_boolean bfd_set_format (bfd *abfd, bfd_format format); ++ ++const char *bfd_format_string (bfd_format format); ++ ++/* Extracted from linker.c. */ ++bfd_boolean bfd_link_split_section (bfd *abfd, asection *sec); ++ ++#define bfd_link_split_section(abfd, sec) \ ++ BFD_SEND (abfd, _bfd_link_split_section, (abfd, sec)) ++ ++void bfd_section_already_linked (bfd *abfd, asection *sec); ++ ++#define bfd_section_already_linked(abfd, sec) \ ++ BFD_SEND (abfd, _section_already_linked, (abfd, sec)) ++ ++/* Extracted from simple.c. */ ++bfd_byte *bfd_simple_get_relocated_section_contents ++ (bfd *abfd, asection *sec, bfd_byte *outbuf, asymbol **symbol_table); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +--- a/arch/x86/include/asm/irq_vectors.h ++++ b/arch/x86/include/asm/irq_vectors.h +@@ -49,6 +49,7 @@ + #ifdef CONFIG_X86_32 + # define SYSCALL_VECTOR 0x80 + #endif ++#define KDBENTER_VECTOR 0x81 + + /* + * Vectors 0x30-0x3f are used for ISA interrupts. +@@ -102,6 +103,12 @@ + #define NUM_INVALIDATE_TLB_VECTORS 8 + + /* ++ * KDB_VECTOR will take over vector 0xfe when it is needed, as in theory ++ * it should not be used anyway. ++ */ ++#define KDB_VECTOR 0xfe ++ ++/* + * Local APIC timer IRQ vector is on a different priority level, + * to work around the 'lost local interrupt if more than 2 IRQ + * sources per level' errata. +--- /dev/null ++++ b/arch/x86/include/asm/kdb.h +@@ -0,0 +1,140 @@ ++#ifndef _ASM_KDB_H ++#define _ASM_KDB_H ++ ++/* ++ * Kernel Debugger Architecture Dependent (x86) Global Headers ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2008 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * KDB_ENTER() is a macro which causes entry into the kernel ++ * debugger from any point in the kernel code stream. If it ++ * is intended to be used from interrupt level, it must use ++ * a non-maskable entry method. The vector is KDB_VECTOR, ++ * defined in hw_irq.h ++ */ ++#define KDB_ENTER() do {if (kdb_on && !KDB_IS_RUNNING()) { asm("\tint $129\n"); }} while(0) ++ ++/* ++ * Needed for exported symbols. ++ */ ++typedef unsigned long kdb_machreg_t; ++ ++/* ++ * Per cpu arch specific kdb state. Must be in range 0xff000000. ++ */ ++#define KDB_STATE_A_IF 0x01000000 /* Saved IF flag */ ++ ++ ++#ifdef CONFIG_X86_32 ++ ++#define kdb_machreg_fmt "0x%lx" ++#define kdb_machreg_fmt0 "0x%08lx" ++#define kdb_bfd_vma_fmt "0x%lx" ++#define kdb_bfd_vma_fmt0 "0x%08lx" ++#define kdb_elfw_addr_fmt "0x%x" ++#define kdb_elfw_addr_fmt0 "0x%08x" ++#define kdb_f_count_fmt "%ld" ++ ++#else /* CONFIG_X86_32 */ ++ ++#define kdb_machreg_fmt "0x%lx" ++#define kdb_machreg_fmt0 "0x%016lx" ++#define kdb_bfd_vma_fmt "0x%lx" ++#define kdb_bfd_vma_fmt0 "0x%016lx" ++#define kdb_elfw_addr_fmt "0x%x" ++#define kdb_elfw_addr_fmt0 "0x%016x" ++#define kdb_f_count_fmt "%ld" ++ ++/* ++ * Functions to safely read and write kernel areas. The {to,from}_xxx ++ * addresses are not necessarily valid, these functions must check for ++ * validity. If the arch already supports get and put routines with ++ * suitable validation and/or recovery on invalid addresses then use ++ * those routines, otherwise check it yourself. ++ */ ++ ++/* ++ * asm-i386 uaccess.h supplies __copy_to_user which relies on MMU to ++ * trap invalid addresses in the _xxx fields. Verify the other address ++ * of the pair is valid by accessing the first and last byte ourselves, ++ * then any access violations should only be caused by the _xxx ++ * addresses, ++ */ ++ ++#include ++ ++static inline int ++__kdba_putarea_size(unsigned long to_xxx, void *from, size_t size) ++{ ++ mm_segment_t oldfs = get_fs(); ++ int r; ++ char c; ++ c = *((volatile char *)from); ++ c = *((volatile char *)from + size - 1); ++ ++ if (to_xxx < PAGE_OFFSET) { ++ return kdb_putuserarea_size(to_xxx, from, size); ++ } ++ ++ set_fs(KERNEL_DS); ++ r = __copy_to_user_inatomic((void *)to_xxx, from, size); ++ set_fs(oldfs); ++ return r; ++} ++ ++static inline int ++__kdba_getarea_size(void *to, unsigned long from_xxx, size_t size) ++{ ++ mm_segment_t oldfs = get_fs(); ++ int r; ++ *((volatile char *)to) = '\0'; ++ *((volatile char *)to + size - 1) = '\0'; ++ ++ if (from_xxx < PAGE_OFFSET) { ++ return kdb_getuserarea_size(to, from_xxx, size); ++ } ++ ++ set_fs(KERNEL_DS); ++ r = __copy_to_user_inatomic(to, (void *)from_xxx, size); ++ set_fs(oldfs); ++ return r; ++} ++ ++/* For numa with replicated code/data, the platform must supply its own ++ * kdba_putarea_size and kdba_getarea_size routines. Without replication kdb ++ * uses the standard architecture routines. ++ */ ++#ifdef CONFIG_NUMA_REPLICATE ++extern int kdba_putarea_size(unsigned long to_xxx, void *from, size_t size); ++extern int kdba_getarea_size(void *to, unsigned long from_xxx, size_t size); ++#else ++#define kdba_putarea_size __kdba_putarea_size ++#define kdba_getarea_size __kdba_getarea_size ++#endif ++ ++static inline int ++kdba_verify_rw(unsigned long addr, size_t size) ++{ ++ unsigned char data[size]; ++ return(kdba_getarea_size(data, addr, size) || kdba_putarea_size(addr, data, size)); ++} ++ ++#endif /* !CONFIG_X86_32 */ ++ ++static inline unsigned long ++kdba_funcptr_value(void *fp) ++{ ++ return (unsigned long)fp; ++} ++ ++#ifdef CONFIG_SMP ++extern void kdba_giveback_vector(int); ++#endif ++ ++#endif /* !_ASM_KDB_H */ +--- /dev/null ++++ b/arch/x86/include/asm/kdbprivate.h +@@ -0,0 +1,241 @@ ++#ifndef _ASM_KDBPRIVATE_H ++#define _ASM_KDBPRIVATE_H ++ ++/* ++ * Kernel Debugger Architecture Dependent (x86) Private Headers ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2008 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++typedef unsigned char kdb_machinst_t; ++ ++/* ++ * KDB_MAXBPT describes the total number of breakpoints ++ * supported by this architecure. ++ */ ++#define KDB_MAXBPT 16 ++ ++/* ++ * KDB_MAXHARDBPT describes the total number of hardware ++ * breakpoint registers that exist. ++ */ ++#define KDB_MAXHARDBPT 4 ++ ++/* Maximum number of arguments to a function */ ++#define KDBA_MAXARGS 16 ++ ++/* ++ * Support for ia32 debug registers ++ */ ++typedef struct _kdbhard_bp { ++ kdb_machreg_t bph_reg; /* Register this breakpoint uses */ ++ ++ unsigned int bph_free:1; /* Register available for use */ ++ unsigned int bph_data:1; /* Data Access breakpoint */ ++ ++ unsigned int bph_write:1; /* Write Data breakpoint */ ++ unsigned int bph_mode:2; /* 0=inst, 1=write, 2=io, 3=read */ ++ unsigned int bph_length:2; /* 0=1, 1=2, 2=BAD, 3=4 (bytes) */ ++ unsigned int bph_installed; /* flag: hw bp is installed */ ++} kdbhard_bp_t; ++ ++#define IA32_BREAKPOINT_INSTRUCTION 0xcc ++ ++#define DR6_BT 0x00008000 ++#define DR6_BS 0x00004000 ++#define DR6_BD 0x00002000 ++ ++#define DR6_B3 0x00000008 ++#define DR6_B2 0x00000004 ++#define DR6_B1 0x00000002 ++#define DR6_B0 0x00000001 ++#define DR6_DR_MASK 0x0000000F ++ ++#define DR7_RW_VAL(dr, drnum) \ ++ (((dr) >> (16 + (4 * (drnum)))) & 0x3) ++ ++#define DR7_RW_SET(dr, drnum, rw) \ ++ do { \ ++ (dr) &= ~(0x3 << (16 + (4 * (drnum)))); \ ++ (dr) |= (((rw) & 0x3) << (16 + (4 * (drnum)))); \ ++ } while (0) ++ ++#define DR7_RW0(dr) DR7_RW_VAL(dr, 0) ++#define DR7_RW0SET(dr,rw) DR7_RW_SET(dr, 0, rw) ++#define DR7_RW1(dr) DR7_RW_VAL(dr, 1) ++#define DR7_RW1SET(dr,rw) DR7_RW_SET(dr, 1, rw) ++#define DR7_RW2(dr) DR7_RW_VAL(dr, 2) ++#define DR7_RW2SET(dr,rw) DR7_RW_SET(dr, 2, rw) ++#define DR7_RW3(dr) DR7_RW_VAL(dr, 3) ++#define DR7_RW3SET(dr,rw) DR7_RW_SET(dr, 3, rw) ++ ++ ++#define DR7_LEN_VAL(dr, drnum) \ ++ (((dr) >> (18 + (4 * (drnum)))) & 0x3) ++ ++#define DR7_LEN_SET(dr, drnum, rw) \ ++ do { \ ++ (dr) &= ~(0x3 << (18 + (4 * (drnum)))); \ ++ (dr) |= (((rw) & 0x3) << (18 + (4 * (drnum)))); \ ++ } while (0) ++ ++#define DR7_LEN0(dr) DR7_LEN_VAL(dr, 0) ++#define DR7_LEN0SET(dr,len) DR7_LEN_SET(dr, 0, len) ++#define DR7_LEN1(dr) DR7_LEN_VAL(dr, 1) ++#define DR7_LEN1SET(dr,len) DR7_LEN_SET(dr, 1, len) ++#define DR7_LEN2(dr) DR7_LEN_VAL(dr, 2) ++#define DR7_LEN2SET(dr,len) DR7_LEN_SET(dr, 2, len) ++#define DR7_LEN3(dr) DR7_LEN_VAL(dr, 3) ++#define DR7_LEN3SET(dr,len) DR7_LEN_SET(dr, 3, len) ++ ++#define DR7_G0(dr) (((dr)>>1)&0x1) ++#define DR7_G0SET(dr) ((dr) |= 0x2) ++#define DR7_G0CLR(dr) ((dr) &= ~0x2) ++#define DR7_G1(dr) (((dr)>>3)&0x1) ++#define DR7_G1SET(dr) ((dr) |= 0x8) ++#define DR7_G1CLR(dr) ((dr) &= ~0x8) ++#define DR7_G2(dr) (((dr)>>5)&0x1) ++#define DR7_G2SET(dr) ((dr) |= 0x20) ++#define DR7_G2CLR(dr) ((dr) &= ~0x20) ++#define DR7_G3(dr) (((dr)>>7)&0x1) ++#define DR7_G3SET(dr) ((dr) |= 0x80) ++#define DR7_G3CLR(dr) ((dr) &= ~0x80) ++ ++#define DR7_L0(dr) (((dr))&0x1) ++#define DR7_L0SET(dr) ((dr) |= 0x1) ++#define DR7_L0CLR(dr) ((dr) &= ~0x1) ++#define DR7_L1(dr) (((dr)>>2)&0x1) ++#define DR7_L1SET(dr) ((dr) |= 0x4) ++#define DR7_L1CLR(dr) ((dr) &= ~0x4) ++#define DR7_L2(dr) (((dr)>>4)&0x1) ++#define DR7_L2SET(dr) ((dr) |= 0x10) ++#define DR7_L2CLR(dr) ((dr) &= ~0x10) ++#define DR7_L3(dr) (((dr)>>6)&0x1) ++#define DR7_L3SET(dr) ((dr) |= 0x40) ++#define DR7_L3CLR(dr) ((dr) &= ~0x40) ++ ++#define DR7_GD 0x00002000 /* General Detect Enable */ ++#define DR7_GE 0x00000200 /* Global exact */ ++#define DR7_LE 0x00000100 /* Local exact */ ++ ++extern kdb_machreg_t kdba_getdr6(void); ++extern void kdba_putdr6(kdb_machreg_t); ++ ++extern kdb_machreg_t kdba_getdr7(void); ++ ++struct kdba_running_process { ++ long sp; /* KDB may be on a different stack */ ++ long ip; /* eip when esp was set */ ++}; ++ ++static inline ++void kdba_unsave_running(struct kdba_running_process *k, struct pt_regs *regs) ++{ ++} ++ ++struct kdb_activation_record; ++extern void kdba_get_stack_info_alternate(kdb_machreg_t addr, int cpu, ++ struct kdb_activation_record *ar); ++ ++extern void kdba_wait_for_cpus(void); ++ ++ ++#ifdef CONFIG_X86_32 ++ ++#define DR_TYPE_EXECUTE 0x0 ++#define DR_TYPE_WRITE 0x1 ++#define DR_TYPE_IO 0x2 ++#define DR_TYPE_RW 0x3 ++ ++/* ++ * Platform specific environment entries ++ */ ++#define KDB_PLATFORM_ENV "IDMODE=x86", "BYTESPERWORD=4", "IDCOUNT=16" ++ ++/* ++ * Support for setjmp/longjmp ++ */ ++#define JB_BX 0 ++#define JB_SI 1 ++#define JB_DI 2 ++#define JB_BP 3 ++#define JB_SP 4 ++#define JB_PC 5 ++ ++typedef struct __kdb_jmp_buf { ++ unsigned long regs[6]; /* kdba_setjmp assumes fixed offsets here */ ++} kdb_jmp_buf; ++ ++extern int asmlinkage kdba_setjmp(kdb_jmp_buf *); ++extern void asmlinkage kdba_longjmp(kdb_jmp_buf *, int); ++#define kdba_setjmp kdba_setjmp ++ ++extern kdb_jmp_buf *kdbjmpbuf; ++ ++/* Arch specific data saved for running processes */ ++static inline ++void kdba_save_running(struct kdba_running_process *k, struct pt_regs *regs) ++{ ++ k->sp = current_stack_pointer; ++ __asm__ __volatile__ ( " lea 1f,%%eax; movl %%eax,%0 ; 1: " : "=r"(k->ip) : : "eax" ); ++} ++ ++extern void kdb_interrupt(void); ++ ++#define KDB_INT_REGISTERS 8 ++ ++#else /* CONFIG_X86_32 */ ++ ++extern kdb_machreg_t kdba_getdr(int); ++extern void kdba_putdr(int, kdb_machreg_t); ++ ++extern kdb_machreg_t kdb_getcr(int); ++ ++/* ++ * Platform specific environment entries ++ */ ++#define KDB_PLATFORM_ENV "IDMODE=x86_64", "BYTESPERWORD=8", "IDCOUNT=16" ++ ++/* ++ * reg indicies for x86_64 setjmp/longjmp ++ */ ++#define JB_RBX 0 ++#define JB_RBP 1 ++#define JB_R12 2 ++#define JB_R13 3 ++#define JB_R14 4 ++#define JB_R15 5 ++#define JB_RSP 6 ++#define JB_PC 7 ++ ++typedef struct __kdb_jmp_buf { ++ unsigned long regs[8]; /* kdba_setjmp assumes fixed offsets here */ ++} kdb_jmp_buf; ++ ++extern int asmlinkage kdba_setjmp(kdb_jmp_buf *); ++extern void asmlinkage kdba_longjmp(kdb_jmp_buf *, int); ++#define kdba_setjmp kdba_setjmp ++ ++extern kdb_jmp_buf *kdbjmpbuf; ++ ++/* Arch specific data saved for running processes */ ++register unsigned long current_stack_pointer asm("rsp") __used; ++ ++static inline ++void kdba_save_running(struct kdba_running_process *k, struct pt_regs *regs) ++{ ++ k->sp = current_stack_pointer; ++ __asm__ __volatile__ ( " lea 0(%%rip),%%rax; movq %%rax,%0 ; " : "=r"(k->ip) : : "rax" ); ++} ++ ++extern asmlinkage void kdb_interrupt(void); ++ ++#define KDB_INT_REGISTERS 16 ++ ++#endif /* !CONFIG_X86_32 */ ++ ++#endif /* !_ASM_KDBPRIVATE_H */ +--- a/arch/x86/include/asm/kdebug.h ++++ b/arch/x86/include/asm/kdebug.h +@@ -15,6 +15,8 @@ enum die_val { + DIE_DIE, + DIE_NMIWATCHDOG, + DIE_KERNELDEBUG, ++ DIE_KDEBUG_ENTER, ++ DIE_KDEBUG_LEAVE, + DIE_TRAP, + DIE_GPF, + DIE_CALL, +--- a/arch/x86/include/asm/ptrace.h ++++ b/arch/x86/include/asm/ptrace.h +@@ -16,6 +16,29 @@ + /* this struct defines the way the registers are stored on the + stack during a system call. */ + ++enum EFLAGS { ++ EF_CF = 0x00000001, ++ EF_PF = 0x00000004, ++ EF_AF = 0x00000010, ++ EF_ZF = 0x00000040, ++ EF_SF = 0x00000080, ++ EF_TF = 0x00000100, ++ EF_IE = 0x00000200, ++ EF_DF = 0x00000400, ++ EF_OF = 0x00000800, ++ EF_IOPL = 0x00003000, ++ EF_IOPL_RING0 = 0x00000000, ++ EF_IOPL_RING1 = 0x00001000, ++ EF_IOPL_RING2 = 0x00002000, ++ EF_NT = 0x00004000, /* nested task */ ++ EF_RF = 0x00010000, /* resume */ ++ EF_VM = 0x00020000, /* virtual mode */ ++ EF_AC = 0x00040000, /* alignment */ ++ EF_VIF = 0x00080000, /* virtual interrupt */ ++ EF_VIP = 0x00100000, /* virtual interrupt pending */ ++ EF_ID = 0x00200000, /* id */ ++}; ++ + #ifndef __KERNEL__ + + struct pt_regs { +--- /dev/null ++++ b/arch/x86/kdb/ChangeLog +@@ -0,0 +1,262 @@ ++2008-11-26 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc6-x86-1. ++ ++2008-11-12 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc4-x86-1. ++ ++2008-11-04 Jay Lan ++ ++ * kdb-v4.4-2.6.28-rc3-x86-1. ++ ++2008-10-28 Jay Lan ++ ++ * "Commandeer vector 0xfe for KDB_VECTOR", version 2. ++ Cliff Wickman ++ * kdb-v4.4-2.6.28-rc2-x86-2. ++ ++2008-10-27 Jay Lan ++ ++ * Commandeer vector 0xfe for KDB_VECTOR, ++ Cliff Wickman ++ * Fix KDB-KDUMP problems on IBM xSeries, ++ Bernhard Walle , Jay Lan ++ * Fix crash when panic() from task context, ++ Bernhard Walle ++ * kdb-v4.4-2.6.28-rc2-x86-1. ++ ++2008-10-20 Jay Lan ++ ++ * kdb-v4.4-2.6.27-x86-1. ++ ++2008-09-30 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc8-x86-1. ++ ++2008-09-22 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc7-x86-1. ++ ++2008-09-03 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc5-x86-1. ++ ++2008-08-19 Jay Lan ++ ++ * kdb-v4.4-2.6.27-rc3-x86-1. ++ ++2008-08-14 Jay Lan ++ ++ * Support 'kdump' command to take a kdump vmcore from KDB, ++ Dan Aloni (da-x@monatomic.org), ++ Jason Xiao (jidong.xiao@gmail.com), ++ Jay Lan (jlan@sgi.com) ++ * kdb-v4.4-2.6.27-rc2-x86-2. ++ ++2008-08-06 Jay Lan ++ ++ * Fix up the NULL pointer deference issue in ohci_kdb_poll_char, ++ Jason Xiao ++ * Backtrace on x86_64 and i386 were incomplete since 2.6.27-rc2. ++ * kdb-v4.4-2.6.27-rc2-x86-1. ++ ++2008-07-18 Jay Lan ++ ++ * support Hardware Breakpoint (bph/bpha) commands ++ IA64: Greg Banks ++ X86: Konstantin Baydarov ++ * kdb-v4.4-2.6.26-x86-2. ++ ++2008-07-14 Jay Lan ++ ++ * kdb-v4.4-2.6.26-x86-1. ++ ++2008-07-11 Jay Lan ++ ++ * New commands and some fixups and enhancements, ++ Joe Korty ++ John Blackwood ++ Jim Houston ++ - Use the non-sleeping copy_from_user_atomic. ++ - Enhance kdb_cmderror diagnostic output. ++ - Expand the KDB 'duplicate command' error message. ++ - Touch NMI watchdog in various KDB busy-loops. ++ - Support IMB HS20 Blade 8843 platform. ++ - Display exactly which cpus needed an NMI to get them into kdb. ++ - Better document that kdb's 'ps A' command can be used to show ++ _all_ processes and threads ++ - Suppress KDB boottime INFO messages if quiet boot. ++ - Add a KDB breakpoint to the OOPs path. ++ - Add CONFIG_DISCONTIGMEM support to kdbm_memmap. ++ - Extend the KDB task command to handle CONFIG_NUMA fields. ++ - Extend the KDB vm command to support NUMA stuff. ++ - Create the KDB mempolicy command. ++ - Create a pgdat command for KDB. ++ - Fix a hang on boot on some i386 systems. ++ * kdb-v4.4-2.6.26-rc9-x86-1. ++ ++2008-06-30 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc8-x86-1. ++ ++2008-06-25 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc7-x86-1. ++ ++2008-06-06 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc5-x86-1. ++ ++2008-05-30 Jay Lan ++ ++ * kdb-v4.4-2.6.26-rc4-x86-1. ++ ++2008-05-20 Jay Lan ++ ++ * Merged and to . ++ * Merged and to ++ . ++ * kdb-v4.4-2.6.26-rc3-x86-1. ++ ++2008-05-15 Jay Lan ++ ++ * Fixed the i386 backtrace problem where KDB failed to find stacks ++ in the kernel space. ++ * kdb-v4.4-2.6.26-rc1-x86-3. ++ ++2008-05-14 Jay Lan ++ ++ * Fixed a bug that bb_all scans only odd number entries of kallsyms. ++ * kdb-v4.4-2.6.26-rc1-x86-2. ++ ++2008-05-13 Jay Lan ++ ++ * Known problem: backtrace for i386 is broken since 2.6.25-rc1. ++ * kdb-v4.4-2.6.26-rc1-x86-1. ++ ++2008-05-13 Jay Lan ++ ++ * Known problem: backtrace for i386 is broken since 2.6.25-rc1. ++ * Fixed a couple of x86_64 problems: ++ - "iret_label" are replaced by "irq_return". ++ - bb1 failure on ia32_sysenter_target() & ia32_cstar_target() ++ * kdb-v4.4-2.6.25-x86-2. ++ ++2008-04-17 Jay Lan ++ ++ * Known problem: backtrace for i386 is broken since 2.6.25-rc1. ++ * kdb-v4.4-2.6.25-x86-1. ++ ++2008-03-19 Jay Lan ++ ++ * i386: systenter_entry was replaced with ia32_sysenter_target since ++ 2.6.25-rc1, Jay Lan ++ * Known problem: backtrace for i386 is broken since 2.6.25-rc1. ++ * kdb-v4.4-2.6.25-rc6-x86-2. ++ ++2008-03-16 Jay Lan ++ ++ * Known problem: backtrace for i386 is broken since 2.6.25-rc1. ++ * kdb-v4.4-2.6.25-rc6-x86-1. ++ ++2008-03-03 Jay Lan ++ ++ * Known problem: backtrace for i386 is broken since 2.6.25-rc1. ++ * kdb-v4.4-2.6.25-rc3-x86-1. ++ ++2008-02-26 Jay Lan ++ ++ * remove 'fastcall' from kdb code. ++ * Known problem: backtrace for i386 is broken since 2.6.25-rc1. ++ * kdb-v4.4-2.6.25-rc2-x86-1. ++ ++2008-02-19 Jay Lan ++ ++ * Known problem: backtrace for i386 is broken. ++ * kdb-v4.4-2.6.25-rc1-x86-1. ++ ++2008-02-01 Jay Lan ++ ++ * Backed out USB UHCI support since it caused dropped characters and ++ broke OHCI. ++ * Restored "archkdbcommon" commands for x86. It was lost at the x86 ++ merge. ++ * Detecting if the HC was "busy", Aaron Young ++ * kdb-v4.4-2.6.24-x86-2. ++ ++2008-01-29 Jay Lan ++ ++ * kdb-v4.4-2.6.24-x86-1. ++ ++2008-01-22 Jay Lan ++ ++ * USB UHCI kdb support, Konstantin Baydarov ++ * kdb-v4.4-2.6.24-rc8-x86-3. ++ ++2008-01-18 Jay Lan ++ ++ * USB EHCI kdb support, Aaron Young ++ * kdb-v4.4-2.6.24-rc8-x86-2. ++ ++2008-01-18 Jay Lan ++ ++ * kdb-v4.4-2.6.24-rc8-x86-1. ++ ++2008-01-09 Jay Lan ++ ++ * Merge arch/x86/kdb/kdba_io_64.c and arch/x86/kdb/kdba_io_32.c to ++ arch/x86/kdb/kdba_io.c ++ * Merge arch/x86/kdb/kdba_id_64.c and arch/x86/kdb/kdba_id_32.c to ++ arch/x86/kdb/kdba_id.c ++ * Merge arch/x86/kdb/pc_keyb_64.h and arch/x86/kdb/pc_keyb_32.h to ++ arch/x86/kdb/pc_keyb.h ++ * kdb-v4.4-2.6.24-rc7-x86-2. ++ ++2008-01-07 Jay Lan ++ ++ * kdb-v4.4-2.6.24-rc7-x86-1. ++ ++2007-12-21 Jay Lan ++ ++ * Renamed kdb/kdba_bt_x86.c to arch/x86/kdba_bt.c. ++ * Find gcc options 'no-optimize-sibling-calls' & 'regparm' from ++ $(KBUILD_CFLAGS) in arch/x86/kdb/Makefile_{32,64}. We used to ++ get them from $(CFLAGS). ++ * Default regparm to 3 on x86_32 if not defined. ++ * kdb v4.4-2.6.24-rc6-x86-1. ++ ++2007-12-12 Jay Lan ++ ++ * Fixed a Makefile_32 error. ++ * kdb v4.4-2.6.24-rc5-x86-1. ++ ++2007-12-05 Jay Lan ++ ++ * Fixed a 'sysctl table check failed' problem. ++ * kdb v4.4-2.6.24-rc4-x86-1. ++ ++2007-11-26 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc3-x86-1. ++ ++2007-11-13 Jay Lan ++ ++ * Back ported "New KDB USB interface" from Aaron Young in ++ v4.4-2.6.23-{i386,x86_64}-2 to 2.6.24 kdb patchset. ++ * Fixed a make problem at arch/x86/Makefile_{32,64}. ++ * kdb v4.4-2.6.24-rc2-x86-2. ++ ++2007-11-12 Jay Lan ++ ++ * kdb v4.4-2.6.24-rc2-x86-1. ++ ++2007-11-09 Jay Lan ++ ++ * Rebase to 2.6.24-rc1 kernel ++ * - merged kdb-v4.4-2.6.23-i386-1 and kdb-v4.4-2.6.23-x86_64-1 ++ * into kdb-v4.4-2.6.24-rc1-x86-1 ++ * - Fields "done", "sglist_len", and "pid" are removed from ++ * struct scsi_cmnd. Thus, these fields are no longer displayed ++ * on "sc" command. ++ * kdb v4.4-2.6.24-rc1-x86-1. +--- /dev/null ++++ b/arch/x86/kdb/ChangeLog_32 +@@ -0,0 +1,865 @@ ++2007-11-08 Jay Lan ++ ++ * New KDB USB interface, Aaron Young ++ * 1. This patch allows KDB to work with any Host Contoller driver ++ * and call the correct HC driver poll routine (as long as the ++ * HC driver provides a .kdb_poll_char routine via it's ++ * associated hc_driver struct). ++ * 2. Hotplugged keyboards are now recognized by KDB. ++ * 3. Currently KDB can only make use of 1 USB type keyboard. ++ * New code can handle up to 8 attached keyboards - input is ++ * multiplexed from all of them while in kdb. ++ * kdb v4.4-2.6.23-common-2. ++ ++2007-10-24 Jay Lan ++ ++ * kdb v4.4-2.6.23-i386-1. ++ ++2007-09-26 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc8-i386-1. ++ ++2007-09-21 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc7-i386-1. ++ ++2007-09-12 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc6-i386-1. ++ ++2007-09-06 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc5-i386-1. ++ ++2007-08-30 Keith Owens ++ ++ * New i386/x86_64 backtrace requires that kdb_save_running() does not ++ exit until after kdb_main_loop() has completed. ++ * kdb v4.4-2.6.23-rc4-i386-2. ++ ++2007-08-30 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc4-i386-1. ++ ++2007-08-24 Keith Owens ++ ++ * kdb v4.4-2.6.23-rc3-i386-1. ++ ++2007-08-07 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc2-i386-1. ++ ++2007-07-31 Keith Owens ++ ++ * Delete obsolete kdba_bt.c. ++ * kdb v4.4-2.6.23-rc1-i386-2. ++ ++2007-07-30 Keith Owens ++ ++ * kdb v4.4-2.6.23-rc1-i386-1. ++ ++2007-07-26 Keith Owens ++ ++ * New x86 backtrace code. ++ * kdb v4.4-2.6.22-i386-2. ++ ++2007-07-09 Keith Owens ++ ++ * kdb v4.4-2.6.22-i386-1. ++ ++2007-07-02 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc7-i386-1. ++ ++2007-06-20 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc5-i386-1. ++ ++2007-06-08 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc4-i386-1. ++ ++2007-05-28 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc3-i386-1. ++ ++2007-05-22 Keith Owens ++ ++ * Register KDBENTER_VECTOR early on the boot cpu. ++ * kdb v4.4-2.6.22-rc2-i386-2. ++ ++2007-05-22 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc2-i386-1. ++ ++2007-05-22 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc1-i386-1. ++ ++2007-05-17 Keith Owens ++ ++ * Update dumpregs comments for rdmsr and wrmsr commands. ++ Bernardo Innocenti. ++ * kdb v4.4-2.6.21-i386-3. ++ ++2007-05-15 Keith Owens ++ ++ * Change kdba_late_init to kdba_arch_init so KDB_ENTER() can be used ++ earlier. ++ * kdb v4.4-2.6.21-i386-2. ++ ++2007-04-29 Keith Owens ++ ++ * kdb v4.4-2.6.21-i386-1. ++ ++2007-04-16 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc7-i386-1. ++ ++2007-04-10 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc6-i386-1. ++ ++2007-04-02 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc5-i386-1. ++ ++2007-03-19 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc4-i386-1. ++ ++2007-03-14 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc3-i386-1. ++ ++2007-03-14 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc2-i386-1. ++ ++2007-03-01 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc1-i386-1. ++ ++2007-03-01 Keith Owens ++ ++ * Remove sparse warnings. ++ * kdb v4.4-2.6.20-i386-3. ++ ++2007-02-16 Keith Owens ++ ++ * Initialise variable bits of struct disassemble_info each time. ++ * kdb v4.4-2.6.20-i386-2. ++ ++2007-02-06 Keith Owens ++ ++ * kdb v4.4-2.6.20-i386-1. ++ ++2007-02-01 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc7-i386-1. ++ ++2007-01-08 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc4-i386-1. ++ ++2007-01-02 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc3-i386-1. ++ ++2006-12-20 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc1-i386-1. ++ ++2006-11-30 Keith Owens ++ ++ * kdb v4.4-2.6.19-i386-1. ++ ++2006-11-27 Keith Owens ++ ++ * Only use VT keyboard if the command line allows it and ACPI indicates ++ that there is an i8042. ++ * kdb v4.4-2.6.19-rc6-i386-2. ++ ++2006-11-20 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc6-i386-1. ++ ++2006-11-09 Keith Owens ++ ++ * Change kdb() to fastcall. ++ * Add unwind info to kdb_call(). Steve Lord. ++ * Only use VT console if the command line allows it. ++ * kdb v4.4-2.6.19-rc5-i386-2. ++ ++2006-11-08 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc5-i386-1. ++ ++2006-11-01 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc4-i386-1. ++ ++2006-10-24 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc3-i386-1. ++ ++2006-10-24 Keith Owens ++ ++ * Remove redundant regs and envp parameters. ++ * kdb v4.4-2.6.19-rc2-i386-2. ++ ++2006-10-18 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc2-i386-1. ++ ++2006-10-11 Keith Owens ++ ++ * Move kdbm_x86.c from the i386 to the common KDB patch. ++ * Make the KDBENTER_VECTOR an interrupt gate instead of a trap gate, it ++ simplifies the code and disables interrupts on KDBENTER(). ++ * Exclude the KDBENTER_VECTOR from irq assignment. ++ * kdb v4.4-2.6.19-rc1-i386-2. ++ ++2006-10-09 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc1-i386-1. ++ ++2006-10-06 Keith Owens ++ ++ * Remove #include ++ * kdb v4.4-2.6.18-i386-2. ++ ++2006-09-20 Keith Owens ++ ++ * kdb v4.4-2.6.18-i386-1. ++ ++2006-09-15 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc7-i386-1. ++ ++2006-08-30 Keith Owens ++ ++ * Add warning for problems when following alternate stacks. ++ * kdb v4.4-2.6.18-rc5-i386-3. ++ ++2006-08-29 Keith Owens ++ ++ * Rewrite all backtrace code. ++ * kdb v4.4-2.6.18-rc5-i386-2. ++ ++2006-08-28 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc5-i386-1. ++ ++2006-08-08 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc4-i386-1. ++ ++2006-08-04 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc3-i386-1. ++ ++2006-07-18 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc2-i386-1. ++ ++2006-07-12 Keith Owens ++ ++ * Remove dead KDB_REASON codes. ++ * sparse cleanups. ++ * kdb v4.4-2.6.18-rc1-i386-2. ++ ++2006-07-07 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc1-i386-1. ++ ++2006-07-04 Keith Owens ++ ++ * Make KDB rendezvous on i386 a two stage approach. ++ * Clean up generation of KDB interrupt code. ++ * Move smp_kdb_stop() and smp_kdb_interrupt() to kdbasupport.c. ++ * Move setting of interrupt traps to kdbasupport.c. ++ * Remove KDB hooks from arch/i386/kernel smp.c, smpboot.c, i8259.c, ++ io_apic.c. ++ * Add KDB_REASON_CPU_UP support. ++ * Move per cpu setup to kdba_cpu_up(). ++ * Rework support for 4K stacks to make backtrace more accurate. ++ * Add BTSP option to get the full backtrace, including kdb routines. ++ * Delete kdba_enable_mce, architectures now do their own setup. ++ * Delete kdba_enable_lbr, kdba_disable_lbr, kdba_print_lbr, ++ page_fault_mca. Only ever implemented on x86, difficult to maintain ++ and rarely used in the field. ++ * Replace #ifdef KDB_HAVE_LONGJMP with #ifdef kdba_setjmp. ++ * kdb v4.4-2.6.17-i386-2. ++ ++2006-06-19 Keith Owens ++ ++ * kdb v4.4-2.6.17-i386-1. ++ ++2006-05-25 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc5-i386-1. ++ ++2006-05-15 Keith Owens ++ ++ * Refresh bfd related files from binutils 2.16.91.0.2. ++ * kdb v4.4-2.6.17-rc4-i386-2. ++ ++2006-05-12 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc4-i386-1. ++ ++2006-04-28 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc3-i386-1. ++ ++2006-04-22 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc2-i386-1. ++ ++2006-04-11 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc1-i386-1. ++ ++2006-03-30 Keith Owens ++ ++ * Change CONFIG_LKCD to CONFIG_LKCD_DUMP. ++ * kdb v4.4-2.6.16-i386-3. ++ ++2006-03-24 Keith Owens ++ ++ * Define a dummy kdba_wait_for_cpus(). ++ * kdb v4.4-2.6.16-i386-2. ++ ++2006-03-21 Keith Owens ++ ++ * kdb v4.4-2.6.16-i386-1. ++ ++2006-03-14 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc6-i386-1. ++ ++2006-02-28 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc5-i386-1. ++ ++2006-02-20 Nathan Scott ++ ++ * kdb v4.4-2.6.16-rc4-i386-1. ++ ++2006-02-06 Keith Owens ++ ++ * Change CONFIG_CRASH_DUMP to CONFIG_LKCD. ++ * kdb v4.4-2.6.16-rc2-i386-2. ++ ++2006-02-06 Keith Owens ++ ++ * kdb v4.4-2.6.16-rc2-i386-1. ++ ++2006-01-18 Keith Owens ++ ++ * kdb v4.4-2.6.16-rc1-i386-1. ++ ++2006-01-08 Keith Owens ++ ++ * Add DIE_KDEBUG_ENTER and DIE_KDEBUG_LEAVE to notify_die. ++ * kdb v4.4-2.6.15-i386-2. ++ ++2006-01-04 Keith Owens ++ ++ * Remove some inlines and the last vestige of CONFIG_NUMA_REPLICATE. ++ * Read the keyboard acknowledgment after sending a character. SuSE ++ Bugzilla 60240. ++ * kdb v4.4-2.6.15-i386-1. ++ ++2005-12-25 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc7-i386-1. ++ ++2005-12-20 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc6-i386-1. ++ ++2005-12-05 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc5-i386-1. ++ ++2005-12-02 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc4-i386-1. ++ ++2005-11-30 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc3-i386-1. ++ ++2005-11-21 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc2-i386-1. ++ ++2005-11-15 Keith Owens ++ ++ * kdb v4.4-2.6.15-rc1-i386-1. ++ ++2005-10-28 Keith Owens ++ ++ * kdb v4.4-2.6.14-i386-1. ++ ++2005-10-21 Keith Owens ++ ++ * kdb v4.4-2.6.14-rc5-i386-1. ++ ++2005-10-11 Keith Owens ++ ++ * kdb v4.4-2.6.14-rc4-i386-1. ++ ++2005-10-04 Keith Owens ++ ++ * kdb v4.4-2.6.14-rc3-i386-1. ++ ++2005-09-21 Keith Owens ++ ++ * Support kdb_current_task in register display and modify commands. ++ * kdb v4.4-2.6.14-rc2-i386-1. ++ ++2005-09-20 Keith Owens ++ ++ * Remove use of __STDC_VERSION__ in ansidecl.h. ++ * kdb v4.4-2.6.14-rc1-i386-1. ++ ++2005-08-29 Keith Owens ++ ++ * kdb v4.4-2.6.13-i386-1. ++ ++2005-08-24 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc7-i386-1. ++ ++2005-08-08 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc6-i386-1. ++ ++2005-08-02 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc5-i386-1. ++ ++2005-07-30 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc4-i386-1. ++ ++2005-07-22 Keith Owens ++ ++ * Compile fix for kprobes. ++ * kdb v4.4-2.6.13-rc3-i386-2. ++ ++2005-07-19 Keith Owens ++ ++ * Add support for USB keyboard (OHCI only). Aaron Young, SGI. ++ * kdb v4.4-2.6.13-rc3-i386-1. ++ ++2005-07-08 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc2-i386-1. ++ ++2005-07-01 Keith Owens ++ ++ * kdb v4.4-2.6.13-rc1-i386-1. ++ ++2005-06-19 Keith Owens ++ ++ * gcc 4 compile fix, remove extern kdb_hardbreaks. Steve Lord. ++ * kdb v4.4-2.6.12-i386-2. ++ ++2005-06-18 Keith Owens ++ ++ * kdb v4.4-2.6.12-i386-1. ++ ++2005-06-08 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc6-i386-1. ++ ++2005-05-25 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc5-i386-1. ++ ++2005-05-08 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc4-i386-1. ++ ++2005-04-21 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc3-i386-1. ++ ++2005-04-06 Keith Owens ++ ++ * kdb v4.4-2.6.12-rc2-i386-1. ++ ++2005-03-29 Keith Owens ++ ++ * Replace __copy_to_user with __copy_to_user_inatomic. ++ * kdb v4.4-2.6.12-rc1-i386-1. ++ ++2005-03-08 Keith Owens ++ ++ * Coexistence patches for lkcd. ++ * kdb v4.4-2.6.11-i386-2. ++ ++2005-03-03 Keith Owens ++ ++ * kdb v4.4-2.6.11-i386-1. ++ ++2005-02-14 Keith Owens ++ ++ * kdb v4.4-2.6.11-rc4-i386-1. ++ ++2005-02-08 Keith Owens ++ ++ * kdb v4.4-2.6.11-rc3-bk4-i386-1. ++ ++2005-02-03 Keith Owens ++ ++ * kdb v4.4-2.6.11-rc3-i386-1. ++ ++2005-01-27 Keith Owens ++ ++ * kdb v4.4-2.6.11-rc2-i386-1. ++ ++2005-01-12 Keith Owens ++ ++ * kdb v4.4-2.6.11-rc1-i386-1. ++ ++2004-12-25 Keith Owens ++ ++ * kdb v4.4-2.6.10-i386-1. ++ ++2004-12-07 Keith Owens ++ ++ * kdb v4.4-2.6.10-rc3-i386-1. ++ ++2004-11-23 Keith Owens ++ ++ * Coexist with asmlinkage/fastcall changes. ++ * kdb v4.4-2.6.10-rc2-i386-1. ++ ++2004-10-29 Keith Owens ++ ++ * Handle change defintions for hard and soft irq context. ++ * Make stack switch in kdb backtrace look more like the oops output. ++ * kdb v4.4-2.6.10-rc1-i386-1. ++ ++2004-10-19 Keith Owens ++ ++ * kdb v4.4-2.6.9-i386-1. ++ ++2004-10-12 Keith Owens ++ ++ * kdb v4.4-2.6.9-rc4-i386-1. ++ ++2004-10-01 Keith Owens ++ ++ * kdb v4.4-2.6.9-rc3-i386-1. ++ ++2004-09-30 Keith Owens ++ ++ * Add stackdepth command. ++ * Handle backtrace with separate soft and hard irq stacks ++ (CONFIG_4KSTACKS). ++ * Work around RESTORE_ALL macro, which can only be used once. ++ * Export kdba_dumpregs. Bryan Cardillo, UPenn. ++ * kdb v4.4-2.6.9-rc2-i386-2. ++ ++2004-09-14 Keith Owens ++ ++ * kdb v4.4-2.6.9-rc2-i386-1. ++ ++2004-08-27 Keith Owens ++ ++ * kdb v4.4-2.6.9-rc1-i386-1. ++ ++2004-08-14 Keith Owens ++ ++ * kdb v4.4-2.6.8-i386-1. ++ ++2004-08-12 Keith Owens ++ ++ * kdb v4.4-2.6.8-rc4-i386-1. ++ ++2004-08-04 Keith Owens ++ ++ * kdb v4.4-2.6.8-rc3-i386-1. ++ ++2004-07-18 Keith Owens ++ ++ * kdb v4.4-2.6.8-rc2-i386-1. ++ ++2004-07-12 Keith Owens ++ ++ * kdb v4.4-2.6.8-rc1-i386-1. ++ ++2004-06-16 Keith Owens ++ ++ * kdb v4.4-2.6.7-i386-1. ++ ++2004-06-10 Keith Owens ++ ++ * kdb v4.4-2.6.7-rc3-i386-1. ++ ++2004-06-09 Keith Owens ++ ++ * Namespace clean up. Mark code/variables as static when it is only ++ used in one file, delete dead code/variables. ++ * kdb v4.4-2.6.7-rc2-i386-3. ++ ++2004-06-08 Keith Owens ++ ++ * Whitespace clean up, no code changes. ++ * kdb v4.4-2.6.7-rc2-i386-2. ++ ++2004-06-07 Keith Owens ++ ++ * Force KALLSYMS and KALLSYMS_ALL for CONFIG_KDB. ++ * kdb v4.4-2.6.7-rc2-i386-1. ++ ++2004-06-06 Keith Owens ++ ++ * Correct Kconfig help text. ++ * Coexist with CONFIG_REGPARM. ++ * Add standard archkdb commands. ++ * Move kdb_{get,put}userarea_size definitions to linux/kdb.h. ++ * kdb v4.4-2.6.6-i386-2. ++ ++2004-05-23 Keith Owens ++ ++ * Move bfd.h and ansidecl.h from arch/$(ARCH)/kdb to include/asm-$(ARCH). ++ * Update copyright notices. ++ * kdb v4.4-2.6.6-i386-1. ++ ++2004-05-10 Keith Owens ++ ++ * kdb v4.3-2.6.6-i386-1. ++ ++2004-05-06 Keith Owens ++ ++ * kdb v4.3-2.6.6-rc3-i386-1. ++ ++2004-05-06 Keith Owens ++ ++ * kdb v4.3-2.6.6-rc2-i386-1. ++ ++2004-04-30 Keith Owens ++ ++ * kdb v4.3-2.6.6-rc1-i386-1. ++ ++2004-04-05 Keith Owens ++ ++ * kdb v4.3-2.6-5-i386-1. ++ ++2004-02-29 Keith Owens ++ ++ * kdb v4.3-2.6-4-rc1-i386-1. ++ ++2004-02-18 Keith Owens ++ ++ * kdb v4.3-2.6-3-i386-1. ++ ++2004-02-17 Keith Owens ++ ++ * Pick up changes from Jim Houston for 2.6. ++ * Sync with kdb v4.3-2.4.25-rc1-i386-1. ++ * Adjust for LDT changes in i386 mainline. ++ * Convert longjmp buffers from static to dynamic allocation, for large ++ cpu counts. ++ * Do not use USB keyboard if it has not been probed. ++ * Do not print section data, 2.6 kallsyms does not support sections :(. ++ * kdb v4.3-2.6-3-rc3-i386-1. ++ ++2003-08-29 Keith Owens ++ ++ * kdb v4.3-2.4.22-i386-1. ++ ++2003-08-05 Keith Owens ++ ++ * Remove duplicate setting of trap for machine_check. ++ * Only reset keyboard when CONFIG_VT_CONSOLE is defined. ++ ++2003-07-27 Keith Owens ++ ++ * kdb v4.3-2.4.22-pre8-i386-5. ++ ++2003-07-20 Keith Owens ++ ++ * Remove compile warning on x86 commands. ++ * kdb v4.3-2.4.21-i386-5. ++ ++2003-07-08 Keith Owens ++ ++ * Add new x86 commands - rdv, gdt, idt, ldt, ldtp, ptex. ++ Vamsi Krishna S., IBM. ++ * kdb v4.3-2.4.21-i386-4. ++ ++2003-07-01 Keith Owens ++ ++ * Convert kdba_find_return() to two passes to reduce false positives. ++ * Correct jmp disp8 offset calculation for out of line lock code. ++ * Use NMI for kdb IPI in clustered APIC mode. Sachin Sant, IBM. ++ * kdb v4.3-2.4.21-i386-3. ++ ++2003-06-23 Keith Owens ++ ++ * Sync with XFS 2.4.21 tree. ++ * kdb v4.3-2.4.21-i386-2. ++ ++2003-06-20 Keith Owens ++ ++ * kdb v4.3-2.4.21-i386-1. ++ ++2003-06-20 Keith Owens ++ ++ * Add CONFIG_KDB_CONTINUE_CATASTROPHIC. ++ * Correct KDB_ENTER() definition. ++ * kdb v4.3-2.4.20-i386-1. ++ ++2003-05-02 Keith Owens ++ ++ * Add kdba_fp_value(). ++ * Limit backtrace size to catch loops. ++ * Add read/write access to user pages. Vamsi Krishna S., IBM ++ * Clean up USB keyboard support. Steven Dake. ++ * kdb v4.2-2.4.20-i386-1. ++ ++2003-04-04 Keith Owens ++ ++ * Workarounds for scheduler bugs. ++ * kdb v4.1-2.4.20-i386-1. ++ ++2003-03-16 Keith Owens ++ ++ * Each cpu saves its state as it enters kdb or before it enters code ++ which cannot call kdb, converting kdb from a pull to a push model. ++ * Clean up kdb interaction with CONFIG_SERIAL_CONSOLE. ++ * Removal of special cases for i386 backtrace from common code ++ simplifies the architecture code. ++ * Add command to dump i386 struct pt_regs. ++ * kdb v4.0-2.4.20-i386-1. ++ ++2003-02-03 Keith Owens ++ ++ * Register kdb commands early. ++ * Handle KDB_ENTER() when kdb=off. ++ * Optimize __kdba_getarea_size when width is a constant. ++ * Decode oops via kallsyms if it is available. ++ * Update copyright notices to 2003. ++ * Handle call *disp32(%reg) in backtrace. ++ * Correct keyboard freeze. Ashish Kalra. ++ * Add command history and editing. Sonic Zhang. ++ * kdb_toggleled is conditional on KDB_BLINK_LED. Bernhard Fischer. ++ * Allow tab on serial line for symbol completion. ++ * Ignore KDB_ENTER() when kdb is already running. ++ * kdb v3.0-2.4.20-i386-1. ++ ++2002-11-29 Keith Owens ++ ++ * Upgrade to 2.4.20. ++ * kdb v2.5-2.4.20-i386-1. ++ ++2002-11-14 Keith Owens ++ ++ * Upgrade to 2.4.20-rc1. ++ * kdb v2.5-2.4.20-rc1-i386-1. ++ ++2002-11-14 Keith Owens ++ ++ * General clean up of handling for breakpoints and single stepping over ++ software breakpoints. ++ * Accept ff 1x as well as ff dx for call *(%reg) in backtrace. ++ * kdb v2.5-2.4.19-i386-1. ++ ++2002-11-01 Keith Owens ++ ++ * Prevent SMP IRQ overwriting KDB_ENTER(). ++ * kdb v2.4-2.4.19-i386-2. ++ ++2002-10-31 Keith Owens ++ ++ * Avoid KDB_VECTOR conflict with DUMP_VECTOR. ++ * Remove kdb_eframe_t. ++ * Sanity check if we have pt_regs. ++ * Remove kdba_getcurrentframe(). ++ * Reinstate missing nmi_watchdog/kdb hook. ++ * kdb v2.4-2.4.19-i386-1. ++ ++2002-10-17 Keith Owens ++ ++ * Correct compile with CONFIG_VT_CONSOLE=n. ++ * kdb v2.3-2.4.19-i386-5. ++ ++2002-10-04 Keith Owens ++ ++ * Add USB keyboard option. ++ * Minimize differences between patches for 2.4 and 2.5 kernels. ++ * kdb v2.3-2.4.19-i386-4. ++ ++2002-08-10 Keith Owens ++ ++ * Replace kdb_port with kdb_serial to support memory mapped I/O. ++ Note: This needs kdb v2.3-2.4.19-common-2 or later. ++ * kdb v2.3-2.4.19-i386-3. ++ ++2002-08-09 Keith Owens ++ ++ * Use -fno-optimize-sibling-calls for kdb if gcc supports it. ++ * .text.lock does not consume an activation frame. ++ * kdb v2.3-2.4.19-i386-2. ++ ++2002-08-07 Keith Owens ++ ++ * Upgrade to 2.4.19. ++ * Remove individual SGI copyrights, the general SGI copyright applies. ++ * New .text.lock name. Hugh Dickins. ++ * Set KERNEL_CS in kdba_getcurrentframe. Hugh Dickins. ++ * Clean up disassembly layout. Hugh Dickins, Keith Owens. ++ * Replace hard coded stack size with THREAD_SIZE. Hugh Dickins. ++ * Better stack layout on bt with no frame pointers. Hugh Dickins. ++ * Make i386 IO breakpoints (bpha
IO) work again. ++ Martin Wilck, Keith Owens. ++ * Remove fixed KDB_MAX_COMMANDS size. ++ * Add set_fs() around __copy_to_user on kernel addresses. ++ Randolph Chung. ++ * Position i386 for CONFIG_NUMA_REPLICATE. ++ * kdb v2.3-2.4.19-i386-1. ++ ++2002-07-09 Keith Owens ++ ++ * Upgrade to 2.4.19-rc1. ++ ++2002-06-14 Keith Owens ++ ++ * Upgrade to 2.4.19-pre10. ++ * kdb v2.1-2.4.19-pre10-i386-1. ++ ++2002-04-09 Keith Owens ++ ++ * Upgrade to 2.4.19-pre6. ++ * kdb v2.1-2.4.19-pre6-i386-1. ++ ++2002-02-26 Keith Owens ++ ++ * Upgrade to 2.4.18. ++ * kdb v2.1-2.4.18-i386-1. ++ ++2002-01-18 Keith Owens ++ ++ * Use new kdb_get/put functions. ++ * Define kdba_{get,put}area_size functions for i386. ++ * Remove over-engineered dblist callback functions. ++ * Correctly handle failing call disp32 in backtrace. ++ * Remove bp_instvalid flag, redundant code. ++ * Remove dead code. ++ * kdb v2.1-2.4.17-i386-1. ++ ++2002-01-04 Keith Owens ++ ++ * Sync xfs <-> kdb i386 code. ++ ++2001-12-22 Keith Owens ++ ++ * Split kdb for i386 as kdb v2.0-2.4.17-i386-1. +--- /dev/null ++++ b/arch/x86/kdb/ChangeLog_64 +@@ -0,0 +1,447 @@ ++2007-11-08 Jay Lan ++ ++ * New KDB USB interface, Aaron Young ++ * 1. This patch allows KDB to work with any Host Contoller driver ++ * and call the correct HC driver poll routine (as long as the ++ * HC driver provides a .kdb_poll_char routine via it's ++ * associated hc_driver struct). ++ * 2. Hotplugged keyboards are now recognized by KDB. ++ * 3. Currently KDB can only make use of 1 USB type keyboard. ++ * New code can handle up to 8 attached keyboards - input is ++ * multiplexed from all of them while in kdb. ++ * kdb v4.4-2.6.23-common-2. ++ ++2007-10-24 Jay Lan ++ ++ * kdb v4.4-2.6.23-x86_64-1. ++ ++2007-09-26 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc8-x86_64-1. ++ ++2007-09-21 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc7-x86_64-1. ++ ++2007-09-12 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc6-x86_64-1. ++ ++2007-09-06 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc5-x86_64-1. ++ ++2007-08-30 Keith Owens ++ ++ * New i386/x86_64 backtrace requires that kdb_save_running() does not ++ exit until after kdb_main_loop() has completed. ++ * kdb v4.4-2.6.23-rc4-x86_64-2. ++ ++2007-08-30 Jay Lan ++ ++ * kdb v4.4-2.6.23-rc4-x86_64-1. ++ ++2007-08-24 Keith Owens ++ ++ * kdb v4.4-2.6.23-rc3-x86_64-1. ++ ++2007-08-07 Jay Lan ++ ++ * v4.4-2.6.23-rc2-x86_64-1. ++ ++2007-07-31 Keith Owens ++ ++ * Delete obsolete kdba_bt.c. ++ * kdb v4.4-2.6.23-rc1-x86_64-2. ++ ++2007-07-30 Keith Owens ++ ++ * kdb v4.4-2.6.23-rc1-x86_64-1. ++ ++2007-07-26 Keith Owens ++ ++ * New x86 backtrace code. ++ * kdb v4.4-2.6.22-x86_64-2. ++ ++2007-07-09 Keith Owens ++ ++ * kdb v4.4-2.6.22-x86_64-1. ++ ++2007-07-02 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc7-x86_64-1. ++ ++2007-06-25 Keith Owens ++ ++ * Hook into DIE_NMIWATCHDOG. ++ * kdb v4.4-2.6.22-rc5-x86_64-2. ++ ++2007-06-20 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc5-x86_64-1. ++ ++2007-06-08 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc4-x86_64-1. ++ ++2007-05-28 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc3-x86_64-1. ++ ++2007-05-22 Keith Owens ++ ++ * Register KDBENTER_VECTOR early on the boot cpu. ++ * kdb v4.4-2.6.22-rc2-x86_64-2. ++ ++2007-05-22 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc2-x86_64-1. ++ ++2007-05-22 Keith Owens ++ ++ * kdb v4.4-2.6.22-rc1-x86_64-1. ++ ++2007-05-17 Keith Owens ++ ++ * Update dumpregs comments for rdmsr and wrmsr commands. ++ Bernardo Innocenti. ++ * kdb v4.4-2.6.21-x86_64-3. ++ ++2007-05-15 Keith Owens ++ ++ * Change kdba_late_init to kdba_arch_init so KDB_ENTER() can be used ++ earlier. ++ * kdb v4.4-2.6.21-x86_64-2. ++ ++2007-04-29 Keith Owens ++ ++ * kdb v4.4-2.6.21-x86_64-1. ++ ++2007-04-16 Keith Owens ++ ++ * Select KALLSYMS and KALLSYMS_ALL when KDB is selected. ++ * kdb v4.4-2.6.21-rc7-x86_64-2. ++ ++2007-04-16 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc7-x86_64-1. ++ ++2007-04-10 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc6-x86_64-1. ++ ++2007-04-02 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc5-x86_64-1. ++ ++2007-03-19 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc4-x86_64-1. ++ ++2007-03-14 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc3-x86_64-1. ++ ++2007-03-14 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc2-x86_64-1. ++ ++2007-03-01 Keith Owens ++ ++ * kdb v4.4-2.6.21-rc1-x86_64-1. ++ ++2007-03-01 Keith Owens ++ ++ * Remove sparse warnings. ++ * kdb v4.4-2.6.20-x86_64-3. ++ ++2007-02-16 Keith Owens ++ ++ * Initialise variable bits of struct disassemble_info each time. ++ * kdb v4.4-2.6.20-x86_64-2. ++ ++2007-02-06 Keith Owens ++ ++ * kdb v4.4-2.6.20-x86_64-1. ++ ++2007-02-01 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc7-x86_64-1. ++ ++2007-01-10 Keith Owens ++ ++ * Correct setjmp for the FRAME_POINTER=y case. ++ * Remove duplicate longjmp code for FRAME_POINTER=n/y. ++ * kdb v4.4-2.6.20-rc4-x86_64-2. ++ ++2007-01-08 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc4-x86_64-1. ++ ++2007-01-02 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc3-x86_64-1. ++ ++2006-12-20 Keith Owens ++ ++ * kdb v4.4-2.6.20-rc1-x86_64-1. ++ ++2006-12-07 Keith Owens ++ ++ * Export kdba_dumpregs. ++ * kdb v4.4-2.6.19-x86_64-2. ++ ++2006-11-30 Keith Owens ++ ++ * kdb v4.4-2.6.19-x86_64-1. ++ ++2006-11-27 Keith Owens ++ ++ * Only use VT keyboard if the command line allows it and ACPI indicates ++ that there is an i8042. ++ * kdb v4.4-2.6.19-rc6-x86_64-2. ++ ++2006-11-20 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc6-x86_64-1. ++ ++2006-11-09 Keith Owens ++ ++ * Only use VT console if the command line allows it. ++ * kdb v4.4-2.6.19-rc5-x86_64-2. ++ ++2006-11-08 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc5-x86_64-1. ++ ++2006-11-01 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc4-x86_64-1. ++ ++2006-10-24 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc3-x86_64-1. ++ ++2006-10-24 Keith Owens ++ ++ * Remove redundant regs and envp parameters. ++ * kdb v4.4-2.6.19-rc2-x86_64-2. ++ ++2006-10-18 Keith Owens ++ ++ * kdb v4.4-2.6.19-rc2-x86_64-1. ++ ++2006-10-11 Keith Owens ++ ++ * Make the KDBENTER_VECTOR an interrupt gate instead of a trap gate, it ++ simplifies the code and disables interrupts on KDB_ENTER(). ++ * Exclude the KDBENTER_VECTOR from irq assignment. ++ * Enable KDB_ENTER() again. ++ * kdb v4.4-2.6.19-rc1-x86_64-2. ++ ++2006-10-09 Keith Owens ++ ++ * KDB_ENTER() is getting spurious activations on some x86_64 hardware. ++ Deactivate KDB_ENTER() until it is fixed. ++ * kdb v4.4-2.6.19-rc1-x86_64-1. ++ ++2006-10-06 Keith Owens ++ ++ * Remove #include ++ * kdb v4.4-2.6.18-x86_64-2. ++ ++2006-09-20 Keith Owens ++ ++ * kdb v4.4-2.6.18-x86_64-1. ++ ++2006-09-15 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc7-x86_64-1. ++ ++2006-08-30 Keith Owens ++ ++ * Do not print debugstackptr in cpu_pda, it will be deleted soon. ++ * Add KDB_ENTER(). ++ * Add warning for problems when following alternate stacks. ++ * kdb v4.4-2.6.18-rc5-x86_64-3. ++ ++2006-08-29 Keith Owens ++ ++ * Rewrite all backtrace code. ++ * Add pt_regs and cpu_pda commands. ++ * Include patch to define orig_ist, to be removed once that patch is in ++ the community tree. ++ * kdb v4.4-2.6.18-rc5-x86_64-2. ++ ++2006-08-28 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc5-x86_64-1. ++ ++2006-08-08 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc4-x86_64-1. ++ ++2006-08-04 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc3-x86_64-1. ++ ++2006-07-18 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc2-x86_64-1. ++ ++2006-07-12 Keith Owens ++ ++ * sparse cleanups ++ * kdb v4.4-2.6.18-rc1-x86_64-2. ++ ++2006-07-07 Keith Owens ++ ++ * kdb v4.4-2.6.18-rc1-x86_64-1. ++ ++2006-07-04 Keith Owens ++ ++ * Make KDB rendezvous on x86_64 a two stage approach. ++ * Move smp_kdb_stop() and smp_kdb_interrupt() to kdbasupport.c. ++ * Move setting of interrupt traps to kdbasupport.c. ++ * Add KDB_REASON_CPU_UP support. ++ * Move per cpu setup to kdba_cpu_up(). ++ * Delete kdba_enable_mce, architectures now do their own setup. ++ * Delete kdba_enable_lbr, kdba_disable_lbr, kdba_print_lbr, ++ page_fault_mca. Only ever implemented on x86, difficult to maintain ++ and rarely used in the field. ++ * Replace #ifdef KDB_HAVE_LONGJMP with #ifdef kdba_setjmp. ++ * kdb v4.4-2.6.17-x86_64-2. ++ ++2006-06-19 Keith Owens ++ ++ * kdb v4.4-2.6.17-x86_64-1. ++ ++2006-05-31 Keith Owens ++ ++ * Define arch/x86_64/kdb/kdb_cmds. ++ * kdb v4.4-2.6.17-rc5-x86_64-2. ++ ++2006-05-25 Keith Owens ++ ++ * kdb v4.4-2.6.17-rc5-x86_64-1. ++ ++2006-05-15 Keith Owens ++ ++ * Refresh bfd related files from binutils 2.16.91.0.2. ++ * kdb v4.4-2.6.17-rc4-x86_64-2. ++ ++2006-05-12 Keith Owens ++ ++ * kdb v4.4-2.6-17-rc4-x86_64-1. ++ ++2006-04-22 Keith Owens ++ ++ * kdb v4.4-2.6-17-rc2-x86_64-1. ++ ++2006-04-13 Keith Owens ++ ++ * Remove trailing white space. ++ * kdb v4.4-2.6-17-rc1-x86_64-1. ++ ++2006-03-25 Jack F. Vogel ++ * Sync with Keith's changes for 2.6.16 ++ * code from Andi Kleen to support above ++ ++2005-09-30 Jack F. Vogel ++ * Port to 2.6.14-rc2 ++ * sync with a couple changes from Keith ++ * Add backtrace code from Jim Houston ++ (thanks Jim) ++ ++2005-08-31 Jack F. Vogel ++ * Change to linker script for kexec ++ thanks to Steven Dake ++ ++2005-08-30 Jack F. Vogel ++ * Notify struct should not be devinit ++ thanks IWAMOTO Toshihiro ++ ++2005-08-25 Jack F. Vogel ++ * Update to 2.6.11 ++ * Fix to synchronize with the notify changes ++ thanks to Jim Houston. ++ ++2004-09-30 Keith Owens ++ * Port to 2.6.9-rc2 ++ * Fix line editting characters. Jim Houston, Comcast. ++ * kdb v4.4-2.6.9-rc2-x86-64-1. ++ ++2004-08-15 Jack F. Vogel ++ * Port to 2.6.8 ++ * tighten up the code, using the built-in ++ die_chain notify interface, thanks to ++ Andi Kleen for pointing this out. ++ ++2004-05-15 Jack F. Vogel ++ * port to 2.6.6 for x86_64 ++ ++2003-12-15 Cliff Neighbors ++ * initial port from i386 to x86_64 ++ ++2002-08-10 Keith Owens ++ ++ * Replace kdb_port with kdb_serial to support memory mapped I/O. ++ Note: This needs kdb v2.3-2.4.19-common-2 or later. ++ * kdb v2.3-2.4.19-i386-3. ++ ++2002-08-09 Keith Owens ++ ++ * Use -fno-optimize-sibling-calls for kdb if gcc supports it. ++ * .text.lock does not consume an activation frame. ++ * kdb v2.3-2.4.19-i386-2. ++ ++2002-08-07 Keith Owens ++ ++ * Upgrade to 2.4.19. ++ * Remove individual SGI copyrights, the general SGI copyright applies. ++ * New .text.lock name. Hugh Dickins. ++ * Set KERNEL_CS in kdba_getcurrentframe. Hugh Dickins. ++ * Clean up disassembly layout. Hugh Dickins, Keith Owens. ++ * Replace hard coded stack size with THREAD_SIZE. Hugh Dickins. ++ * Better stack layout on bt with no frame pointers. Hugh Dickins. ++ * Make i386 IO breakpoints (bpha
IO) work again. ++ Martin Wilck, Keith Owens. ++ * Remove fixed KDB_MAX_COMMANDS size. ++ * Add set_fs() around __copy_to_user on kernel addresses. ++ Randolph Chung. ++ * Position i386 for CONFIG_NUMA_REPLICATE. ++ * kdb v2.3-2.4.19-i386-1. ++ ++2002-07-09 Keith Owens ++ ++ * Upgrade to 2.4.19-rc1. ++ ++2002-06-14 Keith Owens ++ ++ * Upgrade to 2.4.19-pre10. ++ * kdb v2.1-2.4.19-pre10-i386-1. ++ ++2002-04-09 Keith Owens ++ ++ * Upgrade to 2.4.19-pre6. ++ * kdb v2.1-2.4.19-pre6-i386-1. ++ ++2002-02-26 Keith Owens ++ ++ * Upgrade to 2.4.18. ++ * kdb v2.1-2.4.18-i386-1. ++ ++2002-01-18 Keith Owens ++ ++ * Use new kdb_get/put functions. ++ * Define kdba_{get,put}area_size functions for i386. ++ * Remove over-engineered dblist callback functions. ++ * Correctly handle failing call disp32 in backtrace. ++ * Remove bp_instvalid flag, redundant code. ++ * Remove dead code. ++ * kdb v2.1-2.4.17-i386-1. ++ ++2002-01-04 Keith Owens ++ ++ * Sync xfs <-> kdb i386 code. ++ +--- /dev/null ++++ b/arch/x86/kdb/Makefile +@@ -0,0 +1,29 @@ ++# ++# This file is subject to the terms and conditions of the GNU General Public ++# License. See the file "COPYING" in the main directory of this archive ++# for more details. ++# ++# Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++# ++ ++obj-$(CONFIG_KDB) += kdba_bp.o x86-dis.o kdba_bt.o \ ++ kdba_io.o kdba_id.o kdba_support.o ++ ++ifneq (,$(findstring -fno-optimize-sibling-calls,$(KBUILD_CFLAGS))) ++ CFLAGS_kdba_bt.o += -DNO_SIBLINGS ++endif ++ ++REGPARM := $(subst -mregparm=,,$(filter -mregparm=%,$(KBUILD_CFLAGS))) ++ifeq (,$(REGPARM)) ++ifeq ($(CONFIG_X86_32),y) ++ REGPARM := 3 ++else ++ REGPARM := 6 ++endif ++endif ++ ++CFLAGS_kdba_bt.o += -DREGPARM=$(REGPARM) -DCCVERSION="$(CCVERSION)" ++ ++override CFLAGS := $(CFLAGS:%-pg=% ) ++ ++CFLAGS_kdba_io.o += -I $(TOPDIR)/arch/$(SRCARCH)/kdb +--- /dev/null ++++ b/arch/x86/kdb/kdb_cmds_32 +@@ -0,0 +1,17 @@ ++# Standard architecture specific commands for kdb. ++# These commands are appended to those in kdb/kdb_cmds, see that file for ++# restrictions. ++ ++# Standard debugging information for first level support, invoked from archkdb* ++# commands that are defined in kdb/kdb_cmds. ++ ++defcmd archkdbcommon "" "Common arch debugging" ++ set LINES 2000000 ++ set BTAPROMPT 0 ++ -summary ++ -id %eip-24 ++ -cpu ++ -ps ++ -dmesg 600 ++ -bt ++endefcmd +--- /dev/null ++++ b/arch/x86/kdb/kdb_cmds_64 +@@ -0,0 +1,18 @@ ++# Standard architecture specific commands for kdb. ++# These commands are appended to those in kdb/kdb_cmds, see that file for ++# restrictions. ++ ++# Standard debugging information for first level support, invoked from archkdb* ++# commands that are defined in kdb/kdb_cmds. ++ ++defcmd archkdbcommon "" "Common arch debugging" ++ set LINES 2000000 ++ set BTAPROMPT 0 ++ -summary ++ -id %rip-24 ++ -cpu ++ -ps ++ -dmesg 600 ++ -bt ++ -cpu_pda * ++endefcmd +--- /dev/null ++++ b/arch/x86/kdb/kdba_bp.c +@@ -0,0 +1,914 @@ ++/* ++ * Kernel Debugger Architecture Dependent Breakpoint Handling ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++static char *kdba_rwtypes[] = { "Instruction(Register)", "Data Write", ++ "I/O", "Data Access"}; ++ ++/* ++ * Table describing processor architecture hardware ++ * breakpoint registers for every CPU. ++ */ ++ ++static kdbhard_bp_t kdb_hardbreaks[NR_CPUS][KDB_MAXHARDBPT]; ++ ++/* ++ * kdba_db_trap ++ * ++ * Perform breakpoint processing upon entry to the ++ * processor debugger fault. Determine and print ++ * the active breakpoint. ++ * ++ * Parameters: ++ * regs Exception frame containing machine register state ++ * error Error number passed to kdb. ++ * Outputs: ++ * None. ++ * Returns: ++ * KDB_DB_BPT Standard instruction or data breakpoint encountered ++ * KDB_DB_SS Single Step fault ('ss' command or end of 'ssb' command) ++ * KDB_DB_SSB Single Step fault, caller should continue ('ssb' command) ++ * KDB_DB_SSBPT Single step over breakpoint ++ * KDB_DB_NOBPT No existing kdb breakpoint matches this debug exception ++ * Locking: ++ * None. ++ * Remarks: ++ * Yup, there be goto's here. ++ * ++ * If multiple processors receive debug exceptions simultaneously, ++ * one may be waiting at the kdb fence in kdb() while the user ++ * issues a 'bc' command to clear the breakpoint the processor ++ * which is waiting has already encountered. If this is the case, ++ * the debug registers will no longer match any entry in the ++ * breakpoint table, and we'll return the value KDB_DB_NOBPT. ++ * This can cause a panic in die_if_kernel(). It is safer to ++ * disable the breakpoint (bd), go until all processors are past ++ * the breakpoint then clear the breakpoint (bc). This code ++ * recognises a breakpoint even when disabled but not when it has ++ * been cleared. ++ * ++ * WARNING: This routine clears the debug state. It should be called ++ * once per debug and the result cached. ++ */ ++ ++kdb_dbtrap_t ++kdba_db_trap(struct pt_regs *regs, int error_unused) ++{ ++ kdb_machreg_t dr6; ++ kdb_machreg_t dr7; ++ int rw, reg; ++ int i; ++ kdb_dbtrap_t rv = KDB_DB_BPT; ++ kdb_bp_t *bp; ++ int cpu = smp_processor_id(); ++ ++ if (KDB_NULL_REGS(regs)) ++ return KDB_DB_NOBPT; ++ ++ dr6 = kdba_getdr6(); ++ dr7 = kdba_getdr7(); ++ ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdb: dr6 0x%lx dr7 0x%lx\n", dr6, dr7); ++ if (dr6 & DR6_BS) { ++ if (KDB_STATE(SSBPT)) { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("ssbpt\n"); ++ KDB_STATE_CLEAR(SSBPT); ++ for(i=0,bp=kdb_breakpoints; ++ i < KDB_MAXBPT; ++ i++, bp++) { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("bp 0x%p enabled %d delayed %d global %d cpu %d\n", ++ bp, bp->bp_enabled, bp->bp_delayed, bp->bp_global, bp->bp_cpu); ++ if (!bp->bp_enabled) ++ continue; ++ if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) ++ continue; ++ if (KDB_DEBUG(BP)) ++ kdb_printf("bp for this cpu\n"); ++ if (bp->bp_delayed) { ++ bp->bp_delayed = 0; ++ if (KDB_DEBUG(BP)){ ++ /* Can't be hw breakpoint */ ++ if (bp->bp_hardtype) ++ kdb_printf("kdb: Error - hw bp delayed\n"); ++ kdb_printf("kdba_installbp\n"); ++ } ++ kdba_installbp(regs, bp); ++ if (!KDB_STATE(DOING_SS)) { ++ regs->flags &= ~X86_EFLAGS_TF; ++ return(KDB_DB_SSBPT); ++ } ++ break; ++ } ++ } ++ if (i == KDB_MAXBPT) { ++ kdb_printf("kdb: Unable to find delayed breakpoint\n"); ++ } ++ if (!KDB_STATE(DOING_SS)) { ++ regs->flags &= ~X86_EFLAGS_TF; ++ return(KDB_DB_NOBPT); ++ } ++ /* FALLTHROUGH */ ++ } ++ ++ /* ++ * KDB_STATE_DOING_SS is set when the kernel debugger is using ++ * the processor trap flag to single-step a processor. If a ++ * single step trap occurs and this flag is clear, the SS trap ++ * will be ignored by KDB and the kernel will be allowed to deal ++ * with it as necessary (e.g. for ptrace). ++ */ ++ if (!KDB_STATE(DOING_SS)) ++ goto unknown; ++ ++ /* single step */ ++ rv = KDB_DB_SS; /* Indicate single step */ ++ if (KDB_STATE(DOING_SSB)) { ++ unsigned char instruction[2]; ++ ++ kdb_id1(regs->ip); ++ if (kdb_getarea(instruction, regs->ip) || ++ (instruction[0]&0xf0) == 0xe0 || /* short disp jumps */ ++ (instruction[0]&0xf0) == 0x70 || /* Misc. jumps */ ++ instruction[0] == 0xc2 || /* ret */ ++ instruction[0] == 0x9a || /* call */ ++ (instruction[0]&0xf8) == 0xc8 || /* enter, leave, iret, int, */ ++ ((instruction[0] == 0x0f) && ++ ((instruction[1]&0xf0)== 0x80)) ++ ) { ++ /* ++ * End the ssb command here. ++ */ ++ KDB_STATE_CLEAR(DOING_SSB); ++ KDB_STATE_CLEAR(DOING_SS); ++ } else { ++ rv = KDB_DB_SSB; /* Indicate ssb - dismiss immediately */ ++ } ++ } else { ++ /* ++ * Print current insn ++ */ ++ kdb_printf("SS trap at "); ++ kdb_symbol_print(regs->ip, NULL, KDB_SP_DEFAULT|KDB_SP_NEWLINE); ++ kdb_id1(regs->ip); ++ KDB_STATE_CLEAR(DOING_SS); ++ } ++ ++ if (rv != KDB_DB_SSB) ++ regs->flags &= ~X86_EFLAGS_TF; ++ } ++ ++ if (dr6 & DR6_B0) { ++ rw = DR7_RW0(dr7); ++ reg = 0; ++ goto handle; ++ } ++ ++ if (dr6 & DR6_B1) { ++ rw = DR7_RW1(dr7); ++ reg = 1; ++ goto handle; ++ } ++ ++ if (dr6 & DR6_B2) { ++ rw = DR7_RW2(dr7); ++ reg = 2; ++ goto handle; ++ } ++ ++ if (dr6 & DR6_B3) { ++ rw = DR7_RW3(dr7); ++ reg = 3; ++ goto handle; ++ } ++ ++ if (rv > 0) ++ goto handled; ++ ++ goto unknown; /* dismiss */ ++ ++handle: ++ /* ++ * Set Resume Flag ++ */ ++ regs->flags |= X86_EFLAGS_RF; ++ ++ /* ++ * Determine which breakpoint was encountered. ++ */ ++ for(i=0, bp=kdb_breakpoints; ibp_free) ++ && (bp->bp_global || bp->bp_cpu == smp_processor_id()) ++ && (bp->bp_hard[cpu]) ++ && (bp->bp_hard[cpu]->bph_reg == reg)) { ++ /* ++ * Hit this breakpoint. ++ */ ++ kdb_printf("%s breakpoint #%d at " kdb_bfd_vma_fmt "\n", ++ kdba_rwtypes[rw], ++ i, bp->bp_addr); ++ ++ /* ++ * For an instruction breakpoint, disassemble ++ * the current instruction. ++ */ ++ if (rw == 0) { ++ kdb_id1(regs->ip); ++ } ++ ++ goto handled; ++ } ++ } ++ ++unknown: ++ regs->flags |= X86_EFLAGS_RF; /* Supress further faults */ ++ rv = KDB_DB_NOBPT; /* Cause kdb() to return */ ++ ++handled: ++ ++ /* ++ * Clear the pending exceptions. ++ */ ++ kdba_putdr6(0); ++ ++ return rv; ++} ++ ++/* ++ * kdba_bp_trap ++ * ++ * Perform breakpoint processing upon entry to the ++ * processor breakpoint instruction fault. Determine and print ++ * the active breakpoint. ++ * ++ * Parameters: ++ * regs Exception frame containing machine register state ++ * error Error number passed to kdb. ++ * Outputs: ++ * None. ++ * Returns: ++ * 0 Standard instruction or data breakpoint encountered ++ * 1 Single Step fault ('ss' command) ++ * 2 Single Step fault, caller should continue ('ssb' command) ++ * 3 No existing kdb breakpoint matches this debug exception ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * If multiple processors receive debug exceptions simultaneously, ++ * one may be waiting at the kdb fence in kdb() while the user ++ * issues a 'bc' command to clear the breakpoint the processor which ++ * is waiting has already encountered. If this is the case, the ++ * debug registers will no longer match any entry in the breakpoint ++ * table, and we'll return the value '3'. This can cause a panic ++ * in die_if_kernel(). It is safer to disable the breakpoint (bd), ++ * 'go' until all processors are past the breakpoint then clear the ++ * breakpoint (bc). This code recognises a breakpoint even when ++ * disabled but not when it has been cleared. ++ * ++ * WARNING: This routine resets the ip. It should be called ++ * once per breakpoint and the result cached. ++ */ ++ ++kdb_dbtrap_t ++kdba_bp_trap(struct pt_regs *regs, int error_unused) ++{ ++ int i; ++ kdb_dbtrap_t rv; ++ kdb_bp_t *bp; ++ ++ if (KDB_NULL_REGS(regs)) ++ return KDB_DB_NOBPT; ++ ++ /* ++ * Determine which breakpoint was encountered. ++ */ ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_bp_trap: ip=0x%lx (not adjusted) " ++ "flags=0x%lx regs=0x%p sp=0x%lx\n", ++ regs->ip, regs->flags, regs, regs->sp); ++ ++ rv = KDB_DB_NOBPT; /* Cause kdb() to return */ ++ ++ for(i=0, bp=kdb_breakpoints; ibp_free) ++ continue; ++ if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) ++ continue; ++ if ((void *)bp->bp_addr == (void *)(regs->ip - bp->bp_adjust)) { ++ /* Hit this breakpoint. */ ++ regs->ip -= bp->bp_adjust; ++ kdb_printf("Instruction(i) breakpoint #%d at 0x%lx (adjusted)\n", ++ i, regs->ip); ++ kdb_id1(regs->ip); ++ rv = KDB_DB_BPT; ++ bp->bp_delay = 1; ++ /* SSBPT is set when the kernel debugger must single ++ * step a task in order to re-establish an instruction ++ * breakpoint which uses the instruction replacement ++ * mechanism. It is cleared by any action that removes ++ * the need to single-step the breakpoint. ++ */ ++ KDB_STATE_SET(SSBPT); ++ break; ++ } ++ } ++ ++ return rv; ++} ++ ++/* ++ * kdba_handle_bp ++ * ++ * Handle an instruction-breakpoint trap. Called when re-installing ++ * an enabled breakpoint which has has the bp_delay bit set. ++ * ++ * Parameters: ++ * Returns: ++ * Locking: ++ * Remarks: ++ * ++ * Ok, we really need to: ++ * 1) Restore the original instruction byte ++ * 2) Single Step ++ * 3) Restore breakpoint instruction ++ * 4) Continue. ++ * ++ * ++ */ ++ ++static void ++kdba_handle_bp(struct pt_regs *regs, kdb_bp_t *bp) ++{ ++ if (KDB_NULL_REGS(regs)) ++ return; ++ ++ if (KDB_DEBUG(BP)) ++ kdb_printf("regs->ip = 0x%lx\n", regs->ip); ++ ++ /* ++ * Setup single step ++ */ ++ kdba_setsinglestep(regs); ++ ++ /* ++ * Reset delay attribute ++ */ ++ bp->bp_delay = 0; ++ bp->bp_delayed = 1; ++} ++ ++ ++/* ++ * kdba_bptype ++ * ++ * Return a string describing type of breakpoint. ++ * ++ * Parameters: ++ * bph Pointer to hardware breakpoint description ++ * Outputs: ++ * None. ++ * Returns: ++ * Character string. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++char * ++kdba_bptype(kdbhard_bp_t *bph) ++{ ++ char *mode; ++ ++ mode = kdba_rwtypes[bph->bph_mode]; ++ ++ return mode; ++} ++ ++/* ++ * kdba_printbpreg ++ * ++ * Print register name assigned to breakpoint ++ * ++ * Parameters: ++ * bph Pointer hardware breakpoint structure ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++static void ++kdba_printbpreg(kdbhard_bp_t *bph) ++{ ++ kdb_printf(" in dr%ld", bph->bph_reg); ++} ++ ++/* ++ * kdba_printbp ++ * ++ * Print string describing hardware breakpoint. ++ * ++ * Parameters: ++ * bph Pointer to hardware breakpoint description ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++void ++kdba_printbp(kdb_bp_t *bp) ++{ ++ int cpu; ++ ++ kdb_printf("\n is enabled"); ++ if (bp->bp_hardtype) { ++ if (bp->bp_global) ++ cpu = smp_processor_id(); ++ else ++ cpu = bp->bp_cpu; ++ kdba_printbpreg(bp->bp_hard[cpu]); ++ if (bp->bp_hard[cpu]->bph_mode != 0) { ++ kdb_printf(" for %d bytes", ++ bp->bp_hard[cpu]->bph_length+1); ++ } ++ } ++} ++ ++/* ++ * kdba_parsebp ++ * ++ * Parse architecture dependent portion of the ++ * breakpoint command. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic for failure ++ * Locking: ++ * None. ++ * Remarks: ++ * for Ia32 architure, data access, data write and ++ * I/O breakpoints are supported in addition to instruction ++ * breakpoints. ++ * ++ * {datar|dataw|io|inst} [length] ++ */ ++ ++int ++kdba_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp) ++{ ++ int nextarg = *nextargp; ++ int diag; ++ kdbhard_bp_t *bph = &bp->bp_template; ++ ++ bph->bph_mode = 0; /* Default to instruction breakpoint */ ++ bph->bph_length = 0; /* Length must be zero for insn bp */ ++ if ((argc + 1) != nextarg) { ++ if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0) { ++ bph->bph_mode = 3; ++ } else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) { ++ bph->bph_mode = 1; ++ } else if (strnicmp(argv[nextarg], "io", sizeof("io")) == 0) { ++ bph->bph_mode = 2; ++ } else if (strnicmp(argv[nextarg], "inst", sizeof("inst")) == 0) { ++ bph->bph_mode = 0; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ ++ bph->bph_length = 3; /* Default to 4 byte */ ++ ++ nextarg++; ++ ++ if ((argc + 1) != nextarg) { ++ unsigned long len; ++ ++ diag = kdbgetularg((char *)argv[nextarg], ++ &len); ++ if (diag) ++ return diag; ++ ++ ++ if ((len > 4) || (len == 3)) ++ return KDB_BADLENGTH; ++ ++ bph->bph_length = len; ++ bph->bph_length--; /* Normalize for debug register */ ++ nextarg++; ++ } ++ ++ if ((argc + 1) != nextarg) ++ return KDB_ARGCOUNT; ++ ++ /* ++ * Indicate to architecture independent level that ++ * a hardware register assignment is required to enable ++ * this breakpoint. ++ */ ++ ++ bph->bph_free = 0; ++ } else { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_bp: no args, forcehw is %d\n", bp->bp_forcehw); ++ if (bp->bp_forcehw) { ++ /* ++ * We are forced to use a hardware register for this ++ * breakpoint because either the bph or bpha ++ * commands were used to establish this breakpoint. ++ */ ++ bph->bph_free = 0; ++ } else { ++ /* ++ * Indicate to architecture dependent level that ++ * the instruction replacement breakpoint technique ++ * should be used for this breakpoint. ++ */ ++ bph->bph_free = 1; ++ bp->bp_adjust = 1; /* software, int 3 is one byte */ ++ } ++ } ++ ++ if (bph->bph_mode != 2 && kdba_verify_rw(bp->bp_addr, bph->bph_length+1)) { ++ kdb_printf("Invalid address for breakpoint, ignoring bp command\n"); ++ return KDB_BADADDR; ++ } ++ ++ *nextargp = nextarg; ++ return 0; ++} ++ ++/* ++ * kdba_allocbp ++ * ++ * Allocate hw register for bp on specific CPU ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * A pointer to the allocated register kdbhard_bp_t structure for ++ * success, Null and a non-zero diagnostic for failure. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++static kdbhard_bp_t * ++kdba_allocbp(kdbhard_bp_t *bph, int *diagp, unsigned int cpu) ++{ ++ int i; ++ kdbhard_bp_t *newbph; ++ ++ for(i=0; i < KDB_MAXHARDBPT; i++) { ++ newbph=&(kdb_hardbreaks[cpu][i]); ++ if (newbph->bph_free) { ++ break; ++ } ++ } ++ ++ if (i == KDB_MAXHARDBPT) { ++ *diagp = KDB_TOOMANYDBREGS; ++ return NULL; ++ } ++ ++ *diagp = 0; ++ ++ /* ++ * Copy data from template. Can't just copy the entire template ++ * here because the register number in kdb_hardbreaks must be ++ * preserved. ++ */ ++ newbph->bph_data = bph->bph_data; ++ newbph->bph_write = bph->bph_write; ++ newbph->bph_mode = bph->bph_mode; ++ newbph->bph_length = bph->bph_length; ++ ++ /* ++ * Mark entry allocated. ++ */ ++ newbph->bph_free = 0; ++ ++ return newbph; ++} ++ ++/* ++ * kdba_alloc_hwbp ++ * ++ * Associate a hardware registers with a breakpoint. ++ * If hw bp is global hw registers descriptor will be allocated ++ * on every CPU. ++ * ++ * Parameters: ++ * bp - hardware bp ++ * diagp - pointer to variable that will store error when ++ * function complete ++ * Outputs: ++ * None. ++ * Returns: ++ * None ++ * Locking: ++ * None. ++ * Remarks: ++ * Should be called with correct bp->bp_template ++ */ ++ ++void ++kdba_alloc_hwbp(kdb_bp_t *bp, int *diagp) ++{ ++ int i; ++ ++ if (bp->bp_global){ ++ for (i = 0; i < NR_CPUS; ++i) { ++ if (!cpu_online(i)) ++ continue; ++ bp->bp_hard[i] = kdba_allocbp(&bp->bp_template, diagp, i); ++ if (*diagp) ++ break; ++ } ++ } else { ++ bp->bp_hard[bp->bp_cpu] = kdba_allocbp(&bp->bp_template, diagp, bp->bp_cpu); ++ } ++ bp->bp_hardtype = 1; ++} ++ ++/* ++ * kdba_freebp ++ * ++ * Deallocate hw registers descriptor for bp on specific CPU ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic for failure ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++static void ++kdba_freebp(kdbhard_bp_t *bph) ++{ ++ bph->bph_free = 1; ++} ++ ++/* ++ * kdba_free_hwbp ++ * ++ * Frees allocated hw registers descriptors for bp. ++ * If hw bp is global, hw registers descriptors will be freed ++ * on every CPU. ++ * ++ * Parameters: ++ * bp - hardware bp ++ * Outputs: ++ * None. ++ * Returns: ++ * None ++ * Locking: ++ * None. ++ * Remarks: ++ * Should be called with correct bp->bp_template ++ */ ++ ++void ++kdba_free_hwbp(kdb_bp_t *bp) ++{ ++ int i; ++ ++ /* When kernel enters KDB, first, all local bps ++ * are removed, so here we don't need to clear ++ * debug registers. ++ */ ++ ++ if (bp->bp_global){ ++ for (i = 0; i < NR_CPUS; ++i) { ++ if (!cpu_online(i)) ++ continue; ++ if (bp->bp_hard[i]) ++ kdba_freebp(bp->bp_hard[i]); ++ bp->bp_hard[i] = 0; ++ } ++ } else { ++ kdba_freebp(bp->bp_hard[bp->bp_cpu]); ++ bp->bp_hard[bp->bp_cpu] = NULL; ++ } ++ bp->bp_hardtype = 0; ++} ++ ++/* ++ * kdba_initbp ++ * ++ * Initialize the breakpoint table for the hardware breakpoint ++ * register. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * Zero for success, a kdb diagnostic for failure ++ * Locking: ++ * None. ++ * Remarks: ++ * ++ * There is one entry per register. On the ia32 architecture ++ * all the registers are interchangeable, so no special allocation ++ * criteria are required. ++ */ ++ ++void ++kdba_initbp(void) ++{ ++ int i,j; ++ kdbhard_bp_t *bph; ++ ++ /* ++ * Clear the hardware breakpoint table ++ */ ++ ++ memset(kdb_hardbreaks, '\0', sizeof(kdb_hardbreaks)); ++ ++ for (i = 0; i < NR_CPUS; ++i) { ++ /* Called early so we don't know actual ++ * ammount of CPUs ++ */ ++ for(j=0; j < KDB_MAXHARDBPT; j++) { ++ bph=&(kdb_hardbreaks[i][j]); ++ bph->bph_reg = j; ++ bph->bph_free = 1; ++ } ++ } ++} ++ ++/* ++ * kdba_installbp ++ * ++ * Install a breakpoint ++ * ++ * Parameters: ++ * regs Exception frame ++ * bp Breakpoint structure for the breakpoint to be installed ++ * Outputs: ++ * None. ++ * Returns: ++ * 0 if breakpoint installed. ++ * Locking: ++ * None. ++ * Remarks: ++ * For hardware breakpoints, a debug register is allocated ++ * and assigned to the breakpoint. If no debug register is ++ * available, a warning message is printed and the breakpoint ++ * is disabled. ++ * ++ * For instruction replacement breakpoints, we must single-step ++ * over the replaced instruction at this point so we can re-install ++ * the breakpoint instruction after the single-step. SSBPT is set ++ * when the breakpoint is initially hit and is cleared by any action ++ * that removes the need for single-step over the breakpoint. ++ */ ++ ++int ++kdba_installbp(struct pt_regs *regs, kdb_bp_t *bp) ++{ ++ int cpu = smp_processor_id(); ++ ++ /* ++ * Install the breakpoint, if it is not already installed. ++ */ ++ ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdba_installbp bp_installed %d\n", bp->bp_installed); ++ } ++ if (!KDB_STATE(SSBPT)) ++ bp->bp_delay = 0; ++ ++ if (bp->bp_hardtype) { ++ if (KDB_DEBUG(BP) && !bp->bp_global && cpu != bp->bp_cpu){ ++ kdb_printf("kdba_installbp: cpu != bp->bp_cpu for local hw bp\n"); ++ } ++ ++ if (KDB_DEBUG(BP) && !bp->bp_hard[cpu]){ ++ kdb_printf("kdba_installbp: Error - bp_hard[smp_processor_id()] is emply\n"); ++ return 1; ++ } ++ ++ if (!bp->bp_hard[cpu]->bph_installed){ ++ kdba_installdbreg(bp); ++ bp->bp_hard[cpu]->bph_installed = 1; ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", ++ bp->bp_hard[cpu]->bph_reg, bp->bp_addr); ++ } ++ } ++ } else if (!bp->bp_installed) { ++ if (bp->bp_delay) { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_installbp delayed bp\n"); ++ kdba_handle_bp(regs, bp); ++ } else { ++ if (kdb_getarea_size(&(bp->bp_inst), bp->bp_addr, 1) || ++ kdb_putword(bp->bp_addr, IA32_BREAKPOINT_INSTRUCTION, 1)) { ++ kdb_printf("kdba_installbp failed to set software breakpoint at " kdb_bfd_vma_fmt "\n", bp->bp_addr); ++ return(1); ++ } ++ bp->bp_installed = 1; ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdba_installbp instruction 0x%x at " kdb_bfd_vma_fmt "\n", ++ IA32_BREAKPOINT_INSTRUCTION, bp->bp_addr); ++ } ++ } ++ return(0); ++} ++ ++/* ++ * kdba_removebp ++ * ++ * Make a breakpoint ineffective. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++int ++kdba_removebp(kdb_bp_t *bp) ++{ ++ int cpu = smp_processor_id(); ++ ++ /* ++ * For hardware breakpoints, remove it from the active register, ++ * for software breakpoints, restore the instruction stream. ++ */ ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdba_removebp bp_installed %d\n", bp->bp_installed); ++ } ++ ++ if (bp->bp_hardtype) { ++ if (KDB_DEBUG(BP) && !bp->bp_global && cpu != bp->bp_cpu){ ++ kdb_printf("kdba_removebp: cpu != bp->bp_cpu for local hw bp\n"); ++ } ++ ++ if (KDB_DEBUG(BP) && !bp->bp_hard[cpu]){ ++ kdb_printf("kdba_removebp: Error - bp_hard[smp_processor_id()] is emply\n"); ++ return 1; ++ } ++ ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt "\n", ++ bp->bp_hard[cpu]->bph_reg, bp->bp_addr); ++ } ++ ++ if (bp->bp_hard[cpu]->bph_installed){ ++ if (KDB_DEBUG(BP)) { ++ kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", ++ bp->bp_hard[cpu]->bph_reg, bp->bp_addr); ++ } ++ kdba_removedbreg(bp); ++ bp->bp_hard[cpu]->bph_installed = 0; ++ } ++ } else if (bp->bp_installed) { ++ if (KDB_DEBUG(BP)) ++ kdb_printf("kdb: restoring instruction 0x%x at " kdb_bfd_vma_fmt "\n", ++ bp->bp_inst, bp->bp_addr); ++ if (kdb_putword(bp->bp_addr, bp->bp_inst, 1)) ++ return(1); ++ bp->bp_installed = 0; ++ } ++ return(0); ++} +--- /dev/null ++++ b/arch/x86/kdb/kdba_bt.c +@@ -0,0 +1,5758 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 2006, 2007-2009 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * Common code for doing accurate backtraces on i386 and x86_64, including ++ * printing the values of arguments. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define KDB_DEBUG_BB(fmt, ...) \ ++ {if (KDB_DEBUG(BB)) kdb_printf(fmt, ## __VA_ARGS__);} ++#define KDB_DEBUG_BB_OFFSET_PRINTF(offset, prefix, suffix) \ ++ kdb_printf(prefix "%c0x%x" suffix, \ ++ offset >= 0 ? '+' : '-', \ ++ offset >= 0 ? offset : -offset) ++#define KDB_DEBUG_BB_OFFSET(offset, prefix, suffix) \ ++ {if (KDB_DEBUG(BB)) KDB_DEBUG_BB_OFFSET_PRINTF(offset, prefix, suffix);} ++ ++#define BB_CHECK(expr, val, ret) \ ++({ \ ++ if (unlikely(expr)) { \ ++ kdb_printf("%s, line %d: BB_CHECK(" #expr ") failed " \ ++ #val "=%lx\n", \ ++ __FUNCTION__, __LINE__, (long)val); \ ++ bb_giveup = 1; \ ++ return ret; \ ++ } \ ++}) ++ ++static int bb_giveup; ++ ++/* Use BBRG_Rxx for both i386 and x86_64. RAX through R15 must be at the end, ++ * starting with RAX. Some of these codes do not reflect actual registers, ++ * such codes are special cases when parsing the record of register changes. ++ * When updating BBRG_ entries, update bbrg_name as well. ++ */ ++ ++enum bb_reg_code ++{ ++ BBRG_UNDEFINED = 0, /* Register contents are undefined */ ++ BBRG_OSP, /* original stack pointer on entry to function */ ++ BBRG_RAX, ++ BBRG_RBX, ++ BBRG_RCX, ++ BBRG_RDX, ++ BBRG_RDI, ++ BBRG_RSI, ++ BBRG_RBP, ++ BBRG_RSP, ++ BBRG_R8, ++ BBRG_R9, ++ BBRG_R10, ++ BBRG_R11, ++ BBRG_R12, ++ BBRG_R13, ++ BBRG_R14, ++ BBRG_R15, ++}; ++ ++const static char *bbrg_name[] = { ++ [BBRG_UNDEFINED] = "undefined", ++ [BBRG_OSP] = "osp", ++ [BBRG_RAX] = "rax", ++ [BBRG_RBX] = "rbx", ++ [BBRG_RCX] = "rcx", ++ [BBRG_RDX] = "rdx", ++ [BBRG_RDI] = "rdi", ++ [BBRG_RSI] = "rsi", ++ [BBRG_RBP] = "rbp", ++ [BBRG_RSP] = "rsp", ++ [BBRG_R8] = "r8", ++ [BBRG_R9] = "r9", ++ [BBRG_R10] = "r10", ++ [BBRG_R11] = "r11", ++ [BBRG_R12] = "r12", ++ [BBRG_R13] = "r13", ++ [BBRG_R14] = "r14", ++ [BBRG_R15] = "r15", ++}; ++ ++/* Map a register name to its register code. This includes the sub-register ++ * addressable fields, e.g. parts of rax can be addressed as ax, al, ah, eax. ++ * The list is sorted so it can be binary chopped, sort command is: ++ * LANG=C sort -t '"' -k2 ++ */ ++ ++struct bb_reg_code_map { ++ enum bb_reg_code reg; ++ const char *name; ++}; ++ ++const static struct bb_reg_code_map ++bb_reg_code_map[] = { ++ { BBRG_RAX, "ah" }, ++ { BBRG_RAX, "al" }, ++ { BBRG_RAX, "ax" }, ++ { BBRG_RBX, "bh" }, ++ { BBRG_RBX, "bl" }, ++ { BBRG_RBP, "bp" }, ++ { BBRG_RBP, "bpl" }, ++ { BBRG_RBX, "bx" }, ++ { BBRG_RCX, "ch" }, ++ { BBRG_RCX, "cl" }, ++ { BBRG_RCX, "cx" }, ++ { BBRG_RDX, "dh" }, ++ { BBRG_RDI, "di" }, ++ { BBRG_RDI, "dil" }, ++ { BBRG_RDX, "dl" }, ++ { BBRG_RDX, "dx" }, ++ { BBRG_RAX, "eax" }, ++ { BBRG_RBP, "ebp" }, ++ { BBRG_RBX, "ebx" }, ++ { BBRG_RCX, "ecx" }, ++ { BBRG_RDI, "edi" }, ++ { BBRG_RDX, "edx" }, ++ { BBRG_RSI, "esi" }, ++ { BBRG_RSP, "esp" }, ++ { BBRG_R10, "r10" }, ++ { BBRG_R10, "r10d" }, ++ { BBRG_R10, "r10l" }, ++ { BBRG_R10, "r10w" }, ++ { BBRG_R11, "r11" }, ++ { BBRG_R11, "r11d" }, ++ { BBRG_R11, "r11l" }, ++ { BBRG_R11, "r11w" }, ++ { BBRG_R12, "r12" }, ++ { BBRG_R12, "r12d" }, ++ { BBRG_R12, "r12l" }, ++ { BBRG_R12, "r12w" }, ++ { BBRG_R13, "r13" }, ++ { BBRG_R13, "r13d" }, ++ { BBRG_R13, "r13l" }, ++ { BBRG_R13, "r13w" }, ++ { BBRG_R14, "r14" }, ++ { BBRG_R14, "r14d" }, ++ { BBRG_R14, "r14l" }, ++ { BBRG_R14, "r14w" }, ++ { BBRG_R15, "r15" }, ++ { BBRG_R15, "r15d" }, ++ { BBRG_R15, "r15l" }, ++ { BBRG_R15, "r15w" }, ++ { BBRG_R8, "r8" }, ++ { BBRG_R8, "r8d" }, ++ { BBRG_R8, "r8l" }, ++ { BBRG_R8, "r8w" }, ++ { BBRG_R9, "r9" }, ++ { BBRG_R9, "r9d" }, ++ { BBRG_R9, "r9l" }, ++ { BBRG_R9, "r9w" }, ++ { BBRG_RAX, "rax" }, ++ { BBRG_RBP, "rbp" }, ++ { BBRG_RBX, "rbx" }, ++ { BBRG_RCX, "rcx" }, ++ { BBRG_RDI, "rdi" }, ++ { BBRG_RDX, "rdx" }, ++ { BBRG_RSI, "rsi" }, ++ { BBRG_RSP, "rsp" }, ++ { BBRG_RSI, "si" }, ++ { BBRG_RSI, "sil" }, ++ { BBRG_RSP, "sp" }, ++ { BBRG_RSP, "spl" }, ++}; ++ ++/* Record register contents in terms of the values that were passed to this ++ * function, IOW track which registers contain an input value. A register's ++ * contents can be undefined, it can contain an input register value or it can ++ * contain an offset from the original stack pointer. ++ * ++ * This structure is used to represent the current contents of the integer ++ * registers, it is held in an array that is indexed by BBRG_xxx. The element ++ * for BBRG_xxx indicates what input value is currently in BBRG_xxx. When ++ * 'value' is BBRG_OSP then register BBRG_xxx contains a stack pointer, ++ * pointing at 'offset' from the original stack pointer on entry to the ++ * function. When 'value' is not BBRG_OSP then element BBRG_xxx contains the ++ * original contents of an input register and offset is ignored. ++ * ++ * An input register 'value' can be stored in more than one register and/or in ++ * more than one memory location. ++ */ ++ ++struct bb_reg_contains ++{ ++ enum bb_reg_code value: 8; ++ short offset; ++}; ++ ++/* Note: the offsets in struct bb_mem_contains in this code are _NOT_ offsets ++ * from OSP, they are offsets from current RSP. It fits better with the way ++ * that struct pt_regs is built, some code pushes extra data before pt_regs so ++ * working with OSP relative offsets gets messy. struct bb_mem_contains ++ * entries must be in descending order of RSP offset. ++ */ ++ ++typedef struct { DECLARE_BITMAP(bits, BBRG_R15+1); } bbrgmask_t; ++#define BB_SKIP(reg) (1 << (BBRG_ ## reg)) ++struct bb_mem_contains { ++ short offset_address; ++ enum bb_reg_code value: 8; ++}; ++ ++/* Transfer of control to a label outside the current function. If the ++ * transfer is to a known common restore path that expects known registers ++ * and/or a known memory state (e.g. struct pt_regs) then do a sanity check on ++ * the state at this point. ++ */ ++ ++struct bb_name_state { ++ const char *name; /* target function */ ++ bfd_vma address; /* Address of target function */ ++ const char *fname; /* optional from function name */ ++ const struct bb_mem_contains *mem; /* expected memory state */ ++ const struct bb_reg_contains *regs; /* expected register state */ ++ const unsigned short mem_size; /* ARRAY_SIZE(mem) */ ++ const unsigned short regs_size; /* ARRAY_SIZE(regs) */ ++ const short osp_offset; /* RSP in regs == OSP+osp_offset */ ++ const bbrgmask_t skip_mem; /* Some slots in mem may be undefined */ ++ const bbrgmask_t skip_regs; /* Some slots in regs may be undefined */ ++}; ++ ++/* NS (NAME_STATE) macros define the register and memory state when we transfer ++ * control to or start decoding a special case name. Use NS when the target ++ * label always has the same state. Use NS_FROM and specify the source label ++ * if the target state is slightly different depending on where it is branched ++ * from. This gives better state checking, by isolating the special cases. ++ * ++ * Note: for the same target label, NS_FROM entries must be followed by a ++ * single NS entry. ++ */ ++ ++#define NS_FROM(iname, ifname, imem, iregs, iskip_mem, iskip_regs, iosp_offset) \ ++ { \ ++ .name = iname, \ ++ .fname = ifname, \ ++ .mem = imem, \ ++ .regs = iregs, \ ++ .mem_size = ARRAY_SIZE(imem), \ ++ .regs_size = ARRAY_SIZE(iregs), \ ++ .skip_mem.bits[0] = iskip_mem, \ ++ .skip_regs.bits[0] = iskip_regs, \ ++ .osp_offset = iosp_offset, \ ++ .address = 0 \ ++ } ++ ++/* Shorter forms for the common cases */ ++#define NS(iname, imem, iregs, iskip_mem, iskip_regs, iosp_offset) \ ++ NS_FROM(iname, NULL, imem, iregs, iskip_mem, iskip_regs, iosp_offset) ++#define NS_MEM(iname, imem, iskip_mem) \ ++ NS_FROM(iname, NULL, imem, no_regs, iskip_mem, 0, 0) ++#define NS_MEM_FROM(iname, ifname, imem, iskip_mem) \ ++ NS_FROM(iname, ifname, imem, no_regs, iskip_mem, 0, 0) ++#define NS_REG(iname, iregs, iskip_regs) \ ++ NS_FROM(iname, NULL, no_memory, iregs, 0, iskip_regs, 0) ++#define NS_REG_FROM(iname, ifname, iregs, iskip_regs) \ ++ NS_FROM(iname, ifname, no_memory, iregs, 0, iskip_regs, 0) ++ ++static void ++bb_reg_code_set_value(enum bb_reg_code dst, enum bb_reg_code src); ++ ++static const char *bb_mod_name, *bb_func_name; ++ ++static int ++bb_noret(const char *name) ++{ ++ if (strcmp(name, "panic") == 0 || ++ strcmp(name, "do_exit") == 0 || ++ strcmp(name, "do_group_exit") == 0 || ++ strcmp(name, "complete_and_exit") == 0) ++ return 1; ++ return 0; ++} ++ ++/*============================================================================*/ ++/* */ ++/* Most of the basic block code and data is common to x86_64 and i386. This */ ++/* large ifdef contains almost all of the differences between the two */ ++/* architectures. */ ++/* */ ++/* Make sure you update the correct section of this ifdef. */ ++/* */ ++/*============================================================================*/ ++ ++#ifdef CONFIG_X86_64 ++ ++/* Registers that can be used to pass parameters, in the order that parameters ++ * are passed. ++ */ ++ ++const static enum bb_reg_code ++bb_param_reg[] = { ++ BBRG_RDI, ++ BBRG_RSI, ++ BBRG_RDX, ++ BBRG_RCX, ++ BBRG_R8, ++ BBRG_R9, ++}; ++ ++const static enum bb_reg_code ++bb_preserved_reg[] = { ++ BBRG_RBX, ++ BBRG_RBP, ++ BBRG_RSP, ++ BBRG_R12, ++ BBRG_R13, ++ BBRG_R14, ++ BBRG_R15, ++}; ++ ++static const struct bb_mem_contains full_pt_regs[] = { ++ { 0x70, BBRG_RDI }, ++ { 0x68, BBRG_RSI }, ++ { 0x60, BBRG_RDX }, ++ { 0x58, BBRG_RCX }, ++ { 0x50, BBRG_RAX }, ++ { 0x48, BBRG_R8 }, ++ { 0x40, BBRG_R9 }, ++ { 0x38, BBRG_R10 }, ++ { 0x30, BBRG_R11 }, ++ { 0x28, BBRG_RBX }, ++ { 0x20, BBRG_RBP }, ++ { 0x18, BBRG_R12 }, ++ { 0x10, BBRG_R13 }, ++ { 0x08, BBRG_R14 }, ++ { 0x00, BBRG_R15 }, ++}; ++static const struct bb_mem_contains full_pt_regs_plus_1[] = { ++ { 0x78, BBRG_RDI }, ++ { 0x70, BBRG_RSI }, ++ { 0x68, BBRG_RDX }, ++ { 0x60, BBRG_RCX }, ++ { 0x58, BBRG_RAX }, ++ { 0x50, BBRG_R8 }, ++ { 0x48, BBRG_R9 }, ++ { 0x40, BBRG_R10 }, ++ { 0x38, BBRG_R11 }, ++ { 0x30, BBRG_RBX }, ++ { 0x28, BBRG_RBP }, ++ { 0x20, BBRG_R12 }, ++ { 0x18, BBRG_R13 }, ++ { 0x10, BBRG_R14 }, ++ { 0x08, BBRG_R15 }, ++}; ++/* ++ * Going into error_exit we have the hardware pushed error_code on the stack ++ * plus a full pt_regs ++ */ ++static const struct bb_mem_contains error_code_full_pt_regs[] = { ++ { 0x78, BBRG_UNDEFINED }, ++ { 0x70, BBRG_RDI }, ++ { 0x68, BBRG_RSI }, ++ { 0x60, BBRG_RDX }, ++ { 0x58, BBRG_RCX }, ++ { 0x50, BBRG_RAX }, ++ { 0x48, BBRG_R8 }, ++ { 0x40, BBRG_R9 }, ++ { 0x38, BBRG_R10 }, ++ { 0x30, BBRG_R11 }, ++ { 0x28, BBRG_RBX }, ++ { 0x20, BBRG_RBP }, ++ { 0x18, BBRG_R12 }, ++ { 0x10, BBRG_R13 }, ++ { 0x08, BBRG_R14 }, ++ { 0x00, BBRG_R15 }, ++}; ++static const struct bb_mem_contains partial_pt_regs[] = { ++ { 0x40, BBRG_RDI }, ++ { 0x38, BBRG_RSI }, ++ { 0x30, BBRG_RDX }, ++ { 0x28, BBRG_RCX }, ++ { 0x20, BBRG_RAX }, ++ { 0x18, BBRG_R8 }, ++ { 0x10, BBRG_R9 }, ++ { 0x08, BBRG_R10 }, ++ { 0x00, BBRG_R11 }, ++}; ++static const struct bb_mem_contains partial_pt_regs_plus_1[] = { ++ { 0x48, BBRG_RDI }, ++ { 0x40, BBRG_RSI }, ++ { 0x38, BBRG_RDX }, ++ { 0x30, BBRG_RCX }, ++ { 0x28, BBRG_RAX }, ++ { 0x20, BBRG_R8 }, ++ { 0x18, BBRG_R9 }, ++ { 0x10, BBRG_R10 }, ++ { 0x08, BBRG_R11 }, ++}; ++static const struct bb_mem_contains partial_pt_regs_plus_2[] = { ++ { 0x50, BBRG_RDI }, ++ { 0x48, BBRG_RSI }, ++ { 0x40, BBRG_RDX }, ++ { 0x38, BBRG_RCX }, ++ { 0x30, BBRG_RAX }, ++ { 0x28, BBRG_R8 }, ++ { 0x20, BBRG_R9 }, ++ { 0x18, BBRG_R10 }, ++ { 0x10, BBRG_R11 }, ++}; ++static const struct bb_mem_contains no_memory[] = { ++}; ++/* Hardware has already pushed an error_code on the stack. Use undefined just ++ * to set the initial stack offset. ++ */ ++static const struct bb_mem_contains error_code[] = { ++ { 0x0, BBRG_UNDEFINED }, ++}; ++/* error_code plus original rax */ ++static const struct bb_mem_contains error_code_rax[] = { ++ { 0x8, BBRG_UNDEFINED }, ++ { 0x0, BBRG_RAX }, ++}; ++ ++static const struct bb_reg_contains all_regs[] = { ++ [BBRG_RAX] = { BBRG_RAX, 0 }, ++ [BBRG_RBX] = { BBRG_RBX, 0 }, ++ [BBRG_RCX] = { BBRG_RCX, 0 }, ++ [BBRG_RDX] = { BBRG_RDX, 0 }, ++ [BBRG_RDI] = { BBRG_RDI, 0 }, ++ [BBRG_RSI] = { BBRG_RSI, 0 }, ++ [BBRG_RBP] = { BBRG_RBP, 0 }, ++ [BBRG_RSP] = { BBRG_OSP, 0 }, ++ [BBRG_R8 ] = { BBRG_R8, 0 }, ++ [BBRG_R9 ] = { BBRG_R9, 0 }, ++ [BBRG_R10] = { BBRG_R10, 0 }, ++ [BBRG_R11] = { BBRG_R11, 0 }, ++ [BBRG_R12] = { BBRG_R12, 0 }, ++ [BBRG_R13] = { BBRG_R13, 0 }, ++ [BBRG_R14] = { BBRG_R14, 0 }, ++ [BBRG_R15] = { BBRG_R15, 0 }, ++}; ++static const struct bb_reg_contains no_regs[] = { ++}; ++ ++static struct bb_name_state bb_special_cases[] = { ++ ++ /* First the cases that pass data only in memory. We do not check any ++ * register state for these cases. ++ */ ++ ++ /* Simple cases, no exceptions */ ++ NS_MEM("ia32_ptregs_common", partial_pt_regs_plus_1, 0), ++ NS_MEM("ia32_sysret", partial_pt_regs, 0), ++ NS_MEM("int_careful", partial_pt_regs, 0), ++ NS_MEM("ia32_badarg", partial_pt_regs, 0), ++ NS_MEM("int_restore_rest", full_pt_regs, 0), ++ NS_MEM("int_signal", full_pt_regs, 0), ++ NS_MEM("int_very_careful", partial_pt_regs, 0), ++ NS_MEM("ptregscall_common", full_pt_regs_plus_1, 0), ++ NS_MEM("ret_from_intr", partial_pt_regs_plus_2, 0), ++ NS_MEM("stub32_clone", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub32_execve", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub32_fork", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub32_iopl", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub32_rt_sigreturn", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub32_sigaltstack", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub32_sigreturn", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub32_vfork", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub_clone", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub_execve", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub_fork", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub_iopl", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub_rt_sigreturn", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub_sigaltstack", partial_pt_regs_plus_1, 0), ++ NS_MEM("stub_vfork", partial_pt_regs_plus_1, 0), ++ NS_MEM("sysenter_auditsys", partial_pt_regs, ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11)), ++ ++ NS_MEM("paranoid_exit", error_code_full_pt_regs, 0), ++ ++ NS_MEM_FROM("ia32_badsys", "ia32_sysenter_target", ++ partial_pt_regs, ++ /* ia32_sysenter_target uses CLEAR_RREGS to clear R8-R11 on ++ * some paths. It also stomps on RAX. ++ */ ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11) | ++ BB_SKIP(RAX)), ++ NS_MEM_FROM("ia32_badsys", "ia32_cstar_target", ++ partial_pt_regs, ++ /* ia32_cstar_target uses CLEAR_RREGS to clear R8-R11 on some ++ * paths. It also stomps on RAX. Even more confusing, instead ++ * of storing RCX it stores RBP. WTF? ++ */ ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11) | ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ NS_MEM_FROM("ia32_badsys", "ia32_syscall", ++ partial_pt_regs, ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11)), ++ NS_MEM("ia32_badsys", partial_pt_regs, 0), ++ ++#ifdef CONFIG_AUDITSYSCALL ++ NS_MEM_FROM("int_with_check", "sysexit_audit", partial_pt_regs, ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11) | ++ BB_SKIP(RAX)), ++ NS_MEM_FROM("int_with_check", "ia32_cstar_target", partial_pt_regs, ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11) | ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++#endif ++ NS_MEM("int_with_check", no_memory, 0), ++ ++ /* Various bits of code branch to int_ret_from_sys_call, with slightly ++ * different missing values in pt_regs. ++ */ ++ NS_MEM_FROM("int_ret_from_sys_call", "ret_from_fork", ++ partial_pt_regs, ++ BB_SKIP(R11)), ++ NS_MEM_FROM("int_ret_from_sys_call", "stub_execve", ++ partial_pt_regs, ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ NS_MEM_FROM("int_ret_from_sys_call", "stub_rt_sigreturn", ++ partial_pt_regs, ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ NS_MEM_FROM("int_ret_from_sys_call", "kernel_execve", ++ partial_pt_regs, ++ BB_SKIP(RAX)), ++ NS_MEM_FROM("int_ret_from_sys_call", "ia32_syscall", ++ partial_pt_regs, ++ /* ia32_syscall only saves RDI through RCX. */ ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11) | ++ BB_SKIP(RAX)), ++ NS_MEM_FROM("int_ret_from_sys_call", "ia32_sysenter_target", ++ partial_pt_regs, ++ /* ia32_sysenter_target uses CLEAR_RREGS to clear R8-R11 on ++ * some paths. It also stomps on RAX. ++ */ ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11) | ++ BB_SKIP(RAX)), ++ NS_MEM_FROM("int_ret_from_sys_call", "ia32_cstar_target", ++ partial_pt_regs, ++ /* ia32_cstar_target uses CLEAR_RREGS to clear R8-R11 on some ++ * paths. It also stomps on RAX. Even more confusing, instead ++ * of storing RCX it stores RBP. WTF? ++ */ ++ BB_SKIP(R8) | BB_SKIP(R9) | BB_SKIP(R10) | BB_SKIP(R11) | ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ NS_MEM_FROM("int_ret_from_sys_call", "ia32_badsys", ++ partial_pt_regs, BB_SKIP(RAX)), ++ NS_MEM("int_ret_from_sys_call", partial_pt_regs, 0), ++ ++#ifdef CONFIG_PREEMPT ++ NS_MEM("retint_kernel", partial_pt_regs, BB_SKIP(RAX)), ++#endif /* CONFIG_PREEMPT */ ++ ++ NS_MEM("retint_careful", partial_pt_regs, BB_SKIP(RAX)), ++ ++ /* Horrible hack: For a brand new x86_64 task, switch_to() branches to ++ * ret_from_fork with a totally different stack state from all the ++ * other tasks that come out of switch_to(). This non-standard state ++ * cannot be represented so just ignore the branch from switch_to() to ++ * ret_from_fork. Due to inlining and linker labels, switch_to() can ++ * appear as several different function labels, including schedule, ++ * context_switch and __sched_text_start. ++ */ ++ NS_MEM_FROM("ret_from_fork", "schedule", no_memory, 0), ++ NS_MEM_FROM("ret_from_fork", "__schedule", no_memory, 0), ++ NS_MEM_FROM("ret_from_fork", "__sched_text_start", no_memory, 0), ++ NS_MEM_FROM("ret_from_fork", "context_switch", no_memory, 0), ++ NS_MEM("ret_from_fork", full_pt_regs, 0), ++ ++ NS_MEM_FROM("ret_from_sys_call", "ret_from_fork", ++ partial_pt_regs, ++ BB_SKIP(R11)), ++ NS_MEM("ret_from_sys_call", partial_pt_regs, 0), ++ ++ NS_MEM("retint_restore_args", ++ partial_pt_regs, ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ ++ NS_MEM("retint_swapgs", ++ partial_pt_regs, ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ ++ /* Now the cases that pass data in registers. We do not check any ++ * memory state for these cases. ++ */ ++ ++ NS_REG("bad_put_user", ++ all_regs, BB_SKIP(RBX)), ++ ++ NS_REG("bad_get_user", ++ all_regs, BB_SKIP(RAX) | BB_SKIP(RDX)), ++ ++ NS_REG("bad_to_user", ++ all_regs, ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ ++ NS_REG("ia32_ptregs_common", ++ all_regs, ++ 0), ++ ++ NS_REG("copy_user_generic_unrolled", ++ all_regs, ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ ++ NS_REG("copy_user_generic_string", ++ all_regs, ++ BB_SKIP(RAX) | BB_SKIP(RCX)), ++ ++ NS_REG("irq_return", ++ all_regs, ++ 0), ++ ++ /* Finally the cases that pass data in both registers and memory. ++ */ ++ ++ NS("invalid_TSS", error_code, all_regs, 0, 0, 0), ++ NS("segment_not_present", error_code, all_regs, 0, 0, 0), ++ NS("alignment_check", error_code, all_regs, 0, 0, 0), ++ NS("page_fault", error_code, all_regs, 0, 0, 0), ++ NS("general_protection", error_code, all_regs, 0, 0, 0), ++ NS("error_entry", error_code_rax, all_regs, 0, BB_SKIP(RAX), -0x10), ++ NS("error_exit", error_code_full_pt_regs, no_regs, 0, 0, 0x30), ++ NS("common_interrupt", error_code, all_regs, 0, 0, -0x8), ++ NS("save_args", error_code, all_regs, 0, 0, -0x50), ++ NS("int3", no_memory, all_regs, 0, 0, -0x80), ++}; ++ ++static const char *bb_spurious[] = { ++ /* schedule */ ++ "thread_return", ++ /* system_call */ ++ "system_call_after_swapgs", ++ "system_call_fastpath", ++ "ret_from_sys_call", ++ "sysret_check", ++ "sysret_careful", ++ "sysret_signal", ++ "badsys", ++#ifdef CONFIG_AUDITSYSCALL ++ "auditsys", ++ "sysret_audit", ++#endif ++ "tracesys", ++ "int_ret_from_sys_call", ++ "int_with_check", ++ "int_careful", ++ "int_very_careful", ++ "int_signal", ++ "int_restore_rest", ++ /* common_interrupt */ ++ "ret_from_intr", ++ "exit_intr", ++ "retint_with_reschedule", ++ "retint_check", ++ "retint_swapgs", ++ "retint_restore_args", ++ "restore_args", ++ "irq_return", ++ "bad_iret", ++ "retint_careful", ++ "retint_signal", ++#ifdef CONFIG_PREEMPT ++ "retint_kernel", ++#endif /* CONFIG_PREEMPT */ ++ /* paranoid_exit */ ++ "paranoid_swapgs", ++ "paranoid_restore", ++ "paranoid_userspace", ++ "paranoid_schedule", ++ /* error_entry */ ++ "error_swapgs", ++ "error_sti", ++ "error_kernelspace", ++ /* nmi */ ++#ifdef CONFIG_TRACE_IRQFLAGS ++ "nmi_swapgs", ++ "nmi_restore", ++ "nmi_userspace", ++ "nmi_schedule", ++#endif ++ /* load_gs_index */ ++ "gs_change", ++ "bad_gs", ++ /* ia32_sysenter_target */ ++ "sysenter_do_call", ++ "sysenter_dispatch", ++ "sysexit_from_sys_call", ++#ifdef CONFIG_AUDITSYSCALL ++ "sysenter_auditsys", ++ "sysexit_audit", ++#endif ++ "sysenter_tracesys", ++ /* ia32_cstar_target */ ++ "cstar_do_call", ++ "cstar_dispatch", ++ "sysretl_from_sys_call", ++#ifdef CONFIG_AUDITSYSCALL ++ "cstar_auditsys", ++ "sysretl_audit", ++#endif ++ "cstar_tracesys", ++ /* ia32_syscall */ ++ "ia32_do_call", ++ "ia32_sysret", ++ "ia32_tracesys", ++#ifdef CONFIG_HIBERNATION ++ /* restore_image */ ++ "loop", ++ "done", ++#endif /* CONFIG_HIBERNATION */ ++#ifdef CONFIG_KPROBES ++ /* jprobe_return */ ++ "jprobe_return_end", ++ /* kretprobe_trampoline_holder */ ++ "kretprobe_trampoline", ++#endif /* CONFIG_KPROBES */ ++#ifdef CONFIG_KEXEC ++ /* relocate_kernel */ ++ "relocate_new_kernel", ++#endif /* CONFIG_KEXEC */ ++#ifdef CONFIG_XEN ++ /* arch/i386/xen/xen-asm.S */ ++ "xen_irq_enable_direct_end", ++ "xen_irq_disable_direct_end", ++ "xen_save_fl_direct_end", ++ "xen_restore_fl_direct_end", ++ "xen_iret_start_crit", ++ "iret_restore_end", ++ "xen_iret_end_crit", ++ "hyper_iret", ++#endif /* CONFIG_XEN */ ++}; ++ ++static const char *bb_hardware_handlers[] = { ++ "system_call", ++ "common_interrupt", ++ "error_entry", ++ "debug", ++ "nmi", ++ "int3", ++ "double_fault", ++ "stack_segment", ++ "machine_check", ++ "kdb_call", ++}; ++ ++static int ++bb_hardware_pushed_arch(kdb_machreg_t rsp, ++ const struct kdb_activation_record *ar) ++{ ++ /* x86_64 interrupt stacks are 16 byte aligned and you must get the ++ * next rsp from stack, it cannot be statically calculated. Do not ++ * include the word at rsp, it is pushed by hardware but is treated as ++ * a normal software return value. ++ * ++ * When an IST switch occurs (e.g. NMI) then the saved rsp points to ++ * another stack entirely. Assume that the IST stack is 16 byte ++ * aligned and just return the size of the hardware data on this stack. ++ * The stack unwind code will take care of the stack switch. ++ */ ++ kdb_machreg_t saved_rsp = *((kdb_machreg_t *)rsp + 3); ++ int hardware_pushed = saved_rsp - rsp - KDB_WORD_SIZE; ++ if (hardware_pushed < 4 * KDB_WORD_SIZE || ++ saved_rsp < ar->stack.logical_start || ++ saved_rsp >= ar->stack.logical_end) ++ return 4 * KDB_WORD_SIZE; ++ else ++ return hardware_pushed; ++} ++ ++static void ++bb_start_block0(void) ++{ ++ bb_reg_code_set_value(BBRG_RAX, BBRG_RAX); ++ bb_reg_code_set_value(BBRG_RBX, BBRG_RBX); ++ bb_reg_code_set_value(BBRG_RCX, BBRG_RCX); ++ bb_reg_code_set_value(BBRG_RDX, BBRG_RDX); ++ bb_reg_code_set_value(BBRG_RDI, BBRG_RDI); ++ bb_reg_code_set_value(BBRG_RSI, BBRG_RSI); ++ bb_reg_code_set_value(BBRG_RBP, BBRG_RBP); ++ bb_reg_code_set_value(BBRG_RSP, BBRG_OSP); ++ bb_reg_code_set_value(BBRG_R8, BBRG_R8); ++ bb_reg_code_set_value(BBRG_R9, BBRG_R9); ++ bb_reg_code_set_value(BBRG_R10, BBRG_R10); ++ bb_reg_code_set_value(BBRG_R11, BBRG_R11); ++ bb_reg_code_set_value(BBRG_R12, BBRG_R12); ++ bb_reg_code_set_value(BBRG_R13, BBRG_R13); ++ bb_reg_code_set_value(BBRG_R14, BBRG_R14); ++ bb_reg_code_set_value(BBRG_R15, BBRG_R15); ++} ++ ++/* x86_64 does not have a special case for __switch_to */ ++ ++static void ++bb_fixup_switch_to(char *p) ++{ ++} ++ ++static int ++bb_asmlinkage_arch(void) ++{ ++ return strncmp(bb_func_name, "__down", 6) == 0 || ++ strncmp(bb_func_name, "__up", 4) == 0 || ++ strncmp(bb_func_name, "stub_", 5) == 0 || ++ strcmp(bb_func_name, "ret_from_fork") == 0 || ++ strcmp(bb_func_name, "ptregscall_common") == 0; ++} ++ ++#else /* !CONFIG_X86_64 */ ++ ++/* Registers that can be used to pass parameters, in the order that parameters ++ * are passed. ++ */ ++ ++const static enum bb_reg_code ++bb_param_reg[] = { ++ BBRG_RAX, ++ BBRG_RDX, ++ BBRG_RCX, ++}; ++ ++const static enum bb_reg_code ++bb_preserved_reg[] = { ++ BBRG_RBX, ++ BBRG_RBP, ++ BBRG_RSP, ++ BBRG_RSI, ++ BBRG_RDI, ++}; ++ ++static const struct bb_mem_contains full_pt_regs[] = { ++ { 0x18, BBRG_RAX }, ++ { 0x14, BBRG_RBP }, ++ { 0x10, BBRG_RDI }, ++ { 0x0c, BBRG_RSI }, ++ { 0x08, BBRG_RDX }, ++ { 0x04, BBRG_RCX }, ++ { 0x00, BBRG_RBX }, ++}; ++static const struct bb_mem_contains no_memory[] = { ++}; ++/* Hardware has already pushed an error_code on the stack. Use undefined just ++ * to set the initial stack offset. ++ */ ++static const struct bb_mem_contains error_code[] = { ++ { 0x0, BBRG_UNDEFINED }, ++}; ++/* rbx already pushed */ ++static const struct bb_mem_contains rbx_pushed[] = { ++ { 0x0, BBRG_RBX }, ++}; ++#ifdef CONFIG_MATH_EMULATION ++static const struct bb_mem_contains mem_fpu_reg_round[] = { ++ { 0xc, BBRG_RBP }, ++ { 0x8, BBRG_RSI }, ++ { 0x4, BBRG_RDI }, ++ { 0x0, BBRG_RBX }, ++}; ++#endif /* CONFIG_MATH_EMULATION */ ++ ++static const struct bb_reg_contains all_regs[] = { ++ [BBRG_RAX] = { BBRG_RAX, 0 }, ++ [BBRG_RBX] = { BBRG_RBX, 0 }, ++ [BBRG_RCX] = { BBRG_RCX, 0 }, ++ [BBRG_RDX] = { BBRG_RDX, 0 }, ++ [BBRG_RDI] = { BBRG_RDI, 0 }, ++ [BBRG_RSI] = { BBRG_RSI, 0 }, ++ [BBRG_RBP] = { BBRG_RBP, 0 }, ++ [BBRG_RSP] = { BBRG_OSP, 0 }, ++}; ++static const struct bb_reg_contains no_regs[] = { ++}; ++#ifdef CONFIG_MATH_EMULATION ++static const struct bb_reg_contains reg_fpu_reg_round[] = { ++ [BBRG_RBP] = { BBRG_OSP, -0x4 }, ++ [BBRG_RSP] = { BBRG_OSP, -0x10 }, ++}; ++#endif /* CONFIG_MATH_EMULATION */ ++ ++static struct bb_name_state bb_special_cases[] = { ++ ++ /* First the cases that pass data only in memory. We do not check any ++ * register state for these cases. ++ */ ++ ++ /* Simple cases, no exceptions */ ++ NS_MEM("check_userspace", full_pt_regs, 0), ++ NS_MEM("device_not_available_emulate", full_pt_regs, 0), ++ NS_MEM("ldt_ss", full_pt_regs, 0), ++ NS_MEM("no_singlestep", full_pt_regs, 0), ++ NS_MEM("restore_all", full_pt_regs, 0), ++ NS_MEM("restore_nocheck", full_pt_regs, 0), ++ NS_MEM("restore_nocheck_notrace", full_pt_regs, 0), ++ NS_MEM("ret_from_exception", full_pt_regs, 0), ++ NS_MEM("ret_from_fork", full_pt_regs, 0), ++ NS_MEM("ret_from_intr", full_pt_regs, 0), ++ NS_MEM("work_notifysig", full_pt_regs, 0), ++ NS_MEM("work_pending", full_pt_regs, 0), ++ ++#ifdef CONFIG_PREEMPT ++ NS_MEM("resume_kernel", full_pt_regs, 0), ++#endif /* CONFIG_PREEMPT */ ++ ++ NS_MEM("common_interrupt", error_code, 0), ++ NS_MEM("error_code", error_code, 0), ++ ++ NS_MEM("bad_put_user", rbx_pushed, 0), ++ ++ NS_MEM_FROM("resume_userspace", "syscall_badsys", ++ full_pt_regs, BB_SKIP(RAX)), ++ NS_MEM_FROM("resume_userspace", "syscall_fault", ++ full_pt_regs, BB_SKIP(RAX)), ++ NS_MEM_FROM("resume_userspace", "syscall_trace_entry", ++ full_pt_regs, BB_SKIP(RAX)), ++ /* Too difficult to trace through the various vm86 functions for now. ++ * They are C functions that start off with some memory state, fiddle ++ * the registers then jmp directly to resume_userspace. For the ++ * moment, just assume that they are valid and do no checks. ++ */ ++ NS_FROM("resume_userspace", "do_int", ++ no_memory, no_regs, 0, 0, 0), ++ NS_FROM("resume_userspace", "do_sys_vm86", ++ no_memory, no_regs, 0, 0, 0), ++ NS_FROM("resume_userspace", "handle_vm86_fault", ++ no_memory, no_regs, 0, 0, 0), ++ NS_FROM("resume_userspace", "handle_vm86_trap", ++ no_memory, no_regs, 0, 0, 0), ++ NS_MEM("resume_userspace", full_pt_regs, 0), ++ ++ NS_MEM_FROM("syscall_badsys", "ia32_sysenter_target", ++ full_pt_regs, BB_SKIP(RBP)), ++ NS_MEM("syscall_badsys", full_pt_regs, 0), ++ ++ NS_MEM_FROM("syscall_call", "syscall_trace_entry", ++ full_pt_regs, BB_SKIP(RAX)), ++ NS_MEM("syscall_call", full_pt_regs, 0), ++ ++ NS_MEM_FROM("syscall_exit", "syscall_trace_entry", ++ full_pt_regs, BB_SKIP(RAX)), ++ NS_MEM("syscall_exit", full_pt_regs, 0), ++ ++ NS_MEM_FROM("syscall_exit_work", "ia32_sysenter_target", ++ full_pt_regs, BB_SKIP(RAX) | BB_SKIP(RBP)), ++ NS_MEM_FROM("syscall_exit_work", "system_call", ++ full_pt_regs, BB_SKIP(RAX)), ++ NS_MEM("syscall_exit_work", full_pt_regs, 0), ++ ++ NS_MEM_FROM("syscall_trace_entry", "ia32_sysenter_target", ++ full_pt_regs, BB_SKIP(RBP)), ++ NS_MEM_FROM("syscall_trace_entry", "system_call", ++ full_pt_regs, BB_SKIP(RAX)), ++ NS_MEM("syscall_trace_entry", full_pt_regs, 0), ++ ++ /* Now the cases that pass data in registers. We do not check any ++ * memory state for these cases. ++ */ ++ ++ NS_REG("syscall_fault", all_regs, 0), ++ ++ NS_REG("bad_get_user", all_regs, ++ BB_SKIP(RAX) | BB_SKIP(RDX)), ++ ++ /* Finally the cases that pass data in both registers and memory. ++ */ ++ ++ /* This entry is redundant now because bb_fixup_switch_to() hides the ++ * jmp __switch_to case, however the entry is left here as ++ * documentation. ++ * ++ * NS("__switch_to", no_memory, no_regs, 0, 0, 0), ++ */ ++ ++ NS("iret_exc", no_memory, all_regs, 0, 0, 0x20), ++ ++#ifdef CONFIG_MATH_EMULATION ++ NS("fpu_reg_round", mem_fpu_reg_round, reg_fpu_reg_round, 0, 0, 0), ++#endif /* CONFIG_MATH_EMULATION */ ++}; ++ ++static const char *bb_spurious[] = { ++ /* ret_from_exception */ ++ "ret_from_intr", ++ "check_userspace", ++ "resume_userspace", ++ /* resume_kernel */ ++#ifdef CONFIG_PREEMPT ++ "need_resched", ++#endif /* CONFIG_PREEMPT */ ++ /* ia32_sysenter_target */ ++ "sysenter_past_esp", ++ /* system_call */ ++ "no_singlestep", ++ "syscall_call", ++ "syscall_exit", ++ "restore_all", ++ "restore_nocheck", ++ "restore_nocheck_notrace", ++ "ldt_ss", ++ /* do not include iret_exc, it is in a .fixup section */ ++ /* work_pending */ ++ "work_resched", ++ "work_notifysig", ++#ifdef CONFIG_VM86 ++ "work_notifysig_v86", ++#endif /* CONFIG_VM86 */ ++ /* page_fault */ ++ "error_code", ++ /* device_not_available */ ++ "device_not_available_emulate", ++ /* debug */ ++ "debug_esp_fix_insn", ++ "debug_stack_correct", ++ /* nmi */ ++ "nmi_stack_correct", ++ "nmi_stack_fixup", ++ "nmi_debug_stack_check", ++ "nmi_espfix_stack", ++#ifdef CONFIG_HIBERNATION ++ /* restore_image */ ++ "copy_loop", ++ "done", ++#endif /* CONFIG_HIBERNATION */ ++#ifdef CONFIG_KPROBES ++ /* jprobe_return */ ++ "jprobe_return_end", ++#endif /* CONFIG_KPROBES */ ++#ifdef CONFIG_KEXEC ++ /* relocate_kernel */ ++ "relocate_new_kernel", ++#endif /* CONFIG_KEXEC */ ++#ifdef CONFIG_MATH_EMULATION ++ /* assorted *.S files in arch/i386/math_emu */ ++ "Denorm_done", ++ "Denorm_shift_more_than_32", ++ "Denorm_shift_more_than_63", ++ "Denorm_shift_more_than_64", ++ "Do_unmasked_underflow", ++ "Exp_not_underflow", ++ "fpu_Arith_exit", ++ "fpu_reg_round", ++ "fpu_reg_round_signed_special_exit", ++ "fpu_reg_round_special_exit", ++ "L_accum_done", ++ "L_accum_loaded", ++ "L_accum_loop", ++ "L_arg1_larger", ++ "L_bugged", ++ "L_bugged_1", ++ "L_bugged_2", ++ "L_bugged_3", ++ "L_bugged_4", ++ "L_bugged_denorm_486", ++ "L_bugged_round24", ++ "L_bugged_round53", ++ "L_bugged_round64", ++ "LCheck_24_round_up", ++ "LCheck_53_round_up", ++ "LCheck_Round_Overflow", ++ "LCheck_truncate_24", ++ "LCheck_truncate_53", ++ "LCheck_truncate_64", ++ "LDenormal_adj_exponent", ++ "L_deNormalised", ++ "LDo_24_round_up", ++ "LDo_2nd_32_bits", ++ "LDo_2nd_div", ++ "LDo_3rd_32_bits", ++ "LDo_3rd_div", ++ "LDo_53_round_up", ++ "LDo_64_round_up", ++ "L_done", ++ "LDo_truncate_24", ++ "LDown_24", ++ "LDown_53", ++ "LDown_64", ++ "L_entry_bugged", ++ "L_error_exit", ++ "L_exactly_32", ++ "L_exception_exit", ++ "L_exit", ++ "L_exit_nuo_valid", ++ "L_exit_nuo_zero", ++ "L_exit_valid", ++ "L_extent_zero", ++ "LFirst_div_done", ++ "LFirst_div_not_1", ++ "L_Full_Division", ++ "LGreater_Half_24", ++ "LGreater_Half_53", ++ "LGreater_than_1", ++ "LLess_than_1", ++ "L_Make_denorm", ++ "L_more_31_no_low", ++ "L_more_63_no_low", ++ "L_more_than_31", ++ "L_more_than_63", ++ "L_more_than_64", ++ "L_more_than_65", ++ "L_more_than_95", ++ "L_must_be_zero", ++ "L_n_exit", ++ "L_no_adjust", ++ "L_no_bit_lost", ++ "L_no_overflow", ++ "L_no_precision_loss", ++ "L_Normalised", ++ "L_norm_bugged", ++ "L_n_shift_1", ++ "L_nuo_shift_1", ++ "L_overflow", ++ "L_precision_lost_down", ++ "L_precision_lost_up", ++ "LPrevent_2nd_overflow", ++ "LPrevent_3rd_overflow", ++ "LPseudoDenormal", ++ "L_Re_normalise", ++ "LResult_Normalised", ++ "L_round", ++ "LRound_large", ++ "LRound_nearest_24", ++ "LRound_nearest_53", ++ "LRound_nearest_64", ++ "LRound_not_small", ++ "LRound_ovfl", ++ "LRound_precision", ++ "LRound_prep", ++ "L_round_the_result", ++ "LRound_To_24", ++ "LRound_To_53", ++ "LRound_To_64", ++ "LSecond_div_done", ++ "LSecond_div_not_1", ++ "L_shift_1", ++ "L_shift_32", ++ "L_shift_65_nc", ++ "L_shift_done", ++ "Ls_less_than_32", ++ "Ls_more_than_63", ++ "Ls_more_than_95", ++ "L_Store_significand", ++ "L_subtr", ++ "LTest_over", ++ "LTruncate_53", ++ "LTruncate_64", ++ "L_underflow", ++ "L_underflow_to_zero", ++ "LUp_24", ++ "LUp_53", ++ "LUp_64", ++ "L_zero", ++ "Normalise_result", ++ "Signal_underflow", ++ "sqrt_arg_ge_2", ++ "sqrt_get_more_precision", ++ "sqrt_more_prec_large", ++ "sqrt_more_prec_ok", ++ "sqrt_more_prec_small", ++ "sqrt_near_exact", ++ "sqrt_near_exact_large", ++ "sqrt_near_exact_ok", ++ "sqrt_near_exact_small", ++ "sqrt_near_exact_x", ++ "sqrt_prelim_no_adjust", ++ "sqrt_round_result", ++ "sqrt_stage_2_done", ++ "sqrt_stage_2_error", ++ "sqrt_stage_2_finish", ++ "sqrt_stage_2_positive", ++ "sqrt_stage_3_error", ++ "sqrt_stage_3_finished", ++ "sqrt_stage_3_no_error", ++ "sqrt_stage_3_positive", ++ "Unmasked_underflow", ++ "xExp_not_underflow", ++#endif /* CONFIG_MATH_EMULATION */ ++}; ++ ++static const char *bb_hardware_handlers[] = { ++ "ret_from_exception", ++ "system_call", ++ "work_pending", ++ "syscall_fault", ++ "page_fault", ++ "coprocessor_error", ++ "simd_coprocessor_error", ++ "device_not_available", ++ "debug", ++ "nmi", ++ "int3", ++ "overflow", ++ "bounds", ++ "invalid_op", ++ "coprocessor_segment_overrun", ++ "invalid_TSS", ++ "segment_not_present", ++ "stack_segment", ++ "general_protection", ++ "alignment_check", ++ "kdb_call", ++ "divide_error", ++ "machine_check", ++ "spurious_interrupt_bug", ++}; ++ ++static int ++bb_hardware_pushed_arch(kdb_machreg_t rsp, ++ const struct kdb_activation_record *ar) ++{ ++ return (2 * KDB_WORD_SIZE); ++} ++ ++static void ++bb_start_block0(void) ++{ ++ bb_reg_code_set_value(BBRG_RAX, BBRG_RAX); ++ bb_reg_code_set_value(BBRG_RBX, BBRG_RBX); ++ bb_reg_code_set_value(BBRG_RCX, BBRG_RCX); ++ bb_reg_code_set_value(BBRG_RDX, BBRG_RDX); ++ bb_reg_code_set_value(BBRG_RDI, BBRG_RDI); ++ bb_reg_code_set_value(BBRG_RSI, BBRG_RSI); ++ bb_reg_code_set_value(BBRG_RBP, BBRG_RBP); ++ bb_reg_code_set_value(BBRG_RSP, BBRG_OSP); ++} ++ ++/* The i386 code that switches stack in a context switch is an extremely ++ * special case. It saves the rip pointing to a label that is not otherwise ++ * referenced, saves the current rsp then pushes a word. The magic code that ++ * resumes the new task picks up the saved rip and rsp, effectively referencing ++ * a label that otherwise is not used and ignoring the pushed word. ++ * ++ * The simplest way to handle this very strange case is to recognise jmp ++ * address <__switch_to> and treat it as a popfl instruction. This avoids ++ * terminating the block on this jmp and removes one word from the stack state, ++ * which is the end effect of all the magic code. ++ * ++ * Called with the instruction line, starting after the first ':'. ++ */ ++ ++static void ++bb_fixup_switch_to(char *p) ++{ ++ char *p1 = p; ++ p += strspn(p, " \t"); /* start of instruction */ ++ if (strncmp(p, "jmp", 3)) ++ return; ++ p += strcspn(p, " \t"); /* end of instruction */ ++ p += strspn(p, " \t"); /* start of address */ ++ p += strcspn(p, " \t"); /* end of address */ ++ p += strspn(p, " \t"); /* start of comment */ ++ if (strcmp(p, "<__switch_to>") == 0) ++ strcpy(p1, "popfl"); ++} ++ ++static int ++bb_asmlinkage_arch(void) ++{ ++ return strcmp(bb_func_name, "ret_from_exception") == 0 || ++ strcmp(bb_func_name, "syscall_trace_entry") == 0; ++} ++ ++#endif /* CONFIG_X86_64 */ ++ ++ ++/*============================================================================*/ ++/* */ ++/* Common code and data. */ ++/* */ ++/*============================================================================*/ ++ ++ ++/* Tracking registers by decoding the instructions is quite a bit harder than ++ * doing the same tracking using compiler generated information. Register ++ * contents can remain in the same register, they can be copied to other ++ * registers, they can be stored on stack or they can be modified/overwritten. ++ * At any one time, there are 0 or more copies of the original value that was ++ * supplied in each register on input to the current function. If a register ++ * exists in multiple places, one copy of that register is the master version, ++ * the others are temporary copies which may or may not be destroyed before the ++ * end of the function. ++ * ++ * The compiler knows which copy of a register is the master and which are ++ * temporary copies, which makes it relatively easy to track register contents ++ * as they are saved and restored. Without that compiler based knowledge, this ++ * code has to track _every_ possible copy of each register, simply because we ++ * do not know which is the master copy and which are temporary copies which ++ * may be destroyed later. ++ * ++ * It gets worse: registers that contain parameters can be copied to other ++ * registers which are then saved on stack in a lower level function. Also the ++ * stack pointer may be held in multiple registers (typically RSP and RBP) ++ * which contain different offsets from the base of the stack on entry to this ++ * function. All of which means that we have to track _all_ register ++ * movements, or at least as much as possible. ++ * ++ * Start with the basic block that contains the start of the function, by ++ * definition all registers contain their initial value. Track each ++ * instruction's effect on register contents, this includes reading from a ++ * parameter register before any write to that register, IOW the register ++ * really does contain a parameter. The register state is represented by a ++ * dynamically sized array with each entry containing :- ++ * ++ * Register name ++ * Location it is copied to (another register or stack + offset) ++ * ++ * Besides the register tracking array, we track which parameter registers are ++ * read before being written, to determine how many parameters are passed in ++ * registers. We also track which registers contain stack pointers, including ++ * their offset from the original stack pointer on entry to the function. ++ * ++ * At each exit from the current basic block (via JMP instruction or drop ++ * through), the register state is cloned to form the state on input to the ++ * target basic block and the target is marked for processing using this state. ++ * When there are multiple ways to enter a basic block (e.g. several JMP ++ * instructions referencing the same target) then there will be multiple sets ++ * of register state to form the "input" for that basic block, there is no ++ * guarantee that all paths to that block will have the same register state. ++ * ++ * As each target block is processed, all the known sets of register state are ++ * merged to form a suitable subset of the state which agrees with all the ++ * inputs. The most common case is where one path to this block copies a ++ * register to another register but another path does not, therefore the copy ++ * is only a temporary and should not be propogated into this block. ++ * ++ * If the target block already has an input state from the current transfer ++ * point and the new input state is identical to the previous input state then ++ * we have reached a steady state for the arc from the current location to the ++ * target block. Therefore there is no need to process the target block again. ++ * ++ * The steps of "process a block, create state for target block(s), pick a new ++ * target block, merge state for target block, process target block" will ++ * continue until all the state changes have propogated all the way down the ++ * basic block tree, including round any cycles in the tree. The merge step ++ * only deletes tracking entries from the input state(s), it never adds a ++ * tracking entry. Therefore the overall algorithm is guaranteed to converge ++ * to a steady state, the worst possible case is that every tracking entry into ++ * a block is deleted, which will result in an empty output state. ++ * ++ * As each instruction is decoded, it is checked to see if this is the point at ++ * which execution left this function. This can be a call to another function ++ * (actually the return address to this function) or is the instruction which ++ * was about to be executed when an interrupt occurred (including an oops). ++ * Save the register state at this point. ++ * ++ * We always know what the registers contain when execution left this function. ++ * For an interrupt, the registers are in struct pt_regs. For a call to ++ * another function, we have already deduced the register state on entry to the ++ * other function by unwinding to the start of that function. Given the ++ * register state on exit from this function plus the known register contents ++ * on entry to the next function, we can determine the stack pointer value on ++ * input to this function. That in turn lets us calculate the address of input ++ * registers that have been stored on stack, giving us the input parameters. ++ * Finally the stack pointer gives us the return address which is the exit ++ * point from the calling function, repeat the unwind process on that function. ++ * ++ * The data that tracks which registers contain input parameters is function ++ * global, not local to any basic block. To determine which input registers ++ * contain parameters, we have to decode the entire function. Otherwise an ++ * exit early in the function might not have read any parameters yet. ++ */ ++ ++/* Record memory contents in terms of the values that were passed to this ++ * function, IOW track which memory locations contain an input value. A memory ++ * location's contents can be undefined, it can contain an input register value ++ * or it can contain an offset from the original stack pointer. ++ * ++ * This structure is used to record register contents that have been stored in ++ * memory. Location (BBRG_OSP + 'offset_address') contains the input value ++ * from register 'value'. When 'value' is BBRG_OSP then offset_value contains ++ * the offset from the original stack pointer that was stored in this memory ++ * location. When 'value' is not BBRG_OSP then the memory location contains ++ * the original contents of an input register and offset_value is ignored. ++ * ++ * An input register 'value' can be stored in more than one register and/or in ++ * more than one memory location. ++ */ ++ ++struct bb_memory_contains ++{ ++ short offset_address; ++ enum bb_reg_code value: 8; ++ short offset_value; ++}; ++ ++/* Track the register state in each basic block. */ ++ ++struct bb_reg_state ++{ ++ /* Indexed by register value 'reg - BBRG_RAX' */ ++ struct bb_reg_contains contains[KDB_INT_REGISTERS]; ++ int ref_count; ++ int mem_count; ++ /* dynamic size for memory locations, see mem_count */ ++ struct bb_memory_contains memory[0]; ++}; ++ ++static struct bb_reg_state *bb_reg_state, *bb_exit_state; ++static int bb_reg_state_max, bb_reg_params, bb_memory_params; ++ ++struct bb_actual ++{ ++ bfd_vma value; ++ int valid; ++}; ++ ++/* Contains the actual hex value of a register, plus a valid bit. Indexed by ++ * register value 'reg - BBRG_RAX' ++ */ ++static struct bb_actual bb_actual[KDB_INT_REGISTERS]; ++ ++static bfd_vma bb_func_start, bb_func_end; ++static bfd_vma bb_common_interrupt, bb_error_entry, bb_ret_from_intr, ++ bb_thread_return, bb_sync_regs, bb_save_v86_state, ++ bb__sched_text_start, bb__sched_text_end, ++ bb_save_args, bb_save_rest, bb_save_paranoid; ++ ++/* Record jmp instructions, both conditional and unconditional. These form the ++ * arcs between the basic blocks. This is also used to record the state when ++ * one block drops through into the next. ++ * ++ * A bb can have multiple associated bb_jmp entries, one for each jcc ++ * instruction plus at most one bb_jmp for the drop through case. If a bb ++ * drops through to the next bb then the drop through bb_jmp entry will be the ++ * last entry in the set of bb_jmp's that are associated with the bb. This is ++ * enforced by the fact that jcc entries are added during the disassembly phase ++ * of pass 1, the drop through entries are added near the end of pass 1. ++ * ++ * At address 'from' in this block, we have a jump to address 'to'. The ++ * register state at 'from' is copied to the target block. ++ */ ++ ++struct bb_jmp ++{ ++ bfd_vma from; ++ bfd_vma to; ++ struct bb_reg_state *state; ++ unsigned int drop_through: 1; ++}; ++ ++struct bb ++{ ++ bfd_vma start; ++ /* The end address of a basic block is sloppy. It can be the first ++ * byte of the last instruction in the block or it can be the last byte ++ * of the block. ++ */ ++ bfd_vma end; ++ unsigned int changed: 1; ++ unsigned int drop_through: 1; ++}; ++ ++static struct bb **bb_list, *bb_curr; ++static int bb_max, bb_count; ++ ++static struct bb_jmp *bb_jmp_list; ++static int bb_jmp_max, bb_jmp_count; ++ ++/* Add a new bb entry to the list. This does an insert sort. */ ++ ++static struct bb * ++bb_new(bfd_vma order) ++{ ++ int i, j; ++ struct bb *bb, *p; ++ if (bb_giveup) ++ return NULL; ++ if (bb_count == bb_max) { ++ struct bb **bb_list_new; ++ bb_max += 10; ++ bb_list_new = debug_kmalloc(bb_max*sizeof(*bb_list_new), ++ GFP_ATOMIC); ++ if (!bb_list_new) { ++ kdb_printf("\n\n%s: out of debug_kmalloc\n", __FUNCTION__); ++ bb_giveup = 1; ++ return NULL; ++ } ++ memcpy(bb_list_new, bb_list, bb_count*sizeof(*bb_list)); ++ debug_kfree(bb_list); ++ bb_list = bb_list_new; ++ } ++ bb = debug_kmalloc(sizeof(*bb), GFP_ATOMIC); ++ if (!bb) { ++ kdb_printf("\n\n%s: out of debug_kmalloc\n", __FUNCTION__); ++ bb_giveup = 1; ++ return NULL; ++ } ++ memset(bb, 0, sizeof(*bb)); ++ for (i = 0; i < bb_count; ++i) { ++ p = bb_list[i]; ++ if ((p->start && p->start > order) || ++ (p->end && p->end > order)) ++ break; ++ } ++ for (j = bb_count-1; j >= i; --j) ++ bb_list[j+1] = bb_list[j]; ++ bb_list[i] = bb; ++ ++bb_count; ++ return bb; ++} ++ ++/* Add a new bb_jmp entry to the list. This list is not sorted. */ ++ ++static struct bb_jmp * ++bb_jmp_new(bfd_vma from, bfd_vma to, unsigned int drop_through) ++{ ++ struct bb_jmp *bb_jmp; ++ if (bb_giveup) ++ return NULL; ++ if (bb_jmp_count == bb_jmp_max) { ++ struct bb_jmp *bb_jmp_list_new; ++ bb_jmp_max += 10; ++ bb_jmp_list_new = ++ debug_kmalloc(bb_jmp_max*sizeof(*bb_jmp_list_new), ++ GFP_ATOMIC); ++ if (!bb_jmp_list_new) { ++ kdb_printf("\n\n%s: out of debug_kmalloc\n", ++ __FUNCTION__); ++ bb_giveup = 1; ++ return NULL; ++ } ++ memcpy(bb_jmp_list_new, bb_jmp_list, ++ bb_jmp_count*sizeof(*bb_jmp_list)); ++ debug_kfree(bb_jmp_list); ++ bb_jmp_list = bb_jmp_list_new; ++ } ++ bb_jmp = bb_jmp_list + bb_jmp_count++; ++ bb_jmp->from = from; ++ bb_jmp->to = to; ++ bb_jmp->drop_through = drop_through; ++ bb_jmp->state = NULL; ++ return bb_jmp; ++} ++ ++static void ++bb_delete(int i) ++{ ++ struct bb *bb = bb_list[i]; ++ memcpy(bb_list+i, bb_list+i+1, (bb_count-i-1)*sizeof(*bb_list)); ++ bb_list[--bb_count] = NULL; ++ debug_kfree(bb); ++} ++ ++static struct bb * ++bb_add(bfd_vma start, bfd_vma end) ++{ ++ int i; ++ struct bb *bb; ++ /* Ignore basic blocks whose start address is outside the current ++ * function. These occur for call instructions and for tail recursion. ++ */ ++ if (start && ++ (start < bb_func_start || start >= bb_func_end)) ++ return NULL; ++ for (i = 0; i < bb_count; ++i) { ++ bb = bb_list[i]; ++ if ((start && bb->start == start) || ++ (end && bb->end == end)) ++ return bb; ++ } ++ bb = bb_new(start ? start : end); ++ if (bb) { ++ bb->start = start; ++ bb->end = end; ++ } ++ return bb; ++} ++ ++static struct bb_jmp * ++bb_jmp_add(bfd_vma from, bfd_vma to, unsigned int drop_through) ++{ ++ int i; ++ struct bb_jmp *bb_jmp; ++ for (i = 0, bb_jmp = bb_jmp_list; i < bb_jmp_count; ++i, ++bb_jmp) { ++ if (bb_jmp->from == from && ++ bb_jmp->to == to && ++ bb_jmp->drop_through == drop_through) ++ return bb_jmp; ++ } ++ bb_jmp = bb_jmp_new(from, to, drop_through); ++ return bb_jmp; ++} ++ ++static unsigned long bb_curr_addr, bb_exit_addr; ++static char bb_buffer[256]; /* A bit too big to go on stack */ ++ ++/* Computed jmp uses 'jmp *addr(,%reg,[48])' where 'addr' is the start of a ++ * table of addresses that point into the current function. Run the table and ++ * generate bb starts for each target address plus a bb_jmp from this address ++ * to the target address. ++ * ++ * Only called for 'jmp' instructions, with the pointer starting at 'jmp'. ++ */ ++ ++static void ++bb_pass1_computed_jmp(char *p) ++{ ++ unsigned long table, scale; ++ kdb_machreg_t addr; ++ struct bb* bb; ++ p += strcspn(p, " \t"); /* end of instruction */ ++ p += strspn(p, " \t"); /* start of address */ ++ if (*p++ != '*') ++ return; ++ table = simple_strtoul(p, &p, 0); ++ if (strncmp(p, "(,%", 3) != 0) ++ return; ++ p += 3; ++ p += strcspn(p, ","); /* end of reg */ ++ if (*p++ != ',') ++ return; ++ scale = simple_strtoul(p, &p, 0); ++ if (scale != KDB_WORD_SIZE || strcmp(p, ")")) ++ return; ++ while (!bb_giveup) { ++ if (kdb_getword(&addr, table, sizeof(addr))) ++ return; ++ if (addr < bb_func_start || addr >= bb_func_end) ++ return; ++ bb = bb_add(addr, 0); ++ if (bb) ++ bb_jmp_add(bb_curr_addr, addr, 0); ++ table += KDB_WORD_SIZE; ++ } ++} ++ ++/* Pass 1, identify the start and end of each basic block */ ++ ++static int ++bb_dis_pass1(PTR file, const char *fmt, ...) ++{ ++ int l = strlen(bb_buffer); ++ char *p; ++ va_list ap; ++ va_start(ap, fmt); ++ vsnprintf(bb_buffer + l, sizeof(bb_buffer) - l, fmt, ap); ++ va_end(ap); ++ if ((p = strchr(bb_buffer, '\n'))) { ++ *p = '\0'; ++ /* ret[q], iret[q], sysexit, sysret, ud2a or jmp[q] end a ++ * block. As does a call to a function marked noret. ++ */ ++ p = bb_buffer; ++ p += strcspn(p, ":"); ++ if (*p++ == ':') { ++ bb_fixup_switch_to(p); ++ p += strspn(p, " \t"); /* start of instruction */ ++ if (strncmp(p, "ret", 3) == 0 || ++ strncmp(p, "iret", 4) == 0 || ++ strncmp(p, "sysexit", 7) == 0 || ++ strncmp(p, "sysret", 6) == 0 || ++ strncmp(p, "ud2a", 4) == 0 || ++ strncmp(p, "jmp", 3) == 0) { ++ if (strncmp(p, "jmp", 3) == 0) ++ bb_pass1_computed_jmp(p); ++ bb_add(0, bb_curr_addr); ++ }; ++ if (strncmp(p, "call", 4) == 0) { ++ strsep(&p, " \t"); /* end of opcode */ ++ if (p) ++ p += strspn(p, " \t"); /* operand(s) */ ++ if (p && strchr(p, '<')) { ++ p = strchr(p, '<') + 1; ++ *strchr(p, '>') = '\0'; ++ if (bb_noret(p)) ++ bb_add(0, bb_curr_addr); ++ } ++ }; ++ } ++ bb_buffer[0] = '\0'; ++ } ++ return 0; ++} ++ ++static void ++bb_printaddr_pass1(bfd_vma addr, disassemble_info *dip) ++{ ++ kdb_symtab_t symtab; ++ unsigned int offset; ++ struct bb* bb; ++ /* disasm only calls the printaddr routine for the target of jmp, loop ++ * or call instructions, i.e. the start of a basic block. call is ++ * ignored by bb_add because the target address is outside the current ++ * function. ++ */ ++ dip->fprintf_func(dip->stream, "0x%lx", addr); ++ kdbnearsym(addr, &symtab); ++ if (symtab.sym_name) { ++ dip->fprintf_func(dip->stream, " <%s", symtab.sym_name); ++ if ((offset = addr - symtab.sym_start)) ++ dip->fprintf_func(dip->stream, "+0x%x", offset); ++ dip->fprintf_func(dip->stream, ">"); ++ } ++ bb = bb_add(addr, 0); ++ if (bb) ++ bb_jmp_add(bb_curr_addr, addr, 0); ++} ++ ++static void ++bb_pass1(void) ++{ ++ int i; ++ unsigned long addr; ++ struct bb *bb; ++ struct bb_jmp *bb_jmp; ++ ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) ++ kdb_printf("%s: func_name %s func_start " kdb_bfd_vma_fmt0 ++ " func_end " kdb_bfd_vma_fmt0 "\n", ++ __FUNCTION__, ++ bb_func_name, ++ bb_func_start, ++ bb_func_end); ++ kdb_di.fprintf_func = bb_dis_pass1; ++ kdb_di.print_address_func = bb_printaddr_pass1; ++ ++ bb_add(bb_func_start, 0); ++ for (bb_curr_addr = bb_func_start; ++ bb_curr_addr < bb_func_end; ++ ++bb_curr_addr) { ++ unsigned char c; ++ if (kdb_getarea(c, bb_curr_addr)) { ++ kdb_printf("%s: unreadable function code at ", ++ __FUNCTION__); ++ kdb_symbol_print(bb_curr_addr, NULL, KDB_SP_DEFAULT); ++ kdb_printf(", giving up\n"); ++ bb_giveup = 1; ++ return; ++ } ++ } ++ for (addr = bb_func_start; addr < bb_func_end; ) { ++ bb_curr_addr = addr; ++ addr += kdba_id_printinsn(addr, &kdb_di); ++ kdb_di.fprintf_func(NULL, "\n"); ++ } ++ if (bb_giveup) ++ goto out; ++ ++ /* Special case: a block consisting of a single instruction which is ++ * both the target of a jmp and is also an ending instruction, so we ++ * add two blocks using the same address, one as a start and one as an ++ * end, in no guaranteed order. The end must be ordered after the ++ * start. ++ */ ++ for (i = 0; i < bb_count-1; ++i) { ++ struct bb *bb1 = bb_list[i], *bb2 = bb_list[i+1]; ++ if (bb1->end && bb1->end == bb2->start) { ++ bb = bb_list[i+1]; ++ bb_list[i+1] = bb_list[i]; ++ bb_list[i] = bb; ++ } ++ } ++ ++ /* Some bb have a start address, some have an end address. Collapse ++ * them into entries that have both start and end addresses. The first ++ * entry is guaranteed to have a start address. ++ */ ++ for (i = 0; i < bb_count-1; ++i) { ++ struct bb *bb1 = bb_list[i], *bb2 = bb_list[i+1]; ++ if (bb1->end) ++ continue; ++ if (bb2->start) { ++ bb1->end = bb2->start - 1; ++ bb1->drop_through = 1; ++ bb_jmp_add(bb1->end, bb2->start, 1); ++ } else { ++ bb1->end = bb2->end; ++ bb_delete(i+1); ++ } ++ } ++ bb = bb_list[bb_count-1]; ++ if (!bb->end) ++ bb->end = bb_func_end - 1; ++ ++ /* It would be nice to check that all bb have a valid start and end ++ * address but there is just too much garbage code in the kernel to do ++ * that check. Aligned functions in assembler code mean that there is ++ * space between the end of one function and the start of the next and ++ * that space contains previous code from the assembler's buffers. It ++ * looks like dead code with nothing that branches to it, so no start ++ * address. do_sys_vm86() ends with 'jmp resume_userspace' which the C ++ * compiler does not know about so gcc appends the normal exit code, ++ * again nothing branches to this dangling code. ++ * ++ * The best we can do is delete bb entries with no start address. ++ */ ++ for (i = 0; i < bb_count; ++i) { ++ struct bb *bb = bb_list[i]; ++ if (!bb->start) ++ bb_delete(i--); ++ } ++ for (i = 0; i < bb_count; ++i) { ++ struct bb *bb = bb_list[i]; ++ if (!bb->end) { ++ kdb_printf("%s: incomplete bb state\n", __FUNCTION__); ++ bb_giveup = 1; ++ goto debug; ++ } ++ } ++ ++out: ++ if (!KDB_DEBUG(BB)) ++ return; ++debug: ++ kdb_printf("%s: end\n", __FUNCTION__); ++ for (i = 0; i < bb_count; ++i) { ++ bb = bb_list[i]; ++ kdb_printf(" bb[%d] start " ++ kdb_bfd_vma_fmt0 ++ " end " kdb_bfd_vma_fmt0 ++ " drop_through %d", ++ i, bb->start, bb->end, bb->drop_through); ++ kdb_printf("\n"); ++ } ++ for (i = 0; i < bb_jmp_count; ++i) { ++ bb_jmp = bb_jmp_list + i; ++ kdb_printf(" bb_jmp[%d] from " ++ kdb_bfd_vma_fmt0 ++ " to " kdb_bfd_vma_fmt0 ++ " drop_through %d\n", ++ i, bb_jmp->from, bb_jmp->to, bb_jmp->drop_through); ++ } ++} ++ ++/* Pass 2, record register changes in each basic block */ ++ ++/* For each opcode that we care about, indicate how it uses its operands. Most ++ * opcodes can be handled generically because they completely specify their ++ * operands in the instruction, however many opcodes have side effects such as ++ * reading or writing rax or updating rsp. Instructions that change registers ++ * that are not listed in the operands must be handled as special cases. In ++ * addition, instructions that copy registers while preserving their contents ++ * (push, pop, mov) or change the contents in a well defined way (add with an ++ * immediate, lea) must be handled as special cases in order to track the ++ * register contents. ++ * ++ * The tables below only list opcodes that are actually used in the Linux ++ * kernel, so they omit most of the floating point and all of the SSE type ++ * instructions. The operand usage entries only cater for accesses to memory ++ * and to the integer registers, accesses to floating point registers and flags ++ * are not relevant for kernel backtraces. ++ */ ++ ++enum bb_operand_usage { ++ BBOU_UNKNOWN = 0, ++ /* generic entries. because xchg can do any combinations of ++ * read src, write src, read dst and write dst we need to ++ * define all 16 possibilities. These are ordered by rs = 1, ++ * rd = 2, ws = 4, wd = 8, bb_usage_x*() functions rely on this ++ * order. ++ */ ++ BBOU_RS = 1, /* read src */ /* 1 */ ++ BBOU_RD, /* read dst */ /* 2 */ ++ BBOU_RSRD, /* 3 */ ++ BBOU_WS, /* write src */ /* 4 */ ++ BBOU_RSWS, /* 5 */ ++ BBOU_RDWS, /* 6 */ ++ BBOU_RSRDWS, /* 7 */ ++ BBOU_WD, /* write dst */ /* 8 */ ++ BBOU_RSWD, /* 9 */ ++ BBOU_RDWD, /* 10 */ ++ BBOU_RSRDWD, /* 11 */ ++ BBOU_WSWD, /* 12 */ ++ BBOU_RSWSWD, /* 13 */ ++ BBOU_RDWSWD, /* 14 */ ++ BBOU_RSRDWSWD, /* 15 */ ++ /* opcode specific entries */ ++ BBOU_ADD, ++ BBOU_AND, ++ BBOU_CALL, ++ BBOU_CBW, ++ BBOU_CMOV, ++ BBOU_CMPXCHG, ++ BBOU_CMPXCHGD, ++ BBOU_CPUID, ++ BBOU_CWD, ++ BBOU_DIV, ++ BBOU_IDIV, ++ BBOU_IMUL, ++ BBOU_IRET, ++ BBOU_JMP, ++ BBOU_LAHF, ++ BBOU_LEA, ++ BBOU_LEAVE, ++ BBOU_LODS, ++ BBOU_LOOP, ++ BBOU_LSS, ++ BBOU_MONITOR, ++ BBOU_MOV, ++ BBOU_MOVS, ++ BBOU_MUL, ++ BBOU_MWAIT, ++ BBOU_NOP, ++ BBOU_OUTS, ++ BBOU_POP, ++ BBOU_POPF, ++ BBOU_PUSH, ++ BBOU_PUSHF, ++ BBOU_RDMSR, ++ BBOU_RDTSC, ++ BBOU_RET, ++ BBOU_SAHF, ++ BBOU_SCAS, ++ BBOU_SUB, ++ BBOU_SYSEXIT, ++ BBOU_SYSRET, ++ BBOU_WRMSR, ++ BBOU_XADD, ++ BBOU_XCHG, ++ BBOU_XOR, ++}; ++ ++struct bb_opcode_usage { ++ int length; ++ enum bb_operand_usage usage; ++ const char *opcode; ++}; ++ ++/* This table is sorted in alphabetical order of opcode, except that the ++ * trailing '"' is treated as a high value. For example, 'in' sorts after ++ * 'inc', 'bt' after 'btc'. This modified sort order ensures that shorter ++ * opcodes come after long ones. A normal sort would put 'in' first, so 'in' ++ * would match both 'inc' and 'in'. When adding any new entries to this table, ++ * be careful to put shorter entries last in their group. ++ * ++ * To automatically sort the table (in vi) ++ * Mark the first and last opcode line with 'a and 'b ++ * 'a ++ * !'bsed -e 's/"}/}}/' | LANG=C sort -t '"' -k2 | sed -e 's/}}/"}/' ++ * ++ * If a new instruction has to be added, first consider if it affects registers ++ * other than those listed in the operands. Also consider if you want to track ++ * the results of issuing the instruction, IOW can you extract useful ++ * information by looking in detail at the modified registers or memory. If ++ * either test is true then you need a special case to handle the instruction. ++ * ++ * The generic entries at the start of enum bb_operand_usage all have one thing ++ * in common, if a register or memory location is updated then that location ++ * becomes undefined, i.e. we lose track of anything that was previously saved ++ * in that location. So only use a generic BBOU_* value when the result of the ++ * instruction cannot be calculated exactly _and_ when all the affected ++ * registers are listed in the operands. ++ * ++ * Examples: ++ * ++ * 'call' does not generate a known result, but as a side effect of call, ++ * several scratch registers become undefined, so it needs a special BBOU_CALL ++ * entry. ++ * ++ * 'adc' generates a variable result, it depends on the carry flag, so 'adc' ++ * gets a generic entry. 'add' can generate an exact result (add with ++ * immediate on a register that points to the stack) or it can generate an ++ * unknown result (add a variable, or add immediate to a register that does not ++ * contain a stack pointer) so 'add' has its own BBOU_ADD entry. ++ */ ++ ++static const struct bb_opcode_usage ++bb_opcode_usage_all[] = { ++ {3, BBOU_RSRDWD, "adc"}, ++ {3, BBOU_ADD, "add"}, ++ {3, BBOU_AND, "and"}, ++ {3, BBOU_RSWD, "bsf"}, ++ {3, BBOU_RSWD, "bsr"}, ++ {5, BBOU_RSWS, "bswap"}, ++ {3, BBOU_RSRDWD, "btc"}, ++ {3, BBOU_RSRDWD, "btr"}, ++ {3, BBOU_RSRDWD, "bts"}, ++ {2, BBOU_RSRD, "bt"}, ++ {4, BBOU_CALL, "call"}, ++ {4, BBOU_CBW, "cbtw"}, /* Intel cbw */ ++ {3, BBOU_NOP, "clc"}, ++ {3, BBOU_NOP, "cld"}, ++ {7, BBOU_RS, "clflush"}, ++ {4, BBOU_NOP, "clgi"}, ++ {3, BBOU_NOP, "cli"}, ++ {4, BBOU_CWD, "cltd"}, /* Intel cdq */ ++ {4, BBOU_CBW, "cltq"}, /* Intel cdqe */ ++ {4, BBOU_NOP, "clts"}, ++ {4, BBOU_CMOV, "cmov"}, ++ {9, BBOU_CMPXCHGD,"cmpxchg16"}, ++ {8, BBOU_CMPXCHGD,"cmpxchg8"}, ++ {7, BBOU_CMPXCHG, "cmpxchg"}, ++ {3, BBOU_RSRD, "cmp"}, ++ {5, BBOU_CPUID, "cpuid"}, ++ {4, BBOU_CWD, "cqto"}, /* Intel cdo */ ++ {4, BBOU_CWD, "cwtd"}, /* Intel cwd */ ++ {4, BBOU_CBW, "cwtl"}, /* Intel cwde */ ++ {4, BBOU_NOP, "data"}, /* alternative ASM_NOP generates data16 on x86_64 */ ++ {3, BBOU_RSWS, "dec"}, ++ {3, BBOU_DIV, "div"}, ++ {5, BBOU_RS, "fdivl"}, ++ {5, BBOU_NOP, "finit"}, ++ {6, BBOU_RS, "fistpl"}, ++ {4, BBOU_RS, "fldl"}, ++ {4, BBOU_RS, "fmul"}, ++ {6, BBOU_NOP, "fnclex"}, ++ {6, BBOU_NOP, "fninit"}, ++ {6, BBOU_RS, "fnsave"}, ++ {7, BBOU_NOP, "fnsetpm"}, ++ {6, BBOU_RS, "frstor"}, ++ {5, BBOU_WS, "fstsw"}, ++ {5, BBOU_RS, "fsubp"}, ++ {5, BBOU_NOP, "fwait"}, ++ {7, BBOU_RS, "fxrstor"}, ++ {6, BBOU_RS, "fxsave"}, ++ {3, BBOU_NOP, "hlt"}, ++ {4, BBOU_IDIV, "idiv"}, ++ {4, BBOU_IMUL, "imul"}, ++ {3, BBOU_RSWS, "inc"}, ++ {3, BBOU_NOP, "int"}, ++ {7, BBOU_RSRD, "invlpga"}, ++ {6, BBOU_RS, "invlpg"}, ++ {2, BBOU_RSWD, "in"}, ++ {4, BBOU_IRET, "iret"}, ++ {1, BBOU_JMP, "j"}, ++ {4, BBOU_LAHF, "lahf"}, ++ {3, BBOU_RSWD, "lar"}, ++ {5, BBOU_RS, "lcall"}, ++ {5, BBOU_LEAVE, "leave"}, ++ {3, BBOU_LEA, "lea"}, ++ {6, BBOU_NOP, "lfence"}, ++ {4, BBOU_RS, "lgdt"}, ++ {4, BBOU_RS, "lidt"}, ++ {4, BBOU_RS, "ljmp"}, ++ {4, BBOU_RS, "lldt"}, ++ {4, BBOU_RS, "lmsw"}, ++ {4, BBOU_LODS, "lods"}, ++ {4, BBOU_LOOP, "loop"}, ++ {4, BBOU_NOP, "lret"}, ++ {3, BBOU_RSWD, "lsl"}, ++ {3, BBOU_LSS, "lss"}, ++ {3, BBOU_RS, "ltr"}, ++ {6, BBOU_NOP, "mfence"}, ++ {7, BBOU_MONITOR, "monitor"}, ++ {4, BBOU_MOVS, "movs"}, ++ {3, BBOU_MOV, "mov"}, ++ {3, BBOU_MUL, "mul"}, ++ {5, BBOU_MWAIT, "mwait"}, ++ {3, BBOU_RSWS, "neg"}, ++ {3, BBOU_NOP, "nop"}, ++ {3, BBOU_RSWS, "not"}, ++ {2, BBOU_RSRDWD, "or"}, ++ {4, BBOU_OUTS, "outs"}, ++ {3, BBOU_RSRD, "out"}, ++ {5, BBOU_NOP, "pause"}, ++ {4, BBOU_POPF, "popf"}, ++ {3, BBOU_POP, "pop"}, ++ {8, BBOU_RS, "prefetch"}, ++ {5, BBOU_PUSHF, "pushf"}, ++ {4, BBOU_PUSH, "push"}, ++ {3, BBOU_RSRDWD, "rcl"}, ++ {3, BBOU_RSRDWD, "rcr"}, ++ {5, BBOU_RDMSR, "rdmsr"}, ++ {5, BBOU_RDMSR, "rdpmc"}, /* same side effects as rdmsr */ ++ {5, BBOU_RDTSC, "rdtsc"}, ++ {3, BBOU_RET, "ret"}, ++ {3, BBOU_RSRDWD, "rol"}, ++ {3, BBOU_RSRDWD, "ror"}, ++ {4, BBOU_SAHF, "sahf"}, ++ {3, BBOU_RSRDWD, "sar"}, ++ {3, BBOU_RSRDWD, "sbb"}, ++ {4, BBOU_SCAS, "scas"}, ++ {3, BBOU_WS, "set"}, ++ {6, BBOU_NOP, "sfence"}, ++ {4, BBOU_WS, "sgdt"}, ++ {3, BBOU_RSRDWD, "shl"}, ++ {3, BBOU_RSRDWD, "shr"}, ++ {4, BBOU_WS, "sidt"}, ++ {4, BBOU_WS, "sldt"}, ++ {3, BBOU_NOP, "stc"}, ++ {3, BBOU_NOP, "std"}, ++ {4, BBOU_NOP, "stgi"}, ++ {3, BBOU_NOP, "sti"}, ++ {4, BBOU_SCAS, "stos"}, ++ {4, BBOU_WS, "strl"}, ++ {3, BBOU_WS, "str"}, ++ {3, BBOU_SUB, "sub"}, ++ {6, BBOU_NOP, "swapgs"}, ++ {7, BBOU_SYSEXIT, "sysexit"}, ++ {6, BBOU_SYSRET, "sysret"}, ++ {4, BBOU_NOP, "test"}, ++ {4, BBOU_NOP, "ud2a"}, ++ {7, BBOU_RS, "vmclear"}, ++ {8, BBOU_NOP, "vmlaunch"}, ++ {6, BBOU_RS, "vmload"}, ++ {7, BBOU_RS, "vmptrld"}, ++ {6, BBOU_WD, "vmread"}, /* vmread src is an encoding, not a register */ ++ {8, BBOU_NOP, "vmresume"}, ++ {5, BBOU_RS, "vmrun"}, ++ {6, BBOU_RS, "vmsave"}, ++ {7, BBOU_WD, "vmwrite"}, /* vmwrite src is an encoding, not a register */ ++ {3, BBOU_NOP, "vmxoff"}, ++ {6, BBOU_NOP, "wbinvd"}, ++ {5, BBOU_WRMSR, "wrmsr"}, ++ {4, BBOU_XADD, "xadd"}, ++ {4, BBOU_XCHG, "xchg"}, ++ {3, BBOU_XOR, "xor"}, ++ {4, BBOU_NOP, "xrstor"}, ++ {4, BBOU_NOP, "xsave"}, ++ {10, BBOU_WS, "xstore-rng"}, ++}; ++ ++/* To speed up searching, index bb_opcode_usage_all by the first letter of each ++ * opcode. ++ */ ++static struct { ++ const struct bb_opcode_usage *opcode; ++ int size; ++} bb_opcode_usage[26]; ++ ++struct bb_operand { ++ char *base; ++ char *index; ++ char *segment; ++ long disp; ++ unsigned int scale; ++ enum bb_reg_code base_rc; /* UNDEFINED or RAX through R15 */ ++ enum bb_reg_code index_rc; /* UNDEFINED or RAX through R15 */ ++ unsigned int present :1; ++ unsigned int disp_present :1; ++ unsigned int indirect :1; /* must be combined with reg or memory */ ++ unsigned int immediate :1; /* exactly one of these 3 must be set */ ++ unsigned int reg :1; ++ unsigned int memory :1; ++}; ++ ++struct bb_decode { ++ char *prefix; ++ char *opcode; ++ const struct bb_opcode_usage *match; ++ struct bb_operand src; ++ struct bb_operand dst; ++ struct bb_operand dst2; ++}; ++ ++static struct bb_decode bb_decode; ++ ++static enum bb_reg_code ++bb_reg_map(const char *reg) ++{ ++ int lo, hi, c; ++ const struct bb_reg_code_map *p; ++ lo = 0; ++ hi = ARRAY_SIZE(bb_reg_code_map) - 1; ++ while (lo <= hi) { ++ int mid = (hi + lo) / 2; ++ p = bb_reg_code_map + mid; ++ c = strcmp(p->name, reg+1); ++ if (c == 0) ++ return p->reg; ++ else if (c > 0) ++ hi = mid - 1; ++ else ++ lo = mid + 1; ++ } ++ return BBRG_UNDEFINED; ++} ++ ++static void ++bb_parse_operand(char *str, struct bb_operand *operand) ++{ ++ char *p = str; ++ int sign = 1; ++ operand->present = 1; ++ /* extract any segment prefix */ ++ if (p[0] == '%' && p[1] && p[2] == 's' && p[3] == ':') { ++ operand->memory = 1; ++ operand->segment = p; ++ p[3] = '\0'; ++ p += 4; ++ } ++ /* extract displacement, base, index, scale */ ++ if (*p == '*') { ++ /* jmp/call *disp(%reg), *%reg or *0xnnn */ ++ operand->indirect = 1; ++ ++p; ++ } ++ if (*p == '-') { ++ sign = -1; ++ ++p; ++ } ++ if (*p == '$') { ++ operand->immediate = 1; ++ operand->disp_present = 1; ++ operand->disp = simple_strtoul(p+1, &p, 0); ++ } else if (isdigit(*p)) { ++ operand->memory = 1; ++ operand->disp_present = 1; ++ operand->disp = simple_strtoul(p, &p, 0) * sign; ++ } ++ if (*p == '%') { ++ operand->reg = 1; ++ operand->base = p; ++ } else if (*p == '(') { ++ operand->memory = 1; ++ operand->base = ++p; ++ p += strcspn(p, ",)"); ++ if (p == operand->base) ++ operand->base = NULL; ++ if (*p == ',') { ++ *p = '\0'; ++ operand->index = ++p; ++ p += strcspn(p, ",)"); ++ if (p == operand->index) ++ operand->index = NULL; ++ } ++ if (*p == ',') { ++ *p = '\0'; ++ operand->scale = simple_strtoul(p+1, &p, 0); ++ } ++ *p = '\0'; ++ } else if (*p) { ++ kdb_printf("%s: unexpected token '%c' after disp '%s'\n", ++ __FUNCTION__, *p, str); ++ bb_giveup = 1; ++ } ++ if ((operand->immediate + operand->reg + operand->memory != 1) || ++ (operand->indirect && operand->immediate)) { ++ kdb_printf("%s: incorrect decode '%s' N %d I %d R %d M %d\n", ++ __FUNCTION__, str, ++ operand->indirect, operand->immediate, operand->reg, ++ operand->memory); ++ bb_giveup = 1; ++ } ++ if (operand->base) ++ operand->base_rc = bb_reg_map(operand->base); ++ if (operand->index) ++ operand->index_rc = bb_reg_map(operand->index); ++} ++ ++static void ++bb_print_operand(const char *type, const struct bb_operand *operand) ++{ ++ if (!operand->present) ++ return; ++ kdb_printf(" %s %c%c: ", ++ type, ++ operand->indirect ? 'N' : ' ', ++ operand->immediate ? 'I' : ++ operand->reg ? 'R' : ++ operand->memory ? 'M' : ++ '?' ++ ); ++ if (operand->segment) ++ kdb_printf("%s:", operand->segment); ++ if (operand->immediate) { ++ kdb_printf("$0x%lx", operand->disp); ++ } else if (operand->reg) { ++ if (operand->indirect) ++ kdb_printf("*"); ++ kdb_printf("%s", operand->base); ++ } else if (operand->memory) { ++ if (operand->indirect && (operand->base || operand->index)) ++ kdb_printf("*"); ++ if (operand->disp_present) { ++ kdb_printf("0x%lx", operand->disp); ++ } ++ if (operand->base || operand->index || operand->scale) { ++ kdb_printf("("); ++ if (operand->base) ++ kdb_printf("%s", operand->base); ++ if (operand->index || operand->scale) ++ kdb_printf(","); ++ if (operand->index) ++ kdb_printf("%s", operand->index); ++ if (operand->scale) ++ kdb_printf(",%d", operand->scale); ++ kdb_printf(")"); ++ } ++ } ++ if (operand->base_rc) ++ kdb_printf(" base_rc %d (%s)", ++ operand->base_rc, bbrg_name[operand->base_rc]); ++ if (operand->index_rc) ++ kdb_printf(" index_rc %d (%s)", ++ operand->index_rc, ++ bbrg_name[operand->index_rc]); ++ kdb_printf("\n"); ++} ++ ++static void ++bb_print_opcode(void) ++{ ++ const struct bb_opcode_usage *o = bb_decode.match; ++ kdb_printf(" "); ++ if (bb_decode.prefix) ++ kdb_printf("%s ", bb_decode.prefix); ++ kdb_printf("opcode '%s' matched by '%s', usage %d\n", ++ bb_decode.opcode, o->opcode, o->usage); ++} ++ ++static int ++bb_parse_opcode(void) ++{ ++ int c, i; ++ const struct bb_opcode_usage *o; ++ static int bb_parse_opcode_error_limit = 5; ++ c = bb_decode.opcode[0] - 'a'; ++ if (c < 0 || c >= ARRAY_SIZE(bb_opcode_usage)) ++ goto nomatch; ++ o = bb_opcode_usage[c].opcode; ++ if (!o) ++ goto nomatch; ++ for (i = 0; i < bb_opcode_usage[c].size; ++i, ++o) { ++ if (strncmp(bb_decode.opcode, o->opcode, o->length) == 0) { ++ bb_decode.match = o; ++ if (KDB_DEBUG(BB)) ++ bb_print_opcode(); ++ return 0; ++ } ++ } ++nomatch: ++ if (!bb_parse_opcode_error_limit) ++ return 1; ++ --bb_parse_opcode_error_limit; ++ kdb_printf("%s: no match at [%s]%s " kdb_bfd_vma_fmt0 " - '%s'\n", ++ __FUNCTION__, ++ bb_mod_name, bb_func_name, bb_curr_addr, ++ bb_decode.opcode); ++ return 1; ++} ++ ++static bool ++bb_is_int_reg(enum bb_reg_code reg) ++{ ++ return reg >= BBRG_RAX && reg < (BBRG_RAX + KDB_INT_REGISTERS); ++} ++ ++static bool ++bb_is_simple_memory(const struct bb_operand *operand) ++{ ++ return operand->memory && ++ bb_is_int_reg(operand->base_rc) && ++ !operand->index_rc && ++ operand->scale == 0 && ++ !operand->segment; ++} ++ ++static bool ++bb_is_static_disp(const struct bb_operand *operand) ++{ ++ return operand->memory && ++ !operand->base_rc && ++ !operand->index_rc && ++ operand->scale == 0 && ++ !operand->segment && ++ !operand->indirect; ++} ++ ++static enum bb_reg_code ++bb_reg_code_value(enum bb_reg_code reg) ++{ ++ BB_CHECK(!bb_is_int_reg(reg), reg, 0); ++ return bb_reg_state->contains[reg - BBRG_RAX].value; ++} ++ ++static short ++bb_reg_code_offset(enum bb_reg_code reg) ++{ ++ BB_CHECK(!bb_is_int_reg(reg), reg, 0); ++ return bb_reg_state->contains[reg - BBRG_RAX].offset; ++} ++ ++static void ++bb_reg_code_set_value(enum bb_reg_code dst, enum bb_reg_code src) ++{ ++ BB_CHECK(!bb_is_int_reg(dst), dst, ); ++ bb_reg_state->contains[dst - BBRG_RAX].value = src; ++} ++ ++static void ++bb_reg_code_set_offset(enum bb_reg_code dst, short offset) ++{ ++ BB_CHECK(!bb_is_int_reg(dst), dst, ); ++ bb_reg_state->contains[dst - BBRG_RAX].offset = offset; ++} ++ ++static bool ++bb_is_osp_defined(enum bb_reg_code reg) ++{ ++ if (bb_is_int_reg(reg)) ++ return bb_reg_code_value(reg) == BBRG_OSP; ++ else ++ return 0; ++} ++ ++static bfd_vma ++bb_actual_value(enum bb_reg_code reg) ++{ ++ BB_CHECK(!bb_is_int_reg(reg), reg, 0); ++ return bb_actual[reg - BBRG_RAX].value; ++} ++ ++static int ++bb_actual_valid(enum bb_reg_code reg) ++{ ++ BB_CHECK(!bb_is_int_reg(reg), reg, 0); ++ return bb_actual[reg - BBRG_RAX].valid; ++} ++ ++static void ++bb_actual_set_value(enum bb_reg_code reg, bfd_vma value) ++{ ++ BB_CHECK(!bb_is_int_reg(reg), reg, ); ++ bb_actual[reg - BBRG_RAX].value = value; ++} ++ ++static void ++bb_actual_set_valid(enum bb_reg_code reg, int valid) ++{ ++ BB_CHECK(!bb_is_int_reg(reg), reg, ); ++ bb_actual[reg - BBRG_RAX].valid = valid; ++} ++ ++/* The scheduler code switches RSP then does PUSH, it is not an error for RSP ++ * to be undefined in this area of the code. ++ */ ++static bool ++bb_is_scheduler_address(void) ++{ ++ return bb_curr_addr >= bb__sched_text_start && ++ bb_curr_addr < bb__sched_text_end; ++} ++ ++static void ++bb_reg_read(enum bb_reg_code reg) ++{ ++ int i, r = 0; ++ if (!bb_is_int_reg(reg) || ++ bb_reg_code_value(reg) != reg) ++ return; ++ for (i = 0; ++ i < min_t(unsigned int, REGPARM, ARRAY_SIZE(bb_param_reg)); ++ ++i) { ++ if (reg == bb_param_reg[i]) { ++ r = i + 1; ++ break; ++ } ++ } ++ bb_reg_params = max(bb_reg_params, r); ++} ++ ++static void ++bb_do_reg_state_print(const struct bb_reg_state *s) ++{ ++ int i, offset_address, offset_value; ++ const struct bb_memory_contains *c; ++ enum bb_reg_code value; ++ kdb_printf(" bb_reg_state %p\n", s); ++ for (i = 0; i < ARRAY_SIZE(s->contains); ++i) { ++ value = s->contains[i].value; ++ offset_value = s->contains[i].offset; ++ kdb_printf(" %s = %s", ++ bbrg_name[i + BBRG_RAX], bbrg_name[value]); ++ if (value == BBRG_OSP) ++ KDB_DEBUG_BB_OFFSET_PRINTF(offset_value, "", ""); ++ kdb_printf("\n"); ++ } ++ for (i = 0, c = s->memory; i < s->mem_count; ++i, ++c) { ++ offset_address = c->offset_address; ++ value = c->value; ++ offset_value = c->offset_value; ++ kdb_printf(" slot %d offset_address %c0x%x %s", ++ i, ++ offset_address >= 0 ? '+' : '-', ++ offset_address >= 0 ? offset_address : -offset_address, ++ bbrg_name[value]); ++ if (value == BBRG_OSP) ++ KDB_DEBUG_BB_OFFSET_PRINTF(offset_value, "", ""); ++ kdb_printf("\n"); ++ } ++} ++ ++static void ++bb_reg_state_print(const struct bb_reg_state *s) ++{ ++ if (KDB_DEBUG(BB)) ++ bb_do_reg_state_print(s); ++} ++ ++/* Set register 'dst' to contain the value from 'src'. This includes reading ++ * from 'src' and writing to 'dst'. The offset value is copied iff 'src' ++ * contains a stack pointer. ++ * ++ * Be very careful about the context here. 'dst' and 'src' reflect integer ++ * registers by name, _not_ by the value of their contents. "mov %rax,%rsi" ++ * will call this function as bb_reg_set_reg(BBRG_RSI, BBRG_RAX), which ++ * reflects what the assembler code is doing. However we need to track the ++ * _values_ in the registers, not their names. IOW, we really care about "what ++ * value does rax contain when it is copied into rsi?", so we can record the ++ * fact that we now have two copies of that value, one in rax and one in rsi. ++ */ ++ ++static void ++bb_reg_set_reg(enum bb_reg_code dst, enum bb_reg_code src) ++{ ++ enum bb_reg_code src_value = BBRG_UNDEFINED; ++ short offset_value = 0; ++ KDB_DEBUG_BB(" %s = %s", bbrg_name[dst], bbrg_name[src]); ++ if (bb_is_int_reg(src)) { ++ bb_reg_read(src); ++ src_value = bb_reg_code_value(src); ++ KDB_DEBUG_BB(" (%s", bbrg_name[src_value]); ++ if (bb_is_osp_defined(src)) { ++ offset_value = bb_reg_code_offset(src); ++ KDB_DEBUG_BB_OFFSET(offset_value, "", ""); ++ } ++ KDB_DEBUG_BB(")"); ++ } ++ if (bb_is_int_reg(dst)) { ++ bb_reg_code_set_value(dst, src_value); ++ bb_reg_code_set_offset(dst, offset_value); ++ } ++ KDB_DEBUG_BB("\n"); ++} ++ ++static void ++bb_reg_set_undef(enum bb_reg_code dst) ++{ ++ bb_reg_set_reg(dst, BBRG_UNDEFINED); ++} ++ ++/* Delete any record of a stored register held in osp + 'offset' */ ++ ++static void ++bb_delete_memory(short offset) ++{ ++ int i; ++ struct bb_memory_contains *c; ++ for (i = 0, c = bb_reg_state->memory; ++ i < bb_reg_state->mem_count; ++ ++i, ++c) { ++ if (c->offset_address == offset && ++ c->value != BBRG_UNDEFINED) { ++ KDB_DEBUG_BB(" delete %s from ", ++ bbrg_name[c->value]); ++ KDB_DEBUG_BB_OFFSET(offset, "osp", ""); ++ KDB_DEBUG_BB(" slot %d\n", ++ (int)(c - bb_reg_state->memory)); ++ memset(c, BBRG_UNDEFINED, sizeof(*c)); ++ if (i == bb_reg_state->mem_count - 1) ++ --bb_reg_state->mem_count; ++ } ++ } ++} ++ ++/* Set memory location *('dst' + 'offset_address') to contain the supplied ++ * value and offset. 'dst' is assumed to be a register that contains a stack ++ * pointer. ++ */ ++ ++static void ++bb_memory_set_reg_value(enum bb_reg_code dst, short offset_address, ++ enum bb_reg_code value, short offset_value) ++{ ++ int i; ++ struct bb_memory_contains *c, *free = NULL; ++ BB_CHECK(!bb_is_osp_defined(dst), dst, ); ++ KDB_DEBUG_BB(" *(%s", bbrg_name[dst]); ++ KDB_DEBUG_BB_OFFSET(offset_address, "", ""); ++ offset_address += bb_reg_code_offset(dst); ++ KDB_DEBUG_BB_OFFSET(offset_address, " osp", ") = "); ++ KDB_DEBUG_BB("%s", bbrg_name[value]); ++ if (value == BBRG_OSP) ++ KDB_DEBUG_BB_OFFSET(offset_value, "", ""); ++ for (i = 0, c = bb_reg_state->memory; ++ i < bb_reg_state_max; ++ ++i, ++c) { ++ if (c->offset_address == offset_address) ++ free = c; ++ else if (c->value == BBRG_UNDEFINED && !free) ++ free = c; ++ } ++ if (!free) { ++ struct bb_reg_state *new, *old = bb_reg_state; ++ size_t old_size, new_size; ++ int slot; ++ old_size = sizeof(*old) + bb_reg_state_max * ++ sizeof(old->memory[0]); ++ slot = bb_reg_state_max; ++ bb_reg_state_max += 5; ++ new_size = sizeof(*new) + bb_reg_state_max * ++ sizeof(new->memory[0]); ++ new = debug_kmalloc(new_size, GFP_ATOMIC); ++ if (!new) { ++ kdb_printf("\n\n%s: out of debug_kmalloc\n", __FUNCTION__); ++ bb_giveup = 1; ++ } else { ++ memcpy(new, old, old_size); ++ memset((char *)new + old_size, BBRG_UNDEFINED, ++ new_size - old_size); ++ bb_reg_state = new; ++ debug_kfree(old); ++ free = bb_reg_state->memory + slot; ++ } ++ } ++ if (free) { ++ int slot = free - bb_reg_state->memory; ++ free->offset_address = offset_address; ++ free->value = value; ++ free->offset_value = offset_value; ++ KDB_DEBUG_BB(" slot %d", slot); ++ bb_reg_state->mem_count = max(bb_reg_state->mem_count, slot+1); ++ } ++ KDB_DEBUG_BB("\n"); ++} ++ ++/* Set memory location *('dst' + 'offset') to contain the value from register ++ * 'src'. 'dst' is assumed to be a register that contains a stack pointer. ++ * This differs from bb_memory_set_reg_value because it takes a src register ++ * which contains a value and possibly an offset, bb_memory_set_reg_value is ++ * passed the value and offset directly. ++ */ ++ ++static void ++bb_memory_set_reg(enum bb_reg_code dst, enum bb_reg_code src, ++ short offset_address) ++{ ++ int offset_value; ++ enum bb_reg_code value; ++ BB_CHECK(!bb_is_osp_defined(dst), dst, ); ++ if (!bb_is_int_reg(src)) ++ return; ++ value = bb_reg_code_value(src); ++ if (value == BBRG_UNDEFINED) { ++ bb_delete_memory(offset_address + bb_reg_code_offset(dst)); ++ return; ++ } ++ offset_value = bb_reg_code_offset(src); ++ bb_reg_read(src); ++ bb_memory_set_reg_value(dst, offset_address, value, offset_value); ++} ++ ++/* Set register 'dst' to contain the value from memory *('src' + offset_address). ++ * 'src' is assumed to be a register that contains a stack pointer. ++ */ ++ ++static void ++bb_reg_set_memory(enum bb_reg_code dst, enum bb_reg_code src, short offset_address) ++{ ++ int i, defined = 0; ++ struct bb_memory_contains *s; ++ BB_CHECK(!bb_is_osp_defined(src), src, ); ++ KDB_DEBUG_BB(" %s = *(%s", ++ bbrg_name[dst], bbrg_name[src]); ++ KDB_DEBUG_BB_OFFSET(offset_address, "", ")"); ++ offset_address += bb_reg_code_offset(src); ++ KDB_DEBUG_BB_OFFSET(offset_address, " (osp", ")"); ++ for (i = 0, s = bb_reg_state->memory; ++ i < bb_reg_state->mem_count; ++ ++i, ++s) { ++ if (s->offset_address == offset_address && bb_is_int_reg(dst)) { ++ bb_reg_code_set_value(dst, s->value); ++ KDB_DEBUG_BB(" value %s", bbrg_name[s->value]); ++ if (s->value == BBRG_OSP) { ++ bb_reg_code_set_offset(dst, s->offset_value); ++ KDB_DEBUG_BB_OFFSET(s->offset_value, "", ""); ++ } else { ++ bb_reg_code_set_offset(dst, 0); ++ } ++ defined = 1; ++ } ++ } ++ if (!defined) ++ bb_reg_set_reg(dst, BBRG_UNDEFINED); ++ else ++ KDB_DEBUG_BB("\n"); ++} ++ ++/* A generic read from an operand. */ ++ ++static void ++bb_read_operand(const struct bb_operand *operand) ++{ ++ int m = 0; ++ if (operand->base_rc) ++ bb_reg_read(operand->base_rc); ++ if (operand->index_rc) ++ bb_reg_read(operand->index_rc); ++ if (bb_is_simple_memory(operand) && ++ bb_is_osp_defined(operand->base_rc) && ++ bb_decode.match->usage != BBOU_LEA) { ++ m = (bb_reg_code_offset(operand->base_rc) + operand->disp + ++ KDB_WORD_SIZE - 1) / KDB_WORD_SIZE; ++ bb_memory_params = max(bb_memory_params, m); ++ } ++} ++ ++/* A generic write to an operand, resulting in an undefined value in that ++ * location. All well defined operands are handled separately, this function ++ * only handles the opcodes where the result is undefined. ++ */ ++ ++static void ++bb_write_operand(const struct bb_operand *operand) ++{ ++ enum bb_reg_code base_rc = operand->base_rc; ++ if (operand->memory) { ++ if (base_rc) ++ bb_reg_read(base_rc); ++ if (operand->index_rc) ++ bb_reg_read(operand->index_rc); ++ } else if (operand->reg && base_rc) { ++ bb_reg_set_undef(base_rc); ++ } ++ if (bb_is_simple_memory(operand) && bb_is_osp_defined(base_rc)) { ++ int offset; ++ offset = bb_reg_code_offset(base_rc) + operand->disp; ++ offset = ALIGN(offset - KDB_WORD_SIZE + 1, KDB_WORD_SIZE); ++ bb_delete_memory(offset); ++ } ++} ++ ++/* Adjust a register that contains a stack pointer */ ++ ++static void ++bb_adjust_osp(enum bb_reg_code reg, int adjust) ++{ ++ int offset = bb_reg_code_offset(reg), old_offset = offset; ++ KDB_DEBUG_BB(" %s osp offset ", bbrg_name[reg]); ++ KDB_DEBUG_BB_OFFSET(bb_reg_code_offset(reg), "", " -> "); ++ offset += adjust; ++ bb_reg_code_set_offset(reg, offset); ++ KDB_DEBUG_BB_OFFSET(bb_reg_code_offset(reg), "", "\n"); ++ /* When RSP is adjusted upwards, it invalidates any memory ++ * stored between the old and current stack offsets. ++ */ ++ if (reg == BBRG_RSP) { ++ while (old_offset < bb_reg_code_offset(reg)) { ++ bb_delete_memory(old_offset); ++ old_offset += KDB_WORD_SIZE; ++ } ++ } ++} ++ ++/* The current instruction adjusts a register that contains a stack pointer. ++ * Direction is 1 or -1, depending on whether the instruction is add/lea or ++ * sub. ++ */ ++ ++static void ++bb_adjust_osp_instruction(int direction) ++{ ++ enum bb_reg_code dst_reg = bb_decode.dst.base_rc; ++ if (bb_decode.src.immediate || ++ bb_decode.match->usage == BBOU_LEA /* lea has its own checks */) { ++ int adjust = direction * bb_decode.src.disp; ++ bb_adjust_osp(dst_reg, adjust); ++ } else { ++ /* variable stack adjustment, osp offset is not well defined */ ++ KDB_DEBUG_BB(" %s osp offset ", bbrg_name[dst_reg]); ++ KDB_DEBUG_BB_OFFSET(bb_reg_code_offset(dst_reg), "", " -> undefined\n"); ++ bb_reg_code_set_value(dst_reg, BBRG_UNDEFINED); ++ bb_reg_code_set_offset(dst_reg, 0); ++ } ++} ++ ++/* Some instructions using memory have an explicit length suffix (b, w, l, q). ++ * The equivalent instructions using a register imply the length from the ++ * register name. Deduce the operand length. ++ */ ++ ++static int ++bb_operand_length(const struct bb_operand *operand, char opcode_suffix) ++{ ++ int l = 0; ++ switch (opcode_suffix) { ++ case 'b': ++ l = 8; ++ break; ++ case 'w': ++ l = 16; ++ break; ++ case 'l': ++ l = 32; ++ break; ++ case 'q': ++ l = 64; ++ break; ++ } ++ if (l == 0 && operand->reg) { ++ switch (strlen(operand->base)) { ++ case 3: ++ switch (operand->base[2]) { ++ case 'h': ++ case 'l': ++ l = 8; ++ break; ++ default: ++ l = 16; ++ break; ++ } ++ case 4: ++ if (operand->base[1] == 'r') ++ l = 64; ++ else ++ l = 32; ++ break; ++ } ++ } ++ return l; ++} ++ ++static int ++bb_reg_state_size(const struct bb_reg_state *state) ++{ ++ return sizeof(*state) + ++ state->mem_count * sizeof(state->memory[0]); ++} ++ ++/* Canonicalize the current bb_reg_state so it can be compared against ++ * previously created states. Sort the memory entries in descending order of ++ * offset_address (stack grows down). Empty slots are moved to the end of the ++ * list and trimmed. ++ */ ++ ++static void ++bb_reg_state_canonicalize(void) ++{ ++ int i, order, changed; ++ struct bb_memory_contains *p1, *p2, temp; ++ do { ++ changed = 0; ++ for (i = 0, p1 = bb_reg_state->memory; ++ i < bb_reg_state->mem_count-1; ++ ++i, ++p1) { ++ p2 = p1 + 1; ++ if (p2->value == BBRG_UNDEFINED) { ++ order = 0; ++ } else if (p1->value == BBRG_UNDEFINED) { ++ order = 1; ++ } else if (p1->offset_address < p2->offset_address) { ++ order = 1; ++ } else if (p1->offset_address > p2->offset_address) { ++ order = -1; ++ } else { ++ order = 0; ++ } ++ if (order > 0) { ++ temp = *p2; ++ *p2 = *p1; ++ *p1 = temp; ++ changed = 1; ++ } ++ } ++ } while(changed); ++ for (i = 0, p1 = bb_reg_state->memory; ++ i < bb_reg_state_max; ++ ++i, ++p1) { ++ if (p1->value != BBRG_UNDEFINED) ++ bb_reg_state->mem_count = i + 1; ++ } ++ bb_reg_state_print(bb_reg_state); ++} ++ ++static int ++bb_special_case(bfd_vma to) ++{ ++ int i, j, rsp_offset, expect_offset, offset, errors = 0, max_errors = 40; ++ enum bb_reg_code reg, expect_value, value; ++ struct bb_name_state *r; ++ ++ for (i = 0, r = bb_special_cases; ++ i < ARRAY_SIZE(bb_special_cases); ++ ++i, ++r) { ++ if (to == r->address && ++ (r->fname == NULL || strcmp(bb_func_name, r->fname) == 0)) ++ goto match; ++ } ++ /* Some inline assembler code has jumps to .fixup sections which result ++ * in out of line transfers with undefined state, ignore them. ++ */ ++ if (strcmp(bb_func_name, "strnlen_user") == 0 || ++ strcmp(bb_func_name, "copy_from_user") == 0) ++ return 1; ++ return 0; ++ ++match: ++ /* Check the running registers match */ ++ for (reg = BBRG_RAX; reg < r->regs_size; ++reg) { ++ expect_value = r->regs[reg].value; ++ if (test_bit(expect_value, r->skip_regs.bits)) { ++ /* this regs entry is not defined for this label */ ++ continue; ++ } ++ if (expect_value == BBRG_UNDEFINED) ++ continue; ++ expect_offset = r->regs[reg].offset; ++ value = bb_reg_code_value(reg); ++ offset = bb_reg_code_offset(reg); ++ if (expect_value == value && ++ (value != BBRG_OSP || r->osp_offset == offset)) ++ continue; ++ kdb_printf("%s: Expected %s to contain %s", ++ __FUNCTION__, ++ bbrg_name[reg], ++ bbrg_name[expect_value]); ++ if (r->osp_offset) ++ KDB_DEBUG_BB_OFFSET_PRINTF(r->osp_offset, "", ""); ++ kdb_printf(". It actually contains %s", bbrg_name[value]); ++ if (offset) ++ KDB_DEBUG_BB_OFFSET_PRINTF(offset, "", ""); ++ kdb_printf("\n"); ++ ++errors; ++ if (max_errors-- == 0) ++ goto fail; ++ } ++ /* Check that any memory data on stack matches */ ++ i = j = 0; ++ while (i < bb_reg_state->mem_count && ++ j < r->mem_size) { ++ expect_value = r->mem[j].value; ++ if (test_bit(expect_value, r->skip_mem.bits) || ++ expect_value == BBRG_UNDEFINED) { ++ /* this memory slot is not defined for this label */ ++ ++j; ++ continue; ++ } ++ rsp_offset = bb_reg_state->memory[i].offset_address - ++ bb_reg_code_offset(BBRG_RSP); ++ if (rsp_offset > ++ r->mem[j].offset_address) { ++ /* extra slots in memory are OK */ ++ ++i; ++ } else if (rsp_offset < ++ r->mem[j].offset_address) { ++ /* Required memory slot is missing */ ++ kdb_printf("%s: Invalid bb_reg_state.memory, " ++ "missing memory entry[%d] %s\n", ++ __FUNCTION__, j, bbrg_name[expect_value]); ++ ++errors; ++ if (max_errors-- == 0) ++ goto fail; ++ ++j; ++ } else { ++ if (bb_reg_state->memory[i].offset_value || ++ bb_reg_state->memory[i].value != expect_value) { ++ /* memory slot is present but contains wrong ++ * value. ++ */ ++ kdb_printf("%s: Invalid bb_reg_state.memory, " ++ "wrong value in slot %d, " ++ "should be %s, it is %s\n", ++ __FUNCTION__, i, ++ bbrg_name[expect_value], ++ bbrg_name[bb_reg_state->memory[i].value]); ++ ++errors; ++ if (max_errors-- == 0) ++ goto fail; ++ } ++ ++i; ++ ++j; ++ } ++ } ++ while (j < r->mem_size) { ++ expect_value = r->mem[j].value; ++ if (test_bit(expect_value, r->skip_mem.bits) || ++ expect_value == BBRG_UNDEFINED) ++ ++j; ++ else ++ break; ++ } ++ if (j != r->mem_size) { ++ /* Hit end of memory before testing all the pt_reg slots */ ++ kdb_printf("%s: Invalid bb_reg_state.memory, " ++ "missing trailing entries\n", ++ __FUNCTION__); ++ ++errors; ++ if (max_errors-- == 0) ++ goto fail; ++ } ++ if (errors) ++ goto fail; ++ return 1; ++fail: ++ kdb_printf("%s: on transfer to %s\n", __FUNCTION__, r->name); ++ bb_giveup = 1; ++ return 1; ++} ++ ++/* Transfer of control to a label outside the current function. If the ++ * transfer is to a known common code path then do a sanity check on the state ++ * at this point. ++ */ ++ ++static void ++bb_sanity_check(int type) ++{ ++ enum bb_reg_code expect, actual; ++ int i, offset, error = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(bb_preserved_reg); ++i) { ++ expect = bb_preserved_reg[i]; ++ actual = bb_reg_code_value(expect); ++ offset = bb_reg_code_offset(expect); ++ if (expect == actual) ++ continue; ++ /* type == 1 is sysret/sysexit, ignore RSP */ ++ if (type && expect == BBRG_RSP) ++ continue; ++ /* type == 1 is sysret/sysexit, ignore RBP for i386 */ ++ /* We used to have "#ifndef CONFIG_X86_64" for the type=1 RBP ++ * test; however, x86_64 can run ia32 compatible mode and ++ * hit this problem. Perform the following test anyway! ++ */ ++ if (type && expect == BBRG_RBP) ++ continue; ++ /* RSP should contain OSP+0. Except for ptregscall_common and ++ * ia32_ptregs_common, they get a partial pt_regs, fudge the ++ * stack to make it a full pt_regs then reverse the effect on ++ * exit, so the offset is -0x50 on exit. ++ */ ++ if (expect == BBRG_RSP && ++ bb_is_osp_defined(expect) && ++ (offset == 0 || ++ (offset == -0x50 && ++ (strcmp(bb_func_name, "ptregscall_common") == 0 || ++ strcmp(bb_func_name, "ia32_ptregs_common") == 0)))) ++ continue; ++ /* The put_user and save_paranoid functions are special. ++ * %rbx gets clobbered */ ++ if (expect == BBRG_RBX && ++ (strncmp(bb_func_name, "__put_user_", 11) == 0 || ++ strcmp(bb_func_name, "save_paranoid") == 0)) ++ continue; ++ /* Ignore rbp and rsp for error_entry */ ++ if ((strcmp(bb_func_name, "error_entry") == 0) && ++ (expect == BBRG_RBX || ++ (expect == BBRG_RSP && bb_is_osp_defined(expect) && offset == -0x10))) ++ continue; ++ kdb_printf("%s: Expected %s, got %s", ++ __FUNCTION__, ++ bbrg_name[expect], bbrg_name[actual]); ++ if (offset) ++ KDB_DEBUG_BB_OFFSET_PRINTF(offset, "", ""); ++ kdb_printf("\n"); ++ error = 1; ++ } ++ BB_CHECK(error, error, ); ++} ++ ++/* Transfer of control. Follow the arc and save the current state as input to ++ * another basic block. ++ */ ++ ++static void ++bb_transfer(bfd_vma from, bfd_vma to, unsigned int drop_through) ++{ ++ int i, found; ++ size_t size; ++ struct bb* bb = NULL; /*stupid gcc */ ++ struct bb_jmp *bb_jmp; ++ struct bb_reg_state *state; ++ bb_reg_state_canonicalize(); ++ found = 0; ++ for (i = 0; i < bb_jmp_count; ++i) { ++ bb_jmp = bb_jmp_list + i; ++ if (bb_jmp->from == from && ++ bb_jmp->to == to && ++ bb_jmp->drop_through == drop_through) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) { ++ /* Transfer outside the current function. Check the special ++ * cases (mainly in entry.S) first. If it is not a known ++ * special case then check if the target address is the start ++ * of a function or not. If it is the start of a function then ++ * assume tail recursion and require that the state be the same ++ * as on entry. Otherwise assume out of line code (e.g. ++ * spinlock contention path) and ignore it, the state can be ++ * anything. ++ */ ++ kdb_symtab_t symtab; ++ if (bb_special_case(to)) ++ return; ++ kdbnearsym(to, &symtab); ++ if (symtab.sym_start != to) ++ return; ++ bb_sanity_check(0); ++ if (bb_giveup) ++ return; ++#ifdef NO_SIBLINGS ++ /* Only print this message when the kernel is compiled with ++ * -fno-optimize-sibling-calls. Otherwise it would print a ++ * message for every tail recursion call. If you see the ++ * message below then you probably have an assembler label that ++ * is not listed in the special cases. ++ */ ++ kdb_printf(" not matched: from " ++ kdb_bfd_vma_fmt0 ++ " to " kdb_bfd_vma_fmt0 ++ " drop_through %d bb_jmp[%d]\n", ++ from, to, drop_through, i); ++#endif /* NO_SIBLINGS */ ++ return; ++ } ++ KDB_DEBUG_BB(" matched: from " kdb_bfd_vma_fmt0 ++ " to " kdb_bfd_vma_fmt0 ++ " drop_through %d bb_jmp[%d]\n", ++ from, to, drop_through, i); ++ found = 0; ++ for (i = 0; i < bb_count; ++i) { ++ bb = bb_list[i]; ++ if (bb->start == to) { ++ found = 1; ++ break; ++ } ++ } ++ BB_CHECK(!found, to, ); ++ /* If the register state for this arc has already been set (we are ++ * rescanning the block that originates the arc) and the state is the ++ * same as the previous state for this arc then this input to the ++ * target block is the same as last time, so there is no need to rescan ++ * the target block. ++ */ ++ state = bb_jmp->state; ++ size = bb_reg_state_size(bb_reg_state); ++ if (state) { ++ bb_reg_state->ref_count = state->ref_count; ++ if (memcmp(state, bb_reg_state, size) == 0) { ++ KDB_DEBUG_BB(" no state change\n"); ++ return; ++ } ++ if (--state->ref_count == 0) ++ debug_kfree(state); ++ bb_jmp->state = NULL; ++ } ++ /* New input state is required. To save space, check if any other arcs ++ * have the same state and reuse them where possible. The overall set ++ * of inputs to the target block is now different so the target block ++ * must be rescanned. ++ */ ++ bb->changed = 1; ++ for (i = 0; i < bb_jmp_count; ++i) { ++ state = bb_jmp_list[i].state; ++ if (!state) ++ continue; ++ bb_reg_state->ref_count = state->ref_count; ++ if (memcmp(state, bb_reg_state, size) == 0) { ++ KDB_DEBUG_BB(" reuse bb_jmp[%d]\n", i); ++ bb_jmp->state = state; ++ ++state->ref_count; ++ return; ++ } ++ } ++ state = debug_kmalloc(size, GFP_ATOMIC); ++ if (!state) { ++ kdb_printf("\n\n%s: out of debug_kmalloc\n", __FUNCTION__); ++ bb_giveup = 1; ++ return; ++ } ++ memcpy(state, bb_reg_state, size); ++ state->ref_count = 1; ++ bb_jmp->state = state; ++ KDB_DEBUG_BB(" new state %p\n", state); ++} ++ ++/* Isolate the processing for 'mov' so it can be used for 'xadd'/'xchg' as ++ * well. ++ * ++ * xadd/xchg expect this function to return BBOU_NOP for special cases, ++ * otherwise it returns BBOU_RSWD. All special cases must be handled entirely ++ * within this function, including doing bb_read_operand or bb_write_operand ++ * where necessary. ++ */ ++ ++static enum bb_operand_usage ++bb_usage_mov(const struct bb_operand *src, const struct bb_operand *dst, int l) ++{ ++ int full_register_src, full_register_dst; ++ full_register_src = bb_operand_length(src, bb_decode.opcode[l]) ++ == KDB_WORD_SIZE * 8; ++ full_register_dst = bb_operand_length(dst, bb_decode.opcode[l]) ++ == KDB_WORD_SIZE * 8; ++ /* If both src and dst are full integer registers then record the ++ * register change. ++ */ ++ if (src->reg && ++ bb_is_int_reg(src->base_rc) && ++ dst->reg && ++ bb_is_int_reg(dst->base_rc) && ++ full_register_src && ++ full_register_dst) { ++ /* Special case for the code that switches stacks in ++ * jprobe_return. That code must modify RSP but it does it in ++ * a well defined manner. Do not invalidate RSP. ++ */ ++ if (src->base_rc == BBRG_RBX && ++ dst->base_rc == BBRG_RSP && ++ strcmp(bb_func_name, "jprobe_return") == 0) { ++ bb_read_operand(src); ++ return BBOU_NOP; ++ } ++ /* math_abort takes the equivalent of a longjmp structure and ++ * resets the stack. Ignore this, it leaves RSP well defined. ++ */ ++ if (dst->base_rc == BBRG_RSP && ++ strcmp(bb_func_name, "math_abort") == 0) { ++ bb_read_operand(src); ++ return BBOU_NOP; ++ } ++ bb_reg_set_reg(dst->base_rc, src->base_rc); ++ return BBOU_NOP; ++ } ++ /* If the move is from a full integer register to stack then record it. ++ */ ++ if (src->reg && ++ bb_is_simple_memory(dst) && ++ bb_is_osp_defined(dst->base_rc) && ++ full_register_src) { ++ /* Ugly special case. Initializing list heads on stack causes ++ * false references to stack variables when the list head is ++ * used. Static code analysis cannot detect that the list head ++ * has been changed by a previous execution loop and that a ++ * basic block is only executed after the list head has been ++ * changed. ++ * ++ * These false references can result in valid stack variables ++ * being incorrectly cleared on some logic paths. Ignore ++ * stores to stack variables which point to themselves or to ++ * the previous word so the list head initialization is not ++ * recorded. ++ */ ++ if (bb_is_osp_defined(src->base_rc)) { ++ int stack1 = bb_reg_code_offset(src->base_rc); ++ int stack2 = bb_reg_code_offset(dst->base_rc) + ++ dst->disp; ++ if (stack1 == stack2 || ++ stack1 == stack2 - KDB_WORD_SIZE) ++ return BBOU_NOP; ++ } ++ bb_memory_set_reg(dst->base_rc, src->base_rc, dst->disp); ++ return BBOU_NOP; ++ } ++ /* If the move is from stack to a full integer register then record it. ++ */ ++ if (bb_is_simple_memory(src) && ++ bb_is_osp_defined(src->base_rc) && ++ dst->reg && ++ bb_is_int_reg(dst->base_rc) && ++ full_register_dst) { ++#ifdef CONFIG_X86_32 ++ /* mov from TSS_sysenter_sp0+offset to esp to fix up the ++ * sysenter stack, it leaves esp well defined. mov ++ * TSS_ysenter_sp0+offset(%esp),%esp is followed by up to 5 ++ * push instructions to mimic the hardware stack push. If ++ * TSS_sysenter_sp0 is offset then only 3 words will be ++ * pushed. ++ */ ++ if (dst->base_rc == BBRG_RSP && ++ src->disp >= TSS_sysenter_sp0 && ++ bb_is_osp_defined(BBRG_RSP)) { ++ int pushes; ++ pushes = src->disp == TSS_sysenter_sp0 ? 5 : 3; ++ bb_reg_code_set_offset(BBRG_RSP, ++ bb_reg_code_offset(BBRG_RSP) + ++ pushes * KDB_WORD_SIZE); ++ KDB_DEBUG_BB_OFFSET( ++ bb_reg_code_offset(BBRG_RSP), ++ " sysenter fixup, RSP", ++ "\n"); ++ return BBOU_NOP; ++ } ++#endif /* CONFIG_X86_32 */ ++ bb_read_operand(src); ++ bb_reg_set_memory(dst->base_rc, src->base_rc, src->disp); ++ return BBOU_NOP; ++ } ++ /* move %gs:0x,%rsp is used to unconditionally switch to another ++ * stack. Ignore this special case, it is handled by the stack ++ * unwinding code. ++ */ ++ if (src->segment && ++ strcmp(src->segment, "%gs") == 0 && ++ dst->reg && ++ dst->base_rc == BBRG_RSP) ++ return BBOU_NOP; ++ /* move %reg,%reg is a nop */ ++ if (src->reg && ++ dst->reg && ++ !src->segment && ++ !dst->segment && ++ strcmp(src->base, dst->base) == 0) ++ return BBOU_NOP; ++ /* Special case for the code that switches stacks in the scheduler ++ * (switch_to()). That code must modify RSP but it does it in a well ++ * defined manner. Do not invalidate RSP. ++ */ ++ if (dst->reg && ++ dst->base_rc == BBRG_RSP && ++ full_register_dst && ++ bb_is_scheduler_address()) { ++ bb_read_operand(src); ++ return BBOU_NOP; ++ } ++ /* Special case for the code that switches stacks in resume from ++ * hibernation code. That code must modify RSP but it does it in a ++ * well defined manner. Do not invalidate RSP. ++ */ ++ if (src->memory && ++ dst->reg && ++ dst->base_rc == BBRG_RSP && ++ full_register_dst && ++ strcmp(bb_func_name, "restore_image") == 0) { ++ bb_read_operand(src); ++ return BBOU_NOP; ++ } ++ return BBOU_RSWD; ++} ++ ++static enum bb_operand_usage ++bb_usage_xadd(const struct bb_operand *src, const struct bb_operand *dst) ++{ ++ /* Simulate xadd as a series of instructions including mov, that way we ++ * get the benefit of all the special cases already handled by ++ * BBOU_MOV. ++ * ++ * tmp = src + dst, src = dst, dst = tmp. ++ * ++ * For tmp, pick a register that is undefined. If all registers are ++ * defined then pick one that is not being used by xadd. ++ */ ++ enum bb_reg_code reg = BBRG_UNDEFINED; ++ struct bb_operand tmp; ++ struct bb_reg_contains save_tmp; ++ enum bb_operand_usage usage; ++ int undefined = 0; ++ for (reg = BBRG_RAX; reg < BBRG_RAX + KDB_INT_REGISTERS; ++reg) { ++ if (bb_reg_code_value(reg) == BBRG_UNDEFINED) { ++ undefined = 1; ++ break; ++ } ++ } ++ if (!undefined) { ++ for (reg = BBRG_RAX; reg < BBRG_RAX + KDB_INT_REGISTERS; ++reg) { ++ if (reg != src->base_rc && ++ reg != src->index_rc && ++ reg != dst->base_rc && ++ reg != dst->index_rc && ++ reg != BBRG_RSP) ++ break; ++ } ++ } ++ KDB_DEBUG_BB(" %s saving tmp %s\n", __FUNCTION__, bbrg_name[reg]); ++ save_tmp = bb_reg_state->contains[reg - BBRG_RAX]; ++ bb_reg_set_undef(reg); ++ memset(&tmp, 0, sizeof(tmp)); ++ tmp.present = 1; ++ tmp.reg = 1; ++ tmp.base = debug_kmalloc(strlen(bbrg_name[reg]) + 2, GFP_ATOMIC); ++ if (tmp.base) { ++ tmp.base[0] = '%'; ++ strcpy(tmp.base + 1, bbrg_name[reg]); ++ } ++ tmp.base_rc = reg; ++ bb_read_operand(src); ++ bb_read_operand(dst); ++ if (bb_usage_mov(src, dst, sizeof("xadd")-1) == BBOU_NOP) ++ usage = BBOU_RSRD; ++ else ++ usage = BBOU_RSRDWS; ++ bb_usage_mov(&tmp, dst, sizeof("xadd")-1); ++ KDB_DEBUG_BB(" %s restoring tmp %s\n", __FUNCTION__, bbrg_name[reg]); ++ bb_reg_state->contains[reg - BBRG_RAX] = save_tmp; ++ debug_kfree(tmp.base); ++ return usage; ++} ++ ++static enum bb_operand_usage ++bb_usage_xchg(const struct bb_operand *src, const struct bb_operand *dst) ++{ ++ /* Simulate xchg as a series of mov instructions, that way we get the ++ * benefit of all the special cases already handled by BBOU_MOV. ++ * ++ * mov dst,tmp; mov src,dst; mov tmp,src; ++ * ++ * For tmp, pick a register that is undefined. If all registers are ++ * defined then pick one that is not being used by xchg. ++ */ ++ enum bb_reg_code reg = BBRG_UNDEFINED; ++ int rs = BBOU_RS, rd = BBOU_RD, ws = BBOU_WS, wd = BBOU_WD; ++ struct bb_operand tmp; ++ struct bb_reg_contains save_tmp; ++ int undefined = 0; ++ for (reg = BBRG_RAX; reg < BBRG_RAX + KDB_INT_REGISTERS; ++reg) { ++ if (bb_reg_code_value(reg) == BBRG_UNDEFINED) { ++ undefined = 1; ++ break; ++ } ++ } ++ if (!undefined) { ++ for (reg = BBRG_RAX; reg < BBRG_RAX + KDB_INT_REGISTERS; ++reg) { ++ if (reg != src->base_rc && ++ reg != src->index_rc && ++ reg != dst->base_rc && ++ reg != dst->index_rc && ++ reg != BBRG_RSP) ++ break; ++ } ++ } ++ KDB_DEBUG_BB(" %s saving tmp %s\n", __FUNCTION__, bbrg_name[reg]); ++ save_tmp = bb_reg_state->contains[reg - BBRG_RAX]; ++ memset(&tmp, 0, sizeof(tmp)); ++ tmp.present = 1; ++ tmp.reg = 1; ++ tmp.base = debug_kmalloc(strlen(bbrg_name[reg]) + 2, GFP_ATOMIC); ++ if (tmp.base) { ++ tmp.base[0] = '%'; ++ strcpy(tmp.base + 1, bbrg_name[reg]); ++ } ++ tmp.base_rc = reg; ++ if (bb_usage_mov(dst, &tmp, sizeof("xchg")-1) == BBOU_NOP) ++ rd = 0; ++ if (bb_usage_mov(src, dst, sizeof("xchg")-1) == BBOU_NOP) { ++ rs = 0; ++ wd = 0; ++ } ++ if (bb_usage_mov(&tmp, src, sizeof("xchg")-1) == BBOU_NOP) ++ ws = 0; ++ KDB_DEBUG_BB(" %s restoring tmp %s\n", __FUNCTION__, bbrg_name[reg]); ++ bb_reg_state->contains[reg - BBRG_RAX] = save_tmp; ++ debug_kfree(tmp.base); ++ return rs | rd | ws | wd; ++} ++ ++/* Invalidate all the scratch registers */ ++ ++static void ++bb_invalidate_scratch_reg(void) ++{ ++ int i, j; ++ for (i = BBRG_RAX; i < BBRG_RAX + KDB_INT_REGISTERS; ++i) { ++ for (j = 0; j < ARRAY_SIZE(bb_preserved_reg); ++j) { ++ if (i == bb_preserved_reg[j]) ++ goto preserved; ++ } ++ bb_reg_set_undef(i); ++preserved: ++ continue; ++ } ++} ++ ++static void ++bb_pass2_computed_jmp(const struct bb_operand *src) ++{ ++ unsigned long table = src->disp; ++ kdb_machreg_t addr; ++ while (!bb_giveup) { ++ if (kdb_getword(&addr, table, sizeof(addr))) ++ return; ++ if (addr < bb_func_start || addr >= bb_func_end) ++ return; ++ bb_transfer(bb_curr_addr, addr, 0); ++ table += KDB_WORD_SIZE; ++ } ++} ++ ++/* The current instruction has been decoded and all the information is in ++ * bb_decode. Based on the opcode, track any operand usage that we care about. ++ */ ++ ++static void ++bb_usage(void) ++{ ++ enum bb_operand_usage usage = bb_decode.match->usage; ++ struct bb_operand *src = &bb_decode.src; ++ struct bb_operand *dst = &bb_decode.dst; ++ struct bb_operand *dst2 = &bb_decode.dst2; ++ int opcode_suffix, operand_length; ++ ++ /* First handle all the special usage cases, and map them to a generic ++ * case after catering for the side effects. ++ */ ++ ++ if (usage == BBOU_IMUL && ++ src->present && !dst->present && !dst2->present) { ++ /* single operand imul, same effects as mul */ ++ usage = BBOU_MUL; ++ } ++ ++ /* AT&T syntax uses movs for move with sign extension, instead ++ * of the Intel movsx. The AT&T syntax causes problems for the opcode ++ * mapping; movs with sign extension needs to be treated as a generic ++ * read src, write dst, but instead it falls under the movs I/O ++ * instruction. Fix it. ++ */ ++ if (usage == BBOU_MOVS && strlen(bb_decode.opcode) > 5) ++ usage = BBOU_RSWD; ++ ++ /* This switch statement deliberately does not use 'default' at the top ++ * level. That way the compiler will complain if a new BBOU_ enum is ++ * added above and not explicitly handled here. ++ */ ++ switch (usage) { ++ case BBOU_UNKNOWN: /* drop through */ ++ case BBOU_RS: /* drop through */ ++ case BBOU_RD: /* drop through */ ++ case BBOU_RSRD: /* drop through */ ++ case BBOU_WS: /* drop through */ ++ case BBOU_RSWS: /* drop through */ ++ case BBOU_RDWS: /* drop through */ ++ case BBOU_RSRDWS: /* drop through */ ++ case BBOU_WD: /* drop through */ ++ case BBOU_RSWD: /* drop through */ ++ case BBOU_RDWD: /* drop through */ ++ case BBOU_RSRDWD: /* drop through */ ++ case BBOU_WSWD: /* drop through */ ++ case BBOU_RSWSWD: /* drop through */ ++ case BBOU_RDWSWD: /* drop through */ ++ case BBOU_RSRDWSWD: ++ break; /* ignore generic usage for now */ ++ case BBOU_ADD: ++ /* Special case for add instructions that adjust registers ++ * which are mapping the stack. ++ */ ++ if (dst->reg && bb_is_osp_defined(dst->base_rc)) { ++ bb_adjust_osp_instruction(1); ++ usage = BBOU_RS; ++ } else { ++ usage = BBOU_RSRDWD; ++ } ++ break; ++ case BBOU_AND: ++ /* Special case when trying to round the stack pointer ++ * to achieve byte alignment ++ */ ++ if (dst->reg && dst->base_rc == BBRG_RSP && ++ src->immediate && strncmp(bb_func_name, "efi_call", 8) == 0) { ++ usage = BBOU_NOP; ++ } else { ++ usage = BBOU_RSRDWD; ++ } ++ break; ++ case BBOU_CALL: ++ bb_reg_state_print(bb_reg_state); ++ usage = BBOU_NOP; ++ if (bb_is_static_disp(src)) { ++ /* save_args is special. It saves ++ * a partial pt_regs onto the stack and switches ++ * to the interrupt stack. ++ */ ++ if (src->disp == bb_save_args) { ++ bb_memory_set_reg(BBRG_RSP, BBRG_RDI, 0x48); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RSI, 0x40); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RDX, 0x38); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RCX, 0x30); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RAX, 0x28); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R8, 0x20); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R9, 0x18); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R10, 0x10); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R11, 0x08); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RBP, 0); ++ /* This is actually on the interrupt stack, ++ * but we fudge it so the unwind works. ++ */ ++ bb_memory_set_reg_value(BBRG_RSP, -0x8, BBRG_RBP, 0); ++ bb_reg_set_reg(BBRG_RBP, BBRG_RSP); ++ bb_adjust_osp(BBRG_RSP, -KDB_WORD_SIZE); ++ } ++ /* save_rest juggles the stack frame to append the ++ * rest of the pt_regs onto a stack where SAVE_ARGS ++ * or save_args has already been done. ++ */ ++ else if (src->disp == bb_save_rest) { ++ bb_memory_set_reg(BBRG_RSP, BBRG_RBX, 0x30); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RBP, 0x28); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R12, 0x20); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R13, 0x18); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R14, 0x10); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R15, 0x08); ++ } ++ /* error_entry and save_paranoid save a full pt_regs. ++ * Break out so the scratch registers aren't invalidated. ++ */ ++ else if (src->disp == bb_error_entry || src->disp == bb_save_paranoid) { ++ bb_memory_set_reg(BBRG_RSP, BBRG_RDI, 0x70); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RSI, 0x68); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RDX, 0x60); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RCX, 0x58); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RAX, 0x50); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R8, 0x48); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R9, 0x40); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R10, 0x38); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R11, 0x30); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RBX, 0x28); ++ bb_memory_set_reg(BBRG_RSP, BBRG_RBP, 0x20); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R12, 0x18); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R13, 0x10); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R14, 0x08); ++ bb_memory_set_reg(BBRG_RSP, BBRG_R15, 0); ++ break; ++ } ++ } ++ /* Invalidate the scratch registers */ ++ bb_invalidate_scratch_reg(); ++ ++ /* These special cases need scratch registers invalidated first */ ++ if (bb_is_static_disp(src)) { ++ /* Function sync_regs and save_v86_state are special. ++ * Their return value is the new stack pointer ++ */ ++ if (src->disp == bb_sync_regs) { ++ bb_reg_set_reg(BBRG_RAX, BBRG_RSP); ++ } else if (src->disp == bb_save_v86_state) { ++ bb_reg_set_reg(BBRG_RAX, BBRG_RSP); ++ bb_adjust_osp(BBRG_RAX, +KDB_WORD_SIZE); ++ } ++ } ++ break; ++ case BBOU_CBW: ++ /* Convert word in RAX. Read RAX, write RAX */ ++ bb_reg_read(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RAX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_CMOV: ++ /* cmove %gs:0x,%rsp is used to conditionally switch to ++ * another stack. Ignore this special case, it is handled by ++ * the stack unwinding code. ++ */ ++ if (src->segment && ++ strcmp(src->segment, "%gs") == 0 && ++ dst->reg && ++ dst->base_rc == BBRG_RSP) ++ usage = BBOU_NOP; ++ else ++ usage = BBOU_RSWD; ++ break; ++ case BBOU_CMPXCHG: ++ /* Read RAX, write RAX plus src read, dst write */ ++ bb_reg_read(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RAX); ++ usage = BBOU_RSWD; ++ break; ++ case BBOU_CMPXCHGD: ++ /* Read RAX, RBX, RCX, RDX, write RAX, RDX plus src read/write */ ++ bb_reg_read(BBRG_RAX); ++ bb_reg_read(BBRG_RBX); ++ bb_reg_read(BBRG_RCX); ++ bb_reg_read(BBRG_RDX); ++ bb_reg_set_undef(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RDX); ++ usage = BBOU_RSWS; ++ break; ++ case BBOU_CPUID: ++ /* Read RAX, write RAX, RBX, RCX, RDX */ ++ bb_reg_read(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RBX); ++ bb_reg_set_undef(BBRG_RCX); ++ bb_reg_set_undef(BBRG_RDX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_CWD: ++ /* Convert word in RAX, RDX. Read RAX, write RDX */ ++ bb_reg_read(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RDX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_DIV: /* drop through */ ++ case BBOU_IDIV: ++ /* The 8 bit variants only affect RAX, the 16, 32 and 64 bit ++ * variants affect RDX as well. ++ */ ++ switch (usage) { ++ case BBOU_DIV: ++ opcode_suffix = bb_decode.opcode[3]; ++ break; ++ case BBOU_IDIV: ++ opcode_suffix = bb_decode.opcode[4]; ++ break; ++ default: ++ opcode_suffix = 'q'; ++ break; ++ } ++ operand_length = bb_operand_length(src, opcode_suffix); ++ bb_reg_read(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RAX); ++ if (operand_length != 8) { ++ bb_reg_read(BBRG_RDX); ++ bb_reg_set_undef(BBRG_RDX); ++ } ++ usage = BBOU_RS; ++ break; ++ case BBOU_IMUL: ++ /* Only the two and three operand forms get here. The one ++ * operand form is treated as mul. ++ */ ++ if (dst2->present) { ++ /* The three operand form is a special case, read the first two ++ * operands, write the third. ++ */ ++ bb_read_operand(src); ++ bb_read_operand(dst); ++ bb_write_operand(dst2); ++ usage = BBOU_NOP; ++ } else { ++ usage = BBOU_RSRDWD; ++ } ++ break; ++ case BBOU_IRET: ++ bb_sanity_check(0); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_JMP: ++ if (bb_is_static_disp(src)) ++ bb_transfer(bb_curr_addr, src->disp, 0); ++ else if (src->indirect && ++ src->disp && ++ src->base == NULL && ++ src->index && ++ src->scale == KDB_WORD_SIZE) ++ bb_pass2_computed_jmp(src); ++ usage = BBOU_RS; ++ break; ++ case BBOU_LAHF: ++ /* Write RAX */ ++ bb_reg_set_undef(BBRG_RAX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_LEA: ++ /* dst = src + disp. Often used to calculate offsets into the ++ * stack, so check if it uses a stack pointer. ++ */ ++ usage = BBOU_RSWD; ++ if (bb_is_simple_memory(src)) { ++ if (bb_is_osp_defined(src->base_rc)) { ++ bb_reg_set_reg(dst->base_rc, src->base_rc); ++ bb_adjust_osp_instruction(1); ++ usage = BBOU_RS; ++ } else if (src->disp == 0 && ++ src->base_rc == dst->base_rc) { ++ /* lea 0(%reg),%reg is generated by i386 ++ * GENERIC_NOP7. ++ */ ++ usage = BBOU_NOP; ++ } else if (src->disp == 4096 && ++ (src->base_rc == BBRG_R8 || ++ src->base_rc == BBRG_RDI) && ++ strcmp(bb_func_name, "relocate_kernel") == 0) { ++ /* relocate_kernel: setup a new stack at the ++ * end of the physical control page, using ++ * (x86_64) lea 4096(%r8),%rsp or (i386) lea ++ * 4096(%edi),%esp ++ */ ++ usage = BBOU_NOP; ++ } ++ } ++ break; ++ case BBOU_LEAVE: ++ /* RSP = RBP; RBP = *(RSP); RSP += KDB_WORD_SIZE; */ ++ bb_reg_set_reg(BBRG_RSP, BBRG_RBP); ++ if (bb_is_osp_defined(BBRG_RSP)) ++ bb_reg_set_memory(BBRG_RBP, BBRG_RSP, 0); ++ else ++ bb_reg_set_undef(BBRG_RBP); ++ if (bb_is_osp_defined(BBRG_RSP)) ++ bb_adjust_osp(BBRG_RSP, KDB_WORD_SIZE); ++ /* common_interrupt uses leave in a non-standard manner */ ++ if (strcmp(bb_func_name, "common_interrupt") != 0) ++ bb_sanity_check(0); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_LODS: ++ /* Read RSI, write RAX, RSI */ ++ bb_reg_read(BBRG_RSI); ++ bb_reg_set_undef(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RSI); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_LOOP: ++ /* Read and write RCX */ ++ bb_reg_read(BBRG_RCX); ++ bb_reg_set_undef(BBRG_RCX); ++ if (bb_is_static_disp(src)) ++ bb_transfer(bb_curr_addr, src->disp, 0); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_LSS: ++ /* lss offset(%esp),%esp leaves esp well defined */ ++ if (dst->reg && ++ dst->base_rc == BBRG_RSP && ++ bb_is_simple_memory(src) && ++ src->base_rc == BBRG_RSP) { ++ bb_adjust_osp(BBRG_RSP, 2*KDB_WORD_SIZE + src->disp); ++ usage = BBOU_NOP; ++ } else { ++ usage = BBOU_RSWD; ++ } ++ break; ++ case BBOU_MONITOR: ++ /* Read RAX, RCX, RDX */ ++ bb_reg_set_undef(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RCX); ++ bb_reg_set_undef(BBRG_RDX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_MOV: ++ usage = bb_usage_mov(src, dst, sizeof("mov")-1); ++ break; ++ case BBOU_MOVS: ++ /* Read RSI, RDI, write RSI, RDI */ ++ bb_reg_read(BBRG_RSI); ++ bb_reg_read(BBRG_RDI); ++ bb_reg_set_undef(BBRG_RSI); ++ bb_reg_set_undef(BBRG_RDI); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_MUL: ++ /* imul (one operand form only) or mul. Read RAX. If the ++ * operand length is not 8 then write RDX. ++ */ ++ if (bb_decode.opcode[0] == 'i') ++ opcode_suffix = bb_decode.opcode[4]; ++ else ++ opcode_suffix = bb_decode.opcode[3]; ++ operand_length = bb_operand_length(src, opcode_suffix); ++ bb_reg_read(BBRG_RAX); ++ if (operand_length != 8) ++ bb_reg_set_undef(BBRG_RDX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_MWAIT: ++ /* Read RAX, RCX */ ++ bb_reg_read(BBRG_RAX); ++ bb_reg_read(BBRG_RCX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_NOP: ++ break; ++ case BBOU_OUTS: ++ /* Read RSI, RDX, write RSI */ ++ bb_reg_read(BBRG_RSI); ++ bb_reg_read(BBRG_RDX); ++ bb_reg_set_undef(BBRG_RSI); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_POP: ++ /* Complicated by the fact that you can pop from top of stack ++ * to a stack location, for this case the destination location ++ * is calculated after adjusting RSP. Analysis of the kernel ++ * code shows that gcc only uses this strange format to get the ++ * flags into a local variable, e.g. pushf; popl 0x10(%esp); so ++ * I am going to ignore this special case. ++ */ ++ usage = BBOU_WS; ++ if (!bb_is_osp_defined(BBRG_RSP)) { ++ if (!bb_is_scheduler_address()) { ++ kdb_printf("pop when BBRG_RSP is undefined?\n"); ++ bb_giveup = 1; ++ } ++ } else { ++ if (src->reg) { ++ bb_reg_set_memory(src->base_rc, BBRG_RSP, 0); ++ usage = BBOU_NOP; ++ } ++ /* pop %rsp does not adjust rsp */ ++ if (!src->reg || ++ src->base_rc != BBRG_RSP) ++ bb_adjust_osp(BBRG_RSP, KDB_WORD_SIZE); ++ } ++ break; ++ case BBOU_POPF: ++ /* Do not care about flags, just adjust RSP */ ++ if (!bb_is_osp_defined(BBRG_RSP)) { ++ if (!bb_is_scheduler_address()) { ++ kdb_printf("popf when BBRG_RSP is undefined?\n"); ++ bb_giveup = 1; ++ } ++ } else { ++ bb_adjust_osp(BBRG_RSP, KDB_WORD_SIZE); ++ } ++ usage = BBOU_WS; ++ break; ++ case BBOU_PUSH: ++ /* Complicated by the fact that you can push from a stack ++ * location to top of stack, the source location is calculated ++ * before adjusting RSP. Analysis of the kernel code shows ++ * that gcc only uses this strange format to restore the flags ++ * from a local variable, e.g. pushl 0x10(%esp); popf; so I am ++ * going to ignore this special case. ++ */ ++ usage = BBOU_RS; ++ if (!bb_is_osp_defined(BBRG_RSP)) { ++ if (!bb_is_scheduler_address()) { ++ kdb_printf("push when BBRG_RSP is undefined?\n"); ++ bb_giveup = 1; ++ } ++ } else { ++ bb_adjust_osp(BBRG_RSP, -KDB_WORD_SIZE); ++ if (src->reg && ++ bb_reg_code_offset(BBRG_RSP) <= 0) ++ bb_memory_set_reg(BBRG_RSP, src->base_rc, 0); ++ } ++ break; ++ case BBOU_PUSHF: ++ /* Do not care about flags, just adjust RSP */ ++ if (!bb_is_osp_defined(BBRG_RSP)) { ++ if (!bb_is_scheduler_address()) { ++ kdb_printf("pushf when BBRG_RSP is undefined?\n"); ++ bb_giveup = 1; ++ } ++ } else { ++ bb_adjust_osp(BBRG_RSP, -KDB_WORD_SIZE); ++ } ++ usage = BBOU_WS; ++ break; ++ case BBOU_RDMSR: ++ /* Read RCX, write RAX, RDX */ ++ bb_reg_read(BBRG_RCX); ++ bb_reg_set_undef(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RDX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_RDTSC: ++ /* Write RAX, RDX */ ++ bb_reg_set_undef(BBRG_RAX); ++ bb_reg_set_undef(BBRG_RDX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_RET: ++ usage = BBOU_NOP; ++ if (src->immediate && bb_is_osp_defined(BBRG_RSP)) { ++ bb_adjust_osp(BBRG_RSP, src->disp); ++ } ++ /* Functions that restore state which was saved by another ++ * function or build new kernel stacks. We cannot verify what ++ * is being restored so skip the sanity check. ++ */ ++ if (strcmp(bb_func_name, "restore_image") == 0 || ++ strcmp(bb_func_name, "relocate_kernel") == 0 || ++ strcmp(bb_func_name, "identity_mapped") == 0 || ++ strcmp(bb_func_name, "xen_iret_crit_fixup") == 0 || ++ strcmp(bb_func_name, "math_abort") == 0 || ++ strcmp(bb_func_name, "save_args") == 0 || ++ strcmp(bb_func_name, "kretprobe_trampoline_holder") == 0) ++ break; ++ bb_sanity_check(0); ++ break; ++ case BBOU_SAHF: ++ /* Read RAX */ ++ bb_reg_read(BBRG_RAX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_SCAS: ++ /* Read RAX, RDI, write RDI */ ++ bb_reg_read(BBRG_RAX); ++ bb_reg_read(BBRG_RDI); ++ bb_reg_set_undef(BBRG_RDI); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_SUB: ++ /* Special case for sub instructions that adjust registers ++ * which are mapping the stack. ++ */ ++ if (dst->reg && bb_is_osp_defined(dst->base_rc)) { ++ bb_adjust_osp_instruction(-1); ++ usage = BBOU_RS; ++ } else { ++ usage = BBOU_RSRDWD; ++ } ++ break; ++ case BBOU_SYSEXIT: ++ bb_sanity_check(1); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_SYSRET: ++ bb_sanity_check(1); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_WRMSR: ++ /* Read RCX, RAX, RDX */ ++ bb_reg_read(BBRG_RCX); ++ bb_reg_read(BBRG_RAX); ++ bb_reg_read(BBRG_RDX); ++ usage = BBOU_NOP; ++ break; ++ case BBOU_XADD: ++ usage = bb_usage_xadd(src, dst); ++ break; ++ case BBOU_XCHG: ++ /* i386 do_IRQ with 4K stacks does xchg %ebx,%esp; call ++ * irq_handler; mov %ebx,%esp; to switch stacks. Ignore this ++ * stack switch when tracking registers, it is handled by ++ * higher level backtrace code. Convert xchg %ebx,%esp to mov ++ * %esp,%ebx so the later mov %ebx,%esp becomes a NOP and the ++ * stack remains defined so we can backtrace through do_IRQ's ++ * stack switch. ++ * ++ * Ditto for do_softirq. ++ */ ++ if (src->reg && ++ dst->reg && ++ src->base_rc == BBRG_RBX && ++ dst->base_rc == BBRG_RSP && ++ (strcmp(bb_func_name, "do_IRQ") == 0 || ++ strcmp(bb_func_name, "do_softirq") == 0)) { ++ strcpy(bb_decode.opcode, "mov"); ++ usage = bb_usage_mov(dst, src, sizeof("mov")-1); ++ } else { ++ usage = bb_usage_xchg(src, dst); ++ } ++ break; ++ case BBOU_XOR: ++ /* xor %reg,%reg only counts as a register write, the original ++ * contents of reg are irrelevant. ++ */ ++ if (src->reg && dst->reg && src->base_rc == dst->base_rc) ++ usage = BBOU_WS; ++ else ++ usage = BBOU_RSRDWD; ++ break; ++ } ++ ++ /* The switch statement above handled all the special cases. Every ++ * opcode should now have a usage of NOP or one of the generic cases. ++ */ ++ if (usage == BBOU_UNKNOWN || usage == BBOU_NOP) { ++ /* nothing to do */ ++ } else if (usage >= BBOU_RS && usage <= BBOU_RSRDWSWD) { ++ if (usage & BBOU_RS) ++ bb_read_operand(src); ++ if (usage & BBOU_RD) ++ bb_read_operand(dst); ++ if (usage & BBOU_WS) ++ bb_write_operand(src); ++ if (usage & BBOU_WD) ++ bb_write_operand(dst); ++ } else { ++ kdb_printf("%s: opcode not fully handled\n", __FUNCTION__); ++ if (!KDB_DEBUG(BB)) { ++ bb_print_opcode(); ++ if (bb_decode.src.present) ++ bb_print_operand("src", &bb_decode.src); ++ if (bb_decode.dst.present) ++ bb_print_operand("dst", &bb_decode.dst); ++ if (bb_decode.dst2.present) ++ bb_print_operand("dst2", &bb_decode.dst2); ++ } ++ bb_giveup = 1; ++ } ++} ++ ++static void ++bb_parse_buffer(void) ++{ ++ char *p, *src, *dst = NULL, *dst2 = NULL; ++ int paren = 0; ++ p = bb_buffer; ++ memset(&bb_decode, 0, sizeof(bb_decode)); ++ KDB_DEBUG_BB(" '%s'\n", p); ++ p += strcspn(p, ":"); /* skip address and function name+offset: */ ++ if (*p++ != ':') { ++ kdb_printf("%s: cannot find ':' in buffer '%s'\n", ++ __FUNCTION__, bb_buffer); ++ bb_giveup = 1; ++ return; ++ } ++ p += strspn(p, " \t"); /* step to opcode */ ++ if (strncmp(p, "(bad)", 5) == 0) ++ strcpy(p, "nop"); ++ /* separate any opcode prefix */ ++ if (strncmp(p, "lock", 4) == 0 || ++ strncmp(p, "rep", 3) == 0 || ++ strncmp(p, "rex", 3) == 0 || ++ strncmp(p, "addr", 4) == 0) { ++ bb_decode.prefix = p; ++ p += strcspn(p, " \t"); ++ *p++ = '\0'; ++ p += strspn(p, " \t"); ++ } ++ bb_decode.opcode = p; ++ strsep(&p, " \t"); /* step to end of opcode */ ++ if (bb_parse_opcode()) ++ return; ++ if (!p) ++ goto no_operands; ++ p += strspn(p, " \t"); /* step to operand(s) */ ++ if (!*p) ++ goto no_operands; ++ src = p; ++ p = strsep(&p, " \t"); /* strip comments after operands */ ++ /* split 'src','dst' but ignore ',' inside '(' ')' */ ++ while (*p) { ++ if (*p == '(') { ++ ++paren; ++ } else if (*p == ')') { ++ --paren; ++ } else if (*p == ',' && paren == 0) { ++ *p = '\0'; ++ if (dst) ++ dst2 = p+1; ++ else ++ dst = p+1; ++ } ++ ++p; ++ } ++ bb_parse_operand(src, &bb_decode.src); ++ if (KDB_DEBUG(BB)) ++ bb_print_operand("src", &bb_decode.src); ++ if (dst && !bb_giveup) { ++ bb_parse_operand(dst, &bb_decode.dst); ++ if (KDB_DEBUG(BB)) ++ bb_print_operand("dst", &bb_decode.dst); ++ } ++ if (dst2 && !bb_giveup) { ++ bb_parse_operand(dst2, &bb_decode.dst2); ++ if (KDB_DEBUG(BB)) ++ bb_print_operand("dst2", &bb_decode.dst2); ++ } ++no_operands: ++ if (!bb_giveup) ++ bb_usage(); ++} ++ ++static int ++bb_dis_pass2(PTR file, const char *fmt, ...) ++{ ++ char *p; ++ int l = strlen(bb_buffer); ++ va_list ap; ++ va_start(ap, fmt); ++ vsnprintf(bb_buffer + l, sizeof(bb_buffer) - l, fmt, ap); ++ va_end(ap); ++ if ((p = strchr(bb_buffer, '\n'))) { ++ *p = '\0'; ++ p = bb_buffer; ++ p += strcspn(p, ":"); ++ if (*p++ == ':') ++ bb_fixup_switch_to(p); ++ bb_parse_buffer(); ++ bb_buffer[0] = '\0'; ++ } ++ return 0; ++} ++ ++static void ++bb_printaddr_pass2(bfd_vma addr, disassemble_info *dip) ++{ ++ kdb_symtab_t symtab; ++ unsigned int offset; ++ dip->fprintf_func(dip->stream, "0x%lx", addr); ++ kdbnearsym(addr, &symtab); ++ if (symtab.sym_name) { ++ dip->fprintf_func(dip->stream, " <%s", symtab.sym_name); ++ if ((offset = addr - symtab.sym_start)) ++ dip->fprintf_func(dip->stream, "+0x%x", offset); ++ dip->fprintf_func(dip->stream, ">"); ++ } ++} ++ ++/* Set the starting register and memory state for the current bb */ ++ ++static void ++bb_start_block0_special(void) ++{ ++ int i; ++ short offset_address; ++ enum bb_reg_code reg, value; ++ struct bb_name_state *r; ++ for (i = 0, r = bb_special_cases; ++ i < ARRAY_SIZE(bb_special_cases); ++ ++i, ++r) { ++ if (bb_func_start == r->address && r->fname == NULL) ++ goto match; ++ } ++ return; ++match: ++ /* Set the running registers */ ++ for (reg = BBRG_RAX; reg < r->regs_size; ++reg) { ++ value = r->regs[reg].value; ++ if (test_bit(value, r->skip_regs.bits)) { ++ /* this regs entry is not defined for this label */ ++ continue; ++ } ++ bb_reg_code_set_value(reg, value); ++ bb_reg_code_set_offset(reg, r->regs[reg].offset); ++ } ++ /* Set any memory contents, e.g. pt_regs. Adjust RSP as required. */ ++ offset_address = 0; ++ for (i = 0; i < r->mem_size; ++i) { ++ offset_address = max_t(int, ++ r->mem[i].offset_address + KDB_WORD_SIZE, ++ offset_address); ++ } ++ if (bb_reg_code_offset(BBRG_RSP) > -offset_address) ++ bb_adjust_osp(BBRG_RSP, -offset_address - bb_reg_code_offset(BBRG_RSP)); ++ for (i = 0; i < r->mem_size; ++i) { ++ value = r->mem[i].value; ++ if (test_bit(value, r->skip_mem.bits)) { ++ /* this memory entry is not defined for this label */ ++ continue; ++ } ++ bb_memory_set_reg_value(BBRG_RSP, r->mem[i].offset_address, ++ value, 0); ++ bb_reg_set_undef(value); ++ } ++ return; ++} ++ ++static void ++bb_pass2_start_block(int number) ++{ ++ int i, j, k, first, changed; ++ size_t size; ++ struct bb_jmp *bb_jmp; ++ struct bb_reg_state *state; ++ struct bb_memory_contains *c1, *c2; ++ bb_reg_state->mem_count = bb_reg_state_max; ++ size = bb_reg_state_size(bb_reg_state); ++ memset(bb_reg_state, 0, size); ++ ++ if (number == 0) { ++ /* The first block is assumed to have well defined inputs */ ++ bb_start_block0(); ++ /* Some assembler labels have non-standard entry ++ * states. ++ */ ++ bb_start_block0_special(); ++ bb_reg_state_print(bb_reg_state); ++ return; ++ } ++ ++ /* Merge all the input states for the current bb together */ ++ first = 1; ++ changed = 0; ++ for (i = 0; i < bb_jmp_count; ++i) { ++ bb_jmp = bb_jmp_list + i; ++ if (bb_jmp->to != bb_curr->start) ++ continue; ++ state = bb_jmp->state; ++ if (!state) ++ continue; ++ if (first) { ++ size = bb_reg_state_size(state); ++ memcpy(bb_reg_state, state, size); ++ KDB_DEBUG_BB(" first state %p\n", state); ++ bb_reg_state_print(bb_reg_state); ++ first = 0; ++ continue; ++ } ++ ++ KDB_DEBUG_BB(" merging state %p\n", state); ++ /* Merge the register states */ ++ for (j = 0; j < ARRAY_SIZE(state->contains); ++j) { ++ if (memcmp(bb_reg_state->contains + j, ++ state->contains + j, ++ sizeof(bb_reg_state->contains[0]))) { ++ /* Different states for this register from two ++ * or more inputs, make it undefined. ++ */ ++ if (bb_reg_state->contains[j].value == ++ BBRG_UNDEFINED) { ++ KDB_DEBUG_BB(" ignoring %s\n", ++ bbrg_name[j + BBRG_RAX]); ++ } else { ++ bb_reg_set_undef(BBRG_RAX + j); ++ changed = 1; ++ } ++ } ++ } ++ ++ /* Merge the memory states. This relies on both ++ * bb_reg_state->memory and state->memory being sorted in ++ * descending order, with undefined entries at the end. ++ */ ++ c1 = bb_reg_state->memory; ++ c2 = state->memory; ++ j = k = 0; ++ while (j < bb_reg_state->mem_count && ++ k < state->mem_count) { ++ if (c1->offset_address < c2->offset_address) { ++ KDB_DEBUG_BB_OFFSET(c2->offset_address, ++ " ignoring c2->offset_address ", ++ "\n"); ++ ++c2; ++ ++k; ++ continue; ++ } ++ if (c1->offset_address > c2->offset_address) { ++ /* Memory location is not in all input states, ++ * delete the memory location. ++ */ ++ bb_delete_memory(c1->offset_address); ++ changed = 1; ++ ++c1; ++ ++j; ++ continue; ++ } ++ if (memcmp(c1, c2, sizeof(*c1))) { ++ /* Same location, different contents, delete ++ * the memory location. ++ */ ++ bb_delete_memory(c1->offset_address); ++ KDB_DEBUG_BB_OFFSET(c2->offset_address, ++ " ignoring c2->offset_address ", ++ "\n"); ++ changed = 1; ++ } ++ ++c1; ++ ++c2; ++ ++j; ++ ++k; ++ } ++ while (j < bb_reg_state->mem_count) { ++ bb_delete_memory(c1->offset_address); ++ changed = 1; ++ ++c1; ++ ++j; ++ } ++ } ++ if (changed) { ++ KDB_DEBUG_BB(" final state\n"); ++ bb_reg_state_print(bb_reg_state); ++ } ++} ++ ++/* We have reached the exit point from the current function, either a call to ++ * the next function or the instruction that was about to executed when an ++ * interrupt occurred. Save the current register state in bb_exit_state. ++ */ ++ ++static void ++bb_save_exit_state(void) ++{ ++ size_t size; ++ debug_kfree(bb_exit_state); ++ bb_exit_state = NULL; ++ bb_reg_state_canonicalize(); ++ size = bb_reg_state_size(bb_reg_state); ++ bb_exit_state = debug_kmalloc(size, GFP_ATOMIC); ++ if (!bb_exit_state) { ++ kdb_printf("\n\n%s: out of debug_kmalloc\n", __FUNCTION__); ++ bb_giveup = 1; ++ return; ++ } ++ memcpy(bb_exit_state, bb_reg_state, size); ++} ++ ++static int ++bb_pass2_do_changed_blocks(int allow_missing) ++{ ++ int i, j, missing, changed, maxloops; ++ unsigned long addr; ++ struct bb_jmp *bb_jmp; ++ KDB_DEBUG_BB("\n %s: allow_missing %d\n", __FUNCTION__, allow_missing); ++ /* Absolute worst case is we have to iterate over all the basic blocks ++ * in an "out of order" state, each iteration losing one register or ++ * memory state. Any more loops than that is a bug. "out of order" ++ * means that the layout of blocks in memory does not match the logic ++ * flow through those blocks so (for example) block 27 comes before ++ * block 2. To allow for out of order blocks, multiply maxloops by the ++ * number of blocks. ++ */ ++ maxloops = (KDB_INT_REGISTERS + bb_reg_state_max) * bb_count; ++ changed = 1; ++ do { ++ changed = 0; ++ for (i = 0; i < bb_count; ++i) { ++ bb_curr = bb_list[i]; ++ if (!bb_curr->changed) ++ continue; ++ missing = 0; ++ for (j = 0, bb_jmp = bb_jmp_list; ++ j < bb_jmp_count; ++ ++j, ++bb_jmp) { ++ if (bb_jmp->to == bb_curr->start && ++ !bb_jmp->state) ++ ++missing; ++ } ++ if (missing > allow_missing) ++ continue; ++ bb_curr->changed = 0; ++ changed = 1; ++ KDB_DEBUG_BB("\n bb[%d]\n", i); ++ bb_pass2_start_block(i); ++ for (addr = bb_curr->start; ++ addr <= bb_curr->end; ) { ++ bb_curr_addr = addr; ++ if (addr == bb_exit_addr) ++ bb_save_exit_state(); ++ addr += kdba_id_printinsn(addr, &kdb_di); ++ kdb_di.fprintf_func(NULL, "\n"); ++ if (bb_giveup) ++ goto done; ++ } ++ if (!bb_exit_state) { ++ /* ATTRIB_NORET functions are a problem with ++ * the current gcc. Allow the trailing address ++ * a bit of leaway. ++ */ ++ if (addr == bb_exit_addr || ++ addr == bb_exit_addr + 1) ++ bb_save_exit_state(); ++ } ++ if (bb_curr->drop_through) ++ bb_transfer(bb_curr->end, ++ bb_list[i+1]->start, 1); ++ } ++ if (maxloops-- == 0) { ++ kdb_printf("\n\n%s maxloops reached\n", ++ __FUNCTION__); ++ bb_giveup = 1; ++ goto done; ++ } ++ } while(changed); ++done: ++ for (i = 0; i < bb_count; ++i) { ++ bb_curr = bb_list[i]; ++ if (bb_curr->changed) ++ return 1; /* more to do, increase allow_missing */ ++ } ++ return 0; /* all blocks done */ ++} ++ ++/* Assume that the current function is a pass through function that does not ++ * refer to its register parameters. Exclude known asmlinkage functions and ++ * assume the other functions actually use their registers. ++ */ ++ ++static void ++bb_assume_pass_through(void) ++{ ++ static int first_time = 1; ++ if (strncmp(bb_func_name, "sys_", 4) == 0 || ++ strncmp(bb_func_name, "compat_sys_", 11) == 0 || ++ strcmp(bb_func_name, "schedule") == 0 || ++ strcmp(bb_func_name, "do_softirq") == 0 || ++ strcmp(bb_func_name, "printk") == 0 || ++ strcmp(bb_func_name, "vprintk") == 0 || ++ strcmp(bb_func_name, "preempt_schedule") == 0 || ++ strcmp(bb_func_name, "start_kernel") == 0 || ++ strcmp(bb_func_name, "csum_partial") == 0 || ++ strcmp(bb_func_name, "csum_partial_copy_generic") == 0 || ++ strcmp(bb_func_name, "math_state_restore") == 0 || ++ strcmp(bb_func_name, "panic") == 0 || ++ strcmp(bb_func_name, "kdb_printf") == 0 || ++ strcmp(bb_func_name, "kdb_interrupt") == 0) ++ return; ++ if (bb_asmlinkage_arch()) ++ return; ++ bb_reg_params = REGPARM; ++ if (first_time) { ++ kdb_printf(" %s has memory parameters but no register " ++ "parameters.\n Assuming it is a 'pass " ++ "through' function that does not refer to " ++ "its register\n parameters and setting %d " ++ "register parameters\n", ++ bb_func_name, REGPARM); ++ first_time = 0; ++ return; ++ } ++ kdb_printf(" Assuming %s is 'pass through' with %d register " ++ "parameters\n", ++ bb_func_name, REGPARM); ++} ++ ++static void ++bb_pass2(void) ++{ ++ int allow_missing; ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) ++ kdb_printf("%s: start\n", __FUNCTION__); ++ ++ kdb_di.fprintf_func = bb_dis_pass2; ++ kdb_di.print_address_func = bb_printaddr_pass2; ++ ++ bb_reg_state = debug_kmalloc(sizeof(*bb_reg_state), GFP_ATOMIC); ++ if (!bb_reg_state) { ++ kdb_printf("\n\n%s: out of debug_kmalloc\n", __FUNCTION__); ++ bb_giveup = 1; ++ return; ++ } ++ bb_list[0]->changed = 1; ++ ++ /* If a block does not have all its input states available then it is ++ * possible for a register to initially appear to hold a known value, ++ * but when other inputs are available then it becomes a variable ++ * value. The initial false state of "known" can generate false values ++ * for other registers and can even make it look like stack locations ++ * are being changed. ++ * ++ * To avoid these false positives, only process blocks which have all ++ * their inputs defined. That gives a clean depth first traversal of ++ * the tree, except for loops. If there are any loops, then start ++ * processing blocks with one missing input, then two missing inputs ++ * etc. ++ * ++ * Absolute worst case is we have to iterate over all the jmp entries, ++ * each iteration allowing one more missing input. Any more loops than ++ * that is a bug. Watch out for the corner case of 0 jmp entries. ++ */ ++ for (allow_missing = 0; allow_missing <= bb_jmp_count; ++allow_missing) { ++ if (!bb_pass2_do_changed_blocks(allow_missing)) ++ break; ++ if (bb_giveup) ++ break; ++ } ++ if (allow_missing > bb_jmp_count) { ++ kdb_printf("\n\n%s maxloops reached\n", ++ __FUNCTION__); ++ bb_giveup = 1; ++ return; ++ } ++ ++ if (bb_memory_params && bb_reg_params) ++ bb_reg_params = REGPARM; ++ if (REGPARM && ++ bb_memory_params && ++ !bb_reg_params) ++ bb_assume_pass_through(); ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) { ++ kdb_printf("%s: end bb_reg_params %d bb_memory_params %d\n", ++ __FUNCTION__, bb_reg_params, bb_memory_params); ++ if (bb_exit_state) { ++ kdb_printf("%s: bb_exit_state at " kdb_bfd_vma_fmt0 "\n", ++ __FUNCTION__, bb_exit_addr); ++ bb_do_reg_state_print(bb_exit_state); ++ } ++ } ++} ++ ++static void ++bb_cleanup(void) ++{ ++ int i; ++ struct bb* bb; ++ struct bb_reg_state *state; ++ while (bb_count) { ++ bb = bb_list[0]; ++ bb_delete(0); ++ } ++ debug_kfree(bb_list); ++ bb_list = NULL; ++ bb_count = bb_max = 0; ++ for (i = 0; i < bb_jmp_count; ++i) { ++ state = bb_jmp_list[i].state; ++ if (state && --state->ref_count == 0) ++ debug_kfree(state); ++ } ++ debug_kfree(bb_jmp_list); ++ bb_jmp_list = NULL; ++ bb_jmp_count = bb_jmp_max = 0; ++ debug_kfree(bb_reg_state); ++ bb_reg_state = NULL; ++ bb_reg_state_max = 0; ++ debug_kfree(bb_exit_state); ++ bb_exit_state = NULL; ++ bb_reg_params = bb_memory_params = 0; ++ bb_giveup = 0; ++} ++ ++static int ++bb_spurious_global_label(const char *func_name) ++{ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(bb_spurious); ++i) { ++ if (strcmp(bb_spurious[i], func_name) == 0) ++ return 1; ++ } ++ return 0; ++} ++ ++/* Given the current actual register contents plus the exit state deduced from ++ * a basic block analysis of the current function, rollback the actual register ++ * contents to the values they had on entry to this function. ++ */ ++ ++static void ++bb_actual_rollback(const struct kdb_activation_record *ar) ++{ ++ int i, offset_address; ++ struct bb_memory_contains *c; ++ enum bb_reg_code reg; ++ unsigned long address, osp = 0; ++ struct bb_actual new[ARRAY_SIZE(bb_actual)]; ++ ++ ++ if (!bb_exit_state) { ++ kdb_printf("%s: no bb_exit_state, cannot rollback\n", ++ __FUNCTION__); ++ bb_giveup = 1; ++ return; ++ } ++ memcpy(bb_reg_state, bb_exit_state, bb_reg_state_size(bb_exit_state)); ++ memset(new, 0, sizeof(new)); ++ ++ /* The most important register for obtaining saved state is rsp so get ++ * its new value first. Prefer rsp if it is valid, then other ++ * registers. Saved values of rsp in memory are unusable without a ++ * register that points to memory. ++ */ ++ if (!bb_actual_valid(BBRG_RSP)) { ++ kdb_printf("%s: no starting value for RSP, cannot rollback\n", ++ __FUNCTION__); ++ bb_giveup = 1; ++ return; ++ } ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) ++ kdb_printf("%s: rsp " kdb_bfd_vma_fmt0, ++ __FUNCTION__, bb_actual_value(BBRG_RSP)); ++ i = BBRG_RSP; ++ if (!bb_is_osp_defined(i)) { ++ for (i = BBRG_RAX; i < BBRG_RAX + KDB_INT_REGISTERS; ++i) { ++ if (bb_is_osp_defined(i) && bb_actual_valid(i)) ++ break; ++ } ++ } ++ if (bb_is_osp_defined(i) && bb_actual_valid(i)) { ++ osp = new[BBRG_RSP - BBRG_RAX].value = ++ bb_actual_value(i) - bb_reg_code_offset(i); ++ new[BBRG_RSP - BBRG_RAX].valid = 1; ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) ++ kdb_printf(" -> osp " kdb_bfd_vma_fmt0 "\n", osp); ++ } else { ++ bb_actual_set_valid(BBRG_RSP, 0); ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) ++ kdb_printf(" -> undefined\n"); ++ kdb_printf("%s: no ending value for RSP, cannot rollback\n", ++ __FUNCTION__); ++ bb_giveup = 1; ++ return; ++ } ++ ++ /* Now the other registers. First look at register values that have ++ * been copied to other registers. ++ */ ++ for (i = BBRG_RAX; i < BBRG_RAX + KDB_INT_REGISTERS; ++i) { ++ reg = bb_reg_code_value(i); ++ if (bb_is_int_reg(reg)) { ++ new[reg - BBRG_RAX] = bb_actual[i - BBRG_RAX]; ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) { ++ kdb_printf("%s: %s is in %s ", ++ __FUNCTION__, ++ bbrg_name[reg], ++ bbrg_name[i]); ++ if (bb_actual_valid(i)) ++ kdb_printf(" -> " kdb_bfd_vma_fmt0 "\n", ++ bb_actual_value(i)); ++ else ++ kdb_printf("(invalid)\n"); ++ } ++ } ++ } ++ ++ /* Finally register values that have been saved on stack */ ++ for (i = 0, c = bb_reg_state->memory; ++ i < bb_reg_state->mem_count; ++ ++i, ++c) { ++ offset_address = c->offset_address; ++ reg = c->value; ++ if (!bb_is_int_reg(reg)) ++ continue; ++ address = osp + offset_address; ++ if (address < ar->stack.logical_start || ++ address >= ar->stack.logical_end) { ++ new[reg - BBRG_RAX].value = 0; ++ new[reg - BBRG_RAX].valid = 0; ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) ++ kdb_printf("%s: %s -> undefined\n", ++ __FUNCTION__, ++ bbrg_name[reg]); ++ } else { ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) { ++ kdb_printf("%s: %s -> *(osp", ++ __FUNCTION__, ++ bbrg_name[reg]); ++ KDB_DEBUG_BB_OFFSET_PRINTF(offset_address, "", " "); ++ kdb_printf(kdb_bfd_vma_fmt0, address); ++ } ++ new[reg - BBRG_RAX].value = *(bfd_vma *)address; ++ new[reg - BBRG_RAX].valid = 1; ++ if (KDB_DEBUG(BB) | KDB_DEBUG(BB_SUMM)) ++ kdb_printf(") = " kdb_bfd_vma_fmt0 "\n", ++ new[reg - BBRG_RAX].value); ++ } ++ } ++ ++ memcpy(bb_actual, new, sizeof(bb_actual)); ++} ++ ++/* Return true if the current function is an interrupt handler */ ++ ++static bool ++bb_interrupt_handler(kdb_machreg_t rip) ++{ ++ unsigned long disp8, disp32, target, addr = (unsigned long)rip; ++ unsigned char code[5]; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(bb_hardware_handlers); ++i) ++ if (strcmp(bb_func_name, bb_hardware_handlers[i]) == 0) ++ return 1; ++ ++ /* Given the large number of interrupt handlers, it is easiest to look ++ * at the next instruction and see if it is a jmp to the common exit ++ * routines. ++ */ ++ if (kdb_getarea(code, addr) || ++ kdb_getword(&disp32, addr+1, 4) || ++ kdb_getword(&disp8, addr+1, 1)) ++ return 0; /* not a valid code address */ ++ if (code[0] == 0xe9) { ++ target = addr + (s32) disp32 + 5; /* jmp disp32 */ ++ if (target == bb_ret_from_intr || ++ target == bb_common_interrupt || ++ target == bb_error_entry) ++ return 1; ++ } ++ if (code[0] == 0xeb) { ++ target = addr + (s8) disp8 + 2; /* jmp disp8 */ ++ if (target == bb_ret_from_intr || ++ target == bb_common_interrupt || ++ target == bb_error_entry) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* Copy argument information that was deduced by the basic block analysis and ++ * rollback into the kdb stack activation record. ++ */ ++ ++static void ++bb_arguments(struct kdb_activation_record *ar) ++{ ++ int i; ++ enum bb_reg_code reg; ++ kdb_machreg_t rsp; ++ ar->args = bb_reg_params + bb_memory_params; ++ bitmap_zero(ar->valid.bits, KDBA_MAXARGS); ++ for (i = 0; i < bb_reg_params; ++i) { ++ reg = bb_param_reg[i]; ++ if (bb_actual_valid(reg)) { ++ ar->arg[i] = bb_actual_value(reg); ++ set_bit(i, ar->valid.bits); ++ } ++ } ++ if (!bb_actual_valid(BBRG_RSP)) ++ return; ++ rsp = bb_actual_value(BBRG_RSP); ++ for (i = bb_reg_params; i < ar->args; ++i) { ++ rsp += KDB_WORD_SIZE; ++ if (kdb_getarea(ar->arg[i], rsp) == 0) ++ set_bit(i, ar->valid.bits); ++ } ++} ++ ++/* Given an exit address from a function, decompose the entire function into ++ * basic blocks and determine the register state at the exit point. ++ */ ++ ++static void ++kdb_bb(unsigned long exit) ++{ ++ kdb_symtab_t symtab; ++ if (!kdbnearsym(exit, &symtab)) { ++ kdb_printf("%s: address " kdb_bfd_vma_fmt0 " not recognised\n", ++ __FUNCTION__, exit); ++ bb_giveup = 1; ++ return; ++ } ++ bb_exit_addr = exit; ++ bb_mod_name = symtab.mod_name; ++ bb_func_name = symtab.sym_name; ++ bb_func_start = symtab.sym_start; ++ bb_func_end = symtab.sym_end; ++ /* Various global labels exist in the middle of assembler code and have ++ * a non-standard state. Ignore these labels and use the start of the ++ * previous label instead. ++ */ ++ while (bb_spurious_global_label(symtab.sym_name)) { ++ if (!kdbnearsym(symtab.sym_start - 1, &symtab)) ++ break; ++ bb_func_start = symtab.sym_start; ++ } ++ bb_mod_name = symtab.mod_name; ++ bb_func_name = symtab.sym_name; ++ bb_func_start = symtab.sym_start; ++ /* Ignore spurious labels past this point and use the next non-spurious ++ * label as the end point. ++ */ ++ if (kdbnearsym(bb_func_end, &symtab)) { ++ while (bb_spurious_global_label(symtab.sym_name)) { ++ bb_func_end = symtab.sym_end; ++ if (!kdbnearsym(symtab.sym_end + 1, &symtab)) ++ break; ++ } ++ } ++ bb_pass1(); ++ if (!bb_giveup) ++ bb_pass2(); ++ if (bb_giveup) ++ kdb_printf("%s: " kdb_bfd_vma_fmt0 ++ " [%s]%s failed at " kdb_bfd_vma_fmt0 "\n\n", ++ __FUNCTION__, exit, ++ bb_mod_name, bb_func_name, bb_curr_addr); ++} ++ ++static int ++kdb_bb1(int argc, const char **argv) ++{ ++ int diag, nextarg = 1; ++ kdb_machreg_t addr; ++ unsigned long offset; ++ ++ bb_cleanup(); /* in case previous command was interrupted */ ++ kdba_id_init(&kdb_di); ++ if (argc != 1) ++ return KDB_ARGCOUNT; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); ++ if (diag) ++ return diag; ++ if (!addr) ++ return KDB_BADADDR; ++ kdb_save_flags(); ++ kdb_flags |= KDB_DEBUG_FLAG_BB << KDB_DEBUG_FLAG_SHIFT; ++ kdb_bb(addr); ++ bb_cleanup(); ++ kdb_restore_flags(); ++ kdbnearsym_cleanup(); ++ return 0; ++} ++ ++/* Run a basic block analysis on every function in the base kernel. Used as a ++ * global sanity check to find errors in the basic block code. ++ */ ++ ++static int ++kdb_bb_all(int argc, const char **argv) ++{ ++ loff_t pos = 0; ++ const char *symname; ++ unsigned long addr; ++ int i, max_errors = 20; ++ struct bb_name_state *r; ++ kdb_printf("%s: build variables:" ++ " CCVERSION \"" __stringify(CCVERSION) "\"" ++#ifdef CONFIG_X86_64 ++ " CONFIG_X86_64" ++#endif ++#ifdef CONFIG_4KSTACKS ++ " CONFIG_4KSTACKS" ++#endif ++#ifdef CONFIG_PREEMPT ++ " CONFIG_PREEMPT" ++#endif ++#ifdef CONFIG_VM86 ++ " CONFIG_VM86" ++#endif ++#ifdef CONFIG_FRAME_POINTER ++ " CONFIG_FRAME_POINTER" ++#endif ++#ifdef CONFIG_TRACE_IRQFLAGS ++ " CONFIG_TRACE_IRQFLAGS" ++#endif ++#ifdef CONFIG_HIBERNATION ++ " CONFIG_HIBERNATION" ++#endif ++#ifdef CONFIG_KPROBES ++ " CONFIG_KPROBES" ++#endif ++#ifdef CONFIG_KEXEC ++ " CONFIG_KEXEC" ++#endif ++#ifdef CONFIG_MATH_EMULATION ++ " CONFIG_MATH_EMULATION" ++#endif ++#ifdef CONFIG_XEN ++ " CONFIG_XEN" ++#endif ++#ifdef CONFIG_DEBUG_INFO ++ " CONFIG_DEBUG_INFO" ++#endif ++#ifdef NO_SIBLINGS ++ " NO_SIBLINGS" ++#endif ++ " REGPARM=" __stringify(REGPARM) ++ "\n\n", __FUNCTION__); ++ for (i = 0, r = bb_special_cases; ++ i < ARRAY_SIZE(bb_special_cases); ++ ++i, ++r) { ++ if (!r->address) ++ kdb_printf("%s: cannot find special_case name %s\n", ++ __FUNCTION__, r->name); ++ } ++ for (i = 0; i < ARRAY_SIZE(bb_spurious); ++i) { ++ if (!kallsyms_lookup_name(bb_spurious[i])) ++ kdb_printf("%s: cannot find spurious label %s\n", ++ __FUNCTION__, bb_spurious[i]); ++ } ++ while ((symname = kdb_walk_kallsyms(&pos))) { ++ if (strcmp(symname, "_stext") == 0 || ++ strcmp(symname, "stext") == 0) ++ break; ++ } ++ if (!symname) { ++ kdb_printf("%s: cannot find _stext\n", __FUNCTION__); ++ return 0; ++ } ++ kdba_id_init(&kdb_di); ++ i = 0; ++ while ((symname = kdb_walk_kallsyms(&pos))) { ++ if (strcmp(symname, "_etext") == 0) ++ break; ++ if (i++ % 100 == 0) ++ kdb_printf("."); ++ /* x86_64 has some 16 bit functions that appear between stext ++ * and _etext. Skip them. ++ */ ++ if (strcmp(symname, "verify_cpu") == 0 || ++ strcmp(symname, "verify_cpu_noamd") == 0 || ++ strcmp(symname, "verify_cpu_sse_test") == 0 || ++ strcmp(symname, "verify_cpu_no_longmode") == 0 || ++ strcmp(symname, "verify_cpu_sse_ok") == 0 || ++ strcmp(symname, "mode_seta") == 0 || ++ strcmp(symname, "bad_address") == 0 || ++ strcmp(symname, "wakeup_code") == 0 || ++ strcmp(symname, "wakeup_code_start") == 0 || ++ strcmp(symname, "wakeup_start") == 0 || ++ strcmp(symname, "wakeup_32_vector") == 0 || ++ strcmp(symname, "wakeup_32") == 0 || ++ strcmp(symname, "wakeup_long64_vector") == 0 || ++ strcmp(symname, "wakeup_long64") == 0 || ++ strcmp(symname, "gdta") == 0 || ++ strcmp(symname, "idt_48a") == 0 || ++ strcmp(symname, "gdt_48a") == 0 || ++ strcmp(symname, "bogus_real_magic") == 0 || ++ strcmp(symname, "bogus_64_magic") == 0 || ++ strcmp(symname, "no_longmode") == 0 || ++ strcmp(symname, "mode_set") == 0 || ++ strcmp(symname, "mode_seta") == 0 || ++ strcmp(symname, "setbada") == 0 || ++ strcmp(symname, "check_vesa") == 0 || ++ strcmp(symname, "check_vesaa") == 0 || ++ strcmp(symname, "_setbada") == 0 || ++ strcmp(symname, "wakeup_stack_begin") == 0 || ++ strcmp(symname, "wakeup_stack") == 0 || ++ strcmp(symname, "wakeup_level4_pgt") == 0 || ++ strcmp(symname, "acpi_copy_wakeup_routine") == 0 || ++ strcmp(symname, "wakeup_end") == 0 || ++ strcmp(symname, "do_suspend_lowlevel_s4bios") == 0 || ++ strcmp(symname, "do_suspend_lowlevel") == 0 || ++ strcmp(symname, "wakeup_pmode_return") == 0 || ++ strcmp(symname, "restore_registers") == 0) ++ continue; ++ /* __kprobes_text_end contains branches to the middle of code, ++ * with undefined states. ++ */ ++ if (strcmp(symname, "__kprobes_text_end") == 0) ++ continue; ++ /* Data in the middle of the text segment :( */ ++ if (strcmp(symname, "level2_kernel_pgt") == 0 || ++ strcmp(symname, "level3_kernel_pgt") == 0) ++ continue; ++ if (bb_spurious_global_label(symname)) ++ continue; ++ if ((addr = kallsyms_lookup_name(symname)) == 0) ++ continue; ++ // kdb_printf("BB " kdb_bfd_vma_fmt0 " %s\n", addr, symname); ++ bb_cleanup(); /* in case previous command was interrupted */ ++ kdbnearsym_cleanup(); ++ kdb_bb(addr); ++ touch_nmi_watchdog(); ++ if (bb_giveup) { ++ if (max_errors-- == 0) { ++ kdb_printf("%s: max_errors reached, giving up\n", ++ __FUNCTION__); ++ break; ++ } else { ++ bb_giveup = 0; ++ } ++ } ++ } ++ kdb_printf("\n"); ++ bb_cleanup(); ++ kdbnearsym_cleanup(); ++ return 0; ++} ++ ++/* ++ *============================================================================= ++ * ++ * Everything above this line is doing basic block analysis, function by ++ * function. Everything below this line uses the basic block data to do a ++ * complete backtrace over all functions that are used by a process. ++ * ++ *============================================================================= ++ */ ++ ++ ++/*============================================================================*/ ++/* */ ++/* Most of the backtrace code and data is common to x86_64 and i386. This */ ++/* large ifdef contains all of the differences between the two architectures. */ ++/* */ ++/* Make sure you update the correct section of this ifdef. */ ++/* */ ++/*============================================================================*/ ++#define XCS "cs" ++#define RSP "sp" ++#define RIP "ip" ++#define ARCH_RSP sp ++#define ARCH_RIP ip ++ ++#ifdef CONFIG_X86_64 ++ ++#define ARCH_NORMAL_PADDING (16 * 8) ++ ++/* x86_64 has multiple alternate stacks, with different sizes and different ++ * offsets to get the link from one stack to the next. All of the stacks are ++ * in the per_cpu area: either in the orig_ist or irq_stack_ptr. Debug events ++ * can even have multiple nested stacks within the single physical stack, ++ * each nested stack has its own link and some of those links are wrong. ++ * ++ * Consistent it's not! ++ * ++ * Do not assume that these stacks are aligned on their size. ++ */ ++#define INTERRUPT_STACK (N_EXCEPTION_STACKS + 1) ++void ++kdba_get_stack_info_alternate(kdb_machreg_t addr, int cpu, ++ struct kdb_activation_record *ar) ++{ ++ static struct { ++ const char *id; ++ unsigned int total_size; ++ unsigned int nested_size; ++ unsigned int next; ++ } *sdp, stack_data[] = { ++ [STACKFAULT_STACK - 1] = { "stackfault", EXCEPTION_STKSZ, EXCEPTION_STKSZ, EXCEPTION_STKSZ - 2*sizeof(void *) }, ++ [DOUBLEFAULT_STACK - 1] = { "doublefault", EXCEPTION_STKSZ, EXCEPTION_STKSZ, EXCEPTION_STKSZ - 2*sizeof(void *) }, ++ [NMI_STACK - 1] = { "nmi", EXCEPTION_STKSZ, EXCEPTION_STKSZ, EXCEPTION_STKSZ - 2*sizeof(void *) }, ++ [DEBUG_STACK - 1] = { "debug", DEBUG_STKSZ, EXCEPTION_STKSZ, EXCEPTION_STKSZ - 2*sizeof(void *) }, ++ [MCE_STACK - 1] = { "machine check", EXCEPTION_STKSZ, EXCEPTION_STKSZ, EXCEPTION_STKSZ - 2*sizeof(void *) }, ++ [INTERRUPT_STACK - 1] = { "interrupt", IRQ_STACK_SIZE, IRQ_STACK_SIZE, IRQ_STACK_SIZE - sizeof(void *) }, ++ }; ++ unsigned long total_start = 0, total_size, total_end; ++ int sd, found = 0; ++ extern unsigned long kdba_orig_ist(int, int); ++ ++ for (sd = 0, sdp = stack_data; ++ sd < ARRAY_SIZE(stack_data); ++ ++sd, ++sdp) { ++ total_size = sdp->total_size; ++ if (!total_size) ++ continue; /* in case stack_data[] has any holes */ ++ if (cpu < 0) { ++ /* Arbitrary address which can be on any cpu, see if it ++ * falls within any of the alternate stacks ++ */ ++ int c; ++ for_each_online_cpu(c) { ++ if (sd == INTERRUPT_STACK - 1) ++ total_end = (unsigned long)per_cpu(irq_stack_ptr, c); ++ else ++ total_end = per_cpu(orig_ist, c).ist[sd]; ++ total_start = total_end - total_size; ++ if (addr >= total_start && addr < total_end) { ++ found = 1; ++ cpu = c; ++ break; ++ } ++ } ++ if (!found) ++ continue; ++ } ++ /* Only check the supplied or found cpu */ ++ if (sd == INTERRUPT_STACK - 1) ++ total_end = (unsigned long)per_cpu(irq_stack_ptr, cpu); ++ else ++ total_end = per_cpu(orig_ist, cpu).ist[sd]; ++ total_start = total_end - total_size; ++ if (addr >= total_start && addr < total_end) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ return; ++ /* find which nested stack the address is in */ ++ while (addr > total_start + sdp->nested_size) ++ total_start += sdp->nested_size; ++ ar->stack.physical_start = total_start; ++ ar->stack.physical_end = total_start + sdp->nested_size; ++ ar->stack.logical_start = total_start; ++ ar->stack.logical_end = total_start + sdp->next; ++ ar->stack.next = *(unsigned long *)ar->stack.logical_end; ++ ar->stack.id = sdp->id; ++ ++ /* Nasty: when switching to the interrupt stack, the stack state of the ++ * caller is split over two stacks, the original stack and the ++ * interrupt stack. One word (the previous frame pointer) is stored on ++ * the interrupt stack, the rest of the interrupt data is in the old ++ * frame. To make the interrupted stack state look as though it is ++ * contiguous, copy the missing word from the interrupt stack to the ++ * original stack and adjust the new stack pointer accordingly. ++ */ ++ ++ if (sd == INTERRUPT_STACK - 1) { ++ *(unsigned long *)(ar->stack.next - KDB_WORD_SIZE) = ++ ar->stack.next; ++ ar->stack.next -= KDB_WORD_SIZE; ++ } ++} ++ ++/* rip is not in the thread struct for x86_64. We know that the stack value ++ * was saved in schedule near the label thread_return. Setting rip to ++ * thread_return lets the stack trace find that we are in schedule and ++ * correctly decode its prologue. ++ */ ++ ++static kdb_machreg_t ++kdba_bt_stack_rip(const struct task_struct *p) ++{ ++ return bb_thread_return; ++} ++ ++#else /* !CONFIG_X86_64 */ ++ ++#define ARCH_NORMAL_PADDING (19 * 4) ++ ++#ifdef CONFIG_4KSTACKS ++static struct thread_info **kdba_hardirq_ctx, **kdba_softirq_ctx; ++#endif /* CONFIG_4KSTACKS */ ++ ++/* On a 4K stack kernel, hardirq_ctx and softirq_ctx are [NR_CPUS] arrays. The ++ * first element of each per-cpu stack is a struct thread_info. ++ */ ++void ++kdba_get_stack_info_alternate(kdb_machreg_t addr, int cpu, ++ struct kdb_activation_record *ar) ++{ ++#ifdef CONFIG_4KSTACKS ++ struct thread_info *tinfo; ++ tinfo = (struct thread_info *)(addr & -THREAD_SIZE); ++ if (cpu < 0) { ++ /* Arbitrary address, see if it falls within any of the irq ++ * stacks ++ */ ++ int found = 0; ++ for_each_online_cpu(cpu) { ++ if (tinfo == kdba_hardirq_ctx[cpu] || ++ tinfo == kdba_softirq_ctx[cpu]) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ return; ++ } ++ if (tinfo == kdba_hardirq_ctx[cpu] || ++ tinfo == kdba_softirq_ctx[cpu]) { ++ ar->stack.physical_start = (kdb_machreg_t)tinfo; ++ ar->stack.physical_end = ar->stack.physical_start + THREAD_SIZE; ++ ar->stack.logical_start = ar->stack.physical_start + ++ sizeof(struct thread_info); ++ ar->stack.logical_end = ar->stack.physical_end; ++ ar->stack.next = tinfo->previous_esp; ++ if (tinfo == kdba_hardirq_ctx[cpu]) ++ ar->stack.id = "hardirq_ctx"; ++ else ++ ar->stack.id = "softirq_ctx"; ++ } ++#endif /* CONFIG_4KSTACKS */ ++} ++ ++/* rip is in the thread struct for i386 */ ++ ++static kdb_machreg_t ++kdba_bt_stack_rip(const struct task_struct *p) ++{ ++ return p->thread.ip; ++} ++ ++#endif /* CONFIG_X86_64 */ ++ ++/* Given an address which claims to be on a stack, an optional cpu number and ++ * an optional task address, get information about the stack. ++ * ++ * t == NULL, cpu < 0 indicates an arbitrary stack address with no associated ++ * struct task, the address can be in an alternate stack or any task's normal ++ * stack. ++ * ++ * t != NULL, cpu >= 0 indicates a running task, the address can be in an ++ * alternate stack or that task's normal stack. ++ * ++ * t != NULL, cpu < 0 indicates a blocked task, the address can only be in that ++ * task's normal stack. ++ * ++ * t == NULL, cpu >= 0 is not a valid combination. ++ */ ++ ++static void ++kdba_get_stack_info(kdb_machreg_t rsp, int cpu, ++ struct kdb_activation_record *ar, ++ const struct task_struct *t) ++{ ++ struct thread_info *tinfo; ++ struct task_struct *g, *p; ++ memset(&ar->stack, 0, sizeof(ar->stack)); ++ if (KDB_DEBUG(ARA)) ++ kdb_printf("%s: " RSP "=0x%lx cpu=%d task=%p\n", ++ __FUNCTION__, rsp, cpu, t); ++ if (t == NULL || cpu >= 0) { ++ kdba_get_stack_info_alternate(rsp, cpu, ar); ++ if (ar->stack.logical_start) ++ goto out; ++ } ++ rsp &= -THREAD_SIZE; ++ tinfo = (struct thread_info *)rsp; ++ if (t == NULL) { ++ /* Arbitrary stack address without an associated task, see if ++ * it falls within any normal process stack, including the idle ++ * tasks. ++ */ ++ kdb_do_each_thread(g, p) { ++ if (tinfo == task_thread_info(p)) { ++ t = p; ++ goto found; ++ } ++ } kdb_while_each_thread(g, p); ++ for_each_online_cpu(cpu) { ++ p = idle_task(cpu); ++ if (tinfo == task_thread_info(p)) { ++ t = p; ++ goto found; ++ } ++ } ++ found: ++ if (KDB_DEBUG(ARA)) ++ kdb_printf("%s: found task %p\n", __FUNCTION__, t); ++ } else if (cpu >= 0) { ++ /* running task */ ++ struct kdb_running_process *krp = kdb_running_process + cpu; ++ if (krp->p != t || tinfo != task_thread_info(t)) ++ t = NULL; ++ if (KDB_DEBUG(ARA)) ++ kdb_printf("%s: running task %p\n", __FUNCTION__, t); ++ } else { ++ /* blocked task */ ++ if (tinfo != task_thread_info(t)) ++ t = NULL; ++ if (KDB_DEBUG(ARA)) ++ kdb_printf("%s: blocked task %p\n", __FUNCTION__, t); ++ } ++ if (t) { ++ ar->stack.physical_start = rsp; ++ ar->stack.physical_end = rsp + THREAD_SIZE; ++ ar->stack.logical_start = rsp + sizeof(struct thread_info); ++ ar->stack.logical_end = ar->stack.physical_end - ARCH_NORMAL_PADDING; ++ ar->stack.next = 0; ++ ar->stack.id = "normal"; ++ } ++out: ++ if (ar->stack.physical_start && KDB_DEBUG(ARA)) { ++ kdb_printf("%s: ar->stack\n", __FUNCTION__); ++ kdb_printf(" physical_start=0x%lx\n", ar->stack.physical_start); ++ kdb_printf(" physical_end=0x%lx\n", ar->stack.physical_end); ++ kdb_printf(" logical_start=0x%lx\n", ar->stack.logical_start); ++ kdb_printf(" logical_end=0x%lx\n", ar->stack.logical_end); ++ kdb_printf(" next=0x%lx\n", ar->stack.next); ++ kdb_printf(" id=%s\n", ar->stack.id); ++ kdb_printf(" set MDCOUNT %ld\n", ++ (ar->stack.physical_end - ar->stack.physical_start) / ++ KDB_WORD_SIZE); ++ kdb_printf(" mds " kdb_machreg_fmt0 "\n", ++ ar->stack.physical_start); ++ } ++} ++ ++static void ++bt_print_one(kdb_machreg_t rip, kdb_machreg_t rsp, ++ const struct kdb_activation_record *ar, ++ const kdb_symtab_t *symtab, int argcount) ++{ ++ int btsymarg = 0; ++ int nosect = 0; ++ ++ kdbgetintenv("BTSYMARG", &btsymarg); ++ kdbgetintenv("NOSECT", &nosect); ++ ++ kdb_printf(kdb_machreg_fmt0, rsp); ++ kdb_symbol_print(rip, symtab, ++ KDB_SP_SPACEB|KDB_SP_VALUE); ++ if (argcount && ar->args) { ++ int i, argc = ar->args; ++ kdb_printf(" ("); ++ if (argc > argcount) ++ argc = argcount; ++ for (i = 0; i < argc; i++) { ++ if (i) ++ kdb_printf(", "); ++ if (test_bit(i, ar->valid.bits)) ++ kdb_printf("0x%lx", ar->arg[i]); ++ else ++ kdb_printf("invalid"); ++ } ++ kdb_printf(")"); ++ } ++ kdb_printf("\n"); ++ if (symtab->sym_name) { ++ if (!nosect) { ++ kdb_printf(" %s", ++ symtab->mod_name); ++ if (symtab->sec_name && symtab->sec_start) ++ kdb_printf(" 0x%lx 0x%lx", ++ symtab->sec_start, symtab->sec_end); ++ kdb_printf(" 0x%lx 0x%lx\n", ++ symtab->sym_start, symtab->sym_end); ++ } ++ } ++ if (argcount && ar->args && btsymarg) { ++ int i, argc = ar->args; ++ kdb_symtab_t arg_symtab; ++ for (i = 0; i < argc; i++) { ++ kdb_machreg_t arg = ar->arg[i]; ++ if (test_bit(i, ar->valid.bits) && ++ kdbnearsym(arg, &arg_symtab)) { ++ kdb_printf(" ARG %2d ", i); ++ kdb_symbol_print(arg, &arg_symtab, ++ KDB_SP_DEFAULT|KDB_SP_NEWLINE); ++ } ++ } ++ } ++} ++ ++static void ++kdba_bt_new_stack(struct kdb_activation_record *ar, kdb_machreg_t *rsp, ++ int *count, int *suppress) ++{ ++ /* Nasty: save_args builds a partial pt_regs, with r15 through ++ * rbx not being filled in. It passes struct pt_regs* to do_IRQ (in ++ * rdi) but the stack pointer is not adjusted to account for r15 ++ * through rbx. This has two effects :- ++ * ++ * (1) struct pt_regs on an external interrupt actually overlaps with ++ * the local stack area used by do_IRQ. Not only are r15-rbx ++ * undefined, the area that claims to hold their values can even ++ * change as the irq is processed. ++ * ++ * (2) The back stack pointer saved for the new frame is not pointing ++ * at pt_regs, it is pointing at rbx within the pt_regs passed to ++ * do_IRQ. ++ * ++ * There is nothing that I can do about (1) but I have to fix (2) ++ * because kdb backtrace looks for the "start" address of pt_regs as it ++ * walks back through the stacks. When switching from the interrupt ++ * stack to another stack, we have to assume that pt_regs has been ++ * seen and turn off backtrace supression. ++ */ ++ int probable_pt_regs = strcmp(ar->stack.id, "interrupt") == 0; ++ *rsp = ar->stack.next; ++ if (KDB_DEBUG(ARA)) ++ kdb_printf("new " RSP "=" kdb_machreg_fmt0 "\n", *rsp); ++ bb_actual_set_value(BBRG_RSP, *rsp); ++ kdba_get_stack_info(*rsp, -1, ar, NULL); ++ if (!ar->stack.physical_start) { ++ kdb_printf("+++ Cannot resolve next stack\n"); ++ } else if (!*suppress) { ++ kdb_printf(" ======================= <%s>\n", ++ ar->stack.id); ++ ++*count; ++ } ++ if (probable_pt_regs) ++ *suppress = 0; ++} ++ ++/* ++ * kdba_bt_stack ++ * ++ * Inputs: ++ * addr Address provided to 'bt' command, if any. ++ * argcount ++ * p Pointer to task for 'btp' command. ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * Ultimately all the bt* commands come through this routine. If ++ * old_style is 0 then it uses the basic block analysis to get an accurate ++ * backtrace with arguments, otherwise it falls back to the old method of ++ * printing anything on stack that looks like a kernel address. ++ * ++ * Allowing for the stack data pushed by the hardware is tricky. We ++ * deduce the presence of hardware pushed data by looking for interrupt ++ * handlers, either by name or by the code that they contain. This ++ * information must be applied to the next function up the stack, because ++ * the hardware data is above the saved rip for the interrupted (next) ++ * function. ++ * ++ * To make things worse, the amount of data pushed is arch specific and ++ * may depend on the rsp for the next function, not the current function. ++ * The number of bytes pushed by hardware cannot be calculated until we ++ * are actually processing the stack for the interrupted function and have ++ * its rsp. ++ * ++ * It is also possible for an interrupt to occur in user space and for the ++ * interrupt handler to also be interrupted. Check the code selector ++ * whenever the previous function is an interrupt handler and stop ++ * backtracing if the interrupt was not in kernel space. ++ */ ++ ++static int ++kdba_bt_stack(kdb_machreg_t addr, int argcount, const struct task_struct *p, ++ int old_style) ++{ ++ struct kdb_activation_record ar; ++ kdb_machreg_t rip = 0, rsp = 0, prev_rsp, cs; ++ kdb_symtab_t symtab; ++ int rip_at_rsp = 0, count = 0, btsp = 0, suppress, ++ interrupt_handler = 0, prev_interrupt_handler = 0, hardware_pushed, ++ prev_noret = 0; ++ struct pt_regs *regs = NULL; ++ ++ kdbgetintenv("BTSP", &btsp); ++ suppress = !btsp; ++ memset(&ar, 0, sizeof(ar)); ++ if (old_style) ++ kdb_printf("Using old style backtrace, unreliable with no arguments\n"); ++ ++ /* ++ * The caller may have supplied an address at which the stack traceback ++ * operation should begin. This address is assumed by this code to ++ * point to a return address on the stack to be traced back. ++ * ++ * Warning: type in the wrong address and you will get garbage in the ++ * backtrace. ++ */ ++ if (addr) { ++ rsp = addr; ++ kdb_getword(&rip, rsp, sizeof(rip)); ++ rip_at_rsp = 1; ++ suppress = 0; ++ kdba_get_stack_info(rsp, -1, &ar, NULL); ++ } else { ++ if (task_curr(p)) { ++ struct kdb_running_process *krp = ++ kdb_running_process + task_cpu(p); ++ kdb_machreg_t cs; ++ regs = krp->regs; ++ if (krp->seqno && ++ krp->p == p && ++ krp->seqno >= kdb_seqno - 1 && ++ !KDB_NULL_REGS(regs)) { ++ /* valid saved state, continue processing */ ++ } else { ++ kdb_printf ++ ("Process did not save state, cannot backtrace\n"); ++ kdb_ps1(p); ++ return 0; ++ } ++ kdba_getregcontents(XCS, regs, &cs); ++ if ((cs & 0xffff) != __KERNEL_CS) { ++ kdb_printf("Stack is not in kernel space, backtrace not available\n"); ++ return 0; ++ } ++ rip = krp->arch.ARCH_RIP; ++ rsp = krp->arch.ARCH_RSP; ++ kdba_get_stack_info(rsp, kdb_process_cpu(p), &ar, p); ++ } else { ++ /* Not on cpu, assume blocked. Blocked tasks do not ++ * have pt_regs. p->thread contains some data, alas ++ * what it contains differs between i386 and x86_64. ++ */ ++ rip = kdba_bt_stack_rip(p); ++ rsp = p->thread.sp; ++ suppress = 0; ++ kdba_get_stack_info(rsp, -1, &ar, p); ++ } ++ } ++ if (!ar.stack.physical_start) { ++ kdb_printf(RSP "=0x%lx is not in a valid kernel stack, backtrace not available\n", ++ rsp); ++ return 0; ++ } ++ memset(&bb_actual, 0, sizeof(bb_actual)); ++ bb_actual_set_value(BBRG_RSP, rsp); ++ bb_actual_set_valid(BBRG_RSP, 1); ++ ++ kdb_printf(RSP "%*s" RIP "%*sFunction (args)\n", ++ 2*KDB_WORD_SIZE, " ", ++ 2*KDB_WORD_SIZE, " "); ++ if (ar.stack.next && !suppress) ++ kdb_printf(" ======================= <%s>\n", ++ ar.stack.id); ++ ++ bb_cleanup(); ++ /* Run through all the stacks */ ++ while (ar.stack.physical_start) { ++ if (rip_at_rsp) { ++ rip = *(kdb_machreg_t *)rsp; ++ /* I wish that gcc was fixed to include a nop ++ * instruction after ATTRIB_NORET functions. The lack ++ * of a nop means that the return address points to the ++ * start of next function, so fudge it to point to one ++ * byte previous. ++ * ++ * No, we cannot just decrement all rip values. ++ * Sometimes an rip legally points to the start of a ++ * function, e.g. interrupted code or hand crafted ++ * assembler. ++ */ ++ if (prev_noret) { ++ kdbnearsym(rip, &symtab); ++ if (rip == symtab.sym_start) { ++ --rip; ++ if (KDB_DEBUG(ARA)) ++ kdb_printf("\tprev_noret, " RIP ++ "=0x%lx\n", rip); ++ } ++ } ++ } ++ kdbnearsym(rip, &symtab); ++ if (old_style) { ++ if (__kernel_text_address(rip) && !suppress) { ++ bt_print_one(rip, rsp, &ar, &symtab, 0); ++ ++count; ++ } ++ if (rsp == (unsigned long)regs) { ++ if (ar.stack.next && suppress) ++ kdb_printf(" ======================= <%s>\n", ++ ar.stack.id); ++ ++count; ++ suppress = 0; ++ } ++ rsp += sizeof(rip); ++ rip_at_rsp = 1; ++ if (rsp >= ar.stack.logical_end) { ++ if (!ar.stack.next) ++ break; ++ kdba_bt_new_stack(&ar, &rsp, &count, &suppress); ++ rip_at_rsp = 0; ++ continue; ++ } ++ } else { ++ /* Start each analysis with no dynamic data from the ++ * previous kdb_bb() run. ++ */ ++ bb_cleanup(); ++ kdb_bb(rip); ++ if (bb_giveup) ++ break; ++ prev_interrupt_handler = interrupt_handler; ++ interrupt_handler = bb_interrupt_handler(rip); ++ prev_rsp = rsp; ++ if (rip_at_rsp) { ++ if (prev_interrupt_handler) { ++ cs = *((kdb_machreg_t *)rsp + 1) & 0xffff; ++ hardware_pushed = ++ bb_hardware_pushed_arch(rsp, &ar); ++ } else { ++ cs = __KERNEL_CS; ++ hardware_pushed = 0; ++ } ++ rsp += sizeof(rip) + hardware_pushed; ++ if (KDB_DEBUG(ARA)) ++ kdb_printf("%s: " RSP " " ++ kdb_machreg_fmt0 ++ " -> " kdb_machreg_fmt0 ++ " hardware_pushed %d" ++ " prev_interrupt_handler %d" ++ " cs 0x%lx\n", ++ __FUNCTION__, ++ prev_rsp, ++ rsp, ++ hardware_pushed, ++ prev_interrupt_handler, ++ cs); ++ if (rsp >= ar.stack.logical_end && ++ ar.stack.next) { ++ kdba_bt_new_stack(&ar, &rsp, &count, ++ &suppress); ++ rip_at_rsp = 0; ++ continue; ++ } ++ bb_actual_set_value(BBRG_RSP, rsp); ++ } else { ++ cs = __KERNEL_CS; ++ } ++ rip_at_rsp = 1; ++ bb_actual_rollback(&ar); ++ if (bb_giveup) ++ break; ++ if (bb_actual_value(BBRG_RSP) < rsp) { ++ kdb_printf("%s: " RSP " is going backwards, " ++ kdb_machreg_fmt0 " -> " ++ kdb_machreg_fmt0 "\n", ++ __FUNCTION__, ++ rsp, ++ bb_actual_value(BBRG_RSP)); ++ bb_giveup = 1; ++ break; ++ } ++ bb_arguments(&ar); ++ if (!suppress) { ++ bt_print_one(rip, prev_rsp, &ar, &symtab, argcount); ++ ++count; ++ } ++ /* Functions that terminate the backtrace */ ++ if (strcmp(bb_func_name, "cpu_idle") == 0 || ++ strcmp(bb_func_name, "child_rip") == 0) ++ break; ++ if (rsp >= ar.stack.logical_end && ++ !ar.stack.next) ++ break; ++ if (rsp <= (unsigned long)regs && ++ bb_actual_value(BBRG_RSP) > (unsigned long)regs) { ++ if (ar.stack.next && suppress) ++ kdb_printf(" ======================= <%s>\n", ++ ar.stack.id); ++ ++count; ++ suppress = 0; ++ } ++ if (cs != __KERNEL_CS) { ++ kdb_printf("Reached user space\n"); ++ break; ++ } ++ rsp = bb_actual_value(BBRG_RSP); ++ } ++ prev_noret = bb_noret(bb_func_name); ++ if (count > 200) ++ break; ++ } ++ if (bb_giveup) ++ return 1; ++ bb_cleanup(); ++ kdbnearsym_cleanup(); ++ ++ if (count > 200) { ++ kdb_printf("bt truncated, count limit reached\n"); ++ return 1; ++ } else if (suppress) { ++ kdb_printf ++ ("bt did not find pt_regs - no trace produced. Suggest 'set BTSP 1'\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * kdba_bt_address ++ * ++ * Do a backtrace starting at a specified stack address. Use this if the ++ * heuristics get the stack decode wrong. ++ * ++ * Inputs: ++ * addr Address provided to 'bt' command. ++ * argcount ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * mds %rsp comes in handy when examining the stack to do a manual ++ * traceback. ++ */ ++ ++int kdba_bt_address(kdb_machreg_t addr, int argcount) ++{ ++ int ret; ++ kdba_id_init(&kdb_di); /* kdb_bb needs this done once */ ++ ret = kdba_bt_stack(addr, argcount, NULL, 0); ++ if (ret == 1) ++ ret = kdba_bt_stack(addr, argcount, NULL, 1); ++ return ret; ++} ++ ++/* ++ * kdba_bt_process ++ * ++ * Do a backtrace for a specified process. ++ * ++ * Inputs: ++ * p Struct task pointer extracted by 'bt' command. ++ * argcount ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ */ ++ ++int kdba_bt_process(const struct task_struct *p, int argcount) ++{ ++ int ret; ++ kdba_id_init(&kdb_di); /* kdb_bb needs this done once */ ++ ret = kdba_bt_stack(0, argcount, p, 0); ++ if (ret == 1) ++ ret = kdba_bt_stack(0, argcount, p, 1); ++ return ret; ++} ++ ++static int __init kdba_bt_x86_init(void) ++{ ++ int i, c, cp = -1; ++ struct bb_name_state *r; ++ ++ kdb_register_repeat("bb1", kdb_bb1, "", "Analyse one basic block", 0, KDB_REPEAT_NONE); ++ kdb_register_repeat("bb_all", kdb_bb_all, "", "Backtrace check on all built in functions", 0, KDB_REPEAT_NONE); ++ ++ /* Split the opcode usage table by the first letter of each set of ++ * opcodes, for faster mapping of opcode to its operand usage. ++ */ ++ for (i = 0; i < ARRAY_SIZE(bb_opcode_usage_all); ++i) { ++ c = bb_opcode_usage_all[i].opcode[0] - 'a'; ++ if (c != cp) { ++ cp = c; ++ bb_opcode_usage[c].opcode = bb_opcode_usage_all + i; ++ } ++ ++bb_opcode_usage[c].size; ++ } ++ ++ bb_common_interrupt = kallsyms_lookup_name("common_interrupt"); ++ bb_error_entry = kallsyms_lookup_name("error_entry"); ++ bb_ret_from_intr = kallsyms_lookup_name("ret_from_intr"); ++ bb_thread_return = kallsyms_lookup_name("thread_return"); ++ bb_sync_regs = kallsyms_lookup_name("sync_regs"); ++ bb_save_v86_state = kallsyms_lookup_name("save_v86_state"); ++ bb__sched_text_start = kallsyms_lookup_name("__sched_text_start"); ++ bb__sched_text_end = kallsyms_lookup_name("__sched_text_end"); ++ bb_save_args = kallsyms_lookup_name("save_args"); ++ bb_save_rest = kallsyms_lookup_name("save_rest"); ++ bb_save_paranoid = kallsyms_lookup_name("save_paranoid"); ++ for (i = 0, r = bb_special_cases; ++ i < ARRAY_SIZE(bb_special_cases); ++ ++i, ++r) { ++ r->address = kallsyms_lookup_name(r->name); ++ } ++ ++#ifdef CONFIG_4KSTACKS ++ kdba_hardirq_ctx = (struct thread_info **)kallsyms_lookup_name("hardirq_ctx"); ++ kdba_softirq_ctx = (struct thread_info **)kallsyms_lookup_name("softirq_ctx"); ++#endif /* CONFIG_4KSTACKS */ ++ ++ return 0; ++} ++ ++static void __exit kdba_bt_x86_exit(void) ++{ ++ kdb_unregister("bb1"); ++ kdb_unregister("bb_all"); ++} ++ ++module_init(kdba_bt_x86_init) ++module_exit(kdba_bt_x86_exit) +--- /dev/null ++++ b/arch/x86/kdb/kdba_id.c +@@ -0,0 +1,261 @@ ++/* ++ * Kernel Debugger Architecture Dependent Instruction Disassembly ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * kdba_dis_getsym ++ * ++ * Get a symbol for the disassembler. ++ * ++ * Parameters: ++ * addr Address for which to get symbol ++ * dip Pointer to disassemble_info ++ * Returns: ++ * 0 ++ * Locking: ++ * Remarks: ++ * Not used for kdb. ++ */ ++ ++/* ARGSUSED */ ++static int ++kdba_dis_getsym(bfd_vma addr, disassemble_info *dip) ++{ ++ ++ return 0; ++} ++ ++/* ++ * kdba_printaddress ++ * ++ * Print (symbolically) an address. ++ * ++ * Parameters: ++ * addr Address for which to get symbol ++ * dip Pointer to disassemble_info ++ * flag True if a ":" sequence should follow the address ++ * Returns: ++ * 0 ++ * Locking: ++ * Remarks: ++ * ++ */ ++ ++/* ARGSUSED */ ++static void ++kdba_printaddress(kdb_machreg_t addr, disassemble_info *dip, int flag) ++{ ++ kdb_symtab_t symtab; ++ int spaces = 5; ++ unsigned int offset; ++ ++ /* ++ * Print a symbol name or address as necessary. ++ */ ++ kdbnearsym(addr, &symtab); ++ if (symtab.sym_name) { ++ /* Do not use kdb_symbol_print here, it always does ++ * kdb_printf but we want dip->fprintf_func. ++ */ ++ dip->fprintf_func(dip->stream, ++ "0x%0*lx %s", ++ (int)(2*sizeof(addr)), addr, symtab.sym_name); ++ if ((offset = addr - symtab.sym_start) == 0) { ++ spaces += 4; ++ } ++ else { ++ unsigned int o = offset; ++ while (o >>= 4) ++ --spaces; ++ dip->fprintf_func(dip->stream, "+0x%x", offset); ++ } ++ ++ } else { ++ dip->fprintf_func(dip->stream, "0x%lx", addr); ++ } ++ ++ if (flag) { ++ if (spaces < 1) { ++ spaces = 1; ++ } ++ dip->fprintf_func(dip->stream, ":%*s", spaces, " "); ++ } ++} ++ ++/* ++ * kdba_dis_printaddr ++ * ++ * Print (symbolically) an address. Called by GNU disassembly ++ * code via disassemble_info structure. ++ * ++ * Parameters: ++ * addr Address for which to get symbol ++ * dip Pointer to disassemble_info ++ * Returns: ++ * 0 ++ * Locking: ++ * Remarks: ++ * This function will never append ":" to the printed ++ * symbolic address. ++ */ ++ ++static void ++kdba_dis_printaddr(bfd_vma addr, disassemble_info *dip) ++{ ++ kdba_printaddress(addr, dip, 0); ++} ++ ++/* ++ * kdba_dis_getmem ++ * ++ * Fetch 'length' bytes from 'addr' into 'buf'. ++ * ++ * Parameters: ++ * addr Address for which to get symbol ++ * buf Address of buffer to fill with bytes from 'addr' ++ * length Number of bytes to fetch ++ * dip Pointer to disassemble_info ++ * Returns: ++ * 0 if data is available, otherwise error. ++ * Locking: ++ * Remarks: ++ * ++ */ ++ ++/* ARGSUSED */ ++static int ++kdba_dis_getmem(bfd_vma addr, bfd_byte *buf, unsigned int length, disassemble_info *dip) ++{ ++ return kdb_getarea_size(buf, addr, length); ++} ++ ++/* ++ * kdba_id_parsemode ++ * ++ * Parse IDMODE environment variable string and ++ * set appropriate value into "disassemble_info" structure. ++ * ++ * Parameters: ++ * mode Mode string ++ * dip Disassemble_info structure pointer ++ * Returns: ++ * Locking: ++ * Remarks: ++ * We handle the values 'x86' and '8086' to enable either ++ * 32-bit instruction set or 16-bit legacy instruction set. ++ */ ++ ++int ++kdba_id_parsemode(const char *mode, disassemble_info *dip) ++{ ++ if (mode) { ++ if (strcmp(mode, "x86_64") == 0) { ++ dip->mach = bfd_mach_x86_64; ++ } else if (strcmp(mode, "x86") == 0) { ++ dip->mach = bfd_mach_i386_i386; ++ } else if (strcmp(mode, "8086") == 0) { ++ dip->mach = bfd_mach_i386_i8086; ++ } else { ++ return KDB_BADMODE; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * kdba_check_pc ++ * ++ * Check that the pc is satisfactory. ++ * ++ * Parameters: ++ * pc Program Counter Value. ++ * Returns: ++ * None ++ * Locking: ++ * None. ++ * Remarks: ++ * Can change pc. ++ */ ++ ++void ++kdba_check_pc(kdb_machreg_t *pc) ++{ ++ /* No action */ ++} ++ ++/* ++ * kdba_id_printinsn ++ * ++ * Format and print a single instruction at 'pc'. Return the ++ * length of the instruction. ++ * ++ * Parameters: ++ * pc Program Counter Value. ++ * dip Disassemble_info structure pointer ++ * Returns: ++ * Length of instruction, -1 for error. ++ * Locking: ++ * None. ++ * Remarks: ++ * Depends on 'IDMODE' environment variable. ++ */ ++ ++int ++kdba_id_printinsn(kdb_machreg_t pc, disassemble_info *dip) ++{ ++ kdba_printaddress(pc, dip, 1); ++ return print_insn_i386_att(pc, dip); ++} ++ ++/* ++ * kdba_id_init ++ * ++ * Initialize the architecture dependent elements of ++ * the disassembly information structure ++ * for the GNU disassembler. ++ * ++ * Parameters: ++ * None. ++ * Outputs: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++void ++kdba_id_init(disassemble_info *dip) ++{ ++ dip->read_memory_func = kdba_dis_getmem; ++ dip->print_address_func = kdba_dis_printaddr; ++ dip->symbol_at_address_func = kdba_dis_getsym; ++ ++ dip->flavour = bfd_target_elf_flavour; ++ dip->arch = bfd_arch_i386; ++#ifdef CONFIG_X86_64 ++ dip->mach = bfd_mach_x86_64; ++#endif ++#ifdef CONFIG_X86_32 ++ dip->mach = bfd_mach_i386_i386; ++#endif ++ dip->endian = BFD_ENDIAN_LITTLE; ++ ++ dip->display_endian = BFD_ENDIAN_LITTLE; ++} +--- /dev/null ++++ b/arch/x86/kdb/kdba_io.c +@@ -0,0 +1,666 @@ ++/* ++ * Kernel Debugger Architecture Dependent Console I/O handler ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "pc_keyb.h" ++ ++#ifdef CONFIG_VT_CONSOLE ++#define KDB_BLINK_LED 1 ++#else ++#undef KDB_BLINK_LED ++#endif ++ ++#ifdef CONFIG_KDB_USB ++ ++struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS]; ++EXPORT_SYMBOL(kdb_usb_kbds); ++ ++extern int kdb_no_usb; ++ ++static unsigned char kdb_usb_keycode[256] = { ++ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, ++ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, ++ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, ++ 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, ++ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, ++ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, ++ 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95, ++ 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, ++ 115,114, 0, 0, 0,124, 0,181,182,183,184,185,186,187,188,189, ++ 190,191,192,193,194,195,196,197,198, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, ++ 150,158,159,128,136,177,178,176,142,152,173,140 ++}; ++ ++/* ++ * kdb_usb_keyboard_attach() ++ * Attach a USB keyboard to kdb. ++ */ ++int ++kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, ++ void *poll_func, void *compl_func, ++ kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach, ++ kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach, ++ unsigned int bufsize, ++ struct urb *hid_urb) ++{ ++ int i; ++ int rc = -1; ++ ++ if (kdb_no_usb) ++ return 0; ++ ++ /* ++ * Search through the array of KDB USB keyboards (kdb_usb_kbds) ++ * looking for a free index. If found, assign the keyboard to ++ * the array index. ++ */ ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ if (kdb_usb_kbds[i].urb) /* index is already assigned */ ++ continue; ++ ++ /* found a free array index */ ++ kdb_usb_kbds[i].urb = urb; ++ kdb_usb_kbds[i].buffer = buffer; ++ kdb_usb_kbds[i].poll_func = poll_func; ++ ++ kdb_usb_kbds[i].kdb_hc_urb_complete = compl_func; ++ kdb_usb_kbds[i].kdb_hc_keyboard_attach = kdb_hc_keyboard_attach; ++ kdb_usb_kbds[i].kdb_hc_keyboard_detach = kdb_hc_keyboard_detach; ++ ++ /* USB Host Controller specific Keyboadr attach callback. ++ * Currently only UHCI has this callback. ++ */ ++ if (kdb_usb_kbds[i].kdb_hc_keyboard_attach) ++ kdb_usb_kbds[i].kdb_hc_keyboard_attach(i, bufsize); ++ ++ rc = 0; /* success */ ++ ++ break; ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL (kdb_usb_keyboard_attach); ++ ++/* ++ * kdb_usb_keyboard_detach() ++ * Detach a USB keyboard from kdb. ++ */ ++int ++kdb_usb_keyboard_detach(struct urb *urb) ++{ ++ int i; ++ int rc = -1; ++ ++ if (kdb_no_usb) ++ return 0; ++ ++ /* ++ * Search through the array of KDB USB keyboards (kdb_usb_kbds) ++ * looking for the index with the matching URB. If found, ++ * clear the array index. ++ */ ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ if ((kdb_usb_kbds[i].urb != urb) && ++ (kdb_usb_kbds[i].hid_urb != urb)) ++ continue; ++ ++ /* found it, clear the index */ ++ ++ /* USB Host Controller specific Keyboard detach callback. ++ * Currently only UHCI has this callback. ++ */ ++ if (kdb_usb_kbds[i].kdb_hc_keyboard_detach) ++ kdb_usb_kbds[i].kdb_hc_keyboard_detach(urb, i); ++ ++ kdb_usb_kbds[i].urb = NULL; ++ kdb_usb_kbds[i].buffer = NULL; ++ kdb_usb_kbds[i].poll_func = NULL; ++ kdb_usb_kbds[i].caps_lock = 0; ++ kdb_usb_kbds[i].hid_urb = NULL; ++ ++ rc = 0; /* success */ ++ ++ break; ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL (kdb_usb_keyboard_detach); ++ ++/* ++ * get_usb_char ++ * This function drives the USB attached keyboards. ++ * Fetch the USB scancode and decode it. ++ */ ++static int ++get_usb_char(void) ++{ ++ int i; ++ unsigned char keycode, spec; ++ extern u_short plain_map[], shift_map[], ctrl_map[]; ++ int ret = 1; ++ int ret_key = -1, j, max; ++ ++ if (kdb_no_usb) ++ return -1; ++ ++ /* ++ * Loop through all the USB keyboard(s) and return ++ * the first character obtained from them. ++ */ ++ ++ for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { ++ /* skip uninitialized keyboard array entries */ ++ if (!kdb_usb_kbds[i].urb || !kdb_usb_kbds[i].buffer || ++ !kdb_usb_kbds[i].poll_func) ++ continue; ++ ++ /* Transfer char */ ++ ret = (*kdb_usb_kbds[i].poll_func)(kdb_usb_kbds[i].urb); ++ if (ret == -EBUSY && kdb_usb_kbds[i].poll_ret != -EBUSY) ++ kdb_printf("NOTICE: USB HD driver BUSY. " ++ "USB keyboard has been disabled.\n"); ++ ++ kdb_usb_kbds[i].poll_ret = ret; ++ ++ if (ret < 0) /* error or no characters, try the next kbd */ ++ continue; ++ ++ /* If 2 keys was pressed simultaneously, ++ * both keycodes will be in buffer. ++ * Last pressed key will be last non ++ * zero byte. ++ */ ++ for (j=0; j<4; j++){ ++ if (!kdb_usb_kbds[i].buffer[2+j]) ++ break; ++ } ++ /* Last pressed key */ ++ max = j + 1; ++ ++ spec = kdb_usb_kbds[i].buffer[0]; ++ keycode = kdb_usb_kbds[i].buffer[2]; ++ kdb_usb_kbds[i].buffer[0] = (char)0; ++ kdb_usb_kbds[i].buffer[2] = (char)0; ++ ++ ret_key = -1; ++ ++ /* A normal key is pressed, decode it */ ++ if(keycode) ++ keycode = kdb_usb_keycode[keycode]; ++ ++ /* 2 Keys pressed at one time ? */ ++ if (spec && keycode) { ++ switch(spec) ++ { ++ case 0x2: ++ case 0x20: /* Shift */ ++ ret_key = shift_map[keycode]; ++ break; ++ case 0x1: ++ case 0x10: /* Ctrl */ ++ ret_key = ctrl_map[keycode]; ++ break; ++ case 0x4: ++ case 0x40: /* Alt */ ++ break; ++ } ++ } else if (keycode) { /* If only one key pressed */ ++ switch(keycode) ++ { ++ case 0x1C: /* Enter */ ++ ret_key = 13; ++ break; ++ ++ case 0x3A: /* Capslock */ ++ kdb_usb_kbds[i].caps_lock = !(kdb_usb_kbds[i].caps_lock); ++ break; ++ case 0x0E: /* Backspace */ ++ ret_key = 8; ++ break; ++ case 0x0F: /* TAB */ ++ ret_key = 9; ++ break; ++ case 0x77: /* Pause */ ++ break ; ++ default: ++ if(!kdb_usb_kbds[i].caps_lock) { ++ ret_key = plain_map[keycode]; ++ } ++ else { ++ ret_key = shift_map[keycode]; ++ } ++ } ++ } ++ ++ if (ret_key != 1) { ++ /* Key was pressed, return keycode */ ++ ++ /* Clear buffer before urb resending */ ++ if (kdb_usb_kbds[i].buffer) ++ for(j=0; j<8; j++) ++ kdb_usb_kbds[i].buffer[j] = (char)0; ++ ++ /* USB Host Controller specific Urb complete callback. ++ * Currently only UHCI has this callback. ++ */ ++ if (kdb_usb_kbds[i].kdb_hc_urb_complete) ++ (*kdb_usb_kbds[i].kdb_hc_urb_complete)((struct urb *)kdb_usb_kbds[i].urb); ++ ++ return ret_key; ++ } ++ } ++ ++ ++ ++ /* no chars were returned from any of the USB keyboards */ ++ ++ return -1; ++} ++#endif /* CONFIG_KDB_USB */ ++ ++/* ++ * This module contains code to read characters from the keyboard or a serial ++ * port. ++ * ++ * It is used by the kernel debugger, and is polled, not interrupt driven. ++ * ++ */ ++ ++#ifdef KDB_BLINK_LED ++/* ++ * send: Send a byte to the keyboard controller. Used primarily to ++ * alter LED settings. ++ */ ++ ++static void ++kdb_kbdsend(unsigned char byte) ++{ ++ int timeout; ++ for (timeout = 200 * 1000; timeout && (inb(KBD_STATUS_REG) & KBD_STAT_IBF); timeout--); ++ outb(byte, KBD_DATA_REG); ++ udelay(40); ++ for (timeout = 200 * 1000; timeout && (~inb(KBD_STATUS_REG) & KBD_STAT_OBF); timeout--); ++ inb(KBD_DATA_REG); ++ udelay(40); ++} ++ ++static void ++kdb_toggleled(int led) ++{ ++ static int leds; ++ ++ leds ^= led; ++ ++ kdb_kbdsend(KBD_CMD_SET_LEDS); ++ kdb_kbdsend((unsigned char)leds); ++} ++#endif /* KDB_BLINK_LED */ ++ ++#if defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_CORE_CONSOLE) ++#define CONFIG_SERIAL_CONSOLE ++#endif ++ ++#if defined(CONFIG_SERIAL_CONSOLE) ++ ++struct kdb_serial kdb_serial; ++ ++static unsigned int ++serial_inp(struct kdb_serial *kdb_serial, unsigned long offset) ++{ ++ offset <<= kdb_serial->ioreg_shift; ++ ++ switch (kdb_serial->io_type) { ++ case SERIAL_IO_MEM: ++ return readb((void __iomem *)(kdb_serial->iobase + offset)); ++ break; ++ default: ++ return inb(kdb_serial->iobase + offset); ++ break; ++ } ++} ++ ++/* Check if there is a byte ready at the serial port */ ++static int get_serial_char(void) ++{ ++ unsigned char ch; ++ ++ if (kdb_serial.iobase == 0) ++ return -1; ++ ++ if (serial_inp(&kdb_serial, UART_LSR) & UART_LSR_DR) { ++ ch = serial_inp(&kdb_serial, UART_RX); ++ if (ch == 0x7f) ++ ch = 8; ++ return ch; ++ } ++ return -1; ++} ++#endif /* CONFIG_SERIAL_CONSOLE */ ++ ++#ifdef CONFIG_VT_CONSOLE ++ ++static int kbd_exists; ++ ++/* ++ * Check if the keyboard controller has a keypress for us. ++ * Some parts (Enter Release, LED change) are still blocking polled here, ++ * but hopefully they are all short. ++ */ ++static int get_kbd_char(void) ++{ ++ int scancode, scanstatus; ++ static int shift_lock; /* CAPS LOCK state (0-off, 1-on) */ ++ static int shift_key; /* Shift next keypress */ ++ static int ctrl_key; ++ u_short keychar; ++ extern u_short plain_map[], shift_map[], ctrl_map[]; ++ ++ if (KDB_FLAG(NO_I8042) || KDB_FLAG(NO_VT_CONSOLE) || ++ (inb(KBD_STATUS_REG) == 0xff && inb(KBD_DATA_REG) == 0xff)) { ++ kbd_exists = 0; ++ return -1; ++ } ++ kbd_exists = 1; ++ ++ if ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) ++ return -1; ++ ++ /* ++ * Fetch the scancode ++ */ ++ scancode = inb(KBD_DATA_REG); ++ scanstatus = inb(KBD_STATUS_REG); ++ ++ /* ++ * Ignore mouse events. ++ */ ++ if (scanstatus & KBD_STAT_MOUSE_OBF) ++ return -1; ++ ++ /* ++ * Ignore release, trigger on make ++ * (except for shift keys, where we want to ++ * keep the shift state so long as the key is ++ * held down). ++ */ ++ ++ if (((scancode&0x7f) == 0x2a) || ((scancode&0x7f) == 0x36)) { ++ /* ++ * Next key may use shift table ++ */ ++ if ((scancode & 0x80) == 0) { ++ shift_key=1; ++ } else { ++ shift_key=0; ++ } ++ return -1; ++ } ++ ++ if ((scancode&0x7f) == 0x1d) { ++ /* ++ * Left ctrl key ++ */ ++ if ((scancode & 0x80) == 0) { ++ ctrl_key = 1; ++ } else { ++ ctrl_key = 0; ++ } ++ return -1; ++ } ++ ++ if ((scancode & 0x80) != 0) ++ return -1; ++ ++ scancode &= 0x7f; ++ ++ /* ++ * Translate scancode ++ */ ++ ++ if (scancode == 0x3a) { ++ /* ++ * Toggle caps lock ++ */ ++ shift_lock ^= 1; ++ ++#ifdef KDB_BLINK_LED ++ kdb_toggleled(0x4); ++#endif ++ return -1; ++ } ++ ++ if (scancode == 0x0e) { ++ /* ++ * Backspace ++ */ ++ return 8; ++ } ++ ++ /* Special Key */ ++ switch (scancode) { ++ case 0xF: /* Tab */ ++ return 9; ++ case 0x53: /* Del */ ++ return 4; ++ case 0x47: /* Home */ ++ return 1; ++ case 0x4F: /* End */ ++ return 5; ++ case 0x4B: /* Left */ ++ return 2; ++ case 0x48: /* Up */ ++ return 16; ++ case 0x50: /* Down */ ++ return 14; ++ case 0x4D: /* Right */ ++ return 6; ++ } ++ ++ if (scancode == 0xe0) { ++ return -1; ++ } ++ ++ /* ++ * For Japanese 86/106 keyboards ++ * See comment in drivers/char/pc_keyb.c. ++ * - Masahiro Adegawa ++ */ ++ if (scancode == 0x73) { ++ scancode = 0x59; ++ } else if (scancode == 0x7d) { ++ scancode = 0x7c; ++ } ++ ++ if (!shift_lock && !shift_key && !ctrl_key) { ++ keychar = plain_map[scancode]; ++ } else if (shift_lock || shift_key) { ++ keychar = shift_map[scancode]; ++ } else if (ctrl_key) { ++ keychar = ctrl_map[scancode]; ++ } else { ++ keychar = 0x0020; ++ kdb_printf("Unknown state/scancode (%d)\n", scancode); ++ } ++ keychar &= 0x0fff; ++ if (keychar == '\t') ++ keychar = ' '; ++ switch (KTYP(keychar)) { ++ case KT_LETTER: ++ case KT_LATIN: ++ if (isprint(keychar)) ++ break; /* printable characters */ ++ /* drop through */ ++ case KT_SPEC: ++ if (keychar == K_ENTER) ++ break; ++ /* drop through */ ++ default: ++ return(-1); /* ignore unprintables */ ++ } ++ ++ if ((scancode & 0x7f) == 0x1c) { ++ /* ++ * enter key. All done. Absorb the release scancode. ++ */ ++ while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) ++ ; ++ ++ /* ++ * Fetch the scancode ++ */ ++ scancode = inb(KBD_DATA_REG); ++ scanstatus = inb(KBD_STATUS_REG); ++ ++ while (scanstatus & KBD_STAT_MOUSE_OBF) { ++ scancode = inb(KBD_DATA_REG); ++ scanstatus = inb(KBD_STATUS_REG); ++ } ++ ++ if (scancode != 0x9c) { ++ /* ++ * Wasn't an enter-release, why not? ++ */ ++ kdb_printf("kdb: expected enter got 0x%x status 0x%x\n", ++ scancode, scanstatus); ++ } ++ ++ kdb_printf("\n"); ++ return 13; ++ } ++ ++ return keychar & 0xff; ++} ++#endif /* CONFIG_VT_CONSOLE */ ++ ++#ifdef KDB_BLINK_LED ++ ++/* Leave numlock alone, setting it messes up laptop keyboards with the keypad ++ * mapped over normal keys. ++ */ ++static int kdba_blink_mask = 0x1 | 0x4; ++ ++#define BOGOMIPS (boot_cpu_data.loops_per_jiffy/(500000/HZ)) ++static int blink_led(void) ++{ ++ static long delay; ++ ++ if (kbd_exists == 0) ++ return -1; ++ ++ if (--delay < 0) { ++ if (BOGOMIPS == 0) /* early kdb */ ++ delay = 150000000/1000; /* arbitrary bogomips */ ++ else ++ delay = 150000000/BOGOMIPS; /* Roughly 1 second when polling */ ++ kdb_toggleled(kdba_blink_mask); ++ } ++ return -1; ++} ++#endif ++ ++get_char_func poll_funcs[] = { ++#if defined(CONFIG_VT_CONSOLE) ++ get_kbd_char, ++#endif ++#if defined(CONFIG_SERIAL_CONSOLE) ++ get_serial_char, ++#endif ++#ifdef KDB_BLINK_LED ++ blink_led, ++#endif ++#ifdef CONFIG_KDB_USB ++ get_usb_char, ++#endif ++ NULL ++}; ++ ++/* ++ * On some Compaq Deskpro's, there is a keyboard freeze many times after ++ * exiting from the kdb. As kdb's keyboard handler is not interrupt-driven and ++ * uses a polled interface, it makes more sense to disable motherboard keyboard ++ * controller's OBF interrupts during kdb's polling.In case, of interrupts ++ * remaining enabled during kdb's polling, it may cause un-necessary ++ * interrupts being signalled during keypresses, which are also sometimes seen ++ * as spurious interrupts after exiting from kdb. This hack to disable OBF ++ * interrupts before entry to kdb and re-enabling them at kdb exit point also ++ * solves the keyboard freeze issue. These functions are called from ++ * kdb_local(), hence these are arch. specific setup and cleanup functions ++ * executing only on the local processor - ashishk@sco.com ++ */ ++ ++void kdba_local_arch_setup(void) ++{ ++#ifdef CONFIG_VT_CONSOLE ++ int timeout; ++ unsigned char c; ++ ++ while (kbd_read_status() & KBD_STAT_IBF); ++ kbd_write_command(KBD_CCMD_READ_MODE); ++ mdelay(1); ++ while (kbd_read_status() & KBD_STAT_IBF); ++ for (timeout = 200 * 1000; timeout && ++ (!(kbd_read_status() & KBD_STAT_OBF)); timeout--); ++ c = kbd_read_input(); ++ c &= ~KBD_MODE_KBD_INT; ++ while (kbd_read_status() & KBD_STAT_IBF); ++ kbd_write_command(KBD_CCMD_WRITE_MODE); ++ mdelay(1); ++ while (kbd_read_status() & KBD_STAT_IBF); ++ kbd_write_output(c); ++ mdelay(1); ++ while (kbd_read_status() & KBD_STAT_IBF); ++ mdelay(1); ++#endif /* CONFIG_VT_CONSOLE */ ++} ++ ++void kdba_local_arch_cleanup(void) ++{ ++#ifdef CONFIG_VT_CONSOLE ++ int timeout; ++ unsigned char c; ++ ++ while (kbd_read_status() & KBD_STAT_IBF); ++ kbd_write_command(KBD_CCMD_READ_MODE); ++ mdelay(1); ++ while (kbd_read_status() & KBD_STAT_IBF); ++ for (timeout = 200 * 1000; timeout && ++ (!(kbd_read_status() & KBD_STAT_OBF)); timeout--); ++ c = kbd_read_input(); ++ c |= KBD_MODE_KBD_INT; ++ while (kbd_read_status() & KBD_STAT_IBF); ++ kbd_write_command(KBD_CCMD_WRITE_MODE); ++ mdelay(1); ++ while (kbd_read_status() & KBD_STAT_IBF); ++ kbd_write_output(c); ++ mdelay(1); ++ while (kbd_read_status() & KBD_STAT_IBF); ++ mdelay(1); ++#endif /* CONFIG_VT_CONSOLE */ ++} +--- /dev/null ++++ b/arch/x86/kdb/kdba_support.c +@@ -0,0 +1,1536 @@ ++/* ++ * Kernel Debugger Architecture Independent Support Functions ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (c) 1999-2008 Silicon Graphics, Inc. All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++static kdb_machreg_t ++kdba_getcr(int regnum) ++{ ++ kdb_machreg_t contents = 0; ++ switch(regnum) { ++ case 0: ++ __asm__ (_ASM_MOV " %%cr0,%0\n\t":"=r"(contents)); ++ break; ++ case 1: ++ break; ++ case 2: ++ __asm__ (_ASM_MOV " %%cr2,%0\n\t":"=r"(contents)); ++ break; ++ case 3: ++ __asm__ (_ASM_MOV " %%cr3,%0\n\t":"=r"(contents)); ++ break; ++ case 4: ++ __asm__ (_ASM_MOV " %%cr4,%0\n\t":"=r"(contents)); ++ break; ++ default: ++ break; ++ } ++ ++ return contents; ++} ++ ++void ++kdba_putdr(int regnum, kdb_machreg_t contents) ++{ ++ switch(regnum) { ++ case 0: ++ __asm__ (_ASM_MOV " %0,%%db0\n\t"::"r"(contents)); ++ break; ++ case 1: ++ __asm__ (_ASM_MOV " %0,%%db1\n\t"::"r"(contents)); ++ break; ++ case 2: ++ __asm__ (_ASM_MOV " %0,%%db2\n\t"::"r"(contents)); ++ break; ++ case 3: ++ __asm__ (_ASM_MOV " %0,%%db3\n\t"::"r"(contents)); ++ break; ++ case 4: ++ case 5: ++ break; ++ case 6: ++ __asm__ (_ASM_MOV " %0,%%db6\n\t"::"r"(contents)); ++ break; ++ case 7: ++ __asm__ (_ASM_MOV " %0,%%db7\n\t"::"r"(contents)); ++ break; ++ default: ++ break; ++ } ++} ++ ++kdb_machreg_t ++kdba_getdr(int regnum) ++{ ++ kdb_machreg_t contents = 0; ++ switch(regnum) { ++ case 0: ++ __asm__ (_ASM_MOV " %%db0,%0\n\t":"=r"(contents)); ++ break; ++ case 1: ++ __asm__ (_ASM_MOV " %%db1,%0\n\t":"=r"(contents)); ++ break; ++ case 2: ++ __asm__ (_ASM_MOV " %%db2,%0\n\t":"=r"(contents)); ++ break; ++ case 3: ++ __asm__ (_ASM_MOV " %%db3,%0\n\t":"=r"(contents)); ++ break; ++ case 4: ++ case 5: ++ break; ++ case 6: ++ __asm__ (_ASM_MOV " %%db6,%0\n\t":"=r"(contents)); ++ break; ++ case 7: ++ __asm__ (_ASM_MOV " %%db7,%0\n\t":"=r"(contents)); ++ break; ++ default: ++ break; ++ } ++ ++ return contents; ++} ++ ++kdb_machreg_t ++kdba_getdr6(void) ++{ ++ return kdba_getdr(6); ++} ++ ++kdb_machreg_t ++kdba_getdr7(void) ++{ ++ return kdba_getdr(7); ++} ++ ++void ++kdba_putdr6(kdb_machreg_t contents) ++{ ++ kdba_putdr(6, contents); ++} ++ ++static void ++kdba_putdr7(kdb_machreg_t contents) ++{ ++ kdba_putdr(7, contents); ++} ++ ++void ++kdba_installdbreg(kdb_bp_t *bp) ++{ ++ int cpu = smp_processor_id(); ++ ++ kdb_machreg_t dr7; ++ ++ dr7 = kdba_getdr7(); ++ ++ kdba_putdr(bp->bp_hard[cpu]->bph_reg, bp->bp_addr); ++ ++ dr7 |= DR7_GE; ++ if (cpu_has_de) ++ set_in_cr4(X86_CR4_DE); ++ ++ switch (bp->bp_hard[cpu]->bph_reg){ ++ case 0: ++ DR7_RW0SET(dr7,bp->bp_hard[cpu]->bph_mode); ++ DR7_LEN0SET(dr7,bp->bp_hard[cpu]->bph_length); ++ DR7_G0SET(dr7); ++ break; ++ case 1: ++ DR7_RW1SET(dr7,bp->bp_hard[cpu]->bph_mode); ++ DR7_LEN1SET(dr7,bp->bp_hard[cpu]->bph_length); ++ DR7_G1SET(dr7); ++ break; ++ case 2: ++ DR7_RW2SET(dr7,bp->bp_hard[cpu]->bph_mode); ++ DR7_LEN2SET(dr7,bp->bp_hard[cpu]->bph_length); ++ DR7_G2SET(dr7); ++ break; ++ case 3: ++ DR7_RW3SET(dr7,bp->bp_hard[cpu]->bph_mode); ++ DR7_LEN3SET(dr7,bp->bp_hard[cpu]->bph_length); ++ DR7_G3SET(dr7); ++ break; ++ default: ++ kdb_printf("kdb: Bad debug register!! %ld\n", ++ bp->bp_hard[cpu]->bph_reg); ++ break; ++ } ++ ++ kdba_putdr7(dr7); ++ return; ++} ++ ++void ++kdba_removedbreg(kdb_bp_t *bp) ++{ ++ int regnum; ++ kdb_machreg_t dr7; ++ int cpu = smp_processor_id(); ++ ++ if (!bp->bp_hard[cpu]) ++ return; ++ ++ regnum = bp->bp_hard[cpu]->bph_reg; ++ ++ dr7 = kdba_getdr7(); ++ ++ kdba_putdr(regnum, 0); ++ ++ switch (regnum) { ++ case 0: ++ DR7_G0CLR(dr7); ++ DR7_L0CLR(dr7); ++ break; ++ case 1: ++ DR7_G1CLR(dr7); ++ DR7_L1CLR(dr7); ++ break; ++ case 2: ++ DR7_G2CLR(dr7); ++ DR7_L2CLR(dr7); ++ break; ++ case 3: ++ DR7_G3CLR(dr7); ++ DR7_L3CLR(dr7); ++ break; ++ default: ++ kdb_printf("kdb: Bad debug register!! %d\n", regnum); ++ break; ++ } ++ ++ kdba_putdr7(dr7); ++} ++ ++struct kdbregs { ++ char *reg_name; ++ size_t reg_offset; ++}; ++ ++static struct kdbregs dbreglist[] = { ++ { "dr0", 0 }, ++ { "dr1", 1 }, ++ { "dr2", 2 }, ++ { "dr3", 3 }, ++ { "dr6", 6 }, ++ { "dr7", 7 }, ++}; ++ ++static const int ndbreglist = sizeof(dbreglist) / sizeof(struct kdbregs); ++ ++#ifdef CONFIG_X86_32 ++static struct kdbregs kdbreglist[] = { ++ { "ax", offsetof(struct pt_regs, ax) }, ++ { "bx", offsetof(struct pt_regs, bx) }, ++ { "cx", offsetof(struct pt_regs, cx) }, ++ { "dx", offsetof(struct pt_regs, dx) }, ++ ++ { "si", offsetof(struct pt_regs, si) }, ++ { "di", offsetof(struct pt_regs, di) }, ++ { "sp", offsetof(struct pt_regs, sp) }, ++ { "ip", offsetof(struct pt_regs, ip) }, ++ ++ { "bp", offsetof(struct pt_regs, bp) }, ++ { "ss", offsetof(struct pt_regs, ss) }, ++ { "cs", offsetof(struct pt_regs, cs) }, ++ { "flags", offsetof(struct pt_regs, flags) }, ++ ++ { "ds", offsetof(struct pt_regs, ds) }, ++ { "es", offsetof(struct pt_regs, es) }, ++ { "origax", offsetof(struct pt_regs, orig_ax) }, ++ ++}; ++ ++static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs); ++ ++ ++/* ++ * kdba_getregcontents ++ * ++ * Return the contents of the register specified by the ++ * input string argument. Return an error if the string ++ * does not match a machine register. ++ * ++ * The following pseudo register names are supported: ++ * ®s - Prints address of exception frame ++ * kesp - Prints kernel stack pointer at time of fault ++ * cesp - Prints current kernel stack pointer, inside kdb ++ * ceflags - Prints current flags, inside kdb ++ * % - Uses the value of the registers at the ++ * last time the user process entered kernel ++ * mode, instead of the registers at the time ++ * kdb was entered. ++ * ++ * Parameters: ++ * regname Pointer to string naming register ++ * regs Pointer to structure containing registers. ++ * Outputs: ++ * *contents Pointer to unsigned long to recieve register contents ++ * Returns: ++ * 0 Success ++ * KDB_BADREG Invalid register name ++ * Locking: ++ * None. ++ * Remarks: ++ * If kdb was entered via an interrupt from the kernel itself then ++ * ss and sp are *not* on the stack. ++ */ ++ ++int ++kdba_getregcontents(const char *regname, ++ struct pt_regs *regs, ++ kdb_machreg_t *contents) ++{ ++ int i; ++ ++ if (strcmp(regname, "cesp") == 0) { ++ asm volatile("movl %%esp,%0":"=m" (*contents)); ++ return 0; ++ } ++ ++ if (strcmp(regname, "ceflags") == 0) { ++ unsigned long flags; ++ local_save_flags(flags); ++ *contents = flags; ++ return 0; ++ } ++ ++ if (regname[0] == '%') { ++ /* User registers: %%e[a-c]x, etc */ ++ regname++; ++ regs = (struct pt_regs *) ++ (kdb_current_task->thread.sp0 - sizeof(struct pt_regs)); ++ } ++ ++ for (i=0; ics & 0xffff) == __KERNEL_CS) { ++ /* sp and ss are not on stack */ ++ *contents -= 2*4; ++ } ++ return 0; ++ } ++ ++ for (i=0; ics & 0xffff) == __KERNEL_CS) { ++ /* No cpl switch, sp and ss are not on stack */ ++ if (strcmp(kdbreglist[i].reg_name, "sp") == 0) { ++ *contents = (kdb_machreg_t)regs + ++ sizeof(struct pt_regs) - 2*4; ++ return(0); ++ } ++ if (strcmp(kdbreglist[i].reg_name, "xss") == 0) { ++ asm volatile( ++ "pushl %%ss\n" ++ "popl %0\n" ++ :"=m" (*contents)); ++ return(0); ++ } ++ } ++ *contents = *(unsigned long *)((unsigned long)regs + ++ kdbreglist[i].reg_offset); ++ return(0); ++ } ++ ++ return KDB_BADREG; ++} ++ ++/* ++ * kdba_setregcontents ++ * ++ * Set the contents of the register specified by the ++ * input string argument. Return an error if the string ++ * does not match a machine register. ++ * ++ * Supports modification of user-mode registers via ++ * % ++ * ++ * Parameters: ++ * regname Pointer to string naming register ++ * regs Pointer to structure containing registers. ++ * contents Unsigned long containing new register contents ++ * Outputs: ++ * Returns: ++ * 0 Success ++ * KDB_BADREG Invalid register name ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++int ++kdba_setregcontents(const char *regname, ++ struct pt_regs *regs, ++ unsigned long contents) ++{ ++ int i; ++ ++ if (regname[0] == '%') { ++ regname++; ++ regs = (struct pt_regs *) ++ (kdb_current_task->thread.sp0 - sizeof(struct pt_regs)); ++ } ++ ++ for (i=0; ibx); ++ kdb_print_nameval("cx", p->cx); ++ kdb_print_nameval("dx", p->dx); ++ kdb_print_nameval("si", p->si); ++ kdb_print_nameval("di", p->di); ++ kdb_print_nameval("bp", p->bp); ++ kdb_print_nameval("ax", p->ax); ++ kdb_printf(fmt, "ds", p->ds); ++ kdb_printf(fmt, "es", p->es); ++ kdb_print_nameval("orig_ax", p->orig_ax); ++ kdb_print_nameval("ip", p->ip); ++ kdb_printf(fmt, "cs", p->cs); ++ kdb_printf(fmt, "flags", p->flags); ++ kdb_printf(fmt, "sp", p->sp); ++ kdb_printf(fmt, "ss", p->ss); ++ return 0; ++} ++ ++#else /* CONFIG_X86_32 */ ++ ++static struct kdbregs kdbreglist[] = { ++ { "r15", offsetof(struct pt_regs, r15) }, ++ { "r14", offsetof(struct pt_regs, r14) }, ++ { "r13", offsetof(struct pt_regs, r13) }, ++ { "r12", offsetof(struct pt_regs, r12) }, ++ { "bp", offsetof(struct pt_regs, bp) }, ++ { "bx", offsetof(struct pt_regs, bx) }, ++ { "r11", offsetof(struct pt_regs, r11) }, ++ { "r10", offsetof(struct pt_regs, r10) }, ++ { "r9", offsetof(struct pt_regs, r9) }, ++ { "r8", offsetof(struct pt_regs, r8) }, ++ { "ax", offsetof(struct pt_regs, ax) }, ++ { "cx", offsetof(struct pt_regs, cx) }, ++ { "dx", offsetof(struct pt_regs, dx) }, ++ { "si", offsetof(struct pt_regs, si) }, ++ { "di", offsetof(struct pt_regs, di) }, ++ { "orig_ax", offsetof(struct pt_regs, orig_ax) }, ++ { "ip", offsetof(struct pt_regs, ip) }, ++ { "cs", offsetof(struct pt_regs, cs) }, ++ { "flags", offsetof(struct pt_regs, flags) }, ++ { "sp", offsetof(struct pt_regs, sp) }, ++ { "ss", offsetof(struct pt_regs, ss) }, ++}; ++ ++static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs); ++ ++ ++/* ++ * kdba_getregcontents ++ * ++ * Return the contents of the register specified by the ++ * input string argument. Return an error if the string ++ * does not match a machine register. ++ * ++ * The following pseudo register names are supported: ++ * ®s - Prints address of exception frame ++ * krsp - Prints kernel stack pointer at time of fault ++ * crsp - Prints current kernel stack pointer, inside kdb ++ * ceflags - Prints current flags, inside kdb ++ * % - Uses the value of the registers at the ++ * last time the user process entered kernel ++ * mode, instead of the registers at the time ++ * kdb was entered. ++ * ++ * Parameters: ++ * regname Pointer to string naming register ++ * regs Pointer to structure containing registers. ++ * Outputs: ++ * *contents Pointer to unsigned long to recieve register contents ++ * Returns: ++ * 0 Success ++ * KDB_BADREG Invalid register name ++ * Locking: ++ * None. ++ * Remarks: ++ * If kdb was entered via an interrupt from the kernel itself then ++ * ss and sp are *not* on the stack. ++ */ ++int ++kdba_getregcontents(const char *regname, ++ struct pt_regs *regs, ++ kdb_machreg_t *contents) ++{ ++ int i; ++ ++ if (strcmp(regname, "®s") == 0) { ++ *contents = (unsigned long)regs; ++ return 0; ++ } ++ ++ if (strcmp(regname, "krsp") == 0) { ++ *contents = (unsigned long)regs + sizeof(struct pt_regs); ++ if ((regs->cs & 0xffff) == __KERNEL_CS) { ++ /* sp and ss are not on stack */ ++ *contents -= 2*4; ++ } ++ return 0; ++ } ++ ++ if (strcmp(regname, "crsp") == 0) { ++ asm volatile("movq %%rsp,%0":"=m" (*contents)); ++ return 0; ++ } ++ ++ if (strcmp(regname, "ceflags") == 0) { ++ unsigned long flags; ++ local_save_flags(flags); ++ *contents = flags; ++ return 0; ++ } ++ ++ if (regname[0] == '%') { ++ /* User registers: %%r[a-c]x, etc */ ++ regname++; ++ regs = (struct pt_regs *) ++ (current->thread.sp0 - sizeof(struct pt_regs)); ++ } ++ ++ for (i=0; ics & 0xffff) == __KERNEL_CS) { ++ /* No cpl switch, sp is not on stack */ ++ if (strcmp(kdbreglist[i].reg_name, "sp") == 0) { ++ *contents = (kdb_machreg_t)regs + ++ sizeof(struct pt_regs) - 2*8; ++ return(0); ++ } ++#if 0 /* FIXME */ ++ if (strcmp(kdbreglist[i].reg_name, "ss") == 0) { ++ kdb_machreg_t r; ++ ++ r = (kdb_machreg_t)regs + ++ sizeof(struct pt_regs) - 2*8; ++ *contents = (kdb_machreg_t)SS(r); /* XXX */ ++ return(0); ++ } ++#endif ++ } ++ *contents = *(unsigned long *)((unsigned long)regs + ++ kdbreglist[i].reg_offset); ++ return(0); ++ } ++ ++ for (i=0; i ++ * ++ * Parameters: ++ * regname Pointer to string naming register ++ * regs Pointer to structure containing registers. ++ * contents Unsigned long containing new register contents ++ * Outputs: ++ * Returns: ++ * 0 Success ++ * KDB_BADREG Invalid register name ++ * Locking: ++ * None. ++ * Remarks: ++ */ ++ ++int ++kdba_setregcontents(const char *regname, ++ struct pt_regs *regs, ++ unsigned long contents) ++{ ++ int i; ++ ++ if (regname[0] == '%') { ++ regname++; ++ regs = (struct pt_regs *) ++ (current->thread.sp0 - sizeof(struct pt_regs)); ++ } ++ ++ for (i=0; ir15); ++ kdb_print_nameval("r14", p->r14); ++ kdb_print_nameval("r13", p->r13); ++ kdb_print_nameval("r12", p->r12); ++ kdb_print_nameval("bp", p->bp); ++ kdb_print_nameval("bx", p->bx); ++ kdb_print_nameval("r11", p->r11); ++ kdb_print_nameval("r10", p->r10); ++ kdb_print_nameval("r9", p->r9); ++ kdb_print_nameval("r8", p->r8); ++ kdb_print_nameval("ax", p->ax); ++ kdb_print_nameval("cx", p->cx); ++ kdb_print_nameval("dx", p->dx); ++ kdb_print_nameval("si", p->si); ++ kdb_print_nameval("di", p->di); ++ kdb_print_nameval("orig_ax", p->orig_ax); ++ kdb_print_nameval("ip", p->ip); ++ kdb_printf(fmt, "cs", p->cs); ++ kdb_printf(fmt, "flags", p->flags); ++ kdb_printf(fmt, "sp", p->sp); ++ kdb_printf(fmt, "ss", p->ss); ++ return 0; ++} ++#endif /* CONFIG_X86_32 */ ++ ++/* ++ * kdba_dumpregs ++ * ++ * Dump the specified register set to the display. ++ * ++ * Parameters: ++ * regs Pointer to structure containing registers. ++ * type Character string identifying register set to dump ++ * extra string further identifying register (optional) ++ * Outputs: ++ * Returns: ++ * 0 Success ++ * Locking: ++ * None. ++ * Remarks: ++ * This function will dump the general register set if the type ++ * argument is NULL (struct pt_regs). The alternate register ++ * set types supported by this function: ++ * ++ * d Debug registers ++ * c Control registers ++ * u User registers at most recent entry to kernel ++ * for the process currently selected with "pid" command. ++ * Following not yet implemented: ++ * r Memory Type Range Registers (extra defines register) ++ * ++ * MSR on i386/x86_64 are handled by rdmsr/wrmsr commands. ++ */ ++ ++int ++kdba_dumpregs(struct pt_regs *regs, ++ const char *type, ++ const char *extra) ++{ ++ int i; ++ int count = 0; ++ ++ if (type ++ && (type[0] == 'u')) { ++ type = NULL; ++ regs = (struct pt_regs *) ++ (kdb_current_task->thread.sp0 - sizeof(struct pt_regs)); ++ } ++ ++ if (type == NULL) { ++ struct kdbregs *rlp; ++ kdb_machreg_t contents; ++ ++ if (!regs) { ++ kdb_printf("%s: pt_regs not available, use bt* or pid to select a different task\n", __FUNCTION__); ++ return KDB_BADREG; ++ } ++ ++#ifdef CONFIG_X86_32 ++ for (i=0, rlp=kdbreglist; iip : 0; ++} ++ ++int ++kdba_setpc(struct pt_regs *regs, kdb_machreg_t newpc) ++{ ++ if (KDB_NULL_REGS(regs)) ++ return KDB_BADREG; ++ regs->ip = newpc; ++ KDB_STATE_SET(IP_ADJUSTED); ++ return 0; ++} ++ ++/* ++ * kdba_main_loop ++ * ++ * Do any architecture specific set up before entering the main kdb loop. ++ * The primary function of this routine is to make all processes look the ++ * same to kdb, kdb must be able to list a process without worrying if the ++ * process is running or blocked, so make all process look as though they ++ * are blocked. ++ * ++ * Inputs: ++ * reason The reason KDB was invoked ++ * error The hardware-defined error code ++ * error2 kdb's current reason code. Initially error but can change ++ * acording to kdb state. ++ * db_result Result from break or debug point. ++ * regs The exception frame at time of fault/breakpoint. If reason ++ * is SILENT or CPU_UP then regs is NULL, otherwise it should ++ * always be valid. ++ * Returns: ++ * 0 KDB was invoked for an event which it wasn't responsible ++ * 1 KDB handled the event for which it was invoked. ++ * Outputs: ++ * Sets ip and sp in current->thread. ++ * Locking: ++ * None. ++ * Remarks: ++ * none. ++ */ ++ ++int ++kdba_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, ++ kdb_dbtrap_t db_result, struct pt_regs *regs) ++{ ++ int ret; ++ ++#ifdef CONFIG_X86_64 ++ if (regs) ++ kdba_getregcontents("sp", regs, &(current->thread.sp)); ++#endif ++ ret = kdb_save_running(regs, reason, reason2, error, db_result); ++ kdb_unsave_running(regs); ++ return ret; ++} ++ ++void ++kdba_disableint(kdb_intstate_t *state) ++{ ++ unsigned long *fp = (unsigned long *)state; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ *fp = flags; ++} ++ ++void ++kdba_restoreint(kdb_intstate_t *state) ++{ ++ unsigned long flags = *(unsigned long *)state; ++ local_irq_restore(flags); ++} ++ ++void ++kdba_setsinglestep(struct pt_regs *regs) ++{ ++ if (KDB_NULL_REGS(regs)) ++ return; ++ if (regs->flags & X86_EFLAGS_IF) ++ KDB_STATE_SET(A_IF); ++ else ++ KDB_STATE_CLEAR(A_IF); ++ regs->flags = (regs->flags | X86_EFLAGS_TF) & ~X86_EFLAGS_IF; ++} ++ ++void ++kdba_clearsinglestep(struct pt_regs *regs) ++{ ++ if (KDB_NULL_REGS(regs)) ++ return; ++ if (KDB_STATE(A_IF)) ++ regs->flags |= X86_EFLAGS_IF; ++ else ++ regs->flags &= ~X86_EFLAGS_IF; ++} ++ ++#ifdef CONFIG_X86_32 ++int asmlinkage ++kdba_setjmp(kdb_jmp_buf *jb) ++{ ++#ifdef CONFIG_FRAME_POINTER ++ __asm__ ("movl 8(%esp), %eax\n\t" ++ "movl %ebx, 0(%eax)\n\t" ++ "movl %esi, 4(%eax)\n\t" ++ "movl %edi, 8(%eax)\n\t" ++ "movl (%esp), %ecx\n\t" ++ "movl %ecx, 12(%eax)\n\t" ++ "leal 8(%esp), %ecx\n\t" ++ "movl %ecx, 16(%eax)\n\t" ++ "movl 4(%esp), %ecx\n\t" ++ "movl %ecx, 20(%eax)\n\t"); ++#else /* CONFIG_FRAME_POINTER */ ++ __asm__ ("movl 4(%esp), %eax\n\t" ++ "movl %ebx, 0(%eax)\n\t" ++ "movl %esi, 4(%eax)\n\t" ++ "movl %edi, 8(%eax)\n\t" ++ "movl %ebp, 12(%eax)\n\t" ++ "leal 4(%esp), %ecx\n\t" ++ "movl %ecx, 16(%eax)\n\t" ++ "movl 0(%esp), %ecx\n\t" ++ "movl %ecx, 20(%eax)\n\t"); ++#endif /* CONFIG_FRAME_POINTER */ ++ return 0; ++} ++ ++void asmlinkage ++kdba_longjmp(kdb_jmp_buf *jb, int reason) ++{ ++#ifdef CONFIG_FRAME_POINTER ++ __asm__("movl 8(%esp), %ecx\n\t" ++ "movl 12(%esp), %eax\n\t" ++ "movl 20(%ecx), %edx\n\t" ++ "movl 0(%ecx), %ebx\n\t" ++ "movl 4(%ecx), %esi\n\t" ++ "movl 8(%ecx), %edi\n\t" ++ "movl 12(%ecx), %ebp\n\t" ++ "movl 16(%ecx), %esp\n\t" ++ "jmp *%edx\n"); ++#else /* CONFIG_FRAME_POINTER */ ++ __asm__("movl 4(%esp), %ecx\n\t" ++ "movl 8(%esp), %eax\n\t" ++ "movl 20(%ecx), %edx\n\t" ++ "movl 0(%ecx), %ebx\n\t" ++ "movl 4(%ecx), %esi\n\t" ++ "movl 8(%ecx), %edi\n\t" ++ "movl 12(%ecx), %ebp\n\t" ++ "movl 16(%ecx), %esp\n\t" ++ "jmp *%edx\n"); ++#endif /* CONFIG_FRAME_POINTER */ ++} ++ ++#else /* CONFIG_X86_32 */ ++ ++int asmlinkage ++kdba_setjmp(kdb_jmp_buf *jb) ++{ ++#ifdef CONFIG_FRAME_POINTER ++ __asm__ __volatile__ ++ ("movq %%rbx, (0*8)(%%rdi);" ++ "movq %%rcx, (1*8)(%%rdi);" ++ "movq %%r12, (2*8)(%%rdi);" ++ "movq %%r13, (3*8)(%%rdi);" ++ "movq %%r14, (4*8)(%%rdi);" ++ "movq %%r15, (5*8)(%%rdi);" ++ "leaq 16(%%rsp), %%rdx;" ++ "movq %%rdx, (6*8)(%%rdi);" ++ "movq %%rax, (7*8)(%%rdi)" ++ : ++ : "a" (__builtin_return_address(0)), ++ "c" (__builtin_frame_address(1)) ++ ); ++#else /* !CONFIG_FRAME_POINTER */ ++ __asm__ __volatile__ ++ ("movq %%rbx, (0*8)(%%rdi);" ++ "movq %%rbp, (1*8)(%%rdi);" ++ "movq %%r12, (2*8)(%%rdi);" ++ "movq %%r13, (3*8)(%%rdi);" ++ "movq %%r14, (4*8)(%%rdi);" ++ "movq %%r15, (5*8)(%%rdi);" ++ "leaq 8(%%rsp), %%rdx;" ++ "movq %%rdx, (6*8)(%%rdi);" ++ "movq %%rax, (7*8)(%%rdi)" ++ : ++ : "a" (__builtin_return_address(0)) ++ ); ++#endif /* CONFIG_FRAME_POINTER */ ++ return 0; ++} ++ ++void asmlinkage ++kdba_longjmp(kdb_jmp_buf *jb, int reason) ++{ ++ __asm__("movq (0*8)(%rdi),%rbx;" ++ "movq (1*8)(%rdi),%rbp;" ++ "movq (2*8)(%rdi),%r12;" ++ "movq (3*8)(%rdi),%r13;" ++ "movq (4*8)(%rdi),%r14;" ++ "movq (5*8)(%rdi),%r15;" ++ "movq (7*8)(%rdi),%rdx;" ++ "movq (6*8)(%rdi),%rsp;" ++ "mov %rsi, %rax;" ++ "jmpq *%rdx"); ++} ++#endif /* CONFIG_X86_32 */ ++ ++#ifdef CONFIG_X86_32 ++/* ++ * kdba_stackdepth ++ * ++ * Print processes that are using more than a specific percentage of their ++ * stack. ++ * ++ * Inputs: ++ * argc argument count ++ * argv argument vector ++ * Outputs: ++ * None. ++ * Returns: ++ * zero for success, a kdb diagnostic if error ++ * Locking: ++ * none. ++ * Remarks: ++ * If no percentage is supplied, it uses 60. ++ */ ++ ++static void ++kdba_stackdepth1(struct task_struct *p, unsigned long sp) ++{ ++ struct thread_info *tinfo; ++ int used; ++ const char *type; ++ kdb_ps1(p); ++ do { ++ tinfo = (struct thread_info *)(sp & -THREAD_SIZE); ++ used = sizeof(*tinfo) + THREAD_SIZE - (sp & (THREAD_SIZE-1)); ++ type = NULL; ++ if (kdb_task_has_cpu(p)) { ++ struct kdb_activation_record ar; ++ memset(&ar, 0, sizeof(ar)); ++ kdba_get_stack_info_alternate(sp, -1, &ar); ++ type = ar.stack.id; ++ } ++ if (!type) ++ type = "process"; ++ kdb_printf(" %s stack %p sp %lx used %d\n", type, tinfo, sp, used); ++ sp = tinfo->previous_esp; ++ } while (sp); ++} ++ ++static int ++kdba_stackdepth(int argc, const char **argv) ++{ ++ int diag, cpu, threshold, used, over; ++ unsigned long percentage; ++ unsigned long esp; ++ long offset = 0; ++ int nextarg; ++ struct task_struct *p, *g; ++ struct kdb_running_process *krp; ++ struct thread_info *tinfo; ++ ++ if (argc == 0) { ++ percentage = 60; ++ } else if (argc == 1) { ++ nextarg = 1; ++ diag = kdbgetaddrarg(argc, argv, &nextarg, &percentage, &offset, NULL); ++ if (diag) ++ return diag; ++ } else { ++ return KDB_ARGCOUNT; ++ } ++ percentage = max_t(int, percentage, 1); ++ percentage = min_t(int, percentage, 100); ++ threshold = ((2 * THREAD_SIZE * percentage) / 100 + 1) >> 1; ++ kdb_printf("stackdepth: processes using more than %ld%% (%d bytes) of stack\n", ++ percentage, threshold); ++ ++ /* Run the active tasks first, they can have multiple stacks */ ++ for (cpu = 0, krp = kdb_running_process; cpu < NR_CPUS; ++cpu, ++krp) { ++ if (!cpu_online(cpu)) ++ continue; ++ p = krp->p; ++ esp = krp->arch.sp; ++ over = 0; ++ do { ++ tinfo = (struct thread_info *)(esp & -THREAD_SIZE); ++ used = sizeof(*tinfo) + THREAD_SIZE - (esp & (THREAD_SIZE-1)); ++ if (used >= threshold) ++ over = 1; ++ esp = tinfo->previous_esp; ++ } while (esp); ++ if (over) ++ kdba_stackdepth1(p, krp->arch.sp); ++ } ++ /* Now the tasks that are not on cpus */ ++ kdb_do_each_thread(g, p) { ++ if (kdb_task_has_cpu(p)) ++ continue; ++ esp = p->thread.sp; ++ used = sizeof(*tinfo) + THREAD_SIZE - (esp & (THREAD_SIZE-1)); ++ over = used >= threshold; ++ if (over) ++ kdba_stackdepth1(p, esp); ++ } kdb_while_each_thread(g, p); ++ ++ return 0; ++} ++#else /* CONFIG_X86_32 */ ++ ++ ++/* ++ * kdba_entry ++ * ++ * This is the interface routine between ++ * the notifier die_chain and kdb ++ */ ++static int kdba_entry( struct notifier_block *b, unsigned long val, void *v) ++{ ++ struct die_args *args = v; ++ int err, trap, ret = 0; ++ struct pt_regs *regs; ++ ++ regs = args->regs; ++ err = args->err; ++ trap = args->trapnr; ++ switch (val){ ++#ifdef CONFIG_SMP ++ case DIE_NMI_IPI: ++ ret = kdb_ipi(regs, NULL); ++ break; ++#endif /* CONFIG_SMP */ ++ case DIE_OOPS: ++ ret = kdb(KDB_REASON_OOPS, err, regs); ++ break; ++ case DIE_CALL: ++ ret = kdb(KDB_REASON_ENTER, err, regs); ++ break; ++ case DIE_DEBUG: ++ ret = kdb(KDB_REASON_DEBUG, err, regs); ++ break; ++ case DIE_NMIWATCHDOG: ++ ret = kdb(KDB_REASON_NMI, err, regs); ++ break; ++ case DIE_INT3: ++ ret = kdb(KDB_REASON_BREAK, err, regs); ++ // falls thru ++ default: ++ break; ++ } ++ return (ret ? NOTIFY_STOP : NOTIFY_DONE); ++} ++ ++/* ++ * notifier block for kdb entry ++ */ ++static struct notifier_block kdba_notifier = { ++ .notifier_call = kdba_entry ++}; ++#endif /* CONFIG_X86_32 */ ++ ++asmlinkage int kdb_call(void); ++ ++/* Executed once on each cpu at startup. */ ++void ++kdba_cpu_up(void) ++{ ++} ++ ++static int __init ++kdba_arch_init(void) ++{ ++ set_intr_gate(KDBENTER_VECTOR, kdb_call); ++ return 0; ++} ++ ++arch_initcall(kdba_arch_init); ++ ++/* ++ * kdba_init ++ * ++ * Architecture specific initialization. ++ * ++ * Parameters: ++ * None. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * None. ++ */ ++ ++void __init ++kdba_init(void) ++{ ++ kdba_arch_init(); /* Need to register KDBENTER_VECTOR early */ ++ kdb_register("pt_regs", kdba_pt_regs, "address", "Format struct pt_regs", 0); ++#ifdef CONFIG_X86_32 ++ kdb_register("stackdepth", kdba_stackdepth, "[percentage]", "Print processes using >= stack percentage", 0); ++#else ++ register_die_notifier(&kdba_notifier); ++#endif ++ return; ++} ++ ++/* ++ * kdba_adjust_ip ++ * ++ * Architecture specific adjustment of instruction pointer before leaving ++ * kdb. ++ * ++ * Parameters: ++ * reason The reason KDB was invoked ++ * error The hardware-defined error code ++ * regs The exception frame at time of fault/breakpoint. If reason ++ * is SILENT or CPU_UP then regs is NULL, otherwise it should ++ * always be valid. ++ * Returns: ++ * None. ++ * Locking: ++ * None. ++ * Remarks: ++ * noop on ix86. ++ */ ++ ++void ++kdba_adjust_ip(kdb_reason_t reason, int error, struct pt_regs *regs) ++{ ++ return; ++} ++ ++void ++kdba_set_current_task(const struct task_struct *p) ++{ ++ kdb_current_task = p; ++ if (kdb_task_has_cpu(p)) { ++ struct kdb_running_process *krp = kdb_running_process + kdb_process_cpu(p); ++ kdb_current_regs = krp->regs; ++ return; ++ } ++ kdb_current_regs = NULL; ++} ++ ++#ifdef CONFIG_X86_32 ++/* ++ * asm-i386 uaccess.h supplies __copy_to_user which relies on MMU to ++ * trap invalid addresses in the _xxx fields. Verify the other address ++ * of the pair is valid by accessing the first and last byte ourselves, ++ * then any access violations should only be caused by the _xxx ++ * addresses, ++ */ ++ ++int ++kdba_putarea_size(unsigned long to_xxx, void *from, size_t size) ++{ ++ mm_segment_t oldfs = get_fs(); ++ int r; ++ char c; ++ c = *((volatile char *)from); ++ c = *((volatile char *)from + size - 1); ++ ++ if (to_xxx < PAGE_OFFSET) { ++ return kdb_putuserarea_size(to_xxx, from, size); ++ } ++ ++ set_fs(KERNEL_DS); ++ r = __copy_to_user_inatomic((void __user *)to_xxx, from, size); ++ set_fs(oldfs); ++ return r; ++} ++ ++int ++kdba_getarea_size(void *to, unsigned long from_xxx, size_t size) ++{ ++ mm_segment_t oldfs = get_fs(); ++ int r; ++ *((volatile char *)to) = '\0'; ++ *((volatile char *)to + size - 1) = '\0'; ++ ++ if (from_xxx < PAGE_OFFSET) { ++ return kdb_getuserarea_size(to, from_xxx, size); ++ } ++ ++ set_fs(KERNEL_DS); ++ switch (size) { ++ case 1: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, 1); ++ break; ++ case 2: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, 2); ++ break; ++ case 4: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, 4); ++ break; ++ case 8: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, 8); ++ break; ++ default: ++ r = __copy_to_user_inatomic((void __user *)to, (void *)from_xxx, size); ++ break; ++ } ++ set_fs(oldfs); ++ return r; ++} ++ ++int ++kdba_verify_rw(unsigned long addr, size_t size) ++{ ++ unsigned char data[size]; ++ return(kdba_getarea_size(data, addr, size) || kdba_putarea_size(addr, data, size)); ++} ++#endif /* CONFIG_X86_32 */ ++ ++#ifdef CONFIG_SMP ++ ++#include ++ ++gate_desc save_idt[NR_VECTORS]; ++ ++void kdba_takeover_vector(int vector) ++{ ++ memcpy(&save_idt[vector], &idt_table[vector], sizeof(gate_desc)); ++ set_intr_gate(KDB_VECTOR, kdb_interrupt); ++ return; ++} ++ ++void kdba_giveback_vector(int vector) ++{ ++ native_write_idt_entry(idt_table, vector, &save_idt[vector]); ++ return; ++} ++ ++/* When first entering KDB, try a normal IPI. That reduces backtrace problems ++ * on the other cpus. ++ */ ++void ++smp_kdb_stop(void) ++{ ++ if (!KDB_FLAG(NOIPI)) { ++ kdba_takeover_vector(KDB_VECTOR); ++ apic->send_IPI_allbutself(KDB_VECTOR); ++ } ++} ++ ++/* The normal KDB IPI handler */ ++#ifdef CONFIG_X86_64 ++asmlinkage ++#endif ++void ++smp_kdb_interrupt(struct pt_regs *regs) ++{ ++ struct pt_regs *old_regs = set_irq_regs(regs); ++ ack_APIC_irq(); ++ irq_enter(); ++ kdb_ipi(regs, NULL); ++ irq_exit(); ++ set_irq_regs(old_regs); ++} ++ ++/* Invoked once from kdb_wait_for_cpus when waiting for cpus. For those cpus ++ * that have not responded to the normal KDB interrupt yet, hit them with an ++ * NMI event. ++ */ ++void ++kdba_wait_for_cpus(void) ++{ ++ int c; ++ if (KDB_FLAG(CATASTROPHIC)) ++ return; ++ kdb_printf(" Sending NMI to non-responding cpus: "); ++ for_each_online_cpu(c) { ++ if (kdb_running_process[c].seqno < kdb_seqno - 1) { ++ kdb_printf(" %d", c); ++ apic->send_IPI_mask(cpumask_of(c), NMI_VECTOR); ++ } ++ } ++ kdb_printf(".\n"); ++} ++ ++#endif /* CONFIG_SMP */ ++ ++#ifdef CONFIG_KDB_KDUMP ++void kdba_kdump_prepare(struct pt_regs *regs) ++{ ++ int i; ++ struct pt_regs r; ++ if (regs == NULL) ++ regs = &r; ++ ++ for (i = 1; i < NR_CPUS; ++i) { ++ if (!cpu_online(i)) ++ continue; ++ ++ KDB_STATE_SET_CPU(KEXEC, i); ++ } ++ ++ machine_crash_shutdown(regs); ++} ++ ++extern void halt_current_cpu(struct pt_regs *); ++ ++void kdba_kdump_shutdown_slave(struct pt_regs *regs) ++{ ++#ifndef CONFIG_XEN ++ halt_current_cpu(regs); ++#endif /* CONFIG_XEN */ ++} ++ ++#endif /* CONFIG_KDB_KDUMP */ +--- /dev/null ++++ b/arch/x86/kdb/pc_keyb.h +@@ -0,0 +1,137 @@ ++/* ++ * include/linux/pc_keyb.h ++ * ++ * PC Keyboard And Keyboard Controller ++ * ++ * (c) 1997 Martin Mares ++ */ ++ ++/* ++ * Configuration Switches ++ */ ++ ++#undef KBD_REPORT_ERR /* Report keyboard errors */ ++#define KBD_REPORT_UNKN /* Report unknown scan codes */ ++#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ ++#undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */ ++#undef INITIALIZE_MOUSE /* Define if your PS/2 mouse needs initialization. */ ++ ++ ++ ++#define KBD_INIT_TIMEOUT 1000 /* Timeout in ms for initializing the keyboard */ ++#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */ ++#define KBD_TIMEOUT 1000 /* Timeout in ms for keyboard command acknowledge */ ++ ++/* ++ * Internal variables of the driver ++ */ ++ ++extern unsigned char pckbd_read_mask; ++extern unsigned char aux_device_present; ++ ++/* ++ * Keyboard Controller Registers on normal PCs. ++ */ ++ ++#define KBD_STATUS_REG 0x64 /* Status register (R) */ ++#define KBD_CNTL_REG 0x64 /* Controller command register (W) */ ++#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */ ++ ++/* ++ * Keyboard Controller Commands ++ */ ++ ++#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ ++#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ ++#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ ++#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ ++#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ ++#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ ++#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ ++#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ ++#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ ++#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ ++#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if ++ initiated by the auxiliary device */ ++#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ ++ ++/* ++ * Keyboard Commands ++ */ ++ ++#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ ++#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ ++#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ ++#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */ ++#define KBD_CMD_RESET 0xFF /* Reset */ ++ ++/* ++ * Keyboard Replies ++ */ ++ ++#define KBD_REPLY_POR 0xAA /* Power on reset */ ++#define KBD_REPLY_ACK 0xFA /* Command ACK */ ++#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ ++ ++/* ++ * Status Register Bits ++ */ ++ ++#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ ++#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ ++#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ ++#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ ++#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ ++#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ ++#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ ++#define KBD_STAT_PERR 0x80 /* Parity error */ ++ ++#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF) ++ ++/* ++ * Controller Mode Register Bits ++ */ ++ ++#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ ++#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ ++#define KBD_MODE_SYS 0x04 /* The system flag (?) */ ++#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ ++#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ ++#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ ++#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ ++#define KBD_MODE_RFU 0x80 ++ ++/* ++ * Mouse Commands ++ */ ++ ++#define AUX_SET_RES 0xE8 /* Set resolution */ ++#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ ++#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ ++#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ ++#define AUX_SET_STREAM 0xEA /* Set stream mode */ ++#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ ++#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ ++#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ ++#define AUX_RESET 0xFF /* Reset aux device */ ++#define AUX_ACK 0xFA /* Command byte ACK. */ ++ ++#define AUX_BUF_SIZE 2048 /* This might be better divisible by ++ three to make overruns stay in sync ++ but then the read function would need ++ a lock etc - ick */ ++ ++struct aux_queue { ++ unsigned long head; ++ unsigned long tail; ++ wait_queue_head_t proc_list; ++ struct fasync_struct *fasync; ++ unsigned char buf[AUX_BUF_SIZE]; ++}; ++ ++ ++/* How to access the keyboard macros on this platform. */ ++#define kbd_read_input() inb(KBD_DATA_REG) ++#define kbd_read_status() inb(KBD_STATUS_REG) ++#define kbd_write_output(val) outb(val, KBD_DATA_REG) ++#define kbd_write_command(val) outb(val, KBD_CNTL_REG) +--- /dev/null ++++ b/arch/x86/kdb/x86-dis.c +@@ -0,0 +1,4688 @@ ++/* Print i386 instructions for GDB, the GNU debugger. ++ Copyright 1988, 1989, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, ++ 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. ++ ++ This file is part of GDB. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ++ ++/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. ++ * Run through col -b to remove trailing whitespace and various #ifdef/ifndef ++ * __KERNEL__ added. ++ * Keith Owens 15 May 2006 ++ */ ++ ++/* 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu) ++ July 1988 ++ modified by John Hassey (hassey@dg-rtp.dg.com) ++ x86-64 support added by Jan Hubicka (jh@suse.cz) ++ VIA PadLock support by Michal Ludvig (mludvig@suse.cz). */ ++ ++/* The main tables describing the instructions is essentially a copy ++ of the "Opcode Map" chapter (Appendix A) of the Intel 80386 ++ Programmers Manual. Usually, there is a capital letter, followed ++ by a small letter. The capital letter tell the addressing mode, ++ and the small letter tells about the operand size. Refer to ++ the Intel manual for details. */ ++ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++#include ++#define abort() BUG() ++#else /* __KERNEL__ */ ++#include "dis-asm.h" ++#include "sysdep.h" ++#include "opintl.h" ++#endif /* __KERNEL__ */ ++ ++#define MAXLEN 20 ++ ++#ifndef __KERNEL__ ++#include ++#endif /* __KERNEL__ */ ++ ++#ifndef UNIXWARE_COMPAT ++/* Set non-zero for broken, compatible instructions. Set to zero for ++ non-broken opcodes. */ ++#define UNIXWARE_COMPAT 1 ++#endif ++ ++static int fetch_data (struct disassemble_info *, bfd_byte *); ++static void ckprefix (void); ++static const char *prefix_name (int, int); ++static int print_insn (bfd_vma, disassemble_info *); ++static void dofloat (int); ++static void OP_ST (int, int); ++static void OP_STi (int, int); ++static int putop (const char *, int); ++static void oappend (const char *); ++static void append_seg (void); ++static void OP_indirE (int, int); ++static void print_operand_value (char *, int, bfd_vma); ++static void OP_E (int, int); ++static void OP_G (int, int); ++static bfd_vma get64 (void); ++static bfd_signed_vma get32 (void); ++static bfd_signed_vma get32s (void); ++static int get16 (void); ++static void set_op (bfd_vma, int); ++static void OP_REG (int, int); ++static void OP_IMREG (int, int); ++static void OP_I (int, int); ++static void OP_I64 (int, int); ++static void OP_sI (int, int); ++static void OP_J (int, int); ++static void OP_SEG (int, int); ++static void OP_DIR (int, int); ++static void OP_OFF (int, int); ++static void OP_OFF64 (int, int); ++static void ptr_reg (int, int); ++static void OP_ESreg (int, int); ++static void OP_DSreg (int, int); ++static void OP_C (int, int); ++static void OP_D (int, int); ++static void OP_T (int, int); ++static void OP_Rd (int, int); ++static void OP_MMX (int, int); ++static void OP_XMM (int, int); ++static void OP_EM (int, int); ++static void OP_EX (int, int); ++static void OP_MS (int, int); ++static void OP_XS (int, int); ++static void OP_M (int, int); ++static void OP_VMX (int, int); ++static void OP_0fae (int, int); ++static void OP_0f07 (int, int); ++static void NOP_Fixup (int, int); ++static void OP_3DNowSuffix (int, int); ++static void OP_SIMD_Suffix (int, int); ++static void SIMD_Fixup (int, int); ++static void PNI_Fixup (int, int); ++static void SVME_Fixup (int, int); ++static void INVLPG_Fixup (int, int); ++static void BadOp (void); ++static void SEG_Fixup (int, int); ++static void VMX_Fixup (int, int); ++ ++struct dis_private { ++ /* Points to first byte not fetched. */ ++ bfd_byte *max_fetched; ++ bfd_byte the_buffer[MAXLEN]; ++ bfd_vma insn_start; ++ int orig_sizeflag; ++#ifndef __KERNEL__ ++ jmp_buf bailout; ++#endif /* __KERNEL__ */ ++}; ++ ++/* The opcode for the fwait instruction, which we treat as a prefix ++ when we can. */ ++#define FWAIT_OPCODE (0x9b) ++ ++/* Set to 1 for 64bit mode disassembly. */ ++static int mode_64bit; ++ ++/* Flags for the prefixes for the current instruction. See below. */ ++static int prefixes; ++ ++/* REX prefix the current instruction. See below. */ ++static int rex; ++/* Bits of REX we've already used. */ ++static int rex_used; ++#define REX_MODE64 8 ++#define REX_EXTX 4 ++#define REX_EXTY 2 ++#define REX_EXTZ 1 ++/* Mark parts used in the REX prefix. When we are testing for ++ empty prefix (for 8bit register REX extension), just mask it ++ out. Otherwise test for REX bit is excuse for existence of REX ++ only in case value is nonzero. */ ++#define USED_REX(value) \ ++ { \ ++ if (value) \ ++ rex_used |= (rex & value) ? (value) | 0x40 : 0; \ ++ else \ ++ rex_used |= 0x40; \ ++ } ++ ++/* Flags for prefixes which we somehow handled when printing the ++ current instruction. */ ++static int used_prefixes; ++ ++/* Flags stored in PREFIXES. */ ++#define PREFIX_REPZ 1 ++#define PREFIX_REPNZ 2 ++#define PREFIX_LOCK 4 ++#define PREFIX_CS 8 ++#define PREFIX_SS 0x10 ++#define PREFIX_DS 0x20 ++#define PREFIX_ES 0x40 ++#define PREFIX_FS 0x80 ++#define PREFIX_GS 0x100 ++#define PREFIX_DATA 0x200 ++#define PREFIX_ADDR 0x400 ++#define PREFIX_FWAIT 0x800 ++ ++/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) ++ to ADDR (exclusive) are valid. Returns 1 for success, longjmps ++ on error. */ ++#define FETCH_DATA(info, addr) \ ++ ((addr) <= ((struct dis_private *) (info->private_data))->max_fetched \ ++ ? 1 : fetch_data ((info), (addr))) ++ ++static int ++fetch_data (struct disassemble_info *info, bfd_byte *addr) ++{ ++ int status; ++ struct dis_private *priv = (struct dis_private *) info->private_data; ++ bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); ++ ++ status = (*info->read_memory_func) (start, ++ priv->max_fetched, ++ addr - priv->max_fetched, ++ info); ++ if (status != 0) ++ { ++ /* If we did manage to read at least one byte, then ++ print_insn_i386 will do something sensible. Otherwise, print ++ an error. We do that here because this is where we know ++ STATUS. */ ++ if (priv->max_fetched == priv->the_buffer) ++ (*info->memory_error_func) (status, start, info); ++#ifndef __KERNEL__ ++ longjmp (priv->bailout, 1); ++#else /* __KERNEL__ */ ++ /* XXX - what to do? */ ++ kdb_printf("Hmm. longjmp.\n"); ++#endif /* __KERNEL__ */ ++ } ++ else ++ priv->max_fetched = addr; ++ return 1; ++} ++ ++#define XX NULL, 0 ++ ++#define Eb OP_E, b_mode ++#define Ev OP_E, v_mode ++#define Ed OP_E, d_mode ++#define Eq OP_E, q_mode ++#define Edq OP_E, dq_mode ++#define Edqw OP_E, dqw_mode ++#define indirEv OP_indirE, branch_v_mode ++#define indirEp OP_indirE, f_mode ++#define Em OP_E, m_mode ++#define Ew OP_E, w_mode ++#define Ma OP_E, v_mode ++#define M OP_M, 0 /* lea, lgdt, etc. */ ++#define Mp OP_M, f_mode /* 32 or 48 bit memory operand for LDS, LES etc */ ++#define Gb OP_G, b_mode ++#define Gv OP_G, v_mode ++#define Gd OP_G, d_mode ++#define Gdq OP_G, dq_mode ++#define Gm OP_G, m_mode ++#define Gw OP_G, w_mode ++#define Rd OP_Rd, d_mode ++#define Rm OP_Rd, m_mode ++#define Ib OP_I, b_mode ++#define sIb OP_sI, b_mode /* sign extened byte */ ++#define Iv OP_I, v_mode ++#define Iq OP_I, q_mode ++#define Iv64 OP_I64, v_mode ++#define Iw OP_I, w_mode ++#define I1 OP_I, const_1_mode ++#define Jb OP_J, b_mode ++#define Jv OP_J, v_mode ++#define Cm OP_C, m_mode ++#define Dm OP_D, m_mode ++#define Td OP_T, d_mode ++#define Sv SEG_Fixup, v_mode ++ ++#define RMeAX OP_REG, eAX_reg ++#define RMeBX OP_REG, eBX_reg ++#define RMeCX OP_REG, eCX_reg ++#define RMeDX OP_REG, eDX_reg ++#define RMeSP OP_REG, eSP_reg ++#define RMeBP OP_REG, eBP_reg ++#define RMeSI OP_REG, eSI_reg ++#define RMeDI OP_REG, eDI_reg ++#define RMrAX OP_REG, rAX_reg ++#define RMrBX OP_REG, rBX_reg ++#define RMrCX OP_REG, rCX_reg ++#define RMrDX OP_REG, rDX_reg ++#define RMrSP OP_REG, rSP_reg ++#define RMrBP OP_REG, rBP_reg ++#define RMrSI OP_REG, rSI_reg ++#define RMrDI OP_REG, rDI_reg ++#define RMAL OP_REG, al_reg ++#define RMAL OP_REG, al_reg ++#define RMCL OP_REG, cl_reg ++#define RMDL OP_REG, dl_reg ++#define RMBL OP_REG, bl_reg ++#define RMAH OP_REG, ah_reg ++#define RMCH OP_REG, ch_reg ++#define RMDH OP_REG, dh_reg ++#define RMBH OP_REG, bh_reg ++#define RMAX OP_REG, ax_reg ++#define RMDX OP_REG, dx_reg ++ ++#define eAX OP_IMREG, eAX_reg ++#define eBX OP_IMREG, eBX_reg ++#define eCX OP_IMREG, eCX_reg ++#define eDX OP_IMREG, eDX_reg ++#define eSP OP_IMREG, eSP_reg ++#define eBP OP_IMREG, eBP_reg ++#define eSI OP_IMREG, eSI_reg ++#define eDI OP_IMREG, eDI_reg ++#define AL OP_IMREG, al_reg ++#define AL OP_IMREG, al_reg ++#define CL OP_IMREG, cl_reg ++#define DL OP_IMREG, dl_reg ++#define BL OP_IMREG, bl_reg ++#define AH OP_IMREG, ah_reg ++#define CH OP_IMREG, ch_reg ++#define DH OP_IMREG, dh_reg ++#define BH OP_IMREG, bh_reg ++#define AX OP_IMREG, ax_reg ++#define DX OP_IMREG, dx_reg ++#define indirDX OP_IMREG, indir_dx_reg ++ ++#define Sw OP_SEG, w_mode ++#define Ap OP_DIR, 0 ++#define Ob OP_OFF, b_mode ++#define Ob64 OP_OFF64, b_mode ++#define Ov OP_OFF, v_mode ++#define Ov64 OP_OFF64, v_mode ++#define Xb OP_DSreg, eSI_reg ++#define Xv OP_DSreg, eSI_reg ++#define Yb OP_ESreg, eDI_reg ++#define Yv OP_ESreg, eDI_reg ++#define DSBX OP_DSreg, eBX_reg ++ ++#define es OP_REG, es_reg ++#define ss OP_REG, ss_reg ++#define cs OP_REG, cs_reg ++#define ds OP_REG, ds_reg ++#define fs OP_REG, fs_reg ++#define gs OP_REG, gs_reg ++ ++#define MX OP_MMX, 0 ++#define XM OP_XMM, 0 ++#define EM OP_EM, v_mode ++#define EX OP_EX, v_mode ++#define MS OP_MS, v_mode ++#define XS OP_XS, v_mode ++#define VM OP_VMX, q_mode ++#define OPSUF OP_3DNowSuffix, 0 ++#define OPSIMD OP_SIMD_Suffix, 0 ++ ++#define cond_jump_flag NULL, cond_jump_mode ++#define loop_jcxz_flag NULL, loop_jcxz_mode ++ ++/* bits in sizeflag */ ++#define SUFFIX_ALWAYS 4 ++#define AFLAG 2 ++#define DFLAG 1 ++ ++#define b_mode 1 /* byte operand */ ++#define v_mode 2 /* operand size depends on prefixes */ ++#define w_mode 3 /* word operand */ ++#define d_mode 4 /* double word operand */ ++#define q_mode 5 /* quad word operand */ ++#define t_mode 6 /* ten-byte operand */ ++#define x_mode 7 /* 16-byte XMM operand */ ++#define m_mode 8 /* d_mode in 32bit, q_mode in 64bit mode. */ ++#define cond_jump_mode 9 ++#define loop_jcxz_mode 10 ++#define dq_mode 11 /* operand size depends on REX prefixes. */ ++#define dqw_mode 12 /* registers like dq_mode, memory like w_mode. */ ++#define f_mode 13 /* 4- or 6-byte pointer operand */ ++#define const_1_mode 14 ++#define branch_v_mode 15 /* v_mode for branch. */ ++ ++#define es_reg 100 ++#define cs_reg 101 ++#define ss_reg 102 ++#define ds_reg 103 ++#define fs_reg 104 ++#define gs_reg 105 ++ ++#define eAX_reg 108 ++#define eCX_reg 109 ++#define eDX_reg 110 ++#define eBX_reg 111 ++#define eSP_reg 112 ++#define eBP_reg 113 ++#define eSI_reg 114 ++#define eDI_reg 115 ++ ++#define al_reg 116 ++#define cl_reg 117 ++#define dl_reg 118 ++#define bl_reg 119 ++#define ah_reg 120 ++#define ch_reg 121 ++#define dh_reg 122 ++#define bh_reg 123 ++ ++#define ax_reg 124 ++#define cx_reg 125 ++#define dx_reg 126 ++#define bx_reg 127 ++#define sp_reg 128 ++#define bp_reg 129 ++#define si_reg 130 ++#define di_reg 131 ++ ++#define rAX_reg 132 ++#define rCX_reg 133 ++#define rDX_reg 134 ++#define rBX_reg 135 ++#define rSP_reg 136 ++#define rBP_reg 137 ++#define rSI_reg 138 ++#define rDI_reg 139 ++ ++#define indir_dx_reg 150 ++ ++#define FLOATCODE 1 ++#define USE_GROUPS 2 ++#define USE_PREFIX_USER_TABLE 3 ++#define X86_64_SPECIAL 4 ++ ++#define FLOAT NULL, NULL, FLOATCODE, NULL, 0, NULL, 0 ++ ++#define GRP1b NULL, NULL, USE_GROUPS, NULL, 0, NULL, 0 ++#define GRP1S NULL, NULL, USE_GROUPS, NULL, 1, NULL, 0 ++#define GRP1Ss NULL, NULL, USE_GROUPS, NULL, 2, NULL, 0 ++#define GRP2b NULL, NULL, USE_GROUPS, NULL, 3, NULL, 0 ++#define GRP2S NULL, NULL, USE_GROUPS, NULL, 4, NULL, 0 ++#define GRP2b_one NULL, NULL, USE_GROUPS, NULL, 5, NULL, 0 ++#define GRP2S_one NULL, NULL, USE_GROUPS, NULL, 6, NULL, 0 ++#define GRP2b_cl NULL, NULL, USE_GROUPS, NULL, 7, NULL, 0 ++#define GRP2S_cl NULL, NULL, USE_GROUPS, NULL, 8, NULL, 0 ++#define GRP3b NULL, NULL, USE_GROUPS, NULL, 9, NULL, 0 ++#define GRP3S NULL, NULL, USE_GROUPS, NULL, 10, NULL, 0 ++#define GRP4 NULL, NULL, USE_GROUPS, NULL, 11, NULL, 0 ++#define GRP5 NULL, NULL, USE_GROUPS, NULL, 12, NULL, 0 ++#define GRP6 NULL, NULL, USE_GROUPS, NULL, 13, NULL, 0 ++#define GRP7 NULL, NULL, USE_GROUPS, NULL, 14, NULL, 0 ++#define GRP8 NULL, NULL, USE_GROUPS, NULL, 15, NULL, 0 ++#define GRP9 NULL, NULL, USE_GROUPS, NULL, 16, NULL, 0 ++#define GRP10 NULL, NULL, USE_GROUPS, NULL, 17, NULL, 0 ++#define GRP11 NULL, NULL, USE_GROUPS, NULL, 18, NULL, 0 ++#define GRP12 NULL, NULL, USE_GROUPS, NULL, 19, NULL, 0 ++#define GRP13 NULL, NULL, USE_GROUPS, NULL, 20, NULL, 0 ++#define GRP14 NULL, NULL, USE_GROUPS, NULL, 21, NULL, 0 ++#define GRPAMD NULL, NULL, USE_GROUPS, NULL, 22, NULL, 0 ++#define GRPPADLCK1 NULL, NULL, USE_GROUPS, NULL, 23, NULL, 0 ++#define GRPPADLCK2 NULL, NULL, USE_GROUPS, NULL, 24, NULL, 0 ++ ++#define PREGRP0 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 0, NULL, 0 ++#define PREGRP1 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 1, NULL, 0 ++#define PREGRP2 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 2, NULL, 0 ++#define PREGRP3 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 3, NULL, 0 ++#define PREGRP4 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 4, NULL, 0 ++#define PREGRP5 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 5, NULL, 0 ++#define PREGRP6 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 6, NULL, 0 ++#define PREGRP7 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 7, NULL, 0 ++#define PREGRP8 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 8, NULL, 0 ++#define PREGRP9 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 9, NULL, 0 ++#define PREGRP10 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 10, NULL, 0 ++#define PREGRP11 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 11, NULL, 0 ++#define PREGRP12 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 12, NULL, 0 ++#define PREGRP13 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 13, NULL, 0 ++#define PREGRP14 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 14, NULL, 0 ++#define PREGRP15 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 15, NULL, 0 ++#define PREGRP16 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 16, NULL, 0 ++#define PREGRP17 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 17, NULL, 0 ++#define PREGRP18 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 18, NULL, 0 ++#define PREGRP19 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 19, NULL, 0 ++#define PREGRP20 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 20, NULL, 0 ++#define PREGRP21 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 21, NULL, 0 ++#define PREGRP22 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 22, NULL, 0 ++#define PREGRP23 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 23, NULL, 0 ++#define PREGRP24 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 24, NULL, 0 ++#define PREGRP25 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 25, NULL, 0 ++#define PREGRP26 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 26, NULL, 0 ++#define PREGRP27 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 27, NULL, 0 ++#define PREGRP28 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 28, NULL, 0 ++#define PREGRP29 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 29, NULL, 0 ++#define PREGRP30 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 30, NULL, 0 ++#define PREGRP31 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 31, NULL, 0 ++#define PREGRP32 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 32, NULL, 0 ++ ++#define X86_64_0 NULL, NULL, X86_64_SPECIAL, NULL, 0, NULL, 0 ++ ++typedef void (*op_rtn) (int bytemode, int sizeflag); ++ ++struct dis386 { ++ const char *name; ++ op_rtn op1; ++ int bytemode1; ++ op_rtn op2; ++ int bytemode2; ++ op_rtn op3; ++ int bytemode3; ++}; ++ ++/* Upper case letters in the instruction names here are macros. ++ 'A' => print 'b' if no register operands or suffix_always is true ++ 'B' => print 'b' if suffix_always is true ++ 'C' => print 's' or 'l' ('w' or 'd' in Intel mode) depending on operand ++ . size prefix ++ 'E' => print 'e' if 32-bit form of jcxz ++ 'F' => print 'w' or 'l' depending on address size prefix (loop insns) ++ 'H' => print ",pt" or ",pn" branch hint ++ 'I' => honor following macro letter even in Intel mode (implemented only ++ . for some of the macro letters) ++ 'J' => print 'l' ++ 'L' => print 'l' if suffix_always is true ++ 'N' => print 'n' if instruction has no wait "prefix" ++ 'O' => print 'd', or 'o' ++ 'P' => print 'w', 'l' or 'q' if instruction has an operand size prefix, ++ . or suffix_always is true. print 'q' if rex prefix is present. ++ 'Q' => print 'w', 'l' or 'q' if no register operands or suffix_always ++ . is true ++ 'R' => print 'w', 'l' or 'q' ("wd" or "dq" in intel mode) ++ 'S' => print 'w', 'l' or 'q' if suffix_always is true ++ 'T' => print 'q' in 64bit mode and behave as 'P' otherwise ++ 'U' => print 'q' in 64bit mode and behave as 'Q' otherwise ++ 'W' => print 'b' or 'w' ("w" or "de" in intel mode) ++ 'X' => print 's', 'd' depending on data16 prefix (for XMM) ++ 'Y' => 'q' if instruction has an REX 64bit overwrite prefix ++ ++ Many of the above letters print nothing in Intel mode. See "putop" ++ for the details. ++ ++ Braces '{' and '}', and vertical bars '|', indicate alternative ++ mnemonic strings for AT&T, Intel, X86_64 AT&T, and X86_64 Intel ++ modes. In cases where there are only two alternatives, the X86_64 ++ instruction is reserved, and "(bad)" is printed. ++*/ ++ ++static const struct dis386 dis386[] = { ++ /* 00 */ ++ { "addB", Eb, Gb, XX }, ++ { "addS", Ev, Gv, XX }, ++ { "addB", Gb, Eb, XX }, ++ { "addS", Gv, Ev, XX }, ++ { "addB", AL, Ib, XX }, ++ { "addS", eAX, Iv, XX }, ++ { "push{T|}", es, XX, XX }, ++ { "pop{T|}", es, XX, XX }, ++ /* 08 */ ++ { "orB", Eb, Gb, XX }, ++ { "orS", Ev, Gv, XX }, ++ { "orB", Gb, Eb, XX }, ++ { "orS", Gv, Ev, XX }, ++ { "orB", AL, Ib, XX }, ++ { "orS", eAX, Iv, XX }, ++ { "push{T|}", cs, XX, XX }, ++ { "(bad)", XX, XX, XX }, /* 0x0f extended opcode escape */ ++ /* 10 */ ++ { "adcB", Eb, Gb, XX }, ++ { "adcS", Ev, Gv, XX }, ++ { "adcB", Gb, Eb, XX }, ++ { "adcS", Gv, Ev, XX }, ++ { "adcB", AL, Ib, XX }, ++ { "adcS", eAX, Iv, XX }, ++ { "push{T|}", ss, XX, XX }, ++ { "popT|}", ss, XX, XX }, ++ /* 18 */ ++ { "sbbB", Eb, Gb, XX }, ++ { "sbbS", Ev, Gv, XX }, ++ { "sbbB", Gb, Eb, XX }, ++ { "sbbS", Gv, Ev, XX }, ++ { "sbbB", AL, Ib, XX }, ++ { "sbbS", eAX, Iv, XX }, ++ { "push{T|}", ds, XX, XX }, ++ { "pop{T|}", ds, XX, XX }, ++ /* 20 */ ++ { "andB", Eb, Gb, XX }, ++ { "andS", Ev, Gv, XX }, ++ { "andB", Gb, Eb, XX }, ++ { "andS", Gv, Ev, XX }, ++ { "andB", AL, Ib, XX }, ++ { "andS", eAX, Iv, XX }, ++ { "(bad)", XX, XX, XX }, /* SEG ES prefix */ ++ { "daa{|}", XX, XX, XX }, ++ /* 28 */ ++ { "subB", Eb, Gb, XX }, ++ { "subS", Ev, Gv, XX }, ++ { "subB", Gb, Eb, XX }, ++ { "subS", Gv, Ev, XX }, ++ { "subB", AL, Ib, XX }, ++ { "subS", eAX, Iv, XX }, ++ { "(bad)", XX, XX, XX }, /* SEG CS prefix */ ++ { "das{|}", XX, XX, XX }, ++ /* 30 */ ++ { "xorB", Eb, Gb, XX }, ++ { "xorS", Ev, Gv, XX }, ++ { "xorB", Gb, Eb, XX }, ++ { "xorS", Gv, Ev, XX }, ++ { "xorB", AL, Ib, XX }, ++ { "xorS", eAX, Iv, XX }, ++ { "(bad)", XX, XX, XX }, /* SEG SS prefix */ ++ { "aaa{|}", XX, XX, XX }, ++ /* 38 */ ++ { "cmpB", Eb, Gb, XX }, ++ { "cmpS", Ev, Gv, XX }, ++ { "cmpB", Gb, Eb, XX }, ++ { "cmpS", Gv, Ev, XX }, ++ { "cmpB", AL, Ib, XX }, ++ { "cmpS", eAX, Iv, XX }, ++ { "(bad)", XX, XX, XX }, /* SEG DS prefix */ ++ { "aas{|}", XX, XX, XX }, ++ /* 40 */ ++ { "inc{S|}", RMeAX, XX, XX }, ++ { "inc{S|}", RMeCX, XX, XX }, ++ { "inc{S|}", RMeDX, XX, XX }, ++ { "inc{S|}", RMeBX, XX, XX }, ++ { "inc{S|}", RMeSP, XX, XX }, ++ { "inc{S|}", RMeBP, XX, XX }, ++ { "inc{S|}", RMeSI, XX, XX }, ++ { "inc{S|}", RMeDI, XX, XX }, ++ /* 48 */ ++ { "dec{S|}", RMeAX, XX, XX }, ++ { "dec{S|}", RMeCX, XX, XX }, ++ { "dec{S|}", RMeDX, XX, XX }, ++ { "dec{S|}", RMeBX, XX, XX }, ++ { "dec{S|}", RMeSP, XX, XX }, ++ { "dec{S|}", RMeBP, XX, XX }, ++ { "dec{S|}", RMeSI, XX, XX }, ++ { "dec{S|}", RMeDI, XX, XX }, ++ /* 50 */ ++ { "pushS", RMrAX, XX, XX }, ++ { "pushS", RMrCX, XX, XX }, ++ { "pushS", RMrDX, XX, XX }, ++ { "pushS", RMrBX, XX, XX }, ++ { "pushS", RMrSP, XX, XX }, ++ { "pushS", RMrBP, XX, XX }, ++ { "pushS", RMrSI, XX, XX }, ++ { "pushS", RMrDI, XX, XX }, ++ /* 58 */ ++ { "popS", RMrAX, XX, XX }, ++ { "popS", RMrCX, XX, XX }, ++ { "popS", RMrDX, XX, XX }, ++ { "popS", RMrBX, XX, XX }, ++ { "popS", RMrSP, XX, XX }, ++ { "popS", RMrBP, XX, XX }, ++ { "popS", RMrSI, XX, XX }, ++ { "popS", RMrDI, XX, XX }, ++ /* 60 */ ++ { "pusha{P|}", XX, XX, XX }, ++ { "popa{P|}", XX, XX, XX }, ++ { "bound{S|}", Gv, Ma, XX }, ++ { X86_64_0 }, ++ { "(bad)", XX, XX, XX }, /* seg fs */ ++ { "(bad)", XX, XX, XX }, /* seg gs */ ++ { "(bad)", XX, XX, XX }, /* op size prefix */ ++ { "(bad)", XX, XX, XX }, /* adr size prefix */ ++ /* 68 */ ++ { "pushT", Iq, XX, XX }, ++ { "imulS", Gv, Ev, Iv }, ++ { "pushT", sIb, XX, XX }, ++ { "imulS", Gv, Ev, sIb }, ++ { "ins{b||b|}", Yb, indirDX, XX }, ++ { "ins{R||R|}", Yv, indirDX, XX }, ++ { "outs{b||b|}", indirDX, Xb, XX }, ++ { "outs{R||R|}", indirDX, Xv, XX }, ++ /* 70 */ ++ { "joH", Jb, XX, cond_jump_flag }, ++ { "jnoH", Jb, XX, cond_jump_flag }, ++ { "jbH", Jb, XX, cond_jump_flag }, ++ { "jaeH", Jb, XX, cond_jump_flag }, ++ { "jeH", Jb, XX, cond_jump_flag }, ++ { "jneH", Jb, XX, cond_jump_flag }, ++ { "jbeH", Jb, XX, cond_jump_flag }, ++ { "jaH", Jb, XX, cond_jump_flag }, ++ /* 78 */ ++ { "jsH", Jb, XX, cond_jump_flag }, ++ { "jnsH", Jb, XX, cond_jump_flag }, ++ { "jpH", Jb, XX, cond_jump_flag }, ++ { "jnpH", Jb, XX, cond_jump_flag }, ++ { "jlH", Jb, XX, cond_jump_flag }, ++ { "jgeH", Jb, XX, cond_jump_flag }, ++ { "jleH", Jb, XX, cond_jump_flag }, ++ { "jgH", Jb, XX, cond_jump_flag }, ++ /* 80 */ ++ { GRP1b }, ++ { GRP1S }, ++ { "(bad)", XX, XX, XX }, ++ { GRP1Ss }, ++ { "testB", Eb, Gb, XX }, ++ { "testS", Ev, Gv, XX }, ++ { "xchgB", Eb, Gb, XX }, ++ { "xchgS", Ev, Gv, XX }, ++ /* 88 */ ++ { "movB", Eb, Gb, XX }, ++ { "movS", Ev, Gv, XX }, ++ { "movB", Gb, Eb, XX }, ++ { "movS", Gv, Ev, XX }, ++ { "movQ", Sv, Sw, XX }, ++ { "leaS", Gv, M, XX }, ++ { "movQ", Sw, Sv, XX }, ++ { "popU", Ev, XX, XX }, ++ /* 90 */ ++ { "nop", NOP_Fixup, 0, XX, XX }, ++ { "xchgS", RMeCX, eAX, XX }, ++ { "xchgS", RMeDX, eAX, XX }, ++ { "xchgS", RMeBX, eAX, XX }, ++ { "xchgS", RMeSP, eAX, XX }, ++ { "xchgS", RMeBP, eAX, XX }, ++ { "xchgS", RMeSI, eAX, XX }, ++ { "xchgS", RMeDI, eAX, XX }, ++ /* 98 */ ++ { "cW{tR||tR|}", XX, XX, XX }, ++ { "cR{tO||tO|}", XX, XX, XX }, ++ { "Jcall{T|}", Ap, XX, XX }, ++ { "(bad)", XX, XX, XX }, /* fwait */ ++ { "pushfT", XX, XX, XX }, ++ { "popfT", XX, XX, XX }, ++ { "sahf{|}", XX, XX, XX }, ++ { "lahf{|}", XX, XX, XX }, ++ /* a0 */ ++ { "movB", AL, Ob64, XX }, ++ { "movS", eAX, Ov64, XX }, ++ { "movB", Ob64, AL, XX }, ++ { "movS", Ov64, eAX, XX }, ++ { "movs{b||b|}", Yb, Xb, XX }, ++ { "movs{R||R|}", Yv, Xv, XX }, ++ { "cmps{b||b|}", Xb, Yb, XX }, ++ { "cmps{R||R|}", Xv, Yv, XX }, ++ /* a8 */ ++ { "testB", AL, Ib, XX }, ++ { "testS", eAX, Iv, XX }, ++ { "stosB", Yb, AL, XX }, ++ { "stosS", Yv, eAX, XX }, ++ { "lodsB", AL, Xb, XX }, ++ { "lodsS", eAX, Xv, XX }, ++ { "scasB", AL, Yb, XX }, ++ { "scasS", eAX, Yv, XX }, ++ /* b0 */ ++ { "movB", RMAL, Ib, XX }, ++ { "movB", RMCL, Ib, XX }, ++ { "movB", RMDL, Ib, XX }, ++ { "movB", RMBL, Ib, XX }, ++ { "movB", RMAH, Ib, XX }, ++ { "movB", RMCH, Ib, XX }, ++ { "movB", RMDH, Ib, XX }, ++ { "movB", RMBH, Ib, XX }, ++ /* b8 */ ++ { "movS", RMeAX, Iv64, XX }, ++ { "movS", RMeCX, Iv64, XX }, ++ { "movS", RMeDX, Iv64, XX }, ++ { "movS", RMeBX, Iv64, XX }, ++ { "movS", RMeSP, Iv64, XX }, ++ { "movS", RMeBP, Iv64, XX }, ++ { "movS", RMeSI, Iv64, XX }, ++ { "movS", RMeDI, Iv64, XX }, ++ /* c0 */ ++ { GRP2b }, ++ { GRP2S }, ++ { "retT", Iw, XX, XX }, ++ { "retT", XX, XX, XX }, ++ { "les{S|}", Gv, Mp, XX }, ++ { "ldsS", Gv, Mp, XX }, ++ { "movA", Eb, Ib, XX }, ++ { "movQ", Ev, Iv, XX }, ++ /* c8 */ ++ { "enterT", Iw, Ib, XX }, ++ { "leaveT", XX, XX, XX }, ++ { "lretP", Iw, XX, XX }, ++ { "lretP", XX, XX, XX }, ++ { "int3", XX, XX, XX }, ++ { "int", Ib, XX, XX }, ++ { "into{|}", XX, XX, XX }, ++ { "iretP", XX, XX, XX }, ++ /* d0 */ ++ { GRP2b_one }, ++ { GRP2S_one }, ++ { GRP2b_cl }, ++ { GRP2S_cl }, ++ { "aam{|}", sIb, XX, XX }, ++ { "aad{|}", sIb, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "xlat", DSBX, XX, XX }, ++ /* d8 */ ++ { FLOAT }, ++ { FLOAT }, ++ { FLOAT }, ++ { FLOAT }, ++ { FLOAT }, ++ { FLOAT }, ++ { FLOAT }, ++ { FLOAT }, ++ /* e0 */ ++ { "loopneFH", Jb, XX, loop_jcxz_flag }, ++ { "loopeFH", Jb, XX, loop_jcxz_flag }, ++ { "loopFH", Jb, XX, loop_jcxz_flag }, ++ { "jEcxzH", Jb, XX, loop_jcxz_flag }, ++ { "inB", AL, Ib, XX }, ++ { "inS", eAX, Ib, XX }, ++ { "outB", Ib, AL, XX }, ++ { "outS", Ib, eAX, XX }, ++ /* e8 */ ++ { "callT", Jv, XX, XX }, ++ { "jmpT", Jv, XX, XX }, ++ { "Jjmp{T|}", Ap, XX, XX }, ++ { "jmp", Jb, XX, XX }, ++ { "inB", AL, indirDX, XX }, ++ { "inS", eAX, indirDX, XX }, ++ { "outB", indirDX, AL, XX }, ++ { "outS", indirDX, eAX, XX }, ++ /* f0 */ ++ { "(bad)", XX, XX, XX }, /* lock prefix */ ++ { "icebp", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, /* repne */ ++ { "(bad)", XX, XX, XX }, /* repz */ ++ { "hlt", XX, XX, XX }, ++ { "cmc", XX, XX, XX }, ++ { GRP3b }, ++ { GRP3S }, ++ /* f8 */ ++ { "clc", XX, XX, XX }, ++ { "stc", XX, XX, XX }, ++ { "cli", XX, XX, XX }, ++ { "sti", XX, XX, XX }, ++ { "cld", XX, XX, XX }, ++ { "std", XX, XX, XX }, ++ { GRP4 }, ++ { GRP5 }, ++}; ++ ++static const struct dis386 dis386_twobyte[] = { ++ /* 00 */ ++ { GRP6 }, ++ { GRP7 }, ++ { "larS", Gv, Ew, XX }, ++ { "lslS", Gv, Ew, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "syscall", XX, XX, XX }, ++ { "clts", XX, XX, XX }, ++ { "sysretP", XX, XX, XX }, ++ /* 08 */ ++ { "invd", XX, XX, XX }, ++ { "wbinvd", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "ud2a", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { GRPAMD }, ++ { "femms", XX, XX, XX }, ++ { "", MX, EM, OPSUF }, /* See OP_3DNowSuffix. */ ++ /* 10 */ ++ { PREGRP8 }, ++ { PREGRP9 }, ++ { PREGRP30 }, ++ { "movlpX", EX, XM, SIMD_Fixup, 'h' }, ++ { "unpcklpX", XM, EX, XX }, ++ { "unpckhpX", XM, EX, XX }, ++ { PREGRP31 }, ++ { "movhpX", EX, XM, SIMD_Fixup, 'l' }, ++ /* 18 */ ++ { GRP14 }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ /* 20 */ ++ { "movL", Rm, Cm, XX }, ++ { "movL", Rm, Dm, XX }, ++ { "movL", Cm, Rm, XX }, ++ { "movL", Dm, Rm, XX }, ++ { "movL", Rd, Td, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "movL", Td, Rd, XX }, ++ { "(bad)", XX, XX, XX }, ++ /* 28 */ ++ { "movapX", XM, EX, XX }, ++ { "movapX", EX, XM, XX }, ++ { PREGRP2 }, ++ { "movntpX", Ev, XM, XX }, ++ { PREGRP4 }, ++ { PREGRP3 }, ++ { "ucomisX", XM,EX, XX }, ++ { "comisX", XM,EX, XX }, ++ /* 30 */ ++ { "wrmsr", XX, XX, XX }, ++ { "rdtsc", XX, XX, XX }, ++ { "rdmsr", XX, XX, XX }, ++ { "rdpmc", XX, XX, XX }, ++ { "sysenter", XX, XX, XX }, ++ { "sysexit", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ /* 38 */ ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ /* 40 */ ++ { "cmovo", Gv, Ev, XX }, ++ { "cmovno", Gv, Ev, XX }, ++ { "cmovb", Gv, Ev, XX }, ++ { "cmovae", Gv, Ev, XX }, ++ { "cmove", Gv, Ev, XX }, ++ { "cmovne", Gv, Ev, XX }, ++ { "cmovbe", Gv, Ev, XX }, ++ { "cmova", Gv, Ev, XX }, ++ /* 48 */ ++ { "cmovs", Gv, Ev, XX }, ++ { "cmovns", Gv, Ev, XX }, ++ { "cmovp", Gv, Ev, XX }, ++ { "cmovnp", Gv, Ev, XX }, ++ { "cmovl", Gv, Ev, XX }, ++ { "cmovge", Gv, Ev, XX }, ++ { "cmovle", Gv, Ev, XX }, ++ { "cmovg", Gv, Ev, XX }, ++ /* 50 */ ++ { "movmskpX", Gdq, XS, XX }, ++ { PREGRP13 }, ++ { PREGRP12 }, ++ { PREGRP11 }, ++ { "andpX", XM, EX, XX }, ++ { "andnpX", XM, EX, XX }, ++ { "orpX", XM, EX, XX }, ++ { "xorpX", XM, EX, XX }, ++ /* 58 */ ++ { PREGRP0 }, ++ { PREGRP10 }, ++ { PREGRP17 }, ++ { PREGRP16 }, ++ { PREGRP14 }, ++ { PREGRP7 }, ++ { PREGRP5 }, ++ { PREGRP6 }, ++ /* 60 */ ++ { "punpcklbw", MX, EM, XX }, ++ { "punpcklwd", MX, EM, XX }, ++ { "punpckldq", MX, EM, XX }, ++ { "packsswb", MX, EM, XX }, ++ { "pcmpgtb", MX, EM, XX }, ++ { "pcmpgtw", MX, EM, XX }, ++ { "pcmpgtd", MX, EM, XX }, ++ { "packuswb", MX, EM, XX }, ++ /* 68 */ ++ { "punpckhbw", MX, EM, XX }, ++ { "punpckhwd", MX, EM, XX }, ++ { "punpckhdq", MX, EM, XX }, ++ { "packssdw", MX, EM, XX }, ++ { PREGRP26 }, ++ { PREGRP24 }, ++ { "movd", MX, Edq, XX }, ++ { PREGRP19 }, ++ /* 70 */ ++ { PREGRP22 }, ++ { GRP10 }, ++ { GRP11 }, ++ { GRP12 }, ++ { "pcmpeqb", MX, EM, XX }, ++ { "pcmpeqw", MX, EM, XX }, ++ { "pcmpeqd", MX, EM, XX }, ++ { "emms", XX, XX, XX }, ++ /* 78 */ ++ { "vmread", Em, Gm, XX }, ++ { "vmwrite", Gm, Em, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { PREGRP28 }, ++ { PREGRP29 }, ++ { PREGRP23 }, ++ { PREGRP20 }, ++ /* 80 */ ++ { "joH", Jv, XX, cond_jump_flag }, ++ { "jnoH", Jv, XX, cond_jump_flag }, ++ { "jbH", Jv, XX, cond_jump_flag }, ++ { "jaeH", Jv, XX, cond_jump_flag }, ++ { "jeH", Jv, XX, cond_jump_flag }, ++ { "jneH", Jv, XX, cond_jump_flag }, ++ { "jbeH", Jv, XX, cond_jump_flag }, ++ { "jaH", Jv, XX, cond_jump_flag }, ++ /* 88 */ ++ { "jsH", Jv, XX, cond_jump_flag }, ++ { "jnsH", Jv, XX, cond_jump_flag }, ++ { "jpH", Jv, XX, cond_jump_flag }, ++ { "jnpH", Jv, XX, cond_jump_flag }, ++ { "jlH", Jv, XX, cond_jump_flag }, ++ { "jgeH", Jv, XX, cond_jump_flag }, ++ { "jleH", Jv, XX, cond_jump_flag }, ++ { "jgH", Jv, XX, cond_jump_flag }, ++ /* 90 */ ++ { "seto", Eb, XX, XX }, ++ { "setno", Eb, XX, XX }, ++ { "setb", Eb, XX, XX }, ++ { "setae", Eb, XX, XX }, ++ { "sete", Eb, XX, XX }, ++ { "setne", Eb, XX, XX }, ++ { "setbe", Eb, XX, XX }, ++ { "seta", Eb, XX, XX }, ++ /* 98 */ ++ { "sets", Eb, XX, XX }, ++ { "setns", Eb, XX, XX }, ++ { "setp", Eb, XX, XX }, ++ { "setnp", Eb, XX, XX }, ++ { "setl", Eb, XX, XX }, ++ { "setge", Eb, XX, XX }, ++ { "setle", Eb, XX, XX }, ++ { "setg", Eb, XX, XX }, ++ /* a0 */ ++ { "pushT", fs, XX, XX }, ++ { "popT", fs, XX, XX }, ++ { "cpuid", XX, XX, XX }, ++ { "btS", Ev, Gv, XX }, ++ { "shldS", Ev, Gv, Ib }, ++ { "shldS", Ev, Gv, CL }, ++ { GRPPADLCK2 }, ++ { GRPPADLCK1 }, ++ /* a8 */ ++ { "pushT", gs, XX, XX }, ++ { "popT", gs, XX, XX }, ++ { "rsm", XX, XX, XX }, ++ { "btsS", Ev, Gv, XX }, ++ { "shrdS", Ev, Gv, Ib }, ++ { "shrdS", Ev, Gv, CL }, ++ { GRP13 }, ++ { "imulS", Gv, Ev, XX }, ++ /* b0 */ ++ { "cmpxchgB", Eb, Gb, XX }, ++ { "cmpxchgS", Ev, Gv, XX }, ++ { "lssS", Gv, Mp, XX }, ++ { "btrS", Ev, Gv, XX }, ++ { "lfsS", Gv, Mp, XX }, ++ { "lgsS", Gv, Mp, XX }, ++ { "movz{bR|x|bR|x}", Gv, Eb, XX }, ++ { "movz{wR|x|wR|x}", Gv, Ew, XX }, /* yes, there really is movzww ! */ ++ /* b8 */ ++ { "(bad)", XX, XX, XX }, ++ { "ud2b", XX, XX, XX }, ++ { GRP8 }, ++ { "btcS", Ev, Gv, XX }, ++ { "bsfS", Gv, Ev, XX }, ++ { "bsrS", Gv, Ev, XX }, ++ { "movs{bR|x|bR|x}", Gv, Eb, XX }, ++ { "movs{wR|x|wR|x}", Gv, Ew, XX }, /* yes, there really is movsww ! */ ++ /* c0 */ ++ { "xaddB", Eb, Gb, XX }, ++ { "xaddS", Ev, Gv, XX }, ++ { PREGRP1 }, ++ { "movntiS", Ev, Gv, XX }, ++ { "pinsrw", MX, Edqw, Ib }, ++ { "pextrw", Gdq, MS, Ib }, ++ { "shufpX", XM, EX, Ib }, ++ { GRP9 }, ++ /* c8 */ ++ { "bswap", RMeAX, XX, XX }, ++ { "bswap", RMeCX, XX, XX }, ++ { "bswap", RMeDX, XX, XX }, ++ { "bswap", RMeBX, XX, XX }, ++ { "bswap", RMeSP, XX, XX }, ++ { "bswap", RMeBP, XX, XX }, ++ { "bswap", RMeSI, XX, XX }, ++ { "bswap", RMeDI, XX, XX }, ++ /* d0 */ ++ { PREGRP27 }, ++ { "psrlw", MX, EM, XX }, ++ { "psrld", MX, EM, XX }, ++ { "psrlq", MX, EM, XX }, ++ { "paddq", MX, EM, XX }, ++ { "pmullw", MX, EM, XX }, ++ { PREGRP21 }, ++ { "pmovmskb", Gdq, MS, XX }, ++ /* d8 */ ++ { "psubusb", MX, EM, XX }, ++ { "psubusw", MX, EM, XX }, ++ { "pminub", MX, EM, XX }, ++ { "pand", MX, EM, XX }, ++ { "paddusb", MX, EM, XX }, ++ { "paddusw", MX, EM, XX }, ++ { "pmaxub", MX, EM, XX }, ++ { "pandn", MX, EM, XX }, ++ /* e0 */ ++ { "pavgb", MX, EM, XX }, ++ { "psraw", MX, EM, XX }, ++ { "psrad", MX, EM, XX }, ++ { "pavgw", MX, EM, XX }, ++ { "pmulhuw", MX, EM, XX }, ++ { "pmulhw", MX, EM, XX }, ++ { PREGRP15 }, ++ { PREGRP25 }, ++ /* e8 */ ++ { "psubsb", MX, EM, XX }, ++ { "psubsw", MX, EM, XX }, ++ { "pminsw", MX, EM, XX }, ++ { "por", MX, EM, XX }, ++ { "paddsb", MX, EM, XX }, ++ { "paddsw", MX, EM, XX }, ++ { "pmaxsw", MX, EM, XX }, ++ { "pxor", MX, EM, XX }, ++ /* f0 */ ++ { PREGRP32 }, ++ { "psllw", MX, EM, XX }, ++ { "pslld", MX, EM, XX }, ++ { "psllq", MX, EM, XX }, ++ { "pmuludq", MX, EM, XX }, ++ { "pmaddwd", MX, EM, XX }, ++ { "psadbw", MX, EM, XX }, ++ { PREGRP18 }, ++ /* f8 */ ++ { "psubb", MX, EM, XX }, ++ { "psubw", MX, EM, XX }, ++ { "psubd", MX, EM, XX }, ++ { "psubq", MX, EM, XX }, ++ { "paddb", MX, EM, XX }, ++ { "paddw", MX, EM, XX }, ++ { "paddd", MX, EM, XX }, ++ { "(bad)", XX, XX, XX } ++}; ++ ++static const unsigned char onebyte_has_modrm[256] = { ++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++ /* ------------------------------- */ ++ /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 00 */ ++ /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */ ++ /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */ ++ /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */ ++ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */ ++ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */ ++ /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 60 */ ++ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */ ++ /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 80 */ ++ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */ ++ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */ ++ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */ ++ /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* c0 */ ++ /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* d0 */ ++ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0 */ ++ /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 /* f0 */ ++ /* ------------------------------- */ ++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++}; ++ ++static const unsigned char twobyte_has_modrm[256] = { ++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++ /* ------------------------------- */ ++ /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1, /* 0f */ ++ /* 10 */ 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, /* 1f */ ++ /* 20 */ 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1, /* 2f */ ++ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ ++ /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */ ++ /* 50 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 5f */ ++ /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6f */ ++ /* 70 */ 1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1, /* 7f */ ++ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ ++ /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */ ++ /* a0 */ 0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1, /* af */ ++ /* b0 */ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1, /* bf */ ++ /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */ ++ /* d0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* df */ ++ /* e0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* ef */ ++ /* f0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 /* ff */ ++ /* ------------------------------- */ ++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++}; ++ ++static const unsigned char twobyte_uses_SSE_prefix[256] = { ++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++ /* ------------------------------- */ ++ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ ++ /* 10 */ 1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0, /* 1f */ ++ /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0, /* 2f */ ++ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ ++ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ ++ /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */ ++ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, /* 6f */ ++ /* 70 */ 1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1, /* 7f */ ++ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ ++ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ ++ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ ++ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ ++ /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ ++ /* d0 */ 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */ ++ /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */ ++ /* f0 */ 1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0 /* ff */ ++ /* ------------------------------- */ ++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++}; ++ ++static char obuf[100]; ++static char *obufp; ++static char scratchbuf[100]; ++static unsigned char *start_codep; ++static unsigned char *insn_codep; ++static unsigned char *codep; ++static disassemble_info *the_info; ++static int mod; ++static int rm; ++static int reg; ++static unsigned char need_modrm; ++ ++/* If we are accessing mod/rm/reg without need_modrm set, then the ++ values are stale. Hitting this abort likely indicates that you ++ need to update onebyte_has_modrm or twobyte_has_modrm. */ ++#define MODRM_CHECK if (!need_modrm) abort () ++ ++static const char **names64; ++static const char **names32; ++static const char **names16; ++static const char **names8; ++static const char **names8rex; ++static const char **names_seg; ++static const char **index16; ++ ++static const char *intel_names64[] = { ++ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", ++ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" ++}; ++static const char *intel_names32[] = { ++ "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", ++ "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" ++}; ++static const char *intel_names16[] = { ++ "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", ++ "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" ++}; ++static const char *intel_names8[] = { ++ "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", ++}; ++static const char *intel_names8rex[] = { ++ "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", ++ "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" ++}; ++static const char *intel_names_seg[] = { ++ "es", "cs", "ss", "ds", "fs", "gs", "?", "?", ++}; ++static const char *intel_index16[] = { ++ "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx" ++}; ++ ++static const char *att_names64[] = { ++ "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", ++ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" ++}; ++static const char *att_names32[] = { ++ "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", ++ "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" ++}; ++static const char *att_names16[] = { ++ "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di", ++ "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" ++}; ++static const char *att_names8[] = { ++ "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh", ++}; ++static const char *att_names8rex[] = { ++ "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil", ++ "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" ++}; ++static const char *att_names_seg[] = { ++ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "%?", "%?", ++}; ++static const char *att_index16[] = { ++ "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx" ++}; ++ ++static const struct dis386 grps[][8] = { ++ /* GRP1b */ ++ { ++ { "addA", Eb, Ib, XX }, ++ { "orA", Eb, Ib, XX }, ++ { "adcA", Eb, Ib, XX }, ++ { "sbbA", Eb, Ib, XX }, ++ { "andA", Eb, Ib, XX }, ++ { "subA", Eb, Ib, XX }, ++ { "xorA", Eb, Ib, XX }, ++ { "cmpA", Eb, Ib, XX } ++ }, ++ /* GRP1S */ ++ { ++ { "addQ", Ev, Iv, XX }, ++ { "orQ", Ev, Iv, XX }, ++ { "adcQ", Ev, Iv, XX }, ++ { "sbbQ", Ev, Iv, XX }, ++ { "andQ", Ev, Iv, XX }, ++ { "subQ", Ev, Iv, XX }, ++ { "xorQ", Ev, Iv, XX }, ++ { "cmpQ", Ev, Iv, XX } ++ }, ++ /* GRP1Ss */ ++ { ++ { "addQ", Ev, sIb, XX }, ++ { "orQ", Ev, sIb, XX }, ++ { "adcQ", Ev, sIb, XX }, ++ { "sbbQ", Ev, sIb, XX }, ++ { "andQ", Ev, sIb, XX }, ++ { "subQ", Ev, sIb, XX }, ++ { "xorQ", Ev, sIb, XX }, ++ { "cmpQ", Ev, sIb, XX } ++ }, ++ /* GRP2b */ ++ { ++ { "rolA", Eb, Ib, XX }, ++ { "rorA", Eb, Ib, XX }, ++ { "rclA", Eb, Ib, XX }, ++ { "rcrA", Eb, Ib, XX }, ++ { "shlA", Eb, Ib, XX }, ++ { "shrA", Eb, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "sarA", Eb, Ib, XX }, ++ }, ++ /* GRP2S */ ++ { ++ { "rolQ", Ev, Ib, XX }, ++ { "rorQ", Ev, Ib, XX }, ++ { "rclQ", Ev, Ib, XX }, ++ { "rcrQ", Ev, Ib, XX }, ++ { "shlQ", Ev, Ib, XX }, ++ { "shrQ", Ev, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "sarQ", Ev, Ib, XX }, ++ }, ++ /* GRP2b_one */ ++ { ++ { "rolA", Eb, I1, XX }, ++ { "rorA", Eb, I1, XX }, ++ { "rclA", Eb, I1, XX }, ++ { "rcrA", Eb, I1, XX }, ++ { "shlA", Eb, I1, XX }, ++ { "shrA", Eb, I1, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "sarA", Eb, I1, XX }, ++ }, ++ /* GRP2S_one */ ++ { ++ { "rolQ", Ev, I1, XX }, ++ { "rorQ", Ev, I1, XX }, ++ { "rclQ", Ev, I1, XX }, ++ { "rcrQ", Ev, I1, XX }, ++ { "shlQ", Ev, I1, XX }, ++ { "shrQ", Ev, I1, XX }, ++ { "(bad)", XX, XX, XX}, ++ { "sarQ", Ev, I1, XX }, ++ }, ++ /* GRP2b_cl */ ++ { ++ { "rolA", Eb, CL, XX }, ++ { "rorA", Eb, CL, XX }, ++ { "rclA", Eb, CL, XX }, ++ { "rcrA", Eb, CL, XX }, ++ { "shlA", Eb, CL, XX }, ++ { "shrA", Eb, CL, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "sarA", Eb, CL, XX }, ++ }, ++ /* GRP2S_cl */ ++ { ++ { "rolQ", Ev, CL, XX }, ++ { "rorQ", Ev, CL, XX }, ++ { "rclQ", Ev, CL, XX }, ++ { "rcrQ", Ev, CL, XX }, ++ { "shlQ", Ev, CL, XX }, ++ { "shrQ", Ev, CL, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "sarQ", Ev, CL, XX } ++ }, ++ /* GRP3b */ ++ { ++ { "testA", Eb, Ib, XX }, ++ { "(bad)", Eb, XX, XX }, ++ { "notA", Eb, XX, XX }, ++ { "negA", Eb, XX, XX }, ++ { "mulA", Eb, XX, XX }, /* Don't print the implicit %al register, */ ++ { "imulA", Eb, XX, XX }, /* to distinguish these opcodes from other */ ++ { "divA", Eb, XX, XX }, /* mul/imul opcodes. Do the same for div */ ++ { "idivA", Eb, XX, XX } /* and idiv for consistency. */ ++ }, ++ /* GRP3S */ ++ { ++ { "testQ", Ev, Iv, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "notQ", Ev, XX, XX }, ++ { "negQ", Ev, XX, XX }, ++ { "mulQ", Ev, XX, XX }, /* Don't print the implicit register. */ ++ { "imulQ", Ev, XX, XX }, ++ { "divQ", Ev, XX, XX }, ++ { "idivQ", Ev, XX, XX }, ++ }, ++ /* GRP4 */ ++ { ++ { "incA", Eb, XX, XX }, ++ { "decA", Eb, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* GRP5 */ ++ { ++ { "incQ", Ev, XX, XX }, ++ { "decQ", Ev, XX, XX }, ++ { "callT", indirEv, XX, XX }, ++ { "JcallT", indirEp, XX, XX }, ++ { "jmpT", indirEv, XX, XX }, ++ { "JjmpT", indirEp, XX, XX }, ++ { "pushU", Ev, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* GRP6 */ ++ { ++ { "sldtQ", Ev, XX, XX }, ++ { "strQ", Ev, XX, XX }, ++ { "lldt", Ew, XX, XX }, ++ { "ltr", Ew, XX, XX }, ++ { "verr", Ew, XX, XX }, ++ { "verw", Ew, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX } ++ }, ++ /* GRP7 */ ++ { ++ { "sgdtIQ", VMX_Fixup, 0, XX, XX }, ++ { "sidtIQ", PNI_Fixup, 0, XX, XX }, ++ { "lgdt{Q|Q||}", M, XX, XX }, ++ { "lidt{Q|Q||}", SVME_Fixup, 0, XX, XX }, ++ { "smswQ", Ev, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "lmsw", Ew, XX, XX }, ++ { "invlpg", INVLPG_Fixup, w_mode, XX, XX }, ++ }, ++ /* GRP8 */ ++ { ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "btQ", Ev, Ib, XX }, ++ { "btsQ", Ev, Ib, XX }, ++ { "btrQ", Ev, Ib, XX }, ++ { "btcQ", Ev, Ib, XX }, ++ }, ++ /* GRP9 */ ++ { ++ { "(bad)", XX, XX, XX }, ++ { "cmpxchg8b", Eq, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "", VM, XX, XX }, /* See OP_VMX. */ ++ { "vmptrst", Eq, XX, XX }, ++ }, ++ /* GRP10 */ ++ { ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "psrlw", MS, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "psraw", MS, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "psllw", MS, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* GRP11 */ ++ { ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "psrld", MS, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "psrad", MS, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "pslld", MS, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* GRP12 */ ++ { ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "psrlq", MS, Ib, XX }, ++ { "psrldq", MS, Ib, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "psllq", MS, Ib, XX }, ++ { "pslldq", MS, Ib, XX }, ++ }, ++ /* GRP13 */ ++ { ++ { "fxsave", Ev, XX, XX }, ++ { "fxrstor", Ev, XX, XX }, ++ { "ldmxcsr", Ev, XX, XX }, ++ { "stmxcsr", Ev, XX, XX }, ++ { "xsave", Ev, XX, XX }, ++ { "xrstor", OP_0fae, 0, XX, XX }, ++ { "mfence", OP_0fae, 0, XX, XX }, ++ { "clflush", OP_0fae, 0, XX, XX }, ++ }, ++ /* GRP14 */ ++ { ++ { "prefetchnta", Ev, XX, XX }, ++ { "prefetcht0", Ev, XX, XX }, ++ { "prefetcht1", Ev, XX, XX }, ++ { "prefetcht2", Ev, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* GRPAMD */ ++ { ++ { "prefetch", Eb, XX, XX }, ++ { "prefetchw", Eb, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* GRPPADLCK1 */ ++ { ++ { "xstore-rng", OP_0f07, 0, XX, XX }, ++ { "xcrypt-ecb", OP_0f07, 0, XX, XX }, ++ { "xcrypt-cbc", OP_0f07, 0, XX, XX }, ++ { "xcrypt-ctr", OP_0f07, 0, XX, XX }, ++ { "xcrypt-cfb", OP_0f07, 0, XX, XX }, ++ { "xcrypt-ofb", OP_0f07, 0, XX, XX }, ++ { "(bad)", OP_0f07, 0, XX, XX }, ++ { "(bad)", OP_0f07, 0, XX, XX }, ++ }, ++ /* GRPPADLCK2 */ ++ { ++ { "montmul", OP_0f07, 0, XX, XX }, ++ { "xsha1", OP_0f07, 0, XX, XX }, ++ { "xsha256", OP_0f07, 0, XX, XX }, ++ { "(bad)", OP_0f07, 0, XX, XX }, ++ { "(bad)", OP_0f07, 0, XX, XX }, ++ { "(bad)", OP_0f07, 0, XX, XX }, ++ { "(bad)", OP_0f07, 0, XX, XX }, ++ { "(bad)", OP_0f07, 0, XX, XX }, ++ } ++}; ++ ++static const struct dis386 prefix_user_table[][4] = { ++ /* PREGRP0 */ ++ { ++ { "addps", XM, EX, XX }, ++ { "addss", XM, EX, XX }, ++ { "addpd", XM, EX, XX }, ++ { "addsd", XM, EX, XX }, ++ }, ++ /* PREGRP1 */ ++ { ++ { "", XM, EX, OPSIMD }, /* See OP_SIMD_SUFFIX. */ ++ { "", XM, EX, OPSIMD }, ++ { "", XM, EX, OPSIMD }, ++ { "", XM, EX, OPSIMD }, ++ }, ++ /* PREGRP2 */ ++ { ++ { "cvtpi2ps", XM, EM, XX }, ++ { "cvtsi2ssY", XM, Ev, XX }, ++ { "cvtpi2pd", XM, EM, XX }, ++ { "cvtsi2sdY", XM, Ev, XX }, ++ }, ++ /* PREGRP3 */ ++ { ++ { "cvtps2pi", MX, EX, XX }, ++ { "cvtss2siY", Gv, EX, XX }, ++ { "cvtpd2pi", MX, EX, XX }, ++ { "cvtsd2siY", Gv, EX, XX }, ++ }, ++ /* PREGRP4 */ ++ { ++ { "cvttps2pi", MX, EX, XX }, ++ { "cvttss2siY", Gv, EX, XX }, ++ { "cvttpd2pi", MX, EX, XX }, ++ { "cvttsd2siY", Gv, EX, XX }, ++ }, ++ /* PREGRP5 */ ++ { ++ { "divps", XM, EX, XX }, ++ { "divss", XM, EX, XX }, ++ { "divpd", XM, EX, XX }, ++ { "divsd", XM, EX, XX }, ++ }, ++ /* PREGRP6 */ ++ { ++ { "maxps", XM, EX, XX }, ++ { "maxss", XM, EX, XX }, ++ { "maxpd", XM, EX, XX }, ++ { "maxsd", XM, EX, XX }, ++ }, ++ /* PREGRP7 */ ++ { ++ { "minps", XM, EX, XX }, ++ { "minss", XM, EX, XX }, ++ { "minpd", XM, EX, XX }, ++ { "minsd", XM, EX, XX }, ++ }, ++ /* PREGRP8 */ ++ { ++ { "movups", XM, EX, XX }, ++ { "movss", XM, EX, XX }, ++ { "movupd", XM, EX, XX }, ++ { "movsd", XM, EX, XX }, ++ }, ++ /* PREGRP9 */ ++ { ++ { "movups", EX, XM, XX }, ++ { "movss", EX, XM, XX }, ++ { "movupd", EX, XM, XX }, ++ { "movsd", EX, XM, XX }, ++ }, ++ /* PREGRP10 */ ++ { ++ { "mulps", XM, EX, XX }, ++ { "mulss", XM, EX, XX }, ++ { "mulpd", XM, EX, XX }, ++ { "mulsd", XM, EX, XX }, ++ }, ++ /* PREGRP11 */ ++ { ++ { "rcpps", XM, EX, XX }, ++ { "rcpss", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ }, ++ /* PREGRP12 */ ++ { ++ { "rsqrtps", XM, EX, XX }, ++ { "rsqrtss", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ }, ++ /* PREGRP13 */ ++ { ++ { "sqrtps", XM, EX, XX }, ++ { "sqrtss", XM, EX, XX }, ++ { "sqrtpd", XM, EX, XX }, ++ { "sqrtsd", XM, EX, XX }, ++ }, ++ /* PREGRP14 */ ++ { ++ { "subps", XM, EX, XX }, ++ { "subss", XM, EX, XX }, ++ { "subpd", XM, EX, XX }, ++ { "subsd", XM, EX, XX }, ++ }, ++ /* PREGRP15 */ ++ { ++ { "(bad)", XM, EX, XX }, ++ { "cvtdq2pd", XM, EX, XX }, ++ { "cvttpd2dq", XM, EX, XX }, ++ { "cvtpd2dq", XM, EX, XX }, ++ }, ++ /* PREGRP16 */ ++ { ++ { "cvtdq2ps", XM, EX, XX }, ++ { "cvttps2dq",XM, EX, XX }, ++ { "cvtps2dq",XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ }, ++ /* PREGRP17 */ ++ { ++ { "cvtps2pd", XM, EX, XX }, ++ { "cvtss2sd", XM, EX, XX }, ++ { "cvtpd2ps", XM, EX, XX }, ++ { "cvtsd2ss", XM, EX, XX }, ++ }, ++ /* PREGRP18 */ ++ { ++ { "maskmovq", MX, MS, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "maskmovdqu", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ }, ++ /* PREGRP19 */ ++ { ++ { "movq", MX, EM, XX }, ++ { "movdqu", XM, EX, XX }, ++ { "movdqa", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ }, ++ /* PREGRP20 */ ++ { ++ { "movq", EM, MX, XX }, ++ { "movdqu", EX, XM, XX }, ++ { "movdqa", EX, XM, XX }, ++ { "(bad)", EX, XM, XX }, ++ }, ++ /* PREGRP21 */ ++ { ++ { "(bad)", EX, XM, XX }, ++ { "movq2dq", XM, MS, XX }, ++ { "movq", EX, XM, XX }, ++ { "movdq2q", MX, XS, XX }, ++ }, ++ /* PREGRP22 */ ++ { ++ { "pshufw", MX, EM, Ib }, ++ { "pshufhw", XM, EX, Ib }, ++ { "pshufd", XM, EX, Ib }, ++ { "pshuflw", XM, EX, Ib }, ++ }, ++ /* PREGRP23 */ ++ { ++ { "movd", Edq, MX, XX }, ++ { "movq", XM, EX, XX }, ++ { "movd", Edq, XM, XX }, ++ { "(bad)", Ed, XM, XX }, ++ }, ++ /* PREGRP24 */ ++ { ++ { "(bad)", MX, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "punpckhqdq", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ }, ++ /* PREGRP25 */ ++ { ++ { "movntq", EM, MX, XX }, ++ { "(bad)", EM, XM, XX }, ++ { "movntdq", EM, XM, XX }, ++ { "(bad)", EM, XM, XX }, ++ }, ++ /* PREGRP26 */ ++ { ++ { "(bad)", MX, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "punpcklqdq", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ }, ++ /* PREGRP27 */ ++ { ++ { "(bad)", MX, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "addsubpd", XM, EX, XX }, ++ { "addsubps", XM, EX, XX }, ++ }, ++ /* PREGRP28 */ ++ { ++ { "(bad)", MX, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "haddpd", XM, EX, XX }, ++ { "haddps", XM, EX, XX }, ++ }, ++ /* PREGRP29 */ ++ { ++ { "(bad)", MX, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "hsubpd", XM, EX, XX }, ++ { "hsubps", XM, EX, XX }, ++ }, ++ /* PREGRP30 */ ++ { ++ { "movlpX", XM, EX, SIMD_Fixup, 'h' }, /* really only 2 operands */ ++ { "movsldup", XM, EX, XX }, ++ { "movlpd", XM, EX, XX }, ++ { "movddup", XM, EX, XX }, ++ }, ++ /* PREGRP31 */ ++ { ++ { "movhpX", XM, EX, SIMD_Fixup, 'l' }, ++ { "movshdup", XM, EX, XX }, ++ { "movhpd", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ }, ++ /* PREGRP32 */ ++ { ++ { "(bad)", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "(bad)", XM, EX, XX }, ++ { "lddqu", XM, M, XX }, ++ }, ++}; ++ ++static const struct dis386 x86_64_table[][2] = { ++ { ++ { "arpl", Ew, Gw, XX }, ++ { "movs{||lq|xd}", Gv, Ed, XX }, ++ }, ++}; ++ ++#ifdef __KERNEL__ ++#define INTERNAL_DISASSEMBLER_ERROR "" ++#else /* __KERNEL__ */ ++#define INTERNAL_DISASSEMBLER_ERROR _("") ++#endif /* __KERNEL__ */ ++ ++static void ++ckprefix (void) ++{ ++ int newrex; ++ rex = 0; ++ prefixes = 0; ++ used_prefixes = 0; ++ rex_used = 0; ++ while (1) ++ { ++ FETCH_DATA (the_info, codep + 1); ++ newrex = 0; ++ switch (*codep) ++ { ++ /* REX prefixes family. */ ++ case 0x40: ++ case 0x41: ++ case 0x42: ++ case 0x43: ++ case 0x44: ++ case 0x45: ++ case 0x46: ++ case 0x47: ++ case 0x48: ++ case 0x49: ++ case 0x4a: ++ case 0x4b: ++ case 0x4c: ++ case 0x4d: ++ case 0x4e: ++ case 0x4f: ++ if (mode_64bit) ++ newrex = *codep; ++ else ++ return; ++ break; ++ case 0xf3: ++ prefixes |= PREFIX_REPZ; ++ break; ++ case 0xf2: ++ prefixes |= PREFIX_REPNZ; ++ break; ++ case 0xf0: ++ prefixes |= PREFIX_LOCK; ++ break; ++ case 0x2e: ++ prefixes |= PREFIX_CS; ++ break; ++ case 0x36: ++ prefixes |= PREFIX_SS; ++ break; ++ case 0x3e: ++ prefixes |= PREFIX_DS; ++ break; ++ case 0x26: ++ prefixes |= PREFIX_ES; ++ break; ++ case 0x64: ++ prefixes |= PREFIX_FS; ++ break; ++ case 0x65: ++ prefixes |= PREFIX_GS; ++ break; ++ case 0x66: ++ prefixes |= PREFIX_DATA; ++ break; ++ case 0x67: ++ prefixes |= PREFIX_ADDR; ++ break; ++ case FWAIT_OPCODE: ++ /* fwait is really an instruction. If there are prefixes ++ before the fwait, they belong to the fwait, *not* to the ++ following instruction. */ ++ if (prefixes) ++ { ++ prefixes |= PREFIX_FWAIT; ++ codep++; ++ return; ++ } ++ prefixes = PREFIX_FWAIT; ++ break; ++ default: ++ return; ++ } ++ /* Rex is ignored when followed by another prefix. */ ++ if (rex) ++ { ++ oappend (prefix_name (rex, 0)); ++ oappend (" "); ++ } ++ rex = newrex; ++ codep++; ++ } ++} ++ ++/* Return the name of the prefix byte PREF, or NULL if PREF is not a ++ prefix byte. */ ++ ++static const char * ++prefix_name (int pref, int sizeflag) ++{ ++ switch (pref) ++ { ++ /* REX prefixes family. */ ++ case 0x40: ++ return "rex"; ++ case 0x41: ++ return "rexZ"; ++ case 0x42: ++ return "rexY"; ++ case 0x43: ++ return "rexYZ"; ++ case 0x44: ++ return "rexX"; ++ case 0x45: ++ return "rexXZ"; ++ case 0x46: ++ return "rexXY"; ++ case 0x47: ++ return "rexXYZ"; ++ case 0x48: ++ return "rex64"; ++ case 0x49: ++ return "rex64Z"; ++ case 0x4a: ++ return "rex64Y"; ++ case 0x4b: ++ return "rex64YZ"; ++ case 0x4c: ++ return "rex64X"; ++ case 0x4d: ++ return "rex64XZ"; ++ case 0x4e: ++ return "rex64XY"; ++ case 0x4f: ++ return "rex64XYZ"; ++ case 0xf3: ++ return "repz"; ++ case 0xf2: ++ return "repnz"; ++ case 0xf0: ++ return "lock"; ++ case 0x2e: ++ return "cs"; ++ case 0x36: ++ return "ss"; ++ case 0x3e: ++ return "ds"; ++ case 0x26: ++ return "es"; ++ case 0x64: ++ return "fs"; ++ case 0x65: ++ return "gs"; ++ case 0x66: ++ return (sizeflag & DFLAG) ? "data16" : "data32"; ++ case 0x67: ++ if (mode_64bit) ++ return (sizeflag & AFLAG) ? "addr32" : "addr64"; ++ else ++ return (sizeflag & AFLAG) ? "addr16" : "addr32"; ++ case FWAIT_OPCODE: ++ return "fwait"; ++ default: ++ return NULL; ++ } ++} ++ ++static char op1out[100], op2out[100], op3out[100]; ++static int op_ad, op_index[3]; ++static int two_source_ops; ++static bfd_vma op_address[3]; ++static bfd_vma op_riprel[3]; ++static bfd_vma start_pc; ++ ++/* ++ * On the 386's of 1988, the maximum length of an instruction is 15 bytes. ++ * (see topic "Redundant prefixes" in the "Differences from 8086" ++ * section of the "Virtual 8086 Mode" chapter.) ++ * 'pc' should be the address of this instruction, it will ++ * be used to print the target address if this is a relative jump or call ++ * The function returns the length of this instruction in bytes. ++ */ ++ ++static char intel_syntax; ++static char open_char; ++static char close_char; ++static char separator_char; ++static char scale_char; ++ ++/* Here for backwards compatibility. When gdb stops using ++ print_insn_i386_att and print_insn_i386_intel these functions can ++ disappear, and print_insn_i386 be merged into print_insn. */ ++int ++print_insn_i386_att (bfd_vma pc, disassemble_info *info) ++{ ++ intel_syntax = 0; ++ ++ return print_insn (pc, info); ++} ++ ++int ++print_insn_i386_intel (bfd_vma pc, disassemble_info *info) ++{ ++ intel_syntax = 1; ++ ++ return print_insn (pc, info); ++} ++ ++int ++print_insn_i386 (bfd_vma pc, disassemble_info *info) ++{ ++ intel_syntax = -1; ++ ++ return print_insn (pc, info); ++} ++ ++static int ++print_insn (bfd_vma pc, disassemble_info *info) ++{ ++ const struct dis386 *dp; ++ int i; ++ char *first, *second, *third; ++ int needcomma; ++ unsigned char uses_SSE_prefix, uses_LOCK_prefix; ++ int sizeflag; ++ const char *p; ++ struct dis_private priv; ++ ++ mode_64bit = (info->mach == bfd_mach_x86_64_intel_syntax ++ || info->mach == bfd_mach_x86_64); ++ ++ if (intel_syntax == (char) -1) ++ intel_syntax = (info->mach == bfd_mach_i386_i386_intel_syntax ++ || info->mach == bfd_mach_x86_64_intel_syntax); ++ ++ if (info->mach == bfd_mach_i386_i386 ++ || info->mach == bfd_mach_x86_64 ++ || info->mach == bfd_mach_i386_i386_intel_syntax ++ || info->mach == bfd_mach_x86_64_intel_syntax) ++ priv.orig_sizeflag = AFLAG | DFLAG; ++ else if (info->mach == bfd_mach_i386_i8086) ++ priv.orig_sizeflag = 0; ++ else ++ abort (); ++ ++ for (p = info->disassembler_options; p != NULL; ) ++ { ++ if (strncmp (p, "x86-64", 6) == 0) ++ { ++ mode_64bit = 1; ++ priv.orig_sizeflag = AFLAG | DFLAG; ++ } ++ else if (strncmp (p, "i386", 4) == 0) ++ { ++ mode_64bit = 0; ++ priv.orig_sizeflag = AFLAG | DFLAG; ++ } ++ else if (strncmp (p, "i8086", 5) == 0) ++ { ++ mode_64bit = 0; ++ priv.orig_sizeflag = 0; ++ } ++ else if (strncmp (p, "intel", 5) == 0) ++ { ++ intel_syntax = 1; ++ } ++ else if (strncmp (p, "att", 3) == 0) ++ { ++ intel_syntax = 0; ++ } ++ else if (strncmp (p, "addr", 4) == 0) ++ { ++ if (p[4] == '1' && p[5] == '6') ++ priv.orig_sizeflag &= ~AFLAG; ++ else if (p[4] == '3' && p[5] == '2') ++ priv.orig_sizeflag |= AFLAG; ++ } ++ else if (strncmp (p, "data", 4) == 0) ++ { ++ if (p[4] == '1' && p[5] == '6') ++ priv.orig_sizeflag &= ~DFLAG; ++ else if (p[4] == '3' && p[5] == '2') ++ priv.orig_sizeflag |= DFLAG; ++ } ++ else if (strncmp (p, "suffix", 6) == 0) ++ priv.orig_sizeflag |= SUFFIX_ALWAYS; ++ ++ p = strchr (p, ','); ++ if (p != NULL) ++ p++; ++ } ++ ++ if (intel_syntax) ++ { ++ names64 = intel_names64; ++ names32 = intel_names32; ++ names16 = intel_names16; ++ names8 = intel_names8; ++ names8rex = intel_names8rex; ++ names_seg = intel_names_seg; ++ index16 = intel_index16; ++ open_char = '['; ++ close_char = ']'; ++ separator_char = '+'; ++ scale_char = '*'; ++ } ++ else ++ { ++ names64 = att_names64; ++ names32 = att_names32; ++ names16 = att_names16; ++ names8 = att_names8; ++ names8rex = att_names8rex; ++ names_seg = att_names_seg; ++ index16 = att_index16; ++ open_char = '('; ++ close_char = ')'; ++ separator_char = ','; ++ scale_char = ','; ++ } ++ ++ /* The output looks better if we put 7 bytes on a line, since that ++ puts most long word instructions on a single line. */ ++ info->bytes_per_line = 7; ++ ++ info->private_data = &priv; ++ priv.max_fetched = priv.the_buffer; ++ priv.insn_start = pc; ++ ++ obuf[0] = 0; ++ op1out[0] = 0; ++ op2out[0] = 0; ++ op3out[0] = 0; ++ ++ op_index[0] = op_index[1] = op_index[2] = -1; ++ ++ the_info = info; ++ start_pc = pc; ++ start_codep = priv.the_buffer; ++ codep = priv.the_buffer; ++ ++#ifndef __KERNEL__ ++ if (setjmp (priv.bailout) != 0) ++ { ++ const char *name; ++ ++ /* Getting here means we tried for data but didn't get it. That ++ means we have an incomplete instruction of some sort. Just ++ print the first byte as a prefix or a .byte pseudo-op. */ ++ if (codep > priv.the_buffer) ++ { ++ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); ++ if (name != NULL) ++ (*info->fprintf_func) (info->stream, "%s", name); ++ else ++ { ++ /* Just print the first byte as a .byte instruction. */ ++ (*info->fprintf_func) (info->stream, ".byte 0x%x", ++ (unsigned int) priv.the_buffer[0]); ++ } ++ ++ return 1; ++ } ++ ++ return -1; ++ } ++#endif /* __KERNEL__ */ ++ ++ obufp = obuf; ++ ckprefix (); ++ ++ insn_codep = codep; ++ sizeflag = priv.orig_sizeflag; ++ ++ FETCH_DATA (info, codep + 1); ++ two_source_ops = (*codep == 0x62) || (*codep == 0xc8); ++ ++ if ((prefixes & PREFIX_FWAIT) ++ && ((*codep < 0xd8) || (*codep > 0xdf))) ++ { ++ const char *name; ++ ++ /* fwait not followed by floating point instruction. Print the ++ first prefix, which is probably fwait itself. */ ++ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); ++ if (name == NULL) ++ name = INTERNAL_DISASSEMBLER_ERROR; ++ (*info->fprintf_func) (info->stream, "%s", name); ++ return 1; ++ } ++ ++ if (*codep == 0x0f) ++ { ++ FETCH_DATA (info, codep + 2); ++ dp = &dis386_twobyte[*++codep]; ++ need_modrm = twobyte_has_modrm[*codep]; ++ uses_SSE_prefix = twobyte_uses_SSE_prefix[*codep]; ++ uses_LOCK_prefix = (*codep & ~0x02) == 0x20; ++ } ++ else ++ { ++ dp = &dis386[*codep]; ++ need_modrm = onebyte_has_modrm[*codep]; ++ uses_SSE_prefix = 0; ++ uses_LOCK_prefix = 0; ++ } ++ codep++; ++ ++ if (!uses_SSE_prefix && (prefixes & PREFIX_REPZ)) ++ { ++ oappend ("repz "); ++ used_prefixes |= PREFIX_REPZ; ++ } ++ if (!uses_SSE_prefix && (prefixes & PREFIX_REPNZ)) ++ { ++ oappend ("repnz "); ++ used_prefixes |= PREFIX_REPNZ; ++ } ++ if (!uses_LOCK_prefix && (prefixes & PREFIX_LOCK)) ++ { ++ oappend ("lock "); ++ used_prefixes |= PREFIX_LOCK; ++ } ++ ++ if (prefixes & PREFIX_ADDR) ++ { ++ sizeflag ^= AFLAG; ++ if (dp->bytemode3 != loop_jcxz_mode || intel_syntax) ++ { ++ if ((sizeflag & AFLAG) || mode_64bit) ++ oappend ("addr32 "); ++ else ++ oappend ("addr16 "); ++ used_prefixes |= PREFIX_ADDR; ++ } ++ } ++ ++ if (!uses_SSE_prefix && (prefixes & PREFIX_DATA)) ++ { ++ sizeflag ^= DFLAG; ++ if (dp->bytemode3 == cond_jump_mode ++ && dp->bytemode1 == v_mode ++ && !intel_syntax) ++ { ++ if (sizeflag & DFLAG) ++ oappend ("data32 "); ++ else ++ oappend ("data16 "); ++ used_prefixes |= PREFIX_DATA; ++ } ++ } ++ ++ if (need_modrm) ++ { ++ FETCH_DATA (info, codep + 1); ++ mod = (*codep >> 6) & 3; ++ reg = (*codep >> 3) & 7; ++ rm = *codep & 7; ++ } ++ ++ if (dp->name == NULL && dp->bytemode1 == FLOATCODE) ++ { ++ dofloat (sizeflag); ++ } ++ else ++ { ++ int index; ++ if (dp->name == NULL) ++ { ++ switch (dp->bytemode1) ++ { ++ case USE_GROUPS: ++ dp = &grps[dp->bytemode2][reg]; ++ break; ++ ++ case USE_PREFIX_USER_TABLE: ++ index = 0; ++ used_prefixes |= (prefixes & PREFIX_REPZ); ++ if (prefixes & PREFIX_REPZ) ++ index = 1; ++ else ++ { ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ if (prefixes & PREFIX_DATA) ++ index = 2; ++ else ++ { ++ used_prefixes |= (prefixes & PREFIX_REPNZ); ++ if (prefixes & PREFIX_REPNZ) ++ index = 3; ++ } ++ } ++ dp = &prefix_user_table[dp->bytemode2][index]; ++ break; ++ ++ case X86_64_SPECIAL: ++ dp = &x86_64_table[dp->bytemode2][mode_64bit]; ++ break; ++ ++ default: ++ oappend (INTERNAL_DISASSEMBLER_ERROR); ++ break; ++ } ++ } ++ ++ if (putop (dp->name, sizeflag) == 0) ++ { ++ obufp = op1out; ++ op_ad = 2; ++ if (dp->op1) ++ (*dp->op1) (dp->bytemode1, sizeflag); ++ ++ obufp = op2out; ++ op_ad = 1; ++ if (dp->op2) ++ (*dp->op2) (dp->bytemode2, sizeflag); ++ ++ obufp = op3out; ++ op_ad = 0; ++ if (dp->op3) ++ (*dp->op3) (dp->bytemode3, sizeflag); ++ } ++ } ++ ++ /* See if any prefixes were not used. If so, print the first one ++ separately. If we don't do this, we'll wind up printing an ++ instruction stream which does not precisely correspond to the ++ bytes we are disassembling. */ ++ if ((prefixes & ~used_prefixes) != 0) ++ { ++ const char *name; ++ ++ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); ++ if (name == NULL) ++ name = INTERNAL_DISASSEMBLER_ERROR; ++ (*info->fprintf_func) (info->stream, "%s", name); ++ return 1; ++ } ++ if (rex & ~rex_used) ++ { ++ const char *name; ++ name = prefix_name (rex | 0x40, priv.orig_sizeflag); ++ if (name == NULL) ++ name = INTERNAL_DISASSEMBLER_ERROR; ++ (*info->fprintf_func) (info->stream, "%s ", name); ++ } ++ ++ obufp = obuf + strlen (obuf); ++ for (i = strlen (obuf); i < 6; i++) ++ oappend (" "); ++ oappend (" "); ++ (*info->fprintf_func) (info->stream, "%s", obuf); ++ ++ /* The enter and bound instructions are printed with operands in the same ++ order as the intel book; everything else is printed in reverse order. */ ++ if (intel_syntax || two_source_ops) ++ { ++ first = op1out; ++ second = op2out; ++ third = op3out; ++ op_ad = op_index[0]; ++ op_index[0] = op_index[2]; ++ op_index[2] = op_ad; ++ } ++ else ++ { ++ first = op3out; ++ second = op2out; ++ third = op1out; ++ } ++ needcomma = 0; ++ if (*first) ++ { ++ if (op_index[0] != -1 && !op_riprel[0]) ++ (*info->print_address_func) ((bfd_vma) op_address[op_index[0]], info); ++ else ++ (*info->fprintf_func) (info->stream, "%s", first); ++ needcomma = 1; ++ } ++ if (*second) ++ { ++ if (needcomma) ++ (*info->fprintf_func) (info->stream, ","); ++ if (op_index[1] != -1 && !op_riprel[1]) ++ (*info->print_address_func) ((bfd_vma) op_address[op_index[1]], info); ++ else ++ (*info->fprintf_func) (info->stream, "%s", second); ++ needcomma = 1; ++ } ++ if (*third) ++ { ++ if (needcomma) ++ (*info->fprintf_func) (info->stream, ","); ++ if (op_index[2] != -1 && !op_riprel[2]) ++ (*info->print_address_func) ((bfd_vma) op_address[op_index[2]], info); ++ else ++ (*info->fprintf_func) (info->stream, "%s", third); ++ } ++ for (i = 0; i < 3; i++) ++ if (op_index[i] != -1 && op_riprel[i]) ++ { ++ (*info->fprintf_func) (info->stream, " # "); ++ (*info->print_address_func) ((bfd_vma) (start_pc + codep - start_codep ++ + op_address[op_index[i]]), info); ++ } ++ return codep - priv.the_buffer; ++} ++ ++static const char *float_mem[] = { ++ /* d8 */ ++ "fadd{s||s|}", ++ "fmul{s||s|}", ++ "fcom{s||s|}", ++ "fcomp{s||s|}", ++ "fsub{s||s|}", ++ "fsubr{s||s|}", ++ "fdiv{s||s|}", ++ "fdivr{s||s|}", ++ /* d9 */ ++ "fld{s||s|}", ++ "(bad)", ++ "fst{s||s|}", ++ "fstp{s||s|}", ++ "fldenvIC", ++ "fldcw", ++ "fNstenvIC", ++ "fNstcw", ++ /* da */ ++ "fiadd{l||l|}", ++ "fimul{l||l|}", ++ "ficom{l||l|}", ++ "ficomp{l||l|}", ++ "fisub{l||l|}", ++ "fisubr{l||l|}", ++ "fidiv{l||l|}", ++ "fidivr{l||l|}", ++ /* db */ ++ "fild{l||l|}", ++ "fisttp{l||l|}", ++ "fist{l||l|}", ++ "fistp{l||l|}", ++ "(bad)", ++ "fld{t||t|}", ++ "(bad)", ++ "fstp{t||t|}", ++ /* dc */ ++ "fadd{l||l|}", ++ "fmul{l||l|}", ++ "fcom{l||l|}", ++ "fcomp{l||l|}", ++ "fsub{l||l|}", ++ "fsubr{l||l|}", ++ "fdiv{l||l|}", ++ "fdivr{l||l|}", ++ /* dd */ ++ "fld{l||l|}", ++ "fisttp{ll||ll|}", ++ "fst{l||l|}", ++ "fstp{l||l|}", ++ "frstorIC", ++ "(bad)", ++ "fNsaveIC", ++ "fNstsw", ++ /* de */ ++ "fiadd", ++ "fimul", ++ "ficom", ++ "ficomp", ++ "fisub", ++ "fisubr", ++ "fidiv", ++ "fidivr", ++ /* df */ ++ "fild", ++ "fisttp", ++ "fist", ++ "fistp", ++ "fbld", ++ "fild{ll||ll|}", ++ "fbstp", ++ "fistp{ll||ll|}", ++}; ++ ++static const unsigned char float_mem_mode[] = { ++ /* d8 */ ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ /* d9 */ ++ d_mode, ++ 0, ++ d_mode, ++ d_mode, ++ 0, ++ w_mode, ++ 0, ++ w_mode, ++ /* da */ ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ /* db */ ++ d_mode, ++ d_mode, ++ d_mode, ++ d_mode, ++ 0, ++ t_mode, ++ 0, ++ t_mode, ++ /* dc */ ++ q_mode, ++ q_mode, ++ q_mode, ++ q_mode, ++ q_mode, ++ q_mode, ++ q_mode, ++ q_mode, ++ /* dd */ ++ q_mode, ++ q_mode, ++ q_mode, ++ q_mode, ++ 0, ++ 0, ++ 0, ++ w_mode, ++ /* de */ ++ w_mode, ++ w_mode, ++ w_mode, ++ w_mode, ++ w_mode, ++ w_mode, ++ w_mode, ++ w_mode, ++ /* df */ ++ w_mode, ++ w_mode, ++ w_mode, ++ w_mode, ++ t_mode, ++ q_mode, ++ t_mode, ++ q_mode ++}; ++ ++#define ST OP_ST, 0 ++#define STi OP_STi, 0 ++ ++#define FGRPd9_2 NULL, NULL, 0, NULL, 0, NULL, 0 ++#define FGRPd9_4 NULL, NULL, 1, NULL, 0, NULL, 0 ++#define FGRPd9_5 NULL, NULL, 2, NULL, 0, NULL, 0 ++#define FGRPd9_6 NULL, NULL, 3, NULL, 0, NULL, 0 ++#define FGRPd9_7 NULL, NULL, 4, NULL, 0, NULL, 0 ++#define FGRPda_5 NULL, NULL, 5, NULL, 0, NULL, 0 ++#define FGRPdb_4 NULL, NULL, 6, NULL, 0, NULL, 0 ++#define FGRPde_3 NULL, NULL, 7, NULL, 0, NULL, 0 ++#define FGRPdf_4 NULL, NULL, 8, NULL, 0, NULL, 0 ++ ++static const struct dis386 float_reg[][8] = { ++ /* d8 */ ++ { ++ { "fadd", ST, STi, XX }, ++ { "fmul", ST, STi, XX }, ++ { "fcom", STi, XX, XX }, ++ { "fcomp", STi, XX, XX }, ++ { "fsub", ST, STi, XX }, ++ { "fsubr", ST, STi, XX }, ++ { "fdiv", ST, STi, XX }, ++ { "fdivr", ST, STi, XX }, ++ }, ++ /* d9 */ ++ { ++ { "fld", STi, XX, XX }, ++ { "fxch", STi, XX, XX }, ++ { FGRPd9_2 }, ++ { "(bad)", XX, XX, XX }, ++ { FGRPd9_4 }, ++ { FGRPd9_5 }, ++ { FGRPd9_6 }, ++ { FGRPd9_7 }, ++ }, ++ /* da */ ++ { ++ { "fcmovb", ST, STi, XX }, ++ { "fcmove", ST, STi, XX }, ++ { "fcmovbe",ST, STi, XX }, ++ { "fcmovu", ST, STi, XX }, ++ { "(bad)", XX, XX, XX }, ++ { FGRPda_5 }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* db */ ++ { ++ { "fcmovnb",ST, STi, XX }, ++ { "fcmovne",ST, STi, XX }, ++ { "fcmovnbe",ST, STi, XX }, ++ { "fcmovnu",ST, STi, XX }, ++ { FGRPdb_4 }, ++ { "fucomi", ST, STi, XX }, ++ { "fcomi", ST, STi, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* dc */ ++ { ++ { "fadd", STi, ST, XX }, ++ { "fmul", STi, ST, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++#if UNIXWARE_COMPAT ++ { "fsub", STi, ST, XX }, ++ { "fsubr", STi, ST, XX }, ++ { "fdiv", STi, ST, XX }, ++ { "fdivr", STi, ST, XX }, ++#else ++ { "fsubr", STi, ST, XX }, ++ { "fsub", STi, ST, XX }, ++ { "fdivr", STi, ST, XX }, ++ { "fdiv", STi, ST, XX }, ++#endif ++ }, ++ /* dd */ ++ { ++ { "ffree", STi, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "fst", STi, XX, XX }, ++ { "fstp", STi, XX, XX }, ++ { "fucom", STi, XX, XX }, ++ { "fucomp", STi, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++ /* de */ ++ { ++ { "faddp", STi, ST, XX }, ++ { "fmulp", STi, ST, XX }, ++ { "(bad)", XX, XX, XX }, ++ { FGRPde_3 }, ++#if UNIXWARE_COMPAT ++ { "fsubp", STi, ST, XX }, ++ { "fsubrp", STi, ST, XX }, ++ { "fdivp", STi, ST, XX }, ++ { "fdivrp", STi, ST, XX }, ++#else ++ { "fsubrp", STi, ST, XX }, ++ { "fsubp", STi, ST, XX }, ++ { "fdivrp", STi, ST, XX }, ++ { "fdivp", STi, ST, XX }, ++#endif ++ }, ++ /* df */ ++ { ++ { "ffreep", STi, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { "(bad)", XX, XX, XX }, ++ { FGRPdf_4 }, ++ { "fucomip",ST, STi, XX }, ++ { "fcomip", ST, STi, XX }, ++ { "(bad)", XX, XX, XX }, ++ }, ++}; ++ ++static char *fgrps[][8] = { ++ /* d9_2 0 */ ++ { ++ "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", ++ }, ++ ++ /* d9_4 1 */ ++ { ++ "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)", ++ }, ++ ++ /* d9_5 2 */ ++ { ++ "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)", ++ }, ++ ++ /* d9_6 3 */ ++ { ++ "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp", ++ }, ++ ++ /* d9_7 4 */ ++ { ++ "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos", ++ }, ++ ++ /* da_5 5 */ ++ { ++ "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", ++ }, ++ ++ /* db_4 6 */ ++ { ++ "feni(287 only)","fdisi(287 only)","fNclex","fNinit", ++ "fNsetpm(287 only)","(bad)","(bad)","(bad)", ++ }, ++ ++ /* de_3 7 */ ++ { ++ "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", ++ }, ++ ++ /* df_4 8 */ ++ { ++ "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", ++ }, ++}; ++ ++static void ++dofloat (int sizeflag) ++{ ++ const struct dis386 *dp; ++ unsigned char floatop; ++ ++ floatop = codep[-1]; ++ ++ if (mod != 3) ++ { ++ int fp_indx = (floatop - 0xd8) * 8 + reg; ++ ++ putop (float_mem[fp_indx], sizeflag); ++ obufp = op1out; ++ OP_E (float_mem_mode[fp_indx], sizeflag); ++ return; ++ } ++ /* Skip mod/rm byte. */ ++ MODRM_CHECK; ++ codep++; ++ ++ dp = &float_reg[floatop - 0xd8][reg]; ++ if (dp->name == NULL) ++ { ++ putop (fgrps[dp->bytemode1][rm], sizeflag); ++ ++ /* Instruction fnstsw is only one with strange arg. */ ++ if (floatop == 0xdf && codep[-1] == 0xe0) ++ strcpy (op1out, names16[0]); ++ } ++ else ++ { ++ putop (dp->name, sizeflag); ++ ++ obufp = op1out; ++ if (dp->op1) ++ (*dp->op1) (dp->bytemode1, sizeflag); ++ obufp = op2out; ++ if (dp->op2) ++ (*dp->op2) (dp->bytemode2, sizeflag); ++ } ++} ++ ++static void ++OP_ST (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ oappend ("%st"); ++} ++ ++static void ++OP_STi (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ sprintf (scratchbuf, "%%st(%d)", rm); ++ oappend (scratchbuf + intel_syntax); ++} ++ ++/* Capital letters in template are macros. */ ++static int ++putop (const char *template, int sizeflag) ++{ ++ const char *p; ++ int alt = 0; ++ ++ for (p = template; *p; p++) ++ { ++ switch (*p) ++ { ++ default: ++ *obufp++ = *p; ++ break; ++ case '{': ++ alt = 0; ++ if (intel_syntax) ++ alt += 1; ++ if (mode_64bit) ++ alt += 2; ++ while (alt != 0) ++ { ++ while (*++p != '|') ++ { ++ if (*p == '}') ++ { ++ /* Alternative not valid. */ ++ strcpy (obuf, "(bad)"); ++ obufp = obuf + 5; ++ return 1; ++ } ++ else if (*p == '\0') ++ abort (); ++ } ++ alt--; ++ } ++ /* Fall through. */ ++ case 'I': ++ alt = 1; ++ continue; ++ case '|': ++ while (*++p != '}') ++ { ++ if (*p == '\0') ++ abort (); ++ } ++ break; ++ case '}': ++ break; ++ case 'A': ++ if (intel_syntax) ++ break; ++ if (mod != 3 || (sizeflag & SUFFIX_ALWAYS)) ++ *obufp++ = 'b'; ++ break; ++ case 'B': ++ if (intel_syntax) ++ break; ++ if (sizeflag & SUFFIX_ALWAYS) ++ *obufp++ = 'b'; ++ break; ++ case 'C': ++ if (intel_syntax && !alt) ++ break; ++ if ((prefixes & PREFIX_DATA) || (sizeflag & SUFFIX_ALWAYS)) ++ { ++ if (sizeflag & DFLAG) ++ *obufp++ = intel_syntax ? 'd' : 'l'; ++ else ++ *obufp++ = intel_syntax ? 'w' : 's'; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ } ++ break; ++ case 'E': /* For jcxz/jecxz */ ++ if (mode_64bit) ++ { ++ if (sizeflag & AFLAG) ++ *obufp++ = 'r'; ++ else ++ *obufp++ = 'e'; ++ } ++ else ++ if (sizeflag & AFLAG) ++ *obufp++ = 'e'; ++ used_prefixes |= (prefixes & PREFIX_ADDR); ++ break; ++ case 'F': ++ if (intel_syntax) ++ break; ++ if ((prefixes & PREFIX_ADDR) || (sizeflag & SUFFIX_ALWAYS)) ++ { ++ if (sizeflag & AFLAG) ++ *obufp++ = mode_64bit ? 'q' : 'l'; ++ else ++ *obufp++ = mode_64bit ? 'l' : 'w'; ++ used_prefixes |= (prefixes & PREFIX_ADDR); ++ } ++ break; ++ case 'H': ++ if (intel_syntax) ++ break; ++ if ((prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_CS ++ || (prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_DS) ++ { ++ used_prefixes |= prefixes & (PREFIX_CS | PREFIX_DS); ++ *obufp++ = ','; ++ *obufp++ = 'p'; ++ if (prefixes & PREFIX_DS) ++ *obufp++ = 't'; ++ else ++ *obufp++ = 'n'; ++ } ++ break; ++ case 'J': ++ if (intel_syntax) ++ break; ++ *obufp++ = 'l'; ++ break; ++ case 'L': ++ if (intel_syntax) ++ break; ++ if (sizeflag & SUFFIX_ALWAYS) ++ *obufp++ = 'l'; ++ break; ++ case 'N': ++ if ((prefixes & PREFIX_FWAIT) == 0) ++ *obufp++ = 'n'; ++ else ++ used_prefixes |= PREFIX_FWAIT; ++ break; ++ case 'O': ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ *obufp++ = 'o'; ++ else ++ *obufp++ = 'd'; ++ break; ++ case 'T': ++ if (intel_syntax) ++ break; ++ if (mode_64bit) ++ { ++ *obufp++ = 'q'; ++ break; ++ } ++ /* Fall through. */ ++ case 'P': ++ if (intel_syntax) ++ break; ++ if ((prefixes & PREFIX_DATA) ++ || (rex & REX_MODE64) ++ || (sizeflag & SUFFIX_ALWAYS)) ++ { ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ *obufp++ = 'q'; ++ else ++ { ++ if (sizeflag & DFLAG) ++ *obufp++ = 'l'; ++ else ++ *obufp++ = 'w'; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ } ++ } ++ break; ++ case 'U': ++ if (intel_syntax) ++ break; ++ if (mode_64bit) ++ { ++ *obufp++ = 'q'; ++ break; ++ } ++ /* Fall through. */ ++ case 'Q': ++ if (intel_syntax && !alt) ++ break; ++ USED_REX (REX_MODE64); ++ if (mod != 3 || (sizeflag & SUFFIX_ALWAYS)) ++ { ++ if (rex & REX_MODE64) ++ *obufp++ = 'q'; ++ else ++ { ++ if (sizeflag & DFLAG) ++ *obufp++ = intel_syntax ? 'd' : 'l'; ++ else ++ *obufp++ = 'w'; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ } ++ } ++ break; ++ case 'R': ++ USED_REX (REX_MODE64); ++ if (intel_syntax) ++ { ++ if (rex & REX_MODE64) ++ { ++ *obufp++ = 'q'; ++ *obufp++ = 't'; ++ } ++ else if (sizeflag & DFLAG) ++ { ++ *obufp++ = 'd'; ++ *obufp++ = 'q'; ++ } ++ else ++ { ++ *obufp++ = 'w'; ++ *obufp++ = 'd'; ++ } ++ } ++ else ++ { ++ if (rex & REX_MODE64) ++ *obufp++ = 'q'; ++ else if (sizeflag & DFLAG) ++ *obufp++ = 'l'; ++ else ++ *obufp++ = 'w'; ++ } ++ if (!(rex & REX_MODE64)) ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ case 'S': ++ if (intel_syntax) ++ break; ++ if (sizeflag & SUFFIX_ALWAYS) ++ { ++ if (rex & REX_MODE64) ++ *obufp++ = 'q'; ++ else ++ { ++ if (sizeflag & DFLAG) ++ *obufp++ = 'l'; ++ else ++ *obufp++ = 'w'; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ } ++ } ++ break; ++ case 'X': ++ if (prefixes & PREFIX_DATA) ++ *obufp++ = 'd'; ++ else ++ *obufp++ = 's'; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ case 'Y': ++ if (intel_syntax) ++ break; ++ if (rex & REX_MODE64) ++ { ++ USED_REX (REX_MODE64); ++ *obufp++ = 'q'; ++ } ++ break; ++ /* implicit operand size 'l' for i386 or 'q' for x86-64 */ ++ case 'W': ++ /* operand size flag for cwtl, cbtw */ ++ USED_REX (0); ++ if (rex) ++ *obufp++ = 'l'; ++ else if (sizeflag & DFLAG) ++ *obufp++ = 'w'; ++ else ++ *obufp++ = 'b'; ++ if (intel_syntax) ++ { ++ if (rex) ++ { ++ *obufp++ = 'q'; ++ *obufp++ = 'e'; ++ } ++ if (sizeflag & DFLAG) ++ { ++ *obufp++ = 'd'; ++ *obufp++ = 'e'; ++ } ++ else ++ { ++ *obufp++ = 'w'; ++ } ++ } ++ if (!rex) ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ } ++ alt = 0; ++ } ++ *obufp = 0; ++ return 0; ++} ++ ++static void ++oappend (const char *s) ++{ ++ strcpy (obufp, s); ++ obufp += strlen (s); ++} ++ ++static void ++append_seg (void) ++{ ++ if (prefixes & PREFIX_CS) ++ { ++ used_prefixes |= PREFIX_CS; ++ oappend ("%cs:" + intel_syntax); ++ } ++ if (prefixes & PREFIX_DS) ++ { ++ used_prefixes |= PREFIX_DS; ++ oappend ("%ds:" + intel_syntax); ++ } ++ if (prefixes & PREFIX_SS) ++ { ++ used_prefixes |= PREFIX_SS; ++ oappend ("%ss:" + intel_syntax); ++ } ++ if (prefixes & PREFIX_ES) ++ { ++ used_prefixes |= PREFIX_ES; ++ oappend ("%es:" + intel_syntax); ++ } ++ if (prefixes & PREFIX_FS) ++ { ++ used_prefixes |= PREFIX_FS; ++ oappend ("%fs:" + intel_syntax); ++ } ++ if (prefixes & PREFIX_GS) ++ { ++ used_prefixes |= PREFIX_GS; ++ oappend ("%gs:" + intel_syntax); ++ } ++} ++ ++static void ++OP_indirE (int bytemode, int sizeflag) ++{ ++ if (!intel_syntax) ++ oappend ("*"); ++ OP_E (bytemode, sizeflag); ++} ++ ++static void ++print_operand_value (char *buf, int hex, bfd_vma disp) ++{ ++ if (mode_64bit) ++ { ++ if (hex) ++ { ++ char tmp[30]; ++ int i; ++ buf[0] = '0'; ++ buf[1] = 'x'; ++ sprintf_vma (tmp, disp); ++ for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++); ++ strcpy (buf + 2, tmp + i); ++ } ++ else ++ { ++ bfd_signed_vma v = disp; ++ char tmp[30]; ++ int i; ++ if (v < 0) ++ { ++ *(buf++) = '-'; ++ v = -disp; ++ /* Check for possible overflow on 0x8000000000000000. */ ++ if (v < 0) ++ { ++ strcpy (buf, "9223372036854775808"); ++ return; ++ } ++ } ++ if (!v) ++ { ++ strcpy (buf, "0"); ++ return; ++ } ++ ++ i = 0; ++ tmp[29] = 0; ++ while (v) ++ { ++ tmp[28 - i] = (v % 10) + '0'; ++ v /= 10; ++ i++; ++ } ++ strcpy (buf, tmp + 29 - i); ++ } ++ } ++ else ++ { ++ if (hex) ++ sprintf (buf, "0x%x", (unsigned int) disp); ++ else ++ sprintf (buf, "%d", (int) disp); ++ } ++} ++ ++static void ++OP_E (int bytemode, int sizeflag) ++{ ++ bfd_vma disp; ++ int add = 0; ++ int riprel = 0; ++ USED_REX (REX_EXTZ); ++ if (rex & REX_EXTZ) ++ add += 8; ++ ++ /* Skip mod/rm byte. */ ++ MODRM_CHECK; ++ codep++; ++ ++ if (mod == 3) ++ { ++ switch (bytemode) ++ { ++ case b_mode: ++ USED_REX (0); ++ if (rex) ++ oappend (names8rex[rm + add]); ++ else ++ oappend (names8[rm + add]); ++ break; ++ case w_mode: ++ oappend (names16[rm + add]); ++ break; ++ case d_mode: ++ oappend (names32[rm + add]); ++ break; ++ case q_mode: ++ oappend (names64[rm + add]); ++ break; ++ case m_mode: ++ if (mode_64bit) ++ oappend (names64[rm + add]); ++ else ++ oappend (names32[rm + add]); ++ break; ++ case branch_v_mode: ++ if (mode_64bit) ++ oappend (names64[rm + add]); ++ else ++ { ++ if ((sizeflag & DFLAG) || bytemode != branch_v_mode) ++ oappend (names32[rm + add]); ++ else ++ oappend (names16[rm + add]); ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ } ++ break; ++ case v_mode: ++ case dq_mode: ++ case dqw_mode: ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ oappend (names64[rm + add]); ++ else if ((sizeflag & DFLAG) || bytemode != v_mode) ++ oappend (names32[rm + add]); ++ else ++ oappend (names16[rm + add]); ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ case 0: ++ break; ++ default: ++ oappend (INTERNAL_DISASSEMBLER_ERROR); ++ break; ++ } ++ return; ++ } ++ ++ disp = 0; ++ append_seg (); ++ ++ if ((sizeflag & AFLAG) || mode_64bit) /* 32 bit address mode */ ++ { ++ int havesib; ++ int havebase; ++ int base; ++ int index = 0; ++ int scale = 0; ++ ++ havesib = 0; ++ havebase = 1; ++ base = rm; ++ ++ if (base == 4) ++ { ++ havesib = 1; ++ FETCH_DATA (the_info, codep + 1); ++ index = (*codep >> 3) & 7; ++ if (mode_64bit || index != 0x4) ++ /* When INDEX == 0x4 in 32 bit mode, SCALE is ignored. */ ++ scale = (*codep >> 6) & 3; ++ base = *codep & 7; ++ USED_REX (REX_EXTY); ++ if (rex & REX_EXTY) ++ index += 8; ++ codep++; ++ } ++ base += add; ++ ++ switch (mod) ++ { ++ case 0: ++ if ((base & 7) == 5) ++ { ++ havebase = 0; ++ if (mode_64bit && !havesib) ++ riprel = 1; ++ disp = get32s (); ++ } ++ break; ++ case 1: ++ FETCH_DATA (the_info, codep + 1); ++ disp = *codep++; ++ if ((disp & 0x80) != 0) ++ disp -= 0x100; ++ break; ++ case 2: ++ disp = get32s (); ++ break; ++ } ++ ++ if (!intel_syntax) ++ if (mod != 0 || (base & 7) == 5) ++ { ++ print_operand_value (scratchbuf, !riprel, disp); ++ oappend (scratchbuf); ++ if (riprel) ++ { ++ set_op (disp, 1); ++ oappend ("(%rip)"); ++ } ++ } ++ ++ if (havebase || (havesib && (index != 4 || scale != 0))) ++ { ++ if (intel_syntax) ++ { ++ switch (bytemode) ++ { ++ case b_mode: ++ oappend ("BYTE PTR "); ++ break; ++ case w_mode: ++ case dqw_mode: ++ oappend ("WORD PTR "); ++ break; ++ case branch_v_mode: ++ case v_mode: ++ case dq_mode: ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ oappend ("QWORD PTR "); ++ else if ((sizeflag & DFLAG) || bytemode == dq_mode) ++ oappend ("DWORD PTR "); ++ else ++ oappend ("WORD PTR "); ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ case d_mode: ++ oappend ("DWORD PTR "); ++ break; ++ case q_mode: ++ oappend ("QWORD PTR "); ++ break; ++ case m_mode: ++ if (mode_64bit) ++ oappend ("QWORD PTR "); ++ else ++ oappend ("DWORD PTR "); ++ break; ++ case f_mode: ++ if (sizeflag & DFLAG) ++ { ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ oappend ("FWORD PTR "); ++ } ++ else ++ oappend ("DWORD PTR "); ++ break; ++ case t_mode: ++ oappend ("TBYTE PTR "); ++ break; ++ case x_mode: ++ oappend ("XMMWORD PTR "); ++ break; ++ default: ++ break; ++ } ++ } ++ *obufp++ = open_char; ++ if (intel_syntax && riprel) ++ oappend ("rip + "); ++ *obufp = '\0'; ++ if (havebase) ++ oappend (mode_64bit && (sizeflag & AFLAG) ++ ? names64[base] : names32[base]); ++ if (havesib) ++ { ++ if (index != 4) ++ { ++ if (!intel_syntax || havebase) ++ { ++ *obufp++ = separator_char; ++ *obufp = '\0'; ++ } ++ oappend (mode_64bit && (sizeflag & AFLAG) ++ ? names64[index] : names32[index]); ++ } ++ if (scale != 0 || (!intel_syntax && index != 4)) ++ { ++ *obufp++ = scale_char; ++ *obufp = '\0'; ++ sprintf (scratchbuf, "%d", 1 << scale); ++ oappend (scratchbuf); ++ } ++ } ++ if (intel_syntax && disp) ++ { ++ if ((bfd_signed_vma) disp > 0) ++ { ++ *obufp++ = '+'; ++ *obufp = '\0'; ++ } ++ else if (mod != 1) ++ { ++ *obufp++ = '-'; ++ *obufp = '\0'; ++ disp = - (bfd_signed_vma) disp; ++ } ++ ++ print_operand_value (scratchbuf, mod != 1, disp); ++ oappend (scratchbuf); ++ } ++ ++ *obufp++ = close_char; ++ *obufp = '\0'; ++ } ++ else if (intel_syntax) ++ { ++ if (mod != 0 || (base & 7) == 5) ++ { ++ if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS ++ | PREFIX_ES | PREFIX_FS | PREFIX_GS)) ++ ; ++ else ++ { ++ oappend (names_seg[ds_reg - es_reg]); ++ oappend (":"); ++ } ++ print_operand_value (scratchbuf, 1, disp); ++ oappend (scratchbuf); ++ } ++ } ++ } ++ else ++ { /* 16 bit address mode */ ++ switch (mod) ++ { ++ case 0: ++ if (rm == 6) ++ { ++ disp = get16 (); ++ if ((disp & 0x8000) != 0) ++ disp -= 0x10000; ++ } ++ break; ++ case 1: ++ FETCH_DATA (the_info, codep + 1); ++ disp = *codep++; ++ if ((disp & 0x80) != 0) ++ disp -= 0x100; ++ break; ++ case 2: ++ disp = get16 (); ++ if ((disp & 0x8000) != 0) ++ disp -= 0x10000; ++ break; ++ } ++ ++ if (!intel_syntax) ++ if (mod != 0 || rm == 6) ++ { ++ print_operand_value (scratchbuf, 0, disp); ++ oappend (scratchbuf); ++ } ++ ++ if (mod != 0 || rm != 6) ++ { ++ *obufp++ = open_char; ++ *obufp = '\0'; ++ oappend (index16[rm]); ++ if (intel_syntax && disp) ++ { ++ if ((bfd_signed_vma) disp > 0) ++ { ++ *obufp++ = '+'; ++ *obufp = '\0'; ++ } ++ else if (mod != 1) ++ { ++ *obufp++ = '-'; ++ *obufp = '\0'; ++ disp = - (bfd_signed_vma) disp; ++ } ++ ++ print_operand_value (scratchbuf, mod != 1, disp); ++ oappend (scratchbuf); ++ } ++ ++ *obufp++ = close_char; ++ *obufp = '\0'; ++ } ++ else if (intel_syntax) ++ { ++ if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS ++ | PREFIX_ES | PREFIX_FS | PREFIX_GS)) ++ ; ++ else ++ { ++ oappend (names_seg[ds_reg - es_reg]); ++ oappend (":"); ++ } ++ print_operand_value (scratchbuf, 1, disp & 0xffff); ++ oappend (scratchbuf); ++ } ++ } ++} ++ ++static void ++OP_G (int bytemode, int sizeflag) ++{ ++ int add = 0; ++ USED_REX (REX_EXTX); ++ if (rex & REX_EXTX) ++ add += 8; ++ switch (bytemode) ++ { ++ case b_mode: ++ USED_REX (0); ++ if (rex) ++ oappend (names8rex[reg + add]); ++ else ++ oappend (names8[reg + add]); ++ break; ++ case w_mode: ++ oappend (names16[reg + add]); ++ break; ++ case d_mode: ++ oappend (names32[reg + add]); ++ break; ++ case q_mode: ++ oappend (names64[reg + add]); ++ break; ++ case v_mode: ++ case dq_mode: ++ case dqw_mode: ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ oappend (names64[reg + add]); ++ else if ((sizeflag & DFLAG) || bytemode != v_mode) ++ oappend (names32[reg + add]); ++ else ++ oappend (names16[reg + add]); ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ case m_mode: ++ if (mode_64bit) ++ oappend (names64[reg + add]); ++ else ++ oappend (names32[reg + add]); ++ break; ++ default: ++ oappend (INTERNAL_DISASSEMBLER_ERROR); ++ break; ++ } ++} ++ ++static bfd_vma ++get64 (void) ++{ ++ bfd_vma x; ++#ifdef BFD64 ++ unsigned int a; ++ unsigned int b; ++ ++ FETCH_DATA (the_info, codep + 8); ++ a = *codep++ & 0xff; ++ a |= (*codep++ & 0xff) << 8; ++ a |= (*codep++ & 0xff) << 16; ++ a |= (*codep++ & 0xff) << 24; ++ b = *codep++ & 0xff; ++ b |= (*codep++ & 0xff) << 8; ++ b |= (*codep++ & 0xff) << 16; ++ b |= (*codep++ & 0xff) << 24; ++ x = a + ((bfd_vma) b << 32); ++#else ++ abort (); ++ x = 0; ++#endif ++ return x; ++} ++ ++static bfd_signed_vma ++get32 (void) ++{ ++ bfd_signed_vma x = 0; ++ ++ FETCH_DATA (the_info, codep + 4); ++ x = *codep++ & (bfd_signed_vma) 0xff; ++ x |= (*codep++ & (bfd_signed_vma) 0xff) << 8; ++ x |= (*codep++ & (bfd_signed_vma) 0xff) << 16; ++ x |= (*codep++ & (bfd_signed_vma) 0xff) << 24; ++ return x; ++} ++ ++static bfd_signed_vma ++get32s (void) ++{ ++ bfd_signed_vma x = 0; ++ ++ FETCH_DATA (the_info, codep + 4); ++ x = *codep++ & (bfd_signed_vma) 0xff; ++ x |= (*codep++ & (bfd_signed_vma) 0xff) << 8; ++ x |= (*codep++ & (bfd_signed_vma) 0xff) << 16; ++ x |= (*codep++ & (bfd_signed_vma) 0xff) << 24; ++ ++ x = (x ^ ((bfd_signed_vma) 1 << 31)) - ((bfd_signed_vma) 1 << 31); ++ ++ return x; ++} ++ ++static int ++get16 (void) ++{ ++ int x = 0; ++ ++ FETCH_DATA (the_info, codep + 2); ++ x = *codep++ & 0xff; ++ x |= (*codep++ & 0xff) << 8; ++ return x; ++} ++ ++static void ++set_op (bfd_vma op, int riprel) ++{ ++ op_index[op_ad] = op_ad; ++ if (mode_64bit) ++ { ++ op_address[op_ad] = op; ++ op_riprel[op_ad] = riprel; ++ } ++ else ++ { ++ /* Mask to get a 32-bit address. */ ++ op_address[op_ad] = op & 0xffffffff; ++ op_riprel[op_ad] = riprel & 0xffffffff; ++ } ++} ++ ++static void ++OP_REG (int code, int sizeflag) ++{ ++ const char *s; ++ int add = 0; ++ USED_REX (REX_EXTZ); ++ if (rex & REX_EXTZ) ++ add = 8; ++ ++ switch (code) ++ { ++ case indir_dx_reg: ++ if (intel_syntax) ++ s = "[dx]"; ++ else ++ s = "(%dx)"; ++ break; ++ case ax_reg: case cx_reg: case dx_reg: case bx_reg: ++ case sp_reg: case bp_reg: case si_reg: case di_reg: ++ s = names16[code - ax_reg + add]; ++ break; ++ case es_reg: case ss_reg: case cs_reg: ++ case ds_reg: case fs_reg: case gs_reg: ++ s = names_seg[code - es_reg + add]; ++ break; ++ case al_reg: case ah_reg: case cl_reg: case ch_reg: ++ case dl_reg: case dh_reg: case bl_reg: case bh_reg: ++ USED_REX (0); ++ if (rex) ++ s = names8rex[code - al_reg + add]; ++ else ++ s = names8[code - al_reg]; ++ break; ++ case rAX_reg: case rCX_reg: case rDX_reg: case rBX_reg: ++ case rSP_reg: case rBP_reg: case rSI_reg: case rDI_reg: ++ if (mode_64bit) ++ { ++ s = names64[code - rAX_reg + add]; ++ break; ++ } ++ code += eAX_reg - rAX_reg; ++ /* Fall through. */ ++ case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg: ++ case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg: ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ s = names64[code - eAX_reg + add]; ++ else if (sizeflag & DFLAG) ++ s = names32[code - eAX_reg + add]; ++ else ++ s = names16[code - eAX_reg + add]; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ default: ++ s = INTERNAL_DISASSEMBLER_ERROR; ++ break; ++ } ++ oappend (s); ++} ++ ++static void ++OP_IMREG (int code, int sizeflag) ++{ ++ const char *s; ++ ++ switch (code) ++ { ++ case indir_dx_reg: ++ if (intel_syntax) ++ s = "[dx]"; ++ else ++ s = "(%dx)"; ++ break; ++ case ax_reg: case cx_reg: case dx_reg: case bx_reg: ++ case sp_reg: case bp_reg: case si_reg: case di_reg: ++ s = names16[code - ax_reg]; ++ break; ++ case es_reg: case ss_reg: case cs_reg: ++ case ds_reg: case fs_reg: case gs_reg: ++ s = names_seg[code - es_reg]; ++ break; ++ case al_reg: case ah_reg: case cl_reg: case ch_reg: ++ case dl_reg: case dh_reg: case bl_reg: case bh_reg: ++ USED_REX (0); ++ if (rex) ++ s = names8rex[code - al_reg]; ++ else ++ s = names8[code - al_reg]; ++ break; ++ case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg: ++ case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg: ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ s = names64[code - eAX_reg]; ++ else if (sizeflag & DFLAG) ++ s = names32[code - eAX_reg]; ++ else ++ s = names16[code - eAX_reg]; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ default: ++ s = INTERNAL_DISASSEMBLER_ERROR; ++ break; ++ } ++ oappend (s); ++} ++ ++static void ++OP_I (int bytemode, int sizeflag) ++{ ++ bfd_signed_vma op; ++ bfd_signed_vma mask = -1; ++ ++ switch (bytemode) ++ { ++ case b_mode: ++ FETCH_DATA (the_info, codep + 1); ++ op = *codep++; ++ mask = 0xff; ++ break; ++ case q_mode: ++ if (mode_64bit) ++ { ++ op = get32s (); ++ break; ++ } ++ /* Fall through. */ ++ case v_mode: ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ op = get32s (); ++ else if (sizeflag & DFLAG) ++ { ++ op = get32 (); ++ mask = 0xffffffff; ++ } ++ else ++ { ++ op = get16 (); ++ mask = 0xfffff; ++ } ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ case w_mode: ++ mask = 0xfffff; ++ op = get16 (); ++ break; ++ case const_1_mode: ++ if (intel_syntax) ++ oappend ("1"); ++ return; ++ default: ++ oappend (INTERNAL_DISASSEMBLER_ERROR); ++ return; ++ } ++ ++ op &= mask; ++ scratchbuf[0] = '$'; ++ print_operand_value (scratchbuf + 1, 1, op); ++ oappend (scratchbuf + intel_syntax); ++ scratchbuf[0] = '\0'; ++} ++ ++static void ++OP_I64 (int bytemode, int sizeflag) ++{ ++ bfd_signed_vma op; ++ bfd_signed_vma mask = -1; ++ ++ if (!mode_64bit) ++ { ++ OP_I (bytemode, sizeflag); ++ return; ++ } ++ ++ switch (bytemode) ++ { ++ case b_mode: ++ FETCH_DATA (the_info, codep + 1); ++ op = *codep++; ++ mask = 0xff; ++ break; ++ case v_mode: ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ op = get64 (); ++ else if (sizeflag & DFLAG) ++ { ++ op = get32 (); ++ mask = 0xffffffff; ++ } ++ else ++ { ++ op = get16 (); ++ mask = 0xfffff; ++ } ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ case w_mode: ++ mask = 0xfffff; ++ op = get16 (); ++ break; ++ default: ++ oappend (INTERNAL_DISASSEMBLER_ERROR); ++ return; ++ } ++ ++ op &= mask; ++ scratchbuf[0] = '$'; ++ print_operand_value (scratchbuf + 1, 1, op); ++ oappend (scratchbuf + intel_syntax); ++ scratchbuf[0] = '\0'; ++} ++ ++static void ++OP_sI (int bytemode, int sizeflag) ++{ ++ bfd_signed_vma op; ++ bfd_signed_vma mask = -1; ++ ++ switch (bytemode) ++ { ++ case b_mode: ++ FETCH_DATA (the_info, codep + 1); ++ op = *codep++; ++ if ((op & 0x80) != 0) ++ op -= 0x100; ++ mask = 0xffffffff; ++ break; ++ case v_mode: ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ op = get32s (); ++ else if (sizeflag & DFLAG) ++ { ++ op = get32s (); ++ mask = 0xffffffff; ++ } ++ else ++ { ++ mask = 0xffffffff; ++ op = get16 (); ++ if ((op & 0x8000) != 0) ++ op -= 0x10000; ++ } ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ break; ++ case w_mode: ++ op = get16 (); ++ mask = 0xffffffff; ++ if ((op & 0x8000) != 0) ++ op -= 0x10000; ++ break; ++ default: ++ oappend (INTERNAL_DISASSEMBLER_ERROR); ++ return; ++ } ++ ++ scratchbuf[0] = '$'; ++ print_operand_value (scratchbuf + 1, 1, op); ++ oappend (scratchbuf + intel_syntax); ++} ++ ++static void ++OP_J (int bytemode, int sizeflag) ++{ ++ bfd_vma disp; ++ bfd_vma mask = -1; ++ ++ switch (bytemode) ++ { ++ case b_mode: ++ FETCH_DATA (the_info, codep + 1); ++ disp = *codep++; ++ if ((disp & 0x80) != 0) ++ disp -= 0x100; ++ break; ++ case v_mode: ++ if (sizeflag & DFLAG) ++ disp = get32s (); ++ else ++ { ++ disp = get16 (); ++ /* For some reason, a data16 prefix on a jump instruction ++ means that the pc is masked to 16 bits after the ++ displacement is added! */ ++ mask = 0xffff; ++ } ++ break; ++ default: ++ oappend (INTERNAL_DISASSEMBLER_ERROR); ++ return; ++ } ++ disp = (start_pc + codep - start_codep + disp) & mask; ++ set_op (disp, 0); ++ print_operand_value (scratchbuf, 1, disp); ++ oappend (scratchbuf); ++} ++ ++static void ++OP_SEG (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ oappend (names_seg[reg]); ++} ++ ++static void ++OP_DIR (int dummy ATTRIBUTE_UNUSED, int sizeflag) ++{ ++ int seg, offset; ++ ++ if (sizeflag & DFLAG) ++ { ++ offset = get32 (); ++ seg = get16 (); ++ } ++ else ++ { ++ offset = get16 (); ++ seg = get16 (); ++ } ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ if (intel_syntax) ++ sprintf (scratchbuf, "0x%x,0x%x", seg, offset); ++ else ++ sprintf (scratchbuf, "$0x%x,$0x%x", seg, offset); ++ oappend (scratchbuf); ++} ++ ++static void ++OP_OFF (int bytemode ATTRIBUTE_UNUSED, int sizeflag) ++{ ++ bfd_vma off; ++ ++ append_seg (); ++ ++ if ((sizeflag & AFLAG) || mode_64bit) ++ off = get32 (); ++ else ++ off = get16 (); ++ ++ if (intel_syntax) ++ { ++ if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS ++ | PREFIX_ES | PREFIX_FS | PREFIX_GS))) ++ { ++ oappend (names_seg[ds_reg - es_reg]); ++ oappend (":"); ++ } ++ } ++ print_operand_value (scratchbuf, 1, off); ++ oappend (scratchbuf); ++} ++ ++static void ++OP_OFF64 (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ bfd_vma off; ++ ++ if (!mode_64bit) ++ { ++ OP_OFF (bytemode, sizeflag); ++ return; ++ } ++ ++ append_seg (); ++ ++ off = get64 (); ++ ++ if (intel_syntax) ++ { ++ if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS ++ | PREFIX_ES | PREFIX_FS | PREFIX_GS))) ++ { ++ oappend (names_seg[ds_reg - es_reg]); ++ oappend (":"); ++ } ++ } ++ print_operand_value (scratchbuf, 1, off); ++ oappend (scratchbuf); ++} ++ ++static void ++ptr_reg (int code, int sizeflag) ++{ ++ const char *s; ++ ++ *obufp++ = open_char; ++ used_prefixes |= (prefixes & PREFIX_ADDR); ++ if (mode_64bit) ++ { ++ if (!(sizeflag & AFLAG)) ++ s = names32[code - eAX_reg]; ++ else ++ s = names64[code - eAX_reg]; ++ } ++ else if (sizeflag & AFLAG) ++ s = names32[code - eAX_reg]; ++ else ++ s = names16[code - eAX_reg]; ++ oappend (s); ++ *obufp++ = close_char; ++ *obufp = 0; ++} ++ ++static void ++OP_ESreg (int code, int sizeflag) ++{ ++ if (intel_syntax) ++ { ++ if (codep[-1] & 1) ++ { ++ USED_REX (REX_MODE64); ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ if (rex & REX_MODE64) ++ oappend ("QWORD PTR "); ++ else if ((sizeflag & DFLAG)) ++ oappend ("DWORD PTR "); ++ else ++ oappend ("WORD PTR "); ++ } ++ else ++ oappend ("BYTE PTR "); ++ } ++ ++ oappend ("%es:" + intel_syntax); ++ ptr_reg (code, sizeflag); ++} ++ ++static void ++OP_DSreg (int code, int sizeflag) ++{ ++ if (intel_syntax) ++ { ++ if (codep[-1] != 0xd7 && (codep[-1] & 1)) ++ { ++ USED_REX (REX_MODE64); ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ if (rex & REX_MODE64) ++ oappend ("QWORD PTR "); ++ else if ((sizeflag & DFLAG)) ++ oappend ("DWORD PTR "); ++ else ++ oappend ("WORD PTR "); ++ } ++ else ++ oappend ("BYTE PTR "); ++ } ++ ++ if ((prefixes ++ & (PREFIX_CS ++ | PREFIX_DS ++ | PREFIX_SS ++ | PREFIX_ES ++ | PREFIX_FS ++ | PREFIX_GS)) == 0) ++ prefixes |= PREFIX_DS; ++ append_seg (); ++ ptr_reg (code, sizeflag); ++} ++ ++static void ++OP_C (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ int add = 0; ++ if (rex & REX_EXTX) ++ { ++ USED_REX (REX_EXTX); ++ add = 8; ++ } ++ else if (!mode_64bit && (prefixes & PREFIX_LOCK)) ++ { ++ used_prefixes |= PREFIX_LOCK; ++ add = 8; ++ } ++ sprintf (scratchbuf, "%%cr%d", reg + add); ++ oappend (scratchbuf + intel_syntax); ++} ++ ++static void ++OP_D (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ int add = 0; ++ USED_REX (REX_EXTX); ++ if (rex & REX_EXTX) ++ add = 8; ++ if (intel_syntax) ++ sprintf (scratchbuf, "db%d", reg + add); ++ else ++ sprintf (scratchbuf, "%%db%d", reg + add); ++ oappend (scratchbuf); ++} ++ ++static void ++OP_T (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ sprintf (scratchbuf, "%%tr%d", reg); ++ oappend (scratchbuf + intel_syntax); ++} ++ ++static void ++OP_Rd (int bytemode, int sizeflag) ++{ ++ if (mod == 3) ++ OP_E (bytemode, sizeflag); ++ else ++ BadOp (); ++} ++ ++static void ++OP_MMX (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ if (prefixes & PREFIX_DATA) ++ { ++ int add = 0; ++ USED_REX (REX_EXTX); ++ if (rex & REX_EXTX) ++ add = 8; ++ sprintf (scratchbuf, "%%xmm%d", reg + add); ++ } ++ else ++ sprintf (scratchbuf, "%%mm%d", reg); ++ oappend (scratchbuf + intel_syntax); ++} ++ ++static void ++OP_XMM (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ int add = 0; ++ USED_REX (REX_EXTX); ++ if (rex & REX_EXTX) ++ add = 8; ++ sprintf (scratchbuf, "%%xmm%d", reg + add); ++ oappend (scratchbuf + intel_syntax); ++} ++ ++static void ++OP_EM (int bytemode, int sizeflag) ++{ ++ if (mod != 3) ++ { ++ if (intel_syntax && bytemode == v_mode) ++ { ++ bytemode = (prefixes & PREFIX_DATA) ? x_mode : q_mode; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ } ++ OP_E (bytemode, sizeflag); ++ return; ++ } ++ ++ /* Skip mod/rm byte. */ ++ MODRM_CHECK; ++ codep++; ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ if (prefixes & PREFIX_DATA) ++ { ++ int add = 0; ++ ++ USED_REX (REX_EXTZ); ++ if (rex & REX_EXTZ) ++ add = 8; ++ sprintf (scratchbuf, "%%xmm%d", rm + add); ++ } ++ else ++ sprintf (scratchbuf, "%%mm%d", rm); ++ oappend (scratchbuf + intel_syntax); ++} ++ ++static void ++OP_EX (int bytemode, int sizeflag) ++{ ++ int add = 0; ++ if (mod != 3) ++ { ++ if (intel_syntax && bytemode == v_mode) ++ { ++ switch (prefixes & (PREFIX_DATA|PREFIX_REPZ|PREFIX_REPNZ)) ++ { ++ case 0: bytemode = x_mode; break; ++ case PREFIX_REPZ: bytemode = d_mode; used_prefixes |= PREFIX_REPZ; break; ++ case PREFIX_DATA: bytemode = x_mode; used_prefixes |= PREFIX_DATA; break; ++ case PREFIX_REPNZ: bytemode = q_mode; used_prefixes |= PREFIX_REPNZ; break; ++ default: bytemode = 0; break; ++ } ++ } ++ OP_E (bytemode, sizeflag); ++ return; ++ } ++ USED_REX (REX_EXTZ); ++ if (rex & REX_EXTZ) ++ add = 8; ++ ++ /* Skip mod/rm byte. */ ++ MODRM_CHECK; ++ codep++; ++ sprintf (scratchbuf, "%%xmm%d", rm + add); ++ oappend (scratchbuf + intel_syntax); ++} ++ ++static void ++OP_MS (int bytemode, int sizeflag) ++{ ++ if (mod == 3) ++ OP_EM (bytemode, sizeflag); ++ else ++ BadOp (); ++} ++ ++static void ++OP_XS (int bytemode, int sizeflag) ++{ ++ if (mod == 3) ++ OP_EX (bytemode, sizeflag); ++ else ++ BadOp (); ++} ++ ++static void ++OP_M (int bytemode, int sizeflag) ++{ ++ if (mod == 3) ++ BadOp (); /* bad lea,lds,les,lfs,lgs,lss modrm */ ++ else ++ OP_E (bytemode, sizeflag); ++} ++ ++static void ++OP_0f07 (int bytemode, int sizeflag) ++{ ++ if (mod != 3 || rm != 0) ++ BadOp (); ++ else ++ OP_E (bytemode, sizeflag); ++} ++ ++static void ++OP_0fae (int bytemode, int sizeflag) ++{ ++ if (mod == 3) ++ { ++ if (reg == 5) ++ strcpy (obuf + strlen (obuf) - sizeof ("xrstor") + 1, "lfence"); ++ if (reg == 7) ++ strcpy (obuf + strlen (obuf) - sizeof ("clflush") + 1, "sfence"); ++ ++ if (reg < 5 || rm != 0) ++ { ++ BadOp (); /* bad sfence, mfence, or lfence */ ++ return; ++ } ++ } ++ else if (reg != 5 && reg != 7) ++ { ++ BadOp (); /* bad xrstor or clflush */ ++ return; ++ } ++ ++ OP_E (bytemode, sizeflag); ++} ++ ++static void ++NOP_Fixup (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ /* NOP with REPZ prefix is called PAUSE. */ ++ if (prefixes == PREFIX_REPZ) ++ strcpy (obuf, "pause"); ++} ++ ++static const char *const Suffix3DNow[] = { ++/* 00 */ NULL, NULL, NULL, NULL, ++/* 04 */ NULL, NULL, NULL, NULL, ++/* 08 */ NULL, NULL, NULL, NULL, ++/* 0C */ "pi2fw", "pi2fd", NULL, NULL, ++/* 10 */ NULL, NULL, NULL, NULL, ++/* 14 */ NULL, NULL, NULL, NULL, ++/* 18 */ NULL, NULL, NULL, NULL, ++/* 1C */ "pf2iw", "pf2id", NULL, NULL, ++/* 20 */ NULL, NULL, NULL, NULL, ++/* 24 */ NULL, NULL, NULL, NULL, ++/* 28 */ NULL, NULL, NULL, NULL, ++/* 2C */ NULL, NULL, NULL, NULL, ++/* 30 */ NULL, NULL, NULL, NULL, ++/* 34 */ NULL, NULL, NULL, NULL, ++/* 38 */ NULL, NULL, NULL, NULL, ++/* 3C */ NULL, NULL, NULL, NULL, ++/* 40 */ NULL, NULL, NULL, NULL, ++/* 44 */ NULL, NULL, NULL, NULL, ++/* 48 */ NULL, NULL, NULL, NULL, ++/* 4C */ NULL, NULL, NULL, NULL, ++/* 50 */ NULL, NULL, NULL, NULL, ++/* 54 */ NULL, NULL, NULL, NULL, ++/* 58 */ NULL, NULL, NULL, NULL, ++/* 5C */ NULL, NULL, NULL, NULL, ++/* 60 */ NULL, NULL, NULL, NULL, ++/* 64 */ NULL, NULL, NULL, NULL, ++/* 68 */ NULL, NULL, NULL, NULL, ++/* 6C */ NULL, NULL, NULL, NULL, ++/* 70 */ NULL, NULL, NULL, NULL, ++/* 74 */ NULL, NULL, NULL, NULL, ++/* 78 */ NULL, NULL, NULL, NULL, ++/* 7C */ NULL, NULL, NULL, NULL, ++/* 80 */ NULL, NULL, NULL, NULL, ++/* 84 */ NULL, NULL, NULL, NULL, ++/* 88 */ NULL, NULL, "pfnacc", NULL, ++/* 8C */ NULL, NULL, "pfpnacc", NULL, ++/* 90 */ "pfcmpge", NULL, NULL, NULL, ++/* 94 */ "pfmin", NULL, "pfrcp", "pfrsqrt", ++/* 98 */ NULL, NULL, "pfsub", NULL, ++/* 9C */ NULL, NULL, "pfadd", NULL, ++/* A0 */ "pfcmpgt", NULL, NULL, NULL, ++/* A4 */ "pfmax", NULL, "pfrcpit1", "pfrsqit1", ++/* A8 */ NULL, NULL, "pfsubr", NULL, ++/* AC */ NULL, NULL, "pfacc", NULL, ++/* B0 */ "pfcmpeq", NULL, NULL, NULL, ++/* B4 */ "pfmul", NULL, "pfrcpit2", "pfmulhrw", ++/* B8 */ NULL, NULL, NULL, "pswapd", ++/* BC */ NULL, NULL, NULL, "pavgusb", ++/* C0 */ NULL, NULL, NULL, NULL, ++/* C4 */ NULL, NULL, NULL, NULL, ++/* C8 */ NULL, NULL, NULL, NULL, ++/* CC */ NULL, NULL, NULL, NULL, ++/* D0 */ NULL, NULL, NULL, NULL, ++/* D4 */ NULL, NULL, NULL, NULL, ++/* D8 */ NULL, NULL, NULL, NULL, ++/* DC */ NULL, NULL, NULL, NULL, ++/* E0 */ NULL, NULL, NULL, NULL, ++/* E4 */ NULL, NULL, NULL, NULL, ++/* E8 */ NULL, NULL, NULL, NULL, ++/* EC */ NULL, NULL, NULL, NULL, ++/* F0 */ NULL, NULL, NULL, NULL, ++/* F4 */ NULL, NULL, NULL, NULL, ++/* F8 */ NULL, NULL, NULL, NULL, ++/* FC */ NULL, NULL, NULL, NULL, ++}; ++ ++static void ++OP_3DNowSuffix (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ const char *mnemonic; ++ ++ FETCH_DATA (the_info, codep + 1); ++ /* AMD 3DNow! instructions are specified by an opcode suffix in the ++ place where an 8-bit immediate would normally go. ie. the last ++ byte of the instruction. */ ++ obufp = obuf + strlen (obuf); ++ mnemonic = Suffix3DNow[*codep++ & 0xff]; ++ if (mnemonic) ++ oappend (mnemonic); ++ else ++ { ++ /* Since a variable sized modrm/sib chunk is between the start ++ of the opcode (0x0f0f) and the opcode suffix, we need to do ++ all the modrm processing first, and don't know until now that ++ we have a bad opcode. This necessitates some cleaning up. */ ++ op1out[0] = '\0'; ++ op2out[0] = '\0'; ++ BadOp (); ++ } ++} ++ ++static const char *simd_cmp_op[] = { ++ "eq", ++ "lt", ++ "le", ++ "unord", ++ "neq", ++ "nlt", ++ "nle", ++ "ord" ++}; ++ ++static void ++OP_SIMD_Suffix (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ unsigned int cmp_type; ++ ++ FETCH_DATA (the_info, codep + 1); ++ obufp = obuf + strlen (obuf); ++ cmp_type = *codep++ & 0xff; ++ if (cmp_type < 8) ++ { ++ char suffix1 = 'p', suffix2 = 's'; ++ used_prefixes |= (prefixes & PREFIX_REPZ); ++ if (prefixes & PREFIX_REPZ) ++ suffix1 = 's'; ++ else ++ { ++ used_prefixes |= (prefixes & PREFIX_DATA); ++ if (prefixes & PREFIX_DATA) ++ suffix2 = 'd'; ++ else ++ { ++ used_prefixes |= (prefixes & PREFIX_REPNZ); ++ if (prefixes & PREFIX_REPNZ) ++ suffix1 = 's', suffix2 = 'd'; ++ } ++ } ++ sprintf (scratchbuf, "cmp%s%c%c", ++ simd_cmp_op[cmp_type], suffix1, suffix2); ++ used_prefixes |= (prefixes & PREFIX_REPZ); ++ oappend (scratchbuf); ++ } ++ else ++ { ++ /* We have a bad extension byte. Clean up. */ ++ op1out[0] = '\0'; ++ op2out[0] = '\0'; ++ BadOp (); ++ } ++} ++ ++static void ++SIMD_Fixup (int extrachar, int sizeflag ATTRIBUTE_UNUSED) ++{ ++ /* Change movlps/movhps to movhlps/movlhps for 2 register operand ++ forms of these instructions. */ ++ if (mod == 3) ++ { ++ char *p = obuf + strlen (obuf); ++ *(p + 1) = '\0'; ++ *p = *(p - 1); ++ *(p - 1) = *(p - 2); ++ *(p - 2) = *(p - 3); ++ *(p - 3) = extrachar; ++ } ++} ++ ++static void ++PNI_Fixup (int extrachar ATTRIBUTE_UNUSED, int sizeflag) ++{ ++ if (mod == 3 && reg == 1 && rm <= 1) ++ { ++ /* Override "sidt". */ ++ char *p = obuf + strlen (obuf) - 4; ++ ++ /* We might have a suffix when disassembling with -Msuffix. */ ++ if (*p == 'i') ++ --p; ++ ++ if (rm) ++ { ++ /* mwait %eax,%ecx */ ++ strcpy (p, "mwait"); ++ if (!intel_syntax) ++ strcpy (op1out, names32[0]); ++ } ++ else ++ { ++ /* monitor %eax,%ecx,%edx" */ ++ strcpy (p, "monitor"); ++ if (!intel_syntax) ++ { ++ if (!mode_64bit) ++ strcpy (op1out, names32[0]); ++ else if (!(prefixes & PREFIX_ADDR)) ++ strcpy (op1out, names64[0]); ++ else ++ { ++ strcpy (op1out, names32[0]); ++ used_prefixes |= PREFIX_ADDR; ++ } ++ strcpy (op3out, names32[2]); ++ } ++ } ++ if (!intel_syntax) ++ { ++ strcpy (op2out, names32[1]); ++ two_source_ops = 1; ++ } ++ ++ codep++; ++ } ++ else ++ OP_M (0, sizeflag); ++} ++ ++static void ++SVME_Fixup (int bytemode, int sizeflag) ++{ ++ const char *alt; ++ char *p; ++ ++ switch (*codep) ++ { ++ case 0xd8: ++ alt = "vmrun"; ++ break; ++ case 0xd9: ++ alt = "vmmcall"; ++ break; ++ case 0xda: ++ alt = "vmload"; ++ break; ++ case 0xdb: ++ alt = "vmsave"; ++ break; ++ case 0xdc: ++ alt = "stgi"; ++ break; ++ case 0xdd: ++ alt = "clgi"; ++ break; ++ case 0xde: ++ alt = "skinit"; ++ break; ++ case 0xdf: ++ alt = "invlpga"; ++ break; ++ default: ++ OP_M (bytemode, sizeflag); ++ return; ++ } ++ /* Override "lidt". */ ++ p = obuf + strlen (obuf) - 4; ++ /* We might have a suffix. */ ++ if (*p == 'i') ++ --p; ++ strcpy (p, alt); ++ if (!(prefixes & PREFIX_ADDR)) ++ { ++ ++codep; ++ return; ++ } ++ used_prefixes |= PREFIX_ADDR; ++ switch (*codep++) ++ { ++ case 0xdf: ++ strcpy (op2out, names32[1]); ++ two_source_ops = 1; ++ /* Fall through. */ ++ case 0xd8: ++ case 0xda: ++ case 0xdb: ++ *obufp++ = open_char; ++ if (mode_64bit || (sizeflag & AFLAG)) ++ alt = names32[0]; ++ else ++ alt = names16[0]; ++ strcpy (obufp, alt); ++ obufp += strlen (alt); ++ *obufp++ = close_char; ++ *obufp = '\0'; ++ break; ++ } ++} ++ ++static void ++INVLPG_Fixup (int bytemode, int sizeflag) ++{ ++ const char *alt; ++ ++ switch (*codep) ++ { ++ case 0xf8: ++ alt = "swapgs"; ++ break; ++ case 0xf9: ++ alt = "rdtscp"; ++ break; ++ default: ++ OP_M (bytemode, sizeflag); ++ return; ++ } ++ /* Override "invlpg". */ ++ strcpy (obuf + strlen (obuf) - 6, alt); ++ codep++; ++} ++ ++static void ++BadOp (void) ++{ ++ /* Throw away prefixes and 1st. opcode byte. */ ++ codep = insn_codep + 1; ++ oappend ("(bad)"); ++} ++ ++static void ++SEG_Fixup (int extrachar, int sizeflag) ++{ ++ if (mod == 3) ++ { ++ /* We need to add a proper suffix with ++ ++ movw %ds,%ax ++ movl %ds,%eax ++ movq %ds,%rax ++ movw %ax,%ds ++ movl %eax,%ds ++ movq %rax,%ds ++ */ ++ const char *suffix; ++ ++ if (prefixes & PREFIX_DATA) ++ suffix = "w"; ++ else ++ { ++ USED_REX (REX_MODE64); ++ if (rex & REX_MODE64) ++ suffix = "q"; ++ else ++ suffix = "l"; ++ } ++ strcat (obuf, suffix); ++ } ++ else ++ { ++ /* We need to fix the suffix for ++ ++ movw %ds,(%eax) ++ movw %ds,(%rax) ++ movw (%eax),%ds ++ movw (%rax),%ds ++ ++ Override "mov[l|q]". */ ++ char *p = obuf + strlen (obuf) - 1; ++ ++ /* We might not have a suffix. */ ++ if (*p == 'v') ++ ++p; ++ *p = 'w'; ++ } ++ ++ OP_E (extrachar, sizeflag); ++} ++ ++static void ++VMX_Fixup (int extrachar ATTRIBUTE_UNUSED, int sizeflag) ++{ ++ if (mod == 3 && reg == 0 && rm >=1 && rm <= 4) ++ { ++ /* Override "sgdt". */ ++ char *p = obuf + strlen (obuf) - 4; ++ ++ /* We might have a suffix when disassembling with -Msuffix. */ ++ if (*p == 'g') ++ --p; ++ ++ switch (rm) ++ { ++ case 1: ++ strcpy (p, "vmcall"); ++ break; ++ case 2: ++ strcpy (p, "vmlaunch"); ++ break; ++ case 3: ++ strcpy (p, "vmresume"); ++ break; ++ case 4: ++ strcpy (p, "vmxoff"); ++ break; ++ } ++ ++ codep++; ++ } ++ else ++ OP_E (0, sizeflag); ++} ++ ++static void ++OP_VMX (int bytemode, int sizeflag) ++{ ++ used_prefixes |= (prefixes & (PREFIX_DATA | PREFIX_REPZ)); ++ if (prefixes & PREFIX_DATA) ++ strcpy (obuf, "vmclear"); ++ else if (prefixes & PREFIX_REPZ) ++ strcpy (obuf, "vmxon"); ++ else ++ strcpy (obuf, "vmptrld"); ++ OP_E (bytemode, sizeflag); ++} +--- a/arch/x86/kernel/apic/io_apic.c ++++ b/arch/x86/kernel/apic/io_apic.c +@@ -42,6 +42,9 @@ + #include + #include + #include ++#ifdef CONFIG_KDB ++#include ++#endif + + #include + #include +@@ -1193,6 +1196,11 @@ next: + if (test_bit(vector, used_vectors)) + goto next; + ++#ifdef CONFIG_KDB ++ if (vector == KDBENTER_VECTOR) ++ goto next; ++#endif ++ + for_each_cpu_and(new_cpu, tmp_mask, cpu_online_mask) + if (per_cpu(vector_irq, new_cpu)[vector] != -1) + goto next; +--- a/arch/x86/kernel/dumpstack.c ++++ b/arch/x86/kernel/dumpstack.c +@@ -15,6 +15,9 @@ + #include + #include + #include ++#ifdef CONFIG_KDB ++#include ++#endif + + #include + +@@ -260,6 +263,9 @@ void __kprobes oops_end(unsigned long fl + /* Nest count reaches zero, release the lock. */ + arch_spin_unlock(&die_lock); + raw_local_irq_restore(flags); ++#ifdef CONFIG_KB ++ kdb(KDB_REASON_OOPS, signr, regs); ++#endif + oops_exit(); + + if (!signr) +@@ -328,6 +334,9 @@ void die(const char *str, struct pt_regs + + if (__die(str, regs, err)) + sig = 0; ++#ifdef CONFIG_KDB ++ kdb_diemsg = str; ++#endif + oops_end(flags, regs, sig); + } + +@@ -348,6 +357,9 @@ die_nmi(char *str, struct pt_regs *regs, + printk(" on CPU%d, ip %08lx, registers:\n", + smp_processor_id(), regs->ip); + show_registers(regs); ++#ifdef CONFIG_KDB ++ kdb(KDB_REASON_NMI, 0, regs); ++#endif + oops_end(flags, regs, 0); + if (do_panic || panic_on_oops) + panic("Non maskable interrupt"); +--- a/arch/x86/kernel/entry_32.S ++++ b/arch/x86/kernel/entry_32.S +@@ -1008,6 +1008,26 @@ ENTRY(alignment_check) + CFI_ENDPROC + END(alignment_check) + ++#ifdef CONFIG_KDB ++ ++ENTRY(kdb_call) ++ RING0_INT_FRAME ++ pushl %eax # save orig EAX ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ movl %esp,%ecx # struct pt_regs ++ movl $0,%edx # error_code ++ movl $1,%eax # KDB_REASON_ENTER ++ call kdb ++ jmp restore_all ++ CFI_ENDPROC ++ ++#ifdef CONFIG_SMP ++BUILD_INTERRUPT(kdb_interrupt,KDB_VECTOR) ++#endif /* CONFIG_SMP */ ++ ++#endif /* CONFIG_KDB */ ++ + ENTRY(divide_error) + RING0_INT_FRAME + pushl $0 # no error code +--- a/arch/x86/kernel/entry_64.S ++++ b/arch/x86/kernel/entry_64.S +@@ -1331,6 +1331,33 @@ END(xen_failsafe_callback) + + #endif /* CONFIG_XEN */ + ++#ifdef CONFIG_KDB ++ ++#ifdef CONFIG_SMP ++apicinterrupt KDB_VECTOR \ ++ kdb_interrupt, smp_kdb_interrupt ++#endif /* CONFIG_SMP */ ++ ++ENTRY(kdb_call) ++ INTR_FRAME ++ cld ++ pushq $-1 # orig_eax ++ CFI_ADJUST_CFA_OFFSET 8 ++ SAVE_ALL ++ movq $1,%rdi # KDB_REASON_ENTER ++ movq $0,%rsi # error_code ++ movq %rsp,%rdx # struct pt_regs ++ call kdb ++ RESTORE_ALL ++ addq $8,%rsp # forget orig_eax ++ CFI_ADJUST_CFA_OFFSET -8 ++ iretq ++ CFI_ENDPROC ++END(kdb_call) ++ ++#endif /* CONFIG_KDB */ ++ ++ + /* + * Some functions should be protected against kprobes + */ +--- a/arch/x86/kernel/reboot.c ++++ b/arch/x86/kernel/reboot.c +@@ -3,6 +3,10 @@ + #include + #include + #include ++#ifdef CONFIG_KDB ++#include ++#endif /* CONFIG_KDB */ ++#include + #include + #include + #include +@@ -630,6 +634,14 @@ void native_machine_shutdown(void) + /* Make certain I only run on the appropriate processor */ + set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id)); + ++#if defined(CONFIG_X86_32) && defined(CONFIG_KDB) ++ /* ++ * If this restart is occuring while kdb is running (e.g. reboot ++ * command), the other CPU's are already stopped. Don't try to ++ * stop them yet again. ++ */ ++ if (!KDB_IS_RUNNING()) ++#endif /* defined(CONFIG_X86_32) && defined(CONFIG_KDB) */ + /* O.K Now that I'm on the appropriate processor, + * stop all of the others. + */ +@@ -740,6 +752,29 @@ static nmi_shootdown_cb shootdown_callba + + static atomic_t waiting_for_crash_ipi; + ++#ifdef CONFIG_KDB_KDUMP ++void halt_current_cpu(struct pt_regs *regs) ++{ ++#ifdef CONFIG_X86_32 ++ struct pt_regs fixed_regs; ++#endif ++ local_irq_disable(); ++#ifdef CONFIG_X86_32 ++ if (!user_mode_vm(regs)) { ++ crash_fixup_ss_esp(&fixed_regs, regs); ++ regs = &fixed_regs; ++ } ++#endif ++ crash_save_cpu(regs, raw_smp_processor_id()); ++ disable_local_APIC(); ++ atomic_dec(&waiting_for_crash_ipi); ++ /* Assume hlt works */ ++ halt(); ++ for(;;) ++ cpu_relax(); ++} ++#endif /* CONFIG_KDB_KDUMP */ ++ + static int crash_nmi_callback(struct notifier_block *self, + unsigned long val, void *data) + { +--- a/arch/x86/kernel/traps.c ++++ b/arch/x86/kernel/traps.c +@@ -44,6 +44,10 @@ + #include + #endif + ++#ifdef CONFIG_KDB ++#include ++#endif /* CONFIG_KDB */ ++ + #include + #include + #include +@@ -361,6 +365,10 @@ io_check_error(unsigned char reason, str + static notrace __kprobes void + unknown_nmi_error(unsigned char reason, struct pt_regs *regs) + { ++#ifdef CONFIG_KDB ++ (void)kdb(KDB_REASON_NMI, reason, regs); ++#endif /* CONFIG_KDB */ ++ + if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == + NOTIFY_STOP) + return; +@@ -396,6 +404,16 @@ static notrace __kprobes void default_do + if (!cpu) + reason = get_nmi_reason(); + ++#if defined(CONFIG_SMP) && defined(CONFIG_KDB) ++ /* ++ * Call the kernel debugger to see if this NMI is due ++ * to an KDB requested IPI. If so, kdb will handle it. ++ */ ++ if (kdb_ipi(regs, NULL)) { ++ return; ++ } ++#endif /* defined(CONFIG_SMP) && defined(CONFIG_KDB) */ ++ + if (!(reason & 0xc0)) { + if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT) + == NOTIFY_STOP) +@@ -460,6 +478,10 @@ void restart_nmi(void) + /* May run on IST stack. */ + dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) + { ++#ifdef CONFIG_KDB ++ if (kdb(KDB_REASON_BREAK, error_code, regs)) ++ return; ++#endif + #ifdef CONFIG_KPROBES + if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) + == NOTIFY_STOP) +@@ -552,6 +574,11 @@ dotraplinkage void __kprobes do_debug(st + /* Store the virtualized DR6 value */ + tsk->thread.debugreg6 = dr6; + ++#ifdef CONFIG_KDB ++ if (kdb(KDB_REASON_DEBUG, error_code, regs)) ++ return; ++#endif /* CONFIG_KDB */ ++ + if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code, + SIGTRAP) == NOTIFY_STOP) + return; diff --git a/patches.suse/kdb-x86-build-fixes b/patches.suse/kdb-x86-build-fixes new file mode 100644 index 0000000..216b695 --- /dev/null +++ b/patches.suse/kdb-x86-build-fixes @@ -0,0 +1,19 @@ +From: Jeff Mahoney +Subject: kdb: Use $srctree not $TOPDIR in Makefile +Patch-mainline: not yet, whenever KDB is upstream + + $TOPDIR doesn't work when building out of tree. Use $srctree instead. + +Signed-off-by: Jeff Mahoney +--- + arch/x86/kdb/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/x86/kdb/Makefile ++++ b/arch/x86/kdb/Makefile +@@ -26,4 +26,4 @@ CFLAGS_kdba_bt.o += -DREGPARM=$(REGPARM) + + override CFLAGS := $(CFLAGS:%-pg=% ) + +-CFLAGS_kdba_io.o += -I $(TOPDIR)/arch/$(SRCARCH)/kdb ++CFLAGS_kdba_io.o += -I $(srctree)/arch/$(SRCARCH)/kdb diff --git a/patches.suse/kdb_dont_touch_i8042_early.patch b/patches.suse/kdb_dont_touch_i8042_early.patch new file mode 100644 index 0000000..acf9789 --- /dev/null +++ b/patches.suse/kdb_dont_touch_i8042_early.patch @@ -0,0 +1,26 @@ +From: Thomas Renninger +Subject: Avoid early hang when i8042 controller is missing +Patch-Mainline: no +References: bnc#528811 + +On latest machines without i8042 controller LED control +will hang when BIOS option "PORT 60/64 emulation" is enabled +(which is required for specific extra storage BIOS to work). + +Workaround for now: Don't touch LEDs at all. + +--- + arch/x86/kdb/kdba_io.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/x86/kdb/kdba_io.c ++++ b/arch/x86/kdb/kdba_io.c +@@ -23,7 +23,7 @@ + #include "pc_keyb.h" + + #ifdef CONFIG_VT_CONSOLE +-#define KDB_BLINK_LED 1 ++#undef KDB_BLINK_LED + #else + #undef KDB_BLINK_LED + #endif diff --git a/patches.suse/kdb_fix_ia64_build.patch b/patches.suse/kdb_fix_ia64_build.patch new file mode 100644 index 0000000..9860077 --- /dev/null +++ b/patches.suse/kdb_fix_ia64_build.patch @@ -0,0 +1,19 @@ +From: Thomas Renninger +Subject: Fix ia64 - Export kdb_usb_kbds +Patch-Mainline: no +References: none + +--- + arch/ia64/kdb/kdba_io.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/ia64/kdb/kdba_io.c ++++ b/arch/ia64/kdb/kdba_io.c +@@ -43,6 +43,7 @@ + /* support up to 8 USB keyboards (probably excessive, but...) */ + #define KDB_USB_NUM_KEYBOARDS 8 + struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS]; ++EXPORT_SYMBOL(kdb_usb_kbds); + + extern int kdb_no_usb; + diff --git a/patches.suse/kdump-dump_after_notifier.patch b/patches.suse/kdump-dump_after_notifier.patch new file mode 100644 index 0000000..7c58853 --- /dev/null +++ b/patches.suse/kdump-dump_after_notifier.patch @@ -0,0 +1,136 @@ +From: Takenori Nagano +Subject: [PATCH] Add dump_after_notifier sysctl +Patch-mainline: never +References: 265764 + +This patch adds dump_after_notifier sysctl to execute kdump after the notifier +call chain. This basically makes it possible to execute KDB before kdump. + +Signed-off-by: Takenori Nagano +Acked-by: Bernhard Walle + +--- + include/linux/kexec.h | 2 ++ + include/linux/sysctl.h | 1 + + kernel/kexec.c | 29 +++++++++++++++++++++++++++++ + kernel/panic.c | 5 ++++- + kernel/sysctl_check.c | 1 + + 5 files changed, 37 insertions(+), 1 deletion(-) + +--- a/include/linux/kexec.h ++++ b/include/linux/kexec.h +@@ -158,6 +158,7 @@ unsigned long paddr_vmcoreinfo_note(void + + extern struct kimage *kexec_image; + extern struct kimage *kexec_crash_image; ++extern int dump_after_notifier; + + #ifndef kexec_flush_icache_page + #define kexec_flush_icache_page(page) +@@ -212,5 +213,6 @@ struct pt_regs; + struct task_struct; + static inline void crash_kexec(struct pt_regs *regs) { } + static inline int kexec_should_crash(struct task_struct *p) { return 0; } ++#define dump_after_notifier 0 + #endif /* CONFIG_KEXEC */ + #endif /* LINUX_KEXEC_H */ +--- a/include/linux/sysctl.h ++++ b/include/linux/sysctl.h +@@ -162,6 +162,7 @@ enum + KERN_MAX_LOCK_DEPTH=74, + KERN_NMI_WATCHDOG=75, /* int: enable/disable nmi watchdog */ + KERN_PANIC_ON_NMI=76, /* int: whether we will panic on an unrecovered */ ++ KERN_DUMP_AFTER_NOTIFIER=78, /* int: kdump after panic_notifier (SUSE only) */ + KERN_PANIC_ON_IO_NMI=79, /* int: whether we will panic on an io NMI */ + }; + +--- a/kernel/kexec.c ++++ b/kernel/kexec.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -46,6 +47,7 @@ + + /* Per cpu memory for storing cpu states in case of system crash. */ + note_buf_t* crash_notes; ++int dump_after_notifier; + + /* vmcoreinfo stuff */ + static unsigned char vmcoreinfo_data[VMCOREINFO_BYTES]; +@@ -1152,6 +1154,30 @@ void crash_save_cpu(struct pt_regs *regs + final_note(buf); + } + ++#ifdef CONFIG_SYSCTL ++static ctl_table dump_after_notifier_table[] = { ++ { ++ .ctl_name = KERN_DUMP_AFTER_NOTIFIER, ++ .procname = "dump_after_notifier", ++ .data = &dump_after_notifier, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec, ++ }, ++ { .ctl_name = 0 } ++}; ++ ++static ctl_table kexec_sys_table[] = { ++ { ++ .ctl_name = CTL_KERN, ++ .procname = "kernel", ++ .mode = 0555, ++ .child = dump_after_notifier_table, ++ }, ++ { .ctl_name = 0 } ++}; ++#endif ++ + static int __init crash_notes_memory_init(void) + { + /* Allocate memory for saving cpu registers. */ +@@ -1161,6 +1187,9 @@ static int __init crash_notes_memory_ini + " states failed\n"); + return -ENOMEM; + } ++#ifdef CONFIG_SYSCTL ++ register_sysctl_table(kexec_sys_table); ++#endif + return 0; + } + module_init(crash_notes_memory_init) +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -87,7 +87,8 @@ NORET_TYPE void panic(const char * fmt, + * everything else. + * Do we want to call this before we try to display a message? + */ +- crash_kexec(NULL); ++ if (!dump_after_notifier) ++ crash_kexec(NULL); + + /* + * Note smp_send_stop is the usual smp shutdown function, which +@@ -98,6 +99,8 @@ NORET_TYPE void panic(const char * fmt, + + atomic_notifier_call_chain(&panic_notifier_list, 0, buf); + ++ crash_kexec(NULL); ++ + bust_spinlocks(0); + + if (!panic_blink) +--- a/kernel/sysctl_check.c ++++ b/kernel/sysctl_check.c +@@ -106,6 +106,7 @@ static const struct trans_ctl_table tran + { KERN_PANIC_ON_NMI, "panic_on_unrecovered_nmi" }, + { KERN_PANIC_ON_IO_NMI, "panic_on_io_nmi" }, + { KERN_SETUID_DUMPABLE, "suid_dumpable" }, ++ { KERN_DUMP_AFTER_NOTIFIER, "dump_after_notifier" }, + {} + }; + diff --git a/patches.suse/led_classdev.sysfs-name.patch b/patches.suse/led_classdev.sysfs-name.patch new file mode 100644 index 0000000..8291e30 --- /dev/null +++ b/patches.suse/led_classdev.sysfs-name.patch @@ -0,0 +1,22 @@ +Subject: use correct name for /sys/devices/virtual/leds/ entries +From: olh@suse.de +References: 468350 +Patch-mainline: not yet + +the low hanging fruits + +--- + drivers/leds/ledtrig-default-on.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/leds/ledtrig-default-on.c ++++ b/drivers/leds/ledtrig-default-on.c +@@ -23,7 +23,7 @@ static void defon_trig_activate(struct l + } + + static struct led_trigger defon_led_trigger = { +- .name = "default-on", ++ .name = "default::on", + .activate = defon_trig_activate, + }; + diff --git a/patches.suse/linux-2.6.29-dont-wait-for-mouse.patch b/patches.suse/linux-2.6.29-dont-wait-for-mouse.patch new file mode 100644 index 0000000..533ab9e --- /dev/null +++ b/patches.suse/linux-2.6.29-dont-wait-for-mouse.patch @@ -0,0 +1,46 @@ +From dce8113d033975f56630cf6d2a6a908cfb66059d Mon Sep 17 00:00:00 2001 +From: Arjan van de Ven +Date: Sun, 20 Jul 2008 13:12:16 -0700 +Subject: [PATCH] fastboot: remove "wait for all devices before mounting root" delay +Patch-mainline: not yet + +In the non-initrd case, we wait for all devices to finish their +probing before we try to mount the rootfs. +In practice, this means that we end up waiting 2 extra seconds for +the PS/2 mouse probing even though the root holding device has been +ready since a long time. + +The previous two patches in this series made the RAID autodetect code +do it's own "wait for probing to be done" code, and added +"wait and retry" functionality in case the root device isn't actually +available. + +These two changes should make it safe to remove the delay itself, +and this patch does this. On my test laptop, this reduces the boot time +by 2 seconds (kernel time goes from 3.9 to 1.9 seconds). + +Signed-off-by: Arjan van de Ven +Signed-off-by: Greg Kroah-Hartman +--- + init/do_mounts.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -372,6 +372,7 @@ void __init prepare_namespace(void) + ssleep(root_delay); + } + ++#if 0 + /* + * wait for the known devices to complete their probing + * +@@ -380,6 +381,8 @@ void __init prepare_namespace(void) + * for the touchpad of a laptop to initialize. + */ + wait_for_device_probe(); ++#endif ++ async_synchronize_full(); + + md_run_setup(); + diff --git a/patches.suse/linux-2.6.29-enable-async-by-default.patch b/patches.suse/linux-2.6.29-enable-async-by-default.patch new file mode 100644 index 0000000..76d14b6 --- /dev/null +++ b/patches.suse/linux-2.6.29-enable-async-by-default.patch @@ -0,0 +1,22 @@ +From: Arjan van de Ven +Subject: enable async_enabled by default +Patch-mainline: not yet + + +Signed-off-by: Greg Kroah-Hartman + +--- + kernel/async.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/kernel/async.c ++++ b/kernel/async.c +@@ -67,7 +67,7 @@ static LIST_HEAD(async_pending); + static LIST_HEAD(async_running); + static DEFINE_SPINLOCK(async_lock); + +-static int async_enabled = 0; ++static int async_enabled = 1; + + struct async_entry { + struct list_head list; diff --git a/patches.suse/linux-2.6.29-even-faster-kms.patch b/patches.suse/linux-2.6.29-even-faster-kms.patch new file mode 100644 index 0000000..5b5bff2 --- /dev/null +++ b/patches.suse/linux-2.6.29-even-faster-kms.patch @@ -0,0 +1,30 @@ +From: someone at intel +Subject: speed up kms even more + + +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/i915/intel_lvds.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/i915/intel_lvds.c ++++ b/drivers/gpu/drm/i915/intel_lvds.c +@@ -551,7 +551,7 @@ static void intel_lvds_prepare(struct dr + dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + +- intel_lvds_set_power(dev, false); ++// intel_lvds_set_power(dev, false); + } + + static void intel_lvds_commit( struct drm_encoder *encoder) +@@ -563,7 +563,7 @@ static void intel_lvds_commit( struct dr + dev_priv->backlight_duty_cycle = + intel_lvds_get_max_backlight(dev); + +- intel_lvds_set_power(dev, true); ++// intel_lvds_set_power(dev, true); + } + + static void intel_lvds_mode_set(struct drm_encoder *encoder, diff --git a/patches.suse/linux-2.6.29-jbd-longer-commit-interval.patch b/patches.suse/linux-2.6.29-jbd-longer-commit-interval.patch new file mode 100644 index 0000000..824491e --- /dev/null +++ b/patches.suse/linux-2.6.29-jbd-longer-commit-interval.patch @@ -0,0 +1,26 @@ +From 0143f8eb8afcaccba5a78196fb3db4361e0097a7 Mon Sep 17 00:00:00 2001 +From: Arjan van de Ven +Date: Mon, 9 Feb 2009 21:25:32 -0800 +Subject: [PATCH] jbd: longer commit interval +Patch-mainline: not yet + +... 5 seconds is rather harsh on ssd's.. + +Signed-off-by: Arjan van de Ven +Signed-off-by: Greg Kroah-Hartman + +--- + include/linux/jbd.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/jbd.h ++++ b/include/linux/jbd.h +@@ -46,7 +46,7 @@ + /* + * The default maximum commit age, in seconds. + */ +-#define JBD_DEFAULT_MAX_COMMIT_AGE 5 ++#define JBD_DEFAULT_MAX_COMMIT_AGE 15 + + #ifdef CONFIG_JBD_DEBUG + /* diff --git a/patches.suse/linux-2.6.29-kms-after-sata.patch b/patches.suse/linux-2.6.29-kms-after-sata.patch new file mode 100644 index 0000000..17cab4b --- /dev/null +++ b/patches.suse/linux-2.6.29-kms-after-sata.patch @@ -0,0 +1,46 @@ +From: someone at intel +Subject: make kms happen after sata +Patch-mainline: not yet + +I guess this speeds something up, would be nice if we had some descriptions here... + + +Signed-off-by: Greg Kroah-Hartman + + +--- + drivers/Makefile | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -26,15 +26,8 @@ obj-$(CONFIG_REGULATOR) += regulator/ + # default. + obj-y += char/ + +-# gpu/ comes after char for AGP vs DRM startup +-obj-y += gpu/ +- + obj-$(CONFIG_CONNECTOR) += connector/ + +-# i810fb and intelfb depend on char/agp/ +-obj-$(CONFIG_FB_I810) += video/i810/ +-obj-$(CONFIG_FB_INTEL) += video/intelfb/ +- + obj-y += serial/ + obj-$(CONFIG_PARPORT) += parport/ + obj-y += base/ block/ misc/ mfd/ +@@ -46,6 +39,13 @@ obj-$(CONFIG_ATA) += ata/ + obj-$(CONFIG_MTD) += mtd/ + obj-$(CONFIG_SPI) += spi/ + obj-y += net/ ++ ++# gpu/ comes after char for AGP vs DRM startup ++obj-y += gpu/ ++# i810fb and intelfb depend on char/agp/ ++obj-$(CONFIG_FB_I810) += video/i810/ ++obj-$(CONFIG_FB_INTEL) += video/intelfb/ ++ + obj-$(CONFIG_ATM) += atm/ + obj-$(CONFIG_FUSION) += message/ + obj-$(CONFIG_FIREWIRE) += firewire/ diff --git a/patches.suse/linux-2.6.29-silence-acer-message.patch b/patches.suse/linux-2.6.29-silence-acer-message.patch new file mode 100644 index 0000000..b151702 --- /dev/null +++ b/patches.suse/linux-2.6.29-silence-acer-message.patch @@ -0,0 +1,24 @@ +From: Arjan van de Ven +Subject: Silence acer wmi driver on non-acer machines +Date: Fri, 23 Jan 2009 +Patch-mainline: Not yet + +Small fix changing error msg to info msg in acer wmi driver + +Signed-off-by: Greg Kroah-Hartman +--- +--- + drivers/platform/x86/acer-wmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/platform/x86/acer-wmi.c ++++ b/drivers/platform/x86/acer-wmi.c +@@ -1313,7 +1313,7 @@ static int __init acer_wmi_init(void) + AMW0_find_mailled(); + + if (!interface) { +- printk(ACER_ERR "No or unsupported WMI interface, unable to " ++ printk(ACER_INFO "No or unsupported WMI interface, unable to " + "load\n"); + return -ENODEV; + } diff --git a/patches.suse/linux-2.6.29-touchkit.patch b/patches.suse/linux-2.6.29-touchkit.patch new file mode 100644 index 0000000..773bb1d --- /dev/null +++ b/patches.suse/linux-2.6.29-touchkit.patch @@ -0,0 +1,135 @@ +From: someone at intel +Subject: some new touch screen device ids +Patch-mainline: not yet + + +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/input/mouse/psmouse-base.c | 9 +++++++ + drivers/input/mouse/psmouse.h | 1 + drivers/input/mouse/touchkit_ps2.c | 45 +++++++++++++++++++++++++++++++++++-- + drivers/input/mouse/touchkit_ps2.h | 6 ++++ + 4 files changed, 59 insertions(+), 2 deletions(-) + +--- a/drivers/input/mouse/psmouse-base.c ++++ b/drivers/input/mouse/psmouse-base.c +@@ -703,6 +703,9 @@ static int psmouse_extensions(struct psm + + if (touchkit_ps2_detect(psmouse, set_properties) == 0) + return PSMOUSE_TOUCHKIT_PS2; ++ ++ if (elftouch_ps2_detect(psmouse, set_properties) == 0) ++ return PSMOUSE_ELFTOUCH_PS2; + } + + /* +@@ -828,6 +831,12 @@ static const struct psmouse_protocol psm + .alias = "trackpoint", + .detect = trackpoint_detect, + }, ++ { ++ .type = PSMOUSE_ELFTOUCH_PS2, ++ .name = "elftouchPS2", ++ .alias = "elftouch", ++ .detect = elftouch_ps2_detect, ++ }, + #endif + #ifdef CONFIG_MOUSE_PS2_TOUCHKIT + { +--- a/drivers/input/mouse/psmouse.h ++++ b/drivers/input/mouse/psmouse.h +@@ -89,6 +89,7 @@ enum psmouse_type { + PSMOUSE_TRACKPOINT, + PSMOUSE_TOUCHKIT_PS2, + PSMOUSE_CORTRON, ++ PSMOUSE_ELFTOUCH_PS2, + PSMOUSE_HGPK, + PSMOUSE_ELANTECH, + PSMOUSE_FSP, +--- a/drivers/input/mouse/touchkit_ps2.c ++++ b/drivers/input/mouse/touchkit_ps2.c +@@ -51,6 +51,11 @@ + #define TOUCHKIT_GET_X(packet) (((packet)[1] << 7) | (packet)[2]) + #define TOUCHKIT_GET_Y(packet) (((packet)[3] << 7) | (packet)[4]) + ++#define ELFTOUCH_MAX_XC 0x0fff ++#define ELFTOUCH_MAX_YC 0x0fff ++#define ELFTOUCH_GET_X(packet) (((packet)[3] << 7) | (packet)[4]) ++#define ELFTOUCH_GET_Y(packet) (((packet)[1] << 7) | (packet)[2]) ++ + static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse) + { + unsigned char *packet = psmouse->packet; +@@ -59,9 +64,15 @@ static psmouse_ret_t touchkit_ps2_proces + if (psmouse->pktcnt != 5) + return PSMOUSE_GOOD_DATA; + +- input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet)); +- input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet)); ++ if(psmouse->type==PSMOUSE_ELFTOUCH_PS2) { ++ input_report_abs(dev, ABS_X, ELFTOUCH_GET_X(packet)); ++ input_report_abs(dev, ABS_Y, ELFTOUCH_GET_Y(packet)); ++ } else { ++ input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet)); ++ input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet)); ++ } + input_report_key(dev, BTN_TOUCH, TOUCHKIT_GET_TOUCHED(packet)); ++ + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +@@ -99,3 +110,33 @@ int touchkit_ps2_detect(struct psmouse * + + return 0; + } ++ ++int elftouch_ps2_detect(struct psmouse *psmouse, bool set_properties) ++{ ++ struct input_dev *dev = psmouse->dev; ++ unsigned char param[16]; ++ int command, res; ++ ++ param[0]=0x0f4; ++ command = TOUCHKIT_SEND_PARMS(1, 0, TOUCHKIT_CMD); ++ res=ps2_command(&psmouse->ps2dev, param, command); ++ if(res) { return -ENODEV; } ++ ++ param[0]=0x0b0; ++ command = TOUCHKIT_SEND_PARMS(1, 1, TOUCHKIT_CMD); ++ res=ps2_command(&psmouse->ps2dev, param, command); ++ if(res) { return -ENODEV; } ++ ++ if (set_properties) { ++ dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); ++ set_bit(BTN_TOUCH, dev->keybit); ++ input_set_abs_params(dev, ABS_X, 0, ELFTOUCH_MAX_XC, 0, 0); ++ input_set_abs_params(dev, ABS_Y, 0, ELFTOUCH_MAX_YC, 0, 0); ++ ++ psmouse->vendor = "ElfTouch"; ++ psmouse->name = "Touchscreen"; ++ psmouse->protocol_handler = touchkit_ps2_process_byte; ++ psmouse->pktsize = 5; ++ } ++ return 0; ++} +--- a/drivers/input/mouse/touchkit_ps2.h ++++ b/drivers/input/mouse/touchkit_ps2.h +@@ -14,11 +14,17 @@ + + #ifdef CONFIG_MOUSE_PS2_TOUCHKIT + int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties); ++int elftouch_ps2_detect(struct psmouse *psmouse, bool set_properties); + #else + static inline int touchkit_ps2_detect(struct psmouse *psmouse, + bool set_properties) + { + return -ENOSYS; ++} ++static inline int elftouch_ps2_detect(struct psmouse *psmouse, ++ bool set_properties) ++{ ++ return -ENOSYS; + } + #endif /* CONFIG_MOUSE_PS2_TOUCHKIT */ + diff --git a/patches.suse/mm-devzero-optimisation.patch b/patches.suse/mm-devzero-optimisation.patch new file mode 100644 index 0000000..cf4f58c --- /dev/null +++ b/patches.suse/mm-devzero-optimisation.patch @@ -0,0 +1,260 @@ +From: Nick Piggin +Subject: mm: /dev/zero optimisation +References: bnc#430738 +Patch-mainline: no (could be submit) + +Patch for removal of ZERO_PAGE from main VM paths also removed the +/dev/zero optimisation to map directly from ZERO_PAGE when doing +mmap() and also the interesting read(2) "hack" where the MMU was +used to make zero-filling the target buffer zero-copy. + +Some benchmarks have run into issues with this. Customers sometimes +use these benchmarks to qualify and test systems, so even if the +benchmarks themselves are "stupid", it saves some trouble to retain +this optimisation for them. Also, while I don't think it was established +that there is a "real" workload where this helps, but it can't be proven +that one does not exist. + +Signed-off-by: Nick Piggin +--- + drivers/char/mem.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++- + include/linux/mm.h | 2 + + mm/memory.c | 87 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 193 insertions(+), 1 deletion(-) + +--- a/drivers/char/mem.c ++++ b/drivers/char/mem.c +@@ -637,6 +637,100 @@ static ssize_t splice_write_null(struct + return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null); + } + ++#if 1 //ndef CONFIG_XEN ++/* ++ * For fun, we are using the MMU for this. ++ */ ++static inline size_t read_zero_pagealigned(char __user * buf, size_t size) ++{ ++ struct mm_struct *mm; ++ struct vm_area_struct * vma; ++ unsigned long addr=(unsigned long)buf; ++ ++ mm = current->mm; ++ /* Oops, this was forgotten before. -ben */ ++ down_read(&mm->mmap_sem); ++ ++ /* For private mappings, just map in zero pages. */ ++ for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { ++ unsigned long count; ++ ++ if (vma->vm_start > addr || (vma->vm_flags & VM_WRITE) == 0) ++ goto out_up; ++ if (vma->vm_flags & (VM_SHARED | VM_HUGETLB)) ++ break; ++ count = vma->vm_end - addr; ++ if (count > size) ++ count = size; ++ ++ zap_page_range(vma, addr, count, NULL); ++ if (zeromap_page_range(vma, addr, count, PAGE_COPY)) ++ break; ++ ++ size -= count; ++ buf += count; ++ addr += count; ++ if (size == 0) ++ goto out_up; ++ } ++ ++ up_read(&mm->mmap_sem); ++ ++ /* The shared case is hard. Let's do the conventional zeroing. */ ++ do { ++ unsigned long unwritten = clear_user(buf, PAGE_SIZE); ++ if (unwritten) ++ return size + unwritten - PAGE_SIZE; ++ cond_resched(); ++ buf += PAGE_SIZE; ++ size -= PAGE_SIZE; ++ } while (size); ++ ++ return size; ++out_up: ++ up_read(&mm->mmap_sem); ++ return size; ++} ++ ++static ssize_t read_zero(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ unsigned long left, unwritten, written = 0; ++ ++ if (!count) ++ return 0; ++ ++ if (!access_ok(VERIFY_WRITE, buf, count)) ++ return -EFAULT; ++ ++ left = count; ++ ++ /* do we want to be clever? Arbitrary cut-off */ ++ if (count >= PAGE_SIZE*4) { ++ unsigned long partial; ++ ++ /* How much left of the page? */ ++ partial = (PAGE_SIZE-1) & -(unsigned long) buf; ++ unwritten = clear_user(buf, partial); ++ written = partial - unwritten; ++ if (unwritten) ++ goto out; ++ left -= partial; ++ buf += partial; ++ unwritten = read_zero_pagealigned(buf, left & PAGE_MASK); ++ written += (left & PAGE_MASK) - unwritten; ++ if (unwritten) ++ goto out; ++ buf += left & PAGE_MASK; ++ left &= ~PAGE_MASK; ++ } ++ unwritten = clear_user(buf, left); ++ written += left - unwritten; ++out: ++ return written ? written : -EFAULT; ++} ++ ++#else /* CONFIG_XEN */ + static ssize_t read_zero(struct file *file, char __user *buf, + size_t count, loff_t *ppos) + { +@@ -667,15 +761,24 @@ static ssize_t read_zero(struct file *fi + } + return written ? written : -EFAULT; + } ++#endif /* CONFIG_XEN */ + + static int mmap_zero(struct file *file, struct vm_area_struct *vma) + { ++ int err = 0; ++ + #ifndef CONFIG_MMU + return -ENOSYS; + #endif ++ + if (vma->vm_flags & VM_SHARED) + return shmem_zero_setup(vma); +- return 0; ++#if 1 //ndef CONFIG_XEN ++ err = zeromap_page_range(vma, vma->vm_start, ++ vma->vm_end - vma->vm_start, vma->vm_page_prot); ++ BUG_ON(err == -EEXIST); ++#endif ++ return err; + } + + static ssize_t write_full(struct file *file, const char __user *buf, +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -795,6 +795,8 @@ void free_pgd_range(struct mmu_gather *t + unsigned long end, unsigned long floor, unsigned long ceiling); + int copy_page_range(struct mm_struct *dst, struct mm_struct *src, + struct vm_area_struct *vma); ++int zeromap_page_range(struct vm_area_struct *vma, unsigned long from, ++ unsigned long size, pgprot_t prot); + void unmap_mapping_range(struct address_space *mapping, + loff_t const holebegin, loff_t const holelen, int even_cows); + int follow_pfn(struct vm_area_struct *vma, unsigned long address, +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -1572,6 +1572,93 @@ struct page *get_dump_page(unsigned long + } + #endif /* CONFIG_ELF_CORE */ + ++static int zeromap_pte_range(struct mm_struct *mm, pmd_t *pmd, ++ unsigned long addr, unsigned long end, pgprot_t prot) ++{ ++ pte_t *pte; ++ spinlock_t *ptl; ++ int err = 0; ++ ++ pte = pte_alloc_map_lock(mm, pmd, addr, &ptl); ++ if (!pte) ++ return -EAGAIN; ++ arch_enter_lazy_mmu_mode(); ++ do { ++ pte_t zero_pte; ++ ++ if (unlikely(!pte_none(*pte))) { ++ err = -EEXIST; ++ pte++; ++ break; ++ } ++ zero_pte = pte_mkspecial(pfn_pte(my_zero_pfn(addr), prot)); ++ zero_pte = pte_wrprotect(zero_pte); ++ set_pte_at(mm, addr, pte, zero_pte); ++ } while (pte++, addr += PAGE_SIZE, addr != end); ++ arch_leave_lazy_mmu_mode(); ++ pte_unmap_unlock(pte - 1, ptl); ++ return err; ++} ++ ++static inline int zeromap_pmd_range(struct mm_struct *mm, pud_t *pud, ++ unsigned long addr, unsigned long end, pgprot_t prot) ++{ ++ pmd_t *pmd; ++ unsigned long next; ++ int err; ++ ++ pmd = pmd_alloc(mm, pud, addr); ++ if (!pmd) ++ return -EAGAIN; ++ do { ++ next = pmd_addr_end(addr, end); ++ err = zeromap_pte_range(mm, pmd, addr, next, prot); ++ if (err) ++ break; ++ } while (pmd++, addr = next, addr != end); ++ return err; ++} ++ ++static inline int zeromap_pud_range(struct mm_struct *mm, pgd_t *pgd, ++ unsigned long addr, unsigned long end, pgprot_t prot) ++{ ++ pud_t *pud; ++ unsigned long next; ++ int err; ++ ++ pud = pud_alloc(mm, pgd, addr); ++ if (!pud) ++ return -EAGAIN; ++ do { ++ next = pud_addr_end(addr, end); ++ err = zeromap_pmd_range(mm, pud, addr, next, prot); ++ if (err) ++ break; ++ } while (pud++, addr = next, addr != end); ++ return err; ++} ++ ++int zeromap_page_range(struct vm_area_struct *vma, ++ unsigned long addr, unsigned long size, pgprot_t prot) ++{ ++ pgd_t *pgd; ++ unsigned long next; ++ unsigned long end = addr + size; ++ struct mm_struct *mm = vma->vm_mm; ++ int err; ++ ++ BUG_ON(addr >= end); ++ pgd = pgd_offset(mm, addr); ++ flush_cache_range(vma, addr, end); ++ do { ++ next = pgd_addr_end(addr, end); ++ err = zeromap_pud_range(mm, pgd, addr, next, prot); ++ if (err) ++ break; ++ } while (pgd++, addr = next, addr != end); ++ return err; ++} ++ + pte_t *get_locked_pte(struct mm_struct *mm, unsigned long addr, + spinlock_t **ptl) + { diff --git a/patches.suse/mm-increase-dirty-limits.patch b/patches.suse/mm-increase-dirty-limits.patch new file mode 100644 index 0000000..093ed6f --- /dev/null +++ b/patches.suse/mm-increase-dirty-limits.patch @@ -0,0 +1,26 @@ +From: Jan Kara +Subject: Increase limits for starting writeback of dirty data +References: bnc#449662 +Patch-mainline: ? + +Return limits for dirty pages writeback to the numbers from SLES10. This dramatically +improves performance of workloads dirtying a lot of memory (e.g. simple databases not +using direct IO) and we're not aware it would harm anything. + +Signed-off-by: Jan Kara + +--- + mm/page-writeback.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/mm/page-writeback.c ++++ b/mm/page-writeback.c +@@ -83,7 +83,7 @@ int vm_highmem_is_dirtyable; + /* + * The generator of dirty data starts writeback at this percentage + */ +-int vm_dirty_ratio = 10; ++int vm_dirty_ratio = 40; + + /* + * vm_dirty_bytes starts at 0 (disabled) so that it is a function of diff --git a/patches.suse/mm-tune-dirty-limits.patch b/patches.suse/mm-tune-dirty-limits.patch new file mode 100644 index 0000000..d0342bf --- /dev/null +++ b/patches.suse/mm-tune-dirty-limits.patch @@ -0,0 +1,78 @@ +From: Suresh Jayaraman +Subject: [PATCH] mm: Make default VM dirty ratio configurable to suit different workloads +References: bnc#552883 +Patch-mainline: Never + +Based on the observation that higher VM dirty ratio improves performance of +most server workloads that dirties a lot of memory (e.g. simple databases not +using direct IO, workloads doing heavy writes) and the latency-sensitive +workloads like desktop and typical workstations perform better with a +decreased VM dirty ratio, make default VM dirty ratio configurable. This also +ensures that we have the similar dirty pages writeback limit in SLES11 SP1 as +compared to SLES11 GM. + +The default VM dirty ratio is 20 for kernel-desktop flavor and 40 for all the +other flavors. + +Also introduce a new CONFIG_KERNEL_DESKTOP option which might allow to tune +the kernel to suit desktop workloads. + +Signed-off-by: Suresh Jayaraman +Acked-by: Jiri Kosina +Acked-by: Jeff Mahoney +--- + init/Kconfig | 24 ++++++++++++++++++++++++ + mm/page-writeback.c | 2 +- + 2 files changed, 25 insertions(+), 1 deletion(-) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -32,6 +32,13 @@ config SPLIT_PACKAGE + If you aren't packaging a kernel for distribution, it's safe to + say n. + ++config KERNEL_DESKTOP ++ bool "Kernel to suit desktop workloads" ++ default n ++ help ++ This is an option used to tune kernel parameters to better suit ++ desktop workloads. ++ + config ARCH + string + option env="ARCH" +@@ -1131,6 +1138,23 @@ config MMAP_ALLOW_UNINITIALIZED + + See Documentation/nommu-mmap.txt for more information. + ++config DEFAULT_VM_DIRTY_RATIO ++ int "Default VM dirty ratio (in %)" ++ default 20 if KERNEL_DESKTOP ++ default 40 ++ help ++ Allows to tune VM dirty ratio to suit different workloads. Increased ++ VM dirty ratio improves performance of most server workloads that ++ dirties a lot of memory (e.g. simple databases not using direct IO, ++ workloads doing heavy writes). The latency-sensitive workloads like ++ desktop and typical workstations perform better with a decreased ++ VM dirty ratio. ++ ++ Recommended value for desktop workload is 20. ++ Recommended value for server workload is 40. ++ ++ Only use this if you really know what you are doing. ++ + config PROFILING + bool "Profiling support" + help +--- a/mm/page-writeback.c ++++ b/mm/page-writeback.c +@@ -77,7 +77,7 @@ int vm_highmem_is_dirtyable; + /* + * The generator of dirty data starts writeback at this percentage + */ +-int vm_dirty_ratio = 20; ++int vm_dirty_ratio = CONFIG_DEFAULT_VM_DIRTY_RATIO; + + /* + * vm_dirty_bytes starts at 0 (disabled) so that it is a function of diff --git a/patches.suse/nameif-track-rename.patch b/patches.suse/nameif-track-rename.patch new file mode 100644 index 0000000..da5ece4 --- /dev/null +++ b/patches.suse/nameif-track-rename.patch @@ -0,0 +1,53 @@ +Subject: [PATCH] keep track of network interface renaming +From: Olaf Hering +Patch-mainline: not yet + +Keep track about which network interface names were renamed after the +network device driver printed its banner. Example insanity: + +honeydew:~ # dmesg| grep -Ew '(eth[0-9]|rename|renamed)' +e1000: eth0: e1000_probe: Intel(R) PRO/1000 Network Connection +e1000: eth1: e1000_probe: Intel(R) PRO/1000 Network Connection +e1000: eth2: e1000_probe: Intel(R) PRO/1000 Network Connection +e1000: eth3: e1000_probe: Intel(R) PRO/1000 Network Connection +dev_change_name: about to rename 'eth3' to 'eth0' +dev_change_name: about to rename 'eth3' to 'ethxx3' +eth3 renamed to ethxx3 +dev_change_name: about to rename 'ethxx3' to 'eth0' +dev_change_name: about to rename 'eth0' to 'eth3' +eth0 renamed to eth3 +dev_change_name: about to rename 'eth1' to 'eth2' +dev_change_name: about to rename 'eth1' to 'ethxx1' +eth1 renamed to ethxx1 +dev_change_name: about to rename 'ethxx1' to 'eth2' +dev_change_name: about to rename 'eth2' to 'eth1' +eth2 renamed to eth1 +dev_change_name: about to rename 'ethxx3' to 'eth0' +ethxx3 renamed to eth0 +dev_change_name: about to rename 'ethxx1' to 'eth2' +ethxx1 renamed to eth2 +e1000: eth0: e1000_watchdog_task: NIC Link is Up 100 Mbps Full Duplex + + + +Signed-off-by: Olaf Hering + + net/core/dev.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -945,8 +945,12 @@ static int dev_get_valid_name(struct net + return __dev_alloc_name(net, name, buf); + else if (__dev_get_by_name(net, name)) + return -EEXIST; +- else if (buf != name) ++ else if (buf != name) { ++ if (strncmp(name, buf, IFNAMSIZ)) ++ printk(KERN_INFO "%s renamed to %s by %s [%u]\n", ++ buf, name, current->comm, current->pid); + strlcpy(buf, name, IFNAMSIZ); ++ } + + return 0; + } diff --git a/patches.suse/netfilter-ip_conntrack_slp.patch b/patches.suse/netfilter-ip_conntrack_slp.patch new file mode 100644 index 0000000..97ae2e6 --- /dev/null +++ b/patches.suse/netfilter-ip_conntrack_slp.patch @@ -0,0 +1,181 @@ +From: Jiri Bohac +Subject: connection tracking helper for SLP +References: fate#301134 +Patch-mainline: Not yet + +A simple connection tracking helper for SLP. Marks replies to a +SLP broadcast query as ESTABLISHED to allow them to pass through the +firewall. + +Signed-off-by: Jiri Bohac + +--- + net/netfilter/Kconfig | 15 ++++ + net/netfilter/Makefile | 1 + net/netfilter/nf_conntrack_slp.c | 127 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 143 insertions(+) + +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -281,6 +281,21 @@ config NF_CONNTRACK_TFTP + + To compile it as a module, choose M here. If unsure, say N. + ++config NF_CONNTRACK_SLP ++ tristate "SLP protocol support" ++ depends on NF_CONNTRACK ++ depends on NETFILTER_ADVANCED ++ help ++ SLP queries are sometimes sent as broadcast messages from an ++ unprivileged port and responded to with unicast messages to the ++ same port. This make them hard to firewall properly because connection ++ tracking doesn't deal with broadcasts. This helper tracks locally ++ originating broadcast SLP queries and the corresponding ++ responses. It relies on correct IP address configuration, specifically ++ netmask and broadcast address. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config NF_CT_NETLINK + tristate 'Connection tracking netlink interface' + select NETFILTER_NETLINK +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_co + obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o + obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o + obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o ++obj-$(CONFIG_NF_CONNTRACK_SLP) += nf_conntrack_slp.o + + # transparent proxy support + obj-$(CONFIG_NETFILTER_TPROXY) += nf_tproxy_core.o +--- /dev/null ++++ b/net/netfilter/nf_conntrack_slp.c +@@ -0,0 +1,127 @@ ++/* ++ * NetBIOS name service broadcast connection tracking helper ++ * ++ * (c) 2007 Jiri Bohac ++ * (c) 2005 Patrick McHardy ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++/* ++ * This helper tracks locally originating NetBIOS name service ++ * requests by issuing permanent expectations (valid until ++ * timing out) matching all reply connections from the ++ * destination network. The only NetBIOS specific thing is ++ * actually the port number. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define SLP_PORT 427 ++ ++MODULE_AUTHOR("Jiri Bohac "); ++MODULE_DESCRIPTION("SLP broadcast connection tracking helper"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ip_conntrack_slp"); ++ ++static unsigned int timeout __read_mostly = 3; ++module_param(timeout, uint, 0400); ++MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds"); ++ ++static int help(struct sk_buff *skb, unsigned int protoff, ++ struct nf_conn *ct, enum ip_conntrack_info ctinfo) ++{ ++ struct nf_conntrack_expect *exp; ++ struct iphdr *iph = ip_hdr(skb); ++ struct rtable *rt = skb_rtable(skb); ++ struct in_device *in_dev; ++ __be32 mask = 0; ++ ++ /* we're only interested in locally generated packets */ ++ if (skb->sk == NULL) ++ goto out; ++ if (rt == NULL || !(rt->rt_flags & RTCF_BROADCAST)) ++ goto out; ++ if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) ++ goto out; ++ ++ rcu_read_lock(); ++ in_dev = __in_dev_get_rcu(rt->u.dst.dev); ++ if (in_dev != NULL) { ++ for_primary_ifa(in_dev) { ++ if (ifa->ifa_broadcast == iph->daddr) { ++ mask = ifa->ifa_mask; ++ break; ++ } ++ } endfor_ifa(in_dev); ++ } ++ rcu_read_unlock(); ++ ++ if (mask == 0) ++ goto out; ++ ++ exp = nf_ct_expect_alloc(ct); ++ if (exp == NULL) ++ goto out; ++ ++ exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; ++ exp->tuple.src.u.udp.port = htons(SLP_PORT); ++ ++ exp->mask.src.u3.ip = mask; ++ exp->mask.src.u.udp.port = htons(0xFFFF); ++ ++ exp->expectfn = NULL; ++ exp->flags = NF_CT_EXPECT_PERMANENT; ++ exp->class = NF_CT_EXPECT_CLASS_DEFAULT; ++ exp->helper = NULL; ++ ++ nf_ct_expect_related(exp); ++ nf_ct_expect_put(exp); ++ ++ nf_ct_refresh(ct, skb, timeout * HZ); ++out: ++ return NF_ACCEPT; ++} ++ ++static struct nf_conntrack_expect_policy exp_policy = { ++ .max_expected = 1, ++}; ++ ++static struct nf_conntrack_helper helper __read_mostly = { ++ .name = "slp", ++ .tuple.src.l3num = AF_INET, ++ .tuple.src.u.udp.port = __constant_htons(SLP_PORT), ++ .tuple.dst.protonum = IPPROTO_UDP, ++ .me = THIS_MODULE, ++ .help = help, ++ .expect_policy = &exp_policy, ++}; ++ ++static int __init nf_conntrack_slp_init(void) ++{ ++ exp_policy.timeout = timeout; ++ return nf_conntrack_helper_register(&helper); ++} ++ ++static void __exit nf_conntrack_slp_fini(void) ++{ ++ nf_conntrack_helper_unregister(&helper); ++} ++ ++module_init(nf_conntrack_slp_init); ++module_exit(nf_conntrack_slp_fini); diff --git a/patches.suse/netfilter-ipt_LOG-mac b/patches.suse/netfilter-ipt_LOG-mac new file mode 100644 index 0000000..010fd96 --- /dev/null +++ b/patches.suse/netfilter-ipt_LOG-mac @@ -0,0 +1,33 @@ +From: Jaroslav Kysela +Subject: LTC23987-iptables LOG output shows too long MAC info +References: 176921 +Patch-mainline: not yet + +LTC23987-iptables LOG output shows too long MAC info for qeth VLAN interface + +Signed-off-by: Jaroslav Kysela + +--- + net/ipv4/netfilter/ipt_LOG.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/net/ipv4/netfilter/ipt_LOG.c ++++ b/net/ipv4/netfilter/ipt_LOG.c +@@ -409,12 +409,12 @@ ipt_log_packet(u_int8_t pf, + printk("MAC="); + if (skb->dev && skb->dev->hard_header_len && + skb->mac_header != skb->network_header) { +- int i; ++ int i, len; + const unsigned char *p = skb_mac_header(skb); +- for (i = 0; i < skb->dev->hard_header_len; i++,p++) +- printk("%02x%c", *p, +- i==skb->dev->hard_header_len - 1 +- ? ' ':':'); ++ len = (int)(skb_network_header(skb) - p); ++ len = min((int)skb->dev->hard_header_len, len); ++ for (i = 0; i < len; i++,p++) ++ printk("%02x%c", *p, i==len - 1 ? ' ':':'); + } else + printk(" "); + } diff --git a/patches.suse/nfs4acl-ai.diff b/patches.suse/nfs4acl-ai.diff new file mode 100644 index 0000000..e03be77 --- /dev/null +++ b/patches.suse/nfs4acl-ai.diff @@ -0,0 +1,124 @@ +From: Andreas Gruenbacher +Subject: Implement those parts of Automatic Inheritance (AI) which are safe under POSIX +Patch-mainline: not yet + +If AI is disabled for a directory (ACL4_AUTO_INHERIT +not set), nothing changes. If AI is enabled for a directory, the +create-time inheritance algorithm changes as follows: + +* All inherited ACEs will have the ACE4_INHERITED_ACE flag set. + +* The create mode is applied to the ACL (by setting the file masks), +which means that the ACL must no longer be subject to AI permission +propagation, and so the ACL4_PROTECTED is set. + +By itelf, this is relatively useless because it will not allow +permissions to propagate, but AI aware applications can clear the +ACL4_PROTECTED flag when they know what they are doing, and this will +enable AI permission propagation. + +It would be nice if AI aware applications could indicate this fact to +the kernel so that the kernel can avoid setting the ACL4_PROTECTED flag +in the first place, but there is no such user-space interface at this +point. + +Signed-off-by: Andreas Gruenbacher + +--- + fs/nfs4acl_base.c | 12 ++++++++++-- + include/linux/nfs4acl.h | 26 +++++++++++++++++++++++--- + 2 files changed, 33 insertions(+), 5 deletions(-) + +--- a/fs/nfs4acl_base.c ++++ b/fs/nfs4acl_base.c +@@ -152,7 +152,8 @@ nfs4acl_chmod(struct nfs4acl *acl, mode_ + + if (acl->a_owner_mask == owner_mask && + acl->a_group_mask == group_mask && +- acl->a_other_mask == other_mask) ++ acl->a_other_mask == other_mask && ++ (!nfs4acl_is_auto_inherit(acl) || nfs4acl_is_protected(acl))) + return acl; + + clone = nfs4acl_clone(acl); +@@ -163,6 +164,8 @@ nfs4acl_chmod(struct nfs4acl *acl, mode_ + clone->a_owner_mask = owner_mask; + clone->a_group_mask = group_mask; + clone->a_other_mask = other_mask; ++ if (nfs4acl_is_auto_inherit(clone)) ++ clone->a_flags |= ACL4_PROTECTED; + + if (nfs4acl_write_through(&clone)) { + nfs4acl_put(clone); +@@ -559,7 +562,12 @@ nfs4acl_inherit(const struct nfs4acl *di + return ERR_PTR(-ENOMEM); + } + +- acl->a_flags = (dir_acl->a_flags & ACL4_WRITE_THROUGH); ++ acl->a_flags = (dir_acl->a_flags & ~ACL4_PROTECTED); ++ if (nfs4acl_is_auto_inherit(acl)) { ++ nfs4acl_for_each_entry(ace, acl) ++ ace->e_flags |= ACE4_INHERITED_ACE; ++ acl->a_flags |= ACL4_PROTECTED; ++ } + + return acl; + } +--- a/include/linux/nfs4acl.h ++++ b/include/linux/nfs4acl.h +@@ -32,10 +32,16 @@ struct nfs4acl { + _ace--) + + /* a_flags values */ ++#define ACL4_AUTO_INHERIT 0x01 ++#define ACL4_PROTECTED 0x02 ++#define ACL4_DEFAULTED 0x04 + #define ACL4_WRITE_THROUGH 0x40 + +-#define ACL4_VALID_FLAGS \ +- ACL4_WRITE_THROUGH ++#define ACL4_VALID_FLAGS ( \ ++ ACL4_AUTO_INHERIT | \ ++ ACL4_PROTECTED | \ ++ ACL4_DEFAULTED | \ ++ ACL4_WRITE_THROUGH ) + + /* e_type values */ + #define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x0000 +@@ -51,6 +57,7 @@ struct nfs4acl { + /*#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010*/ + /*#define ACE4_FAILED_ACCESS_ACE_FLAG 0x0020*/ + #define ACE4_IDENTIFIER_GROUP 0x0040 ++#define ACE4_INHERITED_ACE 0x0080 + #define ACE4_SPECIAL_WHO 0x4000 /* in-memory representation only */ + + #define ACE4_VALID_FLAGS ( \ +@@ -58,7 +65,8 @@ struct nfs4acl { + ACE4_DIRECTORY_INHERIT_ACE | \ + ACE4_NO_PROPAGATE_INHERIT_ACE | \ + ACE4_INHERIT_ONLY_ACE | \ +- ACE4_IDENTIFIER_GROUP ) ++ ACE4_IDENTIFIER_GROUP | \ ++ ACE4_INHERITED_ACE ) + + /* e_mask bitflags */ + #define ACE4_READ_DATA 0x00000001 +@@ -128,6 +136,18 @@ extern const char nfs4ace_group_who[]; + extern const char nfs4ace_everyone_who[]; + + static inline int ++nfs4acl_is_auto_inherit(const struct nfs4acl *acl) ++{ ++ return acl->a_flags & ACL4_AUTO_INHERIT; ++} ++ ++static inline int ++nfs4acl_is_protected(const struct nfs4acl *acl) ++{ ++ return acl->a_flags & ACL4_PROTECTED; ++} ++ ++static inline int + nfs4ace_is_owner(const struct nfs4ace *ace) + { + return (ace->e_flags & ACE4_SPECIAL_WHO) && diff --git a/patches.suse/nfs4acl-common.diff b/patches.suse/nfs4acl-common.diff new file mode 100644 index 0000000..839f700 --- /dev/null +++ b/patches.suse/nfs4acl-common.diff @@ -0,0 +1,1774 @@ +From: Andreas Gruenbacher +Subject: NFSv4 ACL in-memory representation and manipulation +Patch-mainline: not yet + +* In-memory representation (struct nfs4acl). +* Functionality a filesystem needs such as permission checking, + apply mode to acl, compute mode from acl, inheritance upon file + create. +* Compute a mask-less acl from struct nfs4acl that grants the same + permissions. Protocols which don't understand the masks need + this. +* Convert to/from xattrs. + +Signed-off-by: Andreas Gruenbacher + +--- + fs/Kconfig | 4 + fs/Makefile | 4 + fs/nfs4acl_base.c | 566 +++++++++++++++++++++++++++++++ + fs/nfs4acl_compat.c | 757 ++++++++++++++++++++++++++++++++++++++++++ + fs/nfs4acl_xattr.c | 146 ++++++++ + include/linux/nfs4acl.h | 205 +++++++++++ + include/linux/nfs4acl_xattr.h | 32 + + 7 files changed, 1714 insertions(+) + +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -39,6 +39,10 @@ config FS_POSIX_ACL + bool + default n + ++config FS_NFS4ACL ++ bool ++ default n ++ + source "fs/xfs/Kconfig" + source "fs/gfs2/Kconfig" + source "fs/ocfs2/Kconfig" +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -51,6 +51,10 @@ obj-$(CONFIG_FS_POSIX_ACL) += posix_acl. + obj-$(CONFIG_NFS_COMMON) += nfs_common/ + obj-$(CONFIG_GENERIC_ACL) += generic_acl.o + ++obj-$(CONFIG_FS_NFS4ACL) += nfs4acl.o ++nfs4acl-y := nfs4acl_base.o nfs4acl_xattr.o \ ++ nfs4acl_compat.o ++ + obj-y += quota/ + + obj-$(CONFIG_DMAPI) += dmapi/ +--- /dev/null ++++ b/fs/nfs4acl_base.c +@@ -0,0 +1,567 @@ ++/* ++ * Copyright (C) 2006 Andreas Gruenbacher ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2, or (at your option) any ++ * later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_LICENSE("GPL"); ++ ++/* ++ * ACL entries that have ACE4_SPECIAL_WHO set in ace->e_flags use the ++ * pointer values of these constants in ace->u.e_who to avoid massive ++ * amounts of string comparisons. ++ */ ++ ++const char nfs4ace_owner_who[] = "OWNER@"; ++const char nfs4ace_group_who[] = "GROUP@"; ++const char nfs4ace_everyone_who[] = "EVERYONE@"; ++ ++EXPORT_SYMBOL(nfs4ace_owner_who); ++EXPORT_SYMBOL(nfs4ace_group_who); ++EXPORT_SYMBOL(nfs4ace_everyone_who); ++ ++/** ++ * nfs4acl_alloc - allocate an acl ++ * @count: number of entries ++ */ ++struct nfs4acl * ++nfs4acl_alloc(int count) ++{ ++ size_t size = sizeof(struct nfs4acl) + count * sizeof(struct nfs4ace); ++ struct nfs4acl *acl = kmalloc(size, GFP_KERNEL); ++ ++ if (acl) { ++ memset(acl, 0, size); ++ atomic_set(&acl->a_refcount, 1); ++ acl->a_count = count; ++ } ++ return acl; ++} ++EXPORT_SYMBOL(nfs4acl_alloc); ++ ++/** ++ * nfs4acl_clone - create a copy of an acl ++ */ ++struct nfs4acl * ++nfs4acl_clone(const struct nfs4acl *acl) ++{ ++ int count = acl->a_count; ++ size_t size = sizeof(struct nfs4acl) + count * sizeof(struct nfs4ace); ++ struct nfs4acl *dup = kmalloc(size, GFP_KERNEL); ++ ++ if (dup) { ++ memcpy(dup, acl, size); ++ atomic_set(&dup->a_refcount, 1); ++ } ++ return dup; ++} ++ ++/* ++ * The POSIX permissions are supersets of the below mask flags. ++ * ++ * The ACE4_READ_ATTRIBUTES and ACE4_READ_ACL flags are always granted ++ * in POSIX. The ACE4_SYNCHRONIZE flag has no meaning under POSIX. We ++ * make sure that we do not mask them if they are set, so that users who ++ * rely on these flags won't get confused. ++ */ ++#define ACE4_POSIX_MODE_READ ( \ ++ ACE4_READ_DATA | ACE4_LIST_DIRECTORY ) ++#define ACE4_POSIX_MODE_WRITE ( \ ++ ACE4_WRITE_DATA | ACE4_ADD_FILE | \ ++ ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \ ++ ACE4_DELETE_CHILD ) ++#define ACE4_POSIX_MODE_EXEC ( \ ++ ACE4_EXECUTE) ++ ++static int ++nfs4acl_mask_to_mode(unsigned int mask) ++{ ++ int mode = 0; ++ ++ if (mask & ACE4_POSIX_MODE_READ) ++ mode |= MAY_READ; ++ if (mask & ACE4_POSIX_MODE_WRITE) ++ mode |= MAY_WRITE; ++ if (mask & ACE4_POSIX_MODE_EXEC) ++ mode |= MAY_EXEC; ++ ++ return mode; ++} ++ ++/** ++ * nfs4acl_masks_to_mode - compute file mode permission bits from file masks ++ * ++ * Compute the file mode permission bits from the file masks in the acl. ++ */ ++int ++nfs4acl_masks_to_mode(const struct nfs4acl *acl) ++{ ++ return nfs4acl_mask_to_mode(acl->a_owner_mask) << 6 | ++ nfs4acl_mask_to_mode(acl->a_group_mask) << 3 | ++ nfs4acl_mask_to_mode(acl->a_other_mask); ++} ++EXPORT_SYMBOL(nfs4acl_masks_to_mode); ++ ++static unsigned int ++nfs4acl_mode_to_mask(mode_t mode) ++{ ++ unsigned int mask = ACE4_POSIX_ALWAYS_ALLOWED; ++ ++ if (mode & MAY_READ) ++ mask |= ACE4_POSIX_MODE_READ; ++ if (mode & MAY_WRITE) ++ mask |= ACE4_POSIX_MODE_WRITE; ++ if (mode & MAY_EXEC) ++ mask |= ACE4_POSIX_MODE_EXEC; ++ ++ return mask; ++} ++ ++/** ++ * nfs4acl_chmod - update the file masks to reflect the new mode ++ * @mode: file mode permission bits to apply to the @acl ++ * ++ * Converts the mask flags corresponding to the owner, group, and other file ++ * permissions and computes the file masks. Returns @acl if it already has the ++ * appropriate file masks, or updates the flags in a copy of @acl. Takes over ++ * @acl. ++ */ ++struct nfs4acl * ++nfs4acl_chmod(struct nfs4acl *acl, mode_t mode) ++{ ++ unsigned int owner_mask, group_mask, other_mask; ++ struct nfs4acl *clone; ++ ++ owner_mask = nfs4acl_mode_to_mask(mode >> 6); ++ group_mask = nfs4acl_mode_to_mask(mode >> 3); ++ other_mask = nfs4acl_mode_to_mask(mode); ++ ++ if (acl->a_owner_mask == owner_mask && ++ acl->a_group_mask == group_mask && ++ acl->a_other_mask == other_mask) ++ return acl; ++ ++ clone = nfs4acl_clone(acl); ++ nfs4acl_put(acl); ++ if (!clone) ++ return ERR_PTR(-ENOMEM); ++ ++ clone->a_owner_mask = owner_mask; ++ clone->a_group_mask = group_mask; ++ clone->a_other_mask = other_mask; ++ ++ if (nfs4acl_write_through(&clone)) { ++ nfs4acl_put(clone); ++ clone = ERR_PTR(-ENOMEM); ++ } ++ return clone; ++} ++EXPORT_SYMBOL(nfs4acl_chmod); ++ ++/** ++ * nfs4acl_want_to_mask - convert permission want argument to a mask ++ * @want: @want argument of the permission inode operation ++ * ++ * When checking for append, @want is (MAY_WRITE | MAY_APPEND). ++ */ ++unsigned int ++nfs4acl_want_to_mask(int want) ++{ ++ unsigned int mask = 0; ++ ++ if (want & MAY_READ) ++ mask |= ACE4_READ_DATA; ++ if (want & MAY_APPEND) ++ mask |= ACE4_APPEND_DATA; ++ else if (want & MAY_WRITE) ++ mask |= ACE4_WRITE_DATA; ++ if (want & MAY_EXEC) ++ mask |= ACE4_EXECUTE; ++ ++ return mask; ++} ++EXPORT_SYMBOL(nfs4acl_want_to_mask); ++ ++/** ++ * nfs4acl_capability_check - check for capabilities overriding read/write access ++ * @inode: inode to check ++ * @mask: requested access (ACE4_* bitmask) ++ * ++ * Capabilities other than CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH must be checked ++ * separately. ++ */ ++static inline int nfs4acl_capability_check(struct inode *inode, unsigned int mask) ++{ ++ /* ++ * Read/write DACs are always overridable. ++ * Executable DACs are overridable if at least one exec bit is set. ++ */ ++ if (!(mask & (ACE4_WRITE_ACL | ACE4_WRITE_OWNER)) && ++ (!(mask & ACE4_EXECUTE) || ++ (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))) ++ if (capable(CAP_DAC_OVERRIDE)) ++ return 0; ++ ++ /* ++ * Searching includes executable on directories, else just read. ++ */ ++ if (!(mask & ~(ACE4_READ_DATA | ACE4_EXECUTE)) && ++ (S_ISDIR(inode->i_mode) || !(mask & ACE4_EXECUTE))) ++ if (capable(CAP_DAC_READ_SEARCH)) ++ return 0; ++ ++ return -EACCES; ++} ++ ++/** ++ * nfs4acl_permission - permission check algorithm with masking ++ * @inode: inode to check ++ * @acl: nfs4 acl of the inode ++ * @mask: requested access (ACE4_* bitmask) ++ * ++ * Checks if the current process is granted @mask flags in @acl. With ++ * write-through, the OWNER@ is always granted the owner file mask, the ++ * GROUP@ is always granted the group file mask, and EVERYONE@ is always ++ * granted the other file mask. Otherwise, processes are only granted ++ * @mask flags which they are granted in the @acl as well as in their ++ * file mask. ++ */ ++int nfs4acl_permission(struct inode *inode, const struct nfs4acl *acl, ++ unsigned int mask) ++{ ++ const struct nfs4ace *ace; ++ unsigned int file_mask, requested = mask, denied = 0; ++ int in_owning_group = in_group_p(inode->i_gid); ++ int owner_or_group_class = in_owning_group; ++ ++ /* ++ * A process is in the ++ * - owner file class if it owns the file, in the ++ * - group file class if it is in the file's owning group or ++ * it matches any of the user or group entries, and in the ++ * - other file class otherwise. ++ */ ++ ++ nfs4acl_for_each_entry(ace, acl) { ++ unsigned int ace_mask = ace->e_mask; ++ ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_owner(ace)) { ++ if (current_fsuid() != inode->i_uid) ++ continue; ++ goto is_owner; ++ } else if (nfs4ace_is_group(ace)) { ++ if (!in_owning_group) ++ continue; ++ } else if (nfs4ace_is_unix_id(ace)) { ++ if (ace->e_flags & ACE4_IDENTIFIER_GROUP) { ++ if (!in_group_p(ace->u.e_id)) ++ continue; ++ } else { ++ if (current_fsuid() != ace->u.e_id) ++ continue; ++ } ++ } else ++ goto is_everyone; ++ ++ /* ++ * Apply the group file mask to entries other than OWNER@ and ++ * EVERYONE@. This is not required for correct access checking ++ * but ensures that we grant the same permissions as the acl ++ * computed by nfs4acl_apply_masks(). ++ * ++ * For example, without this restriction, 'group@:rw::allow' ++ * with mode 0600 would grant rw access to owner processes ++ * which are also in the owning group. This cannot be expressed ++ * in an acl. ++ */ ++ if (nfs4ace_is_allow(ace)) ++ ace_mask &= acl->a_group_mask; ++ ++ is_owner: ++ /* The process is in the owner or group file class. */ ++ owner_or_group_class = 1; ++ ++ is_everyone: ++ /* Check which mask flags the ACE allows or denies. */ ++ if (nfs4ace_is_deny(ace)) ++ denied |= ace_mask & mask; ++ mask &= ~ace_mask; ++ ++ /* Keep going until we know which file class the process is in. */ ++ if (!mask && owner_or_group_class) ++ break; ++ } ++ denied |= mask; ++ ++ /* ++ * Figure out which file mask applies. ++ * Clear write-through if the process is in the file group class but ++ * not in the owning group, and so the denied permissions apply. ++ */ ++ if (current_fsuid() == inode->i_uid) ++ file_mask = acl->a_owner_mask; ++ else if (in_owning_group || owner_or_group_class) ++ file_mask = acl->a_group_mask; ++ else ++ file_mask = acl->a_other_mask; ++ ++ denied |= requested & ~file_mask; ++ if (!denied) ++ return 0; ++ return nfs4acl_capability_check(inode, requested); ++} ++EXPORT_SYMBOL(nfs4acl_permission); ++ ++/** ++ * nfs4acl_generic_permission - permission check algorithm without explicit acl ++ * @inode: inode to check permissions for ++ * @mask: requested access (ACE4_* bitmask) ++ * ++ * The file mode of a file without ACL corresponds to an ACL with a single ++ * "EVERYONE:~0::ALLOW" entry, with file masks that correspond to the file mode ++ * permissions. Instead of constructing a temporary ACL and applying ++ * nfs4acl_permission() to it, compute the identical result directly from the file ++ * mode. ++ */ ++int nfs4acl_generic_permission(struct inode *inode, unsigned int mask) ++{ ++ int mode = inode->i_mode; ++ ++ if (current_fsuid() == inode->i_uid) ++ mode >>= 6; ++ else if (in_group_p(inode->i_gid)) ++ mode >>= 3; ++ if (!(mask & ~nfs4acl_mode_to_mask(mode))) ++ return 0; ++ return nfs4acl_capability_check(inode, mask); ++} ++EXPORT_SYMBOL(nfs4acl_generic_permission); ++ ++/* ++ * nfs4ace_is_same_who - do both acl entries refer to the same identifier? ++ */ ++int ++nfs4ace_is_same_who(const struct nfs4ace *a, const struct nfs4ace *b) ++{ ++#define WHO_FLAGS (ACE4_SPECIAL_WHO | ACE4_IDENTIFIER_GROUP) ++ if ((a->e_flags & WHO_FLAGS) != (b->e_flags & WHO_FLAGS)) ++ return 0; ++ if (a->e_flags & ACE4_SPECIAL_WHO) ++ return a->u.e_who == b->u.e_who; ++ else ++ return a->u.e_id == b->u.e_id; ++#undef WHO_FLAGS ++} ++ ++/** ++ * nfs4acl_set_who - set a special who value ++ * @ace: acl entry ++ * @who: who value to use ++ */ ++int ++nfs4ace_set_who(struct nfs4ace *ace, const char *who) ++{ ++ if (!strcmp(who, nfs4ace_owner_who)) ++ who = nfs4ace_owner_who; ++ else if (!strcmp(who, nfs4ace_group_who)) ++ who = nfs4ace_group_who; ++ else if (!strcmp(who, nfs4ace_everyone_who)) ++ who = nfs4ace_everyone_who; ++ else ++ return -EINVAL; ++ ++ ace->u.e_who = who; ++ ace->e_flags |= ACE4_SPECIAL_WHO; ++ ace->e_flags &= ~ACE4_IDENTIFIER_GROUP; ++ return 0; ++} ++EXPORT_SYMBOL(nfs4ace_set_who); ++ ++/** ++ * nfs4acl_allowed_to_who - mask flags allowed to a specific who value ++ * ++ * Computes the mask values allowed to a specific who value, taking ++ * EVERYONE@ entries into account. ++ */ ++static unsigned int ++nfs4acl_allowed_to_who(struct nfs4acl *acl, struct nfs4ace *who) ++{ ++ struct nfs4ace *ace; ++ unsigned int allowed = 0; ++ ++ nfs4acl_for_each_entry_reverse(ace, acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_same_who(ace, who) || ++ nfs4ace_is_everyone(ace)) { ++ if (nfs4ace_is_allow(ace)) ++ allowed |= ace->e_mask; ++ else if (nfs4ace_is_deny(ace)) ++ allowed &= ~ace->e_mask; ++ } ++ } ++ return allowed; ++} ++ ++/** ++ * nfs4acl_compute_max_masks - compute upper bound masks ++ * ++ * Computes upper bound owner, group, and other masks so that none of ++ * the mask flags allowed by the acl are disabled (for any choice of the ++ * file owner or group membership). ++ */ ++static void ++nfs4acl_compute_max_masks(struct nfs4acl *acl) ++{ ++ struct nfs4ace *ace; ++ ++ acl->a_owner_mask = 0; ++ acl->a_group_mask = 0; ++ acl->a_other_mask = 0; ++ ++ nfs4acl_for_each_entry_reverse(ace, acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ ++ if (nfs4ace_is_owner(ace)) { ++ if (nfs4ace_is_allow(ace)) ++ acl->a_owner_mask |= ace->e_mask; ++ else if (nfs4ace_is_deny(ace)) ++ acl->a_owner_mask &= ~ace->e_mask; ++ } else if (nfs4ace_is_everyone(ace)) { ++ if (nfs4ace_is_allow(ace)) { ++ struct nfs4ace who = { ++ .e_flags = ACE4_SPECIAL_WHO, ++ .u.e_who = nfs4ace_group_who, ++ }; ++ ++ acl->a_other_mask |= ace->e_mask; ++ acl->a_group_mask |= ++ nfs4acl_allowed_to_who(acl, &who); ++ acl->a_owner_mask |= ace->e_mask; ++ } else if (nfs4ace_is_deny(ace)) { ++ acl->a_other_mask &= ~ace->e_mask; ++ acl->a_group_mask &= ~ace->e_mask; ++ acl->a_owner_mask &= ~ace->e_mask; ++ } ++ } else { ++ if (nfs4ace_is_allow(ace)) { ++ unsigned int mask = ++ nfs4acl_allowed_to_who(acl, ace); ++ ++ acl->a_group_mask |= mask; ++ acl->a_owner_mask |= mask; ++ } ++ } ++ } ++} ++ ++/** ++ * nfs4acl_inherit - compute the acl a new file will inherit ++ * @dir_acl: acl of the containing direcory ++ * @mode: file type and create mode of the new file ++ * ++ * Given the containing directory's acl, this function will compute the ++ * acl that new files in that directory will inherit, or %NULL if ++ * @dir_acl does not contain acl entries inheritable by this file. ++ * ++ * Without write-through, the file masks in the returned acl are set to ++ * the intersection of the create mode and the maximum permissions ++ * allowed to each file class. With write-through, the file masks are ++ * set to the create mode. ++ */ ++struct nfs4acl * ++nfs4acl_inherit(const struct nfs4acl *dir_acl, mode_t mode) ++{ ++ const struct nfs4ace *dir_ace; ++ struct nfs4acl *acl; ++ struct nfs4ace *ace; ++ int count = 0; ++ ++ if (S_ISDIR(mode)) { ++ nfs4acl_for_each_entry(dir_ace, dir_acl) { ++ if (!nfs4ace_is_inheritable(dir_ace)) ++ continue; ++ count++; ++ } ++ if (!count) ++ return NULL; ++ acl = nfs4acl_alloc(count); ++ if (!acl) ++ return ERR_PTR(-ENOMEM); ++ ace = acl->a_entries; ++ nfs4acl_for_each_entry(dir_ace, dir_acl) { ++ if (!nfs4ace_is_inheritable(dir_ace)) ++ continue; ++ memcpy(ace, dir_ace, sizeof(struct nfs4ace)); ++ if (dir_ace->e_flags & ACE4_NO_PROPAGATE_INHERIT_ACE) ++ nfs4ace_clear_inheritance_flags(ace); ++ if ((dir_ace->e_flags & ACE4_FILE_INHERIT_ACE) && ++ !(dir_ace->e_flags & ACE4_DIRECTORY_INHERIT_ACE)) ++ ace->e_flags |= ACE4_INHERIT_ONLY_ACE; ++ ace++; ++ } ++ } else { ++ nfs4acl_for_each_entry(dir_ace, dir_acl) { ++ if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE)) ++ continue; ++ count++; ++ } ++ if (!count) ++ return NULL; ++ acl = nfs4acl_alloc(count); ++ if (!acl) ++ return ERR_PTR(-ENOMEM); ++ ace = acl->a_entries; ++ nfs4acl_for_each_entry(dir_ace, dir_acl) { ++ if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE)) ++ continue; ++ memcpy(ace, dir_ace, sizeof(struct nfs4ace)); ++ nfs4ace_clear_inheritance_flags(ace); ++ ace++; ++ } ++ } ++ ++ /* The maximum max flags that the owner, group, and other classes ++ are allowed. */ ++ if (dir_acl->a_flags & ACL4_WRITE_THROUGH) { ++ acl->a_owner_mask = ACE4_VALID_MASK; ++ acl->a_group_mask = ACE4_VALID_MASK; ++ acl->a_other_mask = ACE4_VALID_MASK; ++ ++ mode &= ~current->fs->umask; ++ } else ++ nfs4acl_compute_max_masks(acl); ++ ++ /* Apply the create mode. */ ++ acl->a_owner_mask &= nfs4acl_mode_to_mask(mode >> 6); ++ acl->a_group_mask &= nfs4acl_mode_to_mask(mode >> 3); ++ acl->a_other_mask &= nfs4acl_mode_to_mask(mode); ++ ++ if (nfs4acl_write_through(&acl)) { ++ nfs4acl_put(acl); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ acl->a_flags = (dir_acl->a_flags & ACL4_WRITE_THROUGH); ++ ++ return acl; ++} ++EXPORT_SYMBOL(nfs4acl_inherit); +--- /dev/null ++++ b/fs/nfs4acl_compat.c +@@ -0,0 +1,758 @@ ++/* ++ * Copyright (C) 2006 Andreas Gruenbacher ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2, or (at your option) any ++ * later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++/** ++ * struct nfs4acl_alloc - remember how many entries are actually allocated ++ * @acl: acl with a_count <= @count ++ * @count: the actual number of entries allocated in @acl ++ * ++ * We pass around this structure while modifying an acl, so that we do ++ * not have to reallocate when we remove existing entries followed by ++ * adding new entries. ++ */ ++struct nfs4acl_alloc { ++ struct nfs4acl *acl; ++ unsigned int count; ++}; ++ ++/** ++ * nfs4acl_delete_entry - delete an entry in an acl ++ * @x: acl and number of allocated entries ++ * @ace: an entry in @x->acl ++ * ++ * Updates @ace so that it points to the entry before the deleted entry ++ * on return. (When deleting the first entry, @ace will point to the ++ * (non-existant) entry before the first entry). This behavior is the ++ * expected behavior when deleting entries while forward iterating over ++ * an acl. ++ */ ++static void ++nfs4acl_delete_entry(struct nfs4acl_alloc *x, struct nfs4ace **ace) ++{ ++ void *end = x->acl->a_entries + x->acl->a_count; ++ ++ memmove(*ace, *ace + 1, end - (void *)(*ace + 1)); ++ (*ace)--; ++ x->acl->a_count--; ++} ++ ++/** ++ * nfs4acl_insert_entry - insert an entry in an acl ++ * @x: acl and number of allocated entries ++ * @ace: entry before which the new entry shall be inserted ++ * ++ * Insert a new entry in @x->acl at position @ace, and zero-initialize ++ * it. This may require reallocating @x->acl. ++ */ ++static int ++nfs4acl_insert_entry(struct nfs4acl_alloc *x, struct nfs4ace **ace) ++{ ++ if (x->count == x->acl->a_count) { ++ int n = *ace - x->acl->a_entries; ++ struct nfs4acl *acl2; ++ ++ acl2 = nfs4acl_alloc(x->acl->a_count + 1); ++ if (!acl2) ++ return -1; ++ acl2->a_flags = x->acl->a_flags; ++ acl2->a_owner_mask = x->acl->a_owner_mask; ++ acl2->a_group_mask = x->acl->a_group_mask; ++ acl2->a_other_mask = x->acl->a_other_mask; ++ memcpy(acl2->a_entries, x->acl->a_entries, ++ n * sizeof(struct nfs4ace)); ++ memcpy(acl2->a_entries + n + 1, *ace, ++ (x->acl->a_count - n) * sizeof(struct nfs4ace)); ++ kfree(x->acl); ++ x->acl = acl2; ++ x->count = acl2->a_count; ++ *ace = acl2->a_entries + n; ++ } else { ++ void *end = x->acl->a_entries + x->acl->a_count; ++ ++ memmove(*ace + 1, *ace, end - (void *)*ace); ++ x->acl->a_count++; ++ } ++ memset(*ace, 0, sizeof(struct nfs4ace)); ++ return 0; ++} ++ ++/** ++ * nfs4ace_change_mask - change the mask in @ace to @mask ++ * @x: acl and number of allocated entries ++ * @ace: entry to modify ++ * @mask: new mask for @ace ++ * ++ * Set the effective mask of @ace to @mask. This will require splitting ++ * off a separate acl entry if @ace is inheritable. In that case, the ++ * effective- only acl entry is inserted after the inheritable acl ++ * entry, end the inheritable acl entry is set to inheritable-only. If ++ * @mode is 0, either set the original acl entry to inheritable-only if ++ * it was inheritable, or remove it otherwise. The returned @ace points ++ * to the modified or inserted effective-only acl entry if that entry ++ * exists, to the entry that has become inheritable-only, or else to the ++ * previous entry in the acl. This is the expected behavior when ++ * modifying masks while forward iterating over an acl. ++ */ ++static int ++nfs4ace_change_mask(struct nfs4acl_alloc *x, struct nfs4ace **ace, ++ unsigned int mask) ++{ ++ if (mask && (*ace)->e_mask == mask) ++ return 0; ++ if (mask & ~ACE4_POSIX_ALWAYS_ALLOWED) { ++ if (nfs4ace_is_inheritable(*ace)) { ++ if (nfs4acl_insert_entry(x, ace)) ++ return -1; ++ memcpy(*ace, *ace + 1, sizeof(struct nfs4ace)); ++ (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE; ++ (*ace)++; ++ nfs4ace_clear_inheritance_flags(*ace); ++ } ++ (*ace)->e_mask = mask; ++ } else { ++ if (nfs4ace_is_inheritable(*ace)) ++ (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE; ++ else ++ nfs4acl_delete_entry(x, ace); ++ } ++ return 0; ++} ++ ++/** ++ * nfs4acl_move_everyone_aces_down - move everyone@ acl entries to the end ++ * @x: acl and number of allocated entries ++ * ++ * Move all everyone acl entries to the bottom of the acl so that only a ++ * single everyone@ allow acl entry remains at the end, and update the ++ * mask fields of all acl entries on the way. If everyone@ is not ++ * granted any permissions, no empty everyone@ acl entry is inserted. ++ * ++ * This transformation does not modify the permissions that the acl ++ * grants, but we need it to simplify successive transformations. ++ */ ++static int ++nfs4acl_move_everyone_aces_down(struct nfs4acl_alloc *x) ++{ ++ struct nfs4ace *ace; ++ unsigned int allowed = 0, denied = 0; ++ ++ nfs4acl_for_each_entry(ace, x->acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_everyone(ace)) { ++ if (nfs4ace_is_allow(ace)) ++ allowed |= (ace->e_mask & ~denied); ++ else if (nfs4ace_is_deny(ace)) ++ denied |= (ace->e_mask & ~allowed); ++ else ++ continue; ++ if (nfs4ace_change_mask(x, &ace, 0)) ++ return -1; ++ } else { ++ if (nfs4ace_is_allow(ace)) { ++ if (nfs4ace_change_mask(x, &ace, allowed | ++ (ace->e_mask & ~denied))) ++ return -1; ++ } else if (nfs4ace_is_deny(ace)) { ++ if (nfs4ace_change_mask(x, &ace, denied | ++ (ace->e_mask & ~allowed))) ++ return -1; ++ } ++ } ++ } ++ if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) { ++ struct nfs4ace *last_ace = ace - 1; ++ ++ if (nfs4ace_is_everyone(last_ace) && ++ nfs4ace_is_allow(last_ace) && ++ nfs4ace_is_inherit_only(last_ace) && ++ last_ace->e_mask == allowed) ++ last_ace->e_flags &= ~ACE4_INHERIT_ONLY_ACE; ++ else { ++ if (nfs4acl_insert_entry(x, &ace)) ++ return -1; ++ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; ++ ace->e_flags = ACE4_SPECIAL_WHO; ++ ace->e_mask = allowed; ++ ace->u.e_who = nfs4ace_everyone_who; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * __nfs4acl_propagate_everyone - propagate everyone@ mask flags up for @who ++ * @x: acl and number of allocated entries ++ * @who: identifier to propagate mask flags for ++ * @allow: mask flags to propagate up ++ * ++ * Propagate mask flags from the trailing everyone@ allow acl entry up ++ * for the specified @who. ++ * ++ * The idea here is to precede the trailing EVERYONE@ ALLOW entry by an ++ * additional @who ALLOW entry, but with the following optimizations: ++ * (1) we don't bother setting any flags in the new @who ALLOW entry ++ * that has already been allowed or denied by a previous @who entry, (2) ++ * we merge the new @who entry with a previous @who entry if there is ++ * such a previous @who entry and there are no intervening DENY entries ++ * with mask flags that overlap the flags we care about. ++ */ ++static int ++__nfs4acl_propagate_everyone(struct nfs4acl_alloc *x, struct nfs4ace *who, ++ unsigned int allow) ++{ ++ struct nfs4ace *allow_last = NULL, *ace; ++ ++ /* Remove the mask flags from allow that are already determined for ++ this who value, and figure out if there is an ALLOW entry for ++ this who value that is "reachable" from the trailing EVERYONE@ ++ ALLOW ACE. */ ++ nfs4acl_for_each_entry(ace, x->acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_allow(ace)) { ++ if (nfs4ace_is_same_who(ace, who)) { ++ allow &= ~ace->e_mask; ++ allow_last = ace; ++ } ++ } else if (nfs4ace_is_deny(ace)) { ++ if (nfs4ace_is_same_who(ace, who)) ++ allow &= ~ace->e_mask; ++ if (allow & ace->e_mask) ++ allow_last = NULL; ++ } ++ } ++ ++ if (allow) { ++ if (allow_last) ++ return nfs4ace_change_mask(x, &allow_last, ++ allow_last->e_mask | allow); ++ else { ++ struct nfs4ace who_copy; ++ ++ ace = x->acl->a_entries + x->acl->a_count - 1; ++ memcpy(&who_copy, who, sizeof(struct nfs4ace)); ++ if (nfs4acl_insert_entry(x, &ace)) ++ return -1; ++ memcpy(ace, &who_copy, sizeof(struct nfs4ace)); ++ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; ++ nfs4ace_clear_inheritance_flags(ace); ++ ace->e_mask = allow; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * nfs4acl_propagate_everyone - propagate everyone@ mask flags up the acl ++ * @x: acl and number of allocated entries ++ * ++ * Make sure for owner@, group@, and all other users, groups, and ++ * special identifiers that they are allowed or denied all permissions ++ * that are granted be the trailing everyone@ acl entry. If they are ++ * not, try to add the missing permissions to existing allow acl entries ++ * for those users, or introduce additional acl entries if that is not ++ * possible. ++ * ++ * We do this so that no mask flags will get lost when finally applying ++ * the file masks to the acl entries: otherwise, with an other file mask ++ * that is more restrictive than the owner and/or group file mask, mask ++ * flags that were allowed to processes in the owner and group classes ++ * and that the other mask denies would be lost. For example, the ++ * following two acls show the problem when mode 0664 is applied to ++ * them: ++ * ++ * masking without propagation (wrong) ++ * =========================================================== ++ * joe:r::allow => joe:r::allow ++ * everyone@:rwx::allow => everyone@:r::allow ++ * ----------------------------------------------------------- ++ * joe:w::deny => joe:w::deny ++ * everyone@:rwx::allow everyone@:r::allow ++ * ++ * Note that the permissions of joe end up being more restrictive than ++ * what the acl would allow when first computing the allowed flags and ++ * then applying the respective mask. With propagation of permissions, ++ * we get: ++ * ++ * masking after propagation (correct) ++ * =========================================================== ++ * joe:r::allow => joe:rw::allow ++ * owner@:rw::allow ++ * group@:rw::allow ++ * everyone@:rwx::allow everyone@:r::allow ++ * ----------------------------------------------------------- ++ * joe:w::deny => owner@:x::deny ++ * joe:w::deny ++ * owner@:rw::allow ++ * owner@:rw::allow ++ * joe:r::allow ++ * everyone@:rwx::allow everyone@:r::allow ++ * ++ * The examples show the acls that would result from propagation with no ++ * masking performed. In fact, we do apply the respective mask to the ++ * acl entries before computing the propagation because this will save ++ * us from adding acl entries that would end up with empty mask fields ++ * after applying the masks. ++ * ++ * It is ensured that no more than one entry will be inserted for each ++ * who value, no matter how many entries each who value has already. ++ */ ++static int ++nfs4acl_propagate_everyone(struct nfs4acl_alloc *x) ++{ ++ int write_through = (x->acl->a_flags & ACL4_WRITE_THROUGH); ++ struct nfs4ace who = { .e_flags = ACE4_SPECIAL_WHO }; ++ struct nfs4ace *ace; ++ unsigned int owner_allow, group_allow; ++ int retval; ++ ++ if (!((x->acl->a_owner_mask | x->acl->a_group_mask) & ++ ~x->acl->a_other_mask)) ++ return 0; ++ if (!x->acl->a_count) ++ return 0; ++ ace = x->acl->a_entries + x->acl->a_count - 1; ++ if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_everyone(ace)) ++ return 0; ++ if (!(ace->e_mask & ~x->acl->a_other_mask)) { ++ /* None of the allowed permissions will get masked. */ ++ return 0; ++ } ++ owner_allow = ace->e_mask & x->acl->a_owner_mask; ++ group_allow = ace->e_mask & x->acl->a_group_mask; ++ ++ /* Propagate everyone@ permissions through to owner@. */ ++ if (owner_allow && !write_through && ++ (x->acl->a_owner_mask & ~x->acl->a_other_mask)) { ++ who.u.e_who = nfs4ace_owner_who; ++ retval = __nfs4acl_propagate_everyone(x, &who, owner_allow); ++ if (retval) ++ return -1; ++ } ++ ++ if (group_allow && (x->acl->a_group_mask & ~x->acl->a_other_mask)) { ++ int n; ++ ++ if (!write_through) { ++ /* Propagate everyone@ permissions through to group@. */ ++ who.u.e_who = nfs4ace_group_who; ++ retval = __nfs4acl_propagate_everyone(x, &who, ++ group_allow); ++ if (retval) ++ return -1; ++ } ++ ++ /* Start from the entry before the trailing EVERYONE@ ALLOW ++ entry. We will not hit EVERYONE@ entries in the loop. */ ++ for (n = x->acl->a_count - 2; n != -1; n--) { ++ ace = x->acl->a_entries + n; ++ ++ if (nfs4ace_is_inherit_only(ace) || ++ nfs4ace_is_owner(ace) || ++ nfs4ace_is_group(ace)) ++ continue; ++ if (nfs4ace_is_allow(ace) || nfs4ace_is_deny(ace)) { ++ /* Any inserted entry will end up below the ++ current entry. */ ++ retval = __nfs4acl_propagate_everyone(x, ace, ++ group_allow); ++ if (retval) ++ return -1; ++ } ++ } ++ } ++ return 0; ++} ++ ++/** ++ * __nfs4acl_apply_masks - apply the masks to the acl entries ++ * @x: acl and number of allocated entries ++ * ++ * Apply the owner file mask to owner@ entries, the intersection of the ++ * group and other file masks to everyone@ entries, and the group file ++ * mask to all other entries. ++ */ ++static int ++__nfs4acl_apply_masks(struct nfs4acl_alloc *x) ++{ ++ struct nfs4ace *ace; ++ ++ nfs4acl_for_each_entry(ace, x->acl) { ++ unsigned int mask; ++ ++ if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_allow(ace)) ++ continue; ++ if (nfs4ace_is_owner(ace)) ++ mask = x->acl->a_owner_mask; ++ else if (nfs4ace_is_everyone(ace)) ++ mask = x->acl->a_other_mask; ++ else ++ mask = x->acl->a_group_mask; ++ if (nfs4ace_change_mask(x, &ace, ace->e_mask & mask)) ++ return -1; ++ } ++ return 0; ++} ++ ++/** ++ * nfs4acl_max_allowed - maximum mask flags that anybody is allowed ++ */ ++static unsigned int ++nfs4acl_max_allowed(struct nfs4acl *acl) ++{ ++ struct nfs4ace *ace; ++ unsigned int allowed = 0; ++ ++ nfs4acl_for_each_entry_reverse(ace, acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_allow(ace)) ++ allowed |= ace->e_mask; ++ else if (nfs4ace_is_deny(ace)) { ++ if (nfs4ace_is_everyone(ace)) ++ allowed &= ~ace->e_mask; ++ } ++ } ++ return allowed; ++} ++ ++/** ++ * nfs4acl_isolate_owner_class - limit the owner class to the owner file mask ++ * @x: acl and number of allocated entries ++ * ++ * Make sure the owner class (owner@) is granted no more than the owner ++ * mask by first checking which permissions anyone is granted, and then ++ * denying owner@ all permissions beyond that. ++ */ ++static int ++nfs4acl_isolate_owner_class(struct nfs4acl_alloc *x) ++{ ++ struct nfs4ace *ace; ++ unsigned int allowed = 0; ++ ++ allowed = nfs4acl_max_allowed(x->acl); ++ if (allowed & ~x->acl->a_owner_mask) { ++ /* Figure out if we can update an existig OWNER@ DENY entry. */ ++ nfs4acl_for_each_entry(ace, x->acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_deny(ace)) { ++ if (nfs4ace_is_owner(ace)) ++ break; ++ } else if (nfs4ace_is_allow(ace)) { ++ ace = x->acl->a_entries + x->acl->a_count; ++ break; ++ } ++ } ++ if (ace != x->acl->a_entries + x->acl->a_count) { ++ if (nfs4ace_change_mask(x, &ace, ace->e_mask | ++ (allowed & ~x->acl->a_owner_mask))) ++ return -1; ++ } else { ++ /* Insert an owner@ deny entry at the front. */ ++ ace = x->acl->a_entries; ++ if (nfs4acl_insert_entry(x, &ace)) ++ return -1; ++ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; ++ ace->e_flags = ACE4_SPECIAL_WHO; ++ ace->e_mask = allowed & ~x->acl->a_owner_mask; ++ ace->u.e_who = nfs4ace_owner_who; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * __nfs4acl_isolate_who - isolate entry from EVERYONE@ ALLOW entry ++ * @x: acl and number of allocated entries ++ * @who: identifier to isolate ++ * @deny: mask flags this identifier should not be allowed ++ * ++ * Make sure that @who is not allowed any mask flags in @deny by checking ++ * which mask flags this identifier is allowed, and adding excess allowed ++ * mask flags to an existing DENY entry before the trailing EVERYONE@ ALLOW ++ * entry, or inserting such an entry. ++ */ ++static int ++__nfs4acl_isolate_who(struct nfs4acl_alloc *x, struct nfs4ace *who, ++ unsigned int deny) ++{ ++ struct nfs4ace *ace; ++ unsigned int allowed = 0, n; ++ ++ /* Compute the mask flags granted to this who value. */ ++ nfs4acl_for_each_entry_reverse(ace, x->acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_same_who(ace, who)) { ++ if (nfs4ace_is_allow(ace)) ++ allowed |= ace->e_mask; ++ else if (nfs4ace_is_deny(ace)) ++ allowed &= ~ace->e_mask; ++ deny &= ~ace->e_mask; ++ } ++ } ++ if (!deny) ++ return 0; ++ ++ /* Figure out if we can update an existig DENY entry. Start ++ from the entry before the trailing EVERYONE@ ALLOW entry. We ++ will not hit EVERYONE@ entries in the loop. */ ++ for (n = x->acl->a_count - 2; n != -1; n--) { ++ ace = x->acl->a_entries + n; ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_deny(ace)) { ++ if (nfs4ace_is_same_who(ace, who)) ++ break; ++ } else if (nfs4ace_is_allow(ace) && ++ (ace->e_mask & deny)) { ++ n = -1; ++ break; ++ } ++ } ++ if (n != -1) { ++ if (nfs4ace_change_mask(x, &ace, ace->e_mask | deny)) ++ return -1; ++ } else { ++ /* Insert a eny entry before the trailing EVERYONE@ DENY ++ entry. */ ++ struct nfs4ace who_copy; ++ ++ ace = x->acl->a_entries + x->acl->a_count - 1; ++ memcpy(&who_copy, who, sizeof(struct nfs4ace)); ++ if (nfs4acl_insert_entry(x, &ace)) ++ return -1; ++ memcpy(ace, &who_copy, sizeof(struct nfs4ace)); ++ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; ++ nfs4ace_clear_inheritance_flags(ace); ++ ace->e_mask = deny; ++ } ++ return 0; ++} ++ ++/** ++ * nfs4acl_isolate_group_class - limit the group class to the group file mask ++ * @x: acl and number of allocated entries ++ * ++ * Make sure the group class (all entries except owner@ and everyone@) is ++ * granted no more than the group mask by inserting DENY entries for group ++ * class entries where necessary. ++ */ ++static int ++nfs4acl_isolate_group_class(struct nfs4acl_alloc *x) ++{ ++ struct nfs4ace who = { ++ .e_flags = ACE4_SPECIAL_WHO, ++ .u.e_who = nfs4ace_group_who, ++ }; ++ struct nfs4ace *ace; ++ unsigned int deny; ++ ++ if (!x->acl->a_count) ++ return 0; ++ ace = x->acl->a_entries + x->acl->a_count - 1; ++ if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_everyone(ace)) ++ return 0; ++ deny = ace->e_mask & ~x->acl->a_group_mask; ++ ++ if (deny) { ++ unsigned int n; ++ ++ if (__nfs4acl_isolate_who(x, &who, deny)) ++ return -1; ++ ++ /* Start from the entry before the trailing EVERYONE@ ALLOW ++ entry. We will not hit EVERYONE@ entries in the loop. */ ++ for (n = x->acl->a_count - 2; n != -1; n--) { ++ ace = x->acl->a_entries + n; ++ ++ if (nfs4ace_is_inherit_only(ace) || ++ nfs4ace_is_owner(ace) || ++ nfs4ace_is_group(ace)) ++ continue; ++ if (__nfs4acl_isolate_who(x, ace, deny)) ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * __nfs4acl_write_through - grant the full masks to owner@, group@, everyone@ ++ * ++ * Make sure that owner, group@, and everyone@ are allowed the full mask ++ * permissions, and not only the permissions granted both by the acl and ++ * the masks. ++ */ ++static int ++__nfs4acl_write_through(struct nfs4acl_alloc *x) ++{ ++ struct nfs4ace *ace; ++ unsigned int allowed; ++ ++ /* Remove all owner@ and group@ ACEs: we re-insert them at the ++ top. */ ++ nfs4acl_for_each_entry(ace, x->acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if ((nfs4ace_is_owner(ace) || nfs4ace_is_group(ace)) && ++ nfs4ace_change_mask(x, &ace, 0)) ++ return -1; ++ } ++ ++ /* Insert the everyone@ allow entry at the end, or update the ++ existing entry. */ ++ allowed = x->acl->a_other_mask; ++ if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) { ++ ace = x->acl->a_entries + x->acl->a_count - 1; ++ if (x->acl->a_count && nfs4ace_is_everyone(ace) && ++ !nfs4ace_is_inherit_only(ace)) { ++ if (nfs4ace_change_mask(x, &ace, allowed)) ++ return -1; ++ } else { ++ ace = x->acl->a_entries + x->acl->a_count; ++ if (nfs4acl_insert_entry(x, &ace)) ++ return -1; ++ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; ++ ace->e_flags = ACE4_SPECIAL_WHO; ++ ace->e_mask = allowed; ++ ace->u.e_who = nfs4ace_everyone_who; ++ } ++ } ++ ++ /* Compute the permissions that owner@ and group@ are already granted ++ though the everyone@ allow entry at the end. Note that the acl ++ contains no owner@ or group@ entries at this point. */ ++ allowed = 0; ++ nfs4acl_for_each_entry_reverse(ace, x->acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_allow(ace)) { ++ if (nfs4ace_is_everyone(ace)) ++ allowed |= ace->e_mask; ++ } else if (nfs4ace_is_deny(ace)) ++ allowed &= ~ace->e_mask; ++ } ++ ++ /* Insert the appropriate group@ allow entry at the front. */ ++ if (x->acl->a_group_mask & ~allowed) { ++ ace = x->acl->a_entries; ++ if (nfs4acl_insert_entry(x, &ace)) ++ return -1; ++ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; ++ ace->e_flags = ACE4_SPECIAL_WHO; ++ ace->e_mask = x->acl->a_group_mask /*& ~allowed*/; ++ ace->u.e_who = nfs4ace_group_who; ++ } ++ ++ /* Insert the appropriate owner@ allow entry at the front. */ ++ if (x->acl->a_owner_mask & ~allowed) { ++ ace = x->acl->a_entries; ++ if (nfs4acl_insert_entry(x, &ace)) ++ return -1; ++ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; ++ ace->e_flags = ACE4_SPECIAL_WHO; ++ ace->e_mask = x->acl->a_owner_mask /*& ~allowed*/; ++ ace->u.e_who = nfs4ace_owner_who; ++ } ++ ++ /* Insert the appropriate owner@ deny entry at the front. */ ++ allowed = nfs4acl_max_allowed(x->acl); ++ if (allowed & ~x->acl->a_owner_mask) { ++ nfs4acl_for_each_entry(ace, x->acl) { ++ if (nfs4ace_is_inherit_only(ace)) ++ continue; ++ if (nfs4ace_is_allow(ace)) { ++ ace = x->acl->a_entries + x->acl->a_count; ++ break; ++ } ++ if (nfs4ace_is_deny(ace) && nfs4ace_is_owner(ace)) ++ break; ++ } ++ if (ace != x->acl->a_entries + x->acl->a_count) { ++ if (nfs4ace_change_mask(x, &ace, ace->e_mask | ++ (allowed & ~x->acl->a_owner_mask))) ++ return -1; ++ } else { ++ ace = x->acl->a_entries; ++ if (nfs4acl_insert_entry(x, &ace)) ++ return -1; ++ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; ++ ace->e_flags = ACE4_SPECIAL_WHO; ++ ace->e_mask = allowed & ~x->acl->a_owner_mask; ++ ace->u.e_who = nfs4ace_owner_who; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * nfs4acl_apply_masks - apply the masks to the acl ++ * ++ * Apply the masks so that the acl allows no more flags than the ++ * intersection between the flags that the original acl allows and the ++ * mask matching the process. ++ * ++ * Note: this algorithm may push the number of entries in the acl above ++ * ACL4_XATTR_MAX_COUNT, so a read-modify-write cycle would fail. ++ */ ++int ++nfs4acl_apply_masks(struct nfs4acl **acl) ++{ ++ struct nfs4acl_alloc x = { ++ .acl = *acl, ++ .count = (*acl)->a_count, ++ }; ++ int retval = 0; ++ ++ if (nfs4acl_move_everyone_aces_down(&x) || ++ nfs4acl_propagate_everyone(&x) || ++ __nfs4acl_apply_masks(&x) || ++ nfs4acl_isolate_owner_class(&x) || ++ nfs4acl_isolate_group_class(&x)) ++ retval = -ENOMEM; ++ ++ *acl = x.acl; ++ return retval; ++} ++EXPORT_SYMBOL(nfs4acl_apply_masks); ++ ++int nfs4acl_write_through(struct nfs4acl **acl) ++{ ++ struct nfs4acl_alloc x = { ++ .acl = *acl, ++ .count = (*acl)->a_count, ++ }; ++ int retval = 0; ++ ++ if (!((*acl)->a_flags & ACL4_WRITE_THROUGH)) ++ goto out; ++ ++ if (nfs4acl_move_everyone_aces_down(&x) || ++ nfs4acl_propagate_everyone(&x) || ++ __nfs4acl_write_through(&x)) ++ retval = -ENOMEM; ++ ++ *acl = x.acl; ++out: ++ return retval; ++} +--- /dev/null ++++ b/fs/nfs4acl_xattr.c +@@ -0,0 +1,146 @@ ++/* ++ * Copyright (C) 2006 Andreas Gruenbacher ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2, or (at your option) any ++ * later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_LICENSE("GPL"); ++ ++struct nfs4acl * ++nfs4acl_from_xattr(const void *value, size_t size) ++{ ++ const struct nfs4acl_xattr *xattr_acl = value; ++ const struct nfs4ace_xattr *xattr_ace = (void *)(xattr_acl + 1); ++ struct nfs4acl *acl; ++ struct nfs4ace *ace; ++ int count; ++ ++ if (size < sizeof(struct nfs4acl_xattr) || ++ xattr_acl->a_version != ACL4_XATTR_VERSION || ++ (xattr_acl->a_flags & ~ACL4_VALID_FLAGS)) ++ return ERR_PTR(-EINVAL); ++ ++ count = be16_to_cpu(xattr_acl->a_count); ++ if (count > ACL4_XATTR_MAX_COUNT) ++ return ERR_PTR(-EINVAL); ++ ++ acl = nfs4acl_alloc(count); ++ if (!acl) ++ return ERR_PTR(-ENOMEM); ++ ++ acl->a_flags = xattr_acl->a_flags; ++ acl->a_owner_mask = be32_to_cpu(xattr_acl->a_owner_mask); ++ if (acl->a_owner_mask & ~ACE4_VALID_MASK) ++ goto fail_einval; ++ acl->a_group_mask = be32_to_cpu(xattr_acl->a_group_mask); ++ if (acl->a_group_mask & ~ACE4_VALID_MASK) ++ goto fail_einval; ++ acl->a_other_mask = be32_to_cpu(xattr_acl->a_other_mask); ++ if (acl->a_other_mask & ~ACE4_VALID_MASK) ++ goto fail_einval; ++ ++ nfs4acl_for_each_entry(ace, acl) { ++ const char *who = (void *)(xattr_ace + 1), *end; ++ ssize_t used = (void *)who - value; ++ ++ if (used > size) ++ goto fail_einval; ++ end = memchr(who, 0, size - used); ++ if (!end) ++ goto fail_einval; ++ ++ ace->e_type = be16_to_cpu(xattr_ace->e_type); ++ ace->e_flags = be16_to_cpu(xattr_ace->e_flags); ++ ace->e_mask = be32_to_cpu(xattr_ace->e_mask); ++ ace->u.e_id = be32_to_cpu(xattr_ace->e_id); ++ ++ if (ace->e_flags & ~ACE4_VALID_FLAGS) { ++ memset(ace, 0, sizeof(struct nfs4ace)); ++ goto fail_einval; ++ } ++ if (ace->e_type > ACE4_ACCESS_DENIED_ACE_TYPE || ++ (ace->e_mask & ~ACE4_VALID_MASK)) ++ goto fail_einval; ++ ++ if (who == end) { ++ if (ace->u.e_id == -1) ++ goto fail_einval; /* uid/gid needed */ ++ } else if (nfs4ace_set_who(ace, who)) ++ goto fail_einval; ++ ++ xattr_ace = (void *)who + ALIGN(end - who + 1, 4); ++ } ++ ++ return acl; ++ ++fail_einval: ++ nfs4acl_put(acl); ++ return ERR_PTR(-EINVAL); ++} ++EXPORT_SYMBOL(nfs4acl_from_xattr); ++ ++size_t ++nfs4acl_xattr_size(const struct nfs4acl *acl) ++{ ++ size_t size = sizeof(struct nfs4acl_xattr); ++ const struct nfs4ace *ace; ++ ++ nfs4acl_for_each_entry(ace, acl) { ++ size += sizeof(struct nfs4ace_xattr) + ++ (nfs4ace_is_unix_id(ace) ? 4 : ++ ALIGN(strlen(ace->u.e_who) + 1, 4)); ++ } ++ return size; ++} ++EXPORT_SYMBOL(nfs4acl_xattr_size); ++ ++void ++nfs4acl_to_xattr(const struct nfs4acl *acl, void *buffer) ++{ ++ struct nfs4acl_xattr *xattr_acl = buffer; ++ struct nfs4ace_xattr *xattr_ace; ++ const struct nfs4ace *ace; ++ ++ xattr_acl->a_version = ACL4_XATTR_VERSION; ++ xattr_acl->a_flags = acl->a_flags; ++ xattr_acl->a_count = cpu_to_be16(acl->a_count); ++ ++ xattr_acl->a_owner_mask = cpu_to_be32(acl->a_owner_mask); ++ xattr_acl->a_group_mask = cpu_to_be32(acl->a_group_mask); ++ xattr_acl->a_other_mask = cpu_to_be32(acl->a_other_mask); ++ ++ xattr_ace = (void *)(xattr_acl + 1); ++ nfs4acl_for_each_entry(ace, acl) { ++ xattr_ace->e_type = cpu_to_be16(ace->e_type); ++ xattr_ace->e_flags = cpu_to_be16(ace->e_flags & ++ ACE4_VALID_FLAGS); ++ xattr_ace->e_mask = cpu_to_be32(ace->e_mask); ++ if (nfs4ace_is_unix_id(ace)) { ++ xattr_ace->e_id = cpu_to_be32(ace->u.e_id); ++ memset(xattr_ace->e_who, 0, 4); ++ xattr_ace = (void *)xattr_ace->e_who + 4; ++ } else { ++ int sz = ALIGN(strlen(ace->u.e_who) + 1, 4); ++ ++ xattr_ace->e_id = cpu_to_be32(-1); ++ memset(xattr_ace->e_who + sz - 4, 0, 4); ++ strcpy(xattr_ace->e_who, ace->u.e_who); ++ xattr_ace = (void *)xattr_ace->e_who + sz; ++ } ++ } ++} ++EXPORT_SYMBOL(nfs4acl_to_xattr); +--- /dev/null ++++ b/include/linux/nfs4acl.h +@@ -0,0 +1,205 @@ ++#ifndef __NFS4ACL_H ++#define __NFS4ACL_H ++ ++struct nfs4ace { ++ unsigned short e_type; ++ unsigned short e_flags; ++ unsigned int e_mask; ++ union { ++ unsigned int e_id; ++ const char *e_who; ++ } u; ++}; ++ ++struct nfs4acl { ++ atomic_t a_refcount; ++ unsigned int a_owner_mask; ++ unsigned int a_group_mask; ++ unsigned int a_other_mask; ++ unsigned short a_count; ++ unsigned short a_flags; ++ struct nfs4ace a_entries[0]; ++}; ++ ++#define nfs4acl_for_each_entry(_ace, _acl) \ ++ for (_ace = _acl->a_entries; \ ++ _ace != _acl->a_entries + _acl->a_count; \ ++ _ace++) ++ ++#define nfs4acl_for_each_entry_reverse(_ace, _acl) \ ++ for (_ace = _acl->a_entries + _acl->a_count - 1; \ ++ _ace != _acl->a_entries - 1; \ ++ _ace--) ++ ++/* a_flags values */ ++#define ACL4_WRITE_THROUGH 0x40 ++ ++#define ACL4_VALID_FLAGS \ ++ ACL4_WRITE_THROUGH ++ ++/* e_type values */ ++#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x0000 ++#define ACE4_ACCESS_DENIED_ACE_TYPE 0x0001 ++/*#define ACE4_SYSTEM_AUDIT_ACE_TYPE 0x0002*/ ++/*#define ACE4_SYSTEM_ALARM_ACE_TYPE 0x0003*/ ++ ++/* e_flags bitflags */ ++#define ACE4_FILE_INHERIT_ACE 0x0001 ++#define ACE4_DIRECTORY_INHERIT_ACE 0x0002 ++#define ACE4_NO_PROPAGATE_INHERIT_ACE 0x0004 ++#define ACE4_INHERIT_ONLY_ACE 0x0008 ++/*#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010*/ ++/*#define ACE4_FAILED_ACCESS_ACE_FLAG 0x0020*/ ++#define ACE4_IDENTIFIER_GROUP 0x0040 ++#define ACE4_SPECIAL_WHO 0x4000 /* in-memory representation only */ ++ ++#define ACE4_VALID_FLAGS ( \ ++ ACE4_FILE_INHERIT_ACE | \ ++ ACE4_DIRECTORY_INHERIT_ACE | \ ++ ACE4_NO_PROPAGATE_INHERIT_ACE | \ ++ ACE4_INHERIT_ONLY_ACE | \ ++ ACE4_IDENTIFIER_GROUP ) ++ ++/* e_mask bitflags */ ++#define ACE4_READ_DATA 0x00000001 ++#define ACE4_LIST_DIRECTORY 0x00000001 ++#define ACE4_WRITE_DATA 0x00000002 ++#define ACE4_ADD_FILE 0x00000002 ++#define ACE4_APPEND_DATA 0x00000004 ++#define ACE4_ADD_SUBDIRECTORY 0x00000004 ++#define ACE4_READ_NAMED_ATTRS 0x00000008 ++#define ACE4_WRITE_NAMED_ATTRS 0x00000010 ++#define ACE4_EXECUTE 0x00000020 ++#define ACE4_DELETE_CHILD 0x00000040 ++#define ACE4_READ_ATTRIBUTES 0x00000080 ++#define ACE4_WRITE_ATTRIBUTES 0x00000100 ++#define ACE4_DELETE 0x00010000 ++#define ACE4_READ_ACL 0x00020000 ++#define ACE4_WRITE_ACL 0x00040000 ++#define ACE4_WRITE_OWNER 0x00080000 ++#define ACE4_SYNCHRONIZE 0x00100000 ++ ++#define ACE4_VALID_MASK ( \ ++ ACE4_READ_DATA | ACE4_LIST_DIRECTORY | \ ++ ACE4_WRITE_DATA | ACE4_ADD_FILE | \ ++ ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \ ++ ACE4_READ_NAMED_ATTRS | \ ++ ACE4_WRITE_NAMED_ATTRS | \ ++ ACE4_EXECUTE | \ ++ ACE4_DELETE_CHILD | \ ++ ACE4_READ_ATTRIBUTES | \ ++ ACE4_WRITE_ATTRIBUTES | \ ++ ACE4_DELETE | \ ++ ACE4_READ_ACL | \ ++ ACE4_WRITE_ACL | \ ++ ACE4_WRITE_OWNER | \ ++ ACE4_SYNCHRONIZE ) ++ ++#define ACE4_POSIX_ALWAYS_ALLOWED ( \ ++ ACE4_SYNCHRONIZE | \ ++ ACE4_READ_ATTRIBUTES | \ ++ ACE4_READ_ACL ) ++/* ++ * Duplicate an NFS4ACL handle. ++ */ ++static inline struct nfs4acl * ++nfs4acl_get(struct nfs4acl *acl) ++{ ++ if (acl) ++ atomic_inc(&acl->a_refcount); ++ return acl; ++} ++ ++/* ++ * Free an NFS4ACL handle ++ */ ++static inline void ++nfs4acl_put(struct nfs4acl *acl) ++{ ++ if (acl && atomic_dec_and_test(&acl->a_refcount)) ++ kfree(acl); ++} ++ ++/* Special e_who identifiers: we use these pointer values in comparisons ++ instead of strcmp for efficiency. */ ++ ++extern const char nfs4ace_owner_who[]; ++extern const char nfs4ace_group_who[]; ++extern const char nfs4ace_everyone_who[]; ++ ++static inline int ++nfs4ace_is_owner(const struct nfs4ace *ace) ++{ ++ return (ace->e_flags & ACE4_SPECIAL_WHO) && ++ ace->u.e_who == nfs4ace_owner_who; ++} ++ ++static inline int ++nfs4ace_is_group(const struct nfs4ace *ace) ++{ ++ return (ace->e_flags & ACE4_SPECIAL_WHO) && ++ ace->u.e_who == nfs4ace_group_who; ++} ++ ++static inline int ++nfs4ace_is_everyone(const struct nfs4ace *ace) ++{ ++ return (ace->e_flags & ACE4_SPECIAL_WHO) && ++ ace->u.e_who == nfs4ace_everyone_who; ++} ++ ++static inline int ++nfs4ace_is_unix_id(const struct nfs4ace *ace) ++{ ++ return !(ace->e_flags & ACE4_SPECIAL_WHO); ++} ++ ++static inline int ++nfs4ace_is_inherit_only(const struct nfs4ace *ace) ++{ ++ return ace->e_flags & ACE4_INHERIT_ONLY_ACE; ++} ++ ++static inline int ++nfs4ace_is_inheritable(const struct nfs4ace *ace) ++{ ++ return ace->e_flags & (ACE4_FILE_INHERIT_ACE | ++ ACE4_DIRECTORY_INHERIT_ACE); ++} ++ ++static inline void ++nfs4ace_clear_inheritance_flags(struct nfs4ace *ace) ++{ ++ ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE | ++ ACE4_DIRECTORY_INHERIT_ACE | ++ ACE4_NO_PROPAGATE_INHERIT_ACE | ++ ACE4_INHERIT_ONLY_ACE); ++} ++ ++static inline int ++nfs4ace_is_allow(const struct nfs4ace *ace) ++{ ++ return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE; ++} ++ ++static inline int ++nfs4ace_is_deny(const struct nfs4ace *ace) ++{ ++ return ace->e_type == ACE4_ACCESS_DENIED_ACE_TYPE; ++} ++ ++extern struct nfs4acl *nfs4acl_alloc(int count); ++extern struct nfs4acl *nfs4acl_clone(const struct nfs4acl *acl); ++ ++extern unsigned int nfs4acl_want_to_mask(int want); ++extern int nfs4acl_permission(struct inode *, const struct nfs4acl *, unsigned int); ++extern int nfs4acl_generic_permission(struct inode *, unsigned int); ++extern int nfs4ace_is_same_who(const struct nfs4ace *, const struct nfs4ace *); ++extern int nfs4ace_set_who(struct nfs4ace *ace, const char *who); ++extern struct nfs4acl *nfs4acl_inherit(const struct nfs4acl *, mode_t); ++extern int nfs4acl_masks_to_mode(const struct nfs4acl *); ++extern struct nfs4acl *nfs4acl_chmod(struct nfs4acl *, mode_t); ++extern int nfs4acl_apply_masks(struct nfs4acl **acl); ++extern int nfs4acl_write_through(struct nfs4acl **acl); ++ ++#endif /* __NFS4ACL_H */ +--- /dev/null ++++ b/include/linux/nfs4acl_xattr.h +@@ -0,0 +1,32 @@ ++#ifndef __NFS4ACL_XATTR_H ++#define __NFS4ACL_XATTR_H ++ ++#include ++ ++#define NFS4ACL_XATTR "system.nfs4acl" ++ ++struct nfs4ace_xattr { ++ __be16 e_type; ++ __be16 e_flags; ++ __be32 e_mask; ++ __be32 e_id; ++ char e_who[0]; ++}; ++ ++struct nfs4acl_xattr { ++ unsigned char a_version; ++ unsigned char a_flags; ++ __be16 a_count; ++ __be32 a_owner_mask; ++ __be32 a_group_mask; ++ __be32 a_other_mask; ++}; ++ ++#define ACL4_XATTR_VERSION 0 ++#define ACL4_XATTR_MAX_COUNT 1024 ++ ++extern struct nfs4acl *nfs4acl_from_xattr(const void *, size_t); ++extern size_t nfs4acl_xattr_size(const struct nfs4acl *acl); ++extern void nfs4acl_to_xattr(const struct nfs4acl *, void *); ++ ++#endif /* __NFS4ACL_XATTR_H */ diff --git a/patches.suse/nfs4acl-ext3.diff b/patches.suse/nfs4acl-ext3.diff new file mode 100644 index 0000000..f2ac3eb --- /dev/null +++ b/patches.suse/nfs4acl-ext3.diff @@ -0,0 +1,872 @@ +From: Andreas Gruenbacher +Subject: NFSv4 ACLs for ext3 +Patch-mainline: Not yet + +With the acl=nfs4 mount option, ext3 will use NFSv4 ACLs instead of +POSIX ACLs. See http://www.suse.de/~agruen/nfs4acl/ for some +documentation and examples. + +Signed-off-by: Andreas Gruenbacher + +--- + fs/ext3/Kconfig | 7 + fs/ext3/Makefile | 1 + fs/ext3/file.c | 4 + fs/ext3/ialloc.c | 6 + fs/ext3/inode.c | 73 ++++++++ + fs/ext3/namei.c | 15 + + fs/ext3/namei.h | 1 + fs/ext3/nfs4acl.c | 378 ++++++++++++++++++++++++++++++++++++++++++++++ + fs/ext3/nfs4acl.h | 36 ++++ + fs/ext3/super.c | 61 +++++-- + fs/ext3/xattr.c | 9 + + fs/ext3/xattr.h | 5 + include/linux/ext3_fs.h | 1 + include/linux/ext3_fs_i.h | 3 + 14 files changed, 582 insertions(+), 18 deletions(-) + +--- a/fs/ext3/Kconfig ++++ b/fs/ext3/Kconfig +@@ -97,6 +97,13 @@ config EXT3_FS_POSIX_ACL + + If you don't know what Access Control Lists are, say N + ++config EXT3_FS_NFS4ACL ++ bool "Native NFSv4 ACLs (EXPERIMENTAL)" ++ depends on EXT3_FS_XATTR && EXPERIMENTAL ++ select FS_NFS4ACL ++ help ++ Allow to use NFSv4 ACLs instead of POSIX ACLs. ++ + config EXT3_FS_SECURITY + bool "Ext3 Security Labels" + depends on EXT3_FS_XATTR +--- a/fs/ext3/Makefile ++++ b/fs/ext3/Makefile +@@ -10,3 +10,4 @@ ext3-y := balloc.o bitmap.o dir.o file.o + ext3-$(CONFIG_EXT3_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o + ext3-$(CONFIG_EXT3_FS_POSIX_ACL) += acl.o + ext3-$(CONFIG_EXT3_FS_SECURITY) += xattr_security.o ++ext3-$(CONFIG_EXT3_FS_NFS4ACL) += nfs4acl.o +--- a/fs/ext3/file.c ++++ b/fs/ext3/file.c +@@ -24,8 +24,10 @@ + #include + #include + #include ++#include "namei.h" + #include "xattr.h" + #include "acl.h" ++#include "nfs4acl.h" + + /* + * Called when an inode is released. Note that this is different +@@ -81,5 +83,7 @@ const struct inode_operations ext3_file_ + #endif + .check_acl = ext3_check_acl, + .fiemap = ext3_fiemap, ++ .may_create = ext3_may_create, ++ .may_delete = ext3_may_delete, + }; + +--- a/fs/ext3/ialloc.c ++++ b/fs/ext3/ialloc.c +@@ -28,6 +28,7 @@ + + #include "xattr.h" + #include "acl.h" ++#include "nfs4acl.h" + + /* + * ialloc.c contains the inodes allocation and deallocation routines +@@ -593,7 +594,10 @@ got: + if (err) + goto fail_drop; + +- err = ext3_init_acl(handle, inode, dir); ++ if (test_opt(sb, NFS4ACL)) ++ err = ext3_nfs4acl_init(handle, inode, dir); ++ else ++ err = ext3_init_acl(handle, inode, dir); + if (err) + goto fail_free_drop; + +--- a/fs/ext3/inode.c ++++ b/fs/ext3/inode.c +@@ -40,6 +40,7 @@ + #include + #include "xattr.h" + #include "acl.h" ++#include "nfs4acl.h" + + static int ext3_writepage_trans_blocks(struct inode *inode); + +@@ -2790,6 +2791,9 @@ struct inode *ext3_iget(struct super_blo + return inode; + + ei = EXT3_I(inode); ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ ei->i_nfs4acl = EXT3_NFS4ACL_NOT_CACHED; ++#endif + ei->i_block_alloc_info = NULL; + + ret = __ext3_get_inode_loc(inode, &iloc, 0); +@@ -3124,6 +3128,65 @@ int ext3_write_inode(struct inode *inode + return ext3_force_commit(inode->i_sb); + } + ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++static int ext3_inode_change_ok(struct inode *inode, struct iattr *attr) ++{ ++ unsigned int ia_valid = attr->ia_valid; ++ ++ if (!test_opt(inode->i_sb, NFS4ACL)) ++ return inode_change_ok(inode, attr); ++ ++ /* If force is set do it anyway. */ ++ if (ia_valid & ATTR_FORCE) ++ return 0; ++ ++ /* Make sure a caller can chown. */ ++ if ((ia_valid & ATTR_UID) && ++ (current_fsuid() != inode->i_uid || ++ attr->ia_uid != inode->i_uid) && ++ (current_fsuid() != attr->ia_uid || ++ ext3_nfs4acl_permission(inode, ACE4_WRITE_OWNER)) && ++ !capable(CAP_CHOWN)) ++ goto error; ++ ++ /* Make sure caller can chgrp. */ ++ if ((ia_valid & ATTR_GID)) { ++ int in_group = in_group_p(attr->ia_gid); ++ if ((current_fsuid() != inode->i_uid || ++ (!in_group && attr->ia_gid != inode->i_gid)) && ++ (!in_group || ++ ext3_nfs4acl_permission(inode, ACE4_WRITE_OWNER)) && ++ !capable(CAP_CHOWN)) ++ goto error; ++ } ++ ++ /* Make sure a caller can chmod. */ ++ if (ia_valid & ATTR_MODE) { ++ if (current_fsuid() != inode->i_uid && ++ ext3_nfs4acl_permission(inode, ACE4_WRITE_ACL) && ++ !capable(CAP_FOWNER)) ++ goto error; ++ /* Also check the setgid bit! */ ++ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : ++ inode->i_gid) && !capable(CAP_FSETID)) ++ attr->ia_mode &= ~S_ISGID; ++ } ++ ++ /* Check for setting the inode time. */ ++ if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { ++ if (current_fsuid() != inode->i_uid && ++ ext3_nfs4acl_permission(inode, ACE4_WRITE_ATTRIBUTES) && ++ !capable(CAP_FOWNER)) ++ goto error; ++ } ++ return 0; ++error: ++ return -EPERM; ++} ++#else ++# define ext3_inode_change_ok inode_change_ok ++#endif ++ + /* + * ext3_setattr() + * +@@ -3147,7 +3210,7 @@ int ext3_setattr(struct dentry *dentry, + int error, rc = 0; + const unsigned int ia_valid = attr->ia_valid; + +- error = inode_change_ok(inode, attr); ++ error = ext3_inode_change_ok(inode, attr); + if (error) + return error; + +@@ -3200,8 +3263,12 @@ int ext3_setattr(struct dentry *dentry, + + rc = inode_setattr(inode, attr); + +- if (!rc && (ia_valid & ATTR_MODE)) +- rc = ext3_acl_chmod(inode); ++ if (!rc && (ia_valid & ATTR_MODE)) { ++ if (test_opt(inode->i_sb, NFS4ACL)) ++ rc = ext3_nfs4acl_chmod(inode); ++ else ++ rc = ext3_acl_chmod(inode); ++ } + + err_out: + ext3_std_error(inode->i_sb, error); +--- a/fs/ext3/namei.c ++++ b/fs/ext3/namei.c +@@ -40,6 +40,7 @@ + #include "namei.h" + #include "xattr.h" + #include "acl.h" ++#include "nfs4acl.h" + + /* + * define how far ahead to read directories while searching them. +@@ -2445,6 +2446,16 @@ end_rename: + return retval; + } + ++int ext3_permission(struct inode *inode, int mask) ++{ ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ if (test_opt(inode->i_sb, NFS4ACL)) ++ return ext3_nfs4acl_permission(inode, nfs4acl_want_to_mask(mask)); ++ else ++#endif ++ return generic_permission(inode, mask, ext3_check_acl); ++} ++ + /* + * directories can handle most operations... + */ +@@ -2466,6 +2477,8 @@ const struct inode_operations ext3_dir_i + .removexattr = generic_removexattr, + #endif + .check_acl = ext3_check_acl, ++ .may_create = ext3_may_create, ++ .may_delete = ext3_may_delete, + }; + + const struct inode_operations ext3_special_inode_operations = { +@@ -2477,4 +2490,6 @@ const struct inode_operations ext3_speci + .removexattr = generic_removexattr, + #endif + .check_acl = ext3_check_acl, ++ .may_create = ext3_may_create, ++ .may_delete = ext3_may_delete, + }; +--- a/fs/ext3/namei.h ++++ b/fs/ext3/namei.h +@@ -5,4 +5,5 @@ + * + */ + ++extern int ext3_permission (struct inode *, int); + extern struct dentry *ext3_get_parent(struct dentry *child); +--- /dev/null ++++ b/fs/ext3/nfs4acl.c +@@ -0,0 +1,378 @@ ++/* ++ * Copyright (C) 2006 Andreas Gruenbacher ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2, or (at your option) any ++ * later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "namei.h" ++#include "xattr.h" ++#include "nfs4acl.h" ++ ++static inline struct nfs4acl * ++ext3_iget_nfs4acl(struct inode *inode) ++{ ++ struct nfs4acl *acl = EXT3_NFS4ACL_NOT_CACHED; ++ struct ext3_inode_info *ei = EXT3_I(inode); ++ ++ spin_lock(&inode->i_lock); ++ if (ei->i_nfs4acl != EXT3_NFS4ACL_NOT_CACHED) ++ acl = nfs4acl_get(ei->i_nfs4acl); ++ spin_unlock(&inode->i_lock); ++ ++ return acl; ++} ++ ++static inline void ++ext3_iset_nfs4acl(struct inode *inode, struct nfs4acl *acl) ++{ ++ struct ext3_inode_info *ei = EXT3_I(inode); ++ ++ spin_lock(&inode->i_lock); ++ if (ei->i_nfs4acl != EXT3_NFS4ACL_NOT_CACHED) ++ nfs4acl_put(ei->i_nfs4acl); ++ ei->i_nfs4acl = nfs4acl_get(acl); ++ spin_unlock(&inode->i_lock); ++} ++ ++static struct nfs4acl * ++ext3_get_nfs4acl(struct inode *inode) ++{ ++ const int name_index = EXT3_XATTR_INDEX_NFS4ACL; ++ void *value = NULL; ++ struct nfs4acl *acl; ++ int retval; ++ ++ if (!test_opt(inode->i_sb, NFS4ACL)) ++ return NULL; ++ ++ acl = ext3_iget_nfs4acl(inode); ++ if (acl != EXT3_NFS4ACL_NOT_CACHED) ++ return acl; ++ retval = ext3_xattr_get(inode, name_index, "", NULL, 0); ++ if (retval > 0) { ++ value = kmalloc(retval, GFP_KERNEL); ++ if (!value) ++ return ERR_PTR(-ENOMEM); ++ retval = ext3_xattr_get(inode, name_index, "", value, retval); ++ } ++ if (retval > 0) { ++ acl = nfs4acl_from_xattr(value, retval); ++ if (acl == ERR_PTR(-EINVAL)) ++ acl = ERR_PTR(-EIO); ++ } else if (retval == -ENODATA || retval == -ENOSYS) ++ acl = NULL; ++ else ++ acl = ERR_PTR(retval); ++ kfree(value); ++ ++ if (!IS_ERR(acl)) ++ ext3_iset_nfs4acl(inode, acl); ++ ++ return acl; ++} ++ ++static int ++ext3_set_nfs4acl(handle_t *handle, struct inode *inode, struct nfs4acl *acl) ++{ ++ const int name_index = EXT3_XATTR_INDEX_NFS4ACL; ++ size_t size = 0; ++ void *value = NULL; ++ int retval; ++ ++ if (acl) { ++ size = nfs4acl_xattr_size(acl); ++ value = kmalloc(size, GFP_KERNEL); ++ if (!value) ++ return -ENOMEM; ++ nfs4acl_to_xattr(acl, value); ++ } ++ if (handle) ++ retval = ext3_xattr_set_handle(handle, inode, name_index, "", ++ value, size, 0); ++ else ++ retval = ext3_xattr_set(inode, name_index, "", value, size, 0); ++ if (value) ++ kfree(value); ++ if (!retval) ++ ext3_iset_nfs4acl(inode, acl); ++ ++ return retval; ++} ++ ++int ++ext3_nfs4acl_permission(struct inode *inode, unsigned int mask) ++{ ++ struct nfs4acl *acl; ++ int retval; ++ ++ BUG_ON(!test_opt(inode->i_sb, NFS4ACL)); ++ ++ acl = ext3_get_nfs4acl(inode); ++ if (!acl) ++ retval = nfs4acl_generic_permission(inode, mask); ++ else if (IS_ERR(acl)) ++ retval = PTR_ERR(acl); ++ else { ++ retval = nfs4acl_permission(inode, acl, mask); ++ nfs4acl_put(acl); ++ } ++ ++ return retval; ++} ++ ++int ext3_may_create(struct inode *dir, int isdir) ++{ ++ int error; ++ ++ if (test_opt(dir->i_sb, NFS4ACL)) { ++ unsigned int mask = (isdir ? ACE4_ADD_SUBDIRECTORY : ACE4_ADD_FILE) | ++ ACE4_EXECUTE; ++ ++ error = ext3_nfs4acl_permission(dir, mask); ++ } else ++ error = ext3_permission(dir, MAY_WRITE | MAY_EXEC); ++ ++ return error; ++} ++ ++static int check_sticky(struct inode *dir, struct inode *inode) ++{ ++ if (!(dir->i_mode & S_ISVTX)) ++ return 0; ++ if (inode->i_uid == current_fsuid()) ++ return 0; ++ if (dir->i_uid == current_fsuid()) ++ return 0; ++ return !capable(CAP_FOWNER); ++} ++ ++int ext3_may_delete(struct inode *dir, struct inode *inode) ++{ ++ int error; ++ ++ if (test_opt(inode->i_sb, NFS4ACL)) { ++ error = ext3_nfs4acl_permission(dir, ACE4_DELETE_CHILD | ACE4_EXECUTE); ++ if (!error && check_sticky(dir, inode)) ++ error = -EPERM; ++ if (error && !ext3_nfs4acl_permission(inode, ACE4_DELETE)) ++ error = 0; ++ } else { ++ error = ext3_permission(dir, MAY_WRITE | MAY_EXEC); ++ if (!error && check_sticky(dir, inode)) ++ error = -EPERM; ++ } ++ ++ return error; ++} ++ ++int ++ext3_nfs4acl_init(handle_t *handle, struct inode *inode, struct inode *dir) ++{ ++ struct nfs4acl *dir_acl = NULL, *acl; ++ int retval; ++ ++ if (!S_ISLNK(inode->i_mode)) ++ dir_acl = ext3_get_nfs4acl(dir); ++ if (!dir_acl || IS_ERR(dir_acl)) { ++ inode->i_mode &= ~current->fs->umask; ++ return PTR_ERR(dir_acl); ++ } ++ acl = nfs4acl_inherit(dir_acl, inode->i_mode); ++ nfs4acl_put(dir_acl); ++ ++ retval = PTR_ERR(acl); ++ if (acl && !IS_ERR(acl)) { ++ retval = ext3_set_nfs4acl(handle, inode, acl); ++ inode->i_mode = (inode->i_mode & ~S_IRWXUGO) | ++ nfs4acl_masks_to_mode(acl); ++ nfs4acl_put(acl); ++ } ++ return retval; ++} ++ ++int ++ext3_nfs4acl_chmod(struct inode *inode) ++{ ++ struct nfs4acl *acl; ++ int retval; ++ ++ if (S_ISLNK(inode->i_mode)) ++ return -EOPNOTSUPP; ++ acl = ext3_get_nfs4acl(inode); ++ if (!acl || IS_ERR(acl)) ++ return PTR_ERR(acl); ++ acl = nfs4acl_chmod(acl, inode->i_mode); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ retval = ext3_set_nfs4acl(NULL, inode, acl); ++ nfs4acl_put(acl); ++ ++ return retval; ++} ++ ++static size_t ++ext3_xattr_list_nfs4acl(struct dentry *dentry, char *list, size_t list_len, ++ const char *name, size_t name_len, int handler_flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ const size_t size = sizeof(NFS4ACL_XATTR); ++ ++ if (!test_opt(inode->i_sb, NFS4ACL)) ++ return 0; ++ if (list && size <= list_len) ++ memcpy(list, NFS4ACL_XATTR, size); ++ return size; ++} ++ ++static int ++ext3_xattr_get_nfs4acl(struct dentry *dentry, const char *name, void *buffer, ++ size_t buffer_size, int handler_flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct nfs4acl *acl; ++ size_t size; ++ ++ if (!test_opt(inode->i_sb, NFS4ACL)) ++ return -EOPNOTSUPP; ++ if (strcmp(name, "") != 0) ++ return -EINVAL; ++ ++ acl = ext3_get_nfs4acl(inode); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ if (acl == NULL) ++ return -ENODATA; ++ size = nfs4acl_xattr_size(acl); ++ if (buffer) { ++ if (size > buffer_size) ++ return -ERANGE; ++ nfs4acl_to_xattr(acl, buffer); ++ } ++ nfs4acl_put(acl); ++ ++ return size; ++} ++ ++#ifdef NFS4ACL_DEBUG ++static size_t ++ext3_xattr_list_masked_nfs4acl(struct dentry *dentry, char *list, ++ size_t list_len, const char *name, ++ size_t name_len, int handler_flags) ++{ ++ return 0; ++} ++ ++static int ++ext3_xattr_get_masked_nfs4acl(struct dentry *dentry, const char *name, ++ void *buffer, size_t buffer_size, ++ int handler_flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ const int name_index = EXT3_XATTR_INDEX_NFS4ACL; ++ struct nfs4acl *acl; ++ void *xattr; ++ size_t size; ++ int retval; ++ ++ if (!test_opt(inode->i_sb, NFS4ACL)) ++ return -EOPNOTSUPP; ++ if (strcmp(name, "") != 0) ++ return -EINVAL; ++ retval = ext3_xattr_get(inode, name_index, "", NULL, 0); ++ if (retval <= 0) ++ return retval; ++ xattr = kmalloc(retval, GFP_KERNEL); ++ if (!xattr) ++ return -ENOMEM; ++ retval = ext3_xattr_get(inode, name_index, "", xattr, retval); ++ if (retval <= 0) ++ return retval; ++ acl = nfs4acl_from_xattr(xattr, retval); ++ kfree(xattr); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ retval = nfs4acl_apply_masks(&acl); ++ if (retval) { ++ nfs4acl_put(acl); ++ return retval; ++ } ++ size = nfs4acl_xattr_size(acl); ++ if (buffer) { ++ if (size > buffer_size) ++ return -ERANGE; ++ nfs4acl_to_xattr(acl, buffer); ++ } ++ nfs4acl_put(acl); ++ return size; ++} ++#endif ++ ++static int ++ext3_xattr_set_nfs4acl(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags, ++ int handler_flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ handle_t *handle; ++ struct nfs4acl *acl = NULL; ++ int retval, retries = 0; ++ ++ if (S_ISLNK(inode->i_mode) || !test_opt(inode->i_sb, NFS4ACL)) ++ return -EOPNOTSUPP; ++ if (strcmp(name, "") != 0) ++ return -EINVAL; ++ if (current_fsuid() != inode->i_uid && ++ ext3_nfs4acl_permission(inode, ACE4_WRITE_ACL) && ++ !capable(CAP_FOWNER)) ++ return -EPERM; ++ if (value) { ++ acl = nfs4acl_from_xattr(value, size); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ ++ inode->i_mode &= ~S_IRWXUGO; ++ inode->i_mode |= nfs4acl_masks_to_mode(acl); ++ } ++ ++retry: ++ handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb)); ++ if (IS_ERR(handle)) ++ return PTR_ERR(handle); ++ ext3_mark_inode_dirty(handle, inode); ++ retval = ext3_set_nfs4acl(handle, inode, acl); ++ ext3_journal_stop(handle); ++ if (retval == ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) ++ goto retry; ++ nfs4acl_put(acl); ++ return retval; ++} ++ ++struct xattr_handler ext3_nfs4acl_xattr_handler = { ++ .prefix = NFS4ACL_XATTR, ++ .list = ext3_xattr_list_nfs4acl, ++ .get = ext3_xattr_get_nfs4acl, ++ .set = ext3_xattr_set_nfs4acl, ++}; ++ ++#ifdef NFS4ACL_DEBUG ++struct xattr_handler ext3_masked_nfs4acl_xattr_handler = { ++ .prefix = "system.masked-nfs4acl", ++ .list = ext3_xattr_list_masked_nfs4acl, ++ .get = ext3_xattr_get_masked_nfs4acl, ++ .set = ext3_xattr_set_nfs4acl, ++}; ++#endif +--- /dev/null ++++ b/fs/ext3/nfs4acl.h +@@ -0,0 +1,36 @@ ++#ifndef __FS_EXT3_NFS4ACL_H ++#define __FS_EXT3_NFS4ACL_H ++ ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ ++#include ++ ++/* Value for i_nfs4acl if NFS4ACL has not been cached */ ++#define EXT3_NFS4ACL_NOT_CACHED ((void *)-1) ++ ++extern int ext3_nfs4acl_permission(struct inode *, unsigned int); ++extern int ext3_may_create(struct inode *, int); ++extern int ext3_may_delete(struct inode *, struct inode *); ++extern int ext3_nfs4acl_init(handle_t *, struct inode *, struct inode *); ++extern int ext3_nfs4acl_chmod(struct inode *); ++ ++#else /* CONFIG_FS_EXT3_NFS4ACL */ ++ ++#define ext3_may_create NULL ++#define ext3_may_delete NULL ++ ++static inline int ++ext3_nfs4acl_init(handle_t *handle, struct inode *inode, struct inode *dir) ++{ ++ return 0; ++} ++ ++static inline int ++ext3_nfs4acl_chmod(struct inode *inode) ++{ ++ return 0; ++} ++ ++#endif /* CONFIG_FS_EXT3_NFS4ACL */ ++ ++#endif /* __FS_EXT3_NFS4ACL_H */ +--- a/fs/ext3/super.c ++++ b/fs/ext3/super.c +@@ -36,12 +36,14 @@ + #include + #include + #include ++#include + #include + + #include + + #include "xattr.h" + #include "acl.h" ++#include "nfs4acl.h" + #include "namei.h" + + #ifdef CONFIG_EXT3_DEFAULTS_TO_ORDERED +@@ -476,6 +478,9 @@ static struct inode *ext3_alloc_inode(st + ei = kmem_cache_alloc(ext3_inode_cachep, GFP_NOFS); + if (!ei) + return NULL; ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ ei->i_nfs4acl = EXT3_NFS4ACL_NOT_CACHED; ++#endif + ei->i_block_alloc_info = NULL; + ei->vfs_inode.i_version = 1; + atomic_set(&ei->i_datasync_tid, 0); +@@ -529,6 +534,13 @@ static void ext3_clear_inode(struct inod + { + struct ext3_block_alloc_info *rsv = EXT3_I(inode)->i_block_alloc_info; + ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ if (EXT3_I(inode)->i_nfs4acl && ++ EXT3_I(inode)->i_nfs4acl != EXT3_NFS4ACL_NOT_CACHED) { ++ nfs4acl_put(EXT3_I(inode)->i_nfs4acl); ++ EXT3_I(inode)->i_nfs4acl = EXT3_NFS4ACL_NOT_CACHED; ++ } ++#endif + dquot_drop(inode); + ext3_discard_reservation(inode); + EXT3_I(inode)->i_block_alloc_info = NULL; +@@ -803,7 +815,7 @@ enum { + Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, + Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, + Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, +- Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, ++ Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_acl_flavor, Opt_noacl, + Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh, Opt_bh, + Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev, + Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, +@@ -836,6 +848,7 @@ static const match_table_t tokens = { + {Opt_user_xattr, "user_xattr"}, + {Opt_nouser_xattr, "nouser_xattr"}, + {Opt_acl, "acl"}, ++ {Opt_acl_flavor, "acl=%s"}, + {Opt_noacl, "noacl"}, + {Opt_reservation, "reservation"}, + {Opt_noreservation, "noreservation"}, +@@ -1040,20 +1053,33 @@ static int parse_options (char *options, + "(no)user_xattr options not supported"); + break; + #endif +-#ifdef CONFIG_EXT3_FS_POSIX_ACL + case Opt_acl: +- set_opt(sbi->s_mount_opt, POSIX_ACL); ++ args[0].to = args[0].from; ++ /* fall through */ ++ case Opt_acl_flavor: ++#ifdef CONFIG_EXT3_FS_POSIX_ACL ++ if (match_string(&args[0], "") || ++ match_string(&args[0], "posix")) { ++ set_opt(sbi->s_mount_opt, POSIX_ACL); ++ clear_opt(sbi->s_mount_opt, NFS4ACL); ++ } else ++#endif ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ if (match_string(&args[0], "nfs4")) { ++ clear_opt(sbi->s_mount_opt, POSIX_ACL); ++ set_opt(sbi->s_mount_opt, NFS4ACL); ++ } else ++#endif ++ { ++ ext3_msg(sb, KERN_ERR, ++ "unsupported acl flavor"); ++ return 0; ++ } + break; + case Opt_noacl: + clear_opt(sbi->s_mount_opt, POSIX_ACL); ++ clear_opt(sbi->s_mount_opt, NFS4ACL); + break; +-#else +- case Opt_acl: +- case Opt_noacl: +- ext3_msg(sb, KERN_INFO, +- "(no)acl options not supported"); +- break; +-#endif + case Opt_reservation: + set_opt(sbi->s_mount_opt, RESERVATION); + break; +@@ -1698,8 +1724,11 @@ static int ext3_fill_super (struct super + NULL, 0)) + goto failed_mount; + +- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | +- (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); ++ sb->s_flags = (sb->s_flags & ~MS_POSIXACL); ++ if (test_opt(sb, POSIX_ACL)) ++ sb->s_flags |= MS_POSIXACL; ++ if (test_opt(sb, NFS4ACL)) ++ sb->s_flags |= MS_POSIXACL | MS_WITHAPPEND; + + if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV && + (EXT3_HAS_COMPAT_FEATURE(sb, ~0U) || +@@ -2576,8 +2605,12 @@ static int ext3_remount (struct super_bl + if (test_opt(sb, ABORT)) + ext3_abort(sb, __func__, "Abort forced by user"); + +- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | +- (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); ++ sb->s_flags = (sb->s_flags & ~MS_POSIXACL); ++ if (test_opt(sb, POSIX_ACL)) ++ sb->s_flags |= MS_POSIXACL; ++ if (test_opt(sb, NFS4ACL)) ++ sb->s_flags |= MS_POSIXACL; ++ + + es = sbi->s_es; + +--- a/fs/ext3/xattr.c ++++ b/fs/ext3/xattr.c +@@ -114,6 +114,9 @@ static struct xattr_handler *ext3_xattr_ + #ifdef CONFIG_EXT3_FS_SECURITY + [EXT3_XATTR_INDEX_SECURITY] = &ext3_xattr_security_handler, + #endif ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ [EXT3_XATTR_INDEX_NFS4ACL] = &ext3_nfs4acl_xattr_handler, ++#endif + }; + + struct xattr_handler *ext3_xattr_handlers[] = { +@@ -126,6 +129,12 @@ struct xattr_handler *ext3_xattr_handler + #ifdef CONFIG_EXT3_FS_SECURITY + &ext3_xattr_security_handler, + #endif ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ &ext3_nfs4acl_xattr_handler, ++#ifdef NFS4ACL_DEBUG ++ &ext3_masked_nfs4acl_xattr_handler, ++#endif ++#endif + NULL + }; + +--- a/fs/ext3/xattr.h ++++ b/fs/ext3/xattr.h +@@ -21,6 +21,7 @@ + #define EXT3_XATTR_INDEX_TRUSTED 4 + #define EXT3_XATTR_INDEX_LUSTRE 5 + #define EXT3_XATTR_INDEX_SECURITY 6 ++#define EXT3_XATTR_INDEX_NFS4ACL 7 + + struct ext3_xattr_header { + __le32 h_magic; /* magic number for identification */ +@@ -63,6 +64,10 @@ extern struct xattr_handler ext3_xattr_t + extern struct xattr_handler ext3_xattr_acl_access_handler; + extern struct xattr_handler ext3_xattr_acl_default_handler; + extern struct xattr_handler ext3_xattr_security_handler; ++extern struct xattr_handler ext3_nfs4acl_xattr_handler; ++#ifdef NFS4ACL_DEBUG ++extern struct xattr_handler ext3_masked_nfs4acl_xattr_handler; ++#endif + + extern ssize_t ext3_listxattr(struct dentry *, char *, size_t); + +--- a/include/linux/ext3_fs.h ++++ b/include/linux/ext3_fs.h +@@ -406,6 +406,7 @@ struct ext3_inode { + #define EXT3_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */ + #define EXT3_MOUNT_DATA_ERR_ABORT 0x400000 /* Abort on file data write + * error in ordered mode */ ++#define EXT3_MOUNT_NFS4ACL 0x800000 /* NFS version 4 ACLs */ + + /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ + #ifndef _LINUX_EXT2_FS_H +--- a/include/linux/ext3_fs_i.h ++++ b/include/linux/ext3_fs_i.h +@@ -103,6 +103,9 @@ struct ext3_inode_info { + */ + struct rw_semaphore xattr_sem; + #endif ++#ifdef CONFIG_EXT3_FS_NFS4ACL ++ struct nfs4acl *i_nfs4acl; ++#endif + + struct list_head i_orphan; /* unlinked but open inodes */ + diff --git a/patches.suse/nfsacl-client-cache-CHECK.diff b/patches.suse/nfsacl-client-cache-CHECK.diff new file mode 100644 index 0000000..add3ead --- /dev/null +++ b/patches.suse/nfsacl-client-cache-CHECK.diff @@ -0,0 +1,76 @@ +From: Andreas Gruenbacher +Subject: nfsacl: improve cache consistency + +(This one is currently disabled.) + +Index: linux-2.6.11-rc2/fs/nfs/inode.c +=================================================================== +--- linux-2.6.11-rc2.orig/fs/nfs/inode.c ++++ linux-2.6.11-rc2/fs/nfs/inode.c +@@ -65,13 +65,8 @@ static int nfs_statfs(struct super_bloc + static int nfs_show_options(struct seq_file *, struct vfsmount *); + + #ifdef CONFIG_NFS_ACL +-static void nfs_forget_cached_acls(struct inode *); + static void __nfs_forget_cached_acls(struct nfs_inode *nfsi); + #else +-static inline void nfs_forget_cached_acls(struct inode *inode) +-{ +-} +- + static inline void __nfs_forget_cached_acls(struct nfs_inode *nfsi) + { + } +@@ -1188,7 +1183,7 @@ static void __nfs_forget_cached_acls(str + #endif /* CONFIG_NFS_ACL */ + + #ifdef CONFIG_NFS_ACL +-static void nfs_forget_cached_acls(struct inode *inode) ++void nfs_forget_cached_acls(struct inode *inode) + { + dprintk("NFS: nfs_forget_cached_acls(%s/%ld)\n", inode->i_sb->s_id, + inode->i_ino); +@@ -1293,6 +1288,8 @@ int nfs_refresh_inode(struct inode *inod + if ((fattr->valid & NFS_ATTR_WCC) != 0) { + if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) + memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); ++ else ++ nfs_forget_cached_acls(inode); + if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) + memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); + } +Index: linux-2.6.11-rc2/fs/nfs/nfs3proc.c +=================================================================== +--- linux-2.6.11-rc2.orig/fs/nfs/nfs3proc.c ++++ linux-2.6.11-rc2/fs/nfs/nfs3proc.c +@@ -876,7 +876,11 @@ nfs3_proc_setacls(struct inode *inode, s + acl = NULL; + } + } +- nfs_cache_acls(inode, acl, dfacl); ++ if ((fattr.valid & NFS_ATTR_WCC) && ++ timespec_equal(&inode->i_ctime, &fattr.pre_ctime)) ++ nfs_cache_acls(inode, acl, dfacl); ++ else ++ nfs_forget_cached_acls(inode); + status = nfs_refresh_inode(inode, &fattr); + } + +Index: linux-2.6.11-rc2/include/linux/nfs_fs.h +=================================================================== +--- linux-2.6.11-rc2.orig/include/linux/nfs_fs.h ++++ linux-2.6.11-rc2/include/linux/nfs_fs.h +@@ -293,6 +293,13 @@ extern struct inode *nfs_fhget(struct su + struct nfs_fattr *); + extern struct posix_acl *nfs_get_cached_acl(struct inode *, int); + extern void nfs_cache_acls(struct inode *, struct posix_acl *, struct posix_acl *); ++#ifdef CONFIG_NFS_ACL ++void nfs_forget_cached_acls(struct inode *); ++#else ++static inline void nfs_forget_cached_acls(struct inode *inode) ++{ ++} ++#endif + extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); + extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); + extern int nfs_permission(struct inode *, int, struct nameidata *); diff --git a/patches.suse/no-frame-pointer-select b/patches.suse/no-frame-pointer-select new file mode 100644 index 0000000..29454f2 --- /dev/null +++ b/patches.suse/no-frame-pointer-select @@ -0,0 +1,37 @@ +From: Andi Kleen +Subject: Fix stack unwinder Kconfig +Patch-mainline: no +References: bnc#402518 + +Incremental patch for dwarf2 unwinder + +Fix the Kconfigs that do SELECT FRAME_POINTER to do select UNWIND_INFO +instead. + +Signed-off-by: Andi Kleen +Acked-by: Jan Beulich + +--- + lib/Kconfig.debug | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/lib/Kconfig.debug ++++ b/lib/Kconfig.debug +@@ -965,13 +965,15 @@ config FAULT_INJECTION_STACKTRACE_FILTER + depends on FAULT_INJECTION_DEBUG_FS && STACKTRACE_SUPPORT + depends on !X86_64 + select STACKTRACE +- select FRAME_POINTER if !PPC && !S390 ++ select FRAME_POINTER if !PPC && !S390 && !X86 ++ select UNWIND_INFO if X86 && !FRAME_POINTER + help + Provide stacktrace filter for fault-injection capabilities + + config LATENCYTOP + bool "Latency measuring infrastructure" +- select FRAME_POINTER if !MIPS && !PPC && !S390 ++ select FRAME_POINTER if !MIPS && !PPC && !S390 && !X86 ++ select UNWIND_INFO if X86 && !FRAME_POINTER + select KALLSYMS + select KALLSYMS_ALL + select STACKTRACE diff --git a/patches.suse/no-partition-scan b/patches.suse/no-partition-scan new file mode 100644 index 0000000..00c1717 --- /dev/null +++ b/patches.suse/no-partition-scan @@ -0,0 +1,108 @@ +From: Hannes Reinecke +Subject: Implement 'no_partition_scan' commandline option +Refences: FATE#303697 +Patch-mainline: Not yet + +Under certain setups the partition table on the disk is not +useable directly (eg for dmraid or multipathing). So we should +be able to switch it off completely so as not to be flooded with +pointless messages. + +Signed-off-by: Hannes Reinecke + +--- + block/genhd.c | 39 +++++++++++++++++++++++++++++++++++++-- + fs/partitions/check.c | 2 ++ + include/linux/genhd.h | 1 + + 3 files changed, 40 insertions(+), 2 deletions(-) + +--- a/block/genhd.c ++++ b/block/genhd.c +@@ -513,6 +513,18 @@ static int exact_lock(dev_t devt, void * + return 0; + } + ++static int __read_mostly no_partition_scan; ++ ++static int __init no_partition_scan_setup(char *str) ++{ ++ no_partition_scan = 1; ++ printk(KERN_INFO "genhd: omit partition scan.\n"); ++ ++ return 1; ++} ++ ++__setup("no_partition_scan", no_partition_scan_setup); ++ + /** + * add_disk - add partitioning information to kernel list + * @disk: per-device partitioning information +@@ -537,6 +549,9 @@ void add_disk(struct gendisk *disk) + + disk->flags |= GENHD_FL_UP; + ++ if (no_partition_scan) ++ disk->flags |= GENHD_FL_NO_PARTITION_SCAN; ++ + retval = blk_alloc_devt(&disk->part0, &devt); + if (retval) { + WARN_ON(1); +@@ -825,7 +840,27 @@ static ssize_t disk_range_show(struct de + { + struct gendisk *disk = dev_to_disk(dev); + +- return sprintf(buf, "%d\n", disk->minors); ++ return sprintf(buf, "%d\n", ++ (disk->flags & GENHD_FL_NO_PARTITION_SCAN ? 0 : disk->minors)); ++} ++ ++static ssize_t disk_range_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct gendisk *disk = dev_to_disk(dev); ++ int i; ++ ++ if (count > 0 && sscanf(buf, "%d", &i) > 0) { ++ if (i == 0) ++ disk->flags |= GENHD_FL_NO_PARTITION_SCAN; ++ else if (i <= disk->minors) ++ disk->flags &= ~GENHD_FL_NO_PARTITION_SCAN; ++ else ++ count = -EINVAL; ++ } ++ ++ return count; + } + + static ssize_t disk_ext_range_show(struct device *dev, +@@ -879,7 +914,7 @@ static ssize_t disk_discard_alignment_sh + return sprintf(buf, "%d\n", queue_discard_alignment(disk->queue)); + } + +-static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); ++static DEVICE_ATTR(range, S_IRUGO|S_IWUSR, disk_range_show, disk_range_store); + static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); + static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); + static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); +--- a/fs/partitions/check.c ++++ b/fs/partitions/check.c +@@ -559,6 +559,8 @@ int rescan_partitions(struct gendisk *di + disk->fops->revalidate_disk(disk); + check_disk_size_change(disk, bdev); + bdev->bd_invalidated = 0; ++ if (disk->flags & GENHD_FL_NO_PARTITION_SCAN) ++ return 0; + if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) + return 0; + if (IS_ERR(state)) /* I/O error reading the partition table */ +--- a/include/linux/genhd.h ++++ b/include/linux/genhd.h +@@ -122,6 +122,7 @@ struct hd_struct { + #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 + #define GENHD_FL_EXT_DEVT 64 /* allow extended devt */ + #define GENHD_FL_NATIVE_CAPACITY 128 ++#define GENHD_FL_NO_PARTITION_SCAN 256 + + #define BLK_SCSI_MAX_CMDS (256) + #define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) diff --git a/patches.suse/novfs-client-module b/patches.suse/novfs-client-module new file mode 100644 index 0000000..9c6327e --- /dev/null +++ b/patches.suse/novfs-client-module @@ -0,0 +1,15977 @@ +From 9297af3ffd8a1c98f35fb7a273386576e061ff16 Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Thu, 27 Mar 2008 10:40:48 -0700 +Subject: novfs: Add the Novell filesystem client kernel module +Patch-mainline: not yet, being worked on. + +This adds the Novell filesystem client kernel module. + +Things to do before it can be submitted: + - coding style cleanups + - remove typedefs + - function name lowercase + - 80 chars wide + - sparse cleanups + - __user markings + - endian markings + - remove functions that are never called and structures never used + - yeah, there are a lot of them... + - remove wrapper functions + - private kmalloc/free? + - resolve FIXME markings that have been added to the code + - wrong types passed to functions!!! + - userspace interface revisit + - uses /proc/novfs, not nice. + - might need userspace tools rework + - use of semaphore as mutex + - abuse of semaphore in lieu of completions. + +Update May 13 2009 jeffm: +- Merged patches back into master novfs patch + +Cc: Lonnie Iverson +Signed-off-by: Greg Kroah-Hartman + +--- + fs/Kconfig | 1 + fs/Makefile | 1 + fs/novfs/Kconfig | 8 + fs/novfs/Makefile | 19 + fs/novfs/commands.h | 955 ++++++++++ + fs/novfs/daemon.c | 2085 +++++++++++++++++++++++ + fs/novfs/file.c | 1921 +++++++++++++++++++++ + fs/novfs/inode.c | 4638 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + fs/novfs/nwcapi.c | 2202 ++++++++++++++++++++++++ + fs/novfs/nwcapi.h | 1416 +++++++++++++++ + fs/novfs/nwerror.h | 658 +++++++ + fs/novfs/proc.c | 149 + + fs/novfs/profile.c | 704 +++++++ + fs/novfs/scope.c | 659 +++++++ + fs/novfs/vfs.h | 454 +++++ + 15 files changed, 15870 insertions(+) + +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -260,6 +260,7 @@ source "fs/ncpfs/Kconfig" + source "fs/coda/Kconfig" + source "fs/afs/Kconfig" + source "fs/9p/Kconfig" ++source "fs/novfs/Kconfig" + + endif # NETWORK_FILESYSTEMS + +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -127,4 +127,5 @@ obj-$(CONFIG_OCFS2_FS) += ocfs2/ + obj-$(CONFIG_BTRFS_FS) += btrfs/ + obj-$(CONFIG_GFS2_FS) += gfs2/ + obj-$(CONFIG_EXOFS_FS) += exofs/ ++obj-$(CONFIG_NOVFS) += novfs/ + obj-$(CONFIG_CEPH_FS) += ceph/ +--- /dev/null ++++ b/fs/novfs/Kconfig +@@ -0,0 +1,8 @@ ++config NOVFS ++ tristate "Novell Netware Filesystem support (novfs) (EXPERIMENTAL)" ++ depends on INET && EXPERIMENTAL ++ help ++ If you say Y here, you will get an experimental Novell Netware ++ filesystem driver. ++ ++ If unsure, say N. +--- /dev/null ++++ b/fs/novfs/Makefile +@@ -0,0 +1,19 @@ ++# ++# Makefile for the Novell NetWare Client for Linux filesystem. ++# ++ ++NOVFS_VFS_MAJOR = 2 ++NOVFS_VFS_MINOR = 0 ++NOVFS_VFS_SUB = 0 ++NOVFS_VFS_RELEASE = 440 ++ ++EXTRA_CFLAGS += -DNOVFS_VFS_MAJOR=$(NOVFS_VFS_MAJOR) ++EXTRA_CFLAGS += -DNOVFS_VFS_MINOR=$(NOVFS_VFS_MINOR) ++EXTRA_CFLAGS += -DNOVFS_VFS_SUB=$(NOVFS_VFS_SUB) ++EXTRA_CFLAGS += -DNOVFS_VFS_PATCH=$(NOVFS_VFS_PATCH) ++EXTRA_CFLAGS += -DNOVFS_VFS_RELEASE=$(NOVFS_VFS_RELEASE) ++ ++obj-$(CONFIG_NOVFS) += novfs.o ++ ++novfs-objs := inode.o proc.o profile.o daemon.o file.o scope.o nwcapi.o ++ +--- /dev/null ++++ b/fs/novfs/commands.h +@@ -0,0 +1,955 @@ ++/* ++ * NetWare Redirector for Linux ++ * Author: James Turner/Richard Williams ++ * ++ * This file contains all defined commands. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++ ++#ifndef __NOVFS_COMMANDS_H ++#define __NOVFS_COMMANDS_H ++ ++#define VFS_COMMAND_GET_CONNECTED_SERVER_LIST 0 ++#define VFS_COMMAND_GET_SERVER_VOLUME_LIST 1 ++#define VFS_COMMAND_VERIFY_FILE 2 ++#define VFS_COMMAND_OPEN_CONNECTION_BY_ADDR 3 ++#define VFS_COMMAND_LOGIN_IDENTITY 4 ++#define VFS_COMMAND_ENUMERATE_DIRECTORY 5 ++#define VFS_COMMAND_OPEN_FILE 6 ++#define VFS_COMMAND_CREATE_FILE 7 ++#define VFS_COMMAND_CLOSE_FILE 8 ++#define VFS_COMMAND_READ_FILE 9 ++#define VFS_COMMAND_WRITE_FILE 10 ++#define VFS_COMMAND_DELETE_FILE 11 ++#define VFS_COMMAND_CREATE_DIRECOTRY 12 ++#define VFS_COMMAND_START_ENUMERATE 13 ++#define VFS_COMMAND_END_ENUMERATE 14 ++#define VFS_COMMAND_LOGIN_USER 15 ++#define VFS_COMMAND_LOGOUT_USER 16 ++#define VFS_COMMAND_CREATE_CONTEXT 17 ++#define VFS_COMMAND_DESTROY_CONTEXT 18 ++#define VFS_COMMAND_SET_FILE_INFO 19 ++#define VFS_COMMAND_TRUNCATE_FILE 20 ++#define VFS_COMMAND_OPEN_CONNECTION_BY_NAME 21 ++#define VFS_COMMAND_XPLAT_CALL 22 ++#define VFS_COMMAND_RENAME_FILE 23 ++#define VFS_COMMAND_ENUMERATE_DIRECTORY_EX 24 ++#define VFS_COMMAND_GETPWUD 25 ++#define VFS_COMMAND_ENUM_XCONN 26 ++#define VFS_COMMAND_READ_STREAM 27 ++#define VFS_COMMAND_WRITE_STREAM 28 ++#define VFS_COMMAND_CLOSE_STREAM 29 ++#define VFS_COMMAND_GET_VERSION 30 ++#define VFS_COMMAND_SET_MOUNT_PATH 31 ++#define VFS_COMMAND_GET_USER_SPACE 32 ++#define VFS_COMMAND_DBG 33 ++#define VFS_COMMAND_GET_CACHE_FLAG 34 ++#define VFS_COMMAND_GET_EXTENDED_ATTRIBUTE 35 ++#define VFS_COMMAND_LIST_EXTENDED_ATTRIBUTES 36 ++#define VFS_COMMAND_SET_EXTENDED_ATTRIBUTE 37 ++#define VFS_COMMAND_SET_FILE_LOCK 38 ++ ++#define NWD_ACCESS_QUERY 0x00000001 ++#define NWD_ACCESS_READ 0x00000002 ++#define NWD_ACCESS_WRITE 0x00000004 ++#define NWD_ACCESS_EXECUTE 0x00000008 ++#define NWD_ACCESS_VALID 0x0000000F ++ ++/* ++ Share Mode ++ ++ A value of zero in a shared mode field specifies the caller ++ desires exclusive access to the object. ++*/ ++ ++#define NWD_SHARE_READ 0x00000001 ++#define NWD_SHARE_WRITE 0x00000002 ++#define NWD_SHARE_DELETE 0x00000004 ++#define NWD_SHARE_VALID 0x00000007 ++ ++/* ++ Creates a new file. The create API will fail if the specified ++ file already exists. ++*/ ++#define NWD_DISP_CREATE_NEW 0x00000001 ++ ++/* ++ Creates a new file. If the specified file already exists, ++ the create API will overwrite the old file and clear the ++ existing attributes. ++*/ ++#define NWD_DISP_CREATE_ALWAYS 0x00000002 ++ ++/* ++ Opens the file. The API will fail if the file does not exist. ++*/ ++#define NWD_DISP_OPEN_EXISTING 0x00000003 ++ ++/* ++ Opens the file. If the file does not exist, the API will ++ create the file. ++*/ ++#define NWD_DISP_OPEN_ALWAYS 0x00000004 ++ ++/* ++ Opens the file. When the file is opened the API will truncate ++ the stream to zero bytes. The API will fail if the file ++ does not exist. ++*/ ++#define NWD_DISP_TRUNCATE_EXISTING 0x00000005 ++#define NWD_DISP_MAXIMUM 0x00000005 ++ ++/* ++ Open/Create returned information values ++ ++ The bottom two bytes of NWD_ACTION are returned ++ as a value. All values are mutually exclusive. ++*/ ++ ++#define NWD_ACTION_OPENED 0x00000001 ++#define NWD_ACTION_CREATED 0x00000002 ++ ++#define MAX_IO_SIZE (1024 * 32) ++ ++#define MAX_XATTR_NAME_LEN 255 ++#define MAX_PATH_LENGTH 255 ++#define ENOATTR ENODATA ++/*===[ Type definitions ]=================================================*/ ++ ++/*===[ Function prototypes ]==============================================*/ ++ ++#pragma pack(push, 1) ++ ++/*struct _ncl_string ++{ ++ unsigned int type; ++ unsigned char *buffer; ++ unsigned int len; ++ ++} NclString, *PNclString; ++*/ ++struct ncl_string { ++ unsigned int type; ++ unsigned char *buffer; ++ u32 len; ++}; ++ ++struct nwd_string { ++ unsigned int type; ++ unsigned int len; ++ unsigned int boffset; ++}; ++ ++struct novfs_command_request_header { ++ unsigned int CommandType; ++ unsigned long SequenceNumber; ++ struct novfs_schandle SessionId; ++ ++}; ++ ++struct novfs_command_reply_header { ++ unsigned long Sequence_Number; ++ unsigned int ErrorCode; ++ ++}; ++ ++ ++struct novfs_delete_file_request { ++ struct novfs_command_request_header Command; ++ unsigned int isDirectory; ++ unsigned int pathlength; ++ unsigned char path[1]; ++}; ++ ++struct novfs_delete_file_reply { ++ struct novfs_command_reply_header Reply; ++}; ++ ++struct novfs_get_connected_server_list { ++ struct novfs_command_request_header Command; ++}; ++ ++struct novfs_get_connected_server_list_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned char List[1]; ++}; ++ ++struct novfs_get_connected_server_list_request_ex { ++ struct novfs_command_request_header Command; ++}; ++ ++struct novfs_get_connected_server_list_reply_ex { ++ ++ struct novfs_command_reply_header Reply; ++ unsigned int bufferLen; ++ unsigned char List[1]; ++ ++}; ++ ++struct novfs_get_server_volume_list { ++ struct novfs_command_request_header Command; ++ unsigned int Length; ++ unsigned char Name[1]; ++}; ++ ++struct novfs_get_server_volume_list_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned char List[1]; ++}; ++ ++struct novfs_verify_file_request { ++ struct novfs_command_request_header Command; ++ unsigned int pathLen; ++ unsigned char path[1]; ++ ++}; ++ ++struct novfs_verify_file_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned int lastAccessTime; ++ unsigned int modifyTime; ++ unsigned int createTime; ++ unsigned long long fileSize; ++ unsigned int fileMode; ++ ++}; ++ ++struct novfs_begin_enumerate_directory_request { ++ struct novfs_command_request_header Command; ++ unsigned int pathLen; ++ unsigned char path[1]; ++ ++}; ++ ++struct novfs_begin_enumerate_directory_reply { ++ struct novfs_command_reply_header Reply; ++ void *enumerateHandle; ++ ++}; ++ ++struct novfs_end_enumerate_directory_request { ++ struct novfs_command_request_header Command; ++ void *enumerateHandle; ++ ++}; ++ ++struct novfs_end_enumerate_directory_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct novfs_enumerate_directory_request { ++ struct novfs_command_request_header Command; ++ void *enumerateHandle; ++ unsigned int pathLen; ++ unsigned char path[1]; ++ ++}; ++ ++struct novfs_enumerate_directory_reply { ++ struct novfs_command_reply_header Reply; ++ void *enumerateHandle; ++ unsigned int lastAccessTime; ++ unsigned int modifyTime; ++ unsigned int createTime; ++ unsigned long long size; ++ unsigned int mode; ++ unsigned int nameLen; ++ unsigned char name[1]; ++ ++}; ++ ++struct novfs_enumerate_directory_ex_request { ++ struct novfs_command_request_header Command; ++ void *enumerateHandle; ++ unsigned int pathLen; ++ unsigned char path[1]; ++ ++}; ++ ++struct novfs_enumerate_directory_ex_data { ++ unsigned int length; ++ unsigned int lastAccessTime; ++ unsigned int modifyTime; ++ unsigned int createTime; ++ unsigned long long size; ++ unsigned int mode; ++ unsigned int nameLen; ++ unsigned char name[1]; ++ ++}; ++ ++struct novfs_enumerate_directory_ex_reply { ++ struct novfs_command_reply_header Reply; ++ void *enumerateHandle; ++ unsigned int enumCount; ++ ++}; ++ ++struct novfs_open_file_request { ++ struct novfs_command_request_header Command; ++ unsigned int access; /* File Access */ ++ unsigned int mode; /* Sharing Mode */ ++ unsigned int disp; /* Create Disposition */ ++ unsigned int pathLen; ++ unsigned char path[1]; ++ ++}; ++ ++struct novfs_open_file_reply { ++ struct novfs_command_reply_header Reply; ++ void *handle; ++ unsigned int lastAccessTime; ++ unsigned int modifyTime; ++ unsigned int createTime; ++ unsigned int attributes; ++ loff_t size; ++ ++}; ++ ++struct novfs_create_file_request { ++ ++ struct novfs_command_request_header Command; ++ unsigned int pathlength; ++ unsigned char path[1]; ++ ++}; ++ ++struct novfs_create_file_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct novfs_close_file_request { ++ struct novfs_command_request_header Command; ++ void *handle; ++ ++}; ++ ++struct novfs_close_file_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct novfs_read_file_request { ++ struct novfs_command_request_header Command; ++ void *handle; ++ loff_t offset; ++ size_t len; ++ ++}; ++ ++struct novfs_read_file_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned long long bytesRead; ++ unsigned char data[1]; ++ ++}; ++ ++struct novfs_write_file_request { ++ struct novfs_command_request_header Command; ++ void *handle; ++ loff_t offset; ++ size_t len; ++ unsigned char data[1]; ++ ++}; ++ ++struct novfs_write_file_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned long long bytesWritten; ++}; ++ ++struct novfs_read_stream_request { ++ struct novfs_command_request_header Command; ++ void *connection; ++ unsigned char handle[6]; ++ loff_t offset; ++ size_t len; ++}; ++ ++struct novfs_read_stream_reply { ++ struct novfs_command_reply_header Reply; ++ size_t bytesRead; ++ unsigned char data[1]; ++}; ++ ++struct novfs_write_stream_request { ++ struct novfs_command_request_header Command; ++ void *connection; ++ unsigned char handle[6]; ++ loff_t offset; ++ size_t len; ++ unsigned char data[1]; ++}; ++ ++struct novfs_write_stream_reply { ++ struct novfs_command_reply_header Reply; ++ size_t bytesWritten; ++}; ++ ++struct novfs_close_stream_request { ++ struct novfs_command_request_header Command; ++ void *connection; ++ unsigned char handle[6]; ++}; ++ ++struct novfs_close_stream_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct novfs_login_user_request { ++ struct novfs_command_request_header Command; ++ unsigned int srvNameType; ++ unsigned int serverLength; ++ unsigned int serverOffset; ++ unsigned int usrNameType; ++ unsigned int userNameLength; ++ unsigned int userNameOffset; ++ unsigned int pwdNameType; ++ unsigned int passwordLength; ++ unsigned int passwordOffset; ++ ++}; ++ ++struct novfs_login_user_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned int connectionHandle; ++ void *loginIdentity; ++ ++}; ++ ++struct novfs_logout_request { ++ struct novfs_command_request_header Command; ++ unsigned int length; ++ unsigned char Name[1]; ++ ++}; ++ ++struct novfs_logout_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct novfs_create_context_request { ++ struct novfs_command_request_header Command; ++ ++}; ++ ++struct novfs_create_context_reply { ++ struct novfs_command_reply_header Reply; ++ struct novfs_schandle SessionId; ++ ++}; ++ ++struct novfs_destroy_context_request { ++ struct novfs_command_request_header Command; ++ ++}; ++ ++struct novfs_destroy_context_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++/* ++ * Attribute flags. These should be or-ed together to figure out what ++ * has been changed! ++ */ ++#ifndef ATTR_MODE ++#define ATTR_MODE 1 ++#define ATTR_UID 2 ++#define ATTR_GID 4 ++#define ATTR_SIZE 8 ++#define ATTR_ATIME 16 ++#define ATTR_MTIME 32 ++#define ATTR_CTIME 64 ++#define ATTR_ATIME_SET 128 ++#define ATTR_MTIME_SET 256 ++#define ATTR_FORCE 512 /* Not a change, but a change it */ ++#define ATTR_ATTR_FLAG 1024 ++#endif ++ ++struct novfs_lnx_file_info { ++ unsigned int ia_valid; ++ unsigned int ia_mode; ++ uid_t ia_uid; ++ gid_t ia_gid; ++ loff_t ia_size; ++ time_t ia_atime; ++ time_t ia_mtime; ++ time_t ia_ctime; ++ unsigned int ia_attr_flags; ++}; ++ ++struct novfs_set_file_info_request { ++ struct novfs_command_request_header Command; ++ struct novfs_lnx_file_info fileInfo; ++ unsigned int pathlength; ++ char path[1]; ++}; ++ ++struct novfs_set_file_info_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct novfs_truncate_file_request { ++ struct novfs_command_request_header Command; ++ unsigned int pathLen; ++ char path[1]; ++ ++}; ++ ++struct novfs_truncate_file_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct novfs_getpwuid_request { ++ struct novfs_command_request_header Command; ++ unsigned int uid; ++}; ++ ++struct novfs_getpwuid_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned char UserName[1]; ++}; ++ ++struct novfs_get_version_request { ++ struct novfs_command_request_header Command; ++}; ++ ++struct novfs_get_version_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned char Version[1]; ++}; ++ ++struct novfs_set_mount_path { ++ struct novfs_command_request_header Command; ++ unsigned int PathLength; ++ unsigned char Path[1]; ++}; ++ ++struct novfs_set_mount_path_reply { ++ struct novfs_command_reply_header Reply; ++}; ++ ++struct novfs_get_user_space { ++ struct novfs_command_request_header Command; ++}; ++ ++struct novfs_get_user_space_reply { ++ struct novfs_command_reply_header Reply; ++ uint64_t TotalSpace; ++ uint64_t FreeSpace; ++ uint64_t TotalEnties; ++ uint64_t FreeEnties; ++}; ++ ++struct novfs_xplat_call_request { ++ struct novfs_command_request_header Command; ++ unsigned int NwcCommand; ++ unsigned long dataLen; ++ unsigned char data[1]; ++ ++}; ++ ++struct novfs_xplat_call_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned long dataLen; ++ unsigned char data[1]; ++ ++}; ++ ++/* XPlat NWC structures used by the daemon */ ++ ++struct nwd_open_conn_by_name { ++ void *ConnHandle; ++ unsigned int nameLen; ++ unsigned int oName; /* Ofset to the Name */ ++ unsigned int serviceLen; ++ unsigned int oServiceType; /* Offset to service Type; */ ++ unsigned int uConnFlags; ++ unsigned int uTranType; ++ void *newConnHandle; ++ ++}; ++ ++struct nwd_tran_addr { ++ unsigned int uTransportType; ++ unsigned int uAddressLength; ++ unsigned int oAddress; ++ ++}; ++ ++struct nwd_open_conn_by_addr { ++ void *ConnHandle; ++ unsigned int oServiceType; ++ unsigned int uConnFlags; ++ struct nwd_tran_addr TranAddr; ++ ++}; ++ ++struct nwd_close_conn { ++ void *ConnHandle; ++ ++}; ++ ++struct nwd_ncp_req { ++ void *ConnHandle; ++ unsigned int replyLen; ++ unsigned int requestLen; ++ unsigned int function; ++/* unsigned int subFunction; */ ++/* unsigned int verb; */ ++ unsigned int flags; ++ unsigned char data[1]; ++ ++}; ++ ++struct nwd_ncp_rep { ++ unsigned int replyLen; ++ unsigned char data[1]; ++ ++}; ++ ++struct nwc_auth_wid { ++ void *ConnHandle; ++ u32 AuthenticationId; ++ ++}; ++ ++struct nwc_unauthenticate { ++ void *ConnHandle; ++ unsigned int AuthenticationId; ++ ++}; ++ ++struct nwc_lisc_id { ++ void *ConnHandle; ++ ++}; ++ ++struct nwc_unlic_conn { ++ void *ConnHandle; ++ ++}; ++ ++struct nwd_get_id_info { ++ u32 AuthenticationId; ++ unsigned int AuthType; ++ unsigned int NameType; ++ unsigned short int ObjectType; ++ unsigned int IdentityFlags; ++ unsigned int domainLen; ++ unsigned int pDomainNameOffset; ++ unsigned int objectLen; ++ unsigned int pObjectNameOffset; ++ ++}; ++ ++struct nwc_lo_id { ++ u32 AuthenticationId; ++ ++}; ++ ++struct novfs_rename_file_request { ++ struct novfs_command_request_header Command; ++ int directoryFlag; ++ unsigned int newnameLen; ++ unsigned char newname[256]; ++ unsigned int oldnameLen; ++ unsigned char oldname[256]; ++}; ++ ++struct novfs_rename_file_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct nwd_server_version { ++ unsigned int uMajorVersion; ++ unsigned short int uMinorVersion; ++ unsigned short int uRevision; ++}; ++ ++ ++#define MAX_ADDRESS_LENGTH 32 ++ ++struct tagNwdTranAddrEx { ++ unsigned int uTransportType; ++ unsigned int uAddressLength; ++ unsigned char Buffer[MAX_ADDRESS_LENGTH]; ++ ++}; ++ ++struct __NWD_CONN_INFO { ++ unsigned int uInfoVersion; ++ unsigned int uAuthenticationState; ++ unsigned int uBroadcastState; ++ u32 uConnectionReference; ++ unsigned int pTreeNameOffset; ++/* unsigned int pWorkGroupIdOffset; Not used */ ++ unsigned int uSecurityState; ++ unsigned int uConnectionNumber; ++ unsigned int uUserId; ++ unsigned int pServerNameOffset; ++ unsigned int uNdsState; ++ unsigned int uMaxPacketSize; ++ unsigned int uLicenseState; ++ unsigned int uPublicState; ++ unsigned int bcastState; ++ unsigned int pServiceTypeOffset; ++ unsigned int uDistance; ++ u32 uAuthId; ++ unsigned int uDisconnected; ++ struct nwd_server_version ServerVersion; ++ struct nwd_tran_addr TranAddress; ++}; ++ ++struct nwd_conn_info { ++ void *ConnHandle; ++ unsigned int uInfoLevel; ++ unsigned int uInfoLength; ++}; ++ ++struct nwd_open_conn_by_ref { ++ void *uConnReference; ++ unsigned int uConnFlags; ++ void *ConnHandle; ++ ++}; ++ ++struct nwd_get_reqversion { ++ unsigned int uMajorVersion; ++ unsigned int uMinorVersion; ++ unsigned int uRevision; ++ ++}; ++ ++struct nwd_scan_conn_info { ++ unsigned int uScanIndex; ++ unsigned int uScanInfoLevel; ++ unsigned int uScanInfoLen; ++ unsigned int uScanConnInfoOffset; ++ unsigned int uScanFlags; ++ unsigned int uReturnInfoLevel; ++ unsigned int uReturnInfoLength; ++ unsigned int uConnectionReference; ++ unsigned int uReturnConnInfoOffset; ++ ++}; ++ ++struct nwd_get_pref_ds_tree { ++ unsigned int uTreeLength; ++ unsigned int DsTreeNameOffset; ++ ++}; ++ ++struct nwd_set_pref_ds_tree { ++ unsigned int uTreeLength; ++ unsigned int DsTreeNameOffset; ++ ++}; ++ ++struct nwd_set_def_name_ctx { ++ unsigned int uTreeLength; ++ unsigned int TreeOffset; ++ unsigned int uNameLength; ++ unsigned int NameContextOffset; ++ ++}; ++ ++struct nwd_get_def_name_ctx { ++ unsigned int uTreeLength; ++ unsigned int TreeOffset; ++ unsigned int uNameLength; ++ unsigned int NameContextOffset; ++ ++}; ++ ++struct nwd_get_tree_monitored_conn_ref { ++ struct nwd_string TreeName; ++ void *uConnReference; ++ ++}; ++ ++struct nwd_enum_ids { ++ unsigned int Iterator; ++ unsigned int domainNameLen; ++ unsigned int domainNameOffset; ++ unsigned int AuthType; ++ unsigned int objectNameLen; ++ unsigned int objectNameOffset; ++ unsigned int NameType; ++ unsigned short int ObjectType; ++ unsigned int IdentityFlags; ++ u32 AuthenticationId; ++ ++}; ++ ++struct nwd_change_key { ++ unsigned int domainNameOffset; ++ unsigned int domainNameLen; ++ unsigned int AuthType; ++ unsigned int objectNameOffset; ++ unsigned int objectNameLen; ++ unsigned int NameType; ++ unsigned short int ObjectType; ++ unsigned int verifyPasswordOffset; ++ unsigned int verifyPasswordLen; ++ unsigned int newPasswordOffset; ++ unsigned int newPasswordLen; ++ ++}; ++ ++struct nwd_set_primary_conn { ++ void *ConnHandle; ++ ++}; ++ ++struct nwd_get_bcast_notification { ++ unsigned int uMessageFlags; ++ void *uConnReference; ++ unsigned int messageLen; ++ char message[1]; ++ ++}; ++ ++struct nwd_set_conn_info { ++ void *ConnHandle; ++ unsigned int uInfoLevel; ++ unsigned int uInfoLength; ++ unsigned int offsetConnInfo; ++ ++}; ++ ++struct novfs_debug_request { ++ struct novfs_command_request_header Command; ++ int cmdlen; ++ char dbgcmd[1]; ++ ++}; ++ ++struct novfs_debug_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++struct nwd_set_key { ++ void *ConnHandle; ++ unsigned int AuthenticationId; ++ unsigned int objectNameLen; ++ unsigned int objectNameOffset; ++ unsigned short int ObjectType; ++ unsigned int newPasswordLen; ++ unsigned int newPasswordOffset; ++ ++}; ++ ++struct nwd_verify_key { ++ unsigned int AuthType; ++ unsigned int NameType; ++ unsigned short int ObjectType; ++ unsigned int domainNameLen; ++ unsigned int domainNameOffset; ++ unsigned int objectNameLen; ++ unsigned int objectNameOffset; ++ unsigned int verifyPasswordLen; ++ unsigned int verifyPasswordOffset; ++ ++}; ++ ++struct novfs_get_cache_flag { ++ struct novfs_command_request_header Command; ++ int pathLen; ++ unsigned char path[0]; ++ ++}; ++ ++struct novfs_get_cache_flag_reply { ++ struct novfs_command_reply_header Reply; ++ int CacheFlag; ++ ++}; ++ ++struct novfs_xa_list_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned char *pData; ++ ++}; ++ ++struct novfs_xa_get_request { ++ struct novfs_command_request_header Command; ++ unsigned int pathLen; ++ unsigned int nameLen; ++ unsigned char data[1]; //hold path, attribute name ++ ++}; ++ ++struct novfs_xa_get_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned char *pData; ++ ++}; ++ ++struct novfs_xa_set_request { ++ struct novfs_command_request_header Command; ++ unsigned int TtlWriteDataSize; ++ unsigned int WritePosition; ++ int flags; ++ unsigned int pathLen; ++ unsigned int nameLen; ++ unsigned int valueLen; ++ unsigned char data[1]; //hold path, attribute name, value data ++ ++}; ++ ++struct novfs_xa_set_reply { ++ struct novfs_command_reply_header Reply; ++ unsigned char *pData; ++ ++}; ++ ++struct novfs_set_file_lock_request { ++ struct novfs_command_request_header Command; ++ void *handle; ++ unsigned char fl_type; ++ loff_t fl_start; ++ loff_t fl_len; ++ ++}; ++ ++struct novfs_set_file_lock_reply { ++ struct novfs_command_reply_header Reply; ++ ++}; ++ ++ ++struct novfs_scope_list{ ++ struct list_head ScopeList; ++ struct novfs_schandle ScopeId; ++ struct novfs_schandle SessionId; ++ pid_t ScopePid; ++ struct task_struct *ScopeTask; ++ unsigned int ScopeHash; ++ uid_t ScopeUid; ++ uint64_t ScopeUSize; ++ uint64_t ScopeUFree; ++ uint64_t ScopeUTEnties; ++ uint64_t ScopeUAEnties; ++ int ScopeUserNameLength; ++ unsigned char ScopeUserName[32]; ++}; ++ ++#pragma pack(pop) ++ ++#endif /* __NOVFS_COMMANDS_H */ +--- /dev/null ++++ b/fs/novfs/daemon.c +@@ -0,0 +1,2085 @@ ++/* ++ * Novell NCP Redirector for Linux ++ * Author: James Turner ++ * ++ * This file contains all the functions necessary for sending commands to our ++ * daemon module. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vfs.h" ++#include "nwcapi.h" ++#include "commands.h" ++#include "nwerror.h" ++ ++#define QUEUE_SENDING 0 ++#define QUEUE_WAITING 1 ++#define QUEUE_TIMEOUT 2 ++#define QUEUE_ACKED 3 ++#define QUEUE_DONE 4 ++ ++#define TIMEOUT_VALUE 10 ++ ++#define DH_TYPE_UNDEFINED 0 ++#define DH_TYPE_STREAM 1 ++#define DH_TYPE_CONNECTION 2 ++ ++struct daemon_queue { ++ struct list_head list; /* Must be first entry */ ++ spinlock_t lock; /* Used to control access to list */ ++ struct semaphore semaphore; /* Used to signal when data is available */ ++}; ++ ++struct daemon_cmd { ++ struct list_head list; /* Must be first entry */ ++ atomic_t reference; ++ unsigned int status; ++ unsigned int flags; ++ struct semaphore semaphore; ++ unsigned long sequence; ++ struct timer_list timer; ++ void *request; ++ unsigned long reqlen; ++ void *data; ++ int datalen; ++ void *reply; ++ unsigned long replen; ++}; ++ ++struct daemon_handle { ++ struct list_head list; ++ rwlock_t lock; ++ struct novfs_schandle session; ++}; ++ ++struct daemon_resource { ++ struct list_head list; ++ int type; ++ void *connection; ++ unsigned char handle[6]; ++ mode_t mode; ++ loff_t size; ++}; ++ ++struct drive_map { ++ struct list_head list; /* Must be first item */ ++ struct novfs_schandle session; ++ unsigned long hash; ++ int namelen; ++ char name[1]; ++}; ++ ++static void Queue_get(struct daemon_cmd * Que); ++static void Queue_put(struct daemon_cmd * Que); ++static void RemoveDriveMaps(void); ++static int NwdConvertLocalHandle(struct novfs_xplat *pdata, struct daemon_handle * DHandle); ++static int NwdConvertNetwareHandle(struct novfs_xplat *pdata, struct daemon_handle * DHandle); ++static int set_map_drive(struct novfs_xplat *pdata, struct novfs_schandle Session); ++static int unmap_drive(struct novfs_xplat *pdata, struct novfs_schandle Session); ++static int NwdGetMountPath(struct novfs_xplat *pdata); ++static long local_unlink(const char *pathname); ++ ++ ++/*===[ Global variables ]=================================================*/ ++static struct daemon_queue Daemon_Queue; ++ ++static DECLARE_WAIT_QUEUE_HEAD(Read_waitqueue); ++ ++static atomic_t Sequence = ATOMIC_INIT(-1); ++static atomic_t Daemon_Open_Count = ATOMIC_INIT(0); ++ ++static unsigned long Daemon_Command_Timeout = TIMEOUT_VALUE; ++ ++static DECLARE_MUTEX(DriveMapLock); ++static LIST_HEAD(DriveMapList); ++ ++int novfs_max_iosize = PAGE_SIZE; ++ ++void novfs_daemon_queue_init() ++{ ++ INIT_LIST_HEAD(&Daemon_Queue.list); ++ spin_lock_init(&Daemon_Queue.lock); ++ init_MUTEX_LOCKED(&Daemon_Queue.semaphore); ++} ++ ++void novfs_daemon_queue_exit(void) ++{ ++ /* Does nothing for now but we maybe should clear the queue. */ ++} ++ ++/*++======================================================================*/ ++static void novfs_daemon_timer(unsigned long data) ++{ ++ struct daemon_cmd *que = (struct daemon_cmd *) data; ++ ++ if (QUEUE_ACKED != que->status) { ++ que->status = QUEUE_TIMEOUT; ++ } ++ up(&que->semaphore); ++} ++ ++/*++======================================================================*/ ++int Queue_Daemon_Command(void *request, ++ unsigned long reqlen, ++ void *data, ++ int dlen, ++ void **reply, unsigned long * replen, int interruptible) ++{ ++ struct daemon_cmd *que; ++ int retCode = 0; ++ uint64_t ts1, ts2; ++ ++ ts1 = get_nanosecond_time(); ++ ++ DbgPrint("0x%p %d", request, reqlen); ++ ++ if (atomic_read(&Daemon_Open_Count)) { ++ ++ que = kmalloc(sizeof(*que), GFP_KERNEL); ++ ++ DbgPrint("que=0x%p", que); ++ if (que) { ++ atomic_set(&que->reference, 0); ++ que->status = QUEUE_SENDING; ++ que->flags = 0; ++ ++ init_MUTEX_LOCKED(&que->semaphore); ++ ++ que->sequence = atomic_inc_return(&Sequence); ++ ++ ((struct novfs_command_request_header *) request)->SequenceNumber = ++ que->sequence; ++ ++ /* ++ * Setup and start que timer ++ */ ++ init_timer(&que->timer); ++ que->timer.expires = jiffies + (HZ * Daemon_Command_Timeout); ++ que->timer.data = (unsigned long) que; ++ que->timer.function = novfs_daemon_timer; ++ add_timer(&que->timer); ++ ++ /* ++ * Setup request ++ */ ++ que->request = request; ++ que->reqlen = reqlen; ++ que->data = data; ++ que->datalen = dlen; ++ que->reply = NULL; ++ que->replen = 0; ++ ++ /* ++ * Added entry to queue. ++ */ ++ /* ++ * Check to see if interruptible and set flags. ++ */ ++ if (interruptible) { ++ que->flags |= INTERRUPTIBLE; ++ } ++ ++ Queue_get(que); ++ ++ spin_lock(&Daemon_Queue.lock); ++ list_add_tail(&que->list, &Daemon_Queue.list); ++ spin_unlock(&Daemon_Queue.lock); ++ ++ /* ++ * Signal that there is data to be read ++ */ ++ up(&Daemon_Queue.semaphore); ++ ++ /* ++ * Give a change to the other processes. ++ */ ++ yield(); ++ ++ /* ++ * Block waiting for reply or timeout ++ */ ++ down(&que->semaphore); ++ ++ if (QUEUE_ACKED == que->status) { ++ que->status = QUEUE_WAITING; ++ mod_timer(&que->timer, ++ jiffies + ++ (HZ * 2 * Daemon_Command_Timeout)); ++ if (interruptible) { ++ retCode = ++ down_interruptible(&que->semaphore); ++ } else { ++ down(&que->semaphore); ++ } ++ } ++ ++ /* ++ * Delete timer ++ */ ++ del_timer(&que->timer); ++ ++ /* ++ * Check for timeout ++ */ ++ if ((QUEUE_TIMEOUT == que->status) ++ && (NULL == que->reply)) { ++ DbgPrint("Timeout"); ++ retCode = -ETIME; ++ } ++ *reply = que->reply; ++ *replen = que->replen; ++ ++ /* ++ * Remove item from queue ++ */ ++ Queue_put(que); ++ ++ } else { /* Error case with no memory */ ++ ++ retCode = -ENOMEM; ++ *reply = NULL; ++ *replen = 0; ++ } ++ } else { ++ retCode = -EIO; ++ *reply = NULL; ++ *replen = 0; ++ ++ } ++ ts2 = get_nanosecond_time(); ++ ts2 = ts2 - ts1; ++ ++ DbgPrint("%llu retCode=%d", ts2, retCode); ++ return (retCode); ++} ++ ++static void Queue_get(struct daemon_cmd * Que) ++{ ++ DbgPrint("que=0x%p %d", Que, atomic_read(&Que->reference)); ++ atomic_inc(&Que->reference); ++} ++ ++static void Queue_put(struct daemon_cmd * Que) ++{ ++ ++ DbgPrint("que=0x%p %d", Que, atomic_read(&Que->reference)); ++ spin_lock(&Daemon_Queue.lock); ++ ++ if (atomic_dec_and_test(&Que->reference)) { ++ /* ++ * Remove item from queue ++ */ ++ list_del(&Que->list); ++ spin_unlock(&Daemon_Queue.lock); ++ ++ /* ++ * Free item memory ++ */ ++ kfree(Que); ++ } else { ++ spin_unlock(&Daemon_Queue.lock); ++ } ++} ++ ++struct daemon_cmd *get_next_queue(int Set_Queue_Waiting) ++{ ++ struct daemon_cmd *que; ++ ++ DbgPrint("que=0x%p", Daemon_Queue.list.next); ++ ++ spin_lock(&Daemon_Queue.lock); ++ que = (struct daemon_cmd *) Daemon_Queue.list.next; ++ ++ while (que && (que != (struct daemon_cmd *) & Daemon_Queue.list.next) ++ && (que->status != QUEUE_SENDING)) { ++ que = (struct daemon_cmd *) que->list.next; ++ } ++ ++ if ((NULL == que) || (que == (struct daemon_cmd *) & Daemon_Queue.list) ++ || (que->status != QUEUE_SENDING)) { ++ que = NULL; ++ } else if (Set_Queue_Waiting) { ++ que->status = QUEUE_WAITING; ++ } ++ ++ if (que) { ++ atomic_inc(&que->reference); ++ } ++ ++ spin_unlock(&Daemon_Queue.lock); ++ ++ DbgPrint("return=0x%p", que); ++ return (que); ++} ++ ++static struct daemon_cmd *find_queue(unsigned long sequence) ++{ ++ struct daemon_cmd *que; ++ ++ DbgPrint("0x%x", sequence); ++ ++ spin_lock(&Daemon_Queue.lock); ++ que = (struct daemon_cmd *) Daemon_Queue.list.next; ++ ++ while (que && (que != (struct daemon_cmd *) & Daemon_Queue.list.next) ++ && (que->sequence != sequence)) { ++ que = (struct daemon_cmd *) que->list.next; ++ } ++ ++ if ((NULL == que) ++ || (que == (struct daemon_cmd *) & Daemon_Queue.list.next) ++ || (que->sequence != sequence)) { ++ que = NULL; ++ } ++ ++ if (que) { ++ atomic_inc(&que->reference); ++ } ++ ++ spin_unlock(&Daemon_Queue.lock); ++ ++ DbgPrint("return 0x%p", que); ++ return (que); ++} ++ ++int novfs_daemon_open_control(struct inode *Inode, struct file *File) ++{ ++ DbgPrint("pid=%d Count=%d", current->pid, ++ atomic_read(&Daemon_Open_Count)); ++ atomic_inc(&Daemon_Open_Count); ++ ++ return (0); ++} ++ ++int novfs_daemon_close_control(struct inode *Inode, struct file *File) ++{ ++ struct daemon_cmd *que; ++ ++ DbgPrint("pid=%d Count=%d", current->pid, ++ atomic_read(&Daemon_Open_Count)); ++ ++ if (atomic_dec_and_test(&Daemon_Open_Count)) { ++ /* ++ * Signal any pending que itmes. ++ */ ++ ++ spin_lock(&Daemon_Queue.lock); ++ que = (struct daemon_cmd *) Daemon_Queue.list.next; ++ ++ while (que ++ && (que != (struct daemon_cmd *) & Daemon_Queue.list.next) ++ && (que->status != QUEUE_DONE)) { ++ que->status = QUEUE_TIMEOUT; ++ up(&que->semaphore); ++ ++ que = (struct daemon_cmd *) que->list.next; ++ } ++ spin_unlock(&Daemon_Queue.lock); ++ ++ RemoveDriveMaps(); ++ ++ novfs_scope_cleanup(); ++ } ++ ++ return (0); ++} ++ ++ssize_t novfs_daemon_cmd_send(struct file * file, char *buf, size_t len, loff_t * off) ++{ ++ struct daemon_cmd *que; ++ size_t retValue = 0; ++ int Finished = 0; ++ struct novfs_data_list *dlist; ++ int i, dcnt, bcnt, ccnt, error; ++ char *vadr; ++ unsigned long cpylen; ++ ++ DbgPrint("%u %lld", len, *off); ++ if (len > novfs_max_iosize) { ++ novfs_max_iosize = len; ++ } ++ ++ while (!Finished) { ++ que = get_next_queue(1); ++ DbgPrint("0x%p", que); ++ if (que) { ++ retValue = que->reqlen; ++ if (retValue > len) { ++ retValue = len; ++ } ++ if (retValue > 0x80) ++ novfs_dump(0x80, que->request); ++ else ++ novfs_dump(retValue, que->request); ++ ++ cpylen = copy_to_user(buf, que->request, retValue); ++ if (que->datalen && (retValue < len)) { ++ buf += retValue; ++ dlist = que->data; ++ dcnt = que->datalen; ++ for (i = 0; i < dcnt; i++, dlist++) { ++ if (DLREAD == dlist->rwflag) { ++ bcnt = dlist->len; ++ DbgPrint("page=0x%p " ++ "offset=0x%p len=%d", ++ i, dlist->page, ++ dlist->offset, dlist->len); ++ if ((bcnt + retValue) <= len) { ++ void *km_adr = NULL; ++ ++ if (dlist->page) { ++ km_adr = ++ kmap(dlist-> ++ page); ++ vadr = km_adr; ++ vadr += ++ (unsigned long) ++ dlist-> ++ offset; ++ } else { ++ vadr = ++ dlist-> ++ offset; ++ } ++ ++ ccnt = ++ copy_to_user(buf, ++ vadr, ++ bcnt); ++ ++ DbgPrint("Copy %d from 0x%p to 0x%p.", ++ bcnt, vadr, buf); ++ if (bcnt > 0x80) ++ novfs_dump(0x80, ++ vadr); ++ else ++ novfs_dump(bcnt, ++ vadr); ++ ++ if (km_adr) { ++ kunmap(dlist-> ++ page); ++ } ++ ++ retValue += bcnt; ++ buf += bcnt; ++ } else { ++ break; ++ } ++ } ++ } ++ } ++ Queue_put(que); ++ break; ++ } ++ ++ if (O_NONBLOCK & file->f_flags) { ++ retValue = -EAGAIN; ++ break; ++ } else { ++ if ((error = ++ down_interruptible(&Daemon_Queue.semaphore))) { ++ DbgPrint("after down_interruptible error...%d", ++ error); ++ retValue = -EINTR; ++ break; ++ } ++ DbgPrint("after down_interruptible"); ++ } ++ } ++ ++ *off = *off; ++ ++ DbgPrint("return 0x%x", retValue); ++ ++ return (retValue); ++} ++ ++ssize_t novfs_daemon_recv_reply(struct file *file, const char *buf, size_t nbytes, loff_t * ppos) ++{ ++ struct daemon_cmd *que; ++ size_t retValue = 0; ++ void *reply; ++ unsigned long sequence, cpylen; ++ ++ struct novfs_data_list *dlist; ++ char *vadr; ++ int i; ++ ++ DbgPrint("buf=0x%p nbytes=%d ppos=%llx", buf, ++ nbytes, *ppos); ++ ++ /* ++ * Get sequence number from reply buffer ++ */ ++ ++ cpylen = copy_from_user(&sequence, buf, sizeof(sequence)); ++ ++ /* ++ * Find item based on sequence number ++ */ ++ que = find_queue(sequence); ++ ++ DbgPrint("0x%x 0x%p %d", sequence, que, nbytes); ++ if (que) { ++ do { ++ retValue = nbytes; ++ /* ++ * Ack packet from novfsd. Remove timer and ++ * return ++ */ ++ if (nbytes == sizeof(sequence)) { ++ que->status = QUEUE_ACKED; ++ break; ++ } ++ ++ if (NULL != (dlist = que->data)) { ++ int thiscopy, left = nbytes; ++ retValue = 0; ++ ++ DbgPrint("dlist=0x%p count=%d", ++ dlist, que->datalen); ++ for (i = 0; ++ (i < que->datalen) && (retValue < nbytes); ++ i++, dlist++) { ++ __DbgPrint("\n" ++ " dlist[%d].page: 0x%p\n" ++ " dlist[%d].offset: 0x%p\n" ++ " dlist[%d].len: 0x%x\n" ++ " dlist[%d].rwflag: 0x%x\n", ++ i, dlist->page, i, ++ dlist->offset, i, dlist->len, ++ i, dlist->rwflag); ++ ++ if (DLWRITE == dlist->rwflag) { ++ void *km_adr = NULL; ++ ++ if (dlist->page) { ++ km_adr = ++ kmap(dlist->page); ++ vadr = km_adr; ++ vadr += ++ (unsigned long) dlist-> ++ offset; ++ } else { ++ vadr = dlist->offset; ++ } ++ ++ thiscopy = dlist->len; ++ if (thiscopy > left) { ++ thiscopy = left; ++ dlist->len = left; ++ } ++ cpylen = ++ copy_from_user(vadr, buf, ++ thiscopy); ++ ++ if (thiscopy > 0x80) ++ novfs_dump(0x80, vadr); ++ else ++ novfs_dump(thiscopy, vadr); ++ ++ if (km_adr) { ++ kunmap(dlist->page); ++ } ++ ++ left -= thiscopy; ++ retValue += thiscopy; ++ buf += thiscopy; ++ } ++ } ++ que->replen = retValue; ++ } else { ++ reply = kmalloc(nbytes, GFP_KERNEL); ++ DbgPrint("reply=0x%p", reply); ++ if (reply) { ++ retValue = nbytes; ++ que->reply = reply; ++ que->replen = nbytes; ++ ++ retValue -= ++ copy_from_user(reply, buf, ++ retValue); ++ if (retValue > 0x80) ++ novfs_dump(0x80, reply); ++ else ++ novfs_dump(retValue, reply); ++ ++ } else { ++ retValue = -ENOMEM; ++ } ++ } ++ ++ /* ++ * Set status that packet is done. ++ */ ++ que->status = QUEUE_DONE; ++ ++ } while (0); ++ up(&que->semaphore); ++ Queue_put(que); ++ } ++ ++ DbgPrint("return 0x%x", retValue); ++ ++ return (retValue); ++} ++ ++int novfs_do_login(struct ncl_string *Server, struct ncl_string *Username, ++struct ncl_string *Password, void **lgnId, struct novfs_schandle *Session) ++{ ++ struct novfs_login_user_request *cmd; ++ struct novfs_login_user_reply *reply; ++ unsigned long replylen = 0; ++ int retCode, cmdlen, datalen; ++ unsigned char *data; ++ ++ datalen = Server->len + Username->len + Password->len; ++ cmdlen = sizeof(*cmd) + datalen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ data = (unsigned char *) cmd + sizeof(*cmd); ++ cmd->Command.CommandType = VFS_COMMAND_LOGIN_USER; ++ cmd->Command.SequenceNumber = 0; ++ memcpy(&cmd->Command.SessionId, Session, sizeof(*Session)); ++ ++ cmd->srvNameType = Server->type; ++ cmd->serverLength = Server->len; ++ cmd->serverOffset = (unsigned long) (data - (unsigned char *) cmd); ++ memcpy(data, Server->buffer, Server->len); ++ data += Server->len; ++ ++ cmd->usrNameType = Username->type; ++ cmd->userNameLength = Username->len; ++ cmd->userNameOffset = (unsigned long) (data - (unsigned char *) cmd); ++ memcpy(data, Username->buffer, Username->len); ++ data += Username->len; ++ ++ cmd->pwdNameType = Password->type; ++ cmd->passwordLength = Password->len; ++ cmd->passwordOffset = (unsigned long) (data - (unsigned char *) cmd); ++ memcpy(data, Password->buffer, Password->len); ++ data += Password->len; ++ ++ retCode = Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (reply->Reply.ErrorCode) { ++ retCode = reply->Reply.ErrorCode; ++ } else { ++ retCode = 0; ++ if (lgnId) { ++ *lgnId = reply->loginIdentity; ++ } ++ } ++ kfree(reply); ++ } ++ memset(cmd, 0, cmdlen); ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_daemon_logout(struct qstr *Server, struct novfs_schandle *Session) ++{ ++ struct novfs_logout_request *cmd; ++ struct novfs_logout_reply *reply; ++ unsigned long replylen = 0; ++ int retCode, cmdlen; ++ ++ cmdlen = offsetof(struct novfs_logout_request, Name) + Server->len; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_LOGOUT_USER; ++ cmd->Command.SequenceNumber = 0; ++ memcpy(&cmd->Command.SessionId, Session, sizeof(*Session)); ++ cmd->length = Server->len; ++ memcpy(cmd->Name, Server->name, Server->len); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_daemon_getpwuid(uid_t uid, int unamelen, char *uname) ++{ ++ struct novfs_getpwuid_request cmd; ++ struct novfs_getpwuid_reply *reply; ++ unsigned long replylen = 0; ++ int retCode; ++ ++ cmd.Command.CommandType = VFS_COMMAND_GETPWUD; ++ cmd.Command.SequenceNumber = 0; ++ SC_INITIALIZE(cmd.Command.SessionId); ++ cmd.uid = uid; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ } else { ++ retCode = 0; ++ memset(uname, 0, unamelen); ++ replylen = ++ replylen - offsetof(struct ++ novfs_getpwuid_reply, UserName); ++ if (replylen) { ++ if (replylen > unamelen) { ++ retCode = -EINVAL; ++ replylen = unamelen - 1; ++ } ++ memcpy(uname, reply->UserName, replylen); ++ } ++ } ++ kfree(reply); ++ } ++ return (retCode); ++ ++} ++ ++int novfs_daemon_getversion(char *Buf, int length) ++{ ++ struct novfs_get_version_request cmd; ++ struct novfs_get_version_reply *reply; ++ unsigned long replylen = 0; ++ int retVal = 0; ++ ++ cmd.Command.CommandType = VFS_COMMAND_GET_VERSION; ++ cmd.Command.SequenceNumber = 0; ++ SC_INITIALIZE(cmd.Command.SessionId); ++ ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (reply->Reply.ErrorCode) { ++ retVal = -EIO; ++ } else { ++ retVal = ++ replylen - offsetof(struct ++ novfs_get_version_reply, Version); ++ if (retVal < length) { ++ memcpy(Buf, reply->Version, retVal); ++ Buf[retVal] = '\0'; ++ } ++ } ++ kfree(reply); ++ } ++ return (retVal); ++ ++} ++ ++static int daemon_login(struct novfs_login *Login, struct novfs_schandle *Session) ++{ ++ int retCode = -ENOMEM; ++ struct novfs_login lLogin; ++ struct ncl_string server; ++ struct ncl_string username; ++ struct ncl_string password; ++ ++ if (!copy_from_user(&lLogin, Login, sizeof(lLogin))) { ++ server.buffer = kmalloc(lLogin.Server.length, GFP_KERNEL); ++ if (server.buffer) { ++ server.len = lLogin.Server.length; ++ server.type = NWC_STRING_TYPE_ASCII; ++ if (!copy_from_user((void *)server.buffer, lLogin.Server.data, server.len)) { ++ username.buffer = kmalloc(lLogin.UserName.length, GFP_KERNEL); ++ if (username.buffer) { ++ username.len = lLogin.UserName.length; ++ username.type = NWC_STRING_TYPE_ASCII; ++ if (!copy_from_user((void *)username.buffer, lLogin.UserName.data, username.len)) { ++ password.buffer = kmalloc(lLogin.Password.length, GFP_KERNEL); ++ if (password.buffer) ++ { ++ password.len = lLogin.Password.length; ++ password.type = NWC_STRING_TYPE_ASCII; ++ if (!copy_from_user((void *)password.buffer, lLogin.Password.data, password.len)) { ++ retCode = novfs_do_login (&server, &username, &password, NULL, Session); ++ if (!retCode) { ++ char *username; ++ username = novfs_scope_get_username(); ++ if (username) { ++ novfs_add_to_root(username); ++ } ++ } ++ } ++ kfree(password.buffer); ++ } ++ } ++ kfree(username.buffer); ++ } ++ } ++ kfree(server.buffer); ++ } ++ } ++ ++ return (retCode); ++} ++ ++static int daemon_logout(struct novfs_logout *Logout, struct novfs_schandle *Session) ++{ ++ struct novfs_logout lLogout; ++ struct qstr server; ++ int retCode = 0; ++ ++ if (copy_from_user(&lLogout, Logout, sizeof(lLogout))) ++ return -EFAULT; ++ server.name = kmalloc(lLogout.Server.length, GFP_KERNEL); ++ if (!server.name) ++ return -ENOMEM; ++ server.len = lLogout.Server.length; ++ if (copy_from_user((void *)server.name, lLogout.Server.data, server.len)) ++ goto exit; ++ retCode = novfs_daemon_logout(&server, Session); ++exit: ++ kfree(server.name); ++ return (retCode); ++} ++ ++int novfs_daemon_create_sessionId(struct novfs_schandle * SessionId) ++{ ++ struct novfs_create_context_request cmd; ++ struct novfs_create_context_reply *reply; ++ unsigned long replylen = 0; ++ int retCode = 0; ++ ++ DbgPrint("%d", current->pid); ++ ++ cmd.Command.CommandType = VFS_COMMAND_CREATE_CONTEXT; ++ cmd.Command.SequenceNumber = 0; ++ SC_INITIALIZE(cmd.Command.SessionId); ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (!reply->Reply.ErrorCode ++ && replylen > sizeof(struct novfs_command_reply_header)) { ++ *SessionId = reply->SessionId; ++ retCode = 0; ++ } else { ++ SessionId->hTypeId = 0; ++ SessionId->hId = 0; ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ DbgPrint("SessionId=0x%llx", *SessionId); ++ return (retCode); ++} ++ ++int novfs_daemon_destroy_sessionId(struct novfs_schandle SessionId) ++{ ++ struct novfs_destroy_context_request cmd; ++ struct novfs_destroy_context_reply *reply; ++ unsigned long replylen = 0; ++ int retCode = 0; ++ ++ DbgPrint("0x%p:%p", SessionId.hTypeId, ++ SessionId.hId); ++ ++ cmd.Command.CommandType = VFS_COMMAND_DESTROY_CONTEXT; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (!reply->Reply.ErrorCode) { ++ struct drive_map *dm; ++ struct list_head *list; ++ ++ retCode = 0; ++ ++ /* ++ * When destroying the session check to see if there are any ++ * mapped drives. If there are then remove them. ++ */ ++ down(&DriveMapLock); ++ list_for_each(list, &DriveMapList) { ++ dm = list_entry(list, struct drive_map, list); ++ if (SC_EQUAL(SessionId, dm->session)) { ++ local_unlink(dm->name); ++ list = list->prev; ++ list_del(&dm->list); ++ kfree(dm); ++ } ++ ++ } ++ up(&DriveMapLock); ++ ++ } else { ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ return (retCode); ++} ++ ++int novfs_daemon_get_userspace(struct novfs_schandle SessionId, uint64_t * TotalSize, ++ uint64_t * Free, uint64_t * TotalEnties, ++ uint64_t * FreeEnties) ++{ ++ struct novfs_get_user_space cmd; ++ struct novfs_get_user_space_reply *reply; ++ unsigned long replylen = 0; ++ int retCode = 0; ++ ++ DbgPrint("0x%p:%p", SessionId.hTypeId, ++ SessionId.hId); ++ ++ cmd.Command.CommandType = VFS_COMMAND_GET_USER_SPACE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (!reply->Reply.ErrorCode) { ++ ++ __DbgPrint("TotalSpace: %llu\n", reply->TotalSpace); ++ __DbgPrint("FreeSpace: %llu\n", reply->FreeSpace); ++ __DbgPrint("TotalEnties: %llu\n", reply->TotalEnties); ++ __DbgPrint("FreeEnties: %llu\n", reply->FreeEnties); ++ ++ if (TotalSize) ++ *TotalSize = reply->TotalSpace; ++ if (Free) ++ *Free = reply->FreeSpace; ++ if (TotalEnties) ++ *TotalEnties = reply->TotalEnties; ++ if (FreeEnties) ++ *FreeEnties = reply->FreeEnties; ++ retCode = 0; ++ } else { ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ return (retCode); ++} ++ ++int novfs_daemon_set_mnt_point(char *Path) ++{ ++ struct novfs_set_mount_path *cmd; ++ struct novfs_set_mount_path_reply *reply; ++ unsigned long replylen, cmdlen; ++ int retCode = -ENOMEM; ++ ++ DbgPrint("%s", Path); ++ ++ replylen = strlen(Path); ++ ++ cmdlen = sizeof(struct novfs_set_mount_path) + replylen; ++ ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ cmd->Command.CommandType = VFS_COMMAND_SET_MOUNT_PATH; ++ cmd->Command.SequenceNumber = 0; ++ SC_INITIALIZE(cmd->Command.SessionId); ++ cmd->PathLength = replylen; ++ ++ strcpy(cmd->Path, Path); ++ ++ replylen = 0; ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (!reply->Reply.ErrorCode) { ++ retCode = 0; ++ } else { ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ return retCode; ++} ++ ++int novfs_daemon_debug_cmd_send(char *Command) ++{ ++ struct novfs_debug_request cmd; ++ struct novfs_debug_reply *reply; ++ struct novfs_debug_reply lreply; ++ unsigned long replylen, cmdlen; ++ struct novfs_data_list dlist[2]; ++ ++ int retCode = -ENOMEM; ++ ++ DbgPrint("%s", Command); ++ ++ dlist[0].page = NULL; ++ dlist[0].offset = (char *)Command; ++ dlist[0].len = strlen(Command); ++ dlist[0].rwflag = DLREAD; ++ ++ dlist[1].page = NULL; ++ dlist[1].offset = (char *)&lreply; ++ dlist[1].len = sizeof(lreply); ++ dlist[1].rwflag = DLWRITE; ++ ++ cmdlen = offsetof(struct novfs_debug_request, dbgcmd); ++ ++ cmd.Command.CommandType = VFS_COMMAND_DBG; ++ cmd.Command.SequenceNumber = 0; ++ SC_INITIALIZE(cmd.Command.SessionId); ++ cmd.cmdlen = strlen(Command); ++ ++ replylen = 0; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, cmdlen, dlist, 2, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ kfree(reply); ++ } ++ if (0 == retCode) { ++ retCode = lreply.Reply.ErrorCode; ++ } ++ ++ return (retCode); ++} ++ ++int novfs_daemon_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int retCode = -ENOSYS; ++ unsigned long cpylen; ++ struct novfs_schandle session_id; ++ session_id = novfs_scope_get_sessionId(NULL); ++ ++ switch (cmd) { ++ case IOC_LOGIN: ++ retCode = daemon_login((struct novfs_login *) arg, &session_id); ++ break; ++ ++ case IOC_LOGOUT: ++ retCode = daemon_logout((struct novfs_logout *)arg, &session_id); ++ break; ++ case IOC_DEBUGPRINT: ++ { ++ struct Ioctl_Debug { ++ int length; ++ char *data; ++ } io; ++ char *buf; ++ io.length = 0; ++ cpylen = copy_from_user(&io, (char *)arg, sizeof(io)); ++ if (io.length) { ++ buf = kmalloc(io.length + 1, GFP_KERNEL); ++ if (buf) { ++ buf[0] = 0; ++ cpylen = ++ copy_from_user(buf, io.data, ++ io.length); ++ buf[io.length] = '\0'; ++ DbgPrint("%s", buf); ++ kfree(buf); ++ retCode = 0; ++ } ++ } ++ break; ++ } ++ ++ case IOC_XPLAT: ++ { ++ struct novfs_xplat data; ++ ++ cpylen = ++ copy_from_user(&data, (void *)arg, sizeof(data)); ++ retCode = ((data.xfunction & 0x0000FFFF) | 0xCC000000); ++ ++ switch (data.xfunction) { ++ case NWC_GET_MOUNT_PATH: ++ DbgPrint("Call NwdGetMountPath"); ++ retCode = NwdGetMountPath(&data); ++ break; ++ } ++ ++ DbgPrint("[NOVFS XPLAT] status Code = %X\n", retCode); ++ break; ++ } ++ ++ } ++ return (retCode); ++} ++ ++static int daemon_added_resource(struct daemon_handle * DHandle, int Type, void *CHandle, ++ unsigned char * FHandle, unsigned long Mode, u_long Size) ++{ ++ struct daemon_resource *resource; ++ ++ if (FHandle) ++ DbgPrint("DHandle=0x%p Type=%d CHandle=0x%p FHandle=0x%x " ++ "Mode=0x%x Size=%d", DHandle, Type, CHandle, ++ *(u32 *) & FHandle[2], Mode, Size); ++ else ++ DbgPrint("DHandle=0x%p Type=%d CHandle=0x%p\n", ++ DHandle, Type, CHandle); ++ ++ resource = kmalloc(sizeof(struct daemon_resource), GFP_KERNEL); ++ if (!resource) ++ return -ENOMEM; ++ ++ resource->type = Type; ++ resource->connection = CHandle; ++ if (FHandle) ++ memcpy(resource->handle, FHandle, ++ sizeof(resource->handle)); ++ else ++ memset(resource->handle, 0, sizeof(resource->handle)); ++ resource->mode = Mode; ++ resource->size = Size; ++ write_lock(&DHandle->lock); ++ list_add(&resource->list, &DHandle->list); ++ write_unlock(&DHandle->lock); ++ DbgPrint("Adding resource=0x%p", resource); ++ return 0; ++} ++ ++static int daemon_remove_resource(struct daemon_handle * DHandle, int Type, void *CHandle, ++ unsigned long FHandle) ++{ ++ struct daemon_resource *resource; ++ struct list_head *l; ++ int retVal = -ENOMEM; ++ ++ DbgPrint("DHandle=0x%p Type=%d CHandle=0x%p FHandle=0x%x", ++ DHandle, Type, CHandle, FHandle); ++ ++ write_lock(&DHandle->lock); ++ ++ list_for_each(l, &DHandle->list) { ++ resource = list_entry(l, struct daemon_resource, list); ++ ++ if ((Type == resource->type) && ++ (resource->connection == CHandle)) { ++ DbgPrint("Found resource=0x%p", resource); ++ l = l->prev; ++ list_del(&resource->list); ++ kfree(resource); ++ break; ++ } ++ } ++ ++ write_unlock(&DHandle->lock); ++ ++ return (retVal); ++} ++ ++int novfs_daemon_lib_open(struct inode *inode, struct file *file) ++{ ++ struct daemon_handle *dh; ++ ++ DbgPrint("inode=0x%p file=0x%p", inode, file); ++ dh = kmalloc(sizeof(struct daemon_handle), GFP_KERNEL); ++ if (!dh) ++ return -ENOMEM; ++ file->private_data = dh; ++ INIT_LIST_HEAD(&dh->list); ++ rwlock_init(&dh->lock); ++ dh->session = novfs_scope_get_sessionId(NULL); ++ return 0; ++} ++ ++int novfs_daemon_lib_close(struct inode *inode, struct file *file) ++{ ++ struct daemon_handle *dh; ++ struct daemon_resource *resource; ++ struct list_head *l; ++ ++ char commanddata[sizeof(struct novfs_xplat_call_request) + sizeof(struct nwd_close_conn)]; ++ struct novfs_xplat_call_request *cmd; ++ struct xplat_call_reply *reply; ++ struct nwd_close_conn *nwdClose; ++ unsigned long cmdlen, replylen; ++ ++ DbgPrint("inode=0x%p file=0x%p", inode, file); ++ if (file->private_data) { ++ dh = (struct daemon_handle *) file->private_data; ++ ++ list_for_each(l, &dh->list) { ++ resource = list_entry(l, struct daemon_resource, list); ++ ++ if (DH_TYPE_STREAM == resource->type) { ++ novfs_close_stream(resource->connection, ++ resource->handle, ++ dh->session); ++ } else if (DH_TYPE_CONNECTION == resource->type) { ++ cmd = (struct novfs_xplat_call_request *) commanddata; ++ cmdlen = ++ offsetof(struct novfs_xplat_call_request, ++ data) + sizeof(struct nwd_close_conn); ++ cmd->Command.CommandType = ++ VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = dh->session; ++ cmd->NwcCommand = NWC_CLOSE_CONN; ++ ++ cmd->dataLen = sizeof(struct nwd_close_conn); ++ nwdClose = (struct nwd_close_conn *) cmd->data; ++ nwdClose->ConnHandle = ++ (void *) resource->connection; ++ ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, ++ 0, (void **)&reply, ++ &replylen, 0); ++ if (reply) ++ kfree(reply); ++ } ++ l = l->prev; ++ list_del(&resource->list); ++ kfree(resource); ++ } ++ kfree(dh); ++ file->private_data = NULL; ++ } ++ ++ return (0); ++} ++ ++ssize_t novfs_daemon_lib_read(struct file * file, char *buf, size_t len, ++ loff_t * off) ++{ ++ struct daemon_handle *dh; ++ struct daemon_resource *resource; ++ ++ size_t thisread, totalread = 0; ++ loff_t offset = *off; ++ ++ DbgPrint("file=0x%p len=%d off=%lld", file, len, *off); ++ ++ if (file->private_data) { ++ dh = file->private_data; ++ read_lock(&dh->lock); ++ if (&dh->list != dh->list.next) { ++ resource = ++ list_entry(dh->list.next, struct daemon_resource, list); ++ ++ if (DH_TYPE_STREAM == resource->type) { ++ while (len > 0 && (offset < resource->size)) { ++ thisread = len; ++ if (novfs_read_stream ++ (resource->connection, ++ resource->handle, buf, &thisread, ++ &offset, 1, dh->session) ++ || !thisread) { ++ break; ++ } ++ len -= thisread; ++ buf += thisread; ++ offset += thisread; ++ totalread += thisread; ++ } ++ } ++ } ++ read_unlock(&dh->lock); ++ } ++ *off = offset; ++ DbgPrint("return = 0x%x", totalread); ++ return (totalread); ++} ++ ++ssize_t novfs_daemon_lib_write(struct file * file, const char *buf, size_t len, ++ loff_t * off) ++{ ++ struct daemon_handle *dh; ++ struct daemon_resource *resource; ++ ++ size_t thiswrite, totalwrite = -EINVAL; ++ loff_t offset = *off; ++ int status; ++ ++ DbgPrint("file=0x%p len=%d off=%lld", file, len, *off); ++ ++ if (file->private_data) { ++ dh = file->private_data; ++ write_lock(&dh->lock); ++ if (&dh->list != dh->list.next) { ++ resource = ++ list_entry(dh->list.next, struct daemon_resource, list); ++ ++ if ((DH_TYPE_STREAM == resource->type) && (len >= 0)) { ++ totalwrite = 0; ++ do { ++ thiswrite = len; ++ status = ++ novfs_write_stream(resource-> ++ connection, ++ resource->handle, ++ (void *)buf, ++ &thiswrite, ++ &offset, ++ dh->session); ++ if (status || !thiswrite) { ++ /* ++ * If len is zero then the file will have just been ++ * truncated to offset. Update size. ++ */ ++ if (!status && !len) { ++ resource->size = offset; ++ } ++ totalwrite = status; ++ break; ++ } ++ len -= thiswrite; ++ buf += thiswrite; ++ offset += thiswrite; ++ totalwrite += thiswrite; ++ if (offset > resource->size) { ++ resource->size = offset; ++ } ++ } while (len > 0); ++ } ++ } ++ write_unlock(&dh->lock); ++ } ++ *off = offset; ++ DbgPrint("return = 0x%x", totalwrite); ++ ++ return (totalwrite); ++} ++ ++loff_t novfs_daemon_lib_llseek(struct file * file, loff_t offset, int origin) ++{ ++ struct daemon_handle *dh; ++ struct daemon_resource *resource; ++ ++ loff_t retVal = -EINVAL; ++ ++ DbgPrint("file=0x%p offset=%lld origin=%d", file, offset, origin); ++ ++ if (file->private_data) { ++ dh = file->private_data; ++ read_lock(&dh->lock); ++ if (&dh->list != dh->list.next) { ++ resource = ++ list_entry(dh->list.next, struct daemon_resource, list); ++ ++ if (DH_TYPE_STREAM == resource->type) { ++ switch (origin) { ++ case 2: ++ offset += resource->size; ++ break; ++ case 1: ++ offset += file->f_pos; ++ } ++ if (offset >= 0) { ++ if (offset != file->f_pos) { ++ file->f_pos = offset; ++ file->f_version = 0; ++ } ++ retVal = offset; ++ } ++ } ++ } ++ read_unlock(&dh->lock); ++ } ++ ++ DbgPrint("ret %lld", retVal); ++ ++ return retVal; ++} ++ ++#define DbgIocCall(str) __DbgPrint("[VFS XPLAT] Call " str "\n") ++ ++int novfs_daemon_lib_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int retCode = -ENOSYS; ++ struct daemon_handle *dh; ++ void *handle = NULL; ++ unsigned long cpylen; ++ ++ dh = file->private_data; ++ ++ DbgPrint("file=0x%p 0x%x 0x%p dh=0x%p", file, cmd, arg, dh); ++ ++ if (dh) { ++ ++ switch (cmd) { ++ case IOC_LOGIN: ++ retCode = daemon_login((struct novfs_login *)arg, &dh->session); ++ break; ++ ++ case IOC_LOGOUT: ++ retCode = daemon_logout((struct novfs_logout *)arg, &dh->session); ++ break; ++ ++ case IOC_DEBUGPRINT: ++ { ++ struct Ioctl_Debug { ++ int length; ++ char *data; ++ } io; ++ char *buf; ++ io.length = 0; ++ cpylen = ++ copy_from_user(&io, (void *)arg, ++ sizeof(io)); ++ if (io.length) { ++ buf = ++ kmalloc(io.length + 1, ++ GFP_KERNEL); ++ if (buf) { ++ buf[0] = 0; ++ cpylen = ++ copy_from_user(buf, io.data, ++ io.length); ++ buf[io.length] = '\0'; ++ __DbgPrint("%s", buf); ++ kfree(buf); ++ retCode = 0; ++ } ++ } ++ break; ++ } ++ ++ case IOC_XPLAT: ++ { ++ struct novfs_xplat data; ++ ++ cpylen = ++ copy_from_user(&data, (void *)arg, ++ sizeof(data)); ++ retCode = ++ ((data. ++ xfunction & 0x0000FFFF) | 0xCC000000); ++ ++ switch (data.xfunction) { ++ case NWC_OPEN_CONN_BY_NAME: ++ DbgIocCall("NwOpenConnByName"); ++ retCode = ++ novfs_open_conn_by_name(&data, ++ &handle, dh->session); ++ if (!retCode) ++ daemon_added_resource(dh, ++ DH_TYPE_CONNECTION,handle, 0, 0, 0); ++ break; ++ ++ case NWC_OPEN_CONN_BY_ADDRESS: ++ DbgIocCall("NwOpenConnByAddress"); ++ retCode = ++ novfs_open_conn_by_addr(&data, &handle, ++ dh->session); ++ if (!retCode) ++ daemon_added_resource(dh, ++ DH_TYPE_CONNECTION, ++ handle, 0, ++ 0, 0); ++ break; ++ ++ case NWC_OPEN_CONN_BY_REFERENCE: ++ ++ DbgIocCall("NwOpenConnByReference"); ++ retCode = ++ novfs_open_conn_by_ref(&data, &handle, ++ dh->session); ++ if (!retCode) ++ daemon_added_resource(dh, ++ DH_TYPE_CONNECTION, ++ handle, 0, ++ 0, 0); ++ break; ++ ++ case NWC_SYS_CLOSE_CONN: ++ DbgIocCall("NwSysCloseConn"); ++ retCode = ++ novfs_sys_conn_close(&data, (unsigned long *)&handle, dh->session); ++ daemon_remove_resource(dh, DH_TYPE_CONNECTION, handle, 0); ++ break; ++ ++ case NWC_CLOSE_CONN: ++ DbgIocCall("NwCloseConn"); ++ retCode = ++ novfs_conn_close(&data, &handle, ++ dh->session); ++ daemon_remove_resource(dh, ++ DH_TYPE_CONNECTION, ++ handle, 0); ++ break; ++ ++ case NWC_LOGIN_IDENTITY: ++ DbgIocCall("" ++ "NwLoginIdentity"); ++ retCode = ++ novfs_login_id(&data, dh->session); ++ break; ++ ++ case NWC_RAW_NCP_REQUEST: ++ DbgIocCall("[VFS XPLAT] Send Raw " ++ "NCP Request"); ++ retCode = novfs_raw_send(&data, dh->session); ++ break; ++ ++ case NWC_AUTHENTICATE_CONN_WITH_ID: ++ DbgIocCall("[VFS XPLAT] Authenticate " ++ "Conn With ID"); ++ retCode = ++ novfs_auth_conn(&data, ++ dh->session); ++ break; ++ ++ case NWC_UNAUTHENTICATE_CONN: ++ DbgIocCall("[VFS XPLAT] UnAuthenticate " ++ "Conn With ID"); ++ retCode = ++ novfs_unauthenticate(&data, ++ dh->session); ++ break; ++ ++ case NWC_LICENSE_CONN: ++ DbgIocCall("Call NwLicenseConn"); ++ retCode = ++ novfs_license_conn(&data, dh->session); ++ break; ++ ++ case NWC_LOGOUT_IDENTITY: ++ DbgIocCall("NwLogoutIdentity"); ++ retCode = ++ novfs_logout_id(&data, ++ dh->session); ++ break; ++ ++ case NWC_UNLICENSE_CONN: ++ DbgIocCall("NwUnlicense"); ++ retCode = ++ novfs_unlicense_conn(&data, dh->session); ++ break; ++ ++ case NWC_GET_CONN_INFO: ++ DbgIocCall("NwGetConnInfo"); ++ retCode = ++ novfs_get_conn_info(&data, dh->session); ++ break; ++ ++ case NWC_SET_CONN_INFO: ++ DbgIocCall("NwGetConnInfo"); ++ retCode = ++ novfs_set_conn_info(&data, dh->session); ++ break; ++ ++ case NWC_SCAN_CONN_INFO: ++ DbgIocCall("NwScanConnInfo"); ++ retCode = ++ novfs_scan_conn_info(&data, dh->session); ++ break; ++ ++ case NWC_GET_IDENTITY_INFO: ++ DbgIocCall("NwGetIdentityInfo"); ++ retCode = ++ novfs_get_id_info(&data, ++ dh->session); ++ break; ++ ++ case NWC_GET_REQUESTER_VERSION: ++ DbgIocCall("NwGetDaemonVersion"); ++ retCode = ++ novfs_get_daemon_ver(&data, ++ dh->session); ++ break; ++ ++ case NWC_GET_PREFERRED_DS_TREE: ++ DbgIocCall("NwcGetPreferredDsTree"); ++ retCode = ++ novfs_get_preferred_DS_tree(&data, ++ dh->session); ++ break; ++ ++ case NWC_SET_PREFERRED_DS_TREE: ++ DbgIocCall("NwcSetPreferredDsTree"); ++ retCode = ++ novfs_set_preferred_DS_tree(&data, ++ dh->session); ++ break; ++ ++ case NWC_GET_DEFAULT_NAME_CONTEXT: ++ DbgIocCall("NwcGetDefaultNameContext"); ++ retCode = ++ novfs_get_default_ctx(&data, ++ dh->session); ++ break; ++ ++ case NWC_SET_DEFAULT_NAME_CONTEXT: ++ DbgIocCall("NwcSetDefaultNameContext"); ++ retCode = ++ novfs_set_default_ctx(&data, ++ dh->session); ++ break; ++ ++ case NWC_QUERY_FEATURE: ++ DbgIocCall("NwQueryFeature"); ++ retCode = ++ novfs_query_feature(&data, dh->session); ++ break; ++ ++ case NWC_GET_TREE_MONITORED_CONN_REF: ++ DbgIocCall("NwcGetTreeMonitoredConn"); ++ retCode = ++ novfs_get_tree_monitored_conn(&data, ++ dh-> ++ session); ++ break; ++ ++ case NWC_ENUMERATE_IDENTITIES: ++ DbgIocCall("NwcEnumerateIdentities"); ++ retCode = ++ novfs_enum_ids(&data, ++ dh->session); ++ break; ++ ++ case NWC_CHANGE_KEY: ++ DbgIocCall("NwcChangeAuthKey"); ++ retCode = ++ novfs_change_auth_key(&data, ++ dh->session); ++ break; ++ ++ case NWC_CONVERT_LOCAL_HANDLE: ++ DbgIocCall("NwdConvertLocalHandle"); ++ retCode = ++ NwdConvertLocalHandle(&data, dh); ++ break; ++ ++ case NWC_CONVERT_NETWARE_HANDLE: ++ DbgIocCall("NwdConvertNetwareHandle"); ++ retCode = ++ NwdConvertNetwareHandle(&data, dh); ++ break; ++ ++ case NWC_SET_PRIMARY_CONN: ++ DbgIocCall("NwcSetPrimaryConn"); ++ retCode = ++ novfs_set_pri_conn(&data, ++ dh->session); ++ break; ++ ++ case NWC_GET_PRIMARY_CONN: ++ DbgIocCall("NwcGetPrimaryConn"); ++ retCode = ++ novfs_get_pri_conn(&data, ++ dh->session); ++ break; ++ ++ case NWC_MAP_DRIVE: ++ DbgIocCall("NwcMapDrive"); ++ retCode = ++ set_map_drive(&data, dh->session); ++ break; ++ ++ case NWC_UNMAP_DRIVE: ++ DbgIocCall("NwcUnMapDrive"); ++ retCode = ++ unmap_drive(&data, dh->session); ++ break; ++ ++ case NWC_ENUMERATE_DRIVES: ++ DbgIocCall("NwcEnumerateDrives"); ++ retCode = ++ novfs_enum_drives(&data, ++ dh->session); ++ break; ++ ++ case NWC_GET_MOUNT_PATH: ++ DbgIocCall("NwdGetMountPath"); ++ retCode = NwdGetMountPath(&data); ++ break; ++ ++ case NWC_GET_BROADCAST_MESSAGE: ++ DbgIocCall("NwdGetBroadcastMessage"); ++ retCode = ++ novfs_get_bcast_msg(&data, ++ dh->session); ++ break; ++ ++ case NWC_SET_KEY: ++ DbgIocCall("NwdSetKey"); ++ retCode = ++ novfs_set_key_value(&data, dh->session); ++ break; ++ ++ case NWC_VERIFY_KEY: ++ DbgIocCall("NwdVerifyKey"); ++ retCode = ++ novfs_verify_key_value(&data, ++ dh->session); ++ break; ++ ++ case NWC_RAW_NCP_REQUEST_ALL: ++ case NWC_NDS_RESOLVE_NAME_TO_ID: ++ case NWC_FRAGMENT_REQUEST: ++ case NWC_GET_CONFIGURED_NSPS: ++ default: ++ break; ++ ++ } ++ ++ DbgPrint("[NOVFS XPLAT] status Code = %X\n", ++ retCode); ++ break; ++ } ++ } ++ } ++ ++ return (retCode); ++} ++ ++unsigned int novfs_daemon_poll(struct file *file, ++ struct poll_table_struct *poll_table) ++{ ++ struct daemon_cmd *que; ++ unsigned int mask = POLLOUT | POLLWRNORM; ++ ++ que = get_next_queue(0); ++ if (que) ++ mask |= (POLLIN | POLLRDNORM); ++ return mask; ++} ++ ++static int NwdConvertNetwareHandle(struct novfs_xplat *pdata, struct daemon_handle * DHandle) ++{ ++ int retVal; ++ struct nwc_convert_netware_handle nh; ++ unsigned long cpylen; ++ ++ DbgPrint("DHandle=0x%p", DHandle); ++ ++ cpylen = ++ copy_from_user(&nh, pdata->reqData, ++ sizeof(struct nwc_convert_netware_handle)); ++ ++ retVal = ++ daemon_added_resource(DHandle, DH_TYPE_STREAM, ++ Uint32toHandle(nh.ConnHandle), ++ nh.NetWareHandle, nh.uAccessMode, ++ nh.uFileSize); ++ ++ return (retVal); ++} ++ ++static int NwdConvertLocalHandle(struct novfs_xplat *pdata, struct daemon_handle * DHandle) ++{ ++ int retVal = NWE_REQUESTER_FAILURE; ++ struct daemon_resource *resource; ++ struct nwc_convert_local_handle lh; ++ struct list_head *l; ++ unsigned long cpylen; ++ ++ DbgPrint("DHandle=0x%p", DHandle); ++ ++ read_lock(&DHandle->lock); ++ ++ list_for_each(l, &DHandle->list) { ++ resource = list_entry(l, struct daemon_resource, list); ++ ++ if (DH_TYPE_STREAM == resource->type) { ++ lh.uConnReference = ++ HandletoUint32(resource->connection); ++ ++//sgled memcpy(lh.NwWareHandle, resource->handle, sizeof(resource->handle)); ++ memcpy(lh.NetWareHandle, resource->handle, sizeof(resource->handle)); //sgled ++ if (pdata->repLen >= sizeof(struct nwc_convert_local_handle)) { ++ cpylen = ++ copy_to_user(pdata->repData, &lh, ++ sizeof(struct nwc_convert_local_handle)); ++ retVal = 0; ++ } else { ++ retVal = NWE_BUFFER_OVERFLOW; ++ } ++ break; ++ } ++ } ++ ++ read_unlock(&DHandle->lock); ++ ++ return (retVal); ++} ++ ++static int NwdGetMountPath(struct novfs_xplat *pdata) ++{ ++ int retVal = NWE_REQUESTER_FAILURE; ++ int len; ++ unsigned long cpylen; ++ struct nwc_get_mount_path mp; ++ ++ cpylen = copy_from_user(&mp, pdata->reqData, pdata->reqLen); ++ ++ if (novfs_current_mnt) { ++ ++ len = strlen(novfs_current_mnt) + 1; ++ if ((len > mp.MountPathLen) && mp.pMountPath) { ++ retVal = NWE_BUFFER_OVERFLOW; ++ } else { ++ if (mp.pMountPath) { ++ cpylen = ++ copy_to_user(mp.pMountPath, ++ novfs_current_mnt, len); ++ } ++ retVal = 0; ++ } ++ ++ mp.MountPathLen = len; ++ ++ if (pdata->repData && (pdata->repLen >= sizeof(mp))) { ++ cpylen = copy_to_user(pdata->repData, &mp, sizeof(mp)); ++ } ++ } ++ ++ return (retVal); ++} ++ ++static int set_map_drive(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ int retVal; ++ unsigned long cpylen; ++ struct nwc_map_drive_ex symInfo; ++ char *path; ++ struct drive_map *drivemap, *dm; ++ struct list_head *list; ++ ++ retVal = novfs_set_map_drive(pdata, Session); ++ if (retVal) ++ return retVal; ++ if (copy_from_user(&symInfo, pdata->reqData, sizeof(symInfo))) ++ return -EFAULT; ++ drivemap = ++ kmalloc(sizeof(struct drive_map) + symInfo.linkOffsetLength, ++ GFP_KERNEL); ++ if (!drivemap) ++ return -ENOMEM; ++ ++ path = (char *)pdata->reqData; ++ path += symInfo.linkOffset; ++ cpylen = ++ copy_from_user(drivemap->name, path, ++ symInfo.linkOffsetLength); ++ ++ drivemap->session = Session; ++ drivemap->hash = ++ full_name_hash(drivemap->name, ++ symInfo.linkOffsetLength - 1); ++ drivemap->namelen = symInfo.linkOffsetLength - 1; ++ DbgPrint("hash=0x%lx path=%s", drivemap->hash, drivemap->name); ++ ++ dm = (struct drive_map *) & DriveMapList.next; ++ ++ down(&DriveMapLock); ++ ++ list_for_each(list, &DriveMapList) { ++ dm = list_entry(list, struct drive_map, list); ++ __DbgPrint("%s: dm=0x%p\n" ++ " hash: 0x%lx\n" ++ " namelen: %d\n" ++ " name: %s\n", __func__, ++ dm, dm->hash, dm->namelen, dm->name); ++ ++ if (drivemap->hash == dm->hash) { ++ if (0 == ++ strcmp(dm->name, drivemap->name)) { ++ dm = NULL; ++ break; ++ } ++ } else if (drivemap->hash < dm->hash) { ++ break; ++ } ++ } ++ ++ if (dm) { ++ if ((dm == (struct drive_map *) & DriveMapList) || ++ (dm->hash < drivemap->hash)) { ++ list_add(&drivemap->list, &dm->list); ++ } else { ++ list_add_tail(&drivemap->list, ++ &dm->list); ++ } ++ } ++ else ++ kfree(drivemap); ++ up(&DriveMapLock); ++ return (retVal); ++} ++ ++static int unmap_drive(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ int retVal = NWE_REQUESTER_FAILURE; ++ struct nwc_unmap_drive_ex symInfo; ++ char *path; ++ struct drive_map *dm; ++ struct list_head *list; ++ unsigned long hash; ++ ++ ++ retVal = novfs_unmap_drive(pdata, Session); ++ if (retVal) ++ return retVal; ++ if (copy_from_user(&symInfo, pdata->reqData, sizeof(symInfo))) ++ return -EFAULT; ++ ++ path = kmalloc(symInfo.linkLen, GFP_KERNEL); ++ if (!path) ++ return -ENOMEM; ++ if (copy_from_user(path,((struct nwc_unmap_drive_ex *) pdata->reqData)->linkData, symInfo.linkLen)) { ++ kfree(path); ++ return -EFAULT; ++ } ++ ++ hash = full_name_hash(path, symInfo.linkLen - 1); ++ DbgPrint("hash=0x%x path=%s", hash, path); ++ ++ dm = NULL; ++ ++ down(&DriveMapLock); ++ ++ list_for_each(list, &DriveMapList) { ++ dm = list_entry(list, struct drive_map, list); ++ __DbgPrint("%s: dm=0x%p %s\n" ++ " hash: 0x%x\n" ++ " namelen: %d\n", __func__, ++ dm, dm->name, dm->hash, dm->namelen); ++ ++ if (hash == dm->hash) { ++ if (0 == strcmp(dm->name, path)) { ++ break; ++ } ++ } else if (hash < dm->hash) { ++ dm = NULL; ++ break; ++ } ++ } ++ ++ if (dm) { ++ __DbgPrint("%s: Remove dm=0x%p %s\n" ++ " hash: 0x%x\n" ++ " namelen: %d\n", __func__, ++ dm, dm->name, dm->hash, dm->namelen); ++ list_del(&dm->list); ++ kfree(dm); ++ } ++ ++ up(&DriveMapLock); ++ return (retVal); ++} ++ ++static void RemoveDriveMaps(void) ++{ ++ struct drive_map *dm; ++ struct list_head *list; ++ ++ down(&DriveMapLock); ++ list_for_each(list, &DriveMapList) { ++ dm = list_entry(list, struct drive_map, list); ++ ++ __DbgPrint("%s: dm=0x%p\n" ++ " hash: 0x%x\n" ++ " namelen: %d\n" ++ " name: %s\n", __func__, ++ dm, dm->hash, dm->namelen, dm->name); ++ local_unlink(dm->name); ++ list = list->prev; ++ list_del(&dm->list); ++ kfree(dm); ++ } ++ up(&DriveMapLock); ++} ++ ++/* As picked from do_unlinkat() */ ++ ++static long local_unlink(const char *pathname) ++{ ++ int error; ++ struct dentry *dentry; ++ char *name, *c; ++ struct nameidata nd; ++ struct inode *inode = NULL; ++ ++ error = path_lookup(pathname, LOOKUP_PARENT, &nd); ++ DbgPrint("path_lookup %s error: %d\n", pathname, error); ++ if (error) ++ return error; ++ ++ error = -EISDIR; ++ if (nd.last_type != LAST_NORM) ++ goto exit1; ++ mutex_lock(&nd.path.dentry->d_inode->i_mutex); ++ /* Get the filename of pathname */ ++ name=c=(char *)pathname; ++ while (*c!='\0') { ++ if (*c=='/') ++ name=++c; ++ c++; ++ } ++ dentry = lookup_one_len(name, nd.path.dentry, strlen(name)); ++ error = PTR_ERR(dentry); ++ DbgPrint("dentry %p", dentry); ++ if (!(dentry->d_inode->i_mode & S_IFLNK)) { ++ DbgPrint("%s not a link", name); ++ error=-ENOENT; ++ goto exit1; ++ } ++ ++ if (!IS_ERR(dentry)) { ++ /* Why not before? Because we want correct error value */ ++ if (nd.last.name[nd.last.len]) ++ goto slashes; ++ inode = dentry->d_inode; ++ if (inode) ++ atomic_inc(&inode->i_count); ++ error = mnt_want_write(nd.path.mnt); ++ DbgPrint("inode %p mnt_want_write error %d", inode, error); ++ if (error) ++ goto exit2; ++ error = vfs_unlink(nd.path.dentry->d_inode, dentry); ++ mnt_drop_write(nd.path.mnt); ++ exit2: ++ dput(dentry); ++ } ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++ if (inode) ++ iput(inode); /* truncate the inode here */ ++exit1: ++ path_put(&nd.path); ++ DbgPrint("returning error %d", error); ++ return error; ++ ++slashes: ++ error = !dentry->d_inode ? -ENOENT : ++ S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR; ++ goto exit2; ++} ++ +--- /dev/null ++++ b/fs/novfs/file.c +@@ -0,0 +1,1921 @@ ++/* ++ * Novell NCP Redirector for Linux ++ * Author: James Turner ++ * ++ * This file contains functions for accessing files through the daemon. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vfs.h" ++#include "commands.h" ++#include "nwerror.h" ++ ++static ssize_t novfs_tree_read(struct file * file, char *buf, size_t len, loff_t * off); ++extern struct dentry_operations novfs_dentry_operations; ++ ++static struct file_operations novfs_tree_operations = { ++ read:novfs_tree_read, ++}; ++ ++/* ++ * StripTrailingDots was added because some apps will ++ * try and create a file name with a trailing dot. NetWare ++ * doesn't like this and will return an error. ++ */ ++static int StripTrailingDots = 1; ++ ++int novfs_get_alltrees(struct dentry *parent) ++{ ++ unsigned char *p; ++ struct novfs_command_reply_header * reply = NULL; ++ unsigned long replylen = 0; ++ struct novfs_command_request_header cmd; ++ int retCode; ++ struct dentry *entry; ++ struct qstr name; ++ struct inode *inode; ++ ++ cmd.CommandType = 0; ++ cmd.SequenceNumber = 0; ++//sg ??? cmd.SessionId = 0x1234; ++ SC_INITIALIZE(cmd.SessionId); ++ ++ DbgPrint(""); ++ ++ retCode = Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE); ++ DbgPrint("reply=0x%p replylen=%d", reply, replylen); ++ if (reply) { ++ novfs_dump(replylen, reply); ++ if (!reply->ErrorCode ++ && (replylen > sizeof(struct novfs_command_reply_header))) { ++ p = (char *)reply + 8; ++ while (*p) { ++ DbgPrint("%s", p); ++ name.len = strlen(p); ++ name.name = p; ++ name.hash = full_name_hash(name.name, name.len); ++ entry = d_lookup(parent, &name); ++ if (NULL == entry) { ++ DbgPrint("adding %s", p); ++ entry = d_alloc(parent, &name); ++ if (entry) { ++ entry->d_op = &novfs_dentry_operations; ++ inode = novfs_get_inode(parent->d_sb, S_IFREG | 0400, 0, 0, 0, &name); ++ if (inode) { ++ inode->i_fop = &novfs_tree_operations; ++ d_add(entry, inode); ++ } ++ } ++ } ++ p += (name.len + 1); ++ } ++ } ++ kfree(reply); ++ } ++ return (retCode); ++} ++ ++static ssize_t novfs_tree_read(struct file * file, char *buf, size_t len, loff_t * off) ++{ ++ if (file->f_pos != 0) { ++ return (0); ++ } ++ if (copy_to_user(buf, "Tree\n", 5)) { ++ return (0); ++ } ++ return (5); ++} ++ ++int novfs_get_servers(unsigned char ** ServerList, struct novfs_schandle SessionId) ++{ ++ struct novfs_get_connected_server_list req; ++ struct novfs_get_connected_server_list_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0; ++ ++ *ServerList = NULL; ++ ++ req.Command.CommandType = VFS_COMMAND_GET_CONNECTED_SERVER_LIST; ++ req.Command.SessionId = SessionId; ++ ++ retCode = ++ Queue_Daemon_Command(&req, sizeof(req), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ DbgPrint("reply"); ++ replylen -= sizeof(struct novfs_command_reply_header); ++ if (!reply->Reply.ErrorCode && replylen) { ++ memcpy(reply, reply->List, replylen); ++ *ServerList = (unsigned char *) reply; ++ retCode = 0; ++ } else { ++ kfree(reply); ++ retCode = -ENOENT; ++ } ++ } ++ return (retCode); ++} ++ ++int novfs_get_vols(struct qstr *Server, unsigned char ** VolumeList, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_get_server_volume_list *req; ++ struct novfs_get_server_volume_list_reply *reply = NULL; ++ unsigned long replylen = 0, reqlen; ++ int retCode; ++ ++ *VolumeList = NULL; ++ reqlen = sizeof(struct novfs_get_server_volume_list) + Server->len; ++ req = kmalloc(reqlen, GFP_KERNEL); ++ if (!req) ++ return -ENOMEM; ++ req->Command.CommandType = VFS_COMMAND_GET_SERVER_VOLUME_LIST; ++ req->Length = Server->len; ++ memcpy(req->Name, Server->name, Server->len); ++ req->Command.SessionId = SessionId; ++ ++ retCode = ++ Queue_Daemon_Command(req, reqlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ DbgPrint("reply"); ++ novfs_dump(replylen, reply); ++ replylen -= sizeof(struct novfs_command_reply_header); ++ ++ if (!reply->Reply.ErrorCode && replylen) { ++ memcpy(reply, reply->List, replylen); ++ *VolumeList = (unsigned char *) reply; ++ retCode = 0; ++ } else { ++ kfree(reply); ++ retCode = -ENOENT; ++ } ++ } ++ kfree(req); ++ return (retCode); ++} ++ ++int novfs_get_file_info(unsigned char * Path, struct novfs_entry_info * Info, struct novfs_schandle SessionId) ++{ ++ struct novfs_verify_file_reply *reply = NULL; ++ unsigned long replylen = 0; ++ struct novfs_verify_file_request * cmd; ++ int cmdlen; ++ int retCode = -ENOENT; ++ int pathlen; ++ ++ DbgPrint("Path = %s", Path); ++ ++ Info->mode = S_IFDIR | 0700; ++ Info->uid = current_uid(); ++ Info->gid = current_gid(); ++ Info->size = 0; ++ Info->atime = Info->mtime = Info->ctime = CURRENT_TIME; ++ ++ if (Path && *Path) { ++ pathlen = strlen(Path); ++ if (StripTrailingDots) { ++ if ('.' == Path[pathlen - 1]) ++ pathlen--; ++ } ++ cmdlen = offsetof(struct novfs_verify_file_request,path) + pathlen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_VERIFY_FILE; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ cmd->pathLen = pathlen; ++ memcpy(cmd->path, Path, cmd->pathLen); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, ++ (void *)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ if (reply) { ++ ++ if (reply->Reply.ErrorCode) { ++ retCode = -ENOENT; ++ } else { ++ Info->type = 3; ++ Info->mode = S_IRWXU; ++ ++ if (reply-> ++ fileMode & NW_ATTRIBUTE_DIRECTORY) { ++ Info->mode |= S_IFDIR; ++ } else { ++ Info->mode |= S_IFREG; ++ } ++ ++ if (reply-> ++ fileMode & NW_ATTRIBUTE_READ_ONLY) { ++ Info->mode &= ~(S_IWUSR); ++ } ++ ++ Info->uid = current_euid(); ++ Info->gid = current_egid(); ++ Info->size = reply->fileSize; ++ Info->atime.tv_sec = ++ reply->lastAccessTime; ++ Info->atime.tv_nsec = 0; ++ Info->mtime.tv_sec = reply->modifyTime; ++ Info->mtime.tv_nsec = 0; ++ Info->ctime.tv_sec = reply->createTime; ++ Info->ctime.tv_nsec = 0; ++ DbgPrint("replylen=%d sizeof(VERIFY_FILE_REPLY)=%d", ++ replylen, ++ sizeof(struct novfs_verify_file_reply)); ++ if (replylen > ++ sizeof(struct novfs_verify_file_reply)) { ++ unsigned int *lp = ++ &reply->fileMode; ++ lp++; ++ DbgPrint("extra data 0x%x", ++ *lp); ++ Info->mtime.tv_nsec = *lp; ++ } ++ retCode = 0; ++ } ++ ++ kfree(reply); ++ } ++ kfree(cmd); ++ } ++ } ++ ++ DbgPrint("return 0x%x", retCode); ++ return (retCode); ++} ++ ++int novfs_getx_file_info(char *Path, const char *Name, char *buffer, ++ ssize_t buffer_size, ssize_t * dataLen, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_xa_get_reply *reply = NULL; ++ unsigned long replylen = 0; ++ struct novfs_xa_get_request *cmd; ++ int cmdlen; ++ int retCode = -ENOENT; ++ ++ int namelen = strlen(Name); ++ int pathlen = strlen(Path); ++ ++ DbgPrint("xattr: Path = %s, pathlen = %i, Name = %s, namelen = %i", ++ Path, pathlen, Name, namelen); ++ ++ if (namelen > MAX_XATTR_NAME_LEN) { ++ return ENOATTR; ++ } ++ ++ cmdlen = offsetof(struct novfs_xa_get_request, data) + pathlen + 1 + namelen + 1; // two '\0' ++ cmd = (struct novfs_xa_get_request *) kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_GET_EXTENDED_ATTRIBUTE; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->pathLen = pathlen; ++ memcpy(cmd->data, Path, cmd->pathLen + 1); //+ '\0' ++ ++ cmd->nameLen = namelen; ++ memcpy(cmd->data + cmd->pathLen + 1, Name, cmd->nameLen + 1); ++ ++ DbgPrint("xattr: PXA_GET_REQUEST BEGIN"); ++ DbgPrint("xattr: Queue_Daemon_Command %d", ++ cmd->Command.CommandType); ++ DbgPrint("xattr: Command.SessionId = %d", ++ cmd->Command.SessionId); ++ DbgPrint("xattr: pathLen = %d", cmd->pathLen); ++ DbgPrint("xattr: Path = %s", cmd->data); ++ DbgPrint("xattr: nameLen = %d", cmd->nameLen); ++ DbgPrint("xattr: name = %s", (cmd->data + cmd->pathLen + 1)); ++ DbgPrint("xattr: PXA_GET_REQUEST END"); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ ++ if (reply) { ++ ++ if (reply->Reply.ErrorCode) { ++ DbgPrint("xattr: reply->Reply.ErrorCode=%d, %X", ++ reply->Reply.ErrorCode, ++ reply->Reply.ErrorCode); ++ DbgPrint("xattr: replylen=%d", replylen); ++ ++ //0xC9 = EA not found (C9), 0xD1 = EA access denied ++ if ((reply->Reply.ErrorCode == 0xC9) ++ || (reply->Reply.ErrorCode == 0xD1)) { ++ retCode = -ENOATTR; ++ } else { ++ retCode = -ENOENT; ++ } ++ } else { ++ ++ *dataLen = ++ replylen - sizeof(struct novfs_command_reply_header); ++ DbgPrint("xattr: replylen=%u, dataLen=%u", ++ replylen, *dataLen); ++ ++ if (buffer_size >= *dataLen) { ++ DbgPrint("xattr: copying to buffer from &reply->pData"); ++ memcpy(buffer, &reply->pData, *dataLen); ++ ++ retCode = 0; ++ } else { ++ DbgPrint("xattr: (!!!) buffer is smaller then reply"); ++ retCode = -ERANGE; ++ } ++ DbgPrint("xattr: /dumping buffer"); ++ novfs_dump(*dataLen, buffer); ++ DbgPrint("xattr: \\after dumping buffer"); ++ } ++ ++ kfree(reply); ++ } else { ++ DbgPrint("xattr: reply = NULL"); ++ } ++ kfree(cmd); ++ ++ } ++ ++ return retCode; ++} ++ ++int novfs_setx_file_info(char *Path, const char *Name, const void *Value, ++ unsigned long valueLen, unsigned long *bytesWritten, ++ int flags, struct novfs_schandle SessionId) ++{ ++ struct novfs_xa_set_reply *reply = NULL; ++ unsigned long replylen = 0; ++ struct novfs_xa_set_request *cmd; ++ int cmdlen; ++ int retCode = -ENOENT; ++ ++ int namelen = strlen(Name); ++ int pathlen = strlen(Path); ++ ++ DbgPrint("xattr: Path = %s, pathlen = %i, Name = %s, namelen = %i, " ++ "value len = %u", Path, pathlen, Name, namelen, valueLen); ++ ++ if (namelen > MAX_XATTR_NAME_LEN) { ++ return ENOATTR; ++ } ++ ++ cmdlen = offsetof(struct novfs_xa_set_request, data) + pathlen + 1 + namelen + 1 + valueLen; ++ cmd = (struct novfs_xa_set_request *) kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_SET_EXTENDED_ATTRIBUTE; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->flags = flags; ++ cmd->pathLen = pathlen; ++ memcpy(cmd->data, Path, cmd->pathLen); ++ ++ cmd->nameLen = namelen; ++ memcpy(cmd->data + cmd->pathLen + 1, Name, cmd->nameLen + 1); ++ ++ cmd->valueLen = valueLen; ++ memcpy(cmd->data + cmd->pathLen + 1 + cmd->nameLen + 1, Value, ++ valueLen); ++ ++ DbgPrint("xattr: PXA_SET_REQUEST BEGIN"); ++ DbgPrint("attr: Queue_Daemon_Command %d", ++ cmd->Command.CommandType); ++ DbgPrint("xattr: Command.SessionId = %d", ++ cmd->Command.SessionId); ++ DbgPrint("xattr: pathLen = %d", cmd->pathLen); ++ DbgPrint("xattr: Path = %s", cmd->data); ++ DbgPrint("xattr: nameLen = %d", cmd->nameLen); ++ DbgPrint("xattr: name = %s", (cmd->data + cmd->pathLen + 1)); ++ novfs_dump(valueLen < 16 ? valueLen : 16, (char *)Value); ++ ++ DbgPrint("xattr: PXA_SET_REQUEST END"); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ ++ if (reply) { ++ ++ if (reply->Reply.ErrorCode) { ++ DbgPrint("xattr: reply->Reply.ErrorCode=%d, %X", ++ reply->Reply.ErrorCode, ++ reply->Reply.ErrorCode); ++ DbgPrint("xattr: replylen=%d", replylen); ++ ++ retCode = -reply->Reply.ErrorCode; //-ENOENT; ++ } else { ++ ++ DbgPrint("xattr: replylen=%u, real len = %u", ++ replylen, ++ replylen - sizeof(struct novfs_command_reply_header)); ++ memcpy(bytesWritten, &reply->pData, ++ replylen - sizeof(struct novfs_command_reply_header)); ++ ++ retCode = 0; ++ } ++ ++ kfree(reply); ++ } else { ++ DbgPrint("xattr: reply = NULL"); ++ } ++ kfree(cmd); ++ ++ } ++ ++ return retCode; ++} ++ ++int novfs_listx_file_info(char *Path, char *buffer, ssize_t buffer_size, ++ ssize_t * dataLen, struct novfs_schandle SessionId) ++{ ++ struct novfs_xa_list_reply *reply = NULL; ++ unsigned long replylen = 0; ++ struct novfs_verify_file_request *cmd; ++ int cmdlen; ++ int retCode = -ENOENT; ++ ++ int pathlen = strlen(Path); ++ DbgPrint("xattr: Path = %s, pathlen = %i", Path, pathlen); ++ ++ *dataLen = 0; ++ cmdlen = offsetof(struct novfs_verify_file_request, path) + pathlen; ++ cmd = (struct novfs_verify_file_request *) kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_LIST_EXTENDED_ATTRIBUTES; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ cmd->pathLen = pathlen; ++ memcpy(cmd->path, Path, cmd->pathLen + 1); //+ '\0' ++ DbgPrint("xattr: PVERIFY_FILE_REQUEST BEGIN"); ++ DbgPrint("xattr: Queue_Daemon_Command %d", ++ cmd->Command.CommandType); ++ DbgPrint("xattr: Command.SessionId = %d", ++ cmd->Command.SessionId); ++ DbgPrint("xattr: pathLen = %d", cmd->pathLen); ++ DbgPrint("xattr: Path = %s", cmd->path); ++ DbgPrint("xattr: PVERIFY_FILE_REQUEST END"); ++ ++ retCode = Queue_Daemon_Command(cmd, cmdlen, NULL, 0, ++ (void *)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ if (reply) { ++ ++ if (reply->Reply.ErrorCode) { ++ DbgPrint("xattr: reply->Reply.ErrorCode=%d, %X", ++ reply->Reply.ErrorCode, ++ reply->Reply.ErrorCode); ++ DbgPrint("xattr: replylen=%d", replylen); ++ ++ retCode = -ENOENT; ++ } else { ++ *dataLen = ++ replylen - sizeof(struct novfs_command_reply_header); ++ DbgPrint("xattr: replylen=%u, dataLen=%u", ++ replylen, *dataLen); ++ ++ if (buffer_size >= *dataLen) { ++ DbgPrint("xattr: copying to buffer " ++ "from &reply->pData"); ++ memcpy(buffer, &reply->pData, *dataLen); ++ } else { ++ DbgPrint("xattr: (!!!) buffer is " ++ "smaller then reply\n"); ++ retCode = -ERANGE; ++ } ++ DbgPrint("xattr: /dumping buffer"); ++ novfs_dump(*dataLen, buffer); ++ DbgPrint("xattr: \\after dumping buffer"); ++ ++ retCode = 0; ++ } ++ ++ kfree(reply); ++ } else { ++ DbgPrint("xattr: reply = NULL"); ++ } ++ kfree(cmd); ++ ++ } ++ ++ return retCode; ++} ++ ++static int begin_directory_enumerate(unsigned char * Path, int PathLen, void ** EnumHandle, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_begin_enumerate_directory_request *cmd; ++ struct novfs_begin_enumerate_directory_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode, cmdlen; ++ ++ *EnumHandle = 0; ++ ++ cmdlen = offsetof(struct ++ novfs_begin_enumerate_directory_request, path) + PathLen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_START_ENUMERATE; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->pathLen = PathLen; ++ memcpy(cmd->path, Path, PathLen); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++/* ++ * retCode = Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, &replylen, 0); ++ */ ++ if (reply) { ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ } else { ++ *EnumHandle = reply->enumerateHandle; ++ retCode = 0; ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ } else { ++ retCode = -ENOMEM; ++ } ++ return (retCode); ++} ++ ++int novfs_end_directory_enumerate(void *EnumHandle, struct novfs_schandle SessionId) ++{ ++ struct novfs_end_enumerate_directory_request cmd; ++ struct novfs_end_enumerate_directory_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode; ++ ++ cmd.Command.CommandType = VFS_COMMAND_END_ENUMERATE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.enumerateHandle = EnumHandle; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, 0); ++ if (reply) { ++ retCode = 0; ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ ++ return (retCode); ++} ++ ++static int directory_enumerate_ex(void ** EnumHandle, struct novfs_schandle SessionId, int *Count, ++ struct novfs_entry_info **PInfo, int Interrupt) ++{ ++ struct novfs_enumerate_directory_ex_request cmd; ++ struct novfs_enumerate_directory_ex_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0; ++ struct novfs_entry_info * info; ++ struct novfs_enumerate_directory_ex_data *data; ++ int isize; ++ ++ if (PInfo) ++ *PInfo = NULL; ++ *Count = 0; ++ ++ cmd.Command.CommandType = VFS_COMMAND_ENUMERATE_DIRECTORY_EX; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.enumerateHandle = *EnumHandle; ++ cmd.pathLen = 0; ++ cmd.path[0] = '\0'; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, Interrupt); ++ ++ if (reply) { ++ retCode = 0; ++ /* ++ * The VFS_COMMAND_ENUMERATE_DIRECTORY call can return an ++ * error but there could still be valid data. ++ */ ++ ++ if (!reply->Reply.ErrorCode || ++ ((replylen > sizeof(struct novfs_command_reply_header)) && ++ (reply->enumCount > 0))) { ++ DbgPrint("isize=%d", replylen); ++ data = ++ (struct novfs_enumerate_directory_ex_data *) ((char *)reply + ++ sizeof ++ (struct novfs_enumerate_directory_ex_reply)); ++ isize = ++ replylen - sizeof(struct novfs_enumerate_directory_ex_reply *) - ++ reply->enumCount * ++ offsetof(struct ++ novfs_enumerate_directory_ex_data, name); ++ isize += ++ (reply->enumCount * ++ offsetof(struct novfs_entry_info, name)); ++ ++ if (PInfo) { ++ *PInfo = info = kmalloc(isize, GFP_KERNEL); ++ if (*PInfo) { ++ DbgPrint("data=0x%p info=0x%p", ++ data, info); ++ *Count = reply->enumCount; ++ do { ++ DbgPrint("data=0x%p length=%d", ++ data); ++ ++ info->type = 3; ++ info->mode = S_IRWXU; ++ ++ if (data-> ++ mode & ++ NW_ATTRIBUTE_DIRECTORY) { ++ info->mode |= S_IFDIR; ++ info->mode |= S_IXUSR; ++ } else { ++ info->mode |= S_IFREG; ++ } ++ ++ if (data-> ++ mode & ++ NW_ATTRIBUTE_READ_ONLY) { ++ info->mode &= ++ ~(S_IWUSR); ++ } ++ ++ if (data-> ++ mode & NW_ATTRIBUTE_EXECUTE) ++ { ++ info->mode |= S_IXUSR; ++ } ++ ++ info->uid = current_euid(); ++ info->gid = current_egid(); ++ info->size = data->size; ++ info->atime.tv_sec = ++ data->lastAccessTime; ++ info->atime.tv_nsec = 0; ++ info->mtime.tv_sec = ++ data->modifyTime; ++ info->mtime.tv_nsec = 0; ++ info->ctime.tv_sec = ++ data->createTime; ++ info->ctime.tv_nsec = 0; ++ info->namelength = ++ data->nameLen; ++ memcpy(info->name, data->name, ++ data->nameLen); ++ data = ++ (struct novfs_enumerate_directory_ex_data *) ++ & data->name[data->nameLen]; ++ replylen = ++ (int)((char *)&info-> ++ name[info-> ++ namelength] - ++ (char *)info); ++ DbgPrint("info=0x%p", info); ++ novfs_dump(replylen, info); ++ ++ info = ++ (struct novfs_entry_info *) & info-> ++ name[info->namelength]; ++ ++ } while (--reply->enumCount); ++ } ++ } ++ ++ if (reply->Reply.ErrorCode) { ++ retCode = -1; /* Eof of data */ ++ } ++ *EnumHandle = reply->enumerateHandle; ++ } else { ++ retCode = -ENODATA; ++ } ++ kfree(reply); ++ } ++ ++ return (retCode); ++} ++ ++int novfs_get_dir_listex(unsigned char * Path, void ** EnumHandle, int *Count, ++ struct novfs_entry_info **Info, ++ struct novfs_schandle SessionId) ++{ ++ int retCode = -ENOENT; ++ ++ if (Count) ++ *Count = 0; ++ if (Info) ++ *Info = NULL; ++ ++ if ((void *) - 1 == *EnumHandle) { ++ return (-ENODATA); ++ } ++ ++ if (0 == *EnumHandle) { ++ retCode = ++ begin_directory_enumerate(Path, strlen(Path), EnumHandle, ++ SessionId); ++ } ++ ++ if (*EnumHandle) { ++ retCode = ++ directory_enumerate_ex(EnumHandle, SessionId, Count, Info, ++ INTERRUPTIBLE); ++ if (retCode) { ++ novfs_end_directory_enumerate(*EnumHandle, SessionId); ++ retCode = 0; ++ *EnumHandle = Uint32toHandle(-1); ++ } ++ } ++ return (retCode); ++} ++ ++int novfs_open_file(unsigned char * Path, int Flags, struct novfs_entry_info * Info, ++ void ** Handle, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_open_file_request *cmd; ++ struct novfs_open_file_reply *reply; ++ unsigned long replylen = 0; ++ int retCode, cmdlen, pathlen; ++ ++ pathlen = strlen(Path); ++ ++ if (StripTrailingDots) { ++ if ('.' == Path[pathlen - 1]) ++ pathlen--; ++ } ++ ++ *Handle = 0; ++ ++ cmdlen = offsetof(struct novfs_open_file_request, path) + pathlen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_OPEN_FILE; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->access = 0; ++ ++ if (!(Flags & O_WRONLY) || (Flags & O_RDWR)) { ++ cmd->access |= NWD_ACCESS_READ; ++ } ++ ++ if ((Flags & O_WRONLY) || (Flags & O_RDWR)) { ++ cmd->access |= NWD_ACCESS_WRITE; ++ } ++ ++ switch (Flags & (O_CREAT | O_EXCL | O_TRUNC)) { ++ case O_CREAT: ++ cmd->disp = NWD_DISP_OPEN_ALWAYS; ++ break; ++ ++ case O_CREAT | O_EXCL: ++ cmd->disp = NWD_DISP_CREATE_NEW; ++ break; ++ ++ case O_TRUNC: ++ cmd->disp = NWD_DISP_CREATE_ALWAYS; ++ break; ++ ++ case O_CREAT | O_TRUNC: ++ cmd->disp = NWD_DISP_CREATE_ALWAYS; ++ break; ++ ++ case O_CREAT | O_EXCL | O_TRUNC: ++ cmd->disp = NWD_DISP_CREATE_NEW; ++ break; ++ ++ default: ++ cmd->disp = NWD_DISP_OPEN_EXISTING; ++ break; ++ } ++ ++ cmd->mode = NWD_SHARE_READ | NWD_SHARE_WRITE | NWD_SHARE_DELETE; ++ ++ cmd->pathLen = pathlen; ++ memcpy(cmd->path, Path, pathlen); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ ++ if (reply) { ++ if (reply->Reply.ErrorCode) { ++ if (NWE_OBJECT_EXISTS == reply->Reply.ErrorCode) { ++ retCode = -EEXIST; ++ } else if (NWE_ACCESS_DENIED == ++ reply->Reply.ErrorCode) { ++ retCode = -EACCES; ++ } else if (NWE_FILE_IN_USE == ++ reply->Reply.ErrorCode) { ++ retCode = -EBUSY; ++ } else { ++ retCode = -ENOENT; ++ } ++ } else { ++ *Handle = reply->handle; ++ retCode = 0; ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ } else { ++ retCode = -ENOMEM; ++ } ++ return (retCode); ++} ++ ++int novfs_create(unsigned char * Path, int DirectoryFlag, struct novfs_schandle SessionId) ++{ ++ struct novfs_create_file_request *cmd; ++ struct novfs_create_file_reply *reply; ++ unsigned long replylen = 0; ++ int retCode, cmdlen, pathlen; ++ ++ pathlen = strlen(Path); ++ ++ if (StripTrailingDots) { ++ if ('.' == Path[pathlen - 1]) ++ pathlen--; ++ } ++ ++ cmdlen = offsetof(struct novfs_create_file_request, path) + pathlen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ cmd->Command.CommandType = VFS_COMMAND_CREATE_FILE; ++ if (DirectoryFlag) { ++ cmd->Command.CommandType = VFS_COMMAND_CREATE_DIRECOTRY; ++ } ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->pathlength = pathlen; ++ memcpy(cmd->path, Path, pathlen); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ ++ if (reply) { ++ retCode = 0; ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ if (reply->Reply.ErrorCode == NWE_ACCESS_DENIED) ++ retCode = -EACCES; ++ ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++} ++ ++int novfs_close_file(void *Handle, struct novfs_schandle SessionId) ++{ ++ struct novfs_close_file_request cmd; ++ struct novfs_close_file_reply *reply; ++ unsigned long replylen = 0; ++ int retCode; ++ ++ cmd.Command.CommandType = VFS_COMMAND_CLOSE_FILE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.handle = Handle; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, 0); ++ if (reply) { ++ retCode = 0; ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ return (retCode); ++} ++ ++int novfs_read_file(void *Handle, unsigned char * Buffer, size_t * Bytes, ++ loff_t * Offset, struct novfs_schandle SessionId) ++{ ++ struct novfs_read_file_request cmd; ++ struct novfs_read_file_reply * reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0; ++ size_t len; ++ ++ len = *Bytes; ++ *Bytes = 0; ++ ++ if (offsetof(struct novfs_read_file_reply, data) + len ++ > novfs_max_iosize) { ++ len = novfs_max_iosize - offsetof(struct ++ novfs_read_file_reply, data); ++ len = (len / PAGE_SIZE) * PAGE_SIZE; ++ } ++ ++ cmd.Command.CommandType = VFS_COMMAND_READ_FILE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.handle = Handle; ++ cmd.len = len; ++ cmd.offset = *Offset; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ ++ DbgPrint("Queue_Daemon_Command 0x%x replylen=%d", retCode, replylen); ++ ++ if (!retCode) { ++ if (reply->Reply.ErrorCode) { ++ if (NWE_FILE_IO_LOCKED == reply->Reply.ErrorCode) { ++ retCode = -EBUSY; ++ } else { ++ retCode = -EIO; ++ } ++ } else { ++ replylen -= offsetof(struct ++ novfs_read_file_reply, data); ++ ++ if (replylen > 0) { ++ replylen -= ++ copy_to_user(Buffer, reply->data, replylen); ++ *Bytes = replylen; ++ } ++ } ++ } ++ ++ if (reply) { ++ kfree(reply); ++ } ++ ++ DbgPrint("*Bytes=0x%x retCode=0x%x", *Bytes, retCode); ++ ++ return (retCode); ++} ++ ++int novfs_read_pages(void *Handle, struct novfs_data_list *DList, ++ int DList_Cnt, size_t * Bytes, loff_t * Offset, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_read_file_request cmd; ++ struct novfs_read_file_reply * reply = NULL; ++ struct novfs_read_file_reply lreply; ++ unsigned long replylen = 0; ++ int retCode = 0; ++ size_t len; ++ ++ len = *Bytes; ++ *Bytes = 0; ++ ++ DbgPrint("Handle=0x%p Dlst=0x%p Dlcnt=%d Bytes=%d Offset=%lld " ++ "SessionId=0x%p:%p", Handle, DList, DList_Cnt, len, *Offset, ++ SessionId.hTypeId, SessionId.hId); ++ ++ cmd.Command.CommandType = VFS_COMMAND_READ_FILE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.handle = Handle; ++ cmd.len = len; ++ cmd.offset = *Offset; ++ ++ /* ++ * Dlst first entry is reserved for reply header. ++ */ ++ DList[0].page = NULL; ++ DList[0].offset = &lreply; ++ DList[0].len = offsetof(struct novfs_read_file_reply, data); ++ DList[0].rwflag = DLWRITE; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), DList, DList_Cnt, ++ (void *)&reply, &replylen, INTERRUPTIBLE); ++ ++ DbgPrint("Queue_Daemon_Command 0x%x", retCode); ++ ++ if (!retCode) { ++ if (reply) { ++ memcpy(&lreply, reply, sizeof(lreply)); ++ } ++ ++ if (lreply.Reply.ErrorCode) { ++ if (NWE_FILE_IO_LOCKED == lreply.Reply.ErrorCode) { ++ retCode = -EBUSY; ++ } else { ++ retCode = -EIO; ++ } ++ } ++ *Bytes = replylen - offsetof(struct ++ novfs_read_file_reply, data); ++ } ++ ++ if (reply) { ++ kfree(reply); ++ } ++ ++ DbgPrint("retCode=0x%x", retCode); ++ ++ return (retCode); ++} ++ ++int novfs_write_file(void *Handle, unsigned char * Buffer, size_t * Bytes, ++ loff_t * Offset, struct novfs_schandle SessionId) ++{ ++ struct novfs_write_file_request cmd; ++ struct novfs_write_file_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0, cmdlen; ++ size_t len; ++ ++ unsigned long boff; ++ struct page **pages; ++ struct novfs_data_list *dlist; ++ int res = 0, npage, i; ++ struct novfs_write_file_reply lreply; ++ ++ len = *Bytes; ++ cmdlen = offsetof(struct novfs_write_file_request, data); ++ ++ *Bytes = 0; ++ ++ memset(&lreply, 0, sizeof(lreply)); ++ ++ DbgPrint("cmdlen=%ld len=%ld", cmdlen, len); ++ ++ if ((cmdlen + len) > novfs_max_iosize) { ++ len = novfs_max_iosize - cmdlen; ++ len = (len / PAGE_SIZE) * PAGE_SIZE; ++ } ++ cmd.Command.CommandType = VFS_COMMAND_WRITE_FILE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ cmd.handle = Handle; ++ cmd.len = len; ++ cmd.offset = *Offset; ++ ++ DbgPrint("cmdlen=%ld len=%ld", cmdlen, len); ++ ++ npage = ++ (((unsigned long)Buffer & ~PAGE_MASK) + len + ++ (PAGE_SIZE - 1)) >> PAGE_SHIFT; ++ ++ dlist = kmalloc(sizeof(struct novfs_data_list) * (npage + 1), GFP_KERNEL); ++ if (NULL == dlist) { ++ return (-ENOMEM); ++ } ++ ++ pages = kmalloc(sizeof(struct page *) * npage, GFP_KERNEL); ++ ++ if (NULL == pages) { ++ kfree(dlist); ++ return (-ENOMEM); ++ } ++ ++ down_read(¤t->mm->mmap_sem); ++ ++ res = get_user_pages(current, current->mm, (unsigned long)Buffer, npage, 0, /* read type */ ++ 0, /* don't force */ ++ pages, NULL); ++ ++ up_read(¤t->mm->mmap_sem); ++ ++ DbgPrint("res=%d", res); ++ ++ if (res > 0) { ++ boff = (unsigned long)Buffer & ~PAGE_MASK; ++ ++ flush_dcache_page(pages[0]); ++ dlist[0].page = pages[0]; ++ dlist[0].offset = (char *)boff; ++ dlist[0].len = PAGE_SIZE - boff; ++ dlist[0].rwflag = DLREAD; ++ ++ if (dlist[0].len > len) { ++ dlist[0].len = len; ++ } ++ ++ DbgPrint("page=0x%p offset=0x%p len=%d", ++ dlist[0].page, dlist[0].offset, dlist[0].len); ++ ++ boff = dlist[0].len; ++ ++ DbgPrint("len=%d boff=%d", len, boff); ++ ++ for (i = 1; (i < res) && (boff < len); i++) { ++ flush_dcache_page(pages[i]); ++ ++ dlist[i].page = pages[i]; ++ dlist[i].offset = NULL; ++ dlist[i].len = len - boff; ++ if (dlist[i].len > PAGE_SIZE) { ++ dlist[i].len = PAGE_SIZE; ++ } ++ dlist[i].rwflag = DLREAD; ++ ++ boff += dlist[i].len; ++ DbgPrint("%d: page=0x%p offset=0x%p len=%d", i, ++ dlist[i].page, dlist[i].offset, dlist[i].len); ++ } ++ ++ dlist[i].page = NULL; ++ dlist[i].offset = &lreply; ++ dlist[i].len = sizeof(lreply); ++ dlist[i].rwflag = DLWRITE; ++ res++; ++ ++ DbgPrint("Buffer=0x%p boff=0x%x len=%d", Buffer, boff, len); ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, cmdlen, dlist, res, ++ (void *)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ } else { ++ char *kdata; ++ ++ res = 0; ++ ++ kdata = kmalloc(len, GFP_KERNEL); ++ if (kdata) { ++ len -= copy_from_user(kdata, Buffer, len); ++ dlist[0].page = NULL; ++ dlist[0].offset = kdata; ++ dlist[0].len = len; ++ dlist[0].rwflag = DLREAD; ++ ++ dlist[1].page = NULL; ++ dlist[1].offset = &lreply; ++ dlist[1].len = sizeof(lreply); ++ dlist[1].rwflag = DLWRITE; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, cmdlen, dlist, 2, ++ (void *)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ kfree(kdata); ++ } ++ } ++ ++ DbgPrint("retCode=0x%x reply=0x%p", retCode, reply); ++ ++ if (!retCode) { ++ switch (lreply.Reply.ErrorCode) { ++ case 0: ++ *Bytes = (size_t) lreply.bytesWritten; ++ retCode = 0; ++ break; ++ ++ case NWE_INSUFFICIENT_SPACE: ++ retCode = -ENOSPC; ++ break; ++ ++ case NWE_ACCESS_DENIED: ++ retCode = -EACCES; ++ break; ++ ++ default: ++ retCode = -EIO; ++ break; ++ } ++ } ++ ++ if (res) { ++ for (i = 0; i < res; i++) { ++ if (dlist[i].page) { ++ page_cache_release(dlist[i].page); ++ } ++ } ++ } ++ ++ kfree(pages); ++ kfree(dlist); ++ ++ DbgPrint("*Bytes=0x%x retCode=0x%x", *Bytes, ++ retCode); ++ ++ return (retCode); ++} ++ ++/* ++ * Arguments: HANDLE Handle - novfsd file handle ++ * struct page *Page - Page to be written out ++ * struct novfs_schandle SessionId - novfsd session handle ++ * ++ * Returns: 0 - Success ++ * -ENOSPC - Out of space on server ++ * -EACCES - Access denied ++ * -EIO - Any other error ++ * ++ * Abstract: Write page to file. ++ */ ++int novfs_write_page(void *Handle, struct page *Page, struct novfs_schandle SessionId) ++{ ++ struct novfs_write_file_request cmd; ++ struct novfs_write_file_reply lreply; ++ struct novfs_write_file_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0, cmdlen; ++ struct novfs_data_list dlst[2]; ++ ++ DbgPrint("Handle=0x%p Page=0x%p Index=%lu SessionId=0x%llx", ++ Handle, Page, Page->index, SessionId); ++ ++ dlst[0].page = NULL; ++ dlst[0].offset = &lreply; ++ dlst[0].len = sizeof(lreply); ++ dlst[0].rwflag = DLWRITE; ++ ++ dlst[1].page = Page; ++ dlst[1].offset = 0; ++ dlst[1].len = PAGE_CACHE_SIZE; ++ dlst[1].rwflag = DLREAD; ++ ++ cmdlen = offsetof(struct novfs_write_file_request, data); ++ ++ cmd.Command.CommandType = VFS_COMMAND_WRITE_FILE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.handle = Handle; ++ cmd.len = PAGE_CACHE_SIZE; ++ cmd.offset = (loff_t) Page->index << PAGE_CACHE_SHIFT;; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, cmdlen, &dlst, 2, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (!retCode) { ++ if (reply) { ++ memcpy(&lreply, reply, sizeof(lreply)); ++ } ++ switch (lreply.Reply.ErrorCode) { ++ case 0: ++ retCode = 0; ++ break; ++ ++ case NWE_INSUFFICIENT_SPACE: ++ retCode = -ENOSPC; ++ break; ++ ++ case NWE_ACCESS_DENIED: ++ retCode = -EACCES; ++ break; ++ ++ default: ++ retCode = -EIO; ++ break; ++ } ++ } ++ ++ if (reply) { ++ kfree(reply); ++ } ++ ++ DbgPrint("retCode=0x%x", retCode); ++ ++ return (retCode); ++} ++ ++int novfs_write_pages(void *Handle, struct novfs_data_list *DList, int DList_Cnt, ++ size_t Bytes, loff_t Offset, struct novfs_schandle SessionId) ++{ ++ struct novfs_write_file_request cmd; ++ struct novfs_write_file_reply lreply; ++ struct novfs_write_file_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0, cmdlen; ++ size_t len; ++ ++ DbgPrint("Handle=0x%p Dlst=0x%p Dlcnt=%d Bytes=%d Offset=%lld " ++ "SessionId=0x%llx\n", Handle, DList, DList_Cnt, Bytes, ++ Offset, SessionId); ++ ++ DList[0].page = NULL; ++ DList[0].offset = &lreply; ++ DList[0].len = sizeof(lreply); ++ DList[0].rwflag = DLWRITE; ++ ++ len = Bytes; ++ cmdlen = offsetof(struct novfs_write_file_request, data); ++ ++ if (len) { ++ cmd.Command.CommandType = VFS_COMMAND_WRITE_FILE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.handle = Handle; ++ cmd.len = len; ++ cmd.offset = Offset; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, cmdlen, DList, DList_Cnt, ++ (void *)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (!retCode) { ++ if (reply) { ++ memcpy(&lreply, reply, sizeof(lreply)); ++ } ++ switch (lreply.Reply.ErrorCode) { ++ case 0: ++ retCode = 0; ++ break; ++ ++ case NWE_INSUFFICIENT_SPACE: ++ retCode = -ENOSPC; ++ break; ++ ++ case NWE_ACCESS_DENIED: ++ retCode = -EACCES; ++ break; ++ ++ default: ++ retCode = -EIO; ++ break; ++ } ++ } ++ if (reply) { ++ kfree(reply); ++ } ++ } ++ DbgPrint("retCode=0x%x", retCode); ++ ++ return (retCode); ++} ++ ++int novfs_read_stream(void *ConnHandle, unsigned char * Handle, u_char * Buffer, ++ size_t * Bytes, loff_t * Offset, int User, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_read_stream_request cmd; ++ struct novfs_read_stream_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0; ++ size_t len; ++ ++ len = *Bytes; ++ *Bytes = 0; ++ ++ if (offsetof(struct novfs_read_file_reply, data) + len ++ > novfs_max_iosize) { ++ len = novfs_max_iosize - offsetof(struct ++ novfs_read_file_reply, data); ++ len = (len / PAGE_SIZE) * PAGE_SIZE; ++ } ++ ++ cmd.Command.CommandType = VFS_COMMAND_READ_STREAM; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.connection = ConnHandle; ++ memcpy(cmd.handle, Handle, sizeof(cmd.handle)); ++ cmd.len = len; ++ cmd.offset = *Offset; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ ++ DbgPrint("Queue_Daemon_Command 0x%x replylen=%d", retCode, replylen); ++ ++ if (reply) { ++ retCode = 0; ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ } else { ++ replylen -= offsetof(struct ++ novfs_read_stream_reply, data); ++ if (replylen > 0) { ++ if (User) { ++ replylen -= ++ copy_to_user(Buffer, reply->data, ++ replylen); ++ } else { ++ memcpy(Buffer, reply->data, replylen); ++ } ++ ++ *Bytes = replylen; ++ } ++ } ++ kfree(reply); ++ } ++ ++ DbgPrint("*Bytes=0x%x retCode=0x%x", *Bytes, retCode); ++ ++ return (retCode); ++} ++ ++int novfs_write_stream(void *ConnHandle, unsigned char * Handle, u_char * Buffer, ++ size_t * Bytes, loff_t * Offset, struct novfs_schandle SessionId) ++{ ++ struct novfs_write_stream_request * cmd; ++ struct novfs_write_stream_reply * reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0, cmdlen; ++ size_t len; ++ ++ len = *Bytes; ++ cmdlen = len + offsetof(struct novfs_write_stream_request, data); ++ *Bytes = 0; ++ ++ if (cmdlen > novfs_max_iosize) { ++ cmdlen = novfs_max_iosize; ++ len = cmdlen - offsetof(struct ++ novfs_write_stream_request, data); ++ } ++ ++ DbgPrint("cmdlen=%d len=%d", cmdlen, len); ++ ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (cmd) { ++ if (Buffer && len) { ++ len -= copy_from_user(cmd->data, Buffer, len); ++ } ++ ++ DbgPrint("len=%d", len); ++ ++ cmd->Command.CommandType = VFS_COMMAND_WRITE_STREAM; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->connection = ConnHandle; ++ memcpy(cmd->handle, Handle, sizeof(cmd->handle)); ++ cmd->len = len; ++ cmd->offset = *Offset; ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ switch (reply->Reply.ErrorCode) { ++ case 0: ++ retCode = 0; ++ break; ++ ++ case NWE_INSUFFICIENT_SPACE: ++ retCode = -ENOSPC; ++ break; ++ ++ case NWE_ACCESS_DENIED: ++ retCode = -EACCES; ++ break; ++ ++ default: ++ retCode = -EIO; ++ break; ++ } ++ DbgPrint("reply->bytesWritten=0x%lx", ++ reply->bytesWritten); ++ *Bytes = reply->bytesWritten; ++ kfree(reply); ++ } ++ kfree(cmd); ++ } ++ DbgPrint("*Bytes=0x%x retCode=0x%x", *Bytes, retCode); ++ ++ return (retCode); ++} ++ ++int novfs_close_stream(void *ConnHandle, unsigned char * Handle, struct novfs_schandle SessionId) ++{ ++ struct novfs_close_stream_request cmd; ++ struct novfs_close_stream_reply *reply; ++ unsigned long replylen = 0; ++ int retCode; ++ ++ cmd.Command.CommandType = VFS_COMMAND_CLOSE_STREAM; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.connection = ConnHandle; ++ memcpy(cmd.handle, Handle, sizeof(cmd.handle)); ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, 0); ++ if (reply) { ++ retCode = 0; ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ return (retCode); ++} ++ ++int novfs_delete(unsigned char * Path, int DirectoryFlag, struct novfs_schandle SessionId) ++{ ++ struct novfs_delete_file_request *cmd; ++ struct novfs_delete_file_reply *reply; ++ unsigned long replylen = 0; ++ int retCode, cmdlen, pathlen; ++ ++ pathlen = strlen(Path); ++ ++ if (StripTrailingDots) { ++ if ('.' == Path[pathlen - 1]) ++ pathlen--; ++ } ++ ++ cmdlen = offsetof(struct novfs_delete_file_request, path) + pathlen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_DELETE_FILE; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->isDirectory = DirectoryFlag; ++ cmd->pathlength = pathlen; ++ memcpy(cmd->path, Path, pathlen); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ retCode = 0; ++ if (reply->Reply.ErrorCode) { ++ if ((reply->Reply.ErrorCode & 0xFFFF) == 0x0006) { /* Access Denied Error */ ++ retCode = -EACCES; ++ } else { ++ retCode = -EIO; ++ } ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ } else { ++ retCode = -ENOMEM; ++ } ++ return (retCode); ++} ++ ++int novfs_trunc(unsigned char * Path, int PathLen, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_truncate_file_request *cmd; ++ struct novfs_truncate_file_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode, cmdlen; ++ ++ if (StripTrailingDots) { ++ if ('.' == Path[PathLen - 1]) ++ PathLen--; ++ } ++ cmdlen = offsetof(struct novfs_truncate_file_request, path) ++ + PathLen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_TRUNCATE_FILE; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->pathLen = PathLen; ++ memcpy(cmd->path, Path, PathLen); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ if (reply->Reply.ErrorCode) { ++ retCode = -EIO; ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ } else { ++ retCode = -ENOMEM; ++ } ++ return (retCode); ++} ++ ++int novfs_trunc_ex(void *Handle, loff_t Offset, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_write_file_request cmd; ++ struct novfs_write_file_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode = 0, cmdlen; ++ ++ DbgPrint("Handle=0x%p Offset=%lld", Handle, Offset); ++ ++ cmdlen = offsetof(struct novfs_write_file_request, data); ++ ++ cmd.Command.CommandType = VFS_COMMAND_WRITE_FILE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ cmd.handle = Handle; ++ cmd.len = 0; ++ cmd.offset = Offset; ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ ++ DbgPrint("retCode=0x%x reply=0x%p", retCode, reply); ++ ++ if (!retCode) { ++ switch (reply->Reply.ErrorCode) { ++ case 0: ++ retCode = 0; ++ break; ++ ++ case NWE_INSUFFICIENT_SPACE: ++ retCode = -ENOSPC; ++ break; ++ ++ case NWE_ACCESS_DENIED: ++ retCode = -EACCES; ++ break; ++ ++ case NWE_FILE_IO_LOCKED: ++ retCode = -EBUSY; ++ break; ++ ++ default: ++ retCode = -EIO; ++ break; ++ } ++ } ++ ++ if (reply) { ++ kfree(reply); ++ } ++ ++ DbgPrint("retCode=%d", retCode); ++ ++ return (retCode); ++} ++ ++int novfs_rename_file(int DirectoryFlag, unsigned char * OldName, int OldLen, ++ unsigned char * NewName, int NewLen, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_rename_file_request cmd; ++ struct novfs_rename_file_reply *reply; ++ unsigned long replylen = 0; ++ int retCode; ++ ++ __DbgPrint("%s:\n" ++ " DirectoryFlag: %d\n" ++ " OldName: %.*s\n" ++ " NewName: %.*s\n" ++ " SessionId: 0x%llx\n", __func__, ++ DirectoryFlag, OldLen, OldName, NewLen, NewName, SessionId); ++ ++ cmd.Command.CommandType = VFS_COMMAND_RENAME_FILE; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = SessionId; ++ ++ cmd.directoryFlag = DirectoryFlag; ++ ++ if (StripTrailingDots) { ++ if ('.' == OldName[OldLen - 1]) ++ OldLen--; ++ if ('.' == NewName[NewLen - 1]) ++ NewLen--; ++ } ++ ++ cmd.newnameLen = NewLen; ++ memcpy(cmd.newname, NewName, NewLen); ++ ++ cmd.oldnameLen = OldLen; ++ memcpy(cmd.oldname, OldName, OldLen); ++ ++ retCode = ++ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ retCode = 0; ++ if (reply->Reply.ErrorCode) { ++ retCode = -ENOENT; ++ } ++ kfree(reply); ++ } ++ return (retCode); ++} ++ ++int novfs_set_attr(unsigned char * Path, struct iattr *Attr, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_set_file_info_request *cmd; ++ struct novfs_set_file_info_reply *reply; ++ unsigned long replylen = 0; ++ int retCode, cmdlen, pathlen; ++ ++ pathlen = strlen(Path); ++ ++ if (StripTrailingDots) { ++ if ('.' == Path[pathlen - 1]) ++ pathlen--; ++ } ++ ++ cmdlen = offsetof(struct novfs_set_file_info_request,path) + pathlen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_SET_FILE_INFO; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ cmd->fileInfo.ia_valid = Attr->ia_valid; ++ cmd->fileInfo.ia_mode = Attr->ia_mode; ++ cmd->fileInfo.ia_uid = Attr->ia_uid; ++ cmd->fileInfo.ia_gid = Attr->ia_uid; ++ cmd->fileInfo.ia_size = Attr->ia_size; ++ cmd->fileInfo.ia_atime = Attr->ia_atime.tv_sec; ++ cmd->fileInfo.ia_mtime = Attr->ia_mtime.tv_sec;; ++ cmd->fileInfo.ia_ctime = Attr->ia_ctime.tv_sec;; ++/* ++ cmd->fileInfo.ia_attr_flags = Attr->ia_attr_flags; ++*/ ++ cmd->fileInfo.ia_attr_flags = 0; ++ ++ cmd->pathlength = pathlen; ++ memcpy(cmd->path, Path, pathlen); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, ++ &replylen, INTERRUPTIBLE); ++ if (reply) { ++ switch (reply->Reply.ErrorCode) { ++ case 0: ++ retCode = 0; ++ break; ++ ++ case NWE_PARAM_INVALID: ++ retCode = -EINVAL; ++ break; ++ ++ case NWE_FILE_IO_LOCKED: ++ retCode = -EBUSY; ++ break; ++ ++ default: ++ retCode = -EIO; ++ break; ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ } else { ++ retCode = -ENOMEM; ++ } ++ return (retCode); ++} ++ ++int novfs_get_file_cache_flag(unsigned char * Path, ++ struct novfs_schandle SessionId) ++{ ++ struct novfs_get_cache_flag *cmd; ++ struct novfs_get_cache_flag_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int cmdlen; ++ int retCode = 0; ++ int pathlen; ++ ++ DbgPrint("Path = %s", Path); ++ ++ if (Path && *Path) { ++ pathlen = strlen(Path); ++ if (StripTrailingDots) { ++ if ('.' == Path[pathlen - 1]) ++ pathlen--; ++ } ++ cmdlen = offsetof(struct novfs_get_cache_flag, path) + ++ pathlen; ++ cmd = (struct novfs_get_cache_flag *) ++ kmalloc(cmdlen, GFP_KERNEL); ++ if (cmd) { ++ cmd->Command.CommandType = VFS_COMMAND_GET_CACHE_FLAG; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ cmd->pathLen = pathlen; ++ memcpy(cmd->path, Path, cmd->pathLen); ++ ++ Queue_Daemon_Command(cmd, cmdlen, NULL, 0, ++ (void *)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ if (reply) { ++ ++ if (!reply->Reply.ErrorCode) { ++ retCode = reply->CacheFlag; ++ } ++ ++ kfree(reply); ++ } ++ kfree(cmd); ++ } ++ } ++ ++ DbgPrint("return %d", retCode); ++ return (retCode); ++} ++ ++/* ++ * Arguments: ++ * SessionId, file handle, type of lock (read/write or unlock), ++ * start of lock area, length of lock area ++ * ++ * Notes: lock type - fcntl ++ */ ++int novfs_set_file_lock(struct novfs_schandle SessionId, void *Handle, ++ unsigned char fl_type, loff_t fl_start, loff_t fl_len) ++{ ++ struct novfs_set_file_lock_request *cmd; ++ struct novfs_set_file_lock_reply *reply = NULL; ++ unsigned long replylen = 0; ++ int retCode; ++ ++ retCode = -1; ++ ++ DbgPrint("SessionId: 0x%llx\n", SessionId); ++ ++ cmd = ++ (struct novfs_set_file_lock_request *) kmalloc(sizeof(struct novfs_set_file_lock_request), GFP_KERNEL); ++ ++ if (cmd) { ++ DbgPrint("2"); ++ ++ cmd->Command.CommandType = VFS_COMMAND_SET_FILE_LOCK; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = SessionId; ++ ++ cmd->handle = Handle; ++ if (F_RDLCK == fl_type) { ++ fl_type = 1; // LockRegionExclusive ++ } else if (F_WRLCK == fl_type) { ++ fl_type = 0; // LockRegionShared ++ } ++ ++ cmd->fl_type = fl_type; ++ cmd->fl_start = fl_start; ++ cmd->fl_len = fl_len; ++ ++ DbgPrint("3"); ++ ++ DbgPrint("BEGIN dump arguments"); ++ DbgPrint("Queue_Daemon_Command %d", ++ cmd->Command.CommandType); ++ DbgPrint("cmd->handle = 0x%p", cmd->handle); ++ DbgPrint("cmd->fl_type = %u", cmd->fl_type); ++ DbgPrint("cmd->fl_start = 0x%X", cmd->fl_start); ++ DbgPrint("cmd->fl_len = 0x%X", cmd->fl_len); ++ DbgPrint("sizeof(SET_FILE_LOCK_REQUEST) = %u", ++ sizeof(struct novfs_set_file_lock_request)); ++ DbgPrint("END dump arguments"); ++ ++ retCode = ++ Queue_Daemon_Command(cmd, sizeof(struct novfs_set_file_lock_request), ++ NULL, 0, (void *)&reply, &replylen, ++ INTERRUPTIBLE); ++ DbgPrint("4"); ++ ++ if (reply) { ++ DbgPrint("5, ErrorCode = %X", reply->Reply.ErrorCode); ++ ++ if (reply->Reply.ErrorCode) { ++ retCode = reply->Reply.ErrorCode; ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ } ++ ++ DbgPrint("6"); ++ ++ return (retCode); ++} +--- /dev/null ++++ b/fs/novfs/inode.c +@@ -0,0 +1,4638 @@ ++/* ++ * Novell NCP Redirector for Linux ++ * Author: James Turner ++ * ++ * This file contains functions used to control access to the Linux file ++ * system. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/*===[ Include files specific to this module ]============================*/ ++#include "vfs.h" ++ ++ ++struct inode_data { ++ void *Scope; ++ unsigned long Flags; ++ struct list_head IList; ++ struct inode *Inode; ++ unsigned long cntDC; ++ struct list_head DirCache; ++ struct semaphore DirCacheLock; ++ void * FileHandle; ++ int CacheFlag; ++ char Name[1]; /* Needs to be last entry */ ++}; ++ ++#define FILE_UPDATE_TIMEOUT 2 ++ ++/*===[ Function prototypes ]=============================================*/ ++ ++static unsigned long novfs_internal_hash(struct qstr *name); ++static int novfs_d_add(struct dentry *p, struct dentry *d, struct inode *i, int add); ++ ++static int novfs_get_sb(struct file_system_type *Fstype, int Flags, ++ const char *Dev_name, void *Data, struct vfsmount *Mnt); ++ ++static void novfs_kill_sb(struct super_block *SB); ++ ++ ++/* ++ * Declared dentry_operations ++ */ ++int novfs_d_revalidate(struct dentry *, struct nameidata *); ++int novfs_d_hash(struct dentry *, struct qstr *); ++int novfs_d_compare(struct dentry *, struct qstr *, struct qstr *); ++int novfs_d_delete(struct dentry *dentry); ++void novfs_d_release(struct dentry *dentry); ++void novfs_d_iput(struct dentry *dentry, struct inode *inode); ++ ++/* ++ * Declared directory operations ++ */ ++int novfs_dir_open(struct inode *inode, struct file *file); ++int novfs_dir_release(struct inode *inode, struct file *file); ++loff_t novfs_dir_lseek(struct file *file, loff_t offset, int origin); ++ssize_t novfs_dir_read(struct file *file, char *buf, size_t len, loff_t * off); ++void addtodentry(struct dentry *Parent, unsigned char *List, int Level); ++int novfs_filldir(void *data, const char *name, int namelen, loff_t off, ++ ino_t ino, unsigned ftype); ++int novfs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir); ++int novfs_dir_fsync(struct file *file, struct dentry *dentry, int datasync); ++ ++/* ++ * Declared address space operations ++ */ ++int novfs_a_writepage(struct page *page, struct writeback_control *wbc); ++int novfs_a_writepages(struct address_space *mapping, ++ struct writeback_control *wbc); ++int novfs_a_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata); ++int novfs_a_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pagep, void *fsdata); ++int novfs_a_readpage(struct file *file, struct page *page); ++int novfs_a_readpages(struct file *file, struct address_space *mapping, ++ struct list_head *page_lst, unsigned nr_pages); ++ssize_t novfs_a_direct_IO(int rw, struct kiocb *kiocb, const struct iovec *iov, ++ loff_t offset, unsigned long nr_segs); ++ ++/* ++ * Declared file_operations ++ */ ++ssize_t novfs_f_read(struct file *, char *, size_t, loff_t *); ++ssize_t novfs_f_write(struct file *, const char *, size_t, loff_t *); ++int novfs_f_readdir(struct file *, void *, filldir_t); ++int novfs_f_ioctl(struct inode *, struct file *, unsigned int, unsigned long); ++int novfs_f_mmap(struct file *file, struct vm_area_struct *vma); ++int novfs_f_open(struct inode *, struct file *); ++int novfs_f_flush(struct file *, fl_owner_t); ++int novfs_f_release(struct inode *, struct file *); ++int novfs_f_fsync(struct file *, struct dentry *, int datasync); ++int novfs_f_lock(struct file *, int, struct file_lock *); ++ ++/* ++ * Declared inode_operations ++ */ ++int novfs_i_create(struct inode *, struct dentry *, int, struct nameidata *); ++struct dentry *novfs_i_lookup(struct inode *, struct dentry *, ++ struct nameidata *); ++int novfs_i_mkdir(struct inode *, struct dentry *, int); ++int novfs_i_unlink(struct inode *dir, struct dentry *dentry); ++int novfs_i_rmdir(struct inode *, struct dentry *); ++int novfs_i_mknod(struct inode *, struct dentry *, int, dev_t); ++int novfs_i_rename(struct inode *, struct dentry *, struct inode *, ++ struct dentry *); ++int novfs_i_setattr(struct dentry *, struct iattr *); ++int novfs_i_getattr(struct vfsmount *mnt, struct dentry *, struct kstat *); ++int novfs_i_revalidate(struct dentry *dentry); ++ ++/* ++ * Extended attributes operations ++ */ ++ ++ssize_t novfs_i_getxattr(struct dentry *dentry, const char *name, void *buffer, ++ size_t size); ++int novfs_i_setxattr(struct dentry *dentry, const char *name, const void *value, ++ size_t value_size, int flags); ++ssize_t novfs_i_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size); ++ ++void update_inode(struct inode *Inode, struct novfs_entry_info *Info); ++ ++/* ++ * Declared super_operations ++ */ ++void novfs_read_inode(struct inode *inode); ++void novfs_write_inode(struct inode *inode); ++int novfs_notify_change(struct dentry *dentry, struct iattr *attr); ++void novfs_clear_inode(struct inode *inode); ++int novfs_show_options(struct seq_file *s, struct vfsmount *m); ++ ++int novfs_statfs(struct dentry *de, struct kstatfs *buf); ++ ++/* ++ * Declared control interface functions ++ */ ++ssize_t ++novfs_control_Read(struct file *file, char *buf, size_t nbytes, loff_t * ppos); ++ ++ssize_t ++novfs_control_write(struct file *file, const char *buf, size_t nbytes, ++ loff_t * ppos); ++ ++int novfs_control_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg); ++ ++int __init init_novfs(void); ++void __exit exit_novfs(void); ++ ++int novfs_lock_inode_cache(struct inode *i); ++void novfs_unlock_inode_cache(struct inode *i); ++int novfs_enumerate_inode_cache(struct inode *i, struct list_head **iteration, ++ ino_t * ino, struct novfs_entry_info *info); ++int novfs_get_entry(struct inode *i, struct qstr *name, ino_t * ino, ++ struct novfs_entry_info *info); ++int novfs_get_entry_by_pos(struct inode *i, loff_t pos, ino_t * ino, ++ struct novfs_entry_info *info); ++int novfs_get_entry_time(struct inode *i, struct qstr *name, ino_t * ino, ++ struct novfs_entry_info *info, u64 * EntryTime); ++int novfs_get_remove_entry(struct inode *i, ino_t * ino, struct novfs_entry_info *info); ++void novfs_invalidate_inode_cache(struct inode *i); ++struct novfs_dir_cache *novfs_lookup_inode_cache(struct inode *i, struct qstr *name, ++ ino_t ino); ++int novfs_lookup_validate(struct inode *i, struct qstr *name, ino_t ino); ++int novfs_add_inode_entry(struct inode *i, struct qstr *name, ino_t ino, ++ struct novfs_entry_info *info); ++int novfs_update_entry(struct inode *i, struct qstr *name, ino_t ino, ++ struct novfs_entry_info *info); ++void novfs_remove_inode_entry(struct inode *i, struct qstr *name, ino_t ino); ++void novfs_free_invalid_entries(struct inode *i); ++void novfs_free_inode_cache(struct inode *i); ++ ++/*===[ Global variables ]=================================================*/ ++struct dentry_operations novfs_dentry_operations = { ++ .d_revalidate = novfs_d_revalidate, ++ .d_hash = novfs_d_hash, ++ .d_compare = novfs_d_compare, ++ //.d_delete = novfs_d_delete, ++ .d_release = novfs_d_release, ++ .d_iput = novfs_d_iput, ++}; ++ ++struct file_operations novfs_dir_operations = { ++ .owner = THIS_MODULE, ++ .open = novfs_dir_open, ++ .release = novfs_dir_release, ++ .llseek = novfs_dir_lseek, ++ .read = novfs_dir_read, ++ .readdir = novfs_dir_readdir, ++ .fsync = novfs_dir_fsync, ++}; ++ ++static struct file_operations novfs_file_operations = { ++ .owner = THIS_MODULE, ++ .read = novfs_f_read, ++ .write = novfs_f_write, ++ .readdir = novfs_f_readdir, ++ .ioctl = novfs_f_ioctl, ++ .mmap = novfs_f_mmap, ++ .open = novfs_f_open, ++ .flush = novfs_f_flush, ++ .release = novfs_f_release, ++ .fsync = novfs_f_fsync, ++ .llseek = generic_file_llseek, ++ .lock = novfs_f_lock, ++}; ++ ++static struct address_space_operations novfs_nocache_aops = { ++ .readpage = novfs_a_readpage, ++}; ++ ++struct backing_dev_info novfs_backing_dev_info = { ++ .ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE, ++ .state = 0, ++ .capabilities = BDI_CAP_NO_WRITEBACK | BDI_CAP_MAP_COPY, ++ .unplug_io_fn = default_unplug_io_fn, ++}; ++ ++static struct address_space_operations novfs_aops = { ++ .readpage = novfs_a_readpage, ++ .readpages = novfs_a_readpages, ++ .writepage = novfs_a_writepage, ++ .writepages = novfs_a_writepages, ++ .write_begin = novfs_a_write_begin, ++ .write_end = novfs_a_write_end, ++ .set_page_dirty = __set_page_dirty_nobuffers, ++ .direct_IO = novfs_a_direct_IO, ++}; ++ ++static struct inode_operations novfs_inode_operations = { ++ .create = novfs_i_create, ++ .lookup = novfs_i_lookup, ++ .unlink = novfs_i_unlink, ++ .mkdir = novfs_i_mkdir, ++ .rmdir = novfs_i_rmdir, ++ .mknod = novfs_i_mknod, ++ .rename = novfs_i_rename, ++ .setattr = novfs_i_setattr, ++ .getattr = novfs_i_getattr, ++ .getxattr = novfs_i_getxattr, ++ .setxattr = novfs_i_setxattr, ++ .listxattr = novfs_i_listxattr, ++}; ++ ++static struct inode_operations novfs_file_inode_operations = { ++ .setattr = novfs_i_setattr, ++ .getattr = novfs_i_getattr, ++ .getxattr = novfs_i_getxattr, ++ .setxattr = novfs_i_setxattr, ++ .listxattr = novfs_i_listxattr, ++}; ++ ++static struct super_operations novfs_ops = { ++ .statfs = novfs_statfs, ++ .clear_inode = novfs_clear_inode, ++ .drop_inode = generic_delete_inode, ++ .show_options = novfs_show_options, ++ ++}; ++ ++/* Not currently used ++static struct file_operations novfs_Control_operations = { ++ .read = novfs_Control_read, ++ .write = novfs_Control_write, ++ .ioctl = novfs_Control_ioctl, ++}; ++*/ ++ ++static atomic_t novfs_Inode_Number = ATOMIC_INIT(0); ++ ++ ++struct dentry *novfs_root = NULL; ++char *novfs_current_mnt = NULL; ++ ++DECLARE_MUTEX(InodeList_lock); ++ ++LIST_HEAD(InodeList); ++ ++DECLARE_MUTEX(TimeDir_Lock); ++uint64_t lastTime; ++char lastDir[PATH_MAX]; ++ ++uint64_t inHAXTime; ++int inHAX; ++ ++unsigned long InodeCount = 0, DCCount = 0; ++unsigned long novfs_update_timeout = FILE_UPDATE_TIMEOUT; ++int novfs_page_cache = 0; ++ ++struct file_private { ++ int listedall; ++ void *enumHandle; ++}; ++ ++static void PRINT_DENTRY(const char *s, struct dentry *d) ++{ ++ __DbgPrint("%s: 0x%p\n", s, d); ++ __DbgPrint(" d_count: 0x%x\n", d->d_count); ++ __DbgPrint(" d_lock: 0x%x\n", d->d_lock); ++ __DbgPrint(" d_inode: 0x%x\n", d->d_inode); ++ __DbgPrint(" d_lru: 0x%p\n" ++ " next: 0x%p\n" ++ " prev: 0x%p\n", &d->d_lru, d->d_lru.next, ++ d->d_lru.prev); ++ __DbgPrint(" d_child: 0x%p\n" " next: 0x%p\n" ++ " prev: 0x%p\n", &d->d_u.d_child, ++ d->d_u.d_child.next, d->d_u.d_child.prev); ++ __DbgPrint(" d_subdirs: 0x%p\n" " next: 0x%p\n" ++ " prev: 0x%p\n", &d->d_subdirs, d->d_subdirs.next, ++ d->d_subdirs.prev); ++ __DbgPrint(" d_alias: 0x%p\n" " next: 0x%p\n" ++ " prev: 0x%p\n", &d->d_alias, d->d_alias.next, ++ d->d_alias.prev); ++ __DbgPrint(" d_time: 0x%x\n", d->d_time); ++ __DbgPrint(" d_op: 0x%p\n", d->d_op); ++ __DbgPrint(" d_sb: 0x%p\n", d->d_sb); ++ __DbgPrint(" d_flags: 0x%x\n", d->d_flags); ++ __DbgPrint(" d_mounted: 0x%x\n", d->d_mounted); ++ __DbgPrint(" d_fsdata: 0x%p\n", d->d_fsdata); ++/* DbgPrint(" d_cookie: 0x%x\n", d->d_cookie); */ ++ __DbgPrint(" d_parent: 0x%p\n", d->d_parent); ++ __DbgPrint(" d_name: 0x%p %.*s\n", &d->d_name, d->d_name.len, ++ d->d_name.name); ++ __DbgPrint(" name: 0x%p\n" " len: %d\n" ++ " hash: 0x%x\n", d->d_name.name, d->d_name.len, ++ d->d_name.hash); ++ __DbgPrint(" d_hash: 0x%x\n" " next: 0x%x\n" ++ " pprev: 0x%x\n", d->d_hash, d->d_hash.next, ++ d->d_hash.pprev); ++} ++ ++/*++======================================================================*/ ++int novfs_remove_from_root(char *RemoveName) ++{ ++ struct qstr name; ++ struct dentry *dentry; ++ struct inode *dir; ++ ++ DbgPrint("%s", RemoveName); ++ name.len = strlen(RemoveName); ++ name.name = RemoveName; ++ novfs_d_hash(novfs_root, &name); ++ ++ dentry = d_lookup(novfs_root, &name); ++ if (dentry) { ++ if (dentry->d_inode && dentry->d_inode->i_private) { ++ struct inode_data *n_inode = ++ dentry->d_inode->i_private; ++ n_inode->Scope = NULL; ++ } ++ dput(dentry); ++ } ++ ++ dir = novfs_root->d_inode; ++ ++ novfs_lock_inode_cache(dir); ++ novfs_remove_inode_entry(dir, &name, 0); ++ novfs_unlock_inode_cache(dir); ++ ++ return (0); ++} ++ ++/*++======================================================================*/ ++int novfs_add_to_root(char *AddName) ++{ ++ struct qstr name; ++ struct inode *dir; ++ struct novfs_entry_info info; ++ ino_t ino; ++ ++ DbgPrint("%s", AddName); ++ name.len = strlen(AddName); ++ name.name = AddName; ++ novfs_d_hash(novfs_root, &name); ++ ++ dir = novfs_root->d_inode; ++ ++ novfs_lock_inode_cache(dir); ++ ++ ino = 0; ++ ++ if (!novfs_lookup_inode_cache(dir, &name, 0)) { ++ info.mode = S_IFDIR | 0700; ++ info.size = 0; ++ info.atime = info.ctime = info.mtime = CURRENT_TIME; ++ ++ ino = (ino_t)atomic_inc_return(&novfs_Inode_Number); ++ novfs_add_inode_entry(dir, &name, ino, &info); ++ } ++ ++ novfs_unlock_inode_cache(dir); ++ ++ return (0); ++} ++ ++/*++======================================================================*/ ++int novfs_Add_to_Root2(char *AddName) ++{ ++ struct dentry *entry; ++ struct qstr name; ++ struct inode *inode; ++ void *scope; ++ ++ DbgPrint("%s", AddName); ++ name.len = strlen(AddName); ++ name.name = AddName; ++ ++ novfs_d_hash(novfs_root, &name); ++ ++ entry = d_lookup(novfs_root, &name); ++ DbgPrint("novfs_d_lookup 0x%p", entry); ++ if (NULL == entry) { ++ scope = novfs_scope_lookup(); ++ ++ entry = d_alloc(novfs_root, &name); ++ DbgPrint("d_alloc 0x%p", entry); ++ if (entry) { ++ entry->d_op = &novfs_dentry_operations; ++ entry->d_time = jiffies + (novfs_update_timeout * HZ); ++ /* ++ * done in novfs_d_add now... entry->d_fsdata = (void *)novfs_internal_hash( &name ); ++ */ ++ inode = ++ novfs_get_inode(novfs_root->d_sb, S_IFDIR | 0700, 0, novfs_scope_get_uid(scope), 0, &name); ++ DbgPrint("Inode=0x%p", inode); ++ if (inode) { ++ inode->i_atime = ++ inode->i_ctime = ++ inode->i_mtime = CURRENT_TIME; ++ if (!novfs_d_add(novfs_root, entry, inode, 1)) { ++ if (inode->i_private) { ++ struct inode_data *n_inode = inode->i_private; ++ n_inode->Flags = USER_INODE; ++ } ++ PRINT_DENTRY("After novfs_d_add", ++ entry); ++ } else { ++ dput(entry); ++ iput(inode); ++ } ++ } ++ } ++ } else { ++ dput(entry); ++ PRINT_DENTRY("novfs_Add_to_Root: After dput Dentry", entry); ++ } ++ return (0); ++} ++ ++char *novfs_dget_path(struct dentry *Dentry, char *Buf, unsigned int Buflen) ++{ ++ char *retval = &Buf[Buflen]; ++ struct dentry *p = Dentry; ++ ++ *(--retval) = '\0'; ++ Buflen--; ++ ++ if (!IS_ROOT(p) && !IS_ROOT(p->d_parent)) { ++ while (Buflen && !IS_ROOT(p) && !IS_ROOT(p->d_parent)) { ++ if (Buflen > p->d_name.len) { ++ retval -= p->d_name.len; ++ Buflen -= p->d_name.len; ++ memcpy(retval, p->d_name.name, p->d_name.len); ++ *(--retval) = '\\'; ++ Buflen--; ++ p = p->d_parent; ++ } else { ++ retval = NULL; ++ break; ++ } ++ } ++ } else { ++ *(--retval) = '\\'; ++ } ++ ++ if (retval) ++ DbgPrint("%s", retval); ++ return (retval); ++} ++ ++int verify_dentry(struct dentry *dentry, int Flags) ++{ ++ int retVal = -ENOENT; ++ struct inode *dir; ++ struct novfs_entry_info *info = NULL; ++ struct inode_data *id; ++ struct novfs_schandle session; ++ char *path, *list = NULL, *cp; ++ ino_t ino = 0; ++ struct qstr name; ++ int iLock = 0; ++ struct dentry *parent = NULL; ++ u64 ctime; ++ struct inode *inode; ++ ++ if (IS_ROOT(dentry)) { ++ DbgPrint("Root entry"); ++ return (0); ++ } ++ ++ if (dentry && dentry->d_parent && ++ (dir = dentry->d_parent->d_inode) && (id = dir->i_private)) { ++ parent = dget_parent(dentry); ++ ++ info = kmalloc(sizeof(struct novfs_entry_info) + PATH_LENGTH_BUFFER, GFP_KERNEL); ++ ++ if (info) { ++ if (novfs_lock_inode_cache(dir)) { ++ name.len = dentry->d_name.len; ++ name.name = dentry->d_name.name; ++ name.hash = novfs_internal_hash(&name); ++ if (!novfs_get_entry_time(dir, &name, &ino, info, &ctime)) { ++ inode = dentry->d_inode; ++ if (inode && inode->i_private && ++ ((inode->i_size != info->size) || ++ (inode->i_mtime.tv_sec != ++ info->mtime.tv_sec) ++ || (inode->i_mtime.tv_nsec != ++ info->mtime.tv_nsec))) { ++ /* ++ * Values don't match so update. ++ */ ++ struct inode_data *n_inode = inode->i_private; ++ n_inode->Flags |= UPDATE_INODE; ++ } ++ ++ ctime = get_jiffies_64() - ctime; ++ if (Flags || ctime < (u64) (novfs_update_timeout * HZ)) { ++ retVal = 0; ++ novfs_unlock_inode_cache(dir); ++ dput(parent); ++ kfree(info); ++ return (0); ++ } ++ } ++ novfs_unlock_inode_cache(dir); ++ } ++ ++ if (IS_ROOT(dentry->d_parent)) { ++ session = novfs_scope_get_sessionId( ++ novfs_get_scope_from_name(&dentry->d_name)); ++ } else ++ session = novfs_scope_get_sessionId(id->Scope); ++ ++ if (!SC_PRESENT(session)) { ++ id->Scope = novfs_get_scope(dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ ino = 0; ++ retVal = 0; ++ ++ if (IS_ROOT(dentry->d_parent)) { ++ DbgPrint("parent is Root directory"); ++ list = novfs_get_scopeusers(); ++ ++ iLock = novfs_lock_inode_cache(dir); ++ novfs_invalidate_inode_cache(dir); ++ ++ if (list) { ++ cp = list; ++ while (*cp) { ++ name.name = cp; ++ name.len = strlen(cp); ++ name.hash = novfs_internal_hash(&name); ++ cp += (name.len + 1); ++ ino = 0; ++ if (novfs_get_entry(dir, &name, &ino, info)) { ++ info->mode = S_IFDIR | 0700; ++ info->size = 0; ++ info->atime = info->ctime = info->mtime = CURRENT_TIME; ++ ino = (ino_t)atomic_inc_return(&novfs_Inode_Number); ++ novfs_add_inode_entry(dir, &name, ino, info); ++ } ++ } ++ } ++ novfs_free_invalid_entries(dir); ++ } else { ++ ++ path = ++ novfs_dget_path(dentry, info->name, ++ PATH_LENGTH_BUFFER); ++ if (path) { ++ if (dentry->d_name.len <= ++ NW_MAX_PATH_LENGTH) { ++ name.hash = ++ novfs_internal_hash ++ (&dentry->d_name); ++ name.len = dentry->d_name.len; ++ name.name = dentry->d_name.name; ++ ++ retVal = ++ novfs_get_file_info(path, ++ info, ++ session); ++ if (0 == retVal) { ++ dentry->d_time = ++ jiffies + ++ (novfs_update_timeout ++ * HZ); ++ iLock = ++ novfs_lock_inode_cache ++ (dir); ++ if (novfs_update_entry ++ (dir, &name, 0, ++ info)) { ++ if (dentry-> ++ d_inode) { ++ ino = dentry->d_inode->i_ino; ++ } else { ++ ino = (ino_t)atomic_inc_return(&novfs_Inode_Number); ++ } ++ novfs_add_inode_entry ++ (dir, &name, ++ ino, info); ++ } ++ if (dentry->d_inode) { ++ update_inode ++ (dentry-> ++ d_inode, ++ info); ++ id->Flags &= ++ ~UPDATE_INODE; ++ ++ dentry-> ++ d_inode-> ++ i_flags &= ++ ~S_DEAD; ++ if (dentry-> ++ d_inode-> ++ i_private) { ++ ((struct inode_data *) dentry->d_inode->i_private)->Scope = id->Scope; ++ } ++ } ++ } else if (-EINTR != retVal) { ++ retVal = 0; ++ iLock = novfs_lock_inode_cache(dir); ++ novfs_remove_inode_entry(dir, &name, 0); ++ if (dentry->d_inode ++ && !(dentry->d_inode->i_flags & S_DEAD)) { ++ dentry->d_inode->i_flags |= S_DEAD; ++ dentry->d_inode-> i_size = 0; ++ dentry->d_inode->i_atime.tv_sec = ++ dentry->d_inode->i_atime.tv_nsec = ++ dentry->d_inode->i_ctime.tv_sec = ++ dentry->d_inode->i_ctime.tv_nsec = ++ dentry->d_inode->i_mtime.tv_sec = ++ dentry->d_inode->i_mtime.tv_nsec = 0; ++ dentry->d_inode->i_blocks = 0; ++ d_delete(dentry); /* Remove from cache */ ++ } ++ } ++ } else { ++ retVal = -ENAMETOOLONG; ++ } ++ } ++ } ++ } else { ++ retVal = -ENOMEM; ++ } ++ if (iLock) { ++ novfs_unlock_inode_cache(dir); ++ } ++ dput(parent); ++ } ++ ++ if (list) ++ kfree(list); ++ if (info) ++ kfree(info); ++ ++ DbgPrint("return=0x%x", retVal); ++ ++ return (retVal); ++} ++ ++ ++static int novfs_d_add(struct dentry *Parent, struct dentry *d, struct inode *i, int a) ++{ ++ void *scope; ++ struct inode_data *id = NULL; ++ ++ char *path, *buf; ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = novfs_dget_path(d, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ DbgPrint("inode=0x%p ino=%d path %s", i, ++ i->i_ino, path); ++ } ++ kfree(buf); ++ } ++ ++ if (Parent && Parent->d_inode && Parent->d_inode->i_private) { ++ id = (struct inode_data *) Parent->d_inode->i_private; ++ } ++ ++ if (id && id->Scope) { ++ scope = id->Scope; ++ } else { ++ scope = novfs_get_scope(d); ++ } ++ ++ ((struct inode_data *) i->i_private)->Scope = scope; ++ ++ d->d_time = jiffies + (novfs_update_timeout * HZ); ++ if (a) { ++ d_add(d, i); ++ } else { ++ d_instantiate(d, i); ++ } ++ ++ return (0); ++} ++ ++int novfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) ++{ ++ int retCode = 0; ++ struct inode *dir; ++ struct inode_data *id; ++ struct qstr name; ++ ++ __DbgPrint("%s: 0x%p %.*s\n" ++ " d_count: %d\n" ++ " d_inode: 0x%p\n", __func__, ++ dentry, dentry->d_name.len, dentry->d_name.name, ++ dentry->d_count, dentry->d_inode); ++ ++ if (IS_ROOT(dentry)) { ++ retCode = 1; ++ } else { ++ if (dentry->d_inode && ++ dentry->d_parent && ++ (dir = dentry->d_parent->d_inode) && ++ (id = dir->i_private)) { ++ /* ++ * Check timer to see if in valid time limit ++ */ ++ if (jiffies > dentry->d_time) { ++ /* ++ * Revalidate entry ++ */ ++ name.len = dentry->d_name.len; ++ name.name = dentry->d_name.name; ++ name.hash = ++ novfs_internal_hash(&dentry->d_name); ++ dentry->d_time = 0; ++ ++ if (0 == verify_dentry(dentry, 0)) { ++ if (novfs_lock_inode_cache(dir)) { ++ if (novfs_lookup_inode_cache ++ (dir, &name, 0)) { ++ dentry->d_time = ++ jiffies + ++ (novfs_update_timeout ++ * HZ); ++ retCode = 1; ++ } ++ novfs_unlock_inode_cache(dir); ++ } ++ } ++ } else { ++ retCode = 1; ++ } ++ } ++ } ++ ++ if ((0 == retCode) && dentry->d_inode) { ++ /* ++ * Entry has become invalid ++ */ ++/* dput(dentry); ++*/ ++ } ++ ++ DbgPrint("return 0x%x %.*s", retCode, ++ dentry->d_name.len, dentry->d_name.name); ++ ++ return (retCode); ++} ++ ++static unsigned long novfs_internal_hash(struct qstr *name) ++{ ++ unsigned long hash = 0; ++ unsigned int len = name->len; ++ unsigned char *c = (unsigned char *)name->name; ++ ++ while (len--) { ++ /* ++ * Lower case values for the hash. ++ */ ++ hash = partial_name_hash(tolower(*c++), hash); ++ } ++ ++ return (hash); ++} ++ ++int novfs_d_hash(struct dentry *dentry, struct qstr *name) ++{ ++ DbgPrint("%.*s", name->len, name->name); ++ ++ name->hash = novfs_internal_hash(name); ++ ++ return (0); ++} ++ ++int novfs_d_strcmp(struct qstr *s1, struct qstr *s2) ++{ ++ int retCode = 1; ++ unsigned char *str1, *str2; ++ unsigned int len; ++ ++ DbgPrint("s1=%.*s s2=%.*s", s1->len, s1->name, ++ s2->len, s2->name); ++ ++ if (s1->len && (s1->len == s2->len) && (s1->hash == s2->hash)) { ++ len = s1->len; ++ str1 = (unsigned char *)s1->name; ++ str2 = (unsigned char *)s2->name; ++ for (retCode = 0; len--; str1++, str2++) { ++ if (*str1 != *str2) { ++ if (tolower(*str1) != tolower(*str2)) { ++ retCode = 1; ++ break; ++ } ++ } ++ } ++ } ++ ++ DbgPrint("retCode=0x%x", retCode); ++ return (retCode); ++} ++ ++int novfs_d_compare(struct dentry *parent, struct qstr *s1, struct qstr *s2) ++{ ++ int retCode; ++ ++ retCode = novfs_d_strcmp(s1, s2); ++ ++ DbgPrint("retCode=0x%x", retCode); ++ return (retCode); ++} ++ ++int novfs_d_delete(struct dentry *dentry) ++{ ++ int retVal = 0; ++ ++ DbgPrint("0x%p %.*s; d_count: %d; d_inode: 0x%p", ++ dentry, dentry->d_name.len, dentry->d_name.name, ++ dentry->d_count, dentry->d_inode); ++ ++ if (dentry->d_inode && (dentry->d_inode->i_flags & S_DEAD)) { ++ retVal = 1; ++ } ++ ++ dentry->d_time = 0; ++ ++ return (retVal); ++} ++ ++void novfs_d_release(struct dentry *dentry) ++{ ++ DbgPrint("0x%p %.*s", dentry, dentry->d_name.len, ++ dentry->d_name.name); ++} ++ ++void novfs_d_iput(struct dentry *dentry, struct inode *inode) ++{ ++ DbgPrint("Inode=0x%p Ino=%d Dentry=0x%p i_state=%d Name=%.*s", ++ inode, inode->i_ino, dentry, inode->i_state, dentry->d_name.len, ++ dentry->d_name.name); ++ ++ iput(inode); ++ ++} ++ ++int novfs_dir_open(struct inode *dir, struct file *file) ++{ ++ char *path, *buf; ++ struct file_private *file_private = NULL; ++ ++ DbgPrint("Inode 0x%p %d Name %.*s", dir, dir->i_ino, ++ file->f_dentry->d_name.len, file->f_dentry->d_name.name); ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = novfs_dget_path(file->f_dentry, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ DbgPrint("path %s", path); ++ } ++ kfree(buf); ++ } ++ ++ file_private = kmalloc(sizeof(struct file_private), GFP_KERNEL); ++ file_private->listedall = 0; ++ file_private->enumHandle = NULL; ++ ++ file->private_data = file_private; ++ ++ return (0); ++} ++ ++int novfs_dir_release(struct inode *dir, struct file *file) ++{ ++ struct file_private *file_private = file->private_data; ++ struct inode *inode = file->f_dentry->d_inode; ++ struct novfs_schandle sessionId; ++ ++ DbgPrint("Inode 0x%p %d Name %.*s", dir, dir->i_ino, ++ file->f_dentry->d_name.len, file->f_dentry->d_name.name); ++ ++ if (file_private) { ++ if (file_private->enumHandle && (file_private->enumHandle != ((void *)-1))) { ++ sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope); ++ if (SC_PRESENT(sessionId) == 0) { ++ ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(file->f_dentry); ++ sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope); ++ } ++ novfs_end_directory_enumerate(file_private->enumHandle, sessionId); ++ } ++ kfree(file_private); ++ file->private_data = NULL; ++ } ++ ++ return (0); ++} ++ ++loff_t novfs_dir_lseek(struct file * file, loff_t offset, int origin) ++{ ++ struct file_private *file_private = NULL; ++ ++ DbgPrint("offset %lld %d Name %.*s", offset, origin, ++ file->f_dentry->d_name.len, file->f_dentry->d_name.name); ++ //printk("<1> seekdir file = %.*s offset = %i\n", file->f_dentry->d_name.len, file->f_dentry->d_name.name, (int)offset); ++ ++ if (0 != offset) { ++ return -ESPIPE; ++ } ++ ++ file->f_pos = 0; ++ ++ file_private = (struct file_private *) file->private_data; ++ file_private->listedall = 0; ++ if (file_private->enumHandle && (file_private->enumHandle != ((void *)-1))) { ++ struct novfs_schandle sessionId; ++ struct inode *inode = file->f_dentry->d_inode; ++ sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope); ++ if (SC_PRESENT(sessionId) == 0) { ++ ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(file->f_dentry); ++ sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope); ++ } ++ novfs_end_directory_enumerate(file_private->enumHandle, sessionId); ++ } ++ file_private->enumHandle = NULL; ++ ++ return 0; ++ //return(default_llseek(file, offset, origin)); ++} ++ ++ssize_t novfs_dir_read(struct file * file, char *buf, size_t len, loff_t * off) ++{ ++/* ++ int rlen = 0; ++ ++ DbgPrint("dentry path %.*s buf=0x%p len=%d off=%lld", file->f_dentry->d_name.len, file->f_dentry->d_name.name, buf, len, *off); ++ ++ if (0 == *off) ++ { ++ rlen = 8; ++ rlen -= copy_to_user(buf, "Testing\n", 8); ++ *off += rlen; ++ } ++ return(rlen); ++*/ ++ DbgPrint("%lld %d Name %.*s", *off, len, ++ file->f_dentry->d_name.len, file->f_dentry->d_name.name); ++ return (generic_read_dir(file, buf, len, off)); ++} ++ ++static void novfs_Dump_Info(struct novfs_entry_info *info) ++{ ++ char atime_buf[32], mtime_buf[32], ctime_buf[32]; ++ char namebuf[512]; ++ int len = 0; ++ ++ if (info == NULL) { ++ DbgPrint("Dump_Info info == NULL"); ++ return; ++ } ++ ++ if (info->namelength >= 512) { ++ len = 511; ++ } else { ++ len = info->namelength; ++ } ++ ++ memcpy(namebuf, info->name, len); ++ namebuf[len] = '\0'; ++ ++ ctime_r(&info->atime.tv_sec, atime_buf); ++ ctime_r(&info->mtime.tv_sec, mtime_buf); ++ ctime_r(&info->ctime.tv_sec, ctime_buf); ++ DbgPrint("type = %i", info->type); ++ DbgPrint("mode = %x", info->mode); ++ DbgPrint("uid = %d", info->uid); ++ DbgPrint("gid = %d", info->gid); ++ DbgPrint("size = %i", info->size); ++ DbgPrint("atime = %s", atime_buf); ++ DbgPrint("mtime = %s", mtime_buf); ++ DbgPrint("ctime = %s", ctime_buf); ++ DbgPrint("namelength = %i", info->namelength); ++ DbgPrint("name = %s", namebuf); ++} ++ ++void processList(struct file *file, void *dirent, filldir_t filldir, char *list, ++ int type, struct novfs_schandle SessionId) ++{ ++ unsigned char *path, *buf = NULL, *cp; ++ struct qstr name; ++ struct novfs_entry_info *pinfo = NULL; ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ path = buf; ++ if (buf) { ++ path = novfs_dget_path(file->f_dentry, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ strcpy(buf, path); ++ } ++ path = buf + strlen(buf); ++ *path++ = '\\'; ++ } ++ ++ if (list) { ++ cp = list; ++ while (*cp) { ++ name.name = cp; ++ DbgPrint("name.name = %s", name.name); ++ name.len = strlen(cp); ++ name.hash = novfs_internal_hash(&name); ++ cp += (name.len + 1); ++ ++ pinfo = ++ kmalloc(sizeof(struct novfs_entry_info) + ++ PATH_LENGTH_BUFFER, GFP_KERNEL); ++ pinfo->mode = S_IFDIR | 0700; ++ pinfo->size = 0; ++ pinfo->atime = pinfo->ctime = pinfo->mtime = ++ CURRENT_TIME; ++ strcpy(pinfo->name, name.name); ++ pinfo->namelength = name.len; ++ ++ novfs_Dump_Info(pinfo); ++ ++ filldir(dirent, pinfo->name, pinfo->namelength, ++ file->f_pos, file->f_pos, pinfo->mode >> 12); ++ file->f_pos += 1; ++ ++ kfree(pinfo); ++ } ++ } ++ ++ if (buf) { ++ kfree(buf); ++ } ++} ++ ++int processEntries(struct file *file, void *dirent, filldir_t filldir, ++ void ** enumHandle, struct novfs_schandle sessionId) ++{ ++ unsigned char *path = NULL, *buf = NULL; ++ int count = 0, status = 0; ++ struct novfs_entry_info *pinfo = NULL; ++ struct novfs_entry_info *pInfoMem = NULL; ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (!buf) { ++ return -ENOMEM; ++ } ++ ++ path = novfs_dget_path(file->f_dentry, buf, PATH_LENGTH_BUFFER); ++ if (!path) { ++ kfree(buf); ++ return -ENOMEM; ++ } ++ //NWSearchfiles ++ count = 0; ++ status = ++ novfs_get_dir_listex(path, enumHandle, &count, &pinfo, ++ sessionId); ++ pInfoMem = pinfo; ++ ++ if ((count == -1) || (count == 0) || (status != 0)) { ++ kfree(pInfoMem); ++ kfree(buf); ++ return -1; ++ } ++ // parse resultset ++ while (pinfo && count--) { ++ filldir(dirent, pinfo->name, pinfo->namelength, file->f_pos, ++ file->f_pos, pinfo->mode >> 12); ++ file->f_pos += 1; ++ ++ pinfo = (struct novfs_entry_info *) (pinfo->name + pinfo->namelength); ++ } ++ ++ kfree(pInfoMem); ++ kfree(buf); ++ return 0; ++} ++ ++int novfs_dir_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++ unsigned char *list = NULL; ++ int status = 0; //-ENOMEM; ++ struct inode *inode = file->f_dentry->d_inode; ++ struct novfs_schandle sessionId; ++ uid_t uid; ++ int type = 0; ++ struct file_private *file_private = NULL; ++ int lComm; ++ ++ file_private = (struct file_private *) file->private_data; ++ DbgPrint("Name %.*s", file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name); ++ ++ //printk("<1> file = %.*s\n", file->f_dentry->d_name.len, file->f_dentry->d_name.name); ++ ++// Use this hack by default ++#ifndef SKIP_CROSSOVER_HACK ++ // Hack for crossover - begin ++ down(&TimeDir_Lock); ++ if ((file->f_dentry->d_name.len == 7) && ++ ((0 == strncmp(file->f_dentry->d_name.name, " !xover", 7)) || ++ (0 == strncmp(file->f_dentry->d_name.name, "z!xover", 7)))) { ++ //printk("<1> xoverhack: we are in xoverHack\n"); ++ ++ inHAX = 1; ++ inHAXTime = get_nanosecond_time(); ++ //up( &TimeDir_Lock ); ++ //return 0; ++ file_private->listedall = 1; ++ } else { ++ if (inHAX) { ++ if (get_nanosecond_time() - inHAXTime > ++ 100 * 1000 * 1000) { ++ //printk("<1> xoverhack: it was long, long, long ago...\n"); ++ inHAX = 0; ++ } else { ++ //printk("<1> xoverhack: word gotcha in xoverHack...\n"); ++ inHAXTime = get_nanosecond_time(); ++ //up( &TimeDir_Lock ); ++ //return 0; ++ file_private->listedall = 1; ++ } ++ } ++ } ++ ++ up(&TimeDir_Lock); ++ // Hack for crossover - end ++#endif ++ ++ if (file->f_pos == 0) { ++ if (filldir(dirent, ".", 1, file->f_pos, inode->i_ino, DT_DIR) < ++ 0) ++ return 1; ++ file->f_pos++; ++ return 1; ++ } ++ ++ if (file->f_pos == 1) { ++ if (filldir ++ (dirent, "..", 2, file->f_pos, ++ file->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) ++ return 1; ++ file->f_pos++; ++ return 1; ++ } ++ ++ if (file_private->listedall != 0) { ++ return 0; ++ } ++ ++ inode = file->f_dentry->d_inode; ++ if (inode && inode->i_private) { ++ sessionId = ++ novfs_scope_get_sessionId(((struct inode_data *) inode->i_private)-> ++ Scope); ++ if (0 == SC_PRESENT(sessionId)) { ++ ((struct inode_data *) inode->i_private)->Scope = ++ novfs_get_scope(file->f_dentry); ++ sessionId = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ } ++ uid = novfs_scope_get_uid(((struct inode_data *) inode->i_private)->Scope); ++ } else { ++ SC_INITIALIZE(sessionId); ++ uid = current_euid(); ++ } ++ ++ if (IS_ROOT(file->f_dentry) || // Root ++ IS_ROOT(file->f_dentry->d_parent) || // User ++ IS_ROOT(file->f_dentry->d_parent->d_parent)) // Server ++ { ++ if (IS_ROOT(file->f_dentry)) { ++ DbgPrint("Root directory"); ++ list = novfs_get_scopeusers(); ++ type = USER_LIST; ++ } else if (IS_ROOT(file->f_dentry->d_parent)) { ++ DbgPrint("Parent is Root directory"); ++ novfs_get_servers(&list, sessionId); ++ type = SERVER_LIST; ++ } else { ++ DbgPrint("Parent-Parent is Root directory"); ++ novfs_get_vols(&file->f_dentry->d_name, ++ &list, sessionId); ++ type = VOLUME_LIST; ++ } ++ ++ processList(file, dirent, filldir, list, type, sessionId); ++ file_private->listedall = 1; ++ } else { ++ status = ++ processEntries(file, dirent, filldir, ++ &file_private->enumHandle, sessionId); ++ ++ if (status != 0) { ++ file_private->listedall = 1; ++#ifndef SKIP_CROSSOVER_HACK ++ // Hack for crossover part 2 - begin ++ lComm = strlen(current->comm); ++ if ((lComm > 4) ++ && (0 == ++ strcmp(current->comm + lComm - 4, ".EXE"))) { ++ if (filldir ++ (dirent, " !xover", 7, file->f_pos, ++ inode->i_ino, DT_DIR) < 0) ++ return 1; ++ if (filldir ++ (dirent, "z!xover", 7, file->f_pos, ++ inode->i_ino, DT_DIR) < 0) ++ return 1; ++ file->f_pos += 2; ++ } ++ // Hack for crossover part2 - end ++#endif ++ } ++ } ++ ++ file->private_data = file_private; ++ return 1; ++} ++ ++int novfs_dir_fsync(struct file *file, struct dentry *dentry, int datasync) ++{ ++ DbgPrint("Name %.*s", file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name); ++ return (simple_sync_file(file, dentry, datasync)); ++} ++ ++ssize_t novfs_f_read(struct file * file, char *buf, size_t len, loff_t * off) ++{ ++ size_t thisread, totalread = 0; ++ loff_t offset = *off; ++ struct inode *inode; ++ struct novfs_schandle session; ++ struct inode_data *id; ++ ++ if (file->f_dentry && ++ (inode = file->f_dentry->d_inode) && ++ (id = (struct inode_data *) inode->i_private)) { ++ ++ DbgPrint("(0x%p 0x%p %d %lld %.*s)", ++ file->private_data, ++ buf, len, offset, ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name); ++ ++ if (novfs_page_cache && !(file->f_flags & O_DIRECT) && id->CacheFlag) { ++ totalread = do_sync_read(file, buf, len, off); ++ } else { ++ session = novfs_scope_get_sessionId(id->Scope); ++ if (0 == SC_PRESENT(session)) { ++ id->Scope = ++ novfs_get_scope(file->f_dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ while (len > 0 && (offset < i_size_read(inode))) { ++ int retval; ++ thisread = len; ++ retval = ++ novfs_read_file(file->private_data, buf, ++ &thisread, &offset, ++ session); ++ if (retval || !thisread) { ++ if (retval) { ++ totalread = retval; ++ } ++ break; ++ } ++ DbgPrint("thisread = 0x%x", thisread); ++ len -= thisread; ++ buf += thisread; ++ offset += thisread; ++ totalread += thisread; ++ } ++ *off = offset; ++ } ++ } ++ DbgPrint("return = %d", totalread); ++ ++ return (totalread); ++} ++ ++ssize_t novfs_f_write(struct file * file, const char *buf, size_t len, ++ loff_t * off) ++{ ++ ssize_t thiswrite, totalwrite = 0; ++ loff_t offset = *off; ++ struct novfs_schandle session; ++ struct inode *inode; ++ int status; ++ struct inode_data *id; ++ ++ if (file->f_dentry && ++ (inode = file->f_dentry->d_inode) && ++ (id = file->f_dentry->d_inode->i_private)) { ++ DbgPrint("(0x%p 0x%p 0x%p %d %lld %.*s)", ++ file->private_data, inode, id->FileHandle, len, offset, ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name); ++ ++ if (novfs_page_cache && ++ !(file->f_flags & O_DIRECT) && ++ id->CacheFlag && !(file->f_flags & O_WRONLY)) { ++ totalwrite = do_sync_write(file, buf, len, off); ++ } else { ++ if (file->f_flags & O_APPEND) { ++ offset = i_size_read(inode); ++ DbgPrint("appending to end %lld %.*s", ++ offset, file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name); ++ } ++ ++ session = novfs_scope_get_sessionId(id->Scope); ++ if (0 == SC_PRESENT(session)) { ++ id->Scope = ++ novfs_get_scope(file->f_dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ while (len > 0) { ++ thiswrite = len; ++ if ((status = ++ novfs_write_file(file->private_data, ++ (unsigned char *)buf, ++ &thiswrite, &offset, ++ session)) || !thiswrite) { ++ totalwrite = status; ++ break; ++ } ++ DbgPrint("thiswrite = 0x%x", ++ thiswrite); ++ len -= thiswrite; ++ buf += thiswrite; ++ offset += thiswrite; ++ totalwrite += thiswrite; ++ if (offset > i_size_read(inode)) { ++ i_size_write(inode, offset); ++ inode->i_blocks = ++ (offset + inode->i_sb->s_blocksize - ++ 1) >> inode->i_blkbits; ++ } ++ inode->i_mtime = inode->i_atime = CURRENT_TIME; ++ id->Flags |= UPDATE_INODE; ++ ++ } ++ *off = offset; ++ } ++ } ++ DbgPrint("return = 0x%x", totalwrite); ++ ++ return (totalwrite); ++} ++ ++int novfs_f_readdir(struct file *file, void *data, filldir_t fill) ++{ ++ return -EISDIR; ++} ++ ++int novfs_f_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ DbgPrint("file=0x%p cmd=0x%x arg=0x%p", file, cmd, arg); ++ ++ return -ENOSYS; ++} ++ ++int novfs_f_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int retCode = -EINVAL; ++ ++ DbgPrint("file=0x%p %.*s", file, file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name); ++ ++ retCode = generic_file_mmap(file, vma); ++ ++ DbgPrint("retCode=0x%x", retCode); ++ return (retCode); ++} ++ ++int novfs_f_open(struct inode *inode, struct file *file) ++{ ++ struct novfs_entry_info *info = NULL; ++ int retCode = -ENOENT; ++ struct novfs_schandle session; ++ char *path; ++ struct dentry *parent; ++ ino_t ino; ++ struct inode_data *id; ++ int errInfo; ++ ++ DbgPrint("inode=0x%p file=0x%p dentry=0x%p dentry->d_inode=0x%p %.*s", ++ inode, file, file->f_dentry, file->f_dentry->d_inode, ++ file->f_dentry->d_name.len, file->f_dentry->d_name.name); ++ if (file->f_dentry) { ++ DbgPrint("%.*s f_flags=0%o f_mode=0%o i_mode=0%o", ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name, ++ file->f_flags, file->f_mode, inode->i_mode); ++ } ++ ++ if (inode && inode->i_private) { ++ id = (struct inode_data *) file->f_dentry->d_inode->i_private; ++ session = novfs_scope_get_sessionId(id->Scope); ++ if (0 == SC_PRESENT(session)) { ++ id->Scope = novfs_get_scope(file->f_dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ info = kmalloc(sizeof(struct novfs_entry_info) + ++ PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (info) { ++ path = ++ novfs_dget_path(file->f_dentry, info->name, ++ PATH_LENGTH_BUFFER); ++ if (path) { ++ if (file->f_flags & O_TRUNC) { ++ errInfo = ++ novfs_get_file_info(path, info, ++ session); ++ ++ if (errInfo || info->size == 0) { ++ // clear O_TRUNC flag, bug #275366 ++ file->f_flags = ++ file->f_flags & (~O_TRUNC); ++ } ++ } ++ ++ DbgPrint("%s", path); ++ retCode = novfs_open_file(path, ++ file-> ++ f_flags & ~O_EXCL, ++ info, ++ &file->private_data, ++ session); ++ ++ DbgPrint("0x%x 0x%p", retCode, ++ file->private_data); ++ if (!retCode) { ++ /* ++ *update_inode(inode, &info); ++ */ ++ //id->FileHandle = file->private_data; ++ id->CacheFlag = ++ novfs_get_file_cache_flag(path, ++ session); ++ ++ if (!novfs_get_file_info ++ (path, info, session)) { ++ update_inode(inode, info); ++ } ++ ++ parent = dget_parent(file->f_dentry); ++ ++ if (parent && parent->d_inode) { ++ struct inode *dir = ++ parent->d_inode; ++ novfs_lock_inode_cache(dir); ++ ino = 0; ++ if (novfs_get_entry ++ (dir, ++ &file->f_dentry->d_name, ++ &ino, info)) { ++ ((struct inode_data *) inode-> ++ i_private)->Flags |= ++ UPDATE_INODE; ++ } ++ ++ novfs_unlock_inode_cache(dir); ++ } ++ dput(parent); ++ } ++ } ++ kfree(info); ++ } ++ } ++ DbgPrint("retCode=0x%x", retCode); ++ return (retCode); ++} ++ ++int novfs_flush_mapping(void *Handle, struct address_space *mapping, ++ struct novfs_schandle Session) ++{ ++ struct pagevec pagevec; ++ unsigned nrpages; ++ pgoff_t index = 0; ++ int done, rc = 0; ++ ++ pagevec_init(&pagevec, 0); ++ ++ do { ++ done = 1; ++ nrpages = pagevec_lookup_tag(&pagevec, ++ mapping, ++ &index, ++ PAGECACHE_TAG_DIRTY, PAGEVEC_SIZE); ++ ++ if (nrpages) { ++ struct page *page; ++ int i; ++ ++ DbgPrint("%u", nrpages); ++ ++ done = 0; ++ for (i = 0; !rc && (i < nrpages); i++) { ++ page = pagevec.pages[i]; ++ ++ DbgPrint("page 0x%p %lu", page, page->index); ++ ++ lock_page(page); ++ page_cache_get(page); ++ if (page->mapping == mapping) { ++ if (clear_page_dirty_for_io(page)) { ++ rc = novfs_write_page(Handle, ++ page, ++ Session); ++ if (!rc) { ++ //ClearPageDirty(page); ++ radix_tree_tag_clear ++ (&mapping-> ++ page_tree, ++ page_index(page), ++ PAGECACHE_TAG_DIRTY); ++ } ++ } ++ } ++ ++ page_cache_release(page); ++ unlock_page(page); ++ } ++ pagevec_release(&pagevec); ++ } ++ } while (!rc && !done); ++ ++ DbgPrint("return %d", rc); ++ ++ return (rc); ++} ++ ++int novfs_f_flush(struct file *file, fl_owner_t ownid) ++{ ++ ++ int rc = 0; ++#ifdef FLUSH ++ struct inode *inode; ++ struct novfs_schandle session; ++ struct inode_data *id; ++ ++ DbgPrint("Called from 0x%p", __builtin_return_address(0)); ++ if (file->f_dentry && (inode = file->f_dentry->d_inode) ++ && (id = file->f_dentry->d_inode->i_private)) { ++ ++ if ((file->f_flags & O_ACCMODE) != O_RDONLY) { ++ inode = file->f_dentry->d_inode; ++ DbgPrint("%.*s f_flags=0%o f_mode=0%o i_mode=0%o", ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name, file->f_flags, ++ file->f_mode, inode->i_mode); ++ ++ session = novfs_scope_get_sessionId(id->Scope); ++ if (0 == SC_PRESENT(session)) { ++ id->Scope = ++ novfs_get_scope(file->f_dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ if (inode && ++ inode->i_mapping && inode->i_mapping->nrpages) { ++ ++ DbgPrint("%.*s pages=%lu", ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name, ++ inode->i_mapping->nrpages); ++ ++ if (file->f_dentry && ++ file->f_dentry->d_inode && ++ file->f_dentry->d_inode->i_mapping && ++ file->f_dentry->d_inode->i_mapping->a_ops && ++ file->f_dentry->d_inode->i_mapping->a_ops-> ++ writepage) { ++ rc = filemap_fdatawrite(file->f_dentry-> ++ d_inode-> ++ i_mapping); ++ } else { ++ rc = novfs_flush_mapping(file-> ++ private_data, ++ file-> ++ f_dentry-> ++ d_inode-> ++ i_mapping, ++ session); ++ } ++ } ++ } ++ } ++#endif ++ return (rc); ++} ++ ++int novfs_f_release(struct inode *inode, struct file *file) ++{ ++ int retCode = -EACCES; ++ struct novfs_schandle session; ++ struct inode_data *id; ++ ++ DbgPrint("path=%.*s handle=%p", ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name, file->private_data); ++ ++ if (inode && (id = inode->i_private)) { ++ session = novfs_scope_get_sessionId(id->Scope); ++ if (0 == SC_PRESENT(session)) { ++ id->Scope = novfs_get_scope(file->f_dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ if ((file->f_flags & O_ACCMODE) != O_RDONLY) { ++ DbgPrint("%.*s f_flags=0%o f_mode=0%o i_mode=0%o", ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name, file->f_flags, ++ file->f_mode, inode->i_mode); ++ ++ if (inode->i_mapping && inode->i_mapping->nrpages) { ++ ++ DbgPrint("%.*s pages=%lu", ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name, ++ inode->i_mapping->nrpages); ++ ++ if (inode->i_mapping->a_ops && ++ inode->i_mapping->a_ops->writepage) { ++ filemap_fdatawrite(file->f_dentry-> ++ d_inode->i_mapping); ++ } else { ++ novfs_flush_mapping(file->private_data, ++ file->f_dentry-> ++ d_inode->i_mapping, ++ session); ++ } ++ } ++ } ++ ++ if (file->f_dentry && file->f_dentry->d_inode) { ++ invalidate_remote_inode(file->f_dentry->d_inode); ++ } ++ ++ retCode = novfs_close_file(file->private_data, session); ++ //id->FileHandle = 0; ++ } ++ return (retCode); ++} ++ ++int novfs_f_fsync(struct file *file, struct dentry *dentry, int datasync) ++{ ++ return 0; ++} ++ ++int novfs_f_llseek(struct file *file, loff_t offset, int origin) ++{ ++ DbgPrint("File=0x%p Name=%.*s offset=%lld origin=%d", ++ file, file->f_dentry->d_name.len, file->f_dentry->d_name.name, ++ offset, origin); ++ return (generic_file_llseek(file, offset, origin)); ++} ++ ++/*++======================================================================*/ ++int novfs_f_lock(struct file *file, int cmd, struct file_lock *lock) ++/* ++ * Arguments: ++ * "file" - pointer to file structure - contains file handle in "file->private_data" ++ * ++ * "cmd" could be F_SETLK, F_SETLKW, F_GETLK ++ * F_SETLK/F_SETLKW are for setting/unsetting file lock ++ * F_GETLK is for getting infomation about region - is it locked, or not ++ * ++ * "lock" structure - contains "start" and "end" of locking region ++ * ++ * Returns: ++ * 0 on success ++ * -ENOSYS on F_GETLK cmd. It's not implemented. ++ * -EINVAL if (lock->fl_start > lock->fl_end) ++ * -EAGAIN on all other errors ++ * Abstract: ++ * ++ * Notes: ++ * "lock->fl_start" and "lock->fl_end" are of type "long long", ++ * but xtier functions in novfsd "NCFsdLockFile" and "NCFsdUnlockFile" ++ * receive arguments in u64 type. ++ * ++ * ++ *========================================================================*/ ++{ ++ int err_code; ++ ++ struct inode *inode; ++ struct novfs_schandle session; ++ struct inode_data *id; ++ loff_t len; ++ ++ DbgPrint("(0x%p): begin in novfs_f_lock 0x%p", ++ __builtin_return_address(0), file->private_data); ++ DbgPrint("cmd = %d, F_GETLK = %d, F_SETLK = %d, F_SETLKW = %d", ++ cmd, F_GETLK, F_SETLK, F_SETLKW); ++ DbgPrint("lock->fl_start = 0x%llX, lock->fl_end = 0x%llX", ++ lock->fl_start, lock->fl_end); ++ ++ err_code = -1; ++ if (lock->fl_start <= lock->fl_end) { ++ /* Get len from "start" and "end" */ ++ len = lock->fl_end - lock->fl_start + 1; ++ if ((0 == lock->fl_start) && (OFFSET_MAX == lock->fl_end)) { ++ len = 0; ++ } ++ ++ if (file->f_dentry && ++ (inode = file->f_dentry->d_inode) && ++ (id = (struct inode_data *) inode->i_private)) { ++ DbgPrint("(0x%p 0x%p %.*s)", ++ file->private_data, inode, ++ file->f_dentry->d_name.len, ++ file->f_dentry->d_name.name); ++ ++ session = novfs_scope_get_sessionId(id->Scope); ++ if (0 == SC_PRESENT(session)) { ++ id->Scope = ++ novfs_get_scope(file->f_dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ /* fl_type = F_RDLCK, F_WRLCK, F_UNLCK */ ++ switch (cmd) { ++ case F_SETLK: ++#ifdef F_GETLK64 ++ case F_SETLK64: ++#endif ++ ++ err_code = ++ novfs_set_file_lock(session, ++ file->private_data, ++ lock->fl_type, ++ lock->fl_start, len); ++ break; ++ ++ case F_SETLKW: ++#ifdef F_GETLK64 ++ case F_SETLKW64: ++#endif ++ err_code = ++ novfs_set_file_lock(session, ++ file->private_data, ++ lock->fl_type, ++ lock->fl_start, len); ++ break; ++ ++ case F_GETLK: ++#ifdef F_GETLK64 ++ case F_GETLK64: ++#endif ++ err_code = -ENOSYS; ++ /* ++ * Not implemented. We doesn't have appropriate xtier function. ++ * */ ++ break; ++ ++ default: ++ printk ++ ("<1> novfs in novfs_f_lock, not implemented cmd = %d\n", ++ cmd); ++ DbgPrint("novfs in novfs_f_lock, not implemented cmd = %d", ++ cmd); ++ break; ++ } ++ } ++ ++ DbgPrint("lock->fl_type = %u, err_code 0x%X", ++ lock->fl_type, err_code); ++ ++ if ((err_code != 0) && (err_code != -1) ++ && (err_code != -ENOSYS)) { ++ err_code = -EAGAIN; ++ } ++ } else { ++ err_code = -EINVAL; ++ } ++ ++ return (err_code); ++} ++ ++/*++======================================================================*/ ++static void novfs_copy_cache_pages(struct address_space *mapping, ++ struct list_head *pages, int bytes_read, ++ char *data, struct pagevec *plru_pvec) ++{ ++ struct page *page; ++ char *target; ++ ++ while (bytes_read > 0) { ++ if (list_empty(pages)) ++ break; ++ ++ page = list_entry(pages->prev, struct page, lru); ++ list_del(&page->lru); ++ ++ if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) { ++ page_cache_release(page); ++ data += PAGE_CACHE_SIZE; ++ bytes_read -= PAGE_CACHE_SIZE; ++ continue; ++ } ++ ++ target = kmap_atomic(page, KM_USER0); ++ ++ if (PAGE_CACHE_SIZE > bytes_read) { ++ memcpy(target, data, bytes_read); ++ /* zero the tail end of this partial page */ ++ memset(target + bytes_read, 0, ++ PAGE_CACHE_SIZE - bytes_read); ++ bytes_read = 0; ++ } else { ++ memcpy(target, data, PAGE_CACHE_SIZE); ++ bytes_read -= PAGE_CACHE_SIZE; ++ } ++ kunmap_atomic(target, KM_USER0); ++ ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ if (!pagevec_add(plru_pvec, page)) ++ __pagevec_lru_add_file(plru_pvec); ++ data += PAGE_CACHE_SIZE; ++ } ++ return; ++} ++ ++int novfs_a_writepage(struct page *page, struct writeback_control *wbc) ++{ ++ int retCode = -EFAULT; ++ struct inode *inode = page->mapping->host; ++ struct inode_data *id = inode->i_private; ++ loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT); ++ struct novfs_schandle session; ++ struct novfs_data_list dlst[2]; ++ size_t len = PAGE_CACHE_SIZE; ++ ++ session = novfs_scope_get_sessionId(((struct inode_data *) inode->i_private)->Scope); ++ ++ page_cache_get(page); ++ ++ pos = ((loff_t) page->index << PAGE_CACHE_SHIFT); ++ ++ /* ++ * Leave first dlst entry for reply header. ++ */ ++ dlst[1].page = page; ++ dlst[1].offset = NULL; ++ dlst[1].len = len; ++ dlst[1].rwflag = DLREAD; ++ ++ /* ++ * Check size so we don't write pass end of file. ++ */ ++ if ((pos + (loff_t) len) > i_size_read(inode)) { ++ len = (size_t) (i_size_read(inode) - pos); ++ } ++ ++ retCode = novfs_write_pages(id->FileHandle, dlst, 2, len, pos, session); ++ if (!retCode) { ++ SetPageUptodate(page); ++ } ++ ++ unlock_page(page); ++ page_cache_release(page); ++ ++ return (retCode); ++} ++ ++int novfs_a_writepages(struct address_space *mapping, ++ struct writeback_control *wbc) ++{ ++ int retCode = 0; ++ struct inode *inode = mapping->host; ++ struct novfs_schandle session; ++ void *fh = NULL; ++ struct inode_data *id = NULL; ++ ++ int max_page_lookup = novfs_max_iosize / PAGE_CACHE_SIZE; ++ ++ struct novfs_data_list *dlist, *dlptr; ++ struct page **pages; ++ ++ int dlist_idx, i = 0; ++ pgoff_t index, next_index = 0; ++ loff_t pos = 0; ++ size_t tsize; ++ ++ SC_INITIALIZE(session); ++ DbgPrint("inode=0x%p mapping=0x%p wbc=0x%p nr_to_write=%d", ++ inode, mapping, wbc, wbc->nr_to_write); ++ ++ if (inode) { ++ DbgPrint("Inode=0x%p Ino=%d Id=0x%p", inode, inode->i_ino, ++ inode->i_private); ++ ++ if (NULL != (id = inode->i_private)) { ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ fh = ((struct inode_data *) inode->i_private)->FileHandle; ++ } ++ } ++ ++ dlist = kmalloc(sizeof(struct novfs_data_list) * max_page_lookup, GFP_KERNEL); ++ pages = ++ kmalloc(sizeof(struct page *) * max_page_lookup, GFP_KERNEL); ++ ++ if (id) ++ DbgPrint("inode=0x%p fh=0x%p dlist=0x%p pages=0x%p %s", ++ inode, fh, dlist, pages, id->Name); ++ else ++ DbgPrint("inode=0x%p fh=0x%p dlist=0x%p pages=0x%p", ++ inode, fh, dlist, pages); ++ ++ if (dlist && pages) { ++ struct backing_dev_info *bdi = mapping->backing_dev_info; ++ int done = 0; ++ int nr_pages = 0; ++ int scanned = 0; ++ ++ if (wbc->nonblocking && bdi_write_congested(bdi)) { ++ wbc->encountered_congestion = 1; ++ return 0; ++ } ++ ++ if (wbc->sync_mode == WB_SYNC_NONE) { ++ index = mapping->writeback_index; /* Start from prev offset */ ++ } else { ++ index = 0; /* whole-file sweep */ ++ scanned = 1; ++ } ++ ++ next_index = index; ++ ++ while (!done && (wbc->nr_to_write > 0)) { ++ dlist_idx = 0; ++ dlptr = &dlist[1]; ++ ++ DbgPrint("nr_pages=%d", nr_pages); ++ if (!nr_pages) { ++ memset(pages, 0, ++ sizeof(struct page *) * max_page_lookup); ++ ++ spin_lock_irq(&mapping->tree_lock); ++ ++ /* ++ * Need to ask for one less then max_page_lookup or we ++ * will overflow the request buffer. This also frees ++ * the first entry for the reply buffer. ++ */ ++ nr_pages = ++ radix_tree_gang_lookup_tag(&mapping-> ++ page_tree, ++ (void **)pages, ++ index, ++ max_page_lookup - ++ 1, ++ PAGECACHE_TAG_DIRTY); ++ ++ DbgPrint("2; nr_pages=%d\n", nr_pages); ++ /* ++ * Check to see if there are dirty pages and there is a valid ++ * file handle. ++ */ ++ if (nr_pages && !fh) { ++ set_bit(AS_EIO, &mapping->flags); ++ done = 1; ++ DbgPrint("set_bit AS_EIO"); ++ break; ++ } ++ ++ for (i = 0; i < nr_pages; i++) { ++ page_cache_get(pages[i]); ++ } ++ ++ spin_unlock_irq(&mapping->tree_lock); ++ ++ if (nr_pages) { ++ index = pages[nr_pages - 1]->index + 1; ++ pos = ++ (loff_t) pages[0]-> ++ index << PAGE_CACHE_SHIFT; ++ } ++ ++ if (!nr_pages) { ++ if (scanned) { ++ index = 0; ++ scanned = 0; ++ continue; ++ } ++ done = 1; ++ } else { ++ next_index = pages[0]->index; ++ i = 0; ++ } ++ } else { ++ if (pages[i]) { ++ pos = ++ (loff_t) pages[i]-> ++ index << PAGE_CACHE_SHIFT; ++ } ++ } ++ ++ for (; i < nr_pages; i++) { ++ struct page *page = pages[i]; ++ ++ /* ++ * At this point we hold neither mapping->tree_lock nor ++ * lock on the page itself: the page may be truncated or ++ * invalidated (changing page->mapping to NULL), or even ++ * swizzled back from swapper_space to tmpfs file ++ * mapping ++ */ ++ ++ DbgPrint ++ ("novfs_a_writepages: pos=0x%llx index=%d page->index=%d next_index=%d\n", ++ pos, index, page->index, next_index); ++ ++ if (page->index != next_index) { ++ next_index = page->index; ++ break; ++ } ++ next_index = page->index + 1; ++ ++ lock_page(page); ++ ++ if (wbc->sync_mode != WB_SYNC_NONE) ++ wait_on_page_writeback(page); ++ ++ if (page->mapping != mapping ++ || PageWriteback(page) ++ || !clear_page_dirty_for_io(page)) { ++ unlock_page(page); ++ continue; ++ } ++ ++ dlptr[dlist_idx].page = page; ++ dlptr[dlist_idx].offset = NULL; ++ dlptr[dlist_idx].len = PAGE_CACHE_SIZE; ++ dlptr[dlist_idx].rwflag = DLREAD; ++ dlist_idx++; ++ DbgPrint("Add page=0x%p index=0x%lx", ++ page, page->index); ++ } ++ ++ DbgPrint("dlist_idx=%d", dlist_idx); ++ if (dlist_idx) { ++ tsize = dlist_idx * PAGE_CACHE_SIZE; ++ /* ++ * Check size so we don't write pass end of file. ++ */ ++ if ((pos + tsize) > i_size_read(inode)) { ++ tsize = ++ (size_t) (i_size_read(inode) - pos); ++ } ++ ++ retCode = ++ novfs_write_pages(fh, dlist, dlist_idx + 1, ++ tsize, pos, session); ++ switch (retCode) { ++ case 0: ++ wbc->nr_to_write -= dlist_idx; ++ break; ++ ++ case -ENOSPC: ++ set_bit(AS_ENOSPC, &mapping->flags); ++ done = 1; ++ break; ++ ++ default: ++ set_bit(AS_EIO, &mapping->flags); ++ done = 1; ++ break; ++ } ++ ++ do { ++ unlock_page((struct page *) ++ dlptr[dlist_idx - 1].page); ++ page_cache_release((struct page *) ++ dlptr[dlist_idx - ++ 1].page); ++ DbgPrint("release page=0x%p index=0x%lx", ++ dlptr[dlist_idx - 1].page, ++ ((struct page *) ++ dlptr[dlist_idx - ++ 1].page)->index); ++ if (!retCode) { ++ wbc->nr_to_write--; ++ } ++ } while (--dlist_idx); ++ } ++ ++ if (i >= nr_pages) { ++ nr_pages = 0; ++ } ++ } ++ ++ mapping->writeback_index = index; ++ ++ } else { ++ DbgPrint("set_bit AS_EIO"); ++ set_bit(AS_EIO, &mapping->flags); ++ } ++ if (dlist) ++ kfree(dlist); ++ if (pages) ++ kfree(pages); ++ ++ DbgPrint("retCode=%d", retCode); ++ return (0); ++ ++} ++ ++int novfs_a_readpage(struct file *file, struct page *page) ++{ ++ int retCode = 0; ++ void *pbuf; ++ struct inode *inode = NULL; ++ struct dentry *dentry = NULL; ++ loff_t offset; ++ size_t len; ++ struct novfs_schandle session; ++ ++ SC_INITIALIZE(session); ++ DbgPrint("File=0x%p Name=%.*s Page=0x%p", file, ++ file->f_dentry->d_name.len, file->f_dentry->d_name.name, page); ++ ++ dentry = file->f_dentry; ++ ++ if (dentry) { ++ DbgPrint("Dentry=0x%p Name=%.*s", dentry, dentry->d_name.len, ++ dentry->d_name.name); ++ if (dentry->d_inode) { ++ inode = dentry->d_inode; ++ } ++ } ++ ++ if (inode) { ++ DbgPrint("Inode=0x%p Ino=%d", inode, inode->i_ino); ++ ++ if (inode->i_private) { ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ if (0 == SC_PRESENT(session)) { ++ ((struct inode_data *) inode->i_private)->Scope = ++ novfs_get_scope(file->f_dentry); ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ } ++ } ++ } ++ ++ if (!PageUptodate(page)) { ++ struct novfs_data_list dlst[2]; ++ ++ offset = page->index << PAGE_CACHE_SHIFT; ++ len = PAGE_CACHE_SIZE; ++ ++ /* ++ * Save the first entry for the reply header. ++ */ ++ dlst[1].page = page; ++ dlst[1].offset = NULL; ++ dlst[1].len = PAGE_CACHE_SIZE; ++ dlst[1].rwflag = DLWRITE; ++ ++ DbgPrint("calling= novfs_Read_Pages %lld", ++ offset); ++ retCode = ++ novfs_read_pages(file->private_data, dlst, 2, &len, &offset, ++ session); ++ if (len && (len < PAGE_CACHE_SIZE)) { ++ pbuf = kmap_atomic(page, KM_USER0); ++ memset(&((char *)pbuf)[len], 0, PAGE_CACHE_SIZE - len); ++ kunmap_atomic(pbuf, KM_USER0); ++ } ++ ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ } ++ unlock_page(page); ++ ++ DbgPrint("retCode=%d", retCode); ++ return (retCode); ++ ++} ++ ++int novfs_a_readpages(struct file *file, struct address_space *mapping, ++ struct list_head *page_lst, unsigned nr_pages) ++{ ++ int retCode = 0; ++ struct inode *inode = NULL; ++ struct dentry *dentry = NULL; ++ struct novfs_schandle session; ++ loff_t offset; ++ size_t len; ++ ++ unsigned page_idx; ++ struct pagevec lru_pvec; ++ pgoff_t next_index; ++ ++ char *rbuf, done = 0; ++ SC_INITIALIZE(session); ++ ++ DbgPrint("File=0x%p Name=%.*s Pages=%d", file, ++ file->f_dentry->d_name.len, file->f_dentry->d_name.name, ++ nr_pages); ++ ++ dentry = file->f_dentry; ++ ++ if (dentry) { ++ DbgPrint("Dentry=0x%p Name=%.*s", dentry, dentry->d_name.len, ++ dentry->d_name.name); ++ if (dentry->d_inode) { ++ inode = dentry->d_inode; ++ } ++ } ++ ++ if (inode) { ++ DbgPrint("Inode=0x%p Ino=%d", inode, inode->i_ino); ++ ++ if (inode->i_private) { ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ if (0 == SC_PRESENT(session)) { ++ ((struct inode_data *) inode->i_private)->Scope = ++ novfs_get_scope(file->f_dentry); ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ } ++ } ++ } ++ ++ rbuf = kmalloc(novfs_max_iosize, GFP_KERNEL); ++ if (rbuf) { ++ pagevec_init(&lru_pvec, 0); ++ for (page_idx = 0; page_idx < nr_pages && !done;) { ++ struct page *page, *tpage; ++ ++ if (list_empty(page_lst)) ++ break; ++ ++ page = list_entry(page_lst->prev, struct page, lru); ++ ++ next_index = page->index; ++ offset = (loff_t) page->index << PAGE_CACHE_SHIFT; ++ len = 0; ++ ++ /* ++ * Count number of contiguous pages. ++ */ ++ list_for_each_entry_reverse(tpage, page_lst, lru) { ++ if ((next_index != tpage->index) || ++ (len >= novfs_max_iosize - PAGE_SIZE)) { ++ break; ++ } ++ len += PAGE_SIZE; ++ next_index++; ++ } ++ ++ if (len && !done) { ++ struct novfs_data_list dllst[2]; ++ ++ dllst[1].page = NULL; ++ dllst[1].offset = rbuf; ++ dllst[1].len = len; ++ dllst[1].rwflag = DLWRITE; ++ ++ DbgPrint("calling novfs_Read_Pages %lld", ++ offset); ++ if (!novfs_read_pages ++ (file->private_data, dllst, 2, &len, ++ &offset, session)) { ++ novfs_copy_cache_pages(mapping, ++ page_lst, len, ++ rbuf, &lru_pvec); ++ page_idx += len >> PAGE_CACHE_SHIFT; ++ if ((int)(len & PAGE_CACHE_MASK) != len) { ++ page_idx++; ++ } ++ if (len == 0) { ++ done = 1; ++ } ++ } else { ++ done = 1; ++ } ++ } ++ } ++ ++ /* ++ * Free any remaining pages. ++ */ ++ while (!list_empty(page_lst)) { ++ struct page *page = ++ list_entry(page_lst->prev, struct page, lru); ++ ++ list_del(&page->lru); ++ page_cache_release(page); ++ } ++ ++ pagevec_lru_add_file(&lru_pvec); ++ kfree(rbuf); ++ } else { ++ retCode = -ENOMEM; ++ } ++ ++ DbgPrint("retCode=%d", retCode); ++ return (retCode); ++ ++} ++ ++int novfs_a_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ int retVal = 0; ++ loff_t offset = pos; ++ struct novfs_schandle session; ++ struct novfs_data_list dllst[2]; ++ struct inode *inode = file->f_dentry->d_inode; ++ struct page *page; ++ pgoff_t index; ++ unsigned from, to; ++ SC_INITIALIZE(session); ++ ++ index = pos >> PAGE_CACHE_SHIFT; ++ from = pos & (PAGE_CACHE_SIZE - 1); ++ to = from + len; ++ ++ page = grab_cache_page_write_begin(mapping, index, flags); ++ if (!page) ++ return -ENOMEM; ++ ++ *pagep = page; ++ ++ DbgPrint("File=0x%p Page=0x%p offset=0x%llx From=%u To=%u " ++ "filesize=%lld\n", file, page, offset, from, to, ++ i_size_read(file->f_dentry->d_inode)); ++ if (!PageUptodate(page)) { ++ /* ++ * Check to see if whole page ++ */ ++ if ((to == PAGE_CACHE_SIZE) && (from == 0)) { ++ SetPageUptodate(page); ++ } ++ ++ /* ++ * Check to see if we can read page. ++ */ ++ else if ((file->f_flags & O_ACCMODE) != O_WRONLY) { ++ /* ++ * Get session. ++ */ ++ if (file->f_dentry && file->f_dentry->d_inode) { ++ if (file->f_dentry->d_inode->i_private) { ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) ++ inode-> ++ i_private)-> ++ Scope); ++ if (0 == SC_PRESENT(session)) { ++ ((struct inode_data *) inode-> ++ i_private)->Scope = ++ novfs_get_scope(file->f_dentry); ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode->i_private)->Scope); ++ } ++ } ++ } ++ ++ page_cache_get(page); ++ ++ len = i_size_read(inode) - offset; ++ if (len > PAGE_CACHE_SIZE) { ++ len = PAGE_CACHE_SIZE; ++ } ++ ++ if (len) { ++ /* ++ * Read page from server. ++ */ ++ ++ dllst[1].page = page; ++ dllst[1].offset = 0; ++ dllst[1].len = len; ++ dllst[1].rwflag = DLWRITE; ++ ++ DbgPrint("calling novfs_Read_Pages %lld", ++ offset); ++ novfs_read_pages(file->private_data, dllst, 2, ++ &len, &offset, session); ++ ++ /* ++ * Zero unnsed page. ++ */ ++ } ++ ++ if (len < PAGE_CACHE_SIZE) { ++ char *adr = kmap_atomic(page, KM_USER0); ++ memset(adr + len, 0, PAGE_CACHE_SIZE - len); ++ kunmap_atomic(adr, KM_USER0); ++ } ++ } else { ++ /* ++ * Zero section of memory that not going to be used. ++ */ ++ char *adr = kmap_atomic(page, KM_USER0); ++ memset(adr, 0, from); ++ memset(adr + to, 0, PAGE_CACHE_SIZE - to); ++ kunmap_atomic(adr, KM_USER0); ++ ++ DbgPrint("memset 0x%p", adr); ++ } ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ } ++// DbgPrint("return %d", retVal); ++ return (retVal); ++} ++ ++int novfs_a_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *page, void *fsdata) ++{ ++ int retCode = 0; ++ struct inode *inode = page->mapping->host; ++ loff_t offset = pos; ++ struct novfs_schandle session; ++ struct inode_data *id; ++ struct novfs_data_list dlst[1]; ++ pgoff_t index; ++ unsigned from, to; ++ SC_INITIALIZE(session); ++ ++ index = pos >> PAGE_CACHE_SHIFT; ++ from = pos & (PAGE_CACHE_SIZE - 1); ++ to = from + len; ++ ++ ++ DbgPrint("File=0x%p Page=0x%p offset=0x%x To=%u filesize=%lld", ++ file, page, offset, to, i_size_read(file->f_dentry->d_inode)); ++ if (file->f_dentry->d_inode ++ && (id = file->f_dentry->d_inode->i_private)) { ++ session = novfs_scope_get_sessionId(id->Scope); ++ if (0 == SC_PRESENT(session)) { ++ id->Scope = novfs_get_scope(file->f_dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ /* ++ * Setup file handle ++ */ ++ id->FileHandle = file->private_data; ++ ++ if (pos > inode->i_size) { ++ i_size_write(inode, pos); ++ } ++ ++ if (!PageUptodate(page)) { ++ pos = ++ ((loff_t) page->index << PAGE_CACHE_SHIFT) + offset; ++ ++ if (to < offset) { ++ return (retCode); ++ } ++ dlst[0].page = page; ++ dlst[0].offset = (void *)(unsigned long) offset; ++ dlst[0].len = len; ++ dlst[0].rwflag = DLREAD; ++ ++ retCode = ++ novfs_write_pages(id->FileHandle, dlst, 1, len, pos, ++ session); ++ ++ } else { ++ set_page_dirty(page); ++ } ++ } ++ ++ return (retCode); ++} ++ ++/*++======================================================================*/ ++ssize_t novfs_a_direct_IO(int rw, struct kiocb * kiocb, ++ const struct iovec * iov, ++ loff_t offset, unsigned long nr_segs) ++/* ++ * ++ * Notes: This is a dummy function so that we can allow a file ++ * to get the direct IO flag set. novfs_f_read and ++ * novfs_f_write will do the work. Maybe not the best ++ * way to do but it was the easiest to implement. ++ * ++ *========================================================================*/ ++{ ++ return (-EIO); ++} ++ ++/*++======================================================================*/ ++int novfs_i_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *nd) ++{ ++ char *path, *buf; ++ struct novfs_entry_info info; ++ void *handle; ++ struct novfs_schandle session; ++ int retCode = -EACCES; ++ ++ DbgPrint("mode=0%o flags=0%o %.*s", mode, ++ nd->NDOPENFLAGS, dentry->d_name.len, dentry->d_name.name); ++ ++ if (IS_ROOT(dentry) || /* Root */ ++ IS_ROOT(dentry->d_parent) || /* User */ ++ IS_ROOT(dentry->d_parent->d_parent) || /* Server */ ++ IS_ROOT(dentry->d_parent->d_parent->d_parent)) { /* Volume */ ++ return (-EACCES); ++ } ++ ++ if (mode | S_IFREG) { ++ if (dir->i_private) { ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) dir->i_private)-> ++ Scope); ++ if (0 == SC_PRESENT(session)) { ++ ((struct inode_data *) dir->i_private)->Scope = ++ novfs_get_scope(dentry); ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) dir-> ++ i_private)->Scope); ++ } ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = ++ novfs_dget_path(dentry, buf, ++ PATH_LENGTH_BUFFER); ++ if (path) { ++ retCode = ++ novfs_open_file(path, ++ nd-> ++ NDOPENFLAGS | ++ O_RDWR, &info, ++ &handle, session); ++ if (!retCode && handle) { ++ novfs_close_file(handle, ++ session); ++ if (!novfs_i_mknod ++ (dir, dentry, ++ mode | S_IFREG, 0)) { ++ if (dentry->d_inode) { ++ ((struct inode_data *) ++ dentry-> ++ d_inode-> ++ i_private)-> ++ Flags |= UPDATE_INODE; ++ } ++ } ++ } ++ } ++ kfree(buf); ++ } ++ } ++ } ++ return (retCode); ++} ++ ++void update_inode(struct inode *Inode, struct novfs_entry_info *Info) ++{ ++ static char dbuf[128]; ++ ++ DbgPrint("Inode=0x%p I_ino=%d", Inode, Inode->i_ino); ++ ++ DbgPrint("atime=%s", ctime_r(&Info->atime.tv_sec, dbuf)); ++ DbgPrint("ctime=%s", ctime_r(&Info->ctime.tv_sec, dbuf)); ++ DbgPrint("mtime=%s %d", ctime_r(&Info->mtime.tv_sec, dbuf), ++ Info->mtime.tv_nsec); ++ DbgPrint("size=%lld", Info->size); ++ DbgPrint("mode=0%o", Info->mode); ++ ++ if (Inode && ++ ((Inode->i_size != Info->size) || ++ (Inode->i_mtime.tv_sec != Info->mtime.tv_sec) || ++ (Inode->i_mtime.tv_nsec != Info->mtime.tv_nsec))) { ++ DbgPrint ("calling invalidate_remote_inode sz %d %d", ++ Inode->i_size, Info->size); ++ DbgPrint ("calling invalidate_remote_inode sec %d %d", ++ Inode->i_mtime.tv_sec, Info->mtime.tv_sec); ++ DbgPrint ("calling invalidate_remote_inode ns %d %d", ++ Inode->i_mtime.tv_nsec, Info->mtime.tv_nsec); ++ ++ if (Inode && Inode->i_mapping) { ++ invalidate_remote_inode(Inode); ++ } ++ } ++ ++ Inode->i_mode = Info->mode; ++ Inode->i_size = Info->size; ++ Inode->i_atime = Info->atime; ++ Inode->i_ctime = Info->ctime; ++ Inode->i_mtime = Info->mtime; ++ ++ if (Inode->i_size && Inode->i_sb->s_blocksize) { ++ Inode->i_blocks = ++ (unsigned long) (Info->size >> (loff_t) Inode->i_blkbits); ++ Inode->i_bytes = Info->size & (Inode->i_sb->s_blocksize - 1); ++ ++ DbgPrint("i_sb->s_blocksize=%d", Inode->i_sb->s_blocksize); ++ DbgPrint("i_blkbits=%d", Inode->i_blkbits); ++ DbgPrint("i_blocks=%d", Inode->i_blocks); ++ DbgPrint("i_bytes=%d", Inode->i_bytes); ++ } ++} ++ ++struct dentry *novfs_i_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ struct dentry *retVal = ERR_PTR(-ENOENT); ++ struct dentry *parent; ++ struct novfs_entry_info *info = NULL; ++ struct inode_data *id; ++ struct inode *inode = NULL; ++ uid_t uid = current_euid(); ++ ino_t ino = 0; ++ struct qstr name; ++ char *buf; ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ char *path; ++ path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ DbgPrint("dir 0x%p %d hash %d inode 0x%0p %s", ++ dir, dir->i_ino, dentry->d_name.hash, ++ dentry->d_inode, path); ++ } ++ kfree(buf); ++ } else { ++ DbgPrint("dir 0x%p %d name %.*s hash %d inode 0x%0p", ++ dir, dir->i_ino, dentry->d_name.len, dentry->d_name.name, ++ dentry->d_name.hash, dentry->d_inode); ++ } ++ ++ if ((dentry->d_name.len == 7) ++ && (0 == strncmp(dentry->d_name.name, " !xover", 7))) { ++ dentry->d_op = &novfs_dentry_operations; ++ igrab(dir); ++ d_add(dentry, dir); ++ return NULL; ++ } ++ if ((dentry->d_name.len == 7) ++ && (0 == strncmp(dentry->d_name.name, "z!xover", 7))) { ++ dentry->d_op = &novfs_dentry_operations; ++ igrab(dir); ++ d_add(dentry, dir); ++ return NULL; ++ } ++ ++ if (dir && (id = dir->i_private)) { ++ retVal = 0; ++ if (IS_ROOT(dentry)) { ++ DbgPrint("Root entry=0x%p", novfs_root); ++ inode = novfs_root->d_inode; ++ return (0); ++ } else { ++ info = ++ kmalloc(sizeof(struct novfs_entry_info) + ++ PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (info) { ++ if (NULL == ++ (retVal = ++ ERR_PTR(verify_dentry(dentry, 1)))) { ++ name.name = dentry->d_name.name; ++ name.len = dentry->d_name.len; ++ name.hash = novfs_internal_hash(&name); ++ ++ if (novfs_lock_inode_cache(dir)) { ++ if (!novfs_get_entry ++ (dir, &name, &ino, info)) { ++ inode = ++ ilookup(dentry-> ++ d_sb, ino); ++ if (inode) { ++ update_inode ++ (inode, ++ info); ++ } ++ } ++ novfs_unlock_inode_cache(dir); ++ } ++ ++ if (!inode && ino) { ++ uid = novfs_scope_get_uid(id->Scope); ++ if (novfs_lock_inode_cache(dir)) { ++ inode = novfs_get_inode (dentry->d_sb, info->mode, 0, uid, ino, &name); ++ if (inode) { ++ if (!novfs_get_entry(dir, &dentry->d_name, &ino, info)) { ++ update_inode ++ (inode, ++ info); ++ } ++ } ++ novfs_unlock_inode_cache ++ (dir); ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ if (!retVal) { ++ dentry->d_op = &novfs_dentry_operations; ++ if (inode) { ++ parent = dget_parent(dentry); ++ novfs_d_add(dentry->d_parent, dentry, inode, 1); ++ dput(parent); ++ } else { ++ d_add(dentry, inode); ++ } ++ } ++ ++ if (info) ++ kfree(info); ++ ++ DbgPrint("inode=0x%p dentry->d_inode=0x%p return=0x%p", ++ dir, dentry->d_inode, retVal); ++ ++ return (retVal); ++} ++ ++int novfs_i_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int retCode = -ENOENT; ++ struct inode *inode; ++ struct novfs_schandle session; ++ char *path, *buf; ++ uint64_t t64; ++ ++ DbgPrint("dir=0x%p dir->i_ino=%d %.*s", dir, ++ dir->i_ino, dentry->d_name.len, dentry->d_name.name); ++ DbgPrint("IS_ROOT(dentry)=%d", IS_ROOT(dentry)); ++ DbgPrint("IS_ROOT(dentry->d_parent)=%d", ++ IS_ROOT(dentry->d_parent)); ++ DbgPrint("IS_ROOT(dentry->d_parent->d_parent)=%d", ++ IS_ROOT(dentry->d_parent->d_parent)); ++ DbgPrint("IS_ROOT(dentry->d_parent->d_parent->d_parent)=%d", ++ IS_ROOT(dentry->d_parent->d_parent->d_parent)); ++ ++ if (IS_ROOT(dentry) || /* Root */ ++ IS_ROOT(dentry->d_parent) || /* User */ ++ (!IS_ROOT(dentry->d_parent->d_parent) && /* Server */ ++ IS_ROOT(dentry->d_parent->d_parent->d_parent))) { /* Volume */ ++ return (-EACCES); ++ } ++ ++ inode = dentry->d_inode; ++ if (inode) { ++ DbgPrint("dir=0x%p dir->i_ino=%d inode=0x%p ino=%d", ++ dir, dir->i_ino, inode, inode->i_ino); ++ if (inode->i_private) { ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ if (0 == SC_PRESENT(session)) { ++ ((struct inode_data *) inode->i_private)->Scope = ++ novfs_get_scope(dentry); ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ } ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = ++ novfs_dget_path(dentry, buf, ++ PATH_LENGTH_BUFFER); ++ if (path) { ++ DbgPrint("path %s mode 0%o", ++ path, inode->i_mode); ++ if (IS_ROOT(dentry->d_parent->d_parent)) { ++ retCode = novfs_daemon_logout(&dentry->d_name, &session); ++ } else { ++ retCode = ++ novfs_delete(path, ++ S_ISDIR(inode->i_mode), session); ++ if (retCode) { ++ struct iattr ia; ++ memset(&ia, 0, sizeof(ia)); ++ ia.ia_valid = ATTR_MODE; ++ ia.ia_mode = S_IRWXU; ++ novfs_set_attr(path, &ia, session); ++ retCode = novfs_delete(path, S_ISDIR(inode->i_mode), session); ++ } ++ } ++ if (!retCode || IS_DEADDIR(inode)) { ++ novfs_remove_inode_entry(dir, ++ &dentry-> ++ d_name, ++ 0); ++ dentry->d_time = 0; ++ t64 = 0; ++ novfs_scope_set_userspace(&t64, &t64, ++ &t64, &t64); ++ retCode = 0; ++ } ++ } ++ kfree(buf); ++ } ++ } ++ } ++ ++ DbgPrint("retCode 0x%x", retCode); ++ return (retCode); ++} ++ ++int novfs_i_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ char *path, *buf; ++ struct novfs_schandle session; ++ int retCode = 0; ++ struct inode *inode; ++ struct novfs_entry_info info; ++ uid_t uid; ++ ++ DbgPrint("dir=0x%p ino=%d dentry=0x%p %.*s mode=0%lo", ++ dir, dir->i_ino, dentry, dentry->d_name.len, ++ dentry->d_name.name, mode); ++ ++ if (IS_ROOT(dentry) || /* Root */ ++ IS_ROOT(dentry->d_parent) || /* User */ ++ IS_ROOT(dentry->d_parent->d_parent) || /* Server */ ++ IS_ROOT(dentry->d_parent->d_parent->d_parent)) { /* Volume */ ++ return (-EACCES); ++ } ++ ++ mode |= S_IFDIR; ++ mode &= (S_IFMT | S_IRWXU); ++ if (dir->i_private) { ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) dir->i_private)->Scope); ++ if (0 == SC_PRESENT(session)) { ++ ((struct inode_data *) dir->i_private)->Scope = ++ novfs_get_scope(dentry); ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) dir->i_private)-> ++ Scope); ++ } ++ ++ uid = novfs_scope_get_uid(((struct inode_data *) dir->i_private)->Scope); ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ DbgPrint("path %s", path); ++ retCode = ++ novfs_create(path, S_ISDIR(mode), session); ++ if (!retCode) { ++ retCode = ++ novfs_get_file_info(path, &info, ++ session); ++ if (!retCode) { ++ retCode = ++ novfs_i_mknod(dir, dentry, ++ mode, 0); ++ inode = dentry->d_inode; ++ if (inode) { ++ update_inode(inode, ++ &info); ++ ((struct inode_data *) inode-> ++ i_private)->Flags &= ++ ~UPDATE_INODE; ++ ++ dentry->d_time = ++ jiffies + ++ (novfs_update_timeout ++ * HZ); ++ ++ novfs_lock_inode_cache ++ (dir); ++ if (novfs_update_entry ++ (dir, ++ &dentry->d_name, 0, ++ &info)) { ++ novfs_add_inode_entry ++ (dir, ++ &dentry-> ++ d_name, ++ inode-> ++ i_ino, ++ &info); ++ } ++ novfs_unlock_inode_cache ++ (dir); ++ } ++ ++ } ++ } ++ } ++ kfree(buf); ++ } ++ } ++ ++ return (retCode); ++} ++ ++int novfs_i_rmdir(struct inode *inode, struct dentry *dentry) ++{ ++ return (novfs_i_unlink(inode, dentry)); ++} ++ ++int novfs_i_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) ++{ ++ struct inode *inode = NULL; ++ int retCode = -EACCES; ++ uid_t uid; ++ struct dentry *parent; ++ ++ if (IS_ROOT(dentry) || /* Root */ ++ IS_ROOT(dentry->d_parent) || /* User */ ++ IS_ROOT(dentry->d_parent->d_parent) || /* Server */ ++ IS_ROOT(dentry->d_parent->d_parent->d_parent)) { /* Volume */ ++ return (-EACCES); ++ } ++ ++ if (((struct inode_data *) dir->i_private)) { ++ uid = novfs_scope_get_uid(((struct inode_data *) dir->i_private)->Scope); ++ if (mode & (S_IFREG | S_IFDIR)) { ++ inode = ++ novfs_get_inode(dir->i_sb, mode, dev, uid, 0, &dentry->d_name); ++ } ++ } ++ if (inode) { ++ struct novfs_entry_info info; ++ ++ dentry->d_op = &novfs_dentry_operations; ++ parent = dget_parent(dentry); ++ novfs_d_add(parent, dentry, inode, 0); ++ memset(&info, 0, sizeof(info)); ++ info.mode = inode->i_mode; ++ novfs_lock_inode_cache(dir); ++ novfs_add_inode_entry(dir, &dentry->d_name, inode->i_ino, ++ &info); ++ novfs_unlock_inode_cache(dir); ++ ++ dput(parent); ++ ++ retCode = 0; ++ } ++ DbgPrint("return 0x%x", retCode); ++ return retCode; ++} ++ ++int novfs_i_rename(struct inode *odir, struct dentry *od, struct inode *ndir, ++ struct dentry *nd) ++{ ++ int retCode = -ENOTEMPTY; ++ char *newpath, *newbuf, *newcon; ++ char *oldpath, *oldbuf, *oldcon; ++ struct qstr newname, oldname; ++ struct novfs_entry_info *info = NULL; ++ int oldlen, newlen; ++ struct novfs_schandle session; ++ ino_t ino; ++ ++ if (IS_ROOT(od) || /* Root */ ++ IS_ROOT(od->d_parent) || /* User */ ++ IS_ROOT(od->d_parent->d_parent) || /* Server */ ++ IS_ROOT(od->d_parent->d_parent->d_parent)) { /* Volume */ ++ return (-EACCES); ++ } ++ ++ DbgPrint("odir=0x%p ino=%d ndir=0x%p ino=%d", odir, ++ odir->i_ino, ndir, ndir->i_ino); ++ ++ oldbuf = kmalloc(PATH_LENGTH_BUFFER * 2, GFP_KERNEL); ++ newbuf = oldbuf + PATH_LENGTH_BUFFER; ++ if (oldbuf && newbuf) { ++ oldpath = novfs_dget_path(od, oldbuf, PATH_LENGTH_BUFFER); ++ newpath = novfs_dget_path(nd, newbuf, PATH_LENGTH_BUFFER); ++ if (oldpath && newpath) { ++ oldlen = PATH_LENGTH_BUFFER - (int)(oldpath - oldbuf); ++ newlen = PATH_LENGTH_BUFFER - (int)(newpath - newbuf); ++ ++ DbgPrint("od=0x%p od->inode=0x%p od->inode->i_ino=%d %s", ++ od, od->d_inode, od->d_inode->i_ino, oldpath); ++ if (nd->d_inode) { ++ DbgPrint("nd=0x%p nd->inode=0x%p nd->inode->i_ino=%d %s", ++ nd, nd->d_inode, nd->d_inode->i_ino, ++ newpath); ++ } else { ++ DbgPrint("nd=0x%p nd->inode=0x%p %s", ++ nd, nd->d_inode, newpath); ++ } ++ ++ /* ++ * Check to see if two different servers or different volumes ++ */ ++ newcon = strchr(newpath + 1, '\\'); ++ oldcon = strchr(oldpath + 1, '\\'); ++ DbgPrint("newcon=0x%p newpath=0x%p", newcon, newpath); ++ DbgPrint("oldcon=0x%p oldpath=0x%p", oldcon, oldpath); ++ retCode = -EXDEV; ++ if (newcon && oldcon ++ && ((int)(newcon - newpath) == ++ (int)(oldcon - oldpath))) { ++ newcon = strchr(newcon + 1, '\\'); ++ oldcon = strchr(oldcon + 1, '\\'); ++ DbgPrint("2; newcon=0x%p newpath=0x%p", ++ newcon, newpath); ++ DbgPrint("2; oldcon=0x%p oldpath=0x%p", ++ oldcon, oldpath); ++ if (newcon && oldcon && ++ ((int)(newcon - newpath) == (int)(oldcon - oldpath))) { ++ newname.name = newpath; ++ newname.len = (int)(newcon - newpath); ++ newname.hash = 0; ++ ++ oldname.name = oldpath; ++ oldname.len = (int)(oldcon - oldpath); ++ oldname.hash = 0; ++ if (!novfs_d_strcmp(&newname, &oldname)) { ++ ++ if (od->d_inode ++ && od->d_inode->i_private) { ++ ++ if (nd->d_inode ++ && nd->d_inode-> ++ i_private) { ++ session = ++ novfs_scope_get_sessionId ++ (((struct inode_data *) ndir->i_private)->Scope); ++ if (0 == ++ SC_PRESENT ++ (session)) { ++ ((struct inode_data *) ndir->i_private)->Scope = novfs_get_scope(nd); ++ session ++ = ++ novfs_scope_get_sessionId ++ (((struct inode_data *) ndir->i_private)->Scope); ++ } ++ ++ retCode = ++ novfs_delete(newpath, S_ISDIR(nd->d_inode->i_mode), session); ++ if (retCode) { ++ struct iattr ia; ++ memset(&ia, 0, sizeof(ia)); ++ ia.ia_valid = ATTR_MODE; ++ ia.ia_mode = S_IRWXU; ++ novfs_set_attr(newpath, &ia, session); ++ retCode = novfs_delete(newpath, S_ISDIR(nd->d_inode->i_mode), session); ++ } ++ ++ } ++ ++ session = novfs_scope_get_sessionId(((struct inode_data *) ndir->i_private)->Scope); ++ if (0 == SC_PRESENT(session)) { ++ ((struct inode_data *)ndir->i_private)->Scope = novfs_get_scope(nd); ++ session = novfs_scope_get_sessionId(((struct inode_data *) ndir->i_private)->Scope); ++ } ++ retCode = novfs_rename_file(S_ISDIR(od->d_inode->i_mode), oldpath, oldlen - 1, newpath, newlen - 1, session); ++ ++ if (!retCode) { ++ info = (struct novfs_entry_info *) oldbuf; ++ od->d_time = 0; ++ novfs_remove_inode_entry(odir, &od->d_name, 0); ++ novfs_remove_inode_entry(ndir, &nd->d_name, 0); ++ novfs_get_file_info(newpath, info, session); ++ nd->d_time = jiffies + (novfs_update_timeout * HZ); ++ ++ if (od->d_inode && od->d_inode->i_ino) { ++ ino = od->d_inode-> i_ino; ++ } else { ++ ino = (ino_t)atomic_inc_return(&novfs_Inode_Number); ++ } ++ novfs_add_inode_entry(ndir, &nd->d_name, ino, info); ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ if (oldbuf) ++ kfree(oldbuf); ++ ++ DbgPrint("return %d", retCode); ++ return (retCode); ++} ++ ++ ++int novfs_i_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ char *path, *buf; ++ struct inode *inode = dentry->d_inode; ++ char atime_buf[32]; ++ char mtime_buf[32]; ++ char ctime_buf[32]; ++ unsigned int ia_valid = attr->ia_valid; ++ struct novfs_schandle session; ++ int retVal = 0; ++ struct iattr mattr; ++ ++ if (IS_ROOT(dentry) || /* Root */ ++ IS_ROOT(dentry->d_parent) || /* User */ ++ IS_ROOT(dentry->d_parent->d_parent) || /* Server */ ++ IS_ROOT(dentry->d_parent->d_parent->d_parent)) { /* Volume */ ++ return (-EACCES); ++ } ++ ++ if (inode && inode->i_private) { ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode->i_private)-> ++ Scope); ++ if (0 == SC_PRESENT(session)) { ++ ((struct inode_data *) inode->i_private)->Scope = ++ novfs_get_scope(dentry); ++ session = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ } ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ strcpy(atime_buf, "Unspecified"); ++ strcpy(mtime_buf, "Unspecified"); ++ strcpy(ctime_buf, "Unspecified"); ++ if (attr->ia_valid & ATTR_ATIME) { ++ ctime_r(&attr->ia_atime.tv_sec, ++ atime_buf); ++ } ++ if (attr->ia_valid & ATTR_MTIME) { ++ ctime_r(&attr->ia_mtime.tv_sec, ++ mtime_buf); ++ } ++ if (attr->ia_valid & ATTR_CTIME) { ++ ctime_r(&attr->ia_ctime.tv_sec, ++ ctime_buf); ++ } ++ /* Removed for Bug 132374. jlt */ ++ __DbgPrint("%s: %s\n" ++ " ia_valid: 0x%x\n" ++ " ia_mode: 0%o\n" ++ " ia_uid: %d\n" ++ " ia_gid: %d\n" ++ " ia_size: %lld\n" ++ " ia_atime: %s\n" ++ " ia_mtime: %s\n" ++ " ia_ctime: %s\n", __func__, ++ path, ++ attr->ia_valid, ++ attr->ia_mode, ++ attr->ia_uid, ++ attr->ia_gid, ++ attr->ia_size, ++ atime_buf, mtime_buf, ctime_buf); ++ ++ if ((attr->ia_valid & ATTR_FILE) ++ && (attr->ia_valid & ATTR_SIZE)) { ++ memcpy(&mattr, attr, sizeof(mattr)); ++ mattr.ia_valid &= ++ ~(ATTR_FILE | ATTR_SIZE); ++ attr = &mattr; ++ ia_valid = attr->ia_valid; ++#if 0 // thanks to vfs changes in our tree... ++ retVal = ++ novfs_trunc_ex(attr-> ++ ia_file-> ++ private_data, ++ attr-> ++ ia_size, ++ session); ++ if (!retVal) { ++ inode->i_size = attr->ia_size; ++ ((struct inode_data *) inode-> ++ i_private)->Flags |= ++ UPDATE_INODE; ++ } ++#endif ++ } ++ ++ if (ia_valid ++ && !(retVal = ++ novfs_set_attr(path, attr, session))) { ++ ((struct inode_data *) inode->i_private)-> ++ Flags |= UPDATE_INODE; ++ ++ if (ia_valid & ATTR_ATIME) ++ inode->i_atime = attr->ia_atime; ++ if (ia_valid & ATTR_MTIME) ++ inode->i_mtime = attr->ia_mtime; ++ if (ia_valid & ATTR_CTIME) ++ inode->i_ctime = attr->ia_ctime; ++ if (ia_valid & ATTR_MODE) { ++ inode->i_mode = ++ attr-> ++ ia_mode & (S_IFMT | ++ S_IRWXU); ++ } ++ } ++ } ++ } ++ kfree(buf); ++ } ++ DbgPrint("return 0x%x", retVal); ++ ++ return (retVal); ++} ++ ++int novfs_i_getattr(struct vfsmount *mnt, struct dentry *dentry, ++ struct kstat *kstat) ++{ ++ int retCode = 0; ++ char atime_buf[32]; ++ char mtime_buf[32]; ++ char ctime_buf[32]; ++ struct inode *inode = dentry->d_inode; ++ ++ struct novfs_entry_info info; ++ char *path, *buf; ++ struct novfs_schandle session; ++ struct inode_data *id; ++ ++ if (!IS_ROOT(dentry) && !IS_ROOT(dentry->d_parent)) { ++ SC_INITIALIZE(session); ++ id = dentry->d_inode->i_private; ++ ++ if (id && (id->Flags & UPDATE_INODE)) { ++ session = novfs_scope_get_sessionId(id->Scope); ++ ++ if (0 == SC_PRESENT(session)) { ++ id->Scope = novfs_get_scope(dentry); ++ session = novfs_scope_get_sessionId(id->Scope); ++ } ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = ++ novfs_dget_path(dentry, buf, ++ PATH_LENGTH_BUFFER); ++ if (path) { ++ retCode = ++ novfs_get_file_info(path, &info, ++ session); ++ if (!retCode) { ++ update_inode(inode, &info); ++ id->Flags &= ~UPDATE_INODE; ++ } ++ } ++ kfree(buf); ++ } ++ } ++ } ++ ++ kstat->ino = inode->i_ino; ++ kstat->dev = inode->i_sb->s_dev; ++ kstat->mode = inode->i_mode; ++ kstat->nlink = inode->i_nlink; ++ kstat->uid = inode->i_uid; ++ kstat->gid = inode->i_gid; ++ kstat->rdev = inode->i_rdev; ++ kstat->size = i_size_read(inode); ++ kstat->atime = inode->i_atime; ++ kstat->mtime = inode->i_mtime; ++ kstat->ctime = inode->i_ctime; ++ kstat->blksize = inode->i_sb->s_blocksize; ++ kstat->blocks = inode->i_blocks; ++ if (inode->i_bytes) { ++ kstat->blocks++; ++ } ++ ctime_r(&kstat->atime.tv_sec, atime_buf); ++ ctime_r(&kstat->mtime.tv_sec, mtime_buf); ++ ctime_r(&kstat->ctime.tv_sec, ctime_buf); ++ ++ __DbgPrint("%s: 0x%x 0x%p <%.*s>\n" ++ " ino: %d\n" ++ " dev: 0x%x\n" ++ " mode: 0%o\n" ++ " nlink: 0x%x\n" ++ " uid: 0x%x\n" ++ " gid: 0x%x\n" ++ " rdev: 0x%x\n" ++ " size: 0x%llx\n" ++ " atime: %s\n" ++ " mtime: %s\n" ++ " ctime: %s\n" ++ " blksize: 0x%x\n" ++ " blocks: 0x%x\n", __func__, ++ retCode, dentry, dentry->d_name.len, dentry->d_name.name, ++ kstat->ino, ++ kstat->dev, ++ kstat->mode, ++ kstat->nlink, ++ kstat->uid, ++ kstat->gid, ++ kstat->rdev, ++ kstat->size, ++ atime_buf, ++ mtime_buf, ctime_buf, kstat->blksize, kstat->blocks); ++ return (retCode); ++} ++ ++ssize_t novfs_i_getxattr(struct dentry *dentry, const char *name, void *buffer, ++ size_t buffer_size) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct novfs_schandle sessionId; ++ char *path, *buf, *bufRead; ++ ssize_t dataLen; ++ ++ int retxcode = 0; ++ ++ SC_INITIALIZE(sessionId); ++ ++ DbgPrint("Ian"); /*%.*s\n", dentry->d_name.len, dentry->d_name.name); */ ++ DbgPrint("dentry->d_name.len %u, dentry->d_name.name %s", ++ dentry->d_name.len, dentry->d_name.name); ++ DbgPrint("name %s", name); ++ DbgPrint("size %u", buffer_size); ++ ++ if (inode && inode->i_private) { ++ sessionId = ++ novfs_scope_get_sessionId(((struct inode_data *) inode->i_private)-> ++ Scope); ++ DbgPrint("SessionId = %u", sessionId); ++ //if (0 == sessionId) ++ if (0 == SC_PRESENT(sessionId)) { ++ ((struct inode_data *) inode->i_private)->Scope = ++ novfs_get_scope(dentry); ++ sessionId = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ DbgPrint("SessionId = %u", sessionId); ++ } ++ } ++ ++ dataLen = 0; ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ bufRead = kmalloc(XA_BUFFER, GFP_KERNEL); ++ if (bufRead) { ++ retxcode = ++ novfs_getx_file_info(path, name, bufRead, ++ XA_BUFFER, &dataLen, ++ sessionId); ++ DbgPrint("after novfs_GetX_File_Info retxcode = %d", ++ retxcode); ++ if (!retxcode) { ++ novfs_dump(64, bufRead); ++ if (buffer_size != 0) { ++ if (buffer_size >= dataLen) { ++ memcpy(buffer, bufRead, ++ dataLen); ++ } else { ++ DbgPrint("(!!!) not enough buffer_size. buffer_size = %d, dataLen = %d", ++ buffer_size, ++ dataLen); ++ retxcode = -ERANGE; ++ } ++ } ++ ++ if (bufRead) { ++ kfree(bufRead); ++ } ++ } ++ } ++ } ++ kfree(buf); ++ } ++ ++ if (retxcode) { ++ dataLen = retxcode; ++ } else { ++ if ((buffer_size > 0) && (buffer_size < dataLen)) { ++ dataLen = -ERANGE; ++ } ++ } ++ ++ return (dataLen); ++} ++ ++int novfs_i_setxattr(struct dentry *dentry, const char *name, const void *value, ++ size_t value_size, int flags) ++{ ++ ++ struct inode *inode = dentry->d_inode; ++ struct novfs_schandle sessionId; ++ char *path, *buf; ++ unsigned long bytesWritten = 0; ++ int retError = 0; ++ int retxcode = 0; ++ ++ SC_INITIALIZE(sessionId); ++ ++ DbgPrint("Ian"); /*%.*s\n", dentry->d_name.len, dentry->d_name.name); */ ++ DbgPrint("dentry->d_name.len %u, dentry->d_name.name %s", ++ dentry->d_name.len, dentry->d_name.name); ++ DbgPrint("name %s", name); ++ DbgPrint("value_size %u", value_size); ++ DbgPrint("flags %d", flags); ++ ++ if (inode && inode->i_private) { ++ sessionId = ++ novfs_scope_get_sessionId(((struct inode_data *) inode->i_private)-> ++ Scope); ++ DbgPrint("SessionId = %u", sessionId); ++ //if (0 == sessionId) ++ if (0 == SC_PRESENT(sessionId)) { ++ ((struct inode_data *) inode->i_private)->Scope = ++ novfs_get_scope(dentry); ++ sessionId = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ DbgPrint("SessionId = %u", sessionId); ++ } ++ } ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ retxcode = ++ novfs_setx_file_info(path, name, value, value_size, ++ &bytesWritten, flags, ++ sessionId); ++ if (!retxcode) { ++ DbgPrint("bytesWritten = %u", bytesWritten); ++ } ++ } ++ kfree(buf); ++ } ++ ++ if (retxcode) { ++ retError = retxcode; ++ } ++ ++ if (bytesWritten < value_size) { ++ retError = retxcode; ++ } ++ return (retError); ++} ++ ++ssize_t novfs_i_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct novfs_schandle sessionId; ++ char *path, *buf, *bufList; ++ ssize_t dataLen; ++ int retxcode = 0; ++ ++ SC_INITIALIZE(sessionId); ++ ++ DbgPrint("Ian"); //%.*s\n", dentry->d_name.len, dentry->d_name.name); ++ DbgPrint("dentry->d_name.len %u, dentry->d_name.name %s", ++ dentry->d_name.len, dentry->d_name.name); ++ DbgPrint("size %u", buffer_size); ++ ++ if (inode && inode->i_private) { ++ sessionId = ++ novfs_scope_get_sessionId(((struct inode_data *) inode->i_private)-> ++ Scope); ++ DbgPrint("SessionId = %u", sessionId); ++ //if (0 == sessionId) ++ if (0 == SC_PRESENT(sessionId)) { ++ ((struct inode_data *) inode->i_private)->Scope = ++ novfs_get_scope(dentry); ++ sessionId = ++ novfs_scope_get_sessionId(((struct inode_data *) inode-> ++ i_private)->Scope); ++ DbgPrint("SessionId = %u", sessionId); ++ } ++ } ++ ++ dataLen = 0; ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ bufList = kmalloc(XA_BUFFER, GFP_KERNEL); ++ if (bufList) { ++ retxcode = ++ novfs_listx_file_info(path, bufList, ++ XA_BUFFER, &dataLen, ++ sessionId); ++ ++ novfs_dump(64, bufList); ++ if (buffer_size != 0) { ++ if (buffer_size >= dataLen) { ++ memcpy(buffer, bufList, ++ dataLen); ++ } else { ++ DbgPrint("(!!!) not enough buffer_size. buffer_size = %d, dataLen = %d", ++ buffer_size, dataLen); ++ retxcode = -1; ++ } ++ } ++ ++ if (bufList) { ++ kfree(bufList); ++ } ++ } ++ ++ } ++ kfree(buf); ++ } ++ ++ if (retxcode) { ++ dataLen = -1; ++ } else { ++ ++ if ((buffer_size > 0) && (buffer_size < dataLen)) { ++ dataLen = -ERANGE; ++ } ++ } ++ return (dataLen); ++} ++ ++int novfs_i_revalidate(struct dentry *dentry) ++{ ++ ++ DbgPrint("name %.*s", dentry->d_name.len, dentry->d_name.name); ++ ++ return (0); ++} ++ ++void novfs_read_inode(struct inode *inode) ++{ ++ DbgPrint("0x%p %d", inode, inode->i_ino); ++} ++ ++void novfs_write_inode(struct inode *inode) ++{ ++ DbgPrint("Inode=0x%p Ino=%d", inode, inode->i_ino); ++} ++ ++int novfs_notify_change(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ ++ DbgPrint("Dentry=0x%p Name=%.*s Inode=0x%p Ino=%d ia_valid=0x%x", ++ dentry, dentry->d_name.len, dentry->d_name.name, inode, ++ inode->i_ino, attr->ia_valid); ++ return (0); ++} ++ ++void novfs_clear_inode(struct inode *inode) ++{ ++ InodeCount--; ++ ++ if (inode->i_private) { ++ struct inode_data *id = inode->i_private; ++ ++ DbgPrint("inode=0x%p ino=%d Scope=0x%p Name=%s", ++ inode, inode->i_ino, id->Scope, id->Name); ++ ++ novfs_free_inode_cache(inode); ++ ++ down(&InodeList_lock); ++ list_del(&id->IList); ++ up(&InodeList_lock); ++ ++ kfree(inode->i_private); ++ inode->i_private = NULL; ++ ++ remove_inode_hash(inode); ++ ++ } else { ++ DbgPrint("inode=0x%p ino=%d", inode, inode->i_ino); ++ } ++} ++ ++/* Called when /proc/mounts is read */ ++int novfs_show_options(struct seq_file *s, struct vfsmount *m) ++{ ++ char *buf, *path, *tmp; ++ ++ buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ struct path my_path; ++ my_path.mnt = m; ++ my_path.dentry = m->mnt_root; ++ path = d_path(&my_path, buf, PATH_LENGTH_BUFFER); ++ if (path) { ++ if (!novfs_current_mnt ++ || (novfs_current_mnt ++ && strcmp(novfs_current_mnt, path))) { ++ DbgPrint("%.*s %.*s %s", ++ m->mnt_root->d_name.len, ++ m->mnt_root->d_name.name, ++ m->mnt_mountpoint->d_name.len, ++ m->mnt_mountpoint->d_name.name, path); ++ tmp = kmalloc(PATH_LENGTH_BUFFER - ++ (int)(path - buf), ++ GFP_KERNEL); ++ if (tmp) { ++ strcpy(tmp, path); ++ path = novfs_current_mnt; ++ novfs_current_mnt = tmp; ++ novfs_daemon_set_mnt_point(novfs_current_mnt); ++ ++ if (path) { ++ kfree(path); ++ } ++ } ++ } ++ } ++ kfree(buf); ++ } ++ return (0); ++} ++ ++/* Called when statfs(2) system called. */ ++int novfs_statfs(struct dentry *de, struct kstatfs *buf) ++{ ++ uint64_t td, fd, te, fe; ++ struct super_block *sb = de->d_sb; ++ ++ DbgPrint(""); ++ ++ td = fd = te = fe = 0; ++ ++ novfs_scope_get_userspace(&td, &fd, &te, &fe); ++ ++ DbgPrint("td=%llu", td); ++ DbgPrint("fd=%llu", fd); ++ DbgPrint("te=%llu", te); ++ DbgPrint("fe=%llu", fd); ++ /* fix for Nautilus */ ++ if (sb->s_blocksize == 0) ++ sb->s_blocksize = 4096; ++ ++ buf->f_type = sb->s_magic; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = NW_MAX_PATH_LENGTH; ++ buf->f_blocks = ++ (sector_t) (td + ++ (uint64_t) (sb->s_blocksize - ++ 1)) >> (uint64_t) sb->s_blocksize_bits; ++ buf->f_bfree = (sector_t) fd >> (uint64_t) sb->s_blocksize_bits; ++ buf->f_bavail = (sector_t) buf->f_bfree; ++ buf->f_files = (sector_t) te; ++ buf->f_ffree = (sector_t) fe; ++ buf->f_frsize = sb->s_blocksize; ++ if (te > 0xffffffff) ++ buf->f_files = 0xffffffff; ++ ++ if (fe > 0xffffffff) ++ buf->f_ffree = 0xffffffff; ++ ++ DbgPrint("f_type: 0x%x", buf->f_type); ++ DbgPrint("f_bsize: %u", buf->f_bsize); ++ DbgPrint("f_namelen: %d", buf->f_namelen); ++ DbgPrint("f_blocks: %llu", buf->f_blocks); ++ DbgPrint("f_bfree: %llu", buf->f_bfree); ++ DbgPrint("f_bavail: %llu", buf->f_bavail); ++ DbgPrint("f_files: %llu", buf->f_files); ++ DbgPrint("f_ffree: %llu", buf->f_ffree); ++ DbgPrint("f_frsize: %u", buf->f_frsize); ++ ++ return 0; ++} ++ ++struct inode *novfs_get_inode(struct super_block *sb, int mode, int dev, ++ uid_t Uid, ino_t ino, struct qstr *name) ++{ ++ struct inode *inode = new_inode(sb); ++ ++ if (inode) { ++ InodeCount++; ++ inode->i_mode = mode; ++ inode->i_uid = Uid; ++ inode->i_gid = 0; ++ inode->i_blkbits = sb->s_blocksize_bits; ++ inode->i_blocks = 0; ++ inode->i_rdev = 0; ++ inode->i_ino = (ino) ? ino : (ino_t)atomic_inc_return(&novfs_Inode_Number); ++ if (novfs_page_cache) { ++ inode->i_mapping->a_ops = &novfs_aops; ++ } else { ++ inode->i_mapping->a_ops = &novfs_nocache_aops; ++ } ++ inode->i_mapping->backing_dev_info = &novfs_backing_dev_info; ++ inode->i_atime.tv_sec = 0; ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime = inode->i_ctime = inode->i_atime; ++ ++ DbgPrint("Inode=0x%p I_ino=%d len=%d", ++ inode, inode->i_ino, name->len); ++ ++ if (NULL != ++ (inode->i_private = ++ kmalloc(sizeof(struct inode_data) + name->len, ++ GFP_KERNEL))) { ++ struct inode_data *id; ++ id = inode->i_private; ++ ++ DbgPrint("i_private 0x%p", id); ++ ++ id->Scope = NULL; ++ id->Flags = 0; ++ id->Inode = inode; ++ ++ id->cntDC = 1; ++ ++ INIT_LIST_HEAD(&id->DirCache); ++ init_MUTEX(&id->DirCacheLock); ++ ++ id->FileHandle = 0; ++ id->CacheFlag = 0; ++ ++ down(&InodeList_lock); ++ ++ list_add_tail(&id->IList, &InodeList); ++ up(&InodeList_lock); ++ ++ id->Name[0] = '\0'; ++ ++ memcpy(id->Name, name->name, name->len); ++ id->Name[name->len] = '\0'; ++ ++ DbgPrint("name %s", id->Name); ++ } ++ ++ insert_inode_hash(inode); ++ ++ switch (mode & S_IFMT) { ++ ++ case S_IFREG: ++ inode->i_op = &novfs_file_inode_operations; ++ inode->i_fop = &novfs_file_operations; ++ break; ++ ++ case S_IFDIR: ++ inode->i_op = &novfs_inode_operations; ++ inode->i_fop = &novfs_dir_operations; ++ inode->i_blkbits = 0; ++ break; ++ ++ default: ++ init_special_inode(inode, mode, dev); ++ break; ++ } ++ ++ DbgPrint("size=%lld", inode->i_size); ++ DbgPrint("mode=0%o", inode->i_mode); ++ DbgPrint("i_sb->s_blocksize=%d", inode->i_sb->s_blocksize); ++ DbgPrint("i_blkbits=%d", inode->i_blkbits); ++ DbgPrint("i_blocks=%d", inode->i_blocks); ++ DbgPrint("i_bytes=%d", inode->i_bytes); ++ } ++ ++ DbgPrint("0x%p %d", inode, inode->i_ino); ++ return (inode); ++} ++ ++int novfs_fill_super(struct super_block *SB, void *Data, int Silent) ++{ ++ struct inode *inode; ++ struct dentry *server, *tree; ++ struct qstr name; ++ struct novfs_entry_info info; ++ ++ SB->s_blocksize = PAGE_CACHE_SIZE; ++ SB->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ SB->s_maxbytes = 0xFFFFFFFFFFFFFFFFULL; /* Max file size */ ++ SB->s_op = &novfs_ops; ++ SB->s_flags |= (MS_NODIRATIME | MS_NODEV | MS_POSIXACL); ++ SB->s_magic = NOVFS_MAGIC; ++ ++ name.len = 1; ++ name.name = "/"; ++ ++ inode = novfs_get_inode(SB, S_IFDIR | 0777, 0, 0, 0, &name); ++ if (!inode) { ++ return (-ENOMEM); ++ } ++ ++ novfs_root = d_alloc_root(inode); ++ ++ if (!novfs_root) { ++ iput(inode); ++ return (-ENOMEM); ++ } ++ novfs_root->d_time = jiffies + (novfs_update_timeout * HZ); ++ ++ inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; ++ ++ SB->s_root = novfs_root; ++ ++ DbgPrint("root 0x%p", novfs_root); ++ ++ if (novfs_root) { ++ novfs_root->d_op = &novfs_dentry_operations; ++ ++ name.name = SERVER_DIRECTORY_NAME; ++ name.len = strlen(SERVER_DIRECTORY_NAME); ++ name.hash = novfs_internal_hash(&name); ++ ++ inode = novfs_get_inode(SB, S_IFDIR | 0777, 0, 0, 0, &name); ++ if (inode) { ++ info.mode = inode->i_mode; ++ info.namelength = 0; ++ inode->i_size = info.size = 0; ++ inode->i_uid = info.uid = 0; ++ inode->i_gid = info.gid = 0; ++ inode->i_atime = info.atime = ++ inode->i_ctime = info.ctime = ++ inode->i_mtime = info.mtime = CURRENT_TIME; ++ ++ server = d_alloc(novfs_root, &name); ++ if (server) { ++ server->d_op = &novfs_dentry_operations; ++ server->d_time = 0xffffffff; ++ d_add(server, inode); ++ DbgPrint("d_add %s 0x%p", ++ SERVER_DIRECTORY_NAME, server); ++ novfs_add_inode_entry(novfs_root->d_inode, ++ &name, inode->i_ino, ++ &info); ++ } ++ } ++ ++ name.name = TREE_DIRECTORY_NAME; ++ name.len = strlen(TREE_DIRECTORY_NAME); ++ name.hash = novfs_internal_hash(&name); ++ ++ inode = novfs_get_inode(SB, S_IFDIR | 0777, 0, 0, 0, &name); ++ if (inode) { ++ info.mode = inode->i_mode; ++ info.namelength = 0; ++ inode->i_size = info.size = 0; ++ inode->i_uid = info.uid = 0; ++ inode->i_gid = info.gid = 0; ++ inode->i_atime = info.atime = ++ inode->i_ctime = info.ctime = ++ inode->i_mtime = info.mtime = CURRENT_TIME; ++ tree = d_alloc(novfs_root, &name); ++ if (tree) { ++ tree->d_op = &novfs_dentry_operations; ++ tree->d_time = 0xffffffff; ++ ++ d_add(tree, inode); ++ DbgPrint("d_add %s 0x%p", ++ TREE_DIRECTORY_NAME, tree); ++ novfs_add_inode_entry(novfs_root->d_inode, ++ &name, inode->i_ino, ++ &info); ++ } ++ } ++ } ++ ++ return (0); ++} ++ ++static int novfs_get_sb(struct file_system_type *Fstype, int Flags, ++ const char *Dev_name, void *Data, struct vfsmount *Mnt) ++{ ++ DbgPrint("Fstype=0x%x Dev_name=%s", Fstype, Dev_name); ++ return get_sb_nodev(Fstype, Flags, Data, novfs_fill_super, Mnt); ++} ++ ++static void novfs_kill_sb(struct super_block *super) ++{ ++ shrink_dcache_sb(super); ++ kill_litter_super(super); ++} ++ ++ssize_t novfs_Control_read(struct file *file, char *buf, size_t nbytes, ++ loff_t * ppos) ++{ ++ ssize_t retval = 0; ++ ++ DbgPrint("kernel_locked 0x%x", kernel_locked()); ++ ++ return retval; ++} ++ ++ssize_t novfs_Control_write(struct file * file, const char *buf, size_t nbytes, ++ loff_t * ppos) ++{ ++ ssize_t retval = 0; ++ ++ DbgPrint("kernel_locked 0x%x", kernel_locked()); ++ if (buf && nbytes) { ++ } ++ ++ return (retval); ++} ++ ++int novfs_Control_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ int retval = 0; ++ ++ DbgPrint("kernel_locked 0x%x", kernel_locked()); ++ ++ return (retval); ++} ++ ++static struct file_system_type novfs_fs_type = { ++ .name = "novfs", ++ .get_sb = novfs_get_sb, ++ .kill_sb = novfs_kill_sb, ++ .owner = THIS_MODULE, ++}; ++ ++int __init init_novfs(void) ++{ ++ int retCode; ++ ++ lastDir[0] = 0; ++ lastTime = get_nanosecond_time(); ++ ++ inHAX = 0; ++ inHAXTime = get_nanosecond_time(); ++ ++ retCode = novfs_proc_init(); ++ ++ novfs_profile_init(); ++ ++ if (!retCode) { ++ DbgPrint("%s %s %s", __DATE__, __TIME__, NOVFS_VERSION_STRING); ++ novfs_daemon_queue_init(); ++ novfs_scope_init(); ++ retCode = register_filesystem(&novfs_fs_type); ++ if (retCode) { ++ novfs_proc_exit(); ++ novfs_daemon_queue_exit(); ++ novfs_scope_exit(); ++ } ++ } ++ return (retCode); ++} ++ ++void __exit exit_novfs(void) ++{ ++ novfs_scope_exit(); ++ novfs_daemon_queue_exit(); ++ novfs_profile_exit(); ++ novfs_proc_exit(); ++ unregister_filesystem(&novfs_fs_type); ++ ++ if (novfs_current_mnt) { ++ kfree(novfs_current_mnt); ++ novfs_current_mnt = NULL; ++ } ++} ++ ++int novfs_lock_inode_cache(struct inode *i) ++{ ++ struct inode_data *id; ++ int retVal = 0; ++ ++ DbgPrint("0x%p", i); ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ down(&id->DirCacheLock); ++ retVal = 1; ++ } ++ DbgPrint("return %d", retVal); ++ return (retVal); ++} ++ ++void novfs_unlock_inode_cache(struct inode *i) ++{ ++ struct inode_data *id; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ up(&id->DirCacheLock); ++ } ++} ++ ++int novfs_enumerate_inode_cache(struct inode *i, struct list_head **iteration, ++ ino_t * ino, struct novfs_entry_info *info) ++/* ++ * Arguments: struct inode *i - pointer to directory inode ++ * ++ * Returns: 0 - item found ++ * -1 - done ++ * ++ * Abstract: Unlocks inode cache. ++ * ++ * Notes: DirCacheLock should be held before calling this routine. ++ *========================================================================*/ ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ struct list_head *l = NULL; ++ int retVal = -1; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ if ((NULL == iteration) || (NULL == *iteration)) { ++ l = id->DirCache.next; ++ } else { ++ l = *iteration; ++ } ++ ++ if (l == &id->DirCache) { ++ l = NULL; ++ } else { ++ dc = list_entry(l, struct novfs_dir_cache, list); ++ ++ *ino = dc->ino; ++ info->type = 0; ++ info->mode = dc->mode; ++ info->size = dc->size; ++ info->atime = dc->atime; ++ info->mtime = dc->mtime; ++ info->ctime = dc->ctime; ++ info->namelength = dc->nameLen; ++ memcpy(info->name, dc->name, dc->nameLen); ++ info->name[dc->nameLen] = '\0'; ++ retVal = 0; ++ ++ l = l->next; ++ } ++ } ++ *iteration = l; ++ return (retVal); ++} ++ ++/* DirCacheLock should be held before calling this routine. */ ++int novfs_get_entry(struct inode *i, struct qstr *name, ino_t * ino, ++ struct novfs_entry_info *info) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ int retVal = -1; ++ char *n = ""; ++ int nl = 6; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ if (name && name->len) { ++ n = (char *)name->name; ++ nl = name->len; ++ } ++ ++ dc = novfs_lookup_inode_cache(i, name, *ino); ++ if (dc) { ++ dc->flags |= ENTRY_VALID; ++ retVal = 0; ++ *ino = dc->ino; ++ info->type = 0; ++ info->mode = dc->mode; ++ info->size = dc->size; ++ info->atime = dc->atime; ++ info->mtime = dc->mtime; ++ info->ctime = dc->ctime; ++ info->namelength = dc->nameLen; ++ memcpy(info->name, dc->name, dc->nameLen); ++ info->name[dc->nameLen] = '\0'; ++ retVal = 0; ++ } ++ ++ DbgPrint("inode: 0x%p; name: %.*s; ino: %d\n", i, nl, n, *ino); ++ } ++ DbgPrint("return %d", retVal); ++ return (retVal); ++} ++ ++ /*DirCacheLock should be held before calling this routine. */ ++int novfs_get_entry_by_pos(struct inode *i, loff_t pos, ino_t * ino, ++ struct novfs_entry_info *info) ++{ ++ int retVal = -1; ++ loff_t count = 0; ++ loff_t i_pos = pos - 2; ++ struct list_head *inter = NULL; ++ while (!novfs_enumerate_inode_cache(i, &inter, ino, info)) { ++ DbgPrint("info->name = %s", info->name); ++ if (count == i_pos) { ++ retVal = 0; ++ break; ++ } else ++ count++; ++ } ++ ++ return retVal; ++} ++ ++/* DirCacheLock should be held before calling this routine. */ ++int novfs_get_entry_time(struct inode *i, struct qstr *name, ino_t * ino, ++ struct novfs_entry_info *info, u64 * EntryTime) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ int retVal = -1; ++ char *n = ""; ++ int nl = 6; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ if (name && name->len) { ++ n = (char *)name->name; ++ nl = name->len; ++ } ++ DbgPrint("inode: 0x%p; name: %.*s; ino: %d", i, nl, n, *ino); ++ ++ dc = novfs_lookup_inode_cache(i, name, *ino); ++ if (dc) { ++ retVal = 0; ++ *ino = dc->ino; ++ info->type = 0; ++ info->mode = dc->mode; ++ info->size = dc->size; ++ info->atime = dc->atime; ++ info->mtime = dc->mtime; ++ info->ctime = dc->ctime; ++ info->namelength = dc->nameLen; ++ memcpy(info->name, dc->name, dc->nameLen); ++ info->name[dc->nameLen] = '\0'; ++ if (EntryTime) { ++ *EntryTime = dc->jiffies; ++ } ++ retVal = 0; ++ } ++ } ++ DbgPrint("return %d", retVal); ++ return (retVal); ++} ++ ++/* ++ * Abstract: This routine will return the first entry on the list ++ * and then remove it. ++ * ++ * Notes: DirCacheLock should be held before calling this routine. ++ * ++ */ ++int novfs_get_remove_entry(struct inode *i, ino_t * ino, struct novfs_entry_info *info) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ struct list_head *l = NULL; ++ int retVal = -1; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ l = id->DirCache.next; ++ ++ if (l != &id->DirCache) { ++ dc = list_entry(l, struct novfs_dir_cache, list); ++ ++ *ino = dc->ino; ++ info->type = 0; ++ info->mode = dc->mode; ++ info->size = dc->size; ++ info->atime = dc->atime; ++ info->mtime = dc->mtime; ++ info->ctime = dc->ctime; ++ info->namelength = dc->nameLen; ++ memcpy(info->name, dc->name, dc->nameLen); ++ info->name[dc->nameLen] = '\0'; ++ retVal = 0; ++ ++ list_del(&dc->list); ++ kfree(dc); ++ DCCount--; ++ ++ id->cntDC--; ++ } ++ } ++ return (retVal); ++} ++ ++/* ++ * Abstract: Marks all entries in the directory cache as invalid. ++ * ++ * Notes: DirCacheLock should be held before calling this routine. ++ * ++ *========================================================================*/ ++void novfs_invalidate_inode_cache(struct inode *i) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ struct list_head *l; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ list_for_each(l, &id->DirCache) { ++ dc = list_entry(l, struct novfs_dir_cache, list); ++ dc->flags &= ~ENTRY_VALID; ++ } ++ } ++} ++ ++/*++======================================================================*/ ++struct novfs_dir_cache *novfs_lookup_inode_cache(struct inode *i, struct qstr *name, ++ ino_t ino) ++/* ++ * Returns: struct novfs_dir_cache entry if match ++ * NULL - if there is no match. ++ * ++ * Abstract: Checks a inode directory to see if there are any enties ++ * matching name or ino. If name is specified then ino is ++ * not used. ino is use if name is not specified. ++ * ++ * Notes: DirCacheLock should be held before calling this routine. ++ * ++ *========================================================================*/ ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc, *retVal = NULL; ++ struct list_head *l; ++ char *n = ""; ++ int nl = 6; ++ int hash = 0; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ if (name && name->name) { ++ nl = name->len; ++ n = (char *)name->name; ++ hash = name->hash; ++ } ++ DbgPrint("inode: 0x%p; name: %.*s; hash: 0x%x;\n" ++ " len: %d; ino: %d", i, nl, n, hash, nl, ino); ++ ++ list_for_each(l, &id->DirCache) { ++ dc = list_entry(l, struct novfs_dir_cache, list); ++ if (name) { ++ ++/* DbgPrint("novfs_lookup_inode_cache: 0x%p\n" \ ++ " ino: %d\n" \ ++ " hash: 0x%x\n" \ ++ " len: %d\n" \ ++ " name: %.*s\n", ++ dc, dc->ino, dc->hash, dc->nameLen, dc->nameLen, dc->name); ++*/ ++ if ((name->hash == dc->hash) && ++ (name->len == dc->nameLen) && ++ (0 == ++ memcmp(name->name, dc->name, name->len))) { ++ retVal = dc; ++ break; ++ } ++ } else { ++ if (ino == dc->ino) { ++ retVal = dc; ++ break; ++ } ++ } ++ } ++ } ++ ++ DbgPrint("return 0x%p", retVal); ++ return (retVal); ++} ++ ++/* ++ * Checks a inode directory to see if there are any enties matching name ++ * or ino. If entry is found the valid bit is set. ++ * ++ * DirCacheLock should be held before calling this routine. ++ */ ++int novfs_lookup_validate(struct inode *i, struct qstr *name, ino_t ino) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ int retVal = -1; ++ char *n = ""; ++ int nl = 6; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ if (name && name->len) { ++ n = (char *)name->name; ++ nl = name->len; ++ } ++ DbgPrint("inode: 0x%p; name: %.*s; ino: %d", i, nl, n, ino); ++ ++ dc = novfs_lookup_inode_cache(i, name, ino); ++ if (dc) { ++ dc->flags |= ENTRY_VALID; ++ retVal = 0; ++ } ++ } ++ return (retVal); ++} ++ ++/* ++ * Added entry to directory cache. ++ * ++ * DirCacheLock should be held before calling this routine. ++ */ ++int novfs_add_inode_entry(struct inode *i, ++ struct qstr *name, ino_t ino, struct novfs_entry_info *info) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *new; ++ int retVal = -ENOMEM; ++ struct novfs_dir_cache *todel; ++ struct list_head *todeltmp; ++ ++ //SClark ++ DbgPrint("i: %p", i); ++ if ((id = i->i_private)) { ++ DbgPrint("i->i_private: %p", id); ++ if (id->DirCache.next) ++ DbgPrint("id->DirCache.next: %p", id->DirCache.next); ++ } ++ //SClark ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ new = kmalloc(sizeof(struct novfs_dir_cache) + name->len, GFP_KERNEL); ++ if (new) { ++ id->cntDC++; ++ ++ DCCount++; ++ DbgPrint("inode: 0x%p; id: 0x%p; DC: 0x%p; new: 0x%p; " ++ "name: %.*s; ino: %d; size: %lld; mode: 0x%x", ++ i, id, &id->DirCache, new, name->len, ++ name->name, ino, info->size, info->mode); ++ ++ retVal = 0; ++ new->flags = ENTRY_VALID; ++ new->jiffies = get_jiffies_64(); ++ new->size = info->size; ++ new->mode = info->mode; ++ new->atime = info->atime; ++ new->mtime = info->mtime; ++ new->ctime = info->ctime; ++ new->ino = ino; ++ new->hash = name->hash; ++ new->nameLen = name->len; ++ memcpy(new->name, name->name, name->len); ++ new->name[new->nameLen] = '\0'; ++ list_add(&new->list, &id->DirCache); ++ ++ if (id->cntDC > 20) { ++ todeltmp = id->DirCache.prev; ++ todel = list_entry(todeltmp, struct novfs_dir_cache, list); ++ ++ list_del(&todel->list); ++ ++ kfree(todel); ++ ++ DCCount--; ++ id->cntDC--; ++ } ++ ++ } ++ } ++ return (retVal); ++} ++ ++/* ++ * DirCacheLock should be held before calling this routine. ++ */ ++int novfs_update_entry(struct inode *i, struct qstr *name, ino_t ino, ++ struct novfs_entry_info *info) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ int retVal = -1; ++ char *n = ""; ++ int nl = 6; ++ char atime_buf[32]; ++ char mtime_buf[32]; ++ char ctime_buf[32]; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ ++ if (name && name->len) { ++ n = (char *)name->name; ++ nl = name->len; ++ } ++ ctime_r(&info->atime.tv_sec, atime_buf); ++ ctime_r(&info->mtime.tv_sec, mtime_buf); ++ ctime_r(&info->ctime.tv_sec, ctime_buf); ++ DbgPrint("inode: 0x%p; name: %.*s; ino: %d; size: %lld; " ++ "atime: %s; mtime: %s; ctime: %s", ++ i, nl, n, ino, info->size, atime_buf, mtime_buf, ++ ctime_buf); ++ ++ dc = novfs_lookup_inode_cache(i, name, ino); ++ if (dc) { ++ retVal = 0; ++ dc->flags = ENTRY_VALID; ++ dc->jiffies = get_jiffies_64(); ++ dc->size = info->size; ++ dc->mode = info->mode; ++ dc->atime = info->atime; ++ dc->mtime = info->mtime; ++ dc->ctime = info->ctime; ++ ++ ctime_r(&dc->atime.tv_sec, atime_buf); ++ ctime_r(&dc->mtime.tv_sec, mtime_buf); ++ ctime_r(&dc->ctime.tv_sec, ctime_buf); ++ DbgPrint("entry: 0x%p; flags: 0x%x; jiffies: %lld; " ++ "ino: %d; size: %lld; mode: 0%o; atime: %s; " ++ "mtime: %s %d; ctime: %s; hash: 0x%x; " ++ " nameLen: %d; name: %s", ++ dc, dc->flags, dc->jiffies, dc->ino, dc->size, ++ dc->mode, atime_buf, mtime_buf, ++ dc->mtime.tv_nsec, ctime_buf, dc->hash, ++ dc->nameLen, dc->name); ++ } ++ } ++ DbgPrint("return %d", retVal); ++ return (retVal); ++} ++ ++/* ++ * Removes entry from directory cache. You can specify a name ++ * or an inode number. ++ * ++ * DirCacheLock should be held before calling this routine. ++ */ ++void novfs_remove_inode_entry(struct inode *i, struct qstr *name, ino_t ino) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ char *n = ""; ++ int nl = 6; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ dc = novfs_lookup_inode_cache(i, name, ino); ++ if (dc) { ++ if (name && name->name) { ++ nl = name->len; ++ n = (char *)name->name; ++ } ++ DbgPrint("inode: 0x%p; id: 0x%p; DC: 0x%p; " ++ "name: %.*s; ino: %d entry: 0x%p " ++ "[name: %.*s; ino: %d; next: 0x%p; " ++ "prev: 0x%p]", ++ i, id, &id->DirCache, nl, n, ino, dc, ++ dc->nameLen, dc->name, dc->ino, dc->list.next, ++ dc->list.prev); ++ list_del(&dc->list); ++ kfree(dc); ++ DCCount--; ++ ++ id->cntDC--; ++ } ++ } ++} ++ ++/* ++ * Frees all invalid entries in the directory cache. ++ * ++ * DirCacheLock should be held before calling this routine. ++ */ ++void novfs_free_invalid_entries(struct inode *i) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ struct list_head *l; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ list_for_each(l, &id->DirCache) { ++ dc = list_entry(l, struct novfs_dir_cache, list); ++ if (0 == (dc->flags & ENTRY_VALID)) { ++ DbgPrint("inode: 0x%p; id: 0x%p; entry: 0x%p; " ++ "name: %.*s; ino: %d", ++ i, id, dc, dc->nameLen, dc->name, ++ dc->ino); ++ l = l->prev; ++ list_del(&dc->list); ++ kfree(dc); ++ DCCount--; ++ ++ id->cntDC--; ++ } ++ } ++ } ++} ++ ++/* ++ * Frees all entries in the inode cache. ++ * ++ * DirCacheLock should be held before calling this routine. ++ */ ++void novfs_free_inode_cache(struct inode *i) ++{ ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ struct list_head *l; ++ ++ if (i && (id = i->i_private) && id->DirCache.next) { ++ list_for_each(l, &id->DirCache) { ++ dc = list_entry(l, struct novfs_dir_cache, list); ++ l = l->prev; ++ list_del(&dc->list); ++ kfree(dc); ++ DCCount--; ++ ++ id->cntDC--; ++ } ++ } ++} ++ ++void novfs_dump_inode(void *pf) ++{ ++ struct inode *inode; ++ void (*pfunc) (char *Fmt, ...) = pf; ++ struct inode_data *id; ++ struct novfs_dir_cache *dc; ++ struct list_head *il, *l; ++ char atime_buf[32]; ++ char mtime_buf[32]; ++ char ctime_buf[32]; ++ unsigned long icnt = 0, dccnt = 0; ++ ++ down(&InodeList_lock); ++ list_for_each(il, &InodeList) { ++ id = list_entry(il, struct inode_data, IList); ++ inode = id->Inode; ++ if (inode) { ++ icnt++; ++ ++ pfunc("Inode=0x%p I_ino=%d\n", inode, inode->i_ino); ++ ++ pfunc(" atime=%s\n", ++ ctime_r(&inode->i_atime.tv_sec, atime_buf)); ++ pfunc(" ctime=%s\n", ++ ctime_r(&inode->i_mtime.tv_sec, atime_buf)); ++ pfunc(" mtime=%s\n", ++ ctime_r(&inode->i_ctime.tv_sec, atime_buf)); ++ pfunc(" size=%lld\n", inode->i_size); ++ pfunc(" mode=0%o\n", inode->i_mode); ++ pfunc(" count=0%o\n", atomic_read(&inode->i_count)); ++ } ++ ++ pfunc(" nofs_inode_data: 0x%p Name=%s Scope=0x%p\n", id, id->Name, ++ id->Scope); ++ ++ if (id->DirCache.next) { ++ list_for_each(l, &id->DirCache) { ++ dccnt++; ++ dc = list_entry(l, struct novfs_dir_cache, ++ list); ++ ctime_r(&dc->atime.tv_sec, atime_buf); ++ ctime_r(&dc->mtime.tv_sec, mtime_buf); ++ ctime_r(&dc->ctime.tv_sec, ctime_buf); ++ ++ pfunc(" Cache Entry: 0x%p\n" ++ " flags: 0x%x\n" ++ " jiffies: %llu\n" ++ " ino: %u\n" ++ " size: %llu\n" ++ " mode: 0%o\n" ++ " atime: %s\n" ++ " mtime: %s\n" ++ " ctime: %s\n" ++ " hash: 0x%x\n" ++ " len: %d\n" ++ " name: %s\n", ++ dc, dc->flags, dc->jiffies, ++ dc->ino, dc->size, dc->mode, ++ atime_buf, mtime_buf, ctime_buf, ++ dc->hash, dc->nameLen, dc->name); ++ } ++ } ++ } ++ up(&InodeList_lock); ++ ++ pfunc("Inodes: %d(%d) DirCache: %d(%d)\n", InodeCount, icnt, DCCount, ++ dccnt); ++ ++} ++ ++module_init(init_novfs); ++module_exit(exit_novfs); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Novell Inc."); ++MODULE_DESCRIPTION("Novell NetWare Client for Linux"); ++MODULE_VERSION(NOVFS_VERSION_STRING); +--- /dev/null ++++ b/fs/novfs/nwcapi.c +@@ -0,0 +1,2202 @@ ++/* ++ * Novell NCP Redirector for Linux ++ * Author: James Turner/Richard Williams ++ * ++ * This file contains functions used to interface to the library interface of ++ * the daemon. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "nwcapi.h" ++#include "nwerror.h" ++#include "vfs.h" ++#include "commands.h" ++ ++#ifndef strlen_user ++#define strlen_user(str) strnlen_user(str, ~0UL >> 1) ++#endif ++ ++static void GetUserData(struct nwc_scan_conn_info * connInfo, struct novfs_xplat_call_request *cmd, struct novfs_xplat_call_reply *reply); ++static void GetConnData(struct nwc_get_conn_info * connInfo, struct novfs_xplat_call_request *cmd, struct novfs_xplat_call_reply *reply); ++ ++/*++======================================================================*/ ++int novfs_open_conn_by_name(struct novfs_xplat *pdata, void ** Handle, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwd_open_conn_by_name *openConn, *connReply; ++ struct nwc_open_conn_by_name ocbn; ++ int retCode = 0; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ char *data; ++ ++ cpylen = copy_from_user(&ocbn, pdata->reqData, sizeof(ocbn)); ++ datalen = sizeof(*openConn) + strlen_user(ocbn.pName->pString) + strlen_user(ocbn.pServiceType); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_OPEN_CONN_BY_NAME; ++ ++ cmd->dataLen = datalen; ++ openConn = (struct nwd_open_conn_by_name *) cmd->data; ++ ++ openConn->nameLen = strlen_user(ocbn.pName->pString); ++ openConn->serviceLen = strlen_user(ocbn.pServiceType); ++ openConn->uConnFlags = ocbn.uConnFlags; ++ openConn->ConnHandle = Uint32toHandle(ocbn.ConnHandle); ++ data = (char *)openConn; ++ data += sizeof(*openConn); ++ openConn->oName = sizeof(*openConn); ++ ++ openConn->oServiceType = openConn->oName + openConn->nameLen; ++ cpylen = ++ copy_from_user(data, ocbn.pName->pString, ++ openConn->nameLen); ++ data += openConn->nameLen; ++ cpylen = ++ copy_from_user(data, ocbn.pServiceType, ++ openConn->serviceLen); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ /* ++ * we got reply data from the daemon ++ */ ++ connReply = (struct nwd_open_conn_by_name *) reply->data; ++ retCode = reply->Reply.ErrorCode; ++ if (!retCode) { ++ /* ++ * we got valid data. ++ */ ++ connReply = (struct nwd_open_conn_by_name *) reply->data; ++ ocbn.RetConnHandle = HandletoUint32(connReply->newConnHandle); ++ *Handle = connReply->newConnHandle; ++ ++ cpylen = copy_to_user(pdata->reqData, &ocbn, sizeof(ocbn)); ++ DbgPrint("New Conn Handle = %X", connReply->newConnHandle); ++ } ++ kfree(reply); ++ } ++ ++ kfree(cmd); ++ return ((int)retCode); ++ ++} ++ ++int novfs_open_conn_by_addr(struct novfs_xplat *pdata, void ** Handle, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwd_open_conn_by_addr *openConn, *connReply; ++ struct nwc_open_conn_by_addr ocba; ++ struct nwc_tran_addr tranAddr; ++ int retCode = 0; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ char addr[MAX_ADDRESS_LENGTH]; ++ ++ cpylen = copy_from_user(&ocba, pdata->reqData, sizeof(ocba)); ++ datalen = sizeof(*openConn); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_OPEN_CONN_BY_ADDRESS; ++ cmd->dataLen = datalen; ++ openConn = (struct nwd_open_conn_by_addr *) cmd->data; ++ ++ cpylen = ++ copy_from_user(&tranAddr, ocba.pTranAddr, sizeof(tranAddr)); ++ ++ DbgPrint("tranAddr"); ++ novfs_dump(sizeof(tranAddr), &tranAddr); ++ ++ openConn->TranAddr.uTransportType = tranAddr.uTransportType; ++ openConn->TranAddr.uAddressLength = tranAddr.uAddressLength; ++ memset(addr, 0xcc, sizeof(addr) - 1); ++ ++ cpylen = ++ copy_from_user(addr, tranAddr.puAddress, ++ tranAddr.uAddressLength); ++ ++ DbgPrint("addr"); ++ novfs_dump(sizeof(addr), addr); ++ ++ openConn->TranAddr.oAddress = *(unsigned int *) (&addr[2]); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ /* ++ * we got reply data from the daemon ++ */ ++ connReply = (struct nwd_open_conn_by_addr *) reply->data; ++ retCode = reply->Reply.ErrorCode; ++ if (!retCode) { ++ /* ++ * we got valid data. ++ */ ++ connReply = (struct nwd_open_conn_by_addr *) reply->data; ++ ocba.ConnHandle = ++ HandletoUint32(connReply->ConnHandle); ++ *Handle = connReply->ConnHandle; ++ cpylen = ++ copy_to_user(pdata->reqData, &ocba, ++ sizeof(ocba)); ++ DbgPrint("New Conn Handle = %X", connReply->ConnHandle); ++ } ++ kfree(reply); ++ } ++ ++ kfree(cmd); ++ ++ return (retCode); ++ ++} ++ ++int novfs_open_conn_by_ref(struct novfs_xplat *pdata, void ** Handle, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwd_open_conn_by_ref *openConn; ++ struct nwc_open_conn_by_ref ocbr; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = copy_from_user(&ocbr, pdata->reqData, sizeof(ocbr)); ++ datalen = sizeof(*openConn); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_OPEN_CONN_BY_REFERENCE; ++ cmd->dataLen = datalen; ++ openConn = (struct nwd_open_conn_by_ref *) cmd->data; ++ ++ openConn->uConnReference = ++ (void *) (unsigned long) ocbr.uConnReference; ++ openConn->uConnFlags = ocbr.uConnFlags; ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ /* ++ * we got reply data from the daemon ++ */ ++ openConn = (struct nwd_open_conn_by_ref *) reply->data; ++ retCode = reply->Reply.ErrorCode; ++ if (!retCode) { ++ /* ++ * we got valid data. ++ */ ++ ocbr.ConnHandle = ++ HandletoUint32(openConn->ConnHandle); ++ *Handle = openConn->ConnHandle; ++ ++ cpylen = ++ copy_to_user(pdata->reqData, &ocbr, ++ sizeof(ocbr)); ++ DbgPrint("New Conn Handle = %X", openConn->ConnHandle); ++ } ++ kfree(reply); ++ } ++ ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_raw_send(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct nwc_request xRequest; ++ struct nwc_frag *frag, *cFrag, *reqFrag; ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen, totalLen; ++ unsigned int x; ++ struct nwd_ncp_req *ncpData; ++ struct nwd_ncp_rep *ncpReply; ++ unsigned char *reqData; ++ unsigned long actualReplyLength = 0; ++ ++ DbgPrint("[XPLAT] Process Raw NCP Send"); ++ cpylen = copy_from_user(&xRequest, pdata->reqData, sizeof(xRequest)); ++ ++ /* ++ * Figure out the length of the request ++ */ ++ frag = ++ kmalloc(xRequest.uNumReplyFrags * sizeof(struct nwc_frag), GFP_KERNEL); ++ ++ DbgPrint("[XPLAT RawNCP] - Reply Frag Count 0x%X", ++ xRequest.uNumReplyFrags); ++ ++ if (!frag) ++ return (retCode); ++ ++ cpylen = ++ copy_from_user(frag, xRequest.pReplyFrags, ++ xRequest.uNumReplyFrags * sizeof(struct nwc_frag)); ++ totalLen = 0; ++ ++ cFrag = frag; ++ for (x = 0; x < xRequest.uNumReplyFrags; x++) { ++ DbgPrint("[XPLAT - RawNCP] - Frag Len = %d", cFrag->uLength); ++ totalLen += cFrag->uLength; ++ cFrag++; ++ } ++ ++ DbgPrint("[XPLAT - RawNCP] - totalLen = %d", totalLen); ++ datalen = 0; ++ reqFrag = ++ kmalloc(xRequest.uNumRequestFrags * sizeof(struct nwc_frag), ++ GFP_KERNEL); ++ if (!reqFrag) { ++ kfree(frag); ++ return (retCode); ++ } ++ ++ cpylen = ++ copy_from_user(reqFrag, xRequest.pRequestFrags, ++ xRequest.uNumRequestFrags * sizeof(struct nwc_frag)); ++ cFrag = reqFrag; ++ for (x = 0; x < xRequest.uNumRequestFrags; x++) { ++ datalen += cFrag->uLength; ++ cFrag++; ++ } ++ ++ /* ++ * Allocate the cmd Request ++ */ ++ cmdlen = datalen + sizeof(*cmd) + sizeof(*ncpData); ++ DbgPrint("[XPLAT RawNCP] - Frag Count 0x%X", ++ xRequest.uNumRequestFrags); ++ DbgPrint("[XPLAT RawNCP] - Total Command Data Len = %x", cmdlen); ++ ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_RAW_NCP_REQUEST; ++ ++ /* ++ * build the NCP Request ++ */ ++ cmd->dataLen = cmdlen - sizeof(*cmd); ++ ncpData = (struct nwd_ncp_req *) cmd->data; ++ ncpData->replyLen = totalLen; ++ ncpData->requestLen = datalen; ++ ncpData->ConnHandle = (void *) (unsigned long) xRequest.ConnHandle; ++ ncpData->function = xRequest.uFunction; ++ ++ reqData = ncpData->data; ++ cFrag = reqFrag; ++ ++ for (x = 0; x < xRequest.uNumRequestFrags; x++) { ++ cpylen = ++ copy_from_user(reqData, cFrag->pData, ++ cFrag->uLength); ++ reqData += cFrag->uLength; ++ cFrag++; ++ } ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ DbgPrint("RawNCP - reply = %x", reply); ++ DbgPrint("RawNCP - retCode = %x", retCode); ++ ++ if (reply) { ++ /* ++ * we got reply data from the daemon ++ */ ++ ncpReply = (struct nwd_ncp_rep *) reply->data; ++ retCode = reply->Reply.ErrorCode; ++ ++ DbgPrint("RawNCP - Reply Frag Count 0x%X", ++ xRequest.uNumReplyFrags); ++ ++ /* ++ * We need to copy the reply frags to the packet. ++ */ ++ reqData = ncpReply->data; ++ cFrag = frag; ++ ++ totalLen = ncpReply->replyLen; ++ for (x = 0; x < xRequest.uNumReplyFrags; x++) { ++ ++ DbgPrint("RawNCP - Copy Frag %d: 0x%X", x, ++ cFrag->uLength); ++ ++ datalen = ++ min((unsigned long) cFrag->uLength, totalLen); ++ ++ cpylen = ++ copy_to_user(cFrag->pData, reqData, ++ datalen); ++ totalLen -= datalen; ++ reqData += datalen; ++ actualReplyLength += datalen; ++ ++ cFrag++; ++ } ++ ++ kfree(reply); ++ } else { ++ retCode = -EIO; ++ } ++ ++ kfree(cmd); ++ xRequest.uActualReplyLength = actualReplyLength; ++ cpylen = copy_to_user(pdata->reqData, &xRequest, sizeof(xRequest)); ++ ++ kfree(reqFrag); ++ kfree(frag); ++ ++ return (retCode); ++} ++ ++int novfs_conn_close(struct novfs_xplat *pdata, void ** Handle, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_close_conn cc; ++ struct nwd_close_conn *nwdClose; ++ int retCode = 0; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = copy_from_user(&cc, pdata->reqData, sizeof(cc)); ++ ++ datalen = sizeof(*nwdClose); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_CLOSE_CONN; ++ ++ nwdClose = (struct nwd_close_conn *) cmd->data; ++ cmd->dataLen = sizeof(*nwdClose); ++ *Handle = nwdClose->ConnHandle = Uint32toHandle(cc.ConnHandle); ++ ++ /* ++ * send the request ++ */ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, 0); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_sys_conn_close(struct novfs_xplat *pdata, unsigned long *Handle, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_close_conn cc; ++ struct nwd_close_conn *nwdClose; ++ unsigned int retCode = 0; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = copy_from_user(&cc, pdata->reqData, sizeof(cc)); ++ ++ datalen = sizeof(*nwdClose); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_SYS_CLOSE_CONN; ++ ++ nwdClose = (struct nwd_close_conn *) cmd->data; ++ cmd->dataLen = sizeof(*nwdClose); ++ nwdClose->ConnHandle = (void *) (unsigned long) cc.ConnHandle; ++ *Handle = (unsigned long) cc.ConnHandle; ++ ++ /* ++ * send the request ++ */ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, (void **)&reply, &replylen, 0); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_login_id(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct nwc_login_id lgn, *plgn; ++ int retCode = -ENOMEM; ++ struct ncl_string server; ++ struct ncl_string username; ++ struct ncl_string password; ++ unsigned long cpylen; ++ struct nwc_string nwcStr; ++ ++ cpylen = copy_from_user(&lgn, pdata->reqData, sizeof(lgn)); ++ ++ DbgPrint(""); ++ novfs_dump(sizeof(lgn), &lgn); ++ ++ cpylen = copy_from_user(&nwcStr, lgn.pDomainName, sizeof(nwcStr)); ++ DbgPrint("DomainName\n"); ++ novfs_dump(sizeof(nwcStr), &nwcStr); ++ ++ if ((server.buffer = kmalloc(nwcStr.DataLen, GFP_KERNEL))) { ++ server.type = nwcStr.DataType; ++ server.len = nwcStr.DataLen; ++ if (!copy_from_user((void *)server.buffer, nwcStr.pBuffer, server.len)) { ++ DbgPrint("Server"); ++ novfs_dump(server.len, server.buffer); ++ ++ cpylen = copy_from_user(&nwcStr, lgn.pObjectName, sizeof(nwcStr)); ++ DbgPrint("ObjectName"); ++ novfs_dump(sizeof(nwcStr), &nwcStr); ++ ++ if ((username.buffer = kmalloc(nwcStr.DataLen, GFP_KERNEL))) { ++ username.type = nwcStr.DataType; ++ username.len = nwcStr.DataLen; ++ if (!copy_from_user((void *)username.buffer, nwcStr.pBuffer, username.len)) { ++ DbgPrint("User"); ++ novfs_dump(username.len, username.buffer); ++ ++ cpylen = copy_from_user(&nwcStr, lgn.pPassword, sizeof(nwcStr)); ++ DbgPrint("Password"); ++ novfs_dump(sizeof(nwcStr), &nwcStr); ++ ++ if ((password.buffer = kmalloc(nwcStr.DataLen, GFP_KERNEL))) { ++ password.type = nwcStr.DataType; ++ password.len = nwcStr.DataLen; ++ if (!copy_from_user((void *)password.buffer, nwcStr.pBuffer, password.len)) { ++ retCode = novfs_do_login(&server, &username, &password, (void **)&lgn.AuthenticationId, &Session); ++ if (retCode) { ++ lgn.AuthenticationId = 0; ++ } ++ ++ plgn = (struct nwc_login_id *)pdata->reqData; ++ cpylen = copy_to_user(&plgn->AuthenticationId, &lgn.AuthenticationId, sizeof(plgn->AuthenticationId)); ++ } ++ memset(password.buffer, 0, password.len); ++ kfree(password.buffer); ++ } ++ } ++ memset(username.buffer, 0, username.len); ++ kfree(username.buffer); ++ } ++ } ++ kfree(server.buffer); ++ } ++ return (retCode); ++} ++ ++int novfs_auth_conn(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct nwc_auth_with_id pauth; ++ struct nwc_auth_wid *pDauth; ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ datalen = sizeof(*pDauth); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_AUTHENTICATE_CONN_WITH_ID; ++ ++ cpylen = copy_from_user(&pauth, pdata->reqData, sizeof(pauth)); ++ ++ pDauth = (struct nwc_auth_wid *) cmd->data; ++ cmd->dataLen = datalen; ++ pDauth->AuthenticationId = pauth.AuthenticationId; ++ pDauth->ConnHandle = (void *) (unsigned long) pauth.ConnHandle; ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ return (retCode); ++} ++ ++int novfs_license_conn(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_license_conn lisc; ++ struct nwc_lisc_id * pDLisc; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ datalen = sizeof(*pDLisc); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_LICENSE_CONN; ++ ++ cpylen = copy_from_user(&lisc, pdata->reqData, sizeof(lisc)); ++ ++ pDLisc = (struct nwc_lisc_id *) cmd->data; ++ cmd->dataLen = datalen; ++ pDLisc->ConnHandle = (void *) (unsigned long) lisc.ConnHandle; ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++} ++ ++int novfs_logout_id(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_lo_id logout, *pDLogout; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ datalen = sizeof(*pDLogout); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_LOGOUT_IDENTITY; ++ ++ cpylen = ++ copy_from_user(&logout, pdata->reqData, sizeof(logout)); ++ ++ pDLogout = (struct nwc_lo_id *) cmd->data; ++ cmd->dataLen = datalen; ++ pDLogout->AuthenticationId = logout.AuthenticationId; ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++} ++ ++int novfs_unlicense_conn(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_unlic_conn *pUconn, ulc; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = copy_from_user(&ulc, pdata->reqData, sizeof(ulc)); ++ datalen = sizeof(*pUconn); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_UNLICENSE_CONN; ++ cmd->dataLen = datalen; ++ pUconn = (struct nwc_unlic_conn *) cmd->data; ++ ++ pUconn->ConnHandle = (void *) (unsigned long) ulc.ConnHandle; ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ /* ++ * we got reply data from the daemon ++ */ ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ return (retCode); ++ ++} ++ ++int novfs_unauthenticate(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_unauthenticate auth, *pDAuth; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ datalen = sizeof(*pDAuth); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = (struct novfs_xplat_call_request *)kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_UNAUTHENTICATE_CONN; ++ ++ cpylen = copy_from_user(&auth, pdata->reqData, sizeof(auth)); ++ ++ pDAuth = (struct nwc_unauthenticate *) cmd->data; ++ cmd->dataLen = datalen; ++ pDAuth->AuthenticationId = auth.AuthenticationId; ++ pDAuth->ConnHandle = (void *) (unsigned long) auth.ConnHandle; ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_get_conn_info(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_get_conn_info connInfo; ++ struct nwd_conn_info *pDConnInfo; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, replylen, cpylen; ++ ++ cmdlen = sizeof(*cmd) + sizeof(*pDConnInfo); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ cpylen = ++ copy_from_user(&connInfo, pdata->reqData, sizeof(struct nwc_get_conn_info)); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_GET_CONN_INFO; ++ ++ pDConnInfo = (struct nwd_conn_info *) cmd->data; ++ ++ pDConnInfo->ConnHandle = (void *) (unsigned long) connInfo.ConnHandle; ++ pDConnInfo->uInfoLevel = connInfo.uInfoLevel; ++ pDConnInfo->uInfoLength = connInfo.uInfoLength; ++ cmd->dataLen = sizeof(*pDConnInfo); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ if (!retCode) { ++ GetConnData(&connInfo, cmd, reply); ++ } ++ ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_set_conn_info(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_set_conn_info connInfo; ++ struct nwd_set_conn_info *pDConnInfo; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, replylen, cpylen; ++ ++ cmdlen = sizeof(*cmd) + sizeof(*pDConnInfo); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ cpylen = ++ copy_from_user(&connInfo, pdata->reqData, sizeof(struct nwc_set_conn_info)); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_SET_CONN_INFO; ++ ++ pDConnInfo = (struct nwd_set_conn_info *) cmd->data; ++ ++ pDConnInfo->ConnHandle = (void *) (unsigned long) connInfo.ConnHandle; ++ pDConnInfo->uInfoLevel = connInfo.uInfoLevel; ++ pDConnInfo->uInfoLength = connInfo.uInfoLength; ++ cmd->dataLen = sizeof(*pDConnInfo); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_get_id_info(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_get_id_info qidInfo, *gId; ++ struct nwd_get_id_info *idInfo; ++ struct nwc_string xferStr; ++ char *str; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, replylen, cpylen; ++ ++ cmdlen = sizeof(*cmd) + sizeof(*idInfo); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ cpylen = copy_from_user(&qidInfo, pdata->reqData, sizeof(qidInfo)); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_GET_IDENTITY_INFO; ++ ++ idInfo = (struct nwd_get_id_info *) cmd->data; ++ ++ idInfo->AuthenticationId = qidInfo.AuthenticationId; ++ cmd->dataLen = sizeof(*idInfo); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ ++ if (!reply->Reply.ErrorCode) { ++ /* ++ * Save the return info to the user structure. ++ */ ++ gId = pdata->reqData; ++ idInfo = (struct nwd_get_id_info *) reply->data; ++ cpylen = ++ copy_to_user(&gId->AuthenticationId, ++ &idInfo->AuthenticationId, ++ sizeof(idInfo-> ++ AuthenticationId)); ++ cpylen = ++ copy_to_user(&gId->AuthType, ++ &idInfo->AuthType, ++ sizeof(idInfo->AuthType)); ++ cpylen = ++ copy_to_user(&gId->IdentityFlags, ++ &idInfo->IdentityFlags, ++ sizeof(idInfo->IdentityFlags)); ++ cpylen = ++ copy_to_user(&gId->NameType, ++ &idInfo->NameType, ++ sizeof(idInfo->NameType)); ++ cpylen = ++ copy_to_user(&gId->ObjectType, ++ &idInfo->ObjectType, ++ sizeof(idInfo->ObjectType)); ++ ++ cpylen = ++ copy_from_user(&xferStr, gId->pDomainName, ++ sizeof(struct nwc_string)); ++ str = ++ (char *)((char *)reply->data + ++ idInfo->pDomainNameOffset); ++ cpylen = ++ copy_to_user(xferStr.pBuffer, str, ++ idInfo->domainLen); ++ xferStr.DataType = NWC_STRING_TYPE_ASCII; ++ xferStr.DataLen = idInfo->domainLen; ++ cpylen = ++ copy_to_user(gId->pDomainName, &xferStr, ++ sizeof(struct nwc_string)); ++ ++ cpylen = ++ copy_from_user(&xferStr, gId->pObjectName, ++ sizeof(struct nwc_string)); ++ str = ++ (char *)((char *)reply->data + ++ idInfo->pObjectNameOffset); ++ cpylen = ++ copy_to_user(xferStr.pBuffer, str, ++ idInfo->objectLen); ++ xferStr.DataLen = idInfo->objectLen - 1; ++ xferStr.DataType = NWC_STRING_TYPE_ASCII; ++ cpylen = ++ copy_to_user(gId->pObjectName, &xferStr, ++ sizeof(struct nwc_string)); ++ } ++ ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++} ++ ++int novfs_scan_conn_info(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_scan_conn_info connInfo, *rInfo; ++ struct nwd_scan_conn_info *pDConnInfo; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, replylen, cpylen; ++ unsigned char *localData; ++ ++ cpylen = ++ copy_from_user(&connInfo, pdata->reqData, sizeof(struct nwc_scan_conn_info)); ++ ++ cmdlen = sizeof(*cmd) + sizeof(*pDConnInfo) + connInfo.uScanInfoLen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_SCAN_CONN_INFO; ++ ++ pDConnInfo = (struct nwd_scan_conn_info *) cmd->data; ++ ++ DbgPrint("Input Data"); ++ __DbgPrint(" connInfo.uScanIndex = 0x%X\n", connInfo.uScanIndex); ++ __DbgPrint(" connInfo.uConnectionReference = 0x%X\n", ++ connInfo.uConnectionReference); ++ __DbgPrint(" connInfo.uScanInfoLevel = 0x%X\n", ++ connInfo.uScanInfoLevel); ++ __DbgPrint(" connInfo.uScanInfoLen = 0x%X\n", ++ connInfo.uScanInfoLen); ++ __DbgPrint(" connInfo.uReturnInfoLength = 0x%X\n", ++ connInfo.uReturnInfoLength); ++ __DbgPrint(" connInfo.uReturnInfoLevel = 0x%X\n", ++ connInfo.uReturnInfoLevel); ++ __DbgPrint(" connInfo.uScanFlags = 0x%X\n", connInfo.uScanFlags); ++ ++ pDConnInfo->uScanIndex = connInfo.uScanIndex; ++ pDConnInfo->uConnectionReference = ++ connInfo.uConnectionReference; ++ pDConnInfo->uScanInfoLevel = connInfo.uScanInfoLevel; ++ pDConnInfo->uScanInfoLen = connInfo.uScanInfoLen; ++ pDConnInfo->uReturnInfoLength = connInfo.uReturnInfoLength; ++ pDConnInfo->uReturnInfoLevel = connInfo.uReturnInfoLevel; ++ pDConnInfo->uScanFlags = connInfo.uScanFlags; ++ ++ if (pDConnInfo->uScanInfoLen) { ++ localData = (unsigned char *) pDConnInfo; ++ pDConnInfo->uScanConnInfoOffset = sizeof(*pDConnInfo); ++ localData += pDConnInfo->uScanConnInfoOffset; ++ cpylen = ++ copy_from_user(localData, connInfo.pScanConnInfo, ++ connInfo.uScanInfoLen); ++ } else { ++ pDConnInfo->uScanConnInfoOffset = 0; ++ } ++ ++ cmd->dataLen = sizeof(*pDConnInfo); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ DbgPrint("Reply recieved"); ++ __DbgPrint(" NextIndex = %x\n", connInfo.uScanIndex); ++ __DbgPrint(" ErrorCode = %x\n", reply->Reply.ErrorCode); ++ __DbgPrint(" data = %p\n", reply->data); ++ ++ pDConnInfo = (struct nwd_scan_conn_info *) reply->data; ++ retCode = (unsigned long) reply->Reply.ErrorCode; ++ if (!retCode) { ++ GetUserData(&connInfo, cmd, reply); ++ rInfo = (struct nwc_scan_conn_info *) pdata->repData; ++ cpylen = ++ copy_to_user(pdata->repData, ++ &pDConnInfo->uScanIndex, ++ sizeof(pDConnInfo-> ++ uScanIndex)); ++ cpylen = ++ copy_to_user(&rInfo->uConnectionReference, ++ &pDConnInfo-> ++ uConnectionReference, ++ sizeof(pDConnInfo-> ++ uConnectionReference)); ++ } else { ++ unsigned long x; ++ ++ x = 0; ++ rInfo = (struct nwc_scan_conn_info *) pdata->reqData; ++ cpylen = ++ copy_to_user(&rInfo->uConnectionReference, ++ &x, ++ sizeof(rInfo-> ++ uConnectionReference)); ++ } ++ ++ kfree(reply); ++ } else { ++ retCode = -EIO; ++ } ++ kfree(cmd); ++ return (retCode); ++} ++ ++/* ++ * Copies the user data out of the scan conn info call. ++ */ ++static void GetUserData(struct nwc_scan_conn_info * connInfo, struct novfs_xplat_call_request *cmd, struct novfs_xplat_call_reply *reply) ++{ ++ unsigned long uLevel; ++ struct nwd_scan_conn_info *pDConnInfo; ++ ++ unsigned char *srcData = NULL; ++ unsigned long dataLen = 0, cpylen; ++ ++ pDConnInfo = (struct nwd_scan_conn_info *) reply->data; ++ uLevel = pDConnInfo->uReturnInfoLevel; ++ DbgPrint("uLevel = %d, reply = 0x%p, reply->data = 0x%X", ++ uLevel, reply, reply->data); ++ ++ switch (uLevel) { ++ case NWC_CONN_INFO_RETURN_ALL: ++ case NWC_CONN_INFO_NDS_STATE: ++ case NWC_CONN_INFO_MAX_PACKET_SIZE: ++ case NWC_CONN_INFO_LICENSE_STATE: ++ case NWC_CONN_INFO_PUBLIC_STATE: ++ case NWC_CONN_INFO_SERVICE_TYPE: ++ case NWC_CONN_INFO_DISTANCE: ++ case NWC_CONN_INFO_SERVER_VERSION: ++ case NWC_CONN_INFO_AUTH_ID: ++ case NWC_CONN_INFO_SUSPENDED: ++ case NWC_CONN_INFO_WORKGROUP_ID: ++ case NWC_CONN_INFO_SECURITY_STATE: ++ case NWC_CONN_INFO_CONN_NUMBER: ++ case NWC_CONN_INFO_USER_ID: ++ case NWC_CONN_INFO_BCAST_STATE: ++ case NWC_CONN_INFO_CONN_REF: ++ case NWC_CONN_INFO_AUTH_STATE: ++ case NWC_CONN_INFO_TREE_NAME: ++ case NWC_CONN_INFO_SERVER_NAME: ++ case NWC_CONN_INFO_VERSION: ++ srcData = (unsigned char *) pDConnInfo; ++ srcData += pDConnInfo->uReturnConnInfoOffset; ++ dataLen = pDConnInfo->uReturnInfoLength; ++ break; ++ ++ case NWC_CONN_INFO_TRAN_ADDR: ++ { ++ unsigned char *dstData = connInfo->pReturnConnInfo; ++ struct nwc_tran_addr tranAddr; ++ ++ srcData = (unsigned char *) reply->data; ++ dataLen = reply->dataLen; ++ ++ DbgPrint("NWC_CONN_INFO_TRAN_ADDR 0x%p -> 0x%p :: 0x%X", ++ srcData, connInfo->pReturnConnInfo, dataLen); ++ ++ cpylen = ++ copy_from_user(&tranAddr, dstData, ++ sizeof(tranAddr)); ++ ++ srcData += ++ ((struct nwd_scan_conn_info *) srcData)-> ++ uReturnConnInfoOffset; ++ ++ tranAddr.uTransportType = ++ ((struct nwd_tran_addr *) srcData)->uTransportType; ++ tranAddr.uAddressLength = ++ ((struct tagNwdTranAddrEx *) srcData)->uAddressLength; ++ ++ cpylen = ++ copy_to_user(dstData, &tranAddr, sizeof(tranAddr)); ++ cpylen = ++ copy_to_user(tranAddr.puAddress, ++ ((struct tagNwdTranAddrEx *) srcData)->Buffer, ++ ((struct tagNwdTranAddrEx *) srcData)-> ++ uAddressLength); ++ dataLen = 0; ++ break; ++ } ++ case NWC_CONN_INFO_RETURN_NONE: ++ case NWC_CONN_INFO_TREE_NAME_UNICODE: ++ case NWC_CONN_INFO_SERVER_NAME_UNICODE: ++ case NWC_CONN_INFO_LOCAL_TRAN_ADDR: ++ case NWC_CONN_INFO_ALTERNATE_ADDR: ++ case NWC_CONN_INFO_SERVER_GUID: ++ default: ++ break; ++ } ++ ++ if (srcData && dataLen) { ++ DbgPrint("Copy Data 0x%p -> 0x%p :: 0x%X", ++ srcData, connInfo->pReturnConnInfo, dataLen); ++ cpylen = ++ copy_to_user(connInfo->pReturnConnInfo, srcData, dataLen); ++ } ++ ++ return; ++} ++ ++/* ++ * Copies the user data out of the scan conn info call. ++ */ ++static void GetConnData(struct nwc_get_conn_info * connInfo, struct novfs_xplat_call_request *cmd, struct novfs_xplat_call_reply *reply) ++{ ++ unsigned long uLevel; ++ struct nwd_conn_info * pDConnInfo; ++ ++ unsigned char *srcData = NULL; ++ unsigned long dataLen = 0, cpylen; ++ ++ pDConnInfo = (struct nwd_conn_info *) cmd->data; ++ uLevel = pDConnInfo->uInfoLevel; ++ ++ switch (uLevel) { ++ case NWC_CONN_INFO_RETURN_ALL: ++ srcData = (unsigned char *) reply->data; ++ dataLen = reply->dataLen; ++ break; ++ ++ case NWC_CONN_INFO_RETURN_NONE: ++ dataLen = 0; ++ break; ++ ++ case NWC_CONN_INFO_TRAN_ADDR: ++ { ++ unsigned char *dstData = connInfo->pConnInfo; ++ struct nwc_tran_addr tranAddr; ++ ++ srcData = (unsigned char *) reply->data; ++ ++ cpylen = ++ copy_from_user(&tranAddr, dstData, ++ sizeof(tranAddr)); ++ tranAddr.uTransportType = ++ ((struct tagNwdTranAddrEx *) srcData)->uTransportType; ++ tranAddr.uAddressLength = ++ ((struct tagNwdTranAddrEx *) srcData)->uAddressLength; ++ ++ cpylen = ++ copy_to_user(dstData, &tranAddr, sizeof(tranAddr)); ++ cpylen = ++ copy_to_user(tranAddr.puAddress, ++ ((struct tagNwdTranAddrEx *) srcData)->Buffer, ++ ((struct tagNwdTranAddrEx *) srcData)-> ++ uAddressLength); ++ dataLen = 0; ++ break; ++ } ++ case NWC_CONN_INFO_NDS_STATE: ++ case NWC_CONN_INFO_MAX_PACKET_SIZE: ++ case NWC_CONN_INFO_LICENSE_STATE: ++ case NWC_CONN_INFO_PUBLIC_STATE: ++ case NWC_CONN_INFO_SERVICE_TYPE: ++ case NWC_CONN_INFO_DISTANCE: ++ case NWC_CONN_INFO_SERVER_VERSION: ++ case NWC_CONN_INFO_AUTH_ID: ++ case NWC_CONN_INFO_SUSPENDED: ++ case NWC_CONN_INFO_WORKGROUP_ID: ++ case NWC_CONN_INFO_SECURITY_STATE: ++ case NWC_CONN_INFO_CONN_NUMBER: ++ case NWC_CONN_INFO_USER_ID: ++ case NWC_CONN_INFO_BCAST_STATE: ++ case NWC_CONN_INFO_CONN_REF: ++ case NWC_CONN_INFO_AUTH_STATE: ++ case NWC_CONN_INFO_VERSION: ++ case NWC_CONN_INFO_SERVER_NAME: ++ case NWC_CONN_INFO_TREE_NAME: ++ srcData = (unsigned char *) reply->data; ++ dataLen = reply->dataLen; ++ break; ++ ++ case NWC_CONN_INFO_TREE_NAME_UNICODE: ++ case NWC_CONN_INFO_SERVER_NAME_UNICODE: ++ break; ++ ++ case NWC_CONN_INFO_LOCAL_TRAN_ADDR: ++ break; ++ ++ case NWC_CONN_INFO_ALTERNATE_ADDR: ++ break; ++ ++ case NWC_CONN_INFO_SERVER_GUID: ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (srcData && dataLen) { ++ cpylen = ++ copy_to_user(connInfo->pConnInfo, srcData, ++ connInfo->uInfoLength); ++ } ++ ++ return; ++} ++ ++int novfs_get_daemon_ver(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwd_get_reqversion *pDVersion; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ ++ datalen = sizeof(*pDVersion); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_GET_REQUESTER_VERSION; ++ cmdlen = sizeof(*cmd); ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ pDVersion = (struct nwd_get_reqversion *) reply->data; ++ cpylen = ++ copy_to_user(pDVersion, pdata->reqData, ++ sizeof(*pDVersion)); ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_get_preferred_DS_tree(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwd_get_pref_ds_tree *pDGetTree; ++ struct nwc_get_pref_ds_tree xplatCall, *p; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ unsigned char *dPtr; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, ++ sizeof(struct nwc_get_pref_ds_tree)); ++ datalen = sizeof(*pDGetTree) + xplatCall.uTreeLength; ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_GET_PREFERRED_DS_TREE; ++ cmdlen = sizeof(*cmd); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ if (!retCode) { ++ pDGetTree = ++ (struct nwd_get_pref_ds_tree *) reply->data; ++ dPtr = ++ reply->data + pDGetTree->DsTreeNameOffset; ++ p = (struct nwc_get_pref_ds_tree *) pdata->reqData; ++ ++ DbgPrint("Reply recieved"); ++ __DbgPrint(" TreeLen = %x\n", ++ pDGetTree->uTreeLength); ++ __DbgPrint(" TreeName = %s\n", dPtr); ++ ++ cpylen = ++ copy_to_user(p, &pDGetTree->uTreeLength, 4); ++ cpylen = ++ copy_to_user(xplatCall.pDsTreeName, dPtr, ++ pDGetTree->uTreeLength); ++ } ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_set_preferred_DS_tree(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwd_set_pref_ds_tree *pDSetTree; ++ struct nwc_set_pref_ds_tree xplatCall; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ unsigned char *dPtr; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, ++ sizeof(struct nwc_set_pref_ds_tree)); ++ datalen = sizeof(*pDSetTree) + xplatCall.uTreeLength; ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_SET_PREFERRED_DS_TREE; ++ ++ pDSetTree = (struct nwd_set_pref_ds_tree *) cmd->data; ++ pDSetTree->DsTreeNameOffset = sizeof(*pDSetTree); ++ pDSetTree->uTreeLength = xplatCall.uTreeLength; ++ ++ dPtr = cmd->data + sizeof(*pDSetTree); ++ cpylen = ++ copy_from_user(dPtr, xplatCall.pDsTreeName, ++ xplatCall.uTreeLength); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_set_default_ctx(struct novfs_xplat *pdata, ++ struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_set_def_name_ctx xplatCall; ++ struct nwd_set_def_name_ctx * pDSet; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, datalen, replylen, cpylen; ++ unsigned char *dPtr; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, ++ sizeof(struct nwc_set_def_name_ctx)); ++ datalen = ++ sizeof(*pDSet) + xplatCall.uTreeLength + xplatCall.uNameLength; ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_SET_DEFAULT_NAME_CONTEXT; ++ cmd->dataLen = ++ sizeof(struct nwd_set_def_name_ctx) + ++ xplatCall.uTreeLength + xplatCall.uNameLength; ++ ++ pDSet = (struct nwd_set_def_name_ctx *) cmd->data; ++ dPtr = cmd->data; ++ ++ pDSet->TreeOffset = sizeof(struct nwd_set_def_name_ctx); ++ pDSet->uTreeLength = xplatCall.uTreeLength; ++ pDSet->NameContextOffset = ++ pDSet->TreeOffset + xplatCall.uTreeLength; ++ pDSet->uNameLength = xplatCall.uNameLength; ++ ++ //sgled cpylen = copy_from_user(dPtr+pDSet->TreeOffset, xplatCall.pTreeName, xplatCall.uTreeLength); ++ cpylen = copy_from_user(dPtr + pDSet->TreeOffset, xplatCall.pDsTreeName, xplatCall.uTreeLength); //sgled ++ cpylen = ++ copy_from_user(dPtr + pDSet->NameContextOffset, ++ xplatCall.pNameContext, ++ xplatCall.uNameLength); ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_get_default_ctx(struct novfs_xplat *pdata, ++ struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_get_def_name_ctx xplatCall; ++ struct nwd_get_def_name_ctx * pGet; ++ char *dPtr; ++ int retCode = -ENOMEM; ++ unsigned long cmdlen, replylen, cpylen; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, ++ sizeof(struct nwc_get_def_name_ctx)); ++ cmdlen = ++ sizeof(*cmd) + sizeof(struct nwd_get_def_name_ctx ) + ++ xplatCall.uTreeLength; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_GET_DEFAULT_NAME_CONTEXT; ++ cmd->dataLen = ++ sizeof(struct nwd_get_def_name_ctx) + xplatCall.uTreeLength; ++ ++ pGet = (struct nwd_get_def_name_ctx *) cmd->data; ++ dPtr = cmd->data; ++ ++ pGet->TreeOffset = sizeof(struct nwd_get_def_name_ctx ); ++ pGet->uTreeLength = xplatCall.uTreeLength; ++ ++ //sgled cpylen = copy_from_user( dPtr + pGet->TreeOffset, xplatCall.pTreeName, xplatCall.uTreeLength); ++ cpylen = copy_from_user(dPtr + pGet->TreeOffset, xplatCall.pDsTreeName, xplatCall.uTreeLength); //sgled ++ dPtr[pGet->TreeOffset + pGet->uTreeLength] = 0; ++ ++ retCode = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ retCode = reply->Reply.ErrorCode; ++ if (!retCode) { ++ pGet = (struct nwd_get_def_name_ctx *) reply->data; ++ ++ DbgPrint("retCode=0x%x uNameLength1=%d uNameLength2=%d", ++ retCode, pGet->uNameLength, ++ xplatCall.uNameLength); ++ if (xplatCall.uNameLength < pGet->uNameLength) { ++ pGet->uNameLength = ++ xplatCall.uNameLength; ++ retCode = NWE_BUFFER_OVERFLOW; ++ } ++ dPtr = (char *)pGet + pGet->NameContextOffset; ++ cpylen = ++ copy_to_user(xplatCall.pNameContext, dPtr, ++ pGet->uNameLength); ++ } ++ ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (retCode); ++ ++} ++ ++int novfs_query_feature(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct nwc_query_feature xpCall; ++ int status = 0; ++ unsigned long cpylen; ++ ++ cpylen = ++ copy_from_user(&xpCall, pdata->reqData, sizeof(struct nwc_query_feature)); ++ switch (xpCall.Feature) { ++ case NWC_FEAT_NDS: ++ case NWC_FEAT_NDS_MTREE: ++ case NWC_FEAT_PRN_CAPTURE: ++ case NWC_FEAT_NDS_RESOLVE: ++ ++ status = NWE_REQUESTER_FAILURE; ++ ++ } ++ return (status); ++} ++ ++int novfs_get_tree_monitored_conn(struct novfs_xplat *pdata, ++ struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_get_tree_monitored_conn_ref xplatCall, *p; ++ struct nwd_get_tree_monitored_conn_ref *pDConnRef; ++ char *dPtr; ++ unsigned long status = -ENOMEM, cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, ++ sizeof(struct nwc_get_tree_monitored_conn_ref)); ++ datalen = sizeof(*pDConnRef) + xplatCall.pTreeName->DataLen; ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_GET_TREE_MONITORED_CONN_REF; ++ ++ pDConnRef = (struct nwd_get_tree_monitored_conn_ref *) cmd->data; ++ pDConnRef->TreeName.boffset = sizeof(*pDConnRef); ++ pDConnRef->TreeName.len = xplatCall.pTreeName->DataLen; ++ pDConnRef->TreeName.type = xplatCall.pTreeName->DataType; ++ ++ dPtr = cmd->data + sizeof(*pDConnRef); ++ cpylen = ++ copy_from_user(dPtr, xplatCall.pTreeName->pBuffer, ++ pDConnRef->TreeName.len); ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ pDConnRef = (struct nwd_get_tree_monitored_conn_ref *) reply->data; ++ dPtr = reply->data + pDConnRef->TreeName.boffset; ++ p = (struct nwc_get_tree_monitored_conn_ref *) pdata->reqData; ++ cpylen = ++ copy_to_user(&p->uConnReference, ++ &pDConnRef->uConnReference, 4); ++ ++ status = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (status); ++} ++ ++int novfs_enum_ids(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_enum_ids xplatCall, *eId; ++ struct nwd_enum_ids *pEnum; ++ struct nwc_string xferStr; ++ char *str; ++ unsigned long status = -ENOMEM, cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, ++ sizeof(struct nwc_enum_ids)); ++ datalen = sizeof(*pEnum); ++ cmdlen = datalen + sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_ENUMERATE_IDENTITIES; ++ ++ DbgPrint("Send Request"); ++ __DbgPrint(" iterator = %x\n", xplatCall.Iterator); ++ __DbgPrint(" cmdlen = %d\n", cmdlen); ++ ++ pEnum = (struct nwd_enum_ids *) cmd->data; ++ pEnum->Iterator = xplatCall.Iterator; ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ ++ eId = pdata->repData; ++ pEnum = (struct nwd_enum_ids *) reply->data; ++ cpylen = ++ copy_to_user(&eId->Iterator, &pEnum->Iterator, ++ sizeof(pEnum->Iterator)); ++ DbgPrint("[XPLAT NWCAPI] Found AuthId 0x%X", ++ pEnum->AuthenticationId); ++ cpylen = ++ copy_to_user(&eId->AuthenticationId, ++ &pEnum->AuthenticationId, ++ sizeof(pEnum->AuthenticationId)); ++ cpylen = ++ copy_to_user(&eId->AuthType, &pEnum->AuthType, ++ sizeof(pEnum->AuthType)); ++ cpylen = ++ copy_to_user(&eId->IdentityFlags, ++ &pEnum->IdentityFlags, ++ sizeof(pEnum->IdentityFlags)); ++ cpylen = ++ copy_to_user(&eId->NameType, &pEnum->NameType, ++ sizeof(pEnum->NameType)); ++ cpylen = ++ copy_to_user(&eId->ObjectType, &pEnum->ObjectType, ++ sizeof(pEnum->ObjectType)); ++ ++ if (!status) { ++ cpylen = ++ copy_from_user(&xferStr, eId->pDomainName, ++ sizeof(struct nwc_string)); ++ str = ++ (char *)((char *)reply->data + ++ pEnum->domainNameOffset); ++ DbgPrint("[XPLAT NWCAPI] Found Domain %s", ++ str); ++ cpylen = ++ copy_to_user(xferStr.pBuffer, str, ++ pEnum->domainNameLen); ++ xferStr.DataType = NWC_STRING_TYPE_ASCII; ++ xferStr.DataLen = pEnum->domainNameLen - 1; ++ cpylen = ++ copy_to_user(eId->pDomainName, &xferStr, ++ sizeof(struct nwc_string)); ++ ++ cpylen = ++ copy_from_user(&xferStr, eId->pObjectName, ++ sizeof(struct nwc_string)); ++ str = ++ (char *)((char *)reply->data + ++ pEnum->objectNameOffset); ++ DbgPrint("[XPLAT NWCAPI] Found User %s", str); ++ cpylen = ++ copy_to_user(xferStr.pBuffer, str, ++ pEnum->objectNameLen); ++ xferStr.DataType = NWC_STRING_TYPE_ASCII; ++ xferStr.DataLen = pEnum->objectNameLen - 1; ++ cpylen = ++ copy_to_user(eId->pObjectName, &xferStr, ++ sizeof(struct nwc_string)); ++ } ++ ++ kfree(reply); ++ ++ } ++ kfree(cmd); ++ return (status); ++} ++ ++int novfs_change_auth_key(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_change_key xplatCall; ++ struct nwd_change_key *pNewKey; ++ struct nwc_string xferStr; ++ char *str; ++ unsigned long status = -ENOMEM, cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, sizeof(struct nwc_change_key)); ++ ++ datalen = ++ sizeof(struct nwd_change_key) + xplatCall.pDomainName->DataLen + ++ xplatCall.pObjectName->DataLen + xplatCall.pNewPassword->DataLen + ++ xplatCall.pVerifyPassword->DataLen; ++ ++ cmdlen = sizeof(*cmd) + datalen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ pNewKey = (struct nwd_change_key *) cmd->data; ++ cmd->dataLen = datalen; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_CHANGE_KEY; ++ ++ pNewKey->NameType = xplatCall.NameType; ++ pNewKey->ObjectType = xplatCall.ObjectType; ++ pNewKey->AuthType = xplatCall.AuthType; ++ str = (char *)pNewKey; ++ ++ /* ++ * Get the tree name ++ */ ++ str += sizeof(*pNewKey); ++ cpylen = ++ copy_from_user(&xferStr, xplatCall.pDomainName, ++ sizeof(struct nwc_string)); ++ pNewKey->domainNameOffset = sizeof(*pNewKey); ++ cpylen = copy_from_user(str, xferStr.pBuffer, xferStr.DataLen); ++ pNewKey->domainNameLen = xferStr.DataLen; ++ ++ /* ++ * Get the User Name ++ */ ++ str += pNewKey->domainNameLen; ++ cpylen = ++ copy_from_user(&xferStr, xplatCall.pObjectName, ++ sizeof(struct nwc_string)); ++ pNewKey->objectNameOffset = ++ pNewKey->domainNameOffset + pNewKey->domainNameLen; ++ cpylen = copy_from_user(str, xferStr.pBuffer, xferStr.DataLen); ++ pNewKey->objectNameLen = xferStr.DataLen; ++ ++ /* ++ * Get the New Password ++ */ ++ str += pNewKey->objectNameLen; ++ cpylen = ++ copy_from_user(&xferStr, xplatCall.pNewPassword, ++ sizeof(struct nwc_string)); ++ pNewKey->newPasswordOffset = ++ pNewKey->objectNameOffset + pNewKey->objectNameLen; ++ cpylen = copy_from_user(str, xferStr.pBuffer, xferStr.DataLen); ++ pNewKey->newPasswordLen = xferStr.DataLen; ++ ++ /* ++ * Get the Verify Password ++ */ ++ str += pNewKey->newPasswordLen; ++ cpylen = ++ copy_from_user(&xferStr, xplatCall.pVerifyPassword, ++ sizeof(struct nwc_string)); ++ pNewKey->verifyPasswordOffset = ++ pNewKey->newPasswordOffset + pNewKey->newPasswordLen; ++ cpylen = copy_from_user(str, xferStr.pBuffer, xferStr.DataLen); ++ pNewKey->verifyPasswordLen = xferStr.DataLen; ++ ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ memset(cmd, 0, cmdlen); ++ ++ kfree(cmd); ++ return (status); ++} ++ ++int novfs_set_pri_conn(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_set_primary_conn xplatCall; ++ struct nwd_set_primary_conn *pConn; ++ unsigned long status = -ENOMEM, cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, ++ sizeof(struct nwc_set_primary_conn)); ++ ++ datalen = sizeof(struct nwd_set_primary_conn); ++ cmdlen = sizeof(*cmd) + datalen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ pConn = (struct nwd_set_primary_conn *) cmd->data; ++ cmd->dataLen = datalen; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_SET_PRIMARY_CONN; ++ pConn->ConnHandle = (void *) (unsigned long) xplatCall.ConnHandle; ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (status); ++} ++ ++int novfs_get_pri_conn(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request cmd; ++ struct novfs_xplat_call_reply *reply; ++ unsigned long status = -ENOMEM, cmdlen, replylen, cpylen; ++ ++ cmdlen = (unsigned long) (&((struct novfs_xplat_call_request *) 0)->data); ++ ++ cmd.dataLen = 0; ++ cmd.Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd.Command.SequenceNumber = 0; ++ cmd.Command.SessionId = Session; ++ cmd.NwcCommand = NWC_GET_PRIMARY_CONN; ++ ++ status = ++ Queue_Daemon_Command((void *)&cmd, cmdlen, NULL, 0, (void **)&reply, ++ &replylen, INTERRUPTIBLE); ++ ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ if (!status) { ++ cpylen = ++ copy_to_user(pdata->repData, reply->data, ++ sizeof(unsigned long)); ++ } ++ ++ kfree(reply); ++ } ++ ++ return (status); ++} ++ ++int novfs_set_map_drive(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ unsigned long status = 0, datalen, cmdlen, replylen; ++ struct nwc_map_drive_ex symInfo; ++ ++ DbgPrint(""); ++ cmdlen = sizeof(*cmd); ++ if (copy_from_user(&symInfo, pdata->reqData, sizeof(symInfo))) ++ return -EFAULT; ++ datalen = sizeof(symInfo) + symInfo.dirPathOffsetLength + ++ symInfo.linkOffsetLength; ++ ++ __DbgPrint(" cmdlen = %d\n", cmdlen); ++ __DbgPrint(" dataLen = %d\n", datalen); ++ __DbgPrint(" symInfo.dirPathOffsetLength = %d\n", ++ symInfo.dirPathOffsetLength); ++ __DbgPrint(" symInfo.linkOffsetLength = %d\n", symInfo.linkOffsetLength); ++ __DbgPrint(" pdata->datalen = %d\n", pdata->reqLen); ++ ++ novfs_dump(sizeof(symInfo), &symInfo); ++ ++ cmdlen += datalen; ++ ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->dataLen = datalen; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_MAP_DRIVE; ++ ++ if (copy_from_user(cmd->data, pdata->reqData, datalen)) { ++ kfree(cmd); ++ return -EFAULT; ++ } ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (status); ++ ++} ++ ++int novfs_unmap_drive(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ unsigned long status = 0, datalen, cmdlen, replylen, cpylen; ++ struct nwc_unmap_drive_ex symInfo; ++ ++ DbgPrint(""); ++ ++ cpylen = copy_from_user(&symInfo, pdata->reqData, sizeof(symInfo)); ++ cmdlen = sizeof(*cmd); ++ datalen = sizeof(symInfo) + symInfo.linkLen; ++ ++ cmdlen += datalen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->dataLen = datalen; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_UNMAP_DRIVE; ++ ++ cpylen = copy_from_user(cmd->data, pdata->reqData, datalen); ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (status); ++} ++ ++int novfs_enum_drives(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ unsigned long status = 0, cmdlen, replylen, cpylen; ++ unsigned long offset; ++ char *cp; ++ ++ DbgPrint(""); ++ ++ cmdlen = sizeof(*cmd); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->dataLen = 0; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_ENUMERATE_DRIVES; ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ DbgPrint("Status Code = 0x%X", status); ++ if (!status) { ++ offset = ++ sizeof(((struct nwc_get_mapped_drives *) pdata-> ++ repData)->MapBuffLen); ++ cp = reply->data; ++ replylen = ++ ((struct nwc_get_mapped_drives *) pdata->repData)-> ++ MapBuffLen; ++ cpylen = ++ copy_to_user(pdata->repData, cp, offset); ++ cp += offset; ++ cpylen = ++ copy_to_user(((struct nwc_get_mapped_drives *) pdata-> ++ repData)->MapBuffer, cp, ++ min(replylen - offset, ++ reply->dataLen - offset)); ++ } ++ ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (status); ++} ++ ++int novfs_get_bcast_msg(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ unsigned long cmdlen, replylen; ++ int status = 0x8866, cpylen; ++ struct nwc_get_bcast_notification msg; ++ struct nwd_get_bcast_notification *dmsg; ++ ++ cmdlen = sizeof(*cmd) + sizeof(*dmsg); ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cpylen = copy_from_user(&msg, pdata->reqData, sizeof(msg)); ++ cmd->dataLen = sizeof(*dmsg); ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ ++ cmd->NwcCommand = NWC_GET_BROADCAST_MESSAGE; ++ dmsg = (struct nwd_get_bcast_notification *) cmd->data; ++ dmsg->uConnReference = (void *) (unsigned long) msg.uConnReference; ++ ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ ++ if (!status) { ++ char *cp = pdata->repData; ++ ++ dmsg = ++ (struct nwd_get_bcast_notification *) reply->data; ++ if (pdata->repLen < dmsg->messageLen) { ++ dmsg->messageLen = pdata->repLen; ++ } ++ msg.messageLen = dmsg->messageLen; ++ cpylen = ++ offsetof(struct ++ nwc_get_bcast_notification, ++ message); ++ cp += cpylen; ++ cpylen = ++ copy_to_user(pdata->repData, &msg, cpylen); ++ cpylen = ++ copy_to_user(cp, dmsg->message, ++ msg.messageLen); ++ } else { ++ msg.messageLen = 0; ++ msg.message[0] = 0; ++ cpylen = offsetof(struct ++ nwc_get_bcast_notification, ++ message); ++ cpylen = ++ copy_to_user(pdata->repData, &msg, ++ sizeof(msg)); ++ } ++ ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (status); ++} ++ ++int novfs_set_key_value(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_set_key xplatCall; ++ struct nwd_set_key *pNewKey; ++ struct nwc_string cstrObjectName, cstrPassword; ++ char *str; ++ unsigned long status = -ENOMEM, cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = copy_from_user(&xplatCall, pdata->reqData, sizeof(struct nwc_set_key)); ++ cpylen = ++ copy_from_user(&cstrObjectName, xplatCall.pObjectName, ++ sizeof(struct nwc_string)); ++ cpylen = ++ copy_from_user(&cstrPassword, xplatCall.pNewPassword, ++ sizeof(struct nwc_string)); ++ ++ datalen = ++ sizeof(struct nwd_set_key ) + cstrObjectName.DataLen + cstrPassword.DataLen; ++ ++ cmdlen = sizeof(*cmd) + datalen; ++ cmd = kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ pNewKey = (struct nwd_set_key *) cmd->data; ++ cmd->dataLen = datalen; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_SET_KEY; ++ ++ pNewKey->ObjectType = xplatCall.ObjectType; ++ pNewKey->AuthenticationId = xplatCall.AuthenticationId; ++ pNewKey->ConnHandle = (void *) (unsigned long) xplatCall.ConnHandle; ++ str = (char *)pNewKey; ++ ++ /* ++ * Get the User Name ++ */ ++ str += sizeof(struct nwd_set_key ); ++ cpylen = ++ copy_from_user(str, cstrObjectName.pBuffer, ++ cstrObjectName.DataLen); ++ ++ str += pNewKey->objectNameLen = cstrObjectName.DataLen; ++ pNewKey->objectNameOffset = sizeof(struct nwd_set_key ); ++ ++ /* ++ * Get the Verify Password ++ */ ++ cpylen = ++ copy_from_user(str, cstrPassword.pBuffer, ++ cstrPassword.DataLen); ++ ++ pNewKey->newPasswordLen = cstrPassword.DataLen; ++ pNewKey->newPasswordOffset = ++ pNewKey->objectNameOffset + pNewKey->objectNameLen; ++ ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (status); ++} ++ ++int novfs_verify_key_value(struct novfs_xplat *pdata, struct novfs_schandle Session) ++{ ++ struct novfs_xplat_call_request *cmd; ++ struct novfs_xplat_call_reply *reply; ++ struct nwc_verify_key xplatCall; ++ struct nwd_verify_key *pNewKey; ++ struct nwc_string xferStr; ++ char *str; ++ unsigned long status = -ENOMEM, cmdlen, datalen, replylen, cpylen; ++ ++ cpylen = ++ copy_from_user(&xplatCall, pdata->reqData, sizeof(struct nwc_verify_key)); ++ ++ datalen = ++ sizeof(struct nwd_verify_key) + xplatCall.pDomainName->DataLen + ++ xplatCall.pObjectName->DataLen + xplatCall.pVerifyPassword->DataLen; ++ ++ cmdlen = sizeof(*cmd) + datalen; ++ cmd = (struct novfs_xplat_call_request *)kmalloc(cmdlen, GFP_KERNEL); ++ ++ if (!cmd) ++ return -ENOMEM; ++ ++ pNewKey = (struct nwd_verify_key *) cmd->data; ++ cmd->dataLen = datalen; ++ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL; ++ cmd->Command.SequenceNumber = 0; ++ cmd->Command.SessionId = Session; ++ cmd->NwcCommand = NWC_VERIFY_KEY; ++ ++ pNewKey->NameType = xplatCall.NameType; ++ pNewKey->ObjectType = xplatCall.ObjectType; ++ pNewKey->AuthType = xplatCall.AuthType; ++ str = (char *)pNewKey; ++ ++ /* ++ * Get the tree name ++ */ ++ str += sizeof(*pNewKey); ++ cpylen = ++ copy_from_user(&xferStr, xplatCall.pDomainName, ++ sizeof(struct nwc_string)); ++ pNewKey->domainNameOffset = sizeof(*pNewKey); ++ cpylen = copy_from_user(str, xferStr.pBuffer, xferStr.DataLen); ++ pNewKey->domainNameLen = xferStr.DataLen; ++ ++ /* ++ * Get the User Name ++ */ ++ str += pNewKey->domainNameLen; ++ cpylen = ++ copy_from_user(&xferStr, xplatCall.pObjectName, ++ sizeof(struct nwc_string)); ++ pNewKey->objectNameOffset = ++ pNewKey->domainNameOffset + pNewKey->domainNameLen; ++ cpylen = copy_from_user(str, xferStr.pBuffer, xferStr.DataLen); ++ pNewKey->objectNameLen = xferStr.DataLen; ++ ++ /* ++ * Get the Verify Password ++ */ ++ str += pNewKey->objectNameLen; ++ cpylen = ++ copy_from_user(&xferStr, xplatCall.pVerifyPassword, ++ sizeof(struct nwc_string)); ++ pNewKey->verifyPasswordOffset = ++ pNewKey->objectNameOffset + pNewKey->objectNameLen; ++ cpylen = copy_from_user(str, xferStr.pBuffer, xferStr.DataLen); ++ pNewKey->verifyPasswordLen = xferStr.DataLen; ++ ++ status = ++ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, ++ (void **)&reply, &replylen, ++ INTERRUPTIBLE); ++ if (reply) { ++ status = reply->Reply.ErrorCode; ++ kfree(reply); ++ } ++ kfree(cmd); ++ return (status); ++} +--- /dev/null ++++ b/fs/novfs/nwcapi.h +@@ -0,0 +1,1416 @@ ++/* ++ * NetWare Redirector for Linux ++ * Author: Sheffer Clark ++ * ++ * This file contains all typedefs and constants for the NetWare Client APIs. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++#ifndef __NWCLNX_H__ ++#define __NWCLNX_H__ ++ ++#if 0 //sgled hack ++#else //sgled hack (up to endif) ++ ++#define NW_MAX_TREE_NAME_LEN 33 ++#define NW_MAX_SERVICE_TYPE_LEN 49 ++/* Transport Type - (nuint32 value) */ ++#define NWC_TRAN_TYPE_IPX 0x0001 ++#define NWC_TRAN_TYPE_DDP 0x0003 ++#define NWC_TRAN_TYPE_ASP 0x0004 ++#define NWC_TRAN_TYPE_UDP 0x0008 ++#define NWC_TRAN_TYPE_TCP 0x0009 ++#define NWC_TRAN_TYPE_UDP6 0x000A ++#define NWC_TRAN_TYPE_TCP6 0x000B ++#define NWC_TRAN_TYPE_WILD 0x8000 ++ ++// ++// DeviceIoControl requests for the NetWare Redirector ++// ++// Macro definition for defining DeviceIoControl function control codes. ++// The function codes 0 - 2047 are reserved for Microsoft. ++// Function codes 2048 - 4096 are reserved for customers. ++// The NetWare Redirector will use codes beginning at 3600. ++// ++// METHOD_NEITHER User buffers will be passed directly from the application ++// to the file system. The redirector is responsible for either probing ++// and locking the buffers or using a try - except around access of the ++// buffers. ++ ++#define BASE_REQ_NUM 0x4a541000 ++ ++// Connection functions ++#define NWC_OPEN_CONN_BY_NAME (BASE_REQ_NUM + 0) ++#define NWC_OPEN_CONN_BY_ADDRESS (BASE_REQ_NUM + 1) ++#define NWC_OPEN_CONN_BY_REFERENCE (BASE_REQ_NUM + 2) ++#define NWC_CLOSE_CONN (BASE_REQ_NUM + 3) ++#define NWC_SYS_CLOSE_CONN (BASE_REQ_NUM + 4) ++#define NWC_GET_CONN_INFO (BASE_REQ_NUM + 5) ++#define NWC_SET_CONN_INFO (BASE_REQ_NUM + 6) ++#define NWC_SCAN_CONN_INFO (BASE_REQ_NUM + 7) ++#define NWC_MAKE_CONN_PERMANENT (BASE_REQ_NUM + 8) ++#define NWC_LICENSE_CONN (BASE_REQ_NUM + 9) ++#define NWC_UNLICENSE_CONN (BASE_REQ_NUM + 10) ++#define NWC_GET_NUM_CONNS (BASE_REQ_NUM + 11) ++#define NWC_GET_PREFERRED_SERVER (BASE_REQ_NUM + 12) ++#define NWC_SET_PREFERRED_SERVER (BASE_REQ_NUM + 13) ++#define NWC_GET_PRIMARY_CONN (BASE_REQ_NUM + 14) ++#define NWC_SET_PRIMARY_CONN (BASE_REQ_NUM + 15) ++ ++// Authentication functions ++#define NWC_CHANGE_KEY (BASE_REQ_NUM + 20) ++#define NWC_ENUMERATE_IDENTITIES (BASE_REQ_NUM + 21) ++#define NWC_GET_IDENTITY_INFO (BASE_REQ_NUM + 22) ++#define NWC_LOGIN_IDENTITY (BASE_REQ_NUM + 23) ++#define NWC_LOGOUT_IDENTITY (BASE_REQ_NUM + 24) ++#define NWC_SET_KEY (BASE_REQ_NUM + 25) ++#define NWC_VERIFY_KEY (BASE_REQ_NUM + 26) ++#define NWC_AUTHENTICATE_CONN_WITH_ID (BASE_REQ_NUM + 27) ++#define NWC_UNAUTHENTICATE_CONN (BASE_REQ_NUM + 28) ++ ++// Directory Services functions ++#define NWC_GET_DEFAULT_NAME_CONTEXT (BASE_REQ_NUM + 30) ++#define NWC_SET_DEFAULT_NAME_CONTEXT (BASE_REQ_NUM + 31) ++#define NWC_GET_PREFERRED_DS_TREE (BASE_REQ_NUM + 32) ++#define NWC_SET_PREFERRED_DS_TREE (BASE_REQ_NUM + 33) ++#define NWC_GET_TREE_MONITORED_CONN_REF (BASE_REQ_NUM + 34) ++#define NWC_NDS_RESOLVE_NAME_TO_ID (BASE_REQ_NUM + 35) ++ ++// NCP Request functions ++#define NWC_FRAGMENT_REQUEST (BASE_REQ_NUM + 40) ++#define NWC_NCP_ORDERED_REQUEST_ALL (BASE_REQ_NUM + 41) ++#define NWC_RAW_NCP_REQUEST (BASE_REQ_NUM + 42) ++#define NWC_RAW_NCP_REQUEST_ALL (BASE_REQ_NUM + 43) ++ ++// File Handle Conversion functions ++#define NWC_CONVERT_LOCAL_HANDLE (BASE_REQ_NUM + 50) ++#define NWC_CONVERT_NETWARE_HANDLE (BASE_REQ_NUM + 51) ++ ++// Misc. functions ++#define NWC_MAP_DRIVE (BASE_REQ_NUM + 60) ++#define NWC_UNMAP_DRIVE (BASE_REQ_NUM + 61) ++#define NWC_ENUMERATE_DRIVES (BASE_REQ_NUM + 62) ++ ++#define NWC_GET_REQUESTER_VERSION (BASE_REQ_NUM + 63) ++#define NWC_QUERY_FEATURE (BASE_REQ_NUM + 64) ++ ++#define NWC_GET_CONFIGURED_NSPS (BASE_REQ_NUM + 65) ++ ++#define NWC_GET_MOUNT_PATH (BASE_REQ_NUM + 66) ++ ++#define NWC_GET_BROADCAST_MESSAGE (BASE_REQ_NUM + 67) ++ ++#endif //sgled hack ------------------------------- ++ ++#define IOC_XPLAT 0x4a540002 ++ ++struct novfs_xplat { ++ int xfunction; ++ unsigned long reqLen; ++ void *reqData; ++ unsigned long repLen; ++ void *repData; ++ ++}; ++ ++#if 0 ++N_EXTERN_LIBRARY(NWRCODE) ++ NWCLnxReq ++ (nuint32 request, nptr pInBuf, nuint32 inLen, nptr pOutBuf, nuint32 outLen); ++#endif ++// ++// Network Name Format Type ++// ++ ++#define NWC_NAME_FORMAT_NDS 0x0001 ++#define NWC_NAME_FORMAT_BIND 0x0002 ++#define NWC_NAME_FORMAT_BDP 0x0004 ++#define NWC_NAME_FORMAT_NDS_TREE 0x0008 ++#define NWC_NAME_FORMAT_WILD 0x8000 ++ ++// ++// API String Types ++// ++ ++#define NWC_STRING_TYPE_ASCII 0x0001 // multi-byte, not really ascii ++#define NWC_STRING_TYPE_UNICODE 0x0002 ++#define NWC_STRING_TYPE_UTF8 0x0003 ++ ++// ++// Open Connection Flags ++// ++ ++#define NWC_OPEN_LICENSED 0x0001 ++#define NWC_OPEN_UNLICENSED 0x0002 ++#define NWC_OPEN_PRIVATE 0x0004 ++#define NWC_OPEN_PUBLIC 0x0008 ++#define NWC_OPEN_EXISTING_HANDLE 0x0010 ++#define NWC_OPEN_NO_HANDLE 0x0020 ++#define NWC_OPEN_PERMANENT 0x0040 ++#define NWC_OPEN_DISCONNECTED 0x0080 ++#define NWC_OPEN_NEAREST 0x0100 ++#define NWC_OPEN_IGNORE_CACHE 0x0200 ++ ++// ++// Close Connection Flags ++// ++ ++#define NWC_CLOSE_TEMPORARY 0x0000 ++#define NWC_CLOSE_PERMANENT 0x0001 ++ ++// ++// Connection Information Levels ++// ++ ++#define NWC_CONN_INFO_RETURN_ALL 0xFFFF ++#define NWC_CONN_INFO_RETURN_NONE 0x0000 ++#define NWC_CONN_INFO_VERSION 0x0001 ++#define NWC_CONN_INFO_AUTH_STATE 0x0002 ++#define NWC_CONN_INFO_BCAST_STATE 0x0003 ++#define NWC_CONN_INFO_CONN_REF 0x0004 ++#define NWC_CONN_INFO_TREE_NAME 0x0005 ++#define NWC_CONN_INFO_WORKGROUP_ID 0x0006 ++#define NWC_CONN_INFO_SECURITY_STATE 0x0007 ++#define NWC_CONN_INFO_CONN_NUMBER 0x0008 ++#define NWC_CONN_INFO_USER_ID 0x0009 ++#define NWC_CONN_INFO_SERVER_NAME 0x000A ++#define NWC_CONN_INFO_TRAN_ADDR 0x000B ++#define NWC_CONN_INFO_NDS_STATE 0x000C ++#define NWC_CONN_INFO_MAX_PACKET_SIZE 0x000D ++#define NWC_CONN_INFO_LICENSE_STATE 0x000E ++#define NWC_CONN_INFO_PUBLIC_STATE 0x000F ++#define NWC_CONN_INFO_SERVICE_TYPE 0x0010 ++#define NWC_CONN_INFO_DISTANCE 0x0011 ++#define NWC_CONN_INFO_SERVER_VERSION 0x0012 ++#define NWC_CONN_INFO_AUTH_ID 0x0013 ++#define NWC_CONN_INFO_SUSPENDED 0x0014 ++#define NWC_CONN_INFO_TREE_NAME_UNICODE 0x0015 ++#define NWC_CONN_INFO_SERVER_NAME_UNICODE 0x0016 ++#define NWC_CONN_INFO_LOCAL_TRAN_ADDR 0x0017 ++#define NWC_CONN_INFO_ALTERNATE_ADDR 0x0018 ++#define NWC_CONN_INFO_SERVER_GUID 0x0019 ++ ++#define NWC_CONN_INFO_MAX_LEVEL 0x0014 ++ ++// ++// Information Versions ++// ++ ++#define NWC_INFO_VERSION_1 0x0001 ++#define NWC_INFO_VERSION_2 0x0002 ++ ++// ++// Authentication State ++// ++ ++#define NWC_AUTH_TYPE_NONE 0x0000 ++#define NWC_AUTH_TYPE_BINDERY 0x0001 ++#define NWC_AUTH_TYPE_NDS 0x0002 ++#define NWC_AUTH_TYPE_PNW 0x0003 ++ ++#define NWC_AUTH_STATE_NONE 0x0000 ++#define NWC_AUTH_STATE_BINDERY 0x0001 ++#define NWC_AUTH_STATE_NDS 0x0002 ++#define NWC_AUTH_STATE_PNW 0x0003 ++ ++// ++// Authentication Flags ++// ++ ++#define NWC_AUTH_PRIVATE 0x00000004 ++#define NWC_AUTH_PUBLIC 0x00000008 ++ ++// ++// Broadcast State ++// ++ ++#define NWC_BCAST_PERMIT_ALL 0x0000 ++#define NWC_BCAST_PERMIT_SYSTEM 0x0001 ++#define NWC_BCAST_PERMIT_NONE 0x0002 ++#define NWC_BCAST_PERMIT_SYSTEM_POLLED 0x0003 ++#define NWC_BCAST_PERMIT_ALL_POLLED 0x0004 ++ ++// ++// Broadcast State ++// ++ ++#define NWC_NDS_NOT_CAPABLE 0x0000 ++#define NWC_NDS_CAPABLE 0x0001 ++ ++// ++// License State ++// ++ ++#define NWC_NOT_LICENSED 0x0000 ++#define NWC_CONNECTION_LICENSED 0x0001 ++#define NWC_HANDLE_LICENSED 0x0002 ++ ++// ++// Public State ++// ++ ++#define NWC_CONN_PUBLIC 0x0000 ++#define NWC_CONN_PRIVATE 0x0001 ++ ++// ++// Scan Connection Information Flags used ++// for finding connections by specific criteria ++// ++ ++#define NWC_MATCH_NOT_EQUALS 0x0000 ++#define NWC_MATCH_EQUALS 0x0001 ++#define NWC_RETURN_PUBLIC 0x0002 ++#define NWC_RETURN_PRIVATE 0x0004 ++#define NWC_RETURN_LICENSED 0x0008 ++#define NWC_RETURN_UNLICENSED 0x0010 ++ ++// ++// Authentication Types ++// ++ ++#define NWC_AUTHENT_BIND 0x0001 ++#define NWC_AUTHENT_NDS 0x0002 ++#define NWC_AUTHENT_PNW 0x0003 ++ ++// ++// Disconnected info ++// ++ ++#define NWC_SUSPENDED 0x0001 ++ ++// ++// Maximum object lengths ++// ++ ++#define MAX_DEVICE_LENGTH 16 ++#define MAX_NETWORK_NAME_LENGTH 1024 ++#define MAX_OBJECT_NAME_LENGTH 48 ++#define MAX_PASSWORD_LENGTH 128 ++#define MAX_SERVER_NAME_LENGTH 48 ++#define MAX_SERVICE_TYPE_LENGTH 48 ++#define MAX_TREE_NAME_LENGTH 32 ++#define MAX_ADDRESS_LENGTH 32 ++#define MAX_NAME_SERVICE_PROVIDERS 10 ++ ++// ++// Flags for the GetBroadcastMessage API ++// ++ ++#define MESSAGE_GET_NEXT_MESSAGE 1 ++#define MESSAGE_RECEIVED_FOR_CONNECTION 2 ++ ++// ++// This constant must always be equal to the last device ++// ++ ++#define DEVICE_LAST_DEVICE 0x00000003 ++ ++// ++// Defined feature set provided by requester ++// ++ ++#ifndef NWC_FEAT_PRIV_CONN ++#define NWC_FEAT_PRIV_CONN 1 ++#define NWC_FEAT_REQ_AUTH 2 ++#define NWC_FEAT_SECURITY 3 ++#define NWC_FEAT_NDS 4 ++#define NWC_FEAT_NDS_MTREE 5 ++#define NWC_FEAT_PRN_CAPTURE 6 ++#define NWC_FEAT_NDS_RESOLVE 7 ++#endif ++ ++//===[ Type definitions ]================================================== ++ ++ ++// ++// Structure for defining what a transport ++// address looks like ++// ++ ++struct nwc_tran_addr { ++ u32 uTransportType; ++ u32 uAddressLength; ++ unsigned char *puAddress; ++}; ++ ++ ++struct nwc_conn_string { ++ char *pString; ++ u32 uStringType; ++ u32 uNameFormatType; ++ ++}; ++ ++//#if defined(NTYPES_H) ++//typedef NWCString NwcString, *PNwcString; ++//#else ++struct nwc_string { ++ u32 DataType; ++ u32 BuffSize; ++ u32 DataLen; ++ void *pBuffer; ++ u32 CodePage; ++ u32 CountryCode; ++ ++}; ++//#endif ++ ++// ++// Definition of a fragment for the Raw NCP requests ++// ++ ++struct nwc_frag { ++ void *pData; ++ u32 uLength; ++ ++}; ++ ++// ++// Current connection information available for ++// enumeration using GetConnInfo and ScanConnInfo ++// ++ ++#define NW_INFO_BUFFER_SIZE NW_MAX_TREE_NAME_LEN + \ ++ NW_MAX_TREE_NAME_LEN + \ ++ NW_MAX_SERVICE_TYPE_LEN ++//++======================================================================= ++// API Name: NwcCloseConn ++// ++// Arguments In: ConnHandle - The handle to a connection that is ++// no longer needed. ++// ++// Arguments Out: NONE ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_CONN_INVALID ++// NWE_INVALID_OWNER ++// NWE_RESOURCE_LOCK ++// ++// Abstract: This API is used by an application that opened the ++// connection using one of the open connection calls ++// is finished using the connection. After it is closed, ++// the handle may no longer be used to access the ++// connection. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_close_conn { ++ u32 ConnHandle; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcConvertLocalFileHandle ++// ++// Arguments In: NONE ++// ++// Arguments Out: uConnReference - The connection reference associated ++// with the returned NetWare file handle. ++// ++// pNetWareFileHandle - The six byte NetWare file handle ++// associated with the given local file handle. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_RESOURCE_NOT_OWNED ++// ++// Abstract: This API is used to return the NetWare handle that ++// has been associated to a local file handle. ++// In addition to returning the NetWare file handle, ++// this API also returns the connection reference to ++// the connection that owns the file. ++// ++// Notes: This API does not create a new NetWare handle, it ++// only returns the existing handle associated to the ++// local handle. ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_convert_local_handle { ++ u32 uConnReference; ++ unsigned char NetWareHandle[6]; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcConvertNetWareHandle ++// ++// Arguments In: ConnHandle - The connection associated with the ++// NetWare file handle to convert. ++// ++// uAccessMode - The access rights to be used when ++// allocating the local file handle. ++// ++// pNetWareHandle - The NetWare handle that will be ++// bound to the new local handle being created. ++// ++// uFileSize - The current file size of the NetWare ++// file associated with the given NetWare file handle. ++// ++// Arguments Out: NONE ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_RESOURCE_NOT_OWNED ++// ++// Abstract: This API is used to convert a NetWare file handle ++// to a local file handle. ++// ++// The local handle must have been created previously ++// by doing a local open to \Special\$Special.net. ++// ++// Then an Ioctl to this function must be issued using the ++// handle returned from the special net open. ++// ++// Notes: After making this call, the NetWare file handle ++// should not be closed using the NetWare library ++// call, instead it should be closed using the local ++// operating system's close call. ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++struct nwc_convert_netware_handle { ++ u32 ConnHandle; ++ u32 uAccessMode; ++ unsigned char NetWareHandle[6]; ++ u32 uFileSize; ++}; ++ ++ ++//++======================================================================= ++// API Name: NwcGetConnInfo ++// ++// Arguments In: ConnHandle - Connection handle for the connection to ++// get information on. ++// uInfoLevel - Specifies what information should be ++// returned. ++// uInfoLen - Length of the ConnInfo buffer. ++// ++// Arguments Out: pConnInfo - A pointer to a buffer to return connection ++// information in. If the caller is requesting all ++// information the pointer will be to a structure of ++// type NwcConnInfo. If the caller is requesting just ++// a single piece of information, the pointer is the ++// type of information being requested. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_CONN_INVALID ++// NWE_INVALID_OWNER ++// NWE_RESOURCE_LOCK ++// NWE_STRING_TRANSLATION ++// ++// Abstract: This API returns connection information for the specified ++// connection. The requester can receive one piece of ++// information or the whole information structure. ++// Some of the entries in the NwcConnInfo structure are ++// pointers. The requester is responsible for supplying ++// valid pointers for any info specified to be returned. ++// If the requester does not want a piece of information ++// returned, a NULL pointer should be placed in the field. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_get_conn_info { ++ u32 ConnHandle; ++ u32 uInfoLevel; ++ u32 uInfoLength; ++ void *pConnInfo; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcGetDefaultNameContext ++// ++// Arguments In:: uTreeLength - Length of tree string. ++// ++// pDsTreeName - Pointer to tree string (multi-byte) ++// ++// pNameLength - On input, this is the length of the ++// name context buffer. On output, this is the actual ++// length of the name context string. ++// ++// Arguments Out: pNameContext - The buffer to copy the default name ++// context into (multi-byte). ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_BUFFER_OVERFLOW ++// NWE_OBJECT_NOT_FOUND ++// NWE_PARAM_INVALID ++// NWE_RESOURCE_LOCK ++// ++// Abstract: This API returns the default name context that ++// was previously set either by configuration or ++// by calling NwcSetDefaultNameContext. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_get_def_name_ctx { ++ u32 uTreeLength; ++ unsigned char *pDsTreeName; ++ u32 uNameLength; ++// unsigned short *pNameContext; ++ unsigned char *pNameContext; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcGetTreeMonitoredConnReference ++// ++// Arguments In: NONE ++// ++// Arguments Out: uConnReference - The connection reference associated ++// with the monitored connection. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_OBJECT_NOT_FOUND ++// NWE_RESOURCE_LOCK ++// ++// Abstract: This call returns a connection reference to a ++// connection that is monitored. This connection ++// reference may be used to open the connection. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_get_tree_monitored_conn_ref { ++ struct nwc_string *pTreeName; ++ u32 uConnReference; ++ ++}; ++ ++ ++//++======================================================================= ++// API Name: NwcGetPreferredDsTree ++// ++// Arguments In: uTreeLength - On input, this is the length in bytes ++// of the DS tree name buffer. On output, this is the ++// actual length of the DS tree name string in bytes. ++// ++// Arguments Out: pDsTreeName - The buffer to copy the DS tree name into. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_BUFFER_OVERFLOW ++// NWE_PARAM_INVALID ++// NWE_DS_PREFERRED_NOT_FOUND ++// NWE_RESOURCE_LOCK ++// ++// Abstract: This API returns the preferred DS tree name that was ++// previously set either by configuration or ++// by calling NwcSetPreferredDsTree. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++struct nwc_get_pref_ds_tree { ++ u32 uTreeLength; ++ unsigned char *pDsTreeName; ++}; ++ ++//++======================================================================= ++// API Name: NwcLicenseConn ++// ++// Arguments In: ConnHandle - An open connection handle that is in ++// an unlicensed state. ++// ++// Arguments Out: NONE ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_CONN_INVALID ++// NWE_HANDLE_ALREADY_LICENSED ++// ++// ++// Abstract: This API changes a connections state to licensed. ++// The licensed count will be incremented, and if ++// necessary, the license NCP will be sent. ++// If this handle is already in a licensed state, ++// an error will be returned. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_license_conn { ++ u32 ConnHandle; ++}; ++ ++ ++//++======================================================================= ++// API Name: NWCGetMappedDrives ++// ++// Arguments In: ++// Arguments Out: ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_BUFFER_OVERFLOW ++// ++// Abstract: This API returns the NetWare mapped drive info ++// per user. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_get_mapped_drives { ++ u32 MapBuffLen; // Buffer length (actual buffer size returned) ++ struct nwc_mapped_drive_buf *MapBuffer; // Pointer to map buffer ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcGetMountPath ++// ++// Arguments In: MountPathLen - Length of mount path buffer ++// including nul terminator. ++// ++// Arguments Out: MountPath - Pointer to mount path buffer ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_BUFFER_OVERFLOW ++// ++// Abstract: This API returns the mount point of the NOVFS file ++// system. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_get_mount_path { ++ u32 MountPathLen; ++ unsigned char *pMountPath; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcOpenConnByAddr ++// ++// Arguments In: pServiceType - The type of service required. ++// ++// uConnFlags - Specifies whether this connection ++// should be public or private. ++// ++// pTranAddress - Specifies the transport address of ++// the service to open a connection on. ++// a connection to. ++// ++// Arguments Out: ConnHandle - The new connection handle returned. ++// This handle may in turn be used for all requests ++// directed to this connection. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_INSUFFICIENT_RESOURCES ++// NWE_TRAN_INVALID_TYPE ++// NWE_RESOURCE_LOCK ++// NWE_UNSUPPORTED_TRAN_TYPE ++// ++// Abstract: This API will create a service connection to ++// the service specified by the transport address. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_open_conn_by_addr { ++ char *pServiceType; ++ u32 uConnFlags; ++ struct nwc_tran_addr *pTranAddr; ++ u32 ConnHandle; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcOpenConnByName ++// ++// Arguments In: ConnHandle - The connection to use when resolving ++// a name. For instance, if the name is a bindery name ++// the requester will scan the bindery of the given ++// connection to retrieve the service's address. This ++// value can also be NULL if the caller doesn't care ++// which connection is used to resolve the address. ++// ++// pName - A pointer to the name of the service trying ++// to be connected to. This string is NULL terminated, ++// contains no wild cards, and is a maximum of 512 ++// characters long. ++// ++// pServiceType - The type of service required. ++// ++// uConnFlags - Specifies whether this connection ++// should be public or private. ++// ++// uTranType - Specifies the preferred or required ++// transport type to be used. ++// NWC_TRAN_TYPE_WILD may be ORed with the other values ++// or used alone. When ORed with another value, the ++// wild value indicates an unmarked alternative is ++// acceptable. When used alone, the current preferred ++// transport is used. ++// ++// Arguments Out: ConnHandle - The new connection handle returned. ++// This handle may in turn be used for all requests ++// directed to this connection. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_BUFFER_OVERFLOW ++// NWE_INSUFFICIENT_RESOURCES ++// NWE_INVALID_STRING_TYPE ++// NWE_RESOURCE_LOCK ++// NWE_STRING_TRANSLATION ++// NWE_TRAN_INVALID_TYPE ++// NWE_UNSUPPORTED_TRAN_TYPE ++// ++// Abstract: This API will resolve the given name to a network ++// address then create a service connection to the ++// specified service. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_open_conn_by_name { ++ u32 ConnHandle; ++ struct nwc_conn_string *pName; ++ char *pServiceType; ++ u32 uConnFlags; ++ u32 uTranType; ++ u32 RetConnHandle; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcOpenConnByReference ++// ++// Arguments In: uConnReference - A reference handle which identifies ++// a valid connection that the caller wants to obtain ++// a connection handle to. A reference handle can be ++// used to get information about the connection without ++// actually getting a handle to it. A connection handle ++// must be used to make actual requests to that ++// connection. ++// ++// uConnFlags - Currently unused. ++// ++// Arguments Out: ConnHandle - The new connection handle returned. ++// This handle may in turn be used for all requests ++// directed to this connection. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_CONN_INVALID ++// ++// Abstract: This API will open the connection associated with ++// the given connection reference. The connection ++// reference can be obtained by calling the ++// NwcScanConnInfo API. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_open_conn_by_ref { ++ u32 uConnReference; ++ u32 uConnFlags; ++ u32 ConnHandle; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcRawRequest ++// ++// Arguments In: ConnHandle - The connection handle of the connection ++// that the request is being directed to. ++// ++// uFunction - The NCP function that is being called. ++// ++// uNumRequestFrags - The number of fragments that the ++// request packet has been broken into. ++// ++// pRequestFrags - List of fragments that make up the ++// request packet. Each fragment includes the length ++// of the fragment data and a pointer to the data. ++// ++// uNumReplyFrags - The number of fragments the reply ++// packet has been broken into. ++// ++// Arguments Out: pReplyFrags - List of fragments that make up the ++// request packet. Each fragment includes the length ++// of the fragment data and a pointer to the data. ++// ++// uActualReplyLength - Total size of the reply packet ++// after any header and tail information is removed. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_CONN_INVALID ++// ++// Abstract: API for sending raw NCP packets directly to a server. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_request { ++ u32 ConnHandle; ++ u32 uFunction; ++ u32 uNumRequestFrags; ++ struct nwc_frag *pRequestFrags; ++ u32 uNumReplyFrags; ++ struct nwc_frag *pReplyFrags; ++ u32 uActualReplyLength; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcScanConnInfo ++// ++// Arguments In: uScanIndex - The index to be used on the next ++// iteration of the scan. This value should be initially ++// set to zero. The output of this parameter will be ++// used in subsequent calls to this function. ++// ++// uScanInfoLevel - Describes the composition of the ++// pScanConnInfo pointer. If this parameter contains ++// NWC_CONN_INFO_RETURN_ALL, information for all ++// connections will be returned. ++// ++// uScanInfoLen - Lenght of pScanConnInfo buffer ++// ++// pScanConnInfo - This parameter is a pointer to ++// data that describes one piece of connection ++// information. The type of this data depends on ++// which level of information is being scanned for. ++// For instance, if the scan is being used to find all ++// connections with a particular authentication state, ++// pScanConnInfo would be a "pnuint" since ++// authentication state is described as nuint in the ++// NwcConnInfo structure. ++// ++// uScanFlag - This parameter tells whether to return ++// connection information for connections that match ++// the scan criteria or that do not match the scan ++// criteria. If the caller wants to find all the ++// connections that are not in the "NOVELL_INC" DS ++// tree, he would use the call as described below in ++// the description except the uScanFlag parameter would ++// have the value of NWC_MATCH_NOT_EQUALS. This flag ++// is also used to tell the requester whether to ++// return private or public, licensed or unlicensed ++// connections. ++// ++// uReturnInfoLevel - Specifies what information ++// should be returned. ++// ++// uReturnInfoLength - The size in bytes of pConnInfo. ++// ++// Arguments Out: uConnectionReference - Connection reference ++// associated with the information that is being ++// returned. ++// ++// pReturnConnInfo - A pointer to the NwcConnInfo ++// structure defined above. In some of the ++// structures within the union, there are pointers to ++// data to be returned. It is the responsibility of ++// the caller to provide pointers to valid memory ++// to copy this data into. ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_RESOURCE_LOCK ++// NWE_CONN_INVALID ++// NWE_INVALID_LEVEL ++// NWE_STRING_TRANSLATION ++// NWE_INVALID_MATCH_DATA ++// NWE_MATCH_FAILED ++// NWE_BUFFER_OVERFLOW ++// NWE_NO_MORE_ENTRIES ++// ++// Abstract: This API is used to return connection information ++// for multiple connections. It will return one ++// piece or the full structure of connection information ++// for one connection at a time. This call is designed ++// to scan for connections based on any piece of ++// connection information as described in the ++// NwcConnInfo structure. For instance, if the caller ++// wants to scan for all connections in the DS tree ++// "NOVELL_INC", the call would be made with the ++// following paramters: ++// ++// uScanLevelInfo = NWC_CONN_INFO_TREE_NAME ++// pScanConnInfo = "NOVELL_INC" ++// uScanFlag = NWC_MATCH_EQUALS | ++// NWC_RETURN_PUBLIC | ++// NWC_RETURN_LICENSED ++// ++// The scan flag is used to tell if the scan is ++// supposed to return connections that match or don't ++// match. This design doesn't allow any other ++// conditions for this flag (such as greater than or ++// less than). ++// ++// If the caller specifies the uReturnInfoLevel = ++// NWC_CONN_INFO_RETURN_ALL, the full NwcConnInfo ++// structure is returned. The caller must supply ++// data for any pointers in the NwcConnInfo structure ++// (these include tree name, workgroup id, server name ++// and transport address). However if the caller ++// doesn't want to get a particular piece of info ++// that is expecting a pointer to some data, a NULL ++// pointer may be used to indicate to the requester ++// that it should not return that piece of information. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_scan_conn_info { ++ u32 uScanIndex; ++ u32 uScanInfoLevel; ++ u32 uScanInfoLen; ++ void *pScanConnInfo; ++ u32 uScanFlags; ++ u32 uReturnInfoLevel; ++ u32 uReturnInfoLength; ++ u32 uConnectionReference; ++ void *pReturnConnInfo; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcSetConnInfo ++// ++// Arguments In: ConnHandle - Connection handle for the connection to ++// set information on. ++// ++// uInfoLevel - Specifies what information should be set. ++// ++// uInfoLen - Length in bytes of the information being set. ++// ++// pConnInfo - Connection information to set. ++// ++// Arguments Out: NONE ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_RESOURCE_LOCK ++// NWE_CONN_INVALID ++// NWE_INVALID_LEVEL ++// ++// ++// Abstract: This API sets information in the connection associated ++// with the connection handle. ++// ++// Notes: At this time the only setable information levels are: ++// NWC_CONN_INFO_AUTH_STATE ++// NWC_CONN_INFO_BCAST_STATE ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_set_conn_info { ++ u32 ConnHandle; ++ u32 uInfoLevel; ++ u32 uInfoLength; ++ void *pConnInfo; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcSetDefaultNameContext ++// ++// Arguments In:: uTreeLength - Length of tree string. ++// ++// pDsTreeName - The tree string (multi-byte). ++// ++// uNameLength - The length in bytes of the name ++// context string. ++// ++// pNameContext - The string to be used as the default ++// name context (multi-byte). ++// ++// Arguments Out: NONE ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_PARAM_INVALID ++// NWE_RESOURCE_LOCK ++// NWE_STRING_TRANSLATION ++// ++// Abstract: This API sets the default name context. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_set_def_name_ctx { ++ u32 uTreeLength; ++ unsigned char *pDsTreeName; ++ u32 uNameLength; ++// unsined short *pNameContext; ++ unsigned char *pNameContext; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcSetPreferredDsTree ++// ++// Arguments In: uTreeLength - The length in bytes of the DS tree name. ++// ++// pDsTreeName - The string to be used as the preferred ++// DS tree name. ++// ++// Arguments Out: NONE ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_INSUFFICIENT_RESOURCES ++// NWE_RESOURCE_LOCK ++// ++// Abstract: This API sets the preferred DS tree name. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_set_pref_ds_tree { ++ u32 uTreeLength; ++ unsigned char *pDsTreeName; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcSetPrimaryConnection ++// ++// Arguments In: ConnHandle - Connection handle associated to the ++// connection reference which the caller wishes to set ++// as primary. ++// ++// Arguments Out: NONE ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// NWE_CONN_PRIMARY_NOT_SET ++// ++// Abstract: This API sets the primary connection according to ++// the connection handle passed in by the caller. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_set_primary_conn { ++ u32 ConnHandle; ++ ++}; ++ ++ ++//++======================================================================= ++// API Name: NwcQueryFeature ++// ++// Arguments In: Feature - The number associated with a particular ++// feature that the caller wants to know if the requester ++// is supporting ++// ++// Arguments Out: ++// ++// Returns: STATUS_SUCCESS ++// NWE_REQUESTER_FAILURE ++// NWE_ACCESS_VIOLATION ++// ++// Abstract: ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_query_feature { ++ u32 Feature; ++ ++}; ++ ++//++======================================================================= ++// API Name: NWCChangePassword ++// ++// Arguments In: ++// ++// Arguments Out: ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// ++// Abstract: ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_change_key { ++ struct nwc_string *pDomainName; ++ u32 AuthType; ++ struct nwc_string *pObjectName; ++ u32 NameType; ++ u16 ObjectType; ++ struct nwc_string *pVerifyPassword; ++ struct nwc_string *pNewPassword; ++ ++}; ++ ++//++======================================================================= ++// API Name: NWCEnumerateIdentities ` ++// ++// Arguments In: ++// ++// Arguments Out: ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// ++// Abstract: ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_enum_ids { ++ u32 Iterator; ++ struct nwc_string *pDomainName; ++ u32 AuthType; ++ struct nwc_string *pObjectName; ++ u32 NameType; ++ u16 ObjectType; ++ u32 IdentityFlags; ++ u32 AuthenticationId; ++ ++}; ++ ++//++======================================================================= ++// API Name: NWCGetIdentityInfo ++// ++// Arguments In: ++// ++// Arguments Out: ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// ++// Abstract: ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_get_id_info { ++ u32 AuthenticationId; ++ struct nwc_string *pDomainName; ++ u32 AuthType; ++ struct nwc_string *pObjectName; ++ u32 NameType; ++ u16 ObjectType; ++ u32 IdentityFlags; ++ ++}; ++ ++//++======================================================================= ++// API Name: NWCLoginIdentity ++// ++// Arguments In: ++// ++// Arguments Out: ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// ++// Abstract: ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_login_id { ++ struct nwc_string *pDomainName; ++ u32 AuthType; ++ struct nwc_string *pObjectName; ++ u32 NameType; ++ u16 ObjectType; ++ u32 IdentityFlags; ++ struct nwc_string *pPassword; ++ u32 AuthenticationId; ++ ++}; ++ ++ ++//++======================================================================= ++// API Name: NWCSetPassword ++// ++// Arguments In: ++// ++// Arguments Out: ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// ++// Abstract: ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_set_key { ++ u32 ConnHandle; ++ u32 AuthenticationId; ++ struct nwc_string *pObjectName; ++ u16 ObjectType; ++ struct nwc_string *pNewPassword; ++ ++}; ++ ++//++======================================================================= ++// API Name: NWCVerifyPassword ++// ++// Arguments In: ++// ++// Arguments Out: ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// ++// Abstract: ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//++======================================================================= ++ ++struct nwc_verify_key { ++ struct nwc_string *pDomainName; ++ u32 AuthType; ++ struct nwc_string *pObjectName; ++ u32 NameType; ++ u16 ObjectType; ++ struct nwc_string *pVerifyPassword; ++ ++}; ++ ++//++======================================================================= ++// API Name: NwcAuthenticateWithId ++// ++// Arguments In: ConnHandle - The connection to be authenticated ++// ++// AuthenticationId - the authentication Id associated ++// to the information necessary to authenticate this ++// connection. ++// ++// Arguments Out: NONE ++// ++// Returns: STATUS_SUCCESS ++// NWE_ACCESS_VIOLATION ++// ++// Abstract: This API is used to authenticate a connection using ++// an authentication ID that has already been created. ++// ++// Notes: ++// ++// Environment: PASSIVE_LEVEL, LINUX ++// ++//=======================================================================-- ++ ++struct nwc_auth_with_id { ++ u32 ConnHandle; ++ u32 AuthenticationId; ++ ++}; ++ ++ ++struct nwc_unmap_drive_ex { ++// unsigned long connHdl; ++ unsigned int linkLen; ++ char linkData[1]; ++ ++}; ++ ++struct nwc_map_drive_ex { ++ u32 ConnHandle; ++ unsigned int localUid; ++ unsigned int linkOffsetLength; ++ unsigned int linkOffset; ++ unsigned int dirPathOffsetLength; ++ unsigned int dirPathOffset; ++}; ++ ++struct nwc_get_bcast_notification { ++ u32 uMessageFlags; ++ u32 uConnReference; ++ u32 messageLen; ++ char message[1]; ++}; ++ ++#endif /* __NWCLNX_H__ */ +--- /dev/null ++++ b/fs/novfs/nwerror.h +@@ -0,0 +1,658 @@ ++/* ++ * NetWare Redirector for Linux ++ * Author: Tom Buckley ++ * ++ * This file contains all return error codes. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++#ifndef __NOVFS_ERROR_H ++#define __NOVFS_ERROR_H ++ ++ ++/* ++ * Network errors ++ * Decimal values at end of line are 32768 lower than actual ++ */ ++ ++#define SHELL_ERROR 0x8800 ++#define VLM_ERROR 0x8800 ++#define ALREADY_ATTACHED 0x8800 // 0 - Attach attempted to server with valid, existing connection ++#define INVALID_CONNECTION 0x8801 // 1 - Request attempted with invalid or non-attached connection handle ++#define DRIVE_IN_USE 0x8802 // 2 - OS/2 only (NOT USED) ++#define CANT_ADD_CDS 0x8803 // 3 - Map drive attempted but unable to add new current directory structure ++#define DRIVE_CANNOT_MAP 0x8803 ++#define BAD_DRIVE_BASE 0x8804 // 4 - Map drive attempted with invalid path specification ++#define NET_READ_ERROR 0x8805 // 5 - Attempt to receive from the selected transport failed ++#define NET_RECV_ERROR 0x8805 // 5 ++#define UNKNOWN_NET_ERROR 0x8806 // 6 - Network send attempted with an un-specific network error ++#define SERVER_INVALID_SLOT 0x8807 // 7 - Server request attempted with invalid server connection slot ++#define BAD_SERVER_SLOT 0x8807 // 7 ++#define NO_SERVER_SLOTS 0x8808 // 8 - Attach attempted to server with no connection slots available ++#define NET_WRITE_ERROR 0x8809 // 9 - Attempt to send on the selected transport failed ++#define CONNECTION_IN_ERROR_STATE 0x8809 // Client-32 ++#define NET_SEND_ERROR 0x8809 // 9 ++#define SERVER_NO_ROUTE 0x880A // 10 - Attempted to find route to server where no route exists ++#define BAD_LOCAL_TARGET 0x880B // 11 - OS/2 only ++#define TOO_MANY_REQ_FRAGS 0x880C // 12 - Attempted request with too many request fragments specified ++#define CONNECT_LIST_OVERFLOW 0x880D // 13 ++#define BUFFER_OVERFLOW 0x880E // 14 - Attempt to receive more data than the reply buffer had room for ++#define MORE_DATA_ERROR 0x880E // Client-32 ++#define NO_CONN_TO_SERVER 0x880F // 15 ++#define NO_CONNECTION_TO_SERVER 0x880F // 15 - Attempt to get connection for a server not connected ++#define NO_ROUTER_FOUND 0x8810 // 16 - OS/2 only ++#define BAD_FUNC_ERROR 0x8811 // 17 ++#define INVALID_SHELL_CALL 0x8811 // 17 - Attempted function call to non- existent or illegal function ++#define SCAN_COMPLETE 0x8812 ++#define LIP_RESIZE_ERROR 0x8812 // Client-32 ++#define UNSUPPORTED_NAME_FORMAT_TYPE 0x8813 ++#define INVALID_DIR_HANDLE 0x8813 // Client-32 ++#define HANDLE_ALREADY_LICENSED 0x8814 ++#define OUT_OF_CLIENT_MEMORY 0x8814 // Client-32 ++#define HANDLE_ALREADY_UNLICENSED 0x8815 ++#define PATH_NOT_OURS 0x8815 // Client-32 ++#define INVALID_NCP_PACKET_LENGTH 0x8816 ++#define PATH_IS_PRINT_DEVICE 0x8816 // Client-32 ++#define SETTING_UP_TIMEOUT 0x8817 ++#define PATH_IS_EXCLUDED_DEVICE 0x8817 // Client-32 ++#define SETTING_SIGNALS 0x8818 ++#define PATH_IS_INVALID 0x8818 // Client-32 ++#define SERVER_CONNECTION_LOST 0x8819 ++#define NOT_SAME_DEVICE 0x8819 // Client-32 ++#define OUT_OF_HEAP_SPACE 0x881A ++#define INVALID_SERVICE_REQUEST 0x881B ++#define INVALID_SEARCH_HANDLE 0x881B // Client-32 ++#define INVALID_TASK_NUMBER 0x881C ++#define INVALID_DEVICE_HANDLE 0x881C // Client-32 ++#define INVALID_MESSAGE_LENGTH 0x881D ++#define INVALID_SEM_HANDLE 0x881D // Client-32 ++#define EA_SCAN_DONE 0x881E ++#define INVALID_CFG_HANDLE 0x881E // Client-32 ++#define BAD_CONNECTION_NUMBER 0x881F ++#define INVALID_MOD_HANDLE 0x881F // Client-32 ++#define ASYN_FIRST_PASS 0x8820 ++#define INVALID_DEVICE_INDEX 0x8821 ++#define INVALID_CONN_HANDLE 0x8822 ++#define INVALID_QUEUE_ID 0x8823 ++#define INVALID_PDEVICE_HANDLE 0x8824 ++#define INVALID_JOB_HANDLE 0x8825 ++#define INVALID_ELEMENT_ID 0x8826 ++#define ALIAS_NOT_FOUND 0x8827 ++#define RESOURCE_SUSPENDED 0x8828 ++#define INVALID_QUEUE_SPECIFIED 0x8829 ++#define DEVICE_ALREADY_OPEN 0x882A ++#define JOB_ALREADY_OPEN 0x882B ++#define QUEUE_NAME_ID_MISMATCH 0x882C ++#define JOB_ALREADY_STARTED 0x882D ++#define SPECT_DAA_TYPE_NOT_SUPPORTED 0x882E ++#define INVALID_ENVIR_HANDLE 0x882F ++#define NOT_SAME_CONNECTION 0x8830 // 48 - Internal server request attempted accross different server connections ++#define PRIMARY_CONNECTION_NOT_SET 0x8831 // 49 - Attempt to retrieve default connection with no primary connection set ++#define NO_PRIMARY_SET 0x8831 // 49 ++#define KEYWORD_NOT_FOUND 0x8832 // Client-32 ++#define PRINT_CAPTURE_NOT_IN_PROGRESS 0x8832 // Client-32 ++#define NO_CAPTURE_SET 0x8832 // 50 ++#define NO_CAPTURE_IN_PROGRESS 0x8832 // 50 - Capture information requested on port with no capture in progress ++#define BAD_BUFFER_LENGTH 0x8833 // 51 ++#define INVALID_BUFFER_LENGTH 0x8833 // 51 - Used to indicate length which caller requested on a GetDNC or SetDNC was too large ++#define NO_USER_NAME 0x8834 // 52 ++#define NO_NETWARE_PRINT_SPOOLER 0x8835 // 53 - Capture requested without having the local print spooler installed ++#define INVALID_PARAMETER 0x8836 // 54 - Attempted function with an invalid function parameter specified ++#define CONFIG_FILE_OPEN_FAILED 0x8837 // 55 - OS/2 only ++#define NO_CONFIG_FILE 0x8838 // 56 - OS/2 only ++#define CONFIG_FILE_READ_FAILED 0x8839 // 57 - OS/2 only ++#define CONFIG_LINE_TOO_LONG 0x883A // 58 - OS/2 only ++#define CONFIG_LINES_IGNORED 0x883B // 59 - OS/2 only ++#define NOT_MY_RESOURCE 0x883C // 60 - Attempted request made with a parameter using foriegn resource ++#define DAEMON_INSTALLED 0x883D // 61 - OS/2 only ++#define SPOOLER_INSTALLED 0x883E // 62 - Attempted load of print spooler with print spooler already installed ++#define CONN_TABLE_FULL 0x883F // 63 ++#define CONNECTION_TABLE_FULL 0x883F // 63 - Attempted to allocate a connection handle with no more local connection table entries ++#define CONFIG_SECTION_NOT_FOUND 0x8840 // 64 - OS/2 only ++#define BAD_TRAN_TYPE 0x8841 // 65 ++#define INVALID_TRANSPORT_TYPE 0x8841 // 65 - Attempted function on a connection with an invalid transport selected ++#define TDS_TAG_IN_USE 0x8842 // 66 - OS/2 only ++#define TDS_OUT_OF_MEMORY 0x8843 // 67 - OS/2 only ++#define TDS_INVALID_TAG 0x8844 // 68 - Attempted TDS function with invalid tag ++#define TDS_WRITE_TRUNCATED 0x8845 // 69 - Attempted TDS write with buffer that exceeded buffer ++#define NO_CONNECTION_TO_DS 0x8846 // Client-32 ++#define NO_DIRECTORY_SERVICE_CONNECTION 0x8846 // 70 ++#define SERVICE_BUSY 0x8846 // 70 - Attempted request made to partially asynchronous function in busy state ++#define NO_SERVER_ERROR 0x8847 // 71 - Attempted connect failed to find any servers responding ++#define BAD_VLM_ERROR 0x8848 // 72 - Attempted function call to non-existant or not-loaded overlay ++#define NETWORK_DRIVE_IN_USE 0x8849 // 73 - Attempted map to network drive that was already mapped ++#define LOCAL_DRIVE_IN_USE 0x884A // 74 - Attempted map to local drive that was in use ++#define NO_DRIVES_AVAILABLE 0x884B // 75 - Attempted map to next available drive when none were available ++#define DEVICE_NOT_REDIRECTED 0x884C // 76 - The device is not redirected ++#define NO_MORE_SFT_ENTRIES 0x884D // 77 - Maximum number of files was reached ++#define UNLOAD_ERROR 0x884E // 78 - Attempted unload failed ++#define IN_USE_ERROR 0x884F // 79 - Attempted re-use of already in use connection entry ++#define TOO_MANY_REP_FRAGS 0x8850 // 80 - Attempted request with too many reply fragments specified ++#define TABLE_FULL 0x8851 // 81 - Attempted to add a name into the name table after it was full ++#ifndef SOCKET_NOT_OPEN ++#define SOCKET_NOT_OPEN 0x8852 // 82 - Listen was posted on unopened socket ++#endif ++#define MEM_MGR_ERROR 0x8853 // 83 - Attempted enhanced memory operation failed ++#define SFT3_ERROR 0x8854 // 84 - An SFT3 switch occured mid-transfer ++#define PREFERRED_NOT_FOUND 0x8855 // 85 - the preferred directory server was not established but another directory server was returned ++#define DEVICE_NOT_RECOGNIZED 0x8856 // 86 - used to determine if the device is not used by VISE so pass it on to the next redirector, if any. ++#define BAD_NET_TYPE 0x8857 // 87 - the network type (Bind/NDS) does not match the server version ++#define ERROR_OPENING_FILE 0x8858 // 88 - generic open failure error, invalid path, access denied, etc.. ++#define NO_PREFERRED_SPECIFIED 0x8859 // 89 - no preferred name specified ++#define ERROR_OPENING_SOCKET 0x885A // 90 - error opening a socket ++#define REQUESTER_FAILURE 0x885A // Client-32 ++#define RESOURCE_ACCESS_DENIED 0x885B // Client-32 ++#define SIGNATURE_LEVEL_CONFLICT 0x8861 ++#define NO_LOCK_FOUND 0x8862 // OS/2 - process lock on conn handle failed, process ID not recognized ++#define LOCK_TABLE_FULL 0x8863 // OS/2 - process lock on conn handle failed, process lock table full ++#define INVALID_MATCH_DATA 0x8864 ++#define MATCH_FAILED 0x8865 ++#define NO_MORE_ENTRIES 0x8866 ++#define INSUFFICIENT_RESOURCES 0x8867 ++#define STRING_TRANSLATION 0x8868 ++#define STRING_TRANSLATION_NEEDED 0x8868 // Client-32 ++#define ACCESS_VIOLATION 0x8869 ++#define NOT_AUTHENTICATED 0x886A ++#define INVALID_LEVEL 0x886B ++#define RESOURCE_LOCK_ERROR 0x886C ++#define INVALID_NAME_FORMAT 0x886D ++#define OBJECT_EXISTS 0x886E ++#define OBJECT_NOT_FOUND 0x886F ++#define UNSUPPORTED_TRAN_TYPE 0x8870 ++#define INVALID_STRING_TYPE 0x8871 ++#define INVALID_OWNER 0x8872 ++#define UNSUPPORTED_AUTHENTICATOR 0x8873 ++#define IO_PENDING 0x8874 ++#define INVALID_DRIVE_NUM 0x8875 ++#define SHELL_FAILURE 0x88FF ++#define VLM_FAILURE 0x88FF ++ ++#define SVC_ALREADY_REGISTERED 0x8880 // Client-32 ++#define SVC_REGISTRY_FULL 0x8881 // Client-32 ++#define SVC_NOT_REGISTERED 0x8882 // Client-32 ++#define OUT_OF_RESOURCES 0x8883 // Client-32 ++#define RESOLVE_SVC_FAILED 0x8884 // Client-32 ++#define CONNECT_FAILED 0x8885 // Client-32 ++#define PROTOCOL_NOT_BOUND 0x8886 // Client-32 ++#define AUTHENTICATION_FAILED 0x8887 // Client-32 ++#define INVALID_AUTHEN_HANDLE 0x8888 // Client-32 ++#define AUTHEN_HANDLE_ALREADY_EXISTS 0x8889 // Client-32 ++ ++#define DIFF_OBJECT_ALREADY_AUTHEN 0x8890 // Client-32 ++#define REQUEST_NOT_SERVICEABLE 0x8891 // Client-32 ++#define AUTO_RECONNECT_SO_REBUILD 0x8892 // Client-32 ++#define AUTO_RECONNECT_RETRY_REQUEST 0x8893 // Client-32 ++#define ASYNC_REQUEST_IN_USE 0x8894 // Client-32 ++#define ASYNC_REQUEST_CANCELED 0x8895 // Client-32 ++#define SESS_SVC_ALREADY_REGISTERED 0x8896 // Client-32 ++#define SESS_SVC_NOT_REGISTERED 0x8897 // Client-32 ++#define PREVIOUSLY_AUTHENTICATED 0x8899 // Client-32 ++#define RESOLVE_SVC_PARTIAL 0x889A // Client-32 ++#define NO_DEFAULT_SPECIFIED 0x889B // Client-32 ++#define HOOK_REQUEST_NOT_HANDLED 0x889C // Client-32 ++#define HOOK_REQUEST_BUSY 0x889D // Client-32 ++#define HOOK_REQUEST_QUEUED 0x889D // Client-32 ++#define AUTO_RECONNECT_SO_IGNORE 0x889E // Client-32 ++#define ASYNC_REQUEST_NOT_IN_USE 0x889F // Client-32 ++#define AUTO_RECONNECT_FAILURE 0x88A0 // Client-32 ++#define NET_ERROR_ABORT_APPLICATION 0x88A1 // Client-32 ++#define NET_ERROR_SUSPEND_APPLICATION 0x88A2 // Client-32 ++#define NET_ERROR_ABORTED_PROCESS_GROUP 0x88A3 // Client-32 ++#define NET_ERROR_PASSWORD_HAS_EXPIRED 0x88A5 // Client-32 ++#define NET_ERROR_NETWORK_INACTIVE 0x88A6 // Client-32 ++#define REPLY_TRUNCATED 0x88E6 // 230 NLM ++#define UTF8_CONVERSION_FAILED 0x88F0 // NWCALLS ++ ++/* ++ * Server Errors ++ */ ++ ++#define ERR_INSUFFICIENT_SPACE 0x8901 // 001 ++#define NLM_INVALID_CONNECTION 0x890A // 010 ++#define ERR_TIMEOUT 0x8910 // 016 - nlm connection timeout ++#define ERR_NO_MORE_ENTRY 0x8914 // 020 ++#define ERR_BUFFER_TOO_SMALL 0x8977 // 119 ++#define ERR_VOLUME_FLAG_NOT_SET 0x8978 // 120 the service requested, not avail. on the selected vol. ++#define ERR_NO_ITEMS_FOUND 0x8979 // 121 ++#define ERR_CONN_ALREADY_TEMP 0x897A // 122 ++#define ERR_CONN_ALREADY_LOGGED_IN 0x897B // 123 ++#define ERR_CONN_NOT_AUTHENTICATED 0x897C // 124 ++#define ERR_CONN_NOT_LOGGED_IN 0x897D // 125 ++#define NCP_BOUNDARY_CHECK_FAILED 0x897E // 126 ++#define ERR_LOCK_WAITING 0x897F // 127 ++#define ERR_LOCK_FAIL 0x8980 // 128 ++#define FILE_IN_USE_ERROR 0x8980 // 128 ++#define NO_MORE_FILE_HANDLES 0x8981 // 129 ++#define NO_OPEN_PRIVILEGES 0x8982 // 130 ++#define IO_ERROR_NETWORK_DISK 0x8983 // 131 ++#define ERR_AUDITING_HARD_IO_ERROR 0x8983 // 131 ++#define NO_CREATE_PRIVILEGES 0x8984 // 132 ++#define ERR_AUDITING_NOT_SUPV 0x8984 // 132 ++#define NO_CREATE_DELETE_PRIVILEGES 0x8985 // 133 ++#define CREATE_FILE_EXISTS_READ_ONLY 0x8986 // 134 ++#define WILD_CARDS_IN_CREATE_FILE_NAME 0x8987 // 135 ++#define CREATE_FILENAME_ERROR 0x8987 // 135 ++#define INVALID_FILE_HANDLE 0x8988 // 136 ++#define NO_SEARCH_PRIVILEGES 0x8989 // 137 ++#define NO_DELETE_PRIVILEGES 0x898A // 138 ++#define NO_RENAME_PRIVILEGES 0x898B // 139 ++#define NO_MODIFY_PRIVILEGES 0x898C // 140 ++#define SOME_FILES_AFFECTED_IN_USE 0x898D // 141 ++#define NO_FILES_AFFECTED_IN_USE 0x898E // 142 ++#define SOME_FILES_AFFECTED_READ_ONLY 0x898F // 143 ++#define NO_FILES_AFFECTED_READ_ONLY 0x8990 // 144 ++#define SOME_FILES_RENAMED_NAME_EXISTS 0x8991 // 145 ++#define NO_FILES_RENAMED_NAME_EXISTS 0x8992 // 146 ++#define NO_READ_PRIVILEGES 0x8993 // 147 ++#define NO_WRITE_PRIVILEGES_OR_READONLY 0x8994 // 148 ++#define FILE_DETACHED 0x8995 // 149 ++#define SERVER_OUT_OF_MEMORY 0x8996 // 150 ++#define ERR_TARGET_NOT_A_SUBDIRECTORY 0x8996 // 150 can be changed later (note written by server people). ++#define NO_DISK_SPACE_FOR_SPOOL_FILE 0x8997 // 151 ++#define ERR_AUDITING_NOT_ENABLED 0x8997 // 151 ++#define VOLUME_DOES_NOT_EXIST 0x8998 // 152 ++#define DIRECTORY_FULL 0x8999 // 153 ++#define RENAMING_ACROSS_VOLUMES 0x899A // 154 ++#define BAD_DIRECTORY_HANDLE 0x899B // 155 ++#define INVALID_PATH 0x899C // 156 ++#define NO_MORE_TRUSTEES 0x899C // 156 ++#define NO_MORE_DIRECTORY_HANDLES 0x899D // 157 ++#define INVALID_FILENAME 0x899E // 158 ++#define DIRECTORY_ACTIVE 0x899F // 159 ++#define DIRECTORY_NOT_EMPTY 0x89A0 // 160 ++#define DIRECTORY_IO_ERROR 0x89A1 // 161 ++#define READ_FILE_WITH_RECORD_LOCKED 0x89A2 // 162 ++#define ERR_TRANSACTION_RESTARTED 0x89A3 // 163 ++#define ERR_RENAME_DIR_INVALID 0x89A4 // 164 ++#define ERR_INVALID_OPENCREATE_MODE 0x89A5 // 165 ++#define ERR_ALREADY_IN_USE 0x89A6 // 166 ++#define ERR_AUDITING_ACTIVE 0x89A6 // 166 ++#define ERR_INVALID_RESOURCE_TAG 0x89A7 // 167 ++#define ERR_ACCESS_DENIED 0x89A8 // 168 ++#define ERR_AUDITING_NO_RIGHTS 0x89A8 // 168 ++#define ERR_LINK_IN_PATH 0x89A9 // 169 ++#define INVALID_DATA_TYPE 0x89AA // 170 ++#define INVALID_DATA_STREAM 0x89BE // 190 ++#define INVALID_NAME_SPACE 0x89BF // 191 ++#define NO_ACCOUNTING_PRIVILEGES 0x89C0 // 192 ++#define LOGIN_DENIED_NO_ACCOUNT_BALANCE 0x89C1 // 193 ++#define LOGIN_DENIED_NO_CREDIT 0x89C2 // 194 ++#define ERR_AUDITING_RECORD_SIZE 0x89C2 // 194 ++#define ERR_TOO_MANY_HOLDS 0x89C3 // 195 ++#define ACCOUNTING_DISABLED 0x89C4 // 196 ++#define INTRUDER_DETECTION_LOCK 0x89C5 // 197 ++#define NO_CONSOLE_OPERATOR 0x89C6 // 198 ++#define NO_CONSOLE_PRIVILEGES 0x89C6 // 198 ++#define ERR_Q_IO_FAILURE 0x89D0 // 208 ++#define ERR_NO_QUEUE 0x89D1 // 209 ++#define ERR_NO_Q_SERVER 0x89D2 // 210 ++#define ERR_NO_Q_RIGHTS 0x89D3 // 211 ++#define ERR_Q_FULL 0x89D4 // 212 ++#define ERR_NO_Q_JOB 0x89D5 // 213 ++#define ERR_NO_Q_JOB_RIGHTS 0x89D6 // 214 ++#define ERR_Q_IN_SERVICE 0x89D7 // 215 ++#define PASSWORD_NOT_UNIQUE 0x89D7 // 215 ++#define ERR_Q_NOT_ACTIVE 0x89D8 // 216 ++#define PASSWORD_TOO_SHORT 0x89D8 // 216 ++#define ERR_Q_STN_NOT_SERVER 0x89D9 // 217 ++#define LOGIN_DENIED_NO_CONNECTION 0x89D9 // 217 ++#define ERR_MAXIMUM_LOGINS_EXCEEDED 0x89D9 // 217 ++#define ERR_Q_HALTED 0x89DA // 218 ++#define UNAUTHORIZED_LOGIN_TIME 0x89DA // 218 ++#define UNAUTHORIZED_LOGIN_STATION 0x89DB // 219 ++#define ERR_Q_MAX_SERVERS 0x89DB // 219 ++#define ACCOUNT_DISABLED 0x89DC // 220 ++#define PASSWORD_HAS_EXPIRED_NO_GRACE 0x89DE // 222 ++#define PASSWORD_HAS_EXPIRED 0x89DF // 223 ++#define E_NO_MORE_USERS 0x89E7 // 231 ++#define NOT_ITEM_PROPERTY 0x89E8 // 232 ++#define WRITE_PROPERTY_TO_GROUP 0x89E8 // 232 ++#define MEMBER_ALREADY_EXISTS 0x89E9 // 233 ++#define NO_SUCH_MEMBER 0x89EA // 234 ++#define NOT_GROUP_PROPERTY 0x89EB // 235 ++#define NO_SUCH_SEGMENT 0x89EC // 236 ++#define PROPERTY_ALREADY_EXISTS 0x89ED // 237 ++#define OBJECT_ALREADY_EXISTS 0x89EE // 238 ++#define INVALID_NAME 0x89EF // 239 ++#define WILD_CARD_NOT_ALLOWED 0x89F0 // 240 ++#define INVALID_BINDERY_SECURITY 0x89F1 // 241 ++#define NO_OBJECT_READ_PRIVILEGE 0x89F2 // 242 ++#define NO_OBJECT_RENAME_PRIVILEGE 0x89F3 // 243 ++#define NO_OBJECT_DELETE_PRIVILEGE 0x89F4 // 244 ++#define NO_OBJECT_CREATE_PRIVILEGE 0x89F5 // 245 ++#define NO_PROPERTY_DELETE_PRIVILEGE 0x89F6 // 246 ++#define NO_PROPERTY_CREATE_PRIVILEGE 0x89F7 // 247 ++#define NO_PROPERTY_WRITE_PRIVILEGE 0x89F8 // 248 ++#define NO_FREE_CONNECTION_SLOTS 0x89F9 // 249 ++#define NO_PROPERTY_READ_PRIVILEGE 0x89F9 // 249 ++#define NO_MORE_SERVER_SLOTS 0x89FA // 250 ++#define TEMP_REMAP_ERROR 0x89FA // 250 ++#define INVALID_PARAMETERS 0x89FB // 251 ++#define NO_SUCH_PROPERTY 0x89FB // 251 ++#define ERR_NCP_NOT_SUPPORTED 0x89FB // 251 ++#define INTERNET_PACKET_REQT_CANCELED 0x89FC // 252 ++#define UNKNOWN_FILE_SERVER 0x89FC // 252 ++#define MESSAGE_QUEUE_FULL 0x89FC // 252 ++#define NO_SUCH_OBJECT 0x89FC // 252 ++#define LOCK_COLLISION 0x89FD // 253 ++#define BAD_STATION_NUMBER 0x89FD // 253 ++#define INVALID_PACKET_LENGTH 0x89FD // 253 ++#define UNKNOWN_REQUEST 0x89FD // 253 ++#define BINDERY_LOCKED 0x89FE // 254 ++#define TRUSTEE_NOT_FOUND 0x89FE // 254 ++#define DIRECTORY_LOCKED 0x89FE // 254 ++#define INVALID_SEMAPHORE_NAME_LENGTH 0x89FE // 254 ++#define PACKET_NOT_DELIVERABLE 0x89FE // 254 ++#define SERVER_BINDERY_LOCKED 0x89FE // 254 ++#define SOCKET_TABLE_FULL 0x89FE // 254 ++#define SPOOL_DIRECTORY_ERROR 0x89FE // 254 ++#define SUPERVISOR_HAS_DISABLED_LOGIN 0x89FE // 254 ++#define TIMEOUT_FAILURE 0x89FE // 254 ++#define BAD_PRINTER_ERROR 0x89FF // 255 ++#define BAD_RECORD_OFFSET 0x89FF // 255 ++#define CLOSE_FCB_ERROR 0x89FF // 255 ++#define FILE_EXTENSION_ERROR 0x89FF // 255 ++#define FILE_NAME_ERROR 0x89FF // 255 ++#define HARDWARE_FAILURE 0x89FF // 255 ++#define INVALID_DRIVE_NUMBER 0x89FF // 255 ++#define DOS_INVALID_DRIVE 0x000F // 255 ++#define INVALID_INITIAL_SEMAPHORE_VALUE 0x89FF // 255 ++#define INVALID_SEMAPHORE_HANDLE 0x89FF // 255 ++#define IO_BOUND_ERROR 0x89FF // 255 ++#define NO_FILES_FOUND_ERROR 0x89FF // 255 ++#define NO_RESPONSE_FROM_SERVER 0x89FF // 255 ++#define NO_SUCH_OBJECT_OR_BAD_PASSWORD 0x89FF // 255 ++#define PATH_NOT_LOCATABLE 0x89FF // 255 ++#define QUEUE_FULL_ERROR 0x89FF // 255 ++#define REQUEST_NOT_OUTSTANDING 0x89FF // 255 ++#ifndef SOCKET_ALREADY_OPEN ++#define SOCKET_ALREADY_OPEN 0x89FF // 255 ++#endif ++#define LOCK_ERROR 0x89FF // 255 ++#ifndef FAILURE ++#define FAILURE 0x89FF // 255 Generic Failure ++#endif ++ ++#if 0 ++#define NOT_SAME_LOCAL_DRIVE 0x89F6 ++#define TARGET_DRIVE_NOT_LOCAL 0x89F7 ++#define ALREADY_ATTACHED_TO_SERVER 0x89F8 // 248 ++#define NOT_ATTACHED_TO_SERVER 0x89F8 ++#endif ++ ++/* ++ * Network errors ++ * Decimal values at end of line are 32768 lower than actual ++ */ ++#define NWE_ALREADY_ATTACHED 0x8800 // 0 - Attach attempted to server with valid, existing connection ++#define NWE_CONN_INVALID 0x8801 // 1 - Request attempted with invalid or non-attached connection handle ++#define NWE_DRIVE_IN_USE 0x8802 // 2 - OS/2 only (NOT USED) ++#define NWE_DRIVE_CANNOT_MAP 0x8803 // 3 - Map drive attempted but unable to add new current directory structure ++#define NWE_DRIVE_BAD_PATH 0x8804 // 4 - Map drive attempted with invalid path specification ++#define NWE_NET_RECEIVE 0x8805 // 5 - Attempt to receive from the selected transport failed ++#define NWE_NET_UNKNOWN 0x8806 // 6 - Network send attempted with an un-specific network error ++#define NWE_SERVER_BAD_SLOT 0x8807 // 7 - Server request attempted with invalid server connection slot ++#define NWE_SERVER_NO_SLOTS 0x8808 // 8 - Attach attempted to server with no connection slots available ++#define NWE_NET_SEND 0x8809 // 9 - Attempt to send on the selected transport failed ++#define NWE_SERVER_NO_ROUTE 0x880A // 10 - Attempted to find route to server where no route exists ++#define NWE_BAD_LOCAL_TARGET 0x880B // 11 - OS/2 only ++#define NWE_REQ_TOO_MANY_REQ_FRAGS 0x880C // 12 - Attempted request with too many request fragments specified ++#define NWE_CONN_LIST_OVERFLOW 0x880D // 13 ++#define NWE_BUFFER_OVERFLOW 0x880E // 14 - Attempt to receive more data than the reply buffer had room for ++#define NWE_SERVER_NO_CONN 0x880F // 15 - Attempt to get connection for a server not connected ++#define NWE_NO_ROUTER_FOUND 0x8810 // 16 - OS/2 only ++#define NWE_FUNCTION_INVALID 0x8811 // 17 - Attempted function call to non- existent or illegal function ++#define NWE_SCAN_COMPLETE 0x8812 ++#define NWE_UNSUPPORTED_NAME_FORMAT_TYP 0x8813 ++#define NWE_HANDLE_ALREADY_LICENSED 0x8814 ++#define NWE_HANDLE_ALREADY_UNLICENSED 0x8815 ++#define NWE_INVALID_NCP_PACKET_LENGTH 0x8816 ++#define NWE_SETTING_UP_TIMEOUT 0x8817 ++#define NWE_SETTING_SIGNALS 0x8818 ++#define NWE_SERVER_CONNECTION_LOST 0x8819 ++#define NWE_OUT_OF_HEAP_SPACE 0x881A ++#define NWE_INVALID_SERVICE_REQUEST 0x881B ++#define NWE_INVALID_TASK_NUMBER 0x881C ++#define NWE_INVALID_MESSAGE_LENGTH 0x881D ++#define NWE_EA_SCAN_DONE 0x881E ++#define NWE_BAD_CONNECTION_NUMBER 0x881F ++#define NWE_MULT_TREES_NOT_SUPPORTED 0x8820 // 32 - Attempt to open a connection to a DS tree other than the default tree ++#define NWE_CONN_NOT_SAME 0x8830 // 48 - Internal server request attempted across different server connections ++#define NWE_CONN_PRIMARY_NOT_SET 0x8831 // 49 - Attempt to retrieve default connection with no primary connection set ++#define NWE_PRN_CAPTURE_NOT_IN_PROGRESS 0x8832 // 50 - Capture information requested on port with no capture in progress ++#define NWE_BUFFER_INVALID_LEN 0x8833 // 51 - Used to indicate length which caller requested on a GetDNC or SetDNC was too large ++#define NWE_USER_NO_NAME 0x8834 // 52 ++#define NWE_PRN_NO_LOCAL_SPOOLER 0x8835 // 53 - Capture requested without having the local print spooler installed ++#define NWE_PARAM_INVALID 0x8836 // 54 - Attempted function with an invalid function parameter specified ++#define NWE_CFG_OPEN_FAILED 0x8837 // 55 - OS/2 only ++#define NWE_CFG_NO_FILE 0x8838 // 56 - OS/2 only ++#define NWE_CFG_READ_FAILED 0x8839 // 57 - OS/2 only ++#define NWE_CFG_LINE_TOO_LONG 0x883A // 58 - OS/2 only ++#define NWE_CFG_LINES_IGNORED 0x883B // 59 - OS/2 only ++#define NWE_RESOURCE_NOT_OWNED 0x883C // 60 - Attempted request made with a parameter using foriegn resource ++#define NWE_DAEMON_INSTALLED 0x883D // 61 - OS/2 only ++#define NWE_PRN_SPOOLER_INSTALLED 0x883E // 62 - Attempted load of print spooler with print spooler already installed ++#define NWE_CONN_TABLE_FULL 0x883F // 63 - Attempted to allocate a connection handle with no more local connection table entries ++#define NWE_CFG_SECTION_NOT_FOUND 0x8840 // 64 - OS/2 only ++#define NWE_TRAN_INVALID_TYPE 0x8841 // 65 - Attempted function on a connection with an invalid transport selected ++#define NWE_TDS_TAG_IN_USE 0x8842 // 66 - OS/2 only ++#define NWE_TDS_OUT_OF_MEMORY 0x8843 // 67 - OS/2 only ++#define NWE_TDS_INVALID_TAG 0x8844 // 68 - Attempted TDS function with invalid tag ++#define NWE_TDS_WRITE_TRUNCATED 0x8845 // 69 - Attempted TDS write with buffer that exceeded buffer ++#define NWE_DS_NO_CONN 0x8846 // 70 ++#define NWE_SERVICE_BUSY 0x8846 // 70 - Attempted request made to partially asynchronous function in busy state ++#define NWE_SERVER_NOT_FOUND 0x8847 // 71 - Attempted connect failed to find any servers responding ++#define NWE_VLM_INVALID 0x8848 // 72 - Attempted function call to non-existant or not-loaded overlay ++#define NWE_DRIVE_ALREADY_MAPPED 0x8849 // 73 - Attempted map to network drive that was already mapped ++#define NWE_DRIVE_LOCAL_IN_USE 0x884A // 74 - Attempted map to local drive that was in use ++#define NWE_DRIVE_NONE_AVAILABLE 0x884B // 75 - Attempted map to next available drive when none were available ++#define NWE_DEVICE_NOT_REDIRECTED 0x884C // 76 - The device is not redirected ++#define NWE_FILE_MAX_REACHED 0x884D // 77 - Maximum number of files was reached ++#define NWE_UNLOAD_FAILED 0x884E // 78 - Attempted unload failed ++#define NWE_CONN_IN_USE 0x884F // 79 - Attempted re-use of already in use connection entry ++#define NWE_REQ_TOO_MANY_REP_FRAGS 0x8850 // 80 - Attempted request with too many reply fragments specified ++#define NWE_NAME_TABLE_FULL 0x8851 // 81 - Attempted to add a name into the name table after it was full ++#define NWE_SOCKET_NOT_OPEN 0x8852 // 82 - Listen was posted on unopened socket ++#define NWE_MEMORY_MGR_ERROR 0x8853 // 83 - Attempted enhanced memory operation failed ++#define NWE_SFT3_ERROR 0x8854 // 84 - An SFT3 switch occured mid-transfer ++#define NWE_DS_PREFERRED_NOT_FOUND 0x8855 // 85 - the preferred directory server was not established but another directory server was returned ++#define NWE_DEVICE_NOT_RECOGNIZED 0x8856 // 86 - used to determine if the device is not used by VISE so pass it on to the next redirector, if any. ++#define NWE_NET_INVALID_TYPE 0x8857 // 87 - the network type (Bind/NDS) does not match the server version ++#define NWE_FILE_OPEN_FAILED 0x8858 // 88 - generic open failure error, invalid path, access denied, etc.. ++#define NWE_DS_PREFERRED_NOT_SPECIFIED 0x8859 // 89 - no preferred name specified ++#define NWE_SOCKET_OPEN_FAILED 0x885A // 90 - error opening a socket ++#define NWE_SIGNATURE_LEVEL_CONFLICT 0x8861 ++#define NWE_NO_LOCK_FOUND 0x8862 // OS/2 - process lock on conn handle failed, process ID not recognized ++#define NWE_LOCK_TABLE_FULL 0x8863 // OS/2 - process lock on conn handle failed, process lock table full ++#define NWE_INVALID_MATCH_DATA 0x8864 ++#define NWE_MATCH_FAILED 0x8865 ++#define NWE_NO_MORE_ENTRIES 0x8866 ++#define NWE_INSUFFICIENT_RESOURCES 0x8867 ++#define NWE_STRING_TRANSLATION 0x8868 ++#define NWE_ACCESS_VIOLATION 0x8869 ++#define NWE_NOT_AUTHENTICATED 0x886A ++#define NWE_INVALID_LEVEL 0x886B ++#define NWE_RESOURCE_LOCK 0x886C ++#define NWE_INVALID_NAME_FORMAT 0x886D ++#define NWE_OBJECT_EXISTS 0x886E ++#define NWE_OBJECT_NOT_FOUND 0x886F ++#define NWE_UNSUPPORTED_TRAN_TYPE 0x8870 ++#define NWE_INVALID_STRING_TYPE 0x8871 ++#define NWE_INVALID_OWNER 0x8872 ++#define NWE_UNSUPPORTED_AUTHENTICATOR 0x8873 ++#define NWE_IO_PENDING 0x8874 ++#define NWE_INVALID_DRIVE_NUMBER 0x8875 ++#define NWE_REPLY_TRUNCATED 0x88e6 // 230 NLM ++#define NWE_REQUESTER_FAILURE 0x88FF ++ ++/* ++ * Server Errors ++ */ ++#define NWE_INSUFFICIENT_SPACE 0x8901 // 001 ++#define NWE_INVALID_CONNECTION 0x890a // 010 - nlm invalid connection ++#define NWE_TIMEOUT 0x8910 // 016 - nlm connection timeout ++#define NWE_NO_MORE_ENTRY 0x8914 // 020 ++#define NWE_BUFFER_TOO_SMALL 0x8977 // 119 ++#define NWE_VOL_FLAG_NOT_SET 0x8978 // 120 the service requested, not avail. on the selected vol. ++#define NWE_NO_ITEMS_FOUND 0x8979 // 121 ++#define NWE_CONN_ALREADY_TEMP 0x897a // 122 ++#define NWE_CONN_ALREADY_LOGGED_IN 0x897b // 123 ++#define NWE_CONN_NOT_AUTHENTICATED 0x897c // 124 ++#define NWE_CONN_NOT_LOGGED_IN 0x897d // 125 ++#define NWE_NCP_BOUNDARY_CHECK_FAILED 0x897e // 126 ++#define NWE_LOCK_WAITING 0x897f // 127 ++#define NWE_LOCK_FAIL 0x8980 // 128 ++#define NWE_FILE_IN_USE 0x8980 // 128 ++#define NWE_FILE_NO_HANDLES 0x8981 // 129 ++#define NWE_FILE_NO_OPEN_PRIV 0x8982 // 130 ++#define NWE_DISK_IO_ERROR 0x8983 // 131 ++#define NWE_AUDITING_HARD_IO_ERROR 0x8983 // 131 ++#define NWE_FILE_NO_CREATE_PRIV 0x8984 // 132 ++#define NWE_AUDITING_NOT_SUPV 0x8984 // 132 ++#define NWE_FILE_NO_CREATE_DEL_PRIV 0x8985 // 133 ++#define NWE_FILE_EXISTS_READ_ONLY 0x8986 // 134 ++#define NWE_FILE_WILD_CARDS_IN_NAME 0x8987 // 135 ++#define NWE_FILE_INVALID_HANDLE 0x8988 // 136 ++#define NWE_FILE_NO_SRCH_PRIV 0x8989 // 137 ++#define NWE_FILE_NO_DEL_PRIV 0x898A // 138 ++#define NWE_FILE_NO_RENAME_PRIV 0x898B // 139 ++#define NWE_FILE_NO_MOD_PRIV 0x898C // 140 ++#define NWE_FILE_SOME_IN_USE 0x898D // 141 ++#define NWE_FILE_NONE_IN_USE 0x898E // 142 ++#define NWE_FILE_SOME_READ_ONLY 0x898F // 143 ++#define NWE_FILE_NONE_READ_ONLY 0x8990 // 144 ++#define NWE_FILE_SOME_RENAMED_EXIST 0x8991 // 145 ++#define NWE_FILE_NONE_RENAMED_EXIST 0x8992 // 146 ++#define NWE_FILE_NO_READ_PRIV 0x8993 // 147 ++#define NWE_FILE_NO_WRITE_PRIV 0x8994 // 148 ++#define NWE_FILE_READ_ONLY 0x8994 // 148 ++#define NWE_FILE_DETACHED 0x8995 // 149 ++#define NWE_SERVER_OUT_OF_MEMORY 0x8996 // 150 ++#define NWE_DIR_TARGET_INVALID 0x8996 // 150 ++#define NWE_DISK_NO_SPOOL_SPACE 0x8997 // 151 ++#define NWE_AUDITING_NOT_ENABLED 0x8997 // 151 ++#define NWE_VOL_INVALID 0x8998 // 152 ++#define NWE_DIR_FULL 0x8999 // 153 ++#define NWE_VOL_RENAMING_ACROSS 0x899A // 154 ++#define NWE_DIRHANDLE_INVALID 0x899B // 155 ++#define NWE_PATH_INVALID 0x899C // 156 ++#define NWE_TRUSTEES_NO_MORE 0x899C // 156 ++#define NWE_DIRHANDLE_NO_MORE 0x899D // 157 ++#define NWE_FILE_NAME_INVALID 0x899E // 158 ++#define NWE_DIR_ACTIVE 0x899F // 159 ++#define NWE_DIR_NOT_EMPTY 0x89A0 // 160 ++#define NWE_DIR_IO_ERROR 0x89A1 // 161 ++#define NWE_FILE_IO_LOCKED 0x89A2 // 162 ++#define NWE_TTS_RANSACTION_RESTARTED 0x89A3 // 163 ++#define NWE_TTS_TRANSACTION_RESTARTED 0x89A3 // 163 ++#define NWE_DIR_RENAME_INVALID 0x89A4 // 164 ++#define NWE_FILE_OPENCREAT_MODE_INVALID 0x89A5 // 165 ++#define NWE_ALREADY_IN_USE 0x89A6 // 166 ++#define NWE_AUDITING_ACTIVE 0x89A6 // 166 ++#define NWE_RESOURCE_TAG_INVALID 0x89A7 // 167 ++#define NWE_ACCESS_DENIED 0x89A8 // 168 ++#define NWE_AUDITING_NO_RIGHTS 0x89A8 // 168 ++#define NWE_LINK_IN_PATH 0x89A9 // 169 ++#define NWE_INVALID_DATA_TYPE_FLAG 0x89AA // 170 (legacy vol with UTF8) ++#define NWE_DATA_STREAM_INVALID 0x89BE // 190 ++#define NWE_NAME_SPACE_INVALID 0x89BF // 191 ++#define NWE_ACCTING_NO_PRIV 0x89C0 // 192 ++#define NWE_ACCTING_NO_BALANCE 0x89C1 // 193 ++#define NWE_ACCTING_NO_CREDIT 0x89C2 // 194 ++#define NWE_AUDITING_RECORD_SIZE 0x89C2 // 194 ++#define NWE_ACCTING_TOO_MANY_HOLDS 0x89C3 // 195 ++#define NWE_ACCTING_DISABLED 0x89C4 // 196 ++#define NWE_LOGIN_LOCKOUT 0x89C5 // 197 ++#define NWE_CONSOLE_NO_PRIV 0x89C6 // 198 ++#define NWE_Q_IO_FAILURE 0x89D0 // 208 ++#define NWE_Q_NONE 0x89D1 // 209 ++#define NWE_Q_NO_SERVER 0x89D2 // 210 ++#define NWE_Q_NO_RIGHTS 0x89D3 // 211 ++#define NWE_Q_FULL 0x89D4 // 212 ++#define NWE_Q_NO_JOB 0x89D5 // 213 ++#define NWE_Q_NO_JOB_RIGHTS 0x89D6 // 214 ++#define NWE_PASSWORD_UNENCRYPTED 0x89D6 // 214 ++#define NWE_Q_IN_SERVICE 0x89D7 // 215 ++#define NWE_PASSWORD_NOT_UNIQUE 0x89D7 // 215 ++#define NWE_Q_NOT_ACTIVE 0x89D8 // 216 ++#define NWE_PASSWORD_TOO_SHORT 0x89D8 // 216 ++#define NWE_Q_STN_NOT_SERVER 0x89D9 // 217 ++#define NWE_LOGIN_NO_CONN 0x89D9 // 217 ++#define NWE_LOGIN_MAX_EXCEEDED 0x89D9 // 217 ++#define NWE_Q_HALTED 0x89DA // 218 ++#define NWE_LOGIN_UNAUTHORIZED_TIME 0x89DA // 218 ++#define NWE_LOGIN_UNAUTHORIZED_STATION 0x89DB // 219 ++#define NWE_Q_MAX_SERVERS 0x89DB // 219 ++#define NWE_ACCT_DISABLED 0x89DC // 220 ++#define NWE_PASSWORD_INVALID 0x89DE // 222 ++#define NWE_PASSWORD_EXPIRED 0x89DF // 223 ++#define NWE_LOGIN_NO_CONN_AVAIL 0x89E0 // 224 ++#define NWE_E_NO_MORE_USERS 0x89E7 // 231 ++#define NWE_BIND_NOT_ITEM_PROP 0x89E8 // 232 ++#define NWE_BIND_WRITE_TO_GROUP_PROP 0x89E8 // 232 ++#define NWE_BIND_MEMBER_ALREADY_EXISTS 0x89E9 // 233 ++#define NWE_BIND_NO_SUCH_MEMBER 0x89EA // 234 ++#define NWE_BIND_NOT_GROUP_PROP 0x89EB // 235 ++#define NWE_BIND_NO_SUCH_SEGMENT 0x89EC // 236 ++#define NWE_BIND_PROP_ALREADY_EXISTS 0x89ED // 237 ++#define NWE_BIND_OBJ_ALREADY_EXISTS 0x89EE // 238 ++#define NWE_BIND_NAME_INVALID 0x89EF // 239 ++#define NWE_BIND_WILDCARD_INVALID 0x89F0 // 240 ++#define NWE_BIND_SECURITY_INVALID 0x89F1 // 241 ++#define NWE_BIND_OBJ_NO_READ_PRIV 0x89F2 // 242 ++#define NWE_BIND_OBJ_NO_RENAME_PRIV 0x89F3 // 243 ++#define NWE_BIND_OBJ_NO_DELETE_PRIV 0x89F4 // 244 ++#define NWE_BIND_OBJ_NO_CREATE_PRIV 0x89F5 // 245 ++#define NWE_BIND_PROP_NO_DELETE_PRIV 0x89F6 // 246 ++#define NWE_BIND_PROP_NO_CREATE_PRIV 0x89F7 // 247 ++#define NWE_BIND_PROP_NO_WRITE_PRIV 0x89F8 // 248 ++#define NWE_BIND_PROP_NO_READ_PRIV 0x89F9 // 249 ++#define NWE_NO_FREE_CONN_SLOTS 0x89F9 // 249 ++#define NWE_NO_MORE_SERVER_SLOTS 0x89FA // 250 ++#define NWE_TEMP_REMAP_ERROR 0x89FA // 250 ++#define NWE_PARAMETERS_INVALID 0x89FB // 251 ++#define NWE_BIND_NO_SUCH_PROP 0x89FB // 251 ++#define NWE_NCP_NOT_SUPPORTED 0x89FB // 251 ++#define NWE_INET_PACKET_REQ_CANCELED 0x89FC // 252 ++#define NWE_SERVER_UNKNOWN 0x89FC // 252 ++#define NWE_MSG_Q_FULL 0x89FC // 252 ++#define NWE_BIND_NO_SUCH_OBJ 0x89FC // 252 ++#define NWE_LOCK_COLLISION 0x89FD // 253 ++#define NWE_CONN_NUM_INVALID 0x89FD // 253 ++#define NWE_PACKET_LEN_INVALID 0x89FD // 253 ++#define NWE_UNKNOWN_REQ 0x89FD // 253 ++#define NWE_BIND_LOCKED 0x89FE // 254 ++#define NWE_TRUSTEE_NOT_FOUND 0x89FE // 254 ++#define NWE_DIR_LOCKED 0x89FE // 254 ++#define NWE_SEM_INVALID_NAME_LEN 0x89FE // 254 ++#define NWE_PACKET_NOT_DELIVERABLE 0x89FE // 254 ++#define NWE_SOCKET_TABLE_FULL 0x89FE // 254 ++#define NWE_SPOOL_DIR_ERROR 0x89FE // 254 ++#define NWE_LOGIN_DISABLED_BY_SUPER 0x89FE // 254 ++#define NWE_TIMEOUT_FAILURE 0x89FE // 254 ++#define NWE_FILE_EXT 0x89FF // 255 ++#define NWE_FILE_NAME 0x89FF // 255 ++#define NWE_HARD_FAILURE 0x89FF // 255 ++#define NWE_FCB_CLOSE 0x89FF // 255 ++#define NWE_IO_BOUND 0x89FF // 255 ++#define NWE_BAD_SPOOL_PRINTER 0x89FF // 255 ++#define NWE_BAD_RECORD_OFFSET 0x89FF // 255 ++#define NWE_DRIVE_INVALID_NUM 0x89FF // 255 ++#define NWE_SEM_INVALID_INIT_VAL 0x89FF // 255 ++#define NWE_SEM_INVALID_HANDLE 0x89FF // 255 ++#define NWE_NO_FILES_FOUND_ERROR 0x89FF // 255 ++#define NWE_NO_RESPONSE_FROM_SERVER 0x89FF // 255 ++#define NWE_NO_OBJ_OR_BAD_PASSWORD 0x89FF // 255 ++#define NWE_PATH_NOT_LOCATABLE 0x89FF // 255 ++#define NWE_Q_FULL_ERROR 0x89FF // 255 ++#define NWE_REQ_NOT_OUTSTANDING 0x89FF // 255 ++#define NWE_SOCKET_ALREADY_OPEN 0x89FF // 255 ++#define NWE_LOCK_ERROR 0x89FF // 255 ++#define NWE_FAILURE 0x89FF // 255 Generic Failure ++ ++#endif /* __NOVFS_ERROR_H */ +--- /dev/null ++++ b/fs/novfs/proc.c +@@ -0,0 +1,149 @@ ++/* ++ * Novell NCP Redirector for Linux ++ * Author: James Turner ++ * ++ * This module contains functions that create the interface to the proc ++ * filesystem. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "vfs.h" ++ ++struct proc_dir_entry *novfs_procfs_dir; ++struct proc_dir_entry *Novfs_Control; ++struct proc_dir_entry *Novfs_Library; ++struct proc_dir_entry *Novfs_Version; ++ ++static struct file_operations novfs_daemon_proc_fops; ++static struct file_operations novfs_lib_proc_fops; ++ ++/*===[ Code ]=============================================================*/ ++ ++static int Novfs_Get_Version(char *page, char **start, off_t off, int count, int *eof, void *data) ++{ ++ char *buf, tbuf[48]; ++ int len = 0, i; ++ ++ if (!off) { ++ buf = page + off; ++ *start = buf; ++ len = sprintf(buf, "Novfs Version=%s\n", NOVFS_VERSION_STRING); ++ i = novfs_daemon_getversion(tbuf, sizeof(tbuf)); ++ if ((i > 0) && i < (count - len)) { ++ len += sprintf(buf + len, "Novfsd Version=%s\n", tbuf); ++ } ++ ++ if (novfs_current_mnt) { ++ i = strlen(novfs_current_mnt); ++ if ((i > 0) && i < (count - len)) { ++ len += ++ sprintf(buf + len, "Novfs mount=%s\n", ++ novfs_current_mnt); ++ } ++ } ++ DbgPrint("%s", buf); ++ } ++ *eof = 1; ++ return (len); ++} ++ ++int novfs_proc_init(void) ++{ ++ int retCode = 0; ++ ++ novfs_procfs_dir = proc_mkdir(MODULE_NAME, NULL); ++ if (novfs_procfs_dir) { ++ ++ Novfs_Control = create_proc_entry("Control", 0600, novfs_procfs_dir); ++ ++ if (Novfs_Control) { ++ Novfs_Control->size = 0; ++ memcpy(&novfs_daemon_proc_fops, ++ Novfs_Control->proc_fops, ++ sizeof(struct file_operations)); ++ ++ /* ++ * Setup our functions ++ */ ++ novfs_daemon_proc_fops.owner = THIS_MODULE; ++ novfs_daemon_proc_fops.open = novfs_daemon_open_control; ++ novfs_daemon_proc_fops.release = novfs_daemon_close_control; ++ novfs_daemon_proc_fops.read = novfs_daemon_cmd_send; ++ novfs_daemon_proc_fops.write = novfs_daemon_recv_reply; ++ novfs_daemon_proc_fops.ioctl = novfs_daemon_ioctl; ++ ++ Novfs_Control->proc_fops = &novfs_daemon_proc_fops; ++ } else { ++ remove_proc_entry(MODULE_NAME, NULL); ++ return (-ENOENT); ++ } ++ ++ Novfs_Library = create_proc_entry("Library", 0666, novfs_procfs_dir); ++ if (Novfs_Library) { ++ Novfs_Library->size = 0; ++ ++ /* ++ * Setup our file functions ++ */ ++ memcpy(&novfs_lib_proc_fops, Novfs_Library->proc_fops, ++ sizeof(struct file_operations)); ++ novfs_lib_proc_fops.owner = THIS_MODULE; ++ novfs_lib_proc_fops.open = novfs_daemon_lib_open; ++ novfs_lib_proc_fops.release = novfs_daemon_lib_close; ++ novfs_lib_proc_fops.read = novfs_daemon_lib_read; ++ novfs_lib_proc_fops.write = novfs_daemon_lib_write; ++ novfs_lib_proc_fops.llseek = novfs_daemon_lib_llseek; ++ novfs_lib_proc_fops.ioctl = novfs_daemon_lib_ioctl; ++ Novfs_Library->proc_fops = &novfs_lib_proc_fops; ++ } else { ++ remove_proc_entry("Control", novfs_procfs_dir); ++ remove_proc_entry(MODULE_NAME, NULL); ++ return (-ENOENT); ++ } ++ ++ Novfs_Version = ++ create_proc_read_entry("Version", 0444, novfs_procfs_dir, ++ Novfs_Get_Version, NULL); ++ if (Novfs_Version) { ++ Novfs_Version->size = 0; ++ } else { ++ remove_proc_entry("Library", novfs_procfs_dir); ++ remove_proc_entry("Control", novfs_procfs_dir); ++ remove_proc_entry(MODULE_NAME, NULL); ++ retCode = -ENOENT; ++ } ++ } else { ++ retCode = -ENOENT; ++ } ++ return (retCode); ++} ++ ++void novfs_proc_exit(void) ++{ ++ ++ DbgPrint("remove_proc_entry(Version, NULL)\n"); ++ remove_proc_entry("Version", novfs_procfs_dir); ++ ++ DbgPrint("remove_proc_entry(Control, NULL)\n"); ++ remove_proc_entry("Control", novfs_procfs_dir); ++ ++ DbgPrint("remove_proc_entry(Library, NULL)\n"); ++ remove_proc_entry("Library", novfs_procfs_dir); ++ ++ DbgPrint("remove_proc_entry(%s, NULL)\n", ++ MODULE_NAME); ++ remove_proc_entry(MODULE_NAME, NULL); ++ ++ DbgPrint("done\n"); ++} +--- /dev/null ++++ b/fs/novfs/profile.c +@@ -0,0 +1,704 @@ ++/* ++ * Novell NCP Redirector for Linux ++ * Author: James Turner ++ * ++ * This file contains a debugging code for the novfs VFS. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "vfs.h" ++ ++/*===[ Manifest constants ]===============================================*/ ++#define DBGBUFFERSIZE (1024*1024*32) ++ ++/*===[ Type definitions ]=================================================*/ ++struct local_rtc_time { ++ int tm_sec; ++ int tm_min; ++ int tm_hour; ++ int tm_mday; ++ int tm_mon; ++ int tm_year; ++ int tm_wday; ++ int tm_yday; ++ int tm_isdst; ++}; ++ ++char *DbgPrintBuffer = NULL; ++char DbgPrintOn = 0; ++char DbgSyslogOn = 0; ++char DbgProfileOn = 0; ++ ++static unsigned long DbgPrintBufferOffset = 0; ++static unsigned long DbgPrintBufferReadOffset = 0; ++static unsigned long DbgPrintBufferSize = DBGBUFFERSIZE; ++ ++static struct file_operations Dbg_proc_file_operations; ++static struct file_operations dentry_proc_file_ops; ++static struct file_operations inode_proc_file_ops; ++ ++static struct proc_dir_entry *dbg_dir = NULL; ++static struct proc_dir_entry *dbg_file = NULL; ++static struct proc_dir_entry *dentry_file = NULL; ++static struct proc_dir_entry *inode_file = NULL; ++ ++static DECLARE_MUTEX(LocalPrint_lock); ++ ++static ssize_t User_proc_write_DbgBuffer(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos) ++{ ++ ssize_t retval = nbytes; ++ u_char *lbuf, *p; ++ int i; ++ u_long cpylen; ++ ++ lbuf = kmalloc(nbytes + 1, GFP_KERNEL); ++ if (lbuf) { ++ cpylen = copy_from_user(lbuf, buf, nbytes); ++ ++ lbuf[nbytes] = 0; ++ DbgPrint("%s", lbuf); ++ ++ for (i = 0; lbuf[i] && lbuf[i] != '\n'; i++) ; ++ ++ if ('\n' == lbuf[i]) { ++ lbuf[i] = '\0'; ++ } ++ ++ if (!strcmp("on", lbuf)) { ++ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0; ++ DbgPrintOn = 1; ++ } else if (!strcmp("off", lbuf)) { ++ DbgPrintOn = 0; ++ } else if (!strcmp("reset", lbuf)) { ++ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0; ++ } else if (NULL != (p = strchr(lbuf, ' '))) { ++ *p++ = '\0'; ++ if (!strcmp("syslog", lbuf)) { ++ ++ if (!strcmp("on", p)) { ++ DbgSyslogOn = 1; ++ } else if (!strcmp("off", p)) { ++ DbgSyslogOn = 0; ++ } ++ } else if (!strcmp("novfsd", lbuf)) { ++ novfs_daemon_debug_cmd_send(p); ++ } else if (!strcmp("file_update_timeout", lbuf)) { ++ novfs_update_timeout = ++ simple_strtoul(p, NULL, 0); ++ } else if (!strcmp("cache", lbuf)) { ++ if (!strcmp("on", p)) { ++ novfs_page_cache = 1; ++ } else if (!strcmp("off", p)) { ++ novfs_page_cache = 0; ++ } ++ } else if (!strcmp("profile", lbuf)) { ++ if (!strcmp("on", p)) { ++ DbgProfileOn = 1; ++ } else if (!strcmp("off", p)) { ++ DbgProfileOn = 0; ++ } ++ } ++ } ++ kfree(lbuf); ++ } ++ ++ return (retval); ++} ++ ++static ssize_t User_proc_read_DbgBuffer(struct file *file, char *buf, size_t nbytes, loff_t * ppos) ++{ ++ ssize_t retval = 0; ++ size_t count; ++ ++ if (0 != (count = DbgPrintBufferOffset - DbgPrintBufferReadOffset)) { ++ ++ if (count > nbytes) { ++ count = nbytes; ++ } ++ ++ count -= ++ copy_to_user(buf, &DbgPrintBuffer[DbgPrintBufferReadOffset], ++ count); ++ ++ if (count == 0) { ++ if (retval == 0) ++ retval = -EFAULT; ++ } else { ++ DbgPrintBufferReadOffset += count; ++ if (DbgPrintBufferReadOffset >= DbgPrintBufferOffset) { ++ DbgPrintBufferOffset = ++ DbgPrintBufferReadOffset = 0; ++ } ++ retval = count; ++ } ++ } ++ ++ return retval; ++} ++ ++static int proc_read_DbgBuffer(char *page, char **start, off_t off, int count, int *eof, void *data) ++{ ++ int len; ++ ++ printk(KERN_ALERT "proc_read_DbgBuffer: off=%ld count=%d DbgPrintBufferOffset=%lu DbgPrintBufferReadOffset=%lu\n", off, count, DbgPrintBufferOffset, DbgPrintBufferReadOffset); ++ ++ len = DbgPrintBufferOffset - DbgPrintBufferReadOffset; ++ ++ if ((int)(DbgPrintBufferOffset - DbgPrintBufferReadOffset) > count) ++ len = count; ++ ++ if (len) { ++ memcpy(page, &DbgPrintBuffer[DbgPrintBufferReadOffset], len); ++ DbgPrintBufferReadOffset += len; ++ } ++ ++ if (DbgPrintBufferReadOffset >= DbgPrintBufferOffset) ++ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0; ++ ++ printk(KERN_ALERT "proc_read_DbgBuffer: return %d\n", len); ++ ++ return len; ++} ++ ++#define DBG_BUFFER_SIZE (2*1024) ++ ++static int LocalPrint(char *Fmt, ...) ++{ ++ int len = 0; ++ va_list args; ++ ++ if (DbgPrintBuffer) { ++ va_start(args, Fmt); ++ len += vsnprintf(DbgPrintBuffer + DbgPrintBufferOffset, ++ DbgPrintBufferSize - DbgPrintBufferOffset, ++ Fmt, args); ++ DbgPrintBufferOffset += len; ++ } ++ ++ return (len); ++} ++ ++int ___DbgPrint(const char *site, const char *Fmt, ...) ++{ ++ char *buf; ++ int len = 0; ++ unsigned long offset; ++ va_list args; ++ ++ if ((DbgPrintBuffer && DbgPrintOn) || DbgSyslogOn) { ++ buf = kmalloc(DBG_BUFFER_SIZE, GFP_KERNEL); ++ ++ if (buf) { ++ va_start(args, Fmt); ++ len = snprintf(buf, DBG_BUFFER_SIZE, "[%d] %s ", current->pid, site); ++ len += vsnprintf(buf + len, DBG_BUFFER_SIZE - len, Fmt, ++ args); ++ if (-1 == len) { ++ len = DBG_BUFFER_SIZE - 1; ++ buf[len] = '\0'; ++ } ++ /* ++ len = sprintf(&DbgPrintBuffer[offset], "[%llu] ", ts); ++ len += vsprintf(&DbgPrintBuffer[offset+len], Fmt, args); ++ */ ++ ++ if (len) { ++ if (DbgSyslogOn) { ++ printk("<6>%s", buf); ++ } ++ ++ if (DbgPrintBuffer && DbgPrintOn) { ++ if ((DbgPrintBufferOffset + len) > ++ DbgPrintBufferSize) { ++ offset = DbgPrintBufferOffset; ++ DbgPrintBufferOffset = 0; ++ memset(&DbgPrintBuffer[offset], ++ 0, ++ DbgPrintBufferSize - ++ offset); ++ } ++ ++ mb(); ++ ++ if ((DbgPrintBufferOffset + len) < ++ DbgPrintBufferSize) { ++ DbgPrintBufferOffset += len; ++ offset = ++ DbgPrintBufferOffset - len; ++ memcpy(&DbgPrintBuffer[offset], ++ buf, len + 1); ++ } ++ } ++ } ++ kfree(buf); ++ } ++ } ++ ++ return (len); ++} ++ ++static void doline(unsigned char *b, unsigned char *e, unsigned char *l) ++{ ++ unsigned char c; ++ ++ *b++ = ' '; ++ ++ while (l < e) { ++ c = *l++; ++ if ((c < ' ') || (c > '~')) { ++ c = '.'; ++ } ++ *b++ = c; ++ *b = '\0'; ++ } ++} ++ ++void novfs_dump(int size, void *dumpptr) ++{ ++ unsigned char *ptr = (unsigned char *)dumpptr; ++ unsigned char *line = NULL, buf[100], *bptr = buf; ++ int i; ++ ++ if (DbgPrintBuffer || DbgSyslogOn) { ++ if (size) { ++ for (i = 0; i < size; i++) { ++ if (0 == (i % 16)) { ++ if (line) { ++ doline(bptr, ptr, line); ++ __DbgPrint("%s\n", buf); ++ bptr = buf; ++ } ++ bptr += sprintf(bptr, "0x%p: ", ptr); ++ line = ptr; ++ } ++ bptr += sprintf(bptr, "%02x ", *ptr++); ++ } ++ doline(bptr, ptr, line); ++ __DbgPrint("%s\n", buf); ++ } ++ } ++} ++ ++#define FEBRUARY 2 ++#define STARTOFTIME 1970 ++#define SECDAY 86400L ++#define SECYR (SECDAY * 365) ++#define leapyear(year) ((year) % 4 == 0) ++#define days_in_year(a) (leapyear(a) ? 366 : 365) ++#define days_in_month(a) (month_days[(a) - 1]) ++ ++static int month_days[12] = { ++ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ++}; ++ ++/* ++ * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) ++ */ ++static void NovfsGregorianDay(struct local_rtc_time *tm) ++{ ++ int leapsToDate; ++ int lastYear; ++ int day; ++ int MonthOffset[] = ++ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; ++ ++ lastYear = tm->tm_year - 1; ++ ++ /* ++ * Number of leap corrections to apply up to end of last year ++ */ ++ leapsToDate = lastYear / 4 - lastYear / 100 + lastYear / 400; ++ ++ /* ++ * This year is a leap year if it is divisible by 4 except when it is ++ * divisible by 100 unless it is divisible by 400 ++ * ++ * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be ++ */ ++ if ((tm->tm_year % 4 == 0) && ++ ((tm->tm_year % 100 != 0) || (tm->tm_year % 400 == 0)) && ++ (tm->tm_mon > 2)) { ++ /* ++ * We are past Feb. 29 in a leap year ++ */ ++ day = 1; ++ } else { ++ day = 0; ++ } ++ ++ day += lastYear * 365 + leapsToDate + MonthOffset[tm->tm_mon - 1] + ++ tm->tm_mday; ++ ++ tm->tm_wday = day % 7; ++} ++ ++static void private_to_tm(int tim, struct local_rtc_time *tm) ++{ ++ register int i; ++ register long hms, day; ++ ++ day = tim / SECDAY; ++ hms = tim % SECDAY; ++ ++ /* Hours, minutes, seconds are easy */ ++ tm->tm_hour = hms / 3600; ++ tm->tm_min = (hms % 3600) / 60; ++ tm->tm_sec = (hms % 3600) % 60; ++ ++ /* Number of years in days */ ++ for (i = STARTOFTIME; day >= days_in_year(i); i++) ++ day -= days_in_year(i); ++ tm->tm_year = i; ++ ++ /* Number of months in days left */ ++ if (leapyear(tm->tm_year)) ++ days_in_month(FEBRUARY) = 29; ++ for (i = 1; day >= days_in_month(i); i++) ++ day -= days_in_month(i); ++ days_in_month(FEBRUARY) = 28; ++ tm->tm_mon = i; ++ ++ /* Days are what is left over (+1) from all that. */ ++ tm->tm_mday = day + 1; ++ ++ /* ++ * Determine the day of week ++ */ ++ NovfsGregorianDay(tm); ++} ++ ++char *ctime_r(time_t * clock, char *buf) ++{ ++ struct local_rtc_time tm; ++ static char *DAYOFWEEK[] = ++ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; ++ static char *MONTHOFYEAR[] = ++ { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", ++"Oct", "Nov", "Dec" }; ++ ++ private_to_tm(*clock, &tm); ++ ++ sprintf(buf, "%s %s %d %d:%02d:%02d %d", DAYOFWEEK[tm.tm_wday], ++ MONTHOFYEAR[tm.tm_mon - 1], tm.tm_mday, tm.tm_hour, tm.tm_min, ++ tm.tm_sec, tm.tm_year); ++ return (buf); ++} ++ ++static void dump(struct dentry *parent, void *pf) ++{ ++ void (*pfunc) (char *Fmt, ...) = pf; ++ struct l { ++ struct l *next; ++ struct dentry *dentry; ++ } *l, *n, *start; ++ struct list_head *p; ++ struct dentry *d; ++ char *buf, *path, *sd; ++ char inode_number[16]; ++ ++ buf = (char *)kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ ++ if (NULL == buf) { ++ return; ++ } ++ ++ if (parent) { ++ pfunc("starting 0x%p %.*s\n", parent, parent->d_name.len, ++ parent->d_name.name); ++ if (parent->d_subdirs.next == &parent->d_subdirs) { ++ pfunc("No children...\n"); ++ } else { ++ start = kmalloc(sizeof(*start), GFP_KERNEL); ++ if (start) { ++ start->next = NULL; ++ start->dentry = parent; ++ l = start; ++ while (l) { ++ p = l->dentry->d_subdirs.next; ++ while (p != &l->dentry->d_subdirs) { ++ d = list_entry(p, struct dentry, ++ d_u.d_child); ++ p = p->next; ++ ++ if (d->d_subdirs.next != ++ &d->d_subdirs) { ++ n = kmalloc(sizeof ++ (*n), ++ GFP_KERNEL); ++ if (n) { ++ n->next = ++ l->next; ++ l->next = n; ++ n->dentry = d; ++ } ++ } else { ++ path = novfs_scope_dget_path(d, buf, PATH_LENGTH_BUFFER, 1); ++ if (path) { ++ pfunc ++ ("1-0x%p %s\n" ++ " d_name: %.*s\n" ++ " d_parent: 0x%p\n" ++ " d_count: %d\n" ++ " d_flags: 0x%x\n" ++ " d_subdirs: 0x%p\n" ++ " d_inode: 0x%p\n", ++ d, path, ++ d->d_name. ++ len, ++ d->d_name. ++ name, ++ d-> ++ d_parent, ++ atomic_read ++ (&d-> ++ d_count), ++ d->d_flags, ++ d-> ++ d_subdirs. ++ next, ++ d-> ++ d_inode); ++ } ++ } ++ } ++ l = l->next; ++ } ++ l = start; ++ while (l) { ++ d = l->dentry; ++ path = ++ novfs_scope_dget_path(d, buf, ++ PATH_LENGTH_BUFFER, ++ 1); ++ if (path) { ++ sd = " (None)"; ++ if (&d->d_subdirs != ++ d->d_subdirs.next) { ++ sd = ""; ++ } ++ inode_number[0] = '\0'; ++ if (d->d_inode) { ++ sprintf(inode_number, ++ " (%lu)", ++ d->d_inode-> ++ i_ino); ++ } ++ pfunc("0x%p %s\n" ++ " d_parent: 0x%p\n" ++ " d_count: %d\n" ++ " d_flags: 0x%x\n" ++ " d_subdirs: 0x%p%s\n" ++ " d_inode: 0x%p%s\n", ++ d, path, d->d_parent, ++ atomic_read(&d->d_count), ++ d->d_flags, ++ d->d_subdirs.next, sd, ++ d->d_inode, inode_number); ++ } ++ ++ n = l; ++ l = l->next; ++ kfree(n); ++ } ++ } ++ } ++ } ++ ++ kfree(buf); ++ ++} ++ ++static ssize_t common_read(char *buf, size_t len, loff_t * off) ++{ ++ ssize_t retval = 0; ++ size_t count; ++ unsigned long offset = *off; ++ ++ if (0 != (count = DbgPrintBufferOffset - offset)) { ++ if (count > len) { ++ count = len; ++ } ++ ++ count -= copy_to_user(buf, &DbgPrintBuffer[offset], count); ++ ++ if (count == 0) { ++ retval = -EFAULT; ++ } else { ++ *off += (loff_t) count; ++ retval = count; ++ } ++ } ++ return retval; ++ ++} ++ ++static ssize_t novfs_profile_read_inode(struct file * file, char *buf, size_t len, ++ loff_t * off) ++{ ++ ssize_t retval = 0; ++ unsigned long offset = *off; ++ static char save_DbgPrintOn; ++ ++ if (offset == 0) { ++ down(&LocalPrint_lock); ++ save_DbgPrintOn = DbgPrintOn; ++ DbgPrintOn = 0; ++ ++ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0; ++ novfs_dump_inode(LocalPrint); ++ } ++ ++ ++ retval = common_read(buf, len, off); ++ ++ if (0 == retval) { ++ DbgPrintOn = save_DbgPrintOn; ++ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0; ++ ++ up(&LocalPrint_lock); ++ } ++ ++ return retval; ++ ++} ++ ++static ssize_t novfs_profile_dentry_read(struct file * file, char *buf, size_t len, ++ loff_t * off) ++{ ++ ssize_t retval = 0; ++ unsigned long offset = *off; ++ static char save_DbgPrintOn; ++ ++ if (offset == 0) { ++ down(&LocalPrint_lock); ++ save_DbgPrintOn = DbgPrintOn; ++ DbgPrintOn = 0; ++ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0; ++ dump(novfs_root, LocalPrint); ++ } ++ ++ retval = common_read(buf, len, off); ++ ++ if (0 == retval) { ++ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0; ++ DbgPrintOn = save_DbgPrintOn; ++ ++ up(&LocalPrint_lock); ++ } ++ ++ return retval; ++ ++} ++ ++uint64_t get_nanosecond_time() ++{ ++ struct timespec ts; ++ uint64_t retVal; ++ ++ ts = current_kernel_time(); ++ ++ retVal = (uint64_t) NSEC_PER_SEC; ++ retVal *= (uint64_t) ts.tv_sec; ++ retVal += (uint64_t) ts.tv_nsec; ++ ++ return (retVal); ++} ++ ++void novfs_profile_init() ++{ ++ if (novfs_procfs_dir) ++ dbg_dir = novfs_procfs_dir; ++ else ++ dbg_dir = proc_mkdir(MODULE_NAME, NULL); ++ ++ if (dbg_dir) { ++ dbg_file = create_proc_read_entry("Debug", ++ 0600, ++ dbg_dir, ++ proc_read_DbgBuffer, NULL); ++ if (dbg_file) { ++ dbg_file->size = DBGBUFFERSIZE; ++ memcpy(&Dbg_proc_file_operations, dbg_file->proc_fops, ++ sizeof(struct file_operations)); ++ Dbg_proc_file_operations.read = ++ User_proc_read_DbgBuffer; ++ Dbg_proc_file_operations.write = ++ User_proc_write_DbgBuffer; ++ dbg_file->proc_fops = &Dbg_proc_file_operations; ++ } else { ++ remove_proc_entry(MODULE_NAME, NULL); ++ vfree(DbgPrintBuffer); ++ DbgPrintBuffer = NULL; ++ } ++ } ++ ++ if (DbgPrintBuffer) { ++ if (dbg_dir) { ++ inode_file = create_proc_entry("inode", 0600, dbg_dir); ++ if (inode_file) { ++ inode_file->size = 0; ++ memcpy(&inode_proc_file_ops, ++ inode_file->proc_fops, ++ sizeof(struct file_operations)); ++ inode_proc_file_ops.owner = THIS_MODULE; ++ inode_proc_file_ops.read = ++ novfs_profile_read_inode; ++ inode_file->proc_fops = &inode_proc_file_ops; ++ } ++ ++ dentry_file = create_proc_entry("dentry", ++ 0600, dbg_dir); ++ if (dentry_file) { ++ dentry_file->size = 0; ++ memcpy(&dentry_proc_file_ops, ++ dentry_file->proc_fops, ++ sizeof(struct file_operations)); ++ dentry_proc_file_ops.owner = THIS_MODULE; ++ dentry_proc_file_ops.read = novfs_profile_dentry_read; ++ dentry_file->proc_fops = &dentry_proc_file_ops; ++ } ++ ++ } else { ++ vfree(DbgPrintBuffer); ++ DbgPrintBuffer = NULL; ++ } ++ } ++} ++ ++void novfs_profile_exit(void) ++{ ++ if (dbg_file) ++ DbgPrint("Calling remove_proc_entry(Debug, NULL)\n"), ++ remove_proc_entry("Debug", dbg_dir); ++ if (inode_file) ++ DbgPrint("Calling remove_proc_entry(inode, NULL)\n"), ++ remove_proc_entry("inode", dbg_dir); ++ if (dentry_file) ++ DbgPrint("Calling remove_proc_entry(dentry, NULL)\n"), ++ remove_proc_entry("dentry", dbg_dir); ++ ++ if (dbg_dir && (dbg_dir != novfs_procfs_dir)) { ++ DbgPrint("Calling remove_proc_entry(%s, NULL)\n", MODULE_NAME); ++ remove_proc_entry(MODULE_NAME, NULL); ++ } ++} ++ ++ +--- /dev/null ++++ b/fs/novfs/scope.c +@@ -0,0 +1,659 @@ ++/* ++ * Novell NCP Redirector for Linux ++ * Author: James Turner ++ * ++ * This file contains functions used to scope users. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vfs.h" ++ ++#define SHUTDOWN_INTERVAL 5 ++#define CLEANUP_INTERVAL 10 ++#define MAX_USERNAME_LENGTH 32 ++ ++ ++static struct list_head Scope_List; ++static struct semaphore Scope_Lock; ++static struct semaphore Scope_Thread_Delay; ++static int Scope_Thread_Terminate = 0; ++static struct timer_list Scope_Timer; ++static unsigned int Scope_Hash_Val = 1; ++ ++static struct novfs_scope_list *Scope_Search4Scope(struct novfs_schandle Id, ++ int Session, int Locked) ++{ ++ struct novfs_scope_list *scope, *rscope = NULL; ++ struct novfs_schandle cur_scope; ++ struct list_head *sl; ++ int offset; ++ ++ DbgPrint("Scope_Search4Scope: 0x%p:%p 0x%x 0x%x\n", Id.hTypeId, Id.hId, ++ Session, Locked); ++ ++ if (Session) ++ offset = offsetof(struct novfs_scope_list, SessionId); ++ else ++ offset = offsetof(struct novfs_scope_list, ScopeId); ++ ++ if (!Locked) { ++ down(&Scope_Lock); ++ } ++ ++ sl = Scope_List.next; ++ DbgPrint("Scope_Search4Scope: 0x%p\n", sl); ++ while (sl != &Scope_List) { ++ scope = list_entry(sl, struct novfs_scope_list, ScopeList); ++ ++ cur_scope = *(struct novfs_schandle *) ((char *)scope + offset); ++ if (SC_EQUAL(Id, cur_scope)) { ++ rscope = scope; ++ break; ++ } ++ ++ sl = sl->next; ++ } ++ ++ if (!Locked) { ++ up(&Scope_Lock); ++ } ++ ++ DbgPrint("Scope_Search4Scope: return 0x%p\n", rscope); ++ return (rscope); ++} ++ ++static struct novfs_scope_list *Scope_Find_Scope(int Create) ++{ ++ struct novfs_scope_list *scope = NULL, *pscope = NULL; ++ struct task_struct *task; ++ struct novfs_schandle scopeId; ++ int addscope = 0; ++ ++ task = current; ++ ++ DbgPrint("Scope_Find_Scope: %d %d %d %d\n", current_uid(), ++ current_euid(), current_suid(), current_fsuid()); ++ ++ //scopeId = task->euid; ++ UID_TO_SCHANDLE(scopeId, current_euid()); ++ ++ scope = Scope_Search4Scope(scopeId, 0, 0); ++ ++ if (!scope && Create) { ++ scope = kmalloc(sizeof(*pscope), GFP_KERNEL); ++ if (scope) { ++ scope->ScopeId = scopeId; ++ SC_INITIALIZE(scope->SessionId); ++ scope->ScopePid = task->pid; ++ scope->ScopeTask = task; ++ scope->ScopeHash = 0; ++ scope->ScopeUid = current_euid(); ++ scope->ScopeUserName[0] = '\0'; ++ ++ if (!novfs_daemon_create_sessionId(&scope->SessionId)) { ++ DbgPrint("Scope_Find_Scope2: %d %d %d %d\n", ++ current_uid(), current_euid(), ++ current_suid(), current_fsuid()); ++ memset(scope->ScopeUserName, 0, ++ sizeof(scope->ScopeUserName)); ++ scope->ScopeUserNameLength = 0; ++ novfs_daemon_getpwuid(current_euid(), ++ sizeof(scope->ScopeUserName), ++ scope->ScopeUserName); ++ scope->ScopeUserNameLength = ++ strlen(scope->ScopeUserName); ++ addscope = 1; ++ } ++ ++ scope->ScopeHash = Scope_Hash_Val++; ++ DbgPrint("Scope_Find_Scope: Adding 0x%p\n" ++ " ScopeId: 0x%p:%p\n" ++ " SessionId: 0x%p:%p\n" ++ " ScopePid: %d\n" ++ " ScopeTask: 0x%p\n" ++ " ScopeHash: %u\n" ++ " ScopeUid: %u\n" ++ " ScopeUserNameLength: %u\n" ++ " ScopeUserName: %s\n", ++ scope, ++ scope->ScopeId.hTypeId, scope->ScopeId.hId, ++ scope->SessionId.hTypeId, scope->SessionId.hId, ++ scope->ScopePid, ++ scope->ScopeTask, ++ scope->ScopeHash, ++ scope->ScopeUid, ++ scope->ScopeUserNameLength, ++ scope->ScopeUserName); ++ ++ if (SC_PRESENT(scope->SessionId)) { ++ down(&Scope_Lock); ++ pscope = ++ Scope_Search4Scope(scopeId, 0, 1); ++ ++ if (!pscope) { ++ list_add(&scope->ScopeList, ++ &Scope_List); ++ } ++ up(&Scope_Lock); ++ ++ if (pscope) { ++ printk ++ ("<6>Scope_Find_Scope scope not added because it was already there...\n"); ++ novfs_daemon_destroy_sessionId(scope-> ++ SessionId); ++ kfree(scope); ++ scope = pscope; ++ addscope = 0; ++ } ++ } else { ++ kfree(scope); ++ scope = NULL; ++ } ++ } ++ ++ if (addscope) { ++ novfs_add_to_root(scope->ScopeUserName); ++ } ++ } ++ ++ return (scope); ++} ++ ++static int Scope_Validate_Scope(struct novfs_scope_list *Scope) ++{ ++ struct novfs_scope_list *s; ++ struct list_head *sl; ++ int retVal = 0; ++ ++ DbgPrint("Scope_Validate_Scope: 0x%p\n", Scope); ++ ++ down(&Scope_Lock); ++ ++ sl = Scope_List.next; ++ while (sl != &Scope_List) { ++ s = list_entry(sl, struct novfs_scope_list, ScopeList); ++ ++ if (s == Scope) { ++ retVal = 1; ++ break; ++ } ++ ++ sl = sl->next; ++ } ++ ++ up(&Scope_Lock); ++ ++ return (retVal); ++} ++ ++uid_t novfs_scope_get_uid(struct novfs_scope_list *scope) ++{ ++ uid_t uid = 0; ++ if (!scope) ++ scope = Scope_Find_Scope(1); ++ ++ if (scope && Scope_Validate_Scope(scope)) ++ uid = scope->ScopeUid; ++ return uid; ++} ++ ++char *novfs_scope_get_username(void) ++{ ++ char *name = NULL; ++ struct novfs_scope_list *Scope; ++ ++ Scope = Scope_Find_Scope(1); ++ ++ if (Scope && Scope_Validate_Scope(Scope)) ++ name = Scope->ScopeUserName; ++ ++ return name; ++} ++ ++struct novfs_schandle novfs_scope_get_sessionId(struct novfs_scope_list ++ *Scope) ++{ ++ struct novfs_schandle sessionId; ++ DbgPrint("Scope_Get_SessionId: 0x%p\n", Scope); ++ SC_INITIALIZE(sessionId); ++ if (!Scope) ++ Scope = Scope_Find_Scope(1); ++ ++ if (Scope && Scope_Validate_Scope(Scope)) ++ sessionId = Scope->SessionId; ++ DbgPrint("Scope_Get_SessionId: return 0x%p:%p\n", sessionId.hTypeId, ++ sessionId.hId); ++ return (sessionId); ++} ++ ++struct novfs_scope_list *novfs_get_scope_from_name(struct qstr * Name) ++{ ++ struct novfs_scope_list *scope, *rscope = NULL; ++ struct list_head *sl; ++ ++ DbgPrint("Scope_Get_ScopefromName: %.*s\n", Name->len, Name->name); ++ ++ down(&Scope_Lock); ++ ++ sl = Scope_List.next; ++ while (sl != &Scope_List) { ++ scope = list_entry(sl, struct novfs_scope_list, ScopeList); ++ ++ if ((Name->len == scope->ScopeUserNameLength) && ++ (0 == strncmp(scope->ScopeUserName, Name->name, Name->len))) ++ { ++ rscope = scope; ++ break; ++ } ++ ++ sl = sl->next; ++ } ++ ++ up(&Scope_Lock); ++ ++ return (rscope); ++} ++ ++int novfs_scope_set_userspace(uint64_t * TotalSize, uint64_t * Free, ++ uint64_t * TotalEnties, uint64_t * FreeEnties) ++{ ++ struct novfs_scope_list *scope; ++ int retVal = 0; ++ ++ scope = Scope_Find_Scope(1); ++ ++ if (scope) { ++ if (TotalSize) ++ scope->ScopeUSize = *TotalSize; ++ if (Free) ++ scope->ScopeUFree = *Free; ++ if (TotalEnties) ++ scope->ScopeUTEnties = *TotalEnties; ++ if (FreeEnties) ++ scope->ScopeUAEnties = *FreeEnties; ++ } ++ ++ return (retVal); ++} ++ ++int novfs_scope_get_userspace(uint64_t * TotalSize, uint64_t * Free, ++ uint64_t * TotalEnties, uint64_t * FreeEnties) ++{ ++ struct novfs_scope_list *scope; ++ int retVal = 0; ++ ++ uint64_t td, fd, te, fe; ++ ++ scope = Scope_Find_Scope(1); ++ ++ td = fd = te = fe = 0; ++ if (scope) { ++ ++ retVal = ++ novfs_daemon_get_userspace(scope->SessionId, &td, &fd, &te, &fe); ++ ++ scope->ScopeUSize = td; ++ scope->ScopeUFree = fd; ++ scope->ScopeUTEnties = te; ++ scope->ScopeUAEnties = fe; ++ } ++ ++ if (TotalSize) ++ *TotalSize = td; ++ if (Free) ++ *Free = fd; ++ if (TotalEnties) ++ *TotalEnties = te; ++ if (FreeEnties) ++ *FreeEnties = fe; ++ ++ return (retVal); ++} ++ ++struct novfs_scope_list *novfs_get_scope(struct dentry * Dentry) ++{ ++ struct novfs_scope_list *scope = NULL; ++ char *buf, *path, *cp; ++ struct qstr name; ++ ++ buf = (char *)kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL); ++ if (buf) { ++ path = novfs_scope_dget_path(Dentry, buf, PATH_LENGTH_BUFFER, 0); ++ if (path) { ++ DbgPrint("Scope_Get_ScopefromPath: %s\n", path); ++ ++ if (*path == '/') ++ path++; ++ ++ cp = path; ++ if (*cp) { ++ while (*cp && (*cp != '/')) ++ cp++; ++ ++ *cp = '\0'; ++ name.hash = 0; ++ name.len = (int)(cp - path); ++ name.name = path; ++ scope = novfs_get_scope_from_name(&name); ++ } ++ } ++ kfree(buf); ++ } ++ ++ return (scope); ++} ++ ++static char *add_to_list(char *Name, char *List, char *EndOfList) ++{ ++ while (*Name && (List < EndOfList)) { ++ *List++ = *Name++; ++ } ++ ++ if (List < EndOfList) { ++ *List++ = '\0'; ++ } ++ return (List); ++} ++ ++char *novfs_get_scopeusers(void) ++{ ++ struct novfs_scope_list *scope; ++ struct list_head *sl; ++ int asize = 8 * MAX_USERNAME_LENGTH; ++ char *list, *cp, *ep; ++ ++ DbgPrint("Scope_Get_ScopeUsers\n"); ++ ++ do { /* Copy list until done or out of memory */ ++ list = kmalloc(asize, GFP_KERNEL); ++ ++ DbgPrint("Scope_Get_ScopeUsers list=0x%p\n", list); ++ if (list) { ++ cp = list; ++ ep = cp + asize; ++ ++ /* ++ * Add the tree and server entries ++ */ ++ cp = add_to_list(TREE_DIRECTORY_NAME, cp, ep); ++ cp = add_to_list(SERVER_DIRECTORY_NAME, cp, ep); ++ ++ down(&Scope_Lock); ++ ++ sl = Scope_List.next; ++ while ((sl != &Scope_List) && (cp < ep)) { ++ scope = list_entry(sl, struct novfs_scope_list, ScopeList); ++ ++ DbgPrint("Scope_Get_ScopeUsers found 0x%p %s\n", ++ scope, scope->ScopeUserName); ++ ++ cp = add_to_list(scope->ScopeUserName, cp, ep); ++ ++ sl = sl->next; ++ } ++ ++ up(&Scope_Lock); ++ ++ if (cp < ep) { ++ *cp++ = '\0'; ++ asize = 0; ++ } else { /* Allocation was to small, up size */ ++ ++ asize *= 4; ++ kfree(list); ++ list = NULL; ++ } ++ } else { /* if allocation fails return an empty list */ ++ ++ break; ++ } ++ } while (!list); /* List was to small try again */ ++ ++ return (list); ++} ++ ++void *novfs_scope_lookup(void) ++{ ++ return Scope_Find_Scope(1); ++} ++ ++static void Scope_Timer_Function(unsigned long context) ++{ ++ up(&Scope_Thread_Delay); ++} ++ ++static int Scope_Cleanup_Thread(void *Args) ++{ ++ struct novfs_scope_list *scope, *rscope; ++ struct list_head *sl, cleanup; ++ struct task_struct *task; ++ ++ DbgPrint("Scope_Cleanup_Thread: %d\n", current->pid); ++ ++ /* ++ * Setup and start que timer ++ */ ++ init_timer(&Scope_Timer); ++ ++ while (0 == Scope_Thread_Terminate) { ++ DbgPrint("Scope_Cleanup_Thread: looping\n"); ++ if (Scope_Thread_Terminate) { ++ break; ++ } ++ ++ /* ++ * Check scope list for any terminated processes ++ */ ++ down(&Scope_Lock); ++ ++ sl = Scope_List.next; ++ INIT_LIST_HEAD(&cleanup); ++ ++ while (sl != &Scope_List) { ++ scope = list_entry(sl, struct novfs_scope_list, ScopeList); ++ sl = sl->next; ++ ++ rscope = NULL; ++ rcu_read_lock(); ++ for_each_process(task) { ++ if ((task->cred->uid == scope->ScopeUid) ++ || (task->cred->euid == scope->ScopeUid)) { ++ rscope = scope; ++ break; ++ } ++ } ++ rcu_read_unlock(); ++ ++ if (!rscope) { ++ list_move(&scope->ScopeList, &cleanup); ++ DbgPrint("Scope_Cleanup_Thread: Scope=0x%p\n", ++ rscope); ++ } ++ } ++ ++ up(&Scope_Lock); ++ ++ sl = cleanup.next; ++ while (sl != &cleanup) { ++ scope = list_entry(sl, struct novfs_scope_list, ScopeList); ++ sl = sl->next; ++ ++ DbgPrint("Scope_Cleanup_Thread: Removing 0x%p\n" ++ " ScopeId: 0x%p:%p\n" ++ " SessionId: 0x%p:%p\n" ++ " ScopePid: %d\n" ++ " ScopeTask: 0x%p\n" ++ " ScopeHash: %u\n" ++ " ScopeUid: %u\n" ++ " ScopeUserName: %s\n", ++ scope, ++ scope->ScopeId, ++ scope->SessionId, ++ scope->ScopePid, ++ scope->ScopeTask, ++ scope->ScopeHash, ++ scope->ScopeUid, scope->ScopeUserName); ++ if (!Scope_Search4Scope(scope->SessionId, 1, 0)) { ++ novfs_remove_from_root(scope->ScopeUserName); ++ novfs_daemon_destroy_sessionId(scope->SessionId); ++ } ++ kfree(scope); ++ } ++ ++ Scope_Timer.expires = jiffies + HZ * CLEANUP_INTERVAL; ++ Scope_Timer.data = (unsigned long)0; ++ Scope_Timer.function = Scope_Timer_Function; ++ add_timer(&Scope_Timer); ++ DbgPrint("Scope_Cleanup_Thread: sleeping\n"); ++ ++ if (down_interruptible(&Scope_Thread_Delay)) { ++ break; ++ } ++ del_timer(&Scope_Timer); ++ } ++ Scope_Thread_Terminate = 0; ++ ++ printk(KERN_INFO "Scope_Cleanup_Thread: Exit\n"); ++ DbgPrint("Scope_Cleanup_Thread: Exit\n"); ++ return (0); ++} ++ ++void novfs_scope_cleanup(void) ++{ ++ struct novfs_scope_list *scope; ++ struct list_head *sl; ++ ++ DbgPrint("Scope_Cleanup:\n"); ++ ++ /* ++ * Check scope list for any terminated processes ++ */ ++ down(&Scope_Lock); ++ ++ sl = Scope_List.next; ++ ++ while (sl != &Scope_List) { ++ scope = list_entry(sl, struct novfs_scope_list, ScopeList); ++ sl = sl->next; ++ ++ list_del(&scope->ScopeList); ++ ++ DbgPrint("Scope_Cleanup: Removing 0x%p\n" ++ " ScopeId: 0x%p:%p\n" ++ " SessionId: 0x%p:%p\n" ++ " ScopePid: %d\n" ++ " ScopeTask: 0x%p\n" ++ " ScopeHash: %u\n" ++ " ScopeUid: %u\n" ++ " ScopeUserName: %s\n", ++ scope, ++ scope->ScopeId, ++ scope->SessionId, ++ scope->ScopePid, ++ scope->ScopeTask, ++ scope->ScopeHash, ++ scope->ScopeUid, scope->ScopeUserName); ++ if (!Scope_Search4Scope(scope->SessionId, 1, 1)) { ++ novfs_remove_from_root(scope->ScopeUserName); ++ novfs_daemon_destroy_sessionId(scope->SessionId); ++ } ++ kfree(scope); ++ } ++ ++ up(&Scope_Lock); ++ ++} ++ ++/* ++ * Walks the dentry chain building a path. ++ */ ++char *novfs_scope_dget_path(struct dentry *Dentry, char *Buf, unsigned int Buflen, ++ int Flags) ++{ ++ char *retval = &Buf[Buflen]; ++ struct dentry *p = Dentry; ++ int len; ++ ++ *(--retval) = '\0'; ++ Buflen--; ++ ++ do { ++ if (Buflen > p->d_name.len) { ++ retval -= p->d_name.len; ++ Buflen -= p->d_name.len; ++ memcpy(retval, p->d_name.name, p->d_name.len); ++ *(--retval) = '/'; ++ Buflen--; ++ p = p->d_parent; ++ } else { ++ retval = NULL; ++ break; ++ } ++ } while (!IS_ROOT(p)); ++ ++ if (IS_ROOT(Dentry)) { ++ retval++; ++ } ++ ++ if (Flags) { ++ len = strlen(p->d_sb->s_type->name); ++ if (Buflen - len > 0) { ++ retval -= len; ++ Buflen -= len; ++ memcpy(retval, p->d_sb->s_type->name, len); ++ *(--retval) = '/'; ++ Buflen--; ++ } ++ } ++ ++ return (retval); ++} ++ ++void novfs_scope_init(void) ++{ ++ INIT_LIST_HEAD(&Scope_List); ++ init_MUTEX(&Scope_Lock); ++ init_MUTEX_LOCKED(&Scope_Thread_Delay); ++ kthread_run(Scope_Cleanup_Thread, NULL, "novfs_ST"); ++} ++ ++void novfs_scope_exit(void) ++{ ++ unsigned long expires = jiffies + HZ * SHUTDOWN_INTERVAL; ++ ++ printk(KERN_INFO "Scope_Uninit: Start\n"); ++ ++ Scope_Thread_Terminate = 1; ++ ++ up(&Scope_Thread_Delay); ++ ++ mb(); ++ while (Scope_Thread_Terminate && (jiffies < expires)) ++ yield(); ++ /* down(&Scope_Thread_Delay); */ ++ printk(KERN_INFO "Scope_Uninit: Exit\n"); ++ ++} ++ ++ +--- /dev/null ++++ b/fs/novfs/vfs.h +@@ -0,0 +1,454 @@ ++/* ++ * Novell NCP Redirector for Linux ++ * Author: James Turner ++ * ++ * Include file for novfs. ++ * ++ * Copyright (C) 2005 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ */ ++#ifndef __NOVFS_H ++#define __NOVFS_H ++ ++#ifndef __STDC_VERSION__ ++#define __STDC_VERSION__ 0L ++#endif ++ ++#include ++#include ++ ++#include "nwcapi.h" ++ ++ ++#ifndef XTIER_SCHANDLE ++struct novfs_schandle { ++ void * hTypeId; ++ void * hId; ++ ++}; ++ ++#include "commands.h" ++ ++#define SC_PRESENT(X) ((X.hTypeId != NULL) || (X.hId != NULL)) ? 1 : 0 ++#define SC_EQUAL(X, Y) ((X.hTypeId == Y.hTypeId) && (X.hId == Y.hId)) ? 1 : 0 ++#define SC_INITIALIZE(X) {X.hTypeId = X.hId = NULL;} ++ ++#define UID_TO_SCHANDLE(hSC, uid) \ ++ { \ ++ hSC.hTypeId = NULL; \ ++ hSC.hId = (void *)(unsigned long)(uid); \ ++ } ++ ++#define XTIER_SCHANDLE ++#endif ++ ++ ++/*===[ Manifest constants ]===============================================*/ ++#define NOVFS_MAGIC 0x4e574653 ++#define MODULE_NAME "novfs" ++ ++#define TREE_DIRECTORY_NAME ".Trees" ++#define SERVER_DIRECTORY_NAME ".Servers" ++ ++#define PATH_LENGTH_BUFFER PATH_MAX ++#define NW_MAX_PATH_LENGTH 255 ++ ++#define XA_BUFFER (8 * 1024) ++ ++#define IOC_LOGIN 0x4a540000 ++#define IOC_LOGOUT 0x4a540001 ++#define IOC_XPLAT 0x4a540002 ++#define IOC_SESSION 0x4a540003 ++#define IOC_DEBUGPRINT 0x4a540004 ++ ++/* ++ * NetWare file attributes ++ */ ++ ++#define NW_ATTRIBUTE_NORMAL 0x00 ++#define NW_ATTRIBUTE_READ_ONLY 0x01 ++#define NW_ATTRIBUTE_HIDDEN 0x02 ++#define NW_ATTRIBUTE_SYSTEM 0x04 ++#define NW_ATTRIBUTE_EXECUTE_ONLY 0x08 ++#define NW_ATTRIBUTE_DIRECTORY 0x10 ++#define NW_ATTRIBUTE_ARCHIVE 0x20 ++#define NW_ATTRIBUTE_EXECUTE 0x40 ++#define NW_ATTRIBUTE_SHAREABLE 0x80 ++ ++/* ++ * Define READ/WRITE flag for DATA_LIST ++ */ ++#define DLREAD 0 ++#define DLWRITE 1 ++ ++/* ++ * Define list type ++ */ ++#define USER_LIST 1 ++#define SERVER_LIST 2 ++#define VOLUME_LIST 3 ++ ++/* ++ * Define flags used in for inodes ++ */ ++#define USER_INODE 1 ++#define UPDATE_INODE 2 ++ ++/* ++ * Define flags for directory cache flags ++ */ ++#define ENTRY_VALID 0x00000001 ++ ++#ifdef INTENT_MAGIC ++#define NDOPENFLAGS intent.it_flags ++#else ++#define NDOPENFLAGS intent.open.flags ++#endif ++ ++/* ++ * daemon_command_t flags values ++ */ ++#define INTERRUPTIBLE 1 ++ ++#ifndef NOVFS_VFS_MAJOR ++#define NOVFS_VFS_MAJOR 0 ++#endif ++ ++#ifndef NOVFS_VFS_MINOR ++#define NOVFS_VFS_MINOR 0 ++#endif ++ ++#ifndef NOVFS_VFS_SUB ++#define NOVFS_VFS_SUB 0 ++#endif ++ ++#ifndef NOVFS_VFS_RELEASE ++#define NOVFS_VFS_RELEASE 0 ++#endif ++ ++#define VALUE_TO_STR( value ) #value ++#define DEFINE_TO_STR(value) VALUE_TO_STR(value) ++ ++#define NOVFS_VERSION_STRING \ ++ DEFINE_TO_STR(NOVFS_VFS_MAJOR)"." \ ++ DEFINE_TO_STR(NOVFS_VFS_MINOR)"." \ ++ DEFINE_TO_STR(NOVFS_VFS_SUB)"-" \ ++ DEFINE_TO_STR(NOVFS_VFS_RELEASE) \ ++ "\0" ++ ++/*===[ Type definitions ]=================================================*/ ++struct novfs_entry_info { ++ int type; ++ umode_t mode; ++ uid_t uid; ++ gid_t gid; ++ loff_t size; ++ struct timespec atime; ++ struct timespec mtime; ++ struct timespec ctime; ++ int namelength; ++ unsigned char name[1]; ++}; ++ ++struct novfs_string { ++ int length; ++ unsigned char *data; ++}; ++ ++struct novfs_login { ++ struct novfs_string Server; ++ struct novfs_string UserName; ++ struct novfs_string Password; ++}; ++ ++struct novfs_logout { ++ struct novfs_string Server; ++}; ++ ++struct novfs_dir_cache { ++ struct list_head list; ++ int flags; ++ u64 jiffies; ++ ino_t ino; ++ loff_t size; ++ umode_t mode; ++ struct timespec atime; ++ struct timespec mtime; ++ struct timespec ctime; ++ unsigned long hash; ++ int nameLen; ++ char name[1]; ++}; ++ ++struct novfs_data_list { ++ void *page; ++ void *offset; ++ int len; ++ int rwflag; ++}; ++ ++ ++extern char *ctime_r(time_t * clock, char *buf); ++ ++/* ++ * Converts a HANDLE to a u32 type. ++ */ ++static inline u32 HandletoUint32(void * h) ++{ ++ return (u32) ((unsigned long) h); ++} ++ ++/* ++ * Converts a u32 to a HANDLE type. ++ */ ++static inline void *Uint32toHandle(u32 ui32) ++{ ++ return ((void *) (unsigned long) ui32); ++} ++ ++/* Global variables */ ++ ++extern struct dentry *novfs_root; ++extern struct proc_dir_entry *novfs_procfs_dir; ++extern unsigned long novfs_update_timeout; ++extern int novfs_page_cache; ++extern char *novfs_current_mnt; ++extern int novfs_max_iosize; ++ ++ ++/* Global functions */ ++extern int novfs_remove_from_root(char *); ++extern void novfs_dump_inode(void *pf); ++ ++extern void novfs_dump(int size, void *dumpptr); ++ ++extern int Queue_Daemon_Command(void *request, unsigned long reqlen, void *data, ++ int dlen, void **reply, unsigned long * replen, ++ int interruptible); ++extern int novfs_do_login(struct ncl_string * Server, struct ncl_string* Username, struct ncl_string * Password, void **lgnId, struct novfs_schandle *Session); ++ ++extern int novfs_proc_init(void); ++extern void novfs_proc_exit(void); ++ ++/* ++ * daemon.c functions ++ */ ++extern void novfs_daemon_queue_init(void); ++extern void novfs_daemon_queue_exit(void); ++extern int novfs_daemon_logout(struct qstr *Server, struct novfs_schandle *Session); ++extern int novfs_daemon_set_mnt_point(char *Path); ++extern int novfs_daemon_create_sessionId(struct novfs_schandle * SessionId); ++extern int novfs_daemon_destroy_sessionId(struct novfs_schandle SessionId); ++extern int novfs_daemon_getpwuid(uid_t uid, int unamelen, char *uname); ++extern int novfs_daemon_get_userspace(struct novfs_schandle SessionId, ++ uint64_t * TotalSize, uint64_t * TotalFree, ++ uint64_t * TotalDirectoryEnties, ++ uint64_t * FreeDirectoryEnties); ++extern int novfs_daemon_debug_cmd_send(char *Command); ++extern ssize_t novfs_daemon_recv_reply(struct file *file, ++ const char *buf, size_t nbytes, loff_t * ppos); ++extern ssize_t novfs_daemon_cmd_send(struct file *file, char *buf, ++ size_t len, loff_t * off); ++extern int novfs_daemon_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg); ++extern int novfs_daemon_lib_close(struct inode *inode, struct file *file); ++extern int novfs_daemon_lib_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg); ++extern int novfs_daemon_lib_open(struct inode *inode, struct file *file); ++extern ssize_t novfs_daemon_lib_read(struct file *file, char *buf, ++ size_t len, loff_t * off); ++extern ssize_t novfs_daemon_lib_write(struct file *file, const char *buf, ++ size_t len, loff_t * off); ++extern loff_t novfs_daemon_lib_llseek(struct file *file, loff_t offset, ++ int origin); ++extern int novfs_daemon_open_control(struct inode *Inode, struct file *File); ++extern int novfs_daemon_close_control(struct inode *Inode, struct file *File); ++extern int novfs_daemon_getversion(char *Buf, int Length); ++ ++ ++/* ++ * file.c functions ++ */ ++extern int novfs_verify_file(struct qstr *Path, struct novfs_schandle SessionId); ++extern int novfs_get_alltrees(struct dentry *parent); ++extern int novfs_get_servers(unsigned char **ServerList, ++ struct novfs_schandle SessionId); ++extern int novfs_get_vols(struct qstr *Server, ++ unsigned char **VolumeList, struct novfs_schandle SessionId); ++extern int novfs_get_file_info(unsigned char *Path, ++ struct novfs_entry_info *Info, struct novfs_schandle SessionId); ++extern int novfs_getx_file_info(char *Path, const char *Name, ++ char *buffer, ssize_t buffer_size, ssize_t *dataLen, ++ struct novfs_schandle SessionId); ++extern int novfs_listx_file_info(char *Path, char *buffer, ++ ssize_t buffer_size, ssize_t *dataLen, ++ struct novfs_schandle SessionId); ++extern int novfs_setx_file_info(char *Path, const char *Name, const void *Value, ++ unsigned long valueLen, ++ unsigned long *bytesWritten, int flags, ++ struct novfs_schandle SessionId); ++ ++extern int novfs_get_dir_listex(unsigned char *Path, void **EnumHandle, ++ int *Count, struct novfs_entry_info **Info, ++ struct novfs_schandle SessionId); ++extern int novfs_open_file(unsigned char *Path, int Flags, ++ struct novfs_entry_info * Info, void **Handle, ++ struct novfs_schandle SessionId); ++extern int novfs_create(unsigned char *Path, int DirectoryFlag, ++ struct novfs_schandle SessionId); ++extern int novfs_close_file(void * Handle, struct novfs_schandle SessionId); ++extern int novfs_read_file(void * Handle, unsigned char *Buffer, ++ size_t * Bytes, loff_t * Offset, ++ struct novfs_schandle SessionId); ++extern int novfs_read_pages(void * Handle, struct novfs_data_list *DList, ++ int DList_Cnt, size_t * Bytes, loff_t * Offset, ++ struct novfs_schandle SessionId); ++extern int novfs_write_file(void * Handle, unsigned char *Buffer, ++ size_t * Bytes, loff_t * Offset, ++ struct novfs_schandle SessionId); ++extern int novfs_write_page(void * Handle, struct page *Page, ++ struct novfs_schandle SessionId); ++extern int novfs_write_pages(void * Handle, struct novfs_data_list *DList, ++ int DList_Cnt, size_t Bytes, loff_t Offset, ++ struct novfs_schandle SessionId); ++extern int novfs_delete(unsigned char *Path, int DirectoryFlag, ++ struct novfs_schandle SessionId); ++extern int novfs_trunc(unsigned char *Path, int PathLen, ++ struct novfs_schandle SessionId); ++extern int novfs_trunc_ex(void * Handle, loff_t Offset, ++ struct novfs_schandle SessionId); ++extern int novfs_rename_file(int DirectoryFlag, unsigned char *OldName, ++ int OldLen, unsigned char *NewName, int NewLen, ++ struct novfs_schandle SessionId); ++extern int novfs_set_attr(unsigned char *Path, struct iattr *Attr, ++ struct novfs_schandle SessionId); ++extern int novfs_get_file_cache_flag(unsigned char * Path, ++ struct novfs_schandle SessionId); ++extern int novfs_set_file_lock(struct novfs_schandle SessionId, void * fhandle, ++ unsigned char fl_type, loff_t fl_start, ++ loff_t len); ++ ++extern struct inode *novfs_get_inode(struct super_block *sb, int mode, ++ int dev, uid_t uid, ino_t ino, struct qstr *name); ++extern int novfs_read_stream(void * ConnHandle, unsigned char * Handle, ++ unsigned char * Buffer, size_t * Bytes, loff_t * Offset, ++ int User, struct novfs_schandle SessionId); ++extern int novfs_write_stream(void * ConnHandle, unsigned char * Handle, ++ unsigned char * Buffer, size_t * Bytes, loff_t * Offset, ++ struct novfs_schandle SessionId); ++extern int novfs_close_stream(void * ConnHandle, unsigned char * Handle, ++ struct novfs_schandle SessionId); ++ ++extern int novfs_add_to_root(char *); ++extern int novfs_end_directory_enumerate(void *EnumHandle, ++ struct novfs_schandle SessionId); ++ ++/* ++ * scope.c functions ++ */ ++extern void novfs_scope_init(void); ++extern void novfs_scope_exit(void); ++extern void *novfs_scope_lookup(void); ++extern uid_t novfs_scope_get_uid(struct novfs_scope_list *); ++extern struct novfs_schandle novfs_scope_get_sessionId(struct ++ novfs_scope_list *); ++extern char *novfs_get_scopeusers(void); ++extern int novfs_scope_set_userspace(uint64_t * TotalSize, uint64_t * Free, ++ uint64_t * TotalEnties, uint64_t * FreeEnties); ++extern int novfs_scope_get_userspace(uint64_t * TotalSize, uint64_t * Free, ++ uint64_t * TotalEnties, uint64_t * FreeEnties); ++extern char *novfs_scope_dget_path(struct dentry *Dentry, char *Buf, ++ unsigned int Buflen, int Flags); ++extern void novfs_scope_cleanup(void); ++extern struct novfs_scope_list *novfs_get_scope_from_name(struct qstr *); ++extern struct novfs_scope_list *novfs_get_scope(struct dentry *); ++extern char *novfs_scope_get_username(void); ++ ++/* ++ * profile.c functions ++ */ ++extern u64 get_nanosecond_time(void); ++extern int ___DbgPrint(const char *site, const char *Fmt, ...); ++#define DbgPrint(fmt, args...) ___DbgPrint(__func__, ": " fmt "\n", ##args) ++#define __DbgPrint(fmt, args...) ___DbgPrint("", fmt, ##args) ++ ++extern void novfs_profile_init(void); ++extern void novfs_profile_exit(void); ++ ++/* ++ * nwcapi.c functions ++ */ ++extern int novfs_auth_conn(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_conn_close(struct novfs_xplat *pdata, ++ void **Handle, struct novfs_schandle Session); ++extern int novfs_get_conn_info(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_set_conn_info(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_get_daemon_ver(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_get_id_info(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_license_conn(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_login_id(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_logout_id(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_open_conn_by_addr(struct novfs_xplat *pdata, ++ void **Handle, struct novfs_schandle Session); ++extern int novfs_open_conn_by_name(struct novfs_xplat *pdata, ++ void **Handle, struct novfs_schandle Session); ++extern int novfs_open_conn_by_ref(struct novfs_xplat *pdata, ++ void **Handle, struct novfs_schandle Session); ++extern int novfs_query_feature(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_raw_send(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_scan_conn_info(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_sys_conn_close(struct novfs_xplat *pdata, ++ unsigned long *Handle, struct novfs_schandle Session); ++extern int novfs_unauthenticate(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_unlicense_conn(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_change_auth_key(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_enum_ids(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_get_default_ctx(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_get_preferred_DS_tree(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_get_tree_monitored_conn(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_set_default_ctx(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_set_preferred_DS_tree(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_set_pri_conn(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_get_pri_conn(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_set_map_drive(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_unmap_drive(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_enum_drives(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_get_bcast_msg(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_set_key_value(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++extern int novfs_verify_key_value(struct novfs_xplat *pdata, ++ struct novfs_schandle Session); ++ ++ ++#endif /* __NOVFS_H */ ++ diff --git a/patches.suse/novfs-fix-debug-message.patch b/patches.suse/novfs-fix-debug-message.patch new file mode 100644 index 0000000..33f46cf --- /dev/null +++ b/patches.suse/novfs-fix-debug-message.patch @@ -0,0 +1,22 @@ +From: Sankar P +Subject: novfs: fix debug message +Patch-mainline: no + +Signed-off-by: Sankar P +Acked-by: Jiri Benc + +--- + fs/novfs/daemon.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/novfs/daemon.c ++++ b/fs/novfs/daemon.c +@@ -1590,7 +1590,7 @@ int novfs_daemon_lib_ioctl(struct inode + break; + + case NWC_SET_CONN_INFO: +- DbgIocCall("NwGetConnInfo"); ++ DbgIocCall("NwSetConnInfo"); + retCode = + novfs_set_conn_info(&data, dh->session); + break; diff --git a/patches.suse/ocfs2-allocation-resrvations.patch b/patches.suse/ocfs2-allocation-resrvations.patch new file mode 100644 index 0000000..4d26ee8 --- /dev/null +++ b/patches.suse/ocfs2-allocation-resrvations.patch @@ -0,0 +1,1243 @@ +From: Mark Fasheh +Date: Thu, 19 Nov 2009 15:15:38 -0800 +Subject: ocfs2: allocation reservations +Patch-mainline: 2.6.33? +References: bnc#501563 FATE#307247 + +ocfs2: allocation reservations + +This patch improves Ocfs2 allocation policy by allowing an inode to +reserve a portion of the local alloc bitmap for itself. Allocation windows +are advisory in that they won't block use of that portion of the bitmap. +This makes dealing with corner cases much easier - we can always fall back +to previous policy. + +Reservation windows are represented internally by a red-black tree. Within +that tree, each node represents the reservation window of one inode. When +new data is written, we try to allocate from the window first. If that +allocation fails, we fall back to our old heuristics and a new window is +computed from the results. Allocation windows will also be extended if +allocation from them succeeds. + +Signed-off-by: Mark Fasheh + +--- + Documentation/filesystems/ocfs2.txt | 3 + fs/ocfs2/Makefile | 1 + fs/ocfs2/aops.c | 2 + fs/ocfs2/cluster/masklog.c | 1 + fs/ocfs2/cluster/masklog.h | 1 + fs/ocfs2/dir.c | 2 + fs/ocfs2/file.c | 19 + + fs/ocfs2/inode.c | 4 + fs/ocfs2/inode.h | 2 + fs/ocfs2/localalloc.c | 39 +- + fs/ocfs2/ocfs2.h | 5 + fs/ocfs2/reservations.c | 668 ++++++++++++++++++++++++++++++++++++ + fs/ocfs2/reservations.h | 151 ++++++++ + fs/ocfs2/suballoc.c | 1 + fs/ocfs2/suballoc.h | 2 + fs/ocfs2/super.c | 27 + + 16 files changed, 922 insertions(+), 6 deletions(-) + +--- a/Documentation/filesystems/ocfs2.txt ++++ b/Documentation/filesystems/ocfs2.txt +@@ -80,3 +80,6 @@ user_xattr (*) Enables Extended User Att + nouser_xattr Disables Extended User Attributes. + acl Enables POSIX Access Control Lists support. + noacl (*) Disables POSIX Access Control Lists support. ++resv_level=3 (*) Set how agressive allocation reservations will be. ++ Valid values are between 0 (reservations off) to 6 ++ (maximum space for reservations). +--- a/fs/ocfs2/Makefile ++++ b/fs/ocfs2/Makefile +@@ -29,6 +29,7 @@ ocfs2-objs := \ + mmap.o \ + namei.o \ + refcounttree.o \ ++ reservations.o \ + resize.o \ + slot_map.o \ + suballoc.o \ +--- a/fs/ocfs2/aops.c ++++ b/fs/ocfs2/aops.c +@@ -1735,6 +1735,8 @@ int ocfs2_write_begin_nolock(struct addr + goto out; + } + ++ data_ac->ac_resv = &OCFS2_I(inode)->ip_la_data_resv; ++ + credits = ocfs2_calc_extend_credits(inode->i_sb, + &di->id2.i_list, + clusters_to_alloc); +--- a/fs/ocfs2/cluster/masklog.c ++++ b/fs/ocfs2/cluster/masklog.c +@@ -116,6 +116,7 @@ static struct mlog_attribute mlog_attrs[ + define_mask(ERROR), + define_mask(NOTICE), + define_mask(KTHREAD), ++ define_mask(RESERVATIONS), + }; + + static struct attribute *mlog_attr_ptrs[MLOG_MAX_BITS] = {NULL, }; +--- a/fs/ocfs2/cluster/masklog.h ++++ b/fs/ocfs2/cluster/masklog.h +@@ -119,6 +119,7 @@ + #define ML_ERROR 0x0000000100000000ULL /* sent to KERN_ERR */ + #define ML_NOTICE 0x0000000200000000ULL /* setn to KERN_NOTICE */ + #define ML_KTHREAD 0x0000000400000000ULL /* kernel thread activity */ ++#define ML_RESERVATIONS 0x0000000800000000ULL /* ocfs2 alloc reservations */ + + #define MLOG_INITIAL_AND_MASK (ML_ERROR|ML_NOTICE) + #define MLOG_INITIAL_NOT_MASK (ML_ENTRY|ML_EXIT) +--- a/fs/ocfs2/dir.c ++++ b/fs/ocfs2/dir.c +@@ -2991,6 +2991,7 @@ static int ocfs2_expand_inline_dir(struc + * if we only get one now, that's enough to continue. The rest + * will be claimed after the conversion to extents. + */ ++ data_ac->ac_resv = &oi->ip_la_data_resv; + ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, &len); + if (ret) { + mlog_errno(ret); +@@ -3368,6 +3369,7 @@ static int ocfs2_extend_dir(struct ocfs2 + mlog_errno(status); + goto bail; + } ++ data_ac->ac_resv = &OCFS2_I(dir)->ip_la_data_resv; + + credits = ocfs2_calc_extend_credits(sb, el, 1); + } else { +--- a/fs/ocfs2/file.c ++++ b/fs/ocfs2/file.c +@@ -147,6 +147,7 @@ leave: + static int ocfs2_file_release(struct inode *inode, struct file *file) + { + struct ocfs2_inode_info *oi = OCFS2_I(inode); ++ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + mlog_entry("(0x%p, 0x%p, '%.*s')\n", inode, file, + file->f_path.dentry->d_name.len, +@@ -157,6 +158,21 @@ static int ocfs2_file_release(struct ino + oi->ip_flags &= ~OCFS2_INODE_OPEN_DIRECT; + spin_unlock(&oi->ip_lock); + ++#if 0 ++ /* ++ * Disable this for now. Keeping the reservation around a bit ++ * longer gives an improvement for workloads which rapidly do ++ * open()/write()/close() against a file. ++ */ ++ if ((file->f_mode & FMODE_WRITE) && ++ (atomic_read(&inode->i_writecount) == 1)) { ++ down_write(&oi->ip_alloc_sem); ++ ocfs2_resv_discard(&osb->osb_la_resmap, ++ &oi->ip_la_data_resv); ++ up_write(&oi->ip_alloc_sem); ++ } ++#endif ++ + ocfs2_free_file_private(inode, file); + + mlog_exit(0); +@@ -488,6 +504,9 @@ static int ocfs2_truncate_file(struct in + + down_write(&OCFS2_I(inode)->ip_alloc_sem); + ++ ocfs2_resv_discard(&osb->osb_la_resmap, ++ &OCFS2_I(inode)->ip_la_data_resv); ++ + /* + * The inode lock forced other nodes to sync and drop their + * pages, which (correctly) happens even if we have a truncate +--- a/fs/ocfs2/inode.c ++++ b/fs/ocfs2/inode.c +@@ -1101,6 +1101,10 @@ void ocfs2_clear_inode(struct inode *ino + ocfs2_mark_lockres_freeing(&oi->ip_inode_lockres); + ocfs2_mark_lockres_freeing(&oi->ip_open_lockres); + ++ ocfs2_resv_discard(&OCFS2_SB(inode->i_sb)->osb_la_resmap, ++ &oi->ip_la_data_resv); ++ ocfs2_resv_init_once(&oi->ip_la_data_resv); ++ + /* We very well may get a clear_inode before all an inodes + * metadata has hit disk. Of course, we can't drop any cluster + * locks until the journal has finished with it. The only +--- a/fs/ocfs2/inode.h ++++ b/fs/ocfs2/inode.h +@@ -70,6 +70,8 @@ struct ocfs2_inode_info + /* Only valid if the inode is the dir. */ + u32 ip_last_used_slot; + u64 ip_last_used_group; ++ ++ struct ocfs2_alloc_reservation ip_la_data_resv; + }; + + /* +--- a/fs/ocfs2/localalloc.c ++++ b/fs/ocfs2/localalloc.c +@@ -52,7 +52,8 @@ static u32 ocfs2_local_alloc_count_bits( + + static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb, + struct ocfs2_dinode *alloc, +- u32 numbits); ++ u32 numbits, ++ struct ocfs2_alloc_reservation *resv); + + static void ocfs2_clear_local_alloc(struct ocfs2_dinode *alloc); + +@@ -262,6 +263,8 @@ void ocfs2_shutdown_local_alloc(struct o + + osb->local_alloc_state = OCFS2_LA_DISABLED; + ++ ocfs2_resmap_uninit(&osb->osb_la_resmap); ++ + main_bm_inode = ocfs2_get_system_file_inode(osb, + GLOBAL_BITMAP_SYSTEM_INODE, + OCFS2_INVALID_SLOT); +@@ -498,7 +501,7 @@ static int ocfs2_local_alloc_in_range(st + alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data; + la = OCFS2_LOCAL_ALLOC(alloc); + +- start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted); ++ start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted, NULL); + if (start == -1) { + mlog_errno(-ENOSPC); + return 0; +@@ -664,7 +667,8 @@ int ocfs2_claim_local_alloc_bits(struct + alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data; + la = OCFS2_LOCAL_ALLOC(alloc); + +- start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted); ++ start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted, ++ ac->ac_resv); + if (start == -1) { + /* TODO: Shouldn't we just BUG here? */ + status = -ENOSPC; +@@ -687,6 +691,9 @@ int ocfs2_claim_local_alloc_bits(struct + goto bail; + } + ++ ocfs2_resmap_claimed_bits(&osb->osb_la_resmap, ac->ac_resv, start, ++ bits_wanted); ++ + while(bits_wanted--) + ocfs2_set_bit(start++, bitmap); + +@@ -722,11 +729,13 @@ static u32 ocfs2_local_alloc_count_bits( + } + + static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb, +- struct ocfs2_dinode *alloc, +- u32 numbits) ++ struct ocfs2_dinode *alloc, ++ u32 numbits, ++ struct ocfs2_alloc_reservation *resv) + { + int numfound, bitoff, left, startoff, lastzero; + void *bitmap = NULL; ++ struct ocfs2_reservation_map *resmap = &osb->osb_la_resmap; + + mlog_entry("(numbits wanted = %u)\n", numbits); + +@@ -738,6 +747,20 @@ static int ocfs2_local_alloc_find_clear_ + + bitmap = OCFS2_LOCAL_ALLOC(alloc)->la_bitmap; + ++ /* ++ * Ask the reservations code first whether this request can be ++ * easily fulfilled. No errors here are fatal - if we didn't ++ * find the number of bits needed, we'll just take the slow ++ * path. ++ */ ++ if (ocfs2_resmap_resv_bits(resmap, resv, bitmap, &bitoff, &numfound) ++ == 0) { ++ if (numfound >= numbits) { ++ numfound = numbits; ++ goto bail; ++ } ++ } ++ + numfound = bitoff = startoff = 0; + lastzero = -1; + left = le32_to_cpu(alloc->id1.bitmap1.i_total); +@@ -772,8 +795,10 @@ static int ocfs2_local_alloc_find_clear_ + + if (numfound == numbits) + bitoff = startoff - numfound; +- else ++ else { ++ numfound = 0; + bitoff = -1; ++ } + + bail: + mlog_exit(bitoff); +@@ -1096,6 +1121,8 @@ retry_enospc: + memset(OCFS2_LOCAL_ALLOC(alloc)->la_bitmap, 0, + le16_to_cpu(la->la_size)); + ++ ocfs2_resmap_restart(&osb->osb_la_resmap, cluster_count); ++ + mlog(0, "New window allocated:\n"); + mlog(0, "window la_bm_off = %u\n", + OCFS2_LOCAL_ALLOC(alloc)->la_bm_off); +--- a/fs/ocfs2/ocfs2.h ++++ b/fs/ocfs2/ocfs2.h +@@ -47,6 +47,7 @@ + /* For struct ocfs2_blockcheck_stats */ + #include "blockcheck.h" + ++#include "reservations.h" + + /* Caching of metadata buffers */ + +@@ -349,6 +350,10 @@ struct ocfs2_super + + u64 la_last_gd; + ++ struct ocfs2_reservation_map osb_la_resmap; ++ ++ unsigned int osb_resv_level; ++ + /* Next three fields are for local node slot recovery during + * mount. */ + int dirty; +--- /dev/null ++++ b/fs/ocfs2/reservations.c +@@ -0,0 +1,668 @@ ++/* -*- mode: c; c-basic-offset: 8; -*- ++ * vim: noexpandtab sw=8 ts=8 sts=0: ++ * ++ * reservations.c ++ * ++ * Allocation reservations implementation ++ * ++ * Some code borrowed from fs/ext3/balloc.c and is: ++ * ++ * Copyright (C) 1992, 1993, 1994, 1995 ++ * Remy Card (card@masi.ibp.fr) ++ * Laboratoire MASI - Institut Blaise Pascal ++ * Universite Pierre et Marie Curie (Paris VI) ++ * ++ * The rest is copyright (C) 2009 Novell. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define MLOG_MASK_PREFIX ML_RESERVATIONS ++#include ++ ++#include "ocfs2.h" ++ ++#ifdef CONFIG_OCFS2_DEBUG_FS ++#define OCFS2_CHECK_RESERVATIONS ++#endif ++ ++#define OCFS2_CHECK_RESERVATIONS ++ ++ ++DEFINE_SPINLOCK(resv_lock); ++ ++#define OCFS2_MIN_RESV_WINDOW_BITS 8 ++#define OCFS2_MAX_RESV_WINDOW_BITS 1024 ++ ++static unsigned int ocfs2_resv_window_bits(struct ocfs2_reservation_map *resmap) ++{ ++ struct ocfs2_super *osb = resmap->m_osb; ++ ++ mlog(0, "resv_level: %u\n", osb->osb_resv_level); ++ ++ switch (osb->osb_resv_level) { ++ case 6: ++ return OCFS2_MAX_RESV_WINDOW_BITS; ++ case 5: ++ return 512; ++ case 4: ++ return 256; ++ case 3: ++ return 128; ++ case 2: ++ return 64; ++ } ++ ++ return OCFS2_MIN_RESV_WINDOW_BITS; ++} ++ ++static inline unsigned int ocfs2_resv_end(struct ocfs2_alloc_reservation *resv) ++{ ++ if (resv->r_len) ++ return resv->r_start + resv->r_len - 1; ++ return resv->r_start; ++} ++ ++static inline int ocfs2_resv_empty(struct ocfs2_alloc_reservation *resv) ++{ ++ return !!(resv->r_len == 0); ++} ++ ++static inline int ocfs2_resmap_disabled(struct ocfs2_reservation_map *resmap) ++{ ++ if (resmap->m_osb->osb_resv_level == 0) ++ return 1; ++ return 0; ++} ++ ++static void ocfs2_dump_resv(struct ocfs2_reservation_map *resmap) ++{ ++ struct ocfs2_super *osb = resmap->m_osb; ++ struct rb_node *node; ++ struct ocfs2_alloc_reservation *resv; ++ int i = 0; ++ ++ mlog(ML_NOTICE, "Dumping resmap for device %s. Bitmap length: %u\n", ++ osb->dev_str, resmap->m_bitmap_len); ++ ++ node = rb_first(&resmap->m_reservations); ++ while (node) { ++ resv = rb_entry(node, struct ocfs2_alloc_reservation, r_node); ++ ++ mlog(ML_NOTICE, "start: %u\tend: %u\tlen: %u\tlast_start: %u" ++ "\tlast_len: %u\tallocated: %u\n", resv->r_start, ++ ocfs2_resv_end(resv), resv->r_len, resv->r_last_start, ++ resv->r_last_len, resv->r_allocated); ++ ++ node = rb_next(node); ++ i++; ++ } ++ ++ mlog(ML_NOTICE, "%d reservations found\n", i); ++} ++ ++#ifdef OCFS2_CHECK_RESERVATIONS ++static void ocfs2_check_resmap(struct ocfs2_reservation_map *resmap) ++{ ++ unsigned int off = 0; ++ int i = 0; ++ struct rb_node *node; ++ struct ocfs2_alloc_reservation *resv; ++ ++ node = rb_first(&resmap->m_reservations); ++ while (node) { ++ resv = rb_entry(node, struct ocfs2_alloc_reservation, r_node); ++ ++ if (i > 0 && resv->r_start <= off) { ++ mlog(ML_ERROR, "reservation %d has bad start off!\n", ++ i); ++ goto bad; ++ } ++ ++ if (resv->r_len == 0) { ++ mlog(ML_ERROR, "reservation %d has no length!\n", ++ i); ++ goto bad; ++ } ++ ++ if (resv->r_start > ocfs2_resv_end(resv)) { ++ mlog(ML_ERROR, "reservation %d has invalid range!\n", ++ i); ++ goto bad; ++ } ++ ++ if (ocfs2_resv_end(resv) > resmap->m_bitmap_len) { ++ mlog(ML_ERROR, "reservation %d extends past bitmap!\n", ++ i); ++ goto bad; ++ } ++ ++ off = ocfs2_resv_end(resv); ++ node = rb_next(node); ++ ++ i++; ++ } ++ return; ++ ++bad: ++ ocfs2_dump_resv(resmap); ++ BUG(); ++} ++#else ++static inline void ocfs2_check_resmap(struct ocfs2_reservation_map *resmap) ++{ ++ ++} ++#endif ++ ++void ocfs2_resv_init_once(struct ocfs2_alloc_reservation *resv) ++{ ++ memset(resv, 0, sizeof(*resv)); ++} ++ ++int ocfs2_resmap_init(struct ocfs2_super *osb, ++ struct ocfs2_reservation_map *resmap) ++{ ++ memset(resmap, 0, sizeof(*resmap)); ++ ++ resmap->m_osb = osb; ++ resmap->m_reservations = RB_ROOT; ++ /* m_bitmap_len is initialized to zero by the above memset. */ ++ ++ return 0; ++} ++ ++static void __ocfs2_resv_trunc(struct ocfs2_alloc_reservation *resv) ++{ ++ resv->r_len = 0; ++ resv->r_allocated = 0; ++} ++ ++static void ocfs2_resv_remove(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv) ++{ ++ if (resv->r_inuse) { ++ rb_erase(&resv->r_node, &resmap->m_reservations); ++ resv->r_inuse = 0; ++ } ++} ++ ++static void __ocfs2_resv_discard(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv) ++{ ++ assert_spin_locked(&resv_lock); ++ ++ __ocfs2_resv_trunc(resv); ++ ocfs2_resv_remove(resmap, resv); ++} ++ ++/* does nothing if 'resv' is null */ ++void ocfs2_resv_discard(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv) ++{ ++ if (resv) { ++ spin_lock(&resv_lock); ++ __ocfs2_resv_discard(resmap, resv); ++ spin_unlock(&resv_lock); ++ } ++} ++ ++static void ocfs2_resmap_clear_all_resv(struct ocfs2_reservation_map *resmap) ++{ ++ struct rb_node *node; ++ struct ocfs2_alloc_reservation *resv; ++ ++ assert_spin_locked(&resv_lock); ++ ++ while ((node = rb_last(&resmap->m_reservations)) != NULL) { ++ resv = rb_entry(node, struct ocfs2_alloc_reservation, r_node); ++ ++ __ocfs2_resv_discard(resmap, resv); ++ /* ++ * last_len and last_start no longer make sense if ++ * we're changing the range of our allocations. ++ */ ++ resv->r_last_len = resv->r_last_start = 0; ++ } ++} ++ ++/* If any parameters have changed, this function will call ++ * ocfs2_resv_trunc against all existing reservations. */ ++void ocfs2_resmap_restart(struct ocfs2_reservation_map *resmap, ++ unsigned int clen) ++{ ++ if (ocfs2_resmap_disabled(resmap)) ++ return; ++ ++ spin_lock(&resv_lock); ++ ++ ocfs2_resmap_clear_all_resv(resmap); ++ resmap->m_bitmap_len = clen; ++ ++ spin_unlock(&resv_lock); ++} ++ ++void ocfs2_resmap_uninit(struct ocfs2_reservation_map *resmap) ++{ ++ /* Does nothing for now. Keep this around for API symmetry */ ++} ++ ++/* ++ * Determine the number of available bits between my_resv and the next ++ * window and extends my_resv accordingly. ++ */ ++static int ocfs2_try_to_extend_resv(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *my_resv) ++{ ++ unsigned int available, avail_end; ++ struct rb_node *next, *node = &my_resv->r_node; ++ struct ocfs2_alloc_reservation *next_resv; ++ unsigned int bits = ocfs2_resv_window_bits(resmap); ++ ++ next = rb_next(node); ++ ++ if (next) { ++ next_resv = rb_entry(next, struct ocfs2_alloc_reservation, ++ r_node); ++ avail_end = next_resv->r_start; ++ } else { ++ avail_end = resmap->m_bitmap_len - 1; ++ } ++ ++ if (ocfs2_resv_end(my_resv) == avail_end) ++ return -ENOENT; ++ ++ available = avail_end - ocfs2_resv_end(my_resv) - 1; ++ ++ my_resv->r_len += available; ++ if (my_resv->r_len > bits) ++ my_resv->r_len = bits; ++ ++ ocfs2_check_resmap(resmap); ++ ++ return 0; ++} ++ ++static void ocfs2_resv_insert(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *new) ++{ ++ struct rb_root *root = &resmap->m_reservations; ++ struct rb_node *parent = NULL; ++ struct rb_node **p = &root->rb_node; ++ struct ocfs2_alloc_reservation *tmp; ++ ++ assert_spin_locked(&resv_lock); ++ ++ mlog(0, "Insert reservation start: %u len: %u\n", new->r_start, ++ new->r_len); ++ ++ while(*p) { ++ parent = *p; ++ ++ tmp = rb_entry(parent, struct ocfs2_alloc_reservation, r_node); ++ ++ if (new->r_start < tmp->r_start) ++ p = &(*p)->rb_left; ++ else if (new->r_start > ocfs2_resv_end(tmp)) ++ p = &(*p)->rb_right; ++ else { ++ /* This should never happen! */ ++ mlog(ML_ERROR, "Duplicate reservation window!\n"); ++ BUG(); ++ } ++ } ++ ++ rb_link_node(&new->r_node, parent, p); ++ rb_insert_color(&new->r_node, root); ++ new->r_inuse = 1; ++ ++ ocfs2_check_resmap(resmap); ++} ++ ++/** ++ * ocfs2_find_resv() - find the window which contains goal ++ * @resmap: reservation map to search ++ * @goal: which bit to search for ++ * ++ * If a window containing that goal is not found, we return the window ++ * which comes before goal. Returns NULL on empty rbtree or no window ++ * before goal. ++ */ ++static struct ocfs2_alloc_reservation * ++ocfs2_find_resv(struct ocfs2_reservation_map *resmap, unsigned int goal) ++{ ++ struct ocfs2_alloc_reservation *resv; ++ struct rb_node *n = resmap->m_reservations.rb_node; ++ ++ assert_spin_locked(&resv_lock); ++ ++ if (!n) ++ return NULL; ++ ++ do { ++ resv = rb_entry(n, struct ocfs2_alloc_reservation, r_node); ++ ++ if (goal < resv->r_start) ++ n = n->rb_left; ++ else if (goal > ocfs2_resv_end(resv)) ++ n = n->rb_right; ++ else ++ return resv; ++ } while (n); ++ ++ /* ++ * The goal sits on one end of the tree. If it's the leftmost ++ * end, we return NULL. ++ */ ++ if (resv->r_start > goal) ++ return NULL; ++ ++ return resv; ++} ++ ++static void ocfs2_resv_find_window(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv) ++{ ++ struct rb_root *root = &resmap->m_reservations; ++ unsigned int last_start = resv->r_last_start; ++ unsigned int goal = 0; ++ unsigned int len = ocfs2_resv_window_bits(resmap); ++ unsigned int gap_start, gap_end, gap_len; ++ struct ocfs2_alloc_reservation *prev_resv, *next_resv; ++ struct rb_node *prev, *next; ++ ++ if (resv->r_last_len) { ++ unsigned int last_end = last_start + resv->r_last_len - 1; ++ ++ goal = last_end + 1; ++ ++ if (goal >= resmap->m_bitmap_len) ++ goal = 0; ++ } ++ ++ /* ++ * Nasty cases to consider: ++ * ++ * - rbtree is empty ++ * - our window should be first in all reservations ++ * - our window should be last in all reservations ++ * - need to make sure we don't go past end of bitmap ++ */ ++ ++ assert_spin_locked(&resv_lock); ++ ++ if (RB_EMPTY_ROOT(root)) { ++ /* ++ * Easiest case - empty tree. We can just take ++ * whatever window we want. ++ */ ++ ++ mlog(0, "Empty root\n"); ++ ++ resv->r_start = goal; ++ resv->r_len = len; ++ if (ocfs2_resv_end(resv) >= resmap->m_bitmap_len) ++ resv->r_len = resmap->m_bitmap_len - resv->r_start; ++ ++ ocfs2_resv_insert(resmap, resv); ++ return; ++ } ++ ++ prev_resv = ocfs2_find_resv(resmap, goal); ++ ++ if (prev_resv == NULL) { ++ mlog(0, "Farthest left window\n"); ++ ++ /* Ok, we're the farthest left window. */ ++ next = rb_first(root); ++ next_resv = rb_entry(next, struct ocfs2_alloc_reservation, ++ r_node); ++ ++ /* ++ * Try to allocate at far left of tree. If that ++ * doesn't fit, we just start our linear search from ++ * next_resv ++ */ ++ if (next_resv->r_start > (goal + len - 1)) { ++ resv->r_start = goal; ++ resv->r_len = len; ++ ++ ocfs2_resv_insert(resmap, resv); ++ return; ++ } ++ ++ prev_resv = next_resv; ++ next_resv = NULL; ++ } ++ ++ prev = &prev_resv->r_node; ++ ++ /* Now we do a linear search for a window, starting at 'prev_rsv' */ ++ while (1) { ++ next = rb_next(prev); ++ if (next) { ++ mlog(0, "One more resv found in linear search\n"); ++ next_resv = rb_entry(next, ++ struct ocfs2_alloc_reservation, ++ r_node); ++ ++ gap_start = ocfs2_resv_end(prev_resv) + 1; ++ gap_end = next_resv->r_start - 1; ++ gap_len = gap_end - gap_start + 1; ++ } else { ++ mlog(0, "No next node\n"); ++ /* ++ * We're at the rightmost edge of the ++ * tree. See if a reservation between this ++ * window and the end of the bitmap will work. ++ */ ++ gap_start = ocfs2_resv_end(prev_resv) + 1; ++ gap_end = resmap->m_bitmap_len - 1; ++ gap_len = gap_end - gap_start + 1; ++ } ++ ++ if (gap_start <= gap_end ++ && gap_start >= goal ++ && gap_len >= len) { ++ resv->r_start = gap_start; ++ resv->r_len = len; ++ ++ ocfs2_resv_insert(resmap, resv); ++ return; ++ } ++ ++ if (!next) ++ break; ++ ++ prev = next; ++ prev_resv = rb_entry(prev, struct ocfs2_alloc_reservation, ++ r_node); ++ } ++} ++ ++void ocfs2_resmap_claimed_bits(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv, ++ u32 cstart, u32 clen) ++{ ++ unsigned int cend = cstart + clen - 1; ++ ++ if (resmap == NULL || ocfs2_resmap_disabled(resmap)) ++ return; ++ ++ if (resv == NULL) ++ return; ++ ++ spin_lock(&resv_lock); ++ ++ mlog(0, "claim bits: cstart: %u cend: %u clen: %u r_start: %u " ++ "r_end: %u r_len: %u, r_last_start: %u r_last_len: %u\n", ++ cstart, cend, clen, resv->r_start, ocfs2_resv_end(resv), ++ resv->r_len, resv->r_last_start, resv->r_last_len); ++ ++ resv->r_last_len = clen; ++ resv->r_last_start = cstart; ++ ++ if (ocfs2_resv_empty(resv)) { ++ mlog(0, "Empty reservation, find a new window.\n"); ++ /* ++ * Allocation occured without a window. We find an ++ * initial reservation for this inode, based on what ++ * was allocated already. ++ */ ++ ocfs2_resv_find_window(resmap, resv); ++ goto out_unlock; ++ } ++ ++ /* ++ * Did the allocation occur completely outside our ++ * reservation? Clear it then. Otherwise, try to extend our ++ * reservation or alloc a new one, if we've used all the bits. ++ */ ++ if (cend < resv->r_start || ++ cstart > ocfs2_resv_end(resv)) { ++ mlog(0, "Allocated outside reservation\n"); ++ ++ /* Truncate and remove reservation */ ++ __ocfs2_resv_discard(resmap, resv); ++ ++ if (cend < resv->r_start) { ++ /* ++ * The window wasn't used for some reason. We ++ * should start our search *past* it to give a ++ * better chance the next window will be ++ * used. Best way to do this right now is to ++ * fool the search code... ++ */ ++ resv->r_last_start = ocfs2_resv_end(resv) + 1; ++ resv->r_last_len = 1; ++ } ++ ++ ocfs2_resv_find_window(resmap, resv); ++ goto out_unlock; ++ } ++ ++ /* ++ * We allocated at least partially from our ++ * reservation. Adjust it and try to extend. Otherwise, we ++ * search for a new window. ++ */ ++ ++ resv->r_allocated += clen; ++ ++ if (cend < ocfs2_resv_end(resv)) { ++ u32 old_end; ++ ++ mlog(0, "Allocation left at end\n"); ++ ++ /* ++ * Partial allocation, leaving some bits free at ++ * end. We move over the start of the window to take ++ * this into account and try to extend it. ++ */ ++ old_end = ocfs2_resv_end(resv); ++ resv->r_start = cend + 1; /* Start just past last allocation */ ++ resv->r_len = old_end - resv->r_start + 1; ++ ++ if (ocfs2_try_to_extend_resv(resmap, resv) == 0) ++ goto out_unlock; ++ } ++ ++ mlog(0, "discard reservation\n"); ++ ++ /* ++ * No free bits at end or extend failed above. Truncate and ++ * re-search for a new window. ++ */ ++ ++ __ocfs2_resv_discard(resmap, resv); ++ ++ ocfs2_resv_find_window(resmap, resv); ++ ++out_unlock: ++ mlog(0, "Reservation now looks like: r_start: %u r_end: %u " ++ "r_len: %u r_last_start: %u r_last_len: %u\n", ++ resv->r_start, ocfs2_resv_end(resv), resv->r_len, ++ resv->r_last_start, resv->r_last_len); ++ ++ spin_unlock(&resv_lock); ++} ++ ++int ocfs2_resmap_resv_bits(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv, ++ char *disk_bitmap, int *cstart, int *clen) ++{ ++ int ret = -ENOSPC; ++ unsigned int start, len, best_start = 0, best_len = 0; ++ ++ if (resv == NULL || ocfs2_resmap_disabled(resmap)) ++ return -ENOSPC; ++ ++ spin_lock(&resv_lock); ++ ++ if (ocfs2_resv_empty(resv)) { ++ mlog(0, "empty reservation, find new window\n"); ++ ++ ocfs2_resv_find_window(resmap, resv); ++ ++ if (ocfs2_resv_empty(resv)) { ++ /* ++ * If resv is still empty, we return zero ++ * bytes and allow ocfs2_resmap_claimed_bits() ++ * to start our new reservation after the ++ * allocator has done it's work. ++ */ ++ *cstart = *clen = 0; ++ ret = 0; ++ goto out; ++ } ++ } ++ ++ start = resv->r_start; ++ len = 0; ++ ++ while (start <= ocfs2_resv_end(resv)) { ++ if (ocfs2_test_bit(start, disk_bitmap)) { ++ mlog(0, ++ "Reservation was taken at bit %d\n", ++ start + len); ++ best_len = 0; ++ goto next; ++ } ++ ++ /* This is basic, but since the local alloc is ++ * used very predictably, I think we're ok. */ ++ if (!best_len) { ++ best_start = start; ++ best_len = 1; ++ } else { ++ best_len++; ++ } ++ ++next: ++ start++; ++ } ++ ++ if (best_len) { ++ ret = 0; ++ *cstart = best_start; ++ *clen = best_len; ++ } ++out: ++ spin_unlock(&resv_lock); ++ ++ return ret; ++} +--- /dev/null ++++ b/fs/ocfs2/reservations.h +@@ -0,0 +1,151 @@ ++/* -*- mode: c; c-basic-offset: 8; -*- ++ * vim: noexpandtab sw=8 ts=8 sts=0: ++ * ++ * reservations.h ++ * ++ * Allocation reservations function prototypes and structures. ++ * ++ * Copyright (C) 2009 Novell. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public ++ * License along with this program; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * Boston, MA 021110-1307, USA. ++ */ ++ ++#ifndef OCFS2_RESERVATIONS_H ++#define OCFS2_RESERVATIONS_H ++ ++#include ++ ++struct ocfs2_bitmap_resv_ops; ++ ++#define OCFS2_DEFAULT_RESV_LEVEL 3 ++#define OCFS2_MAX_RESV_LEVEL 7 ++#define OCFS2_MIN_RESV_LEVEL 0 ++ ++struct ocfs2_alloc_reservation { ++ struct rb_node r_node; ++ ++ unsigned int r_start; ++ unsigned int r_len; ++ ++ unsigned int r_last_len; ++ unsigned int r_last_start; ++ ++ unsigned int r_allocated; ++ ++ int r_inuse; ++}; ++ ++struct ocfs2_reservation_map { ++ struct rb_root m_reservations; ++ ++ struct ocfs2_super *m_osb; ++ ++ /* The following are not initialized to meaningful values until a disk ++ * bitmap is provided. */ ++ u32 m_bitmap_len; /* Number of valid ++ * bits available */ ++}; ++ ++void ocfs2_resv_init_once(struct ocfs2_alloc_reservation *resv); ++ ++/** ++ * ocfs2_resv_discard() - truncate a reservation ++ * @resmap: ++ * @resv: the reservation to truncate. ++ * ++ * After this function is called, the reservation will be empty, and ++ * unlinked from the rbtree. ++ */ ++void ocfs2_resv_discard(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv); ++ ++ ++/** ++ * ocfs2_resmap_init() - Initialize fields of a reservations bitmap ++ * @resmap: struct ocfs2_reservation_map to initialize ++ * @obj: unused for now ++ * @ops: unused for now ++ * @max_bitmap_bytes: Maximum size of the bitmap (typically blocksize) ++ * ++ * Only possible return value other than '0' is -ENOMEM for failure to ++ * allocation mirror bitmap. ++ */ ++int ocfs2_resmap_init(struct ocfs2_super *osb, ++ struct ocfs2_reservation_map *resmap); ++ ++/** ++ * ocfs2_resmap_restart() - "restart" a reservation bitmap ++ * @resmap: reservations bitmap ++ * @clen: Number of valid bits in the bitmap ++ * ++ * Re-initialize the parameters of a reservation bitmap. This is ++ * useful for local alloc window slides. ++ * ++ * If any bitmap parameters have changed, this function will call ++ * ocfs2_trunc_resv against all existing reservations. A future ++ * version will recalculate existing reservations based on the new ++ * bitmap. ++ */ ++void ocfs2_resmap_restart(struct ocfs2_reservation_map *resmap, ++ unsigned int clen); ++ ++/** ++ * ocfs2_resmap_uninit() - uninitialize a reservation bitmap structure ++ * @resmap: the struct ocfs2_reservation_map to uninitialize ++ */ ++void ocfs2_resmap_uninit(struct ocfs2_reservation_map *resmap); ++ ++/** ++ * ocfs2_resmap_resv_bits() - Return still-valid reservation bits ++ * @resmap: reservations bitmap ++ * @resv: reservation to base search from ++ * @disk_bitmap: up to date (from disk) allocation bitmap ++ * @cstart: start of proposed allocation ++ * @clen: length (in clusters) of proposed allocation ++ * ++ * Using the reservation data from resv, this function will compare ++ * resmap and disk_bitmap to determine what part (if any) of the ++ * reservation window is still clear to use. An empty resv passed here ++ * will just return no allocation. ++ * ++ * On success, zero is returned and the valid allocation area is set in cstart ++ * and clen. If no allocation is found, they are set to zero. ++ * ++ * Returns nonzero on error. ++ */ ++int ocfs2_resmap_resv_bits(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv, ++ char *disk_bitmap, int *cstart, int *clen); ++ ++/** ++ * ocfs2_resmap_claimed_bits() - Tell the reservation code that bits were used. ++ * @resmap: reservations bitmap ++ * @resv: optional reservation to recalulate based on new bitmap ++ * @cstart: start of allocation in clusters ++ * @clen: end of allocation in clusters. ++ * ++ * Tell the reservation code that bits were used to fulfill allocation in ++ * resmap. The bits don't have to have been part of any existing ++ * reservation. But we must always call this function when bits are claimed. ++ * Internally, the reservations code will use this information to mark the ++ * reservations bitmap. If resv is passed, it's next allocation window will be ++ * calculated. ++ */ ++void ocfs2_resmap_claimed_bits(struct ocfs2_reservation_map *resmap, ++ struct ocfs2_alloc_reservation *resv, ++ u32 cstart, u32 clen); ++ ++#endif /* OCFS2_RESERVATIONS_H */ +--- a/fs/ocfs2/suballoc.c ++++ b/fs/ocfs2/suballoc.c +@@ -137,6 +137,7 @@ void ocfs2_free_ac_resource(struct ocfs2 + } + brelse(ac->ac_bh); + ac->ac_bh = NULL; ++ ac->ac_resv = NULL; + } + + void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac) +--- a/fs/ocfs2/suballoc.h ++++ b/fs/ocfs2/suballoc.h +@@ -54,6 +54,8 @@ struct ocfs2_alloc_context { + u64 ac_last_group; + u64 ac_max_block; /* Highest block number to allocate. 0 is + is the same as ~0 - unlimited */ ++ ++ struct ocfs2_alloc_reservation *ac_resv; + }; + + void ocfs2_init_steal_slots(struct ocfs2_super *osb); +--- a/fs/ocfs2/super.c ++++ b/fs/ocfs2/super.c +@@ -95,6 +95,7 @@ struct mount_options + unsigned int atime_quantum; + signed short slot; + unsigned int localalloc_opt; ++ unsigned int resv_level; + char cluster_stack[OCFS2_STACK_LABEL_LEN + 1]; + }; + +@@ -176,6 +177,7 @@ enum { + Opt_noacl, + Opt_usrquota, + Opt_grpquota, ++ Opt_resv_level, + Opt_err, + }; + +@@ -202,6 +204,7 @@ static const match_table_t tokens = { + {Opt_noacl, "noacl"}, + {Opt_usrquota, "usrquota"}, + {Opt_grpquota, "grpquota"}, ++ {Opt_resv_level, "resv_level=%u"}, + {Opt_err, NULL} + }; + +@@ -1030,6 +1033,7 @@ static int ocfs2_fill_super(struct super + osb->osb_commit_interval = parsed_options.commit_interval; + osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt); + osb->local_alloc_bits = osb->local_alloc_default_bits; ++ osb->osb_resv_level = parsed_options.resv_level; + + status = ocfs2_verify_userspace_stack(osb, &parsed_options); + if (status) +@@ -1290,6 +1294,7 @@ static int ocfs2_parse_options(struct su + mopt->slot = OCFS2_INVALID_SLOT; + mopt->localalloc_opt = OCFS2_DEFAULT_LOCAL_ALLOC_SIZE; + mopt->cluster_stack[0] = '\0'; ++ mopt->resv_level = OCFS2_DEFAULT_RESV_LEVEL; + + if (!options) { + status = 1; +@@ -1433,6 +1438,17 @@ static int ocfs2_parse_options(struct su + mopt->mount_opt |= OCFS2_MOUNT_NO_POSIX_ACL; + mopt->mount_opt &= ~OCFS2_MOUNT_POSIX_ACL; + break; ++ case Opt_resv_level: ++ if (is_remount) ++ break; ++ if (match_int(&args[0], &option)) { ++ status = 0; ++ goto bail; ++ } ++ if (option >= OCFS2_MIN_RESV_LEVEL && ++ option < OCFS2_MAX_RESV_LEVEL) ++ mopt->resv_level = option; ++ break; + default: + mlog(ML_ERROR, + "Unrecognized mount option \"%s\" " +@@ -1514,6 +1530,9 @@ static int ocfs2_show_options(struct seq + else + seq_printf(s, ",noacl"); + ++ if (osb->osb_resv_level != OCFS2_DEFAULT_RESV_LEVEL) ++ seq_printf(s, ",resv_level=%d", osb->osb_resv_level); ++ + return 0; + } + +@@ -1688,6 +1707,8 @@ static void ocfs2_inode_init_once(void * + oi->ip_blkno = 0ULL; + oi->ip_clusters = 0; + ++ ocfs2_resv_init_once(&oi->ip_la_data_resv); ++ + ocfs2_lock_res_init_once(&oi->ip_rw_lockres); + ocfs2_lock_res_init_once(&oi->ip_inode_lockres); + ocfs2_lock_res_init_once(&oi->ip_open_lockres); +@@ -2042,6 +2063,12 @@ static int ocfs2_initialize_super(struct + + init_waitqueue_head(&osb->osb_mount_event); + ++ status = ocfs2_resmap_init(osb, &osb->osb_la_resmap); ++ if (status) { ++ mlog_errno(status); ++ goto bail; ++ } ++ + osb->vol_label = kmalloc(OCFS2_MAX_VOL_LABEL_LEN, GFP_KERNEL); + if (!osb->vol_label) { + mlog(ML_ERROR, "unable to alloc vol label\n"); diff --git a/patches.suse/of_platform_driver.module-owner.patch b/patches.suse/of_platform_driver.module-owner.patch new file mode 100644 index 0000000..b87ded8 --- /dev/null +++ b/patches.suse/of_platform_driver.module-owner.patch @@ -0,0 +1,1191 @@ +Subject: add missing module symlink to /sys/bus/*/driver/* in struct of_platform_driver +Patch-mainline: Not yet +From: olh@suse.de +Patch-mainline: not yet +--- + arch/powerpc/kernel/of_platform.c | 1 + + arch/powerpc/platforms/52xx/mpc52xx_gpio.c | 2 ++ + arch/powerpc/platforms/52xx/mpc52xx_gpt.c | 1 + + arch/powerpc/platforms/82xx/ep8248e.c | 1 + + arch/powerpc/platforms/83xx/suspend.c | 1 + + arch/powerpc/platforms/cell/axon_msi.c | 1 + + arch/powerpc/platforms/pasemi/gpio_mdio.c | 1 + + arch/powerpc/sysdev/fsl_msi.c | 1 + + arch/powerpc/sysdev/fsl_rio.c | 1 + + arch/powerpc/sysdev/pmi.c | 1 + + arch/sparc/include/asm/parport.h | 1 + + arch/sparc/kernel/apc.c | 1 + + arch/sparc/kernel/auxio_64.c | 1 + + arch/sparc/kernel/central.c | 2 ++ + arch/sparc/kernel/chmc.c | 1 + + arch/sparc/kernel/pci_fire.c | 1 + + arch/sparc/kernel/pci_psycho.c | 1 + + arch/sparc/kernel/pci_sabre.c | 1 + + arch/sparc/kernel/pci_schizo.c | 1 + + arch/sparc/kernel/pci_sun4v.c | 1 + + arch/sparc/kernel/pmc.c | 1 + + arch/sparc/kernel/power.c | 1 + + arch/sparc/kernel/time_32.c | 1 + + arch/sparc/kernel/time_64.c | 3 +++ + drivers/ata/pata_of_platform.c | 1 + + drivers/ata/sata_fsl.c | 1 + + drivers/atm/fore200e.c | 1 + + drivers/char/hw_random/n2-drv.c | 1 + + drivers/char/hw_random/pasemi-rng.c | 1 + + drivers/char/ipmi/ipmi_si_intf.c | 1 + + drivers/crypto/amcc/crypto4xx_core.c | 1 + + drivers/crypto/talitos.c | 1 + + drivers/dma/fsldma.c | 1 + + drivers/hwmon/ultra45_env.c | 1 + + drivers/i2c/busses/i2c-ibm_iic.c | 1 + + drivers/infiniband/hw/ehca/ehca_main.c | 1 + + drivers/input/misc/sparcspkr.c | 2 ++ + drivers/input/serio/i8042-sparcio.h | 1 + + drivers/input/serio/xilinx_ps2.c | 1 + + drivers/macintosh/smu.c | 1 + + drivers/macintosh/therm_pm72.c | 1 + + drivers/macintosh/therm_windtunnel.c | 1 + + drivers/mmc/host/sdhci-of-core.c | 1 + + drivers/mtd/maps/physmap_of.c | 1 + + drivers/mtd/maps/sun_uflash.c | 1 + + drivers/mtd/nand/fsl_elbc_nand.c | 1 + + drivers/mtd/nand/fsl_upm.c | 1 + + drivers/mtd/nand/ndfc.c | 1 + + drivers/mtd/nand/pasemi_nand.c | 1 + + drivers/mtd/nand/socrates_nand.c | 1 + + drivers/net/ehea/ehea_main.c | 1 + + drivers/net/fec_mpc52xx_phy.c | 1 + + drivers/net/fs_enet/fs_enet-main.c | 1 + + drivers/net/fs_enet/mii-bitbang.c | 1 + + drivers/net/fs_enet/mii-fec.c | 1 + + drivers/net/fsl_pq_mdio.c | 1 + + drivers/net/gianfar.c | 1 + + drivers/net/ibm_newemac/core.c | 1 + + drivers/net/ibm_newemac/mal.c | 1 + + drivers/net/ibm_newemac/rgmii.c | 1 + + drivers/net/ibm_newemac/tah.c | 1 + + drivers/net/ibm_newemac/zmii.c | 1 + + drivers/net/myri_sbus.c | 1 + + drivers/net/niu.c | 1 + + drivers/net/phy/mdio-gpio.c | 1 + + drivers/net/sunbmac.c | 1 + + drivers/net/sunhme.c | 1 + + drivers/net/sunlance.c | 1 + + drivers/net/sunqe.c | 1 + + drivers/net/ucc_geth.c | 1 + + drivers/parport/parport_sunbpp.c | 1 + + drivers/pcmcia/electra_cf.c | 1 + + drivers/pcmcia/m8xx_pcmcia.c | 1 + + drivers/sbus/char/bbc_i2c.c | 1 + + drivers/sbus/char/display7seg.c | 1 + + drivers/sbus/char/envctrl.c | 1 + + drivers/sbus/char/flash.c | 1 + + drivers/sbus/char/uctrl.c | 1 + + drivers/scsi/qlogicpti.c | 1 + + drivers/scsi/sun_esp.c | 1 + + drivers/serial/cpm_uart/cpm_uart_core.c | 1 + + drivers/serial/mpc52xx_uart.c | 1 + + drivers/serial/sunhv.c | 1 + + drivers/serial/sunsab.c | 1 + + drivers/serial/sunsu.c | 1 + + drivers/serial/sunzilog.c | 1 + + drivers/spi/spi_mpc8xxx.c | 1 + + drivers/usb/gadget/fsl_qe_udc.c | 1 + + drivers/usb/host/fhci-hcd.c | 1 + + drivers/usb/host/isp1760-if.c | 1 + + drivers/video/bw2.c | 1 + + drivers/video/cg14.c | 1 + + drivers/video/cg3.c | 1 + + drivers/video/cg6.c | 1 + + drivers/video/ffb.c | 1 + + drivers/video/leo.c | 1 + + drivers/video/p9100.c | 1 + + drivers/video/platinumfb.c | 1 + + drivers/video/tcx.c | 1 + + drivers/watchdog/cpwd.c | 1 + + drivers/watchdog/riowd.c | 1 + + sound/sparc/amd7930.c | 1 + + sound/sparc/cs4231.c | 1 + + sound/sparc/dbri.c | 1 + + 104 files changed, 109 insertions(+) + +--- a/arch/powerpc/kernel/of_platform.c ++++ b/arch/powerpc/kernel/of_platform.c +@@ -307,6 +307,7 @@ static struct of_device_id of_pci_phb_id + }; + + static struct of_platform_driver of_pci_phb_driver = { ++ .owner = THIS_MODULE, + .match_table = of_pci_phb_ids, + .probe = of_pci_phb_probe, + .driver = { +--- a/arch/powerpc/platforms/52xx/mpc52xx_gpio.c ++++ b/arch/powerpc/platforms/52xx/mpc52xx_gpio.c +@@ -192,6 +192,7 @@ static const struct of_device_id mpc52xx + }; + + static struct of_platform_driver mpc52xx_wkup_gpiochip_driver = { ++ .owner = THIS_MODULE, + .name = "gpio_wkup", + .match_table = mpc52xx_wkup_gpiochip_match, + .probe = mpc52xx_wkup_gpiochip_probe, +@@ -348,6 +349,7 @@ static const struct of_device_id mpc52xx + }; + + static struct of_platform_driver mpc52xx_simple_gpiochip_driver = { ++ .owner = THIS_MODULE, + .name = "gpio", + .match_table = mpc52xx_simple_gpiochip_match, + .probe = mpc52xx_simple_gpiochip_probe, +--- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c ++++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c +@@ -783,6 +783,7 @@ static const struct of_device_id mpc52xx + }; + + static struct of_platform_driver mpc52xx_gpt_driver = { ++ .owner = THIS_MODULE, + .name = "mpc52xx-gpt", + .match_table = mpc52xx_gpt_match, + .probe = mpc52xx_gpt_probe, +--- a/arch/powerpc/platforms/82xx/ep8248e.c ++++ b/arch/powerpc/platforms/82xx/ep8248e.c +@@ -173,6 +173,7 @@ static struct of_platform_driver ep8248e + .match_table = ep8248e_mdio_match, + .probe = ep8248e_mdio_probe, + .remove = ep8248e_mdio_remove, ++ .owner = THIS_MODULE, + }; + + struct cpm_pin { +--- a/arch/powerpc/platforms/83xx/suspend.c ++++ b/arch/powerpc/platforms/83xx/suspend.c +@@ -423,6 +423,7 @@ static struct of_device_id pmc_match[] = + }; + + static struct of_platform_driver pmc_driver = { ++ .owner = THIS_MODULE, + .name = "mpc83xx-pmc", + .match_table = pmc_match, + .probe = pmc_probe, +--- a/arch/powerpc/platforms/cell/axon_msi.c ++++ b/arch/powerpc/platforms/cell/axon_msi.c +@@ -446,6 +446,7 @@ static const struct of_device_id axon_ms + }; + + static struct of_platform_driver axon_msi_driver = { ++ .owner = THIS_MODULE, + .match_table = axon_msi_device_id, + .probe = axon_msi_probe, + .shutdown = axon_msi_shutdown, +--- a/arch/powerpc/platforms/pasemi/gpio_mdio.c ++++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c +@@ -300,6 +300,7 @@ MODULE_DEVICE_TABLE(of, gpio_mdio_match) + + static struct of_platform_driver gpio_mdio_driver = + { ++ .owner = THIS_MODULE, + .match_table = gpio_mdio_match, + .probe = gpio_mdio_probe, + .remove = gpio_mdio_remove, +--- a/arch/powerpc/sysdev/fsl_msi.c ++++ b/arch/powerpc/sysdev/fsl_msi.c +@@ -344,6 +344,7 @@ static const struct of_device_id fsl_of_ + }; + + static struct of_platform_driver fsl_of_msi_driver = { ++ .owner = THIS_MODULE, + .name = "fsl-msi", + .match_table = fsl_of_msi_ids, + .probe = fsl_of_msi_probe, +--- a/arch/powerpc/sysdev/fsl_rio.c ++++ b/arch/powerpc/sysdev/fsl_rio.c +@@ -1214,6 +1214,7 @@ static const struct of_device_id fsl_of_ + }; + + static struct of_platform_driver fsl_of_rio_rpn_driver = { ++ .owner = THIS_MODULE, + .name = "fsl-of-rio", + .match_table = fsl_of_rio_rpn_ids, + .probe = fsl_of_rio_rpn_probe, +--- a/arch/powerpc/sysdev/pmi.c ++++ b/arch/powerpc/sysdev/pmi.c +@@ -205,6 +205,7 @@ static int pmi_of_remove(struct of_devic + } + + static struct of_platform_driver pmi_of_platform_driver = { ++ .owner = THIS_MODULE, + .match_table = pmi_match, + .probe = pmi_of_probe, + .remove = pmi_of_remove, +--- a/arch/sparc/include/asm/parport.h ++++ b/arch/sparc/include/asm/parport.h +@@ -232,6 +232,7 @@ static const struct of_device_id ecpp_ma + }; + + static struct of_platform_driver ecpp_driver = { ++ .owner = THIS_MODULE, + .name = "ecpp", + .match_table = ecpp_match, + .probe = ecpp_probe, +--- a/arch/sparc/kernel/apc.c ++++ b/arch/sparc/kernel/apc.c +@@ -174,6 +174,7 @@ static struct of_device_id __initdata ap + MODULE_DEVICE_TABLE(of, apc_match); + + static struct of_platform_driver apc_driver = { ++ .owner = THIS_MODULE, + .name = "apc", + .match_table = apc_match, + .probe = apc_probe, +--- a/arch/sparc/kernel/auxio_64.c ++++ b/arch/sparc/kernel/auxio_64.c +@@ -132,6 +132,7 @@ static int __devinit auxio_probe(struct + } + + static struct of_platform_driver auxio_driver = { ++ .owner = THIS_MODULE, + .match_table = auxio_match, + .probe = auxio_probe, + .driver = { +--- a/arch/sparc/kernel/central.c ++++ b/arch/sparc/kernel/central.c +@@ -148,6 +148,7 @@ static struct of_device_id __initdata cl + }; + + static struct of_platform_driver clock_board_driver = { ++ .owner = THIS_MODULE, + .match_table = clock_board_match, + .probe = clock_board_probe, + .driver = { +@@ -253,6 +254,7 @@ static struct of_device_id __initdata fh + }; + + static struct of_platform_driver fhc_driver = { ++ .owner = THIS_MODULE, + .match_table = fhc_match, + .probe = fhc_probe, + .driver = { +--- a/arch/sparc/kernel/chmc.c ++++ b/arch/sparc/kernel/chmc.c +@@ -811,6 +811,7 @@ static const struct of_device_id us3mc_m + MODULE_DEVICE_TABLE(of, us3mc_match); + + static struct of_platform_driver us3mc_driver = { ++ .owner = THIS_MODULE, + .name = "us3mc", + .match_table = us3mc_match, + .probe = us3mc_probe, +--- a/arch/sparc/kernel/pci_fire.c ++++ b/arch/sparc/kernel/pci_fire.c +@@ -508,6 +508,7 @@ static struct of_device_id __initdata fi + }; + + static struct of_platform_driver fire_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = fire_match, + .probe = fire_probe, +--- a/arch/sparc/kernel/pci_psycho.c ++++ b/arch/sparc/kernel/pci_psycho.c +@@ -602,6 +602,7 @@ static struct of_device_id __initdata ps + }; + + static struct of_platform_driver psycho_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = psycho_match, + .probe = psycho_probe, +--- a/arch/sparc/kernel/pci_sabre.c ++++ b/arch/sparc/kernel/pci_sabre.c +@@ -596,6 +596,7 @@ static struct of_device_id __initdata sa + }; + + static struct of_platform_driver sabre_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = sabre_match, + .probe = sabre_probe, +--- a/arch/sparc/kernel/pci_schizo.c ++++ b/arch/sparc/kernel/pci_schizo.c +@@ -1491,6 +1491,7 @@ static struct of_device_id __initdata sc + }; + + static struct of_platform_driver schizo_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = schizo_match, + .probe = schizo_probe, +--- a/arch/sparc/kernel/pci_sun4v.c ++++ b/arch/sparc/kernel/pci_sun4v.c +@@ -1009,6 +1009,7 @@ static struct of_device_id __initdata pc + }; + + static struct of_platform_driver pci_sun4v_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = pci_sun4v_match, + .probe = pci_sun4v_probe, +--- a/arch/sparc/kernel/pmc.c ++++ b/arch/sparc/kernel/pmc.c +@@ -79,6 +79,7 @@ static struct of_device_id __initdata pm + MODULE_DEVICE_TABLE(of, pmc_match); + + static struct of_platform_driver pmc_driver = { ++ .owner = THIS_MODULE, + .name = "pmc", + .match_table = pmc_match, + .probe = pmc_probe, +--- a/arch/sparc/kernel/power.c ++++ b/arch/sparc/kernel/power.c +@@ -60,6 +60,7 @@ static struct of_device_id __initdata po + }; + + static struct of_platform_driver power_driver = { ++ .owner = THIS_MODULE, + .match_table = power_match, + .probe = power_probe, + .driver = { +--- a/arch/sparc/kernel/time_32.c ++++ b/arch/sparc/kernel/time_32.c +@@ -185,6 +185,7 @@ static struct of_device_id __initdata cl + }; + + static struct of_platform_driver clock_driver = { ++ .owner = THIS_MODULE, + .match_table = clock_match, + .probe = clock_probe, + .driver = { +--- a/arch/sparc/kernel/time_64.c ++++ b/arch/sparc/kernel/time_64.c +@@ -462,6 +462,7 @@ static struct of_device_id __initdata rt + }; + + static struct of_platform_driver rtc_driver = { ++ .owner = THIS_MODULE, + .match_table = rtc_match, + .probe = rtc_probe, + .driver = { +@@ -494,6 +495,7 @@ static struct of_device_id __initdata bq + }; + + static struct of_platform_driver bq4802_driver = { ++ .owner = THIS_MODULE, + .match_table = bq4802_match, + .probe = bq4802_probe, + .driver = { +@@ -557,6 +559,7 @@ static struct of_device_id __initdata mo + }; + + static struct of_platform_driver mostek_driver = { ++ .owner = THIS_MODULE, + .match_table = mostek_match, + .probe = mostek_probe, + .driver = { +--- a/drivers/ata/pata_of_platform.c ++++ b/drivers/ata/pata_of_platform.c +@@ -91,6 +91,7 @@ static struct of_device_id pata_of_platf + MODULE_DEVICE_TABLE(of, pata_of_platform_match); + + static struct of_platform_driver pata_of_platform_driver = { ++ .owner = THIS_MODULE, + .name = "pata_of_platform", + .match_table = pata_of_platform_match, + .probe = pata_of_platform_probe, +--- a/drivers/ata/sata_fsl.c ++++ b/drivers/ata/sata_fsl.c +@@ -1426,6 +1426,7 @@ static struct of_device_id fsl_sata_matc + MODULE_DEVICE_TABLE(of, fsl_sata_match); + + static struct of_platform_driver fsl_sata_driver = { ++ .owner = THIS_MODULE, + .name = "fsl-sata", + .match_table = fsl_sata_match, + .probe = sata_fsl_probe, +--- a/drivers/atm/fore200e.c ++++ b/drivers/atm/fore200e.c +@@ -2693,6 +2693,7 @@ static const struct of_device_id fore200 + MODULE_DEVICE_TABLE(of, fore200e_sba_match); + + static struct of_platform_driver fore200e_sba_driver = { ++ .owner = THIS_MODULE, + .name = "fore_200e", + .match_table = fore200e_sba_match, + .probe = fore200e_sba_probe, +--- a/drivers/char/hw_random/n2-drv.c ++++ b/drivers/char/hw_random/n2-drv.c +@@ -751,6 +751,7 @@ static const struct of_device_id n2rng_m + MODULE_DEVICE_TABLE(of, n2rng_match); + + static struct of_platform_driver n2rng_driver = { ++ .owner = THIS_MODULE, + .name = "n2rng", + .match_table = n2rng_match, + .probe = n2rng_probe, +--- a/drivers/char/hw_random/pasemi-rng.c ++++ b/drivers/char/hw_random/pasemi-rng.c +@@ -140,6 +140,7 @@ static struct of_device_id rng_match[] = + }; + + static struct of_platform_driver rng_driver = { ++ .owner = THIS_MODULE, + .name = "pasemi-rng", + .match_table = rng_match, + .probe = rng_probe, +--- a/drivers/char/ipmi/ipmi_si_intf.c ++++ b/drivers/char/ipmi/ipmi_si_intf.c +@@ -2555,6 +2555,7 @@ static struct of_device_id ipmi_match[] + }; + + static struct of_platform_driver ipmi_of_platform_driver = { ++ .owner = THIS_MODULE, + .name = "ipmi", + .match_table = ipmi_match, + .probe = ipmi_of_probe, +--- a/drivers/crypto/amcc/crypto4xx_core.c ++++ b/drivers/crypto/amcc/crypto4xx_core.c +@@ -1280,6 +1280,7 @@ static const struct of_device_id crypto4 + }; + + static struct of_platform_driver crypto4xx_driver = { ++ .owner = THIS_MODULE, + .name = "crypto4xx", + .match_table = crypto4xx_match, + .probe = crypto4xx_probe, +--- a/drivers/crypto/talitos.c ++++ b/drivers/crypto/talitos.c +@@ -1967,6 +1967,7 @@ static const struct of_device_id talitos + MODULE_DEVICE_TABLE(of, talitos_match); + + static struct of_platform_driver talitos_driver = { ++ .owner = THIS_MODULE, + .name = "talitos", + .match_table = talitos_match, + .probe = talitos_probe, +--- a/drivers/dma/fsldma.c ++++ b/drivers/dma/fsldma.c +@@ -1408,6 +1408,7 @@ static const struct of_device_id fsldma_ + }; + + static struct of_platform_driver fsldma_of_driver = { ++ .owner = THIS_MODULE, + .name = "fsl-elo-dma", + .match_table = fsldma_of_ids, + .probe = fsldma_of_probe, +--- a/drivers/hwmon/ultra45_env.c ++++ b/drivers/hwmon/ultra45_env.c +@@ -300,6 +300,7 @@ static const struct of_device_id env_mat + MODULE_DEVICE_TABLE(of, env_match); + + static struct of_platform_driver env_driver = { ++ .owner = THIS_MODULE, + .name = "ultra45_env", + .match_table = env_match, + .probe = env_probe, +--- a/drivers/i2c/busses/i2c-ibm_iic.c ++++ b/drivers/i2c/busses/i2c-ibm_iic.c +@@ -807,6 +807,7 @@ static const struct of_device_id ibm_iic + }; + + static struct of_platform_driver ibm_iic_driver = { ++ .owner = THIS_MODULE, + .name = "ibm-iic", + .match_table = ibm_iic_match, + .probe = iic_probe, +--- a/drivers/infiniband/hw/ehca/ehca_main.c ++++ b/drivers/infiniband/hw/ehca/ehca_main.c +@@ -937,6 +937,7 @@ MODULE_DEVICE_TABLE(of, ehca_device_tabl + + static struct of_platform_driver ehca_driver = { + .name = "ehca", ++ .owner = THIS_MODULE, + .match_table = ehca_device_table, + .probe = ehca_probe, + .remove = ehca_remove, +--- a/drivers/input/misc/sparcspkr.c ++++ b/drivers/input/misc/sparcspkr.c +@@ -258,6 +258,7 @@ static const struct of_device_id bbc_bee + }; + + static struct of_platform_driver bbc_beep_driver = { ++ .owner = THIS_MODULE, + .name = "bbcbeep", + .match_table = bbc_beep_match, + .probe = bbc_beep_probe, +@@ -337,6 +338,7 @@ static const struct of_device_id grover_ + }; + + static struct of_platform_driver grover_beep_driver = { ++ .owner = THIS_MODULE, + .name = "groverbeep", + .match_table = grover_beep_match, + .probe = grover_beep_probe, +--- a/drivers/input/serio/i8042-sparcio.h ++++ b/drivers/input/serio/i8042-sparcio.h +@@ -96,6 +96,7 @@ static const struct of_device_id sparc_i + MODULE_DEVICE_TABLE(of, sparc_i8042_match); + + static struct of_platform_driver sparc_i8042_driver = { ++ .owner = THIS_MODULE, + .name = "i8042", + .match_table = sparc_i8042_match, + .probe = sparc_i8042_probe, +--- a/drivers/input/serio/xilinx_ps2.c ++++ b/drivers/input/serio/xilinx_ps2.c +@@ -361,6 +361,7 @@ static const struct of_device_id xps2_of + MODULE_DEVICE_TABLE(of, xps2_of_match); + + static struct of_platform_driver xps2_of_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = xps2_of_match, + .probe = xps2_of_probe, +--- a/drivers/macintosh/smu.c ++++ b/drivers/macintosh/smu.c +@@ -670,6 +670,7 @@ static const struct of_device_id smu_pla + + static struct of_platform_driver smu_of_platform_driver = + { ++ .owner = THIS_MODULE, + .name = "smu", + .match_table = smu_platform_match, + .probe = smu_platform_probe, +--- a/drivers/macintosh/therm_pm72.c ++++ b/drivers/macintosh/therm_pm72.c +@@ -2239,6 +2239,7 @@ static const struct of_device_id fcu_mat + + static struct of_platform_driver fcu_of_platform_driver = + { ++ .owner = THIS_MODULE, + .name = "temperature", + .match_table = fcu_match, + .probe = fcu_of_probe, +--- a/drivers/macintosh/therm_windtunnel.c ++++ b/drivers/macintosh/therm_windtunnel.c +@@ -464,6 +464,7 @@ static const struct of_device_id therm_o + }; + + static struct of_platform_driver therm_of_driver = { ++ .owner = THIS_MODULE, + .name = "temperature", + .match_table = therm_of_match, + .probe = therm_of_probe, +--- a/drivers/mmc/host/sdhci-of-core.c ++++ b/drivers/mmc/host/sdhci-of-core.c +@@ -205,6 +205,7 @@ static const struct of_device_id sdhci_o + MODULE_DEVICE_TABLE(of, sdhci_of_match); + + static struct of_platform_driver sdhci_of_driver = { ++ .owner = THIS_MODULE, + .driver.name = "sdhci-of", + .match_table = sdhci_of_match, + .probe = sdhci_of_probe, +--- a/drivers/mtd/maps/physmap_of.c ++++ b/drivers/mtd/maps/physmap_of.c +@@ -374,6 +374,7 @@ static struct of_device_id of_flash_matc + MODULE_DEVICE_TABLE(of, of_flash_match); + + static struct of_platform_driver of_flash_driver = { ++ .owner = THIS_MODULE, + .name = "of-flash", + .match_table = of_flash_match, + .probe = of_flash_probe, +--- a/drivers/mtd/maps/sun_uflash.c ++++ b/drivers/mtd/maps/sun_uflash.c +@@ -148,6 +148,7 @@ static const struct of_device_id uflash_ + MODULE_DEVICE_TABLE(of, uflash_match); + + static struct of_platform_driver uflash_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = uflash_match, + .probe = uflash_probe, +--- a/drivers/mtd/nand/fsl_elbc_nand.c ++++ b/drivers/mtd/nand/fsl_elbc_nand.c +@@ -1079,6 +1079,7 @@ static const struct of_device_id fsl_elb + static struct of_platform_driver fsl_elbc_ctrl_driver = { + .driver = { + .name = "fsl-elbc", ++ .owner = THIS_MODULE, + }, + .match_table = fsl_elbc_match, + .probe = fsl_elbc_ctrl_probe, +--- a/drivers/mtd/nand/fsl_upm.c ++++ b/drivers/mtd/nand/fsl_upm.c +@@ -356,6 +356,7 @@ static struct of_device_id of_fun_match[ + MODULE_DEVICE_TABLE(of, of_fun_match); + + static struct of_platform_driver of_fun_driver = { ++ .owner = THIS_MODULE, + .name = "fsl,upm-nand", + .match_table = of_fun_match, + .probe = fun_probe, +--- a/drivers/mtd/nand/ndfc.c ++++ b/drivers/mtd/nand/ndfc.c +@@ -292,6 +292,7 @@ static const struct of_device_id ndfc_ma + MODULE_DEVICE_TABLE(of, ndfc_match); + + static struct of_platform_driver ndfc_driver = { ++ .owner = THIS_MODULE, + .driver = { + .name = "ndfc", + }, +--- a/drivers/mtd/nand/pasemi_nand.c ++++ b/drivers/mtd/nand/pasemi_nand.c +@@ -221,6 +221,7 @@ MODULE_DEVICE_TABLE(of, pasemi_nand_matc + + static struct of_platform_driver pasemi_nand_driver = + { ++ .owner = THIS_MODULE, + .name = (char*)driver_name, + .match_table = pasemi_nand_match, + .probe = pasemi_nand_probe, +--- a/drivers/mtd/nand/socrates_nand.c ++++ b/drivers/mtd/nand/socrates_nand.c +@@ -301,6 +301,7 @@ static struct of_device_id socrates_nand + MODULE_DEVICE_TABLE(of, socrates_nand_match); + + static struct of_platform_driver socrates_nand_driver = { ++ .owner = THIS_MODULE, + .name = "socrates_nand", + .match_table = socrates_nand_match, + .probe = socrates_nand_probe, +--- a/drivers/net/ehea/ehea_main.c ++++ b/drivers/net/ehea/ehea_main.c +@@ -122,6 +122,7 @@ MODULE_DEVICE_TABLE(of, ehea_device_tabl + + static struct of_platform_driver ehea_driver = { + .name = "ehea", ++ .owner = THIS_MODULE, + .match_table = ehea_device_table, + .probe = ehea_probe_adapter, + .remove = ehea_remove, +--- a/drivers/net/fec_mpc52xx_phy.c ++++ b/drivers/net/fec_mpc52xx_phy.c +@@ -158,6 +158,7 @@ static struct of_device_id mpc52xx_fec_m + MODULE_DEVICE_TABLE(of, mpc52xx_fec_mdio_match); + + struct of_platform_driver mpc52xx_fec_mdio_driver = { ++ .owner = THIS_MODULE, + .name = "mpc5200b-fec-phy", + .probe = mpc52xx_fec_mdio_probe, + .remove = mpc52xx_fec_mdio_remove, +--- a/drivers/net/fs_enet/fs_enet-main.c ++++ b/drivers/net/fs_enet/fs_enet-main.c +@@ -1158,6 +1158,7 @@ static struct of_device_id fs_enet_match + MODULE_DEVICE_TABLE(of, fs_enet_match); + + static struct of_platform_driver fs_enet_driver = { ++ .owner = THIS_MODULE, + .name = "fs_enet", + .match_table = fs_enet_match, + .probe = fs_enet_probe, +--- a/drivers/net/fs_enet/mii-bitbang.c ++++ b/drivers/net/fs_enet/mii-bitbang.c +@@ -224,6 +224,7 @@ static struct of_device_id fs_enet_mdio_ + MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match); + + static struct of_platform_driver fs_enet_bb_mdio_driver = { ++ .owner = THIS_MODULE, + .name = "fsl-bb-mdio", + .match_table = fs_enet_mdio_bb_match, + .probe = fs_enet_mdio_probe, +--- a/drivers/net/fs_enet/mii-fec.c ++++ b/drivers/net/fs_enet/mii-fec.c +@@ -222,6 +222,7 @@ static struct of_device_id fs_enet_mdio_ + MODULE_DEVICE_TABLE(of, fs_enet_mdio_fec_match); + + static struct of_platform_driver fs_enet_fec_mdio_driver = { ++ .owner = THIS_MODULE, + .name = "fsl-fec-mdio", + .match_table = fs_enet_mdio_fec_match, + .probe = fs_enet_mdio_probe, +--- a/drivers/net/fsl_pq_mdio.c ++++ b/drivers/net/fsl_pq_mdio.c +@@ -461,6 +461,7 @@ static struct of_device_id fsl_pq_mdio_m + MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match); + + static struct of_platform_driver fsl_pq_mdio_driver = { ++ .owner = THIS_MODULE, + .name = "fsl-pq_mdio", + .probe = fsl_pq_mdio_probe, + .remove = fsl_pq_mdio_remove, +--- a/drivers/net/gianfar.c ++++ b/drivers/net/gianfar.c +@@ -3053,6 +3053,7 @@ MODULE_DEVICE_TABLE(of, gfar_match); + + /* Structure for a device driver */ + static struct of_platform_driver gfar_driver = { ++ .owner = THIS_MODULE, + .name = "fsl-gianfar", + .match_table = gfar_match, + +--- a/drivers/net/ibm_newemac/core.c ++++ b/drivers/net/ibm_newemac/core.c +@@ -2994,6 +2994,7 @@ static struct of_device_id emac_match[] + MODULE_DEVICE_TABLE(of, emac_match); + + static struct of_platform_driver emac_driver = { ++ .owner = THIS_MODULE, + .name = "emac", + .match_table = emac_match, + +--- a/drivers/net/ibm_newemac/mal.c ++++ b/drivers/net/ibm_newemac/mal.c +@@ -789,6 +789,7 @@ static struct of_device_id mal_platform_ + }; + + static struct of_platform_driver mal_of_driver = { ++ .owner = THIS_MODULE, + .name = "mcmal", + .match_table = mal_platform_match, + +--- a/drivers/net/ibm_newemac/rgmii.c ++++ b/drivers/net/ibm_newemac/rgmii.c +@@ -318,6 +318,7 @@ static struct of_device_id rgmii_match[] + }; + + static struct of_platform_driver rgmii_driver = { ++ .owner = THIS_MODULE, + .name = "emac-rgmii", + .match_table = rgmii_match, + +--- a/drivers/net/ibm_newemac/tah.c ++++ b/drivers/net/ibm_newemac/tah.c +@@ -165,6 +165,7 @@ static struct of_device_id tah_match[] = + }; + + static struct of_platform_driver tah_driver = { ++ .owner = THIS_MODULE, + .name = "emac-tah", + .match_table = tah_match, + +--- a/drivers/net/ibm_newemac/zmii.c ++++ b/drivers/net/ibm_newemac/zmii.c +@@ -311,6 +311,7 @@ static struct of_device_id zmii_match[] + }; + + static struct of_platform_driver zmii_driver = { ++ .owner = THIS_MODULE, + .name = "emac-zmii", + .match_table = zmii_match, + +--- a/drivers/net/myri_sbus.c ++++ b/drivers/net/myri_sbus.c +@@ -1161,6 +1161,7 @@ static const struct of_device_id myri_sb + MODULE_DEVICE_TABLE(of, myri_sbus_match); + + static struct of_platform_driver myri_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "myri", + .match_table = myri_sbus_match, + .probe = myri_sbus_probe, +--- a/drivers/net/niu.c ++++ b/drivers/net/niu.c +@@ -10206,6 +10206,7 @@ static const struct of_device_id niu_mat + MODULE_DEVICE_TABLE(of, niu_match); + + static struct of_platform_driver niu_of_driver = { ++ .owner = THIS_MODULE, + .name = "niu", + .match_table = niu_match, + .probe = niu_of_probe, +--- a/drivers/net/phy/mdio-gpio.c ++++ b/drivers/net/phy/mdio-gpio.c +@@ -241,6 +241,7 @@ static struct of_device_id mdio_ofgpio_m + MODULE_DEVICE_TABLE(of, mdio_ofgpio_match); + + static struct of_platform_driver mdio_ofgpio_driver = { ++ .owner = THIS_MODULE, + .name = "mdio-gpio", + .match_table = mdio_ofgpio_match, + .probe = mdio_ofgpio_probe, +--- a/drivers/net/sunbmac.c ++++ b/drivers/net/sunbmac.c +@@ -1292,6 +1292,7 @@ static const struct of_device_id bigmac_ + MODULE_DEVICE_TABLE(of, bigmac_sbus_match); + + static struct of_platform_driver bigmac_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "sunbmac", + .match_table = bigmac_sbus_match, + .probe = bigmac_sbus_probe, +--- a/drivers/net/sunhme.c ++++ b/drivers/net/sunhme.c +@@ -3295,6 +3295,7 @@ static const struct of_device_id hme_sbu + MODULE_DEVICE_TABLE(of, hme_sbus_match); + + static struct of_platform_driver hme_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "hme", + .match_table = hme_sbus_match, + .probe = hme_sbus_probe, +--- a/drivers/net/sunlance.c ++++ b/drivers/net/sunlance.c +@@ -1546,6 +1546,7 @@ static const struct of_device_id sunlanc + MODULE_DEVICE_TABLE(of, sunlance_sbus_match); + + static struct of_platform_driver sunlance_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "sunlance", + .match_table = sunlance_sbus_match, + .probe = sunlance_sbus_probe, +--- a/drivers/net/sunqe.c ++++ b/drivers/net/sunqe.c +@@ -978,6 +978,7 @@ static const struct of_device_id qec_sbu + MODULE_DEVICE_TABLE(of, qec_sbus_match); + + static struct of_platform_driver qec_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "qec", + .match_table = qec_sbus_match, + .probe = qec_sbus_probe, +--- a/drivers/net/ucc_geth.c ++++ b/drivers/net/ucc_geth.c +@@ -3965,6 +3965,7 @@ static struct of_device_id ucc_geth_matc + MODULE_DEVICE_TABLE(of, ucc_geth_match); + + static struct of_platform_driver ucc_geth_driver = { ++ .owner = THIS_MODULE, + .name = DRV_NAME, + .match_table = ucc_geth_match, + .probe = ucc_geth_probe, +--- a/drivers/parport/parport_sunbpp.c ++++ b/drivers/parport/parport_sunbpp.c +@@ -382,6 +382,7 @@ static const struct of_device_id bpp_mat + MODULE_DEVICE_TABLE(of, bpp_match); + + static struct of_platform_driver bpp_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "bpp", + .match_table = bpp_match, + .probe = bpp_probe, +--- a/drivers/pcmcia/electra_cf.c ++++ b/drivers/pcmcia/electra_cf.c +@@ -356,6 +356,7 @@ static const struct of_device_id electra + MODULE_DEVICE_TABLE(of, electra_cf_match); + + static struct of_platform_driver electra_cf_driver = { ++ .owner = THIS_MODULE, + .name = (char *)driver_name, + .match_table = electra_cf_match, + .probe = electra_cf_probe, +--- a/drivers/pcmcia/m8xx_pcmcia.c ++++ b/drivers/pcmcia/m8xx_pcmcia.c +@@ -1314,6 +1314,7 @@ static const struct of_device_id m8xx_pc + MODULE_DEVICE_TABLE(of, m8xx_pcmcia_match); + + static struct of_platform_driver m8xx_pcmcia_driver = { ++ .owner = THIS_MODULE, + .name = driver_name, + .match_table = m8xx_pcmcia_match, + .probe = m8xx_probe, +--- a/drivers/sbus/char/bbc_i2c.c ++++ b/drivers/sbus/char/bbc_i2c.c +@@ -414,6 +414,7 @@ static const struct of_device_id bbc_i2c + MODULE_DEVICE_TABLE(of, bbc_i2c_match); + + static struct of_platform_driver bbc_i2c_driver = { ++ .owner = THIS_MODULE, + .name = "bbc_i2c", + .match_table = bbc_i2c_match, + .probe = bbc_i2c_probe, +--- a/drivers/sbus/char/display7seg.c ++++ b/drivers/sbus/char/display7seg.c +@@ -265,6 +265,7 @@ static const struct of_device_id d7s_mat + MODULE_DEVICE_TABLE(of, d7s_match); + + static struct of_platform_driver d7s_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = d7s_match, + .probe = d7s_probe, +--- a/drivers/sbus/char/envctrl.c ++++ b/drivers/sbus/char/envctrl.c +@@ -1130,6 +1130,7 @@ static const struct of_device_id envctrl + MODULE_DEVICE_TABLE(of, envctrl_match); + + static struct of_platform_driver envctrl_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = envctrl_match, + .probe = envctrl_probe, +--- a/drivers/sbus/char/flash.c ++++ b/drivers/sbus/char/flash.c +@@ -208,6 +208,7 @@ static const struct of_device_id flash_m + MODULE_DEVICE_TABLE(of, flash_match); + + static struct of_platform_driver flash_driver = { ++ .owner = THIS_MODULE, + .name = "flash", + .match_table = flash_match, + .probe = flash_probe, +--- a/drivers/sbus/char/uctrl.c ++++ b/drivers/sbus/char/uctrl.c +@@ -425,6 +425,7 @@ static const struct of_device_id uctrl_m + MODULE_DEVICE_TABLE(of, uctrl_match); + + static struct of_platform_driver uctrl_driver = { ++ .owner = THIS_MODULE, + .name = "uctrl", + .match_table = uctrl_match, + .probe = uctrl_probe, +--- a/drivers/scsi/qlogicpti.c ++++ b/drivers/scsi/qlogicpti.c +@@ -1456,6 +1456,7 @@ static const struct of_device_id qpti_ma + MODULE_DEVICE_TABLE(of, qpti_match); + + static struct of_platform_driver qpti_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "qpti", + .match_table = qpti_match, + .probe = qpti_sbus_probe, +--- a/drivers/scsi/sun_esp.c ++++ b/drivers/scsi/sun_esp.c +@@ -632,6 +632,7 @@ static const struct of_device_id esp_mat + MODULE_DEVICE_TABLE(of, esp_match); + + static struct of_platform_driver esp_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "esp", + .match_table = esp_match, + .probe = esp_sbus_probe, +--- a/drivers/serial/cpm_uart/cpm_uart_core.c ++++ b/drivers/serial/cpm_uart/cpm_uart_core.c +@@ -1372,6 +1372,7 @@ static struct of_device_id cpm_uart_matc + }; + + static struct of_platform_driver cpm_uart_driver = { ++ .owner = THIS_MODULE, + .name = "cpm_uart", + .match_table = cpm_uart_match, + .probe = cpm_uart_probe, +--- a/drivers/serial/mpc52xx_uart.c ++++ b/drivers/serial/mpc52xx_uart.c +@@ -1464,6 +1464,7 @@ mpc52xx_uart_of_enumerate(void) + MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match); + + static struct of_platform_driver mpc52xx_uart_of_driver = { ++ .owner = THIS_MODULE, + .match_table = mpc52xx_uart_of_match, + .probe = mpc52xx_uart_of_probe, + .remove = mpc52xx_uart_of_remove, +--- a/drivers/serial/sunhv.c ++++ b/drivers/serial/sunhv.c +@@ -630,6 +630,7 @@ static const struct of_device_id hv_matc + MODULE_DEVICE_TABLE(of, hv_match); + + static struct of_platform_driver hv_driver = { ++ .owner = THIS_MODULE, + .name = "hv", + .match_table = hv_match, + .probe = hv_probe, +--- a/drivers/serial/sunsab.c ++++ b/drivers/serial/sunsab.c +@@ -1093,6 +1093,7 @@ static const struct of_device_id sab_mat + MODULE_DEVICE_TABLE(of, sab_match); + + static struct of_platform_driver sab_driver = { ++ .owner = THIS_MODULE, + .name = "sab", + .match_table = sab_match, + .probe = sab_probe, +--- a/drivers/serial/sunsu.c ++++ b/drivers/serial/sunsu.c +@@ -1536,6 +1536,7 @@ static const struct of_device_id su_matc + MODULE_DEVICE_TABLE(of, su_match); + + static struct of_platform_driver su_driver = { ++ .owner = THIS_MODULE, + .name = "su", + .match_table = su_match, + .probe = su_probe, +--- a/drivers/serial/sunzilog.c ++++ b/drivers/serial/sunzilog.c +@@ -1491,6 +1491,7 @@ static const struct of_device_id zs_matc + MODULE_DEVICE_TABLE(of, zs_match); + + static struct of_platform_driver zs_driver = { ++ .owner = THIS_MODULE, + .name = "zs", + .match_table = zs_match, + .probe = zs_probe, +--- a/drivers/spi/spi_mpc8xxx.c ++++ b/drivers/spi/spi_mpc8xxx.c +@@ -1311,6 +1311,7 @@ static const struct of_device_id of_mpc8 + MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match); + + static struct of_platform_driver of_mpc8xxx_spi_driver = { ++ .owner = THIS_MODULE, + .name = "mpc8xxx_spi", + .match_table = of_mpc8xxx_spi_match, + .probe = of_mpc8xxx_spi_probe, +--- a/drivers/usb/gadget/fsl_qe_udc.c ++++ b/drivers/usb/gadget/fsl_qe_udc.c +@@ -2768,6 +2768,7 @@ static const struct of_device_id qe_udc_ + MODULE_DEVICE_TABLE(of, qe_udc_match); + + static struct of_platform_driver udc_driver = { ++ .owner = THIS_MODULE, + .name = (char *)driver_name, + .match_table = qe_udc_match, + .probe = qe_udc_probe, +--- a/drivers/usb/host/fhci-hcd.c ++++ b/drivers/usb/host/fhci-hcd.c +@@ -812,6 +812,7 @@ static const struct of_device_id of_fhci + MODULE_DEVICE_TABLE(of, of_fhci_match); + + static struct of_platform_driver of_fhci_driver = { ++ .owner = THIS_MODULE, + .name = "fsl,usb-fhci", + .match_table = of_fhci_match, + .probe = of_fhci_probe, +--- a/drivers/usb/host/isp1760-if.c ++++ b/drivers/usb/host/isp1760-if.c +@@ -121,6 +121,7 @@ static const struct of_device_id of_isp1 + MODULE_DEVICE_TABLE(of, of_isp1760_match); + + static struct of_platform_driver isp1760_of_driver = { ++ .owner = THIS_MODULE, + .name = "nxp-isp1760", + .match_table = of_isp1760_match, + .probe = of_isp1760_probe, +--- a/drivers/video/bw2.c ++++ b/drivers/video/bw2.c +@@ -377,6 +377,7 @@ static const struct of_device_id bw2_mat + MODULE_DEVICE_TABLE(of, bw2_match); + + static struct of_platform_driver bw2_driver = { ++ .owner = THIS_MODULE, + .name = "bw2", + .match_table = bw2_match, + .probe = bw2_probe, +--- a/drivers/video/cg14.c ++++ b/drivers/video/cg14.c +@@ -597,6 +597,7 @@ static const struct of_device_id cg14_ma + MODULE_DEVICE_TABLE(of, cg14_match); + + static struct of_platform_driver cg14_driver = { ++ .owner = THIS_MODULE, + .name = "cg14", + .match_table = cg14_match, + .probe = cg14_probe, +--- a/drivers/video/cg3.c ++++ b/drivers/video/cg3.c +@@ -464,6 +464,7 @@ static const struct of_device_id cg3_mat + MODULE_DEVICE_TABLE(of, cg3_match); + + static struct of_platform_driver cg3_driver = { ++ .owner = THIS_MODULE, + .name = "cg3", + .match_table = cg3_match, + .probe = cg3_probe, +--- a/drivers/video/cg6.c ++++ b/drivers/video/cg6.c +@@ -857,6 +857,7 @@ static const struct of_device_id cg6_mat + MODULE_DEVICE_TABLE(of, cg6_match); + + static struct of_platform_driver cg6_driver = { ++ .owner = THIS_MODULE, + .name = "cg6", + .match_table = cg6_match, + .probe = cg6_probe, +--- a/drivers/video/ffb.c ++++ b/drivers/video/ffb.c +@@ -1054,6 +1054,7 @@ static const struct of_device_id ffb_mat + MODULE_DEVICE_TABLE(of, ffb_match); + + static struct of_platform_driver ffb_driver = { ++ .owner = THIS_MODULE, + .name = "ffb", + .match_table = ffb_match, + .probe = ffb_probe, +--- a/drivers/video/leo.c ++++ b/drivers/video/leo.c +@@ -664,6 +664,7 @@ static const struct of_device_id leo_mat + MODULE_DEVICE_TABLE(of, leo_match); + + static struct of_platform_driver leo_driver = { ++ .owner = THIS_MODULE, + .name = "leo", + .match_table = leo_match, + .probe = leo_probe, +--- a/drivers/video/p9100.c ++++ b/drivers/video/p9100.c +@@ -354,6 +354,7 @@ static const struct of_device_id p9100_m + MODULE_DEVICE_TABLE(of, p9100_match); + + static struct of_platform_driver p9100_driver = { ++ .owner = THIS_MODULE, + .name = "p9100", + .match_table = p9100_match, + .probe = p9100_probe, +--- a/drivers/video/platinumfb.c ++++ b/drivers/video/platinumfb.c +@@ -680,6 +680,7 @@ static struct of_device_id platinumfb_ma + + static struct of_platform_driver platinum_driver = + { ++ .owner = THIS_MODULE, + .name = "platinumfb", + .match_table = platinumfb_match, + .probe = platinumfb_probe, +--- a/drivers/video/tcx.c ++++ b/drivers/video/tcx.c +@@ -513,6 +513,7 @@ static const struct of_device_id tcx_mat + MODULE_DEVICE_TABLE(of, tcx_match); + + static struct of_platform_driver tcx_driver = { ++ .owner = THIS_MODULE, + .name = "tcx", + .match_table = tcx_match, + .probe = tcx_probe, +--- a/drivers/watchdog/cpwd.c ++++ b/drivers/watchdog/cpwd.c +@@ -676,6 +676,7 @@ static const struct of_device_id cpwd_ma + MODULE_DEVICE_TABLE(of, cpwd_match); + + static struct of_platform_driver cpwd_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = cpwd_match, + .probe = cpwd_probe, +--- a/drivers/watchdog/riowd.c ++++ b/drivers/watchdog/riowd.c +@@ -238,6 +238,7 @@ static const struct of_device_id riowd_m + MODULE_DEVICE_TABLE(of, riowd_match); + + static struct of_platform_driver riowd_driver = { ++ .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = riowd_match, + .probe = riowd_probe, +--- a/sound/sparc/amd7930.c ++++ b/sound/sparc/amd7930.c +@@ -1065,6 +1065,7 @@ static const struct of_device_id amd7930 + }; + + static struct of_platform_driver amd7930_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "audio", + .match_table = amd7930_match, + .probe = amd7930_sbus_probe, +--- a/sound/sparc/cs4231.c ++++ b/sound/sparc/cs4231.c +@@ -2110,6 +2110,7 @@ static const struct of_device_id cs4231_ + MODULE_DEVICE_TABLE(of, cs4231_match); + + static struct of_platform_driver cs4231_driver = { ++ .owner = THIS_MODULE, + .name = "audio", + .match_table = cs4231_match, + .probe = cs4231_probe, +--- a/sound/sparc/dbri.c ++++ b/sound/sparc/dbri.c +@@ -2686,6 +2686,7 @@ static const struct of_device_id dbri_ma + MODULE_DEVICE_TABLE(of, dbri_match); + + static struct of_platform_driver dbri_sbus_driver = { ++ .owner = THIS_MODULE, + .name = "dbri", + .match_table = dbri_match, + .probe = dbri_probe, diff --git a/patches.suse/osync-error b/patches.suse/osync-error new file mode 100644 index 0000000..6863eef --- /dev/null +++ b/patches.suse/osync-error @@ -0,0 +1,49 @@ +From: mason@suse.de +Subject: make sure O_SYNC writes properly return -EIO +References: bnc#58622 + +Make sure to honor the error status of synchronous writeback during +O_SYNC writes + +Acked-by: Jeff Mahoney + +--- + mm/filemap.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -2325,7 +2325,7 @@ generic_file_buffered_write(struct kiocb + + if (likely(status >= 0)) { + written += status; +- *ppos = pos + status; ++ pos += status; + + /* + * For now, when the user asks for O_SYNC, we'll actually give +@@ -2343,10 +2343,23 @@ generic_file_buffered_write(struct kiocb + * to buffered writes (block instantiation inside i_size). So we sync + * the file data here, to try to honour O_DIRECT expectations. + */ +- if (unlikely(file->f_flags & O_DIRECT) && written) ++ if (unlikely(file->f_flags & O_DIRECT) && status >= 0 && written) + status = filemap_write_and_wait_range(mapping, + pos, pos + written - 1); + ++ /* ++ * We must let know userspace if something hasn't been written ++ * correctly. If we got an I/O error it means we got an hardware ++ * failure, anything can be happening to the on-disk data, ++ * letting know userspace that a bit of data might have been ++ * written correctly on disk is a very low priority, compared ++ * to letting know userspace that some data has _not_ been ++ * written at all. ++ */ ++ if (unlikely(status == -EIO)) ++ return status; ++ *ppos = pos; ++ + return written ? written : status; + } + EXPORT_SYMBOL(generic_file_buffered_write); diff --git a/patches.suse/panic-on-io-nmi-SLE11-user-space-api.patch b/patches.suse/panic-on-io-nmi-SLE11-user-space-api.patch new file mode 100644 index 0000000..140370c --- /dev/null +++ b/patches.suse/panic-on-io-nmi-SLE11-user-space-api.patch @@ -0,0 +1,47 @@ +From: Kurt Garloff +Subject: API fix: [PATCH] X86: sysctl to allow panic on IOCK NMI error +References: bnc#427979 +Patch-mainline: never + +Part of patches.suse/panic-on-io-nmi.diff from SLE11 to keep stable user +space API. The rest is upstream as commit 5211a242. + +Signed-off-by: Jiri Benc + +--- + include/linux/sysctl.h | 1 + + kernel/sysctl.c | 2 +- + kernel/sysctl_check.c | 1 + + 3 files changed, 3 insertions(+), 1 deletion(-) + +--- a/include/linux/sysctl.h ++++ b/include/linux/sysctl.h +@@ -162,6 +162,7 @@ enum + KERN_MAX_LOCK_DEPTH=74, + KERN_NMI_WATCHDOG=75, /* int: enable/disable nmi watchdog */ + KERN_PANIC_ON_NMI=76, /* int: whether we will panic on an unrecovered */ ++ KERN_PANIC_ON_IO_NMI=79, /* int: whether we will panic on an io NMI */ + }; + + +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -808,7 +808,7 @@ static struct ctl_table kern_table[] = { + .proc_handler = &proc_dointvec, + }, + { +- .ctl_name = CTL_UNNUMBERED, ++ .ctl_name = KERN_PANIC_ON_IO_NMI, + .procname = "panic_on_io_nmi", + .data = &panic_on_io_nmi, + .maxlen = sizeof(int), +--- a/kernel/sysctl_check.c ++++ b/kernel/sysctl_check.c +@@ -104,6 +104,7 @@ static const struct trans_ctl_table tran + { KERN_MAX_LOCK_DEPTH, "max_lock_depth" }, + { KERN_NMI_WATCHDOG, "nmi_watchdog" }, + { KERN_PANIC_ON_NMI, "panic_on_unrecovered_nmi" }, ++ { KERN_PANIC_ON_IO_NMI, "panic_on_io_nmi" }, + { KERN_SETUID_DUMPABLE, "suid_dumpable" }, + {} + }; diff --git a/patches.suse/parser-match_string.diff b/patches.suse/parser-match_string.diff new file mode 100644 index 0000000..82d779e --- /dev/null +++ b/patches.suse/parser-match_string.diff @@ -0,0 +1,55 @@ +From: Andreas Gruenbacher +Subject: Add match_string() for mount option parsing +References: FATE301275 +Patch-mainline: no + +The match_string() function allows to parse string constants in +mount options. + +Signed-off-by: Andreas Gruenbacher + +--- + include/linux/parser.h | 1 + + lib/parser.c | 14 ++++++++++++++ + 2 files changed, 15 insertions(+) + +--- a/include/linux/parser.h ++++ b/include/linux/parser.h +@@ -26,6 +26,7 @@ typedef struct { + } substring_t; + + int match_token(char *, const match_table_t table, substring_t args[]); ++int match_string(substring_t *s, const char *str); + int match_int(substring_t *, int *result); + int match_octal(substring_t *, int *result); + int match_hex(substring_t *, int *result); +--- a/lib/parser.c ++++ b/lib/parser.c +@@ -114,6 +114,19 @@ int match_token(char *s, const match_tab + } + + /** ++ * match_string: check for a particular parameter ++ * @s: substring to be scanned ++ * @str: string to scan for ++ * ++ * Description: Return if a &substring_t is equal to string @str. ++ */ ++int match_string(substring_t *s, const char *str) ++{ ++ return strlen(str) == s->to - s->from && ++ !memcmp(str, s->from, s->to - s->from); ++} ++ ++/** + * match_number: scan a number in the given base from a substring_t + * @s: substring to be scanned + * @result: resulting integer on success +@@ -224,6 +237,7 @@ char *match_strdup(const substring_t *s) + } + + EXPORT_SYMBOL(match_token); ++EXPORT_SYMBOL(match_string); + EXPORT_SYMBOL(match_int); + EXPORT_SYMBOL(match_octal); + EXPORT_SYMBOL(match_hex); diff --git a/patches.suse/ppc-no-LDFLAGS_MODULE.patch b/patches.suse/ppc-no-LDFLAGS_MODULE.patch new file mode 100644 index 0000000..e14c583 --- /dev/null +++ b/patches.suse/ppc-no-LDFLAGS_MODULE.patch @@ -0,0 +1,32 @@ +Subject: arch/powerpc/lib/crtsavres.o is not available when linking external modules +From: olh@suse.de +Patch-mainline: never + +Maybe it helps. + +--- + arch/powerpc/Makefile | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/arch/powerpc/Makefile ++++ b/arch/powerpc/Makefile +@@ -85,15 +85,17 @@ ifeq ($(GCC_BROKEN_VEC),y) + KBUILD_CFLAGS += $(call cc-option,-mcpu=970) + else + KBUILD_CFLAGS += $(call cc-option,-mcpu=power4) ++# GCC_BROKEN_VEC + endif + else + KBUILD_CFLAGS += $(call cc-option,-mcpu=power4) ++# CONFIG_ALTIVEC + endif + else + KBUILD_CFLAGS += $(call cc-option,-mtune=power4) ++# CONFIG_POWER4_ONLY + endif +-else +-LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o ++# CONFIG_PPC64 + endif + + ifeq ($(CONFIG_TUNE_CELL),y) diff --git a/patches.suse/ppc-powerbook-usb-fn-key-default.patch b/patches.suse/ppc-powerbook-usb-fn-key-default.patch new file mode 100644 index 0000000..10bc083 --- /dev/null +++ b/patches.suse/ppc-powerbook-usb-fn-key-default.patch @@ -0,0 +1,32 @@ +Subject: Default value of usbhid.pb_fnmode module parameter +From: olh@suse.de +References: 220266 +Patch-mainline: not yet + + The kernel default value for usbhid.pb_fnmode is 1, which means that pressing + the Fn keys (F1..F10) without the fn key triggers the special functions + decrease/increase brightness, mute, decrease/increase volume, etc., which is + the default under MacOS. + + At least under 10.2 Beta2, only the volume related special functions work at + all. In addition, Ctrl-Alt-Fx is used to switch between consoles. with + pb_fnmode==1, the fn key needs to be pressed in addition. + + Therefore, pb_fnmode==2 (F1..F10 by default trigger Fn rather than the special + functions) makes more sense under Linux. + + + drivers/hid/hid-apple.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/hid/hid-apple.c ++++ b/drivers/hid/hid-apple.c +@@ -35,7 +35,7 @@ + + #define APPLE_FLAG_FKEY 0x01 + +-static unsigned int fnmode = 1; ++static unsigned int fnmode = 2; + module_param(fnmode, uint, 0644); + MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, " + "[1] = fkeyslast, 2 = fkeysfirst)"); diff --git a/patches.suse/radeon-monitor-jsxx-quirk.patch b/patches.suse/radeon-monitor-jsxx-quirk.patch new file mode 100644 index 0000000..cb3533d --- /dev/null +++ b/patches.suse/radeon-monitor-jsxx-quirk.patch @@ -0,0 +1,65 @@ +Subject: [PATCH] Add quirk for the graphics adapter in some JSxx +From: Tony Breeds +References: 461002 - LTC50817 +Patch-mainline: not yet + +These devices are set to 640x480 by firmware, switch them to +800x600@60. + +Signed-off-by: Tony Breeds +Signed-off-by: Olaf Hering +--- + drivers/video/aty/radeon_monitor.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +--- a/drivers/video/aty/radeon_monitor.c ++++ b/drivers/video/aty/radeon_monitor.c +@@ -727,6 +727,25 @@ static void radeon_videomode_to_var(stru + var->vmode = mode->vmode; + } + ++#ifdef CONFIG_PPC_PSERIES ++static int is_powerblade(const char *model) ++{ ++ struct device_node *root; ++ const char* cp; ++ int len, l, rc = 0; ++ ++ root = of_find_node_by_path("/"); ++ if (root && model) { ++ l = strlen(model); ++ cp = of_get_property(root, "model", &len); ++ if (cp) ++ rc = memcmp(model, cp, min(len, l)) == 0; ++ of_node_put(root); ++ } ++ return rc; ++} ++#endif ++ + /* + * Build the modedb for head 1 (head 2 will come later), check panel infos + * from either BIOS or EDID, and pick up the default mode +@@ -862,6 +881,22 @@ void __devinit radeon_check_modes(struct + has_default_mode = 1; + } + ++#ifdef CONFIG_PPC_PSERIES ++ if (!has_default_mode && ( ++ is_powerblade("IBM,8842") || /* JS20 */ ++ is_powerblade("IBM,8844") || /* JS21 */ ++ is_powerblade("IBM,7998") || /* JS12/JS21/JS22 */ ++ is_powerblade("IBM,0792") || /* QS21 */ ++ is_powerblade("IBM,0793") /* QS22 */ ++ )) { ++ printk("Falling back to 800x600 on JSxx hardware\n"); ++ if (fb_find_mode(&info->var, info, "800x600@60", ++ info->monspecs.modedb, ++ info->monspecs.modedb_len, NULL, 8) != 0) ++ has_default_mode = 1; ++ } ++#endif ++ + /* + * Still no mode, let's pick up a default from the db + */ diff --git a/patches.suse/raw_device_max_minors_param.diff b/patches.suse/raw_device_max_minors_param.diff new file mode 100644 index 0000000..9b66773 --- /dev/null +++ b/patches.suse/raw_device_max_minors_param.diff @@ -0,0 +1,112 @@ +From: Jan Kara +Subject: Allow setting of number of raw devices as a module parameter +References: FATE 302178 +Patch-mainline: never + +Allow setting of maximal number of raw devices as a module parameter. This requires +changing of static array into a vmalloced one (the array is going to be too large +for kmalloc). + +Signed-off-by: Jan Kara + +--- + drivers/char/Kconfig | 2 +- + drivers/char/raw.c | 33 +++++++++++++++++++++++++++------ + 2 files changed, 28 insertions(+), 7 deletions(-) + +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -1036,7 +1036,7 @@ config RAW_DRIVER + with the O_DIRECT flag. + + config MAX_RAW_DEVS +- int "Maximum number of RAW devices to support (1-8192)" ++ int "Maximum number of RAW devices to support (1-65536)" + depends on RAW_DRIVER + default "256" + help +--- a/drivers/char/raw.c ++++ b/drivers/char/raw.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include + +@@ -29,10 +30,15 @@ struct raw_device_data { + }; + + static struct class *raw_class; +-static struct raw_device_data raw_devices[MAX_RAW_MINORS]; ++static struct raw_device_data *raw_devices; + static DEFINE_MUTEX(raw_mutex); + static const struct file_operations raw_ctl_fops; /* forward declaration */ + ++static int max_raw_minors = MAX_RAW_MINORS; ++ ++module_param(max_raw_minors, int, 0); ++MODULE_PARM_DESC(max_raw_minors, "Maximum number of raw devices (1-65536)"); ++ + /* + * Open/close code for raw IO. + * +@@ -158,7 +164,7 @@ static int raw_ctl_ioctl(struct inode *i + goto out; + } + +- if (rq.raw_minor <= 0 || rq.raw_minor >= MAX_RAW_MINORS) { ++ if (rq.raw_minor <= 0 || rq.raw_minor >= max_raw_minors) { + err = -EINVAL; + goto out; + } +@@ -271,12 +277,26 @@ static int __init raw_init(void) + dev_t dev = MKDEV(RAW_MAJOR, 0); + int ret; + +- ret = register_chrdev_region(dev, MAX_RAW_MINORS, "raw"); ++ if (max_raw_minors < 1 || max_raw_minors > 65536) { ++ printk(KERN_WARNING "raw: invalid max_raw_minors (must be" ++ " between 1 and 65536), using %d\n", MAX_RAW_MINORS); ++ max_raw_minors = MAX_RAW_MINORS; ++ } ++ ++ raw_devices = vmalloc(sizeof(struct raw_device_data) * max_raw_minors); ++ if (!raw_devices) { ++ printk(KERN_ERR "Not enough memory for raw device structures\n"); ++ ret = -ENOMEM; ++ goto error; ++ } ++ memset(raw_devices, 0, sizeof(struct raw_device_data) * max_raw_minors); ++ ++ ret = register_chrdev_region(dev, max_raw_minors, "raw"); + if (ret) + goto error; + + cdev_init(&raw_cdev, &raw_fops); +- ret = cdev_add(&raw_cdev, dev, MAX_RAW_MINORS); ++ ret = cdev_add(&raw_cdev, dev, max_raw_minors); + if (ret) { + kobject_put(&raw_cdev.kobj); + goto error_region; +@@ -295,8 +315,9 @@ static int __init raw_init(void) + return 0; + + error_region: +- unregister_chrdev_region(dev, MAX_RAW_MINORS); ++ unregister_chrdev_region(dev, max_raw_minors); + error: ++ vfree(raw_devices); + return ret; + } + +@@ -305,7 +326,7 @@ static void __exit raw_exit(void) + device_destroy(raw_class, MKDEV(RAW_MAJOR, 0)); + class_destroy(raw_class); + cdev_del(&raw_cdev); +- unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), MAX_RAW_MINORS); ++ unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), max_raw_minors); + } + + module_init(raw_init); diff --git a/patches.suse/readahead-request-tunables.patch b/patches.suse/readahead-request-tunables.patch new file mode 100644 index 0000000..2f22792 --- /dev/null +++ b/patches.suse/readahead-request-tunables.patch @@ -0,0 +1,44 @@ +From: Jan Kara +Subject: Update readahead and max_sectors tunables +References: bnc#548529 +Patch-mainline: no + +Increase read_ahead_kb and max_sectors_kb to values from SLES10 SP3 to get +back sequential IO performance if we are not compiling a -desktop kernel +flavor. + +Signed-off-by: Jan Kara + +--- + include/linux/blkdev.h | 4 ++++ + include/linux/mm.h | 4 ++++ + 2 files changed, 8 insertions(+) + +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -1038,7 +1038,11 @@ extern int blk_verify_command(unsigned c + enum blk_default_limits { + BLK_MAX_SEGMENTS = 128, + BLK_SAFE_MAX_SECTORS = 255, ++#ifndef CONFIG_KERNEL_DESKTOP ++ BLK_DEF_MAX_SECTORS = 2048, ++#else + BLK_DEF_MAX_SECTORS = 1024, ++#endif + BLK_MAX_SEGMENT_SIZE = 65536, + BLK_SEG_BOUNDARY_MASK = 0xFFFFFFFFUL, + }; +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -1302,7 +1302,11 @@ int write_one_page(struct page *page, in + void task_dirty_inc(struct task_struct *tsk); + + /* readahead.c */ ++#ifndef CONFIG_KERNEL_DESKTOP ++#define VM_MAX_READAHEAD 512 /* kbytes */ ++#else + #define VM_MAX_READAHEAD 128 /* kbytes */ ++#endif + #define VM_MIN_READAHEAD 16 /* kbytes (includes current page) */ + + int force_page_cache_readahead(struct address_space *mapping, struct file *filp, diff --git a/patches.suse/reiser4-exports b/patches.suse/reiser4-exports new file mode 100644 index 0000000..ece4113 --- /dev/null +++ b/patches.suse/reiser4-exports @@ -0,0 +1,41 @@ +From: ReiserFS Development +Subject: [PATCH] reiser4: add new exports for used symbols +Patch-mainline: Not yet + + This patch exports the following symbols for use in reiser4: + + - __remove_from_page_cache + - find_get_pages + +Acked-by: Jeff Mahoney + +--- + mm/filemap.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -139,6 +139,7 @@ void __remove_from_page_cache(struct pag + dec_bdi_stat(mapping->backing_dev_info, BDI_RECLAIMABLE); + } + } ++EXPORT_SYMBOL_GPL(__remove_from_page_cache); + + void remove_from_page_cache(struct page *page) + { +@@ -151,6 +152,7 @@ void remove_from_page_cache(struct page + spin_unlock_irq(&mapping->tree_lock); + mem_cgroup_uncharge_cache_page(page); + } ++EXPORT_SYMBOL_GPL(remove_from_page_cache); + + static int sync_page(void *word) + { +@@ -779,6 +781,7 @@ repeat: + rcu_read_unlock(); + return ret; + } ++EXPORT_SYMBOL_GPL(find_get_pages); + + /** + * find_get_pages_contig - gang contiguous pagecache lookup diff --git a/patches.suse/reiser4-set_page_dirty_notag b/patches.suse/reiser4-set_page_dirty_notag new file mode 100644 index 0000000..92f5995 --- /dev/null +++ b/patches.suse/reiser4-set_page_dirty_notag @@ -0,0 +1,61 @@ +From: ReiserFS Development +Subject: [PATCH] mm: Add set_page_dirty_notag() helper for reiser4 +Patch-mainline: not yet + +This patch adds a set_page_dirty_notag() helper which is like +set_page_dirty but doesn't add the pages to the radix tree. + +Currently the only user is reiser4. + +Acked-by: Jeff Mahoney +--- + + include/linux/mm.h | 1 + + mm/page-writeback.c | 26 ++++++++++++++++++++++++++ + 2 files changed, 27 insertions(+) + +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -853,6 +853,7 @@ int redirty_page_for_writepage(struct wr + void account_page_dirtied(struct page *page, struct address_space *mapping); + int set_page_dirty(struct page *page); + int set_page_dirty_lock(struct page *page); ++int set_page_dirty_notag(struct page *page); + int clear_page_dirty_for_io(struct page *page); + + extern unsigned long move_page_tables(struct vm_area_struct *vma, +--- a/mm/page-writeback.c ++++ b/mm/page-writeback.c +@@ -1130,6 +1130,32 @@ int __set_page_dirty_nobuffers(struct pa + EXPORT_SYMBOL(__set_page_dirty_nobuffers); + + /* ++ * set_page_dirty_notag() -- similar to __set_page_dirty_nobuffers() ++ * except it doesn't tag the page dirty in the page-cache radix tree. ++ * This means that the address space using this cannot use the regular ++ * filemap ->writepages() helpers and must provide its own means of ++ * tracking and finding non-tagged dirty pages. ++ * ++ * NOTE: furthermore, this version also doesn't handle truncate races. ++ */ ++int set_page_dirty_notag(struct page *page) ++{ ++ struct address_space *mapping = page->mapping; ++ ++ if (!TestSetPageDirty(page)) { ++ unsigned long flags; ++ WARN_ON_ONCE(!PagePrivate(page) && !PageUptodate(page)); ++ local_irq_save(flags); ++ account_page_dirtied(page, mapping); ++ local_irq_restore(flags); ++ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); ++ return 1; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(set_page_dirty_notag); ++ ++/* + * When a writepage implementation decides that it doesn't want to write this + * page for some reason, it should redirty the locked page via + * redirty_page_for_writepage() and it should then unlock the page and return 0 diff --git a/patches.suse/reiserfs-barrier-default b/patches.suse/reiserfs-barrier-default new file mode 100644 index 0000000..5041211 --- /dev/null +++ b/patches.suse/reiserfs-barrier-default @@ -0,0 +1,56 @@ +From: Jeff Mahoney +Subject: Make reiserfs default to barrier=flush +Patch-mainline: not yet + +Change the default reiserfs mount option to barrier=flush + +Signed-off-by: Jeff Mahoney +--- + + fs/reiserfs/Kconfig | 22 ++++++++++++++++++++++ + fs/reiserfs/super.c | 3 +++ + 2 files changed, 25 insertions(+) + +--- a/fs/reiserfs/Kconfig ++++ b/fs/reiserfs/Kconfig +@@ -50,6 +50,28 @@ config REISERFS_PROC_INFO + Almost everyone but ReiserFS developers and people fine-tuning + reiserfs or tracing problems should say N. + ++config REISERFS_DEFAULTS_TO_BARRIERS_ENABLED ++ bool "Default to 'barrier=flush' in reiserfs" ++ depends on REISERFS_FS ++ help ++ Modern disk drives support write caches that can speed up writeback. ++ Some devices, in order to improve their performance statistics, ++ report that the write has been completed even when it has only ++ been committed to volatile cache memory. This can result in ++ severe corruption in the event of power loss. ++ ++ The -o barrier option enables the file system to direct the block ++ layer to issue a barrier, which ensures that the cache has been ++ flushed before proceeding. This can produce some slowdown in ++ certain environments, but allows higher end storage arrays with ++ battery-backed caches to report completes writes sooner than ++ would be otherwise possible. ++ ++ Without this option, disk write caches should be disabled if ++ you value data integrity over writeback performance. ++ ++ If unsure, say N. ++ + config REISERFS_FS_XATTR + bool "ReiserFS extended attributes" + depends on REISERFS_FS +--- a/fs/reiserfs/super.c ++++ b/fs/reiserfs/super.c +@@ -1626,6 +1626,9 @@ static int reiserfs_fill_super(struct su + /* Set default values for options: non-aggressive tails, RO on errors */ + REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_SMALLTAIL); + REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_ERROR_RO); ++#ifdef CONFIG_REISERFS_DEFAULTS_TO_BARRIERS_ENABLED ++ REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_BARRIER_FLUSH); ++#endif + /* no preallocation minimum, be smart in + reiserfs_file_write instead */ + REISERFS_SB(s)->s_alloc_options.preallocmin = 0; diff --git a/patches.suse/rlim-0015-SECURITY-add-task_struct-to-setrlimit.patch b/patches.suse/rlim-0015-SECURITY-add-task_struct-to-setrlimit.patch new file mode 100644 index 0000000..5da8307 --- /dev/null +++ b/patches.suse/rlim-0015-SECURITY-add-task_struct-to-setrlimit.patch @@ -0,0 +1,114 @@ +From 9440a3f562ffe6ab34eff1ab52e6cfc516d6e7ba Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Wed, 26 Aug 2009 18:41:16 +0200 +Subject: [PATCH] SECURITY: add task_struct to setrlimit +References: FATE#305733 +Patch-mainline: no (later) + +Add task_struct to task_setrlimit of security_operations to be able to set +rlimit of different task than current. + +Signed-off-by: Jiri Slaby +Acked-by: Eric Paris +Acked-by: James Morris +--- + include/linux/security.h | 9 ++++++--- + kernel/sys.c | 2 +- + security/capability.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 7 ++++--- + 5 files changed, 16 insertions(+), 10 deletions(-) + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -1602,7 +1602,8 @@ struct security_operations { + int (*task_setnice) (struct task_struct *p, int nice); + int (*task_setioprio) (struct task_struct *p, int ioprio); + int (*task_getioprio) (struct task_struct *p); +- int (*task_setrlimit) (unsigned int resource, struct rlimit *new_rlim); ++ int (*task_setrlimit) (struct task_struct *p, unsigned int resource, ++ struct rlimit *new_rlim); + int (*task_setscheduler) (struct task_struct *p, int policy, + struct sched_param *lp); + int (*task_getscheduler) (struct task_struct *p); +@@ -1867,7 +1868,8 @@ int security_task_setgroups(struct group + int security_task_setnice(struct task_struct *p, int nice); + int security_task_setioprio(struct task_struct *p, int ioprio); + int security_task_getioprio(struct task_struct *p); +-int security_task_setrlimit(unsigned int resource, struct rlimit *new_rlim); ++int security_task_setrlimit(struct task_struct *p, unsigned int resource, ++ struct rlimit *new_rlim); + int security_task_setscheduler(struct task_struct *p, + int policy, struct sched_param *lp); + int security_task_getscheduler(struct task_struct *p); +@@ -2483,7 +2485,8 @@ static inline int security_task_getiopri + return 0; + } + +-static inline int security_task_setrlimit(unsigned int resource, ++static inline int security_task_setrlimit(struct task_struct *p, ++ unsigned int resource, + struct rlimit *new_rlim) + { + return 0; +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1320,7 +1320,7 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, + if (resource == RLIMIT_NOFILE && new_rlim.rlim_max > sysctl_nr_open) + return -EPERM; + +- retval = security_task_setrlimit(resource, &new_rlim); ++ retval = security_task_setrlimit(current, resource, &new_rlim); + if (retval) + return retval; + +--- a/security/capability.c ++++ b/security/capability.c +@@ -466,7 +466,8 @@ static int cap_task_getioprio(struct tas + return 0; + } + +-static int cap_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) ++static int cap_task_setrlimit(struct task_struct *p, unsigned int resource, ++ struct rlimit *new_rlim) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -832,9 +832,10 @@ int security_task_getioprio(struct task_ + return security_ops->task_getioprio(p); + } + +-int security_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) ++int security_task_setrlimit(struct task_struct *p, unsigned int resource, ++ struct rlimit *new_rlim) + { +- return security_ops->task_setrlimit(resource, new_rlim); ++ return security_ops->task_setrlimit(p, resource, new_rlim); + } + + int security_task_setscheduler(struct task_struct *p, +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -3393,16 +3393,17 @@ static int selinux_task_getioprio(struct + return current_has_perm(p, PROCESS__GETSCHED); + } + +-static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) ++static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, ++ struct rlimit *new_rlim) + { +- struct rlimit *old_rlim = current->signal->rlim + resource; ++ struct rlimit *old_rlim = p->signal->rlim + resource; + + /* Control the ability to change the hard limit (whether + lowering or raising it), so that the hard limit can + later be used as a safe reset point for the soft limit + upon context transitions. See selinux_bprm_committing_creds. */ + if (old_rlim->rlim_max != new_rlim->rlim_max) +- return current_has_perm(current, PROCESS__SETRLIMIT); ++ return current_has_perm(p, PROCESS__SETRLIMIT); + + return 0; + } diff --git a/patches.suse/rlim-0016-core-add-task_struct-to-update_rlimit_cpu.patch b/patches.suse/rlim-0016-core-add-task_struct-to-update_rlimit_cpu.patch new file mode 100644 index 0000000..52c89bd --- /dev/null +++ b/patches.suse/rlim-0016-core-add-task_struct-to-update_rlimit_cpu.patch @@ -0,0 +1,76 @@ +From 0c1b5ce8de67c36bbf67db38240a91f358133bdd Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Fri, 28 Aug 2009 14:05:12 +0200 +Subject: [PATCH] core: add task_struct to update_rlimit_cpu +References: FATE#305733 +Patch-mainline: no (later) + +Add task_struct as a parameter to update_rlimit_cpu to be able to set +rlimit_cpu of different task than current. + +Signed-off-by: Jiri Slaby +Acked-by: James Morris +--- + include/linux/posix-timers.h | 2 +- + kernel/posix-cpu-timers.c | 10 +++++----- + kernel/sys.c | 2 +- + security/selinux/hooks.c | 3 ++- + 4 files changed, 9 insertions(+), 8 deletions(-) + +--- a/include/linux/posix-timers.h ++++ b/include/linux/posix-timers.h +@@ -117,6 +117,6 @@ void set_process_cpu_timer(struct task_s + + long clock_nanosleep_restart(struct restart_block *restart_block); + +-void update_rlimit_cpu(unsigned long rlim_new); ++void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new); + + #endif +--- a/kernel/posix-cpu-timers.c ++++ b/kernel/posix-cpu-timers.c +@@ -13,16 +13,16 @@ + /* + * Called after updating RLIMIT_CPU to set timer expiration if necessary. + */ +-void update_rlimit_cpu(unsigned long rlim_new) ++void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new) + { + cputime_t cputime = secs_to_cputime(rlim_new); +- struct signal_struct *const sig = current->signal; ++ struct signal_struct *const sig = task->signal; + + if (cputime_eq(sig->it[CPUCLOCK_PROF].expires, cputime_zero) || + cputime_gt(sig->it[CPUCLOCK_PROF].expires, cputime)) { +- spin_lock_irq(¤t->sighand->siglock); +- set_process_cpu_timer(current, CPUCLOCK_PROF, &cputime, NULL); +- spin_unlock_irq(¤t->sighand->siglock); ++ spin_lock_irq(&task->sighand->siglock); ++ set_process_cpu_timer(task, CPUCLOCK_PROF, &cputime, NULL); ++ spin_unlock_irq(&task->sighand->siglock); + } + } + +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1350,7 +1350,7 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, + if (new_rlim.rlim_cur == RLIM_INFINITY) + goto out; + +- update_rlimit_cpu(new_rlim.rlim_cur); ++ update_rlimit_cpu(current, new_rlim.rlim_cur); + out: + return 0; + } +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2360,7 +2360,8 @@ static void selinux_bprm_committing_cred + initrlim = init_task.signal->rlim + i; + rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur); + } +- update_rlimit_cpu(current->signal->rlim[RLIMIT_CPU].rlim_cur); ++ update_rlimit_cpu(current, ++ current->signal->rlim[RLIMIT_CPU].rlim_cur); + } + } + diff --git a/patches.suse/rlim-0017-sys_setrlimit-make-sure-rlim_max-never-grows.patch b/patches.suse/rlim-0017-sys_setrlimit-make-sure-rlim_max-never-grows.patch new file mode 100644 index 0000000..7755cc4 --- /dev/null +++ b/patches.suse/rlim-0017-sys_setrlimit-make-sure-rlim_max-never-grows.patch @@ -0,0 +1,66 @@ +From 60aa0fa94c00942cc2c9f67f05d4a0d9e15349c7 Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +Date: Thu, 3 Sep 2009 19:21:45 +0200 +Subject: [PATCH] sys_setrlimit: make sure ->rlim_max never grows +References: FATE#305733 +Patch-mainline: no (later) + +Mostly preparation for Jiri's changes, but probably makes sense anyway. + +sys_setrlimit() checks new_rlim.rlim_max <= old_rlim->rlim_max, but when +it takes task_lock() old_rlim->rlim_max can be already lowered. Move this +check under task_lock(). + +Currently this is not important, we can only race with our sub-thread, +this means the application is stupid. But when we change the code to allow +the update of !current task's limits, it becomes important to make sure +->rlim_max can be lowered "reliably" even if we race with the application +doing sys_setrlimit(). + +Signed-off-by: Oleg Nesterov +Signed-off-by: Jiri Slaby +--- + kernel/sys.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1313,10 +1313,6 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, + return -EFAULT; + if (new_rlim.rlim_cur > new_rlim.rlim_max) + return -EINVAL; +- old_rlim = current->signal->rlim + resource; +- if ((new_rlim.rlim_max > old_rlim->rlim_max) && +- !capable(CAP_SYS_RESOURCE)) +- return -EPERM; + if (resource == RLIMIT_NOFILE && new_rlim.rlim_max > sysctl_nr_open) + return -EPERM; + +@@ -1334,11 +1330,16 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, + new_rlim.rlim_cur = 1; + } + ++ old_rlim = current->signal->rlim + resource; + task_lock(current->group_leader); +- *old_rlim = new_rlim; ++ if ((new_rlim.rlim_max <= old_rlim->rlim_max) || ++ capable(CAP_SYS_RESOURCE)) ++ *old_rlim = new_rlim; ++ else ++ retval = -EPERM; + task_unlock(current->group_leader); + +- if (resource != RLIMIT_CPU) ++ if (retval || resource != RLIMIT_CPU) + goto out; + + /* +@@ -1352,7 +1353,7 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, + + update_rlimit_cpu(current, new_rlim.rlim_cur); + out: +- return 0; ++ return retval; + } + + /* diff --git a/patches.suse/rlim-0018-core-split-sys_setrlimit.patch b/patches.suse/rlim-0018-core-split-sys_setrlimit.patch new file mode 100644 index 0000000..ecc4c87 --- /dev/null +++ b/patches.suse/rlim-0018-core-split-sys_setrlimit.patch @@ -0,0 +1,112 @@ +From 282b6f3a2d1c95ed2443ad974e354883b66cd7c9 Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Wed, 26 Aug 2009 23:45:34 +0200 +Subject: [PATCH] core: split sys_setrlimit +References: FATE#305733 +Patch-mainline: no (later) + +Create do_setrlimit from sys_setrlimit and declare do_setrlimit +in the resource header. This is to allow rlimits to be changed +not only by syscall, but later from proc and syscall code too. + +Signed-off-by: Jiri Slaby +--- + include/linux/resource.h | 2 ++ + kernel/sys.c | 40 ++++++++++++++++++++++++---------------- + 2 files changed, 26 insertions(+), 16 deletions(-) + +--- a/include/linux/resource.h ++++ b/include/linux/resource.h +@@ -73,6 +73,8 @@ struct rlimit { + struct task_struct; + + int getrusage(struct task_struct *p, int who, struct rusage __user *ru); ++int do_setrlimit(struct task_struct *tsk, unsigned int resource, ++ struct rlimit *new_rlim); + + #endif /* __KERNEL__ */ + +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1302,42 +1302,41 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned + + #endif + +-SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) ++int do_setrlimit(struct task_struct *tsk, unsigned int resource, ++ struct rlimit *new_rlim) + { +- struct rlimit new_rlim, *old_rlim; ++ struct rlimit *old_rlim; + int retval; + + if (resource >= RLIM_NLIMITS) + return -EINVAL; +- if (copy_from_user(&new_rlim, rlim, sizeof(*rlim))) +- return -EFAULT; +- if (new_rlim.rlim_cur > new_rlim.rlim_max) ++ if (new_rlim->rlim_cur > new_rlim->rlim_max) + return -EINVAL; +- if (resource == RLIMIT_NOFILE && new_rlim.rlim_max > sysctl_nr_open) ++ if (resource == RLIMIT_NOFILE && new_rlim->rlim_max > sysctl_nr_open) + return -EPERM; + +- retval = security_task_setrlimit(current, resource, &new_rlim); ++ retval = security_task_setrlimit(tsk, resource, new_rlim); + if (retval) + return retval; + +- if (resource == RLIMIT_CPU && new_rlim.rlim_cur == 0) { ++ if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) { + /* + * The caller is asking for an immediate RLIMIT_CPU + * expiry. But we use the zero value to mean "it was + * never set". So let's cheat and make it one second + * instead + */ +- new_rlim.rlim_cur = 1; ++ new_rlim->rlim_cur = 1; + } + +- old_rlim = current->signal->rlim + resource; +- task_lock(current->group_leader); +- if ((new_rlim.rlim_max <= old_rlim->rlim_max) || ++ old_rlim = tsk->signal->rlim + resource; ++ task_lock(tsk->group_leader); ++ if ((new_rlim->rlim_max <= old_rlim->rlim_max) || + capable(CAP_SYS_RESOURCE)) +- *old_rlim = new_rlim; ++ *old_rlim = *new_rlim; + else + retval = -EPERM; +- task_unlock(current->group_leader); ++ task_unlock(tsk->group_leader); + + if (retval || resource != RLIMIT_CPU) + goto out; +@@ -1348,14 +1347,23 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, + * very long-standing error, and fixing it now risks breakage of + * applications, so we live with it + */ +- if (new_rlim.rlim_cur == RLIM_INFINITY) ++ if (new_rlim->rlim_cur == RLIM_INFINITY) + goto out; + +- update_rlimit_cpu(current, new_rlim.rlim_cur); ++ update_rlimit_cpu(tsk, new_rlim->rlim_cur); + out: + return retval; + } + ++SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) ++{ ++ struct rlimit new_rlim; ++ ++ if (copy_from_user(&new_rlim, rlim, sizeof(*rlim))) ++ return -EFAULT; ++ return do_setrlimit(current, resource, &new_rlim); ++} ++ + /* + * It would make sense to put struct rusage in the task_struct, + * except that would make the task_struct be *really big*. After diff --git a/patches.suse/rlim-0019-core-allow-setrlimit-to-non-current-tasks.patch b/patches.suse/rlim-0019-core-allow-setrlimit-to-non-current-tasks.patch new file mode 100644 index 0000000..2157825 --- /dev/null +++ b/patches.suse/rlim-0019-core-allow-setrlimit-to-non-current-tasks.patch @@ -0,0 +1,56 @@ +From a35547da680829ce9036a476678a5fd4bfa59a6b Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Fri, 28 Aug 2009 14:08:17 +0200 +Subject: [PATCH] core: allow setrlimit to non-current tasks +References: FATE#305733 +Patch-mainline: no (later) + +Add locking to allow setrlimit accept task parameter other than +current. + +Namely, lock tasklist_lock for read and check whether the task +structure has sighand non-null. Do all the signal processing under +that lock still held. + +Signed-off-by: Jiri Slaby +Cc: Oleg Nesterov +--- + kernel/sys.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1302,6 +1302,7 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned + + #endif + ++/* make sure you are allowed to change @tsk limits before calling this */ + int do_setrlimit(struct task_struct *tsk, unsigned int resource, + struct rlimit *new_rlim) + { +@@ -1315,9 +1316,16 @@ int do_setrlimit(struct task_struct *tsk + if (resource == RLIMIT_NOFILE && new_rlim->rlim_max > sysctl_nr_open) + return -EPERM; + ++ /* protect tsk->signal and tsk->sighand from disappearing */ ++ read_lock(&tasklist_lock); ++ if (!tsk->sighand) { ++ retval = -ESRCH; ++ goto out; ++ } ++ + retval = security_task_setrlimit(tsk, resource, new_rlim); + if (retval) +- return retval; ++ goto out; + + if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) { + /* +@@ -1352,6 +1360,7 @@ int do_setrlimit(struct task_struct *tsk + + update_rlimit_cpu(tsk, new_rlim->rlim_cur); + out: ++ read_unlock(&tasklist_lock); + return retval; + } + diff --git a/patches.suse/rlim-0020-core-optimize-setrlimit-for-current-task.patch b/patches.suse/rlim-0020-core-optimize-setrlimit-for-current-task.patch new file mode 100644 index 0000000..b519a97 --- /dev/null +++ b/patches.suse/rlim-0020-core-optimize-setrlimit-for-current-task.patch @@ -0,0 +1,50 @@ +From d54c730eba63dfe0dcb8c7ab41514c564c159212 Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Fri, 28 Aug 2009 14:08:17 +0200 +Subject: [PATCH] core: optimize setrlimit for current task +References: FATE#305733 +Patch-mainline: no (later) + +Don't take tasklist lock for 'current'. It's not needed, since +current->sighand/signal can't disappear. + +This improves serlimit called especially via sys_setrlimit. + +Signed-off-by: Jiri Slaby +Cc: Oleg Nesterov +--- + kernel/sys.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1316,11 +1316,14 @@ int do_setrlimit(struct task_struct *tsk + if (resource == RLIMIT_NOFILE && new_rlim->rlim_max > sysctl_nr_open) + return -EPERM; + +- /* protect tsk->signal and tsk->sighand from disappearing */ +- read_lock(&tasklist_lock); +- if (!tsk->sighand) { +- retval = -ESRCH; +- goto out; ++ /* optimization: 'current' doesn't need locking, e.g. setrlimit */ ++ if (tsk != current) { ++ /* protect tsk->signal and tsk->sighand from disappearing */ ++ read_lock(&tasklist_lock); ++ if (!tsk->sighand) { ++ retval = -ESRCH; ++ goto out; ++ } + } + + retval = security_task_setrlimit(tsk, resource, new_rlim); +@@ -1360,7 +1363,8 @@ int do_setrlimit(struct task_struct *tsk + + update_rlimit_cpu(tsk, new_rlim->rlim_cur); + out: +- read_unlock(&tasklist_lock); ++ if (tsk != current) ++ read_unlock(&tasklist_lock); + return retval; + } + diff --git a/patches.suse/rlim-0021-FS-proc-switch-limits-reading-to-fops.patch b/patches.suse/rlim-0021-FS-proc-switch-limits-reading-to-fops.patch new file mode 100644 index 0000000..d68ad5a --- /dev/null +++ b/patches.suse/rlim-0021-FS-proc-switch-limits-reading-to-fops.patch @@ -0,0 +1,96 @@ +From 8e75daf5abec2e008b7d2e21876bc60cf0f95ece Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Fri, 27 Nov 2009 15:25:03 +0100 +Subject: [PATCH] FS: proc, switch limits reading to fops +References: FATE#305733 +Patch-mainline: no (later) + +Use fops instead of proc_info_read. We will need fops for limits +writing and the code would look ugly if we used +NOD("limits", S_IFREG|S_IRUSR|S_IWUSR, NULL, + &proc_pid_limits_operations, { .proc_read = proc_pid_limits }), + +We will just use +REG("limits", S_IRUSR|S_IWUSR, proc_pid_limits_operations), + +Signed-off-by: Jiri Slaby +--- + fs/proc/base.c | 37 ++++++++++++++++++++++++++++--------- + 1 file changed, 28 insertions(+), 9 deletions(-) + +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -477,19 +477,30 @@ static const struct limit_names lnames[R + }; + + /* Display limits for a process */ +-static int proc_pid_limits(struct task_struct *task, char *buffer) ++static ssize_t limits_read(struct file *file, char __user *buf, size_t rcount, ++ loff_t *ppos) + { +- unsigned int i; +- int count = 0; +- unsigned long flags; +- char *bufptr = buffer; +- + struct rlimit rlim[RLIM_NLIMITS]; ++ struct task_struct *task; ++ unsigned long flags; ++ unsigned int i; ++ ssize_t count = 0; ++ char *bufptr; + +- if (!lock_task_sighand(task, &flags)) ++ task = get_proc_task(file->f_path.dentry->d_inode); ++ if (!task) ++ return -ESRCH; ++ if (!lock_task_sighand(task, &flags)) { ++ put_task_struct(task); + return 0; ++ } + memcpy(rlim, task->signal->rlim, sizeof(struct rlimit) * RLIM_NLIMITS); + unlock_task_sighand(task, &flags); ++ put_task_struct(task); ++ ++ bufptr = (char *)__get_free_page(GFP_TEMPORARY); ++ if (!bufptr) ++ return -ENOMEM; + + /* + * print the file header +@@ -518,9 +529,17 @@ static int proc_pid_limits(struct task_s + count += sprintf(&bufptr[count], "\n"); + } + ++ count = simple_read_from_buffer(buf, rcount, ppos, bufptr, count); ++ ++ free_page((unsigned long)bufptr); ++ + return count; + } + ++static const struct file_operations proc_pid_limits_operations = { ++ .read = limits_read, ++}; ++ + #ifdef CONFIG_HAVE_ARCH_TRACEHOOK + static int proc_pid_syscall(struct task_struct *task, char *buffer) + { +@@ -2577,7 +2596,7 @@ static const struct pid_entry tgid_base_ + INF("auxv", S_IRUSR, proc_pid_auxv), + ONE("status", S_IRUGO, proc_pid_status), + ONE("personality", S_IRUSR, proc_pid_personality), +- INF("limits", S_IRUSR, proc_pid_limits), ++ REG("limits", S_IRUSR, proc_pid_limits_operations), + #ifdef CONFIG_SCHED_DEBUG + REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), + #endif +@@ -2912,7 +2931,7 @@ static const struct pid_entry tid_base_s + INF("auxv", S_IRUSR, proc_pid_auxv), + ONE("status", S_IRUGO, proc_pid_status), + ONE("personality", S_IRUSR, proc_pid_personality), +- INF("limits", S_IRUSR, proc_pid_limits), ++ REG("limits", S_IRUSR, proc_pid_limits_operations), + #ifdef CONFIG_SCHED_DEBUG + REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), + #endif diff --git a/patches.suse/rlim-0022-FS-proc-make-limits-writable.patch b/patches.suse/rlim-0022-FS-proc-make-limits-writable.patch new file mode 100644 index 0000000..368de27 --- /dev/null +++ b/patches.suse/rlim-0022-FS-proc-make-limits-writable.patch @@ -0,0 +1,109 @@ +From db3d91905e245c7495df8991a9c054b6d4e0dc98 Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Wed, 26 Aug 2009 21:24:30 +0200 +Subject: [PATCH] FS: proc, make limits writable +References: FATE#305733 +Patch-mainline: no (later) + +Allow writing strings such as +Max core file size=0:unlimited +to /proc//limits to change limits. + +Signed-off-by: Jiri Slaby +--- + fs/proc/base.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 66 insertions(+), 2 deletions(-) + +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -536,8 +536,72 @@ static ssize_t limits_read(struct file * + return count; + } + ++static ssize_t limits_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); ++ char str[32 + 1 + 16 + 1 + 16 + 1], *delim, *next; ++ struct rlimit new_rlimit; ++ unsigned int i; ++ int ret; ++ ++ if (!task) { ++ count = -ESRCH; ++ goto out; ++ } ++ if (copy_from_user(str, buf, min(count, sizeof(str) - 1))) { ++ count = -EFAULT; ++ goto put_task; ++ } ++ ++ str[min(count, sizeof(str) - 1)] = 0; ++ ++ delim = strchr(str, '='); ++ if (!delim) { ++ count = -EINVAL; ++ goto put_task; ++ } ++ *delim++ = 0; /* for easy 'str' usage */ ++ new_rlimit.rlim_cur = simple_strtoul(delim, &next, 0); ++ if (*next != ':') { ++ if (strncmp(delim, "unlimited:", 10)) { ++ count = -EINVAL; ++ goto put_task; ++ } ++ new_rlimit.rlim_cur = RLIM_INFINITY; ++ next = delim + 9; /* move to ':' */ ++ } ++ delim = next + 1; ++ new_rlimit.rlim_max = simple_strtoul(delim, &next, 0); ++ if (*next != 0) { ++ if (strcmp(delim, "unlimited")) { ++ count = -EINVAL; ++ goto put_task; ++ } ++ new_rlimit.rlim_max = RLIM_INFINITY; ++ } ++ ++ for (i = 0; i < RLIM_NLIMITS; i++) ++ if (!strcmp(str, lnames[i].name)) ++ break; ++ if (i >= RLIM_NLIMITS) { ++ count = -EINVAL; ++ goto put_task; ++ } ++ ++ ret = do_setrlimit(task, i, &new_rlimit); ++ if (ret) ++ count = ret; ++ ++put_task: ++ put_task_struct(task); ++out: ++ return count; ++} ++ + static const struct file_operations proc_pid_limits_operations = { + .read = limits_read, ++ .write = limits_write, + }; + + #ifdef CONFIG_HAVE_ARCH_TRACEHOOK +@@ -2596,7 +2660,7 @@ static const struct pid_entry tgid_base_ + INF("auxv", S_IRUSR, proc_pid_auxv), + ONE("status", S_IRUGO, proc_pid_status), + ONE("personality", S_IRUSR, proc_pid_personality), +- REG("limits", S_IRUSR, proc_pid_limits_operations), ++ REG("limits", S_IRUSR|S_IWUSR, proc_pid_limits_operations), + #ifdef CONFIG_SCHED_DEBUG + REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), + #endif +@@ -2931,7 +2995,7 @@ static const struct pid_entry tid_base_s + INF("auxv", S_IRUSR, proc_pid_auxv), + ONE("status", S_IRUGO, proc_pid_status), + ONE("personality", S_IRUSR, proc_pid_personality), +- REG("limits", S_IRUSR, proc_pid_limits_operations), ++ REG("limits", S_IRUSR|S_IWUSR, proc_pid_limits_operations), + #ifdef CONFIG_SCHED_DEBUG + REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), + #endif diff --git a/patches.suse/rlim-0023-core-do-security-check-under-task_lock.patch b/patches.suse/rlim-0023-core-do-security-check-under-task_lock.patch new file mode 100644 index 0000000..3dcbfab --- /dev/null +++ b/patches.suse/rlim-0023-core-do-security-check-under-task_lock.patch @@ -0,0 +1,60 @@ +From 70b83579b39dc1369bc58ab395259bd254bf4a38 Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Sat, 14 Nov 2009 17:37:04 +0100 +Subject: [PATCH] core: do security check under task_lock +References: FATE#305733 +Patch-mainline: no (later) + +Do security_task_setrlimit under task_lock. Other tasks may +change limits under our hands while we are checking limits +inside the function. From now on, they can't. + +Signed-off-by: Jiri Slaby +Acked-by: James Morris +Cc: Heiko Carstens +Cc: Andrew Morton +Cc: Ingo Molnar +--- + kernel/sys.c | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1307,7 +1307,7 @@ int do_setrlimit(struct task_struct *tsk + struct rlimit *new_rlim) + { + struct rlimit *old_rlim; +- int retval; ++ int retval = 0; + + if (resource >= RLIM_NLIMITS) + return -EINVAL; +@@ -1326,10 +1326,6 @@ int do_setrlimit(struct task_struct *tsk + } + } + +- retval = security_task_setrlimit(tsk, resource, new_rlim); +- if (retval) +- goto out; +- + if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) { + /* + * The caller is asking for an immediate RLIMIT_CPU +@@ -1342,11 +1338,13 @@ int do_setrlimit(struct task_struct *tsk + + old_rlim = tsk->signal->rlim + resource; + task_lock(tsk->group_leader); +- if ((new_rlim->rlim_max <= old_rlim->rlim_max) || +- capable(CAP_SYS_RESOURCE)) +- *old_rlim = *new_rlim; +- else ++ if ((new_rlim->rlim_max > old_rlim->rlim_max) && ++ !capable(CAP_SYS_RESOURCE)) + retval = -EPERM; ++ if (!retval) ++ retval = security_task_setrlimit(tsk, resource, new_rlim); ++ if (!retval) ++ *old_rlim = *new_rlim; + task_unlock(tsk->group_leader); + + if (retval || resource != RLIMIT_CPU) diff --git a/patches.suse/s390-Kerntypes.diff b/patches.suse/s390-Kerntypes.diff new file mode 100644 index 0000000..a6994ba --- /dev/null +++ b/patches.suse/s390-Kerntypes.diff @@ -0,0 +1,387 @@ +From: Michael Holzheu +Subject: S390: Generate Kerntypes file +Patch-mainline: Not yet + +s390 core changes: + - Remove rule to generate kernel listing. + - Add code to generate kerntypes for use with the lkcd utils. + +Signed-off-by: Michael Holzheu +Signed-off-by: Martin Schwidefsky +Signed-off-by: Michal Marek +--- + + arch/s390/Makefile | 4 + arch/s390/boot/Makefile | 19 ++ + arch/s390/boot/kerntypes.c | 311 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 328 insertions(+), 6 deletions(-) + +--- a/arch/s390/Makefile ++++ b/arch/s390/Makefile +@@ -105,12 +105,12 @@ drivers-$(CONFIG_OPROFILE) += arch/s390/ + + boot := arch/s390/boot + +-all: image bzImage ++all: image bzImage kerntypes.o + + install: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $@ + +-image bzImage: vmlinux ++image bzImage kerntypes.o: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + + zfcpdump: +--- a/arch/s390/boot/Makefile ++++ b/arch/s390/boot/Makefile +@@ -2,15 +2,26 @@ + # Makefile for the linux s390-specific parts of the memory manager. + # + +-COMPILE_VERSION := __linux_compile_version_id__`hostname | \ +- tr -c '[0-9A-Za-z]' '_'`__`date | \ +- tr -c '[0-9A-Za-z]' '_'`_t ++COMPILE_VERSION := __linux_compile_version_id__$(shell hostname | \ ++ tr -c '[0-9A-Za-z]' '_')__$(shell date | \ ++ tr -c '[0-9A-Za-z]' '_')_t + ++ ++chk-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \ ++ > /dev/null 2>&1; then echo "$(1)"; fi ;) ++ ++# Remove possible '-g' from CFLAGS_KERNEL, since we want to use stabs ++# debug format. ++override CFLAGS_KERNEL := $(shell echo $(CFLAGS_KERNEL) | sed 's/-g//') + EXTRA_CFLAGS := -DCOMPILE_VERSION=$(COMPILE_VERSION) -gstabs -I. ++# Assume we don't need the flag if the compiler doesn't know about it ++EXTRA_CFLAGS += $(call chk-option,-fno-eliminate-unused-debug-types) ++ + + targets := image + targets += bzImage + subdir- := compressed ++targets += kerntypes.o + + $(obj)/image: vmlinux FORCE + $(call if_changed,objcopy) +@@ -23,4 +34,4 @@ $(obj)/compressed/vmlinux: FORCE + + install: $(CONFIGURE) $(obj)/image + sh -x $(srctree)/$(obj)/install.sh $(KERNELRELEASE) $(obj)/image \ +- System.map Kerntypes "$(INSTALL_PATH)" ++ System.map "$(INSTALL_PATH)" +--- /dev/null ++++ b/arch/s390/boot/kerntypes.c +@@ -0,0 +1,311 @@ ++/* ++ * kerntypes.c ++ * ++ * Dummy module that includes headers for all kernel types of interest. ++ * The kernel type information is used by the lcrash utility when ++ * analyzing system crash dumps or the live system. Using the type ++ * information for the running system, rather than kernel header files, ++ * makes for a more flexible and robust analysis tool. ++ * ++ * This source code is released under the GNU GPL. ++ */ ++ ++/* generate version for this file */ ++typedef char *COMPILE_VERSION; ++ ++/* General linux types */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_SLUB ++ #include ++#endif ++#ifdef CONFIG_SLAB ++ #include ++#endif ++#ifdef CONFIG_SLQB ++ #include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * s390 specific includes ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* channel subsystem driver */ ++#include "drivers/s390/cio/cio.h" ++#include "drivers/s390/cio/chsc.h" ++#include "drivers/s390/cio/css.h" ++#include "drivers/s390/cio/device.h" ++#include "drivers/s390/cio/chsc_sch.h" ++ ++/* dasd device driver */ ++#include "drivers/s390/block/dasd_int.h" ++#include "drivers/s390/block/dasd_diag.h" ++#include "drivers/s390/block/dasd_eckd.h" ++#include "drivers/s390/block/dasd_fba.h" ++ ++/* networking drivers */ ++#include "include/net/iucv/iucv.h" ++#include "drivers/s390/net/fsm.h" ++#include "drivers/s390/net/ctcm_main.h" ++#include "drivers/s390/net/ctcm_fsms.h" ++#include "drivers/s390/net/lcs.h" ++#include "drivers/s390/net/qeth_core.h" ++#include "drivers/s390/net/qeth_core_mpc.h" ++#include "drivers/s390/net/qeth_l3.h" ++ ++/* zfcp device driver */ ++#include "drivers/s390/scsi/zfcp_def.h" ++#include "drivers/s390/scsi/zfcp_fsf.h" ++ ++/* crypto device driver */ ++#include "drivers/s390/crypto/ap_bus.h" ++#include "drivers/s390/crypto/zcrypt_api.h" ++#include "drivers/s390/crypto/zcrypt_cca_key.h" ++#include "drivers/s390/crypto/zcrypt_pcica.h" ++#include "drivers/s390/crypto/zcrypt_pcicc.h" ++#include "drivers/s390/crypto/zcrypt_pcixcc.h" ++#include "drivers/s390/crypto/zcrypt_cex2a.h" ++ ++/* sclp device driver */ ++#include "drivers/s390/char/sclp.h" ++#include "drivers/s390/char/sclp_rw.h" ++#include "drivers/s390/char/sclp_tty.h" ++ ++/* vmur device driver */ ++#include "drivers/s390/char/vmur.h" ++ ++/* qdio device driver */ ++#include "drivers/s390/cio/qdio.h" ++#include "drivers/s390/cio/qdio_thinint.c" ++ ++ ++/* KVM */ ++#include "include/linux/kvm.h" ++#include "include/linux/kvm_host.h" ++#include "include/linux/kvm_para.h" ++ ++/* Virtio */ ++#include "include/linux/virtio.h" ++#include "include/linux/virtio_config.h" ++#include "include/linux/virtio_ring.h" ++#include "include/linux/virtio_9p.h" ++#include "include/linux/virtio_console.h" ++#include "include/linux/virtio_rng.h" ++#include "include/linux/virtio_balloon.h" ++#include "include/linux/virtio_net.h" ++#include "include/linux/virtio_blk.h" ++ ++/* ++ * include sched.c for types: ++ * - struct prio_array ++ * - struct runqueue ++ */ ++#include "kernel/sched.c" ++/* ++ * include slab.c for struct kmem_cache ++ */ ++#ifdef CONFIG_SLUB ++ #include "mm/slub.c" ++#endif ++#ifdef CONFIG_SLAB ++ #include "mm/slab.c" ++#endif ++#ifdef CONFIG_SLQB ++ #include "mm/slqb.c" ++#endif ++ ++/* include driver core private structures */ ++#include "drivers/base/base.h" diff --git a/patches.suse/s390-System.map.diff b/patches.suse/s390-System.map.diff new file mode 100644 index 0000000..93b9546 --- /dev/null +++ b/patches.suse/s390-System.map.diff @@ -0,0 +1,30 @@ +From: Bernhard Walle +Subject: [PATCH] Strip L2^B symbols +Patch-mainline: never +References: bnc #456682 + +This patches strips all L2^B symbols that happen on s390 only from System.map. +We don't need that symbols as this are local labels. It confuses (older) +versions of crash and just makes System.map larger. + +The proper fix needs to be in binutils. However, since the binutils maintainer +at SUSE is not cooperative I workarounded this in the kernel. The proper +binutils patch is already mainline [1]. + + +Signed-off-by: Bernhard Walle + +[1] http://article.gmane.org/gmane.comp.gnu.binutils.cvs/12731 +--- + scripts/mksysmap | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/scripts/mksysmap ++++ b/scripts/mksysmap +@@ -41,5 +41,5 @@ + # so we just ignore them to let readprofile continue to work. + # (At least sparc64 has __crc_ in the middle). + +-$NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)' > $2 ++$NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)\|\(L2\)' > $2 + diff --git a/patches.suse/sched-revert-latency-defaults b/patches.suse/sched-revert-latency-defaults new file mode 100644 index 0000000..c0f2ebe --- /dev/null +++ b/patches.suse/sched-revert-latency-defaults @@ -0,0 +1,95 @@ +From: Suresh Jayaraman +Subject: Revert sched latency defaults +References: bnc#557307 +Patch-mainline: Never + +The upstream commit 172e082a91 re-tuned the sched latency defaults to better +suit desktop workloads. This hurt server workloads. So revert the latency +defaults to values similar to SLE11 GM to avoid several performance +regressions. + +Also, turn FAIR_SLEEPERS off and NORMALIZED_SLEEPER on. The above scheduler +tunables seem to be most effective with FAIR_SLEEPERS off and +NORMALIZED_SLEEPER on. + +The sysbench, dbench and Specjjb results showed much better performance with +these changes. + +The interbench results didn't show any user visible impact and I expect +desktop workloads won't be affected much. Iam not aware of/heard of any impact +of this tuning that is affecting any specific workload. + +Signed-off-by: Suresh Jayaraman +--- + kernel/sched_fair.c | 12 ++++++------ + kernel/sched_features.h | 4 ++-- + 2 files changed, 8 insertions(+), 8 deletions(-) + +Index: linux-2.6.32-master/kernel/sched_fair.c +=================================================================== +--- linux-2.6.32-master.orig/kernel/sched_fair.c ++++ linux-2.6.32-master/kernel/sched_fair.c +@@ -24,7 +24,7 @@ + + /* + * Targeted preemption latency for CPU-bound tasks: +- * (default: 5ms * (1 + ilog(ncpus)), units: nanoseconds) ++ * (default: 20ms * (1 + ilog(ncpus)), units: nanoseconds) + * + * NOTE: this latency value is not the same as the concept of + * 'timeslice length' - timeslices in CFS are of variable length +@@ -34,13 +34,13 @@ + * (to see the precise effective timeslice length of your workload, + * run vmstat and monitor the context-switches (cs) field) + */ +-unsigned int sysctl_sched_latency = 5000000ULL; ++unsigned int sysctl_sched_latency = 20000000ULL; + + /* + * Minimal preemption granularity for CPU-bound tasks: +- * (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds) ++ * (default: 4 msec * (1 + ilog(ncpus)), units: nanoseconds) + */ +-unsigned int sysctl_sched_min_granularity = 1000000ULL; ++unsigned int sysctl_sched_min_granularity = 4000000ULL; + + /* + * is kept at sysctl_sched_latency / sysctl_sched_min_granularity +@@ -63,13 +63,13 @@ unsigned int __read_mostly sysctl_sched_ + + /* + * SCHED_OTHER wake-up granularity. +- * (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds) ++ * (default: 5 msec * (1 + ilog(ncpus)), units: nanoseconds) + * + * This option delays the preemption effects of decoupled workloads + * and reduces their over-scheduling. Synchronous workloads will still + * have immediate wakeup/sleep latencies. + */ +-unsigned int sysctl_sched_wakeup_granularity = 1000000UL; ++unsigned int sysctl_sched_wakeup_granularity = 5000000UL; + + const_debug unsigned int sysctl_sched_migration_cost = 500000UL; + +Index: linux-2.6.32-master/kernel/sched_features.h +=================================================================== +--- linux-2.6.32-master.orig/kernel/sched_features.h ++++ linux-2.6.32-master/kernel/sched_features.h +@@ -3,7 +3,7 @@ + * considers the task to be running during that period. This gives it + * a service deficit on wakeup, allowing it to run sooner. + */ +-SCHED_FEAT(FAIR_SLEEPERS, 1) ++SCHED_FEAT(FAIR_SLEEPERS, 0) + + /* + * Only give sleepers 50% of their service deficit. This allows +@@ -17,7 +17,7 @@ SCHED_FEAT(GENTLE_FAIR_SLEEPERS, 1) + * longer period, and lighter task an effective shorter period they + * are considered running. + */ +-SCHED_FEAT(NORMALIZED_SLEEPER, 0) ++SCHED_FEAT(NORMALIZED_SLEEPER, 1) + + /* + * Place new tasks ahead so that they do not starve already running diff --git a/patches.suse/scsi-error-test-unit-ready-timeout b/patches.suse/scsi-error-test-unit-ready-timeout new file mode 100644 index 0000000..a3e2f09 --- /dev/null +++ b/patches.suse/scsi-error-test-unit-ready-timeout @@ -0,0 +1,35 @@ +From: garloff@suse.de +Subject: Introduce own timeout for TEST_UNIT_READY +Reference: SUSE41689 +Patch-mainline: not yet + +In error recovery, a SCSI device may need more than the 10s SENSE_TIMEOUT +to respond to TEST_UNIT_READY, as reported in novell bugzilla #56689. +The patch introduces an own timeout for TEST_UNIT_READY which is set +to 30s and used. + +Signed-off-by: Kurt Garloff + +--- + drivers/scsi/scsi_error.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/scsi/scsi_error.c ++++ b/drivers/scsi/scsi_error.c +@@ -42,6 +42,7 @@ + #include "scsi_transport_api.h" + + #define SENSE_TIMEOUT (10*HZ) ++#define TEST_UNIT_READY_TIMEOUT (30*HZ) + + /* + * These should *probably* be handled by the host itself. +@@ -994,7 +995,7 @@ static int scsi_eh_tur(struct scsi_cmnd + int retry_cnt = 1, rtn; + + retry_tur: +- rtn = scsi_send_eh_cmnd(scmd, tur_command, 6, SENSE_TIMEOUT, 0); ++ rtn = scsi_send_eh_cmnd(scmd, tur_command, 6, TEST_UNIT_READY_TIMEOUT, 0); + + SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n", + __func__, scmd, rtn)); diff --git a/patches.suse/scsi-netlink-ml b/patches.suse/scsi-netlink-ml new file mode 100644 index 0000000..5e98bbd --- /dev/null +++ b/patches.suse/scsi-netlink-ml @@ -0,0 +1,215 @@ +Subject: Netlink interface for SCSI sense codes +From: Hannes Reinecke +Date: Fri Nov 21 10:08:01 2008 +0100: +Git: 97746dc5543ef9113c927022dc54ccd26915563d +Patch-mainline: not yet + +Inform the userspace about SCSI sense codes; some of them +carry vital information where userspace should react to. + +Signed-off-by: Hannes Reinecke + +--- + drivers/scsi/scsi_error.c | 79 +++++++++++++++++++++++++++++++++++++++++ + include/scsi/scsi_netlink.h | 6 ++- + include/scsi/scsi_netlink_ml.h | 64 +++++++++++++++++++++++++++++++++ + 3 files changed, 147 insertions(+), 2 deletions(-) + +--- a/drivers/scsi/scsi_error.c ++++ b/drivers/scsi/scsi_error.c +@@ -24,6 +24,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -33,6 +35,7 @@ + #include + #include + #include ++#include + + #include "scsi_priv.h" + #include "scsi_logging.h" +@@ -213,6 +216,80 @@ static inline void scsi_eh_prt_fail_stat + } + #endif + ++#ifdef CONFIG_SCSI_NETLINK ++/** ++ * scsi_post_sense_event - called to post a 'Sense Code' event ++ * ++ * @sdev: SCSI device the sense code occured on ++ * @sshdr: SCSI sense code ++ * ++ * Returns: ++ * 0 on succesful return ++ * otherwise, failing error code ++ * ++ */ ++static void scsi_post_sense_event(struct scsi_device *sdev, ++ struct scsi_sense_hdr *sshdr) ++{ ++ struct sk_buff *skb; ++ struct nlmsghdr *nlh; ++ struct scsi_nl_sense_msg *msg; ++ u32 len, skblen; ++ int err; ++ ++ if (!scsi_nl_sock) { ++ err = -ENOENT; ++ goto send_fail; ++ } ++ ++ len = SCSI_NL_MSGALIGN(sizeof(*msg)); ++ skblen = NLMSG_SPACE(len); ++ ++ skb = alloc_skb(skblen, GFP_ATOMIC); ++ if (!skb) { ++ err = -ENOBUFS; ++ goto send_fail; ++ } ++ ++ nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, ++ skblen - sizeof(*nlh), 0); ++ if (!nlh) { ++ err = -ENOBUFS; ++ goto send_fail_skb; ++ } ++ msg = NLMSG_DATA(nlh); ++ ++ INIT_SCSI_NL_HDR(&msg->snlh, SCSI_NL_TRANSPORT_ML, ++ ML_NL_SCSI_SENSE, len); ++ msg->host_no = sdev->host->host_no; ++ msg->channel = sdev->channel; ++ msg->id = sdev->id; ++ msg->lun = sdev->lun; ++ msg->sense = (sshdr->response_code << 24) | (sshdr->sense_key << 16) | ++ (sshdr->asc << 8) | sshdr->ascq; ++ ++ err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_ML_EVENTS, ++ GFP_KERNEL); ++ if (err && (err != -ESRCH)) ++ /* nlmsg_multicast already kfree_skb'd */ ++ goto send_fail; ++ ++ return; ++ ++send_fail_skb: ++ kfree_skb(skb); ++send_fail: ++ sdev_printk(KERN_WARNING, sdev, ++ "Dropped SCSI Msg %02x/%02x/%02x/%02x: err %d\n", ++ sshdr->response_code, sshdr->sense_key, ++ sshdr->asc, sshdr->ascq, err); ++ return; ++} ++#else ++static inline void scsi_post_sense_event(struct scsi_device *sdev, ++ struct scsi_sense_hdr *sshdr) {} ++#endif ++ + /** + * scsi_check_sense - Examine scsi cmd sense + * @scmd: Cmd to have sense checked. +@@ -235,6 +312,8 @@ static int scsi_check_sense(struct scsi_ + if (scsi_sense_is_deferred(&sshdr)) + return NEEDS_RETRY; + ++ scsi_post_sense_event(sdev, &sshdr); ++ + if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh && + sdev->scsi_dh_data->scsi_dh->check_sense) { + int rc; +--- a/include/scsi/scsi_netlink.h ++++ b/include/scsi/scsi_netlink.h +@@ -35,7 +35,8 @@ + /* SCSI Transport Broadcast Groups */ + /* leaving groups 0 and 1 unassigned */ + #define SCSI_NL_GRP_FC_EVENTS (1<<2) /* Group 2 */ +-#define SCSI_NL_GRP_CNT 3 ++#define SCSI_NL_GRP_ML_EVENTS (1<<3) /* Group 3 */ ++#define SCSI_NL_GRP_CNT 4 + + + /* SCSI_TRANSPORT_MSG event message header */ +@@ -56,7 +57,8 @@ struct scsi_nl_hdr { + /* scsi_nl_hdr->transport value */ + #define SCSI_NL_TRANSPORT 0 + #define SCSI_NL_TRANSPORT_FC 1 +-#define SCSI_NL_MAX_TRANSPORTS 2 ++#define SCSI_NL_TRANSPORT_ML 2 ++#define SCSI_NL_MAX_TRANSPORTS 3 + + /* Transport-based scsi_nl_hdr->msgtype values are defined in each transport */ + +--- /dev/null ++++ b/include/scsi/scsi_netlink_ml.h +@@ -0,0 +1,64 @@ ++/* ++ * SCSI Midlayer Netlink Interface ++ * ++ * Copyright (C) 2008 Hannes Reinecke, SuSE Linux Products GmbH ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++#ifndef SCSI_NETLINK_ML_H ++#define SCSI_NETLINK_ML_H ++ ++#include ++ ++/* ++ * This file intended to be included by both kernel and user space ++ */ ++ ++/* ++ * FC Transport Message Types ++ */ ++ /* kernel -> user */ ++#define ML_NL_SCSI_SENSE 0x0100 ++ /* user -> kernel */ ++/* none */ ++ ++ ++/* ++ * Message Structures : ++ */ ++ ++/* macro to round up message lengths to 8byte boundary */ ++#define SCSI_NL_MSGALIGN(len) (((len) + 7) & ~7) ++ ++ ++/* ++ * SCSI Midlayer SCSI Sense messages : ++ * SCSI_NL_SCSI_SENSE ++ * ++ */ ++struct scsi_nl_sense_msg { ++ struct scsi_nl_hdr snlh; /* must be 1st element ! */ ++ uint64_t seconds; ++ u64 id; ++ u64 lun; ++ u16 host_no; ++ u16 channel; ++ u32 sense; ++} __attribute__((aligned(sizeof(uint64_t)))); ++ ++ ++#endif /* SCSI_NETLINK_ML_H */ ++ diff --git a/patches.suse/setuid-dumpable-wrongdir b/patches.suse/setuid-dumpable-wrongdir new file mode 100644 index 0000000..60b77f9 --- /dev/null +++ b/patches.suse/setuid-dumpable-wrongdir @@ -0,0 +1,48 @@ +From: Kurt Garloff +Subject: suid-dumpable ended up in wrong sysctl dir +Patch-mainline: never + +Diffing in sysctl.c is tricky, using more context is recommended. +suid_dumpable ended up in fs/ instead of kernel/ and the reason +is likely a patch with too little context. + +NOTE: This has been in the wrong dir fs/ since it was introduced by +Alan Cox into mainline on 2005-06-23. However, SUSE shipped it +in the correct directory kernel/ in SLES9. + +By now, it's just something that we are going to have to drag along for +a long time until SLES 11/12/13 time frame... + +Signed-off-by: Kurt Garloff + +--- + kernel/sysctl.c | 7 +++++++ + kernel/sysctl_binary.c | 1 + + 2 files changed, 8 insertions(+) + +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -760,6 +760,13 @@ static struct ctl_table kern_table[] = { + .proc_handler = proc_dointvec, + }, + #endif ++ { ++ .procname = "suid_dumpable", ++ .data = &suid_dumpable, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = proc_dointvec, ++ }, + #if defined(CONFIG_S390) && defined(CONFIG_SMP) + { + .procname = "spin_retry", +--- a/kernel/sysctl_binary.c ++++ b/kernel/sysctl_binary.c +@@ -136,6 +136,7 @@ static const struct bin_table bin_kern_t + { CTL_INT, KERN_MAX_LOCK_DEPTH, "max_lock_depth" }, + { CTL_INT, KERN_NMI_WATCHDOG, "nmi_watchdog" }, + { CTL_INT, KERN_PANIC_ON_NMI, "panic_on_unrecovered_nmi" }, ++ { CTL_INT, KERN_SETUID_DUMPABLE, "suid_dumpable" }, + {} + }; + diff --git a/patches.suse/shmall-bigger b/patches.suse/shmall-bigger new file mode 100644 index 0000000..6364e07 --- /dev/null +++ b/patches.suse/shmall-bigger @@ -0,0 +1,50 @@ +From: Chris Mason +Subject: increase defaults for shmmall, shmmax, msgmax and msgmnb +References: 146656 +Patch-mainline: not yet + +The defaults are too small for most users. + +Acked-by: Jeff Mahoney + +--- + include/linux/msg.h | 4 ++-- + include/linux/sem.h | 2 +- + include/linux/shm.h | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +--- a/include/linux/msg.h ++++ b/include/linux/msg.h +@@ -60,8 +60,8 @@ struct msginfo { + #define MSG_MEM_SCALE 32 + + #define MSGMNI 16 /* <= IPCMNI */ /* max # of msg queue identifiers */ +-#define MSGMAX 8192 /* <= INT_MAX */ /* max size of message (bytes) */ +-#define MSGMNB 16384 /* <= INT_MAX */ /* default max size of a message queue */ ++#define MSGMAX 65536 /* <= INT_MAX */ /* max size of message (bytes) */ ++#define MSGMNB 65536 /* <= INT_MAX */ /* default max size of a message queue */ + + /* unused */ + #define MSGPOOL (MSGMNI * MSGMNB / 1024) /* size in kbytes of message pool */ +--- a/include/linux/sem.h ++++ b/include/linux/sem.h +@@ -63,7 +63,7 @@ struct seminfo { + int semaem; + }; + +-#define SEMMNI 128 /* <= IPCMNI max # of semaphore identifiers */ ++#define SEMMNI 1024 /* <= IPCMNI max # of semaphore identifiers */ + #define SEMMSL 250 /* <= 8 000 max num of semaphores per id */ + #define SEMMNS (SEMMNI*SEMMSL) /* <= INT_MAX max # of semaphores in system */ + #define SEMOPM 32 /* <= 1 000 max num of ops per semop call */ +--- a/include/linux/shm.h ++++ b/include/linux/shm.h +@@ -14,7 +14,7 @@ + * be increased by sysctl + */ + +-#define SHMMAX 0x2000000 /* max shared seg size (bytes) */ ++#define SHMMAX ULONG_MAX /* max shared seg size (bytes) */ + #define SHMMIN 1 /* min shared seg size (bytes) */ + #define SHMMNI 4096 /* max num of segs system wide */ + #ifdef __KERNEL__ diff --git a/patches.suse/silent-stack-overflow-2.patch b/patches.suse/silent-stack-overflow-2.patch new file mode 100644 index 0000000..f904f9d --- /dev/null +++ b/patches.suse/silent-stack-overflow-2.patch @@ -0,0 +1,492 @@ +From: Nick Piggin +Subject: avoid silent stack overflow over the heap +Patch-mainline: no +References: bnc#44807 bnc#211997 + +This is a rewrite of Andrea Arcangeli's patch, which implements a stack +guard feature. That is, it prevents the stack from growing right next +to another vma, and prevents other vmas being allocated right next to the +stack. This will cause a segfault rather than the stack silently overwriting +other memory areas (eg. the heap) in the case that the app has a stack +overflow. + +I have rewritten it so as not to require changes to expand_stack prototype, +support for growsup stacks, and support for powerpc and ia64. + + +Signed-off-by: Nick Piggin +--- + arch/ia64/kernel/sys_ia64.c | 11 +++++ + arch/powerpc/mm/slice.c | 82 +++++++++++++++++++++++++----------------- + arch/x86/kernel/sys_x86_64.c | 52 ++++++++++++++++++++------ + include/linux/mm.h | 1 + kernel/sysctl.c | 7 +++ + mm/mmap.c | 83 ++++++++++++++++++++++++++++++++++++------- + 6 files changed, 177 insertions(+), 59 deletions(-) + +--- a/arch/ia64/kernel/sys_ia64.c ++++ b/arch/ia64/kernel/sys_ia64.c +@@ -59,6 +59,8 @@ arch_get_unmapped_area (struct file *fil + start_addr = addr = (addr + align_mask) & ~align_mask; + + for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { ++ unsigned long guard; ++ + /* At this point: (!vma || addr < vma->vm_end). */ + if (TASK_SIZE - len < addr || RGN_MAP_LIMIT - len < REGION_OFFSET(addr)) { + if (start_addr != TASK_UNMAPPED_BASE) { +@@ -68,7 +70,14 @@ arch_get_unmapped_area (struct file *fil + } + return -ENOMEM; + } +- if (!vma || addr + len <= vma->vm_start) { ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(TASK_SIZE - (addr + len), ++ (unsigned long)guard << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) { ++got_it: + /* Remember the address where we stopped this search: */ + mm->free_area_cache = addr + len; + return addr; +--- a/arch/powerpc/mm/slice.c ++++ b/arch/powerpc/mm/slice.c +@@ -94,11 +94,21 @@ static int slice_area_is_free(struct mm_ + unsigned long len) + { + struct vm_area_struct *vma; ++ unsigned long guard; + + if ((mm->task_size - len) < addr) + return 0; + vma = find_vma(mm, addr); +- return (!vma || (addr + len) <= vma->vm_start); ++ if (!vma) ++ return 1; ++ ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(mm->task_size - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) ++ return 1; ++ return 0; + } + + static int slice_low_has_vma(struct mm_struct *mm, unsigned long slice) +@@ -242,8 +252,10 @@ static unsigned long slice_find_area_bot + + full_search: + for (;;) { ++ unsigned long guard; ++ + addr = _ALIGN_UP(addr, 1ul << pshift); +- if ((TASK_SIZE - len) < addr) ++ if ((mm->task_size - len) < addr) + break; + vma = find_vma(mm, addr); + BUG_ON(vma && (addr >= vma->vm_end)); +@@ -256,7 +268,14 @@ full_search: + addr = _ALIGN_UP(addr + 1, 1ul << SLICE_HIGH_SHIFT); + continue; + } +- if (!vma || addr + len <= vma->vm_start) { ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(mm->task_size - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) { ++got_it: + /* + * Remember the place where we stopped the search: + */ +@@ -264,8 +283,8 @@ full_search: + mm->free_area_cache = addr + len; + return addr; + } +- if (use_cache && (addr + mm->cached_hole_size) < vma->vm_start) +- mm->cached_hole_size = vma->vm_start - addr; ++ if (use_cache && (addr + guard + mm->cached_hole_size) < vma->vm_start) ++ mm->cached_hole_size = vma->vm_start - (addr + guard); + addr = vma->vm_end; + } + +@@ -284,37 +303,23 @@ static unsigned long slice_find_area_top + int psize, int use_cache) + { + struct vm_area_struct *vma; +- unsigned long addr; ++ unsigned long start_addr, addr; + struct slice_mask mask; + int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT); + +- /* check if free_area_cache is useful for us */ + if (use_cache) { + if (len <= mm->cached_hole_size) { ++ start_addr = addr = mm->mmap_base; + mm->cached_hole_size = 0; +- mm->free_area_cache = mm->mmap_base; +- } +- +- /* either no address requested or can't fit in requested +- * address hole +- */ +- addr = mm->free_area_cache; +- +- /* make sure it can fit in the remaining address space */ +- if (addr > len) { +- addr = _ALIGN_DOWN(addr - len, 1ul << pshift); +- mask = slice_range_to_mask(addr, len); +- if (slice_check_fit(mask, available) && +- slice_area_is_free(mm, addr, len)) +- /* remember the address as a hint for +- * next time +- */ +- return (mm->free_area_cache = addr); +- } +- } ++ } else ++ start_addr = addr = mm->free_area_cache; ++ } else ++ start_addr = addr = mm->mmap_base; + +- addr = mm->mmap_base; ++full_search: + while (addr > len) { ++ unsigned long guard; ++ + /* Go down by chunk size */ + addr = _ALIGN_DOWN(addr - len, 1ul << pshift); + +@@ -336,7 +341,15 @@ static unsigned long slice_find_area_top + * return with success: + */ + vma = find_vma(mm, addr); +- if (!vma || (addr + len) <= vma->vm_start) { ++ ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(mm->task_size - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) { ++got_it: + /* remember the address as a hint for next time */ + if (use_cache) + mm->free_area_cache = addr; +@@ -344,11 +357,16 @@ static unsigned long slice_find_area_top + } + + /* remember the largest hole we saw so far */ +- if (use_cache && (addr + mm->cached_hole_size) < vma->vm_start) +- mm->cached_hole_size = vma->vm_start - addr; ++ if (use_cache && (addr + guard + mm->cached_hole_size) < vma->vm_start) ++ mm->cached_hole_size = vma->vm_start - (addr + guard); + + /* try just below the current vma->vm_start */ +- addr = vma->vm_start; ++ addr = vma->vm_start - guard; ++ } ++ if (start_addr != mm->mmap_base) { ++ start_addr = addr = mm->mmap_base; ++ mm->cached_hole_size = 0; ++ goto full_search; + } + + /* +--- a/arch/x86/kernel/sys_x86_64.c ++++ b/arch/x86/kernel/sys_x86_64.c +@@ -93,6 +93,8 @@ arch_get_unmapped_area(struct file *filp + + full_search: + for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { ++ unsigned long guard; ++ + /* At this point: (!vma || addr < vma->vm_end). */ + if (end - len < addr) { + /* +@@ -106,15 +108,22 @@ full_search: + } + return -ENOMEM; + } +- if (!vma || addr + len <= vma->vm_start) { ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(end - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) { ++got_it: + /* + * Remember the place where we stopped the search: + */ + mm->free_area_cache = addr + len; + return addr; + } +- if (addr + mm->cached_hole_size < vma->vm_start) +- mm->cached_hole_size = vma->vm_start - addr; ++ if (addr + guard + mm->cached_hole_size < vma->vm_start) ++ mm->cached_hole_size = vma->vm_start - (addr + guard); + + addr = vma->vm_end; + } +@@ -161,34 +170,51 @@ arch_get_unmapped_area_topdown(struct fi + + /* make sure it can fit in the remaining address space */ + if (addr > len) { +- vma = find_vma(mm, addr-len); +- if (!vma || addr <= vma->vm_start) +- /* remember the address as a hint for next time */ +- return mm->free_area_cache = addr-len; ++ unsigned long guard; ++ ++ addr -= len; ++ vma = find_vma(mm, addr); ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(TASK_SIZE - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) ++ goto got_it; + } + + if (mm->mmap_base < len) + goto bottomup; + + addr = mm->mmap_base-len; +- + do { ++ unsigned long guard; + /* + * Lookup failure means no vma is above this address, + * else if new region fits below vma->vm_start, + * return with success: + */ + vma = find_vma(mm, addr); +- if (!vma || addr+len <= vma->vm_start) ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(TASK_SIZE - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) { ++got_it: + /* remember the address as a hint for next time */ +- return mm->free_area_cache = addr; ++ mm->free_area_cache = addr; ++ return addr; ++ } + + /* remember the largest hole we saw so far */ +- if (addr + mm->cached_hole_size < vma->vm_start) +- mm->cached_hole_size = vma->vm_start - addr; ++ if (addr + guard + mm->cached_hole_size < vma->vm_start) ++ mm->cached_hole_size = vma->vm_start - (addr + guard); + + /* try just below the current vma->vm_start */ +- addr = vma->vm_start-len; ++ addr = vma->vm_start - (len + guard); + } while (len < vma->vm_start); + + bottomup: +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -1324,6 +1324,7 @@ unsigned long ra_submit(struct file_ra_s + struct file *filp); + + /* Do stack extension */ ++extern int heap_stack_gap; + extern int expand_stack(struct vm_area_struct *vma, unsigned long address); + #ifdef CONFIG_IA64 + extern int expand_upwards(struct vm_area_struct *vma, unsigned long address); +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -1267,6 +1267,13 @@ static struct ctl_table vm_table[] = { + .mode = 0644, + .proc_handler = scan_unevictable_handler, + }, ++ { ++ .procname = "heap-stack-gap", ++ .data = &heap_stack_gap, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = proc_dointvec, ++ }, + #ifdef CONFIG_MEMORY_FAILURE + { + .procname = "memory_failure_early_kill", +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -86,6 +86,7 @@ int sysctl_overcommit_memory = OVERCOMMI + int sysctl_overcommit_ratio = 50; /* default is 50% */ + int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT; + struct percpu_counter vm_committed_as; ++int heap_stack_gap __read_mostly = 1; + + /* + * Check that a process has enough memory to allocate a new virtual +@@ -1360,6 +1361,8 @@ arch_get_unmapped_area(struct file *filp + + full_search: + for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { ++ unsigned long guard; ++ + /* At this point: (!vma || addr < vma->vm_end). */ + if (TASK_SIZE - len < addr) { + /* +@@ -1374,15 +1377,23 @@ full_search: + } + return -ENOMEM; + } +- if (!vma || addr + len <= vma->vm_start) { ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(TASK_SIZE - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) { ++got_it: + /* + * Remember the place where we stopped the search: + */ + mm->free_area_cache = addr + len; + return addr; + } +- if (addr + mm->cached_hole_size < vma->vm_start) +- mm->cached_hole_size = vma->vm_start - addr; ++ if (addr + guard + mm->cached_hole_size < vma->vm_start) ++ mm->cached_hole_size = vma->vm_start - (addr + guard); ++ + addr = vma->vm_end; + } + } +@@ -1440,34 +1451,51 @@ arch_get_unmapped_area_topdown(struct fi + + /* make sure it can fit in the remaining address space */ + if (addr > len) { +- vma = find_vma(mm, addr-len); +- if (!vma || addr <= vma->vm_start) +- /* remember the address as a hint for next time */ +- return (mm->free_area_cache = addr-len); ++ unsigned long guard; ++ ++ addr -= len; ++ vma = find_vma(mm, addr); ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(TASK_SIZE - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) ++ goto got_it; + } + + if (mm->mmap_base < len) + goto bottomup; + + addr = mm->mmap_base-len; +- + do { ++ unsigned long guard; + /* + * Lookup failure means no vma is above this address, + * else if new region fits below vma->vm_start, + * return with success: + */ + vma = find_vma(mm, addr); +- if (!vma || addr+len <= vma->vm_start) ++ if (!vma) ++ goto got_it; ++ guard = 0; ++ if (vma->vm_flags & VM_GROWSDOWN) ++ guard = min(TASK_SIZE - (addr + len), ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (addr + len + guard <= vma->vm_start) { ++got_it: + /* remember the address as a hint for next time */ +- return (mm->free_area_cache = addr); ++ mm->free_area_cache = addr; ++ return addr; ++ } + + /* remember the largest hole we saw so far */ +- if (addr + mm->cached_hole_size < vma->vm_start) +- mm->cached_hole_size = vma->vm_start - addr; ++ if (addr + guard + mm->cached_hole_size < vma->vm_start) ++ mm->cached_hole_size = vma->vm_start - (addr + guard); + + /* try just below the current vma->vm_start */ +- addr = vma->vm_start-len; ++ addr = vma->vm_start - (len + guard); + } while (len < vma->vm_start); + + bottomup: +@@ -1699,6 +1727,19 @@ int expand_upwards(struct vm_area_struct + /* Somebody else might have raced and expanded it already */ + if (address > vma->vm_end) { + unsigned long size, grow; ++#ifdef CONFIG_STACK_GROWSUP ++ unsigned long guard; ++ struct vm_area_struct *vm_next; ++ ++ error = -ENOMEM; ++ guard = min(TASK_SIZE - address, ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ vm_next = find_vma(vma->vm_mm, address + guard); ++ if (unlikely(vm_next && vm_next != vma)) { ++ /* stack collision with another vma */ ++ goto out_unlock; ++ } ++#endif + + size = address - vma->vm_start; + grow = (address - vma->vm_end) >> PAGE_SHIFT; +@@ -1707,6 +1748,7 @@ int expand_upwards(struct vm_area_struct + if (!error) + vma->vm_end = address; + } ++out_unlock: __maybe_unused + anon_vma_unlock(vma); + return error; + } +@@ -1743,7 +1785,21 @@ static int expand_downwards(struct vm_ar + /* Somebody else might have raced and expanded it already */ + if (address < vma->vm_start) { + unsigned long size, grow; ++ struct vm_area_struct *prev_vma; ++ ++ find_vma_prev(vma->vm_mm, address, &prev_vma); + ++ error = -ENOMEM; ++ if (prev_vma) { ++ unsigned long guard; ++ ++ guard = min(TASK_SIZE - prev_vma->vm_end, ++ (unsigned long)heap_stack_gap << PAGE_SHIFT); ++ if (unlikely(prev_vma->vm_end + guard > address)) { ++ /* stack collision with another vma */ ++ goto out_unlock; ++ } ++ } + size = vma->vm_end - address; + grow = (vma->vm_start - address) >> PAGE_SHIFT; + +@@ -1753,6 +1809,7 @@ static int expand_downwards(struct vm_ar + vma->vm_pgoff -= grow; + } + } ++ out_unlock: + anon_vma_unlock(vma); + return error; + } diff --git a/patches.suse/slab-handle-memoryless-nodes-v2a.patch b/patches.suse/slab-handle-memoryless-nodes-v2a.patch new file mode 100644 index 0000000..d392be7 --- /dev/null +++ b/patches.suse/slab-handle-memoryless-nodes-v2a.patch @@ -0,0 +1,325 @@ +From: Lee Schermerhorn +Subject: slab - handle memoryless nodes V2a +References: bnc#436025, bnc#570492 +Patch-mainline: not yet + +The slab cache, since [apparently] 2.6.21, does not handle memoryless +nodes well. Specifically, the "fast path"-- ____cache_alloc()--will +never succeed, but will be called twice: once speculatively [expected +to succeed] and once in the fallback path. This adds significant +overhead to all kmem cache allocations, incurring a significant +regression relative to earlier kernels [from before slab.c was +reorganized]. + +This patch addresses the regression by modifying slab.c to treat the +first fallback node in a memoryless node's general zonelist as the "slab +local node" -- i.e., the local node for the purpose of slab allocations. +This is, in fact, the node from which all "local" allocations for cpus +attached to a memoryless node will be satisfied. + +The new function numa_slab_nid(gfp_t) replaces all calls to +numa_node_id() in slab.c. numa_slab_id() will simply return +numa_node_id() for nodes with memory, but will return the first +node in the local node's zonelist selected by the gfp flags. + +Effects of the patch: + +We first noticed the effects of the slab reorganization running the +AIM benchmark on a distro based on 2.6.27. The effect is even more +pronounced in the hackbench results. The platform in an HP rx8640 +numa platform, configured with "0% Cell Local Memory". In this +configuration, all memory appears in a "pseudo-node"--an artifact +of the firmware--and is interleaved across all the physical nodes' +memory on a cacheline granularity. All cpus are presented as +attached to memoryless nodes. + +Here are the results of running hackbench at various load levels +with and without the patch on the same platform configured for +0% CLM and "100% CLM". + +Command: hackbench N process 100, for N = 10..100 by 10 + + + 100% CLM 0% CLM +Tasks no with no with + patch patch %diff patch patch %diff + 400 0.246 0.281 14.23% 2.962 0.410 -86.16% + 800 0.418 0.421 0.72% 6.224 0.793 -87.26% + 1200 0.548 0.532 -2.92% 9.058 1.090 -87.97% + 1600 0.654 0.716 9.48% 12.473 1.562 -87.48% + 2000 0.871 0.859 -1.38% 15.484 1.889 -87.80% + 2400 0.994 1.043 4.93% 18.689 2.309 -87.65% + 2800 1.196 1.195 -0.08% 22.069 2.606 -88.19% + 3200 1.322 1.344 1.66% 25.642 2.988 -88.35% + 3600 1.474 1.519 3.05% 28.003 3.418 -87.79% + 4000 1.682 1.750 4.04% 30.887 3.858 -87.51% + +In the 100% CLM case, the regression does not appear, because +all nodes have local memory. Note that the patch has >10% +overhead on the first run, but then varies widely from run +to run [more below]. For the 0%CLM configuration, the patch +reduced the run time by 86-88%. + + +The following runs extend the number of hackbench tasks using: + + hackbench N process 100, for N = 100 to 400 by 20 + +We didn't run the 0%CLM/no-patch runs as they were taking too +long for our liking. We wanted to see how the patched kernel +performed as we extended the range. + + 100% CLM 0% CLM +Tasks no with no with + patch patch %diff patch patch %diff + 4800 1.879 2.117 12.67% not-run 4.458 + 5600 2.100 2.352 12.00% not-run 5.207 + 6400 2.532 2.447 -3.36% not-run 5.669 + 8000 2.799 2.792 -0.25% not-run 6.651 + 8000 3.244 3.030 -6.60% not-run 7.366 + 8800 3.282 3.550 8.17% not-run 8.169 + 9600 3.595 3.738 3.98% not-run 8.582 +10400 3.811 4.045 6.14% not-run 9.705 +11200 4.090 4.162 1.76% not-run 9.760 +12000 4.408 4.419 0.25% not-run 10.141 +12800 4.665 4.787 2.62% not-run 11.628 +13600 5.095 5.069 -0.51% not-run 11.735 +14400 5.347 5.464 2.19% not-run 12.621 +15200 5.620 5.831 3.75% not-run 13.452 +16000 5.870 6.161 4.96% not-run 14.069 + +The 0% CLM configuration with the patch performs worse than +the 100% CLM configuration. In the 0% CLM case we had 64 +ia64 cores beating on a single zone in the interleaved +memory-only pseudo-node. In the 100% CLM case, we have 16 +cores allocating memory locally to each of 4 nodes, +demonstating the difference between [pseudo-]SMP and NUMA +behavior. + +Note, again, that the first run[s] have higher % difference +between the patched and unpatched kernels for the 100% CLM +config, and then vary quite a bit run to run. To get a feel +for the average overhead, we ran 40 runs at the 16000 task +load point with more interations to increase the runtime +per run: + + hackbench 400 process 200 + +These were run on the 100% CLM configuration, as this best represents +most NUMA platforms: + + No patch with Patch %diff +Average of 40: 9.796 9.857 0.623 + + +Signed-off-by: Lee Schermerhorn +Acked-by: Nick Piggin + + mm/slab.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 78 insertions(+), 13 deletions(-) + +--- a/mm/slab.c ++++ b/mm/slab.c +@@ -305,7 +305,7 @@ struct kmem_list3 { + struct array_cache **alien; /* on other nodes */ + unsigned long next_reap; /* updated without locking */ + int free_touched; /* updated without locking */ +-}; ++} __attribute__((aligned(sizeof(long)))); + + /* + * Need this for bootstrapping a per node allocator. +@@ -968,6 +968,11 @@ static int transfer_objects(struct array + #define drain_alien_cache(cachep, alien) do { } while (0) + #define reap_alien(cachep, l3) do { } while (0) + ++static inline int numa_slab_nid(struct kmem_cache *cachep, gfp_t flags) ++{ ++ return 0; ++} ++ + static inline struct array_cache **alloc_alien_cache(int node, int limit, gfp_t gfp) + { + return (struct array_cache **)BAD_ALIEN_MAGIC; +@@ -999,6 +1004,64 @@ static inline void *____cache_alloc_node + static void *____cache_alloc_node(struct kmem_cache *, gfp_t, int); + static void *alternate_node_alloc(struct kmem_cache *, gfp_t); + ++/* ++ * slow path for numa_slab_nid(), below ++ */ ++static noinline int __numa_slab_nid(struct kmem_cache *cachep, ++ int node, gfp_t flags) ++{ ++ struct zonelist *zonelist; ++ struct zone *zone; ++ enum zone_type highest_zoneidx = gfp_zone(flags); ++ ++ if (likely(node_state(node, N_NORMAL_MEMORY))) ++ return node; ++ ++ /* ++ * memoryless node: consult its zonelist. ++ * Cache the fallback node, if cache pointer provided. ++ */ ++ zonelist = &NODE_DATA(node)->node_zonelists[0]; ++ (void)first_zones_zonelist(zonelist, highest_zoneidx, ++ NULL, ++ &zone); ++ if (cachep) ++ cachep->nodelists[node] = ++ (struct kmem_list3 *)((unsigned long)zone->node << 1 | 1); ++ return zone->node; ++} ++ ++/* ++ * "Local" node for slab is first node in zonelist with memory. ++ * For nodes with memory this will be the actual local node. ++ * ++ * Use nodelist[numa_node_id()] to cache the fallback node for ++ * memoryless nodes. We'll be loading that member soon anyway, ++ * or already have, when called for cache refill, ... Use low ++ * bit of "pointer" as flag for "memoryless_node", indicating ++ * that the fallback nodes is stored here [<<1]. ++ */ ++#define memoryless_node(L3L) ((L3L) & 1) ++static inline int numa_slab_nid(struct kmem_cache *cachep, gfp_t flags) ++{ ++ int node = numa_node_id(); ++ ++ if (likely(cachep)){ ++ unsigned long l3l = (unsigned long)cachep->nodelists[node]; ++ ++ if (likely(l3l)) { ++ if (unlikely(memoryless_node(l3l))) ++ node = (int)(l3l >> 1); ++ return node; ++ } ++ } ++ ++ /* ++ * !cachep || !l3l - the slow path ++ */ ++ return __numa_slab_nid(cachep, node, flags); ++} ++ + static struct array_cache **alloc_alien_cache(int node, int limit, gfp_t gfp) + { + struct array_cache **ac_ptr; +@@ -1098,7 +1161,7 @@ static inline int cache_free_alien(struc + struct array_cache *alien = NULL; + int node; + +- node = numa_node_id(); ++ node = numa_slab_nid(cachep, GFP_KERNEL); + + /* + * Make sure we are not freeing a object from another node to the array +@@ -1443,7 +1506,7 @@ void __init kmem_cache_init(void) + * 6) Resize the head arrays of the kmalloc caches to their final sizes. + */ + +- node = numa_node_id(); ++ node = numa_slab_nid(NULL, GFP_KERNEL); + + /* 1) create the cache_cache */ + INIT_LIST_HEAD(&cache_chain); +@@ -2079,7 +2142,7 @@ static int __init_refok setup_cpu_cache( + } + } + } +- cachep->nodelists[numa_node_id()]->next_reap = ++ cachep->nodelists[numa_slab_nid(cachep, GFP_KERNEL)]->next_reap = + jiffies + REAPTIMEOUT_LIST3 + + ((unsigned long)cachep) % REAPTIMEOUT_LIST3; + +@@ -2411,7 +2474,7 @@ static void check_spinlock_acquired(stru + { + #ifdef CONFIG_SMP + check_irq_off(); +- assert_spin_locked(&cachep->nodelists[numa_node_id()]->list_lock); ++ assert_spin_locked(&cachep->nodelists[numa_slab_nid(cachep, GFP_KERNEL)]->list_lock); + #endif + } + +@@ -2438,7 +2501,7 @@ static void do_drain(void *arg) + { + struct kmem_cache *cachep = arg; + struct array_cache *ac; +- int node = numa_node_id(); ++ int node = numa_slab_nid(cachep, GFP_KERNEL); + + check_irq_off(); + ac = cpu_cache_get(cachep); +@@ -2975,7 +3038,7 @@ static void *cache_alloc_refill(struct k + + retry: + check_irq_off(); +- node = numa_node_id(); ++ node = numa_slab_nid(cachep, flags); + if (unlikely(must_refill)) + goto force_grow; + ac = cpu_cache_get(cachep); +@@ -3185,7 +3248,7 @@ static void *alternate_node_alloc(struct + + if (in_interrupt() || (flags & __GFP_THISNODE)) + return NULL; +- nid_alloc = nid_here = numa_node_id(); ++ nid_alloc = nid_here = numa_slab_nid(cachep, flags); + if (cpuset_do_slab_mem_spread() && (cachep->flags & SLAB_MEM_SPREAD)) + nid_alloc = cpuset_mem_spread_node(); + else if (current->mempolicy) +@@ -3360,6 +3423,7 @@ __cache_alloc_node(struct kmem_cache *ca + { + unsigned long save_flags; + void *ptr; ++ int slab_node = numa_slab_nid(cachep, flags); + + flags &= gfp_allowed_mask; + +@@ -3372,7 +3436,7 @@ __cache_alloc_node(struct kmem_cache *ca + local_irq_save(save_flags); + + if (nodeid == -1) +- nodeid = numa_node_id(); ++ nodeid = slab_node; + + if (unlikely(!cachep->nodelists[nodeid])) { + /* Node not bootstrapped yet */ +@@ -3380,7 +3444,7 @@ __cache_alloc_node(struct kmem_cache *ca + goto out; + } + +- if (nodeid == numa_node_id()) { ++ if (nodeid == slab_node) { + /* + * Use the locally cached objects if possible. + * However ____cache_alloc does not allow fallback +@@ -3425,7 +3489,8 @@ __do_cache_alloc(struct kmem_cache *cach + * ____cache_alloc_node() knows how to locate memory on other nodes + */ + if (!objp) +- objp = ____cache_alloc_node(cache, flags, numa_node_id()); ++ objp = ____cache_alloc_node(cache, flags, ++ numa_slab_nid(cache, flags)); + + out: + return objp; +@@ -3522,7 +3587,7 @@ static void cache_flusharray(struct kmem + { + int batchcount; + struct kmem_list3 *l3; +- int node = numa_node_id(); ++ int node = numa_slab_nid(cachep, GFP_KERNEL); + + batchcount = ac->batchcount; + #if DEBUG +@@ -4172,7 +4237,7 @@ static void cache_reap(struct work_struc + { + struct kmem_cache *searchp; + struct kmem_list3 *l3; +- int node = numa_node_id(); ++ int node = numa_slab_nid(NULL, GFP_KERNEL); + struct delayed_work *work = to_delayed_work(w); + + if (!mutex_trylock(&cache_chain_mutex)) diff --git a/patches.suse/stack-unwind b/patches.suse/stack-unwind new file mode 100644 index 0000000..6364791 --- /dev/null +++ b/patches.suse/stack-unwind @@ -0,0 +1,2170 @@ +Subject: DWARF2 EH-frame based stack unwinding +From: jbeulich@novell.com +Patch-mainline: no + +This includes reverting f1883f86dea84fe47a71a39fc1afccc005915ed8. + +Update Jan 17 2009 jeffm: +- Something in 2.6.29-rc1 tweaked the frame pointer code somehow, so I fixed + that up. + +--- + Makefile | 5 + arch/x86/Kconfig | 2 + arch/x86/Makefile | 2 + arch/x86/include/asm/system.h | 10 + arch/x86/include/asm/unwind.h | 163 ++++ + arch/x86/kernel/dumpstack.c | 89 ++ + arch/x86/kernel/dumpstack.h | 4 + arch/x86/kernel/dumpstack_32.c | 18 + arch/x86/kernel/dumpstack_64.c | 20 + arch/x86/kernel/entry_32.S | 35 + + arch/x86/kernel/entry_64.S | 34 + include/asm-generic/vmlinux.lds.h | 22 + include/linux/module.h | 3 + include/linux/unwind.h | 135 +++ + init/main.c | 3 + kernel/Makefile | 1 + kernel/module.c | 15 + kernel/unwind.c | 1303 ++++++++++++++++++++++++++++++++++++++ + lib/Kconfig.debug | 18 + 19 files changed, 1866 insertions(+), 16 deletions(-) + +--- a/Makefile ++++ b/Makefile +@@ -570,6 +570,11 @@ else + KBUILD_CFLAGS += -fomit-frame-pointer + endif + ++ifdef CONFIG_UNWIND_INFO ++KBUILD_CFLAGS += -fasynchronous-unwind-tables ++LDFLAGS_vmlinux += --eh-frame-hdr ++endif ++ + ifdef CONFIG_DEBUG_INFO + KBUILD_CFLAGS += -g + KBUILD_AFLAGS += -gdwarf-2 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -487,7 +487,7 @@ config X86_ES7000 + config SCHED_OMIT_FRAME_POINTER + def_bool y + prompt "Single-depth WCHAN output" +- depends on X86 ++ depends on X86 && !STACK_UNWIND + ---help--- + Calculate simpler /proc//wchan values. If this option + is disabled then wchan values will recurse back to the +--- a/arch/x86/Makefile ++++ b/arch/x86/Makefile +@@ -105,7 +105,9 @@ KBUILD_CFLAGS += -pipe + # Workaround for a gcc prelease that unfortunately was shipped in a suse release + KBUILD_CFLAGS += -Wno-sign-compare + # ++ifneq ($(CONFIG_UNWIND_INFO),y) + KBUILD_CFLAGS += -fno-asynchronous-unwind-tables ++endif + # prevent gcc from generating any FP code by mistake + KBUILD_CFLAGS += $(call cc-option,-mno-sse -mno-mmx -mno-sse2 -mno-3dnow,) + +--- a/arch/x86/include/asm/system.h ++++ b/arch/x86/include/asm/system.h +@@ -123,12 +123,22 @@ do { \ + #define __switch_canary_iparam + #endif /* CC_STACKPROTECTOR */ + ++/* The stack unwind code needs this but it pollutes traces otherwise */ ++#ifdef CONFIG_UNWIND_INFO ++#define THREAD_RETURN_SYM \ ++ ".globl thread_return\n" \ ++ "thread_return:\n\t" ++#else ++#define THREAD_RETURN_SYM ++#endif ++ + /* Save restore flags to clear handle leaking NT */ + #define switch_to(prev, next, last) \ + asm volatile(SAVE_CONTEXT \ + "movq %%rsp,%P[threadrsp](%[prev])\n\t" /* save RSP */ \ + "movq %P[threadrsp](%[next]),%%rsp\n\t" /* restore RSP */ \ + "call __switch_to\n\t" \ ++ THREAD_RETURN_SYM \ + "movq "__percpu_arg([current_task])",%%rsi\n\t" \ + __switch_canary \ + "movq %P[thread_info](%%rsi),%%r8\n\t" \ +--- /dev/null ++++ b/arch/x86/include/asm/unwind.h +@@ -0,0 +1,163 @@ ++#ifndef _ASM_X86_UNWIND_H ++#define _ASM_X86_UNWIND_H ++ ++/* ++ * Copyright (C) 2002-2009 Novell, Inc. ++ * Jan Beulich ++ * This code is released under version 2 of the GNU GPL. ++ */ ++ ++#ifdef CONFIG_STACK_UNWIND ++ ++#include ++#include ++#include ++ ++struct unwind_frame_info ++{ ++ struct pt_regs regs; ++ struct task_struct *task; ++ unsigned call_frame:1; ++}; ++ ++#define UNW_PC(frame) (frame)->regs.ip ++#define UNW_SP(frame) (frame)->regs.sp ++#ifdef CONFIG_FRAME_POINTER ++#define UNW_FP(frame) (frame)->regs.bp ++#define FRAME_LINK_OFFSET 0 ++#define STACK_BOTTOM(tsk) STACK_LIMIT((tsk)->thread.sp0) ++#define TSK_STACK_TOP(tsk) ((tsk)->thread.sp0) ++#else ++#define UNW_FP(frame) ((void)(frame), 0UL) ++#endif ++/* On x86-64, might need to account for the special exception and interrupt ++ handling stacks here, since normally ++ EXCEPTION_STACK_ORDER < THREAD_ORDER < IRQSTACK_ORDER, ++ but the construct is needed only for getting across the stack switch to ++ the interrupt stack - thus considering the IRQ stack itself is unnecessary, ++ and the overhead of comparing against all exception handling stacks seems ++ not desirable. */ ++#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1)) ++ ++#ifdef CONFIG_X86_64 ++ ++#include ++ ++#define FRAME_RETADDR_OFFSET 8 ++ ++#define UNW_REGISTER_INFO \ ++ PTREGS_INFO(ax), \ ++ PTREGS_INFO(dx), \ ++ PTREGS_INFO(cx), \ ++ PTREGS_INFO(bx), \ ++ PTREGS_INFO(si), \ ++ PTREGS_INFO(di), \ ++ PTREGS_INFO(bp), \ ++ PTREGS_INFO(sp), \ ++ PTREGS_INFO(r8), \ ++ PTREGS_INFO(r9), \ ++ PTREGS_INFO(r10), \ ++ PTREGS_INFO(r11), \ ++ PTREGS_INFO(r12), \ ++ PTREGS_INFO(r13), \ ++ PTREGS_INFO(r14), \ ++ PTREGS_INFO(r15), \ ++ PTREGS_INFO(ip) ++ ++#else /* X86_32 */ ++ ++#include ++ ++#define FRAME_RETADDR_OFFSET 4 ++ ++#define UNW_REGISTER_INFO \ ++ PTREGS_INFO(ax), \ ++ PTREGS_INFO(cx), \ ++ PTREGS_INFO(dx), \ ++ PTREGS_INFO(bx), \ ++ PTREGS_INFO(sp), \ ++ PTREGS_INFO(bp), \ ++ PTREGS_INFO(si), \ ++ PTREGS_INFO(di), \ ++ PTREGS_INFO(ip) ++ ++#endif ++ ++#define UNW_DEFAULT_RA(raItem, dataAlign) \ ++ ((raItem).where == Memory && \ ++ !((raItem).value * (dataAlign) + sizeof(void *))) ++ ++static inline void arch_unw_init_frame_info(struct unwind_frame_info *info, ++ /*const*/ struct pt_regs *regs) ++{ ++#ifdef CONFIG_X86_64 ++ info->regs = *regs; ++#else ++ if (user_mode_vm(regs)) ++ info->regs = *regs; ++ else { ++ memcpy(&info->regs, regs, offsetof(struct pt_regs, sp)); ++ info->regs.sp = (unsigned long)®s->sp; ++ info->regs.ss = __KERNEL_DS; ++ } ++#endif ++} ++ ++static inline void arch_unw_init_blocked(struct unwind_frame_info *info) ++{ ++#ifdef CONFIG_X86_64 ++ extern const char thread_return[]; ++ ++ memset(&info->regs, 0, sizeof(info->regs)); ++ info->regs.ip = (unsigned long)thread_return; ++ info->regs.cs = __KERNEL_CS; ++ probe_kernel_address(info->task->thread.sp, info->regs.bp); ++ info->regs.sp = info->task->thread.sp; ++ info->regs.ss = __KERNEL_DS; ++#else ++ memset(&info->regs, 0, sizeof(info->regs)); ++ info->regs.ip = info->task->thread.ip; ++ info->regs.cs = __KERNEL_CS; ++ probe_kernel_address(info->task->thread.sp, info->regs.bp); ++ info->regs.sp = info->task->thread.sp; ++ info->regs.ss = __KERNEL_DS; ++ info->regs.ds = __USER_DS; ++ info->regs.es = __USER_DS; ++#endif ++} ++ ++extern asmlinkage int ++arch_unwind_init_running(struct unwind_frame_info *, ++ unwind_callback_fn, ++ const struct stacktrace_ops *, void *data); ++ ++static inline int arch_unw_user_mode(/*const*/ struct unwind_frame_info *info) ++{ ++#ifdef CONFIG_X86_64 ++ return user_mode(&info->regs) ++ || (long)info->regs.ip >= 0 ++ || (info->regs.ip >= VSYSCALL_START && info->regs.ip < VSYSCALL_END) ++ || (long)info->regs.sp >= 0; ++#else ++ return user_mode_vm(&info->regs) ++ || info->regs.ip < PAGE_OFFSET ++ || (info->regs.ip >= __fix_to_virt(FIX_VDSO) ++ && info->regs.ip < __fix_to_virt(FIX_VDSO) + PAGE_SIZE) ++ || info->regs.sp < PAGE_OFFSET; ++#endif ++} ++ ++#else ++ ++#define UNW_PC(frame) ((void)(frame), 0UL) ++#define UNW_SP(frame) ((void)(frame), 0UL) ++#define UNW_FP(frame) ((void)(frame), 0UL) ++ ++static inline int arch_unw_user_mode(const void *info) ++{ ++ return 0; ++} ++ ++#endif ++ ++#endif /* _ASM_X86_UNWIND_H */ +--- a/arch/x86/kernel/dumpstack.c ++++ b/arch/x86/kernel/dumpstack.c +@@ -20,6 +20,7 @@ + #endif + + #include ++#include + + #include "dumpstack.h" + +@@ -27,6 +28,11 @@ int panic_on_unrecovered_nmi; + int panic_on_io_nmi; + unsigned int code_bytes = 64; + int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE; ++#ifdef CONFIG_STACK_UNWIND ++static int call_trace = 1; ++#else ++#define call_trace (-1) ++#endif + static int die_counter; + + void printk_address(unsigned long address, int reliable) +@@ -66,6 +72,71 @@ print_ftrace_graph_addr(unsigned long ad + { } + #endif + ++int asmlinkage dump_trace_unwind(struct unwind_frame_info *info, ++ const struct stacktrace_ops *ops, void *data) ++{ ++ int n = 0; ++#ifdef CONFIG_UNWIND_INFO ++ unsigned long sp = UNW_SP(info); ++ ++ if (arch_unw_user_mode(info)) ++ return -1; ++ while (unwind(info) == 0 && UNW_PC(info)) { ++ n++; ++ ops->address(data, UNW_PC(info), 1); ++ if (arch_unw_user_mode(info)) ++ break; ++ if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1)) ++ && sp > UNW_SP(info)) ++ break; ++ sp = UNW_SP(info); ++ } ++#endif ++ return n; ++} ++ ++int try_stack_unwind(struct task_struct *task, struct pt_regs *regs, ++ unsigned long **stack, unsigned long *bp, ++ const struct stacktrace_ops *ops, void *data) ++{ ++#ifdef CONFIG_UNWIND_INFO ++ int unw_ret = 0; ++ struct unwind_frame_info info; ++ if (call_trace < 0) ++ return 0; ++ ++ if (regs) { ++ if (unwind_init_frame_info(&info, task, regs) == 0) ++ unw_ret = dump_trace_unwind(&info, ops, data); ++ } else if (task == current) ++ unw_ret = unwind_init_running(&info, dump_trace_unwind, ops, data); ++ else { ++ if (unwind_init_blocked(&info, task) == 0) ++ unw_ret = dump_trace_unwind(&info, ops, data); ++ } ++ if (unw_ret > 0) { ++ if (call_trace == 1 && !arch_unw_user_mode(&info)) { ++ ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n", ++ UNW_PC(&info)); ++ if ((long)UNW_SP(&info) < 0) { ++ ops->warning(data, "Leftover inexact backtrace:\n"); ++ *stack = (unsigned long *)UNW_SP(&info); ++ if (!stack) { ++ *bp = UNW_FP(&info); ++ return -1; ++ } ++ } else ++ ops->warning(data, "Full inexact backtrace again:\n"); ++ } else if (call_trace >= 1) { ++ return -1; ++ } else ++ ops->warning(data, "Full inexact backtrace again:\n"); ++ } else ++ ops->warning(data, "Inexact backtrace:\n"); ++#endif ++ return 0; ++} ++ + /* + * x86-64 can have up to three kernel stacks: + * process stack +@@ -396,3 +467,21 @@ static int __init code_bytes_setup(char + return 1; + } + __setup("code_bytes=", code_bytes_setup); ++ ++#ifdef CONFIG_STACK_UNWIND ++static int __init call_trace_setup(char *s) ++{ ++ if (!s) ++ return -EINVAL; ++ if (strcmp(s, "old") == 0) ++ call_trace = -1; ++ else if (strcmp(s, "both") == 0) ++ call_trace = 0; ++ else if (strcmp(s, "newfallback") == 0) ++ call_trace = 1; ++ else if (strcmp(s, "new") == 0) ++ call_trace = 2; ++ return 0; ++} ++early_param("call_trace", call_trace_setup); ++#endif +--- a/arch/x86/kernel/dumpstack.h ++++ b/arch/x86/kernel/dumpstack.h +@@ -22,6 +22,10 @@ extern void + show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *sp, unsigned long bp, char *log_lvl); + ++int try_stack_unwind(struct task_struct *task, struct pt_regs *regs, ++ unsigned long **stack, unsigned long *bp, ++ const struct stacktrace_ops *ops, void *data); ++ + extern unsigned int code_bytes; + + /* The form of the top of the frame on the stack */ +--- a/arch/x86/kernel/dumpstack_32.c ++++ b/arch/x86/kernel/dumpstack_32.c +@@ -28,14 +28,6 @@ void dump_trace(struct task_struct *task + if (!task) + task = current; + +- if (!stack) { +- unsigned long dummy; +- +- stack = &dummy; +- if (task && task != current) +- stack = (unsigned long *)task->thread.sp; +- } +- + #ifdef CONFIG_FRAME_POINTER + if (!bp) { + if (task == current) { +@@ -48,6 +40,16 @@ void dump_trace(struct task_struct *task + } + #endif + ++ if (try_stack_unwind(task, regs, &stack, &bp, ops, data)) ++ return; ++ ++ if (!stack) { ++ unsigned long dummy; ++ stack = &dummy; ++ if (task && task != current) ++ stack = (unsigned long *)task->thread.sp; ++ } ++ + for (;;) { + struct thread_info *context; + +--- a/arch/x86/kernel/dumpstack_64.c ++++ b/arch/x86/kernel/dumpstack_64.c +@@ -14,6 +14,7 @@ + #include + #include + ++#include + #include + + #include "dumpstack.h" +@@ -154,13 +155,6 @@ void dump_trace(struct task_struct *task + if (!task) + task = current; + +- if (!stack) { +- unsigned long dummy; +- stack = &dummy; +- if (task && task != current) +- stack = (unsigned long *)task->thread.sp; +- } +- + #ifdef CONFIG_FRAME_POINTER + if (!bp) { + if (task == current) { +@@ -173,6 +167,18 @@ void dump_trace(struct task_struct *task + } + #endif + ++ if (try_stack_unwind(task, regs, &stack, &bp, ops, data)) { ++ put_cpu(); ++ return; ++ } ++ ++ if (!stack) { ++ unsigned long dummy; ++ stack = &dummy; ++ if (task && task != current) ++ stack = (unsigned long *)task->thread.sp; ++ } ++ + /* + * Print function call entries in all stacks, starting at the + * current stack address. If the stacks consist of nested +--- a/arch/x86/kernel/entry_32.S ++++ b/arch/x86/kernel/entry_32.S +@@ -1064,6 +1064,41 @@ END(spurious_interrupt_bug) + */ + .popsection + ++#ifdef CONFIG_STACK_UNWIND ++ENTRY(arch_unwind_init_running) ++ CFI_STARTPROC ++ movl 4(%esp), %edx ++ movl (%esp), %ecx ++ leal 4(%esp), %eax ++ movl %ebx, PT_EBX(%edx) ++ xorl %ebx, %ebx ++ movl %ebx, PT_ECX(%edx) ++ movl %ebx, PT_EDX(%edx) ++ movl %esi, PT_ESI(%edx) ++ movl %edi, PT_EDI(%edx) ++ movl %ebp, PT_EBP(%edx) ++ movl %ebx, PT_EAX(%edx) ++ movl $__USER_DS, PT_DS(%edx) ++ movl $__USER_DS, PT_ES(%edx) ++ movl $__KERNEL_PERCPU, PT_FS(%edx) ++ movl $__KERNEL_STACK_CANARY, PT_GS(%edx) ++ movl %eax, PT_OLDESP(%edx) ++ movl 16(%esp), %eax ++ movl %ebx, PT_ORIG_EAX(%edx) ++ movl %ecx, PT_EIP(%edx) ++ movl 12(%esp), %ecx ++ movl $__KERNEL_CS, PT_CS(%edx) ++ movl %eax, 12(%esp) ++ movl 8(%esp), %eax ++ movl %ecx, 8(%esp) ++ movl %ebx, PT_EFLAGS(%edx) ++ movl PT_EBX(%edx), %ebx ++ movl $__KERNEL_DS, PT_OLDSS(%edx) ++ jmpl *%eax ++ CFI_ENDPROC ++ENDPROC(arch_unwind_init_running) ++#endif ++ + ENTRY(kernel_thread_helper) + pushl $0 # fake return address for unwinder + CFI_STARTPROC +--- a/arch/x86/kernel/entry_64.S ++++ b/arch/x86/kernel/entry_64.S +@@ -1232,6 +1232,40 @@ ENTRY(call_softirq) + CFI_ENDPROC + END(call_softirq) + ++#ifdef CONFIG_STACK_UNWIND ++ENTRY(arch_unwind_init_running) ++ CFI_STARTPROC ++ movq %r15, R15(%rdi) ++ movq %r14, R14(%rdi) ++ xchgq %rsi, %rdx ++ movq %r13, R13(%rdi) ++ movq %r12, R12(%rdi) ++ xorl %eax, %eax ++ movq %rbp, RBP(%rdi) ++ movq %rbx, RBX(%rdi) ++ movq (%rsp), %r9 ++ xchgq %rdx, %rcx ++ movq %rax, R11(%rdi) ++ movq %rax, R10(%rdi) ++ movq %rax, R9(%rdi) ++ movq %rax, R8(%rdi) ++ movq %rax, RAX(%rdi) ++ movq %rax, RCX(%rdi) ++ movq %rax, RDX(%rdi) ++ movq %rax, RSI(%rdi) ++ movq %rax, RDI(%rdi) ++ movq %rax, ORIG_RAX(%rdi) ++ movq %r9, RIP(%rdi) ++ leaq 8(%rsp), %r9 ++ movq $__KERNEL_CS, CS(%rdi) ++ movq %rax, EFLAGS(%rdi) ++ movq %r9, RSP(%rdi) ++ movq $__KERNEL_DS, SS(%rdi) ++ jmpq *%rcx ++ CFI_ENDPROC ++END(arch_unwind_init_running) ++#endif ++ + #ifdef CONFIG_XEN + zeroentry xen_hypervisor_callback xen_do_hypervisor_callback + +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -341,6 +341,8 @@ + MEM_KEEP(exit.rodata) \ + } \ + \ ++ EH_FRAME \ ++ \ + /* Built-in module parameters. */ \ + __param : AT(ADDR(__param) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start___param) = .; \ +@@ -758,3 +760,23 @@ + BSS(bss_align) \ + . = ALIGN(stop_align); \ + VMLINUX_SYMBOL(__bss_stop) = .; ++ ++#ifdef CONFIG_STACK_UNWIND ++#define EH_FRAME \ ++ /* Unwind data binary search table */ \ ++ . = ALIGN(8); \ ++ .eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start_unwind_hdr) = .; \ ++ *(.eh_frame_hdr) \ ++ VMLINUX_SYMBOL(__end_unwind_hdr) = .; \ ++ } \ ++ /* Unwind data */ \ ++ . = ALIGN(8); \ ++ .eh_frame : AT(ADDR(.eh_frame) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start_unwind) = .; \ ++ *(.eh_frame) \ ++ VMLINUX_SYMBOL(__end_unwind) = .; \ ++ } ++#else ++#define EH_FRAME ++#endif +--- a/include/linux/module.h ++++ b/include/linux/module.h +@@ -301,6 +301,9 @@ struct module + /* The size of the executable code in each section. */ + unsigned int init_text_size, core_text_size; + ++ /* The handle returned from unwind_add_table. */ ++ void *unwind_info; ++ + /* Arch-specific module values */ + struct mod_arch_specific arch; + +--- /dev/null ++++ b/include/linux/unwind.h +@@ -0,0 +1,135 @@ ++#ifndef _LINUX_UNWIND_H ++#define _LINUX_UNWIND_H ++ ++/* ++ * Copyright (C) 2002-2009 Novell, Inc. ++ * Jan Beulich ++ * This code is released under version 2 of the GNU GPL. ++ * ++ * A simple API for unwinding kernel stacks. This is used for ++ * debugging and error reporting purposes. The kernel doesn't need ++ * full-blown stack unwinding with all the bells and whistles, so there ++ * is not much point in implementing the full Dwarf2 unwind API. ++ */ ++ ++#include ++ ++struct module; ++struct stacktrace_ops; ++struct unwind_frame_info; ++ ++typedef asmlinkage int (*unwind_callback_fn)(struct unwind_frame_info *, ++ const struct stacktrace_ops *, ++ void *); ++ ++#ifdef CONFIG_STACK_UNWIND ++ ++#include ++#include ++ ++#ifndef ARCH_UNWIND_SECTION_NAME ++#define ARCH_UNWIND_SECTION_NAME ".eh_frame" ++#endif ++ ++/* ++ * Initialize unwind support. ++ */ ++extern void unwind_init(void); ++extern void unwind_setup(void); ++ ++#ifdef CONFIG_MODULES ++ ++extern void *unwind_add_table(struct module *, ++ const void *table_start, ++ unsigned long table_size); ++ ++extern void unwind_remove_table(void *handle, int init_only); ++ ++#endif ++ ++extern int unwind_init_frame_info(struct unwind_frame_info *, ++ struct task_struct *, ++ /*const*/ struct pt_regs *); ++ ++/* ++ * Prepare to unwind a blocked task. ++ */ ++extern int unwind_init_blocked(struct unwind_frame_info *, ++ struct task_struct *); ++ ++/* ++ * Prepare to unwind the currently running thread. ++ */ ++extern int unwind_init_running(struct unwind_frame_info *, ++ unwind_callback_fn, ++ const struct stacktrace_ops *, ++ void *data); ++ ++/* ++ * Unwind to previous to frame. Returns 0 if successful, negative ++ * number in case of an error. ++ */ ++extern int unwind(struct unwind_frame_info *); ++ ++/* ++ * Unwind until the return pointer is in user-land (or until an error ++ * occurs). Returns 0 if successful, negative number in case of ++ * error. ++ */ ++extern int unwind_to_user(struct unwind_frame_info *); ++ ++#else /* CONFIG_STACK_UNWIND */ ++ ++struct unwind_frame_info {}; ++ ++static inline void unwind_init(void) {} ++static inline void unwind_setup(void) {} ++ ++#ifdef CONFIG_MODULES ++ ++static inline void *unwind_add_table(struct module *mod, ++ const void *table_start, ++ unsigned long table_size) ++{ ++ return NULL; ++} ++ ++#endif ++ ++static inline void unwind_remove_table(void *handle, int init_only) ++{ ++} ++ ++static inline int unwind_init_frame_info(struct unwind_frame_info *info, ++ struct task_struct *tsk, ++ const struct pt_regs *regs) ++{ ++ return -ENOSYS; ++} ++ ++static inline int unwind_init_blocked(struct unwind_frame_info *info, ++ struct task_struct *tsk) ++{ ++ return -ENOSYS; ++} ++ ++static inline int unwind_init_running(struct unwind_frame_info *info, ++ unwind_callback_fn cb, ++ const struct stacktrace_ops *ops, ++ void *data) ++{ ++ return -ENOSYS; ++} ++ ++static inline int unwind(struct unwind_frame_info *info) ++{ ++ return -ENOSYS; ++} ++ ++static inline int unwind_to_user(struct unwind_frame_info *info) ++{ ++ return -ENOSYS; ++} ++ ++#endif /* CONFIG_STACK_UNWIND */ ++#endif /* _LINUX_UNWIND_H */ +--- a/init/main.c ++++ b/init/main.c +@@ -50,6 +50,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -560,6 +561,7 @@ asmlinkage void __init start_kernel(void + * Need to run as early as possible, to initialize the + * lockdep hash: + */ ++ unwind_init(); + lockdep_init(); + debug_objects_early_init(); + +@@ -586,6 +588,7 @@ asmlinkage void __init start_kernel(void + setup_arch(&command_line); + mm_init_owner(&init_mm, &init_task); + setup_command_line(command_line); ++ unwind_setup(); + setup_nr_cpu_ids(); + setup_per_cpu_areas(); + smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -53,6 +53,7 @@ obj-$(CONFIG_PROVE_LOCKING) += spinlock. + obj-$(CONFIG_UID16) += uid16.o + obj-$(CONFIG_MODULES) += module.o + obj-$(CONFIG_KALLSYMS) += kallsyms.o ++obj-$(CONFIG_STACK_UNWIND) += unwind.o + obj-$(CONFIG_PM) += power/ + obj-$(CONFIG_FREEZER) += power/ + obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1501,6 +1502,8 @@ static void free_module(struct module *m + remove_sect_attrs(mod); + mod_kobject_remove(mod); + ++ unwind_remove_table(mod->unwind_info, 0); ++ + /* Arch-specific cleanup. */ + module_arch_cleanup(mod); + +@@ -2064,6 +2067,7 @@ static noinline struct module *load_modu + unsigned int symindex = 0; + unsigned int strindex = 0; + unsigned int modindex, versindex, infoindex, pcpuindex; ++ unsigned int unwindex = 0; + struct module *mod; + long err = 0; + void *ptr = NULL; /* Stops spurious gcc warning */ +@@ -2146,10 +2150,15 @@ static noinline struct module *load_modu + versindex = find_sec(hdr, sechdrs, secstrings, "__versions"); + infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo"); + pcpuindex = find_pcpusec(hdr, sechdrs, secstrings); ++#ifdef ARCH_UNWIND_SECTION_NAME ++ unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME); ++#endif + + /* Don't keep modinfo and version sections. */ + sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; + sechdrs[versindex].sh_flags &= ~(unsigned long)SHF_ALLOC; ++ if (unwindex) ++ sechdrs[unwindex].sh_flags |= SHF_ALLOC; + + /* Check module struct version now, before we try to use module. */ + if (!check_modstruct_version(sechdrs, versindex, mod)) { +@@ -2511,6 +2520,11 @@ static noinline struct module *load_modu + } + #endif + ++ /* Size of section 0 is 0, so this works well if no unwind info. */ ++ mod->unwind_info = unwind_add_table(mod, ++ (void *)sechdrs[unwindex].sh_addr, ++ sechdrs[unwindex].sh_size); ++ + /* Get rid of temporary copy */ + vfree(hdr); + +@@ -2632,6 +2646,7 @@ SYSCALL_DEFINE3(init_module, void __user + /* Drop initial reference. */ + module_put(mod); + trim_init_extable(mod); ++ unwind_remove_table(mod->unwind_info, 1); + #ifdef CONFIG_KALLSYMS + mod->num_symtab = mod->core_num_syms; + mod->symtab = mod->core_symtab; +--- /dev/null ++++ b/kernel/unwind.c +@@ -0,0 +1,1303 @@ ++/* ++ * Copyright (C) 2002-2006 Novell, Inc. ++ * Jan Beulich ++ * This code is released under version 2 of the GNU GPL. ++ * ++ * A simple API for unwinding kernel stacks. This is used for ++ * debugging and error reporting purposes. The kernel doesn't need ++ * full-blown stack unwinding with all the bells and whistles, so there ++ * is not much point in implementing the full Dwarf2 unwind API. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern const char __start_unwind[], __end_unwind[]; ++extern const u8 __start_unwind_hdr[], __end_unwind_hdr[]; ++ ++#define MAX_STACK_DEPTH 8 ++ ++#define EXTRA_INFO(f) { \ ++ BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \ ++ % FIELD_SIZEOF(struct unwind_frame_info, f)) \ ++ + offsetof(struct unwind_frame_info, f) \ ++ / FIELD_SIZEOF(struct unwind_frame_info, f), \ ++ FIELD_SIZEOF(struct unwind_frame_info, f) \ ++ } ++#define PTREGS_INFO(f) EXTRA_INFO(regs.f) ++ ++static const struct { ++ unsigned offs:BITS_PER_LONG / 2; ++ unsigned width:BITS_PER_LONG / 2; ++} reg_info[] = { ++ UNW_REGISTER_INFO ++}; ++ ++#undef PTREGS_INFO ++#undef EXTRA_INFO ++ ++#ifndef REG_INVALID ++#define REG_INVALID(r) (reg_info[r].width == 0) ++#endif ++ ++#define DW_CFA_nop 0x00 ++#define DW_CFA_set_loc 0x01 ++#define DW_CFA_advance_loc1 0x02 ++#define DW_CFA_advance_loc2 0x03 ++#define DW_CFA_advance_loc4 0x04 ++#define DW_CFA_offset_extended 0x05 ++#define DW_CFA_restore_extended 0x06 ++#define DW_CFA_undefined 0x07 ++#define DW_CFA_same_value 0x08 ++#define DW_CFA_register 0x09 ++#define DW_CFA_remember_state 0x0a ++#define DW_CFA_restore_state 0x0b ++#define DW_CFA_def_cfa 0x0c ++#define DW_CFA_def_cfa_register 0x0d ++#define DW_CFA_def_cfa_offset 0x0e ++#define DW_CFA_def_cfa_expression 0x0f ++#define DW_CFA_expression 0x10 ++#define DW_CFA_offset_extended_sf 0x11 ++#define DW_CFA_def_cfa_sf 0x12 ++#define DW_CFA_def_cfa_offset_sf 0x13 ++#define DW_CFA_val_offset 0x14 ++#define DW_CFA_val_offset_sf 0x15 ++#define DW_CFA_val_expression 0x16 ++#define DW_CFA_lo_user 0x1c ++#define DW_CFA_GNU_window_save 0x2d ++#define DW_CFA_GNU_args_size 0x2e ++#define DW_CFA_GNU_negative_offset_extended 0x2f ++#define DW_CFA_hi_user 0x3f ++ ++#define DW_EH_PE_FORM 0x07 ++#define DW_EH_PE_native 0x00 ++#define DW_EH_PE_leb128 0x01 ++#define DW_EH_PE_data2 0x02 ++#define DW_EH_PE_data4 0x03 ++#define DW_EH_PE_data8 0x04 ++#define DW_EH_PE_signed 0x08 ++#define DW_EH_PE_ADJUST 0x70 ++#define DW_EH_PE_abs 0x00 ++#define DW_EH_PE_pcrel 0x10 ++#define DW_EH_PE_textrel 0x20 ++#define DW_EH_PE_datarel 0x30 ++#define DW_EH_PE_funcrel 0x40 ++#define DW_EH_PE_aligned 0x50 ++#define DW_EH_PE_indirect 0x80 ++#define DW_EH_PE_omit 0xff ++ ++typedef unsigned long uleb128_t; ++typedef signed long sleb128_t; ++#define sleb128abs __builtin_labs ++ ++static struct unwind_table { ++ struct { ++ unsigned long pc; ++ unsigned long range; ++ } core, init; ++ const void *address; ++ unsigned long size; ++ const unsigned char *header; ++ unsigned long hdrsz; ++ struct unwind_table *link; ++ const char *name; ++} root_table; ++ ++struct unwind_item { ++ enum item_location { ++ Nowhere, ++ Memory, ++ Register, ++ Value ++ } where; ++ uleb128_t value; ++}; ++ ++struct unwind_state { ++ uleb128_t loc, org; ++ const u8 *cieStart, *cieEnd; ++ uleb128_t codeAlign; ++ sleb128_t dataAlign; ++ struct cfa { ++ uleb128_t reg, offs; ++ } cfa; ++ struct unwind_item regs[ARRAY_SIZE(reg_info)]; ++ unsigned stackDepth:8; ++ unsigned version:8; ++ const u8 *label; ++ const u8 *stack[MAX_STACK_DEPTH]; ++}; ++ ++static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; ++ ++static unsigned unwind_debug; ++static int __init unwind_debug_setup(char *s) ++{ ++ unwind_debug = simple_strtoul(s, NULL, 0); ++ return 1; ++} ++__setup("unwind_debug=", unwind_debug_setup); ++#define dprintk(lvl, fmt, args...) \ ++ ((void)(lvl > unwind_debug \ ++ || printk(KERN_DEBUG "unwind: " fmt "\n", ##args))) ++ ++static struct unwind_table *find_table(unsigned long pc) ++{ ++ struct unwind_table *table; ++ ++ for (table = &root_table; table; table = table->link) ++ if ((pc >= table->core.pc ++ && pc < table->core.pc + table->core.range) ++ || (pc >= table->init.pc ++ && pc < table->init.pc + table->init.range)) ++ break; ++ ++ return table; ++} ++ ++static unsigned long read_pointer(const u8 **pLoc, ++ const void *end, ++ signed ptrType, ++ unsigned long text_base, ++ unsigned long data_base); ++ ++static void init_unwind_table(struct unwind_table *table, ++ const char *name, ++ const void *core_start, ++ unsigned long core_size, ++ const void *init_start, ++ unsigned long init_size, ++ const void *table_start, ++ unsigned long table_size, ++ const u8 *header_start, ++ unsigned long header_size) ++{ ++ const u8 *ptr = header_start + 4; ++ const u8 *end = header_start + header_size; ++ ++ table->core.pc = (unsigned long)core_start; ++ table->core.range = core_size; ++ table->init.pc = (unsigned long)init_start; ++ table->init.range = init_size; ++ table->address = table_start; ++ table->size = table_size; ++ /* See if the linker provided table looks valid. */ ++ if (header_size <= 4 ++ || header_start[0] != 1 ++ || (void *)read_pointer(&ptr, end, header_start[1], 0, 0) ++ != table_start ++ || !read_pointer(&ptr, end, header_start[2], 0, 0) ++ || !read_pointer(&ptr, end, header_start[3], 0, ++ (unsigned long)header_start) ++ || !read_pointer(&ptr, end, header_start[3], 0, ++ (unsigned long)header_start)) ++ header_start = NULL; ++ table->hdrsz = header_size; ++ smp_wmb(); ++ table->header = header_start; ++ table->link = NULL; ++ table->name = name; ++} ++ ++void __init unwind_init(void) ++{ ++ init_unwind_table(&root_table, "kernel", ++ _text, _end - _text, ++ NULL, 0, ++ __start_unwind, __end_unwind - __start_unwind, ++ __start_unwind_hdr, __end_unwind_hdr - __start_unwind_hdr); ++} ++ ++static const u32 bad_cie, not_fde; ++static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *); ++static signed fde_pointer_type(const u32 *cie); ++ ++struct eh_frame_hdr_table_entry { ++ unsigned long start, fde; ++}; ++ ++static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2) ++{ ++ const struct eh_frame_hdr_table_entry *e1 = p1; ++ const struct eh_frame_hdr_table_entry *e2 = p2; ++ ++ return (e1->start > e2->start) - (e1->start < e2->start); ++} ++ ++static void swap_eh_frame_hdr_table_entries(void *p1, void *p2, int size) ++{ ++ struct eh_frame_hdr_table_entry *e1 = p1; ++ struct eh_frame_hdr_table_entry *e2 = p2; ++ unsigned long v; ++ ++ v = e1->start; ++ e1->start = e2->start; ++ e2->start = v; ++ v = e1->fde; ++ e1->fde = e2->fde; ++ e2->fde = v; ++} ++ ++static void __init setup_unwind_table(struct unwind_table *table, ++ void *(*alloc)(unsigned long)) ++{ ++ const u8 *ptr; ++ unsigned long tableSize = table->size, hdrSize; ++ unsigned n; ++ const u32 *fde; ++ struct { ++ u8 version; ++ u8 eh_frame_ptr_enc; ++ u8 fde_count_enc; ++ u8 table_enc; ++ unsigned long eh_frame_ptr; ++ unsigned int fde_count; ++ struct eh_frame_hdr_table_entry table[]; ++ } __attribute__((__packed__)) *header; ++ ++ if (table->header) ++ return; ++ ++ if (table->hdrsz) ++ printk(KERN_WARNING ".eh_frame_hdr for '%s' present but unusable\n", ++ table->name); ++ ++ if (tableSize & (sizeof(*fde) - 1)) ++ return; ++ ++ for (fde = table->address, n = 0; ++ tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; ++ tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { ++ const u32 *cie = cie_for_fde(fde, table); ++ signed ptrType; ++ ++ if (cie == ¬_fde) ++ continue; ++ if (cie == NULL ++ || cie == &bad_cie ++ || (ptrType = fde_pointer_type(cie)) < 0) ++ return; ++ ptr = (const u8 *)(fde + 2); ++ if (!read_pointer(&ptr, ++ (const u8 *)(fde + 1) + *fde, ++ ptrType, 0, 0)) ++ return; ++ ++n; ++ } ++ ++ if (tableSize || !n) ++ return; ++ ++ hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) ++ + 2 * n * sizeof(unsigned long); ++ dprintk(2, "Binary lookup table size for %s: %lu bytes", table->name, hdrSize); ++ header = alloc(hdrSize); ++ if (!header) ++ return; ++ header->version = 1; ++ header->eh_frame_ptr_enc = DW_EH_PE_abs|DW_EH_PE_native; ++ header->fde_count_enc = DW_EH_PE_abs|DW_EH_PE_data4; ++ header->table_enc = DW_EH_PE_abs|DW_EH_PE_native; ++ put_unaligned((unsigned long)table->address, &header->eh_frame_ptr); ++ BUILD_BUG_ON(offsetof(typeof(*header), fde_count) ++ % __alignof(typeof(header->fde_count))); ++ header->fde_count = n; ++ ++ BUILD_BUG_ON(offsetof(typeof(*header), table) ++ % __alignof(typeof(*header->table))); ++ for (fde = table->address, tableSize = table->size, n = 0; ++ tableSize; ++ tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { ++ const u32 *cie = fde + 1 - fde[1] / sizeof(*fde); ++ ++ if (!fde[1]) ++ continue; /* this is a CIE */ ++ ptr = (const u8 *)(fde + 2); ++ header->table[n].start = read_pointer(&ptr, ++ (const u8 *)(fde + 1) + *fde, ++ fde_pointer_type(cie), 0, 0); ++ header->table[n].fde = (unsigned long)fde; ++ ++n; ++ } ++ WARN_ON(n != header->fde_count); ++ ++ sort(header->table, ++ n, ++ sizeof(*header->table), ++ cmp_eh_frame_hdr_table_entries, ++ swap_eh_frame_hdr_table_entries); ++ ++ table->hdrsz = hdrSize; ++ smp_wmb(); ++ table->header = (const void *)header; ++} ++ ++static void *__init balloc(unsigned long sz) ++{ ++ return __alloc_bootmem_nopanic(sz, ++ sizeof(unsigned int), ++ __pa(MAX_DMA_ADDRESS)); ++} ++ ++void __init unwind_setup(void) ++{ ++ setup_unwind_table(&root_table, balloc); ++} ++ ++#ifdef CONFIG_MODULES ++ ++static struct unwind_table *last_table; ++ ++/* Must be called with module_mutex held. */ ++void *unwind_add_table(struct module *module, ++ const void *table_start, ++ unsigned long table_size) ++{ ++ struct unwind_table *table; ++ ++ if (table_size <= 0) ++ return NULL; ++ ++ table = kmalloc(sizeof(*table), GFP_KERNEL); ++ if (!table) ++ return NULL; ++ ++ init_unwind_table(table, module->name, ++ module->module_core, module->core_size, ++ module->module_init, module->init_size, ++ table_start, table_size, ++ NULL, 0); ++ ++ if (last_table) ++ last_table->link = table; ++ else ++ root_table.link = table; ++ last_table = table; ++ ++ return table; ++} ++ ++struct unlink_table_info ++{ ++ struct unwind_table *table; ++ int init_only; ++}; ++ ++static int unlink_table(void *arg) ++{ ++ struct unlink_table_info *info = arg; ++ struct unwind_table *table = info->table, *prev; ++ ++ for (prev = &root_table; prev->link && prev->link != table; prev = prev->link) ++ ; ++ ++ if (prev->link) { ++ if (info->init_only) { ++ table->init.pc = 0; ++ table->init.range = 0; ++ info->table = NULL; ++ } else { ++ prev->link = table->link; ++ if (!prev->link) ++ last_table = prev; ++ } ++ } else ++ info->table = NULL; ++ ++ return 0; ++} ++ ++/* Must be called with module_mutex held. */ ++void unwind_remove_table(void *handle, int init_only) ++{ ++ struct unwind_table *table = handle; ++ struct unlink_table_info info; ++ ++ if (!table || table == &root_table) ++ return; ++ ++ if (init_only && table == last_table) { ++ table->init.pc = 0; ++ table->init.range = 0; ++ return; ++ } ++ ++ info.table = table; ++ info.init_only = init_only; ++ stop_machine(unlink_table, &info, NULL); ++ ++ if (info.table) ++ kfree(table); ++} ++ ++#endif /* CONFIG_MODULES */ ++ ++static uleb128_t get_uleb128(const u8 **pcur, const u8 *end) ++{ ++ const u8 *cur = *pcur; ++ uleb128_t value; ++ unsigned shift; ++ ++ for (shift = 0, value = 0; cur < end; shift += 7) { ++ if (shift + 7 > 8 * sizeof(value) ++ && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { ++ cur = end + 1; ++ break; ++ } ++ value |= (uleb128_t)(*cur & 0x7f) << shift; ++ if (!(*cur++ & 0x80)) ++ break; ++ } ++ *pcur = cur; ++ ++ return value; ++} ++ ++static sleb128_t get_sleb128(const u8 **pcur, const u8 *end) ++{ ++ const u8 *cur = *pcur; ++ sleb128_t value; ++ unsigned shift; ++ ++ for (shift = 0, value = 0; cur < end; shift += 7) { ++ if (shift + 7 > 8 * sizeof(value) ++ && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { ++ cur = end + 1; ++ break; ++ } ++ value |= (sleb128_t)(*cur & 0x7f) << shift; ++ if (!(*cur & 0x80)) { ++ value |= -(*cur++ & 0x40) << shift; ++ break; ++ } ++ } ++ *pcur = cur; ++ ++ return value; ++} ++ ++static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *table) ++{ ++ const u32 *cie; ++ ++ if (!*fde || (*fde & (sizeof(*fde) - 1))) ++ return &bad_cie; ++ if (!fde[1]) ++ return ¬_fde; /* this is a CIE */ ++ if ((fde[1] & (sizeof(*fde) - 1)) ++ || fde[1] > (unsigned long)(fde + 1) - (unsigned long)table->address) ++ return NULL; /* this is not a valid FDE */ ++ cie = fde + 1 - fde[1] / sizeof(*fde); ++ if (*cie <= sizeof(*cie) + 4 ++ || *cie >= fde[1] - sizeof(*fde) ++ || (*cie & (sizeof(*cie) - 1)) ++ || cie[1]) ++ return NULL; /* this is not a (valid) CIE */ ++ return cie; ++} ++ ++static unsigned long read_pointer(const u8 **pLoc, ++ const void *end, ++ signed ptrType, ++ unsigned long text_base, ++ unsigned long data_base) ++{ ++ unsigned long value = 0; ++ union { ++ const u8 *p8; ++ const u16 *p16u; ++ const s16 *p16s; ++ const u32 *p32u; ++ const s32 *p32s; ++ const unsigned long *pul; ++ } ptr; ++ ++ if (ptrType < 0 || ptrType == DW_EH_PE_omit) { ++ dprintk(1, "Invalid pointer encoding %02X (%p,%p).", ptrType, *pLoc, end); ++ return 0; ++ } ++ ptr.p8 = *pLoc; ++ switch (ptrType & DW_EH_PE_FORM) { ++ case DW_EH_PE_data2: ++ if (end < (const void *)(ptr.p16u + 1)) { ++ dprintk(1, "Data16 overrun (%p,%p).", ptr.p8, end); ++ return 0; ++ } ++ if (ptrType & DW_EH_PE_signed) ++ value = get_unaligned(ptr.p16s++); ++ else ++ value = get_unaligned(ptr.p16u++); ++ break; ++ case DW_EH_PE_data4: ++#ifdef CONFIG_64BIT ++ if (end < (const void *)(ptr.p32u + 1)) { ++ dprintk(1, "Data32 overrun (%p,%p).", ptr.p8, end); ++ return 0; ++ } ++ if (ptrType & DW_EH_PE_signed) ++ value = get_unaligned(ptr.p32s++); ++ else ++ value = get_unaligned(ptr.p32u++); ++ break; ++ case DW_EH_PE_data8: ++ BUILD_BUG_ON(sizeof(u64) != sizeof(value)); ++#else ++ BUILD_BUG_ON(sizeof(u32) != sizeof(value)); ++#endif ++ case DW_EH_PE_native: ++ if (end < (const void *)(ptr.pul + 1)) { ++ dprintk(1, "DataUL overrun (%p,%p).", ptr.p8, end); ++ return 0; ++ } ++ value = get_unaligned(ptr.pul++); ++ break; ++ case DW_EH_PE_leb128: ++ BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value)); ++ value = ptrType & DW_EH_PE_signed ++ ? get_sleb128(&ptr.p8, end) ++ : get_uleb128(&ptr.p8, end); ++ if ((const void *)ptr.p8 > end) { ++ dprintk(1, "DataLEB overrun (%p,%p).", ptr.p8, end); ++ return 0; ++ } ++ break; ++ default: ++ dprintk(2, "Cannot decode pointer type %02X (%p,%p).", ++ ptrType, ptr.p8, end); ++ return 0; ++ } ++ switch (ptrType & DW_EH_PE_ADJUST) { ++ case DW_EH_PE_abs: ++ break; ++ case DW_EH_PE_pcrel: ++ value += (unsigned long)*pLoc; ++ break; ++ case DW_EH_PE_textrel: ++ if (likely(text_base)) { ++ value += text_base; ++ break; ++ } ++ dprintk(2, "Text-relative encoding %02X (%p,%p), but zero text base.", ++ ptrType, *pLoc, end); ++ return 0; ++ case DW_EH_PE_datarel: ++ if (likely(data_base)) { ++ value += data_base; ++ break; ++ } ++ dprintk(2, "Data-relative encoding %02X (%p,%p), but zero data base.", ++ ptrType, *pLoc, end); ++ return 0; ++ default: ++ dprintk(2, "Cannot adjust pointer type %02X (%p,%p).", ++ ptrType, *pLoc, end); ++ return 0; ++ } ++ if ((ptrType & DW_EH_PE_indirect) ++ && probe_kernel_address(value, value)) { ++ dprintk(1, "Cannot read indirect value %lx (%p,%p).", ++ value, *pLoc, end); ++ return 0; ++ } ++ *pLoc = ptr.p8; ++ ++ return value; ++} ++ ++static signed fde_pointer_type(const u32 *cie) ++{ ++ const u8 *ptr = (const u8 *)(cie + 2); ++ unsigned version = *ptr; ++ ++ if (version != 1) ++ return -1; /* unsupported */ ++ if (*++ptr) { ++ const char *aug; ++ const u8 *end = (const u8 *)(cie + 1) + *cie; ++ uleb128_t len; ++ ++ /* check if augmentation size is first (and thus present) */ ++ if (*ptr != 'z') ++ return -1; ++ /* check if augmentation string is nul-terminated */ ++ if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) ++ return -1; ++ ++ptr; /* skip terminator */ ++ get_uleb128(&ptr, end); /* skip code alignment */ ++ get_sleb128(&ptr, end); /* skip data alignment */ ++ /* skip return address column */ ++ version <= 1 ? (void)++ptr : (void)get_uleb128(&ptr, end); ++ len = get_uleb128(&ptr, end); /* augmentation length */ ++ if (ptr + len < ptr || ptr + len > end) ++ return -1; ++ end = ptr + len; ++ while (*++aug) { ++ if (ptr >= end) ++ return -1; ++ switch (*aug) { ++ case 'L': ++ ++ptr; ++ break; ++ case 'P': { ++ signed ptrType = *ptr++; ++ ++ if (!read_pointer(&ptr, end, ptrType, 0, 0) ++ || ptr > end) ++ return -1; ++ } ++ break; ++ case 'R': ++ return *ptr; ++ default: ++ return -1; ++ } ++ } ++ } ++ return DW_EH_PE_native|DW_EH_PE_abs; ++} ++ ++static int advance_loc(unsigned long delta, struct unwind_state *state) ++{ ++ state->loc += delta * state->codeAlign; ++ ++ return delta > 0; ++} ++ ++static void set_rule(uleb128_t reg, ++ enum item_location where, ++ uleb128_t value, ++ struct unwind_state *state) ++{ ++ if (reg < ARRAY_SIZE(state->regs)) { ++ state->regs[reg].where = where; ++ state->regs[reg].value = value; ++ } ++} ++ ++static int processCFI(const u8 *start, ++ const u8 *end, ++ unsigned long targetLoc, ++ signed ptrType, ++ struct unwind_state *state) ++{ ++ union { ++ const u8 *p8; ++ const u16 *p16; ++ const u32 *p32; ++ } ptr; ++ int result = 1; ++ ++ if (start != state->cieStart) { ++ state->loc = state->org; ++ result = processCFI(state->cieStart, state->cieEnd, 0, ptrType, state); ++ if (targetLoc == 0 && state->label == NULL) ++ return result; ++ } ++ for (ptr.p8 = start; result && ptr.p8 < end; ) { ++ switch (*ptr.p8 >> 6) { ++ uleb128_t value; ++ ++ case 0: ++ switch (*ptr.p8++) { ++ case DW_CFA_nop: ++ break; ++ case DW_CFA_set_loc: ++ state->loc = read_pointer(&ptr.p8, end, ptrType, 0, 0); ++ if (state->loc == 0) ++ result = 0; ++ break; ++ case DW_CFA_advance_loc1: ++ result = ptr.p8 < end && advance_loc(*ptr.p8++, state); ++ break; ++ case DW_CFA_advance_loc2: ++ result = ptr.p8 <= end + 2 ++ && advance_loc(*ptr.p16++, state); ++ break; ++ case DW_CFA_advance_loc4: ++ result = ptr.p8 <= end + 4 ++ && advance_loc(*ptr.p32++, state); ++ break; ++ case DW_CFA_offset_extended: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Memory, get_uleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_val_offset: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Value, get_uleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_offset_extended_sf: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Memory, get_sleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_val_offset_sf: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Value, get_sleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_restore_extended: ++ case DW_CFA_undefined: ++ case DW_CFA_same_value: ++ set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, state); ++ break; ++ case DW_CFA_register: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, ++ Register, ++ get_uleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_remember_state: ++ if (ptr.p8 == state->label) { ++ state->label = NULL; ++ return 1; ++ } ++ if (state->stackDepth >= MAX_STACK_DEPTH) { ++ dprintk(1, "State stack overflow (%p,%p).", ptr.p8, end); ++ return 0; ++ } ++ state->stack[state->stackDepth++] = ptr.p8; ++ break; ++ case DW_CFA_restore_state: ++ if (state->stackDepth) { ++ const uleb128_t loc = state->loc; ++ const u8 *label = state->label; ++ ++ state->label = state->stack[state->stackDepth - 1]; ++ memcpy(&state->cfa, &badCFA, sizeof(state->cfa)); ++ memset(state->regs, 0, sizeof(state->regs)); ++ state->stackDepth = 0; ++ result = processCFI(start, end, 0, ptrType, state); ++ state->loc = loc; ++ state->label = label; ++ } else { ++ dprintk(1, "State stack underflow (%p,%p).", ptr.p8, end); ++ return 0; ++ } ++ break; ++ case DW_CFA_def_cfa: ++ state->cfa.reg = get_uleb128(&ptr.p8, end); ++ /*nobreak*/ ++ case DW_CFA_def_cfa_offset: ++ state->cfa.offs = get_uleb128(&ptr.p8, end); ++ break; ++ case DW_CFA_def_cfa_sf: ++ state->cfa.reg = get_uleb128(&ptr.p8, end); ++ /*nobreak*/ ++ case DW_CFA_def_cfa_offset_sf: ++ state->cfa.offs = get_sleb128(&ptr.p8, end) ++ * state->dataAlign; ++ break; ++ case DW_CFA_def_cfa_register: ++ state->cfa.reg = get_uleb128(&ptr.p8, end); ++ break; ++ /*todo case DW_CFA_def_cfa_expression: */ ++ /*todo case DW_CFA_expression: */ ++ /*todo case DW_CFA_val_expression: */ ++ case DW_CFA_GNU_args_size: ++ get_uleb128(&ptr.p8, end); ++ break; ++ case DW_CFA_GNU_negative_offset_extended: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, ++ Memory, ++ (uleb128_t)0 - get_uleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_GNU_window_save: ++ default: ++ dprintk(1, "Unrecognized CFI op %02X (%p,%p).", ptr.p8[-1], ptr.p8 - 1, end); ++ result = 0; ++ break; ++ } ++ break; ++ case 1: ++ result = advance_loc(*ptr.p8++ & 0x3f, state); ++ break; ++ case 2: ++ value = *ptr.p8++ & 0x3f; ++ set_rule(value, Memory, get_uleb128(&ptr.p8, end), state); ++ break; ++ case 3: ++ set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); ++ break; ++ } ++ if (ptr.p8 > end) { ++ dprintk(1, "Data overrun (%p,%p).", ptr.p8, end); ++ result = 0; ++ } ++ if (result && targetLoc != 0 && targetLoc < state->loc) ++ return 1; ++ } ++ ++ if (result && ptr.p8 < end) ++ dprintk(1, "Data underrun (%p,%p).", ptr.p8, end); ++ ++ return result ++ && ptr.p8 == end ++ && (targetLoc == 0 ++ || (/*todo While in theory this should apply, gcc in practice omits ++ everything past the function prolog, and hence the location ++ never reaches the end of the function. ++ targetLoc < state->loc &&*/ state->label == NULL)); ++} ++ ++/* Unwind to previous to frame. Returns 0 if successful, negative ++ * number in case of an error. */ ++int unwind(struct unwind_frame_info *frame) ++{ ++#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) ++ const u32 *fde = NULL, *cie = NULL; ++ const u8 *ptr = NULL, *end = NULL; ++ unsigned long pc = UNW_PC(frame) - frame->call_frame, sp; ++ unsigned long startLoc = 0, endLoc = 0, cfa; ++ unsigned i; ++ signed ptrType = -1; ++ uleb128_t retAddrReg = 0; ++ const struct unwind_table *table; ++ struct unwind_state state; ++ ++ if (UNW_PC(frame) == 0) ++ return -EINVAL; ++ if ((table = find_table(pc)) != NULL ++ && !(table->size & (sizeof(*fde) - 1))) { ++ const u8 *hdr = table->header; ++ unsigned long tableSize; ++ ++ smp_rmb(); ++ if (hdr && hdr[0] == 1) { ++ switch (hdr[3] & DW_EH_PE_FORM) { ++ case DW_EH_PE_native: tableSize = sizeof(unsigned long); break; ++ case DW_EH_PE_data2: tableSize = 2; break; ++ case DW_EH_PE_data4: tableSize = 4; break; ++ case DW_EH_PE_data8: tableSize = 8; break; ++ default: tableSize = 0; break; ++ } ++ ptr = hdr + 4; ++ end = hdr + table->hdrsz; ++ if (tableSize ++ && read_pointer(&ptr, end, hdr[1], 0, 0) ++ == (unsigned long)table->address ++ && (i = read_pointer(&ptr, end, hdr[2], 0, 0)) > 0 ++ && i == (end - ptr) / (2 * tableSize) ++ && !((end - ptr) % (2 * tableSize))) { ++ do { ++ const u8 *cur = ptr + (i / 2) * (2 * tableSize); ++ ++ startLoc = read_pointer(&cur, ++ cur + tableSize, ++ hdr[3], 0, ++ (unsigned long)hdr); ++ if (pc < startLoc) ++ i /= 2; ++ else { ++ ptr = cur - tableSize; ++ i = (i + 1) / 2; ++ } ++ } while (startLoc && i > 1); ++ if (i == 1 ++ && (startLoc = read_pointer(&ptr, ++ ptr + tableSize, ++ hdr[3], 0, ++ (unsigned long)hdr)) != 0 ++ && pc >= startLoc) ++ fde = (void *)read_pointer(&ptr, ++ ptr + tableSize, ++ hdr[3], 0, ++ (unsigned long)hdr); ++ } ++ } ++ if (hdr && !fde) ++ dprintk(3, "Binary lookup for %lx failed.", pc); ++ ++ if (fde != NULL) { ++ cie = cie_for_fde(fde, table); ++ ptr = (const u8 *)(fde + 2); ++ if (cie != NULL ++ && cie != &bad_cie ++ && cie != ¬_fde ++ && (ptrType = fde_pointer_type(cie)) >= 0 ++ && read_pointer(&ptr, ++ (const u8 *)(fde + 1) + *fde, ++ ptrType, 0, 0) == startLoc) { ++ if (!(ptrType & DW_EH_PE_indirect)) ++ ptrType &= DW_EH_PE_FORM|DW_EH_PE_signed; ++ endLoc = startLoc ++ + read_pointer(&ptr, ++ (const u8 *)(fde + 1) + *fde, ++ ptrType, 0, 0); ++ if (pc >= endLoc) ++ fde = NULL; ++ } else ++ fde = NULL; ++ if (!fde) ++ dprintk(1, "Binary lookup result for %lx discarded.", pc); ++ } ++ if (fde == NULL) { ++ for (fde = table->address, tableSize = table->size; ++ cie = NULL, tableSize > sizeof(*fde) ++ && tableSize - sizeof(*fde) >= *fde; ++ tableSize -= sizeof(*fde) + *fde, ++ fde += 1 + *fde / sizeof(*fde)) { ++ cie = cie_for_fde(fde, table); ++ if (cie == &bad_cie) { ++ cie = NULL; ++ break; ++ } ++ if (cie == NULL ++ || cie == ¬_fde ++ || (ptrType = fde_pointer_type(cie)) < 0) ++ continue; ++ ptr = (const u8 *)(fde + 2); ++ startLoc = read_pointer(&ptr, ++ (const u8 *)(fde + 1) + *fde, ++ ptrType, 0, 0); ++ if (!startLoc) ++ continue; ++ if (!(ptrType & DW_EH_PE_indirect)) ++ ptrType &= DW_EH_PE_FORM|DW_EH_PE_signed; ++ endLoc = startLoc ++ + read_pointer(&ptr, ++ (const u8 *)(fde + 1) + *fde, ++ ptrType, 0, 0); ++ if (pc >= startLoc && pc < endLoc) ++ break; ++ } ++ if (!fde) ++ dprintk(3, "Linear lookup for %lx failed.", pc); ++ } ++ } ++ if (cie != NULL) { ++ memset(&state, 0, sizeof(state)); ++ state.cieEnd = ptr; /* keep here temporarily */ ++ ptr = (const u8 *)(cie + 2); ++ end = (const u8 *)(cie + 1) + *cie; ++ frame->call_frame = 1; ++ if ((state.version = *ptr) != 1) ++ cie = NULL; /* unsupported version */ ++ else if (*++ptr) { ++ /* check if augmentation size is first (and thus present) */ ++ if (*ptr == 'z') { ++ while (++ptr < end && *ptr) { ++ switch (*ptr) { ++ /* check for ignorable (or already handled) ++ * nul-terminated augmentation string */ ++ case 'L': ++ case 'P': ++ case 'R': ++ continue; ++ case 'S': ++ frame->call_frame = 0; ++ continue; ++ default: ++ break; ++ } ++ break; ++ } ++ } ++ if (ptr >= end || *ptr) ++ cie = NULL; ++ } ++ if (!cie) ++ dprintk(1, "CIE unusable (%p,%p).", ptr, end); ++ ++ptr; ++ } ++ if (cie != NULL) { ++ /* get code aligment factor */ ++ state.codeAlign = get_uleb128(&ptr, end); ++ /* get data aligment factor */ ++ state.dataAlign = get_sleb128(&ptr, end); ++ if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) ++ cie = NULL; ++ else if (UNW_PC(frame) % state.codeAlign ++ || UNW_SP(frame) % sleb128abs(state.dataAlign)) { ++ dprintk(1, "Input pointer(s) misaligned (%lx,%lx).", ++ UNW_PC(frame), UNW_SP(frame)); ++ return -EPERM; ++ } else { ++ retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); ++ /* skip augmentation */ ++ if (((const char *)(cie + 2))[1] == 'z') { ++ uleb128_t augSize = get_uleb128(&ptr, end); ++ ++ ptr += augSize; ++ } ++ if (ptr > end ++ || retAddrReg >= ARRAY_SIZE(reg_info) ++ || REG_INVALID(retAddrReg) ++ || reg_info[retAddrReg].width != sizeof(unsigned long)) ++ cie = NULL; ++ } ++ if (!cie) ++ dprintk(1, "CIE validation failed (%p,%p).", ptr, end); ++ } ++ if (cie != NULL) { ++ state.cieStart = ptr; ++ ptr = state.cieEnd; ++ state.cieEnd = end; ++ end = (const u8 *)(fde + 1) + *fde; ++ /* skip augmentation */ ++ if (((const char *)(cie + 2))[1] == 'z') { ++ uleb128_t augSize = get_uleb128(&ptr, end); ++ ++ if ((ptr += augSize) > end) ++ fde = NULL; ++ } ++ if (!fde) ++ dprintk(1, "FDE validation failed (%p,%p).", ptr, end); ++ } ++#ifdef CONFIG_FRAME_POINTER ++ if (cie == NULL || fde == NULL) { ++ unsigned long top = TSK_STACK_TOP(frame->task); ++ unsigned long bottom = STACK_BOTTOM(frame->task); ++ unsigned long fp = UNW_FP(frame); ++ unsigned long sp = UNW_SP(frame); ++ unsigned long link; ++ ++ if ((sp | fp) & sizeof(unsigned long)) ++ return -EPERM; ++ ++# if FRAME_RETADDR_OFFSET < 0 ++ if (!(sp < top && fp <= sp && bottom < fp)) ++# else ++ if (!(sp < top && fp >= sp && bottom < fp)) ++# endif ++ return -ENXIO; ++ ++ if (probe_kernel_address(fp + FRAME_LINK_OFFSET, link)) ++ return -ENXIO; ++ ++# if FRAME_RETADDR_OFFSET < 0 ++ if (!(link > bottom && link < fp)) ++# else ++ if (!(link > bottom && link > fp)) ++# endif ++ return -ENXIO; ++ ++ if (link & (sizeof(unsigned long) - 1)) ++ return -ENXIO; ++ ++ fp += FRAME_RETADDR_OFFSET; ++ if (probe_kernel_address(fp, UNW_PC(frame))) ++ return -ENXIO; ++ ++ /* Ok, we can use it */ ++# if FRAME_RETADDR_OFFSET < 0 ++ UNW_SP(frame) = fp - sizeof(UNW_PC(frame)); ++# else ++ UNW_SP(frame) = fp + sizeof(UNW_PC(frame)); ++# endif ++ UNW_FP(frame) = link; ++ return 0; ++ } ++#endif ++ state.org = startLoc; ++ memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); ++ /* process instructions */ ++ if (!processCFI(ptr, end, pc, ptrType, &state) ++ || state.loc > endLoc ++ || state.regs[retAddrReg].where == Nowhere ++ || state.cfa.reg >= ARRAY_SIZE(reg_info) ++ || reg_info[state.cfa.reg].width != sizeof(unsigned long) ++ || FRAME_REG(state.cfa.reg, unsigned long) % sizeof(unsigned long) ++ || state.cfa.offs % sizeof(unsigned long)) { ++ dprintk(1, "Unusable unwind info (%p,%p).", ptr, end); ++ return -EIO; ++ } ++ /* update frame */ ++#ifndef CONFIG_AS_CFI_SIGNAL_FRAME ++ if (frame->call_frame ++ && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign)) ++ frame->call_frame = 0; ++#endif ++ cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; ++ startLoc = min((unsigned long)UNW_SP(frame), cfa); ++ endLoc = max((unsigned long)UNW_SP(frame), cfa); ++ if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) { ++ startLoc = min(STACK_LIMIT(cfa), cfa); ++ endLoc = max(STACK_LIMIT(cfa), cfa); ++ } ++#ifndef CONFIG_64BIT ++# define CASES CASE(8); CASE(16); CASE(32) ++#else ++# define CASES CASE(8); CASE(16); CASE(32); CASE(64) ++#endif ++ pc = UNW_PC(frame); ++ sp = UNW_SP(frame); ++ for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { ++ if (REG_INVALID(i)) { ++ if (state.regs[i].where == Nowhere) ++ continue; ++ dprintk(1, "Cannot restore register %u (%d).", ++ i, state.regs[i].where); ++ return -EIO; ++ } ++ switch (state.regs[i].where) { ++ default: ++ break; ++ case Register: ++ if (state.regs[i].value >= ARRAY_SIZE(reg_info) ++ || REG_INVALID(state.regs[i].value) ++ || reg_info[i].width > reg_info[state.regs[i].value].width) { ++ dprintk(1, "Cannot restore register %u from register %lu.", ++ i, state.regs[i].value); ++ return -EIO; ++ } ++ switch (reg_info[state.regs[i].value].width) { ++#define CASE(n) \ ++ case sizeof(u##n): \ ++ state.regs[i].value = FRAME_REG(state.regs[i].value, \ ++ const u##n); \ ++ break ++ CASES; ++#undef CASE ++ default: ++ dprintk(1, "Unsupported register size %u (%lu).", ++ reg_info[state.regs[i].value].width, ++ state.regs[i].value); ++ return -EIO; ++ } ++ break; ++ } ++ } ++ for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { ++ if (REG_INVALID(i)) ++ continue; ++ switch (state.regs[i].where) { ++ case Nowhere: ++ if (reg_info[i].width != sizeof(UNW_SP(frame)) ++ || &FRAME_REG(i, __typeof__(UNW_SP(frame))) ++ != &UNW_SP(frame)) ++ continue; ++ UNW_SP(frame) = cfa; ++ break; ++ case Register: ++ switch (reg_info[i].width) { ++#define CASE(n) case sizeof(u##n): \ ++ FRAME_REG(i, u##n) = state.regs[i].value; \ ++ break ++ CASES; ++#undef CASE ++ default: ++ dprintk(1, "Unsupported register size %u (%u).", ++ reg_info[i].width, i); ++ return -EIO; ++ } ++ break; ++ case Value: ++ if (reg_info[i].width != sizeof(unsigned long)) { ++ dprintk(1, "Unsupported value size %u (%u).", ++ reg_info[i].width, i); ++ return -EIO; ++ } ++ FRAME_REG(i, unsigned long) = cfa + state.regs[i].value ++ * state.dataAlign; ++ break; ++ case Memory: { ++ unsigned long addr = cfa + state.regs[i].value ++ * state.dataAlign; ++ ++ if ((state.regs[i].value * state.dataAlign) ++ % sizeof(unsigned long) ++ || addr < startLoc ++ || addr + sizeof(unsigned long) < addr ++ || addr + sizeof(unsigned long) > endLoc) { ++ dprintk(1, "Bad memory location %lx (%lx).", ++ addr, state.regs[i].value); ++ return -EIO; ++ } ++ switch (reg_info[i].width) { ++#define CASE(n) case sizeof(u##n): \ ++ if (probe_kernel_address(addr, \ ++ FRAME_REG(i, u##n))) \ ++ return -EFAULT; \ ++ break ++ CASES; ++#undef CASE ++ default: ++ dprintk(1, "Unsupported memory size %u (%u).", ++ reg_info[i].width, i); ++ return -EIO; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (UNW_PC(frame) % state.codeAlign ++ || UNW_SP(frame) % sleb128abs(state.dataAlign)) { ++ dprintk(1, "Output pointer(s) misaligned (%lx,%lx).", ++ UNW_PC(frame), UNW_SP(frame)); ++ return -EIO; ++ } ++ if (pc == UNW_PC(frame) && sp == UNW_SP(frame)) { ++ dprintk(1, "No progress (%lx,%lx).", pc, sp); ++ return -EIO; ++ } ++ ++ return 0; ++#undef CASES ++#undef FRAME_REG ++} ++EXPORT_SYMBOL_GPL(unwind); ++ ++int unwind_init_frame_info(struct unwind_frame_info *info, ++ struct task_struct *tsk, ++ /*const*/ struct pt_regs *regs) ++{ ++ info->task = tsk; ++ info->call_frame = 0; ++ arch_unw_init_frame_info(info, regs); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(unwind_init_frame_info); ++ ++/* ++ * Prepare to unwind a blocked task. ++ */ ++int unwind_init_blocked(struct unwind_frame_info *info, ++ struct task_struct *tsk) ++{ ++ info->task = tsk; ++ info->call_frame = 0; ++ arch_unw_init_blocked(info); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(unwind_init_blocked); ++ ++/* ++ * Prepare to unwind the currently running thread. ++ */ ++int unwind_init_running(struct unwind_frame_info *info, ++ asmlinkage unwind_callback_fn callback, ++ const struct stacktrace_ops *ops, void *data) ++{ ++ info->task = current; ++ info->call_frame = 0; ++ ++ return arch_unwind_init_running(info, callback, ops, data); ++} ++EXPORT_SYMBOL_GPL(unwind_init_running); ++ ++/* ++ * Unwind until the return pointer is in user-land (or until an error ++ * occurs). Returns 0 if successful, negative number in case of ++ * error. ++ */ ++int unwind_to_user(struct unwind_frame_info *info) ++{ ++ while (!arch_unw_user_mode(info)) { ++ int err = unwind(info); ++ ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(unwind_to_user); +--- a/lib/Kconfig.debug ++++ b/lib/Kconfig.debug +@@ -742,6 +742,24 @@ config FRAME_POINTER + larger and slower, but it gives very useful debugging information + in case of kernel bugs. (precise oopses/stacktraces/warnings) + ++config UNWIND_INFO ++ bool "Compile the kernel with frame unwind information" ++ depends on !IA64 && !PARISC && !ARM ++ depends on !MODULES || !(MIPS || PPC || SUPERH || V850) ++ help ++ If you say Y here the resulting kernel image will be slightly larger ++ but not slower, and it will give very useful debugging information. ++ If you don't debug the kernel, you can say N, but we may not be able ++ to solve problems without frame unwind information or frame pointers. ++ ++config STACK_UNWIND ++ bool "Stack unwind support" ++ depends on UNWIND_INFO ++ depends on X86 ++ help ++ This enables more precise stack traces, omitting all unrelated ++ occurrences of pointers into kernel code from the dump. ++ + config BOOT_PRINTK_DELAY + bool "Delay each boot printk message by N milliseconds" + depends on DEBUG_KERNEL && PRINTK && GENERIC_CALIBRATE_DELAY diff --git a/patches.suse/supported-flag b/patches.suse/supported-flag new file mode 100644 index 0000000..3e0f664 --- /dev/null +++ b/patches.suse/supported-flag @@ -0,0 +1,343 @@ +From: Andreas Gruenbacher +Subject: Novell/external support flag in modules +Patch-mainline: Never, SLES feature + +Upon module load, check if a module is supported, and set the +N (TAINT_NO_SUPPORT) or X (TAINT_EXTERNAL_SUPPORT) tail flags +for unsupported or externally suported modules. + +Changes: +* Feb 21 2008 - jeffm +- 2.6.25 claimed -S and bumped the flags up a bit, modpost now uses -N + +Signed-off-by: Andreas Gruenbacher + +--- + + Documentation/kernel-parameters.txt | 6 +++ + Documentation/sysctl/kernel.txt | 12 ++++++ + Makefile | 5 ++ + include/linux/kernel.h | 8 ++++ + kernel/module.c | 42 ++++++++++++++++++++++- + kernel/panic.c | 4 ++ + kernel/sysctl.c | 9 ++++ + scripts/Makefile.modpost | 4 +- + scripts/mod/modpost.c | 65 +++++++++++++++++++++++++++++++++++- + 9 files changed, 152 insertions(+), 3 deletions(-) + +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -2549,6 +2549,12 @@ and is between 256 and 4096 characters. + improve throughput, but will also increase the + amount of memory reserved for use by the client. + ++ unsupported Allow loading of unsupported kernel modules: ++ 0 = only allow supported modules, ++ 1 = warn when loading unsupported modules, ++ 2 = don't warn. ++ ++ + swiotlb= [IA-64] Number of I/O TLB slabs + + switches= [HW,M68k] +--- a/Documentation/sysctl/kernel.txt ++++ b/Documentation/sysctl/kernel.txt +@@ -477,6 +477,18 @@ can be ORed together: + instead of using the one provided by the hardware. + 512 - A kernel warning has occurred. + 1024 - A module from drivers/staging was loaded. ++ 0x40000000 - An unsupported kernel module was loaded. ++ 0x80000000 - An kernel module with external support was loaded. ++ ++============================================================== ++ ++unsupported: ++ ++Allow to load unsupported kernel modules: ++ ++ 0 - refuse to load unsupported modules, ++ 1 - warn when loading unsupported modules, ++ 2 - don't warn. + + ============================================================== + +--- a/Makefile ++++ b/Makefile +@@ -353,6 +353,11 @@ KBUILD_CFLAGS := -Wall -Wundef -Wstric + -fno-delete-null-pointer-checks + KBUILD_AFLAGS := -D__ASSEMBLY__ + ++# Warn about unsupported modules in kernels built inside Autobuild ++ifneq ($(wildcard /.buildenv),) ++CFLAGS += -DUNSUPPORTED_MODULES=2 ++endif ++ + # Read KERNELRELEASE from include/config/kernel.release (if it exists) + KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) + KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -317,6 +317,7 @@ extern int panic_timeout; + extern int panic_on_oops; + extern int panic_on_unrecovered_nmi; + extern int panic_on_io_nmi; ++extern int unsupported; + extern const char *print_tainted(void); + extern void add_taint(unsigned flag); + extern int test_taint(unsigned flag); +@@ -345,6 +346,13 @@ extern enum system_states { + #define TAINT_WARN 9 + #define TAINT_CRAP 10 + ++/* ++ * Take the upper bits to hopefully allow them ++ * to stay the same for more than one release. ++ */ ++#define TAINT_NO_SUPPORT 30 ++#define TAINT_EXTERNAL_SUPPORT 31 ++ + extern void dump_stack(void) __cold; + + enum { +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -74,6 +74,20 @@ EXPORT_TRACEPOINT_SYMBOL(module_get); + /* If this is set, the section belongs in the init part of the module */ + #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) + ++/* Allow unsupported modules switch. */ ++#ifdef UNSUPPORTED_MODULES ++int unsupported = UNSUPPORTED_MODULES; ++#else ++int unsupported = 2; /* don't warn when loading unsupported modules. */ ++#endif ++ ++static int __init unsupported_setup(char *str) ++{ ++ get_option(&str, &unsupported); ++ return 1; ++} ++__setup("unsupported=", unsupported_setup); ++ + /* List of modules, protected by module_mutex or preempt_disable + * (delete uses stop_machine/add uses RCU list operations). */ + DEFINE_MUTEX(module_mutex); +@@ -1947,7 +1961,7 @@ static noinline struct module *load_modu + Elf_Ehdr *hdr; + Elf_Shdr *sechdrs; + char *secstrings, *args, *modmagic, *strtab = NULL; +- char *staging; ++ char *staging, *supported; + unsigned int i; + unsigned int symindex = 0; + unsigned int strindex = 0; +@@ -2066,6 +2080,28 @@ static noinline struct module *load_modu + mod->name); + } + ++ supported = get_modinfo(sechdrs, infoindex, "supported"); ++ if (supported) { ++ if (!strcmp(supported, "external")) ++ add_taint_module(mod, TAINT_EXTERNAL_SUPPORT); ++ else if (strcmp(supported, "yes")) ++ supported = NULL; ++ } ++ if (!supported) { ++ if (unsupported == 0) { ++ printk(KERN_WARNING "%s: module not supported by " ++ "Novell, refusing to load. To override, echo " ++ "1 > /proc/sys/kernel/unsupported\n", mod->name); ++ err = -ENOEXEC; ++ goto free_hdr; ++ } ++ add_taint_module(mod, TAINT_NO_SUPPORT); ++ if (unsupported == 1) { ++ printk(KERN_WARNING "%s: module not supported by " ++ "Novell, setting U taint flag.\n", mod->name); ++ } ++ } ++ + /* Now copy in args */ + args = strndup_user(uargs, ~0UL >> 1); + if (IS_ERR(args)) { +@@ -2748,6 +2784,10 @@ static char *module_flags(struct module + buf[bx++] = 'F'; + if (mod->taints & (1 << TAINT_CRAP)) + buf[bx++] = 'C'; ++ if (mod->taints & (1 << TAINT_NO_SUPPORT)) ++ buf[bx++] = 'N'; ++ if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT)) ++ buf[bx++] = 'X'; + /* + * TAINT_FORCED_RMMOD: could be added. + * TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -178,6 +178,8 @@ static const struct tnt tnts[] = { + { TAINT_OVERRIDDEN_ACPI_TABLE, 'A', ' ' }, + { TAINT_WARN, 'W', ' ' }, + { TAINT_CRAP, 'C', ' ' }, ++ { TAINT_NO_SUPPORT, 'N', ' ' }, ++ { TAINT_EXTERNAL_SUPPORT, 'X', ' ' }, + }; + + /** +@@ -194,6 +196,8 @@ static const struct tnt tnts[] = { + * 'A' - ACPI table overridden. + * 'W' - Taint on warning. + * 'C' - modules from drivers/staging are loaded. ++ * 'N' - Unsuported modules loaded. ++ * 'X' - Modules with external support loaded. + * + * The string is overwritten by the next call to print_tainted(). + */ +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -636,6 +636,15 @@ static struct ctl_table kern_table[] = { + .extra1 = &pid_max_min, + .extra2 = &pid_max_max, + }, ++#ifdef CONFIG_MODULES ++ { ++ .procname = "unsupported", ++ .data = &unsupported, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec, ++ }, ++#endif + { + .procname = "panic_on_oops", + .data = &panic_on_oops, +--- a/scripts/Makefile.modpost ++++ b/scripts/Makefile.modpost +@@ -81,7 +81,9 @@ modpost = scripts/mod/modpost + $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ + $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ + $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ +- $(if $(cross_build),-c) ++ $(if $(cross_build),-c) \ ++ -N $(firstword $(wildcard $(dir $(MODVERDIR))/Module.supported \ ++ $(objtree)/Module.supported /dev/null)) + + quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules + cmd_modpost = $(modpost) -s +--- a/scripts/mod/modpost.c ++++ b/scripts/mod/modpost.c +@@ -1516,6 +1516,48 @@ static void check_sec_ref(struct module + } + } + ++void *supported_file; ++unsigned long supported_size; ++ ++static const char *supported(struct module *mod) ++{ ++ unsigned long pos = 0; ++ char *line; ++ ++ /* In a first shot, do a simple linear scan. */ ++ while ((line = get_next_line(&pos, supported_file, ++ supported_size))) { ++ const char *basename, *how = "yes"; ++ char *l = line; ++ ++ /* optional type-of-support flag */ ++ for (l = line; *l != '\0'; l++) { ++ if (*l == ' ' || *l == '\t') { ++ *l = '\0'; ++ how = l + 1; ++ break; ++ } ++ } ++ ++ /* skip directory components */ ++ if ((l = strrchr(line, '/'))) ++ line = l + 1; ++ /* strip .ko extension */ ++ l = line + strlen(line); ++ if (l - line > 3 && !strcmp(l-3, ".ko")) ++ *(l-3) = '\0'; ++ ++ /* skip directory components */ ++ if ((basename = strrchr(mod->name, '/'))) ++ basename++; ++ else ++ basename = mod->name; ++ if (!strcmp(basename, line)) ++ return how; ++ } ++ return NULL; ++} ++ + static void read_symbols(char *modname) + { + const char *symname; +@@ -1703,6 +1745,13 @@ static void add_staging_flag(struct buff + buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); + } + ++static void add_supported_flag(struct buffer *b, struct module *mod) ++{ ++ const char *how = supported(mod); ++ if (how) ++ buf_printf(b, "\nMODULE_INFO(supported, \"%s\");\n", how); ++} ++ + /** + * Record CRCs for unresolved symbols + **/ +@@ -1843,6 +1892,13 @@ static void write_if_changed(struct buff + fclose(file); + } + ++static void read_supported(const char *fname) ++{ ++ supported_file = grab_file(fname, &supported_size); ++ if (!supported_file) ++ ; /* ignore error */ ++} ++ + /* parse Module.symvers file. line format: + * 0x12345678symbolmodule[[export]something] + **/ +@@ -1936,12 +1992,13 @@ int main(int argc, char **argv) + struct buffer buf = { }; + char *kernel_read = NULL, *module_read = NULL; + char *dump_write = NULL; ++ const char *supported = NULL; + int opt; + int err; + struct ext_sym_list *extsym_iter; + struct ext_sym_list *extsym_start = NULL; + +- while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:")) != -1) { ++ while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:N:")) != -1) { + switch (opt) { + case 'i': + kernel_read = optarg; +@@ -1979,11 +2036,16 @@ int main(int argc, char **argv) + case 'w': + warn_unresolved = 1; + break; ++ case 'N': ++ supported = optarg; ++ break; + default: + exit(1); + } + } + ++ if (supported) ++ read_supported(supported); + if (kernel_read) + read_dump(kernel_read, 1); + if (module_read) +@@ -2016,6 +2078,7 @@ int main(int argc, char **argv) + + add_header(&buf, mod); + add_staging_flag(&buf, mod->name); ++ add_supported_flag(&buf, mod); + err |= add_versions(&buf, mod); + add_depends(&buf, mod, modules); + add_moddevtable(&buf, mod); diff --git a/patches.suse/supported-flag-enterprise b/patches.suse/supported-flag-enterprise new file mode 100644 index 0000000..75af4ad --- /dev/null +++ b/patches.suse/supported-flag-enterprise @@ -0,0 +1,227 @@ +From: Jeff Mahoney +Subject: Make the supported flag configurable at build time +References: bnc#528097 +Patch-mainline: Never, SLES feature + + In the enterprise kernels, it makes sense to have the supportability + facility. For openSUSE, it's unnecessary, cumbersome, and just plain + wrong. The support commitments for the two releases are totally + different and it doesn't make any sense to pretend that they are. + + This patch adds a CONFIG_ENTERPRISE_SUPPORT option, which enables the support + reporting facility. When it is disabled, the reporting and checking are too. + +Signed-off-by: Jeff Mahoney +--- + Documentation/kernel-parameters.txt | 2 ++ + include/linux/kernel.h | 2 ++ + init/Kconfig | 18 ++++++++++++++++++ + kernel/ksysfs.c | 4 ++++ + kernel/module.c | 12 ++++++++++++ + kernel/panic.c | 2 ++ + kernel/sysctl.c | 2 +- + scripts/Makefile.modpost | 5 +++-- + 8 files changed, 44 insertions(+), 3 deletions(-) + +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -2554,6 +2554,8 @@ and is between 256 and 4096 characters. + 1 = warn when loading unsupported modules, + 2 = don't warn. + ++ CONFIG_ENTERPRISE_SUPPORT must be enabled for this ++ to have any effect. + + swiotlb= [IA-64] Number of I/O TLB slabs + +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -347,12 +347,14 @@ extern enum system_states { + #define TAINT_WARN 9 + #define TAINT_CRAP 10 + ++#ifdef CONFIG_ENTERPRISE_SUPPORT + /* + * Take the upper bits to hopefully allow them + * to stay the same for more than one release. + */ + #define TAINT_NO_SUPPORT 30 + #define TAINT_EXTERNAL_SUPPORT 31 ++#endif + + extern void dump_stack(void) __cold; + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -2,6 +2,24 @@ config SUSE_KERNEL + bool + default y + ++config ENTERPRISE_SUPPORT ++ bool "Enable enterprise support facility" ++ depends on SUSE_KERNEL ++ default n ++ help ++ This feature enables the handling of the "supported" module flag. ++ This flag can be used to report unsupported module loads or even ++ refuse them entirely. It is useful when ensuring that the kernel ++ remains in a state that Novell Technical Services, or its ++ technical partners, is prepared to support. ++ ++ Modules in the list of supported modules will be marked supported ++ on build. The default enforcement mode is to report, but not ++ deny, loading of unsupported modules. ++ ++ If you aren't building a kernel for an enterprise distribution, ++ say n. ++ + config SPLIT_PACKAGE + bool "Split the kernel package into multiple RPMs" + depends on SUSE_KERNEL && MODULES +--- a/kernel/ksysfs.c ++++ b/kernel/ksysfs.c +@@ -156,6 +156,7 @@ static struct bin_attribute notes_attr = + struct kobject *kernel_kobj; + EXPORT_SYMBOL_GPL(kernel_kobj); + ++#ifdef CONFIG_ENTERPRISE_SUPPORT + const char *supported_printable(int taint) + { + int mask = TAINT_PROPRIETARY_MODULE|TAINT_NO_SUPPORT; +@@ -177,6 +178,7 @@ static ssize_t supported_show(struct kob + return sprintf(buf, "%s\n", supported_printable(get_taint())); + } + KERNEL_ATTR_RO(supported); ++#endif + + static struct attribute * kernel_attrs[] = { + #if defined(CONFIG_HOTPLUG) +@@ -192,7 +194,9 @@ static struct attribute * kernel_attrs[] + &kexec_crash_size_attr.attr, + &vmcoreinfo_attr.attr, + #endif ++#ifdef CONFIG_ENTERPRISE_SUPPORT + &supported_attr.attr, ++#endif + NULL + }; + +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -74,6 +74,7 @@ EXPORT_TRACEPOINT_SYMBOL(module_get); + /* If this is set, the section belongs in the init part of the module */ + #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) + ++#ifdef CONFIG_ENTERPRISE_SUPPORT + /* Allow unsupported modules switch. */ + #ifdef UNSUPPORTED_MODULES + int unsupported = UNSUPPORTED_MODULES; +@@ -87,6 +88,7 @@ static int __init unsupported_setup(char + return 1; + } + __setup("unsupported=", unsupported_setup); ++#endif + + /* List of modules, protected by module_mutex or preempt_disable + * (delete uses stop_machine/add uses RCU list operations). */ +@@ -870,6 +872,7 @@ static struct module_attribute initstate + .show = show_initstate, + }; + ++#ifdef CONFIG_ENTERPRISE_SUPPORT + static void setup_modinfo_supported(struct module *mod, const char *s) + { + if (!s) { +@@ -894,12 +897,15 @@ static struct module_attribute modinfo_s + .show = show_modinfo_supported, + .setup = setup_modinfo_supported, + }; ++#endif + + static struct module_attribute *modinfo_attrs[] = { + &modinfo_version, + &modinfo_srcversion, + &initstate, ++#ifdef CONFIG_ENTERPRISE_SUPPORT + &modinfo_supported, ++#endif + #ifdef CONFIG_MODULE_UNLOAD + &refcnt, + #endif +@@ -2421,6 +2427,7 @@ static noinline struct module *load_modu + add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); + add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs); + ++#ifdef CONFIG_ENTERPRISE_SUPPORT + /* We don't use add_taint() here because it also disables lockdep. */ + if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT)) + add_nonfatal_taint(TAINT_EXTERNAL_SUPPORT); +@@ -2440,6 +2447,7 @@ static noinline struct module *load_modu + "fault.\n", mod->name); + } + } ++#endif + + /* Get rid of temporary copy */ + vfree(hdr); +@@ -2808,10 +2816,12 @@ static char *module_flags(struct module + buf[bx++] = 'F'; + if (mod->taints & (1 << TAINT_CRAP)) + buf[bx++] = 'C'; ++#ifdef CONFIG_ENTERPRISE_SUPPORT + if (mod->taints & (1 << TAINT_NO_SUPPORT)) + buf[bx++] = 'N'; + if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT)) + buf[bx++] = 'X'; ++#endif + /* + * TAINT_FORCED_RMMOD: could be added. + * TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't +@@ -3024,7 +3034,9 @@ void print_modules(void) + if (last_unloaded_module[0]) + printk(" [last unloaded: %s]", last_unloaded_module); + printk("\n"); ++#ifdef CONFIG_ENTERPRISE_SUPPORT + printk("Supported: %s\n", supported_printable(get_taint())); ++#endif + } + + #ifdef CONFIG_MODVERSIONS +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -178,8 +178,10 @@ static const struct tnt tnts[] = { + { TAINT_OVERRIDDEN_ACPI_TABLE, 'A', ' ' }, + { TAINT_WARN, 'W', ' ' }, + { TAINT_CRAP, 'C', ' ' }, ++#ifdef CONFIG_ENTERPRISE_SUPPORT + { TAINT_NO_SUPPORT, 'N', ' ' }, + { TAINT_EXTERNAL_SUPPORT, 'X', ' ' }, ++#endif + }; + + /** +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -636,7 +636,7 @@ static struct ctl_table kern_table[] = { + .extra1 = &pid_max_min, + .extra2 = &pid_max_max, + }, +-#ifdef CONFIG_MODULES ++#if defined(CONFIG_MODULES) && defined(CONFIG_ENTERPRISE_SUPPORT) + { + .procname = "unsupported", + .data = &unsupported, +--- a/scripts/Makefile.modpost ++++ b/scripts/Makefile.modpost +@@ -82,8 +82,9 @@ modpost = scripts/mod/modpost + $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ + $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ + $(if $(cross_build),-c) \ +- -N $(firstword $(wildcard $(dir $(MODVERDIR))/Module.supported \ +- $(objtree)/Module.supported /dev/null)) ++ $(if $(CONFIG_ENTERPRISE_SUPPORT), \ ++ -N $(firstword $(wildcard $(dir $(MODVERDIR))/Module.supported \ ++ $(objtree)/Module.supported /dev/null))) + + quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules + cmd_modpost = $(modpost) -s diff --git a/patches.suse/supported-flag-sysfs b/patches.suse/supported-flag-sysfs new file mode 100644 index 0000000..37b3c13 --- /dev/null +++ b/patches.suse/supported-flag-sysfs @@ -0,0 +1,210 @@ +From: Jeff Mahoney +Subject: Export supported status via sysfs +Patch-mainline: Never, SLES feature + + This patch adds a /sys/kernel/supported file indicating the supportability + status of the entire kernel. + + It also adds a /sys/module//supported file indicating the + supportability status of individual modules. + + This is useful because it can be used to obtain the supported status + of a running system without current modules (ie: immediately after + a kernel update but before a reboot) and without generating an oops. + +Signed-off-by: Jeff Mahoney + +--- + + include/linux/kernel.h | 1 + include/linux/module.h | 1 + kernel/ksysfs.c | 23 +++++++++++++++ + kernel/module.c | 71 +++++++++++++++++++++++++++++++++---------------- + kernel/panic.c | 5 +++ + 5 files changed, 78 insertions(+), 23 deletions(-) + +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -320,6 +320,7 @@ extern int panic_on_io_nmi; + extern int unsupported; + extern const char *print_tainted(void); + extern void add_taint(unsigned flag); ++extern void add_nonfatal_taint(unsigned flag); + extern int test_taint(unsigned flag); + extern unsigned long get_taint(void); + extern int root_mountflags; +--- a/include/linux/module.h ++++ b/include/linux/module.h +@@ -398,6 +398,7 @@ struct module *__module_address(unsigned + bool is_module_address(unsigned long addr); + bool is_module_percpu_address(unsigned long addr); + bool is_module_text_address(unsigned long addr); ++const char *supported_printable(int taint); + + static inline int within_module_core(unsigned long addr, struct module *mod) + { +--- a/kernel/ksysfs.c ++++ b/kernel/ksysfs.c +@@ -156,6 +156,28 @@ static struct bin_attribute notes_attr = + struct kobject *kernel_kobj; + EXPORT_SYMBOL_GPL(kernel_kobj); + ++const char *supported_printable(int taint) ++{ ++ int mask = TAINT_PROPRIETARY_MODULE|TAINT_NO_SUPPORT; ++ if ((taint & mask) == mask) ++ return "No, Proprietary and Unsupported modules are loaded"; ++ else if (taint & TAINT_PROPRIETARY_MODULE) ++ return "No, Proprietary modules are loaded"; ++ else if (taint & TAINT_NO_SUPPORT) ++ return "No, Unsupported modules are loaded"; ++ else if (taint & TAINT_EXTERNAL_SUPPORT) ++ return "Yes, External"; ++ else ++ return "Yes"; ++} ++ ++static ssize_t supported_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%s\n", supported_printable(get_taint())); ++} ++KERNEL_ATTR_RO(supported); ++ + static struct attribute * kernel_attrs[] = { + #if defined(CONFIG_HOTPLUG) + &uevent_seqnum_attr.attr, +@@ -170,6 +192,7 @@ static struct attribute * kernel_attrs[] + &kexec_crash_size_attr.attr, + &vmcoreinfo_attr.attr, + #endif ++ &supported_attr.attr, + NULL + }; + +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -937,10 +937,36 @@ static struct module_attribute initstate + .show = show_initstate, + }; + ++static void setup_modinfo_supported(struct module *mod, const char *s) ++{ ++ if (!s) { ++ mod->taints |= (1 << TAINT_NO_SUPPORT); ++ return; ++ } ++ ++ if (strcmp(s, "external") == 0) ++ mod->taints |= (1 << TAINT_EXTERNAL_SUPPORT); ++ else if (strcmp(s, "yes")) ++ mod->taints |= (1 << TAINT_NO_SUPPORT); ++} ++ ++static ssize_t show_modinfo_supported(struct module_attribute *mattr, ++ struct module *mod, char *buffer) ++{ ++ return sprintf(buffer, "%s\n", supported_printable(mod->taints)); ++} ++ ++static struct module_attribute modinfo_supported = { ++ .attr = { .name = "supported", .mode = 0444 }, ++ .show = show_modinfo_supported, ++ .setup = setup_modinfo_supported, ++}; ++ + static struct module_attribute *modinfo_attrs[] = { + &modinfo_version, + &modinfo_srcversion, + &initstate, ++ &modinfo_supported, + #ifdef CONFIG_MODULE_UNLOAD + &refcnt, + #endif +@@ -2027,7 +2053,7 @@ static noinline struct module *load_modu + Elf_Ehdr *hdr; + Elf_Shdr *sechdrs; + char *secstrings, *args, *modmagic, *strtab = NULL; +- char *staging, *supported; ++ char *staging; + unsigned int i; + unsigned int symindex = 0; + unsigned int strindex = 0; +@@ -2146,28 +2172,6 @@ static noinline struct module *load_modu + mod->name); + } + +- supported = get_modinfo(sechdrs, infoindex, "supported"); +- if (supported) { +- if (!strcmp(supported, "external")) +- add_taint_module(mod, TAINT_EXTERNAL_SUPPORT); +- else if (strcmp(supported, "yes")) +- supported = NULL; +- } +- if (!supported) { +- if (unsupported == 0) { +- printk(KERN_WARNING "%s: module not supported by " +- "Novell, refusing to load. To override, echo " +- "1 > /proc/sys/kernel/unsupported\n", mod->name); +- err = -ENOEXEC; +- goto free_hdr; +- } +- add_taint_module(mod, TAINT_NO_SUPPORT); +- if (unsupported == 1) { +- printk(KERN_WARNING "%s: module not supported by " +- "Novell, setting U taint flag.\n", mod->name); +- } +- } +- + /* Now copy in args */ + args = strndup_user(uargs, ~0UL >> 1); + if (IS_ERR(args)) { +@@ -2479,6 +2483,26 @@ static noinline struct module *load_modu + add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); + add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs); + ++ /* We don't use add_taint() here because it also disables lockdep. */ ++ if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT)) ++ add_nonfatal_taint(TAINT_EXTERNAL_SUPPORT); ++ else if (mod->taints == (1 << TAINT_NO_SUPPORT)) { ++ if (unsupported == 0) { ++ printk(KERN_WARNING "%s: module not supported by " ++ "Novell, refusing to load. To override, echo " ++ "1 > /proc/sys/kernel/unsupported\n", mod->name); ++ err = -ENOEXEC; ++ goto free_hdr; ++ } ++ add_nonfatal_taint(TAINT_NO_SUPPORT); ++ if (unsupported == 1) { ++ printk(KERN_WARNING "%s: module is not supported by " ++ "Novell. Novell Technical Services may decline " ++ "your support request if it involves a kernel " ++ "fault.\n", mod->name); ++ } ++ } ++ + /* Get rid of temporary copy */ + vfree(hdr); + +@@ -3061,6 +3085,7 @@ void print_modules(void) + if (last_unloaded_module[0]) + printk(" [last unloaded: %s]", last_unloaded_module); + printk("\n"); ++ printk("Supported: %s\n", supported_printable(get_taint())); + } + + #ifdef CONFIG_MODVERSIONS +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -233,6 +233,11 @@ unsigned long get_taint(void) + return tainted_mask; + } + ++void add_nonfatal_taint(unsigned flag) ++{ ++ set_bit(flag, &tainted_mask); ++} ++ + void add_taint(unsigned flag) + { + /* diff --git a/patches.suse/suse-ppc64-branding b/patches.suse/suse-ppc64-branding new file mode 100644 index 0000000..3ff0a09 --- /dev/null +++ b/patches.suse/suse-ppc64-branding @@ -0,0 +1,21 @@ +From: +Subject: display the product in the frontpanel LCD +Patch-mainline: never + +display the product in the frontpanel LCD +also the uname -r output instead of uname -v. + + arch/powerpc/platforms/pseries/setup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/powerpc/platforms/pseries/setup.c ++++ b/arch/powerpc/platforms/pseries/setup.c +@@ -319,7 +319,7 @@ static void __init pSeries_setup_arch(vo + static int __init pSeries_init_panel(void) + { + /* Manually leave the kernel version on the panel. */ +- ppc_md.progress("Linux ppc64\n", 0); ++ ppc_md.progress("SUSE Linux\n", 0); + ppc_md.progress(init_utsname()->version, 0); + + return 0; diff --git a/patches.suse/twofish-2.6 b/patches.suse/twofish-2.6 new file mode 100644 index 0000000..83c7ba0 --- /dev/null +++ b/patches.suse/twofish-2.6 @@ -0,0 +1,664 @@ +Subject: Twofish encryption for loop device for old S.u.S.E. crypto partitions +From: kraxel@suse.de +Patch-mainline: not yet + +See $subject, used up to 9.2 on new installs. + +--- + drivers/block/Kconfig | 6 + drivers/block/Makefile | 2 + drivers/block/loop_fish2.c | 625 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 633 insertions(+) + +--- a/drivers/block/Kconfig ++++ b/drivers/block/Kconfig +@@ -452,6 +452,12 @@ config SUNVDC + Support for virtual disk devices as a client under Sun + Logical Domains. + ++config CIPHER_TWOFISH ++ tristate "Twofish encryption for loop device for old S.u.S.E. crypto partitions" ++ depends on BLK_DEV_LOOP ++ help ++ Say Y here if you want to support old S.u.S.E. crypto partitions. ++ + source "drivers/s390/block/Kconfig" + + config XILINX_SYSACE +--- a/drivers/block/Makefile ++++ b/drivers/block/Makefile +@@ -38,4 +38,6 @@ obj-$(CONFIG_BLK_DEV_HD) += hd.o + obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o + obj-$(CONFIG_BLK_DEV_DRBD) += drbd/ + ++obj-$(CONFIG_CIPHER_TWOFISH) += loop_fish2.o ++ + swim_mod-objs := swim.o swim_asm.o +--- /dev/null ++++ b/drivers/block/loop_fish2.c +@@ -0,0 +1,625 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ROL(x,c) (((x) << (c)) | ((x) >> (32-(c)))) ++#define ROR(x,c) (((x) >> (c)) | ((x) << (32-(c)))) ++#define Bswap(x) __le32_to_cpu(x) ++ ++#define DWORD __u32 ++#define BYTE unsigned char ++ ++typedef struct fish2_key ++{ int keyLen; /* Key Length in Bit */ ++ DWORD sboxKeys[4]; ++ DWORD subKeys[40]; ++ BYTE key[32]; ++ DWORD sbox_full[1024]; /* This have to be 1024 DWORDs */ ++} fish2_key; ++ ++ ++/* Mul_5B[i] is 0x5B * i in GF(256), whatever that means... */ ++ ++static unsigned char Mul_5B[256] = { ++ 0x00,0x5B,0xB6,0xED,0x05,0x5E,0xB3,0xE8, ++ 0x0A,0x51,0xBC,0xE7,0x0F,0x54,0xB9,0xE2, ++ 0x14,0x4F,0xA2,0xF9,0x11,0x4A,0xA7,0xFC, ++ 0x1E,0x45,0xA8,0xF3,0x1B,0x40,0xAD,0xF6, ++ 0x28,0x73,0x9E,0xC5,0x2D,0x76,0x9B,0xC0, ++ 0x22,0x79,0x94,0xCF,0x27,0x7C,0x91,0xCA, ++ 0x3C,0x67,0x8A,0xD1,0x39,0x62,0x8F,0xD4, ++ 0x36,0x6D,0x80,0xDB,0x33,0x68,0x85,0xDE, ++ 0x50,0x0B,0xE6,0xBD,0x55,0x0E,0xE3,0xB8, ++ 0x5A,0x01,0xEC,0xB7,0x5F,0x04,0xE9,0xB2, ++ 0x44,0x1F,0xF2,0xA9,0x41,0x1A,0xF7,0xAC, ++ 0x4E,0x15,0xF8,0xA3,0x4B,0x10,0xFD,0xA6, ++ 0x78,0x23,0xCE,0x95,0x7D,0x26,0xCB,0x90, ++ 0x72,0x29,0xC4,0x9F,0x77,0x2C,0xC1,0x9A, ++ 0x6C,0x37,0xDA,0x81,0x69,0x32,0xDF,0x84, ++ 0x66,0x3D,0xD0,0x8B,0x63,0x38,0xD5,0x8E, ++ 0xA0,0xFB,0x16,0x4D,0xA5,0xFE,0x13,0x48, ++ 0xAA,0xF1,0x1C,0x47,0xAF,0xF4,0x19,0x42, ++ 0xB4,0xEF,0x02,0x59,0xB1,0xEA,0x07,0x5C, ++ 0xBE,0xE5,0x08,0x53,0xBB,0xE0,0x0D,0x56, ++ 0x88,0xD3,0x3E,0x65,0x8D,0xD6,0x3B,0x60, ++ 0x82,0xD9,0x34,0x6F,0x87,0xDC,0x31,0x6A, ++ 0x9C,0xC7,0x2A,0x71,0x99,0xC2,0x2F,0x74, ++ 0x96,0xCD,0x20,0x7B,0x93,0xC8,0x25,0x7E, ++ 0xF0,0xAB,0x46,0x1D,0xF5,0xAE,0x43,0x18, ++ 0xFA,0xA1,0x4C,0x17,0xFF,0xA4,0x49,0x12, ++ 0xE4,0xBF,0x52,0x09,0xE1,0xBA,0x57,0x0C, ++ 0xEE,0xB5,0x58,0x03,0xEB,0xB0,0x5D,0x06, ++ 0xD8,0x83,0x6E,0x35,0xDD,0x86,0x6B,0x30, ++ 0xD2,0x89,0x64,0x3F,0xD7,0x8C,0x61,0x3A, ++ 0xCC,0x97,0x7A,0x21,0xC9,0x92,0x7F,0x24, ++ 0xC6,0x9D,0x70,0x2B,0xC3,0x98,0x75,0x2E }; ++ ++ ++/* Mul_EF[i] is 0xEF * i in GF(256), whatever that means... */ ++ ++static unsigned char Mul_EF[256] = { ++ 0x00,0xEF,0xB7,0x58,0x07,0xE8,0xB0,0x5F, ++ 0x0E,0xE1,0xB9,0x56,0x09,0xE6,0xBE,0x51, ++ 0x1C,0xF3,0xAB,0x44,0x1B,0xF4,0xAC,0x43, ++ 0x12,0xFD,0xA5,0x4A,0x15,0xFA,0xA2,0x4D, ++ 0x38,0xD7,0x8F,0x60,0x3F,0xD0,0x88,0x67, ++ 0x36,0xD9,0x81,0x6E,0x31,0xDE,0x86,0x69, ++ 0x24,0xCB,0x93,0x7C,0x23,0xCC,0x94,0x7B, ++ 0x2A,0xC5,0x9D,0x72,0x2D,0xC2,0x9A,0x75, ++ 0x70,0x9F,0xC7,0x28,0x77,0x98,0xC0,0x2F, ++ 0x7E,0x91,0xC9,0x26,0x79,0x96,0xCE,0x21, ++ 0x6C,0x83,0xDB,0x34,0x6B,0x84,0xDC,0x33, ++ 0x62,0x8D,0xD5,0x3A,0x65,0x8A,0xD2,0x3D, ++ 0x48,0xA7,0xFF,0x10,0x4F,0xA0,0xF8,0x17, ++ 0x46,0xA9,0xF1,0x1E,0x41,0xAE,0xF6,0x19, ++ 0x54,0xBB,0xE3,0x0C,0x53,0xBC,0xE4,0x0B, ++ 0x5A,0xB5,0xED,0x02,0x5D,0xB2,0xEA,0x05, ++ 0xE0,0x0F,0x57,0xB8,0xE7,0x08,0x50,0xBF, ++ 0xEE,0x01,0x59,0xB6,0xE9,0x06,0x5E,0xB1, ++ 0xFC,0x13,0x4B,0xA4,0xFB,0x14,0x4C,0xA3, ++ 0xF2,0x1D,0x45,0xAA,0xF5,0x1A,0x42,0xAD, ++ 0xD8,0x37,0x6F,0x80,0xDF,0x30,0x68,0x87, ++ 0xD6,0x39,0x61,0x8E,0xD1,0x3E,0x66,0x89, ++ 0xC4,0x2B,0x73,0x9C,0xC3,0x2C,0x74,0x9B, ++ 0xCA,0x25,0x7D,0x92,0xCD,0x22,0x7A,0x95, ++ 0x90,0x7F,0x27,0xC8,0x97,0x78,0x20,0xCF, ++ 0x9E,0x71,0x29,0xC6,0x99,0x76,0x2E,0xC1, ++ 0x8C,0x63,0x3B,0xD4,0x8B,0x64,0x3C,0xD3, ++ 0x82,0x6D,0x35,0xDA,0x85,0x6A,0x32,0xDD, ++ 0xA8,0x47,0x1F,0xF0,0xAF,0x40,0x18,0xF7, ++ 0xA6,0x49,0x11,0xFE,0xA1,0x4E,0x16,0xF9, ++ 0xB4,0x5B,0x03,0xEC,0xB3,0x5C,0x04,0xEB, ++ 0xBA,0x55,0x0D,0xE2,0xBD,0x52,0x0A,0xE5 }; ++ ++static inline DWORD mds_mul(BYTE *y) ++{ DWORD z; ++ ++ z=Mul_EF[y[0]] ^ y[1] ^ Mul_EF[y[2]] ^ Mul_5B[y[3]]; ++ z<<=8; ++ z|=Mul_EF[y[0]] ^ Mul_5B[y[1]] ^ y[2] ^ Mul_EF[y[3]]; ++ z<<=8; ++ z|=Mul_5B[y[0]] ^ Mul_EF[y[1]] ^ Mul_EF[y[2]] ^ y[3]; ++ z<<=8; ++ z|=y[0] ^ Mul_EF[y[1]] ^ Mul_5B[y[2]] ^ Mul_5B[y[3]]; ++ ++ return z; ++} ++ ++/* q0 and q1 are the lookup substitutions done in twofish */ ++ ++static unsigned char q0[256] = ++{ 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, ++ 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, ++ 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, ++ 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, ++ 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, ++ 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, ++ 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, ++ 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, ++ 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, ++ 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, ++ 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, ++ 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, ++ 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, ++ 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, ++ 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, ++ 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, ++ 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, ++ 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, ++ 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, ++ 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, ++ 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, ++ 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, ++ 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, ++ 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, ++ 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, ++ 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, ++ 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, ++ 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, ++ 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, ++ 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, ++ 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, ++ 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0}; ++ ++static unsigned char q1[256] = ++{ 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, ++ 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, ++ 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, ++ 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, ++ 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, ++ 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, ++ 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, ++ 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, ++ 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, ++ 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, ++ 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, ++ 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, ++ 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, ++ 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, ++ 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, ++ 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, ++ 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, ++ 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, ++ 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, ++ 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, ++ 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, ++ 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, ++ 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, ++ 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, ++ 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, ++ 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, ++ 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, ++ 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, ++ 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, ++ 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, ++ 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, ++ 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 ++ }; ++ ++ ++static DWORD f32(DWORD x, const DWORD * k32, int keyLen) ++{ ++ BYTE b[4]; ++ ++ /* Run each byte thru 8x8 S-boxes, xoring with key byte at each stage. */ ++ /* Note that each byte goes through a different combination of S-boxes. */ ++ ++ *((DWORD *) b) = Bswap(x); /* make b[0] = LSB, b[3] = MSB */ ++ ++ switch (((keyLen + 63) / 64) & 3) ++ { ++ case 0: /* 256 bits of key */ ++ b[0] = q1[b[0]]; ++ b[1] = q0[b[1]]; ++ b[2] = q0[b[2]]; ++ b[3] = q1[b[3]]; ++ ++ *((DWORD *) b) ^= k32[3]; ++ ++ /* fall thru, having pre-processed b[0]..b[3] with k32[3] */ ++ case 3: /* 192 bits of key */ ++ b[0] = q1[b[0]]; ++ b[1] = q1[b[1]]; ++ b[2] = q0[b[2]]; ++ b[3] = q0[b[3]]; ++ ++ *((DWORD *) b) ^= k32[2]; ++ ++ /* fall thru, having pre-processed b[0]..b[3] with k32[2] */ ++ case 2: /* 128 bits of key */ ++ b[0] = q0[b[0]]; ++ b[1] = q1[b[1]]; ++ b[2] = q0[b[2]]; ++ b[3] = q1[b[3]]; ++ ++ *((DWORD *) b) ^= k32[1]; ++ ++ b[0] = q0[b[0]]; ++ b[1] = q0[b[1]]; ++ b[2] = q1[b[2]]; ++ b[3] = q1[b[3]]; ++ ++ *((DWORD *) b) ^= k32[0]; ++ ++ b[0] = q1[b[0]]; ++ b[1] = q0[b[1]]; ++ b[2] = q1[b[2]]; ++ b[3] = q0[b[3]]; ++ } ++ ++ ++ /* Now perform the MDS matrix multiply inline. */ ++ return mds_mul(b); ++} ++ ++ ++static void init_sbox(fish2_key *key) ++{ DWORD x,*sbox,z,*k32; ++ int i,keyLen; ++ BYTE b[4]; ++ ++ k32=key->sboxKeys; ++ keyLen=key->keyLen; ++ sbox=key->sbox_full; ++ ++ x=0; ++ for (i=0;i<256;i++,x+=0x01010101) ++ { ++ *((DWORD *) b) = Bswap(x); /* make b[0] = LSB, b[3] = MSB */ ++ ++ switch (((keyLen + 63) / 64) & 3) ++ { ++ case 0: /* 256 bits of key */ ++ b[0] = q1[b[0]]; ++ b[1] = q0[b[1]]; ++ b[2] = q0[b[2]]; ++ b[3] = q1[b[3]]; ++ ++ *((DWORD *) b) ^= k32[3]; ++ ++ /* fall thru, having pre-processed b[0]..b[3] with k32[3] */ ++ case 3: /* 192 bits of key */ ++ b[0] = q1[b[0]]; ++ b[1] = q1[b[1]]; ++ b[2] = q0[b[2]]; ++ b[3] = q0[b[3]]; ++ ++ *((DWORD *) b) ^= k32[2]; ++ ++ /* fall thru, having pre-processed b[0]..b[3] with k32[2] */ ++ case 2: /* 128 bits of key */ ++ b[0] = q0[b[0]]; ++ b[1] = q1[b[1]]; ++ b[2] = q0[b[2]]; ++ b[3] = q1[b[3]]; ++ ++ *((DWORD *) b) ^= k32[1]; ++ ++ b[0] = q0[b[0]]; ++ b[1] = q0[b[1]]; ++ b[2] = q1[b[2]]; ++ b[3] = q1[b[3]]; ++ ++ *((DWORD *) b) ^= k32[0]; ++ ++ b[0] = q1[b[0]]; ++ b[1] = q0[b[1]]; ++ b[2] = q1[b[2]]; ++ b[3] = q0[b[3]]; ++ } ++ ++ z=Mul_EF[b[0]]; ++ z<<=8; ++ z|=Mul_EF[b[0]]; ++ z<<=8; ++ z|=Mul_5B[b[0]]; ++ z<<=8; ++ z|=b[0]; ++ ++ sbox[i]=z; ++ ++ z=b[1]; ++ z<<=8; ++ z|=Mul_5B[b[1]]; ++ z<<=8; ++ z|=Mul_EF[b[1]]; ++ z<<=8; ++ z|=Mul_EF[b[1]]; ++ ++ sbox[i+256]=z; ++ ++ z=Mul_EF[b[2]]; ++ z<<=8; ++ z|=b[2]; ++ z<<=8; ++ z|=Mul_EF[b[2]]; ++ z<<=8; ++ z|=Mul_5B[b[2]]; ++ ++ sbox[i+512]=z; ++ ++ z=Mul_5B[b[3]]; ++ z<<=8; ++ z|=Mul_EF[b[3]]; ++ z<<=8; ++ z|=b[3]; ++ z<<=8; ++ z|=Mul_5B[b[3]]; ++ ++ sbox[i+768]=z; ++ } ++} ++ ++ ++/* Reed-Solomon code parameters: (12,8) reversible code ++ g(x) = x**4 + (a + 1/a) x**3 + a x**2 + (a + 1/a) x + 1 ++ where a = primitive root of field generator 0x14D */ ++#define RS_GF_FDBK 0x14D /* field generator */ ++#define RS_rem(x) \ ++ { BYTE b = x >> 24; \ ++ DWORD g2 = ((b << 1) ^ ((b & 0x80) ? RS_GF_FDBK : 0 )) & 0xFF; \ ++ DWORD g3 = ((b >> 1) & 0x7F) ^ ((b & 1) ? RS_GF_FDBK >> 1 : 0 ) ^ g2 ; \ ++ x = (x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b; \ ++ } ++ ++static DWORD rs_mds(DWORD k0, DWORD k1) ++{ ++ int i, j; ++ DWORD r; ++ ++ for (i = r = 0; i < 2; i++) ++ { ++ r ^= (i) ? k0 : k1; /* merge in 32 more key bits */ ++ for (j = 0; j < 4; j++) /* shift one byte at a time */ ++ RS_rem(r); ++ } ++ return r; ++} ++ ++ ++#define INPUT_WHITEN 0 /* subkey array indices */ ++#define OUTPUT_WHITEN 4 ++#define ROUND_SUBKEYS 8 /* use 2 * (# rounds) */ ++#define TOTAL_SUBKEYS 40 ++ ++static void init_key(fish2_key * key) ++{ ++ int i, k64Cnt; ++ int keyLen = key->keyLen; ++ int subkeyCnt = TOTAL_SUBKEYS; ++ DWORD A, B; ++ DWORD k32e[4], k32o[4]; /* even/odd key dwords */ ++ ++ k64Cnt = (keyLen + 63) / 64; /* round up to next multiple of 64 bits */ ++ for (i = 0; i < k64Cnt; i++) ++ { /* split into even/odd key dwords */ ++ k32e[i] = ((DWORD *)key->key)[2 * i]; ++ k32o[i] = ((DWORD *)key->key)[2 * i + 1]; ++ /* compute S-box keys using (12,8) Reed-Solomon code over GF(256) */ ++ /* store in reverse order */ ++ key->sboxKeys[k64Cnt - 1 - i] = ++ Bswap(rs_mds(Bswap(k32e[i]), Bswap(k32o[i]))); ++ ++ } ++ ++ for (i = 0; i < subkeyCnt / 2; i++) /* compute round subkeys for PHT */ ++ { ++ A = f32(i * 0x02020202, k32e, keyLen); /* A uses even key dwords */ ++ B = f32(i * 0x02020202 + 0x01010101, k32o, keyLen); /* B uses odd key ++ dwords */ ++ B = ROL(B, 8); ++ key->subKeys[2 * i] = A + B; /* combine with a PHT */ ++ key->subKeys[2 * i + 1] = ROL(A + 2 * B, 9); ++ } ++ ++ init_sbox(key); ++} ++ ++ ++static inline DWORD f32_sbox(DWORD x,DWORD *sbox) ++{ ++ /* Run each byte thru 8x8 S-boxes, xoring with key byte at each stage. */ ++ /* Note that each byte goes through a different combination of S-boxes. */ ++ ++ return (sbox[ (x) &0xff]^ ++ sbox[256 + (((x)>> 8)&0xff)]^ ++ sbox[512 + (((x)>>16)&0xff)]^ ++ sbox[768 + (((x)>>24)&0xff)]); ++} ++ ++#define roundE_m(x0,x1,x2,x3,rnd) \ ++ t0 = f32_sbox( x0, key->sbox_full ) ; \ ++ t1 = f32_sbox( ROL(x1,8), key->sbox_full ); \ ++ x2 ^= t0 + t1 + key->subKeys[2*rnd+8]; \ ++ x3 = ROL(x3,1); \ ++ x3 ^= t0 + 2*t1 + key->subKeys[2*rnd+9]; \ ++ x2 = ROR(x2,1); ++ ++ ++static int blockEncrypt_CBC(fish2_key *key,BYTE *src,BYTE *dst,int len) ++{ DWORD xx0,xx1,xx2,xx3,t0,t1,iv0,iv1,iv2,iv3; ++ ++ if (len & 0xF) return -1; ++ ++ iv0=0; ++ iv1=0; ++ iv2=0; ++ iv3=0; ++ for (;len>=16;len-=16) ++ ++ { ++ if ( ( len & 0x1FF) == 0) ++ { iv0=0; ++ iv1=0; ++ iv2=0; ++ iv3=0; ++ } ++ ++ xx0=Bswap(((DWORD *)src)[0]) ^ key->subKeys[0] ^ iv0; ++ xx1=Bswap(((DWORD *)src)[1]) ^ key->subKeys[1] ^ iv1; ++ xx2=Bswap(((DWORD *)src)[2]) ^ key->subKeys[2] ^ iv2; ++ xx3=Bswap(((DWORD *)src)[3]) ^ key->subKeys[3] ^ iv3; ++ ++ src+=16; ++ ++ roundE_m(xx0,xx1,xx2,xx3,0); ++ roundE_m(xx2,xx3,xx0,xx1,1); ++ roundE_m(xx0,xx1,xx2,xx3,2); ++ roundE_m(xx2,xx3,xx0,xx1,3); ++ roundE_m(xx0,xx1,xx2,xx3,4); ++ roundE_m(xx2,xx3,xx0,xx1,5); ++ roundE_m(xx0,xx1,xx2,xx3,6); ++ roundE_m(xx2,xx3,xx0,xx1,7); ++ roundE_m(xx0,xx1,xx2,xx3,8); ++ roundE_m(xx2,xx3,xx0,xx1,9); ++ roundE_m(xx0,xx1,xx2,xx3,10); ++ roundE_m(xx2,xx3,xx0,xx1,11); ++ roundE_m(xx0,xx1,xx2,xx3,12); ++ roundE_m(xx2,xx3,xx0,xx1,13); ++ roundE_m(xx0,xx1,xx2,xx3,14); ++ roundE_m(xx2,xx3,xx0,xx1,15); ++ ++ iv0=xx2 ^ key->subKeys[4]; ++ iv1=xx3 ^ key->subKeys[5]; ++ iv2=xx0 ^ key->subKeys[6]; ++ iv3=xx1 ^ key->subKeys[7]; ++ ++ ((DWORD *)dst)[0] = Bswap(iv0); ++ ((DWORD *)dst)[1] = Bswap(iv1); ++ ((DWORD *)dst)[2] = Bswap(iv2); ++ ((DWORD *)dst)[3] = Bswap(iv3); ++ dst+=16; ++ } ++ return len; ++} ++ ++#define roundD_m(x0,x1,x2,x3,rnd) \ ++ t0 = f32_sbox( x0, key->sbox_full); \ ++ t1 = f32_sbox( ROL(x1,8),key->sbox_full); \ ++ x2 = ROL(x2,1); \ ++ x3 ^= t0 + 2*t1 + key->subKeys[rnd*2+9]; \ ++ x3 = ROR(x3,1); \ ++ x2 ^= t0 + t1 + key->subKeys[rnd*2+8]; ++ ++ ++static int blockDecrypt_CBC(fish2_key *key,BYTE *src,BYTE *dst,int len) ++{ DWORD xx0,xx1,xx2,xx3,t0,t1,lx0,lx1,lx2,lx3,iv0,iv1,iv2,iv3; ++ ++ if (len & 0xF) return -1; ++ ++ iv0=0; ++ iv1=0; ++ iv2=0; ++ iv3=0; ++ ++ for (;len>=16;len-=16) ++ { ++ if ( ( len & 0x1FF) == 0) ++ { iv0=0; ++ iv1=0; ++ iv2=0; ++ iv3=0; ++ } ++ ++ lx0=iv0;iv0=Bswap(((DWORD *)src)[0]);xx0=iv0 ^ key->subKeys[4]; ++ lx1=iv1;iv1=Bswap(((DWORD *)src)[1]);xx1=iv1 ^ key->subKeys[5]; ++ lx2=iv2;iv2=Bswap(((DWORD *)src)[2]);xx2=iv2 ^ key->subKeys[6]; ++ lx3=iv3;iv3=Bswap(((DWORD *)src)[3]);xx3=iv3 ^ key->subKeys[7]; ++ src+=16; ++ ++ roundD_m(xx0,xx1,xx2,xx3,15); ++ roundD_m(xx2,xx3,xx0,xx1,14); ++ roundD_m(xx0,xx1,xx2,xx3,13); ++ roundD_m(xx2,xx3,xx0,xx1,12); ++ roundD_m(xx0,xx1,xx2,xx3,11); ++ roundD_m(xx2,xx3,xx0,xx1,10); ++ roundD_m(xx0,xx1,xx2,xx3,9); ++ roundD_m(xx2,xx3,xx0,xx1,8); ++ roundD_m(xx0,xx1,xx2,xx3,7); ++ roundD_m(xx2,xx3,xx0,xx1,6); ++ roundD_m(xx0,xx1,xx2,xx3,5); ++ roundD_m(xx2,xx3,xx0,xx1,4); ++ roundD_m(xx0,xx1,xx2,xx3,3); ++ roundD_m(xx2,xx3,xx0,xx1,2); ++ roundD_m(xx0,xx1,xx2,xx3,1); ++ roundD_m(xx2,xx3,xx0,xx1,0); ++ ++ ((DWORD *)dst)[0] = Bswap(xx2 ^ key->subKeys[0] ^ lx0); ++ ((DWORD *)dst)[1] = Bswap(xx3 ^ key->subKeys[1] ^ lx1); ++ ((DWORD *)dst)[2] = Bswap(xx0 ^ key->subKeys[2] ^ lx2); ++ ((DWORD *)dst)[3] = Bswap(xx1 ^ key->subKeys[3] ^ lx3); ++ dst+=16; ++ } ++ return len; ++} ++ ++ ++int transfer_fish2(struct loop_device *lo, int cmd, ++ struct page *raw_page, unsigned raw_off, ++ struct page *loop_page, unsigned loop_off, ++ int size, sector_t IV) ++{ ++ char *raw_buf = kmap_atomic(raw_page, KM_USER0) + raw_off; ++ char *loop_buf = kmap_atomic(loop_page, KM_USER1) + loop_off; ++ ++ if (cmd == READ) ++ blockDecrypt_CBC((fish2_key *)lo->key_data,raw_buf,loop_buf,size); ++ else ++ blockEncrypt_CBC((fish2_key *)lo->key_data,loop_buf,raw_buf,size); ++ ++ kunmap_atomic(raw_buf, KM_USER0); ++ kunmap_atomic(loop_buf, KM_USER1); ++ cond_resched(); ++ ++ return 0; ++} ++ ++int fish2_init(struct loop_device *lo,const struct loop_info64 *info) ++{ fish2_key *key; ++ ++ if (info->lo_encrypt_key_size<16 || info->lo_encrypt_key_size>32) ++ return -EINVAL; ++ ++ key=(fish2_key *)kmalloc(sizeof(fish2_key),GFP_KERNEL); ++ ++ if (key==NULL) ++ return -ENOMEM; ++ ++ lo->key_data=key; ++ ++ memset(key->key,0,32); ++ ++ key->keyLen=info->lo_encrypt_key_size << 3; ++ memcpy(key->key,info->lo_encrypt_key,info->lo_encrypt_key_size); ++ ++ init_key(key); ++ ++ return 0; ++} ++ ++static int fish2_release(struct loop_device *lo) ++{ if (lo->key_data!=NULL) ++ { ++ kfree(lo->key_data); ++ lo->key_data=NULL; ++ } ++ return(0); ++} ++ ++static struct loop_func_table fish2_funcs = ++{ .number = LO_CRYPT_FISH2, ++ .transfer = transfer_fish2, ++ .init = fish2_init, ++ .release = fish2_release, ++ .owner = THIS_MODULE ++}; ++ ++int __init loop_fish2_init(void) ++{ ++ int err; ++ ++ if ((err=loop_register_transfer(&fish2_funcs))) ++ { ++ printk(KERN_WARNING "Couldn't register Twofish encryption\n"); ++ return err; ++ } ++ printk(KERN_INFO "loop: registered Twofish encryption \n"); ++ return 0; ++} ++ ++void __exit loop_fish2_exit(void) ++{ ++ if (loop_unregister_transfer(LO_CRYPT_FISH2)) ++ printk(KERN_WARNING "Couldn't unregister Twofish encryption\n"); ++ printk(KERN_INFO "loop: unregistered Twofish encryption \n"); ++} ++ ++module_init(loop_fish2_init); ++module_exit(loop_fish2_exit); ++MODULE_LICENSE("GPL"); diff --git a/patches.suse/unmap_vmas-lat b/patches.suse/unmap_vmas-lat new file mode 100644 index 0000000..6776999 --- /dev/null +++ b/patches.suse/unmap_vmas-lat @@ -0,0 +1,33 @@ +From: andrea@suse.de +Subject: low-latency stuff +Patch-mainline: not yet + + +My point is that preempt and no-preempt should do the same thing there, +otherwise when you benchmark -preempt, you'll get better latency, +but not because of the preempt feature, but just because of unrelated +latency improvements that have nothing to do with preempt. + + +--- + mm/memory.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -1061,11 +1061,11 @@ static unsigned long unmap_page_range(st + return addr; + } + +-#ifdef CONFIG_PREEMPT +-# define ZAP_BLOCK_SIZE (8 * PAGE_SIZE) ++#ifdef CONFIG_SMP ++/* zap one pte page at a time */ ++#define ZAP_BLOCK_SIZE (FREE_PTE_NR * PAGE_SIZE) + #else +-/* No preempt: go for improved straight-line efficiency */ +-# define ZAP_BLOCK_SIZE (1024 * PAGE_SIZE) ++#define ZAP_BLOCK_SIZE (253 * PAGE_SIZE) + #endif + + /** diff --git a/patches.suse/uvcvideo-ignore-hue-control-for-5986-0241.patch b/patches.suse/uvcvideo-ignore-hue-control-for-5986-0241.patch new file mode 100644 index 0000000..7512e69 --- /dev/null +++ b/patches.suse/uvcvideo-ignore-hue-control-for-5986-0241.patch @@ -0,0 +1,57 @@ +From: Brandon Philips +Subject: uvcvideo: ignore hue control for 5986:0241 +References: bnc#499152 +Patch-mainline: Never? I will submit upstream but there is probably a better fix + +Querying the hue control on Bison 5986:0241 causes the chipset to +lockup. So, create a quirk that will avoid offering V4L2_CID_HUE to user +space. + +Signed-off-by: Brandon Philips + +--- + drivers/media/video/uvc/uvc_ctrl.c | 4 ++++ + drivers/media/video/uvc/uvc_driver.c | 8 ++++++++ + drivers/media/video/uvc/uvcvideo.h | 1 + + 3 files changed, 13 insertions(+) + +--- a/drivers/media/video/uvc/uvc_ctrl.c ++++ b/drivers/media/video/uvc/uvc_ctrl.c +@@ -811,6 +811,10 @@ int uvc_query_v4l2_ctrl(struct uvc_video + unsigned int i; + int ret; + ++ if ((chain->dev->quirks & UVC_QUIRK_HUE_EPIPE) && ++ (v4l2_ctrl->id == V4L2_CID_HUE)) ++ return -EINVAL; ++ + ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); + if (ctrl == NULL) + return -EINVAL; +--- a/drivers/media/video/uvc/uvc_driver.c ++++ b/drivers/media/video/uvc/uvc_driver.c +@@ -2198,6 +2198,14 @@ static struct usb_device_id uvc_ids[] = + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_IGNORE_SELECTOR_UNIT }, ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x5986, ++ .idProduct = 0x0241, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 0, ++ .driver_info = UVC_QUIRK_HUE_EPIPE }, + /* Generic USB Video Class */ + { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, + {} +--- a/drivers/media/video/uvc/uvcvideo.h ++++ b/drivers/media/video/uvc/uvcvideo.h +@@ -163,6 +163,7 @@ struct uvc_xu_control { + #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 + #define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 + #define UVC_QUIRK_PROBE_DEF 0x00000100 ++#define UVC_QUIRK_HUE_EPIPE 0x00000200 + + /* Format flags */ + #define UVC_FMT_FLAG_COMPRESSED 0x00000001 diff --git a/patches.suse/wireless-no-aes-select b/patches.suse/wireless-no-aes-select new file mode 100644 index 0000000..94197de --- /dev/null +++ b/patches.suse/wireless-no-aes-select @@ -0,0 +1,33 @@ +Subject: Don't force select AES in wireless +From: ak@suse.de +Patch-mainline: Not yet + +x86 kernels use always the assembler optimized versions of AES and TWOFISH. +But the wireless stack would force enable the generic C aes anyways. +Remove that. The optimized versions provide the cipher as well. + +--- + drivers/net/wireless/Kconfig | 1 - + net/mac80211/Kconfig | 1 - + 2 files changed, 2 deletions(-) + +--- a/drivers/net/wireless/Kconfig ++++ b/drivers/net/wireless/Kconfig +@@ -114,7 +114,6 @@ config AIRO_CS + select WEXT_SPY + select WEXT_PRIV + select CRYPTO +- select CRYPTO_AES + ---help--- + This is the standard Linux driver to support Cisco/Aironet PCMCIA + 802.11 wireless cards. This driver is the same as the Aironet +--- a/net/mac80211/Kconfig ++++ b/net/mac80211/Kconfig +@@ -4,7 +4,6 @@ config MAC80211 + select CRYPTO + select CRYPTO_ECB + select CRYPTO_ARC4 +- select CRYPTO_AES + select CRC32 + ---help--- + This option enables the hardware independent IEEE 802.11 diff --git a/patches.suse/x86-mark_rodata_rw.patch b/patches.suse/x86-mark_rodata_rw.patch new file mode 100644 index 0000000..affdcdc --- /dev/null +++ b/patches.suse/x86-mark_rodata_rw.patch @@ -0,0 +1,184 @@ +From: Nick Piggin +Subject: Add mark_rodata_rw() to un-protect read-only kernel code pages +References: bnc#439348 +Patch-mainline: probably never + +CONFIG_RODATA presents a problem for antivirus vendors who do not have a +clean user-space interface for getting virus scanning triggered, and +currently resort to patching the kernel code instead (presumably the +ystem call table). With CONFIG_RODATA enabled, the kernel rejects such +write accesses. + +Add a new mark_rodata_rw() function to un-protect the read-only kernel code +pages for now, and export mark_rodata_ro() and mark_rodata_rw() to modules. + +This is not meant as a permanent workaround, and will be removed again in the +next release! + +Acked-by: Andres Gruenbacher + +--- + arch/x86/include/asm/cacheflush.h | 3 +++ + arch/x86/mm/init_32.c | 14 ++++++++++++++ + arch/x86/mm/init_64.c | 31 +++++++++++++++++++++++++------ + arch/x86/mm/pageattr.c | 30 ++++++++++++++++++++++++++++-- + 4 files changed, 70 insertions(+), 8 deletions(-) + +--- a/arch/x86/include/asm/cacheflush.h ++++ b/arch/x86/include/asm/cacheflush.h +@@ -135,6 +135,7 @@ int set_memory_x(unsigned long addr, int + int set_memory_nx(unsigned long addr, int numpages); + int set_memory_ro(unsigned long addr, int numpages); + int set_memory_rw(unsigned long addr, int numpages); ++int set_memory_rw_force(unsigned long addr, int numpages); + int set_memory_np(unsigned long addr, int numpages); + int set_memory_4k(unsigned long addr, int numpages); + +@@ -170,12 +171,14 @@ int set_pages_x(struct page *page, int n + int set_pages_nx(struct page *page, int numpages); + int set_pages_ro(struct page *page, int numpages); + int set_pages_rw(struct page *page, int numpages); ++int set_pages_rw_force(struct page *page, int numpages); + + + void clflush_cache_range(void *addr, unsigned int size); + + #ifdef CONFIG_DEBUG_RODATA + void mark_rodata_ro(void); ++void mark_rodata_rw(void); + extern const int rodata_test_data; + extern int kernel_set_to_readonly; + void set_kernel_text_rw(void); +--- a/arch/x86/mm/init_32.c ++++ b/arch/x86/mm/init_32.c +@@ -1068,6 +1068,20 @@ void mark_rodata_ro(void) + set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); + #endif + } ++EXPORT_SYMBOL_GPL(mark_rodata_ro); ++ ++void mark_rodata_rw(void) ++{ ++ unsigned long start = PFN_ALIGN(_text); ++ unsigned long size = PFN_ALIGN(_etext) - start; ++ ++ start += size; ++ size = (unsigned long)__end_rodata - start; ++ set_pages_rw_force(virt_to_page(start), size >> PAGE_SHIFT); ++ printk(KERN_INFO "Write enabling the kernel read-only data: %luk\n", ++ size >> 10); ++} ++EXPORT_SYMBOL_GPL(mark_rodata_rw); + #endif + + int __init reserve_bootmem_generic(unsigned long phys, unsigned long len, +--- a/arch/x86/mm/init_64.c ++++ b/arch/x86/mm/init_64.c +@@ -755,6 +755,7 @@ void set_kernel_text_ro(void) + set_memory_ro(start, (end - start) >> PAGE_SHIFT); + } + ++static int initmem_freed __read_mostly = 0; + void mark_rodata_ro(void) + { + unsigned long start = PFN_ALIGN(_text); +@@ -787,15 +788,33 @@ void mark_rodata_ro(void) + set_memory_ro(start, (end-start) >> PAGE_SHIFT); + #endif + +- free_init_pages("unused kernel memory", +- (unsigned long) page_address(virt_to_page(text_end)), +- (unsigned long) ++ if (!initmem_freed) { ++ initmem_freed = 1; ++ free_init_pages("unused kernel memory", ++ (unsigned long) ++ page_address(virt_to_page(text_end)), ++ (unsigned long) + page_address(virt_to_page(rodata_start))); +- free_init_pages("unused kernel memory", +- (unsigned long) page_address(virt_to_page(rodata_end)), +- (unsigned long) page_address(virt_to_page(data_start))); ++ free_init_pages("unused kernel memory", ++ (unsigned long) ++ page_address(virt_to_page(rodata_end)), ++ (unsigned long) ++ page_address(virt_to_page(data_start))); ++ } + } ++EXPORT_SYMBOL_GPL(mark_rodata_ro); + ++void mark_rodata_rw(void) ++{ ++ unsigned long rodata_start = ++ ((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK; ++ unsigned long end = (unsigned long) &__end_rodata_hpage_align; ++ ++ printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", ++ (end - rodata_start) >> 10); ++ set_memory_rw_force(rodata_start, (end - rodata_start) >> PAGE_SHIFT); ++} ++EXPORT_SYMBOL_GPL(mark_rodata_rw); + #endif + + int __init reserve_bootmem_generic(unsigned long phys, unsigned long len, +--- a/arch/x86/mm/pageattr.c ++++ b/arch/x86/mm/pageattr.c +@@ -245,6 +245,8 @@ static void cpa_flush_array(unsigned lon + } + } + ++static int static_protections_allow_rodata __read_mostly; ++ + /* + * Certain areas of memory on x86 require very specific protection flags, + * for example the BIOS area or kernel text. Callers don't always get this +@@ -276,8 +278,10 @@ static inline pgprot_t static_protection + * catches all aliases. + */ + if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT, +- __pa((unsigned long)__end_rodata) >> PAGE_SHIFT)) +- pgprot_val(forbidden) |= _PAGE_RW; ++ __pa((unsigned long)__end_rodata) >> PAGE_SHIFT)) { ++ if (!static_protections_allow_rodata) ++ pgprot_val(forbidden) |= _PAGE_RW; ++ } + + #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) + /* +@@ -1134,6 +1138,21 @@ int set_memory_rw(unsigned long addr, in + } + EXPORT_SYMBOL_GPL(set_memory_rw); + ++/* hack: bypass kernel rodata section static_protections check. */ ++int set_memory_rw_force(unsigned long addr, int numpages) ++{ ++ static DEFINE_MUTEX(lock); ++ int ret; ++ ++ mutex_lock(&lock); ++ static_protections_allow_rodata = 1; ++ ret = change_page_attr_set(&addr, numpages, __pgprot(_PAGE_RW), 0); ++ static_protections_allow_rodata = 0; ++ mutex_unlock(&lock); ++ ++ return ret; ++} ++ + int set_memory_np(unsigned long addr, int numpages) + { + return change_page_attr_clear(&addr, numpages, __pgprot(_PAGE_PRESENT), 0); +@@ -1248,6 +1267,13 @@ int set_pages_rw(struct page *page, int + return set_memory_rw(addr, numpages); + } + ++int set_pages_rw_force(struct page *page, int numpages) ++{ ++ unsigned long addr = (unsigned long)page_address(page); ++ ++ return set_memory_rw_force(addr, numpages); ++} ++ + #ifdef CONFIG_DEBUG_PAGEALLOC + + static int __set_pages_p(struct page *page, int numpages) diff --git a/patches.suse/xfs-dmapi-2-6-34-api-changes b/patches.suse/xfs-dmapi-2-6-34-api-changes new file mode 100644 index 0000000..4f30ca8 --- /dev/null +++ b/patches.suse/xfs-dmapi-2-6-34-api-changes @@ -0,0 +1,91 @@ +From: Jeff Mahoney +Subject: xfs-dmapi: 2.6.34 API changes +Patch-mainline: Never + + 2.6.34-rc1 changed some XFS APIs. This patch updates them. + +Signed-off-by: Jeff Mahoney +Acked-by: Jeff Mahoney +--- + fs/xfs/dmapi/xfs_dm.c | 4 ++-- + fs/xfs/linux-2.6/xfs_file.c | 23 +++++++++++++++-------- + fs/xfs/linux-2.6/xfs_iops.h | 3 +++ + 3 files changed, 20 insertions(+), 10 deletions(-) + +--- a/fs/xfs/dmapi/xfs_dm.c ++++ b/fs/xfs/dmapi/xfs_dm.c +@@ -1956,7 +1956,7 @@ xfs_dm_get_dmattr( + alloc_size = XFS_BUG_KLUDGE; + if (alloc_size > ATTR_MAX_VALUELEN) + alloc_size = ATTR_MAX_VALUELEN; +- value = kmem_alloc(alloc_size, KM_SLEEP | KM_LARGE); ++ value = kmem_zalloc_large(alloc_size); + + /* Get the attribute's value. */ + +@@ -2877,7 +2877,7 @@ xfs_dm_sync_by_handle( + /* We need to protect against concurrent writers.. */ + ret = filemap_fdatawrite(inode->i_mapping); + down_rw_sems(inode, DM_FLAGS_IMUX); +- err = -xfs_fsync(ip); ++ err = xfs_fsync(inode, 1); + if (!ret) + ret = err; + up_rw_sems(inode, DM_FLAGS_IMUX); +--- a/fs/xfs/linux-2.6/xfs_file.c ++++ b/fs/xfs/linux-2.6/xfs_file.c +@@ -100,13 +100,10 @@ xfs_iozero( + return (-status); + } + +-STATIC int +-xfs_file_fsync( +- struct file *file, +- struct dentry *dentry, +- int datasync) ++int ++xfs_fsync(struct inode *inode, int datasync) + { +- struct xfs_inode *ip = XFS_I(dentry->d_inode); ++ struct xfs_inode *ip = XFS_I(inode); + struct xfs_trans *tp; + int error = 0; + int log_flushed = 0; +@@ -141,8 +138,8 @@ xfs_file_fsync( + * might gets cleared when the inode gets written out via the AIL + * or xfs_iflush_cluster. + */ +- if (((dentry->d_inode->i_state & I_DIRTY_DATASYNC) || +- ((dentry->d_inode->i_state & I_DIRTY_SYNC) && !datasync)) && ++ if (((inode->i_state & I_DIRTY_DATASYNC) || ++ ((inode->i_state & I_DIRTY_SYNC) && !datasync)) && + ip->i_update_core) { + /* + * Kick off a transaction to log the inode core to get the +@@ -210,6 +207,16 @@ xfs_file_fsync( + return -error; + } + ++STATIC int ++xfs_file_fsync( ++ struct file *file, ++ struct dentry *dentry, ++ int datasync) ++{ ++ return xfs_fsync(dentry->d_inode, datasync); ++} ++ ++ + STATIC ssize_t + xfs_file_aio_read( + struct kiocb *iocb, +--- a/fs/xfs/linux-2.6/xfs_iops.h ++++ b/fs/xfs/linux-2.6/xfs_iops.h +@@ -27,4 +27,7 @@ extern ssize_t xfs_vn_listxattr(struct d + + extern void xfs_setup_inode(struct xfs_inode *); + ++extern int xfs_fsync(struct inode *, int); ++ + #endif /* __XFS_IOPS_H__ */ ++ diff --git a/patches.suse/xfs-dmapi-enable b/patches.suse/xfs-dmapi-enable new file mode 100644 index 0000000..c4bed85 --- /dev/null +++ b/patches.suse/xfs-dmapi-enable @@ -0,0 +1,128 @@ +Date: Thu, 09 Oct 2008 17:11:14 +1100 +From: Donald Douwsma +Subject: VFS changes to support DMAPI +Patch-mainline: not yet +References: bnc#450658 + +VFS changes to support DMAPI including open_exec(), mprotect() +and build infastructure. + +Acked-by: Jan Kara + +--- + MAINTAINERS | 7 +++++++ + fs/Kconfig | 19 +++++++++++++++++++ + fs/Makefile | 2 ++ + fs/exec.c | 6 ++++++ + include/linux/fs.h | 2 ++ + include/linux/mm.h | 3 +++ + mm/mprotect.c | 5 +++++ + 7 files changed, 44 insertions(+) + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -6228,6 +6228,13 @@ S: Supported + F: Documentation/filesystems/xfs.txt + F: fs/xfs/ + ++DMAPI ++P: Silicon Graphics Inc ++M: xfs-masters@oss.sgi.com ++L: xfs@oss.sgi.com ++W: http://oss.sgi.com/projects/xfs ++S: Supported ++ + XILINX SYSTEMACE DRIVER + M: Grant Likely + W: http://www.secretlab.ca/ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -57,6 +57,25 @@ config FILE_LOCKING + + source "fs/notify/Kconfig" + ++config DMAPI ++ tristate "DMAPI support" ++ help ++ The Data Management API is a system interface used to implement ++ the interface defined in the X/Open document: ++ "Systems Management: Data Storage Management (XDSM) API", ++ dated February 1997. This interface is used by hierarchical ++ storage management systems. ++ ++ If any DMAPI-capable filesystem is built into the kernel, then ++ DMAPI must also be built into the kernel. ++ ++config DMAPI_DEBUG ++ bool "DMAPI debugging support" ++ depends on DMAPI ++ help ++ If you don't know whether you need it, then you don't need it: ++ answer N. ++ + source "fs/quota/Kconfig" + + source "fs/autofs/Kconfig" +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -53,6 +53,8 @@ obj-$(CONFIG_GENERIC_ACL) += generic_acl + + obj-y += quota/ + ++obj-$(CONFIG_DMAPI) += dmapi/ ++ + obj-$(CONFIG_PROC_FS) += proc/ + obj-y += partitions/ + obj-$(CONFIG_SYSFS) += sysfs/ +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -680,6 +680,12 @@ struct file *open_exec(const char *name) + + fsnotify_open(file->f_path.dentry); + ++ if (file->f_op && file->f_op->open_exec) { ++ err = file->f_op->open_exec(file->f_path.dentry->d_inode); ++ if (err) ++ goto exit; ++ } ++ + err = deny_write_access(file); + if (err) + goto exit; +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1508,6 +1508,8 @@ struct file_operations { + int (*flock) (struct file *, int, struct file_lock *); + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); + ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); ++#define HAVE_FOP_OPEN_EXEC ++ int (*open_exec) (struct inode *); + int (*setlease)(struct file *, long, struct file_lock **); + }; + +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -187,6 +187,9 @@ struct vm_operations_struct { + void (*close)(struct vm_area_struct * area); + int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf); + ++#define HAVE_VMOP_MPROTECT ++ int (*mprotect)(struct vm_area_struct * area, unsigned int newflags); ++ + /* notification that a previously read-only page is about to become + * writable, if an error is returned it will cause a SIGBUS */ + int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf); +--- a/mm/mprotect.c ++++ b/mm/mprotect.c +@@ -294,6 +294,11 @@ SYSCALL_DEFINE3(mprotect, unsigned long, + if (error) + goto out; + ++ if (vma->vm_ops && vma->vm_ops->mprotect) { ++ error = vma->vm_ops->mprotect(vma, newflags); ++ if (error < 0) ++ goto out; ++ } + tmp = vma->vm_end; + if (tmp > end) + tmp = end; diff --git a/patches.suse/xfs-dmapi-fix-incompatible-pointer-type-warning b/patches.suse/xfs-dmapi-fix-incompatible-pointer-type-warning new file mode 100644 index 0000000..7435671 --- /dev/null +++ b/patches.suse/xfs-dmapi-fix-incompatible-pointer-type-warning @@ -0,0 +1,30 @@ +From: Jeff Mahoney +Subject: xfs/dmapi: fix incompatible pointer type warning +Patch-mainline: Whenever dmapi gets upstream + + This fixes an incompatible pointer initialization warning. + +Signed-off-by: Jeff Mahoney +--- + fs/xfs/dmapi/xfs_dm.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/fs/xfs/dmapi/xfs_dm.c ++++ b/fs/xfs/dmapi/xfs_dm.c +@@ -3152,12 +3152,12 @@ STATIC int + xfs_dm_send_namesp_event( + dm_eventtype_t event, + struct xfs_mount *mp, +- xfs_inode_t *ip1, ++ struct xfs_inode *ip1, + dm_right_t vp1_right, +- xfs_inode_t *ip2, ++ struct xfs_inode *ip2, + dm_right_t vp2_right, +- const char *name1, +- const char *name2, ++ const unsigned char *name1, ++ const unsigned char *name2, + mode_t mode, + int retcode, + int flags) diff --git a/patches.suse/xfs-dmapi-re-add-flags-for-xfs_free_eofblocks b/patches.suse/xfs-dmapi-re-add-flags-for-xfs_free_eofblocks new file mode 100644 index 0000000..8938711 --- /dev/null +++ b/patches.suse/xfs-dmapi-re-add-flags-for-xfs_free_eofblocks @@ -0,0 +1,74 @@ +From: Jeff Mahoney +Subject: xfs/dmapi: Re-add flags for xfs_free_eofblocks +Patch-mainline: Depends on dmapi being upstream + + 2.6.33 removed the NOLOCK flag from xfs_free_eofblocks. xfs_dm_punch_hole + needs it because it already holds the iolock for the vnode it's operating + on. This patch restores the flag to avoid a pretty obvious deadlock in + dmapi. + +Signed-off-by: Jeff Mahoney +--- + fs/xfs/dmapi/xfs_dm.c | 2 +- + fs/xfs/xfs_rw.h | 6 ++++++ + fs/xfs/xfs_vnodeops.c | 10 +++------- + 3 files changed, 10 insertions(+), 8 deletions(-) + +--- a/fs/xfs/dmapi/xfs_dm.c ++++ b/fs/xfs/dmapi/xfs_dm.c +@@ -2481,7 +2481,7 @@ xfs_dm_punch_hole( + * leaving them around if we are migrating the file.... + */ + if (!error && (len == 0)) { +- error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_NOLOCK); ++ error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_HASLOCK); + } + + /* +--- a/fs/xfs/xfs_rw.h ++++ b/fs/xfs/xfs_rw.h +@@ -48,6 +48,12 @@ extern xfs_extlen_t xfs_get_extsz_hint(s + /* + * Prototypes for functions in xfs_vnodeops.c. + */ ++ ++/* ++ * Flags for xfs_free_eofblocks ++ */ ++#define XFS_FREE_EOF_TRYLOCK (1<<0) ++#define XFS_FREE_EOF_HASLOCK (1<<1) + extern int xfs_free_eofblocks(struct xfs_mount *mp, struct xfs_inode *ip, + int flags); + +--- a/fs/xfs/xfs_vnodeops.c ++++ b/fs/xfs/xfs_vnodeops.c +@@ -584,11 +584,6 @@ xfs_readlink( + } + + /* +- * Flags for xfs_free_eofblocks +- */ +-#define XFS_FREE_EOF_TRYLOCK (1<<0) +- +-/* + * This is called by xfs_inactive to free any blocks beyond eof + * when the link count isn't zero and by xfs_dm_punch_hole() when + * punching a hole to EOF. +@@ -652,14 +647,15 @@ xfs_free_eofblocks( + xfs_trans_cancel(tp, 0); + return 0; + } +- } else { ++ } else if (!(flags & XFS_FREE_EOF_HASLOCK)){ + xfs_ilock(ip, XFS_IOLOCK_EXCL); + } + error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, + ip->i_size); + if (error) { + xfs_trans_cancel(tp, 0); +- xfs_iunlock(ip, XFS_IOLOCK_EXCL); ++ if (!(flags & XFS_FREE_EOF_HASLOCK)) ++ xfs_iunlock(ip, XFS_IOLOCK_EXCL); + return error; + } + diff --git a/patches.suse/xfs-dmapi-src b/patches.suse/xfs-dmapi-src new file mode 100644 index 0000000..75fb4a9 --- /dev/null +++ b/patches.suse/xfs-dmapi-src @@ -0,0 +1,10791 @@ +Date: Thu, 09 Oct 2008 17:11:31 +1100 +From: Donald Douwsma +Subject: DMAPI Source +Patch-mainline: ? +References: bnc#450658 + +Acked-by: Jan Kara + +--- + fs/dmapi/Makefile | 53 + + fs/dmapi/Status | 128 +++ + fs/dmapi/dmapi.h | 1086 ++++++++++++++++++++++++++ + fs/dmapi/dmapi_attr.c | 93 ++ + fs/dmapi/dmapi_bulkattr.c | 170 ++++ + fs/dmapi/dmapi_config.c | 117 ++ + fs/dmapi/dmapi_dmattr.c | 228 +++++ + fs/dmapi/dmapi_event.c | 860 +++++++++++++++++++++ + fs/dmapi/dmapi_handle.c | 119 ++ + fs/dmapi/dmapi_hole.c | 119 ++ + fs/dmapi/dmapi_io.c | 142 +++ + fs/dmapi/dmapi_kern.h | 598 ++++++++++++++ + fs/dmapi/dmapi_mountinfo.c | 527 +++++++++++++ + fs/dmapi/dmapi_port.h | 138 +++ + fs/dmapi/dmapi_private.h | 619 +++++++++++++++ + fs/dmapi/dmapi_region.c | 91 ++ + fs/dmapi/dmapi_register.c | 1638 ++++++++++++++++++++++++++++++++++++++++ + fs/dmapi/dmapi_right.c | 1256 ++++++++++++++++++++++++++++++ + fs/dmapi/dmapi_session.c | 1824 +++++++++++++++++++++++++++++++++++++++++++++ + fs/dmapi/dmapi_sysent.c | 801 +++++++++++++++++++ + fs/dmapi/sv.h | 89 ++ + 21 files changed, 10696 insertions(+) + +--- /dev/null ++++ b/fs/dmapi/Makefile +@@ -0,0 +1,53 @@ ++# ++# Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of version 2 of the GNU General Public License as ++# published by the Free Software Foundation. ++# ++# This program is distributed in the hope that it would be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++# ++# Further, this software is distributed without any warranty that it is ++# free of the rightful claim of any third person regarding infringement ++# or the like. Any license provided herein, whether implied or ++# otherwise, applies only to this software file. Patent licenses, if ++# any, provided herein do not apply to combinations of this program with ++# other software, or any other product whatsoever. ++# ++# You should have received a copy of the GNU General Public License along ++# with this program; if not, write the Free Software Foundation, Inc., 59 ++# Temple Place - Suite 330, Boston MA 02111-1307, USA. ++# ++# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++# Mountain View, CA 94043, or: ++# ++# http://www.sgi.com ++# ++# For further information regarding this notice, see: ++# ++# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++# ++ ++ifeq ($(CONFIG_DMAPI_DEBUG),y) ++ EXTRA_CFLAGS += -DDEBUG ++ EXTRA_CFLAGS += -g ++endif ++ ++obj-$(CONFIG_DMAPI) += dmapi.o ++ ++dmapi-y += dmapi_sysent.o \ ++ dmapi_attr.o \ ++ dmapi_config.o \ ++ dmapi_bulkattr.o \ ++ dmapi_dmattr.o \ ++ dmapi_event.o \ ++ dmapi_handle.o \ ++ dmapi_hole.o \ ++ dmapi_io.o \ ++ dmapi_mountinfo.o \ ++ dmapi_region.o \ ++ dmapi_register.o \ ++ dmapi_right.o \ ++ dmapi_session.o +--- /dev/null ++++ b/fs/dmapi/Status +@@ -0,0 +1,128 @@ ++Jan21,04 - dm_get_bulkall is now implemented. roehrich ++ ++for linux: ++ ++ ++68 external interfaces in libdm ++ ++ 56 of those interfaces go through to dmi(), the kernel side of DMAPI ++ ++ ++ ++Functions known to work ++---------------------------------------------- ++ ++dm_create_session ++dm_create_userevent ++dm_destroy_session ++dm_getall_sessions ++dm_getall_tokens ++dm_get_allocinfo ++dm_get_bulkall ++dm_get_bulkattr ++dm_get_config_events ++dm_get_dmattr ++dm_get_eventlist ++dm_get_events ++dm_get_fileattr ++dm_get_region ++dm_handle_free ++dm_init_attrloc ++dm_init_service ++dm_obj_ref_hold ++dm_obj_ref_query ++dm_obj_ref_rele ++dm_path_to_fshandle ++dm_path_to_handle ++dm_punch_hole ++dm_query_session ++dm_read_invis ++dm_remove_dmattr ++dm_respond_event ++dm_send_msg ++dm_set_disp ++dm_set_dmattr ++dm_set_eventlist ++dm_set_fileattr ++dm_set_region ++dm_sync_by_handle ++dm_write_invis ++35 ++ ++Functions that seem to work (would like more rigorous test case) ++------------------------------------------ ++ ++dm_pending ++dm_probe_hole - one test case of test_hole.c fails ++dm_request_right ++3 ++ ++Functions untested but probably work ++---------------------------------------------- ++ ++dm_find_eventmsg ++dm_handle_cmp ++dm_handle_to_fshandle ++dm_handle_to_ino ++dm_release_right ++5 ++ ++Functions that do not work ++----------------------------------------- ++ ++dm_get_dioinfo - directio not implemented ++1 ++ ++Functions not supported in SGI DMAPI ++------------------------------------------------------------- ++ ++dm_clear_inherit ++dm_create_by_handle ++dm_getall_inherit ++dm_mkdir_by_handle ++dm_set_inherit ++dm_symlink_by_handle ++ ++ ++ ++ ++Functions that seem to work (would like more rigorous test case) ++---------------------------------------------------------------- ++ ++dm_get_config ++dm_downgrade_right ++dm_get_mountinfo ++dm_set_return_on_destory ++dm_upgrade_right ++ ++ ++ ++Functions that do not work ++----------------------------------------------------------------- ++ ++dm_fd_to_handle - Irix getf not implemented on linux ++dm_get_dirattrs - null pointer reference ++dm_handle_to_path ++dm_getall_dmattr - needs a copy_from_user in place of useracc ++ ++ ++Functions that are untested, but probably work ++----------------------------------------------------------------- ++ ++dm_getall_disp ++dm_handle_hash ++dm_handle_is_valid ++dm_handle_to_fsid ++dm_handle_to_igen ++dm_make_fshandle ++dm_make_handle ++dm_move_event ++dm_query_right ++ ++ ++ ++Other things not working ++---------------------------------- ++ ++- read/write events for memory-mapped I/O? ++ +--- /dev/null ++++ b/fs/dmapi/dmapi.h +@@ -0,0 +1,1086 @@ ++/* ++ * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2.1 of the GNU Lesser General Public License ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this program; if not, write the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, ++ * USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++ ++#ifndef __DMAPI_H__ ++#define __DMAPI_H__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifndef __KERNEL__ ++#include ++#endif ++#include ++ ++#ifndef __user ++#define __user ++#endif ++ ++/************************************************************************** ++ * * ++ * The SGI implementation of DMAPI is based upon the X/Open document * ++ * Systems Management: Data Storage Managment (XDSM) API * ++ * dated February 1997. Not all DMAPI functions and structure fields * ++ * have been implemented. Most importantly, the DMAPI functions * ++ * dm_request_right, dm_release_right, dm_query_right, dm_upgrade_right * ++ * and dm_downgrade_right do not work as described in the specification. * ++ * * ++ * The XFS filesystem currently does not allow its locking mechanisms to * ++ * be externally accessed from user space. While the above-mentioned * ++ * dm_xxx_right functions exist and can be called by applications, they * ++ * always return successfully without actually obtaining any locks * ++ * within the filesystem. * ++ * * ++ * Applications which do not need full rights support and which only * ++ * make dm_xxx_right calls in order to satisfy the input requirements of * ++ * other DMAPI calls should be able to use these routines to avoid * ++ * having to implement special-case code for SGI platforms. Applications * ++ * which truely need the capabilities of a full implementation of rights * ++ * will unfortunately have to come up with alternate software solutions * ++ * until such time as rights can be completely implemented. * ++ * * ++ * Functions and structure fields defined within this file which are not * ++ * supported in the SGI implementation of DMAPI are indicated by comments * ++ * following their definitions such as "not supported", or "not * ++ * completely supported". Any function or field not so marked may be * ++ * assumed to work exactly according to the spec. * ++ * * ++ **************************************************************************/ ++ ++ ++ ++/* The first portion of this file contains defines and typedefs that are ++ DMAPI implementation-dependent, and could be different on other platforms. ++*/ ++ ++typedef __s64 dm_attrloc_t; ++typedef unsigned int dm_boolean_t; ++typedef __u64 dm_eventset_t; ++typedef __u64 dm_fsid_t; ++typedef __u64 dm_ino_t; ++typedef __u32 dm_igen_t; ++typedef __s64 dm_off_t; ++typedef unsigned int dm_sequence_t; ++typedef int dm_sessid_t; ++typedef __u64 dm_size_t; ++typedef __s64 dm_ssize_t; ++typedef int dm_token_t; ++ ++/* XXX dev_t, mode_t, and nlink_t are not the same size in kernel space ++ and user space. This affects the field offsets for dm_stat_t. ++ The following solution is temporary. ++ ++ user space sizes: dev_t=8 mode_t=4 nlink_t=4 ++ kernel space : dev_t=2 mode_t=2 nlink_t=2 ++ ++*/ ++typedef __s64 dm_dev_t; ++typedef int dm_mode_t; ++typedef int dm_nlink_t; ++ ++ ++#define DM_REGION_NOEVENT 0x0 ++#define DM_REGION_READ 0x1 ++#define DM_REGION_WRITE 0x2 ++#define DM_REGION_TRUNCATE 0x4 ++ ++/* Values for the mask argument used with dm_get_fileattr, dm_get_bulkattr, ++ dm_get_dirattrs, and dm_set_fileattr. ++*/ ++ ++#define DM_AT_MODE 0x0001 ++#define DM_AT_UID 0x0002 ++#define DM_AT_GID 0x0004 ++#define DM_AT_ATIME 0x0008 ++#define DM_AT_MTIME 0x0010 ++#define DM_AT_CTIME 0x0020 ++#define DM_AT_SIZE 0x0040 ++#define DM_AT_DTIME 0x0080 ++#define DM_AT_HANDLE 0x0100 ++#define DM_AT_EMASK 0x0200 ++#define DM_AT_PMANR 0x0400 ++#define DM_AT_PATTR 0x0800 ++#define DM_AT_STAT 0x1000 ++#define DM_AT_CFLAG 0x2000 ++ ++#define DM_EV_WAIT 0x1 /* used in dm_get_events() */ ++ ++#define DM_MOUNT_RDONLY 0x1 /* me_mode field in dm_mount_event_t */ ++ ++#define DM_RR_WAIT 0x1 ++ ++#define DM_UNMOUNT_FORCE 0x1 /* ne_mode field in dm_namesp_event_t */ ++ ++#define DM_WRITE_SYNC 0x1 /* used in dm_write_invis() */ ++ ++#define DM_SESSION_INFO_LEN 256 ++#define DM_NO_SESSION 0 ++#define DM_TRUE 1 ++#define DM_FALSE 0 ++#define DM_INVALID_TOKEN 0 ++#define DM_NO_TOKEN (-1) ++#define DM_INVALID_HANP NULL ++#define DM_INVALID_HLEN 0 ++#define DM_GLOBAL_HANP ((void *)(1LL)) ++#define DM_GLOBAL_HLEN ((size_t)(1)) ++#define DM_VER_STR_CONTENTS "SGI DMAPI (XDSM) API, Release 1.1." ++ ++ ++#define DMEV_SET(event_type, event_list) \ ++ ((event_list) |= (1 << (event_type))) ++#define DMEV_CLR(event_type, event_list) \ ++ ((event_list) &= ~(1 << (event_type))) ++#define DMEV_ISSET(event_type, event_list) \ ++ (int)(((event_list) & (1 << (event_type))) != 0) ++#define DMEV_ZERO(event_list) \ ++ (event_list) = 0 ++ ++ ++typedef struct { ++ int vd_offset; /* offset from start of containing struct */ ++ unsigned int vd_length; /* length of data starting at vd_offset */ ++} dm_vardata_t; ++ ++#define DM_GET_VALUE(p, field, type) \ ++ ((type) ((char *)(p) + (p)->field.vd_offset)) ++ ++#define DM_GET_LEN(p, field) \ ++ ((p)->field.vd_length) ++ ++#define DM_STEP_TO_NEXT(p, type) \ ++ ((type) ((p)->_link ? (char *)(p) + (p)->_link : NULL)) ++ ++ ++ ++ ++/* The remainder of this include file contains defines, typedefs, and ++ structures which are strictly defined by the DMAPI 2.3 specification. ++ ++ (The _link field which appears in several structures is an ++ implementation-specific way to implement DM_STEP_TO_NEXT, and ++ should not be referenced directly by application code.) ++*/ ++ ++ ++#define DM_ATTR_NAME_SIZE 8 ++ ++ ++struct dm_attrname { ++ unsigned char an_chars[DM_ATTR_NAME_SIZE]; ++}; ++typedef struct dm_attrname dm_attrname_t; ++ ++ ++struct dm_attrlist { ++ int _link; ++ dm_attrname_t al_name; ++ dm_vardata_t al_data; ++}; ++typedef struct dm_attrlist dm_attrlist_t; ++ ++ ++typedef enum { ++ DM_CONFIG_INVALID, ++ DM_CONFIG_BULKALL, ++ DM_CONFIG_CREATE_BY_HANDLE, ++ DM_CONFIG_DTIME_OVERLOAD, ++ DM_CONFIG_LEGACY, ++ DM_CONFIG_LOCK_UPGRADE, ++ DM_CONFIG_MAX_ATTR_ON_DESTROY, ++ DM_CONFIG_MAX_ATTRIBUTE_SIZE, ++ DM_CONFIG_MAX_HANDLE_SIZE, ++ DM_CONFIG_MAX_MANAGED_REGIONS, ++ DM_CONFIG_MAX_MESSAGE_DATA, ++ DM_CONFIG_OBJ_REF, ++ DM_CONFIG_PENDING, ++ DM_CONFIG_PERS_ATTRIBUTES, ++ DM_CONFIG_PERS_EVENTS, ++ DM_CONFIG_PERS_INHERIT_ATTRIBS, ++ DM_CONFIG_PERS_MANAGED_REGIONS, ++ DM_CONFIG_PUNCH_HOLE, ++ DM_CONFIG_TOTAL_ATTRIBUTE_SPACE, ++ DM_CONFIG_WILL_RETRY ++} dm_config_t; ++ ++ ++struct dm_dioinfo { /* non-standard SGI addition */ ++ unsigned int d_mem; ++ unsigned int d_miniosz; ++ unsigned int d_maxiosz; ++ dm_boolean_t d_dio_only; ++}; ++typedef struct dm_dioinfo dm_dioinfo_t; ++ ++ ++struct dm_dispinfo { ++ int _link; ++ unsigned int di_pad1; /* reserved; do not reference */ ++ dm_vardata_t di_fshandle; ++ dm_eventset_t di_eventset; ++}; ++typedef struct dm_dispinfo dm_dispinfo_t; ++ ++ ++#ifndef HAVE_DM_EVENTTYPE_T ++#define HAVE_DM_EVENTTYPE_T ++typedef enum { ++ DM_EVENT_INVALID = -1, ++ DM_EVENT_CANCEL = 0, /* not supported */ ++ DM_EVENT_MOUNT = 1, ++ DM_EVENT_PREUNMOUNT = 2, ++ DM_EVENT_UNMOUNT = 3, ++ DM_EVENT_DEBUT = 4, /* not supported */ ++ DM_EVENT_CREATE = 5, ++ DM_EVENT_CLOSE = 6, /* not supported */ ++ DM_EVENT_POSTCREATE = 7, ++ DM_EVENT_REMOVE = 8, ++ DM_EVENT_POSTREMOVE = 9, ++ DM_EVENT_RENAME = 10, ++ DM_EVENT_POSTRENAME = 11, ++ DM_EVENT_LINK = 12, ++ DM_EVENT_POSTLINK = 13, ++ DM_EVENT_SYMLINK = 14, ++ DM_EVENT_POSTSYMLINK = 15, ++ DM_EVENT_READ = 16, ++ DM_EVENT_WRITE = 17, ++ DM_EVENT_TRUNCATE = 18, ++ DM_EVENT_ATTRIBUTE = 19, ++ DM_EVENT_DESTROY = 20, ++ DM_EVENT_NOSPACE = 21, ++ DM_EVENT_USER = 22, ++ DM_EVENT_MAX = 23 ++} dm_eventtype_t; ++#endif ++ ++ ++struct dm_eventmsg { ++ int _link; ++ dm_eventtype_t ev_type; ++ dm_token_t ev_token; ++ dm_sequence_t ev_sequence; ++ dm_vardata_t ev_data; ++}; ++typedef struct dm_eventmsg dm_eventmsg_t; ++ ++ ++struct dm_cancel_event { /* not supported */ ++ dm_sequence_t ce_sequence; ++ dm_token_t ce_token; ++}; ++typedef struct dm_cancel_event dm_cancel_event_t; ++ ++ ++struct dm_data_event { ++ dm_vardata_t de_handle; ++ dm_off_t de_offset; ++ dm_size_t de_length; ++}; ++typedef struct dm_data_event dm_data_event_t; ++ ++struct dm_destroy_event { ++ dm_vardata_t ds_handle; ++ dm_attrname_t ds_attrname; ++ dm_vardata_t ds_attrcopy; ++}; ++typedef struct dm_destroy_event dm_destroy_event_t; ++ ++struct dm_mount_event { ++ dm_mode_t me_mode; ++ dm_vardata_t me_handle1; ++ dm_vardata_t me_handle2; ++ dm_vardata_t me_name1; ++ dm_vardata_t me_name2; ++ dm_vardata_t me_roothandle; ++}; ++typedef struct dm_mount_event dm_mount_event_t; ++ ++struct dm_namesp_event { ++ dm_mode_t ne_mode; ++ dm_vardata_t ne_handle1; ++ dm_vardata_t ne_handle2; ++ dm_vardata_t ne_name1; ++ dm_vardata_t ne_name2; ++ int ne_retcode; ++}; ++typedef struct dm_namesp_event dm_namesp_event_t; ++ ++ ++typedef enum { ++ DM_EXTENT_INVALID, ++ DM_EXTENT_RES, ++ DM_EXTENT_HOLE ++} dm_extenttype_t; ++ ++ ++struct dm_extent { ++ dm_extenttype_t ex_type; ++ unsigned int ex_pad1; /* reserved; do not reference */ ++ dm_off_t ex_offset; ++ dm_size_t ex_length; ++}; ++typedef struct dm_extent dm_extent_t; ++ ++struct dm_fileattr { ++ dm_mode_t fa_mode; ++ uid_t fa_uid; ++ gid_t fa_gid; ++ time_t fa_atime; ++ time_t fa_mtime; ++ time_t fa_ctime; ++ time_t fa_dtime; ++ unsigned int fa_pad1; /* reserved; do not reference */ ++ dm_off_t fa_size; ++}; ++typedef struct dm_fileattr dm_fileattr_t; ++ ++ ++struct dm_inherit { /* not supported */ ++ dm_attrname_t ih_name; ++ dm_mode_t ih_filetype; ++}; ++typedef struct dm_inherit dm_inherit_t; ++ ++ ++typedef enum { ++ DM_MSGTYPE_INVALID, ++ DM_MSGTYPE_SYNC, ++ DM_MSGTYPE_ASYNC ++} dm_msgtype_t; ++ ++ ++struct dm_region { ++ dm_off_t rg_offset; ++ dm_size_t rg_size; ++ unsigned int rg_flags; ++ unsigned int rg_pad1; /* reserved; do not reference */ ++}; ++typedef struct dm_region dm_region_t; ++ ++ ++typedef enum { ++ DM_RESP_INVALID, ++ DM_RESP_CONTINUE, ++ DM_RESP_ABORT, ++ DM_RESP_DONTCARE ++} dm_response_t; ++ ++ ++#ifndef HAVE_DM_RIGHT_T ++#define HAVE_DM_RIGHT_T ++typedef enum { ++ DM_RIGHT_NULL, ++ DM_RIGHT_SHARED, ++ DM_RIGHT_EXCL ++} dm_right_t; ++#endif ++ ++ ++struct dm_stat { ++ int _link; ++ dm_vardata_t dt_handle; ++ dm_vardata_t dt_compname; ++ int dt_nevents; ++ dm_eventset_t dt_emask; ++ int dt_pers; /* field not supported */ ++ int dt_pmanreg; ++ time_t dt_dtime; ++ unsigned int dt_change; /* field not supported */ ++ unsigned int dt_pad1; /* reserved; do not reference */ ++ dm_dev_t dt_dev; ++ dm_ino_t dt_ino; ++ dm_mode_t dt_mode; ++ dm_nlink_t dt_nlink; ++ uid_t dt_uid; ++ gid_t dt_gid; ++ dm_dev_t dt_rdev; ++ unsigned int dt_pad2; /* reserved; do not reference */ ++ dm_off_t dt_size; ++ time_t dt_atime; ++ time_t dt_mtime; ++ time_t dt_ctime; ++ unsigned int dt_blksize; ++ dm_size_t dt_blocks; ++ ++ /* Non-standard filesystem-specific fields. Currently XFS is the only ++ supported filesystem type. ++ */ ++ ++ __u64 dt_pad3; /* reserved; do not reference */ ++ int dt_fstype; /* filesystem index; see sysfs(2) */ ++ union { ++ struct { ++ dm_igen_t igen; ++ unsigned int xflags; ++ unsigned int extsize; ++ unsigned int extents; ++ unsigned short aextents; ++ unsigned short dmstate; ++ } sgi_xfs; ++ } fsys_dep; ++}; ++typedef struct dm_stat dm_stat_t; ++ ++#define dt_xfs_igen fsys_dep.sgi_xfs.igen ++#define dt_xfs_xflags fsys_dep.sgi_xfs.xflags ++#define dt_xfs_extsize fsys_dep.sgi_xfs.extsize ++#define dt_xfs_extents fsys_dep.sgi_xfs.extents ++#define dt_xfs_aextents fsys_dep.sgi_xfs.aextents ++#define dt_xfs_dmstate fsys_dep.sgi_xfs.dmstate ++ ++/* Flags for the non-standard dt_xfs_xflags field. */ ++ ++#define DM_XFLAG_REALTIME 0x00000001 ++#define DM_XFLAG_PREALLOC 0x00000002 ++#define DM_XFLAG_IMMUTABLE 0x00000008 ++#define DM_XFLAG_APPEND 0x00000010 ++#define DM_XFLAG_SYNC 0x00000020 ++#define DM_XFLAG_NOATIME 0x00000040 ++#define DM_XFLAG_NODUMP 0x00000080 ++#define DM_XFLAG_HASATTR 0x80000000 ++ ++ ++struct dm_timestruct { ++ time_t dm_tv_sec; ++ int dm_tv_nsec; ++}; ++typedef struct dm_timestruct dm_timestruct_t; ++ ++ ++struct dm_xstat { /* not supported */ ++ dm_stat_t dx_statinfo; ++ dm_vardata_t dx_attrdata; ++}; ++typedef struct dm_xstat dm_xstat_t; ++ ++ ++#define MAXDMFSFIDSZ 46 ++ ++struct dm_fid { ++ __u16 dm_fid_len; /* length of remainder */ ++ __u16 dm_fid_pad; ++ __u32 dm_fid_gen; /* generation number */ ++ __u64 dm_fid_ino; /* 64 bits inode number */ ++}; ++typedef struct dm_fid dm_fid_t; ++ ++ ++struct dm_handle { ++ union { ++ __s64 align; /* force alignment of ha_fid */ ++ dm_fsid_t _ha_fsid; /* unique file system identifier */ ++ } ha_u; ++ dm_fid_t ha_fid; /* file system specific file ID */ ++}; ++typedef struct dm_handle dm_handle_t; ++#define ha_fsid ha_u._ha_fsid ++ ++#define DM_HSIZE(handle) (((char *) &(handle).ha_fid.dm_fid_pad \ ++ - (char *) &(handle)) \ ++ + (handle).ha_fid.dm_fid_len) ++ ++#define DM_HANDLE_CMP(h1, h2) memcmp(h1, h2, sizeof(dm_handle_t)) ++ ++#define DM_FSHSIZE sizeof(dm_fsid_t) ++ ++ ++/* The following list provides the prototypes for all functions defined in ++ the DMAPI interface. ++*/ ++ ++extern int ++dm_clear_inherit( /* not supported */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep); ++ ++extern int ++dm_create_by_handle( /* not supported */ ++ dm_sessid_t sid, ++ void __user *dirhanp, ++ size_t dirhlen, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname); ++ ++extern int ++dm_create_session( ++ dm_sessid_t oldsid, ++ char __user *sessinfop, ++ dm_sessid_t __user *newsidp); ++ ++extern int ++dm_create_userevent( ++ dm_sessid_t sid, ++ size_t msglen, ++ void __user *msgdatap, ++ dm_token_t __user *tokenp); ++ ++extern int ++dm_destroy_session( ++ dm_sessid_t sid); ++ ++extern int ++dm_downgrade_right( /* not completely supported; see caveat above */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token); ++ ++extern int ++dm_fd_to_handle( ++ int fd, ++ void **hanpp, ++ size_t *hlenp); ++ ++extern int ++dm_find_eventmsg( ++ dm_sessid_t sid, ++ dm_token_t token, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++extern int ++dm_get_allocinfo( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t *offp, ++ unsigned int nelem, ++ dm_extent_t *extentp, ++ unsigned int *nelemp); ++ ++extern int ++dm_get_bulkall( /* not supported */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int mask, ++ dm_attrname_t *attrnamep, ++ dm_attrloc_t *locp, ++ size_t buflen, ++ void *bufp, ++ size_t *rlenp); ++ ++extern int ++dm_get_bulkattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int mask, ++ dm_attrloc_t *locp, ++ size_t buflen, ++ void *bufp, ++ size_t *rlenp); ++ ++extern int ++dm_get_config( ++ void __user *hanp, ++ size_t hlen, ++ dm_config_t flagname, ++ dm_size_t __user *retvalp); ++ ++extern int ++dm_get_config_events( ++ void __user *hanp, ++ size_t hlen, ++ unsigned int nelem, ++ dm_eventset_t __user *eventsetp, ++ unsigned int __user *nelemp); ++ ++extern int ++dm_get_dirattrs( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int mask, ++ dm_attrloc_t *locp, ++ size_t buflen, ++ void *bufp, ++ size_t *rlenp); ++ ++extern int ++dm_get_dmattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++extern int ++dm_get_eventlist( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int nelem, ++ dm_eventset_t __user *eventsetp, ++ unsigned int __user *nelemp); ++ ++extern int ++dm_get_events( ++ dm_sessid_t sid, ++ unsigned int maxmsgs, ++ unsigned int flags, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++extern int ++dm_get_fileattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int mask, ++ dm_stat_t __user *statp); ++ ++extern int ++dm_get_mountinfo( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++extern int ++dm_get_region( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int nelem, ++ dm_region_t __user *regbufp, ++ unsigned int __user *nelemp); ++ ++extern int ++dm_getall_disp( ++ dm_sessid_t sid, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++extern int ++dm_getall_dmattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++extern int ++dm_getall_inherit( /* not supported */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int nelem, ++ dm_inherit_t __user *inheritbufp, ++ unsigned int __user *nelemp); ++ ++extern int ++dm_getall_sessions( ++ unsigned int nelem, ++ dm_sessid_t __user *sidbufp, ++ unsigned int __user *nelemp); ++ ++extern int ++dm_getall_tokens( ++ dm_sessid_t sid, ++ unsigned int nelem, ++ dm_token_t __user *tokenbufp, ++ unsigned int __user *nelemp); ++ ++extern int ++dm_handle_cmp( ++ void *hanp1, ++ size_t hlen1, ++ void *hanp2, ++ size_t hlen2); ++ ++extern void ++dm_handle_free( ++ void *hanp, ++ size_t hlen); ++ ++extern u_int ++dm_handle_hash( ++ void *hanp, ++ size_t hlen); ++ ++extern dm_boolean_t ++dm_handle_is_valid( ++ void *hanp, ++ size_t hlen); ++ ++extern int ++dm_handle_to_fshandle( ++ void *hanp, ++ size_t hlen, ++ void **fshanpp, ++ size_t *fshlenp); ++ ++extern int ++dm_handle_to_fsid( ++ void *hanp, ++ size_t hlen, ++ dm_fsid_t *fsidp); ++ ++extern int ++dm_handle_to_igen( ++ void *hanp, ++ size_t hlen, ++ dm_igen_t *igenp); ++ ++extern int ++dm_handle_to_ino( ++ void *hanp, ++ size_t hlen, ++ dm_ino_t *inop); ++ ++extern int ++dm_handle_to_path( ++ void *dirhanp, ++ size_t dirhlen, ++ void *targhanp, ++ size_t targhlen, ++ size_t buflen, ++ char *pathbufp, ++ size_t *rlenp); ++ ++extern int ++dm_init_attrloc( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrloc_t __user *locp); ++ ++extern int ++dm_init_service( ++ char **versionstrpp); ++ ++extern int ++dm_make_handle( ++ dm_fsid_t *fsidp, ++ dm_ino_t *inop, ++ dm_igen_t *igenp, ++ void **hanpp, ++ size_t *hlenp); ++ ++extern int ++dm_make_fshandle( ++ dm_fsid_t *fsidp, ++ void **hanpp, ++ size_t *hlenp); ++ ++extern int ++dm_mkdir_by_handle( /* not supported */ ++ dm_sessid_t sid, ++ void __user *dirhanp, ++ size_t dirhlen, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname); ++ ++extern int ++dm_move_event( ++ dm_sessid_t srcsid, ++ dm_token_t token, ++ dm_sessid_t targetsid, ++ dm_token_t __user *rtokenp); ++ ++extern int ++dm_obj_ref_hold( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen); ++ ++extern int ++dm_obj_ref_query( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void *hanp, ++ size_t hlen); ++ ++extern int ++dm_obj_ref_rele( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen); ++ ++extern int ++dm_path_to_fshandle( ++ char *path, ++ void **hanpp, ++ size_t *hlenp); ++ ++extern int ++dm_path_to_handle( ++ char *path, ++ void **hanpp, ++ size_t *hlenp); ++ ++extern int ++dm_pending( ++ dm_sessid_t sid, ++ dm_token_t token, ++ dm_timestruct_t __user *delay); ++ ++extern int ++dm_probe_hole( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t off, ++ dm_size_t len, ++ dm_off_t __user *roffp, ++ dm_size_t __user *rlenp); ++ ++extern int ++dm_punch_hole( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t off, ++ dm_size_t len); ++ ++extern int ++dm_query_right( /* not completely supported; see caveat above */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_right_t __user *rightp); ++ ++extern int ++dm_query_session( ++ dm_sessid_t sid, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++extern dm_ssize_t ++dm_read_invis( ++ dm_sessid_t sid, ++ void *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t off, ++ dm_size_t len, ++ void *bufp); ++ ++extern int ++dm_release_right( /* not completely supported; see caveat above */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token); ++ ++extern int ++dm_remove_dmattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ int setdtime, ++ dm_attrname_t __user *attrnamep); ++ ++extern int ++dm_request_right( /* not completely supported; see caveat above */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int flags, ++ dm_right_t right); ++ ++extern int ++dm_respond_event( ++ dm_sessid_t sid, ++ dm_token_t token, ++ dm_response_t response, ++ int reterror, ++ size_t buflen, ++ void __user *respbufp); ++ ++extern int ++dm_send_msg( ++ dm_sessid_t targetsid, ++ dm_msgtype_t msgtype, ++ size_t buflen, ++ void __user *bufp); ++ ++extern int ++dm_set_disp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_eventset_t __user *eventsetp, ++ unsigned int maxevent); ++ ++extern int ++dm_set_dmattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ int setdtime, ++ size_t buflen, ++ void __user *bufp); ++ ++extern int ++dm_set_eventlist( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_eventset_t __user *eventsetp, ++ unsigned int maxevent); ++ ++extern int ++dm_set_fileattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int mask, ++ dm_fileattr_t __user *attrp); ++ ++extern int ++dm_set_inherit( /* not supported */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ mode_t mode); ++ ++extern int ++dm_set_region( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ unsigned int nelem, ++ dm_region_t __user *regbufp, ++ dm_boolean_t __user *exactflagp); ++ ++extern int ++dm_set_return_on_destroy( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ dm_boolean_t enable); ++ ++extern int ++dm_symlink_by_handle( /* not supported */ ++ dm_sessid_t sid, ++ void __user *dirhanp, ++ size_t dirhlen, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname, ++ char __user *path); ++ ++extern int ++dm_sync_by_handle( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token); ++ ++extern int ++dm_upgrade_right( /* not completely supported; see caveat above */ ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token); ++ ++extern dm_ssize_t ++dm_write_invis( ++ dm_sessid_t sid, ++ void *hanp, ++ size_t hlen, ++ dm_token_t token, ++ int flags, ++ dm_off_t off, ++ dm_size_t len, ++ void *bufp); ++ ++/* Non-standard SGI additions to the DMAPI interface. */ ++ ++int ++dm_open_by_handle( ++ void __user *hanp, ++ size_t hlen, ++ int mode); ++ ++extern int ++dm_get_dioinfo( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_dioinfo_t __user *diop); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __DMAPI_H__ */ +--- /dev/null ++++ b/fs/dmapi/dmapi_attr.c +@@ -0,0 +1,93 @@ ++/* ++ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++ ++/* Retrieve attributes for a single file, directory or symlink. */ ++ ++int ++dm_get_fileattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int mask, ++ dm_stat_t __user *statp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_fileattr(tdp->td_ip, tdp->td_right, ++ mask, statp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++/* Set one or more file attributes of a file, directory, or symlink. */ ++ ++int ++dm_set_fileattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int mask, ++ dm_fileattr_t __user *attrp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->set_fileattr(tdp->td_ip, tdp->td_right, ++ mask, attrp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_bulkattr.c +@@ -0,0 +1,170 @@ ++/* ++ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++ ++int ++dm_init_attrloc( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrloc_t __user *locp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS|DM_TDT_DIR, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->init_attrloc(tdp->td_ip, tdp->td_right, locp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++/* ++ * Retrieves both standard and DM specific file attributes for the file ++ * system indicated by the handle. (The FS has to be mounted). ++ * Syscall returns 1 to indicate SUCCESS and more information is available. ++ * -1 is returned on error, and errno will be set appropriately. ++ * 0 is returned upon successful completion. ++ */ ++ ++int ++dm_get_bulkattr_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int mask, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_bulkattr_rvp(tdp->td_ip, tdp->td_right, ++ mask, locp, buflen, bufp, rlenp, rvp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++/* ++ * Retrieves attributes of directory entries given a handle to that ++ * directory. Iterative. ++ * Syscall returns 1 to indicate SUCCESS and more information is available. ++ * -1 is returned on error, and errno will be set appropriately. ++ * 0 is returned upon successful completion. ++ */ ++ ++int ++dm_get_dirattrs_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int mask, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_DIR, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_dirattrs_rvp(tdp->td_ip, tdp->td_right, ++ mask, locp, buflen, bufp, rlenp, rvp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_get_bulkall_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int mask, ++ dm_attrname_t __user *attrnamep, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_bulkall_rvp(tdp->td_ip, tdp->td_right, ++ mask, attrnamep, locp, buflen, bufp, rlenp, rvp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_config.c +@@ -0,0 +1,117 @@ ++/* ++ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++ ++#include ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++int ++dm_get_config( ++ void __user *hanp, ++ size_t hlen, ++ dm_config_t flagname, ++ dm_size_t __user *retvalp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ dm_size_t retval; ++ int system = 1; ++ int error; ++ ++ /* Trap and process configuration parameters which are system-wide. */ ++ ++ switch (flagname) { ++ case DM_CONFIG_LEGACY: ++ case DM_CONFIG_PENDING: ++ case DM_CONFIG_OBJ_REF: ++ retval = DM_TRUE; ++ break; ++ case DM_CONFIG_MAX_MESSAGE_DATA: ++ retval = DM_MAX_MSG_DATA; ++ break; ++ default: ++ system = 0; ++ break; ++ } ++ if (system) { ++ if (copy_to_user(retvalp, &retval, sizeof(retval))) ++ return(-EFAULT); ++ return(0); ++ } ++ ++ /* Must be filesystem-specific. Convert the handle into an inode. */ ++ ++ if ((error = dm_get_config_tdp(hanp, hlen, &tdp)) != 0) ++ return(error); ++ ++ /* Now call the filesystem-specific routine to determine the ++ value of the configuration option for that filesystem. ++ */ ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_config(tdp->td_ip, tdp->td_right, ++ flagname, retvalp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_get_config_events( ++ void __user *hanp, ++ size_t hlen, ++ u_int nelem, ++ dm_eventset_t __user *eventsetp, ++ u_int __user *nelemp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ /* Convert the handle into an inode. */ ++ ++ if ((error = dm_get_config_tdp(hanp, hlen, &tdp)) != 0) ++ return(error); ++ ++ /* Now call the filesystem-specific routine to determine the ++ events supported by that filesystem. ++ */ ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_config_events(tdp->td_ip, tdp->td_right, ++ nelem, eventsetp, nelemp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_dmattr.c +@@ -0,0 +1,228 @@ ++/* ++ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++ ++int ++dm_clear_inherit( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->clear_inherit(tdp->td_ip, tdp->td_right, ++ attrnamep); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_get_dmattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_dmattr(tdp->td_ip, tdp->td_right, ++ attrnamep, buflen, bufp, rlenp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_getall_dmattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->getall_dmattr(tdp->td_ip, tdp->td_right, ++ buflen, bufp, rlenp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_getall_inherit( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int nelem, ++ dm_inherit_t __user *inheritbufp, ++ u_int __user *nelemp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->getall_inherit(tdp->td_ip, tdp->td_right, ++ nelem, inheritbufp, nelemp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_remove_dmattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ int setdtime, ++ dm_attrname_t __user *attrnamep) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->remove_dmattr(tdp->td_ip, tdp->td_right, ++ setdtime, attrnamep); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_set_dmattr( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ int setdtime, ++ size_t buflen, ++ void __user *bufp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->set_dmattr(tdp->td_ip, tdp->td_right, ++ attrnamep, setdtime, buflen, bufp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_set_inherit( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ mode_t mode) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->set_inherit(tdp->td_ip, tdp->td_right, ++ attrnamep, mode); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_event.c +@@ -0,0 +1,860 @@ ++/* ++ * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#include ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++/* The "rights" portion of the DMAPI spec is not currently implemented. A ++ framework for rights is provided in the code, but turns out to be a noop ++ in practice. The following comments are a brain dump to serve as input to ++ the poor soul that eventually has to get DMAPI rights working in IRIX. ++ ++ A DMAPI right is similar but not identical to the mrlock_t mechanism ++ already used within the kernel. The similarities are that it is a ++ sleeping lock, and that a multiple-reader, single-writer protocol is used. ++ How locks are obtained and dropped are different however. With a mrlock_t, ++ a thread grabs the lock, does some stuff, then drops the lock, and all other ++ threads block in the meantime (assuming a write lock). There is a one-to- ++ one relationship between the lock and the thread which obtained the lock. ++ Not so with DMAPI right locks. A DMAPI lock is associated with a particular ++ session/token/hanp/hlen quad; since there is a dm_tokdata_t structure for ++ each such quad, you can think of it as a one-to-one relationship between the ++ lock and a dm_tokdata_t. Any application thread which presents the correct ++ quad is entitled to grab or release the lock, or to use the rights ++ associated with that lock. The thread that grabs the lock does not have to ++ be the one to use the lock, nor does it have to be the thread which drops ++ the lock. The lock can be held for very long periods of time, even across ++ multiple systems calls by multiple application threads. The idea is that a ++ coordinated group of DMAPI application threads can grab the lock, issue a ++ series of inode accesses and/or updates, then drop the lock, and be assured ++ that no other thread in the system could be modifying the inode at the same ++ time. The kernel is expected to blindly trust that the application will ++ not forget to unlock inodes it has locked, and will not deadlock itself ++ against the kernel. ++ ++ There are two types of DMAPI rights, file object (inode) and filesystem ++ object (superblock?). An inode right is the equivalent of the combination ++ of both the XFS ilock and iolock; if held exclusively, no data or metadata ++ within the file can be changed by non-lock-holding threads. The filesystem ++ object lock is a little fuzzier; I think that if it is held, things like ++ unmounts can be blocked, plus there is an event mask associated with the ++ filesystem which can't be updated without the lock. (By the way, that ++ event mask is supposed to be persistent in the superblock; add that to ++ your worklist :-) ++ ++ All events generated by XFS currently arrive with no rights, i.e. ++ DM_RIGHT_NULL, and return to the filesystem with no rights. It would be ++ smart to leave it this way if possible, because it otherwise becomes more ++ likely that an application thread will deadlock against the kernel if the ++ one responsible for calling dm_get_events() happens to touch a file which ++ was locked at the time the event was queued. Since the thread is blocked, ++ it can't read the event in order to find and drop the lock. Catch-22. If ++ you do have events that arrive with non-null rights, then dm_enqueue() needs ++ to have code added for synchronous events which atomically switches the ++ right from being a thread-based right to a dm_tokdata_t-based right without ++ allowing the lock to drop in between. You will probably have to add a new ++ dm_fsys_vector entry point to do this. The lock can't be lost during the ++ switch, or other threads might change the inode or superblock in between. ++ Likewise, if you need to return to the filesystem holding a right, then ++ you need a DMAPI-to-thread atomic switch to occur, most likely in ++ dm_change_right(). Again, the lock must not be lost during the switch; the ++ DMAPI spec spends a couple of pages stressing this. Another dm_fsys_vector ++ entry point is probably the answer. ++ ++ There are several assumptions implied in the current layout of the code. ++ First of all, if an event returns to the filesystem with a return value of ++ zero, then the filesystem can assume that any locks (rights) held at the ++ start of the event are still in effect at the end of the event. (Note that ++ the application could have temporarily dropped and reaquired the right ++ while the event was outstanding, however). If the event returns to the ++ filesystem with an errno, then the filesystem must assume that it has lost ++ any and all rights associated with any of the objects in the event. This ++ was done for a couple of reasons. First of all, since an errno is being ++ returned, most likely the filesystem is going to immediately drop all the ++ locks anyway. If the DMAPI code was required to unconditionally reobtain ++ all locks before returning to the filesystem, then dm_pending() wouldn't ++ work for NFS server threads because the process would block indefinitely ++ trying to get its thread-based rights back, because the DMAPI-rights ++ associated with the dm_tokdata_t in the outstanding event would prevent ++ the rights from being obtained. That would be a bad thing. We wouldn't ++ be able to let users Cntl-C out of read/write/truncate events either. ++ ++ If a case should ever surface where the thread has lost its rights even ++ though it has a zero return status, or where the thread has rights even ++ though it is returning with an errno, then this logic will have to be ++ reworked. This could be done by changing the 'right' parameters on all ++ the event calls to (dm_right_t *), so that they could serve both as IN ++ and OUT parameters. ++ ++ Some events such as DM_EVENT_DESTROY arrive without holding an inode ++ reference; if you don't have an inode reference, you can't have a right ++ on the file. ++ ++ One more quirk. The DM_EVENT_UNMOUNT event is defined to be synchronous ++ when it's behavior is asynchronous. If an unmount event arrives with ++ rights, the event should return with the same rights and should NOT leave ++ any rights in the dm_tokdata_t where the application could use them. ++*/ ++ ++ ++#define GETNEXTOFF(vdat) ((vdat).vd_offset + (vdat).vd_length) ++#define HANDLE_SIZE(tdp) \ ++ ((tdp)->td_type & DM_TDT_VFS ? DM_FSHSIZE : DM_HSIZE((tdp)->td_handle)) ++ ++ ++/* Given an inode pointer in a filesystem known to support DMAPI, ++ build a tdp structure for the corresponding inode. ++*/ ++ ++static dm_tokdata_t * ++dm_ip_data( ++ struct inode *ip, ++ dm_right_t right, ++ int referenced) /* != 0, caller holds inode reference */ ++{ ++ int error; ++ dm_tokdata_t *tdp; ++ int filetype; ++ ++ tdp = kmem_cache_alloc(dm_tokdata_cachep, GFP_KERNEL); ++ if (tdp == NULL) { ++ printk("%s/%d: kmem_cache_alloc(dm_tokdata_cachep) returned NULL\n", __FUNCTION__, __LINE__); ++ return NULL; ++ } ++ ++ tdp->td_next = NULL; ++ tdp->td_tevp = NULL; ++ tdp->td_app_ref = 0; ++ tdp->td_orig_right = right; ++ tdp->td_right = right; ++ tdp->td_flags = DM_TDF_ORIG; ++ if (referenced) { ++ tdp->td_flags |= DM_TDF_EVTREF; ++ } ++ ++ filetype = ip->i_mode & S_IFMT; ++ if (filetype == S_IFREG) { ++ tdp->td_type = DM_TDT_REG; ++ } else if (filetype == S_IFDIR) { ++ tdp->td_type = DM_TDT_DIR; ++ } else if (filetype == S_IFLNK) { ++ tdp->td_type = DM_TDT_LNK; ++ } else { ++ tdp->td_type = DM_TDT_OTH; ++ } ++ ++ if (referenced) { ++ tdp->td_ip = ip; ++ } else { ++ tdp->td_ip = NULL; ++ } ++ tdp->td_vcount = 0; ++ ++ if ((error = dm_ip_to_handle(ip, &tdp->td_handle)) != 0) { ++ panic("dm_ip_data: dm_ip_to_handle failed for ip %p in " ++ "a DMAPI filesystem, errno %d\n", ip, error); ++ } ++ ++ return(tdp); ++} ++ ++ ++/* Given a sb pointer to a filesystem known to support DMAPI, build a tdp ++ structure for that sb. ++*/ ++static dm_tokdata_t * ++dm_sb_data( ++ struct super_block *sb, ++ struct inode *ip, /* will be NULL for DM_EVENT_UNMOUNT */ ++ dm_right_t right) ++{ ++ dm_tokdata_t *tdp; ++ struct filesystem_dmapi_operations *dops; ++ dm_fsid_t fsid; ++ ++ dops = dm_fsys_ops(sb); ++ ASSERT(dops); ++ dops->get_fsid(sb, &fsid); ++ ++ tdp = kmem_cache_alloc(dm_tokdata_cachep, GFP_KERNEL); ++ if (tdp == NULL) { ++ printk("%s/%d: kmem_cache_alloc(dm_tokdata_cachep) returned NULL\n", __FUNCTION__, __LINE__); ++ return NULL; ++ } ++ ++ tdp->td_next = NULL; ++ tdp->td_tevp = NULL; ++ tdp->td_app_ref = 0; ++ tdp->td_orig_right = right; ++ tdp->td_right = right; ++ tdp->td_flags = DM_TDF_ORIG; ++ if (ip) { ++ tdp->td_flags |= DM_TDF_EVTREF; ++ } ++ tdp->td_type = DM_TDT_VFS; ++ tdp->td_ip = ip; ++ tdp->td_vcount = 0; ++ ++ memcpy(&tdp->td_handle.ha_fsid, &fsid, sizeof(fsid)); ++ memset((char *)&tdp->td_handle.ha_fsid + sizeof(fsid), 0, ++ sizeof(tdp->td_handle) - sizeof(fsid)); ++ ++ return(tdp); ++} ++ ++ ++/* Link a tdp structure into the tevp. */ ++ ++static void ++dm_add_handle_to_event( ++ dm_tokevent_t *tevp, ++ dm_tokdata_t *tdp) ++{ ++ tdp->td_next = tevp->te_tdp; ++ tevp->te_tdp = tdp; ++ tdp->td_tevp = tevp; ++} ++ ++ ++/* Generate the given data event for the inode, and wait for a reply. The ++ caller must guarantee that the inode's reference count is greater than zero ++ so that the filesystem can't disappear while the request is outstanding. ++*/ ++ ++int ++dm_send_data_event( ++ dm_eventtype_t event, ++ struct inode *ip, ++ dm_right_t vp_right, /* current right for ip */ ++ dm_off_t offset, ++ size_t length, ++ int flags) /* 0 or DM_FLAGS_NDELAY */ ++{ ++ dm_data_event_t *datap; ++ dm_tokevent_t *tevp; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ tdp = dm_ip_data(ip, vp_right, /* reference held */ 1); ++ if (tdp == NULL) ++ return -ENOMEM; ++ ++ /* Calculate the size of the event in bytes, create an event structure ++ for it, and insert the file's handle into the event. ++ */ ++ ++ tevp = dm_evt_create_tevp(event, HANDLE_SIZE(tdp), (void **)&datap); ++ if (tevp == NULL) { ++ kmem_cache_free(dm_tokdata_cachep, tdp); ++ return(-ENOMEM); ++ } ++ dm_add_handle_to_event(tevp, tdp); ++ ++ /* Now fill in all the dm_data_event_t fields. */ ++ ++ datap->de_handle.vd_offset = sizeof(*datap); ++ datap->de_handle.vd_length = HANDLE_SIZE(tdp); ++ memcpy((char *)datap + datap->de_handle.vd_offset, &tdp->td_handle, ++ datap->de_handle.vd_length); ++ datap->de_offset = offset; ++ datap->de_length = length; ++ ++ /* Queue the message and wait for the reply. */ ++ ++ error = dm_enqueue_normal_event(ip->i_sb, &tevp, flags); ++ ++ /* If no errors occurred, we must leave with the same rights we had ++ upon entry. If errors occurred, we must leave with no rights. ++ */ ++ ++ dm_evt_rele_tevp(tevp, error); ++ ++ return(error); ++} ++ ++ ++/* Generate the destroy event for the inode and wait until the request has been ++ queued. The caller does not hold an inode reference or a right on the inode, ++ but it must otherwise lock down the inode such that the filesystem can't ++ disappear while the request is waiting to be queued. While waiting to be ++ queued, the inode must not be referenceable either by path or by a call ++ to dm_handle_to_ip(). ++*/ ++ ++int ++dm_send_destroy_event( ++ struct inode *ip, ++ dm_right_t vp_right) /* always DM_RIGHT_NULL */ ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokevent_t *tevp; ++ dm_tokdata_t *tdp; ++ dm_destroy_event_t *destp; ++ dm_attrname_t attrname; ++ char *value; ++ int value_len; ++ int error; ++ ++ tdp = dm_ip_data(ip, vp_right, /* no reference held */ 0); ++ if (tdp == NULL) ++ return -ENOMEM; ++ ++ if ((error = dm_waitfor_destroy_attrname(ip->i_sb, &attrname)) != 0) ++ return(error); ++ ++ /* If a return-on-destroy attribute name exists for this filesystem, ++ see if the object being deleted has this attribute. If the object ++ doesn't have the attribute or if we encounter an error, then send ++ the event without the attribute. ++ */ ++ ++ value_len = -1; /* because zero is a valid attribute length */ ++ if (attrname.an_chars[0] != '\0') { ++ fsys_vector = dm_fsys_vector(ip); ++ error = fsys_vector->get_destroy_dmattr(ip, vp_right, &attrname, ++ &value, &value_len); ++ if (error && error != -ENODATA) ++ return error; ++ } ++ ++ /* Now that we know the size of the attribute value, if any, calculate ++ the size of the event in bytes, create an event structure for it, ++ and insert the handle into the event. ++ */ ++ ++ tevp = dm_evt_create_tevp(DM_EVENT_DESTROY, ++ HANDLE_SIZE(tdp) + (value_len >= 0 ? value_len : 0), ++ (void **)&destp); ++ if (tevp == NULL) { ++ kmem_cache_free(dm_tokdata_cachep, tdp); ++ if (value_len > 0) ++ kfree(value); ++ return(-ENOMEM); ++ } ++ dm_add_handle_to_event(tevp, tdp); ++ ++ /* Now fill in all the dm_destroy_event_t fields. */ ++ ++ destp->ds_handle.vd_offset = sizeof(*destp); ++ destp->ds_handle.vd_length = HANDLE_SIZE(tdp); ++ memcpy((char *)destp + destp->ds_handle.vd_offset, &tdp->td_handle, ++ destp->ds_handle.vd_length); ++ if (value_len >= 0) { ++ destp->ds_attrname = attrname; ++ destp->ds_attrcopy.vd_length = value_len; ++ if (value_len == 0) { ++ destp->ds_attrcopy.vd_offset = 0; ++ } else { ++ destp->ds_attrcopy.vd_offset = GETNEXTOFF(destp->ds_handle); ++ memcpy((char *)destp + destp->ds_attrcopy.vd_offset, value, ++ value_len); ++ kfree(value); ++ } ++ } ++ ++ /* Queue the message asynchronously. */ ++ ++ error = dm_enqueue_normal_event(ip->i_sb, &tevp, 0); ++ ++ /* Since we had no rights upon entry, we have none to reobtain before ++ leaving. ++ */ ++ ++ dm_evt_rele_tevp(tevp, 1); ++ ++ return(error); ++} ++ ++ ++/* The dm_mount_event_t event is sent in turn to all sessions that have asked ++ for it until one either rejects it or accepts it. The filesystem is not ++ going anywhere because the mount is blocked until the event is answered. ++*/ ++ ++int ++dm_send_mount_event( ++ struct super_block *sb, /* filesystem being mounted */ ++ dm_right_t vfsp_right, ++ struct inode *ip, /* mounted on directory */ ++ dm_right_t vp_right, ++ struct inode *rootip, ++ dm_right_t rootvp_right, ++ char *name1, /* mount path */ ++ char *name2) /* filesystem device name */ ++{ ++ int error; ++ dm_tokevent_t *tevp = NULL; ++ dm_tokdata_t *tdp1 = NULL; /* filesystem handle for event */ ++ dm_tokdata_t *tdp2 = NULL; /* file handle for mounted-on dir. */ ++ dm_tokdata_t *tdp3 = NULL; /* file handle for root inode */ ++ dm_mount_event_t *mp; ++ size_t nextoff; ++ ++ /* Convert the sb to a filesystem handle, and ip and rootip into ++ file handles. ip (the mounted-on directory) may not have a handle ++ if it is a different filesystem type which does not support DMAPI. ++ */ ++ ++ tdp1 = dm_sb_data(sb, rootip, vfsp_right); ++ if (tdp1 == NULL) ++ goto out_nomem; ++ ++ if ((ip == NULL) || dm_check_dmapi_ip(ip)) { ++ ip = NULL; /* we are mounting on non-DMAPI FS */ ++ } else { ++ tdp2 = dm_ip_data(ip, vp_right, /* reference held */ 1); ++ if (tdp2 == NULL) ++ goto out_nomem; ++ } ++ ++ tdp3 = dm_ip_data(rootip, rootvp_right, /* reference held */ 1); ++ if (tdp3 == NULL) ++ goto out_nomem; ++ ++ /* Calculate the size of the event in bytes, create an event structure ++ for it, and insert the handles into the event. ++ */ ++ ++ tevp = dm_evt_create_tevp(DM_EVENT_MOUNT, ++ HANDLE_SIZE(tdp1) + (ip ? HANDLE_SIZE(tdp2) : 0) + ++ HANDLE_SIZE(tdp3) + strlen(name1) + 1 + ++ strlen(name2) + 1, (void **)&mp); ++ if (tevp == NULL) ++ goto out_nomem; ++ ++ dm_add_handle_to_event(tevp, tdp1); ++ if (ip) ++ dm_add_handle_to_event(tevp, tdp2); ++ dm_add_handle_to_event(tevp, tdp3); ++ ++ /* Now fill in all the dm_mount_event_t fields. */ ++ ++ mp->me_handle1.vd_offset = sizeof(*mp); ++ mp->me_handle1.vd_length = HANDLE_SIZE(tdp1); ++ memcpy((char *) mp + mp->me_handle1.vd_offset, &tdp1->td_handle, ++ mp->me_handle1.vd_length); ++ nextoff = GETNEXTOFF(mp->me_handle1); ++ ++ if (ip) { ++ mp->me_handle2.vd_offset = nextoff; ++ mp->me_handle2.vd_length = HANDLE_SIZE(tdp2); ++ memcpy((char *)mp + mp->me_handle2.vd_offset, &tdp2->td_handle, ++ mp->me_handle2.vd_length); ++ nextoff = GETNEXTOFF(mp->me_handle2); ++ } ++ ++ mp->me_name1.vd_offset = nextoff; ++ mp->me_name1.vd_length = strlen(name1) + 1; ++ memcpy((char *)mp + mp->me_name1.vd_offset, name1, mp->me_name1.vd_length); ++ nextoff = GETNEXTOFF(mp->me_name1); ++ ++ mp->me_name2.vd_offset = nextoff; ++ mp->me_name2.vd_length = strlen(name2) + 1; ++ memcpy((char *)mp + mp->me_name2.vd_offset, name2, mp->me_name2.vd_length); ++ nextoff = GETNEXTOFF(mp->me_name2); ++ ++ mp->me_roothandle.vd_offset = nextoff; ++ mp->me_roothandle.vd_length = HANDLE_SIZE(tdp3); ++ memcpy((char *)mp + mp->me_roothandle.vd_offset, &tdp3->td_handle, ++ mp->me_roothandle.vd_length); ++ ++ mp->me_mode = (sb->s_flags & MS_RDONLY ? DM_MOUNT_RDONLY : 0); ++ ++ /* Queue the message and wait for the reply. */ ++ ++ error = dm_enqueue_mount_event(sb, tevp); ++ ++ /* If no errors occurred, we must leave with the same rights we had ++ upon entry. If errors occurred, we must leave with no rights. ++ */ ++ ++ dm_evt_rele_tevp(tevp, error); ++ ++ return(error); ++ ++out_nomem: ++ if (tevp) ++ kfree(tevp); ++ if (tdp1) ++ kmem_cache_free(dm_tokdata_cachep, tdp1); ++ if (tdp2) ++ kmem_cache_free(dm_tokdata_cachep, tdp2); ++ if (tdp3) ++ kmem_cache_free(dm_tokdata_cachep, tdp3); ++ return -ENOMEM; ++} ++ ++ ++/* Generate an DM_EVENT_UNMOUNT event and wait for a reply. The 'retcode' ++ field indicates whether this is a successful or unsuccessful unmount. ++ If successful, the filesystem is already unmounted, and any pending handle ++ reference to the filesystem will be failed. If the unmount was ++ unsuccessful, then the filesystem will be placed back into full service. ++ ++ The DM_EVENT_UNMOUNT event should really be asynchronous, because the ++ application has no control over whether or not the unmount succeeds. (The ++ DMAPI spec defined it that way because asynchronous events aren't always ++ guaranteed to be delivered.) ++ ++ Since the filesystem is already unmounted in the successful case, the ++ DM_EVENT_UNMOUNT event can't make available any inode to be used in ++ subsequent sid/hanp/hlen/token calls by the application. The event will ++ hang around until the application does a DM_RESP_CONTINUE, but the handle ++ within the event is unusable by the application. ++*/ ++ ++void ++dm_send_unmount_event( ++ struct super_block *sb, ++ struct inode *ip, /* NULL if unmount successful */ ++ dm_right_t vfsp_right, ++ mode_t mode, ++ int retcode, /* errno, if unmount failed */ ++ int flags) ++{ ++ dm_namesp_event_t *np; ++ dm_tokevent_t *tevp; ++ dm_tokdata_t *tdp1; ++ ++ /* If the unmount failed, put the filesystem back into full service, ++ allowing blocked handle references to finish. If it succeeded, put ++ the filesystem into the DM_STATE_UNMOUNTED state and fail all ++ blocked DM_NO_TOKEN handle accesses. ++ */ ++ ++ if (retcode != 0) { /* unmount was unsuccessful */ ++ dm_change_fsys_entry(sb, DM_STATE_MOUNTED); ++ } else { ++ dm_change_fsys_entry(sb, DM_STATE_UNMOUNTED); ++ } ++ ++ /* If the event wasn't in the filesystem dm_eventset_t, just remove ++ the filesystem from the list of DMAPI filesystems and return. ++ */ ++ ++ if (flags & DM_FLAGS_UNWANTED) { ++ if (retcode == 0) ++ dm_remove_fsys_entry(sb); ++ return; ++ } ++ ++ /* Calculate the size of the event in bytes and allocate zeroed memory ++ for it. ++ */ ++ ++ tdp1 = dm_sb_data(sb, ip, vfsp_right); ++ if (tdp1 == NULL) ++ return; ++ ++ tevp = dm_evt_create_tevp(DM_EVENT_UNMOUNT, HANDLE_SIZE(tdp1), ++ (void **)&np); ++ if (tevp == NULL) { ++ kmem_cache_free(dm_tokdata_cachep, tdp1); ++ return; ++ } ++ ++ dm_add_handle_to_event(tevp, tdp1); ++ ++ /* Now copy in all the dm_namesp_event_t specific fields. */ ++ ++ np->ne_handle1.vd_offset = sizeof(*np); ++ np->ne_handle1.vd_length = HANDLE_SIZE(tdp1); ++ memcpy((char *) np + np->ne_handle1.vd_offset, &tdp1->td_handle, ++ np->ne_handle1.vd_length); ++ np->ne_mode = mode; ++ np->ne_retcode = retcode; ++ ++ /* Since DM_EVENT_UNMOUNT is effectively asynchronous, queue the ++ message and ignore any error return for DM_EVENT_UNMOUNT. ++ */ ++ ++ (void)dm_enqueue_normal_event(sb, &tevp, flags); ++ ++ if (retcode == 0) ++ dm_remove_fsys_entry(sb); ++ ++ dm_evt_rele_tevp(tevp, 0); ++} ++ ++ ++/* Generate the given namespace event and wait for a reply (if synchronous) or ++ until the event has been queued (asynchronous). The caller must guarantee ++ that at least one inode within the filesystem has had its reference count ++ bumped so that the filesystem can't disappear while the event is ++ outstanding. ++*/ ++ ++int ++dm_send_namesp_event( ++ dm_eventtype_t event, ++ struct super_block *sb, /* used by PREUNMOUNT */ ++ struct inode *ip1, ++ dm_right_t vp1_right, ++ struct inode *ip2, ++ dm_right_t vp2_right, ++ const char *name1, ++ const char *name2, ++ mode_t mode, ++ int retcode, ++ int flags) ++{ ++ dm_namesp_event_t *np; ++ dm_tokevent_t *tevp; ++ dm_tokdata_t *tdp1 = NULL; /* primary handle for event */ ++ dm_tokdata_t *tdp2 = NULL; /* additional handle for event */ ++ size_t nextoff; ++ int error; ++ ++ if (sb == NULL) ++ sb = ip1->i_sb; ++ ++ switch (event) { ++ case DM_EVENT_PREUNMOUNT: ++ /* ++ * PREUNMOUNT - Send the file system handle in handle1, ++ * and the handle for the root dir in the second. Otherwise ++ * it's a normal sync message; i.e. succeeds or fails ++ * depending on the app's return code. ++ * ip1 and ip2 are both the root dir of mounted FS ++ * vp1_right is the filesystem right. ++ * vp2_right is the root inode right. ++ */ ++ ++ if (flags & DM_FLAGS_UNWANTED) { ++ dm_change_fsys_entry(sb, DM_STATE_UNMOUNTING); ++ return(0); ++ } ++ if (ip1 == NULL) { ++ /* If preunmount happens after kill_super then ++ * it's too late; there's nothing left with which ++ * to construct an event. ++ */ ++ return(0); ++ } ++ tdp1 = dm_sb_data(sb, ip1, vp1_right); ++ if (tdp1 == NULL) ++ return -ENOMEM; ++ tdp2 = dm_ip_data(ip2, vp2_right, /* reference held */ 1); ++ if (tdp2 == NULL) { ++ kmem_cache_free(dm_tokdata_cachep, tdp1); ++ return -ENOMEM; ++ } ++ break; ++ ++ case DM_EVENT_NOSPACE: ++ /* vp1_right is the filesystem right. */ ++ ++ tdp1 = dm_sb_data(sb, ip1, vp1_right); ++ if (tdp1 == NULL) ++ return -ENOMEM; ++ tdp2 = dm_ip_data(ip2, vp2_right, /* reference held */ 1); /* additional info - not in the spec */ ++ if (tdp2 == NULL) { ++ kmem_cache_free(dm_tokdata_cachep, tdp1); ++ return -ENOMEM; ++ } ++ break; ++ ++ default: ++ /* All other events only pass in inodes and don't require any ++ special cases. ++ */ ++ ++ tdp1 = dm_ip_data(ip1, vp1_right, /* reference held */ 1); ++ if (tdp1 == NULL) ++ return -ENOMEM; ++ if (ip2) { ++ tdp2 = dm_ip_data(ip2, vp2_right, /* reference held */ 1); ++ if (tdp2 == NULL) { ++ kmem_cache_free(dm_tokdata_cachep, tdp1); ++ return -ENOMEM; ++ } ++ } ++ } ++ ++ /* Calculate the size of the event in bytes and allocate zeroed memory ++ for it. ++ */ ++ ++ tevp = dm_evt_create_tevp(event, ++ HANDLE_SIZE(tdp1) + (ip2 ? HANDLE_SIZE(tdp2) : 0) + ++ (name1 ? strlen(name1) + 1 : 0) + ++ (name2 ? strlen(name2) + 1 : 0), (void **)&np); ++ if (tevp == NULL) { ++ if (tdp1) ++ kmem_cache_free(dm_tokdata_cachep, tdp1); ++ if (tdp2) ++ kmem_cache_free(dm_tokdata_cachep, tdp2); ++ return(-ENOMEM); ++ } ++ ++ dm_add_handle_to_event(tevp, tdp1); ++ if (ip2) ++ dm_add_handle_to_event(tevp, tdp2); ++ ++ /* Now copy in all the dm_namesp_event_t specific fields. */ ++ ++ np->ne_handle1.vd_offset = sizeof(*np); ++ np->ne_handle1.vd_length = HANDLE_SIZE(tdp1); ++ memcpy((char *) np + np->ne_handle1.vd_offset, &tdp1->td_handle, ++ np->ne_handle1.vd_length); ++ nextoff = GETNEXTOFF(np->ne_handle1); ++ if (ip2) { ++ np->ne_handle2.vd_offset = nextoff; ++ np->ne_handle2.vd_length = HANDLE_SIZE(tdp2); ++ memcpy((char *)np + np->ne_handle2.vd_offset, &tdp2->td_handle, ++ np->ne_handle2.vd_length); ++ nextoff = GETNEXTOFF(np->ne_handle2); ++ } ++ if (name1) { ++ np->ne_name1.vd_offset = nextoff; ++ np->ne_name1.vd_length = strlen(name1) + 1; ++ memcpy((char *)np + np->ne_name1.vd_offset, name1, ++ np->ne_name1.vd_length); ++ nextoff = GETNEXTOFF(np->ne_name1); ++ } ++ if (name2) { ++ np->ne_name2.vd_offset = nextoff; ++ np->ne_name2.vd_length = strlen(name2) + 1; ++ memcpy((char *)np + np->ne_name2.vd_offset, name2, ++ np->ne_name2.vd_length); ++ } ++ np->ne_mode = mode; ++ np->ne_retcode = retcode; ++ ++ /* Queue the message and wait for the reply. */ ++ ++ error = dm_enqueue_normal_event(sb, &tevp, flags); ++ ++ /* If no errors occurred, we must leave with the same rights we had ++ upon entry. If errors occurred, we must leave with no rights. ++ */ ++ ++ dm_evt_rele_tevp(tevp, error); ++ ++ if (!error && event == DM_EVENT_PREUNMOUNT) { ++ dm_change_fsys_entry(sb, DM_STATE_UNMOUNTING); ++ } ++ ++ return(error); ++} ++ ++ ++/* ++ * Send a message of type "DM_EVENT_USER". Since no inode is involved, we ++ * don't have to worry about rights here. ++ */ ++ ++int ++dm_send_msg( ++ dm_sessid_t targetsid, ++ dm_msgtype_t msgtype, /* SYNC or ASYNC */ ++ size_t buflen, ++ void __user *bufp) ++{ ++ dm_tokevent_t *tevp; ++ int sync; ++ void *msgp; ++ int error; ++ ++ if (buflen > DM_MAX_MSG_DATA) ++ return(-E2BIG); ++ if (msgtype == DM_MSGTYPE_ASYNC) { ++ sync = 0; ++ } else if (msgtype == DM_MSGTYPE_SYNC) { ++ sync = 1; ++ } else { ++ return(-EINVAL); ++ } ++ ++ tevp = dm_evt_create_tevp(DM_EVENT_USER, buflen, (void **)&msgp); ++ if (tevp == NULL) ++ return -ENOMEM; ++ ++ if (buflen && copy_from_user(msgp, bufp, buflen)) { ++ dm_evt_rele_tevp(tevp, 0); ++ return(-EFAULT); ++ } ++ ++ /* Enqueue the request and wait for the reply. */ ++ ++ error = dm_enqueue_sendmsg_event(targetsid, tevp, sync); ++ ++ /* Destroy the tevp and return the reply. (dm_pending is not ++ supported here.) ++ */ ++ ++ dm_evt_rele_tevp(tevp, error); ++ ++ return(error); ++} ++ ++ ++/* ++ * Send a message of type "DM_EVENT_USER". Since no inode is involved, we ++ * don't have to worry about rights here. ++ */ ++ ++int ++dm_create_userevent( ++ dm_sessid_t sid, ++ size_t msglen, ++ void __user *msgdatap, ++ dm_token_t __user *tokenp) /* return token created */ ++{ ++ dm_tokevent_t *tevp; ++ dm_token_t token; ++ int error; ++ void *msgp; ++ ++ if (msglen > DM_MAX_MSG_DATA) ++ return(-E2BIG); ++ ++ tevp = dm_evt_create_tevp(DM_EVENT_USER, msglen, (void **)&msgp); ++ if (tevp == NULL) ++ return(-ENOMEM); ++ ++ if (msglen && copy_from_user(msgp, msgdatap, msglen)) { ++ dm_evt_rele_tevp(tevp, 0); ++ return(-EFAULT); ++ } ++ ++ /* Queue the message. If that didn't work, free the tevp structure. */ ++ ++ if ((error = dm_enqueue_user_event(sid, tevp, &token)) != 0) ++ dm_evt_rele_tevp(tevp, 0); ++ ++ if (!error && copy_to_user(tokenp, &token, sizeof(token))) ++ error = -EFAULT; ++ ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_handle.c +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++ ++int ++dm_create_by_handle( ++ dm_sessid_t sid, ++ void __user *dirhanp, ++ size_t dirhlen, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, dirhanp, dirhlen, token, DM_TDT_DIR, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->create_by_handle(tdp->td_ip, tdp->td_right, ++ hanp, hlen, cname); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_mkdir_by_handle( ++ dm_sessid_t sid, ++ void __user *dirhanp, ++ size_t dirhlen, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, dirhanp, dirhlen, token, DM_TDT_DIR, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->mkdir_by_handle(tdp->td_ip, tdp->td_right, ++ hanp, hlen, cname); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_symlink_by_handle( ++ dm_sessid_t sid, ++ void __user *dirhanp, ++ size_t dirhlen, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname, ++ char __user *path) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, dirhanp, dirhlen, token, DM_TDT_DIR, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->symlink_by_handle(tdp->td_ip, tdp->td_right, ++ hanp, hlen, cname, path); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_hole.c +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++ ++int ++dm_get_allocinfo_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t __user *offp, ++ u_int nelem, ++ dm_extent_t __user *extentp, ++ u_int __user *nelemp, ++ int *rvp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_allocinfo_rvp(tdp->td_ip, tdp->td_right, ++ offp, nelem, extentp, nelemp, rvp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_probe_hole( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t off, ++ dm_size_t len, ++ dm_off_t __user *roffp, ++ dm_size_t __user *rlenp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->probe_hole(tdp->td_ip, tdp->td_right, ++ off, len, roffp, rlenp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_punch_hole( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t off, ++ dm_size_t len) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->punch_hole(tdp->td_ip, tdp->td_right, off, len); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_io.c +@@ -0,0 +1,142 @@ ++/* ++ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++ ++int ++dm_read_invis_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->read_invis_rvp(tdp->td_ip, tdp->td_right, ++ off, len, bufp, rvp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_write_invis_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ int flags, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->write_invis_rvp(tdp->td_ip, tdp->td_right, ++ flags, off, len, bufp, rvp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_sync_by_handle ( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->sync_by_handle(tdp->td_ip, tdp->td_right); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_get_dioinfo ( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_dioinfo_t __user *diop) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_dioinfo(tdp->td_ip, tdp->td_right, diop); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_kern.h +@@ -0,0 +1,598 @@ ++/* ++ * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++ ++#ifndef __DMAPI_KERN_H__ ++#define __DMAPI_KERN_H__ ++ ++#include ++ ++union sys_dmapi_uarg { ++ void *p; ++ __u64 u; ++}; ++typedef union sys_dmapi_uarg sys_dmapi_u; ++ ++struct sys_dmapi_args { ++ sys_dmapi_u uarg1, uarg2, uarg3, uarg4, uarg5, uarg6, uarg7, uarg8, ++ uarg9, uarg10, uarg11; ++}; ++typedef struct sys_dmapi_args sys_dmapi_args_t; ++ ++#define DM_Uarg(uap,i) uap->uarg##i.u ++#define DM_Parg(uap,i) uap->uarg##i.p ++ ++#ifdef __KERNEL__ ++ ++struct dm_handle_t; ++ ++/* The first group of definitions and prototypes define the filesystem's ++ interface into the DMAPI code. ++*/ ++ ++ ++/* Definitions used for the flags field on dm_send_data_event(), ++ dm_send_unmount_event(), and dm_send_namesp_event() calls. ++*/ ++ ++#define DM_FLAGS_NDELAY 0x001 /* return EAGAIN after dm_pending() */ ++#define DM_FLAGS_UNWANTED 0x002 /* event not in fsys dm_eventset_t */ ++ ++/* Possible code levels reported by dm_code_level(). */ ++ ++#define DM_CLVL_INIT 0 /* DMAPI prior to X/Open compliance */ ++#define DM_CLVL_XOPEN 1 /* X/Open compliant DMAPI */ ++ ++ ++/* ++ * Filesystem operations accessed by the DMAPI core. ++ */ ++struct filesystem_dmapi_operations { ++ int (*get_fsys_vector)(struct super_block *sb, void *addr); ++ int (*fh_to_inode)(struct super_block *sb, struct inode **ip, ++ dm_fid_t *fid); ++ int (*inode_to_fh)(struct inode *ip, dm_fid_t *fid, ++ dm_fsid_t *fsid ); ++ void (*get_fsid)(struct super_block *sb, dm_fsid_t *fsid); ++#define HAVE_DM_QUEUE_FLUSH ++ int (*flushing)(struct inode *ip); ++}; ++ ++ ++/* Prototypes used outside of the DMI module/directory. */ ++ ++int dm_send_data_event( ++ dm_eventtype_t event, ++ struct inode *ip, ++ dm_right_t vp_right, ++ dm_off_t off, ++ size_t len, ++ int flags); ++ ++int dm_send_destroy_event( ++ struct inode *ip, ++ dm_right_t vp_right); ++ ++int dm_send_mount_event( ++ struct super_block *sb, ++ dm_right_t vfsp_right, ++ struct inode *ip, ++ dm_right_t vp_right, ++ struct inode *rootip, ++ dm_right_t rootvp_right, ++ char *name1, ++ char *name2); ++ ++int dm_send_namesp_event( ++ dm_eventtype_t event, ++ struct super_block *sb, ++ struct inode *ip1, ++ dm_right_t vp1_right, ++ struct inode *ip2, ++ dm_right_t vp2_right, ++ const char *name1, ++ const char *name2, ++ mode_t mode, ++ int retcode, ++ int flags); ++ ++void dm_send_unmount_event( ++ struct super_block *sbp, ++ struct inode *ip, ++ dm_right_t sbp_right, ++ mode_t mode, ++ int retcode, ++ int flags); ++ ++int dm_code_level(void); ++ ++int dm_ip_to_handle ( ++ struct inode *ip, ++ dm_handle_t *handlep); ++ ++#define HAVE_DM_RELEASE_THREADS_ERRNO ++int dm_release_threads( ++ struct super_block *sb, ++ struct inode *inode, ++ int errno); ++ ++void dmapi_register( ++ struct file_system_type *fstype, ++ struct filesystem_dmapi_operations *dmapiops); ++ ++void dmapi_unregister( ++ struct file_system_type *fstype); ++ ++int dmapi_registered( ++ struct file_system_type *fstype, ++ struct filesystem_dmapi_operations **dmapiops); ++ ++ ++/* The following prototypes and definitions are used by DMAPI as its ++ interface into the filesystem code. Communication between DMAPI and the ++ filesystem are established as follows: ++ 1. DMAPI uses the VFS_DMAPI_FSYS_VECTOR to ask for the addresses ++ of all the functions within the filesystem that it may need to call. ++ 2. The filesystem returns an array of function name/address pairs which ++ DMAPI builds into a function vector. ++ The VFS_DMAPI_FSYS_VECTOR call is only made one time for a particular ++ filesystem type. From then on, DMAPI uses its function vector to call the ++ filesystem functions directly. Functions in the array which DMAPI doesn't ++ recognize are ignored. A dummy function which returns ENOSYS is used for ++ any function that DMAPI needs but which was not provided by the filesystem. ++ If XFS doesn't recognize the VFS_DMAPI_FSYS_VECTOR, DMAPI assumes that it ++ doesn't have the X/Open support code; in this case DMAPI uses the XFS-code ++ originally bundled within DMAPI. ++ ++ The goal of this interface is allow incremental changes to be made to ++ both the filesystem and to DMAPI while minimizing inter-patch dependencies, ++ and to eventually allow DMAPI to support multiple filesystem types at the ++ same time should that become necessary. ++*/ ++ ++typedef enum { ++ DM_FSYS_CLEAR_INHERIT = 0, ++ DM_FSYS_CREATE_BY_HANDLE = 1, ++ DM_FSYS_DOWNGRADE_RIGHT = 2, ++ DM_FSYS_GET_ALLOCINFO_RVP = 3, ++ DM_FSYS_GET_BULKALL_RVP = 4, ++ DM_FSYS_GET_BULKATTR_RVP = 5, ++ DM_FSYS_GET_CONFIG = 6, ++ DM_FSYS_GET_CONFIG_EVENTS = 7, ++ DM_FSYS_GET_DESTROY_DMATTR = 8, ++ DM_FSYS_GET_DIOINFO = 9, ++ DM_FSYS_GET_DIRATTRS_RVP = 10, ++ DM_FSYS_GET_DMATTR = 11, ++ DM_FSYS_GET_EVENTLIST = 12, ++ DM_FSYS_GET_FILEATTR = 13, ++ DM_FSYS_GET_REGION = 14, ++ DM_FSYS_GETALL_DMATTR = 15, ++ DM_FSYS_GETALL_INHERIT = 16, ++ DM_FSYS_INIT_ATTRLOC = 17, ++ DM_FSYS_MKDIR_BY_HANDLE = 18, ++ DM_FSYS_PROBE_HOLE = 19, ++ DM_FSYS_PUNCH_HOLE = 20, ++ DM_FSYS_READ_INVIS_RVP = 21, ++ DM_FSYS_RELEASE_RIGHT = 22, ++ DM_FSYS_REMOVE_DMATTR = 23, ++ DM_FSYS_REQUEST_RIGHT = 24, ++ DM_FSYS_SET_DMATTR = 25, ++ DM_FSYS_SET_EVENTLIST = 26, ++ DM_FSYS_SET_FILEATTR = 27, ++ DM_FSYS_SET_INHERIT = 28, ++ DM_FSYS_SET_REGION = 29, ++ DM_FSYS_SYMLINK_BY_HANDLE = 30, ++ DM_FSYS_SYNC_BY_HANDLE = 31, ++ DM_FSYS_UPGRADE_RIGHT = 32, ++ DM_FSYS_WRITE_INVIS_RVP = 33, ++ DM_FSYS_OBJ_REF_HOLD = 34, ++ DM_FSYS_MAX = 35 ++} dm_fsys_switch_t; ++ ++ ++#define DM_FSYS_OBJ 0x1 /* object refers to a fsys handle */ ++ ++ ++/* ++ * Prototypes for filesystem-specific functions. ++ */ ++ ++typedef int (*dm_fsys_clear_inherit_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_attrname_t __user *attrnamep); ++ ++typedef int (*dm_fsys_create_by_handle_t)( ++ struct inode *ip, ++ dm_right_t right, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname); ++ ++typedef int (*dm_fsys_downgrade_right_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int type); /* DM_FSYS_OBJ or zero */ ++ ++typedef int (*dm_fsys_get_allocinfo_rvp_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_off_t __user *offp, ++ u_int nelem, ++ dm_extent_t __user *extentp, ++ u_int __user *nelemp, ++ int *rvalp); ++ ++typedef int (*dm_fsys_get_bulkall_rvp_t)( ++ struct inode *ip, /* root inode */ ++ dm_right_t right, ++ u_int mask, ++ dm_attrname_t __user *attrnamep, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvalp); ++ ++typedef int (*dm_fsys_get_bulkattr_rvp_t)( ++ struct inode *ip, /* root inode */ ++ dm_right_t right, ++ u_int mask, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvalp); ++ ++typedef int (*dm_fsys_get_config_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_config_t flagname, ++ dm_size_t __user *retvalp); ++ ++typedef int (*dm_fsys_get_config_events_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int nelem, ++ dm_eventset_t __user *eventsetp, ++ u_int __user *nelemp); ++ ++typedef int (*dm_fsys_get_destroy_dmattr_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_attrname_t *attrnamep, ++ char **valuepp, ++ int *vlenp); ++ ++typedef int (*dm_fsys_get_dioinfo_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_dioinfo_t __user *diop); ++ ++typedef int (*dm_fsys_get_dirattrs_rvp_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int mask, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvalp); ++ ++typedef int (*dm_fsys_get_dmattr_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_attrname_t __user *attrnamep, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++typedef int (*dm_fsys_get_eventlist_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int type, ++ u_int nelem, ++ dm_eventset_t *eventsetp, /* in kernel space! */ ++ u_int *nelemp); /* in kernel space! */ ++ ++typedef int (*dm_fsys_get_fileattr_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int mask, ++ dm_stat_t __user *statp); ++ ++typedef int (*dm_fsys_get_region_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int nelem, ++ dm_region_t __user *regbufp, ++ u_int __user *nelemp); ++ ++typedef int (*dm_fsys_getall_dmattr_t)( ++ struct inode *ip, ++ dm_right_t right, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++typedef int (*dm_fsys_getall_inherit_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int nelem, ++ dm_inherit_t __user *inheritbufp, ++ u_int __user *nelemp); ++ ++typedef int (*dm_fsys_init_attrloc_t)( ++ struct inode *ip, /* sometimes root inode */ ++ dm_right_t right, ++ dm_attrloc_t __user *locp); ++ ++typedef int (*dm_fsys_mkdir_by_handle_t)( ++ struct inode *ip, ++ dm_right_t right, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname); ++ ++typedef int (*dm_fsys_probe_hole_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_off_t off, ++ dm_size_t len, ++ dm_off_t __user *roffp, ++ dm_size_t __user *rlenp); ++ ++typedef int (*dm_fsys_punch_hole_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_off_t off, ++ dm_size_t len); ++ ++typedef int (*dm_fsys_read_invis_rvp_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp); ++ ++typedef int (*dm_fsys_release_right_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int type); ++ ++typedef int (*dm_fsys_remove_dmattr_t)( ++ struct inode *ip, ++ dm_right_t right, ++ int setdtime, ++ dm_attrname_t __user *attrnamep); ++ ++typedef int (*dm_fsys_request_right_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int type, /* DM_FSYS_OBJ or zero */ ++ u_int flags, ++ dm_right_t newright); ++ ++typedef int (*dm_fsys_set_dmattr_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_attrname_t __user *attrnamep, ++ int setdtime, ++ size_t buflen, ++ void __user *bufp); ++ ++typedef int (*dm_fsys_set_eventlist_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int type, ++ dm_eventset_t *eventsetp, /* in kernel space! */ ++ u_int maxevent); ++ ++typedef int (*dm_fsys_set_fileattr_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int mask, ++ dm_fileattr_t __user *attrp); ++ ++typedef int (*dm_fsys_set_inherit_t)( ++ struct inode *ip, ++ dm_right_t right, ++ dm_attrname_t __user *attrnamep, ++ mode_t mode); ++ ++typedef int (*dm_fsys_set_region_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int nelem, ++ dm_region_t __user *regbufp, ++ dm_boolean_t __user *exactflagp); ++ ++typedef int (*dm_fsys_symlink_by_handle_t)( ++ struct inode *ip, ++ dm_right_t right, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname, ++ char __user *path); ++ ++typedef int (*dm_fsys_sync_by_handle_t)( ++ struct inode *ip, ++ dm_right_t right); ++ ++typedef int (*dm_fsys_upgrade_right_t)( ++ struct inode *ip, ++ dm_right_t right, ++ u_int type); /* DM_FSYS_OBJ or zero */ ++ ++typedef int (*dm_fsys_write_invis_rvp_t)( ++ struct inode *ip, ++ dm_right_t right, ++ int flags, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp); ++ ++typedef void (*dm_fsys_obj_ref_hold_t)( ++ struct inode *ip); ++ ++ ++/* Structure definitions used by the VFS_DMAPI_FSYS_VECTOR call. */ ++ ++typedef struct { ++ dm_fsys_switch_t func_no; /* function number */ ++ union { ++ dm_fsys_clear_inherit_t clear_inherit; ++ dm_fsys_create_by_handle_t create_by_handle; ++ dm_fsys_downgrade_right_t downgrade_right; ++ dm_fsys_get_allocinfo_rvp_t get_allocinfo_rvp; ++ dm_fsys_get_bulkall_rvp_t get_bulkall_rvp; ++ dm_fsys_get_bulkattr_rvp_t get_bulkattr_rvp; ++ dm_fsys_get_config_t get_config; ++ dm_fsys_get_config_events_t get_config_events; ++ dm_fsys_get_destroy_dmattr_t get_destroy_dmattr; ++ dm_fsys_get_dioinfo_t get_dioinfo; ++ dm_fsys_get_dirattrs_rvp_t get_dirattrs_rvp; ++ dm_fsys_get_dmattr_t get_dmattr; ++ dm_fsys_get_eventlist_t get_eventlist; ++ dm_fsys_get_fileattr_t get_fileattr; ++ dm_fsys_get_region_t get_region; ++ dm_fsys_getall_dmattr_t getall_dmattr; ++ dm_fsys_getall_inherit_t getall_inherit; ++ dm_fsys_init_attrloc_t init_attrloc; ++ dm_fsys_mkdir_by_handle_t mkdir_by_handle; ++ dm_fsys_probe_hole_t probe_hole; ++ dm_fsys_punch_hole_t punch_hole; ++ dm_fsys_read_invis_rvp_t read_invis_rvp; ++ dm_fsys_release_right_t release_right; ++ dm_fsys_remove_dmattr_t remove_dmattr; ++ dm_fsys_request_right_t request_right; ++ dm_fsys_set_dmattr_t set_dmattr; ++ dm_fsys_set_eventlist_t set_eventlist; ++ dm_fsys_set_fileattr_t set_fileattr; ++ dm_fsys_set_inherit_t set_inherit; ++ dm_fsys_set_region_t set_region; ++ dm_fsys_symlink_by_handle_t symlink_by_handle; ++ dm_fsys_sync_by_handle_t sync_by_handle; ++ dm_fsys_upgrade_right_t upgrade_right; ++ dm_fsys_write_invis_rvp_t write_invis_rvp; ++ dm_fsys_obj_ref_hold_t obj_ref_hold; ++ } u_fc; ++} fsys_function_vector_t; ++ ++struct dm_fcntl_vector { ++ int code_level; ++ int count; /* Number of functions in the vector */ ++ fsys_function_vector_t *vecp; ++}; ++typedef struct dm_fcntl_vector dm_fcntl_vector_t; ++ ++struct dm_fcntl_mapevent { ++ size_t length; /* length of transfer */ ++ dm_eventtype_t max_event; /* Maximum (WRITE or READ) event */ ++ int error; /* returned error code */ ++}; ++typedef struct dm_fcntl_mapevent dm_fcntl_mapevent_t; ++ ++#endif /* __KERNEL__ */ ++ ++ ++/* The following definitions are needed both by the kernel and by the ++ library routines. ++*/ ++ ++#define DM_MAX_HANDLE_SIZE 56 /* maximum size for a file handle */ ++ ++ ++/* ++ * Opcodes for dmapi ioctl. ++ */ ++ ++#define DM_CLEAR_INHERIT 1 ++#define DM_CREATE_BY_HANDLE 2 ++#define DM_CREATE_SESSION 3 ++#define DM_CREATE_USEREVENT 4 ++#define DM_DESTROY_SESSION 5 ++#define DM_DOWNGRADE_RIGHT 6 ++#define DM_FD_TO_HANDLE 7 ++#define DM_FIND_EVENTMSG 8 ++#define DM_GET_ALLOCINFO 9 ++#define DM_GET_BULKALL 10 ++#define DM_GET_BULKATTR 11 ++#define DM_GET_CONFIG 12 ++#define DM_GET_CONFIG_EVENTS 13 ++#define DM_GET_DIOINFO 14 ++#define DM_GET_DIRATTRS 15 ++#define DM_GET_DMATTR 16 ++#define DM_GET_EVENTLIST 17 ++#define DM_GET_EVENTS 18 ++#define DM_GET_FILEATTR 19 ++#define DM_GET_MOUNTINFO 20 ++#define DM_GET_REGION 21 ++#define DM_GETALL_DISP 22 ++#define DM_GETALL_DMATTR 23 ++#define DM_GETALL_INHERIT 24 ++#define DM_GETALL_SESSIONS 25 ++#define DM_GETALL_TOKENS 26 ++#define DM_INIT_ATTRLOC 27 ++#define DM_MKDIR_BY_HANDLE 28 ++#define DM_MOVE_EVENT 29 ++#define DM_OBJ_REF_HOLD 30 ++#define DM_OBJ_REF_QUERY 31 ++#define DM_OBJ_REF_RELE 32 ++#define DM_PATH_TO_FSHANDLE 33 ++#define DM_PATH_TO_HANDLE 34 ++#define DM_PENDING 35 ++#define DM_PROBE_HOLE 36 ++#define DM_PUNCH_HOLE 37 ++#define DM_QUERY_RIGHT 38 ++#define DM_QUERY_SESSION 39 ++#define DM_READ_INVIS 40 ++#define DM_RELEASE_RIGHT 41 ++#define DM_REMOVE_DMATTR 42 ++#define DM_REQUEST_RIGHT 43 ++#define DM_RESPOND_EVENT 44 ++#define DM_SEND_MSG 45 ++#define DM_SET_DISP 46 ++#define DM_SET_DMATTR 47 ++#define DM_SET_EVENTLIST 48 ++#define DM_SET_FILEATTR 49 ++#define DM_SET_INHERIT 50 ++#define DM_SET_REGION 51 ++#define DM_SET_RETURN_ON_DESTROY 52 ++#define DM_SYMLINK_BY_HANDLE 53 ++#define DM_SYNC_BY_HANDLE 54 ++#define DM_UPGRADE_RIGHT 55 ++#define DM_WRITE_INVIS 56 ++#define DM_OPEN_BY_HANDLE 57 ++ ++#endif /* __DMAPI_KERN_H__ */ +--- /dev/null ++++ b/fs/dmapi/dmapi_mountinfo.c +@@ -0,0 +1,527 @@ ++/* ++ * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++static LIST_HEAD(dm_fsys_map); ++static spinlock_t dm_fsys_lock = SPIN_LOCK_UNLOCKED; ++ ++int ++dm_code_level(void) ++{ ++ return DM_CLVL_XOPEN; /* initial X/Open compliant release */ ++} ++ ++ ++/* Dummy routine which is stored in each function vector slot for which the ++ filesystem provides no function of its own. If an application calls the ++ function, he will just get ENOSYS. ++*/ ++ ++static int ++dm_enosys(void) ++{ ++ return -ENOSYS; /* function not supported by filesystem */ ++} ++ ++ ++/* dm_query_fsys_for_vector() asks a filesystem for its list of supported ++ DMAPI functions, and builds a dm_vector_map_t structure based upon the ++ reply. We ignore functions supported by the filesystem which we do not ++ know about, and we substitute the subroutine 'dm_enosys' for each function ++ we know about but the filesystem does not support. ++*/ ++ ++static void ++dm_query_fsys_for_vector( ++ dm_vector_map_t *map) ++{ ++ struct super_block *sb = map->sb; ++ fsys_function_vector_t *vecp; ++ dm_fcntl_vector_t vecrq; ++ dm_fsys_vector_t *vptr; ++ struct filesystem_dmapi_operations *dmapiops = map->dmapiops; ++ int error; ++ int i; ++ ++ ++ /* Allocate a function vector and initialize all fields with a ++ dummy function that returns ENOSYS. ++ */ ++ ++ vptr = map->vptr = kmem_cache_alloc(dm_fsys_vptr_cachep, GFP_KERNEL); ++ if (vptr == NULL) { ++ printk("%s/%d: kmem_cache_alloc(dm_fsys_vptr_cachep) returned NULL\n", __FUNCTION__, __LINE__); ++ return; ++ } ++ ++ vptr->code_level = 0; ++ vptr->clear_inherit = (dm_fsys_clear_inherit_t)dm_enosys; ++ vptr->create_by_handle = (dm_fsys_create_by_handle_t)dm_enosys; ++ vptr->downgrade_right = (dm_fsys_downgrade_right_t)dm_enosys; ++ vptr->get_allocinfo_rvp = (dm_fsys_get_allocinfo_rvp_t)dm_enosys; ++ vptr->get_bulkall_rvp = (dm_fsys_get_bulkall_rvp_t)dm_enosys; ++ vptr->get_bulkattr_rvp = (dm_fsys_get_bulkattr_rvp_t)dm_enosys; ++ vptr->get_config = (dm_fsys_get_config_t)dm_enosys; ++ vptr->get_config_events = (dm_fsys_get_config_events_t)dm_enosys; ++ vptr->get_destroy_dmattr = (dm_fsys_get_destroy_dmattr_t)dm_enosys; ++ vptr->get_dioinfo = (dm_fsys_get_dioinfo_t)dm_enosys; ++ vptr->get_dirattrs_rvp = (dm_fsys_get_dirattrs_rvp_t)dm_enosys; ++ vptr->get_dmattr = (dm_fsys_get_dmattr_t)dm_enosys; ++ vptr->get_eventlist = (dm_fsys_get_eventlist_t)dm_enosys; ++ vptr->get_fileattr = (dm_fsys_get_fileattr_t)dm_enosys; ++ vptr->get_region = (dm_fsys_get_region_t)dm_enosys; ++ vptr->getall_dmattr = (dm_fsys_getall_dmattr_t)dm_enosys; ++ vptr->getall_inherit = (dm_fsys_getall_inherit_t)dm_enosys; ++ vptr->init_attrloc = (dm_fsys_init_attrloc_t)dm_enosys; ++ vptr->mkdir_by_handle = (dm_fsys_mkdir_by_handle_t)dm_enosys; ++ vptr->probe_hole = (dm_fsys_probe_hole_t)dm_enosys; ++ vptr->punch_hole = (dm_fsys_punch_hole_t)dm_enosys; ++ vptr->read_invis_rvp = (dm_fsys_read_invis_rvp_t)dm_enosys; ++ vptr->release_right = (dm_fsys_release_right_t)dm_enosys; ++ vptr->request_right = (dm_fsys_request_right_t)dm_enosys; ++ vptr->remove_dmattr = (dm_fsys_remove_dmattr_t)dm_enosys; ++ vptr->set_dmattr = (dm_fsys_set_dmattr_t)dm_enosys; ++ vptr->set_eventlist = (dm_fsys_set_eventlist_t)dm_enosys; ++ vptr->set_fileattr = (dm_fsys_set_fileattr_t)dm_enosys; ++ vptr->set_inherit = (dm_fsys_set_inherit_t)dm_enosys; ++ vptr->set_region = (dm_fsys_set_region_t)dm_enosys; ++ vptr->symlink_by_handle = (dm_fsys_symlink_by_handle_t)dm_enosys; ++ vptr->sync_by_handle = (dm_fsys_sync_by_handle_t)dm_enosys; ++ vptr->upgrade_right = (dm_fsys_upgrade_right_t)dm_enosys; ++ vptr->write_invis_rvp = (dm_fsys_write_invis_rvp_t)dm_enosys; ++ vptr->obj_ref_hold = (dm_fsys_obj_ref_hold_t)dm_enosys; ++ ++ /* Issue a call to the filesystem in order to obtain ++ its vector of filesystem-specific DMAPI routines. ++ */ ++ ++ vecrq.count = 0; ++ vecrq.vecp = NULL; ++ ++ error = -ENOSYS; ++ ASSERT(dmapiops); ++ if (dmapiops->get_fsys_vector) ++ error = dmapiops->get_fsys_vector(sb, (caddr_t)&vecrq); ++ ++ /* If we still have an error at this point, then the filesystem simply ++ does not support DMAPI, so we give up with all functions set to ++ ENOSYS. ++ */ ++ ++ if (error || vecrq.count == 0) { ++ kmem_cache_free(dm_fsys_vptr_cachep, vptr); ++ map->vptr = NULL; ++ return; ++ } ++ ++ /* The request succeeded and we were given a vector which we need to ++ map to our current level. Overlay the dummy function with every ++ filesystem function we understand. ++ */ ++ ++ vptr->code_level = vecrq.code_level; ++ vecp = vecrq.vecp; ++ for (i = 0; i < vecrq.count; i++) { ++ switch (vecp[i].func_no) { ++ case DM_FSYS_CLEAR_INHERIT: ++ vptr->clear_inherit = vecp[i].u_fc.clear_inherit; ++ break; ++ case DM_FSYS_CREATE_BY_HANDLE: ++ vptr->create_by_handle = vecp[i].u_fc.create_by_handle; ++ break; ++ case DM_FSYS_DOWNGRADE_RIGHT: ++ vptr->downgrade_right = vecp[i].u_fc.downgrade_right; ++ break; ++ case DM_FSYS_GET_ALLOCINFO_RVP: ++ vptr->get_allocinfo_rvp = vecp[i].u_fc.get_allocinfo_rvp; ++ break; ++ case DM_FSYS_GET_BULKALL_RVP: ++ vptr->get_bulkall_rvp = vecp[i].u_fc.get_bulkall_rvp; ++ break; ++ case DM_FSYS_GET_BULKATTR_RVP: ++ vptr->get_bulkattr_rvp = vecp[i].u_fc.get_bulkattr_rvp; ++ break; ++ case DM_FSYS_GET_CONFIG: ++ vptr->get_config = vecp[i].u_fc.get_config; ++ break; ++ case DM_FSYS_GET_CONFIG_EVENTS: ++ vptr->get_config_events = vecp[i].u_fc.get_config_events; ++ break; ++ case DM_FSYS_GET_DESTROY_DMATTR: ++ vptr->get_destroy_dmattr = vecp[i].u_fc.get_destroy_dmattr; ++ break; ++ case DM_FSYS_GET_DIOINFO: ++ vptr->get_dioinfo = vecp[i].u_fc.get_dioinfo; ++ break; ++ case DM_FSYS_GET_DIRATTRS_RVP: ++ vptr->get_dirattrs_rvp = vecp[i].u_fc.get_dirattrs_rvp; ++ break; ++ case DM_FSYS_GET_DMATTR: ++ vptr->get_dmattr = vecp[i].u_fc.get_dmattr; ++ break; ++ case DM_FSYS_GET_EVENTLIST: ++ vptr->get_eventlist = vecp[i].u_fc.get_eventlist; ++ break; ++ case DM_FSYS_GET_FILEATTR: ++ vptr->get_fileattr = vecp[i].u_fc.get_fileattr; ++ break; ++ case DM_FSYS_GET_REGION: ++ vptr->get_region = vecp[i].u_fc.get_region; ++ break; ++ case DM_FSYS_GETALL_DMATTR: ++ vptr->getall_dmattr = vecp[i].u_fc.getall_dmattr; ++ break; ++ case DM_FSYS_GETALL_INHERIT: ++ vptr->getall_inherit = vecp[i].u_fc.getall_inherit; ++ break; ++ case DM_FSYS_INIT_ATTRLOC: ++ vptr->init_attrloc = vecp[i].u_fc.init_attrloc; ++ break; ++ case DM_FSYS_MKDIR_BY_HANDLE: ++ vptr->mkdir_by_handle = vecp[i].u_fc.mkdir_by_handle; ++ break; ++ case DM_FSYS_PROBE_HOLE: ++ vptr->probe_hole = vecp[i].u_fc.probe_hole; ++ break; ++ case DM_FSYS_PUNCH_HOLE: ++ vptr->punch_hole = vecp[i].u_fc.punch_hole; ++ break; ++ case DM_FSYS_READ_INVIS_RVP: ++ vptr->read_invis_rvp = vecp[i].u_fc.read_invis_rvp; ++ break; ++ case DM_FSYS_RELEASE_RIGHT: ++ vptr->release_right = vecp[i].u_fc.release_right; ++ break; ++ case DM_FSYS_REMOVE_DMATTR: ++ vptr->remove_dmattr = vecp[i].u_fc.remove_dmattr; ++ break; ++ case DM_FSYS_REQUEST_RIGHT: ++ vptr->request_right = vecp[i].u_fc.request_right; ++ break; ++ case DM_FSYS_SET_DMATTR: ++ vptr->set_dmattr = vecp[i].u_fc.set_dmattr; ++ break; ++ case DM_FSYS_SET_EVENTLIST: ++ vptr->set_eventlist = vecp[i].u_fc.set_eventlist; ++ break; ++ case DM_FSYS_SET_FILEATTR: ++ vptr->set_fileattr = vecp[i].u_fc.set_fileattr; ++ break; ++ case DM_FSYS_SET_INHERIT: ++ vptr->set_inherit = vecp[i].u_fc.set_inherit; ++ break; ++ case DM_FSYS_SET_REGION: ++ vptr->set_region = vecp[i].u_fc.set_region; ++ break; ++ case DM_FSYS_SYMLINK_BY_HANDLE: ++ vptr->symlink_by_handle = vecp[i].u_fc.symlink_by_handle; ++ break; ++ case DM_FSYS_SYNC_BY_HANDLE: ++ vptr->sync_by_handle = vecp[i].u_fc.sync_by_handle; ++ break; ++ case DM_FSYS_UPGRADE_RIGHT: ++ vptr->upgrade_right = vecp[i].u_fc.upgrade_right; ++ break; ++ case DM_FSYS_WRITE_INVIS_RVP: ++ vptr->write_invis_rvp = vecp[i].u_fc.write_invis_rvp; ++ break; ++ case DM_FSYS_OBJ_REF_HOLD: ++ vptr->obj_ref_hold = vecp[i].u_fc.obj_ref_hold; ++ break; ++ default: /* ignore ones we don't understand */ ++ break; ++ } ++ } ++} ++ ++ ++/* Must hold dm_fsys_lock. ++ * This returns the prototype for all instances of the fstype. ++ */ ++static dm_vector_map_t * ++dm_fsys_map_by_fstype( ++ struct file_system_type *fstype) ++{ ++ struct list_head *p; ++ dm_vector_map_t *proto = NULL; ++ dm_vector_map_t *m; ++ ++ ASSERT_ALWAYS(fstype); ++ list_for_each(p, &dm_fsys_map) { ++ m = list_entry(p, dm_vector_map_t, ftype_list); ++ if (m->f_type == fstype) { ++ proto = m; ++ break; ++ } ++ } ++ return proto; ++} ++ ++ ++/* Must hold dm_fsys_lock */ ++static dm_vector_map_t * ++dm_fsys_map_by_sb( ++ struct super_block *sb) ++{ ++ struct list_head *p; ++ dm_vector_map_t *proto; ++ dm_vector_map_t *m; ++ dm_vector_map_t *foundmap = NULL; ++ ++ proto = dm_fsys_map_by_fstype(sb->s_type); ++ if(proto == NULL) { ++ return NULL; ++ } ++ ++ list_for_each(p, &proto->sb_list) { ++ m = list_entry(p, dm_vector_map_t, sb_list); ++ if (m->sb == sb) { ++ foundmap = m; ++ break; ++ } ++ } ++ return foundmap; ++} ++ ++ ++#ifdef CONFIG_DMAPI_DEBUG ++static void ++sb_list( ++ struct super_block *sb) ++{ ++ struct list_head *p; ++ dm_vector_map_t *proto; ++ dm_vector_map_t *m; ++ ++ proto = dm_fsys_map_by_fstype(sb->s_type); ++ ASSERT(proto); ++ ++printk("%s/%d: Current sb_list\n", __FUNCTION__, __LINE__); ++ list_for_each(p, &proto->sb_list) { ++ m = list_entry(p, dm_vector_map_t, sb_list); ++printk("%s/%d: map 0x%p, sb 0x%p, vptr 0x%p, dmapiops 0x%p\n", __FUNCTION__, __LINE__, m, m->sb, m->vptr, m->dmapiops); ++ } ++printk("%s/%d: Done sb_list\n", __FUNCTION__, __LINE__); ++} ++#else ++#define sb_list(x) ++#endif ++ ++#ifdef CONFIG_DMAPI_DEBUG ++static void ++ftype_list(void) ++{ ++ struct list_head *p; ++ dm_vector_map_t *m; ++ ++printk("%s/%d: Current ftype_list\n", __FUNCTION__, __LINE__); ++ list_for_each(p, &dm_fsys_map) { ++ m = list_entry(p, dm_vector_map_t, ftype_list); ++ printk("%s/%d: FS 0x%p, ftype 0x%p %s\n", __FUNCTION__, __LINE__, m, m->f_type, m->f_type->name); ++ } ++printk("%s/%d: Done ftype_list\n", __FUNCTION__, __LINE__); ++} ++#else ++#define ftype_list() ++#endif ++ ++/* Ask for vptr for this filesystem instance. ++ * The caller knows this inode is on a dmapi-managed filesystem. ++ */ ++dm_fsys_vector_t * ++dm_fsys_vector( ++ struct inode *ip) ++{ ++ dm_vector_map_t *map; ++ ++ spin_lock(&dm_fsys_lock); ++ ftype_list(); ++ map = dm_fsys_map_by_sb(ip->i_sb); ++ spin_unlock(&dm_fsys_lock); ++ ASSERT(map); ++ ASSERT(map->vptr); ++ return map->vptr; ++} ++ ++ ++/* Ask for the dmapiops for this filesystem instance. The caller is ++ * also asking if this is a dmapi-managed filesystem. ++ */ ++struct filesystem_dmapi_operations * ++dm_fsys_ops( ++ struct super_block *sb) ++{ ++ dm_vector_map_t *proto = NULL; ++ dm_vector_map_t *map; ++ ++ spin_lock(&dm_fsys_lock); ++ ftype_list(); ++ sb_list(sb); ++ map = dm_fsys_map_by_sb(sb); ++ if (map == NULL) ++ proto = dm_fsys_map_by_fstype(sb->s_type); ++ spin_unlock(&dm_fsys_lock); ++ ++ if ((map == NULL) && (proto == NULL)) ++ return NULL; ++ ++ if (map == NULL) { ++ /* Find out if it's dmapi-managed */ ++ dm_vector_map_t *m; ++ ++ ASSERT(proto); ++ m = kmem_cache_alloc(dm_fsys_map_cachep, GFP_KERNEL); ++ if (m == NULL) { ++ printk("%s/%d: kmem_cache_alloc(dm_fsys_map_cachep) returned NULL\n", __FUNCTION__, __LINE__); ++ return NULL; ++ } ++ memset(m, 0, sizeof(*m)); ++ m->dmapiops = proto->dmapiops; ++ m->f_type = sb->s_type; ++ m->sb = sb; ++ INIT_LIST_HEAD(&m->sb_list); ++ INIT_LIST_HEAD(&m->ftype_list); ++ ++ dm_query_fsys_for_vector(m); ++ if (m->vptr == NULL) { ++ /* This isn't dmapi-managed */ ++ kmem_cache_free(dm_fsys_map_cachep, m); ++ return NULL; ++ } ++ ++ spin_lock(&dm_fsys_lock); ++ if ((map = dm_fsys_map_by_sb(sb)) == NULL) ++ list_add(&m->sb_list, &proto->sb_list); ++ spin_unlock(&dm_fsys_lock); ++ ++ if (map) { ++ kmem_cache_free(dm_fsys_vptr_cachep, m->vptr); ++ kmem_cache_free(dm_fsys_map_cachep, m); ++ } ++ else { ++ map = m; ++ } ++ } ++ ++ return map->dmapiops; ++} ++ ++ ++ ++/* Called when a filesystem instance is unregistered from dmapi */ ++void ++dm_fsys_ops_release( ++ struct super_block *sb) ++{ ++ dm_vector_map_t *map; ++ ++ spin_lock(&dm_fsys_lock); ++ ASSERT(!list_empty(&dm_fsys_map)); ++ map = dm_fsys_map_by_sb(sb); ++ ASSERT(map); ++ list_del(&map->sb_list); ++ spin_unlock(&dm_fsys_lock); ++ ++ ASSERT(map->vptr); ++ kmem_cache_free(dm_fsys_vptr_cachep, map->vptr); ++ kmem_cache_free(dm_fsys_map_cachep, map); ++} ++ ++ ++/* Called by a filesystem module that is loading into the kernel. ++ * This creates a new dm_vector_map_t which serves as the prototype ++ * for instances of this fstype and also provides the list_head ++ * for instances of this fstype. The prototypes are the only ones ++ * on the fstype_list, and will never be on the sb_list. ++ */ ++void ++dmapi_register( ++ struct file_system_type *fstype, ++ struct filesystem_dmapi_operations *dmapiops) ++{ ++ dm_vector_map_t *proto; ++ ++ proto = kmem_cache_alloc(dm_fsys_map_cachep, GFP_KERNEL); ++ if (proto == NULL) { ++ printk("%s/%d: kmem_cache_alloc(dm_fsys_map_cachep) returned NULL\n", __FUNCTION__, __LINE__); ++ return; ++ } ++ memset(proto, 0, sizeof(*proto)); ++ proto->dmapiops = dmapiops; ++ proto->f_type = fstype; ++ INIT_LIST_HEAD(&proto->sb_list); ++ INIT_LIST_HEAD(&proto->ftype_list); ++ ++ spin_lock(&dm_fsys_lock); ++ ASSERT(dm_fsys_map_by_fstype(fstype) == NULL); ++ list_add(&proto->ftype_list, &dm_fsys_map); ++ ftype_list(); ++ spin_unlock(&dm_fsys_lock); ++} ++ ++/* Called by a filesystem module that is unloading from the kernel */ ++void ++dmapi_unregister( ++ struct file_system_type *fstype) ++{ ++ struct list_head *p; ++ dm_vector_map_t *proto; ++ dm_vector_map_t *m; ++ ++ spin_lock(&dm_fsys_lock); ++ ASSERT(!list_empty(&dm_fsys_map)); ++ proto = dm_fsys_map_by_fstype(fstype); ++ ASSERT(proto); ++ list_del(&proto->ftype_list); ++ spin_unlock(&dm_fsys_lock); ++ ++ p = &proto->sb_list; ++ while (!list_empty(p)) { ++ m = list_entry(p->next, dm_vector_map_t, sb_list); ++ list_del(&m->sb_list); ++ ASSERT(m->vptr); ++ kmem_cache_free(dm_fsys_vptr_cachep, m->vptr); ++ kmem_cache_free(dm_fsys_map_cachep, m); ++ } ++ kmem_cache_free(dm_fsys_map_cachep, proto); ++} ++ ++ ++int ++dmapi_registered( ++ struct file_system_type *fstype, ++ struct filesystem_dmapi_operations **dmapiops) ++{ ++ return 0; ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_port.h +@@ -0,0 +1,138 @@ ++/* ++ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#ifndef _DMAPI_PORT_H ++#define _DMAPI_PORT_H ++ ++#include ++#include "sv.h" ++ ++#include /* preempt needs this */ ++#include ++ ++typedef spinlock_t lock_t; ++ ++#define spinlock_init(lock, name) spin_lock_init(lock) ++#define spinlock_destroy(lock) ++ ++#define mutex_spinlock(lock) ({ spin_lock(lock); 0; }) ++#define mutex_spinunlock(lock, s) spin_unlock(lock) ++#define nested_spinlock(lock) spin_lock(lock) ++#define nested_spinunlock(lock) spin_unlock(lock) ++ ++typedef signed int __int32_t; ++typedef unsigned int __uint32_t; ++typedef signed long long int __int64_t; ++typedef unsigned long long int __uint64_t; ++ ++ ++/* __psint_t is the same size as a pointer */ ++#if (BITS_PER_LONG == 32) ++typedef __int32_t __psint_t; ++typedef __uint32_t __psunsigned_t; ++#elif (BITS_PER_LONG == 64) ++typedef __int64_t __psint_t; ++typedef __uint64_t __psunsigned_t; ++#else ++#error BITS_PER_LONG must be 32 or 64 ++#endif ++ ++static inline void ++assfail(char *a, char *f, int l) ++{ ++ printk("DMAPI assertion failed: %s, file: %s, line: %d\n", a, f, l); ++ BUG(); ++} ++ ++#ifdef DEBUG ++#define doass 1 ++# ifdef lint ++# define ASSERT(EX) ((void)0) /* avoid "constant in conditional" babble */ ++# else ++# define ASSERT(EX) ((!doass||(EX))?((void)0):assfail(#EX, __FILE__, __LINE__)) ++# endif /* lint */ ++#else ++# define ASSERT(x) ((void)0) ++#endif /* DEBUG */ ++ ++#define ASSERT_ALWAYS(EX) ((EX)?((void)0):assfail(#EX, __FILE__, __LINE__)) ++ ++ ++#if defined __i386__ ++ ++/* Side effect free 64 bit mod operation */ ++static inline __u32 dmapi_do_mod(void *a, __u32 b, int n) ++{ ++ switch (n) { ++ case 4: ++ return *(__u32 *)a % b; ++ case 8: ++ { ++ unsigned long __upper, __low, __high, __mod; ++ __u64 c = *(__u64 *)a; ++ __upper = __high = c >> 32; ++ __low = c; ++ if (__high) { ++ __upper = __high % (b); ++ __high = __high / (b); ++ } ++ asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (b), "0" (__low), "1" (__upper)); ++ asm("":"=A" (c):"a" (__low),"d" (__high)); ++ return __mod; ++ } ++ } ++ ++ /* NOTREACHED */ ++ return 0; ++} ++#else ++ ++/* Side effect free 64 bit mod operation */ ++static inline __u32 dmapi_do_mod(void *a, __u32 b, int n) ++{ ++ switch (n) { ++ case 4: ++ return *(__u32 *)a % b; ++ case 8: ++ { ++ __u64 c = *(__u64 *)a; ++ return do_div(c, b); ++ } ++ } ++ ++ /* NOTREACHED */ ++ return 0; ++} ++#endif ++ ++#define do_mod(a, b) dmapi_do_mod(&(a), (b), sizeof(a)) ++ ++#endif /* _DMAPI_PORT_H */ +--- /dev/null ++++ b/fs/dmapi/dmapi_private.h +@@ -0,0 +1,619 @@ ++/* ++ * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#ifndef _DMAPI_PRIVATE_H ++#define _DMAPI_PRIVATE_H ++ ++#include ++#include "dmapi_port.h" ++#include "sv.h" ++ ++#ifdef CONFIG_PROC_FS ++#define DMAPI_PROCFS "orig/fs/dmapi_v2" /* DMAPI device in /proc. */ ++#define DMAPI_DBG_PROCFS "orig/fs/dmapi_d" /* DMAPI debugging dir */ ++#endif ++ ++extern struct kmem_cache *dm_fsreg_cachep; ++extern struct kmem_cache *dm_tokdata_cachep; ++extern struct kmem_cache *dm_session_cachep; ++extern struct kmem_cache *dm_fsys_map_cachep; ++extern struct kmem_cache *dm_fsys_vptr_cachep; ++ ++typedef struct dm_tokdata { ++ struct dm_tokdata *td_next; ++ struct dm_tokevent *td_tevp; /* pointer to owning tevp */ ++ int td_app_ref; /* # app threads currently active */ ++ dm_right_t td_orig_right; /* original right held when created */ ++ dm_right_t td_right; /* current right held for this handle */ ++ short td_flags; ++ short td_type; /* object type */ ++ int td_vcount; /* # of current application VN_HOLDs */ ++ struct inode *td_ip; /* inode pointer */ ++ dm_handle_t td_handle; /* handle for ip or sb */ ++} dm_tokdata_t; ++ ++/* values for td_type */ ++ ++#define DM_TDT_NONE 0x00 /* td_handle is empty */ ++#define DM_TDT_VFS 0x01 /* td_handle points to a sb */ ++#define DM_TDT_REG 0x02 /* td_handle points to a file */ ++#define DM_TDT_DIR 0x04 /* td_handle points to a directory */ ++#define DM_TDT_LNK 0x08 /* td_handle points to a symlink */ ++#define DM_TDT_OTH 0x10 /* some other object eg. pipe, socket */ ++ ++#define DM_TDT_VNO (DM_TDT_REG|DM_TDT_DIR|DM_TDT_LNK|DM_TDT_OTH) ++#define DM_TDT_ANY (DM_TDT_VFS|DM_TDT_REG|DM_TDT_DIR|DM_TDT_LNK|DM_TDT_OTH) ++ ++/* values for td_flags */ ++ ++#define DM_TDF_ORIG 0x0001 /* part of the original event */ ++#define DM_TDF_EVTREF 0x0002 /* event thread holds inode reference */ ++#define DM_TDF_STHREAD 0x0004 /* only one app can use this handle */ ++#define DM_TDF_RIGHT 0x0008 /* vcount bumped for dm_request_right */ ++#define DM_TDF_HOLD 0x0010 /* vcount bumped for dm_obj_ref_hold */ ++ ++ ++/* Because some events contain __u64 fields, we force te_msg and te_event ++ to always be 8-byte aligned. In order to send more than one message in ++ a single dm_get_events() call, we also ensure that each message is an ++ 8-byte multiple. ++*/ ++ ++typedef struct dm_tokevent { ++ struct dm_tokevent *te_next; ++ struct dm_tokevent *te_hashnext; /* hash chain */ ++ lock_t te_lock; /* lock for all fields but te_*next. ++ * te_next and te_hashnext are ++ * protected by the session lock. ++ */ ++ short te_flags; ++ short te_allocsize; /* alloc'ed size of this structure */ ++ sv_t te_evt_queue; /* queue waiting for dm_respond_event */ ++ sv_t te_app_queue; /* queue waiting for handle access */ ++ int te_evt_ref; /* number of event procs using token */ ++ int te_app_ref; /* number of app procs using token */ ++ int te_app_slp; /* number of app procs sleeping */ ++ int te_reply; /* return errno for sync messages */ ++ dm_tokdata_t *te_tdp; /* list of handle/right pairs */ ++ union { ++ __u64 align; /* force alignment of te_msg */ ++ dm_eventmsg_t te_msg; /* user visible part */ ++ } te_u; ++ __u64 te_event; /* start of dm_xxx_event_t message */ ++} dm_tokevent_t; ++ ++#define te_msg te_u.te_msg ++ ++/* values for te_flags */ ++ ++#define DM_TEF_LOCKED 0x0001 /* event "locked" by dm_get_events() */ ++#define DM_TEF_INTERMED 0x0002 /* a dm_pending reply was received */ ++#define DM_TEF_FINAL 0x0004 /* dm_respond_event has been received */ ++#define DM_TEF_HASHED 0x0010 /* event is on hash chain */ ++#define DM_TEF_FLUSH 0x0020 /* flushing threads from queues */ ++ ++ ++#ifdef CONFIG_DMAPI_DEBUG ++#define DM_SHASH_DEBUG ++#endif ++ ++typedef struct dm_sesshash { ++ dm_tokevent_t *h_next; /* ptr to chain of tokevents */ ++#ifdef DM_SHASH_DEBUG ++ int maxlength; ++ int curlength; ++ int num_adds; ++ int num_dels; ++ int dup_hits; ++#endif ++} dm_sesshash_t; ++ ++ ++typedef struct dm_eventq { ++ dm_tokevent_t *eq_head; ++ dm_tokevent_t *eq_tail; ++ int eq_count; /* size of queue */ ++} dm_eventq_t; ++ ++ ++typedef struct dm_session { ++ struct dm_session *sn_next; /* sessions linkage */ ++ dm_sessid_t sn_sessid; /* user-visible session number */ ++ u_int sn_flags; ++ lock_t sn_qlock; /* lock for newq/delq related fields */ ++ sv_t sn_readerq; /* waiting for message on sn_newq */ ++ sv_t sn_writerq; /* waiting for room on sn_newq */ ++ u_int sn_readercnt; /* count of waiting readers */ ++ u_int sn_writercnt; /* count of waiting readers */ ++ dm_eventq_t sn_newq; /* undelivered event queue */ ++ dm_eventq_t sn_delq; /* delivered event queue */ ++ dm_eventq_t sn_evt_writerq; /* events of thrds in sn_writerq */ ++ dm_sesshash_t *sn_sesshash; /* buckets for tokevent hash chains */ ++#ifdef DM_SHASH_DEBUG ++ int sn_buckets_in_use; ++ int sn_max_buckets_in_use; ++#endif ++ char sn_info[DM_SESSION_INFO_LEN]; /* user-supplied info */ ++} dm_session_t; ++ ++/* values for sn_flags */ ++ ++#define DM_SN_WANTMOUNT 0x0001 /* session wants to get mount events */ ++ ++ ++typedef enum { ++ DM_STATE_MOUNTING, ++ DM_STATE_MOUNTED, ++ DM_STATE_UNMOUNTING, ++ DM_STATE_UNMOUNTED ++} dm_fsstate_t; ++ ++ ++typedef struct dm_fsreg { ++ struct dm_fsreg *fr_next; ++ struct super_block *fr_sb; /* filesystem pointer */ ++ dm_tokevent_t *fr_tevp; ++ dm_fsid_t fr_fsid; /* filesystem ID */ ++ void *fr_msg; /* dm_mount_event_t for filesystem */ ++ int fr_msgsize; /* size of dm_mount_event_t */ ++ dm_fsstate_t fr_state; ++ sv_t fr_dispq; ++ int fr_dispcnt; ++ dm_eventq_t fr_evt_dispq; /* events of thrds in fr_dispq */ ++ sv_t fr_queue; /* queue for hdlcnt/sbcnt/unmount */ ++ lock_t fr_lock; ++ int fr_hdlcnt; /* threads blocked during unmount */ ++ int fr_vfscnt; /* threads in VFS_VGET or VFS_ROOT */ ++ int fr_unmount; /* if non-zero, umount is sleeping */ ++ dm_attrname_t fr_rattr; /* dm_set_return_on_destroy attribute */ ++ dm_session_t *fr_sessp [DM_EVENT_MAX]; ++} dm_fsreg_t; ++ ++ ++ ++ ++/* events valid in dm_set_disp() when called with a filesystem handle. */ ++ ++#define DM_VALID_DISP_EVENTS ( \ ++ (1 << DM_EVENT_PREUNMOUNT) | \ ++ (1 << DM_EVENT_UNMOUNT) | \ ++ (1 << DM_EVENT_NOSPACE) | \ ++ (1 << DM_EVENT_DEBUT) | \ ++ (1 << DM_EVENT_CREATE) | \ ++ (1 << DM_EVENT_POSTCREATE) | \ ++ (1 << DM_EVENT_REMOVE) | \ ++ (1 << DM_EVENT_POSTREMOVE) | \ ++ (1 << DM_EVENT_RENAME) | \ ++ (1 << DM_EVENT_POSTRENAME) | \ ++ (1 << DM_EVENT_LINK) | \ ++ (1 << DM_EVENT_POSTLINK) | \ ++ (1 << DM_EVENT_SYMLINK) | \ ++ (1 << DM_EVENT_POSTSYMLINK) | \ ++ (1 << DM_EVENT_READ) | \ ++ (1 << DM_EVENT_WRITE) | \ ++ (1 << DM_EVENT_TRUNCATE) | \ ++ (1 << DM_EVENT_ATTRIBUTE) | \ ++ (1 << DM_EVENT_DESTROY) ) ++ ++ ++/* isolate the read/write/trunc events of a dm_tokevent_t */ ++ ++#define DM_EVENT_RDWRTRUNC(tevp) ( \ ++ ((tevp)->te_msg.ev_type == DM_EVENT_READ) || \ ++ ((tevp)->te_msg.ev_type == DM_EVENT_WRITE) || \ ++ ((tevp)->te_msg.ev_type == DM_EVENT_TRUNCATE) ) ++ ++ ++/* ++ * Global handle hack isolation. ++ */ ++ ++#define DM_GLOBALHAN(hanp, hlen) (((hanp) == DM_GLOBAL_HANP) && \ ++ ((hlen) == DM_GLOBAL_HLEN)) ++ ++ ++#define DM_MAX_MSG_DATA 3960 ++ ++ ++ ++/* Supported filesystem function vector functions. */ ++ ++ ++typedef struct { ++ int code_level; ++ dm_fsys_clear_inherit_t clear_inherit; ++ dm_fsys_create_by_handle_t create_by_handle; ++ dm_fsys_downgrade_right_t downgrade_right; ++ dm_fsys_get_allocinfo_rvp_t get_allocinfo_rvp; ++ dm_fsys_get_bulkall_rvp_t get_bulkall_rvp; ++ dm_fsys_get_bulkattr_rvp_t get_bulkattr_rvp; ++ dm_fsys_get_config_t get_config; ++ dm_fsys_get_config_events_t get_config_events; ++ dm_fsys_get_destroy_dmattr_t get_destroy_dmattr; ++ dm_fsys_get_dioinfo_t get_dioinfo; ++ dm_fsys_get_dirattrs_rvp_t get_dirattrs_rvp; ++ dm_fsys_get_dmattr_t get_dmattr; ++ dm_fsys_get_eventlist_t get_eventlist; ++ dm_fsys_get_fileattr_t get_fileattr; ++ dm_fsys_get_region_t get_region; ++ dm_fsys_getall_dmattr_t getall_dmattr; ++ dm_fsys_getall_inherit_t getall_inherit; ++ dm_fsys_init_attrloc_t init_attrloc; ++ dm_fsys_mkdir_by_handle_t mkdir_by_handle; ++ dm_fsys_probe_hole_t probe_hole; ++ dm_fsys_punch_hole_t punch_hole; ++ dm_fsys_read_invis_rvp_t read_invis_rvp; ++ dm_fsys_release_right_t release_right; ++ dm_fsys_remove_dmattr_t remove_dmattr; ++ dm_fsys_request_right_t request_right; ++ dm_fsys_set_dmattr_t set_dmattr; ++ dm_fsys_set_eventlist_t set_eventlist; ++ dm_fsys_set_fileattr_t set_fileattr; ++ dm_fsys_set_inherit_t set_inherit; ++ dm_fsys_set_region_t set_region; ++ dm_fsys_symlink_by_handle_t symlink_by_handle; ++ dm_fsys_sync_by_handle_t sync_by_handle; ++ dm_fsys_upgrade_right_t upgrade_right; ++ dm_fsys_write_invis_rvp_t write_invis_rvp; ++ dm_fsys_obj_ref_hold_t obj_ref_hold; ++} dm_fsys_vector_t; ++ ++ ++typedef struct { ++ struct list_head ftype_list; /* list of fstypes */ ++ struct list_head sb_list; /* list of sb's per fstype */ ++ struct file_system_type *f_type; ++ struct filesystem_dmapi_operations *dmapiops; ++ dm_fsys_vector_t *vptr; ++ struct super_block *sb; ++} dm_vector_map_t; ++ ++ ++extern dm_session_t *dm_sessions; /* head of session list */ ++extern dm_fsreg_t *dm_registers; ++extern lock_t dm_reg_lock; /* lock for registration list */ ++ ++/* ++ * Kernel only prototypes. ++ */ ++ ++int dm_find_session_and_lock( ++ dm_sessid_t sid, ++ dm_session_t **sessionpp, ++ unsigned long *lcp); ++ ++int dm_find_msg_and_lock( ++ dm_sessid_t sid, ++ dm_token_t token, ++ dm_tokevent_t **tevpp, ++ unsigned long *lcp); ++ ++dm_tokevent_t * dm_evt_create_tevp( ++ dm_eventtype_t event, ++ int variable_size, ++ void **msgpp); ++ ++int dm_app_get_tdp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ short types, ++ dm_right_t right, ++ dm_tokdata_t **tdpp); ++ ++int dm_get_config_tdp( ++ void __user *hanp, ++ size_t hlen, ++ dm_tokdata_t **tdpp); ++ ++void dm_app_put_tdp( ++ dm_tokdata_t *tdp); ++ ++void dm_put_tevp( ++ dm_tokevent_t *tevp, ++ dm_tokdata_t *tdp); ++ ++void dm_evt_rele_tevp( ++ dm_tokevent_t *tevp, ++ int droprights); ++ ++int dm_enqueue_normal_event( ++ struct super_block *sbp, ++ dm_tokevent_t **tevpp, ++ int flags); ++ ++int dm_enqueue_mount_event( ++ struct super_block *sbp, ++ dm_tokevent_t *tevp); ++ ++int dm_enqueue_sendmsg_event( ++ dm_sessid_t targetsid, ++ dm_tokevent_t *tevp, ++ int synch); ++ ++int dm_enqueue_user_event( ++ dm_sessid_t sid, ++ dm_tokevent_t *tevp, ++ dm_token_t *tokenp); ++ ++int dm_obj_ref_query_rvp( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen, ++ int *rvp); ++ ++int dm_read_invis_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp); ++ ++int dm_write_invis_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ int flags, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp); ++ ++int dm_get_bulkattr_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int mask, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvp); ++ ++int dm_get_bulkall_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int mask, ++ dm_attrname_t __user *attrnamep, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvp); ++ ++int dm_get_dirattrs_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int mask, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvp); ++ ++int dm_get_allocinfo_rvp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_off_t __user *offp, ++ u_int nelem, ++ dm_extent_t __user *extentp, ++ u_int __user *nelemp, ++ int *rvp); ++ ++int dm_waitfor_destroy_attrname( ++ struct super_block *sb, ++ dm_attrname_t *attrnamep); ++ ++void dm_clear_fsreg( ++ dm_session_t *s); ++ ++int dm_add_fsys_entry( ++ struct super_block *sb, ++ dm_tokevent_t *tevp); ++ ++void dm_change_fsys_entry( ++ struct super_block *sb, ++ dm_fsstate_t newstate); ++ ++void dm_remove_fsys_entry( ++ struct super_block *sb); ++ ++dm_fsys_vector_t *dm_fsys_vector( ++ struct inode *ip); ++ ++struct filesystem_dmapi_operations *dm_fsys_ops( ++ struct super_block *sb); ++ ++void dm_fsys_ops_release( ++ struct super_block *sb); ++ ++int dm_waitfor_disp_session( ++ struct super_block *sb, ++ dm_tokevent_t *tevp, ++ dm_session_t **sessionpp, ++ unsigned long *lcp); ++ ++struct inode * dm_handle_to_ip ( ++ dm_handle_t *handlep, ++ short *typep); ++ ++int dm_check_dmapi_ip( ++ struct inode *ip); ++ ++dm_tokevent_t * dm_find_mount_tevp_and_lock( ++ dm_fsid_t *fsidp, ++ unsigned long *lcp); ++ ++int dm_path_to_hdl( ++ char __user *path, ++ void __user *hanp, ++ size_t __user *hlenp); ++ ++int dm_path_to_fshdl( ++ char __user *path, ++ void __user *hanp, ++ size_t __user *hlenp); ++ ++int dm_fd_to_hdl( ++ int fd, ++ void __user *hanp, ++ size_t __user *hlenp); ++ ++int dm_upgrade_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token); ++ ++int dm_downgrade_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token); ++ ++int dm_request_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int flags, ++ dm_right_t right); ++ ++int dm_release_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token); ++ ++int dm_query_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_right_t __user *rightp); ++ ++ ++int dm_set_eventlist( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_eventset_t __user *eventsetp, ++ u_int maxevent); ++ ++int dm_obj_ref_hold( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen); ++ ++int dm_obj_ref_rele( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen); ++ ++int dm_get_eventlist( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int nelem, ++ dm_eventset_t __user *eventsetp, ++ u_int __user *nelemp); ++ ++ ++int dm_set_disp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_eventset_t __user *eventsetp, ++ u_int maxevent); ++ ++ ++int dm_set_return_on_destroy( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ dm_boolean_t enable); ++ ++ ++int dm_get_mountinfo( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp); ++ ++void dm_link_event( ++ dm_tokevent_t *tevp, ++ dm_eventq_t *queue); ++ ++void dm_unlink_event( ++ dm_tokevent_t *tevp, ++ dm_eventq_t *queue); ++ ++int dm_open_by_handle_rvp( ++ unsigned int fd, ++ void __user *hanp, ++ size_t hlen, ++ int mode, ++ int *rvp); ++ ++int dm_copyin_handle( ++ void __user *hanp, ++ size_t hlen, ++ dm_handle_t *handlep); ++ ++int dm_release_disp_threads( ++ dm_fsid_t *fsid, ++ struct inode *inode, ++ int errno); ++ ++#endif /* _DMAPI_PRIVATE_H */ +--- /dev/null ++++ b/fs/dmapi/dmapi_region.c +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++ ++int ++dm_get_region( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int nelem, ++ dm_region_t __user *regbufp, ++ u_int __user *nelemp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_region(tdp->td_ip, tdp->td_right, ++ nelem, regbufp, nelemp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++ ++int ++dm_set_region( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int nelem, ++ dm_region_t __user *regbufp, ++ dm_boolean_t __user *exactflagp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->set_region(tdp->td_ip, tdp->td_right, ++ nelem, regbufp, exactflagp); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_register.c +@@ -0,0 +1,1638 @@ ++/* ++ * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++/* LOOKUP_POSTIVE was removed in Linux 2.6 */ ++#ifndef LOOKUP_POSITIVE ++#define LOOKUP_POSITIVE 0 ++#endif ++ ++dm_fsreg_t *dm_registers; /* head of filesystem registration list */ ++int dm_fsys_cnt; /* number of filesystems on dm_registers list */ ++lock_t dm_reg_lock = SPIN_LOCK_UNLOCKED;/* lock for dm_registers */ ++ ++ ++ ++#ifdef CONFIG_PROC_FS ++static int ++fsreg_read_pfs(char *buffer, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int len; ++ int i; ++ dm_fsreg_t *fsrp = (dm_fsreg_t*)data; ++ char statebuf[30]; ++ ++#define CHKFULL if(len >= count) break; ++#define ADDBUF(a,b) len += sprintf(buffer + len, a, b); CHKFULL; ++ ++ switch (fsrp->fr_state) { ++ case DM_STATE_MOUNTING: sprintf(statebuf, "mounting"); break; ++ case DM_STATE_MOUNTED: sprintf(statebuf, "mounted"); break; ++ case DM_STATE_UNMOUNTING: sprintf(statebuf, "unmounting"); break; ++ case DM_STATE_UNMOUNTED: sprintf(statebuf, "unmounted"); break; ++ default: ++ sprintf(statebuf, "unknown:%d", (int)fsrp->fr_state); ++ break; ++ } ++ ++ len=0; ++ while(1){ ++ ADDBUF("fsrp=0x%p\n", fsrp); ++ ADDBUF("fr_next=0x%p\n", fsrp->fr_next); ++ ADDBUF("fr_sb=0x%p\n", fsrp->fr_sb); ++ ADDBUF("fr_tevp=0x%p\n", fsrp->fr_tevp); ++ ADDBUF("fr_fsid=%c\n", '?'); ++ ADDBUF("fr_msg=0x%p\n", fsrp->fr_msg); ++ ADDBUF("fr_msgsize=%d\n", fsrp->fr_msgsize); ++ ADDBUF("fr_state=%s\n", statebuf); ++ ADDBUF("fr_dispq=%c\n", '?'); ++ ADDBUF("fr_dispcnt=%d\n", fsrp->fr_dispcnt); ++ ++ ADDBUF("fr_evt_dispq.eq_head=0x%p\n", fsrp->fr_evt_dispq.eq_head); ++ ADDBUF("fr_evt_dispq.eq_tail=0x%p\n", fsrp->fr_evt_dispq.eq_tail); ++ ADDBUF("fr_evt_dispq.eq_count=%d\n", fsrp->fr_evt_dispq.eq_count); ++ ++ ADDBUF("fr_queue=%c\n", '?'); ++ ADDBUF("fr_lock=%c\n", '?'); ++ ADDBUF("fr_hdlcnt=%d\n", fsrp->fr_hdlcnt); ++ ADDBUF("fr_vfscnt=%d\n", fsrp->fr_vfscnt); ++ ADDBUF("fr_unmount=%d\n", fsrp->fr_unmount); ++ ++ len += sprintf(buffer + len, "fr_rattr="); ++ CHKFULL; ++ for(i = 0; i <= DM_ATTR_NAME_SIZE; ++i){ ++ ADDBUF("%c", fsrp->fr_rattr.an_chars[i]); ++ } ++ CHKFULL; ++ len += sprintf(buffer + len, "\n"); ++ CHKFULL; ++ ++ for(i = 0; i < DM_EVENT_MAX; i++){ ++ if( fsrp->fr_sessp[i] != NULL ){ ++ ADDBUF("fr_sessp[%d]=", i); ++ ADDBUF("0x%p\n", fsrp->fr_sessp[i]); ++ } ++ } ++ CHKFULL; ++ ++ break; ++ } ++ ++ if (offset >= len) { ++ *start = buffer; ++ *eof = 1; ++ return 0; ++ } ++ *start = buffer + offset; ++ if ((len -= offset) > count) ++ return count; ++ *eof = 1; ++ ++ return len; ++} ++#endif ++ ++ ++/* Returns a pointer to the filesystem structure for the filesystem ++ referenced by fsidp. The caller is responsible for obtaining dm_reg_lock ++ before calling this routine. ++*/ ++ ++static dm_fsreg_t * ++dm_find_fsreg( ++ dm_fsid_t *fsidp) ++{ ++ dm_fsreg_t *fsrp; ++ ++ for (fsrp = dm_registers; fsrp; fsrp = fsrp->fr_next) { ++ if (!memcmp(&fsrp->fr_fsid, fsidp, sizeof(*fsidp))) ++ break; ++ } ++ return(fsrp); ++} ++ ++ ++/* Given a fsid_t, dm_find_fsreg_and_lock() finds the dm_fsreg_t structure ++ for that filesytem if one exists, and returns a pointer to the structure ++ after obtaining its 'fr_lock' so that the caller can safely modify the ++ dm_fsreg_t. The caller is responsible for releasing 'fr_lock'. ++*/ ++ ++static dm_fsreg_t * ++dm_find_fsreg_and_lock( ++ dm_fsid_t *fsidp, ++ unsigned long *lcp) /* address of returned lock cookie */ ++{ ++ dm_fsreg_t *fsrp; ++ ++ for (;;) { ++ *lcp = mutex_spinlock(&dm_reg_lock); ++ ++ if ((fsrp = dm_find_fsreg(fsidp)) == NULL) { ++ mutex_spinunlock(&dm_reg_lock, *lcp); ++ return(NULL); ++ } ++ if (spin_trylock(&fsrp->fr_lock)) { ++ nested_spinunlock(&dm_reg_lock); ++ return(fsrp); /* success */ ++ } ++ ++ /* If the second lock is not available, drop the first and ++ start over. This gives the CPU a chance to process any ++ interrupts, and also allows processes which want a fr_lock ++ for a different filesystem to proceed. ++ */ ++ ++ mutex_spinunlock(&dm_reg_lock, *lcp); ++ } ++} ++ ++ ++/* dm_add_fsys_entry() is called when a DM_EVENT_MOUNT event is about to be ++ sent. It creates a dm_fsreg_t structure for the filesystem and stores a ++ pointer to a copy of the mount event within that structure so that it is ++ available for subsequent dm_get_mountinfo() calls. ++*/ ++ ++int ++dm_add_fsys_entry( ++ struct super_block *sb, ++ dm_tokevent_t *tevp) ++{ ++ dm_fsreg_t *fsrp; ++ int msgsize; ++ void *msg; ++ unsigned long lc; /* lock cookie */ ++ dm_fsid_t fsid; ++ struct filesystem_dmapi_operations *dops; ++ ++ dops = dm_fsys_ops(sb); ++ ASSERT(dops); ++ dops->get_fsid(sb, &fsid); ++ ++ /* Allocate and initialize a dm_fsreg_t structure for the filesystem. */ ++ ++ msgsize = tevp->te_allocsize - offsetof(dm_tokevent_t, te_event); ++ msg = kmalloc(msgsize, GFP_KERNEL); ++ if (msg == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return -ENOMEM; ++ } ++ memcpy(msg, &tevp->te_event, msgsize); ++ ++ fsrp = kmem_cache_alloc(dm_fsreg_cachep, GFP_KERNEL); ++ if (fsrp == NULL) { ++ kfree(msg); ++ printk("%s/%d: kmem_cache_alloc(dm_fsreg_cachep) returned NULL\n", __FUNCTION__, __LINE__); ++ return -ENOMEM; ++ } ++ memset(fsrp, 0, sizeof(*fsrp)); ++ ++ fsrp->fr_sb = sb; ++ fsrp->fr_tevp = tevp; ++ memcpy(&fsrp->fr_fsid, &fsid, sizeof(fsid)); ++ fsrp->fr_msg = msg; ++ fsrp->fr_msgsize = msgsize; ++ fsrp->fr_state = DM_STATE_MOUNTING; ++ sv_init(&fsrp->fr_dispq, SV_DEFAULT, "fr_dispq"); ++ sv_init(&fsrp->fr_queue, SV_DEFAULT, "fr_queue"); ++ spinlock_init(&fsrp->fr_lock, "fr_lock"); ++ ++ /* If no other mounted DMAPI filesystem already has this same ++ fsid_t, then add this filesystem to the list. ++ */ ++ ++ lc = mutex_spinlock(&dm_reg_lock); ++ ++ if (!dm_find_fsreg(&fsid)) { ++ fsrp->fr_next = dm_registers; ++ dm_registers = fsrp; ++ dm_fsys_cnt++; ++ mutex_spinunlock(&dm_reg_lock, lc); ++#ifdef CONFIG_PROC_FS ++ { ++ char buf[100]; ++ struct proc_dir_entry *entry; ++ ++ sprintf(buf, DMAPI_DBG_PROCFS "/fsreg/0x%p", fsrp); ++ entry = create_proc_read_entry(buf, 0, NULL, fsreg_read_pfs, fsrp); ++ } ++#endif ++ return(0); ++ } ++ ++ /* A fsid_t collision occurred, so prevent this new filesystem from ++ mounting. ++ */ ++ ++ mutex_spinunlock(&dm_reg_lock, lc); ++ ++ sv_destroy(&fsrp->fr_dispq); ++ sv_destroy(&fsrp->fr_queue); ++ spinlock_destroy(&fsrp->fr_lock); ++ kfree(msg); ++ kmem_cache_free(dm_fsreg_cachep, fsrp); ++ return(-EBUSY); ++} ++ ++ ++/* dm_change_fsys_entry() is called whenever a filesystem's mount state is ++ about to change. The state is changed to DM_STATE_MOUNTED after a ++ successful DM_EVENT_MOUNT event or after a failed unmount. It is changed ++ to DM_STATE_UNMOUNTING after a successful DM_EVENT_PREUNMOUNT event. ++ Finally, the state is changed to DM_STATE_UNMOUNTED after a successful ++ unmount. It stays in this state until the DM_EVENT_UNMOUNT event is ++ queued, at which point the filesystem entry is removed. ++*/ ++ ++void ++dm_change_fsys_entry( ++ struct super_block *sb, ++ dm_fsstate_t newstate) ++{ ++ dm_fsreg_t *fsrp; ++ int seq_error; ++ unsigned long lc; /* lock cookie */ ++ dm_fsid_t fsid; ++ struct filesystem_dmapi_operations *dops; ++ ++ /* Find the filesystem referenced by the sb's fsid_t. This should ++ always succeed. ++ */ ++ ++ dops = dm_fsys_ops(sb); ++ ASSERT(dops); ++ dops->get_fsid(sb, &fsid); ++ ++ if ((fsrp = dm_find_fsreg_and_lock(&fsid, &lc)) == NULL) { ++ panic("dm_change_fsys_entry: can't find DMAPI fsrp for " ++ "sb %p\n", sb); ++ } ++ ++ /* Make sure that the new state is acceptable given the current state ++ of the filesystem. Any error here is a major DMAPI/filesystem ++ screwup. ++ */ ++ ++ seq_error = 0; ++ switch (newstate) { ++ case DM_STATE_MOUNTED: ++ if (fsrp->fr_state != DM_STATE_MOUNTING && ++ fsrp->fr_state != DM_STATE_UNMOUNTING) { ++ seq_error++; ++ } ++ break; ++ case DM_STATE_UNMOUNTING: ++ if (fsrp->fr_state != DM_STATE_MOUNTED) ++ seq_error++; ++ break; ++ case DM_STATE_UNMOUNTED: ++ if (fsrp->fr_state != DM_STATE_UNMOUNTING) ++ seq_error++; ++ break; ++ default: ++ seq_error++; ++ break; ++ } ++ if (seq_error) { ++ panic("dm_change_fsys_entry: DMAPI sequence error: old state " ++ "%d, new state %d, fsrp %p\n", fsrp->fr_state, ++ newstate, fsrp); ++ } ++ ++ /* If the old state was DM_STATE_UNMOUNTING, then processes could be ++ sleeping in dm_handle_to_ip() waiting for their DM_NO_TOKEN handles ++ to be translated to inodes. Wake them up so that they either ++ continue (new state is DM_STATE_MOUNTED) or fail (new state is ++ DM_STATE_UNMOUNTED). ++ */ ++ ++ if (fsrp->fr_state == DM_STATE_UNMOUNTING) { ++ if (fsrp->fr_hdlcnt) ++ sv_broadcast(&fsrp->fr_queue); ++ } ++ ++ /* Change the filesystem's mount state to its new value. */ ++ ++ fsrp->fr_state = newstate; ++ fsrp->fr_tevp = NULL; /* not valid after DM_STATE_MOUNTING */ ++ ++ /* If the new state is DM_STATE_UNMOUNTING, wait until any application ++ threads currently in the process of making VFS_VGET and VFS_ROOT ++ calls are done before we let this unmount thread continue the ++ unmount. (We want to make sure that the unmount will see these ++ inode references during its scan.) ++ */ ++ ++ if (newstate == DM_STATE_UNMOUNTING) { ++ while (fsrp->fr_vfscnt) { ++ fsrp->fr_unmount++; ++ sv_wait(&fsrp->fr_queue, 1, &fsrp->fr_lock, lc); ++ lc = mutex_spinlock(&fsrp->fr_lock); ++ fsrp->fr_unmount--; ++ } ++ } ++ ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++} ++ ++ ++/* dm_remove_fsys_entry() gets called after a failed mount or after an ++ DM_EVENT_UNMOUNT event has been queued. (The filesystem entry must stay ++ until the DM_EVENT_UNMOUNT reply is queued so that the event can use the ++ 'fr_sessp' list to see which session to send the event to.) ++*/ ++ ++void ++dm_remove_fsys_entry( ++ struct super_block *sb) ++{ ++ dm_fsreg_t **fsrpp; ++ dm_fsreg_t *fsrp; ++ unsigned long lc; /* lock cookie */ ++ struct filesystem_dmapi_operations *dops; ++ dm_fsid_t fsid; ++ ++ dops = dm_fsys_ops(sb); ++ ASSERT(dops); ++ dops->get_fsid(sb, &fsid); ++ ++ /* Find the filesystem referenced by the sb's fsid_t and dequeue ++ it after verifying that the fr_state shows a filesystem that is ++ either mounting or unmounted. ++ */ ++ ++ lc = mutex_spinlock(&dm_reg_lock); ++ ++ fsrpp = &dm_registers; ++ while ((fsrp = *fsrpp) != NULL) { ++ if (!memcmp(&fsrp->fr_fsid, &fsid, sizeof(fsrp->fr_fsid))) ++ break; ++ fsrpp = &fsrp->fr_next; ++ } ++ if (fsrp == NULL) { ++ mutex_spinunlock(&dm_reg_lock, lc); ++ panic("dm_remove_fsys_entry: can't find DMAPI fsrp for " ++ "sb %p\n", sb); ++ } ++ ++ nested_spinlock(&fsrp->fr_lock); ++ ++ /* Verify that it makes sense to remove this entry. */ ++ ++ if (fsrp->fr_state != DM_STATE_MOUNTING && ++ fsrp->fr_state != DM_STATE_UNMOUNTED) { ++ nested_spinunlock(&fsrp->fr_lock); ++ mutex_spinunlock(&dm_reg_lock, lc); ++ panic("dm_remove_fsys_entry: DMAPI sequence error: old state " ++ "%d, fsrp %p\n", fsrp->fr_state, fsrp); ++ } ++ ++ *fsrpp = fsrp->fr_next; ++ dm_fsys_cnt--; ++ ++ nested_spinunlock(&dm_reg_lock); ++ ++ /* Since the filesystem is about to finish unmounting, we must be sure ++ that no inodes are being referenced within the filesystem before we ++ let this event thread continue. If the filesystem is currently in ++ state DM_STATE_MOUNTING, then we know by definition that there can't ++ be any references. If the filesystem is DM_STATE_UNMOUNTED, then ++ any application threads referencing handles with DM_NO_TOKEN should ++ have already been awakened by dm_change_fsys_entry and should be ++ long gone by now. Just in case they haven't yet left, sleep here ++ until they are really gone. ++ */ ++ ++ while (fsrp->fr_hdlcnt) { ++ fsrp->fr_unmount++; ++ sv_wait(&fsrp->fr_queue, 1, &fsrp->fr_lock, lc); ++ lc = mutex_spinlock(&fsrp->fr_lock); ++ fsrp->fr_unmount--; ++ } ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ ++ /* Release all memory. */ ++ ++#ifdef CONFIG_PROC_FS ++ { ++ char buf[100]; ++ sprintf(buf, DMAPI_DBG_PROCFS "/fsreg/0x%p", fsrp); ++ remove_proc_entry(buf, NULL); ++ } ++#endif ++ dm_fsys_ops_release(sb); ++ sv_destroy(&fsrp->fr_dispq); ++ sv_destroy(&fsrp->fr_queue); ++ spinlock_destroy(&fsrp->fr_lock); ++ kfree(fsrp->fr_msg); ++ kmem_cache_free(dm_fsreg_cachep, fsrp); ++} ++ ++ ++/* Get an inode for the object referenced by handlep. We cannot use ++ altgetvfs() because it fails if the VFS_OFFLINE bit is set, which means ++ that any call to dm_handle_to_ip() while a umount is in progress would ++ return an error, even if the umount can't possibly succeed because users ++ are in the filesystem. The requests would start to fail as soon as the ++ umount begins, even before the application receives the DM_EVENT_PREUNMOUNT ++ event. ++ ++ dm_handle_to_ip() emulates the behavior of lookup() while an unmount is ++ in progress. Any call to dm_handle_to_ip() while the filesystem is in the ++ DM_STATE_UNMOUNTING state will block. If the unmount eventually succeeds, ++ the requests will wake up and fail. If the unmount fails, the requests will ++ wake up and complete normally. ++ ++ While a filesystem is in state DM_STATE_MOUNTING, dm_handle_to_ip() will ++ fail all requests. Per the DMAPI spec, the only handles in the filesystem ++ which are valid during a mount event are the handles within the event ++ itself. ++*/ ++ ++struct inode * ++dm_handle_to_ip( ++ dm_handle_t *handlep, ++ short *typep) ++{ ++ dm_fsreg_t *fsrp; ++ short type; ++ unsigned long lc; /* lock cookie */ ++ int error = 0; ++ dm_fid_t *fidp; ++ struct super_block *sb; ++ struct inode *ip; ++ int filetype; ++ struct filesystem_dmapi_operations *dmapiops; ++ ++ if ((fsrp = dm_find_fsreg_and_lock(&handlep->ha_fsid, &lc)) == NULL) ++ return NULL; ++ ++ fidp = (dm_fid_t*)&handlep->ha_fid; ++ /* If mounting, and we are not asking for a filesystem handle, ++ * then fail the request. (dm_fid_len==0 for fshandle) ++ */ ++ if ((fsrp->fr_state == DM_STATE_MOUNTING) && ++ (fidp->dm_fid_len != 0)) { ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ return NULL; ++ } ++ ++ for (;;) { ++ if (fsrp->fr_state == DM_STATE_MOUNTING) ++ break; ++ if (fsrp->fr_state == DM_STATE_MOUNTED) ++ break; ++ if (fsrp->fr_state == DM_STATE_UNMOUNTED) { ++ if (fsrp->fr_unmount && fsrp->fr_hdlcnt == 0) ++ sv_broadcast(&fsrp->fr_queue); ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ return NULL; ++ } ++ ++ /* Must be DM_STATE_UNMOUNTING. */ ++ ++ fsrp->fr_hdlcnt++; ++ sv_wait(&fsrp->fr_queue, 1, &fsrp->fr_lock, lc); ++ lc = mutex_spinlock(&fsrp->fr_lock); ++ fsrp->fr_hdlcnt--; ++ } ++ ++ fsrp->fr_vfscnt++; ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ ++ /* Now that the mutex is released, wait until we have access to the ++ inode. ++ */ ++ ++ sb = fsrp->fr_sb; ++ error = -ENOSYS; ++ dmapiops = dm_fsys_ops(sb); ++ ASSERT(dmapiops); ++ if (dmapiops->fh_to_inode) ++ error = dmapiops->fh_to_inode(sb, &ip, (void*)fidp); ++ ++ lc = mutex_spinlock(&fsrp->fr_lock); ++ ++ fsrp->fr_vfscnt--; ++ if (fsrp->fr_unmount && fsrp->fr_vfscnt == 0) ++ sv_broadcast(&fsrp->fr_queue); ++ ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ if (error || ip == NULL) ++ return NULL; ++ ++ filetype = ip->i_mode & S_IFMT; ++ if (fidp->dm_fid_len == 0) { ++ type = DM_TDT_VFS; ++ } else if (filetype == S_IFREG) { ++ type = DM_TDT_REG; ++ } else if (filetype == S_IFDIR) { ++ type = DM_TDT_DIR; ++ } else if (filetype == S_IFLNK) { ++ type = DM_TDT_LNK; ++ } else { ++ type = DM_TDT_OTH; ++ } ++ *typep = type; ++ return ip; ++} ++ ++ ++int ++dm_ip_to_handle( ++ struct inode *ip, ++ dm_handle_t *handlep) ++{ ++ int error; ++ dm_fid_t fid; ++ dm_fsid_t fsid; ++ int hsize; ++ struct filesystem_dmapi_operations *dops; ++ ++ dops = dm_fsys_ops(ip->i_sb); ++ ASSERT(dops); ++ ++ error = dops->inode_to_fh(ip, &fid, &fsid); ++ if (error) ++ return error; ++ ++ memcpy(&handlep->ha_fsid, &fsid, sizeof(fsid)); ++ memcpy(&handlep->ha_fid, &fid, fid.dm_fid_len + sizeof fid.dm_fid_len); ++ hsize = DM_HSIZE(*handlep); ++ memset((char *)handlep + hsize, 0, sizeof(*handlep) - hsize); ++ return 0; ++} ++ ++ ++/* Given an inode, check if that inode resides in filesystem that supports ++ DMAPI. Returns zero if the inode is in a DMAPI filesystem, otherwise ++ returns an errno. ++*/ ++ ++int ++dm_check_dmapi_ip( ++ struct inode *ip) ++{ ++ dm_handle_t handle; ++ /* REFERENCED */ ++ dm_fsreg_t *fsrp; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ if ((error = dm_ip_to_handle(ip, &handle)) != 0) ++ return(error); ++ ++ if ((fsrp = dm_find_fsreg_and_lock(&handle.ha_fsid, &lc)) == NULL) ++ return(-EBADF); ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ return(0); ++} ++ ++ ++/* Return a pointer to the DM_EVENT_MOUNT event while a mount is still in ++ progress. This is only called by dm_get_config and dm_get_config_events ++ which need to access the filesystem during a mount but which don't have ++ a session and token to use. ++*/ ++ ++dm_tokevent_t * ++dm_find_mount_tevp_and_lock( ++ dm_fsid_t *fsidp, ++ unsigned long *lcp) /* address of returned lock cookie */ ++{ ++ dm_fsreg_t *fsrp; ++ ++ if ((fsrp = dm_find_fsreg_and_lock(fsidp, lcp)) == NULL) ++ return(NULL); ++ ++ if (!fsrp->fr_tevp || fsrp->fr_state != DM_STATE_MOUNTING) { ++ mutex_spinunlock(&fsrp->fr_lock, *lcp); ++ return(NULL); ++ } ++ nested_spinlock(&fsrp->fr_tevp->te_lock); ++ nested_spinunlock(&fsrp->fr_lock); ++ return(fsrp->fr_tevp); ++} ++ ++ ++/* Wait interruptibly until a session registers disposition for 'event' in ++ filesystem 'sb'. Upon successful exit, both the filesystem's dm_fsreg_t ++ structure and the session's dm_session_t structure are locked. The caller ++ is responsible for unlocking both structures using the returned cookies. ++ ++ Warning: The locks can be dropped in any order, but the 'lc2p' cookie MUST ++ BE USED FOR THE FIRST UNLOCK, and the lc1p cookie must be used for the ++ second unlock. If this is not done, the CPU will be interruptible while ++ holding a mutex, which could deadlock the machine! ++*/ ++ ++static int ++dm_waitfor_disp( ++ struct super_block *sb, ++ dm_tokevent_t *tevp, ++ dm_fsreg_t **fsrpp, ++ unsigned long *lc1p, /* addr of first returned lock cookie */ ++ dm_session_t **sessionpp, ++ unsigned long *lc2p) /* addr of 2nd returned lock cookie */ ++{ ++ dm_eventtype_t event = tevp->te_msg.ev_type; ++ dm_session_t *s; ++ dm_fsreg_t *fsrp; ++ dm_fsid_t fsid; ++ struct filesystem_dmapi_operations *dops; ++ ++ dops = dm_fsys_ops(sb); ++ ASSERT(dops); ++ ++ dops->get_fsid(sb, &fsid); ++ if ((fsrp = dm_find_fsreg_and_lock(&fsid, lc1p)) == NULL) ++ return -ENOENT; ++ ++ /* If no session is registered for this event in the specified ++ filesystem, then sleep interruptibly until one does. ++ */ ++ ++ for (;;) { ++ int rc = 0; ++ ++ /* The dm_find_session_and_lock() call is needed because a ++ session that is in the process of being removed might still ++ be in the dm_fsreg_t structure but won't be in the ++ dm_sessions list. ++ */ ++ ++ if ((s = fsrp->fr_sessp[event]) != NULL && ++ dm_find_session_and_lock(s->sn_sessid, &s, lc2p) == 0) { ++ break; ++ } ++ ++ /* Noone is currently registered. DM_EVENT_UNMOUNT events ++ don't wait for anyone to register because the unmount is ++ already past the point of no return. ++ */ ++ ++ if (event == DM_EVENT_UNMOUNT) { ++ mutex_spinunlock(&fsrp->fr_lock, *lc1p); ++ return -ENOENT; ++ } ++ ++ /* Wait until a session registers for disposition of this ++ event. ++ */ ++ ++ fsrp->fr_dispcnt++; ++ dm_link_event(tevp, &fsrp->fr_evt_dispq); ++ ++ sv_wait_sig(&fsrp->fr_dispq, 1, &fsrp->fr_lock, *lc1p); ++ rc = signal_pending(current); ++ ++ *lc1p = mutex_spinlock(&fsrp->fr_lock); ++ fsrp->fr_dispcnt--; ++ dm_unlink_event(tevp, &fsrp->fr_evt_dispq); ++#ifdef HAVE_DM_QUEUE_FLUSH ++ if (tevp->te_flags & DM_TEF_FLUSH) { ++ mutex_spinunlock(&fsrp->fr_lock, *lc1p); ++ return tevp->te_reply; ++ } ++#endif /* HAVE_DM_QUEUE_FLUSH */ ++ if (rc) { /* if signal was received */ ++ mutex_spinunlock(&fsrp->fr_lock, *lc1p); ++ return -EINTR; ++ } ++ } ++ *sessionpp = s; ++ *fsrpp = fsrp; ++ return 0; ++} ++ ++ ++/* Returns the session pointer for the session registered for an event ++ in the given sb. If successful, the session is locked upon return. The ++ caller is responsible for releasing the lock. If no session is currently ++ registered for the event, dm_waitfor_disp_session() will sleep interruptibly ++ until a registration occurs. ++*/ ++ ++int ++dm_waitfor_disp_session( ++ struct super_block *sb, ++ dm_tokevent_t *tevp, ++ dm_session_t **sessionpp, ++ unsigned long *lcp) ++{ ++ dm_fsreg_t *fsrp; ++ unsigned long lc2; ++ int error; ++ ++ if (tevp->te_msg.ev_type < 0 || tevp->te_msg.ev_type > DM_EVENT_MAX) ++ return(-EIO); ++ ++ error = dm_waitfor_disp(sb, tevp, &fsrp, lcp, sessionpp, &lc2); ++ if (!error) ++ mutex_spinunlock(&fsrp->fr_lock, lc2); /* rev. cookie order*/ ++ return(error); ++} ++ ++ ++/* Find the session registered for the DM_EVENT_DESTROY event on the specified ++ filesystem, sleeping if necessary until registration occurs. Once found, ++ copy the session's return-on-destroy attribute name, if any, back to the ++ caller. ++*/ ++ ++int ++dm_waitfor_destroy_attrname( ++ struct super_block *sbp, ++ dm_attrname_t *attrnamep) ++{ ++ dm_tokevent_t *tevp; ++ dm_session_t *s; ++ dm_fsreg_t *fsrp; ++ int error; ++ unsigned long lc1; /* first lock cookie */ ++ unsigned long lc2; /* second lock cookie */ ++ void *msgp; ++ ++ tevp = dm_evt_create_tevp(DM_EVENT_DESTROY, 1, (void**)&msgp); ++ error = dm_waitfor_disp(sbp, tevp, &fsrp, &lc1, &s, &lc2); ++ if (!error) { ++ *attrnamep = fsrp->fr_rattr; /* attribute or zeros */ ++ mutex_spinunlock(&s->sn_qlock, lc2); /* rev. cookie order */ ++ mutex_spinunlock(&fsrp->fr_lock, lc1); ++ } ++ dm_evt_rele_tevp(tevp,0); ++ return(error); ++} ++ ++ ++/* Unregisters the session for the disposition of all events on all ++ filesystems. This routine is not called until the session has been ++ dequeued from the session list and its session lock has been dropped, ++ but before the actual structure is freed, so it is safe to grab the ++ 'dm_reg_lock' here. If dm_waitfor_disp_session() happens to be called ++ by another thread, it won't find this session on the session list and ++ will wait until a new session registers. ++*/ ++ ++void ++dm_clear_fsreg( ++ dm_session_t *s) ++{ ++ dm_fsreg_t *fsrp; ++ int event; ++ unsigned long lc; /* lock cookie */ ++ ++ lc = mutex_spinlock(&dm_reg_lock); ++ ++ for (fsrp = dm_registers; fsrp != NULL; fsrp = fsrp->fr_next) { ++ nested_spinlock(&fsrp->fr_lock); ++ for (event = 0; event < DM_EVENT_MAX; event++) { ++ if (fsrp->fr_sessp[event] != s) ++ continue; ++ fsrp->fr_sessp[event] = NULL; ++ if (event == DM_EVENT_DESTROY) ++ memset(&fsrp->fr_rattr, 0, sizeof(fsrp->fr_rattr)); ++ } ++ nested_spinunlock(&fsrp->fr_lock); ++ } ++ ++ mutex_spinunlock(&dm_reg_lock, lc); ++} ++ ++ ++/* ++ * Return the handle for the object named by path. ++ */ ++ ++int ++dm_path_to_hdl( ++ char __user *path, /* any path name */ ++ void __user *hanp, /* user's data buffer */ ++ size_t __user *hlenp) /* set to size of data copied */ ++{ ++ /* REFERENCED */ ++ dm_fsreg_t *fsrp; ++ dm_handle_t handle; ++ size_t hlen; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ struct nameidata nd; ++ struct inode *inode; ++ size_t len; ++ char *name; ++ struct filesystem_dmapi_operations *dops; ++ ++ /* XXX get things straightened out so getname() works here? */ ++ if (!(len = strnlen_user(path, PATH_MAX))) ++ return(-EFAULT); ++ if (len == 1) ++ return(-ENOENT); ++ if (len > PATH_MAX) ++ return(-ENAMETOOLONG); ++ name = kmalloc(len, GFP_KERNEL); ++ if (name == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return(-ENOMEM); ++ } ++ if (copy_from_user(name, path, len)) { ++ kfree(name); ++ return(-EFAULT); ++ } ++ ++ error = path_lookup(name, LOOKUP_POSITIVE, &nd); ++ kfree(name); ++ if (error) ++ return error; ++ ++ ASSERT(nd.path.dentry); ++ ASSERT(nd.path.dentry->d_inode); ++ inode = igrab(nd.path.dentry->d_inode); ++ path_put(&nd.path); ++ ++ dops = dm_fsys_ops(inode->i_sb); ++ if (dops == NULL) { ++ /* No longer in a dmapi-capable filesystem...Toto */ ++ iput(inode); ++ return -EINVAL; ++ } ++ ++ /* we need the inode */ ++ error = dm_ip_to_handle(inode, &handle); ++ iput(inode); ++ if (error) ++ return(error); ++ ++ if ((fsrp = dm_find_fsreg_and_lock(&handle.ha_fsid, &lc)) == NULL) ++ return(-EBADF); ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ ++ hlen = DM_HSIZE(handle); ++ ++ if (copy_to_user(hanp, &handle, (int)hlen)) ++ return(-EFAULT); ++ if (put_user(hlen,hlenp)) ++ return(-EFAULT); ++ return 0; ++} ++ ++ ++/* ++ * Return the handle for the file system containing the object named by path. ++ */ ++ ++int ++dm_path_to_fshdl( ++ char __user *path, /* any path name */ ++ void __user *hanp, /* user's data buffer */ ++ size_t __user *hlenp) /* set to size of data copied */ ++{ ++ /* REFERENCED */ ++ dm_fsreg_t *fsrp; ++ dm_handle_t handle; ++ size_t hlen; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ struct nameidata nd; ++ struct inode *inode; ++ size_t len; ++ char *name; ++ struct filesystem_dmapi_operations *dops; ++ ++ /* XXX get things straightened out so getname() works here? */ ++ if(!(len = strnlen_user(path, PATH_MAX))) ++ return(-EFAULT); ++ if (len == 1) ++ return(-ENOENT); ++ if (len > PATH_MAX) ++ return(-ENAMETOOLONG); ++ name = kmalloc(len, GFP_KERNEL); ++ if (name == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return(-ENOMEM); ++ } ++ if (copy_from_user(name, path, len)) { ++ kfree(name); ++ return(-EFAULT); ++ } ++ ++ error = path_lookup(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd); ++ kfree(name); ++ if (error) ++ return error; ++ ++ ASSERT(nd.path.dentry); ++ ASSERT(nd.path.dentry->d_inode); ++ ++ inode = igrab(nd.path.dentry->d_inode); ++ path_put(&nd.path); ++ ++ dops = dm_fsys_ops(inode->i_sb); ++ if (dops == NULL) { ++ /* No longer in a dmapi-capable filesystem...Toto */ ++ iput(inode); ++ return -EINVAL; ++ } ++ ++ error = dm_ip_to_handle(inode, &handle); ++ iput(inode); ++ ++ if (error) ++ return(error); ++ ++ if ((fsrp = dm_find_fsreg_and_lock(&handle.ha_fsid, &lc)) == NULL) ++ return(-EBADF); ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ ++ hlen = DM_FSHSIZE; ++ if(copy_to_user(hanp, &handle, (int)hlen)) ++ return(-EFAULT); ++ if(put_user(hlen,hlenp)) ++ return(-EFAULT); ++ return 0; ++} ++ ++ ++int ++dm_fd_to_hdl( ++ int fd, /* any file descriptor */ ++ void __user *hanp, /* user's data buffer */ ++ size_t __user *hlenp) /* set to size of data copied */ ++{ ++ /* REFERENCED */ ++ dm_fsreg_t *fsrp; ++ dm_handle_t handle; ++ size_t hlen; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ struct file *filep = fget(fd); ++ struct inode *ip = filep->f_dentry->d_inode; ++ ++ if (!filep) ++ return(-EBADF); ++ if ((error = dm_ip_to_handle(ip, &handle)) != 0) ++ return(error); ++ ++ if ((fsrp = dm_find_fsreg_and_lock(&handle.ha_fsid, &lc)) == NULL) ++ return(-EBADF); ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ ++ hlen = DM_HSIZE(handle); ++ if (copy_to_user(hanp, &handle, (int)hlen)) ++ return(-EFAULT); ++ fput(filep); ++ if(put_user(hlen, hlenp)) ++ return(-EFAULT); ++ return 0; ++} ++ ++ ++/* Enable events on an object. */ ++ ++int ++dm_set_eventlist( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_eventset_t __user *eventsetp, ++ u_int maxevent) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_eventset_t eventset; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ if (copy_from_user(&eventset, eventsetp, sizeof(eventset))) ++ return(-EFAULT); ++ ++ /* Do some minor sanity checking. */ ++ ++ if (maxevent == 0 || maxevent > DM_EVENT_MAX) ++ return(-EINVAL); ++ ++ /* Access the specified object. */ ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_ANY, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->set_eventlist(tdp->td_ip, tdp->td_right, ++ (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0), ++ &eventset, maxevent); ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++/* Return the list of enabled events for an object. */ ++ ++int ++dm_get_eventlist( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int nelem, ++ dm_eventset_t __user *eventsetp, ++ u_int __user *nelemp) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ dm_eventset_t eventset; ++ u_int elem; ++ int error; ++ ++ if (nelem == 0) ++ return(-EINVAL); ++ ++ /* Access the specified object. */ ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_ANY, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ /* Get the object's event list. */ ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->get_eventlist(tdp->td_ip, tdp->td_right, ++ (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0), ++ nelem, &eventset, &elem); ++ ++ dm_app_put_tdp(tdp); ++ ++ if (error) ++ return(error); ++ ++ if (copy_to_user(eventsetp, &eventset, sizeof(eventset))) ++ return(-EFAULT); ++ if (put_user(nelem, nelemp)) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++/* Register for disposition of events. The handle must either be the ++ global handle or must be the handle of a file system. The list of events ++ is pointed to by eventsetp. ++*/ ++ ++int ++dm_set_disp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_eventset_t __user *eventsetp, ++ u_int maxevent) ++{ ++ dm_session_t *s; ++ dm_fsreg_t *fsrp; ++ dm_tokdata_t *tdp; ++ dm_eventset_t eventset; ++ int error; ++ unsigned long lc1; /* first lock cookie */ ++ unsigned long lc2; /* second lock cookie */ ++ u_int i; ++ ++ /* Copy in and validate the event mask. Only the lower maxevent bits ++ are meaningful, so clear any bits set above maxevent. ++ */ ++ ++ if (maxevent == 0 || maxevent > DM_EVENT_MAX) ++ return(-EINVAL); ++ if (copy_from_user(&eventset, eventsetp, sizeof(eventset))) ++ return(-EFAULT); ++ eventset &= (1 << maxevent) - 1; ++ ++ /* If the caller specified the global handle, then the only valid token ++ is DM_NO_TOKEN, and the only valid event in the event mask is ++ DM_EVENT_MOUNT. If it is set, add the session to the list of ++ sessions that want to receive mount events. If it is clear, remove ++ the session from the list. Since DM_EVENT_MOUNT events never block ++ waiting for a session to register, there is noone to wake up if we ++ do add the session to the list. ++ */ ++ ++ if (DM_GLOBALHAN(hanp, hlen)) { ++ if (token != DM_NO_TOKEN) ++ return(-EINVAL); ++ if ((error = dm_find_session_and_lock(sid, &s, &lc1)) != 0) ++ return(error); ++ if (eventset == 0) { ++ s->sn_flags &= ~DM_SN_WANTMOUNT; ++ error = 0; ++ } else if (eventset == 1 << DM_EVENT_MOUNT) { ++ s->sn_flags |= DM_SN_WANTMOUNT; ++ error = 0; ++ } else { ++ error = -EINVAL; ++ } ++ mutex_spinunlock(&s->sn_qlock, lc1); ++ return(error); ++ } ++ ++ /* Since it's not the global handle, it had better be a filesystem ++ handle. Verify that the first 'maxevent' events in the event list ++ are all valid for a filesystem handle. ++ */ ++ ++ if (eventset & ~DM_VALID_DISP_EVENTS) ++ return(-EINVAL); ++ ++ /* Verify that the session is valid, that the handle is a filesystem ++ handle, and that the filesystem is capable of sending events. (If ++ a dm_fsreg_t structure exists, then the filesystem can issue events.) ++ */ ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsrp = dm_find_fsreg_and_lock(&tdp->td_handle.ha_fsid, &lc1); ++ if (fsrp == NULL) { ++ dm_app_put_tdp(tdp); ++ return(-EINVAL); ++ } ++ ++ /* Now that we own 'fsrp->fr_lock', get the lock on the session so that ++ it can't disappear while we add it to the filesystem's event mask. ++ */ ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc2)) != 0) { ++ mutex_spinunlock(&fsrp->fr_lock, lc1); ++ dm_app_put_tdp(tdp); ++ return(error); ++ } ++ ++ /* Update the event disposition array for this filesystem, adding ++ and/or removing the session as appropriate. If this session is ++ dropping registration for DM_EVENT_DESTROY, or is overriding some ++ other session's registration for DM_EVENT_DESTROY, then clear any ++ any attr-on-destroy attribute name also. ++ */ ++ ++ for (i = 0; i < DM_EVENT_MAX; i++) { ++ if (DMEV_ISSET(i, eventset)) { ++ if (i == DM_EVENT_DESTROY && fsrp->fr_sessp[i] != s) ++ memset(&fsrp->fr_rattr, 0, sizeof(fsrp->fr_rattr)); ++ fsrp->fr_sessp[i] = s; ++ } else if (fsrp->fr_sessp[i] == s) { ++ if (i == DM_EVENT_DESTROY) ++ memset(&fsrp->fr_rattr, 0, sizeof(fsrp->fr_rattr)); ++ fsrp->fr_sessp[i] = NULL; ++ } ++ } ++ mutex_spinunlock(&s->sn_qlock, lc2); /* reverse cookie order */ ++ ++ /* Wake up all processes waiting for a disposition on this filesystem ++ in case any of them happen to be waiting for an event which we just ++ added. ++ */ ++ ++ if (fsrp->fr_dispcnt) ++ sv_broadcast(&fsrp->fr_dispq); ++ ++ mutex_spinunlock(&fsrp->fr_lock, lc1); ++ ++ dm_app_put_tdp(tdp); ++ return(0); ++} ++ ++ ++/* ++ * Register a specific attribute name with a filesystem. The value of ++ * the attribute is to be returned with an asynchronous destroy event. ++ */ ++ ++int ++dm_set_return_on_destroy( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_attrname_t __user *attrnamep, ++ dm_boolean_t enable) ++{ ++ dm_attrname_t attrname; ++ dm_tokdata_t *tdp; ++ dm_fsreg_t *fsrp; ++ dm_session_t *s; ++ int error; ++ unsigned long lc1; /* first lock cookie */ ++ unsigned long lc2; /* second lock cookie */ ++ ++ /* If a dm_attrname_t is provided, copy it in and validate it. */ ++ ++ if (enable && (error = copy_from_user(&attrname, attrnamep, sizeof(attrname))) != 0) ++ return(error); ++ ++ /* Validate the filesystem handle and use it to get the filesystem's ++ disposition structure. ++ */ ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS, ++ DM_RIGHT_EXCL, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsrp = dm_find_fsreg_and_lock(&tdp->td_handle.ha_fsid, &lc1); ++ if (fsrp == NULL) { ++ dm_app_put_tdp(tdp); ++ return(-EINVAL); ++ } ++ ++ /* Now that we own 'fsrp->fr_lock', get the lock on the session so that ++ it can't disappear while we add it to the filesystem's event mask. ++ */ ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc2)) != 0) { ++ mutex_spinunlock(&fsrp->fr_lock, lc1); ++ dm_app_put_tdp(tdp); ++ return(error); ++ } ++ ++ /* A caller cannot disable return-on-destroy if he is not registered ++ for DM_EVENT_DESTROY. Enabling return-on-destroy is an implicit ++ dm_set_disp() for DM_EVENT_DESTROY; we wake up all processes ++ waiting for a disposition in case any was waiting for a ++ DM_EVENT_DESTROY event. ++ */ ++ ++ error = 0; ++ if (enable) { ++ fsrp->fr_sessp[DM_EVENT_DESTROY] = s; ++ fsrp->fr_rattr = attrname; ++ if (fsrp->fr_dispcnt) ++ sv_broadcast(&fsrp->fr_dispq); ++ } else if (fsrp->fr_sessp[DM_EVENT_DESTROY] != s) { ++ error = -EINVAL; ++ } else { ++ memset(&fsrp->fr_rattr, 0, sizeof(fsrp->fr_rattr)); ++ } ++ mutex_spinunlock(&s->sn_qlock, lc2); /* reverse cookie order */ ++ mutex_spinunlock(&fsrp->fr_lock, lc1); ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_get_mountinfo( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ dm_fsreg_t *fsrp; ++ dm_tokdata_t *tdp; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ /* Make sure that the caller's buffer is 8-byte aligned. */ ++ ++ if (((unsigned long)bufp & (sizeof(__u64) - 1)) != 0) ++ return(-EFAULT); ++ ++ /* Verify that the handle is a filesystem handle, and that the ++ filesystem is capable of sending events. If not, return an error. ++ */ ++ ++ error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS, ++ DM_RIGHT_SHARED, &tdp); ++ if (error != 0) ++ return(error); ++ ++ /* Find the filesystem entry. This should always succeed as the ++ dm_app_get_tdp call created a filesystem reference. Once we find ++ the entry, drop the lock. The mountinfo message is never modified, ++ the filesystem entry can't disappear, and we don't want to hold a ++ spinlock while doing copyout calls. ++ */ ++ ++ fsrp = dm_find_fsreg_and_lock(&tdp->td_handle.ha_fsid, &lc); ++ if (fsrp == NULL) { ++ dm_app_put_tdp(tdp); ++ return(-EINVAL); ++ } ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ ++ /* Copy the message into the user's buffer and update his 'rlenp'. */ ++ ++ if (put_user(fsrp->fr_msgsize, rlenp)) { ++ error = -EFAULT; ++ } else if (fsrp->fr_msgsize > buflen) { /* user buffer not big enough */ ++ error = -E2BIG; ++ } else if (copy_to_user(bufp, fsrp->fr_msg, fsrp->fr_msgsize)) { ++ error = -EFAULT; ++ } else { ++ error = 0; ++ } ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_getall_disp( ++ dm_sessid_t sid, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ dm_session_t *s; /* pointer to session given by sid */ ++ unsigned long lc1; /* first lock cookie */ ++ unsigned long lc2; /* second lock cookie */ ++ int totalsize; ++ int msgsize; ++ int fsyscnt; ++ dm_dispinfo_t *prevmsg; ++ dm_fsreg_t *fsrp; ++ int error; ++ char *kbuf; ++ ++ int tmp3; ++ int tmp4; ++ ++ /* Because the dm_getall_disp structure contains a __u64 field, ++ make sure that the buffer provided by the caller is aligned so ++ that he can read such fields successfully. ++ */ ++ ++ if (((unsigned long)bufp & (sizeof(__u64) - 1)) != 0) ++ return(-EFAULT); ++ ++ /* Compute the size of a dm_dispinfo structure, rounding up to an ++ 8-byte boundary so that any subsequent structures will also be ++ aligned. ++ */ ++ ++#if 0 ++ /* XXX ug, what is going on here? */ ++ msgsize = (sizeof(dm_dispinfo_t) + DM_FSHSIZE + sizeof(uint64_t) - 1) & ++ ~(sizeof(uint64_t) - 1); ++#else ++ tmp3 = sizeof(dm_dispinfo_t) + DM_FSHSIZE; ++ tmp3 += sizeof(__u64); ++ tmp3 -= 1; ++ tmp4 = ~((int)sizeof(__u64) - 1); ++ msgsize = tmp3 & tmp4; ++#endif ++ ++ /* Loop until we can get the right amount of temp space, being careful ++ not to hold a mutex during the allocation. Usually only one trip. ++ */ ++ ++ for (;;) { ++ if ((fsyscnt = dm_fsys_cnt) == 0) { ++ /*if (dm_cpoutsizet(rlenp, 0))*/ ++ if (put_user(0,rlenp)) ++ return(-EFAULT); ++ return(0); ++ } ++ kbuf = kmalloc(fsyscnt * msgsize, GFP_KERNEL); ++ if (kbuf == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return -ENOMEM; ++ } ++ ++ lc1 = mutex_spinlock(&dm_reg_lock); ++ if (fsyscnt == dm_fsys_cnt) ++ break; ++ ++ mutex_spinunlock(&dm_reg_lock, lc1); ++ kfree(kbuf); ++ } ++ ++ /* Find the indicated session and lock it. */ ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc2)) != 0) { ++ mutex_spinunlock(&dm_reg_lock, lc1); ++ kfree(kbuf); ++ return(error); ++ } ++ ++ /* Create a dm_dispinfo structure for each filesystem in which ++ this session has at least one event selected for disposition. ++ */ ++ ++ totalsize = 0; /* total bytes to transfer to the user */ ++ prevmsg = NULL; ++ ++ for (fsrp = dm_registers; fsrp; fsrp = fsrp->fr_next) { ++ dm_dispinfo_t *disp; ++ int event; ++ int found; ++ ++ disp = (dm_dispinfo_t *)(kbuf + totalsize); ++ ++ DMEV_ZERO(disp->di_eventset); ++ ++ for (event = 0, found = 0; event < DM_EVENT_MAX; event++) { ++ if (fsrp->fr_sessp[event] != s) ++ continue; ++ DMEV_SET(event, disp->di_eventset); ++ found++; ++ } ++ if (!found) ++ continue; ++ ++ disp->_link = 0; ++ disp->di_fshandle.vd_offset = sizeof(dm_dispinfo_t); ++ disp->di_fshandle.vd_length = DM_FSHSIZE; ++ ++ memcpy((char *)disp + disp->di_fshandle.vd_offset, ++ &fsrp->fr_fsid, disp->di_fshandle.vd_length); ++ ++ if (prevmsg) ++ prevmsg->_link = msgsize; ++ ++ prevmsg = disp; ++ totalsize += msgsize; ++ } ++ mutex_spinunlock(&s->sn_qlock, lc2); /* reverse cookie order */ ++ mutex_spinunlock(&dm_reg_lock, lc1); ++ ++ if (put_user(totalsize, rlenp)) { ++ error = -EFAULT; ++ } else if (totalsize > buflen) { /* no more room */ ++ error = -E2BIG; ++ } else if (totalsize && copy_to_user(bufp, kbuf, totalsize)) { ++ error = -EFAULT; ++ } else { ++ error = 0; ++ } ++ ++ kfree(kbuf); ++ return(error); ++} ++ ++int ++dm_open_by_handle_rvp( ++ unsigned int fd, ++ void __user *hanp, ++ size_t hlen, ++ int flags, ++ int *rvp) ++{ ++ const struct cred *cred = current_cred(); ++ dm_handle_t handle; ++ int error; ++ short td_type; ++ struct dentry *dentry; ++ struct inode *inodep; ++ int new_fd; ++ struct file *mfilp; ++ struct file *filp; ++ ++ if ((error = dm_copyin_handle(hanp, hlen, &handle)) != 0) { ++ return(error); ++ } ++ ++ if ((inodep = dm_handle_to_ip(&handle, &td_type)) == NULL) { ++ return(-EBADF); ++ } ++ if ((td_type == DM_TDT_VFS) || (td_type == DM_TDT_OTH)) { ++ iput(inodep); ++ return(-EBADF); ++ } ++ ++ if ((new_fd = get_unused_fd()) < 0) { ++ iput(inodep); ++ return(-EMFILE); ++ } ++ ++ dentry = d_obtain_alias(inodep); ++ if (dentry == NULL) { ++ iput(inodep); ++ put_unused_fd(new_fd); ++ return(-ENOMEM); ++ } ++ ++ mfilp = fget(fd); ++ if (!mfilp) { ++ dput(dentry); ++ put_unused_fd(new_fd); ++ return(-EBADF); ++ } ++ ++ mntget(mfilp->f_vfsmnt); ++ ++ /* Create file pointer */ ++ filp = dentry_open(dentry, mfilp->f_vfsmnt, flags, cred); ++ if (IS_ERR(filp)) { ++ put_unused_fd(new_fd); ++ fput(mfilp); ++ return PTR_ERR(filp); ++ } ++ ++ if (td_type == DM_TDT_REG) ++ filp->f_mode |= FMODE_NOCMTIME; ++ ++ fd_install(new_fd, filp); ++ fput(mfilp); ++ *rvp = new_fd; ++ return 0; ++} ++ ++ ++#ifdef HAVE_DM_QUEUE_FLUSH ++/* Find the threads that have a reference to our filesystem and force ++ them to return with the specified errno. ++ We look for them in each dm_fsreg_t's fr_evt_dispq. ++*/ ++ ++int ++dm_release_disp_threads( ++ dm_fsid_t *fsidp, ++ struct inode *inode, /* may be null */ ++ int errno) ++{ ++ unsigned long lc; ++ dm_fsreg_t *fsrp; ++ dm_tokevent_t *tevp; ++ dm_tokdata_t *tdp; ++ dm_eventq_t *queue; ++ int found_events = 0; ++ ++ if ((fsrp = dm_find_fsreg_and_lock(fsidp, &lc)) == NULL){ ++ return 0; ++ } ++ ++ queue = &fsrp->fr_evt_dispq; ++ for (tevp = queue->eq_head; tevp; tevp = tevp->te_next) { ++ nested_spinlock(&tevp->te_lock); ++ if (inode) { ++ for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) { ++ if( tdp->td_ip == inode ) { ++ tevp->te_flags |= DM_TEF_FLUSH; ++ tevp->te_reply = errno; ++ found_events = 1; ++ break; ++ } ++ } ++ } ++ else { ++ tevp->te_flags |= DM_TEF_FLUSH; ++ tevp->te_reply = errno; ++ found_events = 1; ++ } ++ nested_spinunlock(&tevp->te_lock); ++ } ++ ++ if (found_events && fsrp->fr_dispcnt) ++ sv_broadcast(&fsrp->fr_dispq); ++ mutex_spinunlock(&fsrp->fr_lock, lc); ++ return 0; ++} ++#endif /* HAVE_DM_QUEUE_FLUSH */ +--- /dev/null ++++ b/fs/dmapi/dmapi_right.c +@@ -0,0 +1,1256 @@ ++/* ++ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#include ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++ ++#define DM_FG_STHREAD 0x001 /* keep other threads from using tdp */ ++#define DM_FG_MUSTEXIST 0x002 /* handle must exist in the event */ ++#define DM_FG_DONTADD 0x004 /* don't add handle if not in event */ ++ ++/* Get a handle of the form (void *, size_t) from user space and convert it to ++ a handle_t. Do as much validation of the result as possible; any error ++ other than a bad address should return EBADF per the DMAPI spec. ++*/ ++ ++int ++dm_copyin_handle( ++ void __user *hanp, /* input, handle data */ ++ size_t hlen, /* input, size of handle data */ ++ dm_handle_t *handlep) /* output, copy of data */ ++{ ++ u_short len; ++ dm_fid_t *fidp; ++ ++ fidp = (dm_fid_t*)&handlep->ha_fid; ++ ++ if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep)) ++ return -EBADF; ++ ++ if (copy_from_user(handlep, hanp, hlen)) ++ return -EFAULT; ++ ++ if (hlen < sizeof(*handlep)) ++ memset((char *)handlep + hlen, 0, sizeof(*handlep) - hlen); ++ ++ if (hlen == sizeof(handlep->ha_fsid)) ++ return 0; /* FS handle, nothing more to check */ ++ ++ len = hlen - sizeof(handlep->ha_fsid) - sizeof(fidp->dm_fid_len); ++ ++ if ((fidp->dm_fid_len != len) || fidp->dm_fid_pad) ++ return -EBADF; ++ return 0; ++} ++ ++/* Allocate and initialize a tevp structure. Called from both application and ++ event threads. ++*/ ++ ++static dm_tokevent_t * ++dm_init_tevp( ++ int ev_size, /* size of event structure */ ++ int var_size) /* size of variable-length data */ ++{ ++ dm_tokevent_t *tevp; ++ int msgsize; ++ ++ /* Calculate the size of the event in bytes and allocate memory for it. ++ Zero all but the variable portion of the message, which will be ++ eventually overlaid by the caller with data. ++ */ ++ ++ msgsize = offsetof(dm_tokevent_t, te_event) + ev_size + var_size; ++ tevp = kmalloc(msgsize, GFP_KERNEL); ++ if (tevp == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return NULL; ++ } ++ memset(tevp, 0, msgsize - var_size); ++ ++ /* Now initialize all the non-zero fields. */ ++ ++ spinlock_init(&tevp->te_lock, "te_lock"); ++ sv_init(&tevp->te_evt_queue, SV_DEFAULT, "te_evt_queue"); ++ sv_init(&tevp->te_app_queue, SV_DEFAULT, "te_app_queue"); ++ tevp->te_allocsize = msgsize; ++ tevp->te_msg.ev_type = DM_EVENT_INVALID; ++ tevp->te_flags = 0; ++ ++ return(tevp); ++} ++ ++ ++/* Given the event type and the number of bytes of variable length data that ++ will follow the event, dm_evt_create_tevp() creates a dm_tokevent_t ++ structure to hold the event and initializes all the common event fields. ++ ++ No locking is required for this routine because the caller is an event ++ thread, and is therefore the only thread that can see the event. ++*/ ++ ++dm_tokevent_t * ++dm_evt_create_tevp( ++ dm_eventtype_t event, ++ int variable_size, ++ void **msgpp) ++{ ++ dm_tokevent_t *tevp; ++ int evsize; ++ ++ switch (event) { ++ case DM_EVENT_READ: ++ case DM_EVENT_WRITE: ++ case DM_EVENT_TRUNCATE: ++ evsize = sizeof(dm_data_event_t); ++ break; ++ ++ case DM_EVENT_DESTROY: ++ evsize = sizeof(dm_destroy_event_t); ++ break; ++ ++ case DM_EVENT_MOUNT: ++ evsize = sizeof(dm_mount_event_t); ++ break; ++ ++ case DM_EVENT_PREUNMOUNT: ++ case DM_EVENT_UNMOUNT: ++ case DM_EVENT_NOSPACE: ++ case DM_EVENT_CREATE: ++ case DM_EVENT_REMOVE: ++ case DM_EVENT_RENAME: ++ case DM_EVENT_SYMLINK: ++ case DM_EVENT_LINK: ++ case DM_EVENT_POSTCREATE: ++ case DM_EVENT_POSTREMOVE: ++ case DM_EVENT_POSTRENAME: ++ case DM_EVENT_POSTSYMLINK: ++ case DM_EVENT_POSTLINK: ++ case DM_EVENT_ATTRIBUTE: ++ case DM_EVENT_DEBUT: /* currently not supported */ ++ case DM_EVENT_CLOSE: /* currently not supported */ ++ evsize = sizeof(dm_namesp_event_t); ++ break; ++ ++ case DM_EVENT_CANCEL: /* currently not supported */ ++ evsize = sizeof(dm_cancel_event_t); ++ break; ++ ++ case DM_EVENT_USER: ++ evsize = 0; ++ break; ++ ++ default: ++ panic("dm_create_tevp: called with unknown event type %d\n", ++ event); ++ } ++ ++ /* Allocate and initialize an event structure of the correct size. */ ++ ++ tevp = dm_init_tevp(evsize, variable_size); ++ if (tevp == NULL) ++ return NULL; ++ tevp->te_evt_ref = 1; ++ ++ /* Fields ev_token, ev_sequence, and _link are all filled in when the ++ event is queued onto a session. Initialize all other fields here. ++ */ ++ ++ tevp->te_msg.ev_type = event; ++ tevp->te_msg.ev_data.vd_offset = offsetof(dm_tokevent_t, te_event) - ++ offsetof(dm_tokevent_t, te_msg); ++ tevp->te_msg.ev_data.vd_length = evsize + variable_size; ++ ++ /* Give the caller a pointer to the event-specific structure. */ ++ ++ *msgpp = ((char *)&tevp->te_msg + tevp->te_msg.ev_data.vd_offset); ++ return(tevp); ++} ++ ++ ++/* Given a pointer to an event (tevp) and a pointer to a handle_t, look for a ++ tdp structure within the event which contains the handle_t. Either verify ++ that the event contains the tdp, or optionally add the tdp to the ++ event. Called only from application threads. ++ ++ On entry, tevp->te_lock is held; it is dropped prior to return. ++*/ ++ ++static int ++dm_app_lookup_tdp( ++ dm_handle_t *handlep, /* the handle we are looking for */ ++ dm_tokevent_t *tevp, /* the event to search for the handle */ ++ unsigned long *lcp, /* address of active lock cookie */ ++ short types, /* acceptable object types */ ++ dm_right_t right, /* minimum right the object must have */ ++ u_int flags, ++ dm_tokdata_t **tdpp) /* if ! NULL, pointer to matching tdp */ ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ struct inode *ip; ++ int error; ++ ++ /* Bump the tevp application reference counter so that the event ++ can't disappear in case we have to drop the lock for a while. ++ */ ++ ++ tevp->te_app_ref++; ++ *tdpp = NULL; /* assume failure */ ++ ++ for (;;) { ++ /* Look for a matching tdp in the tevp. */ ++ ++ for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) { ++ if (DM_HANDLE_CMP(&tdp->td_handle, handlep) == 0) ++ break; ++ } ++ ++ /* If the tdp exists, but either we need single-thread access ++ to the handle and can't get it, or some other thread already ++ has single-thread access, then sleep until we can try again. ++ */ ++ ++ if (tdp != NULL && tdp->td_app_ref && ++ ((flags & DM_FG_STHREAD) || ++ (tdp->td_flags & DM_TDF_STHREAD))) { ++ tevp->te_app_slp++; ++ sv_wait(&tevp->te_app_queue, 1, ++ &tevp->te_lock, *lcp); ++ *lcp = mutex_spinlock(&tevp->te_lock); ++ tevp->te_app_slp--; ++ continue; ++ } ++ ++ if (tdp != NULL && ++ (tdp->td_vcount > 0 || tdp->td_flags & DM_TDF_EVTREF)) { ++ /* We have an existing tdp with a non-zero inode ++ reference count. If it's the wrong type, return ++ an appropriate errno. ++ */ ++ ++ if (!(tdp->td_type & types)) { ++ mutex_spinunlock(&tevp->te_lock, *lcp); ++ dm_put_tevp(tevp, NULL); /* no destroy events */ ++ return(-EOPNOTSUPP); ++ } ++ ++ /* If the current access right isn't high enough, ++ complain. ++ */ ++ ++ if (tdp->td_right < right) { ++ mutex_spinunlock(&tevp->te_lock, *lcp); ++ dm_put_tevp(tevp, NULL); /* no destroy events */ ++ return(-EACCES); ++ } ++ ++ /* The handle is acceptable. Increment the tdp ++ application and inode references and mark the tdp ++ as single-threaded if necessary. ++ */ ++ ++ tdp->td_app_ref++; ++ if (flags & DM_FG_STHREAD) ++ tdp->td_flags |= DM_TDF_STHREAD; ++ tdp->td_vcount++; ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ (void)fsys_vector->obj_ref_hold(tdp->td_ip); ++ ++ mutex_spinunlock(&tevp->te_lock, *lcp); ++ *tdpp = tdp; ++ return(0); ++ } ++ ++ /* If the tdp is not in the tevp or does not have an inode ++ reference, check to make sure it is okay to add/update it. ++ */ ++ ++ if (flags & DM_FG_MUSTEXIST) { ++ mutex_spinunlock(&tevp->te_lock, *lcp); ++ dm_put_tevp(tevp, NULL); /* no destroy events */ ++ return(-EACCES); /* i.e. an insufficient right */ ++ } ++ if (flags & DM_FG_DONTADD) { ++ tevp->te_app_ref--; ++ mutex_spinunlock(&tevp->te_lock, *lcp); ++ return(0); ++ } ++ ++ /* If a tdp structure doesn't yet exist, create one and link ++ it into the tevp. Drop the lock while we are doing this as ++ zallocs can go to sleep. Once we have the memory, make ++ sure that another thread didn't simultaneously add the same ++ handle to the same event. If so, toss ours and start over. ++ */ ++ ++ if (tdp == NULL) { ++ dm_tokdata_t *tmp; ++ ++ mutex_spinunlock(&tevp->te_lock, *lcp); ++ ++ tdp = kmem_cache_alloc(dm_tokdata_cachep, GFP_KERNEL); ++ if (tdp == NULL){ ++ printk("%s/%d: kmem_cache_alloc(dm_tokdata_cachep) returned NULL\n", __FUNCTION__, __LINE__); ++ return(-ENOMEM); ++ } ++ memset(tdp, 0, sizeof(*tdp)); ++ ++ *lcp = mutex_spinlock(&tevp->te_lock); ++ ++ for (tmp = tevp->te_tdp; tmp; tmp = tmp->td_next) { ++ if (DM_HANDLE_CMP(&tmp->td_handle, handlep) == 0) ++ break; ++ } ++ if (tmp) { ++ kmem_cache_free(dm_tokdata_cachep, tdp); ++ continue; ++ } ++ ++ tdp->td_next = tevp->te_tdp; ++ tevp->te_tdp = tdp; ++ tdp->td_tevp = tevp; ++ tdp->td_handle = *handlep; ++ } ++ ++ /* Temporarily single-thread access to the tdp so that other ++ threads don't touch it while we are filling the rest of the ++ fields in. ++ */ ++ ++ tdp->td_app_ref = 1; ++ tdp->td_flags |= DM_TDF_STHREAD; ++ ++ /* Drop the spinlock while we access, validate, and obtain the ++ proper rights to the object. This can take a very long time ++ if the inode is not in memory, if the filesystem is ++ unmounting, or if the request_right() call should block ++ because some other tdp or kernel thread is holding a right. ++ */ ++ ++ mutex_spinunlock(&tevp->te_lock, *lcp); ++ ++ if ((ip = dm_handle_to_ip(handlep, &tdp->td_type)) == NULL) { ++ error = -EBADF; ++ } else { ++ tdp->td_vcount = 1; ++ tdp->td_ip = ip; ++ ++ /* The handle is usable. Check that the type of the ++ object matches one of the types that the caller ++ will accept. ++ */ ++ ++ if (!(types & tdp->td_type)) { ++ error = -EOPNOTSUPP; ++ } else if (right > DM_RIGHT_NULL) { ++ /* Attempt to get the rights required by the ++ caller. If rights can't be obtained, return ++ an error. ++ */ ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->request_right(tdp->td_ip, ++ DM_RIGHT_NULL, ++ (tdp->td_type == DM_TDT_VFS ? ++ DM_FSYS_OBJ : 0), ++ DM_RR_WAIT, right); ++ if (!error) { ++ tdp->td_right = right; ++ } ++ } else { ++ error = 0; ++ } ++ } ++ if (error != 0) { ++ dm_put_tevp(tevp, tdp); /* destroy event risk, although tiny */ ++ return(error); ++ } ++ ++ *lcp = mutex_spinlock(&tevp->te_lock); ++ ++ /* Wake up any threads which may have seen our tdp while we ++ were filling it in. ++ */ ++ ++ if (!(flags & DM_FG_STHREAD)) { ++ tdp->td_flags &= ~DM_TDF_STHREAD; ++ if (tevp->te_app_slp) ++ sv_broadcast(&tevp->te_app_queue); ++ } ++ ++ mutex_spinunlock(&tevp->te_lock, *lcp); ++ *tdpp = tdp; ++ return(0); ++ } ++} ++ ++ ++/* dm_app_get_tdp_by_token() is called whenever the application request ++ contains a session ID and contains a token other than DM_NO_TOKEN. ++ Most of the callers provide a right that is either DM_RIGHT_SHARED or ++ DM_RIGHT_EXCL, but a few of the callers such as dm_obj_ref_hold() may ++ specify a right of DM_RIGHT_NULL. ++*/ ++ ++static int ++dm_app_get_tdp_by_token( ++ dm_sessid_t sid, /* an existing session ID */ ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, /* an existing token */ ++ short types, /* acceptable object types */ ++ dm_right_t right, /* minimum right the object must have */ ++ u_int flags, ++ dm_tokdata_t **tdpp) ++{ ++ dm_tokevent_t *tevp; ++ dm_handle_t handle; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ if (right < DM_RIGHT_NULL || right > DM_RIGHT_EXCL) ++ return(-EINVAL); ++ ++ if ((error = dm_copyin_handle(hanp, hlen, &handle)) != 0) ++ return(error); ++ ++ /* Find and lock the event which corresponds to the specified ++ session/token pair. ++ */ ++ ++ if ((error = dm_find_msg_and_lock(sid, token, &tevp, &lc)) != 0) ++ return(error); ++ ++ return(dm_app_lookup_tdp(&handle, tevp, &lc, types, ++ right, flags, tdpp)); ++} ++ ++ ++/* Function dm_app_get_tdp() must ONLY be called from routines associated with ++ application calls, e.g. dm_read_invis, dm_set_disp, etc. It must not be ++ called by a thread responsible for generating an event such as ++ dm_send_data_event()! ++ ++ dm_app_get_tdp() is the interface used by all application calls other than ++ dm_get_events, dm_respond_event, dm_get_config, dm_get_config_events, and by ++ the dm_obj_ref_* and dm_*_right families of requests. ++ ++ dm_app_get_tdp() converts a sid/hanp/hlen/token quad into a tdp pointer, ++ increments the number of active application threads in the event, and ++ increments the number of active application threads using the tdp. The ++ 'right' parameter must be either DM_RIGHT_SHARED or DM_RIGHT_EXCL. The ++ token may either be DM_NO_TOKEN, or can be a token received in a synchronous ++ event. ++*/ ++ ++int ++dm_app_get_tdp( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ short types, ++ dm_right_t right, /* minimum right */ ++ dm_tokdata_t **tdpp) ++{ ++ dm_session_t *s; ++ dm_handle_t handle; ++ dm_tokevent_t *tevp; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ ASSERT(right >= DM_RIGHT_SHARED); ++ ++ /* If a token other than DM_NO_TOKEN is specified, find the event on ++ this session which owns the token and increment its reference count. ++ */ ++ ++ if (token != DM_NO_TOKEN) { /* look up existing tokevent struct */ ++ return(dm_app_get_tdp_by_token(sid, hanp, hlen, token, types, ++ right, DM_FG_MUSTEXIST, tdpp)); ++ } ++ ++ /* The token is DM_NO_TOKEN. In this case we only want to verify that ++ the session ID is valid, and do not need to continue holding the ++ session lock after we know that to be true. ++ */ ++ ++ if ((error = dm_copyin_handle(hanp, hlen, &handle)) != 0) ++ return(error); ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0) ++ return(error); ++ mutex_spinunlock(&s->sn_qlock, lc); ++ ++ /* When DM_NO_TOKEN is used, we simply block until we can obtain the ++ right that we want (since the tevp contains no tdp structures). ++ The blocking when we eventually support it will occur within ++ fsys_vector->request_right(). ++ */ ++ ++ tevp = dm_init_tevp(0, 0); ++ lc = mutex_spinlock(&tevp->te_lock); ++ ++ return(dm_app_lookup_tdp(&handle, tevp, &lc, types, right, 0, tdpp)); ++} ++ ++ ++/* dm_get_config_tdp() is only called by dm_get_config() and ++ dm_get_config_events(), which neither have a session ID nor a token. ++ Both of these calls are supposed to work even if the filesystem is in the ++ process of being mounted, as long as the caller only uses handles within ++ the mount event. ++*/ ++ ++int ++dm_get_config_tdp( ++ void __user *hanp, ++ size_t hlen, ++ dm_tokdata_t **tdpp) ++{ ++ dm_handle_t handle; ++ dm_tokevent_t *tevp; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ if ((error = dm_copyin_handle(hanp, hlen, &handle)) != 0) ++ return(error); ++ ++ tevp = dm_init_tevp(0, 0); ++ lc = mutex_spinlock(&tevp->te_lock); ++ ++ /* Try to use the handle provided by the caller and assume DM_NO_TOKEN. ++ This will fail if the filesystem is in the process of being mounted. ++ */ ++ ++ error = dm_app_lookup_tdp(&handle, tevp, &lc, DM_TDT_ANY, ++ DM_RIGHT_NULL, 0, tdpp); ++ ++ if (!error) { ++ return(0); ++ } ++ ++ /* Perhaps the filesystem is still mounting, in which case we need to ++ see if this is one of the handles in the DM_EVENT_MOUNT tevp. ++ */ ++ ++ if ((tevp = dm_find_mount_tevp_and_lock(&handle.ha_fsid, &lc)) == NULL) ++ return(-EBADF); ++ ++ return(dm_app_lookup_tdp(&handle, tevp, &lc, DM_TDT_ANY, ++ DM_RIGHT_NULL, DM_FG_MUSTEXIST, tdpp)); ++} ++ ++ ++/* dm_put_tdp() is called to release any right held on the inode, and to ++ VN_RELE() all references held on the inode. It is the caller's ++ responsibility to ensure that no other application threads are using the ++ tdp, and if necessary to unlink the tdp from the tevp before calling ++ this routine and to free the tdp afterwards. ++*/ ++ ++static void ++dm_put_tdp( ++ dm_tokdata_t *tdp) ++{ ++ ASSERT(tdp->td_app_ref <= 1); ++ ++ /* If the application thread is holding a right, or if the event ++ thread had a right but it has disappeared because of a dm_pending ++ or Cntl-C, then we need to release it here. ++ */ ++ ++ if (tdp->td_right != DM_RIGHT_NULL) { ++ dm_fsys_vector_t *fsys_vector; ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ (void)fsys_vector->release_right(tdp->td_ip, tdp->td_right, ++ (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0)); ++ tdp->td_right = DM_RIGHT_NULL; ++ } ++ ++ /* Given that we wouldn't be here if there was still an event thread, ++ this VN_RELE loop has the potential of generating a DM_EVENT_DESTROY ++ event if some other thread has unlinked the file. ++ */ ++ ++ while (tdp->td_vcount > 0) { ++ iput(tdp->td_ip); ++ tdp->td_vcount--; ++ } ++ ++ tdp->td_flags &= ~(DM_TDF_HOLD|DM_TDF_RIGHT); ++ tdp->td_ip = NULL; ++} ++ ++ ++/* Function dm_put_tevp() must ONLY be called from routines associated with ++ application threads, e.g. dm_read_invis, dm_get_events, etc. It must not be ++ called by a thread responsible for generating an event, such as ++ dm_send_data_event. ++ ++ PLEASE NOTE: It is possible for this routine to generate DM_EVENT_DESTROY ++ events, because its calls to dm_put_tdp drop inode references, and another ++ thread may have already unlinked a file whose inode we are de-referencing. ++ This sets the stage for various types of deadlock if the thread calling ++ dm_put_tevp is the same thread that calls dm_respond_event! In particular, ++ the dm_sent_destroy_event routine needs to obtain the dm_reg_lock, ++ dm_session_lock, and sn_qlock in order to queue the destroy event. No ++ caller of dm_put_tevp can hold any of these locks! ++ ++ Other possible deadlocks are that dm_send_destroy_event could block waiting ++ for a thread to register for the event using dm_set_disp() and/or ++ dm_set_return_on_destroy, or it could block because the session's sn_newq ++ is at the dm_max_queued_msgs event limit. The only safe solution ++ (unimplemented) is to have a separate kernel thread for each filesystem ++ whose only job is to do the inode-dereferencing. That way dm_respond_event ++ will not block, so the application can keep calling dm_get_events to read ++ events even if the filesystem thread should block. (If the filesystem ++ thread blocks, so will all subsequent destroy events for the same ++ filesystem.) ++*/ ++ ++void ++dm_put_tevp( ++ dm_tokevent_t *tevp, ++ dm_tokdata_t *tdp) ++{ ++ int free_tdp = 0; ++ unsigned long lc; /* lock cookie */ ++ ++ lc = mutex_spinlock(&tevp->te_lock); ++ ++ if (tdp != NULL) { ++ if (tdp->td_vcount > 1 || (tdp->td_flags & DM_TDF_EVTREF)) { ++ ASSERT(tdp->td_app_ref > 0); ++ ++ iput(tdp->td_ip); ++ tdp->td_vcount--; ++ } else { ++ ASSERT(tdp->td_app_ref == 1); ++ ++ /* The inode reference count is either already at ++ zero (e.g. a failed dm_handle_to_ip() call in ++ dm_app_lookup_tdp()) or is going to zero. We can't ++ hold the lock while we decrement the count because ++ we could potentially end up being busy for a long ++ time in VOP_INACTIVATE. Use single-threading to ++ lock others out while we clean house. ++ */ ++ ++ tdp->td_flags |= DM_TDF_STHREAD; ++ ++ /* WARNING - A destroy event is possible here if we are ++ giving up the last reference on an inode which has ++ been previously unlinked by some other thread! ++ */ ++ ++ mutex_spinunlock(&tevp->te_lock, lc); ++ dm_put_tdp(tdp); ++ lc = mutex_spinlock(&tevp->te_lock); ++ ++ /* If this tdp is not one of the original tdps in the ++ event, then remove it from the tevp. ++ */ ++ ++ if (!(tdp->td_flags & DM_TDF_ORIG)) { ++ dm_tokdata_t **tdpp = &tevp->te_tdp; ++ ++ while (*tdpp && *tdpp != tdp) { ++ tdpp = &(*tdpp)->td_next; ++ } ++ if (*tdpp == NULL) { ++ panic("dm_remove_tdp_from_tevp: tdp " ++ "%p not in tevp %p\n", tdp, ++ tevp); ++ } ++ *tdpp = tdp->td_next; ++ free_tdp++; ++ } ++ } ++ ++ /* If this is the last app thread actively using the tdp, clear ++ any single-threading and wake up any other app threads who ++ might be waiting to use this tdp, single-threaded or ++ otherwise. ++ */ ++ ++ if (--tdp->td_app_ref == 0) { ++ if (tdp->td_flags & DM_TDF_STHREAD) { ++ tdp->td_flags &= ~DM_TDF_STHREAD; ++ if (tevp->te_app_slp) ++ sv_broadcast(&tevp->te_app_queue); ++ } ++ } ++ ++ if (free_tdp) { ++ kmem_cache_free(dm_tokdata_cachep, tdp); ++ } ++ } ++ ++ /* If other application threads are using this token/event, they will ++ do the cleanup. ++ */ ++ ++ if (--tevp->te_app_ref > 0) { ++ mutex_spinunlock(&tevp->te_lock, lc); ++ return; ++ } ++ ++ /* If event generation threads are waiting for this thread to go away, ++ wake them up and let them do the cleanup. ++ */ ++ ++ if (tevp->te_evt_ref > 0) { ++ sv_broadcast(&tevp->te_evt_queue); ++ mutex_spinunlock(&tevp->te_lock, lc); ++ return; ++ } ++ ++ /* This thread is the last active thread using the token/event. No ++ lock can be held while we disassemble the tevp because we could ++ potentially end up being busy for a long time in VOP_INACTIVATE. ++ */ ++ ++ mutex_spinunlock(&tevp->te_lock, lc); ++ ++ /* WARNING - One or more destroy events are possible here if we are ++ giving up references on inodes which have been previously unlinked ++ by other kernel threads! ++ */ ++ ++ while ((tdp = tevp->te_tdp) != NULL) { ++ tevp->te_tdp = tdp->td_next; ++ dm_put_tdp(tdp); ++ kmem_cache_free(dm_tokdata_cachep, tdp); ++ } ++ spinlock_destroy(&tevp->te_lock); ++ sv_destroy(&tevp->te_evt_queue); ++ sv_destroy(&tevp->te_app_queue); ++ kfree(tevp); ++} ++ ++ ++/* No caller of dm_app_put_tevp can hold either of the locks dm_reg_lock, ++ dm_session_lock, or any sn_qlock! (See dm_put_tevp for details.) ++*/ ++ ++void ++dm_app_put_tdp( ++ dm_tokdata_t *tdp) ++{ ++ dm_put_tevp(tdp->td_tevp, tdp); ++} ++ ++ ++/* dm_change_right is only called if the event thread is the one doing the ++ cleanup on a completed event. It looks at the current rights of a tdp ++ and compares that with the rights it had on the tdp when the event was ++ created. If different, it reaquires the original rights, then transfers ++ the rights back to being thread-based. ++*/ ++ ++static void ++dm_change_right( ++ dm_tokdata_t *tdp) ++{ ++#ifdef HAVE_DMAPI_RIGHTS ++ dm_fsys_vector_t *fsys_vector; ++ int error; ++ u_int type; ++#endif ++ ++ /* If the event doesn't have an inode reference, if the original right ++ was DM_RIGHT_NULL, or if the rights were never switched from being ++ thread-based to tdp-based, then there is nothing to do. ++ */ ++ ++ if (!(tdp->td_flags & DM_TDF_EVTREF)) ++ return; ++ ++ if (tdp->td_orig_right == DM_RIGHT_NULL) ++ return; ++ ++ /* DEBUG - Need a check here for event-based rights. */ ++ ++#ifdef HAVE_DMAPI_RIGHTS ++ /* The "rights" vectors are stubs now anyway. When they are ++ * implemented then bhv locking will have to be sorted out. ++ */ ++ ++ /* If the current right is not the same as it was when the event was ++ created, first get back the original right. ++ */ ++ ++ if (tdp->td_right != tdp->td_orig_right) { ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ type = (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0); ++ ++ switch (tdp->td_orig_right) { ++ case DM_RIGHT_SHARED: ++ if (tdp->td_right == DM_RIGHT_EXCL) { ++ error = fsys_vector->downgrade_right( ++ tdp->td_ip, tdp->td_right, type); ++ if (!error) ++ break; ++ (void)fsys_vector->release_right(tdp->td_ip, ++ tdp->td_right, type); ++ } ++ (void)fsys_vector->request_right(tdp->td_ip, ++ tdp->td_right, type, DM_RR_WAIT, ++ tdp->td_orig_right); ++ break; ++ ++ case DM_RIGHT_EXCL: ++ if (tdp->td_right == DM_RIGHT_SHARED) { ++ error = fsys_vector->upgrade_right(tdp->td_ip, ++ tdp->td_right, type); ++ if (!error) ++ break; ++ (void)fsys_vector->release_right(tdp->td_ip, ++ tdp->td_right, type); ++ } ++ (void)fsys_vector->request_right(tdp->td_ip, ++ tdp->td_right, type, DM_RR_WAIT, ++ tdp->td_orig_right); ++ break; ++ case DM_RIGHT_NULL: ++ break; ++ } ++ } ++#endif ++ ++ /* We now have back the same level of rights as we had when the event ++ was generated. Now transfer the rights from being tdp-based back ++ to thread-based. ++ */ ++ ++ /* DEBUG - Add a call here to transfer rights back to thread-based. */ ++ ++ /* Finally, update the tdp so that we don't mess with the rights when ++ we eventually call dm_put_tdp. ++ */ ++ ++ tdp->td_right = DM_RIGHT_NULL; ++} ++ ++ ++/* This routine is only called by event threads. The calls to dm_put_tdp ++ are not a deadlock risk here because this is an event thread, and it is ++ okay for such a thread to block on an induced destroy event. Okay, maybe ++ there is a slight risk; say that the event contains three inodes all of ++ which have DM_RIGHT_EXCL, and say that we are at the dm_max_queued_msgs ++ limit, and that the first inode is already unlinked. In that case the ++ destroy event will block waiting to be queued, and the application thread ++ could happen to reference one of the other locked inodes. Deadlock. ++*/ ++ ++void ++dm_evt_rele_tevp( ++ dm_tokevent_t *tevp, ++ int droprights) /* non-zero, evt thread loses rights */ ++{ ++ dm_tokdata_t *tdp; ++ unsigned long lc; /* lock cookie */ ++ ++ lc = mutex_spinlock(&tevp->te_lock); ++ ++ /* If we are here without DM_TEF_FINAL set and with at least one ++ application reference still remaining, then one of several ++ possibilities is true: ++ 1. This is an asynchronous event which has been queued but has not ++ yet been delivered, or which is in the process of being delivered. ++ 2. This is an unmount event (pseudo-asynchronous) yet to be ++ delivered or in the process of being delivered. ++ 3. This event had DM_FLAGS_NDELAY specified, and the application ++ has sent a dm_pending() reply for the event. ++ 4. This is a DM_EVENT_READ, DM_EVENT_WRITE, or DM_EVENT_TRUNCATE ++ event and the user typed a Cntl-C. ++ In all of these cases, the correct behavior is to leave the ++ responsibility of releasing any rights to the application threads ++ when they are done. ++ */ ++ ++ if (tevp->te_app_ref > 0 && !(tevp->te_flags & DM_TEF_FINAL)) { ++ tevp->te_evt_ref--; ++ for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) { ++ if (tdp->td_flags & DM_TDF_EVTREF) { ++ tdp->td_flags &= ~DM_TDF_EVTREF; ++ if (tdp->td_vcount == 0) { ++ tdp->td_ip = NULL; ++ } ++ } ++ } ++ mutex_spinunlock(&tevp->te_lock, lc); ++ return; /* not the last thread */ ++ } ++ ++ /* If the application reference count is non-zero here, that can only ++ mean that dm_respond_event() has been called, but the application ++ still has one or more threads in the kernel that haven't let go of ++ the tevp. In these cases, the event thread must wait until all ++ application threads have given up their references, and their ++ rights to handles within the event. ++ */ ++ ++ while (tevp->te_app_ref) { ++ sv_wait(&tevp->te_evt_queue, 1, &tevp->te_lock, lc); ++ lc = mutex_spinlock(&tevp->te_lock); ++ } ++ ++ /* This thread is the last active thread using the token/event. Reset ++ the rights of any inode that was part of the original event back ++ to their initial values before returning to the filesystem. The ++ exception is if the event failed (droprights is non-zero), in which ++ case we chose to return to the filesystem with all rights released. ++ Release the rights on any inode that was not part of the original ++ event. Give up all remaining application inode references ++ regardless of whether or not the inode was part of the original ++ event. ++ */ ++ ++ mutex_spinunlock(&tevp->te_lock, lc); ++ ++ while ((tdp = tevp->te_tdp) != NULL) { ++ tevp->te_tdp = tdp->td_next; ++ if ((tdp->td_flags & DM_TDF_ORIG) && ++ (tdp->td_flags & DM_TDF_EVTREF) && ++ (!droprights)) { ++ dm_change_right(tdp); ++ } ++ dm_put_tdp(tdp); ++ kmem_cache_free(dm_tokdata_cachep, tdp); ++ } ++ spinlock_destroy(&tevp->te_lock); ++ sv_destroy(&tevp->te_evt_queue); ++ sv_destroy(&tevp->te_app_queue); ++ kfree(tevp); ++} ++ ++ ++/* dm_obj_ref_hold() is just a fancy way to get an inode reference on an object ++ to hold it in kernel memory. ++*/ ++ ++int ++dm_obj_ref_hold( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_NULL, DM_FG_STHREAD, &tdp); ++ ++ /* The tdp is single-threaded, so no mutex lock needed for update. */ ++ ++ if (error == 0) { ++ if (tdp->td_flags & DM_TDF_HOLD) { /* if already held */ ++ error = -EBUSY; ++ } else { ++ tdp->td_flags |= DM_TDF_HOLD; ++ tdp->td_vcount++; ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ (void)fsys_vector->obj_ref_hold(tdp->td_ip); ++ } ++ dm_app_put_tdp(tdp); ++ } ++ return(error); ++} ++ ++ ++int ++dm_obj_ref_rele( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen) ++{ ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_NULL, DM_FG_MUSTEXIST|DM_FG_STHREAD, &tdp); ++ ++ /* The tdp is single-threaded, so no mutex lock needed for update. */ ++ ++ if (error == 0) { ++ if (!(tdp->td_flags & DM_TDF_HOLD)) { /* if not held */ ++ error = -EACCES; /* use the DM_FG_MUSTEXIST errno */ ++ } else { ++ tdp->td_flags &= ~DM_TDF_HOLD; ++ iput(tdp->td_ip); ++ tdp->td_vcount--; ++ } ++ dm_app_put_tdp(tdp); ++ } ++ return(error); ++} ++ ++ ++int ++dm_obj_ref_query_rvp( ++ dm_sessid_t sid, ++ dm_token_t token, ++ void __user *hanp, ++ size_t hlen, ++ int *rvp) ++{ ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_VNO, ++ DM_RIGHT_NULL, DM_FG_DONTADD|DM_FG_STHREAD, &tdp); ++ if (error != 0) ++ return(error); ++ ++ /* If the request is valid but the handle just isn't present in the ++ event or the hold flag isn't set, return zero, else return one. ++ */ ++ ++ if (tdp) { ++ if (tdp->td_flags & DM_TDF_HOLD) { /* if held */ ++ *rvp = 1; ++ } else { ++ *rvp = 0; ++ } ++ dm_app_put_tdp(tdp); ++ } else { ++ *rvp = 0; ++ } ++ return(0); ++} ++ ++ ++int ++dm_downgrade_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY, ++ DM_RIGHT_EXCL, DM_FG_MUSTEXIST|DM_FG_STHREAD, &tdp); ++ if (error != 0) ++ return(error); ++ ++ /* Attempt the downgrade. Filesystems which support rights but not ++ the downgrading of rights will return ENOSYS. ++ */ ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->downgrade_right(tdp->td_ip, tdp->td_right, ++ (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0)); ++ ++ /* The tdp is single-threaded, so no mutex lock needed for update. */ ++ ++ if (error == 0) ++ tdp->td_right = DM_RIGHT_SHARED; ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_query_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ dm_right_t __user *rightp) ++{ ++ dm_tokdata_t *tdp; ++ dm_right_t right; ++ int error; ++ ++ error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY, ++ DM_RIGHT_NULL, DM_FG_DONTADD|DM_FG_STHREAD, &tdp); ++ if (error != 0) ++ return(error); ++ ++ /* Get the current right and copy it to the caller. The tdp is ++ single-threaded, so no mutex lock is needed. If the tdp is not in ++ the event we are supposed to return DM_RIGHT_NULL in order to be ++ compatible with Veritas. ++ */ ++ ++ if (tdp) { ++ right = tdp->td_right; ++ dm_app_put_tdp(tdp); ++ } else { ++ right = DM_RIGHT_NULL; ++ } ++ if (copy_to_user(rightp, &right, sizeof(right))) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++int ++dm_release_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY, ++ DM_RIGHT_SHARED, DM_FG_MUSTEXIST|DM_FG_STHREAD, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->release_right(tdp->td_ip, tdp->td_right, ++ (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0)); ++ ++ /* The tdp is single-threaded, so no mutex lock needed for update. */ ++ ++ if (error == 0) { ++ tdp->td_right = DM_RIGHT_NULL; ++ if (tdp->td_flags & DM_TDF_RIGHT) { ++ tdp->td_flags &= ~DM_TDF_RIGHT; ++ iput(tdp->td_ip); ++ tdp->td_vcount--; ++ } ++ } ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_request_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token, ++ u_int flags, ++ dm_right_t right) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY, ++ DM_RIGHT_NULL, DM_FG_STHREAD, &tdp); ++ if (error != 0) ++ return(error); ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->request_right(tdp->td_ip, tdp->td_right, ++ (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0), flags, right); ++ ++ /* The tdp is single-threaded, so no mutex lock is needed for update. ++ ++ If this is the first dm_request_right call for this inode, then we ++ need to bump the inode reference count for two reasons. First of ++ all, it is supposed to be impossible for the file to disappear or ++ for the filesystem to be unmounted while a right is held on a file; ++ bumping the file's inode reference count ensures this. Second, if ++ rights are ever actually implemented, it will most likely be done ++ without changes to the on-disk inode, which means that we can't let ++ the inode become unreferenced while a right on it is held. ++ */ ++ ++ if (error == 0) { ++ if (!(tdp->td_flags & DM_TDF_RIGHT)) { /* if first call */ ++ tdp->td_flags |= DM_TDF_RIGHT; ++ tdp->td_vcount++; ++ (void)fsys_vector->obj_ref_hold(tdp->td_ip); ++ } ++ tdp->td_right = right; ++ } ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} ++ ++ ++int ++dm_upgrade_right( ++ dm_sessid_t sid, ++ void __user *hanp, ++ size_t hlen, ++ dm_token_t token) ++{ ++ dm_fsys_vector_t *fsys_vector; ++ dm_tokdata_t *tdp; ++ int error; ++ ++ error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY, ++ DM_RIGHT_SHARED, DM_FG_MUSTEXIST|DM_FG_STHREAD, &tdp); ++ if (error != 0) ++ return(error); ++ ++ /* If the object already has the DM_RIGHT_EXCL right, no need to ++ attempt an upgrade. ++ */ ++ ++ if (tdp->td_right == DM_RIGHT_EXCL) { ++ dm_app_put_tdp(tdp); ++ return(0); ++ } ++ ++ /* Attempt the upgrade. Filesystems which support rights but not ++ the upgrading of rights will return ENOSYS. ++ */ ++ ++ fsys_vector = dm_fsys_vector(tdp->td_ip); ++ error = fsys_vector->upgrade_right(tdp->td_ip, tdp->td_right, ++ (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0)); ++ ++ /* The tdp is single-threaded, so no mutex lock needed for update. */ ++ ++ if (error == 0) ++ tdp->td_right = DM_RIGHT_EXCL; ++ ++ dm_app_put_tdp(tdp); ++ return(error); ++} +--- /dev/null ++++ b/fs/dmapi/dmapi_session.c +@@ -0,0 +1,1824 @@ ++/* ++ * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++ ++#include ++#include ++#include ++#ifdef CONFIG_PROC_FS ++#include ++#endif ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++dm_session_t *dm_sessions = NULL; /* head of session list */ ++u_int dm_sessions_active = 0; /* # sessions currently active */ ++dm_sessid_t dm_next_sessid = 1; /* next session ID to use */ ++lock_t dm_session_lock = SPIN_LOCK_UNLOCKED;/* lock for session list */ ++ ++dm_token_t dm_next_token = 1; /* next token ID to use */ ++dm_sequence_t dm_next_sequence = 1; /* next sequence number to use */ ++lock_t dm_token_lock = SPIN_LOCK_UNLOCKED;/* dm_next_token/dm_next_sequence lock */ ++ ++int dm_max_queued_msgs = 2048; /* max # undelivered msgs/session */ ++ ++int dm_hash_buckets = 1009; /* prime -- number of buckets */ ++ ++#define DM_SHASH(sess,inodenum) \ ++ ((sess)->sn_sesshash + do_mod((inodenum), dm_hash_buckets)) ++ ++ ++#ifdef CONFIG_PROC_FS ++static int ++sessions_read_pfs(char *buffer, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int len; ++ dm_session_t *sessp = (dm_session_t*)data; ++ ++#define CHKFULL if(len >= count) break; ++#define ADDBUF(a,b) len += sprintf(buffer + len, a, b); CHKFULL; ++ ++ len=0; ++ while(1){ ++ ADDBUF("sessp=0x%p\n", sessp); ++ ADDBUF("sn_next=0x%p\n", sessp->sn_next); ++ ADDBUF("sn_sessid=%d\n", sessp->sn_sessid); ++ ADDBUF("sn_flags=%x\n", sessp->sn_flags); ++ ADDBUF("sn_qlock=%c\n", '?'); ++ ADDBUF("sn_readerq=%c\n", '?'); ++ ADDBUF("sn_writerq=%c\n", '?'); ++ ADDBUF("sn_readercnt=%u\n", sessp->sn_readercnt); ++ ADDBUF("sn_writercnt=%u\n", sessp->sn_writercnt); ++ ++ ADDBUF("sn_newq.eq_head=0x%p\n", sessp->sn_newq.eq_head); ++ ADDBUF("sn_newq.eq_tail=0x%p\n", sessp->sn_newq.eq_tail); ++ ADDBUF("sn_newq.eq_count=%d\n", sessp->sn_newq.eq_count); ++ ++ ADDBUF("sn_delq.eq_head=0x%p\n", sessp->sn_delq.eq_head); ++ ADDBUF("sn_delq.eq_tail=0x%p\n", sessp->sn_delq.eq_tail); ++ ADDBUF("sn_delq.eq_count=%d\n", sessp->sn_delq.eq_count); ++ ++ ADDBUF("sn_evt_writerq.eq_head=0x%p\n", sessp->sn_evt_writerq.eq_head); ++ ADDBUF("sn_evt_writerq.eq_tail=0x%p\n", sessp->sn_evt_writerq.eq_tail); ++ ADDBUF("sn_evt_writerq.eq_count=%d\n", sessp->sn_evt_writerq.eq_count); ++ ++ ADDBUF("sn_info=\"%s\"\n", sessp->sn_info); ++ ++ break; ++ } ++ ++ if (offset >= len) { ++ *start = buffer; ++ *eof = 1; ++ return 0; ++ } ++ *start = buffer + offset; ++ if ((len -= offset) > count) ++ return count; ++ *eof = 1; ++ ++ return len; ++} ++#endif ++ ++ ++/* Link a session to the end of the session list. New sessions are always ++ added at the end of the list so that dm_enqueue_mount_event() doesn't ++ miss a session. The caller must have obtained dm_session_lock before ++ calling this routine. ++*/ ++ ++static void ++link_session( ++ dm_session_t *s) ++{ ++ dm_session_t *tmp; ++ ++ if ((tmp = dm_sessions) == NULL) { ++ dm_sessions = s; ++ } else { ++ while (tmp->sn_next != NULL) ++ tmp = tmp->sn_next; ++ tmp->sn_next = s; ++ } ++ s->sn_next = NULL; ++ dm_sessions_active++; ++} ++ ++ ++/* Remove a session from the session list. The caller must have obtained ++ dm_session_lock before calling this routine. unlink_session() should only ++ be used in situations where the session is known to be on the dm_sessions ++ list; otherwise it panics. ++*/ ++ ++static void ++unlink_session( ++ dm_session_t *s) ++{ ++ dm_session_t *tmp; ++ ++ if (dm_sessions == s) { ++ dm_sessions = dm_sessions->sn_next; ++ } else { ++ for (tmp = dm_sessions; tmp; tmp = tmp->sn_next) { ++ if (tmp->sn_next == s) ++ break; ++ } ++ if (tmp == NULL) { ++ panic("unlink_session: corrupt DMAPI session list, " ++ "dm_sessions %p, session %p\n", ++ dm_sessions, s); ++ } ++ tmp->sn_next = s->sn_next; ++ } ++ s->sn_next = NULL; ++ dm_sessions_active--; ++} ++ ++ ++/* Link an event to the end of an event queue. The caller must have obtained ++ the session's sn_qlock before calling this routine. ++*/ ++ ++void ++dm_link_event( ++ dm_tokevent_t *tevp, ++ dm_eventq_t *queue) ++{ ++ if (queue->eq_tail) { ++ queue->eq_tail->te_next = tevp; ++ queue->eq_tail = tevp; ++ } else { ++ queue->eq_head = queue->eq_tail = tevp; ++ } ++ tevp->te_next = NULL; ++ queue->eq_count++; ++} ++ ++ ++/* Remove an event from an event queue. The caller must have obtained the ++ session's sn_qlock before calling this routine. dm_unlink_event() should ++ only be used in situations where the event is known to be on the queue; ++ otherwise it panics. ++*/ ++ ++void ++dm_unlink_event( ++ dm_tokevent_t *tevp, ++ dm_eventq_t *queue) ++{ ++ dm_tokevent_t *tmp; ++ ++ if (queue->eq_head == tevp) { ++ queue->eq_head = tevp->te_next; ++ if (queue->eq_head == NULL) ++ queue->eq_tail = NULL; ++ } else { ++ tmp = queue->eq_head; ++ while (tmp && tmp->te_next != tevp) ++ tmp = tmp->te_next; ++ if (tmp == NULL) { ++ panic("dm_unlink_event: corrupt DMAPI queue %p, " ++ "tevp %p\n", queue, tevp); ++ } ++ tmp->te_next = tevp->te_next; ++ if (tmp->te_next == NULL) ++ queue->eq_tail = tmp; ++ } ++ tevp->te_next = NULL; ++ queue->eq_count--; ++} ++ ++/* Link a regular file event to a hash bucket. The caller must have obtained ++ the session's sn_qlock before calling this routine. ++ The tokevent must be for a regular file object--DM_TDT_REG. ++*/ ++ ++static void ++hash_event( ++ dm_session_t *s, ++ dm_tokevent_t *tevp) ++{ ++ dm_sesshash_t *sh; ++ dm_ino_t ino; ++ ++ if (s->sn_sesshash == NULL) { ++ s->sn_sesshash = kmalloc(dm_hash_buckets * sizeof(dm_sesshash_t), GFP_KERNEL); ++ if (s->sn_sesshash == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return; ++ } ++ memset(s->sn_sesshash, 0, dm_hash_buckets * sizeof(dm_sesshash_t)); ++ } ++ ++ ino = (&tevp->te_tdp->td_handle.ha_fid)->dm_fid_ino; ++ sh = DM_SHASH(s, ino); ++ ++#ifdef DM_SHASH_DEBUG ++ if (sh->h_next == NULL) { ++ s->sn_buckets_in_use++; ++ if (s->sn_buckets_in_use > s->sn_max_buckets_in_use) ++ s->sn_max_buckets_in_use++; ++ } ++ sh->maxlength++; ++ sh->curlength++; ++ sh->num_adds++; ++#endif ++ ++ tevp->te_flags |= DM_TEF_HASHED; ++ tevp->te_hashnext = sh->h_next; ++ sh->h_next = tevp; ++} ++ ++ ++/* Remove a regular file event from a hash bucket. The caller must have ++ obtained the session's sn_qlock before calling this routine. ++ The tokevent must be for a regular file object--DM_TDT_REG. ++*/ ++ ++static void ++unhash_event( ++ dm_session_t *s, ++ dm_tokevent_t *tevp) ++{ ++ dm_sesshash_t *sh; ++ dm_tokevent_t *tmp; ++ dm_ino_t ino; ++ ++ if (s->sn_sesshash == NULL) ++ return; ++ ++ ino = (&tevp->te_tdp->td_handle.ha_fid)->dm_fid_ino; ++ sh = DM_SHASH(s, ino); ++ ++ if (sh->h_next == tevp) { ++ sh->h_next = tevp->te_hashnext; /* leap frog */ ++ } else { ++ tmp = sh->h_next; ++ while (tmp->te_hashnext != tevp) { ++ tmp = tmp->te_hashnext; ++ } ++ tmp->te_hashnext = tevp->te_hashnext; /* leap frog */ ++ } ++ tevp->te_hashnext = NULL; ++ tevp->te_flags &= ~DM_TEF_HASHED; ++ ++#ifdef DM_SHASH_DEBUG ++ if (sh->h_next == NULL) ++ s->sn_buckets_in_use--; ++ sh->curlength--; ++ sh->num_dels++; ++#endif ++} ++ ++ ++/* Determine if this is a repeat event. The caller MUST be holding ++ the session lock. ++ The tokevent must be for a regular file object--DM_TDT_REG. ++ Returns: ++ 0 == match not found ++ 1 == match found ++*/ ++ ++static int ++repeated_event( ++ dm_session_t *s, ++ dm_tokevent_t *tevp) ++{ ++ dm_sesshash_t *sh; ++ dm_data_event_t *d_event1; ++ dm_data_event_t *d_event2; ++ dm_tokevent_t *tevph; ++ dm_ino_t ino1; ++ dm_ino_t ino2; ++ ++ if ((!s->sn_newq.eq_tail) && (!s->sn_delq.eq_tail)) { ++ return(0); ++ } ++ if (s->sn_sesshash == NULL) { ++ return(0); ++ } ++ ++ ino1 = (&tevp->te_tdp->td_handle.ha_fid)->dm_fid_ino; ++ sh = DM_SHASH(s, ino1); ++ ++ if (sh->h_next == NULL) { ++ /* bucket is empty, no match here */ ++ return(0); ++ } ++ ++ d_event1 = (dm_data_event_t *)((char *)&tevp->te_msg + tevp->te_msg.ev_data.vd_offset); ++ tevph = sh->h_next; ++ while (tevph) { ++ /* find something with the same event type and handle type */ ++ if ((tevph->te_msg.ev_type == tevp->te_msg.ev_type) && ++ (tevph->te_tdp->td_type == tevp->te_tdp->td_type)) { ++ ++ ino2 = (&tevp->te_tdp->td_handle.ha_fid)->dm_fid_ino; ++ d_event2 = (dm_data_event_t *)((char *)&tevph->te_msg + tevph->te_msg.ev_data.vd_offset); ++ ++ /* If the two events are operating on the same file, ++ and the same part of that file, then we have a ++ match. ++ */ ++ if ((ino1 == ino2) && ++ (d_event2->de_offset == d_event1->de_offset) && ++ (d_event2->de_length == d_event1->de_length)) { ++ /* found a match */ ++#ifdef DM_SHASH_DEBUG ++ sh->dup_hits++; ++#endif ++ return(1); ++ } ++ } ++ tevph = tevph->te_hashnext; ++ } ++ ++ /* No match found */ ++ return(0); ++} ++ ++ ++/* Return a pointer to a session given its session ID, or EINVAL if no session ++ has the session ID (per the DMAPI spec). The caller must have obtained ++ dm_session_lock before calling this routine. ++*/ ++ ++static int ++dm_find_session( ++ dm_sessid_t sid, ++ dm_session_t **sessionpp) ++{ ++ dm_session_t *s; ++ ++ for (s = dm_sessions; s; s = s->sn_next) { ++ if (s->sn_sessid == sid) { ++ *sessionpp = s; ++ return(0); ++ } ++ } ++ return(-EINVAL); ++} ++ ++ ++/* Return a pointer to a locked session given its session ID. '*lcp' is ++ used to obtain the session's sn_qlock. Caller is responsible for eventually ++ unlocking it. ++*/ ++ ++int ++dm_find_session_and_lock( ++ dm_sessid_t sid, ++ dm_session_t **sessionpp, ++ unsigned long *lcp) /* addr of returned lock cookie */ ++{ ++ int error; ++ ++ for (;;) { ++ *lcp = mutex_spinlock(&dm_session_lock); ++ ++ if ((error = dm_find_session(sid, sessionpp)) != 0) { ++ mutex_spinunlock(&dm_session_lock, *lcp); ++ return(error); ++ } ++ if (spin_trylock(&(*sessionpp)->sn_qlock)) { ++ nested_spinunlock(&dm_session_lock); ++ return(0); /* success */ ++ } ++ ++ /* If the second lock is not available, drop the first and ++ start over. This gives the CPU a chance to process any ++ interrupts, and also allows processes which want a sn_qlock ++ for a different session to proceed. ++ */ ++ ++ mutex_spinunlock(&dm_session_lock, *lcp); ++ } ++} ++ ++ ++/* Return a pointer to the event on the specified session's sn_delq which ++ contains the given token. The caller must have obtained the session's ++ sn_qlock before calling this routine. ++*/ ++ ++static int ++dm_find_msg( ++ dm_session_t *s, ++ dm_token_t token, ++ dm_tokevent_t **tevpp) ++{ ++ dm_tokevent_t *tevp; ++ ++ if (token <= DM_INVALID_TOKEN) ++ return(-EINVAL); ++ ++ for (tevp = s->sn_delq.eq_head; tevp; tevp = tevp->te_next) { ++ if (tevp->te_msg.ev_token == token) { ++ *tevpp = tevp; ++ return(0); ++ } ++ } ++ return(-ESRCH); ++} ++ ++ ++/* Given a session ID and token, find the tevp on the specified session's ++ sn_delq which corresponds to that session ID/token pair. If a match is ++ found, lock the tevp's te_lock and return a pointer to the tevp. ++ '*lcp' is used to obtain the tevp's te_lock. The caller is responsible ++ for eventually unlocking it. ++*/ ++ ++int ++dm_find_msg_and_lock( ++ dm_sessid_t sid, ++ dm_token_t token, ++ dm_tokevent_t **tevpp, ++ unsigned long *lcp) /* address of returned lock cookie */ ++{ ++ dm_session_t *s; ++ int error; ++ ++ if ((error = dm_find_session_and_lock(sid, &s, lcp)) != 0) ++ return(error); ++ ++ if ((error = dm_find_msg(s, token, tevpp)) != 0) { ++ mutex_spinunlock(&s->sn_qlock, *lcp); ++ return(error); ++ } ++ nested_spinlock(&(*tevpp)->te_lock); ++ nested_spinunlock(&s->sn_qlock); ++ return(0); ++} ++ ++ ++/* Create a new session, or resume an old session if one is given. */ ++ ++int ++dm_create_session( ++ dm_sessid_t old, ++ char __user *info, ++ dm_sessid_t __user *new) ++{ ++ dm_session_t *s; ++ dm_sessid_t sid; ++ char sessinfo[DM_SESSION_INFO_LEN]; ++ size_t len; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ len = strnlen_user(info, DM_SESSION_INFO_LEN-1); ++ if (copy_from_user(sessinfo, info, len)) ++ return(-EFAULT); ++ lc = mutex_spinlock(&dm_session_lock); ++ sid = dm_next_sessid++; ++ mutex_spinunlock(&dm_session_lock, lc); ++ if (copy_to_user(new, &sid, sizeof(sid))) ++ return(-EFAULT); ++ ++ if (old == DM_NO_SESSION) { ++ s = kmem_cache_alloc(dm_session_cachep, GFP_KERNEL); ++ if (s == NULL) { ++ printk("%s/%d: kmem_cache_alloc(dm_session_cachep) returned NULL\n", __FUNCTION__, __LINE__); ++ return -ENOMEM; ++ } ++ memset(s, 0, sizeof(*s)); ++ ++ sv_init(&s->sn_readerq, SV_DEFAULT, "dmreadq"); ++ sv_init(&s->sn_writerq, SV_DEFAULT, "dmwritq"); ++ spinlock_init(&s->sn_qlock, "sn_qlock"); ++ } else { ++ lc = mutex_spinlock(&dm_session_lock); ++ if ((error = dm_find_session(old, &s)) != 0) { ++ mutex_spinunlock(&dm_session_lock, lc); ++ return(error); ++ } ++ unlink_session(s); ++ mutex_spinunlock(&dm_session_lock, lc); ++#ifdef CONFIG_PROC_FS ++ { ++ char buf[100]; ++ sprintf(buf, DMAPI_DBG_PROCFS "/sessions/0x%p", s); ++ remove_proc_entry(buf, NULL); ++ } ++#endif ++ } ++ memcpy(s->sn_info, sessinfo, len); ++ s->sn_info[len-1] = 0; /* if not NULL, then now 'tis */ ++ s->sn_sessid = sid; ++ lc = mutex_spinlock(&dm_session_lock); ++ link_session(s); ++ mutex_spinunlock(&dm_session_lock, lc); ++#ifdef CONFIG_PROC_FS ++ { ++ char buf[100]; ++ struct proc_dir_entry *entry; ++ ++ sprintf(buf, DMAPI_DBG_PROCFS "/sessions/0x%p", s); ++ entry = create_proc_read_entry(buf, 0, NULL, sessions_read_pfs, s); ++ } ++#endif ++ return(0); ++} ++ ++ ++int ++dm_destroy_session( ++ dm_sessid_t sid) ++{ ++ dm_session_t *s; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ /* The dm_session_lock must be held until the session is unlinked. */ ++ ++ lc = mutex_spinlock(&dm_session_lock); ++ ++ if ((error = dm_find_session(sid, &s)) != 0) { ++ mutex_spinunlock(&dm_session_lock, lc); ++ return(error); ++ } ++ nested_spinlock(&s->sn_qlock); ++ ++ /* The session exists. Check to see if it is still in use. If any ++ messages still exist on the sn_newq or sn_delq, or if any processes ++ are waiting for messages to arrive on the session, then the session ++ must not be destroyed. ++ */ ++ ++ if (s->sn_newq.eq_head || s->sn_readercnt || s->sn_delq.eq_head) { ++ nested_spinunlock(&s->sn_qlock); ++ mutex_spinunlock(&dm_session_lock, lc); ++ return(-EBUSY); ++ } ++ ++ /* The session is not in use. Dequeue it from the session chain. */ ++ ++ unlink_session(s); ++ nested_spinunlock(&s->sn_qlock); ++ mutex_spinunlock(&dm_session_lock, lc); ++ ++#ifdef CONFIG_PROC_FS ++ { ++ char buf[100]; ++ sprintf(buf, DMAPI_DBG_PROCFS "/sessions/0x%p", s); ++ remove_proc_entry(buf, NULL); ++ } ++#endif ++ ++ /* Now clear the sessions's disposition registration, and then destroy ++ the session structure. ++ */ ++ ++ dm_clear_fsreg(s); ++ ++ spinlock_destroy(&s->sn_qlock); ++ sv_destroy(&s->sn_readerq); ++ sv_destroy(&s->sn_writerq); ++ if (s->sn_sesshash) ++ kfree(s->sn_sesshash); ++ kmem_cache_free(dm_session_cachep, s); ++ return(0); ++} ++ ++ ++/* ++ * Return a list of all active sessions. ++ */ ++ ++int ++dm_getall_sessions( ++ u_int nelem, ++ dm_sessid_t __user *sidp, ++ u_int __user *nelemp) ++{ ++ dm_session_t *s; ++ u_int sesscnt; ++ dm_sessid_t *sesslist; ++ unsigned long lc; /* lock cookie */ ++ int error; ++ int i; ++ ++ /* Loop until we can get the right amount of temp space, being careful ++ not to hold a mutex during the allocation. Usually only one trip. ++ */ ++ ++ for (;;) { ++ if ((sesscnt = dm_sessions_active) == 0) { ++ /*if (suword(nelemp, 0))*/ ++ if (put_user(0, nelemp)) ++ return(-EFAULT); ++ return(0); ++ } ++ sesslist = kmalloc(sesscnt * sizeof(*sidp), GFP_KERNEL); ++ if (sesslist == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return -ENOMEM; ++ } ++ ++ lc = mutex_spinlock(&dm_session_lock); ++ if (sesscnt == dm_sessions_active) ++ break; ++ ++ mutex_spinunlock(&dm_session_lock, lc); ++ kfree(sesslist); ++ } ++ ++ /* Make a temp copy of the data, then release the mutex. */ ++ ++ for (i = 0, s = dm_sessions; i < sesscnt; i++, s = s->sn_next) ++ sesslist[i] = s->sn_sessid; ++ ++ mutex_spinunlock(&dm_session_lock, lc); ++ ++ /* Now copy the data to the user. */ ++ ++ if(put_user(sesscnt, nelemp)) { ++ error = -EFAULT; ++ } else if (sesscnt > nelem) { ++ error = -E2BIG; ++ } else if (copy_to_user(sidp, sesslist, sesscnt * sizeof(*sidp))) { ++ error = -EFAULT; ++ } else { ++ error = 0; ++ } ++ kfree(sesslist); ++ return(error); ++} ++ ++ ++/* ++ * Return the descriptive string associated with a session. ++ */ ++ ++int ++dm_query_session( ++ dm_sessid_t sid, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ dm_session_t *s; /* pointer to session given by sid */ ++ int len; /* length of session info string */ ++ int error; ++ char sessinfo[DM_SESSION_INFO_LEN]; ++ unsigned long lc; /* lock cookie */ ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0) ++ return(error); ++ ++ len = strlen(s->sn_info) + 1; /* NULL terminated when created */ ++ memcpy(sessinfo, s->sn_info, len); ++ ++ mutex_spinunlock(&s->sn_qlock, lc); ++ ++ /* Now that the mutex is released, copy the sessinfo to the user. */ ++ ++ if (put_user(len, rlenp)) { ++ error = -EFAULT; ++ } else if (len > buflen) { ++ error = -E2BIG; ++ } else if (copy_to_user(bufp, sessinfo, len)) { ++ error = -EFAULT; ++ } else { ++ error = 0; ++ } ++ return(error); ++} ++ ++ ++/* ++ * Return all of the previously delivered tokens (that is, their IDs) ++ * for the given session. ++ */ ++ ++int ++dm_getall_tokens( ++ dm_sessid_t sid, /* session obtaining tokens from */ ++ u_int nelem, /* size of tokenbufp */ ++ dm_token_t __user *tokenbufp,/* buffer to copy token IDs to */ ++ u_int __user *nelemp) /* return number copied to tokenbufp */ ++{ ++ dm_session_t *s; /* pointer to session given by sid */ ++ dm_tokevent_t *tevp; /* event message queue traversal */ ++ unsigned long lc; /* lock cookie */ ++ int tokcnt; ++ dm_token_t *toklist; ++ int error; ++ int i; ++ ++ /* Loop until we can get the right amount of temp space, being careful ++ not to hold a mutex during the allocation. Usually only one trip. ++ */ ++ ++ for (;;) { ++ if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0) ++ return(error); ++ tokcnt = s->sn_delq.eq_count; ++ mutex_spinunlock(&s->sn_qlock, lc); ++ ++ if (tokcnt == 0) { ++ /*if (suword(nelemp, 0))*/ ++ if (put_user(0, nelemp)) ++ return(-EFAULT); ++ return(0); ++ } ++ toklist = kmalloc(tokcnt * sizeof(*tokenbufp), GFP_KERNEL); ++ if (toklist == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return -ENOMEM; ++ } ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0) { ++ kfree(toklist); ++ return(error); ++ } ++ ++ if (tokcnt == s->sn_delq.eq_count) ++ break; ++ ++ mutex_spinunlock(&s->sn_qlock, lc); ++ kfree(toklist); ++ } ++ ++ /* Make a temp copy of the data, then release the mutex. */ ++ ++ tevp = s->sn_delq.eq_head; ++ for (i = 0; i < tokcnt; i++, tevp = tevp->te_next) ++ toklist[i] = tevp->te_msg.ev_token; ++ ++ mutex_spinunlock(&s->sn_qlock, lc); ++ ++ /* Now copy the data to the user. */ ++ ++ if (put_user(tokcnt, nelemp)) { ++ error = -EFAULT; ++ } else if (tokcnt > nelem) { ++ error = -E2BIG; ++ } else if (copy_to_user(tokenbufp,toklist,tokcnt*sizeof(*tokenbufp))) { ++ error = -EFAULT; ++ } else { ++ error = 0; ++ } ++ kfree(toklist); ++ return(error); ++} ++ ++ ++/* ++ * Return the message identified by token. ++ */ ++ ++int ++dm_find_eventmsg( ++ dm_sessid_t sid, ++ dm_token_t token, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ dm_tokevent_t *tevp; /* message identified by token */ ++ int msgsize; /* size of message to copy out */ ++ void *msg; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ /* Because some of the events (dm_data_event_t in particular) contain ++ __u64 fields, we need to make sure that the buffer provided by the ++ caller is aligned such that he can read those fields successfully. ++ */ ++ ++ if (((unsigned long)bufp & (sizeof(__u64) - 1)) != 0) ++ return(-EFAULT); ++ ++ /* Allocate the right amount of temp space, being careful not to hold ++ a mutex during the allocation. ++ */ ++ ++ if ((error = dm_find_msg_and_lock(sid, token, &tevp, &lc)) != 0) ++ return(error); ++ msgsize = tevp->te_allocsize - offsetof(dm_tokevent_t, te_msg); ++ mutex_spinunlock(&tevp->te_lock, lc); ++ ++ msg = kmalloc(msgsize, GFP_KERNEL); ++ if (msg == NULL) { ++ printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__); ++ return -ENOMEM; ++ } ++ ++ if ((error = dm_find_msg_and_lock(sid, token, &tevp, &lc)) != 0) { ++ kfree(msg); ++ return(error); ++ } ++ ++ /* Make a temp copy of the data, then release the mutex. */ ++ ++ memcpy(msg, &tevp->te_msg, msgsize); ++ mutex_spinunlock(&tevp->te_lock, lc); ++ ++ /* Now copy the data to the user. */ ++ ++ if (put_user(msgsize,rlenp)) { ++ error = -EFAULT; ++ } else if (msgsize > buflen) { /* user buffer not big enough */ ++ error = -E2BIG; ++ } else if (copy_to_user( bufp, msg, msgsize )) { ++ error = -EFAULT; ++ } else { ++ error = 0; ++ } ++ kfree(msg); ++ return(error); ++} ++ ++ ++int ++dm_move_event( ++ dm_sessid_t srcsid, ++ dm_token_t token, ++ dm_sessid_t targetsid, ++ dm_token_t __user *rtokenp) ++{ ++ dm_session_t *s1; ++ dm_session_t *s2; ++ dm_tokevent_t *tevp; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ int hash_it = 0; ++ ++ lc = mutex_spinlock(&dm_session_lock); ++ ++ if ((error = dm_find_session(srcsid, &s1)) != 0 || ++ (error = dm_find_session(targetsid, &s2)) != 0 || ++ (error = dm_find_msg(s1, token, &tevp)) != 0) { ++ mutex_spinunlock(&dm_session_lock, lc); ++ return(error); ++ } ++ dm_unlink_event(tevp, &s1->sn_delq); ++ if (tevp->te_flags & DM_TEF_HASHED) { ++ unhash_event(s1, tevp); ++ hash_it = 1; ++ } ++ dm_link_event(tevp, &s2->sn_delq); ++ if (hash_it) ++ hash_event(s2, tevp); ++ mutex_spinunlock(&dm_session_lock, lc); ++ ++ if (copy_to_user(rtokenp, &token, sizeof(token))) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++/* ARGSUSED */ ++int ++dm_pending( ++ dm_sessid_t sid, ++ dm_token_t token, ++ dm_timestruct_t __user *delay) /* unused */ ++{ ++ dm_tokevent_t *tevp; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ if ((error = dm_find_msg_and_lock(sid, token, &tevp, &lc)) != 0) ++ return(error); ++ ++ tevp->te_flags |= DM_TEF_INTERMED; ++ if (tevp->te_evt_ref > 0) /* if event generation threads exist */ ++ sv_broadcast(&tevp->te_evt_queue); ++ ++ mutex_spinunlock(&tevp->te_lock, lc); ++ return(0); ++} ++ ++ ++int ++dm_get_events( ++ dm_sessid_t sid, ++ u_int maxmsgs, ++ u_int flags, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ dm_session_t *s; /* pointer to session given by sid */ ++ dm_tokevent_t *tevp; /* next event message on queue */ ++ int error; ++ unsigned long lc1; /* first lock cookie */ ++ unsigned long lc2 = 0; /* second lock cookie */ ++ int totalsize; ++ int msgsize; ++ dm_eventmsg_t __user *prevmsg; ++ int prev_msgsize = 0; ++ u_int msgcnt; ++ ++ /* Because some of the events (dm_data_event_t in particular) contain ++ __u64 fields, we need to make sure that the buffer provided by the ++ caller is aligned such that he can read those fields successfully. ++ */ ++ ++ if (((unsigned long)bufp & (sizeof(__u64) - 1)) != 0) ++ return(-EFAULT); ++ ++ /* Find the indicated session and lock it. */ ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc1)) != 0) ++ return(error); ++ ++ /* Check for messages on sn_newq. If there aren't any that haven't ++ already been grabbed by another process, and if we are supposed to ++ to wait until one shows up, then go to sleep interruptibly on the ++ sn_readerq semaphore. The session can't disappear out from under ++ us as long as sn_readerq is non-zero. ++ */ ++ ++ for (;;) { ++ int rc; ++ ++ for (tevp = s->sn_newq.eq_head; tevp; tevp = tevp->te_next) { ++ lc2 = mutex_spinlock(&tevp->te_lock); ++ if (!(tevp->te_flags & DM_TEF_LOCKED)) ++ break; ++ mutex_spinunlock(&tevp->te_lock, lc2); ++ } ++ if (tevp) ++ break; /* got one! */ ++ ++ if (!(flags & DM_EV_WAIT)) { ++ mutex_spinunlock(&s->sn_qlock, lc1); ++ return(-EAGAIN); ++ } ++ s->sn_readercnt++; ++ ++ sv_wait_sig(&s->sn_readerq, 1, &s->sn_qlock, lc1); ++ rc = signal_pending(current); ++ ++ lc1 = mutex_spinlock(&s->sn_qlock); ++ s->sn_readercnt--; ++ if (rc) { /* if signal was received */ ++ mutex_spinunlock(&s->sn_qlock, lc1); ++ return(-EINTR); ++ } ++ } ++ ++ /* At least one message is available for delivery, and we have both the ++ session lock and event lock. Mark the event so that it is not ++ grabbed by other daemons, then drop both locks prior copying the ++ data to the caller's buffer. Leaving the event on the queue in a ++ marked state prevents both the session and the event from ++ disappearing out from under us while we don't have the locks. ++ */ ++ ++ tevp->te_flags |= DM_TEF_LOCKED; ++ mutex_spinunlock(&tevp->te_lock, lc2); /* reverse cookie order */ ++ mutex_spinunlock(&s->sn_qlock, lc1); ++ ++ /* Continue to deliver messages until there are no more, the ++ user's buffer becomes full, or we hit his maxmsgs limit. ++ */ ++ ++ totalsize = 0; /* total bytes transferred to the user */ ++ prevmsg = NULL; ++ msgcnt = 0; ++ ++ while (tevp) { ++ /* Compute the number of bytes to be moved, rounding up to an ++ 8-byte boundary so that any subsequent messages will also be ++ aligned. ++ */ ++ ++ msgsize = tevp->te_allocsize - offsetof(dm_tokevent_t, te_msg); ++ msgsize = (msgsize + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1); ++ totalsize += msgsize; ++ ++ /* If it fits, copy the message into the user's buffer and ++ update his 'rlenp'. Update the _link pointer for any ++ previous message. ++ */ ++ ++ if (totalsize > buflen) { /* no more room */ ++ error = -E2BIG; ++ } else if (put_user(totalsize, rlenp)) { ++ error = -EFAULT; ++ } else if (copy_to_user(bufp, &tevp->te_msg, msgsize)) { ++ error = -EFAULT; ++ } else if (prevmsg && put_user(prev_msgsize, &prevmsg->_link)) { ++ error = -EFAULT; ++ } else { ++ error = 0; ++ } ++ ++ /* If an error occurred, just unmark the event and leave it on ++ the queue for someone else. Note that other daemons may ++ have gone to sleep because this event was marked, so wake ++ them up. Also, if at least one message has already been ++ delivered, then an error here is not really an error. ++ */ ++ ++ lc1 = mutex_spinlock(&s->sn_qlock); ++ lc2 = mutex_spinlock(&tevp->te_lock); ++ tevp->te_flags &= ~DM_TEF_LOCKED; /* drop the mark */ ++ ++ if (error) { ++ if (s->sn_readercnt) ++ sv_signal(&s->sn_readerq); ++ ++ mutex_spinunlock(&tevp->te_lock, lc2); /* rev. order */ ++ mutex_spinunlock(&s->sn_qlock, lc1); ++ if (prevmsg) ++ return(0); ++ if (error == -E2BIG && put_user(totalsize,rlenp)) ++ error = -EFAULT; ++ return(error); ++ } ++ ++ /* The message was successfully delivered. Unqueue it. */ ++ ++ dm_unlink_event(tevp, &s->sn_newq); ++ ++ /* Wake up the first of any processes waiting for room on the ++ sn_newq. ++ */ ++ ++ if (s->sn_writercnt) ++ sv_signal(&s->sn_writerq); ++ ++ /* If the message is synchronous, add it to the sn_delq while ++ still holding the lock. If it is asynchronous, free it. ++ */ ++ ++ if (tevp->te_msg.ev_token != DM_INVALID_TOKEN) { /* synch */ ++ dm_link_event(tevp, &s->sn_delq); ++ mutex_spinunlock(&tevp->te_lock, lc2); ++ } else { ++ tevp->te_flags |= DM_TEF_FINAL; ++ if (tevp->te_flags & DM_TEF_HASHED) ++ unhash_event(s, tevp); ++ mutex_spinunlock(&tevp->te_lock, lc2); ++ dm_put_tevp(tevp, NULL);/* can't cause destroy events */ ++ } ++ ++ /* Update our notion of where we are in the user's buffer. If ++ he doesn't want any more messages, then stop. ++ */ ++ ++ prevmsg = (dm_eventmsg_t __user *)bufp; ++ prev_msgsize = msgsize; ++ bufp = (char __user *)bufp + msgsize; ++ ++ msgcnt++; ++ if (maxmsgs && msgcnt >= maxmsgs) { ++ mutex_spinunlock(&s->sn_qlock, lc1); ++ break; ++ } ++ ++ /* While still holding the sn_qlock, see if any additional ++ messages are available for delivery. ++ */ ++ ++ for (tevp = s->sn_newq.eq_head; tevp; tevp = tevp->te_next) { ++ lc2 = mutex_spinlock(&tevp->te_lock); ++ if (!(tevp->te_flags & DM_TEF_LOCKED)) { ++ tevp->te_flags |= DM_TEF_LOCKED; ++ mutex_spinunlock(&tevp->te_lock, lc2); ++ break; ++ } ++ mutex_spinunlock(&tevp->te_lock, lc2); ++ } ++ mutex_spinunlock(&s->sn_qlock, lc1); ++ } ++ return(0); ++} ++ ++ ++/* ++ * Remove an event message from the delivered queue, set the returned ++ * error where the event generator wants it, and wake up the generator. ++ * Also currently have the user side release any locks it holds... ++ */ ++ ++/* ARGSUSED */ ++int ++dm_respond_event( ++ dm_sessid_t sid, ++ dm_token_t token, ++ dm_response_t response, ++ int reterror, ++ size_t buflen, /* unused */ ++ void __user *respbufp) /* unused */ ++{ ++ dm_session_t *s; /* pointer to session given by sid */ ++ dm_tokevent_t *tevp; /* event message queue traversal */ ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ /* Sanity check the input parameters. */ ++ ++ switch (response) { ++ case DM_RESP_CONTINUE: /* continue must have reterror == 0 */ ++ if (reterror != 0) ++ return(-EINVAL); ++ break; ++ case DM_RESP_ABORT: /* abort must have errno set */ ++ if (reterror <= 0) ++ return(-EINVAL); ++ break; ++ case DM_RESP_DONTCARE: ++ reterror = -1; /* to distinguish DM_RESP_DONTCARE */ ++ break; ++ default: ++ return(-EINVAL); ++ } ++ ++ /* Hold session lock until the event is unqueued. */ ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0) ++ return(error); ++ ++ if ((error = dm_find_msg(s, token, &tevp)) != 0) { ++ mutex_spinunlock(&s->sn_qlock, lc); ++ return(error); ++ } ++ nested_spinlock(&tevp->te_lock); ++ ++ if ((response == DM_RESP_DONTCARE) && ++ (tevp->te_msg.ev_type != DM_EVENT_MOUNT)) { ++ error = -EINVAL; ++ nested_spinunlock(&tevp->te_lock); ++ mutex_spinunlock(&s->sn_qlock, lc); ++ } else { ++ dm_unlink_event(tevp, &s->sn_delq); ++ if (tevp->te_flags & DM_TEF_HASHED) ++ unhash_event(s, tevp); ++ tevp->te_reply = -reterror; /* linux wants negative errno */ ++ tevp->te_flags |= DM_TEF_FINAL; ++ if (tevp->te_evt_ref) ++ sv_broadcast(&tevp->te_evt_queue); ++ nested_spinunlock(&tevp->te_lock); ++ mutex_spinunlock(&s->sn_qlock, lc); ++ error = 0; ++ ++ /* Absolutely no locks can be held when calling dm_put_tevp! */ ++ ++ dm_put_tevp(tevp, NULL); /* this can generate destroy events */ ++ } ++ return(error); ++} ++ ++/* The caller must hold sn_qlock. ++ This will return the tokevent locked. ++ */ ++static dm_tokevent_t * ++__find_match_event_no_waiters_locked( ++ dm_tokevent_t *tevp1, ++ dm_eventq_t *queue) ++{ ++ dm_tokevent_t *tevp2, *next_tevp; ++ dm_tokdata_t *tdp1 = tevp1->te_tdp; ++ dm_tokdata_t *tdp2; ++ dm_data_event_t *d_event1; ++ dm_data_event_t *d_event2; ++ ++ d_event1 = (dm_data_event_t *)((char *)&tevp1->te_msg + tevp1->te_msg.ev_data.vd_offset); ++ ++ for(tevp2 = queue->eq_head; tevp2; tevp2 = next_tevp) { ++ nested_spinlock(&tevp2->te_lock); ++ next_tevp = tevp2->te_next; ++ ++ /* Just compare the first tdp's in each--there should ++ be just one, if it's the match we want. ++ */ ++ tdp2 = tevp2->te_tdp; ++ if ((tevp2->te_msg.ev_type == tevp1->te_msg.ev_type) && ++ (tevp2->te_tdp->td_type == tevp1->te_tdp->td_type) && ++ (tevp2->te_evt_ref == 0) && (tdp2->td_next == NULL) && ++ (memcmp(&tdp1->td_handle, &tdp2->td_handle, ++ sizeof(dm_handle_t)) == 0)) { ++ ++ d_event2 = (dm_data_event_t *)((char *)&tevp2->te_msg + tevp2->te_msg.ev_data.vd_offset); ++ ++ ++ if ((d_event2->de_offset == d_event1->de_offset) && ++ (d_event2->de_length == d_event1->de_length)) { ++ /* Match -- return it locked */ ++ return tevp2; ++ } ++ } ++ nested_spinunlock(&tevp2->te_lock); ++ } ++ return NULL; ++} ++ ++/* The caller must hold the sn_qlock. ++ The returned tokevent will be locked with nested_spinlock. ++ */ ++static dm_tokevent_t * ++find_match_event_no_waiters_locked( ++ dm_session_t *s, ++ dm_tokevent_t *tevp) ++{ ++ dm_tokevent_t *tevp2; ++ ++ if ((!s->sn_newq.eq_tail) && (!s->sn_delq.eq_tail)) ++ return NULL; ++ if (!tevp->te_tdp) ++ return NULL; ++ if (tevp->te_tdp->td_next) { ++ /* If it has multiple tdp's then don't bother trying to ++ find a match. ++ */ ++ return NULL; ++ } ++ tevp2 = __find_match_event_no_waiters_locked(tevp, &s->sn_newq); ++ if (tevp2 == NULL) ++ tevp2 = __find_match_event_no_waiters_locked(tevp, &s->sn_delq); ++ /* returns a locked tokevent */ ++ return tevp2; ++} ++ ++ ++ ++/* Queue the filled in event message pointed to by tevp on the session s, and ++ (if a synchronous event) wait for the reply from the DMAPI application. ++ The caller MUST be holding the session lock before calling this routine! ++ The session lock is always released upon exit. ++ Returns: ++ -1 == don't care ++ 0 == success (or async event) ++ > 0 == errno describing reason for failure ++*/ ++ ++static int ++dm_enqueue( ++ dm_session_t *s, ++ unsigned long lc, /* input lock cookie */ ++ dm_tokevent_t **tevpp, /* in/out parameter */ ++ int sync, ++ int flags, ++ int interruptable) ++{ ++ int is_unmount = 0; ++ int is_hashable = 0; ++ int reply; ++ dm_tokevent_t *tevp = *tevpp; ++ ++ /* If the caller isn't planning to stick around for the result ++ and this request is identical to one that is already on the ++ queues then just give the caller an EAGAIN. Release the ++ session lock before returning. ++ ++ We look only at NDELAY requests with an event type of READ, ++ WRITE, or TRUNCATE on objects that are regular files. ++ */ ++ ++ if ((flags & DM_FLAGS_NDELAY) && DM_EVENT_RDWRTRUNC(tevp) && ++ (tevp->te_tdp->td_type == DM_TDT_REG)) { ++ if (repeated_event(s, tevp)) { ++ mutex_spinunlock(&s->sn_qlock, lc); ++ return -EAGAIN; ++ } ++ is_hashable = 1; ++ } ++ ++ /* If the caller is a sync event then look for a matching sync ++ event. If there is a match and it doesn't currently have ++ event threads waiting on it, then we will drop our own ++ tokevent and jump on the matching event. ++ */ ++ if (((flags & DM_FLAGS_NDELAY) == 0) && DM_EVENT_RDWRTRUNC(tevp) && ++ (tevp->te_tdp->td_type == DM_TDT_REG)) { ++ dm_tokevent_t *tevp2; ++ if ((tevp2 = find_match_event_no_waiters_locked(s, tevp))) { ++ ASSERT(tevp2->te_evt_ref == 0); ++ tevp2->te_evt_ref++; ++ nested_spinunlock(&tevp2->te_lock); ++ nested_spinlock(&tevp->te_lock); ++ tevp->te_evt_ref--; ++ nested_spinunlock(&tevp->te_lock); ++ mutex_spinunlock(&s->sn_qlock, lc); ++ /* All locks have been released */ ++ dm_evt_rele_tevp(tevp, 1); ++ *tevpp = tevp = tevp2; ++ goto wait_on_tevp; ++ } ++ } ++ ++ if (tevp->te_msg.ev_type == DM_EVENT_UNMOUNT) ++ is_unmount = 1; ++ ++ /* Check for room on sn_newq. If there is no room for new messages, ++ then go to sleep on the sn_writerq semaphore. The ++ session cannot disappear out from under us as long as sn_writercnt ++ is non-zero. ++ */ ++ ++ while (s->sn_newq.eq_count >= dm_max_queued_msgs) { /* no room */ ++ s->sn_writercnt++; ++ dm_link_event(tevp, &s->sn_evt_writerq); ++ if (interruptable) { ++ sv_wait_sig(&s->sn_writerq, 1, &s->sn_qlock, lc); ++ if (signal_pending(current)) { ++ s->sn_writercnt--; ++ return -EINTR; ++ } ++ } else { ++ sv_wait(&s->sn_writerq, 1, &s->sn_qlock, lc); ++ } ++ lc = mutex_spinlock(&s->sn_qlock); ++ s->sn_writercnt--; ++ dm_unlink_event(tevp, &s->sn_evt_writerq); ++#ifdef HAVE_DM_QUEUE_FLUSH ++ /* We hold the sn_qlock, from here to after we get into ++ * the sn_newq. Any thread going through ++ * dm_release_threads() looking for us is already past us ++ * and has set the DM_TEF_FLUSH flag for us or is blocked on ++ * sn_qlock and will find us in sn_newq after we release ++ * the sn_qlock. ++ * We check for dop->flushing anyway, in case the ++ * dm_release_threads() already completed before we ++ * could enter dmapi. ++ */ ++ if (!sync) { ++ /* async events are forced into the newq */ ++ break; ++ } ++ if (tevp->te_flags & DM_TEF_FLUSH) { ++ mutex_spinunlock(&s->sn_qlock, lc); ++ return tevp->te_reply; ++ } ++ else { ++ struct filesystem_dmapi_operations *dops; ++ dm_tokdata_t *tdp; ++ int errno = 0; ++ ++ nested_spinlock(&tevp->te_lock); ++ for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) { ++ if (tdp->td_ip) { ++ dops = dm_fsys_ops(tdp->td_ip->i_sb); ++ ASSERT(dops); ++ if (dops->flushing) ++ errno = dops->flushing(tdp->td_ip); ++ if (errno) { ++ nested_spinunlock(&tevp->te_lock); ++ mutex_spinunlock(&s->sn_qlock, lc); ++ return errno; ++ } ++ } ++ } ++ nested_spinunlock(&tevp->te_lock); ++ } ++#endif /* HAVE_DM_QUEUE_FLUSH */ ++ } ++ ++ /* Assign a sequence number and token to the event and bump the ++ application reference count by one. We don't need 'te_lock' here ++ because this thread is still the only thread that can see the event. ++ */ ++ ++ nested_spinlock(&dm_token_lock); ++ tevp->te_msg.ev_sequence = dm_next_sequence++; ++ if (sync) { ++ tevp->te_msg.ev_token = dm_next_token++; ++ } else { ++ tevp->te_msg.ev_token = DM_INVALID_TOKEN; ++ } ++ nested_spinunlock(&dm_token_lock); ++ ++ tevp->te_app_ref++; ++ ++ /* Room exists on the sn_newq queue, so add this request. If the ++ queue was previously empty, wake up the first of any processes ++ that are waiting for an event. ++ */ ++ ++ dm_link_event(tevp, &s->sn_newq); ++ if (is_hashable) ++ hash_event(s, tevp); ++ ++ if (s->sn_readercnt) ++ sv_signal(&s->sn_readerq); ++ ++ mutex_spinunlock(&s->sn_qlock, lc); ++ ++ /* Now that the message is queued, processes issuing asynchronous ++ events or DM_EVENT_UNMOUNT events are ready to continue. ++ */ ++ ++ if (!sync || is_unmount) ++ return 0; ++ ++ /* Synchronous requests wait until a final reply is received. If the ++ caller supplied the DM_FLAGS_NDELAY flag, the process will return ++ EAGAIN if dm_pending() sets DM_TEF_INTERMED. We also let users ++ Cntl-C out of a read, write, and truncate requests. ++ */ ++ ++wait_on_tevp: ++ lc = mutex_spinlock(&tevp->te_lock); ++ ++ while (!(tevp->te_flags & DM_TEF_FINAL)) { ++ if ((tevp->te_flags & DM_TEF_INTERMED) && ++ (flags & DM_FLAGS_NDELAY)) { ++ mutex_spinunlock(&tevp->te_lock, lc); ++ return -EAGAIN; ++ } ++ if (tevp->te_msg.ev_type == DM_EVENT_READ || ++ tevp->te_msg.ev_type == DM_EVENT_WRITE || ++ tevp->te_msg.ev_type == DM_EVENT_TRUNCATE) { ++ sv_wait_sig(&tevp->te_evt_queue, 1, &tevp->te_lock, lc); ++ if (signal_pending(current)){ ++ return -EINTR; ++ } ++ } else { ++ sv_wait(&tevp->te_evt_queue, 1, &tevp->te_lock, lc); ++ } ++ lc = mutex_spinlock(&tevp->te_lock); ++#ifdef HAVE_DM_QUEUE_FLUSH ++ /* Did we pop out because of queue flushing? */ ++ if (tevp->te_flags & DM_TEF_FLUSH) { ++ mutex_spinunlock(&tevp->te_lock, lc); ++ return tevp->te_reply; ++ } ++#endif /* HAVE_DM_QUEUE_FLUSH */ ++ } ++ ++ /* Return both the tevp and the reply which was stored in the tevp by ++ dm_respond_event. The tevp structure has already been removed from ++ the reply queue by this point in dm_respond_event(). ++ */ ++ ++ reply = tevp->te_reply; ++ mutex_spinunlock(&tevp->te_lock, lc); ++ return reply; ++} ++ ++ ++/* The filesystem is guaranteed to stay mounted while this event is ++ outstanding. ++*/ ++ ++int ++dm_enqueue_normal_event( ++ struct super_block *sb, ++ dm_tokevent_t **tevpp, ++ int flags) ++{ ++ dm_session_t *s; ++ int error; ++ int sync; ++ unsigned long lc; /* lock cookie */ ++ ++ switch ((*tevpp)->te_msg.ev_type) { ++ case DM_EVENT_READ: ++ case DM_EVENT_WRITE: ++ case DM_EVENT_TRUNCATE: ++ case DM_EVENT_PREUNMOUNT: ++ case DM_EVENT_UNMOUNT: ++ case DM_EVENT_NOSPACE: ++ case DM_EVENT_CREATE: ++ case DM_EVENT_REMOVE: ++ case DM_EVENT_RENAME: ++ case DM_EVENT_SYMLINK: ++ case DM_EVENT_LINK: ++ case DM_EVENT_DEBUT: /* not currently supported */ ++ sync = 1; ++ break; ++ ++ case DM_EVENT_DESTROY: ++ case DM_EVENT_POSTCREATE: ++ case DM_EVENT_POSTREMOVE: ++ case DM_EVENT_POSTRENAME: ++ case DM_EVENT_POSTSYMLINK: ++ case DM_EVENT_POSTLINK: ++ case DM_EVENT_ATTRIBUTE: ++ case DM_EVENT_CLOSE: /* not currently supported */ ++ case DM_EVENT_CANCEL: /* not currently supported */ ++ sync = 0; ++ break; ++ ++ default: ++ return(-EIO); /* garbage event number */ ++ } ++ ++ /* Wait until a session selects disposition for the event. The session ++ is locked upon return from dm_waitfor_disp_session(). ++ */ ++ ++ if ((error = dm_waitfor_disp_session(sb, *tevpp, &s, &lc)) != 0) ++ return(error); ++ ++ return(dm_enqueue(s, lc, tevpp, sync, flags, 0)); ++} ++ ++ ++/* Traverse the session list checking for sessions with the WANTMOUNT flag ++ set. When one is found, send it the message. Possible responses to the ++ message are one of DONTCARE, CONTINUE, or ABORT. The action taken in each ++ case is: ++ DONTCARE (-1) - Send the event to the next session with WANTMOUNT set ++ CONTINUE ( 0) - Proceed with the mount, errno zero. ++ ABORT (>0) - Fail the mount, return the returned errno. ++ ++ The mount request is sent to sessions in ascending session ID order. ++ Since the session list can change dramatically while this process is ++ sleeping in dm_enqueue(), this routine must use session IDs rather than ++ session pointers when keeping track of where it is in the list. Since ++ new sessions are always added at the end of the queue, and have increasing ++ session ID values, we don't have to worry about missing any session. ++*/ ++ ++int ++dm_enqueue_mount_event( ++ struct super_block *sb, ++ dm_tokevent_t *tevp) ++{ ++ dm_session_t *s; ++ dm_sessid_t sid; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ /* Make the mounting filesystem visible to other DMAPI calls. */ ++ ++ if ((error = dm_add_fsys_entry(sb, tevp)) != 0){ ++ return(error); ++ } ++ ++ /* Walk through the session list presenting the mount event to each ++ session that is interested until a session accepts or rejects it, ++ or until all sessions ignore it. ++ */ ++ ++ for (sid = DM_NO_SESSION, error = 1; error > 0; sid = s->sn_sessid) { ++ ++ lc = mutex_spinlock(&dm_session_lock); ++ for (s = dm_sessions; s; s = s->sn_next) { ++ if (s->sn_sessid > sid && s->sn_flags & DM_SN_WANTMOUNT) { ++ nested_spinlock(&s->sn_qlock); ++ nested_spinunlock(&dm_session_lock); ++ break; ++ } ++ } ++ if (s == NULL) { ++ mutex_spinunlock(&dm_session_lock, lc); ++ break; /* noone wants it; proceed with mount */ ++ } ++ error = dm_enqueue(s, lc, &tevp, 1, 0, 0); ++ } ++ ++ /* If the mount will be allowed to complete, then update the fsrp entry ++ accordingly. If the mount is to be aborted, remove the fsrp entry. ++ */ ++ ++ if (error >= 0) { ++ dm_change_fsys_entry(sb, DM_STATE_MOUNTED); ++ error = 0; ++ } else { ++ dm_remove_fsys_entry(sb); ++ } ++ return(error); ++} ++ ++int ++dm_enqueue_sendmsg_event( ++ dm_sessid_t targetsid, ++ dm_tokevent_t *tevp, ++ int sync) ++{ ++ dm_session_t *s; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ if ((error = dm_find_session_and_lock(targetsid, &s, &lc)) != 0) ++ return(error); ++ ++ return(dm_enqueue(s, lc, &tevp, sync, 0, 1)); ++} ++ ++ ++dm_token_t ++dm_enqueue_user_event( ++ dm_sessid_t sid, ++ dm_tokevent_t *tevp, ++ dm_token_t *tokenp) ++{ ++ dm_session_t *s; ++ int error; ++ unsigned long lc; /* lock cookie */ ++ ++ /* Atomically find and lock the session whose session id is 'sid'. */ ++ ++ if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0) ++ return(error); ++ ++ /* Assign a sequence number and token to the event, bump the ++ application reference count by one, and decrement the event ++ count because the caller gives up all ownership of the event. ++ We don't need 'te_lock' here because this thread is still the ++ only thread that can see the event. ++ */ ++ ++ nested_spinlock(&dm_token_lock); ++ tevp->te_msg.ev_sequence = dm_next_sequence++; ++ *tokenp = tevp->te_msg.ev_token = dm_next_token++; ++ nested_spinunlock(&dm_token_lock); ++ ++ tevp->te_flags &= ~(DM_TEF_INTERMED|DM_TEF_FINAL); ++ tevp->te_app_ref++; ++ tevp->te_evt_ref--; ++ ++ /* Add the request to the tail of the sn_delq. Now it's visible. */ ++ ++ dm_link_event(tevp, &s->sn_delq); ++ mutex_spinunlock(&s->sn_qlock, lc); ++ ++ return(0); ++} ++ ++#ifdef HAVE_DM_QUEUE_FLUSH ++/* If inode is non-null, find any tdp referencing that inode and flush the ++ * thread waiting on that inode and set DM_TEF_FLUSH for that tokevent. ++ * Otherwise, if inode is null, find any tdp referencing the specified fsid ++ * and flush that thread and set DM_TEF_FLUSH for that tokevent. ++ */ ++static int ++dm_flush_events( ++ dm_session_t *s, ++ dm_fsid_t *fsidp, ++ struct inode *inode, /* may be null */ ++ dm_eventq_t *queue, ++ int is_writerq, ++ int errno) ++{ ++ dm_tokevent_t *tevp, *next_tevp; ++ dm_tokdata_t *tdp; ++ int found_events = 0; ++ ++ ASSERT(fsidp); ++ for (tevp = queue->eq_head; tevp; tevp = next_tevp) { ++ nested_spinlock(&tevp->te_lock); ++ next_tevp = tevp->te_next; ++ ++ for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) { ++ if( inode ) { ++ if( tdp->td_ip == inode ) { ++ break; ++ } ++ } ++ else if(memcmp(fsidp, &tdp->td_handle.ha_fsid, sizeof(*fsidp)) == 0) { ++ break; ++ } ++ } ++ ++ if (tdp != NULL) { ++ /* found a handle reference in this event */ ++ ++found_events; ++ tevp->te_flags |= DM_TEF_FLUSH; ++ ++ /* Set the reply value, unless dm_get_events is ++ already on this one. ++ */ ++ if (! (tevp->te_flags & DM_TEF_LOCKED)) ++ tevp->te_reply = errno; ++ ++ /* If it is on the sn_evt_writerq or is being ++ used by dm_get_events then we're done with it. ++ */ ++ if (is_writerq || (tevp->te_flags & DM_TEF_LOCKED)) { ++ nested_spinunlock(&tevp->te_lock); ++ continue; ++ } ++ ++ /* If there is a thread waiting on a synchronous ++ event then be like dm_respond_event. ++ */ ++ ++ if ((tevp->te_evt_ref) && ++ (tevp->te_msg.ev_token != DM_INVALID_TOKEN)) { ++ ++ tevp->te_flags |= DM_TEF_FINAL; ++ dm_unlink_event(tevp, queue); ++ if (tevp->te_flags & DM_TEF_HASHED) ++ unhash_event(s, tevp); ++ sv_broadcast(&tevp->te_evt_queue); ++ nested_spinunlock(&tevp->te_lock); ++ dm_put_tevp(tevp, NULL); ++ continue; ++ } ++ } ++ nested_spinunlock(&tevp->te_lock); ++ } ++ ++ return(found_events); ++} ++ ++ ++/* If inode is non-null then find any threads that have a reference to that ++ * inode and flush them with the specified errno. ++ * Otherwise,if inode is null, then find any threads that have a reference ++ * to that sb and flush them with the specified errno. ++ * We look for these threads in each session's sn_evt_writerq, sn_newq, ++ * and sn_delq. ++ */ ++int ++dm_release_threads( ++ struct super_block *sb, ++ struct inode *inode, /* may be null */ ++ int errno) ++{ ++ dm_sessid_t sid; ++ dm_session_t *s; ++ unsigned long lc; ++ u_int sesscnt; ++ dm_sessid_t *sidlist; ++ int i; ++ int found_events = 0; ++ dm_fsid_t fsid; ++ struct filesystem_dmapi_operations *dops; ++ ++ ASSERT(sb); ++ dops = dm_fsys_ops(sb); ++ ASSERT(dops); ++ dops->get_fsid(sb, &fsid); ++ dm_release_disp_threads(&fsid, inode, errno); ++ ++ /* Loop until we can get the right amount of temp space, being careful ++ not to hold a mutex during the allocation. Usually only one trip. ++ */ ++ ++ for (;;) { ++ lc = mutex_spinlock(&dm_session_lock); ++ sesscnt = dm_sessions_active; ++ mutex_spinunlock(&dm_session_lock, lc); ++ ++ if (sesscnt == 0) ++ return 0; ++ ++ sidlist = kmalloc(sesscnt * sizeof(sid), GFP_KERNEL); ++ ++ lc = mutex_spinlock(&dm_session_lock); ++ if (sesscnt == dm_sessions_active) ++ break; ++ ++ mutex_spinunlock(&dm_session_lock, lc); ++ kfree(sidlist); ++ } ++ ++ for (i = 0, s = dm_sessions; i < sesscnt; i++, s = s->sn_next) ++ sidlist[i] = s->sn_sessid; ++ ++ mutex_spinunlock(&dm_session_lock, lc); ++ ++ ++ for (i = 0; i < sesscnt; i++) { ++ sid = sidlist[i]; ++ if( dm_find_session_and_lock( sid, &s, &lc ) == 0 ){ ++ found_events = dm_flush_events( s, &fsid, inode, ++ &s->sn_evt_writerq, 1, ++ errno ); ++ if (found_events) ++ sv_broadcast(&s->sn_writerq); ++ ++ dm_flush_events(s, &fsid, inode, &s->sn_newq, 0, errno); ++ dm_flush_events(s, &fsid, inode, &s->sn_delq, 0, errno); ++ ++ mutex_spinunlock( &s->sn_qlock, lc ); ++ } ++ } ++ kfree(sidlist); ++ ++ return 0; ++} ++#endif /* HAVE_DM_QUEUE_FLUSH */ +--- /dev/null ++++ b/fs/dmapi/dmapi_sysent.c +@@ -0,0 +1,801 @@ ++/* ++ * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++ ++/* Data Migration API (DMAPI) ++ */ ++ ++ ++/* We're using MISC_MAJOR / MISC_DYNAMIC_MINOR. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "dmapi.h" ++#include "dmapi_kern.h" ++#include "dmapi_private.h" ++ ++struct kmem_cache *dm_fsreg_cachep = NULL; ++struct kmem_cache *dm_tokdata_cachep = NULL; ++struct kmem_cache *dm_session_cachep = NULL; ++struct kmem_cache *dm_fsys_map_cachep = NULL; ++struct kmem_cache *dm_fsys_vptr_cachep = NULL; ++ ++static int ++dmapi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ sys_dmapi_args_t kargs; ++ sys_dmapi_args_t *uap = &kargs; ++ int error = 0; ++ int rvp = -ENOSYS; ++ int use_rvp = 0; ++ ++ if (!capable(CAP_MKNOD)) ++ return -EPERM; ++ ++ if( copy_from_user( &kargs, (sys_dmapi_args_t __user *)arg, ++ sizeof(sys_dmapi_args_t) ) ) ++ return -EFAULT; ++ ++ unlock_kernel(); ++ ++ switch (cmd) { ++ case DM_CLEAR_INHERIT: ++ error = dm_clear_inherit( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_attrname_t __user *) DM_Parg(uap,5));/* attrnamep */ ++ break; ++ case DM_CREATE_BY_HANDLE: ++ error = dm_create_by_handle( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* dirhanp */ ++ (size_t) DM_Uarg(uap,3), /* dirhlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (void __user *) DM_Parg(uap,5), /* hanp */ ++ (size_t) DM_Uarg(uap,6), /* hlen */ ++ (char __user *) DM_Parg(uap,7));/* cname */ ++ break; ++ case DM_CREATE_SESSION: ++ error = dm_create_session( ++ (dm_sessid_t) DM_Uarg(uap,1), /* oldsid */ ++ (char __user *) DM_Parg(uap,2), /* sessinfop */ ++ (dm_sessid_t __user *) DM_Parg(uap,3));/* newsidp */ ++ break; ++ case DM_CREATE_USEREVENT: ++ error = dm_create_userevent( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (size_t) DM_Uarg(uap,2), /* msglen */ ++ (void __user *) DM_Parg(uap,3), /* msgdatap */ ++ (dm_token_t __user *) DM_Parg(uap,4));/* tokenp */ ++ break; ++ case DM_DESTROY_SESSION: ++ error = dm_destroy_session( ++ (dm_sessid_t) DM_Uarg(uap,1));/* sid */ ++ break; ++ case DM_DOWNGRADE_RIGHT: ++ error = dm_downgrade_right( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4));/* token */ ++ break; ++ case DM_FD_TO_HANDLE: ++ error = dm_fd_to_hdl( ++ (int) DM_Uarg(uap,1), /* fd */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t __user *) DM_Parg(uap,3));/* hlenp */ ++ break; ++ case DM_FIND_EVENTMSG: ++ error = dm_find_eventmsg( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (dm_token_t) DM_Uarg(uap,2), /* token */ ++ (size_t) DM_Uarg(uap,3), /* buflen */ ++ (void __user *) DM_Parg(uap,4), /* bufp */ ++ (size_t __user *) DM_Parg(uap,5));/* rlenp */ ++ break; ++ case DM_GET_ALLOCINFO: ++ use_rvp = 1; ++ error = dm_get_allocinfo_rvp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_off_t __user *) DM_Parg(uap,5), /* offp */ ++ (u_int) DM_Uarg(uap,6), /* nelem */ ++ (dm_extent_t __user *) DM_Parg(uap,7), /* extentp */ ++ (u_int __user *) DM_Parg(uap,8), /* nelemp */ ++ &rvp); ++ break; ++ case DM_GET_BULKALL: ++ use_rvp = 1; ++ error = dm_get_bulkall_rvp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* mask */ ++ (dm_attrname_t __user *) DM_Parg(uap,6),/* attrnamep */ ++ (dm_attrloc_t __user *) DM_Parg(uap,7),/* locp */ ++ (size_t) DM_Uarg(uap,8), /* buflen */ ++ (void __user *) DM_Parg(uap,9), /* bufp */ ++ (size_t __user *) DM_Parg(uap,10),/* rlenp */ ++ &rvp); ++ break; ++ case DM_GET_BULKATTR: ++ use_rvp = 1; ++ error = dm_get_bulkattr_rvp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* mask */ ++ (dm_attrloc_t __user *)DM_Parg(uap,6), /* locp */ ++ (size_t) DM_Uarg(uap,7), /* buflen */ ++ (void __user *) DM_Parg(uap,8), /* bufp */ ++ (size_t __user *) DM_Parg(uap,9), /* rlenp */ ++ &rvp); ++ break; ++ case DM_GET_CONFIG: ++ error = dm_get_config( ++ (void __user *) DM_Parg(uap,1), /* hanp */ ++ (size_t) DM_Uarg(uap,2), /* hlen */ ++ (dm_config_t) DM_Uarg(uap,3), /* flagname */ ++ (dm_size_t __user *)DM_Parg(uap,4));/* retvalp */ ++ break; ++ case DM_GET_CONFIG_EVENTS: ++ error = dm_get_config_events( ++ (void __user *) DM_Parg(uap,1), /* hanp */ ++ (size_t) DM_Uarg(uap,2), /* hlen */ ++ (u_int) DM_Uarg(uap,3), /* nelem */ ++ (dm_eventset_t __user *) DM_Parg(uap,4),/* eventsetp */ ++ (u_int __user *) DM_Parg(uap,5));/* nelemp */ ++ break; ++ case DM_GET_DIOINFO: ++ error = dm_get_dioinfo( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_dioinfo_t __user *)DM_Parg(uap,5));/* diop */ ++ break; ++ case DM_GET_DIRATTRS: ++ use_rvp = 1; ++ error = dm_get_dirattrs_rvp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* mask */ ++ (dm_attrloc_t __user *)DM_Parg(uap,6), /* locp */ ++ (size_t) DM_Uarg(uap,7), /* buflen */ ++ (void __user *) DM_Parg(uap,8), /* bufp */ ++ (size_t __user *) DM_Parg(uap,9), /* rlenp */ ++ &rvp); ++ break; ++ case DM_GET_DMATTR: ++ error = dm_get_dmattr( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_attrname_t __user *) DM_Parg(uap,5),/* attrnamep */ ++ (size_t) DM_Uarg(uap,6), /* buflen */ ++ (void __user *) DM_Parg(uap,7), /* bufp */ ++ (size_t __user *) DM_Parg(uap,8));/* rlenp */ ++ ++ break; ++ case DM_GET_EVENTLIST: ++ error = dm_get_eventlist( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* nelem */ ++ (dm_eventset_t __user *) DM_Parg(uap,6),/* eventsetp */ ++ (u_int __user *) DM_Parg(uap,7));/* nelemp */ ++ break; ++ case DM_GET_EVENTS: ++ error = dm_get_events( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (u_int) DM_Uarg(uap,2), /* maxmsgs */ ++ (u_int) DM_Uarg(uap,3), /* flags */ ++ (size_t) DM_Uarg(uap,4), /* buflen */ ++ (void __user *) DM_Parg(uap,5), /* bufp */ ++ (size_t __user *) DM_Parg(uap,6));/* rlenp */ ++ break; ++ case DM_GET_FILEATTR: ++ error = dm_get_fileattr( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* mask */ ++ (dm_stat_t __user *) DM_Parg(uap,6));/* statp */ ++ break; ++ case DM_GET_MOUNTINFO: ++ error = dm_get_mountinfo( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (size_t) DM_Uarg(uap,5), /* buflen */ ++ (void __user *) DM_Parg(uap,6), /* bufp */ ++ (size_t __user *) DM_Parg(uap,7));/* rlenp */ ++ break; ++ case DM_GET_REGION: ++ error = dm_get_region( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* nelem */ ++ (dm_region_t __user *) DM_Parg(uap,6), /* regbufp */ ++ (u_int __user *) DM_Parg(uap,7));/* nelemp */ ++ break; ++ case DM_GETALL_DISP: ++ error = dm_getall_disp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (size_t) DM_Uarg(uap,2), /* buflen */ ++ (void __user *) DM_Parg(uap,3), /* bufp */ ++ (size_t __user *) DM_Parg(uap,4));/* rlenp */ ++ break; ++ case DM_GETALL_DMATTR: ++ error = dm_getall_dmattr( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (size_t) DM_Uarg(uap,5), /* buflen */ ++ (void __user *) DM_Parg(uap,6), /* bufp */ ++ (size_t __user *) DM_Parg(uap,7));/* rlenp */ ++ break; ++ case DM_GETALL_INHERIT: ++ error = dm_getall_inherit( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* nelem */ ++ (dm_inherit_t __user *)DM_Parg(uap,6), /* inheritbufp*/ ++ (u_int __user *) DM_Parg(uap,7));/* nelemp */ ++ break; ++ case DM_GETALL_SESSIONS: ++ error = dm_getall_sessions( ++ (u_int) DM_Uarg(uap,1), /* nelem */ ++ (dm_sessid_t __user *) DM_Parg(uap,2), /* sidbufp */ ++ (u_int __user *) DM_Parg(uap,3));/* nelemp */ ++ break; ++ case DM_GETALL_TOKENS: ++ error = dm_getall_tokens( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (u_int) DM_Uarg(uap,2), /* nelem */ ++ (dm_token_t __user *) DM_Parg(uap,3), /* tokenbufp */ ++ (u_int __user *) DM_Parg(uap,4));/* nelemp */ ++ break; ++ case DM_INIT_ATTRLOC: ++ error = dm_init_attrloc( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_attrloc_t __user *) DM_Parg(uap,5));/* locp */ ++ break; ++ case DM_MKDIR_BY_HANDLE: ++ error = dm_mkdir_by_handle( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* dirhanp */ ++ (size_t) DM_Uarg(uap,3), /* dirhlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (void __user *) DM_Parg(uap,5), /* hanp */ ++ (size_t) DM_Uarg(uap,6), /* hlen */ ++ (char __user *) DM_Parg(uap,7));/* cname */ ++ break; ++ case DM_MOVE_EVENT: ++ error = dm_move_event( ++ (dm_sessid_t) DM_Uarg(uap,1), /* srcsid */ ++ (dm_token_t) DM_Uarg(uap,2), /* token */ ++ (dm_sessid_t) DM_Uarg(uap,3), /* targetsid */ ++ (dm_token_t __user *) DM_Parg(uap,4));/* rtokenp */ ++ break; ++ case DM_OBJ_REF_HOLD: ++ error = dm_obj_ref_hold( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (dm_token_t) DM_Uarg(uap,2), /* token */ ++ (void __user *) DM_Parg(uap,3), /* hanp */ ++ (size_t) DM_Uarg(uap,4));/* hlen */ ++ break; ++ case DM_OBJ_REF_QUERY: ++ use_rvp = 1; ++ error = dm_obj_ref_query_rvp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (dm_token_t) DM_Uarg(uap,2), /* token */ ++ (void __user *) DM_Parg(uap,3), /* hanp */ ++ (size_t) DM_Uarg(uap,4), /* hlen */ ++ &rvp); ++ break; ++ case DM_OBJ_REF_RELE: ++ error = dm_obj_ref_rele( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (dm_token_t) DM_Uarg(uap,2), /* token */ ++ (void __user *) DM_Parg(uap,3), /* hanp */ ++ (size_t) DM_Uarg(uap,4));/* hlen */ ++ break; ++ case DM_PATH_TO_FSHANDLE: ++ error = dm_path_to_fshdl( ++ (char __user *) DM_Parg(uap,1), /* path */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t __user *) DM_Parg(uap,3));/* hlenp */ ++ break; ++ case DM_PATH_TO_HANDLE: ++ error = dm_path_to_hdl( ++ (char __user *) DM_Parg(uap,1), /* path */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t __user *) DM_Parg(uap,3));/* hlenp */ ++ break; ++ case DM_PENDING: ++ error = dm_pending( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (dm_token_t) DM_Uarg(uap,2), /* token */ ++ (dm_timestruct_t __user *) DM_Parg(uap,3));/* delay */ ++ break; ++ case DM_PROBE_HOLE: ++ error = dm_probe_hole( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_off_t) DM_Uarg(uap,5), /* off */ ++ (dm_size_t) DM_Uarg(uap,6), /* len */ ++ (dm_off_t __user *) DM_Parg(uap,7), /* roffp */ ++ (dm_size_t __user *) DM_Parg(uap,8));/* rlenp */ ++ break; ++ case DM_PUNCH_HOLE: ++ error = dm_punch_hole( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_off_t) DM_Uarg(uap,5), /* off */ ++ (dm_size_t) DM_Uarg(uap,6));/* len */ ++ break; ++ case DM_QUERY_RIGHT: ++ error = dm_query_right( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_right_t __user *) DM_Parg(uap,5));/* rightp */ ++ break; ++ case DM_QUERY_SESSION: ++ error = dm_query_session( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (size_t) DM_Uarg(uap,2), /* buflen */ ++ (void __user *) DM_Parg(uap,3), /* bufp */ ++ (size_t __user *) DM_Parg(uap,4));/* rlenp */ ++ break; ++ case DM_READ_INVIS: ++ use_rvp = 1; ++ error = dm_read_invis_rvp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_off_t) DM_Uarg(uap,5), /* off */ ++ (dm_size_t) DM_Uarg(uap,6), /* len */ ++ (void __user *) DM_Parg(uap,7), /* bufp */ ++ &rvp); ++ break; ++ case DM_RELEASE_RIGHT: ++ error = dm_release_right( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4));/* token */ ++ break; ++ case DM_REMOVE_DMATTR: ++ error = dm_remove_dmattr( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (int) DM_Uarg(uap,5), /* setdtime */ ++ (dm_attrname_t __user *) DM_Parg(uap,6));/* attrnamep */ ++ break; ++ case DM_REQUEST_RIGHT: ++ error = dm_request_right( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* flags */ ++ (dm_right_t) DM_Uarg(uap,6));/* right */ ++ break; ++ case DM_RESPOND_EVENT: ++ error = dm_respond_event( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (dm_token_t) DM_Uarg(uap,2), /* token */ ++ (dm_response_t) DM_Uarg(uap,3), /* response */ ++ (int) DM_Uarg(uap,4), /* reterror */ ++ (size_t) DM_Uarg(uap,5), /* buflen */ ++ (void __user *) DM_Parg(uap,6));/* respbufp */ ++ break; ++ case DM_SEND_MSG: ++ error = dm_send_msg( ++ (dm_sessid_t) DM_Uarg(uap,1), /* targetsid */ ++ (dm_msgtype_t) DM_Uarg(uap,2), /* msgtype */ ++ (size_t) DM_Uarg(uap,3), /* buflen */ ++ (void __user *) DM_Parg(uap,4));/* bufp */ ++ break; ++ case DM_SET_DISP: ++ error = dm_set_disp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_eventset_t __user *) DM_Parg(uap,5),/* eventsetp */ ++ (u_int) DM_Uarg(uap,6));/* maxevent */ ++ break; ++ case DM_SET_DMATTR: ++ error = dm_set_dmattr( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_attrname_t __user *) DM_Parg(uap,5),/* attrnamep */ ++ (int) DM_Uarg(uap,6), /* setdtime */ ++ (size_t) DM_Uarg(uap,7), /* buflen */ ++ (void __user *) DM_Parg(uap,8));/* bufp */ ++ break; ++ case DM_SET_EVENTLIST: ++ error = dm_set_eventlist( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_eventset_t __user *) DM_Parg(uap,5),/* eventsetp */ ++ (u_int) DM_Uarg(uap,6));/* maxevent */ ++ break; ++ case DM_SET_FILEATTR: ++ error = dm_set_fileattr( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* mask */ ++ (dm_fileattr_t __user *)DM_Parg(uap,6));/* attrp */ ++ break; ++ case DM_SET_INHERIT: ++ error = dm_set_inherit( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_attrname_t __user *)DM_Parg(uap,5),/* attrnamep */ ++ (mode_t) DM_Uarg(uap,6));/* mode */ ++ break; ++ case DM_SET_REGION: ++ error = dm_set_region( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (u_int) DM_Uarg(uap,5), /* nelem */ ++ (dm_region_t __user *) DM_Parg(uap,6), /* regbufp */ ++ (dm_boolean_t __user *) DM_Parg(uap,7));/* exactflagp */ ++ break; ++ case DM_SET_RETURN_ON_DESTROY: ++ error = dm_set_return_on_destroy( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (dm_attrname_t __user *) DM_Parg(uap,5),/* attrnamep */ ++ (dm_boolean_t) DM_Uarg(uap,6));/* enable */ ++ break; ++ case DM_SYMLINK_BY_HANDLE: ++ error = dm_symlink_by_handle( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* dirhanp */ ++ (size_t) DM_Uarg(uap,3), /* dirhlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (void __user *) DM_Parg(uap,5), /* hanp */ ++ (size_t) DM_Uarg(uap,6), /* hlen */ ++ (char __user *) DM_Parg(uap,7), /* cname */ ++ (char __user *) DM_Parg(uap,8));/* path */ ++ break; ++ case DM_SYNC_BY_HANDLE: ++ error = dm_sync_by_handle( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4));/* token */ ++ break; ++ case DM_UPGRADE_RIGHT: ++ error = dm_upgrade_right( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4));/* token */ ++ break; ++ case DM_WRITE_INVIS: ++ use_rvp = 1; ++ error = dm_write_invis_rvp( ++ (dm_sessid_t) DM_Uarg(uap,1), /* sid */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (dm_token_t) DM_Uarg(uap,4), /* token */ ++ (int) DM_Uarg(uap,5), /* flags */ ++ (dm_off_t) DM_Uarg(uap,6), /* off */ ++ (dm_size_t) DM_Uarg(uap,7), /* len */ ++ (void __user *) DM_Parg(uap,8), /* bufp */ ++ &rvp); ++ break; ++ case DM_OPEN_BY_HANDLE: ++ use_rvp = 1; ++ error = dm_open_by_handle_rvp( ++ (unsigned int) DM_Uarg(uap,1), /* fd */ ++ (void __user *) DM_Parg(uap,2), /* hanp */ ++ (size_t) DM_Uarg(uap,3), /* hlen */ ++ (int) DM_Uarg(uap,4), /* flags */ ++ &rvp); ++ break; ++ default: ++ error = -ENOSYS; ++ break; ++ } ++ ++ lock_kernel(); ++ ++ /* If it was an *_rvp() function, then ++ if error==0, return |rvp| ++ */ ++ if( use_rvp && (error == 0) ) ++ return rvp; ++ else ++ return error; ++} ++ ++ ++ ++static int ++dmapi_open(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++ ++static int ++dmapi_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++ ++/* say hello, and let me know the device is hooked up */ ++static ssize_t ++dmapi_dump(struct file *file, char __user *buf, size_t count, loff_t *ppos) ++{ ++ char tmp[50]; ++ int len; ++ if( *ppos == 0 ){ ++ len = sprintf( tmp, "# " DM_VER_STR_CONTENTS "\n" ); ++ if( copy_to_user(buf, tmp, len) ) ++ return -EFAULT; ++ *ppos += 1; ++ return len; ++ } ++ return 0; ++} ++ ++static struct file_operations dmapi_fops = { ++ .open = dmapi_open, ++ .ioctl = dmapi_ioctl, ++ .read = dmapi_dump, ++ .release = dmapi_release ++}; ++ ++static struct miscdevice dmapi_dev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "dmapi", ++ .fops = &dmapi_fops ++}; ++ ++ ++ ++#ifdef CONFIG_PROC_FS ++static int ++dmapi_summary(char *buffer, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int len; ++ ++ extern u_int dm_sessions_active; ++ extern dm_sessid_t dm_next_sessid; ++ extern dm_token_t dm_next_token; ++ extern dm_sequence_t dm_next_sequence; ++ extern int dm_fsys_cnt; ++ ++#define CHKFULL if(len >= count) break; ++#define ADDBUF(a,b) len += sprintf(buffer + len, a, b); CHKFULL; ++ ++ len=0; ++ while(1){ ++ ADDBUF("dm_sessions_active=%u\n", dm_sessions_active); ++ ADDBUF("dm_next_sessid=%d\n", (int)dm_next_sessid); ++ ADDBUF("dm_next_token=%d\n", (int)dm_next_token); ++ ADDBUF("dm_next_sequence=%u\n", (u_int)dm_next_sequence); ++ ADDBUF("dm_fsys_cnt=%d\n", dm_fsys_cnt); ++ ++ break; ++ } ++ ++ if (offset >= len) { ++ *start = buffer; ++ *eof = 1; ++ return 0; ++ } ++ *start = buffer + offset; ++ if ((len -= offset) > count) ++ return count; ++ *eof = 1; ++ ++ return len; ++} ++#endif ++ ++ ++static void __init ++dmapi_init_procfs(int dmapi_minor) ++{ ++#ifdef CONFIG_PROC_FS ++ struct proc_dir_entry *entry; ++ ++ if ((entry = proc_mkdir( DMAPI_DBG_PROCFS, NULL)) == NULL ) ++ return; ++ entry->mode = S_IFDIR | S_IRUSR | S_IXUSR; ++ ++ if ((entry = proc_mkdir( DMAPI_DBG_PROCFS "/fsreg", NULL)) == NULL ) ++ return; ++ ++ if ((entry = proc_mkdir( DMAPI_DBG_PROCFS "/sessions", NULL)) == NULL ) ++ return; ++ ++ entry = create_proc_read_entry( DMAPI_DBG_PROCFS "/summary", ++ 0, NULL, dmapi_summary, NULL); ++#endif ++} ++ ++#if 0 ++static void __exit ++dmapi_cleanup_procfs(void) ++{ ++#ifdef CONFIG_PROC_FS ++ remove_proc_entry( DMAPI_DBG_PROCFS "/summary", NULL); ++ remove_proc_entry( DMAPI_DBG_PROCFS "/fsreg", NULL); ++ remove_proc_entry( DMAPI_DBG_PROCFS "/sessions", NULL); ++ remove_proc_entry( DMAPI_DBG_PROCFS, NULL); ++#endif ++} ++#endif ++ ++int __init dmapi_init(void) ++{ ++ int ret; ++ ++ dm_tokdata_cachep = kmem_cache_create("dm_tokdata", ++ sizeof(struct dm_tokdata), 0, 0, NULL); ++ if (dm_tokdata_cachep == NULL) ++ goto out; ++ ++ dm_fsreg_cachep = kmem_cache_create("dm_fsreg", ++ sizeof(struct dm_fsreg), 0, 0, NULL); ++ if (dm_fsreg_cachep == NULL) ++ goto out_free_tokdata_cachep; ++ ++ dm_session_cachep = kmem_cache_create("dm_session", ++ sizeof(struct dm_session), 0, 0, NULL); ++ if (dm_session_cachep == NULL) ++ goto out_free_fsreg_cachep; ++ ++ dm_fsys_map_cachep = kmem_cache_create("dm_fsys_map", ++ sizeof(dm_vector_map_t), 0, 0, NULL); ++ if (dm_fsys_map_cachep == NULL) ++ goto out_free_session_cachep; ++ dm_fsys_vptr_cachep = kmem_cache_create("dm_fsys_vptr", ++ sizeof(dm_fsys_vector_t), 0, 0, NULL); ++ if (dm_fsys_vptr_cachep == NULL) ++ goto out_free_fsys_map_cachep; ++ ++ ret = misc_register(&dmapi_dev); ++ if (ret) { ++ printk(KERN_ERR "dmapi_init: misc_register returned %d\n", ret); ++ goto out_free_fsys_vptr_cachep; ++ } ++ ++ dmapi_init_procfs(dmapi_dev.minor); ++ return 0; ++ ++ out_free_fsys_vptr_cachep: ++ kmem_cache_destroy(dm_fsys_vptr_cachep); ++ out_free_fsys_map_cachep: ++ kmem_cache_destroy(dm_fsys_map_cachep); ++ out_free_session_cachep: ++ kmem_cache_destroy(dm_session_cachep); ++ out_free_fsreg_cachep: ++ kmem_cache_destroy(dm_fsreg_cachep); ++ out_free_tokdata_cachep: ++ kmem_cache_destroy(dm_tokdata_cachep); ++ out: ++ return -ENOMEM; ++} ++ ++#if 0 ++void __exit dmapi_uninit(void) ++{ ++ misc_deregister(&dmapi_dev); ++ dmapi_cleanup_procfs(); ++ kmem_cache_destroy(dm_tokdata_cachep); ++ kmem_cache_destroy(dm_fsreg_cachep); ++ kmem_cache_destroy(dm_session_cachep); ++ kmem_cache_destroy(dm_fsys_map_cachep); ++ kmem_cache_destroy(dm_fsys_vptr_cachep); ++} ++#endif ++ ++module_init(dmapi_init); ++/*module_exit(dmapi_uninit);*/ /* Some other day */ ++ ++MODULE_AUTHOR("Silicon Graphics, Inc."); ++MODULE_DESCRIPTION("SGI Data Migration Subsystem"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(dm_send_mount_event); ++EXPORT_SYMBOL(dm_send_namesp_event); ++EXPORT_SYMBOL(dm_send_unmount_event); ++EXPORT_SYMBOL(dm_send_data_event); ++EXPORT_SYMBOL(dm_send_destroy_event); ++EXPORT_SYMBOL(dm_ip_to_handle); ++EXPORT_SYMBOL(dmapi_register); ++EXPORT_SYMBOL(dmapi_unregister); ++EXPORT_SYMBOL(dmapi_registered); ++EXPORT_SYMBOL(dm_release_threads); +--- /dev/null ++++ b/fs/dmapi/sv.h +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * ++ * Further, this software is distributed without any warranty that it is ++ * free of the rightful claim of any third person regarding infringement ++ * or the like. Any license provided herein, whether implied or ++ * otherwise, applies only to this software file. Patent licenses, if ++ * any, provided herein do not apply to combinations of this program with ++ * other software, or any other product whatsoever. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write the Free Software Foundation, Inc., 59 ++ * Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, ++ * Mountain View, CA 94043, or: ++ * ++ * http://www.sgi.com ++ * ++ * For further information regarding this notice, see: ++ * ++ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ ++ */ ++#ifndef __DMAPI_SV_H__ ++#define __DMAPI_SV_H__ ++ ++#include ++#include ++#include ++ ++/* ++ * Synchronisation variables. ++ * ++ * (Parameters "pri", "svf" and "rts" are not implemented) ++ */ ++ ++typedef struct sv_s { ++ wait_queue_head_t waiters; ++} sv_t; ++ ++#define SV_FIFO 0x0 /* sv_t is FIFO type */ ++#define SV_LIFO 0x2 /* sv_t is LIFO type */ ++#define SV_PRIO 0x4 /* sv_t is PRIO type */ ++#define SV_KEYED 0x6 /* sv_t is KEYED type */ ++#define SV_DEFAULT SV_FIFO ++ ++ ++static inline void _sv_wait(sv_t *sv, spinlock_t *lock, int state, ++ unsigned long timeout) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ add_wait_queue_exclusive(&sv->waiters, &wait); ++ __set_current_state(state); ++ spin_unlock(lock); ++ ++ schedule_timeout(timeout); ++ ++ remove_wait_queue(&sv->waiters, &wait); ++} ++ ++#define init_sv(sv,type,name,flag) \ ++ init_waitqueue_head(&(sv)->waiters) ++#define sv_init(sv,flag,name) \ ++ init_waitqueue_head(&(sv)->waiters) ++#define sv_destroy(sv) \ ++ /*NOTHING*/ ++#define sv_wait(sv, pri, lock, s) \ ++ _sv_wait(sv, lock, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT) ++#define sv_wait_sig(sv, pri, lock, s) \ ++ _sv_wait(sv, lock, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT) ++#define sv_timedwait(sv, pri, lock, s, svf, ts, rts) \ ++ _sv_wait(sv, lock, TASK_UNINTERRUPTIBLE, timespec_to_jiffies(ts)) ++#define sv_timedwait_sig(sv, pri, lock, s, svf, ts, rts) \ ++ _sv_wait(sv, lock, TASK_INTERRUPTIBLE, timespec_to_jiffies(ts)) ++#define sv_signal(sv) \ ++ wake_up(&(sv)->waiters) ++#define sv_broadcast(sv) \ ++ wake_up_all(&(sv)->waiters) ++ ++#endif /* __DMAPI_SV_H__ */ diff --git a/patches.suse/xfs-dmapi-xfs-enable b/patches.suse/xfs-dmapi-xfs-enable new file mode 100644 index 0000000..891e137 --- /dev/null +++ b/patches.suse/xfs-dmapi-xfs-enable @@ -0,0 +1,3808 @@ +Date: Thu, 09 Oct 2008 17:11:53 +1100 +From: Donald Douwsma +Subject: DMAPI support for xfs +Patch-mainline: Not yet +References: bnc#450658 + +Acked-by: Jan Kara + +--- + fs/xfs/Kconfig | 13 + fs/xfs/Makefile | 5 + fs/xfs/dmapi/Makefile | 28 + fs/xfs/dmapi/xfs_dm.c | 3327 +++++++++++++++++++++++++++++++++++++++++++ + fs/xfs/dmapi/xfs_dm.h | 23 + fs/xfs/linux-2.6/xfs_file.c | 76 + fs/xfs/linux-2.6/xfs_ksyms.c | 92 + + fs/xfs/linux-2.6/xfs_linux.h | 4 + fs/xfs/linux-2.6/xfs_super.c | 13 + fs/xfs/xfs_dmops.c | 20 + fs/xfs/xfs_itable.c | 2 + fs/xfs/xfs_itable.h | 5 + fs/xfs/xfs_mount.h | 1 + fs/xfs/xfs_rw.c | 1 + fs/xfs/xfs_rw.h | 5 + fs/xfs/xfs_vnodeops.c | 2 + 16 files changed, 3609 insertions(+), 8 deletions(-) + +--- a/fs/xfs/Kconfig ++++ b/fs/xfs/Kconfig +@@ -36,6 +36,19 @@ config XFS_QUOTA + with or without the generic quota support enabled (CONFIG_QUOTA) - + they are completely independent subsystems. + ++config XFS_DMAPI ++ tristate "XFS DMAPI support" ++ depends on XFS_FS ++ select DMAPI ++ help ++ The Data Management API is a system interface used to implement ++ the interface defined in the X/Open document: ++ "Systems Management: Data Storage Management (XDSM) API", ++ dated February 1997. This interface is used by hierarchical ++ storage management systems. ++ ++ If unsure, say N. ++ + config XFS_POSIX_ACL + bool "XFS POSIX ACL support" + depends on XFS_FS +--- a/fs/xfs/Makefile ++++ b/fs/xfs/Makefile +@@ -41,6 +41,8 @@ ifeq ($(CONFIG_XFS_QUOTA),y) + xfs-$(CONFIG_PROC_FS) += quota/xfs_qm_stats.o + endif + ++obj-$(CONFIG_XFS_DMAPI) += dmapi/ ++ + xfs-$(CONFIG_XFS_RT) += xfs_rtalloc.o + xfs-$(CONFIG_XFS_POSIX_ACL) += $(XFS_LINUX)/xfs_acl.o + xfs-$(CONFIG_PROC_FS) += $(XFS_LINUX)/xfs_stats.o +@@ -107,7 +109,8 @@ xfs-y += $(addprefix $(XFS_LINUX)/, \ + xfs_iops.o \ + xfs_super.o \ + xfs_sync.o \ +- xfs_xattr.o) ++ xfs_xattr.o \ ++ xfs_ksyms.o) + + # Objects in support/ + xfs-y += $(addprefix support/, \ +--- /dev/null ++++ b/fs/xfs/dmapi/Makefile +@@ -0,0 +1,28 @@ ++# ++# Copyright (c) 2006 Silicon Graphics, Inc. ++# All Rights Reserved. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License as ++# published by the Free Software Foundation. ++# ++# This program is distributed in the hope that it would be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write the Free Software Foundation, ++# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++# ++ ++EXTRA_CFLAGS += -I$(src)/.. -I$(src)/../linux-2.6 ++EXTRA_CFLAGS += -I$(srctree)/fs/dmapi ++ ++ifeq ($(CONFIG_XFS_DEBUG),y) ++ EXTRA_CFLAGS += -g -DDEBUG ++endif ++ ++obj-$(CONFIG_XFS_DMAPI) += xfs_dmapi.o ++ ++xfs_dmapi-y += xfs_dm.o +--- /dev/null ++++ b/fs/xfs/dmapi/xfs_dm.c +@@ -0,0 +1,3327 @@ ++/* ++ * Copyright (c) 2000-2006 Silicon Graphics, Inc. ++ * All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++#include "xfs.h" ++#include "xfs_fs.h" ++#include "xfs_types.h" ++#include "xfs_bit.h" ++#include "xfs_log.h" ++#include "xfs_inum.h" ++#include "xfs_trans.h" ++#include "xfs_sb.h" ++#include "xfs_ag.h" ++#include "xfs_dir2.h" ++#include "xfs_alloc.h" ++#include "xfs_dmapi.h" ++#include "xfs_mount.h" ++#include "xfs_da_btree.h" ++#include "xfs_bmap_btree.h" ++#include "xfs_alloc_btree.h" ++#include "xfs_ialloc_btree.h" ++#include "xfs_dir2_sf.h" ++#include "xfs_attr_sf.h" ++#include "xfs_dinode.h" ++#include "xfs_inode.h" ++#include "xfs_btree.h" ++#include "xfs_ialloc.h" ++#include "xfs_itable.h" ++#include "xfs_bmap.h" ++#include "xfs_rw.h" ++#include "xfs_acl.h" ++#include "xfs_attr.h" ++#include "xfs_attr_leaf.h" ++#include "xfs_inode_item.h" ++#include "xfs_vnodeops.h" ++#include ++#include ++#include "xfs_dm.h" ++ ++#include ++ ++#define MAXNAMLEN MAXNAMELEN ++ ++#define MIN_DIO_SIZE(mp) ((mp)->m_sb.sb_sectsize) ++#define MAX_DIO_SIZE(mp) (INT_MAX & ~(MIN_DIO_SIZE(mp) - 1)) ++ ++static void up_rw_sems(struct inode *ip, int flags) ++{ ++ if (flags & DM_FLAGS_IALLOCSEM_WR) ++ up_write(&ip->i_alloc_sem); ++ if (flags & DM_FLAGS_IMUX) ++ mutex_unlock(&ip->i_mutex); ++} ++ ++static void down_rw_sems(struct inode *ip, int flags) ++{ ++ if (flags & DM_FLAGS_IMUX) ++ mutex_lock(&ip->i_mutex); ++ if (flags & DM_FLAGS_IALLOCSEM_WR) ++ down_write(&ip->i_alloc_sem); ++} ++ ++ ++/* Structure used to hold the on-disk version of a dm_attrname_t. All ++ on-disk attribute names start with the 8-byte string "SGI_DMI_". ++*/ ++ ++typedef struct { ++ char dan_chars[DMATTR_PREFIXLEN + DM_ATTR_NAME_SIZE + 1]; ++} dm_dkattrname_t; ++ ++/* Structure used by xfs_dm_get_bulkall(), used as the "private_data" ++ * that we want xfs_bulkstat to send to our formatter. ++ */ ++typedef struct { ++ dm_fsid_t fsid; ++ void __user *laststruct; ++ dm_dkattrname_t attrname; ++} dm_bulkstat_one_t; ++ ++/* In the on-disk inode, DMAPI attribute names consist of the user-provided ++ name with the DMATTR_PREFIXSTRING pre-pended. This string must NEVER be ++ changed! ++*/ ++ ++static const char dmattr_prefix[DMATTR_PREFIXLEN + 1] = DMATTR_PREFIXSTRING; ++ ++static dm_size_t dm_min_dio_xfer = 0; /* direct I/O disabled for now */ ++ ++ ++/* See xfs_dm_get_dmattr() for a description of why this is needed. */ ++ ++#define XFS_BUG_KLUDGE 256 /* max size of an in-inode attribute value */ ++ ++#define DM_MAX_ATTR_BYTES_ON_DESTROY 256 ++ ++#define DM_STAT_SIZE(dmtype,namelen) \ ++ (sizeof(dmtype) + sizeof(dm_handle_t) + namelen) ++ ++#define DM_STAT_ALIGN (sizeof(__uint64_t)) ++ ++/* DMAPI's E2BIG == EA's ERANGE */ ++#define DM_EA_XLATE_ERR(err) { if (err == ERANGE) err = E2BIG; } ++ ++static inline size_t dm_stat_align(size_t size) ++{ ++ return (size + (DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1); ++} ++ ++static inline size_t dm_stat_size(size_t namelen) ++{ ++ return dm_stat_align(sizeof(dm_stat_t) + sizeof(dm_handle_t) + namelen); ++} ++ ++/* ++ * xfs_dm_send_data_event() ++ * ++ * Send data event to DMAPI. Drop IO lock (if specified) before ++ * the dm_send_data_event() call and reacquire it afterwards. ++ */ ++int ++xfs_dm_send_data_event( ++ dm_eventtype_t event, ++ xfs_inode_t *ip, ++ xfs_off_t offset, ++ size_t length, ++ int flags, ++ int *lock_flags) ++{ ++ struct inode *inode = &ip->i_vnode; ++ int error; ++ uint16_t dmstate; ++ ++ /* Returns positive errors to XFS */ ++ ++ do { ++ dmstate = ip->i_d.di_dmstate; ++ if (lock_flags) ++ xfs_iunlock(ip, *lock_flags); ++ ++ up_rw_sems(inode, flags); ++ ++ error = dm_send_data_event(event, inode, DM_RIGHT_NULL, ++ offset, length, flags); ++ error = -error; /* DMAPI returns negative errors */ ++ ++ down_rw_sems(inode, flags); ++ ++ if (lock_flags) ++ xfs_ilock(ip, *lock_flags); ++ } while (!error && (ip->i_d.di_dmstate != dmstate)); ++ ++ return error; ++} ++ ++/* prohibited_mr_events ++ * ++ * Return event bits representing any events which cannot have managed ++ * region events set due to memory mapping of the file. If the maximum ++ * protection allowed in any pregion includes PROT_WRITE, and the region ++ * is shared and not text, then neither READ nor WRITE events can be set. ++ * Otherwise if the file is memory mapped, no READ event can be set. ++ * ++ */ ++STATIC int ++prohibited_mr_events( ++ struct address_space *mapping) ++{ ++ int prohibited = (1 << DM_EVENT_READ); ++ ++ if (!mapping_mapped(mapping)) ++ return 0; ++ ++ spin_lock(&mapping->i_mmap_lock); ++ if (mapping_writably_mapped(mapping)) ++ prohibited |= (1 << DM_EVENT_WRITE); ++ spin_unlock(&mapping->i_mmap_lock); ++ ++ return prohibited; ++} ++ ++#ifdef DEBUG_RIGHTS ++STATIC int ++xfs_vp_to_hexhandle( ++ struct inode *inode, ++ u_int type, ++ char *buffer) ++{ ++ dm_handle_t handle; ++ u_char *ip; ++ int length; ++ int error; ++ int i; ++ ++ /* ++ * XXX: dm_vp_to_handle doesn't exist. ++ * Looks like this debug code is rather dead. ++ */ ++ if ((error = dm_vp_to_handle(inode, &handle))) ++ return(error); ++ ++ if (type == DM_FSYS_OBJ) { /* a filesystem handle */ ++ length = DM_FSHSIZE; ++ } else { ++ length = DM_HSIZE(handle); ++ } ++ for (ip = (u_char *)&handle, i = 0; i < length; i++) { ++ *buffer++ = "0123456789abcdef"[ip[i] >> 4]; ++ *buffer++ = "0123456789abcdef"[ip[i] & 0xf]; ++ } ++ *buffer = '\0'; ++ return(0); ++} ++#endif /* DEBUG_RIGHTS */ ++ ++ ++ ++ ++/* Copy in and validate an attribute name from user space. It should be a ++ string of at least one and at most DM_ATTR_NAME_SIZE characters. Because ++ the dm_attrname_t structure doesn't provide room for the trailing NULL ++ byte, we just copy in one extra character and then zero it if it ++ happens to be non-NULL. ++*/ ++ ++STATIC int ++xfs_copyin_attrname( ++ dm_attrname_t __user *from, /* dm_attrname_t in user space */ ++ dm_dkattrname_t *to) /* name buffer in kernel space */ ++{ ++ int error = 0; ++ size_t len; ++ ++ strcpy(to->dan_chars, dmattr_prefix); ++ ++ len = strnlen_user((char __user *)from, DM_ATTR_NAME_SIZE); ++ if (len == 0) ++ error = EFAULT; ++ else { ++ if (copy_from_user(&to->dan_chars[DMATTR_PREFIXLEN], from, len)) ++ to->dan_chars[sizeof(to->dan_chars) - 1] = '\0'; ++ else if (to->dan_chars[DMATTR_PREFIXLEN] == '\0') ++ error = EINVAL; ++ else ++ to->dan_chars[DMATTR_PREFIXLEN + len - 1] = '\0'; ++ } ++ ++ return error; ++} ++ ++ ++/* ++ * Convert the XFS flags into their DMAPI flag equivalent for export ++ */ ++STATIC uint ++_xfs_dic2dmflags( ++ __uint16_t di_flags) ++{ ++ uint flags = 0; ++ ++ if (di_flags & XFS_DIFLAG_ANY) { ++ if (di_flags & XFS_DIFLAG_REALTIME) ++ flags |= DM_XFLAG_REALTIME; ++ if (di_flags & XFS_DIFLAG_PREALLOC) ++ flags |= DM_XFLAG_PREALLOC; ++ if (di_flags & XFS_DIFLAG_IMMUTABLE) ++ flags |= DM_XFLAG_IMMUTABLE; ++ if (di_flags & XFS_DIFLAG_APPEND) ++ flags |= DM_XFLAG_APPEND; ++ if (di_flags & XFS_DIFLAG_SYNC) ++ flags |= DM_XFLAG_SYNC; ++ if (di_flags & XFS_DIFLAG_NOATIME) ++ flags |= DM_XFLAG_NOATIME; ++ if (di_flags & XFS_DIFLAG_NODUMP) ++ flags |= DM_XFLAG_NODUMP; ++ } ++ return flags; ++} ++ ++STATIC uint ++xfs_ip2dmflags( ++ xfs_inode_t *ip) ++{ ++ return _xfs_dic2dmflags(ip->i_d.di_flags) | ++ (XFS_IFORK_Q(ip) ? DM_XFLAG_HASATTR : 0); ++} ++ ++STATIC uint ++xfs_dic2dmflags( ++ xfs_dinode_t *dip) ++{ ++ return _xfs_dic2dmflags(be16_to_cpu(dip->di_flags)) | ++ (XFS_DFORK_Q(dip) ? DM_XFLAG_HASATTR : 0); ++} ++ ++/* ++ * This copies selected fields in an inode into a dm_stat structure. Because ++ * these fields must return the same values as they would in stat(), the ++ * majority of this code was copied directly from xfs_getattr(). Any future ++ * changes to xfs_gettattr() must also be reflected here. ++ */ ++STATIC void ++xfs_dip_to_stat( ++ xfs_mount_t *mp, ++ xfs_ino_t ino, ++ xfs_dinode_t *dip, ++ dm_stat_t *buf) ++{ ++ xfs_dinode_t *dic = dip; ++ ++ /* ++ * The inode format changed when we moved the link count and ++ * made it 32 bits long. If this is an old format inode, ++ * convert it in memory to look like a new one. If it gets ++ * flushed to disk we will convert back before flushing or ++ * logging it. We zero out the new projid field and the old link ++ * count field. We'll handle clearing the pad field (the remains ++ * of the old uuid field) when we actually convert the inode to ++ * the new format. We don't change the version number so that we ++ * can distinguish this from a real new format inode. ++ */ ++ if (dic->di_version == 1) { ++ buf->dt_nlink = be16_to_cpu(dic->di_onlink); ++ /*buf->dt_xfs_projid = 0;*/ ++ } else { ++ buf->dt_nlink = be32_to_cpu(dic->di_nlink); ++ /*buf->dt_xfs_projid = be16_to_cpu(dic->di_projid);*/ ++ } ++ buf->dt_ino = ino; ++ buf->dt_dev = new_encode_dev(mp->m_ddev_targp->bt_dev); ++ buf->dt_mode = be16_to_cpu(dic->di_mode); ++ buf->dt_uid = be32_to_cpu(dic->di_uid); ++ buf->dt_gid = be32_to_cpu(dic->di_gid); ++ buf->dt_size = be64_to_cpu(dic->di_size); ++ buf->dt_atime = be32_to_cpu(dic->di_atime.t_sec); ++ buf->dt_mtime = be32_to_cpu(dic->di_mtime.t_sec); ++ buf->dt_ctime = be32_to_cpu(dic->di_ctime.t_sec); ++ buf->dt_xfs_xflags = xfs_dic2dmflags(dip); ++ buf->dt_xfs_extsize = ++ be32_to_cpu(dic->di_extsize) << mp->m_sb.sb_blocklog; ++ buf->dt_xfs_extents = be32_to_cpu(dic->di_nextents); ++ buf->dt_xfs_aextents = be16_to_cpu(dic->di_anextents); ++ buf->dt_xfs_igen = be32_to_cpu(dic->di_gen); ++ buf->dt_xfs_dmstate = be16_to_cpu(dic->di_dmstate); ++ ++ switch (dic->di_format) { ++ case XFS_DINODE_FMT_DEV: ++ buf->dt_rdev = xfs_dinode_get_rdev(dic); ++ buf->dt_blksize = BLKDEV_IOSIZE; ++ buf->dt_blocks = 0; ++ break; ++ case XFS_DINODE_FMT_LOCAL: ++ case XFS_DINODE_FMT_UUID: ++ buf->dt_rdev = 0; ++ buf->dt_blksize = mp->m_sb.sb_blocksize; ++ buf->dt_blocks = 0; ++ break; ++ case XFS_DINODE_FMT_EXTENTS: ++ case XFS_DINODE_FMT_BTREE: ++ buf->dt_rdev = 0; ++ buf->dt_blksize = mp->m_sb.sb_blocksize; ++ buf->dt_blocks = ++ XFS_FSB_TO_BB(mp, be64_to_cpu(dic->di_nblocks)); ++ break; ++ } ++ ++ memset(&buf->dt_pad1, 0, sizeof(buf->dt_pad1)); ++ memset(&buf->dt_pad2, 0, sizeof(buf->dt_pad2)); ++ memset(&buf->dt_pad3, 0, sizeof(buf->dt_pad3)); ++ ++ /* Finally fill in the DMAPI specific fields */ ++ buf->dt_pers = 0; ++ buf->dt_change = 0; ++ buf->dt_nevents = DM_EVENT_MAX; ++ buf->dt_emask = be32_to_cpu(dic->di_dmevmask); ++ buf->dt_dtime = be32_to_cpu(dic->di_ctime.t_sec); ++ /* Set if one of READ, WRITE or TRUNCATE bits is set in emask */ ++ buf->dt_pmanreg = (DMEV_ISSET(DM_EVENT_READ, buf->dt_emask) || ++ DMEV_ISSET(DM_EVENT_WRITE, buf->dt_emask) || ++ DMEV_ISSET(DM_EVENT_TRUNCATE, buf->dt_emask)) ? 1 : 0; ++} ++ ++/* ++ * Pull out both ondisk and incore fields, incore has preference. ++ * The inode must be kept locked SHARED by the caller. ++ */ ++STATIC void ++xfs_ip_to_stat( ++ xfs_mount_t *mp, ++ xfs_ino_t ino, ++ xfs_inode_t *ip, ++ dm_stat_t *buf) ++{ ++ xfs_icdinode_t *dic = &ip->i_d; ++ ++ buf->dt_ino = ino; ++ buf->dt_nlink = dic->di_nlink; ++ /*buf->dt_xfs_projid = dic->di_projid;*/ ++ buf->dt_mode = dic->di_mode; ++ buf->dt_uid = dic->di_uid; ++ buf->dt_gid = dic->di_gid; ++ buf->dt_size = XFS_ISIZE(ip); ++ buf->dt_dev = new_encode_dev(mp->m_ddev_targp->bt_dev); ++ buf->dt_atime = VFS_I(ip)->i_atime.tv_sec; ++ buf->dt_mtime = dic->di_mtime.t_sec; ++ buf->dt_ctime = dic->di_ctime.t_sec; ++ buf->dt_xfs_xflags = xfs_ip2dmflags(ip); ++ buf->dt_xfs_extsize = dic->di_extsize << mp->m_sb.sb_blocklog; ++ buf->dt_xfs_extents = dic->di_nextents; ++ buf->dt_xfs_aextents = dic->di_anextents; ++ buf->dt_xfs_igen = dic->di_gen; ++ buf->dt_xfs_dmstate = dic->di_dmstate; ++ ++ switch (dic->di_format) { ++ case XFS_DINODE_FMT_DEV: ++ buf->dt_rdev = ip->i_df.if_u2.if_rdev; ++ buf->dt_blksize = BLKDEV_IOSIZE; ++ buf->dt_blocks = 0; ++ break; ++ case XFS_DINODE_FMT_LOCAL: ++ case XFS_DINODE_FMT_UUID: ++ buf->dt_rdev = 0; ++ buf->dt_blksize = mp->m_sb.sb_blocksize; ++ buf->dt_blocks = 0; ++ break; ++ case XFS_DINODE_FMT_EXTENTS: ++ case XFS_DINODE_FMT_BTREE: ++ buf->dt_rdev = 0; ++ buf->dt_blksize = mp->m_sb.sb_blocksize; ++ buf->dt_blocks = XFS_FSB_TO_BB(mp, ++ (dic->di_nblocks + ip->i_delayed_blks)); ++ break; ++ } ++ ++ memset(&buf->dt_pad1, 0, sizeof(buf->dt_pad1)); ++ memset(&buf->dt_pad2, 0, sizeof(buf->dt_pad2)); ++ memset(&buf->dt_pad3, 0, sizeof(buf->dt_pad3)); ++ ++ /* Finally fill in the DMAPI specific fields */ ++ buf->dt_pers = 0; ++ buf->dt_change = 0; ++ buf->dt_nevents = DM_EVENT_MAX; ++ buf->dt_emask = dic->di_dmevmask; ++ buf->dt_dtime = dic->di_ctime.t_sec; ++ /* Set if one of READ, WRITE or TRUNCATE bits is set in emask */ ++ buf->dt_pmanreg = (DMEV_ISSET(DM_EVENT_READ, buf->dt_emask) || ++ DMEV_ISSET(DM_EVENT_WRITE, buf->dt_emask) || ++ DMEV_ISSET(DM_EVENT_TRUNCATE, buf->dt_emask)) ? 1 : 0; ++} ++ ++/* ++ * Take the handle and put it at the end of a dm_xstat buffer. ++ * dt_compname is unused in bulkstat - so we zero it out. ++ * Finally, update link in dm_xstat_t to point to next struct. ++ */ ++STATIC void ++xfs_dm_handle_to_xstat( ++ dm_xstat_t *xbuf, ++ size_t xstat_sz, ++ dm_handle_t *handle, ++ size_t handle_sz) ++{ ++ dm_stat_t *sbuf = &xbuf->dx_statinfo; ++ ++ memcpy(xbuf + 1, handle, handle_sz); ++ sbuf->dt_handle.vd_offset = (ssize_t) sizeof(dm_xstat_t); ++ sbuf->dt_handle.vd_length = (size_t) DM_HSIZE(*handle); ++ memset(&sbuf->dt_compname, 0, sizeof(dm_vardata_t)); ++ sbuf->_link = xstat_sz; ++} ++ ++STATIC int ++xfs_dm_bulkall_iget_one( ++ xfs_mount_t *mp, ++ xfs_ino_t ino, ++ xfs_daddr_t bno, ++ int *value_lenp, ++ dm_xstat_t *xbuf, ++ u_int *xstat_szp, ++ char *attr_name, ++ caddr_t attr_buf) ++{ ++ xfs_inode_t *ip; ++ dm_handle_t handle; ++ u_int xstat_sz = *xstat_szp; ++ int value_len = *value_lenp; ++ int error; ++ ++ error = xfs_iget(mp, NULL, ino, ++ XFS_IGET_BULKSTAT, XFS_ILOCK_SHARED, &ip, bno); ++ if (error) ++ return error; ++ ++ xfs_ip_to_stat(mp, ino, ip, &xbuf->dx_statinfo); ++ dm_ip_to_handle(&ip->i_vnode, &handle); ++ xfs_dm_handle_to_xstat(xbuf, xstat_sz, &handle, sizeof(handle)); ++ ++ /* Drop ILOCK_SHARED for call to xfs_attr_get */ ++ xfs_iunlock(ip, XFS_ILOCK_SHARED); ++ ++ memset(&xbuf->dx_attrdata, 0, sizeof(dm_vardata_t)); ++ error = xfs_attr_get(ip, attr_name, attr_buf, &value_len, ATTR_ROOT); ++ iput(&ip->i_vnode); ++ ++ DM_EA_XLATE_ERR(error); ++ if (error && (error != ENOATTR)) { ++ if (error == E2BIG) ++ error = ENOMEM; ++ return error; ++ } ++ ++ /* How much space was in the attr? */ ++ if (error != ENOATTR) { ++ xbuf->dx_attrdata.vd_offset = xstat_sz; ++ xbuf->dx_attrdata.vd_length = value_len; ++ xstat_sz += (value_len+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1); ++ } ++ *xstat_szp = xbuf->dx_statinfo._link = xstat_sz; ++ *value_lenp = value_len; ++ return 0; ++} ++ ++ ++STATIC int ++xfs_dm_inline_attr( ++ xfs_mount_t *mp, ++ xfs_dinode_t *dip, ++ char *attr_name, ++ caddr_t attr_buf, ++ int *value_lenp) ++{ ++ if (dip->di_aformat == XFS_DINODE_FMT_LOCAL) { ++ xfs_attr_shortform_t *sf; ++ xfs_attr_sf_entry_t *sfe; ++ unsigned int namelen = strlen(attr_name); ++ unsigned int valuelen = *value_lenp; ++ int i; ++ ++ sf = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip); ++ sfe = &sf->list[0]; ++ for (i = 0; i < sf->hdr.count; ++ sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { ++ if (sfe->namelen != namelen) ++ continue; ++ if (!(sfe->flags & XFS_ATTR_ROOT)) ++ continue; ++ if (memcmp(attr_name, sfe->nameval, namelen) != 0) ++ continue; ++ if (valuelen < sfe->valuelen) ++ return ERANGE; ++ valuelen = sfe->valuelen; ++ memcpy(attr_buf, &sfe->nameval[namelen], valuelen); ++ *value_lenp = valuelen; ++ return 0; ++ } ++ } ++ *value_lenp = 0; ++ return ENOATTR; ++} ++ ++STATIC void ++dm_dip_to_handle( ++ xfs_ino_t ino, ++ xfs_dinode_t *dip, ++ dm_fsid_t *fsid, ++ dm_handle_t *handlep) ++{ ++ dm_fid_t fid; ++ int hsize; ++ ++ fid.dm_fid_len = sizeof(struct dm_fid) - sizeof(fid.dm_fid_len); ++ fid.dm_fid_pad = 0; ++ fid.dm_fid_ino = ino; ++ fid.dm_fid_gen = be32_to_cpu(dip->di_gen); ++ ++ memcpy(&handlep->ha_fsid, fsid, sizeof(*fsid)); ++ memcpy(&handlep->ha_fid, &fid, fid.dm_fid_len + sizeof(fid.dm_fid_len)); ++ hsize = DM_HSIZE(*handlep); ++ memset((char *)handlep + hsize, 0, sizeof(*handlep) - hsize); ++} ++ ++STATIC int ++xfs_dm_bulkall_inline_one( ++ xfs_mount_t *mp, ++ xfs_ino_t ino, ++ xfs_dinode_t *dip, ++ dm_fsid_t *fsid, ++ int *value_lenp, ++ dm_xstat_t *xbuf, ++ u_int *xstat_szp, ++ char *attr_name, ++ caddr_t attr_buf) ++{ ++ dm_handle_t handle; ++ u_int xstat_sz = *xstat_szp; ++ int value_len = *value_lenp; ++ int error; ++ ++ if (dip->di_mode == 0) ++ return ENOENT; ++ ++ xfs_dip_to_stat(mp, ino, dip, &xbuf->dx_statinfo); ++ dm_dip_to_handle(ino, dip, fsid, &handle); ++ xfs_dm_handle_to_xstat(xbuf, xstat_sz, &handle, sizeof(handle)); ++ ++ memset(&xbuf->dx_attrdata, 0, sizeof(dm_vardata_t)); ++ error = xfs_dm_inline_attr(mp, dip, attr_name, attr_buf, &value_len); ++ DM_EA_XLATE_ERR(error); ++ if (error && (error != ENOATTR)) { ++ if (error == E2BIG) ++ error = ENOMEM; ++ return error; ++ } ++ ++ /* How much space was in the attr? */ ++ if (error != ENOATTR) { ++ xbuf->dx_attrdata.vd_offset = xstat_sz; ++ xbuf->dx_attrdata.vd_length = value_len; ++ xstat_sz += (value_len+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1); ++ } ++ *xstat_szp = xbuf->dx_statinfo._link = xstat_sz; ++ *value_lenp = value_len; ++ return 0; ++} ++ ++/* ++ * This is used by dm_get_bulkall(). ++ * Given a inumber, it igets the inode and fills the given buffer ++ * with the dm_xstat structure for the file. ++ */ ++STATIC int ++xfs_dm_bulkall_one( ++ xfs_mount_t *mp, /* mount point for filesystem */ ++ xfs_ino_t ino, /* inode number to get data for */ ++ void __user *buffer, /* buffer to place output in */ ++ int ubsize, /* size of buffer */ ++ void *private_data, /* my private data */ ++ xfs_daddr_t bno, /* starting block of inode cluster */ ++ int *ubused, /* amount of buffer we used */ ++ void *dibuff, /* on-disk inode buffer */ ++ int *res) /* bulkstat result code */ ++{ ++ dm_xstat_t *xbuf; ++ u_int xstat_sz; ++ int error; ++ int value_len; ++ int kern_buf_sz; ++ int attr_buf_sz; ++ caddr_t attr_buf; ++ void __user *attr_user_buf; ++ dm_bulkstat_one_t *dmb = (dm_bulkstat_one_t*)private_data; ++ ++ /* Returns positive errors to XFS */ ++ ++ *res = BULKSTAT_RV_NOTHING; ++ ++ if (!buffer || xfs_internal_inum(mp, ino)) ++ return EINVAL; ++ ++ xstat_sz = DM_STAT_SIZE(*xbuf, 0); ++ xstat_sz = (xstat_sz + (DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1); ++ if (xstat_sz > ubsize) ++ return ENOMEM; ++ ++ kern_buf_sz = xstat_sz; ++ xbuf = kmem_alloc(kern_buf_sz, KM_SLEEP); ++ ++ /* Determine place to drop attr value, and available space. */ ++ value_len = ubsize - xstat_sz; ++ if (value_len > ATTR_MAX_VALUELEN) ++ value_len = ATTR_MAX_VALUELEN; ++ ++ attr_user_buf = buffer + xstat_sz; ++ attr_buf_sz = value_len; ++ attr_buf = kmem_alloc(attr_buf_sz, KM_SLEEP); ++ ++ if (!dibuff) ++ error = xfs_dm_bulkall_iget_one(mp, ino, bno, ++ &value_len, xbuf, &xstat_sz, ++ dmb->attrname.dan_chars, ++ attr_buf); ++ else ++ error = xfs_dm_bulkall_inline_one(mp, ino, ++ (xfs_dinode_t *)dibuff, ++ &dmb->fsid, ++ &value_len, xbuf, &xstat_sz, ++ dmb->attrname.dan_chars, ++ attr_buf); ++ if (error) ++ goto out_free_buffers; ++ ++ if (copy_to_user(buffer, xbuf, kern_buf_sz)) { ++ error = EFAULT; ++ goto out_free_buffers; ++ } ++ if (copy_to_user(attr_user_buf, attr_buf, value_len)) { ++ error = EFAULT; ++ goto out_free_buffers; ++ } ++ ++ kmem_free(attr_buf); ++ kmem_free(xbuf); ++ ++ *res = BULKSTAT_RV_DIDONE; ++ if (ubused) ++ *ubused = xstat_sz; ++ dmb->laststruct = buffer; ++ return 0; ++ ++ out_free_buffers: ++ kmem_free(attr_buf); ++ kmem_free(xbuf); ++ return error; ++} ++ ++/* ++ * Take the handle and put it at the end of a dm_stat buffer. ++ * dt_compname is unused in bulkstat - so we zero it out. ++ * Finally, update link in dm_stat_t to point to next struct. ++ */ ++STATIC void ++xfs_dm_handle_to_stat( ++ dm_stat_t *sbuf, ++ size_t stat_sz, ++ dm_handle_t *handle, ++ size_t handle_sz) ++{ ++ memcpy(sbuf + 1, handle, handle_sz); ++ sbuf->dt_handle.vd_offset = (ssize_t) sizeof(dm_stat_t); ++ sbuf->dt_handle.vd_length = (size_t) DM_HSIZE(*handle); ++ memset(&sbuf->dt_compname, 0, sizeof(dm_vardata_t)); ++ sbuf->_link = stat_sz; ++} ++ ++STATIC int ++xfs_dm_bulkattr_iget_one( ++ xfs_mount_t *mp, ++ xfs_ino_t ino, ++ xfs_daddr_t bno, ++ dm_stat_t *sbuf, ++ u_int stat_sz) ++{ ++ xfs_inode_t *ip; ++ dm_handle_t handle; ++ int error; ++ ++ error = xfs_iget(mp, NULL, ino, ++ XFS_IGET_BULKSTAT, XFS_ILOCK_SHARED, &ip, bno); ++ if (error) ++ return error; ++ ++ xfs_ip_to_stat(mp, ino, ip, sbuf); ++ dm_ip_to_handle(&ip->i_vnode, &handle); ++ xfs_dm_handle_to_stat(sbuf, stat_sz, &handle, sizeof(handle)); ++ ++ xfs_iput(ip, XFS_ILOCK_SHARED); ++ return 0; ++} ++ ++STATIC int ++xfs_dm_bulkattr_inline_one( ++ xfs_mount_t *mp, ++ xfs_ino_t ino, ++ xfs_dinode_t *dip, ++ dm_fsid_t *fsid, ++ dm_stat_t *sbuf, ++ u_int stat_sz) ++{ ++ dm_handle_t handle; ++ ++ if (dip->di_mode == 0) ++ return ENOENT; ++ xfs_dip_to_stat(mp, ino, dip, sbuf); ++ dm_dip_to_handle(ino, dip, fsid, &handle); ++ xfs_dm_handle_to_stat(sbuf, stat_sz, &handle, sizeof(handle)); ++ return 0; ++} ++ ++/* ++ * This is used by dm_get_bulkattr(). ++ * Given a inumber, it igets the inode and fills the given buffer ++ * with the dm_stat structure for the file. ++ */ ++STATIC int ++xfs_dm_bulkattr_one( ++ xfs_mount_t *mp, /* mount point for filesystem */ ++ xfs_ino_t ino, /* inode number to get data for */ ++ void __user *buffer, /* buffer to place output in */ ++ int ubsize, /* size of buffer */ ++ void *private_data, /* my private data */ ++ xfs_daddr_t bno, /* starting block of inode cluster */ ++ int *ubused, /* amount of buffer we used */ ++ void *dibuff, /* on-disk inode buffer */ ++ int *res) /* bulkstat result code */ ++{ ++ dm_stat_t *sbuf; ++ u_int stat_sz; ++ int error; ++ dm_bulkstat_one_t *dmb = (dm_bulkstat_one_t*)private_data; ++ ++ /* Returns positive errors to XFS */ ++ ++ *res = BULKSTAT_RV_NOTHING; ++ ++ if (!buffer || xfs_internal_inum(mp, ino)) ++ return EINVAL; ++ ++ stat_sz = DM_STAT_SIZE(*sbuf, 0); ++ stat_sz = (stat_sz+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1); ++ if (stat_sz > ubsize) ++ return ENOMEM; ++ ++ sbuf = kmem_alloc(stat_sz, KM_SLEEP); ++ ++ if (!dibuff) ++ error = xfs_dm_bulkattr_iget_one(mp, ino, bno, sbuf, stat_sz); ++ else ++ error = xfs_dm_bulkattr_inline_one(mp, ino, ++ (xfs_dinode_t *)dibuff, ++ &dmb->fsid, sbuf, stat_sz); ++ if (error) ++ goto out_free_buffer; ++ ++ if (copy_to_user(buffer, sbuf, stat_sz)) { ++ error = EFAULT; ++ goto out_free_buffer; ++ } ++ ++ kmem_free(sbuf); ++ *res = BULKSTAT_RV_DIDONE; ++ if (ubused) ++ *ubused = stat_sz; ++ dmb->laststruct = buffer; ++ return 0; ++ ++ out_free_buffer: ++ kmem_free(sbuf); ++ return error; ++} ++ ++/* xfs_dm_f_get_eventlist - return the dm_eventset_t mask for inode ip. */ ++ ++STATIC int ++xfs_dm_f_get_eventlist( ++ xfs_inode_t *ip, ++ dm_right_t right, ++ u_int nelem, ++ dm_eventset_t *eventsetp, /* in kernel space! */ ++ u_int *nelemp) /* in kernel space! */ ++{ ++ dm_eventset_t eventset; ++ ++ if (right < DM_RIGHT_SHARED) ++ return(EACCES); ++ ++ /* Note that we MUST return a regular file's managed region bits as ++ part of the mask because dm_get_eventlist is supposed to return the ++ union of all managed region flags in those bits. Since we only ++ support one region, we can just return the bits as they are. For ++ all other object types, the bits will already be zero. Handy, huh? ++ */ ++ ++ eventset = ip->i_d.di_dmevmask; ++ ++ /* Now copy the event mask and event count back to the caller. We ++ return the lesser of nelem and DM_EVENT_MAX. ++ */ ++ ++ if (nelem > DM_EVENT_MAX) ++ nelem = DM_EVENT_MAX; ++ eventset &= (1 << nelem) - 1; ++ ++ *eventsetp = eventset; ++ *nelemp = nelem; ++ return(0); ++} ++ ++ ++/* xfs_dm_f_set_eventlist - update the dm_eventset_t mask in the inode vp. Only the ++ bits from zero to maxevent-1 are being replaced; higher bits are preserved. ++*/ ++ ++STATIC int ++xfs_dm_f_set_eventlist( ++ xfs_inode_t *ip, ++ dm_right_t right, ++ dm_eventset_t *eventsetp, /* in kernel space! */ ++ u_int maxevent) ++{ ++ dm_eventset_t eventset; ++ dm_eventset_t max_mask; ++ dm_eventset_t valid_events; ++ xfs_trans_t *tp; ++ xfs_mount_t *mp; ++ int error; ++ ++ if (right < DM_RIGHT_EXCL) ++ return(EACCES); ++ ++ eventset = *eventsetp; ++ if (maxevent >= sizeof(ip->i_d.di_dmevmask) * NBBY) ++ return(EINVAL); ++ max_mask = (1 << maxevent) - 1; ++ ++ if (S_ISDIR(ip->i_d.di_mode)) { ++ valid_events = DM_XFS_VALID_DIRECTORY_EVENTS; ++ } else { /* file or symlink */ ++ valid_events = DM_XFS_VALID_FILE_EVENTS; ++ } ++ if ((eventset & max_mask) & ~valid_events) ++ return(EINVAL); ++ ++ /* Adjust the event mask so that the managed region bits will not ++ be altered. ++ */ ++ ++ max_mask &= ~(1 <i_mount; ++ tp = xfs_trans_alloc(mp, XFS_TRANS_SET_DMATTRS); ++ error = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0); ++ if (error) { ++ xfs_trans_cancel(tp, 0); ++ return(error); ++ } ++ xfs_ilock(ip, XFS_ILOCK_EXCL); ++ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); ++ ++ ip->i_d.di_dmevmask = (eventset & max_mask) | (ip->i_d.di_dmevmask & ~max_mask); ++ ++ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); ++ igrab(&ip->i_vnode); ++ xfs_trans_commit(tp, 0); ++ ++ return(0); ++} ++ ++ ++/* xfs_dm_fs_get_eventlist - return the dm_eventset_t mask for filesystem vfsp. */ ++ ++STATIC int ++xfs_dm_fs_get_eventlist( ++ xfs_mount_t *mp, ++ dm_right_t right, ++ u_int nelem, ++ dm_eventset_t *eventsetp, /* in kernel space! */ ++ u_int *nelemp) /* in kernel space! */ ++{ ++ dm_eventset_t eventset; ++ ++ if (right < DM_RIGHT_SHARED) ++ return(EACCES); ++ ++ eventset = mp->m_dmevmask; ++ ++ /* Now copy the event mask and event count back to the caller. We ++ return the lesser of nelem and DM_EVENT_MAX. ++ */ ++ ++ if (nelem > DM_EVENT_MAX) ++ nelem = DM_EVENT_MAX; ++ eventset &= (1 << nelem) - 1; ++ ++ *eventsetp = eventset; ++ *nelemp = nelem; ++ return(0); ++} ++ ++ ++/* xfs_dm_fs_set_eventlist - update the dm_eventset_t mask in the mount structure for ++ filesystem vfsp. Only the bits from zero to maxevent-1 are being replaced; ++ higher bits are preserved. ++*/ ++ ++STATIC int ++xfs_dm_fs_set_eventlist( ++ xfs_mount_t *mp, ++ dm_right_t right, ++ dm_eventset_t *eventsetp, /* in kernel space! */ ++ u_int maxevent) ++{ ++ dm_eventset_t eventset; ++ dm_eventset_t max_mask; ++ ++ if (right < DM_RIGHT_EXCL) ++ return(EACCES); ++ ++ eventset = *eventsetp; ++ ++ if (maxevent >= sizeof(mp->m_dmevmask) * NBBY) ++ return(EINVAL); ++ max_mask = (1 << maxevent) - 1; ++ ++ if ((eventset & max_mask) & ~DM_XFS_VALID_FS_EVENTS) ++ return(EINVAL); ++ ++ mp->m_dmevmask = (eventset & max_mask) | (mp->m_dmevmask & ~max_mask); ++ return(0); ++} ++ ++ ++/* Code in this routine must exactly match the logic in xfs_diordwr() in ++ order for this to work! ++*/ ++ ++STATIC int ++xfs_dm_direct_ok( ++ xfs_inode_t *ip, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp) ++{ ++ xfs_mount_t *mp; ++ ++ mp = ip->i_mount; ++ ++ /* Realtime files can ONLY do direct I/O. */ ++ ++ if (XFS_IS_REALTIME_INODE(ip)) ++ return(1); ++ ++ /* If direct I/O is disabled, or if the request is too small, use ++ buffered I/O. ++ */ ++ ++ if (!dm_min_dio_xfer || len < dm_min_dio_xfer) ++ return(0); ++ ++#if 0 ++ /* If the request is not well-formed or is too large, use ++ buffered I/O. ++ */ ++ ++ if ((__psint_t)bufp & scache_linemask) /* if buffer not aligned */ ++ return(0); ++ if (off & mp->m_blockmask) /* if file offset not aligned */ ++ return(0); ++ if (len & mp->m_blockmask) /* if xfer length not aligned */ ++ return(0); ++ if (len > ctooff(v.v_maxdmasz - 1)) /* if transfer too large */ ++ return(0); ++ ++ /* A valid direct I/O candidate. */ ++ ++ return(1); ++#else ++ return(0); ++#endif ++} ++ ++ ++/* We need to be able to select various combinations of O_NONBLOCK, ++ O_DIRECT, and O_SYNC, yet we don't have a file descriptor and we don't have ++ the file's pathname. All we have is a handle. ++*/ ++ ++STATIC int ++xfs_dm_rdwr( ++ struct inode *inode, ++ uint fflag, ++ mode_t fmode, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp) ++{ ++ const struct cred *cred = current_cred(); ++ xfs_inode_t *ip = XFS_I(inode); ++ int error; ++ int oflags; ++ ssize_t xfer; ++ struct file *file; ++ struct dentry *dentry; ++ ++ if ((off < 0) || (off > i_size_read(inode)) || !S_ISREG(inode->i_mode)) ++ return EINVAL; ++ ++ if (fmode & FMODE_READ) { ++ oflags = O_RDONLY; ++ } else { ++ oflags = O_WRONLY; ++ } ++ ++ /* ++ * Build file descriptor flags and I/O flags. O_NONBLOCK is needed so ++ * that we don't block on mandatory file locks. This is an invisible IO, ++ * don't change the atime. ++ */ ++ ++ oflags |= O_LARGEFILE | O_NONBLOCK | O_NOATIME; ++ if (xfs_dm_direct_ok(ip, off, len, bufp)) ++ oflags |= O_DIRECT; ++ ++ if (fflag & O_SYNC) ++ oflags |= O_SYNC; ++ ++ if (inode->i_fop == NULL) { ++ /* no iput; caller did get, and will do put */ ++ return EINVAL; ++ } ++ ++ igrab(inode); ++ ++ dentry = d_obtain_alias(inode); ++ if (dentry == NULL) { ++ iput(inode); ++ return ENOMEM; ++ } ++ ++ file = dentry_open(dentry, mntget(ip->i_mount->m_vfsmount), oflags, ++ cred); ++ if (IS_ERR(file)) { ++ return -PTR_ERR(file); ++ } ++ file->f_mode |= FMODE_NOCMTIME; ++ ++ if (fmode & FMODE_READ) { ++ xfer = file->f_op->read(file, bufp, len, (loff_t*)&off); ++ } else { ++ xfer = file->f_op->write(file, bufp, len, (loff_t*)&off); ++ } ++ ++ if (xfer >= 0) { ++ *rvp = xfer; ++ error = 0; ++ } else { ++ /* xfs_read/xfs_write return negative error--flip it */ ++ error = -(int)xfer; ++ } ++ ++ fput(file); ++ return error; ++} ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_clear_inherit( ++ struct inode *inode, ++ dm_right_t right, ++ dm_attrname_t __user *attrnamep) ++{ ++ return(-ENOSYS); /* Return negative error to DMAPI */ ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_create_by_handle( ++ struct inode *inode, ++ dm_right_t right, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname) ++{ ++ return(-ENOSYS); /* Return negative error to DMAPI */ ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_downgrade_right( ++ struct inode *inode, ++ dm_right_t right, ++ u_int type) /* DM_FSYS_OBJ or zero */ ++{ ++#ifdef DEBUG_RIGHTS ++ char buffer[sizeof(dm_handle_t) * 2 + 1]; ++ ++ if (!xfs_vp_to_hexhandle(inode, type, buffer)) { ++ printf("dm_downgrade_right: old %d new %d type %d handle %s\n", ++ right, DM_RIGHT_SHARED, type, buffer); ++ } else { ++ printf("dm_downgrade_right: old %d new %d type %d handle " ++ "\n", right, DM_RIGHT_SHARED, type); ++ } ++#endif /* DEBUG_RIGHTS */ ++ return(0); ++} ++ ++ ++/* Note: xfs_dm_get_allocinfo() makes no attempt to coalesce two adjacent ++ extents when both are of type DM_EXTENT_RES; this is left to the caller. ++ XFS guarantees that there will never be two adjacent DM_EXTENT_HOLE extents. ++ ++ In order to provide the caller with all extents in a file including ++ those beyond the file's last byte offset, we have to use the xfs_bmapi() ++ interface. ++*/ ++ ++STATIC int ++xfs_dm_get_allocinfo_rvp( ++ struct inode *inode, ++ dm_right_t right, ++ dm_off_t __user *offp, ++ u_int nelem, ++ dm_extent_t __user *extentp, ++ u_int __user *nelemp, ++ int *rvp) ++{ ++ xfs_inode_t *ip = XFS_I(inode); ++ xfs_mount_t *mp; /* file system mount point */ ++ xfs_fileoff_t fsb_offset; ++ xfs_filblks_t fsb_length; ++ dm_off_t startoff; ++ int elem; ++ xfs_bmbt_irec_t *bmp = NULL; ++ u_int bmpcnt = 50; ++ u_int bmpsz = sizeof(xfs_bmbt_irec_t) * bmpcnt; ++ int error = 0; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ if ((inode->i_mode & S_IFMT) != S_IFREG) ++ return(-EINVAL); ++ ++ if (copy_from_user( &startoff, offp, sizeof(startoff))) ++ return(-EFAULT); ++ ++ mp = ip->i_mount; ++ ASSERT(mp); ++ ++ if (startoff > XFS_MAXIOFFSET(mp)) ++ return(-EINVAL); ++ ++ if (nelem == 0) ++ return(-EINVAL); ++ ++ /* Convert the caller's starting offset into filesystem allocation ++ units as required by xfs_bmapi(). Round the offset down so that ++ it is sure to be included in the reply. ++ */ ++ ++ fsb_offset = XFS_B_TO_FSBT(mp, startoff); ++ fsb_length = XFS_B_TO_FSB(mp, XFS_MAXIOFFSET(mp)) - fsb_offset; ++ elem = 0; ++ ++ if (fsb_length) ++ bmp = kmem_alloc(bmpsz, KM_SLEEP); ++ ++ while (fsb_length && elem < nelem) { ++ dm_extent_t extent; ++ xfs_filblks_t fsb_bias; ++ dm_size_t bias; ++ int lock; ++ int num; ++ int i; ++ ++ /* Compute how many getbmap structures to use on the xfs_bmapi ++ call. ++ */ ++ ++ num = MIN((u_int)(nelem - elem), bmpcnt); ++ ++ xfs_ilock(ip, XFS_IOLOCK_SHARED); ++ lock = xfs_ilock_map_shared(ip); ++ ++ error = xfs_bmapi(NULL, ip, fsb_offset, fsb_length, ++ XFS_BMAPI_ENTIRE, NULL, 0, bmp, &num, NULL, NULL); ++ ++ xfs_iunlock_map_shared(ip, lock); ++ xfs_iunlock(ip, XFS_IOLOCK_SHARED); ++ ++ if (error) { ++ error = -error; /* Return negative error to DMAPI */ ++ goto finish_out; ++ } ++ ++ /* Fill in the caller's extents, adjusting the bias in the ++ first entry if necessary. ++ */ ++ ++ for (i = 0; i < num; i++, extentp++) { ++ bias = startoff - XFS_FSB_TO_B(mp, bmp[i].br_startoff); ++ extent.ex_offset = startoff; ++ extent.ex_length = ++ XFS_FSB_TO_B(mp, bmp[i].br_blockcount) - bias; ++ if (bmp[i].br_startblock == HOLESTARTBLOCK) { ++ extent.ex_type = DM_EXTENT_HOLE; ++ } else { ++ extent.ex_type = DM_EXTENT_RES; ++ } ++ startoff = extent.ex_offset + extent.ex_length; ++ ++ if (copy_to_user( extentp, &extent, sizeof(extent))) { ++ error = -EFAULT; ++ goto finish_out; ++ } ++ ++ fsb_bias = fsb_offset - bmp[i].br_startoff; ++ fsb_offset += bmp[i].br_blockcount - fsb_bias; ++ fsb_length -= bmp[i].br_blockcount - fsb_bias; ++ elem++; ++ } ++ } ++ ++ if (fsb_length == 0) { ++ startoff = 0; ++ } ++ if (copy_to_user( offp, &startoff, sizeof(startoff))) { ++ error = -EFAULT; ++ goto finish_out; ++ } ++ ++ if (copy_to_user( nelemp, &elem, sizeof(elem))) { ++ error = -EFAULT; ++ goto finish_out; ++ } ++ ++ *rvp = (fsb_length == 0 ? 0 : 1); ++ ++finish_out: ++ if (bmp) ++ kmem_free(bmp); ++ return(error); ++} ++ ++ ++STATIC int ++xfs_dm_zero_xstatinfo_link( ++ dm_xstat_t __user *dxs) ++{ ++ dm_xstat_t *ldxs; ++ int error = 0; ++ ++ if (!dxs) ++ return 0; ++ ldxs = kmalloc(sizeof(*ldxs), GFP_KERNEL); ++ if (!ldxs) ++ return -ENOMEM; ++ if (copy_from_user(ldxs, dxs, sizeof(*dxs))) { ++ error = -EFAULT; ++ } else { ++ ldxs->dx_statinfo._link = 0; ++ if (copy_to_user(dxs, ldxs, sizeof(*dxs))) ++ error = -EFAULT; ++ } ++ kfree(ldxs); ++ return error; ++} ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_get_bulkall_rvp( ++ struct inode *inode, ++ dm_right_t right, ++ u_int mask, ++ dm_attrname_t __user *attrnamep, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, /* address of buffer in user space */ ++ size_t __user *rlenp, /* user space address */ ++ int *rvalp) ++{ ++ int error, done; ++ int nelems; ++ u_int statstruct_sz; ++ dm_attrloc_t loc; ++ xfs_mount_t *mp = XFS_I(inode)->i_mount; ++ dm_attrname_t attrname; ++ dm_bulkstat_one_t dmb; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (copy_from_user(&attrname, attrnamep, sizeof(attrname)) || ++ copy_from_user(&loc, locp, sizeof(loc))) ++ return -EFAULT; ++ ++ if (attrname.an_chars[0] == '\0') ++ return(-EINVAL); ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ /* Because we will write directly to the user's buffer, make sure that ++ the buffer is properly aligned. ++ */ ++ ++ if (((unsigned long)bufp & (DM_STAT_ALIGN - 1)) != 0) ++ return(-EFAULT); ++ ++ /* Size of the handle is constant for this function. ++ * If there are no files with attributes, then this will be the ++ * maximum number of inodes we can get. ++ */ ++ ++ statstruct_sz = DM_STAT_SIZE(dm_xstat_t, 0); ++ statstruct_sz = (statstruct_sz+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1); ++ ++ nelems = buflen / statstruct_sz; ++ if (nelems < 1) { ++ if (put_user( statstruct_sz, rlenp )) ++ return(-EFAULT); ++ return(-E2BIG); ++ } ++ ++ /* Build the on-disk version of the attribute name. */ ++ strcpy(dmb.attrname.dan_chars, dmattr_prefix); ++ strncpy(&dmb.attrname.dan_chars[DMATTR_PREFIXLEN], ++ attrname.an_chars, DM_ATTR_NAME_SIZE + 1); ++ dmb.attrname.dan_chars[sizeof(dmb.attrname.dan_chars) - 1] = '\0'; ++ ++ /* ++ * fill the buffer with dm_xstat_t's ++ */ ++ ++ dmb.laststruct = NULL; ++ memcpy(&dmb.fsid, mp->m_fixedfsid, sizeof(dm_fsid_t)); ++ error = xfs_bulkstat(mp, (xfs_ino_t *)&loc, &nelems, ++ xfs_dm_bulkall_one, (void*)&dmb, statstruct_sz, ++ bufp, BULKSTAT_FG_INLINE, &done); ++ if (error) ++ return(-error); /* Return negative error to DMAPI */ ++ ++ *rvalp = !done ? 1 : 0; ++ ++ if (put_user( statstruct_sz * nelems, rlenp )) ++ return(-EFAULT); ++ ++ if (copy_to_user( locp, &loc, sizeof(loc))) ++ return(-EFAULT); ++ /* ++ * If we didn't do any, we must not have any more to do. ++ */ ++ if (nelems < 1) ++ return(0); ++ /* ++ * Set _link in the last struct to zero ++ */ ++ return xfs_dm_zero_xstatinfo_link((dm_xstat_t __user *)dmb.laststruct); ++} ++ ++ ++STATIC int ++xfs_dm_zero_statinfo_link( ++ dm_stat_t __user *dxs) ++{ ++ dm_stat_t *ldxs; ++ int error = 0; ++ ++ if (!dxs) ++ return 0; ++ ldxs = kmalloc(sizeof(*ldxs), GFP_KERNEL); ++ if (!ldxs) ++ return -ENOMEM; ++ if (copy_from_user(ldxs, dxs, sizeof(*dxs))) { ++ error = -EFAULT; ++ } else { ++ ldxs->_link = 0; ++ if (copy_to_user(dxs, ldxs, sizeof(*dxs))) ++ error = -EFAULT; ++ } ++ kfree(ldxs); ++ return error; ++} ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_get_bulkattr_rvp( ++ struct inode *inode, ++ dm_right_t right, ++ u_int mask, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvalp) ++{ ++ int error, done; ++ int nelems; ++ u_int statstruct_sz; ++ dm_attrloc_t loc; ++ xfs_mount_t *mp = XFS_I(inode)->i_mount; ++ dm_bulkstat_one_t dmb; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ if (copy_from_user( &loc, locp, sizeof(loc))) ++ return(-EFAULT); ++ ++ /* Because we will write directly to the user's buffer, make sure that ++ the buffer is properly aligned. ++ */ ++ ++ if (((unsigned long)bufp & (DM_STAT_ALIGN - 1)) != 0) ++ return(-EFAULT); ++ ++ /* size of the handle is constant for this function */ ++ ++ statstruct_sz = DM_STAT_SIZE(dm_stat_t, 0); ++ statstruct_sz = (statstruct_sz+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1); ++ ++ nelems = buflen / statstruct_sz; ++ if (nelems < 1) { ++ if (put_user( statstruct_sz, rlenp )) ++ return(-EFAULT); ++ return(-E2BIG); ++ } ++ ++ dmb.laststruct = NULL; ++ memcpy(&dmb.fsid, mp->m_fixedfsid, sizeof(dm_fsid_t)); ++ error = xfs_bulkstat(mp, (xfs_ino_t *)&loc, &nelems, ++ xfs_dm_bulkattr_one, (void*)&dmb, ++ statstruct_sz, bufp, BULKSTAT_FG_INLINE, &done); ++ if (error) ++ return(-error); /* Return negative error to DMAPI */ ++ ++ *rvalp = !done ? 1 : 0; ++ ++ if (put_user( statstruct_sz * nelems, rlenp )) ++ return(-EFAULT); ++ ++ if (copy_to_user( locp, &loc, sizeof(loc))) ++ return(-EFAULT); ++ ++ /* ++ * If we didn't do any, we must not have any more to do. ++ */ ++ if (nelems < 1) ++ return(0); ++ /* ++ * Set _link in the last struct to zero ++ */ ++ return xfs_dm_zero_statinfo_link((dm_stat_t __user *)dmb.laststruct); ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_get_config( ++ struct inode *inode, ++ dm_right_t right, ++ dm_config_t flagname, ++ dm_size_t __user *retvalp) ++{ ++ dm_size_t retval; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ switch (flagname) { ++ case DM_CONFIG_DTIME_OVERLOAD: ++ case DM_CONFIG_PERS_ATTRIBUTES: ++ case DM_CONFIG_PERS_EVENTS: ++ case DM_CONFIG_PERS_MANAGED_REGIONS: ++ case DM_CONFIG_PUNCH_HOLE: ++ case DM_CONFIG_WILL_RETRY: ++ retval = DM_TRUE; ++ break; ++ ++ case DM_CONFIG_CREATE_BY_HANDLE: /* these will never be done */ ++ case DM_CONFIG_LOCK_UPGRADE: ++ case DM_CONFIG_PERS_INHERIT_ATTRIBS: ++ retval = DM_FALSE; ++ break; ++ ++ case DM_CONFIG_BULKALL: ++ retval = DM_TRUE; ++ break; ++ case DM_CONFIG_MAX_ATTR_ON_DESTROY: ++ retval = DM_MAX_ATTR_BYTES_ON_DESTROY; ++ break; ++ ++ case DM_CONFIG_MAX_ATTRIBUTE_SIZE: ++ retval = ATTR_MAX_VALUELEN; ++ break; ++ ++ case DM_CONFIG_MAX_HANDLE_SIZE: ++ retval = DM_MAX_HANDLE_SIZE; ++ break; ++ ++ case DM_CONFIG_MAX_MANAGED_REGIONS: ++ retval = 1; ++ break; ++ ++ case DM_CONFIG_TOTAL_ATTRIBUTE_SPACE: ++ retval = 0x7fffffff; /* actually it's unlimited */ ++ break; ++ ++ default: ++ return(-EINVAL); ++ } ++ ++ /* Copy the results back to the user. */ ++ ++ if (copy_to_user( retvalp, &retval, sizeof(retval))) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_get_config_events( ++ struct inode *inode, ++ dm_right_t right, ++ u_int nelem, ++ dm_eventset_t __user *eventsetp, ++ u_int __user *nelemp) ++{ ++ dm_eventset_t eventset; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (nelem == 0) ++ return(-EINVAL); ++ ++ eventset = DM_XFS_SUPPORTED_EVENTS; ++ ++ /* Now copy the event mask and event count back to the caller. We ++ return the lesser of nelem and DM_EVENT_MAX. ++ */ ++ ++ if (nelem > DM_EVENT_MAX) ++ nelem = DM_EVENT_MAX; ++ eventset &= (1 << nelem) - 1; ++ ++ if (copy_to_user( eventsetp, &eventset, sizeof(eventset))) ++ return(-EFAULT); ++ ++ if (put_user(nelem, nelemp)) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_get_destroy_dmattr( ++ struct inode *inode, ++ dm_right_t right, ++ dm_attrname_t *attrnamep, ++ char **valuepp, ++ int *vlenp) ++{ ++ dm_dkattrname_t dkattrname; ++ int alloc_size; ++ int value_len; ++ char *value; ++ int error; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ *vlenp = -1; /* assume failure by default */ ++ ++ if (attrnamep->an_chars[0] == '\0') ++ return(-EINVAL); ++ ++ /* Build the on-disk version of the attribute name. */ ++ ++ strcpy(dkattrname.dan_chars, dmattr_prefix); ++ strncpy(&dkattrname.dan_chars[DMATTR_PREFIXLEN], ++ (char *)attrnamep->an_chars, DM_ATTR_NAME_SIZE + 1); ++ dkattrname.dan_chars[sizeof(dkattrname.dan_chars) - 1] = '\0'; ++ ++ /* xfs_attr_get will not return anything if the buffer is too small, ++ and we don't know how big to make the buffer, so this may take ++ two tries to get it right. The initial try must use a buffer of ++ at least XFS_BUG_KLUDGE bytes to prevent buffer overflow because ++ of a bug in XFS. ++ */ ++ ++ alloc_size = XFS_BUG_KLUDGE; ++ value = kmalloc(alloc_size, GFP_KERNEL); ++ if (value == NULL) ++ return(-ENOMEM); ++ ++ error = xfs_attr_get(XFS_I(inode), dkattrname.dan_chars, value, ++ &value_len, ATTR_ROOT); ++ if (error == ERANGE) { ++ kfree(value); ++ alloc_size = value_len; ++ value = kmalloc(alloc_size, GFP_KERNEL); ++ if (value == NULL) ++ return(-ENOMEM); ++ ++ error = xfs_attr_get(XFS_I(inode), dkattrname.dan_chars, value, ++ &value_len, ATTR_ROOT); ++ } ++ if (error) { ++ kfree(value); ++ DM_EA_XLATE_ERR(error); ++ return(-error); /* Return negative error to DMAPI */ ++ } ++ ++ /* The attribute exists and has a value. Note that a value_len of ++ zero is valid! ++ */ ++ ++ if (value_len == 0) { ++ kfree(value); ++ *vlenp = 0; ++ return(0); ++ } else if (value_len > DM_MAX_ATTR_BYTES_ON_DESTROY) { ++ char *value2; ++ ++ value2 = kmalloc(DM_MAX_ATTR_BYTES_ON_DESTROY, GFP_KERNEL); ++ if (value2 == NULL) { ++ kfree(value); ++ return(-ENOMEM); ++ } ++ memcpy(value2, value, DM_MAX_ATTR_BYTES_ON_DESTROY); ++ kfree(value); ++ value = value2; ++ value_len = DM_MAX_ATTR_BYTES_ON_DESTROY; ++ } ++ *vlenp = value_len; ++ *valuepp = value; ++ return(0); ++} ++ ++/* This code was taken from xfs_fcntl(F_DIOINFO) and modified slightly because ++ we don't have a flags parameter (no open file). ++ Taken from xfs_ioctl(XFS_IOC_DIOINFO) on Linux. ++*/ ++ ++STATIC int ++xfs_dm_get_dioinfo( ++ struct inode *inode, ++ dm_right_t right, ++ dm_dioinfo_t __user *diop) ++{ ++ dm_dioinfo_t dio; ++ xfs_mount_t *mp; ++ xfs_inode_t *ip = XFS_I(inode); ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ mp = ip->i_mount; ++ ++ dio.d_miniosz = dio.d_mem = MIN_DIO_SIZE(mp); ++ dio.d_maxiosz = MAX_DIO_SIZE(mp); ++ dio.d_dio_only = DM_FALSE; ++ ++ if (copy_to_user(diop, &dio, sizeof(dio))) ++ return(-EFAULT); ++ return(0); ++} ++ ++typedef struct dm_readdir_cb { ++ xfs_mount_t *mp; ++ char __user *ubuf; ++ dm_stat_t __user *lastbuf; ++ size_t spaceleft; ++ size_t nwritten; ++ int error; ++ dm_stat_t kstat; ++} dm_readdir_cb_t; ++ ++STATIC int ++dm_filldir(void *__buf, const char *name, int namelen, loff_t offset, ++ u64 ino, unsigned int d_type) ++{ ++ dm_readdir_cb_t *cb = __buf; ++ dm_stat_t *statp = &cb->kstat; ++ size_t len; ++ int error; ++ int needed; ++ ++ /* ++ * Make sure we have enough space. ++ */ ++ needed = dm_stat_size(namelen + 1); ++ if (cb->spaceleft < needed) { ++ cb->spaceleft = 0; ++ return -ENOSPC; ++ } ++ ++ error = -EINVAL; ++ if (xfs_internal_inum(cb->mp, ino)) ++ goto out_err; ++ ++ memset(statp, 0, dm_stat_size(MAXNAMLEN)); ++ error = -xfs_dm_bulkattr_iget_one(cb->mp, ino, 0, ++ statp, needed); ++ if (error) ++ goto out_err; ++ ++ /* ++ * On return from bulkstat_one(), stap->_link points ++ * at the end of the handle in the stat structure. ++ */ ++ statp->dt_compname.vd_offset = statp->_link; ++ statp->dt_compname.vd_length = namelen + 1; ++ ++ len = statp->_link; ++ ++ /* Word-align the record */ ++ statp->_link = dm_stat_align(len + namelen + 1); ++ ++ error = -EFAULT; ++ if (copy_to_user(cb->ubuf, statp, len)) ++ goto out_err; ++ if (copy_to_user(cb->ubuf + len, name, namelen)) ++ goto out_err; ++ if (put_user(0, cb->ubuf + len + namelen)) ++ goto out_err; ++ ++ cb->lastbuf = (dm_stat_t __user *)cb->ubuf; ++ cb->spaceleft -= statp->_link; ++ cb->nwritten += statp->_link; ++ cb->ubuf += statp->_link; ++ ++ return 0; ++ ++ out_err: ++ cb->error = error; ++ return error; ++} ++ ++/* Returns negative errors to DMAPI */ ++STATIC int ++xfs_dm_get_dirattrs_rvp( ++ struct inode *inode, ++ dm_right_t right, ++ u_int mask, ++ dm_attrloc_t __user *locp, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp, ++ int *rvp) ++{ ++ xfs_inode_t *dp = XFS_I(inode); ++ xfs_mount_t *mp = dp->i_mount; ++ dm_readdir_cb_t *cb; ++ dm_attrloc_t loc; ++ int error; ++ ++ if (right < DM_RIGHT_SHARED) ++ return -EACCES; ++ ++ /* ++ * Make sure that the buffer is properly aligned. ++ */ ++ if (((unsigned long)bufp & (DM_STAT_ALIGN - 1)) != 0) ++ return -EFAULT; ++ ++ if (mask & ~(DM_AT_HANDLE|DM_AT_EMASK|DM_AT_PMANR|DM_AT_PATTR| ++ DM_AT_DTIME|DM_AT_CFLAG|DM_AT_STAT)) ++ return -EINVAL; ++ ++ if (!S_ISDIR(inode->i_mode)) ++ return -EINVAL; ++ ++ /* ++ * bufp should be able to fit at least one dm_stat entry including ++ * dt_handle and full size MAXNAMLEN dt_compname. ++ */ ++ if (buflen < dm_stat_size(MAXNAMLEN)) ++ return -ENOMEM; ++ ++ if (copy_from_user(&loc, locp, sizeof(loc))) ++ return -EFAULT; ++ ++ cb = kzalloc(sizeof(*cb) + dm_stat_size(MAXNAMLEN), GFP_KERNEL); ++ if (!cb) ++ return -ENOMEM; ++ ++ cb->mp = mp; ++ cb->spaceleft = buflen; ++ cb->ubuf = bufp; ++ ++ mutex_lock(&inode->i_mutex); ++ error = -ENOENT; ++ if (!IS_DEADDIR(inode)) { ++ error = -xfs_readdir(dp, cb, dp->i_size, ++ (xfs_off_t *)&loc, dm_filldir); ++ } ++ mutex_unlock(&inode->i_mutex); ++ ++ if (error) ++ goto out_kfree; ++ if (cb->error) { ++ error = cb->error; ++ goto out_kfree; ++ } ++ ++ error = -EFAULT; ++ if (cb->lastbuf && put_user(0, &cb->lastbuf->_link)) ++ goto out_kfree; ++ if (put_user(cb->nwritten, rlenp)) ++ goto out_kfree; ++ if (copy_to_user(locp, &loc, sizeof(loc))) ++ goto out_kfree; ++ ++ if (cb->nwritten) ++ *rvp = 1; ++ else ++ *rvp = 0; ++ error = 0; ++ ++ out_kfree: ++ kfree(cb); ++ return error; ++} ++ ++STATIC int ++xfs_dm_get_dmattr( ++ struct inode *inode, ++ dm_right_t right, ++ dm_attrname_t __user *attrnamep, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ dm_dkattrname_t name; ++ char *value; ++ int value_len; ++ int alloc_size; ++ int error; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ if ((error = xfs_copyin_attrname(attrnamep, &name)) != 0) ++ return(-error); /* Return negative error to DMAPI */ ++ ++ /* Allocate a buffer to receive the attribute's value. We allocate ++ at least one byte even if the caller specified a buflen of zero. ++ (A buflen of zero is considered valid.) ++ ++ Allocating a minimum of XFS_BUG_KLUDGE bytes temporarily works ++ around a bug within XFS in which in-inode attribute values are not ++ checked to see if they will fit in the buffer before they are ++ copied. Since no in-core attribute value can be larger than 256 ++ bytes (an 8-bit size field), we allocate that minimum size here to ++ prevent buffer overrun in both the kernel's and user's buffers. ++ */ ++ ++ alloc_size = buflen; ++ if (alloc_size < XFS_BUG_KLUDGE) ++ alloc_size = XFS_BUG_KLUDGE; ++ if (alloc_size > ATTR_MAX_VALUELEN) ++ alloc_size = ATTR_MAX_VALUELEN; ++ value = kmem_alloc(alloc_size, KM_SLEEP | KM_LARGE); ++ ++ /* Get the attribute's value. */ ++ ++ value_len = alloc_size; /* in/out parameter */ ++ ++ error = xfs_attr_get(XFS_I(inode), name.dan_chars, value, &value_len, ++ ATTR_ROOT); ++ DM_EA_XLATE_ERR(error); ++ ++ /* DMAPI requires an errno of ENOENT if an attribute does not exist, ++ so remap ENOATTR here. ++ */ ++ ++ if (error == ENOATTR) ++ error = ENOENT; ++ if (!error && value_len > buflen) ++ error = E2BIG; ++ if (!error && copy_to_user(bufp, value, value_len)) ++ error = EFAULT; ++ if (!error || error == E2BIG) { ++ if (put_user(value_len, rlenp)) ++ error = EFAULT; ++ } ++ ++ kmem_free(value); ++ return(-error); /* Return negative error to DMAPI */ ++} ++ ++STATIC int ++xfs_dm_get_eventlist( ++ struct inode *inode, ++ dm_right_t right, ++ u_int type, ++ u_int nelem, ++ dm_eventset_t *eventsetp, ++ u_int *nelemp) ++{ ++ int error; ++ xfs_inode_t *ip = XFS_I(inode); ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (type == DM_FSYS_OBJ) { ++ error = xfs_dm_fs_get_eventlist(ip->i_mount, right, nelem, ++ eventsetp, nelemp); ++ } else { ++ error = xfs_dm_f_get_eventlist(ip, right, nelem, ++ eventsetp, nelemp); ++ } ++ return(-error); /* Returns negative error to DMAPI */ ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_get_fileattr( ++ struct inode *inode, ++ dm_right_t right, ++ u_int mask, /* not used; always return everything */ ++ dm_stat_t __user *statp) ++{ ++ dm_stat_t stat; ++ xfs_inode_t *ip = XFS_I(inode); ++ xfs_mount_t *mp; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ /* Find the mount point. */ ++ ++ mp = ip->i_mount; ++ ++ xfs_ilock(ip, XFS_ILOCK_SHARED); ++ xfs_ip_to_stat(mp, ip->i_ino, ip, &stat); ++ xfs_iunlock(ip, XFS_ILOCK_SHARED); ++ ++ if (copy_to_user( statp, &stat, sizeof(stat))) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++/* We currently only support a maximum of one managed region per file, and ++ use the DM_EVENT_READ, DM_EVENT_WRITE, and DM_EVENT_TRUNCATE events in ++ the file's dm_eventset_t event mask to implement the DM_REGION_READ, ++ DM_REGION_WRITE, and DM_REGION_TRUNCATE flags for that single region. ++*/ ++ ++STATIC int ++xfs_dm_get_region( ++ struct inode *inode, ++ dm_right_t right, ++ u_int nelem, ++ dm_region_t __user *regbufp, ++ u_int __user *nelemp) ++{ ++ dm_eventset_t evmask; ++ dm_region_t region; ++ xfs_inode_t *ip = XFS_I(inode); ++ u_int elem; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ evmask = ip->i_d.di_dmevmask; /* read the mask "atomically" */ ++ ++ /* Get the file's current managed region flags out of the ++ dm_eventset_t mask and use them to build a managed region that ++ covers the entire file, i.e. set rg_offset and rg_size to zero. ++ */ ++ ++ memset((char *)®ion, 0, sizeof(region)); ++ ++ if (evmask & (1 << DM_EVENT_READ)) ++ region.rg_flags |= DM_REGION_READ; ++ if (evmask & (1 << DM_EVENT_WRITE)) ++ region.rg_flags |= DM_REGION_WRITE; ++ if (evmask & (1 << DM_EVENT_TRUNCATE)) ++ region.rg_flags |= DM_REGION_TRUNCATE; ++ ++ elem = (region.rg_flags ? 1 : 0); ++ ++ if (copy_to_user( nelemp, &elem, sizeof(elem))) ++ return(-EFAULT); ++ if (elem > nelem) ++ return(-E2BIG); ++ if (elem && copy_to_user(regbufp, ®ion, sizeof(region))) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++STATIC int ++xfs_dm_getall_dmattr( ++ struct inode *inode, ++ dm_right_t right, ++ size_t buflen, ++ void __user *bufp, ++ size_t __user *rlenp) ++{ ++ attrlist_cursor_kern_t cursor; ++ attrlist_t *attrlist; ++ dm_attrlist_t __user *ulist; ++ int *last_link; ++ int alignment; ++ int total_size; ++ int list_size = 8192; /* should be big enough */ ++ int error; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ /* Verify that the user gave us a buffer that is 4-byte aligned, lock ++ it down, and work directly within that buffer. As a side-effect, ++ values of buflen < sizeof(int) return EINVAL. ++ */ ++ ++ alignment = sizeof(int) - 1; ++ if ((((__psint_t)bufp & alignment) != 0) || ++ !access_ok(VERIFY_WRITE, bufp, buflen)) { ++ return(-EFAULT); ++ } ++ buflen &= ~alignment; /* round down the alignment */ ++ ++ /* Initialize all the structures and variables for the main loop. */ ++ ++ memset(&cursor, 0, sizeof(cursor)); ++ attrlist = (attrlist_t *)kmem_alloc(list_size, KM_SLEEP); ++ total_size = 0; ++ ulist = (dm_attrlist_t *)bufp; ++ last_link = NULL; ++ ++ /* Use vop_attr_list to get the names of DMAPI attributes, and use ++ vop_attr_get to get their values. There is a risk here that the ++ DMAPI attributes could change between the vop_attr_list and ++ vop_attr_get calls. If we can detect it, we return EIO to notify ++ the user. ++ */ ++ ++ do { ++ int i; ++ ++ /* Get a buffer full of attribute names. If there aren't any ++ more or if we encounter an error, then finish up. ++ */ ++ ++ error = xfs_attr_list(XFS_I(inode), (char *)attrlist, list_size, ++ ATTR_ROOT, &cursor); ++ DM_EA_XLATE_ERR(error); ++ ++ if (error || attrlist->al_count == 0) ++ break; ++ ++ for (i = 0; i < attrlist->al_count; i++) { ++ attrlist_ent_t *entry; ++ char *user_name; ++ int size_needed; ++ int value_len; ++ ++ /* Skip over all non-DMAPI attributes. If the ++ attribute name is too long, we assume it is ++ non-DMAPI even if it starts with the correct ++ prefix. ++ */ ++ ++ entry = ATTR_ENTRY(attrlist, i); ++ if (strncmp(entry->a_name, dmattr_prefix, DMATTR_PREFIXLEN)) ++ continue; ++ user_name = &entry->a_name[DMATTR_PREFIXLEN]; ++ if (strlen(user_name) > DM_ATTR_NAME_SIZE) ++ continue; ++ ++ /* We have a valid DMAPI attribute to return. If it ++ won't fit in the user's buffer, we still need to ++ keep track of the number of bytes for the user's ++ next call. ++ */ ++ ++ ++ size_needed = sizeof(*ulist) + entry->a_valuelen; ++ size_needed = (size_needed + alignment) & ~alignment; ++ ++ total_size += size_needed; ++ if (total_size > buflen) ++ continue; ++ ++ /* Start by filling in all the fields in the ++ dm_attrlist_t structure. ++ */ ++ ++ strncpy((char *)ulist->al_name.an_chars, user_name, ++ DM_ATTR_NAME_SIZE); ++ ulist->al_data.vd_offset = sizeof(*ulist); ++ ulist->al_data.vd_length = entry->a_valuelen; ++ ulist->_link = size_needed; ++ last_link = &ulist->_link; ++ ++ /* Next read the attribute's value into its correct ++ location after the dm_attrlist structure. Any sort ++ of error indicates that the data is moving under us, ++ so we return EIO to let the user know. ++ */ ++ ++ value_len = entry->a_valuelen; ++ ++ error = xfs_attr_get(XFS_I(inode), entry->a_name, ++ (void *)(ulist + 1), &value_len, ++ ATTR_ROOT); ++ DM_EA_XLATE_ERR(error); ++ ++ if (error || value_len != entry->a_valuelen) { ++ error = EIO; ++ break; ++ } ++ ++ ulist = (dm_attrlist_t *)((char *)ulist + ulist->_link); ++ } ++ } while (!error && attrlist->al_more); ++ if (last_link) ++ *last_link = 0; ++ ++ if (!error && total_size > buflen) ++ error = E2BIG; ++ if (!error || error == E2BIG) { ++ if (put_user(total_size, rlenp)) ++ error = EFAULT; ++ } ++ ++ kmem_free(attrlist); ++ return(-error); /* Return negative error to DMAPI */ ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_getall_inherit( ++ struct inode *inode, ++ dm_right_t right, ++ u_int nelem, ++ dm_inherit_t __user *inheritbufp, ++ u_int __user *nelemp) ++{ ++ return(-ENOSYS); /* Return negative error to DMAPI */ ++} ++ ++ ++/* Initialize location pointer for subsequent dm_get_dirattrs, ++ dm_get_bulkattr, and dm_get_bulkall calls. The same initialization must ++ work for inode-based routines (dm_get_dirattrs) and filesystem-based ++ routines (dm_get_bulkattr and dm_get_bulkall). Filesystem-based functions ++ call this routine using the filesystem's root inode. ++*/ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_init_attrloc( ++ struct inode *inode, ++ dm_right_t right, ++ dm_attrloc_t __user *locp) ++{ ++ dm_attrloc_t loc = 0; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ if (copy_to_user( locp, &loc, sizeof(loc))) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_mkdir_by_handle( ++ struct inode *inode, ++ dm_right_t right, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname) ++{ ++ return(-ENOSYS); /* Return negative error to DMAPI */ ++} ++ ++ ++/* ++ * Probe and Punch ++ * ++ * Hole punching alignment is based on the underlying device base ++ * allocation size. Because it is not defined in the DMAPI spec, we ++ * can align how we choose here. Round inwards (offset up and length ++ * down) to the block, extent or page size whichever is bigger. Our ++ * DMAPI implementation rounds the hole geometry strictly inwards. If ++ * this is not possible, return EINVAL for both for xfs_dm_probe_hole ++ * and xfs_dm_punch_hole which differs from the DMAPI spec. Note that ++ * length = 0 is special - it means "punch to EOF" and at that point ++ * we treat the punch as remove everything past offset (including ++ * preallocation past EOF). ++ */ ++ ++STATIC int ++xfs_dm_round_hole( ++ dm_off_t offset, ++ dm_size_t length, ++ dm_size_t align, ++ xfs_fsize_t filesize, ++ dm_off_t *roff, ++ dm_size_t *rlen) ++{ ++ ++ dm_off_t off = offset; ++ dm_size_t len = length; ++ ++ /* Try to round offset up to the nearest boundary */ ++ *roff = roundup_64(off, align); ++ if ((*roff >= filesize) || (len && (len < align))) ++ return -EINVAL; ++ ++ if ((len == 0) || ((off + len) == filesize)) { ++ /* punch to EOF */ ++ *rlen = 0; ++ } else { ++ /* Round length down to the nearest boundary. */ ++ ASSERT(len >= align); ++ ASSERT(align > (*roff - off)); ++ len -= *roff - off; ++ *rlen = len - do_mod(len, align); ++ if (*rlen == 0) ++ return -EINVAL; /* requested length is too small */ ++ } ++#ifdef CONFIG_DMAPI_DEBUG ++ printk("xfs_dm_round_hole: off %lu, len %ld, align %lu, " ++ "filesize %llu, roff %ld, rlen %ld\n", ++ offset, length, align, filesize, *roff, *rlen); ++#endif ++ return 0; /* hole geometry successfully rounded */ ++} ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_probe_hole( ++ struct inode *inode, ++ dm_right_t right, ++ dm_off_t off, ++ dm_size_t len, ++ dm_off_t __user *roffp, ++ dm_size_t __user *rlenp) ++{ ++ dm_off_t roff; ++ dm_size_t rlen; ++ xfs_inode_t *ip = XFS_I(inode); ++ xfs_mount_t *mp; ++ uint lock_flags; ++ xfs_fsize_t realsize; ++ dm_size_t align; ++ int error; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return -EACCES; ++ ++ if ((ip->i_d.di_mode & S_IFMT) != S_IFREG) ++ return -EINVAL; ++ ++ mp = ip->i_mount; ++ lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL; ++ xfs_ilock(ip, lock_flags); ++ realsize = ip->i_size; ++ xfs_iunlock(ip, lock_flags); ++ ++ if ((off + len) > realsize) ++ return -E2BIG; ++ ++ align = 1 << mp->m_sb.sb_blocklog; ++ ++ error = xfs_dm_round_hole(off, len, align, realsize, &roff, &rlen); ++ if (error) ++ return error; ++ ++ if (copy_to_user( roffp, &roff, sizeof(roff))) ++ return -EFAULT; ++ if (copy_to_user( rlenp, &rlen, sizeof(rlen))) ++ return -EFAULT; ++ return(0); ++} ++ ++ ++STATIC int ++xfs_dm_punch_hole( ++ struct inode *inode, ++ dm_right_t right, ++ dm_off_t off, ++ dm_size_t len) ++{ ++ xfs_flock64_t bf; ++ int error = 0; ++ xfs_inode_t *ip = XFS_I(inode); ++ xfs_mount_t *mp; ++ dm_size_t align; ++ xfs_fsize_t realsize; ++ dm_off_t roff; ++ dm_size_t rlen; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_EXCL) ++ return -EACCES; ++ ++ /* Make sure there are no leases. */ ++ error = break_lease(inode, FMODE_WRITE); ++ if (error) ++ return -EBUSY; ++ ++ error = get_write_access(inode); ++ if (error) ++ return -EBUSY; ++ ++ mp = ip->i_mount; ++ ++ down_rw_sems(inode, DM_SEM_FLAG_WR); ++ ++ xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); ++ realsize = ip->i_size; ++ xfs_iunlock(ip, XFS_ILOCK_EXCL); ++ align = xfs_get_extsz_hint(ip); ++ if (align == 0) ++ align = 1; ++ ++ align <<= mp->m_sb.sb_blocklog; ++ ++ if ((off + len) > realsize) { ++ xfs_iunlock(ip, XFS_IOLOCK_EXCL); ++ error = -E2BIG; ++ goto up_and_out; ++ } ++ ++ if ((off + len) == realsize) ++ len = 0; ++ ++ error = xfs_dm_round_hole(off, len, align, realsize, &roff, &rlen); ++ if (error || (off != roff) || (len != rlen)) { ++ xfs_iunlock(ip, XFS_IOLOCK_EXCL); ++ error = -EINVAL; ++ goto up_and_out; ++ } ++ ++ bf.l_type = 0; ++ bf.l_whence = 0; ++ bf.l_start = (xfs_off_t)off; ++ if (len) { ++ bf.l_len = len; ++ } ++ else { ++ /* ++ * When we are punching to EOF, we have to make sure we punch ++ * the last partial block that contains EOF. Round up ++ * the length to make sure we punch the block and not just ++ * zero it. ++ */ ++ bf.l_len = roundup_64((realsize - off), mp->m_sb.sb_blocksize); ++ } ++ ++#ifdef CONFIG_DMAPI_DEBUG ++ printk("xfs_dm_punch_hole: off %lu, len %ld, align %lu\n", ++ off, len, align); ++#endif ++ ++ error = xfs_change_file_space(ip, XFS_IOC_UNRESVSP, &bf, ++ (xfs_off_t)off, XFS_ATTR_DMI|XFS_ATTR_NOLOCK); ++ ++ /* ++ * if punching to end of file, kill any blocks past EOF that ++ * may have been (speculatively) preallocated. No point in ++ * leaving them around if we are migrating the file.... ++ */ ++ if (!error && (len == 0)) { ++ error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_NOLOCK); ++ } ++ ++ /* ++ * negate the error for return here as core XFS functions return ++ * positive error numbers ++ */ ++ if (error) ++ error = -error; ++ ++ /* Let threads in send_data_event know we punched the file. */ ++ ip->i_d.di_dmstate++; ++ xfs_iunlock(ip, XFS_IOLOCK_EXCL); ++ ++up_and_out: ++ up_rw_sems(inode, DM_SEM_FLAG_WR); ++ put_write_access(inode); ++ ++ return error; ++} ++ ++ ++STATIC int ++xfs_dm_read_invis_rvp( ++ struct inode *inode, ++ dm_right_t right, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp) ++{ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_SHARED) ++ return(-EACCES); ++ ++ return(-xfs_dm_rdwr(inode, 0, FMODE_READ, off, len, bufp, rvp)); ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_release_right( ++ struct inode *inode, ++ dm_right_t right, ++ u_int type) /* DM_FSYS_OBJ or zero */ ++{ ++#ifdef DEBUG_RIGHTS ++ char buffer[sizeof(dm_handle_t) * 2 + 1]; ++ ++ if (!xfs_vp_to_hexhandle(inode, type, buffer)) { ++ printf("dm_release_right: old %d type %d handle %s\n", ++ right, type, buffer); ++ } else { ++ printf("dm_release_right: old %d type %d handle " ++ " \n", right, type); ++ } ++#endif /* DEBUG_RIGHTS */ ++ return(0); ++} ++ ++ ++STATIC int ++xfs_dm_remove_dmattr( ++ struct inode *inode, ++ dm_right_t right, ++ int setdtime, ++ dm_attrname_t __user *attrnamep) ++{ ++ dm_dkattrname_t name; ++ int error; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_EXCL) ++ return(-EACCES); ++ ++ if ((error = xfs_copyin_attrname(attrnamep, &name)) != 0) ++ return(-error); /* Return negative error to DMAPI */ ++ ++ /* Remove the attribute from the object. */ ++ ++ error = xfs_attr_remove(XFS_I(inode), name.dan_chars, setdtime ? ++ ATTR_ROOT : (ATTR_ROOT|ATTR_KERNOTIME)); ++ DM_EA_XLATE_ERR(error); ++ ++ if (error == ENOATTR) ++ error = ENOENT; ++ return(-error); /* Return negative error to DMAPI */ ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_request_right( ++ struct inode *inode, ++ dm_right_t right, ++ u_int type, /* DM_FSYS_OBJ or zero */ ++ u_int flags, ++ dm_right_t newright) ++{ ++#ifdef DEBUG_RIGHTS ++ char buffer[sizeof(dm_handle_t) * 2 + 1]; ++ ++ if (!xfs_vp_to_hexhandle(inode, type, buffer)) { ++ printf("dm_request_right: old %d new %d type %d flags 0x%x " ++ "handle %s\n", right, newright, type, flags, buffer); ++ } else { ++ printf("dm_request_right: old %d new %d type %d flags 0x%x " ++ "handle \n", right, newright, type, flags); ++ } ++#endif /* DEBUG_RIGHTS */ ++ return(0); ++} ++ ++ ++STATIC int ++xfs_dm_set_dmattr( ++ struct inode *inode, ++ dm_right_t right, ++ dm_attrname_t __user *attrnamep, ++ int setdtime, ++ size_t buflen, ++ void __user *bufp) ++{ ++ dm_dkattrname_t name; ++ char *value; ++ int alloc_size; ++ int error; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_EXCL) ++ return(-EACCES); ++ ++ if ((error = xfs_copyin_attrname(attrnamep, &name)) != 0) ++ return(-error); /* Return negative error to DMAPI */ ++ if (buflen > ATTR_MAX_VALUELEN) ++ return(-E2BIG); ++ ++ /* Copy in the attribute's value and store the pair in ++ the object. We allocate a buffer of at least one byte even if the ++ caller specified a buflen of zero. (A buflen of zero is considered ++ valid.) ++ */ ++ ++ alloc_size = (buflen == 0) ? 1 : buflen; ++ value = kmem_alloc(alloc_size, KM_SLEEP); ++ if (copy_from_user( value, bufp, buflen)) { ++ error = EFAULT; ++ } else { ++ error = xfs_attr_set(XFS_I(inode), name.dan_chars, value, buflen, ++ setdtime ? ATTR_ROOT : ++ (ATTR_ROOT|ATTR_KERNOTIME)); ++ DM_EA_XLATE_ERR(error); ++ } ++ kmem_free(value); ++ return(-error); /* Return negative error to DMAPI */ ++} ++ ++STATIC int ++xfs_dm_set_eventlist( ++ struct inode *inode, ++ dm_right_t right, ++ u_int type, ++ dm_eventset_t *eventsetp, /* in kernel space! */ ++ u_int maxevent) ++{ ++ int error; ++ xfs_inode_t *ip = XFS_I(inode); ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (type == DM_FSYS_OBJ) { ++ error = xfs_dm_fs_set_eventlist(ip->i_mount, right, eventsetp, maxevent); ++ } else { ++ error = xfs_dm_f_set_eventlist(ip, right, eventsetp, maxevent); ++ } ++ return(-error); /* Return negative error to DMAPI */ ++} ++ ++ ++/* ++ * This turned out not XFS-specific, but leave it here with get_fileattr. ++ */ ++ ++STATIC int ++xfs_dm_set_fileattr( ++ struct inode *inode, ++ dm_right_t right, ++ u_int mask, ++ dm_fileattr_t __user *statp) ++{ ++ dm_fileattr_t stat; ++ struct iattr iattr; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_EXCL) ++ return(-EACCES); ++ ++ if (copy_from_user( &stat, statp, sizeof(stat))) ++ return(-EFAULT); ++ ++ iattr.ia_valid = 0; ++ ++ if (mask & DM_AT_MODE) { ++ iattr.ia_valid |= ATTR_MODE; ++ iattr.ia_mode = stat.fa_mode; ++ } ++ if (mask & DM_AT_UID) { ++ iattr.ia_valid |= ATTR_UID; ++ iattr.ia_uid = stat.fa_uid; ++ } ++ if (mask & DM_AT_GID) { ++ iattr.ia_valid |= ATTR_GID; ++ iattr.ia_gid = stat.fa_gid; ++ } ++ if (mask & DM_AT_ATIME) { ++ iattr.ia_valid |= ATTR_ATIME; ++ iattr.ia_atime.tv_sec = stat.fa_atime; ++ iattr.ia_atime.tv_nsec = 0; ++ inode->i_atime.tv_sec = stat.fa_atime; ++ } ++ if (mask & DM_AT_MTIME) { ++ iattr.ia_valid |= ATTR_MTIME; ++ iattr.ia_mtime.tv_sec = stat.fa_mtime; ++ iattr.ia_mtime.tv_nsec = 0; ++ } ++ if (mask & DM_AT_CTIME) { ++ iattr.ia_valid |= ATTR_CTIME; ++ iattr.ia_ctime.tv_sec = stat.fa_ctime; ++ iattr.ia_ctime.tv_nsec = 0; ++ } ++ ++ /* ++ * DM_AT_DTIME only takes effect if DM_AT_CTIME is not specified. We ++ * overload ctime to also act as dtime, i.e. DM_CONFIG_DTIME_OVERLOAD. ++ */ ++ if ((mask & DM_AT_DTIME) && !(mask & DM_AT_CTIME)) { ++ iattr.ia_valid |= ATTR_CTIME; ++ iattr.ia_ctime.tv_sec = stat.fa_dtime; ++ iattr.ia_ctime.tv_nsec = 0; ++ } ++ if (mask & DM_AT_SIZE) { ++ iattr.ia_valid |= ATTR_SIZE; ++ iattr.ia_size = stat.fa_size; ++ } ++ ++ return -xfs_setattr(XFS_I(inode), &iattr, XFS_ATTR_DMI); ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_set_inherit( ++ struct inode *inode, ++ dm_right_t right, ++ dm_attrname_t __user *attrnamep, ++ mode_t mode) ++{ ++ return(-ENOSYS); /* Return negative error to DMAPI */ ++} ++ ++ ++STATIC int ++xfs_dm_set_region( ++ struct inode *inode, ++ dm_right_t right, ++ u_int nelem, ++ dm_region_t __user *regbufp, ++ dm_boolean_t __user *exactflagp) ++{ ++ xfs_inode_t *ip = XFS_I(inode); ++ xfs_trans_t *tp; ++ xfs_mount_t *mp; ++ dm_region_t region; ++ dm_eventset_t new_mask; ++ dm_eventset_t mr_mask; ++ int error; ++ u_int exactflag; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_EXCL) ++ return(-EACCES); ++ ++ /* If the caller gave us more than one dm_region_t structure, complain. ++ (He has to call dm_get_config() to find out what our limit is.) ++ */ ++ ++ if (nelem > 1) ++ return(-E2BIG); ++ ++ /* If the user provided a dm_region_t structure, then copy it in, ++ validate it, and convert its flags to the corresponding bits in a ++ dm_set_eventlist() event mask. A call with zero regions is ++ equivalent to clearing all region flags. ++ */ ++ ++ new_mask = 0; ++ if (nelem == 1) { ++ if (copy_from_user( ®ion, regbufp, sizeof(region))) ++ return(-EFAULT); ++ ++ if (region.rg_flags & ~(DM_REGION_READ|DM_REGION_WRITE|DM_REGION_TRUNCATE)) ++ return(-EINVAL); ++ if (region.rg_flags & DM_REGION_READ) ++ new_mask |= 1 << DM_EVENT_READ; ++ if (region.rg_flags & DM_REGION_WRITE) ++ new_mask |= 1 << DM_EVENT_WRITE; ++ if (region.rg_flags & DM_REGION_TRUNCATE) ++ new_mask |= 1 << DM_EVENT_TRUNCATE; ++ } ++ mr_mask = (1 << DM_EVENT_READ) | (1 << DM_EVENT_WRITE) | (1 << DM_EVENT_TRUNCATE); ++ ++ /* Get the file's existing event mask, clear the old managed region ++ bits, add in the new ones, and update the file's mask. ++ */ ++ ++ if (new_mask & prohibited_mr_events(inode->i_mapping)) { ++ /* If the change is simply to remove the READ ++ * bit, then that's always okay. Otherwise, it's busy. ++ */ ++ dm_eventset_t m1; ++ m1 = ip->i_d.di_dmevmask & ((1 << DM_EVENT_WRITE) | (1 << DM_EVENT_TRUNCATE)); ++ if (m1 != new_mask) { ++ return -EBUSY; ++ } ++ } ++ ++ mp = ip->i_mount; ++ tp = xfs_trans_alloc(mp, XFS_TRANS_SET_DMATTRS); ++ error = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES (mp), 0, 0, 0); ++ if (error) { ++ xfs_trans_cancel(tp, 0); ++ return(-error); /* Return negative error to DMAPI */ ++ } ++ xfs_ilock(ip, XFS_ILOCK_EXCL); ++ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); ++ ++ ip->i_d.di_dmevmask = (ip->i_d.di_dmevmask & ~mr_mask) | new_mask; ++ ++ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); ++ igrab(inode); ++ xfs_trans_commit(tp, 0); ++ ++ /* Return the proper value for *exactflagp depending upon whether or not ++ we "changed" the user's managed region. In other words, if the user ++ specified a non-zero value for either rg_offset or rg_size, we ++ round each of those values back to zero. ++ */ ++ ++ if (nelem && (region.rg_offset || region.rg_size)) { ++ exactflag = DM_FALSE; /* user region was changed */ ++ } else { ++ exactflag = DM_TRUE; /* user region was unchanged */ ++ } ++ if (copy_to_user( exactflagp, &exactflag, sizeof(exactflag))) ++ return(-EFAULT); ++ return(0); ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_symlink_by_handle( ++ struct inode *inode, ++ dm_right_t right, ++ void __user *hanp, ++ size_t hlen, ++ char __user *cname, ++ char __user *path) ++{ ++ return(-ENOSYS); /* Return negative errors to DMAPI */ ++} ++ ++ ++/* ++ * xfs_dm_sync_by_handle needs to do the same thing as sys_fsync() ++ */ ++STATIC int ++xfs_dm_sync_by_handle( ++ struct inode *inode, ++ dm_right_t right) ++{ ++ int err, ret; ++ xfs_inode_t *ip = XFS_I(inode); ++ ++ /* Returns negative errors to DMAPI */ ++ if (right < DM_RIGHT_EXCL) ++ return(-EACCES); ++ ++ /* We need to protect against concurrent writers.. */ ++ ret = filemap_fdatawrite(inode->i_mapping); ++ down_rw_sems(inode, DM_FLAGS_IMUX); ++ err = -xfs_fsync(ip); ++ if (!ret) ++ ret = err; ++ up_rw_sems(inode, DM_FLAGS_IMUX); ++ err = filemap_fdatawait(inode->i_mapping); ++ if (!ret) ++ ret = err; ++ xfs_iflags_clear(ip, XFS_ITRUNCATED); ++ return ret; ++} ++ ++ ++/* ARGSUSED */ ++STATIC int ++xfs_dm_upgrade_right( ++ struct inode *inode, ++ dm_right_t right, ++ u_int type) /* DM_FSYS_OBJ or zero */ ++{ ++#ifdef DEBUG_RIGHTS ++ char buffer[sizeof(dm_handle_t) * 2 + 1]; ++ ++ if (!xfs_vp_to_hexhandle(inode, type, buffer)) { ++ printf("dm_upgrade_right: old %d new %d type %d handle %s\n", ++ right, DM_RIGHT_EXCL, type, buffer); ++ } else { ++ printf("dm_upgrade_right: old %d new %d type %d handle " ++ "\n", right, DM_RIGHT_EXCL, type); ++ } ++#endif /* DEBUG_RIGHTS */ ++ return(0); ++} ++ ++ ++STATIC int ++xfs_dm_write_invis_rvp( ++ struct inode *inode, ++ dm_right_t right, ++ int flags, ++ dm_off_t off, ++ dm_size_t len, ++ void __user *bufp, ++ int *rvp) ++{ ++ int fflag = 0; ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (right < DM_RIGHT_EXCL) ++ return(-EACCES); ++ ++ if (flags & DM_WRITE_SYNC) ++ fflag |= O_SYNC; ++ return(-xfs_dm_rdwr(inode, fflag, FMODE_WRITE, off, len, bufp, rvp)); ++} ++ ++ ++STATIC void ++xfs_dm_obj_ref_hold( ++ struct inode *inode) ++{ ++ igrab(inode); ++} ++ ++ ++static fsys_function_vector_t xfs_fsys_vector[DM_FSYS_MAX]; ++ ++ ++STATIC int ++xfs_dm_get_dmapiops( ++ struct super_block *sb, ++ void *addr) ++{ ++ static int initialized = 0; ++ dm_fcntl_vector_t *vecrq; ++ fsys_function_vector_t *vecp; ++ int i = 0; ++ ++ vecrq = (dm_fcntl_vector_t *)addr; ++ vecrq->count = ++ sizeof(xfs_fsys_vector) / sizeof(xfs_fsys_vector[0]); ++ vecrq->vecp = xfs_fsys_vector; ++ if (initialized) ++ return(0); ++ vecrq->code_level = DM_CLVL_XOPEN; ++ vecp = xfs_fsys_vector; ++ ++ vecp[i].func_no = DM_FSYS_CLEAR_INHERIT; ++ vecp[i++].u_fc.clear_inherit = xfs_dm_clear_inherit; ++ vecp[i].func_no = DM_FSYS_CREATE_BY_HANDLE; ++ vecp[i++].u_fc.create_by_handle = xfs_dm_create_by_handle; ++ vecp[i].func_no = DM_FSYS_DOWNGRADE_RIGHT; ++ vecp[i++].u_fc.downgrade_right = xfs_dm_downgrade_right; ++ vecp[i].func_no = DM_FSYS_GET_ALLOCINFO_RVP; ++ vecp[i++].u_fc.get_allocinfo_rvp = xfs_dm_get_allocinfo_rvp; ++ vecp[i].func_no = DM_FSYS_GET_BULKALL_RVP; ++ vecp[i++].u_fc.get_bulkall_rvp = xfs_dm_get_bulkall_rvp; ++ vecp[i].func_no = DM_FSYS_GET_BULKATTR_RVP; ++ vecp[i++].u_fc.get_bulkattr_rvp = xfs_dm_get_bulkattr_rvp; ++ vecp[i].func_no = DM_FSYS_GET_CONFIG; ++ vecp[i++].u_fc.get_config = xfs_dm_get_config; ++ vecp[i].func_no = DM_FSYS_GET_CONFIG_EVENTS; ++ vecp[i++].u_fc.get_config_events = xfs_dm_get_config_events; ++ vecp[i].func_no = DM_FSYS_GET_DESTROY_DMATTR; ++ vecp[i++].u_fc.get_destroy_dmattr = xfs_dm_get_destroy_dmattr; ++ vecp[i].func_no = DM_FSYS_GET_DIOINFO; ++ vecp[i++].u_fc.get_dioinfo = xfs_dm_get_dioinfo; ++ vecp[i].func_no = DM_FSYS_GET_DIRATTRS_RVP; ++ vecp[i++].u_fc.get_dirattrs_rvp = xfs_dm_get_dirattrs_rvp; ++ vecp[i].func_no = DM_FSYS_GET_DMATTR; ++ vecp[i++].u_fc.get_dmattr = xfs_dm_get_dmattr; ++ vecp[i].func_no = DM_FSYS_GET_EVENTLIST; ++ vecp[i++].u_fc.get_eventlist = xfs_dm_get_eventlist; ++ vecp[i].func_no = DM_FSYS_GET_FILEATTR; ++ vecp[i++].u_fc.get_fileattr = xfs_dm_get_fileattr; ++ vecp[i].func_no = DM_FSYS_GET_REGION; ++ vecp[i++].u_fc.get_region = xfs_dm_get_region; ++ vecp[i].func_no = DM_FSYS_GETALL_DMATTR; ++ vecp[i++].u_fc.getall_dmattr = xfs_dm_getall_dmattr; ++ vecp[i].func_no = DM_FSYS_GETALL_INHERIT; ++ vecp[i++].u_fc.getall_inherit = xfs_dm_getall_inherit; ++ vecp[i].func_no = DM_FSYS_INIT_ATTRLOC; ++ vecp[i++].u_fc.init_attrloc = xfs_dm_init_attrloc; ++ vecp[i].func_no = DM_FSYS_MKDIR_BY_HANDLE; ++ vecp[i++].u_fc.mkdir_by_handle = xfs_dm_mkdir_by_handle; ++ vecp[i].func_no = DM_FSYS_PROBE_HOLE; ++ vecp[i++].u_fc.probe_hole = xfs_dm_probe_hole; ++ vecp[i].func_no = DM_FSYS_PUNCH_HOLE; ++ vecp[i++].u_fc.punch_hole = xfs_dm_punch_hole; ++ vecp[i].func_no = DM_FSYS_READ_INVIS_RVP; ++ vecp[i++].u_fc.read_invis_rvp = xfs_dm_read_invis_rvp; ++ vecp[i].func_no = DM_FSYS_RELEASE_RIGHT; ++ vecp[i++].u_fc.release_right = xfs_dm_release_right; ++ vecp[i].func_no = DM_FSYS_REMOVE_DMATTR; ++ vecp[i++].u_fc.remove_dmattr = xfs_dm_remove_dmattr; ++ vecp[i].func_no = DM_FSYS_REQUEST_RIGHT; ++ vecp[i++].u_fc.request_right = xfs_dm_request_right; ++ vecp[i].func_no = DM_FSYS_SET_DMATTR; ++ vecp[i++].u_fc.set_dmattr = xfs_dm_set_dmattr; ++ vecp[i].func_no = DM_FSYS_SET_EVENTLIST; ++ vecp[i++].u_fc.set_eventlist = xfs_dm_set_eventlist; ++ vecp[i].func_no = DM_FSYS_SET_FILEATTR; ++ vecp[i++].u_fc.set_fileattr = xfs_dm_set_fileattr; ++ vecp[i].func_no = DM_FSYS_SET_INHERIT; ++ vecp[i++].u_fc.set_inherit = xfs_dm_set_inherit; ++ vecp[i].func_no = DM_FSYS_SET_REGION; ++ vecp[i++].u_fc.set_region = xfs_dm_set_region; ++ vecp[i].func_no = DM_FSYS_SYMLINK_BY_HANDLE; ++ vecp[i++].u_fc.symlink_by_handle = xfs_dm_symlink_by_handle; ++ vecp[i].func_no = DM_FSYS_SYNC_BY_HANDLE; ++ vecp[i++].u_fc.sync_by_handle = xfs_dm_sync_by_handle; ++ vecp[i].func_no = DM_FSYS_UPGRADE_RIGHT; ++ vecp[i++].u_fc.upgrade_right = xfs_dm_upgrade_right; ++ vecp[i].func_no = DM_FSYS_WRITE_INVIS_RVP; ++ vecp[i++].u_fc.write_invis_rvp = xfs_dm_write_invis_rvp; ++ vecp[i].func_no = DM_FSYS_OBJ_REF_HOLD; ++ vecp[i++].u_fc.obj_ref_hold = xfs_dm_obj_ref_hold; ++ ++ return(0); ++} ++ ++ ++/* xfs_dm_send_mmap_event - send events needed for memory mapping a file. ++ * ++ * This is a workaround called for files that are about to be ++ * mapped. DMAPI events are not being generated at a low enough level ++ * in the kernel for page reads/writes to generate the correct events. ++ * So for memory-mapped files we generate read or write events for the ++ * whole byte range being mapped. If the mmap call can never cause a ++ * write to the file, then only a read event is sent. ++ * ++ * Code elsewhere prevents adding managed regions to a file while it ++ * is still mapped. ++ */ ++ ++STATIC int ++xfs_dm_send_mmap_event( ++ struct vm_area_struct *vma, ++ unsigned int wantflag) ++{ ++ xfs_inode_t *ip; ++ int error = 0; ++ dm_eventtype_t max_event = DM_EVENT_READ; ++ xfs_fsize_t filesize; ++ xfs_off_t length, end_of_area, evsize, offset; ++ int iolock; ++ ++ if (!vma->vm_file) ++ return 0; ++ ++ ip = XFS_I(vma->vm_file->f_dentry->d_inode); ++ ++ if (!S_ISREG(vma->vm_file->f_dentry->d_inode->i_mode) || ++ !(ip->i_mount->m_flags & XFS_MOUNT_DMAPI)) ++ return 0; ++ ++ /* If they specifically asked for 'read', then give it to them. ++ * Otherwise, see if it's possible to give them 'write'. ++ */ ++ if( wantflag & VM_READ ){ ++ max_event = DM_EVENT_READ; ++ } ++ else if( ! (vma->vm_flags & VM_DENYWRITE) ) { ++ if((wantflag & VM_WRITE) || (vma->vm_flags & VM_WRITE)) ++ max_event = DM_EVENT_WRITE; ++ } ++ ++ if( (wantflag & VM_WRITE) && (max_event != DM_EVENT_WRITE) ){ ++ return -EACCES; ++ } ++ ++ /* Figure out how much of the file is being requested by the user. */ ++ offset = 0; /* beginning of file, for now */ ++ length = 0; /* whole file, for now */ ++ ++ filesize = ip->i_new_size; ++ if (filesize < ip->i_size) { ++ filesize = ip->i_size; ++ } ++ ++ /* Set first byte number beyond the map area. */ ++ ++ if (length) { ++ end_of_area = offset + length; ++ if (end_of_area > filesize) ++ end_of_area = filesize; ++ } else { ++ end_of_area = filesize; ++ } ++ ++ /* Set the real amount being mapped. */ ++ evsize = end_of_area - offset; ++ if (evsize < 0) ++ evsize = 0; ++ ++ if (max_event == DM_EVENT_READ) ++ iolock = XFS_IOLOCK_SHARED; ++ else ++ iolock = XFS_IOLOCK_EXCL; ++ ++ xfs_ilock(ip, iolock); ++ /* If write possible, try a DMAPI write event */ ++ if (max_event == DM_EVENT_WRITE && DM_EVENT_ENABLED(ip, max_event)) { ++ error = xfs_dm_send_data_event(max_event, ip, offset, ++ evsize, 0, &iolock); ++ goto out_unlock; ++ } ++ ++ /* Try a read event if max_event was != DM_EVENT_WRITE or if it ++ * was DM_EVENT_WRITE but the WRITE event was not enabled. ++ */ ++ if (DM_EVENT_ENABLED(ip, DM_EVENT_READ)) { ++ error = xfs_dm_send_data_event(DM_EVENT_READ, ip, offset, ++ evsize, 0, &iolock); ++ } ++out_unlock: ++ xfs_iunlock(ip, iolock); ++ return -error; ++} ++ ++ ++STATIC int ++xfs_dm_send_destroy_event( ++ xfs_inode_t *ip, ++ dm_right_t vp_right) /* always DM_RIGHT_NULL */ ++{ ++ /* Returns positive errors to XFS */ ++ return -dm_send_destroy_event(&ip->i_vnode, vp_right); ++} ++ ++ ++STATIC int ++xfs_dm_send_namesp_event( ++ dm_eventtype_t event, ++ struct xfs_mount *mp, ++ xfs_inode_t *ip1, ++ dm_right_t vp1_right, ++ xfs_inode_t *ip2, ++ dm_right_t vp2_right, ++ const char *name1, ++ const char *name2, ++ mode_t mode, ++ int retcode, ++ int flags) ++{ ++ /* Returns positive errors to XFS */ ++ return -dm_send_namesp_event(event, mp ? mp->m_super : NULL, ++ &ip1->i_vnode, vp1_right, ++ ip2 ? &ip2->i_vnode : NULL, vp2_right, ++ name1, name2, ++ mode, retcode, flags); ++} ++ ++STATIC int ++xfs_dm_send_mount_event( ++ struct xfs_mount *mp, ++ dm_right_t root_right, ++ char *mtpt, ++ char *fsname) ++{ ++ return dm_send_mount_event(mp->m_super, root_right, ++ NULL, DM_RIGHT_NULL, ++ mp->m_rootip ? VFS_I(mp->m_rootip) : NULL, ++ DM_RIGHT_NULL, mtpt, fsname); ++} ++ ++STATIC void ++xfs_dm_send_unmount_event( ++ struct xfs_mount *mp, ++ xfs_inode_t *ip, /* NULL if unmount successful */ ++ dm_right_t vfsp_right, ++ mode_t mode, ++ int retcode, /* errno, if unmount failed */ ++ int flags) ++{ ++ dm_send_unmount_event(mp->m_super, ip ? &ip->i_vnode : NULL, ++ vfsp_right, mode, retcode, flags); ++} ++ ++ ++/* ++ * Data migration operations accessed by the rest of XFS. ++ * When DMAPI support is configured in, this vector is used. ++ */ ++ ++xfs_dmops_t xfs_dmcore_xfs = { ++ .xfs_send_data = xfs_dm_send_data_event, ++ .xfs_send_mmap = xfs_dm_send_mmap_event, ++ .xfs_send_destroy = xfs_dm_send_destroy_event, ++ .xfs_send_namesp = xfs_dm_send_namesp_event, ++ .xfs_send_mount = xfs_dm_send_mount_event, ++ .xfs_send_unmount = xfs_dm_send_unmount_event, ++}; ++EXPORT_SYMBOL(xfs_dmcore_xfs); ++ ++STATIC int ++xfs_dm_fh_to_inode( ++ struct super_block *sb, ++ struct inode **inode, ++ dm_fid_t *dmfid) ++{ ++ xfs_mount_t *mp = XFS_M(sb); ++ xfs_inode_t *ip; ++ xfs_ino_t ino; ++ unsigned int igen; ++ int error; ++ ++ *inode = NULL; ++ ++ if (!dmfid->dm_fid_len) { ++ /* filesystem handle */ ++ *inode = igrab(&mp->m_rootip->i_vnode); ++ if (!*inode) ++ return -ENOENT; ++ return 0; ++ } ++ ++ if (dmfid->dm_fid_len != sizeof(*dmfid) - sizeof(dmfid->dm_fid_len)) ++ return -EINVAL; ++ ++ ino = dmfid->dm_fid_ino; ++ igen = dmfid->dm_fid_gen; ++ ++ /* fail requests for ino 0 gracefully. */ ++ if (ino == 0) ++ return -ESTALE; ++ ++ error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0); ++ if (error) ++ return -error; ++ if (!ip) ++ return -EIO; ++ ++ if (!ip->i_d.di_mode || ip->i_d.di_gen != igen) { ++ xfs_iput_new(ip, XFS_ILOCK_SHARED); ++ return -ENOENT; ++ } ++ ++ *inode = &ip->i_vnode; ++ xfs_iunlock(ip, XFS_ILOCK_SHARED); ++ return 0; ++} ++ ++STATIC int ++xfs_dm_inode_to_fh( ++ struct inode *inode, ++ dm_fid_t *dmfid, ++ dm_fsid_t *dmfsid) ++{ ++ xfs_inode_t *ip = XFS_I(inode); ++ ++ /* Returns negative errors to DMAPI */ ++ ++ if (ip->i_mount->m_fixedfsid == NULL) ++ return -EINVAL; ++ ++ dmfid->dm_fid_len = sizeof(dm_fid_t) - sizeof(dmfid->dm_fid_len); ++ dmfid->dm_fid_pad = 0; ++ /* ++ * use memcpy because the inode is a long long and there's no ++ * assurance that dmfid->dm_fid_ino is properly aligned. ++ */ ++ memcpy(&dmfid->dm_fid_ino, &ip->i_ino, sizeof(dmfid->dm_fid_ino)); ++ dmfid->dm_fid_gen = ip->i_d.di_gen; ++ ++ memcpy(dmfsid, ip->i_mount->m_fixedfsid, sizeof(*dmfsid)); ++ return 0; ++} ++ ++STATIC void ++xfs_dm_get_fsid( ++ struct super_block *sb, ++ dm_fsid_t *fsid) ++{ ++ memcpy(fsid, XFS_M(sb)->m_fixedfsid, sizeof(*fsid)); ++} ++ ++/* ++ * Filesystem operations accessed by the DMAPI core. ++ */ ++static struct filesystem_dmapi_operations xfs_dmapiops = { ++ .get_fsys_vector = xfs_dm_get_dmapiops, ++ .fh_to_inode = xfs_dm_fh_to_inode, ++ .inode_to_fh = xfs_dm_inode_to_fh, ++ .get_fsid = xfs_dm_get_fsid, ++}; ++ ++static int __init ++xfs_dm_init(void) ++{ ++ printk(KERN_INFO "SGI XFS Data Management API subsystem\n"); ++ ++ dmapi_register(&xfs_fs_type, &xfs_dmapiops); ++ return 0; ++} ++ ++static void __exit ++xfs_dm_exit(void) ++{ ++ dmapi_unregister(&xfs_fs_type); ++} ++ ++MODULE_AUTHOR("Silicon Graphics, Inc."); ++MODULE_DESCRIPTION("SGI XFS dmapi subsystem"); ++MODULE_LICENSE("GPL"); ++ ++module_init(xfs_dm_init); ++module_exit(xfs_dm_exit); +--- /dev/null ++++ b/fs/xfs/dmapi/xfs_dm.h +@@ -0,0 +1,23 @@ ++/* ++ * Copyright (c) 2006 Silicon Graphics, Inc. ++ * All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++#ifndef __XFS_DM_H__ ++#define __XFS_DM_H__ ++ ++extern struct file_system_type xfs_fs_type; ++ ++#endif /* __XFS_DM_H__ */ +--- a/fs/xfs/linux-2.6/xfs_file.c ++++ b/fs/xfs/linux-2.6/xfs_file.c +@@ -47,6 +47,9 @@ + #include + + static const struct vm_operations_struct xfs_file_vm_ops; ++#ifdef HAVE_DMAPI ++static struct vm_operations_struct xfs_dmapi_file_vm_ops; ++#endif + + /* + * xfs_iozero +@@ -938,6 +941,23 @@ xfs_file_release( + return -xfs_release(XFS_I(inode)); + } + ++#ifdef HAVE_DMAPI ++STATIC int ++xfs_vm_fault( ++ struct vm_area_struct *vma, ++ struct vm_fault *vmf) ++{ ++ struct inode *inode = vma->vm_file->f_path.dentry->d_inode; ++ struct xfs_mount *mp = XFS_M(inode->i_sb); ++ ++ ASSERT_ALWAYS(mp->m_flags & XFS_MOUNT_DMAPI); ++ ++ if (XFS_SEND_MMAP(mp, vma, 0)) ++ return VM_FAULT_SIGBUS; ++ return filemap_fault(vma, vmf); ++} ++#endif /* HAVE_DMAPI */ ++ + STATIC int + xfs_file_readdir( + struct file *filp, +@@ -978,10 +998,56 @@ xfs_file_mmap( + vma->vm_ops = &xfs_file_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; + ++#ifdef HAVE_DMAPI ++ if (XFS_M(filp->f_path.dentry->d_inode->i_sb)->m_flags & XFS_MOUNT_DMAPI) ++ vma->vm_ops = &xfs_dmapi_file_vm_ops; ++#endif /* HAVE_DMAPI */ ++ + file_accessed(filp); + return 0; + } + ++#ifdef HAVE_DMAPI ++#ifdef HAVE_VMOP_MPROTECT ++STATIC int ++xfs_vm_mprotect( ++ struct vm_area_struct *vma, ++ unsigned int newflags) ++{ ++ struct inode *inode = vma->vm_file->f_path.dentry->d_inode; ++ struct xfs_mount *mp = XFS_M(inode->i_sb); ++ int error = 0; ++ ++ if (mp->m_flags & XFS_MOUNT_DMAPI) { ++ if ((vma->vm_flags & VM_MAYSHARE) && ++ (newflags & VM_WRITE) && !(vma->vm_flags & VM_WRITE)) ++ error = XFS_SEND_MMAP(mp, vma, VM_WRITE); ++ } ++ return error; ++} ++#endif /* HAVE_VMOP_MPROTECT */ ++#endif /* HAVE_DMAPI */ ++ ++#ifdef HAVE_FOP_OPEN_EXEC ++/* If the user is attempting to execute a file that is offline then ++ * we have to trigger a DMAPI READ event before the file is marked as busy ++ * otherwise the invisible I/O will not be able to write to the file to bring ++ * it back online. ++ */ ++STATIC int ++xfs_file_open_exec( ++ struct inode *inode) ++{ ++ struct xfs_mount *mp = XFS_M(inode->i_sb); ++ struct xfs_inode *ip = XFS_I(inode); ++ ++ if (unlikely(mp->m_flags & XFS_MOUNT_DMAPI) && ++ DM_EVENT_ENABLED(ip, DM_EVENT_READ)) ++ return -XFS_SEND_DATA(mp, DM_EVENT_READ, ip, 0, 0, 0, NULL); ++ return 0; ++} ++#endif /* HAVE_FOP_OPEN_EXEC */ ++ + /* + * mmap()d file has taken write protection fault and is being made + * writable. We can set the page state up correctly for a writable +@@ -1033,3 +1099,13 @@ static const struct vm_operations_struct + .fault = filemap_fault, + .page_mkwrite = xfs_vm_page_mkwrite, + }; ++ ++#ifdef HAVE_DMAPI ++static struct vm_operations_struct xfs_dmapi_file_vm_ops = { ++ .fault = xfs_vm_fault, ++ .page_mkwrite = xfs_vm_page_mkwrite, ++#ifdef HAVE_VMOP_MPROTECT ++ .mprotect = xfs_vm_mprotect, ++#endif ++}; ++#endif /* HAVE_DMAPI */ +--- /dev/null ++++ b/fs/xfs/linux-2.6/xfs_ksyms.c +@@ -0,0 +1,92 @@ ++/* ++ * Copyright (c) 2004-2008 Silicon Graphics, Inc. ++ * All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it would be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "xfs.h" ++#include "xfs_fs.h" ++#include "xfs_bit.h" ++#include "xfs_buf.h" ++#include "xfs_log.h" ++#include "xfs_inum.h" ++#include "xfs_trans.h" ++#include "xfs_sb.h" ++#include "xfs_ag.h" ++#include "xfs_dir2.h" ++#include "xfs_alloc.h" ++#include "xfs_dmapi.h" ++#include "xfs_quota.h" ++#include "xfs_mount.h" ++#include "xfs_da_btree.h" ++#include "xfs_bmap_btree.h" ++#include "xfs_alloc_btree.h" ++#include "xfs_ialloc_btree.h" ++#include "xfs_dir2_sf.h" ++#include "xfs_attr_sf.h" ++#include "xfs_dinode.h" ++#include "xfs_inode.h" ++#include "xfs_btree.h" ++#include "xfs_ialloc.h" ++#include "xfs_bmap.h" ++#include "xfs_rtalloc.h" ++#include "xfs_error.h" ++#include "xfs_itable.h" ++#include "xfs_rw.h" ++#include "xfs_dir2_data.h" ++#include "xfs_dir2_leaf.h" ++#include "xfs_dir2_block.h" ++#include "xfs_dir2_node.h" ++#include "xfs_acl.h" ++#include "xfs_attr.h" ++#include "xfs_attr_leaf.h" ++#include "xfs_inode_item.h" ++#include "xfs_buf_item.h" ++#include "xfs_extfree_item.h" ++#include "xfs_log_priv.h" ++#include "xfs_trans_priv.h" ++#include "xfs_trans_space.h" ++#include "xfs_utils.h" ++#include "xfs_iomap.h" ++#include "xfs_filestream.h" ++#include "xfs_vnodeops.h" ++ ++EXPORT_SYMBOL(xfs_iunlock); ++EXPORT_SYMBOL(xfs_attr_remove); ++EXPORT_SYMBOL(xfs_iunlock_map_shared); ++EXPORT_SYMBOL(xfs_iget); ++EXPORT_SYMBOL(xfs_bmapi); ++EXPORT_SYMBOL(xfs_internal_inum); ++EXPORT_SYMBOL(xfs_attr_set); ++EXPORT_SYMBOL(xfs_trans_reserve); ++EXPORT_SYMBOL(xfs_trans_ijoin); ++EXPORT_SYMBOL(xfs_free_eofblocks); ++EXPORT_SYMBOL(kmem_free); ++EXPORT_SYMBOL(_xfs_trans_commit); ++EXPORT_SYMBOL(xfs_ilock); ++EXPORT_SYMBOL(xfs_attr_get); ++EXPORT_SYMBOL(xfs_readdir); ++EXPORT_SYMBOL(xfs_setattr); ++EXPORT_SYMBOL(xfs_trans_alloc); ++EXPORT_SYMBOL(xfs_trans_cancel); ++EXPORT_SYMBOL(xfs_fsync); ++EXPORT_SYMBOL(xfs_iput_new); ++EXPORT_SYMBOL(xfs_bulkstat); ++EXPORT_SYMBOL(xfs_ilock_map_shared); ++EXPORT_SYMBOL(xfs_iput); ++EXPORT_SYMBOL(xfs_trans_log_inode); ++EXPORT_SYMBOL(xfs_attr_list); ++EXPORT_SYMBOL(kmem_alloc); ++EXPORT_SYMBOL(xfs_change_file_space); +--- a/fs/xfs/linux-2.6/xfs_linux.h ++++ b/fs/xfs/linux-2.6/xfs_linux.h +@@ -160,6 +160,10 @@ + #define xfs_itruncate_data(ip, off) \ + (-vmtruncate(VFS_I(ip), (off))) + ++#undef HAVE_DMAPI ++#if defined(CONFIG_XFS_DMAPI) || defined(CONFIG_XFS_DMAPI_MODULE) ++#define HAVE_DMAPI ++#endif + + /* Move the kernel do_div definition off to one side */ + +--- a/fs/xfs/linux-2.6/xfs_super.c ++++ b/fs/xfs/linux-2.6/xfs_super.c +@@ -1670,8 +1670,16 @@ xfs_fs_get_sb( + void *data, + struct vfsmount *mnt) + { +- return get_sb_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super, ++ int error; ++ ++ error = get_sb_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super, + mnt); ++ if (!error) { ++ xfs_mount_t *mp = XFS_M(mnt->mnt_sb); ++ mp->m_vfsmount = mnt; ++ } ++ ++ return error; + } + + static const struct super_operations xfs_super_operations = { +@@ -1689,13 +1697,14 @@ static const struct super_operations xfs + .show_options = xfs_fs_show_options, + }; + +-static struct file_system_type xfs_fs_type = { ++struct file_system_type xfs_fs_type = { + .owner = THIS_MODULE, + .name = "xfs", + .get_sb = xfs_fs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, + }; ++EXPORT_SYMBOL(xfs_fs_type); + + STATIC int __init + xfs_init_zones(void) +--- a/fs/xfs/xfs_dmops.c ++++ b/fs/xfs/xfs_dmops.c +@@ -40,9 +40,21 @@ int + xfs_dmops_get(struct xfs_mount *mp) + { + if (mp->m_flags & XFS_MOUNT_DMAPI) { +- cmn_err(CE_WARN, +- "XFS: dmapi support not available in this kernel."); +- return EINVAL; ++ struct xfs_dmops *ops; ++ ++ ops = symbol_get(xfs_dmcore_xfs); ++ if (!ops) { ++ request_module("xfs_dmapi"); ++ ops = symbol_get(xfs_dmcore_xfs); ++ } ++ ++ if (!ops) { ++ cmn_err(CE_WARN, "XFS: no dmapi support available."); ++ return EINVAL; ++ } ++ mp->m_dm_ops = ops; ++ } else { ++ mp->m_dm_ops = &xfs_dmcore_stub; + } + + mp->m_dm_ops = &xfs_dmcore_stub; +@@ -52,4 +64,6 @@ xfs_dmops_get(struct xfs_mount *mp) + void + xfs_dmops_put(struct xfs_mount *mp) + { ++ if (mp->m_dm_ops != &xfs_dmcore_stub) ++ symbol_put(xfs_dmcore_xfs); + } +--- a/fs/xfs/xfs_itable.c ++++ b/fs/xfs/xfs_itable.c +@@ -39,7 +39,7 @@ + #include "xfs_error.h" + #include "xfs_btree.h" + +-STATIC int ++int + xfs_internal_inum( + xfs_mount_t *mp, + xfs_ino_t ino) +--- a/fs/xfs/xfs_itable.h ++++ b/fs/xfs/xfs_itable.h +@@ -99,6 +99,11 @@ xfs_bulkstat_one( + void *dibuff, + int *stat); + ++int ++xfs_internal_inum( ++ xfs_mount_t *mp, ++ xfs_ino_t ino); ++ + typedef int (*inumbers_fmt_pf)( + void __user *ubuffer, /* buffer to write to */ + const xfs_inogrp_t *buffer, /* buffer to read from */ +--- a/fs/xfs/xfs_mount.h ++++ b/fs/xfs/xfs_mount.h +@@ -259,6 +259,7 @@ typedef struct xfs_mount { + __int64_t m_update_flags; /* sb flags we need to update + on the next remount,rw */ + struct list_head m_mplist; /* inode shrinker mount list */ ++ struct vfsmount *m_vfsmount; + } xfs_mount_t; + + /* +--- a/fs/xfs/xfs_rw.c ++++ b/fs/xfs/xfs_rw.c +@@ -202,3 +202,4 @@ xfs_get_extsz_hint( + + return extsz; + } ++EXPORT_SYMBOL(xfs_get_extsz_hint); +--- a/fs/xfs/xfs_rw.h ++++ b/fs/xfs/xfs_rw.h +@@ -45,5 +45,10 @@ extern int xfs_read_buf(struct xfs_mount + extern void xfs_ioerror_alert(char *func, struct xfs_mount *mp, + xfs_buf_t *bp, xfs_daddr_t blkno); + extern xfs_extlen_t xfs_get_extsz_hint(struct xfs_inode *ip); ++/* ++ * Prototypes for functions in xfs_vnodeops.c. ++ */ ++extern int xfs_free_eofblocks(struct xfs_mount *mp, struct xfs_inode *ip, ++ int flags); + + #endif /* __XFS_RW_H__ */ +--- a/fs/xfs/xfs_vnodeops.c ++++ b/fs/xfs/xfs_vnodeops.c +@@ -593,7 +593,7 @@ xfs_readlink( + * when the link count isn't zero and by xfs_dm_punch_hole() when + * punching a hole to EOF. + */ +-STATIC int ++int + xfs_free_eofblocks( + xfs_mount_t *mp, + xfs_inode_t *ip, diff --git a/patches.suse/xfs-nfsd-dmapi-aware b/patches.suse/xfs-nfsd-dmapi-aware new file mode 100644 index 0000000..5828e03 --- /dev/null +++ b/patches.suse/xfs-nfsd-dmapi-aware @@ -0,0 +1,181 @@ +From: Greg Banks +Subject: Make NFSD DMAPI aware +References: 74107, 173874, bnc#450658 +Patch-mainline: obstruction... + +G'day, + +The NFSv3 protocol specifies an error, NFS3ERR_JUKEBOX, which a server +should return when an I/O operation will take a very long time. +This causes a different pattern of retries in clients, and avoids +a number of serious problems associated with I/Os which take longer +than an RPC timeout. The Linux knfsd server has code to generate the +jukebox error and many NFS clients are known to have working code to +handle it. + +One scenario in which a server should emit the JUKEBOX error is when +a file data which the client is attempting to access is managed by +an HSM (Hierarchical Storage Manager) and is not present on the disk +and needs to be brought in from tape. Due to the nature of tapes this +operation can take minutes rather than the milliseconds normally seen +for local file data. + +Currently the Linux knfsd handles this situation poorly. A READ NFS +call will cause the nfsd thread handling it to block until the file +is available, without sending a reply to the NFS client. After a +few seconds the client retries, and this second READ call causes +another nfsd to block behind the first one. A few seconds later and +the client's retries have blocked *all* the nfsd threads, and all NFS +service from the server stops until the original file arrives on disk. + +WRITEs and SETATTRs which truncate the file are marginally better, in +that the knfsd dupcache will catch the retries and drop them without +blocking an nfsd (the dupcache *will* catch the retries because the +cache entry remains in RC_INPROG state and is not reused until the +first call finishes). However the first call still blocks, so given +WRITEs to enough offline files the server can still be locked up. + +There are also client-side implications, depending on the client +implementation. For example, on a Linux client an RPC retry loop uses +an RPC request slot, so reads from enough separate offline files can +lock up a mountpoint. + +This patch seeks to remedy the interaction between knfsd and HSMs by +providing mechanisms to allow knfsd to tell an underlying filesystem +(which supports HSMs) not to block for reads, writes and truncates +of offline files. It's a port of a Linux 2.4 patch used in SGI's +ProPack distro for the last 12 months. The patch: + +* provides a new ATTR_NO_BLOCK flag which the kernel can + use to tell a filesystem's inode_ops->setattr() operation not + to block when truncating an offline file. XFS already obeys + this flag (inside a #ifdef) + +* changes knfsd to provide ATTR_NO_BLOCK when it does the VFS + calls to implement the SETATTR NFS call. + +* changes knfsd to supply the O_NONBLOCK flag in the temporary + struct file it uses for VFS reads and writes, in order to ask + the filesystem not to block when reading or writing an offline + file. XFS already obeys this new semantic for O_NONBLOCK + (and in SLES9 so does JFS). + +* adds code to translate the -EAGAIN the filesystem returns when + it would have blocked, to the -ETIMEDOUT that knfsd expects. + + +Signed-off-by: Greg Banks +(SLES9 patch Acked-by: okir@suse.de) +Signed-off-by: NeilBrown +Acked-by: Jan Kara + + fs/nfsd/vfs.c | 32 ++++++++++++++++++++++++++++++-- + fs/xfs/linux-2.6/xfs_iops.c | 7 ++++++- + include/linux/fs.h | 1 + + 3 files changed, 37 insertions(+), 3 deletions(-) + + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -404,6 +404,15 @@ nfsd_setattr(struct svc_rqst *rqstp, str + put_write_access(inode); + goto out_nfserr; + } ++ ++ /* ++ * Tell a Hierarchical Storage Manager (e.g. via DMAPI) to ++ * return EAGAIN when an action would take minutes instead of ++ * milliseconds so that NFS can reply to the client with ++ * NFSERR_JUKEBOX instead of blocking an nfsd thread. ++ */ ++ if (rqstp->rq_vers >= 3) ++ iap->ia_valid |= ATTR_NO_BLOCK; + } + + /* sanitize the mode change */ +@@ -436,6 +445,9 @@ nfsd_setattr(struct svc_rqst *rqstp, str + if (!check_guard || guardtime == inode->i_ctime.tv_sec) { + fh_lock(fhp); + host_err = notify_change(dentry, iap); ++ /* to get NFSERR_JUKEBOX on the wire, need -ETIMEDOUT */ ++ if (host_err == -EAGAIN) ++ host_err = -ETIMEDOUT; + err = nfserrno(host_err); + fh_unlock(fhp); + } +@@ -919,6 +931,10 @@ nfsd_vfs_read(struct svc_rqst *rqstp, st + if (ra && ra->p_set) + file->f_ra = ra->p_ra; + ++ /* Support HSMs -- see comment in nfsd_setattr() */ ++ if (rqstp->rq_vers >= 3) ++ file->f_flags |= O_NONBLOCK; ++ + if (file->f_op->splice_read && rqstp->rq_splice_ok) { + struct splice_desc sd = { + .len = 0, +@@ -951,8 +967,12 @@ nfsd_vfs_read(struct svc_rqst *rqstp, st + *count = host_err; + err = 0; + fsnotify_access(file->f_path.dentry); +- } else ++ } else { ++ /* to get NFSERR_JUKEBOX on the wire, need -ETIMEDOUT */ ++ if (host_err == -EAGAIN) ++ host_err = -ETIMEDOUT; + err = nfserrno(host_err); ++ } + out: + return err; + } +@@ -1053,6 +1073,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, s + spin_unlock(&file->f_lock); + } + ++ /* Support HSMs -- see comment in nfsd_setattr() */ ++ if (rqstp->rq_vers >= 3) ++ file->f_flags |= O_NONBLOCK; ++ + /* Write the data. */ + oldfs = get_fs(); set_fs(KERNEL_DS); + host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); +@@ -1074,8 +1098,12 @@ out_nfserr: + dprintk("nfsd: write complete host_err=%d\n", host_err); + if (host_err >= 0) + err = 0; +- else ++ else { ++ /* to get NFSERR_JUKEBOX on the wire, need -ETIMEDOUT */ ++ if (host_err == -EAGAIN) ++ host_err = -ETIMEDOUT; + err = nfserrno(host_err); ++ } + out: + return err; + } +--- a/fs/xfs/linux-2.6/xfs_iops.c ++++ b/fs/xfs/linux-2.6/xfs_iops.c +@@ -544,7 +544,12 @@ xfs_vn_setattr( + struct dentry *dentry, + struct iattr *iattr) + { +- return -xfs_setattr(XFS_I(dentry->d_inode), iattr, 0); ++ int flags = 0; ++#ifdef ATTR_NO_BLOCK ++ if (iattr->ia_valid & ATTR_NO_BLOCK) ++ flags |= O_NONBLOCK; ++#endif ++ return -xfs_setattr(XFS_I(dentry->d_inode), iattr, flags); + } + + /* +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -438,6 +438,7 @@ typedef void (dio_iodone_t)(struct kiocb + #define ATTR_KILL_PRIV (1 << 14) + #define ATTR_OPEN (1 << 15) /* Truncating from open(O_TRUNC) */ + #define ATTR_TIMES_SET (1 << 16) ++#define ATTR_NO_BLOCK (1 << 17) /* Return EAGAIN and don't block on long truncates */ + + /* + * This is the Inode Attributes structure, used for notify_change(). It diff --git a/patches.xen.tar.bz2 b/patches.xen.tar.bz2 deleted file mode 100644 index a44ca61..0000000 Binary files a/patches.xen.tar.bz2 and /dev/null differ diff --git a/patches.xen/add-console-use-vt b/patches.xen/add-console-use-vt new file mode 100644 index 0000000..fdd91c4 --- /dev/null +++ b/patches.xen/add-console-use-vt @@ -0,0 +1,46 @@ +Subject: add console_use_vt +From: kraxel@suse.de +Patch-mainline: no + +$subject says all + +--- head-2010-05-25.orig/drivers/char/tty_io.c 2010-05-25 09:12:10.000000000 +0200 ++++ head-2010-05-25/drivers/char/tty_io.c 2010-05-25 09:13:34.000000000 +0200 +@@ -136,6 +136,8 @@ LIST_HEAD(tty_drivers); /* linked list + DEFINE_MUTEX(tty_mutex); + EXPORT_SYMBOL(tty_mutex); + ++int console_use_vt = 1; ++ + static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); + static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); + ssize_t redirected_tty_write(struct file *, const char __user *, +@@ -1778,7 +1780,7 @@ retry_open: + goto got_driver; + } + #ifdef CONFIG_VT +- if (device == MKDEV(TTY_MAJOR, 0)) { ++ if (console_use_vt && device == MKDEV(TTY_MAJOR, 0)) { + extern struct tty_driver *console_driver; + driver = tty_driver_kref_get(console_driver); + index = fg_console; +@@ -3160,7 +3162,8 @@ static int __init tty_init(void) + "console"); + + #ifdef CONFIG_VT +- vty_init(&console_fops); ++ if (console_use_vt) ++ vty_init(&console_fops); + #endif + return 0; + } +--- head-2010-05-25.orig/include/linux/console.h 2010-05-25 09:12:10.000000000 +0200 ++++ head-2010-05-25/include/linux/console.h 2010-01-19 14:51:01.000000000 +0100 +@@ -63,6 +63,7 @@ extern const struct consw dummy_con; /* + extern const struct consw vga_con; /* VGA text console */ + extern const struct consw newport_con; /* SGI Newport console */ + extern const struct consw prom_con; /* SPARC PROM console */ ++extern int console_use_vt; + + int con_is_bound(const struct consw *csw); + int register_con_driver(const struct consw *csw, int first, int last); diff --git a/patches.xen/ipv6-no-autoconf b/patches.xen/ipv6-no-autoconf new file mode 100644 index 0000000..106936f --- /dev/null +++ b/patches.xen/ipv6-no-autoconf @@ -0,0 +1,35 @@ +From: Olaf Kirch +Subject: Allow to bring up network interface w/o ipv6 autoconf +References: bnc#161888 +Patch-mainline: no + +When bringing up a xen bridge device, it will always be configured to +use a MAC address of ff:ff:ff:ff:ff:fe. This greatly confuses IPv6 DAD, +which starts logging lots and lots of useless messages to syslog. + +We really want to disable IPv6 on these interfaces, and there doesn't +seem to be a reliable way to do this without bringing the interface +up first (and triggering IPv6 autoconf). + +This patch makes autoconf (DAD and router discovery) depend on the +interface's ability to do multicast. Turning off multicast for an +interface before bringing it up will suppress autoconfiguration. + +--- head-2010-04-15.orig/net/ipv6/addrconf.c 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/net/ipv6/addrconf.c 2010-04-15 09:39:07.000000000 +0200 +@@ -2832,6 +2832,7 @@ static void addrconf_dad_start(struct in + + spin_lock(&ifp->lock); + if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || ++ !(dev->flags&IFF_MULTICAST) || + idev->cnf.accept_dad < 1 || + !(ifp->flags&IFA_F_TENTATIVE) || + ifp->flags & IFA_F_NODAD) { +@@ -2925,6 +2926,7 @@ static void addrconf_dad_completed(struc + if (ifp->idev->cnf.forwarding == 0 && + ifp->idev->cnf.rtr_solicits > 0 && + (dev->flags&IFF_LOOPBACK) == 0 && ++ (dev->flags & IFF_MULTICAST) && + (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { + /* + * If a host as already performed a random delay diff --git a/patches.xen/linux-2.6.19-rc1-kexec-move_segment_code-i386.patch b/patches.xen/linux-2.6.19-rc1-kexec-move_segment_code-i386.patch new file mode 100644 index 0000000..7991481 --- /dev/null +++ b/patches.xen/linux-2.6.19-rc1-kexec-move_segment_code-i386.patch @@ -0,0 +1,155 @@ +Subject: kexec: Move asm segment handling code to the assembly file (i386) +From: http://xenbits.xensource.com/xen-unstable.hg (tip 13816) +Patch-mainline: n/a + +This patch moves the idt, gdt, and segment handling code from machine_kexec.c +to relocate_kernel.S. The main reason behind this move is to avoid code +duplication in the Xen hypervisor. With this patch all code required to kexec +is put on the control page. + +On top of that this patch also counts as a cleanup - I think it is much +nicer to write assembly directly in assembly files than wrap inline assembly +in C functions for no apparent reason. + +Signed-off-by: Magnus Damm +Acked-by: jbeulich@novell.com + + Applies to 2.6.19-rc1. + jb: fixed up register usage (paralleling what's needed for 2.6.30 on x86-64) + +--- head-2010-01-19.orig/arch/x86/kernel/machine_kexec_32.c 2010-01-19 13:26:10.000000000 +0100 ++++ head-2010-01-19/arch/x86/kernel/machine_kexec_32.c 2010-01-19 14:51:07.000000000 +0100 +@@ -27,48 +27,6 @@ + #include + #include + +-static void set_idt(void *newidt, __u16 limit) +-{ +- struct desc_ptr curidt; +- +- /* ia32 supports unaliged loads & stores */ +- curidt.size = limit; +- curidt.address = (unsigned long)newidt; +- +- load_idt(&curidt); +-} +- +- +-static void set_gdt(void *newgdt, __u16 limit) +-{ +- struct desc_ptr curgdt; +- +- /* ia32 supports unaligned loads & stores */ +- curgdt.size = limit; +- curgdt.address = (unsigned long)newgdt; +- +- load_gdt(&curgdt); +-} +- +-static void load_segments(void) +-{ +-#define __STR(X) #X +-#define STR(X) __STR(X) +- +- __asm__ __volatile__ ( +- "\tljmp $"STR(__KERNEL_CS)",$1f\n" +- "\t1:\n" +- "\tmovl $"STR(__KERNEL_DS)",%%eax\n" +- "\tmovl %%eax,%%ds\n" +- "\tmovl %%eax,%%es\n" +- "\tmovl %%eax,%%fs\n" +- "\tmovl %%eax,%%gs\n" +- "\tmovl %%eax,%%ss\n" +- : : : "eax", "memory"); +-#undef STR +-#undef __STR +-} +- + static void machine_kexec_free_page_tables(struct kimage *image) + { + free_page((unsigned long)image->arch.pgd); +@@ -228,24 +186,6 @@ void machine_kexec(struct kimage *image) + page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page) + << PAGE_SHIFT); + +- /* +- * The segment registers are funny things, they have both a +- * visible and an invisible part. Whenever the visible part is +- * set to a specific selector, the invisible part is loaded +- * with from a table in memory. At no other time is the +- * descriptor table in memory accessed. +- * +- * I take advantage of this here by force loading the +- * segments, before I zap the gdt with an invalid value. +- */ +- load_segments(); +- /* +- * The gdt & idt are now invalid. +- * If you want to load them you must set up your own idt & gdt. +- */ +- set_gdt(phys_to_virt(0), 0); +- set_idt(phys_to_virt(0), 0); +- + /* now call it */ + image->start = relocate_kernel_ptr((unsigned long)image->head, + (unsigned long)page_list, +--- head-2010-01-19.orig/arch/x86/kernel/relocate_kernel_32.S 2009-06-10 05:05:27.000000000 +0200 ++++ head-2010-01-19/arch/x86/kernel/relocate_kernel_32.S 2010-01-19 14:51:07.000000000 +0100 +@@ -87,14 +87,32 @@ relocate_kernel: + movl PTR(PA_PGD)(%ebp), %eax + movl %eax, %cr3 + ++ /* setup idt */ ++ lidtl idt_48 - relocate_kernel(%edi) ++ ++ /* setup gdt */ ++ leal gdt - relocate_kernel(%edi), %eax ++ movl %eax, (gdt_48 - relocate_kernel) + 2(%edi) ++ lgdtl gdt_48 - relocate_kernel(%edi) ++ ++ /* setup data segment registers */ ++ mov $(gdt_ds - gdt), %eax ++ mov %eax, %ds ++ mov %eax, %es ++ mov %eax, %fs ++ mov %eax, %gs ++ mov %eax, %ss ++ + /* setup a new stack at the end of the physical control page */ + lea PAGE_SIZE(%edi), %esp + +- /* jump to identity mapped page */ ++ /* load new code segment and jump to identity mapped page */ ++ pushl $0 ++ pushl $(gdt_cs - gdt) + movl %edi, %eax + addl $(identity_mapped - relocate_kernel), %eax + pushl %eax +- ret ++ iretl + + identity_mapped: + /* store the start address on the stack */ +@@ -271,5 +289,22 @@ swap_pages: + popl %ebp + ret + ++ .align 16 ++gdt: ++ .quad 0x0000000000000000 /* NULL descriptor */ ++gdt_cs: ++ .quad 0x00cf9a000000ffff /* kernel 4GB code at 0x00000000 */ ++gdt_ds: ++ .quad 0x00cf92000000ffff /* kernel 4GB data at 0x00000000 */ ++gdt_end: ++ ++gdt_48: ++ .word gdt_end - gdt - 1 /* limit */ ++ .long 0 /* base - filled in by code above */ ++ ++idt_48: ++ .word 0 /* limit */ ++ .long 0 /* base */ ++ + .globl kexec_control_code_size + .set kexec_control_code_size, . - relocate_kernel diff --git a/patches.xen/linux-2.6.19-rc1-kexec-move_segment_code-x86_64.patch b/patches.xen/linux-2.6.19-rc1-kexec-move_segment_code-x86_64.patch new file mode 100644 index 0000000..bbd3780 --- /dev/null +++ b/patches.xen/linux-2.6.19-rc1-kexec-move_segment_code-x86_64.patch @@ -0,0 +1,150 @@ +Subject: kexec: Move asm segment handling code to the assembly file (x86_64) +From: http://xenbits.xensource.com/xen-unstable.hg (tip 13816) +Patch-mainline: n/a + +This patch moves the idt, gdt, and segment handling code from machine_kexec.c +to relocate_kernel.S. The main reason behind this move is to avoid code +duplication in the Xen hypervisor. With this patch all code required to kexec +is put on the control page. + +On top of that this patch also counts as a cleanup - I think it is much +nicer to write assembly directly in assembly files than wrap inline assembly +in C functions for no apparent reason. + +Signed-off-by: Magnus Damm +Acked-by: jbeulich@novell.com + + Applies to 2.6.19-rc1. + jb: fixed up register usage for 2.6.30 (bnc#545206) + +--- head-2010-04-15.orig/arch/x86/kernel/machine_kexec_64.c 2010-04-15 09:37:47.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/machine_kexec_64.c 2010-04-15 09:38:56.000000000 +0200 +@@ -203,47 +203,6 @@ static int init_pgtable(struct kimage *i + return init_transition_pgtable(image, level4p); + } + +-static void set_idt(void *newidt, u16 limit) +-{ +- struct desc_ptr curidt; +- +- /* x86-64 supports unaliged loads & stores */ +- curidt.size = limit; +- curidt.address = (unsigned long)newidt; +- +- __asm__ __volatile__ ( +- "lidtq %0\n" +- : : "m" (curidt) +- ); +-}; +- +- +-static void set_gdt(void *newgdt, u16 limit) +-{ +- struct desc_ptr curgdt; +- +- /* x86-64 supports unaligned loads & stores */ +- curgdt.size = limit; +- curgdt.address = (unsigned long)newgdt; +- +- __asm__ __volatile__ ( +- "lgdtq %0\n" +- : : "m" (curgdt) +- ); +-}; +- +-static void load_segments(void) +-{ +- __asm__ __volatile__ ( +- "\tmovl %0,%%ds\n" +- "\tmovl %0,%%es\n" +- "\tmovl %0,%%ss\n" +- "\tmovl %0,%%fs\n" +- "\tmovl %0,%%gs\n" +- : : "a" (__KERNEL_DS) : "memory" +- ); +-} +- + int machine_kexec_prepare(struct kimage *image) + { + unsigned long start_pgtable; +@@ -311,24 +270,6 @@ void machine_kexec(struct kimage *image) + page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page) + << PAGE_SHIFT); + +- /* +- * The segment registers are funny things, they have both a +- * visible and an invisible part. Whenever the visible part is +- * set to a specific selector, the invisible part is loaded +- * with from a table in memory. At no other time is the +- * descriptor table in memory accessed. +- * +- * I take advantage of this here by force loading the +- * segments, before I zap the gdt with an invalid value. +- */ +- load_segments(); +- /* +- * The gdt & idt are now invalid. +- * If you want to load them you must set up your own idt & gdt. +- */ +- set_gdt(phys_to_virt(0), 0); +- set_idt(phys_to_virt(0), 0); +- + /* now call it */ + image->start = relocate_kernel((unsigned long)image->head, + (unsigned long)page_list, +--- head-2010-04-15.orig/arch/x86/kernel/relocate_kernel_64.S 2010-04-15 09:37:47.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/relocate_kernel_64.S 2010-01-19 14:51:10.000000000 +0100 +@@ -91,13 +91,30 @@ relocate_kernel: + /* Switch to the identity mapped page tables */ + movq %r9, %cr3 + ++ /* setup idt */ ++ lidtq idt_80 - relocate_kernel(%r8) ++ ++ /* setup gdt */ ++ leaq gdt - relocate_kernel(%r8), %rax ++ movq %rax, (gdt_80 - relocate_kernel) + 2(%r8) ++ lgdtq gdt_80 - relocate_kernel(%r8) ++ ++ /* setup data segment registers */ ++ xorl %eax, %eax ++ movl %eax, %ds ++ movl %eax, %es ++ movl %eax, %fs ++ movl %eax, %gs ++ movl %eax, %ss ++ + /* setup a new stack at the end of the physical control page */ + lea PAGE_SIZE(%r8), %rsp + +- /* jump to identity mapped page */ ++ /* load new code segment and jump to identity mapped page */ + addq $(identity_mapped - relocate_kernel), %r8 ++ pushq $(gdt_cs - gdt) + pushq %r8 +- ret ++ lretq + + identity_mapped: + /* store the start address on the stack */ +@@ -262,5 +279,20 @@ swap_pages: + 3: + ret + ++ .align 16 ++gdt: ++ .quad 0x0000000000000000 /* NULL descriptor */ ++gdt_cs: ++ .quad 0x00af9a000000ffff ++gdt_end: ++ ++gdt_80: ++ .word gdt_end - gdt - 1 /* limit */ ++ .quad 0 /* base - filled in by code above */ ++ ++idt_80: ++ .word 0 /* limit */ ++ .quad 0 /* base */ ++ + .globl kexec_control_code_size + .set kexec_control_code_size, . - relocate_kernel diff --git a/patches.xen/pci-guestdev b/patches.xen/pci-guestdev new file mode 100644 index 0000000..096966b --- /dev/null +++ b/patches.xen/pci-guestdev @@ -0,0 +1,2641 @@ +Subject: xen/dom0: Reserve devices for guest use +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 898:ca12928cdafe) +Patch-mainline: n/a + +jb: Added support for reassign_resources=all (bnc#574224). +jb: Used kzalloc() instead of all kmalloc()+memset() pairs. +Acked-by: jbeulich@novell.com + +--- head-2010-04-29.orig/Documentation/kernel-parameters.txt 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/Documentation/kernel-parameters.txt 2010-04-29 09:30:30.000000000 +0200 +@@ -834,6 +834,24 @@ and is between 256 and 4096 characters. + gpt [EFI] Forces disk with valid GPT signature but + invalid Protective MBR to be treated as GPT. + ++ guestdev= [PCI,ACPI,XEN] ++ Format: {|}][,{|}[,...]] ++ Format of device path: [:]-.[-.[,...]][+iomul] ++ Format of sbdf: [:]:.[+iomul] ++ Specifies PCI device for guest domain. ++ If PCI-PCI bridge is specified, all PCI devices ++ behind PCI-PCI bridge are reserved. ++ +iomul means that this PCI function will share ++ IO ports with other +iomul functions under same ++ switch. NOTE: if +iomul is specfied, all the functions ++ of the device will share IO ports. ++ ++ guestiomuldev= [PCI,ACPI,XEN] ++ Format: [sbd][,][,...] ++ Format of sbdf: [:]: ++ Note: function shouldn't be specified. ++ Specifies PCI device for IO port multiplexing driver. ++ + gvp11= [HW,SCSI] + + hashdist= [KNL,NUMA] Large hashes allocated during boot +@@ -2183,6 +2201,10 @@ and is between 256 and 4096 characters. + Run specified binary instead of /init from the ramdisk, + used for early userspace startup. See initrd. + ++ reassign_resources [PCI,ACPI,XEN] ++ Use guestdev= parameter to reassign device's ++ resources, or specify =all here. ++ + reboot= [BUGS=X86-32,BUGS=ARM,BUGS=IA-64] Rebooting mode + Format: [,[,...]] + See arch/*/kernel/reboot.c or arch/*/kernel/process.c +--- head-2010-04-29.orig/drivers/acpi/pci_root.c 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/drivers/acpi/pci_root.c 2010-04-15 09:39:25.000000000 +0200 +@@ -419,6 +419,40 @@ out: + } + EXPORT_SYMBOL(acpi_pci_osc_control_set); + ++#ifdef CONFIG_PCI_GUESTDEV ++#include ++ ++static ssize_t seg_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct list_head *entry; ++ ++ list_for_each(entry, &acpi_pci_roots) { ++ struct acpi_pci_root *root; ++ root = list_entry(entry, struct acpi_pci_root, node); ++ if (&root->device->dev == dev) ++ return sprintf(buf, "%04x\n", root->segment); ++ } ++ return 0; ++} ++static DEVICE_ATTR(seg, 0444, seg_show, NULL); ++ ++static ssize_t bbn_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct list_head *entry; ++ ++ list_for_each(entry, &acpi_pci_roots) { ++ struct acpi_pci_root *root; ++ root = list_entry(entry, struct acpi_pci_root, node); ++ if (&root->device->dev == dev) ++ return sprintf(buf, "%02x\n", root->bus_nr); ++ } ++ return 0; ++} ++static DEVICE_ATTR(bbn, 0444, bbn_show, NULL); ++#endif ++ + static int __devinit acpi_pci_root_add(struct acpi_device *device) + { + unsigned long long segment, bus; +@@ -530,6 +564,13 @@ static int __devinit acpi_pci_root_add(s + if (flags != base_flags) + acpi_pci_osc_support(root, flags); + ++#ifdef CONFIG_PCI_GUESTDEV ++ if (device_create_file(&device->dev, &dev_attr_seg)) ++ dev_warn(&device->dev, "could not create seg attr\n"); ++ if (device_create_file(&device->dev, &dev_attr_bbn)) ++ dev_warn(&device->dev, "could not create bbn attr\n"); ++#endif ++ + pci_acpi_add_bus_pm_notifier(device, root->bus); + if (device->wakeup.flags.run_wake) + device_set_run_wake(root->bus->bridge, true); +@@ -575,3 +616,31 @@ static int __init acpi_pci_root_init(voi + } + + subsys_initcall(acpi_pci_root_init); ++ ++#ifdef CONFIG_PCI_GUESTDEV ++int acpi_pci_get_root_seg_bbn(char *hid, char *uid, int *seg, int *bbn) ++{ ++ struct list_head *entry; ++ ++ list_for_each(entry, &acpi_pci_roots) { ++ struct acpi_pci_root *root; ++ ++ root = list_entry(entry, struct acpi_pci_root, node); ++ if (strcmp(acpi_device_hid(root->device), hid)) ++ continue; ++ ++ if (!root->device->pnp.unique_id) { ++ if (strlen(uid)) ++ continue; ++ } else { ++ if (strcmp(root->device->pnp.unique_id, uid)) ++ continue; ++ } ++ ++ *seg = (int)root->segment; ++ *bbn = (int)root->bus_nr; ++ return TRUE; ++ } ++ return FALSE; ++} ++#endif +--- head-2010-04-29.orig/drivers/acpi/scan.c 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/drivers/acpi/scan.c 2010-04-15 09:39:27.000000000 +0200 +@@ -170,6 +170,16 @@ acpi_device_hid_show(struct device *dev, + } + static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL); + ++#ifdef CONFIG_PCI_GUESTDEV ++static ssize_t ++acpi_device_uid_show(struct device *dev, struct device_attribute *attr, char *buf) { ++ struct acpi_device *acpi_dev = to_acpi_device(dev); ++ ++ return sprintf(buf, "%s\n", acpi_dev->pnp.unique_id); ++} ++static DEVICE_ATTR(uid, 0444, acpi_device_uid_show, NULL); ++#endif ++ + static ssize_t + acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); +@@ -210,6 +220,13 @@ static int acpi_device_setup_files(struc + if (result) + goto end; + ++#ifdef CONFIG_PCI_GUESTDEV ++ if(dev->pnp.unique_id) { ++ result = device_create_file(&dev->dev, &dev_attr_uid); ++ if(result) ++ goto end; ++ } ++#endif + /* + * If device has _EJ0, 'eject' file is created that is used to trigger + * hot-removal function from userland. +@@ -273,6 +290,9 @@ static void acpi_free_ids(struct acpi_de + kfree(id->id); + kfree(id); + } ++#ifdef CONFIG_PCI_GUESTDEV ++ kfree(device->pnp.unique_id); ++#endif + } + + static void acpi_device_release(struct device *dev) +@@ -1096,6 +1116,11 @@ static void acpi_device_set_id(struct ac + for (i = 0; i < cid_list->count; i++) + acpi_add_id(device, cid_list->ids[i].string); + } ++#ifdef CONFIG_PCI_GUESTDEV ++ if (info->valid & ACPI_VALID_UID) ++ device->pnp.unique_id = kstrdup(info->unique_id.string, ++ GFP_KERNEL); ++#endif + if (info->valid & ACPI_VALID_ADR) { + device->pnp.bus_address = info->address; + device->flags.bus_address = 1; +--- head-2010-04-29.orig/drivers/pci/Kconfig 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/drivers/pci/Kconfig 2010-03-24 13:55:21.000000000 +0100 +@@ -31,6 +31,20 @@ config PCI_DEBUG + + When in doubt, say N. + ++config PCI_GUESTDEV ++ bool "PCI Device Reservation for Passthrough" ++ depends on PCI && ACPI && XEN ++ default y ++ help ++ Say Y here if you want to reserve PCI device for passthrough. ++ ++config PCI_IOMULTI ++ bool "PCI Device IO Multiplex for Passthrough" ++ depends on PCI && ACPI && XEN ++ default y ++ help ++ Say Y here if you need io multiplexing. ++ + config PCI_STUB + tristate "PCI Stub driver" + depends on PCI +--- head-2010-04-29.orig/drivers/pci/Makefile 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/drivers/pci/Makefile 2010-03-24 13:55:21.000000000 +0100 +@@ -7,6 +7,8 @@ obj-y += access.o bus.o probe.o remove. + irq.o vpd.o + obj-$(CONFIG_PROC_FS) += proc.o + obj-$(CONFIG_SYSFS) += slot.o ++obj-$(CONFIG_PCI_GUESTDEV) += guestdev.o ++obj-$(CONFIG_PCI_IOMULTI) += iomulti.o + + obj-$(CONFIG_PCI_QUIRKS) += quirks.o + +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/drivers/pci/guestdev.c 2010-04-28 15:57:49.000000000 +0200 +@@ -0,0 +1,887 @@ ++/* ++ * Copyright (c) 2008, 2009 NEC Corporation. ++ * Copyright (c) 2009 Isaku Yamahata ++ * VA Linux Systems Japan K.K. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple ++ * Place - Suite 330, Boston, MA 02111-1307 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HID_LEN 8 ++#define UID_LEN 8 ++#define DEV_LEN 2 ++#define FUNC_LEN 1 ++#define DEV_NUM_MAX 31 ++#define FUNC_NUM_MAX 7 ++#define INVALID_SEG (-1) ++#define INVALID_BBN (-1) ++#define GUESTDEV_STR_MAX 128 ++ ++#define GUESTDEV_FLAG_TYPE_MASK 0x3 ++#define GUESTDEV_FLAG_DEVICEPATH 0x1 ++#define GUESTDEV_FLAG_SBDF 0x2 ++ ++#define GUESTDEV_OPT_IOMUL 0x1 ++ ++struct guestdev { ++ int flags; ++ int options; ++ struct list_head root_list; ++ union { ++ struct devicepath { ++ char hid[HID_LEN + 1]; ++ char uid[UID_LEN + 1]; ++ int seg; ++ int bbn; ++ struct devicepath_node *child; ++ } devicepath; ++ struct sbdf { ++ int seg; ++ int bus; ++ int dev; ++ int func; ++ } sbdf; ++ } u; ++}; ++ ++struct devicepath_node { ++ int dev; ++ int func; ++ struct devicepath_node *child; ++}; ++ ++struct pcidev_sbdf { ++ int seg; ++ int bus; ++ struct pcidev_sbdf_node *child; ++}; ++ ++struct pcidev_sbdf_node { ++ int dev; ++ int func; ++ struct pcidev_sbdf_node *child; ++}; ++ ++static char guestdev_param[COMMAND_LINE_SIZE]; ++static LIST_HEAD(guestdev_list); ++ ++/* Get hid and uid */ ++static int __init pci_get_hid_uid(char *str, char *hid, char *uid) ++{ ++ char *sp, *ep; ++ int len; ++ ++ sp = str; ++ ep = strchr(sp, ':'); ++ if (!ep) { ++ ep = strchr(sp, '-'); ++ if (!ep) ++ goto format_err_end; ++ } ++ /* hid length */ ++ len = ep - sp; ++ if (len <= 0 || HID_LEN < len) ++ goto format_err_end; ++ ++ strlcpy(hid, sp, len); ++ ++ if (*ep == '-') { /* no uid */ ++ uid[0] = '\0'; ++ return TRUE; ++ } ++ ++ sp = ep + 1; ++ ep = strchr(sp, '-'); ++ if (!ep) ++ ep = strchr(sp, '\0'); ++ ++ /* uid length */ ++ len = ep - sp; ++ if (len <= 0 || UID_LEN < len) ++ goto format_err_end; ++ ++ strlcpy(uid, sp, len); ++ return TRUE; ++ ++format_err_end: ++ return FALSE; ++} ++ ++/* Get device and function */ ++static int __init pci_get_dev_func(char *str, int *dev, int *func) ++{ ++ if (sscanf(str, "%02x.%01x", dev, func) != 2) ++ goto format_err_end; ++ ++ if (*dev < 0 || DEV_NUM_MAX < *dev) ++ goto format_err_end; ++ ++ if (*func < 0 || FUNC_NUM_MAX < *func) ++ goto format_err_end; ++ ++ return TRUE; ++ ++format_err_end: ++ return FALSE; ++} ++ ++/* Check extended guestdev parameter format error */ ++static int __init pci_check_extended_guestdev_format(char *str) ++{ ++ int flg; ++ char *p; ++ ++ /* Check extended format */ ++ if (strpbrk(str, "(|)") == NULL) ++ return TRUE; ++ ++ flg = 0; ++ p = str; ++ while (*p) { ++ switch (*p) { ++ case '(': ++ /* Check nesting error */ ++ if (flg != 0) ++ goto format_err_end; ++ flg = 1; ++ /* Check position of '(' is head or ++ previos charactor of '(' is not '-'. */ ++ if (p == str || *(p - 1) != '-') ++ goto format_err_end; ++ break; ++ case ')': ++ /* Check nesting error */ ++ if (flg != 1) ++ goto format_err_end; ++ flg = 0; ++ /* Check next charactor of ')' is not '\0' */ ++ if (*(p + 1) != '\0') ++ goto format_err_end; ++ break; ++ case '|': ++ /* Check position of '|' is outside of '(' and ')' */ ++ if (flg != 1) ++ goto format_err_end; ++ break; ++ default: ++ break; ++ } ++ p++; ++ } ++ /* Check number of '(' and ')' are not equal */ ++ if (flg != 0) ++ goto format_err_end; ++ return TRUE; ++ ++format_err_end: ++ printk(KERN_ERR ++ "PCI: The format of the guestdev parameter is illegal. [%s]\n", ++ str); ++ return FALSE; ++} ++ ++/* Make guestdev strings */ ++static void pci_make_guestdev_str(struct guestdev *gdev, ++ char *gdev_str, int buf_size) ++{ ++ struct devicepath_node *node; ++ int count; ++ ++ switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) { ++ case GUESTDEV_FLAG_DEVICEPATH: ++ memset(gdev_str, 0, buf_size); ++ ++ if (strlen(gdev->u.devicepath.uid)) ++ count = snprintf(gdev_str, buf_size, "%s:%s", ++ gdev->u.devicepath.hid, ++ gdev->u.devicepath.uid); ++ else ++ count = snprintf(gdev_str, buf_size, "%s", ++ gdev->u.devicepath.hid); ++ if (count < 0) ++ return; ++ ++ node = gdev->u.devicepath.child; ++ while (node) { ++ gdev_str += count; ++ buf_size -= count; ++ if (buf_size <= 0) ++ return; ++ count = snprintf(gdev_str, buf_size, "-%02x.%01x", ++ node->dev, node->func); ++ if (count < 0) ++ return; ++ node = node->child; ++ } ++ break; ++ case GUESTDEV_FLAG_SBDF: ++ snprintf(gdev_str, buf_size, "%04x:%02x:%02x.%01x", ++ gdev->u.sbdf.seg, gdev->u.sbdf.bus, ++ gdev->u.sbdf.dev, gdev->u.sbdf.func); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++/* Free guestdev and nodes */ ++static void __init pci_free_guestdev(struct guestdev *gdev) ++{ ++ struct devicepath_node *node, *next; ++ ++ if (!gdev) ++ return; ++ if (gdev->flags & GUESTDEV_FLAG_DEVICEPATH) { ++ node = gdev->u.devicepath.child; ++ while (node) { ++ next = node->child; ++ kfree(node); ++ node = next; ++ } ++ } ++ list_del(&gdev->root_list); ++ kfree(gdev); ++} ++ ++/* Copy guestdev and nodes */ ++struct guestdev __init *pci_copy_guestdev(struct guestdev *gdev_src) ++{ ++ struct guestdev *gdev; ++ struct devicepath_node *node, *node_src, *node_upper; ++ ++ BUG_ON(!(gdev_src->flags & GUESTDEV_FLAG_DEVICEPATH)); ++ ++ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); ++ if (!gdev) ++ goto allocate_err_end; ++ ++ INIT_LIST_HEAD(&gdev->root_list); ++ gdev->flags = gdev_src->flags; ++ gdev->options = gdev_src->options; ++ strcpy(gdev->u.devicepath.hid, gdev_src->u.devicepath.hid); ++ strcpy(gdev->u.devicepath.uid, gdev_src->u.devicepath.uid); ++ gdev->u.devicepath.seg = gdev_src->u.devicepath.seg; ++ gdev->u.devicepath.bbn = gdev_src->u.devicepath.bbn; ++ ++ node_upper = NULL; ++ ++ node_src = gdev_src->u.devicepath.child; ++ while (node_src) { ++ node = kzalloc(sizeof(*node), GFP_KERNEL); ++ if (!node) ++ goto allocate_err_end; ++ node->dev = node_src->dev; ++ node->func = node_src->func; ++ if (!node_upper) ++ gdev->u.devicepath.child = node; ++ else ++ node_upper->child = node; ++ node_upper = node; ++ node_src = node_src->child; ++ } ++ ++ return gdev; ++ ++allocate_err_end: ++ if (gdev) ++ pci_free_guestdev(gdev); ++ printk(KERN_ERR "PCI: Failed to allocate memory.\n"); ++ return NULL; ++} ++ ++/* Make guestdev from path strings */ ++static int __init pci_make_devicepath_guestdev(char *path_str, int options) ++{ ++ char hid[HID_LEN + 1], uid[UID_LEN + 1]; ++ char *sp, *ep; ++ struct guestdev *gdev, *gdev_org; ++ struct devicepath_node *node, *node_tmp; ++ int dev, func, ret_val; ++ ++ ret_val = 0; ++ gdev = gdev_org = NULL; ++ sp = path_str; ++ /* Look for end of hid:uid'-' */ ++ ep = strchr(sp, '-'); ++ /* Only hid, uid. (No dev, func) */ ++ if (!ep) ++ goto format_err_end; ++ ++ memset(hid, 0 ,sizeof(hid)); ++ memset(uid, 0, sizeof(uid)); ++ if (!pci_get_hid_uid(sp, hid, uid)) ++ goto format_err_end; ++ ++ gdev_org = kzalloc(sizeof(*gdev_org), GFP_KERNEL); ++ if (!gdev_org) ++ goto allocate_err_end; ++ INIT_LIST_HEAD(&gdev_org->root_list); ++ gdev_org->flags = GUESTDEV_FLAG_DEVICEPATH; ++ gdev_org->options = options; ++ strcpy(gdev_org->u.devicepath.hid, hid); ++ strcpy(gdev_org->u.devicepath.uid, uid); ++ gdev_org->u.devicepath.seg = INVALID_SEG; ++ gdev_org->u.devicepath.bbn = INVALID_BBN; ++ ++ gdev = gdev_org; ++ ++ sp = ep + 1; ++ ep = sp; ++ do { ++ if (*sp == '(') { ++ sp++; ++ if (strchr(sp, '|')) { ++ gdev = pci_copy_guestdev(gdev_org); ++ if (!gdev) { ++ ret_val = -ENOMEM; ++ goto end; ++ } ++ } ++ continue; ++ } ++ if (gdev && pci_get_dev_func(sp, &dev, &func)) { ++ node = kzalloc(sizeof(*node), GFP_KERNEL); ++ if (!node) ++ goto allocate_err_end; ++ node->dev = dev; ++ node->func = func; ++ /* add node to end of guestdev */ ++ if (gdev->u.devicepath.child) { ++ node_tmp = gdev->u.devicepath.child; ++ while (node_tmp->child) { ++ node_tmp = node_tmp->child; ++ } ++ node_tmp->child = node; ++ } else ++ gdev->u.devicepath.child = node; ++ } else if (gdev) { ++ printk(KERN_ERR ++ "PCI: Can't obtain dev# and #func# from %s.\n", ++ sp); ++ ret_val = -EINVAL; ++ if (gdev == gdev_org) ++ goto end; ++ pci_free_guestdev(gdev); ++ gdev = NULL; ++ } ++ ++ ep = strpbrk(sp, "-|)"); ++ if (!ep) ++ ep = strchr(sp, '\0'); ++ /* Is *ep '|' OR ')' OR '\0' ? */ ++ if (*ep != '-') { ++ if (gdev) ++ list_add_tail(&gdev->root_list, &guestdev_list); ++ if (*ep == '|') { ++ /* Between '|' and '|' ? */ ++ if (strchr(ep + 1, '|')) { ++ gdev = pci_copy_guestdev(gdev_org); ++ if (!gdev) { ++ ret_val = -ENOMEM; ++ goto end; ++ } ++ } else { ++ gdev = gdev_org; ++ gdev_org = NULL; ++ } ++ } else { ++ gdev_org = NULL; ++ gdev = NULL; ++ } ++ } ++ if (*ep == ')') ++ ep++; ++ sp = ep + 1; ++ } while (*ep != '\0'); ++ ++ goto end; ++ ++format_err_end: ++ printk(KERN_ERR ++ "PCI: The format of the guestdev parameter is illegal. [%s]\n", ++ path_str); ++ ret_val = -EINVAL; ++ goto end; ++ ++allocate_err_end: ++ printk(KERN_ERR "PCI: Failed to allocate memory.\n"); ++ ret_val = -ENOMEM; ++ goto end; ++ ++end: ++ if (gdev_org && (gdev_org != gdev)) ++ pci_free_guestdev(gdev_org); ++ if (gdev) ++ pci_free_guestdev(gdev); ++ return ret_val; ++} ++ ++static int __init pci_make_sbdf_guestdev(char* str, int options) ++{ ++ struct guestdev *gdev; ++ int seg, bus, dev, func; ++ ++ if (sscanf(str, "%x:%x:%x.%x", &seg, &bus, &dev, &func) != 4) { ++ seg = 0; ++ if (sscanf(str, "%x:%x.%x", &bus, &dev, &func) != 3) ++ return -EINVAL; ++ } ++ gdev = kmalloc(sizeof(*gdev), GFP_KERNEL); ++ if (!gdev) { ++ printk(KERN_ERR "PCI: Failed to allocate memory.\n"); ++ return -ENOMEM; ++ } ++ INIT_LIST_HEAD(&gdev->root_list); ++ gdev->flags = GUESTDEV_FLAG_SBDF; ++ gdev->options = options; ++ gdev->u.sbdf.seg = seg; ++ gdev->u.sbdf.bus = bus; ++ gdev->u.sbdf.dev = dev; ++ gdev->u.sbdf.func = func; ++ list_add_tail(&gdev->root_list, &guestdev_list); ++ return 0; ++} ++ ++static int __init pci_parse_options(const char *str) ++{ ++ int options = 0; ++ char *ep; ++ ++ while (str) { ++ str++; ++ ep = strchr(str, '+'); ++ if (ep) ++ ep = '\0'; /* Chop */ ++ ++ if (!strcmp(str, "iomul")) ++ options |= GUESTDEV_OPT_IOMUL; ++ ++ str = ep; ++ } ++ return options; ++} ++ ++/* Parse guestdev parameter */ ++static int __init pci_parse_guestdev(void) ++{ ++ int len; ++ char *sp, *ep, *op; ++ int options; ++ struct list_head *head; ++ struct guestdev *gdev; ++ char path_str[GUESTDEV_STR_MAX]; ++ int ret_val = 0; ++ ++ len = strlen(guestdev_param); ++ if (len == 0) ++ return 0; ++ ++ sp = guestdev_param; ++ ++ do { ++ ep = strchr(sp, ','); ++ /* Chop */ ++ if (ep) ++ *ep = '\0'; ++ options = 0; ++ op = strchr(sp, '+'); ++ if (op && (!ep || op < ep)) { ++ options = pci_parse_options(op); ++ *op = '\0'; /* Chop */ ++ } ++ ret_val = pci_make_sbdf_guestdev(sp, options); ++ if (ret_val == -EINVAL) { ++ if (pci_check_extended_guestdev_format(sp)) { ++ ret_val = pci_make_devicepath_guestdev( ++ sp, options); ++ if (ret_val && ret_val != -EINVAL) ++ break; ++ } ++ } else if (ret_val) ++ break; ++ ++ if (ep) ++ ep++; ++ sp = ep; ++ } while (ep); ++ ++ list_for_each(head, &guestdev_list) { ++ gdev = list_entry(head, struct guestdev, root_list); ++ pci_make_guestdev_str(gdev, path_str, GUESTDEV_STR_MAX); ++ printk(KERN_DEBUG ++ "PCI: %s has been reserved for guest domain.\n", ++ path_str); ++ } ++ return 0; ++} ++ ++arch_initcall(pci_parse_guestdev); ++ ++/* Get command line */ ++static int __init pci_guestdev_setup(char *str) ++{ ++ if (strlen(str) >= COMMAND_LINE_SIZE) ++ return 0; ++ strlcpy(guestdev_param, str, sizeof(guestdev_param)); ++ return 1; ++} ++ ++__setup("guestdev=", pci_guestdev_setup); ++ ++/* Free sbdf and nodes */ ++static void pci_free_sbdf(struct pcidev_sbdf *sbdf) ++{ ++ struct pcidev_sbdf_node *node, *next; ++ ++ node = sbdf->child; ++ while (node) { ++ next = node->child; ++ kfree(node); ++ node = next; ++ } ++ /* Skip kfree(sbdf) */ ++} ++ ++/* Does PCI device belong to sub tree specified by guestdev with device path? */ ++typedef int (*pci_node_match_t)(const struct devicepath_node *gdev_node, ++ const struct pcidev_sbdf_node *sbdf_node, ++ int options); ++ ++static int pci_node_match(const struct devicepath_node *gdev_node, ++ const struct pcidev_sbdf_node *sbdf_node, ++ int options_unused) ++{ ++ return (gdev_node->dev == sbdf_node->dev && ++ gdev_node->func == sbdf_node->func); ++} ++ ++static int pci_is_in_devicepath_sub_tree(struct guestdev *gdev, ++ struct pcidev_sbdf *sbdf, ++ pci_node_match_t match) ++{ ++ int seg, bbn; ++ struct devicepath_node *gdev_node; ++ struct pcidev_sbdf_node *sbdf_node; ++ ++ if (!gdev || !sbdf) ++ return FALSE; ++ ++ BUG_ON(!(gdev->flags & GUESTDEV_FLAG_DEVICEPATH)); ++ ++ /* Compare seg and bbn */ ++ if (gdev->u.devicepath.seg == INVALID_SEG || ++ gdev->u.devicepath.bbn == INVALID_BBN) { ++ if (acpi_pci_get_root_seg_bbn(gdev->u.devicepath.hid, ++ gdev->u.devicepath.uid, &seg, &bbn)) { ++ gdev->u.devicepath.seg = seg; ++ gdev->u.devicepath.bbn = bbn; ++ } else ++ return FALSE; ++ } ++ ++ if (gdev->u.devicepath.seg != sbdf->seg || ++ gdev->u.devicepath.bbn != sbdf->bus) ++ return FALSE; ++ ++ gdev_node = gdev->u.devicepath.child; ++ sbdf_node = sbdf->child; ++ ++ /* Compare dev and func */ ++ while (gdev_node) { ++ if (!sbdf_node) ++ return FALSE; ++ if (!match(gdev_node, sbdf_node, gdev->options)) ++ return FALSE; ++ gdev_node = gdev_node->child; ++ sbdf_node = sbdf_node->child; ++ } ++ return TRUE; ++} ++ ++/* Get sbdf from device */ ++static int pci_get_sbdf_from_pcidev( ++ struct pci_dev *dev, struct pcidev_sbdf *sbdf) ++{ ++ struct pcidev_sbdf_node *node; ++ ++ if (!dev) ++ return FALSE; ++ ++ for(;;) { ++ node = kzalloc(sizeof(*node), GFP_KERNEL); ++ if (!node) { ++ printk(KERN_ERR "PCI: Failed to allocate memory.\n"); ++ goto err_end; ++ } ++ node->dev = PCI_SLOT(dev->devfn); ++ node->func = PCI_FUNC(dev->devfn); ++ ++ if (!sbdf->child) ++ sbdf->child = node; ++ else { ++ node->child = sbdf->child; ++ sbdf->child = node; ++ } ++ if (!dev->bus) ++ goto err_end; ++ if (!dev->bus->self) ++ break; ++ dev = dev->bus->self; ++ } ++ if (sscanf(dev_name(&dev->dev), "%04x:%02x", &sbdf->seg, &sbdf->bus) != 2) ++ goto err_end; ++ return TRUE; ++ ++err_end: ++ pci_free_sbdf(sbdf); ++ return FALSE; ++} ++ ++/* Does PCI device belong to sub tree specified by guestdev with sbdf? */ ++typedef int (*pci_sbdf_match_t)(const struct guestdev *gdev, ++ const struct pci_dev *dev); ++ ++static int pci_sbdf_match(const struct guestdev *gdev, ++ const struct pci_dev *dev) ++{ ++ int seg, bus; ++ ++ if (sscanf(dev_name(&dev->dev), "%04x:%02x", &seg, &bus) != 2) ++ return FALSE; ++ ++ return gdev->u.sbdf.seg == seg && ++ gdev->u.sbdf.bus == bus && ++ gdev->u.sbdf.dev == PCI_SLOT(dev->devfn) && ++ gdev->u.sbdf.func == PCI_FUNC(dev->devfn); ++} ++ ++static int pci_is_in_sbdf_sub_tree(struct guestdev *gdev, struct pci_dev *dev, ++ pci_sbdf_match_t match) ++{ ++ BUG_ON(!(gdev->flags & GUESTDEV_FLAG_SBDF)); ++ for (;;) { ++ if (match(gdev, dev)) ++ return TRUE; ++ if (!dev->bus || !dev->bus->self) ++ break; ++ dev = dev->bus->self; ++ } ++ return FALSE; ++} ++ ++/* Does PCI device belong to sub tree specified by guestdev parameter? */ ++static int __pci_is_guestdev(struct pci_dev *dev, pci_node_match_t node_match, ++ pci_sbdf_match_t sbdf_match) ++{ ++ struct guestdev *gdev; ++ struct pcidev_sbdf pcidev_sbdf, *sbdf = NULL; ++ struct list_head *head; ++ int result = FALSE; ++ ++ if (!dev) ++ return FALSE; ++ ++ list_for_each(head, &guestdev_list) { ++ gdev = list_entry(head, struct guestdev, root_list); ++ switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) { ++ case GUESTDEV_FLAG_DEVICEPATH: ++ if (sbdf == NULL) { ++ sbdf = &pcidev_sbdf; ++ memset(sbdf, 0 ,sizeof(*sbdf)); ++ if (!pci_get_sbdf_from_pcidev(dev, sbdf)) ++ goto out; ++ } ++ if (pci_is_in_devicepath_sub_tree(gdev, sbdf, ++ node_match)) { ++ result = TRUE; ++ goto out; ++ } ++ break; ++ case GUESTDEV_FLAG_SBDF: ++ if (pci_is_in_sbdf_sub_tree(gdev, dev, sbdf_match)) { ++ result = TRUE; ++ goto out; ++ } ++ break; ++ default: ++ BUG(); ++ } ++ } ++out: ++ if (sbdf) ++ pci_free_sbdf(sbdf); ++ return result; ++} ++ ++int pci_is_guestdev(struct pci_dev *dev) ++{ ++ return __pci_is_guestdev(dev, pci_node_match, pci_sbdf_match); ++} ++EXPORT_SYMBOL_GPL(pci_is_guestdev); ++ ++static int reassign_resources; ++ ++static int __init pci_set_reassign_resources(char *str) ++{ ++ if (str && !strcmp(str, "all")) ++ reassign_resources = -1; ++ else ++ reassign_resources = 1; ++ ++ return 1; ++} ++__setup("reassign_resources", pci_set_reassign_resources); ++ ++int pci_is_guestdev_to_reassign(struct pci_dev *dev) ++{ ++ if (reassign_resources < 0) ++ return TRUE; ++ if (reassign_resources) ++ return pci_is_guestdev(dev); ++ return FALSE; ++} ++ ++#ifdef CONFIG_PCI_IOMULTI ++static int pci_iomul_node_match(const struct devicepath_node *gdev_node, ++ const struct pcidev_sbdf_node *sbdf_node, ++ int options) ++{ ++ return (options & GUESTDEV_OPT_IOMUL) && ++ ((gdev_node->child != NULL && ++ sbdf_node->child != NULL && ++ gdev_node->dev == sbdf_node->dev && ++ gdev_node->func == sbdf_node->func) || ++ (gdev_node->child == NULL && ++ sbdf_node->child == NULL && ++ gdev_node->dev == sbdf_node->dev)); ++} ++ ++static int pci_iomul_sbdf_match(const struct guestdev *gdev, ++ const struct pci_dev *dev) ++{ ++ int seg, bus; ++ ++ if (sscanf(dev_name(&dev->dev), "%04x:%02x", &seg, &bus) != 2) ++ return FALSE; ++ ++ return (gdev->options & GUESTDEV_OPT_IOMUL) && ++ gdev->u.sbdf.seg == seg && ++ gdev->u.sbdf.bus == bus && ++ gdev->u.sbdf.dev == PCI_SLOT(dev->devfn); ++} ++ ++int pci_is_iomuldev(struct pci_dev *dev) ++{ ++ return __pci_is_guestdev(dev, ++ pci_iomul_node_match, pci_iomul_sbdf_match); ++} ++#endif /* CONFIG_PCI_IOMULTI */ ++ ++/* Check whether the devicepath exists under the pci root bus */ ++static int __init pci_check_devicepath_exists( ++ struct guestdev *gdev, struct pci_bus *bus) ++{ ++ struct devicepath_node *node; ++ struct pci_dev *dev; ++ ++ BUG_ON(!(gdev->flags & GUESTDEV_FLAG_DEVICEPATH)); ++ ++ node = gdev->u.devicepath.child; ++ while (node) { ++ if (!bus) ++ return FALSE; ++ dev = pci_get_slot(bus, PCI_DEVFN(node->dev, node->func)); ++ if (!dev) ++ return FALSE; ++ bus = dev->subordinate; ++ node = node->child; ++ pci_dev_put(dev); ++ } ++ return TRUE; ++} ++ ++/* Check whether the guestdev exists in the PCI device tree */ ++static int __init pci_check_guestdev_exists(void) ++{ ++ struct list_head *head; ++ struct guestdev *gdev; ++ int seg, bbn; ++ struct pci_bus *bus; ++ struct pci_dev *dev; ++ char path_str[GUESTDEV_STR_MAX]; ++ ++ list_for_each(head, &guestdev_list) { ++ gdev = list_entry(head, struct guestdev, root_list); ++ switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) { ++ case GUESTDEV_FLAG_DEVICEPATH: ++ if (gdev->u.devicepath.seg == INVALID_SEG || ++ gdev->u.devicepath.bbn == INVALID_BBN) { ++ if (acpi_pci_get_root_seg_bbn( ++ gdev->u.devicepath.hid, ++ gdev->u.devicepath.uid, &seg, &bbn)) { ++ gdev->u.devicepath.seg = seg; ++ gdev->u.devicepath.bbn = bbn; ++ } else { ++ pci_make_guestdev_str(gdev, ++ path_str, GUESTDEV_STR_MAX); ++ printk(KERN_INFO ++ "PCI: Device does not exist. %s\n", ++ path_str); ++ continue; ++ } ++ } ++ ++ bus = pci_find_bus(gdev->u.devicepath.seg, ++ gdev->u.devicepath.bbn); ++ if (!bus || ++ !pci_check_devicepath_exists(gdev, bus)) { ++ pci_make_guestdev_str(gdev, path_str, ++ GUESTDEV_STR_MAX); ++ printk(KERN_INFO ++ "PCI: Device does not exist. %s\n", ++ path_str); ++ } ++ break; ++ case GUESTDEV_FLAG_SBDF: ++ bus = pci_find_bus(gdev->u.sbdf.seg, gdev->u.sbdf.bus); ++ if (bus) { ++ dev = pci_get_slot(bus, ++ PCI_DEVFN(gdev->u.sbdf.dev, ++ gdev->u.sbdf.func)); ++ if (dev) { ++ pci_dev_put(dev); ++ continue; ++ } ++ } ++ pci_make_guestdev_str(gdev, path_str, GUESTDEV_STR_MAX); ++ printk(KERN_INFO "PCI: Device does not exist. %s\n", ++ path_str); ++ break; ++ default: ++ BUG(); ++ } ++ } ++ return 0; ++} ++ ++fs_initcall(pci_check_guestdev_exists); ++ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/drivers/pci/iomulti.c 2010-03-24 13:55:21.000000000 +0100 +@@ -0,0 +1,1415 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ * ++ * Copyright (c) 2009 Isaku Yamahata ++ * VA Linux Systems Japan K.K. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "pci.h" ++#include "iomulti.h" ++ ++#define PCI_NUM_BARS 6 ++#define PCI_BUS_MAX 255 ++#define PCI_DEV_MAX 31 ++#define PCI_FUNC_MAX 7 ++#define PCI_NUM_FUNC 8 ++ ++/* see pci_resource_len */ ++static inline resource_size_t pci_iomul_len(const struct resource* r) ++{ ++ if (r->start == 0 && r->start == r->end) ++ return 0; ++ return r->end - r->start + 1; ++} ++ ++#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) ++/* stolen from pbus_size_io() */ ++static unsigned long pdev_size_io(struct pci_dev *pdev) ++{ ++ unsigned long size = 0, size1 = 0; ++ int i; ++ ++ for (i = 0; i < PCI_NUM_RESOURCES; i++) { ++ struct resource *r = &pdev->resource[i]; ++ unsigned long r_size; ++ ++ if (!(r->flags & IORESOURCE_IO)) ++ continue; ++ ++ r_size = r->end - r->start + 1; ++ ++ if (r_size < 0x400) ++ /* Might be re-aligned for ISA */ ++ size += r_size; ++ else ++ size1 += r_size; ++ } ++ ++/* To be fixed in 2.5: we should have sort of HAVE_ISA ++ flag in the struct pci_bus. */ ++#if defined(CONFIG_ISA) || defined(CONFIG_EISA) ++ size = (size & 0xff) + ((size & ~0xffUL) << 2); ++#endif ++ size = ROUND_UP(size + size1, 4096); ++ return size; ++} ++ ++/* ++ * primary bus number of PCI-PCI bridge in switch on which ++ * this slots sits. ++ * i.e. the primary bus number of PCI-PCI bridge of downstream port ++ * or root port in switch. ++ * the secondary bus number of PCI-PCI bridge of upstream port ++ * in switch. ++ */ ++static inline unsigned char pci_dev_switch_busnr(struct pci_dev *pdev) ++{ ++ if (pci_find_capability(pdev, PCI_CAP_ID_EXP)) ++ return pdev->bus->primary; ++ return pdev->bus->number; ++} ++ ++struct pci_iomul_func { ++ int segment; ++ uint8_t bus; ++ uint8_t devfn; ++ ++ /* only start and end are used */ ++ unsigned long io_size; ++ uint8_t io_bar; ++ struct resource resource[PCI_NUM_BARS]; ++ struct resource dummy_parent; ++}; ++ ++struct pci_iomul_switch { ++ struct list_head list; /* bus_list_lock protects */ ++ ++ /* ++ * This lock the following entry and following ++ * pci_iomul_slot/pci_iomul_func. ++ */ ++ struct mutex lock; ++ struct kref kref; ++ ++ struct resource io_resource; ++ struct resource *io_region; ++ unsigned int count; ++ struct pci_dev *current_pdev; ++ ++ int segment; ++ uint8_t bus; ++ ++ uint32_t io_base; ++ uint32_t io_limit; ++ ++ /* func which has the largeset io size*/ ++ struct pci_iomul_func *func; ++ ++ struct list_head slots; ++}; ++ ++struct pci_iomul_slot { ++ struct list_head sibling; ++ struct kref kref; ++ /* ++ * busnr ++ * when pcie, the primary busnr of the PCI-PCI bridge on which ++ * this devices sits. ++ */ ++ uint8_t switch_busnr; ++ struct resource dummy_parent[PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES]; ++ ++ /* device */ ++ int segment; ++ uint8_t bus; ++ uint8_t dev; ++ ++ struct pci_iomul_func *func[PCI_NUM_FUNC]; ++}; ++ ++static LIST_HEAD(switch_list); ++static DEFINE_MUTEX(switch_list_lock); ++ ++/*****************************************************************************/ ++static int inline pci_iomul_switch_io_allocated( ++ const struct pci_iomul_switch *sw) ++{ ++ return !(sw->io_base == 0 || sw->io_base > sw->io_limit); ++} ++ ++static struct pci_iomul_switch *pci_iomul_find_switch_locked(int segment, ++ uint8_t bus) ++{ ++ struct pci_iomul_switch *sw; ++ ++ BUG_ON(!mutex_is_locked(&switch_list_lock)); ++ list_for_each_entry(sw, &switch_list, list) { ++ if (sw->segment == segment && sw->bus == bus) ++ return sw; ++ } ++ return NULL; ++} ++ ++static struct pci_iomul_slot *pci_iomul_find_slot_locked( ++ struct pci_iomul_switch *sw, uint8_t busnr, uint8_t dev) ++{ ++ struct pci_iomul_slot *slot; ++ ++ BUG_ON(!mutex_is_locked(&sw->lock)); ++ list_for_each_entry(slot, &sw->slots, sibling) { ++ if (slot->bus == busnr && slot->dev == dev) ++ return slot; ++ } ++ return NULL; ++} ++ ++static void pci_iomul_switch_get(struct pci_iomul_switch *sw); ++/* on successfull exit, sw->lock is locked for use slot and ++ * refrence count of sw is incremented. ++ */ ++static void pci_iomul_get_lock_switch(struct pci_dev *pdev, ++ struct pci_iomul_switch **swp, ++ struct pci_iomul_slot **slot) ++{ ++ mutex_lock(&switch_list_lock); ++ ++ *swp = pci_iomul_find_switch_locked(pci_domain_nr(pdev->bus), ++ pci_dev_switch_busnr(pdev)); ++ if (*swp == NULL) { ++ *slot = NULL; ++ goto out; ++ } ++ ++ mutex_lock(&(*swp)->lock); ++ *slot = pci_iomul_find_slot_locked(*swp, pdev->bus->number, ++ PCI_SLOT(pdev->devfn)); ++ if (*slot == NULL) { ++ mutex_unlock(&(*swp)->lock); ++ *swp = NULL; ++ } else { ++ pci_iomul_switch_get(*swp); ++ } ++out: ++ mutex_unlock(&switch_list_lock); ++} ++ ++static struct pci_iomul_switch *pci_iomul_switch_alloc(int segment, ++ uint8_t bus) ++{ ++ struct pci_iomul_switch *sw; ++ ++ BUG_ON(!mutex_is_locked(&switch_list_lock)); ++ ++ sw = kmalloc(sizeof(*sw), GFP_KERNEL); ++ ++ mutex_init(&sw->lock); ++ kref_init(&sw->kref); ++ sw->io_region = NULL; ++ sw->count = 0; ++ sw->current_pdev = NULL; ++ sw->segment = segment; ++ sw->bus = bus; ++ sw->io_base = 0; ++ sw->io_limit = 0; ++ sw->func = NULL; ++ INIT_LIST_HEAD(&sw->slots); ++ ++ return sw; ++} ++ ++static void pci_iomul_switch_add_locked(struct pci_iomul_switch *sw) ++{ ++ BUG_ON(!mutex_is_locked(&switch_list_lock)); ++ list_add(&sw->list, &switch_list); ++} ++ ++#ifdef CONFIG_HOTPLUG_PCI ++static void pci_iomul_switch_del_locked(struct pci_iomul_switch *sw) ++{ ++ BUG_ON(!mutex_is_locked(&switch_list_lock)); ++ list_del(&sw->list); ++} ++#endif ++ ++static void pci_iomul_switch_get(struct pci_iomul_switch *sw) ++{ ++ kref_get(&sw->kref); ++} ++ ++static void pci_iomul_switch_release(struct kref *kref) ++{ ++ struct pci_iomul_switch *sw = container_of(kref, ++ struct pci_iomul_switch, ++ kref); ++ kfree(sw); ++} ++ ++static void pci_iomul_switch_put(struct pci_iomul_switch *sw) ++{ ++ kref_put(&sw->kref, &pci_iomul_switch_release); ++} ++ ++static int __devinit pci_iomul_slot_init(struct pci_dev *pdev, ++ struct pci_iomul_slot *slot) ++{ ++ u16 rpcap; ++ u16 cap; ++ ++ rpcap = pci_find_capability(pdev, PCI_CAP_ID_EXP); ++ if (!rpcap) { ++ /* pci device isn't supported */ ++ printk(KERN_INFO ++ "PCI: sharing io port of non PCIe device %s " ++ "isn't supported. ignoring.\n", ++ pci_name(pdev)); ++ return -ENOSYS; ++ } ++ ++ pci_read_config_word(pdev, rpcap + PCI_CAP_FLAGS, &cap); ++ switch ((cap & PCI_EXP_FLAGS_TYPE) >> 4) { ++ case PCI_EXP_TYPE_RC_END: ++ printk(KERN_INFO ++ "PCI: io port sharing of root complex integrated " ++ "endpoint %s isn't supported. ignoring.\n", ++ pci_name(pdev)); ++ return -ENOSYS; ++ case PCI_EXP_TYPE_ENDPOINT: ++ case PCI_EXP_TYPE_LEG_END: ++ break; ++ default: ++ printk(KERN_INFO ++ "PCI: io port sharing of non endpoint %s " ++ "doesn't make sense. ignoring.\n", ++ pci_name(pdev)); ++ return -EINVAL; ++ } ++ ++ kref_init(&slot->kref); ++ slot->switch_busnr = pci_dev_switch_busnr(pdev); ++ slot->segment = pci_domain_nr(pdev->bus); ++ slot->bus = pdev->bus->number; ++ slot->dev = PCI_SLOT(pdev->devfn); ++ ++ return 0; ++} ++ ++static struct pci_iomul_slot *__devinit ++pci_iomul_slot_alloc(struct pci_dev *pdev) ++{ ++ struct pci_iomul_slot *slot; ++ ++ slot = kzalloc(sizeof(*slot), GFP_KERNEL); ++ if (slot == NULL) ++ return NULL; ++ ++ if (pci_iomul_slot_init(pdev, slot) != 0) { ++ kfree(slot); ++ return NULL; ++ } ++ return slot; ++} ++ ++static void pci_iomul_slot_add_locked(struct pci_iomul_switch *sw, ++ struct pci_iomul_slot *slot) ++{ ++ BUG_ON(!mutex_is_locked(&sw->lock)); ++ list_add(&slot->sibling, &sw->slots); ++} ++ ++#ifdef CONFIG_HOTPLUG_PCI ++static void pci_iomul_slot_del_locked(struct pci_iomul_switch *sw, ++ struct pci_iomul_slot *slot) ++{ ++ BUG_ON(!mutex_is_locked(&sw->lock)); ++ list_del(&slot->sibling); ++} ++#endif ++ ++static void pci_iomul_slot_get(struct pci_iomul_slot *slot) ++{ ++ kref_get(&slot->kref); ++} ++ ++static void pci_iomul_slot_release(struct kref *kref) ++{ ++ struct pci_iomul_slot *slot = container_of(kref, struct pci_iomul_slot, ++ kref); ++ kfree(slot); ++} ++ ++static void pci_iomul_slot_put(struct pci_iomul_slot *slot) ++{ ++ kref_put(&slot->kref, &pci_iomul_slot_release); ++} ++ ++/*****************************************************************************/ ++static int pci_get_sbd(const char *str, ++ int *segment__, uint8_t *bus__, uint8_t *dev__) ++{ ++ int segment; ++ int bus; ++ int dev; ++ ++ if (sscanf(str, "%x:%x:%x", &segment, &bus, &dev) != 3) { ++ if (sscanf(str, "%x:%x", &bus, &dev) == 2) ++ segment = 0; ++ else ++ return -EINVAL; ++ } ++ ++ if (segment < 0 || INT_MAX <= segment) ++ return -EINVAL; ++ if (bus < 0 || PCI_BUS_MAX < bus) ++ return -EINVAL; ++ if (dev < 0 || PCI_DEV_MAX < dev) ++ return -EINVAL; ++ ++ *segment__ = segment; ++ *bus__ = bus; ++ *dev__ = dev; ++ return 0; ++} ++ ++static char iomul_param[COMMAND_LINE_SIZE]; ++#define TOKEN_MAX 10 /* SSSS:BB:DD length is 10 */ ++static int pci_is_iomul_dev_param(struct pci_dev *pdev) ++{ ++ int len; ++ char *p; ++ char *next_str; ++ ++ for (p = &iomul_param[0]; *p != '\0'; p = next_str + 1) { ++ next_str = strchr(p, ','); ++ if (next_str != NULL) ++ len = next_str - p; ++ else ++ len = strlen(p); ++ ++ if (len > 0 && len <= TOKEN_MAX) { ++ char tmp[TOKEN_MAX+1]; ++ int seg; ++ uint8_t bus; ++ uint8_t dev; ++ ++ strlcpy(tmp, p, len); ++ if (pci_get_sbd(tmp, &seg, &bus, &dev) == 0 && ++ pci_domain_nr(pdev->bus) == seg && ++ pdev->bus->number == bus && ++ PCI_SLOT(pdev->devfn) == dev) ++ return 1; ++ } ++ if (next_str == NULL) ++ break; ++ } ++ ++ /* check guestcev=+iomul option */ ++ return pci_is_iomuldev(pdev); ++} ++ ++/* ++ * Format: [:]:[,[:]:[,...] ++ */ ++static int __init pci_iomul_param_setup(char *str) ++{ ++ if (strlen(str) >= COMMAND_LINE_SIZE) ++ return 0; ++ ++ /* parse it after pci bus scanning */ ++ strlcpy(iomul_param, str, sizeof(iomul_param)); ++ return 1; ++} ++__setup("guestiomuldev=", pci_iomul_param_setup); ++ ++/*****************************************************************************/ ++static void __devinit pci_iomul_set_bridge_io_window(struct pci_dev *bridge, ++ uint32_t io_base, ++ uint32_t io_limit) ++{ ++ uint16_t l; ++ uint32_t upper16; ++ ++ io_base >>= 12; ++ io_base <<= 4; ++ io_limit >>= 12; ++ io_limit <<= 4; ++ l = (io_base & 0xff) | ((io_limit & 0xff) << 8); ++ upper16 = ((io_base & 0xffff00) >> 8) | ++ (((io_limit & 0xffff00) >> 8) << 16); ++ ++ /* Temporarily disable the I/O range before updating PCI_IO_BASE. */ ++ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); ++ /* Update lower 16 bits of I/O base/limit. */ ++ pci_write_config_word(bridge, PCI_IO_BASE, l); ++ /* Update upper 16 bits of I/O base/limit. */ ++ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, upper16); ++} ++ ++static void __devinit pci_disable_bridge_io_window(struct pci_dev *bridge) ++{ ++ /* set base = 0xffffff limit = 0x0 */ ++ pci_iomul_set_bridge_io_window(bridge, 0xffffff, 0); ++} ++ ++static int __devinit pci_iomul_func_scan(struct pci_dev *pdev, ++ struct pci_iomul_slot *slot, ++ uint8_t func) ++{ ++ struct pci_iomul_func *f; ++ unsigned int i; ++ ++ f = kzalloc(sizeof(*f), GFP_KERNEL); ++ if (f == NULL) ++ return -ENOMEM; ++ ++ f->segment = slot->segment; ++ f->bus = slot->bus; ++ f->devfn = PCI_DEVFN(slot->dev, func); ++ f->io_size = pdev_size_io(pdev); ++ ++ for (i = 0; i < PCI_NUM_BARS; i++) { ++ if (!(pci_resource_flags(pdev, i) & IORESOURCE_IO)) ++ continue; ++ if (pci_resource_len(pdev, i) == 0) ++ continue; ++ ++ f->io_bar |= 1 << i; ++ f->resource[i] = pdev->resource[i]; ++ } ++ ++ if (f->io_bar) ++ slot->func[func] = f; ++ else ++ kfree(f); ++ return 0; ++} ++ ++/* ++ * This is tricky part. ++ * fake PCI resource assignment routines by setting flags to 0. ++ * PCI resource allocate routines think the resource should ++ * be allocated by checking flags. 0 means this resource isn't used. ++ * See pbus_size_io() and pdev_sort_resources(). ++ * ++ * After allocated resources, flags (IORESOURCE_IO) is exported ++ * to other part including user process. ++ * So we have to set flags to IORESOURCE_IO, but at the same time ++ * we must prevent those resources from reassigning when pci hot plug. ++ * To achieve that, set r->parent to dummy resource. ++ */ ++static void __devinit pci_iomul_disable_resource(struct resource *r) ++{ ++ /* don't allocate this resource */ ++ r->flags = 0; ++} ++ ++static void __devinit pci_iomul_reenable_resource( ++ struct resource *dummy_parent, struct resource *r) ++{ ++ int ret; ++ ++ dummy_parent->start = r->start; ++ dummy_parent->end = r->end; ++ dummy_parent->flags = r->flags; ++ dummy_parent->name = "PCI IOMUL dummy resource"; ++ ++ ret = request_resource(dummy_parent, r); ++ BUG_ON(ret); ++} ++ ++static void __devinit pci_iomul_fixup_ioresource(struct pci_dev *pdev, ++ struct pci_iomul_func *func, ++ int reassign, int dealloc) ++{ ++ uint8_t i; ++ struct resource *r; ++ ++ printk(KERN_INFO "PCI: deallocating io resource[%s]. io size 0x%lx\n", ++ pci_name(pdev), func->io_size); ++ for (i = 0; i < PCI_NUM_BARS; i++) { ++ r = &pdev->resource[i]; ++ if (!(func->io_bar & (1 << i))) ++ continue; ++ ++ if (reassign) { ++ r->end -= r->start; ++ r->start = 0; ++ pci_update_resource(pdev, i); ++ func->resource[i] = *r; ++ } ++ ++ if (dealloc) ++ /* don't allocate this resource */ ++ pci_iomul_disable_resource(r); ++ } ++ ++ /* parent PCI-PCI bridge */ ++ if (!reassign) ++ return; ++ pdev = pdev->bus->self; ++ if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST) ++ return; ++ pci_disable_bridge_io_window(pdev); ++ for (i = 0; i < PCI_NUM_RESOURCES; i++) { ++ r = &pdev->resource[i]; ++ if (!(r->flags & IORESOURCE_IO)) ++ continue; ++ ++ r->end -= r->start; ++ r->start = 0; ++ if (i < PCI_BRIDGE_RESOURCES) ++ pci_update_resource(pdev, i); ++ } ++} ++ ++static void __devinit __quirk_iomul_dealloc_ioresource( ++ struct pci_iomul_switch *sw, ++ struct pci_dev *pdev, struct pci_iomul_slot *slot) ++{ ++ struct pci_iomul_func *f; ++ struct pci_iomul_func *__f; ++ ++ if (pci_iomul_func_scan(pdev, slot, PCI_FUNC(pdev->devfn)) != 0) ++ return; ++ ++ f = slot->func[PCI_FUNC(pdev->devfn)]; ++ if (f == NULL) ++ return; ++ ++ __f = sw->func; ++ /* sw->io_base == 0 means that we are called at boot time. ++ * != 0 means that we are called by php after boot. */ ++ if (sw->io_base == 0 && ++ (__f == NULL || __f->io_size < f->io_size)) { ++ if (__f != NULL) { ++ struct pci_bus *__pbus; ++ struct pci_dev *__pdev; ++ ++ __pbus = pci_find_bus(__f->segment, __f->bus); ++ BUG_ON(__pbus == NULL); ++ __pdev = pci_get_slot(__pbus, __f->devfn); ++ BUG_ON(__pdev == NULL); ++ pci_iomul_fixup_ioresource(__pdev, __f, 0, 1); ++ pci_dev_put(__pdev); ++ } ++ ++ pci_iomul_fixup_ioresource(pdev, f, 1, 0); ++ sw->func = f; ++ } else { ++ pci_iomul_fixup_ioresource(pdev, f, 1, 1); ++ } ++} ++ ++static void __devinit quirk_iomul_dealloc_ioresource(struct pci_dev *pdev) ++{ ++ struct pci_iomul_switch *sw; ++ struct pci_iomul_slot *slot; ++ ++ if (pdev->hdr_type != PCI_HEADER_TYPE_NORMAL) ++ return; ++ if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST) ++ return; /* PCI Host Bridge isn't a target device */ ++ if (!pci_is_iomul_dev_param(pdev)) ++ return; ++ ++ mutex_lock(&switch_list_lock); ++ sw = pci_iomul_find_switch_locked(pci_domain_nr(pdev->bus), ++ pci_dev_switch_busnr(pdev)); ++ if (sw == NULL) { ++ sw = pci_iomul_switch_alloc(pci_domain_nr(pdev->bus), ++ pci_dev_switch_busnr(pdev)); ++ if (sw == NULL) { ++ mutex_unlock(&switch_list_lock); ++ printk(KERN_WARNING ++ "PCI: can't allocate memory " ++ "for sw of IO mulplexing %s", pci_name(pdev)); ++ return; ++ } ++ pci_iomul_switch_add_locked(sw); ++ } ++ pci_iomul_switch_get(sw); ++ mutex_unlock(&switch_list_lock); ++ ++ mutex_lock(&sw->lock); ++ slot = pci_iomul_find_slot_locked(sw, pdev->bus->number, ++ PCI_SLOT(pdev->devfn)); ++ if (slot == NULL) { ++ slot = pci_iomul_slot_alloc(pdev); ++ if (slot == NULL) { ++ mutex_unlock(&sw->lock); ++ pci_iomul_switch_put(sw); ++ printk(KERN_WARNING "PCI: can't allocate memory " ++ "for IO mulplexing %s", pci_name(pdev)); ++ return; ++ } ++ pci_iomul_slot_add_locked(sw, slot); ++ } ++ ++ printk(KERN_INFO "PCI: disable device and release io resource[%s].\n", ++ pci_name(pdev)); ++ pci_disable_device(pdev); ++ ++ __quirk_iomul_dealloc_ioresource(sw, pdev, slot); ++ ++ mutex_unlock(&sw->lock); ++ pci_iomul_switch_put(sw); ++} ++DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, ++ quirk_iomul_dealloc_ioresource); ++ ++static void __devinit pci_iomul_read_bridge_io(struct pci_iomul_switch *sw) ++{ ++ struct pci_iomul_func *f = sw->func; ++ ++ struct pci_bus *pbus; ++ struct pci_dev *pdev; ++ struct pci_dev *bridge; ++ ++ uint16_t l; ++ uint16_t base_upper16; ++ uint16_t limit_upper16; ++ uint32_t io_base; ++ uint32_t io_limit; ++ ++ pbus = pci_find_bus(f->segment, f->bus); ++ BUG_ON(pbus == NULL); ++ ++ pdev = pci_get_slot(pbus, f->devfn); ++ BUG_ON(pdev == NULL); ++ ++ bridge = pdev->bus->self; ++ pci_read_config_word(bridge, PCI_IO_BASE, &l); ++ pci_read_config_word(bridge, PCI_IO_BASE_UPPER16, &base_upper16); ++ pci_read_config_word(bridge, PCI_IO_LIMIT_UPPER16, &limit_upper16); ++ ++ io_base = (l & 0xf0) | ((uint32_t)base_upper16 << 8); ++ io_base <<= 8; ++ io_limit = (l >> 8) | ((uint32_t)limit_upper16 << 8); ++ io_limit <<= 8; ++ io_limit |= 0xfff; ++ ++ sw->io_base = io_base; ++ sw->io_limit = io_limit; ++ ++ pci_dev_put(pdev); ++ printk(KERN_INFO "PCI: bridge %s base 0x%x limit 0x%x\n", ++ pci_name(bridge), sw->io_base, sw->io_limit); ++} ++ ++static void __devinit pci_iomul_setup_brige(struct pci_dev *bridge, ++ uint32_t io_base, ++ uint32_t io_limit) ++{ ++ uint16_t cmd; ++ ++ if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_HOST) ++ return; ++ ++ pci_iomul_set_bridge_io_window(bridge, io_base, io_limit); ++ ++ /* and forcibly enables IO */ ++ pci_read_config_word(bridge, PCI_COMMAND, &cmd); ++ if (!(cmd & PCI_COMMAND_IO)) { ++ cmd |= PCI_COMMAND_IO; ++ printk(KERN_INFO "PCI: Forcibly Enabling IO %s\n", ++ pci_name(bridge)); ++ pci_write_config_word(bridge, PCI_COMMAND, cmd); ++ } ++} ++ ++struct __bar { ++ unsigned long size; ++ uint8_t bar; ++}; ++ ++/* decending order */ ++static int __devinit pci_iomul_bar_cmp(const void *lhs__, const void *rhs__) ++{ ++ const struct __bar *lhs = (struct __bar*)lhs__; ++ const struct __bar *rhs = (struct __bar*)rhs__; ++ return - (lhs->size - rhs->size); ++} ++ ++static void __devinit pci_iomul_setup_dev(struct pci_dev *pdev, ++ struct pci_iomul_func *f, ++ uint32_t io_base) ++{ ++ struct __bar bars[PCI_NUM_BARS]; ++ int i; ++ uint8_t num_bars = 0; ++ struct resource *r; ++ ++ printk(KERN_INFO "PCI: Forcibly assign IO %s from 0x%x\n", ++ pci_name(pdev), io_base); ++ ++ for (i = 0; i < PCI_NUM_BARS; i++) { ++ if (!(f->io_bar & (1 << i))) ++ continue; ++ ++ r = &f->resource[i]; ++ bars[num_bars].size = pci_iomul_len(r); ++ bars[num_bars].bar = i; ++ ++ num_bars++; ++ } ++ ++ sort(bars, num_bars, sizeof(bars[0]), &pci_iomul_bar_cmp, NULL); ++ ++ for (i = 0; i < num_bars; i++) { ++ struct resource *fr = &f->resource[bars[i].bar]; ++ r = &pdev->resource[bars[i].bar]; ++ ++ BUG_ON(r->start != 0); ++ r->start += io_base; ++ r->end += io_base; ++ ++ fr->start = r->start; ++ fr->end = r->end; ++ ++ /* pci_update_resource() check flags. */ ++ r->flags = fr->flags; ++ pci_update_resource(pdev, bars[i].bar); ++ pci_iomul_reenable_resource(&f->dummy_parent, r); ++ ++ io_base += bars[i].size; ++ } ++} ++ ++static void __devinit pci_iomul_release_io_resource( ++ struct pci_dev *pdev, struct pci_iomul_switch *sw, ++ struct pci_iomul_slot *slot, struct pci_iomul_func *f) ++{ ++ int i; ++ struct resource *r; ++ ++ for (i = 0; i < PCI_NUM_BARS; i++) { ++ if (pci_resource_flags(pdev, i) & IORESOURCE_IO && ++ pdev->resource[i].parent != NULL) { ++ r = &pdev->resource[i]; ++ f->resource[i] = *r; ++ release_resource(r); ++ pci_iomul_reenable_resource(&f->dummy_parent, r); ++ } ++ } ++ ++ /* parent PCI-PCI bridge */ ++ pdev = pdev->bus->self; ++ if ((pdev->class >> 8) != PCI_CLASS_BRIDGE_HOST) { ++ for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { ++ struct resource *parent = pdev->resource[i].parent; ++ ++ if (pci_resource_flags(pdev, i) & IORESOURCE_IO && ++ parent != NULL) { ++ r = &pdev->resource[i]; ++ ++ sw->io_resource.flags = r->flags; ++ sw->io_resource.start = sw->io_base; ++ sw->io_resource.end = sw->io_limit; ++ sw->io_resource.name = "PCI IO Multiplexer"; ++ ++ release_resource(r); ++ pci_iomul_reenable_resource( ++ &slot->dummy_parent[i - PCI_BRIDGE_RESOURCES], r); ++ ++ if (request_resource(parent, ++ &sw->io_resource)) ++ printk(KERN_ERR ++ "PCI IOMul: can't allocate " ++ "resource. [0x%x, 0x%x]", ++ sw->io_base, sw->io_limit); ++ } ++ } ++ } ++} ++ ++static void __devinit quirk_iomul_reassign_ioresource(struct pci_dev *pdev) ++{ ++ struct pci_iomul_switch *sw; ++ struct pci_iomul_slot *slot; ++ struct pci_iomul_func *sf; ++ struct pci_iomul_func *f; ++ ++ pci_iomul_get_lock_switch(pdev, &sw, &slot); ++ if (sw == NULL || slot == NULL) ++ return; ++ ++ if (sw->io_base == 0) ++ pci_iomul_read_bridge_io(sw); ++ if (!pci_iomul_switch_io_allocated(sw)) ++ goto out; ++ ++ sf = sw->func; ++ f = slot->func[PCI_FUNC(pdev->devfn)]; ++ if (f == NULL) ++ /* (sf == NULL || f == NULL) case ++ * can happen when all the specified devices ++ * don't have io space ++ */ ++ goto out; ++ ++ if (sf != NULL && ++ (pci_domain_nr(pdev->bus) != sf->segment || ++ pdev->bus->number != sf->bus || ++ PCI_SLOT(pdev->devfn) != PCI_SLOT(sf->devfn)) && ++ PCI_FUNC(pdev->devfn) == 0) { ++ pci_iomul_setup_brige(pdev->bus->self, ++ sw->io_base, sw->io_limit); ++ } ++ ++ BUG_ON(f->io_size > sw->io_limit - sw->io_base + 1); ++ if (/* f == sf */ ++ sf != NULL && ++ pci_domain_nr(pdev->bus) == sf->segment && ++ pdev->bus->number == sf->bus && ++ pdev->devfn == sf->devfn) ++ pci_iomul_release_io_resource(pdev, sw, slot, f); ++ else ++ pci_iomul_setup_dev(pdev, f, sw->io_base); ++ ++out: ++ mutex_unlock(&sw->lock); ++ pci_iomul_switch_put(sw); ++} ++ ++DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, ++ quirk_iomul_reassign_ioresource); ++ ++/*****************************************************************************/ ++#ifdef CONFIG_HOTPLUG_PCI ++static int __devinit __pci_iomul_notifier_del_device(struct pci_dev *pdev) ++{ ++ struct pci_iomul_switch *sw; ++ struct pci_iomul_slot *slot; ++ int i; ++ ++ pci_iomul_get_lock_switch(pdev, &sw, &slot); ++ if (sw == NULL || slot == NULL) ++ return 0; ++ ++ if (sw->func == slot->func[PCI_FUNC(pdev->devfn)]) ++ sw->func = NULL; ++ kfree(slot->func[PCI_FUNC(pdev->devfn)]); ++ slot->func[PCI_FUNC(pdev->devfn)] = NULL; ++ for (i = 0; i < PCI_NUM_FUNC; i++) { ++ if (slot->func[i] != NULL) ++ goto out; ++ } ++ ++ pci_iomul_slot_del_locked(sw, slot); ++ pci_iomul_slot_put(slot); ++ ++out: ++ mutex_unlock(&sw->lock); ++ pci_iomul_switch_put(sw); ++ return 0; ++} ++ ++static int __devinit __pci_iomul_notifier_del_switch(struct pci_dev *pdev) ++{ ++ struct pci_iomul_switch *sw; ++ ++ mutex_lock(&switch_list_lock); ++ sw = pci_iomul_find_switch_locked(pci_domain_nr(pdev->bus), ++ pdev->bus->number); ++ if (sw == NULL) ++ goto out; ++ ++ pci_iomul_switch_del_locked(sw); ++ ++ mutex_lock(&sw->lock); ++ if (sw->io_resource.parent) ++ release_resource(&sw->io_resource); ++ sw->io_base = 0; /* to tell this switch is removed */ ++ sw->io_limit = 0; ++ BUG_ON(!list_empty(&sw->slots)); ++ mutex_unlock(&sw->lock); ++ ++out: ++ mutex_unlock(&switch_list_lock); ++ pci_iomul_switch_put(sw); ++ return 0; ++} ++ ++static int __devinit pci_iomul_notifier_del_device(struct pci_dev *pdev) ++{ ++ int ret; ++ switch (pdev->hdr_type) { ++ case PCI_HEADER_TYPE_NORMAL: ++ ret = __pci_iomul_notifier_del_device(pdev); ++ break; ++ case PCI_HEADER_TYPE_BRIDGE: ++ ret = __pci_iomul_notifier_del_switch(pdev); ++ break; ++ default: ++ printk(KERN_WARNING "PCI IOMUL: " ++ "device %s has unknown header type %02x, ignoring.\n", ++ pci_name(pdev), pdev->hdr_type); ++ ret = -EIO; ++ break; ++ } ++ return ret; ++} ++ ++static int __devinit pci_iomul_notifier(struct notifier_block *nb, ++ unsigned long action, void *data) ++{ ++ struct device *dev = data; ++ struct pci_dev *pdev = to_pci_dev(dev); ++ ++ switch (action) { ++ case BUS_NOTIFY_ADD_DEVICE: ++ quirk_iomul_reassign_ioresource(pdev); ++ break; ++ case BUS_NOTIFY_DEL_DEVICE: ++ return pci_iomul_notifier_del_device(pdev); ++ default: ++ /* nothing */ ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block pci_iomul_nb = { ++ .notifier_call = pci_iomul_notifier, ++}; ++ ++static int __init pci_iomul_hotplug_init(void) ++{ ++ bus_register_notifier(&pci_bus_type, &pci_iomul_nb); ++ return 0; ++} ++ ++late_initcall(pci_iomul_hotplug_init); ++#endif ++ ++/*****************************************************************************/ ++struct pci_iomul_data { ++ struct mutex lock; ++ ++ struct pci_dev *pdev; ++ struct pci_iomul_switch *sw; ++ struct pci_iomul_slot *slot; /* slot::kref */ ++ struct pci_iomul_func **func; /* when dereferencing, ++ sw->lock is necessary */ ++}; ++ ++static int pci_iomul_func_ioport(struct pci_iomul_func *func, ++ uint8_t bar, uint64_t offset, int *port) ++{ ++ if (!(func->io_bar & (1 << bar))) ++ return -EINVAL; ++ ++ *port = func->resource[bar].start + offset; ++ if (*port < func->resource[bar].start || ++ *port > func->resource[bar].end) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static inline int pci_iomul_valid(struct pci_iomul_data *iomul) ++{ ++ BUG_ON(!mutex_is_locked(&iomul->lock)); ++ BUG_ON(!mutex_is_locked(&iomul->sw->lock)); ++ return pci_iomul_switch_io_allocated(iomul->sw) && ++ *iomul->func != NULL; ++} ++ ++static void __pci_iomul_enable_io(struct pci_dev *pdev) ++{ ++ uint16_t cmd; ++ ++ pci_dev_get(pdev); ++ pci_read_config_word(pdev, PCI_COMMAND, &cmd); ++ cmd |= PCI_COMMAND_IO; ++ pci_write_config_word(pdev, PCI_COMMAND, cmd); ++} ++ ++static void __pci_iomul_disable_io(struct pci_iomul_data *iomul, ++ struct pci_dev *pdev) ++{ ++ uint16_t cmd; ++ ++ if (!pci_iomul_valid(iomul)) ++ return; ++ ++ pci_read_config_word(pdev, PCI_COMMAND, &cmd); ++ cmd &= ~PCI_COMMAND_IO; ++ pci_write_config_word(pdev, PCI_COMMAND, cmd); ++ pci_dev_put(pdev); ++} ++ ++static int pci_iomul_open(struct inode *inode, struct file *filp) ++{ ++ struct pci_iomul_data *iomul; ++ iomul = kmalloc(sizeof(*iomul), GFP_KERNEL); ++ if (iomul == NULL) ++ return -ENOMEM; ++ ++ mutex_init(&iomul->lock); ++ iomul->pdev = NULL; ++ iomul->sw = NULL; ++ iomul->slot = NULL; ++ iomul->func = NULL; ++ filp->private_data = (void*)iomul; ++ ++ return 0; ++} ++ ++static int pci_iomul_release(struct inode *inode, struct file *filp) ++{ ++ struct pci_iomul_data *iomul = ++ (struct pci_iomul_data*)filp->private_data; ++ struct pci_iomul_switch *sw; ++ struct pci_iomul_slot *slot = NULL; ++ ++ mutex_lock(&iomul->lock); ++ sw = iomul->sw; ++ slot = iomul->slot; ++ if (iomul->pdev != NULL) { ++ if (sw != NULL) { ++ mutex_lock(&sw->lock); ++ if (sw->current_pdev == iomul->pdev) { ++ __pci_iomul_disable_io(iomul, ++ sw->current_pdev); ++ sw->current_pdev = NULL; ++ } ++ sw->count--; ++ if (sw->count == 0) { ++ release_region(sw->io_region->start, sw->io_region->end - sw->io_region->start + 1); ++ sw->io_region = NULL; ++ } ++ mutex_unlock(&sw->lock); ++ } ++ pci_dev_put(iomul->pdev); ++ } ++ mutex_unlock(&iomul->lock); ++ ++ if (slot != NULL) ++ pci_iomul_slot_put(slot); ++ if (sw != NULL) ++ pci_iomul_switch_put(sw); ++ kfree(iomul); ++ return 0; ++} ++ ++static long pci_iomul_setup(struct pci_iomul_data *iomul, ++ struct pci_iomul_setup __user *arg) ++{ ++ long error = 0; ++ struct pci_iomul_setup setup; ++ struct pci_iomul_switch *sw = NULL; ++ struct pci_iomul_slot *slot; ++ struct pci_bus *pbus; ++ struct pci_dev *pdev; ++ ++ if (copy_from_user(&setup, arg, sizeof(setup))) ++ return -EFAULT; ++ ++ pbus = pci_find_bus(setup.segment, setup.bus); ++ if (pbus == NULL) ++ return -ENODEV; ++ pdev = pci_get_slot(pbus, setup.dev); ++ if (pdev == NULL) ++ return -ENODEV; ++ ++ mutex_lock(&iomul->lock); ++ if (iomul->sw != NULL) { ++ error = -EBUSY; ++ goto out0; ++ } ++ ++ pci_iomul_get_lock_switch(pdev, &sw, &slot); ++ if (sw == NULL || slot == NULL) { ++ error = -ENODEV; ++ goto out0; ++ } ++ if (!pci_iomul_switch_io_allocated(sw)) { ++ error = -ENODEV; ++ goto out; ++ } ++ ++ if (slot->func[setup.func] == NULL) { ++ error = -ENODEV; ++ goto out; ++ } ++ ++ if (sw->count == 0) { ++ BUG_ON(sw->io_region != NULL); ++ sw->io_region = ++ request_region(sw->io_base, ++ sw->io_limit - sw->io_base + 1, ++ "PCI IO Multiplexer driver"); ++ if (sw->io_region == NULL) { ++ mutex_unlock(&sw->lock); ++ error = -EBUSY; ++ goto out; ++ } ++ } ++ sw->count++; ++ pci_iomul_slot_get(slot); ++ ++ iomul->pdev = pdev; ++ iomul->sw = sw; ++ iomul->slot = slot; ++ iomul->func = &slot->func[setup.func]; ++ ++out: ++ mutex_unlock(&sw->lock); ++out0: ++ mutex_unlock(&iomul->lock); ++ if (error != 0) { ++ if (sw != NULL) ++ pci_iomul_switch_put(sw); ++ pci_dev_put(pdev); ++ } ++ return error; ++} ++ ++static int pci_iomul_lock(struct pci_iomul_data *iomul, ++ struct pci_iomul_switch **sw, ++ struct pci_iomul_func **func) ++{ ++ mutex_lock(&iomul->lock); ++ *sw = iomul->sw; ++ if (*sw == NULL) { ++ mutex_unlock(&iomul->lock); ++ return -ENODEV; ++ } ++ mutex_lock(&(*sw)->lock); ++ if (!pci_iomul_valid(iomul)) { ++ mutex_unlock(&(*sw)->lock); ++ mutex_unlock(&iomul->lock); ++ return -ENODEV; ++ } ++ *func = *iomul->func; ++ ++ return 0; ++} ++ ++static long pci_iomul_disable_io(struct pci_iomul_data *iomul) ++{ ++ long error = 0; ++ struct pci_iomul_switch *sw; ++ struct pci_iomul_func *dummy_func; ++ struct pci_dev *pdev; ++ ++ if (pci_iomul_lock(iomul, &sw, &dummy_func) < 0) ++ return -ENODEV; ++ ++ pdev = iomul->pdev; ++ if (pdev == NULL) ++ error = -ENODEV; ++ ++ if (pdev != NULL && sw->current_pdev == pdev) { ++ __pci_iomul_disable_io(iomul, pdev); ++ sw->current_pdev = NULL; ++ } ++ ++ mutex_unlock(&sw->lock); ++ mutex_unlock(&iomul->lock); ++ return error; ++} ++ ++static void pci_iomul_switch_to( ++ struct pci_iomul_data *iomul, struct pci_iomul_switch *sw, ++ struct pci_dev *next_pdev) ++{ ++ if (sw->current_pdev == next_pdev) ++ /* nothing to do */ ++ return; ++ ++ if (sw->current_pdev != NULL) ++ __pci_iomul_disable_io(iomul, sw->current_pdev); ++ ++ __pci_iomul_enable_io(next_pdev); ++ sw->current_pdev = next_pdev; ++} ++ ++static long pci_iomul_in(struct pci_iomul_data *iomul, ++ struct pci_iomul_in __user *arg) ++{ ++ struct pci_iomul_in in; ++ struct pci_iomul_switch *sw; ++ struct pci_iomul_func *func; ++ ++ long error = 0; ++ int port; ++ uint32_t value = 0; ++ ++ if (copy_from_user(&in, arg, sizeof(in))) ++ return -EFAULT; ++ ++ if (pci_iomul_lock(iomul, &sw, &func) < 0) ++ return -ENODEV; ++ ++ error = pci_iomul_func_ioport(func, in.bar, in.offset, &port); ++ if (error) ++ goto out; ++ ++ pci_iomul_switch_to(iomul, sw, iomul->pdev); ++ switch (in.size) { ++ case 4: ++ value = inl(port); ++ break; ++ case 2: ++ value = inw(port); ++ break; ++ case 1: ++ value = inb(port); ++ break; ++ default: ++ error = -EINVAL; ++ break; ++ } ++ ++out: ++ mutex_unlock(&sw->lock); ++ mutex_unlock(&iomul->lock); ++ ++ if (error == 0 && put_user(value, &arg->value)) ++ return -EFAULT; ++ return error; ++} ++ ++static long pci_iomul_out(struct pci_iomul_data *iomul, ++ struct pci_iomul_out __user *arg) ++{ ++ struct pci_iomul_in out; ++ struct pci_iomul_switch *sw; ++ struct pci_iomul_func *func; ++ ++ long error = 0; ++ int port; ++ ++ if (copy_from_user(&out, arg, sizeof(out))) ++ return -EFAULT; ++ ++ if (pci_iomul_lock(iomul, &sw, &func) < 0) ++ return -ENODEV; ++ ++ error = pci_iomul_func_ioport(func, out.bar, out.offset, &port); ++ if (error) ++ goto out; ++ ++ pci_iomul_switch_to(iomul, sw, iomul->pdev); ++ switch (out.size) { ++ case 4: ++ outl(out.value, port); ++ break; ++ case 2: ++ outw(out.value, port); ++ break; ++ case 1: ++ outb(out.value, port); ++ break; ++ default: ++ error = -EINVAL; ++ break; ++ } ++ ++out: ++ mutex_unlock(&sw->lock); ++ mutex_unlock(&iomul->lock); ++ return error; ++} ++ ++static long pci_iomul_ioctl(struct file *filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ long error; ++ struct pci_iomul_data *iomul = ++ (struct pci_iomul_data*)filp->private_data; ++ ++ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ ++ switch (cmd) { ++ case PCI_IOMUL_SETUP: ++ error = pci_iomul_setup(iomul, ++ (struct pci_iomul_setup __user *)arg); ++ break; ++ case PCI_IOMUL_DISABLE_IO: ++ error = pci_iomul_disable_io(iomul); ++ break; ++ case PCI_IOMUL_IN: ++ error = pci_iomul_in(iomul, (struct pci_iomul_in __user *)arg); ++ break; ++ case PCI_IOMUL_OUT: ++ error = pci_iomul_out(iomul, ++ (struct pci_iomul_out __user *)arg); ++ break; ++ default: ++ error = -ENOSYS; ++ break; ++ } ++ ++ return error; ++} ++ ++static const struct file_operations pci_iomul_fops = { ++ .owner = THIS_MODULE, ++ ++ .open = pci_iomul_open, /* nonseekable_open */ ++ .release = pci_iomul_release, ++ ++ .unlocked_ioctl = pci_iomul_ioctl, ++}; ++ ++static struct miscdevice pci_iomul_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "pci_iomul", ++ .fops = &pci_iomul_fops, ++}; ++ ++static int pci_iomul_init(void) ++{ ++ int error; ++ error = misc_register(&pci_iomul_miscdev); ++ if (error != 0) { ++ printk(KERN_ALERT "Couldn't register /dev/misc/pci_iomul"); ++ return error; ++ } ++ printk("PCI IO multiplexer device installed.\n"); ++ return 0; ++} ++ ++#if 0 ++static void pci_iomul_cleanup(void) ++{ ++ misc_deregister(&pci_iomul_miscdev); ++} ++#endif ++ ++/* ++ * This must be called after pci fixup final which is called by ++ * device_initcall(pci_init). ++ */ ++late_initcall(pci_iomul_init); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Isaku Yamahata "); ++MODULE_DESCRIPTION("PCI IO space multiplexing driver"); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/drivers/pci/iomulti.h 2010-03-24 13:55:21.000000000 +0100 +@@ -0,0 +1,51 @@ ++#ifndef PCI_IOMULTI_H ++#define PCI_IOMULTI_H ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ * ++ * Copyright (c) 2009 Isaku Yamahata ++ * VA Linux Systems Japan K.K. ++ * ++ */ ++ ++struct pci_iomul_setup { ++ uint16_t segment; ++ uint8_t bus; ++ uint8_t dev; ++ uint8_t func; ++}; ++ ++struct pci_iomul_in { ++ uint8_t bar; ++ uint64_t offset; ++ ++ uint8_t size; ++ uint32_t value; ++}; ++ ++struct pci_iomul_out { ++ uint8_t bar; ++ uint64_t offset; ++ ++ uint8_t size; ++ uint32_t value; ++}; ++ ++#define PCI_IOMUL_SETUP _IOW ('P', 0, struct pci_iomul_setup) ++#define PCI_IOMUL_DISABLE_IO _IO ('P', 1) ++#define PCI_IOMUL_IN _IOWR('P', 2, struct pci_iomul_in) ++#define PCI_IOMUL_OUT _IOW ('P', 3, struct pci_iomul_out) ++ ++#endif /* PCI_IOMULTI_H */ +--- head-2010-04-29.orig/drivers/pci/pci.c 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/drivers/pci/pci.c 2010-04-29 09:30:41.000000000 +0200 +@@ -2901,6 +2901,13 @@ resource_size_t pci_specified_resource_a + */ + int pci_is_reassigndev(struct pci_dev *dev) + { ++#ifdef CONFIG_PCI_GUESTDEV ++ int result; ++ ++ result = pci_is_guestdev_to_reassign(dev); ++ if (result) ++ return result; ++#endif /* CONFIG_PCI_GUESTDEV */ + return (pci_specified_resource_alignment(dev) != 0); + } + +--- head-2010-04-29.orig/drivers/pci/pci.h 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/drivers/pci/pci.h 2010-03-24 13:55:21.000000000 +0100 +@@ -337,4 +337,11 @@ static inline int pci_dev_specific_reset + } + #endif + ++#ifdef CONFIG_PCI_GUESTDEV ++extern int pci_is_guestdev_to_reassign(struct pci_dev *dev); ++extern int pci_is_iomuldev(struct pci_dev *dev); ++#else ++#define pci_is_iomuldev(dev) 0 ++#endif ++ + #endif /* DRIVERS_PCI_H */ +--- head-2010-04-29.orig/include/linux/acpi.h 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/include/linux/acpi.h 2010-03-24 13:55:21.000000000 +0100 +@@ -247,6 +247,8 @@ int acpi_check_region(resource_size_t st + int acpi_check_mem_region(resource_size_t start, resource_size_t n, + const char *name); + ++int acpi_pci_get_root_seg_bbn(char *hid, char *uid, int *seg, int *bbn); ++ + #ifdef CONFIG_PM_SLEEP + void __init acpi_no_s4_hw_signature(void); + void __init acpi_old_suspend_ordering(void); +--- head-2010-04-29.orig/include/linux/pci.h 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/include/linux/pci.h 2010-03-24 13:55:21.000000000 +0100 +@@ -1504,5 +1504,11 @@ int pci_vpd_find_tag(const u8 *buf, unsi + int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, + unsigned int len, const char *kw); + ++#ifdef CONFIG_PCI_GUESTDEV ++int pci_is_guestdev(struct pci_dev *dev); ++#else ++#define pci_is_guestdev(dev) 0 ++#endif ++ + #endif /* __KERNEL__ */ + #endif /* LINUX_PCI_H */ diff --git a/patches.xen/pci-reserve b/patches.xen/pci-reserve new file mode 100644 index 0000000..4a9cccb --- /dev/null +++ b/patches.xen/pci-reserve @@ -0,0 +1,237 @@ +Subject: linux/pci: reserve io/memory space for bridge +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 1010:10eae161c153) +Patch-mainline: n/a + +reserve io/memory space for bridge which will be used later +by PCI hotplug. + +Signed-off-by: Isaku Yamahata +Acked-by: jbeulich@novell.com + +--- head-2010-04-29.orig/Documentation/kernel-parameters.txt 2010-04-29 09:30:30.000000000 +0200 ++++ head-2010-04-29/Documentation/kernel-parameters.txt 2010-04-29 09:30:50.000000000 +0200 +@@ -2032,6 +2032,13 @@ and is between 256 and 4096 characters. + off: Turn ECRC off + on: Turn ECRC on. + ++ pci_reserve= [PCI] ++ Format: [[+IO][+MEM]][,...] ++ Format of sbdf: [:]:. ++ Specifies the least reserved io size or memory size ++ which is assigned to PCI bridge even when no child ++ pci device exists. This is useful with PCI hotplug. ++ + pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power + Management. + off Disable ASPM. +--- head-2010-04-29.orig/drivers/pci/Kconfig 2010-03-24 13:55:21.000000000 +0100 ++++ head-2010-04-29/drivers/pci/Kconfig 2010-03-24 14:00:05.000000000 +0100 +@@ -45,6 +45,13 @@ config PCI_IOMULTI + help + Say Y here if you need io multiplexing. + ++config PCI_RESERVE ++ bool "PCI IO/MEMORY space reserve" ++ depends on PCI && XEN_PRIVILEGED_GUEST ++ default y ++ help ++ Say Y here if you need PCI IO/MEMORY space reserve ++ + config PCI_STUB + tristate "PCI Stub driver" + depends on PCI +--- head-2010-04-29.orig/drivers/pci/Makefile 2010-03-24 13:55:21.000000000 +0100 ++++ head-2010-04-29/drivers/pci/Makefile 2010-03-24 14:00:05.000000000 +0100 +@@ -9,6 +9,7 @@ obj-$(CONFIG_PROC_FS) += proc.o + obj-$(CONFIG_SYSFS) += slot.o + obj-$(CONFIG_PCI_GUESTDEV) += guestdev.o + obj-$(CONFIG_PCI_IOMULTI) += iomulti.o ++obj-$(CONFIG_PCI_RESERVE) += reserve.o + + obj-$(CONFIG_PCI_QUIRKS) += quirks.o + +--- head-2010-04-29.orig/drivers/pci/pci.h 2010-03-24 13:55:21.000000000 +0100 ++++ head-2010-04-29/drivers/pci/pci.h 2010-03-24 14:00:05.000000000 +0100 +@@ -344,4 +344,19 @@ extern int pci_is_iomuldev(struct pci_de + #define pci_is_iomuldev(dev) 0 + #endif + ++#ifdef CONFIG_PCI_RESERVE ++unsigned long pci_reserve_size_io(struct pci_bus *bus); ++unsigned long pci_reserve_size_mem(struct pci_bus *bus); ++#else ++static inline unsigned long pci_reserve_size_io(struct pci_bus *bus) ++{ ++ return 0; ++} ++ ++static inline unsigned long pci_reserve_size_mem(struct pci_bus *bus) ++{ ++ return 0; ++} ++#endif /* CONFIG_PCI_RESERVE */ ++ + #endif /* DRIVERS_PCI_H */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/drivers/pci/reserve.c 2010-03-24 14:00:05.000000000 +0100 +@@ -0,0 +1,138 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ * ++ * Copyright (c) 2009 Isaku Yamahata ++ * VA Linux Systems Japan K.K. ++ * ++ */ ++ ++#include ++#include ++ ++#include ++ ++static char pci_reserve_param[COMMAND_LINE_SIZE]; ++ ++/* pci_reserve= [PCI] ++ * Format: [[+IO][+MEM]][,...] ++ * Format of sbdf: [:]:. ++ */ ++static int pci_reserve_parse_size(const char *str, ++ unsigned long *io_size, ++ unsigned long *mem_size) ++{ ++ if (sscanf(str, "io%lx", io_size) == 1 || ++ sscanf(str, "IO%lx", io_size) == 1) ++ return 0; ++ ++ if (sscanf(str, "mem%lx", mem_size) == 1 || ++ sscanf(str, "MEM%lx", mem_size) == 1) ++ return 0; ++ ++ return -EINVAL; ++} ++ ++static int pci_reserve_parse_one(const char *str, ++ int *seg, int *bus, int *dev, int *func, ++ unsigned long *io_size, ++ unsigned long *mem_size) ++{ ++ char *p; ++ ++ *io_size = 0; ++ *mem_size = 0; ++ ++ if (sscanf(str, "%x:%x:%x.%x", seg, bus, dev, func) != 4) { ++ *seg = 0; ++ if (sscanf(str, "%x:%x.%x", bus, dev, func) != 3) { ++ return -EINVAL; ++ } ++ } ++ ++ p = strchr(str, '+'); ++ if (p == NULL) ++ return -EINVAL; ++ if (pci_reserve_parse_size(++p, io_size, mem_size)) ++ return -EINVAL; ++ ++ p = strchr(p, '+'); ++ return p ? pci_reserve_parse_size(p + 1, io_size, mem_size) : 0; ++} ++ ++static unsigned long pci_reserve_size(struct pci_bus *pbus, int flags) ++{ ++ char *sp; ++ char *ep; ++ ++ int seg; ++ int bus; ++ int dev; ++ int func; ++ ++ unsigned long io_size; ++ unsigned long mem_size; ++ ++ sp = pci_reserve_param; ++ ++ do { ++ ep = strchr(sp, ','); ++ if (ep) ++ *ep = '\0'; /* chomp */ ++ ++ if (pci_reserve_parse_one(sp, &seg, &bus, &dev, &func, ++ &io_size, &mem_size) == 0) { ++ if (pci_domain_nr(pbus) == seg && ++ pbus->number == bus && ++ PCI_SLOT(pbus->self->devfn) == dev && ++ PCI_FUNC(pbus->self->devfn) == func) { ++ switch (flags) { ++ case IORESOURCE_IO: ++ return io_size; ++ case IORESOURCE_MEM: ++ return mem_size; ++ default: ++ break; ++ } ++ } ++ } ++ ++ if (ep) { ++ *ep = ','; /* restore chomp'ed ',' for later */ ++ ep++; ++ } ++ sp = ep; ++ } while (ep); ++ ++ return 0; ++} ++ ++unsigned long pci_reserve_size_io(struct pci_bus *pbus) ++{ ++ return pci_reserve_size(pbus, IORESOURCE_IO); ++} ++ ++unsigned long pci_reserve_size_mem(struct pci_bus *pbus) ++{ ++ return pci_reserve_size(pbus, IORESOURCE_MEM); ++} ++ ++static int __init pci_reserve_setup(char *str) ++{ ++ if (strlen(str) >= sizeof(pci_reserve_param)) ++ return 0; ++ strlcpy(pci_reserve_param, str, sizeof(pci_reserve_param)); ++ return 1; ++} ++__setup("pci_reserve=", pci_reserve_setup); +--- head-2010-04-29.orig/drivers/pci/setup-bus.c 2010-04-29 09:29:51.000000000 +0200 ++++ head-2010-04-29/drivers/pci/setup-bus.c 2010-03-24 14:09:20.000000000 +0100 +@@ -448,6 +448,9 @@ static void pbus_size_io(struct pci_bus + size = ALIGN(size + size1, 4096); + if (size < old_size) + size = old_size; ++ size1 = pci_reserve_size_io(bus); ++ if (size < size1) ++ size = ALIGN(size1, 4096); + if (!size) { + if (b_res->start || b_res->end) + dev_info(&bus->self->dev, "disabling bridge window " +@@ -537,7 +540,8 @@ static int pbus_size_mem(struct pci_bus + min_align = align1 >> 1; + align += aligns[order]; + } +- size = ALIGN(size, min_align); ++ size = ALIGN(max(size, (resource_size_t)pci_reserve_size_mem(bus)), ++ min_align); + if (!size) { + if (b_res->start || b_res->end) + dev_info(&bus->self->dev, "disabling bridge window " diff --git a/patches.xen/sfc-driverlink b/patches.xen/sfc-driverlink new file mode 100644 index 0000000..bc3cf71 --- /dev/null +++ b/patches.xen/sfc-driverlink @@ -0,0 +1,1133 @@ +From: David Riddoch +commit d96c061bfd1839e34e136de0555564520acc97af +Author: Steve Hodgson +Date: Mon Jul 14 15:38:47 2008 +0100 + +Subject: sfc: Driverlink API for exporting hardware features to client drivers +References: FATE#303479 +Patch-mainline: n/a +Acked-by: jbeulich@novell.com + +--- head-2009-11-06.orig/drivers/net/sfc/Makefile 2009-11-06 10:29:51.000000000 +0100 ++++ head-2009-11-06/drivers/net/sfc/Makefile 2009-07-28 10:04:25.000000000 +0200 +@@ -1,6 +1,7 @@ + sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.o \ + falcon_xmac.o selftest.o ethtool.o xfp_phy.o \ +- mdio_10g.o tenxpress.o boards.o sfe4001.o ++ mdio_10g.o tenxpress.o boards.o sfe4001.o \ ++ driverlink.o + sfc-$(CONFIG_SFC_MTD) += mtd.o + + obj-$(CONFIG_SFC) += sfc.o +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-11-06/drivers/net/sfc/driverlink.c 2009-07-28 10:04:25.000000000 +0200 +@@ -0,0 +1,367 @@ ++/**************************************************************************** ++ * Driver for Solarflare Solarstorm network controllers and boards ++ * Copyright 2005 Fen Systems Ltd. ++ * Copyright 2005-2008 Solarflare Communications Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "net_driver.h" ++#include "efx.h" ++#include "driverlink_api.h" ++#include "driverlink.h" ++ ++/* Protects @efx_driverlink_lock and @efx_driver_list */ ++static DEFINE_MUTEX(efx_driverlink_lock); ++ ++/* List of all registered drivers */ ++static LIST_HEAD(efx_driver_list); ++ ++/* List of all registered Efx ports */ ++static LIST_HEAD(efx_port_list); ++ ++/** ++ * Driver link handle used internally to track devices ++ * @efx_dev: driverlink device handle exported to consumers ++ * @efx: efx_nic backing the driverlink device ++ * @port_node: per-device list head ++ * @driver_node: per-driver list head ++ */ ++struct efx_dl_handle { ++ struct efx_dl_device efx_dev; ++ struct efx_nic *efx; ++ struct list_head port_node; ++ struct list_head driver_node; ++}; ++ ++static struct efx_dl_handle *efx_dl_handle(struct efx_dl_device *efx_dev) ++{ ++ return container_of(efx_dev, struct efx_dl_handle, efx_dev); ++} ++ ++/* Remove an Efx device, and call the driver's remove() callback if ++ * present. The caller must hold @efx_driverlink_lock. */ ++static void efx_dl_del_device(struct efx_dl_device *efx_dev) ++{ ++ struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); ++ ++ EFX_INFO(efx_handle->efx, "%s driverlink client unregistering\n", ++ efx_dev->driver->name); ++ ++ if (efx_dev->driver->remove) ++ efx_dev->driver->remove(efx_dev); ++ ++ list_del(&efx_handle->driver_node); ++ list_del(&efx_handle->port_node); ++ ++ kfree(efx_handle); ++} ++ ++/* Attempt to probe the given device with the driver, creating a ++ * new &struct efx_dl_device. If the probe routine returns an error, ++ * then the &struct efx_dl_device is destroyed */ ++static void efx_dl_try_add_device(struct efx_nic *efx, ++ struct efx_dl_driver *driver) ++{ ++ struct efx_dl_handle *efx_handle; ++ struct efx_dl_device *efx_dev; ++ int rc; ++ ++ efx_handle = kzalloc(sizeof(*efx_handle), GFP_KERNEL); ++ if (!efx_handle) ++ goto fail; ++ efx_dev = &efx_handle->efx_dev; ++ efx_handle->efx = efx; ++ efx_dev->driver = driver; ++ efx_dev->pci_dev = efx->pci_dev; ++ INIT_LIST_HEAD(&efx_handle->port_node); ++ INIT_LIST_HEAD(&efx_handle->driver_node); ++ ++ rc = driver->probe(efx_dev, efx->net_dev, ++ efx->dl_info, efx->silicon_rev); ++ if (rc) ++ goto fail; ++ ++ list_add_tail(&efx_handle->driver_node, &driver->device_list); ++ list_add_tail(&efx_handle->port_node, &efx->dl_device_list); ++ ++ EFX_INFO(efx, "%s driverlink client registered\n", driver->name); ++ return; ++ ++ fail: ++ EFX_INFO(efx, "%s driverlink client skipped\n", driver->name); ++ ++ kfree(efx_handle); ++} ++ ++/* Unregister a driver from the driverlink layer, calling the ++ * driver's remove() callback for every attached device */ ++void efx_dl_unregister_driver(struct efx_dl_driver *driver) ++{ ++ struct efx_dl_handle *efx_handle, *efx_handle_n; ++ ++ printk(KERN_INFO "Efx driverlink unregistering %s driver\n", ++ driver->name); ++ ++ mutex_lock(&efx_driverlink_lock); ++ ++ list_for_each_entry_safe(efx_handle, efx_handle_n, ++ &driver->device_list, driver_node) ++ efx_dl_del_device(&efx_handle->efx_dev); ++ ++ list_del(&driver->node); ++ ++ mutex_unlock(&efx_driverlink_lock); ++} ++EXPORT_SYMBOL(efx_dl_unregister_driver); ++ ++/* Register a new driver with the driverlink layer. The driver's ++ * probe routine will be called for every attached nic. */ ++int efx_dl_register_driver(struct efx_dl_driver *driver) ++{ ++ struct efx_nic *efx; ++ int rc; ++ ++ printk(KERN_INFO "Efx driverlink registering %s driver\n", ++ driver->name); ++ ++ INIT_LIST_HEAD(&driver->node); ++ INIT_LIST_HEAD(&driver->device_list); ++ ++ rc = mutex_lock_interruptible(&efx_driverlink_lock); ++ if (rc) ++ return rc; ++ ++ list_add_tail(&driver->node, &efx_driver_list); ++ list_for_each_entry(efx, &efx_port_list, dl_node) ++ efx_dl_try_add_device(efx, driver); ++ ++ mutex_unlock(&efx_driverlink_lock); ++ ++ return 0; ++} ++EXPORT_SYMBOL(efx_dl_register_driver); ++ ++void efx_dl_unregister_nic(struct efx_nic *efx) ++{ ++ struct efx_dl_handle *efx_handle, *efx_handle_n; ++ ++ mutex_lock(&efx_driverlink_lock); ++ ++ list_for_each_entry_safe_reverse(efx_handle, efx_handle_n, ++ &efx->dl_device_list, ++ port_node) ++ efx_dl_del_device(&efx_handle->efx_dev); ++ ++ list_del(&efx->dl_node); ++ ++ mutex_unlock(&efx_driverlink_lock); ++} ++ ++int efx_dl_register_nic(struct efx_nic *efx) ++{ ++ struct efx_dl_driver *driver; ++ int rc; ++ ++ rc = mutex_lock_interruptible(&efx_driverlink_lock); ++ if (rc) ++ return rc; ++ ++ list_add_tail(&efx->dl_node, &efx_port_list); ++ list_for_each_entry(driver, &efx_driver_list, node) ++ efx_dl_try_add_device(efx, driver); ++ ++ mutex_unlock(&efx_driverlink_lock); ++ ++ return 0; ++} ++ ++/* Dummy callback implementations. ++ * To avoid a branch point on the fast-path, the callbacks are always ++ * implemented - they are never NULL. ++ */ ++static enum efx_veto efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, ++ struct sk_buff *skb) ++{ ++ return EFX_ALLOW_PACKET; ++} ++ ++static enum efx_veto efx_dummy_rx_packet_callback(struct efx_dl_device *efx_dev, ++ const char *pkt_buf, int len) ++{ ++ return EFX_ALLOW_PACKET; ++} ++ ++static int efx_dummy_request_mtu_callback(struct efx_dl_device *efx_dev, ++ int new_mtu) ++{ ++ return 0; ++} ++ ++static void efx_dummy_mtu_changed_callback(struct efx_dl_device *efx_dev, ++ int mtu) ++{ ++ return; ++} ++ ++static void efx_dummy_event_callback(struct efx_dl_device *efx_dev, void *event) ++{ ++ return; ++} ++ ++struct efx_dl_callbacks efx_default_callbacks = { ++ .tx_packet = efx_dummy_tx_packet_callback, ++ .rx_packet = efx_dummy_rx_packet_callback, ++ .request_mtu = efx_dummy_request_mtu_callback, ++ .mtu_changed = efx_dummy_mtu_changed_callback, ++ .event = efx_dummy_event_callback, ++}; ++ ++void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, ++ struct efx_dl_callbacks *callbacks) ++{ ++ struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); ++ struct efx_nic *efx = efx_handle->efx; ++ ++ efx_suspend(efx); ++ ++ EFX_INFO(efx, "removing callback hooks into %s driver\n", ++ efx_dev->driver->name); ++ ++ if (callbacks->tx_packet) { ++ BUG_ON(efx->dl_cb_dev.tx_packet != efx_dev); ++ efx->dl_cb.tx_packet = efx_default_callbacks.tx_packet; ++ efx->dl_cb_dev.tx_packet = NULL; ++ } ++ if (callbacks->rx_packet) { ++ BUG_ON(efx->dl_cb_dev.rx_packet != efx_dev); ++ efx->dl_cb.rx_packet = efx_default_callbacks.rx_packet; ++ efx->dl_cb_dev.rx_packet = NULL; ++ } ++ if (callbacks->request_mtu) { ++ BUG_ON(efx->dl_cb_dev.request_mtu != efx_dev); ++ efx->dl_cb.request_mtu = efx_default_callbacks.request_mtu; ++ efx->dl_cb_dev.request_mtu = NULL; ++ } ++ if (callbacks->mtu_changed) { ++ BUG_ON(efx->dl_cb_dev.mtu_changed != efx_dev); ++ efx->dl_cb.mtu_changed = efx_default_callbacks.mtu_changed; ++ efx->dl_cb_dev.mtu_changed = NULL; ++ } ++ if (callbacks->event) { ++ BUG_ON(efx->dl_cb_dev.event != efx_dev); ++ efx->dl_cb.event = efx_default_callbacks.event; ++ efx->dl_cb_dev.event = NULL; ++ } ++ ++ efx_resume(efx); ++} ++EXPORT_SYMBOL(efx_dl_unregister_callbacks); ++ ++int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, ++ struct efx_dl_callbacks *callbacks) ++{ ++ struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); ++ struct efx_nic *efx = efx_handle->efx; ++ int rc = 0; ++ ++ efx_suspend(efx); ++ ++ /* Check that the requested callbacks are not already hooked. */ ++ if ((callbacks->tx_packet && efx->dl_cb_dev.tx_packet) || ++ (callbacks->rx_packet && efx->dl_cb_dev.rx_packet) || ++ (callbacks->request_mtu && efx->dl_cb_dev.request_mtu) || ++ (callbacks->mtu_changed && efx->dl_cb_dev.mtu_changed) || ++ (callbacks->event && efx->dl_cb_dev.event)) { ++ rc = -EBUSY; ++ goto out; ++ } ++ ++ EFX_INFO(efx, "adding callback hooks to %s driver\n", ++ efx_dev->driver->name); ++ ++ /* Hook in the requested callbacks, leaving any NULL members ++ * referencing the members of @efx_default_callbacks */ ++ if (callbacks->tx_packet) { ++ efx->dl_cb.tx_packet = callbacks->tx_packet; ++ efx->dl_cb_dev.tx_packet = efx_dev; ++ } ++ if (callbacks->rx_packet) { ++ efx->dl_cb.rx_packet = callbacks->rx_packet; ++ efx->dl_cb_dev.rx_packet = efx_dev; ++ } ++ if (callbacks->request_mtu) { ++ efx->dl_cb.request_mtu = callbacks->request_mtu; ++ efx->dl_cb_dev.request_mtu = efx_dev; ++ } ++ if (callbacks->mtu_changed) { ++ efx->dl_cb.mtu_changed = callbacks->mtu_changed; ++ efx->dl_cb_dev.mtu_changed = efx_dev; ++ } ++ if (callbacks->event) { ++ efx->dl_cb.event = callbacks->event; ++ efx->dl_cb_dev.event = efx_dev; ++ } ++ ++ out: ++ efx_resume(efx); ++ ++ return rc; ++} ++EXPORT_SYMBOL(efx_dl_register_callbacks); ++ ++void efx_dl_schedule_reset(struct efx_dl_device *efx_dev) ++{ ++ struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); ++ struct efx_nic *efx = efx_handle->efx; ++ ++ efx_schedule_reset(efx, RESET_TYPE_ALL); ++} ++EXPORT_SYMBOL(efx_dl_schedule_reset); ++ ++void efx_dl_reset_unlock(void) ++{ ++ mutex_unlock(&efx_driverlink_lock); ++} ++ ++/* Suspend ready for reset, serialising against all the driverlink interfacse ++ * and calling the suspend() callback of every registered driver */ ++void efx_dl_reset_suspend(struct efx_nic *efx) ++{ ++ struct efx_dl_handle *efx_handle; ++ struct efx_dl_device *efx_dev; ++ ++ mutex_lock(&efx_driverlink_lock); ++ ++ list_for_each_entry_reverse(efx_handle, ++ &efx->dl_device_list, ++ port_node) { ++ efx_dev = &efx_handle->efx_dev; ++ if (efx_dev->driver->reset_suspend) ++ efx_dev->driver->reset_suspend(efx_dev); ++ } ++} ++ ++/* Resume after a reset, calling the resume() callback of every registered ++ * driver, and releasing @Efx_driverlink_lock acquired in ++ * efx_dl_reset_resume() */ ++void efx_dl_reset_resume(struct efx_nic *efx, int ok) ++{ ++ struct efx_dl_handle *efx_handle; ++ struct efx_dl_device *efx_dev; ++ ++ list_for_each_entry(efx_handle, &efx->dl_device_list, ++ port_node) { ++ efx_dev = &efx_handle->efx_dev; ++ if (efx_dev->driver->reset_resume) ++ efx_dev->driver->reset_resume(efx_dev, ok); ++ } ++ ++ mutex_unlock(&efx_driverlink_lock); ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-11-06/drivers/net/sfc/driverlink.h 2009-07-28 10:04:25.000000000 +0200 +@@ -0,0 +1,43 @@ ++/**************************************************************************** ++ * Driver for Solarflare Solarstorm network controllers and boards ++ * Copyright 2005 Fen Systems Ltd. ++ * Copyright 2006-2008 Solarflare Communications Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ */ ++ ++#ifndef EFX_DRIVERLINK_H ++#define EFX_DRIVERLINK_H ++ ++/* Forward declarations */ ++struct efx_dl_device; ++struct efx_nic; ++ ++/* Efx callback devices ++ * ++ * A list of the devices that own each callback. The partner to ++ * struct efx_dl_callbacks. ++ */ ++struct efx_dl_cb_devices { ++ struct efx_dl_device *tx_packet; ++ struct efx_dl_device *rx_packet; ++ struct efx_dl_device *request_mtu; ++ struct efx_dl_device *mtu_changed; ++ struct efx_dl_device *event; ++}; ++ ++extern struct efx_dl_callbacks efx_default_callbacks; ++ ++#define EFX_DL_CALLBACK(_port, _name, ...) \ ++ (_port)->dl_cb._name((_port)->dl_cb_dev._name, __VA_ARGS__) ++ ++extern int efx_dl_register_nic(struct efx_nic *efx); ++extern void efx_dl_unregister_nic(struct efx_nic *efx); ++ ++/* Suspend and resume client drivers over a hardware reset */ ++extern void efx_dl_reset_suspend(struct efx_nic *efx); ++extern void efx_dl_reset_resume(struct efx_nic *efx, int ok); ++ ++#endif /* EFX_DRIVERLINK_H */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-11-06/drivers/net/sfc/driverlink_api.h 2009-07-28 10:04:25.000000000 +0200 +@@ -0,0 +1,303 @@ ++/**************************************************************************** ++ * Driver for Solarflare Solarstorm network controllers and boards ++ * Copyright 2005-2006 Fen Systems Ltd. ++ * Copyright 2005-2008 Solarflare Communications Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ */ ++ ++#ifndef EFX_DRIVERLINK_API_H ++#define EFX_DRIVERLINK_API_H ++ ++#include ++ ++/* Forward declarations */ ++struct pci_dev; ++struct net_device; ++struct sk_buff; ++struct efx_dl_device; ++struct efx_dl_device_info; ++ ++/* An extra safeguard in addition to symbol versioning */ ++#define EFX_DRIVERLINK_API_VERSION 2 ++ ++/** ++ * struct efx_dl_driver - An Efx driverlink device driver ++ * ++ * A driverlink client defines and initializes as many instances of ++ * efx_dl_driver as required, registering each one with ++ * efx_dl_register_driver(). ++ * ++ * @name: Name of the driver ++ * @probe: Called when device added ++ * The client should use the @def_info linked list and @silicon_rev ++ * to determine if they wish to attach to this device. ++ * Context: process, driverlink semaphore held ++ * @remove: Called when device removed ++ * The client must ensure the finish all operations with this ++ * device before returning from this method. ++ * Context: process, driverlink semaphore held ++ * @reset_suspend: Called before device is reset ++ * Called immediately before a hardware reset. The client must stop all ++ * hardware processing before returning from this method. Callbacks will ++ * be inactive when this method is called. ++ * Context: process, driverlink semaphore held. rtnl_lock may be held ++ * @reset_resume: Called after device is reset ++ * Called after a hardware reset. If @ok is true, the client should ++ * state and resume normal operations. If @ok is false, the client should ++ * abandon use of the hardware resources. remove() will still be called. ++ * Context: process, driverlink semaphore held. rtnl_lock may be held ++ */ ++struct efx_dl_driver { ++ const char *name; ++ ++ int (*probe) (struct efx_dl_device *efx_dl_dev, ++ const struct net_device *net_dev, ++ const struct efx_dl_device_info *dev_info, ++ const char *silicon_rev); ++ void (*remove) (struct efx_dl_device *efx_dev); ++ void (*reset_suspend) (struct efx_dl_device *efx_dev); ++ void (*reset_resume) (struct efx_dl_device *efx_dev, int ok); ++ ++/* private: */ ++ struct list_head node; ++ struct list_head device_list; ++}; ++ ++/** ++ * enum efx_dl_device_info_type - Device information identifier. ++ * ++ * Used to identify each item in the &struct efx_dl_device_info linked list ++ * provided to each driverlink client in the probe() @dev_info member. ++ * ++ * @EFX_DL_FALCON_RESOURCES: Information type is &struct efx_dl_falcon_resources ++ */ ++enum efx_dl_device_info_type { ++ /** Falcon resources available for export */ ++ EFX_DL_FALCON_RESOURCES = 0, ++}; ++ ++/** ++ * struct efx_dl_device_info - device information structure ++ * ++ * @next: Link to next structure, if any ++ * @type: Type code for this structure ++ */ ++struct efx_dl_device_info { ++ struct efx_dl_device_info *next; ++ enum efx_dl_device_info_type type; ++}; ++ ++/** ++ * enum efx_dl_falcon_resource_flags - Falcon resource information flags. ++ * ++ * Flags that describe hardware variations for the current Falcon device. ++ * ++ * @EFX_DL_FALCON_DUAL_FUNC: Port is dual-function. ++ * Certain silicon revisions have two pci functions, and require ++ * certain hardware resources to be accessed via the secondary ++ * function ++ * @EFX_DL_FALCON_USE_MSI: Port is initialised to use MSI/MSI-X interrupts. ++ * Falcon supports traditional legacy interrupts and MSI/MSI-X ++ * interrupts. The choice is made at run time by the sfc driver, and ++ * notified to the clients by this enumeration ++ */ ++enum efx_dl_falcon_resource_flags { ++ EFX_DL_FALCON_DUAL_FUNC = 0x1, ++ EFX_DL_FALCON_USE_MSI = 0x2, ++}; ++ ++/** ++ * struct efx_dl_falcon_resources - Falcon resource information. ++ * ++ * This structure describes Falcon hardware resources available for ++ * use by a driverlink driver. ++ * ++ * @hdr: Resource linked list header ++ * @biu_lock: Register access lock. ++ * Some Falcon revisions require register access for configuration ++ * registers to be serialised between ports and PCI functions. ++ * The sfc driver will provide the appropriate lock semantics for ++ * the underlying hardware. ++ * @buffer_table_min: First available buffer table entry ++ * @buffer_table_lim: Last available buffer table entry + 1 ++ * @evq_timer_min: First available event queue with timer ++ * @evq_timer_lim: Last available event queue with timer + 1 ++ * @evq_int_min: First available event queue with interrupt ++ * @evq_int_lim: Last available event queue with interrupt + 1 ++ * @rxq_min: First available RX queue ++ * @rxq_lim: Last available RX queue + 1 ++ * @txq_min: First available TX queue ++ * @txq_lim: Last available TX queue + 1 ++ * @flags: Hardware variation flags ++ */ ++struct efx_dl_falcon_resources { ++ struct efx_dl_device_info hdr; ++ spinlock_t *biu_lock; ++ unsigned buffer_table_min; ++ unsigned buffer_table_lim; ++ unsigned evq_timer_min; ++ unsigned evq_timer_lim; ++ unsigned evq_int_min; ++ unsigned evq_int_lim; ++ unsigned rxq_min; ++ unsigned rxq_lim; ++ unsigned txq_min; ++ unsigned txq_lim; ++ enum efx_dl_falcon_resource_flags flags; ++}; ++ ++/** ++ * struct efx_dl_device - An Efx driverlink device. ++ * ++ * @pci_dev: PCI device used by the sfc driver. ++ * @priv: Driver private data ++ * Driverlink clients can use this to store a pointer to their ++ * internal per-device data structure. Each (driver, device) ++ * tuple has a separate &struct efx_dl_device, so clients can use ++ * this @priv field independently. ++ * @driver: Efx driverlink driver for this device ++ */ ++struct efx_dl_device { ++ struct pci_dev *pci_dev; ++ void *priv; ++ struct efx_dl_driver *driver; ++}; ++ ++/** ++ * enum efx_veto - Packet veto request flag. ++ * ++ * This is the return type for the rx_packet() and tx_packet() methods ++ * in &struct efx_dl_callbacks. ++ * ++ * @EFX_ALLOW_PACKET: Packet may be transmitted/received ++ * @EFX_VETO_PACKET: Packet must not be transmitted/received ++ */ ++enum efx_veto { ++ EFX_ALLOW_PACKET = 0, ++ EFX_VETO_PACKET = 1, ++}; ++ ++/** ++ * struct efx_dl_callbacks - Efx callbacks ++ * ++ * This is a tighly controlled set of simple callbacks, that are attached ++ * to the sfc driver via efx_dl_register_callbacks(). They export just enough ++ * state to allow clients to make use of the available hardware resources. ++ * ++ * For efficiency, only one client can hook each callback. Since these ++ * callbacks are called on packet transmit and reception paths, and the ++ * sfc driver may have multiple tx and rx queues per port, clients should ++ * avoid acquiring locks or allocating memory. ++ * ++ * @tx_packet: Called when packet is about to be transmitted ++ * Called for every packet about to be transmitted, providing means ++ * for the client to snoop traffic, and veto transmission by returning ++ * %EFX_VETO_PACKET (the sfc driver will subsequently free the skb). ++ * Context: tasklet, netif_tx_lock held ++ * @rx_packet: Called when packet is received ++ * Called for every received packet (after LRO), allowing the client ++ * to snoop every received packet (on every rx queue), and veto ++ * reception by returning %EFX_VETO_PACKET. ++ * Context: tasklet ++ * @request_mtu: Called to request MTU change. ++ * Called whenever the user requests the net_dev mtu to be changed. ++ * If the client returns an error, the mtu change is aborted. The sfc ++ * driver guarantees that no other callbacks are running. ++ * Context: process, rtnl_lock held. ++ * @mtu_changed: Called when MTU has been changed. ++ * Called after the mtu has been successfully changed, always after ++ * a previous call to request_mtu(). The sfc driver guarantees that no ++ * other callbacks are running. ++ * Context: process, rtnl_lock held. ++ * @event: Called when a hardware NIC event is not understood by the sfc driver. ++ * Context: tasklet. ++ */ ++struct efx_dl_callbacks { ++ enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev, ++ struct sk_buff *skb); ++ enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev, ++ const char *pkt_hdr, int pkt_len); ++ int (*request_mtu) (struct efx_dl_device *efx_dev, int new_mtu); ++ void (*mtu_changed) (struct efx_dl_device *efx_dev, int mtu); ++ void (*event) (struct efx_dl_device *efx_dev, void *p_event); ++}; ++ ++/* Include API version number in symbol used for efx_dl_register_driver */ ++#define efx_dl_stringify_1(x, y) x ## y ++#define efx_dl_stringify_2(x, y) efx_dl_stringify_1(x, y) ++#define efx_dl_register_driver \ ++ efx_dl_stringify_2(efx_dl_register_driver_api_ver_, \ ++ EFX_DRIVERLINK_API_VERSION) ++ ++/* Exported driverlink api used to register and unregister the client driver ++ * and any callbacks [only one per port allowed], and to allow a client driver ++ * to request reset to recover from an error condition. ++ * ++ * All of these functions acquire the driverlink semaphore, so must not be ++ * called from an efx_dl_driver or efx_dl_callbacks member, and must be called ++ * from process context. ++ */ ++extern int efx_dl_register_driver(struct efx_dl_driver *driver); ++ ++extern void efx_dl_unregister_driver(struct efx_dl_driver *driver); ++ ++extern int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, ++ struct efx_dl_callbacks *callbacks); ++ ++extern void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, ++ struct efx_dl_callbacks *callbacks); ++ ++/* Schedule a reset without grabbing any locks */ ++extern void efx_dl_schedule_reset(struct efx_dl_device *efx_dev); ++ ++/** ++ * efx_dl_for_each_device_info_matching - iterate an efx_dl_device_info list ++ * @_dev_info: Pointer to first &struct efx_dl_device_info ++ * @_type: Type code to look for ++ * @_info_type: Structure type corresponding to type code ++ * @_field: Name of &struct efx_dl_device_info field in the type ++ * @_p: Iterator variable ++ * ++ * Example: ++ * struct efx_dl_falcon_resources *res; ++ * efx_dl_for_each_device_info_matching(dev_info, EFX_DL_FALCON_RESOURCES, ++ * struct efx_dl_falcon_resources, ++ * hdr, res) { ++ * if (res->flags & EFX_DL_FALCON_DUAL_FUNC) ++ * .... ++ * } ++ */ ++#define efx_dl_for_each_device_info_matching(_dev_info, _type, \ ++ _info_type, _field, _p) \ ++ for ((_p) = container_of((_dev_info), _info_type, _field); \ ++ (_p) != NULL; \ ++ (_p) = container_of((_p)->_field.next, _info_type, _field))\ ++ if ((_p)->_field.type != _type) \ ++ continue; \ ++ else ++ ++/** ++ * efx_dl_search_device_info - search an efx_dl_device_info list ++ * @_dev_info: Pointer to first &struct efx_dl_device_info ++ * @_type: Type code to look for ++ * @_info_type: Structure type corresponding to type code ++ * @_field: Name of &struct efx_dl_device_info member in this type ++ * @_p: Result variable ++ * ++ * Example: ++ * struct efx_dl_falcon_resources *res; ++ * efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES, ++ * struct efx_dl_falcon_resources, hdr, res); ++ * if (res) ++ * .... ++ */ ++#define efx_dl_search_device_info(_dev_info, _type, _info_type, \ ++ _field, _p) \ ++ efx_dl_for_each_device_info_matching((_dev_info), (_type), \ ++ _info_type, _field, (_p)) \ ++ break; ++ ++#endif /* EFX_DRIVERLINK_API_H */ +--- head-2009-11-06.orig/drivers/net/sfc/efx.c 2009-11-06 10:29:51.000000000 +0100 ++++ head-2009-11-06/drivers/net/sfc/efx.c 2009-10-12 13:40:25.000000000 +0200 +@@ -1487,12 +1487,21 @@ static int efx_change_mtu(struct net_dev + + efx_stop_all(efx); + ++ /* Ask driverlink client if we can change MTU */ ++ rc = EFX_DL_CALLBACK(efx, request_mtu, new_mtu); ++ if (rc) ++ goto out; ++ + EFX_LOG(efx, "changing MTU to %d\n", new_mtu); + + efx_fini_channels(efx); + net_dev->mtu = new_mtu; + efx_init_channels(efx); + ++ /* Notify driverlink client of new MTU */ ++ EFX_DL_CALLBACK(efx, mtu_changed, new_mtu); ++ ++ out: + efx_start_all(efx); + return rc; + } +@@ -1680,6 +1689,23 @@ static void efx_unregister_netdev(struct + * Device reset and suspend + * + **************************************************************************/ ++/* Serialise access to the driverlink callbacks, by quiescing event processing ++ * (without flushing the descriptor queues), and acquiring the rtnl_lock */ ++void efx_suspend(struct efx_nic *efx) ++{ ++ EFX_LOG(efx, "suspending operations\n"); ++ ++ rtnl_lock(); ++ efx_stop_all(efx); ++} ++ ++void efx_resume(struct efx_nic *efx) ++{ ++ EFX_LOG(efx, "resuming operations\n"); ++ ++ efx_start_all(efx); ++ rtnl_unlock(); ++} + + /* Tears down the entire software state and most of the hardware state + * before reset. */ +@@ -1760,8 +1786,8 @@ static int efx_reset(struct efx_nic *efx + enum reset_type method = efx->reset_pending; + int rc = 0; + +- /* Serialise with kernel interfaces */ + rtnl_lock(); ++ efx_dl_reset_suspend(efx); + + /* If we're not RUNNING then don't reset. Leave the reset_pending + * flag set so that efx_pci_probe_main will be retried */ +@@ -1807,6 +1833,7 @@ out_disable: + } + + out_unlock: ++ efx_dl_reset_resume(efx, 1); + rtnl_unlock(); + return rc; + } +@@ -1951,6 +1978,9 @@ static int efx_init_struct(struct efx_ni + efx->mac_op = &efx_dummy_mac_operations; + efx->phy_op = &efx_dummy_phy_operations; + efx->mdio.dev = net_dev; ++ INIT_LIST_HEAD(&efx->dl_node); ++ INIT_LIST_HEAD(&efx->dl_device_list); ++ efx->dl_cb = efx_default_callbacks; + INIT_WORK(&efx->phy_work, efx_phy_work); + INIT_WORK(&efx->mac_work, efx_mac_work); + atomic_set(&efx->netif_stop_count, 1); +@@ -2054,6 +2084,7 @@ static void efx_pci_remove(struct pci_de + efx = pci_get_drvdata(pci_dev); + if (!efx) + return; ++ efx_dl_unregister_nic(efx); + + /* Mark the NIC as fini, then stop the interface */ + rtnl_lock(); +@@ -2230,9 +2261,16 @@ static int __devinit efx_pci_probe(struc + if (rc) + goto fail5; + ++ /* Register with driverlink layer */ ++ rc = efx_dl_register_nic(efx); ++ if (rc) ++ goto fail6; ++ + EFX_LOG(efx, "initialisation successful\n"); + return 0; + ++ fail6: ++ efx_unregister_netdev(efx); + fail5: + efx_pci_remove_main(efx); + fail4: +--- head-2009-11-06.orig/drivers/net/sfc/falcon.c 2009-11-06 10:29:51.000000000 +0100 ++++ head-2009-11-06/drivers/net/sfc/falcon.c 2009-07-28 10:04:25.000000000 +0200 +@@ -36,14 +36,14 @@ + + /** + * struct falcon_nic_data - Falcon NIC state +- * @next_buffer_table: First available buffer table id ++ * @resources: Resource information for driverlink client + * @pci_dev2: The secondary PCI device if present + * @i2c_data: Operations and state for I2C bit-bashing algorithm + * @int_error_count: Number of internal errors seen recently + * @int_error_expire: Time at which error count will be expired + */ + struct falcon_nic_data { +- unsigned next_buffer_table; ++ struct efx_dl_falcon_resources resources; + struct pci_dev *pci_dev2; + struct i2c_algo_bit_data i2c_data; + +@@ -336,8 +336,8 @@ static int falcon_alloc_special_buffer(s + memset(buffer->addr, 0xff, len); + + /* Select new buffer ID */ +- buffer->index = nic_data->next_buffer_table; +- nic_data->next_buffer_table += buffer->entries; ++ buffer->index = nic_data->resources.buffer_table_min; ++ nic_data->resources.buffer_table_min += buffer->entries; + + EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, +@@ -960,10 +960,12 @@ static void falcon_handle_driver_event(s + case TX_DESCQ_FLS_DONE_EV_DECODE: + EFX_TRACE(efx, "channel %d TXQ %d flushed\n", + channel->channel, ev_sub_data); ++ EFX_DL_CALLBACK(efx, event, event); + break; + case RX_DESCQ_FLS_DONE_EV_DECODE: + EFX_TRACE(efx, "channel %d RXQ %d flushed\n", + channel->channel, ev_sub_data); ++ EFX_DL_CALLBACK(efx, event, event); + break; + case EVQ_INIT_DONE_EV_DECODE: + EFX_LOG(efx, "channel %d EVQ %d initialised\n", +@@ -972,14 +974,17 @@ static void falcon_handle_driver_event(s + case SRM_UPD_DONE_EV_DECODE: + EFX_TRACE(efx, "channel %d SRAM update done\n", + channel->channel); ++ EFX_DL_CALLBACK(efx, event, event); + break; + case WAKE_UP_EV_DECODE: + EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", + channel->channel, ev_sub_data); ++ EFX_DL_CALLBACK(efx, event, event); + break; + case TIMER_EV_DECODE: + EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", + channel->channel, ev_sub_data); ++ EFX_DL_CALLBACK(efx, event, event); + break; + case RX_RECOVERY_EV_DECODE: + EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " +@@ -1004,6 +1009,7 @@ static void falcon_handle_driver_event(s + EFX_TRACE(efx, "channel %d unknown driver event code %d " + "data %04x\n", channel->channel, ev_sub_code, + ev_sub_data); ++ EFX_DL_CALLBACK(efx, event, event); + break; + } + } +@@ -2744,6 +2750,59 @@ static int falcon_probe_nvconfig(struct + return rc; + } + ++/* Looks at available SRAM resources and silicon revision, and works out ++ * how many queues we can support, and where things like descriptor caches ++ * should live. */ ++static int falcon_dimension_resources(struct efx_nic *efx) ++{ ++ unsigned internal_dcs_entries; ++ struct falcon_nic_data *nic_data = efx->nic_data; ++ struct efx_dl_falcon_resources *res = &nic_data->resources; ++ ++ /* Fill out the driverlink resource list */ ++ res->hdr.type = EFX_DL_FALCON_RESOURCES; ++ res->biu_lock = &efx->biu_lock; ++ efx->dl_info = &res->hdr; ++ ++ /* NB. The minimum values get increased as this driver initialises ++ * its resources, so this should prevent any overlap. ++ */ ++ switch (falcon_rev(efx)) { ++ case FALCON_REV_A1: ++ res->rxq_min = 16; ++ res->txq_min = 16; ++ res->evq_int_min = 4; ++ res->evq_int_lim = 5; ++ res->evq_timer_min = 5; ++ res->evq_timer_lim = 4096; ++ internal_dcs_entries = 8192; ++ break; ++ case FALCON_REV_B0: ++ default: ++ res->rxq_min = 0; ++ res->txq_min = 0; ++ res->evq_int_min = 0; ++ res->evq_int_lim = 64; ++ res->evq_timer_min = 64; ++ res->evq_timer_lim = 4096; ++ internal_dcs_entries = 4096; ++ break; ++ } ++ ++ /* Internal SRAM only for now */ ++ res->rxq_lim = internal_dcs_entries / RX_DC_ENTRIES; ++ res->txq_lim = internal_dcs_entries / TX_DC_ENTRIES; ++ res->buffer_table_lim = 8192; ++ ++ if (FALCON_IS_DUAL_FUNC(efx)) ++ res->flags |= EFX_DL_FALCON_DUAL_FUNC; ++ ++ if (EFX_INT_MODE_USE_MSI(efx)) ++ res->flags |= EFX_DL_FALCON_USE_MSI; ++ ++ return 0; ++} ++ + /* Probe the NIC variant (revision, ASIC vs FPGA, function count, port + * count, port speed). Set workaround and feature flags accordingly. + */ +@@ -2771,9 +2830,11 @@ static int falcon_probe_nic_variant(stru + EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n"); + return -ENODEV; + } ++ efx->silicon_rev = "falcon/a1"; + break; + + case FALCON_REV_B0: ++ efx->silicon_rev = "falcon/b0"; + break; + + default: +@@ -2883,6 +2944,10 @@ int falcon_probe_nic(struct efx_nic *efx + if (rc) + goto fail5; + ++ rc = falcon_dimension_resources(efx); ++ if (rc) ++ goto fail6; ++ + /* Initialise I2C adapter */ + efx->i2c_adap.owner = THIS_MODULE; + nic_data->i2c_data = falcon_i2c_bit_operations; +@@ -2892,10 +2957,12 @@ int falcon_probe_nic(struct efx_nic *efx + strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name)); + rc = i2c_bit_add_bus(&efx->i2c_adap); + if (rc) +- goto fail5; ++ goto fail6; + + return 0; + ++ fail6: ++ efx->dl_info = NULL; + fail5: + falcon_remove_spi_devices(efx); + falcon_free_buffer(efx, &efx->irq_status); +@@ -3083,6 +3150,7 @@ void falcon_remove_nic(struct efx_nic *e + /* Tear down the private nic state */ + kfree(efx->nic_data); + efx->nic_data = NULL; ++ efx->dl_info = NULL; + } + + void falcon_update_nic_stats(struct efx_nic *efx) +--- head-2009-11-06.orig/drivers/net/sfc/net_driver.h 2009-11-06 10:29:51.000000000 +0100 ++++ head-2009-11-06/drivers/net/sfc/net_driver.h 2009-07-28 10:04:25.000000000 +0200 +@@ -29,6 +29,8 @@ + + #include "enum.h" + #include "bitfield.h" ++#include "driverlink_api.h" ++#include "driverlink.h" + + /************************************************************************** + * +@@ -754,6 +756,12 @@ union efx_multicast_hash { + * @loopback_mode: Loopback status + * @loopback_modes: Supported loopback mode bitmask + * @loopback_selftest: Offline self-test private state ++ * @silicon_rev: Silicon revision description for driverlink ++ * @dl_info: Linked list of hardware parameters exposed through driverlink ++ * @dl_node: Driverlink port list ++ * @dl_device_list: Driverlink device list ++ * @dl_cb: Driverlink callbacks table ++ * @dl_cb_dev: Driverlink callback owner devices + * + * The @priv field of the corresponding &struct net_device points to + * this. +@@ -844,6 +852,13 @@ struct efx_nic { + unsigned int loopback_modes; + + void *loopback_selftest; ++ ++ const char *silicon_rev; ++ struct efx_dl_device_info *dl_info; ++ struct list_head dl_node; ++ struct list_head dl_device_list; ++ struct efx_dl_callbacks dl_cb; ++ struct efx_dl_cb_devices dl_cb_dev; + }; + + static inline int efx_dev_registered(struct efx_nic *efx) +--- head-2009-11-06.orig/drivers/net/sfc/rx.c 2009-11-06 10:29:51.000000000 +0100 ++++ head-2009-11-06/drivers/net/sfc/rx.c 2009-11-06 10:32:03.000000000 +0100 +@@ -447,7 +447,21 @@ static void efx_rx_packet_lro(struct efx + struct efx_rx_buffer *rx_buf, + bool checksummed) + { ++ struct efx_nic *efx = channel->efx; + struct napi_struct *napi = &channel->napi_str; ++ enum efx_veto veto; ++ ++ /* It would be faster if we had access to packets at the ++ * other side of generic LRO. Unfortunately, there isn't ++ * an obvious interface to this, so veto packets before LRO */ ++ veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len); ++ if (unlikely(veto)) { ++ EFX_TRACE(efx, "LRO RX vetoed by driverlink %s driver\n", ++ efx->dl_cb_dev.rx_packet->driver->name); ++ /* Free the buffer now */ ++ efx_free_rx_buffer(efx, rx_buf); ++ return; ++ } + + /* Pass the skb/page into the LRO engine */ + if (rx_buf->page) { +@@ -550,6 +564,7 @@ void __efx_rx_packet(struct efx_channel + struct efx_rx_buffer *rx_buf, bool checksummed) + { + struct efx_nic *efx = channel->efx; ++ enum efx_veto veto; + struct sk_buff *skb; + + /* If we're in loopback test, then pass the packet directly to the +@@ -561,6 +576,16 @@ void __efx_rx_packet(struct efx_channel + goto done; + } + ++ /* Allow callback to veto the packet */ ++ veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len); ++ if (unlikely(veto)) { ++ EFX_LOG(efx, "RX vetoed by driverlink %s driver\n", ++ efx->dl_cb_dev.rx_packet->driver->name); ++ /* Free the buffer now */ ++ efx_free_rx_buffer(efx, rx_buf); ++ goto done; ++ } ++ + if (rx_buf->skb) { + prefetch(skb_shinfo(rx_buf->skb)); + +--- head-2009-11-06.orig/drivers/net/sfc/tx.c 2009-11-06 10:29:51.000000000 +0100 ++++ head-2009-11-06/drivers/net/sfc/tx.c 2009-10-12 13:40:32.000000000 +0200 +@@ -374,6 +374,7 @@ netdev_tx_t efx_hard_start_xmit(struct s + { + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_tx_queue *tx_queue; ++ enum efx_veto veto; + + if (unlikely(efx->port_inhibited)) + return NETDEV_TX_BUSY; +@@ -383,6 +384,17 @@ netdev_tx_t efx_hard_start_xmit(struct s + else + tx_queue = &efx->tx_queue[EFX_TX_QUEUE_NO_CSUM]; + ++ /* See if driverlink wants to veto the packet. */ ++ veto = EFX_DL_CALLBACK(efx, tx_packet, skb); ++ if (unlikely(veto)) { ++ EFX_TRACE(efx, "TX queue %d packet vetoed by " ++ "driverlink %s driver\n", tx_queue->queue, ++ efx->dl_cb_dev.tx_packet->driver->name); ++ /* Free the skb; nothing else will do it */ ++ dev_kfree_skb_any(skb); ++ return NETDEV_TX_OK; ++ } ++ + return efx_xmit(efx, tx_queue, skb); + } + diff --git a/patches.xen/sfc-driverlink-conditional b/patches.xen/sfc-driverlink-conditional new file mode 100644 index 0000000..c3a264f --- /dev/null +++ b/patches.xen/sfc-driverlink-conditional @@ -0,0 +1,248 @@ +From: jbeulich@novell.com +Subject: conditionalize driverlink additions to Solarflare driver +Patch-mainline: n/a +References: FATE#303479 + +At once converted the EFX_TRACE() invocations after vetoed RX/TX +callbacks to ...LOG() ones, which is consistent with Solarflare's +current code according to David Riddoch (2008-09-12). + +--- head-2009-11-06.orig/drivers/net/sfc/Kconfig 2009-04-21 11:02:22.000000000 +0200 ++++ head-2009-11-06/drivers/net/sfc/Kconfig 2009-10-12 13:41:03.000000000 +0200 +@@ -12,8 +12,12 @@ config SFC + To compile this driver as a module, choose M here. The module + will be called sfc. + ++config SFC_DRIVERLINK ++ bool ++ + config SFC_RESOURCE + depends on SFC && X86 ++ select SFC_DRIVERLINK + tristate "Solarflare Solarstorm SFC4000 resource driver" + help + This module provides the SFC resource manager driver. +--- head-2009-11-06.orig/drivers/net/sfc/Makefile 2009-02-06 12:42:18.000000000 +0100 ++++ head-2009-11-06/drivers/net/sfc/Makefile 2009-10-12 13:41:03.000000000 +0200 +@@ -1,7 +1,7 @@ + sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.o \ + falcon_xmac.o selftest.o ethtool.o xfp_phy.o \ +- mdio_10g.o tenxpress.o boards.o sfe4001.o \ +- driverlink.o ++ mdio_10g.o tenxpress.o boards.o sfe4001.o ++sfc-$(CONFIG_SFC_DRIVERLINK) += driverlink.o + sfc-$(CONFIG_SFC_MTD) += mtd.o + + obj-$(CONFIG_SFC) += sfc.o +--- head-2009-11-06.orig/drivers/net/sfc/driverlink.c 2009-07-28 10:04:25.000000000 +0200 ++++ head-2009-11-06/drivers/net/sfc/driverlink.c 2009-10-12 13:41:03.000000000 +0200 +@@ -14,7 +14,6 @@ + #include + #include "net_driver.h" + #include "efx.h" +-#include "driverlink_api.h" + #include "driverlink.h" + + /* Protects @efx_driverlink_lock and @efx_driver_list */ +--- head-2009-11-06.orig/drivers/net/sfc/driverlink.h 2009-07-28 10:04:25.000000000 +0200 ++++ head-2009-11-06/drivers/net/sfc/driverlink.h 2009-10-12 13:41:03.000000000 +0200 +@@ -15,6 +15,10 @@ + struct efx_dl_device; + struct efx_nic; + ++#ifdef CONFIG_SFC_DRIVERLINK ++ ++#include "driverlink_api.h" ++ + /* Efx callback devices + * + * A list of the devices that own each callback. The partner to +@@ -40,4 +44,23 @@ extern void efx_dl_unregister_nic(struct + extern void efx_dl_reset_suspend(struct efx_nic *efx); + extern void efx_dl_reset_resume(struct efx_nic *efx, int ok); + ++#define EFX_DL_LOG EFX_LOG ++ ++#else /* CONFIG_SFC_DRIVERLINK */ ++ ++enum efx_veto { EFX_ALLOW_PACKET = 0 }; ++ ++static inline int efx_nop_callback(struct efx_nic *efx) { return 0; } ++#define EFX_DL_CALLBACK(port, name, ...) efx_nop_callback(port) ++ ++static inline int efx_dl_register_nic(struct efx_nic *efx) { return 0; } ++static inline void efx_dl_unregister_nic(struct efx_nic *efx) {} ++ ++static inline void efx_dl_reset_suspend(struct efx_nic *efx) {} ++static inline void efx_dl_reset_resume(struct efx_nic *efx, int ok) {} ++ ++#define EFX_DL_LOG(efx, fmt, args...) ((void)(efx)) ++ ++#endif /* CONFIG_SFC_DRIVERLINK */ ++ + #endif /* EFX_DRIVERLINK_H */ +--- head-2009-11-06.orig/drivers/net/sfc/efx.c 2009-10-12 13:40:25.000000000 +0200 ++++ head-2009-11-06/drivers/net/sfc/efx.c 2009-10-12 13:41:03.000000000 +0200 +@@ -1689,6 +1689,7 @@ static void efx_unregister_netdev(struct + * Device reset and suspend + * + **************************************************************************/ ++#ifdef CONFIG_SFC_DRIVERLINK + /* Serialise access to the driverlink callbacks, by quiescing event processing + * (without flushing the descriptor queues), and acquiring the rtnl_lock */ + void efx_suspend(struct efx_nic *efx) +@@ -1706,6 +1707,7 @@ void efx_resume(struct efx_nic *efx) + efx_start_all(efx); + rtnl_unlock(); + } ++#endif + + /* Tears down the entire software state and most of the hardware state + * before reset. */ +@@ -1978,9 +1980,11 @@ static int efx_init_struct(struct efx_ni + efx->mac_op = &efx_dummy_mac_operations; + efx->phy_op = &efx_dummy_phy_operations; + efx->mdio.dev = net_dev; ++#ifdef CONFIG_SFC_DRIVERLINK + INIT_LIST_HEAD(&efx->dl_node); + INIT_LIST_HEAD(&efx->dl_device_list); + efx->dl_cb = efx_default_callbacks; ++#endif + INIT_WORK(&efx->phy_work, efx_phy_work); + INIT_WORK(&efx->mac_work, efx_mac_work); + atomic_set(&efx->netif_stop_count, 1); +--- head-2009-11-06.orig/drivers/net/sfc/falcon.c 2009-07-28 10:04:25.000000000 +0200 ++++ head-2009-11-06/drivers/net/sfc/falcon.c 2009-10-12 13:41:03.000000000 +0200 +@@ -36,6 +36,7 @@ + + /** + * struct falcon_nic_data - Falcon NIC state ++ * @next_buffer_table: First available buffer table id + * @resources: Resource information for driverlink client + * @pci_dev2: The secondary PCI device if present + * @i2c_data: Operations and state for I2C bit-bashing algorithm +@@ -43,7 +44,11 @@ + * @int_error_expire: Time at which error count will be expired + */ + struct falcon_nic_data { ++#ifndef CONFIG_SFC_DRIVERLINK ++ unsigned next_buffer_table; ++#else + struct efx_dl_falcon_resources resources; ++#endif + struct pci_dev *pci_dev2; + struct i2c_algo_bit_data i2c_data; + +@@ -336,8 +341,13 @@ static int falcon_alloc_special_buffer(s + memset(buffer->addr, 0xff, len); + + /* Select new buffer ID */ ++#ifndef CONFIG_SFC_DRIVERLINK ++ buffer->index = nic_data->next_buffer_table; ++ nic_data->next_buffer_table += buffer->entries; ++#else + buffer->index = nic_data->resources.buffer_table_min; + nic_data->resources.buffer_table_min += buffer->entries; ++#endif + + EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, +@@ -2755,6 +2765,7 @@ static int falcon_probe_nvconfig(struct + * should live. */ + static int falcon_dimension_resources(struct efx_nic *efx) + { ++#ifdef CONFIG_SFC_DRIVERLINK + unsigned internal_dcs_entries; + struct falcon_nic_data *nic_data = efx->nic_data; + struct efx_dl_falcon_resources *res = &nic_data->resources; +@@ -2799,6 +2810,7 @@ static int falcon_dimension_resources(st + + if (EFX_INT_MODE_USE_MSI(efx)) + res->flags |= EFX_DL_FALCON_USE_MSI; ++#endif + + return 0; + } +@@ -2962,7 +2974,9 @@ int falcon_probe_nic(struct efx_nic *efx + return 0; + + fail6: ++#ifdef CONFIG_SFC_DRIVERLINK + efx->dl_info = NULL; ++#endif + fail5: + falcon_remove_spi_devices(efx); + falcon_free_buffer(efx, &efx->irq_status); +@@ -3150,7 +3164,9 @@ void falcon_remove_nic(struct efx_nic *e + /* Tear down the private nic state */ + kfree(efx->nic_data); + efx->nic_data = NULL; ++#ifdef CONFIG_SFC_DRIVERLINK + efx->dl_info = NULL; ++#endif + } + + void falcon_update_nic_stats(struct efx_nic *efx) +--- head-2009-11-06.orig/drivers/net/sfc/net_driver.h 2009-07-28 10:04:25.000000000 +0200 ++++ head-2009-11-06/drivers/net/sfc/net_driver.h 2009-10-12 13:41:03.000000000 +0200 +@@ -29,7 +29,6 @@ + + #include "enum.h" + #include "bitfield.h" +-#include "driverlink_api.h" + #include "driverlink.h" + + /************************************************************************** +@@ -854,11 +853,13 @@ struct efx_nic { + void *loopback_selftest; + + const char *silicon_rev; ++#ifdef CONFIG_SFC_DRIVERLINK + struct efx_dl_device_info *dl_info; + struct list_head dl_node; + struct list_head dl_device_list; + struct efx_dl_callbacks dl_cb; + struct efx_dl_cb_devices dl_cb_dev; ++#endif + }; + + static inline int efx_dev_registered(struct efx_nic *efx) +--- head-2009-11-06.orig/drivers/net/sfc/rx.c 2009-11-06 10:32:03.000000000 +0100 ++++ head-2009-11-06/drivers/net/sfc/rx.c 2009-11-06 10:32:24.000000000 +0100 +@@ -456,8 +456,8 @@ static void efx_rx_packet_lro(struct efx + * an obvious interface to this, so veto packets before LRO */ + veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len); + if (unlikely(veto)) { +- EFX_TRACE(efx, "LRO RX vetoed by driverlink %s driver\n", +- efx->dl_cb_dev.rx_packet->driver->name); ++ EFX_DL_LOG(efx, "LRO RX vetoed by driverlink %s driver\n", ++ efx->dl_cb_dev.rx_packet->driver->name); + /* Free the buffer now */ + efx_free_rx_buffer(efx, rx_buf); + return; +@@ -579,8 +579,8 @@ void __efx_rx_packet(struct efx_channel + /* Allow callback to veto the packet */ + veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len); + if (unlikely(veto)) { +- EFX_LOG(efx, "RX vetoed by driverlink %s driver\n", +- efx->dl_cb_dev.rx_packet->driver->name); ++ EFX_DL_LOG(efx, "RX vetoed by driverlink %s driver\n", ++ efx->dl_cb_dev.rx_packet->driver->name); + /* Free the buffer now */ + efx_free_rx_buffer(efx, rx_buf); + goto done; +--- head-2009-11-06.orig/drivers/net/sfc/tx.c 2009-10-12 13:40:32.000000000 +0200 ++++ head-2009-11-06/drivers/net/sfc/tx.c 2009-10-12 13:41:03.000000000 +0200 +@@ -387,9 +387,9 @@ netdev_tx_t efx_hard_start_xmit(struct s + /* See if driverlink wants to veto the packet. */ + veto = EFX_DL_CALLBACK(efx, tx_packet, skb); + if (unlikely(veto)) { +- EFX_TRACE(efx, "TX queue %d packet vetoed by " +- "driverlink %s driver\n", tx_queue->queue, +- efx->dl_cb_dev.tx_packet->driver->name); ++ EFX_DL_LOG(efx, "TX queue %d packet vetoed by " ++ "driverlink %s driver\n", tx_queue->queue, ++ efx->dl_cb_dev.tx_packet->driver->name); + /* Free the skb; nothing else will do it */ + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; diff --git a/patches.xen/sfc-endianness b/patches.xen/sfc-endianness new file mode 100644 index 0000000..1f6dc3d --- /dev/null +++ b/patches.xen/sfc-endianness @@ -0,0 +1,18 @@ +From: jbeulich@novell.com +Subject: fix building with gcc 4.4 +Patch-mainline: n/a + +--- head-2009-05-19.orig/drivers/net/sfc/sfc_resource/ci/efhw/hardware_sysdep.h 2008-07-17 16:18:07.000000000 +0200 ++++ head-2009-05-19/drivers/net/sfc/sfc_resource/ci/efhw/hardware_sysdep.h 2009-05-19 15:44:02.000000000 +0200 +@@ -42,9 +42,9 @@ + + #include + +-#ifdef __LITTLE_ENDIAN ++#if defined(__LITTLE_ENDIAN) + #define EFHW_IS_LITTLE_ENDIAN +-#elif __BIG_ENDIAN ++#elif defined(__BIG_ENDIAN) + #define EFHW_IS_BIG_ENDIAN + #else + #error Unknown endianness diff --git a/patches.xen/sfc-external-sram b/patches.xen/sfc-external-sram new file mode 100644 index 0000000..176e5c7 --- /dev/null +++ b/patches.xen/sfc-external-sram @@ -0,0 +1,299 @@ +From: Kieran Mansley +Subject: enable access to Falcon's external SRAM +References: bnc#489105 +Patch-mainline: n/a + +Include ability to reference external SRAM on Solarflare Falcon NICs to +allow event queues to be accessed by virtualised guests. + +Acked-by: jbeulich@novell.com + +--- head-2009-07-28.orig/drivers/net/sfc/falcon.c 2009-07-28 10:05:40.000000000 +0200 ++++ head-2009-07-28/drivers/net/sfc/falcon.c 2009-07-28 10:06:53.000000000 +0200 +@@ -36,6 +36,9 @@ + + /** + * struct falcon_nic_data - Falcon NIC state ++ * @sram_cfg: SRAM configuration value ++ * @tx_dc_base: Base address in SRAM of TX queue descriptor caches ++ * @rx_dc_base: Base address in SRAM of RX queue descriptor caches + * @next_buffer_table: First available buffer table id + * @resources: Resource information for driverlink client + * @pci_dev2: The secondary PCI device if present +@@ -44,6 +47,9 @@ + * @int_error_expire: Time at which error count will be expired + */ + struct falcon_nic_data { ++ int sram_cfg; ++ unsigned tx_dc_base; ++ unsigned rx_dc_base; + #ifndef CONFIG_SFC_DRIVERLINK + unsigned next_buffer_table; + #else +@@ -74,11 +80,11 @@ static int disable_dma_stats; + */ + #define TX_DC_ENTRIES 16 + #define TX_DC_ENTRIES_ORDER 0 +-#define TX_DC_BASE 0x130000 ++#define TX_DC_INTERNAL_BASE 0x130000 + + #define RX_DC_ENTRIES 64 + #define RX_DC_ENTRIES_ORDER 2 +-#define RX_DC_BASE 0x100000 ++#define RX_DC_INTERNAL_BASE 0x100000 + + static const unsigned int + /* "Large" EEPROM device: Atmel AT25640 or similar +@@ -468,9 +474,17 @@ void falcon_push_buffers(struct efx_tx_q + int falcon_probe_tx(struct efx_tx_queue *tx_queue) + { + struct efx_nic *efx = tx_queue->efx; +- return falcon_alloc_special_buffer(efx, &tx_queue->txd, +- FALCON_TXD_RING_SIZE * +- sizeof(efx_qword_t)); ++ int rc = falcon_alloc_special_buffer(efx, &tx_queue->txd, ++ FALCON_TXD_RING_SIZE * ++ sizeof(efx_qword_t)); ++#ifdef CONFIG_SFC_DRIVERLINK ++ if (rc == 0) { ++ struct falcon_nic_data *nic_data = efx->nic_data; ++ nic_data->resources.txq_min = max(nic_data->resources.txq_min, ++ (unsigned)tx_queue->queue + 1); ++ } ++#endif ++ return rc; + } + + void falcon_init_tx(struct efx_tx_queue *tx_queue) +@@ -610,9 +624,17 @@ void falcon_notify_rx_desc(struct efx_rx + int falcon_probe_rx(struct efx_rx_queue *rx_queue) + { + struct efx_nic *efx = rx_queue->efx; +- return falcon_alloc_special_buffer(efx, &rx_queue->rxd, +- FALCON_RXD_RING_SIZE * +- sizeof(efx_qword_t)); ++ int rc = falcon_alloc_special_buffer(efx, &rx_queue->rxd, ++ FALCON_RXD_RING_SIZE * ++ sizeof(efx_qword_t)); ++#ifdef CONFIG_SFC_DRIVERLINK ++ if (rc == 0) { ++ struct falcon_nic_data *nic_data = efx->nic_data; ++ nic_data->resources.rxq_min = max(nic_data->resources.rxq_min, ++ (unsigned)rx_queue->queue + 1); ++ } ++#endif ++ return rc; + } + + void falcon_init_rx(struct efx_rx_queue *rx_queue) +@@ -1120,9 +1142,18 @@ int falcon_probe_eventq(struct efx_chann + { + struct efx_nic *efx = channel->efx; + unsigned int evq_size; ++ int rc; + + evq_size = FALCON_EVQ_SIZE * sizeof(efx_qword_t); +- return falcon_alloc_special_buffer(efx, &channel->eventq, evq_size); ++ rc = falcon_alloc_special_buffer(efx, &channel->eventq, evq_size); ++#ifdef CONFIG_SFC_DRIVERLINK ++ if (rc == 0) { ++ struct falcon_nic_data *nic_data = efx->nic_data; ++ nic_data->resources.evq_int_min = max(nic_data->resources.evq_int_min, ++ (unsigned)channel->channel + 1); ++ } ++#endif ++ return rc; + } + + void falcon_init_eventq(struct efx_channel *channel) +@@ -2618,19 +2649,22 @@ fail5: + */ + static int falcon_reset_sram(struct efx_nic *efx) + { ++ struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t srm_cfg_reg_ker, gpio_cfg_reg_ker; +- int count; ++ int count, onchip, sram_cfg_val; + + /* Set the SRAM wake/sleep GPIO appropriately. */ ++ onchip = (nic_data->sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY); + falcon_read(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER); + EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OEN, 1); +- EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OUT, 1); ++ EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OUT, onchip); + falcon_write(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER); + + /* Initiate SRAM reset */ ++ sram_cfg_val = onchip ? 0 : nic_data->sram_cfg; + EFX_POPULATE_OWORD_2(srm_cfg_reg_ker, + SRAM_OOB_BT_INIT_EN, 1, +- SRM_NUM_BANKS_AND_BANK_SIZE, 0); ++ SRM_NUM_BANKS_AND_BANK_SIZE, sram_cfg_val); + falcon_write(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER); + + /* Wait for SRAM reset to complete */ +@@ -2702,8 +2736,10 @@ static void falcon_remove_spi_devices(st + /* Extract non-volatile configuration */ + static int falcon_probe_nvconfig(struct efx_nic *efx) + { ++ struct falcon_nic_data *nic_data = efx->nic_data; + struct falcon_nvconfig *nvconfig; + int board_rev; ++ bool onchip_sram; + int rc; + + nvconfig = kmalloc(sizeof(*nvconfig), GFP_KERNEL); +@@ -2716,6 +2752,7 @@ static int falcon_probe_nvconfig(struct + efx->phy_type = PHY_TYPE_NONE; + efx->mdio.prtad = MDIO_PRTAD_NONE; + board_rev = 0; ++ onchip_sram = true; + rc = 0; + } else if (rc) { + goto fail1; +@@ -2726,6 +2763,13 @@ static int falcon_probe_nvconfig(struct + efx->phy_type = v2->port0_phy_type; + efx->mdio.prtad = v2->port0_phy_addr; + board_rev = le16_to_cpu(v2->board_revision); ++#ifdef CONFIG_SFC_DRIVERLINK ++ onchip_sram = EFX_OWORD_FIELD(nvconfig->nic_stat_reg, ++ ONCHIP_SRAM); ++#else ++ /* We have no use for external SRAM */ ++ onchip_sram = true; ++#endif + + if (le16_to_cpu(nvconfig->board_struct_ver) >= 3) { + __le32 fl = v3->spi_device_type[EE_SPI_FLASH]; +@@ -2750,6 +2794,21 @@ static int falcon_probe_nvconfig(struct + + efx_set_board_info(efx, board_rev); + ++ /* Read the SRAM configuration. The register is initialised ++ * automatically but might may been reset since boot. ++ */ ++ if (onchip_sram) { ++ nic_data->sram_cfg = SRM_NB_BSZ_ONCHIP_ONLY; ++ } else { ++ nic_data->sram_cfg = ++ EFX_OWORD_FIELD(nvconfig->srm_cfg_reg, ++ SRM_NUM_BANKS_AND_BANK_SIZE); ++ WARN_ON(nic_data->sram_cfg == SRM_NB_BSZ_RESERVED); ++ /* Replace invalid setting with the smallest defaults */ ++ if (nic_data->sram_cfg == SRM_NB_BSZ_DEFAULT) ++ nic_data->sram_cfg = SRM_NB_BSZ_1BANKS_2M; ++ } ++ + kfree(nvconfig); + return 0; + +@@ -2765,9 +2824,9 @@ static int falcon_probe_nvconfig(struct + * should live. */ + static int falcon_dimension_resources(struct efx_nic *efx) + { ++ struct falcon_nic_data *nic_data = efx->nic_data; + #ifdef CONFIG_SFC_DRIVERLINK + unsigned internal_dcs_entries; +- struct falcon_nic_data *nic_data = efx->nic_data; + struct efx_dl_falcon_resources *res = &nic_data->resources; + + /* Fill out the driverlink resource list */ +@@ -2800,16 +2859,64 @@ static int falcon_dimension_resources(st + break; + } + +- /* Internal SRAM only for now */ +- res->rxq_lim = internal_dcs_entries / RX_DC_ENTRIES; +- res->txq_lim = internal_dcs_entries / TX_DC_ENTRIES; +- res->buffer_table_lim = 8192; ++ if (nic_data->sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY) { ++ res->rxq_lim = internal_dcs_entries / RX_DC_ENTRIES; ++ res->txq_lim = internal_dcs_entries / TX_DC_ENTRIES; ++ res->buffer_table_lim = 8192; ++ nic_data->tx_dc_base = TX_DC_INTERNAL_BASE; ++ nic_data->rx_dc_base = RX_DC_INTERNAL_BASE; ++ } else { ++ unsigned sram_bytes, vnic_bytes, max_vnics, n_vnics, dcs; ++ ++ /* Determine how much SRAM we have to play with. We have ++ * to fit buffer table and descriptor caches in. ++ */ ++ switch (nic_data->sram_cfg) { ++ case SRM_NB_BSZ_1BANKS_2M: ++ default: ++ sram_bytes = 2 * 1024 * 1024; ++ break; ++ case SRM_NB_BSZ_1BANKS_4M: ++ case SRM_NB_BSZ_2BANKS_4M: ++ sram_bytes = 4 * 1024 * 1024; ++ break; ++ case SRM_NB_BSZ_1BANKS_8M: ++ case SRM_NB_BSZ_2BANKS_8M: ++ sram_bytes = 8 * 1024 * 1024; ++ break; ++ case SRM_NB_BSZ_2BANKS_16M: ++ sram_bytes = 16 * 1024 * 1024; ++ break; ++ } ++ /* For each VNIC allow at least 512 buffer table entries ++ * and descriptor cache for an rxq and txq. Buffer table ++ * space for evqs and dmaqs is relatively trivial, so not ++ * considered in this calculation. ++ */ ++ vnic_bytes = 512 * 8 + RX_DC_ENTRIES * 8 + TX_DC_ENTRIES * 8; ++ max_vnics = sram_bytes / vnic_bytes; ++ for (n_vnics = 1; n_vnics < res->evq_timer_min + max_vnics;) ++ n_vnics *= 2; ++ res->rxq_lim = n_vnics; ++ res->txq_lim = n_vnics; ++ ++ dcs = n_vnics * TX_DC_ENTRIES * 8; ++ nic_data->tx_dc_base = sram_bytes - dcs; ++ dcs = n_vnics * RX_DC_ENTRIES * 8; ++ nic_data->rx_dc_base = nic_data->tx_dc_base - dcs; ++ res->buffer_table_lim = nic_data->rx_dc_base / 8; ++ } + + if (FALCON_IS_DUAL_FUNC(efx)) + res->flags |= EFX_DL_FALCON_DUAL_FUNC; + + if (EFX_INT_MODE_USE_MSI(efx)) + res->flags |= EFX_DL_FALCON_USE_MSI; ++#else ++ /* We ignore external SRAM */ ++ EFX_BUG_ON_PARANOID(nic_data->sram_cfg != SRM_NB_BSZ_ONCHIP_ONLY); ++ nic_data->tx_dc_base = TX_DC_INTERNAL_BASE; ++ nic_data->rx_dc_base = RX_DC_INTERNAL_BASE; + #endif + + return 0; +@@ -2998,13 +3105,15 @@ int falcon_probe_nic(struct efx_nic *efx + */ + int falcon_init_nic(struct efx_nic *efx) + { ++ struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t temp; + unsigned thresh; + int rc; + +- /* Use on-chip SRAM */ ++ /* Use on-chip SRAM if wanted. */ + falcon_read(efx, &temp, NIC_STAT_REG); +- EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, 1); ++ EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, ++ nic_data->sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY); + falcon_write(efx, &temp, NIC_STAT_REG); + + /* Set the source of the GMAC clock */ +@@ -3023,9 +3132,9 @@ int falcon_init_nic(struct efx_nic *efx) + return rc; + + /* Set positions of descriptor caches in SRAM. */ +- EFX_POPULATE_OWORD_1(temp, SRM_TX_DC_BASE_ADR, TX_DC_BASE / 8); ++ EFX_POPULATE_OWORD_1(temp, SRM_TX_DC_BASE_ADR, nic_data->tx_dc_base / 8); + falcon_write(efx, &temp, SRM_TX_DC_CFG_REG_KER); +- EFX_POPULATE_OWORD_1(temp, SRM_RX_DC_BASE_ADR, RX_DC_BASE / 8); ++ EFX_POPULATE_OWORD_1(temp, SRM_RX_DC_BASE_ADR, nic_data->rx_dc_base / 8); + falcon_write(efx, &temp, SRM_RX_DC_CFG_REG_KER); + + /* Set TX descriptor cache size. */ diff --git a/patches.xen/sfc-resource-driver b/patches.xen/sfc-resource-driver new file mode 100644 index 0000000..ae5345e --- /dev/null +++ b/patches.xen/sfc-resource-driver @@ -0,0 +1,15053 @@ +From: David Riddoch +# replaces http://xenbits.xensource.com/linux-2.6.18-xen.hg c/s 421: +# HG changeset patch +# User Keir Fraser +# Date 1203330569 0 +# Node ID e4dd072db2595c420bb21d9e835416f4fd543526 +# Parent fc90e9b2c12b316b5460ece28f013e6de881af1a +Subject: Solarflare: Resource driver. +References: FATE#303479 +Patch-mainline: n/a +Acked-by: jbeulich@novell.com + +--- head-2009-04-21.orig/drivers/net/sfc/Kconfig 2009-04-21 11:01:52.000000000 +0200 ++++ head-2009-04-21/drivers/net/sfc/Kconfig 2009-04-21 11:02:22.000000000 +0200 +@@ -11,6 +11,13 @@ config SFC + + To compile this driver as a module, choose M here. The module + will be called sfc. ++ ++config SFC_RESOURCE ++ depends on SFC && X86 ++ tristate "Solarflare Solarstorm SFC4000 resource driver" ++ help ++ This module provides the SFC resource manager driver. ++ + config SFC_MTD + bool "Solarflare Solarstorm SFC4000 flash MTD support" + depends on SFC && MTD && !(SFC=y && MTD=m) +--- head-2009-04-21.orig/drivers/net/sfc/Makefile 2009-04-21 11:01:52.000000000 +0200 ++++ head-2009-04-21/drivers/net/sfc/Makefile 2009-02-06 12:42:18.000000000 +0100 +@@ -5,3 +5,5 @@ sfc-y += efx.o falcon.o tx.o rx.o falc + sfc-$(CONFIG_SFC_MTD) += mtd.o + + obj-$(CONFIG_SFC) += sfc.o ++ ++obj-$(CONFIG_SFC_RESOURCE) += sfc_resource/ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/Makefile 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,14 @@ ++obj-$(CONFIG_SFC_RESOURCE) := sfc_resource.o ++ ++EXTRA_CFLAGS += -D__CI_HARDWARE_CONFIG_FALCON__ ++EXTRA_CFLAGS += -D__ci_driver__ ++EXTRA_CFLAGS += -Werror ++EXTRA_CFLAGS += -Idrivers/net/sfc -Idrivers/net/sfc/sfc_resource ++ ++sfc_resource-objs := resource_driver.o iopage.o efx_vi_shm.o \ ++ driverlink_new.o kernel_proc.o kfifo.o \ ++ nic.o eventq.o falcon.o falcon_hash.o \ ++ assert_valid.o buddy.o buffer_table.o filter_resource.o \ ++ iobufset_resource.o resource_manager.o resources.o \ ++ vi_resource_alloc.o vi_resource_event.o vi_resource_flush.o \ ++ vi_resource_manager.o driver_object.o kernel_compat.o +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/assert_valid.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,92 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains functions to assert validness of resources and ++ * resource manager in DEBUG build of the resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++ ++#ifndef NDEBUG ++#include ++#include ++#include ++ ++void ++efrm_resource_manager_assert_valid(struct efrm_resource_manager *rm, ++ const char *file, int line) ++{ ++ _EFRM_ASSERT(rm, file, line); ++ _EFRM_ASSERT(rm->rm_name, file, line); ++ _EFRM_ASSERT(rm->rm_type < EFRM_RESOURCE_NUM, file, line); ++ _EFRM_ASSERT(rm->rm_dtor, file, line); ++} ++EXPORT_SYMBOL(efrm_resource_manager_assert_valid); ++ ++/* ++ * \param rs resource to validate ++ * \param ref_count_is_zero One of 3 values ++ * > 0 - check ref count is zero ++ * = 0 - check ref count is non-zero ++ * < 0 - ref count could be any value ++ */ ++void ++efrm_resource_assert_valid(struct efrm_resource *rs, int ref_count_is_zero, ++ const char *file, int line) ++{ ++ struct efrm_resource_manager *rm; ++ ++ _EFRM_ASSERT(rs, file, line); ++ ++ if (ref_count_is_zero >= 0) { ++ if (!(ref_count_is_zero || rs->rs_ref_count > 0) ++ || !(!ref_count_is_zero || rs->rs_ref_count == 0)) ++ EFRM_WARN("%s: check %szero ref=%d " EFRM_RESOURCE_FMT, ++ __func__, ++ ref_count_is_zero == 0 ? "non-" : "", ++ rs->rs_ref_count, ++ EFRM_RESOURCE_PRI_ARG(rs->rs_handle)); ++ ++ _EFRM_ASSERT(!(ref_count_is_zero == 0) || ++ rs->rs_ref_count != 0, file, line); ++ _EFRM_ASSERT(!(ref_count_is_zero > 0) || ++ rs->rs_ref_count == 0, file, line); ++ } ++ ++ rm = efrm_rm_table[EFRM_RESOURCE_TYPE(rs->rs_handle)]; ++ efrm_resource_manager_assert_valid(rm, file, line); ++} ++EXPORT_SYMBOL(efrm_resource_assert_valid); ++ ++#endif +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/buddy.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,220 @@ ++ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains implementation of a buddy allocator. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include /* get uintXX types on win32 */ ++#include ++#include ++#include ++ ++#if 1 ++#define DEBUG_ALLOC(x) ++#else ++#define DEBUG_ALLOC(x) x ++ ++static inline void efrm_buddy_dump(struct efrm_buddy_allocator *b) ++{ ++ unsigned o; ++ ++ EFRM_NOTICE("%s: dump allocator with order %u", ++ __func__, b->order); ++ for (o = 0; o <= b->order; o++) { ++ struct list_head *l = &b->free_lists[o]; ++ while (l->next != &b->free_lists[o]) { ++ l = l->next; ++ EFRM_NOTICE("%s: order %x: %zx", __func__, o, ++ l - b->links); ++ } ++ } ++} ++#endif ++ ++/* ++ * The purpose of the following inline functions is to give the ++ * understandable names to the simple actions. ++ */ ++static inline void ++efrm_buddy_free_list_add(struct efrm_buddy_allocator *b, ++ unsigned order, unsigned addr) ++{ ++ list_add(&b->links[addr], &b->free_lists[order]); ++ b->orders[addr] = (uint8_t) order; ++} ++static inline void ++efrm_buddy_free_list_del(struct efrm_buddy_allocator *b, unsigned addr) ++{ ++ list_del(&b->links[addr]); ++ b->links[addr].next = NULL; ++} ++static inline int ++efrm_buddy_free_list_empty(struct efrm_buddy_allocator *b, unsigned order) ++{ ++ return list_empty(&b->free_lists[order]); ++} ++static inline unsigned ++efrm_buddy_free_list_pop(struct efrm_buddy_allocator *b, unsigned order) ++{ ++ struct list_head *l = list_pop(&b->free_lists[order]); ++ l->next = NULL; ++ return (unsigned)(l - b->links); ++} ++static inline int ++efrm_buddy_addr_in_free_list(struct efrm_buddy_allocator *b, unsigned addr) ++{ ++ return b->links[addr].next != NULL; ++} ++static inline unsigned ++efrm_buddy_free_list_first(struct efrm_buddy_allocator *b, unsigned order) ++{ ++ return (unsigned)(b->free_lists[order].next - b->links); ++} ++ ++int efrm_buddy_ctor(struct efrm_buddy_allocator *b, unsigned order) ++{ ++ unsigned o; ++ unsigned size = 1 << order; ++ ++ DEBUG_ALLOC(EFRM_NOTICE("%s(%u)", __func__, order)); ++ EFRM_ASSERT(b); ++ EFRM_ASSERT(order <= sizeof(unsigned) * 8 - 1); ++ ++ b->order = order; ++ b->free_lists = vmalloc((order + 1) * sizeof(struct list_head)); ++ if (b->free_lists == NULL) ++ goto fail1; ++ ++ b->links = vmalloc(size * sizeof(struct list_head)); ++ if (b->links == NULL) ++ goto fail2; ++ ++ b->orders = vmalloc(size); ++ if (b->orders == NULL) ++ goto fail3; ++ ++ memset(b->links, 0, size * sizeof(struct list_head)); ++ ++ for (o = 0; o <= b->order; ++o) ++ INIT_LIST_HEAD(b->free_lists + o); ++ ++ efrm_buddy_free_list_add(b, b->order, 0); ++ ++ return 0; ++ ++fail3: ++ vfree(b->links); ++fail2: ++ vfree(b->free_lists); ++fail1: ++ return -ENOMEM; ++} ++ ++void efrm_buddy_dtor(struct efrm_buddy_allocator *b) ++{ ++ EFRM_ASSERT(b); ++ ++ vfree(b->free_lists); ++ vfree(b->links); ++ vfree(b->orders); ++} ++ ++int efrm_buddy_alloc(struct efrm_buddy_allocator *b, unsigned order) ++{ ++ unsigned smallest; ++ unsigned addr; ++ ++ DEBUG_ALLOC(EFRM_NOTICE("%s(%u)", __func__, order)); ++ EFRM_ASSERT(b); ++ ++ /* Find smallest chunk that is big enough. ?? Can optimise this by ++ ** keeping array of pointers to smallest chunk for each order. ++ */ ++ smallest = order; ++ while (smallest <= b->order && ++ efrm_buddy_free_list_empty(b, smallest)) ++ ++smallest; ++ ++ if (smallest > b->order) { ++ DEBUG_ALLOC(EFRM_NOTICE ++ ("buddy - alloc order %d failed - max order %d", ++ order, b->order);); ++ return -ENOMEM; ++ } ++ ++ /* Split blocks until we get one of the correct size. */ ++ addr = efrm_buddy_free_list_pop(b, smallest); ++ ++ DEBUG_ALLOC(EFRM_NOTICE("buddy - alloc %x order %d cut from order %d", ++ addr, order, smallest);); ++ while (smallest-- > order) ++ efrm_buddy_free_list_add(b, smallest, addr + (1 << smallest)); ++ ++ EFRM_DO_DEBUG(b->orders[addr] = (uint8_t) order); ++ ++ EFRM_ASSERT(addr < 1u << b->order); ++ return addr; ++} ++ ++void ++efrm_buddy_free(struct efrm_buddy_allocator *b, unsigned addr, ++ unsigned order) ++{ ++ unsigned buddy_addr; ++ ++ DEBUG_ALLOC(EFRM_NOTICE("%s(%u, %u)", __func__, addr, order)); ++ EFRM_ASSERT(b); ++ EFRM_ASSERT(order <= b->order); ++ EFRM_ASSERT((unsigned long)addr + ((unsigned long)1 << order) <= ++ (unsigned long)1 << b->order); ++ EFRM_ASSERT(!efrm_buddy_addr_in_free_list(b, addr)); ++ EFRM_ASSERT(b->orders[addr] == order); ++ ++ /* merge free blocks */ ++ while (order < b->order) { ++ buddy_addr = addr ^ (1 << order); ++ if (!efrm_buddy_addr_in_free_list(b, buddy_addr) || ++ b->orders[buddy_addr] != order) ++ break; ++ efrm_buddy_free_list_del(b, buddy_addr); ++ if (buddy_addr < addr) ++ addr = buddy_addr; ++ ++order; ++ } ++ ++ DEBUG_ALLOC(EFRM_NOTICE ++ ("buddy - free %x merged into order %d", addr, order);); ++ efrm_buddy_free_list_add(b, order, addr); ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/buffer_table.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,209 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains abstraction of the buffer table on the NIC. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++/* ++** Might be worth keeping a bitmap of which entries are clear. Then we ++** wouldn't need to clear them all again when we free an allocation. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/*! Comment? */ ++struct efrm_buffer_table { ++ spinlock_t lock; ++ struct efrm_buddy_allocator buddy; ++}; ++ ++/* Efab buffer state. */ ++static struct efrm_buffer_table efrm_buffers; ++ ++int efrm_buffer_table_ctor(unsigned low, unsigned high) ++{ ++ int log2_n_entries, rc, i; ++ ++ EFRM_ASSERT(high > 0); ++ EFRM_ASSERT(low < high); ++ ++ EFRM_TRACE("%s: low=%u high=%u", __func__, low, high); ++ EFRM_NOTICE("%s: low=%u high=%u", __func__, low, high); ++ ++ log2_n_entries = fls(high - 1); ++ ++ rc = efrm_buddy_ctor(&efrm_buffers.buddy, log2_n_entries); ++ if (rc < 0) { ++ EFRM_ERR("efrm_buffer_table_ctor: efrm_buddy_ctor(%d) " ++ "failed (%d)", log2_n_entries, rc); ++ return rc; ++ } ++ for (i = 0; i < (1 << log2_n_entries); ++i) { ++ rc = efrm_buddy_alloc(&efrm_buffers.buddy, 0); ++ EFRM_ASSERT(rc >= 0); ++ EFRM_ASSERT(rc < (1 << log2_n_entries)); ++ } ++ for (i = low; i < (int) high; ++i) ++ efrm_buddy_free(&efrm_buffers.buddy, i, 0); ++ ++ spin_lock_init(&efrm_buffers.lock); ++ ++ EFRM_TRACE("%s: done", __func__); ++ ++ return 0; ++} ++ ++void efrm_buffer_table_dtor(void) ++{ ++ /* ?? debug check that all allocations have been freed? */ ++ ++ spin_lock_destroy(&efrm_buffers.lock); ++ efrm_buddy_dtor(&efrm_buffers.buddy); ++ ++ EFRM_TRACE("%s: done", __func__); ++} ++ ++/**********************************************************************/ ++ ++int ++efrm_buffer_table_alloc(unsigned order, ++ struct efhw_buffer_table_allocation *a) ++{ ++ irq_flags_t lock_flags; ++ int rc; ++ ++ EFRM_ASSERT(&efrm_buffers.buddy); ++ EFRM_ASSERT(a); ++ ++ /* Round up to multiple of two, as the buffer clear logic works in ++ * pairs when not in "full" mode. */ ++ order = max_t(unsigned, order, 1); ++ ++ spin_lock_irqsave(&efrm_buffers.lock, lock_flags); ++ rc = efrm_buddy_alloc(&efrm_buffers.buddy, order); ++ spin_unlock_irqrestore(&efrm_buffers.lock, lock_flags); ++ ++ if (rc < 0) { ++ EFRM_ERR("efrm_buffer_table_alloc: failed (n=%ld) rc %d", ++ 1ul << order, rc); ++ return rc; ++ } ++ ++ EFRM_TRACE("efrm_buffer_table_alloc: base=%d n=%ld", ++ rc, 1ul << order); ++ a->order = order; ++ a->base = (unsigned)rc; ++ return 0; ++} ++ ++void efrm_buffer_table_free(struct efhw_buffer_table_allocation *a) ++{ ++ irq_flags_t lock_flags; ++ struct efhw_nic *nic; ++ int nic_i; ++ ++ EFRM_ASSERT(&efrm_buffers.buddy); ++ EFRM_ASSERT(a); ++ EFRM_ASSERT(a->base != -1); ++ EFRM_ASSERT((unsigned long)a->base + (1ul << a->order) <= ++ efrm_buddy_size(&efrm_buffers.buddy)); ++ ++ EFRM_TRACE("efrm_buffer_table_free: base=%d n=%ld", ++ a->base, (1ul << a->order)); ++ ++ EFRM_FOR_EACH_NIC(nic_i, nic) ++ efhw_nic_buffer_table_clear(nic, a->base, 1ul << a->order); ++ ++ spin_lock_irqsave(&efrm_buffers.lock, lock_flags); ++ efrm_buddy_free(&efrm_buffers.buddy, a->base, a->order); ++ spin_unlock_irqrestore(&efrm_buffers.lock, lock_flags); ++ ++ EFRM_DO_DEBUG(a->base = a->order = -1); ++} ++ ++/**********************************************************************/ ++ ++void ++efrm_buffer_table_set(struct efhw_buffer_table_allocation *a, ++ struct efhw_nic *nic, ++ unsigned i, dma_addr_t dma_addr, int owner) ++{ ++ EFRM_ASSERT(a); ++ EFRM_ASSERT(i < (unsigned)1 << a->order); ++ ++ efhw_nic_buffer_table_set(nic, dma_addr, EFHW_NIC_PAGE_SIZE, ++ 0, owner, a->base + i); ++} ++ ++ ++int efrm_buffer_table_size(void) ++{ ++ return efrm_buddy_size(&efrm_buffers.buddy); ++} ++ ++/**********************************************************************/ ++ ++int ++efrm_page_register(struct efhw_nic *nic, dma_addr_t dma_addr, int owner, ++ efhw_buffer_addr_t *buf_addr_out) ++{ ++ struct efhw_buffer_table_allocation alloc; ++ int rc; ++ ++ rc = efrm_buffer_table_alloc(0, &alloc); ++ if (rc == 0) { ++ efrm_buffer_table_set(&alloc, nic, 0, dma_addr, owner); ++ efrm_buffer_table_commit(); ++ *buf_addr_out = EFHW_BUFFER_ADDR(alloc.base, 0); ++ } ++ return rc; ++} ++EXPORT_SYMBOL(efrm_page_register); ++ ++void efrm_page_unregister(efhw_buffer_addr_t buf_addr) ++{ ++ struct efhw_buffer_table_allocation alloc; ++ ++ alloc.order = 0; ++ alloc.base = EFHW_BUFFER_PAGE(buf_addr); ++ efrm_buffer_table_free(&alloc); ++} ++EXPORT_SYMBOL(efrm_page_unregister); ++ ++void efrm_buffer_table_commit(void) ++{ ++ struct efhw_nic *nic; ++ int nic_i; ++ ++ EFRM_FOR_EACH_NIC(nic_i, nic) ++ efhw_nic_buffer_table_commit(nic); ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/efab/hardware.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,188 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides EtherFabric NIC hardware interface. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_EFAB_HARDWARE_H__ ++#define __CI_DRIVER_EFAB_HARDWARE_H__ ++ ++#include "ci/driver/efab/hardware/workarounds.h" ++#include ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Common EtherFabric definitions ++ * ++ *---------------------------------------------------------------------------*/ ++ ++#include ++#include ++#include ++ ++/*---------------------------------------------------------------------------- ++ * ++ * EtherFabric varients ++ * ++ *---------------------------------------------------------------------------*/ ++ ++#include ++ ++/*---------------------------------------------------------------------------- ++ * ++ * EtherFabric Portable Hardware Layer defines ++ * ++ *---------------------------------------------------------------------------*/ ++ ++ /*-------------- Initialisation ------------ */ ++#define efhw_nic_close_hardware(nic) \ ++ ((nic)->efhw_func->close_hardware(nic)) ++ ++#define efhw_nic_init_hardware(nic, ev_handlers, mac_addr, non_irq_evq) \ ++ ((nic)->efhw_func->init_hardware((nic), (ev_handlers), (mac_addr), \ ++ (non_irq_evq))) ++ ++/*-------------- Interrupt support ------------ */ ++/** Handle interrupt. Return 0 if not handled, 1 if handled. */ ++#define efhw_nic_interrupt(nic) \ ++ ((nic)->efhw_func->interrupt(nic)) ++ ++#define efhw_nic_interrupt_enable(nic) \ ++ ((nic)->efhw_func->interrupt_enable(nic)) ++ ++#define efhw_nic_interrupt_disable(nic) \ ++ ((nic)->efhw_func->interrupt_disable(nic)) ++ ++#define efhw_nic_set_interrupt_moderation(nic, evq, val) \ ++ ((nic)->efhw_func->set_interrupt_moderation(nic, evq, val)) ++ ++/*-------------- Event support ------------ */ ++ ++#define efhw_nic_event_queue_enable(nic, evq, size, q_base, buf_base, \ ++ interrupting) \ ++ ((nic)->efhw_func->event_queue_enable((nic), (evq), (size), (q_base), \ ++ (buf_base), (interrupting))) ++ ++#define efhw_nic_event_queue_disable(nic, evq, timer_only) \ ++ ((nic)->efhw_func->event_queue_disable(nic, evq, timer_only)) ++ ++#define efhw_nic_wakeup_request(nic, q_base, index, evq) \ ++ ((nic)->efhw_func->wakeup_request(nic, q_base, index, evq)) ++ ++#define efhw_nic_sw_event(nic, data, ev) \ ++ ((nic)->efhw_func->sw_event(nic, data, ev)) ++ ++/*-------------- Filter support ------------ */ ++#define efhw_nic_ipfilter_set(nic, type, index, dmaq, \ ++ saddr, sport, daddr, dport) \ ++ ((nic)->efhw_func->ipfilter_set(nic, type, index, dmaq, \ ++ saddr, sport, daddr, dport)) ++ ++#define efhw_nic_ipfilter_clear(nic, index) \ ++ ((nic)->efhw_func->ipfilter_clear(nic, index)) ++ ++/*-------------- DMA support ------------ */ ++#define efhw_nic_dmaq_tx_q_init(nic, dmaq, evq, owner, tag, \ ++ dmaq_size, index, flags) \ ++ ((nic)->efhw_func->dmaq_tx_q_init(nic, dmaq, evq, owner, tag, \ ++ dmaq_size, index, flags)) ++ ++#define efhw_nic_dmaq_rx_q_init(nic, dmaq, evq, owner, tag, \ ++ dmaq_size, index, flags) \ ++ ((nic)->efhw_func->dmaq_rx_q_init(nic, dmaq, evq, owner, tag, \ ++ dmaq_size, index, flags)) ++ ++#define efhw_nic_dmaq_tx_q_disable(nic, dmaq) \ ++ ((nic)->efhw_func->dmaq_tx_q_disable(nic, dmaq)) ++ ++#define efhw_nic_dmaq_rx_q_disable(nic, dmaq) \ ++ ((nic)->efhw_func->dmaq_rx_q_disable(nic, dmaq)) ++ ++#define efhw_nic_flush_tx_dma_channel(nic, dmaq) \ ++ ((nic)->efhw_func->flush_tx_dma_channel(nic, dmaq)) ++ ++#define efhw_nic_flush_rx_dma_channel(nic, dmaq) \ ++ ((nic)->efhw_func->flush_rx_dma_channel(nic, dmaq)) ++ ++/*-------------- MAC Low level interface ---- */ ++#define efhw_gmac_get_mac_addr(nic) \ ++ ((nic)->gmac->get_mac_addr((nic)->gmac)) ++ ++/*-------------- Buffer table -------------- */ ++#define efhw_nic_buffer_table_set(nic, addr, bufsz, region, \ ++ own_id, buf_id) \ ++ ((nic)->efhw_func->buffer_table_set(nic, addr, bufsz, region, \ ++ own_id, buf_id)) ++ ++#define efhw_nic_buffer_table_set_n(nic, buf_id, addr, bufsz, \ ++ region, n_pages, own_id) \ ++ ((nic)->efhw_func->buffer_table_set_n(nic, buf_id, addr, bufsz, \ ++ region, n_pages, own_id)) ++ ++#define efhw_nic_buffer_table_clear(nic, id, num) \ ++ ((nic)->efhw_func->buffer_table_clear(nic, id, num)) ++ ++#define efhw_nic_buffer_table_commit(nic) \ ++ ((nic)->efhw_func->buffer_table_commit(nic)) ++ ++/*-------------- New filter API ------------ */ ++#define efhw_nic_filter_set(nic, spec, index_out) \ ++ ((nic)->efhw_func->filter_set(nic, spec, index_out)) ++ ++#define efhw_nic_filter_clear(nic, type, index_out) \ ++ ((nic)->efhw_func->filter_clear(nic, type, index_out)) ++ ++ ++/* --- DMA --- */ ++#define EFHW_DMA_ADDRMASK (0xffffffffffffffffULL) ++ ++/* --- Buffers --- */ ++#define EFHW_BUFFER_ADDR FALCON_BUFFER_4K_ADDR ++#define EFHW_BUFFER_PAGE FALCON_BUFFER_4K_PAGE ++#define EFHW_BUFFER_OFF FALCON_BUFFER_4K_OFF ++ ++/* --- Filters --- */ ++#define EFHW_IP_FILTER_NUM FALCON_FILTER_TBL_NUM ++ ++#define EFHW_MAX_PAGE_SIZE FALCON_MAX_PAGE_SIZE ++ ++#if PAGE_SIZE <= EFHW_MAX_PAGE_SIZE ++#define EFHW_NIC_PAGE_SIZE PAGE_SIZE ++#else ++#define EFHW_NIC_PAGE_SIZE EFHW_MAX_PAGE_SIZE ++#endif ++#define EFHW_NIC_PAGE_MASK (~(EFHW_NIC_PAGE_SIZE-1)) ++ ++#endif /* __CI_DRIVER_EFAB_HARDWARE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/efab/hardware/common.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,68 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides EtherFabric NIC hardware interface common ++ * definitions. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_EFAB_HARDWARE_COMMON_H__ ++#define __CI_DRIVER_EFAB_HARDWARE_COMMON_H__ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * EtherFabric constants ++ * ++ *---------------------------------------------------------------------------*/ ++ ++#define EFHW_1K 0x00000400u ++#define EFHW_2K 0x00000800u ++#define EFHW_4K 0x00001000u ++#define EFHW_8K 0x00002000u ++#define EFHW_16K 0x00004000u ++#define EFHW_32K 0x00008000u ++#define EFHW_64K 0x00010000u ++#define EFHW_128K 0x00020000u ++#define EFHW_256K 0x00040000u ++#define EFHW_512K 0x00080000u ++#define EFHW_1M 0x00100000u ++#define EFHW_2M 0x00200000u ++#define EFHW_4M 0x00400000u ++#define EFHW_8M 0x00800000u ++#define EFHW_16M 0x01000000u ++#define EFHW_32M 0x02000000u ++#define EFHW_48M 0x03000000u ++#define EFHW_64M 0x04000000u ++#define EFHW_128M 0x08000000u ++#define EFHW_256M 0x10000000u ++#define EFHW_512M 0x20000000u ++#define EFHW_1G 0x40000000u ++#define EFHW_2G 0x80000000u ++#define EFHW_4G 0x100000000ULL ++#define EFHW_8G 0x200000000ULL ++ ++#endif /* __CI_DRIVER_EFAB_HARDWARE_COMMON_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/efab/hardware/falcon.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,422 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides EtherFabric NIC - EFXXXX (aka Falcon) specific ++ * definitions. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_EFAB_HARDWARE_FALCON_H__ ++#define __CI_DRIVER_EFAB_HARDWARE_FALCON_H__ ++ ++/*---------------------------------------------------------------------------- ++ * Compile options ++ *---------------------------------------------------------------------------*/ ++ ++/* Falcon has an 8K maximum page size. */ ++#define FALCON_MAX_PAGE_SIZE EFHW_8K ++ ++/* include the register definitions */ ++#include ++#include ++#include ++#include ++ ++#define FALCON_DMA_TX_DESC_BYTES 8 ++#define FALCON_DMA_RX_PHYS_DESC_BYTES 8 ++#define FALCON_DMA_RX_BUF_DESC_BYTES 4 ++ ++ ++/* ---- efhw_event_t helpers --- */ ++ ++#ifndef EFHW_IS_LITTLE_ENDIAN ++#error This needs lots of cpu_to_le64s() in ++#endif ++ ++/*!\ TODO look at whether there is an efficiency gain to be had by ++ treating the event codes to 32bit masks as is done for EF1 ++ ++ These masks apply to the full 64 bits of the event to extract the ++ event code - followed by the common event codes to expect ++ */ ++#define __FALCON_OPEN_MASK(WIDTH) ((((uint64_t)1) << (WIDTH)) - 1) ++#define FALCON_EVENT_CODE_MASK \ ++ (__FALCON_OPEN_MASK(EV_CODE_WIDTH) << EV_CODE_LBN) ++#define FALCON_EVENT_EV_Q_ID_MASK \ ++ (__FALCON_OPEN_MASK(DRIVER_EV_EVQ_ID_WIDTH) << DRIVER_EV_EVQ_ID_LBN) ++#define FALCON_EVENT_TX_FLUSH_Q_ID_MASK \ ++ (__FALCON_OPEN_MASK(DRIVER_EV_TX_DESCQ_ID_WIDTH) << \ ++ DRIVER_EV_TX_DESCQ_ID_LBN) ++#define FALCON_EVENT_RX_FLUSH_Q_ID_MASK \ ++ (__FALCON_OPEN_MASK(DRIVER_EV_RX_DESCQ_ID_WIDTH) << \ ++ DRIVER_EV_RX_DESCQ_ID_LBN) ++#define FALCON_EVENT_DRV_SUBCODE_MASK \ ++ (__FALCON_OPEN_MASK(DRIVER_EV_SUB_CODE_WIDTH) << \ ++ DRIVER_EV_SUB_CODE_LBN) ++ ++#define FALCON_EVENT_FMT "[ev:%x:%08x:%08x]" ++#define FALCON_EVENT_PRI_ARG(e) \ ++ ((unsigned)(((e).u64 & FALCON_EVENT_CODE_MASK) >> EV_CODE_LBN)), \ ++ ((unsigned)((e).u64 >> 32)), ((unsigned)((e).u64 & 0xFFFFFFFF)) ++ ++#define FALCON_EVENT_CODE(evp) ((evp)->u64 & FALCON_EVENT_CODE_MASK) ++#define FALCON_EVENT_WAKE_EVQ_ID(evp) \ ++ (((evp)->u64 & FALCON_EVENT_EV_Q_ID_MASK) >> DRIVER_EV_EVQ_ID_LBN) ++#define FALCON_EVENT_TX_FLUSH_Q_ID(evp) \ ++ (((evp)->u64 & FALCON_EVENT_TX_FLUSH_Q_ID_MASK) >> \ ++ DRIVER_EV_TX_DESCQ_ID_LBN) ++#define FALCON_EVENT_RX_FLUSH_Q_ID(evp) \ ++ (((evp)->u64 & FALCON_EVENT_RX_FLUSH_Q_ID_MASK) >> \ ++ DRIVER_EV_RX_DESCQ_ID_LBN) ++#define FALCON_EVENT_DRIVER_SUBCODE(evp) \ ++ (((evp)->u64 & FALCON_EVENT_DRV_SUBCODE_MASK) >> \ ++ DRIVER_EV_SUB_CODE_LBN) ++ ++#define FALCON_EVENT_CODE_CHAR ((uint64_t)DRIVER_EV_DECODE << EV_CODE_LBN) ++#define FALCON_EVENT_CODE_SW ((uint64_t)DRV_GEN_EV_DECODE << EV_CODE_LBN) ++ ++ ++/* so this is the size in bytes of an awful lot of things */ ++#define FALCON_REGISTER128 (16) ++ ++/* we define some unique dummy values as a debug aid */ ++#ifdef _WIN32 ++#define FALCON_ATOMIC_BASE 0xdeadbeef00000000ui64 ++#else ++#define FALCON_ATOMIC_BASE 0xdeadbeef00000000ULL ++#endif ++#define FALCON_ATOMIC_UPD_REG (FALCON_ATOMIC_BASE | 0x1) ++#define FALCON_ATOMIC_PTR_TBL_REG (FALCON_ATOMIC_BASE | 0x2) ++#define FALCON_ATOMIC_SRPM_UDP_EVQ_REG (FALCON_ATOMIC_BASE | 0x3) ++#define FALCON_ATOMIC_RX_FLUSH_DESCQ (FALCON_ATOMIC_BASE | 0x4) ++#define FALCON_ATOMIC_TX_FLUSH_DESCQ (FALCON_ATOMIC_BASE | 0x5) ++#define FALCON_ATOMIC_INT_EN_REG (FALCON_ATOMIC_BASE | 0x6) ++#define FALCON_ATOMIC_TIMER_CMD_REG (FALCON_ATOMIC_BASE | 0x7) ++#define FALCON_ATOMIC_PACE_REG (FALCON_ATOMIC_BASE | 0x8) ++#define FALCON_ATOMIC_INT_ACK_REG (FALCON_ATOMIC_BASE | 0x9) ++/* XXX It crashed with odd value in FALCON_ATOMIC_INT_ADR_REG */ ++#define FALCON_ATOMIC_INT_ADR_REG (FALCON_ATOMIC_BASE | 0xa) ++ ++/*---------------------------------------------------------------------------- ++ * ++ * PCI control blocks for Falcon - ++ * (P) primary is for NET ++ * (S) secondary is for CHAR ++ * ++ *---------------------------------------------------------------------------*/ ++ ++#define FALCON_P_CTR_AP_BAR 2 ++#define FALCON_S_CTR_AP_BAR 0 ++#define FALCON_S_DEVID 0x6703 ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Falcon constants ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/* Note: the following constants have moved to values in struct efhw_nic: ++ * FALCON_EVQ_TBL_NUM -> nic->num_evqs ++ * FALCON_DMAQ_NUM -> nic->num_dmaqs ++ * FALCON_TIMERS_NUM -> nic->num_times ++ * These replacement constants are used as sanity checks in assertions in ++ * certain functions that don't have access to struct efhw_nic. ++ */ ++#define FALCON_DMAQ_NUM_SANITY (EFHW_4K) ++#define FALCON_EVQ_TBL_NUM_SANITY (EFHW_4K) ++#define FALCON_TIMERS_NUM_SANITY (EFHW_4K) ++ ++/* This value is an upper limit on the total number of filter table ++ * entries. The actual size of filter table is determined at runtime, as ++ * it can vary. ++ */ ++#define FALCON_FILTER_TBL_NUM (EFHW_8K) ++ ++/* max number of buffers which can be pushed before commiting */ ++#define FALCON_BUFFER_UPD_MAX (128) ++ ++/* We can tell falcon to write its RX buffers in 32 byte quantums, ++ and since we pad packets 2 bytes to the right we can't use ++ a full page (not unless we use jumbo mode for all queues) ++ ++ NOTE: tests/nic/dma.c assumes that the value here is the real NIC ++ value, so we explicitly round it down to the nearest 32 bytes */ ++ ++/* #define FALCON_RX_USR_BUF_SIZE round_down(4096-2,32) */ ++#define FALCON_RX_USR_BUF_SIZE 4064 ++ ++#define FALCON_EVQ_RPTR_REG_P0 0x400 ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Falcon requires user-space descriptor pushes to be: ++ * dword[0-2]; wiob(); dword[3] ++ * ++ * Driver register access must be locked against other threads from ++ * the same driver but can be in any order: i.e dword[0-3]; wiob() ++ * ++ * The following helpers ensure that valid dword orderings are exercised ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/* A union to allow writting 64bit values as 32bit values, without ++ * hitting the compilers aliasing rules. We hope the compiler optimises ++ * away the copy's anyway */ ++union __u64to32 { ++ uint64_t u64; ++ struct { ++#ifdef EFHW_IS_LITTLE_ENDIAN ++ uint32_t a; ++ uint32_t b; ++#else ++ uint32_t b; ++ uint32_t a; ++#endif ++ } s; ++}; ++ ++static inline void ++falcon_write_ddd_d(volatile char __iomem *kva, ++ uint32_t d0, uint32_t d1, uint32_t d2, uint32_t d3) ++{ ++ writel(d0, kva + 0); ++ writel(d1, kva + 4); ++ writel(d2, kva + 8); ++ mmiowb(); ++ writel(d3, kva + 12); ++} ++ ++static inline void falcon_write_q(volatile char __iomem *kva, uint64_t q) ++{ ++ union __u64to32 u; ++ u.u64 = q; ++ ++ writel(u.s.a, kva); ++ mmiowb(); ++ writel(u.s.b, kva + 4); ++} ++ ++static inline void falcon_read_q(volatile char __iomem *addr, uint64_t *q0) ++{ ++ /* It is essential that we read dword0 first, so that ++ * the shadow register is updated with the latest value ++ * and we get a self consistent value. ++ */ ++ union __u64to32 u; ++ u.s.a = readl(addr); ++ rmb(); ++ u.s.b = readl(addr + 4); ++ ++ *q0 = u.u64; ++} ++ ++static inline void ++falcon_write_qq(volatile char __iomem *kva, uint64_t q0, uint64_t q1) ++{ ++ writeq(q0, kva + 0); ++ falcon_write_q(kva + 8, q1); ++} ++ ++static inline void ++falcon_read_qq(volatile char __iomem *addr, uint64_t *q0, uint64_t *q1) ++{ ++ falcon_read_q(addr, q0); ++ *q1 = readq(addr + 8); ++} ++ ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Buffer virtual addresses (4K buffers) ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/* Form a buffer virtual address from buffer ID and offset. If the offset ++** is larger than the buffer size, then the buffer indexed will be ++** calculated appropriately. It is the responsibility of the caller to ++** ensure that they have valid buffers programmed at that address. ++*/ ++#define FALCON_VADDR_8K_S (13) ++#define FALCON_VADDR_4K_S (12) ++#define FALCON_VADDR_M 0xfffff /* post shift mask */ ++ ++#define FALCON_BUFFER_8K_ADDR(id, off) (((id) << FALCON_VADDR_8K_S) + (off)) ++#define FALCON_BUFFER_8K_PAGE(vaddr) \ ++ (((vaddr) >> FALCON_VADDR_8K_S) & FALCON_VADDR_M) ++#define FALCON_BUFFER_8K_OFF(vaddr) \ ++ ((vaddr) & __FALCON_MASK32(FALCON_VADDR_8K_S)) ++ ++#define FALCON_BUFFER_4K_ADDR(id, off) (((id) << FALCON_VADDR_4K_S) + (off)) ++#define FALCON_BUFFER_4K_PAGE(vaddr) \ ++ (((vaddr) >> FALCON_VADDR_4K_S) & FALCON_VADDR_M) ++#define FALCON_BUFFER_4K_OFF(vaddr) \ ++ ((vaddr) & __FALCON_MASK32(FALCON_VADDR_4K_S)) ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Timer helpers ++ * ++ *---------------------------------------------------------------------------*/ ++ ++static inline int falcon_timer_page_addr(uint idx) ++{ ++ ++ EFHW_ASSERT(TIMER_CMD_REG_KER_OFST == ++ (TIMER_CMD_REG_PAGE4_OFST - 4 * EFHW_8K)); ++ ++ EFHW_ASSERT(idx < FALCON_TIMERS_NUM_SANITY); ++ ++ if (idx < 4) ++ return TIMER_CMD_REG_KER_OFST + (idx * EFHW_8K); ++ else if (idx < 1024) ++ return TIMER_CMD_REG_PAGE4_OFST + ((idx - 4) * EFHW_8K); ++ else ++ return TIMER_CMD_REG_PAGE123K_OFST + ((idx - 1024) * EFHW_8K); ++} ++ ++#define FALCON_TIMER_PAGE_MASK (EFHW_8K-1) ++ ++static inline int falcon_timer_page_offset(uint idx) ++{ ++ return falcon_timer_page_addr(idx) & FALCON_TIMER_PAGE_MASK; ++} ++ ++/*---------------------------------------------------------------------------- ++ * ++ * DMA Queue helpers ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/* iSCSI queue for A1; see bug 5427 for more details. */ ++#define FALCON_A1_ISCSI_DMAQ 4 ++ ++/*! returns an address within a bar of the TX DMA doorbell */ ++static inline uint falcon_tx_dma_page_addr(uint dmaq_idx) ++{ ++ uint page; ++ ++ EFHW_ASSERT((((TX_DESC_UPD_REG_PAGE123K_OFST) & (EFHW_8K - 1)) == ++ (((TX_DESC_UPD_REG_PAGE4_OFST) & (EFHW_8K - 1))))); ++ ++ EFHW_ASSERT(dmaq_idx < FALCON_DMAQ_NUM_SANITY); ++ ++ if (dmaq_idx < 1024) ++ page = TX_DESC_UPD_REG_PAGE4_OFST + ((dmaq_idx - 4) * EFHW_8K); ++ else ++ page = ++ TX_DESC_UPD_REG_PAGE123K_OFST + ++ ((dmaq_idx - 1024) * EFHW_8K); ++ ++ return page; ++} ++ ++/*! returns an address within a bar of the RX DMA doorbell */ ++static inline uint falcon_rx_dma_page_addr(uint dmaq_idx) ++{ ++ uint page; ++ ++ EFHW_ASSERT((((RX_DESC_UPD_REG_PAGE123K_OFST) & (EFHW_8K - 1)) == ++ ((RX_DESC_UPD_REG_PAGE4_OFST) & (EFHW_8K - 1)))); ++ ++ EFHW_ASSERT(dmaq_idx < FALCON_DMAQ_NUM_SANITY); ++ ++ if (dmaq_idx < 1024) ++ page = RX_DESC_UPD_REG_PAGE4_OFST + ((dmaq_idx - 4) * EFHW_8K); ++ else ++ page = ++ RX_DESC_UPD_REG_PAGE123K_OFST + ++ ((dmaq_idx - 1024) * EFHW_8K); ++ ++ return page; ++} ++ ++/*! "page"=NIC-dependent register set size */ ++#define FALCON_DMA_PAGE_MASK (EFHW_8K-1) ++ ++/*! returns an address within a bar of the start of the "page" ++ containing the TX DMA doorbell */ ++static inline int falcon_tx_dma_page_base(uint dma_idx) ++{ ++ return falcon_tx_dma_page_addr(dma_idx) & ~FALCON_DMA_PAGE_MASK; ++} ++ ++/*! returns an address within a bar of the start of the "page" ++ containing the RX DMA doorbell */ ++static inline int falcon_rx_dma_page_base(uint dma_idx) ++{ ++ return falcon_rx_dma_page_addr(dma_idx) & ~FALCON_DMA_PAGE_MASK; ++} ++ ++/*! returns an offset within a "page" of the TX DMA doorbell */ ++static inline int falcon_tx_dma_page_offset(uint dma_idx) ++{ ++ return falcon_tx_dma_page_addr(dma_idx) & FALCON_DMA_PAGE_MASK; ++} ++ ++/*! returns an offset within a "page" of the RX DMA doorbell */ ++static inline int falcon_rx_dma_page_offset(uint dma_idx) ++{ ++ return falcon_rx_dma_page_addr(dma_idx) & FALCON_DMA_PAGE_MASK; ++} ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Events ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/* Falcon nails down the event queue mappings */ ++#define FALCON_EVQ_KERNEL0 (0) /* hardwired for net driver */ ++#define FALCON_EVQ_CHAR (4) /* char driver's event queue */ ++ ++/* reserved by the drivers */ ++#define FALCON_EVQ_TBL_RESERVED (8) ++ ++/* default DMA-Q sizes */ ++#define FALCON_DMA_Q_DEFAULT_TX_SIZE 512 ++ ++#define FALCON_DMA_Q_DEFAULT_RX_SIZE 512 ++ ++#define FALCON_DMA_Q_DEFAULT_MMAP \ ++ (FALCON_DMA_Q_DEFAULT_TX_SIZE * (FALCON_DMA_TX_DESC_BYTES * 2)) ++ ++/*---------------------------------------------------------------------------- ++ * ++ * DEBUG - Analyser trigger ++ * ++ *---------------------------------------------------------------------------*/ ++ ++static inline void ++falcon_deadbeef(volatile char __iomem *efhw_kva, unsigned what) ++{ ++ writel(what, efhw_kva + 0x300); ++ mmiowb(); ++} ++#endif /* __CI_DRIVER_EFAB_HARDWARE_FALCON_H__ */ ++/*! \cidoxg_end */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/efab/hardware/falcon/falcon_core.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,1147 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides EtherFabric NIC - EFXXXX (aka Falcon) core register ++ * definitions. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#define FALCON_EXTENDED_P_BAR 1 ++ ++/*************---- Bus Interface Unit Registers C Header ----*************/ ++#define IOM_IND_ADR_REG_OFST 0x0 /* IO-mapped indirect access address ++ register */ ++ #define IOM_AUTO_ADR_INC_EN_LBN 16 ++ #define IOM_AUTO_ADR_INC_EN_WIDTH 1 ++ #define IOM_IND_ADR_LBN 0 ++ #define IOM_IND_ADR_WIDTH 16 ++#define IOM_IND_DAT_REG_OFST 0x4 /* IO-mapped indirect access data register */ ++ #define IOM_IND_DAT_LBN 0 ++ #define IOM_IND_DAT_WIDTH 32 ++#define ADR_REGION_REG_KER_OFST 0x0 /* Address region register */ ++#define ADR_REGION_REG_OFST 0x0 /* Address region register */ ++ #define ADR_REGION3_LBN 96 ++ #define ADR_REGION3_WIDTH 18 ++ #define ADR_REGION2_LBN 64 ++ #define ADR_REGION2_WIDTH 18 ++ #define ADR_REGION1_LBN 32 ++ #define ADR_REGION1_WIDTH 18 ++ #define ADR_REGION0_LBN 0 ++ #define ADR_REGION0_WIDTH 18 ++#define INT_EN_REG_KER_OFST 0x10 /* Kernel driver Interrupt enable register */ ++ #define KER_INT_CHAR_LBN 4 ++ #define KER_INT_CHAR_WIDTH 1 ++ #define KER_INT_KER_LBN 3 ++ #define KER_INT_KER_WIDTH 1 ++ #define ILL_ADR_ERR_INT_EN_KER_LBN 2 ++ #define ILL_ADR_ERR_INT_EN_KER_WIDTH 1 ++ #define SRM_PERR_INT_EN_KER_LBN 1 ++ #define SRM_PERR_INT_EN_KER_WIDTH 1 ++ #define DRV_INT_EN_KER_LBN 0 ++ #define DRV_INT_EN_KER_WIDTH 1 ++#define INT_EN_REG_CHAR_OFST 0x20 /* Char Driver interrupt enable register */ ++ #define CHAR_INT_CHAR_LBN 4 ++ #define CHAR_INT_CHAR_WIDTH 1 ++ #define CHAR_INT_KER_LBN 3 ++ #define CHAR_INT_KER_WIDTH 1 ++ #define ILL_ADR_ERR_INT_EN_CHAR_LBN 2 ++ #define ILL_ADR_ERR_INT_EN_CHAR_WIDTH 1 ++ #define SRM_PERR_INT_EN_CHAR_LBN 1 ++ #define SRM_PERR_INT_EN_CHAR_WIDTH 1 ++ #define DRV_INT_EN_CHAR_LBN 0 ++ #define DRV_INT_EN_CHAR_WIDTH 1 ++#define INT_ADR_REG_KER_OFST 0x30 /* Interrupt host address for Kernel driver */ ++ #define INT_ADR_KER_LBN 0 ++ #define INT_ADR_KER_WIDTH 64 ++ #define DRV_INT_KER_LBN 32 ++ #define DRV_INT_KER_WIDTH 1 ++ #define EV_FF_HALF_INT_KER_LBN 3 ++ #define EV_FF_HALF_INT_KER_WIDTH 1 ++ #define EV_FF_FULL_INT_KER_LBN 2 ++ #define EV_FF_FULL_INT_KER_WIDTH 1 ++ #define ILL_ADR_ERR_INT_KER_LBN 1 ++ #define ILL_ADR_ERR_INT_KER_WIDTH 1 ++ #define SRAM_PERR_INT_KER_LBN 0 ++ #define SRAM_PERR_INT_KER_WIDTH 1 ++#define INT_ADR_REG_CHAR_OFST 0x40 /* Interrupt host address for Char driver */ ++ #define INT_ADR_CHAR_LBN 0 ++ #define INT_ADR_CHAR_WIDTH 64 ++ #define DRV_INT_CHAR_LBN 32 ++ #define DRV_INT_CHAR_WIDTH 1 ++ #define EV_FF_HALF_INT_CHAR_LBN 3 ++ #define EV_FF_HALF_INT_CHAR_WIDTH 1 ++ #define EV_FF_FULL_INT_CHAR_LBN 2 ++ #define EV_FF_FULL_INT_CHAR_WIDTH 1 ++ #define ILL_ADR_ERR_INT_CHAR_LBN 1 ++ #define ILL_ADR_ERR_INT_CHAR_WIDTH 1 ++ #define SRAM_PERR_INT_CHAR_LBN 0 ++ #define SRAM_PERR_INT_CHAR_WIDTH 1 ++#define INT_ISR0_B0_OFST 0x90 /* B0 only */ ++#define INT_ISR1_B0_OFST 0xA0 ++#define INT_ACK_REG_KER_A1_OFST 0x50 /* Kernel interrupt acknowledge register */ ++ #define RESERVED_LBN 0 ++ #define RESERVED_WIDTH 32 ++#define INT_ACK_REG_CHAR_A1_OFST 0x60 /* CHAR interrupt acknowledge register */ ++ #define RESERVED_LBN 0 ++ #define RESERVED_WIDTH 32 ++/*************---- Global CSR Registers C Header ----*************/ ++#define NIC_STAT_REG_KER_OFST 0x200 /* ASIC strap status register */ ++#define NIC_STAT_REG_OFST 0x200 /* ASIC strap status register */ ++ #define ONCHIP_SRAM_LBN 16 ++ #define ONCHIP_SRAM_WIDTH 0 ++ #define STRAP_PINS_LBN 0 ++ #define STRAP_PINS_WIDTH 3 ++#define GPIO_CTL_REG_KER_OFST 0x210 /* GPIO control register */ ++#define GPIO_CTL_REG_OFST 0x210 /* GPIO control register */ ++ #define GPIO_OEN_LBN 24 ++ #define GPIO_OEN_WIDTH 4 ++ #define GPIO_OUT_LBN 16 ++ #define GPIO_OUT_WIDTH 4 ++ #define GPIO_IN_LBN 8 ++ #define GPIO_IN_WIDTH 4 ++ #define GPIO_PWRUP_VALUE_LBN 0 ++ #define GPIO_PWRUP_VALUE_WIDTH 4 ++#define GLB_CTL_REG_KER_OFST 0x220 /* Global control register */ ++#define GLB_CTL_REG_OFST 0x220 /* Global control register */ ++ #define SWRST_LBN 0 ++ #define SWRST_WIDTH 1 ++#define FATAL_INTR_REG_KER_OFST 0x230 /* Fatal interrupt register for Kernel */ ++ #define PCI_BUSERR_INT_KER_EN_LBN 43 ++ #define PCI_BUSERR_INT_KER_EN_WIDTH 1 ++ #define SRAM_OOB_INT_KER_EN_LBN 42 ++ #define SRAM_OOB_INT_KER_EN_WIDTH 1 ++ #define BUFID_OOB_INT_KER_EN_LBN 41 ++ #define BUFID_OOB_INT_KER_EN_WIDTH 1 ++ #define MEM_PERR_INT_KER_EN_LBN 40 ++ #define MEM_PERR_INT_KER_EN_WIDTH 1 ++ #define RBUF_OWN_INT_KER_EN_LBN 39 ++ #define RBUF_OWN_INT_KER_EN_WIDTH 1 ++ #define TBUF_OWN_INT_KER_EN_LBN 38 ++ #define TBUF_OWN_INT_KER_EN_WIDTH 1 ++ #define RDESCQ_OWN_INT_KER_EN_LBN 37 ++ #define RDESCQ_OWN_INT_KER_EN_WIDTH 1 ++ #define TDESCQ_OWN_INT_KER_EN_LBN 36 ++ #define TDESCQ_OWN_INT_KER_EN_WIDTH 1 ++ #define EVQ_OWN_INT_KER_EN_LBN 35 ++ #define EVQ_OWN_INT_KER_EN_WIDTH 1 ++ #define EVFF_OFLO_INT_KER_EN_LBN 34 ++ #define EVFF_OFLO_INT_KER_EN_WIDTH 1 ++ #define ILL_ADR_INT_KER_EN_LBN 33 ++ #define ILL_ADR_INT_KER_EN_WIDTH 1 ++ #define SRM_PERR_INT_KER_EN_LBN 32 ++ #define SRM_PERR_INT_KER_EN_WIDTH 1 ++ #define PCI_BUSERR_INT_KER_LBN 11 ++ #define PCI_BUSERR_INT_KER_WIDTH 1 ++ #define SRAM_OOB_INT_KER_LBN 10 ++ #define SRAM_OOB_INT_KER_WIDTH 1 ++ #define BUFID_OOB_INT_KER_LBN 9 ++ #define BUFID_OOB_INT_KER_WIDTH 1 ++ #define MEM_PERR_INT_KER_LBN 8 ++ #define MEM_PERR_INT_KER_WIDTH 1 ++ #define RBUF_OWN_INT_KER_LBN 7 ++ #define RBUF_OWN_INT_KER_WIDTH 1 ++ #define TBUF_OWN_INT_KER_LBN 6 ++ #define TBUF_OWN_INT_KER_WIDTH 1 ++ #define RDESCQ_OWN_INT_KER_LBN 5 ++ #define RDESCQ_OWN_INT_KER_WIDTH 1 ++ #define TDESCQ_OWN_INT_KER_LBN 4 ++ #define TDESCQ_OWN_INT_KER_WIDTH 1 ++ #define EVQ_OWN_INT_KER_LBN 3 ++ #define EVQ_OWN_INT_KER_WIDTH 1 ++ #define EVFF_OFLO_INT_KER_LBN 2 ++ #define EVFF_OFLO_INT_KER_WIDTH 1 ++ #define ILL_ADR_INT_KER_LBN 1 ++ #define ILL_ADR_INT_KER_WIDTH 1 ++ #define SRM_PERR_INT_KER_LBN 0 ++ #define SRM_PERR_INT_KER_WIDTH 1 ++#define FATAL_INTR_REG_OFST 0x240 /* Fatal interrupt register for Char */ ++ #define PCI_BUSERR_INT_CHAR_EN_LBN 43 ++ #define PCI_BUSERR_INT_CHAR_EN_WIDTH 1 ++ #define SRAM_OOB_INT_CHAR_EN_LBN 42 ++ #define SRAM_OOB_INT_CHAR_EN_WIDTH 1 ++ #define BUFID_OOB_INT_CHAR_EN_LBN 41 ++ #define BUFID_OOB_INT_CHAR_EN_WIDTH 1 ++ #define MEM_PERR_INT_CHAR_EN_LBN 40 ++ #define MEM_PERR_INT_CHAR_EN_WIDTH 1 ++ #define RBUF_OWN_INT_CHAR_EN_LBN 39 ++ #define RBUF_OWN_INT_CHAR_EN_WIDTH 1 ++ #define TBUF_OWN_INT_CHAR_EN_LBN 38 ++ #define TBUF_OWN_INT_CHAR_EN_WIDTH 1 ++ #define RDESCQ_OWN_INT_CHAR_EN_LBN 37 ++ #define RDESCQ_OWN_INT_CHAR_EN_WIDTH 1 ++ #define TDESCQ_OWN_INT_CHAR_EN_LBN 36 ++ #define TDESCQ_OWN_INT_CHAR_EN_WIDTH 1 ++ #define EVQ_OWN_INT_CHAR_EN_LBN 35 ++ #define EVQ_OWN_INT_CHAR_EN_WIDTH 1 ++ #define EVFF_OFLO_INT_CHAR_EN_LBN 34 ++ #define EVFF_OFLO_INT_CHAR_EN_WIDTH 1 ++ #define ILL_ADR_INT_CHAR_EN_LBN 33 ++ #define ILL_ADR_INT_CHAR_EN_WIDTH 1 ++ #define SRM_PERR_INT_CHAR_EN_LBN 32 ++ #define SRM_PERR_INT_CHAR_EN_WIDTH 1 ++ #define FATAL_INTR_REG_EN_BITS 0xffffffffffffffffULL ++ #define PCI_BUSERR_INT_CHAR_LBN 11 ++ #define PCI_BUSERR_INT_CHAR_WIDTH 1 ++ #define SRAM_OOB_INT_CHAR_LBN 10 ++ #define SRAM_OOB_INT_CHAR_WIDTH 1 ++ #define BUFID_OOB_INT_CHAR_LBN 9 ++ #define BUFID_OOB_INT_CHAR_WIDTH 1 ++ #define MEM_PERR_INT_CHAR_LBN 8 ++ #define MEM_PERR_INT_CHAR_WIDTH 1 ++ #define RBUF_OWN_INT_CHAR_LBN 7 ++ #define RBUF_OWN_INT_CHAR_WIDTH 1 ++ #define TBUF_OWN_INT_CHAR_LBN 6 ++ #define TBUF_OWN_INT_CHAR_WIDTH 1 ++ #define RDESCQ_OWN_INT_CHAR_LBN 5 ++ #define RDESCQ_OWN_INT_CHAR_WIDTH 1 ++ #define TDESCQ_OWN_INT_CHAR_LBN 4 ++ #define TDESCQ_OWN_INT_CHAR_WIDTH 1 ++ #define EVQ_OWN_INT_CHAR_LBN 3 ++ #define EVQ_OWN_INT_CHAR_WIDTH 1 ++ #define EVFF_OFLO_INT_CHAR_LBN 2 ++ #define EVFF_OFLO_INT_CHAR_WIDTH 1 ++ #define ILL_ADR_INT_CHAR_LBN 1 ++ #define ILL_ADR_INT_CHAR_WIDTH 1 ++ #define SRM_PERR_INT_CHAR_LBN 0 ++ #define SRM_PERR_INT_CHAR_WIDTH 1 ++#define DP_CTRL_REG_OFST 0x250 /* Datapath control register */ ++ #define FLS_EVQ_ID_LBN 0 ++ #define FLS_EVQ_ID_WIDTH 12 ++#define MEM_STAT_REG_KER_OFST 0x260 /* Memory status register */ ++#define MEM_STAT_REG_OFST 0x260 /* Memory status register */ ++ #define MEM_PERR_VEC_LBN 53 ++ #define MEM_PERR_VEC_WIDTH 38 ++ #define MBIST_CORR_LBN 38 ++ #define MBIST_CORR_WIDTH 15 ++ #define MBIST_ERR_LBN 0 ++ #define MBIST_ERR_WIDTH 38 ++#define DEBUG_REG_KER_OFST 0x270 /* Debug register */ ++#define DEBUG_REG_OFST 0x270 /* Debug register */ ++ #define DEBUG_BLK_SEL2_LBN 47 ++ #define DEBUG_BLK_SEL2_WIDTH 3 ++ #define DEBUG_BLK_SEL1_LBN 44 ++ #define DEBUG_BLK_SEL1_WIDTH 3 ++ #define DEBUG_BLK_SEL0_LBN 41 ++ #define DEBUG_BLK_SEL0_WIDTH 3 ++ #define MISC_DEBUG_ADDR_LBN 36 ++ #define MISC_DEBUG_ADDR_WIDTH 5 ++ #define SERDES_DEBUG_ADDR_LBN 31 ++ #define SERDES_DEBUG_ADDR_WIDTH 5 ++ #define EM_DEBUG_ADDR_LBN 26 ++ #define EM_DEBUG_ADDR_WIDTH 5 ++ #define SR_DEBUG_ADDR_LBN 21 ++ #define SR_DEBUG_ADDR_WIDTH 5 ++ #define EV_DEBUG_ADDR_LBN 16 ++ #define EV_DEBUG_ADDR_WIDTH 5 ++ #define RX_DEBUG_ADDR_LBN 11 ++ #define RX_DEBUG_ADDR_WIDTH 5 ++ #define TX_DEBUG_ADDR_LBN 6 ++ #define TX_DEBUG_ADDR_WIDTH 5 ++ #define BIU_DEBUG_ADDR_LBN 1 ++ #define BIU_DEBUG_ADDR_WIDTH 5 ++ #define DEBUG_EN_LBN 0 ++ #define DEBUG_EN_WIDTH 1 ++#define DRIVER_REG0_KER_OFST 0x280 /* Driver scratch register 0 */ ++#define DRIVER_REG0_OFST 0x280 /* Driver scratch register 0 */ ++ #define DRIVER_DW0_LBN 0 ++ #define DRIVER_DW0_WIDTH 32 ++#define DRIVER_REG1_KER_OFST 0x290 /* Driver scratch register 1 */ ++#define DRIVER_REG1_OFST 0x290 /* Driver scratch register 1 */ ++ #define DRIVER_DW1_LBN 0 ++ #define DRIVER_DW1_WIDTH 32 ++#define DRIVER_REG2_KER_OFST 0x2A0 /* Driver scratch register 2 */ ++#define DRIVER_REG2_OFST 0x2A0 /* Driver scratch register 2 */ ++ #define DRIVER_DW2_LBN 0 ++ #define DRIVER_DW2_WIDTH 32 ++#define DRIVER_REG3_KER_OFST 0x2B0 /* Driver scratch register 3 */ ++#define DRIVER_REG3_OFST 0x2B0 /* Driver scratch register 3 */ ++ #define DRIVER_DW3_LBN 0 ++ #define DRIVER_DW3_WIDTH 32 ++#define DRIVER_REG4_KER_OFST 0x2C0 /* Driver scratch register 4 */ ++#define DRIVER_REG4_OFST 0x2C0 /* Driver scratch register 4 */ ++ #define DRIVER_DW4_LBN 0 ++ #define DRIVER_DW4_WIDTH 32 ++#define DRIVER_REG5_KER_OFST 0x2D0 /* Driver scratch register 5 */ ++#define DRIVER_REG5_OFST 0x2D0 /* Driver scratch register 5 */ ++ #define DRIVER_DW5_LBN 0 ++ #define DRIVER_DW5_WIDTH 32 ++#define DRIVER_REG6_KER_OFST 0x2E0 /* Driver scratch register 6 */ ++#define DRIVER_REG6_OFST 0x2E0 /* Driver scratch register 6 */ ++ #define DRIVER_DW6_LBN 0 ++ #define DRIVER_DW6_WIDTH 32 ++#define DRIVER_REG7_KER_OFST 0x2F0 /* Driver scratch register 7 */ ++#define DRIVER_REG7_OFST 0x2F0 /* Driver scratch register 7 */ ++ #define DRIVER_DW7_LBN 0 ++ #define DRIVER_DW7_WIDTH 32 ++#define ALTERA_BUILD_REG_OFST 0x300 /* Altera build register */ ++#define ALTERA_BUILD_REG_OFST 0x300 /* Altera build register */ ++ #define ALTERA_BUILD_VER_LBN 0 ++ #define ALTERA_BUILD_VER_WIDTH 32 ++ ++/* so called CSR spare register ++ - contains separate parity enable bits for the various internal memory ++ blocks */ ++#define MEM_PARITY_ERR_EN_REG_KER 0x310 ++#define MEM_PARITY_ALL_BLOCKS_EN_LBN 64 ++#define MEM_PARITY_ALL_BLOCKS_EN_WIDTH 38 ++#define MEM_PARITY_TX_DATA_EN_LBN 72 ++#define MEM_PARITY_TX_DATA_EN_WIDTH 2 ++ ++/*************---- Event & Timer Module Registers C Header ----*************/ ++ ++#if FALCON_EXTENDED_P_BAR ++#define EVQ_RPTR_REG_KER_OFST 0x11B00 /* Event queue read pointer register */ ++#else ++#define EVQ_RPTR_REG_KER_OFST 0x1B00 /* Event queue read pointer register */ ++#endif ++ ++#define EVQ_RPTR_REG_OFST 0xFA0000 /* Event queue read pointer register ++ array. */ ++ #define EVQ_RPTR_LBN 0 ++ #define EVQ_RPTR_WIDTH 15 ++ ++#if FALCON_EXTENDED_P_BAR ++#define EVQ_PTR_TBL_KER_OFST 0x11A00 /* Event queue pointer table for kernel ++ access */ ++#else ++#define EVQ_PTR_TBL_KER_OFST 0x1A00 /* Event queue pointer table for kernel ++ access */ ++#endif ++ ++#define EVQ_PTR_TBL_CHAR_OFST 0xF60000 /* Event queue pointer table for char ++ direct access */ ++ #define EVQ_WKUP_OR_INT_EN_LBN 39 ++ #define EVQ_WKUP_OR_INT_EN_WIDTH 1 ++ #define EVQ_NXT_WPTR_LBN 24 ++ #define EVQ_NXT_WPTR_WIDTH 15 ++ #define EVQ_EN_LBN 23 ++ #define EVQ_EN_WIDTH 1 ++ #define EVQ_SIZE_LBN 20 ++ #define EVQ_SIZE_WIDTH 3 ++ #define EVQ_BUF_BASE_ID_LBN 0 ++ #define EVQ_BUF_BASE_ID_WIDTH 20 ++#define TIMER_CMD_REG_KER_OFST 0x420 /* Timer table for kernel access. ++ Page-mapped */ ++#define TIMER_CMD_REG_PAGE4_OFST 0x8420 /* Timer table for user-level access. ++ Page-mapped. For lowest 1K queues. ++ */ ++#define TIMER_CMD_REG_PAGE123K_OFST 0x1000420 /* Timer table for user-level ++ access. Page-mapped. ++ For upper 3K queues. */ ++#define TIMER_TBL_OFST 0xF70000 /* Timer table for char driver direct access */ ++ #define TIMER_MODE_LBN 12 ++ #define TIMER_MODE_WIDTH 2 ++ #define TIMER_VAL_LBN 0 ++ #define TIMER_VAL_WIDTH 12 ++ #define TIMER_MODE_INT_HLDOFF 2 ++ #define EVQ_BUF_SIZE_LBN 0 ++ #define EVQ_BUF_SIZE_WIDTH 1 ++#define DRV_EV_REG_KER_OFST 0x440 /* Driver generated event register */ ++#define DRV_EV_REG_OFST 0x440 /* Driver generated event register */ ++ #define DRV_EV_QID_LBN 64 ++ #define DRV_EV_QID_WIDTH 12 ++ #define DRV_EV_DATA_LBN 0 ++ #define DRV_EV_DATA_WIDTH 64 ++#define EVQ_CTL_REG_KER_OFST 0x450 /* Event queue control register */ ++#define EVQ_CTL_REG_OFST 0x450 /* Event queue control register */ ++ #define RX_EVQ_WAKEUP_MASK_B0_LBN 15 ++ #define RX_EVQ_WAKEUP_MASK_B0_WIDTH 6 ++ #define EVQ_OWNERR_CTL_LBN 14 ++ #define EVQ_OWNERR_CTL_WIDTH 1 ++ #define EVQ_FIFO_AF_TH_LBN 8 ++ #define EVQ_FIFO_AF_TH_WIDTH 6 ++ #define EVQ_FIFO_NOTAF_TH_LBN 0 ++ #define EVQ_FIFO_NOTAF_TH_WIDTH 6 ++/*************---- SRAM Module Registers C Header ----*************/ ++#define BUF_TBL_CFG_REG_KER_OFST 0x600 /* Buffer table configuration register */ ++#define BUF_TBL_CFG_REG_OFST 0x600 /* Buffer table configuration register */ ++ #define BUF_TBL_MODE_LBN 3 ++ #define BUF_TBL_MODE_WIDTH 1 ++#define SRM_RX_DC_CFG_REG_KER_OFST 0x610 /* SRAM receive descriptor cache ++ configuration register */ ++#define SRM_RX_DC_CFG_REG_OFST 0x610 /* SRAM receive descriptor cache ++ configuration register */ ++ #define SRM_RX_DC_BASE_ADR_LBN 0 ++ #define SRM_RX_DC_BASE_ADR_WIDTH 21 ++#define SRM_TX_DC_CFG_REG_KER_OFST 0x620 /* SRAM transmit descriptor cache ++ configuration register */ ++#define SRM_TX_DC_CFG_REG_OFST 0x620 /* SRAM transmit descriptor cache ++ configuration register */ ++ #define SRM_TX_DC_BASE_ADR_LBN 0 ++ #define SRM_TX_DC_BASE_ADR_WIDTH 21 ++#define SRM_CFG_REG_KER_OFST 0x630 /* SRAM configuration register */ ++#define SRM_CFG_REG_OFST 0x630 /* SRAM configuration register */ ++ #define SRAM_OOB_ADR_INTEN_LBN 5 ++ #define SRAM_OOB_ADR_INTEN_WIDTH 1 ++ #define SRAM_OOB_BUF_INTEN_LBN 4 ++ #define SRAM_OOB_BUF_INTEN_WIDTH 1 ++ #define SRAM_BT_INIT_EN_LBN 3 ++ #define SRAM_BT_INIT_EN_WIDTH 1 ++ #define SRM_NUM_BANK_LBN 2 ++ #define SRM_NUM_BANK_WIDTH 1 ++ #define SRM_BANK_SIZE_LBN 0 ++ #define SRM_BANK_SIZE_WIDTH 2 ++#define BUF_TBL_UPD_REG_KER_OFST 0x650 /* Buffer table update register */ ++#define BUF_TBL_UPD_REG_OFST 0x650 /* Buffer table update register */ ++ #define BUF_UPD_CMD_LBN 63 ++ #define BUF_UPD_CMD_WIDTH 1 ++ #define BUF_CLR_CMD_LBN 62 ++ #define BUF_CLR_CMD_WIDTH 1 ++ #define BUF_CLR_END_ID_LBN 32 ++ #define BUF_CLR_END_ID_WIDTH 20 ++ #define BUF_CLR_START_ID_LBN 0 ++ #define BUF_CLR_START_ID_WIDTH 20 ++#define SRM_UPD_EVQ_REG_KER_OFST 0x660 /* Buffer table update register */ ++#define SRM_UPD_EVQ_REG_OFST 0x660 /* Buffer table update register */ ++ #define SRM_UPD_EVQ_ID_LBN 0 ++ #define SRM_UPD_EVQ_ID_WIDTH 12 ++#define SRAM_PARITY_REG_KER_OFST 0x670 /* SRAM parity register. */ ++#define SRAM_PARITY_REG_OFST 0x670 /* SRAM parity register. */ ++ #define FORCE_SRAM_PERR_LBN 0 ++ #define FORCE_SRAM_PERR_WIDTH 1 ++ ++#if FALCON_EXTENDED_P_BAR ++#define BUF_HALF_TBL_KER_OFST 0x18000 /* Buffer table in half buffer table ++ mode direct access by kernel driver */ ++#else ++#define BUF_HALF_TBL_KER_OFST 0x8000 /* Buffer table in half buffer table ++ mode direct access by kernel driver */ ++#endif ++ ++ ++#define BUF_HALF_TBL_OFST 0x800000 /* Buffer table in half buffer table mode ++ direct access by char driver */ ++ #define BUF_ADR_HBUF_ODD_LBN 44 ++ #define BUF_ADR_HBUF_ODD_WIDTH 20 ++ #define BUF_OWNER_ID_HBUF_ODD_LBN 32 ++ #define BUF_OWNER_ID_HBUF_ODD_WIDTH 12 ++ #define BUF_ADR_HBUF_EVEN_LBN 12 ++ #define BUF_ADR_HBUF_EVEN_WIDTH 20 ++ #define BUF_OWNER_ID_HBUF_EVEN_LBN 0 ++ #define BUF_OWNER_ID_HBUF_EVEN_WIDTH 12 ++ ++ ++#if FALCON_EXTENDED_P_BAR ++#define BUF_FULL_TBL_KER_OFST 0x18000 /* Buffer table in full buffer table ++ mode direct access by kernel driver */ ++#else ++#define BUF_FULL_TBL_KER_OFST 0x8000 /* Buffer table in full buffer table mode ++ direct access by kernel driver */ ++#endif ++ ++ ++ ++ ++#define BUF_FULL_TBL_OFST 0x800000 /* Buffer table in full buffer table mode ++ direct access by char driver */ ++ #define IP_DAT_BUF_SIZE_LBN 50 ++ #define IP_DAT_BUF_SIZE_WIDTH 1 ++ #define BUF_ADR_REGION_LBN 48 ++ #define BUF_ADR_REGION_WIDTH 2 ++ #define BUF_ADR_FBUF_LBN 14 ++ #define BUF_ADR_FBUF_WIDTH 34 ++ #define BUF_OWNER_ID_FBUF_LBN 0 ++ #define BUF_OWNER_ID_FBUF_WIDTH 14 ++#define SRM_DBG_REG_OFST 0x3000000 /* SRAM debug access */ ++ #define SRM_DBG_LBN 0 ++ #define SRM_DBG_WIDTH 64 ++/*************---- RX Datapath Registers C Header ----*************/ ++ ++#define RX_CFG_REG_KER_OFST 0x800 /* Receive configuration register */ ++#define RX_CFG_REG_OFST 0x800 /* Receive configuration register */ ++ ++#if !defined(FALCON_64K_RXFIFO) && !defined(FALCON_PRE_02020029) ++# if !defined(FALCON_128K_RXFIFO) ++# define FALCON_128K_RXFIFO ++# endif ++#endif ++ ++#if defined(FALCON_128K_RXFIFO) ++ ++/* new for B0 */ ++ #define RX_TOEP_TCP_SUPPRESS_B0_LBN 48 ++ #define RX_TOEP_TCP_SUPPRESS_B0_WIDTH 1 ++ #define RX_INGR_EN_B0_LBN 47 ++ #define RX_INGR_EN_B0_WIDTH 1 ++ #define RX_TOEP_IPV4_B0_LBN 46 ++ #define RX_TOEP_IPV4_B0_WIDTH 1 ++ #define RX_HASH_ALG_B0_LBN 45 ++ #define RX_HASH_ALG_B0_WIDTH 1 ++ #define RX_HASH_INSERT_HDR_B0_LBN 44 ++ #define RX_HASH_INSERT_HDR_B0_WIDTH 1 ++/* moved for B0 */ ++ #define RX_DESC_PUSH_EN_B0_LBN 43 ++ #define RX_DESC_PUSH_EN_B0_WIDTH 1 ++ #define RX_RDW_PATCH_EN_LBN 42 /* Non head of line blocking */ ++ #define RX_RDW_PATCH_EN_WIDTH 1 ++ #define RX_PCI_BURST_SIZE_B0_LBN 39 ++ #define RX_PCI_BURST_SIZE_B0_WIDTH 3 ++ #define RX_OWNERR_CTL_B0_LBN 38 ++ #define RX_OWNERR_CTL_B0_WIDTH 1 ++ #define RX_XON_TX_TH_B0_LBN 33 ++ #define RX_XON_TX_TH_B0_WIDTH 5 ++ #define RX_XOFF_TX_TH_B0_LBN 28 ++ #define RX_XOFF_TX_TH_B0_WIDTH 5 ++ #define RX_USR_BUF_SIZE_B0_LBN 19 ++ #define RX_USR_BUF_SIZE_B0_WIDTH 9 ++ #define RX_XON_MAC_TH_B0_LBN 10 ++ #define RX_XON_MAC_TH_B0_WIDTH 9 ++ #define RX_XOFF_MAC_TH_B0_LBN 1 ++ #define RX_XOFF_MAC_TH_B0_WIDTH 9 ++ #define RX_XOFF_MAC_EN_B0_LBN 0 ++ #define RX_XOFF_MAC_EN_B0_WIDTH 1 ++ ++#elif !defined(FALCON_PRE_02020029) ++/* new for B0 */ ++ #define RX_TOEP_TCP_SUPPRESS_B0_LBN 46 ++ #define RX_TOEP_TCP_SUPPRESS_B0_WIDTH 1 ++ #define RX_INGR_EN_B0_LBN 45 ++ #define RX_INGR_EN_B0_WIDTH 1 ++ #define RX_TOEP_IPV4_B0_LBN 44 ++ #define RX_TOEP_IPV4_B0_WIDTH 1 ++ #define RX_HASH_ALG_B0_LBN 43 ++ #define RX_HASH_ALG_B0_WIDTH 41 ++ #define RX_HASH_INSERT_HDR_B0_LBN 42 ++ #define RX_HASH_INSERT_HDR_B0_WIDTH 1 ++/* moved for B0 */ ++ #define RX_DESC_PUSH_EN_B0_LBN 41 ++ #define RX_DESC_PUSH_EN_B0_WIDTH 1 ++ #define RX_PCI_BURST_SIZE_B0_LBN 37 ++ #define RX_PCI_BURST_SIZE_B0_WIDTH 3 ++ #define RX_OWNERR_CTL_B0_LBN 36 ++ #define RX_OWNERR_CTL_B0_WIDTH 1 ++ #define RX_XON_TX_TH_B0_LBN 31 ++ #define RX_XON_TX_TH_B0_WIDTH 5 ++ #define RX_XOFF_TX_TH_B0_LBN 26 ++ #define RX_XOFF_TX_TH_B0_WIDTH 5 ++ #define RX_USR_BUF_SIZE_B0_LBN 17 ++ #define RX_USR_BUF_SIZE_B0_WIDTH 9 ++ #define RX_XON_MAC_TH_B0_LBN 9 ++ #define RX_XON_MAC_TH_B0_WIDTH 8 ++ #define RX_XOFF_MAC_TH_B0_LBN 1 ++ #define RX_XOFF_MAC_TH_B0_WIDTH 8 ++ #define RX_XOFF_MAC_EN_B0_LBN 0 ++ #define RX_XOFF_MAC_EN_B0_WIDTH 1 ++ ++#else ++/* new for B0 */ ++ #define RX_TOEP_TCP_SUPPRESS_B0_LBN 44 ++ #define RX_TOEP_TCP_SUPPRESS_B0_WIDTH 1 ++ #define RX_INGR_EN_B0_LBN 43 ++ #define RX_INGR_EN_B0_WIDTH 1 ++ #define RX_TOEP_IPV4_B0_LBN 42 ++ #define RX_TOEP_IPV4_B0_WIDTH 1 ++ #define RX_HASH_ALG_B0_LBN 41 ++ #define RX_HASH_ALG_B0_WIDTH 41 ++ #define RX_HASH_INSERT_HDR_B0_LBN 40 ++ #define RX_HASH_INSERT_HDR_B0_WIDTH 1 ++/* moved for B0 */ ++ #define RX_DESC_PUSH_EN_B0_LBN 35 ++ #define RX_DESC_PUSH_EN_B0_WIDTH 1 ++ #define RX_PCI_BURST_SIZE_B0_LBN 35 ++ #define RX_PCI_BURST_SIZE_B0_WIDTH 2 ++ #define RX_OWNERR_CTL_B0_LBN 34 ++ #define RX_OWNERR_CTL_B0_WIDTH 1 ++ #define RX_XON_TX_TH_B0_LBN 29 ++ #define RX_XON_TX_TH_B0_WIDTH 5 ++ #define RX_XOFF_TX_TH_B0_LBN 24 ++ #define RX_XOFF_TX_TH_B0_WIDTH 5 ++ #define RX_USR_BUF_SIZE_B0_LBN 15 ++ #define RX_USR_BUF_SIZE_B0_WIDTH 9 ++ #define RX_XON_MAC_TH_B0_LBN 8 ++ #define RX_XON_MAC_TH_B0_WIDTH 7 ++ #define RX_XOFF_MAC_TH_B0_LBN 1 ++ #define RX_XOFF_MAC_TH_B0_WIDTH 7 ++ #define RX_XOFF_MAC_EN_B0_LBN 0 ++ #define RX_XOFF_MAC_EN_B0_WIDTH 1 ++ ++#endif ++ ++/* A0/A1 */ ++ #define RX_PUSH_EN_A1_LBN 35 ++ #define RX_PUSH_EN_A1_WIDTH 1 ++ #define RX_PCI_BURST_SIZE_A1_LBN 31 ++ #define RX_PCI_BURST_SIZE_A1_WIDTH 3 ++ #define RX_OWNERR_CTL_A1_LBN 30 ++ #define RX_OWNERR_CTL_A1_WIDTH 1 ++ #define RX_XON_TX_TH_A1_LBN 25 ++ #define RX_XON_TX_TH_A1_WIDTH 5 ++ #define RX_XOFF_TX_TH_A1_LBN 20 ++ #define RX_XOFF_TX_TH_A1_WIDTH 5 ++ #define RX_USR_BUF_SIZE_A1_LBN 11 ++ #define RX_USR_BUF_SIZE_A1_WIDTH 9 ++ #define RX_XON_MAC_TH_A1_LBN 6 ++ #define RX_XON_MAC_TH_A1_WIDTH 5 ++ #define RX_XOFF_MAC_TH_A1_LBN 1 ++ #define RX_XOFF_MAC_TH_A1_WIDTH 5 ++ #define RX_XOFF_MAC_EN_A1_LBN 0 ++ #define RX_XOFF_MAC_EN_A1_WIDTH 1 ++ ++#define RX_FILTER_CTL_REG_OFST 0x810 /* Receive filter control registers */ ++ #define SCATTER_ENBL_NO_MATCH_Q_B0_LBN 40 ++ #define SCATTER_ENBL_NO_MATCH_Q_B0_WIDTH 1 ++ #define UDP_FULL_SRCH_LIMIT_LBN 32 ++ #define UDP_FULL_SRCH_LIMIT_WIDTH 8 ++ #define NUM_KER_LBN 24 ++ #define NUM_KER_WIDTH 2 ++ #define UDP_WILD_SRCH_LIMIT_LBN 16 ++ #define UDP_WILD_SRCH_LIMIT_WIDTH 8 ++ #define TCP_WILD_SRCH_LIMIT_LBN 8 ++ #define TCP_WILD_SRCH_LIMIT_WIDTH 8 ++ #define TCP_FULL_SRCH_LIMIT_LBN 0 ++ #define TCP_FULL_SRCH_LIMIT_WIDTH 8 ++#define RX_FLUSH_DESCQ_REG_KER_OFST 0x820 /* Receive flush descriptor queue ++ register */ ++#define RX_FLUSH_DESCQ_REG_OFST 0x820 /* Receive flush descriptor queue ++ register */ ++ #define RX_FLUSH_DESCQ_CMD_LBN 24 ++ #define RX_FLUSH_DESCQ_CMD_WIDTH 1 ++ #define RX_FLUSH_EVQ_ID_LBN 12 ++ #define RX_FLUSH_EVQ_ID_WIDTH 12 ++ #define RX_FLUSH_DESCQ_LBN 0 ++ #define RX_FLUSH_DESCQ_WIDTH 12 ++#define RX_DESC_UPD_REG_KER_OFST 0x830 /* Kernel receive descriptor update ++ register. Page-mapped */ ++#define RX_DESC_UPD_REG_PAGE4_OFST 0x8830 /* Char & user receive descriptor ++ update register. Page-mapped. ++ For lowest 1K queues. */ ++#define RX_DESC_UPD_REG_PAGE123K_OFST 0x1000830 /* Char & user receive ++ descriptor update register. ++ Page-mapped. For upper ++ 3K queues. */ ++ #define RX_DESC_WPTR_LBN 96 ++ #define RX_DESC_WPTR_WIDTH 12 ++ #define RX_DESC_PUSH_CMD_LBN 95 ++ #define RX_DESC_PUSH_CMD_WIDTH 1 ++ #define RX_DESC_LBN 0 ++ #define RX_DESC_WIDTH 64 ++ #define RX_KER_DESC_LBN 0 ++ #define RX_KER_DESC_WIDTH 64 ++ #define RX_USR_DESC_LBN 0 ++ #define RX_USR_DESC_WIDTH 32 ++#define RX_DC_CFG_REG_KER_OFST 0x840 /* Receive descriptor cache ++ configuration register */ ++#define RX_DC_CFG_REG_OFST 0x840 /* Receive descriptor cache ++ configuration register */ ++ #define RX_DC_SIZE_LBN 0 ++ #define RX_DC_SIZE_WIDTH 2 ++#define RX_DC_PF_WM_REG_KER_OFST 0x850 /* Receive descriptor cache pre-fetch ++ watermark register */ ++#define RX_DC_PF_WM_REG_OFST 0x850 /* Receive descriptor cache pre-fetch ++ watermark register */ ++ #define RX_DC_PF_LWM_LO_LBN 0 ++ #define RX_DC_PF_LWM_LO_WIDTH 6 ++ ++#define RX_RSS_TKEY_B0_OFST 0x860 /* RSS Toeplitz hash key (B0 only) */ ++ ++#define RX_NODESC_DROP_REG 0x880 ++ #define RX_NODESC_DROP_CNT_LBN 0 ++ #define RX_NODESC_DROP_CNT_WIDTH 16 ++ ++#define XM_TX_CFG_REG_OFST 0x1230 ++ #define XM_AUTO_PAD_LBN 5 ++ #define XM_AUTO_PAD_WIDTH 1 ++ ++#define RX_FILTER_TBL0_OFST 0xF00000 /* Receive filter table - even entries */ ++ #define RSS_EN_0_B0_LBN 110 ++ #define RSS_EN_0_B0_WIDTH 1 ++ #define SCATTER_EN_0_B0_LBN 109 ++ #define SCATTER_EN_0_B0_WIDTH 1 ++ #define TCP_UDP_0_LBN 108 ++ #define TCP_UDP_0_WIDTH 1 ++ #define RXQ_ID_0_LBN 96 ++ #define RXQ_ID_0_WIDTH 12 ++ #define DEST_IP_0_LBN 64 ++ #define DEST_IP_0_WIDTH 32 ++ #define DEST_PORT_TCP_0_LBN 48 ++ #define DEST_PORT_TCP_0_WIDTH 16 ++ #define SRC_IP_0_LBN 16 ++ #define SRC_IP_0_WIDTH 32 ++ #define SRC_TCP_DEST_UDP_0_LBN 0 ++ #define SRC_TCP_DEST_UDP_0_WIDTH 16 ++#define RX_FILTER_TBL1_OFST 0xF00010 /* Receive filter table - odd entries */ ++ #define RSS_EN_1_B0_LBN 110 ++ #define RSS_EN_1_B0_WIDTH 1 ++ #define SCATTER_EN_1_B0_LBN 109 ++ #define SCATTER_EN_1_B0_WIDTH 1 ++ #define TCP_UDP_1_LBN 108 ++ #define TCP_UDP_1_WIDTH 1 ++ #define RXQ_ID_1_LBN 96 ++ #define RXQ_ID_1_WIDTH 12 ++ #define DEST_IP_1_LBN 64 ++ #define DEST_IP_1_WIDTH 32 ++ #define DEST_PORT_TCP_1_LBN 48 ++ #define DEST_PORT_TCP_1_WIDTH 16 ++ #define SRC_IP_1_LBN 16 ++ #define SRC_IP_1_WIDTH 32 ++ #define SRC_TCP_DEST_UDP_1_LBN 0 ++ #define SRC_TCP_DEST_UDP_1_WIDTH 16 ++ ++#if FALCON_EXTENDED_P_BAR ++#define RX_DESC_PTR_TBL_KER_OFST 0x11800 /* Receive descriptor pointer ++ kernel access */ ++#else ++#define RX_DESC_PTR_TBL_KER_OFST 0x1800 /* Receive descriptor pointer ++ kernel access */ ++#endif ++ ++ ++#define RX_DESC_PTR_TBL_OFST 0xF40000 /* Receive descriptor pointer table */ ++ #define RX_ISCSI_DDIG_EN_LBN 88 ++ #define RX_ISCSI_DDIG_EN_WIDTH 1 ++ #define RX_ISCSI_HDIG_EN_LBN 87 ++ #define RX_ISCSI_HDIG_EN_WIDTH 1 ++ #define RX_DESC_PREF_ACT_LBN 86 ++ #define RX_DESC_PREF_ACT_WIDTH 1 ++ #define RX_DC_HW_RPTR_LBN 80 ++ #define RX_DC_HW_RPTR_WIDTH 6 ++ #define RX_DESCQ_HW_RPTR_LBN 68 ++ #define RX_DESCQ_HW_RPTR_WIDTH 12 ++ #define RX_DESCQ_SW_WPTR_LBN 56 ++ #define RX_DESCQ_SW_WPTR_WIDTH 12 ++ #define RX_DESCQ_BUF_BASE_ID_LBN 36 ++ #define RX_DESCQ_BUF_BASE_ID_WIDTH 20 ++ #define RX_DESCQ_EVQ_ID_LBN 24 ++ #define RX_DESCQ_EVQ_ID_WIDTH 12 ++ #define RX_DESCQ_OWNER_ID_LBN 10 ++ #define RX_DESCQ_OWNER_ID_WIDTH 14 ++ #define RX_DESCQ_LABEL_LBN 5 ++ #define RX_DESCQ_LABEL_WIDTH 5 ++ #define RX_DESCQ_SIZE_LBN 3 ++ #define RX_DESCQ_SIZE_WIDTH 2 ++ #define RX_DESCQ_TYPE_LBN 2 ++ #define RX_DESCQ_TYPE_WIDTH 1 ++ #define RX_DESCQ_JUMBO_LBN 1 ++ #define RX_DESCQ_JUMBO_WIDTH 1 ++ #define RX_DESCQ_EN_LBN 0 ++ #define RX_DESCQ_EN_WIDTH 1 ++ ++ ++#define RX_RSS_INDIR_TBL_B0_OFST 0xFB0000 /* RSS indirection table (B0 only) */ ++ #define RX_RSS_INDIR_ENT_B0_LBN 0 ++ #define RX_RSS_INDIR_ENT_B0_WIDTH 6 ++ ++/*************---- TX Datapath Registers C Header ----*************/ ++#define TX_FLUSH_DESCQ_REG_KER_OFST 0xA00 /* Transmit flush descriptor ++ queue register */ ++#define TX_FLUSH_DESCQ_REG_OFST 0xA00 /* Transmit flush descriptor queue ++ register */ ++ #define TX_FLUSH_DESCQ_CMD_LBN 12 ++ #define TX_FLUSH_DESCQ_CMD_WIDTH 1 ++ #define TX_FLUSH_DESCQ_LBN 0 ++ #define TX_FLUSH_DESCQ_WIDTH 12 ++#define TX_DESC_UPD_REG_KER_OFST 0xA10 /* Kernel transmit descriptor update ++ register. Page-mapped */ ++#define TX_DESC_UPD_REG_PAGE4_OFST 0x8A10 /* Char & user transmit descriptor ++ update register. Page-mapped */ ++#define TX_DESC_UPD_REG_PAGE123K_OFST 0x1000A10 /* Char & user transmit ++ descriptor update register. ++ Page-mapped */ ++ #define TX_DESC_WPTR_LBN 96 ++ #define TX_DESC_WPTR_WIDTH 12 ++ #define TX_DESC_PUSH_CMD_LBN 95 ++ #define TX_DESC_PUSH_CMD_WIDTH 1 ++ #define TX_DESC_LBN 0 ++ #define TX_DESC_WIDTH 95 ++ #define TX_KER_DESC_LBN 0 ++ #define TX_KER_DESC_WIDTH 64 ++ #define TX_USR_DESC_LBN 0 ++ #define TX_USR_DESC_WIDTH 64 ++#define TX_DC_CFG_REG_KER_OFST 0xA20 /* Transmit descriptor cache ++ configuration register */ ++#define TX_DC_CFG_REG_OFST 0xA20 /* Transmit descriptor cache configuration ++ register */ ++ #define TX_DC_SIZE_LBN 0 ++ #define TX_DC_SIZE_WIDTH 2 ++ ++#if FALCON_EXTENDED_P_BAR ++#define TX_DESC_PTR_TBL_KER_OFST 0x11900 /* Transmit descriptor pointer. */ ++#else ++#define TX_DESC_PTR_TBL_KER_OFST 0x1900 /* Transmit descriptor pointer. */ ++#endif ++ ++ ++#define TX_DESC_PTR_TBL_OFST 0xF50000 /* Transmit descriptor pointer */ ++ #define TX_NON_IP_DROP_DIS_B0_LBN 91 ++ #define TX_NON_IP_DROP_DIS_B0_WIDTH 1 ++ #define TX_IP_CHKSM_DIS_B0_LBN 90 ++ #define TX_IP_CHKSM_DIS_B0_WIDTH 1 ++ #define TX_TCP_CHKSM_DIS_B0_LBN 89 ++ #define TX_TCP_CHKSM_DIS_B0_WIDTH 1 ++ #define TX_DESCQ_EN_LBN 88 ++ #define TX_DESCQ_EN_WIDTH 1 ++ #define TX_ISCSI_DDIG_EN_LBN 87 ++ #define TX_ISCSI_DDIG_EN_WIDTH 1 ++ #define TX_ISCSI_HDIG_EN_LBN 86 ++ #define TX_ISCSI_HDIG_EN_WIDTH 1 ++ #define TX_DC_HW_RPTR_LBN 80 ++ #define TX_DC_HW_RPTR_WIDTH 6 ++ #define TX_DESCQ_HW_RPTR_LBN 68 ++ #define TX_DESCQ_HW_RPTR_WIDTH 12 ++ #define TX_DESCQ_SW_WPTR_LBN 56 ++ #define TX_DESCQ_SW_WPTR_WIDTH 12 ++ #define TX_DESCQ_BUF_BASE_ID_LBN 36 ++ #define TX_DESCQ_BUF_BASE_ID_WIDTH 20 ++ #define TX_DESCQ_EVQ_ID_LBN 24 ++ #define TX_DESCQ_EVQ_ID_WIDTH 12 ++ #define TX_DESCQ_OWNER_ID_LBN 10 ++ #define TX_DESCQ_OWNER_ID_WIDTH 14 ++ #define TX_DESCQ_LABEL_LBN 5 ++ #define TX_DESCQ_LABEL_WIDTH 5 ++ #define TX_DESCQ_SIZE_LBN 3 ++ #define TX_DESCQ_SIZE_WIDTH 2 ++ #define TX_DESCQ_TYPE_LBN 1 ++ #define TX_DESCQ_TYPE_WIDTH 2 ++ #define TX_DESCQ_FLUSH_LBN 0 ++ #define TX_DESCQ_FLUSH_WIDTH 1 ++#define TX_CFG_REG_KER_OFST 0xA50 /* Transmit configuration register */ ++#define TX_CFG_REG_OFST 0xA50 /* Transmit configuration register */ ++ #define TX_IP_ID_P1_OFS_LBN 32 ++ #define TX_IP_ID_P1_OFS_WIDTH 15 ++ #define TX_IP_ID_P0_OFS_LBN 16 ++ #define TX_IP_ID_P0_OFS_WIDTH 15 ++ #define TX_TURBO_EN_LBN 3 ++ #define TX_TURBO_EN_WIDTH 1 ++ #define TX_OWNERR_CTL_LBN 2 ++ #define TX_OWNERR_CTL_WIDTH 2 ++ #define TX_NON_IP_DROP_DIS_LBN 1 ++ #define TX_NON_IP_DROP_DIS_WIDTH 1 ++ #define TX_IP_ID_REP_EN_LBN 0 ++ #define TX_IP_ID_REP_EN_WIDTH 1 ++#define TX_RESERVED_REG_KER_OFST 0xA80 /* Transmit configuration register */ ++#define TX_RESERVED_REG_OFST 0xA80 /* Transmit configuration register */ ++ #define TX_CSR_PUSH_EN_LBN 89 ++ #define TX_CSR_PUSH_EN_WIDTH 1 ++ #define TX_RX_SPACER_LBN 64 ++ #define TX_RX_SPACER_WIDTH 8 ++ #define TX_SW_EV_EN_LBN 59 ++ #define TX_SW_EV_EN_WIDTH 1 ++ #define TX_RX_SPACER_EN_LBN 57 ++ #define TX_RX_SPACER_EN_WIDTH 1 ++ #define TX_CSR_PREF_WD_TMR_LBN 24 ++ #define TX_CSR_PREF_WD_TMR_WIDTH 16 ++ #define TX_CSR_ONLY1TAG_LBN 21 ++ #define TX_CSR_ONLY1TAG_WIDTH 1 ++ #define TX_PREF_THRESHOLD_LBN 19 ++ #define TX_PREF_THRESHOLD_WIDTH 2 ++ #define TX_ONE_PKT_PER_Q_LBN 18 ++ #define TX_ONE_PKT_PER_Q_WIDTH 1 ++ #define TX_DIS_NON_IP_EV_LBN 17 ++ #define TX_DIS_NON_IP_EV_WIDTH 1 ++ #define TX_DMA_SPACER_LBN 8 ++ #define TX_DMA_SPACER_WIDTH 8 ++ #define TX_FLUSH_MIN_LEN_EN_B0_LBN 7 ++ #define TX_FLUSH_MIN_LEN_EN_B0_WIDTH 1 ++ #define TX_TCP_DIS_A1_LBN 7 ++ #define TX_TCP_DIS_A1_WIDTH 1 ++ #define TX_IP_DIS_A1_LBN 6 ++ #define TX_IP_DIS_A1_WIDTH 1 ++ #define TX_MAX_CPL_LBN 2 ++ #define TX_MAX_CPL_WIDTH 2 ++ #define TX_MAX_PREF_LBN 0 ++ #define TX_MAX_PREF_WIDTH 2 ++#define TX_VLAN_REG_OFST 0xAE0 /* Transmit VLAN tag register */ ++ #define TX_VLAN_EN_LBN 127 ++ #define TX_VLAN_EN_WIDTH 1 ++ #define TX_VLAN7_PORT1_EN_LBN 125 ++ #define TX_VLAN7_PORT1_EN_WIDTH 1 ++ #define TX_VLAN7_PORT0_EN_LBN 124 ++ #define TX_VLAN7_PORT0_EN_WIDTH 1 ++ #define TX_VLAN7_LBN 112 ++ #define TX_VLAN7_WIDTH 12 ++ #define TX_VLAN6_PORT1_EN_LBN 109 ++ #define TX_VLAN6_PORT1_EN_WIDTH 1 ++ #define TX_VLAN6_PORT0_EN_LBN 108 ++ #define TX_VLAN6_PORT0_EN_WIDTH 1 ++ #define TX_VLAN6_LBN 96 ++ #define TX_VLAN6_WIDTH 12 ++ #define TX_VLAN5_PORT1_EN_LBN 93 ++ #define TX_VLAN5_PORT1_EN_WIDTH 1 ++ #define TX_VLAN5_PORT0_EN_LBN 92 ++ #define TX_VLAN5_PORT0_EN_WIDTH 1 ++ #define TX_VLAN5_LBN 80 ++ #define TX_VLAN5_WIDTH 12 ++ #define TX_VLAN4_PORT1_EN_LBN 77 ++ #define TX_VLAN4_PORT1_EN_WIDTH 1 ++ #define TX_VLAN4_PORT0_EN_LBN 76 ++ #define TX_VLAN4_PORT0_EN_WIDTH 1 ++ #define TX_VLAN4_LBN 64 ++ #define TX_VLAN4_WIDTH 12 ++ #define TX_VLAN3_PORT1_EN_LBN 61 ++ #define TX_VLAN3_PORT1_EN_WIDTH 1 ++ #define TX_VLAN3_PORT0_EN_LBN 60 ++ #define TX_VLAN3_PORT0_EN_WIDTH 1 ++ #define TX_VLAN3_LBN 48 ++ #define TX_VLAN3_WIDTH 12 ++ #define TX_VLAN2_PORT1_EN_LBN 45 ++ #define TX_VLAN2_PORT1_EN_WIDTH 1 ++ #define TX_VLAN2_PORT0_EN_LBN 44 ++ #define TX_VLAN2_PORT0_EN_WIDTH 1 ++ #define TX_VLAN2_LBN 32 ++ #define TX_VLAN2_WIDTH 12 ++ #define TX_VLAN1_PORT1_EN_LBN 29 ++ #define TX_VLAN1_PORT1_EN_WIDTH 1 ++ #define TX_VLAN1_PORT0_EN_LBN 28 ++ #define TX_VLAN1_PORT0_EN_WIDTH 1 ++ #define TX_VLAN1_LBN 16 ++ #define TX_VLAN1_WIDTH 12 ++ #define TX_VLAN0_PORT1_EN_LBN 13 ++ #define TX_VLAN0_PORT1_EN_WIDTH 1 ++ #define TX_VLAN0_PORT0_EN_LBN 12 ++ #define TX_VLAN0_PORT0_EN_WIDTH 1 ++ #define TX_VLAN0_LBN 0 ++ #define TX_VLAN0_WIDTH 12 ++#define TX_FIL_CTL_REG_OFST 0xAF0 /* Transmit filter control register */ ++ #define TX_MADR1_FIL_EN_LBN 65 ++ #define TX_MADR1_FIL_EN_WIDTH 1 ++ #define TX_MADR0_FIL_EN_LBN 64 ++ #define TX_MADR0_FIL_EN_WIDTH 1 ++ #define TX_IPFIL31_PORT1_EN_LBN 63 ++ #define TX_IPFIL31_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL31_PORT0_EN_LBN 62 ++ #define TX_IPFIL31_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL30_PORT1_EN_LBN 61 ++ #define TX_IPFIL30_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL30_PORT0_EN_LBN 60 ++ #define TX_IPFIL30_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL29_PORT1_EN_LBN 59 ++ #define TX_IPFIL29_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL29_PORT0_EN_LBN 58 ++ #define TX_IPFIL29_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL28_PORT1_EN_LBN 57 ++ #define TX_IPFIL28_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL28_PORT0_EN_LBN 56 ++ #define TX_IPFIL28_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL27_PORT1_EN_LBN 55 ++ #define TX_IPFIL27_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL27_PORT0_EN_LBN 54 ++ #define TX_IPFIL27_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL26_PORT1_EN_LBN 53 ++ #define TX_IPFIL26_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL26_PORT0_EN_LBN 52 ++ #define TX_IPFIL26_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL25_PORT1_EN_LBN 51 ++ #define TX_IPFIL25_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL25_PORT0_EN_LBN 50 ++ #define TX_IPFIL25_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL24_PORT1_EN_LBN 49 ++ #define TX_IPFIL24_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL24_PORT0_EN_LBN 48 ++ #define TX_IPFIL24_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL23_PORT1_EN_LBN 47 ++ #define TX_IPFIL23_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL23_PORT0_EN_LBN 46 ++ #define TX_IPFIL23_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL22_PORT1_EN_LBN 45 ++ #define TX_IPFIL22_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL22_PORT0_EN_LBN 44 ++ #define TX_IPFIL22_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL21_PORT1_EN_LBN 43 ++ #define TX_IPFIL21_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL21_PORT0_EN_LBN 42 ++ #define TX_IPFIL21_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL20_PORT1_EN_LBN 41 ++ #define TX_IPFIL20_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL20_PORT0_EN_LBN 40 ++ #define TX_IPFIL20_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL19_PORT1_EN_LBN 39 ++ #define TX_IPFIL19_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL19_PORT0_EN_LBN 38 ++ #define TX_IPFIL19_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL18_PORT1_EN_LBN 37 ++ #define TX_IPFIL18_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL18_PORT0_EN_LBN 36 ++ #define TX_IPFIL18_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL17_PORT1_EN_LBN 35 ++ #define TX_IPFIL17_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL17_PORT0_EN_LBN 34 ++ #define TX_IPFIL17_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL16_PORT1_EN_LBN 33 ++ #define TX_IPFIL16_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL16_PORT0_EN_LBN 32 ++ #define TX_IPFIL16_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL15_PORT1_EN_LBN 31 ++ #define TX_IPFIL15_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL15_PORT0_EN_LBN 30 ++ #define TX_IPFIL15_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL14_PORT1_EN_LBN 29 ++ #define TX_IPFIL14_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL14_PORT0_EN_LBN 28 ++ #define TX_IPFIL14_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL13_PORT1_EN_LBN 27 ++ #define TX_IPFIL13_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL13_PORT0_EN_LBN 26 ++ #define TX_IPFIL13_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL12_PORT1_EN_LBN 25 ++ #define TX_IPFIL12_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL12_PORT0_EN_LBN 24 ++ #define TX_IPFIL12_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL11_PORT1_EN_LBN 23 ++ #define TX_IPFIL11_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL11_PORT0_EN_LBN 22 ++ #define TX_IPFIL11_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL10_PORT1_EN_LBN 21 ++ #define TX_IPFIL10_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL10_PORT0_EN_LBN 20 ++ #define TX_IPFIL10_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL9_PORT1_EN_LBN 19 ++ #define TX_IPFIL9_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL9_PORT0_EN_LBN 18 ++ #define TX_IPFIL9_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL8_PORT1_EN_LBN 17 ++ #define TX_IPFIL8_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL8_PORT0_EN_LBN 16 ++ #define TX_IPFIL8_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL7_PORT1_EN_LBN 15 ++ #define TX_IPFIL7_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL7_PORT0_EN_LBN 14 ++ #define TX_IPFIL7_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL6_PORT1_EN_LBN 13 ++ #define TX_IPFIL6_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL6_PORT0_EN_LBN 12 ++ #define TX_IPFIL6_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL5_PORT1_EN_LBN 11 ++ #define TX_IPFIL5_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL5_PORT0_EN_LBN 10 ++ #define TX_IPFIL5_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL4_PORT1_EN_LBN 9 ++ #define TX_IPFIL4_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL4_PORT0_EN_LBN 8 ++ #define TX_IPFIL4_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL3_PORT1_EN_LBN 7 ++ #define TX_IPFIL3_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL3_PORT0_EN_LBN 6 ++ #define TX_IPFIL3_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL2_PORT1_EN_LBN 5 ++ #define TX_IPFIL2_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL2_PORT0_EN_LBN 4 ++ #define TX_IPFIL2_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL1_PORT1_EN_LBN 3 ++ #define TX_IPFIL1_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL1_PORT0_EN_LBN 2 ++ #define TX_IPFIL1_PORT0_EN_WIDTH 1 ++ #define TX_IPFIL0_PORT1_EN_LBN 1 ++ #define TX_IPFIL0_PORT1_EN_WIDTH 1 ++ #define TX_IPFIL0_PORT0_EN_LBN 0 ++ #define TX_IPFIL0_PORT0_EN_WIDTH 1 ++#define TX_IPFIL_TBL_OFST 0xB00 /* Transmit IP source address filter table */ ++ #define TX_IPFIL_MASK_LBN 32 ++ #define TX_IPFIL_MASK_WIDTH 32 ++ #define TX_IP_SRC_ADR_LBN 0 ++ #define TX_IP_SRC_ADR_WIDTH 32 ++#define TX_PACE_REG_A1_OFST 0xF80000 /* Transmit pace control register */ ++#define TX_PACE_REG_B0_OFST 0xA90 /* Transmit pace control register */ ++ #define TX_PACE_SB_NOTAF_LBN 19 ++ #define TX_PACE_SB_NOTAF_WIDTH 10 ++ #define TX_PACE_SB_AF_LBN 9 ++ #define TX_PACE_SB_AF_WIDTH 10 ++ #define TX_PACE_FB_BASE_LBN 5 ++ #define TX_PACE_FB_BASE_WIDTH 4 ++ #define TX_PACE_BIN_TH_LBN 0 ++ #define TX_PACE_BIN_TH_WIDTH 5 ++#define TX_PACE_TBL_A1_OFST 0xF80040 /* Transmit pacing table */ ++#define TX_PACE_TBL_FIRST_QUEUE_A1 4 ++#define TX_PACE_TBL_B0_OFST 0xF80000 /* Transmit pacing table */ ++#define TX_PACE_TBL_FIRST_QUEUE_B0 0 ++ #define TX_PACE_LBN 0 ++ #define TX_PACE_WIDTH 5 ++ ++/*************---- EE/Flash Registers C Header ----*************/ ++#define EE_SPI_HCMD_REG_KER_OFST 0x100 /* SPI host command register */ ++#define EE_SPI_HCMD_REG_OFST 0x100 /* SPI host command register */ ++ #define EE_SPI_HCMD_CMD_EN_LBN 31 ++ #define EE_SPI_HCMD_CMD_EN_WIDTH 1 ++ #define EE_WR_TIMER_ACTIVE_LBN 28 ++ #define EE_WR_TIMER_ACTIVE_WIDTH 1 ++ #define EE_SPI_HCMD_SF_SEL_LBN 24 ++ #define EE_SPI_HCMD_SF_SEL_WIDTH 1 ++ #define EE_SPI_HCMD_DABCNT_LBN 16 ++ #define EE_SPI_HCMD_DABCNT_WIDTH 5 ++ #define EE_SPI_HCMD_READ_LBN 15 ++ #define EE_SPI_HCMD_READ_WIDTH 1 ++ #define EE_SPI_HCMD_DUBCNT_LBN 12 ++ #define EE_SPI_HCMD_DUBCNT_WIDTH 2 ++ #define EE_SPI_HCMD_ADBCNT_LBN 8 ++ #define EE_SPI_HCMD_ADBCNT_WIDTH 2 ++ #define EE_SPI_HCMD_ENC_LBN 0 ++ #define EE_SPI_HCMD_ENC_WIDTH 8 ++#define EE_SPI_HADR_REG_KER_OFST 0X110 /* SPI host address register */ ++#define EE_SPI_HADR_REG_OFST 0X110 /* SPI host address register */ ++ #define EE_SPI_HADR_DUBYTE_LBN 24 ++ #define EE_SPI_HADR_DUBYTE_WIDTH 8 ++ #define EE_SPI_HADR_ADR_LBN 0 ++ #define EE_SPI_HADR_ADR_WIDTH 24 ++#define EE_SPI_HDATA_REG_KER_OFST 0x120 /* SPI host data register */ ++#define EE_SPI_HDATA_REG_OFST 0x120 /* SPI host data register */ ++ #define EE_SPI_HDATA3_LBN 96 ++ #define EE_SPI_HDATA3_WIDTH 32 ++ #define EE_SPI_HDATA2_LBN 64 ++ #define EE_SPI_HDATA2_WIDTH 32 ++ #define EE_SPI_HDATA1_LBN 32 ++ #define EE_SPI_HDATA1_WIDTH 32 ++ #define EE_SPI_HDATA0_LBN 0 ++ #define EE_SPI_HDATA0_WIDTH 32 ++#define EE_BASE_PAGE_REG_KER_OFST 0x130 /* Expansion ROM base mirror register */ ++#define EE_BASE_PAGE_REG_OFST 0x130 /* Expansion ROM base mirror register */ ++ #define EE_EXP_ROM_WINDOW_BASE_LBN 16 ++ #define EE_EXP_ROM_WINDOW_BASE_WIDTH 13 ++ #define EE_EXPROM_MASK_LBN 0 ++ #define EE_EXPROM_MASK_WIDTH 13 ++#define EE_VPD_CFG0_REG_KER_OFST 0X140 /* SPI/VPD configuration register */ ++#define EE_VPD_CFG0_REG_OFST 0X140 /* SPI/VPD configuration register */ ++ #define EE_SF_FASTRD_EN_LBN 127 ++ #define EE_SF_FASTRD_EN_WIDTH 1 ++ #define EE_SF_CLOCK_DIV_LBN 120 ++ #define EE_SF_CLOCK_DIV_WIDTH 7 ++ #define EE_VPD_WIP_POLL_LBN 119 ++ #define EE_VPD_WIP_POLL_WIDTH 1 ++ #define EE_VPDW_LENGTH_LBN 80 ++ #define EE_VPDW_LENGTH_WIDTH 15 ++ #define EE_VPDW_BASE_LBN 64 ++ #define EE_VPDW_BASE_WIDTH 15 ++ #define EE_VPD_WR_CMD_EN_LBN 56 ++ #define EE_VPD_WR_CMD_EN_WIDTH 8 ++ #define EE_VPD_BASE_LBN 32 ++ #define EE_VPD_BASE_WIDTH 24 ++ #define EE_VPD_LENGTH_LBN 16 ++ #define EE_VPD_LENGTH_WIDTH 13 ++ #define EE_VPD_AD_SIZE_LBN 8 ++ #define EE_VPD_AD_SIZE_WIDTH 5 ++ #define EE_VPD_ACCESS_ON_LBN 5 ++ #define EE_VPD_ACCESS_ON_WIDTH 1 ++#define EE_VPD_SW_CNTL_REG_KER_OFST 0X150 /* VPD access SW control register */ ++#define EE_VPD_SW_CNTL_REG_OFST 0X150 /* VPD access SW control register */ ++ #define EE_VPD_CYCLE_PENDING_LBN 31 ++ #define EE_VPD_CYCLE_PENDING_WIDTH 1 ++ #define EE_VPD_CYC_WRITE_LBN 28 ++ #define EE_VPD_CYC_WRITE_WIDTH 1 ++ #define EE_VPD_CYC_ADR_LBN 0 ++ #define EE_VPD_CYC_ADR_WIDTH 15 ++#define EE_VPD_SW_DATA_REG_KER_OFST 0x160 /* VPD access SW data register */ ++#define EE_VPD_SW_DATA_REG_OFST 0x160 /* VPD access SW data register */ ++ #define EE_VPD_CYC_DAT_LBN 0 ++ #define EE_VPD_CYC_DAT_WIDTH 32 +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/efab/hardware/falcon/falcon_desc.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,75 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides EtherFabric NIC - EFXXXX (aka Falcon) descriptor ++ * definitions. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++/*************---- Descriptors C Headers ----*************/ ++/* Receive Kernel IP Descriptor */ ++ #define RX_KER_BUF_SIZE_LBN 48 ++ #define RX_KER_BUF_SIZE_WIDTH 14 ++ #define RX_KER_BUF_REGION_LBN 46 ++ #define RX_KER_BUF_REGION_WIDTH 2 ++ #define RX_KER_BUF_REGION0_DECODE 0 ++ #define RX_KER_BUF_REGION1_DECODE 1 ++ #define RX_KER_BUF_REGION2_DECODE 2 ++ #define RX_KER_BUF_REGION3_DECODE 3 ++ #define RX_KER_BUF_ADR_LBN 0 ++ #define RX_KER_BUF_ADR_WIDTH 46 ++/* Receive User IP Descriptor */ ++ #define RX_USR_2BYTE_OFS_LBN 20 ++ #define RX_USR_2BYTE_OFS_WIDTH 12 ++ #define RX_USR_BUF_ID_LBN 0 ++ #define RX_USR_BUF_ID_WIDTH 20 ++/* Transmit Kernel IP Descriptor */ ++ #define TX_KER_PORT_LBN 63 ++ #define TX_KER_PORT_WIDTH 1 ++ #define TX_KER_CONT_LBN 62 ++ #define TX_KER_CONT_WIDTH 1 ++ #define TX_KER_BYTE_CNT_LBN 48 ++ #define TX_KER_BYTE_CNT_WIDTH 14 ++ #define TX_KER_BUF_REGION_LBN 46 ++ #define TX_KER_BUF_REGION_WIDTH 2 ++ #define TX_KER_BUF_REGION0_DECODE 0 ++ #define TX_KER_BUF_REGION1_DECODE 1 ++ #define TX_KER_BUF_REGION2_DECODE 2 ++ #define TX_KER_BUF_REGION3_DECODE 3 ++ #define TX_KER_BUF_ADR_LBN 0 ++ #define TX_KER_BUF_ADR_WIDTH 46 ++/* Transmit User IP Descriptor */ ++ #define TX_USR_PORT_LBN 47 ++ #define TX_USR_PORT_WIDTH 1 ++ #define TX_USR_CONT_LBN 46 ++ #define TX_USR_CONT_WIDTH 1 ++ #define TX_USR_BYTE_CNT_LBN 33 ++ #define TX_USR_BYTE_CNT_WIDTH 13 ++ #define TX_USR_BUF_ID_LBN 13 ++ #define TX_USR_BUF_ID_WIDTH 20 ++ #define TX_USR_BYTE_OFS_LBN 0 ++ #define TX_USR_BYTE_OFS_WIDTH 13 +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/efab/hardware/falcon/falcon_event.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,155 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides EtherFabric NIC - EFXXXX (aka Falcon) event ++ * definitions. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++/*************---- Events Format C Header ----*************/ ++/*************---- Event entry ----*************/ ++ #define EV_CODE_LBN 60 ++ #define EV_CODE_WIDTH 4 ++ #define RX_IP_EV_DECODE 0 ++ #define TX_IP_EV_DECODE 2 ++ #define DRIVER_EV_DECODE 5 ++ #define GLOBAL_EV_DECODE 6 ++ #define DRV_GEN_EV_DECODE 7 ++ #define EV_DATA_LBN 0 ++ #define EV_DATA_WIDTH 60 ++/******---- Receive IP events for both Kernel & User event queues ----******/ ++ #define RX_EV_PKT_OK_LBN 56 ++ #define RX_EV_PKT_OK_WIDTH 1 ++ #define RX_EV_BUF_OWNER_ID_ERR_LBN 54 ++ #define RX_EV_BUF_OWNER_ID_ERR_WIDTH 1 ++ #define RX_EV_IP_HDR_CHKSUM_ERR_LBN 52 ++ #define RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1 ++ #define RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51 ++ #define RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1 ++ #define RX_EV_ETH_CRC_ERR_LBN 50 ++ #define RX_EV_ETH_CRC_ERR_WIDTH 1 ++ #define RX_EV_FRM_TRUNC_LBN 49 ++ #define RX_EV_FRM_TRUNC_WIDTH 1 ++ #define RX_EV_DRIB_NIB_LBN 48 ++ #define RX_EV_DRIB_NIB_WIDTH 1 ++ #define RX_EV_TOBE_DISC_LBN 47 ++ #define RX_EV_TOBE_DISC_WIDTH 1 ++ #define RX_EV_PKT_TYPE_LBN 44 ++ #define RX_EV_PKT_TYPE_WIDTH 3 ++ #define RX_EV_PKT_TYPE_ETH_DECODE 0 ++ #define RX_EV_PKT_TYPE_LLC_DECODE 1 ++ #define RX_EV_PKT_TYPE_JUMBO_DECODE 2 ++ #define RX_EV_PKT_TYPE_VLAN_DECODE 3 ++ #define RX_EV_PKT_TYPE_VLAN_LLC_DECODE 4 ++ #define RX_EV_PKT_TYPE_VLAN_JUMBO_DECODE 5 ++ #define RX_EV_HDR_TYPE_LBN 42 ++ #define RX_EV_HDR_TYPE_WIDTH 2 ++ #define RX_EV_HDR_TYPE_TCP_IPV4_DECODE 0 ++ #define RX_EV_HDR_TYPE_UDP_IPV4_DECODE 1 ++ #define RX_EV_HDR_TYPE_OTHER_IP_DECODE 2 ++ #define RX_EV_HDR_TYPE_NON_IP_DECODE 3 ++ #define RX_EV_DESC_Q_EMPTY_LBN 41 ++ #define RX_EV_DESC_Q_EMPTY_WIDTH 1 ++ #define RX_EV_MCAST_HASH_MATCH_LBN 40 ++ #define RX_EV_MCAST_HASH_MATCH_WIDTH 1 ++ #define RX_EV_MCAST_PKT_LBN 39 ++ #define RX_EV_MCAST_PKT_WIDTH 1 ++ #define RX_EV_Q_LABEL_LBN 32 ++ #define RX_EV_Q_LABEL_WIDTH 5 ++ #define RX_JUMBO_CONT_LBN 31 ++ #define RX_JUMBO_CONT_WIDTH 1 ++ #define RX_SOP_LBN 15 ++ #define RX_SOP_WIDTH 1 ++ #define RX_PORT_LBN 30 ++ #define RX_PORT_WIDTH 1 ++ #define RX_EV_BYTE_CNT_LBN 16 ++ #define RX_EV_BYTE_CNT_WIDTH 14 ++ #define RX_iSCSI_PKT_OK_LBN 14 ++ #define RX_iSCSI_PKT_OK_WIDTH 1 ++ #define RX_ISCSI_DDIG_ERR_LBN 13 ++ #define RX_ISCSI_DDIG_ERR_WIDTH 1 ++ #define RX_ISCSI_HDIG_ERR_LBN 12 ++ #define RX_ISCSI_HDIG_ERR_WIDTH 1 ++ #define RX_EV_DESC_PTR_LBN 0 ++ #define RX_EV_DESC_PTR_WIDTH 12 ++/******---- Transmit IP events for both Kernel & User event queues ----******/ ++ #define TX_EV_PKT_ERR_LBN 38 ++ #define TX_EV_PKT_ERR_WIDTH 1 ++ #define TX_EV_PKT_TOO_BIG_LBN 37 ++ #define TX_EV_PKT_TOO_BIG_WIDTH 1 ++ #define TX_EV_Q_LABEL_LBN 32 ++ #define TX_EV_Q_LABEL_WIDTH 5 ++ #define TX_EV_PORT_LBN 16 ++ #define TX_EV_PORT_WIDTH 1 ++ #define TX_EV_WQ_FF_FULL_LBN 15 ++ #define TX_EV_WQ_FF_FULL_WIDTH 1 ++ #define TX_EV_BUF_OWNER_ID_ERR_LBN 14 ++ #define TX_EV_BUF_OWNER_ID_ERR_WIDTH 1 ++ #define TX_EV_COMP_LBN 12 ++ #define TX_EV_COMP_WIDTH 1 ++ #define TX_EV_DESC_PTR_LBN 0 ++ #define TX_EV_DESC_PTR_WIDTH 12 ++/*************---- Char or Kernel driver events ----*************/ ++ #define DRIVER_EV_SUB_CODE_LBN 56 ++ #define DRIVER_EV_SUB_CODE_WIDTH 4 ++ #define TX_DESCQ_FLS_DONE_EV_DECODE 0x0 ++ #define RX_DESCQ_FLS_DONE_EV_DECODE 0x1 ++ #define EVQ_INIT_DONE_EV_DECODE 0x2 ++ #define EVQ_NOT_EN_EV_DECODE 0x3 ++ #define RX_DESCQ_FLSFF_OVFL_EV_DECODE 0x4 ++ #define SRM_UPD_DONE_EV_DECODE 0x5 ++ #define WAKE_UP_EV_DECODE 0x6 ++ #define TX_PKT_NON_TCP_UDP_DECODE 0x9 ++ #define TIMER_EV_DECODE 0xA ++ #define RX_DSC_ERROR_EV_DECODE 0xE ++ #define DRIVER_EV_TX_DESCQ_ID_LBN 0 ++ #define DRIVER_EV_TX_DESCQ_ID_WIDTH 12 ++ #define DRIVER_EV_RX_DESCQ_ID_LBN 0 ++ #define DRIVER_EV_RX_DESCQ_ID_WIDTH 12 ++ #define DRIVER_EV_EVQ_ID_LBN 0 ++ #define DRIVER_EV_EVQ_ID_WIDTH 12 ++ #define DRIVER_TMR_ID_LBN 0 ++ #define DRIVER_TMR_ID_WIDTH 12 ++ #define DRIVER_EV_SRM_UPD_LBN 0 ++ #define DRIVER_EV_SRM_UPD_WIDTH 2 ++ #define SRM_CLR_EV_DECODE 0 ++ #define SRM_UPD_EV_DECODE 1 ++ #define SRM_ILLCLR_EV_DECODE 2 ++/********---- Global events. Sent to both event queue 0 and 4. ----********/ ++ #define XFP_PHY_INTR_LBN 10 ++ #define XFP_PHY_INTR_WIDTH 1 ++ #define XG_PHY_INTR_LBN 9 ++ #define XG_PHY_INTR_WIDTH 1 ++ #define G_PHY1_INTR_LBN 8 ++ #define G_PHY1_INTR_WIDTH 1 ++ #define G_PHY0_INTR_LBN 7 ++ #define G_PHY0_INTR_WIDTH 1 ++/*************---- Driver generated events ----*************/ ++ #define DRV_GEN_EV_CODE_LBN 60 ++ #define DRV_GEN_EV_CODE_WIDTH 4 ++ #define DRV_GEN_EV_DATA_LBN 0 ++ #define DRV_GEN_EV_DATA_WIDTH 60 +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/efab/hardware/falcon/falcon_intr_vec.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,44 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides EtherFabric NIC - EFXXXX (aka Falcon) interrupt ++ * vector definitions. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++/*************---- Interrupt Vector Format C Header ----*************/ ++#define DW0_OFST 0x0 /* Double-word 0: Event queue FIFO interrupts */ ++ #define EVQ_FIFO_HF_LBN 1 ++ #define EVQ_FIFO_HF_WIDTH 1 ++ #define EVQ_FIFO_AF_LBN 0 ++ #define EVQ_FIFO_AF_WIDTH 1 ++#define DW1_OFST 0x4 /* Double-word 1: Interrupt indicator */ ++ #define INT_FLAG_LBN 0 ++ #define INT_FLAG_WIDTH 1 ++#define DW2_OFST 0x8 /* Double-word 2: Fatal interrupts */ ++ #define FATAL_INT_LBN 0 ++ #define FATAL_INT_WIDTH 1 +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/efab/hardware/workarounds.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,67 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides workaround settings for EtherFabric NICs. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_EFAB_WORKAROUNDS_H__ ++#define __CI_DRIVER_EFAB_WORKAROUNDS_H__ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Hardware workarounds which have global scope ++ * ++ *---------------------------------------------------------------------------*/ ++ ++#if defined(__CI_HARDWARE_CONFIG_FALCON_B0__) ++/*------------------------------- B0 ---------------------------------------*/ ++ ++#define BUG2175_WORKAROUND 0 /* TX event batching for dual port operation. ++ This removes the effect (dup TX events) ++ of the fix ++ (TX event per packet + batch events) */ ++#define BUG5302_WORKAROUND 0 /* unstick TX DMAQ after out-of-range wr ptr */ ++#define BUG5762_WORKAROUND 0 /* Set all queues to jumbo mode */ ++#define BUG5391_WORKAROUND 0 /* Misaligned TX can't span 512-byte boundary */ ++#define BUG7916_WORKAROUND 0 /* RX flush gets lost */ ++ ++#else ++/*------------------------------- A0/A1 ------------------------------------*/ ++ ++#define BUG2175_WORKAROUND 1 /* TX event batching for dual port operation. ++ This removes the effect (dup TX events) ++ of the fix ++ (TX event per packet + batch events) */ ++#define BUG5302_WORKAROUND 1 /* unstick TX DMAQ after out-of-range wr ptr */ ++#define BUG5762_WORKAROUND 1 /* Set all queues to jumbo mode */ ++#define BUG5391_WORKAROUND 1 /* Misaligned TX can't span 512-byte boundary */ ++#define BUG7916_WORKAROUND 1 /* RX flush gets lost */ ++ ++#endif /* B0/A01 */ ++ ++#endif /* __CI_DRIVER_EFAB_WORKAROUNDS_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/resource/efx_vi.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,273 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains public EFX VI API to Solarflare resource manager. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_RESOURCE_EFX_VI_H__ ++#define __CI_DRIVER_RESOURCE_EFX_VI_H__ ++ ++/* Default size of event queue in the efx_vi resource. Copied from ++ * CI_CFG_NETIF_EVENTQ_SIZE */ ++#define EFX_VI_EVENTQ_SIZE_DEFAULT 1024 ++ ++extern int efx_vi_eventq_size; ++ ++/************************************************************************** ++ * efx_vi_state types, allocation and free ++ **************************************************************************/ ++ ++/*! Handle for refering to a efx_vi */ ++struct efx_vi_state; ++ ++/*! ++ * Allocate an efx_vi, including event queue and pt_endpoint ++ * ++ * \param vih_out Pointer to a handle that is set on success ++ * \param ifindex Index of the network interface desired ++ * \return Zero on success (and vih_out set), non-zero on failure. ++ */ ++extern int ++efx_vi_alloc(struct efx_vi_state **vih_out, int ifindex); ++ ++/*! ++ * Free a previously allocated efx_vi ++ * ++ * \param vih The handle of the efx_vi to free ++ */ ++extern void ++efx_vi_free(struct efx_vi_state *vih); ++ ++/*! ++ * Reset a previously allocated efx_vi ++ * ++ * \param vih The handle of the efx_vi to reset ++ */ ++extern void ++efx_vi_reset(struct efx_vi_state *vih); ++ ++/************************************************************************** ++ * efx_vi_eventq types and functions ++ **************************************************************************/ ++ ++/*! ++ * Register a function to receive callbacks when event queue timeouts ++ * or wakeups occur. Only one function per efx_vi can be registered ++ * at once. ++ * ++ * \param vih The handle to identify the efx_vi ++ * \param callback The function to callback ++ * \param context An argument to pass to the callback function ++ * \return Zero on success, non-zero on failure. ++ */ ++extern int ++efx_vi_eventq_register_callback(struct efx_vi_state *vih, ++ void (*callback)(void *context, int is_timeout), ++ void *context); ++ ++/*! ++ * Remove the current eventq timeout or wakeup callback function ++ * ++ * \param vih The handle to identify the efx_vi ++ * \return Zero on success, non-zero on failure ++ */ ++extern int ++efx_vi_eventq_kill_callback(struct efx_vi_state *vih); ++ ++/************************************************************************** ++ * efx_vi_dma_map types and functions ++ **************************************************************************/ ++ ++/*! ++ * Handle for refering to a efx_vi ++ */ ++struct efx_vi_dma_map_state; ++ ++/*! ++ * Map a list of buffer pages so they are registered with the hardware ++ * ++ * \param vih The handle to identify the efx_vi ++ * \param addrs An array of page pointers to map ++ * \param n_addrs Length of the page pointer array. Must be a power of two. ++ * \param dmh_out Set on success to a handle used to refer to this mapping ++ * \return Zero on success, non-zero on failure. ++ */ ++extern int ++efx_vi_dma_map_pages(struct efx_vi_state *vih, struct page **pages, ++ int n_pages, struct efx_vi_dma_map_state **dmh_out); ++extern int ++efx_vi_dma_map_addrs(struct efx_vi_state *vih, ++ unsigned long long *dev_bus_addrs, int n_pages, ++ struct efx_vi_dma_map_state **dmh_out); ++ ++/*! ++ * Unmap a previously mapped set of pages so they are no longer registered ++ * with the hardware. ++ * ++ * \param vih The handle to identify the efx_vi ++ * \param dmh The handle to identify the dma mapping ++ */ ++extern void ++efx_vi_dma_unmap_pages(struct efx_vi_state *vih, ++ struct efx_vi_dma_map_state *dmh); ++extern void ++efx_vi_dma_unmap_addrs(struct efx_vi_state *vih, ++ struct efx_vi_dma_map_state *dmh); ++ ++/*! ++ * Retrieve the buffer address of the mapping ++ * ++ * \param vih The handle to identify the efx_vi ++ * \param dmh The handle to identify the buffer mapping ++ * \return The buffer address on success, or zero on failure ++ */ ++extern unsigned ++efx_vi_dma_get_map_addr(struct efx_vi_state *vih, ++ struct efx_vi_dma_map_state *dmh); ++ ++/************************************************************************** ++ * efx_vi filter functions ++ **************************************************************************/ ++ ++#define EFX_VI_STATIC_FILTERS 32 ++ ++/*! Handle to refer to a filter instance */ ++struct filter_resource_t; ++ ++/*! ++ * Allocate and add a filter ++ * ++ * \param vih The handle to identify the efx_vi ++ * \param protocol The protocol of the new filter: UDP or TCP ++ * \param ip_addr_be32 The local ip address of the filter ++ * \param port_le16 The local port of the filter ++ * \param fh_out Set on success to be a handle to refer to this filter ++ * \return Zero on success, non-zero on failure. ++ */ ++extern int ++efx_vi_filter(struct efx_vi_state *vih, int protocol, unsigned ip_addr_be32, ++ int port_le16, struct filter_resource_t **fh_out); ++ ++/*! ++ * Remove a filter and free resources associated with it ++ * ++ * \param vih The handle to identify the efx_vi ++ * \param fh The handle to identify the filter ++ * \return Zero on success, non-zero on failure ++ */ ++extern int ++efx_vi_filter_stop(struct efx_vi_state *vih, struct filter_resource_t *fh); ++ ++/************************************************************************** ++ * efx_vi hw resources types and functions ++ **************************************************************************/ ++ ++/*! Constants for the type field in efx_vi_hw_resource */ ++#define EFX_VI_HW_RESOURCE_TXDMAQ 0x0 /* PFN of TX DMA Q */ ++#define EFX_VI_HW_RESOURCE_RXDMAQ 0x1 /* PFN of RX DMA Q */ ++#define EFX_VI_HW_RESOURCE_EVQTIMER 0x4 /* Address of event q timer */ ++ ++/* Address of event q pointer (EF1) */ ++#define EFX_VI_HW_RESOURCE_EVQPTR 0x5 ++/* Address of register pointer (Falcon A) */ ++#define EFX_VI_HW_RESOURCE_EVQRPTR 0x6 ++/* Offset of register pointer (Falcon B) */ ++#define EFX_VI_HW_RESOURCE_EVQRPTR_OFFSET 0x7 ++/* Address of mem KVA */ ++#define EFX_VI_HW_RESOURCE_EVQMEMKVA 0x8 ++/* PFN of doorbell page (Falcon) */ ++#define EFX_VI_HW_RESOURCE_BELLPAGE 0x9 ++ ++/*! How large an array to allocate for the get_() functions - smaller ++ than the total number of constants as some are mutually exclusive */ ++#define EFX_VI_HW_RESOURCE_MAXSIZE 0x7 ++ ++/*! Constants for the mem_type field in efx_vi_hw_resource */ ++#define EFX_VI_HW_RESOURCE_IOBUFFER 0 /* Host memory */ ++#define EFX_VI_HW_RESOURCE_PERIPHERAL 1 /* Card memory/registers */ ++ ++/*! ++ * Data structure providing information on a hardware resource mapping ++ */ ++struct efx_vi_hw_resource { ++ u8 type; /*!< What this resource represents */ ++ u8 mem_type; /*!< What type of memory is it in, eg, ++ * host or iomem */ ++ u8 more_to_follow; /*!< Is this part of a multi-region resource */ ++ u32 length; /*!< Length of the resource in bytes */ ++ unsigned long address; /*!< Address of this resource */ ++}; ++ ++/*! ++ * Metadata concerning the list of hardware resource mappings ++ */ ++struct efx_vi_hw_resource_metadata { ++ int evq_order; ++ int evq_offs; ++ int evq_capacity; ++ int instance; ++ unsigned rx_capacity; ++ unsigned tx_capacity; ++ int nic_arch; ++ int nic_revision; ++ char nic_variant; ++}; ++ ++/*! ++ * Obtain a list of hardware resource mappings, using virtual addresses ++ * ++ * \param vih The handle to identify the efx_vi ++ * \param mdata Pointer to a structure to receive the metadata ++ * \param hw_res_array An array to receive the list of hardware resources ++ * \param length The length of hw_res_array. Updated on success to contain ++ * the number of entries in the supplied array that were used. ++ * \return Zero on success, non-zero on failure ++ */ ++extern int ++efx_vi_hw_resource_get_virt(struct efx_vi_state *vih, ++ struct efx_vi_hw_resource_metadata *mdata, ++ struct efx_vi_hw_resource *hw_res_array, ++ int *length); ++ ++/*! ++ * Obtain a list of hardware resource mappings, using physical addresses ++ * ++ * \param vih The handle to identify the efx_vi ++ * \param mdata Pointer to a structure to receive the metadata ++ * \param hw_res_array An array to receive the list of hardware resources ++ * \param length The length of hw_res_array. Updated on success to contain ++ * the number of entries in the supplied array that were used. ++ * \return Zero on success, non-zero on failure ++ */ ++extern int ++efx_vi_hw_resource_get_phys(struct efx_vi_state *vih, ++ struct efx_vi_hw_resource_metadata *mdata, ++ struct efx_vi_hw_resource *hw_res_array, ++ int *length); ++ ++#endif /* __CI_DRIVER_RESOURCE_EFX_VI_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/driver/resource/linux_efhw_nic.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,69 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains definition of the public type struct linux_efhw_nic. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_RESOURCE_LINUX_RESOURCE__ ++#define __CI_DRIVER_RESOURCE_LINUX_RESOURCE__ ++ ++#include ++#include ++ ++ ++/************************************************************************ ++ * Per-nic structure in the resource driver * ++ ************************************************************************/ ++ ++struct linux_efhw_nic { ++ struct efrm_nic efrm_nic; ++ ++ struct pci_dev *pci_dev; /*!< pci descriptor */ ++ struct tasklet_struct tasklet; /*!< for interrupt bottom half */ ++ ++ /* Physical addresses of the control aperture bar. */ ++ unsigned long ctr_ap_pci_addr; ++ ++ /*! Callbacks for driverlink, when needed. */ ++ struct efx_dl_callbacks *dl_callbacks; ++ ++ /*! Event handlers. */ ++ struct efhw_ev_handler *ev_handlers; ++ ++}; ++ ++#define linux_efhw_nic(_efhw_nic) \ ++ container_of(_efhw_nic, struct linux_efhw_nic, efrm_nic.efhw_nic) ++ ++#endif /* __CI_DRIVER_RESOURCE_LINUX_RESOURCE__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/checks.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,118 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides helpers to turn bit shifts into dword shifts and ++ * check that the bit fields haven't overflown the dword etc. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_CHECK_H__ ++#define __CI_EFHW_CHECK_H__ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Helpers to turn bit shifts into dword shifts and check that the bit fields ++ * haven't overflown the dword etc. Aim is to preserve consistency with the ++ * autogenerated headers - once stable we could hard code. ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/* mask constructors */ ++#define __FALCON_MASK(WIDTH, T) ((((T)1) << (WIDTH)) - 1) ++#define __FALCON_MASK32(WIDTH) __FALCON_MASK((WIDTH), uint32_t) ++#define __FALCON_MASK64(WIDTH) __FALCON_MASK((WIDTH), uint64_t) ++ ++#define __FALCON_MASKFIELD32(LBN, WIDTH) \ ++ ((uint32_t)(__FALCON_MASK32(WIDTH) << (LBN))) ++ ++/* constructors for fields which span the first and second dwords */ ++#define __LW(LBN) (32 - LBN) ++#define __LOW(v, LBN, WIDTH) \ ++ ((uint32_t)(((v) & __FALCON_MASK64(__LW((LBN)))) << (LBN))) ++#define __HIGH(v, LBN, WIDTH) \ ++ ((uint32_t)(((v) >> __LW((LBN))) & \ ++ __FALCON_MASK64((WIDTH - __LW((LBN)))))) ++/* constructors for fields within the second dword */ ++#define __DW2(LBN) ((LBN) - 32) ++ ++/* constructors for fields which span the second and third dwords */ ++#define __LW2(LBN) (64 - LBN) ++#define __LOW2(v, LBN, WIDTH) \ ++ ((uint32_t)(((v) & __FALCON_MASK64(__LW2((LBN)))) << ((LBN) - 32))) ++#define __HIGH2(v, LBN, WIDTH) \ ++ ((uint32_t)(((v) >> __LW2((LBN))) & \ ++ __FALCON_MASK64((WIDTH - __LW2((LBN)))))) ++ ++/* constructors for fields within the third dword */ ++#define __DW3(LBN) ((LBN) - 64) ++ ++/* constructors for fields which span the third and fourth dwords */ ++#define __LW3(LBN) (96 - LBN) ++#define __LOW3(v, LBN, WIDTH) \ ++ ((uint32_t)(((v) & __FALCON_MASK64(__LW3((LBN)))) << ((LBN) - 64))) ++#define __HIGH3(v, LBN, WIDTH) \ ++ ((ci_unit32)(((v) >> __LW3((LBN))) & \ ++ __FALCON_MASK64((WIDTH - __LW3((LBN)))))) ++ ++/* constructors for fields within the fourth dword */ ++#define __DW4(LBN) ((LBN) - 96) ++ ++/* checks that the autogenerated headers are consistent with our model */ ++#define __WIDTHCHCK(a, b) EFHW_ASSERT((a) == (b)) ++#define __RANGECHCK(v, WIDTH) \ ++ EFHW_ASSERT(((uint64_t)(v) & ~(__FALCON_MASK64((WIDTH)))) == 0) ++ ++/* fields within the first dword */ ++#define __DWCHCK(LBN, WIDTH) \ ++ EFHW_ASSERT(((LBN) >= 0) && (((LBN)+(WIDTH)) <= 32)) ++ ++/* fields which span the first and second dwords */ ++#define __LWCHK(LBN, WIDTH) EFHW_ASSERT(WIDTH >= __LW(LBN)) ++ ++/* fields within the second dword */ ++#define __DW2CHCK(LBN, WIDTH) \ ++ EFHW_ASSERT(((LBN) >= 32) && (((LBN)+(WIDTH)) <= 64)) ++ ++/* fields which span the second and third dwords */ ++#define __LW2CHK(LBN, WIDTH) EFHW_ASSERT(WIDTH >= __LW2(LBN)) ++ ++/* fields within the third dword */ ++#define __DW3CHCK(LBN, WIDTH) \ ++ EFHW_ASSERT(((LBN) >= 64) && (((LBN)+(WIDTH)) <= 96)) ++ ++/* fields which span the third and fourth dwords */ ++#define __LW3CHK(LBN, WIDTH) EFHW_ASSERT(WIDTH >= __LW3(LBN)) ++ ++/* fields within the fourth dword */ ++#define __DW4CHCK(LBN, WIDTH) \ ++ EFHW_ASSERT(((LBN) >= 96) && (((LBN)+(WIDTH)) <= 128)) ++ ++/* fields in the first qword */ ++#define __QWCHCK(LBN, WIDTH) \ ++ EFHW_ASSERT(((LBN) >= 0) && (((LBN)+(WIDTH)) <= 64)) ++ ++#endif /* __CI_EFHW_CHECK_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/common.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,93 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides API of the efhw library which may be used both from ++ * the kernel and from the user-space code. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_COMMON_H__ ++#define __CI_EFHW_COMMON_H__ ++ ++#include ++ ++typedef uint32_t efhw_buffer_addr_t; ++#define EFHW_BUFFER_ADDR_FMT "[ba:%"PRIx32"]" ++ ++/*! Comment? */ ++typedef union { ++ uint64_t u64; ++ struct { ++ uint32_t a; ++ uint32_t b; ++ } opaque; ++} efhw_event_t; ++ ++/* Flags for TX/RX queues */ ++#define EFHW_VI_JUMBO_EN 0x01 /*! scatter RX over multiple desc */ ++#define EFHW_VI_ISCSI_RX_HDIG_EN 0x02 /*! iscsi rx header digest */ ++#define EFHW_VI_ISCSI_TX_HDIG_EN 0x04 /*! iscsi tx header digest */ ++#define EFHW_VI_ISCSI_RX_DDIG_EN 0x08 /*! iscsi rx data digest */ ++#define EFHW_VI_ISCSI_TX_DDIG_EN 0x10 /*! iscsi tx data digest */ ++#define EFHW_VI_TX_PHYS_ADDR_EN 0x20 /*! TX physical address mode */ ++#define EFHW_VI_RX_PHYS_ADDR_EN 0x40 /*! RX physical address mode */ ++#define EFHW_VI_RM_WITH_INTERRUPT 0x80 /*! VI with an interrupt */ ++#define EFHW_VI_TX_IP_CSUM_DIS 0x100 /*! enable ip checksum generation */ ++#define EFHW_VI_TX_TCPUDP_CSUM_DIS 0x200 /*! enable tcp/udp checksum ++ generation */ ++#define EFHW_VI_TX_TCPUDP_ONLY 0x400 /*! drop non-tcp/udp packets */ ++ ++/* Types of hardware filter */ ++/* Each of these values implicitly selects scatter filters on B0 - or in ++ EFHW_IP_FILTER_TYPE_NOSCAT_B0_MASK if a non-scatter filter is required */ ++#define EFHW_IP_FILTER_TYPE_UDP_WILDCARD (0) /* dest host only */ ++#define EFHW_IP_FILTER_TYPE_UDP_FULL (1) /* dest host and port */ ++#define EFHW_IP_FILTER_TYPE_TCP_WILDCARD (2) /* dest based filter */ ++#define EFHW_IP_FILTER_TYPE_TCP_FULL (3) /* src filter */ ++/* Same again, but with RSS (for B0 only) */ ++#define EFHW_IP_FILTER_TYPE_UDP_WILDCARD_RSS_B0 (4) ++#define EFHW_IP_FILTER_TYPE_UDP_FULL_RSS_B0 (5) ++#define EFHW_IP_FILTER_TYPE_TCP_WILDCARD_RSS_B0 (6) ++#define EFHW_IP_FILTER_TYPE_TCP_FULL_RSS_B0 (7) ++ ++#define EFHW_IP_FILTER_TYPE_FULL_MASK (0x1) /* Mask for full / wildcard */ ++#define EFHW_IP_FILTER_TYPE_TCP_MASK (0x2) /* Mask for TCP type */ ++#define EFHW_IP_FILTER_TYPE_RSS_B0_MASK (0x4) /* Mask for B0 RSS enable */ ++#define EFHW_IP_FILTER_TYPE_NOSCAT_B0_MASK (0x8) /* Mask for B0 SCATTER dsbl */ ++ ++#define EFHW_IP_FILTER_TYPE_MASK (0xffff) /* Mask of types above */ ++ ++#define EFHW_IP_FILTER_BROADCAST (0x10000) /* driverlink filter ++ support */ ++ ++#endif /* __CI_EFHW_COMMON_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/common_sysdep.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,61 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides version-independent Linux kernel API for ++ * userland-to-kernel interfaces. ++ * Only kernels >=2.6.9 are supported. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_COMMON_LINUX_H__ ++#define __CI_EFHW_COMMON_LINUX_H__ ++ ++#include ++ ++/* Dirty hack, but Linux kernel does not provide DMA_ADDR_T_FMT */ ++#if BITS_PER_LONG == 64 || defined(CONFIG_HIGHMEM64G) ++#define DMA_ADDR_T_FMT "%llx" ++#else ++#define DMA_ADDR_T_FMT "%x" ++#endif ++ ++/* Linux kernel also does not provide PRIx32... Sigh. */ ++#define PRIx32 "x" ++ ++#ifdef __ia64__ ++# define PRIx64 "lx" ++#else ++# define PRIx64 "llx" ++#endif ++ ++#endif /* __CI_EFHW_COMMON_LINUX_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/debug.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,84 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides debug-related API for efhw library using Linux kernel ++ * primitives. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_DEBUG_LINUX_H__ ++#define __CI_EFHW_DEBUG_LINUX_H__ ++ ++#define EFHW_PRINTK_PREFIX "[sfc efhw] " ++ ++#define EFHW_PRINTK(level, fmt, ...) \ ++ printk(level EFHW_PRINTK_PREFIX fmt "\n", __VA_ARGS__) ++ ++/* Following macros should be used with non-zero format parameters ++ * due to __VA_ARGS__ limitations. Use "%s" with __func__ if you can't ++ * find better parameters. */ ++#define EFHW_ERR(fmt, ...) EFHW_PRINTK(KERN_ERR, fmt, __VA_ARGS__) ++#define EFHW_WARN(fmt, ...) EFHW_PRINTK(KERN_WARNING, fmt, __VA_ARGS__) ++#define EFHW_NOTICE(fmt, ...) EFHW_PRINTK(KERN_NOTICE, fmt, __VA_ARGS__) ++#if 0 && !defined(NDEBUG) ++#define EFHW_TRACE(fmt, ...) EFHW_PRINTK(KERN_DEBUG, fmt, __VA_ARGS__) ++#else ++#define EFHW_TRACE(fmt, ...) ++#endif ++ ++#ifndef NDEBUG ++#define EFHW_ASSERT(cond) BUG_ON((cond) == 0) ++#define EFHW_DO_DEBUG(expr) expr ++#else ++#define EFHW_ASSERT(cond) ++#define EFHW_DO_DEBUG(expr) ++#endif ++ ++#define EFHW_TEST(expr) \ ++ do { \ ++ if (unlikely(!(expr))) \ ++ BUG(); \ ++ } while (0) ++ ++/* Build time asserts. We paste the line number into the type name ++ * so that the macro can be used more than once per file even if the ++ * compiler objects to multiple identical typedefs. Collisions ++ * between use in different header files is still possible. */ ++#ifndef EFHW_BUILD_ASSERT ++#define __EFHW_BUILD_ASSERT_NAME(_x) __EFHW_BUILD_ASSERT_ILOATHECPP(_x) ++#define __EFHW_BUILD_ASSERT_ILOATHECPP(_x) __EFHW_BUILD_ASSERT__ ##_x ++#define EFHW_BUILD_ASSERT(e) \ ++ typedef char __EFHW_BUILD_ASSERT_NAME(__LINE__)[(e) ? 1 : -1] ++#endif ++ ++#endif /* __CI_EFHW_DEBUG_LINUX_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/efhw_config.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,43 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides some limits used in both kernel and userland code. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_EFAB_CONFIG_H__ ++#define __CI_EFHW_EFAB_CONFIG_H__ ++ ++#define EFHW_MAX_NR_DEVS 5 /* max number of efhw devices supported */ ++ ++#endif /* __CI_EFHW_EFAB_CONFIG_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/efhw_types.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,382 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides struct efhw_nic and some related types. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_EFAB_TYPES_H__ ++#define __CI_EFHW_EFAB_TYPES_H__ ++ ++#include ++#include ++#include ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * forward type declarations ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_nic; ++ ++/*-------------------------------------------------------------------- ++ * ++ * Managed interface ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_buffer_table_allocation{ ++ unsigned base; ++ unsigned order; ++}; ++ ++struct eventq_resource_hardware { ++ /*!iobuffer allocated for eventq - can be larger than eventq */ ++ struct efhw_iopages iobuff; ++ unsigned iobuff_off; ++ struct efhw_buffer_table_allocation buf_tbl_alloc; ++ int capacity; /*!< capacity of event queue */ ++}; ++ ++/*-------------------------------------------------------------------- ++ * ++ * event queues and event driven callbacks ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_keventq { ++ int lock; ++ caddr_t evq_base; ++ int32_t evq_ptr; ++ uint32_t evq_mask; ++ unsigned instance; ++ struct eventq_resource_hardware hw; ++ struct efhw_ev_handler *ev_handlers; ++}; ++ ++/*-------------------------------------------------------------------- ++ * ++ * filters ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_filter_spec { ++ uint dmaq_id; ++ uint32_t saddr_le32; ++ uint32_t daddr_le32; ++ uint16_t sport_le16; ++ uint16_t dport_le16; ++ unsigned tcp : 1; ++ unsigned full : 1; ++ unsigned rss : 1; /* not supported on A1 */ ++ unsigned scatter : 1; /* not supported on A1 */ ++}; ++ ++struct efhw_filter_depth { ++ unsigned needed; ++ unsigned max; ++}; ++ ++struct efhw_filter_search_limits { ++ unsigned tcp_full; ++ unsigned tcp_wild; ++ unsigned udp_full; ++ unsigned udp_wild; ++}; ++ ++ ++/********************************************************************** ++ * Portable HW interface. *************************************** ++ **********************************************************************/ ++ ++/*-------------------------------------------------------------------- ++ * ++ * EtherFabric Functional units - configuration and control ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_func_ops { ++ ++ /*-------------- Initialisation ------------ */ ++ ++ /*! close down all hardware functional units - leaves NIC in a safe ++ state for driver unload */ ++ void (*close_hardware) (struct efhw_nic *nic); ++ ++ /*! initialise all hardware functional units */ ++ int (*init_hardware) (struct efhw_nic *nic, ++ struct efhw_ev_handler *, ++ const uint8_t *mac_addr, int non_irq_evq); ++ ++ /*-------------- Interrupt support ------------ */ ++ ++ /*! Main interrupt routine ++ ** This function returns, ++ ** - zero, if the IRQ was not generated by EF1 ++ ** - non-zero, if EF1 was the source of the IRQ ++ ** ++ ** ++ ** opaque is an OS provided pointer for use by the OS callbacks ++ ** e.g in Windows used to indicate DPC scheduled ++ */ ++ int (*interrupt) (struct efhw_nic *nic); ++ ++ /*! Enable the interrupt */ ++ void (*interrupt_enable) (struct efhw_nic *nic); ++ ++ /*! Disable the interrupt */ ++ void (*interrupt_disable) (struct efhw_nic *nic); ++ ++ /*! Set interrupt moderation strategy for the given IRQ unit ++ ** val is in usec ++ */ ++ void (*set_interrupt_moderation)(struct efhw_nic *nic, int evq, ++ uint val); ++ ++ /*-------------- Event support ------------ */ ++ ++ /*! Enable the given event queue ++ depending on the underlying implementation (EF1 or Falcon) then ++ either a q_base_addr in host memory, or a buffer base id should ++ be proivded ++ */ ++ void (*event_queue_enable) (struct efhw_nic *nic, ++ uint evq, /* evnt queue index */ ++ uint evq_size, /* units of #entries */ ++ dma_addr_t q_base_addr, uint buf_base_id, ++ int interrupting); ++ ++ /*! Disable the given event queue (and any associated timer) */ ++ void (*event_queue_disable) (struct efhw_nic *nic, uint evq, ++ int timer_only); ++ ++ /*! request wakeup from the NIC on a given event Q */ ++ void (*wakeup_request) (struct efhw_nic *nic, dma_addr_t q_base_addr, ++ int next_i, int evq); ++ ++ /*! Push a SW event on a given eventQ */ ++ void (*sw_event) (struct efhw_nic *nic, int data, int evq); ++ ++ /*-------------- IP Filter API ------------ */ ++ ++ /*! Setup a given filter - The software can request a filter_i, ++ * but some EtherFabric implementations will override with ++ * a more suitable index ++ */ ++ int (*ipfilter_set) (struct efhw_nic *nic, int type, ++ int *filter_i, int dmaq, ++ unsigned saddr_be32, unsigned sport_be16, ++ unsigned daddr_be32, unsigned dport_be16); ++ ++ /*! Clear down a given filter */ ++ void (*ipfilter_clear) (struct efhw_nic *nic, int filter_idx); ++ ++ /*-------------- DMA support ------------ */ ++ ++ /*! Initialise NIC state for a given TX DMAQ */ ++ void (*dmaq_tx_q_init) (struct efhw_nic *nic, ++ uint dmaq, uint evq, uint owner, uint tag, ++ uint dmaq_size, uint buf_idx, uint flags); ++ ++ /*! Initialise NIC state for a given RX DMAQ */ ++ void (*dmaq_rx_q_init) (struct efhw_nic *nic, ++ uint dmaq, uint evq, uint owner, uint tag, ++ uint dmaq_size, uint buf_idx, uint flags); ++ ++ /*! Disable a given TX DMAQ */ ++ void (*dmaq_tx_q_disable) (struct efhw_nic *nic, uint dmaq); ++ ++ /*! Disable a given RX DMAQ */ ++ void (*dmaq_rx_q_disable) (struct efhw_nic *nic, uint dmaq); ++ ++ /*! Flush a given TX DMA channel */ ++ int (*flush_tx_dma_channel) (struct efhw_nic *nic, uint dmaq); ++ ++ /*! Flush a given RX DMA channel */ ++ int (*flush_rx_dma_channel) (struct efhw_nic *nic, uint dmaq); ++ ++ /*-------------- Buffer table Support ------------ */ ++ ++ /*! Initialise a buffer table page */ ++ void (*buffer_table_set) (struct efhw_nic *nic, ++ dma_addr_t dma_addr, ++ uint bufsz, uint region, ++ int own_id, int buffer_id); ++ ++ /*! Initialise a block of buffer table pages */ ++ void (*buffer_table_set_n) (struct efhw_nic *nic, int buffer_id, ++ dma_addr_t dma_addr, ++ uint bufsz, uint region, ++ int n_pages, int own_id); ++ ++ /*! Clear a block of buffer table pages */ ++ void (*buffer_table_clear) (struct efhw_nic *nic, int buffer_id, ++ int num); ++ ++ /*! Commit a buffer table update */ ++ void (*buffer_table_commit) (struct efhw_nic *nic); ++ ++ /*-------------- New filter API ------------ */ ++ ++ /*! Set a given filter */ ++ int (*filter_set) (struct efhw_nic *nic, struct efhw_filter_spec *spec, ++ int *filter_idx_out); ++ ++ /*! Clear a given filter */ ++ void (*filter_clear) (struct efhw_nic *nic, int filter_idx); ++}; ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * NIC type ++ * ++ *---------------------------------------------------------------------------*/ ++ ++struct efhw_device_type { ++ int arch; /* enum efhw_arch */ ++ char variant; /* 'A', 'B', ... */ ++ int revision; /* 0, 1, ... */ ++}; ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * EtherFabric NIC instance - nic.c for HW independent functions ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/*! */ ++struct efhw_nic { ++ /*! zero base index in efrm_nic_tablep->nic array */ ++ int index; ++ int ifindex; /*!< OS level nic index */ ++ struct net *nd_net; ++ ++ struct efhw_device_type devtype; ++ ++ /*! Options that can be set by user. */ ++ unsigned options; ++# define NIC_OPT_EFTEST 0x1 /* owner is an eftest app */ ++ ++# define NIC_OPT_DEFAULT 0 ++ ++ /*! Internal flags that indicate hardware properties at runtime. */ ++ unsigned flags; ++# define NIC_FLAG_NO_INTERRUPT 0x01 /* to be set at init time only */ ++# define NIC_FLAG_TRY_MSI 0x02 ++# define NIC_FLAG_MSI 0x04 ++# define NIC_FLAG_OS_IRQ_EN 0x08 ++ ++ unsigned mtu; /*!< MAC MTU (includes MAC hdr) */ ++ ++ /* hardware resources */ ++ ++ /*! I/O address of the start of the bar */ ++ volatile char __iomem *bar_ioaddr; ++ ++ /*! Bar number of control aperture. */ ++ unsigned ctr_ap_bar; ++ /*! Length of control aperture in bytes. */ ++ unsigned ctr_ap_bytes; ++ ++ uint8_t mac_addr[ETH_ALEN]; /*!< mac address */ ++ ++ /*! EtherFabric Functional Units -- functions */ ++ const struct efhw_func_ops *efhw_func; ++ ++ /*! This lock protects a number of misc NIC resources. It should ++ * only be used for things that can be at the bottom of the lock ++ * order. ie. You mustn't attempt to grab any other lock while ++ * holding this one. ++ */ ++ spinlock_t *reg_lock; ++ spinlock_t the_reg_lock; ++ ++ int buf_commit_outstanding; /*!< outstanding buffer commits */ ++ ++ /*! interrupt callbacks (hard-irq) */ ++ void (*irq_handler) (struct efhw_nic *, int unit); ++ ++ /*! event queues per driver */ ++ struct efhw_keventq interrupting_evq; ++ ++/* for marking when we are not using an IRQ unit ++ - 0 is a valid offset to an IRQ unit on EF1! */ ++#define EFHW_IRQ_UNIT_UNUSED 0xffff ++ /*! interrupt unit in use for the interrupting event queue */ ++ unsigned int irq_unit; ++ ++ struct efhw_keventq non_interrupting_evq; ++ ++ struct efhw_iopage irq_iobuff; /*!< Falcon SYSERR interrupt */ ++ ++ /* The new driverlink infrastructure. */ ++ struct efx_dl_device *net_driver_dev; ++ struct efx_dlfilt_cb_s *dlfilter_cb; ++ ++ /*! Bit masks of the sizes of event queues and dma queues supported ++ * by the nic. */ ++ unsigned evq_sizes; ++ unsigned rxq_sizes; ++ unsigned txq_sizes; ++ ++ /* Size of filter table. */ ++ unsigned ip_filter_tbl_size; ++ ++ /* Number of filters currently used */ ++ unsigned ip_filter_tbl_used; ++ ++ /* Dynamically allocated filter state. */ ++ uint8_t *filter_in_use; ++ struct efhw_filter_spec *filter_spec_cache; ++ ++ /* Currently required and maximum filter table search depths. */ ++ struct efhw_filter_depth tcp_full_srch; ++ struct efhw_filter_depth tcp_wild_srch; ++ struct efhw_filter_depth udp_full_srch; ++ struct efhw_filter_depth udp_wild_srch; ++ ++ /* Number of event queues, DMA queues and timers. */ ++ unsigned num_evqs; ++ unsigned num_dmaqs; ++ unsigned num_timers; ++}; ++ ++ ++#define EFHW_KVA(nic) ((nic)->bar_ioaddr) ++ ++ ++#endif /* __CI_EFHW_EFHW_TYPES_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/eventq.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,72 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains API provided by efhw/eventq.c file. This file is not ++ * designed for use outside of the SFC resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_EVENTQ_H__ ++#define __CI_EFHW_EVENTQ_H__ ++ ++#include ++#include ++ ++/*! Poll the event queue. */ ++extern int efhw_keventq_poll(struct efhw_nic *, struct efhw_keventq *); ++ ++/*! Callbacks for handling events. */ ++struct efhw_ev_handler { ++ void (*wakeup_fn)(struct efhw_nic *nic, unsigned); ++ void (*timeout_fn)(struct efhw_nic *nic, unsigned); ++ void (*dmaq_flushed_fn) (struct efhw_nic *, unsigned, int); ++}; ++ ++extern int efhw_keventq_ctor(struct efhw_nic *, int instance, ++ struct efhw_keventq *, struct efhw_ev_handler *); ++extern void efhw_keventq_dtor(struct efhw_nic *, struct efhw_keventq *); ++ ++extern void efhw_handle_txdmaq_flushed(struct efhw_nic *, ++ struct efhw_ev_handler *, ++ efhw_event_t *); ++extern void efhw_handle_rxdmaq_flushed(struct efhw_nic *, ++ struct efhw_ev_handler *, ++ efhw_event_t *); ++extern void efhw_handle_wakeup_event(struct efhw_nic *, ++ struct efhw_ev_handler *, ++ efhw_event_t *); ++extern void efhw_handle_timeout_event(struct efhw_nic *, ++ struct efhw_ev_handler *, ++ efhw_event_t *); ++ ++#endif /* __CI_EFHW_EVENTQ_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/eventq_macros.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,77 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides some event-related macros. This file is designed for ++ * use from kernel and from the userland contexts. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_EVENTQ_MACROS_H__ ++#define __CI_EFHW_EVENTQ_MACROS_H__ ++ ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * Event Queue manipulation ++ * ++ *--------------------------------------------------------------------*/ ++ ++#define EFHW_EVENT_OFFSET(q, s, i) \ ++ (((s)->evq_ptr - (i) * (int32_t)sizeof(efhw_event_t)) \ ++ & (q)->evq_mask) ++ ++#define EFHW_EVENT_PTR(q, s, i) \ ++ ((efhw_event_t *)((q)->evq_base + EFHW_EVENT_OFFSET(q, s, i))) ++ ++#define EFHW_EVENTQ_NEXT(s) \ ++ do { ((s)->evq_ptr += sizeof(efhw_event_t)); } while (0) ++ ++#define EFHW_EVENTQ_PREV(s) \ ++ do { ((s)->evq_ptr -= sizeof(efhw_event_t)); } while (0) ++ ++/* Be worried about this on byteswapped machines */ ++/* Due to crazy chipsets, we see the event words being written in ++** arbitrary order (bug4539). So test for presence of event must ensure ++** that both halves have changed from the null. ++*/ ++#define EFHW_IS_EVENT(evp) \ ++ (((evp)->opaque.a != (uint32_t)-1) && \ ++ ((evp)->opaque.b != (uint32_t)-1)) ++#define EFHW_CLEAR_EVENT(evp) ((evp)->u64 = (uint64_t)-1) ++#define EFHW_CLEAR_EVENT_VALUE 0xff ++ ++#define EFHW_EVENT_OVERFLOW(evq, s) \ ++ (EFHW_IS_EVENT(EFHW_EVENT_PTR(evq, s, 1))) ++ ++#endif /* __CI_EFHW_EVENTQ_MACROS_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/falcon.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,94 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains API provided by efhw/falcon.c file. This file is not ++ * designed for use outside of the SFC resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_FALCON_H__ ++#define __CI_EFHW_FALCON_H__ ++ ++#include ++#include ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Locks - unfortunately required ++ * ++ *---------------------------------------------------------------------------*/ ++ ++#define FALCON_LOCK_DECL irq_flags_t lock_state ++#define FALCON_LOCK_LOCK(nic) \ ++ spin_lock_irqsave((nic)->reg_lock, lock_state) ++#define FALCON_LOCK_UNLOCK(nic) \ ++ spin_unlock_irqrestore((nic)->reg_lock, lock_state) ++ ++extern struct efhw_func_ops falcon_char_functional_units; ++ ++/*! specify a pace value for a TX DMA Queue */ ++extern void falcon_nic_pace(struct efhw_nic *nic, uint dmaq, uint pace); ++ ++/*! configure the pace engine */ ++extern void falcon_nic_pace_cfg(struct efhw_nic *nic, int fb_base, ++ int bin_thresh); ++ ++/*! confirm buffer table updates - should be used for items where ++ loss of data would be unacceptable. E.g for the buffers that back ++ an event or DMA queue */ ++extern void falcon_nic_buffer_table_confirm(struct efhw_nic *nic); ++ ++/*! Reset the all the TX DMA queue pointers. */ ++extern void falcon_clobber_tx_dma_ptrs(struct efhw_nic *nic, uint dmaq); ++ ++extern int ++falcon_handle_char_event(struct efhw_nic *nic, ++ struct efhw_ev_handler *h, efhw_event_t *evp); ++ ++/*! Acknowledge to HW that processing is complete on a given event queue */ ++extern void falcon_nic_evq_ack(struct efhw_nic *nic, uint evq, /* evq id */ ++ uint rptr, /* new read pointer update */ ++ bool wakeup /* request a wakeup event if ++ ptr's != */ ++ ); ++ ++extern void ++falcon_nic_buffer_table_set_n(struct efhw_nic *nic, int buffer_id, ++ dma_addr_t dma_addr, uint bufsz, uint region, ++ int n_pages, int own_id); ++ ++extern int falcon_nic_filter_ctor(struct efhw_nic *nic); ++ ++extern void falcon_nic_filter_dtor(struct efhw_nic *nic); ++ ++#endif /* __CI_EFHW_FALCON_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/falcon_hash.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,58 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains API provided by efhw/falcon_hash.c file. ++ * Function declared in this file are not exported from the Linux ++ * sfc_resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_FALCON_HASH_H__ ++#define __CI_EFHW_FALCON_HASH_H__ ++ ++extern unsigned int ++falcon_hash_get_ip_key(unsigned int src_ip, unsigned int src_port, ++ unsigned int dest_ip, unsigned int dest_port, ++ int tcp, int full); ++ ++extern unsigned int ++falcon_hash_function1(unsigned int key, unsigned int nfilters); ++ ++extern unsigned int ++falcon_hash_function2(unsigned int key, unsigned int nfilters); ++ ++extern unsigned int ++falcon_hash_iterator(unsigned int hash1, unsigned int hash2, ++ unsigned int n_search, unsigned int nfilters); ++ ++#endif /* __CI_EFHW_FALCON_HASH_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/hardware_sysdep.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,69 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides version-independent Linux kernel API for header files ++ * with hardware-related definitions (in ci/driver/efab/hardware*). ++ * Only kernels >=2.6.9 are supported. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_HARDWARE_LINUX_H__ ++#define __CI_EFHW_HARDWARE_LINUX_H__ ++ ++#include ++ ++#ifdef __LITTLE_ENDIAN ++#define EFHW_IS_LITTLE_ENDIAN ++#elif __BIG_ENDIAN ++#define EFHW_IS_BIG_ENDIAN ++#else ++#error Unknown endianness ++#endif ++ ++#ifndef readq ++static inline uint64_t __readq(volatile void __iomem *addr) ++{ ++ return *(volatile uint64_t *)addr; ++} ++#define readq(x) __readq(x) ++#endif ++ ++#ifndef writeq ++static inline void __writeq(uint64_t v, volatile void __iomem *addr) ++{ ++ *(volatile uint64_t *)addr = v; ++} ++#define writeq(val, addr) __writeq((val), (addr)) ++#endif ++ ++#endif /* __CI_EFHW_HARDWARE_LINUX_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/iopage.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,58 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains OS-independent API for allocating iopage types. ++ * The implementation of these functions is highly OS-dependent. ++ * This file is not designed for use outside of the SFC resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_RESOURCE_IOPAGE_H__ ++#define __CI_DRIVER_RESOURCE_IOPAGE_H__ ++ ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * memory allocation ++ * ++ *--------------------------------------------------------------------*/ ++ ++extern int efhw_iopage_alloc(struct efhw_nic *, struct efhw_iopage *p); ++extern void efhw_iopage_free(struct efhw_nic *, struct efhw_iopage *p); ++ ++extern int efhw_iopages_alloc(struct efhw_nic *, struct efhw_iopages *p, ++ unsigned order); ++extern void efhw_iopages_free(struct efhw_nic *, struct efhw_iopages *p); ++ ++#endif /* __CI_DRIVER_RESOURCE_IOPAGE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/iopage_types.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,190 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides struct efhw_page and struct efhw_iopage for Linux ++ * kernel. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_IOPAGE_LINUX_H__ ++#define __CI_EFHW_IOPAGE_LINUX_H__ ++ ++#include ++#include ++#include ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * struct efhw_page: A single page of memory. Directly mapped in the ++ * driver, and can be mapped to userlevel. ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_page { ++ unsigned long kva; ++}; ++ ++static inline int efhw_page_alloc(struct efhw_page *p) ++{ ++ p->kva = __get_free_page(in_interrupt()? GFP_ATOMIC : GFP_KERNEL); ++ return p->kva ? 0 : -ENOMEM; ++} ++ ++static inline int efhw_page_alloc_zeroed(struct efhw_page *p) ++{ ++ p->kva = get_zeroed_page(in_interrupt()? GFP_ATOMIC : GFP_KERNEL); ++ return p->kva ? 0 : -ENOMEM; ++} ++ ++static inline void efhw_page_free(struct efhw_page *p) ++{ ++ free_page(p->kva); ++ EFHW_DO_DEBUG(memset(p, 0, sizeof(*p))); ++} ++ ++static inline char *efhw_page_ptr(struct efhw_page *p) ++{ ++ return (char *)p->kva; ++} ++ ++static inline unsigned efhw_page_pfn(struct efhw_page *p) ++{ ++ return (unsigned)(__pa(p->kva) >> PAGE_SHIFT); ++} ++ ++static inline void efhw_page_mark_invalid(struct efhw_page *p) ++{ ++ p->kva = 0; ++} ++ ++static inline int efhw_page_is_valid(struct efhw_page *p) ++{ ++ return p->kva != 0; ++} ++ ++static inline void efhw_page_init_from_va(struct efhw_page *p, void *va) ++{ ++ p->kva = (unsigned long)va; ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * struct efhw_iopage: A single page of memory. Directly mapped in the driver, ++ * and can be mapped to userlevel. Can also be accessed by the NIC. ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_iopage { ++ struct efhw_page p; ++ dma_addr_t dma_addr; ++}; ++ ++static inline dma_addr_t efhw_iopage_dma_addr(struct efhw_iopage *p) ++{ ++ return p->dma_addr; ++} ++ ++#define efhw_iopage_ptr(iop) efhw_page_ptr(&(iop)->p) ++#define efhw_iopage_pfn(iop) efhw_page_pfn(&(iop)->p) ++#define efhw_iopage_mark_invalid(iop) efhw_page_mark_invalid(&(iop)->p) ++#define efhw_iopage_is_valid(iop) efhw_page_is_valid(&(iop)->p) ++ ++/*-------------------------------------------------------------------- ++ * ++ * struct efhw_iopages: A set of pages that are contiguous in physical ++ * memory. Directly mapped in the driver, and can be mapped to userlevel. ++ * Can also be accessed by the NIC. ++ * ++ * NB. The O/S may be unwilling to allocate many, or even any of these. So ++ * only use this type where the NIC really needs a physically contiguous ++ * buffer. ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_iopages { ++ caddr_t kva; ++ unsigned order; ++ dma_addr_t dma_addr; ++}; ++ ++static inline caddr_t efhw_iopages_ptr(struct efhw_iopages *p) ++{ ++ return p->kva; ++} ++ ++static inline unsigned efhw_iopages_pfn(struct efhw_iopages *p) ++{ ++ return (unsigned)(__pa(p->kva) >> PAGE_SHIFT); ++} ++ ++static inline dma_addr_t efhw_iopages_dma_addr(struct efhw_iopages *p) ++{ ++ return p->dma_addr; ++} ++ ++static inline unsigned efhw_iopages_size(struct efhw_iopages *p) ++{ ++ return 1u << (p->order + PAGE_SHIFT); ++} ++ ++/* struct efhw_iopage <-> struct efhw_iopages conversions for handling ++ * physically contiguous allocations in iobufsets for iSCSI. This allows ++ * the essential information about contiguous allocations from ++ * efhw_iopages_alloc() to be saved away in the struct efhw_iopage array in ++ * an iobufset. (Changing the iobufset resource to use a union type would ++ * involve a lot of code changes, and make the iobufset's metadata larger ++ * which could be bad as it's supposed to fit into a single page on some ++ * platforms.) ++ */ ++static inline void ++efhw_iopage_init_from_iopages(struct efhw_iopage *iopage, ++ struct efhw_iopages *iopages, unsigned pageno) ++{ ++ iopage->p.kva = ((unsigned long)efhw_iopages_ptr(iopages)) ++ + (pageno * PAGE_SIZE); ++ iopage->dma_addr = efhw_iopages_dma_addr(iopages) + ++ (pageno * PAGE_SIZE); ++} ++ ++static inline void ++efhw_iopages_init_from_iopage(struct efhw_iopages *iopages, ++ struct efhw_iopage *iopage, unsigned order) ++{ ++ iopages->kva = (caddr_t) efhw_iopage_ptr(iopage); ++ EFHW_ASSERT(iopages->kva); ++ iopages->order = order; ++ iopages->dma_addr = efhw_iopage_dma_addr(iopage); ++} ++ ++#endif /* __CI_EFHW_IOPAGE_LINUX_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/nic.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,62 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains API provided by efhw/nic.c file. This file is not ++ * designed for use outside of the SFC resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_NIC_H__ ++#define __CI_EFHW_NIC_H__ ++ ++#include ++#include ++ ++ ++/* Convert PCI info to device type. Returns false when device is not ++ * recognised. ++ */ ++extern int efhw_device_type_init(struct efhw_device_type *dt, ++ int vendor_id, int device_id, int revision); ++ ++/* Initialise fields that do not involve touching hardware. */ ++extern void efhw_nic_init(struct efhw_nic *nic, unsigned flags, ++ unsigned options, struct efhw_device_type dev_type); ++ ++/*! Destruct NIC resources */ ++extern void efhw_nic_dtor(struct efhw_nic *nic); ++ ++/*! Shutdown interrupts */ ++extern void efhw_nic_close_interrupts(struct efhw_nic *nic); ++ ++#endif /* __CI_EFHW_NIC_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/public.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,104 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides public API of efhw library exported from the SFC ++ * resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_PUBLIC_H__ ++#define __CI_EFHW_PUBLIC_H__ ++ ++#include ++#include ++ ++/*! Returns true if we have some EtherFabric functional units - ++ whether configured or not */ ++static inline int efhw_nic_have_functional_units(struct efhw_nic *nic) ++{ ++ return nic->efhw_func != 0; ++} ++ ++/*! Returns true if the EtherFabric functional units have been configured */ ++static inline int efhw_nic_have_hw(struct efhw_nic *nic) ++{ ++ return efhw_nic_have_functional_units(nic) && (EFHW_KVA(nic) != 0); ++} ++ ++/*! Helper function to allocate the iobuffer needed by an eventq ++ * - it ensures the eventq has the correct alignment for the NIC ++ * ++ * \param rm Event-queue resource manager ++ * \param instance Event-queue instance (index) ++ * \param buf_bytes Requested size of eventq ++ * \return < 0 if iobuffer allocation fails ++ */ ++int efhw_nic_event_queue_alloc_iobuffer(struct efhw_nic *nic, ++ struct eventq_resource_hardware *h, ++ int evq_instance, unsigned buf_bytes); ++ ++extern void falcon_nic_set_rx_usr_buf_size(struct efhw_nic *, ++ int rx_usr_buf_size); ++ ++/*! Get RX filter search limits from RX_FILTER_CTL_REG. ++ * use_raw_values = 0 to get actual depth of search, or 1 to get raw values ++ * from register. ++ */ ++extern void ++falcon_nic_get_rx_filter_search_limits(struct efhw_nic *nic, ++ struct efhw_filter_search_limits *lim, ++ int use_raw_values); ++ ++/*! Set RX filter search limits in RX_FILTER_CTL_REG. ++ * use_raw_values = 0 if specifying actual depth of search, or 1 if specifying ++ * raw values to write to the register. ++ */ ++extern void ++falcon_nic_set_rx_filter_search_limits(struct efhw_nic *nic, ++ struct efhw_filter_search_limits *lim, ++ int use_raw_values); ++ ++ ++/*! Legacy RX IP filter search depth control interface */ ++extern void ++falcon_nic_rx_filter_ctl_set(struct efhw_nic *nic, uint32_t tcp_full, ++ uint32_t tcp_wild, ++ uint32_t udp_full, uint32_t udp_wild); ++ ++/*! Legacy RX IP filter search depth control interface */ ++extern void ++falcon_nic_rx_filter_ctl_get(struct efhw_nic *nic, uint32_t *tcp_full, ++ uint32_t *tcp_wild, ++ uint32_t *udp_full, uint32_t *udp_wild); ++ ++#endif /* __CI_EFHW_PUBLIC_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efhw/sysdep.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,55 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides version-independent Linux kernel API for efhw library. ++ * Only kernels >=2.6.9 are supported. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFHW_SYSDEP_LINUX_H__ ++#define __CI_EFHW_SYSDEP_LINUX_H__ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include /* necessary for etherdevice.h on some kernels */ ++#include ++ ++typedef unsigned long irq_flags_t; ++ ++#define spin_lock_destroy(l_) do {} while (0) ++ ++#endif /* __CI_EFHW_SYSDEP_LINUX_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/buddy.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,68 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides private API for buddy allocator. This API is not ++ * designed for use outside of SFC resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_BUDDY_H__ ++#define __CI_EFRM_BUDDY_H__ ++ ++#include ++ ++/*! Comment? */ ++struct efrm_buddy_allocator { ++ struct list_head *free_lists; /* array[order+1] */ ++ struct list_head *links; /* array[1<order; ++} ++ ++int efrm_buddy_ctor(struct efrm_buddy_allocator *b, unsigned order); ++void efrm_buddy_dtor(struct efrm_buddy_allocator *b); ++int efrm_buddy_alloc(struct efrm_buddy_allocator *b, unsigned order); ++void efrm_buddy_free(struct efrm_buddy_allocator *b, unsigned addr, ++ unsigned order); ++ ++ ++#endif /* __CI_EFRM_BUDDY_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/buffer_table.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,81 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides private buffer table API. This API is not designed ++ * for use outside of SFC resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_BUFFER_TABLE_H__ ++#define __CI_EFRM_BUFFER_TABLE_H__ ++ ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * NIC's buffer table. ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! Managed interface. */ ++ ++/*! construct a managed buffer table object, allocated over a region of ++ * the NICs buffer table space ++ */ ++extern int efrm_buffer_table_ctor(unsigned low, unsigned high); ++/*! destructor for above */ ++extern void efrm_buffer_table_dtor(void); ++ ++/*! allocate a contiguous region of buffer table space */ ++extern int efrm_buffer_table_alloc(unsigned order, ++ struct efhw_buffer_table_allocation *a); ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * buffer table operations through the HW independent API ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! free a previously allocated region of buffer table space */ ++extern void efrm_buffer_table_free(struct efhw_buffer_table_allocation *a); ++ ++/*! commit the update of a buffer table entry to every NIC */ ++extern void efrm_buffer_table_commit(void); ++ ++extern void efrm_buffer_table_set(struct efhw_buffer_table_allocation *, ++ struct efhw_nic *, ++ unsigned i, dma_addr_t dma_addr, int owner); ++ ++ ++#endif /* __CI_EFRM_BUFFER_TABLE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/debug.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,78 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides debug-related API for efrm library using Linux kernel ++ * primitives. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_DEBUG_LINUX_H__ ++#define __CI_EFRM_DEBUG_LINUX_H__ ++ ++#define EFRM_PRINTK_PREFIX "[sfc efrm] " ++ ++#define EFRM_PRINTK(level, fmt, ...) \ ++ printk(level EFRM_PRINTK_PREFIX fmt "\n", __VA_ARGS__) ++ ++/* Following macros should be used with non-zero format parameters ++ * due to __VA_ARGS__ limitations. Use "%s" with __func__ if you can't ++ * find better parameters. */ ++#define EFRM_ERR(fmt, ...) EFRM_PRINTK(KERN_ERR, fmt, __VA_ARGS__) ++#define EFRM_WARN(fmt, ...) EFRM_PRINTK(KERN_WARNING, fmt, __VA_ARGS__) ++#define EFRM_NOTICE(fmt, ...) EFRM_PRINTK(KERN_NOTICE, fmt, __VA_ARGS__) ++#if !defined(NDEBUG) ++#define EFRM_TRACE(fmt, ...) EFRM_PRINTK(KERN_DEBUG, fmt, __VA_ARGS__) ++#else ++#define EFRM_TRACE(fmt, ...) ++#endif ++ ++#ifndef NDEBUG ++#define EFRM_ASSERT(cond) BUG_ON((cond) == 0) ++#define _EFRM_ASSERT(cond, file, line) \ ++ do { \ ++ if (unlikely(!(cond))) { \ ++ EFRM_ERR("assertion \"%s\" failed at %s %d", \ ++ #cond, file, line); \ ++ BUG(); \ ++ } \ ++ } while (0) ++ ++#define EFRM_DO_DEBUG(expr) expr ++#define EFRM_VERIFY_EQ(expr, val) EFRM_ASSERT((expr) == (val)) ++#else ++#define EFRM_ASSERT(cond) ++#define EFRM_DO_DEBUG(expr) ++#define EFRM_VERIFY_EQ(expr, val) expr ++#endif ++ ++#endif /* __CI_EFRM_DEBUG_LINUX_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/driver_private.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,89 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides private API of efrm library to be used from the SFC ++ * resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_DRIVER_PRIVATE_H__ ++#define __CI_EFRM_DRIVER_PRIVATE_H__ ++ ++#include ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * global variables ++ * ++ *--------------------------------------------------------------------*/ ++ ++/* Internal structure for resource driver */ ++extern struct efrm_resource_manager *efrm_rm_table[]; ++ ++/*-------------------------------------------------------------------- ++ * ++ * efrm_nic_table handling ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efrm_nic; ++ ++extern void efrm_driver_ctor(void); ++extern void efrm_driver_dtor(void); ++extern int efrm_driver_register_nic(struct efrm_nic *, int nic_index, ++ int ifindex); ++extern int efrm_driver_unregister_nic(struct efrm_nic *); ++ ++/*-------------------------------------------------------------------- ++ * ++ * create/destroy resource managers ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct vi_resource_dimensions { ++ unsigned evq_int_min, evq_int_lim; ++ unsigned evq_timer_min, evq_timer_lim; ++ unsigned rxq_min, rxq_lim; ++ unsigned txq_min, txq_lim; ++}; ++ ++/*! Initialise resources */ ++extern int ++efrm_resources_init(const struct vi_resource_dimensions *, ++ int buffer_table_min, int buffer_table_lim); ++ ++/*! Tear down resources */ ++extern void efrm_resources_fini(void); ++ ++#endif /* __CI_EFRM_DRIVER_PRIVATE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/efrm_client.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,32 @@ ++#ifndef __EFRM_CLIENT_H__ ++#define __EFRM_CLIENT_H__ ++ ++ ++struct efrm_client; ++ ++ ++struct efrm_client_callbacks { ++ /* Called before device is reset. Callee may block. */ ++ void (*pre_reset)(struct efrm_client *, void *user_data); ++ void (*stop)(struct efrm_client *, void *user_data); ++ void (*restart)(struct efrm_client *, void *user_data); ++}; ++ ++ ++#define EFRM_IFINDEX_DEFAULT -1 ++ ++ ++/* NB. Callbacks may be invoked even before this returns. */ ++extern int efrm_client_get(int ifindex, struct efrm_client_callbacks *, ++ void *user_data, struct efrm_client **client_out); ++extern void efrm_client_put(struct efrm_client *); ++ ++extern struct efhw_nic *efrm_client_get_nic(struct efrm_client *); ++ ++#if 0 ++/* For each resource type... */ ++extern void efrm_x_resource_resume(struct x_resource *); ++#endif ++ ++ ++#endif /* __EFRM_CLIENT_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/efrm_nic.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,26 @@ ++#ifndef __EFRM_NIC_H__ ++#define __EFRM_NIC_H__ ++ ++#include ++ ++ ++struct efrm_nic_per_vi { ++ unsigned long state; ++ struct vi_resource *vi; ++}; ++ ++ ++struct efrm_nic { ++ struct efhw_nic efhw_nic; ++ struct list_head link; ++ struct list_head clients; ++ struct efrm_nic_per_vi *vis; ++}; ++ ++ ++#define efrm_nic(_efhw_nic) \ ++ container_of(_efhw_nic, struct efrm_nic, efhw_nic) ++ ++ ++ ++#endif /* __EFRM_NIC_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/filter.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,122 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides public API for filter resource. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_FILTER_H__ ++#define __CI_EFRM_FILTER_H__ ++ ++#include ++#include ++ ++ ++struct filter_resource; ++struct vi_resource; ++struct efrm_client; ++ ++ ++/*! ++ * Allocate filter resource. ++ * ++ * \param vi_parent VI resource to use as parent. The function takes ++ * reference to the VI resource on success. ++ * \param frs_out pointer to return the new filter resource ++ * ++ * \return status code; if non-zero, frs_out is unchanged ++ */ ++extern int ++efrm_filter_resource_alloc(struct vi_resource *vi_parent, ++ struct filter_resource **frs_out); ++ ++extern void ++efrm_filter_resource_release(struct filter_resource *); ++ ++ ++extern int efrm_filter_resource_clear(struct filter_resource *frs); ++ ++extern int __efrm_filter_resource_set(struct filter_resource *frs, int type, ++ unsigned saddr_be32, uint16_t sport_be16, ++ unsigned daddr_be32, uint16_t dport_be16); ++ ++static inline int ++efrm_filter_resource_tcp_set(struct filter_resource *frs, ++ unsigned saddr, uint16_t sport, ++ unsigned daddr, uint16_t dport) ++{ ++ int type; ++ ++ EFRM_ASSERT((saddr && sport) || (!saddr && !sport)); ++ ++ type = ++ saddr ? EFHW_IP_FILTER_TYPE_TCP_FULL : ++ EFHW_IP_FILTER_TYPE_TCP_WILDCARD; ++ ++ return __efrm_filter_resource_set(frs, type, ++ saddr, sport, daddr, dport); ++} ++ ++static inline int ++efrm_filter_resource_udp_set(struct filter_resource *frs, ++ unsigned saddr, uint16_t sport, ++ unsigned daddr, uint16_t dport) ++{ ++ int type; ++ ++ EFRM_ASSERT((saddr && sport) || (!saddr && !sport)); ++ ++ type = ++ saddr ? EFHW_IP_FILTER_TYPE_UDP_FULL : ++ EFHW_IP_FILTER_TYPE_UDP_WILDCARD; ++ ++ return __efrm_filter_resource_set(frs, ++ type, saddr, sport, daddr, dport); ++} ++ ++ ++extern int ++efrm_filter_resource_instance(struct filter_resource *); ++ ++extern struct efrm_resource * ++efrm_filter_resource_to_resource(struct filter_resource *); ++ ++extern struct filter_resource * ++efrm_filter_resource_from_resource(struct efrm_resource *); ++ ++extern void ++efrm_filter_resource_free(struct filter_resource *); ++ ++ ++#endif /* __CI_EFRM_FILTER_H__ */ ++/*! \cidoxg_end */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/iobufset.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,110 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides public API for iobufset resource. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_IOBUFSET_H__ ++#define __CI_EFRM_IOBUFSET_H__ ++ ++#include ++ ++/*! Iobufset resource structture. ++ * Users should not access the structure fields directly, but use the API ++ * below. ++ * However, this structure should not be moved out of public headers, ++ * because part of API (ex. efrm_iobufset_dma_addr function) is inline and ++ * is used in the fast-path code. ++ */ ++struct iobufset_resource { ++ struct efrm_resource rs; ++ struct vi_resource *evq; ++ struct iobufset_resource *linked; ++ struct efhw_buffer_table_allocation buf_tbl_alloc; ++ unsigned int n_bufs; ++ unsigned int pages_per_contiguous_chunk; ++ unsigned chunk_order; ++ struct efhw_iopage bufs[1]; ++ /*!< up to n_bufs can follow this, so this must be the last member */ ++}; ++ ++#define iobufset_resource(rs1) \ ++ container_of((rs1), struct iobufset_resource, rs) ++ ++/*! ++ * Allocate iobufset resource. ++ * ++ * \param vi VI that "owns" these buffers. Grabs a reference ++ * on success. ++ * \param linked Uses memory from an existing iobufset. Grabs a ++ * reference on success. ++ * \param iobrs_out pointer to return the new filter resource ++ * ++ * \return status code; if non-zero, frs_out is unchanged ++ */ ++extern int ++efrm_iobufset_resource_alloc(int32_t n_pages, ++ int32_t pages_per_contiguous_chunk, ++ struct vi_resource *vi, ++ struct iobufset_resource *linked, ++ bool phys_addr_mode, ++ struct iobufset_resource **iobrs_out); ++ ++extern void efrm_iobufset_resource_free(struct iobufset_resource *); ++extern void efrm_iobufset_resource_release(struct iobufset_resource *); ++ ++static inline char * ++efrm_iobufset_ptr(struct iobufset_resource *rs, unsigned offs) ++{ ++ EFRM_ASSERT(offs < (unsigned)(rs->n_bufs << PAGE_SHIFT)); ++ return efhw_iopage_ptr(&rs->bufs[offs >> PAGE_SHIFT]) ++ + (offs & (PAGE_SIZE - 1)); ++} ++ ++static inline char *efrm_iobufset_page_ptr(struct iobufset_resource *rs, ++ unsigned page_i) ++{ ++ EFRM_ASSERT(page_i < (unsigned)rs->n_bufs); ++ return efhw_iopage_ptr(&rs->bufs[page_i]); ++} ++ ++static inline dma_addr_t ++efrm_iobufset_dma_addr(struct iobufset_resource *rs, unsigned offs) ++{ ++ EFRM_ASSERT(offs < (unsigned)(rs->n_bufs << PAGE_SHIFT)); ++ return efhw_iopage_dma_addr(&rs->bufs[offs >> PAGE_SHIFT]) ++ + (offs & (PAGE_SIZE - 1)); ++} ++ ++#endif /* __CI_EFRM_IOBUFSET_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/nic_set.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,104 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides public API for NIC sets. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_NIC_SET_H__ ++#define __CI_EFRM_NIC_SET_H__ ++ ++#include ++#include ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * efrm_nic_set_t - tracks which NICs something has been done on ++ * ++ *--------------------------------------------------------------------*/ ++ ++/* Internal suructure of efrm_nic_set_t should not be referenced outside of ++ * this file. Add a new accessor if you should do it. */ ++typedef struct { ++ uint32_t nics; ++} efrm_nic_set_t; ++ ++#if EFHW_MAX_NR_DEVS > 32 ++#error change efrm_nic_set to handle EFHW_MAX_NR_DEVS number of devices ++#endif ++ ++static inline bool ++efrm_nic_set_read(const efrm_nic_set_t *nic_set, unsigned index) ++{ ++ EFRM_ASSERT(nic_set); ++ EFRM_ASSERT(index < EFHW_MAX_NR_DEVS && index < 32); ++ return (nic_set->nics & (1 << index)) ? true : false; ++} ++ ++static inline void ++efrm_nic_set_write(efrm_nic_set_t *nic_set, unsigned index, bool value) ++{ ++ EFRM_ASSERT(nic_set); ++ EFRM_ASSERT(index < EFHW_MAX_NR_DEVS && index < 32); ++ EFRM_ASSERT(value == false || value == true); ++ nic_set->nics = (nic_set->nics & (~(1 << index))) + (value << index); ++} ++ ++static inline void efrm_nic_set_clear(efrm_nic_set_t *nic_set) ++{ ++ nic_set->nics = 0; ++} ++ ++static inline void efrm_nic_set_all(efrm_nic_set_t *nic_set) ++{ ++ nic_set->nics = 0xffffffff; ++} ++ ++static inline bool efrm_nic_set_is_all_clear(efrm_nic_set_t *nic_set) ++{ ++ return nic_set->nics == 0 ? true : false; ++} ++ ++#define EFRM_NIC_SET_FMT "%x" ++ ++static inline uint32_t efrm_nic_set_pri_arg(efrm_nic_set_t *nic_set) ++{ ++ return nic_set->nics; ++} ++ ++#define EFRM_FOR_EACH_NIC_INDEX_IN_SET(_set, _nic_i) \ ++ for ((_nic_i) = 0; (_nic_i) < EFHW_MAX_NR_DEVS; ++(_nic_i)) \ ++ if (efrm_nic_set_read((_set), (_nic_i))) ++ ++#endif /* __CI_EFRM_NIC_SET_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/nic_table.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,98 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides public API for NIC table. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_NIC_TABLE_H__ ++#define __CI_EFRM_NIC_TABLE_H__ ++ ++#include ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * struct efrm_nic_table - top level driver object keeping all NICs - ++ * implemented in driver_object.c ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! Comment? */ ++struct efrm_nic_table { ++ /*! nics attached to this driver */ ++ struct efhw_nic *nic[EFHW_MAX_NR_DEVS]; ++ /*! pointer to an arbitrary struct efhw_nic if one exists; ++ * for code which does not care which NIC it wants but ++ * still needs one. Note you cannot assume nic[0] exists. */ ++ struct efhw_nic *a_nic; ++ uint32_t nic_count; /*!< number of nics attached to this driver */ ++ spinlock_t lock; /*!< lock for table modifications */ ++ atomic_t ref_count; /*!< refcount for users of nic table */ ++}; ++ ++/* Resource driver structures used by other drivers as well */ ++extern struct efrm_nic_table *efrm_nic_tablep; ++ ++static inline void efrm_nic_table_hold(void) ++{ ++ atomic_inc(&efrm_nic_tablep->ref_count); ++} ++ ++static inline void efrm_nic_table_rele(void) ++{ ++ atomic_dec(&efrm_nic_tablep->ref_count); ++} ++ ++static inline int efrm_nic_table_held(void) ++{ ++ return atomic_read(&efrm_nic_tablep->ref_count) != 0; ++} ++ ++/* Run code block _x multiple times with variable nic set to each ++ * registered NIC in turn. ++ * DO NOT "break" out of this loop early. */ ++#define EFRM_FOR_EACH_NIC(_nic_i, _nic) \ ++ for ((_nic_i) = (efrm_nic_table_hold(), 0); \ ++ (_nic_i) < EFHW_MAX_NR_DEVS || (efrm_nic_table_rele(), 0); \ ++ (_nic_i)++) \ ++ if (((_nic) = efrm_nic_tablep->nic[_nic_i])) ++ ++#define EFRM_FOR_EACH_NIC_IN_SET(_set, _i, _nic) \ ++ for ((_i) = (efrm_nic_table_hold(), 0); \ ++ (_i) < EFHW_MAX_NR_DEVS || (efrm_nic_table_rele(), 0); \ ++ ++(_i)) \ ++ if (((_nic) = efrm_nic_tablep->nic[_i]) && \ ++ efrm_nic_set_read((_set), (_i))) ++ ++#endif /* __CI_EFRM_NIC_TABLE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/private.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,118 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides private API of efrm library -- resource handling. ++ * This API is not designed for use outside of SFC resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_PRIVATE_H__ ++#define __CI_EFRM_PRIVATE_H__ ++ ++#include ++#include ++#include ++#include ++ ++/*-------------------------------------------------------------------- ++ * ++ * create resource managers ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! Create a resource manager for various types of resources ++ */ ++extern int ++efrm_create_iobufset_resource_manager(struct efrm_resource_manager **out); ++ ++extern int ++efrm_create_filter_resource_manager(struct efrm_resource_manager **out); ++ ++extern int ++efrm_create_vi_resource_manager(struct efrm_resource_manager **out, ++ const struct vi_resource_dimensions *); ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * Instance pool management ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! Allocate instance pool. Use kfifo_vfree to destroy it. */ ++static inline int ++efrm_kfifo_id_ctor(struct kfifo **ids_out, ++ unsigned int base, unsigned int limit, spinlock_t *lock) ++{ ++ unsigned int i; ++ struct kfifo *ids; ++ unsigned char *buffer; ++ unsigned int size = roundup_pow_of_two((limit - base) * sizeof(int)); ++ EFRM_ASSERT(base <= limit); ++ buffer = vmalloc(size); ++ ids = kfifo_init(buffer, size, GFP_KERNEL, lock); ++ if (IS_ERR(ids)) ++ return PTR_ERR(ids); ++ for (i = base; i < limit; i++) ++ EFRM_VERIFY_EQ(__kfifo_put(ids, (unsigned char *)&i, ++ sizeof(i)), sizeof(i)); ++ ++ *ids_out = ids; ++ return 0; ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * Various private functions ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! Initialize the fields in the provided resource manager memory area ++ * \param rm The area of memory to be initialized ++ * \param dtor A method to destroy the resource manager ++ * \param name A Textual name for the resource manager ++ * \param type The type of resource managed ++ * \param initial_table_size Initial size of the ID table ++ * \param auto_destroy Destroy resource manager on driver onload iff true ++ * ++ * A default table size is provided if the value 0 is provided. ++ */ ++extern int ++efrm_resource_manager_ctor(struct efrm_resource_manager *rm, ++ void (*dtor)(struct efrm_resource_manager *), ++ const char *name, unsigned type); ++ ++extern void efrm_resource_manager_dtor(struct efrm_resource_manager *rm); ++ ++ ++#endif /* __CI_EFRM_PRIVATE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/resource.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,119 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides public interface of efrm library -- resource handling. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_RESOURCE_H__ ++#define __CI_EFRM_RESOURCE_H__ ++ ++/*-------------------------------------------------------------------- ++ * ++ * headers for type dependencies ++ * ++ *--------------------------------------------------------------------*/ ++ ++#include ++#include ++#include ++#include ++ ++#ifndef __ci_driver__ ++#error "Driver-only file" ++#endif ++ ++/*-------------------------------------------------------------------- ++ * ++ * struct efrm_resource - represents an allocated resource ++ * (eg. pinned pages of memory, or resource on a NIC) ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! Representation of an allocated resource */ ++struct efrm_resource { ++ int rs_ref_count; ++ efrm_resource_handle_t rs_handle; ++ struct efrm_client *rs_client; ++ struct list_head rs_client_link; ++ struct list_head rs_manager_link; ++}; ++ ++/*-------------------------------------------------------------------- ++ * ++ * managed resource abstraction ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! Factory for resources of a specific type */ ++struct efrm_resource_manager { ++ const char *rm_name; /*!< human readable only */ ++ spinlock_t rm_lock; ++#ifndef NDEBUG ++ unsigned rm_type; ++#endif ++ int rm_resources; ++ int rm_resources_hiwat; ++ struct list_head rm_resources_list; ++ /** ++ * Destructor for the resource manager. Other resource managers ++ * might be already dead, although the system guarantees that ++ * managers are destructed in the order by which they were created ++ */ ++ void (*rm_dtor)(struct efrm_resource_manager *); ++}; ++ ++#ifdef NDEBUG ++# define EFRM_RESOURCE_ASSERT_VALID(rs, rc_mbz) ++# define EFRM_RESOURCE_MANAGER_ASSERT_VALID(rm) ++#else ++/*! Check validity of resource and report on failure */ ++extern void efrm_resource_assert_valid(struct efrm_resource *, ++ int rc_may_be_zero, ++ const char *file, int line); ++# define EFRM_RESOURCE_ASSERT_VALID(rs, rc_mbz) \ ++ efrm_resource_assert_valid((rs), (rc_mbz), __FILE__, __LINE__) ++ ++/*! Check validity of resource manager and report on failure */ ++extern void efrm_resource_manager_assert_valid(struct efrm_resource_manager *, ++ const char *file, int line); ++# define EFRM_RESOURCE_MANAGER_ASSERT_VALID(rm) \ ++ efrm_resource_manager_assert_valid((rm), __FILE__, __LINE__) ++#endif ++ ++ ++extern void efrm_resource_ref(struct efrm_resource *rs); ++extern int __efrm_resource_release(struct efrm_resource *); ++ ++ ++#endif /* __CI_EFRM_RESOURCE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/resource_id.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,104 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides public type and definitions resource handle, and the ++ * definitions of resource types. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_EFRM_RESOURCE_ID_H__ ++#define __CI_DRIVER_EFRM_RESOURCE_ID_H__ ++ ++/*********************************************************************** ++ * Resource handles ++ * ++ * Resource handles are intended for identifying resources at kernel ++ * level, within the context of a particular NIC. particularly because ++ * for some resource types, the low 16 bites correspond to hardware ++ * IDs. They were historically also used at user level, with a nonce ++ * stored in the bits 16 to 27 (inclusive), but that approach is ++ * deprecated (but sill alive!). ++ * ++ * The handle value 0 is used to mean "no resource". ++ * Identify resources within the context of a file descriptor at user ++ * level. ++ ***********************************************************************/ ++ ++typedef struct { ++ uint32_t handle; ++} efrm_resource_handle_t; ++ ++/* You may think these following functions should all have ++ * _HANDLE_ in their names, but really we are providing an abstract set ++ * of methods on a (hypothetical) efrm_resource_t object, with ++ * efrm_resource_handle_t being just the reference one holds to access ++ * the object (aka "this" or "self"). ++ */ ++ ++/* Below I use inline instead of macros where possible in order to get ++ * more type checking help from the compiler; hopefully we'll never ++ * have to rewrite these to use #define as we've found some horrible ++ * compiler on which we cannot make static inline do the Right Thing (tm). ++ * ++ * For consistency and to avoid pointless change I spell these ++ * routines as macro names (CAPTILIZE_UNDERSCORED), which also serves ++ * to remind people they are compact and inlined. ++ */ ++ ++#define EFRM_RESOURCE_FMT "[rs:%08x]" ++ ++static inline unsigned EFRM_RESOURCE_PRI_ARG(efrm_resource_handle_t h) ++{ ++ return h.handle; ++} ++ ++static inline unsigned EFRM_RESOURCE_INSTANCE(efrm_resource_handle_t h) ++{ ++ return h.handle & 0x0000ffff; ++} ++ ++static inline unsigned EFRM_RESOURCE_TYPE(efrm_resource_handle_t h) ++{ ++ return (h.handle & 0xf0000000) >> 28; ++} ++ ++/*********************************************************************** ++ * Resource type codes ++ ***********************************************************************/ ++ ++#define EFRM_RESOURCE_IOBUFSET 0x0 ++#define EFRM_RESOURCE_VI 0x1 ++#define EFRM_RESOURCE_FILTER 0x2 ++#define EFRM_RESOURCE_NUM 0x3 /* This isn't a resource! */ ++ ++#define EFRM_RESOURCE_NAME(type) \ ++ ((type) == EFRM_RESOURCE_IOBUFSET? "IOBUFSET" : \ ++ (type) == EFRM_RESOURCE_VI? "VI" : \ ++ (type) == EFRM_RESOURCE_FILTER? "FILTER" : \ ++ "") ++ ++#endif /* __CI_DRIVER_EFRM_RESOURCE_ID_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/sysdep.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,46 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides Linux-like system-independent API for efrm library. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_SYSDEP_H__ ++#define __CI_EFRM_SYSDEP_H__ ++ ++/* Spinlocks are defined in efhw/sysdep.h */ ++#include ++ ++#include ++ ++#endif /* __CI_EFRM_SYSDEP_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/sysdep_linux.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,93 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides version-independent Linux kernel API for efrm library. ++ * Only kernels >=2.6.9 are supported. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Kfifo API is partially stolen from linux-2.6.22/include/linux/list.h ++ * Copyright (C) 2004 Stelian Pop ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_SYSDEP_LINUX_H__ ++#define __CI_EFRM_SYSDEP_LINUX_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/******************************************************************** ++ * ++ * List API ++ * ++ ********************************************************************/ ++ ++static inline struct list_head *list_pop(struct list_head *list) ++{ ++ struct list_head *link = list->next; ++ list_del(link); ++ return link; ++} ++ ++static inline struct list_head *list_pop_tail(struct list_head *list) ++{ ++ struct list_head *link = list->prev; ++ list_del(link); ++ return link; ++} ++ ++/******************************************************************** ++ * ++ * Kfifo API ++ * ++ ********************************************************************/ ++ ++static inline void kfifo_vfree(struct kfifo *fifo) ++{ ++ vfree(fifo->buffer); ++ kfree(fifo); ++} ++ ++#endif /* __CI_EFRM_SYSDEP_LINUX_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/vi_resource.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,157 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains public API for VI resource. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_VI_RESOURCE_H__ ++#define __CI_EFRM_VI_RESOURCE_H__ ++ ++#include ++#include ++#include ++ ++struct vi_resource; ++ ++/* Make these inline instead of macros for type checking */ ++static inline struct vi_resource * ++efrm_to_vi_resource(struct efrm_resource *rs) ++{ ++ EFRM_ASSERT(EFRM_RESOURCE_TYPE(rs->rs_handle) == EFRM_RESOURCE_VI); ++ return (struct vi_resource *) rs; ++} ++static inline struct ++efrm_resource *efrm_from_vi_resource(struct vi_resource *rs) ++{ ++ return (struct efrm_resource *)rs; ++} ++ ++#define EFAB_VI_RESOURCE_INSTANCE(virs) \ ++ EFRM_RESOURCE_INSTANCE(efrm_from_vi_resource(virs)->rs_handle) ++ ++#define EFAB_VI_RESOURCE_PRI_ARG(virs) \ ++ EFRM_RESOURCE_PRI_ARG(efrm_from_vi_resource(virs)->rs_handle) ++ ++extern int ++efrm_vi_resource_alloc(struct efrm_client *client, ++ struct vi_resource *evq_virs, ++ uint16_t vi_flags, int32_t evq_capacity, ++ int32_t txq_capacity, int32_t rxq_capacity, ++ uint8_t tx_q_tag, uint8_t rx_q_tag, ++ struct vi_resource **virs_in_out, ++ uint32_t *out_io_mmap_bytes, ++ uint32_t *out_mem_mmap_bytes, ++ uint32_t *out_txq_capacity, ++ uint32_t *out_rxq_capacity); ++ ++extern void efrm_vi_resource_free(struct vi_resource *); ++extern void efrm_vi_resource_release(struct vi_resource *); ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * eventq handling ++ * ++ *--------------------------------------------------------------------*/ ++ ++/*! Reset an event queue and clear any associated timers */ ++extern void efrm_eventq_reset(struct vi_resource *virs); ++ ++/*! Register a kernel-level handler for the event queue. This function is ++ * called whenever a timer expires, or whenever the event queue is woken ++ * but no thread is blocked on it. ++ * ++ * This function returns -EBUSY if a callback is already installed. ++ * ++ * \param rs Event-queue resource ++ * \param handler Callback-handler ++ * \param arg Argument to pass to callback-handler ++ * \return Status code ++ */ ++extern int ++efrm_eventq_register_callback(struct vi_resource *rs, ++ void (*handler)(void *arg, int is_timeout, ++ struct efhw_nic *nic), ++ void *arg); ++ ++/*! Kill the kernel-level callback. ++ * ++ * This function stops the timer from running and unregisters the callback ++ * function. It waits for any running timeout handlers to complete before ++ * returning. ++ * ++ * \param rs Event-queue resource ++ * \return Nothing ++ */ ++extern void efrm_eventq_kill_callback(struct vi_resource *rs); ++ ++/*! Ask the NIC to generate a wakeup when an event is next delivered. */ ++extern void efrm_eventq_request_wakeup(struct vi_resource *rs, ++ unsigned current_ptr); ++ ++/*! Register a kernel-level handler for flush completions. ++ * \TODO Currently, it is unsafe to install a callback more than once. ++ * ++ * \param rs VI resource being flushed. ++ * \param handler Callback handler function. ++ * \param arg Argument to be passed to handler. ++ */ ++extern void ++efrm_vi_register_flush_callback(struct vi_resource *rs, ++ void (*handler)(void *), ++ void *arg); ++ ++int efrm_vi_resource_flush_retry(struct vi_resource *virs); ++ ++/*! Comment? */ ++extern int efrm_pt_flush(struct vi_resource *); ++ ++/*! Comment? */ ++extern int efrm_pt_pace(struct vi_resource *, unsigned int val); ++ ++uint32_t efrm_vi_rm_txq_bytes(struct vi_resource *virs ++ /*,struct efhw_nic *nic */); ++uint32_t efrm_vi_rm_rxq_bytes(struct vi_resource *virs ++ /*,struct efhw_nic *nic */); ++uint32_t efrm_vi_rm_evq_bytes(struct vi_resource *virs ++ /*,struct efhw_nic *nic */); ++ ++ ++/* Fill [out_vi_data] with information required to allow a VI to be init'd. ++ * [out_vi_data] must ref at least VI_MAPPINGS_SIZE bytes. ++ */ ++extern void efrm_vi_resource_mappings(struct vi_resource *, void *out_vi_data); ++ ++ ++#endif /* __CI_EFRM_VI_RESOURCE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/vi_resource_manager.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,155 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains type definitions for VI resource. These types ++ * may be used outside of the SFC resource driver, but such use is not ++ * recommended. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_DRIVER_EFAB_VI_RESOURCE_MANAGER_H__ ++#define __CI_DRIVER_EFAB_VI_RESOURCE_MANAGER_H__ ++ ++#include ++#include ++ ++ ++#define EFRM_VI_RM_DMA_QUEUE_COUNT 2 ++#define EFRM_VI_RM_DMA_QUEUE_TX 0 ++#define EFRM_VI_RM_DMA_QUEUE_RX 1 ++ ++/** Numbers of bits which can be set in the evq_state member of ++ * vi_resource_evq_info. */ ++enum { ++ /** This bit is set if a wakeup has been requested on the NIC. */ ++ VI_RESOURCE_EVQ_STATE_WAKEUP_PENDING, ++ /** This bit is set if the wakeup is valid for the sleeping ++ * process. */ ++ VI_RESOURCE_EVQ_STATE_CALLBACK_REGISTERED, ++ /** This bit is set if a wakeup or timeout event is currently being ++ * processed. */ ++ VI_RESOURCE_EVQ_STATE_BUSY, ++}; ++#define VI_RESOURCE_EVQ_STATE(X) \ ++ (((int32_t)1) << (VI_RESOURCE_EVQ_STATE_##X)) ++ ++ ++/*! Global information for the VI resource manager. */ ++struct vi_resource_manager { ++ struct efrm_resource_manager rm; ++ ++ struct kfifo *instances_with_timer; ++ int with_timer_base; ++ int with_timer_limit; ++ struct kfifo *instances_with_interrupt; ++ int with_interrupt_base; ++ int with_interrupt_limit; ++ ++ bool iscsi_dmaq_instance_is_free; ++ ++ /* We keep VI resources which need flushing on these lists. The VI ++ * is put on the outstanding list when the flush request is issued ++ * to the hardware and removed when the flush event arrives. The ++ * hardware can only handle a limited number of RX flush requests at ++ * once, so VIs are placed in the waiting list until the flush can ++ * be issued. Flushes can be requested by the client or internally ++ * by the VI resource manager. In the former case, the reference ++ * count must be non-zero for the duration of the flush and in the ++ * later case, the reference count must be zero. */ ++ struct list_head rx_flush_waiting_list; ++ struct list_head rx_flush_outstanding_list; ++ struct list_head tx_flush_outstanding_list; ++ int rx_flush_outstanding_count; ++ ++ /* once the flush has happened we push the close into the work queue ++ * so its OK on Windows to free the resources (Bug 3469). Resources ++ * on this list have zero reference count. ++ */ ++ struct list_head close_pending; ++ struct work_struct work_item; ++ struct workqueue_struct *workqueue; ++}; ++ ++struct vi_resource_nic_info { ++ struct eventq_resource_hardware evq_pages; ++ struct efhw_iopages dmaq_pages[EFRM_VI_RM_DMA_QUEUE_COUNT]; ++}; ++ ++struct vi_resource { ++ /* Some macros make the assumption that the struct efrm_resource is ++ * the first member of a struct vi_resource. */ ++ struct efrm_resource rs; ++ atomic_t evq_refs; /*!< Number of users of the event queue. */ ++ ++ uint32_t bar_mmap_bytes; ++ uint32_t mem_mmap_bytes; ++ ++ int32_t evq_capacity; ++ int32_t dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_COUNT]; ++ ++ uint8_t dmaq_tag[EFRM_VI_RM_DMA_QUEUE_COUNT]; ++ uint16_t flags; ++ ++ /* we keep PT endpoints that have been destroyed on a list ++ * until we have seen their TX and RX DMAQs flush complete ++ * (see Bug 1217) ++ */ ++ struct list_head rx_flush_link; ++ struct list_head tx_flush_link; ++ int rx_flushing; ++ int rx_flush_outstanding; ++ int tx_flushing; ++ uint64_t flush_time; ++ int flush_count; ++ ++ void (*flush_callback_fn)(void *); ++ void *flush_callback_arg; ++ ++ void (*evq_callback_fn) (void *arg, int is_timeout, ++ struct efhw_nic *nic); ++ void *evq_callback_arg; ++ ++ struct vi_resource *evq_virs; /*!< EVQ for DMA queues */ ++ ++ struct efhw_buffer_table_allocation ++ dmaq_buf_tbl_alloc[EFRM_VI_RM_DMA_QUEUE_COUNT]; ++ ++ struct vi_resource_nic_info nic_info; ++}; ++ ++#undef vi_resource ++#define vi_resource(rs1) container_of((rs1), struct vi_resource, rs) ++ ++static inline dma_addr_t ++efrm_eventq_dma_addr(struct vi_resource *virs) ++{ ++ struct eventq_resource_hardware *hw; ++ hw = &virs->nic_info.evq_pages; ++ return efhw_iopages_dma_addr(&hw->iobuff) + hw->iobuff_off; ++} ++ ++#endif /* __CI_DRIVER_EFAB_VI_RESOURCE_MANAGER_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/ci/efrm/vi_resource_private.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,65 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains private API for VI resource. The API is not designed ++ * to be used outside of the SFC resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __CI_EFRM_VI_RESOURCE_PRIVATE_H__ ++#define __CI_EFRM_VI_RESOURCE_PRIVATE_H__ ++ ++#include ++#include ++ ++extern struct vi_resource_manager *efrm_vi_manager; ++ ++/*************************************************************************/ ++ ++extern void efrm_vi_rm_delayed_free(struct work_struct *data); ++ ++extern void efrm_vi_rm_salvage_flushed_vis(void); ++ ++void efrm_vi_rm_free_flushed_resource(struct vi_resource *virs); ++ ++void efrm_vi_rm_init_dmaq(struct vi_resource *virs, int queue_index, ++ struct efhw_nic *nic); ++ ++/*! Wakeup handler */ ++extern void efrm_handle_wakeup_event(struct efhw_nic *nic, unsigned id); ++ ++/*! Timeout handler */ ++extern void efrm_handle_timeout_event(struct efhw_nic *nic, unsigned id); ++ ++/*! DMA flush handler */ ++extern void efrm_handle_dmaq_flushed(struct efhw_nic *nic, unsigned id, ++ int rx_flush); ++ ++/*! SRAM update handler */ ++extern void efrm_handle_sram_event(struct efhw_nic *nic); ++ ++#endif /* __CI_EFRM_VI_RESOURCE_PRIVATE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/driver_object.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,328 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains support for the global driver variables. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "efrm_internal.h" ++ ++/* We use #define rather than static inline here so that the Windows ++ * "prefast" compiler can see its own locking primitive when these ++ * two function are used (and then perform extra checking where they ++ * are used) ++ * ++ * Both macros operate on an irq_flags_t ++*/ ++ ++#define efrm_driver_lock(irqlock_state) \ ++ spin_lock_irqsave(&efrm_nic_tablep->lock, irqlock_state) ++ ++#define efrm_driver_unlock(irqlock_state) \ ++ spin_unlock_irqrestore(&efrm_nic_tablep->lock, \ ++ irqlock_state); ++ ++/* These routines are all methods on the architecturally singleton ++ global variables: efrm_nic_table, efrm_rm_table. ++ ++ I hope we never find a driver model that does not allow global ++ structure variables :) (but that would break almost every driver I've ++ ever seen). ++*/ ++ ++/*! Exported driver state */ ++static struct efrm_nic_table efrm_nic_table; ++struct efrm_nic_table *efrm_nic_tablep; ++EXPORT_SYMBOL(efrm_nic_tablep); ++ ++ ++/* Internal table with resource managers. ++ * We'd like to not export it, but we are still using efrm_rm_table ++ * in the char driver. So, it is declared in the private header with ++ * a purpose. */ ++struct efrm_resource_manager *efrm_rm_table[EFRM_RESOURCE_NUM]; ++EXPORT_SYMBOL(efrm_rm_table); ++ ++ ++/* List of registered nics. */ ++static LIST_HEAD(efrm_nics); ++ ++ ++void efrm_driver_ctor(void) ++{ ++ efrm_nic_tablep = &efrm_nic_table; ++ spin_lock_init(&efrm_nic_tablep->lock); ++ EFRM_TRACE("%s: driver created", __func__); ++} ++ ++void efrm_driver_dtor(void) ++{ ++ EFRM_ASSERT(!efrm_nic_table_held()); ++ ++ spin_lock_destroy(&efrm_nic_tablep->lock); ++ memset(&efrm_nic_table, 0, sizeof(efrm_nic_table)); ++ memset(&efrm_rm_table, 0, sizeof(efrm_rm_table)); ++ EFRM_TRACE("%s: driver deleted", __func__); ++} ++ ++int efrm_driver_register_nic(struct efrm_nic *rnic, int nic_index, ++ int ifindex) ++{ ++ struct efhw_nic *nic = &rnic->efhw_nic; ++ struct efrm_nic_per_vi *vis; ++ int max_vis, rc = 0; ++ irq_flags_t lock_flags; ++ ++ EFRM_ASSERT(nic_index >= 0); ++ EFRM_ASSERT(ifindex >= 0); ++ ++ max_vis = 4096; /* TODO: Get runtime value. */ ++ vis = vmalloc(max_vis * sizeof(rnic->vis[0])); ++ if (vis == NULL) { ++ EFRM_ERR("%s: Out of memory", __func__); ++ return -ENOMEM; ++ } ++ ++ efrm_driver_lock(lock_flags); ++ ++ if (efrm_nic_table_held()) { ++ EFRM_ERR("%s: driver object is in use", __func__); ++ rc = -EBUSY; ++ goto done; ++ } ++ ++ if (efrm_nic_tablep->nic_count == EFHW_MAX_NR_DEVS) { ++ EFRM_ERR("%s: filled up NIC table size %d", __func__, ++ EFHW_MAX_NR_DEVS); ++ rc = -E2BIG; ++ goto done; ++ } ++ ++ rnic->vis = vis; ++ ++ EFRM_ASSERT(efrm_nic_tablep->nic[nic_index] == NULL); ++ efrm_nic_tablep->nic[nic_index] = nic; ++ nic->index = nic_index; ++ nic->ifindex = ifindex; ++ ++ if (efrm_nic_tablep->a_nic == NULL) ++ efrm_nic_tablep->a_nic = nic; ++ ++ efrm_nic_tablep->nic_count++; ++ ++ INIT_LIST_HEAD(&rnic->clients); ++ list_add(&rnic->link, &efrm_nics); ++ ++ efrm_driver_unlock(lock_flags); ++ return 0; ++ ++done: ++ efrm_driver_unlock(lock_flags); ++ vfree(vis); ++ return rc; ++} ++ ++int efrm_driver_unregister_nic(struct efrm_nic *rnic) ++{ ++ struct efhw_nic *nic = &rnic->efhw_nic; ++ int rc = 0; ++ int nic_index = nic->index; ++ irq_flags_t lock_flags; ++ ++ EFRM_ASSERT(nic_index >= 0); ++ ++ efrm_driver_lock(lock_flags); ++ ++ if (efrm_nic_table_held()) { ++ EFRM_ERR("%s: driver object is in use", __func__); ++ rc = -EBUSY; ++ goto done; ++ } ++ if (!list_empty(&rnic->clients)) { ++ EFRM_ERR("%s: nic has active clients", __func__); ++ rc = -EBUSY; ++ goto done; ++ } ++ ++ EFRM_ASSERT(efrm_nic_tablep->nic[nic_index] == nic); ++ EFRM_ASSERT(list_empty(&rnic->clients)); ++ ++ list_del(&rnic->link); ++ ++ nic->index = -1; ++ efrm_nic_tablep->nic[nic_index] = NULL; ++ ++ --efrm_nic_tablep->nic_count; ++ ++ if (efrm_nic_tablep->a_nic == nic) { ++ if (efrm_nic_tablep->nic_count == 0) { ++ efrm_nic_tablep->a_nic = NULL; ++ } else { ++ for (nic_index = 0; nic_index < EFHW_MAX_NR_DEVS; ++ nic_index++) { ++ if (efrm_nic_tablep->nic[nic_index] != NULL) ++ efrm_nic_tablep->a_nic = ++ efrm_nic_tablep->nic[nic_index]; ++ } ++ EFRM_ASSERT(efrm_nic_tablep->a_nic); ++ } ++ } ++ ++done: ++ efrm_driver_unlock(lock_flags); ++ return rc; ++} ++ ++ ++int efrm_nic_pre_reset(struct efhw_nic *nic) ++{ ++ struct efrm_nic *rnic = efrm_nic(nic); ++ struct efrm_client *client; ++ struct efrm_resource *rs; ++ struct list_head *client_link; ++ struct list_head *rs_link; ++ irq_flags_t lock_flags; ++ ++ spin_lock_irqsave(&efrm_nic_tablep->lock, lock_flags); ++ list_for_each(client_link, &rnic->clients) { ++ client = container_of(client_link, struct efrm_client, link); ++ EFRM_ERR("%s: client %p", __func__, client); ++ if (client->callbacks->pre_reset) ++ client->callbacks->pre_reset(client, client->user_data); ++ list_for_each(rs_link, &client->resources) { ++ rs = container_of(rs_link, struct efrm_resource, ++ rs_client_link); ++ EFRM_ERR("%s: resource %p", __func__, rs); ++ /* TODO: mark rs defunct */ ++ } ++ } ++ spin_unlock_irqrestore(&efrm_nic_tablep->lock, lock_flags); ++ ++ return 0; ++} ++ ++ ++int efrm_nic_stop(struct efhw_nic *nic) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++ ++int efrm_nic_resume(struct efhw_nic *nic) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++ ++static void efrm_client_nullcb(struct efrm_client *client, void *user_data) ++{ ++} ++ ++static struct efrm_client_callbacks efrm_null_callbacks = { ++ efrm_client_nullcb, ++ efrm_client_nullcb, ++ efrm_client_nullcb ++}; ++ ++ ++int efrm_client_get(int ifindex, struct efrm_client_callbacks *callbacks, ++ void *user_data, struct efrm_client **client_out) ++{ ++ struct efrm_nic *n, *rnic = NULL; ++ irq_flags_t lock_flags; ++ struct list_head *link; ++ struct efrm_client *client; ++ ++ if (callbacks == NULL) ++ callbacks = &efrm_null_callbacks; ++ ++ client = kmalloc(sizeof(*client), GFP_KERNEL); ++ if (client == NULL) ++ return -ENOMEM; ++ ++ spin_lock_irqsave(&efrm_nic_tablep->lock, lock_flags); ++ list_for_each(link, &efrm_nics) { ++ n = container_of(link, struct efrm_nic, link); ++ if (n->efhw_nic.ifindex == ifindex || ifindex < 0) { ++ rnic = n; ++ break; ++ } ++ } ++ if (rnic) { ++ client->user_data = user_data; ++ client->callbacks = callbacks; ++ client->nic = &rnic->efhw_nic; ++ client->ref_count = 1; ++ INIT_LIST_HEAD(&client->resources); ++ list_add(&client->link, &rnic->clients); ++ } ++ spin_unlock_irqrestore(&efrm_nic_tablep->lock, lock_flags); ++ ++ if (rnic == NULL) ++ return -ENODEV; ++ ++ *client_out = client; ++ return 0; ++} ++EXPORT_SYMBOL(efrm_client_get); ++ ++ ++void efrm_client_put(struct efrm_client *client) ++{ ++ irq_flags_t lock_flags; ++ ++ EFRM_ASSERT(client->ref_count > 0); ++ ++ spin_lock_irqsave(&efrm_nic_tablep->lock, lock_flags); ++ if (--client->ref_count > 0) ++ client = NULL; ++ else ++ list_del(&client->link); ++ spin_unlock_irqrestore(&efrm_nic_tablep->lock, lock_flags); ++ kfree(client); ++} ++EXPORT_SYMBOL(efrm_client_put); ++ ++ ++struct efhw_nic *efrm_client_get_nic(struct efrm_client *client) ++{ ++ return client->nic; ++} ++EXPORT_SYMBOL(efrm_client_get_nic); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/driverlink_new.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,260 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains driverlink code which interacts with the sfc network ++ * driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include "linux_resource_internal.h" ++#include "driverlink_api.h" ++#include "kernel_compat.h" ++#include ++ ++#include ++#include ++#include ++ ++/* The DL driver and associated calls */ ++static int efrm_dl_probe(struct efx_dl_device *efrm_dev, ++ const struct net_device *net_dev, ++ const struct efx_dl_device_info *dev_info, ++ const char *silicon_rev); ++ ++static void efrm_dl_remove(struct efx_dl_device *efrm_dev); ++ ++static void efrm_dl_reset_suspend(struct efx_dl_device *efrm_dev); ++ ++static void efrm_dl_reset_resume(struct efx_dl_device *efrm_dev, int ok); ++ ++static void efrm_dl_mtu_changed(struct efx_dl_device *, int); ++static void efrm_dl_event_falcon(struct efx_dl_device *efx_dev, void *p_event); ++ ++static struct efx_dl_driver efrm_dl_driver = { ++ .name = "resource", ++ .probe = efrm_dl_probe, ++ .remove = efrm_dl_remove, ++ .reset_suspend = efrm_dl_reset_suspend, ++ .reset_resume = efrm_dl_reset_resume ++}; ++ ++static void ++init_vi_resource_dimensions(struct vi_resource_dimensions *rd, ++ const struct efx_dl_falcon_resources *res) ++{ ++ rd->evq_timer_min = res->evq_timer_min; ++ rd->evq_timer_lim = res->evq_timer_lim; ++ rd->evq_int_min = res->evq_int_min; ++ rd->evq_int_lim = res->evq_int_lim; ++ rd->rxq_min = res->rxq_min; ++ rd->rxq_lim = res->rxq_lim; ++ rd->txq_min = res->txq_min; ++ rd->txq_lim = res->txq_lim; ++ EFRM_TRACE ++ ("Using evq_int(%d-%d) evq_timer(%d-%d) RXQ(%d-%d) TXQ(%d-%d)", ++ res->evq_int_min, res->evq_int_lim, res->evq_timer_min, ++ res->evq_timer_lim, res->rxq_min, res->rxq_lim, res->txq_min, ++ res->txq_lim); ++} ++ ++static int ++efrm_dl_probe(struct efx_dl_device *efrm_dev, ++ const struct net_device *net_dev, ++ const struct efx_dl_device_info *dev_info, ++ const char *silicon_rev) ++{ ++ struct vi_resource_dimensions res_dim; ++ struct efx_dl_falcon_resources *res; ++ struct linux_efhw_nic *lnic; ++ struct pci_dev *dev; ++ struct efhw_nic *nic; ++ unsigned probe_flags = 0; ++ int non_irq_evq; ++ int rc; ++ ++ efrm_dev->priv = NULL; ++ ++ efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES, ++ struct efx_dl_falcon_resources, ++ hdr, res); ++ ++ if (res == NULL) { ++ EFRM_ERR("%s: Unable to find falcon driverlink resources", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (res->flags & EFX_DL_FALCON_USE_MSI) ++ probe_flags |= NIC_FLAG_TRY_MSI; ++ ++ dev = efrm_dev->pci_dev; ++ if (res->flags & EFX_DL_FALCON_DUAL_FUNC) { ++ unsigned vendor = dev->vendor; ++ EFRM_ASSERT(dev->bus != NULL); ++ dev = NULL; ++ ++ while ((dev = pci_get_device(vendor, FALCON_S_DEVID, dev)) ++ != NULL) { ++ EFRM_ASSERT(dev->bus != NULL); ++ /* With PCIe (since it's point to point) ++ * the slot ID is usually 0 and ++ * the bus ID changes NIC to NIC, so we really ++ * need to check both. */ ++ if (PCI_SLOT(dev->devfn) == ++ PCI_SLOT(efrm_dev->pci_dev->devfn) ++ && dev->bus->number == ++ efrm_dev->pci_dev->bus->number) ++ break; ++ } ++ if (dev == NULL) { ++ EFRM_ERR("%s: Unable to find falcon secondary " ++ "PCI device.", __func__); ++ return -ENODEV; ++ } ++ pci_dev_put(dev); ++ } ++ ++ init_vi_resource_dimensions(&res_dim, res); ++ ++ EFRM_ASSERT(res_dim.evq_timer_lim > res_dim.evq_timer_min); ++ res_dim.evq_timer_lim--; ++ non_irq_evq = res_dim.evq_timer_lim; ++ ++ rc = efrm_nic_add(dev, probe_flags, net_dev->dev_addr, &lnic, ++ res->biu_lock, ++ res->buffer_table_min, res->buffer_table_lim, ++ non_irq_evq, &res_dim); ++ if (rc != 0) ++ return rc; ++ ++ nic = &lnic->efrm_nic.efhw_nic; ++ nic->mtu = net_dev->mtu + ETH_HLEN; ++ nic->net_driver_dev = efrm_dev; ++ nic->ifindex = net_dev->ifindex; ++#ifdef CONFIG_NET_NS ++ nic->nd_net = net_dev->nd_net; ++#endif ++ efrm_dev->priv = nic; ++ ++ /* Register a callback so we're told when MTU changes. ++ * We dynamically allocate efx_dl_callbacks, because ++ * the callbacks that we want depends on the NIC type. ++ */ ++ lnic->dl_callbacks = ++ kmalloc(sizeof(struct efx_dl_callbacks), GFP_KERNEL); ++ if (!lnic->dl_callbacks) { ++ EFRM_ERR("Out of memory (%s)", __func__); ++ efrm_nic_del(lnic); ++ return -ENOMEM; ++ } ++ memset(lnic->dl_callbacks, 0, sizeof(*lnic->dl_callbacks)); ++ lnic->dl_callbacks->mtu_changed = efrm_dl_mtu_changed; ++ ++ if ((res->flags & EFX_DL_FALCON_DUAL_FUNC) == 0) { ++ /* Net driver receives all management events. ++ * Register a callback to receive the ones ++ * we're interested in. */ ++ lnic->dl_callbacks->event = efrm_dl_event_falcon; ++ } ++ ++ rc = efx_dl_register_callbacks(efrm_dev, lnic->dl_callbacks); ++ if (rc < 0) { ++ EFRM_ERR("%s: efx_dl_register_callbacks failed (%d)", ++ __func__, rc); ++ kfree(lnic->dl_callbacks); ++ efrm_nic_del(lnic); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/* When we unregister ourselves on module removal, this function will be ++ * called for all the devices we claimed */ ++static void efrm_dl_remove(struct efx_dl_device *efrm_dev) ++{ ++ struct efhw_nic *nic = efrm_dev->priv; ++ struct linux_efhw_nic *lnic = linux_efhw_nic(nic); ++ EFRM_TRACE("%s called", __func__); ++ if (lnic->dl_callbacks) { ++ efx_dl_unregister_callbacks(efrm_dev, lnic->dl_callbacks); ++ kfree(lnic->dl_callbacks); ++ } ++ if (efrm_dev->priv) ++ efrm_nic_del(lnic); ++ EFRM_TRACE("%s OK", __func__); ++} ++ ++static void efrm_dl_reset_suspend(struct efx_dl_device *efrm_dev) ++{ ++ EFRM_NOTICE("%s:", __func__); ++} ++ ++static void efrm_dl_reset_resume(struct efx_dl_device *efrm_dev, int ok) ++{ ++ EFRM_NOTICE("%s: ok=%d", __func__, ok); ++} ++ ++int efrm_driverlink_register(void) ++{ ++ EFRM_TRACE("%s:", __func__); ++ return efx_dl_register_driver(&efrm_dl_driver); ++} ++ ++void efrm_driverlink_unregister(void) ++{ ++ EFRM_TRACE("%s:", __func__); ++ efx_dl_unregister_driver(&efrm_dl_driver); ++} ++ ++static void efrm_dl_mtu_changed(struct efx_dl_device *efx_dev, int mtu) ++{ ++ struct efhw_nic *nic = efx_dev->priv; ++ ++ ASSERT_RTNL(); /* Since we're looking at efx_dl_device::port_net_dev */ ++ ++ EFRM_TRACE("%s: old=%d new=%d", __func__, nic->mtu, mtu + ETH_HLEN); ++ /* If this happened we must have agreed to it above */ ++ nic->mtu = mtu + ETH_HLEN; ++} ++ ++static void efrm_dl_event_falcon(struct efx_dl_device *efx_dev, void *p_event) ++{ ++ struct efhw_nic *nic = efx_dev->priv; ++ struct linux_efhw_nic *lnic = linux_efhw_nic(nic); ++ efhw_event_t *ev = p_event; ++ ++ switch (FALCON_EVENT_CODE(ev)) { ++ case FALCON_EVENT_CODE_CHAR: ++ falcon_handle_char_event(nic, lnic->ev_handlers, ev); ++ break; ++ default: ++ EFRM_WARN("%s: unknown event type=%x", __func__, ++ (unsigned)FALCON_EVENT_CODE(ev)); ++ break; ++ } ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/efrm_internal.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,41 @@ ++#ifndef __EFRM_INTERNAL_H__ ++#define __EFRM_INTERNAL_H__ ++ ++ ++struct filter_resource { ++ struct efrm_resource rs; ++ struct vi_resource *pt; ++ int filter_idx; ++}; ++ ++#define filter_resource(rs1) container_of((rs1), struct filter_resource, rs) ++ ++ ++struct efrm_client { ++ void *user_data; ++ struct list_head link; ++ struct efrm_client_callbacks *callbacks; ++ struct efhw_nic *nic; ++ int ref_count; ++ struct list_head resources; ++}; ++ ++ ++extern void efrm_client_add_resource(struct efrm_client *, ++ struct efrm_resource *); ++ ++extern int efrm_buffer_table_size(void); ++ ++ ++static inline void efrm_resource_init(struct efrm_resource *rs, ++ int type, int instance) ++{ ++ EFRM_ASSERT(instance >= 0); ++ EFRM_ASSERT(type >= 0 && type < EFRM_RESOURCE_NUM); ++ rs->rs_ref_count = 1; ++ rs->rs_handle.handle = (type << 28u) | ++ (((unsigned)jiffies & 0xfff) << 16) | instance; ++} ++ ++ ++#endif /* __EFRM_INTERNAL_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/efx_vi_shm.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,707 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides implementation of EFX VI API, used from Xen ++ * acceleration driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include "linux_resource_internal.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include "kernel_compat.h" ++ ++#if EFX_VI_STATIC_FILTERS ++struct filter_list_t { ++ struct filter_list_t *next; ++ struct filter_resource *fres; ++}; ++#endif ++ ++struct efx_vi_state { ++ struct vi_resource *vi_res; ++ ++ int ifindex; ++ struct efrm_client *efrm_client; ++ struct efhw_nic *nic; ++ ++ void (*callback_fn)(void *arg, int is_timeout); ++ void *callback_arg; ++ ++ struct completion flush_completion; ++ ++#if EFX_VI_STATIC_FILTERS ++ struct filter_list_t fres[EFX_VI_STATIC_FILTERS]; ++ struct filter_list_t *free_fres; ++ struct filter_list_t *used_fres; ++#endif ++}; ++ ++static void efx_vi_flush_complete(void *state_void) ++{ ++ struct efx_vi_state *state = (struct efx_vi_state *)state_void; ++ ++ complete(&state->flush_completion); ++} ++ ++static inline int alloc_ep(struct efx_vi_state *state) ++{ ++ int rc; ++ ++ rc = efrm_vi_resource_alloc(state->efrm_client, NULL, EFHW_VI_JUMBO_EN, ++ efx_vi_eventq_size, ++ FALCON_DMA_Q_DEFAULT_TX_SIZE, ++ FALCON_DMA_Q_DEFAULT_RX_SIZE, ++ 0, 0, &state->vi_res, NULL, NULL, NULL, ++ NULL); ++ if (rc < 0) { ++ EFRM_ERR("%s: ERROR efrm_vi_resource_alloc error %d", ++ __func__, rc); ++ return rc; ++ } ++ ++ efrm_vi_register_flush_callback(state->vi_res, &efx_vi_flush_complete, ++ (void *)state); ++ ++ return 0; ++} ++ ++static int free_ep(struct efx_vi_state *efx_state) ++{ ++ efrm_vi_resource_release(efx_state->vi_res); ++ ++ return 0; ++} ++ ++#if EFX_VI_STATIC_FILTERS ++static int efx_vi_alloc_static_filters(struct efx_vi_state *efx_state) ++{ ++ int i; ++ int rc; ++ ++ efx_state->free_fres = efx_state->used_fres = NULL; ++ ++ for (i = 0; i < EFX_VI_STATIC_FILTERS; i++) { ++ rc = efrm_filter_resource_alloc(efx_state->vi_res, ++ &efx_state->fres[i].fres); ++ if (rc < 0) { ++ EFRM_ERR("%s: efrm_filter_resource_alloc failed: %d", ++ __func__, rc); ++ while (i > 0) { ++ i--; ++ efrm_filter_resource_release(efx_state-> ++ fres[i].fres); ++ } ++ efx_state->free_fres = NULL; ++ return rc; ++ } ++ efx_state->fres[i].next = efx_state->free_fres; ++ efx_state->free_fres = &efx_state->fres[i]; ++ } ++ ++ return 0; ++} ++#endif ++ ++int efx_vi_alloc(struct efx_vi_state **vih_out, int ifindex) ++{ ++ struct efx_vi_state *efx_state; ++ int rc; ++ ++ efx_state = kmalloc(sizeof(struct efx_vi_state), GFP_KERNEL); ++ ++ if (!efx_state) { ++ EFRM_ERR("%s: failed to allocate memory for efx_vi_state", ++ __func__); ++ rc = -ENOMEM; ++ goto fail; ++ } ++ ++ efx_state->ifindex = ifindex; ++ rc = efrm_client_get(ifindex, NULL, NULL, &efx_state->efrm_client); ++ if (rc < 0) { ++ EFRM_ERR("%s: efrm_client_get(%d) failed: %d", __func__, ++ ifindex, rc); ++ rc = -ENODEV; ++ goto fail_no_ifindex; ++ } ++ efx_state->nic = efrm_client_get_nic(efx_state->efrm_client); ++ ++ init_completion(&efx_state->flush_completion); ++ ++ /* basically allocate_pt_endpoint() */ ++ rc = alloc_ep(efx_state); ++ if (rc) { ++ EFRM_ERR("%s: alloc_ep failed: %d", __func__, rc); ++ goto fail_no_pt; ++ } ++#if EFX_VI_STATIC_FILTERS ++ /* Statically allocate a set of filter resources - removes the ++ restriction on not being able to use efx_vi_filter() from ++ in_atomic() */ ++ rc = efx_vi_alloc_static_filters(efx_state); ++ if (rc) ++ goto fail_no_filters; ++#endif ++ ++ *vih_out = efx_state; ++ ++ return 0; ++#if EFX_VI_STATIC_FILTERS ++fail_no_filters: ++ free_ep(efx_state); ++#endif ++fail_no_pt: ++ efrm_client_put(efx_state->efrm_client); ++fail_no_ifindex: ++ kfree(efx_state); ++fail: ++ return rc; ++} ++EXPORT_SYMBOL(efx_vi_alloc); ++ ++void efx_vi_free(struct efx_vi_state *vih) ++{ ++ struct efx_vi_state *efx_state = vih; ++ ++ /* TODO flush dma channels, init dma queues?. See ef_free_vnic() */ ++#if EFX_VI_STATIC_FILTERS ++ int i; ++ ++ for (i = 0; i < EFX_VI_STATIC_FILTERS; i++) ++ efrm_filter_resource_release(efx_state->fres[i].fres); ++#endif ++ ++ if (efx_state->vi_res) ++ free_ep(efx_state); ++ ++ efrm_client_put(efx_state->efrm_client); ++ ++ kfree(efx_state); ++} ++EXPORT_SYMBOL(efx_vi_free); ++ ++void efx_vi_reset(struct efx_vi_state *vih) ++{ ++ struct efx_vi_state *efx_state = vih; ++ ++ efrm_pt_flush(efx_state->vi_res); ++ ++ while (wait_for_completion_timeout(&efx_state->flush_completion, HZ) ++ == 0) ++ efrm_vi_resource_flush_retry(efx_state->vi_res); ++ ++ /* Bosch the eventq */ ++ efrm_eventq_reset(efx_state->vi_res); ++ return; ++} ++EXPORT_SYMBOL(efx_vi_reset); ++ ++static void ++efx_vi_eventq_callback(void *context, int is_timeout, struct efhw_nic *nic) ++{ ++ struct efx_vi_state *efx_state = (struct efx_vi_state *)context; ++ ++ EFRM_ASSERT(efx_state->callback_fn); ++ ++ return efx_state->callback_fn(efx_state->callback_arg, is_timeout); ++} ++ ++int ++efx_vi_eventq_register_callback(struct efx_vi_state *vih, ++ void (*callback)(void *context, int is_timeout), ++ void *context) ++{ ++ struct efx_vi_state *efx_state = vih; ++ ++ efx_state->callback_fn = callback; ++ efx_state->callback_arg = context; ++ ++ /* Register the eventq timeout event callback */ ++ efrm_eventq_register_callback(efx_state->vi_res, ++ efx_vi_eventq_callback, efx_state); ++ ++ return 0; ++} ++EXPORT_SYMBOL(efx_vi_eventq_register_callback); ++ ++int efx_vi_eventq_kill_callback(struct efx_vi_state *vih) ++{ ++ struct efx_vi_state *efx_state = vih; ++ ++ if (efx_state->vi_res->evq_callback_fn) ++ efrm_eventq_kill_callback(efx_state->vi_res); ++ ++ efx_state->callback_fn = NULL; ++ efx_state->callback_arg = NULL; ++ ++ return 0; ++} ++EXPORT_SYMBOL(efx_vi_eventq_kill_callback); ++ ++struct efx_vi_dma_map_state { ++ struct efhw_buffer_table_allocation bt_handle; ++ int n_pages; ++ dma_addr_t *dma_addrs; ++}; ++ ++int ++efx_vi_dma_map_pages(struct efx_vi_state *vih, struct page **pages, ++ int n_pages, struct efx_vi_dma_map_state **dmh_out) ++{ ++ struct efx_vi_state *efx_state = vih; ++ int order = fls(n_pages - 1), rc, i, evq_id; ++ dma_addr_t dma_addr; ++ struct efx_vi_dma_map_state *dm_state; ++ ++ if (n_pages != (1 << order)) { ++ EFRM_WARN("%s: Can only allocate buffers in power of 2 " ++ "sizes (not %d)", __func__, n_pages); ++ return -EINVAL; ++ } ++ ++ dm_state = kmalloc(sizeof(struct efx_vi_dma_map_state), GFP_KERNEL); ++ if (!dm_state) ++ return -ENOMEM; ++ ++ dm_state->dma_addrs = kmalloc(sizeof(dma_addr_t) * n_pages, ++ GFP_KERNEL); ++ if (!dm_state->dma_addrs) { ++ kfree(dm_state); ++ return -ENOMEM; ++ } ++ ++ rc = efrm_buffer_table_alloc(order, &dm_state->bt_handle); ++ if (rc < 0) { ++ kfree(dm_state->dma_addrs); ++ kfree(dm_state); ++ return rc; ++ } ++ ++ evq_id = EFRM_RESOURCE_INSTANCE(efx_state->vi_res->rs.rs_handle); ++ for (i = 0; i < n_pages; i++) { ++ /* TODO do we need to get_page() here ? */ ++ ++ dma_addr = pci_map_page(linux_efhw_nic(efx_state->nic)-> ++ pci_dev, pages[i], 0, PAGE_SIZE, ++ PCI_DMA_TODEVICE); ++ ++ efrm_buffer_table_set(&dm_state->bt_handle, efx_state->nic, ++ i, dma_addr, evq_id); ++ ++ dm_state->dma_addrs[i] = dma_addr; ++ ++ /* Would be nice to not have to call commit each time, but ++ * comment says there are hardware restrictions on how often ++ * you can go without it, so do this to be safe */ ++ efrm_buffer_table_commit(); ++ } ++ ++ dm_state->n_pages = n_pages; ++ ++ *dmh_out = dm_state; ++ ++ return 0; ++} ++EXPORT_SYMBOL(efx_vi_dma_map_pages); ++ ++/* Function needed as Xen can't get pages for grants in dom0, but can ++ get dma address */ ++int ++efx_vi_dma_map_addrs(struct efx_vi_state *vih, ++ unsigned long long *bus_dev_addrs, ++ int n_pages, struct efx_vi_dma_map_state **dmh_out) ++{ ++ struct efx_vi_state *efx_state = vih; ++ int order = fls(n_pages - 1), rc, i, evq_id; ++ dma_addr_t dma_addr; ++ struct efx_vi_dma_map_state *dm_state; ++ ++ if (n_pages != (1 << order)) { ++ EFRM_WARN("%s: Can only allocate buffers in power of 2 " ++ "sizes (not %d)", __func__, n_pages); ++ return -EINVAL; ++ } ++ ++ dm_state = kmalloc(sizeof(struct efx_vi_dma_map_state), GFP_KERNEL); ++ if (!dm_state) ++ return -ENOMEM; ++ ++ dm_state->dma_addrs = kmalloc(sizeof(dma_addr_t) * n_pages, ++ GFP_KERNEL); ++ if (!dm_state->dma_addrs) { ++ kfree(dm_state); ++ return -ENOMEM; ++ } ++ ++ rc = efrm_buffer_table_alloc(order, &dm_state->bt_handle); ++ if (rc < 0) { ++ kfree(dm_state->dma_addrs); ++ kfree(dm_state); ++ return rc; ++ } ++ ++ evq_id = EFRM_RESOURCE_INSTANCE(efx_state->vi_res->rs.rs_handle); ++#if 0 ++ EFRM_WARN("%s: mapping %d pages to evq %d, bt_ids %d-%d\n", ++ __func__, n_pages, evq_id, ++ dm_state->bt_handle.base, ++ dm_state->bt_handle.base + n_pages); ++#endif ++ for (i = 0; i < n_pages; i++) { ++ ++ dma_addr = (dma_addr_t)bus_dev_addrs[i]; ++ ++ efrm_buffer_table_set(&dm_state->bt_handle, efx_state->nic, ++ i, dma_addr, evq_id); ++ ++ dm_state->dma_addrs[i] = dma_addr; ++ ++ /* Would be nice to not have to call commit each time, but ++ * comment says there are hardware restrictions on how often ++ * you can go without it, so do this to be safe */ ++ efrm_buffer_table_commit(); ++ } ++ ++ dm_state->n_pages = n_pages; ++ ++ *dmh_out = dm_state; ++ ++ return 0; ++} ++EXPORT_SYMBOL(efx_vi_dma_map_addrs); ++ ++void ++efx_vi_dma_unmap_pages(struct efx_vi_state *vih, ++ struct efx_vi_dma_map_state *dmh) ++{ ++ struct efx_vi_state *efx_state = vih; ++ struct efx_vi_dma_map_state *dm_state = ++ (struct efx_vi_dma_map_state *)dmh; ++ int i; ++ ++ efrm_buffer_table_free(&dm_state->bt_handle); ++ ++ for (i = 0; i < dm_state->n_pages; ++i) ++ pci_unmap_page(linux_efhw_nic(efx_state->nic)->pci_dev, ++ dm_state->dma_addrs[i], PAGE_SIZE, ++ PCI_DMA_TODEVICE); ++ ++ kfree(dm_state->dma_addrs); ++ kfree(dm_state); ++ ++ return; ++} ++EXPORT_SYMBOL(efx_vi_dma_unmap_pages); ++ ++void ++efx_vi_dma_unmap_addrs(struct efx_vi_state *vih, ++ struct efx_vi_dma_map_state *dmh) ++{ ++ struct efx_vi_dma_map_state *dm_state = ++ (struct efx_vi_dma_map_state *)dmh; ++ ++ efrm_buffer_table_free(&dm_state->bt_handle); ++ ++ kfree(dm_state->dma_addrs); ++ kfree(dm_state); ++ ++ return; ++} ++EXPORT_SYMBOL(efx_vi_dma_unmap_addrs); ++ ++unsigned ++efx_vi_dma_get_map_addr(struct efx_vi_state *vih, ++ struct efx_vi_dma_map_state *dmh) ++{ ++ struct efx_vi_dma_map_state *dm_state = ++ (struct efx_vi_dma_map_state *)dmh; ++ ++ return EFHW_BUFFER_ADDR(dm_state->bt_handle.base, 0); ++} ++EXPORT_SYMBOL(efx_vi_dma_get_map_addr); ++ ++#if EFX_VI_STATIC_FILTERS ++static int ++get_filter(struct efx_vi_state *efx_state, ++ efrm_resource_handle_t pthandle, struct filter_resource **fres_out) ++{ ++ struct filter_list_t *flist; ++ if (efx_state->free_fres == NULL) ++ return -ENOMEM; ++ else { ++ flist = efx_state->free_fres; ++ efx_state->free_fres = flist->next; ++ flist->next = efx_state->used_fres; ++ efx_state->used_fres = flist; ++ *fres_out = flist->fres; ++ return 0; ++ } ++} ++#endif ++ ++static void ++release_filter(struct efx_vi_state *efx_state, struct filter_resource *fres) ++{ ++#if EFX_VI_STATIC_FILTERS ++ struct filter_list_t *flist = efx_state->used_fres, *prev = NULL; ++ while (flist) { ++ if (flist->fres == fres) { ++ if (prev) ++ prev->next = flist->next; ++ else ++ efx_state->used_fres = flist->next; ++ flist->next = efx_state->free_fres; ++ efx_state->free_fres = flist; ++ return; ++ } ++ prev = flist; ++ flist = flist->next; ++ } ++ EFRM_ERR("%s: couldn't find filter", __func__); ++#else ++ return efrm_filter_resource_release(fres); ++#endif ++} ++ ++int ++efx_vi_filter(struct efx_vi_state *vih, int protocol, ++ unsigned ip_addr_be32, int port_le16, ++ struct filter_resource_t **fh_out) ++{ ++ struct efx_vi_state *efx_state = vih; ++ struct filter_resource *uninitialized_var(frs); ++ int rc; ++ ++#if EFX_VI_STATIC_FILTERS ++ rc = get_filter(efx_state, efx_state->vi_res->rs.rs_handle, &frs); ++#else ++ rc = efrm_filter_resource_alloc(efx_state->vi_res, &frs); ++#endif ++ if (rc < 0) ++ return rc; ++ ++ /* Add the hardware filter. We pass in the source port and address ++ * as 0 (wildcard) to minimise the number of filters needed. */ ++ if (protocol == IPPROTO_TCP) { ++ rc = efrm_filter_resource_tcp_set(frs, 0, 0, ip_addr_be32, ++ port_le16); ++ } else { ++ rc = efrm_filter_resource_udp_set(frs, 0, 0, ip_addr_be32, ++ port_le16); ++ } ++ ++ *fh_out = (struct filter_resource_t *)frs; ++ ++ return rc; ++} ++EXPORT_SYMBOL(efx_vi_filter); ++ ++int ++efx_vi_filter_stop(struct efx_vi_state *vih, struct filter_resource_t *fh) ++{ ++ struct efx_vi_state *efx_state = vih; ++ struct filter_resource *frs = (struct filter_resource *)fh; ++ int rc; ++ ++ rc = efrm_filter_resource_clear(frs); ++ release_filter(efx_state, frs); ++ ++ return rc; ++} ++EXPORT_SYMBOL(efx_vi_filter_stop); ++ ++int ++efx_vi_hw_resource_get_virt(struct efx_vi_state *vih, ++ struct efx_vi_hw_resource_metadata *mdata, ++ struct efx_vi_hw_resource *hw_res_array, ++ int *length) ++{ ++ EFRM_NOTICE("%s: TODO!", __func__); ++ ++ return 0; ++} ++EXPORT_SYMBOL(efx_vi_hw_resource_get_virt); ++ ++int ++efx_vi_hw_resource_get_phys(struct efx_vi_state *vih, ++ struct efx_vi_hw_resource_metadata *mdata, ++ struct efx_vi_hw_resource *hw_res_array, ++ int *length) ++{ ++ struct efx_vi_state *efx_state = vih; ++ struct linux_efhw_nic *lnic = linux_efhw_nic(efx_state->nic); ++ unsigned long phys = lnic->ctr_ap_pci_addr; ++ struct efrm_resource *ep_res = &efx_state->vi_res->rs; ++ unsigned ep_mmap_bytes; ++ int i; ++ ++ if (*length < EFX_VI_HW_RESOURCE_MAXSIZE) ++ return -EINVAL; ++ ++ mdata->nic_arch = efx_state->nic->devtype.arch; ++ mdata->nic_variant = efx_state->nic->devtype.variant; ++ mdata->nic_revision = efx_state->nic->devtype.revision; ++ ++ mdata->evq_order = ++ efx_state->vi_res->nic_info.evq_pages.iobuff.order; ++ mdata->evq_offs = efx_state->vi_res->nic_info.evq_pages.iobuff_off; ++ mdata->evq_capacity = efx_vi_eventq_size; ++ mdata->instance = EFRM_RESOURCE_INSTANCE(ep_res->rs_handle); ++ mdata->rx_capacity = FALCON_DMA_Q_DEFAULT_RX_SIZE; ++ mdata->tx_capacity = FALCON_DMA_Q_DEFAULT_TX_SIZE; ++ ++ ep_mmap_bytes = FALCON_DMA_Q_DEFAULT_MMAP; ++ EFRM_ASSERT(ep_mmap_bytes == PAGE_SIZE * 2); ++ ++#ifndef NDEBUG ++ { ++ /* Sanity about doorbells */ ++ unsigned long tx_dma_page_addr, rx_dma_page_addr; ++ ++ /* get rx doorbell address */ ++ rx_dma_page_addr = ++ phys + falcon_rx_dma_page_addr(mdata->instance); ++ /* get tx doorbell address */ ++ tx_dma_page_addr = ++ phys + falcon_tx_dma_page_addr(mdata->instance); ++ ++ /* Check the lower bits of the TX doorbell will be ++ * consistent. */ ++ EFRM_ASSERT((TX_DESC_UPD_REG_PAGE4_OFST & ++ FALCON_DMA_PAGE_MASK) == ++ (TX_DESC_UPD_REG_PAGE123K_OFST & ++ FALCON_DMA_PAGE_MASK)); ++ ++ /* Check the lower bits of the RX doorbell will be ++ * consistent. */ ++ EFRM_ASSERT((RX_DESC_UPD_REG_PAGE4_OFST & ++ FALCON_DMA_PAGE_MASK) == ++ (RX_DESC_UPD_REG_PAGE123K_OFST & ++ FALCON_DMA_PAGE_MASK)); ++ ++ /* Check that the doorbells will be in the same page. */ ++ EFRM_ASSERT((TX_DESC_UPD_REG_PAGE4_OFST & PAGE_MASK) == ++ (RX_DESC_UPD_REG_PAGE4_OFST & PAGE_MASK)); ++ ++ /* Check that the doorbells are in the same page. */ ++ EFRM_ASSERT((tx_dma_page_addr & PAGE_MASK) == ++ (rx_dma_page_addr & PAGE_MASK)); ++ ++ /* Check that the TX doorbell offset is correct. */ ++ EFRM_ASSERT((TX_DESC_UPD_REG_PAGE4_OFST & ~PAGE_MASK) == ++ (tx_dma_page_addr & ~PAGE_MASK)); ++ ++ /* Check that the RX doorbell offset is correct. */ ++ EFRM_ASSERT((RX_DESC_UPD_REG_PAGE4_OFST & ~PAGE_MASK) == ++ (rx_dma_page_addr & ~PAGE_MASK)); ++ } ++#endif ++ ++ i = 0; ++ hw_res_array[i].type = EFX_VI_HW_RESOURCE_TXDMAQ; ++ hw_res_array[i].mem_type = EFX_VI_HW_RESOURCE_PERIPHERAL; ++ hw_res_array[i].more_to_follow = 0; ++ hw_res_array[i].length = PAGE_SIZE; ++ hw_res_array[i].address = ++ (unsigned long)efx_state->vi_res->nic_info. ++ dmaq_pages[EFRM_VI_RM_DMA_QUEUE_TX].kva; ++ ++ i++; ++ hw_res_array[i].type = EFX_VI_HW_RESOURCE_RXDMAQ; ++ hw_res_array[i].mem_type = EFX_VI_HW_RESOURCE_PERIPHERAL; ++ hw_res_array[i].more_to_follow = 0; ++ hw_res_array[i].length = PAGE_SIZE; ++ hw_res_array[i].address = ++ (unsigned long)efx_state->vi_res->nic_info. ++ dmaq_pages[EFRM_VI_RM_DMA_QUEUE_RX].kva; ++ ++ i++; ++ hw_res_array[i].type = EFX_VI_HW_RESOURCE_EVQTIMER; ++ hw_res_array[i].mem_type = EFX_VI_HW_RESOURCE_PERIPHERAL; ++ hw_res_array[i].more_to_follow = 0; ++ hw_res_array[i].length = PAGE_SIZE; ++ hw_res_array[i].address = ++ (unsigned long)phys + falcon_timer_page_addr(mdata->instance); ++ ++ /* NB EFX_VI_HW_RESOURCE_EVQPTR not used on Falcon */ ++ ++ i++; ++ switch (efx_state->nic->devtype.variant) { ++ case 'A': ++ hw_res_array[i].type = EFX_VI_HW_RESOURCE_EVQRPTR; ++ hw_res_array[i].mem_type = EFX_VI_HW_RESOURCE_PERIPHERAL; ++ hw_res_array[i].more_to_follow = 0; ++ hw_res_array[i].length = PAGE_SIZE; ++ hw_res_array[i].address = (unsigned long)phys + ++ EVQ_RPTR_REG_OFST + ++ (FALCON_REGISTER128 * mdata->instance); ++ break; ++ case 'B': ++ hw_res_array[i].type = EFX_VI_HW_RESOURCE_EVQRPTR_OFFSET; ++ hw_res_array[i].mem_type = EFX_VI_HW_RESOURCE_PERIPHERAL; ++ hw_res_array[i].more_to_follow = 0; ++ hw_res_array[i].length = PAGE_SIZE; ++ hw_res_array[i].address = ++ (unsigned long)FALCON_EVQ_RPTR_REG_P0; ++ break; ++ default: ++ EFRM_ASSERT(0); ++ break; ++ } ++ ++ i++; ++ hw_res_array[i].type = EFX_VI_HW_RESOURCE_EVQMEMKVA; ++ hw_res_array[i].mem_type = EFX_VI_HW_RESOURCE_IOBUFFER; ++ hw_res_array[i].more_to_follow = 0; ++ hw_res_array[i].length = PAGE_SIZE; ++ hw_res_array[i].address = (unsigned long)efx_state->vi_res-> ++ nic_info.evq_pages.iobuff.kva; ++ ++ i++; ++ hw_res_array[i].type = EFX_VI_HW_RESOURCE_BELLPAGE; ++ hw_res_array[i].mem_type = EFX_VI_HW_RESOURCE_PERIPHERAL; ++ hw_res_array[i].more_to_follow = 0; ++ hw_res_array[i].length = PAGE_SIZE; ++ hw_res_array[i].address = ++ (unsigned long)(phys + ++ falcon_tx_dma_page_addr(mdata->instance)) ++ >> PAGE_SHIFT; ++ ++ i++; ++ ++ EFRM_ASSERT(i <= *length); ++ ++ *length = i; ++ ++ return 0; ++} ++EXPORT_SYMBOL(efx_vi_hw_resource_get_phys); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/eventq.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,321 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains event queue support. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define KEVENTQ_MAGIC 0x07111974 ++ ++/*! Helper function to allocate the iobuffer needed by an eventq ++ * - it ensures the eventq has the correct alignment for the NIC ++ * ++ * \param rm Event-queue resource manager ++ * \param instance Event-queue instance (index) ++ * \param buf_bytes Requested size of eventq ++ * \return < 0 if iobuffer allocation fails ++ */ ++int ++efhw_nic_event_queue_alloc_iobuffer(struct efhw_nic *nic, ++ struct eventq_resource_hardware *h, ++ int evq_instance, unsigned buf_bytes) ++{ ++ unsigned int page_order; ++ int rc; ++ ++ /* Allocate an iobuffer. */ ++ page_order = get_order(buf_bytes); ++ ++ h->iobuff_off = 0; ++ ++ EFHW_TRACE("allocating eventq size %x", ++ 1u << (page_order + PAGE_SHIFT)); ++ rc = efhw_iopages_alloc(nic, &h->iobuff, page_order); ++ if (rc < 0) { ++ EFHW_WARN("%s: failed to allocate %u pages", ++ __func__, 1u << page_order); ++ return rc; ++ } ++ ++ /* Set the eventq pages to match EFHW_CLEAR_EVENT() */ ++ if (EFHW_CLEAR_EVENT_VALUE) ++ memset(efhw_iopages_ptr(&h->iobuff) + h->iobuff_off, ++ EFHW_CLEAR_EVENT_VALUE, (1u << page_order) * PAGE_SIZE); ++ ++ EFHW_TRACE("%s: allocated %u pages", __func__, 1u << (page_order)); ++ ++ /* For Falcon the NIC is programmed with the base buffer address of a ++ * contiguous region of buffer space. This means that larger than a ++ * PAGE event queues can be expected to allocate even when the host's ++ * physical memory is fragmented */ ++ EFHW_ASSERT(efhw_nic_have_hw(nic)); ++ EFHW_ASSERT(page_order <= h->buf_tbl_alloc.order); ++ ++ /* Initialise the buffer table entries. */ ++ falcon_nic_buffer_table_set_n(nic, h->buf_tbl_alloc.base, ++ efhw_iopages_dma_addr(&h->iobuff) + ++ h->iobuff_off, EFHW_NIC_PAGE_SIZE, 0, ++ 1 << page_order, 0); ++ ++ if (evq_instance >= FALCON_EVQ_TBL_RESERVED) ++ falcon_nic_buffer_table_confirm(nic); ++ return 0; ++} ++ ++/********************************************************************** ++ * Kernel event queue management. ++ */ ++ ++/* Values for [struct efhw_keventq::lock] field. */ ++#define KEVQ_UNLOCKED 0 ++#define KEVQ_LOCKED 1 ++#define KEVQ_RECHECK 2 ++ ++int ++efhw_keventq_ctor(struct efhw_nic *nic, int instance, ++ struct efhw_keventq *evq, ++ struct efhw_ev_handler *ev_handlers) ++{ ++ int rc; ++ unsigned buf_bytes = evq->hw.capacity * sizeof(efhw_event_t); ++ ++ evq->instance = instance; ++ evq->ev_handlers = ev_handlers; ++ ++ /* allocate an IObuffer for the eventq */ ++ rc = efhw_nic_event_queue_alloc_iobuffer(nic, &evq->hw, evq->instance, ++ buf_bytes); ++ if (rc < 0) ++ return rc; ++ ++ /* Zero the timer-value for this queue. ++ AND Tell the nic about the event queue. */ ++ efhw_nic_event_queue_enable(nic, evq->instance, evq->hw.capacity, ++ efhw_iopages_dma_addr(&evq->hw.iobuff) + ++ evq->hw.iobuff_off, ++ evq->hw.buf_tbl_alloc.base, ++ 1 /* interrupting */); ++ ++ evq->lock = KEVQ_UNLOCKED; ++ evq->evq_base = efhw_iopages_ptr(&evq->hw.iobuff) + evq->hw.iobuff_off; ++ evq->evq_ptr = 0; ++ evq->evq_mask = (evq->hw.capacity * sizeof(efhw_event_t)) - 1u; ++ ++ EFHW_TRACE("%s: [%d] base=%p end=%p", __func__, evq->instance, ++ evq->evq_base, evq->evq_base + buf_bytes); ++ ++ return 0; ++} ++ ++void efhw_keventq_dtor(struct efhw_nic *nic, struct efhw_keventq *evq) ++{ ++ EFHW_ASSERT(evq); ++ ++ EFHW_TRACE("%s: [%d]", __func__, evq->instance); ++ ++ /* Zero the timer-value for this queue. ++ And Tell NIC to stop using this event queue. */ ++ efhw_nic_event_queue_disable(nic, evq->instance, 0); ++ ++ /* free the pages used by the eventq itself */ ++ efhw_iopages_free(nic, &evq->hw.iobuff); ++} ++ ++void ++efhw_handle_txdmaq_flushed(struct efhw_nic *nic, struct efhw_ev_handler *h, ++ efhw_event_t *evp) ++{ ++ int instance = (int)FALCON_EVENT_TX_FLUSH_Q_ID(evp); ++ EFHW_TRACE("%s: instance=%d", __func__, instance); ++ ++ if (!h->dmaq_flushed_fn) { ++ EFHW_WARN("%s: no handler registered", __func__); ++ return; ++ } ++ ++ h->dmaq_flushed_fn(nic, instance, false); ++} ++ ++void ++efhw_handle_rxdmaq_flushed(struct efhw_nic *nic, struct efhw_ev_handler *h, ++ efhw_event_t *evp) ++{ ++ unsigned instance = (unsigned)FALCON_EVENT_RX_FLUSH_Q_ID(evp); ++ EFHW_TRACE("%s: instance=%d", __func__, instance); ++ ++ if (!h->dmaq_flushed_fn) { ++ EFHW_WARN("%s: no handler registered", __func__); ++ return; ++ } ++ ++ h->dmaq_flushed_fn(nic, instance, true); ++} ++ ++void ++efhw_handle_wakeup_event(struct efhw_nic *nic, struct efhw_ev_handler *h, ++ efhw_event_t *evp) ++{ ++ unsigned instance = (unsigned)FALCON_EVENT_WAKE_EVQ_ID(evp); ++ ++ if (!h->wakeup_fn) { ++ EFHW_WARN("%s: no handler registered", __func__); ++ return; ++ } ++ ++ h->wakeup_fn(nic, instance); ++} ++ ++void ++efhw_handle_timeout_event(struct efhw_nic *nic, struct efhw_ev_handler *h, ++ efhw_event_t *evp) ++{ ++ unsigned instance = (unsigned)FALCON_EVENT_WAKE_EVQ_ID(evp); ++ ++ if (!h->timeout_fn) { ++ EFHW_WARN("%s: no handler registered", __func__); ++ return; ++ } ++ ++ h->timeout_fn(nic, instance); ++} ++ ++/********************************************************************** ++ * Kernel event queue event handling. ++ */ ++ ++int efhw_keventq_poll(struct efhw_nic *nic, struct efhw_keventq *q) ++{ ++ efhw_event_t *ev; ++ int l, count = 0; ++ ++ EFHW_ASSERT(nic); ++ EFHW_ASSERT(q); ++ EFHW_ASSERT(q->ev_handlers); ++ ++ /* Acquire the lock, or mark the queue as needing re-checking. */ ++ for (;;) { ++ l = q->lock; ++ if (l == KEVQ_UNLOCKED) { ++ if ((int)cmpxchg(&q->lock, l, KEVQ_LOCKED) == l) ++ break; ++ } else if (l == KEVQ_LOCKED) { ++ if ((int)cmpxchg(&q->lock, l, KEVQ_RECHECK) == l) ++ return 0; ++ } else { /* already marked for re-checking */ ++ EFHW_ASSERT(l == KEVQ_RECHECK); ++ return 0; ++ } ++ } ++ ++ if (unlikely(EFHW_EVENT_OVERFLOW(q, q))) ++ goto overflow; ++ ++ ev = EFHW_EVENT_PTR(q, q, 0); ++ ++#ifndef NDEBUG ++ if (!EFHW_IS_EVENT(ev)) ++ EFHW_TRACE("%s: %d NO EVENTS!", __func__, q->instance); ++#endif ++ ++ for (;;) { ++ /* Convention for return codes for handlers is: ++ ** 0 - no error, event consumed ++ ** 1 - no error, event not consumed ++ ** -ve - error, event not consumed ++ */ ++ if (likely(EFHW_IS_EVENT(ev))) { ++ count++; ++ ++ switch (FALCON_EVENT_CODE(ev)) { ++ ++ case FALCON_EVENT_CODE_CHAR: ++ falcon_handle_char_event(nic, q->ev_handlers, ++ ev); ++ break; ++ ++ default: ++ EFHW_ERR("efhw_keventq_poll: [%d] UNEXPECTED " ++ "EVENT:"FALCON_EVENT_FMT, ++ q->instance, ++ FALCON_EVENT_PRI_ARG(*ev)); ++ } ++ ++ EFHW_CLEAR_EVENT(ev); ++ EFHW_EVENTQ_NEXT(q); ++ ++ ev = EFHW_EVENT_PTR(q, q, 0); ++ } else { ++ /* No events left. Release the lock (checking if we ++ * need to re-poll to avoid race). */ ++ l = q->lock; ++ if (l == KEVQ_LOCKED) { ++ if ((int)cmpxchg(&q->lock, l, KEVQ_UNLOCKED) ++ == l) { ++ EFHW_TRACE ++ ("efhw_keventq_poll: %d clean exit", ++ q->instance); ++ goto clean_exit; ++ } ++ } ++ ++ /* Potentially more work to do. */ ++ l = q->lock; ++ EFHW_ASSERT(l == KEVQ_RECHECK); ++ EFHW_TEST((int)cmpxchg(&q->lock, l, KEVQ_LOCKED) == l); ++ EFHW_TRACE("efhw_keventq_poll: %d re-poll required", ++ q->instance); ++ } ++ } ++ ++ /* shouldn't get here */ ++ EFHW_ASSERT(0); ++ ++overflow: ++ /* ?? Oh dear. Should we poll everything that could have possibly ++ ** happened? Or merely cry out in anguish... ++ */ ++ EFHW_WARN("efhw_keventq_poll: %d ***** OVERFLOW nic %d *****", ++ q->instance, nic->index); ++ ++ q->lock = KEVQ_UNLOCKED; ++ return count; ++ ++clean_exit: ++ /* Ack the processed events so that this event queue can potentially ++ raise interrupts again */ ++ falcon_nic_evq_ack(nic, q->instance, ++ (EFHW_EVENT_OFFSET(q, q, 0) / sizeof(efhw_event_t)), ++ false); ++ return count; ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/falcon.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,2525 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains Falcon hardware support. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Workarounds and options ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/* Keep a software copy of the filter table and check for duplicates. */ ++#define FALCON_FULL_FILTER_CACHE 1 ++ ++/* Read filters back from the hardware to detect corruption. */ ++#define FALCON_VERIFY_FILTERS 0 ++ ++/* Options */ ++#define RX_FILTER_CTL_SRCH_LIMIT_TCP_FULL 8 /* default search limit */ ++#define RX_FILTER_CTL_SRCH_LIMIT_TCP_WILD 8 /* default search limit */ ++#define RX_FILTER_CTL_SRCH_LIMIT_UDP_FULL 8 /* default search limit */ ++#define RX_FILTER_CTL_SRCH_LIMIT_UDP_WILD 8 /* default search limit */ ++ ++#define FALCON_MAC_SET_TYPE_BY_SPEED 0 ++ ++/* FIXME: We should detect mode at runtime. */ ++#define FALCON_BUFFER_TABLE_FULL_MODE 1 ++ ++/* "Fudge factors" - difference between programmed value and actual depth */ ++#define RX_FILTER_CTL_SRCH_FUDGE_WILD 3 /* increase the search limit */ ++#define RX_FILTER_CTL_SRCH_FUDGE_FULL 1 /* increase the search limit */ ++#define TX_FILTER_CTL_SRCH_FUDGE_WILD 3 /* increase the search limit */ ++#define TX_FILTER_CTL_SRCH_FUDGE_FULL 1 /* increase the search limit */ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Debug Macros ++ * ++ *---------------------------------------------------------------------------*/ ++ ++#define _DEBUG_SYM_ static ++ ++ /*---------------------------------------------------------------------------- ++ * ++ * Macros and forward declarations ++ * ++ *--------------------------------------------------------------------------*/ ++ ++#define FALCON_REGION_NUM 4 /* number of supported memory regions */ ++ ++#define FALCON_BUFFER_TBL_HALF_BYTES 4 ++#define FALCON_BUFFER_TBL_FULL_BYTES 8 ++ ++/* Shadow buffer table - hack for testing only */ ++#if FALCON_BUFFER_TABLE_FULL_MODE == 0 ++# define FALCON_USE_SHADOW_BUFFER_TABLE 1 ++#else ++# define FALCON_USE_SHADOW_BUFFER_TABLE 0 ++#endif ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Header assertion checks ++ * ++ *---------------------------------------------------------------------------*/ ++ ++#define FALCON_ASSERT_VALID() /* nothing yet */ ++ ++/* Falcon has a 128bit register model but most registers have useful ++ defaults or only implement a small number of bits. Some registers ++ can be programmed 32bits UNLOCKED all others should be interlocked ++ against other threads within the same protection domain. ++ ++ Aim is for software to perform the minimum number of writes and ++ also to minimise the read-modify-write activity (which generally ++ indicates a lack of clarity in the use model). ++ ++ Registers which are programmed in this module are listed below ++ together with the method of access. Care must be taken to ensure ++ remain adequate if the register spec changes. ++ ++ All 128bits programmed ++ FALCON_BUFFER_TBL_HALF ++ RX_FILTER_TBL ++ TX_DESC_PTR_TBL ++ RX_DESC_PTR_TBL ++ DRV_EV_REG ++ ++ All 64bits programmed ++ FALCON_BUFFER_TBL_FULL ++ ++ 32 bits are programmed (UNLOCKED) ++ EVQ_RPTR_REG ++ ++ Low 64bits programmed remainder are written with a random number ++ RX_DC_CFG_REG ++ TX_DC_CFG_REG ++ SRM_RX_DC_CFG_REG ++ SRM_TX_DC_CFG_REG ++ BUF_TBL_CFG_REG ++ BUF_TBL_UPD_REG ++ SRM_UPD_EVQ_REG ++ EVQ_PTR_TBL ++ TIMER_CMD_REG ++ TX_PACE_TBL ++ FATAL_INTR_REG ++ INT_EN_REG (When enabling interrupts) ++ TX_FLUSH_DESCQ_REG ++ RX_FLUSH_DESCQ ++ ++ Read Modify Write on low 32bits remainder are written with a random number ++ INT_EN_REG (When sending a driver interrupt) ++ DRIVER_REGX ++ ++ Read Modify Write on low 64bits remainder are written with a random number ++ SRM_CFG_REG_OFST ++ RX_CFG_REG_OFST ++ RX_FILTER_CTL_REG ++ ++ Read Modify Write on full 128bits ++ TXDP_RESERVED_REG (aka TXDP_UNDOCUMENTED) ++ TX_CFG_REG ++ ++*/ ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * DMAQ low-level register interface ++ * ++ *---------------------------------------------------------------------------*/ ++ ++static unsigned dmaq_sizes[] = { ++ 512, ++ EFHW_1K, ++ EFHW_2K, ++ EFHW_4K, ++}; ++ ++#define N_DMAQ_SIZES (sizeof(dmaq_sizes) / sizeof(dmaq_sizes[0])) ++ ++static inline ulong falcon_dma_tx_q_offset(struct efhw_nic *nic, unsigned dmaq) ++{ ++ EFHW_ASSERT(dmaq < nic->num_dmaqs); ++ return TX_DESC_PTR_TBL_OFST + dmaq * FALCON_REGISTER128; ++} ++ ++static inline uint falcon_dma_tx_q_size_index(uint dmaq_size) ++{ ++ uint i; ++ ++ /* size must be one of the various options, otherwise we assert */ ++ for (i = 0; i < N_DMAQ_SIZES; i++) { ++ if (dmaq_size == dmaq_sizes[i]) ++ break; ++ } ++ EFHW_ASSERT(i < N_DMAQ_SIZES); ++ return i; ++} ++ ++static void ++falcon_dmaq_tx_q_init(struct efhw_nic *nic, ++ uint dmaq, uint evq_id, uint own_id, ++ uint tag, uint dmaq_size, uint buf_idx, uint flags) ++{ ++ FALCON_LOCK_DECL; ++ uint index, desc_type; ++ uint64_t val1, val2, val3; ++ ulong offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ /* Q attributes */ ++ int iscsi_hdig_en = ((flags & EFHW_VI_ISCSI_TX_HDIG_EN) != 0); ++ int iscsi_ddig_en = ((flags & EFHW_VI_ISCSI_TX_DDIG_EN) != 0); ++ int csum_ip_dis = ((flags & EFHW_VI_TX_IP_CSUM_DIS) != 0); ++ int csum_tcp_dis = ((flags & EFHW_VI_TX_TCPUDP_CSUM_DIS) != 0); ++ int non_ip_drop_dis = ((flags & EFHW_VI_TX_TCPUDP_ONLY) == 0); ++ ++ /* initialise the TX descriptor queue pointer table */ ++ ++ /* NB physical vs buffer addressing is determined by the Queue ID. */ ++ ++ offset = falcon_dma_tx_q_offset(nic, dmaq); ++ index = falcon_dma_tx_q_size_index(dmaq_size); ++ ++ /* allow VI flag to override this queue's descriptor type */ ++ desc_type = (flags & EFHW_VI_TX_PHYS_ADDR_EN) ? 0 : 1; ++ ++ /* bug9403: It is dangerous to allow buffer-addressed queues to ++ * have owner_id=0. */ ++ EFHW_ASSERT((own_id > 0) || desc_type == 0); ++ ++ /* dword 1 */ ++ __DWCHCK(TX_DESCQ_FLUSH_LBN, TX_DESCQ_FLUSH_WIDTH); ++ __DWCHCK(TX_DESCQ_TYPE_LBN, TX_DESCQ_TYPE_WIDTH); ++ __DWCHCK(TX_DESCQ_SIZE_LBN, TX_DESCQ_SIZE_WIDTH); ++ __DWCHCK(TX_DESCQ_LABEL_LBN, TX_DESCQ_LABEL_WIDTH); ++ __DWCHCK(TX_DESCQ_OWNER_ID_LBN, TX_DESCQ_OWNER_ID_WIDTH); ++ ++ __LWCHK(TX_DESCQ_EVQ_ID_LBN, TX_DESCQ_EVQ_ID_WIDTH); ++ ++ __RANGECHCK(1, TX_DESCQ_FLUSH_WIDTH); ++ __RANGECHCK(desc_type, TX_DESCQ_TYPE_WIDTH); ++ __RANGECHCK(index, TX_DESCQ_SIZE_WIDTH); ++ __RANGECHCK(tag, TX_DESCQ_LABEL_WIDTH); ++ __RANGECHCK(own_id, TX_DESCQ_OWNER_ID_WIDTH); ++ __RANGECHCK(evq_id, TX_DESCQ_EVQ_ID_WIDTH); ++ ++ val1 = ((desc_type << TX_DESCQ_TYPE_LBN) | ++ (index << TX_DESCQ_SIZE_LBN) | ++ (tag << TX_DESCQ_LABEL_LBN) | ++ (own_id << TX_DESCQ_OWNER_ID_LBN) | ++ (__LOW(evq_id, TX_DESCQ_EVQ_ID_LBN, TX_DESCQ_EVQ_ID_WIDTH))); ++ ++ /* dword 2 */ ++ __DW2CHCK(TX_DESCQ_BUF_BASE_ID_LBN, TX_DESCQ_BUF_BASE_ID_WIDTH); ++ __RANGECHCK(buf_idx, TX_DESCQ_BUF_BASE_ID_WIDTH); ++ ++ val2 = ((__HIGH(evq_id, TX_DESCQ_EVQ_ID_LBN, TX_DESCQ_EVQ_ID_WIDTH)) | ++ (buf_idx << __DW2(TX_DESCQ_BUF_BASE_ID_LBN))); ++ ++ /* dword 3 */ ++ __DW3CHCK(TX_ISCSI_HDIG_EN_LBN, TX_ISCSI_HDIG_EN_WIDTH); ++ __DW3CHCK(TX_ISCSI_DDIG_EN_LBN, TX_ISCSI_DDIG_EN_WIDTH); ++ __RANGECHCK(iscsi_hdig_en, TX_ISCSI_HDIG_EN_WIDTH); ++ __RANGECHCK(iscsi_ddig_en, TX_ISCSI_DDIG_EN_WIDTH); ++ ++ val3 = ((iscsi_hdig_en << __DW3(TX_ISCSI_HDIG_EN_LBN)) | ++ (iscsi_ddig_en << __DW3(TX_ISCSI_DDIG_EN_LBN)) | ++ (1 << __DW3(TX_DESCQ_EN_LBN))); /* queue enable bit */ ++ ++ switch (nic->devtype.variant) { ++ case 'B': ++ __DW3CHCK(TX_NON_IP_DROP_DIS_B0_LBN, ++ TX_NON_IP_DROP_DIS_B0_WIDTH); ++ __DW3CHCK(TX_IP_CHKSM_DIS_B0_LBN, TX_IP_CHKSM_DIS_B0_WIDTH); ++ __DW3CHCK(TX_TCP_CHKSM_DIS_B0_LBN, TX_TCP_CHKSM_DIS_B0_WIDTH); ++ ++ val3 |= ((non_ip_drop_dis << __DW3(TX_NON_IP_DROP_DIS_B0_LBN))| ++ (csum_ip_dis << __DW3(TX_IP_CHKSM_DIS_B0_LBN)) | ++ (csum_tcp_dis << __DW3(TX_TCP_CHKSM_DIS_B0_LBN))); ++ break; ++ case 'A': ++ if (csum_ip_dis || csum_tcp_dis || !non_ip_drop_dis) ++ EFHW_WARN ++ ("%s: bad settings for A1 csum_ip_dis=%d " ++ "csum_tcp_dis=%d non_ip_drop_dis=%d", ++ __func__, csum_ip_dis, ++ csum_tcp_dis, non_ip_drop_dis); ++ break; ++ default: ++ EFHW_ASSERT(0); ++ break; ++ } ++ ++ EFHW_TRACE("%s: txq %x evq %u tag %x id %x buf %x " ++ "%x:%x:%x->%" PRIx64 ":%" PRIx64 ":%" PRIx64, ++ __func__, ++ dmaq, evq_id, tag, own_id, buf_idx, dmaq_size, ++ iscsi_hdig_en, iscsi_ddig_en, val1, val2, val3); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + offset, ((val2 << 32) | val1), val3); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ return; ++} ++ ++static inline ulong ++falcon_dma_rx_q_offset(struct efhw_nic *nic, unsigned dmaq) ++{ ++ EFHW_ASSERT(dmaq < nic->num_dmaqs); ++ return RX_DESC_PTR_TBL_OFST + dmaq * FALCON_REGISTER128; ++} ++ ++static void ++falcon_dmaq_rx_q_init(struct efhw_nic *nic, ++ uint dmaq, uint evq_id, uint own_id, ++ uint tag, uint dmaq_size, uint buf_idx, uint flags) ++{ ++ FALCON_LOCK_DECL; ++ uint i, desc_type = 1; ++ uint64_t val1, val2, val3; ++ ulong offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ /* Q attributes */ ++#if BUG5762_WORKAROUND ++ int jumbo = 1; /* Queues must not have mixed types */ ++#else ++ int jumbo = ((flags & EFHW_VI_JUMBO_EN) != 0); ++#endif ++ int iscsi_hdig_en = ((flags & EFHW_VI_ISCSI_RX_HDIG_EN) != 0); ++ int iscsi_ddig_en = ((flags & EFHW_VI_ISCSI_RX_DDIG_EN) != 0); ++ ++ /* initialise the TX descriptor queue pointer table */ ++ offset = falcon_dma_rx_q_offset(nic, dmaq); ++ ++ /* size must be one of the various options, otherwise we assert */ ++ for (i = 0; i < N_DMAQ_SIZES; i++) { ++ if (dmaq_size == dmaq_sizes[i]) ++ break; ++ } ++ EFHW_ASSERT(i < N_DMAQ_SIZES); ++ ++ /* allow VI flag to override this queue's descriptor type */ ++ desc_type = (flags & EFHW_VI_RX_PHYS_ADDR_EN) ? 0 : 1; ++ ++ /* bug9403: It is dangerous to allow buffer-addressed queues to have ++ * owner_id=0 */ ++ EFHW_ASSERT((own_id > 0) || desc_type == 0); ++ ++ /* dword 1 */ ++ __DWCHCK(RX_DESCQ_EN_LBN, RX_DESCQ_EN_WIDTH); ++ __DWCHCK(RX_DESCQ_JUMBO_LBN, RX_DESCQ_JUMBO_WIDTH); ++ __DWCHCK(RX_DESCQ_TYPE_LBN, RX_DESCQ_TYPE_WIDTH); ++ __DWCHCK(RX_DESCQ_SIZE_LBN, RX_DESCQ_SIZE_WIDTH); ++ __DWCHCK(RX_DESCQ_LABEL_LBN, RX_DESCQ_LABEL_WIDTH); ++ __DWCHCK(RX_DESCQ_OWNER_ID_LBN, RX_DESCQ_OWNER_ID_WIDTH); ++ ++ __LWCHK(RX_DESCQ_EVQ_ID_LBN, RX_DESCQ_EVQ_ID_WIDTH); ++ ++ __RANGECHCK(1, RX_DESCQ_EN_WIDTH); ++ __RANGECHCK(jumbo, RX_DESCQ_JUMBO_WIDTH); ++ __RANGECHCK(desc_type, RX_DESCQ_TYPE_WIDTH); ++ __RANGECHCK(i, RX_DESCQ_SIZE_WIDTH); ++ __RANGECHCK(tag, RX_DESCQ_LABEL_WIDTH); ++ __RANGECHCK(own_id, RX_DESCQ_OWNER_ID_WIDTH); ++ __RANGECHCK(evq_id, RX_DESCQ_EVQ_ID_WIDTH); ++ ++ val1 = ((1 << RX_DESCQ_EN_LBN) | ++ (jumbo << RX_DESCQ_JUMBO_LBN) | ++ (desc_type << RX_DESCQ_TYPE_LBN) | ++ (i << RX_DESCQ_SIZE_LBN) | ++ (tag << RX_DESCQ_LABEL_LBN) | ++ (own_id << RX_DESCQ_OWNER_ID_LBN) | ++ (__LOW(evq_id, RX_DESCQ_EVQ_ID_LBN, RX_DESCQ_EVQ_ID_WIDTH))); ++ ++ /* dword 2 */ ++ __DW2CHCK(RX_DESCQ_BUF_BASE_ID_LBN, RX_DESCQ_BUF_BASE_ID_WIDTH); ++ __RANGECHCK(buf_idx, RX_DESCQ_BUF_BASE_ID_WIDTH); ++ ++ val2 = ((__HIGH(evq_id, RX_DESCQ_EVQ_ID_LBN, RX_DESCQ_EVQ_ID_WIDTH)) | ++ (buf_idx << __DW2(RX_DESCQ_BUF_BASE_ID_LBN))); ++ ++ /* dword 3 */ ++ __DW3CHCK(RX_ISCSI_HDIG_EN_LBN, RX_ISCSI_HDIG_EN_WIDTH); ++ __DW3CHCK(RX_ISCSI_DDIG_EN_LBN, RX_ISCSI_DDIG_EN_WIDTH); ++ __RANGECHCK(iscsi_hdig_en, RX_ISCSI_HDIG_EN_WIDTH); ++ __RANGECHCK(iscsi_ddig_en, RX_ISCSI_DDIG_EN_WIDTH); ++ ++ val3 = (iscsi_hdig_en << __DW3(RX_ISCSI_HDIG_EN_LBN)) | ++ (iscsi_ddig_en << __DW3(RX_ISCSI_DDIG_EN_LBN)); ++ ++ EFHW_TRACE("%s: rxq %x evq %u tag %x id %x buf %x %s " ++ "%x:%x:%x -> %" PRIx64 ":%" PRIx64 ":%" PRIx64, ++ __func__, ++ dmaq, evq_id, tag, own_id, buf_idx, ++ jumbo ? "jumbo" : "normal", dmaq_size, ++ iscsi_hdig_en, iscsi_ddig_en, val1, val2, val3); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + offset, ((val2 << 32) | val1), val3); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ return; ++} ++ ++static void falcon_dmaq_tx_q_disable(struct efhw_nic *nic, uint dmaq) ++{ ++ FALCON_LOCK_DECL; ++ uint64_t val1, val2, val3; ++ ulong offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ /* initialise the TX descriptor queue pointer table */ ++ ++ offset = falcon_dma_tx_q_offset(nic, dmaq); ++ ++ /* dword 1 */ ++ __DWCHCK(TX_DESCQ_TYPE_LBN, TX_DESCQ_TYPE_WIDTH); ++ ++ val1 = ((uint64_t) 1 << TX_DESCQ_TYPE_LBN); ++ ++ /* dword 2 */ ++ val2 = 0; ++ ++ /* dword 3 */ ++ val3 = (0 << __DW3(TX_DESCQ_EN_LBN)); /* queue enable bit */ ++ ++ EFHW_TRACE("%s: %x->%" PRIx64 ":%" PRIx64 ":%" PRIx64, ++ __func__, dmaq, val1, val2, val3); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + offset, ((val2 << 32) | val1), val3); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ return; ++} ++ ++static void falcon_dmaq_rx_q_disable(struct efhw_nic *nic, uint dmaq) ++{ ++ FALCON_LOCK_DECL; ++ uint64_t val1, val2, val3; ++ ulong offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ /* initialise the TX descriptor queue pointer table */ ++ offset = falcon_dma_rx_q_offset(nic, dmaq); ++ ++ /* dword 1 */ ++ __DWCHCK(RX_DESCQ_EN_LBN, RX_DESCQ_EN_WIDTH); ++ __DWCHCK(RX_DESCQ_TYPE_LBN, RX_DESCQ_TYPE_WIDTH); ++ ++ val1 = ((0 << RX_DESCQ_EN_LBN) | (1 << RX_DESCQ_TYPE_LBN)); ++ ++ /* dword 2 */ ++ val2 = 0; ++ ++ /* dword 3 */ ++ val3 = 0; ++ ++ EFHW_TRACE("falcon_dmaq_rx_q_disable: %x->%" ++ PRIx64 ":%" PRIx64 ":%" PRIx64, ++ dmaq, val1, val2, val3); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + offset, ((val2 << 32) | val1), val3); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ return; ++} ++ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Buffer Table low-level register interface ++ * ++ *---------------------------------------------------------------------------*/ ++ ++/*! Convert a (potentially) 64-bit physical address to 32-bits. Every use ++** of this function is a place where we're not 64-bit clean. ++*/ ++static inline uint32_t dma_addr_to_u32(dma_addr_t addr) ++{ ++ /* Top bits had better be zero! */ ++ EFHW_ASSERT(addr == (addr & 0xffffffff)); ++ return (uint32_t) addr; ++} ++ ++static inline uint32_t ++falcon_nic_buffer_table_entry32_mk(dma_addr_t dma_addr, int own_id) ++{ ++ uint32_t dma_addr32 = FALCON_BUFFER_4K_PAGE(dma_addr_to_u32(dma_addr)); ++ ++ /* don't do this to me */ ++ EFHW_BUILD_ASSERT(BUF_ADR_HBUF_ODD_LBN == BUF_ADR_HBUF_EVEN_LBN + 32); ++ EFHW_BUILD_ASSERT(BUF_OWNER_ID_HBUF_ODD_LBN == ++ BUF_OWNER_ID_HBUF_EVEN_LBN + 32); ++ ++ EFHW_BUILD_ASSERT(BUF_OWNER_ID_HBUF_ODD_WIDTH == ++ BUF_OWNER_ID_HBUF_EVEN_WIDTH); ++ EFHW_BUILD_ASSERT(BUF_ADR_HBUF_ODD_WIDTH == BUF_ADR_HBUF_EVEN_WIDTH); ++ ++ __DWCHCK(BUF_ADR_HBUF_EVEN_LBN, BUF_ADR_HBUF_EVEN_WIDTH); ++ __DWCHCK(BUF_OWNER_ID_HBUF_EVEN_LBN, BUF_OWNER_ID_HBUF_EVEN_WIDTH); ++ ++ __RANGECHCK(dma_addr32, BUF_ADR_HBUF_EVEN_WIDTH); ++ __RANGECHCK(own_id, BUF_OWNER_ID_HBUF_EVEN_WIDTH); ++ ++ return (dma_addr32 << BUF_ADR_HBUF_EVEN_LBN) | ++ (own_id << BUF_OWNER_ID_HBUF_EVEN_LBN); ++} ++ ++static inline uint64_t ++falcon_nic_buffer_table_entry64_mk(dma_addr_t dma_addr, ++ int bufsz, /* bytes */ ++ int region, int own_id) ++{ ++ __DW2CHCK(IP_DAT_BUF_SIZE_LBN, IP_DAT_BUF_SIZE_WIDTH); ++ __DW2CHCK(BUF_ADR_REGION_LBN, BUF_ADR_REGION_WIDTH); ++ __LWCHK(BUF_ADR_FBUF_LBN, BUF_ADR_FBUF_WIDTH); ++ __DWCHCK(BUF_OWNER_ID_FBUF_LBN, BUF_OWNER_ID_FBUF_WIDTH); ++ ++ EFHW_ASSERT((bufsz == EFHW_4K) || (bufsz == EFHW_8K)); ++ ++ dma_addr = (dma_addr >> 12) & __FALCON_MASK64(BUF_ADR_FBUF_WIDTH); ++ ++ __RANGECHCK(dma_addr, BUF_ADR_FBUF_WIDTH); ++ __RANGECHCK(1, IP_DAT_BUF_SIZE_WIDTH); ++ __RANGECHCK(region, BUF_ADR_REGION_WIDTH); ++ __RANGECHCK(own_id, BUF_OWNER_ID_FBUF_WIDTH); ++ ++ return ((uint64_t) (bufsz == EFHW_8K) << IP_DAT_BUF_SIZE_LBN) | ++ ((uint64_t) region << BUF_ADR_REGION_LBN) | ++ ((uint64_t) dma_addr << BUF_ADR_FBUF_LBN) | ++ ((uint64_t) own_id << BUF_OWNER_ID_FBUF_LBN); ++} ++ ++static inline void ++_falcon_nic_buffer_table_set32(struct efhw_nic *nic, ++ dma_addr_t dma_addr, uint bufsz, ++ uint region, /* not used */ ++ int own_id, int buffer_id) ++{ ++ /* programming the half table needs to be done in pairs. */ ++ uint64_t entry, val, shift; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ volatile char __iomem *offset; ++ ++ EFHW_BUILD_ASSERT(BUF_ADR_HBUF_ODD_LBN == BUF_ADR_HBUF_EVEN_LBN + 32); ++ EFHW_BUILD_ASSERT(BUF_OWNER_ID_HBUF_ODD_LBN == ++ BUF_OWNER_ID_HBUF_EVEN_LBN + 32); ++ ++ shift = (buffer_id & 1) ? 32 : 0; ++ ++ offset = (efhw_kva + BUF_HALF_TBL_OFST + ++ ((buffer_id & ~1) * FALCON_BUFFER_TBL_HALF_BYTES)); ++ ++ entry = falcon_nic_buffer_table_entry32_mk(dma_addr_to_u32(dma_addr), ++ own_id); ++ ++#if FALCON_USE_SHADOW_BUFFER_TABLE ++ val = _falcon_buffer_table[buffer_id & ~1]; ++#else ++ /* This will not work unless we've completed ++ * the buffer table updates */ ++ falcon_read_q(offset, &val); ++#endif ++ val &= ~(((uint64_t) 0xffffffff) << shift); ++ val |= (entry << shift); ++ ++ EFHW_TRACE("%s[%x]: %lx:%x:%" PRIx64 "->%x = %" ++ PRIx64, __func__, buffer_id, (unsigned long) dma_addr, ++ own_id, entry, (unsigned)(offset - efhw_kva), val); ++ ++ /* Falcon requires that access to this register is serialised */ ++ falcon_write_q(offset, val); ++ ++ /* NB. No mmiowb(). Caller should do that e.g by calling commit */ ++ ++#if FALCON_USE_SHADOW_BUFFER_TABLE ++ _falcon_buffer_table[buffer_id & ~1] = val; ++#endif ++ ++ /* Confirm the entry if the event queues haven't been set up. */ ++ if (!nic->irq_handler) { ++ uint64_t new_val; ++ int count = 0; ++ while (1) { ++ mmiowb(); ++ falcon_read_q(offset, &new_val); ++ if (new_val == val) ++ break; ++ count++; ++ if (count > 1000) { ++ EFHW_WARN("%s: poll Timeout", __func__); ++ break; ++ } ++ udelay(1); ++ } ++ } ++} ++ ++static inline void ++_falcon_nic_buffer_table_set64(struct efhw_nic *nic, ++ dma_addr_t dma_addr, uint bufsz, ++ uint region, int own_id, int buffer_id) ++{ ++ volatile char __iomem *offset; ++ uint64_t entry; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ EFHW_ASSERT(region < FALCON_REGION_NUM); ++ ++ EFHW_ASSERT((bufsz == EFHW_4K) || ++ (bufsz == EFHW_8K && FALCON_BUFFER_TABLE_FULL_MODE)); ++ ++ offset = (efhw_kva + BUF_FULL_TBL_OFST + ++ (buffer_id * FALCON_BUFFER_TBL_FULL_BYTES)); ++ ++ entry = falcon_nic_buffer_table_entry64_mk(dma_addr, bufsz, region, ++ own_id); ++ ++ EFHW_TRACE("%s[%x]: %lx:bufsz=%x:region=%x:ownid=%x", ++ __func__, buffer_id, (unsigned long) dma_addr, bufsz, ++ region, own_id); ++ ++ EFHW_TRACE("%s: BUF[%x]:NIC[%x]->%" PRIx64, ++ __func__, buffer_id, ++ (unsigned int)(offset - efhw_kva), entry); ++ ++ /* Falcon requires that access to this register is serialised */ ++ falcon_write_q(offset, entry); ++ ++ /* NB. No mmiowb(). Caller should do that e.g by calling commit */ ++ ++ /* Confirm the entry if the event queues haven't been set up. */ ++ if (!nic->irq_handler) { ++ uint64_t new_entry; ++ int count = 0; ++ while (1) { ++ mmiowb(); ++ falcon_read_q(offset, &new_entry); ++ if (new_entry == entry) ++ return; ++ count++; ++ if (count > 1000) { ++ EFHW_WARN("%s: poll Timeout waiting for " ++ "value %"PRIx64 ++ " (last was %"PRIx64")", ++ __func__, entry, new_entry); ++ break; ++ } ++ udelay(1); ++ } ++ } ++} ++ ++#if FALCON_BUFFER_TABLE_FULL_MODE ++#define _falcon_nic_buffer_table_set _falcon_nic_buffer_table_set64 ++#else ++#define _falcon_nic_buffer_table_set _falcon_nic_buffer_table_set32 ++#endif ++ ++static inline void _falcon_nic_buffer_table_commit(struct efhw_nic *nic) ++{ ++ /* MUST be called holding the FALCON_LOCK */ ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ uint64_t cmd; ++ ++ EFHW_BUILD_ASSERT(BUF_TBL_UPD_REG_KER_OFST == BUF_TBL_UPD_REG_OFST); ++ ++ __DW2CHCK(BUF_UPD_CMD_LBN, BUF_UPD_CMD_WIDTH); ++ __RANGECHCK(1, BUF_UPD_CMD_WIDTH); ++ ++ cmd = ((uint64_t) 1 << BUF_UPD_CMD_LBN); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ falcon_write_qq(efhw_kva + BUF_TBL_UPD_REG_OFST, ++ cmd, FALCON_ATOMIC_UPD_REG); ++ mmiowb(); ++ ++ nic->buf_commit_outstanding++; ++ EFHW_TRACE("COMMIT REQ out=%d", nic->buf_commit_outstanding); ++} ++ ++static void falcon_nic_buffer_table_commit(struct efhw_nic *nic) ++{ ++ /* nothing to do */ ++} ++ ++static inline void ++_falcon_nic_buffer_table_clear(struct efhw_nic *nic, int buffer_id, int num) ++{ ++ uint64_t cmd; ++ uint64_t start_id = buffer_id; ++ uint64_t end_id = buffer_id + num - 1; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ volatile char __iomem *offset = (efhw_kva + BUF_TBL_UPD_REG_OFST); ++ ++ EFHW_BUILD_ASSERT(BUF_TBL_UPD_REG_KER_OFST == BUF_TBL_UPD_REG_OFST); ++ ++#if !FALCON_BUFFER_TABLE_FULL_MODE ++ /* buffer_ids in half buffer mode reference pairs of buffers */ ++ EFHW_ASSERT(buffer_id % 1 == 0); ++ EFHW_ASSERT(num % 1 == 0); ++ start_id = start_id >> 1; ++ end_id = end_id >> 1; ++#endif ++ ++ EFHW_ASSERT(num >= 1); ++ ++ __DWCHCK(BUF_CLR_START_ID_LBN, BUF_CLR_START_ID_WIDTH); ++ __DW2CHCK(BUF_CLR_END_ID_LBN, BUF_CLR_END_ID_WIDTH); ++ ++ __DW2CHCK(BUF_CLR_CMD_LBN, BUF_CLR_CMD_WIDTH); ++ __RANGECHCK(1, BUF_CLR_CMD_WIDTH); ++ ++ __RANGECHCK(start_id, BUF_CLR_START_ID_WIDTH); ++ __RANGECHCK(end_id, BUF_CLR_END_ID_WIDTH); ++ ++ cmd = (((uint64_t) 1 << BUF_CLR_CMD_LBN) | ++ (start_id << BUF_CLR_START_ID_LBN) | ++ (end_id << BUF_CLR_END_ID_LBN)); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ falcon_write_qq(offset, cmd, FALCON_ATOMIC_UPD_REG); ++ mmiowb(); ++ ++ nic->buf_commit_outstanding++; ++ EFHW_TRACE("COMMIT CLEAR out=%d", nic->buf_commit_outstanding); ++} ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Events low-level register interface ++ * ++ *---------------------------------------------------------------------------*/ ++ ++static unsigned eventq_sizes[] = { ++ 512, ++ EFHW_1K, ++ EFHW_2K, ++ EFHW_4K, ++ EFHW_8K, ++ EFHW_16K, ++ EFHW_32K ++}; ++ ++#define N_EVENTQ_SIZES (sizeof(eventq_sizes) / sizeof(eventq_sizes[0])) ++ ++static inline void falcon_nic_srm_upd_evq(struct efhw_nic *nic, int evq) ++{ ++ /* set up the eventq which will receive events from the SRAM module. ++ * i.e buffer table updates and clears, TX and RX aperture table ++ * updates */ ++ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ EFHW_BUILD_ASSERT(SRM_UPD_EVQ_REG_OFST == SRM_UPD_EVQ_REG_KER_OFST); ++ ++ __DWCHCK(SRM_UPD_EVQ_ID_LBN, SRM_UPD_EVQ_ID_WIDTH); ++ __RANGECHCK(evq, SRM_UPD_EVQ_ID_WIDTH); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + SRM_UPD_EVQ_REG_OFST, ++ ((uint64_t) evq << SRM_UPD_EVQ_ID_LBN), ++ FALCON_ATOMIC_SRPM_UDP_EVQ_REG); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++static void ++falcon_nic_evq_ptr_tbl(struct efhw_nic *nic, ++ uint evq, /* evq id */ ++ uint enable, /* 1 to enable, 0 to disable */ ++ uint buf_base_id,/* Buffer table base for EVQ */ ++ uint evq_size /* Number of events */) ++{ ++ FALCON_LOCK_DECL; ++ uint i, val; ++ ulong offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ /* size must be one of the various options, otherwise we assert */ ++ for (i = 0; i < N_EVENTQ_SIZES; i++) { ++ if (evq_size <= eventq_sizes[i]) ++ break; ++ } ++ EFHW_ASSERT(i < N_EVENTQ_SIZES); ++ ++ __DWCHCK(EVQ_BUF_BASE_ID_LBN, EVQ_BUF_BASE_ID_WIDTH); ++ __DWCHCK(EVQ_SIZE_LBN, EVQ_SIZE_WIDTH); ++ __DWCHCK(EVQ_EN_LBN, EVQ_EN_WIDTH); ++ ++ __RANGECHCK(i, EVQ_SIZE_WIDTH); ++ __RANGECHCK(buf_base_id, EVQ_BUF_BASE_ID_WIDTH); ++ __RANGECHCK(1, EVQ_EN_WIDTH); ++ ++ /* if !enable then only evq needs to be correct, although valid ++ * values need to be passed in for other arguments to prevent ++ * assertions */ ++ ++ val = ((i << EVQ_SIZE_LBN) | (buf_base_id << EVQ_BUF_BASE_ID_LBN) | ++ (enable ? (1 << EVQ_EN_LBN) : 0)); ++ ++ EFHW_ASSERT(evq < nic->num_evqs); ++ ++ offset = EVQ_PTR_TBL_CHAR_OFST; ++ offset += evq * FALCON_REGISTER128; ++ ++ EFHW_TRACE("%s: evq %u en=%x:buf=%x:size=%x->%x at %lx", ++ __func__, evq, enable, buf_base_id, evq_size, val, ++ offset); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + offset, val, FALCON_ATOMIC_PTR_TBL_REG); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ ++ /* caller must wait for an update done event before writing any more ++ table entries */ ++ ++ return; ++} ++ ++void ++falcon_nic_evq_ack(struct efhw_nic *nic, ++ uint evq, /* evq id */ ++ uint rptr, /* new read pointer update */ ++ bool wakeup /* request a wakeup event if ptr's != */ ++ ) ++{ ++ uint val; ++ ulong offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ EFHW_BUILD_ASSERT(FALCON_EVQ_CHAR == 4); ++ ++ __DWCHCK(EVQ_RPTR_LBN, EVQ_RPTR_WIDTH); ++ __RANGECHCK(rptr, EVQ_RPTR_WIDTH); ++ ++ val = (rptr << EVQ_RPTR_LBN); ++ ++ EFHW_ASSERT(evq < nic->num_evqs); ++ ++ if (evq < FALCON_EVQ_CHAR) { ++ offset = EVQ_RPTR_REG_KER_OFST; ++ offset += evq * FALCON_REGISTER128; ++ ++ EFHW_ASSERT(!wakeup); /* don't try this at home */ ++ } else { ++ offset = EVQ_RPTR_REG_OFST + (FALCON_EVQ_CHAR * ++ FALCON_REGISTER128); ++ offset += (evq - FALCON_EVQ_CHAR) * FALCON_REGISTER128; ++ ++ /* nothing to do for interruptless event queues which do ++ * not want a wakeup */ ++ if (evq != FALCON_EVQ_CHAR && !wakeup) ++ return; ++ } ++ ++ EFHW_TRACE("%s: %x %x %x->%x", __func__, evq, rptr, wakeup, val); ++ ++ writel(val, efhw_kva + offset); ++ mmiowb(); ++} ++ ++/*---------------------------------------------------------------------------*/ ++ ++static inline void ++falcon_drv_ev(struct efhw_nic *nic, uint64_t data, uint qid) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ /* send an event from one driver to the other */ ++ EFHW_BUILD_ASSERT(DRV_EV_REG_KER_OFST == DRV_EV_REG_OFST); ++ EFHW_BUILD_ASSERT(DRV_EV_DATA_LBN == 0); ++ EFHW_BUILD_ASSERT(DRV_EV_DATA_WIDTH == 64); ++ EFHW_BUILD_ASSERT(DRV_EV_QID_LBN == 64); ++ EFHW_BUILD_ASSERT(DRV_EV_QID_WIDTH == 12); ++ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + DRV_EV_REG_OFST, data, qid); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++_DEBUG_SYM_ void ++falcon_ab_timer_tbl_set(struct efhw_nic *nic, ++ uint evq, /* timer id */ ++ uint mode, /* mode bits */ ++ uint countdown /* counting value to set */) ++{ ++ FALCON_LOCK_DECL; ++ uint val; ++ ulong offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ EFHW_BUILD_ASSERT(TIMER_VAL_LBN == 0); ++ ++ __DWCHCK(TIMER_MODE_LBN, TIMER_MODE_WIDTH); ++ __DWCHCK(TIMER_VAL_LBN, TIMER_VAL_WIDTH); ++ ++ __RANGECHCK(mode, TIMER_MODE_WIDTH); ++ __RANGECHCK(countdown, TIMER_VAL_WIDTH); ++ ++ val = ((mode << TIMER_MODE_LBN) | (countdown << TIMER_VAL_LBN)); ++ ++ if (evq < FALCON_EVQ_CHAR) { ++ offset = TIMER_CMD_REG_KER_OFST; ++ offset += evq * EFHW_8K; /* PAGE mapped register */ ++ } else { ++ offset = TIMER_TBL_OFST; ++ offset += evq * FALCON_REGISTER128; ++ } ++ EFHW_ASSERT(evq < nic->num_evqs); ++ ++ EFHW_TRACE("%s: evq %u mode %x (%s) time %x -> %08x", ++ __func__, evq, mode, ++ mode == 0 ? "DISABLE" : ++ mode == 1 ? "IMMED" : ++ mode == 2 ? (evq < 5 ? "HOLDOFF" : "RX_TRIG") : ++ "", countdown, val); ++ ++ /* Falcon requires 128 bit atomic access for this register when ++ * accessed from the driver. User access to timers is paged mapped ++ */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + offset, val, FALCON_ATOMIC_TIMER_CMD_REG); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ return; ++} ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * Rate pacing - Low level interface ++ * ++ *--------------------------------------------------------------------*/ ++void falcon_nic_pace(struct efhw_nic *nic, uint dmaq, uint pace) ++{ ++ /* Pace specified in 2^(units of microseconds). This is the minimum ++ additional delay imposed over and above the IPG. ++ ++ Pacing only available on the virtual interfaces ++ */ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ulong offset; ++ ++ if (pace > 20) ++ pace = 20; /* maxm supported value */ ++ ++ __DWCHCK(TX_PACE_LBN, TX_PACE_WIDTH); ++ __RANGECHCK(pace, TX_PACE_WIDTH); ++ ++ switch (nic->devtype.variant) { ++ case 'A': ++ EFHW_ASSERT(dmaq >= TX_PACE_TBL_FIRST_QUEUE_A1); ++ offset = TX_PACE_TBL_A1_OFST; ++ offset += (dmaq - TX_PACE_TBL_FIRST_QUEUE_A1) * 16; ++ break; ++ case 'B': ++ /* Would be nice to assert this, but as dmaq is unsigned and ++ * TX_PACE_TBL_FIRST_QUEUE_B0 is 0, it makes no sense ++ * EFHW_ASSERT(dmaq >= TX_PACE_TBL_FIRST_QUEUE_B0); ++ */ ++ offset = TX_PACE_TBL_B0_OFST; ++ offset += (dmaq - TX_PACE_TBL_FIRST_QUEUE_B0) * 16; ++ break; ++ default: ++ EFHW_ASSERT(0); ++ offset = 0; ++ break; ++ } ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + offset, pace, FALCON_ATOMIC_PACE_REG); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ ++ EFHW_TRACE("%s: txq %d offset=%lx pace=2^%x", ++ __func__, dmaq, offset, pace); ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * Interrupt - Low level interface ++ * ++ *--------------------------------------------------------------------*/ ++ ++static void falcon_nic_handle_fatal_int(struct efhw_nic *nic) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ uint64_t val; ++ ++ offset = (efhw_kva + FATAL_INTR_REG_OFST); ++ ++ /* Falcon requires 32 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ val = readl(offset); ++ FALCON_LOCK_UNLOCK(nic); ++ ++ /* ?? BUG3249 - need to disable illegal address interrupt */ ++ /* ?? BUG3114 - need to backport interrupt storm protection code */ ++ EFHW_ERR("fatal interrupt: %s%s%s%s%s%s%s%s%s%s%s%s[%" PRIx64 "]", ++ val & (1 << PCI_BUSERR_INT_CHAR_LBN) ? "PCI-bus-error " : "", ++ val & (1 << SRAM_OOB_INT_CHAR_LBN) ? "SRAM-oob " : "", ++ val & (1 << BUFID_OOB_INT_CHAR_LBN) ? "bufid-oob " : "", ++ val & (1 << MEM_PERR_INT_CHAR_LBN) ? "int-parity " : "", ++ val & (1 << RBUF_OWN_INT_CHAR_LBN) ? "rx-bufid-own " : "", ++ val & (1 << TBUF_OWN_INT_CHAR_LBN) ? "tx-bufid-own " : "", ++ val & (1 << RDESCQ_OWN_INT_CHAR_LBN) ? "rx-desc-own " : "", ++ val & (1 << TDESCQ_OWN_INT_CHAR_LBN) ? "tx-desc-own " : "", ++ val & (1 << EVQ_OWN_INT_CHAR_LBN) ? "evq-own " : "", ++ val & (1 << EVFF_OFLO_INT_CHAR_LBN) ? "evq-fifo " : "", ++ val & (1 << ILL_ADR_INT_CHAR_LBN) ? "ill-addr " : "", ++ val & (1 << SRM_PERR_INT_CHAR_LBN) ? "sram-parity " : "", val); ++} ++ ++static void falcon_nic_interrupt_hw_enable(struct efhw_nic *nic) ++{ ++ FALCON_LOCK_DECL; ++ uint val; ++ volatile char __iomem *offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ EFHW_BUILD_ASSERT(DRV_INT_EN_CHAR_WIDTH == 1); ++ ++ if (nic->flags & NIC_FLAG_NO_INTERRUPT) ++ return; ++ ++ offset = (efhw_kva + INT_EN_REG_CHAR_OFST); ++ val = 1 << DRV_INT_EN_CHAR_LBN; ++ ++ EFHW_NOTICE("%s: %x -> %x", __func__, (int)(offset - efhw_kva), ++ val); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(offset, val, FALCON_ATOMIC_INT_EN_REG); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++static void falcon_nic_interrupt_hw_disable(struct efhw_nic *nic) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ EFHW_BUILD_ASSERT(SRAM_PERR_INT_KER_WIDTH == 1); ++ EFHW_BUILD_ASSERT(DRV_INT_EN_KER_LBN == 0); ++ EFHW_BUILD_ASSERT(SRAM_PERR_INT_CHAR_WIDTH == 1); ++ EFHW_BUILD_ASSERT(DRV_INT_EN_CHAR_LBN == 0); ++ EFHW_BUILD_ASSERT(SRAM_PERR_INT_KER_LBN == SRAM_PERR_INT_CHAR_LBN); ++ EFHW_BUILD_ASSERT(DRV_INT_EN_KER_LBN == DRV_INT_EN_CHAR_LBN); ++ ++ if (nic->flags & NIC_FLAG_NO_INTERRUPT) ++ return; ++ ++ offset = (efhw_kva + INT_EN_REG_CHAR_OFST); ++ ++ EFHW_NOTICE("%s: %x -> 0", __func__, (int)(offset - efhw_kva)); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(offset, 0, FALCON_ATOMIC_INT_EN_REG); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++static void falcon_nic_irq_addr_set(struct efhw_nic *nic, dma_addr_t dma_addr) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *offset; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ++ offset = (efhw_kva + INT_ADR_REG_CHAR_OFST); ++ ++ EFHW_NOTICE("%s: %x -> " DMA_ADDR_T_FMT, __func__, ++ (int)(offset - efhw_kva), dma_addr); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(offset, dma_addr, FALCON_ATOMIC_INT_ADR_REG); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * RXDP - low level interface ++ * ++ *--------------------------------------------------------------------*/ ++ ++void ++falcon_nic_set_rx_usr_buf_size(struct efhw_nic *nic, int usr_buf_bytes) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ uint64_t val, val2, usr_buf_size = usr_buf_bytes / 32; ++ int rubs_lbn, rubs_width, roec_lbn; ++ ++ EFHW_BUILD_ASSERT(RX_CFG_REG_OFST == RX_CFG_REG_KER_OFST); ++ ++ switch (nic->devtype.variant) { ++ default: ++ EFHW_ASSERT(0); ++ /* Fall-through to avoid compiler warnings. */ ++ case 'A': ++ rubs_lbn = RX_USR_BUF_SIZE_A1_LBN; ++ rubs_width = RX_USR_BUF_SIZE_A1_WIDTH; ++ roec_lbn = RX_OWNERR_CTL_A1_LBN; ++ break; ++ case 'B': ++ rubs_lbn = RX_USR_BUF_SIZE_B0_LBN; ++ rubs_width = RX_USR_BUF_SIZE_B0_WIDTH; ++ roec_lbn = RX_OWNERR_CTL_B0_LBN; ++ break; ++ } ++ ++ __DWCHCK(rubs_lbn, rubs_width); ++ __QWCHCK(roec_lbn, 1); ++ __RANGECHCK(usr_buf_size, rubs_width); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_read_qq(efhw_kva + RX_CFG_REG_OFST, &val, &val2); ++ ++ val &= ~((__FALCON_MASK64(rubs_width)) << rubs_lbn); ++ val |= (usr_buf_size << rubs_lbn); ++ ++ /* shouldn't be needed for a production driver */ ++ val |= ((uint64_t) 1 << roec_lbn); ++ ++ falcon_write_qq(efhw_kva + RX_CFG_REG_OFST, val, val2); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++EXPORT_SYMBOL(falcon_nic_set_rx_usr_buf_size); ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * TXDP - low level interface ++ * ++ *--------------------------------------------------------------------*/ ++ ++_DEBUG_SYM_ void falcon_nic_tx_cfg(struct efhw_nic *nic, int unlocked) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ uint64_t val1, val2; ++ ++ EFHW_BUILD_ASSERT(TX_CFG_REG_OFST == TX_CFG_REG_KER_OFST); ++ __DWCHCK(TX_OWNERR_CTL_LBN, TX_OWNERR_CTL_WIDTH); ++ __DWCHCK(TX_NON_IP_DROP_DIS_LBN, TX_NON_IP_DROP_DIS_WIDTH); ++ ++ FALCON_LOCK_LOCK(nic); ++ falcon_read_qq(efhw_kva + TX_CFG_REG_OFST, &val1, &val2); ++ ++ /* Will flag fatal interrupts on owner id errors. This should not be ++ on for production code because there is otherwise a denial of ++ serivce attack possible */ ++ val1 |= (1 << TX_OWNERR_CTL_LBN); ++ ++ /* Setup user queue TCP/UDP only packet security */ ++ if (unlocked) ++ val1 |= (1 << TX_NON_IP_DROP_DIS_LBN); ++ else ++ val1 &= ~(1 << TX_NON_IP_DROP_DIS_LBN); ++ ++ falcon_write_qq(efhw_kva + TX_CFG_REG_OFST, val1, val2); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * Random thresholds - Low level interface (Would like these to be op ++ * defaults wherever possible) ++ * ++ *--------------------------------------------------------------------*/ ++ ++void falcon_nic_pace_cfg(struct efhw_nic *nic, int fb_base, int bin_thresh) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ unsigned offset = 0; ++ uint64_t val; ++ ++ __DWCHCK(TX_PACE_FB_BASE_LBN, TX_PACE_FB_BASE_WIDTH); ++ __DWCHCK(TX_PACE_BIN_TH_LBN, TX_PACE_BIN_TH_WIDTH); ++ ++ switch (nic->devtype.variant) { ++ case 'A': offset = TX_PACE_REG_A1_OFST; break; ++ case 'B': offset = TX_PACE_REG_B0_OFST; break; ++ default: EFHW_ASSERT(0); break; ++ } ++ ++ val = (0x15 << TX_PACE_SB_NOTAF_LBN); ++ val |= (0xb << TX_PACE_SB_AF_LBN); ++ ++ val |= ((fb_base & __FALCON_MASK64(TX_PACE_FB_BASE_WIDTH)) << ++ TX_PACE_FB_BASE_LBN); ++ val |= ((bin_thresh & __FALCON_MASK64(TX_PACE_BIN_TH_WIDTH)) << ++ TX_PACE_BIN_TH_LBN); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + offset, val, 0); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++ ++/********************************************************************** ++ * Implementation of the HAL. ******************************************** ++ **********************************************************************/ ++ ++/*---------------------------------------------------------------------------- ++ * ++ * Initialisation and configuration discovery ++ * ++ *---------------------------------------------------------------------------*/ ++ ++static int falcon_nic_init_irq_channel(struct efhw_nic *nic, int enable) ++{ ++ /* create a buffer for the irq channel */ ++ int rc; ++ ++ if (enable) { ++ rc = efhw_iopage_alloc(nic, &nic->irq_iobuff); ++ if (rc < 0) ++ return rc; ++ ++ falcon_nic_irq_addr_set(nic, ++ efhw_iopage_dma_addr(&nic->irq_iobuff)); ++ } else { ++ if (efhw_iopage_is_valid(&nic->irq_iobuff)) ++ efhw_iopage_free(nic, &nic->irq_iobuff); ++ ++ efhw_iopage_mark_invalid(&nic->irq_iobuff); ++ falcon_nic_irq_addr_set(nic, 0); ++ } ++ ++ EFHW_TRACE("%s: %lx %sable", __func__, ++ (unsigned long) efhw_iopage_dma_addr(&nic->irq_iobuff), ++ enable ? "en" : "dis"); ++ ++ return 0; ++} ++ ++static void falcon_nic_close_hardware(struct efhw_nic *nic) ++{ ++ /* check we are in possession of some hardware */ ++ if (!efhw_nic_have_hw(nic)) ++ return; ++ ++ falcon_nic_init_irq_channel(nic, 0); ++ falcon_nic_filter_dtor(nic); ++ ++ EFHW_NOTICE("%s:", __func__); ++} ++ ++static int ++falcon_nic_init_hardware(struct efhw_nic *nic, ++ struct efhw_ev_handler *ev_handlers, ++ const uint8_t *mac_addr, int non_irq_evq) ++{ ++ int rc; ++ ++ /* header sanity checks */ ++ FALCON_ASSERT_VALID(); ++ ++ /* Initialise supporting modules */ ++ rc = falcon_nic_filter_ctor(nic); ++ if (rc < 0) ++ return rc; ++ ++#if FALCON_USE_SHADOW_BUFFER_TABLE ++ CI_ZERO_ARRAY(_falcon_buffer_table, FALCON_BUFFER_TBL_NUM); ++#endif ++ ++ /* Initialise the top level hardware blocks */ ++ memcpy(nic->mac_addr, mac_addr, ETH_ALEN); ++ ++ EFHW_TRACE("%s:", __func__); ++ ++ /* nic.c:efhw_nic_init marks all the interrupt units as unused. ++ ++ ?? TODO we should be able to request the non-interrupting event ++ queue and the net driver's (for a net driver that is using libefhw) ++ additional RSS queues here. ++ ++ Result would be that that net driver could call ++ nic.c:efhw_nic_allocate_common_hardware_resources() and that the ++ IFDEF FALCON's can be removed from ++ nic.c:efhw_nic_allocate_common_hardware_resources() ++ */ ++ nic->irq_unit = INT_EN_REG_CHAR_OFST; ++ ++ /***************************************************************** ++ * The rest of this function deals with initialization of the NICs ++ * hardware (as opposed to the initialization of the ++ * struct efhw_nic data structure */ ++ ++ /* char driver grabs SRM events onto the non interrupting ++ * event queue */ ++ falcon_nic_srm_upd_evq(nic, non_irq_evq); ++ ++ /* RXDP tweaks */ ++ ++ /* ?? bug2396 rx_cfg should be ok so long as the net driver ++ * always pushes buffers big enough for the link MTU */ ++ ++ /* set the RX buffer cutoff size to be the same as PAGE_SIZE. ++ * Use this value when we think that there will be a lot of ++ * jumbo frames. ++ * ++ * The default value 1600 is useful when packets are small, ++ * but would means that jumbo frame RX queues would need more ++ * descriptors pushing */ ++ falcon_nic_set_rx_usr_buf_size(nic, FALCON_RX_USR_BUF_SIZE); ++ ++ /* TXDP tweaks */ ++ /* ?? bug2396 looks ok */ ++ falcon_nic_tx_cfg(nic, /*unlocked(for non-UDP/TCP)= */ 0); ++ falcon_nic_pace_cfg(nic, 4, 2); ++ ++ /* ?? bug2396 ++ * netdriver must load first or else must RMW this register */ ++ falcon_nic_rx_filter_ctl_set(nic, RX_FILTER_CTL_SRCH_LIMIT_TCP_FULL, ++ RX_FILTER_CTL_SRCH_LIMIT_TCP_WILD, ++ RX_FILTER_CTL_SRCH_LIMIT_UDP_FULL, ++ RX_FILTER_CTL_SRCH_LIMIT_UDP_WILD); ++ ++ if (!(nic->flags & NIC_FLAG_NO_INTERRUPT)) { ++ rc = efhw_keventq_ctor(nic, FALCON_EVQ_CHAR, ++ &nic->interrupting_evq, ev_handlers); ++ if (rc < 0) { ++ EFHW_ERR("%s: efhw_keventq_ctor() failed (%d) evq=%d", ++ __func__, rc, FALCON_EVQ_CHAR); ++ return rc; ++ } ++ } ++ rc = efhw_keventq_ctor(nic, non_irq_evq, ++ &nic->non_interrupting_evq, NULL); ++ if (rc < 0) { ++ EFHW_ERR("%s: efhw_keventq_ctor() failed (%d) evq=%d", ++ __func__, rc, non_irq_evq); ++ return rc; ++ } ++ ++ /* allocate IRQ channel */ ++ rc = falcon_nic_init_irq_channel(nic, 1); ++ /* ignore failure at user-level for eftest */ ++ if ((rc < 0) && !(nic->options & NIC_OPT_EFTEST)) ++ return rc; ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * Interrupt ++ * ++ *--------------------------------------------------------------------*/ ++ ++static void ++falcon_nic_interrupt_enable(struct efhw_nic *nic) ++{ ++ struct efhw_keventq *q; ++ unsigned rdptr; ++ ++ if (nic->flags & NIC_FLAG_NO_INTERRUPT) ++ return; ++ ++ /* Enable driver interrupts */ ++ EFHW_NOTICE("%s: enable master interrupt", __func__); ++ falcon_nic_interrupt_hw_enable(nic); ++ ++ /* An interrupting eventq must start of day ack its read pointer */ ++ q = &nic->interrupting_evq; ++ rdptr = EFHW_EVENT_OFFSET(q, q, 1) / sizeof(efhw_event_t); ++ falcon_nic_evq_ack(nic, FALCON_EVQ_CHAR, rdptr, false); ++ EFHW_NOTICE("%s: ACK evq[%d]:%x", __func__, ++ FALCON_EVQ_CHAR, rdptr); ++} ++ ++static void falcon_nic_interrupt_disable(struct efhw_nic *nic) ++{ ++ /* NB. No need to check for NIC_FLAG_NO_INTERRUPT, as ++ ** falcon_nic_interrupt_hw_disable() will do it. */ ++ falcon_nic_interrupt_hw_disable(nic); ++} ++ ++static void ++falcon_nic_set_interrupt_moderation(struct efhw_nic *nic, int evq, ++ uint32_t val) ++{ ++ if (evq < 0) ++ evq = FALCON_EVQ_CHAR; ++ ++ falcon_ab_timer_tbl_set(nic, evq, TIMER_MODE_INT_HLDOFF, val / 5); ++} ++ ++static inline void legacy_irq_ack(struct efhw_nic *nic) ++{ ++ EFHW_ASSERT(!(nic->flags & NIC_FLAG_NO_INTERRUPT)); ++ ++ if (!(nic->flags & NIC_FLAG_MSI)) { ++ writel(1, EFHW_KVA(nic) + INT_ACK_REG_CHAR_A1_OFST); ++ mmiowb(); ++ /* ?? FIXME: We should be doing a read here to ensure IRQ is ++ * thoroughly acked before we return from ISR. */ ++ } ++} ++ ++static int falcon_nic_interrupt(struct efhw_nic *nic) ++{ ++ uint32_t *syserr_ptr = ++ (uint32_t *) efhw_iopage_ptr(&nic->irq_iobuff); ++ int handled = 0; ++ int done_ack = 0; ++ ++ EFHW_ASSERT(!(nic->flags & NIC_FLAG_NO_INTERRUPT)); ++ EFHW_ASSERT(syserr_ptr); ++ ++ /* FIFO fill level interrupt - just log it. */ ++ if (unlikely(*(syserr_ptr + (DW0_OFST / 4)))) { ++ EFHW_WARN("%s: *** FIFO *** %x", __func__, ++ *(syserr_ptr + (DW0_OFST / 4))); ++ *(syserr_ptr + (DW0_OFST / 4)) = 0; ++ handled++; ++ } ++ ++ /* Fatal interrupts. */ ++ if (unlikely(*(syserr_ptr + (DW2_OFST / 4)))) { ++ *(syserr_ptr + (DW2_OFST / 4)) = 0; ++ falcon_nic_handle_fatal_int(nic); ++ handled++; ++ } ++ ++ /* Event queue interrupt. For legacy interrupts we have to check ++ * that the interrupt is for us, because it could be shared. */ ++ if (*(syserr_ptr + (DW1_OFST / 4))) { ++ *(syserr_ptr + (DW1_OFST / 4)) = 0; ++ /* ACK must come before callback to handler fn. */ ++ legacy_irq_ack(nic); ++ done_ack = 1; ++ handled++; ++ if (nic->irq_handler) ++ nic->irq_handler(nic, 0); ++ } ++ ++ if (unlikely(!done_ack)) { ++ if (!handled) ++ /* Shared interrupt line (hopefully). */ ++ return 0; ++ legacy_irq_ack(nic); ++ } ++ ++ EFHW_TRACE("%s: handled %d", __func__, handled); ++ return 1; ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * Event Management - and SW event posting ++ * ++ *--------------------------------------------------------------------*/ ++ ++static void ++falcon_nic_event_queue_enable(struct efhw_nic *nic, uint evq, uint evq_size, ++ dma_addr_t q_base_addr, /* not used */ ++ uint buf_base_id, int interrupting) ++{ ++ EFHW_ASSERT(nic); ++ ++ /* Whether or not queue has an interrupt depends on ++ * instance number and h/w variant, so [interrupting] is ++ * ignored. ++ */ ++ falcon_ab_timer_tbl_set(nic, evq, 0/*disable*/, 0); ++ ++ falcon_nic_evq_ptr_tbl(nic, evq, 1, buf_base_id, evq_size); ++ EFHW_TRACE("%s: enable evq %u size %u", __func__, evq, evq_size); ++} ++ ++static void ++falcon_nic_event_queue_disable(struct efhw_nic *nic, uint evq, int timer_only) ++{ ++ EFHW_ASSERT(nic); ++ ++ falcon_ab_timer_tbl_set(nic, evq, 0 /* disable */ , 0); ++ ++ if (!timer_only) ++ falcon_nic_evq_ptr_tbl(nic, evq, 0, 0, 0); ++ EFHW_TRACE("%s: disenable evq %u", __func__, evq); ++} ++ ++static void ++falcon_nic_wakeup_request(struct efhw_nic *nic, dma_addr_t q_base_addr, ++ int next_i, int evq) ++{ ++ EFHW_ASSERT(evq > FALCON_EVQ_CHAR); ++ falcon_nic_evq_ack(nic, evq, next_i, true); ++ EFHW_TRACE("%s: evq %d next_i %d", __func__, evq, next_i); ++} ++ ++static void falcon_nic_sw_event(struct efhw_nic *nic, int data, int evq) ++{ ++ uint64_t ev_data = data; ++ ++ ev_data &= ~FALCON_EVENT_CODE_MASK; ++ ev_data |= FALCON_EVENT_CODE_SW; ++ ++ falcon_drv_ev(nic, ev_data, evq); ++ EFHW_NOTICE("%s: evq[%d]->%x", __func__, evq, data); ++} ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * Buffer table - helpers ++ * ++ *--------------------------------------------------------------------*/ ++ ++#define FALCON_LAZY_COMMIT_HWM (FALCON_BUFFER_UPD_MAX - 16) ++ ++/* Note re.: ++ * falcon_nic_buffer_table_lazy_commit(struct efhw_nic *nic) ++ * falcon_nic_buffer_table_update_poll(struct efhw_nic *nic) ++ * falcon_nic_buffer_table_confirm(struct efhw_nic *nic) ++ * -- these are no-ops in the user-level driver because it would need to ++ * coordinate with the real driver on the number of outstanding commits. ++ * ++ * An exception is made for eftest apps, which manage the hardware without ++ * using the char driver. ++ */ ++ ++static inline void falcon_nic_buffer_table_lazy_commit(struct efhw_nic *nic) ++{ ++ /* Do nothing if operating in synchronous mode. */ ++ if (!nic->irq_handler) ++ return; ++} ++ ++static inline void falcon_nic_buffer_table_update_poll(struct efhw_nic *nic) ++{ ++ FALCON_LOCK_DECL; ++ int count = 0, rc = 0; ++ ++ /* We can be called here early days */ ++ if (!nic->irq_handler) ++ return; ++ ++ /* If we need to gather buffer update events then poll the ++ non-interrupting event queue */ ++ ++ /* For each _buffer_table_commit there will be an update done ++ event. We don't keep track of how many buffers each commit has ++ committed, just make sure that all the expected events have been ++ gathered */ ++ FALCON_LOCK_LOCK(nic); ++ ++ EFHW_TRACE("%s: %d", __func__, nic->buf_commit_outstanding); ++ ++ while (nic->buf_commit_outstanding > 0) { ++ /* we're not expecting to handle any events that require ++ * upcalls into the core driver */ ++ struct efhw_ev_handler handler; ++ memset(&handler, 0, sizeof(handler)); ++ nic->non_interrupting_evq.ev_handlers = &handler; ++ rc = efhw_keventq_poll(nic, &nic->non_interrupting_evq); ++ nic->non_interrupting_evq.ev_handlers = NULL; ++ ++ if (rc < 0) { ++ EFHW_ERR("%s: poll ERROR (%d:%d) ***** ", ++ __func__, rc, ++ nic->buf_commit_outstanding); ++ goto out; ++ } ++ ++ FALCON_LOCK_UNLOCK(nic); ++ ++ if (count++) ++ udelay(1); ++ ++ if (count > 1000) { ++ EFHW_WARN("%s: poll Timeout ***** (%d)", __func__, ++ nic->buf_commit_outstanding); ++ nic->buf_commit_outstanding = 0; ++ return; ++ } ++ FALCON_LOCK_LOCK(nic); ++ } ++ ++out: ++ FALCON_LOCK_UNLOCK(nic); ++ return; ++} ++ ++void falcon_nic_buffer_table_confirm(struct efhw_nic *nic) ++{ ++ /* confirm buffer table updates - should be used for items where ++ loss of data would be unacceptable. E.g for the buffers that back ++ an event or DMA queue */ ++ FALCON_LOCK_DECL; ++ ++ /* Do nothing if operating in synchronous mode. */ ++ if (!nic->irq_handler) ++ return; ++ ++ FALCON_LOCK_LOCK(nic); ++ ++ _falcon_nic_buffer_table_commit(nic); ++ ++ FALCON_LOCK_UNLOCK(nic); ++ ++ falcon_nic_buffer_table_update_poll(nic); ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * Buffer table - API ++ * ++ *--------------------------------------------------------------------*/ ++ ++static void ++falcon_nic_buffer_table_clear(struct efhw_nic *nic, int buffer_id, int num) ++{ ++ FALCON_LOCK_DECL; ++ FALCON_LOCK_LOCK(nic); ++ _falcon_nic_buffer_table_clear(nic, buffer_id, num); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++static void ++falcon_nic_buffer_table_set(struct efhw_nic *nic, dma_addr_t dma_addr, ++ uint bufsz, uint region, ++ int own_id, int buffer_id) ++{ ++ FALCON_LOCK_DECL; ++ ++ EFHW_ASSERT(region < FALCON_REGION_NUM); ++ ++ EFHW_ASSERT((bufsz == EFHW_4K) || ++ (bufsz == EFHW_8K && FALCON_BUFFER_TABLE_FULL_MODE)); ++ ++ falcon_nic_buffer_table_update_poll(nic); ++ ++ FALCON_LOCK_LOCK(nic); ++ ++ _falcon_nic_buffer_table_set(nic, dma_addr, bufsz, region, own_id, ++ buffer_id); ++ ++ falcon_nic_buffer_table_lazy_commit(nic); ++ ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++void ++falcon_nic_buffer_table_set_n(struct efhw_nic *nic, int buffer_id, ++ dma_addr_t dma_addr, uint bufsz, uint region, ++ int n_pages, int own_id) ++{ ++ /* used to set up a contiguous range of buffers */ ++ FALCON_LOCK_DECL; ++ ++ EFHW_ASSERT(region < FALCON_REGION_NUM); ++ ++ EFHW_ASSERT((bufsz == EFHW_4K) || ++ (bufsz == EFHW_8K && FALCON_BUFFER_TABLE_FULL_MODE)); ++ ++ while (n_pages--) { ++ ++ falcon_nic_buffer_table_update_poll(nic); ++ ++ FALCON_LOCK_LOCK(nic); ++ ++ _falcon_nic_buffer_table_set(nic, dma_addr, bufsz, region, ++ own_id, buffer_id++); ++ ++ falcon_nic_buffer_table_lazy_commit(nic); ++ ++ FALCON_LOCK_UNLOCK(nic); ++ ++ dma_addr += bufsz; ++ } ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * DMA Queues - mid level API ++ * ++ *--------------------------------------------------------------------*/ ++ ++#if BUG5302_WORKAROUND ++ ++/* Tx queues can get stuck if the software write pointer is set to an index ++ * beyond the configured size of the queue, such that they will not flush. ++ * This code can be run before attempting a flush; it will detect the bogus ++ * value and reset it. This fixes most instances of this problem, although ++ * sometimes it does not work, or we may not detect it in the first place, ++ * if the out-of-range value was replaced by an in-range value earlier. ++ * (In those cases we have to apply a bigger hammer later, if we see that ++ * the queue is still not flushing.) ++ */ ++static void ++falcon_check_for_bogus_tx_dma_wptr(struct efhw_nic *nic, uint dmaq) ++{ ++ FALCON_LOCK_DECL; ++ uint64_t val_low64, val_high64; ++ uint64_t size, hwptr, swptr, val; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ulong offset = falcon_dma_tx_q_offset(nic, dmaq); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_read_qq(efhw_kva + offset, &val_low64, &val_high64); ++ FALCON_LOCK_UNLOCK(nic); ++ ++ size = (val_low64 >> TX_DESCQ_SIZE_LBN) ++ & __FALCON_MASK64(TX_DESCQ_SIZE_WIDTH); ++ size = (1 << size) * 512; ++ hwptr = (val_high64 >> __DW3(TX_DESCQ_HW_RPTR_LBN)) ++ & __FALCON_MASK64(TX_DESCQ_HW_RPTR_WIDTH); ++ swptr = (val_low64 >> TX_DESCQ_SW_WPTR_LBN) ++ & __FALCON_MASK64(__LW2(TX_DESCQ_SW_WPTR_LBN)); ++ val = (val_high64) ++ & ++ __FALCON_MASK64(__DW3 ++ (TX_DESCQ_SW_WPTR_LBN + TX_DESCQ_SW_WPTR_WIDTH)); ++ val = val << __LW2(TX_DESCQ_SW_WPTR_LBN); ++ swptr = swptr | val; ++ ++ if (swptr >= size) { ++ EFHW_WARN("Resetting bad write pointer for TXQ[%d]", dmaq); ++ writel((uint32_t) ((hwptr + 0) & (size - 1)), ++ efhw_kva + falcon_tx_dma_page_addr(dmaq) + 12); ++ mmiowb(); ++ } ++} ++ ++/* Here's that "bigger hammer": we reset all the pointers (hardware read, ++ * hardware descriptor cache read, software write) to zero. ++ */ ++void falcon_clobber_tx_dma_ptrs(struct efhw_nic *nic, uint dmaq) ++{ ++ FALCON_LOCK_DECL; ++ uint64_t val_low64, val_high64; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ulong offset = falcon_dma_tx_q_offset(nic, dmaq); ++ ++ EFHW_WARN("Recovering stuck TXQ[%d]", dmaq); ++ FALCON_LOCK_LOCK(nic); ++ falcon_read_qq(efhw_kva + offset, &val_low64, &val_high64); ++ val_high64 &= ~(__FALCON_MASK64(TX_DESCQ_HW_RPTR_WIDTH) ++ << __DW3(TX_DESCQ_HW_RPTR_LBN)); ++ val_high64 &= ~(__FALCON_MASK64(TX_DC_HW_RPTR_WIDTH) ++ << __DW3(TX_DC_HW_RPTR_LBN)); ++ falcon_write_qq(efhw_kva + offset, val_low64, val_high64); ++ mmiowb(); ++ writel(0, efhw_kva + falcon_tx_dma_page_addr(dmaq) + 12); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++#endif ++ ++static inline int ++__falcon_really_flush_tx_dma_channel(struct efhw_nic *nic, uint dmaq) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ uint val; ++ ++ EFHW_BUILD_ASSERT(TX_FLUSH_DESCQ_REG_KER_OFST == ++ TX_FLUSH_DESCQ_REG_OFST); ++ ++ __DWCHCK(TX_FLUSH_DESCQ_CMD_LBN, TX_FLUSH_DESCQ_CMD_WIDTH); ++ __DWCHCK(TX_FLUSH_DESCQ_LBN, TX_FLUSH_DESCQ_WIDTH); ++ __RANGECHCK(dmaq, TX_FLUSH_DESCQ_WIDTH); ++ ++ val = ((1 << TX_FLUSH_DESCQ_CMD_LBN) | (dmaq << TX_FLUSH_DESCQ_LBN)); ++ ++ EFHW_TRACE("TX DMA flush[%d]", dmaq); ++ ++#if BUG5302_WORKAROUND ++ falcon_check_for_bogus_tx_dma_wptr(nic, dmaq); ++#endif ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + TX_FLUSH_DESCQ_REG_OFST, ++ val, FALCON_ATOMIC_TX_FLUSH_DESCQ); ++ ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ return 0; ++} ++ ++static inline int ++__falcon_is_tx_dma_channel_flushed(struct efhw_nic *nic, uint dmaq) ++{ ++ FALCON_LOCK_DECL; ++ uint64_t val_low64, val_high64; ++ uint64_t enable, flush_pending; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ulong offset = falcon_dma_tx_q_offset(nic, dmaq); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_read_qq(efhw_kva + offset, &val_low64, &val_high64); ++ FALCON_LOCK_UNLOCK(nic); ++ ++ /* should see one of three values for these 2 bits ++ * 1, queue enabled no flush pending ++ * - i.e. first flush request ++ * 2, queue enabled, flush pending ++ * - i.e. request to reflush before flush finished ++ * 3, queue disabled (no flush pending) ++ * - flush complete ++ */ ++ __DWCHCK(TX_DESCQ_FLUSH_LBN, TX_DESCQ_FLUSH_WIDTH); ++ __DW3CHCK(TX_DESCQ_EN_LBN, TX_DESCQ_EN_WIDTH); ++ enable = val_high64 & (1 << __DW3(TX_DESCQ_EN_LBN)); ++ flush_pending = val_low64 & (1 << TX_DESCQ_FLUSH_LBN); ++ ++ if (enable && !flush_pending) ++ return 0; ++ ++ EFHW_TRACE("%d, %s: %s, %sflush pending", dmaq, __func__, ++ enable ? "enabled" : "disabled", ++ flush_pending ? "" : "NO "); ++ /* still in progress */ ++ if (enable && flush_pending) ++ return -EALREADY; ++ ++ return -EAGAIN; ++} ++ ++static int falcon_flush_tx_dma_channel(struct efhw_nic *nic, uint dmaq) ++{ ++ int rc; ++ rc = __falcon_is_tx_dma_channel_flushed(nic, dmaq); ++ if (rc < 0) { ++ EFHW_WARN("%s: failed %d", __func__, rc); ++ return rc; ++ } ++ return __falcon_really_flush_tx_dma_channel(nic, dmaq); ++} ++ ++static int ++__falcon_really_flush_rx_dma_channel(struct efhw_nic *nic, uint dmaq) ++{ ++ FALCON_LOCK_DECL; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ uint val; ++ ++ EFHW_BUILD_ASSERT(RX_FLUSH_DESCQ_REG_KER_OFST == ++ RX_FLUSH_DESCQ_REG_OFST); ++ ++ __DWCHCK(RX_FLUSH_DESCQ_CMD_LBN, RX_FLUSH_DESCQ_CMD_WIDTH); ++ __DWCHCK(RX_FLUSH_DESCQ_LBN, RX_FLUSH_DESCQ_WIDTH); ++ __RANGECHCK(dmaq, RX_FLUSH_DESCQ_WIDTH); ++ ++ val = ((1 << RX_FLUSH_DESCQ_CMD_LBN) | (dmaq << RX_FLUSH_DESCQ_LBN)); ++ ++ EFHW_TRACE("RX DMA flush[%d]", dmaq); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_write_qq(efhw_kva + RX_FLUSH_DESCQ_REG_OFST, val, ++ FALCON_ATOMIC_RX_FLUSH_DESCQ); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++ return 0; ++} ++ ++static inline int ++__falcon_is_rx_dma_channel_flushed(struct efhw_nic *nic, uint dmaq) ++{ ++ FALCON_LOCK_DECL; ++ uint64_t val; ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ ulong offset = falcon_dma_rx_q_offset(nic, dmaq); ++ ++ /* Falcon requires 128 bit atomic access for this register */ ++ FALCON_LOCK_LOCK(nic); ++ falcon_read_q(efhw_kva + offset, &val); ++ FALCON_LOCK_UNLOCK(nic); ++ ++ __DWCHCK(RX_DESCQ_EN_LBN, RX_DESCQ_EN_WIDTH); ++ ++ /* is it enabled? */ ++ return (val & (1 << RX_DESCQ_EN_LBN)) ++ ? 0 : -EAGAIN; ++} ++ ++static int falcon_flush_rx_dma_channel(struct efhw_nic *nic, uint dmaq) ++{ ++ int rc; ++ rc = __falcon_is_rx_dma_channel_flushed(nic, dmaq); ++ if (rc < 0) { ++ EFHW_ERR("%s: failed %d", __func__, rc); ++ return rc; ++ } ++ return __falcon_really_flush_rx_dma_channel(nic, dmaq); ++} ++ ++/*-------------------------------------------------------------------- ++ * ++ * Falcon specific event callbacks ++ * ++ *--------------------------------------------------------------------*/ ++ ++int ++falcon_handle_char_event(struct efhw_nic *nic, struct efhw_ev_handler *h, ++ efhw_event_t *ev) ++{ ++ EFHW_TRACE("DRIVER EVENT: "FALCON_EVENT_FMT, ++ FALCON_EVENT_PRI_ARG(*ev)); ++ ++ switch (FALCON_EVENT_DRIVER_SUBCODE(ev)) { ++ ++ case TX_DESCQ_FLS_DONE_EV_DECODE: ++ EFHW_TRACE("TX[%d] flushed", ++ (int)FALCON_EVENT_TX_FLUSH_Q_ID(ev)); ++ efhw_handle_txdmaq_flushed(nic, h, ev); ++ break; ++ ++ case RX_DESCQ_FLS_DONE_EV_DECODE: ++ EFHW_TRACE("RX[%d] flushed", ++ (int)FALCON_EVENT_TX_FLUSH_Q_ID(ev)); ++ efhw_handle_rxdmaq_flushed(nic, h, ev); ++ break; ++ ++ case SRM_UPD_DONE_EV_DECODE: ++ nic->buf_commit_outstanding = ++ max(0, nic->buf_commit_outstanding - 1); ++ EFHW_TRACE("COMMIT DONE %d", nic->buf_commit_outstanding); ++ break; ++ ++ case EVQ_INIT_DONE_EV_DECODE: ++ EFHW_TRACE("%sEVQ INIT", ""); ++ break; ++ ++ case WAKE_UP_EV_DECODE: ++ EFHW_TRACE("%sWAKE UP", ""); ++ efhw_handle_wakeup_event(nic, h, ev); ++ break; ++ ++ case TIMER_EV_DECODE: ++ EFHW_TRACE("%sTIMER", ""); ++ efhw_handle_timeout_event(nic, h, ev); ++ break; ++ ++ case RX_DESCQ_FLSFF_OVFL_EV_DECODE: ++ /* This shouldn't happen. */ ++ EFHW_ERR("%s: RX flush fifo overflowed", __func__); ++ return -EINVAL; ++ ++ default: ++ EFHW_TRACE("UNKOWN DRIVER EVENT: " FALCON_EVENT_FMT, ++ FALCON_EVENT_PRI_ARG(*ev)); ++ break; ++ } ++ return 0; ++} ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * Filter search depth control ++ * ++ *--------------------------------------------------------------------*/ ++ ++ ++#define Q0_READ(q0, name) \ ++ ((unsigned)(((q0) >> name##_LBN) & (__FALCON_MASK64(name##_WIDTH)))) ++#define Q0_MASK(name) \ ++ ((__FALCON_MASK64(name##_WIDTH)) << name##_LBN) ++#define Q0_VALUE(name, value) \ ++ (((uint64_t)(value)) << name##_LBN) ++ ++#define Q1_READ(q1, name) \ ++ ((unsigned)(((q1) >> (name##_LBN - 64)) & \ ++ (__FALCON_MASK64(name##_WIDTH)))) ++#define Q1_MASK(name) \ ++ ((__FALCON_MASK64(name##_WIDTH)) << (name##_LBN - 64)) ++#define Q1_VALUE(name, value) \ ++ (((uint64_t)(value)) << (name##_LBN - 64)) ++ ++ ++void ++falcon_nic_get_rx_filter_search_limits(struct efhw_nic *nic, ++ struct efhw_filter_search_limits *lim, ++ int use_raw_values) ++{ ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ FALCON_LOCK_DECL; ++ uint64_t q0, q1; ++ unsigned ff = (use_raw_values ? 0 : RX_FILTER_CTL_SRCH_FUDGE_FULL); ++ unsigned wf = (use_raw_values ? 0 : RX_FILTER_CTL_SRCH_FUDGE_WILD); ++ ++ FALCON_LOCK_LOCK(nic); ++ falcon_read_qq(efhw_kva + RX_FILTER_CTL_REG_OFST, &q0, &q1); ++ FALCON_LOCK_UNLOCK(nic); ++ ++ lim->tcp_full = Q0_READ(q0, TCP_FULL_SRCH_LIMIT) - ff; ++ lim->tcp_wild = Q0_READ(q0, TCP_WILD_SRCH_LIMIT) - wf; ++ lim->udp_full = Q0_READ(q0, UDP_FULL_SRCH_LIMIT) - ff; ++ lim->udp_wild = Q0_READ(q0, UDP_WILD_SRCH_LIMIT) - wf; ++} ++EXPORT_SYMBOL(falcon_nic_get_rx_filter_search_limits); ++ ++ ++void ++falcon_nic_set_rx_filter_search_limits(struct efhw_nic *nic, ++ struct efhw_filter_search_limits *lim, ++ int use_raw_values) ++{ ++ volatile char __iomem *efhw_kva = EFHW_KVA(nic); ++ FALCON_LOCK_DECL; ++ uint64_t q0, q1; ++ unsigned ff = (use_raw_values ? 0 : RX_FILTER_CTL_SRCH_FUDGE_FULL); ++ unsigned wf = (use_raw_values ? 0 : RX_FILTER_CTL_SRCH_FUDGE_WILD); ++ ++ FALCON_LOCK_LOCK(nic); ++ falcon_read_qq(efhw_kva + RX_FILTER_CTL_REG_OFST, &q0, &q1); ++ ++ q0 &= ~Q0_MASK(TCP_FULL_SRCH_LIMIT); ++ q0 &= ~Q0_MASK(TCP_WILD_SRCH_LIMIT); ++ q0 &= ~Q0_MASK(UDP_FULL_SRCH_LIMIT); ++ q0 &= ~Q0_MASK(UDP_WILD_SRCH_LIMIT); ++ q0 |= Q0_VALUE(TCP_FULL_SRCH_LIMIT, lim->tcp_full + ff); ++ q0 |= Q0_VALUE(TCP_WILD_SRCH_LIMIT, lim->tcp_wild + wf); ++ q0 |= Q0_VALUE(UDP_FULL_SRCH_LIMIT, lim->udp_full + ff); ++ q0 |= Q0_VALUE(UDP_WILD_SRCH_LIMIT, lim->udp_wild + wf); ++ nic->tcp_full_srch.max = lim->tcp_full + ff ++ - RX_FILTER_CTL_SRCH_FUDGE_FULL; ++ nic->tcp_wild_srch.max = lim->tcp_wild + wf ++ - RX_FILTER_CTL_SRCH_FUDGE_WILD; ++ nic->udp_full_srch.max = lim->udp_full + ff ++ - RX_FILTER_CTL_SRCH_FUDGE_FULL; ++ nic->udp_wild_srch.max = lim->udp_wild + wf ++ - RX_FILTER_CTL_SRCH_FUDGE_WILD; ++ ++ falcon_write_qq(efhw_kva + RX_FILTER_CTL_REG_OFST, q0, q1); ++ mmiowb(); ++ FALCON_LOCK_UNLOCK(nic); ++} ++EXPORT_SYMBOL(falcon_nic_set_rx_filter_search_limits); ++ ++ ++#undef READ_Q0 ++#undef Q0_MASK ++#undef Q0_VALUE ++#undef READ_Q1 ++#undef Q1_MASK ++#undef Q1_VALUE ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * New unified filter API ++ * ++ *--------------------------------------------------------------------*/ ++ ++ ++#if FALCON_FULL_FILTER_CACHE ++static inline struct efhw_filter_spec * ++filter_spec_cache_entry(struct efhw_nic *nic, int filter_idx) ++{ ++ EFHW_ASSERT(nic->filter_spec_cache); ++ return &nic->filter_spec_cache[filter_idx]; ++} ++#endif ++ ++ ++static int filter_is_active(struct efhw_nic *nic, int filter_idx) ++{ ++ return nic->filter_in_use[filter_idx]; ++} ++ ++ ++static void set_filter_cache_entry(struct efhw_nic *nic, ++ struct efhw_filter_spec *spec, ++ int filter_idx) ++{ ++ nic->filter_in_use[filter_idx] = 1; ++#if FALCON_FULL_FILTER_CACHE ++ memcpy(filter_spec_cache_entry(nic, filter_idx), spec, ++ sizeof(struct efhw_filter_spec)); ++#endif ++} ++ ++ ++static void clear_filter_cache_entry(struct efhw_nic *nic, ++ int filter_idx) ++{ ++ nic->filter_in_use[filter_idx] = 0; ++#if FALCON_FULL_FILTER_CACHE ++ memset(filter_spec_cache_entry(nic, filter_idx), 0, ++ sizeof(struct efhw_filter_spec)); ++#endif ++} ++ ++ ++#if FALCON_FULL_FILTER_CACHE ++static int filter_is_duplicate(struct efhw_nic *nic, ++ struct efhw_filter_spec *spec, int filter_idx) ++{ ++ struct efhw_filter_spec *cmp; ++ ++ cmp = filter_spec_cache_entry(nic, filter_idx); ++ ++ EFHW_ASSERT(filter_is_active(nic, filter_idx)); ++ ++ return (spec->saddr_le32 == cmp->saddr_le32) && ++ (spec->daddr_le32 == cmp->daddr_le32) && ++ (spec->sport_le16 == cmp->sport_le16) && ++ (spec->dport_le16 == cmp->dport_le16) && ++ (spec->tcp == cmp->tcp) && ++ (spec->full == cmp->full); ++} ++#endif ++ ++ ++static void common_build_ip_filter(struct efhw_nic *nic, int tcp, int full, ++ int rss, int scatter, uint dmaq_id, ++ unsigned saddr_le32, unsigned sport_le16, ++ unsigned daddr_le32, unsigned dport_le16, ++ uint64_t *q0, uint64_t *q1) ++{ ++ uint64_t v1, v2, v3, v4; ++ unsigned tmp_port_le16; ++ ++ if (!full) { ++ saddr_le32 = 0; ++ sport_le16 = 0; ++ if (!tcp) { ++ tmp_port_le16 = sport_le16; ++ sport_le16 = dport_le16; ++ dport_le16 = tmp_port_le16; ++ } ++ } ++ ++ v4 = (((!tcp) << __DW4(TCP_UDP_0_LBN)) | ++ (dmaq_id << __DW4(RXQ_ID_0_LBN))); ++ ++ switch (nic->devtype.variant) { ++ case 'A': ++ EFHW_ASSERT(!rss); ++ break; ++ case 'B': ++ v4 |= scatter << __DW4(SCATTER_EN_0_B0_LBN); ++ v4 |= rss << __DW4(RSS_EN_0_B0_LBN); ++ break; ++ default: ++ EFHW_ASSERT(0); ++ break; ++ } ++ ++ v3 = daddr_le32; ++ v2 = ((dport_le16 << __DW2(DEST_PORT_TCP_0_LBN)) | ++ (__HIGH(saddr_le32, SRC_IP_0_LBN, SRC_IP_0_WIDTH))); ++ v1 = ((__LOW(saddr_le32, SRC_IP_0_LBN, SRC_IP_0_WIDTH)) | ++ (sport_le16 << SRC_TCP_DEST_UDP_0_LBN)); ++ ++ *q0 = (v2 << 32) | v1; ++ *q1 = (v4 << 32) | v3; ++} ++ ++ ++static void build_filter(struct efhw_nic *nic, struct efhw_filter_spec *spec, ++ unsigned *key, unsigned *tbl_size, ++ struct efhw_filter_depth **depth, ++ uint64_t *q0, uint64_t *q1) ++{ ++ *key = falcon_hash_get_ip_key(spec->saddr_le32, ++ spec->sport_le16, ++ spec->daddr_le32, ++ spec->dport_le16, ++ spec->tcp, ++ spec->full); ++ *tbl_size = nic->ip_filter_tbl_size; ++ if (spec->tcp && spec->full) ++ *depth = &nic->tcp_full_srch; ++ else if (spec->tcp && !spec->full) ++ *depth = &nic->tcp_wild_srch; ++ else if (!spec->tcp && spec->full) ++ *depth = &nic->udp_full_srch; ++ else ++ *depth = &nic->udp_wild_srch; ++ common_build_ip_filter(nic, spec->tcp, spec->full, ++ spec->rss, spec->scatter, ++ spec->dmaq_id, ++ spec->saddr_le32, ++ spec->sport_le16, ++ spec->daddr_le32, ++ spec->dport_le16, ++ q0, q1); ++} ++ ++ ++#if FALCON_VERIFY_FILTERS ++static void verify_filters(struct efhw_nic *nic) ++{ ++ unsigned table_offset, table_stride; ++ unsigned i, dummy_key, dummy_tbl_size; ++ struct efhw_filter_depth *dummy_depth; ++ unsigned filter_tbl_size; ++ struct efhw_filter_spec *spec; ++ uint64_t q0_expect, q1_expect, q0_got, q1_got; ++ ++ filter_tbl_size = nic->ip_filter_tbl_size; ++ table_offset = RX_FILTER_TBL0_OFST; ++ table_stride = 2 * FALCON_REGISTER128; ++ ++ for (i = 0; i < filter_tbl_size; i++) { ++ if (!filter_is_active(nic, type, i)) ++ continue; ++ ++ spec = filter_spec_cache_entry(nic, type, i); ++ ++ build_filter(nic, spec, &dummy_key, &dummy_tbl_size, ++ &dummy_depth, &q0_expect, &q1_expect); ++ ++ falcon_read_qq(EFHW_KVA(nic) + table_offset + i * table_stride, ++ &q0_got, &q1_got); ++ ++ if ((q0_got != q0_expect) || (q1_got != q1_expect)) { ++ falcon_write_qq(EFHW_KVA(nic) + 0x300, ++ q0_got, q1_got); ++ EFHW_ERR("ERROR: RX-filter[%d][%d] was " ++ "%"PRIx64":%" PRIx64" expected " ++ "%"PRIx64":%"PRIx64, ++ nic->index, i, q0_got, q1_got, ++ q0_expect, q1_expect); ++ } ++ } ++} ++#endif ++ ++ ++static void write_filter_table_entry(struct efhw_nic *nic, ++ unsigned filter_idx, ++ uint64_t q0, uint64_t q1) ++{ ++ unsigned table_offset, table_stride, offset; ++ ++ EFHW_ASSERT(filter_idx < nic->ip_filter_tbl_size); ++ table_offset = RX_FILTER_TBL0_OFST; ++ table_stride = 2 * FALCON_REGISTER128; ++ ++ offset = table_offset + filter_idx * table_stride; ++ falcon_write_qq(EFHW_KVA(nic) + offset, q0, q1); ++ mmiowb(); ++ ++#if FALCON_VERIFY_FILTERS ++ { ++ uint64_t q0read, q1read; ++ ++ /* Read a different entry first - ensure BIU flushed shadow */ ++ falcon_read_qq(EFHW_KVA(nic) + offset + 0x10, &q0read, &q1read); ++ falcon_read_qq(EFHW_KVA(nic) + offset, &q0read, &q1read); ++ EFHW_ASSERT(q0read == q0); ++ EFHW_ASSERT(q1read == q1); ++ ++ verify_filters(nic, type); ++ } ++#endif ++} ++ ++ ++static int falcon_nic_filter_set(struct efhw_nic *nic, ++ struct efhw_filter_spec *spec, ++ int *filter_idx_out) ++{ ++ FALCON_LOCK_DECL; ++ unsigned key = 0, tbl_size = 0, hash1, hash2, k; ++ struct efhw_filter_depth *depth = NULL; ++ int filter_idx = -1; ++ int rc = 0; ++ uint64_t q0, q1; ++ ++ build_filter(nic, spec, &key, &tbl_size, &depth, &q0, &q1); ++ ++ if (tbl_size == 0) ++ return -EINVAL; ++ ++ EFHW_TRACE("%s: depth->max=%d", __func__, depth->max); ++ ++ hash1 = falcon_hash_function1(key, tbl_size); ++ hash2 = falcon_hash_function2(key, tbl_size); ++ ++ FALCON_LOCK_LOCK(nic); ++ ++ for (k = 0; k < depth->max; k++) { ++ filter_idx = falcon_hash_iterator(hash1, hash2, k, tbl_size); ++ if (!filter_is_active(nic, filter_idx)) ++ break; ++#if FALCON_FULL_FILTER_CACHE ++ if (filter_is_duplicate(nic, spec, filter_idx)) { ++ EFHW_WARN("%s: ERROR: duplicate filter (disabling " ++ "interrupts)", __func__); ++ falcon_nic_interrupt_hw_disable(nic); ++ rc = -EINVAL; ++ goto fail1; ++ } ++#endif ++ } ++ if (k == depth->max) { ++ rc = -EADDRINUSE; ++ filter_idx = -1; ++ goto fail1; ++ } else if (depth->needed < (k + 1)) { ++ depth->needed = k + 1; ++ } ++ ++ EFHW_ASSERT(filter_idx < (int)tbl_size); ++ ++ set_filter_cache_entry(nic, spec, filter_idx); ++ write_filter_table_entry(nic, filter_idx, q0, q1); ++ ++ ++nic->ip_filter_tbl_used; ++ ++ *filter_idx_out = filter_idx; ++ ++ EFHW_TRACE("%s: filter index %d rxq %u set in %u", ++ __func__, filter_idx, spec->dmaq_id, k); ++ ++fail1: ++ FALCON_LOCK_UNLOCK(nic); ++ return rc; ++} ++ ++ ++static void falcon_nic_filter_clear(struct efhw_nic *nic, ++ int filter_idx) ++{ ++ FALCON_LOCK_DECL; ++ ++ if (filter_idx < 0) ++ return; ++ ++ FALCON_LOCK_LOCK(nic); ++ if (filter_is_active(nic, filter_idx)) { ++ if (--nic->ip_filter_tbl_used == 0) { ++ nic->tcp_full_srch.needed = 0; ++ nic->tcp_wild_srch.needed = 0; ++ nic->udp_full_srch.needed = 0; ++ nic->udp_wild_srch.needed = 0; ++ } ++ } ++ clear_filter_cache_entry(nic, filter_idx); ++ write_filter_table_entry(nic, filter_idx, 0, 0); ++ FALCON_LOCK_UNLOCK(nic); ++} ++ ++ ++int ++falcon_nic_filter_ctor(struct efhw_nic *nic) ++{ ++ nic->ip_filter_tbl_size = 8 * 1024; ++ nic->ip_filter_tbl_used = 0; ++ ++ nic->tcp_full_srch.needed = 0; ++ nic->tcp_full_srch.max = RX_FILTER_CTL_SRCH_LIMIT_TCP_FULL ++ - RX_FILTER_CTL_SRCH_FUDGE_FULL; ++ nic->tcp_wild_srch.needed = 0; ++ nic->tcp_wild_srch.max = RX_FILTER_CTL_SRCH_LIMIT_TCP_WILD ++ - RX_FILTER_CTL_SRCH_FUDGE_WILD; ++ nic->udp_full_srch.needed = 0; ++ nic->udp_full_srch.max = RX_FILTER_CTL_SRCH_LIMIT_UDP_FULL ++ - RX_FILTER_CTL_SRCH_FUDGE_FULL; ++ nic->udp_wild_srch.needed = 0; ++ nic->udp_wild_srch.max = RX_FILTER_CTL_SRCH_LIMIT_UDP_WILD ++ - RX_FILTER_CTL_SRCH_FUDGE_WILD; ++ ++ nic->filter_in_use = vmalloc(FALCON_FILTER_TBL_NUM); ++ if (nic->filter_in_use == NULL) ++ return -ENOMEM; ++ memset(nic->filter_in_use, 0, FALCON_FILTER_TBL_NUM); ++#if FALCON_FULL_FILTER_CACHE ++ nic->filter_spec_cache = vmalloc(FALCON_FILTER_TBL_NUM ++ * sizeof(struct efhw_filter_spec)); ++ if (nic->filter_spec_cache == NULL) ++ return -ENOMEM; ++ memset(nic->filter_spec_cache, 0, FALCON_FILTER_TBL_NUM ++ * sizeof(struct efhw_filter_spec)); ++#endif ++ ++ return 0; ++} ++ ++ ++void ++falcon_nic_filter_dtor(struct efhw_nic *nic) ++{ ++#if FALCON_FULL_FILTER_CACHE ++ if (nic->filter_spec_cache) ++ vfree(nic->filter_spec_cache); ++#endif ++ if (nic->filter_in_use) ++ vfree(nic->filter_in_use); ++} ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * Compatibility with old filter API ++ * ++ *--------------------------------------------------------------------*/ ++ ++void ++falcon_nic_rx_filter_ctl_get(struct efhw_nic *nic, uint32_t *tcp_full, ++ uint32_t *tcp_wild, ++ uint32_t *udp_full, uint32_t *udp_wild) ++{ ++ struct efhw_filter_search_limits lim; ++ ++ falcon_nic_get_rx_filter_search_limits(nic, &lim, 0); ++ *tcp_full = (uint32_t)lim.tcp_full; ++ *tcp_wild = (uint32_t)lim.tcp_wild; ++ *udp_full = (uint32_t)lim.udp_full; ++ *udp_wild = (uint32_t)lim.udp_wild; ++} ++EXPORT_SYMBOL(falcon_nic_rx_filter_ctl_get); ++ ++ ++void ++falcon_nic_rx_filter_ctl_set(struct efhw_nic *nic, uint32_t tcp_full, ++ uint32_t tcp_wild, ++ uint32_t udp_full, uint32_t udp_wild) ++{ ++ struct efhw_filter_search_limits lim; ++ ++ lim.tcp_full = (unsigned)tcp_full; ++ lim.tcp_wild = (unsigned)tcp_wild; ++ lim.udp_full = (unsigned)udp_full; ++ lim.udp_wild = (unsigned)udp_wild; ++ falcon_nic_set_rx_filter_search_limits(nic, &lim, 0); ++} ++EXPORT_SYMBOL(falcon_nic_rx_filter_ctl_set); ++ ++ ++static int ++falcon_nic_ipfilter_set(struct efhw_nic *nic, int type, int *_filter_idx, ++ int dmaq, ++ unsigned saddr_be32, unsigned sport_be16, ++ unsigned daddr_be32, unsigned dport_be16) ++{ ++ struct efhw_filter_spec spec; ++ ++ spec.dmaq_id = dmaq; ++ spec.saddr_le32 = ntohl(saddr_be32); ++ spec.daddr_le32 = ntohl(daddr_be32); ++ spec.sport_le16 = ntohs((unsigned short) sport_be16); ++ spec.dport_le16 = ntohs((unsigned short) dport_be16); ++ spec.tcp = ((type & EFHW_IP_FILTER_TYPE_TCP_MASK) != 0); ++ spec.full = ((type & EFHW_IP_FILTER_TYPE_FULL_MASK) != 0); ++ spec.rss = ((type & EFHW_IP_FILTER_TYPE_RSS_B0_MASK) != 0); ++ spec.scatter = ((type & EFHW_IP_FILTER_TYPE_NOSCAT_B0_MASK) == 0); ++ return falcon_nic_filter_set(nic, &spec, _filter_idx); ++} ++ ++static void falcon_nic_ipfilter_clear(struct efhw_nic *nic, int filter_idx) ++{ ++ falcon_nic_filter_clear(nic, filter_idx); ++} ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * Abstraction Layer Hooks ++ * ++ *--------------------------------------------------------------------*/ ++ ++struct efhw_func_ops falcon_char_functional_units = { ++ falcon_nic_close_hardware, ++ falcon_nic_init_hardware, ++ falcon_nic_interrupt, ++ falcon_nic_interrupt_enable, ++ falcon_nic_interrupt_disable, ++ falcon_nic_set_interrupt_moderation, ++ falcon_nic_event_queue_enable, ++ falcon_nic_event_queue_disable, ++ falcon_nic_wakeup_request, ++ falcon_nic_sw_event, ++ falcon_nic_ipfilter_set, ++ falcon_nic_ipfilter_clear, ++ falcon_dmaq_tx_q_init, ++ falcon_dmaq_rx_q_init, ++ falcon_dmaq_tx_q_disable, ++ falcon_dmaq_rx_q_disable, ++ falcon_flush_tx_dma_channel, ++ falcon_flush_rx_dma_channel, ++ falcon_nic_buffer_table_set, ++ falcon_nic_buffer_table_set_n, ++ falcon_nic_buffer_table_clear, ++ falcon_nic_buffer_table_commit, ++ falcon_nic_filter_set, ++ falcon_nic_filter_clear, ++}; ++ ++ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/falcon_hash.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,159 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains EtherFabric NIC hash algorithms implementation. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++ ++ ++static unsigned int ++common_get_ip_key(unsigned int src_ip, unsigned int src_port, ++ unsigned int dest_ip, unsigned int dest_port, ++ int tcp, int full, int tx, unsigned int masked_q_id) ++{ ++ ++ unsigned int tmp_port, result; ++ ++ EFHW_ASSERT(tcp == 0 || tcp == 1); ++ EFHW_ASSERT(full == 0 || full == 1); ++ EFHW_ASSERT(masked_q_id < (1 << 10)); ++ ++ /* m=masked_q_id(TX)/0(RX) u=UDP S,D=src/dest addr s,d=src/dest port ++ * ++ * Wildcard filters have src(TX)/dest(RX) addr and port = 0; ++ * and UDP wildcard filters have the src and dest port fields swapped. ++ * ++ * Addr/port fields are little-endian. ++ * ++ * 3322222222221111111111 ++ * 10987654321098765432109876543210 ++ * ++ * 000000000000000000000mmmmmmmmmmu ^ ++ * DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ^ ++ * ddddddddddddddddSSSSSSSSSSSSSSSS ^ ++ * SSSSSSSSSSSSSSSSssssssssssssssss ++ */ ++ ++ if (!tx) ++ masked_q_id = 0; ++ ++ if (!full) { ++ if (tx) { ++ dest_ip = 0; ++ dest_port = 0; ++ } else { ++ src_ip = 0; ++ src_port = 0; ++ } ++ if (!tcp) { ++ tmp_port = src_port; ++ src_port = dest_port; ++ dest_port = tmp_port; ++ } ++ } ++ ++ result = ((masked_q_id << 1) | (!tcp)) ^ ++ (dest_ip) ^ ++ (((dest_port & 0xffff) << 16) | ((src_ip >> 16) & 0xffff)) ^ ++ (((src_ip & 0xffff) << 16) | (src_port & 0xffff)); ++ ++ EFHW_TRACE("%s: IP %s %s %x", __func__, tcp ? "TCP" : "UDP", ++ full ? "Full" : "Wildcard", result); ++ ++ return result; ++} ++ ++ ++unsigned int ++falcon_hash_get_ip_key(unsigned int src_ip, unsigned int src_port, ++ unsigned int dest_ip, unsigned int dest_port, ++ int tcp, int full) ++{ ++ return common_get_ip_key(src_ip, src_port, dest_ip, dest_port, tcp, ++ full, 0, 0); ++} ++ ++ ++/* This function generates the First Hash key */ ++unsigned int falcon_hash_function1(unsigned int key, unsigned int nfilters) ++{ ++ ++ unsigned short int lfsr_reg; ++ unsigned int tmp_key; ++ int index; ++ ++ unsigned short int lfsr_input; ++ unsigned short int single_bit_key; ++ unsigned short int bit16_lfsr; ++ unsigned short int bit3_lfsr; ++ ++ lfsr_reg = 0xFFFF; ++ tmp_key = key; ++ ++ /* For Polynomial equation X^16+X^3+1 */ ++ for (index = 0; index < 32; index++) { ++ /* Get the bit from key and shift the key */ ++ single_bit_key = (tmp_key & 0x80000000) >> 31; ++ tmp_key = tmp_key << 1; ++ ++ /* get the Tap bits to XOR operation */ ++ bit16_lfsr = (lfsr_reg & 0x8000) >> 15; ++ bit3_lfsr = (lfsr_reg & 0x0004) >> 2; ++ ++ /* Get the Input value to the LFSR */ ++ lfsr_input = ((bit16_lfsr ^ bit3_lfsr) ^ single_bit_key); ++ ++ /* Shift and store out of the two TAPs */ ++ lfsr_reg = lfsr_reg << 1; ++ lfsr_reg = lfsr_reg | (lfsr_input & 0x0001); ++ ++ } ++ ++ lfsr_reg = lfsr_reg & (nfilters - 1); ++ ++ return lfsr_reg; ++} ++ ++/* This function generates the Second Hash */ ++unsigned int ++falcon_hash_function2(unsigned int key, unsigned int nfilters) ++{ ++ return (unsigned int)(((unsigned long long)key * 2 - 1) & ++ (nfilters - 1)); ++} ++ ++/* This function iterates through the hash table */ ++unsigned int ++falcon_hash_iterator(unsigned int hash1, unsigned int hash2, ++ unsigned int n_search, unsigned int nfilters) ++{ ++ return (hash1 + (n_search * hash2)) & (nfilters - 1); ++} ++ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/filter_resource.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,250 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains filters support. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "efrm_internal.h" ++ ++ ++struct filter_resource_manager { ++ struct efrm_resource_manager rm; ++ struct kfifo *free_ids; ++}; ++ ++static struct filter_resource_manager *efrm_filter_manager; ++ ++ ++void efrm_filter_resource_free(struct filter_resource *frs) ++{ ++ struct efhw_nic *nic = frs->rs.rs_client->nic; ++ int id; ++ ++ EFRM_RESOURCE_ASSERT_VALID(&frs->rs, 1); ++ ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT, __func__, ++ EFRM_RESOURCE_PRI_ARG(frs->rs.rs_handle)); ++ ++ efhw_nic_ipfilter_clear(nic, frs->filter_idx); ++ frs->filter_idx = -1; ++ efrm_vi_resource_release(frs->pt); ++ ++ /* Free this filter. */ ++ id = EFRM_RESOURCE_INSTANCE(frs->rs.rs_handle); ++ EFRM_VERIFY_EQ(kfifo_put(efrm_filter_manager->free_ids, ++ (unsigned char *)&id, sizeof(id)), ++ sizeof(id)); ++ ++ efrm_client_put(frs->rs.rs_client); ++ EFRM_DO_DEBUG(memset(frs, 0, sizeof(*frs))); ++ kfree(frs); ++} ++EXPORT_SYMBOL(efrm_filter_resource_free); ++ ++ ++void efrm_filter_resource_release(struct filter_resource *frs) ++{ ++ if (__efrm_resource_release(&frs->rs)) ++ efrm_filter_resource_free(frs); ++} ++EXPORT_SYMBOL(efrm_filter_resource_release); ++ ++ ++static void filter_rm_dtor(struct efrm_resource_manager *rm) ++{ ++ EFRM_TRACE("%s:", __func__); ++ ++ EFRM_RESOURCE_MANAGER_ASSERT_VALID(&efrm_filter_manager->rm); ++ EFRM_ASSERT(&efrm_filter_manager->rm == rm); ++ ++ kfifo_vfree(efrm_filter_manager->free_ids); ++ EFRM_TRACE("%s: done", __func__); ++} ++ ++/**********************************************************************/ ++/**********************************************************************/ ++/**********************************************************************/ ++ ++int efrm_create_filter_resource_manager(struct efrm_resource_manager **rm_out) ++{ ++ int rc; ++ ++ EFRM_ASSERT(rm_out); ++ ++ efrm_filter_manager = ++ kmalloc(sizeof(struct filter_resource_manager), GFP_KERNEL); ++ if (efrm_filter_manager == 0) ++ return -ENOMEM; ++ memset(efrm_filter_manager, 0, sizeof(*efrm_filter_manager)); ++ ++ rc = efrm_resource_manager_ctor(&efrm_filter_manager->rm, ++ filter_rm_dtor, "FILTER", ++ EFRM_RESOURCE_FILTER); ++ if (rc < 0) ++ goto fail1; ++ ++ /* Create a pool of free instances */ ++ rc = efrm_kfifo_id_ctor(&efrm_filter_manager->free_ids, ++ 0, EFHW_IP_FILTER_NUM, ++ &efrm_filter_manager->rm.rm_lock); ++ if (rc != 0) ++ goto fail2; ++ ++ *rm_out = &efrm_filter_manager->rm; ++ EFRM_TRACE("%s: filter resources created - %d IDs", ++ __func__, kfifo_len(efrm_filter_manager->free_ids)); ++ return 0; ++ ++fail2: ++ efrm_resource_manager_dtor(&efrm_filter_manager->rm); ++fail1: ++ memset(efrm_filter_manager, 0, sizeof(*efrm_filter_manager)); ++ kfree(efrm_filter_manager); ++ return rc; ++ ++} ++ ++ ++int efrm_filter_resource_clear(struct filter_resource *frs) ++{ ++ struct efhw_nic *nic = frs->rs.rs_client->nic; ++ ++ efhw_nic_ipfilter_clear(nic, frs->filter_idx); ++ frs->filter_idx = -1; ++ return 0; ++} ++EXPORT_SYMBOL(efrm_filter_resource_clear); ++ ++ ++int ++__efrm_filter_resource_set(struct filter_resource *frs, int type, ++ unsigned saddr, uint16_t sport, ++ unsigned daddr, uint16_t dport) ++{ ++ struct efhw_nic *nic = frs->rs.rs_client->nic; ++ int vi_instance; ++ ++ EFRM_ASSERT(frs); ++ ++ if (efrm_nic_tablep->a_nic->devtype.variant >= 'B' && ++ (frs->pt->flags & EFHW_VI_JUMBO_EN) == 0) ++ type |= EFHW_IP_FILTER_TYPE_NOSCAT_B0_MASK; ++ vi_instance = EFRM_RESOURCE_INSTANCE(frs->pt->rs.rs_handle); ++ ++ return efhw_nic_ipfilter_set(nic, type, &frs->filter_idx, ++ vi_instance, saddr, sport, daddr, dport); ++} ++EXPORT_SYMBOL(__efrm_filter_resource_set);; ++ ++ ++int ++efrm_filter_resource_alloc(struct vi_resource *vi_parent, ++ struct filter_resource **frs_out) ++{ ++ struct filter_resource *frs; ++ int rc, instance; ++ ++ EFRM_ASSERT(frs_out); ++ EFRM_ASSERT(efrm_filter_manager); ++ EFRM_RESOURCE_MANAGER_ASSERT_VALID(&efrm_filter_manager->rm); ++ EFRM_ASSERT(vi_parent != NULL); ++ EFRM_ASSERT(EFRM_RESOURCE_TYPE(vi_parent->rs.rs_handle) == ++ EFRM_RESOURCE_VI); ++ ++ /* Allocate resource data structure. */ ++ frs = kmalloc(sizeof(struct filter_resource), GFP_KERNEL); ++ if (!frs) ++ return -ENOMEM; ++ ++ /* Allocate an instance. */ ++ rc = kfifo_get(efrm_filter_manager->free_ids, ++ (unsigned char *)&instance, sizeof(instance)); ++ if (rc != sizeof(instance)) { ++ EFRM_TRACE("%s: out of instances", __func__); ++ EFRM_ASSERT(rc == 0); ++ rc = -EBUSY; ++ goto fail1; ++ } ++ ++ /* Initialise the resource DS. */ ++ efrm_resource_init(&frs->rs, EFRM_RESOURCE_FILTER, instance); ++ frs->pt = vi_parent; ++ efrm_resource_ref(&frs->pt->rs); ++ frs->filter_idx = -1; ++ ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT " VI %d", __func__, ++ EFRM_RESOURCE_PRI_ARG(frs->rs.rs_handle), ++ EFRM_RESOURCE_INSTANCE(vi_parent->rs.rs_handle)); ++ ++ efrm_client_add_resource(vi_parent->rs.rs_client, &frs->rs); ++ *frs_out = frs; ++ return 0; ++ ++fail1: ++ memset(frs, 0, sizeof(*frs)); ++ kfree(frs); ++ return rc; ++} ++EXPORT_SYMBOL(efrm_filter_resource_alloc); ++ ++ ++int efrm_filter_resource_instance(struct filter_resource *frs) ++{ ++ return EFRM_RESOURCE_INSTANCE(frs->rs.rs_handle); ++} ++EXPORT_SYMBOL(efrm_filter_resource_instance); ++ ++ ++struct efrm_resource * ++efrm_filter_resource_to_resource(struct filter_resource *frs) ++{ ++ return &frs->rs; ++} ++EXPORT_SYMBOL(efrm_filter_resource_to_resource); ++ ++ ++struct filter_resource * ++efrm_filter_resource_from_resource(struct efrm_resource *rs) ++{ ++ return filter_resource(rs); ++} ++EXPORT_SYMBOL(efrm_filter_resource_from_resource); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/iobufset_resource.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,404 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains non-contiguous I/O buffers support. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "efrm_internal.h" ++ ++ ++#define EFRM_IOBUFSET_MAX_NUM_INSTANCES 0x00010000 ++ ++struct iobufset_resource_manager { ++ struct efrm_resource_manager rm; ++ struct kfifo *free_ids; ++}; ++ ++struct iobufset_resource_manager *efrm_iobufset_manager; ++ ++#define iobsrs(rs1) iobufset_resource(rs1) ++ ++/* Returns size of iobufset resource data structure. */ ++static inline size_t iobsrs_size(int n_pages) ++{ ++ return offsetof(struct iobufset_resource, bufs) + ++ n_pages * sizeof(struct efhw_iopage); ++} ++ ++void efrm_iobufset_resource_free(struct iobufset_resource *rs) ++{ ++ unsigned int i; ++ int id; ++ ++ EFRM_RESOURCE_ASSERT_VALID(&rs->rs, 1); ++ ++ if (!rs->linked && rs->buf_tbl_alloc.base != (unsigned) -1) ++ efrm_buffer_table_free(&rs->buf_tbl_alloc); ++ ++ /* see comment on call to efhw_iopage_alloc in the alloc routine above ++ for discussion on use of efrm_nic_tablep->a_nic here */ ++ EFRM_ASSERT(efrm_nic_tablep->a_nic); ++ if (rs->linked) { ++ /* Nothing to do. */ ++ } else if (rs->chunk_order == 0) { ++ for (i = 0; i < rs->n_bufs; ++i) ++ efhw_iopage_free(efrm_nic_tablep->a_nic, &rs->bufs[i]); ++ } else { ++ /* it is important that this is executed in increasing page ++ * order because some implementations of ++ * efhw_iopages_init_from_iopage() assume this */ ++ for (i = 0; i < rs->n_bufs; ++ i += rs->pages_per_contiguous_chunk) { ++ struct efhw_iopages iopages; ++ efhw_iopages_init_from_iopage(&iopages, &rs->bufs[i], ++ rs->chunk_order); ++ efhw_iopages_free(efrm_nic_tablep->a_nic, &iopages); ++ } ++ } ++ ++ /* free the instance number */ ++ id = EFRM_RESOURCE_INSTANCE(rs->rs.rs_handle); ++ EFRM_VERIFY_EQ(kfifo_put(efrm_iobufset_manager->free_ids, ++ (unsigned char *)&id, sizeof(id)), sizeof(id)); ++ ++ efrm_vi_resource_release(rs->evq); ++ if (rs->linked) ++ efrm_iobufset_resource_release(rs->linked); ++ ++ efrm_client_put(rs->rs.rs_client); ++ if (iobsrs_size(rs->n_bufs) < PAGE_SIZE) { ++ EFRM_DO_DEBUG(memset(rs, 0, sizeof(*rs))); ++ kfree(rs); ++ } else { ++ EFRM_DO_DEBUG(memset(rs, 0, sizeof(*rs))); ++ vfree(rs); ++ } ++} ++EXPORT_SYMBOL(efrm_iobufset_resource_free); ++ ++ ++void efrm_iobufset_resource_release(struct iobufset_resource *iobrs) ++{ ++ if (__efrm_resource_release(&iobrs->rs)) ++ efrm_iobufset_resource_free(iobrs); ++} ++EXPORT_SYMBOL(efrm_iobufset_resource_release); ++ ++ ++ ++int ++efrm_iobufset_resource_alloc(int32_t n_pages, ++ int32_t pages_per_contiguous_chunk, ++ struct vi_resource *vi_evq, ++ struct iobufset_resource *linked, ++ bool phys_addr_mode, ++ struct iobufset_resource **iobrs_out) ++{ ++ struct iobufset_resource *iobrs; ++ int rc, instance, object_size; ++ unsigned int i; ++ ++ EFRM_ASSERT(iobrs_out); ++ EFRM_ASSERT(efrm_iobufset_manager); ++ EFRM_RESOURCE_MANAGER_ASSERT_VALID(&efrm_iobufset_manager->rm); ++ EFRM_RESOURCE_ASSERT_VALID(&vi_evq->rs, 0); ++ EFRM_ASSERT(EFRM_RESOURCE_TYPE(vi_evq->rs.rs_handle) == ++ EFRM_RESOURCE_VI); ++ EFRM_ASSERT(efrm_nic_tablep->a_nic); ++ ++ if (linked) { ++ /* This resource will share properties and memory with ++ * another. Only difference is that we'll program it into ++ * the buffer table of another nic. ++ */ ++ n_pages = linked->n_bufs; ++ pages_per_contiguous_chunk = linked->pages_per_contiguous_chunk; ++ phys_addr_mode = linked->buf_tbl_alloc.base == (unsigned) -1; ++ } ++ ++ /* allocate the resource data structure. */ ++ object_size = iobsrs_size(n_pages); ++ if (object_size < PAGE_SIZE) { ++ /* this should be OK from a tasklet */ ++ /* Necessary to do atomic alloc() as this ++ can be called from a weird-ass iSCSI context that is ++ !in_interrupt but is in_atomic - See BUG3163 */ ++ iobrs = kmalloc(object_size, GFP_ATOMIC); ++ } else { /* can't do this within a tasklet */ ++#ifndef NDEBUG ++ if (in_interrupt() || in_atomic()) { ++ EFRM_ERR("%s(): alloc->u.iobufset.in_n_pages=%d", ++ __func__, n_pages); ++ EFRM_ASSERT(!in_interrupt()); ++ EFRM_ASSERT(!in_atomic()); ++ } ++#endif ++ iobrs = (struct iobufset_resource *) vmalloc(object_size); ++ } ++ if (iobrs == NULL) { ++ EFRM_WARN("%s: failed to allocate container", __func__); ++ rc = -ENOMEM; ++ goto fail1; ++ } ++ ++ /* Allocate an instance number. */ ++ rc = kfifo_get(efrm_iobufset_manager->free_ids, ++ (unsigned char *)&instance, sizeof(instance)); ++ if (rc != sizeof(instance)) { ++ EFRM_WARN("%s: out of instances", __func__); ++ EFRM_ASSERT(rc == 0); ++ rc = -EBUSY; ++ goto fail3; ++ } ++ ++ efrm_resource_init(&iobrs->rs, EFRM_RESOURCE_IOBUFSET, instance); ++ ++ iobrs->evq = vi_evq; ++ iobrs->linked = linked; ++ iobrs->n_bufs = n_pages; ++ iobrs->pages_per_contiguous_chunk = pages_per_contiguous_chunk; ++ iobrs->chunk_order = fls(iobrs->pages_per_contiguous_chunk - 1); ++ iobrs->buf_tbl_alloc.base = (unsigned) -1; ++ ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT " %u pages", __func__, ++ EFRM_RESOURCE_PRI_ARG(iobrs->rs.rs_handle), iobrs->n_bufs); ++ ++ /* Allocate the iobuffers. */ ++ if (linked) { ++ memcpy(iobrs->bufs, linked->bufs, ++ iobrs->n_bufs * sizeof(iobrs->bufs[0])); ++ } else if (iobrs->chunk_order == 0) { ++ memset(iobrs->bufs, 0, iobrs->n_bufs * sizeof(iobrs->bufs[0])); ++ for (i = 0; i < iobrs->n_bufs; ++i) { ++ /* due to bug2426 we have to specifiy a NIC when ++ * allocating a DMAable page, which is a bit messy. ++ * For now we assume that if the page is suitable ++ * (e.g. DMAable) by one nic (efrm_nic_tablep->a_nic), ++ * it is suitable for all NICs. ++ * XXX I bet that breaks in Solaris. ++ */ ++ rc = efhw_iopage_alloc(efrm_nic_tablep->a_nic, ++ &iobrs->bufs[i]); ++ if (rc < 0) { ++ EFRM_WARN("%s: failed (rc %d) to allocate " ++ "page (i=%u)", __func__, rc, i); ++ goto fail4; ++ } ++ } ++ } else { ++ struct efhw_iopages iopages; ++ unsigned j; ++ ++ memset(iobrs->bufs, 0, iobrs->n_bufs * sizeof(iobrs->bufs[0])); ++ for (i = 0; i < iobrs->n_bufs; ++ i += iobrs->pages_per_contiguous_chunk) { ++ rc = efhw_iopages_alloc(efrm_nic_tablep->a_nic, ++ &iopages, iobrs->chunk_order); ++ if (rc < 0) { ++ EFRM_WARN("%s: failed (rc %d) to allocate " ++ "pages (i=%u order %d)", ++ __func__, rc, i, ++ iobrs->chunk_order); ++ goto fail4; ++ } ++ for (j = 0; j < iobrs->pages_per_contiguous_chunk; ++ j++) { ++ /* some implementation of ++ * efhw_iopage_init_from_iopages() rely on ++ * this function being called for ++ * _all_ pages in the chunk */ ++ efhw_iopage_init_from_iopages( ++ &iobrs->bufs[i + j], ++ &iopages, j); ++ } ++ } ++ } ++ ++ if (!phys_addr_mode) { ++ unsigned owner_id = EFAB_VI_RESOURCE_INSTANCE(iobrs->evq); ++ ++ if (!linked) { ++ /* Allocate space in the NIC's buffer table. */ ++ rc = efrm_buffer_table_alloc(fls(iobrs->n_bufs - 1), ++ &iobrs->buf_tbl_alloc); ++ if (rc < 0) { ++ EFRM_WARN("%s: failed (%d) to alloc %d buffer " ++ "table entries", __func__, rc, ++ iobrs->n_bufs); ++ goto fail5; ++ } ++ EFRM_ASSERT(((unsigned)1 << iobrs->buf_tbl_alloc.order) ++ >= (unsigned) iobrs->n_bufs); ++ } else { ++ iobrs->buf_tbl_alloc = linked->buf_tbl_alloc; ++ } ++ ++ /* Initialise the buffer table entries. */ ++ for (i = 0; i < iobrs->n_bufs; ++i) { ++ /*\ ?? \TODO burst them! */ ++ efrm_buffer_table_set(&iobrs->buf_tbl_alloc, ++ vi_evq->rs.rs_client->nic, ++ i, ++ efhw_iopage_dma_addr(&iobrs-> ++ bufs[i]), ++ owner_id); ++ } ++ efrm_buffer_table_commit(); ++ } ++ ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT " %d pages @ " ++ EFHW_BUFFER_ADDR_FMT, __func__, ++ EFRM_RESOURCE_PRI_ARG(iobrs->rs.rs_handle), ++ iobrs->n_bufs, EFHW_BUFFER_ADDR(iobrs->buf_tbl_alloc.base, ++ 0)); ++ efrm_resource_ref(&iobrs->evq->rs); ++ if (linked != NULL) ++ efrm_resource_ref(&linked->rs); ++ efrm_client_add_resource(vi_evq->rs.rs_client, &iobrs->rs); ++ *iobrs_out = iobrs; ++ return 0; ++ ++fail5: ++ i = iobrs->n_bufs; ++fail4: ++ /* see comment on call to efhw_iopage_alloc above for a discussion ++ * on use of efrm_nic_tablep->a_nic here */ ++ if (linked) { ++ /* Nothing to do. */ ++ } else if (iobrs->chunk_order == 0) { ++ while (i--) { ++ struct efhw_iopage *page = &iobrs->bufs[i]; ++ efhw_iopage_free(efrm_nic_tablep->a_nic, page); ++ } ++ } else { ++ unsigned int j; ++ for (j = 0; j < i; j += iobrs->pages_per_contiguous_chunk) { ++ struct efhw_iopages iopages; ++ ++ EFRM_ASSERT(j % iobrs->pages_per_contiguous_chunk ++ == 0); ++ /* it is important that this is executed in increasing ++ * page order because some implementations of ++ * efhw_iopages_init_from_iopage() assume this */ ++ efhw_iopages_init_from_iopage(&iopages, ++ &iobrs->bufs[j], ++ iobrs->chunk_order); ++ efhw_iopages_free(efrm_nic_tablep->a_nic, &iopages); ++ } ++ } ++fail3: ++ if (object_size < PAGE_SIZE) ++ kfree(iobrs); ++ else ++ vfree(iobrs); ++fail1: ++ return rc; ++} ++EXPORT_SYMBOL(efrm_iobufset_resource_alloc); ++ ++static void iobufset_rm_dtor(struct efrm_resource_manager *rm) ++{ ++ EFRM_ASSERT(&efrm_iobufset_manager->rm == rm); ++ kfifo_vfree(efrm_iobufset_manager->free_ids); ++} ++ ++int ++efrm_create_iobufset_resource_manager(struct efrm_resource_manager **rm_out) ++{ ++ int rc, max; ++ ++ EFRM_ASSERT(rm_out); ++ ++ efrm_iobufset_manager = ++ kmalloc(sizeof(*efrm_iobufset_manager), GFP_KERNEL); ++ if (efrm_iobufset_manager == 0) ++ return -ENOMEM; ++ memset(efrm_iobufset_manager, 0, sizeof(*efrm_iobufset_manager)); ++ ++ /* ++ * Bug 1145, 1370: We need to set initial size of both the resource ++ * table and instance id table so they never need to grow as we ++ * want to be allocate new iobufset at tasklet time. Lets make ++ * a pessimistic guess at maximum number of iobufsets possible. ++ * Could be less because ++ * - jumbo frames have same no of packets per iobufset BUT more ++ * pages per buffer ++ * - buffer table entries used independently of iobufsets by ++ * sendfile ++ * ++ * Based on TCP/IP stack setting of PKTS_PER_SET_S=5 ... ++ * - can't use this define here as it breaks the layering. ++ */ ++#define MIN_PAGES_PER_IOBUFSET (1 << 4) ++ ++ max = efrm_buffer_table_size() / MIN_PAGES_PER_IOBUFSET; ++ max = min_t(int, max, EFRM_IOBUFSET_MAX_NUM_INSTANCES); ++ ++ /* HACK: There currently exists an option to allocate buffers that ++ * are not programmed into the buffer table, so the max number is ++ * not limited by the buffer table size. I'm hoping this usage ++ * will go away eventually. ++ */ ++ max = 32768; ++ ++ rc = efrm_kfifo_id_ctor(&efrm_iobufset_manager->free_ids, ++ 0, max, &efrm_iobufset_manager->rm.rm_lock); ++ if (rc != 0) ++ goto fail1; ++ ++ rc = efrm_resource_manager_ctor(&efrm_iobufset_manager->rm, ++ iobufset_rm_dtor, "IOBUFSET", ++ EFRM_RESOURCE_IOBUFSET); ++ if (rc < 0) ++ goto fail2; ++ ++ *rm_out = &efrm_iobufset_manager->rm; ++ return 0; ++ ++fail2: ++ kfifo_vfree(efrm_iobufset_manager->free_ids); ++fail1: ++ EFRM_DO_DEBUG(memset(efrm_iobufset_manager, 0, ++ sizeof(*efrm_iobufset_manager))); ++ kfree(efrm_iobufset_manager); ++ return rc; ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/iopage.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,103 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides Linux-specific implementation for iopage API used ++ * from efhw library. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include "kernel_compat.h" ++#include /* for dma_addr_t */ ++ ++int efhw_iopage_alloc(struct efhw_nic *nic, struct efhw_iopage *p) ++{ ++ struct linux_efhw_nic *lnic = linux_efhw_nic(nic); ++ dma_addr_t handle; ++ void *kva; ++ ++ kva = efrm_pci_alloc_consistent(lnic->pci_dev, PAGE_SIZE, ++ &handle); ++ if (kva == 0) ++ return -ENOMEM; ++ ++ EFHW_ASSERT((handle & ~PAGE_MASK) == 0); ++ ++ memset((void *)kva, 0, PAGE_SIZE); ++ efhw_page_init_from_va(&p->p, kva); ++ ++ p->dma_addr = handle; ++ ++ return 0; ++} ++ ++void efhw_iopage_free(struct efhw_nic *nic, struct efhw_iopage *p) ++{ ++ struct linux_efhw_nic *lnic = linux_efhw_nic(nic); ++ EFHW_ASSERT(efhw_page_is_valid(&p->p)); ++ ++ efrm_pci_free_consistent(lnic->pci_dev, PAGE_SIZE, ++ efhw_iopage_ptr(p), p->dma_addr); ++} ++ ++int ++efhw_iopages_alloc(struct efhw_nic *nic, struct efhw_iopages *p, ++ unsigned order) ++{ ++ unsigned bytes = 1u << (order + PAGE_SHIFT); ++ struct linux_efhw_nic *lnic = linux_efhw_nic(nic); ++ dma_addr_t handle; ++ caddr_t addr; ++ int gfp_flag; ++ ++ /* Set __GFP_COMP if available to make reference counting work. ++ * This is recommended here: ++ * http://www.forbiddenweb.org/viewtopic.php?id=83167&page=4#348331 ++ */ ++ gfp_flag = ((in_atomic() ? GFP_ATOMIC : GFP_KERNEL) | __GFP_COMP); ++ addr = efrm_dma_alloc_coherent(&lnic->pci_dev->dev, bytes, &handle, ++ gfp_flag); ++ if (addr == NULL) ++ return -ENOMEM; ++ ++ EFHW_ASSERT((handle & ~PAGE_MASK) == 0); ++ ++ p->order = order; ++ p->dma_addr = handle; ++ p->kva = addr; ++ ++ return 0; ++} ++ ++void efhw_iopages_free(struct efhw_nic *nic, struct efhw_iopages *p) ++{ ++ unsigned bytes = 1u << (p->order + PAGE_SHIFT); ++ struct linux_efhw_nic *lnic = linux_efhw_nic(nic); ++ ++ efrm_dma_free_coherent(&lnic->pci_dev->dev, bytes, ++ (void *)p->kva, p->dma_addr); ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/kernel_compat.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,118 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file provides compatibility layer for various Linux kernel versions ++ * (starting from 2.6.9 RHEL kernel). ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#define IN_KERNEL_COMPAT_C ++#include ++#include ++#include "kernel_compat.h" ++ ++/* Set this to 1 to enable very basic counting of iopage(s) allocations, then ++ * call dump_iopage_counts() to show the number of current allocations of ++ * orders 0-7. ++ */ ++#define EFRM_IOPAGE_COUNTS_ENABLED 0 ++ ++ ++/**************************************************************************** ++ * ++ * allocate a buffer suitable for DMA to/from the NIC ++ * ++ ****************************************************************************/ ++ ++#if EFRM_IOPAGE_COUNTS_ENABLED ++ ++static int iopage_counts[8]; ++ ++void dump_iopage_counts(void) ++{ ++ EFRM_NOTICE("iopage counts: %d %d %d %d %d %d %d %d", iopage_counts[0], ++ iopage_counts[1], iopage_counts[2], iopage_counts[3], ++ iopage_counts[4], iopage_counts[5], iopage_counts[6], ++ iopage_counts[7]); ++} ++ ++#endif ++ ++ ++ ++/*********** pci_alloc_consistent / pci_free_consistent ***********/ ++ ++void *efrm_dma_alloc_coherent(struct device *dev, size_t size, ++ dma_addr_t *dma_addr, int flag) ++{ ++ void *ptr; ++ unsigned order; ++ ++ order = __ffs(size/PAGE_SIZE); ++ EFRM_ASSERT(size == (PAGE_SIZE< ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef DRIVER_LINUX_RESOURCE_KERNEL_COMPAT_H ++#define DRIVER_LINUX_RESOURCE_KERNEL_COMPAT_H ++ ++#include ++#include ++#include ++#include ++ ++/********* pci_map_*() ********************/ ++ ++extern void *efrm_dma_alloc_coherent(struct device *dev, size_t size, ++ dma_addr_t *dma_addr, int flag); ++ ++extern void efrm_dma_free_coherent(struct device *dev, size_t size, ++ void *ptr, dma_addr_t dma_addr); ++ ++static inline void *efrm_pci_alloc_consistent(struct pci_dev *hwdev, ++ size_t size, ++ dma_addr_t *dma_addr) ++{ ++ return efrm_dma_alloc_coherent(&hwdev->dev, size, dma_addr, ++ GFP_ATOMIC); ++} ++ ++static inline void efrm_pci_free_consistent(struct pci_dev *hwdev, size_t size, ++ void *ptr, dma_addr_t dma_addr) ++{ ++ efrm_dma_free_coherent(&hwdev->dev, size, ptr, dma_addr); ++} ++ ++ ++#endif /* DRIVER_LINUX_RESOURCE_KERNEL_COMPAT_H */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/kernel_proc.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,109 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains /proc/driver/sfc_resource/ implementation. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++ ++/** Top level directory for sfc specific stats **/ ++static struct proc_dir_entry *efrm_proc_root; /* = NULL */ ++ ++static int ++efrm_resource_read_proc(char *buf, char **start, off_t offset, int count, ++ int *eof, void *data); ++ ++int efrm_install_proc_entries(void) ++{ ++ /* create the top-level directory for etherfabric specific stuff */ ++ efrm_proc_root = proc_mkdir("driver/sfc_resource", NULL); ++ if (!efrm_proc_root) ++ return -ENOMEM; ++ ++ if (create_proc_read_entry("resources", 0, efrm_proc_root, ++ efrm_resource_read_proc, 0) == NULL) { ++ EFRM_WARN("%s: Unable to create /proc/drivers/sfc_resource/" ++ "resources", __func__); ++ } ++ return 0; ++} ++ ++void efrm_uninstall_proc_entries(void) ++{ ++ EFRM_ASSERT(efrm_proc_root); ++ remove_proc_entry("resources", efrm_proc_root); ++ remove_proc_entry(efrm_proc_root->name, efrm_proc_root->parent); ++ efrm_proc_root = NULL; ++} ++ ++/**************************************************************************** ++ * ++ * /proc/drivers/sfc/resources ++ * ++ ****************************************************************************/ ++ ++#define EFRM_PROC_PRINTF(buf, len, fmt, ...) \ ++ do { \ ++ if (count - len > 0) \ ++ len += snprintf(buf+len, count-len, (fmt), \ ++ __VA_ARGS__); \ ++ } while (0) ++ ++static int ++efrm_resource_read_proc(char *buf, char **start, off_t offset, int count, ++ int *eof, void *data) ++{ ++ irq_flags_t lock_flags; ++ int len = 0; ++ int type; ++ struct efrm_resource_manager *rm; ++ ++ for (type = 0; type < EFRM_RESOURCE_NUM; type++) { ++ rm = efrm_rm_table[type]; ++ if (rm == NULL) ++ continue; ++ ++ EFRM_PROC_PRINTF(buf, len, "*** %s ***\n", rm->rm_name); ++ ++ spin_lock_irqsave(&rm->rm_lock, lock_flags); ++ EFRM_PROC_PRINTF(buf, len, "current = %u\n", rm->rm_resources); ++ EFRM_PROC_PRINTF(buf, len, " max = %u\n\n", ++ rm->rm_resources_hiwat); ++ spin_unlock_irqrestore(&rm->rm_lock, lock_flags); ++ } ++ ++ return count ? strlen(buf) : 0; ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/kfifo.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* ++ * A simple kernel FIFO implementation. ++ * ++ * Copyright (C) 2004 Stelian Pop ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/* ++ * This file is stolen from the Linux kernel sources ++ * (linux-2.6.22/kernel/kfifo.c) into sfc_resource driver. ++ * It should be used for old kernels without kfifo implementation. ++ * Most part of linux/kfifo.h is incorporated into ++ * ci/efrm/sysdep_linux.h. ++ */ ++#include ++#ifdef HAS_NO_KFIFO ++ ++#include ++#include ++#include ++#include ++/*#include */ ++ ++/** ++ * kfifo_init - allocates a new FIFO using a preallocated buffer ++ * @buffer: the preallocated buffer to be used. ++ * @size: the size of the internal buffer, this have to be a power of 2. ++ * @gfp_mask: get_free_pages mask, passed to kmalloc() ++ * @lock: the lock to be used to protect the fifo buffer ++ * ++ * Do NOT pass the kfifo to kfifo_free() after use! Simply free the ++ * &struct kfifo with kfree(). ++ */ ++struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size, ++ gfp_t gfp_mask, spinlock_t *lock) ++{ ++ struct kfifo *fifo; ++ ++ /* size must be a power of 2 */ ++ BUG_ON(size & (size - 1)); ++ ++ fifo = kmalloc(sizeof(struct kfifo), gfp_mask); ++ if (!fifo) ++ return ERR_PTR(-ENOMEM); ++ ++ fifo->buffer = buffer; ++ fifo->size = size; ++ fifo->in = fifo->out = 0; ++ fifo->lock = lock; ++ ++ return fifo; ++} ++EXPORT_SYMBOL(kfifo_init); ++ ++/** ++ * kfifo_alloc - allocates a new FIFO and its internal buffer ++ * @size: the size of the internal buffer to be allocated. ++ * @gfp_mask: get_free_pages mask, passed to kmalloc() ++ * @lock: the lock to be used to protect the fifo buffer ++ * ++ * The size will be rounded-up to a power of 2. ++ */ ++struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock) ++{ ++ unsigned char *buffer; ++ struct kfifo *ret; ++ ++ /* ++ * round up to the next power of 2, since our 'let the indices ++ * wrap' tachnique works only in this case. ++ */ ++ if (size & (size - 1)) { ++ BUG_ON(size > 0x80000000); ++ size = roundup_pow_of_two(size); ++ } ++ ++ buffer = kmalloc(size, gfp_mask); ++ if (!buffer) ++ return ERR_PTR(-ENOMEM); ++ ++ ret = kfifo_init(buffer, size, gfp_mask, lock); ++ ++ if (IS_ERR(ret)) ++ kfree(buffer); ++ ++ return ret; ++} ++EXPORT_SYMBOL(kfifo_alloc); ++ ++/** ++ * kfifo_free - frees the FIFO ++ * @fifo: the fifo to be freed. ++ */ ++void kfifo_free(struct kfifo *fifo) ++{ ++ kfree(fifo->buffer); ++ kfree(fifo); ++} ++EXPORT_SYMBOL(kfifo_free); ++ ++/** ++ * __kfifo_put - puts some data into the FIFO, no locking version ++ * @fifo: the fifo to be used. ++ * @buffer: the data to be added. ++ * @len: the length of the data to be added. ++ * ++ * This function copies at most @len bytes from the @buffer into ++ * the FIFO depending on the free space, and returns the number of ++ * bytes copied. ++ * ++ * Note that with only one concurrent reader and one concurrent ++ * writer, you don't need extra locking to use these functions. ++ */ ++unsigned int ++__kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len) ++{ ++ unsigned int l; ++ ++ len = min(len, fifo->size - fifo->in + fifo->out); ++ ++ /* ++ * Ensure that we sample the fifo->out index -before- we ++ * start putting bytes into the kfifo. ++ */ ++ ++ smp_mb(); ++ ++ /* first put the data starting from fifo->in to buffer end */ ++ l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); ++ memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l); ++ ++ /* then put the rest (if any) at the beginning of the buffer */ ++ memcpy(fifo->buffer, buffer + l, len - l); ++ ++ /* ++ * Ensure that we add the bytes to the kfifo -before- ++ * we update the fifo->in index. ++ */ ++ ++ smp_wmb(); ++ ++ fifo->in += len; ++ ++ return len; ++} ++EXPORT_SYMBOL(__kfifo_put); ++ ++/** ++ * __kfifo_get - gets some data from the FIFO, no locking version ++ * @fifo: the fifo to be used. ++ * @buffer: where the data must be copied. ++ * @len: the size of the destination buffer. ++ * ++ * This function copies at most @len bytes from the FIFO into the ++ * @buffer and returns the number of copied bytes. ++ * ++ * Note that with only one concurrent reader and one concurrent ++ * writer, you don't need extra locking to use these functions. ++ */ ++unsigned int ++__kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len) ++{ ++ unsigned int l; ++ ++ len = min(len, fifo->in - fifo->out); ++ ++ /* ++ * Ensure that we sample the fifo->in index -before- we ++ * start removing bytes from the kfifo. ++ */ ++ ++ smp_rmb(); ++ ++ /* first get the data from fifo->out until the end of the buffer */ ++ l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); ++ memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l); ++ ++ /* then get the rest (if any) from the beginning of the buffer */ ++ memcpy(buffer + l, fifo->buffer, len - l); ++ ++ /* ++ * Ensure that we remove the bytes from the kfifo -before- ++ * we update the fifo->out index. ++ */ ++ ++ smp_mb(); ++ ++ fifo->out += len; ++ ++ return len; ++} ++EXPORT_SYMBOL(__kfifo_get); ++ ++#endif +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/linux_resource_internal.h 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,76 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains Linux-specific API internal for the resource driver. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#ifndef __LINUX_RESOURCE_INTERNAL__ ++#define __LINUX_RESOURCE_INTERNAL__ ++ ++#include ++#include ++#include ++#include ++ ++ ++/*! Linux specific EtherFabric initialisation */ ++extern int ++linux_efrm_nic_ctor(struct linux_efhw_nic *, struct pci_dev *, ++ spinlock_t *reg_lock, ++ unsigned nic_flags, unsigned nic_options); ++ ++/*! Linux specific EtherFabric initialisation */ ++extern void linux_efrm_nic_dtor(struct linux_efhw_nic *); ++ ++/*! Linux specific EtherFabric initialisation -- interrupt registration */ ++extern int linux_efrm_irq_ctor(struct linux_efhw_nic *); ++ ++/*! Linux specific EtherFabric initialisation -- interrupt deregistration */ ++extern void linux_efrm_irq_dtor(struct linux_efhw_nic *); ++ ++extern int efrm_driverlink_register(void); ++extern void efrm_driverlink_unregister(void); ++ ++extern int ++efrm_nic_add(struct pci_dev *dev, unsigned int opts, const uint8_t *mac_addr, ++ struct linux_efhw_nic **lnic_out, spinlock_t *reg_lock, ++ int bt_min, int bt_max, int non_irq_evq, ++ const struct vi_resource_dimensions *); ++extern void efrm_nic_del(struct linux_efhw_nic *); ++ ++ ++extern int efrm_install_proc_entries(void); ++extern void efrm_uninstall_proc_entries(void); ++ ++#endif /* __LINUX_RESOURCE_INTERNAL__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/nic.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,174 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains EtherFabric Generic NIC instance (init, interrupts, ++ * etc) ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++int efhw_device_type_init(struct efhw_device_type *dt, ++ int vendor_id, int device_id, ++ int class_revision) ++{ ++ if (vendor_id != 0x1924) ++ return 0; ++ ++ switch (device_id) { ++ case 0x0703: ++ case 0x6703: ++ dt->variant = 'A'; ++ switch (class_revision) { ++ case 0: ++ dt->revision = 0; ++ break; ++ case 1: ++ dt->revision = 1; ++ break; ++ default: ++ return 0; ++ } ++ break; ++ case 0x0710: ++ dt->variant = 'B'; ++ switch (class_revision) { ++ case 2: ++ dt->revision = 0; ++ break; ++ default: ++ return 0; ++ } ++ break; ++ default: ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++/*-------------------------------------------------------------------- ++ * ++ * NIC Initialisation ++ * ++ *--------------------------------------------------------------------*/ ++ ++/* make this separate from initialising data structure ++** to allow this to be called at a later time once we can access PCI ++** config space to find out what hardware we have ++*/ ++void efhw_nic_init(struct efhw_nic *nic, unsigned flags, unsigned options, ++ struct efhw_device_type dev_type) ++{ ++ nic->devtype = dev_type; ++ nic->flags = flags; ++ nic->options = options; ++ nic->bar_ioaddr = 0; ++ spin_lock_init(&nic->the_reg_lock); ++ nic->reg_lock = &nic->the_reg_lock; ++ nic->mtu = 1500 + ETH_HLEN; ++ ++ nic->irq_unit = EFHW_IRQ_UNIT_UNUSED; ++ ++ nic->evq_sizes = 512 | 1024 | 2048 | 4096 | 8192 | ++ 16384 | 32768; ++ nic->txq_sizes = 512 | 1024 | 2048 | 4096; ++ nic->rxq_sizes = 512 | 1024 | 2048 | 4096; ++ nic->efhw_func = &falcon_char_functional_units; ++ nic->ctr_ap_bytes = EFHW_64M; ++ switch (nic->devtype.variant) { ++ case 'A': ++ nic->ctr_ap_bar = FALCON_S_CTR_AP_BAR; ++ nic->num_evqs = 4096; ++ nic->num_dmaqs = 4096; ++ nic->num_timers = 4096; ++ break; ++ case 'B': ++ nic->flags |= NIC_FLAG_NO_INTERRUPT; ++ nic->ctr_ap_bar = FALCON_P_CTR_AP_BAR; ++ nic->num_evqs = 4096; ++ nic->num_dmaqs = 4096; ++ nic->num_timers = 4096; ++ break; ++ default: ++ EFHW_ASSERT(0); ++ break; ++ } ++} ++ ++ ++void efhw_nic_close_interrupts(struct efhw_nic *nic) ++{ ++ EFHW_ASSERT(nic); ++ if (!efhw_nic_have_hw(nic)) ++ return; ++ ++ EFHW_ASSERT(efhw_nic_have_hw(nic)); ++ ++ if (nic->irq_unit != EFHW_IRQ_UNIT_UNUSED) ++ efhw_nic_interrupt_disable(nic); ++} ++ ++void efhw_nic_dtor(struct efhw_nic *nic) ++{ ++ EFHW_ASSERT(nic); ++ ++ /* Check that we have functional units because the software only ++ * driver doesn't initialise anything hardware related any more */ ++ ++ /* close interrupts is called first because the act of deregistering ++ the driver could cause this driver to change from master to slave ++ and hence the implicit interrupt mappings would be wrong */ ++ ++ EFHW_TRACE("%s: functional units ... ", __func__); ++ ++ if (efhw_nic_have_functional_units(nic)) { ++ efhw_nic_close_interrupts(nic); ++ efhw_nic_close_hardware(nic); ++ } ++ EFHW_TRACE("%s: functional units ... done", __func__); ++ ++ /* destroy event queues */ ++ EFHW_TRACE("%s: event queues ... ", __func__); ++ ++ if (nic->interrupting_evq.evq_mask) ++ efhw_keventq_dtor(nic, &nic->interrupting_evq); ++ if (nic->non_interrupting_evq.evq_mask) ++ efhw_keventq_dtor(nic, &nic->non_interrupting_evq); ++ ++ EFHW_TRACE("%s: event queues ... done", __func__); ++ ++ spin_lock_destroy(&nic->the_reg_lock); ++ ++ EFHW_TRACE("%s: DONE", __func__); ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/resource_driver.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,600 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains main driver entry points. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include "linux_resource_internal.h" ++#include "kernel_compat.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Solarflare Communications"); ++MODULE_LICENSE("GPL"); ++ ++static struct efhw_ev_handler ev_handler = { ++ .wakeup_fn = efrm_handle_wakeup_event, ++ .timeout_fn = efrm_handle_timeout_event, ++ .dmaq_flushed_fn = efrm_handle_dmaq_flushed, ++}; ++ ++const int max_hardware_init_repeats = 10; ++ ++/*-------------------------------------------------------------------- ++ * ++ * Module load time variables ++ * ++ *--------------------------------------------------------------------*/ ++/* See docs/notes/pci_alloc_consistent */ ++static int do_irq = 1; /* enable interrupts */ ++ ++#if defined(CONFIG_X86_XEN) ++static int irq_moderation = 60; /* interrupt moderation (60 usec) */ ++#else ++static int irq_moderation = 20; /* interrupt moderation (20 usec) */ ++#endif ++static int nic_options = NIC_OPT_DEFAULT; ++int efx_vi_eventq_size = EFX_VI_EVENTQ_SIZE_DEFAULT; ++ ++module_param(do_irq, int, S_IRUGO); ++MODULE_PARM_DESC(do_irq, "Enable interrupts. " ++ "Do not turn it off unless you know what are you doing."); ++module_param(irq_moderation, int, S_IRUGO); ++MODULE_PARM_DESC(irq_moderation, "IRQ moderation in usec"); ++module_param(nic_options, int, S_IRUGO); ++MODULE_PARM_DESC(nic_options, "Nic options -- see efhw_types.h"); ++module_param(efx_vi_eventq_size, int, S_IRUGO); ++MODULE_PARM_DESC(efx_vi_eventq_size, ++ "Size of event queue allocated by efx_vi library"); ++ ++/*-------------------------------------------------------------------- ++ * ++ * Linux specific NIC initialisation ++ * ++ *--------------------------------------------------------------------*/ ++ ++static inline irqreturn_t ++linux_efrm_interrupt(int irr, void *dev_id) ++{ ++ return efhw_nic_interrupt((struct efhw_nic *)dev_id); ++} ++ ++int linux_efrm_irq_ctor(struct linux_efhw_nic *lnic) ++{ ++ struct efhw_nic *nic = &lnic->efrm_nic.efhw_nic; ++ ++ nic->flags &= ~NIC_FLAG_MSI; ++ if (nic->flags & NIC_FLAG_TRY_MSI) { ++ int rc = pci_enable_msi(lnic->pci_dev); ++ if (rc < 0) { ++ EFRM_WARN("%s: Could not enable MSI (%d)", ++ __func__, rc); ++ EFRM_WARN("%s: Continuing with legacy interrupt mode", ++ __func__); ++ } else { ++ EFRM_NOTICE("%s: MSI enabled", __func__); ++ nic->flags |= NIC_FLAG_MSI; ++ } ++ } ++ ++ if (request_irq(lnic->pci_dev->irq, linux_efrm_interrupt, ++ IRQF_SHARED, "sfc_resource", nic)) { ++ EFRM_ERR("Request for interrupt #%d failed", ++ lnic->pci_dev->irq); ++ nic->flags &= ~NIC_FLAG_OS_IRQ_EN; ++ return -EBUSY; ++ } ++ nic->flags |= NIC_FLAG_OS_IRQ_EN; ++ ++ return 0; ++} ++ ++void linux_efrm_irq_dtor(struct linux_efhw_nic *lnic) ++{ ++ EFRM_TRACE("%s: start", __func__); ++ ++ if (lnic->efrm_nic.efhw_nic.flags & NIC_FLAG_OS_IRQ_EN) { ++ free_irq(lnic->pci_dev->irq, &lnic->efrm_nic.efhw_nic); ++ lnic->efrm_nic.efhw_nic.flags &= ~NIC_FLAG_OS_IRQ_EN; ++ } ++ ++ if (lnic->efrm_nic.efhw_nic.flags & NIC_FLAG_MSI) { ++ pci_disable_msi(lnic->pci_dev); ++ lnic->efrm_nic.efhw_nic.flags &= ~NIC_FLAG_MSI; ++ } ++ ++ EFRM_TRACE("%s: done", __func__); ++} ++ ++/* Allocate buffer table entries for a particular NIC. ++ */ ++static int efrm_nic_buffer_table_alloc(struct efhw_nic *nic) ++{ ++ int capacity; ++ int page_order; ++ int rc; ++ ++ /* Choose queue size. */ ++ for (capacity = 8192; capacity <= nic->evq_sizes; capacity <<= 1) { ++ if (capacity > nic->evq_sizes) { ++ EFRM_ERR ++ ("%s: Unable to choose EVQ size (supported=%x)", ++ __func__, nic->evq_sizes); ++ return -E2BIG; ++ } else if (capacity & nic->evq_sizes) ++ break; ++ } ++ ++ nic->interrupting_evq.hw.capacity = capacity; ++ nic->interrupting_evq.hw.buf_tbl_alloc.base = (unsigned)-1; ++ ++ nic->non_interrupting_evq.hw.capacity = capacity; ++ nic->non_interrupting_evq.hw.buf_tbl_alloc.base = (unsigned)-1; ++ ++ /* allocate buffer table entries to map onto the iobuffer */ ++ page_order = get_order(capacity * sizeof(efhw_event_t)); ++ if (!(nic->flags & NIC_FLAG_NO_INTERRUPT)) { ++ rc = efrm_buffer_table_alloc(page_order, ++ &nic->interrupting_evq ++ .hw.buf_tbl_alloc); ++ if (rc < 0) { ++ EFRM_WARN ++ ("%s: failed (%d) to alloc %d buffer table entries", ++ __func__, rc, page_order); ++ return rc; ++ } ++ } ++ rc = efrm_buffer_table_alloc(page_order, ++ &nic->non_interrupting_evq.hw. ++ buf_tbl_alloc); ++ if (rc < 0) { ++ EFRM_WARN ++ ("%s: failed (%d) to alloc %d buffer table entries", ++ __func__, rc, page_order); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/* Free buffer table entries allocated for a particular NIC. ++ */ ++static void efrm_nic_buffer_table_free(struct efhw_nic *nic) ++{ ++ if (nic->interrupting_evq.hw.buf_tbl_alloc.base != (unsigned)-1) ++ efrm_buffer_table_free(&nic->interrupting_evq.hw ++ .buf_tbl_alloc); ++ if (nic->non_interrupting_evq.hw.buf_tbl_alloc.base != (unsigned)-1) ++ efrm_buffer_table_free(&nic->non_interrupting_evq ++ .hw.buf_tbl_alloc); ++} ++ ++static int iomap_bar(struct linux_efhw_nic *lnic, size_t len) ++{ ++ volatile char __iomem *ioaddr; ++ ++ ioaddr = ioremap_nocache(lnic->ctr_ap_pci_addr, len); ++ if (ioaddr == 0) ++ return -ENOMEM; ++ ++ lnic->efrm_nic.efhw_nic.bar_ioaddr = ioaddr; ++ return 0; ++} ++ ++static int linux_efhw_nic_map_ctr_ap(struct linux_efhw_nic *lnic) ++{ ++ struct efhw_nic *nic = &lnic->efrm_nic.efhw_nic; ++ int rc; ++ ++ rc = iomap_bar(lnic, nic->ctr_ap_bytes); ++ ++ /* Bug 5195: workaround for now. */ ++ if (rc != 0 && nic->ctr_ap_bytes > 16 * 1024 * 1024) { ++ /* Try half the size for now. */ ++ nic->ctr_ap_bytes /= 2; ++ EFRM_WARN("Bug 5195 WORKAROUND: retrying iomap of %d bytes", ++ nic->ctr_ap_bytes); ++ rc = iomap_bar(lnic, nic->ctr_ap_bytes); ++ } ++ ++ if (rc < 0) { ++ EFRM_ERR("Failed (%d) to map bar (%d bytes)", ++ rc, nic->ctr_ap_bytes); ++ return rc; ++ } ++ ++ return rc; ++} ++ ++int ++linux_efrm_nic_ctor(struct linux_efhw_nic *lnic, struct pci_dev *dev, ++ spinlock_t *reg_lock, ++ unsigned nic_flags, unsigned nic_options) ++{ ++ struct efhw_device_type dev_type; ++ struct efhw_nic *nic = &lnic->efrm_nic.efhw_nic; ++ u8 class_revision; ++ int rc; ++ ++ rc = pci_read_config_byte(dev, PCI_CLASS_REVISION, &class_revision); ++ if (rc != 0) { ++ EFRM_ERR("%s: pci_read_config_byte failed (%d)", ++ __func__, rc); ++ return rc; ++ } ++ ++ if (!efhw_device_type_init(&dev_type, dev->vendor, dev->device, ++ class_revision)) { ++ EFRM_ERR("%s: efhw_device_type_init failed %04x:%04x(%d)", ++ __func__, (unsigned) dev->vendor, ++ (unsigned) dev->device, (int) class_revision); ++ return -ENODEV; ++ } ++ ++ EFRM_NOTICE("attaching device type %04x:%04x %d:%c%d", ++ (unsigned) dev->vendor, (unsigned) dev->device, ++ dev_type.arch, dev_type.variant, dev_type.revision); ++ ++ /* Initialise the adapter-structure. */ ++ efhw_nic_init(nic, nic_flags, nic_options, dev_type); ++ lnic->pci_dev = dev; ++ ++ rc = pci_enable_device(dev); ++ if (rc < 0) { ++ EFRM_ERR("%s: pci_enable_device failed (%d)", ++ __func__, rc); ++ return rc; ++ } ++ ++ lnic->ctr_ap_pci_addr = pci_resource_start(dev, nic->ctr_ap_bar); ++ ++ if (!pci_dma_supported(dev, (dma_addr_t)EFHW_DMA_ADDRMASK)) { ++ EFRM_ERR("%s: pci_dma_supported(%lx) failed", __func__, ++ (unsigned long)EFHW_DMA_ADDRMASK); ++ return -ENODEV; ++ } ++ ++ if (pci_set_dma_mask(dev, (dma_addr_t)EFHW_DMA_ADDRMASK)) { ++ EFRM_ERR("%s: pci_set_dma_mask(%lx) failed", __func__, ++ (unsigned long)EFHW_DMA_ADDRMASK); ++ return -ENODEV; ++ } ++ ++ if (pci_set_consistent_dma_mask(dev, (dma_addr_t)EFHW_DMA_ADDRMASK)) { ++ EFRM_ERR("%s: pci_set_consistent_dma_mask(%lx) failed", ++ __func__, (unsigned long)EFHW_DMA_ADDRMASK); ++ return -ENODEV; ++ } ++ ++ rc = linux_efhw_nic_map_ctr_ap(lnic); ++ if (rc < 0) ++ return rc; ++ ++ /* By default struct efhw_nic contains its own lock for protecting ++ * access to nic registers. We override it with a pointer to the ++ * lock in the net driver. This is needed when resource and net ++ * drivers share a single PCI function (falcon B series). ++ */ ++ nic->reg_lock = reg_lock; ++ return 0; ++} ++ ++void linux_efrm_nic_dtor(struct linux_efhw_nic *lnic) ++{ ++ struct efhw_nic *nic = &lnic->efrm_nic.efhw_nic; ++ volatile char __iomem *bar_ioaddr = nic->bar_ioaddr; ++ ++ efhw_nic_dtor(nic); ++ ++ /* Unmap the bar. */ ++ EFRM_ASSERT(bar_ioaddr); ++ iounmap(bar_ioaddr); ++ nic->bar_ioaddr = 0; ++} ++ ++/**************************************************************************** ++ * ++ * efrm_tasklet - used to poll the eventq which may result in further callbacks ++ * ++ ****************************************************************************/ ++ ++static void efrm_tasklet(unsigned long pdev) ++{ ++ struct efhw_nic *nic = (struct efhw_nic *)pdev; ++ ++ EFRM_ASSERT(!(nic->flags & NIC_FLAG_NO_INTERRUPT)); ++ ++ efhw_keventq_poll(nic, &nic->interrupting_evq); ++ EFRM_TRACE("%s: complete", __func__); ++} ++ ++/**************************************************************************** ++ * ++ * char driver specific interrupt callbacks -- run at hard IRQL ++ * ++ ****************************************************************************/ ++static void efrm_handle_eventq_irq(struct efhw_nic *nic, int evq) ++{ ++ /* NB. The interrupt must have already been acked (for legacy mode). */ ++ ++ EFRM_TRACE("%s: starting tasklet", __func__); ++ EFRM_ASSERT(!(nic->flags & NIC_FLAG_NO_INTERRUPT)); ++ ++ tasklet_schedule(&linux_efhw_nic(nic)->tasklet); ++} ++ ++/* A count of how many NICs this driver knows about. */ ++static int n_nics_probed; ++ ++/**************************************************************************** ++ * ++ * efrm_nic_add: add the NIC to the resource driver ++ * ++ * NOTE: the flow of control through this routine is quite subtle ++ * because of the number of operations that can fail. We therefore ++ * take the apporaching of keeping the return code (rc) variable ++ * accurate, and only do operations while it is non-negative. Tear down ++ * is done at the end if rc is negative, depending on what has been set up ++ * by that point. ++ * ++ * So basically just make sure that any code you add checks rc>=0 before ++ * doing any work and you'll be fine. ++ * ++ ****************************************************************************/ ++int ++efrm_nic_add(struct pci_dev *dev, unsigned flags, const uint8_t *mac_addr, ++ struct linux_efhw_nic **lnic_out, spinlock_t *reg_lock, ++ int bt_min, int bt_lim, int non_irq_evq, ++ const struct vi_resource_dimensions *res_dim) ++{ ++ struct linux_efhw_nic *lnic = NULL; ++ struct efhw_nic *nic = NULL; ++ int count = 0, rc = 0, resources_init = 0; ++ int constructed = 0; ++ int registered_nic = 0; ++ int buffers_allocated = 0; ++ static unsigned nic_index; /* = 0; */ ++ ++ EFRM_TRACE("%s: device detected (Slot '%s', IRQ %d)", __func__, ++ pci_name(dev) ? pci_name(dev) : "?", dev->irq); ++ ++ /* Ensure that we have room for the new adapter-structure. */ ++ if (efrm_nic_tablep->nic_count == EFHW_MAX_NR_DEVS) { ++ EFRM_WARN("%s: WARNING: too many devices", __func__); ++ rc = -ENOMEM; ++ goto failed; ++ } ++ ++ if (n_nics_probed == 0) { ++ rc = efrm_resources_init(res_dim, bt_min, bt_lim); ++ if (rc != 0) ++ goto failed; ++ resources_init = 1; ++ } ++ ++ /* Allocate memory for the new adapter-structure. */ ++ lnic = kmalloc(sizeof(*lnic), GFP_KERNEL); ++ if (lnic == NULL) { ++ EFRM_ERR("%s: ERROR: failed to allocate memory", __func__); ++ rc = -ENOMEM; ++ goto failed; ++ } ++ memset(lnic, 0, sizeof(*lnic)); ++ nic = &lnic->efrm_nic.efhw_nic; ++ ++ lnic->ev_handlers = &ev_handler; ++ ++ /* OS specific hardware mappings */ ++ rc = linux_efrm_nic_ctor(lnic, dev, reg_lock, flags, nic_options); ++ if (rc < 0) { ++ EFRM_ERR("%s: ERROR: initialisation failed", __func__); ++ goto failed; ++ } ++ ++ constructed = 1; ++ ++ /* Tell the driver about the NIC - this needs to be done before the ++ resources managers get created below. Note we haven't initialised ++ the hardware yet, and I don't like doing this before the perhaps ++ unreliable hardware initialisation. However, there's quite a lot ++ of code to review if we wanted to hardware init before bringing ++ up the resource managers. */ ++ rc = efrm_driver_register_nic(&lnic->efrm_nic, nic_index, ++ /* TODO: ifindex */ nic_index); ++ if (rc < 0) { ++ EFRM_ERR("%s: cannot register nic %d with nic error code %d", ++ __func__, efrm_nic_tablep->nic_count, rc); ++ goto failed; ++ } ++ ++nic_index; ++ registered_nic = 1; ++ ++ rc = efrm_nic_buffer_table_alloc(nic); ++ if (rc < 0) ++ goto failed; ++ buffers_allocated = 1; ++ ++ /****************************************************/ ++ /* hardware bringup */ ++ /****************************************************/ ++ /* Detecting hardware can be a slightly unreliable process; ++ we want to make sure that we maximise our chances, so we ++ loop a few times until all is good. */ ++ for (count = 0; count < max_hardware_init_repeats; count++) { ++ rc = efhw_nic_init_hardware(nic, &ev_handler, mac_addr, ++ non_irq_evq); ++ if (rc >= 0) ++ break; ++ ++ /* pain */ ++ EFRM_ERR ++ ("error - hardware initialisation failed code %d, " ++ "attempt %d of %d", rc, count + 1, ++ max_hardware_init_repeats); ++ } ++ if (rc < 0) ++ goto failed; ++ ++ tasklet_init(&lnic->tasklet, efrm_tasklet, (ulong)nic); ++ ++ /* set up interrupt handlers (hard-irq) */ ++ nic->irq_handler = &efrm_handle_eventq_irq; ++ ++ /* this device can now take management interrupts */ ++ if (do_irq && !(nic->flags & NIC_FLAG_NO_INTERRUPT)) { ++ rc = linux_efrm_irq_ctor(lnic); ++ if (rc < 0) { ++ EFRM_ERR("Interrupt initialisation failed (%d)", rc); ++ goto failed; ++ } ++ efhw_nic_set_interrupt_moderation(nic, -1, irq_moderation); ++ efhw_nic_interrupt_enable(nic); ++ } ++ EFRM_TRACE("interrupts are %sregistered", do_irq ? "" : "not "); ++ ++ *lnic_out = lnic; ++ EFRM_ASSERT(rc == 0); ++ ++n_nics_probed; ++ return 0; ++ ++failed: ++ if (buffers_allocated) ++ efrm_nic_buffer_table_free(nic); ++ if (registered_nic) ++ efrm_driver_unregister_nic(&lnic->efrm_nic); ++ if (constructed) ++ linux_efrm_nic_dtor(lnic); ++ kfree(lnic); /* safe in any case */ ++ if (resources_init) ++ efrm_resources_fini(); ++ return rc; ++} ++ ++/**************************************************************************** ++ * ++ * efrm_nic_del: Remove the nic from the resource driver structures ++ * ++ ****************************************************************************/ ++void efrm_nic_del(struct linux_efhw_nic *lnic) ++{ ++ struct efhw_nic *nic = &lnic->efrm_nic.efhw_nic; ++ ++ EFRM_TRACE("%s:", __func__); ++ EFRM_ASSERT(nic); ++ ++ efrm_nic_buffer_table_free(nic); ++ ++ efrm_driver_unregister_nic(&lnic->efrm_nic); ++ ++ /* ++ * Synchronise here with any running ISR. ++ * Remove the OS handler. There should be no IRQs being generated ++ * by our NIC at this point. ++ */ ++ if (efhw_nic_have_functional_units(nic)) { ++ efhw_nic_close_interrupts(nic); ++ linux_efrm_irq_dtor(lnic); ++ tasklet_kill(&lnic->tasklet); ++ } ++ ++ /* Close down hardware and free resources. */ ++ linux_efrm_nic_dtor(lnic); ++ kfree(lnic); ++ ++ if (--n_nics_probed == 0) ++ efrm_resources_fini(); ++ ++ EFRM_TRACE("%s: done", __func__); ++} ++ ++/**************************************************************************** ++ * ++ * init_module: register as a PCI driver. ++ * ++ ****************************************************************************/ ++static int init_sfc_resource(void) ++{ ++ int rc = 0; ++ ++ EFRM_TRACE("%s: RESOURCE driver starting", __func__); ++ ++ efrm_driver_ctor(); ++ ++ /* Register the driver so that our 'probe' function is called for ++ * each EtherFabric device in the system. ++ */ ++ rc = efrm_driverlink_register(); ++ if (rc == -ENODEV) ++ EFRM_ERR("%s: no devices found", __func__); ++ if (rc < 0) ++ goto failed_driverlink; ++ ++ if (efrm_install_proc_entries() != 0) { ++ /* Do not fail, but print a warning */ ++ EFRM_WARN("%s: WARNING: failed to install /proc entries", ++ __func__); ++ } ++ ++ return 0; ++ ++failed_driverlink: ++ efrm_driver_dtor(); ++ return rc; ++} ++ ++/**************************************************************************** ++ * ++ * cleanup_module: module-removal entry-point ++ * ++ ****************************************************************************/ ++static void cleanup_sfc_resource(void) ++{ ++ efrm_uninstall_proc_entries(); ++ ++ efrm_driverlink_unregister(); ++ ++ /* Clean up char-driver specific initialisation. ++ - driver dtor can use both work queue and buffer table entries */ ++ efrm_driver_dtor(); ++ ++ EFRM_TRACE("%s: unloaded", __func__); ++} ++ ++module_init(init_sfc_resource); ++module_exit(cleanup_sfc_resource); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/resource_manager.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,145 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains generic code for resources and resource managers. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "efrm_internal.h" ++ ++/********************************************************************** ++ * struct efrm_resource_manager ++ */ ++ ++void efrm_resource_manager_dtor(struct efrm_resource_manager *rm) ++{ ++ EFRM_RESOURCE_MANAGER_ASSERT_VALID(rm); ++ ++ /* call destructor */ ++ EFRM_DO_DEBUG(if (rm->rm_resources) ++ EFRM_ERR("%s: %s leaked %d resources", ++ __func__, rm->rm_name, rm->rm_resources)); ++ EFRM_ASSERT(rm->rm_resources == 0); ++ EFRM_ASSERT(list_empty(&rm->rm_resources_list)); ++ ++ rm->rm_dtor(rm); ++ ++ /* clear out things built by efrm_resource_manager_ctor */ ++ spin_lock_destroy(&rm->rm_lock); ++ ++ /* and the free the memory */ ++ EFRM_DO_DEBUG(memset(rm, 0, sizeof(*rm))); ++ kfree(rm); ++} ++ ++/* Construct a resource manager. Resource managers are singletons. */ ++int ++efrm_resource_manager_ctor(struct efrm_resource_manager *rm, ++ void (*dtor)(struct efrm_resource_manager *), ++ const char *name, unsigned type) ++{ ++ EFRM_ASSERT(rm); ++ EFRM_ASSERT(dtor); ++ ++ rm->rm_name = name; ++ EFRM_DO_DEBUG(rm->rm_type = type); ++ rm->rm_dtor = dtor; ++ spin_lock_init(&rm->rm_lock); ++ rm->rm_resources = 0; ++ rm->rm_resources_hiwat = 0; ++ INIT_LIST_HEAD(&rm->rm_resources_list); ++ EFRM_RESOURCE_MANAGER_ASSERT_VALID(rm); ++ return 0; ++} ++ ++ ++void efrm_client_add_resource(struct efrm_client *client, ++ struct efrm_resource *rs) ++{ ++ struct efrm_resource_manager *rm; ++ irq_flags_t lock_flags; ++ ++ EFRM_ASSERT(client != NULL); ++ EFRM_ASSERT(rs != NULL); ++ ++ spin_lock_irqsave(&efrm_nic_tablep->lock, lock_flags); ++ rm = efrm_rm_table[EFRM_RESOURCE_TYPE(rs->rs_handle)]; ++ ++rm->rm_resources; ++ list_add(&rs->rs_manager_link, &rm->rm_resources_list); ++ if (rm->rm_resources > rm->rm_resources_hiwat) ++ rm->rm_resources_hiwat = rm->rm_resources; ++ rs->rs_client = client; ++ ++client->ref_count; ++ list_add(&rs->rs_client_link, &client->resources); ++ spin_unlock_irqrestore(&efrm_nic_tablep->lock, lock_flags); ++} ++ ++ ++void efrm_resource_ref(struct efrm_resource *rs) ++{ ++ irq_flags_t lock_flags; ++ spin_lock_irqsave(&efrm_nic_tablep->lock, lock_flags); ++ ++rs->rs_ref_count; ++ spin_unlock_irqrestore(&efrm_nic_tablep->lock, lock_flags); ++} ++EXPORT_SYMBOL(efrm_resource_ref); ++ ++ ++int __efrm_resource_release(struct efrm_resource *rs) ++{ ++ struct efrm_resource_manager *rm; ++ irq_flags_t lock_flags; ++ int free_rs; ++ ++ spin_lock_irqsave(&efrm_nic_tablep->lock, lock_flags); ++ free_rs = --rs->rs_ref_count == 0; ++ if (free_rs) { ++ rm = efrm_rm_table[EFRM_RESOURCE_TYPE(rs->rs_handle)]; ++ EFRM_ASSERT(rm->rm_resources > 0); ++ --rm->rm_resources; ++ list_del(&rs->rs_manager_link); ++ list_del(&rs->rs_client_link); ++ } ++ spin_unlock_irqrestore(&efrm_nic_tablep->lock, lock_flags); ++ return free_rs; ++} ++EXPORT_SYMBOL(__efrm_resource_release); ++ ++/* ++ * vi: sw=8:ai:aw ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/resources.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,94 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains resource managers initialisation functions. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++ ++int ++efrm_resources_init(const struct vi_resource_dimensions *vi_res_dim, ++ int buffer_table_min, int buffer_table_lim) ++{ ++ int i, rc; ++ ++ rc = efrm_buffer_table_ctor(buffer_table_min, buffer_table_lim); ++ if (rc != 0) ++ return rc; ++ ++ /* Create resources in the correct order */ ++ for (i = 0; i < EFRM_RESOURCE_NUM; ++i) { ++ struct efrm_resource_manager **rmp = &efrm_rm_table[i]; ++ ++ EFRM_ASSERT(*rmp == NULL); ++ switch (i) { ++ case EFRM_RESOURCE_VI: ++ rc = efrm_create_vi_resource_manager(rmp, ++ vi_res_dim); ++ break; ++ case EFRM_RESOURCE_FILTER: ++ rc = efrm_create_filter_resource_manager(rmp); ++ break; ++ case EFRM_RESOURCE_IOBUFSET: ++ rc = efrm_create_iobufset_resource_manager(rmp); ++ break; ++ default: ++ rc = 0; ++ break; ++ } ++ ++ if (rc < 0) { ++ EFRM_ERR("%s: failed type=%d (%d)", ++ __func__, i, rc); ++ efrm_buffer_table_dtor(); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++void efrm_resources_fini(void) ++{ ++ int i; ++ ++ for (i = EFRM_RESOURCE_NUM - 1; i >= 0; --i) ++ if (efrm_rm_table[i]) { ++ efrm_resource_manager_dtor(efrm_rm_table[i]); ++ efrm_rm_table[i] = NULL; ++ } ++ ++ efrm_buffer_table_dtor(); ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/vi_resource_alloc.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,820 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains allocation of VI resources. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "efrm_internal.h" ++ ++ ++/*** Data definitions ****************************************************/ ++ ++static const char *dmaq_names[] = { "TX", "RX" }; ++ ++struct vi_resource_manager *efrm_vi_manager; ++ ++/*** Forward references **************************************************/ ++ ++static int ++efrm_vi_resource_alloc_or_free(struct efrm_client *client, ++ int alloc, struct vi_resource *evq_virs, ++ uint16_t vi_flags, int32_t evq_capacity, ++ int32_t txq_capacity, int32_t rxq_capacity, ++ uint8_t tx_q_tag, uint8_t rx_q_tag, ++ struct vi_resource **virs_in_out); ++ ++/*** Reference count handling ********************************************/ ++ ++static inline void efrm_vi_rm_get_ref(struct vi_resource *virs) ++{ ++ atomic_inc(&virs->evq_refs); ++} ++ ++static inline void efrm_vi_rm_drop_ref(struct vi_resource *virs) ++{ ++ EFRM_ASSERT(atomic_read(&virs->evq_refs) != 0); ++ if (atomic_dec_and_test(&virs->evq_refs)) ++ efrm_vi_resource_alloc_or_free(virs->rs.rs_client, false, NULL, ++ 0, 0, 0, 0, 0, 0, &virs); ++} ++ ++/*** Instance numbers ****************************************************/ ++ ++static inline int efrm_vi_rm_alloc_id(uint16_t vi_flags, int32_t evq_capacity) ++{ ++ irq_flags_t lock_flags; ++ int instance; ++ int rc; ++ ++ if (efrm_nic_tablep->a_nic == NULL) /* ?? FIXME: surely not right */ ++ return -ENODEV; ++ ++ spin_lock_irqsave(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ /* Falcon A1 RX phys addr wierdness. */ ++ if (efrm_nic_tablep->a_nic->devtype.variant == 'A' && ++ (vi_flags & EFHW_VI_RX_PHYS_ADDR_EN)) { ++ if (vi_flags & EFHW_VI_JUMBO_EN) { ++ /* Falcon-A cannot do phys + scatter. */ ++ EFRM_WARN ++ ("%s: falcon-A does not support phys+scatter mode", ++ __func__); ++ instance = -1; ++ } else if (efrm_vi_manager->iscsi_dmaq_instance_is_free ++ && evq_capacity == 0) { ++ /* Falcon-A has a single RXQ that gives the correct ++ * semantics for physical addressing. However, it ++ * happens to have the same instance number as the ++ * 'char' event queue, so we cannot also hand out ++ * the event queue. */ ++ efrm_vi_manager->iscsi_dmaq_instance_is_free = false; ++ instance = FALCON_A1_ISCSI_DMAQ; ++ } else { ++ EFRM_WARN("%s: iSCSI receive queue not free", ++ __func__); ++ instance = -1; ++ } ++ goto unlock_out; ++ } ++ ++ if (vi_flags & EFHW_VI_RM_WITH_INTERRUPT) { ++ rc = __kfifo_get(efrm_vi_manager->instances_with_interrupt, ++ (unsigned char *)&instance, sizeof(instance)); ++ if (rc != sizeof(instance)) { ++ EFRM_ASSERT(rc == 0); ++ instance = -1; ++ } ++ goto unlock_out; ++ } ++ ++ /* Otherwise a normal run-of-the-mill VI. */ ++ rc = __kfifo_get(efrm_vi_manager->instances_with_timer, ++ (unsigned char *)&instance, sizeof(instance)); ++ if (rc != sizeof(instance)) { ++ EFRM_ASSERT(rc == 0); ++ instance = -1; ++ } ++ ++unlock_out: ++ spin_unlock_irqrestore(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ return instance; ++} ++ ++static void efrm_vi_rm_free_id(int instance) ++{ ++ irq_flags_t lock_flags; ++ struct kfifo *instances; ++ ++ if (efrm_nic_tablep->a_nic == NULL) /* ?? FIXME: surely not right */ ++ return; ++ ++ if (efrm_nic_tablep->a_nic->devtype.variant == 'A' && ++ instance == FALCON_A1_ISCSI_DMAQ) { ++ EFRM_ASSERT(efrm_vi_manager->iscsi_dmaq_instance_is_free == ++ false); ++ spin_lock_irqsave(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ efrm_vi_manager->iscsi_dmaq_instance_is_free = true; ++ spin_unlock_irqrestore(&efrm_vi_manager->rm.rm_lock, ++ lock_flags); ++ } else { ++ if (instance >= efrm_vi_manager->with_timer_base && ++ instance < efrm_vi_manager->with_timer_limit) { ++ instances = efrm_vi_manager->instances_with_timer; ++ } else { ++ EFRM_ASSERT(instance >= ++ efrm_vi_manager->with_interrupt_base); ++ EFRM_ASSERT(instance < ++ efrm_vi_manager->with_interrupt_limit); ++ instances = efrm_vi_manager->instances_with_interrupt; ++ } ++ ++ EFRM_VERIFY_EQ(kfifo_put(instances, (unsigned char *)&instance, ++ sizeof(instance)), sizeof(instance)); ++ } ++} ++ ++/*** Queue sizes *********************************************************/ ++ ++/* NB. This should really take a nic as an argument, but that makes ++ * the buffer table allocation difficult. */ ++uint32_t efrm_vi_rm_evq_bytes(struct vi_resource *virs ++ /*,struct efhw_nic *nic */) ++{ ++ return virs->evq_capacity * sizeof(efhw_event_t); ++} ++EXPORT_SYMBOL(efrm_vi_rm_evq_bytes); ++ ++/* NB. This should really take a nic as an argument, but that makes ++ * the buffer table allocation difficult. */ ++uint32_t efrm_vi_rm_txq_bytes(struct vi_resource *virs ++ /*,struct efhw_nic *nic */) ++{ ++ return virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX] * ++ FALCON_DMA_TX_DESC_BYTES; ++} ++EXPORT_SYMBOL(efrm_vi_rm_txq_bytes); ++ ++/* NB. This should really take a nic as an argument, but that makes ++ * the buffer table allocation difficult. */ ++uint32_t efrm_vi_rm_rxq_bytes(struct vi_resource *virs ++ /*,struct efhw_nic *nic */) ++{ ++ uint32_t bytes_per_desc = ((virs->flags & EFHW_VI_RX_PHYS_ADDR_EN) ++ ? FALCON_DMA_RX_PHYS_DESC_BYTES ++ : FALCON_DMA_RX_BUF_DESC_BYTES); ++ return virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX] * bytes_per_desc; ++} ++EXPORT_SYMBOL(efrm_vi_rm_rxq_bytes); ++ ++static int choose_size(int size_rq, unsigned sizes) ++{ ++ int size; ++ ++ /* size_rq < 0 means default, but we interpret this as 'minimum'. */ ++ ++ for (size = 256;; size <<= 1) ++ if ((size & sizes) && size >= size_rq) ++ return size; ++ else if ((sizes & ~((size - 1) | size)) == 0) ++ return -1; ++} ++ ++static int ++efrm_vi_rm_adjust_alloc_request(struct vi_resource *virs, struct efhw_nic *nic) ++{ ++ int capacity; ++ ++ EFRM_ASSERT(nic->efhw_func); ++ ++ if (virs->evq_capacity) { ++ capacity = choose_size(virs->evq_capacity, nic->evq_sizes); ++ if (capacity < 0) { ++ EFRM_ERR("vi_resource: bad evq size %d (supported=%x)", ++ virs->evq_capacity, nic->evq_sizes); ++ return -E2BIG; ++ } ++ virs->evq_capacity = capacity; ++ } ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX]) { ++ capacity = ++ choose_size(virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX], ++ nic->txq_sizes); ++ if (capacity < 0) { ++ EFRM_ERR("vi_resource: bad txq size %d (supported=%x)", ++ virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX], ++ nic->txq_sizes); ++ return -E2BIG; ++ } ++ virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX] = capacity; ++ } ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX]) { ++ capacity = ++ choose_size(virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX], ++ nic->rxq_sizes); ++ if (capacity < 0) { ++ EFRM_ERR("vi_resource: bad rxq size %d (supported=%x)", ++ virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX], ++ nic->rxq_sizes); ++ return -E2BIG; ++ } ++ virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX] = capacity; ++ } ++ ++ return 0; ++} ++ ++/* remove the reference to the event queue in this VI resource and decrement ++ the event queue's use count */ ++static inline void efrm_vi_rm_detach_evq(struct vi_resource *virs) ++{ ++ struct vi_resource *evq_virs; ++ ++ EFRM_ASSERT(virs != NULL); ++ ++ evq_virs = virs->evq_virs; ++ ++ if (evq_virs != NULL) { ++ virs->evq_virs = NULL; ++ if (evq_virs == virs) { ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT ++ " had internal event queue ", __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle)); ++ } else { ++ efrm_vi_rm_drop_ref(evq_virs); ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT " had event queue " ++ EFRM_RESOURCE_FMT, __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle), ++ EFRM_RESOURCE_PRI_ARG(evq_virs->rs. ++ rs_handle)); ++ } ++ } else { ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT ++ " had no event queue (nothing to do)", ++ __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle)); ++ } ++} ++ ++/*** Buffer Table allocations ********************************************/ ++ ++static int ++efrm_vi_rm_alloc_or_free_buffer_table(struct vi_resource *virs, bool is_alloc) ++{ ++ uint32_t bytes; ++ int page_order; ++ int rc; ++ ++ if (!is_alloc) ++ goto destroy; ++ ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX]) { ++ bytes = efrm_vi_rm_txq_bytes(virs); ++ page_order = get_order(bytes); ++ rc = efrm_buffer_table_alloc(page_order, ++ (virs->dmaq_buf_tbl_alloc + ++ EFRM_VI_RM_DMA_QUEUE_TX)); ++ if (rc != 0) { ++ EFRM_TRACE ++ ("%s: Error %d allocating TX buffer table entry", ++ __func__, rc); ++ goto fail_txq_alloc; ++ } ++ } ++ ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX]) { ++ bytes = efrm_vi_rm_rxq_bytes(virs); ++ page_order = get_order(bytes); ++ rc = efrm_buffer_table_alloc(page_order, ++ (virs->dmaq_buf_tbl_alloc + ++ EFRM_VI_RM_DMA_QUEUE_RX)); ++ if (rc != 0) { ++ EFRM_TRACE ++ ("%s: Error %d allocating RX buffer table entry", ++ __func__, rc); ++ goto fail_rxq_alloc; ++ } ++ } ++ return 0; ++ ++destroy: ++ rc = 0; ++ ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX]) { ++ efrm_buffer_table_free(&virs-> ++ dmaq_buf_tbl_alloc ++ [EFRM_VI_RM_DMA_QUEUE_RX]); ++ } ++fail_rxq_alloc: ++ ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX]) { ++ efrm_buffer_table_free(&virs-> ++ dmaq_buf_tbl_alloc ++ [EFRM_VI_RM_DMA_QUEUE_TX]); ++ } ++fail_txq_alloc: ++ ++ return rc; ++} ++ ++/*** Per-NIC allocations *************************************************/ ++ ++static inline int ++efrm_vi_rm_init_evq(struct vi_resource *virs, struct efhw_nic *nic) ++{ ++ int instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ struct eventq_resource_hardware *evq_hw = ++ &virs->nic_info.evq_pages; ++ uint32_t buf_bytes = efrm_vi_rm_evq_bytes(virs); ++ int rc; ++ ++ if (virs->evq_capacity == 0) ++ return 0; ++ evq_hw->capacity = virs->evq_capacity; ++ ++ /* Allocate buffer table entries to map onto the iobuffer. This ++ * currently allocates its own buffer table entries on Falcon which is ++ * a bit wasteful on a multi-NIC system. */ ++ evq_hw->buf_tbl_alloc.base = (unsigned)-1; ++ rc = efrm_buffer_table_alloc(get_order(buf_bytes), ++ &evq_hw->buf_tbl_alloc); ++ if (rc < 0) { ++ EFHW_WARN("%s: failed (%d) to alloc %d buffer table entries", ++ __func__, rc, get_order(buf_bytes)); ++ return rc; ++ } ++ ++ /* Allocate the event queue memory. */ ++ rc = efhw_nic_event_queue_alloc_iobuffer(nic, evq_hw, instance, ++ buf_bytes); ++ if (rc != 0) { ++ EFRM_ERR("%s: Error allocating iobuffer: %d", __func__, rc); ++ efrm_buffer_table_free(&evq_hw->buf_tbl_alloc); ++ return rc; ++ } ++ ++ /* Initialise the event queue hardware */ ++ efhw_nic_event_queue_enable(nic, instance, virs->evq_capacity, ++ efhw_iopages_dma_addr(&evq_hw->iobuff) + ++ evq_hw->iobuff_off, ++ evq_hw->buf_tbl_alloc.base, ++ instance < 64); ++ ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT " capacity=%u", __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle), ++ virs->evq_capacity); ++ ++#if defined(__ia64__) ++ /* Page size may be large, so for now just increase the ++ * size of the requested evq up to a round number of ++ * pages ++ */ ++ buf_bytes = CI_ROUNDUP(buf_bytes, PAGE_SIZE); ++#endif ++ EFRM_ASSERT(buf_bytes % PAGE_SIZE == 0); ++ ++ virs->mem_mmap_bytes += buf_bytes; ++ ++ return 0; ++} ++ ++static inline void ++efrm_vi_rm_fini_evq(struct vi_resource *virs, struct efhw_nic *nic) ++{ ++ int instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ struct vi_resource_nic_info *nic_info = &virs->nic_info; ++ ++ if (virs->evq_capacity == 0) ++ return; ++ ++ /* Zero the timer-value for this queue. ++ And Tell NIC to stop using this event queue. */ ++ efhw_nic_event_queue_disable(nic, instance, 0); ++ ++ if (nic_info->evq_pages.buf_tbl_alloc.base != (unsigned)-1) ++ efrm_buffer_table_free(&nic_info->evq_pages.buf_tbl_alloc); ++ ++ efhw_iopages_free(nic, &nic_info->evq_pages.iobuff); ++} ++ ++/*! FIXME: we should make sure this number is never zero (=> unprotected) */ ++/*! FIXME: put this definition in a relevant header (e.g. as (evqid)+1) */ ++#define EFAB_EVQ_OWNER_ID(evqid) ((evqid)) ++ ++void ++efrm_vi_rm_init_dmaq(struct vi_resource *virs, int queue_type, ++ struct efhw_nic *nic) ++{ ++ int instance; ++ int evq_instance; ++ efhw_buffer_addr_t buf_addr; ++ ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ evq_instance = EFRM_RESOURCE_INSTANCE(virs->evq_virs->rs.rs_handle); ++ ++ buf_addr = virs->dmaq_buf_tbl_alloc[queue_type].base; ++ ++ if (queue_type == EFRM_VI_RM_DMA_QUEUE_TX) { ++ efhw_nic_dmaq_tx_q_init(nic, ++ instance, /* dmaq */ ++ evq_instance, /* evq */ ++ EFAB_EVQ_OWNER_ID(evq_instance), /* owner */ ++ virs->dmaq_tag[queue_type], /* tag */ ++ virs->dmaq_capacity[queue_type], /* size of queue */ ++ buf_addr, /* buffer index */ ++ virs->flags); /* user specified Q attrs */ ++ } else { ++ efhw_nic_dmaq_rx_q_init(nic, ++ instance, /* dmaq */ ++ evq_instance, /* evq */ ++ EFAB_EVQ_OWNER_ID(evq_instance), /* owner */ ++ virs->dmaq_tag[queue_type], /* tag */ ++ virs->dmaq_capacity[queue_type], /* size of queue */ ++ buf_addr, /* buffer index */ ++ virs->flags); /* user specified Q attrs */ ++ } ++} ++ ++static int ++efrm_vi_rm_init_or_fini_dmaq(struct vi_resource *virs, ++ int queue_type, int init, ++ struct efhw_nic *nic) ++{ ++ int rc; ++ int instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ uint32_t buf_bytes; ++ struct vi_resource_nic_info *nic_info = &virs->nic_info; ++ int page_order; ++ uint32_t num_pages; ++ struct efhw_iopages *iobuff; ++ ++ if (!init) ++ goto destroy; ++ ++ /* Ignore disabled queues. */ ++ if (virs->dmaq_capacity[queue_type] == 0) { ++ if (queue_type == EFRM_VI_RM_DMA_QUEUE_TX) ++ efhw_nic_dmaq_tx_q_disable(nic, instance); ++ else ++ efhw_nic_dmaq_rx_q_disable(nic, instance); ++ return 0; ++ } ++ ++ buf_bytes = (queue_type == EFRM_VI_RM_DMA_QUEUE_TX ++ ? efrm_vi_rm_txq_bytes(virs) ++ : efrm_vi_rm_rxq_bytes(virs)); ++ ++ page_order = get_order(buf_bytes); ++ ++ rc = efhw_iopages_alloc(nic, &nic_info->dmaq_pages[queue_type], ++ page_order); ++ if (rc != 0) { ++ EFRM_ERR("%s: Failed to allocate %s DMA buffer.", __func__, ++ dmaq_names[queue_type]); ++ goto fail_iopages; ++ } ++ ++ num_pages = 1 << page_order; ++ iobuff = &nic_info->dmaq_pages[queue_type]; ++ efhw_nic_buffer_table_set_n(nic, ++ virs->dmaq_buf_tbl_alloc[queue_type].base, ++ efhw_iopages_dma_addr(iobuff), ++ EFHW_NIC_PAGE_SIZE, 0, num_pages, 0); ++ ++ falcon_nic_buffer_table_confirm(nic); ++ ++ virs->mem_mmap_bytes += roundup(buf_bytes, PAGE_SIZE); ++ ++ /* Make sure there is an event queue. */ ++ if (virs->evq_virs->evq_capacity <= 0) { ++ EFRM_ERR("%s: Cannot use empty event queue for %s DMA", ++ __func__, dmaq_names[queue_type]); ++ rc = -EINVAL; ++ goto fail_evq; ++ } ++ ++ efrm_vi_rm_init_dmaq(virs, queue_type, nic); ++ ++ return 0; ++ ++destroy: ++ rc = 0; ++ ++ /* Ignore disabled queues. */ ++ if (virs->dmaq_capacity[queue_type] == 0) ++ return 0; ++ ++ /* Ensure TX pacing turned off -- queue flush doesn't reset this. */ ++ if (queue_type == EFRM_VI_RM_DMA_QUEUE_TX) ++ falcon_nic_pace(nic, instance, 0); ++ ++ /* No need to disable the queue here. Nobody is using it anyway. */ ++ ++fail_evq: ++ efhw_iopages_free(nic, &nic_info->dmaq_pages[queue_type]); ++fail_iopages: ++ ++ return rc; ++} ++ ++static int ++efrm_vi_rm_init_or_fini_nic(struct vi_resource *virs, int init, ++ struct efhw_nic *nic) ++{ ++ int rc; ++#ifndef NDEBUG ++ int instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++#endif ++ ++ if (!init) ++ goto destroy; ++ ++ rc = efrm_vi_rm_init_evq(virs, nic); ++ if (rc != 0) ++ goto fail_evq; ++ ++ rc = efrm_vi_rm_init_or_fini_dmaq(virs, EFRM_VI_RM_DMA_QUEUE_TX, ++ init, nic); ++ if (rc != 0) ++ goto fail_txq; ++ ++ rc = efrm_vi_rm_init_or_fini_dmaq(virs, EFRM_VI_RM_DMA_QUEUE_RX, ++ init, nic); ++ if (rc != 0) ++ goto fail_rxq; ++ ++ /* Allocate space for the control page. */ ++ EFRM_ASSERT(falcon_tx_dma_page_offset(instance) < PAGE_SIZE); ++ EFRM_ASSERT(falcon_rx_dma_page_offset(instance) < PAGE_SIZE); ++ EFRM_ASSERT(falcon_timer_page_offset(instance) < PAGE_SIZE); ++ virs->bar_mmap_bytes += PAGE_SIZE; ++ ++ return 0; ++ ++destroy: ++ rc = 0; ++ ++ efrm_vi_rm_init_or_fini_dmaq(virs, EFRM_VI_RM_DMA_QUEUE_RX, ++ false, nic); ++fail_rxq: ++ ++ efrm_vi_rm_init_or_fini_dmaq(virs, EFRM_VI_RM_DMA_QUEUE_TX, ++ false, nic); ++fail_txq: ++ ++ efrm_vi_rm_fini_evq(virs, nic); ++fail_evq: ++ ++ EFRM_ASSERT(rc != 0 || !init); ++ return rc; ++} ++ ++static int ++efrm_vi_resource_alloc_or_free(struct efrm_client *client, ++ int alloc, struct vi_resource *evq_virs, ++ uint16_t vi_flags, int32_t evq_capacity, ++ int32_t txq_capacity, int32_t rxq_capacity, ++ uint8_t tx_q_tag, uint8_t rx_q_tag, ++ struct vi_resource **virs_in_out) ++{ ++ struct efhw_nic *nic = client->nic; ++ struct vi_resource *virs; ++ int rc; ++ int instance; ++ ++ EFRM_ASSERT(virs_in_out); ++ EFRM_ASSERT(efrm_vi_manager); ++ EFRM_RESOURCE_MANAGER_ASSERT_VALID(&efrm_vi_manager->rm); ++ ++ if (!alloc) ++ goto destroy; ++ ++ rx_q_tag &= (1 << TX_DESCQ_LABEL_WIDTH) - 1; ++ tx_q_tag &= (1 << RX_DESCQ_LABEL_WIDTH) - 1; ++ ++ virs = kmalloc(sizeof(*virs), GFP_KERNEL); ++ if (virs == NULL) { ++ EFRM_ERR("%s: Error allocating VI resource object", ++ __func__); ++ rc = -ENOMEM; ++ goto fail_alloc; ++ } ++ memset(virs, 0, sizeof(*virs)); ++ ++ /* Some macros make the assumption that the struct efrm_resource is ++ * the first member of a struct vi_resource. */ ++ EFRM_ASSERT(&virs->rs == (struct efrm_resource *) (virs)); ++ ++ instance = efrm_vi_rm_alloc_id(vi_flags, evq_capacity); ++ if (instance < 0) { ++ /* Clear out the close list... */ ++ efrm_vi_rm_salvage_flushed_vis(); ++ instance = efrm_vi_rm_alloc_id(vi_flags, evq_capacity); ++ if (instance >= 0) ++ EFRM_TRACE("%s: Salvaged a closed VI.", __func__); ++ } ++ ++ if (instance < 0) { ++ /* Could flush resources and try again here. */ ++ EFRM_ERR("%s: Out of appropriate VI resources", __func__); ++ rc = -EBUSY; ++ goto fail_alloc_id; ++ } ++ ++ EFRM_TRACE("%s: new VI ID %d", __func__, instance); ++ efrm_resource_init(&virs->rs, EFRM_RESOURCE_VI, instance); ++ ++ /* Start with one reference. Any external VIs using the EVQ of this ++ * resource will increment this reference rather than the resource ++ * reference to avoid DMAQ flushes from waiting for other DMAQ ++ * flushes to complete. When the resource reference goes to zero, ++ * the DMAQ flush happens. When the flush completes, this reference ++ * is decremented. When this reference reaches zero, the instance ++ * is freed. */ ++ atomic_set(&virs->evq_refs, 1); ++ ++ virs->bar_mmap_bytes = 0; ++ virs->mem_mmap_bytes = 0; ++ virs->evq_capacity = evq_capacity; ++ virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX] = txq_capacity; ++ virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX] = rxq_capacity; ++ virs->dmaq_tag[EFRM_VI_RM_DMA_QUEUE_TX] = tx_q_tag; ++ virs->dmaq_tag[EFRM_VI_RM_DMA_QUEUE_RX] = rx_q_tag; ++ virs->flags = vi_flags; ++ INIT_LIST_HEAD(&virs->tx_flush_link); ++ INIT_LIST_HEAD(&virs->rx_flush_link); ++ virs->tx_flushing = 0; ++ virs->rx_flushing = 0; ++ ++ /* Adjust the queue sizes. */ ++ rc = efrm_vi_rm_adjust_alloc_request(virs, nic); ++ if (rc != 0) ++ goto fail_adjust_request; ++ ++ /* Attach the EVQ early so that we can ensure that the NIC sets ++ * match. */ ++ if (evq_virs == NULL) { ++ evq_virs = virs; ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT ++ " has no external event queue", __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle)); ++ } else { ++ /* Make sure the resource managers are the same. */ ++ if (EFRM_RESOURCE_TYPE(evq_virs->rs.rs_handle) != ++ EFRM_RESOURCE_VI) { ++ EFRM_ERR("%s: Mismatched owner for event queue VI " ++ EFRM_RESOURCE_FMT, __func__, ++ EFRM_RESOURCE_PRI_ARG(evq_virs->rs.rs_handle)); ++ return -EINVAL; ++ } ++ EFRM_ASSERT(atomic_read(&evq_virs->evq_refs) != 0); ++ efrm_vi_rm_get_ref(evq_virs); ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT " uses event queue " ++ EFRM_RESOURCE_FMT, ++ __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle), ++ EFRM_RESOURCE_PRI_ARG(evq_virs->rs.rs_handle)); ++ } ++ virs->evq_virs = evq_virs; ++ ++ rc = efrm_vi_rm_alloc_or_free_buffer_table(virs, true); ++ if (rc != 0) ++ goto fail_buffer_table; ++ ++ rc = efrm_vi_rm_init_or_fini_nic(virs, true, nic); ++ if (rc != 0) ++ goto fail_init_nic; ++ ++ efrm_client_add_resource(client, &virs->rs); ++ *virs_in_out = virs; ++ EFRM_TRACE("%s: Allocated " EFRM_RESOURCE_FMT, __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle)); ++ return 0; ++ ++destroy: ++ virs = *virs_in_out; ++ EFRM_RESOURCE_ASSERT_VALID(&virs->rs, 1); ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ ++ EFRM_TRACE("%s: Freeing %d", __func__, ++ EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle)); ++ ++ /* Destroying the VI. The reference count must be zero. */ ++ EFRM_ASSERT(atomic_read(&virs->evq_refs) == 0); ++ ++ /* The EVQ should have gone (and DMA disabled) so that this ++ * function can't be re-entered to destroy the EVQ VI. */ ++ EFRM_ASSERT(virs->evq_virs == NULL); ++ rc = 0; ++ ++fail_init_nic: ++ efrm_vi_rm_init_or_fini_nic(virs, false, nic); ++ ++ efrm_vi_rm_alloc_or_free_buffer_table(virs, false); ++fail_buffer_table: ++ ++ efrm_vi_rm_detach_evq(virs); ++ ++fail_adjust_request: ++ ++ EFRM_ASSERT(virs->evq_callback_fn == NULL); ++ EFRM_TRACE("%s: delete VI ID %d", __func__, instance); ++ efrm_vi_rm_free_id(instance); ++fail_alloc_id: ++ if (!alloc) ++ efrm_client_put(virs->rs.rs_client); ++ EFRM_DO_DEBUG(memset(virs, 0, sizeof(*virs))); ++ kfree(virs); ++fail_alloc: ++ *virs_in_out = NULL; ++ ++ return rc; ++} ++ ++/*** Resource object ****************************************************/ ++ ++int ++efrm_vi_resource_alloc(struct efrm_client *client, ++ struct vi_resource *evq_virs, ++ uint16_t vi_flags, int32_t evq_capacity, ++ int32_t txq_capacity, int32_t rxq_capacity, ++ uint8_t tx_q_tag, uint8_t rx_q_tag, ++ struct vi_resource **virs_out, ++ uint32_t *out_io_mmap_bytes, ++ uint32_t *out_mem_mmap_bytes, ++ uint32_t *out_txq_capacity, uint32_t *out_rxq_capacity) ++{ ++ int rc; ++ EFRM_ASSERT(client != NULL); ++ rc = efrm_vi_resource_alloc_or_free(client, true, evq_virs, vi_flags, ++ evq_capacity, txq_capacity, ++ rxq_capacity, tx_q_tag, rx_q_tag, ++ virs_out); ++ if (rc == 0) { ++ if (out_io_mmap_bytes != NULL) ++ *out_io_mmap_bytes = (*virs_out)->bar_mmap_bytes; ++ if (out_mem_mmap_bytes != NULL) ++ *out_mem_mmap_bytes = (*virs_out)->mem_mmap_bytes; ++ if (out_txq_capacity != NULL) ++ *out_txq_capacity = ++ (*virs_out)->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX]; ++ if (out_rxq_capacity != NULL) ++ *out_rxq_capacity = ++ (*virs_out)->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX]; ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL(efrm_vi_resource_alloc); ++ ++void efrm_vi_rm_free_flushed_resource(struct vi_resource *virs) ++{ ++ EFRM_ASSERT(virs != NULL); ++ EFRM_ASSERT(virs->rs.rs_ref_count == 0); ++ ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT, __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle)); ++ /* release the associated event queue then drop our own reference ++ * count */ ++ efrm_vi_rm_detach_evq(virs); ++ efrm_vi_rm_drop_ref(virs); ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/vi_resource_event.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,250 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains event handling for VI resource. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "efrm_internal.h" ++ ++ ++static inline int ++efrm_eventq_bytes(struct vi_resource *virs) ++{ ++ return efrm_vi_rm_evq_bytes(virs); ++} ++ ++ ++static inline efhw_event_t * ++efrm_eventq_base(struct vi_resource *virs) ++{ ++ struct eventq_resource_hardware *hw; ++ hw = &(virs->nic_info.evq_pages); ++ return (efhw_event_t *) (efhw_iopages_ptr(&(hw->iobuff)) + ++ hw->iobuff_off); ++} ++ ++ ++void ++efrm_eventq_request_wakeup(struct vi_resource *virs, unsigned current_ptr) ++{ ++ struct efhw_nic *nic = virs->rs.rs_client->nic; ++ int next_i; ++ next_i = ((current_ptr / sizeof(efhw_event_t)) & ++ (virs->evq_capacity - 1)); ++ ++ efhw_nic_wakeup_request(nic, efrm_eventq_dma_addr(virs), next_i, ++ EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle)); ++} ++EXPORT_SYMBOL(efrm_eventq_request_wakeup); ++ ++void efrm_eventq_reset(struct vi_resource *virs) ++{ ++ struct efhw_nic *nic = virs->rs.rs_client->nic; ++ int instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ ++ EFRM_ASSERT(virs->evq_capacity != 0); ++ ++ /* FIXME: Protect against concurrent resets. */ ++ ++ efhw_nic_event_queue_disable(nic, instance, 0); ++ ++ memset(efrm_eventq_base(virs), EFHW_CLEAR_EVENT_VALUE, ++ efrm_eventq_bytes(virs)); ++ efhw_nic_event_queue_enable(nic, instance, virs->evq_capacity, ++ efrm_eventq_dma_addr(virs), ++ virs->nic_info.evq_pages. ++ buf_tbl_alloc.base, ++ instance < 64); ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT, __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle)); ++} ++EXPORT_SYMBOL(efrm_eventq_reset); ++ ++int ++efrm_eventq_register_callback(struct vi_resource *virs, ++ void (*handler) (void *, int, ++ struct efhw_nic *nic), ++ void *arg) ++{ ++ struct efrm_nic_per_vi *cb_info; ++ int instance; ++ int bit; ++ ++ EFRM_RESOURCE_ASSERT_VALID(&virs->rs, 0); ++ EFRM_ASSERT(virs->evq_capacity != 0); ++ EFRM_ASSERT(handler != NULL); ++ ++ /* ?? TODO: Get rid of this test when client is compulsory. */ ++ if (virs->rs.rs_client == NULL) { ++ EFRM_ERR("%s: no client", __func__); ++ return -EINVAL; ++ } ++ ++ virs->evq_callback_arg = arg; ++ virs->evq_callback_fn = handler; ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ cb_info = &efrm_nic(virs->rs.rs_client->nic)->vis[instance]; ++ ++ /* The handler can be set only once. */ ++ bit = test_and_set_bit(VI_RESOURCE_EVQ_STATE_CALLBACK_REGISTERED, ++ &cb_info->state); ++ if (bit) ++ return -EBUSY; ++ cb_info->vi = virs; ++ ++ return 0; ++} ++EXPORT_SYMBOL(efrm_eventq_register_callback); ++ ++void efrm_eventq_kill_callback(struct vi_resource *virs) ++{ ++ struct efrm_nic_per_vi *cb_info; ++ int32_t evq_state; ++ int instance; ++ int bit; ++ ++ EFRM_RESOURCE_ASSERT_VALID(&virs->rs, 0); ++ EFRM_ASSERT(virs->evq_capacity != 0); ++ EFRM_ASSERT(virs->rs.rs_client != NULL); ++ ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ cb_info = &efrm_nic(virs->rs.rs_client->nic)->vis[instance]; ++ cb_info->vi = NULL; ++ ++ /* Disable the timer. */ ++ efhw_nic_event_queue_disable(virs->rs.rs_client->nic, ++ instance, /*timer_only */ 1); ++ ++ /* Disable the callback. */ ++ bit = test_and_clear_bit(VI_RESOURCE_EVQ_STATE_CALLBACK_REGISTERED, ++ &cb_info->state); ++ EFRM_ASSERT(bit); /* do not call me twice! */ ++ ++ /* Spin until the callback is complete. */ ++ do { ++ rmb(); ++ ++ udelay(1); ++ evq_state = cb_info->state; ++ } while ((evq_state & VI_RESOURCE_EVQ_STATE(BUSY))); ++ ++ virs->evq_callback_fn = NULL; ++} ++EXPORT_SYMBOL(efrm_eventq_kill_callback); ++ ++static void ++efrm_eventq_do_callback(struct efhw_nic *nic, unsigned instance, ++ bool is_timeout) ++{ ++ struct efrm_nic *rnic = efrm_nic(nic); ++ void (*handler) (void *, int is_timeout, struct efhw_nic *nic); ++ void *arg; ++ struct efrm_nic_per_vi *cb_info; ++ int32_t evq_state; ++ int32_t new_evq_state; ++ struct vi_resource *virs; ++ int bit; ++ ++ EFRM_ASSERT(efrm_vi_manager); ++ ++ cb_info = &rnic->vis[instance]; ++ ++ /* Set the BUSY bit and clear WAKEUP_PENDING. Do this ++ * before waking up the sleeper to avoid races. */ ++ while (1) { ++ evq_state = cb_info->state; ++ new_evq_state = evq_state; ++ ++ if ((evq_state & VI_RESOURCE_EVQ_STATE(BUSY)) != 0) { ++ EFRM_ERR("%s:%d: evq_state[%d] corrupted!", ++ __func__, __LINE__, instance); ++ return; ++ } ++ ++ if (!is_timeout) ++ new_evq_state &= ~VI_RESOURCE_EVQ_STATE(WAKEUP_PENDING); ++ ++ if (evq_state & VI_RESOURCE_EVQ_STATE(CALLBACK_REGISTERED)) { ++ new_evq_state |= VI_RESOURCE_EVQ_STATE(BUSY); ++ virs = cb_info->vi; ++ if (cmpxchg(&cb_info->state, evq_state, ++ new_evq_state) == evq_state) ++ break; ++ } else { ++ /* Just update the state if necessary. */ ++ if (new_evq_state == evq_state || ++ cmpxchg(&cb_info->state, evq_state, ++ new_evq_state) == evq_state) ++ return; ++ } ++ } ++ ++ if (virs) { ++ handler = virs->evq_callback_fn; ++ arg = virs->evq_callback_arg; ++ EFRM_ASSERT(handler != NULL); ++ handler(arg, is_timeout, nic); ++ } ++ ++ /* Clear the BUSY bit. */ ++ bit = ++ test_and_clear_bit(VI_RESOURCE_EVQ_STATE_BUSY, ++ &cb_info->state); ++ if (!bit) { ++ EFRM_ERR("%s:%d: evq_state corrupted!", ++ __func__, __LINE__); ++ } ++} ++ ++void efrm_handle_wakeup_event(struct efhw_nic *nic, unsigned instance) ++{ ++ efrm_eventq_do_callback(nic, instance, false); ++} ++ ++void efrm_handle_timeout_event(struct efhw_nic *nic, unsigned instance) ++{ ++ efrm_eventq_do_callback(nic, instance, true); ++} ++ ++void efrm_handle_sram_event(struct efhw_nic *nic) ++{ ++ if (nic->buf_commit_outstanding > 0) ++ nic->buf_commit_outstanding--; ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/vi_resource_flush.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,483 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains DMA queue flushing of VI resources. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "efrm_internal.h" ++ ++ ++/* can fail as workitem can already be scheuled -- ignore failure */ ++#define EFRM_VI_RM_DELAYED_FREE(manager) \ ++ queue_work(manager->workqueue, &manager->work_item) ++ ++static const int flush_fifo_hwm = 8 /* TODO should be a HW specific const */ ; ++ ++static void ++efrm_vi_resource_rx_flush_done(struct vi_resource *virs, bool *completed) ++{ ++ /* We should only get a flush event if there is a flush ++ * outstanding. */ ++ EFRM_ASSERT(virs->rx_flush_outstanding); ++ ++ virs->rx_flush_outstanding = 0; ++ virs->rx_flushing = 0; ++ ++ list_del(&virs->rx_flush_link); ++ efrm_vi_manager->rx_flush_outstanding_count--; ++ ++ if (virs->tx_flushing == 0) { ++ list_add_tail(&virs->rx_flush_link, ++ &efrm_vi_manager->close_pending); ++ *completed = 1; ++ } ++} ++ ++static void ++efrm_vi_resource_tx_flush_done(struct vi_resource *virs, bool *completed) ++{ ++ /* We should only get a flush event if there is a flush ++ * outstanding. */ ++ EFRM_ASSERT(virs->tx_flushing); ++ ++ virs->tx_flushing = 0; ++ ++ list_del(&virs->tx_flush_link); ++ ++ if (virs->rx_flushing == 0) { ++ list_add_tail(&virs->rx_flush_link, ++ &efrm_vi_manager->close_pending); ++ *completed = 1; ++ } ++} ++ ++static void ++efrm_vi_resource_issue_rx_flush(struct vi_resource *virs, bool *completed) ++{ ++ struct efhw_nic *nic = virs->rs.rs_client->nic; ++ int instance; ++ int rc; ++ ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ ++ list_add_tail(&virs->rx_flush_link, ++ &efrm_vi_manager->rx_flush_outstanding_list); ++ virs->rx_flush_outstanding = virs->rx_flushing; ++ efrm_vi_manager->rx_flush_outstanding_count++; ++ ++ EFRM_TRACE("%s: rx queue %d flush requested for nic %d", ++ __func__, instance, nic->index); ++ rc = efhw_nic_flush_rx_dma_channel(nic, instance); ++ if (rc == -EAGAIN) ++ efrm_vi_resource_rx_flush_done(virs, completed); ++} ++ ++static void ++efrm_vi_resource_issue_tx_flush(struct vi_resource *virs, bool *completed) ++{ ++ struct efhw_nic *nic = virs->rs.rs_client->nic; ++ int instance; ++ int rc; ++ ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ ++ list_add_tail(&virs->tx_flush_link, ++ &efrm_vi_manager->tx_flush_outstanding_list); ++ ++ EFRM_TRACE("%s: tx queue %d flush requested for nic %d", ++ __func__, instance, nic->index); ++ rc = efhw_nic_flush_tx_dma_channel(nic, instance); ++ if (rc == -EAGAIN) ++ efrm_vi_resource_tx_flush_done(virs, completed); ++} ++ ++static void efrm_vi_resource_process_waiting_flushes(bool *completed) ++{ ++ struct vi_resource *virs; ++ ++ while (efrm_vi_manager->rx_flush_outstanding_count < flush_fifo_hwm && ++ !list_empty(&efrm_vi_manager->rx_flush_waiting_list)) { ++ virs = ++ list_entry(list_pop ++ (&efrm_vi_manager->rx_flush_waiting_list), ++ struct vi_resource, rx_flush_link); ++ efrm_vi_resource_issue_rx_flush(virs, completed); ++ } ++} ++ ++#if BUG7916_WORKAROUND || BUG5302_WORKAROUND ++static void ++efrm_vi_resource_flush_retry_vi(struct vi_resource *virs, ++ int64_t time_now, bool *completed) ++{ ++ struct efhw_nic *nic; ++ int instance; ++ ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ ++ virs->flush_count++; ++ virs->flush_time = time_now; ++ nic = virs->rs.rs_client->nic; ++ ++#if BUG7916_WORKAROUND ++ if (virs->rx_flush_outstanding) { ++ EFRM_TRACE("%s: Retrying RX flush on instance %d", ++ __func__, instance); ++ ++ list_del(&virs->rx_flush_link); ++ efrm_vi_manager->rx_flush_outstanding_count--; ++ efrm_vi_resource_issue_rx_flush(virs, completed); ++ efrm_vi_resource_process_waiting_flushes(completed); ++ } ++#endif ++ ++#if BUG5302_WORKAROUND ++ if (virs->tx_flushing) { ++ if (virs->flush_count > 5) { ++ EFRM_TRACE("%s: VI resource stuck flush pending " ++ "(instance=%d, count=%d)", ++ __func__, instance, virs->flush_count); ++ falcon_clobber_tx_dma_ptrs(nic, instance); ++ } else { ++ EFRM_TRACE("%s: Retrying TX flush on instance %d", ++ __func__, instance); ++ } ++ ++ list_del(&virs->tx_flush_link); ++ efrm_vi_resource_issue_tx_flush(virs, completed); ++ } ++#endif ++} ++#endif ++ ++int efrm_vi_resource_flush_retry(struct vi_resource *virs) ++{ ++#if BUG7916_WORKAROUND || BUG5302_WORKAROUND ++ irq_flags_t lock_flags; ++ bool completed = false; ++ ++ if (virs->rx_flushing == 0 && virs->tx_flushing == 0) ++ return -EALREADY; ++ ++ spin_lock_irqsave(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ efrm_vi_resource_flush_retry_vi(virs, get_jiffies_64(), &completed); ++ spin_unlock_irqrestore(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ if (completed) ++ EFRM_VI_RM_DELAYED_FREE(efrm_vi_manager); ++#endif ++ ++ return 0; ++} ++EXPORT_SYMBOL(efrm_vi_resource_flush_retry); ++ ++#if BUG7916_WORKAROUND || BUG5302_WORKAROUND ++/* resource manager lock should be taken before this call */ ++static void efrm_vi_handle_flush_loss(bool *completed) ++{ ++ struct list_head *pos, *temp; ++ struct vi_resource *virs; ++ int64_t time_now, time_pending; ++ ++ /* It's possible we miss flushes - the list is sorted in order we ++ * generate flushes, see if any are very old. It's also possible ++ * that we decide an endpoint is flushed even though we've not ++ * received all the flush events. We *should * mark as ++ * completed, reclaim and loop again. ?? ++ * THIS NEEDS BACKPORTING FROM THE FALCON branch ++ */ ++ time_now = get_jiffies_64(); ++ ++#if BUG7916_WORKAROUND ++ list_for_each_safe(pos, temp, ++ &efrm_vi_manager->rx_flush_outstanding_list) { ++ virs = container_of(pos, struct vi_resource, rx_flush_link); ++ ++ time_pending = time_now - virs->flush_time; ++ ++ /* List entries are held in reverse chronological order. Only ++ * process the old ones. */ ++ if (time_pending <= 0x100000000LL) ++ break; ++ ++ efrm_vi_resource_flush_retry_vi(virs, time_now, completed); ++ } ++#endif ++ ++#if BUG5302_WORKAROUND ++ list_for_each_safe(pos, temp, ++ &efrm_vi_manager->tx_flush_outstanding_list) { ++ virs = container_of(pos, struct vi_resource, tx_flush_link); ++ ++ time_pending = time_now - virs->flush_time; ++ ++ /* List entries are held in reverse chronological order. ++ * Only process the old ones. */ ++ if (time_pending <= 0x100000000LL) ++ break; ++ ++ efrm_vi_resource_flush_retry_vi(virs, time_now, completed); ++ } ++#endif ++} ++#endif ++ ++void ++efrm_vi_register_flush_callback(struct vi_resource *virs, ++ void (*handler)(void *), void *arg) ++{ ++ if (handler == NULL) { ++ virs->flush_callback_fn = handler; ++ wmb(); ++ virs->flush_callback_arg = arg; ++ } else { ++ virs->flush_callback_arg = arg; ++ wmb(); ++ virs->flush_callback_fn = handler; ++ } ++} ++EXPORT_SYMBOL(efrm_vi_register_flush_callback); ++ ++int efrm_pt_flush(struct vi_resource *virs) ++{ ++ int instance; ++ irq_flags_t lock_flags; ++ bool completed = false; ++ ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ ++ EFRM_ASSERT(virs->rx_flushing == 0); ++ EFRM_ASSERT(virs->rx_flush_outstanding == 0); ++ EFRM_ASSERT(virs->tx_flushing == 0); ++ ++ EFRM_TRACE("%s: " EFRM_RESOURCE_FMT " EVQ=%d TXQ=%d RXQ=%d", ++ __func__, EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle), ++ virs->evq_capacity, ++ virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX], ++ virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX]); ++ ++ spin_lock_irqsave(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX] != 0) ++ virs->rx_flushing = 1; ++ ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX] != 0) ++ virs->tx_flushing = 1; ++ ++ /* Clean up immediately if there are no flushes. */ ++ if (virs->rx_flushing == 0 && virs->tx_flushing == 0) { ++ list_add_tail(&virs->rx_flush_link, ++ &efrm_vi_manager->close_pending); ++ completed = true; ++ } ++ ++ /* Issue the RX flush if possible or queue it for later. */ ++ if (virs->rx_flushing) { ++#if BUG7916_WORKAROUND || BUG5302_WORKAROUND ++ if (efrm_vi_manager->rx_flush_outstanding_count >= ++ flush_fifo_hwm) ++ efrm_vi_handle_flush_loss(&completed); ++#endif ++ if (efrm_vi_manager->rx_flush_outstanding_count >= ++ flush_fifo_hwm) { ++ list_add_tail(&virs->rx_flush_link, ++ &efrm_vi_manager->rx_flush_waiting_list); ++ } else { ++ efrm_vi_resource_issue_rx_flush(virs, &completed); ++ } ++ } ++ ++ /* Issue the TX flush. There's no limit to the number of ++ * outstanding TX flushes. */ ++ if (virs->tx_flushing) ++ efrm_vi_resource_issue_tx_flush(virs, &completed); ++ ++ virs->flush_time = get_jiffies_64(); ++ ++ spin_unlock_irqrestore(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ if (completed) ++ EFRM_VI_RM_DELAYED_FREE(efrm_vi_manager); ++ ++ return 0; ++} ++EXPORT_SYMBOL(efrm_pt_flush); ++ ++static void ++efrm_handle_rx_dmaq_flushed(struct efhw_nic *flush_nic, int instance, ++ bool *completed) ++{ ++ struct list_head *pos, *temp; ++ struct vi_resource *virs; ++ ++ list_for_each_safe(pos, temp, ++ &efrm_vi_manager->rx_flush_outstanding_list) { ++ virs = container_of(pos, struct vi_resource, rx_flush_link); ++ ++ if (instance == EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle)) { ++ efrm_vi_resource_rx_flush_done(virs, completed); ++ efrm_vi_resource_process_waiting_flushes(completed); ++ return; ++ } ++ } ++ EFRM_TRACE("%s: Unhandled rx flush event, nic %d, instance %d", ++ __func__, flush_nic->index, instance); ++} ++ ++static void ++efrm_handle_tx_dmaq_flushed(struct efhw_nic *flush_nic, int instance, ++ bool *completed) ++{ ++ struct list_head *pos, *temp; ++ struct vi_resource *virs; ++ ++ list_for_each_safe(pos, temp, ++ &efrm_vi_manager->tx_flush_outstanding_list) { ++ virs = container_of(pos, struct vi_resource, tx_flush_link); ++ ++ if (instance == EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle)) { ++ efrm_vi_resource_tx_flush_done(virs, completed); ++ return; ++ } ++ } ++ EFRM_TRACE("%s: Unhandled tx flush event, nic %d, instance %d", ++ __func__, flush_nic->index, instance); ++} ++ ++void ++efrm_handle_dmaq_flushed(struct efhw_nic *flush_nic, unsigned instance, ++ int rx_flush) ++{ ++ irq_flags_t lock_flags; ++ bool completed = false; ++ ++ EFRM_TRACE("%s: nic_i=%d instance=%d rx_flush=%d", __func__, ++ flush_nic->index, instance, rx_flush); ++ ++ spin_lock_irqsave(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ if (rx_flush) ++ efrm_handle_rx_dmaq_flushed(flush_nic, instance, &completed); ++ else ++ efrm_handle_tx_dmaq_flushed(flush_nic, instance, &completed); ++ ++#if BUG7916_WORKAROUND || BUG5302_WORKAROUND ++ efrm_vi_handle_flush_loss(&completed); ++#endif ++ ++ spin_unlock_irqrestore(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ if (completed) ++ EFRM_VI_RM_DELAYED_FREE(efrm_vi_manager); ++} ++ ++static void ++efrm_vi_rm_reinit_dmaqs(struct vi_resource *virs) ++{ ++ struct efhw_nic *nic = virs->rs.rs_client->nic; ++ ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_TX] != 0) ++ efrm_vi_rm_init_dmaq(virs, EFRM_VI_RM_DMA_QUEUE_TX, nic); ++ if (virs->dmaq_capacity[EFRM_VI_RM_DMA_QUEUE_RX]) ++ efrm_vi_rm_init_dmaq(virs, EFRM_VI_RM_DMA_QUEUE_RX, nic); ++} ++ ++/* free any PT endpoints whose flush has now complete */ ++void efrm_vi_rm_delayed_free(struct work_struct *data) ++{ ++ irq_flags_t lock_flags; ++ struct list_head close_pending; ++ struct vi_resource *virs; ++ ++ EFRM_RESOURCE_MANAGER_ASSERT_VALID(&efrm_vi_manager->rm); ++ ++ spin_lock_irqsave(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ list_replace_init(&efrm_vi_manager->close_pending, &close_pending); ++ spin_unlock_irqrestore(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ EFRM_TRACE("%s: %p", __func__, efrm_vi_manager); ++ while (!list_empty(&close_pending)) { ++ virs = ++ list_entry(list_pop(&close_pending), struct vi_resource, ++ rx_flush_link); ++ EFRM_TRACE("%s: flushed VI instance=%d", __func__, ++ EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle)); ++ ++ if (virs->flush_callback_fn != NULL) { ++ efrm_vi_rm_reinit_dmaqs(virs); ++ virs->flush_callback_fn(virs->flush_callback_arg); ++ } else ++ efrm_vi_rm_free_flushed_resource(virs); ++ } ++} ++ ++void efrm_vi_rm_salvage_flushed_vis(void) ++{ ++#if BUG7916_WORKAROUND || BUG5302_WORKAROUND ++ irq_flags_t lock_flags; ++ bool completed; ++ ++ spin_lock_irqsave(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ efrm_vi_handle_flush_loss(&completed); ++ spin_unlock_irqrestore(&efrm_vi_manager->rm.rm_lock, lock_flags); ++#endif ++ ++ efrm_vi_rm_delayed_free(&efrm_vi_manager->work_item); ++} ++ ++void efrm_vi_resource_free(struct vi_resource *virs) ++{ ++ efrm_vi_register_flush_callback(virs, NULL, NULL); ++ efrm_pt_flush(virs); ++} ++EXPORT_SYMBOL(efrm_vi_resource_free); ++ ++ ++void efrm_vi_resource_release(struct vi_resource *virs) ++{ ++ if (__efrm_resource_release(&virs->rs)) ++ efrm_vi_resource_free(virs); ++} ++EXPORT_SYMBOL(efrm_vi_resource_release); ++ ++/* ++ * vi: sw=8:ai:aw ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-04-21/drivers/net/sfc/sfc_resource/vi_resource_manager.c 2008-07-17 16:18:07.000000000 +0200 +@@ -0,0 +1,231 @@ ++/**************************************************************************** ++ * Driver for Solarflare network controllers - ++ * resource management for Xen backend, OpenOnload, etc ++ * (including support for SFE4001 10GBT NIC) ++ * ++ * This file contains the VI resource manager. ++ * ++ * Copyright 2005-2007: Solarflare Communications Inc, ++ * 9501 Jeronimo Road, Suite 250, ++ * Irvine, CA 92618, USA ++ * ++ * Developed and maintained by Solarflare Communications: ++ * ++ * ++ * ++ * Certain parts of the driver were implemented by ++ * Alexandra Kossovsky ++ * OKTET Labs Ltd, Russia, ++ * http://oktetlabs.ru, ++ * by request of Solarflare Communications ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, incorporated herein by reference. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ **************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "efrm_internal.h" ++ ++ ++int efrm_pt_pace(struct vi_resource *virs, unsigned int val) ++{ ++ struct efhw_nic *nic = virs->rs.rs_client->nic; ++ int instance; ++ ++ EFRM_RESOURCE_ASSERT_VALID(&virs->rs, 0); ++ instance = EFRM_RESOURCE_INSTANCE(virs->rs.rs_handle); ++ falcon_nic_pace(nic, instance, val); ++ EFRM_TRACE("%s[%d]=%d DONE", __func__, instance, val); ++ return 0; ++} ++EXPORT_SYMBOL(efrm_pt_pace); ++ ++/*** Resource manager creation/destruction *******************************/ ++ ++static void efrm_vi_rm_dtor(struct efrm_resource_manager *rm); ++ ++static int ++efrm_create_or_destroy_vi_resource_manager( ++ struct efrm_resource_manager **rm_in_out, ++ const struct vi_resource_dimensions *dims, ++ bool destroy) ++{ ++ struct vi_resource *virs; ++ struct list_head *pos, *temp; ++ struct list_head flush_pending; ++ irq_flags_t lock_flags; ++ int rc; ++ unsigned dmaq_min, dmaq_lim; ++ ++ EFRM_ASSERT(rm_in_out); ++ ++ if (destroy) ++ goto destroy; ++ ++ EFRM_ASSERT(dims); ++ EFRM_NOTICE("vi_resource_manager: evq_int=%u-%u evq_timer=%u-%u", ++ dims->evq_int_min, dims->evq_int_lim, ++ dims->evq_timer_min, dims->evq_timer_lim); ++ EFRM_NOTICE("vi_resource_manager: rxq=%u-%u txq=%u-%u", ++ dims->rxq_min, dims->rxq_lim, ++ dims->txq_min, dims->txq_lim); ++ ++ efrm_vi_manager = kmalloc(sizeof(*efrm_vi_manager), GFP_KERNEL); ++ if (efrm_vi_manager == NULL) { ++ rc = -ENOMEM; ++ goto fail_alloc; ++ } ++ ++ memset(efrm_vi_manager, 0, sizeof(*efrm_vi_manager)); ++ ++ efrm_vi_manager->iscsi_dmaq_instance_is_free = true; ++ ++ dmaq_min = max(dims->rxq_min, dims->txq_min); ++ dmaq_lim = min(dims->rxq_lim, dims->txq_lim); ++ ++ efrm_vi_manager->with_timer_base = ++ max(dmaq_min, dims->evq_timer_min); ++ efrm_vi_manager->with_timer_limit = ++ min(dmaq_lim, dims->evq_timer_lim); ++ rc = efrm_kfifo_id_ctor(&efrm_vi_manager->instances_with_timer, ++ efrm_vi_manager->with_timer_base, ++ efrm_vi_manager->with_timer_limit, ++ &efrm_vi_manager->rm.rm_lock); ++ if (rc < 0) ++ goto fail_with_timer_id_pool; ++ ++ efrm_vi_manager->with_interrupt_base = ++ max(dmaq_min, dims->evq_int_min); ++ efrm_vi_manager->with_interrupt_limit = ++ min(dmaq_lim, dims->evq_int_lim); ++ efrm_vi_manager->with_interrupt_limit = ++ max(efrm_vi_manager->with_interrupt_limit, ++ efrm_vi_manager->with_interrupt_base); ++ rc = efrm_kfifo_id_ctor(&efrm_vi_manager->instances_with_interrupt, ++ efrm_vi_manager->with_interrupt_base, ++ efrm_vi_manager->with_interrupt_limit, ++ &efrm_vi_manager->rm.rm_lock); ++ if (rc < 0) ++ goto fail_with_int_id_pool; ++ ++ INIT_LIST_HEAD(&efrm_vi_manager->rx_flush_waiting_list); ++ INIT_LIST_HEAD(&efrm_vi_manager->rx_flush_outstanding_list); ++ INIT_LIST_HEAD(&efrm_vi_manager->tx_flush_outstanding_list); ++ efrm_vi_manager->rx_flush_outstanding_count = 0; ++ ++ INIT_LIST_HEAD(&efrm_vi_manager->close_pending); ++ efrm_vi_manager->workqueue = create_workqueue("sfc_vi"); ++ if (efrm_vi_manager->workqueue == NULL) ++ goto fail_create_workqueue; ++ INIT_WORK(&efrm_vi_manager->work_item, efrm_vi_rm_delayed_free); ++ ++ /* NB. This must be the last step to avoid things getting tangled. ++ * efrm_resource_manager_dtor calls the vi_rm_dtor which ends up in ++ * this function. */ ++ rc = efrm_resource_manager_ctor(&efrm_vi_manager->rm, efrm_vi_rm_dtor, ++ "VI", EFRM_RESOURCE_VI); ++ if (rc < 0) ++ goto fail_rm_ctor; ++ ++ *rm_in_out = &efrm_vi_manager->rm; ++ return 0; ++ ++destroy: ++ rc = 0; ++ EFRM_RESOURCE_MANAGER_ASSERT_VALID(*rm_in_out); ++ ++ /* Abort outstanding flushes. Note, a VI resource can be on more ++ * than one of these lists. We handle this by starting with the TX ++ * list and then append VIs to this list if they aren't on the TX ++ * list already. A VI is on the TX flush list if tx_flushing ++ * is not empty. */ ++ spin_lock_irqsave(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ list_replace_init(&efrm_vi_manager->tx_flush_outstanding_list, ++ &flush_pending); ++ ++ list_for_each_safe(pos, temp, ++ &efrm_vi_manager->rx_flush_waiting_list) { ++ virs = container_of(pos, struct vi_resource, rx_flush_link); ++ ++ list_del(&virs->rx_flush_link); ++ if (virs->tx_flushing == 0) ++ list_add_tail(&virs->tx_flush_link, &flush_pending); ++ } ++ ++ list_for_each_safe(pos, temp, ++ &efrm_vi_manager->rx_flush_outstanding_list) { ++ virs = container_of(pos, struct vi_resource, rx_flush_link); ++ ++ list_del(&virs->rx_flush_link); ++ if (virs->tx_flushing == 0) ++ list_add_tail(&virs->tx_flush_link, &flush_pending); ++ } ++ ++ spin_unlock_irqrestore(&efrm_vi_manager->rm.rm_lock, lock_flags); ++ ++ while (!list_empty(&flush_pending)) { ++ virs = ++ list_entry(list_pop(&flush_pending), struct vi_resource, ++ tx_flush_link); ++ EFRM_TRACE("%s: found PT endpoint " EFRM_RESOURCE_FMT ++ " with flush pending [Tx=0x%x, Rx=0x%x, RxO=0x%x]", ++ __func__, ++ EFRM_RESOURCE_PRI_ARG(virs->rs.rs_handle), ++ virs->tx_flushing, ++ virs->rx_flushing, ++ virs->rx_flush_outstanding); ++ efrm_vi_rm_free_flushed_resource(virs); ++ } ++ ++fail_rm_ctor: ++ ++ /* Complete outstanding closes. */ ++ destroy_workqueue(efrm_vi_manager->workqueue); ++fail_create_workqueue: ++ EFRM_ASSERT(list_empty(&efrm_vi_manager->close_pending)); ++ kfifo_vfree(efrm_vi_manager->instances_with_interrupt); ++fail_with_int_id_pool: ++ ++ kfifo_vfree(efrm_vi_manager->instances_with_timer); ++fail_with_timer_id_pool: ++ ++ if (destroy) ++ return 0; ++ ++ EFRM_DO_DEBUG(memset(efrm_vi_manager, 0, sizeof(*efrm_vi_manager))); ++ kfree(efrm_vi_manager); ++fail_alloc: ++ ++ *rm_in_out = NULL; ++ EFRM_ERR("%s: failed rc=%d", __func__, rc); ++ return rc; ++} ++ ++int ++efrm_create_vi_resource_manager(struct efrm_resource_manager **rm_out, ++ const struct vi_resource_dimensions *dims) ++{ ++ return efrm_create_or_destroy_vi_resource_manager(rm_out, dims, false); ++} ++ ++static void efrm_vi_rm_dtor(struct efrm_resource_manager *rm) ++{ ++ efrm_create_or_destroy_vi_resource_manager(&rm, NULL, true); ++} diff --git a/patches.xen/sfc-set-arch b/patches.xen/sfc-set-arch new file mode 100644 index 0000000..a1ef8da --- /dev/null +++ b/patches.xen/sfc-set-arch @@ -0,0 +1,38 @@ +From: Kieran Mansley +Subject: set efhw_arch field of device type +References: bnc#489105 +Patch-mainline: n/a + +Acked-by: jbeulich@novell.com + +--- head-2009-04-07.orig/drivers/net/sfc/sfc_resource/ci/efhw/common.h 2009-04-07 14:39:57.000000000 +0200 ++++ head-2009-04-07/drivers/net/sfc/sfc_resource/ci/efhw/common.h 2009-04-07 15:02:05.000000000 +0200 +@@ -41,6 +41,10 @@ + + #include + ++enum efhw_arch { ++ EFHW_ARCH_FALCON, ++}; ++ + typedef uint32_t efhw_buffer_addr_t; + #define EFHW_BUFFER_ADDR_FMT "[ba:%"PRIx32"]" + +--- head-2009-04-07.orig/drivers/net/sfc/sfc_resource/nic.c 2009-04-07 14:39:57.000000000 +0200 ++++ head-2009-04-07/drivers/net/sfc/sfc_resource/nic.c 2009-04-07 15:02:05.000000000 +0200 +@@ -47,6 +47,7 @@ int efhw_device_type_init(struct efhw_de + switch (device_id) { + case 0x0703: + case 0x6703: ++ dt->arch = EFHW_ARCH_FALCON; + dt->variant = 'A'; + switch (class_revision) { + case 0: +@@ -60,6 +61,7 @@ int efhw_device_type_init(struct efhw_de + } + break; + case 0x0710: ++ dt->arch = EFHW_ARCH_FALCON; + dt->variant = 'B'; + switch (class_revision) { + case 2: diff --git a/patches.xen/tmem b/patches.xen/tmem new file mode 100644 index 0000000..9cb58df --- /dev/null +++ b/patches.xen/tmem @@ -0,0 +1,1395 @@ +Subject: Transcendent memory ("tmem") for Linux +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 908:baeb818cd2dc) +Patch-mainline: n/a + +Tmem, when called from a tmem-capable (paravirtualized) guest, makes +use of otherwise unutilized ("fallow") memory to create and manage +pools of pages that can be accessed from the guest either as +"ephemeral" pages or as "persistent" pages. In either case, the pages +are not directly addressible by the guest, only copied to and fro via +the tmem interface. Ephemeral pages are a nice place for a guest to +put recently evicted clean pages that it might need again; these pages +can be reclaimed synchronously by Xen for other guests or other uses. +Persistent pages are a nice place for a guest to put "swap" pages to +avoid sending them to disk. These pages retain data as long as the +guest lives, but count against the guest memory allocation. + +This patch contains the Linux paravirtualization changes to +complement the tmem Xen patch (xen-unstable c/s 19646). It +implements "precache" (ext3 only as of now), "preswap", +and limited "shared precache" (ocfs2 only as of now) support. +CONFIG options are required to turn on +the support (but in this patch they default to "y"). If +the underlying Xen does not have tmem support or has it +turned off, this is sensed early to avoid nearly all +hypercalls. + +Lots of useful prose about tmem can be found at +http://oss.oracle.com/projects/tmem + +Signed-off-by: Dan Magenheimer +Acked-by: jbeulich@novell.com + +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/Documentation/transcendent-memory.txt 2010-03-24 14:09:47.000000000 +0100 +@@ -0,0 +1,176 @@ ++Normal memory is directly addressable by the kernel, of a known ++normally-fixed size, synchronously accessible, and persistent (though ++not across a reboot). ++ ++What if there was a class of memory that is of unknown and dynamically ++variable size, is addressable only indirectly by the kernel, can be ++configured either as persistent or as "ephemeral" (meaning it will be ++around for awhile, but might disappear without warning), and is still ++fast enough to be synchronously accessible? ++ ++We call this latter class "transcendent memory" and it provides an ++interesting opportunity to more efficiently utilize RAM in a virtualized ++environment. However this "memory but not really memory" may also have ++applications in NON-virtualized environments, such as hotplug-memory ++deletion, SSDs, and page cache compression. Others have suggested ideas ++such as allowing use of highmem memory without a highmem kernel, or use ++of spare video memory. ++ ++Transcendent memory, or "tmem" for short, provides a well-defined API to ++access this unusual class of memory. (A summary of the API is provided ++below.) The basic operations are page-copy-based and use a flexible ++object-oriented addressing mechanism. Tmem assumes that some "privileged ++entity" is capable of executing tmem requests and storing pages of data; ++this entity is currently a hypervisor and operations are performed via ++hypercalls, but the entity could be a kernel policy, or perhaps a ++"memory node" in a cluster of blades connected by a high-speed ++interconnect such as hypertransport or QPI. ++ ++Since tmem is not directly accessible and because page copying is done ++to/from physical pageframes, it more suitable for in-kernel memory needs ++than for userland applications. However, there may be yet undiscovered ++userland possibilities. ++ ++With the tmem concept outlined vaguely and its broader potential hinted, ++we will overview two existing examples of how tmem can be used by the ++kernel. ++ ++"Cleancache" can be thought of as a page-granularity victim cache for clean ++pages that the kernel's pageframe replacement algorithm (PFRA) would like ++to keep around, but can't since there isn't enough memory. So when the ++PFRA "evicts" a page, it first puts it into the cleancache via a call to ++tmem. And any time a filesystem reads a page from disk, it first attempts ++to get the page from cleancache. If it's there, a disk access is eliminated. ++If not, the filesystem just goes to the disk like normal. Cleancache is ++"ephemeral" so whether a page is kept in cleancache (between the "put" and ++the "get") is dependent on a number of factors that are invisible to ++the kernel. ++ ++"Frontswap" is so named because it can be thought of as the opposite of ++a "backing store". Frontswap IS persistent, but for various reasons may not ++always be available for use, again due to factors that may not be visible to ++the kernel. (But, briefly, if the kernel is being "good" and has shared its ++resources nicely, then it will be able to use frontswap, else it will not.) ++Once a page is put, a get on the page will always succeed. So when the ++kernel finds itself in a situation where it needs to swap out a page, it ++first attempts to use frontswap. If the put works, a disk write and ++(usually) a disk read are avoided. If it doesn't, the page is written ++to swap as usual. Unlike cleancache, whether a page is stored in frontswap ++vs swap is recorded in kernel data structures, so when a page needs to ++be fetched, the kernel does a get if it is in frontswap and reads from ++swap if it is not in frontswap. ++ ++Both cleancache and frontswap may be optionally compressed, trading off 2x ++space reduction vs 10x performance for access. Cleancache also has a ++sharing feature, which allows different nodes in a "virtual cluster" ++to share a local page cache. ++ ++Tmem has some similarity to IBM's Collaborative Memory Management, but ++creates more of a partnership between the kernel and the "privileged ++entity" and is not very invasive. Tmem may be applicable for KVM and ++containers; there is some disagreement on the extent of its value. ++Tmem is highly complementary to ballooning (aka page granularity hot ++plug) and memory deduplication (aka transparent content-based page ++sharing) but still has value when neither are present. ++ ++Performance is difficult to quantify because some benchmarks respond ++very favorably to increases in memory and tmem may do quite well on ++those, depending on how much tmem is available which may vary widely ++and dynamically, depending on conditions completely outside of the ++system being measured. Ideas on how best to provide useful metrics ++would be appreciated. ++ ++Tmem is supported starting in Xen 4.0 and is in Xen's Linux 2.6.18-xen ++source tree. It is also released as a technology preview in Oracle's ++Xen-based virtualization product, Oracle VM 2.2. Again, Xen is not ++necessarily a requirement, but currently provides the only existing ++implementation of tmem. ++ ++Lots more information about tmem can be found at: ++ http://oss.oracle.com/projects/tmem ++and there was a talk about it on the first day of Linux Symposium in ++July 2009; an updated talk is planned at linux.conf.au in January 2010. ++Tmem is the result of a group effort, including Dan Magenheimer, ++Chris Mason, Dave McCracken, Kurt Hackel and Zhigang Wang, with helpful ++input from Jeremy Fitzhardinge, Keir Fraser, Ian Pratt, Sunil Mushran, ++Joel Becker, and Jan Beulich. ++ ++THE TRANSCENDENT MEMORY API ++ ++Transcendent memory is made up of a set of pools. Each pool is made ++up of a set of objects. And each object contains a set of pages. ++The combination of a 32-bit pool id, a 64-bit object id, and a 32-bit ++page id, uniquely identify a page of tmem data, and this tuple is called ++a "handle." Commonly, the three parts of a handle are used to address ++a filesystem, a file within that filesystem, and a page within that file; ++however an OS can use any values as long as they uniquely identify ++a page of data. ++ ++When a tmem pool is created, it is given certain attributes: It can ++be private or shared, and it can be persistent or ephemeral. Each ++combination of these attributes provides a different set of useful ++functionality and also defines a slightly different set of semantics ++for the various operations on the pool. Other pool attributes include ++the size of the page and a version number. ++ ++Once a pool is created, operations are performed on the pool. Pages ++are copied between the OS and tmem and are addressed using a handle. ++Pages and/or objects may also be flushed from the pool. When all ++operations are completed, a pool can be destroyed. ++ ++The specific tmem functions are called in Linux through a set of ++accessor functions: ++ ++int (*new_pool)(struct tmem_pool_uuid uuid, u32 flags); ++int (*destroy_pool)(u32 pool_id); ++int (*put_page)(u32 pool_id, u64 object, u32 index, unsigned long pfn); ++int (*get_page)(u32 pool_id, u64 object, u32 index, unsigned long pfn); ++int (*flush_page)(u32 pool_id, u64 object, u32 index); ++int (*flush_object)(u32 pool_id, u64 object); ++ ++The new_pool accessor creates a new pool and returns a pool id ++which is a non-negative 32-bit integer. If the flags parameter ++specifies that the pool is to be shared, the uuid is a 128-bit "shared ++secret" else it is ignored. The destroy_pool accessor destroys the pool. ++(Note: shared pools are not supported until security implications ++are better understood.) ++ ++The put_page accessor copies a page of data from the specified pageframe ++and associates it with the specified handle. ++ ++The get_page accessor looks up a page of data in tmem associated with ++the specified handle and, if found, copies it to the specified pageframe. ++ ++The flush_page accessor ensures that subsequent gets of a page with ++the specified handle will fail. The flush_object accessor ensures ++that subsequent gets of any page matching the pool id and object ++will fail. ++ ++There are many subtle but critical behaviors for get_page and put_page: ++- Any put_page (with one notable exception) may be rejected and the client ++ must be prepared to deal with that failure. A put_page copies, NOT moves, ++ data; that is the data exists in both places. Linux is responsible for ++ destroying or overwriting its own copy, or alternately managing any ++ coherency between the copies. ++- Every page successfully put to a persistent pool must be found by a ++ subsequent get_page that specifies the same handle. A page successfully ++ put to an ephemeral pool has an indeterminate lifetime and even an ++ immediately subsequent get_page may fail. ++- A get_page to a private pool is destructive, that is it behaves as if ++ the get_page were atomically followed by a flush_page. A get_page ++ to a shared pool is non-destructive. A flush_page behaves just like ++ a get_page to a private pool except the data is thrown away. ++- Put-put-get coherency is guaranteed. For example, after the sequence: ++ put_page(ABC,D1); ++ put_page(ABC,D2); ++ get_page(ABC,E) ++ E may never contain the data from D1. However, even for a persistent ++ pool, the get_page may fail if the second put_page indicates failure. ++- Get-get coherency is guaranteed. For example, in the sequence: ++ put_page(ABC,D); ++ get_page(ABC,E1); ++ get_page(ABC,E2) ++ if the first get_page fails, the second must also fail. ++- A tmem implementation provides no serialization guarantees (e.g. to ++ an SMP Linux). So if different Linux threads are putting and flushing ++ the same page, the results are indeterminate. +--- head-2010-05-12.orig/fs/btrfs/extent_io.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/fs/btrfs/extent_io.c 2010-04-15 09:41:13.000000000 +0200 +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include "extent_io.h" + #include "extent_map.h" + #include "compat.h" +@@ -2030,6 +2031,13 @@ static int __extent_read_full_page(struc + + set_page_extent_mapped(page); + ++ if (!PageUptodate(page)) { ++ if (precache_get(page->mapping, page->index, page) == 1) { ++ BUG_ON(blocksize != PAGE_SIZE); ++ goto out; ++ } ++ } ++ + end = page_end; + lock_extent(tree, start, end, GFP_NOFS); + +@@ -2146,6 +2154,7 @@ static int __extent_read_full_page(struc + cur = cur + iosize; + page_offset += iosize; + } ++out: + if (!nr) { + if (!PageError(page)) + SetPageUptodate(page); +--- head-2010-05-12.orig/fs/btrfs/super.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/fs/btrfs/super.c 2010-04-15 09:41:20.000000000 +0200 +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + #include "compat.h" + #include "ctree.h" + #include "disk-io.h" +@@ -477,6 +478,7 @@ static int btrfs_fill_super(struct super + sb->s_root = root_dentry; + + save_mount_options(sb, data); ++ precache_init(sb); + return 0; + + fail_close: +--- head-2010-05-12.orig/fs/buffer.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/fs/buffer.c 2010-03-24 14:09:47.000000000 +0100 +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + + static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); + +@@ -276,6 +277,11 @@ void invalidate_bdev(struct block_device + + invalidate_bh_lrus(); + invalidate_mapping_pages(mapping, 0, -1); ++ ++ /* 99% of the time, we don't need to flush the precache on the bdev. ++ * But, for the strange corners, lets be cautious ++ */ ++ precache_flush_inode(mapping); + } + EXPORT_SYMBOL(invalidate_bdev); + +--- head-2010-05-12.orig/fs/ext3/super.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/fs/ext3/super.c 2010-03-24 14:09:47.000000000 +0100 +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + + #include + +@@ -1370,6 +1371,7 @@ static int ext3_setup_super(struct super + } else { + ext3_msg(sb, KERN_INFO, "using internal journal"); + } ++ precache_init(sb); + return res; + } + +--- head-2010-05-12.orig/fs/ext4/super.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/fs/ext4/super.c 2010-04-15 09:41:30.000000000 +0200 +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + #include + + #include "ext4.h" +@@ -1784,6 +1785,8 @@ static int ext4_setup_super(struct super + EXT4_INODES_PER_GROUP(sb), + sbi->s_mount_opt); + ++ precache_init(sb); ++ + return res; + } + +--- head-2010-05-12.orig/fs/mpage.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/fs/mpage.c 2010-04-15 09:41:38.000000000 +0200 +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + /* + * I/O completion handler for multipage BIOs. +@@ -286,6 +287,13 @@ do_mpage_readpage(struct bio *bio, struc + SetPageMappedToDisk(page); + } + ++ if (fully_mapped && ++ blocks_per_page == 1 && !PageUptodate(page) && ++ precache_get(page->mapping, page->index, page) == 1) { ++ SetPageUptodate(page); ++ goto confused; ++ } ++ + /* + * This page will go to BIO. Do we need to send this BIO off first? + */ +--- head-2010-05-12.orig/fs/ocfs2/super.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/fs/ocfs2/super.c 2010-03-24 14:09:47.000000000 +0100 +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + #include + + #define MLOG_MASK_PREFIX ML_SUPER +@@ -2260,6 +2261,7 @@ static int ocfs2_initialize_super(struct + mlog_errno(status); + goto bail; + } ++ shared_precache_init(sb, &di->id2.i_super.s_uuid[0]); + + bail: + mlog_exit(status); +--- head-2010-05-12.orig/fs/super.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/fs/super.c 2010-05-12 08:57:07.000000000 +0200 +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + #include "internal.h" + +@@ -105,6 +106,9 @@ static struct super_block *alloc_super(s + s->s_qcop = sb_quotactl_ops; + s->s_op = &default_op; + s->s_time_gran = 1000000000; ++#ifdef CONFIG_PRECACHE ++ s->precache_poolid = -1; ++#endif + } + out: + return s; +@@ -195,6 +199,7 @@ void deactivate_super(struct super_block + vfs_dq_off(s, 0); + down_write(&s->s_umount); + fs->kill_sb(s); ++ precache_flush_filesystem(s); + put_filesystem(fs); + put_super(s); + } +@@ -221,6 +226,7 @@ void deactivate_locked_super(struct supe + spin_unlock(&sb_lock); + vfs_dq_off(s, 0); + fs->kill_sb(s); ++ precache_flush_filesystem(s); + put_filesystem(fs); + put_super(s); + } else { +--- head-2010-05-12.orig/include/linux/fs.h 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/include/linux/fs.h 2010-03-24 14:09:47.000000000 +0100 +@@ -1377,6 +1377,9 @@ struct super_block { + /* Granularity of c/m/atime in ns. + Cannot be worse than a second */ + u32 s_time_gran; ++#ifdef CONFIG_PRECACHE ++ u32 precache_poolid; ++#endif + + /* + * The next field is for VFS *only*. No filesystems have any business +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/linux/precache.h 2010-03-24 14:09:47.000000000 +0100 +@@ -0,0 +1,55 @@ ++#ifndef _LINUX_PRECACHE_H ++ ++#include ++#include ++ ++#ifdef CONFIG_PRECACHE ++extern void precache_init(struct super_block *sb); ++extern void shared_precache_init(struct super_block *sb, char *uuid); ++extern int precache_get(struct address_space *mapping, unsigned long index, ++ struct page *empty_page); ++extern int precache_put(struct address_space *mapping, unsigned long index, ++ struct page *page); ++extern int precache_flush(struct address_space *mapping, unsigned long index); ++extern int precache_flush_inode(struct address_space *mapping); ++extern int precache_flush_filesystem(struct super_block *s); ++#else ++static inline void precache_init(struct super_block *sb) ++{ ++} ++ ++static inline void shared_precache_init(struct super_block *sb, char *uuid) ++{ ++} ++ ++static inline int precache_get(struct address_space *mapping, ++ unsigned long index, struct page *empty_page) ++{ ++ return 0; ++} ++ ++static inline int precache_put(struct address_space *mapping, ++ unsigned long index, struct page *page) ++{ ++ return 0; ++} ++ ++static inline int precache_flush(struct address_space *mapping, ++ unsigned long index) ++{ ++ return 0; ++} ++ ++static inline int precache_flush_inode(struct address_space *mapping) ++{ ++ return 0; ++} ++ ++static inline int precache_flush_filesystem(struct super_block *s) ++{ ++ return 0; ++} ++#endif ++ ++#define _LINUX_PRECACHE_H ++#endif /* _LINUX_PRECACHE_H */ +--- head-2010-05-12.orig/include/linux/swap.h 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/include/linux/swap.h 2010-03-24 14:09:47.000000000 +0100 +@@ -183,8 +183,61 @@ struct swap_info_struct { + struct block_device *bdev; /* swap device or bdev of swap file */ + struct file *swap_file; /* seldom referenced */ + unsigned int old_block_size; /* seldom referenced */ ++#ifdef CONFIG_PRESWAP ++ unsigned long *preswap_map; ++ unsigned int preswap_pages; ++#endif + }; + ++#ifdef CONFIG_PRESWAP ++ ++#include ++extern int preswap_sysctl_handler(struct ctl_table *, int, void __user *, ++ size_t *, loff_t *); ++extern const unsigned long preswap_zero, preswap_infinity; ++ ++extern struct swap_info_struct *get_swap_info_struct(unsigned int type); ++ ++extern void preswap_shrink(unsigned long); ++extern int preswap_test(struct swap_info_struct *, unsigned long); ++extern void preswap_init(unsigned); ++extern int preswap_put(struct page *); ++extern int preswap_get(struct page *); ++extern void preswap_flush(unsigned, unsigned long); ++extern void preswap_flush_area(unsigned); ++#else ++static inline void preswap_shrink(unsigned long target_pages) ++{ ++} ++ ++static inline int preswap_test(struct swap_info_struct *sis, unsigned long offset) ++{ ++ return 0; ++} ++ ++static inline void preswap_init(unsigned type) ++{ ++} ++ ++static inline int preswap_put(struct page *page) ++{ ++ return 0; ++} ++ ++static inline int preswap_get(struct page *get) ++{ ++ return 0; ++} ++ ++static inline void preswap_flush(unsigned type, unsigned long offset) ++{ ++} ++ ++static inline void preswap_flush_area(unsigned type) ++{ ++} ++#endif /* CONFIG_PRESWAP */ ++ + struct swap_list_t { + int head; /* head of priority-ordered swapfile list */ + int next; /* swapfile to be used next */ +--- head-2010-05-12.orig/kernel/sysctl.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/kernel/sysctl.c 2010-03-24 14:09:47.000000000 +0100 +@@ -1274,6 +1274,17 @@ static struct ctl_table vm_table[] = { + .mode = 0644, + .proc_handler = proc_dointvec, + }, ++#ifdef CONFIG_PRESWAP ++ { ++ .procname = "preswap", ++ .data = NULL, ++ .maxlen = sizeof(unsigned long), ++ .mode = 0644, ++ .proc_handler = preswap_sysctl_handler, ++ .extra1 = (void *)&preswap_zero, ++ .extra2 = (void *)&preswap_infinity, ++ }, ++#endif + #ifdef CONFIG_MEMORY_FAILURE + { + .procname = "memory_failure_early_kill", +--- head-2010-05-12.orig/mm/Kconfig 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/mm/Kconfig 2010-03-24 14:09:47.000000000 +0100 +@@ -287,3 +287,31 @@ config NOMMU_INITIAL_TRIM_EXCESS + of 1 says that all excess pages should be trimmed. + + See Documentation/nommu-mmap.txt for more information. ++ ++# ++# support for transcendent memory ++# ++config TMEM ++ bool ++ help ++ In a virtualized environment, allows unused and underutilized ++ system physical memory to be made accessible through a narrow ++ well-defined page-copy-based API. If unsure, say Y. ++ ++config PRECACHE ++ bool "Cache clean pages in transcendent memory" ++ depends on XEN ++ select TMEM ++ help ++ Allows the transcendent memory pool to be used to store clean ++ page-cache pages which, under some circumstances, will greatly ++ reduce paging and thus improve performance. If unsure, say Y. ++ ++config PRESWAP ++ bool "Swap pages to transcendent memory" ++ depends on XEN ++ select TMEM ++ help ++ Allows the transcendent memory pool to be used as a pseudo-swap ++ device which, under some circumstances, will greatly reduce ++ swapping and thus improve performance. If unsure, say Y. +--- head-2010-05-12.orig/mm/Makefile 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/mm/Makefile 2010-03-24 14:09:47.000000000 +0100 +@@ -17,6 +17,9 @@ obj-y += init-mm.o + + obj-$(CONFIG_BOUNCE) += bounce.o + obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o ++obj-$(CONFIG_TMEM) += tmem.o ++obj-$(CONFIG_PRESWAP) += preswap.o ++obj-$(CONFIG_PRECACHE) += precache.o + obj-$(CONFIG_HAS_DMA) += dmapool.o + obj-$(CONFIG_HUGETLBFS) += hugetlb.o + obj-$(CONFIG_NUMA) += mempolicy.o +--- head-2010-05-12.orig/mm/filemap.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/mm/filemap.c 2010-03-24 14:09:47.000000000 +0100 +@@ -33,6 +33,7 @@ + #include + #include /* for BUG_ON(!in_atomic()) only */ + #include ++#include + #include /* for page_is_file_cache() */ + #include "internal.h" + +@@ -119,6 +120,16 @@ void __remove_from_page_cache(struct pag + { + struct address_space *mapping = page->mapping; + ++ /* ++ * if we're uptodate, flush out into the precache, otherwise ++ * invalidate any existing precache entries. We can't leave ++ * stale data around in the precache once our page is gone ++ */ ++ if (PageUptodate(page)) ++ precache_put(page->mapping, page->index, page); ++ else ++ precache_flush(page->mapping, page->index); ++ + radix_tree_delete(&mapping->page_tree, page->index); + page->mapping = NULL; + mapping->nrpages--; +--- head-2010-05-12.orig/mm/page_io.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/mm/page_io.c 2010-04-15 09:41:45.000000000 +0200 +@@ -111,6 +111,13 @@ int swap_writepage(struct page *page, st + return ret; + } + ++ if (preswap_put(page) == 1) { ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ goto out; ++ } ++ + bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write); + if (bio == NULL) { + set_page_dirty(page); +@@ -179,6 +186,12 @@ int swap_readpage(struct page *page) + return ret; + } + ++ if (preswap_get(page) == 1) { ++ SetPageUptodate(page); ++ unlock_page(page); ++ goto out; ++ } ++ + bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read); + if (bio == NULL) { + unlock_page(page); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/mm/precache.c 2010-03-24 14:09:47.000000000 +0100 +@@ -0,0 +1,140 @@ ++/* ++ * linux/mm/precache.c ++ * ++ * Implements "precache" for filesystems/pagecache on top of transcendent ++ * memory ("tmem") API. A filesystem creates an "ephemeral tmem pool" ++ * and retains the returned pool_id in its superblock. Clean pages evicted ++ * from pagecache may be "put" into the pool and associated with a "handle" ++ * consisting of the pool_id, an object (inode) id, and an index (page offset). ++ * Note that the page is copied to tmem; no kernel mappings are changed. ++ * If the page is later needed, the filesystem (or VFS) issues a "get", passing ++ * the same handle and an empty pageframe. If successful, the page is copied ++ * into the pageframe and a disk read is avoided. But since the tmem pool ++ * is of indeterminate size, a "put" page has indeterminate longevity ++ * ("ephemeral"), and the "get" may fail, in which case the filesystem must ++ * read the page from disk as before. Note that the filesystem/pagecache are ++ * responsible for maintaining coherency between the pagecache, precache, ++ * and the disk, for which "flush page" and "flush object" actions are ++ * provided. And when a filesystem is unmounted, it must "destroy" the pool. ++ * ++ * Two types of pools may be created for a precache: "private" or "shared". ++ * For a private pool, a successful "get" always flushes, implementing ++ * exclusive semantics; for a "shared" pool (which is intended for use by ++ * co-resident nodes of a cluster filesystem), the "flush" is not guaranteed. ++ * In either case, a failed "duplicate" put (overwrite) always guarantee ++ * the old data is flushed. ++ * ++ * Note also that multiple accesses to a tmem pool may be concurrent and any ++ * ordering must be guaranteed by the caller. ++ * ++ * Copyright (C) 2008,2009 Dan Magenheimer, Oracle Corp. ++ */ ++ ++#include ++#include ++#include "tmem.h" ++ ++static int precache_auto_allocate; /* set to 1 to auto_allocate */ ++ ++int precache_put(struct address_space *mapping, unsigned long index, ++ struct page *page) ++{ ++ u32 tmem_pool = mapping->host->i_sb->precache_poolid; ++ u64 obj = (unsigned long) mapping->host->i_ino; ++ u32 ind = (u32) index; ++ unsigned long mfn = pfn_to_mfn(page_to_pfn(page)); ++ int ret; ++ ++ if ((s32)tmem_pool < 0) { ++ if (!precache_auto_allocate) ++ return 0; ++ /* a put on a non-existent precache may auto-allocate one */ ++ ret = tmem_new_pool(0, 0, 0); ++ if (ret < 0) ++ return 0; ++ printk(KERN_INFO ++ "Mapping superblock for s_id=%s to precache_id=%d\n", ++ mapping->host->i_sb->s_id, tmem_pool); ++ mapping->host->i_sb->precache_poolid = tmem_pool; ++ } ++ if (ind != index) ++ return 0; ++ mb(); /* ensure page is quiescent; tmem may address it with an alias */ ++ return tmem_put_page(tmem_pool, obj, ind, mfn); ++} ++ ++int precache_get(struct address_space *mapping, unsigned long index, ++ struct page *empty_page) ++{ ++ u32 tmem_pool = mapping->host->i_sb->precache_poolid; ++ u64 obj = (unsigned long) mapping->host->i_ino; ++ u32 ind = (u32) index; ++ unsigned long mfn = pfn_to_mfn(page_to_pfn(empty_page)); ++ ++ if ((s32)tmem_pool < 0) ++ return 0; ++ if (ind != index) ++ return 0; ++ ++ return tmem_get_page(tmem_pool, obj, ind, mfn); ++} ++EXPORT_SYMBOL(precache_get); ++ ++int precache_flush(struct address_space *mapping, unsigned long index) ++{ ++ u32 tmem_pool = mapping->host->i_sb->precache_poolid; ++ u64 obj = (unsigned long) mapping->host->i_ino; ++ u32 ind = (u32) index; ++ ++ if ((s32)tmem_pool < 0) ++ return 0; ++ if (ind != index) ++ return 0; ++ ++ return tmem_flush_page(tmem_pool, obj, ind); ++} ++EXPORT_SYMBOL(precache_flush); ++ ++int precache_flush_inode(struct address_space *mapping) ++{ ++ u32 tmem_pool = mapping->host->i_sb->precache_poolid; ++ u64 obj = (unsigned long) mapping->host->i_ino; ++ ++ if ((s32)tmem_pool < 0) ++ return 0; ++ ++ return tmem_flush_object(tmem_pool, obj); ++} ++EXPORT_SYMBOL(precache_flush_inode); ++ ++int precache_flush_filesystem(struct super_block *sb) ++{ ++ u32 tmem_pool = sb->precache_poolid; ++ int ret; ++ ++ if ((s32)tmem_pool < 0) ++ return 0; ++ ret = tmem_destroy_pool(tmem_pool); ++ if (!ret) ++ return 0; ++ printk(KERN_INFO ++ "Unmapping superblock for s_id=%s from precache_id=%d\n", ++ sb->s_id, ret); ++ sb->precache_poolid = 0; ++ return 1; ++} ++EXPORT_SYMBOL(precache_flush_filesystem); ++ ++void precache_init(struct super_block *sb) ++{ ++ sb->precache_poolid = tmem_new_pool(0, 0, 0); ++} ++EXPORT_SYMBOL(precache_init); ++ ++void shared_precache_init(struct super_block *sb, char *uuid) ++{ ++ u64 uuid_lo = *(u64 *)uuid; ++ u64 uuid_hi = *(u64 *)(&uuid[8]); ++ sb->precache_poolid = tmem_new_pool(uuid_lo, uuid_hi, TMEM_POOL_SHARED); ++} ++EXPORT_SYMBOL(shared_precache_init); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/mm/preswap.c 2010-03-24 14:09:47.000000000 +0100 +@@ -0,0 +1,182 @@ ++/* ++ * linux/mm/preswap.c ++ * ++ * Implements a fast "preswap" on top of the transcendent memory ("tmem") API. ++ * When a swapdisk is enabled (with swapon), a "private persistent tmem pool" ++ * is created along with a bit-per-page preswap_map. When swapping occurs ++ * and a page is about to be written to disk, a "put" into the pool may first ++ * be attempted by passing the pageframe to be swapped, along with a "handle" ++ * consisting of a pool_id, an object id, and an index. Since the pool is of ++ * indeterminate size, the "put" may be rejected, in which case the page ++ * is swapped to disk as normal. If the "put" is successful, the page is ++ * copied to tmem and the preswap_map records the success. Later, when ++ * the page needs to be swapped in, the preswap_map is checked and, if set, ++ * the page may be obtained with a "get" operation. Note that the swap ++ * subsystem is responsible for: maintaining coherency between the swapcache, ++ * preswap, and the swapdisk; for evicting stale pages from preswap; and for ++ * emptying preswap when swapoff is performed. The "flush page" and "flush ++ * object" actions are provided for this. ++ * ++ * Note that if a "duplicate put" is performed to overwrite a page and ++ * the "put" operation fails, the page (and old data) is flushed and lost. ++ * Also note that multiple accesses to a tmem pool may be concurrent and ++ * any ordering must be guaranteed by the caller. ++ * ++ * Copyright (C) 2008,2009 Dan Magenheimer, Oracle Corp. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "tmem.h" ++ ++static u32 preswap_poolid = -1; /* if negative, preswap will never call tmem */ ++ ++const unsigned long preswap_zero = 0, preswap_infinity = ~0UL; /* for sysctl */ ++ ++/* ++ * Swizzling increases objects per swaptype, increasing tmem concurrency ++ * for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS ++ */ ++#define SWIZ_BITS 4 ++#define SWIZ_MASK ((1 << SWIZ_BITS) - 1) ++#define oswiz(_type, _ind) ((_type << SWIZ_BITS) | (_ind & SWIZ_MASK)) ++#define iswiz(_ind) (_ind >> SWIZ_BITS) ++ ++/* ++ * preswap_map test/set/clear operations (must be atomic) ++ */ ++ ++int preswap_test(struct swap_info_struct *sis, unsigned long offset) ++{ ++ if (!sis->preswap_map) ++ return 0; ++ return test_bit(offset % BITS_PER_LONG, ++ &sis->preswap_map[offset/BITS_PER_LONG]); ++} ++ ++static inline void preswap_set(struct swap_info_struct *sis, ++ unsigned long offset) ++{ ++ if (!sis->preswap_map) ++ return; ++ set_bit(offset % BITS_PER_LONG, ++ &sis->preswap_map[offset/BITS_PER_LONG]); ++} ++ ++static inline void preswap_clear(struct swap_info_struct *sis, ++ unsigned long offset) ++{ ++ if (!sis->preswap_map) ++ return; ++ clear_bit(offset % BITS_PER_LONG, ++ &sis->preswap_map[offset/BITS_PER_LONG]); ++} ++ ++/* ++ * preswap tmem operations ++ */ ++ ++/* returns 1 if the page was successfully put into preswap, 0 if the page ++ * was declined, and -ERRNO for a specific error */ ++int preswap_put(struct page *page) ++{ ++ swp_entry_t entry = { .val = page_private(page), }; ++ unsigned type = swp_type(entry); ++ pgoff_t offset = swp_offset(entry); ++ u64 ind64 = (u64)offset; ++ u32 ind = (u32)offset; ++ unsigned long mfn = pfn_to_mfn(page_to_pfn(page)); ++ struct swap_info_struct *sis = get_swap_info_struct(type); ++ int dup = 0, ret; ++ ++ if ((s32)preswap_poolid < 0) ++ return 0; ++ if (ind64 != ind) ++ return 0; ++ if (preswap_test(sis, offset)) ++ dup = 1; ++ mb(); /* ensure page is quiescent; tmem may address it with an alias */ ++ ret = tmem_put_page(preswap_poolid, oswiz(type, ind), iswiz(ind), mfn); ++ if (ret == 1) { ++ preswap_set(sis, offset); ++ if (!dup) ++ sis->preswap_pages++; ++ } else if (dup) { ++ /* failed dup put always results in an automatic flush of ++ * the (older) page from preswap */ ++ preswap_clear(sis, offset); ++ sis->preswap_pages--; ++ } ++ return ret; ++} ++ ++/* returns 1 if the page was successfully gotten from preswap, 0 if the page ++ * was not present (should never happen!), and -ERRNO for a specific error */ ++int preswap_get(struct page *page) ++{ ++ swp_entry_t entry = { .val = page_private(page), }; ++ unsigned type = swp_type(entry); ++ pgoff_t offset = swp_offset(entry); ++ u64 ind64 = (u64)offset; ++ u32 ind = (u32)offset; ++ unsigned long mfn = pfn_to_mfn(page_to_pfn(page)); ++ struct swap_info_struct *sis = get_swap_info_struct(type); ++ int ret; ++ ++ if ((s32)preswap_poolid < 0) ++ return 0; ++ if (ind64 != ind) ++ return 0; ++ if (!preswap_test(sis, offset)) ++ return 0; ++ ret = tmem_get_page(preswap_poolid, oswiz(type, ind), iswiz(ind), mfn); ++ return ret; ++} ++ ++/* flush a single page from preswap */ ++void preswap_flush(unsigned type, unsigned long offset) ++{ ++ u64 ind64 = (u64)offset; ++ u32 ind = (u32)offset; ++ struct swap_info_struct *sis = get_swap_info_struct(type); ++ int ret = 1; ++ ++ if ((s32)preswap_poolid < 0) ++ return; ++ if (ind64 != ind) ++ return; ++ if (preswap_test(sis, offset)) { ++ ret = tmem_flush_page(preswap_poolid, ++ oswiz(type, ind), iswiz(ind)); ++ sis->preswap_pages--; ++ preswap_clear(sis, offset); ++ } ++} ++ ++/* flush all pages from the passed swaptype */ ++void preswap_flush_area(unsigned type) ++{ ++ struct swap_info_struct *sis = get_swap_info_struct(type); ++ int ind; ++ ++ if ((s32)preswap_poolid < 0) ++ return; ++ for (ind = SWIZ_MASK; ind >= 0; ind--) ++ (void)tmem_flush_object(preswap_poolid, oswiz(type, ind)); ++ sis->preswap_pages = 0; ++} ++ ++void preswap_init(unsigned type) ++{ ++ /* only need one tmem pool for all swap types */ ++ if ((s32)preswap_poolid >= 0) ++ return; ++ preswap_poolid = tmem_new_pool(0, 0, TMEM_POOL_PERSIST); ++} +--- head-2010-05-12.orig/mm/swapfile.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/mm/swapfile.c 2010-03-24 14:09:47.000000000 +0100 +@@ -587,6 +587,7 @@ static unsigned char swap_entry_free(str + swap_list.next = p->type; + nr_swap_pages++; + p->inuse_pages--; ++ preswap_flush(p->type, offset); + } + + return usage; +@@ -1029,7 +1030,7 @@ static int unuse_mm(struct mm_struct *mm + * Recycle to start on reaching the end, returning 0 when empty. + */ + static unsigned int find_next_to_unuse(struct swap_info_struct *si, +- unsigned int prev) ++ unsigned int prev, unsigned int preswap) + { + unsigned int max = si->max; + unsigned int i = prev; +@@ -1055,6 +1056,12 @@ static unsigned int find_next_to_unuse(s + prev = 0; + i = 1; + } ++ if (preswap) { ++ if (preswap_test(si, i)) ++ break; ++ else ++ continue; ++ } + count = si->swap_map[i]; + if (count && swap_count(count) != SWAP_MAP_BAD) + break; +@@ -1066,8 +1073,12 @@ static unsigned int find_next_to_unuse(s + * We completely avoid races by reading each swap page in advance, + * and then search for the process using it. All the necessary + * page table adjustments can then be made atomically. ++ * ++ * if the boolean preswap is true, only unuse pages_to_unuse pages; ++ * pages_to_unuse==0 means all pages + */ +-static int try_to_unuse(unsigned int type) ++static int try_to_unuse(unsigned int type, unsigned int preswap, ++ unsigned long pages_to_unuse) + { + struct swap_info_struct *si = swap_info[type]; + struct mm_struct *start_mm; +@@ -1100,7 +1111,7 @@ static int try_to_unuse(unsigned int typ + * one pass through swap_map is enough, but not necessarily: + * there are races when an instance of an entry might be missed. + */ +- while ((i = find_next_to_unuse(si, i)) != 0) { ++ while ((i = find_next_to_unuse(si, i, preswap)) != 0) { + if (signal_pending(current)) { + retval = -EINTR; + break; +@@ -1267,6 +1278,8 @@ static int try_to_unuse(unsigned int typ + * interactive performance. + */ + cond_resched(); ++ if (preswap && pages_to_unuse && !--pages_to_unuse) ++ break; + } + + mmput(start_mm); +@@ -1611,7 +1624,7 @@ SYSCALL_DEFINE1(swapoff, const char __us + spin_unlock(&swap_lock); + + current->flags |= PF_OOM_ORIGIN; +- err = try_to_unuse(type); ++ err = try_to_unuse(type, 0, 0); + current->flags &= ~PF_OOM_ORIGIN; + + if (err) { +@@ -1663,9 +1676,14 @@ SYSCALL_DEFINE1(swapoff, const char __us + swap_map = p->swap_map; + p->swap_map = NULL; + p->flags = 0; ++ preswap_flush_area(type); + spin_unlock(&swap_lock); + mutex_unlock(&swapon_mutex); + vfree(swap_map); ++#ifdef CONFIG_PRESWAP ++ if (p->preswap_map) ++ vfree(p->preswap_map); ++#endif + /* Destroy swap account informatin */ + swap_cgroup_swapoff(type); + +@@ -1821,6 +1839,7 @@ SYSCALL_DEFINE2(swapon, const char __use + unsigned long maxpages; + unsigned long swapfilepages; + unsigned char *swap_map = NULL; ++ unsigned long *preswap_map = NULL; + struct page *page = NULL; + struct inode *inode = NULL; + int did_down = 0; +@@ -2021,6 +2040,12 @@ SYSCALL_DEFINE2(swapon, const char __use + } + } + ++#ifdef CONFIG_PRESWAP ++ preswap_map = vmalloc(maxpages / sizeof(long)); ++ if (preswap_map) ++ memset(preswap_map, 0, maxpages / sizeof(long)); ++#endif ++ + error = swap_cgroup_swapon(type, maxpages); + if (error) + goto bad_swap; +@@ -2059,6 +2084,9 @@ SYSCALL_DEFINE2(swapon, const char __use + else + p->prio = --least_priority; + p->swap_map = swap_map; ++#ifdef CONFIG_PRESWAP ++ p->preswap_map = preswap_map; ++#endif + p->flags |= SWP_WRITEOK; + nr_swap_pages += nr_good_pages; + total_swap_pages += nr_good_pages; +@@ -2082,6 +2110,7 @@ SYSCALL_DEFINE2(swapon, const char __use + swap_list.head = swap_list.next = type; + else + swap_info[prev]->next = type; ++ preswap_init(type); + spin_unlock(&swap_lock); + mutex_unlock(&swapon_mutex); + error = 0; +@@ -2098,6 +2127,7 @@ bad_swap_2: + p->swap_file = NULL; + p->flags = 0; + spin_unlock(&swap_lock); ++ vfree(preswap_map); + vfree(swap_map); + if (swap_file) + filp_close(swap_file, NULL); +@@ -2316,6 +2346,10 @@ int valid_swaphandles(swp_entry_t entry, + base++; + + spin_lock(&swap_lock); ++ if (preswap_test(si, target)) { ++ spin_unlock(&swap_lock); ++ return 0; ++ } + if (end > si->max) /* don't go beyond end of map */ + end = si->max; + +@@ -2326,6 +2360,9 @@ int valid_swaphandles(swp_entry_t entry, + break; + if (swap_count(si->swap_map[toff]) == SWAP_MAP_BAD) + break; ++ /* Don't read in preswap pages */ ++ if (preswap_test(si, toff)) ++ break; + } + /* Count contiguous allocated slots below our target */ + for (toff = target; --toff >= base; nr_pages++) { +@@ -2334,6 +2371,9 @@ int valid_swaphandles(swp_entry_t entry, + break; + if (swap_count(si->swap_map[toff]) == SWAP_MAP_BAD) + break; ++ /* Don't read in preswap pages */ ++ if (preswap_test(si, toff)) ++ break; + } + spin_unlock(&swap_lock); + +@@ -2560,3 +2600,98 @@ static void free_swap_count_continuation + } + } + } ++ ++#ifdef CONFIG_PRESWAP ++/* ++ * preswap infrastructure functions ++ */ ++ ++struct swap_info_struct *get_swap_info_struct(unsigned int type) ++{ ++ BUG_ON(type > MAX_SWAPFILES); ++ return swap_info[type]; ++} ++ ++/* code structure leveraged from sys_swapoff */ ++void preswap_shrink(unsigned long target_pages) ++{ ++ struct swap_info_struct *si = NULL; ++ unsigned long total_pages = 0, total_pages_to_unuse; ++ unsigned long pages = 0, unuse_pages = 0; ++ int type; ++ int wrapped = 0; ++ ++ do { ++ /* ++ * we don't want to hold swap_lock while doing a very ++ * lengthy try_to_unuse, but swap_list may change ++ * so restart scan from swap_list.head each time ++ */ ++ spin_lock(&swap_lock); ++ total_pages = 0; ++ for (type = swap_list.head; type >= 0; type = si->next) { ++ si = swap_info[type]; ++ total_pages += si->preswap_pages; ++ } ++ if (total_pages <= target_pages) { ++ spin_unlock(&swap_lock); ++ return; ++ } ++ total_pages_to_unuse = total_pages - target_pages; ++ for (type = swap_list.head; type >= 0; type = si->next) { ++ si = swap_info[type]; ++ if (total_pages_to_unuse < si->preswap_pages) ++ pages = unuse_pages = total_pages_to_unuse; ++ else { ++ pages = si->preswap_pages; ++ unuse_pages = 0; /* unuse all */ ++ } ++ if (security_vm_enough_memory_kern(pages)) ++ continue; ++ vm_unacct_memory(pages); ++ break; ++ } ++ spin_unlock(&swap_lock); ++ if (type < 0) ++ return; ++ current->flags |= PF_OOM_ORIGIN; ++ (void)try_to_unuse(type, 1, unuse_pages); ++ current->flags &= ~PF_OOM_ORIGIN; ++ wrapped++; ++ } while (wrapped <= 3); ++} ++ ++ ++#ifdef CONFIG_SYSCTL ++/* cat /sys/proc/vm/preswap provides total number of pages in preswap ++ * across all swaptypes. echo N > /sys/proc/vm/preswap attempts to shrink ++ * preswap page usage to N (usually 0) */ ++int preswap_sysctl_handler(ctl_table *table, int write, ++ void __user *buffer, size_t *length, loff_t *ppos) ++{ ++ unsigned long npages; ++ int type; ++ unsigned long totalpages = 0; ++ struct swap_info_struct *si = NULL; ++ ++ /* modeled after hugetlb_sysctl_handler in mm/hugetlb.c */ ++ if (!write) { ++ spin_lock(&swap_lock); ++ for (type = swap_list.head; type >= 0; type = si->next) { ++ si = swap_info[type]; ++ totalpages += si->preswap_pages; ++ } ++ spin_unlock(&swap_lock); ++ npages = totalpages; ++ } ++ table->data = &npages; ++ table->maxlen = sizeof(unsigned long); ++ proc_doulongvec_minmax(table, write, buffer, length, ppos); ++ ++ if (write) ++ preswap_shrink(npages); ++ ++ return 0; ++} ++#endif ++#endif /* CONFIG_PRESWAP */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/mm/tmem.h 2010-03-24 14:09:47.000000000 +0100 +@@ -0,0 +1,84 @@ ++/* ++ * linux/mm/tmem.h ++ * ++ * Interface to transcendent memory, used by mm/precache.c and mm/preswap.c ++ * Currently implemented on XEN, but may be implemented elsewhere in future. ++ * ++ * Copyright (C) 2008,2009 Dan Magenheimer, Oracle Corp. ++ */ ++ ++#ifdef CONFIG_XEN ++#include ++ ++/* Bits for HYPERVISOR_tmem_op(TMEM_NEW_POOL) */ ++#define TMEM_POOL_MIN_PAGESHIFT 12 ++#define TMEM_POOL_PAGEORDER (PAGE_SHIFT - TMEM_POOL_MIN_PAGESHIFT) ++ ++extern int xen_tmem_op(u32 tmem_cmd, u32 tmem_pool, u64 object, u32 index, ++ unsigned long gmfn, u32 tmem_offset, u32 pfn_offset, u32 len); ++extern int xen_tmem_new_pool(u32 tmem_cmd, u64 uuid_lo, u64 uuid_hi, u32 flags); ++ ++static inline int tmem_put_page(u32 pool_id, u64 object, u32 index, ++ unsigned long gmfn) ++{ ++ return xen_tmem_op(TMEM_PUT_PAGE, pool_id, object, index, ++ gmfn, 0, 0, 0); ++} ++ ++static inline int tmem_get_page(u32 pool_id, u64 object, u32 index, ++ unsigned long gmfn) ++{ ++ return xen_tmem_op(TMEM_GET_PAGE, pool_id, object, index, ++ gmfn, 0, 0, 0); ++} ++ ++static inline int tmem_flush_page(u32 pool_id, u64 object, u32 index) ++{ ++ return xen_tmem_op(TMEM_FLUSH_PAGE, pool_id, object, index, ++ 0, 0, 0, 0); ++} ++ ++static inline int tmem_flush_object(u32 pool_id, u64 object) ++{ ++ return xen_tmem_op(TMEM_FLUSH_OBJECT, pool_id, object, 0, 0, 0, 0, 0); ++} ++ ++static inline int tmem_new_pool(u64 uuid_lo, u64 uuid_hi, u32 flags) ++{ ++ BUILD_BUG_ON((TMEM_POOL_PAGEORDER < 0) || ++ (TMEM_POOL_PAGEORDER >= TMEM_POOL_PAGESIZE_MASK)); ++ flags |= TMEM_POOL_PAGEORDER << TMEM_POOL_PAGESIZE_SHIFT; ++ return xen_tmem_new_pool(TMEM_NEW_POOL, uuid_lo, uuid_hi, flags); ++} ++ ++static inline int tmem_destroy_pool(u32 pool_id) ++{ ++ return xen_tmem_op(TMEM_DESTROY_POOL, pool_id, 0, 0, 0, 0, 0, 0); ++} ++#else ++struct tmem_op { ++ u32 cmd; ++ s32 pool_id; /* private > 0; shared < 0; 0 is invalid */ ++ union { ++ struct { /* for cmd == TMEM_NEW_POOL */ ++ u64 uuid[2]; ++ u32 flags; ++ } new; ++ struct { /* for cmd == TMEM_CONTROL */ ++ u32 subop; ++ u32 cli_id; ++ u32 arg1; ++ u32 arg2; ++ void *buf; ++ } ctrl; ++ struct { ++ u64 object; ++ u32 index; ++ u32 tmem_offset; ++ u32 pfn_offset; ++ u32 len; ++ unsigned long pfn; /* page frame */ ++ } gen; ++ } u; ++}; ++#endif +--- head-2010-05-12.orig/mm/truncate.c 2010-05-12 08:55:24.000000000 +0200 ++++ head-2010-05-12/mm/truncate.c 2010-04-15 09:41:48.000000000 +0200 +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include /* grr. try_to_release_page, + do_invalidatepage */ +@@ -51,6 +52,7 @@ void do_invalidatepage(struct page *page + static inline void truncate_partial_page(struct page *page, unsigned partial) + { + zero_user_segment(page, partial, PAGE_CACHE_SIZE); ++ precache_flush(page->mapping, page->index); + if (page_has_private(page)) + do_invalidatepage(page, partial); + } +@@ -108,6 +110,10 @@ truncate_complete_page(struct address_sp + clear_page_mlock(page); + remove_from_page_cache(page); + ClearPageMappedToDisk(page); ++ /* this must be after the remove_from_page_cache which ++ * calls precache_put ++ */ ++ precache_flush(mapping, page->index); + page_cache_release(page); /* pagecache ref */ + return 0; + } +@@ -215,6 +221,7 @@ void truncate_inode_pages_range(struct a + pgoff_t next; + int i; + ++ precache_flush_inode(mapping); + if (mapping->nrpages == 0) + return; + +@@ -290,6 +297,7 @@ void truncate_inode_pages_range(struct a + pagevec_release(&pvec); + mem_cgroup_uncharge_end(); + } ++ precache_flush_inode(mapping); + } + EXPORT_SYMBOL(truncate_inode_pages_range); + +@@ -428,6 +436,7 @@ int invalidate_inode_pages2_range(struct + int did_range_unmap = 0; + int wrapped = 0; + ++ precache_flush_inode(mapping); + pagevec_init(&pvec, 0); + next = start; + while (next <= end && !wrapped && +@@ -486,6 +495,7 @@ int invalidate_inode_pages2_range(struct + mem_cgroup_uncharge_end(); + cond_resched(); + } ++ precache_flush_inode(mapping); + return ret; + } + EXPORT_SYMBOL_GPL(invalidate_inode_pages2_range); diff --git a/patches.xen/xen-balloon-max-target b/patches.xen/xen-balloon-max-target new file mode 100644 index 0000000..63ba1f3 --- /dev/null +++ b/patches.xen/xen-balloon-max-target @@ -0,0 +1,78 @@ +From: ccoffing@novell.com +Subject: Expose min/max limits of domain ballooning +Patch-mainline: obsolete +References: 152667, 184727 + +jb: Also added this to the sysfs representation. + +--- head-2010-02-03.orig/drivers/xen/balloon/balloon.c 2010-02-03 11:51:26.000000000 +0100 ++++ head-2010-02-03/drivers/xen/balloon/balloon.c 2010-02-03 11:56:18.000000000 +0100 +@@ -239,7 +239,7 @@ static unsigned long current_target(void + return target; + } + +-static unsigned long minimum_target(void) ++unsigned long balloon_minimum_target(void) + { + #ifndef CONFIG_XEN + #define max_pfn num_physpages +@@ -461,7 +461,7 @@ static void balloon_process(struct work_ + void balloon_set_new_target(unsigned long target) + { + /* No need for lock. Not read-modify-write updates. */ +- bs.target_pages = max(target, minimum_target()); ++ bs.target_pages = max(target, balloon_minimum_target()); + schedule_work(&balloon_worker); + } + +@@ -536,10 +536,13 @@ static int balloon_read(char *page, char + page, + "Current allocation: %8lu kB\n" + "Requested target: %8lu kB\n" ++ "Minimum target: %8lu kB\n" ++ "Maximum target: %8lu kB\n" + "Low-mem balloon: %8lu kB\n" + "High-mem balloon: %8lu kB\n" + "Driver pages: %8lu kB\n", + PAGES2KB(bs.current_pages), PAGES2KB(bs.target_pages), ++ PAGES2KB(balloon_minimum_target()), PAGES2KB(num_physpages), + PAGES2KB(bs.balloon_low), PAGES2KB(bs.balloon_high), + PAGES2KB(bs.driver_pages)); + +--- head-2010-02-03.orig/drivers/xen/balloon/common.h 2009-06-09 15:01:37.000000000 +0200 ++++ head-2010-02-03/drivers/xen/balloon/common.h 2009-08-19 10:36:49.000000000 +0200 +@@ -52,5 +52,6 @@ int balloon_sysfs_init(void); + void balloon_sysfs_exit(void); + + void balloon_set_new_target(unsigned long target); ++unsigned long balloon_minimum_target(void); + + #endif /* __XEN_BALLOON_COMMON_H__ */ +--- head-2010-02-03.orig/drivers/xen/balloon/sysfs.c 2010-01-28 10:34:35.000000000 +0100 ++++ head-2010-02-03/drivers/xen/balloon/sysfs.c 2009-08-19 10:36:47.000000000 +0200 +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -53,6 +54,8 @@ + static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL) + + BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(bs.current_pages)); ++BALLOON_SHOW(min_kb, "%lu\n", PAGES2KB(balloon_minimum_target())); ++BALLOON_SHOW(max_kb, "%lu\n", PAGES2KB(num_physpages)); + BALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(bs.balloon_low)); + BALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(bs.balloon_high)); + BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(bs.driver_pages)); +@@ -123,6 +126,8 @@ static struct sysdev_attribute *balloon_ + + static struct attribute *balloon_info_attrs[] = { + &attr_current_kb.attr, ++ &attr_min_kb.attr, ++ &attr_max_kb.attr, + &attr_low_kb.attr, + &attr_high_kb.attr, + &attr_driver_kb.attr, diff --git a/patches.xen/xen-blkback-bimodal-suse b/patches.xen/xen-blkback-bimodal-suse new file mode 100644 index 0000000..7b37197 --- /dev/null +++ b/patches.xen/xen-blkback-bimodal-suse @@ -0,0 +1,39 @@ +Subject: backward compatibility +From: Gerd Hoffmann +Patch-mainline: obsolete + +--- + linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c | 6 ++++++ + linux-2.6-xen-sparse/drivers/xen/blktap/xenbus.c | 6 ++++++ + 2 files changed, 12 insertions(+) + +--- head-2010-04-29.orig/drivers/xen/blkback/xenbus.c 2010-03-24 15:25:21.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blkback/xenbus.c 2010-03-25 14:37:55.000000000 +0100 +@@ -500,6 +500,12 @@ static int connect_ring(struct backend_i + be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; + else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) + be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; ++#if 1 /* maintain compatibility with early sles10-sp1 and paravirt netware betas */ ++ else if (0 == strcmp(protocol, "1")) ++ be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; ++ else if (0 == strcmp(protocol, "2")) ++ be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; ++#endif + else { + xenbus_dev_fatal(dev, err, "unknown fe protocol %s", protocol); + return -1; +--- head-2010-04-29.orig/drivers/xen/blktap/xenbus.c 2010-04-29 09:52:52.000000000 +0200 ++++ head-2010-04-29/drivers/xen/blktap/xenbus.c 2010-04-29 10:15:25.000000000 +0200 +@@ -441,6 +441,12 @@ static int connect_ring(struct backend_i + be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; + else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) + be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; ++#if 1 /* maintain compatibility with early sles10-sp1 and paravirt netware betas */ ++ else if (0 == strcmp(protocol, "1")) ++ be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; ++ else if (0 == strcmp(protocol, "2")) ++ be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; ++#endif + else { + xenbus_dev_fatal(dev, err, "unknown fe protocol %s", protocol); + return -1; diff --git a/patches.xen/xen-blkback-cdrom b/patches.xen/xen-blkback-cdrom new file mode 100644 index 0000000..36d9a6c --- /dev/null +++ b/patches.xen/xen-blkback-cdrom @@ -0,0 +1,233 @@ +Subject: CDROM removable media-present attribute plus handling code +From: plc@novell.com +Patch-mainline: obsolete +References: 159907 + +--- head-2010-03-24.orig/drivers/xen/blkback/Makefile 2009-06-09 15:01:37.000000000 +0200 ++++ head-2010-03-24/drivers/xen/blkback/Makefile 2010-03-25 14:38:02.000000000 +0100 +@@ -1,4 +1,4 @@ + obj-$(CONFIG_XEN_BLKDEV_BACKEND) := blkbk.o + obj-$(CONFIG_XEN_BLKBACK_PAGEMAP) += blkback-pagemap.o + +-blkbk-y := blkback.o xenbus.o interface.o vbd.o ++blkbk-y := blkback.o xenbus.o interface.o vbd.o cdrom.o +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-03-24/drivers/xen/blkback/cdrom.c 2010-03-25 14:38:02.000000000 +0100 +@@ -0,0 +1,162 @@ ++/****************************************************************************** ++ * blkback/cdrom.c ++ * ++ * Routines for managing cdrom watch and media-present attribute of a ++ * cdrom type virtual block device (VBD). ++ * ++ * Copyright (c) 2003-2005, Keir Fraser & Steve Hand ++ * Copyright (c) 2007 Pat Campbell ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation; or, when distributed ++ * separately from the Linux kernel or incorporated into other ++ * software packages, subject to the following license: ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this source file (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, copy, modify, ++ * merge, publish, distribute, sublicense, and/or sell copies of the Software, ++ * and to permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#include "common.h" ++ ++#undef DPRINTK ++#define DPRINTK(_f, _a...) \ ++ printk("(%s() file=%s, line=%d) " _f "\n", \ ++ __PRETTY_FUNCTION__, __FILE__ , __LINE__ , ##_a ) ++ ++ ++#define MEDIA_PRESENT "media-present" ++ ++static void cdrom_media_changed(struct xenbus_watch *, const char **, unsigned int); ++ ++/** ++ * Writes media-present=1 attribute for the given vbd device if not ++ * already there ++ */ ++static int cdrom_xenstore_write_media_present(struct backend_info *be) ++{ ++ struct xenbus_device *dev = be->dev; ++ struct xenbus_transaction xbt; ++ int err; ++ int media_present; ++ ++ err = xenbus_scanf(XBT_NIL, dev->nodename, MEDIA_PRESENT, "%d", ++ &media_present); ++ if (0 < err) { ++ DPRINTK("already written err%d", err); ++ return(0); ++ } ++ media_present = 1; ++ ++again: ++ err = xenbus_transaction_start(&xbt); ++ if (err) { ++ xenbus_dev_fatal(dev, err, "starting transaction"); ++ return(-1); ++ } ++ ++ err = xenbus_printf(xbt, dev->nodename, MEDIA_PRESENT, "%d", media_present ); ++ if (err) { ++ xenbus_dev_fatal(dev, err, "writing %s/%s", ++ dev->nodename, MEDIA_PRESENT); ++ goto abort; ++ } ++ err = xenbus_transaction_end(xbt, 0); ++ if (err == -EAGAIN) ++ goto again; ++ if (err) ++ xenbus_dev_fatal(dev, err, "ending transaction"); ++ return 0; ++ abort: ++ xenbus_transaction_end(xbt, 1); ++ return -1; ++} ++ ++/** ++ * ++ */ ++static int cdrom_is_type(struct backend_info *be) ++{ ++ DPRINTK("type:%x", be->blkif->vbd.type ); ++ return (be->blkif->vbd.type & VDISK_CDROM) ++ && (be->blkif->vbd.type & GENHD_FL_REMOVABLE); ++} ++ ++/** ++ * ++ */ ++void cdrom_add_media_watch(struct backend_info *be) ++{ ++ struct xenbus_device *dev = be->dev; ++ int err; ++ ++ DPRINTK("nodename:%s", dev->nodename); ++ if (cdrom_is_type(be)) { ++ DPRINTK("is a cdrom"); ++ if ( cdrom_xenstore_write_media_present(be) == 0 ) { ++ DPRINTK( "xenstore wrote OK"); ++ err = xenbus_watch_path2(dev, dev->nodename, MEDIA_PRESENT, ++ &be->cdrom_watch, ++ cdrom_media_changed); ++ if (err) ++ DPRINTK( "media_present watch add failed" ); ++ } ++ } ++} ++ ++/** ++ * Callback received when the "media_present" xenstore node is changed ++ */ ++static void cdrom_media_changed(struct xenbus_watch *watch, ++ const char **vec, unsigned int len) ++{ ++ int err; ++ unsigned media_present; ++ struct backend_info *be ++ = container_of(watch, struct backend_info, cdrom_watch); ++ struct xenbus_device *dev = be->dev; ++ ++ if (!cdrom_is_type(be)) { ++ DPRINTK("callback not for a cdrom" ); ++ return; ++ } ++ ++ err = xenbus_scanf(XBT_NIL, dev->nodename, MEDIA_PRESENT, "%d", ++ &media_present); ++ if (err == 0 || err == -ENOENT) { ++ DPRINTK("xenbus_read of cdrom media_present node error:%d",err); ++ return; ++ } ++ ++ if (media_present == 0) ++ vbd_free(&be->blkif->vbd); ++ else { ++ char *p = strrchr(dev->otherend, '/') + 1; ++ long handle = simple_strtoul(p, NULL, 0); ++ ++ if (!be->blkif->vbd.bdev) { ++ err = vbd_create(be->blkif, handle, be->major, be->minor, ++ !strchr(be->mode, 'w'), 1); ++ if (err) { ++ be->major = be->minor = 0; ++ xenbus_dev_fatal(dev, err, "creating vbd structure"); ++ return; ++ } ++ } ++ } ++} +--- head-2010-03-24.orig/drivers/xen/blkback/common.h 2010-03-24 15:09:22.000000000 +0100 ++++ head-2010-03-24/drivers/xen/blkback/common.h 2010-03-25 14:38:02.000000000 +0100 +@@ -106,6 +106,7 @@ struct backend_info + struct xenbus_device *dev; + blkif_t *blkif; + struct xenbus_watch backend_watch; ++ struct xenbus_watch cdrom_watch; + unsigned major; + unsigned minor; + char *mode; +@@ -152,4 +153,7 @@ int blkif_schedule(void *arg); + int blkback_barrier(struct xenbus_transaction xbt, + struct backend_info *be, int state); + ++/* cdrom media change */ ++void cdrom_add_media_watch(struct backend_info *be); ++ + #endif /* __BLKIF__BACKEND__COMMON_H__ */ +--- head-2010-03-24.orig/drivers/xen/blkback/vbd.c 2010-03-24 15:25:21.000000000 +0100 ++++ head-2010-03-24/drivers/xen/blkback/vbd.c 2010-03-25 14:38:02.000000000 +0100 +@@ -108,6 +108,9 @@ int vbd_translate(struct phys_req *req, + if ((operation != READ) && vbd->readonly) + goto out; + ++ if (vbd->bdev == NULL) ++ goto out; ++ + if (unlikely((req->sector_number + req->nr_sects) > vbd_sz(vbd))) + goto out; + +--- head-2010-03-24.orig/drivers/xen/blkback/xenbus.c 2010-03-25 14:37:59.000000000 +0100 ++++ head-2010-03-24/drivers/xen/blkback/xenbus.c 2010-03-25 14:38:02.000000000 +0100 +@@ -187,6 +187,12 @@ static int blkback_remove(struct xenbus_ + be->backend_watch.node = NULL; + } + ++ if (be->cdrom_watch.node) { ++ unregister_xenbus_watch(&be->cdrom_watch); ++ kfree(be->cdrom_watch.node); ++ be->cdrom_watch.node = NULL; ++ } ++ + if (be->blkif) { + blkif_disconnect(be->blkif); + vbd_free(&be->blkif->vbd); +@@ -343,6 +349,9 @@ static void backend_changed(struct xenbu + + /* We're potentially connected now */ + update_blkif_status(be->blkif); ++ ++ /* Add watch for cdrom media status if necessay */ ++ cdrom_add_media_watch(be); + } + } + diff --git a/patches.xen/xen-blkfront-cdrom b/patches.xen/xen-blkfront-cdrom new file mode 100644 index 0000000..1b231b4 --- /dev/null +++ b/patches.xen/xen-blkfront-cdrom @@ -0,0 +1,707 @@ +From: plc@novell.com +Subject: implement forwarding of CD-ROM specific commands +Patch-mainline: obsolete +References: fate#300964 + +--- head-2010-04-15.orig/drivers/cdrom/Makefile 2010-04-28 15:44:04.000000000 +0200 ++++ head-2010-04-15/drivers/cdrom/Makefile 2010-03-25 14:38:07.000000000 +0100 +@@ -9,6 +9,7 @@ obj-$(CONFIG_BLK_DEV_IDECD) += + obj-$(CONFIG_BLK_DEV_SR) += cdrom.o + obj-$(CONFIG_PARIDE_PCD) += cdrom.o + obj-$(CONFIG_CDROM_PKTCDVD) += cdrom.o ++obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += cdrom.o + + obj-$(CONFIG_VIOCD) += viocd.o cdrom.o + obj-$(CONFIG_GDROM) += gdrom.o cdrom.o +--- head-2010-04-15.orig/drivers/xen/blkfront/Makefile 2007-06-12 13:13:44.000000000 +0200 ++++ head-2010-04-15/drivers/xen/blkfront/Makefile 2010-03-25 14:38:07.000000000 +0100 +@@ -1,5 +1,5 @@ + + obj-$(CONFIG_XEN_BLKDEV_FRONTEND) := xenblk.o + +-xenblk-objs := blkfront.o vbd.o ++xenblk-objs := blkfront.o vbd.o vcd.o + +--- head-2010-04-15.orig/drivers/xen/blkfront/blkfront.c 2010-03-25 14:38:05.000000000 +0100 ++++ head-2010-04-15/drivers/xen/blkfront/blkfront.c 2010-03-25 14:38:07.000000000 +0100 +@@ -395,6 +395,8 @@ static void connect(struct blkfront_info + add_disk(info->gd); + + info->is_ready = 1; ++ ++ register_vcd(info); + } + + /** +@@ -424,6 +426,8 @@ static void blkfront_closing(struct blkf + + xlvbd_sysfs_delif(info); + ++ unregister_vcd(info); ++ + xlvbd_del(info); + + out: +--- head-2010-04-15.orig/drivers/xen/blkfront/block.h 2010-03-24 15:14:47.000000000 +0100 ++++ head-2010-04-15/drivers/xen/blkfront/block.h 2010-03-25 14:38:07.000000000 +0100 +@@ -163,4 +163,8 @@ static inline void xlvbd_sysfs_delif(str + } + #endif + ++/* Virtual cdrom block-device */ ++extern void register_vcd(struct blkfront_info *info); ++extern void unregister_vcd(struct blkfront_info *info); ++ + #endif /* __XEN_DRIVERS_BLOCK_H__ */ +--- head-2010-04-15.orig/drivers/xen/blkfront/vbd.c 2010-03-25 16:41:12.000000000 +0100 ++++ head-2010-04-15/drivers/xen/blkfront/vbd.c 2010-03-25 14:38:07.000000000 +0100 +@@ -369,7 +369,8 @@ xlvbd_add(blkif_sector_t capacity, int v + goto out; + info->mi = mi; + +- if ((minor & ((1 << mi->type->partn_shift) - 1)) == 0) ++ if (!(vdisk_info & VDISK_CDROM) && ++ (minor & ((1 << mi->type->partn_shift) - 1)) == 0) + nr_minors = 1 << mi->type->partn_shift; + + err = xlbd_reserve_minors(mi, minor, nr_minors); +@@ -383,7 +384,7 @@ xlvbd_add(blkif_sector_t capacity, int v + + offset = mi->index * mi->type->disks_per_major + + (minor >> mi->type->partn_shift); +- if (nr_minors > 1) { ++ if (nr_minors > 1 || (vdisk_info & VDISK_CDROM)) { + if (offset < 26) { + sprintf(gd->disk_name, "%s%c", + mi->type->diskname, 'a' + offset ); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-15/drivers/xen/blkfront/vcd.c 2010-04-28 16:17:50.000000000 +0200 +@@ -0,0 +1,505 @@ ++/******************************************************************************* ++ * vcd.c ++ * ++ * Implements CDROM cmd packet passing between frontend guest and backend driver. ++ * ++ * Copyright (c) 2008, Pat Campell plc@novell.com ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this source file (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, copy, modify, ++ * merge, publish, distribute, sublicense, and/or sell copies of the Software, ++ * and to permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#define REVISION "$Revision: 1.0 $" ++ ++#include ++#include ++#include ++#include ++#include ++#include "block.h" ++ ++/* List of cdrom_device_info, can have as many as blkfront supports */ ++struct vcd_disk { ++ struct list_head vcd_entry; ++ struct cdrom_device_info vcd_cdrom_info; ++ spinlock_t vcd_cdrom_info_lock; ++}; ++static LIST_HEAD(vcd_disks); ++static DEFINE_SPINLOCK(vcd_disks_lock); ++ ++static struct vcd_disk *xencdrom_get_list_entry(struct gendisk *disk) ++{ ++ struct vcd_disk *ret_vcd = NULL; ++ struct vcd_disk *vcd; ++ ++ spin_lock(&vcd_disks_lock); ++ list_for_each_entry(vcd, &vcd_disks, vcd_entry) { ++ if (vcd->vcd_cdrom_info.disk == disk) { ++ spin_lock(&vcd->vcd_cdrom_info_lock); ++ ret_vcd = vcd; ++ break; ++ } ++ } ++ spin_unlock(&vcd_disks_lock); ++ return ret_vcd; ++} ++ ++static void submit_message(struct blkfront_info *info, void *sp) ++{ ++ struct request *req = NULL; ++ ++ req = blk_get_request(info->rq, READ, __GFP_WAIT); ++ if (blk_rq_map_kern(info->rq, req, sp, PAGE_SIZE, __GFP_WAIT)) ++ goto out; ++ ++ req->rq_disk = info->gd; ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18) ++ req->cmd_type = REQ_TYPE_BLOCK_PC; ++ req->cmd_flags |= REQ_NOMERGE; ++#else ++ req->flags |= REQ_BLOCK_PC; ++#endif ++ req->__sector = 0; ++ req->__data_len = PAGE_SIZE; ++ req->timeout = 60*HZ; ++ ++ blk_execute_rq(req->q, info->gd, req, 1); ++ ++out: ++ blk_put_request(req); ++} ++ ++static int submit_cdrom_cmd(struct blkfront_info *info, ++ struct packet_command *cgc) ++{ ++ int ret = 0; ++ struct page *page; ++ size_t size; ++ union xen_block_packet *sp; ++ struct xen_cdrom_packet *xcp; ++ struct vcd_generic_command *vgc; ++ ++ if (cgc->buffer && cgc->buflen > MAX_PACKET_DATA) { ++ printk(KERN_WARNING "%s() Packet buffer length is to large \n", __func__); ++ return -EIO; ++ } ++ ++ page = alloc_page(GFP_NOIO|__GFP_ZERO); ++ if (!page) { ++ printk(KERN_CRIT "%s() Unable to allocate page\n", __func__); ++ return -ENOMEM; ++ } ++ ++ size = PAGE_SIZE; ++ sp = page_address(page); ++ xcp = &(sp->xcp); ++ xcp->type = XEN_TYPE_CDROM_PACKET; ++ xcp->payload_offset = PACKET_PAYLOAD_OFFSET; ++ ++ vgc = (struct vcd_generic_command *)((char *)sp + xcp->payload_offset); ++ memcpy(vgc->cmd, cgc->cmd, CDROM_PACKET_SIZE); ++ vgc->stat = cgc->stat; ++ vgc->data_direction = cgc->data_direction; ++ vgc->quiet = cgc->quiet; ++ vgc->timeout = cgc->timeout; ++ if (cgc->sense) { ++ vgc->sense_offset = PACKET_SENSE_OFFSET; ++ memcpy((char *)sp + vgc->sense_offset, cgc->sense, sizeof(struct request_sense)); ++ } ++ if (cgc->buffer) { ++ vgc->buffer_offset = PACKET_BUFFER_OFFSET; ++ memcpy((char *)sp + vgc->buffer_offset, cgc->buffer, cgc->buflen); ++ vgc->buflen = cgc->buflen; ++ } ++ ++ submit_message(info,sp); ++ ++ if (xcp->ret) ++ ret = xcp->err; ++ ++ if (cgc->sense) { ++ memcpy(cgc->sense, (char *)sp + PACKET_SENSE_OFFSET, sizeof(struct request_sense)); ++ } ++ if (cgc->buffer && cgc->buflen) { ++ memcpy(cgc->buffer, (char *)sp + PACKET_BUFFER_OFFSET, cgc->buflen); ++ } ++ ++ __free_page(page); ++ return ret; ++} ++ ++ ++static int xencdrom_open(struct cdrom_device_info *cdi, int purpose) ++{ ++ int ret = 0; ++ struct page *page; ++ struct blkfront_info *info; ++ union xen_block_packet *sp; ++ struct xen_cdrom_open *xco; ++ ++ info = cdi->disk->private_data; ++ ++ if (!info->xbdev) ++ return -ENODEV; ++ ++ if (strlen(info->xbdev->otherend) > MAX_PACKET_DATA) { ++ return -EIO; ++ } ++ ++ page = alloc_page(GFP_NOIO|__GFP_ZERO); ++ if (!page) { ++ printk(KERN_CRIT "%s() Unable to allocate page\n", __func__); ++ return -ENOMEM; ++ } ++ ++ sp = page_address(page); ++ xco = &(sp->xco); ++ xco->type = XEN_TYPE_CDROM_OPEN; ++ xco->payload_offset = sizeof(struct xen_cdrom_open); ++ strcpy((char *)sp + xco->payload_offset, info->xbdev->otherend); ++ ++ submit_message(info,sp); ++ ++ if (xco->ret) { ++ ret = xco->err; ++ goto out; ++ } ++ ++ if (xco->media_present) ++ set_capacity(cdi->disk, xco->sectors); ++ ++out: ++ __free_page(page); ++ return ret; ++} ++ ++static void xencdrom_release(struct cdrom_device_info *cdi) ++{ ++} ++ ++static int xencdrom_media_changed(struct cdrom_device_info *cdi, int disc_nr) ++{ ++ int ret; ++ struct page *page; ++ struct blkfront_info *info; ++ union xen_block_packet *sp; ++ struct xen_cdrom_media_changed *xcmc; ++ ++ info = cdi->disk->private_data; ++ ++ page = alloc_page(GFP_NOIO|__GFP_ZERO); ++ if (!page) { ++ printk(KERN_CRIT "%s() Unable to allocate page\n", __func__); ++ return -ENOMEM; ++ } ++ ++ sp = page_address(page); ++ xcmc = &(sp->xcmc); ++ xcmc->type = XEN_TYPE_CDROM_MEDIA_CHANGED; ++ submit_message(info,sp); ++ ret = xcmc->media_changed; ++ ++ __free_page(page); ++ ++ return ret; ++} ++ ++static int xencdrom_tray_move(struct cdrom_device_info *cdi, int position) ++{ ++ int ret; ++ struct packet_command cgc; ++ struct blkfront_info *info; ++ ++ info = cdi->disk->private_data; ++ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); ++ cgc.cmd[0] = GPCMD_START_STOP_UNIT; ++ if (position) ++ cgc.cmd[4] = 2; ++ else ++ cgc.cmd[4] = 3; ++ ret = submit_cdrom_cmd(info, &cgc); ++ return ret; ++} ++ ++static int xencdrom_lock_door(struct cdrom_device_info *cdi, int lock) ++{ ++ int ret = 0; ++ struct blkfront_info *info; ++ struct packet_command cgc; ++ ++ info = cdi->disk->private_data; ++ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); ++ cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; ++ cgc.cmd[4] = lock; ++ ret = submit_cdrom_cmd(info, &cgc); ++ return ret; ++} ++ ++static int xencdrom_packet(struct cdrom_device_info *cdi, ++ struct packet_command *cgc) ++{ ++ int ret = -EIO; ++ struct blkfront_info *info; ++ ++ info = cdi->disk->private_data; ++ ret = submit_cdrom_cmd(info, cgc); ++ cgc->stat = ret; ++ return ret; ++} ++ ++static int xencdrom_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, ++ void *arg) ++{ ++ return -EINVAL; ++} ++ ++/* Query backend to see if CDROM packets are supported */ ++static int xencdrom_supported(struct blkfront_info *info) ++{ ++ struct page *page; ++ union xen_block_packet *sp; ++ struct xen_cdrom_support *xcs; ++ ++ page = alloc_page(GFP_NOIO|__GFP_ZERO); ++ if (!page) { ++ printk(KERN_CRIT "%s() Unable to allocate page\n", __func__); ++ return -ENOMEM; ++ } ++ ++ sp = page_address(page); ++ xcs = &(sp->xcs); ++ xcs->type = XEN_TYPE_CDROM_SUPPORT; ++ submit_message(info,sp); ++ return xcs->supported; ++} ++ ++static struct cdrom_device_ops xencdrom_dops = { ++ .open = xencdrom_open, ++ .release = xencdrom_release, ++ .media_changed = xencdrom_media_changed, ++ .tray_move = xencdrom_tray_move, ++ .lock_door = xencdrom_lock_door, ++ .generic_packet = xencdrom_packet, ++ .audio_ioctl = xencdrom_audio_ioctl, ++ .capability = (CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | \ ++ CDC_MEDIA_CHANGED | CDC_GENERIC_PACKET | CDC_DVD | \ ++ CDC_CD_R), ++ .n_minors = 1, ++}; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static int xencdrom_block_open(struct inode *inode, struct file *file) ++{ ++ struct block_device *bd = inode->i_bdev; ++#else ++static int xencdrom_block_open(struct block_device *bd, fmode_t mode) ++{ ++#endif ++ struct blkfront_info *info = bd->bd_disk->private_data; ++ struct vcd_disk *vcd; ++ int ret = 0; ++ ++ if (!info->xbdev) ++ return -ENODEV; ++ ++ if ((vcd = xencdrom_get_list_entry(info->gd))) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ ret = cdrom_open(&vcd->vcd_cdrom_info, inode, file); ++#else ++ ret = cdrom_open(&vcd->vcd_cdrom_info, bd, mode); ++#endif ++ info->users = vcd->vcd_cdrom_info.use_count; ++ spin_unlock(&vcd->vcd_cdrom_info_lock); ++ } ++ return ret; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static int xencdrom_block_release(struct inode *inode, struct file *file) ++{ ++ struct gendisk *gd = inode->i_bdev->bd_disk; ++#else ++static int xencdrom_block_release(struct gendisk *gd, fmode_t mode) ++{ ++#endif ++ struct blkfront_info *info = gd->private_data; ++ struct vcd_disk *vcd; ++ int ret = 0; ++ ++ if ((vcd = xencdrom_get_list_entry(info->gd))) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ ret = cdrom_release(&vcd->vcd_cdrom_info, file); ++#else ++ cdrom_release(&vcd->vcd_cdrom_info, mode); ++#endif ++ spin_unlock(&vcd->vcd_cdrom_info_lock); ++ if (vcd->vcd_cdrom_info.use_count == 0) { ++ info->users = 1; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ blkif_release(inode, file); ++#else ++ blkif_release(gd, mode); ++#endif ++ } ++ } ++ return ret; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static int xencdrom_block_ioctl(struct inode *inode, struct file *file, ++ unsigned cmd, unsigned long arg) ++{ ++ struct block_device *bd = inode->i_bdev; ++#else ++static int xencdrom_block_ioctl(struct block_device *bd, fmode_t mode, ++ unsigned cmd, unsigned long arg) ++{ ++#endif ++ struct blkfront_info *info = bd->bd_disk->private_data; ++ struct vcd_disk *vcd; ++ int ret = 0; ++ ++ if (!(vcd = xencdrom_get_list_entry(info->gd))) ++ goto out; ++ ++ switch (cmd) { ++ case 2285: /* SG_IO */ ++ ret = -ENOSYS; ++ break; ++ case CDROMEJECT: ++ ret = xencdrom_tray_move(&vcd->vcd_cdrom_info, 1); ++ break; ++ case CDROMCLOSETRAY: ++ ret = xencdrom_tray_move(&vcd->vcd_cdrom_info, 0); ++ break; ++ case CDROM_GET_CAPABILITY: ++ ret = vcd->vcd_cdrom_info.ops->capability & ~vcd->vcd_cdrom_info.mask; ++ break; ++ case CDROM_SET_OPTIONS: ++ ret = vcd->vcd_cdrom_info.options; ++ break; ++ case CDROM_SEND_PACKET: ++ ret = submit_cdrom_cmd(info, (struct packet_command *)arg); ++ break; ++ default: ++ /* Not supported, augment supported above if necessary */ ++ printk("%s():%d Unsupported IOCTL:%x \n", __func__, __LINE__, cmd); ++ ret = -ENOTTY; ++ break; ++ } ++ spin_unlock(&vcd->vcd_cdrom_info_lock); ++out: ++ return ret; ++} ++ ++/* Called as result of cdrom_open, vcd_cdrom_info_lock already held */ ++static int xencdrom_block_media_changed(struct gendisk *disk) ++{ ++ struct vcd_disk *vcd; ++ struct vcd_disk *ret_vcd = NULL; ++ int ret = 0; ++ ++ spin_lock(&vcd_disks_lock); ++ list_for_each_entry(vcd, &vcd_disks, vcd_entry) { ++ if (vcd->vcd_cdrom_info.disk == disk) { ++ ret_vcd = vcd; ++ break; ++ } ++ } ++ spin_unlock(&vcd_disks_lock); ++ if (ret_vcd) { ++ ret = cdrom_media_changed(&ret_vcd->vcd_cdrom_info); ++ } ++ return ret; ++} ++ ++static const struct block_device_operations xencdrom_bdops = ++{ ++ .owner = THIS_MODULE, ++ .open = xencdrom_block_open, ++ .release = xencdrom_block_release, ++ .ioctl = xencdrom_block_ioctl, ++ .media_changed = xencdrom_block_media_changed, ++}; ++ ++void register_vcd(struct blkfront_info *info) ++{ ++ struct gendisk *gd = info->gd; ++ struct vcd_disk *vcd; ++ ++ /* Make sure this is for a CD device */ ++ if (!(gd->flags & GENHD_FL_CD)) ++ goto out; ++ ++ /* Make sure we have backend support */ ++ if (!xencdrom_supported(info)) { ++ goto out; ++ } ++ ++ /* Create new vcd_disk and fill in cdrom_info */ ++ vcd = (struct vcd_disk *)kzalloc(sizeof(struct vcd_disk), GFP_KERNEL); ++ if (!vcd) { ++ printk(KERN_INFO "%s(): Unable to allocate vcd struct!\n", __func__); ++ goto out; ++ } ++ spin_lock_init(&vcd->vcd_cdrom_info_lock); ++ ++ vcd->vcd_cdrom_info.ops = &xencdrom_dops; ++ vcd->vcd_cdrom_info.speed = 4; ++ vcd->vcd_cdrom_info.capacity = 1; ++ vcd->vcd_cdrom_info.options = 0; ++ strcpy(vcd->vcd_cdrom_info.name, gd->disk_name); ++ vcd->vcd_cdrom_info.mask = (CDC_CD_RW | CDC_DVD_R | CDC_DVD_RAM | ++ CDC_SELECT_DISC | CDC_SELECT_SPEED | ++ CDC_MRW | CDC_MRW_W | CDC_RAM); ++ ++ if (register_cdrom(&(vcd->vcd_cdrom_info)) != 0) { ++ printk(KERN_WARNING "%s() Cannot register blkdev as a cdrom %d!\n", __func__, ++ gd->major); ++ goto err_out; ++ } ++ gd->fops = &xencdrom_bdops; ++ vcd->vcd_cdrom_info.disk = gd; ++ ++ spin_lock(&vcd_disks_lock); ++ list_add(&(vcd->vcd_entry), &vcd_disks); ++ spin_unlock(&vcd_disks_lock); ++out: ++ return; ++err_out: ++ kfree(vcd); ++} ++ ++void unregister_vcd(struct blkfront_info *info) { ++ struct gendisk *gd = info->gd; ++ struct vcd_disk *vcd; ++ ++ spin_lock(&vcd_disks_lock); ++ list_for_each_entry(vcd, &vcd_disks, vcd_entry) { ++ if (vcd->vcd_cdrom_info.disk == gd) { ++ spin_lock(&vcd->vcd_cdrom_info_lock); ++ unregister_cdrom(&vcd->vcd_cdrom_info); ++ list_del(&vcd->vcd_entry); ++ spin_unlock(&vcd->vcd_cdrom_info_lock); ++ kfree(vcd); ++ break; ++ } ++ } ++ spin_unlock(&vcd_disks_lock); ++} ++ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-15/include/xen/interface/io/cdromif.h 2010-03-25 14:38:07.000000000 +0100 +@@ -0,0 +1,120 @@ ++/****************************************************************************** ++ * cdromif.h ++ * ++ * Shared definitions between backend driver and Xen guest Virtual CDROM ++ * block device. ++ * ++ * Copyright (c) 2008, Pat Campell plc@novell.com ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this source file (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, copy, modify, ++ * merge, publish, distribute, sublicense, and/or sell copies of the Software, ++ * and to permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#ifndef __XEN_PUBLIC_IO_CDROMIF_H__ ++#define __XEN_PUBLIC_IO_CDROMIF_H__ ++ ++/* ++ * Queries backend for CDROM support ++ */ ++#define XEN_TYPE_CDROM_SUPPORT _IO('c', 1) ++ ++struct xen_cdrom_support ++{ ++ uint32_t type; ++ int8_t ret; /* returned, 0 succeded, -1 error */ ++ int8_t err; /* returned, backend errno */ ++ int8_t supported; /* returned, 1 supported */ ++}; ++ ++/* ++ * Opens backend device, returns drive geometry or ++ * any encountered errors ++ */ ++#define XEN_TYPE_CDROM_OPEN _IO('c', 2) ++ ++struct xen_cdrom_open ++{ ++ uint32_t type; ++ int8_t ret; ++ int8_t err; ++ int8_t pad; ++ int8_t media_present; /* returned */ ++ uint32_t sectors; /* returned */ ++ uint32_t sector_size; /* returned */ ++ int32_t payload_offset; /* offset to backend node name payload */ ++}; ++ ++/* ++ * Queries backend for media changed status ++ */ ++#define XEN_TYPE_CDROM_MEDIA_CHANGED _IO('c', 3) ++ ++struct xen_cdrom_media_changed ++{ ++ uint32_t type; ++ int8_t ret; ++ int8_t err; ++ int8_t media_changed; /* returned */ ++}; ++ ++/* ++ * Sends vcd generic CDROM packet to backend, followed ++ * immediately by the vcd_generic_command payload ++ */ ++#define XEN_TYPE_CDROM_PACKET _IO('c', 4) ++ ++struct xen_cdrom_packet ++{ ++ uint32_t type; ++ int8_t ret; ++ int8_t err; ++ int8_t pad[2]; ++ int32_t payload_offset; /* offset to vcd_generic_command payload */ ++}; ++ ++/* CDROM_PACKET_COMMAND, payload for XEN_TYPE_CDROM_PACKET */ ++struct vcd_generic_command ++{ ++ uint8_t cmd[CDROM_PACKET_SIZE]; ++ uint8_t pad[4]; ++ uint32_t buffer_offset; ++ uint32_t buflen; ++ int32_t stat; ++ uint32_t sense_offset; ++ uint8_t data_direction; ++ uint8_t pad1[3]; ++ int32_t quiet; ++ int32_t timeout; ++}; ++ ++union xen_block_packet ++{ ++ uint32_t type; ++ struct xen_cdrom_support xcs; ++ struct xen_cdrom_open xco; ++ struct xen_cdrom_media_changed xcmc; ++ struct xen_cdrom_packet xcp; ++}; ++ ++#define PACKET_PAYLOAD_OFFSET (sizeof(struct xen_cdrom_packet)) ++#define PACKET_SENSE_OFFSET (PACKET_PAYLOAD_OFFSET + sizeof(struct vcd_generic_command)) ++#define PACKET_BUFFER_OFFSET (PACKET_SENSE_OFFSET + sizeof(struct request_sense)) ++#define MAX_PACKET_DATA (PAGE_SIZE - sizeof(struct xen_cdrom_packet) - \ ++ sizeof(struct vcd_generic_command) - sizeof(struct request_sense)) ++ ++#endif diff --git a/patches.xen/xen-blkif-protocol-fallback-hack b/patches.xen/xen-blkif-protocol-fallback-hack new file mode 100644 index 0000000..ef35e69 --- /dev/null +++ b/patches.xen/xen-blkif-protocol-fallback-hack @@ -0,0 +1,225 @@ +Subject: 32-on-64 blkif protocol negotiation fallback for old guests. +From: kraxel@suse.de +References: 244055 +Patch-mainline: never. + +See the comment below. Oh well. + +--- head-2010-04-29.orig/drivers/xen/Kconfig 2010-03-31 14:08:31.000000000 +0200 ++++ head-2010-04-29/drivers/xen/Kconfig 2010-03-31 14:08:50.000000000 +0200 +@@ -30,6 +30,9 @@ config XEN_PRIVCMD + def_bool y + depends on PROC_FS + ++config XEN_DOMCTL ++ tristate ++ + config XEN_XENBUS_DEV + def_bool y + depends on PROC_FS +@@ -49,6 +52,7 @@ config XEN_BLKDEV_BACKEND + tristate "Block-device backend driver" + depends on XEN_BACKEND + default XEN_BACKEND ++ select XEN_DOMCTL + help + The block-device backend driver allows the kernel to export its + block devices to other guests via a high-performance shared-memory +@@ -58,6 +62,7 @@ config XEN_BLKDEV_TAP + tristate "Block-device tap backend driver" + depends on XEN_BACKEND + default XEN_BACKEND ++ select XEN_DOMCTL + help + The block tap driver is an alternative to the block back driver + and allows VM block requests to be redirected to userspace through +--- head-2010-04-29.orig/drivers/xen/blkback/xenbus.c 2010-03-25 14:37:55.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blkback/xenbus.c 2010-03-25 14:37:59.000000000 +0100 +@@ -21,6 +21,7 @@ + #include + #include + #include "common.h" ++#include "../core/domctl.h" + + #undef DPRINTK + #define DPRINTK(fmt, args...) \ +@@ -492,8 +493,10 @@ static int connect_ring(struct backend_i + be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; + err = xenbus_gather(XBT_NIL, dev->otherend, "protocol", + "%63s", protocol, NULL); +- if (err) +- strcpy(protocol, "unspecified, assuming native"); ++ if (err) { ++ strcpy(protocol, "unspecified"); ++ be->blkif->blk_protocol = xen_guest_blkif_protocol(be->blkif->domid); ++ } + else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE)) + be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; + else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) +--- head-2010-04-29.orig/drivers/xen/blktap/xenbus.c 2010-04-29 10:15:25.000000000 +0200 ++++ head-2010-04-29/drivers/xen/blktap/xenbus.c 2010-04-29 10:15:31.000000000 +0200 +@@ -39,6 +39,7 @@ + #include + #include + #include "common.h" ++#include "../core/domctl.h" + + + struct backend_info +@@ -433,8 +434,10 @@ static int connect_ring(struct backend_i + be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; + err = xenbus_gather(XBT_NIL, dev->otherend, "protocol", + "%63s", protocol, NULL); +- if (err) +- strcpy(protocol, "unspecified, assuming native"); ++ if (err) { ++ strcpy(protocol, "unspecified"); ++ be->blkif->blk_protocol = xen_guest_blkif_protocol(be->blkif->domid); ++ } + else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE)) + be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; + else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) +--- head-2010-04-29.orig/drivers/xen/core/Makefile 2010-04-19 14:52:49.000000000 +0200 ++++ head-2010-04-29/drivers/xen/core/Makefile 2010-04-19 14:55:02.000000000 +0200 +@@ -12,3 +12,6 @@ obj-$(CONFIG_XEN_SYSFS) += xen_sysfs.o + obj-$(CONFIG_XEN_SMPBOOT) += smpboot.o + obj-$(CONFIG_SMP) += spinlock.o + obj-$(CONFIG_KEXEC) += machine_kexec.o ++obj-$(CONFIG_XEN_DOMCTL) += domctl.o ++CFLAGS_domctl.o := -D__XEN_PUBLIC_XEN_H__ -D__XEN_PUBLIC_GRANT_TABLE_H__ ++CFLAGS_domctl.o += -D__XEN_TOOLS__ -imacros xen/interface/domctl.h +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/drivers/xen/core/domctl.c 2010-05-07 12:12:03.000000000 +0200 +@@ -0,0 +1,127 @@ ++/* ++ * !!! dirty hack alert !!! ++ * ++ * Problem: old guests kernels don't have a "protocol" node ++ * in the frontend xenstore directory, so mixing ++ * 32 and 64bit domains doesn't work. ++ * ++ * Upstream plans to solve this in the tools, by letting them ++ * create a protocol node. Which certainly makes sense. ++ * But it isn't trivial and isn't done yet. Too bad. ++ * ++ * So for the time being we use the get_address_size domctl ++ * hypercall for a pretty good guess. Not nice as the domctl ++ * hypercall isn't supposed to be used by the kernel. Because ++ * we don't want to have dependencies between dom0 kernel and ++ * xen kernel versions. Now we have one. Ouch. ++ */ ++#undef __XEN_PUBLIC_XEN_H__ ++#undef __XEN_PUBLIC_GRANT_TABLE_H__ ++#undef __XEN_TOOLS__ ++#include ++#include ++#include ++#include ++ ++#include "domctl.h" ++ ++/* stuff copied from xen/interface/domctl.h, which we can't ++ * include directly for the reasons outlined above .... */ ++ ++typedef struct xen_domctl_address_size { ++ uint32_t size; ++} xen_domctl_address_size_t; ++ ++typedef __attribute__((aligned(8))) uint64_t uint64_aligned_t; ++ ++union xen_domctl { ++ /* v4: sle10 sp1: xen 3.0.4 + 32-on-64 patches */ ++ struct { ++ uint32_t cmd; ++ uint32_t interface_version; ++ domid_t domain; ++ union { ++ /* left out lots of other struct xen_domctl_foobar */ ++ struct xen_domctl_address_size address_size; ++ uint64_t dummy_align; ++ uint8_t dummy_pad[128]; ++ }; ++ } v4; ++ ++ /* ++ * v5: upstream: xen 3.1 ++ * v6: upstream: xen 4.0 ++ * v7: sle11 sp1: xen 4.0 + cpupools patches ++ */ ++ struct { ++ uint32_t cmd; ++ uint32_t interface_version; ++ domid_t domain; ++ union { ++ struct xen_domctl_address_size address_size; ++ uint64_aligned_t dummy_align; ++ uint8_t dummy_pad[128]; ++ }; ++ } v5, v6, v7; ++}; ++ ++/* The actual code comes here */ ++ ++static inline int hypervisor_domctl(void *domctl) ++{ ++ return _hypercall1(int, domctl, domctl); ++} ++ ++int xen_guest_address_size(int domid) ++{ ++ union xen_domctl domctl; ++ int low, ret; ++ ++#define guest_address_size(ver) do { \ ++ memset(&domctl, 0, sizeof(domctl)); \ ++ domctl.v##ver.cmd = XEN_DOMCTL_get_address_size; \ ++ domctl.v##ver.interface_version = low = ver; \ ++ domctl.v##ver.domain = domid; \ ++ ret = hypervisor_domctl(&domctl) ?: domctl.v##ver.address_size.size; \ ++ if (ret == 32 || ret == 64) { \ ++ printk("v" #ver " domctl worked ok: dom%d is %d-bit\n", \ ++ domid, ret); \ ++ return ret; \ ++ } \ ++} while (0) ++ ++ BUILD_BUG_ON(XEN_DOMCTL_INTERFACE_VERSION > 7); ++ guest_address_size(7); ++#if CONFIG_XEN_COMPAT < 0x040100 ++ guest_address_size(6); ++#endif ++#if CONFIG_XEN_COMPAT < 0x040000 ++ guest_address_size(5); ++#endif ++#if CONFIG_XEN_COMPAT < 0x030100 ++ guest_address_size(4); ++#endif ++ ++ ret = BITS_PER_LONG; ++ printk("v%d...7 domctls failed, assuming dom%d is native: %d\n", ++ low, domid, ret); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(xen_guest_address_size); ++ ++int xen_guest_blkif_protocol(int domid) ++{ ++ int address_size = xen_guest_address_size(domid); ++ ++ if (address_size == BITS_PER_LONG) ++ return BLKIF_PROTOCOL_NATIVE; ++ if (address_size == 32) ++ return BLKIF_PROTOCOL_X86_32; ++ if (address_size == 64) ++ return BLKIF_PROTOCOL_X86_64; ++ return BLKIF_PROTOCOL_NATIVE; ++} ++EXPORT_SYMBOL_GPL(xen_guest_blkif_protocol); ++ ++MODULE_LICENSE("GPL"); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/drivers/xen/core/domctl.h 2010-03-25 14:37:59.000000000 +0100 +@@ -0,0 +1,2 @@ ++int xen_guest_address_size(int domid); ++int xen_guest_blkif_protocol(int domid); diff --git a/patches.xen/xen-blktap-write-barriers b/patches.xen/xen-blktap-write-barriers new file mode 100644 index 0000000..a774c26 --- /dev/null +++ b/patches.xen/xen-blktap-write-barriers @@ -0,0 +1,105 @@ +From: kwolf@suse.de +Subject: blktap: Write Barriers +Patch-mainline: obsolete + +--- head-2010-04-29.orig/drivers/xen/blktap/blktap.c 2010-04-29 09:52:39.000000000 +0200 ++++ head-2010-04-29/drivers/xen/blktap/blktap.c 2010-04-29 10:16:10.000000000 +0200 +@@ -1388,6 +1388,9 @@ static int do_block_io_op(blkif_t *blkif + dispatch_rw_block_io(blkif, &req, pending_req); + break; + ++ case BLKIF_OP_WRITE_BARRIER: ++ /* TODO Some counter? */ ++ /* Fall through */ + case BLKIF_OP_WRITE: + blkif->st_wr_req++; + dispatch_rw_block_io(blkif, &req, pending_req); +@@ -1419,7 +1422,7 @@ static void dispatch_rw_block_io(blkif_t + pending_req_t *pending_req) + { + extern void ll_rw_block(int rw, int nr, struct buffer_head * bhs[]); +- int op, operation = (req->operation == BLKIF_OP_WRITE) ? WRITE : READ; ++ int op, operation; + struct gnttab_map_grant_ref map[BLKIF_MAX_SEGMENTS_PER_REQUEST*2]; + unsigned int nseg; + int ret, i, nr_sects = 0; +@@ -1431,6 +1434,21 @@ static void dispatch_rw_block_io(blkif_t + struct mm_struct *mm; + struct vm_area_struct *vma = NULL; + ++ switch (req->operation) { ++ case BLKIF_OP_READ: ++ operation = READ; ++ break; ++ case BLKIF_OP_WRITE: ++ operation = WRITE; ++ break; ++ case BLKIF_OP_WRITE_BARRIER: ++ operation = WRITE_BARRIER; ++ break; ++ default: ++ operation = 0; /* make gcc happy */ ++ BUG(); ++ } ++ + if (blkif->dev_num < 0 || blkif->dev_num >= MAX_TAP_DEV) + goto fail_response; + +@@ -1470,7 +1488,7 @@ static void dispatch_rw_block_io(blkif_t + + pending_req->blkif = blkif; + pending_req->id = req->id; +- pending_req->operation = operation; ++ pending_req->operation = req->operation; + pending_req->status = BLKIF_RSP_OKAY; + pending_req->nr_pages = nseg; + op = 0; +@@ -1487,7 +1505,7 @@ static void dispatch_rw_block_io(blkif_t + kvaddr = idx_to_kaddr(mmap_idx, pending_idx, i); + + flags = GNTMAP_host_map; +- if (operation == WRITE) ++ if (operation != READ) + flags |= GNTMAP_readonly; + gnttab_set_map_op(&map[op], kvaddr, flags, + req->seg[i].gref, blkif->domid); +@@ -1504,7 +1522,7 @@ static void dispatch_rw_block_io(blkif_t + + flags = GNTMAP_host_map | GNTMAP_application_map + | GNTMAP_contains_pte; +- if (operation == WRITE) ++ if (operation != READ) + flags |= GNTMAP_readonly; + gnttab_set_map_op(&map[op], ptep, flags, + req->seg[i].gref, blkif->domid); +--- head-2010-04-29.orig/drivers/xen/blktap/xenbus.c 2010-04-29 10:15:31.000000000 +0200 ++++ head-2010-04-29/drivers/xen/blktap/xenbus.c 2010-04-29 10:16:08.000000000 +0200 +@@ -402,7 +402,28 @@ static void connect(struct backend_info + int err; + + struct xenbus_device *dev = be->dev; ++ struct xenbus_transaction xbt; + ++ /* Write feature-barrier to xenstore */ ++again: ++ err = xenbus_transaction_start(&xbt); ++ if (err) { ++ xenbus_dev_fatal(dev, err, "starting transaction"); ++ return; ++ } ++ ++ err = xenbus_printf(xbt, dev->nodename, "feature-barrier", "1"); ++ if (err) { ++ xenbus_dev_fatal(dev, err, "writing feature-barrier"); ++ xenbus_transaction_end(xbt, 1); ++ return; ++ } ++ ++ err = xenbus_transaction_end(xbt, 0); ++ if (err == -EAGAIN) ++ goto again; ++ ++ /* Switch state */ + err = xenbus_switch_state(dev, XenbusStateConnected); + if (err) + xenbus_dev_fatal(dev, err, "switching to Connected state", diff --git a/patches.xen/xen-clockevents b/patches.xen/xen-clockevents new file mode 100644 index 0000000..3e1de62 --- /dev/null +++ b/patches.xen/xen-clockevents @@ -0,0 +1,975 @@ +From: jbeulich@novell.com +Subject: replace Xen's custom time handling with such using GENERIC_CLOCKEVENTS infrastructure +Patch-mainline: n/a + +Once validated this could be merged into the 2.6.?? patch. + +--- head-2010-05-12.orig/arch/x86/Kconfig 2010-03-25 16:41:03.000000000 +0100 ++++ head-2010-05-12/arch/x86/Kconfig 2010-03-25 14:39:15.000000000 +0100 +@@ -80,7 +80,6 @@ config CLOCKSOURCE_WATCHDOG + + config GENERIC_CLOCKEVENTS + def_bool y +- depends on !XEN + + config GENERIC_CLOCKEVENTS_BROADCAST + def_bool y +--- head-2010-05-12.orig/arch/x86/include/mach-xen/asm/hypervisor.h 2009-11-23 10:45:08.000000000 +0100 ++++ head-2010-05-12/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:39:15.000000000 +0100 +@@ -72,8 +72,6 @@ extern start_info_t *xen_start_info; + #define init_hypervisor(c) ((void)((c)->x86_hyper_vendor = X86_HYPER_VENDOR_XEN)) + #define init_hypervisor_platform() init_hypervisor(&boot_cpu_data) + +-struct vcpu_runstate_info *setup_runstate_area(unsigned int cpu); +- + /* arch/xen/kernel/evtchn.c */ + /* Force a proper event-channel callback from Xen. */ + void force_evtchn_callback(void); +--- head-2010-05-12.orig/arch/x86/kernel/time-xen.c 2010-05-12 09:14:09.000000000 +0200 ++++ head-2010-05-12/arch/x86/kernel/time-xen.c 2010-05-12 09:14:39.000000000 +0200 +@@ -25,7 +25,7 @@ + #include + #include + +-#include ++#include + #include + #include + +@@ -55,13 +55,7 @@ static DEFINE_PER_CPU(struct shadow_time + static struct timespec shadow_tv; + static u32 shadow_tv_version; + +-/* Keep track of last time we did processing/updating of jiffies and xtime. */ +-static u64 processed_system_time; /* System time (ns) at last processing. */ +-static DEFINE_PER_CPU(u64, processed_system_time); +- +-/* How much CPU time was spent blocked and how much was 'stolen'? */ +-static DEFINE_PER_CPU(u64, processed_stolen_time); +-static DEFINE_PER_CPU(u64, processed_blocked_time); ++static u64 jiffies_bias, system_time_bias; + + /* Current runstate of each CPU (updated automatically by the hypervisor). */ + static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate); +@@ -69,10 +63,6 @@ static DEFINE_PER_CPU(struct vcpu_runsta + /* Must be signed, as it's compared with s64 quantities which can be -ve. */ + #define NS_PER_TICK (1000000000LL/HZ) + +-static struct vcpu_set_periodic_timer xen_set_periodic_tick = { +- .period_ns = NS_PER_TICK +-}; +- + static void __clock_was_set(struct work_struct *unused) + { + clock_was_set(); +@@ -205,6 +195,11 @@ static u64 get_nsec_offset(struct shadow + return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift); + } + ++static inline u64 processed_system_time(void) ++{ ++ return (jiffies_64 - jiffies_bias) * NS_PER_TICK + system_time_bias; ++} ++ + static void __update_wallclock(time_t sec, long nsec) + { + long wtm_nsec, xtime_nsec; +@@ -212,7 +207,7 @@ static void __update_wallclock(time_t se + u64 tmp, wc_nsec; + + /* Adjust wall-clock time base. */ +- wc_nsec = processed_system_time; ++ wc_nsec = processed_system_time(); + wc_nsec += sec * (u64)NSEC_PER_SEC; + wc_nsec += nsec; + +@@ -244,6 +239,17 @@ static void update_wallclock(void) + __update_wallclock(shadow_tv.tv_sec, shadow_tv.tv_nsec); + } + ++void xen_check_wallclock_update(void) ++{ ++ if (shadow_tv_version != HYPERVISOR_shared_info->wc_version) { ++ write_seqlock(&xtime_lock); ++ update_wallclock(); ++ write_sequnlock(&xtime_lock); ++ if (keventd_up()) ++ schedule_work(&clock_was_set_work); ++ } ++} ++ + /* + * Reads a consistent set of time-base values from Xen, into a shadow data + * area. +@@ -309,7 +315,7 @@ static void sync_xen_wallclock(unsigned + op.cmd = XENPF_settime; + op.u.settime.secs = sec; + op.u.settime.nsecs = nsec; +- op.u.settime.system_time = processed_system_time; ++ op.u.settime.system_time = processed_system_time(); + WARN_ON(HYPERVISOR_platform_op(&op)); + + update_wallclock(); +@@ -320,7 +326,7 @@ static void sync_xen_wallclock(unsigned + mod_timer(&sync_xen_wallclock_timer, jiffies + 60*HZ); + } + +-static unsigned long long local_clock(void) ++unsigned long long xen_local_clock(void) + { + unsigned int cpu = get_cpu(); + struct shadow_time_info *shadow = &per_cpu(shadow_time, cpu); +@@ -344,7 +350,7 @@ static unsigned long long local_clock(vo + /* + * Runstate accounting + */ +-static void get_runstate_snapshot(struct vcpu_runstate_info *res) ++void get_runstate_snapshot(struct vcpu_runstate_info *res) + { + u64 state_time; + struct vcpu_runstate_info *state; +@@ -380,7 +386,7 @@ unsigned long long sched_clock(void) + */ + preempt_disable(); + +- now = local_clock(); ++ now = xen_local_clock(); + + get_runstate_snapshot(&runstate); + +@@ -423,140 +429,6 @@ unsigned long profile_pc(struct pt_regs + } + EXPORT_SYMBOL(profile_pc); + +-/* +- * Default timer interrupt handler +- */ +-static irqreturn_t timer_interrupt(int irq, void *dev_id) +-{ +- s64 delta, delta_cpu, stolen, blocked; +- unsigned int i, cpu = smp_processor_id(); +- struct shadow_time_info *shadow = &per_cpu(shadow_time, cpu); +- struct vcpu_runstate_info runstate; +- +- /* Keep nmi watchdog up to date */ +- inc_irq_stat(irq0_irqs); +- +- /* +- * Here we are in the timer irq handler. We just have irqs locally +- * disabled but we don't know if the timer_bh is running on the other +- * CPU. We need to avoid to SMP race with it. NOTE: we don' t need +- * the irq version of write_lock because as just said we have irq +- * locally disabled. -arca +- */ +- write_seqlock(&xtime_lock); +- +- do { +- get_time_values_from_xen(cpu); +- +- /* Obtain a consistent snapshot of elapsed wallclock cycles. */ +- delta = delta_cpu = +- shadow->system_timestamp + get_nsec_offset(shadow); +- delta -= processed_system_time; +- delta_cpu -= per_cpu(processed_system_time, cpu); +- +- get_runstate_snapshot(&runstate); +- } while (!time_values_up_to_date(cpu)); +- +- if ((unlikely(delta < -(s64)permitted_clock_jitter) || +- unlikely(delta_cpu < -(s64)permitted_clock_jitter)) +- && printk_ratelimit()) { +- printk("Timer ISR/%u: Time went backwards: " +- "delta=%lld delta_cpu=%lld shadow=%lld " +- "off=%lld processed=%lld cpu_processed=%lld\n", +- cpu, delta, delta_cpu, shadow->system_timestamp, +- (s64)get_nsec_offset(shadow), +- processed_system_time, +- per_cpu(processed_system_time, cpu)); +- for (i = 0; i < num_online_cpus(); i++) +- printk(" %d: %lld\n", i, +- per_cpu(processed_system_time, i)); +- } +- +- /* System-wide jiffy work. */ +- if (delta >= NS_PER_TICK) { +- do_div(delta, NS_PER_TICK); +- processed_system_time += delta * NS_PER_TICK; +- while (delta > HZ) { +- clobber_induction_variable(delta); +- do_timer(HZ); +- delta -= HZ; +- } +- do_timer(delta); +- } +- +- if (shadow_tv_version != HYPERVISOR_shared_info->wc_version) { +- update_wallclock(); +- if (keventd_up()) +- schedule_work(&clock_was_set_work); +- } +- +- write_sequnlock(&xtime_lock); +- +- /* +- * Account stolen ticks. +- * ensures that the ticks are accounted as stolen. +- */ +- stolen = runstate.time[RUNSTATE_runnable] +- + runstate.time[RUNSTATE_offline] +- - per_cpu(processed_stolen_time, cpu); +- if ((stolen > 0) && (delta_cpu > 0)) { +- delta_cpu -= stolen; +- if (unlikely(delta_cpu < 0)) +- stolen += delta_cpu; /* clamp local-time progress */ +- do_div(stolen, NS_PER_TICK); +- per_cpu(processed_stolen_time, cpu) += stolen * NS_PER_TICK; +- per_cpu(processed_system_time, cpu) += stolen * NS_PER_TICK; +- account_steal_ticks(stolen); +- } +- +- /* +- * Account blocked ticks. +- * ensures that the ticks are accounted as idle/wait. +- */ +- blocked = runstate.time[RUNSTATE_blocked] +- - per_cpu(processed_blocked_time, cpu); +- if ((blocked > 0) && (delta_cpu > 0)) { +- delta_cpu -= blocked; +- if (unlikely(delta_cpu < 0)) +- blocked += delta_cpu; /* clamp local-time progress */ +- do_div(blocked, NS_PER_TICK); +- per_cpu(processed_blocked_time, cpu) += blocked * NS_PER_TICK; +- per_cpu(processed_system_time, cpu) += blocked * NS_PER_TICK; +- account_idle_ticks(blocked); +- } +- +- /* Account user/system ticks. */ +- if (delta_cpu > 0) { +- cputime_t ct; +- +- do_div(delta_cpu, NS_PER_TICK); +- per_cpu(processed_system_time, cpu) += delta_cpu * NS_PER_TICK; +- ct = jiffies_to_cputime(delta_cpu); +- if (user_mode_vm(get_irq_regs())) +- account_user_time(current, ct, cputime_to_scaled(ct)); +- else if (current != idle_task(cpu) +- || irq_count() != HARDIRQ_OFFSET) +- account_system_time(current, HARDIRQ_OFFSET, +- ct, cputime_to_scaled(ct)); +- else +- account_idle_ticks(delta_cpu); +- } +- +- /* Offlined for more than a few seconds? Avoid lockup warnings. */ +- if (stolen > 5*HZ) +- touch_softlockup_watchdog(); +- +- /* Local timer processing (see update_process_times()). */ +- run_local_timers(); +- rcu_check_callbacks(cpu, user_mode_vm(get_irq_regs())); +- printk_tick(); +- scheduler_tick(); +- run_posix_cpu_timers(current); +- profile_tick(CPU_PROFILING); +- +- return IRQ_HANDLED; +-} +- + void mark_tsc_unstable(char *reason) + { + #ifndef CONFIG_XEN /* XXX Should tell the hypervisor about this fact. */ +@@ -565,24 +437,13 @@ void mark_tsc_unstable(char *reason) + } + EXPORT_SYMBOL_GPL(mark_tsc_unstable); + +-static void init_missing_ticks_accounting(unsigned int cpu) +-{ +- struct vcpu_runstate_info *runstate = setup_runstate_area(cpu); +- +- per_cpu(processed_blocked_time, cpu) = +- runstate->time[RUNSTATE_blocked]; +- per_cpu(processed_stolen_time, cpu) = +- runstate->time[RUNSTATE_runnable] + +- runstate->time[RUNSTATE_offline]; +-} +- + static cycle_t cs_last; + + static cycle_t xen_clocksource_read(struct clocksource *cs) + { + #ifdef CONFIG_SMP + cycle_t last = get64(&cs_last); +- cycle_t ret = local_clock(); ++ cycle_t ret = xen_local_clock(); + + if (unlikely((s64)(ret - last) < 0)) { + if (last - ret > permitted_clock_jitter +@@ -608,37 +469,28 @@ static cycle_t xen_clocksource_read(stru + last = cur; + } + #else +- return local_clock(); ++ return xen_local_clock(); + #endif + } + + /* No locking required. Interrupts are disabled on all CPUs. */ + static void xen_clocksource_resume(struct clocksource *cs) + { ++ unsigned long seq; + unsigned int cpu; + + init_cpu_khz(); + +- for_each_online_cpu(cpu) { +- switch (HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, cpu, +- &xen_set_periodic_tick)) { +- case 0: +-#if CONFIG_XEN_COMPAT <= 0x030004 +- case -ENOSYS: +-#endif +- break; +- default: +- BUG(); +- } ++ for_each_online_cpu(cpu) + get_time_values_from_xen(cpu); +- per_cpu(processed_system_time, cpu) = +- per_cpu(shadow_time, 0).system_timestamp; +- init_missing_ticks_accounting(cpu); +- } + +- processed_system_time = per_cpu(shadow_time, 0).system_timestamp; ++ do { ++ seq = read_seqbegin(&xtime_lock); ++ jiffies_bias = jiffies_64; ++ } while (read_seqretry(&xtime_lock, seq)); ++ system_time_bias = per_cpu(shadow_time, 0).system_timestamp; + +- cs_last = local_clock(); ++ cs_last = xen_local_clock(); + } + + static struct clocksource clocksource_xen = { +@@ -683,7 +535,7 @@ void xen_read_persistent_clock(struct ti + rmb(); + } while ((s->wc_version & 1) | (version ^ s->wc_version)); + +- delta = local_clock() + (u64)sec * NSEC_PER_SEC + nsec; ++ delta = xen_local_clock() + (u64)sec * NSEC_PER_SEC + nsec; + do_div(delta, NSEC_PER_SEC); + + ts->tv_sec = delta; +@@ -698,42 +550,17 @@ int xen_update_persistent_clock(void) + return 0; + } + +-/* Dynamically-mapped IRQ. */ +-static int __read_mostly timer_irq = -1; +-static struct irqaction timer_action = { +- .handler = timer_interrupt, +- .flags = IRQF_DISABLED|IRQF_TIMER, +- .name = "timer" +-}; +- +-static void __init setup_cpu0_timer_irq(void) +-{ +- timer_irq = bind_virq_to_irqaction(VIRQ_TIMER, 0, &timer_action); +- BUG_ON(timer_irq < 0); +-} +- + void __init time_init(void) + { + init_cpu_khz(); + printk(KERN_INFO "Xen reported: %u.%03u MHz processor.\n", + cpu_khz / 1000, cpu_khz % 1000); + +- switch (HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, 0, +- &xen_set_periodic_tick)) { +- case 0: +-#if CONFIG_XEN_COMPAT <= 0x030004 +- case -ENOSYS: +-#endif +- break; +- default: +- BUG(); +- } +- ++ setup_runstate_area(0); + get_time_values_from_xen(0); + +- processed_system_time = per_cpu(shadow_time, 0).system_timestamp; +- per_cpu(processed_system_time, 0) = processed_system_time; +- init_missing_ticks_accounting(0); ++ jiffies_bias = jiffies_64; ++ system_time_bias = per_cpu(shadow_time, 0).system_timestamp; + + clocksource_register(&clocksource_xen); + +@@ -742,7 +569,7 @@ void __init time_init(void) + use_tsc_delay(); + + /* Cannot request_irq() until kmem is initialised. */ +- late_time_init = setup_cpu0_timer_irq; ++ late_time_init = xen_clockevents_init; + } + + /* Convert jiffies to system time. */ +@@ -758,13 +585,13 @@ u64 jiffies_to_st(unsigned long j) + if (delta < 1) { + /* Triggers in some wrap-around cases, but that's okay: + * we just end up with a shorter timeout. */ +- st = processed_system_time + NS_PER_TICK; ++ st = processed_system_time() + NS_PER_TICK; + } else if (((unsigned long)delta >> (BITS_PER_LONG-3)) != 0) { + /* Very long timeout means there is no pending timer. + * We indicate this to Xen by passing zero timeout. */ + st = 0; + } else { +- st = processed_system_time + delta * (u64)NS_PER_TICK; ++ st = processed_system_time() + delta * (u64)NS_PER_TICK; + } + } while (read_seqretry(&xtime_lock, seq)); + +@@ -772,73 +599,10 @@ u64 jiffies_to_st(unsigned long j) + } + EXPORT_SYMBOL(jiffies_to_st); + +-/* +- * stop_hz_timer / start_hz_timer - enter/exit 'tickless mode' on an idle cpu +- * These functions are based on implementations from arch/s390/kernel/time.c +- */ +-static void stop_hz_timer(void) +-{ +- struct vcpu_set_singleshot_timer singleshot; +- unsigned int cpu = smp_processor_id(); +- unsigned long j; +- int rc; +- +- cpumask_set_cpu(cpu, nohz_cpu_mask); +- +- /* See matching smp_mb in rcu_start_batch in rcupdate.c. These mbs */ +- /* ensure that if __rcu_pending (nested in rcu_needs_cpu) fetches a */ +- /* value of rcp->cur that matches rdp->quiescbatch and allows us to */ +- /* stop the hz timer then the cpumasks created for subsequent values */ +- /* of cur in rcu_start_batch are guaranteed to pick up the updated */ +- /* nohz_cpu_mask and so will not depend on this cpu. */ +- +- smp_mb(); +- +- /* Leave ourselves in tick mode if rcu or softirq or timer pending. */ +- if (rcu_needs_cpu(cpu) || printk_needs_cpu(cpu) || +- local_softirq_pending() || +- (j = get_next_timer_interrupt(jiffies), +- time_before_eq(j, jiffies))) { +- cpumask_clear_cpu(cpu, nohz_cpu_mask); +- j = jiffies + 1; +- } +- +- singleshot.timeout_abs_ns = jiffies_to_st(j); +- if (!singleshot.timeout_abs_ns) +- return; +- singleshot.timeout_abs_ns += NS_PER_TICK / 2; +- singleshot.flags = 0; +- rc = HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, cpu, &singleshot); +-#if CONFIG_XEN_COMPAT <= 0x030004 +- if (rc) { +- BUG_ON(rc != -ENOSYS); +- rc = HYPERVISOR_set_timer_op(singleshot.timeout_abs_ns); +- } +-#endif +- BUG_ON(rc); +-} +- +-static void start_hz_timer(void) +-{ +- unsigned int cpu = smp_processor_id(); +- int rc = HYPERVISOR_vcpu_op(VCPUOP_stop_singleshot_timer, cpu, NULL); +- +-#if CONFIG_XEN_COMPAT <= 0x030004 +- if (rc) { +- BUG_ON(rc != -ENOSYS); +- rc = HYPERVISOR_set_timer_op(0); +- } +-#endif +- BUG_ON(rc); +- cpumask_clear_cpu(cpu, nohz_cpu_mask); +-} +- + void xen_safe_halt(void) + { +- stop_hz_timer(); + /* Blocking includes an implicit local_irq_enable(). */ + HYPERVISOR_block(); +- start_hz_timer(); + } + EXPORT_SYMBOL(xen_safe_halt); + +@@ -849,47 +613,6 @@ void xen_halt(void) + } + EXPORT_SYMBOL(xen_halt); + +-#ifdef CONFIG_SMP +-int __cpuinit local_setup_timer(unsigned int cpu) +-{ +- int seq, irq; +- +- BUG_ON(cpu == 0); +- +- switch (HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, cpu, +- &xen_set_periodic_tick)) { +- case 0: +-#if CONFIG_XEN_COMPAT <= 0x030004 +- case -ENOSYS: +-#endif +- break; +- default: +- BUG(); +- } +- +- do { +- seq = read_seqbegin(&xtime_lock); +- /* Use cpu0 timestamp: cpu's shadow is not initialised yet. */ +- per_cpu(processed_system_time, cpu) = +- per_cpu(shadow_time, 0).system_timestamp; +- init_missing_ticks_accounting(cpu); +- } while (read_seqretry(&xtime_lock, seq)); +- +- irq = bind_virq_to_irqaction(VIRQ_TIMER, cpu, &timer_action); +- if (irq < 0) +- return irq; +- BUG_ON(timer_irq != irq); +- +- return 0; +-} +- +-void __cpuinit local_teardown_timer(unsigned int cpu) +-{ +- BUG_ON(cpu == 0); +- unbind_from_per_cpu_irq(timer_irq, cpu, &timer_action); +-} +-#endif +- + #ifdef CONFIG_CPU_FREQ + static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +--- head-2010-05-12.orig/drivers/xen/Kconfig 2010-03-31 14:10:55.000000000 +0200 ++++ head-2010-05-12/drivers/xen/Kconfig 2010-03-31 14:11:27.000000000 +0200 +@@ -355,9 +355,6 @@ config HAVE_IRQ_IGNORE_UNHANDLED + config IRQ_PER_CPU + bool + +-config NO_IDLE_HZ +- def_bool y +- + config ARCH_HAS_WALK_MEMORY + def_bool y + depends on X86 +--- head-2010-05-12.orig/drivers/xen/core/Makefile 2010-04-19 14:55:02.000000000 +0200 ++++ head-2010-05-12/drivers/xen/core/Makefile 2010-03-25 14:39:15.000000000 +0100 +@@ -12,6 +12,7 @@ obj-$(CONFIG_XEN_SYSFS) += xen_sysfs.o + obj-$(CONFIG_XEN_SMPBOOT) += smpboot.o + obj-$(CONFIG_SMP) += spinlock.o + obj-$(CONFIG_KEXEC) += machine_kexec.o ++obj-$(CONFIG_GENERIC_CLOCKEVENTS) += clockevents.o + obj-$(CONFIG_XEN_DOMCTL) += domctl.o + CFLAGS_domctl.o := -D__XEN_PUBLIC_XEN_H__ -D__XEN_PUBLIC_GRANT_TABLE_H__ + CFLAGS_domctl.o += -D__XEN_TOOLS__ -imacros xen/interface/domctl.h +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/drivers/xen/core/clockevents.c 2010-03-25 14:39:15.000000000 +0100 +@@ -0,0 +1,298 @@ ++/* ++ * Xen clockevent functions ++ * ++ * See arch/x86/xen/time.c for copyright and credits for derived ++ * portions of this file. ++ * ++ * Xen clockevent implementation ++ * ++ * Xen has two clockevent implementations: ++ * ++ * The old timer_op one works with all released versions of Xen prior ++ * to version 3.0.4. This version of the hypervisor provides a ++ * single-shot timer with nanosecond resolution. However, sharing the ++ * same event channel is a 100Hz tick which is delivered while the ++ * vcpu is running. We don't care about or use this tick, but it will ++ * cause the core time code to think the timer fired too soon, and ++ * will end up resetting it each time. It could be filtered, but ++ * doing so has complications when the ktime clocksource is not yet ++ * the xen clocksource (ie, at boot time). ++ * ++ * The new vcpu_op-based timer interface allows the tick timer period ++ * to be changed or turned off. The tick timer is not useful as a ++ * periodic timer because events are only delivered to running vcpus. ++ * The one-shot timer can report when a timeout is in the past, so ++ * set_next_event is capable of returning -ETIME when appropriate. ++ * This interface is used when available. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define XEN_SHIFT 22 ++ ++/* Xen may fire a timer up to this many ns early */ ++#define TIMER_SLOP 100000 ++#define NS_PER_TICK (1000000000LL / HZ) ++ ++/* ++ * Get a hypervisor absolute time. In theory we could maintain an ++ * offset between the kernel's time and the hypervisor's time, and ++ * apply that to a kernel's absolute timeout. Unfortunately the ++ * hypervisor and kernel times can drift even if the kernel is using ++ * the Xen clocksource, because ntp can warp the kernel's clocksource. ++ */ ++static u64 get_abs_timeout(unsigned long delta) ++{ ++ return xen_local_clock() + delta; ++} ++ ++#if CONFIG_XEN_COMPAT <= 0x030004 ++static void timerop_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ WARN_ON(1); /* unsupported */ ++ break; ++ ++ case CLOCK_EVT_MODE_ONESHOT: ++ case CLOCK_EVT_MODE_RESUME: ++ break; ++ ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ if (HYPERVISOR_set_timer_op(0)) /* cancel timeout */ ++ BUG(); ++ break; ++ } ++} ++ ++static int timerop_set_next_event(unsigned long delta, ++ struct clock_event_device *evt) ++{ ++ WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT); ++ ++ if (HYPERVISOR_set_timer_op(get_abs_timeout(delta)) < 0) ++ BUG(); ++ ++ /* ++ * We may have missed the deadline, but there's no real way of ++ * knowing for sure. If the event was in the past, then we'll ++ * get an immediate interrupt. ++ */ ++ ++ return 0; ++} ++#endif ++ ++static void vcpuop_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ WARN_ON(1); /* unsupported */ ++ break; ++ ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ if (HYPERVISOR_vcpu_op(VCPUOP_stop_singleshot_timer, ++ smp_processor_id(), NULL)) ++ BUG(); ++ /* fall through */ ++ case CLOCK_EVT_MODE_ONESHOT: ++ if (HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, ++ smp_processor_id(), NULL)) ++ BUG(); ++ break; ++ ++ case CLOCK_EVT_MODE_RESUME: ++ break; ++ } ++} ++ ++static int vcpuop_set_next_event(unsigned long delta, ++ struct clock_event_device *evt) ++{ ++ struct vcpu_set_singleshot_timer single; ++ int ret; ++ ++ WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT); ++ ++ single.timeout_abs_ns = get_abs_timeout(delta); ++ single.flags = VCPU_SSHOTTMR_future; ++ ++ ret = HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, ++ smp_processor_id(), &single); ++ ++ BUG_ON(ret != 0 && ret != -ETIME); ++ ++ return ret; ++} ++ ++static DEFINE_PER_CPU(struct clock_event_device, xen_clock_event) = { ++ .name = "xen", ++ .features = CLOCK_EVT_FEAT_ONESHOT, ++ ++ .max_delta_ns = 0xffffffff, ++ .min_delta_ns = TIMER_SLOP, ++ ++ .mult = 1, ++ .shift = 0, ++ .rating = 500, ++ ++ .irq = -1, ++}; ++ ++/* snapshots of runstate info */ ++static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate_snapshot); ++ ++/* unused ns of stolen and blocked time */ ++static DEFINE_PER_CPU(unsigned int, xen_residual_stolen); ++static DEFINE_PER_CPU(unsigned int, xen_residual_blocked); ++ ++static void init_missing_ticks_accounting(unsigned int cpu) ++{ ++ per_cpu(xen_runstate_snapshot, cpu) = *setup_runstate_area(cpu); ++ if (cpu == smp_processor_id()) ++ get_runstate_snapshot(&__get_cpu_var(xen_runstate_snapshot)); ++ per_cpu(xen_residual_stolen, cpu) = 0; ++ per_cpu(xen_residual_blocked, cpu) = 0; ++} ++ ++static irqreturn_t timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *evt = &__get_cpu_var(xen_clock_event); ++ struct vcpu_runstate_info state, *snap; ++ s64 blocked, stolen; ++ irqreturn_t ret = IRQ_NONE; ++ ++ if (evt->event_handler) { ++ evt->event_handler(evt); ++ ret = IRQ_HANDLED; ++ } ++ ++ xen_check_wallclock_update(); ++ ++ get_runstate_snapshot(&state); ++ snap = &__get_cpu_var(xen_runstate_snapshot); ++ ++ stolen = state.time[RUNSTATE_runnable] - snap->time[RUNSTATE_runnable] ++ + state.time[RUNSTATE_offline] - snap->time[RUNSTATE_offline] ++ + percpu_read(xen_residual_stolen); ++ ++ if (stolen >= NS_PER_TICK) ++ account_steal_ticks(div_u64_rem(stolen, NS_PER_TICK, ++ &__get_cpu_var(xen_residual_stolen))); ++ else ++ percpu_write(xen_residual_stolen, stolen > 0 ? stolen : 0); ++ ++ blocked = state.time[RUNSTATE_blocked] - snap->time[RUNSTATE_blocked] ++ + percpu_read(xen_residual_blocked); ++ ++ if (blocked >= NS_PER_TICK) ++ account_idle_ticks(div_u64_rem(blocked, NS_PER_TICK, ++ &__get_cpu_var(xen_residual_blocked))); ++ else ++ percpu_write(xen_residual_blocked, blocked > 0 ? blocked : 0); ++ ++ *snap = state; ++ ++ return ret; ++} ++ ++static struct irqaction timer_action = { ++ .handler = timer_interrupt, ++ .flags = IRQF_DISABLED|IRQF_TIMER, ++ .name = "timer" ++}; ++ ++void __cpuinit xen_setup_cpu_clockevents(void) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct clock_event_device *evt = &per_cpu(xen_clock_event, cpu); ++ ++ init_missing_ticks_accounting(cpu); ++ ++ evt->cpumask = cpumask_of(cpu); ++ clockevents_register_device(evt); ++} ++ ++#ifdef CONFIG_SMP ++int __cpuinit local_setup_timer(unsigned int cpu) ++{ ++ struct clock_event_device *evt = &per_cpu(xen_clock_event, cpu); ++ ++ BUG_ON(cpu == smp_processor_id()); ++ ++ evt->irq = bind_virq_to_irqaction(VIRQ_TIMER, cpu, &timer_action); ++ if (evt->irq < 0) ++ return evt->irq; ++ BUG_ON(per_cpu(xen_clock_event.irq, 0) != evt->irq); ++ ++ evt->set_mode = percpu_read(xen_clock_event.set_mode); ++ evt->set_next_event = percpu_read(xen_clock_event.set_next_event); ++ ++ return 0; ++} ++ ++void __cpuinit local_teardown_timer(unsigned int cpu) ++{ ++ struct clock_event_device *evt = &per_cpu(xen_clock_event, cpu); ++ ++ BUG_ON(cpu == 0); ++ unbind_from_per_cpu_irq(evt->irq, cpu, &timer_action); ++} ++#endif ++ ++void xen_clockevents_resume(void) ++{ ++ unsigned int cpu; ++ ++ if (percpu_read(xen_clock_event.set_mode) != vcpuop_set_mode) ++ return; ++ ++ for_each_online_cpu(cpu) { ++ init_missing_ticks_accounting(cpu); ++ if (HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL)) ++ BUG(); ++ } ++} ++ ++void __init xen_clockevents_init(void) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct clock_event_device *evt = &__get_cpu_var(xen_clock_event); ++ ++ switch (HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, ++ cpu, NULL)) { ++ case 0: ++ /* ++ * Successfully turned off 100Hz tick, so we have the ++ * vcpuop-based timer interface ++ */ ++ evt->set_mode = vcpuop_set_mode; ++ evt->set_next_event = vcpuop_set_next_event; ++ break; ++#if CONFIG_XEN_COMPAT <= 0x030004 ++ case -ENOSYS: ++ printk(KERN_DEBUG "Xen: using timerop interface\n"); ++ evt->set_mode = timerop_set_mode; ++ evt->set_next_event = timerop_set_next_event; ++ break; ++#endif ++ default: ++ BUG(); ++ } ++ ++ evt->irq = bind_virq_to_irqaction(VIRQ_TIMER, cpu, &timer_action); ++ BUG_ON(evt->irq < 0); ++ ++ xen_setup_cpu_clockevents(); ++} +--- head-2010-05-12.orig/drivers/xen/core/machine_reboot.c 2009-12-18 14:19:13.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/machine_reboot.c 2010-03-25 14:39:15.000000000 +0100 +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -163,10 +164,12 @@ static int take_machine_down(void *_susp + } else + BUG_ON(suspend_cancelled > 0); + suspend->resume_notifier(suspend_cancelled); +- if (suspend_cancelled >= 0) { ++ if (suspend_cancelled >= 0) + post_suspend(suspend_cancelled); ++ if (!suspend_cancelled) ++ xen_clockevents_resume(); ++ if (suspend_cancelled >= 0) + sysdev_resume(); +- } + if (!suspend_cancelled) { + #ifdef __x86_64__ + /* +--- head-2010-05-12.orig/drivers/xen/core/smpboot.c 2010-03-19 15:20:27.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/smpboot.c 2010-03-25 14:39:15.000000000 +0100 +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -206,6 +207,7 @@ static void __cpuinit cpu_bringup(void) + identify_secondary_cpu(¤t_cpu_data); + touch_softlockup_watchdog(); + preempt_disable(); ++ xen_setup_cpu_clockevents(); + local_irq_enable(); + } + +--- head-2010-05-12.orig/drivers/xen/core/spinlock.c 2010-02-24 12:38:54.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/spinlock.c 2010-03-25 14:39:15.000000000 +0100 +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + + #ifdef TICKET_SHIFT +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/clock.h 2010-03-25 14:39:15.000000000 +0100 +@@ -0,0 +1,19 @@ ++#ifndef __XEN_CPU_CLOCK_H__ ++#define __XEN_CPU_CLOCK_H__ ++ ++struct vcpu_runstate_info *setup_runstate_area(unsigned int cpu); ++void get_runstate_snapshot(struct vcpu_runstate_info *); ++ ++unsigned long long xen_local_clock(void); ++void xen_check_wallclock_update(void); ++ ++#ifdef CONFIG_GENERIC_CLOCKEVENTS ++void xen_clockevents_init(void); ++void xen_setup_cpu_clockevents(void); ++void xen_clockevents_resume(void); ++#else ++static inline void xen_setup_cpu_clockevents(void) {} ++static inline void xen_clockevents_resume(void) {} ++#endif ++ ++#endif /* __XEN_CPU_CLOCK_H__ */ +--- head-2010-05-12.orig/kernel/hrtimer.c 2010-03-24 15:12:46.000000000 +0100 ++++ head-2010-05-12/kernel/hrtimer.c 2010-03-25 14:39:15.000000000 +0100 +@@ -1108,7 +1108,7 @@ ktime_t hrtimer_get_remaining(const stru + } + EXPORT_SYMBOL_GPL(hrtimer_get_remaining); + +-#if defined(CONFIG_NO_HZ) || defined(CONFIG_NO_IDLE_HZ) ++#ifdef CONFIG_NO_HZ + /** + * hrtimer_get_next_event - get the time until next expiry event + * +--- head-2010-05-12.orig/kernel/timer.c 2010-04-15 10:05:03.000000000 +0200 ++++ head-2010-05-12/kernel/timer.c 2010-04-15 11:42:54.000000000 +0200 +@@ -1044,7 +1044,7 @@ static inline void __run_timers(struct t + spin_unlock_irq(&base->lock); + } + +-#if defined(CONFIG_NO_HZ) || defined(CONFIG_NO_IDLE_HZ) ++#ifdef CONFIG_NO_HZ + /* + * Find out when the next timer event is due to happen. This + * is used on S/390 to stop all activity when a CPU is idle. diff --git a/patches.xen/xen-configurable-guest-devices b/patches.xen/xen-configurable-guest-devices new file mode 100644 index 0000000..e4784de --- /dev/null +++ b/patches.xen/xen-configurable-guest-devices @@ -0,0 +1,74 @@ +From: jbeulich@novell.com +Subject: allow number of guest devices to be configurable +Patch-mainline: obsolete + +... and derive NR_DYNIRQS from this (rather than having a hard-coded +value). +Similarly, allow the number of simultaneous transmits in netback to be +configurable. + +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/irq_vectors.h 2010-03-30 17:15:14.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/irq_vectors.h 2010-04-15 11:44:09.000000000 +0200 +@@ -95,7 +95,7 @@ extern int nr_pirqs; + #endif + + #define DYNIRQ_BASE (PIRQ_BASE + nr_pirqs) +-#define NR_DYNIRQS 256 ++#define NR_DYNIRQS (64 + CONFIG_XEN_NR_GUEST_DEVICES) + + #define NR_IRQS (NR_PIRQS + NR_DYNIRQS) + +--- head-2010-04-15.orig/drivers/xen/Kconfig 2010-03-31 14:11:27.000000000 +0200 ++++ head-2010-04-15/drivers/xen/Kconfig 2010-03-31 14:11:36.000000000 +0200 +@@ -98,6 +98,15 @@ config XEN_NETDEV_BACKEND + network devices to other guests via a high-performance shared-memory + interface. + ++config XEN_NETDEV_TX_SHIFT ++ int "Maximum simultaneous transmit requests (as a power of 2)" ++ depends on XEN_NETDEV_BACKEND ++ range 5 16 ++ default 8 ++ help ++ The maximum number transmits the driver can hold pending, expressed ++ as the exponent of a power of 2. ++ + config XEN_NETDEV_PIPELINED_TRANSMITTER + bool "Pipelined transmitter (DANGEROUS)" + depends on XEN_NETDEV_BACKEND +@@ -309,6 +318,16 @@ config XEN_SYSFS + help + Xen hypervisor attributes will show up under /sys/hypervisor/. + ++config XEN_NR_GUEST_DEVICES ++ int "Number of guest devices" ++ range 0 4032 if 64BIT ++ range 0 960 ++ default 256 if XEN_BACKEND ++ default 16 ++ help ++ Specify the total number of virtual devices (i.e. both frontend ++ and backend) that you want the kernel to be able to service. ++ + choice + prompt "Xen version compatibility" + default XEN_COMPAT_030002_AND_LATER +--- head-2010-04-15.orig/drivers/xen/netback/netback.c 2010-01-04 13:31:26.000000000 +0100 ++++ head-2010-04-15/drivers/xen/netback/netback.c 2010-03-25 14:39:26.000000000 +0100 +@@ -71,7 +71,7 @@ static DECLARE_TASKLET(net_rx_tasklet, n + static struct timer_list net_timer; + static struct timer_list netbk_tx_pending_timer; + +-#define MAX_PENDING_REQS 256 ++#define MAX_PENDING_REQS (1U << CONFIG_XEN_NETDEV_TX_SHIFT) + + static struct sk_buff_head rx_queue; + +@@ -1265,6 +1265,7 @@ static void net_tx_action(unsigned long + net_tx_action_dealloc(); + + mop = tx_map_ops; ++ BUILD_BUG_ON(MAX_SKB_FRAGS >= MAX_PENDING_REQS); + while (((NR_PENDING_REQS + MAX_SKB_FRAGS) < MAX_PENDING_REQS) && + !list_empty(&net_schedule_list)) { + /* Get a netif from the list with work to do. */ diff --git a/patches.xen/xen-cpufreq-report b/patches.xen/xen-cpufreq-report new file mode 100644 index 0000000..daaf962 --- /dev/null +++ b/patches.xen/xen-cpufreq-report @@ -0,0 +1,57 @@ +From: jbeulich@novell.com +Subject: make /proc/cpuinfo track CPU speed +Patch-mainline: obsolete + +--- head-2010-03-15.orig/arch/x86/kernel/acpi/processor_extcntl_xen.c 2010-03-16 10:37:21.000000000 +0100 ++++ head-2010-03-15/arch/x86/kernel/acpi/processor_extcntl_xen.c 2010-03-16 10:42:20.000000000 +0100 +@@ -206,3 +206,14 @@ void arch_acpi_processor_init_extcntl(co + *ops = &xen_extcntl_ops; + } + EXPORT_SYMBOL(arch_acpi_processor_init_extcntl); ++ ++unsigned int cpufreq_quick_get(unsigned int cpu) ++{ ++ xen_platform_op_t op = { ++ .cmd = XENPF_get_cpu_freq, ++ .interface_version = XENPF_INTERFACE_VERSION, ++ .u.get_cpu_freq.vcpu = cpu ++ }; ++ ++ return HYPERVISOR_platform_op(&op) == 0 ? op.u.get_cpu_freq.freq : 0; ++} +--- head-2010-03-15.orig/include/linux/cpufreq.h 2010-03-16 10:34:57.000000000 +0100 ++++ head-2010-03-15/include/linux/cpufreq.h 2010-01-25 13:46:23.000000000 +0100 +@@ -303,7 +303,7 @@ static inline unsigned int cpufreq_get(u + #endif + + /* query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it */ +-#ifdef CONFIG_CPU_FREQ ++#if defined(CONFIG_CPU_FREQ) || defined(CONFIG_PROCESSOR_EXTERNAL_CONTROL) + unsigned int cpufreq_quick_get(unsigned int cpu); + #else + static inline unsigned int cpufreq_quick_get(unsigned int cpu) +--- head-2010-03-15.orig/include/xen/interface/platform.h 2010-01-04 11:56:34.000000000 +0100 ++++ head-2010-03-15/include/xen/interface/platform.h 2010-01-25 13:46:23.000000000 +0100 +@@ -355,6 +355,14 @@ struct xenpf_mem_hotadd + uint32_t flags; + }; + ++#define XENPF_get_cpu_freq ('N' << 24) ++struct xenpf_get_cpu_freq { ++ /* IN variables */ ++ uint32_t vcpu; ++ /* OUT variables */ ++ uint32_t freq; /* in kHz */ ++}; ++ + struct xen_platform_op { + uint32_t cmd; + uint32_t interface_version; /* XENPF_INTERFACE_VERSION */ +@@ -374,6 +382,7 @@ struct xen_platform_op { + struct xenpf_cpu_ol cpu_ol; + struct xenpf_cpu_hotadd cpu_add; + struct xenpf_mem_hotadd mem_add; ++ struct xenpf_get_cpu_freq get_cpu_freq; + uint8_t pad[128]; + } u; + }; diff --git a/patches.xen/xen-dcdbas b/patches.xen/xen-dcdbas new file mode 100644 index 0000000..9e9d059 --- /dev/null +++ b/patches.xen/xen-dcdbas @@ -0,0 +1,288 @@ +From: jbeulich@novell.com +Subject: force proper address translation in DCDBAS +Patch-mainline: n/a + +The only caveat is that this doesn't work when Dom0 has its vCPU-s pinned. + +--- head-2010-04-15.orig/drivers/firmware/Kconfig 2010-03-25 14:39:33.000000000 +0100 ++++ head-2010-04-15/drivers/firmware/Kconfig 2009-10-21 12:05:13.000000000 +0200 +@@ -90,6 +90,7 @@ config DELL_RBU + config DCDBAS + tristate "Dell Systems Management Base Driver" + depends on X86 ++ select XEN_DOMCTL if XEN + help + The Dell Systems Management Base Driver provides a sysfs interface + for systems management software to perform System Management +--- head-2010-04-15.orig/drivers/firmware/dcdbas.c 2010-04-15 09:37:45.000000000 +0200 ++++ head-2010-04-15/drivers/firmware/dcdbas.c 2010-04-15 11:45:50.000000000 +0200 +@@ -37,6 +37,10 @@ + #include + #include + ++#ifdef CONFIG_XEN ++#include "../xen/core/domctl.h" ++#endif ++ + #include "dcdbas.h" + + #define DRIVER_NAME "dcdbas" +@@ -107,7 +111,7 @@ static int smi_data_buf_realloc(unsigned + /* set up new buffer for use */ + smi_data_buf = buf; + smi_data_buf_handle = handle; +- smi_data_buf_phys_addr = (u32) virt_to_phys(buf); ++ smi_data_buf_phys_addr = (u32) handle; + smi_data_buf_size = size; + + dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", +@@ -245,7 +249,9 @@ static ssize_t host_control_on_shutdown_ + */ + int dcdbas_smi_request(struct smi_cmd *smi_cmd) + { ++#ifndef CONFIG_XEN + cpumask_var_t old_mask; ++#endif + int ret = 0; + + if (smi_cmd->magic != SMI_CMD_MAGIC) { +@@ -255,6 +261,7 @@ int dcdbas_smi_request(struct smi_cmd *s + } + + /* SMI requires CPU 0 */ ++#ifndef CONFIG_XEN + if (!alloc_cpumask_var(&old_mask, GFP_KERNEL)) + return -ENOMEM; + +@@ -266,6 +273,14 @@ int dcdbas_smi_request(struct smi_cmd *s + ret = -EBUSY; + goto out; + } ++#else ++ ret = xen_set_physical_cpu_affinity(0); ++ if (ret) { ++ dev_dbg(&dcdbas_pdev->dev, "%s: failed (%d) to get CPU 0\n", ++ __func__, ret); ++ return ret; ++ } ++#endif + + /* generate SMI */ + asm volatile ( +@@ -278,9 +293,13 @@ int dcdbas_smi_request(struct smi_cmd *s + : "memory" + ); + ++#ifndef CONFIG_XEN + out: + set_cpus_allowed_ptr(current, old_mask); + free_cpumask_var(old_mask); ++#else ++ xen_set_physical_cpu_affinity(-1); ++#endif + return ret; + } + +@@ -320,7 +339,7 @@ static ssize_t smi_request_store(struct + break; + case 1: + /* Calling Interface SMI */ +- smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer); ++ smi_cmd->ebx = (u32) virt_to_bus(smi_cmd->command_buffer); + ret = dcdbas_smi_request(smi_cmd); + if (!ret) + ret = count; +@@ -601,6 +620,11 @@ static int __init dcdbas_init(void) + { + int error; + ++#ifdef CONFIG_XEN ++ if (!is_initial_xendomain()) ++ return -ENODEV; ++#endif ++ + error = platform_driver_register(&dcdbas_driver); + if (error) + return error; +--- head-2010-04-15.orig/drivers/xen/core/domctl.c 2010-03-25 14:37:59.000000000 +0100 ++++ head-2010-04-15/drivers/xen/core/domctl.c 2010-05-07 12:14:29.000000000 +0200 +@@ -20,6 +20,8 @@ + #undef __XEN_TOOLS__ + #include + #include ++#include ++#include + #include + #include + +@@ -34,6 +36,29 @@ typedef struct xen_domctl_address_size { + + typedef __attribute__((aligned(8))) uint64_t uint64_aligned_t; + ++struct xenctl_cpumap_v4 { ++ XEN_GUEST_HANDLE(uint8) bitmap; ++ uint32_t nr_cpus; ++}; ++ ++struct xenctl_cpumap_v5 { ++ union { ++ XEN_GUEST_HANDLE(uint8) bitmap; ++ uint64_aligned_t _align; ++ }; ++ uint32_t nr_cpus; ++}; ++ ++struct xen_domctl_vcpuaffinity_v4 { ++ uint32_t vcpu; ++ struct xenctl_cpumap_v4 cpumap; ++}; ++ ++struct xen_domctl_vcpuaffinity_v5 { ++ uint32_t vcpu; ++ struct xenctl_cpumap_v5 cpumap; ++}; ++ + union xen_domctl { + /* v4: sle10 sp1: xen 3.0.4 + 32-on-64 patches */ + struct { +@@ -43,6 +68,7 @@ union xen_domctl { + union { + /* left out lots of other struct xen_domctl_foobar */ + struct xen_domctl_address_size address_size; ++ struct xen_domctl_vcpuaffinity_v4 vcpu_affinity; + uint64_t dummy_align; + uint8_t dummy_pad[128]; + }; +@@ -59,6 +85,7 @@ union xen_domctl { + domid_t domain; + union { + struct xen_domctl_address_size address_size; ++ struct xen_domctl_vcpuaffinity_v5 vcpu_affinity; + uint64_aligned_t dummy_align; + uint8_t dummy_pad[128]; + }; +@@ -124,4 +151,118 @@ int xen_guest_blkif_protocol(int domid) + } + EXPORT_SYMBOL_GPL(xen_guest_blkif_protocol); + ++#ifdef CONFIG_X86 ++ ++#define vcpuaffinity(what, ver) ({ \ ++ memset(&domctl, 0, sizeof(domctl)); \ ++ domctl.v##ver.cmd = XEN_DOMCTL_##what##vcpuaffinity; \ ++ domctl.v##ver.interface_version = ver; \ ++ /* domctl.v##ver.domain = 0; */ \ ++ domctl.v##ver.vcpu_affinity.vcpu = smp_processor_id(); \ ++ domctl.v##ver.vcpu_affinity.cpumap.nr_cpus = nr; \ ++ set_xen_guest_handle(domctl.v##ver.vcpu_affinity.cpumap.bitmap, \ ++ mask); \ ++ hypervisor_domctl(&domctl); \ ++}) ++ ++static inline int get_vcpuaffinity(unsigned int nr, void *mask) ++{ ++ union xen_domctl domctl; ++ int rc; ++ ++ BUILD_BUG_ON(XEN_DOMCTL_INTERFACE_VERSION > 7); ++ rc = vcpuaffinity(get, 7); ++#if CONFIG_XEN_COMPAT < 0x040100 ++ if (rc) ++ rc = vcpuaffinity(get, 6); ++#endif ++#if CONFIG_XEN_COMPAT < 0x040000 ++ if (rc) ++ rc = vcpuaffinity(get, 5); ++#endif ++#if CONFIG_XEN_COMPAT < 0x030100 ++ if (rc) ++ rc = vcpuaffinity(get, 4); ++#endif ++ return rc; ++} ++ ++static inline int set_vcpuaffinity(unsigned int nr, void *mask) ++{ ++ union xen_domctl domctl; ++ int rc; ++ ++ BUILD_BUG_ON(XEN_DOMCTL_INTERFACE_VERSION > 7); ++ rc = vcpuaffinity(set, 7); ++#if CONFIG_XEN_COMPAT < 0x040100 ++ if (rc) ++ rc = vcpuaffinity(set, 6); ++#endif ++#if CONFIG_XEN_COMPAT < 0x040000 ++ if (rc) ++ rc = vcpuaffinity(set, 5); ++#endif ++#if CONFIG_XEN_COMPAT < 0x030100 ++ if (rc) ++ rc = vcpuaffinity(set, 4); ++#endif ++ return rc; ++} ++ ++static DEFINE_PER_CPU(void *, saved_pcpu_affinity); ++ ++#define BITS_PER_PAGE (PAGE_SIZE * BITS_PER_LONG / sizeof(long)) ++ ++int xen_set_physical_cpu_affinity(int pcpu) ++{ ++ int rc; ++ ++ if (!is_initial_xendomain()) ++ return -EPERM; ++ ++ if (pcpu >= 0) { ++ void *oldmap; ++ ++ if (pcpu > BITS_PER_PAGE) ++ return -ERANGE; ++ ++ if (percpu_read(saved_pcpu_affinity)) ++ return -EBUSY; ++ ++ oldmap = (void *)get_zeroed_page(GFP_KERNEL); ++ if (!oldmap) ++ return -ENOMEM; ++ ++ rc = get_vcpuaffinity(BITS_PER_PAGE, oldmap); ++ if (!rc) { ++ void *newmap = kzalloc(BITS_TO_LONGS(pcpu + 1) ++ * sizeof(long), GFP_KERNEL); ++ ++ if (newmap) { ++ __set_bit(pcpu, newmap); ++ rc = set_vcpuaffinity(pcpu + 1, newmap); ++ kfree(newmap); ++ } else ++ rc = -ENOMEM; ++ } ++ ++ if (!rc) ++ percpu_write(saved_pcpu_affinity, oldmap); ++ else ++ free_page((unsigned long)oldmap); ++ } else { ++ if (!percpu_read(saved_pcpu_affinity)) ++ return 0; ++ rc = set_vcpuaffinity(BITS_PER_PAGE, ++ percpu_read(saved_pcpu_affinity)); ++ free_page((unsigned long)percpu_read(saved_pcpu_affinity)); ++ percpu_write(saved_pcpu_affinity, NULL); ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(xen_set_physical_cpu_affinity); ++ ++#endif /* CONFIG_X86 */ ++ + MODULE_LICENSE("GPL"); +--- head-2010-04-15.orig/drivers/xen/core/domctl.h 2010-03-25 14:37:59.000000000 +0100 ++++ head-2010-04-15/drivers/xen/core/domctl.h 2009-10-21 13:24:42.000000000 +0200 +@@ -1,2 +1,3 @@ + int xen_guest_address_size(int domid); + int xen_guest_blkif_protocol(int domid); ++int xen_set_physical_cpu_affinity(int pcpu); diff --git a/patches.xen/xen-floppy b/patches.xen/xen-floppy new file mode 100644 index 0000000..b840f16 --- /dev/null +++ b/patches.xen/xen-floppy @@ -0,0 +1,28 @@ +From: jbeulich@novell.com +Subject: Xen: improve floppy behavior +Patch-mainline: n/a +References: bnc#584216 + +Timing is significantly different from native both because Xen traps +I/O port accesses and using DMA not being possible (without intrusive +changes). Due to the overhead of trapped port accesses, I/O is already +slow enough (and Xen doesn't run on very old hardware anyway), so the +situation can easily be improved by not enforcing REALLY_SLOW_IO. + +This doesn't completely address the issue - Xen just cannot guarantee +scheduling of a particular vCPU with a maximum latency of about 80us +(needed for the default FIFO threshold value of 10). The only complete +solution would require making ISA DMA usable on Xen. + +--- head-2010-03-24.orig/drivers/block/floppy.c 2010-03-24 13:43:18.000000000 +0100 ++++ head-2010-03-24/drivers/block/floppy.c 2010-03-25 14:39:44.000000000 +0100 +@@ -146,7 +146,9 @@ + + #undef FLOPPY_SILENT_DCL_CLEAR + ++#ifndef CONFIG_XEN + #define REALLY_SLOW_IO ++#endif + + #define DEBUGT 2 + diff --git a/patches.xen/xen-ipi-per-cpu-irq b/patches.xen/xen-ipi-per-cpu-irq new file mode 100644 index 0000000..2caef38 --- /dev/null +++ b/patches.xen/xen-ipi-per-cpu-irq @@ -0,0 +1,787 @@ +From: jbeulich@novell.com +Subject: fold IPIs onto a single IRQ each +Patch-mainline: n/a + +--- head-2010-04-15.orig/arch/x86/kernel/apic/ipi-xen.c 2010-03-24 15:25:06.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/apic/ipi-xen.c 2010-01-25 13:46:29.000000000 +0100 +@@ -21,31 +21,22 @@ + + #include + +-DECLARE_PER_CPU(int, ipi_to_irq[NR_IPIS]); +- +-static inline void __send_IPI_one(unsigned int cpu, int vector) +-{ +- int irq = per_cpu(ipi_to_irq, cpu)[vector]; +- BUG_ON(irq < 0); +- notify_remote_via_irq(irq); +-} +- + static void __send_IPI_shortcut(unsigned int shortcut, int vector) + { + unsigned int cpu; + + switch (shortcut) { + case APIC_DEST_SELF: +- __send_IPI_one(smp_processor_id(), vector); ++ notify_remote_via_ipi(vector, smp_processor_id()); + break; + case APIC_DEST_ALLBUT: + for_each_online_cpu(cpu) + if (cpu != smp_processor_id()) +- __send_IPI_one(cpu, vector); ++ notify_remote_via_ipi(vector, cpu); + break; + case APIC_DEST_ALLINC: + for_each_online_cpu(cpu) +- __send_IPI_one(cpu, vector); ++ notify_remote_via_ipi(vector, cpu); + break; + default: + printk("XXXXXX __send_IPI_shortcut %08x vector %d\n", shortcut, +@@ -63,7 +54,7 @@ void xen_send_IPI_mask_allbutself(const + WARN_ON(!cpumask_subset(cpumask, cpu_online_mask)); + for_each_cpu_and(cpu, cpumask, cpu_online_mask) + if (cpu != smp_processor_id()) +- __send_IPI_one(cpu, vector); ++ notify_remote_via_ipi(vector, cpu); + local_irq_restore(flags); + } + +@@ -75,7 +66,7 @@ void xen_send_IPI_mask(const struct cpum + local_irq_save(flags); + WARN_ON(!cpumask_subset(cpumask, cpu_online_mask)); + for_each_cpu_and(cpu, cpumask, cpu_online_mask) +- __send_IPI_one(cpu, vector); ++ notify_remote_via_ipi(vector, cpu); + local_irq_restore(flags); + } + +--- head-2010-04-15.orig/arch/x86/kernel/irq-xen.c 2010-03-24 16:00:05.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/irq-xen.c 2010-01-25 13:46:29.000000000 +0100 +@@ -312,6 +312,7 @@ void fixup_irqs(void) + + affinity = desc->affinity; + if (!irq_has_action(irq) || ++ (desc->status & IRQ_PER_CPU) || + cpumask_equal(affinity, cpu_online_mask)) { + raw_spin_unlock(&desc->lock); + continue; +--- head-2010-04-15.orig/drivers/xen/Kconfig 2010-03-31 14:09:58.000000000 +0200 ++++ head-2010-04-15/drivers/xen/Kconfig 2010-03-31 14:10:55.000000000 +0200 +@@ -4,6 +4,7 @@ + + config XEN + bool ++ select IRQ_PER_CPU if SMP + + if XEN + config XEN_INTERFACE_VERSION +@@ -351,6 +352,9 @@ endmenu + config HAVE_IRQ_IGNORE_UNHANDLED + def_bool y + ++config IRQ_PER_CPU ++ bool ++ + config NO_IDLE_HZ + def_bool y + +--- head-2010-04-15.orig/drivers/xen/core/evtchn.c 2010-04-23 15:20:28.000000000 +0200 ++++ head-2010-04-15/drivers/xen/core/evtchn.c 2010-04-23 15:20:31.000000000 +0200 +@@ -59,6 +59,22 @@ static DEFINE_SPINLOCK(irq_mapping_updat + static int evtchn_to_irq[NR_EVENT_CHANNELS] = { + [0 ... NR_EVENT_CHANNELS-1] = -1 }; + ++/* IRQ <-> IPI mapping. */ ++#ifndef NR_IPIS ++#define NR_IPIS 1 ++#endif ++#if defined(CONFIG_SMP) && defined(CONFIG_X86) ++static int ipi_to_irq[NR_IPIS] __read_mostly = {[0 ... NR_IPIS-1] = -1}; ++static DEFINE_PER_CPU(int[NR_IPIS], ipi_to_evtchn); ++#else ++#define PER_CPU_IPI_IRQ ++#endif ++#if !defined(CONFIG_SMP) || !defined(PER_CPU_IPI_IRQ) ++#define BUG_IF_IPI(irq) BUG_ON(type_from_irq(irq) == IRQT_IPI) ++#else ++#define BUG_IF_IPI(irq) ((void)(irq)) ++#endif ++ + /* Binding types. */ + enum { + IRQT_UNBOUND, +@@ -117,12 +133,14 @@ static inline u32 mk_irq_info(u32 type, + * Accessors for packed IRQ information. + */ + ++#ifdef PER_CPU_IPI_IRQ + static inline unsigned int evtchn_from_irq(int irq) + { + const struct irq_cfg *cfg = irq_cfg(irq); + + return cfg ? cfg->info & ((1U << _EVTCHN_BITS) - 1) : 0; + } ++#endif + + static inline unsigned int index_from_irq(int irq) + { +@@ -139,6 +157,25 @@ static inline unsigned int type_from_irq + return cfg ? cfg->info >> (32 - _IRQT_BITS) : IRQT_UNBOUND; + } + ++#ifndef PER_CPU_IPI_IRQ ++static inline unsigned int evtchn_from_per_cpu_irq(unsigned int irq, ++ unsigned int cpu) ++{ ++ BUG_ON(type_from_irq(irq) != IRQT_IPI); ++ return per_cpu(ipi_to_evtchn, cpu)[index_from_irq(irq)]; ++} ++ ++static inline unsigned int evtchn_from_irq(unsigned int irq) ++{ ++ if (type_from_irq(irq) != IRQT_IPI) { ++ const struct irq_cfg *cfg = irq_cfg(irq); ++ ++ return cfg ? cfg->info & ((1U << _EVTCHN_BITS) - 1) : 0; ++ } ++ return evtchn_from_per_cpu_irq(irq, smp_processor_id()); ++} ++#endif ++ + unsigned int irq_from_evtchn(unsigned int port) + { + return evtchn_to_irq[port]; +@@ -148,11 +185,10 @@ EXPORT_SYMBOL_GPL(irq_from_evtchn); + /* IRQ <-> VIRQ mapping. */ + DEFINE_PER_CPU(int[NR_VIRQS], virq_to_irq) = {[0 ... NR_VIRQS-1] = -1}; + ++#if defined(CONFIG_SMP) && defined(PER_CPU_IPI_IRQ) + /* IRQ <-> IPI mapping. */ +-#ifndef NR_IPIS +-#define NR_IPIS 1 +-#endif + DEFINE_PER_CPU(int[NR_IPIS], ipi_to_irq) = {[0 ... NR_IPIS-1] = -1}; ++#endif + + #ifdef CONFIG_SMP + +@@ -176,8 +212,14 @@ static void bind_evtchn_to_cpu(unsigned + + BUG_ON(!test_bit(chn, s->evtchn_mask)); + +- if (irq != -1) +- cpumask_copy(irq_to_desc(irq)->affinity, cpumask_of(cpu)); ++ if (irq != -1) { ++ struct irq_desc *desc = irq_to_desc(irq); ++ ++ if (!(desc->status & IRQ_PER_CPU)) ++ cpumask_copy(desc->affinity, cpumask_of(cpu)); ++ else ++ cpumask_set_cpu(cpu, desc->affinity); ++ } + + clear_bit(chn, per_cpu(cpu_evtchn_mask, cpu_evtchn[chn])); + set_bit(chn, per_cpu(cpu_evtchn_mask, cpu)); +@@ -350,7 +392,7 @@ asmlinkage void __irq_entry evtchn_do_up + + static struct irq_chip dynirq_chip; + +-static int find_unbound_irq(unsigned int cpu) ++static int find_unbound_irq(unsigned int cpu, bool percpu) + { + static int warned; + int irq; +@@ -360,10 +402,19 @@ static int find_unbound_irq(unsigned int + struct irq_cfg *cfg = desc->chip_data; + + if (!cfg->bindcount) { ++ irq_flow_handler_t handle; ++ const char *name; ++ + desc->status |= IRQ_NOPROBE; ++ if (!percpu) { ++ handle = handle_level_irq; ++ name = "level"; ++ } else { ++ handle = handle_percpu_irq; ++ name = "percpu"; ++ } + set_irq_chip_and_handler_name(irq, &dynirq_chip, +- handle_level_irq, +- "level"); ++ handle, name); + return irq; + } + } +@@ -384,7 +435,7 @@ static int bind_caller_port_to_irq(unsig + spin_lock(&irq_mapping_update_lock); + + if ((irq = evtchn_to_irq[caller_port]) == -1) { +- if ((irq = find_unbound_irq(smp_processor_id())) < 0) ++ if ((irq = find_unbound_irq(smp_processor_id(), false)) < 0) + goto out; + + evtchn_to_irq[caller_port] = irq; +@@ -407,7 +458,7 @@ static int bind_local_port_to_irq(unsign + + BUG_ON(evtchn_to_irq[local_port] != -1); + +- if ((irq = find_unbound_irq(smp_processor_id())) < 0) { ++ if ((irq = find_unbound_irq(smp_processor_id(), false)) < 0) { + struct evtchn_close close = { .port = local_port }; + if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) + BUG(); +@@ -460,7 +511,7 @@ static int bind_virq_to_irq(unsigned int + spin_lock(&irq_mapping_update_lock); + + if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) { +- if ((irq = find_unbound_irq(cpu)) < 0) ++ if ((irq = find_unbound_irq(cpu, false)) < 0) + goto out; + + bind_virq.virq = virq; +@@ -485,6 +536,7 @@ static int bind_virq_to_irq(unsigned int + return irq; + } + ++#if defined(CONFIG_SMP) && defined(PER_CPU_IPI_IRQ) + static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) + { + struct evtchn_bind_ipi bind_ipi; +@@ -493,7 +545,7 @@ static int bind_ipi_to_irq(unsigned int + spin_lock(&irq_mapping_update_lock); + + if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) { +- if ((irq = find_unbound_irq(cpu)) < 0) ++ if ((irq = find_unbound_irq(cpu, false)) < 0) + goto out; + + bind_ipi.vcpu = cpu; +@@ -516,6 +568,7 @@ static int bind_ipi_to_irq(unsigned int + spin_unlock(&irq_mapping_update_lock); + return irq; + } ++#endif + + static void unbind_from_irq(unsigned int irq) + { +@@ -523,6 +576,7 @@ static void unbind_from_irq(unsigned int + unsigned int cpu; + int evtchn = evtchn_from_irq(irq); + ++ BUG_IF_IPI(irq); + spin_lock(&irq_mapping_update_lock); + + if (!--irq_cfg(irq)->bindcount && VALID_EVTCHN(evtchn)) { +@@ -536,10 +590,12 @@ static void unbind_from_irq(unsigned int + per_cpu(virq_to_irq, cpu_from_evtchn(evtchn)) + [index_from_irq(irq)] = -1; + break; ++#if defined(CONFIG_SMP) && defined(PER_CPU_IPI_IRQ) + case IRQT_IPI: + per_cpu(ipi_to_irq, cpu_from_evtchn(evtchn)) + [index_from_irq(irq)] = -1; + break; ++#endif + default: + break; + } +@@ -562,6 +618,46 @@ static void unbind_from_irq(unsigned int + spin_unlock(&irq_mapping_update_lock); + } + ++#if defined(CONFIG_SMP) && !defined(PER_CPU_IPI_IRQ) ++void unbind_from_per_cpu_irq(unsigned int irq, unsigned int cpu) ++{ ++ struct evtchn_close close; ++ int evtchn = evtchn_from_per_cpu_irq(irq, cpu); ++ ++ spin_lock(&irq_mapping_update_lock); ++ ++ if (VALID_EVTCHN(evtchn)) { ++ struct irq_desc *desc = irq_to_desc(irq); ++ ++ mask_evtchn(evtchn); ++ ++ BUG_ON(irq_cfg(irq)->bindcount <= 1); ++ irq_cfg(irq)->bindcount--; ++ cpumask_clear_cpu(cpu, desc->affinity); ++ ++ close.port = evtchn; ++ if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) ++ BUG(); ++ ++ switch (type_from_irq(irq)) { ++ case IRQT_IPI: ++ per_cpu(ipi_to_evtchn, cpu)[index_from_irq(irq)] = 0; ++ break; ++ default: ++ BUG(); ++ break; ++ } ++ ++ /* Closed ports are implicitly re-bound to VCPU0. */ ++ bind_evtchn_to_cpu(evtchn, 0); ++ ++ evtchn_to_irq[evtchn] = -1; ++ } ++ ++ spin_unlock(&irq_mapping_update_lock); ++} ++#endif /* CONFIG_SMP && !PER_CPU_IPI_IRQ */ ++ + int bind_caller_port_to_irqhandler( + unsigned int caller_port, + irq_handler_t handler, +@@ -656,6 +752,8 @@ int bind_virq_to_irqhandler( + } + EXPORT_SYMBOL_GPL(bind_virq_to_irqhandler); + ++#ifdef CONFIG_SMP ++#ifdef PER_CPU_IPI_IRQ + int bind_ipi_to_irqhandler( + unsigned int ipi, + unsigned int cpu, +@@ -679,7 +777,71 @@ int bind_ipi_to_irqhandler( + + return irq; + } +-EXPORT_SYMBOL_GPL(bind_ipi_to_irqhandler); ++#else ++int __cpuinit bind_ipi_to_irqaction( ++ unsigned int ipi, ++ unsigned int cpu, ++ struct irqaction *action) ++{ ++ struct evtchn_bind_ipi bind_ipi; ++ int evtchn, irq, retval = 0; ++ ++ spin_lock(&irq_mapping_update_lock); ++ ++ if (VALID_EVTCHN(per_cpu(ipi_to_evtchn, cpu)[ipi])) { ++ spin_unlock(&irq_mapping_update_lock); ++ return -EBUSY; ++ } ++ ++ if ((irq = ipi_to_irq[ipi]) == -1) { ++ if ((irq = find_unbound_irq(cpu, true)) < 0) { ++ spin_unlock(&irq_mapping_update_lock); ++ return irq; ++ } ++ ++ /* Extra reference so count will never drop to zero. */ ++ irq_cfg(irq)->bindcount++; ++ ++ ipi_to_irq[ipi] = irq; ++ irq_cfg(irq)->info = mk_irq_info(IRQT_IPI, ipi, 0); ++ retval = 1; ++ } ++ ++ bind_ipi.vcpu = cpu; ++ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, ++ &bind_ipi) != 0) ++ BUG(); ++ ++ evtchn = bind_ipi.port; ++ evtchn_to_irq[evtchn] = irq; ++ per_cpu(ipi_to_evtchn, cpu)[ipi] = evtchn; ++ ++ bind_evtchn_to_cpu(evtchn, cpu); ++ ++ irq_cfg(irq)->bindcount++; ++ ++ spin_unlock(&irq_mapping_update_lock); ++ ++ if (retval == 0) { ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ unmask_evtchn(evtchn); ++ local_irq_restore(flags); ++ } else { ++ action->flags |= IRQF_PERCPU | IRQF_NO_SUSPEND; ++ retval = setup_irq(irq, action); ++ if (retval) { ++ unbind_from_per_cpu_irq(irq, cpu); ++ BUG_ON(retval > 0); ++ irq = retval; ++ } ++ } ++ ++ return irq; ++} ++#endif /* PER_CPU_IPI_IRQ */ ++#endif /* CONFIG_SMP */ + + void unbind_from_irqhandler(unsigned int irq, void *dev_id) + { +@@ -705,6 +867,7 @@ static void rebind_irq_to_cpu(unsigned i + { + int evtchn = evtchn_from_irq(irq); + ++ BUG_IF_IPI(irq); + if (VALID_EVTCHN(evtchn)) + rebind_evtchn_to_cpu(evtchn, tcpu); + } +@@ -965,10 +1128,21 @@ int irq_ignore_unhandled(unsigned int ir + return !!(irq_status.flags & XENIRQSTAT_shared); + } + ++#if defined(CONFIG_SMP) && !defined(PER_CPU_IPI_IRQ) ++void notify_remote_via_ipi(unsigned int ipi, unsigned int cpu) ++{ ++ int evtchn = evtchn_from_per_cpu_irq(ipi_to_irq[ipi], cpu); ++ ++ if (VALID_EVTCHN(evtchn)) ++ notify_remote_via_evtchn(evtchn); ++} ++#endif ++ + void notify_remote_via_irq(int irq) + { + int evtchn = evtchn_from_irq(irq); + ++ BUG_IF_IPI(irq); + if (VALID_EVTCHN(evtchn)) + notify_remote_via_evtchn(evtchn); + } +@@ -976,6 +1150,7 @@ EXPORT_SYMBOL_GPL(notify_remote_via_irq) + + int irq_to_evtchn_port(int irq) + { ++ BUG_IF_IPI(irq); + return evtchn_from_irq(irq); + } + EXPORT_SYMBOL_GPL(irq_to_evtchn_port); +@@ -1091,11 +1266,17 @@ static void restore_cpu_virqs(unsigned i + + static void restore_cpu_ipis(unsigned int cpu) + { ++#ifdef CONFIG_SMP + struct evtchn_bind_ipi bind_ipi; + int ipi, irq, evtchn; + + for (ipi = 0; ipi < NR_IPIS; ipi++) { ++#ifdef PER_CPU_IPI_IRQ + if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) ++#else ++ if ((irq = ipi_to_irq[ipi]) == -1 ++ || !VALID_EVTCHN(per_cpu(ipi_to_evtchn, cpu)[ipi])) ++#endif + continue; + + BUG_ON(irq_cfg(irq)->info != mk_irq_info(IRQT_IPI, ipi, 0)); +@@ -1109,13 +1290,18 @@ static void restore_cpu_ipis(unsigned in + + /* Record the new mapping. */ + evtchn_to_irq[evtchn] = irq; ++#ifdef PER_CPU_IPI_IRQ + irq_cfg(irq)->info = mk_irq_info(IRQT_IPI, ipi, evtchn); ++#else ++ per_cpu(ipi_to_evtchn, cpu)[ipi] = evtchn; ++#endif + bind_evtchn_to_cpu(evtchn, cpu); + + /* Ready for use. */ + if (!(irq_to_desc(irq)->status & IRQ_DISABLED)) + unmask_evtchn(evtchn); + } ++#endif + } + + static int evtchn_resume(struct sys_device *dev) +--- head-2010-04-15.orig/drivers/xen/core/smpboot.c 2010-03-19 15:20:15.000000000 +0100 ++++ head-2010-04-15/drivers/xen/core/smpboot.c 2010-03-19 15:20:24.000000000 +0100 +@@ -40,14 +40,10 @@ cpumask_var_t vcpu_initialized_mask; + DEFINE_PER_CPU(struct cpuinfo_x86, cpu_info); + EXPORT_PER_CPU_SYMBOL(cpu_info); + +-static DEFINE_PER_CPU(int, resched_irq); +-static DEFINE_PER_CPU(int, callfunc_irq); +-static DEFINE_PER_CPU(int, call1func_irq); +-static DEFINE_PER_CPU(int, reboot_irq); +-static char resched_name[NR_CPUS][15]; +-static char callfunc_name[NR_CPUS][15]; +-static char call1func_name[NR_CPUS][15]; +-static char reboot_name[NR_CPUS][15]; ++static int __read_mostly resched_irq = -1; ++static int __read_mostly callfunc_irq = -1; ++static int __read_mostly call1func_irq = -1; ++static int __read_mostly reboot_irq = -1; + + #ifdef CONFIG_X86_LOCAL_APIC + #define set_cpu_to_apicid(cpu, apicid) (per_cpu(x86_cpu_to_apicid, cpu) = (apicid)) +@@ -109,58 +105,68 @@ remove_siblinginfo(unsigned int cpu) + + static int __cpuinit xen_smp_intr_init(unsigned int cpu) + { ++ static struct irqaction resched_action = { ++ .handler = smp_reschedule_interrupt, ++ .flags = IRQF_DISABLED, ++ .name = "resched" ++ }, callfunc_action = { ++ .handler = smp_call_function_interrupt, ++ .flags = IRQF_DISABLED, ++ .name = "callfunc" ++ }, call1func_action = { ++ .handler = smp_call_function_single_interrupt, ++ .flags = IRQF_DISABLED, ++ .name = "call1func" ++ }, reboot_action = { ++ .handler = smp_reboot_interrupt, ++ .flags = IRQF_DISABLED, ++ .name = "reboot" ++ }; + int rc; + +- per_cpu(resched_irq, cpu) = per_cpu(callfunc_irq, cpu) = +- per_cpu(call1func_irq, cpu) = per_cpu(reboot_irq, cpu) = -1; +- +- sprintf(resched_name[cpu], "resched%u", cpu); +- rc = bind_ipi_to_irqhandler(RESCHEDULE_VECTOR, +- cpu, +- smp_reschedule_interrupt, +- IRQF_DISABLED|IRQF_NOBALANCING, +- resched_name[cpu], +- NULL); ++ rc = bind_ipi_to_irqaction(RESCHEDULE_VECTOR, ++ cpu, ++ &resched_action); + if (rc < 0) +- goto fail; +- per_cpu(resched_irq, cpu) = rc; +- +- sprintf(callfunc_name[cpu], "callfunc%u", cpu); +- rc = bind_ipi_to_irqhandler(CALL_FUNCTION_VECTOR, +- cpu, +- smp_call_function_interrupt, +- IRQF_DISABLED|IRQF_NOBALANCING, +- callfunc_name[cpu], +- NULL); ++ return rc; ++ if (resched_irq < 0) ++ resched_irq = rc; ++ else ++ BUG_ON(resched_irq != rc); ++ ++ rc = bind_ipi_to_irqaction(CALL_FUNCTION_VECTOR, ++ cpu, ++ &callfunc_action); + if (rc < 0) +- goto fail; +- per_cpu(callfunc_irq, cpu) = rc; +- +- sprintf(call1func_name[cpu], "call1func%u", cpu); +- rc = bind_ipi_to_irqhandler(CALL_FUNC_SINGLE_VECTOR, +- cpu, +- smp_call_function_single_interrupt, +- IRQF_DISABLED|IRQF_NOBALANCING, +- call1func_name[cpu], +- NULL); ++ goto unbind_resched; ++ if (callfunc_irq < 0) ++ callfunc_irq = rc; ++ else ++ BUG_ON(callfunc_irq != rc); ++ ++ rc = bind_ipi_to_irqaction(CALL_FUNC_SINGLE_VECTOR, ++ cpu, ++ &call1func_action); + if (rc < 0) +- goto fail; +- per_cpu(call1func_irq, cpu) = rc; +- +- sprintf(reboot_name[cpu], "reboot%u", cpu); +- rc = bind_ipi_to_irqhandler(REBOOT_VECTOR, +- cpu, +- smp_reboot_interrupt, +- IRQF_DISABLED|IRQF_NOBALANCING, +- reboot_name[cpu], +- NULL); ++ goto unbind_call; ++ if (call1func_irq < 0) ++ call1func_irq = rc; ++ else ++ BUG_ON(call1func_irq != rc); ++ ++ rc = bind_ipi_to_irqaction(REBOOT_VECTOR, ++ cpu, ++ &reboot_action); + if (rc < 0) +- goto fail; +- per_cpu(reboot_irq, cpu) = rc; ++ goto unbind_call1; ++ if (reboot_irq < 0) ++ reboot_irq = rc; ++ else ++ BUG_ON(reboot_irq != rc); + + rc = xen_spinlock_init(cpu); + if (rc < 0) +- goto fail; ++ goto unbind_reboot; + + if ((cpu != 0) && ((rc = local_setup_timer(cpu)) != 0)) + goto fail; +@@ -168,15 +174,15 @@ static int __cpuinit xen_smp_intr_init(u + return 0; + + fail: +- if (per_cpu(resched_irq, cpu) >= 0) +- unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); +- if (per_cpu(callfunc_irq, cpu) >= 0) +- unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); +- if (per_cpu(call1func_irq, cpu) >= 0) +- unbind_from_irqhandler(per_cpu(call1func_irq, cpu), NULL); +- if (per_cpu(reboot_irq, cpu) >= 0) +- unbind_from_irqhandler(per_cpu(reboot_irq, cpu), NULL); + xen_spinlock_cleanup(cpu); ++ unbind_reboot: ++ unbind_from_per_cpu_irq(reboot_irq, cpu); ++ unbind_call1: ++ unbind_from_per_cpu_irq(call1func_irq, cpu); ++ unbind_call: ++ unbind_from_per_cpu_irq(callfunc_irq, cpu); ++ unbind_resched: ++ unbind_from_per_cpu_irq(resched_irq, cpu); + return rc; + } + +@@ -186,10 +192,10 @@ static void __cpuinit xen_smp_intr_exit( + if (cpu != 0) + local_teardown_timer(cpu); + +- unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); +- unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); +- unbind_from_irqhandler(per_cpu(call1func_irq, cpu), NULL); +- unbind_from_irqhandler(per_cpu(reboot_irq, cpu), NULL); ++ unbind_from_per_cpu_irq(resched_irq, cpu); ++ unbind_from_per_cpu_irq(callfunc_irq, cpu); ++ unbind_from_per_cpu_irq(call1func_irq, cpu); ++ unbind_from_per_cpu_irq(reboot_irq, cpu); + xen_spinlock_cleanup(cpu); + } + #endif +--- head-2010-04-15.orig/drivers/xen/core/spinlock.c 2010-04-15 10:14:50.000000000 +0200 ++++ head-2010-04-15/drivers/xen/core/spinlock.c 2010-02-24 12:38:00.000000000 +0100 +@@ -14,8 +14,7 @@ + + #ifdef TICKET_SHIFT + +-static DEFINE_PER_CPU(int, spinlock_irq) = -1; +-static char spinlock_name[NR_CPUS][15]; ++static int __read_mostly spinlock_irq = -1; + + struct spinning { + arch_spinlock_t *lock; +@@ -32,29 +31,31 @@ static DEFINE_PER_CPU(arch_rwlock_t, spi + + int __cpuinit xen_spinlock_init(unsigned int cpu) + { ++ static struct irqaction spinlock_action = { ++ .handler = smp_reschedule_interrupt, ++ .flags = IRQF_DISABLED, ++ .name = "spinlock" ++ }; + int rc; + +- sprintf(spinlock_name[cpu], "spinlock%u", cpu); +- rc = bind_ipi_to_irqhandler(SPIN_UNLOCK_VECTOR, +- cpu, +- smp_reschedule_interrupt, +- IRQF_DISABLED|IRQF_NOBALANCING, +- spinlock_name[cpu], +- NULL); ++ rc = bind_ipi_to_irqaction(SPIN_UNLOCK_VECTOR, ++ cpu, ++ &spinlock_action); + if (rc < 0) + return rc; + +- disable_irq(rc); /* make sure it's never delivered */ +- per_cpu(spinlock_irq, cpu) = rc; ++ if (spinlock_irq < 0) { ++ disable_irq(rc); /* make sure it's never delivered */ ++ spinlock_irq = rc; ++ } else ++ BUG_ON(spinlock_irq != rc); + + return 0; + } + + void __cpuinit xen_spinlock_cleanup(unsigned int cpu) + { +- if (per_cpu(spinlock_irq, cpu) >= 0) +- unbind_from_irqhandler(per_cpu(spinlock_irq, cpu), NULL); +- per_cpu(spinlock_irq, cpu) = -1; ++ unbind_from_per_cpu_irq(spinlock_irq, cpu); + } + + static unsigned int spin_adjust(struct spinning *spinning, +@@ -84,7 +85,7 @@ unsigned int xen_spin_adjust(const arch_ + bool xen_spin_wait(arch_spinlock_t *lock, unsigned int *ptok, + unsigned int flags) + { +- int irq = percpu_read(spinlock_irq); ++ int irq = spinlock_irq; + bool rc; + typeof(vcpu_info(0)->evtchn_upcall_mask) upcall_mask; + arch_rwlock_t *rm_lock; +@@ -240,7 +241,7 @@ void xen_spin_kick(arch_spinlock_t *lock + raw_local_irq_restore(flags); + + if (unlikely(spinning)) { +- notify_remote_via_irq(per_cpu(spinlock_irq, cpu)); ++ notify_remote_via_ipi(SPIN_UNLOCK_VECTOR, cpu); + return; + } + } +--- head-2010-04-15.orig/include/xen/evtchn.h 2010-03-31 14:10:36.000000000 +0200 ++++ head-2010-04-15/include/xen/evtchn.h 2010-03-31 14:41:42.000000000 +0200 +@@ -93,6 +93,8 @@ int bind_virq_to_irqhandler( + unsigned long irqflags, + const char *devname, + void *dev_id); ++#if defined(CONFIG_SMP) && !defined(MODULE) ++#ifndef CONFIG_X86 + int bind_ipi_to_irqhandler( + unsigned int ipi, + unsigned int cpu, +@@ -100,6 +102,13 @@ int bind_ipi_to_irqhandler( + unsigned long irqflags, + const char *devname, + void *dev_id); ++#else ++int bind_ipi_to_irqaction( ++ unsigned int ipi, ++ unsigned int cpu, ++ struct irqaction *action); ++#endif ++#endif + + /* + * Common unbind function for all event sources. Takes IRQ to unbind from. +@@ -108,6 +117,11 @@ int bind_ipi_to_irqhandler( + */ + void unbind_from_irqhandler(unsigned int irq, void *dev_id); + ++#if defined(CONFIG_SMP) && !defined(MODULE) && defined(CONFIG_X86) ++/* Specialized unbind function for per-CPU IRQs. */ ++void unbind_from_per_cpu_irq(unsigned int irq, unsigned int cpu); ++#endif ++ + #ifndef CONFIG_XEN + void irq_resume(void); + #endif +@@ -183,5 +197,9 @@ void xen_poll_irq(int irq); + void notify_remote_via_irq(int irq); + int irq_to_evtchn_port(int irq); + ++#if defined(CONFIG_SMP) && !defined(MODULE) && defined(CONFIG_X86) ++void notify_remote_via_ipi(unsigned int ipi, unsigned int cpu); ++#endif ++ + #endif /* __ASM_EVTCHN_H__ */ + #endif /* CONFIG_PARAVIRT_XEN */ diff --git a/patches.xen/xen-kconfig-compat b/patches.xen/xen-kconfig-compat new file mode 100644 index 0000000..24a38e5 --- /dev/null +++ b/patches.xen/xen-kconfig-compat @@ -0,0 +1,32 @@ +From: jbeulich@novell.com +Subject: add backward-compatibility configure options +Patch-mainline: n/a + +--- head-2010-03-24.orig/drivers/xen/Kconfig 2010-03-31 14:08:50.000000000 +0200 ++++ head-2010-03-24/drivers/xen/Kconfig 2010-03-31 14:09:58.000000000 +0200 +@@ -321,6 +321,15 @@ choice + config XEN_COMPAT_030100_AND_LATER + bool "3.1.0 and later" + ++ config XEN_COMPAT_030200_AND_LATER ++ bool "3.2.0 and later" ++ ++ config XEN_COMPAT_030300_AND_LATER ++ bool "3.3.0 and later" ++ ++ config XEN_COMPAT_030400_AND_LATER ++ bool "3.4.0 and later" ++ + config XEN_COMPAT_LATEST_ONLY + bool "no compatibility code" + +@@ -329,6 +338,9 @@ endchoice + config XEN_COMPAT + hex + default 0xffffff if XEN_COMPAT_LATEST_ONLY ++ default 0x030400 if XEN_COMPAT_030400_AND_LATER ++ default 0x030300 if XEN_COMPAT_030300_AND_LATER ++ default 0x030200 if XEN_COMPAT_030200_AND_LATER + default 0x030100 if XEN_COMPAT_030100_AND_LATER + default 0x030004 if XEN_COMPAT_030004_AND_LATER + default 0x030002 if XEN_COMPAT_030002_AND_LATER diff --git a/patches.xen/xen-kzalloc b/patches.xen/xen-kzalloc new file mode 100644 index 0000000..351199d --- /dev/null +++ b/patches.xen/xen-kzalloc @@ -0,0 +1,211 @@ +From: jbeulich@novell.com +Subject: use kzalloc() in favor of kmalloc()+memset() +Patch-mainline: n/a + +Also use clear_page() in favor of memset(, 0, PAGE_SIZE). + +--- head-2010-04-29.orig/drivers/xen/blkback/blkback.c 2010-03-25 14:38:05.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blkback/blkback.c 2010-04-28 16:32:16.000000000 +0200 +@@ -671,7 +671,7 @@ static int __init blkif_init(void) + + mmap_pages = blkif_reqs * BLKIF_MAX_SEGMENTS_PER_REQUEST; + +- pending_reqs = kmalloc(sizeof(pending_reqs[0]) * ++ pending_reqs = kzalloc(sizeof(pending_reqs[0]) * + blkif_reqs, GFP_KERNEL); + pending_grant_handles = kmalloc(sizeof(pending_grant_handles[0]) * + mmap_pages, GFP_KERNEL); +@@ -688,7 +688,6 @@ static int __init blkif_init(void) + + blkif_interface_init(); + +- memset(pending_reqs, 0, sizeof(pending_reqs)); + INIT_LIST_HEAD(&pending_free); + + for (i = 0; i < blkif_reqs; i++) +--- head-2010-04-29.orig/drivers/xen/blkback/interface.c 2010-03-24 15:09:22.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blkback/interface.c 2010-04-28 16:37:43.000000000 +0200 +@@ -41,11 +41,10 @@ blkif_t *blkif_alloc(domid_t domid) + { + blkif_t *blkif; + +- blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL); ++ blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL|__GFP_ZERO); + if (!blkif) + return ERR_PTR(-ENOMEM); + +- memset(blkif, 0, sizeof(*blkif)); + blkif->domid = domid; + spin_lock_init(&blkif->blk_ring_lock); + atomic_set(&blkif->refcnt, 1); +--- head-2010-04-29.orig/drivers/xen/blktap/interface.c 2010-03-24 15:09:22.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blktap/interface.c 2010-04-28 16:38:55.000000000 +0200 +@@ -41,11 +41,10 @@ blkif_t *tap_alloc_blkif(domid_t domid) + { + blkif_t *blkif; + +- blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL); ++ blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL|__GFP_ZERO); + if (!blkif) + return ERR_PTR(-ENOMEM); + +- memset(blkif, 0, sizeof(*blkif)); + blkif->domid = domid; + spin_lock_init(&blkif->blk_ring_lock); + atomic_set(&blkif->refcnt, 1); +--- head-2010-04-29.orig/drivers/xen/core/machine_reboot.c 2010-03-25 14:39:15.000000000 +0100 ++++ head-2010-04-29/drivers/xen/core/machine_reboot.c 2010-04-28 17:04:28.000000000 +0200 +@@ -102,7 +102,7 @@ static void post_suspend(int suspend_can + BUG(); + HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO); + +- memset(empty_zero_page, 0, PAGE_SIZE); ++ clear_page(empty_zero_page); + + fpp = PAGE_SIZE/sizeof(unsigned long); + for (i = 0, j = 0, k = -1; i < max_pfn; i += fpp, j++) { +--- head-2010-04-29.orig/drivers/xen/core/smpboot.c 2010-04-15 11:43:29.000000000 +0200 ++++ head-2010-04-29/drivers/xen/core/smpboot.c 2010-04-28 16:44:14.000000000 +0200 +@@ -218,17 +218,12 @@ static void __cpuinit cpu_initialize_con + ctxt.flags = VGCF_IN_KERNEL; + ctxt.user_regs.ds = __USER_DS; + ctxt.user_regs.es = __USER_DS; +- ctxt.user_regs.fs = 0; +- ctxt.user_regs.gs = 0; + ctxt.user_regs.ss = __KERNEL_DS; + ctxt.user_regs.eip = (unsigned long)cpu_bringup_and_idle; + ctxt.user_regs.eflags = X86_EFLAGS_IF | 0x1000; /* IOPL_RING1 */ + +- memset(&ctxt.fpu_ctxt, 0, sizeof(ctxt.fpu_ctxt)); +- + smp_trap_init(ctxt.trap_ctxt); + +- ctxt.ldt_ents = 0; + ctxt.gdt_frames[0] = arbitrary_virt_to_mfn(get_cpu_gdt_table(cpu)); + ctxt.gdt_ents = GDT_SIZE / 8; + +--- head-2010-04-29.orig/drivers/xen/netback/interface.c 2010-04-30 10:42:29.000000000 +0200 ++++ head-2010-04-29/drivers/xen/netback/interface.c 2010-04-30 10:49:15.000000000 +0200 +@@ -227,7 +227,6 @@ netif_t *netif_alloc(struct device *pare + SET_NETDEV_DEV(dev, parent); + + netif = netdev_priv(dev); +- memset(netif, 0, sizeof(*netif)); + netif->domid = domid; + netif->group = UINT_MAX; + netif->handle = handle; +--- head-2010-04-29.orig/drivers/xen/scsiback/emulate.c 2010-03-24 15:10:29.000000000 +0100 ++++ head-2010-04-29/drivers/xen/scsiback/emulate.c 2010-04-28 16:51:05.000000000 +0200 +@@ -240,13 +240,11 @@ static void __report_luns(pending_req_t + alloc_len = sizeof(struct scsi_lun) * alloc_luns + + VSCSI_REPORT_LUNS_HEADER; + retry: +- if ((buff = kmalloc(alloc_len, GFP_KERNEL)) == NULL) { ++ if ((buff = kzalloc(alloc_len, GFP_KERNEL)) == NULL) { + printk(KERN_ERR "scsiback:%s kmalloc err\n", __FUNCTION__); + goto fail; + } + +- memset(buff, 0, alloc_len); +- + one_lun = (struct scsi_lun *) &buff[8]; + spin_lock_irqsave(&info->v2p_lock, flags); + list_for_each_entry(entry, head, l) { +--- head-2010-04-29.orig/drivers/xen/scsiback/interface.c 2010-03-24 15:09:22.000000000 +0100 ++++ head-2010-04-29/drivers/xen/scsiback/interface.c 2010-04-28 16:51:29.000000000 +0200 +@@ -46,11 +46,10 @@ struct vscsibk_info *vscsibk_info_alloc( + { + struct vscsibk_info *info; + +- info = kmem_cache_alloc(scsiback_cachep, GFP_KERNEL); ++ info = kmem_cache_alloc(scsiback_cachep, GFP_KERNEL|__GFP_ZERO); + if (!info) + return ERR_PTR(-ENOMEM); + +- memset(info, 0, sizeof(*info)); + info->domid = domid; + spin_lock_init(&info->ring_lock); + atomic_set(&info->nr_unreplied_reqs, 0); +--- head-2010-04-29.orig/drivers/xen/scsiback/scsiback.c 2010-03-24 15:25:21.000000000 +0100 ++++ head-2010-04-29/drivers/xen/scsiback/scsiback.c 2010-04-28 16:52:02.000000000 +0200 +@@ -676,7 +676,7 @@ static int __init scsiback_init(void) + + mmap_pages = vscsiif_reqs * VSCSIIF_SG_TABLESIZE; + +- pending_reqs = kmalloc(sizeof(pending_reqs[0]) * ++ pending_reqs = kzalloc(sizeof(pending_reqs[0]) * + vscsiif_reqs, GFP_KERNEL); + pending_grant_handles = kmalloc(sizeof(pending_grant_handles[0]) * + mmap_pages, GFP_KERNEL); +@@ -691,7 +691,6 @@ static int __init scsiback_init(void) + if (scsiback_interface_init() < 0) + goto out_of_kmem; + +- memset(pending_reqs, 0, sizeof(pending_reqs)); + INIT_LIST_HEAD(&pending_free); + + for (i = 0; i < vscsiif_reqs; i++) +--- head-2010-04-29.orig/drivers/xen/sfc_netutil/accel_cuckoo_hash.c 2010-04-15 11:11:11.000000000 +0200 ++++ head-2010-04-29/drivers/xen/sfc_netutil/accel_cuckoo_hash.c 2010-04-28 16:54:07.000000000 +0200 +@@ -77,7 +77,7 @@ int cuckoo_hash_init(cuckoo_hash_table * + BUG_ON(length_bits >= sizeof(unsigned) * 8); + BUG_ON(key_length > sizeof(cuckoo_hash_key)); + +- table_mem = kmalloc(sizeof(cuckoo_hash_entry) * 2 * length, GFP_KERNEL); ++ table_mem = kzalloc(sizeof(cuckoo_hash_entry) * 2 * length, GFP_KERNEL); + + if (table_mem == NULL) + return -ENOMEM; +@@ -93,9 +93,6 @@ int cuckoo_hash_init(cuckoo_hash_table * + + set_hash_parameters(hashtab); + +- /* Zero the table */ +- memset(hashtab->table0, 0, length * 2 * sizeof(cuckoo_hash_entry)); +- + return 0; + } + EXPORT_SYMBOL_GPL(cuckoo_hash_init); +--- head-2010-04-29.orig/drivers/xen/tpmback/interface.c 2010-03-24 15:09:22.000000000 +0100 ++++ head-2010-04-29/drivers/xen/tpmback/interface.c 2010-04-28 16:55:39.000000000 +0200 +@@ -26,11 +26,10 @@ static tpmif_t *alloc_tpmif(domid_t domi + { + tpmif_t *tpmif; + +- tpmif = kmem_cache_alloc(tpmif_cachep, GFP_KERNEL); ++ tpmif = kmem_cache_alloc(tpmif_cachep, GFP_KERNEL|__GFP_ZERO); + if (tpmif == NULL) + goto out_of_memory; + +- memset(tpmif, 0, sizeof (*tpmif)); + tpmif->domid = domid; + tpmif->status = DISCONNECTED; + tpmif->bi = bi; +@@ -131,7 +130,7 @@ int tpmif_map(tpmif_t *tpmif, unsigned l + } + + tpmif->tx = (tpmif_tx_interface_t *)tpmif->tx_area->addr; +- memset(tpmif->tx, 0, PAGE_SIZE); ++ clear_page(tpmif->tx); + + err = bind_interdomain_evtchn_to_irqhandler( + tpmif->domid, evtchn, tpmif_be_int, 0, tpmif->devname, tpmif); +--- head-2010-04-29.orig/drivers/xen/usbback/usbback.c 2010-04-15 17:36:18.000000000 +0200 ++++ head-2010-04-29/drivers/xen/usbback/usbback.c 2010-04-28 16:56:36.000000000 +0200 +@@ -1149,7 +1149,7 @@ static int __init usbback_init(void) + return -ENODEV; + + mmap_pages = usbif_reqs * USBIF_MAX_SEGMENTS_PER_REQUEST; +- pending_reqs = kmalloc(sizeof(pending_reqs[0]) * ++ pending_reqs = kzalloc(sizeof(pending_reqs[0]) * + usbif_reqs, GFP_KERNEL); + pending_grant_handles = kmalloc(sizeof(pending_grant_handles[0]) * + mmap_pages, GFP_KERNEL); +@@ -1163,7 +1163,6 @@ static int __init usbback_init(void) + for (i = 0; i < mmap_pages; i++) + pending_grant_handles[i] = USBBACK_INVALID_HANDLE; + +- memset(pending_reqs, 0, sizeof(pending_reqs)); + INIT_LIST_HEAD(&pending_free); + + for (i = 0; i < usbif_reqs; i++) diff --git a/patches.xen/xen-modular-blktap b/patches.xen/xen-modular-blktap new file mode 100644 index 0000000..cbc56a1 --- /dev/null +++ b/patches.xen/xen-modular-blktap @@ -0,0 +1,27 @@ +From: ccoffing@novell.com +Subject: Retain backwards-compatible module name with CONFIG_XEN_BLKDEV_TAP=m +Patch-mainline: obsolete + +--- head-2009-05-29.orig/drivers/xen/blktap/Makefile 2007-06-12 13:13:44.000000000 +0200 ++++ head-2009-05-29/drivers/xen/blktap/Makefile 2009-05-29 12:39:04.000000000 +0200 +@@ -1,5 +1,5 @@ + LINUXINCLUDE += -I../xen/include/public/io + +-obj-$(CONFIG_XEN_BLKDEV_TAP) := xenblktap.o ++obj-$(CONFIG_XEN_BLKDEV_TAP) := blktap.o + +-xenblktap-y := xenbus.o interface.o blktap.o ++blktap-y := xenbus.o interface.o blocktap.o +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2009-05-29/drivers/xen/blktap/blocktap.c 2009-05-29 12:39:04.000000000 +0200 +@@ -0,0 +1 @@ ++#include "blktap.c" +--- head-2009-05-29.orig/drivers/xen/blktap2/Makefile 2009-05-29 10:25:53.000000000 +0200 ++++ head-2009-05-29/drivers/xen/blktap2/Makefile 2009-05-29 12:39:04.000000000 +0200 +@@ -1,3 +1,4 @@ +-obj-$(CONFIG_XEN_BLKDEV_TAP2) := blktap.o ++obj-$(CONFIG_XEN_BLKDEV_TAP2) := blktap2.o + +-blktap-objs := control.o ring.o wait_queue.o device.o request.o sysfs.o ++blktap2-y := control.o ring.o wait_queue.o device.o request.o ++blktap2-$(CONFIG_SYSFS) += sysfs.o diff --git a/patches.xen/xen-netback-generalize b/patches.xen/xen-netback-generalize new file mode 100644 index 0000000..857f50f --- /dev/null +++ b/patches.xen/xen-netback-generalize @@ -0,0 +1,1295 @@ +From: Dongxiao Xu +Subject: [PATCH 1/3] Netback: Generalize static/global variables into 'struct xen_netbk'. +Patch-mainline: n/a + + Put all the static/global variables in netback.c into xen_netback + structure. Do some preparations for the support of netback multiple + threads. + +Signed-off-by: Dongxiao Xu + +jb: various cleanups +Acked-by: jbeulich@novell.com + +--- head-2010-04-29.orig/drivers/xen/netback/common.h 2010-03-24 15:06:12.000000000 +0100 ++++ head-2010-04-29/drivers/xen/netback/common.h 2010-04-30 11:11:33.000000000 +0200 +@@ -217,4 +217,74 @@ static inline int netbk_can_sg(struct ne + return netif->features & NETIF_F_SG; + } + ++struct pending_tx_info { ++ netif_tx_request_t req; ++ netif_t *netif; ++}; ++typedef unsigned int pending_ring_idx_t; ++ ++struct netbk_rx_meta { ++ skb_frag_t frag; ++ int id; ++ u8 copy:1; ++}; ++ ++struct netbk_tx_pending_inuse { ++ struct list_head list; ++ unsigned long alloc_time; ++}; ++ ++#define MAX_PENDING_REQS (1U << CONFIG_XEN_NETDEV_TX_SHIFT) ++#define MAX_MFN_ALLOC 64 ++ ++struct xen_netbk { ++ struct tasklet_struct net_tx_tasklet; ++ struct tasklet_struct net_rx_tasklet; ++ ++ struct sk_buff_head rx_queue; ++ struct sk_buff_head tx_queue; ++ ++ struct timer_list net_timer; ++ struct timer_list tx_pending_timer; ++ ++ pending_ring_idx_t pending_prod; ++ pending_ring_idx_t pending_cons; ++ pending_ring_idx_t dealloc_prod; ++ pending_ring_idx_t dealloc_cons; ++ ++ struct list_head pending_inuse_head; ++ struct list_head net_schedule_list; ++ ++ spinlock_t net_schedule_list_lock; ++ spinlock_t release_lock; ++ ++ struct page **mmap_pages; ++ ++ unsigned int alloc_index; ++ ++ struct pending_tx_info pending_tx_info[MAX_PENDING_REQS]; ++ struct netbk_tx_pending_inuse pending_inuse[MAX_PENDING_REQS]; ++ struct gnttab_unmap_grant_ref tx_unmap_ops[MAX_PENDING_REQS]; ++ struct gnttab_map_grant_ref tx_map_ops[MAX_PENDING_REQS]; ++ ++ grant_handle_t grant_tx_handle[MAX_PENDING_REQS]; ++ u16 pending_ring[MAX_PENDING_REQS]; ++ u16 dealloc_ring[MAX_PENDING_REQS]; ++ ++ struct multicall_entry rx_mcl[NET_RX_RING_SIZE+3]; ++ struct mmu_update rx_mmu[NET_RX_RING_SIZE]; ++ struct gnttab_transfer grant_trans_op[NET_RX_RING_SIZE]; ++ struct gnttab_copy grant_copy_op[NET_RX_RING_SIZE]; ++ DECLARE_BITMAP(rx_notify, NR_DYNIRQS); ++#if !defined(NR_DYNIRQS) ++# error ++#elif NR_DYNIRQS <= 0x10000 ++ u16 notify_list[NET_RX_RING_SIZE]; ++#else ++ int notify_list[NET_RX_RING_SIZE]; ++#endif ++ struct netbk_rx_meta meta[NET_RX_RING_SIZE]; ++ ++ unsigned long mfn_list[MAX_MFN_ALLOC]; ++}; + #endif /* __NETIF__BACKEND__COMMON_H__ */ +--- head-2010-04-29.orig/drivers/xen/netback/netback.c 2010-01-04 13:31:57.000000000 +0100 ++++ head-2010-04-29/drivers/xen/netback/netback.c 2010-04-30 11:49:12.000000000 +0200 +@@ -35,23 +35,18 @@ + */ + + #include "common.h" ++#include + #include + #include + + /*define NETBE_DEBUG_INTERRUPT*/ + +-struct netbk_rx_meta { +- skb_frag_t frag; +- int id; +- u8 copy:1; +-}; ++static struct xen_netbk *__read_mostly xen_netbk; ++static const unsigned int __read_mostly netbk_nr_groups = 1; + +-struct netbk_tx_pending_inuse { +- struct list_head list; +- unsigned long alloc_time; +-}; ++#define GET_GROUP_INDEX(netif) (0) + +-static void netif_idx_release(u16 pending_idx); ++static void netif_idx_release(struct xen_netbk *, u16 pending_idx); + static void make_tx_response(netif_t *netif, + netif_tx_request_t *txp, + s8 st); +@@ -62,81 +57,67 @@ static netif_rx_response_t *make_rx_resp + u16 size, + u16 flags); + +-static void net_tx_action(unsigned long unused); +-static DECLARE_TASKLET(net_tx_tasklet, net_tx_action, 0); +- +-static void net_rx_action(unsigned long unused); +-static DECLARE_TASKLET(net_rx_tasklet, net_rx_action, 0); +- +-static struct timer_list net_timer; +-static struct timer_list netbk_tx_pending_timer; ++static void net_tx_action(unsigned long group); ++static void net_rx_action(unsigned long group); + +-#define MAX_PENDING_REQS (1U << CONFIG_XEN_NETDEV_TX_SHIFT) +- +-static struct sk_buff_head rx_queue; +- +-static struct page **mmap_pages; +-static inline unsigned long idx_to_pfn(unsigned int idx) ++static inline unsigned long idx_to_pfn(struct xen_netbk *netbk, unsigned int idx) + { +- return page_to_pfn(mmap_pages[idx]); ++ return page_to_pfn(netbk->mmap_pages[idx]); + } + +-static inline unsigned long idx_to_kaddr(unsigned int idx) ++static inline unsigned long idx_to_kaddr(struct xen_netbk *netbk, unsigned int idx) + { +- return (unsigned long)pfn_to_kaddr(idx_to_pfn(idx)); ++ return (unsigned long)pfn_to_kaddr(idx_to_pfn(netbk, idx)); + } + + /* extra field used in struct page */ +-static inline void netif_set_page_index(struct page *pg, unsigned int index) ++union page_ext { ++ struct { ++#if BITS_PER_LONG < 64 ++#define GROUP_WIDTH (BITS_PER_LONG - CONFIG_XEN_NETDEV_TX_SHIFT) ++#define MAX_GROUPS ((1U << GROUP_WIDTH) - 1) ++ unsigned int grp:GROUP_WIDTH; ++ unsigned int idx:CONFIG_XEN_NETDEV_TX_SHIFT; ++#else ++#define MAX_GROUPS UINT_MAX ++ unsigned int grp, idx; ++#endif ++ } e; ++ void *mapping; ++}; ++ ++static inline void netif_set_page_ext(struct page *pg, unsigned int group, ++ unsigned int idx) + { +- *(unsigned long *)&pg->mapping = index; ++ union page_ext ext = { .e = { .grp = group + 1, .idx = idx } }; ++ ++ BUILD_BUG_ON(sizeof(ext) > sizeof(ext.mapping)); ++ pg->mapping = ext.mapping; + } + +-static inline int netif_page_index(struct page *pg) ++static inline unsigned int netif_page_group(const struct page *pg) + { +- unsigned long idx = (unsigned long)pg->mapping; ++ union page_ext ext = { .mapping = pg->mapping }; + +- if (!PageForeign(pg)) +- return -1; ++ return ext.e.grp - 1; ++} + +- if ((idx >= MAX_PENDING_REQS) || (mmap_pages[idx] != pg)) +- return -1; ++static inline unsigned int netif_page_index(const struct page *pg) ++{ ++ union page_ext ext = { .mapping = pg->mapping }; + +- return idx; ++ return ext.e.idx; + } + + #define PKT_PROT_LEN 64 + +-static struct pending_tx_info { +- netif_tx_request_t req; +- netif_t *netif; +-} pending_tx_info[MAX_PENDING_REQS]; +-static u16 pending_ring[MAX_PENDING_REQS]; +-typedef unsigned int PEND_RING_IDX; + #define MASK_PEND_IDX(_i) ((_i)&(MAX_PENDING_REQS-1)) +-static PEND_RING_IDX pending_prod, pending_cons; +-#define NR_PENDING_REQS (MAX_PENDING_REQS - pending_prod + pending_cons) + +-/* Freed TX SKBs get batched on this ring before return to pending_ring. */ +-static u16 dealloc_ring[MAX_PENDING_REQS]; +-static PEND_RING_IDX dealloc_prod, dealloc_cons; +- +-/* Doubly-linked list of in-use pending entries. */ +-static struct netbk_tx_pending_inuse pending_inuse[MAX_PENDING_REQS]; +-static LIST_HEAD(pending_inuse_head); +- +-static struct sk_buff_head tx_queue; +- +-static grant_handle_t grant_tx_handle[MAX_PENDING_REQS]; +-static gnttab_unmap_grant_ref_t tx_unmap_ops[MAX_PENDING_REQS]; +-static gnttab_map_grant_ref_t tx_map_ops[MAX_PENDING_REQS]; +- +-static struct list_head net_schedule_list; +-static spinlock_t net_schedule_list_lock; +- +-#define MAX_MFN_ALLOC 64 +-static unsigned long mfn_list[MAX_MFN_ALLOC]; +-static unsigned int alloc_index = 0; ++static inline pending_ring_idx_t nr_pending_reqs(const struct xen_netbk *netbk) ++{ ++ return MAX_PENDING_REQS - ++ netbk->pending_prod + netbk->pending_cons; ++} + + /* Setting this allows the safe use of this driver without netloop. */ + static int MODPARM_copy_skb = 1; +@@ -148,13 +129,13 @@ MODULE_PARM_DESC(permute_returns, "Rando + + int netbk_copy_skb_mode; + +-static inline unsigned long alloc_mfn(void) ++static inline unsigned long alloc_mfn(struct xen_netbk *netbk) + { +- BUG_ON(alloc_index == 0); +- return mfn_list[--alloc_index]; ++ BUG_ON(netbk->alloc_index == 0); ++ return netbk->mfn_list[--netbk->alloc_index]; + } + +-static int check_mfn(int nr) ++static int check_mfn(struct xen_netbk *netbk, unsigned int nr) + { + struct xen_memory_reservation reservation = { + .extent_order = 0, +@@ -162,24 +143,27 @@ static int check_mfn(int nr) + }; + int rc; + +- if (likely(alloc_index >= nr)) ++ if (likely(netbk->alloc_index >= nr)) + return 0; + +- set_xen_guest_handle(reservation.extent_start, mfn_list + alloc_index); +- reservation.nr_extents = MAX_MFN_ALLOC - alloc_index; ++ set_xen_guest_handle(reservation.extent_start, ++ netbk->mfn_list + netbk->alloc_index); ++ reservation.nr_extents = MAX_MFN_ALLOC - netbk->alloc_index; + rc = HYPERVISOR_memory_op(XENMEM_increase_reservation, &reservation); + if (likely(rc > 0)) +- alloc_index += rc; ++ netbk->alloc_index += rc; + +- return alloc_index >= nr ? 0 : -ENOMEM; ++ return netbk->alloc_index >= nr ? 0 : -ENOMEM; + } + +-static inline void maybe_schedule_tx_action(void) ++static inline void maybe_schedule_tx_action(unsigned int group) + { ++ struct xen_netbk *netbk = &xen_netbk[group]; ++ + smp_mb(); +- if ((NR_PENDING_REQS < (MAX_PENDING_REQS/2)) && +- !list_empty(&net_schedule_list)) +- tasklet_schedule(&net_tx_tasklet); ++ if ((nr_pending_reqs(netbk) < (MAX_PENDING_REQS/2)) && ++ !list_empty(&netbk->net_schedule_list)) ++ tasklet_schedule(&netbk->net_tx_tasklet); + } + + static struct sk_buff *netbk_copy_skb(struct sk_buff *skb) +@@ -288,6 +272,7 @@ static void tx_queue_callback(unsigned l + int netif_be_start_xmit(struct sk_buff *skb, struct net_device *dev) + { + netif_t *netif = netdev_priv(dev); ++ struct xen_netbk *netbk; + + BUG_ON(skb->dev != dev); + +@@ -337,8 +322,9 @@ int netif_be_start_xmit(struct sk_buff * + } + } + +- skb_queue_tail(&rx_queue, skb); +- tasklet_schedule(&net_rx_tasklet); ++ netbk = &xen_netbk[GET_GROUP_INDEX(netif)]; ++ skb_queue_tail(&netbk->rx_queue, skb); ++ tasklet_schedule(&netbk->net_rx_tasklet); + + return NETDEV_TX_OK; + +@@ -393,19 +379,29 @@ static u16 netbk_gop_frag(netif_t *netif + multicall_entry_t *mcl; + netif_rx_request_t *req; + unsigned long old_mfn, new_mfn; +- int idx = netif_page_index(page); ++ struct xen_netbk *netbk = &xen_netbk[GET_GROUP_INDEX(netif)]; + + old_mfn = virt_to_mfn(page_address(page)); + + req = RING_GET_REQUEST(&netif->rx, netif->rx.req_cons + i); + if (netif->copying_receiver) { ++ unsigned int group, idx; ++ + /* The fragment needs to be copied rather than + flipped. */ + meta->copy = 1; + copy_gop = npo->copy + npo->copy_prod++; + copy_gop->flags = GNTCOPY_dest_gref; +- if (idx > -1) { +- struct pending_tx_info *src_pend = &pending_tx_info[idx]; ++ if (PageForeign(page) && ++ page->mapping != NULL && ++ (idx = netif_page_index(page)) < MAX_PENDING_REQS && ++ (group = netif_page_group(page)) < netbk_nr_groups) { ++ struct pending_tx_info *src_pend; ++ ++ netbk = &xen_netbk[group]; ++ BUG_ON(netbk->mmap_pages[idx] != page); ++ src_pend = &netbk->pending_tx_info[idx]; ++ BUG_ON(group != GET_GROUP_INDEX(src_pend->netif)); + copy_gop->source.domid = src_pend->netif->domid; + copy_gop->source.u.ref = src_pend->req.gref; + copy_gop->flags |= GNTCOPY_source_gref; +@@ -421,7 +417,7 @@ static u16 netbk_gop_frag(netif_t *netif + } else { + meta->copy = 0; + if (!xen_feature(XENFEAT_auto_translated_physmap)) { +- new_mfn = alloc_mfn(); ++ new_mfn = alloc_mfn(netbk); + + /* + * Set the new P2M table entry before +@@ -566,7 +562,7 @@ static void netbk_add_frag_responses(net + } + } + +-static void net_rx_action(unsigned long unused) ++static void net_rx_action(unsigned long group) + { + netif_t *netif = NULL; + s8 status; +@@ -576,52 +572,37 @@ static void net_rx_action(unsigned long + struct sk_buff_head rxq; + struct sk_buff *skb; + int notify_nr = 0; +- int ret; ++ int ret, eagain; + int nr_frags; + int count; + unsigned long offset; +- int eagain; +- +- /* +- * Putting hundreds of bytes on the stack is considered rude. +- * Static works because a tasklet can only be on one CPU at any time. +- */ +- static multicall_entry_t rx_mcl[NET_RX_RING_SIZE+3]; +- static mmu_update_t rx_mmu[NET_RX_RING_SIZE]; +- static gnttab_transfer_t grant_trans_op[NET_RX_RING_SIZE]; +- static gnttab_copy_t grant_copy_op[NET_RX_RING_SIZE]; +- static DECLARE_BITMAP(rx_notify, NR_DYNIRQS); +-#if NR_DYNIRQS <= 0x10000 +- static u16 notify_list[NET_RX_RING_SIZE]; +-#else +- static int notify_list[NET_RX_RING_SIZE]; +-#endif +- static struct netbk_rx_meta meta[NET_RX_RING_SIZE]; ++ struct xen_netbk *netbk = &xen_netbk[group]; + + struct netrx_pending_operations npo = { +- mmu: rx_mmu, +- trans: grant_trans_op, +- copy: grant_copy_op, +- mcl: rx_mcl, +- meta: meta}; ++ .mmu = netbk->rx_mmu, ++ .trans = netbk->grant_trans_op, ++ .copy = netbk->grant_copy_op, ++ .mcl = netbk->rx_mcl, ++ .meta = netbk->meta, ++ }; + + skb_queue_head_init(&rxq); + + count = 0; + +- while ((skb = skb_dequeue(&rx_queue)) != NULL) { ++ while ((skb = skb_dequeue(&netbk->rx_queue)) != NULL) { + nr_frags = skb_shinfo(skb)->nr_frags; + *(int *)skb->cb = nr_frags; + + if (!xen_feature(XENFEAT_auto_translated_physmap) && + !((netif_t *)netdev_priv(skb->dev))->copying_receiver && +- check_mfn(nr_frags + 1)) { ++ check_mfn(netbk, nr_frags + 1)) { + /* Memory squeeze? Back off for an arbitrary while. */ + if ( net_ratelimit() ) + WPRINTK("Memory squeeze in netback " + "driver.\n"); +- mod_timer(&net_timer, jiffies + HZ); +- skb_queue_head(&rx_queue, skb); ++ mod_timer(&netbk->net_timer, jiffies + HZ); ++ skb_queue_head(&netbk->rx_queue, skb); + break; + } + +@@ -636,39 +617,39 @@ static void net_rx_action(unsigned long + break; + } + +- BUG_ON(npo.meta_prod > ARRAY_SIZE(meta)); ++ BUG_ON(npo.meta_prod > ARRAY_SIZE(netbk->meta)); + + npo.mmu_mcl = npo.mcl_prod; + if (npo.mcl_prod) { + BUG_ON(xen_feature(XENFEAT_auto_translated_physmap)); +- BUG_ON(npo.mmu_prod > ARRAY_SIZE(rx_mmu)); ++ BUG_ON(npo.mmu_prod > ARRAY_SIZE(netbk->rx_mmu)); + mcl = npo.mcl + npo.mcl_prod++; + + BUG_ON(mcl[-1].op != __HYPERVISOR_update_va_mapping); + mcl[-1].args[MULTI_UVMFLAGS_INDEX] = UVMF_TLB_FLUSH|UVMF_ALL; + + mcl->op = __HYPERVISOR_mmu_update; +- mcl->args[0] = (unsigned long)rx_mmu; ++ mcl->args[0] = (unsigned long)netbk->rx_mmu; + mcl->args[1] = npo.mmu_prod; + mcl->args[2] = 0; + mcl->args[3] = DOMID_SELF; + } + + if (npo.trans_prod) { +- BUG_ON(npo.trans_prod > ARRAY_SIZE(grant_trans_op)); ++ BUG_ON(npo.trans_prod > ARRAY_SIZE(netbk->grant_trans_op)); + mcl = npo.mcl + npo.mcl_prod++; + mcl->op = __HYPERVISOR_grant_table_op; + mcl->args[0] = GNTTABOP_transfer; +- mcl->args[1] = (unsigned long)grant_trans_op; ++ mcl->args[1] = (unsigned long)netbk->grant_trans_op; + mcl->args[2] = npo.trans_prod; + } + + if (npo.copy_prod) { +- BUG_ON(npo.copy_prod > ARRAY_SIZE(grant_copy_op)); ++ BUG_ON(npo.copy_prod > ARRAY_SIZE(netbk->grant_copy_op)); + mcl = npo.mcl + npo.mcl_prod++; + mcl->op = __HYPERVISOR_grant_table_op; + mcl->args[0] = GNTTABOP_copy; +- mcl->args[1] = (unsigned long)grant_copy_op; ++ mcl->args[1] = (unsigned long)netbk->grant_copy_op; + mcl->args[2] = npo.copy_prod; + } + +@@ -676,7 +657,7 @@ static void net_rx_action(unsigned long + if (!npo.mcl_prod) + return; + +- BUG_ON(npo.mcl_prod > ARRAY_SIZE(rx_mcl)); ++ BUG_ON(npo.mcl_prod > ARRAY_SIZE(netbk->rx_mcl)); + + ret = HYPERVISOR_multicall(npo.mcl, npo.mcl_prod); + BUG_ON(ret != 0); +@@ -707,7 +688,7 @@ static void net_rx_action(unsigned long + atomic_set(&(skb_shinfo(skb)->dataref), 1); + skb_shinfo(skb)->frag_list = NULL; + skb_shinfo(skb)->nr_frags = 0; +- netbk_free_pages(nr_frags, meta + npo.meta_cons + 1); ++ netbk_free_pages(nr_frags, netbk->meta + npo.meta_cons + 1); + } + + if(!eagain) +@@ -716,7 +697,7 @@ static void net_rx_action(unsigned long + netif->stats.tx_packets++; + } + +- id = meta[npo.meta_cons].id; ++ id = netbk->meta[npo.meta_cons].id; + flags = nr_frags ? NETRXF_more_data : 0; + + if (skb->ip_summed == CHECKSUM_PARTIAL) /* local packet? */ +@@ -724,14 +705,14 @@ static void net_rx_action(unsigned long + else if (skb->proto_data_valid) /* remote but checksummed? */ + flags |= NETRXF_data_validated; + +- if (meta[npo.meta_cons].copy) ++ if (netbk->meta[npo.meta_cons].copy) + offset = 0; + else + offset = offset_in_page(skb->data); + resp = make_rx_response(netif, id, status, offset, + skb_headlen(skb), flags); + +- if (meta[npo.meta_cons].frag.size) { ++ if (netbk->meta[npo.meta_cons].frag.size) { + struct netif_extra_info *gso = + (struct netif_extra_info *) + RING_GET_RESPONSE(&netif->rx, +@@ -739,7 +720,7 @@ static void net_rx_action(unsigned long + + resp->flags |= NETRXF_extra_info; + +- gso->u.gso.size = meta[npo.meta_cons].frag.size; ++ gso->u.gso.size = netbk->meta[npo.meta_cons].frag.size; + gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4; + gso->u.gso.pad = 0; + gso->u.gso.features = 0; +@@ -749,13 +730,13 @@ static void net_rx_action(unsigned long + } + + netbk_add_frag_responses(netif, status, +- meta + npo.meta_cons + 1, ++ netbk->meta + npo.meta_cons + 1, + nr_frags); + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netif->rx, ret); + irq = netif->irq - DYNIRQ_BASE; +- if (ret && !__test_and_set_bit(irq, rx_notify)) +- notify_list[notify_nr++] = irq; ++ if (ret && !__test_and_set_bit(irq, netbk->rx_notify)) ++ netbk->notify_list[notify_nr++] = irq; + + if (netif_queue_stopped(netif->dev) && + netif_schedulable(netif) && +@@ -772,45 +753,46 @@ static void net_rx_action(unsigned long + { + netif->rx_req_cons_peek += skb_shinfo(skb)->nr_frags + 1 + + !!skb_shinfo(skb)->gso_size; +- skb_queue_head(&rx_queue, skb); ++ skb_queue_head(&netbk->rx_queue, skb); + } + + npo.meta_cons += nr_frags + 1; + } + + if (notify_nr == 1) { +- irq = *notify_list; +- __clear_bit(irq, rx_notify); ++ irq = *netbk->notify_list; ++ __clear_bit(irq, netbk->rx_notify); + notify_remote_via_irq(irq + DYNIRQ_BASE); + } else { + for (count = ret = 0; ret < notify_nr; ++ret) { +- irq = notify_list[ret]; +- __clear_bit(irq, rx_notify); +- if (!multi_notify_remote_via_irq(rx_mcl + count, ++ irq = netbk->notify_list[ret]; ++ __clear_bit(irq, netbk->rx_notify); ++ if (!multi_notify_remote_via_irq(netbk->rx_mcl + count, + irq + DYNIRQ_BASE)) + ++count; + } +- if (HYPERVISOR_multicall(rx_mcl, count)) ++ if (HYPERVISOR_multicall(netbk->rx_mcl, count)) + BUG(); + } + + /* More work to do? */ +- if (!skb_queue_empty(&rx_queue) && !timer_pending(&net_timer)) +- tasklet_schedule(&net_rx_tasklet); ++ if (!skb_queue_empty(&netbk->rx_queue) && ++ !timer_pending(&netbk->net_timer)) ++ tasklet_schedule(&netbk->net_rx_tasklet); + #if 0 + else + xen_network_done_notify(); + #endif + } + +-static void net_alarm(unsigned long unused) ++static void net_alarm(unsigned long group) + { +- tasklet_schedule(&net_rx_tasklet); ++ tasklet_schedule(&xen_netbk[group].net_rx_tasklet); + } + +-static void netbk_tx_pending_timeout(unsigned long unused) ++static void netbk_tx_pending_timeout(unsigned long group) + { +- tasklet_schedule(&net_tx_tasklet); ++ tasklet_schedule(&xen_netbk[group].net_tx_tasklet); + } + + struct net_device_stats *netif_be_get_stats(struct net_device *dev) +@@ -826,27 +808,31 @@ static int __on_net_schedule_list(netif_ + + static void remove_from_net_schedule_list(netif_t *netif) + { +- spin_lock_irq(&net_schedule_list_lock); ++ struct xen_netbk *netbk = &xen_netbk[GET_GROUP_INDEX(netif)]; ++ ++ spin_lock_irq(&netbk->net_schedule_list_lock); + if (likely(__on_net_schedule_list(netif))) { + list_del(&netif->list); + netif->list.next = NULL; + netif_put(netif); + } +- spin_unlock_irq(&net_schedule_list_lock); ++ spin_unlock_irq(&netbk->net_schedule_list_lock); + } + + static void add_to_net_schedule_list_tail(netif_t *netif) + { ++ struct xen_netbk *netbk = &xen_netbk[GET_GROUP_INDEX(netif)]; ++ + if (__on_net_schedule_list(netif)) + return; + +- spin_lock_irq(&net_schedule_list_lock); ++ spin_lock_irq(&netbk->net_schedule_list_lock); + if (!__on_net_schedule_list(netif) && + likely(netif_schedulable(netif))) { +- list_add_tail(&netif->list, &net_schedule_list); ++ list_add_tail(&netif->list, &netbk->net_schedule_list); + netif_get(netif); + } +- spin_unlock_irq(&net_schedule_list_lock); ++ spin_unlock_irq(&netbk->net_schedule_list_lock); + } + + /* +@@ -869,7 +855,7 @@ void netif_schedule_work(netif_t *netif) + + if (more_to_do) { + add_to_net_schedule_list_tail(netif); +- maybe_schedule_tx_action(); ++ maybe_schedule_tx_action(GET_GROUP_INDEX(netif)); + } + } + +@@ -906,17 +892,19 @@ static void tx_credit_callback(unsigned + netif_schedule_work(netif); + } + +-static inline int copy_pending_req(PEND_RING_IDX pending_idx) ++static inline int copy_pending_req(struct xen_netbk *netbk, ++ pending_ring_idx_t pending_idx) + { +- return gnttab_copy_grant_page(grant_tx_handle[pending_idx], +- &mmap_pages[pending_idx]); ++ return gnttab_copy_grant_page(netbk->grant_tx_handle[pending_idx], ++ &netbk->mmap_pages[pending_idx]); + } + +-static void permute_dealloc_ring(PEND_RING_IDX dc, PEND_RING_IDX dp) ++static void permute_dealloc_ring(u16 *dealloc_ring, pending_ring_idx_t dc, ++ pending_ring_idx_t dp) + { + static unsigned random_src = 0x12345678; + unsigned dst_offset; +- PEND_RING_IDX dest; ++ pending_ring_idx_t dest; + u16 tmp; + + while (dc != dp) { +@@ -931,62 +919,67 @@ static void permute_dealloc_ring(PEND_RI + } + } + +-inline static void net_tx_action_dealloc(void) ++inline static void net_tx_action_dealloc(struct xen_netbk *netbk) + { + struct netbk_tx_pending_inuse *inuse, *n; + gnttab_unmap_grant_ref_t *gop; + u16 pending_idx; +- PEND_RING_IDX dc, dp; ++ pending_ring_idx_t dc, dp; + netif_t *netif; + int ret; + LIST_HEAD(list); + +- dc = dealloc_cons; +- gop = tx_unmap_ops; ++ dc = netbk->dealloc_cons; ++ gop = netbk->tx_unmap_ops; + + /* + * Free up any grants we have finished using + */ + do { +- dp = dealloc_prod; ++ dp = netbk->dealloc_prod; + + /* Ensure we see all indices enqueued by netif_idx_release(). */ + smp_rmb(); + + if (MODPARM_permute_returns) +- permute_dealloc_ring(dc, dp); ++ permute_dealloc_ring(netbk->dealloc_ring, dc, dp); + + while (dc != dp) { + unsigned long pfn; ++ struct netbk_tx_pending_inuse *pending_inuse = ++ netbk->pending_inuse; + +- pending_idx = dealloc_ring[MASK_PEND_IDX(dc++)]; ++ pending_idx = netbk->dealloc_ring[MASK_PEND_IDX(dc++)]; + list_move_tail(&pending_inuse[pending_idx].list, &list); + +- pfn = idx_to_pfn(pending_idx); ++ pfn = idx_to_pfn(netbk, pending_idx); + /* Already unmapped? */ + if (!phys_to_machine_mapping_valid(pfn)) + continue; + +- gnttab_set_unmap_op(gop, idx_to_kaddr(pending_idx), ++ gnttab_set_unmap_op(gop, idx_to_kaddr(netbk, pending_idx), + GNTMAP_host_map, +- grant_tx_handle[pending_idx]); ++ netbk->grant_tx_handle[pending_idx]); + gop++; + } + + if (netbk_copy_skb_mode != NETBK_DELAYED_COPY_SKB || +- list_empty(&pending_inuse_head)) ++ list_empty(&netbk->pending_inuse_head)) + break; + + /* Copy any entries that have been pending for too long. */ +- list_for_each_entry_safe(inuse, n, &pending_inuse_head, list) { ++ list_for_each_entry_safe(inuse, n, &netbk->pending_inuse_head, list) { ++ struct pending_tx_info *pending_tx_info ++ = netbk->pending_tx_info; ++ + if (time_after(inuse->alloc_time + HZ / 2, jiffies)) + break; + +- pending_idx = inuse - pending_inuse; ++ pending_idx = inuse - netbk->pending_inuse; + + pending_tx_info[pending_idx].netif->nr_copied_skbs++; + +- switch (copy_pending_req(pending_idx)) { ++ switch (copy_pending_req(netbk, pending_idx)) { + case 0: + list_move_tail(&inuse->list, &list); + continue; +@@ -999,26 +992,30 @@ inline static void net_tx_action_dealloc + + break; + } +- } while (dp != dealloc_prod); ++ } while (dp != netbk->dealloc_prod); + +- dealloc_cons = dc; ++ netbk->dealloc_cons = dc; + + ret = HYPERVISOR_grant_table_op( +- GNTTABOP_unmap_grant_ref, tx_unmap_ops, gop - tx_unmap_ops); ++ GNTTABOP_unmap_grant_ref, netbk->tx_unmap_ops, ++ gop - netbk->tx_unmap_ops); + BUG_ON(ret); + + list_for_each_entry_safe(inuse, n, &list, list) { +- pending_idx = inuse - pending_inuse; ++ struct pending_tx_info *pending_tx_info = ++ netbk->pending_tx_info; + ++ pending_idx = inuse - netbk->pending_inuse; + netif = pending_tx_info[pending_idx].netif; + + make_tx_response(netif, &pending_tx_info[pending_idx].req, + NETIF_RSP_OKAY); + + /* Ready for next use. */ +- gnttab_reset_grant_page(mmap_pages[pending_idx]); ++ gnttab_reset_grant_page(netbk->mmap_pages[pending_idx]); + +- pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx; ++ netbk->pending_ring[MASK_PEND_IDX(netbk->pending_prod++)] = ++ pending_idx; + + netif_put(netif); + +@@ -1095,9 +1092,14 @@ static gnttab_map_grant_ref_t *netbk_get + start = ((unsigned long)shinfo->frags[0].page == pending_idx); + + for (i = start; i < shinfo->nr_frags; i++, txp++) { +- pending_idx = pending_ring[MASK_PEND_IDX(pending_cons++)]; ++ struct xen_netbk *netbk = &xen_netbk[GET_GROUP_INDEX(netif)]; ++ pending_ring_idx_t index = MASK_PEND_IDX(netbk->pending_cons++); ++ struct pending_tx_info *pending_tx_info = ++ netbk->pending_tx_info; ++ ++ pending_idx = netbk->pending_ring[index]; + +- gnttab_set_map_op(mop++, idx_to_kaddr(pending_idx), ++ gnttab_set_map_op(mop++, idx_to_kaddr(netbk, pending_idx), + GNTMAP_host_map | GNTMAP_readonly, + txp->gref, netif->domid); + +@@ -1110,11 +1112,12 @@ static gnttab_map_grant_ref_t *netbk_get + return mop; + } + +-static int netbk_tx_check_mop(struct sk_buff *skb, +- gnttab_map_grant_ref_t **mopp) ++static int netbk_tx_check_mop(struct xen_netbk *netbk, struct sk_buff *skb, ++ gnttab_map_grant_ref_t **mopp) + { + gnttab_map_grant_ref_t *mop = *mopp; + int pending_idx = *((u16 *)skb->data); ++ struct pending_tx_info *pending_tx_info = netbk->pending_tx_info; + netif_t *netif = pending_tx_info[pending_idx].netif; + netif_tx_request_t *txp; + struct skb_shared_info *shinfo = skb_shinfo(skb); +@@ -1124,14 +1127,16 @@ static int netbk_tx_check_mop(struct sk_ + /* Check status of header. */ + err = mop->status; + if (unlikely(err)) { ++ pending_ring_idx_t index = MASK_PEND_IDX(netbk->pending_prod++); ++ + txp = &pending_tx_info[pending_idx].req; + make_tx_response(netif, txp, NETIF_RSP_ERROR); +- pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx; ++ netbk->pending_ring[index] = pending_idx; + netif_put(netif); + } else { +- set_phys_to_machine(idx_to_pfn(pending_idx), ++ set_phys_to_machine(idx_to_pfn(netbk, pending_idx), + FOREIGN_FRAME(mop->dev_bus_addr >> PAGE_SHIFT)); +- grant_tx_handle[pending_idx] = mop->handle; ++ netbk->grant_tx_handle[pending_idx] = mop->handle; + } + + /* Skip first skb fragment if it is on same page as header fragment. */ +@@ -1139,25 +1144,27 @@ static int netbk_tx_check_mop(struct sk_ + + for (i = start; i < nr_frags; i++) { + int j, newerr; ++ pending_ring_idx_t index; + + pending_idx = (unsigned long)shinfo->frags[i].page; + + /* Check error status: if okay then remember grant handle. */ + newerr = (++mop)->status; + if (likely(!newerr)) { +- set_phys_to_machine(idx_to_pfn(pending_idx), ++ set_phys_to_machine(idx_to_pfn(netbk, pending_idx), + FOREIGN_FRAME(mop->dev_bus_addr>>PAGE_SHIFT)); +- grant_tx_handle[pending_idx] = mop->handle; ++ netbk->grant_tx_handle[pending_idx] = mop->handle; + /* Had a previous error? Invalidate this fragment. */ + if (unlikely(err)) +- netif_idx_release(pending_idx); ++ netif_idx_release(netbk, pending_idx); + continue; + } + + /* Error on this fragment: respond to client with an error. */ + txp = &pending_tx_info[pending_idx].req; + make_tx_response(netif, txp, NETIF_RSP_ERROR); +- pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx; ++ index = MASK_PEND_IDX(netbk->pending_prod++); ++ netbk->pending_ring[index] = pending_idx; + netif_put(netif); + + /* Not the first error? Preceding frags already invalidated. */ +@@ -1166,10 +1173,10 @@ static int netbk_tx_check_mop(struct sk_ + + /* First error: invalidate header and preceding fragments. */ + pending_idx = *((u16 *)skb->data); +- netif_idx_release(pending_idx); ++ netif_idx_release(netbk, pending_idx); + for (j = start; j < i; j++) { + pending_idx = (unsigned long)shinfo->frags[i].page; +- netif_idx_release(pending_idx); ++ netif_idx_release(netbk, pending_idx); + } + + /* Remember the error: invalidate all subsequent fragments. */ +@@ -1180,7 +1187,7 @@ static int netbk_tx_check_mop(struct sk_ + return err; + } + +-static void netbk_fill_frags(struct sk_buff *skb) ++static void netbk_fill_frags(struct xen_netbk *netbk, struct sk_buff *skb) + { + struct skb_shared_info *shinfo = skb_shinfo(skb); + int nr_frags = shinfo->nr_frags; +@@ -1193,12 +1200,12 @@ static void netbk_fill_frags(struct sk_b + + pending_idx = (unsigned long)frag->page; + +- pending_inuse[pending_idx].alloc_time = jiffies; +- list_add_tail(&pending_inuse[pending_idx].list, +- &pending_inuse_head); ++ netbk->pending_inuse[pending_idx].alloc_time = jiffies; ++ list_add_tail(&netbk->pending_inuse[pending_idx].list, ++ &netbk->pending_inuse_head); + +- txp = &pending_tx_info[pending_idx].req; +- frag->page = mmap_pages[pending_idx]; ++ txp = &netbk->pending_tx_info[pending_idx].req; ++ frag->page = netbk->mmap_pages[pending_idx]; + frag->size = txp->size; + frag->page_offset = txp->offset; + +@@ -1260,9 +1267,9 @@ static int netbk_set_skb_gso(struct sk_b + } + + /* Called after netfront has transmitted */ +-static void net_tx_action(unsigned long unused) ++static void net_tx_action(unsigned long group) + { +- struct list_head *ent; ++ struct xen_netbk *netbk = &xen_netbk[group]; + struct sk_buff *skb; + netif_t *netif; + netif_tx_request_t txreq; +@@ -1274,15 +1281,15 @@ static void net_tx_action(unsigned long + unsigned int data_len; + int ret, work_to_do; + +- net_tx_action_dealloc(); ++ net_tx_action_dealloc(netbk); + +- mop = tx_map_ops; ++ mop = netbk->tx_map_ops; + BUILD_BUG_ON(MAX_SKB_FRAGS >= MAX_PENDING_REQS); +- while (((NR_PENDING_REQS + MAX_SKB_FRAGS) < MAX_PENDING_REQS) && +- !list_empty(&net_schedule_list)) { ++ while (((nr_pending_reqs(netbk) + MAX_SKB_FRAGS) < MAX_PENDING_REQS) && ++ !list_empty(&netbk->net_schedule_list)) { + /* Get a netif from the list with work to do. */ +- ent = net_schedule_list.next; +- netif = list_entry(ent, netif_t, list); ++ netif = list_first_entry(&netbk->net_schedule_list, ++ netif_t, list); + netif_get(netif); + remove_from_net_schedule_list(netif); + +@@ -1364,7 +1371,7 @@ static void net_tx_action(unsigned long + continue; + } + +- pending_idx = pending_ring[MASK_PEND_IDX(pending_cons)]; ++ pending_idx = netbk->pending_ring[MASK_PEND_IDX(netbk->pending_cons)]; + + data_len = (txreq.size > PKT_PROT_LEN && + ret < MAX_SKB_FRAGS) ? +@@ -1392,14 +1399,14 @@ static void net_tx_action(unsigned long + } + } + +- gnttab_set_map_op(mop, idx_to_kaddr(pending_idx), ++ gnttab_set_map_op(mop, idx_to_kaddr(netbk, pending_idx), + GNTMAP_host_map | GNTMAP_readonly, + txreq.gref, netif->domid); + mop++; + +- memcpy(&pending_tx_info[pending_idx].req, ++ memcpy(&netbk->pending_tx_info[pending_idx].req, + &txreq, sizeof(txreq)); +- pending_tx_info[pending_idx].netif = netif; ++ netbk->pending_tx_info[pending_idx].netif = netif; + *((u16 *)skb->data) = pending_idx; + + __skb_put(skb, data_len); +@@ -1414,20 +1421,20 @@ static void net_tx_action(unsigned long + skb_shinfo(skb)->frags[0].page = (void *)~0UL; + } + +- __skb_queue_tail(&tx_queue, skb); ++ __skb_queue_tail(&netbk->tx_queue, skb); + +- pending_cons++; ++ netbk->pending_cons++; + + mop = netbk_get_requests(netif, skb, txfrags, mop); + + netif->tx.req_cons = i; + netif_schedule_work(netif); + +- if ((mop - tx_map_ops) >= ARRAY_SIZE(tx_map_ops)) ++ if ((mop - netbk->tx_map_ops) >= ARRAY_SIZE(netbk->tx_map_ops)) + break; + } + +- if (mop == tx_map_ops) ++ if (mop == netbk->tx_map_ops) + goto out; + + /* NOTE: some maps may fail with GNTST_eagain, which could be successfully +@@ -1435,20 +1442,21 @@ static void net_tx_action(unsigned long + * req and let the frontend resend the relevant packet again. This is fine + * because it is unlikely that a network buffer will be paged out or shared, + * and therefore it is unlikely to fail with GNTST_eagain. */ +- ret = HYPERVISOR_grant_table_op( +- GNTTABOP_map_grant_ref, tx_map_ops, mop - tx_map_ops); ++ ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, ++ netbk->tx_map_ops, ++ mop - netbk->tx_map_ops); + BUG_ON(ret); + +- mop = tx_map_ops; +- while ((skb = __skb_dequeue(&tx_queue)) != NULL) { ++ mop = netbk->tx_map_ops; ++ while ((skb = __skb_dequeue(&netbk->tx_queue)) != NULL) { + netif_tx_request_t *txp; + + pending_idx = *((u16 *)skb->data); +- netif = pending_tx_info[pending_idx].netif; +- txp = &pending_tx_info[pending_idx].req; ++ netif = netbk->pending_tx_info[pending_idx].netif; ++ txp = &netbk->pending_tx_info[pending_idx].req; + + /* Check the remap error code. */ +- if (unlikely(netbk_tx_check_mop(skb, &mop))) { ++ if (unlikely(netbk_tx_check_mop(netbk, skb, &mop))) { + DPRINTK("netback grant failed.\n"); + skb_shinfo(skb)->nr_frags = 0; + kfree_skb(skb); +@@ -1457,7 +1465,7 @@ static void net_tx_action(unsigned long + + data_len = skb->len; + memcpy(skb->data, +- (void *)(idx_to_kaddr(pending_idx)|txp->offset), ++ (void *)(idx_to_kaddr(netbk, pending_idx)|txp->offset), + data_len); + if (data_len < txp->size) { + /* Append the packet payload as a fragment. */ +@@ -1465,7 +1473,7 @@ static void net_tx_action(unsigned long + txp->size -= data_len; + } else { + /* Schedule a response immediately. */ +- netif_idx_release(pending_idx); ++ netif_idx_release(netbk, pending_idx); + } + + /* +@@ -1481,7 +1489,7 @@ static void net_tx_action(unsigned long + } + skb->proto_csum_blank = !!(txp->flags & NETTXF_csum_blank); + +- netbk_fill_frags(skb); ++ netbk_fill_frags(netbk, skb); + + skb->dev = netif->dev; + skb->protocol = eth_type_trans(skb, skb->dev); +@@ -1502,36 +1510,39 @@ static void net_tx_action(unsigned long + + out: + if (netbk_copy_skb_mode == NETBK_DELAYED_COPY_SKB && +- !list_empty(&pending_inuse_head)) { ++ !list_empty(&netbk->pending_inuse_head)) { + struct netbk_tx_pending_inuse *oldest; + +- oldest = list_entry(pending_inuse_head.next, ++ oldest = list_entry(netbk->pending_inuse_head.next, + struct netbk_tx_pending_inuse, list); +- mod_timer(&netbk_tx_pending_timer, oldest->alloc_time + HZ); ++ mod_timer(&netbk->tx_pending_timer, oldest->alloc_time + HZ); + } + } + +-static void netif_idx_release(u16 pending_idx) ++static void netif_idx_release(struct xen_netbk *netbk, u16 pending_idx) + { +- static DEFINE_SPINLOCK(_lock); + unsigned long flags; + +- spin_lock_irqsave(&_lock, flags); +- dealloc_ring[MASK_PEND_IDX(dealloc_prod)] = pending_idx; ++ spin_lock_irqsave(&netbk->release_lock, flags); ++ netbk->dealloc_ring[MASK_PEND_IDX(netbk->dealloc_prod)] = pending_idx; + /* Sync with net_tx_action_dealloc: insert idx /then/ incr producer. */ + smp_wmb(); +- dealloc_prod++; +- spin_unlock_irqrestore(&_lock, flags); ++ netbk->dealloc_prod++; ++ spin_unlock_irqrestore(&netbk->release_lock, flags); + +- tasklet_schedule(&net_tx_tasklet); ++ tasklet_schedule(&netbk->net_tx_tasklet); + } + + static void netif_page_release(struct page *page, unsigned int order) + { +- int idx = netif_page_index(page); ++ unsigned int idx = netif_page_index(page); ++ unsigned int group = netif_page_group(page); ++ struct xen_netbk *netbk = &xen_netbk[group]; ++ + BUG_ON(order); +- BUG_ON(idx < 0); +- netif_idx_release(idx); ++ BUG_ON(group >= netbk_nr_groups || idx >= MAX_PENDING_REQS); ++ BUG_ON(netbk->mmap_pages[idx] != page); ++ netif_idx_release(netbk, idx); + } + + irqreturn_t netif_be_int(int irq, void *dev_id) +@@ -1539,7 +1550,7 @@ irqreturn_t netif_be_int(int irq, void * + netif_t *netif = dev_id; + + add_to_net_schedule_list_tail(netif); +- maybe_schedule_tx_action(); ++ maybe_schedule_tx_action(GET_GROUP_INDEX(netif)); + + if (netif_schedulable(netif) && !netbk_queue_full(netif)) + netif_wake_queue(netif->dev); +@@ -1605,29 +1616,35 @@ static irqreturn_t netif_be_dbg(int irq, + { + struct list_head *ent; + netif_t *netif; +- int i = 0; ++ unsigned int i = 0, group; + + printk(KERN_ALERT "netif_schedule_list:\n"); +- spin_lock_irq(&net_schedule_list_lock); + +- list_for_each (ent, &net_schedule_list) { +- netif = list_entry(ent, netif_t, list); +- printk(KERN_ALERT " %d: private(rx_req_cons=%08x " +- "rx_resp_prod=%08x\n", +- i, netif->rx.req_cons, netif->rx.rsp_prod_pvt); +- printk(KERN_ALERT " tx_req_cons=%08x tx_resp_prod=%08x)\n", +- netif->tx.req_cons, netif->tx.rsp_prod_pvt); +- printk(KERN_ALERT " shared(rx_req_prod=%08x " +- "rx_resp_prod=%08x\n", +- netif->rx.sring->req_prod, netif->rx.sring->rsp_prod); +- printk(KERN_ALERT " rx_event=%08x tx_req_prod=%08x\n", +- netif->rx.sring->rsp_event, netif->tx.sring->req_prod); +- printk(KERN_ALERT " tx_resp_prod=%08x, tx_event=%08x)\n", +- netif->tx.sring->rsp_prod, netif->tx.sring->rsp_event); +- i++; ++ for (group = 0; group < netbk_nr_groups; ++group) { ++ struct xen_netbk *netbk = &xen_netbk[group]; ++ ++ spin_lock_irq(&netbk->net_schedule_list_lock); ++ ++ list_for_each(ent, &netbk->net_schedule_list) { ++ netif = list_entry(ent, netif_t, list); ++ printk(KERN_ALERT " %d: private(rx_req_cons=%08x " ++ "rx_resp_prod=%08x\n", ++ i, netif->rx.req_cons, netif->rx.rsp_prod_pvt); ++ printk(KERN_ALERT " tx_req_cons=%08x tx_resp_prod=%08x)\n", ++ netif->tx.req_cons, netif->tx.rsp_prod_pvt); ++ printk(KERN_ALERT " shared(rx_req_prod=%08x " ++ "rx_resp_prod=%08x\n", ++ netif->rx.sring->req_prod, netif->rx.sring->rsp_prod); ++ printk(KERN_ALERT " rx_event=%08x tx_req_prod=%08x\n", ++ netif->rx.sring->rsp_event, netif->tx.sring->req_prod); ++ printk(KERN_ALERT " tx_resp_prod=%08x, tx_event=%08x)\n", ++ netif->tx.sring->rsp_prod, netif->tx.sring->rsp_event); ++ i++; ++ } ++ ++ spin_unlock_irq(&netbk->netbk->net_schedule_list_lock); + } + +- spin_unlock_irq(&net_schedule_list_lock); + printk(KERN_ALERT " ** End of netif_schedule_list **\n"); + + return IRQ_HANDLED; +@@ -1642,7 +1659,8 @@ static struct irqaction netif_be_dbg_act + + static int __init netback_init(void) + { +- int i; ++ unsigned int i, group; ++ int rc; + struct page *page; + + if (!is_running_on_xen()) +@@ -1651,37 +1669,55 @@ static int __init netback_init(void) + /* We can increase reservation by this much in net_rx_action(). */ + balloon_update_driver_allowance(NET_RX_RING_SIZE); + +- skb_queue_head_init(&rx_queue); +- skb_queue_head_init(&tx_queue); +- +- init_timer(&net_timer); +- net_timer.data = 0; +- net_timer.function = net_alarm; +- +- init_timer(&netbk_tx_pending_timer); +- netbk_tx_pending_timer.data = 0; +- netbk_tx_pending_timer.function = netbk_tx_pending_timeout; +- +- mmap_pages = alloc_empty_pages_and_pagevec(MAX_PENDING_REQS); +- if (mmap_pages == NULL) { +- printk("%s: out of memory\n", __FUNCTION__); ++ xen_netbk = __vmalloc(netbk_nr_groups * sizeof(*xen_netbk), ++ GFP_KERNEL|__GFP_HIGHMEM|__GFP_ZERO, PAGE_KERNEL); ++ if (!xen_netbk) { ++ printk(KERN_ALERT "%s: out of memory\n", __func__); + return -ENOMEM; + } + +- for (i = 0; i < MAX_PENDING_REQS; i++) { +- page = mmap_pages[i]; +- SetPageForeign(page, netif_page_release); +- netif_set_page_index(page, i); +- INIT_LIST_HEAD(&pending_inuse[i].list); +- } ++ for (group = 0; group < netbk_nr_groups; group++) { ++ struct xen_netbk *netbk = &xen_netbk[group]; + +- pending_cons = 0; +- pending_prod = MAX_PENDING_REQS; +- for (i = 0; i < MAX_PENDING_REQS; i++) +- pending_ring[i] = i; ++ tasklet_init(&netbk->net_tx_tasklet, net_tx_action, group); ++ tasklet_init(&netbk->net_rx_tasklet, net_rx_action, group); + +- spin_lock_init(&net_schedule_list_lock); +- INIT_LIST_HEAD(&net_schedule_list); ++ skb_queue_head_init(&netbk->rx_queue); ++ skb_queue_head_init(&netbk->tx_queue); ++ ++ netbk->mmap_pages = ++ alloc_empty_pages_and_pagevec(MAX_PENDING_REQS); ++ if (netbk->mmap_pages == NULL) { ++ printk(KERN_ALERT "%s: out of memory\n", __func__); ++ rc = -ENOMEM; ++ goto failed_init; ++ } ++ ++ init_timer(&netbk->net_timer); ++ netbk->net_timer.data = group; ++ netbk->net_timer.function = net_alarm; ++ ++ init_timer(&netbk->tx_pending_timer); ++ netbk->tx_pending_timer.data = group; ++ netbk->tx_pending_timer.function = ++ netbk_tx_pending_timeout; ++ ++ netbk->pending_prod = MAX_PENDING_REQS; ++ ++ INIT_LIST_HEAD(&netbk->pending_inuse_head); ++ INIT_LIST_HEAD(&netbk->net_schedule_list); ++ ++ spin_lock_init(&netbk->net_schedule_list_lock); ++ spin_lock_init(&netbk->release_lock); ++ ++ for (i = 0; i < MAX_PENDING_REQS; i++) { ++ page = netbk->mmap_pages[i]; ++ SetPageForeign(page, netif_page_release); ++ netif_set_page_ext(page, group, i); ++ netbk->pending_ring[i] = i; ++ INIT_LIST_HEAD(&netbk->pending_inuse[i].list); ++ } ++ } + + netbk_copy_skb_mode = NETBK_DONT_COPY_SKB; + if (MODPARM_copy_skb) { +@@ -1703,6 +1739,19 @@ static int __init netback_init(void) + #endif + + return 0; ++ ++failed_init: ++ while (group-- > 0) { ++ struct xen_netbk *netbk = &xen_netbk[group]; ++ ++ free_empty_pages_and_pagevec(netbk->mmap_pages, ++ MAX_PENDING_REQS); ++ del_timer(&netbk->tx_pending_timer); ++ del_timer(&netbk->net_timer); ++ } ++ vfree(xen_netbk); ++ ++ return rc; + } + + module_init(netback_init); diff --git a/patches.xen/xen-netback-kernel-threads b/patches.xen/xen-netback-kernel-threads new file mode 100644 index 0000000..319814e --- /dev/null +++ b/patches.xen/xen-netback-kernel-threads @@ -0,0 +1,286 @@ +From: Dongxiao Xu +Subject: [PATCH 3/3] Use Kernel thread to replace the tasklet. +Patch-mainline: n/a + + Kernel thread has more control over QoS, and could improve + dom0's userspace responseness. + +Signed-off-by: Dongxiao Xu + +Subject: xen: ensure locking gnttab_copy_grant_page is safe against interrupts. + +Now that netback processing occurs in a thread instead of a tasklet +gnttab_copy_grant_page needs to be safe against interrupts. + +The code is currently commented out in this tree but on 2.6.18 we observed a +deadlock where the netback thread called gnttab_copy_grant_page, locked +gnttab_dma_lock for writing, was interrupted and on return from interrupt the +network stack's TX tasklet ended up calling __gnttab_dma_map_page via the +hardware driver->swiotlb and tries to take gnttab_dma_lock for reading. + +Signed-off-by: Ian Campbell +Cc: Jeremy Fitzhardinge # +Cc: "Xu, Dongxiao" + +jb: changed write_seq{,un}lock_irq() to write_seq{,un}lock_bh(), and + made the use of kernel threads optional (but default) +Acked-by: jbeulich@novell.com + +--- head-2010-04-29.orig/drivers/xen/core/gnttab.c 2010-04-15 11:42:34.000000000 +0200 ++++ head-2010-04-29/drivers/xen/core/gnttab.c 2010-04-15 11:44:26.000000000 +0200 +@@ -554,14 +554,14 @@ int gnttab_copy_grant_page(grant_ref_t r + mfn = pfn_to_mfn(pfn); + new_mfn = virt_to_mfn(new_addr); + +- write_seqlock(&gnttab_dma_lock); ++ write_seqlock_bh(&gnttab_dma_lock); + + /* Make seq visible before checking page_mapped. */ + smp_mb(); + + /* Has the page been DMA-mapped? */ + if (unlikely(page_mapped(page))) { +- write_sequnlock(&gnttab_dma_lock); ++ write_sequnlock_bh(&gnttab_dma_lock); + put_page(new_page); + err = -EBUSY; + goto out; +@@ -578,7 +578,7 @@ int gnttab_copy_grant_page(grant_ref_t r + BUG_ON(err); + BUG_ON(unmap.status); + +- write_sequnlock(&gnttab_dma_lock); ++ write_sequnlock_bh(&gnttab_dma_lock); + + if (!xen_feature(XENFEAT_auto_translated_physmap)) { + set_phys_to_machine(page_to_pfn(new_page), INVALID_P2M_ENTRY); +--- head-2010-04-29.orig/drivers/xen/netback/common.h 2010-04-30 11:32:08.000000000 +0200 ++++ head-2010-04-29/drivers/xen/netback/common.h 2010-04-30 11:32:26.000000000 +0200 +@@ -239,8 +239,16 @@ struct netbk_tx_pending_inuse { + #define MAX_MFN_ALLOC 64 + + struct xen_netbk { +- struct tasklet_struct net_tx_tasklet; +- struct tasklet_struct net_rx_tasklet; ++ union { ++ struct { ++ struct tasklet_struct net_tx_tasklet; ++ struct tasklet_struct net_rx_tasklet; ++ }; ++ struct { ++ wait_queue_head_t netbk_action_wq; ++ struct task_struct *task; ++ }; ++ }; + + struct sk_buff_head rx_queue; + struct sk_buff_head tx_queue; +--- head-2010-04-29.orig/drivers/xen/netback/netback.c 2010-04-30 11:49:27.000000000 +0200 ++++ head-2010-04-29/drivers/xen/netback/netback.c 2010-04-30 11:49:32.000000000 +0200 +@@ -35,6 +35,7 @@ + */ + + #include "common.h" ++#include + #include + #include + #include +@@ -43,6 +44,8 @@ + + struct xen_netbk *__read_mostly xen_netbk; + unsigned int __read_mostly netbk_nr_groups; ++static bool __read_mostly use_kthreads = true; ++static bool __initdata bind_threads; + + #define GET_GROUP_INDEX(netif) ((netif)->group) + +@@ -127,7 +130,11 @@ static int MODPARM_permute_returns = 0; + module_param_named(permute_returns, MODPARM_permute_returns, bool, S_IRUSR|S_IWUSR); + MODULE_PARM_DESC(permute_returns, "Randomly permute the order in which TX responses are sent to the frontend"); + module_param_named(groups, netbk_nr_groups, uint, 0); +-MODULE_PARM_DESC(groups, "Specify the number of tasklet pairs to use"); ++MODULE_PARM_DESC(groups, "Specify the number of tasklet pairs/threads to use"); ++module_param_named(tasklets, use_kthreads, invbool, 0); ++MODULE_PARM_DESC(tasklets, "Use tasklets instead of kernel threads"); ++module_param_named(bind, bind_threads, bool, 0); ++MODULE_PARM_DESC(bind, "Bind kernel threads to (v)CPUs"); + + int netbk_copy_skb_mode; + +@@ -164,8 +171,12 @@ static inline void maybe_schedule_tx_act + + smp_mb(); + if ((nr_pending_reqs(netbk) < (MAX_PENDING_REQS/2)) && +- !list_empty(&netbk->net_schedule_list)) +- tasklet_schedule(&netbk->net_tx_tasklet); ++ !list_empty(&netbk->net_schedule_list)) { ++ if (use_kthreads) ++ wake_up(&netbk->netbk_action_wq); ++ else ++ tasklet_schedule(&netbk->net_tx_tasklet); ++ } + } + + static struct sk_buff *netbk_copy_skb(struct sk_buff *skb) +@@ -326,7 +337,10 @@ int netif_be_start_xmit(struct sk_buff * + + netbk = &xen_netbk[GET_GROUP_INDEX(netif)]; + skb_queue_tail(&netbk->rx_queue, skb); +- tasklet_schedule(&netbk->net_rx_tasklet); ++ if (use_kthreads) ++ wake_up(&netbk->netbk_action_wq); ++ else ++ tasklet_schedule(&netbk->net_rx_tasklet); + + return NETDEV_TX_OK; + +@@ -779,8 +793,12 @@ static void net_rx_action(unsigned long + + /* More work to do? */ + if (!skb_queue_empty(&netbk->rx_queue) && +- !timer_pending(&netbk->net_timer)) +- tasklet_schedule(&netbk->net_rx_tasklet); ++ !timer_pending(&netbk->net_timer)) { ++ if (use_kthreads) ++ wake_up(&netbk->netbk_action_wq); ++ else ++ tasklet_schedule(&netbk->net_rx_tasklet); ++ } + #if 0 + else + xen_network_done_notify(); +@@ -789,12 +807,18 @@ static void net_rx_action(unsigned long + + static void net_alarm(unsigned long group) + { +- tasklet_schedule(&xen_netbk[group].net_rx_tasklet); ++ if (use_kthreads) ++ wake_up(&xen_netbk[group].netbk_action_wq); ++ else ++ tasklet_schedule(&xen_netbk[group].net_rx_tasklet); + } + + static void netbk_tx_pending_timeout(unsigned long group) + { +- tasklet_schedule(&xen_netbk[group].net_tx_tasklet); ++ if (use_kthreads) ++ wake_up(&xen_netbk[group].netbk_action_wq); ++ else ++ tasklet_schedule(&xen_netbk[group].net_tx_tasklet); + } + + struct net_device_stats *netif_be_get_stats(struct net_device *dev) +@@ -1506,7 +1530,10 @@ static void net_tx_action(unsigned long + continue; + } + +- netif_rx(skb); ++ if (use_kthreads) ++ netif_rx_ni(skb); ++ else ++ netif_rx(skb); + netif->dev->last_rx = jiffies; + } + +@@ -1532,7 +1559,10 @@ static void netif_idx_release(struct xen + netbk->dealloc_prod++; + spin_unlock_irqrestore(&netbk->release_lock, flags); + +- tasklet_schedule(&netbk->net_tx_tasklet); ++ if (use_kthreads) ++ wake_up(&netbk->netbk_action_wq); ++ else ++ tasklet_schedule(&netbk->net_tx_tasklet); + } + + static void netif_page_release(struct page *page, unsigned int order) +@@ -1670,6 +1700,46 @@ static struct irqaction netif_be_dbg_act + }; + #endif + ++static inline int rx_work_todo(struct xen_netbk *netbk) ++{ ++ return !skb_queue_empty(&netbk->rx_queue); ++} ++ ++static inline int tx_work_todo(struct xen_netbk *netbk) ++{ ++ if (netbk->dealloc_cons != netbk->dealloc_prod) ++ return 1; ++ ++ if (nr_pending_reqs(netbk) + MAX_SKB_FRAGS < MAX_PENDING_REQS && ++ !list_empty(&netbk->net_schedule_list)) ++ return 1; ++ ++ return 0; ++} ++ ++static int netbk_action_thread(void *index) ++{ ++ unsigned long group = (unsigned long)index; ++ struct xen_netbk *netbk = &xen_netbk[group]; ++ ++ while (!kthread_should_stop()) { ++ wait_event_interruptible(netbk->netbk_action_wq, ++ rx_work_todo(netbk) || ++ tx_work_todo(netbk) || ++ kthread_should_stop()); ++ cond_resched(); ++ ++ if (rx_work_todo(netbk)) ++ net_rx_action(group); ++ ++ if (tx_work_todo(netbk)) ++ net_tx_action(group); ++ } ++ ++ return 0; ++} ++ ++ + static int __init netback_init(void) + { + unsigned int i, group; +@@ -1697,8 +1767,26 @@ static int __init netback_init(void) + for (group = 0; group < netbk_nr_groups; group++) { + struct xen_netbk *netbk = &xen_netbk[group]; + +- tasklet_init(&netbk->net_tx_tasklet, net_tx_action, group); +- tasklet_init(&netbk->net_rx_tasklet, net_rx_action, group); ++ if (use_kthreads) { ++ init_waitqueue_head(&netbk->netbk_action_wq); ++ netbk->task = kthread_create(netbk_action_thread, ++ (void *)(long)group, ++ "netback/%u", group); ++ ++ if (!IS_ERR(netbk->task)) { ++ if (bind_threads) ++ kthread_bind(netbk->task, group); ++ wake_up_process(netbk->task); ++ } else { ++ printk(KERN_ALERT ++ "kthread_create() fails at netback\n"); ++ rc = PTR_ERR(netbk->task); ++ goto failed_init; ++ } ++ } else { ++ tasklet_init(&netbk->net_tx_tasklet, net_tx_action, group); ++ tasklet_init(&netbk->net_rx_tasklet, net_rx_action, group); ++ } + + skb_queue_head_init(&netbk->rx_queue); + skb_queue_head_init(&netbk->tx_queue); +@@ -1762,8 +1850,11 @@ failed_init: + while (group-- > 0) { + struct xen_netbk *netbk = &xen_netbk[group]; + +- free_empty_pages_and_pagevec(netbk->mmap_pages, +- MAX_PENDING_REQS); ++ if (use_kthreads && netbk->task && !IS_ERR(netbk->task)) ++ kthread_stop(netbk->task); ++ if (netbk->mmap_pages) ++ free_empty_pages_and_pagevec(netbk->mmap_pages, ++ MAX_PENDING_REQS); + del_timer(&netbk->tx_pending_timer); + del_timer(&netbk->net_timer); + } diff --git a/patches.xen/xen-netback-multiple-tasklets b/patches.xen/xen-netback-multiple-tasklets new file mode 100644 index 0000000..d13758f --- /dev/null +++ b/patches.xen/xen-netback-multiple-tasklets @@ -0,0 +1,155 @@ +From: Dongxiao Xu +Subject: [PATCH 2/3] Netback: Multiple tasklets support. +Patch-mainline: n/a + + Now netback uses one pair of tasklets for Tx/Rx data transaction. Netback + tasklet could only run at one CPU at a time, and it is used to serve all the + netfronts. Therefore it has become a performance bottle neck. This patch is to + use multiple tasklet pairs to replace the current single pair in dom0. + + Assuming that Dom0 has CPUNR VCPUs, we define CPUNR kinds of tasklets pair + (CPUNR for Tx, and CPUNR for Rx). Each pare of tasklets serve specific group of + netfronts. Also for those global and static variables, we duplicated them for + each group in order to avoid the spinlock. + +Signed-off-by: Dongxiao Xu + +jb: some cleanups +Acked-by: jbeulich@novell.com + +--- head-2010-04-29.orig/drivers/xen/netback/common.h 2010-04-30 11:11:33.000000000 +0200 ++++ head-2010-04-29/drivers/xen/netback/common.h 2010-04-30 11:32:08.000000000 +0200 +@@ -58,6 +58,7 @@ + typedef struct netif_st { + /* Unique identifier for this interface. */ + domid_t domid; ++ unsigned int group; + unsigned int handle; + + u8 fe_dev_addr[6]; +@@ -260,6 +261,7 @@ struct xen_netbk { + + struct page **mmap_pages; + ++ atomic_t nr_groups; + unsigned int alloc_index; + + struct pending_tx_info pending_tx_info[MAX_PENDING_REQS]; +@@ -287,4 +289,8 @@ struct xen_netbk { + + unsigned long mfn_list[MAX_MFN_ALLOC]; + }; ++ ++extern struct xen_netbk *xen_netbk; ++extern unsigned int netbk_nr_groups; ++ + #endif /* __NETIF__BACKEND__COMMON_H__ */ +--- head-2010-04-29.orig/drivers/xen/netback/interface.c 2010-01-26 09:03:24.000000000 +0100 ++++ head-2010-04-29/drivers/xen/netback/interface.c 2010-04-30 10:42:29.000000000 +0200 +@@ -54,14 +54,36 @@ module_param_named(queue_length, netbk_q + + static void __netif_up(netif_t *netif) + { ++ unsigned int group = 0; ++ unsigned int min_groups = atomic_read(&xen_netbk[0].nr_groups); ++ unsigned int i; ++ ++ /* Find the list which contains least number of domains. */ ++ for (i = 1; i < netbk_nr_groups; i++) { ++ unsigned int nr_groups = atomic_read(&xen_netbk[i].nr_groups); ++ ++ if (nr_groups < min_groups) { ++ group = i; ++ min_groups = nr_groups; ++ } ++ } ++ ++ atomic_inc(&xen_netbk[group].nr_groups); ++ netif->group = group; ++ + enable_irq(netif->irq); + netif_schedule_work(netif); + } + + static void __netif_down(netif_t *netif) + { ++ struct xen_netbk *netbk = xen_netbk + netif->group; ++ + disable_irq(netif->irq); + netif_deschedule_work(netif); ++ ++ netif->group = UINT_MAX; ++ atomic_dec(&netbk->nr_groups); + } + + static int net_open(struct net_device *dev) +@@ -207,6 +229,7 @@ netif_t *netif_alloc(struct device *pare + netif = netdev_priv(dev); + memset(netif, 0, sizeof(*netif)); + netif->domid = domid; ++ netif->group = UINT_MAX; + netif->handle = handle; + atomic_set(&netif->refcnt, 1); + init_waitqueue_head(&netif->waiting_to_free); +--- head-2010-04-29.orig/drivers/xen/netback/netback.c 2010-04-30 11:49:12.000000000 +0200 ++++ head-2010-04-29/drivers/xen/netback/netback.c 2010-04-30 11:49:27.000000000 +0200 +@@ -41,10 +41,10 @@ + + /*define NETBE_DEBUG_INTERRUPT*/ + +-static struct xen_netbk *__read_mostly xen_netbk; +-static const unsigned int __read_mostly netbk_nr_groups = 1; ++struct xen_netbk *__read_mostly xen_netbk; ++unsigned int __read_mostly netbk_nr_groups; + +-#define GET_GROUP_INDEX(netif) (0) ++#define GET_GROUP_INDEX(netif) ((netif)->group) + + static void netif_idx_release(struct xen_netbk *, u16 pending_idx); + static void make_tx_response(netif_t *netif, +@@ -126,6 +126,8 @@ MODULE_PARM_DESC(copy_skb, "Copy data re + static int MODPARM_permute_returns = 0; + module_param_named(permute_returns, MODPARM_permute_returns, bool, S_IRUSR|S_IWUSR); + MODULE_PARM_DESC(permute_returns, "Randomly permute the order in which TX responses are sent to the frontend"); ++module_param_named(groups, netbk_nr_groups, uint, 0); ++MODULE_PARM_DESC(groups, "Specify the number of tasklet pairs to use"); + + int netbk_copy_skb_mode; + +@@ -1548,9 +1550,20 @@ static void netif_page_release(struct pa + irqreturn_t netif_be_int(int irq, void *dev_id) + { + netif_t *netif = dev_id; ++ unsigned int group = GET_GROUP_INDEX(netif); ++ ++ if (unlikely(group >= netbk_nr_groups)) { ++ /* ++ * Short of having a way to bind the IRQ in disabled mode ++ * (IRQ_NOAUTOEN), we have to ignore the first invocation(s) ++ * (before we got assigned to a group). ++ */ ++ BUG_ON(group != UINT_MAX); ++ return IRQ_HANDLED; ++ } + + add_to_net_schedule_list_tail(netif); +- maybe_schedule_tx_action(GET_GROUP_INDEX(netif)); ++ maybe_schedule_tx_action(group); + + if (netif_schedulable(netif) && !netbk_queue_full(netif)) + netif_wake_queue(netif->dev); +@@ -1666,8 +1679,13 @@ static int __init netback_init(void) + if (!is_running_on_xen()) + return -ENODEV; + ++ if (!netbk_nr_groups) ++ netbk_nr_groups = (num_online_cpus() + 1) / 2; ++ if (netbk_nr_groups > MAX_GROUPS) ++ netbk_nr_groups = MAX_GROUPS; ++ + /* We can increase reservation by this much in net_rx_action(). */ +- balloon_update_driver_allowance(NET_RX_RING_SIZE); ++ balloon_update_driver_allowance(netbk_nr_groups * NET_RX_RING_SIZE); + + xen_netbk = __vmalloc(netbk_nr_groups * sizeof(*xen_netbk), + GFP_KERNEL|__GFP_HIGHMEM|__GFP_ZERO, PAGE_KERNEL); diff --git a/patches.xen/xen-netback-notify-multi b/patches.xen/xen-netback-notify-multi new file mode 100644 index 0000000..8dc539a --- /dev/null +++ b/patches.xen/xen-netback-notify-multi @@ -0,0 +1,85 @@ +From: jbeulich@novell.com +Subject: netback: use multicall for send multiple notifications +Patch-mainline: obsolete + +This also does a small fairness improvement since now notifications +get sent in the order requests came in rather than in the inverse one. + +--- head-2010-04-15.orig/drivers/xen/core/evtchn.c 2010-04-23 15:20:36.000000000 +0200 ++++ head-2010-04-15/drivers/xen/core/evtchn.c 2010-04-23 15:20:52.000000000 +0200 +@@ -1336,6 +1336,21 @@ void notify_remote_via_irq(int irq) + } + EXPORT_SYMBOL_GPL(notify_remote_via_irq); + ++int multi_notify_remote_via_irq(multicall_entry_t *mcl, int irq) ++{ ++ int evtchn = evtchn_from_irq(irq); ++ ++ BUG_ON(type_from_irq(irq) == IRQT_VIRQ); ++ BUG_IF_IPI(irq); ++ ++ if (!VALID_EVTCHN(evtchn)) ++ return -EINVAL; ++ ++ multi_notify_remote_via_evtchn(mcl, evtchn); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(multi_notify_remote_via_irq); ++ + int irq_to_evtchn_port(int irq) + { + BUG_IF_VIRQ_PER_CPU(irq); +--- head-2010-04-15.orig/drivers/xen/netback/netback.c 2010-01-04 13:31:44.000000000 +0100 ++++ head-2010-04-15/drivers/xen/netback/netback.c 2010-01-04 13:31:57.000000000 +0100 +@@ -778,10 +778,20 @@ static void net_rx_action(unsigned long + npo.meta_cons += nr_frags + 1; + } + +- while (notify_nr != 0) { +- irq = notify_list[--notify_nr]; ++ if (notify_nr == 1) { ++ irq = *notify_list; + __clear_bit(irq, rx_notify); + notify_remote_via_irq(irq + DYNIRQ_BASE); ++ } else { ++ for (count = ret = 0; ret < notify_nr; ++ret) { ++ irq = notify_list[ret]; ++ __clear_bit(irq, rx_notify); ++ if (!multi_notify_remote_via_irq(rx_mcl + count, ++ irq + DYNIRQ_BASE)) ++ ++count; ++ } ++ if (HYPERVISOR_multicall(rx_mcl, count)) ++ BUG(); + } + + /* More work to do? */ +--- head-2010-04-15.orig/include/xen/evtchn.h 2010-03-31 14:11:09.000000000 +0200 ++++ head-2010-04-15/include/xen/evtchn.h 2010-03-31 14:42:45.000000000 +0200 +@@ -192,6 +192,18 @@ static inline void notify_remote_via_evt + VOID(HYPERVISOR_event_channel_op(EVTCHNOP_send, &send)); + } + ++static inline void ++multi_notify_remote_via_evtchn(multicall_entry_t *mcl, int port) ++{ ++ struct evtchn_send *send = (void *)(mcl->args + 2); ++ ++ BUILD_BUG_ON(sizeof(*send) > sizeof(mcl->args) - 2 * sizeof(*mcl->args)); ++ send->port = port; ++ mcl->op = __HYPERVISOR_event_channel_op; ++ mcl->args[0] = EVTCHNOP_send; ++ mcl->args[1] = (unsigned long)send; ++} ++ + /* Clear an irq's pending state, in preparation for polling on it. */ + void xen_clear_irq_pending(int irq); + +@@ -210,6 +222,7 @@ void xen_poll_irq(int irq); + * by bind_*_to_irqhandler(). + */ + void notify_remote_via_irq(int irq); ++int multi_notify_remote_via_irq(multicall_entry_t *, int irq); + int irq_to_evtchn_port(int irq); + + #if defined(CONFIG_SMP) && !defined(MODULE) && defined(CONFIG_X86) diff --git a/patches.xen/xen-netback-nr-irqs b/patches.xen/xen-netback-nr-irqs new file mode 100644 index 0000000..5714739 --- /dev/null +++ b/patches.xen/xen-netback-nr-irqs @@ -0,0 +1,61 @@ +From: jbeulich@novell.com +Subject: netback: reduce overhead of IRQ recording +Patch-mainline: obsolete + +Since both NR_PIRQS and NR_DYNIRQS are no longer hardcoded, the +(memory) overhead of tracking which ones to send notifications to can +be pretty unbounded. Also, store the dynirq rather than the raw irq +to push up the limit where the type of notify_list needs to become +'int' rather than 'u16'. + +--- head-2010-01-19.orig/drivers/xen/netback/interface.c 2010-01-26 08:58:19.000000000 +0100 ++++ head-2010-01-19/drivers/xen/netback/interface.c 2010-01-26 09:03:24.000000000 +0100 +@@ -343,6 +343,7 @@ int netif_map(netif_t *netif, unsigned l + netif->dev->name, netif); + if (err < 0) + goto err_hypervisor; ++ BUG_ON(err < DYNIRQ_BASE || err >= DYNIRQ_BASE + NR_DYNIRQS); + netif->irq = err; + disable_irq(netif->irq); + +--- head-2010-01-19.orig/drivers/xen/netback/netback.c 2010-01-04 13:31:38.000000000 +0100 ++++ head-2010-01-19/drivers/xen/netback/netback.c 2010-01-04 13:31:44.000000000 +0100 +@@ -590,8 +590,12 @@ static void net_rx_action(unsigned long + static mmu_update_t rx_mmu[NET_RX_RING_SIZE]; + static gnttab_transfer_t grant_trans_op[NET_RX_RING_SIZE]; + static gnttab_copy_t grant_copy_op[NET_RX_RING_SIZE]; +- static unsigned char rx_notify[NR_IRQS]; ++ static DECLARE_BITMAP(rx_notify, NR_DYNIRQS); ++#if NR_DYNIRQS <= 0x10000 + static u16 notify_list[NET_RX_RING_SIZE]; ++#else ++ static int notify_list[NET_RX_RING_SIZE]; ++#endif + static struct netbk_rx_meta meta[NET_RX_RING_SIZE]; + + struct netrx_pending_operations npo = { +@@ -749,11 +753,9 @@ static void net_rx_action(unsigned long + nr_frags); + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netif->rx, ret); +- irq = netif->irq; +- if (ret && !rx_notify[irq]) { +- rx_notify[irq] = 1; ++ irq = netif->irq - DYNIRQ_BASE; ++ if (ret && !__test_and_set_bit(irq, rx_notify)) + notify_list[notify_nr++] = irq; +- } + + if (netif_queue_stopped(netif->dev) && + netif_schedulable(netif) && +@@ -778,8 +780,8 @@ static void net_rx_action(unsigned long + + while (notify_nr != 0) { + irq = notify_list[--notify_nr]; +- rx_notify[irq] = 0; +- notify_remote_via_irq(irq); ++ __clear_bit(irq, rx_notify); ++ notify_remote_via_irq(irq + DYNIRQ_BASE); + } + + /* More work to do? */ diff --git a/patches.xen/xen-netfront-ethtool b/patches.xen/xen-netfront-ethtool new file mode 100644 index 0000000..368971c --- /dev/null +++ b/patches.xen/xen-netfront-ethtool @@ -0,0 +1,31 @@ +From: ksrinivasan@novell.com +Subject: netfront: ethtool -i does not return info about xennet driver +Patch-mainline: n/a +References: bnc#591179 + +Signed-off-by: K. Y. Srinivasan + +--- linux.orig/drivers/xen/netfront/netfront.c 2010-02-08 06:59:48.000000000 -0700 ++++ linux/drivers/xen/netfront/netfront.c 2010-03-26 17:18:30.000000000 -0600 +@@ -1766,6 +1766,13 @@ static void xennet_set_features(struct n + xennet_set_tso(dev, 1); + } + ++static void netfront_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *info) ++{ ++ strcpy(info->driver, "netfront"); ++ strcpy(info->bus_info, dev_name(dev->dev.parent)); ++} ++ + static int network_connect(struct net_device *dev) + { + struct netfront_info *np = netdev_priv(dev); +@@ -1874,6 +1881,7 @@ static void netif_uninit(struct net_devi + + static const struct ethtool_ops network_ethtool_ops = + { ++ .get_drvinfo = netfront_get_drvinfo, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, + .get_sg = ethtool_op_get_sg, diff --git a/patches.xen/xen-no-reboot-vector b/patches.xen/xen-no-reboot-vector new file mode 100644 index 0000000..1c73cb9 --- /dev/null +++ b/patches.xen/xen-no-reboot-vector @@ -0,0 +1,148 @@ +From: jbeulich@novell.com +Subject: eliminate REBOOT_VECTOR +Patch-mainline: n/a + +We can do without it, and can that way save one event channel per CPU +(i.e. a significant number when having many CPUs, given that there are +only 1024/4096 of them on 32-/64-bit). + +--- head-2010-04-15.orig/arch/x86/include/asm/hw_irq.h 2010-03-24 16:00:05.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/asm/hw_irq.h 2010-03-30 17:14:57.000000000 +0200 +@@ -137,7 +137,6 @@ extern asmlinkage void smp_invalidate_in + extern irqreturn_t smp_reschedule_interrupt(int, void *); + extern irqreturn_t smp_call_function_interrupt(int, void *); + extern irqreturn_t smp_call_function_single_interrupt(int, void *); +-extern irqreturn_t smp_reboot_interrupt(int, void *); + #endif + #endif + +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/irq_vectors.h 2010-03-29 18:11:31.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/irq_vectors.h 2010-03-30 17:15:14.000000000 +0200 +@@ -12,8 +12,7 @@ + #define CALL_FUNCTION_VECTOR 1 + #define CALL_FUNC_SINGLE_VECTOR 2 + #define SPIN_UNLOCK_VECTOR 3 +-#define REBOOT_VECTOR 4 +-#define NR_IPIS 5 ++#define NR_IPIS 4 + + /* + * The maximum number of vectors supported by i386 processors +--- head-2010-04-15.orig/arch/x86/kernel/smp-xen.c 2010-04-15 10:48:32.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/smp-xen.c 2010-04-15 11:43:22.000000000 +0200 +@@ -29,6 +29,9 @@ + #include + #include + #include ++ ++static unsigned int __read_mostly reboot = NR_CPUS; ++ + /* + * Some notes on x86 processor bugs affecting SMP operation: + * +@@ -132,19 +135,6 @@ void xen_send_call_func_ipi(const struct + xen_send_IPI_mask_allbutself(mask, CALL_FUNCTION_VECTOR); + } + +-/* +- * this function calls the 'stop' function on all other CPUs in the system. +- */ +- +-irqreturn_t smp_reboot_interrupt(int irq, void *dev_id) +-{ +- irq_enter(); +- stop_this_cpu(NULL); +- irq_exit(); +- +- return IRQ_HANDLED; +-} +- + void xen_smp_send_stop(void) + { + unsigned long flags; +@@ -159,8 +149,10 @@ void xen_smp_send_stop(void) + * (this implies we cannot stop CPUs spinning with irq off + * currently) + */ ++ reboot = raw_smp_processor_id(); ++ wmb(); + if (num_online_cpus() > 1) { +- xen_send_IPI_allbutself(REBOOT_VECTOR); ++ xen_send_IPI_allbutself(RESCHEDULE_VECTOR); + + /* Don't wait longer than a second */ + wait = USEC_PER_SEC; +@@ -180,7 +172,13 @@ void xen_smp_send_stop(void) + */ + irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id) + { +- inc_irq_stat(irq_resched_count); ++ if (likely(reboot >= NR_CPUS) || reboot == raw_smp_processor_id()) ++ inc_irq_stat(irq_resched_count); ++ else { ++ irq_enter(); ++ stop_this_cpu(NULL); ++ irq_exit(); ++ } + return IRQ_HANDLED; + } + +--- head-2010-04-15.orig/drivers/xen/core/smpboot.c 2010-03-25 14:39:15.000000000 +0100 ++++ head-2010-04-15/drivers/xen/core/smpboot.c 2010-04-15 11:43:29.000000000 +0200 +@@ -44,7 +44,6 @@ EXPORT_PER_CPU_SYMBOL(cpu_info); + static int __read_mostly resched_irq = -1; + static int __read_mostly callfunc_irq = -1; + static int __read_mostly call1func_irq = -1; +-static int __read_mostly reboot_irq = -1; + + #ifdef CONFIG_X86_LOCAL_APIC + #define set_cpu_to_apicid(cpu, apicid) (per_cpu(x86_cpu_to_apicid, cpu) = (apicid)) +@@ -118,10 +117,6 @@ static int __cpuinit xen_smp_intr_init(u + .handler = smp_call_function_single_interrupt, + .flags = IRQF_DISABLED, + .name = "call1func" +- }, reboot_action = { +- .handler = smp_reboot_interrupt, +- .flags = IRQF_DISABLED, +- .name = "reboot" + }; + int rc; + +@@ -155,19 +150,9 @@ static int __cpuinit xen_smp_intr_init(u + else + BUG_ON(call1func_irq != rc); + +- rc = bind_ipi_to_irqaction(REBOOT_VECTOR, +- cpu, +- &reboot_action); +- if (rc < 0) +- goto unbind_call1; +- if (reboot_irq < 0) +- reboot_irq = rc; +- else +- BUG_ON(reboot_irq != rc); +- + rc = xen_spinlock_init(cpu); + if (rc < 0) +- goto unbind_reboot; ++ goto unbind_call1; + + if ((cpu != 0) && ((rc = local_setup_timer(cpu)) != 0)) + goto fail; +@@ -176,8 +161,6 @@ static int __cpuinit xen_smp_intr_init(u + + fail: + xen_spinlock_cleanup(cpu); +- unbind_reboot: +- unbind_from_per_cpu_irq(reboot_irq, cpu, NULL); + unbind_call1: + unbind_from_per_cpu_irq(call1func_irq, cpu, NULL); + unbind_call: +@@ -196,7 +179,6 @@ static void __cpuinit xen_smp_intr_exit( + unbind_from_per_cpu_irq(resched_irq, cpu, NULL); + unbind_from_per_cpu_irq(callfunc_irq, cpu, NULL); + unbind_from_per_cpu_irq(call1func_irq, cpu, NULL); +- unbind_from_per_cpu_irq(reboot_irq, cpu, NULL); + xen_spinlock_cleanup(cpu); + } + #endif diff --git a/patches.xen/xen-op-packet b/patches.xen/xen-op-packet new file mode 100644 index 0000000..b53c70c --- /dev/null +++ b/patches.xen/xen-op-packet @@ -0,0 +1,190 @@ +From: plc@novell.com +Subject: add support for new operation type BLKIF_OP_PACKET +Patch-mainline: n/a +References: fate#300964 + +--- head-2010-04-29.orig/drivers/xen/blkback/blkback.c 2010-03-24 15:25:21.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blkback/blkback.c 2010-03-25 14:38:05.000000000 +0100 +@@ -195,13 +195,15 @@ static void fast_flush_area(pending_req_ + + static void print_stats(blkif_t *blkif) + { +- printk(KERN_DEBUG "%s: oo %3d | rd %4d | wr %4d | br %4d\n", ++ printk(KERN_DEBUG "%s: oo %3d | rd %4d | wr %4d | br %4d | pk %4d\n", + current->comm, blkif->st_oo_req, +- blkif->st_rd_req, blkif->st_wr_req, blkif->st_br_req); ++ blkif->st_rd_req, blkif->st_wr_req, blkif->st_br_req, ++ blkif->st_pk_req); + blkif->st_print = jiffies + msecs_to_jiffies(10 * 1000); + blkif->st_rd_req = 0; + blkif->st_wr_req = 0; + blkif->st_oo_req = 0; ++ blkif->st_pk_req = 0; + } + + int blkif_schedule(void *arg) +@@ -374,6 +376,13 @@ handle_request: + blkif->st_wr_req++; + ret = dispatch_rw_block_io(blkif, &req, pending_req); + break; ++ case BLKIF_OP_PACKET: ++ DPRINTK("error: block operation BLKIF_OP_PACKET not implemented\n"); ++ blkif->st_pk_req++; ++ make_response(blkif, req.id, req.operation, ++ BLKIF_RSP_ERROR); ++ free_req(pending_req); ++ break; + default: + /* A good sign something is wrong: sleep for a while to + * avoid excessive CPU consumption by a bad guest. */ +--- head-2010-04-29.orig/drivers/xen/blkback/common.h 2010-03-25 14:38:02.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blkback/common.h 2010-03-25 14:38:05.000000000 +0100 +@@ -92,6 +92,7 @@ typedef struct blkif_st { + int st_wr_req; + int st_oo_req; + int st_br_req; ++ int st_pk_req; + int st_rd_sect; + int st_wr_sect; + +--- head-2010-04-29.orig/drivers/xen/blkfront/blkfront.c 2010-03-24 15:25:21.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blkfront/blkfront.c 2010-03-25 14:38:05.000000000 +0100 +@@ -671,6 +671,8 @@ static int blkif_queue_request(struct re + BLKIF_OP_WRITE : BLKIF_OP_READ; + if (blk_barrier_rq(req)) + ring_req->operation = BLKIF_OP_WRITE_BARRIER; ++ if (blk_pc_request(req)) ++ ring_req->operation = BLKIF_OP_PACKET; + + ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); + BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST); +@@ -728,7 +730,7 @@ void do_blkif_request(struct request_que + + blk_start_request(req); + +- if (!blk_fs_request(req)) { ++ if (!blk_fs_request(req) && !blk_pc_request(req)) { + __blk_end_request_all(req, -EIO); + continue; + } +@@ -799,6 +801,7 @@ static irqreturn_t blkif_int(int irq, vo + /* fall through */ + case BLKIF_OP_READ: + case BLKIF_OP_WRITE: ++ case BLKIF_OP_PACKET: + if (unlikely(bret->status != BLKIF_RSP_OKAY)) + DPRINTK("Bad return from blkdev data " + "request: %x\n", bret->status); +--- head-2010-04-29.orig/drivers/xen/blktap/blktap.c 2010-04-29 10:16:10.000000000 +0200 ++++ head-2010-04-29/drivers/xen/blktap/blktap.c 2010-04-29 10:16:17.000000000 +0200 +@@ -1148,13 +1148,14 @@ static void fast_flush_area(pending_req_ + + static void print_stats(blkif_t *blkif) + { +- printk(KERN_DEBUG "%s: oo %3d | rd %4d | wr %4d\n", ++ printk(KERN_DEBUG "%s: oo %3d | rd %4d | wr %4d | pk %4d\n", + current->comm, blkif->st_oo_req, +- blkif->st_rd_req, blkif->st_wr_req); ++ blkif->st_rd_req, blkif->st_wr_req, blkif->st_pk_req); + blkif->st_print = jiffies + msecs_to_jiffies(10 * 1000); + blkif->st_rd_req = 0; + blkif->st_wr_req = 0; + blkif->st_oo_req = 0; ++ blkif->st_pk_req = 0; + } + + int tap_blkif_schedule(void *arg) +@@ -1396,6 +1397,11 @@ static int do_block_io_op(blkif_t *blkif + dispatch_rw_block_io(blkif, &req, pending_req); + break; + ++ case BLKIF_OP_PACKET: ++ blkif->st_pk_req++; ++ dispatch_rw_block_io(blkif, &req, pending_req); ++ break; ++ + default: + /* A good sign something is wrong: sleep for a while to + * avoid excessive CPU consumption by a bad guest. */ +@@ -1435,6 +1441,8 @@ static void dispatch_rw_block_io(blkif_t + struct vm_area_struct *vma = NULL; + + switch (req->operation) { ++ case BLKIF_OP_PACKET: ++ /* Fall through */ + case BLKIF_OP_READ: + operation = READ; + break; +--- head-2010-04-29.orig/drivers/xen/blktap/common.h 2010-03-24 15:09:22.000000000 +0100 ++++ head-2010-04-29/drivers/xen/blktap/common.h 2010-03-25 14:38:05.000000000 +0100 +@@ -75,6 +75,7 @@ typedef struct blkif_st { + int st_rd_req; + int st_wr_req; + int st_oo_req; ++ int st_pk_req; + int st_rd_sect; + int st_wr_sect; + +--- head-2010-04-29.orig/drivers/xen/blktap2/blktap.h 2010-04-15 11:24:08.000000000 +0200 ++++ head-2010-04-29/drivers/xen/blktap2/blktap.h 2010-04-15 11:42:01.000000000 +0200 +@@ -138,6 +138,7 @@ struct blktap_statistics { + int st_rd_req; + int st_wr_req; + int st_oo_req; ++ int st_pk_req; + int st_rd_sect; + int st_wr_sect; + s64 st_rd_cnt; +--- head-2010-04-29.orig/drivers/xen/blktap2/device.c 2010-04-19 14:54:02.000000000 +0200 ++++ head-2010-04-29/drivers/xen/blktap2/device.c 2010-04-19 14:55:13.000000000 +0200 +@@ -369,7 +369,8 @@ blktap_device_fail_pending_requests(stru + + BTERR("%u:%u: failing pending %s of %d pages\n", + blktap_device_major, tap->minor, +- (request->operation == BLKIF_OP_READ ? ++ (request->operation == BLKIF_OP_PACKET ? ++ "packet" : request->operation == BLKIF_OP_READ ? + "read" : "write"), request->nr_pages); + + blktap_unmap(tap, request); +@@ -410,6 +411,7 @@ blktap_device_finish_request(struct blkt + switch (request->operation) { + case BLKIF_OP_READ: + case BLKIF_OP_WRITE: ++ case BLKIF_OP_PACKET: + if (unlikely(res->status != BLKIF_RSP_OKAY)) + BTERR("Bad return from device data " + "request: %x\n", res->status); +@@ -649,6 +651,8 @@ blktap_device_process_request(struct blk + blkif_req.handle = 0; + blkif_req.operation = rq_data_dir(req) ? + BLKIF_OP_WRITE : BLKIF_OP_READ; ++ if (unlikely(blk_pc_request(req))) ++ blkif_req.operation = BLKIF_OP_PACKET; + + request->id = (unsigned long)req; + request->operation = blkif_req.operation; +@@ -714,7 +718,9 @@ blktap_device_process_request(struct blk + wmb(); /* blktap_poll() reads req_prod_pvt asynchronously */ + ring->ring.req_prod_pvt++; + +- if (rq_data_dir(req)) { ++ if (unlikely(blk_pc_request(req))) ++ tap->stats.st_pk_req++; ++ else if (rq_data_dir(req)) { + tap->stats.st_wr_sect += nr_sects; + tap->stats.st_wr_req++; + } else { +--- head-2010-04-29.orig/include/xen/interface/io/blkif.h 2010-01-19 16:01:04.000000000 +0100 ++++ head-2010-04-29/include/xen/interface/io/blkif.h 2010-03-25 14:38:05.000000000 +0100 +@@ -76,6 +76,10 @@ + * "feature-flush-cache" node! + */ + #define BLKIF_OP_FLUSH_DISKCACHE 3 ++/* ++ * Device specific command packet contained within the request ++ */ ++#define BLKIF_OP_PACKET 4 + + /* + * Maximum scatter/gather segments per request. diff --git a/patches.xen/xen-sections b/patches.xen/xen-sections new file mode 100644 index 0000000..a571e74 --- /dev/null +++ b/patches.xen/xen-sections @@ -0,0 +1,107 @@ +From: jbeulich@novell.com +Subject: fix placement of some routines/data +Patch-mainline: obsolete + +--- head-2010-05-12.orig/arch/x86/kernel/time-xen.c 2010-05-12 09:03:15.000000000 +0200 ++++ head-2010-05-12/arch/x86/kernel/time-xen.c 2010-05-12 09:13:55.000000000 +0200 +@@ -676,7 +676,7 @@ int xen_update_persistent_clock(void) + /* Dynamically-mapped IRQ. */ + DEFINE_PER_CPU(int, timer_irq); + +-static void setup_cpu0_timer_irq(void) ++static void __init setup_cpu0_timer_irq(void) + { + per_cpu(timer_irq, 0) = + bind_virq_to_irqhandler( +@@ -901,7 +901,7 @@ int __cpuinit local_setup_timer(unsigned + return 0; + } + +-void __cpuexit local_teardown_timer(unsigned int cpu) ++void __cpuinit local_teardown_timer(unsigned int cpu) + { + BUG_ON(cpu == 0); + unbind_from_irqhandler(per_cpu(timer_irq, cpu), NULL); +--- head-2010-05-12.orig/drivers/xen/core/cpu_hotplug.c 2010-03-24 15:17:58.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/cpu_hotplug.c 2010-01-25 13:45:39.000000000 +0100 +@@ -24,7 +24,7 @@ static int local_cpu_hotplug_request(voi + return (current->mm != NULL); + } + +-static void vcpu_hotplug(unsigned int cpu) ++static void __cpuinit vcpu_hotplug(unsigned int cpu) + { + int err; + char dir[32], state[32]; +@@ -51,7 +51,7 @@ static void vcpu_hotplug(unsigned int cp + } + } + +-static void handle_vcpu_hotplug_event( ++static void __cpuinit handle_vcpu_hotplug_event( + struct xenbus_watch *watch, const char **vec, unsigned int len) + { + unsigned int cpu; +@@ -80,12 +80,12 @@ static int smpboot_cpu_notify(struct not + return NOTIFY_OK; + } + +-static int setup_cpu_watcher(struct notifier_block *notifier, +- unsigned long event, void *data) ++static int __cpuinit setup_cpu_watcher(struct notifier_block *notifier, ++ unsigned long event, void *data) + { + unsigned int i; + +- static struct xenbus_watch cpu_watch = { ++ static struct xenbus_watch __cpuinitdata cpu_watch = { + .node = "cpu", + .callback = handle_vcpu_hotplug_event, + .flags = XBWF_new_thread }; +@@ -105,7 +105,7 @@ static int __init setup_vcpu_hotplug_eve + { + static struct notifier_block hotplug_cpu = { + .notifier_call = smpboot_cpu_notify }; +- static struct notifier_block xsn_cpu = { ++ static struct notifier_block __cpuinitdata xsn_cpu = { + .notifier_call = setup_cpu_watcher }; + + if (!is_running_on_xen()) +@@ -119,7 +119,7 @@ static int __init setup_vcpu_hotplug_eve + + arch_initcall(setup_vcpu_hotplug_event); + +-int smp_suspend(void) ++int __ref smp_suspend(void) + { + unsigned int cpu; + int err; +@@ -140,7 +140,7 @@ int smp_suspend(void) + return 0; + } + +-void smp_resume(void) ++void __ref smp_resume(void) + { + unsigned int cpu; + +--- head-2010-05-12.orig/drivers/xen/core/smpboot.c 2010-03-24 15:25:21.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/smpboot.c 2010-03-19 15:20:15.000000000 +0100 +@@ -181,7 +181,7 @@ static int __cpuinit xen_smp_intr_init(u + } + + #ifdef CONFIG_HOTPLUG_CPU +-static void __cpuexit xen_smp_intr_exit(unsigned int cpu) ++static void __cpuinit xen_smp_intr_exit(unsigned int cpu) + { + if (cpu != 0) + local_teardown_timer(cpu); +@@ -400,7 +400,7 @@ int __cpuexit __cpu_disable(void) + return 0; + } + +-void __cpuexit __cpu_die(unsigned int cpu) ++void __cpuinit __cpu_die(unsigned int cpu) + { + while (HYPERVISOR_vcpu_op(VCPUOP_is_up, cpu, NULL)) { + current->state = TASK_UNINTERRUPTIBLE; diff --git a/patches.xen/xen-spinlock-poll-early b/patches.xen/xen-spinlock-poll-early new file mode 100644 index 0000000..1defb82 --- /dev/null +++ b/patches.xen/xen-spinlock-poll-early @@ -0,0 +1,177 @@ +From: jbeulich@novell.com +Subject: Go into polling mode early if lock owner is not running +Patch-mainline: n/a + +This could be merged into the original ticket spinlock code once +validated, if there wasn't the dependency on smp-processor-id.h, which +only gets introduced in the 2.6.32 merge. + +--- head-2010-03-15.orig/arch/x86/include/mach-xen/asm/spinlock.h 2010-02-24 12:25:27.000000000 +0100 ++++ head-2010-03-15/arch/x86/include/mach-xen/asm/spinlock.h 2010-02-24 12:39:22.000000000 +0100 +@@ -41,6 +41,10 @@ + #ifdef TICKET_SHIFT + + #include ++#include ++#include ++ ++DECLARE_PER_CPU(struct vcpu_runstate_info, runstate); + + int xen_spinlock_init(unsigned int cpu); + void xen_spinlock_cleanup(unsigned int cpu); +@@ -113,6 +117,9 @@ static __always_inline int __ticket_spin + : + : "memory", "cc"); + ++ if (tmp) ++ lock->owner = raw_smp_processor_id(); ++ + return tmp; + } + #elif TICKET_SHIFT == 16 +@@ -179,10 +186,17 @@ static __always_inline int __ticket_spin + : + : "memory", "cc"); + ++ if (tmp) ++ lock->owner = raw_smp_processor_id(); ++ + return tmp; + } + #endif + ++#define __ticket_spin_count(lock) \ ++ (per_cpu(runstate.state, (lock)->owner) == RUNSTATE_running \ ++ ? 1 << 10 : 1) ++ + static inline int __ticket_spin_is_locked(arch_spinlock_t *lock) + { + int tmp = ACCESS_ONCE(lock->slock); +@@ -204,16 +218,18 @@ static __always_inline void __ticket_spi + bool free; + + __ticket_spin_lock_preamble; +- if (likely(free)) { ++ if (likely(free)) ++ raw_local_irq_restore(flags); ++ else { ++ token = xen_spin_adjust(lock, token); + raw_local_irq_restore(flags); +- return; ++ do { ++ count = __ticket_spin_count(lock); ++ __ticket_spin_lock_body; ++ } while (unlikely(!count) ++ && !xen_spin_wait(lock, &token, flags)); + } +- token = xen_spin_adjust(lock, token); +- raw_local_irq_restore(flags); +- do { +- count = 1 << 10; +- __ticket_spin_lock_body; +- } while (unlikely(!count) && !xen_spin_wait(lock, &token, flags)); ++ lock->owner = raw_smp_processor_id(); + } + + static __always_inline void __ticket_spin_lock_flags(arch_spinlock_t *lock, +@@ -223,13 +239,15 @@ static __always_inline void __ticket_spi + bool free; + + __ticket_spin_lock_preamble; +- if (likely(free)) +- return; +- token = xen_spin_adjust(lock, token); +- do { +- count = 1 << 10; +- __ticket_spin_lock_body; +- } while (unlikely(!count) && !xen_spin_wait(lock, &token, flags)); ++ if (unlikely(!free)) { ++ token = xen_spin_adjust(lock, token); ++ do { ++ count = __ticket_spin_count(lock); ++ __ticket_spin_lock_body; ++ } while (unlikely(!count) ++ && !xen_spin_wait(lock, &token, flags)); ++ } ++ lock->owner = raw_smp_processor_id(); + } + + static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock) +@@ -246,6 +264,7 @@ static __always_inline void __ticket_spi + #undef __ticket_spin_lock_preamble + #undef __ticket_spin_lock_body + #undef __ticket_spin_unlock_body ++#undef __ticket_spin_count + #endif + + #define __arch_spin(n) __ticket_spin_##n +--- head-2010-03-15.orig/arch/x86/include/mach-xen/asm/spinlock_types.h 2010-01-28 10:38:23.000000000 +0100 ++++ head-2010-03-15/arch/x86/include/mach-xen/asm/spinlock_types.h 2010-01-26 11:27:24.000000000 +0100 +@@ -26,6 +26,11 @@ typedef union { + # define TICKET_SHIFT 16 + u16 cur, seq; + #endif ++#if CONFIG_NR_CPUS <= 256 ++ u8 owner; ++#else ++ u16 owner; ++#endif + #else + /* + * This differs from the pre-2.6.24 spinlock by always using xchgb +--- head-2010-03-15.orig/arch/x86/kernel/time-xen.c 2010-03-02 10:20:07.000000000 +0100 ++++ head-2010-03-15/arch/x86/kernel/time-xen.c 2010-03-02 10:20:42.000000000 +0100 +@@ -58,7 +58,7 @@ static u32 shadow_tv_version; + static u64 jiffies_bias, system_time_bias; + + /* Current runstate of each CPU (updated automatically by the hypervisor). */ +-static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate); ++DEFINE_PER_CPU(struct vcpu_runstate_info, runstate); + + /* Must be signed, as it's compared with s64 quantities which can be -ve. */ + #define NS_PER_TICK (1000000000LL/HZ) +--- head-2010-03-15.orig/drivers/xen/core/spinlock.c 2010-02-26 15:34:33.000000000 +0100 ++++ head-2010-03-15/drivers/xen/core/spinlock.c 2010-03-19 08:48:35.000000000 +0100 +@@ -39,6 +39,8 @@ int __cpuinit xen_spinlock_init(unsigned + }; + int rc; + ++ setup_runstate_area(cpu); ++ + rc = bind_ipi_to_irqaction(SPIN_UNLOCK_VECTOR, + cpu, + &spinlock_action); +@@ -86,6 +88,7 @@ unsigned int xen_spin_adjust(const arch_ + bool xen_spin_wait(arch_spinlock_t *lock, unsigned int *ptok, + unsigned int flags) + { ++ unsigned int cpu = raw_smp_processor_id(); + int irq = spinlock_irq; + bool rc; + typeof(vcpu_info(0)->evtchn_upcall_mask) upcall_mask; +@@ -93,7 +96,7 @@ bool xen_spin_wait(arch_spinlock_t *lock + struct spinning spinning, *other; + + /* If kicker interrupt not initialized yet, just spin. */ +- if (unlikely(irq < 0) || unlikely(!cpu_online(raw_smp_processor_id()))) ++ if (unlikely(irq < 0) || unlikely(!cpu_online(cpu))) + return false; + + /* announce we're spinning */ +@@ -114,6 +117,7 @@ bool xen_spin_wait(arch_spinlock_t *lock + * we weren't looking. + */ + if (lock->cur == spinning.ticket) { ++ lock->owner = cpu; + /* + * If we interrupted another spinlock while it was + * blocking, make sure it doesn't block (again) +@@ -207,6 +211,8 @@ bool xen_spin_wait(arch_spinlock_t *lock + if (!free) + token = spin_adjust(other->prev, lock, token); + other->ticket = token >> TICKET_SHIFT; ++ if (lock->cur == other->ticket) ++ lock->owner = cpu; + } + raw_local_irq_restore(upcall_mask); + diff --git a/patches.xen/xen-staging-build b/patches.xen/xen-staging-build new file mode 100644 index 0000000..54ddfbe --- /dev/null +++ b/patches.xen/xen-staging-build @@ -0,0 +1,40 @@ +From: jbeulich@novell.com +Subject: fix issue with Windows-style types used in drivers/staging/ +Patch-mainline: obsolete + +--- head-2010-03-24.orig/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 16:41:12.000000000 +0100 ++++ head-2010-03-24/arch/x86/include/mach-xen/asm/hypervisor.h 2009-11-23 10:45:08.000000000 +0100 +@@ -354,4 +354,9 @@ MULTI_grant_table_op(multicall_entry_t * + + #define uvm_multi(cpumask) ((unsigned long)cpumask_bits(cpumask) | UVMF_MULTI) + ++#ifdef LINUX ++/* drivers/staging/ use Windows-style types, including VOID */ ++#undef VOID ++#endif ++ + #endif /* __HYPERVISOR_H__ */ +--- head-2010-03-24.orig/drivers/staging/vt6655/ttype.h 2010-03-25 16:41:12.000000000 +0100 ++++ head-2010-03-24/drivers/staging/vt6655/ttype.h 2009-10-13 17:02:12.000000000 +0200 +@@ -30,6 +30,9 @@ + #ifndef __TTYPE_H__ + #define __TTYPE_H__ + ++#ifdef CONFIG_XEN ++#include ++#endif + + /******* Common definitions and typedefs ***********************************/ + +--- head-2010-03-24.orig/drivers/staging/vt6656/ttype.h 2010-03-25 16:41:12.000000000 +0100 ++++ head-2010-03-24/drivers/staging/vt6656/ttype.h 2009-10-13 17:02:12.000000000 +0200 +@@ -30,6 +30,9 @@ + #ifndef __TTYPE_H__ + #define __TTYPE_H__ + ++#ifdef CONFIG_XEN ++#include ++#endif + + /******* Common definitions and typedefs ***********************************/ + diff --git a/patches.xen/xen-swiotlb-heuristics b/patches.xen/xen-swiotlb-heuristics new file mode 100644 index 0000000..3fe022e --- /dev/null +++ b/patches.xen/xen-swiotlb-heuristics @@ -0,0 +1,32 @@ +From: jbeulich@novell.com +Subject: adjust Xen's swiotlb default size setting +Patch-mainline: obsolete + +--- head-2010-04-15.orig/lib/swiotlb-xen.c 2010-04-15 10:54:48.000000000 +0200 ++++ head-2010-04-15/lib/swiotlb-xen.c 2010-04-15 11:42:17.000000000 +0200 +@@ -216,8 +216,8 @@ swiotlb_init_with_default_size(size_t de + void __init + swiotlb_init(int verbose) + { +- long ram_end; +- size_t defsz = 64 * (1 << 20); /* 64MB default size */ ++ unsigned long ram_end; ++ size_t defsz = 64 << 20; /* 64MB default size */ + + if (swiotlb_force == 1) { + swiotlb = 1; +@@ -226,8 +226,12 @@ swiotlb_init(int verbose) + is_initial_xendomain()) { + /* Domain 0 always has a swiotlb. */ + ram_end = HYPERVISOR_memory_op(XENMEM_maximum_ram_page, NULL); +- if (ram_end <= 0x7ffff) +- defsz = 2 * (1 << 20); /* 2MB on <2GB on systems. */ ++ if (ram_end <= 0x1ffff) ++ defsz = 2 << 20; /* 2MB on <512MB systems. */ ++ else if (ram_end <= 0x3ffff) ++ defsz = 4 << 20; /* 4MB on <1GB systems. */ ++ else if (ram_end <= 0x7ffff) ++ defsz = 8 << 20; /* 8MB on <2GB systems. */ + swiotlb = 1; + } + diff --git a/patches.xen/xen-sysdev-suspend b/patches.xen/xen-sysdev-suspend new file mode 100644 index 0000000..0b298a8 --- /dev/null +++ b/patches.xen/xen-sysdev-suspend @@ -0,0 +1,506 @@ +From: jbeulich@novell.com +Subject: use base kernel suspend/resume infrastructure +Patch-mainline: obsolete + +... rather than calling just a few functions explicitly. + +--- head-2010-05-12.orig/arch/x86/kernel/time-xen.c 2010-05-12 09:13:55.000000000 +0200 ++++ head-2010-05-12/arch/x86/kernel/time-xen.c 2010-05-12 09:14:03.000000000 +0200 +@@ -69,6 +69,10 @@ static DEFINE_PER_CPU(struct vcpu_runsta + /* Must be signed, as it's compared with s64 quantities which can be -ve. */ + #define NS_PER_TICK (1000000000LL/HZ) + ++static struct vcpu_set_periodic_timer xen_set_periodic_tick = { ++ .period_ns = NS_PER_TICK ++}; ++ + static void __clock_was_set(struct work_struct *unused) + { + clock_was_set(); +@@ -561,6 +565,17 @@ void mark_tsc_unstable(char *reason) + } + EXPORT_SYMBOL_GPL(mark_tsc_unstable); + ++static void init_missing_ticks_accounting(unsigned int cpu) ++{ ++ struct vcpu_runstate_info *runstate = setup_runstate_area(cpu); ++ ++ per_cpu(processed_blocked_time, cpu) = ++ runstate->time[RUNSTATE_blocked]; ++ per_cpu(processed_stolen_time, cpu) = ++ runstate->time[RUNSTATE_runnable] + ++ runstate->time[RUNSTATE_offline]; ++} ++ + static cycle_t cs_last; + + static cycle_t xen_clocksource_read(struct clocksource *cs) +@@ -597,11 +612,32 @@ static cycle_t xen_clocksource_read(stru + #endif + } + ++/* No locking required. Interrupts are disabled on all CPUs. */ + static void xen_clocksource_resume(struct clocksource *cs) + { +- extern void time_resume(void); ++ unsigned int cpu; ++ ++ init_cpu_khz(); ++ ++ for_each_online_cpu(cpu) { ++ switch (HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, cpu, ++ &xen_set_periodic_tick)) { ++ case 0: ++#if CONFIG_XEN_COMPAT <= 0x030004 ++ case -ENOSYS: ++#endif ++ break; ++ default: ++ BUG(); ++ } ++ get_time_values_from_xen(cpu); ++ per_cpu(processed_system_time, cpu) = ++ per_cpu(shadow_time, 0).system_timestamp; ++ init_missing_ticks_accounting(cpu); ++ } ++ ++ processed_system_time = per_cpu(shadow_time, 0).system_timestamp; + +- time_resume(); + cs_last = local_clock(); + } + +@@ -633,17 +669,6 @@ struct vcpu_runstate_info *setup_runstat + return rs; + } + +-static void init_missing_ticks_accounting(unsigned int cpu) +-{ +- struct vcpu_runstate_info *runstate = setup_runstate_area(cpu); +- +- per_cpu(processed_blocked_time, cpu) = +- runstate->time[RUNSTATE_blocked]; +- per_cpu(processed_stolen_time, cpu) = +- runstate->time[RUNSTATE_runnable] + +- runstate->time[RUNSTATE_offline]; +-} +- + void xen_read_persistent_clock(struct timespec *ts) + { + const shared_info_t *s = HYPERVISOR_shared_info; +@@ -689,10 +714,6 @@ static void __init setup_cpu0_timer_irq( + BUG_ON(per_cpu(timer_irq, 0) < 0); + } + +-static struct vcpu_set_periodic_timer xen_set_periodic_tick = { +- .period_ns = NS_PER_TICK +-}; +- + void __init time_init(void) + { + init_cpu_khz(); +@@ -830,35 +851,6 @@ void xen_halt(void) + } + EXPORT_SYMBOL(xen_halt); + +-/* No locking required. Interrupts are disabled on all CPUs. */ +-void time_resume(void) +-{ +- unsigned int cpu; +- +- init_cpu_khz(); +- +- for_each_online_cpu(cpu) { +- switch (HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, cpu, +- &xen_set_periodic_tick)) { +- case 0: +-#if CONFIG_XEN_COMPAT <= 0x030004 +- case -ENOSYS: +-#endif +- break; +- default: +- BUG(); +- } +- get_time_values_from_xen(cpu); +- per_cpu(processed_system_time, cpu) = +- per_cpu(shadow_time, 0).system_timestamp; +- init_missing_ticks_accounting(cpu); +- } +- +- processed_system_time = per_cpu(shadow_time, 0).system_timestamp; +- +- update_wallclock(); +-} +- + #ifdef CONFIG_SMP + static char timer_name[NR_CPUS][15]; + +--- head-2010-05-12.orig/drivers/xen/core/evtchn.c 2010-04-15 11:03:28.000000000 +0200 ++++ head-2010-05-12/drivers/xen/core/evtchn.c 2010-04-23 15:20:28.000000000 +0200 +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1117,10 +1118,21 @@ static void restore_cpu_ipis(unsigned in + } + } + +-void irq_resume(void) ++static int evtchn_resume(struct sys_device *dev) + { + unsigned int cpu, irq, evtchn; + struct irq_cfg *cfg; ++ struct evtchn_status status; ++ ++ /* Avoid doing anything in the 'suspend cancelled' case. */ ++ status.dom = DOMID_SELF; ++ status.port = evtchn_from_irq(percpu_read(virq_to_irq[VIRQ_TIMER])); ++ if (HYPERVISOR_event_channel_op(EVTCHNOP_status, &status)) ++ BUG(); ++ if (status.status == EVTCHNSTAT_virq ++ && status.vcpu == smp_processor_id() ++ && status.u.virq == VIRQ_TIMER) ++ return 0; + + init_evtchn_cpu_bindings(); + +@@ -1156,7 +1168,32 @@ void irq_resume(void) + restore_cpu_ipis(cpu); + } + ++ return 0; ++} ++ ++static struct sysdev_class evtchn_sysclass = { ++ .name = "evtchn", ++ .resume = evtchn_resume, ++}; ++ ++static struct sys_device device_evtchn = { ++ .id = 0, ++ .cls = &evtchn_sysclass, ++}; ++ ++static int __init evtchn_register(void) ++{ ++ int err; ++ ++ if (is_initial_xendomain()) ++ return 0; ++ ++ err = sysdev_class_register(&evtchn_sysclass); ++ if (!err) ++ err = sysdev_register(&device_evtchn); ++ return err; + } ++core_initcall(evtchn_register); + #endif + + int __init arch_early_irq_init(void) +--- head-2010-05-12.orig/drivers/xen/core/gnttab.c 2010-04-15 11:04:07.000000000 +0200 ++++ head-2010-05-12/drivers/xen/core/gnttab.c 2010-04-15 11:42:34.000000000 +0200 +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -708,23 +709,37 @@ EXPORT_SYMBOL(gnttab_post_map_adjust); + + #endif /* __HAVE_ARCH_PTE_SPECIAL */ + +-int gnttab_resume(void) ++static int gnttab_resume(struct sys_device *dev) + { + if (max_nr_grant_frames() < nr_grant_frames) + return -ENOSYS; + return gnttab_map(0, nr_grant_frames - 1); + } ++#define gnttab_resume() gnttab_resume(NULL) + + #ifdef CONFIG_PM_SLEEP +-int gnttab_suspend(void) +-{ + #ifdef CONFIG_X86 ++static int gnttab_suspend(struct sys_device *dev, pm_message_t state) ++{ + apply_to_page_range(&init_mm, (unsigned long)shared, + PAGE_SIZE * nr_grant_frames, + unmap_pte_fn, NULL); +-#endif + return 0; + } ++#else ++#define gnttab_suspend NULL ++#endif ++ ++static struct sysdev_class gnttab_sysclass = { ++ .name = "gnttab", ++ .resume = gnttab_resume, ++ .suspend = gnttab_suspend, ++}; ++ ++static struct sys_device device_gnttab = { ++ .id = 0, ++ .cls = &gnttab_sysclass, ++}; + #endif + + #else /* !CONFIG_XEN */ +@@ -804,6 +819,17 @@ int __devinit gnttab_init(void) + if (!is_running_on_xen()) + return -ENODEV; + ++#if defined(CONFIG_XEN) && defined(CONFIG_PM_SLEEP) ++ if (!is_initial_xendomain()) { ++ int err = sysdev_class_register(&gnttab_sysclass); ++ ++ if (!err) ++ err = sysdev_register(&device_gnttab); ++ if (err) ++ return err; ++ } ++#endif ++ + nr_grant_frames = 1; + boot_max_nr_grant_frames = __max_nr_grant_frames(); + +--- head-2010-05-12.orig/drivers/xen/core/machine_reboot.c 2010-03-24 15:17:58.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/machine_reboot.c 2009-12-18 14:19:13.000000000 +0100 +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include "../../base/base.h" + + #if defined(__i386__) || defined(__x86_64__) + #include +@@ -145,47 +146,28 @@ struct suspend { + static int take_machine_down(void *_suspend) + { + struct suspend *suspend = _suspend; +- int suspend_cancelled, err; +- extern void time_resume(void); ++ int suspend_cancelled; + +- if (suspend->fast_suspend) { +- BUG_ON(!irqs_disabled()); +- } else { +- BUG_ON(irqs_disabled()); +- +- for (;;) { +- err = smp_suspend(); +- if (err) +- return err; +- +- xenbus_suspend(); +- preempt_disable(); +- +- if (num_online_cpus() == 1) +- break; +- +- preempt_enable(); +- xenbus_suspend_cancel(); +- } +- +- local_irq_disable(); +- } ++ BUG_ON(!irqs_disabled()); + + mm_pin_all(); +- gnttab_suspend(); +- pre_suspend(); +- +- /* +- * This hypercall returns 1 if suspend was cancelled or the domain was +- * merely checkpointed, and 0 if it is resuming in a new domain. +- */ +- suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info)); ++ suspend_cancelled = sysdev_suspend(PMSG_SUSPEND); ++ if (!suspend_cancelled) { ++ pre_suspend(); + ++ /* ++ * This hypercall returns 1 if suspend was cancelled or the domain was ++ * merely checkpointed, and 0 if it is resuming in a new domain. ++ */ ++ suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info)); ++ } else ++ BUG_ON(suspend_cancelled > 0); + suspend->resume_notifier(suspend_cancelled); +- post_suspend(suspend_cancelled); +- gnttab_resume(); ++ if (suspend_cancelled >= 0) { ++ post_suspend(suspend_cancelled); ++ sysdev_resume(); ++ } + if (!suspend_cancelled) { +- irq_resume(); + #ifdef __x86_64__ + /* + * Older versions of Xen do not save/restore the user %cr3. +@@ -197,10 +179,6 @@ static int take_machine_down(void *_susp + current->active_mm->pgd))); + #endif + } +- time_resume(); +- +- if (!suspend->fast_suspend) +- local_irq_enable(); + + return suspend_cancelled; + } +@@ -208,8 +186,14 @@ static int take_machine_down(void *_susp + int __xen_suspend(int fast_suspend, void (*resume_notifier)(int)) + { + int err, suspend_cancelled; ++ const char *what; + struct suspend suspend; + ++#define _check(fn, args...) ({ \ ++ what = #fn; \ ++ err = (fn)(args); \ ++}) ++ + BUG_ON(smp_processor_id() != 0); + BUG_ON(in_interrupt()); + +@@ -225,41 +209,91 @@ int __xen_suspend(int fast_suspend, void + if (num_possible_cpus() == 1) + fast_suspend = 0; + +- if (fast_suspend) { +- err = stop_machine_create(); +- if (err) +- return err; ++ if (fast_suspend && _check(stop_machine_create)) { ++ printk(KERN_ERR "%s() failed: %d\n", what, err); ++ return err; + } + + suspend.fast_suspend = fast_suspend; + suspend.resume_notifier = resume_notifier; + ++ if (_check(dpm_suspend_start, PMSG_SUSPEND)) { ++ if (fast_suspend) ++ stop_machine_destroy(); ++ printk(KERN_ERR "%s() failed: %d\n", what, err); ++ return err; ++ } ++ + if (fast_suspend) { + xenbus_suspend(); ++ ++ if (_check(dpm_suspend_noirq, PMSG_SUSPEND)) { ++ xenbus_suspend_cancel(); ++ dpm_resume_end(PMSG_RESUME); ++ stop_machine_destroy(); ++ printk(KERN_ERR "%s() failed: %d\n", what, err); ++ return err; ++ } ++ + err = stop_machine(take_machine_down, &suspend, + &cpumask_of_cpu(0)); + if (err < 0) + xenbus_suspend_cancel(); + } else { ++ BUG_ON(irqs_disabled()); ++ ++ for (;;) { ++ xenbus_suspend(); ++ ++ if (!_check(dpm_suspend_noirq, PMSG_SUSPEND) ++ && _check(smp_suspend)) ++ dpm_resume_noirq(PMSG_RESUME); ++ if (err) { ++ xenbus_suspend_cancel(); ++ dpm_resume_end(PMSG_RESUME); ++ printk(KERN_ERR "%s() failed: %d\n", ++ what, err); ++ return err; ++ } ++ ++ preempt_disable(); ++ ++ if (num_online_cpus() == 1) ++ break; ++ ++ preempt_enable(); ++ ++ dpm_resume_noirq(PMSG_RESUME); ++ ++ xenbus_suspend_cancel(); ++ } ++ ++ local_irq_disable(); + err = take_machine_down(&suspend); ++ local_irq_enable(); + } + +- if (err < 0) +- return err; ++ dpm_resume_noirq(PMSG_RESUME); + +- suspend_cancelled = err; +- if (!suspend_cancelled) { +- xencons_resume(); +- xenbus_resume(); +- } else { +- xenbus_suspend_cancel(); ++ if (err >= 0) { ++ suspend_cancelled = err; ++ if (!suspend_cancelled) { ++ xencons_resume(); ++ xenbus_resume(); ++ } else { ++ xenbus_suspend_cancel(); ++ err = 0; ++ } ++ ++ if (!fast_suspend) ++ smp_resume(); + } + +- if (!fast_suspend) +- smp_resume(); +- else ++ dpm_resume_end(PMSG_RESUME); ++ ++ if (fast_suspend) + stop_machine_destroy(); + +- return 0; ++ return err; + } + #endif +--- head-2010-05-12.orig/include/xen/evtchn.h 2010-03-31 14:02:34.000000000 +0200 ++++ head-2010-05-12/include/xen/evtchn.h 2010-03-31 14:10:36.000000000 +0200 +@@ -108,7 +108,9 @@ int bind_ipi_to_irqhandler( + */ + void unbind_from_irqhandler(unsigned int irq, void *dev_id); + ++#ifndef CONFIG_XEN + void irq_resume(void); ++#endif + + /* Entry point for notifications into Linux subsystems. */ + asmlinkage void evtchn_do_upcall(struct pt_regs *regs); +--- head-2010-05-12.orig/include/xen/gnttab.h 2008-11-04 11:13:10.000000000 +0100 ++++ head-2010-05-12/include/xen/gnttab.h 2009-11-06 11:10:15.000000000 +0100 +@@ -110,8 +110,9 @@ static inline void __gnttab_dma_unmap_pa + + void gnttab_reset_grant_page(struct page *page); + +-int gnttab_suspend(void); ++#ifndef CONFIG_XEN + int gnttab_resume(void); ++#endif + + void *arch_gnttab_alloc_shared(unsigned long *frames); + diff --git a/patches.xen/xen-unpriv-build b/patches.xen/xen-unpriv-build new file mode 100644 index 0000000..eb729e3 --- /dev/null +++ b/patches.xen/xen-unpriv-build @@ -0,0 +1,298 @@ +From: jbeulich@novell.com +Subject: no need to build certain bits when building non-privileged kernel +Patch-mainline: n/a + +--- head-2010-05-12.orig/arch/x86/Kconfig 2010-03-25 14:39:15.000000000 +0100 ++++ head-2010-05-12/arch/x86/Kconfig 2010-05-06 16:05:14.000000000 +0200 +@@ -737,6 +737,7 @@ config APB_TIMER + config DMI + default y + bool "Enable DMI scanning" if EMBEDDED ++ depends on !XEN_UNPRIVILEGED_GUEST + ---help--- + Enabled scanning of DMI to identify machine quirks. Say Y + here unless you have verified that your setup is not +@@ -817,6 +818,7 @@ config AMD_IOMMU_STATS + # need this always selected by IOMMU for the VIA workaround + config SWIOTLB + def_bool y if X86_64 || XEN ++ prompt "Software I/O TLB" if XEN_UNPRIVILEGED_GUEST && !XEN_PCIDEV_FRONTEND + ---help--- + Support for software bounce buffers used on x86-64 systems + which don't have a hardware IOMMU (e.g. the current generation +@@ -2021,13 +2023,15 @@ config PCI_GOBIOS + + config PCI_GOMMCONFIG + bool "MMConfig" ++ depends on !XEN_UNPRIVILEGED_GUEST + + config PCI_GODIRECT + bool "Direct" ++ depends on !XEN_UNPRIVILEGED_GUEST + + config PCI_GOOLPC + bool "OLPC" +- depends on OLPC ++ depends on OLPC && !XEN_UNPRIVILEGED_GUEST + + config PCI_GOXEN_FE + bool "Xen PCI Frontend" +@@ -2038,6 +2042,7 @@ config PCI_GOXEN_FE + + config PCI_GOANY + bool "Any" ++ depends on !XEN_UNPRIVILEGED_GUEST + + endchoice + +@@ -2068,7 +2073,7 @@ config PCI_MMCONFIG + + config XEN_PCIDEV_FRONTEND + def_bool y +- prompt "Xen PCI Frontend" if X86_64 ++ prompt "Xen PCI Frontend" if X86_64 && !XEN_UNPRIVILEGED_GUEST + depends on PCI && XEN && (PCI_GOXEN_FE || PCI_GOANY || X86_64) + select HOTPLUG + help +@@ -2213,7 +2218,7 @@ endif # X86_32 + + config K8_NB + def_bool y +- depends on CPU_SUP_AMD && PCI ++ depends on CPU_SUP_AMD && PCI && !XEN_UNPRIVILEGED_GUEST + + source "drivers/pcmcia/Kconfig" + +@@ -2264,7 +2269,9 @@ source "net/Kconfig" + + source "drivers/Kconfig" + ++if !XEN_UNPRIVILEGED_GUEST + source "drivers/firmware/Kconfig" ++endif + + source "fs/Kconfig" + +--- head-2010-05-12.orig/arch/x86/include/mach-xen/asm/swiotlb.h 2010-03-24 16:00:05.000000000 +0100 ++++ head-2010-05-12/arch/x86/include/mach-xen/asm/swiotlb.h 2010-03-25 14:39:33.000000000 +0100 +@@ -1,6 +1,10 @@ + #include_next + ++#ifdef CONFIG_SWIOTLB + #define pci_swiotlb_detect() 1 ++#else ++#define swiotlb_init(verbose) ((void)(verbose)) ++#endif + + dma_addr_t swiotlb_map_single_phys(struct device *, phys_addr_t, size_t size, + int dir); +--- head-2010-05-12.orig/drivers/firmware/Kconfig 2009-10-21 12:05:13.000000000 +0200 ++++ head-2010-05-12/drivers/firmware/Kconfig 2010-04-28 17:21:34.000000000 +0200 +@@ -115,7 +115,7 @@ config DMIID + + config ISCSI_IBFT_FIND + bool "iSCSI Boot Firmware Table Attributes" +- depends on X86 && !XEN_UNPRIVILEGED_GUEST ++ depends on X86 + default n + help + This option enables the kernel to find the region of memory +--- head-2010-05-12.orig/drivers/xen/Kconfig 2010-03-31 14:11:36.000000000 +0200 ++++ head-2010-05-12/drivers/xen/Kconfig 2010-03-31 14:12:07.000000000 +0200 +@@ -275,6 +275,7 @@ config XEN_USB_FRONTEND_HCD_PM + + config XEN_GRANT_DEV + tristate "User-space granted page access driver" ++ depends on XEN_BACKEND != n + default XEN_PRIVILEGED_GUEST + help + Device for accessing (in user-space) pages that have been granted +--- head-2010-05-12.orig/drivers/xen/balloon/balloon.c 2010-02-03 11:56:18.000000000 +0100 ++++ head-2010-05-12/drivers/xen/balloon/balloon.c 2010-04-15 11:44:37.000000000 +0200 +@@ -663,6 +663,9 @@ void balloon_update_driver_allowance(lon + bs.driver_pages += delta; + balloon_unlock(flags); + } ++EXPORT_SYMBOL_GPL(balloon_update_driver_allowance); ++ ++#if defined(CONFIG_XEN_BACKEND) || defined(CONFIG_XEN_BACKEND_MODULE) + + #ifdef CONFIG_XEN + static int dealloc_pte_fn( +@@ -771,6 +774,7 @@ struct page **alloc_empty_pages_and_page + pagevec = NULL; + goto out; + } ++EXPORT_SYMBOL_GPL(alloc_empty_pages_and_pagevec); + + void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages) + { +@@ -791,6 +795,9 @@ void free_empty_pages_and_pagevec(struct + + schedule_work(&balloon_worker); + } ++EXPORT_SYMBOL_GPL(free_empty_pages_and_pagevec); ++ ++#endif /* CONFIG_XEN_BACKEND */ + + void balloon_release_driver_page(struct page *page) + { +@@ -804,10 +811,6 @@ void balloon_release_driver_page(struct + + schedule_work(&balloon_worker); + } +- +-EXPORT_SYMBOL_GPL(balloon_update_driver_allowance); +-EXPORT_SYMBOL_GPL(alloc_empty_pages_and_pagevec); +-EXPORT_SYMBOL_GPL(free_empty_pages_and_pagevec); + EXPORT_SYMBOL_GPL(balloon_release_driver_page); + + MODULE_LICENSE("Dual BSD/GPL"); +--- head-2010-05-12.orig/drivers/xen/core/Makefile 2010-03-25 14:39:15.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/Makefile 2010-03-25 14:39:33.000000000 +0100 +@@ -2,9 +2,10 @@ + # Makefile for the linux kernel. + # + +-obj-y := evtchn.o gnttab.o reboot.o machine_reboot.o firmware.o ++obj-y := evtchn.o gnttab.o reboot.o machine_reboot.o + +-obj-$(CONFIG_PCI) += pci.o ++priv-$(CONFIG_PCI) += pci.o ++obj-$(CONFIG_XEN_PRIVILEGED_GUEST) += firmware.o $(priv-y) + obj-$(CONFIG_PROC_FS) += xen_proc.o + obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor_sysfs.o + obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o +--- head-2010-05-12.orig/drivers/xen/core/gnttab.c 2010-04-15 11:44:26.000000000 +0200 ++++ head-2010-05-12/drivers/xen/core/gnttab.c 2010-04-15 11:44:35.000000000 +0200 +@@ -438,8 +438,6 @@ static inline unsigned int max_nr_grant_ + + #ifdef CONFIG_XEN + +-static DEFINE_SEQLOCK(gnttab_dma_lock); +- + #ifdef CONFIG_X86 + static int map_pte_fn(pte_t *pte, struct page *pmd_page, + unsigned long addr, void *data) +@@ -509,6 +507,10 @@ static int gnttab_map(unsigned int start + return 0; + } + ++#if defined(CONFIG_XEN_BACKEND) || defined(CONFIG_XEN_BACKEND_MODULE) ++ ++static DEFINE_SEQLOCK(gnttab_dma_lock); ++ + static void gnttab_page_free(struct page *page, unsigned int order) + { + BUG_ON(order); +@@ -640,6 +642,8 @@ void __gnttab_dma_map_page(struct page * + } while (unlikely(read_seqretry(&gnttab_dma_lock, seq))); + } + ++#endif /* CONFIG_XEN_BACKEND */ ++ + #ifdef __HAVE_ARCH_PTE_SPECIAL + + static unsigned int GNTMAP_pte_special; +--- head-2010-05-12.orig/drivers/xen/privcmd/Makefile 2007-07-10 09:42:30.000000000 +0200 ++++ head-2010-05-12/drivers/xen/privcmd/Makefile 2010-03-25 14:39:33.000000000 +0100 +@@ -1,3 +1,3 @@ +- +-obj-y += privcmd.o +-obj-$(CONFIG_COMPAT) += compat_privcmd.o ++priv-$(CONFIG_COMPAT) := compat_privcmd.o ++obj-y := privcmd.o ++obj-$(CONFIG_XEN_PRIVILEGED_GUEST) += $(priv-y) +--- head-2010-05-12.orig/drivers/xen/privcmd/privcmd.c 2010-03-24 15:12:36.000000000 +0100 ++++ head-2010-05-12/drivers/xen/privcmd/privcmd.c 2010-03-25 14:39:33.000000000 +0100 +@@ -33,6 +33,9 @@ + static struct proc_dir_entry *privcmd_intf; + static struct proc_dir_entry *capabilities_intf; + ++#ifndef CONFIG_XEN_PRIVILEGED_GUEST ++#define HAVE_ARCH_PRIVCMD_MMAP ++#endif + #ifndef HAVE_ARCH_PRIVCMD_MMAP + static int enforce_singleshot_mapping_fn(pte_t *pte, struct page *pmd_page, + unsigned long addr, void *data) +@@ -57,12 +60,14 @@ static long privcmd_ioctl(struct file *f + { + long ret; + void __user *udata = (void __user *) data; ++#ifdef CONFIG_XEN_PRIVILEGED_GUEST + unsigned long i, addr, nr, nr_pages; + int paged_out; + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + LIST_HEAD(pagelist); + struct list_head *l, *l2; ++#endif + + switch (cmd) { + case IOCTL_PRIVCMD_HYPERCALL: { +@@ -87,6 +92,8 @@ static long privcmd_ioctl(struct file *f + } + break; + ++#ifdef CONFIG_XEN_PRIVILEGED_GUEST ++ + case IOCTL_PRIVCMD_MMAP: { + #define MMAP_NR_PER_PAGE \ + (unsigned long)((PAGE_SIZE - sizeof(*l)) / sizeof(*msg)) +@@ -392,6 +399,8 @@ static long privcmd_ioctl(struct file *f + } + break; + ++#endif /* CONFIG_XEN_PRIVILEGED_GUEST */ ++ + default: + ret = -EINVAL; + break; +@@ -427,7 +436,9 @@ static int privcmd_mmap(struct file * fi + + static const struct file_operations privcmd_file_ops = { + .unlocked_ioctl = privcmd_ioctl, ++#ifdef CONFIG_XEN_PRIVILEGED_GUEST + .mmap = privcmd_mmap, ++#endif + }; + + static int capabilities_read(char *page, char **start, off_t off, +--- head-2010-05-12.orig/fs/compat_ioctl.c 2010-05-12 09:02:56.000000000 +0200 ++++ head-2010-05-12/fs/compat_ioctl.c 2010-05-12 09:15:02.000000000 +0200 +@@ -1602,7 +1602,7 @@ static long do_ioctl_trans(int fd, unsig + return do_video_stillpicture(fd, cmd, argp); + case VIDEO_SET_SPU_PALETTE: + return do_video_set_spu_palette(fd, cmd, argp); +-#ifdef CONFIG_XEN ++#ifdef CONFIG_XEN_PRIVILEGED_GUEST + case IOCTL_PRIVCMD_MMAP_32: + case IOCTL_PRIVCMD_MMAPBATCH_32: + case IOCTL_PRIVCMD_MMAPBATCH_V2_32: +--- head-2010-05-12.orig/include/xen/firmware.h 2007-07-02 08:16:19.000000000 +0200 ++++ head-2010-05-12/include/xen/firmware.h 2010-03-25 14:39:33.000000000 +0100 +@@ -5,6 +5,10 @@ + void copy_edd(void); + #endif + ++#ifdef CONFIG_XEN_PRIVILEGED_GUEST + void copy_edid(void); ++#else ++static inline void copy_edid(void) {} ++#endif + + #endif /* __XEN_FIRMWARE_H__ */ +--- head-2010-05-12.orig/include/xen/gnttab.h 2009-11-06 11:10:15.000000000 +0100 ++++ head-2010-05-12/include/xen/gnttab.h 2010-03-25 14:39:33.000000000 +0100 +@@ -103,7 +103,11 @@ void gnttab_grant_foreign_transfer_ref(g + unsigned long pfn); + + int gnttab_copy_grant_page(grant_ref_t ref, struct page **pagep); ++#if defined(CONFIG_XEN_BACKEND) || defined(CONFIG_XEN_BACKEND_MODULE) + void __gnttab_dma_map_page(struct page *page); ++#else ++#define __gnttab_dma_map_page __gnttab_dma_unmap_page ++#endif + static inline void __gnttab_dma_unmap_page(struct page *page) + { + } diff --git a/patches.xen/xen-virq-per-cpu-irq b/patches.xen/xen-virq-per-cpu-irq new file mode 100644 index 0000000..7e3279d --- /dev/null +++ b/patches.xen/xen-virq-per-cpu-irq @@ -0,0 +1,649 @@ +From: jbeulich@novell.com +Subject: fold per-CPU VIRQs onto a single IRQ each +Patch-mainline: obsolete + +--- head-2010-05-12.orig/arch/x86/kernel/time-xen.c 2010-05-12 09:14:03.000000000 +0200 ++++ head-2010-05-12/arch/x86/kernel/time-xen.c 2010-05-12 09:14:09.000000000 +0200 +@@ -699,19 +699,17 @@ int xen_update_persistent_clock(void) + } + + /* Dynamically-mapped IRQ. */ +-DEFINE_PER_CPU(int, timer_irq); ++static int __read_mostly timer_irq = -1; ++static struct irqaction timer_action = { ++ .handler = timer_interrupt, ++ .flags = IRQF_DISABLED|IRQF_TIMER, ++ .name = "timer" ++}; + + static void __init setup_cpu0_timer_irq(void) + { +- per_cpu(timer_irq, 0) = +- bind_virq_to_irqhandler( +- VIRQ_TIMER, +- 0, +- timer_interrupt, +- IRQF_DISABLED|IRQF_TIMER|IRQF_NOBALANCING, +- "timer0", +- NULL); +- BUG_ON(per_cpu(timer_irq, 0) < 0); ++ timer_irq = bind_virq_to_irqaction(VIRQ_TIMER, 0, &timer_action); ++ BUG_ON(timer_irq < 0); + } + + void __init time_init(void) +@@ -852,8 +850,6 @@ void xen_halt(void) + EXPORT_SYMBOL(xen_halt); + + #ifdef CONFIG_SMP +-static char timer_name[NR_CPUS][15]; +- + int __cpuinit local_setup_timer(unsigned int cpu) + { + int seq, irq; +@@ -879,16 +875,10 @@ int __cpuinit local_setup_timer(unsigned + init_missing_ticks_accounting(cpu); + } while (read_seqretry(&xtime_lock, seq)); + +- sprintf(timer_name[cpu], "timer%u", cpu); +- irq = bind_virq_to_irqhandler(VIRQ_TIMER, +- cpu, +- timer_interrupt, +- IRQF_DISABLED|IRQF_TIMER|IRQF_NOBALANCING, +- timer_name[cpu], +- NULL); ++ irq = bind_virq_to_irqaction(VIRQ_TIMER, cpu, &timer_action); + if (irq < 0) + return irq; +- per_cpu(timer_irq, cpu) = irq; ++ BUG_ON(timer_irq != irq); + + return 0; + } +@@ -896,7 +886,7 @@ int __cpuinit local_setup_timer(unsigned + void __cpuinit local_teardown_timer(unsigned int cpu) + { + BUG_ON(cpu == 0); +- unbind_from_irqhandler(per_cpu(timer_irq, cpu), NULL); ++ unbind_from_per_cpu_irq(timer_irq, cpu, &timer_action); + } + #endif + +--- head-2010-05-12.orig/drivers/xen/core/evtchn.c 2010-04-23 15:20:31.000000000 +0200 ++++ head-2010-05-12/drivers/xen/core/evtchn.c 2010-04-23 15:20:36.000000000 +0200 +@@ -59,6 +59,23 @@ static DEFINE_SPINLOCK(irq_mapping_updat + static int evtchn_to_irq[NR_EVENT_CHANNELS] = { + [0 ... NR_EVENT_CHANNELS-1] = -1 }; + ++#if defined(CONFIG_SMP) && defined(CONFIG_X86) ++static struct per_cpu_irqaction { ++ struct irqaction action; /* must be first */ ++ struct per_cpu_irqaction *next; ++ cpumask_t cpus; ++} *virq_actions[NR_VIRQS]; ++/* IRQ <-> VIRQ mapping. */ ++static DECLARE_BITMAP(virq_per_cpu, NR_VIRQS) __read_mostly; ++static DEFINE_PER_CPU(int[NR_VIRQS], virq_to_evtchn); ++#define BUG_IF_VIRQ_PER_CPU(irq) \ ++ BUG_ON(type_from_irq(irq) == IRQT_VIRQ \ ++ && test_bit(index_from_irq(irq), virq_per_cpu)) ++#else ++#define BUG_IF_VIRQ_PER_CPU(irq) ((void)(irq)) ++#define PER_CPU_VIRQ_IRQ ++#endif ++ + /* IRQ <-> IPI mapping. */ + #ifndef NR_IPIS + #define NR_IPIS 1 +@@ -133,15 +150,6 @@ static inline u32 mk_irq_info(u32 type, + * Accessors for packed IRQ information. + */ + +-#ifdef PER_CPU_IPI_IRQ +-static inline unsigned int evtchn_from_irq(int irq) +-{ +- const struct irq_cfg *cfg = irq_cfg(irq); +- +- return cfg ? cfg->info & ((1U << _EVTCHN_BITS) - 1) : 0; +-} +-#endif +- + static inline unsigned int index_from_irq(int irq) + { + const struct irq_cfg *cfg = irq_cfg(irq); +@@ -157,24 +165,39 @@ static inline unsigned int type_from_irq + return cfg ? cfg->info >> (32 - _IRQT_BITS) : IRQT_UNBOUND; + } + +-#ifndef PER_CPU_IPI_IRQ + static inline unsigned int evtchn_from_per_cpu_irq(unsigned int irq, + unsigned int cpu) + { +- BUG_ON(type_from_irq(irq) != IRQT_IPI); +- return per_cpu(ipi_to_evtchn, cpu)[index_from_irq(irq)]; ++ switch (type_from_irq(irq)) { ++#ifndef PER_CPU_VIRQ_IRQ ++ case IRQT_VIRQ: ++ return per_cpu(virq_to_evtchn, cpu)[index_from_irq(irq)]; ++#endif ++#ifndef PER_CPU_IPI_IRQ ++ case IRQT_IPI: ++ return per_cpu(ipi_to_evtchn, cpu)[index_from_irq(irq)]; ++#endif ++ } ++ BUG(); ++ return 0; + } + + static inline unsigned int evtchn_from_irq(unsigned int irq) + { +- if (type_from_irq(irq) != IRQT_IPI) { +- const struct irq_cfg *cfg = irq_cfg(irq); ++ const struct irq_cfg *cfg; + +- return cfg ? cfg->info & ((1U << _EVTCHN_BITS) - 1) : 0; ++ switch (type_from_irq(irq)) { ++#ifndef PER_CPU_VIRQ_IRQ ++ case IRQT_VIRQ: ++#endif ++#ifndef PER_CPU_IPI_IRQ ++ case IRQT_IPI: ++#endif ++ return evtchn_from_per_cpu_irq(irq, smp_processor_id()); + } +- return evtchn_from_per_cpu_irq(irq, smp_processor_id()); ++ cfg = irq_cfg(irq); ++ return cfg ? cfg->info & ((1U << _EVTCHN_BITS) - 1) : 0; + } +-#endif + + unsigned int irq_from_evtchn(unsigned int port) + { +@@ -522,6 +545,14 @@ static int bind_virq_to_irq(unsigned int + evtchn = bind_virq.port; + + evtchn_to_irq[evtchn] = irq; ++#ifndef PER_CPU_VIRQ_IRQ ++ { ++ unsigned int cpu; ++ ++ for_each_possible_cpu(cpu) ++ per_cpu(virq_to_evtchn, cpu)[virq] = evtchn; ++ } ++#endif + irq_cfg(irq)->info = mk_irq_info(IRQT_VIRQ, virq, evtchn); + + per_cpu(virq_to_irq, cpu)[virq] = irq; +@@ -576,7 +607,9 @@ static void unbind_from_irq(unsigned int + unsigned int cpu; + int evtchn = evtchn_from_irq(irq); + ++ BUG_IF_VIRQ_PER_CPU(irq); + BUG_IF_IPI(irq); ++ + spin_lock(&irq_mapping_update_lock); + + if (!--irq_cfg(irq)->bindcount && VALID_EVTCHN(evtchn)) { +@@ -589,6 +622,11 @@ static void unbind_from_irq(unsigned int + case IRQT_VIRQ: + per_cpu(virq_to_irq, cpu_from_evtchn(evtchn)) + [index_from_irq(irq)] = -1; ++#ifndef PER_CPU_VIRQ_IRQ ++ for_each_possible_cpu(cpu) ++ per_cpu(virq_to_evtchn, cpu) ++ [index_from_irq(irq)] = 0; ++#endif + break; + #if defined(CONFIG_SMP) && defined(PER_CPU_IPI_IRQ) + case IRQT_IPI: +@@ -618,11 +656,13 @@ static void unbind_from_irq(unsigned int + spin_unlock(&irq_mapping_update_lock); + } + +-#if defined(CONFIG_SMP) && !defined(PER_CPU_IPI_IRQ) +-void unbind_from_per_cpu_irq(unsigned int irq, unsigned int cpu) ++#if defined(CONFIG_SMP) && (!defined(PER_CPU_IPI_IRQ) || !defined(PER_CPU_VIRQ_IRQ)) ++void unbind_from_per_cpu_irq(unsigned int irq, unsigned int cpu, ++ struct irqaction *action) + { + struct evtchn_close close; + int evtchn = evtchn_from_per_cpu_irq(irq, cpu); ++ struct irqaction *free_action = NULL; + + spin_lock(&irq_mapping_update_lock); + +@@ -633,6 +673,32 @@ void unbind_from_per_cpu_irq(unsigned in + + BUG_ON(irq_cfg(irq)->bindcount <= 1); + irq_cfg(irq)->bindcount--; ++ ++#ifndef PER_CPU_VIRQ_IRQ ++ if (type_from_irq(irq) == IRQT_VIRQ) { ++ unsigned int virq = index_from_irq(irq); ++ struct per_cpu_irqaction *cur, *prev = NULL; ++ ++ cur = virq_actions[virq]; ++ while (cur) { ++ if (cur->action.dev_id == action) { ++ cpu_clear(cpu, cur->cpus); ++ if (cpus_empty(cur->cpus)) { ++ if (prev) ++ prev->next = cur->next; ++ else ++ virq_actions[virq] = cur->next; ++ free_action = action; ++ } ++ } else if (cpu_isset(cpu, cur->cpus)) ++ evtchn = 0; ++ cur = (prev = cur)->next; ++ } ++ if (!VALID_EVTCHN(evtchn)) ++ goto done; ++ } ++#endif ++ + cpumask_clear_cpu(cpu, desc->affinity); + + close.port = evtchn; +@@ -640,9 +706,16 @@ void unbind_from_per_cpu_irq(unsigned in + BUG(); + + switch (type_from_irq(irq)) { ++#ifndef PER_CPU_VIRQ_IRQ ++ case IRQT_VIRQ: ++ per_cpu(virq_to_evtchn, cpu)[index_from_irq(irq)] = 0; ++ break; ++#endif ++#ifndef PER_CPU_IPI_IRQ + case IRQT_IPI: + per_cpu(ipi_to_evtchn, cpu)[index_from_irq(irq)] = 0; + break; ++#endif + default: + BUG(); + break; +@@ -654,9 +727,16 @@ void unbind_from_per_cpu_irq(unsigned in + evtchn_to_irq[evtchn] = -1; + } + ++#ifndef PER_CPU_VIRQ_IRQ ++done: ++#endif + spin_unlock(&irq_mapping_update_lock); ++ ++ if (free_action) ++ free_irq(irq, free_action); + } +-#endif /* CONFIG_SMP && !PER_CPU_IPI_IRQ */ ++EXPORT_SYMBOL_GPL(unbind_from_per_cpu_irq); ++#endif /* CONFIG_SMP && (!PER_CPU_IPI_IRQ || !PER_CPU_VIRQ_IRQ) */ + + int bind_caller_port_to_irqhandler( + unsigned int caller_port, +@@ -738,6 +818,8 @@ int bind_virq_to_irqhandler( + { + int irq, retval; + ++ BUG_IF_VIRQ_PER_CPU(virq); ++ + irq = bind_virq_to_irq(virq, cpu); + if (irq < 0) + return irq; +@@ -753,6 +835,108 @@ int bind_virq_to_irqhandler( + EXPORT_SYMBOL_GPL(bind_virq_to_irqhandler); + + #ifdef CONFIG_SMP ++#ifndef PER_CPU_VIRQ_IRQ ++int bind_virq_to_irqaction( ++ unsigned int virq, ++ unsigned int cpu, ++ struct irqaction *action) ++{ ++ struct evtchn_bind_virq bind_virq; ++ int evtchn, irq, retval = 0; ++ struct per_cpu_irqaction *cur = NULL, *new; ++ ++ BUG_ON(!test_bit(virq, virq_per_cpu)); ++ ++ if (action->dev_id) ++ return -EINVAL; ++ ++ new = kzalloc(sizeof(*new), GFP_ATOMIC); ++ if (new) { ++ new->action = *action; ++ new->action.dev_id = action; ++ } ++ ++ spin_lock(&irq_mapping_update_lock); ++ ++ for (cur = virq_actions[virq]; cur; cur = cur->next) ++ if (cur->action.dev_id == action) ++ break; ++ if (!cur) { ++ if (!new) { ++ spin_unlock(&irq_mapping_update_lock); ++ return -ENOMEM; ++ } ++ new->next = virq_actions[virq]; ++ virq_actions[virq] = cur = new; ++ retval = 1; ++ } ++ cpu_set(cpu, cur->cpus); ++ action = &cur->action; ++ ++ if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) { ++ unsigned int nr; ++ ++ BUG_ON(!retval); ++ ++ if ((irq = find_unbound_irq(cpu, true)) < 0) { ++ if (cur) ++ virq_actions[virq] = cur->next; ++ spin_unlock(&irq_mapping_update_lock); ++ if (cur != new) ++ kfree(new); ++ return irq; ++ } ++ ++ /* Extra reference so count will never drop to zero. */ ++ irq_cfg(irq)->bindcount++; ++ ++ for_each_possible_cpu(nr) ++ per_cpu(virq_to_irq, nr)[virq] = irq; ++ irq_cfg(irq)->info = mk_irq_info(IRQT_VIRQ, virq, 0); ++ } ++ ++ evtchn = per_cpu(virq_to_evtchn, cpu)[virq]; ++ if (!VALID_EVTCHN(evtchn)) { ++ bind_virq.virq = virq; ++ bind_virq.vcpu = cpu; ++ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, ++ &bind_virq) != 0) ++ BUG(); ++ evtchn = bind_virq.port; ++ evtchn_to_irq[evtchn] = irq; ++ per_cpu(virq_to_evtchn, cpu)[virq] = evtchn; ++ ++ bind_evtchn_to_cpu(evtchn, cpu); ++ } ++ ++ irq_cfg(irq)->bindcount++; ++ ++ spin_unlock(&irq_mapping_update_lock); ++ ++ if (cur != new) ++ kfree(new); ++ ++ if (retval == 0) { ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ unmask_evtchn(evtchn); ++ local_irq_restore(flags); ++ } else { ++ action->flags |= IRQF_PERCPU; ++ retval = setup_irq(irq, action); ++ if (retval) { ++ unbind_from_per_cpu_irq(irq, cpu, cur->action.dev_id); ++ BUG_ON(retval > 0); ++ irq = retval; ++ } ++ } ++ ++ return irq; ++} ++EXPORT_SYMBOL_GPL(bind_virq_to_irqaction); ++#endif ++ + #ifdef PER_CPU_IPI_IRQ + int bind_ipi_to_irqhandler( + unsigned int ipi, +@@ -832,7 +1016,7 @@ int __cpuinit bind_ipi_to_irqaction( + action->flags |= IRQF_PERCPU | IRQF_NO_SUSPEND; + retval = setup_irq(irq, action); + if (retval) { +- unbind_from_per_cpu_irq(irq, cpu); ++ unbind_from_per_cpu_irq(irq, cpu, NULL); + BUG_ON(retval > 0); + irq = retval; + } +@@ -867,7 +1051,9 @@ static void rebind_irq_to_cpu(unsigned i + { + int evtchn = evtchn_from_irq(irq); + ++ BUG_IF_VIRQ_PER_CPU(irq); + BUG_IF_IPI(irq); ++ + if (VALID_EVTCHN(evtchn)) + rebind_evtchn_to_cpu(evtchn, tcpu); + } +@@ -1142,7 +1328,9 @@ void notify_remote_via_irq(int irq) + { + int evtchn = evtchn_from_irq(irq); + ++ BUG_ON(type_from_irq(irq) == IRQT_VIRQ); + BUG_IF_IPI(irq); ++ + if (VALID_EVTCHN(evtchn)) + notify_remote_via_evtchn(evtchn); + } +@@ -1150,6 +1338,7 @@ EXPORT_SYMBOL_GPL(notify_remote_via_irq) + + int irq_to_evtchn_port(int irq) + { ++ BUG_IF_VIRQ_PER_CPU(irq); + BUG_IF_IPI(irq); + return evtchn_from_irq(irq); + } +@@ -1244,6 +1433,12 @@ static void restore_cpu_virqs(unsigned i + if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) + continue; + ++#ifndef PER_CPU_VIRQ_IRQ ++ if (test_bit(virq, virq_per_cpu) ++ && !VALID_EVTCHN(per_cpu(virq_to_evtchn, cpu)[virq])) ++ continue; ++#endif ++ + BUG_ON(irq_cfg(irq)->info != mk_irq_info(IRQT_VIRQ, virq, 0)); + + /* Get a new binding from Xen. */ +@@ -1256,7 +1451,20 @@ static void restore_cpu_virqs(unsigned i + + /* Record the new mapping. */ + evtchn_to_irq[evtchn] = irq; ++#ifdef PER_CPU_VIRQ_IRQ + irq_cfg(irq)->info = mk_irq_info(IRQT_VIRQ, virq, evtchn); ++#else ++ if (test_bit(virq, virq_per_cpu)) ++ per_cpu(virq_to_evtchn, cpu)[virq] = evtchn; ++ else { ++ unsigned int cpu; ++ ++ irq_cfg(irq)->info = mk_irq_info(IRQT_VIRQ, virq, ++ evtchn); ++ for_each_possible_cpu(cpu) ++ per_cpu(virq_to_evtchn, cpu)[virq] = evtchn; ++ } ++#endif + bind_evtchn_to_cpu(evtchn, cpu); + + /* Ready for use. */ +@@ -1312,7 +1520,11 @@ static int evtchn_resume(struct sys_devi + + /* Avoid doing anything in the 'suspend cancelled' case. */ + status.dom = DOMID_SELF; ++#ifdef PER_CPU_VIRQ_IRQ + status.port = evtchn_from_irq(percpu_read(virq_to_irq[VIRQ_TIMER])); ++#else ++ status.port = percpu_read(virq_to_evtchn[VIRQ_TIMER]); ++#endif + if (HYPERVISOR_event_channel_op(EVTCHNOP_status, &status)) + BUG(); + if (status.status == EVTCHNSTAT_virq +@@ -1541,6 +1753,15 @@ void __init xen_init_IRQ(void) + unsigned int i; + struct physdev_pirq_eoi_gmfn eoi_gmfn; + ++#ifndef PER_CPU_VIRQ_IRQ ++ __set_bit(VIRQ_TIMER, virq_per_cpu); ++ __set_bit(VIRQ_DEBUG, virq_per_cpu); ++ __set_bit(VIRQ_XENOPROF, virq_per_cpu); ++#ifdef CONFIG_IA64 ++ __set_bit(VIRQ_ITC, virq_per_cpu); ++#endif ++#endif ++ + init_evtchn_cpu_bindings(); + + i = get_order(sizeof(unsigned long) * BITS_TO_LONGS(nr_pirqs)); +--- head-2010-05-12.orig/drivers/xen/core/smpboot.c 2010-03-19 15:20:24.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/smpboot.c 2010-03-19 15:20:27.000000000 +0100 +@@ -176,13 +176,13 @@ static int __cpuinit xen_smp_intr_init(u + fail: + xen_spinlock_cleanup(cpu); + unbind_reboot: +- unbind_from_per_cpu_irq(reboot_irq, cpu); ++ unbind_from_per_cpu_irq(reboot_irq, cpu, NULL); + unbind_call1: +- unbind_from_per_cpu_irq(call1func_irq, cpu); ++ unbind_from_per_cpu_irq(call1func_irq, cpu, NULL); + unbind_call: +- unbind_from_per_cpu_irq(callfunc_irq, cpu); ++ unbind_from_per_cpu_irq(callfunc_irq, cpu, NULL); + unbind_resched: +- unbind_from_per_cpu_irq(resched_irq, cpu); ++ unbind_from_per_cpu_irq(resched_irq, cpu, NULL); + return rc; + } + +@@ -192,10 +192,10 @@ static void __cpuinit xen_smp_intr_exit( + if (cpu != 0) + local_teardown_timer(cpu); + +- unbind_from_per_cpu_irq(resched_irq, cpu); +- unbind_from_per_cpu_irq(callfunc_irq, cpu); +- unbind_from_per_cpu_irq(call1func_irq, cpu); +- unbind_from_per_cpu_irq(reboot_irq, cpu); ++ unbind_from_per_cpu_irq(resched_irq, cpu, NULL); ++ unbind_from_per_cpu_irq(callfunc_irq, cpu, NULL); ++ unbind_from_per_cpu_irq(call1func_irq, cpu, NULL); ++ unbind_from_per_cpu_irq(reboot_irq, cpu, NULL); + xen_spinlock_cleanup(cpu); + } + #endif +--- head-2010-05-12.orig/drivers/xen/core/spinlock.c 2010-02-24 12:38:00.000000000 +0100 ++++ head-2010-05-12/drivers/xen/core/spinlock.c 2010-02-24 12:38:54.000000000 +0100 +@@ -55,7 +55,7 @@ int __cpuinit xen_spinlock_init(unsigned + + void __cpuinit xen_spinlock_cleanup(unsigned int cpu) + { +- unbind_from_per_cpu_irq(spinlock_irq, cpu); ++ unbind_from_per_cpu_irq(spinlock_irq, cpu, NULL); + } + + static unsigned int spin_adjust(struct spinning *spinning, +--- head-2010-05-12.orig/drivers/xen/netback/netback.c 2010-03-24 15:32:27.000000000 +0100 ++++ head-2010-05-12/drivers/xen/netback/netback.c 2010-01-04 13:31:26.000000000 +0100 +@@ -1619,6 +1619,12 @@ static irqreturn_t netif_be_dbg(int irq, + + return IRQ_HANDLED; + } ++ ++static struct irqaction netif_be_dbg_action = { ++ .handler = netif_be_dbg, ++ .flags = IRQF_SHARED, ++ .name = "net-be-dbg" ++}; + #endif + + static int __init netback_init(void) +@@ -1678,12 +1684,9 @@ static int __init netback_init(void) + netif_xenbus_init(); + + #ifdef NETBE_DEBUG_INTERRUPT +- (void)bind_virq_to_irqhandler(VIRQ_DEBUG, +- 0, +- netif_be_dbg, +- IRQF_SHARED, +- "net-be-dbg", +- &netif_be_dbg); ++ (void)bind_virq_to_irqaction(VIRQ_DEBUG, ++ 0, ++ &netif_be_dbg_action); + #endif + + return 0; +--- head-2010-05-12.orig/drivers/xen/xenoprof/xenoprofile.c 2010-03-24 15:17:58.000000000 +0100 ++++ head-2010-05-12/drivers/xen/xenoprof/xenoprofile.c 2010-01-07 11:04:10.000000000 +0100 +@@ -210,6 +210,11 @@ static irqreturn_t xenoprof_ovf_interrup + return IRQ_HANDLED; + } + ++static struct irqaction ovf_action = { ++ .handler = xenoprof_ovf_interrupt, ++ .flags = IRQF_DISABLED, ++ .name = "xenoprof" ++}; + + static void unbind_virq(void) + { +@@ -217,7 +222,7 @@ static void unbind_virq(void) + + for_each_online_cpu(i) { + if (ovf_irq[i] >= 0) { +- unbind_from_irqhandler(ovf_irq[i], NULL); ++ unbind_from_per_cpu_irq(ovf_irq[i], i, &ovf_action); + ovf_irq[i] = -1; + } + } +@@ -230,12 +235,7 @@ static int bind_virq(void) + int result; + + for_each_online_cpu(i) { +- result = bind_virq_to_irqhandler(VIRQ_XENOPROF, +- i, +- xenoprof_ovf_interrupt, +- IRQF_DISABLED|IRQF_NOBALANCING, +- "xenoprof", +- NULL); ++ result = bind_virq_to_irqaction(VIRQ_XENOPROF, i, &ovf_action); + + if (result < 0) { + unbind_virq(); +--- head-2010-05-12.orig/include/xen/evtchn.h 2010-03-31 14:41:42.000000000 +0200 ++++ head-2010-05-12/include/xen/evtchn.h 2010-03-31 14:11:09.000000000 +0200 +@@ -93,6 +93,17 @@ int bind_virq_to_irqhandler( + unsigned long irqflags, + const char *devname, + void *dev_id); ++#if defined(CONFIG_SMP) && defined(CONFIG_XEN) && defined(CONFIG_X86) ++int bind_virq_to_irqaction( ++ unsigned int virq, ++ unsigned int cpu, ++ struct irqaction *action); ++#else ++#define bind_virq_to_irqaction(virq, cpu, action) \ ++ bind_virq_to_irqhandler(virq, cpu, (action)->handler, \ ++ (action)->flags | IRQF_NOBALANCING, \ ++ (action)->name, action) ++#endif + #if defined(CONFIG_SMP) && !defined(MODULE) + #ifndef CONFIG_X86 + int bind_ipi_to_irqhandler( +@@ -117,9 +128,13 @@ int bind_ipi_to_irqaction( + */ + void unbind_from_irqhandler(unsigned int irq, void *dev_id); + +-#if defined(CONFIG_SMP) && !defined(MODULE) && defined(CONFIG_X86) ++#if defined(CONFIG_SMP) && defined(CONFIG_XEN) && defined(CONFIG_X86) + /* Specialized unbind function for per-CPU IRQs. */ +-void unbind_from_per_cpu_irq(unsigned int irq, unsigned int cpu); ++void unbind_from_per_cpu_irq(unsigned int irq, unsigned int cpu, ++ struct irqaction *); ++#else ++#define unbind_from_per_cpu_irq(irq, cpu, action) \ ++ unbind_from_irqhandler(irq, action) + #endif + + #ifndef CONFIG_XEN diff --git a/patches.xen/xen-x86-bigmem b/patches.xen/xen-x86-bigmem new file mode 100644 index 0000000..62674fd --- /dev/null +++ b/patches.xen/xen-x86-bigmem @@ -0,0 +1,143 @@ +From: jbeulich@novell.com +Subject: fix issues with the assignment of huge amounts of memory +Patch-mainline: obsolete +References: bnc#482614, bnc#537435 + +--- head-2010-04-15.orig/arch/x86/kernel/e820-xen.c 2010-04-15 10:48:32.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/e820-xen.c 2010-04-15 11:48:01.000000000 +0200 +@@ -1093,6 +1093,26 @@ static int __init parse_memopt(char *p) + + userdef = 1; + mem_size = memparse(p, &p); ++#ifdef CONFIG_XEN ++ /* ++ * A little less than 2% of available memory are needed for page ++ * tables, p2m map, and mem_map. Hence the maximum amount of memory ++ * we can potentially balloon up to can in no case exceed about 50 ++ * times of what we've been given initially. Since even with that we ++ * won't be able to boot (due to various calculations done based on ++ * the total number of pages) we further restrict this to factor 32. ++ */ ++ if ((mem_size >> (PAGE_SHIFT + 5)) > xen_start_info->nr_pages) { ++ u64 size = (u64)xen_start_info->nr_pages << 5; ++ ++ printk(KERN_WARNING "mem=%Luk is invalid for an initial" ++ " allocation of %luk, using %Luk\n", ++ (unsigned long long)mem_size >> 10, ++ xen_start_info->nr_pages << (PAGE_SHIFT - 10), ++ (unsigned long long)size << (PAGE_SHIFT - 10)); ++ mem_size = size << PAGE_SHIFT; ++ } ++#endif + e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1); + + i = e820.nr_map - 1; +@@ -1291,6 +1311,7 @@ void __init e820_reserve_resources_late( + char *__init default_machine_specific_memory_setup(void) + { + int rc, nr_map; ++ unsigned long long maxmem; + struct xen_memory_map memmap; + static struct e820entry __initdata map[E820MAX]; + +@@ -1316,6 +1337,22 @@ char *__init default_machine_specific_me + BUG(); + + #ifdef CONFIG_XEN ++ /* See the comment in parse_memopt(). */ ++ for (maxmem = rc = 0; rc < e820.nr_map; ++rc) ++ if (e820.map[rc].type == E820_RAM) ++ maxmem += e820.map[rc].size; ++ if ((maxmem >> (PAGE_SHIFT + 5)) > xen_start_info->nr_pages) { ++ unsigned long long size = (u64)xen_start_info->nr_pages << 5; ++ ++ printk(KERN_WARNING "maxmem of %LuM is invalid for an initial" ++ " allocation of %luM, using %LuM\n", ++ maxmem >> 20, ++ xen_start_info->nr_pages >> (20 - PAGE_SHIFT), ++ size >> (20 - PAGE_SHIFT)); ++ size <<= PAGE_SHIFT; ++ e820_remove_range(size, ULLONG_MAX - size, E820_RAM, 1); ++ } ++ + if (is_initial_xendomain()) { + memmap.nr_entries = E820MAX; + set_xen_guest_handle(memmap.buffer, machine_e820.map); +--- head-2010-04-15.orig/arch/x86/kernel/setup-xen.c 2010-04-15 11:46:02.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/setup-xen.c 2010-04-15 11:48:03.000000000 +0200 +@@ -130,12 +130,7 @@ static struct notifier_block xen_panic_b + unsigned long *phys_to_machine_mapping; + EXPORT_SYMBOL(phys_to_machine_mapping); + +-unsigned long *pfn_to_mfn_frame_list_list, +-#ifdef CONFIG_X86_64 +- *pfn_to_mfn_frame_list[512]; +-#else +- *pfn_to_mfn_frame_list[128]; +-#endif ++unsigned long *pfn_to_mfn_frame_list_list, **pfn_to_mfn_frame_list; + + /* Raw start-of-day parameters from the hypervisor. */ + start_info_t *xen_start_info; +@@ -1167,17 +1162,17 @@ void __init setup_arch(char **cmdline_p) + p2m_pages = xen_start_info->nr_pages; + + if (!xen_feature(XENFEAT_auto_translated_physmap)) { +- unsigned long i, j; ++ unsigned long i, j, size; + unsigned int k, fpp; + + /* Make sure we have a large enough P->M table. */ + phys_to_machine_mapping = alloc_bootmem_pages( + max_pfn * sizeof(unsigned long)); +- memset(phys_to_machine_mapping, ~0, +- max_pfn * sizeof(unsigned long)); + memcpy(phys_to_machine_mapping, + (unsigned long *)xen_start_info->mfn_list, + p2m_pages * sizeof(unsigned long)); ++ memset(phys_to_machine_mapping + p2m_pages, ~0, ++ (max_pfn - p2m_pages) * sizeof(unsigned long)); + free_bootmem( + __pa(xen_start_info->mfn_list), + PFN_PHYS(PFN_UP(xen_start_info->nr_pages * +@@ -1187,15 +1182,26 @@ void __init setup_arch(char **cmdline_p) + * Initialise the list of the frames that specify the list of + * frames that make up the p2m table. Used by save/restore. + */ +- pfn_to_mfn_frame_list_list = alloc_bootmem_pages(PAGE_SIZE); +- + fpp = PAGE_SIZE/sizeof(unsigned long); ++ size = (max_pfn + fpp - 1) / fpp; ++ size = (size + fpp - 1) / fpp; ++ ++size; /* include a zero terminator for crash tools */ ++ size *= sizeof(unsigned long); ++ pfn_to_mfn_frame_list_list = alloc_bootmem_pages(size); ++ if (size > PAGE_SIZE ++ && xen_create_contiguous_region((unsigned long) ++ pfn_to_mfn_frame_list_list, ++ get_order(size), 0)) ++ BUG(); ++ size -= sizeof(unsigned long); ++ pfn_to_mfn_frame_list = alloc_bootmem(size); ++ + for (i = j = 0, k = -1; i < max_pfn; i += fpp, j++) { + if (j == fpp) + j = 0; + if (j == 0) { + k++; +- BUG_ON(k>=ARRAY_SIZE(pfn_to_mfn_frame_list)); ++ BUG_ON(k * sizeof(unsigned long) >= size); + pfn_to_mfn_frame_list[k] = + alloc_bootmem_pages(PAGE_SIZE); + pfn_to_mfn_frame_list_list[k] = +--- head-2010-04-15.orig/drivers/xen/core/machine_reboot.c 2010-03-25 14:39:15.000000000 +0100 ++++ head-2010-04-15/drivers/xen/core/machine_reboot.c 2010-03-25 14:41:06.000000000 +0100 +@@ -80,7 +80,7 @@ static void post_suspend(int suspend_can + unsigned long shinfo_mfn; + extern unsigned long max_pfn; + extern unsigned long *pfn_to_mfn_frame_list_list; +- extern unsigned long *pfn_to_mfn_frame_list[]; ++ extern unsigned long **pfn_to_mfn_frame_list; + + if (suspend_cancelled) { + xen_start_info->store_mfn = diff --git a/patches.xen/xen-x86-consistent-nmi b/patches.xen/xen-x86-consistent-nmi new file mode 100644 index 0000000..1c9c204 --- /dev/null +++ b/patches.xen/xen-x86-consistent-nmi @@ -0,0 +1,247 @@ +From: jbeulich@novell.com +Subject: make i386 and x86 NMI code consistent, disable all APIC-related stuff +Patch-mainline: obsolete +References: 191115 + +--- head-2010-04-15.orig/arch/x86/include/asm/irq.h 2010-04-15 09:37:45.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/asm/irq.h 2010-03-25 14:40:02.000000000 +0100 +@@ -15,7 +15,7 @@ static inline int irq_canonicalize(int i + return ((irq == 2) ? 9 : irq); + } + +-#ifdef CONFIG_X86_LOCAL_APIC ++#if defined(CONFIG_X86_LOCAL_APIC) && !defined(CONFIG_XEN) + # define ARCH_HAS_NMI_WATCHDOG + #endif + +--- head-2010-04-15.orig/arch/x86/include/asm/nmi.h 2010-04-15 09:37:45.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/asm/nmi.h 2010-03-25 14:40:02.000000000 +0100 +@@ -5,8 +5,6 @@ + #include + #include + +-#ifdef ARCH_HAS_NMI_WATCHDOG +- + /** + * do_nmi_callback + * +@@ -16,6 +14,11 @@ + int do_nmi_callback(struct pt_regs *regs, int cpu); + + extern void die_nmi(char *str, struct pt_regs *regs, int do_panic); ++ ++extern int unknown_nmi_panic; ++ ++#ifdef ARCH_HAS_NMI_WATCHDOG ++ + extern int check_nmi_watchdog(void); + extern int nmi_watchdog_enabled; + extern int avail_to_resrv_perfctr_nmi_bit(unsigned int); +@@ -41,7 +44,6 @@ extern unsigned int nmi_watchdog; + struct ctl_table; + extern int proc_nmi_enabled(struct ctl_table *, int , + void __user *, size_t *, loff_t *); +-extern int unknown_nmi_panic; + + void arch_trigger_all_cpu_backtrace(void); + #define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace +@@ -64,7 +66,6 @@ static inline int nmi_watchdog_active(vo + */ + return nmi_watchdog & (NMI_LOCAL_APIC | NMI_IO_APIC); + } +-#endif + + void lapic_watchdog_stop(void); + int lapic_watchdog_init(unsigned nmi_hz); +@@ -72,6 +73,9 @@ int lapic_wd_event(unsigned nmi_hz); + unsigned lapic_adjust_nmi_hz(unsigned hz); + void disable_lapic_nmi_watchdog(void); + void enable_lapic_nmi_watchdog(void); ++ ++#endif ++ + void stop_nmi(void); + void restart_nmi(void); + +--- head-2010-04-15.orig/arch/x86/kernel/apic/Makefile 2010-03-24 16:00:05.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/apic/Makefile 2010-03-25 14:40:02.000000000 +0100 +@@ -18,8 +18,6 @@ obj-$(CONFIG_X86_NUMAQ) += numaq_32.o + obj-$(CONFIG_X86_ES7000) += es7000_32.o + obj-$(CONFIG_X86_SUMMIT) += summit_32.o + +-obj-$(CONFIG_XEN) += nmi.o +- + probe_64-$(CONFIG_XEN) := probe_32.o + + disabled-obj-$(CONFIG_XEN) := apic_flat_$(BITS).o apic_noop.o +--- head-2010-04-15.orig/arch/x86/kernel/apic/nmi.c 2010-04-15 10:05:32.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/apic/nmi.c 2010-04-15 11:46:23.000000000 +0200 +@@ -28,8 +28,10 @@ + #include + #include + +-#ifndef CONFIG_XEN ++#ifdef ARCH_HAS_NMI_WATCHDOG + #include ++#else ++#include + #endif + #include + #include +@@ -40,6 +42,9 @@ + #include + + int unknown_nmi_panic; ++ ++#ifdef ARCH_HAS_NMI_WATCHDOG ++ + int nmi_watchdog_enabled; + + /* For reliability, we're prepared to waste bits here. */ +@@ -178,13 +183,11 @@ int __init check_nmi_watchdog(void) + kfree(prev_nmi_count); + return 0; + error: +-#ifndef CONFIG_XEN + if (nmi_watchdog == NMI_IO_APIC) { + if (!timer_through_8259) + legacy_pic->chip->mask(0); + on_each_cpu(__acpi_nmi_disable, NULL, 1); + } +-#endif + + #ifdef CONFIG_X86_32 + timer_ack = 0; +@@ -474,8 +477,11 @@ nmi_watchdog_tick(struct pt_regs *regs, + return rc; + } + ++#endif /* ARCH_HAS_NMI_WATCHDOG */ ++ + #ifdef CONFIG_SYSCTL + ++#ifdef ARCH_HAS_NMI_WATCHDOG + static void enable_ioapic_nmi_watchdog_single(void *unused) + { + __get_cpu_var(wd_enabled) = 1; +@@ -493,6 +499,7 @@ static void disable_ioapic_nmi_watchdog( + { + on_each_cpu(stop_apic_nmi_watchdog, NULL, 1); + } ++#endif + + static int __init setup_unknown_nmi_panic(char *str) + { +@@ -511,6 +518,7 @@ static int unknown_nmi_panic_callback(st + return 0; + } + ++#ifdef ARCH_HAS_NMI_WATCHDOG + /* + * proc handler for /proc/sys/kernel/nmi + */ +@@ -548,6 +556,7 @@ int proc_nmi_enabled(struct ctl_table *t + } + return 0; + } ++#endif + + #endif /* CONFIG_SYSCTL */ + +@@ -560,6 +569,7 @@ int do_nmi_callback(struct pt_regs *regs + return 0; + } + ++#ifdef ARCH_HAS_NMI_WATCHDOG + void arch_trigger_all_cpu_backtrace(void) + { + int i; +@@ -576,3 +586,4 @@ void arch_trigger_all_cpu_backtrace(void + mdelay(1); + } + } ++#endif +--- head-2010-04-15.orig/arch/x86/kernel/cpu/Makefile 2010-03-24 16:00:05.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/cpu/Makefile 2010-03-25 14:40:02.000000000 +0100 +@@ -34,7 +34,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ + + obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o + +-disabled-obj-$(CONFIG_XEN) := hypervisor.o perf_event.o sched.o vmware.o ++disabled-obj-$(CONFIG_XEN) := hypervisor.o perfctr-watchdog.o perf_event.o sched.o vmware.o + + quiet_cmd_mkcapflags = MKCAP $@ + cmd_mkcapflags = $(PERL) $(srctree)/$(src)/mkcapflags.pl $< $@ +--- head-2010-04-15.orig/arch/x86/kernel/head-xen.c 2010-04-15 10:13:18.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/head-xen.c 2010-04-15 11:46:18.000000000 +0200 +@@ -183,12 +183,10 @@ void __init xen_arch_setup(void) + .address = CALLBACK_ADDR(system_call) + }; + #endif +-#if defined(CONFIG_X86_LOCAL_APIC) || defined(CONFIG_X86_32) + static const struct callback_register __initconst nmi_cb = { + .type = CALLBACKTYPE_nmi, + .address = CALLBACK_ADDR(nmi) + }; +-#endif + + ret = HYPERVISOR_callback_op(CALLBACKOP_register, &event); + if (ret == 0) +@@ -212,7 +210,6 @@ void __init xen_arch_setup(void) + #endif + BUG_ON(ret); + +-#if defined(CONFIG_X86_LOCAL_APIC) || defined(CONFIG_X86_32) + ret = HYPERVISOR_callback_op(CALLBACKOP_register, &nmi_cb); + #if CONFIG_XEN_COMPAT <= 0x030002 + if (ret == -ENOSYS) { +@@ -223,6 +220,5 @@ void __init xen_arch_setup(void) + HYPERVISOR_nmi_op(XENNMI_register_callback, &cb); + } + #endif +-#endif + } + #endif /* CONFIG_XEN */ +--- head-2010-04-15.orig/arch/x86/kernel/traps-xen.c 2010-03-25 16:41:03.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/traps-xen.c 2010-03-25 14:40:02.000000000 +0100 +@@ -51,6 +51,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -394,12 +395,14 @@ static notrace __kprobes void default_do + == NOTIFY_STOP) + return; + #ifdef CONFIG_X86_LOCAL_APIC ++#ifdef ARCH_HAS_NMI_WATCHDOG + /* + * Ok, so this is none of the documented NMI sources, + * so it must be the NMI watchdog. + */ + if (nmi_watchdog_tick(regs, reason)) + return; ++#endif + if (!do_nmi_callback(regs, cpu)) + unknown_nmi_error(reason, regs); + #else +--- head-2010-04-15.orig/kernel/sysctl.c 2010-03-24 14:53:41.000000000 +0100 ++++ head-2010-04-15/kernel/sysctl.c 2010-03-25 14:40:02.000000000 +0100 +@@ -699,6 +699,7 @@ static struct ctl_table kern_table[] = { + .mode = 0644, + .proc_handler = proc_dointvec, + }, ++#ifdef ARCH_HAS_NMI_WATCHDOG + { + .procname = "nmi_watchdog", + .data = &nmi_watchdog_enabled, +@@ -707,6 +708,7 @@ static struct ctl_table kern_table[] = { + .proc_handler = proc_nmi_enabled, + }, + #endif ++#endif + #if defined(CONFIG_X86) + { + .procname = "panic_on_unrecovered_nmi", diff --git a/patches.xen/xen-x86-dcr-fallback b/patches.xen/xen-x86-dcr-fallback new file mode 100644 index 0000000..e24ff5b --- /dev/null +++ b/patches.xen/xen-x86-dcr-fallback @@ -0,0 +1,168 @@ +Subject: Add fallback when XENMEM_exchange fails to replace contiguous region +From: jbeulich@novell.com +Patch-mainline: obsolete +References: 181869 + +This avoids losing precious special memory in places where any memory can be +used. + +--- head-2010-04-15.orig/arch/x86/mm/hypervisor.c 2010-03-24 15:25:06.000000000 +0100 ++++ head-2010-04-15/arch/x86/mm/hypervisor.c 2009-06-09 15:52:17.000000000 +0200 +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -719,6 +720,83 @@ void xen_destroy_contiguous_region(unsig + BUG(); + + balloon_unlock(flags); ++ ++ if (unlikely(!success)) { ++ /* Try hard to get the special memory back to Xen. */ ++ exchange.in.extent_order = 0; ++ set_xen_guest_handle(exchange.in.extent_start, &in_frame); ++ ++ for (i = 0; i < (1U<> PAGE_SHIFT; ++ set_phys_to_machine(pfn, frame); ++ if (!xen_feature(XENFEAT_auto_translated_physmap)) { ++ mmu.ptr = ((uint64_t)frame << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE; ++ mmu.val = pfn; ++ cr_mcl[j].op = __HYPERVISOR_mmu_update; ++ cr_mcl[j].args[0] = (unsigned long)&mmu; ++ cr_mcl[j].args[1] = 1; ++ cr_mcl[j].args[2] = 0; ++ cr_mcl[j].args[3] = DOMID_SELF; ++ ++j; ++ } ++ ++ cr_mcl[j].op = __HYPERVISOR_memory_op; ++ cr_mcl[j].args[0] = XENMEM_decrease_reservation; ++ cr_mcl[j].args[1] = (unsigned long)&exchange.in; ++ ++ if (HYPERVISOR_multicall(cr_mcl, j + 1)) ++ BUG(); ++ BUG_ON(cr_mcl[j].result != 1); ++ while (j--) ++ BUG_ON(cr_mcl[j].result != 0); ++ ++ balloon_unlock(flags); ++ ++ free_empty_pages(&page, 1); ++ ++ in_frame++; ++ vstart += PAGE_SIZE; ++ } ++ } + } + EXPORT_SYMBOL_GPL(xen_destroy_contiguous_region); + +--- head-2010-04-15.orig/drivers/xen/balloon/balloon.c 2010-04-15 11:44:37.000000000 +0200 ++++ head-2010-04-15/drivers/xen/balloon/balloon.c 2010-04-15 11:46:09.000000000 +0200 +@@ -776,7 +776,11 @@ struct page **alloc_empty_pages_and_page + } + EXPORT_SYMBOL_GPL(alloc_empty_pages_and_pagevec); + +-void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages) ++#endif /* CONFIG_XEN_BACKEND */ ++ ++#ifdef CONFIG_XEN ++static void _free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages, ++ bool free_vec) + { + unsigned long flags; + int i; +@@ -787,17 +791,33 @@ void free_empty_pages_and_pagevec(struct + balloon_lock(flags); + for (i = 0; i < nr_pages; i++) { + BUG_ON(page_count(pagevec[i]) != 1); +- balloon_append(pagevec[i], 0); ++ balloon_append(pagevec[i], !free_vec); ++ } ++ if (!free_vec) { ++ bs.current_pages -= nr_pages; ++ totalram_pages = bs.current_pages - totalram_bias; + } + balloon_unlock(flags); + +- kfree(pagevec); ++ if (free_vec) ++ kfree(pagevec); + + schedule_work(&balloon_worker); + } +-EXPORT_SYMBOL_GPL(free_empty_pages_and_pagevec); + +-#endif /* CONFIG_XEN_BACKEND */ ++void free_empty_pages(struct page **pagevec, int nr_pages) ++{ ++ _free_empty_pages_and_pagevec(pagevec, nr_pages, false); ++} ++#endif ++ ++#if defined(CONFIG_XEN_BACKEND) || defined(CONFIG_XEN_BACKEND_MODULE) ++void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages) ++{ ++ _free_empty_pages_and_pagevec(pagevec, nr_pages, true); ++} ++EXPORT_SYMBOL_GPL(free_empty_pages_and_pagevec); ++#endif + + void balloon_release_driver_page(struct page *page) + { +--- head-2010-04-15.orig/include/xen/balloon.h 2010-03-24 15:12:36.000000000 +0100 ++++ head-2010-04-15/include/xen/balloon.h 2009-06-09 15:52:17.000000000 +0200 +@@ -47,6 +47,10 @@ void balloon_update_driver_allowance(lon + struct page **alloc_empty_pages_and_pagevec(int nr_pages); + void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages); + ++/* Free an empty page range (not allocated through ++ alloc_empty_pages_and_pagevec), adding to the balloon. */ ++void free_empty_pages(struct page **pagevec, int nr_pages); ++ + void balloon_release_driver_page(struct page *page); + + /* diff --git a/patches.xen/xen-x86-exit-mmap b/patches.xen/xen-x86-exit-mmap new file mode 100644 index 0000000..7ca2af6 --- /dev/null +++ b/patches.xen/xen-x86-exit-mmap @@ -0,0 +1,73 @@ +Subject: be more aggressive about de-activating mm-s under destruction +From: jbeulich@novell.com +Patch-mainline: obsolete + +... by not only handling the current task on the CPU arch_exit_mmap() +gets executed on, but also forcing remote CPUs to do so. + +--- head-2010-04-15.orig/arch/x86/mm/pgtable-xen.c 2010-04-15 11:47:53.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/pgtable-xen.c 2010-04-15 11:48:29.000000000 +0200 +@@ -1,6 +1,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -438,27 +439,44 @@ void arch_dup_mmap(struct mm_struct *old + mm_pin(mm); + } + +-void arch_exit_mmap(struct mm_struct *mm) ++/* ++ * We aggressively remove defunct pgd from cr3. We execute unmap_vmas() *much* ++ * faster this way, as no hypercalls are needed for the page table updates. ++ */ ++static void leave_active_mm(struct task_struct *tsk, struct mm_struct *mm) ++ __releases(tsk->alloc_lock) + { +- struct task_struct *tsk = current; +- +- task_lock(tsk); +- +- /* +- * We aggressively remove defunct pgd from cr3. We execute unmap_vmas() +- * *much* faster this way, as no tlb flushes means bigger wrpt batches. +- */ + if (tsk->active_mm == mm) { + tsk->active_mm = &init_mm; + atomic_inc(&init_mm.mm_count); + + switch_mm(mm, &init_mm, tsk); + +- atomic_dec(&mm->mm_count); +- BUG_ON(atomic_read(&mm->mm_count) == 0); ++ if (atomic_dec_and_test(&mm->mm_count)) ++ BUG(); + } + + task_unlock(tsk); ++} ++ ++static void _leave_active_mm(void *mm) ++{ ++ struct task_struct *tsk = current; ++ ++ if (spin_trylock(&tsk->alloc_lock)) ++ leave_active_mm(tsk, mm); ++} ++ ++void arch_exit_mmap(struct mm_struct *mm) ++{ ++ struct task_struct *tsk = current; ++ ++ task_lock(tsk); ++ leave_active_mm(tsk, mm); ++ ++ preempt_disable(); ++ smp_call_function_many(mm_cpumask(mm), _leave_active_mm, mm, 1); ++ preempt_enable(); + + if (PagePinned(virt_to_page(mm->pgd)) + && atomic_read(&mm->mm_count) == 1 diff --git a/patches.xen/xen-x86-machphys-prediction b/patches.xen/xen-x86-machphys-prediction new file mode 100644 index 0000000..b633164 --- /dev/null +++ b/patches.xen/xen-x86-machphys-prediction @@ -0,0 +1,204 @@ +From: jbeulich@novell.com +Subject: properly predict phys<->mach translations +Patch-mainline: obsolete + +--- head-2009-07-28.orig/arch/x86/include/mach-xen/asm/maddr_32.h 2009-07-28 12:14:16.000000000 +0200 ++++ head-2009-07-28/arch/x86/include/mach-xen/asm/maddr_32.h 2009-07-29 10:56:35.000000000 +0200 +@@ -30,17 +30,19 @@ extern unsigned int machine_to_phys_or + + static inline unsigned long pfn_to_mfn(unsigned long pfn) + { +- if (xen_feature(XENFEAT_auto_translated_physmap)) ++ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) + return pfn; +- BUG_ON(max_mapnr && pfn >= max_mapnr); ++ if (likely(max_mapnr)) ++ BUG_ON(pfn >= max_mapnr); + return phys_to_machine_mapping[pfn] & ~FOREIGN_FRAME_BIT; + } + + static inline int phys_to_machine_mapping_valid(unsigned long pfn) + { +- if (xen_feature(XENFEAT_auto_translated_physmap)) ++ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) + return 1; +- BUG_ON(max_mapnr && pfn >= max_mapnr); ++ if (likely(max_mapnr)) ++ BUG_ON(pfn >= max_mapnr); + return (phys_to_machine_mapping[pfn] != INVALID_P2M_ENTRY); + } + +@@ -48,7 +50,7 @@ static inline unsigned long mfn_to_pfn(u + { + unsigned long pfn; + +- if (xen_feature(XENFEAT_auto_translated_physmap)) ++ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) + return mfn; + + if (unlikely((mfn >> machine_to_phys_order) != 0)) +@@ -95,17 +97,18 @@ static inline unsigned long mfn_to_pfn(u + static inline unsigned long mfn_to_local_pfn(unsigned long mfn) + { + unsigned long pfn = mfn_to_pfn(mfn); +- if ((pfn < max_mapnr) +- && !xen_feature(XENFEAT_auto_translated_physmap) +- && (phys_to_machine_mapping[pfn] != mfn)) ++ if (likely(pfn < max_mapnr) ++ && likely(!xen_feature(XENFEAT_auto_translated_physmap)) ++ && unlikely(phys_to_machine_mapping[pfn] != mfn)) + return max_mapnr; /* force !pfn_valid() */ + return pfn; + } + + static inline void set_phys_to_machine(unsigned long pfn, unsigned long mfn) + { +- BUG_ON(max_mapnr && pfn >= max_mapnr); +- if (xen_feature(XENFEAT_auto_translated_physmap)) { ++ if (likely(max_mapnr)) ++ BUG_ON(pfn >= max_mapnr); ++ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) { + BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY); + return; + } +--- head-2009-07-28.orig/arch/x86/include/mach-xen/asm/maddr_64.h 2009-07-28 12:14:16.000000000 +0200 ++++ head-2009-07-28/arch/x86/include/mach-xen/asm/maddr_64.h 2009-07-29 10:56:35.000000000 +0200 +@@ -25,17 +25,19 @@ extern unsigned int machine_to_phys_or + + static inline unsigned long pfn_to_mfn(unsigned long pfn) + { +- if (xen_feature(XENFEAT_auto_translated_physmap)) ++ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) + return pfn; +- BUG_ON(max_mapnr && pfn >= max_mapnr); ++ if (likely(max_mapnr)) ++ BUG_ON(pfn >= max_mapnr); + return phys_to_machine_mapping[pfn] & ~FOREIGN_FRAME_BIT; + } + + static inline int phys_to_machine_mapping_valid(unsigned long pfn) + { +- if (xen_feature(XENFEAT_auto_translated_physmap)) ++ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) + return 1; +- BUG_ON(max_mapnr && pfn >= max_mapnr); ++ if (likely(max_mapnr)) ++ BUG_ON(pfn >= max_mapnr); + return (phys_to_machine_mapping[pfn] != INVALID_P2M_ENTRY); + } + +@@ -43,7 +45,7 @@ static inline unsigned long mfn_to_pfn(u + { + unsigned long pfn; + +- if (xen_feature(XENFEAT_auto_translated_physmap)) ++ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) + return mfn; + + if (unlikely((mfn >> machine_to_phys_order) != 0)) +@@ -90,17 +92,18 @@ static inline unsigned long mfn_to_pfn(u + static inline unsigned long mfn_to_local_pfn(unsigned long mfn) + { + unsigned long pfn = mfn_to_pfn(mfn); +- if ((pfn < max_mapnr) +- && !xen_feature(XENFEAT_auto_translated_physmap) +- && (phys_to_machine_mapping[pfn] != mfn)) ++ if (likely(pfn < max_mapnr) ++ && likely(!xen_feature(XENFEAT_auto_translated_physmap)) ++ && unlikely(phys_to_machine_mapping[pfn] != mfn)) + return max_mapnr; /* force !pfn_valid() */ + return pfn; + } + + static inline void set_phys_to_machine(unsigned long pfn, unsigned long mfn) + { +- BUG_ON(max_mapnr && pfn >= max_mapnr); +- if (xen_feature(XENFEAT_auto_translated_physmap)) { ++ if (likely(max_mapnr)) ++ BUG_ON(pfn >= max_mapnr); ++ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) { + BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY); + return; + } +--- head-2009-07-28.orig/arch/x86/include/mach-xen/asm/pgtable_types.h 2009-07-28 13:14:11.000000000 +0200 ++++ head-2009-07-28/arch/x86/include/mach-xen/asm/pgtable_types.h 2009-07-29 10:56:35.000000000 +0200 +@@ -207,7 +207,7 @@ typedef struct { pgdval_t pgd; } pgd_t; + #define __pgd_ma(x) ((pgd_t) { (x) } ) + static inline pgd_t xen_make_pgd(pgdval_t val) + { +- if (val & _PAGE_PRESENT) ++ if (likely(val & _PAGE_PRESENT)) + val = pte_phys_to_machine(val); + return (pgd_t) { val }; + } +@@ -217,10 +217,10 @@ static inline pgdval_t xen_pgd_val(pgd_t + { + pgdval_t ret = __pgd_val(pgd); + #if PAGETABLE_LEVELS == 2 && CONFIG_XEN_COMPAT <= 0x030002 +- if (ret) ++ if (likely(ret)) + ret = machine_to_phys(ret) | _PAGE_PRESENT; + #else +- if (ret & _PAGE_PRESENT) ++ if (likely(ret & _PAGE_PRESENT)) + ret = pte_machine_to_phys(ret); + #endif + return ret; +@@ -237,7 +237,7 @@ typedef struct { pudval_t pud; } pud_t; + #define __pud_ma(x) ((pud_t) { (x) } ) + static inline pud_t xen_make_pud(pudval_t val) + { +- if (val & _PAGE_PRESENT) ++ if (likely(val & _PAGE_PRESENT)) + val = pte_phys_to_machine(val); + return (pud_t) { val }; + } +@@ -246,7 +246,7 @@ static inline pud_t xen_make_pud(pudval_ + static inline pudval_t xen_pud_val(pud_t pud) + { + pudval_t ret = __pud_val(pud); +- if (ret & _PAGE_PRESENT) ++ if (likely(ret & _PAGE_PRESENT)) + ret = pte_machine_to_phys(ret); + return ret; + } +@@ -266,7 +266,7 @@ typedef struct { pmdval_t pmd; } pmd_t; + #define __pmd_ma(x) ((pmd_t) { (x) } ) + static inline pmd_t xen_make_pmd(pmdval_t val) + { +- if (val & _PAGE_PRESENT) ++ if (likely(val & _PAGE_PRESENT)) + val = pte_phys_to_machine(val); + return (pmd_t) { val }; + } +@@ -276,10 +276,10 @@ static inline pmdval_t xen_pmd_val(pmd_t + { + pmdval_t ret = __pmd_val(pmd); + #if CONFIG_XEN_COMPAT <= 0x030002 +- if (ret) ++ if (likely(ret)) + ret = pte_machine_to_phys(ret) | _PAGE_PRESENT; + #else +- if (ret & _PAGE_PRESENT) ++ if (likely(ret & _PAGE_PRESENT)) + ret = pte_machine_to_phys(ret); + #endif + return ret; +@@ -308,7 +308,7 @@ static inline pmdval_t pmd_flags(pmd_t p + #define __pte_ma(x) ((pte_t) { .pte = (x) } ) + static inline pte_t xen_make_pte(pteval_t val) + { +- if ((val & (_PAGE_PRESENT|_PAGE_IOMAP)) == _PAGE_PRESENT) ++ if (likely((val & (_PAGE_PRESENT|_PAGE_IOMAP)) == _PAGE_PRESENT)) + val = pte_phys_to_machine(val); + return (pte_t) { .pte = val }; + } +@@ -317,7 +317,7 @@ static inline pte_t xen_make_pte(pteval_ + static inline pteval_t xen_pte_val(pte_t pte) + { + pteval_t ret = __pte_val(pte); +- if ((pte.pte_low & (_PAGE_PRESENT|_PAGE_IOMAP)) == _PAGE_PRESENT) ++ if (likely((pte.pte_low & (_PAGE_PRESENT|_PAGE_IOMAP)) == _PAGE_PRESENT)) + ret = pte_machine_to_phys(ret); + return ret; + } diff --git a/patches.xen/xen-x86-no-lapic b/patches.xen/xen-x86-no-lapic new file mode 100644 index 0000000..a4d477d --- /dev/null +++ b/patches.xen/xen-x86-no-lapic @@ -0,0 +1,374 @@ +From: jbeulich@novell.com +Subject: Disallow all accesses to the local APIC page +Patch-mainline: n/a +References: bnc#191115 + +--- head-2010-05-12.orig/arch/x86/include/asm/apic.h 2010-03-24 15:01:37.000000000 +0100 ++++ head-2010-05-12/arch/x86/include/asm/apic.h 2010-03-25 14:40:58.000000000 +0100 +@@ -10,7 +10,9 @@ + #include + #include + #include ++#ifndef CONFIG_XEN + #include ++#endif + #include + #include + #include +@@ -49,6 +51,7 @@ static inline void generic_apic_probe(vo + #ifdef CONFIG_X86_LOCAL_APIC + + extern unsigned int apic_verbosity; ++#ifndef CONFIG_XEN + extern int local_apic_timer_c2_ok; + + extern int disable_apic; +@@ -121,6 +124,8 @@ extern u64 native_apic_icr_read(void); + + extern int x2apic_mode; + ++#endif /* CONFIG_XEN */ ++ + #ifdef CONFIG_X86_X2APIC + /* + * Make previous memory operations globally visible before +@@ -367,6 +372,8 @@ struct apic { + */ + extern struct apic *apic; + ++#ifndef CONFIG_XEN ++ + /* + * APIC functionality to boot other CPUs - only used on SMP: + */ +@@ -460,6 +467,8 @@ static inline void default_wait_for_init + + extern void generic_bigsmp_probe(void); + ++#endif /* CONFIG_XEN */ ++ + + #ifdef CONFIG_X86_LOCAL_APIC + +@@ -479,6 +488,8 @@ static inline const struct cpumask *defa + DECLARE_EARLY_PER_CPU(u16, x86_bios_cpu_apicid); + + ++#ifndef CONFIG_XEN ++ + static inline unsigned int read_apic_id(void) + { + unsigned int reg; +@@ -587,6 +598,8 @@ extern int default_cpu_present_to_apicid + extern int default_check_phys_apicid_present(int phys_apicid); + #endif + ++#endif /* CONFIG_XEN */ ++ + #endif /* CONFIG_X86_LOCAL_APIC */ + + #ifdef CONFIG_X86_32 +--- head-2010-05-12.orig/arch/x86/include/asm/apicdef.h 2010-05-12 08:55:22.000000000 +0200 ++++ head-2010-05-12/arch/x86/include/asm/apicdef.h 2010-03-25 14:40:58.000000000 +0100 +@@ -17,6 +17,8 @@ + */ + #define IO_APIC_SLOT_SIZE 1024 + ++#ifndef CONFIG_XEN ++ + #define APIC_ID 0x20 + + #define APIC_LVR 0x30 +@@ -142,6 +144,16 @@ + #define APIC_BASE_MSR 0x800 + #define X2APIC_ENABLE (1UL << 10) + ++#else /* CONFIG_XEN */ ++ ++enum { ++ APIC_DEST_ALLBUT = 0x1, ++ APIC_DEST_SELF, ++ APIC_DEST_ALLINC ++}; ++ ++#endif /* CONFIG_XEN */ ++ + #ifdef CONFIG_X86_32 + # define MAX_IO_APICS 64 + #else +@@ -149,6 +161,8 @@ + # define MAX_LOCAL_APIC 32768 + #endif + ++#ifndef CONFIG_XEN ++ + /* + * All x86-64 systems are xAPIC compatible. + * In the following, "apicid" is a physical APIC ID. +@@ -419,6 +433,8 @@ struct local_apic { + + #undef u32 + ++#endif /* CONFIG_XEN */ ++ + #ifdef CONFIG_X86_32 + #define BAD_APICID 0xFFu + #else +--- head-2010-05-12.orig/arch/x86/include/mach-xen/asm/fixmap.h 2010-04-15 10:29:09.000000000 +0200 ++++ head-2010-05-12/arch/x86/include/mach-xen/asm/fixmap.h 2010-04-15 11:47:12.000000000 +0200 +@@ -17,7 +17,6 @@ + #ifndef __ASSEMBLY__ + #include + #include +-#include + #include + #ifdef CONFIG_X86_32 + #include +@@ -85,10 +84,10 @@ enum fixed_addresses { + #ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT + FIX_OHCI1394_BASE, + #endif ++#ifndef CONFIG_XEN + #ifdef CONFIG_X86_LOCAL_APIC + FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ + #endif +-#ifndef CONFIG_XEN + #ifdef CONFIG_X86_IO_APIC + FIX_IO_APIC_BASE_0, + FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS - 1, +--- head-2010-05-12.orig/arch/x86/include/mach-xen/asm/smp.h 2010-04-26 11:32:06.000000000 +0200 ++++ head-2010-05-12/arch/x86/include/mach-xen/asm/smp.h 2010-04-28 17:21:52.000000000 +0200 +@@ -15,7 +15,7 @@ + # include + # endif + #endif +-#include ++#include + #include + + extern int smp_num_siblings; +@@ -181,7 +181,7 @@ extern unsigned disabled_cpus __cpuinitd + + #include + +-#ifdef CONFIG_X86_LOCAL_APIC ++#if defined(CONFIG_X86_LOCAL_APIC) && !defined(CONFIG_XEN) + + #ifndef CONFIG_X86_64 + static inline int logical_smp_processor_id(void) +--- head-2010-05-12.orig/arch/x86/kernel/acpi/boot.c 2010-04-15 10:07:05.000000000 +0200 ++++ head-2010-05-12/arch/x86/kernel/acpi/boot.c 2010-04-15 11:47:20.000000000 +0200 +@@ -74,13 +74,13 @@ int acpi_sci_override_gsi __initdata; + #ifndef CONFIG_XEN + int acpi_skip_timer_override __initdata; + int acpi_use_timer_override __initdata; +-#else +-#define acpi_skip_timer_override 0 +-#endif + + #ifdef CONFIG_X86_LOCAL_APIC + static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE; + #endif ++#else ++#define acpi_skip_timer_override 0 ++#endif + + #ifndef __HAVE_ARCH_CMPXCHG + #warning ACPI uses CMPXCHG, i486 and later hardware +@@ -139,6 +139,7 @@ static int __init acpi_parse_madt(struct + return -ENODEV; + } + ++#ifndef CONFIG_XEN + if (madt->address) { + acpi_lapic_addr = (u64) madt->address; + +@@ -146,7 +147,6 @@ static int __init acpi_parse_madt(struct + madt->address); + } + +-#ifndef CONFIG_XEN + default_acpi_madt_oem_check(madt->header.oem_id, + madt->header.oem_table_id); + #endif +@@ -247,6 +247,7 @@ static int __init + acpi_parse_lapic_addr_ovr(struct acpi_subtable_header * header, + const unsigned long end) + { ++#ifndef CONFIG_XEN + struct acpi_madt_local_apic_override *lapic_addr_ovr = NULL; + + lapic_addr_ovr = (struct acpi_madt_local_apic_override *)header; +@@ -255,6 +256,7 @@ acpi_parse_lapic_addr_ovr(struct acpi_su + return -EINVAL; + + acpi_lapic_addr = lapic_addr_ovr->address; ++#endif + + return 0; + } +@@ -1094,7 +1096,7 @@ int mp_register_gsi(struct device *dev, + + ioapic_pin = mp_find_ioapic_pin(ioapic, gsi); + +-#ifdef CONFIG_X86_32 ++#if defined(CONFIG_X86_32) && !defined(CONFIG_XEN) + if (ioapic_renumber_irq) + gsi = ioapic_renumber_irq(ioapic, gsi); + #endif +--- head-2010-05-12.orig/arch/x86/kernel/apic/io_apic-xen.c 2010-05-12 09:09:25.000000000 +0200 ++++ head-2010-05-12/arch/x86/kernel/apic/io_apic-xen.c 2010-05-12 09:15:16.000000000 +0200 +@@ -1071,7 +1071,9 @@ static inline int irq_trigger(int idx) + return MPBIOS_trigger(idx); + } + ++#ifndef CONFIG_XEN + int (*ioapic_renumber_irq)(int ioapic, int irq); ++#endif + static int pin_2_irq(int idx, int apic, int pin) + { + int irq, i; +@@ -1093,11 +1095,13 @@ static int pin_2_irq(int idx, int apic, + while (i < apic) + irq += nr_ioapic_registers[i++]; + irq += pin; ++#ifndef CONFIG_XEN + /* + * For MPS mode, so far only needed by ES7000 platform + */ + if (ioapic_renumber_irq) + irq = ioapic_renumber_irq(apic, irq); ++#endif + } + + #ifdef CONFIG_X86_32 +@@ -4106,10 +4110,12 @@ int io_apic_set_pci_routing(struct devic + u8 __init io_apic_unique_id(u8 id) + { + #ifdef CONFIG_X86_32 ++#ifndef CONFIG_XEN + if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && + !APIC_XAPIC(apic_version[boot_cpu_physical_apicid])) + return io_apic_get_unique_id(nr_ioapics, id); + else ++#endif + return id; + #else + int i; +--- head-2010-05-12.orig/arch/x86/kernel/irq-xen.c 2010-01-25 13:46:29.000000000 +0100 ++++ head-2010-05-12/arch/x86/kernel/irq-xen.c 2010-03-25 14:40:58.000000000 +0100 +@@ -15,9 +15,9 @@ + #include + #include + ++#ifndef CONFIG_XEN + atomic_t irq_err_count; + +-#ifndef CONFIG_XEN + /* Function pointer for generic interrupt vector handling */ + void (*x86_platform_ipi_callback)(void) = NULL; + #endif +@@ -57,7 +57,7 @@ static int show_other_interrupts(struct + for_each_online_cpu(j) + seq_printf(p, "%10u ", irq_stats(j)->__nmi_count); + seq_printf(p, " Non-maskable interrupts\n"); +-#ifdef CONFIG_X86_LOCAL_APIC ++#if defined(CONFIG_X86_LOCAL_APIC) && !defined(CONFIG_XEN) + seq_printf(p, "%*s: ", prec, "LOC"); + for_each_online_cpu(j) + seq_printf(p, "%10u ", irq_stats(j)->apic_timer_irqs); +@@ -122,10 +122,12 @@ static int show_other_interrupts(struct + seq_printf(p, "%10u ", per_cpu(mce_poll_count, j)); + seq_printf(p, " Machine check polls\n"); + #endif ++#ifndef CONFIG_XEN + seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count)); + #if defined(CONFIG_X86_IO_APIC) + seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count)); + #endif ++#endif + return 0; + } + +@@ -221,12 +223,16 @@ u64 arch_irq_stat_cpu(unsigned int cpu) + + u64 arch_irq_stat(void) + { ++#ifndef CONFIG_XEN + u64 sum = atomic_read(&irq_err_count); + + #ifdef CONFIG_X86_IO_APIC + sum += atomic_read(&irq_mis_count); + #endif + return sum; ++#else ++ return 0; ++#endif + } + + +--- head-2010-05-12.orig/arch/x86/kernel/mpparse-xen.c 2010-04-15 10:48:32.000000000 +0200 ++++ head-2010-05-12/arch/x86/kernel/mpparse-xen.c 2010-03-25 14:40:58.000000000 +0100 +@@ -288,7 +288,9 @@ static int __init smp_check_mpc(struct m + + printk(KERN_INFO "MPTABLE: Product ID: %s\n", str); + ++#ifndef CONFIG_XEN + printk(KERN_INFO "MPTABLE: APIC at: 0x%X\n", mpc->lapic); ++#endif + + return 1; + } +@@ -320,12 +322,14 @@ static int __init smp_read_mpc(struct mp + if (!smp_check_mpc(mpc, oem, str)) + return 0; + +-#if defined(CONFIG_X86_32) && !defined(CONFIG_XEN) ++#ifndef CONFIG_XEN ++#ifdef CONFIG_X86_32 + generic_mps_oem_check(mpc, oem, str); + #endif + /* save the local APIC address, it might be non-default */ + if (!acpi_lapic) + mp_lapic_addr = mpc->lapic; ++#endif + + if (early) + return 1; +@@ -512,10 +516,12 @@ static inline void __init construct_defa + int linttypes[2] = { mp_ExtINT, mp_NMI }; + int i; + ++#ifndef CONFIG_XEN + /* + * local APIC has default address + */ + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; ++#endif + + /* + * 2 CPUs, numbered 0 & 1. +@@ -648,10 +654,12 @@ void __init default_get_smp_config(unsig + */ + if (mpf->feature1 != 0) { + if (early) { ++#ifndef CONFIG_XEN + /* + * local APIC has default address + */ + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; ++#endif + return; + } + +--- head-2010-05-12.orig/drivers/xen/core/smpboot.c 2010-04-28 16:44:14.000000000 +0200 ++++ head-2010-05-12/drivers/xen/core/smpboot.c 2010-04-28 17:21:59.000000000 +0200 +@@ -341,7 +341,7 @@ void __init smp_prepare_cpus(unsigned in + * Here we can be sure that there is an IO-APIC in the system. Let's + * go and set it up: + */ +- if (!skip_ioapic_setup && nr_ioapics) ++ if (cpu_has_apic && !skip_ioapic_setup && nr_ioapics) + setup_IO_APIC(); + #endif + } diff --git a/patches.xen/xen-x86-panic-no-reboot b/patches.xen/xen-x86-panic-no-reboot new file mode 100644 index 0000000..78bcc0b --- /dev/null +++ b/patches.xen/xen-x86-panic-no-reboot @@ -0,0 +1,32 @@ +From: jbeulich@novell.com +Subject: Don't automatically reboot Dom0 on panic (match native) +Patch-mainline: obsolete + +$subject says it all. + +--- head-2010-04-15.orig/arch/x86/kernel/setup-xen.c 2010-04-15 10:48:32.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/setup-xen.c 2010-04-15 11:46:02.000000000 +0200 +@@ -791,15 +791,17 @@ void __init setup_arch(char **cmdline_p) + unsigned long p2m_pages; + struct physdev_set_iopl set_iopl; + ++ if (!is_initial_xendomain()) { + #ifdef CONFIG_X86_32 +- /* Force a quick death if the kernel panics (not domain 0). */ +- extern int panic_timeout; +- if (!panic_timeout && !is_initial_xendomain()) +- panic_timeout = 1; ++ /* Force a quick death if the kernel panics (not domain 0). */ ++ extern int panic_timeout; ++ if (!panic_timeout) ++ panic_timeout = 1; + #endif + +- /* Register a call for panic conditions. */ +- atomic_notifier_chain_register(&panic_notifier_list, &xen_panic_block); ++ /* Register a call for panic conditions. */ ++ atomic_notifier_chain_register(&panic_notifier_list, &xen_panic_block); ++ } + #endif /* CONFIG_XEN */ + + #ifdef CONFIG_X86_32 diff --git a/patches.xen/xen-x86-per-cpu-vcpu-info b/patches.xen/xen-x86-per-cpu-vcpu-info new file mode 100644 index 0000000..71de338 --- /dev/null +++ b/patches.xen/xen-x86-per-cpu-vcpu-info @@ -0,0 +1,628 @@ +From: jbeulich@novell.com +Subject: x86: use per-cpu storage for shared vcpu_info structure +Patch-mainline: obsolete + +... reducing access code size and latency, as well as being the +prerequisite for removing the limitation on 32 vCPU-s per guest. + +--- head-2010-04-15.orig/arch/x86/include/asm/percpu.h 2010-04-28 15:44:01.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/asm/percpu.h 2010-03-25 14:42:40.000000000 +0100 +@@ -190,6 +190,38 @@ do { \ + pfo_ret__; \ + }) + ++#define percpu_xchg_op(op, var, val) \ ++({ \ ++ typedef typeof(var) pxo_T__; \ ++ pxo_T__ pxo_ret__; \ ++ if (0) \ ++ pxo_ret__ = (val); \ ++ switch (sizeof(var)) { \ ++ case 1: \ ++ asm(op "b %0,"__percpu_arg(1) \ ++ : "=q" (pxo_ret__), "+m" (var) \ ++ : "0" ((pxo_T__)(val))); \ ++ break; \ ++ case 2: \ ++ asm(op "w %0,"__percpu_arg(1) \ ++ : "=r" (pxo_ret__), "+m" (var) \ ++ : "0" ((pxo_T__)(val))); \ ++ break; \ ++ case 4: \ ++ asm(op "l %0,"__percpu_arg(1) \ ++ : "=r" (pxo_ret__), "+m" (var) \ ++ : "0" ((pxo_T__)(val))); \ ++ break; \ ++ case 8: \ ++ asm(op "q %0,"__percpu_arg(1) \ ++ : "=r" (pxo_ret__), "+m" (var) \ ++ : "0" ((pxo_T__)(val))); \ ++ break; \ ++ default: __bad_percpu_size(); \ ++ } \ ++ pxo_ret__; \ ++}) ++ + /* + * percpu_read() makes gcc load the percpu variable every time it is + * accessed while percpu_read_stable() allows the value to be cached. +@@ -207,6 +239,10 @@ do { \ + #define percpu_and(var, val) percpu_to_op("and", var, val) + #define percpu_or(var, val) percpu_to_op("or", var, val) + #define percpu_xor(var, val) percpu_to_op("xor", var, val) ++#define percpu_xchg(var, val) percpu_xchg_op("xchg", var, val) ++#if defined(CONFIG_X86_XADD) || defined(CONFIG_X86_64) ++#define percpu_xadd(var, val) percpu_xchg_op("xadd", var, val) ++#endif + + #define __this_cpu_read_1(pcp) percpu_from_op("mov", (pcp), "m"(pcp)) + #define __this_cpu_read_2(pcp) percpu_from_op("mov", (pcp), "m"(pcp)) +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:41:00.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:41:15.000000000 +0100 +@@ -50,12 +50,26 @@ + + extern shared_info_t *HYPERVISOR_shared_info; + ++#ifdef CONFIG_XEN_VCPU_INFO_PLACEMENT ++DECLARE_PER_CPU(struct vcpu_info, vcpu_info); ++#define vcpu_info(cpu) (&per_cpu(vcpu_info, cpu)) ++#define current_vcpu_info() (&__get_cpu_var(vcpu_info)) ++#define vcpu_info_read(fld) percpu_read(vcpu_info.fld) ++#define vcpu_info_write(fld, val) percpu_write(vcpu_info.fld, val) ++#define vcpu_info_xchg(fld, val) percpu_xchg(vcpu_info.fld, val) ++void setup_vcpu_info(unsigned int cpu); ++void adjust_boot_vcpu_info(void); ++#else + #define vcpu_info(cpu) (HYPERVISOR_shared_info->vcpu_info + (cpu)) + #ifdef CONFIG_SMP + #define current_vcpu_info() vcpu_info(smp_processor_id()) + #else + #define current_vcpu_info() vcpu_info(0) + #endif ++#define vcpu_info_read(fld) (current_vcpu_info()->fld) ++#define vcpu_info_write(fld, val) (current_vcpu_info()->fld = (val)) ++static inline void setup_vcpu_info(unsigned int cpu) {} ++#endif + + #ifdef CONFIG_X86_32 + extern unsigned long hypervisor_virt_start; +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/irqflags.h 2010-03-24 15:32:27.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/irqflags.h 2010-03-25 14:41:15.000000000 +0100 +@@ -12,7 +12,7 @@ + * includes these barriers, for example. + */ + +-#define xen_save_fl(void) (current_vcpu_info()->evtchn_upcall_mask) ++#define xen_save_fl(void) vcpu_info_read(evtchn_upcall_mask) + + #define xen_restore_fl(f) \ + do { \ +@@ -28,7 +28,7 @@ do { \ + + #define xen_irq_disable() \ + do { \ +- current_vcpu_info()->evtchn_upcall_mask = 1; \ ++ vcpu_info_write(evtchn_upcall_mask, 1); \ + barrier(); \ + } while (0) + +@@ -90,8 +90,6 @@ static inline void halt(void) + #define evtchn_upcall_pending /* 0 */ + #define evtchn_upcall_mask 1 + +-#define sizeof_vcpu_shift 6 +- + #ifdef CONFIG_X86_64 + # define __REG_si %rsi + # define __CPU_num PER_CPU_VAR(cpu_number) +@@ -100,6 +98,22 @@ static inline void halt(void) + # define __CPU_num TI_cpu(%ebp) + #endif + ++#ifdef CONFIG_XEN_VCPU_INFO_PLACEMENT ++ ++#define GET_VCPU_INFO PER_CPU(vcpu_info, __REG_si) ++#define __DISABLE_INTERRUPTS movb $1,PER_CPU_VAR(vcpu_info+evtchn_upcall_mask) ++#define __ENABLE_INTERRUPTS movb $0,PER_CPU_VAR(vcpu_info+evtchn_upcall_mask) ++#define __TEST_PENDING cmpb $0,PER_CPU_VAR(vcpu_info+evtchn_upcall_pending+0) ++#define DISABLE_INTERRUPTS(clb) __DISABLE_INTERRUPTS ++#define ENABLE_INTERRUPTS(clb) __ENABLE_INTERRUPTS ++ ++#define __SIZEOF_DISABLE_INTERRUPTS 8 ++#define __SIZEOF_TEST_PENDING 8 ++ ++#else /* CONFIG_XEN_VCPU_INFO_PLACEMENT */ ++ ++#define sizeof_vcpu_shift 6 ++ + #ifdef CONFIG_SMP + #define GET_VCPU_INFO movl __CPU_num,%esi ; \ + shl $sizeof_vcpu_shift,%esi ; \ +@@ -116,15 +130,21 @@ static inline void halt(void) + #define ENABLE_INTERRUPTS(clb) GET_VCPU_INFO ; \ + __ENABLE_INTERRUPTS + ++#define __SIZEOF_DISABLE_INTERRUPTS 4 ++#define __SIZEOF_TEST_PENDING 3 ++ ++#endif /* CONFIG_XEN_VCPU_INFO_PLACEMENT */ ++ + #ifndef CONFIG_X86_64 + #define INTERRUPT_RETURN iret +-#define ENABLE_INTERRUPTS_SYSEXIT __ENABLE_INTERRUPTS ; \ ++#define ENABLE_INTERRUPTS_SYSEXIT \ ++ movb $0,evtchn_upcall_mask(%esi) /* __ENABLE_INTERRUPTS */ ; \ + sysexit_scrit: /**** START OF SYSEXIT CRITICAL REGION ****/ ; \ +- __TEST_PENDING ; \ ++ cmpb $0,evtchn_upcall_pending(%esi) /* __TEST_PENDING */ ; \ + jnz 14f /* process more events if necessary... */ ; \ + movl PT_ESI(%esp), %esi ; \ + sysexit ; \ +-14: __DISABLE_INTERRUPTS ; \ ++14: movb $1,evtchn_upcall_mask(%esi) /* __DISABLE_INTERRUPTS */ ; \ + TRACE_IRQS_OFF ; \ + sysexit_ecrit: /**** END OF SYSEXIT CRITICAL REGION ****/ ; \ + mov $__KERNEL_PERCPU, %ecx ; \ +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/pgtable_64.h 2010-03-25 14:41:00.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/pgtable_64.h 2010-03-25 14:41:15.000000000 +0100 +@@ -117,6 +117,8 @@ static inline void xen_set_pgd(pgd_t *pg + + #define __pte_mfn(_pte) (((_pte).pte & PTE_PFN_MASK) >> PAGE_SHIFT) + ++extern unsigned long early_arbitrary_virt_to_mfn(void *va); ++ + /* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/system.h 2010-01-25 13:43:44.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/system.h 2010-03-25 14:41:15.000000000 +0100 +@@ -245,8 +245,8 @@ static inline void xen_write_cr0(unsigne + asm volatile("mov %0,%%cr0": : "r" (val), "m" (__force_order)); + } + +-#define xen_read_cr2() (current_vcpu_info()->arch.cr2) +-#define xen_write_cr2(val) ((void)(current_vcpu_info()->arch.cr2 = (val))) ++#define xen_read_cr2() vcpu_info_read(arch.cr2) ++#define xen_write_cr2(val) vcpu_info_write(arch.cr2, val) + + static inline unsigned long xen_read_cr3(void) + { +--- head-2010-04-15.orig/arch/x86/kernel/cpu/common-xen.c 2010-03-24 16:00:05.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/cpu/common-xen.c 2010-03-25 14:41:15.000000000 +0100 +@@ -335,8 +335,16 @@ static const char *__cpuinit table_looku + __u32 cpu_caps_cleared[NCAPINTS] __cpuinitdata; + __u32 cpu_caps_set[NCAPINTS] __cpuinitdata; + +-void load_percpu_segment(int cpu) ++void __ref load_percpu_segment(int cpu) + { ++#ifdef CONFIG_XEN_VCPU_INFO_PLACEMENT ++ static bool done; ++ ++ if (!done) { ++ done = true; ++ adjust_boot_vcpu_info(); ++ } ++#endif + #ifdef CONFIG_X86_32 + loadsegment(fs, __KERNEL_PERCPU); + #else +--- head-2010-04-15.orig/arch/x86/kernel/entry_32-xen.S 2010-01-25 15:45:18.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/entry_32-xen.S 2010-03-25 14:41:15.000000000 +0100 +@@ -471,6 +471,9 @@ sysenter_exit: + movl PT_EIP(%esp), %edx + movl PT_OLDESP(%esp), %ecx + xorl %ebp,%ebp ++#ifdef CONFIG_XEN_VCPU_INFO_PLACEMENT ++ GET_VCPU_INFO ++#endif + TRACE_IRQS_ON + 1: mov PT_FS(%esp), %fs + PTGS_TO_GS +@@ -1036,7 +1039,9 @@ critical_region_fixup: + + .section .rodata,"a" + critical_fixup_table: +- .byte -1,-1,-1 # testb $0xff,(%esi) = __TEST_PENDING ++ .rept __SIZEOF_TEST_PENDING ++ .byte -1 ++ .endr + .byte -1,-1 # jnz 14f + .byte 0 # pop %ebx + .byte 1 # pop %ecx +@@ -1055,7 +1060,9 @@ critical_fixup_table: + .byte 10,10,10 # add $8,%esp + #endif + .byte 12 # iret +- .byte -1,-1,-1,-1 # movb $1,1(%esi) = __DISABLE_INTERRUPTS ++ .rept __SIZEOF_DISABLE_INTERRUPTS ++ .byte -1 ++ .endr + .previous + + # Hypervisor uses this for application faults while it executes. +--- head-2010-04-15.orig/arch/x86/kernel/head-xen.c 2010-04-15 11:46:18.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/head-xen.c 2010-04-28 17:22:58.000000000 +0200 +@@ -153,6 +153,8 @@ void __init xen_start_kernel(void) + HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO); + clear_page(empty_zero_page); + ++ setup_vcpu_info(0); ++ + /* Set up mapping of lowest 1MB of physical memory. */ + for (i = 0; i < NR_FIX_ISAMAPS; i++) + if (is_initial_xendomain()) +--- head-2010-04-15.orig/arch/x86/kernel/time-xen.c 2010-03-02 10:20:42.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/time-xen.c 2010-03-25 14:41:15.000000000 +0100 +@@ -282,16 +282,10 @@ static void get_time_values_from_xen(uns + local_irq_restore(flags); + } + +-static inline int time_values_up_to_date(unsigned int cpu) ++static inline int time_values_up_to_date(void) + { +- struct vcpu_time_info *src; +- struct shadow_time_info *dst; +- +- src = &vcpu_info(cpu)->time; +- dst = &per_cpu(shadow_time, cpu); +- + rmb(); +- return (dst->version == src->version); ++ return percpu_read(shadow_time.version) == vcpu_info_read(time.version); + } + + static void sync_xen_wallclock(unsigned long dummy); +@@ -337,7 +331,7 @@ unsigned long long xen_local_clock(void) + local_time_version = shadow->version; + rdtsc_barrier(); + time = shadow->system_timestamp + get_nsec_offset(shadow); +- if (!time_values_up_to_date(cpu)) ++ if (!time_values_up_to_date()) + get_time_values_from_xen(cpu); + barrier(); + } while (local_time_version != shadow->version); +--- head-2010-04-15.orig/arch/x86/mm/hypervisor.c 2010-03-25 14:41:00.000000000 +0100 ++++ head-2010-04-15/arch/x86/mm/hypervisor.c 2010-03-25 14:41:15.000000000 +0100 +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -50,7 +51,104 @@ + EXPORT_SYMBOL(hypercall_page); + + shared_info_t *__read_mostly HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page; ++#ifndef CONFIG_XEN_VCPU_INFO_PLACEMENT + EXPORT_SYMBOL(HYPERVISOR_shared_info); ++#else ++DEFINE_PER_CPU(struct vcpu_info, vcpu_info) __aligned(sizeof(struct vcpu_info)); ++EXPORT_PER_CPU_SYMBOL(vcpu_info); ++ ++void __ref setup_vcpu_info(unsigned int cpu) ++{ ++ struct vcpu_info *v = &per_cpu(vcpu_info, cpu); ++ struct vcpu_register_vcpu_info info; ++#ifdef CONFIG_X86_64 ++ static bool first = true; ++ ++ if (first) { ++ first = false; ++ info.mfn = early_arbitrary_virt_to_mfn(v); ++ } else ++#endif ++ info.mfn = arbitrary_virt_to_mfn(v); ++ info.offset = offset_in_page(v); ++ ++ if (HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info)) ++ BUG(); ++} ++ ++void __init adjust_boot_vcpu_info(void) ++{ ++ unsigned long lpfn, rpfn, lmfn, rmfn; ++ pte_t *lpte, *rpte; ++ unsigned int level; ++ mmu_update_t mmu[2]; ++ ++ /* ++ * setup_vcpu_info() cannot be used more than once for a given (v)CPU, ++ * hence we must swap the underlying MFNs of the two pages holding old ++ * and new vcpu_info of the boot CPU. ++ * ++ * Do *not* use __get_cpu_var() or percpu_{write,...}() here, as the per- ++ * CPU segment didn't get reloaded yet. Using percpu_read(), as in ++ * arch_use_lazy_mmu_mode(), though undesirable, is safe except for the ++ * accesses to variables that were updated in setup_percpu_areas(). ++ */ ++ lpte = lookup_address((unsigned long)&vcpu_info ++ + (__per_cpu_load - __per_cpu_start), ++ &level); ++ rpte = lookup_address((unsigned long)&per_cpu(vcpu_info, 0), &level); ++ BUG_ON(!lpte || !(pte_flags(*lpte) & _PAGE_PRESENT)); ++ BUG_ON(!rpte || !(pte_flags(*rpte) & _PAGE_PRESENT)); ++ lmfn = __pte_mfn(*lpte); ++ rmfn = __pte_mfn(*rpte); ++ ++ if (lmfn == rmfn) ++ return; ++ ++ lpfn = mfn_to_local_pfn(lmfn); ++ rpfn = mfn_to_local_pfn(rmfn); ++ ++ printk(KERN_INFO ++ "Swapping MFNs for PFN %lx and %lx (MFN %lx and %lx)\n", ++ lpfn, rpfn, lmfn, rmfn); ++ ++ xen_l1_entry_update(lpte, pfn_pte_ma(rmfn, pte_pgprot(*lpte))); ++ xen_l1_entry_update(rpte, pfn_pte_ma(lmfn, pte_pgprot(*rpte))); ++#ifdef CONFIG_X86_64 ++ if (HYPERVISOR_update_va_mapping((unsigned long)__va(lpfn<> PAGE_SHIFT; ++} ++ + #ifndef CONFIG_XEN + static int __init parse_direct_gbpages_off(char *arg) + { +--- head-2010-04-15.orig/drivers/xen/Kconfig 2010-03-31 14:12:07.000000000 +0200 ++++ head-2010-04-15/drivers/xen/Kconfig 2010-03-31 14:12:47.000000000 +0200 +@@ -367,6 +367,18 @@ config XEN_COMPAT + default 0x030002 if XEN_COMPAT_030002_AND_LATER + default 0 + ++config XEN_VCPU_INFO_PLACEMENT ++ bool "Place shared vCPU info in per-CPU storage" ++# depends on X86 && (XEN_COMPAT >= 0x00030101) ++ depends on X86 ++ depends on !XEN_COMPAT_030002_AND_LATER ++ depends on !XEN_COMPAT_030004_AND_LATER ++ depends on !XEN_COMPAT_030100_AND_LATER ++ default SMP ++ ---help--- ++ This allows faster access to the per-vCPU shared info ++ structure. ++ + endmenu + + config HAVE_IRQ_IGNORE_UNHANDLED +--- head-2010-04-15.orig/drivers/xen/core/evtchn.c 2010-04-23 15:20:52.000000000 +0200 ++++ head-2010-04-15/drivers/xen/core/evtchn.c 2010-04-23 15:21:14.000000000 +0200 +@@ -323,6 +323,24 @@ static DEFINE_PER_CPU(unsigned int, upca + static DEFINE_PER_CPU(unsigned int, current_l1i); + static DEFINE_PER_CPU(unsigned int, current_l2i); + ++#ifndef vcpu_info_xchg ++#define vcpu_info_xchg(fld, val) xchg(¤t_vcpu_info()->fld, val) ++#endif ++ ++#ifndef percpu_xadd ++#define percpu_xadd(var, val) \ ++({ \ ++ typeof(var) __tmp_var__; \ ++ unsigned long flags; \ ++ local_irq_save(flags); \ ++ __tmp_var__ = get_cpu_var(var); \ ++ __get_cpu_var(var) += (val); \ ++ put_cpu_var(var); \ ++ local_irq_restore(flags); \ ++ __tmp_var__; \ ++}) ++#endif ++ + /* NB. Interrupts are disabled on entry. */ + asmlinkage void __irq_entry evtchn_do_upcall(struct pt_regs *regs) + { +@@ -331,25 +349,25 @@ asmlinkage void __irq_entry evtchn_do_up + unsigned long masked_l1, masked_l2; + unsigned int l1i, l2i, start_l1i, start_l2i, port, count, i; + int irq; +- vcpu_info_t *vcpu_info = current_vcpu_info(); + + exit_idle(); + irq_enter(); + + do { + /* Avoid a callback storm when we reenable delivery. */ +- vcpu_info->evtchn_upcall_pending = 0; ++ vcpu_info_write(evtchn_upcall_pending, 0); + + /* Nested invocations bail immediately. */ +- percpu_add(upcall_count, 1); +- if (unlikely(percpu_read(upcall_count) != 1)) ++ if (unlikely(percpu_xadd(upcall_count, 1))) + break; + + #ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */ + /* Clear master flag /before/ clearing selector flag. */ + wmb(); ++#else ++ barrier(); + #endif +- l1 = xchg(&vcpu_info->evtchn_pending_sel, 0); ++ l1 = vcpu_info_xchg(evtchn_pending_sel, 0); + + start_l1i = l1i = percpu_read(current_l1i); + start_l2i = percpu_read(current_l2i); +@@ -1370,7 +1388,6 @@ void unmask_evtchn(int port) + { + shared_info_t *s = HYPERVISOR_shared_info; + unsigned int cpu = smp_processor_id(); +- vcpu_info_t *vcpu_info = &s->vcpu_info[cpu]; + + BUG_ON(!irqs_disabled()); + +@@ -1384,10 +1401,13 @@ void unmask_evtchn(int port) + synch_clear_bit(port, s->evtchn_mask); + + /* Did we miss an interrupt 'edge'? Re-fire if so. */ +- if (synch_test_bit(port, s->evtchn_pending) && +- !synch_test_and_set_bit(port / BITS_PER_LONG, +- &vcpu_info->evtchn_pending_sel)) +- vcpu_info->evtchn_upcall_pending = 1; ++ if (synch_test_bit(port, s->evtchn_pending)) { ++ vcpu_info_t *v = current_vcpu_info(); ++ ++ if (!synch_test_and_set_bit(port / BITS_PER_LONG, ++ &v->evtchn_pending_sel)) ++ v->evtchn_upcall_pending = 1; ++ } + } + EXPORT_SYMBOL_GPL(unmask_evtchn); + +--- head-2010-04-15.orig/drivers/xen/core/machine_reboot.c 2010-03-25 14:41:06.000000000 +0100 ++++ head-2010-04-15/drivers/xen/core/machine_reboot.c 2010-03-25 14:41:15.000000000 +0100 +@@ -74,7 +74,7 @@ static void pre_suspend(void) + mfn_to_pfn(xen_start_info->console.domU.mfn); + } + +-static void post_suspend(int suspend_cancelled) ++static void post_suspend(int suspend_cancelled, int fast_suspend) + { + int i, j, k, fpp; + unsigned long shinfo_mfn; +@@ -91,8 +91,21 @@ static void post_suspend(int suspend_can + #ifdef CONFIG_SMP + cpumask_copy(vcpu_initialized_mask, cpu_online_mask); + #endif +- for_each_possible_cpu(i) ++ for_each_possible_cpu(i) { + setup_runstate_area(i); ++ ++#ifdef CONFIG_XEN_VCPU_INFO_PLACEMENT ++ if (fast_suspend && i != smp_processor_id() ++ && HYPERVISOR_vcpu_op(VCPUOP_down, i, NULL)) ++ BUG(); ++ ++ setup_vcpu_info(i); ++ ++ if (fast_suspend && i != smp_processor_id() ++ && HYPERVISOR_vcpu_op(VCPUOP_up, i, NULL)) ++ BUG(); ++#endif ++ } + } + + shinfo_mfn = xen_start_info->shared_info >> PAGE_SHIFT; +@@ -134,7 +147,7 @@ static void post_suspend(int suspend_can + #define switch_idle_mm() ((void)0) + #define mm_pin_all() ((void)0) + #define pre_suspend() xen_pre_suspend() +-#define post_suspend(x) xen_post_suspend(x) ++#define post_suspend(x, f) xen_post_suspend(x) + + #endif + +@@ -165,7 +178,7 @@ static int take_machine_down(void *_susp + BUG_ON(suspend_cancelled > 0); + suspend->resume_notifier(suspend_cancelled); + if (suspend_cancelled >= 0) +- post_suspend(suspend_cancelled); ++ post_suspend(suspend_cancelled, suspend->fast_suspend); + if (!suspend_cancelled) + xen_clockevents_resume(); + if (suspend_cancelled >= 0) +--- head-2010-04-15.orig/drivers/xen/core/smpboot.c 2010-04-28 17:21:59.000000000 +0200 ++++ head-2010-04-15/drivers/xen/core/smpboot.c 2010-04-28 17:22:15.000000000 +0200 +@@ -348,8 +348,13 @@ void __init smp_prepare_cpus(unsigned in + + void __init smp_prepare_boot_cpu(void) + { ++ unsigned int cpu; ++ + switch_to_new_gdt(smp_processor_id()); + prefill_possible_map(); ++ for_each_possible_cpu(cpu) ++ if (cpu != smp_processor_id()) ++ setup_vcpu_info(cpu); + } + + #ifdef CONFIG_HOTPLUG_CPU +--- head-2010-04-15.orig/drivers/xen/core/spinlock.c 2010-03-19 08:48:35.000000000 +0100 ++++ head-2010-04-15/drivers/xen/core/spinlock.c 2010-03-25 14:41:15.000000000 +0100 +@@ -105,7 +105,7 @@ bool xen_spin_wait(arch_spinlock_t *lock + spinning.prev = percpu_read(_spinning); + smp_wmb(); + percpu_write(_spinning, &spinning); +- upcall_mask = current_vcpu_info()->evtchn_upcall_mask; ++ upcall_mask = vcpu_info_read(evtchn_upcall_mask); + + do { + bool nested = false; +@@ -171,12 +171,12 @@ bool xen_spin_wait(arch_spinlock_t *lock + * intended event processing will happen with the poll + * call. + */ +- current_vcpu_info()->evtchn_upcall_mask = +- nested ? upcall_mask : flags; ++ vcpu_info_write(evtchn_upcall_mask, ++ nested ? upcall_mask : flags); + + xen_poll_irq(irq); + +- current_vcpu_info()->evtchn_upcall_mask = upcall_mask; ++ vcpu_info_write(evtchn_upcall_mask, upcall_mask); + + rc = !xen_test_irq_pending(irq); + if (!rc) diff --git a/patches.xen/xen-x86-pmd-handling b/patches.xen/xen-x86-pmd-handling new file mode 100644 index 0000000..7336d1a --- /dev/null +++ b/patches.xen/xen-x86-pmd-handling @@ -0,0 +1,605 @@ +From: jbeulich@novell.com +Subject: consolidate pmd/pud/pgd entry handling +Patch-mainline: obsolete + +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:39:15.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:41:00.000000000 +0100 +@@ -97,10 +97,12 @@ void xen_invlpg(unsigned long ptr); + void xen_l1_entry_update(pte_t *ptr, pte_t val); + void xen_l2_entry_update(pmd_t *ptr, pmd_t val); + void xen_l3_entry_update(pud_t *ptr, pud_t val); /* x86_64/PAE */ +-void xen_l4_entry_update(pgd_t *ptr, pgd_t val); /* x86_64 only */ ++void xen_l4_entry_update(pgd_t *ptr, int user, pgd_t val); /* x86_64 only */ + void xen_pgd_pin(unsigned long ptr); + void xen_pgd_unpin(unsigned long ptr); + ++void xen_init_pgd_pin(void); ++ + void xen_set_ldt(const void *ptr, unsigned int ents); + + #ifdef CONFIG_SMP +@@ -333,6 +335,18 @@ MULTI_update_va_mapping( + } + + static inline void ++MULTI_mmu_update(multicall_entry_t *mcl, mmu_update_t *req, ++ unsigned int count, unsigned int *success_count, ++ domid_t domid) ++{ ++ mcl->op = __HYPERVISOR_mmu_update; ++ mcl->args[0] = (unsigned long)req; ++ mcl->args[1] = count; ++ mcl->args[2] = (unsigned long)success_count; ++ mcl->args[3] = domid; ++} ++ ++static inline void + MULTI_grant_table_op(multicall_entry_t *mcl, unsigned int cmd, + void *uop, unsigned int count) + { +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/pgalloc.h 2010-03-25 16:41:03.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/pgalloc.h 2010-03-25 14:41:00.000000000 +0100 +@@ -75,20 +75,16 @@ static inline void pmd_populate(struct m + struct page *pte) + { + unsigned long pfn = page_to_pfn(pte); ++ pmd_t ent = __pmd(((pmdval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE); + + paravirt_alloc_pte(mm, pfn); +- if (PagePinned(virt_to_page(mm->pgd))) { +- if (!PageHighMem(pte)) +- BUG_ON(HYPERVISOR_update_va_mapping( +- (unsigned long)__va(pfn << PAGE_SHIFT), +- pfn_pte(pfn, PAGE_KERNEL_RO), 0)); +-#ifndef CONFIG_X86_64 +- else if (!TestSetPagePinned(pte)) +- kmap_flush_unused(); ++ if (PagePinned(virt_to_page(pmd))) { ++#ifndef CONFIG_HIGHPTE ++ BUG_ON(PageHighMem(pte)); + #endif +- set_pmd(pmd, __pmd(((pmdval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE)); ++ set_pmd(pmd, ent); + } else +- *pmd = __pmd(((pmdval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE); ++ *pmd = ent; + } + + #define pmd_pgtable(pmd) pmd_page(pmd) +@@ -116,39 +112,28 @@ extern void pud_populate(struct mm_struc + #else /* !CONFIG_X86_PAE */ + static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) + { ++ pud_t ent = __pud(_PAGE_TABLE | __pa(pmd)); ++ + paravirt_alloc_pmd(mm, __pa(pmd) >> PAGE_SHIFT); +- if (unlikely(PagePinned(virt_to_page((mm)->pgd)))) { +- BUG_ON(HYPERVISOR_update_va_mapping( +- (unsigned long)pmd, +- pfn_pte(virt_to_phys(pmd)>>PAGE_SHIFT, +- PAGE_KERNEL_RO), 0)); +- set_pud(pud, __pud(_PAGE_TABLE | __pa(pmd))); +- } else +- *pud = __pud(_PAGE_TABLE | __pa(pmd)); ++ if (PagePinned(virt_to_page(pud))) ++ set_pud(pud, ent); ++ else ++ *pud = ent; + } + #endif /* CONFIG_X86_PAE */ + + #if PAGETABLE_LEVELS > 3 + #define __user_pgd(pgd) ((pgd) + PTRS_PER_PGD) + +-/* +- * We need to use the batch mode here, but pgd_pupulate() won't be +- * be called frequently. +- */ + static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud) + { ++ pgd_t ent = __pgd(_PAGE_TABLE | __pa(pud)); ++ + paravirt_alloc_pud(mm, __pa(pud) >> PAGE_SHIFT); +- if (unlikely(PagePinned(virt_to_page((mm)->pgd)))) { +- BUG_ON(HYPERVISOR_update_va_mapping( +- (unsigned long)pud, +- pfn_pte(virt_to_phys(pud)>>PAGE_SHIFT, +- PAGE_KERNEL_RO), 0)); +- set_pgd(pgd, __pgd(_PAGE_TABLE | __pa(pud))); +- set_pgd(__user_pgd(pgd), __pgd(_PAGE_TABLE | __pa(pud))); +- } else { +- *(pgd) = __pgd(_PAGE_TABLE | __pa(pud)); +- *__user_pgd(pgd) = *(pgd); +- } ++ if (unlikely(PagePinned(virt_to_page(pgd)))) ++ xen_l4_entry_update(pgd, 1, ent); ++ else ++ *__user_pgd(pgd) = *pgd = ent; + } + + static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/pgtable-3level.h 2010-03-24 15:25:06.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/pgtable-3level.h 2010-03-25 14:41:00.000000000 +0100 +@@ -61,12 +61,15 @@ static inline void __xen_pte_clear(pte_t + ptep->pte_high = 0; + } + +-static inline void xen_pmd_clear(pmd_t *pmd) +-{ +- xen_l2_entry_update(pmd, __pmd(0)); +-} ++#define xen_pmd_clear(pmd) \ ++({ \ ++ pmd_t *__pmdp = (pmd); \ ++ PagePinned(virt_to_page(__pmdp)) \ ++ ? set_pmd(__pmdp, __pmd(0)) \ ++ : (void)(*__pmdp = __pmd(0)); \ ++}) + +-static inline void pud_clear(pud_t *pudp) ++static inline void __xen_pud_clear(pud_t *pudp) + { + pgdval_t pgd; + +@@ -87,6 +90,14 @@ static inline void pud_clear(pud_t *pudp + xen_tlb_flush(); + } + ++#define xen_pud_clear(pudp) \ ++({ \ ++ pud_t *__pudp = (pudp); \ ++ PagePinned(virt_to_page(__pudp)) \ ++ ? __xen_pud_clear(__pudp) \ ++ : (void)(*__pudp = __pud(0)); \ ++}) ++ + #ifdef CONFIG_SMP + static inline pte_t xen_ptep_get_and_clear(pte_t *ptep, pte_t res) + { +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/pgtable_64.h 2010-03-25 16:41:03.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/pgtable_64.h 2010-03-25 14:41:00.000000000 +0100 +@@ -79,33 +79,41 @@ static inline void xen_set_pmd(pmd_t *pm + xen_l2_entry_update(pmdp, pmd); + } + +-static inline void xen_pmd_clear(pmd_t *pmd) +-{ +- xen_set_pmd(pmd, xen_make_pmd(0)); +-} ++#define xen_pmd_clear(pmd) \ ++({ \ ++ pmd_t *__pmdp = (pmd); \ ++ PagePinned(virt_to_page(__pmdp)) \ ++ ? set_pmd(__pmdp, xen_make_pmd(0)) \ ++ : (void)(*__pmdp = xen_make_pmd(0)); \ ++}) + + static inline void xen_set_pud(pud_t *pudp, pud_t pud) + { + xen_l3_entry_update(pudp, pud); + } + +-static inline void xen_pud_clear(pud_t *pud) +-{ +- xen_set_pud(pud, xen_make_pud(0)); +-} ++#define xen_pud_clear(pud) \ ++({ \ ++ pud_t *__pudp = (pud); \ ++ PagePinned(virt_to_page(__pudp)) \ ++ ? set_pud(__pudp, xen_make_pud(0)) \ ++ : (void)(*__pudp = xen_make_pud(0)); \ ++}) + + #define __user_pgd(pgd) ((pgd) + PTRS_PER_PGD) + + static inline void xen_set_pgd(pgd_t *pgdp, pgd_t pgd) + { +- xen_l4_entry_update(pgdp, pgd); ++ xen_l4_entry_update(pgdp, 0, pgd); + } + +-static inline void xen_pgd_clear(pgd_t *pgd) +-{ +- xen_set_pgd(pgd, xen_make_pgd(0)); +- xen_set_pgd(__user_pgd(pgd), xen_make_pgd(0)); +-} ++#define xen_pgd_clear(pgd) \ ++({ \ ++ pgd_t *__pgdp = (pgd); \ ++ PagePinned(virt_to_page(__pgdp)) \ ++ ? xen_l4_entry_update(__pgdp, 1, xen_make_pgd(0)) \ ++ : (void)(*__user_pgd(__pgdp) = *__pgdp = xen_make_pgd(0)); \ ++}) + + #define __pte_mfn(_pte) (((_pte).pte & PTE_PFN_MASK) >> PAGE_SHIFT) + +--- head-2010-04-15.orig/arch/x86/mm/hypervisor.c 2009-06-09 15:52:17.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/hypervisor.c 2010-03-25 14:41:00.000000000 +0100 +@@ -360,31 +360,91 @@ void xen_l1_entry_update(pte_t *ptr, pte + } + EXPORT_SYMBOL_GPL(xen_l1_entry_update); + ++static void do_lN_entry_update(mmu_update_t *mmu, unsigned int mmu_count, ++ struct page *page) ++{ ++ if (likely(page)) { ++ multicall_entry_t mcl[2]; ++ unsigned long pfn = page_to_pfn(page); ++ ++ MULTI_update_va_mapping(mcl, ++ (unsigned long)__va(pfn << PAGE_SHIFT), ++ pfn_pte(pfn, PAGE_KERNEL_RO), 0); ++ SetPagePinned(page); ++ MULTI_mmu_update(mcl + 1, mmu, mmu_count, NULL, DOMID_SELF); ++ if (unlikely(HYPERVISOR_multicall_check(mcl, 2, NULL))) ++ BUG(); ++ } else if (unlikely(HYPERVISOR_mmu_update(mmu, mmu_count, ++ NULL, DOMID_SELF) < 0)) ++ BUG(); ++} ++ + void xen_l2_entry_update(pmd_t *ptr, pmd_t val) + { + mmu_update_t u; ++ struct page *page = NULL; ++ ++ if (likely(pmd_present(val)) && likely(!pmd_large(val)) ++ && likely(mem_map) ++ && likely(PagePinned(virt_to_page(ptr)))) { ++ page = pmd_page(val); ++ if (unlikely(PagePinned(page))) ++ page = NULL; ++ else if (PageHighMem(page)) { ++#ifndef CONFIG_HIGHPTE ++ BUG(); ++#endif ++ kmap_flush_unused(); ++ page = NULL; ++ } ++ } + u.ptr = virt_to_machine(ptr); + u.val = __pmd_val(val); +- BUG_ON(HYPERVISOR_mmu_update(&u, 1, NULL, DOMID_SELF) < 0); ++ do_lN_entry_update(&u, 1, page); + } + + #if defined(CONFIG_X86_PAE) || defined(CONFIG_X86_64) + void xen_l3_entry_update(pud_t *ptr, pud_t val) + { + mmu_update_t u; ++ struct page *page = NULL; ++ ++ if (likely(pud_present(val)) ++#ifdef CONFIG_X86_64 ++ && likely(!pud_large(val)) ++#endif ++ && likely(mem_map) ++ && likely(PagePinned(virt_to_page(ptr)))) { ++ page = pud_page(val); ++ if (unlikely(PagePinned(page))) ++ page = NULL; ++ } + u.ptr = virt_to_machine(ptr); + u.val = __pud_val(val); +- BUG_ON(HYPERVISOR_mmu_update(&u, 1, NULL, DOMID_SELF) < 0); ++ do_lN_entry_update(&u, 1, page); + } + #endif + + #ifdef CONFIG_X86_64 +-void xen_l4_entry_update(pgd_t *ptr, pgd_t val) ++void xen_l4_entry_update(pgd_t *ptr, int user, pgd_t val) + { +- mmu_update_t u; +- u.ptr = virt_to_machine(ptr); +- u.val = __pgd_val(val); +- BUG_ON(HYPERVISOR_mmu_update(&u, 1, NULL, DOMID_SELF) < 0); ++ mmu_update_t u[2]; ++ struct page *page = NULL; ++ ++ if (likely(pgd_present(val)) && likely(mem_map) ++ && likely(PagePinned(virt_to_page(ptr)))) { ++ page = pgd_page(val); ++ if (unlikely(PagePinned(page))) ++ page = NULL; ++ } ++ u[0].ptr = virt_to_machine(ptr); ++ u[0].val = __pgd_val(val); ++ if (user) { ++ u[1].ptr = virt_to_machine(__user_pgd(ptr)); ++ u[1].val = __pgd_val(val); ++ do_lN_entry_update(u, 2, page); ++ } else ++ do_lN_entry_update(u, 1, page); + } + #endif /* CONFIG_X86_64 */ + +--- head-2010-04-15.orig/arch/x86/mm/init_32-xen.c 2010-03-25 14:37:41.000000000 +0100 ++++ head-2010-04-15/arch/x86/mm/init_32-xen.c 2010-03-25 14:41:00.000000000 +0100 +@@ -750,6 +750,8 @@ static void __init zone_sizes_init(void) + #endif + + free_area_init_nodes(max_zone_pfns); ++ ++ xen_init_pgd_pin(); + } + + #ifndef CONFIG_NO_BOOTMEM +@@ -1028,8 +1030,6 @@ void __init mem_init(void) + + save_pg_dir(); + zap_low_mappings(true); +- +- SetPagePinned(virt_to_page(init_mm.pgd)); + } + + #ifdef CONFIG_MEMORY_HOTPLUG +--- head-2010-04-15.orig/arch/x86/mm/init_64-xen.c 2010-04-15 11:41:27.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/init_64-xen.c 2010-04-15 11:47:48.000000000 +0200 +@@ -194,8 +194,11 @@ static pud_t *fill_pud(pgd_t *pgd, unsig + { + if (pgd_none(*pgd)) { + pud_t *pud = (pud_t *)spp_getpage(); +- make_page_readonly(pud, XENFEAT_writable_page_tables); +- pgd_populate(&init_mm, pgd, pud); ++ if (!after_bootmem) { ++ make_page_readonly(pud, XENFEAT_writable_page_tables); ++ xen_l4_entry_update(pgd, __pgd(__pa(pud) | _PAGE_TABLE)); ++ } else ++ pgd_populate(&init_mm, pgd, pud); + if (pud != pud_offset(pgd, 0)) + printk(KERN_ERR "PAGETABLE BUG #00! %p <-> %p\n", + pud, pud_offset(pgd, 0)); +@@ -207,8 +210,11 @@ static pmd_t *fill_pmd(pud_t *pud, unsig + { + if (pud_none(*pud)) { + pmd_t *pmd = (pmd_t *) spp_getpage(); +- make_page_readonly(pmd, XENFEAT_writable_page_tables); +- pud_populate(&init_mm, pud, pmd); ++ if (!after_bootmem) { ++ make_page_readonly(pmd, XENFEAT_writable_page_tables); ++ xen_l3_entry_update(pud, __pud(__pa(pmd) | _PAGE_TABLE)); ++ } else ++ pud_populate(&init_mm, pud, pmd); + if (pmd != pmd_offset(pud, 0)) + printk(KERN_ERR "PAGETABLE BUG #01! %p <-> %p\n", + pmd, pmd_offset(pud, 0)); +@@ -541,7 +547,6 @@ phys_pmd_init(pmd_t *pmd_page, unsigned + XENFEAT_writable_page_tables); + *pmd = __pmd(pte_phys | _PAGE_TABLE); + } else { +- make_page_readonly(pte, XENFEAT_writable_page_tables); + spin_lock(&init_mm.page_table_lock); + pmd_populate_kernel(&init_mm, pmd, __va(pte_phys)); + spin_unlock(&init_mm.page_table_lock); +@@ -630,7 +635,6 @@ phys_pud_init(pud_t *pud_page, unsigned + else + *pud = __pud(pmd_phys | _PAGE_TABLE); + } else { +- make_page_readonly(pmd, XENFEAT_writable_page_tables); + spin_lock(&init_mm.page_table_lock); + pud_populate(&init_mm, pud, __va(pmd_phys)); + spin_unlock(&init_mm.page_table_lock); +@@ -804,7 +808,6 @@ kernel_physical_mapping_init(unsigned lo + XENFEAT_writable_page_tables); + xen_l4_entry_update(pgd, __pgd(pud_phys | _PAGE_TABLE)); + } else { +- make_page_readonly(pud, XENFEAT_writable_page_tables); + spin_lock(&init_mm.page_table_lock); + pgd_populate(&init_mm, pgd, __va(pud_phys)); + spin_unlock(&init_mm.page_table_lock); +@@ -869,7 +872,7 @@ void __init paging_init(void) + + free_area_init_nodes(max_zone_pfns); + +- SetPagePinned(virt_to_page(init_mm.pgd)); ++ xen_init_pgd_pin(); + } + + /* +--- head-2010-04-15.orig/arch/x86/mm/pgtable-xen.c 2010-04-15 10:53:40.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/pgtable-xen.c 2010-04-15 11:47:53.000000000 +0200 +@@ -66,16 +66,16 @@ early_param("userpte", setup_userpte); + void __pte_free(pgtable_t pte) + { + if (!PageHighMem(pte)) { +- unsigned long va = (unsigned long)page_address(pte); +- unsigned int level; +- pte_t *ptep = lookup_address(va, &level); +- +- BUG_ON(!ptep || level != PG_LEVEL_4K || !pte_present(*ptep)); +- if (!pte_write(*ptep) +- && HYPERVISOR_update_va_mapping(va, +- mk_pte(pte, PAGE_KERNEL), +- 0)) +- BUG(); ++ if (PagePinned(pte)) { ++ unsigned long pfn = page_to_pfn(pte); ++ ++ if (HYPERVISOR_update_va_mapping((unsigned long)__va(pfn << PAGE_SHIFT), ++ pfn_pte(pfn, ++ PAGE_KERNEL), ++ 0)) ++ BUG(); ++ ClearPagePinned(pte); ++ } + } else + #ifdef CONFIG_HIGHPTE + ClearPagePinned(pte); +@@ -117,14 +117,15 @@ pmd_t *pmd_alloc_one(struct mm_struct *m + + void __pmd_free(pgtable_t pmd) + { +- unsigned long va = (unsigned long)page_address(pmd); +- unsigned int level; +- pte_t *ptep = lookup_address(va, &level); +- +- BUG_ON(!ptep || level != PG_LEVEL_4K || !pte_present(*ptep)); +- if (!pte_write(*ptep) +- && HYPERVISOR_update_va_mapping(va, mk_pte(pmd, PAGE_KERNEL), 0)) +- BUG(); ++ if (PagePinned(pmd)) { ++ unsigned long pfn = page_to_pfn(pmd); ++ ++ if (HYPERVISOR_update_va_mapping((unsigned long)__va(pfn << PAGE_SHIFT), ++ pfn_pte(pfn, PAGE_KERNEL), ++ 0)) ++ BUG(); ++ ClearPagePinned(pmd); ++ } + + ClearPageForeign(pmd); + init_page_count(pmd); +@@ -212,21 +213,20 @@ static inline unsigned int pgd_walk_set_ + { + unsigned long pfn = page_to_pfn(page); + +- if (PageHighMem(page)) { +- if (pgprot_val(flags) & _PAGE_RW) +- ClearPagePinned(page); +- else +- SetPagePinned(page); +- } else { +- MULTI_update_va_mapping(per_cpu(pb_mcl, cpu) + seq, +- (unsigned long)__va(pfn << PAGE_SHIFT), +- pfn_pte(pfn, flags), 0); +- if (unlikely(++seq == PIN_BATCH)) { +- if (unlikely(HYPERVISOR_multicall_check(per_cpu(pb_mcl, cpu), +- PIN_BATCH, NULL))) +- BUG(); +- seq = 0; +- } ++ if (pgprot_val(flags) & _PAGE_RW) ++ ClearPagePinned(page); ++ else ++ SetPagePinned(page); ++ if (PageHighMem(page)) ++ return seq; ++ MULTI_update_va_mapping(per_cpu(pb_mcl, cpu) + seq, ++ (unsigned long)__va(pfn << PAGE_SHIFT), ++ pfn_pte(pfn, flags), 0); ++ if (unlikely(++seq == PIN_BATCH)) { ++ if (unlikely(HYPERVISOR_multicall_check(per_cpu(pb_mcl, cpu), ++ PIN_BATCH, NULL))) ++ BUG(); ++ seq = 0; + } + + return seq; +@@ -273,6 +273,16 @@ static void pgd_walk(pgd_t *pgd_base, pg + } + } + ++#ifdef CONFIG_X86_PAE ++ for (; g < PTRS_PER_PGD; g++, pgd++) { ++ BUG_ON(pgd_none(*pgd)); ++ pud = pud_offset(pgd, 0); ++ BUG_ON(pud_none(*pud)); ++ pmd = pmd_offset(pud, 0); ++ seq = pgd_walk_set_prot(virt_to_page(pmd),flags,cpu,seq); ++ } ++#endif ++ + mcl = per_cpu(pb_mcl, cpu); + #ifdef CONFIG_X86_64 + if (unlikely(seq > PIN_BATCH - 2)) { +@@ -308,6 +318,51 @@ static void pgd_walk(pgd_t *pgd_base, pg + put_cpu(); + } + ++void __init xen_init_pgd_pin(void) ++{ ++ pgd_t *pgd = init_mm.pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ unsigned int g, u, m; ++ ++ if (xen_feature(XENFEAT_auto_translated_physmap)) ++ return; ++ ++ SetPagePinned(virt_to_page(pgd)); ++ for (g = 0; g < PTRS_PER_PGD; g++, pgd++) { ++#ifndef CONFIG_X86_PAE ++ if (g >= pgd_index(HYPERVISOR_VIRT_START) ++ && g <= pgd_index(HYPERVISOR_VIRT_END - 1)) ++ continue; ++#endif ++ if (!pgd_present(*pgd)) ++ continue; ++ pud = pud_offset(pgd, 0); ++ if (PTRS_PER_PUD > 1) /* not folded */ ++ SetPagePinned(virt_to_page(pud)); ++ for (u = 0; u < PTRS_PER_PUD; u++, pud++) { ++ if (!pud_present(*pud)) ++ continue; ++ pmd = pmd_offset(pud, 0); ++ if (PTRS_PER_PMD > 1) /* not folded */ ++ SetPagePinned(virt_to_page(pmd)); ++ for (m = 0; m < PTRS_PER_PMD; m++, pmd++) { ++#ifdef CONFIG_X86_PAE ++ if (g == pgd_index(HYPERVISOR_VIRT_START) ++ && m >= pmd_index(HYPERVISOR_VIRT_START)) ++ continue; ++#endif ++ if (!pmd_present(*pmd)) ++ continue; ++ SetPagePinned(pmd_page(*pmd)); ++ } ++ } ++ } ++#ifdef CONFIG_X86_64 ++ SetPagePinned(virt_to_page(level3_user_pgt)); ++#endif ++} ++ + static void __pgd_pin(pgd_t *pgd) + { + pgd_walk(pgd, PAGE_KERNEL_RO); +@@ -498,21 +553,18 @@ static void pgd_dtor(pgd_t *pgd) + + void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmd) + { +- struct page *page = virt_to_page(pmd); +- unsigned long pfn = page_to_pfn(page); +- +- paravirt_alloc_pmd(mm, __pa(pmd) >> PAGE_SHIFT); +- + /* Note: almost everything apart from _PAGE_PRESENT is + reserved at the pmd (PDPT) level. */ +- if (PagePinned(virt_to_page(mm->pgd))) { +- BUG_ON(PageHighMem(page)); +- BUG_ON(HYPERVISOR_update_va_mapping( +- (unsigned long)__va(pfn << PAGE_SHIFT), +- pfn_pte(pfn, PAGE_KERNEL_RO), 0)); +- set_pud(pudp, __pud(__pa(pmd) | _PAGE_PRESENT)); +- } else +- *pudp = __pud(__pa(pmd) | _PAGE_PRESENT); ++ pud_t pud = __pud(__pa(pmd) | _PAGE_PRESENT); ++ ++ paravirt_alloc_pmd(mm, page_to_pfn(virt_to_page(pmd))); ++ ++ if (likely(!PagePinned(virt_to_page(pudp)))) { ++ *pudp = pud; ++ return; ++ } ++ ++ set_pud(pudp, pud); + + /* + * According to Intel App note "TLBs, Paging-Structure Caches, +@@ -607,13 +659,10 @@ static void pgd_prepopulate_pmd(struct m + i++, pud++, addr += PUD_SIZE) { + pmd_t *pmd = pmds[i]; + +- if (i >= KERNEL_PGD_BOUNDARY) { ++ if (i >= KERNEL_PGD_BOUNDARY) + memcpy(pmd, + (pmd_t *)pgd_page_vaddr(swapper_pg_dir[i]), + sizeof(pmd_t) * PTRS_PER_PMD); +- make_lowmem_page_readonly( +- pmd, XENFEAT_writable_page_tables); +- } + + /* It is safe to poke machine addresses of pmds under the pgd_lock. */ + pud_populate(mm, pud, pmd); diff --git a/patches.xen/xen-x86_64-dump-user-pgt b/patches.xen/xen-x86_64-dump-user-pgt new file mode 100644 index 0000000..4028a75 --- /dev/null +++ b/patches.xen/xen-x86_64-dump-user-pgt @@ -0,0 +1,51 @@ +From: jbeulich@novell.com +Subject: dump the correct page tables for user mode faults +Patch-mainline: obsolete + +--- head-2010-03-15.orig/arch/x86/mm/fault-xen.c 2010-01-28 10:38:23.000000000 +0100 ++++ head-2010-03-15/arch/x86/mm/fault-xen.c 2010-01-25 14:00:59.000000000 +0100 +@@ -329,6 +329,7 @@ static void dump_pagetable(unsigned long + out: + printk(KERN_CONT "\n"); + } ++#define dump_pagetable(addr, krnl) dump_pagetable(addr) + + #else /* CONFIG_X86_64: */ + +@@ -453,7 +454,7 @@ static int bad_address(void *p) + return probe_kernel_address((unsigned long *)p, dummy); + } + +-static void dump_pagetable(unsigned long address) ++static void dump_pagetable(unsigned long address, bool kernel) + { + pgd_t *base = __va(read_cr3() & PHYSICAL_PAGE_MASK); + pgd_t *pgd = base + pgd_index(address); +@@ -461,6 +462,9 @@ static void dump_pagetable(unsigned long + pmd_t *pmd; + pte_t *pte; + ++ if (!kernel) ++ pgd = __user_pgd(base) + pgd_index(address); ++ + if (bad_address(pgd)) + goto bad; + +@@ -599,7 +603,7 @@ show_fault_oops(struct pt_regs *regs, un + printk(KERN_ALERT "IP:"); + printk_address(regs->ip, 1); + +- dump_pagetable(address); ++ dump_pagetable(address, !(error_code & PF_USER)); + } + + static noinline void +@@ -616,7 +620,7 @@ pgtable_bad(struct pt_regs *regs, unsign + + printk(KERN_ALERT "%s: Corrupted page table at address %lx\n", + tsk->comm, address); +- dump_pagetable(address); ++ dump_pagetable(address, !(error_code & PF_USER)); + + tsk->thread.cr2 = address; + tsk->thread.trap_no = 14; diff --git a/patches.xen/xen-x86_64-note-init-p2m b/patches.xen/xen-x86_64-note-init-p2m new file mode 100644 index 0000000..ba095f9 --- /dev/null +++ b/patches.xen/xen-x86_64-note-init-p2m @@ -0,0 +1,343 @@ +From: jbeulich@novell.com +Subject: eliminate scalability issues from initial mapping setup +Patch-mainline: obsolete +References: bnc#417417 + +Direct Xen to place the initial P->M table outside of the initial +mapping, as otherwise the 1G (implementation) / 2G (theoretical) +restriction on the size of the initial mapping limits the amount +of memory a domain can be handed initially. + +Note that the flags passed to HYPERVISOR_update_va_mapping() from +__make_page_writable() and make_lowmem_page_writable() are +intentionally not including UVMF_ALL. This is intended to be on optimal +choice between the overhead of a potential spurious page fault (as +remote CPUs may still have read-only translations in their TLBs) and +the overhead of cross processor flushes. Flushing on the local CPU +shouldn't be as expensive (and hence can be viewed as an optimization +avoiding the spurious page fault on the local CPU), but is required +when the functions are used before the page fault handler gets set up. + +--- head-2010-05-12.orig/arch/x86/kernel/head64-xen.c 2010-03-24 16:00:05.000000000 +0100 ++++ head-2010-05-12/arch/x86/kernel/head64-xen.c 2010-03-25 14:48:29.000000000 +0100 +@@ -121,6 +121,14 @@ void __init x86_64_start_reservations(ch + + reserve_early(__pa_symbol(&_text), __pa_symbol(&__bss_stop), "TEXT DATA BSS"); + ++ if (xen_feature(XENFEAT_auto_translated_physmap)) ++ xen_start_info->mfn_list = ~0UL; ++ else if (xen_start_info->mfn_list < __START_KERNEL_map) ++ reserve_early(xen_start_info->first_p2m_pfn << PAGE_SHIFT, ++ (xen_start_info->first_p2m_pfn ++ + xen_start_info->nr_p2m_frames) << PAGE_SHIFT, ++ "INITP2M"); ++ + /* + * At this point everything still needed from the boot loader + * or BIOS or kernel text should be early reserved or marked not +--- head-2010-05-12.orig/arch/x86/kernel/head_64-xen.S 2010-03-25 14:46:03.000000000 +0100 ++++ head-2010-05-12/arch/x86/kernel/head_64-xen.S 2010-03-25 14:48:29.000000000 +0100 +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -146,6 +147,7 @@ ENTRY(empty_zero_page) + ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, .quad startup_64) + ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .quad hypercall_page) + ELFNOTE(Xen, XEN_ELFNOTE_L1_MFN_VALID, .quad _PAGE_PRESENT, _PAGE_PRESENT) ++ ELFNOTE(Xen, XEN_ELFNOTE_INIT_P2M, .quad VMEMMAP_START) + ELFNOTE(Xen, XEN_ELFNOTE_FEATURES, .asciz "writable_page_tables|writable_descriptor_tables|auto_translated_physmap|pae_pgdir_above_4gb|supervisor_mode_kernel") + ELFNOTE(Xen, XEN_ELFNOTE_LOADER, .asciz "generic") + ELFNOTE(Xen, XEN_ELFNOTE_SUSPEND_CANCEL, .long 1) +--- head-2010-05-12.orig/arch/x86/kernel/setup-xen.c 2010-04-15 11:48:03.000000000 +0200 ++++ head-2010-05-12/arch/x86/kernel/setup-xen.c 2010-04-15 11:49:47.000000000 +0200 +@@ -1152,7 +1152,7 @@ void __init setup_arch(char **cmdline_p) + difference = xen_start_info->nr_pages - max_pfn; + + set_xen_guest_handle(reservation.extent_start, +- ((unsigned long *)xen_start_info->mfn_list) + max_pfn); ++ phys_to_machine_mapping + max_pfn); + reservation.nr_extents = difference; + ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, + &reservation); +@@ -1169,14 +1169,86 @@ void __init setup_arch(char **cmdline_p) + phys_to_machine_mapping = alloc_bootmem_pages( + max_pfn * sizeof(unsigned long)); + memcpy(phys_to_machine_mapping, +- (unsigned long *)xen_start_info->mfn_list, ++ __va(__pa(xen_start_info->mfn_list)), + p2m_pages * sizeof(unsigned long)); + memset(phys_to_machine_mapping + p2m_pages, ~0, + (max_pfn - p2m_pages) * sizeof(unsigned long)); +- free_bootmem( +- __pa(xen_start_info->mfn_list), +- PFN_PHYS(PFN_UP(xen_start_info->nr_pages * +- sizeof(unsigned long)))); ++ ++#ifdef CONFIG_X86_64 ++ if (xen_start_info->mfn_list == VMEMMAP_START) { ++ /* ++ * Since it is well isolated we can (and since it is ++ * perhaps large we should) also free the page tables ++ * mapping the initial P->M table. ++ */ ++ unsigned long va = VMEMMAP_START, pa; ++ pgd_t *pgd = pgd_offset_k(va); ++ pud_t *pud_page = pud_offset(pgd, 0); ++ ++ BUILD_BUG_ON(VMEMMAP_START & ~PGDIR_MASK); ++ xen_l4_entry_update(pgd, __pgd(0)); ++ for(;;) { ++ pud_t *pud = pud_page + pud_index(va); ++ ++ if (pud_none(*pud)) ++ va += PUD_SIZE; ++ else if (pud_large(*pud)) { ++ pa = pud_val(*pud) & PHYSICAL_PAGE_MASK; ++ make_pages_writable(__va(pa), ++ PUD_SIZE >> PAGE_SHIFT, ++ XENFEAT_writable_page_tables); ++ free_bootmem(pa, PUD_SIZE); ++ va += PUD_SIZE; ++ } else { ++ pmd_t *pmd = pmd_offset(pud, va); ++ ++ if (pmd_large(*pmd)) { ++ pa = pmd_val(*pmd) & PHYSICAL_PAGE_MASK; ++ make_pages_writable(__va(pa), ++ PMD_SIZE >> PAGE_SHIFT, ++ XENFEAT_writable_page_tables); ++ free_bootmem(pa, PMD_SIZE); ++ } else if (!pmd_none(*pmd)) { ++ pte_t *pte = pte_offset_kernel(pmd, va); ++ ++ for (i = 0; i < PTRS_PER_PTE; ++i) { ++ if (pte_none(pte[i])) ++ break; ++ pa = pte_pfn(pte[i]) << PAGE_SHIFT; ++ make_page_writable(__va(pa), ++ XENFEAT_writable_page_tables); ++ free_bootmem(pa, PAGE_SIZE); ++ } ++ ClearPagePinned(virt_to_page(pte)); ++ make_page_writable(pte, ++ XENFEAT_writable_page_tables); ++ free_bootmem(__pa(pte), PAGE_SIZE); ++ } ++ va += PMD_SIZE; ++ if (pmd_index(va)) ++ continue; ++ ClearPagePinned(virt_to_page(pmd)); ++ make_page_writable(pmd, ++ XENFEAT_writable_page_tables); ++ free_bootmem(__pa((unsigned long)pmd ++ & PAGE_MASK), ++ PAGE_SIZE); ++ } ++ if (!pud_index(va)) ++ break; ++ } ++ ClearPagePinned(virt_to_page(pud_page)); ++ make_page_writable(pud_page, ++ XENFEAT_writable_page_tables); ++ free_bootmem(__pa((unsigned long)pud_page & PAGE_MASK), ++ PAGE_SIZE); ++ } else if (!WARN_ON(xen_start_info->mfn_list ++ < __START_KERNEL_map)) ++#endif ++ free_bootmem(__pa(xen_start_info->mfn_list), ++ PFN_PHYS(PFN_UP(xen_start_info->nr_pages * ++ sizeof(unsigned long)))); ++ + + /* + * Initialise the list of the frames that specify the list of +--- head-2010-05-12.orig/arch/x86/mm/init-xen.c 2010-04-15 10:48:32.000000000 +0200 ++++ head-2010-05-12/arch/x86/mm/init-xen.c 2010-04-15 11:49:33.000000000 +0200 +@@ -339,9 +339,22 @@ unsigned long __init_refok init_memory_m + + __flush_tlb_all(); + +- if (!after_bootmem && e820_table_top > e820_table_start) ++ if (!after_bootmem && e820_table_top > e820_table_start) { ++#ifdef CONFIG_X86_64 ++ if (xen_start_info->mfn_list < __START_KERNEL_map ++ && e820_table_start <= xen_start_info->first_p2m_pfn ++ && e820_table_top > xen_start_info->first_p2m_pfn) { ++ reserve_early(e820_table_start << PAGE_SHIFT, ++ xen_start_info->first_p2m_pfn ++ << PAGE_SHIFT, ++ "PGTABLE"); ++ e820_table_start = xen_start_info->first_p2m_pfn ++ + xen_start_info->nr_p2m_frames; ++ } ++#endif + reserve_early(e820_table_start << PAGE_SHIFT, + e820_table_top << PAGE_SHIFT, "PGTABLE"); ++ } + + if (!after_bootmem) + early_memtest(start, end); +--- head-2010-05-12.orig/arch/x86/mm/init_64-xen.c 2010-04-15 11:49:18.000000000 +0200 ++++ head-2010-05-12/arch/x86/mm/init_64-xen.c 2010-04-15 11:49:32.000000000 +0200 +@@ -183,6 +183,17 @@ static int __init nonx32_setup(char *str + } + __setup("noexec32=", nonx32_setup); + ++static __init unsigned long get_table_end(void) ++{ ++ BUG_ON(!e820_table_end); ++ if (xen_start_info->mfn_list < __START_KERNEL_map ++ && e820_table_end == xen_start_info->first_p2m_pfn) { ++ e820_table_end += xen_start_info->nr_p2m_frames; ++ e820_table_top += xen_start_info->nr_p2m_frames; ++ } ++ return e820_table_end++; ++} ++ + /* + * NOTE: This function is marked __ref because it calls __init function + * (alloc_bootmem_pages). It's safe to do it ONLY when after_bootmem == 0. +@@ -194,8 +205,7 @@ static __ref void *spp_getpage(void) + if (after_bootmem) + ptr = (void *) get_zeroed_page(GFP_ATOMIC | __GFP_NOTRACK); + else if (e820_table_end < e820_table_top) { +- ptr = __va(e820_table_end << PAGE_SHIFT); +- e820_table_end++; ++ ptr = __va(get_table_end() << PAGE_SHIFT); + memset(ptr, 0, PAGE_SIZE); + } else + ptr = alloc_bootmem_pages(PAGE_SIZE); +@@ -390,8 +400,7 @@ static __ref void *alloc_low_page(unsign + return adr; + } + +- BUG_ON(!e820_table_end); +- pfn = e820_table_end++; ++ pfn = get_table_end(); + if (pfn >= e820_table_top) + panic("alloc_low_page: ran out of memory"); + +@@ -417,14 +426,29 @@ static inline int __meminit make_readonl + /* Make new page tables read-only on the first pass. */ + if (!xen_feature(XENFEAT_writable_page_tables) + && !max_pfn_mapped +- && (paddr >= (e820_table_start << PAGE_SHIFT)) +- && (paddr < (e820_table_top << PAGE_SHIFT))) +- readonly = 1; ++ && (paddr >= (e820_table_start << PAGE_SHIFT))) { ++ unsigned long top = e820_table_top; ++ ++ /* Account for the range get_table_end() skips. */ ++ if (xen_start_info->mfn_list < __START_KERNEL_map ++ && e820_table_end <= xen_start_info->first_p2m_pfn ++ && top > xen_start_info->first_p2m_pfn) ++ top += xen_start_info->nr_p2m_frames; ++ if (paddr < (top << PAGE_SHIFT)) ++ readonly = 1; ++ } + /* Make old page tables read-only. */ + if (!xen_feature(XENFEAT_writable_page_tables) + && (paddr >= (xen_start_info->pt_base - __START_KERNEL_map)) + && (paddr < (e820_table_end << PAGE_SHIFT))) + readonly = 1; ++ /* Make P->M table (and its page tables) read-only. */ ++ if (!xen_feature(XENFEAT_writable_page_tables) ++ && xen_start_info->mfn_list < __START_KERNEL_map ++ && paddr >= (xen_start_info->first_p2m_pfn << PAGE_SHIFT) ++ && paddr < (xen_start_info->first_p2m_pfn ++ + xen_start_info->nr_p2m_frames) << PAGE_SHIFT) ++ readonly = 1; + + /* + * No need for writable mapping of kernel image. This also ensures that +@@ -724,6 +748,12 @@ void __init xen_init_pt(void) + (PTRS_PER_PUD - pud_index(__START_KERNEL_map)) + * sizeof(*level3_kernel_pgt)); + ++ /* Copy the initial P->M table mappings if necessary. */ ++ addr = pgd_index(xen_start_info->mfn_list); ++ if (addr < pgd_index(__START_KERNEL_map)) ++ init_level4_pgt[addr] = ++ ((pgd_t *)xen_start_info->pt_base)[addr]; ++ + /* Do an early initialization of the fixmap area. */ + addr = __fix_to_virt(FIX_EARLYCON_MEM_BASE); + if (pud_present(level3_kernel_pgt[pud_index(addr)])) { +@@ -755,22 +785,27 @@ void __init xen_init_pt(void) + void __init xen_finish_init_mapping(void) + { + unsigned long start, end; ++ struct mmuext_op mmuext; + + /* Re-vector virtual addresses pointing into the initial + mapping to the just-established permanent ones. */ + xen_start_info = __va(__pa(xen_start_info)); + xen_start_info->pt_base = (unsigned long) + __va(__pa(xen_start_info->pt_base)); +- if (!xen_feature(XENFEAT_auto_translated_physmap)) { ++ if (!xen_feature(XENFEAT_auto_translated_physmap) ++ && xen_start_info->mfn_list >= __START_KERNEL_map) + phys_to_machine_mapping = + __va(__pa(xen_start_info->mfn_list)); +- xen_start_info->mfn_list = (unsigned long) +- phys_to_machine_mapping; +- } + if (xen_start_info->mod_start) + xen_start_info->mod_start = (unsigned long) + __va(__pa(xen_start_info->mod_start)); + ++ /* Unpin the no longer used Xen provided page tables. */ ++ mmuext.cmd = MMUEXT_UNPIN_TABLE; ++ mmuext.arg1.mfn = virt_to_mfn(xen_start_info->pt_base); ++ if (HYPERVISOR_mmuext_op(&mmuext, 1, NULL, DOMID_SELF)) ++ BUG(); ++ + /* Destroy the Xen-created mappings beyond the kernel image. */ + start = PAGE_ALIGN(_brk_end); + end = __START_KERNEL_map + (e820_table_start << PAGE_SHIFT); +--- head-2010-05-12.orig/arch/x86/mm/pageattr-xen.c 2010-03-25 14:37:41.000000000 +0100 ++++ head-2010-05-12/arch/x86/mm/pageattr-xen.c 2010-03-25 14:48:29.000000000 +0100 +@@ -1465,7 +1465,7 @@ static void __make_page_writable(unsigne + + pte = lookup_address(va, &level); + BUG_ON(!pte || level != PG_LEVEL_4K); +- if (HYPERVISOR_update_va_mapping(va, pte_mkwrite(*pte), 0)) ++ if (HYPERVISOR_update_va_mapping(va, pte_mkwrite(*pte), UVMF_INVLPG)) + BUG(); + if (in_secondary_range(va)) { + unsigned long pfn = pte_pfn(*pte); +--- head-2010-05-12.orig/arch/x86/mm/pgtable-xen.c 2010-04-15 11:49:15.000000000 +0200 ++++ head-2010-05-12/arch/x86/mm/pgtable-xen.c 2010-04-15 11:49:41.000000000 +0200 +@@ -344,7 +344,7 @@ void __init xen_init_pgd_pin(void) + if (PTRS_PER_PUD > 1) /* not folded */ + SetPagePinned(virt_to_page(pud)); + for (u = 0; u < PTRS_PER_PUD; u++, pud++) { +- if (!pud_present(*pud)) ++ if (!pud_present(*pud) || pud_large(*pud)) + continue; + pmd = pmd_offset(pud, 0); + if (PTRS_PER_PMD > 1) /* not folded */ +@@ -355,7 +355,7 @@ void __init xen_init_pgd_pin(void) + && m >= pmd_index(HYPERVISOR_VIRT_START)) + continue; + #endif +- if (!pmd_present(*pmd)) ++ if (!pmd_present(*pmd) || pmd_large(*pmd)) + continue; + SetPagePinned(pmd_page(*pmd)); + } +--- head-2010-05-12.orig/arch/x86/mm/pgtable_32-xen.c 2010-05-12 09:09:25.000000000 +0200 ++++ head-2010-05-12/arch/x86/mm/pgtable_32-xen.c 2010-05-12 09:15:36.000000000 +0200 +@@ -175,6 +175,6 @@ void make_lowmem_page_writable(void *va, + pte = lookup_address((unsigned long)va, &level); + BUG_ON(!pte || level != PG_LEVEL_4K || !pte_present(*pte)); + rc = HYPERVISOR_update_va_mapping( +- (unsigned long)va, pte_mkwrite(*pte), 0); ++ (unsigned long)va, pte_mkwrite(*pte), UVMF_INVLPG); + BUG_ON(rc); + } diff --git a/patches.xen/xen-x86_64-pgd-alloc-order b/patches.xen/xen-x86_64-pgd-alloc-order new file mode 100644 index 0000000..4c1d699 --- /dev/null +++ b/patches.xen/xen-x86_64-pgd-alloc-order @@ -0,0 +1,337 @@ +From: jbeulich@novell.com +Subject: don't require order-1 allocations for pgd-s +Patch-mainline: n/a + +At the same time remove the useless user mode pair of init_level4_pgt. + +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:45:56.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:46:03.000000000 +0100 +@@ -102,8 +102,8 @@ void do_hypervisor_callback(struct pt_re + * be MACHINE addresses. + */ + +-void xen_pt_switch(unsigned long ptr); +-void xen_new_user_pt(unsigned long ptr); /* x86_64 only */ ++void xen_pt_switch(pgd_t *); ++void xen_new_user_pt(pgd_t *); /* x86_64 only */ + void xen_load_gs(unsigned int selector); /* x86_64 only */ + void xen_tlb_flush(void); + void xen_invlpg(unsigned long ptr); +@@ -111,7 +111,7 @@ void xen_invlpg(unsigned long ptr); + void xen_l1_entry_update(pte_t *ptr, pte_t val); + void xen_l2_entry_update(pmd_t *ptr, pmd_t val); + void xen_l3_entry_update(pud_t *ptr, pud_t val); /* x86_64/PAE */ +-void xen_l4_entry_update(pgd_t *ptr, int user, pgd_t val); /* x86_64 only */ ++void xen_l4_entry_update(pgd_t *ptr, pgd_t val); /* x86_64 only */ + void xen_pgd_pin(pgd_t *); + void xen_pgd_unpin(pgd_t *); + +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/mmu_context.h 2010-03-24 15:32:27.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/mmu_context.h 2010-03-25 14:46:03.000000000 +0100 +@@ -82,6 +82,9 @@ static inline void switch_mm(struct mm_s + { + unsigned cpu = smp_processor_id(); + struct mmuext_op _op[2 + (sizeof(long) > 4)], *op = _op; ++#ifdef CONFIG_X86_64 ++ pgd_t *upgd; ++#endif + + if (likely(prev != next)) { + BUG_ON(!xen_feature(XENFEAT_writable_page_tables) && +@@ -100,10 +103,11 @@ static inline void switch_mm(struct mm_s + op->arg1.mfn = virt_to_mfn(next->pgd); + op++; + +- /* xen_new_user_pt(__pa(__user_pgd(next->pgd))) */ ++ /* xen_new_user_pt(next->pgd) */ + #ifdef CONFIG_X86_64 + op->cmd = MMUEXT_NEW_USER_BASEPTR; +- op->arg1.mfn = virt_to_mfn(__user_pgd(next->pgd)); ++ upgd = __user_pgd(next->pgd); ++ op->arg1.mfn = likely(upgd) ? virt_to_mfn(upgd) : 0; + op++; + #endif + +@@ -131,7 +135,7 @@ static inline void switch_mm(struct mm_s + * to make sure to use no freed page tables. + */ + load_cr3(next->pgd); +- xen_new_user_pt(__pa(__user_pgd(next->pgd))); ++ xen_new_user_pt(next->pgd); + load_LDT_nolock(&next->context); + } + } +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/pgalloc.h 2010-03-25 14:41:00.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/pgalloc.h 2010-03-25 14:46:03.000000000 +0100 +@@ -123,15 +123,13 @@ static inline void pud_populate(struct m + #endif /* CONFIG_X86_PAE */ + + #if PAGETABLE_LEVELS > 3 +-#define __user_pgd(pgd) ((pgd) + PTRS_PER_PGD) +- + static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud) + { + pgd_t ent = __pgd(_PAGE_TABLE | __pa(pud)); + + paravirt_alloc_pud(mm, __pa(pud) >> PAGE_SHIFT); + if (unlikely(PagePinned(virt_to_page(pgd)))) +- xen_l4_entry_update(pgd, 1, ent); ++ xen_l4_entry_update(pgd, ent); + else + *__user_pgd(pgd) = *pgd = ent; + } +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/pgtable_64.h 2010-03-25 14:41:15.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/pgtable_64.h 2010-03-25 14:46:03.000000000 +0100 +@@ -100,18 +100,25 @@ static inline void xen_set_pud(pud_t *pu + : (void)(*__pudp = xen_make_pud(0)); \ + }) + +-#define __user_pgd(pgd) ((pgd) + PTRS_PER_PGD) ++static inline pgd_t *__user_pgd(pgd_t *pgd) ++{ ++ if (unlikely(((unsigned long)pgd & PAGE_MASK) ++ == (unsigned long)init_level4_pgt)) ++ return NULL; ++ return (pgd_t *)(virt_to_page(pgd)->index ++ + ((unsigned long)pgd & ~PAGE_MASK)); ++} + + static inline void xen_set_pgd(pgd_t *pgdp, pgd_t pgd) + { +- xen_l4_entry_update(pgdp, 0, pgd); ++ xen_l4_entry_update(pgdp, pgd); + } + + #define xen_pgd_clear(pgd) \ + ({ \ + pgd_t *__pgdp = (pgd); \ + PagePinned(virt_to_page(__pgdp)) \ +- ? xen_l4_entry_update(__pgdp, 1, xen_make_pgd(0)) \ ++ ? xen_l4_entry_update(__pgdp, xen_make_pgd(0)) \ + : (void)(*__user_pgd(__pgdp) = *__pgdp = xen_make_pgd(0)); \ + }) + +--- head-2010-04-15.orig/arch/x86/kernel/cpu/common-xen.c 2010-03-25 14:41:15.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/cpu/common-xen.c 2010-03-25 14:46:03.000000000 +0100 +@@ -1037,8 +1037,7 @@ DEFINE_PER_CPU_FIRST(union irq_stack_uni + void xen_switch_pt(void) + { + #ifdef CONFIG_XEN +- xen_pt_switch(__pa_symbol(init_level4_pgt)); +- xen_new_user_pt(__pa_symbol(__user_pgd(init_level4_pgt))); ++ xen_pt_switch(init_level4_pgt); + #endif + } + +--- head-2010-04-15.orig/arch/x86/kernel/head_64-xen.S 2010-03-24 16:00:05.000000000 +0100 ++++ head-2010-04-15/arch/x86/kernel/head_64-xen.S 2010-03-25 14:46:03.000000000 +0100 +@@ -56,14 +56,6 @@ ENTRY(name) + __PAGE_ALIGNED_BSS + NEXT_PAGE(init_level4_pgt) + .fill 512,8,0 +- /* +- * We update two pgd entries to make kernel and user pgd consistent +- * at pgd_populate(). It can be used for kernel modules. So we place +- * this page here for those cases to avoid memory corruption. +- * We also use this page to establish the initial mapping for the +- * vsyscall area. +- */ +- .fill 512,8,0 + + NEXT_PAGE(level3_kernel_pgt) + .fill 512,8,0 +--- head-2010-04-15.orig/arch/x86/mm/hypervisor.c 2010-03-25 17:55:14.000000000 +0100 ++++ head-2010-04-15/arch/x86/mm/hypervisor.c 2010-03-25 17:55:21.000000000 +0100 +@@ -524,7 +524,7 @@ void xen_l3_entry_update(pud_t *ptr, pud + #endif + + #ifdef CONFIG_X86_64 +-void xen_l4_entry_update(pgd_t *ptr, int user, pgd_t val) ++void xen_l4_entry_update(pgd_t *ptr, pgd_t val) + { + mmu_update_t u[2]; + struct page *page = NULL; +@@ -537,8 +537,11 @@ void xen_l4_entry_update(pgd_t *ptr, int + } + u[0].ptr = virt_to_machine(ptr); + u[0].val = __pgd_val(val); +- if (user) { +- u[1].ptr = virt_to_machine(__user_pgd(ptr)); ++ if (((unsigned long)ptr & ~PAGE_MASK) ++ <= pgd_index(TASK_SIZE_MAX) * sizeof(*ptr)) { ++ ptr = __user_pgd(ptr); ++ BUG_ON(!ptr); ++ u[1].ptr = virt_to_machine(ptr); + u[1].val = __pgd_val(val); + do_lN_entry_update(u, 2, page); + } else +@@ -546,21 +549,25 @@ void xen_l4_entry_update(pgd_t *ptr, int + } + #endif /* CONFIG_X86_64 */ + +-void xen_pt_switch(unsigned long ptr) ++#ifdef CONFIG_X86_64 ++void xen_pt_switch(pgd_t *pgd) + { + struct mmuext_op op; + op.cmd = MMUEXT_NEW_BASEPTR; +- op.arg1.mfn = pfn_to_mfn(ptr >> PAGE_SHIFT); ++ op.arg1.mfn = virt_to_mfn(pgd); + BUG_ON(HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF) < 0); + } + +-void xen_new_user_pt(unsigned long ptr) ++void xen_new_user_pt(pgd_t *pgd) + { + struct mmuext_op op; ++ ++ pgd = __user_pgd(pgd); + op.cmd = MMUEXT_NEW_USER_BASEPTR; +- op.arg1.mfn = pfn_to_mfn(ptr >> PAGE_SHIFT); ++ op.arg1.mfn = pgd ? virt_to_mfn(pgd) : 0; + BUG_ON(HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF) < 0); + } ++#endif + + void xen_tlb_flush(void) + { +@@ -637,7 +644,14 @@ void xen_pgd_pin(pgd_t *pgd) + op[0].arg1.mfn = virt_to_mfn(pgd); + #ifdef CONFIG_X86_64 + op[1].cmd = op[0].cmd = MMUEXT_PIN_L4_TABLE; +- op[1].arg1.mfn = virt_to_mfn(__user_pgd(pgd)); ++ pgd = __user_pgd(pgd); ++ if (pgd) ++ op[1].arg1.mfn = virt_to_mfn(pgd); ++ else { ++ op[1].cmd = MMUEXT_PIN_L3_TABLE; ++ op[1].arg1.mfn = pfn_to_mfn(__pa_symbol(level3_user_pgt) ++ >> PAGE_SHIFT); ++ } + #endif + if (HYPERVISOR_mmuext_op(op, NR_PGD_PIN_OPS, NULL, DOMID_SELF) < 0) + BUG(); +@@ -650,8 +664,10 @@ void xen_pgd_unpin(pgd_t *pgd) + op[0].cmd = MMUEXT_UNPIN_TABLE; + op[0].arg1.mfn = virt_to_mfn(pgd); + #ifdef CONFIG_X86_64 ++ pgd = __user_pgd(pgd); ++ BUG_ON(!pgd); + op[1].cmd = MMUEXT_UNPIN_TABLE; +- op[1].arg1.mfn = virt_to_mfn(__user_pgd(pgd)); ++ op[1].arg1.mfn = virt_to_mfn(pgd); + #endif + if (HYPERVISOR_mmuext_op(op, NR_PGD_PIN_OPS, NULL, DOMID_SELF) < 0) + BUG(); +--- head-2010-04-15.orig/arch/x86/mm/init_64-xen.c 2010-04-15 11:49:06.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/init_64-xen.c 2010-04-15 11:49:18.000000000 +0200 +@@ -724,9 +724,6 @@ void __init xen_init_pt(void) + (PTRS_PER_PUD - pud_index(__START_KERNEL_map)) + * sizeof(*level3_kernel_pgt)); + +- __user_pgd(init_level4_pgt)[pgd_index(VSYSCALL_START)] = +- __pgd(__pa_symbol(level3_user_pgt) | _PAGE_TABLE); +- + /* Do an early initialization of the fixmap area. */ + addr = __fix_to_virt(FIX_EARLYCON_MEM_BASE); + if (pud_present(level3_kernel_pgt[pud_index(addr)])) { +@@ -742,8 +739,6 @@ void __init xen_init_pt(void) + + early_make_page_readonly(init_level4_pgt, + XENFEAT_writable_page_tables); +- early_make_page_readonly(__user_pgd(init_level4_pgt), +- XENFEAT_writable_page_tables); + early_make_page_readonly(level3_kernel_pgt, + XENFEAT_writable_page_tables); + early_make_page_readonly(level3_user_pgt, +--- head-2010-04-15.orig/arch/x86/mm/pgtable-xen.c 2010-04-15 11:49:08.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/pgtable-xen.c 2010-04-15 11:49:15.000000000 +0200 +@@ -291,9 +291,11 @@ static void pgd_walk(pgd_t *pgd_base, pg + BUG(); + seq = 0; + } ++ pgd = __user_pgd(pgd_base); ++ BUG_ON(!pgd); + MULTI_update_va_mapping(mcl + seq, +- (unsigned long)__user_pgd(pgd_base), +- pfn_pte(virt_to_phys(__user_pgd(pgd_base))>>PAGE_SHIFT, flags), ++ (unsigned long)pgd, ++ pfn_pte(virt_to_phys(pgd)>>PAGE_SHIFT, flags), + 0); + MULTI_update_va_mapping(mcl + seq + 1, + (unsigned long)pgd_base, +@@ -681,12 +683,29 @@ static void pgd_prepopulate_pmd(struct m + } + } + ++static inline pgd_t *user_pgd_alloc(pgd_t *pgd) ++{ + #ifdef CONFIG_X86_64 +-/* We allocate two contiguous pages for kernel and user. */ +-#define PGD_ORDER 1 +-#else +-#define PGD_ORDER 0 ++ if (pgd) { ++ pgd_t *upgd = (void *)__get_free_page(PGALLOC_GFP); ++ ++ if (upgd) ++ virt_to_page(pgd)->index = (long)upgd; ++ else { ++ free_page((unsigned long)pgd); ++ pgd = NULL; ++ } ++ } ++#endif ++ return pgd; ++} ++ ++static inline void user_pgd_free(pgd_t *pgd) ++{ ++#ifdef CONFIG_X86_64 ++ free_page(virt_to_page(pgd)->index); + #endif ++} + + pgd_t *pgd_alloc(struct mm_struct *mm) + { +@@ -694,7 +713,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm) + pmd_t *pmds[PREALLOCATED_PMDS]; + unsigned long flags; + +- pgd = (pgd_t *)__get_free_pages(PGALLOC_GFP, PGD_ORDER); ++ pgd = user_pgd_alloc((void *)__get_free_page(PGALLOC_GFP)); + + if (pgd == NULL) + goto out; +@@ -733,7 +752,8 @@ pgd_t *pgd_alloc(struct mm_struct *mm) + out_free_pmds: + free_pmds(pmds, mm, !xen_feature(XENFEAT_pae_pgdir_above_4gb)); + out_free_pgd: +- free_pages((unsigned long)pgd, PGD_ORDER); ++ user_pgd_free(pgd); ++ free_page((unsigned long)pgd); + out: + return NULL; + } +@@ -752,7 +772,8 @@ void pgd_free(struct mm_struct *mm, pgd_ + + pgd_mop_up_pmds(mm, pgd); + paravirt_pgd_free(mm, pgd); +- free_pages((unsigned long)pgd, PGD_ORDER); ++ user_pgd_free(pgd); ++ free_page((unsigned long)pgd); + } + + /* blktap and gntdev need this, as otherwise they would implicitly (and +--- head-2010-04-15.orig/drivers/xen/core/machine_reboot.c 2010-03-25 14:41:15.000000000 +0100 ++++ head-2010-04-15/drivers/xen/core/machine_reboot.c 2010-03-25 14:46:03.000000000 +0100 +@@ -191,8 +191,7 @@ static int take_machine_down(void *_susp + * in fast-suspend mode as that implies a new enough Xen. + */ + if (!suspend->fast_suspend) +- xen_new_user_pt(__pa(__user_pgd( +- current->active_mm->pgd))); ++ xen_new_user_pt(current->active_mm->pgd); + #endif + } + diff --git a/patches.xen/xen-x86_64-pgd-pin b/patches.xen/xen-x86_64-pgd-pin new file mode 100644 index 0000000..221ddb2 --- /dev/null +++ b/patches.xen/xen-x86_64-pgd-pin @@ -0,0 +1,111 @@ +From: jbeulich@novell.com +Subject: make pinning of pgd pairs transparent to callers +Patch-mainline: obsolete + +--- head-2010-04-15.orig/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:41:15.000000000 +0100 ++++ head-2010-04-15/arch/x86/include/mach-xen/asm/hypervisor.h 2010-03-25 14:45:56.000000000 +0100 +@@ -112,8 +112,8 @@ void xen_l1_entry_update(pte_t *ptr, pte + void xen_l2_entry_update(pmd_t *ptr, pmd_t val); + void xen_l3_entry_update(pud_t *ptr, pud_t val); /* x86_64/PAE */ + void xen_l4_entry_update(pgd_t *ptr, int user, pgd_t val); /* x86_64 only */ +-void xen_pgd_pin(unsigned long ptr); +-void xen_pgd_unpin(unsigned long ptr); ++void xen_pgd_pin(pgd_t *); ++void xen_pgd_unpin(pgd_t *); + + void xen_init_pgd_pin(void); + +--- head-2010-04-15.orig/arch/x86/mm/hypervisor.c 2010-03-25 14:41:15.000000000 +0100 ++++ head-2010-04-15/arch/x86/mm/hypervisor.c 2010-03-25 17:55:14.000000000 +0100 +@@ -623,26 +623,38 @@ EXPORT_SYMBOL_GPL(xen_invlpg_mask); + + #endif /* CONFIG_SMP */ + +-void xen_pgd_pin(unsigned long ptr) +-{ +- struct mmuext_op op; + #ifdef CONFIG_X86_64 +- op.cmd = MMUEXT_PIN_L4_TABLE; +-#elif defined(CONFIG_X86_PAE) +- op.cmd = MMUEXT_PIN_L3_TABLE; ++#define NR_PGD_PIN_OPS 2 + #else +- op.cmd = MMUEXT_PIN_L2_TABLE; ++#define NR_PGD_PIN_OPS 1 + #endif +- op.arg1.mfn = pfn_to_mfn(ptr >> PAGE_SHIFT); +- BUG_ON(HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF) < 0); ++ ++void xen_pgd_pin(pgd_t *pgd) ++{ ++ struct mmuext_op op[NR_PGD_PIN_OPS]; ++ ++ op[0].cmd = MMUEXT_PIN_L3_TABLE; ++ op[0].arg1.mfn = virt_to_mfn(pgd); ++#ifdef CONFIG_X86_64 ++ op[1].cmd = op[0].cmd = MMUEXT_PIN_L4_TABLE; ++ op[1].arg1.mfn = virt_to_mfn(__user_pgd(pgd)); ++#endif ++ if (HYPERVISOR_mmuext_op(op, NR_PGD_PIN_OPS, NULL, DOMID_SELF) < 0) ++ BUG(); + } + +-void xen_pgd_unpin(unsigned long ptr) ++void xen_pgd_unpin(pgd_t *pgd) + { +- struct mmuext_op op; +- op.cmd = MMUEXT_UNPIN_TABLE; +- op.arg1.mfn = pfn_to_mfn(ptr >> PAGE_SHIFT); +- BUG_ON(HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF) < 0); ++ struct mmuext_op op[NR_PGD_PIN_OPS]; ++ ++ op[0].cmd = MMUEXT_UNPIN_TABLE; ++ op[0].arg1.mfn = virt_to_mfn(pgd); ++#ifdef CONFIG_X86_64 ++ op[1].cmd = MMUEXT_UNPIN_TABLE; ++ op[1].arg1.mfn = virt_to_mfn(__user_pgd(pgd)); ++#endif ++ if (HYPERVISOR_mmuext_op(op, NR_PGD_PIN_OPS, NULL, DOMID_SELF) < 0) ++ BUG(); + } + + void xen_set_ldt(const void *ptr, unsigned int ents) +--- head-2010-04-15.orig/arch/x86/mm/init_64-xen.c 2010-04-15 11:48:38.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/init_64-xen.c 2010-04-15 11:49:06.000000000 +0200 +@@ -753,10 +753,8 @@ void __init xen_init_pt(void) + early_make_page_readonly(level1_fixmap_pgt, + XENFEAT_writable_page_tables); + +- if (!xen_feature(XENFEAT_writable_page_tables)) { +- xen_pgd_pin(__pa_symbol(init_level4_pgt)); +- xen_pgd_pin(__pa_symbol(__user_pgd(init_level4_pgt))); +- } ++ if (!xen_feature(XENFEAT_writable_page_tables)) ++ xen_pgd_pin(init_level4_pgt); + } + + void __init xen_finish_init_mapping(void) +--- head-2010-04-15.orig/arch/x86/mm/pgtable-xen.c 2010-04-15 11:48:29.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/pgtable-xen.c 2010-04-15 11:49:08.000000000 +0200 +@@ -368,19 +368,13 @@ static void __pgd_pin(pgd_t *pgd) + { + pgd_walk(pgd, PAGE_KERNEL_RO); + kmap_flush_unused(); +- xen_pgd_pin(__pa(pgd)); /* kernel */ +-#ifdef CONFIG_X86_64 +- xen_pgd_pin(__pa(__user_pgd(pgd))); /* user */ +-#endif ++ xen_pgd_pin(pgd); + SetPagePinned(virt_to_page(pgd)); + } + + static void __pgd_unpin(pgd_t *pgd) + { +- xen_pgd_unpin(__pa(pgd)); +-#ifdef CONFIG_X86_64 +- xen_pgd_unpin(__pa(__user_pgd(pgd))); +-#endif ++ xen_pgd_unpin(pgd); + pgd_walk(pgd, PAGE_KERNEL); + ClearPagePinned(virt_to_page(pgd)); + } diff --git a/patches.xen/xen3-acpi_processor_check_maxcpus.patch b/patches.xen/xen3-acpi_processor_check_maxcpus.patch new file mode 100644 index 0000000..50c7be2 --- /dev/null +++ b/patches.xen/xen3-acpi_processor_check_maxcpus.patch @@ -0,0 +1,37 @@ +From: Thomas Renninger +Subject: Do not try to set up acpi processor stuff on cores exceeding maxcpus= +References: bnc#601520 +Patch-Mainline: Not yet + +Signed-off-by: Thomas Renninger + +Automatically created from "patches.fixes/acpi_processor_check_maxcpus.patch" by xen-port-patches.py + +--- head-2010-05-25.orig/drivers/acpi/processor_driver.c 2010-05-25 09:25:03.000000000 +0200 ++++ head-2010-05-25/drivers/acpi/processor_driver.c 2010-05-25 09:51:32.000000000 +0200 +@@ -448,6 +448,11 @@ static int acpi_processor_get_info(struc + return -ENODEV; + } + } ++#if defined(CONFIG_SMP) && defined(CONFIG_PROCESSOR_EXTERNAL_CONTROL) ++ if (pr->id >= setup_max_cpus && pr->id > 0) ++ pr->id = -1; ++#endif ++ + /* + * On some boxes several processors use the same processor bus id. + * But they are located in different scope. For example: +@@ -597,8 +602,11 @@ static int __cpuinit acpi_processor_add( + } + + #ifdef CONFIG_SMP +- if (pr->id >= setup_max_cpus && pr->id != 0) +- return 0; ++ if (pr->id >= setup_max_cpus && pr->id != 0) { ++ if (!processor_cntl_external()) ++ return 0; ++ WARN_ON(pr->id != -1); ++ } + #endif + + BUG_ON(!processor_cntl_external() && diff --git a/patches.xen/xen3-auto-arch-i386.diff b/patches.xen/xen3-auto-arch-i386.diff new file mode 100644 index 0000000..cb7ffdd --- /dev/null +++ b/patches.xen/xen3-auto-arch-i386.diff @@ -0,0 +1,193 @@ +Subject: xen3 arch-i386 +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 1017:948c933f8839) +Patch-mainline: n/a +Acked-by: jbeulich@novell.com + +--- head-2010-01-19.orig/arch/x86/kernel/asm-offsets_32.c 2009-09-10 00:13:59.000000000 +0200 ++++ head-2010-01-19/arch/x86/kernel/asm-offsets_32.c 2010-01-19 16:00:16.000000000 +0100 +@@ -93,9 +93,14 @@ void foo(void) + OFFSET(pbe_orig_address, pbe, orig_address); + OFFSET(pbe_next, pbe, next); + ++#ifndef CONFIG_X86_NO_TSS + /* Offset from the sysenter stack to tss.sp0 */ +- DEFINE(TSS_sysenter_sp0, offsetof(struct tss_struct, x86_tss.sp0) - ++ DEFINE(SYSENTER_stack_sp0, offsetof(struct tss_struct, x86_tss.sp0) - + sizeof(struct tss_struct)); ++#else ++ /* sysenter stack points directly to sp0 */ ++ DEFINE(SYSENTER_stack_sp0, 0); ++#endif + + DEFINE(PAGE_SIZE_asm, PAGE_SIZE); + DEFINE(PAGE_SHIFT_asm, PAGE_SHIFT); +--- head-2010-01-19.orig/arch/x86/kernel/entry_32.S 2010-01-19 13:27:24.000000000 +0100 ++++ head-2010-01-19/arch/x86/kernel/entry_32.S 2010-01-19 16:00:16.000000000 +0100 +@@ -401,7 +401,7 @@ ENTRY(ia32_sysenter_target) + CFI_SIGNAL_FRAME + CFI_DEF_CFA esp, 0 + CFI_REGISTER esp, ebp +- movl TSS_sysenter_sp0(%esp),%esp ++ movl SYSENTER_stack_sp0(%esp),%esp + sysenter_past_esp: + /* + * Interrupts are disabled here, but we can't trace it until +@@ -1381,7 +1381,7 @@ END(page_fault) + * that sets up the real kernel stack. Check here, since we can't + * allow the wrong stack to be used. + * +- * "TSS_sysenter_sp0+12" is because the NMI/debug handler will have ++ * "SYSENTER_stack_sp0+12" is because the NMI/debug handler will have + * already pushed 3 words if it hits on the sysenter instruction: + * eflags, cs and eip. + * +@@ -1393,7 +1393,7 @@ END(page_fault) + cmpw $__KERNEL_CS, 4(%esp) + jne \ok + \label: +- movl TSS_sysenter_sp0 + \offset(%esp), %esp ++ movl SYSENTER_stack_sp0 + \offset(%esp), %esp + CFI_DEF_CFA esp, 0 + CFI_UNDEFINED eip + pushfl +--- head-2010-01-19.orig/arch/x86/kernel/machine_kexec_32.c 2010-01-19 14:51:07.000000000 +0100 ++++ head-2010-01-19/arch/x86/kernel/machine_kexec_32.c 2010-01-19 16:00:16.000000000 +0100 +@@ -27,6 +27,10 @@ + #include + #include + ++#ifdef CONFIG_XEN ++#include ++#endif ++ + static void machine_kexec_free_page_tables(struct kimage *image) + { + free_page((unsigned long)image->arch.pgd); +@@ -97,6 +101,55 @@ static void machine_kexec_prepare_page_t + __pa(control_page), __pa(control_page)); + } + ++#ifdef CONFIG_XEN ++ ++#define __ma(x) (pfn_to_mfn(__pa((x)) >> PAGE_SHIFT) << PAGE_SHIFT) ++ ++#if PAGES_NR > KEXEC_XEN_NO_PAGES ++#error PAGES_NR is greater than KEXEC_XEN_NO_PAGES - Xen support will break ++#endif ++ ++#if PA_CONTROL_PAGE != 0 ++#error PA_CONTROL_PAGE is non zero - Xen support will break ++#endif ++ ++void machine_kexec_setup_load_arg(xen_kexec_image_t *xki, struct kimage *image) ++{ ++ void *control_page; ++ ++ memset(xki->page_list, 0, sizeof(xki->page_list)); ++ ++ control_page = page_address(image->control_code_page); ++ memcpy(control_page, relocate_kernel, PAGE_SIZE); ++ ++ xki->page_list[PA_CONTROL_PAGE] = __ma(control_page); ++ xki->page_list[PA_PGD] = __ma(kexec_pgd); ++#ifdef CONFIG_X86_PAE ++ xki->page_list[PA_PMD_0] = __ma(kexec_pmd0); ++ xki->page_list[PA_PMD_1] = __ma(kexec_pmd1); ++#endif ++ xki->page_list[PA_PTE_0] = __ma(kexec_pte0); ++ xki->page_list[PA_PTE_1] = __ma(kexec_pte1); ++ ++} ++ ++int __init machine_kexec_setup_resources(struct resource *hypervisor, ++ struct resource *phys_cpus, ++ int nr_phys_cpus) ++{ ++ int k; ++ ++ /* The per-cpu crash note resources belong to the hypervisor resource */ ++ for (k = 0; k < nr_phys_cpus; k++) ++ request_resource(hypervisor, phys_cpus + k); ++ ++ return 0; ++} ++ ++void machine_kexec_register_resources(struct resource *res) { ; } ++ ++#endif /* CONFIG_XEN */ ++ + /* + * A architecture hook called to validate the + * proposed image and prepare the control pages +@@ -134,6 +187,7 @@ void machine_kexec_cleanup(struct kimage + machine_kexec_free_page_tables(image); + } + ++#ifndef CONFIG_XEN + /* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. +@@ -199,6 +253,7 @@ void machine_kexec(struct kimage *image) + + __ftrace_enabled_restore(save_ftrace_enabled); + } ++#endif + + void arch_crash_save_vmcoreinfo(void) + { +--- head-2010-01-19.orig/arch/x86/kernel/vm86_32.c 2010-01-19 13:26:11.000000000 +0100 ++++ head-2010-01-19/arch/x86/kernel/vm86_32.c 2010-01-19 16:00:16.000000000 +0100 +@@ -125,7 +125,9 @@ static int copy_vm86_regs_from_user(stru + + struct pt_regs *save_v86_state(struct kernel_vm86_regs *regs) + { ++#ifndef CONFIG_X86_NO_TSS + struct tss_struct *tss; ++#endif + struct pt_regs *ret; + unsigned long tmp; + +@@ -148,12 +150,16 @@ struct pt_regs *save_v86_state(struct ke + do_exit(SIGSEGV); + } + ++#ifndef CONFIG_X86_NO_TSS + tss = &per_cpu(init_tss, get_cpu()); ++#endif + current->thread.sp0 = current->thread.saved_sp0; + current->thread.sysenter_cs = __KERNEL_CS; + load_sp0(tss, ¤t->thread); + current->thread.saved_sp0 = 0; ++#ifndef CONFIG_X86_NO_TSS + put_cpu(); ++#endif + + ret = KVM86->regs32; + +@@ -279,7 +285,9 @@ out: + + static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk) + { ++#ifndef CONFIG_X86_NO_TSS + struct tss_struct *tss; ++#endif + /* + * make sure the vm86() system call doesn't try to do anything silly + */ +@@ -323,12 +331,16 @@ static void do_sys_vm86(struct kernel_vm + tsk->thread.saved_fs = info->regs32->fs; + tsk->thread.saved_gs = get_user_gs(info->regs32); + ++#ifndef CONFIG_X86_NO_TSS + tss = &per_cpu(init_tss, get_cpu()); ++#endif + tsk->thread.sp0 = (unsigned long) &info->VM86_TSS_ESP0; + if (cpu_has_sep) + tsk->thread.sysenter_cs = 0; + load_sp0(tss, &tsk->thread); ++#ifndef CONFIG_X86_NO_TSS + put_cpu(); ++#endif + + tsk->thread.screen_bitmap = info->screen_bitmap; + if (info->flags & VM86_SCREEN_BITMAP) diff --git a/patches.xen/xen3-auto-arch-x86.diff b/patches.xen/xen3-auto-arch-x86.diff new file mode 100644 index 0000000..ebface2 --- /dev/null +++ b/patches.xen/xen3-auto-arch-x86.diff @@ -0,0 +1,443 @@ +Subject: xen3 arch-x86 +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 1017:948c933f8839) +Patch-mainline: n/a +Acked-by: jbeulich@novell.com + +List of files that don't require modification anymore (and hence +removed from this patch), for reference and in case upstream wants to +take the forward porting patches: +2.6.26/arch/x86/kernel/crash.c +2.6.30/arch/x86/kernel/acpi/boot.c + +--- head-2010-04-15.orig/arch/x86/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -111,6 +111,10 @@ endif + # prevent gcc from generating any FP code by mistake + KBUILD_CFLAGS += $(call cc-option,-mno-sse -mno-mmx -mno-sse2 -mno-3dnow,) + ++# Xen subarch support ++mflags-$(CONFIG_X86_XEN) := -Iinclude/asm-x86/mach-xen ++mcore-$(CONFIG_X86_XEN) := arch/x86/mach-xen/ ++ + KBUILD_CFLAGS += $(mflags-y) + KBUILD_AFLAGS += $(mflags-y) + +@@ -149,9 +153,26 @@ boot := arch/x86/boot + + BOOT_TARGETS = bzlilo bzdisk fdimage fdimage144 fdimage288 isoimage + +-PHONY += bzImage $(BOOT_TARGETS) ++PHONY += bzImage vmlinuz $(BOOT_TARGETS) ++ ++ifdef CONFIG_XEN ++CPPFLAGS := -D__XEN_INTERFACE_VERSION__=$(CONFIG_XEN_INTERFACE_VERSION) \ ++ -Iinclude$(if $(KBUILD_SRC),2)/asm/mach-xen $(CPPFLAGS) ++ ++ifdef CONFIG_X86_64 ++LDFLAGS_vmlinux := -e startup_64 ++endif + + # Default kernel to build ++all: vmlinuz ++ ++# KBUILD_IMAGE specifies the target image being built ++KBUILD_IMAGE := $(boot)/vmlinuz ++ ++vmlinuz: vmlinux ++ $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE) ++else ++# Default kernel to build + all: bzImage + + # KBUILD_IMAGE specify target image being built +@@ -167,6 +188,7 @@ endif + + $(BOOT_TARGETS): vmlinux + $(Q)$(MAKE) $(build)=$(boot) $@ ++endif + + PHONY += install + install: +--- head-2010-04-15.orig/arch/x86/boot/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/boot/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -23,6 +23,7 @@ ROOT_DEV := CURRENT + SVGA_MODE := -DSVGA_MODE=NORMAL_VGA + + targets := vmlinux.bin setup.bin setup.elf bzImage ++targets += vmlinuz vmlinux-stripped + targets += fdimage fdimage144 fdimage288 image.iso mtools.conf + subdir- := compressed + +@@ -195,6 +196,14 @@ bzlilo: $(obj)/bzImage + cp System.map $(INSTALL_PATH)/ + if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi + ++$(obj)/vmlinuz: $(obj)/vmlinux-stripped FORCE ++ $(call if_changed,gzip) ++ @echo 'Kernel: $@ is ready' ' (#'`cat .version`')' ++ ++$(obj)/vmlinux-stripped: OBJCOPYFLAGS := -g --strip-unneeded ++$(obj)/vmlinux-stripped: vmlinux FORCE ++ $(call if_changed,objcopy) ++ + install: + sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(obj)/bzImage \ + System.map "$(INSTALL_PATH)" +--- head-2010-04-15.orig/arch/x86/kernel/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -117,9 +117,12 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) + + obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o + ++obj-$(CONFIG_X86_XEN) += fixup.o ++ + ### + # 64 bit specific files + ifeq ($(CONFIG_X86_64),y) ++ obj-$(CONFIG_X86_XEN_GENAPIC) += genapic_xen_64.o + obj-$(CONFIG_X86_UV) += tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o + obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o + obj-$(CONFIG_AUDIT) += audit_64.o +@@ -130,4 +133,10 @@ ifeq ($(CONFIG_X86_64),y) + + obj-$(CONFIG_PCI_MMCONFIG) += mmconf-fam10h_64.o + obj-y += vsmp_64.o ++ ++ time_64-$(CONFIG_XEN) += time_32.o ++ pci-dma_64-$(CONFIG_XEN) += pci-dma_32.o + endif ++ ++disabled-obj-$(CONFIG_XEN) := i8259_$(BITS).o reboot.o smpboot_$(BITS).o ++%/head_$(BITS).o %/head_$(BITS).s: $(if $(CONFIG_XEN),EXTRA_AFLAGS,dummy) := +--- head-2010-04-15.orig/arch/x86/kernel/acpi/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/acpi/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -5,6 +5,9 @@ obj-$(CONFIG_ACPI_SLEEP) += sleep.o wake + + ifneq ($(CONFIG_ACPI_PROCESSOR),) + obj-y += cstate.o ++ifneq ($(CONFIG_PROCESSOR_EXTERNAL_CONTROL),) ++obj-$(CONFIG_XEN) += processor_extcntl_xen.o ++endif + endif + + $(obj)/wakeup_rm.o: $(obj)/realmode/wakeup.bin +@@ -12,3 +15,4 @@ $(obj)/wakeup_rm.o: $(obj)/realmode/w + $(obj)/realmode/wakeup.bin: FORCE + $(Q)$(MAKE) $(build)=$(obj)/realmode + ++disabled-obj-$(CONFIG_XEN) := cstate.o wakeup_$(BITS).o +--- head-2010-04-15.orig/arch/x86/kernel/cpu/mcheck/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/cpu/mcheck/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -4,6 +4,7 @@ obj-$(CONFIG_X86_ANCIENT_MCE) += winchip + obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o + obj-$(CONFIG_X86_MCE_XEON75XX) += mce-xeon75xx.o + obj-$(CONFIG_X86_MCE_AMD) += mce_amd.o ++obj-$(CONFIG_X86_XEN_MCE) += mce_dom0.o + obj-$(CONFIG_X86_MCE_THRESHOLD) += threshold.o + obj-$(CONFIG_X86_MCE_INJECT) += mce-inject.o + +--- head-2010-04-15.orig/arch/x86/kernel/cpu/mcheck/mce.c 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/cpu/mcheck/mce.c 2010-04-15 09:44:40.000000000 +0200 +@@ -1149,8 +1149,15 @@ void mce_log_therm_throt_event(__u64 sta + * Periodic polling timer for "silent" machine check errors. If the + * poller finds an MCE, poll 2x faster. When the poller finds no more + * errors, poll 2x slower (up to check_interval seconds). ++ * ++ * We will disable polling in DOM0 since all CMCI/Polling ++ * mechanism will be done in XEN for Intel CPUs + */ ++#if defined (CONFIG_X86_XEN_MCE) ++static int check_interval = 0; /* disable polling */ ++#else + static int check_interval = 5 * 60; /* 5 minutes */ ++#endif + + static DEFINE_PER_CPU(int, mce_next_interval); /* in jiffies */ + static DEFINE_PER_CPU(struct timer_list, mce_timer); +@@ -1315,6 +1322,7 @@ static int __cpuinit __mcheck_cpu_apply_ + + /* This should be disabled by the BIOS, but isn't always */ + if (c->x86_vendor == X86_VENDOR_AMD) { ++#ifndef CONFIG_XEN + if (c->x86 == 15 && banks > 4) { + /* + * disable GART TBL walk error reporting, which +@@ -1323,6 +1331,7 @@ static int __cpuinit __mcheck_cpu_apply_ + */ + clear_bit(10, (unsigned long *)&mce_banks[4].ctl); + } ++#endif + if (c->x86 <= 17 && mce_bootlog < 0) { + /* + * Lots of broken BIOS around that don't clear them +@@ -1390,6 +1399,7 @@ static void __cpuinit __mcheck_cpu_ancie + + static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c) + { ++#ifndef CONFIG_X86_64_XEN + switch (c->x86_vendor) { + case X86_VENDOR_INTEL: + mce_intel_feature_init(c); +@@ -1400,6 +1410,7 @@ static void __mcheck_cpu_init_vendor(str + default: + break; + } ++#endif + } + + static void __mcheck_cpu_init_timer(void) +@@ -2096,6 +2107,16 @@ static __init int mcheck_init_device(voi + register_hotcpu_notifier(&mce_cpu_notifier); + misc_register(&mce_log_device); + ++#ifdef CONFIG_X86_XEN_MCE ++ if (is_initial_xendomain()) { ++ /* Register vIRQ handler for MCE LOG processing */ ++ extern void bind_virq_for_mce(void); ++ ++ printk(KERN_DEBUG "MCE: bind virq for DOM0 logging\n"); ++ bind_virq_for_mce(); ++ } ++#endif ++ + return err; + } + +--- head-2010-04-15.orig/arch/x86/kernel/cpu/mtrr/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/cpu/mtrr/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -1,3 +1,4 @@ + obj-y := main.o if.o generic.o cleanup.o + obj-$(CONFIG_X86_32) += amd.o cyrix.o centaur.o + ++obj-$(CONFIG_XEN) := main.o if.o +--- head-2010-04-15.orig/arch/x86/lib/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/lib/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -41,3 +41,5 @@ else + lib-y += copy_user_64.o rwlock_64.o copy_user_nocache_64.o + lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem_64.o + endif ++ ++lib-$(CONFIG_XEN_SCRUB_PAGES) += scrub.o +--- head-2010-04-15.orig/arch/x86/mm/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/mm/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -25,4 +25,6 @@ obj-$(CONFIG_NUMA) += numa.o numa_$(BIT + obj-$(CONFIG_K8_NUMA) += k8topology_64.o + obj-$(CONFIG_ACPI_NUMA) += srat_$(BITS).o + ++obj-$(CONFIG_XEN) += hypervisor.o ++ + obj-$(CONFIG_MEMTEST) += memtest.o +--- head-2010-04-15.orig/arch/x86/oprofile/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/oprofile/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -6,7 +6,14 @@ DRIVER_OBJS = $(addprefix ../../../drive + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + ++ifdef CONFIG_XEN ++XENOPROF_COMMON_OBJS = $(addprefix ../../../drivers/xen/xenoprof/, \ ++ xenoprofile.o) ++oprofile-y := $(DRIVER_OBJS) \ ++ $(XENOPROF_COMMON_OBJS) xenoprof.o ++else + oprofile-y := $(DRIVER_OBJS) init.o backtrace.o + oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_amd.o \ + op_model_ppro.o op_model_p4.o + oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o ++endif +--- head-2010-04-15.orig/arch/x86/pci/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/pci/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -4,6 +4,9 @@ obj-$(CONFIG_PCI_BIOS) += pcbios.o + obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o + obj-$(CONFIG_PCI_DIRECT) += direct.o + obj-$(CONFIG_PCI_OLPC) += olpc.o ++# pcifront should be after mmconfig.o and direct.o as it should only ++# take over if direct access to the PCI bus is unavailable ++obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront.o + + obj-y += fixup.o + obj-$(CONFIG_ACPI) += acpi.o +--- head-2010-04-15.orig/arch/x86/power/cpu.c 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/power/cpu.c 2010-03-24 15:01:37.000000000 +0100 +@@ -126,6 +126,7 @@ static void do_fpu_end(void) + + static void fix_processor_context(void) + { ++#ifndef CONFIG_X86_NO_TSS + int cpu = smp_processor_id(); + struct tss_struct *t = &per_cpu(init_tss, cpu); + +@@ -138,7 +139,10 @@ static void fix_processor_context(void) + + #ifdef CONFIG_X86_64 + get_cpu_gdt_table(cpu)[GDT_ENTRY_TSS].type = 9; ++#endif ++#endif + ++#ifdef CONFIG_X86_64 + syscall_init(); /* This sets MSR_*STAR and related */ + #endif + load_TR_desc(); /* This does ltr */ +--- head-2010-04-15.orig/arch/x86/include/asm/acpi.h 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/asm/acpi.h 2010-03-24 15:01:37.000000000 +0100 +@@ -30,6 +30,10 @@ + #include + #include + ++#ifdef CONFIG_XEN ++#include ++#endif ++ + #define COMPILER_DEPENDENT_INT64 long long + #define COMPILER_DEPENDENT_UINT64 unsigned long long + +@@ -120,6 +124,27 @@ extern unsigned long acpi_wakeup_address + /* early initialization routine */ + extern void acpi_reserve_wakeup_memory(void); + ++#ifdef CONFIG_XEN ++static inline int acpi_notify_hypervisor_state(u8 sleep_state, ++ u32 pm1a_cnt_val, ++ u32 pm1b_cnt_val) ++{ ++ struct xen_platform_op op = { ++ .cmd = XENPF_enter_acpi_sleep, ++ .interface_version = XENPF_INTERFACE_VERSION, ++ .u = { ++ .enter_acpi_sleep = { ++ .pm1a_cnt_val = pm1a_cnt_val, ++ .pm1b_cnt_val = pm1b_cnt_val, ++ .sleep_state = sleep_state, ++ }, ++ }, ++ }; ++ ++ return HYPERVISOR_platform_op(&op); ++} ++#endif /* CONFIG_XEN */ ++ + /* + * Check if the CPU can handle C2 and deeper + */ +@@ -178,7 +203,9 @@ static inline void disable_acpi(void) { + + #endif /* !CONFIG_ACPI */ + ++#ifndef CONFIG_XEN + #define ARCH_HAS_POWER_INIT 1 ++#endif + + struct bootnode; + +--- head-2010-04-15.orig/arch/x86/include/asm/apic.h 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/asm/apic.h 2010-03-24 15:01:37.000000000 +0100 +@@ -15,7 +15,9 @@ + #include + #include + ++#ifndef CONFIG_XEN + #define ARCH_APICTIMER_STOPS_ON_C3 1 ++#endif + + /* + * Debugging macros +--- head-2010-04-15.orig/arch/x86/include/asm/kexec.h 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/asm/kexec.h 2010-03-24 15:01:37.000000000 +0100 +@@ -163,6 +163,19 @@ struct kimage_arch { + }; + #endif + ++/* Under Xen we need to work with machine addresses. These macros give the ++ * machine address of a certain page to the generic kexec code instead of ++ * the pseudo physical address which would be given by the default macros. ++ */ ++ ++#ifdef CONFIG_XEN ++#define KEXEC_ARCH_HAS_PAGE_MACROS ++#define kexec_page_to_pfn(page) pfn_to_mfn(page_to_pfn(page)) ++#define kexec_pfn_to_page(pfn) pfn_to_page(mfn_to_pfn(pfn)) ++#define kexec_virt_to_phys(addr) virt_to_machine(addr) ++#define kexec_phys_to_virt(addr) phys_to_virt(machine_to_phys(addr)) ++#endif ++ + #endif /* __ASSEMBLY__ */ + + #endif /* _ASM_X86_KEXEC_H */ +--- head-2010-04-15.orig/arch/x86/include/asm/types.h 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/include/asm/types.h 2010-03-24 15:01:37.000000000 +0100 +@@ -9,7 +9,7 @@ + #ifndef __ASSEMBLY__ + + typedef u64 dma64_addr_t; +-#if defined(CONFIG_X86_64) || defined(CONFIG_HIGHMEM64G) ++#if defined(CONFIG_X86_64) || defined(CONFIG_XEN) || defined(CONFIG_HIGHMEM64G) + /* DMA addresses come in 32-bit and 64-bit flavours. */ + typedef u64 dma_addr_t; + #else +--- head-2010-04-15.orig/arch/x86/vdso/Makefile 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/vdso/Makefile 2010-03-24 15:01:37.000000000 +0100 +@@ -65,6 +65,8 @@ obj-$(VDSO32-y) += vdso32-syms.lds + vdso32.so-$(VDSO32-y) += int80 + vdso32.so-$(CONFIG_COMPAT) += syscall + vdso32.so-$(VDSO32-y) += sysenter ++xen-vdso32-$(subst 1,$(CONFIG_COMPAT),$(shell expr $(CONFIG_XEN_COMPAT)0 '<' 0x0302000)) += int80 ++vdso32.so-$(CONFIG_XEN) += $(xen-vdso32-y) + + vdso32-images = $(vdso32.so-y:%=vdso32-%.so) + +--- head-2010-04-15.orig/arch/x86/vdso/vdso32-setup.c 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/vdso/vdso32-setup.c 2010-03-24 15:01:37.000000000 +0100 +@@ -26,6 +26,10 @@ + #include + #include + ++#ifdef CONFIG_XEN ++#include ++#endif ++ + enum { + VDSO_DISABLED = 0, + VDSO_ENABLED = 1, +@@ -225,6 +229,7 @@ static inline void map_compat_vdso(int m + + void enable_sep_cpu(void) + { ++#ifndef CONFIG_XEN + int cpu = get_cpu(); + struct tss_struct *tss = &per_cpu(init_tss, cpu); + +@@ -239,6 +244,35 @@ void enable_sep_cpu(void) + wrmsr(MSR_IA32_SYSENTER_ESP, tss->x86_tss.sp1, 0); + wrmsr(MSR_IA32_SYSENTER_EIP, (unsigned long) ia32_sysenter_target, 0); + put_cpu(); ++#else ++ extern asmlinkage void ia32pv_sysenter_target(void); ++ static struct callback_register sysenter = { ++ .type = CALLBACKTYPE_sysenter, ++ .address = { __KERNEL_CS, (unsigned long)ia32pv_sysenter_target }, ++ }; ++ ++ if (!boot_cpu_has(X86_FEATURE_SEP)) ++ return; ++ ++ get_cpu(); ++ ++ if (xen_feature(XENFEAT_supervisor_mode_kernel)) ++ sysenter.address.eip = (unsigned long)ia32_sysenter_target; ++ ++ switch (HYPERVISOR_callback_op(CALLBACKOP_register, &sysenter)) { ++ case 0: ++ break; ++#if CONFIG_XEN_COMPAT < 0x030200 ++ case -ENOSYS: ++ sysenter.type = CALLBACKTYPE_sysenter_deprecated; ++ if (HYPERVISOR_callback_op(CALLBACKOP_register, &sysenter) == 0) ++ break; ++#endif ++ default: ++ clear_bit(X86_FEATURE_SEP, boot_cpu_data.x86_capability); ++ break; ++ } ++#endif + } + + static struct vm_area_struct gate_vma; diff --git a/patches.xen/xen3-auto-arch-x86_64.diff b/patches.xen/xen3-auto-arch-x86_64.diff new file mode 100644 index 0000000..0a3af92 --- /dev/null +++ b/patches.xen/xen3-auto-arch-x86_64.diff @@ -0,0 +1,211 @@ +Subject: xen3 arch-x86_64 +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 1017:948c933f8839) +Patch-mainline: n/a +Acked-by: jbeulich@novell.com + +--- head-2010-04-15.orig/arch/x86/kernel/asm-offsets_64.c 2010-04-15 09:37:46.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/asm-offsets_64.c 2010-01-19 16:00:48.000000000 +0100 +@@ -115,8 +115,10 @@ int main(void) + ENTRY(cr8); + BLANK(); + #undef ENTRY ++#ifndef CONFIG_X86_NO_TSS + DEFINE(TSS_ist, offsetof(struct tss_struct, x86_tss.ist)); + BLANK(); ++#endif + DEFINE(crypto_tfm_ctx_offset, offsetof(struct crypto_tfm, __crt_ctx)); + BLANK(); + DEFINE(__NR_syscall_max, sizeof(syscalls) - 1); +--- head-2010-04-15.orig/arch/x86/kernel/machine_kexec_64.c 2010-04-15 09:38:56.000000000 +0200 ++++ head-2010-04-15/arch/x86/kernel/machine_kexec_64.c 2010-04-15 09:44:51.000000000 +0200 +@@ -21,6 +21,119 @@ + #include + #include + ++#ifdef CONFIG_XEN ++ ++/* In the case of Xen, override hypervisor functions to be able to create ++ * a regular identity mapping page table... ++ */ ++ ++#include ++#include ++ ++#define x__pmd(x) ((pmd_t) { (x) } ) ++#define x__pud(x) ((pud_t) { (x) } ) ++#define x__pgd(x) ((pgd_t) { (x) } ) ++ ++#define x_pmd_val(x) ((x).pmd) ++#define x_pud_val(x) ((x).pud) ++#define x_pgd_val(x) ((x).pgd) ++ ++static inline void x_set_pmd(pmd_t *dst, pmd_t val) ++{ ++ x_pmd_val(*dst) = x_pmd_val(val); ++} ++ ++static inline void x_set_pud(pud_t *dst, pud_t val) ++{ ++ x_pud_val(*dst) = phys_to_machine(x_pud_val(val)); ++} ++ ++static inline void x_pud_clear (pud_t *pud) ++{ ++ x_pud_val(*pud) = 0; ++} ++ ++static inline void x_set_pgd(pgd_t *dst, pgd_t val) ++{ ++ x_pgd_val(*dst) = phys_to_machine(x_pgd_val(val)); ++} ++ ++static inline void x_pgd_clear (pgd_t * pgd) ++{ ++ x_pgd_val(*pgd) = 0; ++} ++ ++#define X__PAGE_KERNEL_LARGE_EXEC \ ++ _PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_PSE ++#define X_KERNPG_TABLE _PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY ++ ++#define __ma(x) (pfn_to_mfn(__pa((x)) >> PAGE_SHIFT) << PAGE_SHIFT) ++ ++#if PAGES_NR > KEXEC_XEN_NO_PAGES ++#error PAGES_NR is greater than KEXEC_XEN_NO_PAGES - Xen support will break ++#endif ++ ++#if PA_CONTROL_PAGE != 0 ++#error PA_CONTROL_PAGE is non zero - Xen support will break ++#endif ++ ++void machine_kexec_setup_load_arg(xen_kexec_image_t *xki, struct kimage *image) ++{ ++ void *control_page; ++ void *table_page; ++ ++ memset(xki->page_list, 0, sizeof(xki->page_list)); ++ ++ control_page = page_address(image->control_code_page) + PAGE_SIZE; ++ memcpy(control_page, relocate_kernel, PAGE_SIZE); ++ ++ table_page = page_address(image->control_code_page); ++ ++ xki->page_list[PA_CONTROL_PAGE] = __ma(control_page); ++ xki->page_list[PA_TABLE_PAGE] = __ma(table_page); ++ ++ xki->page_list[PA_PGD] = __ma(kexec_pgd); ++ xki->page_list[PA_PUD_0] = __ma(kexec_pud0); ++ xki->page_list[PA_PUD_1] = __ma(kexec_pud1); ++ xki->page_list[PA_PMD_0] = __ma(kexec_pmd0); ++ xki->page_list[PA_PMD_1] = __ma(kexec_pmd1); ++ xki->page_list[PA_PTE_0] = __ma(kexec_pte0); ++ xki->page_list[PA_PTE_1] = __ma(kexec_pte1); ++} ++ ++int __init machine_kexec_setup_resources(struct resource *hypervisor, ++ struct resource *phys_cpus, ++ int nr_phys_cpus) ++{ ++ int k; ++ ++ /* The per-cpu crash note resources belong to the hypervisor resource */ ++ for (k = 0; k < nr_phys_cpus; k++) ++ request_resource(hypervisor, phys_cpus + k); ++ ++ return 0; ++} ++ ++void machine_kexec_register_resources(struct resource *res) { ; } ++ ++#else /* CONFIG_XEN */ ++ ++#define x__pmd(x) __pmd(x) ++#define x__pud(x) __pud(x) ++#define x__pgd(x) __pgd(x) ++ ++#define x_set_pmd(x, y) set_pmd(x, y) ++#define x_set_pud(x, y) set_pud(x, y) ++#define x_set_pgd(x, y) set_pgd(x, y) ++ ++#define x_pud_clear(x) pud_clear(x) ++#define x_pgd_clear(x) pgd_clear(x) ++ ++#define X__PAGE_KERNEL_LARGE_EXEC __PAGE_KERNEL_LARGE_EXEC ++#define X_KERNPG_TABLE _KERNPG_TABLE ++ ++#endif /* CONFIG_XEN */ ++ + static int init_one_level2_page(struct kimage *image, pgd_t *pgd, + unsigned long addr) + { +@@ -63,7 +176,7 @@ static void init_level2_page(pmd_t *leve + addr &= PAGE_MASK; + end_addr = addr + PUD_SIZE; + while (addr < end_addr) { +- set_pmd(level2p++, __pmd(addr | __PAGE_KERNEL_LARGE_EXEC)); ++ x_set_pmd(level2p++, x__pmd(addr | X__PAGE_KERNEL_LARGE_EXEC)); + addr += PMD_SIZE; + } + } +@@ -88,12 +201,12 @@ static int init_level3_page(struct kimag + } + level2p = (pmd_t *)page_address(page); + init_level2_page(level2p, addr); +- set_pud(level3p++, __pud(__pa(level2p) | _KERNPG_TABLE)); ++ x_set_pud(level3p++, x__pud(__pa(level2p) | X_KERNPG_TABLE)); + addr += PUD_SIZE; + } + /* clear the unused entries */ + while (addr < end_addr) { +- pud_clear(level3p++); ++ x_pud_clear(level3p++); + addr += PUD_SIZE; + } + out: +@@ -123,12 +236,12 @@ static int init_level4_page(struct kimag + result = init_level3_page(image, level3p, addr, last_addr); + if (result) + goto out; +- set_pgd(level4p++, __pgd(__pa(level3p) | _KERNPG_TABLE)); ++ x_set_pgd(level4p++, x__pgd(__pa(level3p) | X_KERNPG_TABLE)); + addr += PGDIR_SIZE; + } + /* clear the unused entries */ + while (addr < end_addr) { +- pgd_clear(level4p++); ++ x_pgd_clear(level4p++); + addr += PGDIR_SIZE; + } + out: +@@ -189,8 +302,14 @@ static int init_pgtable(struct kimage *i + { + pgd_t *level4p; + int result; ++ unsigned long x_max_pfn = max_pfn; ++ ++#ifdef CONFIG_XEN ++ x_max_pfn = HYPERVISOR_memory_op(XENMEM_maximum_ram_page, NULL); ++#endif ++ + level4p = (pgd_t *)__va(start_pgtable); +- result = init_level4_page(image, level4p, 0, max_pfn << PAGE_SHIFT); ++ result = init_level4_page(image, level4p, 0, x_max_pfn << PAGE_SHIFT); + if (result) + return result; + /* +@@ -224,6 +343,7 @@ void machine_kexec_cleanup(struct kimage + free_transition_pgtable(image); + } + ++#ifndef CONFIG_XEN + /* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. +@@ -283,6 +403,7 @@ void machine_kexec(struct kimage *image) + + __ftrace_enabled_restore(save_ftrace_enabled); + } ++#endif + + void arch_crash_save_vmcoreinfo(void) + { diff --git a/patches.xen/xen3-auto-common.diff b/patches.xen/xen3-auto-common.diff new file mode 100644 index 0000000..5c616ef --- /dev/null +++ b/patches.xen/xen3-auto-common.diff @@ -0,0 +1,4165 @@ +Subject: xen3 common +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 1017:948c933f8839) +Patch-mainline: n/a +Acked-by: jbeulich@novell.com + +List of files that don't require modification anymore (and hence got +removed from this patch), for reference and in case upstream wants to +take the forward porting patches: +2.6.22/include/linux/sched.h +2.6.22/kernel/softlockup.c +2.6.22/kernel/timer.c +2.6.25/mm/highmem.c +2.6.30/include/linux/pci_regs.h + +--- head-2010-05-25.orig/drivers/Makefile 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/Makefile 2010-05-12 08:58:07.000000000 +0200 +@@ -34,6 +34,7 @@ obj-$(CONFIG_PARPORT) += parport/ + obj-y += base/ block/ misc/ mfd/ + obj-$(CONFIG_NUBUS) += nubus/ + obj-y += macintosh/ ++obj-$(CONFIG_XEN) += xen/ + obj-$(CONFIG_IDE) += ide/ + obj-$(CONFIG_SCSI) += scsi/ + obj-$(CONFIG_ATA) += ata/ +--- head-2010-05-25.orig/drivers/acpi/Makefile 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/acpi/Makefile 2010-03-24 14:53:41.000000000 +0100 +@@ -64,5 +64,8 @@ obj-$(CONFIG_ACPI_POWER_METER) += power_ + processor-y := processor_driver.o processor_throttling.o + processor-y += processor_idle.o processor_thermal.o + processor-$(CONFIG_CPU_FREQ) += processor_perflib.o ++ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL ++processor-objs += processor_perflib.o processor_extcntl.o ++endif + + obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o +--- head-2010-05-25.orig/drivers/acpi/acpica/hwsleep.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/acpi/acpica/hwsleep.c 2010-03-24 14:53:41.000000000 +0100 +@@ -236,7 +236,11 @@ acpi_status asmlinkage acpi_enter_sleep_ + u32 pm1b_control; + struct acpi_bit_register_info *sleep_type_reg_info; + struct acpi_bit_register_info *sleep_enable_reg_info; ++#if !(defined(CONFIG_XEN) && defined(CONFIG_X86)) + u32 in_value; ++#else ++ int err; ++#endif + struct acpi_object_list arg_list; + union acpi_object arg; + acpi_status status; +@@ -347,6 +351,7 @@ acpi_status asmlinkage acpi_enter_sleep_ + + /* Write #2: Write both SLP_TYP + SLP_EN */ + ++#if !(defined(CONFIG_XEN) && defined(CONFIG_X86)) + status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); +@@ -386,6 +391,16 @@ acpi_status asmlinkage acpi_enter_sleep_ + /* Spin until we wake */ + + } while (!in_value); ++#else ++ /* PV ACPI just need check hypercall return value */ ++ err = acpi_notify_hypervisor_state(sleep_state, ++ PM1Acontrol, PM1Bcontrol); ++ if (err) { ++ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ++ "Hypervisor failure [%d]\n", err)); ++ return_ACPI_STATUS(AE_ERROR); ++ } ++#endif + + return_ACPI_STATUS(AE_OK); + } +--- head-2010-05-25.orig/drivers/acpi/processor_driver.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/acpi/processor_driver.c 2010-05-25 09:19:10.000000000 +0200 +@@ -443,7 +443,8 @@ static int acpi_processor_get_info(struc + */ + if (pr->id == -1) { + if (ACPI_FAILURE +- (acpi_processor_hotadd_init(pr->handle, &pr->id))) { ++ (acpi_processor_hotadd_init(pr->handle, &pr->id)) && ++ !processor_cntl_external()) { + return -ENODEV; + } + } +@@ -494,7 +495,11 @@ static int acpi_processor_get_info(struc + return 0; + } + ++#ifndef CONFIG_XEN + static DEFINE_PER_CPU(void *, processor_device_array); ++#else ++static void *processor_device_array[NR_ACPI_CPUS]; ++#endif + + static void acpi_processor_notify(struct acpi_device *device, u32 event) + { +@@ -575,8 +580,11 @@ static int __cpuinit acpi_processor_add( + strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); + device->driver_data = pr; + ++ processor_extcntl_init(); ++ + result = acpi_processor_get_info(device); +- if (result) { ++ if (result || ++ ((pr->id == -1) && !processor_cntl_external())) { + /* Processor is physically not present */ + return 0; + } +@@ -586,23 +594,36 @@ static int __cpuinit acpi_processor_add( + return 0; + #endif + +- BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0)); ++ BUG_ON(!processor_cntl_external() && ++ ((pr->id >= nr_cpu_ids) || (pr->id < 0))); + + /* + * Buggy BIOS check + * ACPI id of processors can be reported wrongly by the BIOS. + * Don't trust it blindly + */ ++#ifndef CONFIG_XEN + if (per_cpu(processor_device_array, pr->id) != NULL && + per_cpu(processor_device_array, pr->id) != device) { ++#else ++ BUG_ON(pr->acpi_id >= NR_ACPI_CPUS); ++ if (processor_device_array[pr->acpi_id] != NULL && ++ processor_device_array[pr->acpi_id] != device) { ++#endif + printk(KERN_WARNING "BIOS reported wrong ACPI id " + "for the processor\n"); + result = -ENODEV; + goto err_free_cpumask; + } ++#ifndef CONFIG_XEN + per_cpu(processor_device_array, pr->id) = device; + + per_cpu(processors, pr->id) = pr; ++#else ++ processor_device_array[pr->acpi_id] = device; ++ if (pr->id != -1) ++ per_cpu(processors, pr->id) = pr; ++#endif + + result = acpi_processor_add_fs(device); + if (result) +@@ -614,15 +635,27 @@ static int __cpuinit acpi_processor_add( + goto err_remove_fs; + } + +-#ifdef CONFIG_CPU_FREQ ++#if defined(CONFIG_CPU_FREQ) || defined(CONFIG_PROCESSOR_EXTERNAL_CONTROL) + acpi_processor_ppc_has_changed(pr, 0); + #endif +- acpi_processor_get_throttling_info(pr); +- acpi_processor_get_limit_info(pr); + ++ /* ++ * pr->id may equal to -1 while processor_cntl_external enabled. ++ * throttle and thermal module don't support this case. ++ * Tx only works when dom0 vcpu == pcpu num by far, as we give ++ * control to dom0. ++ */ ++ if (pr->id != -1) { ++ acpi_processor_get_throttling_info(pr); ++ acpi_processor_get_limit_info(pr); ++ } + + acpi_processor_power_init(pr, device); + ++ result = processor_extcntl_prepare(pr); ++ if (result) ++ goto end; ++ + pr->cdev = thermal_cooling_device_register("Processor", device, + &processor_cooling_ops); + if (IS_ERR(pr->cdev)) { +@@ -674,7 +707,7 @@ static int acpi_processor_remove(struct + + pr = acpi_driver_data(device); + +- if (pr->id >= nr_cpu_ids) ++ if (!processor_cntl_external() && pr->id >= nr_cpu_ids) + goto free; + + if (type == ACPI_BUS_REMOVAL_EJECT) { +@@ -695,8 +728,14 @@ static int acpi_processor_remove(struct + pr->cdev = NULL; + } + ++#ifndef CONFIG_XEN + per_cpu(processors, pr->id) = NULL; + per_cpu(processor_device_array, pr->id) = NULL; ++#else ++ if (pr->id != -1) ++ per_cpu(processors, pr->id) = NULL; ++ processor_device_array[pr->acpi_id] = NULL; ++#endif + + free: + free_cpumask_var(pr->throttling.shared_cpu_map); +@@ -752,6 +791,10 @@ int acpi_processor_device_add(acpi_handl + return -ENODEV; + } + ++ if (processor_cntl_external() && acpi_driver_data(*device)) ++ processor_notify_external(acpi_driver_data(*device), ++ PROCESSOR_HOTPLUG, HOTPLUG_TYPE_ADD); ++ + return 0; + } + +@@ -781,6 +824,10 @@ static void __ref acpi_processor_hotplug + "Unable to add the device\n"); + break; + } ++ pr = acpi_driver_data(device); ++ if (processor_cntl_external() && pr) ++ processor_notify_external(pr, ++ PROCESSOR_HOTPLUG, HOTPLUG_TYPE_ADD); + break; + case ACPI_NOTIFY_EJECT_REQUEST: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, +@@ -797,6 +844,9 @@ static void __ref acpi_processor_hotplug + "Driver data is NULL, dropping EJECT\n"); + return; + } ++ if (processor_cntl_external()) ++ processor_notify_external(pr, PROCESSOR_HOTPLUG, ++ HOTPLUG_TYPE_REMOVE); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, +@@ -861,6 +911,11 @@ static acpi_status acpi_processor_hotadd + + static int acpi_processor_handle_eject(struct acpi_processor *pr) + { ++#ifdef CONFIG_XEN ++ if (pr->id == -1) ++ return (0); ++#endif ++ + if (cpu_online(pr->id)) + cpu_down(pr->id); + +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-25/drivers/acpi/processor_extcntl.c 2010-03-24 14:53:41.000000000 +0100 +@@ -0,0 +1,241 @@ ++/* ++ * processor_extcntl.c - channel to external control logic ++ * ++ * Copyright (C) 2008, Intel corporation ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define ACPI_PROCESSOR_COMPONENT 0x01000000 ++#define ACPI_PROCESSOR_CLASS "processor" ++#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" ++#define _COMPONENT ACPI_PROCESSOR_COMPONENT ++ACPI_MODULE_NAME("acpi_processor") ++ ++static int processor_extcntl_parse_csd(struct acpi_processor *pr); ++static int processor_extcntl_get_performance(struct acpi_processor *pr); ++/* ++ * External processor control logic may register with its own set of ++ * ops to get ACPI related notification. One example is like VMM. ++ */ ++const struct processor_extcntl_ops *processor_extcntl_ops; ++EXPORT_SYMBOL(processor_extcntl_ops); ++ ++static int processor_notify_smm(void) ++{ ++ acpi_status status; ++ static int is_done = 0; ++ ++ /* only need successfully notify BIOS once */ ++ /* avoid double notification which may lead to unexpected result */ ++ if (is_done) ++ return 0; ++ ++ /* Can't write pstate_cnt to smi_cmd if either value is zero */ ++ if ((!acpi_fadt.smi_cmd) || (!acpi_fadt.pstate_cnt)) { ++ ACPI_DEBUG_PRINT((ACPI_DB_INFO,"No SMI port or pstate_cnt\n")); ++ return 0; ++ } ++ ++ ACPI_DEBUG_PRINT((ACPI_DB_INFO, ++ "Writing pstate_cnt [0x%x] to smi_cmd [0x%x]\n", ++ acpi_fadt.pstate_cnt, acpi_fadt.smi_cmd)); ++ ++ /* FADT v1 doesn't support pstate_cnt, many BIOS vendors use ++ * it anyway, so we need to support it... */ ++ if (acpi_fadt_is_v1) { ++ ACPI_DEBUG_PRINT((ACPI_DB_INFO, ++ "Using v1.0 FADT reserved value for pstate_cnt\n")); ++ } ++ ++ status = acpi_os_write_port(acpi_fadt.smi_cmd, ++ (u32) acpi_fadt.pstate_cnt, 8); ++ if (ACPI_FAILURE(status)) ++ return status; ++ ++ is_done = 1; ++ ++ return 0; ++} ++ ++int processor_notify_external(struct acpi_processor *pr, int event, int type) ++{ ++ int ret = -EINVAL; ++ ++ if (!processor_cntl_external()) ++ return -EINVAL; ++ ++ switch (event) { ++ case PROCESSOR_PM_INIT: ++ case PROCESSOR_PM_CHANGE: ++ if ((type >= PM_TYPE_MAX) || ++ !processor_extcntl_ops->pm_ops[type]) ++ break; ++ ++ ret = processor_extcntl_ops->pm_ops[type](pr, event); ++ break; ++ case PROCESSOR_HOTPLUG: ++ if (processor_extcntl_ops->hotplug) ++ ret = processor_extcntl_ops->hotplug(pr, type); ++ break; ++ default: ++ printk(KERN_ERR "Unsupport processor events %d.\n", event); ++ break; ++ } ++ ++ return ret; ++} ++ ++/* ++ * External control logic can decide to grab full or part of physical ++ * processor control bits. Take a VMM for example, physical processors ++ * are owned by VMM and thus existence information like hotplug is ++ * always required to be notified to VMM. Similar is processor idle ++ * state which is also necessarily controlled by VMM. But for other ++ * control bits like performance/throttle states, VMM may choose to ++ * control or not upon its own policy. ++ */ ++void processor_extcntl_init(void) ++{ ++ if (!processor_extcntl_ops) ++ arch_acpi_processor_init_extcntl(&processor_extcntl_ops); ++} ++ ++/* ++ * This is called from ACPI processor init, and targeted to hold ++ * some tricky housekeeping jobs to satisfy external control model. ++ * For example, we may put dependency parse stub here for idle ++ * and performance state. Those information may be not available ++ * if splitting from dom0 control logic like cpufreq driver. ++ */ ++int processor_extcntl_prepare(struct acpi_processor *pr) ++{ ++ /* parse cstate dependency information */ ++ if (processor_pm_external()) ++ processor_extcntl_parse_csd(pr); ++ ++ /* Initialize performance states */ ++ if (processor_pmperf_external()) ++ processor_extcntl_get_performance(pr); ++ ++ return 0; ++} ++ ++/* ++ * Currently no _CSD is implemented which is why existing ACPI code ++ * doesn't parse _CSD at all. But to keep interface complete with ++ * external control logic, we put a placeholder here for future ++ * compatibility. ++ */ ++static int processor_extcntl_parse_csd(struct acpi_processor *pr) ++{ ++ int i; ++ ++ for (i = 0; i < pr->power.count; i++) { ++ if (!pr->power.states[i].valid) ++ continue; ++ ++ /* No dependency by default */ ++ pr->power.states[i].domain_info = NULL; ++ pr->power.states[i].csd_count = 0; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Existing ACPI module does parse performance states at some point, ++ * when acpi-cpufreq driver is loaded which however is something ++ * we'd like to disable to avoid confliction with external control ++ * logic. So we have to collect raw performance information here ++ * when ACPI processor object is found and started. ++ */ ++static int processor_extcntl_get_performance(struct acpi_processor *pr) ++{ ++ int ret; ++ struct acpi_processor_performance *perf; ++ struct acpi_psd_package *pdomain; ++ ++ if (pr->performance) ++ return -EBUSY; ++ ++ perf = kzalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL); ++ if (!perf) ++ return -ENOMEM; ++ ++ pr->performance = perf; ++ /* Get basic performance state information */ ++ ret = acpi_processor_get_performance_info(pr); ++ if (ret < 0) ++ goto err_out; ++ ++ /* ++ * Well, here we need retrieve performance dependency information ++ * from _PSD object. The reason why existing interface is not used ++ * is due to the reason that existing interface sticks to Linux cpu ++ * id to construct some bitmap, however we want to split ACPI ++ * processor objects from Linux cpu id logic. For example, even ++ * when Linux is configured as UP, we still want to parse all ACPI ++ * processor objects to external logic. In this case, it's preferred ++ * to use ACPI ID instead. ++ */ ++ pdomain = &pr->performance->domain_info; ++ pdomain->num_processors = 0; ++ ret = acpi_processor_get_psd(pr); ++ if (ret < 0) { ++ /* ++ * _PSD is optional - assume no coordination if absent (or ++ * broken), matching native kernels' behavior. ++ */ ++ pdomain->num_entries = ACPI_PSD_REV0_ENTRIES; ++ pdomain->revision = ACPI_PSD_REV0_REVISION; ++ pdomain->domain = pr->acpi_id; ++ pdomain->coord_type = DOMAIN_COORD_TYPE_SW_ALL; ++ pdomain->num_processors = 1; ++ } ++ ++ /* Some sanity check */ ++ if ((pdomain->revision != ACPI_PSD_REV0_REVISION) || ++ (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) || ++ ((pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL) && ++ (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY) && ++ (pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL))) { ++ ret = -EINVAL; ++ goto err_out; ++ } ++ ++ /* Last step is to notify BIOS that external logic exists */ ++ processor_notify_smm(); ++ ++ processor_notify_external(pr, PROCESSOR_PM_INIT, PM_TYPE_PERF); ++ ++ return 0; ++err_out: ++ pr->performance = NULL; ++ kfree(perf); ++ return ret; ++} +--- head-2010-05-25.orig/drivers/acpi/processor_idle.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/acpi/processor_idle.c 2010-04-15 09:43:01.000000000 +0200 +@@ -456,7 +456,8 @@ static int acpi_processor_get_power_info + */ + cx.entry_method = ACPI_CSTATE_HALT; + snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT"); +- } else { ++ /* This doesn't apply to external control case */ ++ } else if (!processor_pm_external()) { + continue; + } + if (cx.type == ACPI_STATE_C1 && +@@ -495,6 +496,12 @@ static int acpi_processor_get_power_info + + cx.power = obj->integer.value; + ++#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL ++ /* cache control methods to notify external logic */ ++ if (processor_pm_external()) ++ memcpy(&cx.reg, reg, sizeof(*reg)); ++#endif ++ + current_count++; + memcpy(&(pr->power.states[current_count]), &cx, sizeof(cx)); + +@@ -1229,6 +1236,11 @@ int __cpuinit acpi_processor_power_init( + if (!entry) + return -EIO; + #endif ++ ++ if (processor_pm_external()) ++ processor_notify_external(pr, ++ PROCESSOR_PM_INIT, PM_TYPE_IDLE); ++ + return 0; + } + +--- head-2010-05-25.orig/drivers/acpi/processor_perflib.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/acpi/processor_perflib.c 2010-04-15 09:43:05.000000000 +0200 +@@ -79,6 +79,7 @@ MODULE_PARM_DESC(ignore_ppc, "If the fre + + static int acpi_processor_ppc_status; + ++#ifdef CONFIG_CPU_FREQ + static int acpi_processor_ppc_notifier(struct notifier_block *nb, + unsigned long event, void *data) + { +@@ -121,6 +122,7 @@ static int acpi_processor_ppc_notifier(s + static struct notifier_block acpi_ppc_notifier_block = { + .notifier_call = acpi_processor_ppc_notifier, + }; ++#endif /* CONFIG_CPU_FREQ */ + + static int acpi_processor_get_platform_limit(struct acpi_processor *pr) + { +@@ -209,7 +211,12 @@ int acpi_processor_ppc_has_changed(struc + if (ret < 0) + return (ret); + else ++#ifdef CONFIG_CPU_FREQ + return cpufreq_update_policy(pr->id); ++#elif defined(CONFIG_PROCESSOR_EXTERNAL_CONTROL) ++ return processor_notify_external(pr, ++ PROCESSOR_PM_CHANGE, PM_TYPE_PERF); ++#endif + } + + int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) +@@ -225,6 +232,7 @@ int acpi_processor_get_bios_limit(int cp + } + EXPORT_SYMBOL(acpi_processor_get_bios_limit); + ++#ifdef CONFIG_CPU_FREQ + void acpi_processor_ppc_init(void) + { + if (!cpufreq_register_notifier +@@ -243,6 +251,7 @@ void acpi_processor_ppc_exit(void) + + acpi_processor_ppc_status &= ~PPC_REGISTERED; + } ++#endif /* CONFIG_CPU_FREQ */ + + static int acpi_processor_get_performance_control(struct acpi_processor *pr) + { +@@ -390,7 +399,10 @@ static int acpi_processor_get_performanc + return result; + } + +-static int acpi_processor_get_performance_info(struct acpi_processor *pr) ++#ifndef CONFIG_PROCESSOR_EXTERNAL_CONTROL ++static ++#endif ++int acpi_processor_get_performance_info(struct acpi_processor *pr) + { + int result = 0; + acpi_status status = AE_OK; +@@ -435,6 +447,7 @@ static int acpi_processor_get_performanc + return result; + } + ++#ifdef CONFIG_CPU_FREQ + int acpi_processor_notify_smm(struct module *calling_module) + { + acpi_status status; +@@ -495,8 +508,12 @@ int acpi_processor_notify_smm(struct mod + } + + EXPORT_SYMBOL(acpi_processor_notify_smm); ++#endif /* CONFIG_CPU_FREQ */ + +-static int acpi_processor_get_psd(struct acpi_processor *pr) ++#ifndef CONFIG_PROCESSOR_EXTERNAL_CONTROL ++static ++#endif ++int acpi_processor_get_psd(struct acpi_processor *pr) + { + int result = 0; + acpi_status status = AE_OK; +--- head-2010-05-25.orig/drivers/acpi/sleep.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/acpi/sleep.c 2010-03-24 14:53:41.000000000 +0100 +@@ -60,6 +60,7 @@ static struct notifier_block tts_notifie + static int acpi_sleep_prepare(u32 acpi_state) + { + #ifdef CONFIG_ACPI_SLEEP ++#ifndef CONFIG_ACPI_PV_SLEEP + /* do we have a wakeup address for S2 and S3? */ + if (acpi_state == ACPI_STATE_S3) { + if (!acpi_wakeup_address) { +@@ -69,6 +70,7 @@ static int acpi_sleep_prepare(u32 acpi_s + (acpi_physical_address)acpi_wakeup_address); + + } ++#endif + ACPI_FLUSH_CPU_CACHE(); + acpi_enable_wakeup_device_prep(acpi_state); + #endif +@@ -249,7 +251,14 @@ static int acpi_suspend_enter(suspend_st + break; + + case ACPI_STATE_S3: ++#ifdef CONFIG_ACPI_PV_SLEEP ++ /* Hyperviosr will save and restore CPU context ++ * and then we can skip low level housekeeping here. ++ */ ++ acpi_enter_sleep_state(acpi_state); ++#else + do_suspend_lowlevel(); ++#endif + break; + } + +--- head-2010-05-25.orig/drivers/char/agp/intel-agp.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/char/agp/intel-agp.c 2010-04-15 09:43:13.000000000 +0200 +@@ -443,6 +443,13 @@ static struct page *i8xx_alloc_pages(voi + if (page == NULL) + return NULL; + ++#ifdef CONFIG_XEN ++ if (xen_create_contiguous_region((unsigned long)page_address(page), 2, 32)) { ++ __free_pages(page, 2); ++ return NULL; ++ } ++#endif ++ + if (set_pages_uc(page, 4) < 0) { + set_pages_wb(page, 4); + __free_pages(page, 2); +@@ -459,6 +466,9 @@ static void i8xx_destroy_pages(struct pa + return; + + set_pages_wb(page, 4); ++#ifdef CONFIG_XEN ++ xen_destroy_contiguous_region((unsigned long)page_address(page), 2); ++#endif + put_page(page); + __free_pages(page, 2); + atomic_dec(&agp_bridge->current_memory_agp); +--- head-2010-05-25.orig/drivers/char/mem.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/char/mem.c 2010-04-15 09:43:24.000000000 +0200 +@@ -89,6 +89,7 @@ void __weak unxlate_dev_mem_ptr(unsigned + { + } + ++#ifndef ARCH_HAS_DEV_MEM + /* + * This funcion reads the *physical* memory. The f_pos points directly to the + * memory location. +@@ -211,6 +212,7 @@ static ssize_t write_mem(struct file *fi + *ppos += written; + return written; + } ++#endif + + int __weak phys_mem_access_prot_allowed(struct file *file, + unsigned long pfn, unsigned long size, pgprot_t *vma_prot) +@@ -337,6 +339,9 @@ static int mmap_mem(struct file *file, s + static int mmap_kmem(struct file *file, struct vm_area_struct *vma) + { + unsigned long pfn; ++#ifdef CONFIG_XEN ++ unsigned long i, count; ++#endif + + /* Turn a kernel-virtual address into a physical page frame */ + pfn = __pa((u64)vma->vm_pgoff << PAGE_SHIFT) >> PAGE_SHIFT; +@@ -351,6 +356,13 @@ static int mmap_kmem(struct file *file, + if (!pfn_valid(pfn)) + return -EIO; + ++#ifdef CONFIG_XEN ++ count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; ++ for (i = 0; i < count; i++) ++ if ((pfn + i) != mfn_to_local_pfn(pfn_to_mfn(pfn + i))) ++ return -EIO; ++#endif ++ + vma->vm_pgoff = pfn; + return mmap_mem(file, vma); + } +@@ -845,6 +857,7 @@ static int open_port(struct inode * inod + #define open_kmem open_mem + #define open_oldmem open_mem + ++#ifndef ARCH_HAS_DEV_MEM + static const struct file_operations mem_fops = { + .llseek = memory_lseek, + .read = read_mem, +@@ -853,6 +866,9 @@ static const struct file_operations mem_ + .open = open_mem, + .get_unmapped_area = get_unmapped_area_mem, + }; ++#else ++extern const struct file_operations mem_fops; ++#endif + + #ifdef CONFIG_DEVKMEM + static const struct file_operations kmem_fops = { +--- head-2010-05-25.orig/drivers/char/tpm/Makefile 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/char/tpm/Makefile 2010-03-24 14:53:41.000000000 +0100 +@@ -9,3 +9,5 @@ obj-$(CONFIG_TCG_TIS) += tpm_tis.o + obj-$(CONFIG_TCG_NSC) += tpm_nsc.o + obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o + obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o ++obj-$(CONFIG_TCG_XEN) += tpm_xenu.o ++tpm_xenu-y = tpm_xen.o tpm_vtpm.o +--- head-2010-05-25.orig/drivers/char/tpm/tpm.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/char/tpm/tpm.h 2010-03-24 14:53:41.000000000 +0100 +@@ -108,6 +108,9 @@ struct tpm_chip { + struct dentry **bios_dir; + + struct list_head list; ++#ifdef CONFIG_XEN ++ void *priv; ++#endif + void (*release) (struct device *); + }; + +@@ -266,6 +269,18 @@ struct tpm_cmd_t { + + ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *); + ++#ifdef CONFIG_XEN ++static inline void *chip_get_private(const struct tpm_chip *chip) ++{ ++ return chip->priv; ++} ++ ++static inline void chip_set_private(struct tpm_chip *chip, void *priv) ++{ ++ chip->priv = priv; ++} ++#endif ++ + extern void tpm_get_timeouts(struct tpm_chip *); + extern void tpm_gen_interrupt(struct tpm_chip *); + extern void tpm_continue_selftest(struct tpm_chip *); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-25/drivers/char/tpm/tpm_vtpm.c 2010-03-24 14:53:41.000000000 +0100 +@@ -0,0 +1,542 @@ ++/* ++ * Copyright (C) 2006 IBM Corporation ++ * ++ * Authors: ++ * Stefan Berger ++ * ++ * Generic device driver part for device drivers in a virtualized ++ * environment. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "tpm.h" ++#include "tpm_vtpm.h" ++ ++/* read status bits */ ++enum { ++ STATUS_BUSY = 0x01, ++ STATUS_DATA_AVAIL = 0x02, ++ STATUS_READY = 0x04 ++}; ++ ++struct transmission { ++ struct list_head next; ++ ++ unsigned char *request; ++ size_t request_len; ++ size_t request_buflen; ++ ++ unsigned char *response; ++ size_t response_len; ++ size_t response_buflen; ++ ++ unsigned int flags; ++}; ++ ++enum { ++ TRANSMISSION_FLAG_WAS_QUEUED = 0x1 ++}; ++ ++ ++enum { ++ DATAEX_FLAG_QUEUED_ONLY = 0x1 ++}; ++ ++ ++/* local variables */ ++ ++/* local function prototypes */ ++static int _vtpm_send_queued(struct tpm_chip *chip); ++ ++ ++/* ============================================================= ++ * Some utility functions ++ * ============================================================= ++ */ ++static void vtpm_state_init(struct vtpm_state *vtpms) ++{ ++ vtpms->current_request = NULL; ++ spin_lock_init(&vtpms->req_list_lock); ++ init_waitqueue_head(&vtpms->req_wait_queue); ++ INIT_LIST_HEAD(&vtpms->queued_requests); ++ ++ vtpms->current_response = NULL; ++ spin_lock_init(&vtpms->resp_list_lock); ++ init_waitqueue_head(&vtpms->resp_wait_queue); ++ ++ vtpms->disconnect_time = jiffies; ++} ++ ++ ++static inline struct transmission *transmission_alloc(void) ++{ ++ return kzalloc(sizeof(struct transmission), GFP_ATOMIC); ++} ++ ++static unsigned char * ++transmission_set_req_buffer(struct transmission *t, ++ unsigned char *buffer, size_t len) ++{ ++ if (t->request_buflen < len) { ++ kfree(t->request); ++ t->request = kmalloc(len, GFP_KERNEL); ++ if (!t->request) { ++ t->request_buflen = 0; ++ return NULL; ++ } ++ t->request_buflen = len; ++ } ++ ++ memcpy(t->request, buffer, len); ++ t->request_len = len; ++ ++ return t->request; ++} ++ ++static unsigned char * ++transmission_set_res_buffer(struct transmission *t, ++ const unsigned char *buffer, size_t len) ++{ ++ if (t->response_buflen < len) { ++ kfree(t->response); ++ t->response = kmalloc(len, GFP_ATOMIC); ++ if (!t->response) { ++ t->response_buflen = 0; ++ return NULL; ++ } ++ t->response_buflen = len; ++ } ++ ++ memcpy(t->response, buffer, len); ++ t->response_len = len; ++ ++ return t->response; ++} ++ ++static inline void transmission_free(struct transmission *t) ++{ ++ kfree(t->request); ++ kfree(t->response); ++ kfree(t); ++} ++ ++/* ============================================================= ++ * Interface with the lower layer driver ++ * ============================================================= ++ */ ++/* ++ * Lower layer uses this function to make a response available. ++ */ ++int vtpm_vd_recv(const struct tpm_chip *chip, ++ const unsigned char *buffer, size_t count, ++ void *ptr) ++{ ++ unsigned long flags; ++ int ret_size = 0; ++ struct transmission *t; ++ struct vtpm_state *vtpms; ++ ++ vtpms = (struct vtpm_state *)chip_get_private(chip); ++ ++ /* ++ * The list with requests must contain one request ++ * only and the element there must be the one that ++ * was passed to me from the front-end. ++ */ ++ spin_lock_irqsave(&vtpms->resp_list_lock, flags); ++ if (vtpms->current_request != ptr) { ++ spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); ++ return 0; ++ } ++ ++ if ((t = vtpms->current_request)) { ++ transmission_free(t); ++ vtpms->current_request = NULL; ++ } ++ ++ t = transmission_alloc(); ++ if (t) { ++ if (!transmission_set_res_buffer(t, buffer, count)) { ++ transmission_free(t); ++ spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); ++ return -ENOMEM; ++ } ++ ret_size = count; ++ vtpms->current_response = t; ++ wake_up_interruptible(&vtpms->resp_wait_queue); ++ } ++ spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); ++ ++ return ret_size; ++} ++ ++ ++/* ++ * Lower layer indicates its status (connected/disconnected) ++ */ ++void vtpm_vd_status(const struct tpm_chip *chip, u8 vd_status) ++{ ++ struct vtpm_state *vtpms; ++ ++ vtpms = (struct vtpm_state *)chip_get_private(chip); ++ ++ vtpms->vd_status = vd_status; ++ if ((vtpms->vd_status & TPM_VD_STATUS_CONNECTED) == 0) { ++ vtpms->disconnect_time = jiffies; ++ } ++} ++ ++/* ============================================================= ++ * Interface with the generic TPM driver ++ * ============================================================= ++ */ ++static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) ++{ ++ int rc = 0; ++ unsigned long flags; ++ struct vtpm_state *vtpms; ++ ++ vtpms = (struct vtpm_state *)chip_get_private(chip); ++ ++ /* ++ * Check if the previous operation only queued the command ++ * In this case there won't be a response, so I just ++ * return from here and reset that flag. In any other ++ * case I should receive a response from the back-end. ++ */ ++ spin_lock_irqsave(&vtpms->resp_list_lock, flags); ++ if ((vtpms->flags & DATAEX_FLAG_QUEUED_ONLY) != 0) { ++ vtpms->flags &= ~DATAEX_FLAG_QUEUED_ONLY; ++ spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); ++ /* ++ * The first few commands (measurements) must be ++ * queued since it might not be possible to talk to the ++ * TPM, yet. ++ * Return a response of up to 30 '0's. ++ */ ++ ++ count = min_t(size_t, count, 30); ++ memset(buf, 0x0, count); ++ return count; ++ } ++ /* ++ * Check whether something is in the responselist and if ++ * there's nothing in the list wait for something to appear. ++ */ ++ ++ if (!vtpms->current_response) { ++ spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); ++ interruptible_sleep_on_timeout(&vtpms->resp_wait_queue, ++ 1000); ++ spin_lock_irqsave(&vtpms->resp_list_lock ,flags); ++ } ++ ++ if (vtpms->current_response) { ++ struct transmission *t = vtpms->current_response; ++ vtpms->current_response = NULL; ++ rc = min(count, t->response_len); ++ memcpy(buf, t->response, rc); ++ transmission_free(t); ++ } ++ ++ spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); ++ return rc; ++} ++ ++static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) ++{ ++ int rc = 0; ++ unsigned long flags; ++ struct transmission *t = transmission_alloc(); ++ struct vtpm_state *vtpms; ++ ++ vtpms = (struct vtpm_state *)chip_get_private(chip); ++ ++ if (!t) ++ return -ENOMEM; ++ /* ++ * If there's a current request, it must be the ++ * previous request that has timed out. ++ */ ++ spin_lock_irqsave(&vtpms->req_list_lock, flags); ++ if (vtpms->current_request != NULL) { ++ printk("WARNING: Sending although there is a request outstanding.\n" ++ " Previous request must have timed out.\n"); ++ transmission_free(vtpms->current_request); ++ vtpms->current_request = NULL; ++ } ++ spin_unlock_irqrestore(&vtpms->req_list_lock, flags); ++ ++ /* ++ * Queue the packet if the driver below is not ++ * ready, yet, or there is any packet already ++ * in the queue. ++ * If the driver below is ready, unqueue all ++ * packets first before sending our current ++ * packet. ++ * For each unqueued packet, except for the ++ * last (=current) packet, call the function ++ * tpm_xen_recv to wait for the response to come ++ * back. ++ */ ++ if ((vtpms->vd_status & TPM_VD_STATUS_CONNECTED) == 0) { ++ if (time_after(jiffies, ++ vtpms->disconnect_time + HZ * 10)) { ++ rc = -ENOENT; ++ } else { ++ goto queue_it; ++ } ++ } else { ++ /* ++ * Send all queued packets. ++ */ ++ if (_vtpm_send_queued(chip) == 0) { ++ ++ vtpms->current_request = t; ++ ++ rc = vtpm_vd_send(vtpms->tpm_private, ++ buf, ++ count, ++ t); ++ /* ++ * The generic TPM driver will call ++ * the function to receive the response. ++ */ ++ if (rc < 0) { ++ vtpms->current_request = NULL; ++ goto queue_it; ++ } ++ } else { ++queue_it: ++ if (!transmission_set_req_buffer(t, buf, count)) { ++ transmission_free(t); ++ rc = -ENOMEM; ++ goto exit; ++ } ++ /* ++ * An error occurred. Don't event try ++ * to send the current request. Just ++ * queue it. ++ */ ++ spin_lock_irqsave(&vtpms->req_list_lock, flags); ++ vtpms->flags |= DATAEX_FLAG_QUEUED_ONLY; ++ list_add_tail(&t->next, &vtpms->queued_requests); ++ spin_unlock_irqrestore(&vtpms->req_list_lock, flags); ++ } ++ } ++ ++exit: ++ return rc; ++} ++ ++ ++/* ++ * Send all queued requests. ++ */ ++static int _vtpm_send_queued(struct tpm_chip *chip) ++{ ++ int rc; ++ int error = 0; ++ long flags; ++ unsigned char buffer[1]; ++ struct vtpm_state *vtpms; ++ vtpms = (struct vtpm_state *)chip_get_private(chip); ++ ++ spin_lock_irqsave(&vtpms->req_list_lock, flags); ++ ++ while (!list_empty(&vtpms->queued_requests)) { ++ /* ++ * Need to dequeue them. ++ * Read the result into a dummy buffer. ++ */ ++ struct transmission *qt = (struct transmission *) ++ vtpms->queued_requests.next; ++ list_del(&qt->next); ++ vtpms->current_request = qt; ++ spin_unlock_irqrestore(&vtpms->req_list_lock, flags); ++ ++ rc = vtpm_vd_send(vtpms->tpm_private, ++ qt->request, ++ qt->request_len, ++ qt); ++ ++ if (rc < 0) { ++ spin_lock_irqsave(&vtpms->req_list_lock, flags); ++ if ((qt = vtpms->current_request) != NULL) { ++ /* ++ * requeue it at the beginning ++ * of the list ++ */ ++ list_add(&qt->next, ++ &vtpms->queued_requests); ++ } ++ vtpms->current_request = NULL; ++ error = 1; ++ break; ++ } ++ /* ++ * After this point qt is not valid anymore! ++ * It is freed when the front-end is delivering ++ * the data by calling tpm_recv ++ */ ++ /* ++ * Receive response into provided dummy buffer ++ */ ++ rc = vtpm_recv(chip, buffer, sizeof(buffer)); ++ spin_lock_irqsave(&vtpms->req_list_lock, flags); ++ } ++ ++ spin_unlock_irqrestore(&vtpms->req_list_lock, flags); ++ ++ return error; ++} ++ ++static void vtpm_cancel(struct tpm_chip *chip) ++{ ++ unsigned long flags; ++ struct vtpm_state *vtpms = (struct vtpm_state *)chip_get_private(chip); ++ ++ spin_lock_irqsave(&vtpms->resp_list_lock,flags); ++ ++ if (!vtpms->current_response && vtpms->current_request) { ++ spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); ++ interruptible_sleep_on(&vtpms->resp_wait_queue); ++ spin_lock_irqsave(&vtpms->resp_list_lock,flags); ++ } ++ ++ if (vtpms->current_response) { ++ struct transmission *t = vtpms->current_response; ++ vtpms->current_response = NULL; ++ transmission_free(t); ++ } ++ ++ spin_unlock_irqrestore(&vtpms->resp_list_lock,flags); ++} ++ ++static u8 vtpm_status(struct tpm_chip *chip) ++{ ++ u8 rc = 0; ++ unsigned long flags; ++ struct vtpm_state *vtpms; ++ ++ vtpms = (struct vtpm_state *)chip_get_private(chip); ++ ++ spin_lock_irqsave(&vtpms->resp_list_lock, flags); ++ /* ++ * Data are available if: ++ * - there's a current response ++ * - the last packet was queued only (this is fake, but necessary to ++ * get the generic TPM layer to call the receive function.) ++ */ ++ if (vtpms->current_response || ++ 0 != (vtpms->flags & DATAEX_FLAG_QUEUED_ONLY)) { ++ rc = STATUS_DATA_AVAIL; ++ } else if (!vtpms->current_response && !vtpms->current_request) { ++ rc = STATUS_READY; ++ } ++ ++ spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); ++ return rc; ++} ++ ++static struct file_operations vtpm_ops = { ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .open = tpm_open, ++ .read = tpm_read, ++ .write = tpm_write, ++ .release = tpm_release, ++}; ++ ++static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); ++static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); ++static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); ++static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); ++static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); ++static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, ++ NULL); ++static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); ++static DEVICE_ATTR(cancel, S_IWUSR |S_IWGRP, NULL, tpm_store_cancel); ++ ++static struct attribute *vtpm_attrs[] = { ++ &dev_attr_pubek.attr, ++ &dev_attr_pcrs.attr, ++ &dev_attr_enabled.attr, ++ &dev_attr_active.attr, ++ &dev_attr_owned.attr, ++ &dev_attr_temp_deactivated.attr, ++ &dev_attr_caps.attr, ++ &dev_attr_cancel.attr, ++ NULL, ++}; ++ ++static struct attribute_group vtpm_attr_grp = { .attrs = vtpm_attrs }; ++ ++#define TPM_LONG_TIMEOUT (10 * 60 * HZ) ++ ++static struct tpm_vendor_specific tpm_vtpm = { ++ .recv = vtpm_recv, ++ .send = vtpm_send, ++ .cancel = vtpm_cancel, ++ .status = vtpm_status, ++ .req_complete_mask = STATUS_BUSY | STATUS_DATA_AVAIL, ++ .req_complete_val = STATUS_DATA_AVAIL, ++ .req_canceled = STATUS_READY, ++ .attr_group = &vtpm_attr_grp, ++ .miscdev = { ++ .fops = &vtpm_ops, ++ }, ++ .duration = { ++ TPM_LONG_TIMEOUT, ++ TPM_LONG_TIMEOUT, ++ TPM_LONG_TIMEOUT, ++ }, ++}; ++ ++struct tpm_chip *init_vtpm(struct device *dev, ++ struct tpm_private *tp) ++{ ++ long rc; ++ struct tpm_chip *chip; ++ struct vtpm_state *vtpms; ++ ++ vtpms = kzalloc(sizeof(struct vtpm_state), GFP_KERNEL); ++ if (!vtpms) ++ return ERR_PTR(-ENOMEM); ++ ++ vtpm_state_init(vtpms); ++ vtpms->tpm_private = tp; ++ ++ chip = tpm_register_hardware(dev, &tpm_vtpm); ++ if (!chip) { ++ rc = -ENODEV; ++ goto err_free_mem; ++ } ++ ++ chip_set_private(chip, vtpms); ++ ++ return chip; ++ ++err_free_mem: ++ kfree(vtpms); ++ ++ return ERR_PTR(rc); ++} ++ ++void cleanup_vtpm(struct device *dev) ++{ ++ struct tpm_chip *chip = dev_get_drvdata(dev); ++ struct vtpm_state *vtpms = (struct vtpm_state*)chip_get_private(chip); ++ tpm_remove_hardware(dev); ++ kfree(vtpms); ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-25/drivers/char/tpm/tpm_vtpm.h 2010-03-24 14:53:41.000000000 +0100 +@@ -0,0 +1,55 @@ ++#ifndef TPM_VTPM_H ++#define TPM_VTPM_H ++ ++struct tpm_chip; ++struct tpm_private; ++ ++struct vtpm_state { ++ struct transmission *current_request; ++ spinlock_t req_list_lock; ++ wait_queue_head_t req_wait_queue; ++ ++ struct list_head queued_requests; ++ ++ struct transmission *current_response; ++ spinlock_t resp_list_lock; ++ wait_queue_head_t resp_wait_queue; // processes waiting for responses ++ ++ u8 vd_status; ++ u8 flags; ++ ++ unsigned long disconnect_time; ++ ++ /* ++ * The following is a private structure of the underlying ++ * driver. It is passed as parameter in the send function. ++ */ ++ struct tpm_private *tpm_private; ++}; ++ ++ ++enum vdev_status { ++ TPM_VD_STATUS_DISCONNECTED = 0x0, ++ TPM_VD_STATUS_CONNECTED = 0x1 ++}; ++ ++/* this function is called from tpm_vtpm.c */ ++int vtpm_vd_send(struct tpm_private * tp, ++ const u8 * buf, size_t count, void *ptr); ++ ++/* these functions are offered by tpm_vtpm.c */ ++struct tpm_chip *init_vtpm(struct device *, ++ struct tpm_private *); ++void cleanup_vtpm(struct device *); ++int vtpm_vd_recv(const struct tpm_chip* chip, ++ const unsigned char *buffer, size_t count, void *ptr); ++void vtpm_vd_status(const struct tpm_chip *, u8 status); ++ ++static inline struct tpm_private *tpm_private_from_dev(struct device *dev) ++{ ++ struct tpm_chip *chip = dev_get_drvdata(dev); ++ struct vtpm_state *vtpms = chip_get_private(chip); ++ return vtpms->tpm_private; ++} ++ ++#endif +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-25/drivers/char/tpm/tpm_xen.c 2010-03-24 14:53:41.000000000 +0100 +@@ -0,0 +1,722 @@ ++/* ++ * Copyright (c) 2005, IBM Corporation ++ * ++ * Author: Stefan Berger, stefanb@us.ibm.com ++ * Grant table support: Mahadevan Gomathisankaran ++ * ++ * This code has been derived from drivers/xen/netfront/netfront.c ++ * ++ * Copyright (c) 2002-2004, K A Fraser ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation; or, when distributed ++ * separately from the Linux kernel or incorporated into other ++ * software packages, subject to the following license: ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this source file (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, copy, modify, ++ * merge, publish, distribute, sublicense, and/or sell copies of the Software, ++ * and to permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "tpm.h" ++#include "tpm_vtpm.h" ++ ++#undef DEBUG ++ ++/* local structures */ ++struct tpm_private { ++ struct tpm_chip *chip; ++ ++ tpmif_tx_interface_t *tx; ++ atomic_t refcnt; ++ unsigned int irq; ++ u8 is_connected; ++ u8 is_suspended; ++ ++ spinlock_t tx_lock; ++ ++ struct tx_buffer *tx_buffers[TPMIF_TX_RING_SIZE]; ++ ++ atomic_t tx_busy; ++ void *tx_remember; ++ ++ domid_t backend_id; ++ wait_queue_head_t wait_q; ++ ++ struct xenbus_device *dev; ++ int ring_ref; ++}; ++ ++struct tx_buffer { ++ unsigned int size; // available space in data ++ unsigned int len; // used space in data ++ unsigned char *data; // pointer to a page ++}; ++ ++ ++/* locally visible variables */ ++static grant_ref_t gref_head; ++static struct tpm_private *my_priv; ++ ++/* local function prototypes */ ++static irqreturn_t tpmif_int(int irq, ++ void *tpm_priv, ++ struct pt_regs *ptregs); ++static void tpmif_rx_action(unsigned long unused); ++static int tpmif_connect(struct xenbus_device *dev, ++ struct tpm_private *tp, ++ domid_t domid); ++static DECLARE_TASKLET(tpmif_rx_tasklet, tpmif_rx_action, 0); ++static int tpmif_allocate_tx_buffers(struct tpm_private *tp); ++static void tpmif_free_tx_buffers(struct tpm_private *tp); ++static void tpmif_set_connected_state(struct tpm_private *tp, ++ u8 newstate); ++static int tpm_xmit(struct tpm_private *tp, ++ const u8 * buf, size_t count, int userbuffer, ++ void *remember); ++static void destroy_tpmring(struct tpm_private *tp); ++void __exit tpmif_exit(void); ++ ++#define DPRINTK(fmt, args...) \ ++ pr_debug("xen_tpm_fr (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args) ++#define IPRINTK(fmt, args...) \ ++ printk(KERN_INFO "xen_tpm_fr: " fmt, ##args) ++#define WPRINTK(fmt, args...) \ ++ printk(KERN_WARNING "xen_tpm_fr: " fmt, ##args) ++ ++#define GRANT_INVALID_REF 0 ++ ++ ++static inline int ++tx_buffer_copy(struct tx_buffer *txb, const u8 *src, int len, ++ int isuserbuffer) ++{ ++ int copied = len; ++ ++ if (len > txb->size) ++ copied = txb->size; ++ if (isuserbuffer) { ++ if (copy_from_user(txb->data, src, copied)) ++ return -EFAULT; ++ } else { ++ memcpy(txb->data, src, copied); ++ } ++ txb->len = len; ++ return copied; ++} ++ ++static inline struct tx_buffer *tx_buffer_alloc(void) ++{ ++ struct tx_buffer *txb; ++ ++ txb = kzalloc(sizeof(struct tx_buffer), GFP_KERNEL); ++ if (!txb) ++ return NULL; ++ ++ txb->len = 0; ++ txb->size = PAGE_SIZE; ++ txb->data = (unsigned char *)__get_free_page(GFP_KERNEL); ++ if (txb->data == NULL) { ++ kfree(txb); ++ txb = NULL; ++ } ++ ++ return txb; ++} ++ ++ ++static inline void tx_buffer_free(struct tx_buffer *txb) ++{ ++ if (txb) { ++ free_page((long)txb->data); ++ kfree(txb); ++ } ++} ++ ++/************************************************************** ++ Utility function for the tpm_private structure ++**************************************************************/ ++static void tpm_private_init(struct tpm_private *tp) ++{ ++ spin_lock_init(&tp->tx_lock); ++ init_waitqueue_head(&tp->wait_q); ++ atomic_set(&tp->refcnt, 1); ++} ++ ++static void tpm_private_put(void) ++{ ++ if (!atomic_dec_and_test(&my_priv->refcnt)) ++ return; ++ ++ tpmif_free_tx_buffers(my_priv); ++ kfree(my_priv); ++ my_priv = NULL; ++} ++ ++static struct tpm_private *tpm_private_get(void) ++{ ++ int err; ++ ++ if (my_priv) { ++ atomic_inc(&my_priv->refcnt); ++ return my_priv; ++ } ++ ++ my_priv = kzalloc(sizeof(struct tpm_private), GFP_KERNEL); ++ if (!my_priv) ++ return NULL; ++ ++ tpm_private_init(my_priv); ++ err = tpmif_allocate_tx_buffers(my_priv); ++ if (err < 0) ++ tpm_private_put(); ++ ++ return my_priv; ++} ++ ++/************************************************************** ++ ++ The interface to let the tpm plugin register its callback ++ function and send data to another partition using this module ++ ++**************************************************************/ ++ ++static DEFINE_MUTEX(suspend_lock); ++/* ++ * Send data via this module by calling this function ++ */ ++int vtpm_vd_send(struct tpm_private *tp, ++ const u8 * buf, size_t count, void *ptr) ++{ ++ int sent; ++ ++ mutex_lock(&suspend_lock); ++ sent = tpm_xmit(tp, buf, count, 0, ptr); ++ mutex_unlock(&suspend_lock); ++ ++ return sent; ++} ++ ++/************************************************************** ++ XENBUS support code ++**************************************************************/ ++ ++static int setup_tpmring(struct xenbus_device *dev, ++ struct tpm_private *tp) ++{ ++ tpmif_tx_interface_t *sring; ++ int err; ++ ++ tp->ring_ref = GRANT_INVALID_REF; ++ ++ sring = (void *)__get_free_page(GFP_KERNEL); ++ if (!sring) { ++ xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring"); ++ return -ENOMEM; ++ } ++ tp->tx = sring; ++ ++ err = xenbus_grant_ring(dev, virt_to_mfn(tp->tx)); ++ if (err < 0) { ++ free_page((unsigned long)sring); ++ tp->tx = NULL; ++ xenbus_dev_fatal(dev, err, "allocating grant reference"); ++ goto fail; ++ } ++ tp->ring_ref = err; ++ ++ err = tpmif_connect(dev, tp, dev->otherend_id); ++ if (err) ++ goto fail; ++ ++ return 0; ++fail: ++ destroy_tpmring(tp); ++ return err; ++} ++ ++ ++static void destroy_tpmring(struct tpm_private *tp) ++{ ++ tpmif_set_connected_state(tp, 0); ++ ++ if (tp->ring_ref != GRANT_INVALID_REF) { ++ gnttab_end_foreign_access(tp->ring_ref, (unsigned long)tp->tx); ++ tp->ring_ref = GRANT_INVALID_REF; ++ tp->tx = NULL; ++ } ++ ++ if (tp->irq) ++ unbind_from_irqhandler(tp->irq, tp); ++ ++ tp->irq = 0; ++} ++ ++ ++static int talk_to_backend(struct xenbus_device *dev, ++ struct tpm_private *tp) ++{ ++ const char *message = NULL; ++ int err; ++ struct xenbus_transaction xbt; ++ ++ err = setup_tpmring(dev, tp); ++ if (err) { ++ xenbus_dev_fatal(dev, err, "setting up ring"); ++ goto out; ++ } ++ ++again: ++ err = xenbus_transaction_start(&xbt); ++ if (err) { ++ xenbus_dev_fatal(dev, err, "starting transaction"); ++ goto destroy_tpmring; ++ } ++ ++ err = xenbus_printf(xbt, dev->nodename, ++ "ring-ref","%u", tp->ring_ref); ++ if (err) { ++ message = "writing ring-ref"; ++ goto abort_transaction; ++ } ++ ++ err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", ++ irq_to_evtchn_port(tp->irq)); ++ if (err) { ++ message = "writing event-channel"; ++ goto abort_transaction; ++ } ++ ++ err = xenbus_transaction_end(xbt, 0); ++ if (err == -EAGAIN) ++ goto again; ++ if (err) { ++ xenbus_dev_fatal(dev, err, "completing transaction"); ++ goto destroy_tpmring; ++ } ++ ++ xenbus_switch_state(dev, XenbusStateConnected); ++ ++ return 0; ++ ++abort_transaction: ++ xenbus_transaction_end(xbt, 1); ++ if (message) ++ xenbus_dev_error(dev, err, "%s", message); ++destroy_tpmring: ++ destroy_tpmring(tp); ++out: ++ return err; ++} ++ ++/** ++ * Callback received when the backend's state changes. ++ */ ++static void backend_changed(struct xenbus_device *dev, ++ enum xenbus_state backend_state) ++{ ++ struct tpm_private *tp = tpm_private_from_dev(&dev->dev); ++ DPRINTK("\n"); ++ ++ switch (backend_state) { ++ case XenbusStateInitialising: ++ case XenbusStateInitWait: ++ case XenbusStateInitialised: ++ case XenbusStateReconfiguring: ++ case XenbusStateReconfigured: ++ case XenbusStateUnknown: ++ break; ++ ++ case XenbusStateConnected: ++ tpmif_set_connected_state(tp, 1); ++ break; ++ ++ case XenbusStateClosing: ++ tpmif_set_connected_state(tp, 0); ++ xenbus_frontend_closed(dev); ++ break; ++ ++ case XenbusStateClosed: ++ tpmif_set_connected_state(tp, 0); ++ if (tp->is_suspended == 0) ++ device_unregister(&dev->dev); ++ xenbus_frontend_closed(dev); ++ break; ++ } ++} ++ ++static int tpmfront_probe(struct xenbus_device *dev, ++ const struct xenbus_device_id *id) ++{ ++ int err; ++ int handle; ++ struct tpm_private *tp = tpm_private_get(); ++ ++ if (!tp) ++ return -ENOMEM; ++ ++ tp->chip = init_vtpm(&dev->dev, tp); ++ if (IS_ERR(tp->chip)) ++ return PTR_ERR(tp->chip); ++ ++ err = xenbus_scanf(XBT_NIL, dev->nodename, ++ "handle", "%i", &handle); ++ if (XENBUS_EXIST_ERR(err)) ++ return err; ++ ++ if (err < 0) { ++ xenbus_dev_fatal(dev,err,"reading virtual-device"); ++ return err; ++ } ++ ++ tp->dev = dev; ++ ++ err = talk_to_backend(dev, tp); ++ if (err) { ++ tpm_private_put(); ++ return err; ++ } ++ ++ return 0; ++} ++ ++ ++static int tpmfront_remove(struct xenbus_device *dev) ++{ ++ struct tpm_private *tp = tpm_private_from_dev(&dev->dev); ++ destroy_tpmring(tp); ++ cleanup_vtpm(&dev->dev); ++ return 0; ++} ++ ++static int tpmfront_suspend(struct xenbus_device *dev) ++{ ++ struct tpm_private *tp = tpm_private_from_dev(&dev->dev); ++ u32 ctr; ++ ++ /* Take the lock, preventing any application from sending. */ ++ mutex_lock(&suspend_lock); ++ tp->is_suspended = 1; ++ ++ for (ctr = 0; atomic_read(&tp->tx_busy); ctr++) { ++ if ((ctr % 10) == 0) ++ printk("TPM-FE [INFO]: Waiting for outstanding " ++ "request.\n"); ++ /* Wait for a request to be responded to. */ ++ interruptible_sleep_on_timeout(&tp->wait_q, 100); ++ } ++ ++ return 0; ++} ++ ++static int tpmfront_suspend_finish(struct tpm_private *tp) ++{ ++ tp->is_suspended = 0; ++ /* Allow applications to send again. */ ++ mutex_unlock(&suspend_lock); ++ return 0; ++} ++ ++static int tpmfront_suspend_cancel(struct xenbus_device *dev) ++{ ++ struct tpm_private *tp = tpm_private_from_dev(&dev->dev); ++ return tpmfront_suspend_finish(tp); ++} ++ ++static int tpmfront_resume(struct xenbus_device *dev) ++{ ++ struct tpm_private *tp = tpm_private_from_dev(&dev->dev); ++ destroy_tpmring(tp); ++ return talk_to_backend(dev, tp); ++} ++ ++static int tpmif_connect(struct xenbus_device *dev, ++ struct tpm_private *tp, ++ domid_t domid) ++{ ++ int err; ++ ++ tp->backend_id = domid; ++ ++ err = bind_listening_port_to_irqhandler( ++ domid, tpmif_int, SA_SAMPLE_RANDOM, "tpmif", tp); ++ if (err <= 0) { ++ WPRINTK("bind_listening_port_to_irqhandler failed " ++ "(err=%d)\n", err); ++ return err; ++ } ++ tp->irq = err; ++ ++ return 0; ++} ++ ++static struct xenbus_device_id tpmfront_ids[] = { ++ { "vtpm" }, ++ { "" } ++}; ++ ++static struct xenbus_driver tpmfront = { ++ .name = "vtpm", ++ .owner = THIS_MODULE, ++ .ids = tpmfront_ids, ++ .probe = tpmfront_probe, ++ .remove = tpmfront_remove, ++ .resume = tpmfront_resume, ++ .otherend_changed = backend_changed, ++ .suspend = tpmfront_suspend, ++ .suspend_cancel = tpmfront_suspend_cancel, ++}; ++ ++static void __init init_tpm_xenbus(void) ++{ ++ xenbus_register_frontend(&tpmfront); ++} ++ ++static int tpmif_allocate_tx_buffers(struct tpm_private *tp) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < TPMIF_TX_RING_SIZE; i++) { ++ tp->tx_buffers[i] = tx_buffer_alloc(); ++ if (!tp->tx_buffers[i]) { ++ tpmif_free_tx_buffers(tp); ++ return -ENOMEM; ++ } ++ } ++ return 0; ++} ++ ++static void tpmif_free_tx_buffers(struct tpm_private *tp) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < TPMIF_TX_RING_SIZE; i++) ++ tx_buffer_free(tp->tx_buffers[i]); ++} ++ ++static void tpmif_rx_action(unsigned long priv) ++{ ++ struct tpm_private *tp = (struct tpm_private *)priv; ++ int i = 0; ++ unsigned int received; ++ unsigned int offset = 0; ++ u8 *buffer; ++ tpmif_tx_request_t *tx = &tp->tx->ring[i].req; ++ ++ atomic_set(&tp->tx_busy, 0); ++ wake_up_interruptible(&tp->wait_q); ++ ++ received = tx->size; ++ ++ buffer = kmalloc(received, GFP_ATOMIC); ++ if (!buffer) ++ return; ++ ++ for (i = 0; i < TPMIF_TX_RING_SIZE && offset < received; i++) { ++ struct tx_buffer *txb = tp->tx_buffers[i]; ++ tpmif_tx_request_t *tx; ++ unsigned int tocopy; ++ ++ tx = &tp->tx->ring[i].req; ++ tocopy = tx->size; ++ if (tocopy > PAGE_SIZE) ++ tocopy = PAGE_SIZE; ++ ++ memcpy(&buffer[offset], txb->data, tocopy); ++ ++ gnttab_release_grant_reference(&gref_head, tx->ref); ++ ++ offset += tocopy; ++ } ++ ++ vtpm_vd_recv(tp->chip, buffer, received, tp->tx_remember); ++ kfree(buffer); ++} ++ ++ ++static irqreturn_t tpmif_int(int irq, void *tpm_priv, struct pt_regs *ptregs) ++{ ++ struct tpm_private *tp = tpm_priv; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tp->tx_lock, flags); ++ tpmif_rx_tasklet.data = (unsigned long)tp; ++ tasklet_schedule(&tpmif_rx_tasklet); ++ spin_unlock_irqrestore(&tp->tx_lock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++static int tpm_xmit(struct tpm_private *tp, ++ const u8 * buf, size_t count, int isuserbuffer, ++ void *remember) ++{ ++ tpmif_tx_request_t *tx; ++ TPMIF_RING_IDX i; ++ unsigned int offset = 0; ++ ++ spin_lock_irq(&tp->tx_lock); ++ ++ if (unlikely(atomic_read(&tp->tx_busy))) { ++ printk("tpm_xmit: There's an outstanding request/response " ++ "on the way!\n"); ++ spin_unlock_irq(&tp->tx_lock); ++ return -EBUSY; ++ } ++ ++ if (tp->is_connected != 1) { ++ spin_unlock_irq(&tp->tx_lock); ++ return -EIO; ++ } ++ ++ for (i = 0; count > 0 && i < TPMIF_TX_RING_SIZE; i++) { ++ struct tx_buffer *txb = tp->tx_buffers[i]; ++ int copied; ++ ++ if (!txb) { ++ DPRINTK("txb (i=%d) is NULL. buffers initilized?\n" ++ "Not transmitting anything!\n", i); ++ spin_unlock_irq(&tp->tx_lock); ++ return -EFAULT; ++ } ++ ++ copied = tx_buffer_copy(txb, &buf[offset], count, ++ isuserbuffer); ++ if (copied < 0) { ++ /* An error occurred */ ++ spin_unlock_irq(&tp->tx_lock); ++ return copied; ++ } ++ count -= copied; ++ offset += copied; ++ ++ tx = &tp->tx->ring[i].req; ++ tx->addr = virt_to_machine(txb->data); ++ tx->size = txb->len; ++ tx->unused = 0; ++ ++ DPRINTK("First 4 characters sent by TPM-FE are " ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ txb->data[0],txb->data[1],txb->data[2],txb->data[3]); ++ ++ /* Get the granttable reference for this page. */ ++ tx->ref = gnttab_claim_grant_reference(&gref_head); ++ if (tx->ref == -ENOSPC) { ++ spin_unlock_irq(&tp->tx_lock); ++ DPRINTK("Grant table claim reference failed in " ++ "func:%s line:%d file:%s\n", ++ __FUNCTION__, __LINE__, __FILE__); ++ return -ENOSPC; ++ } ++ gnttab_grant_foreign_access_ref(tx->ref, ++ tp->backend_id, ++ virt_to_mfn(txb->data), ++ 0 /*RW*/); ++ wmb(); ++ } ++ ++ atomic_set(&tp->tx_busy, 1); ++ tp->tx_remember = remember; ++ ++ mb(); ++ ++ notify_remote_via_irq(tp->irq); ++ ++ spin_unlock_irq(&tp->tx_lock); ++ return offset; ++} ++ ++ ++static void tpmif_notify_upperlayer(struct tpm_private *tp) ++{ ++ /* Notify upper layer about the state of the connection to the BE. */ ++ vtpm_vd_status(tp->chip, (tp->is_connected ++ ? TPM_VD_STATUS_CONNECTED ++ : TPM_VD_STATUS_DISCONNECTED)); ++} ++ ++ ++static void tpmif_set_connected_state(struct tpm_private *tp, u8 is_connected) ++{ ++ /* ++ * Don't notify upper layer if we are in suspend mode and ++ * should disconnect - assumption is that we will resume ++ * The mutex keeps apps from sending. ++ */ ++ if (is_connected == 0 && tp->is_suspended == 1) ++ return; ++ ++ /* ++ * Unlock the mutex if we are connected again ++ * after being suspended - now resuming. ++ * This also removes the suspend state. ++ */ ++ if (is_connected == 1 && tp->is_suspended == 1) ++ tpmfront_suspend_finish(tp); ++ ++ if (is_connected != tp->is_connected) { ++ tp->is_connected = is_connected; ++ tpmif_notify_upperlayer(tp); ++ } ++} ++ ++ ++ ++/* ================================================================= ++ * Initialization function. ++ * ================================================================= ++ */ ++ ++ ++static int __init tpmif_init(void) ++{ ++ struct tpm_private *tp; ++ ++ if (is_initial_xendomain()) ++ return -EPERM; ++ ++ tp = tpm_private_get(); ++ if (!tp) ++ return -ENOMEM; ++ ++ IPRINTK("Initialising the vTPM driver.\n"); ++ if (gnttab_alloc_grant_references(TPMIF_TX_RING_SIZE, ++ &gref_head) < 0) { ++ tpm_private_put(); ++ return -EFAULT; ++ } ++ ++ init_tpm_xenbus(); ++ return 0; ++} ++ ++ ++module_init(tpmif_init); ++ ++MODULE_LICENSE("Dual BSD/GPL"); +--- head-2010-05-25.orig/drivers/edac/edac_mc.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/edac/edac_mc.c 2010-03-24 14:53:41.000000000 +0100 +@@ -602,6 +602,10 @@ static void edac_mc_scrub_block(unsigned + + debugf3("%s()\n", __func__); + ++#ifdef CONFIG_XEN ++ page = mfn_to_local_pfn(page); ++#endif ++ + /* ECC error page was not in our memory. Ignore it. */ + if (!pfn_valid(page)) + return; +--- head-2010-05-25.orig/drivers/firmware/dell_rbu.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/firmware/dell_rbu.c 2010-04-15 09:43:35.000000000 +0200 +@@ -170,9 +170,28 @@ static int create_packet(void *data, siz + spin_lock(&rbu_data.lock); + goto out_alloc_packet_array; + } ++#ifdef CONFIG_XEN ++ if (ordernum && xen_create_contiguous_region( ++ (unsigned long)packet_data_temp_buf, ordernum, 0)) { ++ free_pages((unsigned long)packet_data_temp_buf, ++ ordernum); ++ printk(KERN_WARNING ++ "dell_rbu:%s: failed to adjust new " ++ "packet\n", __func__); ++ retval = -ENOMEM; ++ spin_lock(&rbu_data.lock); ++ goto out_alloc_packet_array; ++ } ++#endif + +- if ((unsigned long)virt_to_phys(packet_data_temp_buf) ++ if ((unsigned long)virt_to_bus(packet_data_temp_buf) + < allocation_floor) { ++#ifdef CONFIG_XEN ++ if (ordernum) ++ xen_destroy_contiguous_region( ++ (unsigned long)packet_data_temp_buf, ++ ordernum); ++#endif + pr_debug("packet 0x%lx below floor at 0x%lx.\n", + (unsigned long)virt_to_phys( + packet_data_temp_buf), +@@ -186,7 +205,7 @@ static int create_packet(void *data, siz + newpacket->data = packet_data_temp_buf; + + pr_debug("create_packet: newpacket at physical addr %lx\n", +- (unsigned long)virt_to_phys(newpacket->data)); ++ (unsigned long)virt_to_bus(newpacket->data)); + + /* packets may not have fixed size */ + newpacket->length = length; +@@ -205,7 +224,7 @@ out_alloc_packet_array: + /* always free packet array */ + for (;idx>0;idx--) { + pr_debug("freeing unused packet below floor 0x%lx.\n", +- (unsigned long)virt_to_phys( ++ (unsigned long)virt_to_bus( + invalid_addr_packet_array[idx-1])); + free_pages((unsigned long)invalid_addr_packet_array[idx-1], + ordernum); +@@ -349,6 +368,13 @@ static void packet_empty_list(void) + * to make sure there are no stale RBU packets left in memory + */ + memset(newpacket->data, 0, rbu_data.packetsize); ++#ifdef CONFIG_XEN ++ if (newpacket->ordernum) ++ xen_destroy_contiguous_region( ++ (unsigned long)newpacket->data, ++ newpacket->ordernum); ++#endif ++ + free_pages((unsigned long) newpacket->data, + newpacket->ordernum); + kfree(newpacket); +@@ -403,7 +429,9 @@ static int img_update_realloc(unsigned l + { + unsigned char *image_update_buffer = NULL; + unsigned long rc; ++#ifndef CONFIG_XEN + unsigned long img_buf_phys_addr; ++#endif + int ordernum; + int dma_alloc = 0; + +@@ -434,15 +462,19 @@ static int img_update_realloc(unsigned l + + spin_unlock(&rbu_data.lock); + ++#ifndef CONFIG_XEN + ordernum = get_order(size); + image_update_buffer = + (unsigned char *) __get_free_pages(GFP_KERNEL, ordernum); + + img_buf_phys_addr = +- (unsigned long) virt_to_phys(image_update_buffer); ++ (unsigned long) virt_to_bus(image_update_buffer); + + if (img_buf_phys_addr > BIOS_SCAN_LIMIT) { + free_pages((unsigned long) image_update_buffer, ordernum); ++#else ++ { ++#endif + ordernum = -1; + image_update_buffer = dma_alloc_coherent(NULL, size, + &dell_rbu_dmaaddr, GFP_KERNEL); +@@ -695,6 +727,12 @@ static struct bin_attribute rbu_packet_s + static int __init dcdrbu_init(void) + { + int rc; ++ ++#ifdef CONFIG_XEN ++ if (!is_initial_xendomain()) ++ return -ENODEV; ++#endif ++ + spin_lock_init(&rbu_data.lock); + + init_packet_head(); +--- head-2010-05-25.orig/drivers/ide/ide-lib.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/ide/ide-lib.c 2010-03-24 14:53:41.000000000 +0100 +@@ -18,12 +18,12 @@ void ide_toggle_bounce(ide_drive_t *driv + { + u64 addr = BLK_BOUNCE_HIGH; /* dma64_addr_t */ + +- if (!PCI_DMA_BUS_IS_PHYS) { +- addr = BLK_BOUNCE_ANY; +- } else if (on && drive->media == ide_disk) { ++ if (on && drive->media == ide_disk) { + struct device *dev = drive->hwif->dev; + +- if (dev && dev->dma_mask) ++ if (!PCI_DMA_BUS_IS_PHYS) ++ addr = BLK_BOUNCE_ANY; ++ else if (dev && dev->dma_mask) + addr = *dev->dma_mask; + } + +--- head-2010-05-25.orig/drivers/oprofile/buffer_sync.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/oprofile/buffer_sync.c 2010-04-15 09:43:44.000000000 +0200 +@@ -8,6 +8,10 @@ + * @author Barry Kasindorf + * @author Robert Richter + * ++ * Modified by Aravind Menon for Xen ++ * These modifications are: ++ * Copyright (C) 2005 Hewlett-Packard Co. ++ * + * This is the core of the buffer management. Each + * CPU buffer is processed and entered into the + * global event buffer. Such processing is necessary +@@ -43,6 +47,8 @@ static cpumask_var_t marked_cpus; + static DEFINE_SPINLOCK(task_mortuary); + static void process_task_mortuary(void); + ++static int cpu_current_domain[NR_CPUS]; ++ + /* Take ownership of the task struct and place it on the + * list for processing. Only after two full buffer syncs + * does the task eventually get freed, because by then +@@ -61,7 +67,6 @@ task_free_notify(struct notifier_block * + return NOTIFY_OK; + } + +- + /* The task is on its way out. A sync of the buffer means we can catch + * any remaining samples for this task. + */ +@@ -154,6 +159,11 @@ static void end_sync(void) + int sync_start(void) + { + int err; ++ int i; ++ ++ for (i = 0; i < NR_CPUS; i++) { ++ cpu_current_domain[i] = COORDINATOR_DOMAIN; ++ } + + if (!zalloc_cpumask_var(&marked_cpus, GFP_KERNEL)) + return -ENOMEM; +@@ -285,13 +295,29 @@ static void add_cpu_switch(int i) + last_cookie = INVALID_COOKIE; + } + +-static void add_kernel_ctx_switch(unsigned int in_kernel) ++static void add_cpu_mode_switch(unsigned int cpu_mode) + { + add_event_entry(ESCAPE_CODE); +- if (in_kernel) ++ switch (cpu_mode) { ++ case CPU_MODE_USER: ++ add_event_entry(USER_ENTER_SWITCH_CODE); ++ break; ++ case CPU_MODE_KERNEL: + add_event_entry(KERNEL_ENTER_SWITCH_CODE); +- else +- add_event_entry(KERNEL_EXIT_SWITCH_CODE); ++ break; ++ case CPU_MODE_XEN: ++ add_event_entry(XEN_ENTER_SWITCH_CODE); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void add_domain_switch(unsigned long domain_id) ++{ ++ add_event_entry(ESCAPE_CODE); ++ add_event_entry(DOMAIN_SWITCH_CODE); ++ add_event_entry(domain_id); + } + + static void +@@ -372,12 +398,12 @@ static inline void add_sample_entry(unsi + * for later lookup from userspace. Return 0 on failure. + */ + static int +-add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) ++add_sample(struct mm_struct *mm, struct op_sample *s, int cpu_mode) + { + unsigned long cookie; + off_t offset; + +- if (in_kernel) { ++ if (cpu_mode >= CPU_MODE_KERNEL) { + add_sample_entry(s->eip, s->event); + return 1; + } +@@ -502,9 +528,10 @@ void sync_buffer(int cpu) + unsigned long val; + struct task_struct *new; + unsigned long cookie = 0; +- int in_kernel = 1; ++ int cpu_mode = CPU_MODE_KERNEL; + sync_buffer_state state = sb_buffer_start; + unsigned int i; ++ int domain_switch = 0; + unsigned long available; + unsigned long flags; + struct op_entry entry; +@@ -514,6 +541,11 @@ void sync_buffer(int cpu) + + add_cpu_switch(cpu); + ++ /* We need to assign the first samples in this CPU buffer to the ++ same domain that we were processing at the last sync_buffer */ ++ if (cpu_current_domain[cpu] != COORDINATOR_DOMAIN) ++ add_domain_switch(cpu_current_domain[cpu]); ++ + op_cpu_buffer_reset(cpu); + available = op_cpu_buffer_entries(cpu); + +@@ -522,6 +554,13 @@ void sync_buffer(int cpu) + if (!sample) + break; + ++ if (domain_switch) { ++ cpu_current_domain[cpu] = sample->eip; ++ add_domain_switch(sample->eip); ++ domain_switch = 0; ++ continue; ++ } ++ + if (is_code(sample->eip)) { + flags = sample->event; + if (flags & TRACE_BEGIN) { +@@ -530,10 +569,10 @@ void sync_buffer(int cpu) + } + if (flags & KERNEL_CTX_SWITCH) { + /* kernel/userspace switch */ +- in_kernel = flags & IS_KERNEL; ++ cpu_mode = flags & CPU_MODE_MASK; + if (state == sb_buffer_start) + state = sb_sample_start; +- add_kernel_ctx_switch(flags & IS_KERNEL); ++ add_cpu_mode_switch(cpu_mode); + } + if (flags & USER_CTX_SWITCH + && op_cpu_buffer_get_data(&entry, &val)) { +@@ -546,16 +585,23 @@ void sync_buffer(int cpu) + cookie = get_exec_dcookie(mm); + add_user_ctx_switch(new, cookie); + } ++ if (flags & DOMAIN_SWITCH) ++ domain_switch = 1; + if (op_cpu_buffer_get_size(&entry)) + add_data(&entry, mm); + continue; + } + ++ if (cpu_current_domain[cpu] != COORDINATOR_DOMAIN) { ++ add_sample_entry(sample->eip, sample->event); ++ continue; ++ } ++ + if (state < sb_bt_start) + /* ignore sample */ + continue; + +- if (add_sample(mm, sample, in_kernel)) ++ if (add_sample(mm, sample, cpu_mode)) + continue; + + /* ignore backtraces if failed to add a sample */ +@@ -566,6 +612,10 @@ void sync_buffer(int cpu) + } + release_mm(mm); + ++ /* We reset domain to COORDINATOR at each CPU switch */ ++ if (cpu_current_domain[cpu] != COORDINATOR_DOMAIN) ++ add_domain_switch(COORDINATOR_DOMAIN); ++ + mark_done(cpu); + + mutex_unlock(&buffer_mutex); +--- head-2010-05-25.orig/drivers/oprofile/cpu_buffer.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/oprofile/cpu_buffer.c 2010-03-24 14:53:41.000000000 +0100 +@@ -8,6 +8,10 @@ + * @author Barry Kasindorf + * @author Robert Richter + * ++ * Modified by Aravind Menon for Xen ++ * These modifications are: ++ * Copyright (C) 2005 Hewlett-Packard Co. ++ * + * Each CPU has a local buffer that stores PC value/event + * pairs. We also log context switches when we notice them. + * Eventually each CPU's buffer is processed into the global +@@ -54,6 +58,8 @@ static void wq_sync_buffer(struct work_s + #define DEFAULT_TIMER_EXPIRE (HZ / 10) + static int work_enabled; + ++static int32_t current_domain = COORDINATOR_DOMAIN; ++ + unsigned long oprofile_get_cpu_buffer_size(void) + { + return oprofile_cpu_buffer_size; +@@ -97,7 +103,7 @@ int alloc_cpu_buffers(void) + struct oprofile_cpu_buffer *b = &per_cpu(op_cpu_buffer, i); + + b->last_task = NULL; +- b->last_is_kernel = -1; ++ b->last_cpu_mode = -1; + b->tracing = 0; + b->buffer_size = buffer_size; + b->sample_received = 0; +@@ -215,7 +221,7 @@ unsigned long op_cpu_buffer_entries(int + + static int + op_add_code(struct oprofile_cpu_buffer *cpu_buf, unsigned long backtrace, +- int is_kernel, struct task_struct *task) ++ int cpu_mode, struct task_struct *task) + { + struct op_entry entry; + struct op_sample *sample; +@@ -228,16 +234,15 @@ op_add_code(struct oprofile_cpu_buffer * + flags |= TRACE_BEGIN; + + /* notice a switch from user->kernel or vice versa */ +- is_kernel = !!is_kernel; +- if (cpu_buf->last_is_kernel != is_kernel) { +- cpu_buf->last_is_kernel = is_kernel; +- flags |= KERNEL_CTX_SWITCH; +- if (is_kernel) +- flags |= IS_KERNEL; ++ if (cpu_buf->last_cpu_mode != cpu_mode) { ++ cpu_buf->last_cpu_mode = cpu_mode; ++ flags |= KERNEL_CTX_SWITCH | cpu_mode; + } + + /* notice a task switch */ +- if (cpu_buf->last_task != task) { ++ /* if not processing other domain samples */ ++ if (cpu_buf->last_task != task && ++ current_domain == COORDINATOR_DOMAIN) { + cpu_buf->last_task = task; + flags |= USER_CTX_SWITCH; + } +@@ -286,14 +291,14 @@ op_add_sample(struct oprofile_cpu_buffer + /* + * This must be safe from any context. + * +- * is_kernel is needed because on some architectures you cannot ++ * cpu_mode is needed because on some architectures you cannot + * tell if you are in kernel or user space simply by looking at +- * pc. We tag this in the buffer by generating kernel enter/exit +- * events whenever is_kernel changes ++ * pc. We tag this in the buffer by generating kernel/user (and ++ * xen) enter events whenever cpu_mode changes + */ + static int + log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, +- unsigned long backtrace, int is_kernel, unsigned long event) ++ unsigned long backtrace, int cpu_mode, unsigned long event) + { + cpu_buf->sample_received++; + +@@ -302,7 +307,7 @@ log_sample(struct oprofile_cpu_buffer *c + return 0; + } + +- if (op_add_code(cpu_buf, backtrace, is_kernel, current)) ++ if (op_add_code(cpu_buf, backtrace, cpu_mode, current)) + goto fail; + + if (op_add_sample(cpu_buf, pc, event)) +@@ -457,6 +462,25 @@ fail: + return; + } + ++int oprofile_add_domain_switch(int32_t domain_id) ++{ ++ struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[smp_processor_id()]; ++ ++ /* should have space for switching into and out of domain ++ (2 slots each) plus one sample and one cpu mode switch */ ++ if (((nr_available_slots(cpu_buf) < 6) && ++ (domain_id != COORDINATOR_DOMAIN)) || ++ (nr_available_slots(cpu_buf) < 2)) ++ return 0; ++ ++ add_code(cpu_buf, DOMAIN_SWITCH); ++ add_sample(cpu_buf, domain_id, 0); ++ ++ current_domain = domain_id; ++ ++ return 1; ++} ++ + /* + * This serves to avoid cpu buffer overflow, and makes sure + * the task mortuary progresses +--- head-2010-05-25.orig/drivers/oprofile/cpu_buffer.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/oprofile/cpu_buffer.h 2010-03-24 14:53:41.000000000 +0100 +@@ -40,7 +40,7 @@ struct op_entry; + struct oprofile_cpu_buffer { + unsigned long buffer_size; + struct task_struct *last_task; +- int last_is_kernel; ++ int last_cpu_mode; + int tracing; + unsigned long sample_received; + unsigned long sample_lost_overflow; +@@ -62,7 +62,7 @@ static inline void op_cpu_buffer_reset(i + { + struct oprofile_cpu_buffer *cpu_buf = &per_cpu(op_cpu_buffer, cpu); + +- cpu_buf->last_is_kernel = -1; ++ cpu_buf->last_cpu_mode = -1; + cpu_buf->last_task = NULL; + } + +@@ -112,9 +112,13 @@ int op_cpu_buffer_get_data(struct op_ent + } + + /* extra data flags */ +-#define KERNEL_CTX_SWITCH (1UL << 0) +-#define IS_KERNEL (1UL << 1) ++#define CPU_MODE_USER 0 ++#define CPU_MODE_KERNEL 1 ++#define CPU_MODE_XEN 2 ++#define CPU_MODE_MASK 3 + #define TRACE_BEGIN (1UL << 2) + #define USER_CTX_SWITCH (1UL << 3) ++#define KERNEL_CTX_SWITCH (1UL << 4) ++#define DOMAIN_SWITCH (1UL << 5) + + #endif /* OPROFILE_CPU_BUFFER_H */ +--- head-2010-05-25.orig/drivers/oprofile/event_buffer.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/oprofile/event_buffer.h 2010-03-24 14:53:41.000000000 +0100 +@@ -30,6 +30,9 @@ void wake_up_buffer_waiter(void); + #define INVALID_COOKIE ~0UL + #define NO_COOKIE 0UL + ++/* Constant used to refer to coordinator domain (Xen) */ ++#define COORDINATOR_DOMAIN -1 ++ + extern const struct file_operations event_buffer_fops; + + /* mutex between sync_cpu_buffers() and the +--- head-2010-05-25.orig/drivers/oprofile/oprof.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/oprofile/oprof.c 2010-03-24 14:53:41.000000000 +0100 +@@ -5,6 +5,10 @@ + * @remark Read the file COPYING + * + * @author John Levon ++ * ++ * Modified by Aravind Menon for Xen ++ * These modifications are: ++ * Copyright (C) 2005 Hewlett-Packard Co. + */ + + #include +@@ -35,6 +39,32 @@ static DEFINE_MUTEX(start_mutex); + */ + static int timer = 0; + ++int oprofile_set_active(int active_domains[], unsigned int adomains) ++{ ++ int err; ++ ++ if (!oprofile_ops.set_active) ++ return -EINVAL; ++ ++ mutex_lock(&start_mutex); ++ err = oprofile_ops.set_active(active_domains, adomains); ++ mutex_unlock(&start_mutex); ++ return err; ++} ++ ++int oprofile_set_passive(int passive_domains[], unsigned int pdomains) ++{ ++ int err; ++ ++ if (!oprofile_ops.set_passive) ++ return -EINVAL; ++ ++ mutex_lock(&start_mutex); ++ err = oprofile_ops.set_passive(passive_domains, pdomains); ++ mutex_unlock(&start_mutex); ++ return err; ++} ++ + int oprofile_setup(void) + { + int err; +--- head-2010-05-25.orig/drivers/oprofile/oprof.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/oprofile/oprof.h 2010-03-24 14:53:41.000000000 +0100 +@@ -39,4 +39,7 @@ void oprofile_timer_init(struct oprofile + int oprofile_set_backtrace(unsigned long depth); + int oprofile_set_timeout(unsigned long time); + ++int oprofile_set_active(int active_domains[], unsigned int adomains); ++int oprofile_set_passive(int passive_domains[], unsigned int pdomains); ++ + #endif /* OPROF_H */ +--- head-2010-05-25.orig/drivers/oprofile/oprofile_files.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/drivers/oprofile/oprofile_files.c 2010-03-24 14:53:41.000000000 +0100 +@@ -5,11 +5,17 @@ + * @remark Read the file COPYING + * + * @author John Levon ++ * ++ * Modified by Aravind Menon for Xen ++ * These modifications are: ++ * Copyright (C) 2005 Hewlett-Packard Co. + */ + + #include + #include + #include ++#include ++#include + + #include "event_buffer.h" + #include "oprofile_stats.h" +@@ -165,6 +171,195 @@ static const struct file_operations dump + .write = dump_write, + }; + ++#define TMPBUFSIZE 512 ++ ++static unsigned int adomains = 0; ++static int active_domains[MAX_OPROF_DOMAINS + 1]; ++static DEFINE_MUTEX(adom_mutex); ++ ++static ssize_t adomain_write(struct file * file, char const __user * buf, ++ size_t count, loff_t * offset) ++{ ++ char *tmpbuf; ++ char *startp, *endp; ++ int i; ++ unsigned long val; ++ ssize_t retval = count; ++ ++ if (*offset) ++ return -EINVAL; ++ if (count > TMPBUFSIZE - 1) ++ return -EINVAL; ++ ++ if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ if (copy_from_user(tmpbuf, buf, count)) { ++ kfree(tmpbuf); ++ return -EFAULT; ++ } ++ tmpbuf[count] = 0; ++ ++ mutex_lock(&adom_mutex); ++ ++ startp = tmpbuf; ++ /* Parse one more than MAX_OPROF_DOMAINS, for easy error checking */ ++ for (i = 0; i <= MAX_OPROF_DOMAINS; i++) { ++ val = simple_strtoul(startp, &endp, 0); ++ if (endp == startp) ++ break; ++ while (ispunct(*endp) || isspace(*endp)) ++ endp++; ++ active_domains[i] = val; ++ if (active_domains[i] != val) ++ /* Overflow, force error below */ ++ i = MAX_OPROF_DOMAINS + 1; ++ startp = endp; ++ } ++ /* Force error on trailing junk */ ++ adomains = *startp ? MAX_OPROF_DOMAINS + 1 : i; ++ ++ kfree(tmpbuf); ++ ++ if (adomains > MAX_OPROF_DOMAINS ++ || oprofile_set_active(active_domains, adomains)) { ++ adomains = 0; ++ retval = -EINVAL; ++ } ++ ++ mutex_unlock(&adom_mutex); ++ return retval; ++} ++ ++static ssize_t adomain_read(struct file * file, char __user * buf, ++ size_t count, loff_t * offset) ++{ ++ char * tmpbuf; ++ size_t len; ++ int i; ++ ssize_t retval; ++ ++ if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ mutex_lock(&adom_mutex); ++ ++ len = 0; ++ for (i = 0; i < adomains; i++) ++ len += snprintf(tmpbuf + len, ++ len < TMPBUFSIZE ? TMPBUFSIZE - len : 0, ++ "%u ", active_domains[i]); ++ WARN_ON(len > TMPBUFSIZE); ++ if (len != 0 && len <= TMPBUFSIZE) ++ tmpbuf[len-1] = '\n'; ++ ++ mutex_unlock(&adom_mutex); ++ ++ retval = simple_read_from_buffer(buf, count, offset, tmpbuf, len); ++ ++ kfree(tmpbuf); ++ return retval; ++} ++ ++ ++static const struct file_operations active_domain_ops = { ++ .read = adomain_read, ++ .write = adomain_write, ++}; ++ ++static unsigned int pdomains = 0; ++static int passive_domains[MAX_OPROF_DOMAINS]; ++static DEFINE_MUTEX(pdom_mutex); ++ ++static ssize_t pdomain_write(struct file * file, char const __user * buf, ++ size_t count, loff_t * offset) ++{ ++ char *tmpbuf; ++ char *startp, *endp; ++ int i; ++ unsigned long val; ++ ssize_t retval = count; ++ ++ if (*offset) ++ return -EINVAL; ++ if (count > TMPBUFSIZE - 1) ++ return -EINVAL; ++ ++ if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ if (copy_from_user(tmpbuf, buf, count)) { ++ kfree(tmpbuf); ++ return -EFAULT; ++ } ++ tmpbuf[count] = 0; ++ ++ mutex_lock(&pdom_mutex); ++ ++ startp = tmpbuf; ++ /* Parse one more than MAX_OPROF_DOMAINS, for easy error checking */ ++ for (i = 0; i <= MAX_OPROF_DOMAINS; i++) { ++ val = simple_strtoul(startp, &endp, 0); ++ if (endp == startp) ++ break; ++ while (ispunct(*endp) || isspace(*endp)) ++ endp++; ++ passive_domains[i] = val; ++ if (passive_domains[i] != val) ++ /* Overflow, force error below */ ++ i = MAX_OPROF_DOMAINS + 1; ++ startp = endp; ++ } ++ /* Force error on trailing junk */ ++ pdomains = *startp ? MAX_OPROF_DOMAINS + 1 : i; ++ ++ kfree(tmpbuf); ++ ++ if (pdomains > MAX_OPROF_DOMAINS ++ || oprofile_set_passive(passive_domains, pdomains)) { ++ pdomains = 0; ++ retval = -EINVAL; ++ } ++ ++ mutex_unlock(&pdom_mutex); ++ return retval; ++} ++ ++static ssize_t pdomain_read(struct file * file, char __user * buf, ++ size_t count, loff_t * offset) ++{ ++ char * tmpbuf; ++ size_t len; ++ int i; ++ ssize_t retval; ++ ++ if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ mutex_lock(&pdom_mutex); ++ ++ len = 0; ++ for (i = 0; i < pdomains; i++) ++ len += snprintf(tmpbuf + len, ++ len < TMPBUFSIZE ? TMPBUFSIZE - len : 0, ++ "%u ", passive_domains[i]); ++ WARN_ON(len > TMPBUFSIZE); ++ if (len != 0 && len <= TMPBUFSIZE) ++ tmpbuf[len-1] = '\n'; ++ ++ mutex_unlock(&pdom_mutex); ++ ++ retval = simple_read_from_buffer(buf, count, offset, tmpbuf, len); ++ ++ kfree(tmpbuf); ++ return retval; ++} ++ ++static const struct file_operations passive_domain_ops = { ++ .read = pdomain_read, ++ .write = pdomain_write, ++}; ++ + void oprofile_create_files(struct super_block *sb, struct dentry *root) + { + /* reinitialize default values */ +@@ -175,6 +370,8 @@ void oprofile_create_files(struct super_ + + oprofilefs_create_file(sb, root, "enable", &enable_fops); + oprofilefs_create_file_perm(sb, root, "dump", &dump_fops, 0666); ++ oprofilefs_create_file(sb, root, "active_domains", &active_domain_ops); ++ oprofilefs_create_file(sb, root, "passive_domains", &passive_domain_ops); + oprofilefs_create_file(sb, root, "buffer", &event_buffer_fops); + oprofilefs_create_ulong(sb, root, "buffer_size", &oprofile_buffer_size); + oprofilefs_create_ulong(sb, root, "buffer_watershed", &oprofile_buffer_watershed); +--- head-2010-05-25.orig/fs/aio.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/fs/aio.c 2010-03-24 14:53:41.000000000 +0100 +@@ -40,6 +40,11 @@ + #include + #include + ++#ifdef CONFIG_EPOLL ++#include ++#include ++#endif ++ + #if DEBUG > 1 + #define dprintk printk + #else +@@ -997,6 +1002,11 @@ put_rq: + if (waitqueue_active(&ctx->wait)) + wake_up(&ctx->wait); + ++#ifdef CONFIG_EPOLL ++ if (ctx->file && waitqueue_active(&ctx->poll_wait)) ++ wake_up(&ctx->poll_wait); ++#endif ++ + spin_unlock_irqrestore(&ctx->ctx_lock, flags); + return ret; + } +@@ -1005,6 +1015,8 @@ EXPORT_SYMBOL(aio_complete); + /* aio_read_evt + * Pull an event off of the ioctx's event ring. Returns the number of + * events fetched (0 or 1 ;-) ++ * If ent parameter is 0, just returns the number of events that would ++ * be fetched. + * FIXME: make this use cmpxchg. + * TODO: make the ringbuffer user mmap()able (requires FIXME). + */ +@@ -1027,13 +1039,18 @@ static int aio_read_evt(struct kioctx *i + + head = ring->head % info->nr; + if (head != ring->tail) { +- struct io_event *evp = aio_ring_event(info, head, KM_USER1); +- *ent = *evp; +- head = (head + 1) % info->nr; +- smp_mb(); /* finish reading the event before updatng the head */ +- ring->head = head; +- ret = 1; +- put_aio_ring_event(evp, KM_USER1); ++ if (ent) { /* event requested */ ++ struct io_event *evp = ++ aio_ring_event(info, head, KM_USER1); ++ *ent = *evp; ++ head = (head + 1) % info->nr; ++ /* finish reading the event before updatng the head */ ++ smp_mb(); ++ ring->head = head; ++ ret = 1; ++ put_aio_ring_event(evp, KM_USER1); ++ } else /* only need to know availability */ ++ ret = 1; + } + spin_unlock(&info->ring_lock); + +@@ -1218,6 +1235,13 @@ static void io_destroy(struct kioctx *io + + aio_cancel_all(ioctx); + wait_for_all_aios(ioctx); ++#ifdef CONFIG_EPOLL ++ /* forget the poll file, but it's up to the user to close it */ ++ if (ioctx->file) { ++ ioctx->file->private_data = 0; ++ ioctx->file = 0; ++ } ++#endif + + /* + * Wake up any waiters. The setting of ctx->dead must be seen +@@ -1228,6 +1252,67 @@ static void io_destroy(struct kioctx *io + put_ioctx(ioctx); /* once for the lookup */ + } + ++#ifdef CONFIG_EPOLL ++ ++static int aio_queue_fd_close(struct inode *inode, struct file *file) ++{ ++ struct kioctx *ioctx = file->private_data; ++ if (ioctx) { ++ file->private_data = 0; ++ spin_lock_irq(&ioctx->ctx_lock); ++ ioctx->file = 0; ++ spin_unlock_irq(&ioctx->ctx_lock); ++ } ++ return 0; ++} ++ ++static unsigned int aio_queue_fd_poll(struct file *file, poll_table *wait) ++{ unsigned int pollflags = 0; ++ struct kioctx *ioctx = file->private_data; ++ ++ if (ioctx) { ++ ++ spin_lock_irq(&ioctx->ctx_lock); ++ /* Insert inside our poll wait queue */ ++ poll_wait(file, &ioctx->poll_wait, wait); ++ ++ /* Check our condition */ ++ if (aio_read_evt(ioctx, 0)) ++ pollflags = POLLIN | POLLRDNORM; ++ spin_unlock_irq(&ioctx->ctx_lock); ++ } ++ ++ return pollflags; ++} ++ ++static const struct file_operations aioq_fops = { ++ .release = aio_queue_fd_close, ++ .poll = aio_queue_fd_poll ++}; ++ ++/* make_aio_fd: ++ * Create a file descriptor that can be used to poll the event queue. ++ * Based and piggybacked on the excellent epoll code. ++ */ ++ ++static int make_aio_fd(struct kioctx *ioctx) ++{ ++ int error, fd; ++ struct inode *inode; ++ struct file *file; ++ ++ error = ep_getfd(&fd, &inode, &file, NULL, &aioq_fops); ++ if (error) ++ return error; ++ ++ /* associate the file with the IO context */ ++ file->private_data = ioctx; ++ ioctx->file = file; ++ init_waitqueue_head(&ioctx->poll_wait); ++ return fd; ++} ++#endif ++ + /* sys_io_setup: + * Create an aio_context capable of receiving at least nr_events. + * ctxp must not point to an aio_context that already exists, and +@@ -1240,18 +1325,30 @@ static void io_destroy(struct kioctx *io + * resources are available. May fail with -EFAULT if an invalid + * pointer is passed for ctxp. Will fail with -ENOSYS if not + * implemented. ++ * ++ * To request a selectable fd, the user context has to be initialized ++ * to 1, instead of 0, and the return value is the fd. ++ * This keeps the system call compatible, since a non-zero value ++ * was not allowed so far. + */ + SYSCALL_DEFINE2(io_setup, unsigned, nr_events, aio_context_t __user *, ctxp) + { + struct kioctx *ioctx = NULL; + unsigned long ctx; + long ret; ++ int make_fd = 0; + + ret = get_user(ctx, ctxp); + if (unlikely(ret)) + goto out; + + ret = -EINVAL; ++#ifdef CONFIG_EPOLL ++ if (ctx == 1) { ++ make_fd = 1; ++ ctx = 0; ++ } ++#endif + if (unlikely(ctx || nr_events == 0)) { + pr_debug("EINVAL: io_setup: ctx %lu nr_events %u\n", + ctx, nr_events); +@@ -1262,8 +1359,12 @@ SYSCALL_DEFINE2(io_setup, unsigned, nr_e + ret = PTR_ERR(ioctx); + if (!IS_ERR(ioctx)) { + ret = put_user(ioctx->user_id, ctxp); +- if (!ret) +- return 0; ++#ifdef CONFIG_EPOLL ++ if (make_fd && ret >= 0) ++ ret = make_aio_fd(ioctx); ++#endif ++ if (ret >= 0) ++ return ret; + + get_ioctx(ioctx); /* io_destroy() expects us to hold a ref */ + io_destroy(ioctx); +--- head-2010-05-25.orig/fs/compat_ioctl.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/fs/compat_ioctl.c 2010-05-12 08:57:55.000000000 +0200 +@@ -116,6 +116,13 @@ + #include + #endif + ++#ifdef CONFIG_XEN ++#include ++#include ++#include ++#include ++#endif ++ + static int w_long(unsigned int fd, unsigned int cmd, + compat_ulong_t __user *argp) + { +@@ -1518,6 +1525,19 @@ IGNORE_IOCTL(FBIOGETCMAP32) + IGNORE_IOCTL(FBIOSCURSOR32) + IGNORE_IOCTL(FBIOGCURSOR32) + #endif ++ ++#ifdef CONFIG_XEN ++HANDLE_IOCTL(IOCTL_PRIVCMD_MMAP_32, privcmd_ioctl_32) ++HANDLE_IOCTL(IOCTL_PRIVCMD_MMAPBATCH_32, privcmd_ioctl_32) ++HANDLE_IOCTL(IOCTL_PRIVCMD_MMAPBATCH_V2_32, privcmd_ioctl_32) ++COMPATIBLE_IOCTL(IOCTL_PRIVCMD_HYPERCALL) ++COMPATIBLE_IOCTL(IOCTL_EVTCHN_BIND_VIRQ) ++COMPATIBLE_IOCTL(IOCTL_EVTCHN_BIND_INTERDOMAIN) ++COMPATIBLE_IOCTL(IOCTL_EVTCHN_BIND_UNBOUND_PORT) ++COMPATIBLE_IOCTL(IOCTL_EVTCHN_UNBIND) ++COMPATIBLE_IOCTL(IOCTL_EVTCHN_NOTIFY) ++COMPATIBLE_IOCTL(IOCTL_EVTCHN_RESET) ++#endif + }; + + /* +--- head-2010-05-25.orig/include/acpi/processor.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/acpi/processor.h 2010-03-24 14:53:41.000000000 +0100 +@@ -17,6 +17,12 @@ + #define ACPI_PROCESSOR_MAX_THROTTLE 250 /* 25% */ + #define ACPI_PROCESSOR_MAX_DUTY_WIDTH 4 + ++#ifdef CONFIG_XEN ++#define NR_ACPI_CPUS (NR_CPUS < 256 ? 256 : NR_CPUS) ++#else ++#define NR_ACPI_CPUS NR_CPUS ++#endif /* CONFIG_XEN */ ++ + #define ACPI_PDC_REVISION_ID 0x1 + + #define ACPI_PSD_REV0_REVISION 0 /* Support for _PSD as in ACPI 3.0 */ +@@ -42,6 +48,17 @@ + + struct acpi_processor_cx; + ++#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL ++struct acpi_csd_package { ++ acpi_integer num_entries; ++ acpi_integer revision; ++ acpi_integer domain; ++ acpi_integer coord_type; ++ acpi_integer num_processors; ++ acpi_integer index; ++} __attribute__ ((packed)); ++#endif ++ + struct acpi_power_register { + u8 descriptor; + u16 length; +@@ -74,6 +91,12 @@ struct acpi_processor_cx { + u32 power; + u32 usage; + u64 time; ++#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL ++ /* Require raw information for external control logic */ ++ struct acpi_power_register reg; ++ u32 csd_count; ++ struct acpi_csd_package *domain_info; ++#endif + struct acpi_processor_cx_policy promotion; + struct acpi_processor_cx_policy demotion; + char desc[ACPI_CX_DESC_LEN]; +@@ -300,6 +323,9 @@ static inline void acpi_processor_ppc_ex + { + return; + } ++#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL ++int acpi_processor_ppc_has_changed(struct acpi_processor *pr); ++#else + static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr, + int event_flag) + { +@@ -313,6 +339,7 @@ static inline int acpi_processor_ppc_has + } + return 0; + } ++#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ + static inline int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) + { + return -ENODEV; +@@ -366,4 +393,120 @@ static inline void acpi_thermal_cpufreq_ + } + #endif + ++/* ++ * Following are interfaces geared to external processor PM control ++ * logic like a VMM ++ */ ++/* Events notified to external control logic */ ++#define PROCESSOR_PM_INIT 1 ++#define PROCESSOR_PM_CHANGE 2 ++#define PROCESSOR_HOTPLUG 3 ++ ++/* Objects for the PM events */ ++#define PM_TYPE_IDLE 0 ++#define PM_TYPE_PERF 1 ++#define PM_TYPE_THR 2 ++#define PM_TYPE_MAX 3 ++ ++/* Processor hotplug events */ ++#define HOTPLUG_TYPE_ADD 0 ++#define HOTPLUG_TYPE_REMOVE 1 ++ ++#ifdef CONFIG_PROCESSOR_EXTERNAL_CONTROL ++struct processor_extcntl_ops { ++ /* Transfer processor PM events to external control logic */ ++ int (*pm_ops[PM_TYPE_MAX])(struct acpi_processor *pr, int event); ++ /* Notify physical processor status to external control logic */ ++ int (*hotplug)(struct acpi_processor *pr, int type); ++}; ++extern const struct processor_extcntl_ops *processor_extcntl_ops; ++ ++static inline int processor_cntl_external(void) ++{ ++ return (processor_extcntl_ops != NULL); ++} ++ ++static inline int processor_pm_external(void) ++{ ++ return processor_cntl_external() && ++ (processor_extcntl_ops->pm_ops[PM_TYPE_IDLE] != NULL); ++} ++ ++static inline int processor_pmperf_external(void) ++{ ++ return processor_cntl_external() && ++ (processor_extcntl_ops->pm_ops[PM_TYPE_PERF] != NULL); ++} ++ ++static inline int processor_pmthr_external(void) ++{ ++ return processor_cntl_external() && ++ (processor_extcntl_ops->pm_ops[PM_TYPE_THR] != NULL); ++} ++ ++extern int processor_notify_external(struct acpi_processor *pr, ++ int event, int type); ++extern void processor_extcntl_init(void); ++extern int processor_extcntl_prepare(struct acpi_processor *pr); ++extern int acpi_processor_get_performance_info(struct acpi_processor *pr); ++extern int acpi_processor_get_psd(struct acpi_processor *pr); ++void arch_acpi_processor_init_extcntl(const struct processor_extcntl_ops **); ++#else ++static inline int processor_cntl_external(void) {return 0;} ++static inline int processor_pm_external(void) {return 0;} ++static inline int processor_pmperf_external(void) {return 0;} ++static inline int processor_pmthr_external(void) {return 0;} ++static inline int processor_notify_external(struct acpi_processor *pr, ++ int event, int type) ++{ ++ return 0; ++} ++static inline void processor_extcntl_init(void) {} ++static inline int processor_extcntl_prepare(struct acpi_processor *pr) ++{ ++ return 0; ++} ++#endif /* CONFIG_PROCESSOR_EXTERNAL_CONTROL */ ++ ++#ifdef CONFIG_XEN ++static inline void xen_convert_pct_reg(struct xen_pct_register *xpct, ++ struct acpi_pct_register *apct) ++{ ++ xpct->descriptor = apct->descriptor; ++ xpct->length = apct->length; ++ xpct->space_id = apct->space_id; ++ xpct->bit_width = apct->bit_width; ++ xpct->bit_offset = apct->bit_offset; ++ xpct->reserved = apct->reserved; ++ xpct->address = apct->address; ++} ++ ++static inline void xen_convert_pss_states(struct xen_processor_px *xpss, ++ struct acpi_processor_px *apss, int state_count) ++{ ++ int i; ++ for(i=0; icore_frequency = apss->core_frequency; ++ xpss->power = apss->power; ++ xpss->transition_latency = apss->transition_latency; ++ xpss->bus_master_latency = apss->bus_master_latency; ++ xpss->control = apss->control; ++ xpss->status = apss->status; ++ xpss++; ++ apss++; ++ } ++} ++ ++static inline void xen_convert_psd_pack(struct xen_psd_package *xpsd, ++ struct acpi_psd_package *apsd) ++{ ++ xpsd->num_entries = apsd->num_entries; ++ xpsd->revision = apsd->revision; ++ xpsd->domain = apsd->domain; ++ xpsd->coord_type = apsd->coord_type; ++ xpsd->num_processors = apsd->num_processors; ++} ++ ++#endif /* CONFIG_XEN */ ++ + #endif +--- head-2010-05-25.orig/include/asm-generic/pgtable.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/asm-generic/pgtable.h 2010-03-24 14:53:41.000000000 +0100 +@@ -99,6 +99,10 @@ static inline void ptep_set_wrprotect(st + } + #endif + ++#ifndef arch_change_pte_range ++#define arch_change_pte_range(mm, pmd, addr, end, newprot) 0 ++#endif ++ + #ifndef __HAVE_ARCH_PTE_SAME + #define pte_same(A,B) (pte_val(A) == pte_val(B)) + #endif +--- head-2010-05-25.orig/include/linux/aio.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/aio.h 2010-03-24 14:53:41.000000000 +0100 +@@ -199,6 +199,12 @@ struct kioctx { + + struct delayed_work wq; + ++#ifdef CONFIG_EPOLL ++ /* poll integration */ ++ wait_queue_head_t poll_wait; ++ struct file *file; ++#endif ++ + struct rcu_head rcu_head; + }; + +--- head-2010-05-25.orig/include/linux/highmem.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/highmem.h 2010-03-24 14:53:41.000000000 +0100 +@@ -136,12 +136,14 @@ alloc_zeroed_user_highpage_movable(struc + return __alloc_zeroed_user_highpage(__GFP_MOVABLE, vma, vaddr); + } + ++#ifndef __HAVE_ARCH_CLEAR_HIGHPAGE + static inline void clear_highpage(struct page *page) + { + void *kaddr = kmap_atomic(page, KM_USER0); + clear_page(kaddr); + kunmap_atomic(kaddr, KM_USER0); + } ++#endif + + static inline void zero_user_segments(struct page *page, + unsigned start1, unsigned end1, +@@ -195,6 +197,8 @@ static inline void copy_user_highpage(st + + #endif + ++#ifndef __HAVE_ARCH_COPY_HIGHPAGE ++ + static inline void copy_highpage(struct page *to, struct page *from) + { + char *vfrom, *vto; +@@ -206,4 +210,6 @@ static inline void copy_highpage(struct + kunmap_atomic(vto, KM_USER1); + } + ++#endif ++ + #endif /* _LINUX_HIGHMEM_H */ +--- head-2010-05-25.orig/include/linux/interrupt.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/interrupt.h 2010-03-24 14:53:41.000000000 +0100 +@@ -317,6 +317,12 @@ static inline int disable_irq_wake(unsig + } + #endif /* CONFIG_GENERIC_HARDIRQS */ + ++#ifdef CONFIG_HAVE_IRQ_IGNORE_UNHANDLED ++int irq_ignore_unhandled(unsigned int irq); ++#else ++#define irq_ignore_unhandled(irq) 0 ++#endif ++ + #ifndef __ARCH_SET_SOFTIRQ_PENDING + #define set_softirq_pending(x) (local_softirq_pending() = (x)) + #define or_softirq_pending(x) (local_softirq_pending() |= (x)) +--- head-2010-05-25.orig/include/linux/kexec.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/kexec.h 2010-03-24 14:53:41.000000000 +0100 +@@ -46,6 +46,13 @@ + KEXEC_CORE_NOTE_NAME_BYTES + \ + KEXEC_CORE_NOTE_DESC_BYTES ) + ++#ifndef KEXEC_ARCH_HAS_PAGE_MACROS ++#define kexec_page_to_pfn(page) page_to_pfn(page) ++#define kexec_pfn_to_page(pfn) pfn_to_page(pfn) ++#define kexec_virt_to_phys(addr) virt_to_phys(addr) ++#define kexec_phys_to_virt(addr) phys_to_virt(addr) ++#endif ++ + /* + * This structure is used to hold the arguments that are used when loading + * kernel binaries. +@@ -112,6 +119,12 @@ struct kimage { + extern void machine_kexec(struct kimage *image); + extern int machine_kexec_prepare(struct kimage *image); + extern void machine_kexec_cleanup(struct kimage *image); ++#ifdef CONFIG_XEN ++extern int xen_machine_kexec_load(struct kimage *image); ++extern void xen_machine_kexec_unload(struct kimage *image); ++extern void xen_machine_kexec_setup_resources(void); ++extern void xen_machine_kexec_register_resources(struct resource *res); ++#endif + extern asmlinkage long sys_kexec_load(unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments, +--- head-2010-05-25.orig/include/linux/mm.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/mm.h 2010-03-24 14:53:41.000000000 +0100 +@@ -103,7 +103,12 @@ extern unsigned int kobjsize(const void + + #define VM_CAN_NONLINEAR 0x08000000 /* Has ->fault & does nonlinear pages */ + #define VM_MIXEDMAP 0x10000000 /* Can contain "struct page" and pure PFN pages */ ++#ifndef CONFIG_XEN + #define VM_SAO 0x20000000 /* Strong Access Ordering (powerpc) */ ++#else ++#define VM_SAO 0 ++#define VM_FOREIGN 0x20000000 /* Has pages belonging to another VM */ ++#endif + #define VM_PFN_AT_MMAP 0x40000000 /* PFNMAP vma that is fully mapped at mmap time */ + #define VM_MERGEABLE 0x80000000 /* KSM may merge identical pages */ + +@@ -128,6 +133,12 @@ extern unsigned int kobjsize(const void + */ + #define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_RESERVED | VM_PFNMAP) + ++#ifdef CONFIG_XEN ++struct vm_foreign_map { ++ struct page **map; ++}; ++#endif ++ + /* + * mapping from the currently active vm_flags protection bits (the + * low four bits) to a page protection mask.. +@@ -199,6 +210,15 @@ struct vm_operations_struct { + */ + int (*access)(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write); ++ ++ /* Area-specific function for clearing the PTE at @ptep. Returns the ++ * original value of @ptep. */ ++ pte_t (*zap_pte)(struct vm_area_struct *vma, ++ unsigned long addr, pte_t *ptep, int is_fullmm); ++ ++ /* called before close() to indicate no more pages should be mapped */ ++ void (*unmap)(struct vm_area_struct *area); ++ + #ifdef CONFIG_NUMA + /* + * set_policy() op must add a reference to any non-NULL @new mempolicy +--- head-2010-05-25.orig/include/linux/oprofile.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/oprofile.h 2010-03-24 14:53:41.000000000 +0100 +@@ -16,6 +16,8 @@ + #include + #include + #include ++ ++#include + + /* Each escaped entry is prefixed by ESCAPE_CODE + * then one of the following codes, then the +@@ -28,14 +30,18 @@ + #define CPU_SWITCH_CODE 2 + #define COOKIE_SWITCH_CODE 3 + #define KERNEL_ENTER_SWITCH_CODE 4 +-#define KERNEL_EXIT_SWITCH_CODE 5 ++#define USER_ENTER_SWITCH_CODE 5 + #define MODULE_LOADED_CODE 6 + #define CTX_TGID_CODE 7 + #define TRACE_BEGIN_CODE 8 + #define TRACE_END_CODE 9 + #define XEN_ENTER_SWITCH_CODE 10 ++#ifndef CONFIG_XEN + #define SPU_PROFILING_CODE 11 + #define SPU_CTX_SWITCH_CODE 12 ++#else ++#define DOMAIN_SWITCH_CODE 11 ++#endif + #define IBS_FETCH_CODE 13 + #define IBS_OP_CODE 14 + +@@ -49,6 +55,11 @@ struct oprofile_operations { + /* create any necessary configuration files in the oprofile fs. + * Optional. */ + int (*create_files)(struct super_block * sb, struct dentry * root); ++ /* setup active domains with Xen */ ++ int (*set_active)(int *active_domains, unsigned int adomains); ++ /* setup passive domains with Xen */ ++ int (*set_passive)(int *passive_domains, unsigned int pdomains); ++ + /* Do any necessary interrupt setup. Optional. */ + int (*setup)(void); + /* Do any necessary interrupt shutdown. Optional. */ +@@ -110,6 +121,9 @@ void oprofile_add_pc(unsigned long pc, i + /* add a backtrace entry, to be called from the ->backtrace callback */ + void oprofile_add_trace(unsigned long eip); + ++/* add a domain switch entry */ ++int oprofile_add_domain_switch(int32_t domain_id); ++ + + /** + * Create a file of the given name as a child of the given root, with +--- head-2010-05-25.orig/include/linux/page-flags.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/page-flags.h 2010-03-24 14:53:41.000000000 +0100 +@@ -109,6 +109,11 @@ enum pageflags { + #ifdef CONFIG_MEMORY_FAILURE + PG_hwpoison, /* hardware poisoned page. Don't touch */ + #endif ++#ifdef CONFIG_XEN ++ PG_foreign, /* Page is owned by foreign allocator. */ ++ PG_netback, /* Page is owned by netback */ ++ PG_blkback, /* Page is owned by blkback */ ++#endif + __NR_PAGEFLAGS, + + /* Filesystems */ +@@ -337,6 +342,27 @@ static inline void SetPageUptodate(struc + + CLEARPAGEFLAG(Uptodate, uptodate) + ++#define PageForeign(page) test_bit(PG_foreign, &(page)->flags) ++#define SetPageForeign(_page, dtor) do { \ ++ set_bit(PG_foreign, &(_page)->flags); \ ++ BUG_ON((dtor) == (void (*)(struct page *, unsigned int))0); \ ++ (_page)->index = (long)(dtor); \ ++} while (0) ++#define ClearPageForeign(page) do { \ ++ clear_bit(PG_foreign, &(page)->flags); \ ++ (page)->index = 0; \ ++} while (0) ++#define PageForeignDestructor(_page, order) \ ++ ((void (*)(struct page *, unsigned int))(_page)->index)(_page, order) ++ ++#define PageNetback(page) test_bit(PG_netback, &(page)->flags) ++#define SetPageNetback(page) set_bit(PG_netback, &(page)->flags) ++#define ClearPageNetback(page) clear_bit(PG_netback, &(page)->flags) ++ ++#define PageBlkback(page) test_bit(PG_blkback, &(page)->flags) ++#define SetPageBlkback(page) set_bit(PG_blkback, &(page)->flags) ++#define ClearPageBlkback(page) clear_bit(PG_blkback, &(page)->flags) ++ + extern void cancel_dirty_page(struct page *page, unsigned int account_size); + + int test_clear_page_writeback(struct page *page); +@@ -413,6 +439,14 @@ PAGEFLAG_FALSE(MemError) + #define __PG_MLOCKED 0 + #endif + ++#if !defined(CONFIG_XEN) ++# define __PG_XEN 0 ++#elif defined(CONFIG_X86) ++# define __PG_XEN ((1 << PG_pinned) | (1 << PG_foreign)) ++#else ++# define __PG_XEN (1 << PG_foreign) ++#endif ++ + /* + * Flags checked when a page is freed. Pages being freed should not have + * these flags set. It they are, there is a problem. +@@ -422,7 +456,7 @@ PAGEFLAG_FALSE(MemError) + 1 << PG_private | 1 << PG_private_2 | \ + 1 << PG_buddy | 1 << PG_writeback | 1 << PG_reserved | \ + 1 << PG_slab | 1 << PG_swapcache | 1 << PG_active | \ +- 1 << PG_unevictable | __PG_MLOCKED | __PG_HWPOISON) ++ 1 << PG_unevictable | __PG_MLOCKED | __PG_HWPOISON | __PG_XEN) + + /* + * Flags checked when a page is prepped for return by the page allocator. +--- head-2010-05-25.orig/include/linux/pci.h 2010-03-24 13:55:21.000000000 +0100 ++++ head-2010-05-25/include/linux/pci.h 2010-03-24 14:53:41.000000000 +0100 +@@ -962,6 +962,11 @@ static inline int pci_msi_enabled(void) + { + return 0; + } ++ ++#ifdef CONFIG_XEN ++#define register_msi_get_owner(func) 0 ++#define unregister_msi_get_owner(func) 0 ++#endif + #else + extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); + extern void pci_msi_shutdown(struct pci_dev *dev); +@@ -974,6 +979,10 @@ extern void pci_disable_msix(struct pci_ + extern void msi_remove_pci_irq_vectors(struct pci_dev *dev); + extern void pci_restore_msi_state(struct pci_dev *dev); + extern int pci_msi_enabled(void); ++#ifdef CONFIG_XEN ++extern int register_msi_get_owner(int (*func)(struct pci_dev *dev)); ++extern int unregister_msi_get_owner(int (*func)(struct pci_dev *dev)); ++#endif + #endif + + #ifndef CONFIG_PCIEASPM +--- head-2010-05-25.orig/include/linux/skbuff.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/skbuff.h 2010-04-15 09:43:55.000000000 +0200 +@@ -272,6 +272,8 @@ typedef unsigned char *sk_buff_data_t; + * @local_df: allow local fragmentation + * @cloned: Head may be cloned (check refcnt to be sure) + * @nohdr: Payload reference only, must not modify header ++ * @proto_data_valid: Protocol data validated since arriving at localhost ++ * @proto_csum_blank: Protocol csum must be added before leaving localhost + * @pkt_type: Packet class + * @fclone: skbuff clone status + * @ip_summed: Driver fed us an IP checksum +@@ -377,9 +379,13 @@ struct sk_buff { + #ifdef CONFIG_NETVM + __u8 emergency:1; + #endif ++#ifdef CONFIG_XEN ++ __u8 proto_data_valid:1, ++ proto_csum_blank:1; ++#endif + kmemcheck_bitfield_end(flags2); + +- /* 0/14 bit hole */ ++ /* 0/9...15 bit hole */ + + #ifdef CONFIG_NET_DMA + dma_cookie_t dma_cookie; +--- head-2010-05-25.orig/include/linux/vermagic.h 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/include/linux/vermagic.h 2010-03-24 14:53:41.000000000 +0100 +@@ -22,6 +22,11 @@ + #else + #define MODULE_VERMAGIC_MODVERSIONS "" + #endif ++#ifdef CONFIG_XEN ++#define MODULE_VERMAGIC_XEN "Xen " ++#else ++#define MODULE_VERMAGIC_XEN ++#endif + #ifndef MODULE_ARCH_VERMAGIC + #define MODULE_ARCH_VERMAGIC "" + #endif +@@ -30,5 +35,5 @@ + UTS_RELEASE " " \ + MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT \ + MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS \ +- MODULE_ARCH_VERMAGIC ++ MODULE_VERMAGIC_XEN MODULE_ARCH_VERMAGIC + +--- head-2010-05-25.orig/kernel/irq/spurious.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/kernel/irq/spurious.c 2010-03-24 14:53:41.000000000 +0100 +@@ -233,7 +233,7 @@ void note_interrupt(unsigned int irq, st + */ + if (time_after(jiffies, desc->last_unhandled + HZ/10)) + desc->irqs_unhandled = 1; +- else ++ else if (!irq_ignore_unhandled(irq)) + desc->irqs_unhandled++; + desc->last_unhandled = jiffies; + if (unlikely(action_ret != IRQ_NONE)) +--- head-2010-05-25.orig/kernel/kexec.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/kernel/kexec.c 2010-03-24 14:53:41.000000000 +0100 +@@ -360,13 +360,26 @@ static int kimage_is_destination_range(s + return 0; + } + +-static struct page *kimage_alloc_pages(gfp_t gfp_mask, unsigned int order) ++static struct page *kimage_alloc_pages(gfp_t gfp_mask, unsigned int order, unsigned long limit) + { + struct page *pages; + + pages = alloc_pages(gfp_mask, order); + if (pages) { + unsigned int count, i; ++#ifdef CONFIG_XEN ++ int address_bits; ++ ++ if (limit == ~0UL) ++ address_bits = BITS_PER_LONG; ++ else ++ address_bits = long_log2(limit); ++ ++ if (xen_limit_pages_to_max_mfn(pages, order, address_bits) < 0) { ++ __free_pages(pages, order); ++ return NULL; ++ } ++#endif + pages->mapping = NULL; + set_page_private(pages, order); + count = 1 << order; +@@ -430,10 +443,10 @@ static struct page *kimage_alloc_normal_ + do { + unsigned long pfn, epfn, addr, eaddr; + +- pages = kimage_alloc_pages(GFP_KERNEL, order); ++ pages = kimage_alloc_pages(GFP_KERNEL, order, KEXEC_CONTROL_MEMORY_LIMIT); + if (!pages) + break; +- pfn = page_to_pfn(pages); ++ pfn = kexec_page_to_pfn(pages); + epfn = pfn + count; + addr = pfn << PAGE_SHIFT; + eaddr = epfn << PAGE_SHIFT; +@@ -467,6 +480,7 @@ static struct page *kimage_alloc_normal_ + return pages; + } + ++#ifndef CONFIG_XEN + static struct page *kimage_alloc_crash_control_pages(struct kimage *image, + unsigned int order) + { +@@ -520,7 +534,7 @@ static struct page *kimage_alloc_crash_c + } + /* If I don't overlap any segments I have found my hole! */ + if (i == image->nr_segments) { +- pages = pfn_to_page(hole_start >> PAGE_SHIFT); ++ pages = kexec_pfn_to_page(hole_start >> PAGE_SHIFT); + break; + } + } +@@ -547,6 +561,13 @@ struct page *kimage_alloc_control_pages( + + return pages; + } ++#else /* !CONFIG_XEN */ ++struct page *kimage_alloc_control_pages(struct kimage *image, ++ unsigned int order) ++{ ++ return kimage_alloc_normal_control_pages(image, order); ++} ++#endif + + static int kimage_add_entry(struct kimage *image, kimage_entry_t entry) + { +@@ -562,7 +583,7 @@ static int kimage_add_entry(struct kimag + return -ENOMEM; + + ind_page = page_address(page); +- *image->entry = virt_to_phys(ind_page) | IND_INDIRECTION; ++ *image->entry = kexec_virt_to_phys(ind_page) | IND_INDIRECTION; + image->entry = ind_page; + image->last_entry = ind_page + + ((PAGE_SIZE/sizeof(kimage_entry_t)) - 1); +@@ -621,13 +642,13 @@ static void kimage_terminate(struct kima + #define for_each_kimage_entry(image, ptr, entry) \ + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \ + ptr = (entry & IND_INDIRECTION)? \ +- phys_to_virt((entry & PAGE_MASK)): ptr +1) ++ kexec_phys_to_virt((entry & PAGE_MASK)): ptr +1) + + static void kimage_free_entry(kimage_entry_t entry) + { + struct page *page; + +- page = pfn_to_page(entry >> PAGE_SHIFT); ++ page = kexec_pfn_to_page(entry >> PAGE_SHIFT); + kimage_free_pages(page); + } + +@@ -639,6 +660,10 @@ static void kimage_free(struct kimage *i + if (!image) + return; + ++#ifdef CONFIG_XEN ++ xen_machine_kexec_unload(image); ++#endif ++ + kimage_free_extra_pages(image); + for_each_kimage_entry(image, ptr, entry) { + if (entry & IND_INDIRECTION) { +@@ -714,7 +739,7 @@ static struct page *kimage_alloc_page(st + * have a match. + */ + list_for_each_entry(page, &image->dest_pages, lru) { +- addr = page_to_pfn(page) << PAGE_SHIFT; ++ addr = kexec_page_to_pfn(page) << PAGE_SHIFT; + if (addr == destination) { + list_del(&page->lru); + return page; +@@ -725,16 +750,16 @@ static struct page *kimage_alloc_page(st + kimage_entry_t *old; + + /* Allocate a page, if we run out of memory give up */ +- page = kimage_alloc_pages(gfp_mask, 0); ++ page = kimage_alloc_pages(gfp_mask, 0, KEXEC_SOURCE_MEMORY_LIMIT); + if (!page) + return NULL; + /* If the page cannot be used file it away */ +- if (page_to_pfn(page) > ++ if (kexec_page_to_pfn(page) > + (KEXEC_SOURCE_MEMORY_LIMIT >> PAGE_SHIFT)) { + list_add(&page->lru, &image->unuseable_pages); + continue; + } +- addr = page_to_pfn(page) << PAGE_SHIFT; ++ addr = kexec_page_to_pfn(page) << PAGE_SHIFT; + + /* If it is the destination page we want use it */ + if (addr == destination) +@@ -757,7 +782,7 @@ static struct page *kimage_alloc_page(st + struct page *old_page; + + old_addr = *old & PAGE_MASK; +- old_page = pfn_to_page(old_addr >> PAGE_SHIFT); ++ old_page = kexec_pfn_to_page(old_addr >> PAGE_SHIFT); + copy_highpage(page, old_page); + *old = addr | (*old & ~PAGE_MASK); + +@@ -813,7 +838,7 @@ static int kimage_load_normal_segment(st + result = -ENOMEM; + goto out; + } +- result = kimage_add_page(image, page_to_pfn(page) ++ result = kimage_add_page(image, kexec_page_to_pfn(page) + << PAGE_SHIFT); + if (result < 0) + goto out; +@@ -845,6 +870,7 @@ out: + return result; + } + ++#ifndef CONFIG_XEN + static int kimage_load_crash_segment(struct kimage *image, + struct kexec_segment *segment) + { +@@ -867,7 +893,7 @@ static int kimage_load_crash_segment(str + char *ptr; + size_t uchunk, mchunk; + +- page = pfn_to_page(maddr >> PAGE_SHIFT); ++ page = kexec_pfn_to_page(maddr >> PAGE_SHIFT); + if (!page) { + result = -ENOMEM; + goto out; +@@ -916,6 +942,13 @@ static int kimage_load_segment(struct ki + + return result; + } ++#else /* CONFIG_XEN */ ++static int kimage_load_segment(struct kimage *image, ++ struct kexec_segment *segment) ++{ ++ return kimage_load_normal_segment(image, segment); ++} ++#endif + + /* + * Exec Kernel system call: for obvious reasons only root may call it. +@@ -1019,6 +1052,13 @@ SYSCALL_DEFINE4(kexec_load, unsigned lon + } + kimage_terminate(image); + } ++#ifdef CONFIG_XEN ++ if (image) { ++ result = xen_machine_kexec_load(image); ++ if (result) ++ goto out; ++ } ++#endif + /* Install the new kernel, and Uninstall the old */ + image = xchg(dest_image, image); + +--- head-2010-05-25.orig/kernel/sysctl.c 2010-03-24 14:09:47.000000000 +0100 ++++ head-2010-05-25/kernel/sysctl.c 2010-03-24 14:53:41.000000000 +0100 +@@ -776,7 +776,7 @@ static struct ctl_table kern_table[] = { + .proc_handler = proc_dointvec, + }, + #endif +-#if defined(CONFIG_ACPI_SLEEP) && defined(CONFIG_X86) ++#if defined(CONFIG_ACPI_SLEEP) && defined(CONFIG_X86) && !defined(CONFIG_ACPI_PV_SLEEP) + { + .procname = "acpi_video_flags", + .data = &acpi_realmode_flags, +--- head-2010-05-25.orig/mm/memory.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/mm/memory.c 2010-04-15 09:44:04.000000000 +0200 +@@ -599,6 +599,12 @@ struct page *vm_normal_page(struct vm_ar + { + unsigned long pfn = pte_pfn(pte); + ++#if defined(CONFIG_XEN) && defined(CONFIG_X86) ++ /* XEN: Covers user-space grant mappings (even of local pages). */ ++ if (unlikely(vma->vm_flags & VM_FOREIGN)) ++ return NULL; ++#endif ++ + if (HAVE_PTE_SPECIAL) { + if (likely(!pte_special(pte))) + goto check_pfn; +@@ -630,6 +636,9 @@ struct page *vm_normal_page(struct vm_ar + return NULL; + check_pfn: + if (unlikely(pfn > highest_memmap_pfn)) { ++#ifdef CONFIG_XEN ++ if (!(vma->vm_flags & VM_RESERVED)) ++#endif + print_bad_pte(vma, addr, pte, NULL); + return NULL; + } +@@ -935,8 +944,12 @@ static unsigned long zap_pte_range(struc + page->index > details->last_index)) + continue; + } +- ptent = ptep_get_and_clear_full(mm, addr, pte, +- tlb->fullmm); ++ if (unlikely(vma->vm_ops && vma->vm_ops->zap_pte)) ++ ptent = vma->vm_ops->zap_pte(vma, addr, pte, ++ tlb->fullmm); ++ else ++ ptent = ptep_get_and_clear_full(mm, addr, pte, ++ tlb->fullmm); + tlb_remove_tlb_entry(tlb, pte, addr); + if (unlikely(!page)) + continue; +@@ -1203,6 +1216,7 @@ unsigned long zap_page_range(struct vm_a + tlb_finish_mmu(tlb, address, end); + return end; + } ++EXPORT_SYMBOL(zap_page_range); + + /** + * zap_vma_ptes - remove ptes mapping the vma +@@ -1399,6 +1413,28 @@ int __get_user_pages(struct task_struct + continue; + } + ++#ifdef CONFIG_XEN ++ if (vma && (vma->vm_flags & VM_FOREIGN)) { ++ struct vm_foreign_map *foreign_map = ++ vma->vm_private_data; ++ struct page **map = foreign_map->map; ++ int offset = (start - vma->vm_start) >> PAGE_SHIFT; ++ if (map[offset] != NULL) { ++ if (pages) { ++ struct page *page = map[offset]; ++ ++ pages[i] = page; ++ get_page(page); ++ } ++ if (vmas) ++ vmas[i] = vma; ++ i++; ++ start += PAGE_SIZE; ++ len--; ++ continue; ++ } ++ } ++#endif + if (!vma || + (vma->vm_flags & (VM_IO | VM_PFNMAP)) || + !(vm_flags & vma->vm_flags)) +--- head-2010-05-25.orig/mm/mmap.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/mm/mmap.c 2010-04-29 09:42:36.000000000 +0200 +@@ -1944,6 +1944,12 @@ static void unmap_region(struct mm_struc + tlb_finish_mmu(tlb, start, end); + } + ++static inline void unmap_vma(struct vm_area_struct *vma) ++{ ++ if (unlikely(vma->vm_ops && vma->vm_ops->unmap)) ++ vma->vm_ops->unmap(vma); ++} ++ + /* + * Create a list of vma's touched by the unmap, removing them from the mm's + * vma list as we go.. +@@ -1959,6 +1965,7 @@ detach_vmas_to_be_unmapped(struct mm_str + insertion_point = (prev ? &prev->vm_next : &mm->mmap); + do { + rb_erase(&vma->vm_rb, &mm->mm_rb); ++ unmap_vma(vma); + mm->map_count--; + tail_vma = vma; + vma = vma->vm_next; +@@ -2297,6 +2304,9 @@ void exit_mmap(struct mm_struct *mm) + + arch_exit_mmap(mm); + ++ for (vma = mm->mmap; vma; vma = vma->vm_next) ++ unmap_vma(vma); ++ + vma = mm->mmap; + if (!vma) /* Can happen if dup_mmap() received an OOM */ + return; +--- head-2010-05-25.orig/mm/mprotect.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/mm/mprotect.c 2010-04-15 09:44:14.000000000 +0200 +@@ -90,6 +90,8 @@ static inline void change_pmd_range(stru + next = pmd_addr_end(addr, end); + if (pmd_none_or_clear_bad(pmd)) + continue; ++ if (arch_change_pte_range(mm, pmd, addr, next, newprot)) ++ continue; + change_pte_range(mm, pmd, addr, next, newprot, dirty_accountable); + } while (pmd++, addr = next, addr != end); + } +--- head-2010-05-25.orig/mm/page_alloc.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/mm/page_alloc.c 2010-03-24 14:59:37.000000000 +0100 +@@ -609,6 +609,13 @@ static void __free_pages_ok(struct page + int bad = 0; + int wasMlocked = __TestClearPageMlocked(page); + ++#ifdef CONFIG_XEN ++ if (PageForeign(page)) { ++ PageForeignDestructor(page, order); ++ return; ++ } ++#endif ++ + trace_mm_page_free_direct(page, order); + kmemcheck_free_shadow(page, order); + +@@ -1110,6 +1117,13 @@ void free_hot_cold_page(struct page *pag + int migratetype; + int wasMlocked = __TestClearPageMlocked(page); + ++#ifdef CONFIG_XEN ++ if (PageForeign(page)) { ++ PageForeignDestructor(page, 0); ++ return; ++ } ++#endif ++ + trace_mm_page_free_direct(page, 0); + kmemcheck_free_shadow(page, 0); + +--- head-2010-05-25.orig/net/core/dev.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/net/core/dev.c 2010-05-25 09:19:25.000000000 +0200 +@@ -139,6 +139,12 @@ + /* This should be increased if a protocol with a bigger head is added. */ + #define GRO_MAX_HEAD (MAX_HEADER + 128) + ++#ifdef CONFIG_XEN ++#include ++#include ++#include ++#endif ++ + /* + * The list of packet types we will receive (as opposed to discard) + * and the routines to invoke. +@@ -2005,6 +2011,43 @@ static struct netdev_queue *dev_pick_tx( + return netdev_get_tx_queue(dev, queue_index); + } + ++#ifdef CONFIG_XEN ++inline int skb_checksum_setup(struct sk_buff *skb) ++{ ++ if (skb->proto_csum_blank) { ++ if (skb->protocol != htons(ETH_P_IP)) ++ goto out; ++ skb->h.raw = (unsigned char *)skb->nh.iph + 4*skb->nh.iph->ihl; ++ if (skb->h.raw >= skb->tail) ++ goto out; ++ switch (skb->nh.iph->protocol) { ++ case IPPROTO_TCP: ++ skb->csum = offsetof(struct tcphdr, check); ++ break; ++ case IPPROTO_UDP: ++ skb->csum = offsetof(struct udphdr, check); ++ break; ++ default: ++ if (net_ratelimit()) ++ printk(KERN_ERR "Attempting to checksum a non-" ++ "TCP/UDP packet, dropping a protocol" ++ " %d packet", skb->nh.iph->protocol); ++ goto out; ++ } ++ if ((skb->h.raw + skb->csum + 2) > skb->tail) ++ goto out; ++ skb->ip_summed = CHECKSUM_HW; ++ skb->proto_csum_blank = 0; ++ } ++ return 0; ++out: ++ return -EPROTO; ++} ++#else ++inline int skb_checksum_setup(struct sk_buff *skb) { return 0; } ++#endif ++EXPORT_SYMBOL(skb_checksum_setup); ++ + static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, + struct net_device *dev, + struct netdev_queue *txq) +@@ -2086,6 +2129,12 @@ int dev_queue_xmit(struct sk_buff *skb) + struct Qdisc *q; + int rc = -ENOMEM; + ++ /* If a checksum-deferred packet is forwarded to a device that needs a ++ * checksum, correct the pointers and force checksumming. ++ */ ++ if (skb_checksum_setup(skb)) ++ goto out_kfree_skb; ++ + /* GSO will handle the following emulations directly. */ + if (netif_needs_gso(dev, skb)) + goto gso; +@@ -2574,6 +2623,19 @@ int netif_receive_skb(struct sk_buff *sk + } + #endif + ++#ifdef CONFIG_XEN ++ switch (skb->ip_summed) { ++ case CHECKSUM_UNNECESSARY: ++ skb->proto_data_valid = 1; ++ break; ++ case CHECKSUM_HW: ++ /* XXX Implement me. */ ++ default: ++ skb->proto_data_valid = 0; ++ break; ++ } ++#endif ++ + if (skb_emergency(skb)) + goto skip_taps; + +--- head-2010-05-25.orig/net/core/skbuff.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/net/core/skbuff.c 2010-03-24 14:53:41.000000000 +0100 +@@ -645,6 +645,10 @@ static struct sk_buff *__skb_clone(struc + n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len; + n->cloned = 1; + n->nohdr = 0; ++#ifdef CONFIG_XEN ++ C(proto_data_valid); ++ C(proto_csum_blank); ++#endif + n->destructor = NULL; + C(tail); + C(end); +--- head-2010-05-25.orig/net/ipv4/netfilter/nf_nat_proto_tcp.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/net/ipv4/netfilter/nf_nat_proto_tcp.c 2010-03-24 14:53:41.000000000 +0100 +@@ -75,6 +75,9 @@ tcp_manip_pkt(struct sk_buff *skb, + if (hdrsize < sizeof(*hdr)) + return true; + ++ if (skb_checksum_setup(skb)) ++ return false; ++ + inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); + inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0); + return true; +--- head-2010-05-25.orig/net/ipv4/netfilter/nf_nat_proto_udp.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/net/ipv4/netfilter/nf_nat_proto_udp.c 2010-03-24 14:53:41.000000000 +0100 +@@ -60,6 +60,10 @@ udp_manip_pkt(struct sk_buff *skb, + newport = tuple->dst.u.udp.port; + portptr = &hdr->dest; + } ++ ++ if (skb_checksum_setup(skb)) ++ return false; ++ + if (hdr->check || skb->ip_summed == CHECKSUM_PARTIAL) { + inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); + inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, +--- head-2010-05-25.orig/net/ipv4/xfrm4_output.c 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/net/ipv4/xfrm4_output.c 2010-03-24 14:53:41.000000000 +0100 +@@ -81,7 +81,7 @@ static int xfrm4_output_finish(struct sk + #endif + + skb->protocol = htons(ETH_P_IP); +- return xfrm_output(skb); ++ return skb_checksum_setup(skb) ?: xfrm_output(skb); + } + + int xfrm4_output(struct sk_buff *skb) +--- head-2010-05-25.orig/scripts/Makefile.build 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/scripts/Makefile.build 2010-03-24 14:53:41.000000000 +0100 +@@ -76,6 +76,21 @@ ifndef obj + $(warning kbuild: Makefile.build is included improperly) + endif + ++ifeq ($(CONFIG_XEN),y) ++Makefile.xen := $(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD),$(objtree)/scripts)/Makefile.xen ++$(Makefile.xen): $(srctree)/scripts/Makefile.xen.awk $(srctree)/scripts/Makefile.build ++ @echo ' Updating $@' ++ $(if $(shell echo a | $(AWK) '{ print gensub(/a/, "AA", "g"); }'),\ ++ ,$(error 'Your awk program does not define gensub. Use gawk or another awk with gensub')) ++ @$(AWK) -f $< $(filter-out $<,$^) >$@ ++ ++xen-src-single-used-m := $(patsubst $(srctree)/%,%,$(wildcard $(addprefix $(srctree)/,$(single-used-m:.o=-xen.c)))) ++xen-single-used-m := $(xen-src-single-used-m:-xen.c=.o) ++single-used-m := $(filter-out $(xen-single-used-m),$(single-used-m)) ++ ++-include $(Makefile.xen) ++endif ++ + # =========================================================================== + + ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),) +--- head-2010-05-25.orig/scripts/Makefile.lib 2010-05-25 09:12:09.000000000 +0200 ++++ head-2010-05-25/scripts/Makefile.lib 2010-03-24 14:53:41.000000000 +0100 +@@ -22,6 +22,12 @@ obj-m := $(filter-out $(obj-y),$(obj-m)) + + lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m))) + ++# Remove objects forcibly disabled ++ ++obj-y := $(filter-out $(disabled-obj-y),$(obj-y)) ++obj-m := $(filter-out $(disabled-obj-y),$(obj-m)) ++lib-y := $(filter-out $(disabled-obj-y),$(lib-y)) ++ + + # Handle objects in subdirs + # --------------------------------------------------------------------------- diff --git a/patches.xen/xen3-auto-include-xen-interface.diff b/patches.xen/xen3-auto-include-xen-interface.diff new file mode 100644 index 0000000..b377fae --- /dev/null +++ b/patches.xen/xen3-auto-include-xen-interface.diff @@ -0,0 +1,6268 @@ +Subject: xen3 include-xen-interface +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 1017:948c933f8839) +Patch-mainline: n/a +Acked-by: jbeulich@novell.com + +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/COPYING 2007-06-12 13:14:19.000000000 +0200 +@@ -0,0 +1,38 @@ ++XEN NOTICE ++========== ++ ++This copyright applies to all files within this subdirectory and its ++subdirectories: ++ include/public/*.h ++ include/public/hvm/*.h ++ include/public/io/*.h ++ ++The intention is that these files can be freely copied into the source ++tree of an operating system when porting that OS to run on Xen. Doing ++so does *not* cause the OS to become subject to the terms of the GPL. ++ ++All other files in the Xen source distribution are covered by version ++2 of the GNU General Public License except where explicitly stated ++otherwise within individual source files. ++ ++ -- Keir Fraser (on behalf of the Xen team) ++ ++===================================================================== ++ ++Permission is hereby granted, free of charge, to any person obtaining a copy ++of this software and associated documentation files (the "Software"), to ++deal in the Software without restriction, including without limitation the ++rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++sell copies of the Software, and to permit persons to whom the Software is ++furnished to do so, subject to the following conditions: ++ ++The above copyright notice and this permission notice shall be included in ++all copies or substantial portions of the Software. ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++DEALINGS IN THE SOFTWARE. +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/arch-x86/cpuid.h 2008-01-21 11:15:27.000000000 +0100 +@@ -0,0 +1,68 @@ ++/****************************************************************************** ++ * arch-x86/cpuid.h ++ * ++ * CPUID interface to Xen. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2007 Citrix Systems, Inc. ++ * ++ * Authors: ++ * Keir Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_ARCH_X86_CPUID_H__ ++#define __XEN_PUBLIC_ARCH_X86_CPUID_H__ ++ ++/* Xen identification leaves start at 0x40000000. */ ++#define XEN_CPUID_FIRST_LEAF 0x40000000 ++#define XEN_CPUID_LEAF(i) (XEN_CPUID_FIRST_LEAF + (i)) ++ ++/* ++ * Leaf 1 (0x40000000) ++ * EAX: Largest Xen-information leaf. All leaves up to an including @EAX ++ * are supported by the Xen host. ++ * EBX-EDX: "XenVMMXenVMM" signature, allowing positive identification ++ * of a Xen host. ++ */ ++#define XEN_CPUID_SIGNATURE_EBX 0x566e6558 /* "XenV" */ ++#define XEN_CPUID_SIGNATURE_ECX 0x65584d4d /* "MMXe" */ ++#define XEN_CPUID_SIGNATURE_EDX 0x4d4d566e /* "nVMM" */ ++ ++/* ++ * Leaf 2 (0x40000001) ++ * EAX[31:16]: Xen major version. ++ * EAX[15: 0]: Xen minor version. ++ * EBX-EDX: Reserved (currently all zeroes). ++ */ ++ ++/* ++ * Leaf 3 (0x40000002) ++ * EAX: Number of hypercall transfer pages. This register is always guaranteed ++ * to specify one hypercall page. ++ * EBX: Base address of Xen-specific MSRs. ++ * ECX: Features 1. Unused bits are set to zero. ++ * EDX: Features 2. Unused bits are set to zero. ++ */ ++ ++/* Does the host support MMU_PT_UPDATE_PRESERVE_AD for this guest? */ ++#define _XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD 0 ++#define XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD (1u<<0) ++ ++#endif /* __XEN_PUBLIC_ARCH_X86_CPUID_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/arch-x86/hvm/save.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,439 @@ ++/* ++ * Structure definitions for HVM state that is held by Xen and must ++ * be saved along with the domain's memory and device-model state. ++ * ++ * Copyright (c) 2007 XenSource Ltd. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef __XEN_PUBLIC_HVM_SAVE_X86_H__ ++#define __XEN_PUBLIC_HVM_SAVE_X86_H__ ++ ++/* ++ * Save/restore header: general info about the save file. ++ */ ++ ++#define HVM_FILE_MAGIC 0x54381286 ++#define HVM_FILE_VERSION 0x00000001 ++ ++struct hvm_save_header { ++ uint32_t magic; /* Must be HVM_FILE_MAGIC */ ++ uint32_t version; /* File format version */ ++ uint64_t changeset; /* Version of Xen that saved this file */ ++ uint32_t cpuid; /* CPUID[0x01][%eax] on the saving machine */ ++ uint32_t gtsc_khz; /* Guest's TSC frequency in kHz */ ++}; ++ ++DECLARE_HVM_SAVE_TYPE(HEADER, 1, struct hvm_save_header); ++ ++ ++/* ++ * Processor ++ */ ++ ++struct hvm_hw_cpu { ++ uint8_t fpu_regs[512]; ++ ++ uint64_t rax; ++ uint64_t rbx; ++ uint64_t rcx; ++ uint64_t rdx; ++ uint64_t rbp; ++ uint64_t rsi; ++ uint64_t rdi; ++ uint64_t rsp; ++ uint64_t r8; ++ uint64_t r9; ++ uint64_t r10; ++ uint64_t r11; ++ uint64_t r12; ++ uint64_t r13; ++ uint64_t r14; ++ uint64_t r15; ++ ++ uint64_t rip; ++ uint64_t rflags; ++ ++ uint64_t cr0; ++ uint64_t cr2; ++ uint64_t cr3; ++ uint64_t cr4; ++ ++ uint64_t dr0; ++ uint64_t dr1; ++ uint64_t dr2; ++ uint64_t dr3; ++ uint64_t dr6; ++ uint64_t dr7; ++ ++ uint32_t cs_sel; ++ uint32_t ds_sel; ++ uint32_t es_sel; ++ uint32_t fs_sel; ++ uint32_t gs_sel; ++ uint32_t ss_sel; ++ uint32_t tr_sel; ++ uint32_t ldtr_sel; ++ ++ uint32_t cs_limit; ++ uint32_t ds_limit; ++ uint32_t es_limit; ++ uint32_t fs_limit; ++ uint32_t gs_limit; ++ uint32_t ss_limit; ++ uint32_t tr_limit; ++ uint32_t ldtr_limit; ++ uint32_t idtr_limit; ++ uint32_t gdtr_limit; ++ ++ uint64_t cs_base; ++ uint64_t ds_base; ++ uint64_t es_base; ++ uint64_t fs_base; ++ uint64_t gs_base; ++ uint64_t ss_base; ++ uint64_t tr_base; ++ uint64_t ldtr_base; ++ uint64_t idtr_base; ++ uint64_t gdtr_base; ++ ++ uint32_t cs_arbytes; ++ uint32_t ds_arbytes; ++ uint32_t es_arbytes; ++ uint32_t fs_arbytes; ++ uint32_t gs_arbytes; ++ uint32_t ss_arbytes; ++ uint32_t tr_arbytes; ++ uint32_t ldtr_arbytes; ++ ++ uint64_t sysenter_cs; ++ uint64_t sysenter_esp; ++ uint64_t sysenter_eip; ++ ++ /* msr for em64t */ ++ uint64_t shadow_gs; ++ ++ /* msr content saved/restored. */ ++ uint64_t msr_flags; ++ uint64_t msr_lstar; ++ uint64_t msr_star; ++ uint64_t msr_cstar; ++ uint64_t msr_syscall_mask; ++ uint64_t msr_efer; ++ uint64_t msr_tsc_aux; ++ ++ /* guest's idea of what rdtsc() would return */ ++ uint64_t tsc; ++ ++ /* pending event, if any */ ++ union { ++ uint32_t pending_event; ++ struct { ++ uint8_t pending_vector:8; ++ uint8_t pending_type:3; ++ uint8_t pending_error_valid:1; ++ uint32_t pending_reserved:19; ++ uint8_t pending_valid:1; ++ }; ++ }; ++ /* error code for pending event */ ++ uint32_t error_code; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(CPU, 2, struct hvm_hw_cpu); ++ ++ ++/* ++ * PIC ++ */ ++ ++struct hvm_hw_vpic { ++ /* IR line bitmasks. */ ++ uint8_t irr; ++ uint8_t imr; ++ uint8_t isr; ++ ++ /* Line IRx maps to IRQ irq_base+x */ ++ uint8_t irq_base; ++ ++ /* ++ * Where are we in ICW2-4 initialisation (0 means no init in progress)? ++ * Bits 0-1 (=x): Next write at A=1 sets ICW(x+1). ++ * Bit 2: ICW1.IC4 (1 == ICW4 included in init sequence) ++ * Bit 3: ICW1.SNGL (0 == ICW3 included in init sequence) ++ */ ++ uint8_t init_state:4; ++ ++ /* IR line with highest priority. */ ++ uint8_t priority_add:4; ++ ++ /* Reads from A=0 obtain ISR or IRR? */ ++ uint8_t readsel_isr:1; ++ ++ /* Reads perform a polling read? */ ++ uint8_t poll:1; ++ ++ /* Automatically clear IRQs from the ISR during INTA? */ ++ uint8_t auto_eoi:1; ++ ++ /* Automatically rotate IRQ priorities during AEOI? */ ++ uint8_t rotate_on_auto_eoi:1; ++ ++ /* Exclude slave inputs when considering in-service IRQs? */ ++ uint8_t special_fully_nested_mode:1; ++ ++ /* Special mask mode excludes masked IRs from AEOI and priority checks. */ ++ uint8_t special_mask_mode:1; ++ ++ /* Is this a master PIC or slave PIC? (NB. This is not programmable.) */ ++ uint8_t is_master:1; ++ ++ /* Edge/trigger selection. */ ++ uint8_t elcr; ++ ++ /* Virtual INT output. */ ++ uint8_t int_output; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(PIC, 3, struct hvm_hw_vpic); ++ ++ ++/* ++ * IO-APIC ++ */ ++ ++#ifdef __ia64__ ++#define VIOAPIC_IS_IOSAPIC 1 ++#define VIOAPIC_NUM_PINS 24 ++#else ++#define VIOAPIC_NUM_PINS 48 /* 16 ISA IRQs, 32 non-legacy PCI IRQS. */ ++#endif ++ ++struct hvm_hw_vioapic { ++ uint64_t base_address; ++ uint32_t ioregsel; ++ uint32_t id; ++ union vioapic_redir_entry ++ { ++ uint64_t bits; ++ struct { ++ uint8_t vector; ++ uint8_t delivery_mode:3; ++ uint8_t dest_mode:1; ++ uint8_t delivery_status:1; ++ uint8_t polarity:1; ++ uint8_t remote_irr:1; ++ uint8_t trig_mode:1; ++ uint8_t mask:1; ++ uint8_t reserve:7; ++#if !VIOAPIC_IS_IOSAPIC ++ uint8_t reserved[4]; ++ uint8_t dest_id; ++#else ++ uint8_t reserved[3]; ++ uint16_t dest_id; ++#endif ++ } fields; ++ } redirtbl[VIOAPIC_NUM_PINS]; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(IOAPIC, 4, struct hvm_hw_vioapic); ++ ++ ++/* ++ * LAPIC ++ */ ++ ++struct hvm_hw_lapic { ++ uint64_t apic_base_msr; ++ uint32_t disabled; /* VLAPIC_xx_DISABLED */ ++ uint32_t timer_divisor; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(LAPIC, 5, struct hvm_hw_lapic); ++ ++struct hvm_hw_lapic_regs { ++ uint8_t data[1024]; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(LAPIC_REGS, 6, struct hvm_hw_lapic_regs); ++ ++ ++/* ++ * IRQs ++ */ ++ ++struct hvm_hw_pci_irqs { ++ /* ++ * Virtual interrupt wires for a single PCI bus. ++ * Indexed by: device*4 + INTx#. ++ */ ++ union { ++ unsigned long i[16 / sizeof (unsigned long)]; /* DECLARE_BITMAP(i, 32*4); */ ++ uint64_t pad[2]; ++ }; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(PCI_IRQ, 7, struct hvm_hw_pci_irqs); ++ ++struct hvm_hw_isa_irqs { ++ /* ++ * Virtual interrupt wires for ISA devices. ++ * Indexed by ISA IRQ (assumes no ISA-device IRQ sharing). ++ */ ++ union { ++ unsigned long i[1]; /* DECLARE_BITMAP(i, 16); */ ++ uint64_t pad[1]; ++ }; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(ISA_IRQ, 8, struct hvm_hw_isa_irqs); ++ ++struct hvm_hw_pci_link { ++ /* ++ * PCI-ISA interrupt router. ++ * Each PCI is 'wire-ORed' into one of four links using ++ * the traditional 'barber's pole' mapping ((device + INTx#) & 3). ++ * The router provides a programmable mapping from each link to a GSI. ++ */ ++ uint8_t route[4]; ++ uint8_t pad0[4]; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(PCI_LINK, 9, struct hvm_hw_pci_link); ++ ++/* ++ * PIT ++ */ ++ ++struct hvm_hw_pit { ++ struct hvm_hw_pit_channel { ++ uint32_t count; /* can be 65536 */ ++ uint16_t latched_count; ++ uint8_t count_latched; ++ uint8_t status_latched; ++ uint8_t status; ++ uint8_t read_state; ++ uint8_t write_state; ++ uint8_t write_latch; ++ uint8_t rw_mode; ++ uint8_t mode; ++ uint8_t bcd; /* not supported */ ++ uint8_t gate; /* timer start */ ++ } channels[3]; /* 3 x 16 bytes */ ++ uint32_t speaker_data_on; ++ uint32_t pad0; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(PIT, 10, struct hvm_hw_pit); ++ ++ ++/* ++ * RTC ++ */ ++ ++#define RTC_CMOS_SIZE 14 ++struct hvm_hw_rtc { ++ /* CMOS bytes */ ++ uint8_t cmos_data[RTC_CMOS_SIZE]; ++ /* Index register for 2-part operations */ ++ uint8_t cmos_index; ++ uint8_t pad0; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(RTC, 11, struct hvm_hw_rtc); ++ ++ ++/* ++ * HPET ++ */ ++ ++#define HPET_TIMER_NUM 3 /* 3 timers supported now */ ++struct hvm_hw_hpet { ++ /* Memory-mapped, software visible registers */ ++ uint64_t capability; /* capabilities */ ++ uint64_t res0; /* reserved */ ++ uint64_t config; /* configuration */ ++ uint64_t res1; /* reserved */ ++ uint64_t isr; /* interrupt status reg */ ++ uint64_t res2[25]; /* reserved */ ++ uint64_t mc64; /* main counter */ ++ uint64_t res3; /* reserved */ ++ struct { /* timers */ ++ uint64_t config; /* configuration/cap */ ++ uint64_t cmp; /* comparator */ ++ uint64_t fsb; /* FSB route, not supported now */ ++ uint64_t res4; /* reserved */ ++ } timers[HPET_TIMER_NUM]; ++ uint64_t res5[4*(24-HPET_TIMER_NUM)]; /* reserved, up to 0x3ff */ ++ ++ /* Hidden register state */ ++ uint64_t period[HPET_TIMER_NUM]; /* Last value written to comparator */ ++}; ++ ++DECLARE_HVM_SAVE_TYPE(HPET, 12, struct hvm_hw_hpet); ++ ++ ++/* ++ * PM timer ++ */ ++ ++struct hvm_hw_pmtimer { ++ uint32_t tmr_val; /* PM_TMR_BLK.TMR_VAL: 32bit free-running counter */ ++ uint16_t pm1a_sts; /* PM1a_EVT_BLK.PM1a_STS: status register */ ++ uint16_t pm1a_en; /* PM1a_EVT_BLK.PM1a_EN: enable register */ ++}; ++ ++DECLARE_HVM_SAVE_TYPE(PMTIMER, 13, struct hvm_hw_pmtimer); ++ ++/* ++ * MTRR MSRs ++ */ ++ ++struct hvm_hw_mtrr { ++#define MTRR_VCNT 8 ++#define NUM_FIXED_MSR 11 ++ uint64_t msr_pat_cr; ++ /* mtrr physbase & physmask msr pair*/ ++ uint64_t msr_mtrr_var[MTRR_VCNT*2]; ++ uint64_t msr_mtrr_fixed[NUM_FIXED_MSR]; ++ uint64_t msr_mtrr_cap; ++ uint64_t msr_mtrr_def_type; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(MTRR, 14, struct hvm_hw_mtrr); ++ ++/* ++ * Viridian hypervisor context. ++ */ ++ ++struct hvm_viridian_context { ++ uint64_t hypercall_gpa; ++ uint64_t guest_os_id; ++}; ++ ++DECLARE_HVM_SAVE_TYPE(VIRIDIAN, 15, struct hvm_viridian_context); ++ ++/* ++ * Largest type-code in use ++ */ ++#define HVM_SAVE_CODE_MAX 15 ++ ++#endif /* __XEN_PUBLIC_HVM_SAVE_X86_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/arch-x86/xen-mca.h 2010-05-07 11:10:48.000000000 +0200 +@@ -0,0 +1,423 @@ ++/****************************************************************************** ++ * arch-x86/mca.h ++ * ++ * Contributed by Advanced Micro Devices, Inc. ++ * Author: Christoph Egger ++ * ++ * Guest OS machine check interface to x86 Xen. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++/* Full MCA functionality has the following Usecases from the guest side: ++ * ++ * Must have's: ++ * 1. Dom0 and DomU register machine check trap callback handlers ++ * (already done via "set_trap_table" hypercall) ++ * 2. Dom0 registers machine check event callback handler ++ * (doable via EVTCHNOP_bind_virq) ++ * 3. Dom0 and DomU fetches machine check data ++ * 4. Dom0 wants Xen to notify a DomU ++ * 5. Dom0 gets DomU ID from physical address ++ * 6. Dom0 wants Xen to kill DomU (already done for "xm destroy") ++ * ++ * Nice to have's: ++ * 7. Dom0 wants Xen to deactivate a physical CPU ++ * This is better done as separate task, physical CPU hotplugging, ++ * and hypercall(s) should be sysctl's ++ * 8. Page migration proposed from Xen NUMA work, where Dom0 can tell Xen to ++ * move a DomU (or Dom0 itself) away from a malicious page ++ * producing correctable errors. ++ * 9. offlining physical page: ++ * Xen free's and never re-uses a certain physical page. ++ * 10. Testfacility: Allow Dom0 to write values into machine check MSR's ++ * and tell Xen to trigger a machine check ++ */ ++ ++#ifndef __XEN_PUBLIC_ARCH_X86_MCA_H__ ++#define __XEN_PUBLIC_ARCH_X86_MCA_H__ ++ ++/* Hypercall */ ++#define __HYPERVISOR_mca __HYPERVISOR_arch_0 ++ ++/* ++ * The xen-unstable repo has interface version 0x03000001; out interface ++ * is incompatible with that and any future minor revisions, so we ++ * choose a different version number range that is numerically less ++ * than that used in xen-unstable. ++ */ ++#define XEN_MCA_INTERFACE_VERSION 0x01ecc003 ++ ++/* IN: Dom0 calls hypercall to retrieve nonurgent telemetry */ ++#define XEN_MC_NONURGENT 0x0001 ++/* IN: Dom0/DomU calls hypercall to retrieve urgent telemetry */ ++#define XEN_MC_URGENT 0x0002 ++/* IN: Dom0 acknowledges previosly-fetched telemetry */ ++#define XEN_MC_ACK 0x0004 ++ ++/* OUT: All is ok */ ++#define XEN_MC_OK 0x0 ++/* OUT: Domain could not fetch data. */ ++#define XEN_MC_FETCHFAILED 0x1 ++/* OUT: There was no machine check data to fetch. */ ++#define XEN_MC_NODATA 0x2 ++/* OUT: Between notification time and this hypercall an other ++ * (most likely) correctable error happened. The fetched data, ++ * does not match the original machine check data. */ ++#define XEN_MC_NOMATCH 0x4 ++ ++/* OUT: DomU did not register MC NMI handler. Try something else. */ ++#define XEN_MC_CANNOTHANDLE 0x8 ++/* OUT: Notifying DomU failed. Retry later or try something else. */ ++#define XEN_MC_NOTDELIVERED 0x10 ++/* Note, XEN_MC_CANNOTHANDLE and XEN_MC_NOTDELIVERED are mutually exclusive. */ ++ ++ ++#ifndef __ASSEMBLY__ ++ ++#define VIRQ_MCA VIRQ_ARCH_0 /* G. (DOM0) Machine Check Architecture */ ++ ++/* ++ * Machine Check Architecure: ++ * structs are read-only and used to report all kinds of ++ * correctable and uncorrectable errors detected by the HW. ++ * Dom0 and DomU: register a handler to get notified. ++ * Dom0 only: Correctable errors are reported via VIRQ_MCA ++ * Dom0 and DomU: Uncorrectable errors are reported via nmi handlers ++ */ ++#define MC_TYPE_GLOBAL 0 ++#define MC_TYPE_BANK 1 ++#define MC_TYPE_EXTENDED 2 ++#define MC_TYPE_RECOVERY 3 ++ ++struct mcinfo_common { ++ uint16_t type; /* structure type */ ++ uint16_t size; /* size of this struct in bytes */ ++}; ++ ++ ++#define MC_FLAG_CORRECTABLE (1 << 0) ++#define MC_FLAG_UNCORRECTABLE (1 << 1) ++#define MC_FLAG_RECOVERABLE (1 << 2) ++#define MC_FLAG_POLLED (1 << 3) ++#define MC_FLAG_RESET (1 << 4) ++#define MC_FLAG_CMCI (1 << 5) ++#define MC_FLAG_MCE (1 << 6) ++/* contains global x86 mc information */ ++struct mcinfo_global { ++ struct mcinfo_common common; ++ ++ /* running domain at the time in error (most likely the impacted one) */ ++ uint16_t mc_domid; ++ uint16_t mc_vcpuid; /* virtual cpu scheduled for mc_domid */ ++ uint32_t mc_socketid; /* physical socket of the physical core */ ++ uint16_t mc_coreid; /* physical impacted core */ ++ uint16_t mc_core_threadid; /* core thread of physical core */ ++ uint32_t mc_apicid; ++ uint32_t mc_flags; ++ uint64_t mc_gstatus; /* global status */ ++}; ++ ++/* contains bank local x86 mc information */ ++struct mcinfo_bank { ++ struct mcinfo_common common; ++ ++ uint16_t mc_bank; /* bank nr */ ++ uint16_t mc_domid; /* Usecase 5: domain referenced by mc_addr on dom0 ++ * and if mc_addr is valid. Never valid on DomU. */ ++ uint64_t mc_status; /* bank status */ ++ uint64_t mc_addr; /* bank address, only valid ++ * if addr bit is set in mc_status */ ++ uint64_t mc_misc; ++ uint64_t mc_ctrl2; ++ uint64_t mc_tsc; ++}; ++ ++ ++struct mcinfo_msr { ++ uint64_t reg; /* MSR */ ++ uint64_t value; /* MSR value */ ++}; ++ ++/* contains mc information from other ++ * or additional mc MSRs */ ++struct mcinfo_extended { ++ struct mcinfo_common common; ++ ++ /* You can fill up to five registers. ++ * If you need more, then use this structure ++ * multiple times. */ ++ ++ uint32_t mc_msrs; /* Number of msr with valid values. */ ++ /* ++ * Currently Intel extended MSR (32/64) include all gp registers ++ * and E(R)FLAGS, E(R)IP, E(R)MISC, up to 11/19 of them might be ++ * useful at present. So expand this array to 16/32 to leave room. ++ */ ++ struct mcinfo_msr mc_msr[sizeof(void *) * 4]; ++}; ++ ++/* Recovery Action flags. Giving recovery result information to DOM0 */ ++ ++/* Xen takes successful recovery action, the error is recovered */ ++#define REC_ACTION_RECOVERED (0x1 << 0) ++/* No action is performed by XEN */ ++#define REC_ACTION_NONE (0x1 << 1) ++/* It's possible DOM0 might take action ownership in some case */ ++#define REC_ACTION_NEED_RESET (0x1 << 2) ++ ++/* Different Recovery Action types, if the action is performed successfully, ++ * REC_ACTION_RECOVERED flag will be returned. ++ */ ++ ++/* Page Offline Action */ ++#define MC_ACTION_PAGE_OFFLINE (0x1 << 0) ++/* CPU offline Action */ ++#define MC_ACTION_CPU_OFFLINE (0x1 << 1) ++/* L3 cache disable Action */ ++#define MC_ACTION_CACHE_SHRINK (0x1 << 2) ++ ++/* Below interface used between XEN/DOM0 for passing XEN's recovery action ++ * information to DOM0. ++ * usage Senario: After offlining broken page, XEN might pass its page offline ++ * recovery action result to DOM0. DOM0 will save the information in ++ * non-volatile memory for further proactive actions, such as offlining the ++ * easy broken page earlier when doing next reboot. ++*/ ++struct page_offline_action ++{ ++ /* Params for passing the offlined page number to DOM0 */ ++ uint64_t mfn; ++ uint64_t status; ++}; ++ ++struct cpu_offline_action ++{ ++ /* Params for passing the identity of the offlined CPU to DOM0 */ ++ uint32_t mc_socketid; ++ uint16_t mc_coreid; ++ uint16_t mc_core_threadid; ++}; ++ ++#define MAX_UNION_SIZE 16 ++struct mcinfo_recovery ++{ ++ struct mcinfo_common common; ++ uint16_t mc_bank; /* bank nr */ ++ uint8_t action_flags; ++ uint8_t action_types; ++ union { ++ struct page_offline_action page_retire; ++ struct cpu_offline_action cpu_offline; ++ uint8_t pad[MAX_UNION_SIZE]; ++ } action_info; ++}; ++ ++ ++#define MCINFO_HYPERCALLSIZE 1024 ++#define MCINFO_MAXSIZE 768 ++ ++#define MCINFO_FLAGS_UNCOMPLETE 0x1 ++struct mc_info { ++ /* Number of mcinfo_* entries in mi_data */ ++ uint32_t mi_nentries; ++ uint32_t flags; ++ uint64_t mi_data[(MCINFO_MAXSIZE - 1) / 8]; ++}; ++typedef struct mc_info mc_info_t; ++DEFINE_XEN_GUEST_HANDLE(mc_info_t); ++ ++#define __MC_MSR_ARRAYSIZE 8 ++#define __MC_NMSRS 1 ++#define MC_NCAPS 7 /* 7 CPU feature flag words */ ++#define MC_CAPS_STD_EDX 0 /* cpuid level 0x00000001 (%edx) */ ++#define MC_CAPS_AMD_EDX 1 /* cpuid level 0x80000001 (%edx) */ ++#define MC_CAPS_TM 2 /* cpuid level 0x80860001 (TransMeta) */ ++#define MC_CAPS_LINUX 3 /* Linux-defined */ ++#define MC_CAPS_STD_ECX 4 /* cpuid level 0x00000001 (%ecx) */ ++#define MC_CAPS_VIA 5 /* cpuid level 0xc0000001 */ ++#define MC_CAPS_AMD_ECX 6 /* cpuid level 0x80000001 (%ecx) */ ++ ++struct mcinfo_logical_cpu { ++ uint32_t mc_cpunr; ++ uint32_t mc_chipid; ++ uint16_t mc_coreid; ++ uint16_t mc_threadid; ++ uint32_t mc_apicid; ++ uint32_t mc_clusterid; ++ uint32_t mc_ncores; ++ uint32_t mc_ncores_active; ++ uint32_t mc_nthreads; ++ int32_t mc_cpuid_level; ++ uint32_t mc_family; ++ uint32_t mc_vendor; ++ uint32_t mc_model; ++ uint32_t mc_step; ++ char mc_vendorid[16]; ++ char mc_brandid[64]; ++ uint32_t mc_cpu_caps[MC_NCAPS]; ++ uint32_t mc_cache_size; ++ uint32_t mc_cache_alignment; ++ int32_t mc_nmsrvals; ++ struct mcinfo_msr mc_msrvalues[__MC_MSR_ARRAYSIZE]; ++}; ++typedef struct mcinfo_logical_cpu xen_mc_logical_cpu_t; ++DEFINE_XEN_GUEST_HANDLE(xen_mc_logical_cpu_t); ++ ++ ++/* ++ * OS's should use these instead of writing their own lookup function ++ * each with its own bugs and drawbacks. ++ * We use macros instead of static inline functions to allow guests ++ * to include this header in assembly files (*.S). ++ */ ++/* Prototype: ++ * uint32_t x86_mcinfo_nentries(struct mc_info *mi); ++ */ ++#define x86_mcinfo_nentries(_mi) \ ++ (_mi)->mi_nentries ++/* Prototype: ++ * struct mcinfo_common *x86_mcinfo_first(struct mc_info *mi); ++ */ ++#define x86_mcinfo_first(_mi) \ ++ ((struct mcinfo_common *)(_mi)->mi_data) ++/* Prototype: ++ * struct mcinfo_common *x86_mcinfo_next(struct mcinfo_common *mic); ++ */ ++#define x86_mcinfo_next(_mic) \ ++ ((struct mcinfo_common *)((uint8_t *)(_mic) + (_mic)->size)) ++ ++/* Prototype: ++ * void x86_mcinfo_lookup(void *ret, struct mc_info *mi, uint16_t type); ++ */ ++#define x86_mcinfo_lookup(_ret, _mi, _type) \ ++ do { \ ++ uint32_t found, i; \ ++ struct mcinfo_common *_mic; \ ++ \ ++ found = 0; \ ++ (_ret) = NULL; \ ++ if (_mi == NULL) break; \ ++ _mic = x86_mcinfo_first(_mi); \ ++ for (i = 0; i < x86_mcinfo_nentries(_mi); i++) { \ ++ if (_mic->type == (_type)) { \ ++ found = 1; \ ++ break; \ ++ } \ ++ _mic = x86_mcinfo_next(_mic); \ ++ } \ ++ (_ret) = found ? _mic : NULL; \ ++ } while (0) ++ ++ ++/* Usecase 1 ++ * Register machine check trap callback handler ++ * (already done via "set_trap_table" hypercall) ++ */ ++ ++/* Usecase 2 ++ * Dom0 registers machine check event callback handler ++ * done by EVTCHNOP_bind_virq ++ */ ++ ++/* Usecase 3 ++ * Fetch machine check data from hypervisor. ++ * Note, this hypercall is special, because both Dom0 and DomU must use this. ++ */ ++#define XEN_MC_fetch 1 ++struct xen_mc_fetch { ++ /* IN/OUT variables. */ ++ uint32_t flags; /* IN: XEN_MC_NONURGENT, XEN_MC_URGENT, ++ XEN_MC_ACK if ack'ing an earlier fetch */ ++ /* OUT: XEN_MC_OK, XEN_MC_FETCHFAILED, ++ XEN_MC_NODATA, XEN_MC_NOMATCH */ ++ uint32_t _pad0; ++ uint64_t fetch_id; /* OUT: id for ack, IN: id we are ack'ing */ ++ ++ /* OUT variables. */ ++ XEN_GUEST_HANDLE(mc_info_t) data; ++}; ++typedef struct xen_mc_fetch xen_mc_fetch_t; ++DEFINE_XEN_GUEST_HANDLE(xen_mc_fetch_t); ++ ++ ++/* Usecase 4 ++ * This tells the hypervisor to notify a DomU about the machine check error ++ */ ++#define XEN_MC_notifydomain 2 ++struct xen_mc_notifydomain { ++ /* IN variables. */ ++ uint16_t mc_domid; /* The unprivileged domain to notify. */ ++ uint16_t mc_vcpuid; /* The vcpu in mc_domid to notify. ++ * Usually echo'd value from the fetch hypercall. */ ++ ++ /* IN/OUT variables. */ ++ uint32_t flags; ++ ++/* IN: XEN_MC_CORRECTABLE, XEN_MC_TRAP */ ++/* OUT: XEN_MC_OK, XEN_MC_CANNOTHANDLE, XEN_MC_NOTDELIVERED, XEN_MC_NOMATCH */ ++}; ++typedef struct xen_mc_notifydomain xen_mc_notifydomain_t; ++DEFINE_XEN_GUEST_HANDLE(xen_mc_notifydomain_t); ++ ++#define XEN_MC_physcpuinfo 3 ++struct xen_mc_physcpuinfo { ++ /* IN/OUT */ ++ uint32_t ncpus; ++ uint32_t _pad0; ++ /* OUT */ ++ XEN_GUEST_HANDLE(xen_mc_logical_cpu_t) info; ++}; ++ ++#define XEN_MC_msrinject 4 ++#define MC_MSRINJ_MAXMSRS 8 ++struct xen_mc_msrinject { ++ /* IN */ ++ uint32_t mcinj_cpunr; /* target processor id */ ++ uint32_t mcinj_flags; /* see MC_MSRINJ_F_* below */ ++ uint32_t mcinj_count; /* 0 .. count-1 in array are valid */ ++ uint32_t _pad0; ++ struct mcinfo_msr mcinj_msr[MC_MSRINJ_MAXMSRS]; ++}; ++ ++/* Flags for mcinj_flags above; bits 16-31 are reserved */ ++#define MC_MSRINJ_F_INTERPOSE 0x1 ++ ++#define XEN_MC_mceinject 5 ++struct xen_mc_mceinject { ++ unsigned int mceinj_cpunr; /* target processor id */ ++}; ++ ++struct xen_mc { ++ uint32_t cmd; ++ uint32_t interface_version; /* XEN_MCA_INTERFACE_VERSION */ ++ union { ++ struct xen_mc_fetch mc_fetch; ++ struct xen_mc_notifydomain mc_notifydomain; ++ struct xen_mc_physcpuinfo mc_physcpuinfo; ++ struct xen_mc_msrinject mc_msrinject; ++ struct xen_mc_mceinject mc_mceinject; ++ } u; ++}; ++typedef struct xen_mc xen_mc_t; ++DEFINE_XEN_GUEST_HANDLE(xen_mc_t); ++ ++#endif /* __ASSEMBLY__ */ ++ ++#endif /* __XEN_PUBLIC_ARCH_X86_MCA_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/arch-x86/xen-x86_32.h 2008-07-21 11:00:33.000000000 +0200 +@@ -0,0 +1,180 @@ ++/****************************************************************************** ++ * xen-x86_32.h ++ * ++ * Guest OS interface to x86 32-bit Xen. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2004-2007, K A Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ ++#define __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ ++ ++/* ++ * Hypercall interface: ++ * Input: %ebx, %ecx, %edx, %esi, %edi (arguments 1-5) ++ * Output: %eax ++ * Access is via hypercall page (set up by guest loader or via a Xen MSR): ++ * call hypercall_page + hypercall-number * 32 ++ * Clobbered: Argument registers (e.g., 2-arg hypercall clobbers %ebx,%ecx) ++ */ ++ ++#if __XEN_INTERFACE_VERSION__ < 0x00030203 ++/* ++ * Legacy hypercall interface: ++ * As above, except the entry sequence to the hypervisor is: ++ * mov $hypercall-number*32,%eax ; int $0x82 ++ */ ++#define TRAP_INSTR "int $0x82" ++#endif ++ ++/* ++ * These flat segments are in the Xen-private section of every GDT. Since these ++ * are also present in the initial GDT, many OSes will be able to avoid ++ * installing their own GDT. ++ */ ++#define FLAT_RING1_CS 0xe019 /* GDT index 259 */ ++#define FLAT_RING1_DS 0xe021 /* GDT index 260 */ ++#define FLAT_RING1_SS 0xe021 /* GDT index 260 */ ++#define FLAT_RING3_CS 0xe02b /* GDT index 261 */ ++#define FLAT_RING3_DS 0xe033 /* GDT index 262 */ ++#define FLAT_RING3_SS 0xe033 /* GDT index 262 */ ++ ++#define FLAT_KERNEL_CS FLAT_RING1_CS ++#define FLAT_KERNEL_DS FLAT_RING1_DS ++#define FLAT_KERNEL_SS FLAT_RING1_SS ++#define FLAT_USER_CS FLAT_RING3_CS ++#define FLAT_USER_DS FLAT_RING3_DS ++#define FLAT_USER_SS FLAT_RING3_SS ++ ++#define __HYPERVISOR_VIRT_START_PAE 0xF5800000 ++#define __MACH2PHYS_VIRT_START_PAE 0xF5800000 ++#define __MACH2PHYS_VIRT_END_PAE 0xF6800000 ++#define HYPERVISOR_VIRT_START_PAE \ ++ mk_unsigned_long(__HYPERVISOR_VIRT_START_PAE) ++#define MACH2PHYS_VIRT_START_PAE \ ++ mk_unsigned_long(__MACH2PHYS_VIRT_START_PAE) ++#define MACH2PHYS_VIRT_END_PAE \ ++ mk_unsigned_long(__MACH2PHYS_VIRT_END_PAE) ++ ++/* Non-PAE bounds are obsolete. */ ++#define __HYPERVISOR_VIRT_START_NONPAE 0xFC000000 ++#define __MACH2PHYS_VIRT_START_NONPAE 0xFC000000 ++#define __MACH2PHYS_VIRT_END_NONPAE 0xFC400000 ++#define HYPERVISOR_VIRT_START_NONPAE \ ++ mk_unsigned_long(__HYPERVISOR_VIRT_START_NONPAE) ++#define MACH2PHYS_VIRT_START_NONPAE \ ++ mk_unsigned_long(__MACH2PHYS_VIRT_START_NONPAE) ++#define MACH2PHYS_VIRT_END_NONPAE \ ++ mk_unsigned_long(__MACH2PHYS_VIRT_END_NONPAE) ++ ++#define __HYPERVISOR_VIRT_START __HYPERVISOR_VIRT_START_PAE ++#define __MACH2PHYS_VIRT_START __MACH2PHYS_VIRT_START_PAE ++#define __MACH2PHYS_VIRT_END __MACH2PHYS_VIRT_END_PAE ++ ++#ifndef HYPERVISOR_VIRT_START ++#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START) ++#endif ++ ++#define MACH2PHYS_VIRT_START mk_unsigned_long(__MACH2PHYS_VIRT_START) ++#define MACH2PHYS_VIRT_END mk_unsigned_long(__MACH2PHYS_VIRT_END) ++#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>2) ++#ifndef machine_to_phys_mapping ++#define machine_to_phys_mapping ((unsigned long *)MACH2PHYS_VIRT_START) ++#endif ++ ++/* 32-/64-bit invariability for control interfaces (domctl/sysctl). */ ++#if defined(__XEN__) || defined(__XEN_TOOLS__) ++#undef ___DEFINE_XEN_GUEST_HANDLE ++#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ ++ typedef struct { type *p; } \ ++ __guest_handle_ ## name; \ ++ typedef struct { union { type *p; uint64_aligned_t q; }; } \ ++ __guest_handle_64_ ## name ++#undef set_xen_guest_handle ++#define set_xen_guest_handle(hnd, val) \ ++ do { if ( sizeof(hnd) == 8 ) *(uint64_t *)&(hnd) = 0; \ ++ (hnd).p = val; \ ++ } while ( 0 ) ++#define uint64_aligned_t uint64_t __attribute__((aligned(8))) ++#define __XEN_GUEST_HANDLE_64(name) __guest_handle_64_ ## name ++#define XEN_GUEST_HANDLE_64(name) __XEN_GUEST_HANDLE_64(name) ++#endif ++ ++#ifndef __ASSEMBLY__ ++ ++struct cpu_user_regs { ++ uint32_t ebx; ++ uint32_t ecx; ++ uint32_t edx; ++ uint32_t esi; ++ uint32_t edi; ++ uint32_t ebp; ++ uint32_t eax; ++ uint16_t error_code; /* private */ ++ uint16_t entry_vector; /* private */ ++ uint32_t eip; ++ uint16_t cs; ++ uint8_t saved_upcall_mask; ++ uint8_t _pad0; ++ uint32_t eflags; /* eflags.IF == !saved_upcall_mask */ ++ uint32_t esp; ++ uint16_t ss, _pad1; ++ uint16_t es, _pad2; ++ uint16_t ds, _pad3; ++ uint16_t fs, _pad4; ++ uint16_t gs, _pad5; ++}; ++typedef struct cpu_user_regs cpu_user_regs_t; ++DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t); ++ ++/* ++ * Page-directory addresses above 4GB do not fit into architectural %cr3. ++ * When accessing %cr3, or equivalent field in vcpu_guest_context, guests ++ * must use the following accessor macros to pack/unpack valid MFNs. ++ */ ++#define xen_pfn_to_cr3(pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20)) ++#define xen_cr3_to_pfn(cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20)) ++ ++struct arch_vcpu_info { ++ unsigned long cr2; ++ unsigned long pad[5]; /* sizeof(vcpu_info_t) == 64 */ ++}; ++typedef struct arch_vcpu_info arch_vcpu_info_t; ++ ++struct xen_callback { ++ unsigned long cs; ++ unsigned long eip; ++}; ++typedef struct xen_callback xen_callback_t; ++ ++#endif /* !__ASSEMBLY__ */ ++ ++#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/arch-x86/xen-x86_64.h 2008-04-02 12:34:02.000000000 +0200 +@@ -0,0 +1,212 @@ ++/****************************************************************************** ++ * xen-x86_64.h ++ * ++ * Guest OS interface to x86 64-bit Xen. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2004-2006, K A Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ ++#define __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ ++ ++/* ++ * Hypercall interface: ++ * Input: %rdi, %rsi, %rdx, %r10, %r8 (arguments 1-5) ++ * Output: %rax ++ * Access is via hypercall page (set up by guest loader or via a Xen MSR): ++ * call hypercall_page + hypercall-number * 32 ++ * Clobbered: argument registers (e.g., 2-arg hypercall clobbers %rdi,%rsi) ++ */ ++ ++#if __XEN_INTERFACE_VERSION__ < 0x00030203 ++/* ++ * Legacy hypercall interface: ++ * As above, except the entry sequence to the hypervisor is: ++ * mov $hypercall-number*32,%eax ; syscall ++ * Clobbered: %rcx, %r11, argument registers (as above) ++ */ ++#define TRAP_INSTR "syscall" ++#endif ++ ++/* ++ * 64-bit segment selectors ++ * These flat segments are in the Xen-private section of every GDT. Since these ++ * are also present in the initial GDT, many OSes will be able to avoid ++ * installing their own GDT. ++ */ ++ ++#define FLAT_RING3_CS32 0xe023 /* GDT index 260 */ ++#define FLAT_RING3_CS64 0xe033 /* GDT index 261 */ ++#define FLAT_RING3_DS32 0xe02b /* GDT index 262 */ ++#define FLAT_RING3_DS64 0x0000 /* NULL selector */ ++#define FLAT_RING3_SS32 0xe02b /* GDT index 262 */ ++#define FLAT_RING3_SS64 0xe02b /* GDT index 262 */ ++ ++#define FLAT_KERNEL_DS64 FLAT_RING3_DS64 ++#define FLAT_KERNEL_DS32 FLAT_RING3_DS32 ++#define FLAT_KERNEL_DS FLAT_KERNEL_DS64 ++#define FLAT_KERNEL_CS64 FLAT_RING3_CS64 ++#define FLAT_KERNEL_CS32 FLAT_RING3_CS32 ++#define FLAT_KERNEL_CS FLAT_KERNEL_CS64 ++#define FLAT_KERNEL_SS64 FLAT_RING3_SS64 ++#define FLAT_KERNEL_SS32 FLAT_RING3_SS32 ++#define FLAT_KERNEL_SS FLAT_KERNEL_SS64 ++ ++#define FLAT_USER_DS64 FLAT_RING3_DS64 ++#define FLAT_USER_DS32 FLAT_RING3_DS32 ++#define FLAT_USER_DS FLAT_USER_DS64 ++#define FLAT_USER_CS64 FLAT_RING3_CS64 ++#define FLAT_USER_CS32 FLAT_RING3_CS32 ++#define FLAT_USER_CS FLAT_USER_CS64 ++#define FLAT_USER_SS64 FLAT_RING3_SS64 ++#define FLAT_USER_SS32 FLAT_RING3_SS32 ++#define FLAT_USER_SS FLAT_USER_SS64 ++ ++#define __HYPERVISOR_VIRT_START 0xFFFF800000000000 ++#define __HYPERVISOR_VIRT_END 0xFFFF880000000000 ++#define __MACH2PHYS_VIRT_START 0xFFFF800000000000 ++#define __MACH2PHYS_VIRT_END 0xFFFF804000000000 ++ ++#ifndef HYPERVISOR_VIRT_START ++#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START) ++#define HYPERVISOR_VIRT_END mk_unsigned_long(__HYPERVISOR_VIRT_END) ++#endif ++ ++#define MACH2PHYS_VIRT_START mk_unsigned_long(__MACH2PHYS_VIRT_START) ++#define MACH2PHYS_VIRT_END mk_unsigned_long(__MACH2PHYS_VIRT_END) ++#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>3) ++#ifndef machine_to_phys_mapping ++#define machine_to_phys_mapping ((unsigned long *)HYPERVISOR_VIRT_START) ++#endif ++ ++/* ++ * int HYPERVISOR_set_segment_base(unsigned int which, unsigned long base) ++ * @which == SEGBASE_* ; @base == 64-bit base address ++ * Returns 0 on success. ++ */ ++#define SEGBASE_FS 0 ++#define SEGBASE_GS_USER 1 ++#define SEGBASE_GS_KERNEL 2 ++#define SEGBASE_GS_USER_SEL 3 /* Set user %gs specified in base[15:0] */ ++ ++/* ++ * int HYPERVISOR_iret(void) ++ * All arguments are on the kernel stack, in the following format. ++ * Never returns if successful. Current kernel context is lost. ++ * The saved CS is mapped as follows: ++ * RING0 -> RING3 kernel mode. ++ * RING1 -> RING3 kernel mode. ++ * RING2 -> RING3 kernel mode. ++ * RING3 -> RING3 user mode. ++ * However RING0 indicates that the guest kernel should return to iteself ++ * directly with ++ * orb $3,1*8(%rsp) ++ * iretq ++ * If flags contains VGCF_in_syscall: ++ * Restore RAX, RIP, RFLAGS, RSP. ++ * Discard R11, RCX, CS, SS. ++ * Otherwise: ++ * Restore RAX, R11, RCX, CS:RIP, RFLAGS, SS:RSP. ++ * All other registers are saved on hypercall entry and restored to user. ++ */ ++/* Guest exited in SYSCALL context? Return to guest with SYSRET? */ ++#define _VGCF_in_syscall 8 ++#define VGCF_in_syscall (1<<_VGCF_in_syscall) ++#define VGCF_IN_SYSCALL VGCF_in_syscall ++ ++#ifndef __ASSEMBLY__ ++ ++struct iret_context { ++ /* Top of stack (%rsp at point of hypercall). */ ++ uint64_t rax, r11, rcx, flags, rip, cs, rflags, rsp, ss; ++ /* Bottom of iret stack frame. */ ++}; ++ ++#if defined(__GNUC__) && !defined(__STRICT_ANSI__) ++/* Anonymous union includes both 32- and 64-bit names (e.g., eax/rax). */ ++#define __DECL_REG(name) union { \ ++ uint64_t r ## name, e ## name; \ ++ uint32_t _e ## name; \ ++} ++#else ++/* Non-gcc sources must always use the proper 64-bit name (e.g., rax). */ ++#define __DECL_REG(name) uint64_t r ## name ++#endif ++ ++struct cpu_user_regs { ++ uint64_t r15; ++ uint64_t r14; ++ uint64_t r13; ++ uint64_t r12; ++ __DECL_REG(bp); ++ __DECL_REG(bx); ++ uint64_t r11; ++ uint64_t r10; ++ uint64_t r9; ++ uint64_t r8; ++ __DECL_REG(ax); ++ __DECL_REG(cx); ++ __DECL_REG(dx); ++ __DECL_REG(si); ++ __DECL_REG(di); ++ uint32_t error_code; /* private */ ++ uint32_t entry_vector; /* private */ ++ __DECL_REG(ip); ++ uint16_t cs, _pad0[1]; ++ uint8_t saved_upcall_mask; ++ uint8_t _pad1[3]; ++ __DECL_REG(flags); /* rflags.IF == !saved_upcall_mask */ ++ __DECL_REG(sp); ++ uint16_t ss, _pad2[3]; ++ uint16_t es, _pad3[3]; ++ uint16_t ds, _pad4[3]; ++ uint16_t fs, _pad5[3]; /* Non-zero => takes precedence over fs_base. */ ++ uint16_t gs, _pad6[3]; /* Non-zero => takes precedence over gs_base_usr. */ ++}; ++typedef struct cpu_user_regs cpu_user_regs_t; ++DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t); ++ ++#undef __DECL_REG ++ ++#define xen_pfn_to_cr3(pfn) ((unsigned long)(pfn) << 12) ++#define xen_cr3_to_pfn(cr3) ((unsigned long)(cr3) >> 12) ++ ++struct arch_vcpu_info { ++ unsigned long cr2; ++ unsigned long pad; /* sizeof(vcpu_info_t) == 64 */ ++}; ++typedef struct arch_vcpu_info arch_vcpu_info_t; ++ ++typedef unsigned long xen_callback_t; ++ ++#endif /* !__ASSEMBLY__ */ ++ ++#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/arch-x86/xen.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,200 @@ ++/****************************************************************************** ++ * arch-x86/xen.h ++ * ++ * Guest OS interface to x86 Xen. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2004-2006, K A Fraser ++ */ ++ ++#include "../xen.h" ++ ++#ifndef __XEN_PUBLIC_ARCH_X86_XEN_H__ ++#define __XEN_PUBLIC_ARCH_X86_XEN_H__ ++ ++/* Structural guest handles introduced in 0x00030201. */ ++#if __XEN_INTERFACE_VERSION__ >= 0x00030201 ++#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ ++ typedef struct { type *p; } __guest_handle_ ## name ++#else ++#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ ++ typedef type * __guest_handle_ ## name ++#endif ++ ++#define __DEFINE_XEN_GUEST_HANDLE(name, type) \ ++ ___DEFINE_XEN_GUEST_HANDLE(name, type); \ ++ ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type) ++#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name) ++#define __XEN_GUEST_HANDLE(name) __guest_handle_ ## name ++#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name) ++#define set_xen_guest_handle(hnd, val) do { (hnd).p = val; } while (0) ++#ifdef __XEN_TOOLS__ ++#define get_xen_guest_handle(val, hnd) do { val = (hnd).p; } while (0) ++#endif ++ ++#if defined(__i386__) ++#include "xen-x86_32.h" ++#elif defined(__x86_64__) ++#include "xen-x86_64.h" ++#endif ++ ++#ifndef __ASSEMBLY__ ++typedef unsigned long xen_pfn_t; ++#define PRI_xen_pfn "lx" ++#endif ++ ++/* ++ * SEGMENT DESCRIPTOR TABLES ++ */ ++/* ++ * A number of GDT entries are reserved by Xen. These are not situated at the ++ * start of the GDT because some stupid OSes export hard-coded selector values ++ * in their ABI. These hard-coded values are always near the start of the GDT, ++ * so Xen places itself out of the way, at the far end of the GDT. ++ */ ++#define FIRST_RESERVED_GDT_PAGE 14 ++#define FIRST_RESERVED_GDT_BYTE (FIRST_RESERVED_GDT_PAGE * 4096) ++#define FIRST_RESERVED_GDT_ENTRY (FIRST_RESERVED_GDT_BYTE / 8) ++ ++/* Maximum number of virtual CPUs in legacy multi-processor guests. */ ++#define XEN_LEGACY_MAX_VCPUS 32 ++ ++#ifndef __ASSEMBLY__ ++ ++typedef unsigned long xen_ulong_t; ++ ++/* ++ * Send an array of these to HYPERVISOR_set_trap_table(). ++ * The privilege level specifies which modes may enter a trap via a software ++ * interrupt. On x86/64, since rings 1 and 2 are unavailable, we allocate ++ * privilege levels as follows: ++ * Level == 0: Noone may enter ++ * Level == 1: Kernel may enter ++ * Level == 2: Kernel may enter ++ * Level == 3: Everyone may enter ++ */ ++#define TI_GET_DPL(_ti) ((_ti)->flags & 3) ++#define TI_GET_IF(_ti) ((_ti)->flags & 4) ++#define TI_SET_DPL(_ti,_dpl) ((_ti)->flags |= (_dpl)) ++#define TI_SET_IF(_ti,_if) ((_ti)->flags |= ((!!(_if))<<2)) ++struct trap_info { ++ uint8_t vector; /* exception vector */ ++ uint8_t flags; /* 0-3: privilege level; 4: clear event enable? */ ++ uint16_t cs; /* code selector */ ++ unsigned long address; /* code offset */ ++}; ++typedef struct trap_info trap_info_t; ++DEFINE_XEN_GUEST_HANDLE(trap_info_t); ++ ++typedef uint64_t tsc_timestamp_t; /* RDTSC timestamp */ ++ ++/* ++ * The following is all CPU context. Note that the fpu_ctxt block is filled ++ * in by FXSAVE if the CPU has feature FXSR; otherwise FSAVE is used. ++ */ ++struct vcpu_guest_context { ++ /* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */ ++ struct { char x[512]; } fpu_ctxt; /* User-level FPU registers */ ++#define VGCF_I387_VALID (1<<0) ++#define VGCF_IN_KERNEL (1<<2) ++#define _VGCF_i387_valid 0 ++#define VGCF_i387_valid (1<<_VGCF_i387_valid) ++#define _VGCF_in_kernel 2 ++#define VGCF_in_kernel (1<<_VGCF_in_kernel) ++#define _VGCF_failsafe_disables_events 3 ++#define VGCF_failsafe_disables_events (1<<_VGCF_failsafe_disables_events) ++#define _VGCF_syscall_disables_events 4 ++#define VGCF_syscall_disables_events (1<<_VGCF_syscall_disables_events) ++#define _VGCF_online 5 ++#define VGCF_online (1<<_VGCF_online) ++ unsigned long flags; /* VGCF_* flags */ ++ struct cpu_user_regs user_regs; /* User-level CPU registers */ ++ struct trap_info trap_ctxt[256]; /* Virtual IDT */ ++ unsigned long ldt_base, ldt_ents; /* LDT (linear address, # ents) */ ++ unsigned long gdt_frames[16], gdt_ents; /* GDT (machine frames, # ents) */ ++ unsigned long kernel_ss, kernel_sp; /* Virtual TSS (only SS1/SP1) */ ++ /* NB. User pagetable on x86/64 is placed in ctrlreg[1]. */ ++ unsigned long ctrlreg[8]; /* CR0-CR7 (control registers) */ ++ unsigned long debugreg[8]; /* DB0-DB7 (debug registers) */ ++#ifdef __i386__ ++ unsigned long event_callback_cs; /* CS:EIP of event callback */ ++ unsigned long event_callback_eip; ++ unsigned long failsafe_callback_cs; /* CS:EIP of failsafe callback */ ++ unsigned long failsafe_callback_eip; ++#else ++ unsigned long event_callback_eip; ++ unsigned long failsafe_callback_eip; ++#ifdef __XEN__ ++ union { ++ unsigned long syscall_callback_eip; ++ struct { ++ unsigned int event_callback_cs; /* compat CS of event cb */ ++ unsigned int failsafe_callback_cs; /* compat CS of failsafe cb */ ++ }; ++ }; ++#else ++ unsigned long syscall_callback_eip; ++#endif ++#endif ++ unsigned long vm_assist; /* VMASST_TYPE_* bitmap */ ++#ifdef __x86_64__ ++ /* Segment base addresses. */ ++ uint64_t fs_base; ++ uint64_t gs_base_kernel; ++ uint64_t gs_base_user; ++#endif ++}; ++typedef struct vcpu_guest_context vcpu_guest_context_t; ++DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); ++ ++struct arch_shared_info { ++ unsigned long max_pfn; /* max pfn that appears in table */ ++ /* Frame containing list of mfns containing list of mfns containing p2m. */ ++ xen_pfn_t pfn_to_mfn_frame_list_list; ++ unsigned long nmi_reason; ++ uint64_t pad[32]; ++}; ++typedef struct arch_shared_info arch_shared_info_t; ++ ++#endif /* !__ASSEMBLY__ */ ++ ++/* ++ * Prefix forces emulation of some non-trapping instructions. ++ * Currently only CPUID. ++ */ ++#ifdef __ASSEMBLY__ ++#define XEN_EMULATE_PREFIX .byte 0x0f,0x0b,0x78,0x65,0x6e ; ++#define XEN_CPUID XEN_EMULATE_PREFIX cpuid ++#else ++#define XEN_EMULATE_PREFIX ".byte 0x0f,0x0b,0x78,0x65,0x6e ; " ++#define XEN_CPUID XEN_EMULATE_PREFIX "cpuid" ++#endif ++ ++#endif /* __XEN_PUBLIC_ARCH_X86_XEN_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/arch-x86_32.h 2007-06-12 13:14:19.000000000 +0200 +@@ -0,0 +1,27 @@ ++/****************************************************************************** ++ * arch-x86_32.h ++ * ++ * Guest OS interface to x86 32-bit Xen. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2004-2006, K A Fraser ++ */ ++ ++#include "arch-x86/xen.h" +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/arch-x86_64.h 2007-06-12 13:14:19.000000000 +0200 +@@ -0,0 +1,27 @@ ++/****************************************************************************** ++ * arch-x86_64.h ++ * ++ * Guest OS interface to x86 64-bit Xen. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2004-2006, K A Fraser ++ */ ++ ++#include "arch-x86/xen.h" +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/dom0_ops.h 2007-06-12 13:14:19.000000000 +0200 +@@ -0,0 +1,120 @@ ++/****************************************************************************** ++ * dom0_ops.h ++ * ++ * Process command requests from domain-0 guest OS. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2002-2003, B Dragovic ++ * Copyright (c) 2002-2006, K Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_DOM0_OPS_H__ ++#define __XEN_PUBLIC_DOM0_OPS_H__ ++ ++#include "xen.h" ++#include "platform.h" ++ ++#if __XEN_INTERFACE_VERSION__ >= 0x00030204 ++#error "dom0_ops.h is a compatibility interface only" ++#endif ++ ++#define DOM0_INTERFACE_VERSION XENPF_INTERFACE_VERSION ++ ++#define DOM0_SETTIME XENPF_settime ++#define dom0_settime xenpf_settime ++#define dom0_settime_t xenpf_settime_t ++ ++#define DOM0_ADD_MEMTYPE XENPF_add_memtype ++#define dom0_add_memtype xenpf_add_memtype ++#define dom0_add_memtype_t xenpf_add_memtype_t ++ ++#define DOM0_DEL_MEMTYPE XENPF_del_memtype ++#define dom0_del_memtype xenpf_del_memtype ++#define dom0_del_memtype_t xenpf_del_memtype_t ++ ++#define DOM0_READ_MEMTYPE XENPF_read_memtype ++#define dom0_read_memtype xenpf_read_memtype ++#define dom0_read_memtype_t xenpf_read_memtype_t ++ ++#define DOM0_MICROCODE XENPF_microcode_update ++#define dom0_microcode xenpf_microcode_update ++#define dom0_microcode_t xenpf_microcode_update_t ++ ++#define DOM0_PLATFORM_QUIRK XENPF_platform_quirk ++#define dom0_platform_quirk xenpf_platform_quirk ++#define dom0_platform_quirk_t xenpf_platform_quirk_t ++ ++typedef uint64_t cpumap_t; ++ ++/* Unsupported legacy operation -- defined for API compatibility. */ ++#define DOM0_MSR 15 ++struct dom0_msr { ++ /* IN variables. */ ++ uint32_t write; ++ cpumap_t cpu_mask; ++ uint32_t msr; ++ uint32_t in1; ++ uint32_t in2; ++ /* OUT variables. */ ++ uint32_t out1; ++ uint32_t out2; ++}; ++typedef struct dom0_msr dom0_msr_t; ++DEFINE_XEN_GUEST_HANDLE(dom0_msr_t); ++ ++/* Unsupported legacy operation -- defined for API compatibility. */ ++#define DOM0_PHYSICAL_MEMORY_MAP 40 ++struct dom0_memory_map_entry { ++ uint64_t start, end; ++ uint32_t flags; /* reserved */ ++ uint8_t is_ram; ++}; ++typedef struct dom0_memory_map_entry dom0_memory_map_entry_t; ++DEFINE_XEN_GUEST_HANDLE(dom0_memory_map_entry_t); ++ ++struct dom0_op { ++ uint32_t cmd; ++ uint32_t interface_version; /* DOM0_INTERFACE_VERSION */ ++ union { ++ struct dom0_msr msr; ++ struct dom0_settime settime; ++ struct dom0_add_memtype add_memtype; ++ struct dom0_del_memtype del_memtype; ++ struct dom0_read_memtype read_memtype; ++ struct dom0_microcode microcode; ++ struct dom0_platform_quirk platform_quirk; ++ struct dom0_memory_map_entry physical_memory_map; ++ uint8_t pad[128]; ++ } u; ++}; ++typedef struct dom0_op dom0_op_t; ++DEFINE_XEN_GUEST_HANDLE(dom0_op_t); ++ ++#endif /* __XEN_PUBLIC_DOM0_OPS_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/domctl.h 2010-05-07 11:10:48.000000000 +0200 +@@ -0,0 +1,919 @@ ++/****************************************************************************** ++ * domctl.h ++ * ++ * Domain management operations. For use by node control stack. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2002-2003, B Dragovic ++ * Copyright (c) 2002-2006, K Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_DOMCTL_H__ ++#define __XEN_PUBLIC_DOMCTL_H__ ++ ++#if !defined(__XEN__) && !defined(__XEN_TOOLS__) ++#error "domctl operations are intended for use by node control tools only" ++#endif ++ ++#include "xen.h" ++#include "grant_table.h" ++ ++#define XEN_DOMCTL_INTERFACE_VERSION 0x00000007 ++ ++struct xenctl_cpumap { ++ XEN_GUEST_HANDLE_64(uint8) bitmap; ++ uint32_t nr_cpus; ++}; ++ ++/* ++ * NB. xen_domctl.domain is an IN/OUT parameter for this operation. ++ * If it is specified as zero, an id is auto-allocated and returned. ++ */ ++/* XEN_DOMCTL_createdomain */ ++struct xen_domctl_createdomain { ++ /* IN parameters */ ++ uint32_t ssidref; ++ xen_domain_handle_t handle; ++ /* Is this an HVM guest (as opposed to a PV guest)? */ ++#define _XEN_DOMCTL_CDF_hvm_guest 0 ++#define XEN_DOMCTL_CDF_hvm_guest (1U<<_XEN_DOMCTL_CDF_hvm_guest) ++ /* Use hardware-assisted paging if available? */ ++#define _XEN_DOMCTL_CDF_hap 1 ++#define XEN_DOMCTL_CDF_hap (1U<<_XEN_DOMCTL_CDF_hap) ++ /* Should domain memory integrity be verifed by tboot during Sx? */ ++#define _XEN_DOMCTL_CDF_s3_integrity 2 ++#define XEN_DOMCTL_CDF_s3_integrity (1U<<_XEN_DOMCTL_CDF_s3_integrity) ++ /* Disable out-of-sync shadow page tables? */ ++#define _XEN_DOMCTL_CDF_oos_off 3 ++#define XEN_DOMCTL_CDF_oos_off (1U<<_XEN_DOMCTL_CDF_oos_off) ++ uint32_t flags; ++}; ++typedef struct xen_domctl_createdomain xen_domctl_createdomain_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_createdomain_t); ++ ++/* XEN_DOMCTL_getdomaininfo */ ++struct xen_domctl_getdomaininfo { ++ /* OUT variables. */ ++ domid_t domain; /* Also echoed in domctl.domain */ ++ /* Domain is scheduled to die. */ ++#define _XEN_DOMINF_dying 0 ++#define XEN_DOMINF_dying (1U<<_XEN_DOMINF_dying) ++ /* Domain is an HVM guest (as opposed to a PV guest). */ ++#define _XEN_DOMINF_hvm_guest 1 ++#define XEN_DOMINF_hvm_guest (1U<<_XEN_DOMINF_hvm_guest) ++ /* The guest OS has shut down. */ ++#define _XEN_DOMINF_shutdown 2 ++#define XEN_DOMINF_shutdown (1U<<_XEN_DOMINF_shutdown) ++ /* Currently paused by control software. */ ++#define _XEN_DOMINF_paused 3 ++#define XEN_DOMINF_paused (1U<<_XEN_DOMINF_paused) ++ /* Currently blocked pending an event. */ ++#define _XEN_DOMINF_blocked 4 ++#define XEN_DOMINF_blocked (1U<<_XEN_DOMINF_blocked) ++ /* Domain is currently running. */ ++#define _XEN_DOMINF_running 5 ++#define XEN_DOMINF_running (1U<<_XEN_DOMINF_running) ++ /* Being debugged. */ ++#define _XEN_DOMINF_debugged 6 ++#define XEN_DOMINF_debugged (1U<<_XEN_DOMINF_debugged) ++ /* XEN_DOMINF_shutdown guest-supplied code. */ ++#define XEN_DOMINF_shutdownmask 255 ++#define XEN_DOMINF_shutdownshift 16 ++ uint32_t flags; /* XEN_DOMINF_* */ ++ uint64_aligned_t tot_pages; ++ uint64_aligned_t max_pages; ++ uint64_aligned_t shr_pages; ++ uint64_aligned_t shared_info_frame; /* GMFN of shared_info struct */ ++ uint64_aligned_t cpu_time; ++ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ ++ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ ++ uint32_t ssidref; ++ xen_domain_handle_t handle; ++ uint32_t cpupool; ++}; ++typedef struct xen_domctl_getdomaininfo xen_domctl_getdomaininfo_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_getdomaininfo_t); ++ ++ ++/* XEN_DOMCTL_getmemlist */ ++struct xen_domctl_getmemlist { ++ /* IN variables. */ ++ /* Max entries to write to output buffer. */ ++ uint64_aligned_t max_pfns; ++ /* Start index in guest's page list. */ ++ uint64_aligned_t start_pfn; ++ XEN_GUEST_HANDLE_64(uint64) buffer; ++ /* OUT variables. */ ++ uint64_aligned_t num_pfns; ++}; ++typedef struct xen_domctl_getmemlist xen_domctl_getmemlist_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_getmemlist_t); ++ ++ ++/* XEN_DOMCTL_getpageframeinfo */ ++ ++#define XEN_DOMCTL_PFINFO_LTAB_SHIFT 28 ++#define XEN_DOMCTL_PFINFO_NOTAB (0x0U<<28) ++#define XEN_DOMCTL_PFINFO_L1TAB (0x1U<<28) ++#define XEN_DOMCTL_PFINFO_L2TAB (0x2U<<28) ++#define XEN_DOMCTL_PFINFO_L3TAB (0x3U<<28) ++#define XEN_DOMCTL_PFINFO_L4TAB (0x4U<<28) ++#define XEN_DOMCTL_PFINFO_LTABTYPE_MASK (0x7U<<28) ++#define XEN_DOMCTL_PFINFO_LPINTAB (0x1U<<31) ++#define XEN_DOMCTL_PFINFO_XTAB (0xfU<<28) /* invalid page */ ++#define XEN_DOMCTL_PFINFO_PAGEDTAB (0x8U<<28) ++#define XEN_DOMCTL_PFINFO_LTAB_MASK (0xfU<<28) ++ ++struct xen_domctl_getpageframeinfo { ++ /* IN variables. */ ++ uint64_aligned_t gmfn; /* GMFN to query */ ++ /* OUT variables. */ ++ /* Is the page PINNED to a type? */ ++ uint32_t type; /* see above type defs */ ++}; ++typedef struct xen_domctl_getpageframeinfo xen_domctl_getpageframeinfo_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_getpageframeinfo_t); ++ ++ ++/* XEN_DOMCTL_getpageframeinfo2 */ ++struct xen_domctl_getpageframeinfo2 { ++ /* IN variables. */ ++ uint64_aligned_t num; ++ /* IN/OUT variables. */ ++ XEN_GUEST_HANDLE_64(uint32) array; ++}; ++typedef struct xen_domctl_getpageframeinfo2 xen_domctl_getpageframeinfo2_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_getpageframeinfo2_t); ++ ++/* XEN_DOMCTL_getpageframeinfo3 */ ++struct xen_domctl_getpageframeinfo3 { ++ /* IN variables. */ ++ uint64_aligned_t num; ++ /* IN/OUT variables. */ ++ XEN_GUEST_HANDLE_64(xen_pfn_t) array; ++}; ++ ++ ++/* ++ * Control shadow pagetables operation ++ */ ++/* XEN_DOMCTL_shadow_op */ ++ ++/* Disable shadow mode. */ ++#define XEN_DOMCTL_SHADOW_OP_OFF 0 ++ ++/* Enable shadow mode (mode contains ORed XEN_DOMCTL_SHADOW_ENABLE_* flags). */ ++#define XEN_DOMCTL_SHADOW_OP_ENABLE 32 ++ ++/* Log-dirty bitmap operations. */ ++ /* Return the bitmap and clean internal copy for next round. */ ++#define XEN_DOMCTL_SHADOW_OP_CLEAN 11 ++ /* Return the bitmap but do not modify internal copy. */ ++#define XEN_DOMCTL_SHADOW_OP_PEEK 12 ++ ++/* Memory allocation accessors. */ ++#define XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION 30 ++#define XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION 31 ++ ++/* Legacy enable operations. */ ++ /* Equiv. to ENABLE with no mode flags. */ ++#define XEN_DOMCTL_SHADOW_OP_ENABLE_TEST 1 ++ /* Equiv. to ENABLE with mode flag ENABLE_LOG_DIRTY. */ ++#define XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY 2 ++ /* Equiv. to ENABLE with mode flags ENABLE_REFCOUNT and ENABLE_TRANSLATE. */ ++#define XEN_DOMCTL_SHADOW_OP_ENABLE_TRANSLATE 3 ++ ++/* Mode flags for XEN_DOMCTL_SHADOW_OP_ENABLE. */ ++ /* ++ * Shadow pagetables are refcounted: guest does not use explicit mmu ++ * operations nor write-protect its pagetables. ++ */ ++#define XEN_DOMCTL_SHADOW_ENABLE_REFCOUNT (1 << 1) ++ /* ++ * Log pages in a bitmap as they are dirtied. ++ * Used for live relocation to determine which pages must be re-sent. ++ */ ++#define XEN_DOMCTL_SHADOW_ENABLE_LOG_DIRTY (1 << 2) ++ /* ++ * Automatically translate GPFNs into MFNs. ++ */ ++#define XEN_DOMCTL_SHADOW_ENABLE_TRANSLATE (1 << 3) ++ /* ++ * Xen does not steal virtual address space from the guest. ++ * Requires HVM support. ++ */ ++#define XEN_DOMCTL_SHADOW_ENABLE_EXTERNAL (1 << 4) ++ ++struct xen_domctl_shadow_op_stats { ++ uint32_t fault_count; ++ uint32_t dirty_count; ++}; ++typedef struct xen_domctl_shadow_op_stats xen_domctl_shadow_op_stats_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_shadow_op_stats_t); ++ ++struct xen_domctl_shadow_op { ++ /* IN variables. */ ++ uint32_t op; /* XEN_DOMCTL_SHADOW_OP_* */ ++ ++ /* OP_ENABLE */ ++ uint32_t mode; /* XEN_DOMCTL_SHADOW_ENABLE_* */ ++ ++ /* OP_GET_ALLOCATION / OP_SET_ALLOCATION */ ++ uint32_t mb; /* Shadow memory allocation in MB */ ++ ++ /* OP_PEEK / OP_CLEAN */ ++ XEN_GUEST_HANDLE_64(uint8) dirty_bitmap; ++ uint64_aligned_t pages; /* Size of buffer. Updated with actual size. */ ++ struct xen_domctl_shadow_op_stats stats; ++}; ++typedef struct xen_domctl_shadow_op xen_domctl_shadow_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_shadow_op_t); ++ ++ ++/* XEN_DOMCTL_max_mem */ ++struct xen_domctl_max_mem { ++ /* IN variables. */ ++ uint64_aligned_t max_memkb; ++}; ++typedef struct xen_domctl_max_mem xen_domctl_max_mem_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_max_mem_t); ++ ++ ++/* XEN_DOMCTL_setvcpucontext */ ++/* XEN_DOMCTL_getvcpucontext */ ++struct xen_domctl_vcpucontext { ++ uint32_t vcpu; /* IN */ ++ XEN_GUEST_HANDLE_64(vcpu_guest_context_t) ctxt; /* IN/OUT */ ++}; ++typedef struct xen_domctl_vcpucontext xen_domctl_vcpucontext_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_vcpucontext_t); ++ ++ ++/* XEN_DOMCTL_getvcpuinfo */ ++struct xen_domctl_getvcpuinfo { ++ /* IN variables. */ ++ uint32_t vcpu; ++ /* OUT variables. */ ++ uint8_t online; /* currently online (not hotplugged)? */ ++ uint8_t blocked; /* blocked waiting for an event? */ ++ uint8_t running; /* currently scheduled on its CPU? */ ++ uint64_aligned_t cpu_time; /* total cpu time consumed (ns) */ ++ uint32_t cpu; /* current mapping */ ++}; ++typedef struct xen_domctl_getvcpuinfo xen_domctl_getvcpuinfo_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_getvcpuinfo_t); ++ ++ ++/* Get/set which physical cpus a vcpu can execute on. */ ++/* XEN_DOMCTL_setvcpuaffinity */ ++/* XEN_DOMCTL_getvcpuaffinity */ ++struct xen_domctl_vcpuaffinity { ++ uint32_t vcpu; /* IN */ ++ struct xenctl_cpumap cpumap; /* IN/OUT */ ++}; ++typedef struct xen_domctl_vcpuaffinity xen_domctl_vcpuaffinity_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_vcpuaffinity_t); ++ ++ ++/* XEN_DOMCTL_max_vcpus */ ++struct xen_domctl_max_vcpus { ++ uint32_t max; /* maximum number of vcpus */ ++}; ++typedef struct xen_domctl_max_vcpus xen_domctl_max_vcpus_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_max_vcpus_t); ++ ++ ++/* XEN_DOMCTL_scheduler_op */ ++/* Scheduler types. */ ++#define XEN_SCHEDULER_SEDF 4 ++#define XEN_SCHEDULER_CREDIT 5 ++#define XEN_SCHEDULER_CREDIT2 6 ++/* Set or get info? */ ++#define XEN_DOMCTL_SCHEDOP_putinfo 0 ++#define XEN_DOMCTL_SCHEDOP_getinfo 1 ++struct xen_domctl_scheduler_op { ++ uint32_t sched_id; /* XEN_SCHEDULER_* */ ++ uint32_t cmd; /* XEN_DOMCTL_SCHEDOP_* */ ++ union { ++ struct xen_domctl_sched_sedf { ++ uint64_aligned_t period; ++ uint64_aligned_t slice; ++ uint64_aligned_t latency; ++ uint32_t extratime; ++ uint32_t weight; ++ } sedf; ++ struct xen_domctl_sched_credit { ++ uint16_t weight; ++ uint16_t cap; ++ } credit; ++ struct xen_domctl_sched_credit2 { ++ uint16_t weight; ++ } credit2; ++ } u; ++}; ++typedef struct xen_domctl_scheduler_op xen_domctl_scheduler_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_scheduler_op_t); ++ ++ ++/* XEN_DOMCTL_setdomainhandle */ ++struct xen_domctl_setdomainhandle { ++ xen_domain_handle_t handle; ++}; ++typedef struct xen_domctl_setdomainhandle xen_domctl_setdomainhandle_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_setdomainhandle_t); ++ ++ ++/* XEN_DOMCTL_setdebugging */ ++struct xen_domctl_setdebugging { ++ uint8_t enable; ++}; ++typedef struct xen_domctl_setdebugging xen_domctl_setdebugging_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_setdebugging_t); ++ ++ ++/* XEN_DOMCTL_irq_permission */ ++struct xen_domctl_irq_permission { ++ uint8_t pirq; ++ uint8_t allow_access; /* flag to specify enable/disable of IRQ access */ ++}; ++typedef struct xen_domctl_irq_permission xen_domctl_irq_permission_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_irq_permission_t); ++ ++ ++/* XEN_DOMCTL_iomem_permission */ ++struct xen_domctl_iomem_permission { ++ uint64_aligned_t first_mfn;/* first page (physical page number) in range */ ++ uint64_aligned_t nr_mfns; /* number of pages in range (>0) */ ++ uint8_t allow_access; /* allow (!0) or deny (0) access to range? */ ++}; ++typedef struct xen_domctl_iomem_permission xen_domctl_iomem_permission_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_iomem_permission_t); ++ ++ ++/* XEN_DOMCTL_ioport_permission */ ++struct xen_domctl_ioport_permission { ++ uint32_t first_port; /* first port int range */ ++ uint32_t nr_ports; /* size of port range */ ++ uint8_t allow_access; /* allow or deny access to range? */ ++}; ++typedef struct xen_domctl_ioport_permission xen_domctl_ioport_permission_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_ioport_permission_t); ++ ++ ++/* XEN_DOMCTL_hypercall_init */ ++struct xen_domctl_hypercall_init { ++ uint64_aligned_t gmfn; /* GMFN to be initialised */ ++}; ++typedef struct xen_domctl_hypercall_init xen_domctl_hypercall_init_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_hypercall_init_t); ++ ++ ++/* XEN_DOMCTL_arch_setup */ ++#define _XEN_DOMAINSETUP_hvm_guest 0 ++#define XEN_DOMAINSETUP_hvm_guest (1UL<<_XEN_DOMAINSETUP_hvm_guest) ++#define _XEN_DOMAINSETUP_query 1 /* Get parameters (for save) */ ++#define XEN_DOMAINSETUP_query (1UL<<_XEN_DOMAINSETUP_query) ++#define _XEN_DOMAINSETUP_sioemu_guest 2 ++#define XEN_DOMAINSETUP_sioemu_guest (1UL<<_XEN_DOMAINSETUP_sioemu_guest) ++typedef struct xen_domctl_arch_setup { ++ uint64_aligned_t flags; /* XEN_DOMAINSETUP_* */ ++#ifdef __ia64__ ++ uint64_aligned_t bp; /* mpaddr of boot param area */ ++ uint64_aligned_t maxmem; /* Highest memory address for MDT. */ ++ uint64_aligned_t xsi_va; /* Xen shared_info area virtual address. */ ++ uint32_t hypercall_imm; /* Break imm for Xen hypercalls. */ ++ int8_t vhpt_size_log2; /* Log2 of VHPT size. */ ++#endif ++} xen_domctl_arch_setup_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_arch_setup_t); ++ ++ ++/* XEN_DOMCTL_settimeoffset */ ++struct xen_domctl_settimeoffset { ++ int32_t time_offset_seconds; /* applied to domain wallclock time */ ++}; ++typedef struct xen_domctl_settimeoffset xen_domctl_settimeoffset_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_settimeoffset_t); ++ ++/* XEN_DOMCTL_gethvmcontext */ ++/* XEN_DOMCTL_sethvmcontext */ ++typedef struct xen_domctl_hvmcontext { ++ uint32_t size; /* IN/OUT: size of buffer / bytes filled */ ++ XEN_GUEST_HANDLE_64(uint8) buffer; /* IN/OUT: data, or call ++ * gethvmcontext with NULL ++ * buffer to get size req'd */ ++} xen_domctl_hvmcontext_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_hvmcontext_t); ++ ++ ++/* XEN_DOMCTL_set_address_size */ ++/* XEN_DOMCTL_get_address_size */ ++typedef struct xen_domctl_address_size { ++ uint32_t size; ++} xen_domctl_address_size_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_address_size_t); ++ ++ ++/* XEN_DOMCTL_real_mode_area */ ++struct xen_domctl_real_mode_area { ++ uint32_t log; /* log2 of Real Mode Area size */ ++}; ++typedef struct xen_domctl_real_mode_area xen_domctl_real_mode_area_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_real_mode_area_t); ++ ++ ++/* XEN_DOMCTL_sendtrigger */ ++#define XEN_DOMCTL_SENDTRIGGER_NMI 0 ++#define XEN_DOMCTL_SENDTRIGGER_RESET 1 ++#define XEN_DOMCTL_SENDTRIGGER_INIT 2 ++#define XEN_DOMCTL_SENDTRIGGER_POWER 3 ++#define XEN_DOMCTL_SENDTRIGGER_SLEEP 4 ++struct xen_domctl_sendtrigger { ++ uint32_t trigger; /* IN */ ++ uint32_t vcpu; /* IN */ ++}; ++typedef struct xen_domctl_sendtrigger xen_domctl_sendtrigger_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_sendtrigger_t); ++ ++ ++/* Assign PCI device to HVM guest. Sets up IOMMU structures. */ ++/* XEN_DOMCTL_assign_device */ ++/* XEN_DOMCTL_test_assign_device */ ++/* XEN_DOMCTL_deassign_device */ ++struct xen_domctl_assign_device { ++ uint32_t machine_bdf; /* machine PCI ID of assigned device */ ++}; ++typedef struct xen_domctl_assign_device xen_domctl_assign_device_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_assign_device_t); ++ ++/* Retrieve sibling devices infomation of machine_bdf */ ++/* XEN_DOMCTL_get_device_group */ ++struct xen_domctl_get_device_group { ++ uint32_t machine_bdf; /* IN */ ++ uint32_t max_sdevs; /* IN */ ++ uint32_t num_sdevs; /* OUT */ ++ XEN_GUEST_HANDLE_64(uint32) sdev_array; /* OUT */ ++}; ++typedef struct xen_domctl_get_device_group xen_domctl_get_device_group_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_get_device_group_t); ++ ++/* Pass-through interrupts: bind real irq -> hvm devfn. */ ++/* XEN_DOMCTL_bind_pt_irq */ ++/* XEN_DOMCTL_unbind_pt_irq */ ++typedef enum pt_irq_type_e { ++ PT_IRQ_TYPE_PCI, ++ PT_IRQ_TYPE_ISA, ++ PT_IRQ_TYPE_MSI, ++ PT_IRQ_TYPE_MSI_TRANSLATE, ++} pt_irq_type_t; ++struct xen_domctl_bind_pt_irq { ++ uint32_t machine_irq; ++ pt_irq_type_t irq_type; ++ uint32_t hvm_domid; ++ ++ union { ++ struct { ++ uint8_t isa_irq; ++ } isa; ++ struct { ++ uint8_t bus; ++ uint8_t device; ++ uint8_t intx; ++ } pci; ++ struct { ++ uint8_t gvec; ++ uint32_t gflags; ++ uint64_aligned_t gtable; ++ } msi; ++ } u; ++}; ++typedef struct xen_domctl_bind_pt_irq xen_domctl_bind_pt_irq_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_bind_pt_irq_t); ++ ++ ++/* Bind machine I/O address range -> HVM address range. */ ++/* XEN_DOMCTL_memory_mapping */ ++#define DPCI_ADD_MAPPING 1 ++#define DPCI_REMOVE_MAPPING 0 ++struct xen_domctl_memory_mapping { ++ uint64_aligned_t first_gfn; /* first page (hvm guest phys page) in range */ ++ uint64_aligned_t first_mfn; /* first page (machine page) in range */ ++ uint64_aligned_t nr_mfns; /* number of pages in range (>0) */ ++ uint32_t add_mapping; /* add or remove mapping */ ++ uint32_t padding; /* padding for 64-bit aligned structure */ ++}; ++typedef struct xen_domctl_memory_mapping xen_domctl_memory_mapping_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_memory_mapping_t); ++ ++ ++/* Bind machine I/O port range -> HVM I/O port range. */ ++/* XEN_DOMCTL_ioport_mapping */ ++struct xen_domctl_ioport_mapping { ++ uint32_t first_gport; /* first guest IO port*/ ++ uint32_t first_mport; /* first machine IO port */ ++ uint32_t nr_ports; /* size of port range */ ++ uint32_t add_mapping; /* add or remove mapping */ ++}; ++typedef struct xen_domctl_ioport_mapping xen_domctl_ioport_mapping_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_ioport_mapping_t); ++ ++ ++/* ++ * Pin caching type of RAM space for x86 HVM domU. ++ */ ++/* XEN_DOMCTL_pin_mem_cacheattr */ ++/* Caching types: these happen to be the same as x86 MTRR/PAT type codes. */ ++#define XEN_DOMCTL_MEM_CACHEATTR_UC 0 ++#define XEN_DOMCTL_MEM_CACHEATTR_WC 1 ++#define XEN_DOMCTL_MEM_CACHEATTR_WT 4 ++#define XEN_DOMCTL_MEM_CACHEATTR_WP 5 ++#define XEN_DOMCTL_MEM_CACHEATTR_WB 6 ++#define XEN_DOMCTL_MEM_CACHEATTR_UCM 7 ++struct xen_domctl_pin_mem_cacheattr { ++ uint64_aligned_t start, end; ++ uint32_t type; /* XEN_DOMCTL_MEM_CACHEATTR_* */ ++}; ++typedef struct xen_domctl_pin_mem_cacheattr xen_domctl_pin_mem_cacheattr_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_pin_mem_cacheattr_t); ++ ++ ++/* XEN_DOMCTL_set_ext_vcpucontext */ ++/* XEN_DOMCTL_get_ext_vcpucontext */ ++struct xen_domctl_ext_vcpucontext { ++ /* IN: VCPU that this call applies to. */ ++ uint32_t vcpu; ++ /* ++ * SET: Size of struct (IN) ++ * GET: Size of struct (OUT) ++ */ ++ uint32_t size; ++#if defined(__i386__) || defined(__x86_64__) ++ /* SYSCALL from 32-bit mode and SYSENTER callback information. */ ++ /* NB. SYSCALL from 64-bit mode is contained in vcpu_guest_context_t */ ++ uint64_aligned_t syscall32_callback_eip; ++ uint64_aligned_t sysenter_callback_eip; ++ uint16_t syscall32_callback_cs; ++ uint16_t sysenter_callback_cs; ++ uint8_t syscall32_disables_events; ++ uint8_t sysenter_disables_events; ++#endif ++}; ++typedef struct xen_domctl_ext_vcpucontext xen_domctl_ext_vcpucontext_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_ext_vcpucontext_t); ++ ++/* ++ * Set optimizaton features for a domain ++ */ ++/* XEN_DOMCTL_set_opt_feature */ ++struct xen_domctl_set_opt_feature { ++#if defined(__ia64__) ++ struct xen_ia64_opt_feature optf; ++#else ++ /* Make struct non-empty: do not depend on this field name! */ ++ uint64_t dummy; ++#endif ++}; ++typedef struct xen_domctl_set_opt_feature xen_domctl_set_opt_feature_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_set_opt_feature_t); ++ ++/* ++ * Set the target domain for a domain ++ */ ++/* XEN_DOMCTL_set_target */ ++struct xen_domctl_set_target { ++ domid_t target; ++}; ++typedef struct xen_domctl_set_target xen_domctl_set_target_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_set_target_t); ++ ++#if defined(__i386__) || defined(__x86_64__) ++# define XEN_CPUID_INPUT_UNUSED 0xFFFFFFFF ++/* XEN_DOMCTL_set_cpuid */ ++struct xen_domctl_cpuid { ++ uint32_t input[2]; ++ uint32_t eax; ++ uint32_t ebx; ++ uint32_t ecx; ++ uint32_t edx; ++}; ++typedef struct xen_domctl_cpuid xen_domctl_cpuid_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_cpuid_t); ++#endif ++ ++/* XEN_DOMCTL_subscribe */ ++struct xen_domctl_subscribe { ++ uint32_t port; /* IN */ ++}; ++typedef struct xen_domctl_subscribe xen_domctl_subscribe_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_subscribe_t); ++ ++/* ++ * Define the maximum machine address size which should be allocated ++ * to a guest. ++ */ ++/* XEN_DOMCTL_set_machine_address_size */ ++/* XEN_DOMCTL_get_machine_address_size */ ++ ++/* ++ * Do not inject spurious page faults into this domain. ++ */ ++/* XEN_DOMCTL_suppress_spurious_page_faults */ ++ ++/* XEN_DOMCTL_debug_op */ ++#define XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF 0 ++#define XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON 1 ++struct xen_domctl_debug_op { ++ uint32_t op; /* IN */ ++ uint32_t vcpu; /* IN */ ++}; ++typedef struct xen_domctl_debug_op xen_domctl_debug_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_debug_op_t); ++ ++/* ++ * Request a particular record from the HVM context ++ */ ++/* XEN_DOMCTL_gethvmcontext_partial */ ++typedef struct xen_domctl_hvmcontext_partial { ++ uint32_t type; /* IN: Type of record required */ ++ uint32_t instance; /* IN: Instance of that type */ ++ XEN_GUEST_HANDLE_64(uint8) buffer; /* OUT: buffer to write record into */ ++} xen_domctl_hvmcontext_partial_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_hvmcontext_partial_t); ++ ++/* XEN_DOMCTL_disable_migrate */ ++typedef struct xen_domctl_disable_migrate { ++ uint32_t disable; /* IN: 1: disable migration and restore */ ++} xen_domctl_disable_migrate_t; ++ ++ ++/* XEN_DOMCTL_gettscinfo */ ++/* XEN_DOMCTL_settscinfo */ ++struct xen_guest_tsc_info { ++ uint32_t tsc_mode; ++ uint32_t gtsc_khz; ++ uint32_t incarnation; ++ uint32_t pad; ++ uint64_aligned_t elapsed_nsec; ++}; ++typedef struct xen_guest_tsc_info xen_guest_tsc_info_t; ++DEFINE_XEN_GUEST_HANDLE(xen_guest_tsc_info_t); ++typedef struct xen_domctl_tsc_info { ++ XEN_GUEST_HANDLE_64(xen_guest_tsc_info_t) out_info; /* OUT */ ++ xen_guest_tsc_info_t info; /* IN */ ++} xen_domctl_tsc_info_t; ++ ++/* XEN_DOMCTL_gdbsx_guestmemio guest mem io */ ++struct xen_domctl_gdbsx_memio { ++ /* IN */ ++ uint64_aligned_t pgd3val;/* optional: init_mm.pgd[3] value */ ++ uint64_aligned_t gva; /* guest virtual address */ ++ uint64_aligned_t uva; /* user buffer virtual address */ ++ uint32_t len; /* number of bytes to read/write */ ++ uint8_t gwr; /* 0 = read from guest. 1 = write to guest */ ++ /* OUT */ ++ uint32_t remain; /* bytes remaining to be copied */ ++}; ++ ++/* XEN_DOMCTL_gdbsx_pausevcpu */ ++/* XEN_DOMCTL_gdbsx_unpausevcpu */ ++struct xen_domctl_gdbsx_pauseunp_vcpu { /* pause/unpause a vcpu */ ++ uint32_t vcpu; /* which vcpu */ ++}; ++ ++/* XEN_DOMCTL_gdbsx_domstatus */ ++struct xen_domctl_gdbsx_domstatus { ++ /* OUT */ ++ uint8_t paused; /* is the domain paused */ ++ uint32_t vcpu_id; /* any vcpu in an event? */ ++ uint32_t vcpu_ev; /* if yes, what event? */ ++}; ++ ++/* ++ * Memory event operations ++ */ ++ ++/* XEN_DOMCTL_mem_event_op */ ++ ++/* Add and remove memory handlers */ ++#define XEN_DOMCTL_MEM_EVENT_OP_ENABLE 0 ++#define XEN_DOMCTL_MEM_EVENT_OP_DISABLE 1 ++ ++/* ++ * Page memory in and out. ++ */ ++#define XEN_DOMCTL_MEM_EVENT_OP_PAGING (1 << 0) ++ ++/* Domain memory paging */ ++#define XEN_DOMCTL_MEM_EVENT_OP_PAGING_NOMINATE 0 ++#define XEN_DOMCTL_MEM_EVENT_OP_PAGING_EVICT 1 ++#define XEN_DOMCTL_MEM_EVENT_OP_PAGING_PREP 2 ++#define XEN_DOMCTL_MEM_EVENT_OP_PAGING_RESUME 3 ++ ++struct xen_domctl_mem_event_op { ++ uint32_t op; /* XEN_DOMCTL_MEM_EVENT_OP_* */ ++ uint32_t mode; /* XEN_DOMCTL_MEM_EVENT_ENABLE_* */ ++ ++ /* OP_ENABLE */ ++ uint64_aligned_t shared_addr; /* IN: Virtual address of shared page */ ++ uint64_aligned_t ring_addr; /* IN: Virtual address of ring page */ ++ ++ /* Other OPs */ ++ uint64_aligned_t gfn; /* IN: gfn of page being operated on */ ++}; ++typedef struct xen_domctl_mem_event_op xen_domctl_mem_event_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_mem_event_op_t); ++ ++/* ++ * Memory sharing operations ++ */ ++/* XEN_DOMCTL_mem_sharing_op */ ++ ++#define XEN_DOMCTL_MEM_SHARING_OP_CONTROL 0 ++#define XEN_DOMCTL_MEM_SHARING_OP_NOMINATE_GFN 1 ++#define XEN_DOMCTL_MEM_SHARING_OP_NOMINATE_GREF 2 ++#define XEN_DOMCTL_MEM_SHARING_OP_SHARE 3 ++#define XEN_DOMCTL_MEM_SHARING_OP_RESUME 4 ++#define XEN_DOMCTL_MEM_SHARING_OP_DEBUG_GFN 5 ++#define XEN_DOMCTL_MEM_SHARING_OP_DEBUG_MFN 6 ++#define XEN_DOMCTL_MEM_SHARING_OP_DEBUG_GREF 7 ++ ++#define XEN_DOMCTL_MEM_SHARING_S_HANDLE_INVALID (-10) ++#define XEN_DOMCTL_MEM_SHARING_C_HANDLE_INVALID (-9) ++ ++struct xen_domctl_mem_sharing_op { ++ uint8_t op; /* XEN_DOMCTL_MEM_EVENT_OP_* */ ++ ++ union { ++ uint8_t enable; /* OP_CONTROL */ ++ ++ struct mem_sharing_op_nominate { /* OP_NOMINATE_xxx */ ++ union { ++ uint64_aligned_t gfn; /* IN: gfn to nominate */ ++ uint32_t grant_ref; /* IN: grant ref to nominate */ ++ } u; ++ uint64_aligned_t handle; /* OUT: the handle */ ++ } nominate; ++ struct mem_sharing_op_share { /* OP_SHARE */ ++ uint64_aligned_t source_handle; /* IN: handle to the source page */ ++ uint64_aligned_t client_handle; /* IN: handle to the client page */ ++ } share; ++ struct mem_sharing_op_debug { /* OP_DEBUG_xxx */ ++ union { ++ uint64_aligned_t gfn; /* IN: gfn to debug */ ++ uint64_aligned_t mfn; /* IN: mfn to debug */ ++ grant_ref_t gref; /* IN: gref to debug */ ++ } u; ++ } debug; ++ } u; ++}; ++typedef struct xen_domctl_mem_sharing_op xen_domctl_mem_sharing_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_mem_sharing_op_t); ++ ++struct xen_domctl { ++ uint32_t cmd; ++#define XEN_DOMCTL_createdomain 1 ++#define XEN_DOMCTL_destroydomain 2 ++#define XEN_DOMCTL_pausedomain 3 ++#define XEN_DOMCTL_unpausedomain 4 ++#define XEN_DOMCTL_getdomaininfo 5 ++#define XEN_DOMCTL_getmemlist 6 ++#define XEN_DOMCTL_getpageframeinfo 7 ++#define XEN_DOMCTL_getpageframeinfo2 8 ++#define XEN_DOMCTL_setvcpuaffinity 9 ++#define XEN_DOMCTL_shadow_op 10 ++#define XEN_DOMCTL_max_mem 11 ++#define XEN_DOMCTL_setvcpucontext 12 ++#define XEN_DOMCTL_getvcpucontext 13 ++#define XEN_DOMCTL_getvcpuinfo 14 ++#define XEN_DOMCTL_max_vcpus 15 ++#define XEN_DOMCTL_scheduler_op 16 ++#define XEN_DOMCTL_setdomainhandle 17 ++#define XEN_DOMCTL_setdebugging 18 ++#define XEN_DOMCTL_irq_permission 19 ++#define XEN_DOMCTL_iomem_permission 20 ++#define XEN_DOMCTL_ioport_permission 21 ++#define XEN_DOMCTL_hypercall_init 22 ++#define XEN_DOMCTL_arch_setup 23 ++#define XEN_DOMCTL_settimeoffset 24 ++#define XEN_DOMCTL_getvcpuaffinity 25 ++#define XEN_DOMCTL_real_mode_area 26 ++#define XEN_DOMCTL_resumedomain 27 ++#define XEN_DOMCTL_sendtrigger 28 ++#define XEN_DOMCTL_subscribe 29 ++#define XEN_DOMCTL_gethvmcontext 33 ++#define XEN_DOMCTL_sethvmcontext 34 ++#define XEN_DOMCTL_set_address_size 35 ++#define XEN_DOMCTL_get_address_size 36 ++#define XEN_DOMCTL_assign_device 37 ++#define XEN_DOMCTL_bind_pt_irq 38 ++#define XEN_DOMCTL_memory_mapping 39 ++#define XEN_DOMCTL_ioport_mapping 40 ++#define XEN_DOMCTL_pin_mem_cacheattr 41 ++#define XEN_DOMCTL_set_ext_vcpucontext 42 ++#define XEN_DOMCTL_get_ext_vcpucontext 43 ++#define XEN_DOMCTL_set_opt_feature 44 ++#define XEN_DOMCTL_test_assign_device 45 ++#define XEN_DOMCTL_set_target 46 ++#define XEN_DOMCTL_deassign_device 47 ++#define XEN_DOMCTL_unbind_pt_irq 48 ++#define XEN_DOMCTL_set_cpuid 49 ++#define XEN_DOMCTL_get_device_group 50 ++#define XEN_DOMCTL_set_machine_address_size 51 ++#define XEN_DOMCTL_get_machine_address_size 52 ++#define XEN_DOMCTL_suppress_spurious_page_faults 53 ++#define XEN_DOMCTL_debug_op 54 ++#define XEN_DOMCTL_gethvmcontext_partial 55 ++#define XEN_DOMCTL_mem_event_op 56 ++#define XEN_DOMCTL_mem_sharing_op 57 ++#define XEN_DOMCTL_disable_migrate 58 ++#define XEN_DOMCTL_gettscinfo 59 ++#define XEN_DOMCTL_settscinfo 60 ++#define XEN_DOMCTL_getpageframeinfo3 61 ++#define XEN_DOMCTL_gdbsx_guestmemio 1000 ++#define XEN_DOMCTL_gdbsx_pausevcpu 1001 ++#define XEN_DOMCTL_gdbsx_unpausevcpu 1002 ++#define XEN_DOMCTL_gdbsx_domstatus 1003 ++ uint32_t interface_version; /* XEN_DOMCTL_INTERFACE_VERSION */ ++ domid_t domain; ++ union { ++ struct xen_domctl_createdomain createdomain; ++ struct xen_domctl_getdomaininfo getdomaininfo; ++ struct xen_domctl_getmemlist getmemlist; ++ struct xen_domctl_getpageframeinfo getpageframeinfo; ++ struct xen_domctl_getpageframeinfo2 getpageframeinfo2; ++ struct xen_domctl_getpageframeinfo3 getpageframeinfo3; ++ struct xen_domctl_vcpuaffinity vcpuaffinity; ++ struct xen_domctl_shadow_op shadow_op; ++ struct xen_domctl_max_mem max_mem; ++ struct xen_domctl_vcpucontext vcpucontext; ++ struct xen_domctl_getvcpuinfo getvcpuinfo; ++ struct xen_domctl_max_vcpus max_vcpus; ++ struct xen_domctl_scheduler_op scheduler_op; ++ struct xen_domctl_setdomainhandle setdomainhandle; ++ struct xen_domctl_setdebugging setdebugging; ++ struct xen_domctl_irq_permission irq_permission; ++ struct xen_domctl_iomem_permission iomem_permission; ++ struct xen_domctl_ioport_permission ioport_permission; ++ struct xen_domctl_hypercall_init hypercall_init; ++ struct xen_domctl_arch_setup arch_setup; ++ struct xen_domctl_settimeoffset settimeoffset; ++ struct xen_domctl_disable_migrate disable_migrate; ++ struct xen_domctl_tsc_info tsc_info; ++ struct xen_domctl_real_mode_area real_mode_area; ++ struct xen_domctl_hvmcontext hvmcontext; ++ struct xen_domctl_hvmcontext_partial hvmcontext_partial; ++ struct xen_domctl_address_size address_size; ++ struct xen_domctl_sendtrigger sendtrigger; ++ struct xen_domctl_get_device_group get_device_group; ++ struct xen_domctl_assign_device assign_device; ++ struct xen_domctl_bind_pt_irq bind_pt_irq; ++ struct xen_domctl_memory_mapping memory_mapping; ++ struct xen_domctl_ioport_mapping ioport_mapping; ++ struct xen_domctl_pin_mem_cacheattr pin_mem_cacheattr; ++ struct xen_domctl_ext_vcpucontext ext_vcpucontext; ++ struct xen_domctl_set_opt_feature set_opt_feature; ++ struct xen_domctl_set_target set_target; ++ struct xen_domctl_subscribe subscribe; ++ struct xen_domctl_debug_op debug_op; ++ struct xen_domctl_mem_event_op mem_event_op; ++ struct xen_domctl_mem_sharing_op mem_sharing_op; ++#if defined(__i386__) || defined(__x86_64__) ++ struct xen_domctl_cpuid cpuid; ++#endif ++ struct xen_domctl_gdbsx_memio gdbsx_guest_memio; ++ struct xen_domctl_gdbsx_pauseunp_vcpu gdbsx_pauseunp_vcpu; ++ struct xen_domctl_gdbsx_domstatus gdbsx_domstatus; ++ uint8_t pad[128]; ++ } u; ++}; ++typedef struct xen_domctl xen_domctl_t; ++DEFINE_XEN_GUEST_HANDLE(xen_domctl_t); ++ ++#endif /* __XEN_PUBLIC_DOMCTL_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/hvm/e820.h 2007-06-12 13:14:19.000000000 +0200 +@@ -0,0 +1,34 @@ ++ ++/* ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef __XEN_PUBLIC_HVM_E820_H__ ++#define __XEN_PUBLIC_HVM_E820_H__ ++ ++/* E820 location in HVM virtual address space. */ ++#define HVM_E820_PAGE 0x00090000 ++#define HVM_E820_NR_OFFSET 0x000001E8 ++#define HVM_E820_OFFSET 0x000002D0 ++ ++#define HVM_BELOW_4G_RAM_END 0xF0000000 ++#define HVM_BELOW_4G_MMIO_START HVM_BELOW_4G_RAM_END ++#define HVM_BELOW_4G_MMIO_LENGTH ((1ULL << 32) - HVM_BELOW_4G_MMIO_START) ++ ++#endif /* __XEN_PUBLIC_HVM_E820_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/hvm/hvm_info_table.h 2010-05-07 11:10:48.000000000 +0200 +@@ -0,0 +1,75 @@ ++/****************************************************************************** ++ * hvm/hvm_info_table.h ++ * ++ * HVM parameter and information table, written into guest memory map. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef __XEN_PUBLIC_HVM_HVM_INFO_TABLE_H__ ++#define __XEN_PUBLIC_HVM_HVM_INFO_TABLE_H__ ++ ++#define HVM_INFO_PFN 0x09F ++#define HVM_INFO_OFFSET 0x800 ++#define HVM_INFO_PADDR ((HVM_INFO_PFN << 12) + HVM_INFO_OFFSET) ++ ++/* Maximum we can support with current vLAPIC ID mapping. */ ++#define HVM_MAX_VCPUS 128 ++ ++struct hvm_info_table { ++ char signature[8]; /* "HVM INFO" */ ++ uint32_t length; ++ uint8_t checksum; ++ ++ /* Should firmware build ACPI tables? */ ++ uint8_t acpi_enabled; ++ ++ /* Should firmware build APIC descriptors (APIC MADT / MP BIOS)? */ ++ uint8_t apic_mode; ++ ++ /* How many CPUs does this domain have? */ ++ uint32_t nr_vcpus; ++ ++ /* ++ * MEMORY MAP provided by HVM domain builder. ++ * Notes: ++ * 1. page_to_phys(x) = x << 12 ++ * 2. If a field is zero, the corresponding range does not exist. ++ */ ++ /* ++ * 0x0 to page_to_phys(low_mem_pgend)-1: ++ * RAM below 4GB (except for VGA hole 0xA0000-0xBFFFF) ++ */ ++ uint32_t low_mem_pgend; ++ /* ++ * page_to_phys(reserved_mem_pgstart) to 0xFFFFFFFF: ++ * Reserved for special memory mappings ++ */ ++ uint32_t reserved_mem_pgstart; ++ /* ++ * 0x100000000 to page_to_phys(high_mem_pgend)-1: ++ * RAM above 4GB ++ */ ++ uint32_t high_mem_pgend; ++ ++ /* Bitmap of which CPUs are online at boot time. */ ++ uint8_t vcpu_online[(HVM_MAX_VCPUS + 7)/8]; ++}; ++ ++#endif /* __XEN_PUBLIC_HVM_HVM_INFO_TABLE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/hvm/hvm_op.h 2009-06-23 09:28:21.000000000 +0200 +@@ -0,0 +1,133 @@ ++/* ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef __XEN_PUBLIC_HVM_HVM_OP_H__ ++#define __XEN_PUBLIC_HVM_HVM_OP_H__ ++ ++#include "../xen.h" ++ ++/* Get/set subcommands: extra argument == pointer to xen_hvm_param struct. */ ++#define HVMOP_set_param 0 ++#define HVMOP_get_param 1 ++struct xen_hvm_param { ++ domid_t domid; /* IN */ ++ uint32_t index; /* IN */ ++ uint64_t value; /* IN/OUT */ ++}; ++typedef struct xen_hvm_param xen_hvm_param_t; ++DEFINE_XEN_GUEST_HANDLE(xen_hvm_param_t); ++ ++/* Set the logical level of one of a domain's PCI INTx wires. */ ++#define HVMOP_set_pci_intx_level 2 ++struct xen_hvm_set_pci_intx_level { ++ /* Domain to be updated. */ ++ domid_t domid; ++ /* PCI INTx identification in PCI topology (domain:bus:device:intx). */ ++ uint8_t domain, bus, device, intx; ++ /* Assertion level (0 = unasserted, 1 = asserted). */ ++ uint8_t level; ++}; ++typedef struct xen_hvm_set_pci_intx_level xen_hvm_set_pci_intx_level_t; ++DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_pci_intx_level_t); ++ ++/* Set the logical level of one of a domain's ISA IRQ wires. */ ++#define HVMOP_set_isa_irq_level 3 ++struct xen_hvm_set_isa_irq_level { ++ /* Domain to be updated. */ ++ domid_t domid; ++ /* ISA device identification, by ISA IRQ (0-15). */ ++ uint8_t isa_irq; ++ /* Assertion level (0 = unasserted, 1 = asserted). */ ++ uint8_t level; ++}; ++typedef struct xen_hvm_set_isa_irq_level xen_hvm_set_isa_irq_level_t; ++DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_isa_irq_level_t); ++ ++#define HVMOP_set_pci_link_route 4 ++struct xen_hvm_set_pci_link_route { ++ /* Domain to be updated. */ ++ domid_t domid; ++ /* PCI link identifier (0-3). */ ++ uint8_t link; ++ /* ISA IRQ (1-15), or 0 (disable link). */ ++ uint8_t isa_irq; ++}; ++typedef struct xen_hvm_set_pci_link_route xen_hvm_set_pci_link_route_t; ++DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_pci_link_route_t); ++ ++/* Flushes all VCPU TLBs: @arg must be NULL. */ ++#define HVMOP_flush_tlbs 5 ++ ++/* Following tools-only interfaces may change in future. */ ++#if defined(__XEN__) || defined(__XEN_TOOLS__) ++ ++/* Track dirty VRAM. */ ++#define HVMOP_track_dirty_vram 6 ++struct xen_hvm_track_dirty_vram { ++ /* Domain to be tracked. */ ++ domid_t domid; ++ /* First pfn to track. */ ++ uint64_aligned_t first_pfn; ++ /* Number of pages to track. */ ++ uint64_aligned_t nr; ++ /* OUT variable. */ ++ /* Dirty bitmap buffer. */ ++ XEN_GUEST_HANDLE_64(uint8) dirty_bitmap; ++}; ++typedef struct xen_hvm_track_dirty_vram xen_hvm_track_dirty_vram_t; ++DEFINE_XEN_GUEST_HANDLE(xen_hvm_track_dirty_vram_t); ++ ++/* Notify that some pages got modified by the Device Model. */ ++#define HVMOP_modified_memory 7 ++struct xen_hvm_modified_memory { ++ /* Domain to be updated. */ ++ domid_t domid; ++ /* First pfn. */ ++ uint64_aligned_t first_pfn; ++ /* Number of pages. */ ++ uint64_aligned_t nr; ++}; ++typedef struct xen_hvm_modified_memory xen_hvm_modified_memory_t; ++DEFINE_XEN_GUEST_HANDLE(xen_hvm_modified_memory_t); ++ ++#define HVMOP_set_mem_type 8 ++typedef enum { ++ HVMMEM_ram_rw, /* Normal read/write guest RAM */ ++ HVMMEM_ram_ro, /* Read-only; writes are discarded */ ++ HVMMEM_mmio_dm, /* Reads and write go to the device model */ ++} hvmmem_type_t; ++/* Notify that a region of memory is to be treated in a specific way. */ ++struct xen_hvm_set_mem_type { ++ /* Domain to be updated. */ ++ domid_t domid; ++ /* Memory type */ ++ hvmmem_type_t hvmmem_type; ++ /* First pfn. */ ++ uint64_aligned_t first_pfn; ++ /* Number of pages. */ ++ uint64_aligned_t nr; ++}; ++typedef struct xen_hvm_set_mem_type xen_hvm_set_mem_type_t; ++DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_mem_type_t); ++ ++ ++#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ ++ ++#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/hvm/ioreq.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,119 @@ ++/* ++ * ioreq.h: I/O request definitions for device models ++ * Copyright (c) 2004, Intel Corporation. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef _IOREQ_H_ ++#define _IOREQ_H_ ++ ++#define IOREQ_READ 1 ++#define IOREQ_WRITE 0 ++ ++#define STATE_IOREQ_NONE 0 ++#define STATE_IOREQ_READY 1 ++#define STATE_IOREQ_INPROCESS 2 ++#define STATE_IORESP_READY 3 ++ ++#define IOREQ_TYPE_PIO 0 /* pio */ ++#define IOREQ_TYPE_COPY 1 /* mmio ops */ ++#define IOREQ_TYPE_TIMEOFFSET 7 ++#define IOREQ_TYPE_INVALIDATE 8 /* mapcache */ ++ ++/* ++ * VMExit dispatcher should cooperate with instruction decoder to ++ * prepare this structure and notify service OS and DM by sending ++ * virq ++ */ ++struct ioreq { ++ uint64_t addr; /* physical address */ ++ uint64_t data; /* data (or paddr of data) */ ++ uint32_t count; /* for rep prefixes */ ++ uint32_t size; /* size in bytes */ ++ uint32_t vp_eport; /* evtchn for notifications to/from device model */ ++ uint16_t _pad0; ++ uint8_t state:4; ++ uint8_t data_is_ptr:1; /* if 1, data above is the guest paddr ++ * of the real data to use. */ ++ uint8_t dir:1; /* 1=read, 0=write */ ++ uint8_t df:1; ++ uint8_t _pad1:1; ++ uint8_t type; /* I/O type */ ++}; ++typedef struct ioreq ioreq_t; ++ ++struct shared_iopage { ++ struct ioreq vcpu_ioreq[1]; ++}; ++typedef struct shared_iopage shared_iopage_t; ++ ++struct buf_ioreq { ++ uint8_t type; /* I/O type */ ++ uint8_t pad:1; ++ uint8_t dir:1; /* 1=read, 0=write */ ++ uint8_t size:2; /* 0=>1, 1=>2, 2=>4, 3=>8. If 8, use two buf_ioreqs */ ++ uint32_t addr:20;/* physical address */ ++ uint32_t data; /* data */ ++}; ++typedef struct buf_ioreq buf_ioreq_t; ++ ++#define IOREQ_BUFFER_SLOT_NUM 511 /* 8 bytes each, plus 2 4-byte indexes */ ++struct buffered_iopage { ++ unsigned int read_pointer; ++ unsigned int write_pointer; ++ buf_ioreq_t buf_ioreq[IOREQ_BUFFER_SLOT_NUM]; ++}; /* NB. Size of this structure must be no greater than one page. */ ++typedef struct buffered_iopage buffered_iopage_t; ++ ++#if defined(__ia64__) ++struct pio_buffer { ++ uint32_t page_offset; ++ uint32_t pointer; ++ uint32_t data_end; ++ uint32_t buf_size; ++ void *opaque; ++}; ++ ++#define PIO_BUFFER_IDE_PRIMARY 0 /* I/O port = 0x1F0 */ ++#define PIO_BUFFER_IDE_SECONDARY 1 /* I/O port = 0x170 */ ++#define PIO_BUFFER_ENTRY_NUM 2 ++struct buffered_piopage { ++ struct pio_buffer pio[PIO_BUFFER_ENTRY_NUM]; ++ uint8_t buffer[1]; ++}; ++#endif /* defined(__ia64__) */ ++ ++#define ACPI_PM1A_EVT_BLK_ADDRESS 0x0000000000001f40 ++#define ACPI_PM1A_CNT_BLK_ADDRESS (ACPI_PM1A_EVT_BLK_ADDRESS + 0x04) ++#define ACPI_PM_TMR_BLK_ADDRESS (ACPI_PM1A_EVT_BLK_ADDRESS + 0x08) ++#define ACPI_GPE0_BLK_ADDRESS (ACPI_PM_TMR_BLK_ADDRESS + 0x20) ++#define ACPI_GPE0_BLK_LEN 0x08 ++ ++#endif /* _IOREQ_H_ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/hvm/params.h 2009-04-07 13:58:49.000000000 +0200 +@@ -0,0 +1,111 @@ ++/* ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef __XEN_PUBLIC_HVM_PARAMS_H__ ++#define __XEN_PUBLIC_HVM_PARAMS_H__ ++ ++#include "hvm_op.h" ++ ++/* ++ * Parameter space for HVMOP_{set,get}_param. ++ */ ++ ++/* ++ * How should CPU0 event-channel notifications be delivered? ++ * val[63:56] == 0: val[55:0] is a delivery GSI (Global System Interrupt). ++ * val[63:56] == 1: val[55:0] is a delivery PCI INTx line, as follows: ++ * Domain = val[47:32], Bus = val[31:16], ++ * DevFn = val[15: 8], IntX = val[ 1: 0] ++ * If val == 0 then CPU0 event-channel notifications are not delivered. ++ */ ++#define HVM_PARAM_CALLBACK_IRQ 0 ++ ++/* ++ * These are not used by Xen. They are here for convenience of HVM-guest ++ * xenbus implementations. ++ */ ++#define HVM_PARAM_STORE_PFN 1 ++#define HVM_PARAM_STORE_EVTCHN 2 ++ ++#define HVM_PARAM_PAE_ENABLED 4 ++ ++#define HVM_PARAM_IOREQ_PFN 5 ++ ++#define HVM_PARAM_BUFIOREQ_PFN 6 ++ ++#ifdef __ia64__ ++ ++#define HVM_PARAM_NVRAM_FD 7 ++#define HVM_PARAM_VHPT_SIZE 8 ++#define HVM_PARAM_BUFPIOREQ_PFN 9 ++ ++#elif defined(__i386__) || defined(__x86_64__) ++ ++/* Expose Viridian interfaces to this HVM guest? */ ++#define HVM_PARAM_VIRIDIAN 9 ++ ++#endif ++ ++/* ++ * Set mode for virtual timers (currently x86 only): ++ * delay_for_missed_ticks (default): ++ * Do not advance a vcpu's time beyond the correct delivery time for ++ * interrupts that have been missed due to preemption. Deliver missed ++ * interrupts when the vcpu is rescheduled and advance the vcpu's virtual ++ * time stepwise for each one. ++ * no_delay_for_missed_ticks: ++ * As above, missed interrupts are delivered, but guest time always tracks ++ * wallclock (i.e., real) time while doing so. ++ * no_missed_ticks_pending: ++ * No missed interrupts are held pending. Instead, to ensure ticks are ++ * delivered at some non-zero rate, if we detect missed ticks then the ++ * internal tick alarm is not disabled if the VCPU is preempted during the ++ * next tick period. ++ * one_missed_tick_pending: ++ * Missed interrupts are collapsed together and delivered as one 'late tick'. ++ * Guest time always tracks wallclock (i.e., real) time. ++ */ ++#define HVM_PARAM_TIMER_MODE 10 ++#define HVMPTM_delay_for_missed_ticks 0 ++#define HVMPTM_no_delay_for_missed_ticks 1 ++#define HVMPTM_no_missed_ticks_pending 2 ++#define HVMPTM_one_missed_tick_pending 3 ++ ++/* Boolean: Enable virtual HPET (high-precision event timer)? (x86-only) */ ++#define HVM_PARAM_HPET_ENABLED 11 ++ ++/* Identity-map page directory used by Intel EPT when CR0.PG=0. */ ++#define HVM_PARAM_IDENT_PT 12 ++ ++/* Device Model domain, defaults to 0. */ ++#define HVM_PARAM_DM_DOMAIN 13 ++ ++/* ACPI S state: currently support S0 and S3 on x86. */ ++#define HVM_PARAM_ACPI_S_STATE 14 ++ ++/* TSS used on Intel when CR0.PE=0. */ ++#define HVM_PARAM_VM86_TSS 15 ++ ++/* Boolean: Enable aligning all periodic vpts to reduce interrupts */ ++#define HVM_PARAM_VPT_ALIGN 16 ++ ++#define HVM_NR_PARAMS 17 ++ ++#endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/hvm/save.h 2008-04-02 12:34:02.000000000 +0200 +@@ -0,0 +1,88 @@ ++/* ++ * hvm/save.h ++ * ++ * Structure definitions for HVM state that is held by Xen and must ++ * be saved along with the domain's memory and device-model state. ++ * ++ * Copyright (c) 2007 XenSource Ltd. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef __XEN_PUBLIC_HVM_SAVE_H__ ++#define __XEN_PUBLIC_HVM_SAVE_H__ ++ ++/* ++ * Structures in this header *must* have the same layout in 32bit ++ * and 64bit environments: this means that all fields must be explicitly ++ * sized types and aligned to their sizes, and the structs must be ++ * a multiple of eight bytes long. ++ * ++ * Only the state necessary for saving and restoring (i.e. fields ++ * that are analogous to actual hardware state) should go in this file. ++ * Internal mechanisms should be kept in Xen-private headers. ++ */ ++ ++#if !defined(__GNUC__) || defined(__STRICT_ANSI__) ++#error "Anonymous structs/unions are a GNU extension." ++#endif ++ ++/* ++ * Each entry is preceded by a descriptor giving its type and length ++ */ ++struct hvm_save_descriptor { ++ uint16_t typecode; /* Used to demux the various types below */ ++ uint16_t instance; /* Further demux within a type */ ++ uint32_t length; /* In bytes, *not* including this descriptor */ ++}; ++ ++ ++/* ++ * Each entry has a datatype associated with it: for example, the CPU state ++ * is saved as a HVM_SAVE_TYPE(CPU), which has HVM_SAVE_LENGTH(CPU), ++ * and is identified by a descriptor with typecode HVM_SAVE_CODE(CPU). ++ * DECLARE_HVM_SAVE_TYPE binds these things together with some type-system ++ * ugliness. ++ */ ++ ++#define DECLARE_HVM_SAVE_TYPE(_x, _code, _type) \ ++ struct __HVM_SAVE_TYPE_##_x { _type t; char c[_code]; } ++ ++#define HVM_SAVE_TYPE(_x) typeof (((struct __HVM_SAVE_TYPE_##_x *)(0))->t) ++#define HVM_SAVE_LENGTH(_x) (sizeof (HVM_SAVE_TYPE(_x))) ++#define HVM_SAVE_CODE(_x) (sizeof (((struct __HVM_SAVE_TYPE_##_x *)(0))->c)) ++ ++ ++/* ++ * The series of save records is teminated by a zero-type, zero-length ++ * descriptor. ++ */ ++ ++struct hvm_save_end {}; ++DECLARE_HVM_SAVE_TYPE(END, 0, struct hvm_save_end); ++ ++#if defined(__i386__) || defined(__x86_64__) ++#include "../arch-x86/hvm/save.h" ++#elif defined(__ia64__) ++#include "../arch-ia64/hvm/save.h" ++#else ++#error "unsupported architecture" ++#endif ++ ++#endif /* __XEN_PUBLIC_HVM_SAVE_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/io/fsif.h 2009-06-23 09:28:21.000000000 +0200 +@@ -0,0 +1,192 @@ ++/****************************************************************************** ++ * fsif.h ++ * ++ * Interface to FS level split device drivers. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2007, Grzegorz Milos, . ++ */ ++ ++#ifndef __XEN_PUBLIC_IO_FSIF_H__ ++#define __XEN_PUBLIC_IO_FSIF_H__ ++ ++#include "ring.h" ++#include "../grant_table.h" ++ ++#define REQ_FILE_OPEN 1 ++#define REQ_FILE_CLOSE 2 ++#define REQ_FILE_READ 3 ++#define REQ_FILE_WRITE 4 ++#define REQ_STAT 5 ++#define REQ_FILE_TRUNCATE 6 ++#define REQ_REMOVE 7 ++#define REQ_RENAME 8 ++#define REQ_CREATE 9 ++#define REQ_DIR_LIST 10 ++#define REQ_CHMOD 11 ++#define REQ_FS_SPACE 12 ++#define REQ_FILE_SYNC 13 ++ ++struct fsif_open_request { ++ grant_ref_t gref; ++}; ++ ++struct fsif_close_request { ++ uint32_t fd; ++}; ++ ++struct fsif_read_request { ++ uint32_t fd; ++ int32_t pad; ++ uint64_t len; ++ uint64_t offset; ++ grant_ref_t grefs[1]; /* Variable length */ ++}; ++ ++struct fsif_write_request { ++ uint32_t fd; ++ int32_t pad; ++ uint64_t len; ++ uint64_t offset; ++ grant_ref_t grefs[1]; /* Variable length */ ++}; ++ ++struct fsif_stat_request { ++ uint32_t fd; ++}; ++ ++/* This structure is a copy of some fields from stat structure, returned ++ * via the ring. */ ++struct fsif_stat_response { ++ int32_t stat_mode; ++ uint32_t stat_uid; ++ uint32_t stat_gid; ++ int32_t stat_ret; ++ int64_t stat_size; ++ int64_t stat_atime; ++ int64_t stat_mtime; ++ int64_t stat_ctime; ++}; ++ ++struct fsif_truncate_request { ++ uint32_t fd; ++ int32_t pad; ++ int64_t length; ++}; ++ ++struct fsif_remove_request { ++ grant_ref_t gref; ++}; ++ ++struct fsif_rename_request { ++ uint16_t old_name_offset; ++ uint16_t new_name_offset; ++ grant_ref_t gref; ++}; ++ ++struct fsif_create_request { ++ int8_t directory; ++ int8_t pad; ++ int16_t pad2; ++ int32_t mode; ++ grant_ref_t gref; ++}; ++ ++struct fsif_list_request { ++ uint32_t offset; ++ grant_ref_t gref; ++}; ++ ++#define NR_FILES_SHIFT 0 ++#define NR_FILES_SIZE 16 /* 16 bits for the number of files mask */ ++#define NR_FILES_MASK (((1ULL << NR_FILES_SIZE) - 1) << NR_FILES_SHIFT) ++#define ERROR_SIZE 32 /* 32 bits for the error mask */ ++#define ERROR_SHIFT (NR_FILES_SIZE + NR_FILES_SHIFT) ++#define ERROR_MASK (((1ULL << ERROR_SIZE) - 1) << ERROR_SHIFT) ++#define HAS_MORE_SHIFT (ERROR_SHIFT + ERROR_SIZE) ++#define HAS_MORE_FLAG (1ULL << HAS_MORE_SHIFT) ++ ++struct fsif_chmod_request { ++ uint32_t fd; ++ int32_t mode; ++}; ++ ++struct fsif_space_request { ++ grant_ref_t gref; ++}; ++ ++struct fsif_sync_request { ++ uint32_t fd; ++}; ++ ++ ++/* FS operation request */ ++struct fsif_request { ++ uint8_t type; /* Type of the request */ ++ uint8_t pad; ++ uint16_t id; /* Request ID, copied to the response */ ++ uint32_t pad2; ++ union { ++ struct fsif_open_request fopen; ++ struct fsif_close_request fclose; ++ struct fsif_read_request fread; ++ struct fsif_write_request fwrite; ++ struct fsif_stat_request fstat; ++ struct fsif_truncate_request ftruncate; ++ struct fsif_remove_request fremove; ++ struct fsif_rename_request frename; ++ struct fsif_create_request fcreate; ++ struct fsif_list_request flist; ++ struct fsif_chmod_request fchmod; ++ struct fsif_space_request fspace; ++ struct fsif_sync_request fsync; ++ } u; ++}; ++typedef struct fsif_request fsif_request_t; ++ ++/* FS operation response */ ++struct fsif_response { ++ uint16_t id; ++ uint16_t pad1; ++ uint32_t pad2; ++ union { ++ uint64_t ret_val; ++ struct fsif_stat_response fstat; ++ } u; ++}; ++ ++typedef struct fsif_response fsif_response_t; ++ ++#define FSIF_RING_ENTRY_SIZE 64 ++ ++#define FSIF_NR_READ_GNTS ((FSIF_RING_ENTRY_SIZE - sizeof(struct fsif_read_request)) / \ ++ sizeof(grant_ref_t) + 1) ++#define FSIF_NR_WRITE_GNTS ((FSIF_RING_ENTRY_SIZE - sizeof(struct fsif_write_request)) / \ ++ sizeof(grant_ref_t) + 1) ++ ++DEFINE_RING_TYPES(fsif, struct fsif_request, struct fsif_response); ++ ++#define STATE_INITIALISED "init" ++#define STATE_READY "ready" ++#define STATE_CLOSING "closing" ++#define STATE_CLOSED "closed" ++ ++ ++#endif +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/io/pciif.h 2009-04-07 13:58:49.000000000 +0200 +@@ -0,0 +1,124 @@ ++/* ++ * PCI Backend/Frontend Common Data Structures & Macros ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Author: Ryan Wilson ++ */ ++#ifndef __XEN_PCI_COMMON_H__ ++#define __XEN_PCI_COMMON_H__ ++ ++/* Be sure to bump this number if you change this file */ ++#define XEN_PCI_MAGIC "7" ++ ++/* xen_pci_sharedinfo flags */ ++#define _XEN_PCIF_active (0) ++#define XEN_PCIF_active (1<<_XEN_PCIF_active) ++#define _XEN_PCIB_AERHANDLER (1) ++#define XEN_PCIB_AERHANDLER (1<<_XEN_PCIB_AERHANDLER) ++#define _XEN_PCIB_active (2) ++#define XEN_PCIB_active (1<<_XEN_PCIB_active) ++ ++/* xen_pci_op commands */ ++#define XEN_PCI_OP_conf_read (0) ++#define XEN_PCI_OP_conf_write (1) ++#define XEN_PCI_OP_enable_msi (2) ++#define XEN_PCI_OP_disable_msi (3) ++#define XEN_PCI_OP_enable_msix (4) ++#define XEN_PCI_OP_disable_msix (5) ++#define XEN_PCI_OP_aer_detected (6) ++#define XEN_PCI_OP_aer_resume (7) ++#define XEN_PCI_OP_aer_mmio (8) ++#define XEN_PCI_OP_aer_slotreset (9) ++ ++/* xen_pci_op error numbers */ ++#define XEN_PCI_ERR_success (0) ++#define XEN_PCI_ERR_dev_not_found (-1) ++#define XEN_PCI_ERR_invalid_offset (-2) ++#define XEN_PCI_ERR_access_denied (-3) ++#define XEN_PCI_ERR_not_implemented (-4) ++/* XEN_PCI_ERR_op_failed - backend failed to complete the operation */ ++#define XEN_PCI_ERR_op_failed (-5) ++ ++/* ++ * it should be PAGE_SIZE-sizeof(struct xen_pci_op))/sizeof(struct msix_entry)) ++ * Should not exceed 128 ++ */ ++#define SH_INFO_MAX_VEC 128 ++ ++struct xen_msix_entry { ++ uint16_t vector; ++ uint16_t entry; ++}; ++struct xen_pci_op { ++ /* IN: what action to perform: XEN_PCI_OP_* */ ++ uint32_t cmd; ++ ++ /* OUT: will contain an error number (if any) from errno.h */ ++ int32_t err; ++ ++ /* IN: which device to touch */ ++ uint32_t domain; /* PCI Domain/Segment */ ++ uint32_t bus; ++ uint32_t devfn; ++ ++ /* IN: which configuration registers to touch */ ++ int32_t offset; ++ int32_t size; ++ ++ /* IN/OUT: Contains the result after a READ or the value to WRITE */ ++ uint32_t value; ++ /* IN: Contains extra infor for this operation */ ++ uint32_t info; ++ /*IN: param for msi-x */ ++ struct xen_msix_entry msix_entries[SH_INFO_MAX_VEC]; ++}; ++ ++/*used for pcie aer handling*/ ++struct xen_pcie_aer_op ++{ ++ ++ /* IN: what action to perform: XEN_PCI_OP_* */ ++ uint32_t cmd; ++ /*IN/OUT: return aer_op result or carry error_detected state as input*/ ++ int32_t err; ++ ++ /* IN: which device to touch */ ++ uint32_t domain; /* PCI Domain/Segment*/ ++ uint32_t bus; ++ uint32_t devfn; ++}; ++struct xen_pci_sharedinfo { ++ /* flags - XEN_PCIF_* */ ++ uint32_t flags; ++ struct xen_pci_op op; ++ struct xen_pcie_aer_op aer_op; ++}; ++ ++#endif /* __XEN_PCI_COMMON_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/io/tpmif.h 2007-06-12 13:14:19.000000000 +0200 +@@ -0,0 +1,77 @@ ++/****************************************************************************** ++ * tpmif.h ++ * ++ * TPM I/O interface for Xen guest OSes. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2005, IBM Corporation ++ * ++ * Author: Stefan Berger, stefanb@us.ibm.com ++ * Grant table support: Mahadevan Gomathisankaran ++ * ++ * This code has been derived from tools/libxc/xen/io/netif.h ++ * ++ * Copyright (c) 2003-2004, Keir Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_IO_TPMIF_H__ ++#define __XEN_PUBLIC_IO_TPMIF_H__ ++ ++#include "../grant_table.h" ++ ++struct tpmif_tx_request { ++ unsigned long addr; /* Machine address of packet. */ ++ grant_ref_t ref; /* grant table access reference */ ++ uint16_t unused; ++ uint16_t size; /* Packet size in bytes. */ ++}; ++typedef struct tpmif_tx_request tpmif_tx_request_t; ++ ++/* ++ * The TPMIF_TX_RING_SIZE defines the number of pages the ++ * front-end and backend can exchange (= size of array). ++ */ ++typedef uint32_t TPMIF_RING_IDX; ++ ++#define TPMIF_TX_RING_SIZE 1 ++ ++/* This structure must fit in a memory page. */ ++ ++struct tpmif_ring { ++ struct tpmif_tx_request req; ++}; ++typedef struct tpmif_ring tpmif_ring_t; ++ ++struct tpmif_tx_interface { ++ struct tpmif_ring ring[TPMIF_TX_RING_SIZE]; ++}; ++typedef struct tpmif_tx_interface tpmif_tx_interface_t; ++ ++#endif ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/io/usbif.h 2010-02-24 13:13:46.000000000 +0100 +@@ -0,0 +1,151 @@ ++/* ++ * usbif.h ++ * ++ * USB I/O interface for Xen guest OSes. ++ * ++ * Copyright (C) 2009, FUJITSU LABORATORIES LTD. ++ * Author: Noboru Iwamatsu ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef __XEN_PUBLIC_IO_USBIF_H__ ++#define __XEN_PUBLIC_IO_USBIF_H__ ++ ++#include "ring.h" ++#include "../grant_table.h" ++ ++enum usb_spec_version { ++ USB_VER_UNKNOWN = 0, ++ USB_VER_USB11, ++ USB_VER_USB20, ++ USB_VER_USB30, /* not supported yet */ ++}; ++ ++/* ++ * USB pipe in usbif_request ++ * ++ * bits 0-5 are specific bits for virtual USB driver. ++ * bits 7-31 are standard urb pipe. ++ * ++ * - port number(NEW): bits 0-4 ++ * (USB_MAXCHILDREN is 31) ++ * ++ * - operation flag(NEW): bit 5 ++ * (0 = submit urb, ++ * 1 = unlink urb) ++ * ++ * - direction: bit 7 ++ * (0 = Host-to-Device [Out] ++ * 1 = Device-to-Host [In]) ++ * ++ * - device address: bits 8-14 ++ * ++ * - endpoint: bits 15-18 ++ * ++ * - pipe type: bits 30-31 ++ * (00 = isochronous, 01 = interrupt, ++ * 10 = control, 11 = bulk) ++ */ ++#define usbif_pipeportnum(pipe) ((pipe) & 0x1f) ++#define usbif_setportnum_pipe(pipe, portnum) \ ++ ((pipe)|(portnum)) ++ ++#define usbif_pipeunlink(pipe) ((pipe) & 0x20) ++#define usbif_pipesubmit(pipe) (!usbif_pipeunlink(pipe)) ++#define usbif_setunlink_pipe(pipe) ((pipe)|(0x20)) ++ ++#define USBIF_BACK_MAX_PENDING_REQS (128) ++#define USBIF_MAX_SEGMENTS_PER_REQUEST (16) ++ ++/* ++ * RING for transferring urbs. ++ */ ++struct usbif_request_segment { ++ grant_ref_t gref; ++ uint16_t offset; ++ uint16_t length; ++}; ++ ++struct usbif_urb_request { ++ uint16_t id; /* request id */ ++ uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */ ++ ++ /* basic urb parameter */ ++ uint32_t pipe; ++ uint16_t transfer_flags; ++ uint16_t buffer_length; ++ union { ++ uint8_t ctrl[8]; /* setup_packet (Ctrl) */ ++ ++ struct { ++ uint16_t interval; /* maximum (1024*8) in usb core */ ++ uint16_t start_frame; /* start frame */ ++ uint16_t number_of_packets; /* number of ISO packet */ ++ uint16_t nr_frame_desc_segs; /* number of iso_frame_desc segments */ ++ } isoc; ++ ++ struct { ++ uint16_t interval; /* maximum (1024*8) in usb core */ ++ uint16_t pad[3]; ++ } intr; ++ ++ struct { ++ uint16_t unlink_id; /* unlink request id */ ++ uint16_t pad[3]; ++ } unlink; ++ ++ } u; ++ ++ /* urb data segments */ ++ struct usbif_request_segment seg[USBIF_MAX_SEGMENTS_PER_REQUEST]; ++}; ++typedef struct usbif_urb_request usbif_urb_request_t; ++ ++struct usbif_urb_response { ++ uint16_t id; /* request id */ ++ uint16_t start_frame; /* start frame (ISO) */ ++ int32_t status; /* status (non-ISO) */ ++ int32_t actual_length; /* actual transfer length */ ++ int32_t error_count; /* number of ISO errors */ ++}; ++typedef struct usbif_urb_response usbif_urb_response_t; ++ ++DEFINE_RING_TYPES(usbif_urb, struct usbif_urb_request, struct usbif_urb_response); ++#define USB_URB_RING_SIZE __CONST_RING_SIZE(usbif_urb, PAGE_SIZE) ++ ++/* ++ * RING for notifying connect/disconnect events to frontend ++ */ ++struct usbif_conn_request { ++ uint16_t id; ++}; ++typedef struct usbif_conn_request usbif_conn_request_t; ++ ++struct usbif_conn_response { ++ uint16_t id; /* request id */ ++ uint8_t portnum; /* port number */ ++ uint8_t speed; /* usb_device_speed */ ++}; ++typedef struct usbif_conn_response usbif_conn_response_t; ++ ++DEFINE_RING_TYPES(usbif_conn, struct usbif_conn_request, struct usbif_conn_response); ++#define USB_CONN_RING_SIZE __CONST_RING_SIZE(usbif_conn, PAGE_SIZE) ++ ++#endif /* __XEN_PUBLIC_IO_USBIF_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/io/vscsiif.h 2008-07-21 11:00:33.000000000 +0200 +@@ -0,0 +1,105 @@ ++/****************************************************************************** ++ * vscsiif.h ++ * ++ * Based on the blkif.h code. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright(c) FUJITSU Limited 2008. ++ */ ++ ++#ifndef __XEN__PUBLIC_IO_SCSI_H__ ++#define __XEN__PUBLIC_IO_SCSI_H__ ++ ++#include "ring.h" ++#include "../grant_table.h" ++ ++/* command between backend and frontend */ ++#define VSCSIIF_ACT_SCSI_CDB 1 /* SCSI CDB command */ ++#define VSCSIIF_ACT_SCSI_ABORT 2 /* SCSI Device(Lun) Abort*/ ++#define VSCSIIF_ACT_SCSI_RESET 3 /* SCSI Device(Lun) Reset*/ ++ ++ ++#define VSCSIIF_BACK_MAX_PENDING_REQS 128 ++ ++/* ++ * Maximum scatter/gather segments per request. ++ * ++ * Considering balance between allocating al least 16 "vscsiif_request" ++ * structures on one page (4096bytes) and number of scatter gather ++ * needed, we decided to use 26 as a magic number. ++ */ ++#define VSCSIIF_SG_TABLESIZE 26 ++ ++/* ++ * base on linux kernel 2.6.18 ++ */ ++#define VSCSIIF_MAX_COMMAND_SIZE 16 ++#define VSCSIIF_SENSE_BUFFERSIZE 96 ++ ++ ++struct vscsiif_request { ++ uint16_t rqid; /* private guest value, echoed in resp */ ++ uint8_t act; /* command between backend and frontend */ ++ uint8_t cmd_len; ++ ++ uint8_t cmnd[VSCSIIF_MAX_COMMAND_SIZE]; ++ uint16_t timeout_per_command; /* The command is issued by twice ++ the value in Backend. */ ++ uint16_t channel, id, lun; ++ uint16_t padding; ++ uint8_t sc_data_direction; /* for DMA_TO_DEVICE(1) ++ DMA_FROM_DEVICE(2) ++ DMA_NONE(3) requests */ ++ uint8_t nr_segments; /* Number of pieces of scatter-gather */ ++ ++ struct scsiif_request_segment { ++ grant_ref_t gref; ++ uint16_t offset; ++ uint16_t length; ++ } seg[VSCSIIF_SG_TABLESIZE]; ++ uint32_t reserved[3]; ++}; ++typedef struct vscsiif_request vscsiif_request_t; ++ ++struct vscsiif_response { ++ uint16_t rqid; ++ uint8_t padding; ++ uint8_t sense_len; ++ uint8_t sense_buffer[VSCSIIF_SENSE_BUFFERSIZE]; ++ int32_t rslt; ++ uint32_t residual_len; /* request bufflen - ++ return the value from physical device */ ++ uint32_t reserved[36]; ++}; ++typedef struct vscsiif_response vscsiif_response_t; ++ ++DEFINE_RING_TYPES(vscsiif, struct vscsiif_request, struct vscsiif_response); ++ ++ ++#endif /*__XEN__PUBLIC_IO_SCSI_H__*/ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/kexec.h 2008-11-25 12:22:34.000000000 +0100 +@@ -0,0 +1,168 @@ ++/****************************************************************************** ++ * kexec.h - Public portion ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Xen port written by: ++ * - Simon 'Horms' Horman ++ * - Magnus Damm ++ */ ++ ++#ifndef _XEN_PUBLIC_KEXEC_H ++#define _XEN_PUBLIC_KEXEC_H ++ ++ ++/* This file describes the Kexec / Kdump hypercall interface for Xen. ++ * ++ * Kexec under vanilla Linux allows a user to reboot the physical machine ++ * into a new user-specified kernel. The Xen port extends this idea ++ * to allow rebooting of the machine from dom0. When kexec for dom0 ++ * is used to reboot, both the hypervisor and the domains get replaced ++ * with some other kernel. It is possible to kexec between vanilla ++ * Linux and Xen and back again. Xen to Xen works well too. ++ * ++ * The hypercall interface for kexec can be divided into three main ++ * types of hypercall operations: ++ * ++ * 1) Range information: ++ * This is used by the dom0 kernel to ask the hypervisor about various ++ * address information. This information is needed to allow kexec-tools ++ * to fill in the ELF headers for /proc/vmcore properly. ++ * ++ * 2) Load and unload of images: ++ * There are no big surprises here, the kexec binary from kexec-tools ++ * runs in userspace in dom0. The tool loads/unloads data into the ++ * dom0 kernel such as new kernel, initramfs and hypervisor. When ++ * loaded the dom0 kernel performs a load hypercall operation, and ++ * before releasing all page references the dom0 kernel calls unload. ++ * ++ * 3) Kexec operation: ++ * This is used to start a previously loaded kernel. ++ */ ++ ++#include "xen.h" ++ ++#if defined(__i386__) || defined(__x86_64__) ++#define KEXEC_XEN_NO_PAGES 17 ++#endif ++ ++/* ++ * Prototype for this hypercall is: ++ * int kexec_op(int cmd, void *args) ++ * @cmd == KEXEC_CMD_... ++ * KEXEC operation to perform ++ * @args == Operation-specific extra arguments (NULL if none). ++ */ ++ ++/* ++ * Kexec supports two types of operation: ++ * - kexec into a regular kernel, very similar to a standard reboot ++ * - KEXEC_TYPE_DEFAULT is used to specify this type ++ * - kexec into a special "crash kernel", aka kexec-on-panic ++ * - KEXEC_TYPE_CRASH is used to specify this type ++ * - parts of our system may be broken at kexec-on-panic time ++ * - the code should be kept as simple and self-contained as possible ++ */ ++ ++#define KEXEC_TYPE_DEFAULT 0 ++#define KEXEC_TYPE_CRASH 1 ++ ++ ++/* The kexec implementation for Xen allows the user to load two ++ * types of kernels, KEXEC_TYPE_DEFAULT and KEXEC_TYPE_CRASH. ++ * All data needed for a kexec reboot is kept in one xen_kexec_image_t ++ * per "instance". The data mainly consists of machine address lists to pages ++ * together with destination addresses. The data in xen_kexec_image_t ++ * is passed to the "code page" which is one page of code that performs ++ * the final relocations before jumping to the new kernel. ++ */ ++ ++typedef struct xen_kexec_image { ++#if defined(__i386__) || defined(__x86_64__) ++ unsigned long page_list[KEXEC_XEN_NO_PAGES]; ++#endif ++#if defined(__ia64__) ++ unsigned long reboot_code_buffer; ++#endif ++ unsigned long indirection_page; ++ unsigned long start_address; ++} xen_kexec_image_t; ++ ++/* ++ * Perform kexec having previously loaded a kexec or kdump kernel ++ * as appropriate. ++ * type == KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH [in] ++ */ ++#define KEXEC_CMD_kexec 0 ++typedef struct xen_kexec_exec { ++ int type; ++} xen_kexec_exec_t; ++ ++/* ++ * Load/Unload kernel image for kexec or kdump. ++ * type == KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH [in] ++ * image == relocation information for kexec (ignored for unload) [in] ++ */ ++#define KEXEC_CMD_kexec_load 1 ++#define KEXEC_CMD_kexec_unload 2 ++typedef struct xen_kexec_load { ++ int type; ++ xen_kexec_image_t image; ++} xen_kexec_load_t; ++ ++#define KEXEC_RANGE_MA_CRASH 0 /* machine address and size of crash area */ ++#define KEXEC_RANGE_MA_XEN 1 /* machine address and size of Xen itself */ ++#define KEXEC_RANGE_MA_CPU 2 /* machine address and size of a CPU note */ ++#define KEXEC_RANGE_MA_XENHEAP 3 /* machine address and size of xenheap ++ * Note that although this is adjacent ++ * to Xen it exists in a separate EFI ++ * region on ia64, and thus needs to be ++ * inserted into iomem_machine separately */ ++#define KEXEC_RANGE_MA_BOOT_PARAM 4 /* machine address and size of ++ * the ia64_boot_param */ ++#define KEXEC_RANGE_MA_EFI_MEMMAP 5 /* machine address and size of ++ * of the EFI Memory Map */ ++#define KEXEC_RANGE_MA_VMCOREINFO 6 /* machine address and size of vmcoreinfo */ ++ ++/* ++ * Find the address and size of certain memory areas ++ * range == KEXEC_RANGE_... [in] ++ * nr == physical CPU number (starting from 0) if KEXEC_RANGE_MA_CPU [in] ++ * size == number of bytes reserved in window [out] ++ * start == address of the first byte in the window [out] ++ */ ++#define KEXEC_CMD_kexec_get_range 3 ++typedef struct xen_kexec_range { ++ int range; ++ int nr; ++ unsigned long size; ++ unsigned long start; ++} xen_kexec_range_t; ++ ++#endif /* _XEN_PUBLIC_KEXEC_H */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/mem_event.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,69 @@ ++/****************************************************************************** ++ * mem_event.h ++ * ++ * Memory event common structures. ++ * ++ * Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _XEN_PUBLIC_MEM_EVENT_H ++#define _XEN_PUBLIC_MEM_EVENT_H ++ ++ ++#include "xen.h" ++#include "io/ring.h" ++ ++ ++/* Memory event notification modes */ ++#define MEM_EVENT_MODE_ASYNC 0 ++#define MEM_EVENT_MODE_SYNC (1 << 0) ++#define MEM_EVENT_MODE_SYNC_ALL (1 << 1) ++ ++/* Memory event flags */ ++#define MEM_EVENT_FLAG_VCPU_PAUSED (1 << 0) ++#define MEM_EVENT_FLAG_DOM_PAUSED (1 << 1) ++#define MEM_EVENT_FLAG_OUT_OF_MEM (1 << 2) ++ ++ ++typedef struct mem_event_shared_page { ++ int port; ++} mem_event_shared_page_t; ++ ++typedef struct mem_event_st { ++ unsigned long gfn; ++ unsigned long offset; ++ unsigned long p2mt; ++ int vcpu_id; ++ uint64_t flags; ++} mem_event_request_t, mem_event_response_t; ++ ++ ++DEFINE_RING_TYPES(mem_event, mem_event_request_t, mem_event_response_t); ++ ++ ++#endif ++ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/nmi.h 2009-06-23 09:28:21.000000000 +0200 +@@ -0,0 +1,80 @@ ++/****************************************************************************** ++ * nmi.h ++ * ++ * NMI callback registration and reason codes. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2005, Keir Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_NMI_H__ ++#define __XEN_PUBLIC_NMI_H__ ++ ++#include "xen.h" ++ ++/* ++ * NMI reason codes: ++ * Currently these are x86-specific, stored in arch_shared_info.nmi_reason. ++ */ ++ /* I/O-check error reported via ISA port 0x61, bit 6. */ ++#define _XEN_NMIREASON_io_error 0 ++#define XEN_NMIREASON_io_error (1UL << _XEN_NMIREASON_io_error) ++ /* Parity error reported via ISA port 0x61, bit 7. */ ++#define _XEN_NMIREASON_parity_error 1 ++#define XEN_NMIREASON_parity_error (1UL << _XEN_NMIREASON_parity_error) ++ /* Unknown hardware-generated NMI. */ ++#define _XEN_NMIREASON_unknown 2 ++#define XEN_NMIREASON_unknown (1UL << _XEN_NMIREASON_unknown) ++ ++/* ++ * long nmi_op(unsigned int cmd, void *arg) ++ * NB. All ops return zero on success, else a negative error code. ++ */ ++ ++/* ++ * Register NMI callback for this (calling) VCPU. Currently this only makes ++ * sense for domain 0, vcpu 0. All other callers will be returned EINVAL. ++ * arg == pointer to xennmi_callback structure. ++ */ ++#define XENNMI_register_callback 0 ++struct xennmi_callback { ++ unsigned long handler_address; ++ unsigned long pad; ++}; ++typedef struct xennmi_callback xennmi_callback_t; ++DEFINE_XEN_GUEST_HANDLE(xennmi_callback_t); ++ ++/* ++ * Deregister NMI callback for this (calling) VCPU. ++ * arg == NULL. ++ */ ++#define XENNMI_unregister_callback 1 ++ ++#endif /* __XEN_PUBLIC_NMI_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/platform.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,393 @@ ++/****************************************************************************** ++ * platform.h ++ * ++ * Hardware platform operations. Intended for use by domain-0 kernel. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2002-2006, K Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_PLATFORM_H__ ++#define __XEN_PUBLIC_PLATFORM_H__ ++ ++#include "xen.h" ++ ++#define XENPF_INTERFACE_VERSION 0x03000001 ++ ++/* ++ * Set clock such that it would read after 00:00:00 UTC, ++ * 1 January, 1970 if the current system time was . ++ */ ++#define XENPF_settime 17 ++struct xenpf_settime { ++ /* IN variables. */ ++ uint32_t secs; ++ uint32_t nsecs; ++ uint64_t system_time; ++}; ++typedef struct xenpf_settime xenpf_settime_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_settime_t); ++ ++/* ++ * Request memory range (@mfn, @mfn+@nr_mfns-1) to have type @type. ++ * On x86, @type is an architecture-defined MTRR memory type. ++ * On success, returns the MTRR that was used (@reg) and a handle that can ++ * be passed to XENPF_DEL_MEMTYPE to accurately tear down the new setting. ++ * (x86-specific). ++ */ ++#define XENPF_add_memtype 31 ++struct xenpf_add_memtype { ++ /* IN variables. */ ++ xen_pfn_t mfn; ++ uint64_t nr_mfns; ++ uint32_t type; ++ /* OUT variables. */ ++ uint32_t handle; ++ uint32_t reg; ++}; ++typedef struct xenpf_add_memtype xenpf_add_memtype_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_add_memtype_t); ++ ++/* ++ * Tear down an existing memory-range type. If @handle is remembered then it ++ * should be passed in to accurately tear down the correct setting (in case ++ * of overlapping memory regions with differing types). If it is not known ++ * then @handle should be set to zero. In all cases @reg must be set. ++ * (x86-specific). ++ */ ++#define XENPF_del_memtype 32 ++struct xenpf_del_memtype { ++ /* IN variables. */ ++ uint32_t handle; ++ uint32_t reg; ++}; ++typedef struct xenpf_del_memtype xenpf_del_memtype_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_del_memtype_t); ++ ++/* Read current type of an MTRR (x86-specific). */ ++#define XENPF_read_memtype 33 ++struct xenpf_read_memtype { ++ /* IN variables. */ ++ uint32_t reg; ++ /* OUT variables. */ ++ xen_pfn_t mfn; ++ uint64_t nr_mfns; ++ uint32_t type; ++}; ++typedef struct xenpf_read_memtype xenpf_read_memtype_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_read_memtype_t); ++ ++#define XENPF_microcode_update 35 ++struct xenpf_microcode_update { ++ /* IN variables. */ ++ XEN_GUEST_HANDLE(const_void) data;/* Pointer to microcode data */ ++ uint32_t length; /* Length of microcode data. */ ++}; ++typedef struct xenpf_microcode_update xenpf_microcode_update_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_microcode_update_t); ++ ++#define XENPF_platform_quirk 39 ++#define QUIRK_NOIRQBALANCING 1 /* Do not restrict IO-APIC RTE targets */ ++#define QUIRK_IOAPIC_BAD_REGSEL 2 /* IO-APIC REGSEL forgets its value */ ++#define QUIRK_IOAPIC_GOOD_REGSEL 3 /* IO-APIC REGSEL behaves properly */ ++struct xenpf_platform_quirk { ++ /* IN variables. */ ++ uint32_t quirk_id; ++}; ++typedef struct xenpf_platform_quirk xenpf_platform_quirk_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_platform_quirk_t); ++ ++#define XENPF_firmware_info 50 ++#define XEN_FW_DISK_INFO 1 /* from int 13 AH=08/41/48 */ ++#define XEN_FW_DISK_MBR_SIGNATURE 2 /* from MBR offset 0x1b8 */ ++#define XEN_FW_VBEDDC_INFO 3 /* from int 10 AX=4f15 */ ++struct xenpf_firmware_info { ++ /* IN variables. */ ++ uint32_t type; ++ uint32_t index; ++ /* OUT variables. */ ++ union { ++ struct { ++ /* Int13, Fn48: Check Extensions Present. */ ++ uint8_t device; /* %dl: bios device number */ ++ uint8_t version; /* %ah: major version */ ++ uint16_t interface_support; /* %cx: support bitmap */ ++ /* Int13, Fn08: Legacy Get Device Parameters. */ ++ uint16_t legacy_max_cylinder; /* %cl[7:6]:%ch: max cyl # */ ++ uint8_t legacy_max_head; /* %dh: max head # */ ++ uint8_t legacy_sectors_per_track; /* %cl[5:0]: max sector # */ ++ /* Int13, Fn41: Get Device Parameters (as filled into %ds:%esi). */ ++ /* NB. First uint16_t of buffer must be set to buffer size. */ ++ XEN_GUEST_HANDLE(void) edd_params; ++ } disk_info; /* XEN_FW_DISK_INFO */ ++ struct { ++ uint8_t device; /* bios device number */ ++ uint32_t mbr_signature; /* offset 0x1b8 in mbr */ ++ } disk_mbr_signature; /* XEN_FW_DISK_MBR_SIGNATURE */ ++ struct { ++ /* Int10, AX=4F15: Get EDID info. */ ++ uint8_t capabilities; ++ uint8_t edid_transfer_time; ++ /* must refer to 128-byte buffer */ ++ XEN_GUEST_HANDLE(uint8) edid; ++ } vbeddc_info; /* XEN_FW_VBEDDC_INFO */ ++ } u; ++}; ++typedef struct xenpf_firmware_info xenpf_firmware_info_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_firmware_info_t); ++ ++#define XENPF_enter_acpi_sleep 51 ++struct xenpf_enter_acpi_sleep { ++ /* IN variables */ ++ uint16_t pm1a_cnt_val; /* PM1a control value. */ ++ uint16_t pm1b_cnt_val; /* PM1b control value. */ ++ uint32_t sleep_state; /* Which state to enter (Sn). */ ++ uint32_t flags; /* Must be zero. */ ++}; ++typedef struct xenpf_enter_acpi_sleep xenpf_enter_acpi_sleep_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_enter_acpi_sleep_t); ++ ++#define XENPF_change_freq 52 ++struct xenpf_change_freq { ++ /* IN variables */ ++ uint32_t flags; /* Must be zero. */ ++ uint32_t cpu; /* Physical cpu. */ ++ uint64_t freq; /* New frequency (Hz). */ ++}; ++typedef struct xenpf_change_freq xenpf_change_freq_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_change_freq_t); ++ ++/* ++ * Get idle times (nanoseconds since boot) for physical CPUs specified in the ++ * @cpumap_bitmap with range [0..@cpumap_nr_cpus-1]. The @idletime array is ++ * indexed by CPU number; only entries with the corresponding @cpumap_bitmap ++ * bit set are written to. On return, @cpumap_bitmap is modified so that any ++ * non-existent CPUs are cleared. Such CPUs have their @idletime array entry ++ * cleared. ++ */ ++#define XENPF_getidletime 53 ++struct xenpf_getidletime { ++ /* IN/OUT variables */ ++ /* IN: CPUs to interrogate; OUT: subset of IN which are present */ ++ XEN_GUEST_HANDLE(uint8) cpumap_bitmap; ++ /* IN variables */ ++ /* Size of cpumap bitmap. */ ++ uint32_t cpumap_nr_cpus; ++ /* Must be indexable for every cpu in cpumap_bitmap. */ ++ XEN_GUEST_HANDLE(uint64) idletime; ++ /* OUT variables */ ++ /* System time when the idletime snapshots were taken. */ ++ uint64_t now; ++}; ++typedef struct xenpf_getidletime xenpf_getidletime_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_getidletime_t); ++ ++#define XENPF_set_processor_pminfo 54 ++ ++/* ability bits */ ++#define XEN_PROCESSOR_PM_CX 1 ++#define XEN_PROCESSOR_PM_PX 2 ++#define XEN_PROCESSOR_PM_TX 4 ++ ++/* cmd type */ ++#define XEN_PM_CX 0 ++#define XEN_PM_PX 1 ++#define XEN_PM_TX 2 ++ ++/* Px sub info type */ ++#define XEN_PX_PCT 1 ++#define XEN_PX_PSS 2 ++#define XEN_PX_PPC 4 ++#define XEN_PX_PSD 8 ++ ++struct xen_power_register { ++ uint32_t space_id; ++ uint32_t bit_width; ++ uint32_t bit_offset; ++ uint32_t access_size; ++ uint64_t address; ++}; ++ ++struct xen_processor_csd { ++ uint32_t domain; /* domain number of one dependent group */ ++ uint32_t coord_type; /* coordination type */ ++ uint32_t num; /* number of processors in same domain */ ++}; ++typedef struct xen_processor_csd xen_processor_csd_t; ++DEFINE_XEN_GUEST_HANDLE(xen_processor_csd_t); ++ ++struct xen_processor_cx { ++ struct xen_power_register reg; /* GAS for Cx trigger register */ ++ uint8_t type; /* cstate value, c0: 0, c1: 1, ... */ ++ uint32_t latency; /* worst latency (ms) to enter/exit this cstate */ ++ uint32_t power; /* average power consumption(mW) */ ++ uint32_t dpcnt; /* number of dependency entries */ ++ XEN_GUEST_HANDLE(xen_processor_csd_t) dp; /* NULL if no dependency */ ++}; ++typedef struct xen_processor_cx xen_processor_cx_t; ++DEFINE_XEN_GUEST_HANDLE(xen_processor_cx_t); ++ ++struct xen_processor_flags { ++ uint32_t bm_control:1; ++ uint32_t bm_check:1; ++ uint32_t has_cst:1; ++ uint32_t power_setup_done:1; ++ uint32_t bm_rld_set:1; ++}; ++ ++struct xen_processor_power { ++ uint32_t count; /* number of C state entries in array below */ ++ struct xen_processor_flags flags; /* global flags of this processor */ ++ XEN_GUEST_HANDLE(xen_processor_cx_t) states; /* supported c states */ ++}; ++ ++struct xen_pct_register { ++ uint8_t descriptor; ++ uint16_t length; ++ uint8_t space_id; ++ uint8_t bit_width; ++ uint8_t bit_offset; ++ uint8_t reserved; ++ uint64_t address; ++}; ++ ++struct xen_processor_px { ++ uint64_t core_frequency; /* megahertz */ ++ uint64_t power; /* milliWatts */ ++ uint64_t transition_latency; /* microseconds */ ++ uint64_t bus_master_latency; /* microseconds */ ++ uint64_t control; /* control value */ ++ uint64_t status; /* success indicator */ ++}; ++typedef struct xen_processor_px xen_processor_px_t; ++DEFINE_XEN_GUEST_HANDLE(xen_processor_px_t); ++ ++struct xen_psd_package { ++ uint64_t num_entries; ++ uint64_t revision; ++ uint64_t domain; ++ uint64_t coord_type; ++ uint64_t num_processors; ++}; ++ ++struct xen_processor_performance { ++ uint32_t flags; /* flag for Px sub info type */ ++ uint32_t platform_limit; /* Platform limitation on freq usage */ ++ struct xen_pct_register control_register; ++ struct xen_pct_register status_register; ++ uint32_t state_count; /* total available performance states */ ++ XEN_GUEST_HANDLE(xen_processor_px_t) states; ++ struct xen_psd_package domain_info; ++ uint32_t shared_type; /* coordination type of this processor */ ++}; ++typedef struct xen_processor_performance xen_processor_performance_t; ++DEFINE_XEN_GUEST_HANDLE(xen_processor_performance_t); ++ ++struct xenpf_set_processor_pminfo { ++ /* IN variables */ ++ uint32_t id; /* ACPI CPU ID */ ++ uint32_t type; /* {XEN_PM_CX, XEN_PM_PX} */ ++ union { ++ struct xen_processor_power power;/* Cx: _CST/_CSD */ ++ struct xen_processor_performance perf; /* Px: _PPC/_PCT/_PSS/_PSD */ ++ } u; ++}; ++typedef struct xenpf_set_processor_pminfo xenpf_set_processor_pminfo_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_set_processor_pminfo_t); ++ ++#define XENPF_get_cpuinfo 55 ++struct xenpf_pcpuinfo { ++ /* IN */ ++ uint32_t xen_cpuid; ++ /* OUT */ ++ /* The maxium cpu_id that is present */ ++ uint32_t max_present; ++#define XEN_PCPU_FLAGS_ONLINE 1 ++ /* Correponding xen_cpuid is not present*/ ++#define XEN_PCPU_FLAGS_INVALID 2 ++ uint32_t flags; ++ uint32_t apic_id; ++ uint32_t acpi_id; ++}; ++typedef struct xenpf_pcpuinfo xenpf_pcpuinfo_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_pcpuinfo_t); ++ ++#define XENPF_cpu_online 56 ++#define XENPF_cpu_offline 57 ++struct xenpf_cpu_ol ++{ ++ uint32_t cpuid; ++}; ++typedef struct xenpf_cpu_ol xenpf_cpu_ol_t; ++DEFINE_XEN_GUEST_HANDLE(xenpf_cpu_ol_t); ++ ++#define XENPF_cpu_hotadd 58 ++struct xenpf_cpu_hotadd ++{ ++ uint32_t apic_id; ++ uint32_t acpi_id; ++ uint32_t pxm; ++}; ++ ++#define XENPF_mem_hotadd 59 ++struct xenpf_mem_hotadd ++{ ++ uint64_t spfn; ++ uint64_t epfn; ++ uint32_t pxm; ++ uint32_t flags; ++}; ++ ++struct xen_platform_op { ++ uint32_t cmd; ++ uint32_t interface_version; /* XENPF_INTERFACE_VERSION */ ++ union { ++ struct xenpf_settime settime; ++ struct xenpf_add_memtype add_memtype; ++ struct xenpf_del_memtype del_memtype; ++ struct xenpf_read_memtype read_memtype; ++ struct xenpf_microcode_update microcode; ++ struct xenpf_platform_quirk platform_quirk; ++ struct xenpf_firmware_info firmware_info; ++ struct xenpf_enter_acpi_sleep enter_acpi_sleep; ++ struct xenpf_change_freq change_freq; ++ struct xenpf_getidletime getidletime; ++ struct xenpf_set_processor_pminfo set_pminfo; ++ struct xenpf_pcpuinfo pcpu_info; ++ struct xenpf_cpu_ol cpu_ol; ++ struct xenpf_cpu_hotadd cpu_add; ++ struct xenpf_mem_hotadd mem_add; ++ uint8_t pad[128]; ++ } u; ++}; ++typedef struct xen_platform_op xen_platform_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_platform_op_t); ++ ++#endif /* __XEN_PUBLIC_PLATFORM_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/sysctl.h 2010-05-07 11:10:48.000000000 +0200 +@@ -0,0 +1,607 @@ ++/****************************************************************************** ++ * sysctl.h ++ * ++ * System management operations. For use by node control stack. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2002-2006, K Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_SYSCTL_H__ ++#define __XEN_PUBLIC_SYSCTL_H__ ++ ++#if !defined(__XEN__) && !defined(__XEN_TOOLS__) ++#error "sysctl operations are intended for use by node control tools only" ++#endif ++ ++#include "xen.h" ++#include "domctl.h" ++ ++#define XEN_SYSCTL_INTERFACE_VERSION 0x00000008 ++ ++/* ++ * Read console content from Xen buffer ring. ++ */ ++/* XEN_SYSCTL_readconsole */ ++struct xen_sysctl_readconsole { ++ /* IN: Non-zero -> clear after reading. */ ++ uint8_t clear; ++ /* IN: Non-zero -> start index specified by @index field. */ ++ uint8_t incremental; ++ uint8_t pad0, pad1; ++ /* ++ * IN: Start index for consuming from ring buffer (if @incremental); ++ * OUT: End index after consuming from ring buffer. ++ */ ++ uint32_t index; ++ /* IN: Virtual address to write console data. */ ++ XEN_GUEST_HANDLE_64(char) buffer; ++ /* IN: Size of buffer; OUT: Bytes written to buffer. */ ++ uint32_t count; ++}; ++typedef struct xen_sysctl_readconsole xen_sysctl_readconsole_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_readconsole_t); ++ ++/* Get trace buffers machine base address */ ++/* XEN_SYSCTL_tbuf_op */ ++struct xen_sysctl_tbuf_op { ++ /* IN variables */ ++#define XEN_SYSCTL_TBUFOP_get_info 0 ++#define XEN_SYSCTL_TBUFOP_set_cpu_mask 1 ++#define XEN_SYSCTL_TBUFOP_set_evt_mask 2 ++#define XEN_SYSCTL_TBUFOP_set_size 3 ++#define XEN_SYSCTL_TBUFOP_enable 4 ++#define XEN_SYSCTL_TBUFOP_disable 5 ++ uint32_t cmd; ++ /* IN/OUT variables */ ++ struct xenctl_cpumap cpu_mask; ++ uint32_t evt_mask; ++ /* OUT variables */ ++ uint64_aligned_t buffer_mfn; ++ uint32_t size; /* Also an IN variable! */ ++}; ++typedef struct xen_sysctl_tbuf_op xen_sysctl_tbuf_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_tbuf_op_t); ++ ++/* ++ * Get physical information about the host machine ++ */ ++/* XEN_SYSCTL_physinfo */ ++ /* (x86) The platform supports HVM guests. */ ++#define _XEN_SYSCTL_PHYSCAP_hvm 0 ++#define XEN_SYSCTL_PHYSCAP_hvm (1u<<_XEN_SYSCTL_PHYSCAP_hvm) ++ /* (x86) The platform supports HVM-guest direct access to I/O devices. */ ++#define _XEN_SYSCTL_PHYSCAP_hvm_directio 1 ++#define XEN_SYSCTL_PHYSCAP_hvm_directio (1u<<_XEN_SYSCTL_PHYSCAP_hvm_directio) ++struct xen_sysctl_physinfo { ++ uint32_t threads_per_core; ++ uint32_t cores_per_socket; ++ uint32_t nr_cpus, max_cpu_id; ++ uint32_t nr_nodes, max_node_id; ++ uint32_t cpu_khz; ++ uint64_aligned_t total_pages; ++ uint64_aligned_t free_pages; ++ uint64_aligned_t scrub_pages; ++ uint32_t hw_cap[8]; ++ ++ /* XEN_SYSCTL_PHYSCAP_??? */ ++ uint32_t capabilities; ++}; ++typedef struct xen_sysctl_physinfo xen_sysctl_physinfo_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_physinfo_t); ++ ++/* ++ * Get the ID of the current scheduler. ++ */ ++/* XEN_SYSCTL_sched_id */ ++struct xen_sysctl_sched_id { ++ /* OUT variable */ ++ uint32_t sched_id; ++}; ++typedef struct xen_sysctl_sched_id xen_sysctl_sched_id_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_sched_id_t); ++ ++/* Interface for controlling Xen software performance counters. */ ++/* XEN_SYSCTL_perfc_op */ ++/* Sub-operations: */ ++#define XEN_SYSCTL_PERFCOP_reset 1 /* Reset all counters to zero. */ ++#define XEN_SYSCTL_PERFCOP_query 2 /* Get perfctr information. */ ++struct xen_sysctl_perfc_desc { ++ char name[80]; /* name of perf counter */ ++ uint32_t nr_vals; /* number of values for this counter */ ++}; ++typedef struct xen_sysctl_perfc_desc xen_sysctl_perfc_desc_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_perfc_desc_t); ++typedef uint32_t xen_sysctl_perfc_val_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_perfc_val_t); ++ ++struct xen_sysctl_perfc_op { ++ /* IN variables. */ ++ uint32_t cmd; /* XEN_SYSCTL_PERFCOP_??? */ ++ /* OUT variables. */ ++ uint32_t nr_counters; /* number of counters description */ ++ uint32_t nr_vals; /* number of values */ ++ /* counter information (or NULL) */ ++ XEN_GUEST_HANDLE_64(xen_sysctl_perfc_desc_t) desc; ++ /* counter values (or NULL) */ ++ XEN_GUEST_HANDLE_64(xen_sysctl_perfc_val_t) val; ++}; ++typedef struct xen_sysctl_perfc_op xen_sysctl_perfc_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_perfc_op_t); ++ ++/* XEN_SYSCTL_getdomaininfolist */ ++struct xen_sysctl_getdomaininfolist { ++ /* IN variables. */ ++ domid_t first_domain; ++ uint32_t max_domains; ++ XEN_GUEST_HANDLE_64(xen_domctl_getdomaininfo_t) buffer; ++ /* OUT variables. */ ++ uint32_t num_domains; ++}; ++typedef struct xen_sysctl_getdomaininfolist xen_sysctl_getdomaininfolist_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_getdomaininfolist_t); ++ ++/* Inject debug keys into Xen. */ ++/* XEN_SYSCTL_debug_keys */ ++struct xen_sysctl_debug_keys { ++ /* IN variables. */ ++ XEN_GUEST_HANDLE_64(char) keys; ++ uint32_t nr_keys; ++}; ++typedef struct xen_sysctl_debug_keys xen_sysctl_debug_keys_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_debug_keys_t); ++ ++/* Get physical CPU information. */ ++/* XEN_SYSCTL_getcpuinfo */ ++struct xen_sysctl_cpuinfo { ++ uint64_aligned_t idletime; ++}; ++typedef struct xen_sysctl_cpuinfo xen_sysctl_cpuinfo_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_cpuinfo_t); ++struct xen_sysctl_getcpuinfo { ++ /* IN variables. */ ++ uint32_t max_cpus; ++ XEN_GUEST_HANDLE_64(xen_sysctl_cpuinfo_t) info; ++ /* OUT variables. */ ++ uint32_t nr_cpus; ++}; ++typedef struct xen_sysctl_getcpuinfo xen_sysctl_getcpuinfo_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_getcpuinfo_t); ++ ++/* XEN_SYSCTL_availheap */ ++struct xen_sysctl_availheap { ++ /* IN variables. */ ++ uint32_t min_bitwidth; /* Smallest address width (zero if don't care). */ ++ uint32_t max_bitwidth; /* Largest address width (zero if don't care). */ ++ int32_t node; /* NUMA node of interest (-1 for all nodes). */ ++ /* OUT variables. */ ++ uint64_aligned_t avail_bytes;/* Bytes available in the specified region. */ ++}; ++typedef struct xen_sysctl_availheap xen_sysctl_availheap_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_availheap_t); ++ ++/* XEN_SYSCTL_get_pmstat */ ++struct pm_px_val { ++ uint64_aligned_t freq; /* Px core frequency */ ++ uint64_aligned_t residency; /* Px residency time */ ++ uint64_aligned_t count; /* Px transition count */ ++}; ++typedef struct pm_px_val pm_px_val_t; ++DEFINE_XEN_GUEST_HANDLE(pm_px_val_t); ++ ++struct pm_px_stat { ++ uint8_t total; /* total Px states */ ++ uint8_t usable; /* usable Px states */ ++ uint8_t last; /* last Px state */ ++ uint8_t cur; /* current Px state */ ++ XEN_GUEST_HANDLE_64(uint64) trans_pt; /* Px transition table */ ++ XEN_GUEST_HANDLE_64(pm_px_val_t) pt; ++}; ++typedef struct pm_px_stat pm_px_stat_t; ++DEFINE_XEN_GUEST_HANDLE(pm_px_stat_t); ++ ++struct pm_cx_stat { ++ uint32_t nr; /* entry nr in triggers & residencies, including C0 */ ++ uint32_t last; /* last Cx state */ ++ uint64_aligned_t idle_time; /* idle time from boot */ ++ XEN_GUEST_HANDLE_64(uint64) triggers; /* Cx trigger counts */ ++ XEN_GUEST_HANDLE_64(uint64) residencies; /* Cx residencies */ ++}; ++ ++struct xen_sysctl_get_pmstat { ++#define PMSTAT_CATEGORY_MASK 0xf0 ++#define PMSTAT_PX 0x10 ++#define PMSTAT_CX 0x20 ++#define PMSTAT_get_max_px (PMSTAT_PX | 0x1) ++#define PMSTAT_get_pxstat (PMSTAT_PX | 0x2) ++#define PMSTAT_reset_pxstat (PMSTAT_PX | 0x3) ++#define PMSTAT_get_max_cx (PMSTAT_CX | 0x1) ++#define PMSTAT_get_cxstat (PMSTAT_CX | 0x2) ++#define PMSTAT_reset_cxstat (PMSTAT_CX | 0x3) ++ uint32_t type; ++ uint32_t cpuid; ++ union { ++ struct pm_px_stat getpx; ++ struct pm_cx_stat getcx; ++ /* other struct for tx, etc */ ++ } u; ++}; ++typedef struct xen_sysctl_get_pmstat xen_sysctl_get_pmstat_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_get_pmstat_t); ++ ++/* ++ * Status codes. Must be greater than 0 to avoid confusing ++ * sysctl callers that see 0 as a plain successful return. ++ */ ++#define XEN_CPU_HOTPLUG_STATUS_OFFLINE 1 ++#define XEN_CPU_HOTPLUG_STATUS_ONLINE 2 ++#define XEN_CPU_HOTPLUG_STATUS_NEW 3 ++ ++/* XEN_SYSCTL_cpu_hotplug */ ++struct xen_sysctl_cpu_hotplug { ++ /* IN variables */ ++ uint32_t cpu; /* Physical cpu. */ ++#define XEN_SYSCTL_CPU_HOTPLUG_ONLINE 0 ++#define XEN_SYSCTL_CPU_HOTPLUG_OFFLINE 1 ++#define XEN_SYSCTL_CPU_HOTPLUG_STATUS 2 ++ uint32_t op; /* hotplug opcode */ ++}; ++typedef struct xen_sysctl_cpu_hotplug xen_sysctl_cpu_hotplug_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_cpu_hotplug_t); ++ ++/* ++ * Get/set xen power management, include ++ * 1. cpufreq governors and related parameters ++ */ ++/* XEN_SYSCTL_pm_op */ ++struct xen_userspace { ++ uint32_t scaling_setspeed; ++}; ++typedef struct xen_userspace xen_userspace_t; ++ ++struct xen_ondemand { ++ uint32_t sampling_rate_max; ++ uint32_t sampling_rate_min; ++ ++ uint32_t sampling_rate; ++ uint32_t up_threshold; ++}; ++typedef struct xen_ondemand xen_ondemand_t; ++ ++/* ++ * cpufreq para name of this structure named ++ * same as sysfs file name of native linux ++ */ ++#define CPUFREQ_NAME_LEN 16 ++struct xen_get_cpufreq_para { ++ /* IN/OUT variable */ ++ uint32_t cpu_num; ++ uint32_t freq_num; ++ uint32_t gov_num; ++ ++ /* for all governors */ ++ /* OUT variable */ ++ XEN_GUEST_HANDLE_64(uint32) affected_cpus; ++ XEN_GUEST_HANDLE_64(uint32) scaling_available_frequencies; ++ XEN_GUEST_HANDLE_64(char) scaling_available_governors; ++ char scaling_driver[CPUFREQ_NAME_LEN]; ++ ++ uint32_t cpuinfo_cur_freq; ++ uint32_t cpuinfo_max_freq; ++ uint32_t cpuinfo_min_freq; ++ uint32_t scaling_cur_freq; ++ ++ char scaling_governor[CPUFREQ_NAME_LEN]; ++ uint32_t scaling_max_freq; ++ uint32_t scaling_min_freq; ++ ++ /* for specific governor */ ++ union { ++ struct xen_userspace userspace; ++ struct xen_ondemand ondemand; ++ } u; ++ ++ int32_t turbo_enabled; ++}; ++ ++struct xen_set_cpufreq_gov { ++ char scaling_governor[CPUFREQ_NAME_LEN]; ++}; ++ ++struct xen_set_cpufreq_para { ++ #define SCALING_MAX_FREQ 1 ++ #define SCALING_MIN_FREQ 2 ++ #define SCALING_SETSPEED 3 ++ #define SAMPLING_RATE 4 ++ #define UP_THRESHOLD 5 ++ ++ uint32_t ctrl_type; ++ uint32_t ctrl_value; ++}; ++ ++struct xen_sysctl_pm_op { ++ #define PM_PARA_CATEGORY_MASK 0xf0 ++ #define CPUFREQ_PARA 0x10 ++ ++ /* cpufreq command type */ ++ #define GET_CPUFREQ_PARA (CPUFREQ_PARA | 0x01) ++ #define SET_CPUFREQ_GOV (CPUFREQ_PARA | 0x02) ++ #define SET_CPUFREQ_PARA (CPUFREQ_PARA | 0x03) ++ #define GET_CPUFREQ_AVGFREQ (CPUFREQ_PARA | 0x04) ++ ++ /* set/reset scheduler power saving option */ ++ #define XEN_SYSCTL_pm_op_set_sched_opt_smt 0x21 ++ ++ /* cpuidle max_cstate access command */ ++ #define XEN_SYSCTL_pm_op_get_max_cstate 0x22 ++ #define XEN_SYSCTL_pm_op_set_max_cstate 0x23 ++ ++ /* set scheduler migration cost value */ ++ #define XEN_SYSCTL_pm_op_set_vcpu_migration_delay 0x24 ++ #define XEN_SYSCTL_pm_op_get_vcpu_migration_delay 0x25 ++ ++ /* enable/disable turbo mode when in dbs governor */ ++ #define XEN_SYSCTL_pm_op_enable_turbo 0x26 ++ #define XEN_SYSCTL_pm_op_disable_turbo 0x27 ++ ++ uint32_t cmd; ++ uint32_t cpuid; ++ union { ++ struct xen_get_cpufreq_para get_para; ++ struct xen_set_cpufreq_gov set_gov; ++ struct xen_set_cpufreq_para set_para; ++ uint64_aligned_t get_avgfreq; ++ uint32_t set_sched_opt_smt; ++ uint32_t get_max_cstate; ++ uint32_t set_max_cstate; ++ uint32_t get_vcpu_migration_delay; ++ uint32_t set_vcpu_migration_delay; ++ } u; ++}; ++ ++/* XEN_SYSCTL_page_offline_op */ ++struct xen_sysctl_page_offline_op { ++ /* IN: range of page to be offlined */ ++#define sysctl_page_offline 1 ++#define sysctl_page_online 2 ++#define sysctl_query_page_offline 3 ++ uint32_t cmd; ++ uint32_t start; ++ uint32_t end; ++ /* OUT: result of page offline request */ ++ /* ++ * bit 0~15: result flags ++ * bit 16~31: owner ++ */ ++ XEN_GUEST_HANDLE(uint32) status; ++}; ++ ++#define PG_OFFLINE_STATUS_MASK (0xFFUL) ++ ++/* The result is invalid, i.e. HV does not handle it */ ++#define PG_OFFLINE_INVALID (0x1UL << 0) ++ ++#define PG_OFFLINE_OFFLINED (0x1UL << 1) ++#define PG_OFFLINE_PENDING (0x1UL << 2) ++#define PG_OFFLINE_FAILED (0x1UL << 3) ++ ++#define PG_ONLINE_FAILED PG_OFFLINE_FAILED ++#define PG_ONLINE_ONLINED PG_OFFLINE_OFFLINED ++ ++#define PG_OFFLINE_STATUS_OFFLINED (0x1UL << 1) ++#define PG_OFFLINE_STATUS_ONLINE (0x1UL << 2) ++#define PG_OFFLINE_STATUS_OFFLINE_PENDING (0x1UL << 3) ++#define PG_OFFLINE_STATUS_BROKEN (0x1UL << 4) ++ ++#define PG_OFFLINE_MISC_MASK (0xFFUL << 4) ++ ++/* only valid when PG_OFFLINE_FAILED */ ++#define PG_OFFLINE_XENPAGE (0x1UL << 8) ++#define PG_OFFLINE_DOM0PAGE (0x1UL << 9) ++#define PG_OFFLINE_ANONYMOUS (0x1UL << 10) ++#define PG_OFFLINE_NOT_CONV_RAM (0x1UL << 11) ++#define PG_OFFLINE_OWNED (0x1UL << 12) ++ ++#define PG_OFFLINE_BROKEN (0x1UL << 13) ++#define PG_ONLINE_BROKEN PG_OFFLINE_BROKEN ++ ++#define PG_OFFLINE_OWNER_SHIFT 16 ++ ++/* XEN_SYSCTL_lockprof_op */ ++/* Sub-operations: */ ++#define XEN_SYSCTL_LOCKPROF_reset 1 /* Reset all profile data to zero. */ ++#define XEN_SYSCTL_LOCKPROF_query 2 /* Get lock profile information. */ ++/* Record-type: */ ++#define LOCKPROF_TYPE_GLOBAL 0 /* global lock, idx meaningless */ ++#define LOCKPROF_TYPE_PERDOM 1 /* per-domain lock, idx is domid */ ++#define LOCKPROF_TYPE_N 2 /* number of types */ ++struct xen_sysctl_lockprof_data { ++ char name[40]; /* lock name (may include up to 2 %d specifiers) */ ++ int32_t type; /* LOCKPROF_TYPE_??? */ ++ int32_t idx; /* index (e.g. domain id) */ ++ uint64_aligned_t lock_cnt; /* # of locking succeeded */ ++ uint64_aligned_t block_cnt; /* # of wait for lock */ ++ uint64_aligned_t lock_time; /* nsecs lock held */ ++ uint64_aligned_t block_time; /* nsecs waited for lock */ ++}; ++typedef struct xen_sysctl_lockprof_data xen_sysctl_lockprof_data_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_lockprof_data_t); ++struct xen_sysctl_lockprof_op { ++ /* IN variables. */ ++ uint32_t cmd; /* XEN_SYSCTL_LOCKPROF_??? */ ++ uint32_t max_elem; /* size of output buffer */ ++ /* OUT variables (query only). */ ++ uint32_t nr_elem; /* number of elements available */ ++ uint64_aligned_t time; /* nsecs of profile measurement */ ++ /* profile information (or NULL) */ ++ XEN_GUEST_HANDLE_64(xen_sysctl_lockprof_data_t) data; ++}; ++typedef struct xen_sysctl_lockprof_op xen_sysctl_lockprof_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_lockprof_op_t); ++ ++/* XEN_SYSCTL_topologyinfo */ ++#define INVALID_TOPOLOGY_ID (~0U) ++struct xen_sysctl_topologyinfo { ++ /* ++ * IN: maximum addressable entry in the caller-provided arrays. ++ * OUT: largest cpu identifier in the system. ++ * If OUT is greater than IN then the arrays are truncated! ++ */ ++ uint32_t max_cpu_index; ++ ++ /* ++ * If not NULL, this array is filled with core/socket/node identifier for ++ * each cpu. ++ * If a cpu has no core/socket/node information (e.g., cpu not present) ++ * then the sentinel value ~0u is written. ++ * The size of this array is specified by the caller in @max_cpu_index. ++ * If the actual @max_cpu_index is smaller than the array then the trailing ++ * elements of the array will not be written by the sysctl. ++ */ ++ XEN_GUEST_HANDLE_64(uint32) cpu_to_core; ++ XEN_GUEST_HANDLE_64(uint32) cpu_to_socket; ++ XEN_GUEST_HANDLE_64(uint32) cpu_to_node; ++}; ++typedef struct xen_sysctl_topologyinfo xen_sysctl_topologyinfo_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_topologyinfo_t); ++ ++/* XEN_SYSCTL_numainfo */ ++struct xen_sysctl_numainfo { ++ /* ++ * IN: maximum addressable entry in the caller-provided arrays. ++ * OUT: largest node identifier in the system. ++ * If OUT is greater than IN then the arrays are truncated! ++ */ ++ uint32_t max_node_index; ++ ++ /* NB. Entries are 0 if node is not present. */ ++ XEN_GUEST_HANDLE_64(uint64) node_to_memsize; ++ XEN_GUEST_HANDLE_64(uint64) node_to_memfree; ++ ++ /* ++ * Array, of size (max_node_index+1)^2, listing memory access distances ++ * between nodes. If an entry has no node distance information (e.g., node ++ * not present) then the value ~0u is written. ++ * ++ * Note that the array rows must be indexed by multiplying by the minimum ++ * of the caller-provided max_node_index and the returned value of ++ * max_node_index. That is, if the largest node index in the system is ++ * smaller than the caller can handle, a smaller 2-d array is constructed ++ * within the space provided by the caller. When this occurs, trailing ++ * space provided by the caller is not modified. If the largest node index ++ * in the system is larger than the caller can handle, then a 2-d array of ++ * the maximum size handleable by the caller is constructed. ++ */ ++ XEN_GUEST_HANDLE_64(uint32) node_to_node_distance; ++}; ++typedef struct xen_sysctl_numainfo xen_sysctl_numainfo_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_numainfo_t); ++ ++/* XEN_SYSCTL_cpupool_op */ ++#define XEN_SYSCTL_CPUPOOL_OP_CREATE 1 /* C */ ++#define XEN_SYSCTL_CPUPOOL_OP_DESTROY 2 /* D */ ++#define XEN_SYSCTL_CPUPOOL_OP_INFO 3 /* I */ ++#define XEN_SYSCTL_CPUPOOL_OP_ADDCPU 4 /* A */ ++#define XEN_SYSCTL_CPUPOOL_OP_RMCPU 5 /* R */ ++#define XEN_SYSCTL_CPUPOOL_OP_MOVEDOMAIN 6 /* M */ ++#define XEN_SYSCTL_CPUPOOL_OP_FREEINFO 7 /* F */ ++#define XEN_SYSCTL_CPUPOOL_PAR_ANY 0xFFFFFFFF ++struct xen_sysctl_cpupool_op { ++ uint32_t op; /* IN */ ++ uint32_t cpupool_id; /* IN: CDIARM OUT: CI */ ++ uint32_t sched_id; /* IN: C OUT: I */ ++ uint32_t domid; /* IN: M */ ++ uint32_t cpu; /* IN: AR */ ++ uint32_t n_dom; /* OUT: I */ ++ struct xenctl_cpumap cpumap; /* OUT: IF */ ++}; ++typedef struct xen_sysctl_cpupool_op xen_sysctl_cpupool_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_cpupool_op_t); ++ ++/* XEN_SYSCTL_scheduler_op */ ++/* Set or get info? */ ++#define XEN_SYSCTL_SCHEDOP_putinfo 0 ++#define XEN_SYSCTL_SCHEDOP_getinfo 1 ++struct xen_sysctl_scheduler_op { ++ uint32_t sched_id; /* XEN_SCHEDULER_* (domctl.h) */ ++ uint32_t cmd; /* XEN_SYSCTL_SCHEDOP_* */ ++ union { ++ } u; ++}; ++typedef struct xen_sysctl_scheduler_op xen_sysctl_scheduler_op_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_scheduler_op_t); ++ ++struct xen_sysctl { ++ uint32_t cmd; ++#define XEN_SYSCTL_readconsole 1 ++#define XEN_SYSCTL_tbuf_op 2 ++#define XEN_SYSCTL_physinfo 3 ++#define XEN_SYSCTL_sched_id 4 ++#define XEN_SYSCTL_perfc_op 5 ++#define XEN_SYSCTL_getdomaininfolist 6 ++#define XEN_SYSCTL_debug_keys 7 ++#define XEN_SYSCTL_getcpuinfo 8 ++#define XEN_SYSCTL_availheap 9 ++#define XEN_SYSCTL_get_pmstat 10 ++#define XEN_SYSCTL_cpu_hotplug 11 ++#define XEN_SYSCTL_pm_op 12 ++#define XEN_SYSCTL_page_offline_op 14 ++#define XEN_SYSCTL_lockprof_op 15 ++#define XEN_SYSCTL_topologyinfo 16 ++#define XEN_SYSCTL_numainfo 17 ++#define XEN_SYSCTL_cpupool_op 18 ++#define XEN_SYSCTL_scheduler_op 19 ++ uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */ ++ union { ++ struct xen_sysctl_readconsole readconsole; ++ struct xen_sysctl_tbuf_op tbuf_op; ++ struct xen_sysctl_physinfo physinfo; ++ struct xen_sysctl_topologyinfo topologyinfo; ++ struct xen_sysctl_numainfo numainfo; ++ struct xen_sysctl_sched_id sched_id; ++ struct xen_sysctl_perfc_op perfc_op; ++ struct xen_sysctl_getdomaininfolist getdomaininfolist; ++ struct xen_sysctl_debug_keys debug_keys; ++ struct xen_sysctl_getcpuinfo getcpuinfo; ++ struct xen_sysctl_availheap availheap; ++ struct xen_sysctl_get_pmstat get_pmstat; ++ struct xen_sysctl_cpu_hotplug cpu_hotplug; ++ struct xen_sysctl_pm_op pm_op; ++ struct xen_sysctl_page_offline_op page_offline; ++ struct xen_sysctl_lockprof_op lockprof_op; ++ struct xen_sysctl_cpupool_op cpupool_op; ++ struct xen_sysctl_scheduler_op scheduler_op; ++ uint8_t pad[128]; ++ } u; ++}; ++typedef struct xen_sysctl xen_sysctl_t; ++DEFINE_XEN_GUEST_HANDLE(xen_sysctl_t); ++ ++#endif /* __XEN_PUBLIC_SYSCTL_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/tmem.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,144 @@ ++/****************************************************************************** ++ * tmem.h ++ * ++ * Guest OS interface to Xen Transcendent Memory. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2004, K A Fraser ++ */ ++ ++#ifndef __XEN_PUBLIC_TMEM_H__ ++#define __XEN_PUBLIC_TMEM_H__ ++ ++#include "xen.h" ++ ++/* Commands to HYPERVISOR_tmem_op() */ ++#define TMEM_CONTROL 0 ++#define TMEM_NEW_POOL 1 ++#define TMEM_DESTROY_POOL 2 ++#define TMEM_NEW_PAGE 3 ++#define TMEM_PUT_PAGE 4 ++#define TMEM_GET_PAGE 5 ++#define TMEM_FLUSH_PAGE 6 ++#define TMEM_FLUSH_OBJECT 7 ++#define TMEM_READ 8 ++#define TMEM_WRITE 9 ++#define TMEM_XCHG 10 ++ ++/* Privileged commands to HYPERVISOR_tmem_op() */ ++#define TMEM_AUTH 101 ++#define TMEM_RESTORE_NEW 102 ++ ++/* Subops for HYPERVISOR_tmem_op(TMEM_CONTROL) */ ++#define TMEMC_THAW 0 ++#define TMEMC_FREEZE 1 ++#define TMEMC_FLUSH 2 ++#define TMEMC_DESTROY 3 ++#define TMEMC_LIST 4 ++#define TMEMC_SET_WEIGHT 5 ++#define TMEMC_SET_CAP 6 ++#define TMEMC_SET_COMPRESS 7 ++#define TMEMC_QUERY_FREEABLE_MB 8 ++#define TMEMC_SAVE_BEGIN 10 ++#define TMEMC_SAVE_GET_VERSION 11 ++#define TMEMC_SAVE_GET_MAXPOOLS 12 ++#define TMEMC_SAVE_GET_CLIENT_WEIGHT 13 ++#define TMEMC_SAVE_GET_CLIENT_CAP 14 ++#define TMEMC_SAVE_GET_CLIENT_FLAGS 15 ++#define TMEMC_SAVE_GET_POOL_FLAGS 16 ++#define TMEMC_SAVE_GET_POOL_NPAGES 17 ++#define TMEMC_SAVE_GET_POOL_UUID 18 ++#define TMEMC_SAVE_GET_NEXT_PAGE 19 ++#define TMEMC_SAVE_GET_NEXT_INV 20 ++#define TMEMC_SAVE_END 21 ++#define TMEMC_RESTORE_BEGIN 30 ++#define TMEMC_RESTORE_PUT_PAGE 32 ++#define TMEMC_RESTORE_FLUSH_PAGE 33 ++ ++/* Bits for HYPERVISOR_tmem_op(TMEM_NEW_POOL) */ ++#define TMEM_POOL_PERSIST 1 ++#define TMEM_POOL_SHARED 2 ++#define TMEM_POOL_PAGESIZE_SHIFT 4 ++#define TMEM_POOL_PAGESIZE_MASK 0xf ++#define TMEM_POOL_VERSION_SHIFT 24 ++#define TMEM_POOL_VERSION_MASK 0xff ++ ++/* Bits for client flags (save/restore) */ ++#define TMEM_CLIENT_COMPRESS 1 ++#define TMEM_CLIENT_FROZEN 2 ++ ++/* Special errno values */ ++#define EFROZEN 1000 ++#define EEMPTY 1001 ++ ++ ++#ifndef __ASSEMBLY__ ++typedef xen_pfn_t tmem_cli_mfn_t; ++typedef XEN_GUEST_HANDLE(char) tmem_cli_va_t; ++struct tmem_op { ++ uint32_t cmd; ++ int32_t pool_id; ++ union { ++ struct { ++ uint64_t uuid[2]; ++ uint32_t flags; ++ uint32_t arg1; ++ } new; /* for cmd == TMEM_NEW_POOL, TMEM_AUTH, TMEM_RESTORE_NEW */ ++ struct { ++ uint32_t subop; ++ uint32_t cli_id; ++ uint32_t arg1; ++ uint32_t arg2; ++ uint64_t arg3; ++ tmem_cli_va_t buf; ++ } ctrl; /* for cmd == TMEM_CONTROL */ ++ struct { ++ ++ uint64_t object; ++ uint32_t index; ++ uint32_t tmem_offset; ++ uint32_t pfn_offset; ++ uint32_t len; ++ tmem_cli_mfn_t cmfn; /* client machine page frame */ ++ } gen; /* for all other cmd ("generic") */ ++ } u; ++}; ++typedef struct tmem_op tmem_op_t; ++DEFINE_XEN_GUEST_HANDLE(tmem_op_t); ++ ++struct tmem_handle { ++ uint32_t pool_id; ++ uint32_t index; ++ uint64_t oid; ++}; ++ ++#endif ++ ++#endif /* __XEN_PUBLIC_TMEM_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/trace.h 2010-05-07 11:10:48.000000000 +0200 +@@ -0,0 +1,227 @@ ++/****************************************************************************** ++ * include/public/trace.h ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Mark Williamson, (C) 2004 Intel Research Cambridge ++ * Copyright (C) 2005 Bin Ren ++ */ ++ ++#ifndef __XEN_PUBLIC_TRACE_H__ ++#define __XEN_PUBLIC_TRACE_H__ ++ ++#define TRACE_EXTRA_MAX 7 ++#define TRACE_EXTRA_SHIFT 28 ++ ++/* Trace classes */ ++#define TRC_CLS_SHIFT 16 ++#define TRC_GEN 0x0001f000 /* General trace */ ++#define TRC_SCHED 0x0002f000 /* Xen Scheduler trace */ ++#define TRC_DOM0OP 0x0004f000 /* Xen DOM0 operation trace */ ++#define TRC_HVM 0x0008f000 /* Xen HVM trace */ ++#define TRC_MEM 0x0010f000 /* Xen memory trace */ ++#define TRC_PV 0x0020f000 /* Xen PV traces */ ++#define TRC_SHADOW 0x0040f000 /* Xen shadow tracing */ ++#define TRC_PM 0x0080f000 /* Xen power management trace */ ++#define TRC_ALL 0x0ffff000 ++#define TRC_HD_TO_EVENT(x) ((x)&0x0fffffff) ++#define TRC_HD_CYCLE_FLAG (1UL<<31) ++#define TRC_HD_INCLUDES_CYCLE_COUNT(x) ( !!( (x) & TRC_HD_CYCLE_FLAG ) ) ++#define TRC_HD_EXTRA(x) (((x)>>TRACE_EXTRA_SHIFT)&TRACE_EXTRA_MAX) ++ ++/* Trace subclasses */ ++#define TRC_SUBCLS_SHIFT 12 ++ ++/* trace subclasses for SVM */ ++#define TRC_HVM_ENTRYEXIT 0x00081000 /* VMENTRY and #VMEXIT */ ++#define TRC_HVM_HANDLER 0x00082000 /* various HVM handlers */ ++ ++#define TRC_SCHED_MIN 0x00021000 /* Just runstate changes */ ++#define TRC_SCHED_CLASS 0x00022000 /* Scheduler-specific */ ++#define TRC_SCHED_VERBOSE 0x00028000 /* More inclusive scheduling */ ++ ++/* Trace events per class */ ++#define TRC_LOST_RECORDS (TRC_GEN + 1) ++#define TRC_TRACE_WRAP_BUFFER (TRC_GEN + 2) ++#define TRC_TRACE_CPU_CHANGE (TRC_GEN + 3) ++#define TRC_TRACE_IRQ (TRC_GEN + 4) ++ ++#define TRC_SCHED_RUNSTATE_CHANGE (TRC_SCHED_MIN + 1) ++#define TRC_SCHED_CONTINUE_RUNNING (TRC_SCHED_MIN + 2) ++#define TRC_SCHED_DOM_ADD (TRC_SCHED_VERBOSE + 1) ++#define TRC_SCHED_DOM_REM (TRC_SCHED_VERBOSE + 2) ++#define TRC_SCHED_SLEEP (TRC_SCHED_VERBOSE + 3) ++#define TRC_SCHED_WAKE (TRC_SCHED_VERBOSE + 4) ++#define TRC_SCHED_YIELD (TRC_SCHED_VERBOSE + 5) ++#define TRC_SCHED_BLOCK (TRC_SCHED_VERBOSE + 6) ++#define TRC_SCHED_SHUTDOWN (TRC_SCHED_VERBOSE + 7) ++#define TRC_SCHED_CTL (TRC_SCHED_VERBOSE + 8) ++#define TRC_SCHED_ADJDOM (TRC_SCHED_VERBOSE + 9) ++#define TRC_SCHED_SWITCH (TRC_SCHED_VERBOSE + 10) ++#define TRC_SCHED_S_TIMER_FN (TRC_SCHED_VERBOSE + 11) ++#define TRC_SCHED_T_TIMER_FN (TRC_SCHED_VERBOSE + 12) ++#define TRC_SCHED_DOM_TIMER_FN (TRC_SCHED_VERBOSE + 13) ++#define TRC_SCHED_SWITCH_INFPREV (TRC_SCHED_VERBOSE + 14) ++#define TRC_SCHED_SWITCH_INFNEXT (TRC_SCHED_VERBOSE + 15) ++ ++#define TRC_MEM_PAGE_GRANT_MAP (TRC_MEM + 1) ++#define TRC_MEM_PAGE_GRANT_UNMAP (TRC_MEM + 2) ++#define TRC_MEM_PAGE_GRANT_TRANSFER (TRC_MEM + 3) ++#define TRC_MEM_SET_P2M_ENTRY (TRC_MEM + 4) ++#define TRC_MEM_DECREASE_RESERVATION (TRC_MEM + 5) ++#define TRC_MEM_POD_POPULATE (TRC_MEM + 16) ++#define TRC_MEM_POD_ZERO_RECLAIM (TRC_MEM + 17) ++#define TRC_MEM_POD_SUPERPAGE_SPLINTER (TRC_MEM + 18) ++ ++ ++#define TRC_PV_HYPERCALL (TRC_PV + 1) ++#define TRC_PV_TRAP (TRC_PV + 3) ++#define TRC_PV_PAGE_FAULT (TRC_PV + 4) ++#define TRC_PV_FORCED_INVALID_OP (TRC_PV + 5) ++#define TRC_PV_EMULATE_PRIVOP (TRC_PV + 6) ++#define TRC_PV_EMULATE_4GB (TRC_PV + 7) ++#define TRC_PV_MATH_STATE_RESTORE (TRC_PV + 8) ++#define TRC_PV_PAGING_FIXUP (TRC_PV + 9) ++#define TRC_PV_GDT_LDT_MAPPING_FAULT (TRC_PV + 10) ++#define TRC_PV_PTWR_EMULATION (TRC_PV + 11) ++#define TRC_PV_PTWR_EMULATION_PAE (TRC_PV + 12) ++ /* Indicates that addresses in trace record are 64 bits */ ++#define TRC_64_FLAG (0x100) ++ ++#define TRC_SHADOW_NOT_SHADOW (TRC_SHADOW + 1) ++#define TRC_SHADOW_FAST_PROPAGATE (TRC_SHADOW + 2) ++#define TRC_SHADOW_FAST_MMIO (TRC_SHADOW + 3) ++#define TRC_SHADOW_FALSE_FAST_PATH (TRC_SHADOW + 4) ++#define TRC_SHADOW_MMIO (TRC_SHADOW + 5) ++#define TRC_SHADOW_FIXUP (TRC_SHADOW + 6) ++#define TRC_SHADOW_DOMF_DYING (TRC_SHADOW + 7) ++#define TRC_SHADOW_EMULATE (TRC_SHADOW + 8) ++#define TRC_SHADOW_EMULATE_UNSHADOW_USER (TRC_SHADOW + 9) ++#define TRC_SHADOW_EMULATE_UNSHADOW_EVTINJ (TRC_SHADOW + 10) ++#define TRC_SHADOW_EMULATE_UNSHADOW_UNHANDLED (TRC_SHADOW + 11) ++#define TRC_SHADOW_WRMAP_BF (TRC_SHADOW + 12) ++#define TRC_SHADOW_PREALLOC_UNPIN (TRC_SHADOW + 13) ++#define TRC_SHADOW_RESYNC_FULL (TRC_SHADOW + 14) ++#define TRC_SHADOW_RESYNC_ONLY (TRC_SHADOW + 15) ++ ++/* trace events per subclass */ ++#define TRC_HVM_VMENTRY (TRC_HVM_ENTRYEXIT + 0x01) ++#define TRC_HVM_VMEXIT (TRC_HVM_ENTRYEXIT + 0x02) ++#define TRC_HVM_VMEXIT64 (TRC_HVM_ENTRYEXIT + TRC_64_FLAG + 0x02) ++#define TRC_HVM_PF_XEN (TRC_HVM_HANDLER + 0x01) ++#define TRC_HVM_PF_XEN64 (TRC_HVM_HANDLER + TRC_64_FLAG + 0x01) ++#define TRC_HVM_PF_INJECT (TRC_HVM_HANDLER + 0x02) ++#define TRC_HVM_PF_INJECT64 (TRC_HVM_HANDLER + TRC_64_FLAG + 0x02) ++#define TRC_HVM_INJ_EXC (TRC_HVM_HANDLER + 0x03) ++#define TRC_HVM_INJ_VIRQ (TRC_HVM_HANDLER + 0x04) ++#define TRC_HVM_REINJ_VIRQ (TRC_HVM_HANDLER + 0x05) ++#define TRC_HVM_IO_READ (TRC_HVM_HANDLER + 0x06) ++#define TRC_HVM_IO_WRITE (TRC_HVM_HANDLER + 0x07) ++#define TRC_HVM_CR_READ (TRC_HVM_HANDLER + 0x08) ++#define TRC_HVM_CR_READ64 (TRC_HVM_HANDLER + TRC_64_FLAG + 0x08) ++#define TRC_HVM_CR_WRITE (TRC_HVM_HANDLER + 0x09) ++#define TRC_HVM_CR_WRITE64 (TRC_HVM_HANDLER + TRC_64_FLAG + 0x09) ++#define TRC_HVM_DR_READ (TRC_HVM_HANDLER + 0x0A) ++#define TRC_HVM_DR_WRITE (TRC_HVM_HANDLER + 0x0B) ++#define TRC_HVM_MSR_READ (TRC_HVM_HANDLER + 0x0C) ++#define TRC_HVM_MSR_WRITE (TRC_HVM_HANDLER + 0x0D) ++#define TRC_HVM_CPUID (TRC_HVM_HANDLER + 0x0E) ++#define TRC_HVM_INTR (TRC_HVM_HANDLER + 0x0F) ++#define TRC_HVM_NMI (TRC_HVM_HANDLER + 0x10) ++#define TRC_HVM_SMI (TRC_HVM_HANDLER + 0x11) ++#define TRC_HVM_VMMCALL (TRC_HVM_HANDLER + 0x12) ++#define TRC_HVM_HLT (TRC_HVM_HANDLER + 0x13) ++#define TRC_HVM_INVLPG (TRC_HVM_HANDLER + 0x14) ++#define TRC_HVM_INVLPG64 (TRC_HVM_HANDLER + TRC_64_FLAG + 0x14) ++#define TRC_HVM_MCE (TRC_HVM_HANDLER + 0x15) ++#define TRC_HVM_IOPORT_READ (TRC_HVM_HANDLER + 0x16) ++#define TRC_HVM_IOMEM_READ (TRC_HVM_HANDLER + 0x17) ++#define TRC_HVM_CLTS (TRC_HVM_HANDLER + 0x18) ++#define TRC_HVM_LMSW (TRC_HVM_HANDLER + 0x19) ++#define TRC_HVM_LMSW64 (TRC_HVM_HANDLER + TRC_64_FLAG + 0x19) ++#define TRC_HVM_INTR_WINDOW (TRC_HVM_HANDLER + 0x20) ++#define TRC_HVM_NPF (TRC_HVM_HANDLER + 0x21) ++ ++#define TRC_HVM_IOPORT_WRITE (TRC_HVM_HANDLER + 0x216) ++#define TRC_HVM_IOMEM_WRITE (TRC_HVM_HANDLER + 0x217) ++ ++/* trace subclasses for power management */ ++#define TRC_PM_FREQ 0x00801000 /* xen cpu freq events */ ++#define TRC_PM_IDLE 0x00802000 /* xen cpu idle events */ ++ ++/* trace events for per class */ ++#define TRC_PM_FREQ_CHANGE (TRC_PM_FREQ + 0x01) ++#define TRC_PM_IDLE_ENTRY (TRC_PM_IDLE + 0x01) ++#define TRC_PM_IDLE_EXIT (TRC_PM_IDLE + 0x02) ++ ++/* This structure represents a single trace buffer record. */ ++struct t_rec { ++ uint32_t event:28; ++ uint32_t extra_u32:3; /* # entries in trailing extra_u32[] array */ ++ uint32_t cycles_included:1; /* u.cycles or u.no_cycles? */ ++ union { ++ struct { ++ uint32_t cycles_lo, cycles_hi; /* cycle counter timestamp */ ++ uint32_t extra_u32[7]; /* event data items */ ++ } cycles; ++ struct { ++ uint32_t extra_u32[7]; /* event data items */ ++ } nocycles; ++ } u; ++}; ++ ++/* ++ * This structure contains the metadata for a single trace buffer. The head ++ * field, indexes into an array of struct t_rec's. ++ */ ++struct t_buf { ++ /* Assume the data buffer size is X. X is generally not a power of 2. ++ * CONS and PROD are incremented modulo (2*X): ++ * 0 <= cons < 2*X ++ * 0 <= prod < 2*X ++ * This is done because addition modulo X breaks at 2^32 when X is not a ++ * power of 2: ++ * (((2^32 - 1) % X) + 1) % X != (2^32) % X ++ */ ++ uint32_t cons; /* Offset of next item to be consumed by control tools. */ ++ uint32_t prod; /* Offset of next item to be produced by Xen. */ ++ /* Records follow immediately after the meta-data header. */ ++}; ++ ++/* Structure used to pass MFNs to the trace buffers back to trace consumers. ++ * Offset is an offset into the mapped structure where the mfn list will be held. ++ * MFNs will be at ((unsigned long *)(t_info))+(t_info->cpu_offset[cpu]). ++ */ ++struct t_info { ++ uint16_t tbuf_size; /* Size in pages of each trace buffer */ ++ uint16_t mfn_offset[]; /* Offset within t_info structure of the page list per cpu */ ++ /* MFN lists immediately after the header */ ++}; ++ ++#endif /* __XEN_PUBLIC_TRACE_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/xen-compat.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,44 @@ ++/****************************************************************************** ++ * xen-compat.h ++ * ++ * Guest OS interface to Xen. Compatibility layer. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (c) 2006, Christian Limpach ++ */ ++ ++#ifndef __XEN_PUBLIC_XEN_COMPAT_H__ ++#define __XEN_PUBLIC_XEN_COMPAT_H__ ++ ++#define __XEN_LATEST_INTERFACE_VERSION__ 0x0003020a ++ ++#if defined(__XEN__) || defined(__XEN_TOOLS__) ++/* Xen is built with matching headers and implements the latest interface. */ ++#define __XEN_INTERFACE_VERSION__ __XEN_LATEST_INTERFACE_VERSION__ ++#elif !defined(__XEN_INTERFACE_VERSION__) ++/* Guests which do not specify a version get the legacy interface. */ ++#define __XEN_INTERFACE_VERSION__ 0x00000000 ++#endif ++ ++#if __XEN_INTERFACE_VERSION__ > __XEN_LATEST_INTERFACE_VERSION__ ++#error "These header files do not support the requested interface version." ++#endif ++ ++#endif /* __XEN_PUBLIC_XEN_COMPAT_H__ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/xenoprof.h 2007-06-12 13:14:19.000000000 +0200 +@@ -0,0 +1,138 @@ ++/****************************************************************************** ++ * xenoprof.h ++ * ++ * Interface for enabling system wide profiling based on hardware performance ++ * counters ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Copyright (C) 2005 Hewlett-Packard Co. ++ * Written by Aravind Menon & Jose Renato Santos ++ */ ++ ++#ifndef __XEN_PUBLIC_XENOPROF_H__ ++#define __XEN_PUBLIC_XENOPROF_H__ ++ ++#include "xen.h" ++ ++/* ++ * Commands to HYPERVISOR_xenoprof_op(). ++ */ ++#define XENOPROF_init 0 ++#define XENOPROF_reset_active_list 1 ++#define XENOPROF_reset_passive_list 2 ++#define XENOPROF_set_active 3 ++#define XENOPROF_set_passive 4 ++#define XENOPROF_reserve_counters 5 ++#define XENOPROF_counter 6 ++#define XENOPROF_setup_events 7 ++#define XENOPROF_enable_virq 8 ++#define XENOPROF_start 9 ++#define XENOPROF_stop 10 ++#define XENOPROF_disable_virq 11 ++#define XENOPROF_release_counters 12 ++#define XENOPROF_shutdown 13 ++#define XENOPROF_get_buffer 14 ++#define XENOPROF_set_backtrace 15 ++#define XENOPROF_last_op 15 ++ ++#define MAX_OPROF_EVENTS 32 ++#define MAX_OPROF_DOMAINS 25 ++#define XENOPROF_CPU_TYPE_SIZE 64 ++ ++/* Xenoprof performance events (not Xen events) */ ++struct event_log { ++ uint64_t eip; ++ uint8_t mode; ++ uint8_t event; ++}; ++ ++/* PC value that indicates a special code */ ++#define XENOPROF_ESCAPE_CODE ~0UL ++/* Transient events for the xenoprof->oprofile cpu buf */ ++#define XENOPROF_TRACE_BEGIN 1 ++ ++/* Xenoprof buffer shared between Xen and domain - 1 per VCPU */ ++struct xenoprof_buf { ++ uint32_t event_head; ++ uint32_t event_tail; ++ uint32_t event_size; ++ uint32_t vcpu_id; ++ uint64_t xen_samples; ++ uint64_t kernel_samples; ++ uint64_t user_samples; ++ uint64_t lost_samples; ++ struct event_log event_log[1]; ++}; ++#ifndef __XEN__ ++typedef struct xenoprof_buf xenoprof_buf_t; ++DEFINE_XEN_GUEST_HANDLE(xenoprof_buf_t); ++#endif ++ ++struct xenoprof_init { ++ int32_t num_events; ++ int32_t is_primary; ++ char cpu_type[XENOPROF_CPU_TYPE_SIZE]; ++}; ++typedef struct xenoprof_init xenoprof_init_t; ++DEFINE_XEN_GUEST_HANDLE(xenoprof_init_t); ++ ++struct xenoprof_get_buffer { ++ int32_t max_samples; ++ int32_t nbuf; ++ int32_t bufsize; ++ uint64_t buf_gmaddr; ++}; ++typedef struct xenoprof_get_buffer xenoprof_get_buffer_t; ++DEFINE_XEN_GUEST_HANDLE(xenoprof_get_buffer_t); ++ ++struct xenoprof_counter { ++ uint32_t ind; ++ uint64_t count; ++ uint32_t enabled; ++ uint32_t event; ++ uint32_t hypervisor; ++ uint32_t kernel; ++ uint32_t user; ++ uint64_t unit_mask; ++}; ++typedef struct xenoprof_counter xenoprof_counter_t; ++DEFINE_XEN_GUEST_HANDLE(xenoprof_counter_t); ++ ++typedef struct xenoprof_passive { ++ uint16_t domain_id; ++ int32_t max_samples; ++ int32_t nbuf; ++ int32_t bufsize; ++ uint64_t buf_gmaddr; ++} xenoprof_passive_t; ++DEFINE_XEN_GUEST_HANDLE(xenoprof_passive_t); ++ ++ ++#endif /* __XEN_PUBLIC_XENOPROF_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/xsm/acm.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,223 @@ ++/* ++ * acm.h: Xen access control module interface defintions ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Reiner Sailer ++ * Copyright (c) 2005, International Business Machines Corporation. ++ */ ++ ++#ifndef _XEN_PUBLIC_ACM_H ++#define _XEN_PUBLIC_ACM_H ++ ++#include "../xen.h" ++ ++/* default ssid reference value if not supplied */ ++#define ACM_DEFAULT_SSID 0x0 ++#define ACM_DEFAULT_LOCAL_SSID 0x0 ++ ++/* Internal ACM ERROR types */ ++#define ACM_OK 0 ++#define ACM_UNDEF -1 ++#define ACM_INIT_SSID_ERROR -2 ++#define ACM_INIT_SOID_ERROR -3 ++#define ACM_ERROR -4 ++ ++/* External ACCESS DECISIONS */ ++#define ACM_ACCESS_PERMITTED 0 ++#define ACM_ACCESS_DENIED -111 ++#define ACM_NULL_POINTER_ERROR -200 ++ ++/* ++ Error codes reported in when trying to test for a new policy ++ These error codes are reported in an array of tuples where ++ each error code is followed by a parameter describing the error ++ more closely, such as a domain id. ++*/ ++#define ACM_EVTCHN_SHARING_VIOLATION 0x100 ++#define ACM_GNTTAB_SHARING_VIOLATION 0x101 ++#define ACM_DOMAIN_LOOKUP 0x102 ++#define ACM_CHWALL_CONFLICT 0x103 ++#define ACM_SSIDREF_IN_USE 0x104 ++ ++ ++/* primary policy in lower 4 bits */ ++#define ACM_NULL_POLICY 0 ++#define ACM_CHINESE_WALL_POLICY 1 ++#define ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY 2 ++#define ACM_POLICY_UNDEFINED 15 ++ ++/* combinations have secondary policy component in higher 4bit */ ++#define ACM_CHINESE_WALL_AND_SIMPLE_TYPE_ENFORCEMENT_POLICY \ ++ ((ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY << 4) | ACM_CHINESE_WALL_POLICY) ++ ++/* policy: */ ++#define ACM_POLICY_NAME(X) \ ++ ((X) == (ACM_NULL_POLICY)) ? "NULL" : \ ++ ((X) == (ACM_CHINESE_WALL_POLICY)) ? "CHINESE WALL" : \ ++ ((X) == (ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY)) ? "SIMPLE TYPE ENFORCEMENT" : \ ++ ((X) == (ACM_CHINESE_WALL_AND_SIMPLE_TYPE_ENFORCEMENT_POLICY)) ? "CHINESE WALL AND SIMPLE TYPE ENFORCEMENT" : \ ++ "UNDEFINED" ++ ++/* the following policy versions must be increased ++ * whenever the interpretation of the related ++ * policy's data structure changes ++ */ ++#define ACM_POLICY_VERSION 4 ++#define ACM_CHWALL_VERSION 1 ++#define ACM_STE_VERSION 1 ++ ++/* defines a ssid reference used by xen */ ++typedef uint32_t ssidref_t; ++ ++/* hooks that are known to domains */ ++#define ACMHOOK_none 0 ++#define ACMHOOK_sharing 1 ++#define ACMHOOK_authorization 2 ++#define ACMHOOK_conflictset 3 ++ ++/* -------security policy relevant type definitions-------- */ ++ ++/* type identifier; compares to "equal" or "not equal" */ ++typedef uint16_t domaintype_t; ++ ++/* CHINESE WALL POLICY DATA STRUCTURES ++ * ++ * current accumulated conflict type set: ++ * When a domain is started and has a type that is in ++ * a conflict set, the conflicting types are incremented in ++ * the aggregate set. When a domain is destroyed, the ++ * conflicting types to its type are decremented. ++ * If a domain has multiple types, this procedure works over ++ * all those types. ++ * ++ * conflict_aggregate_set[i] holds the number of ++ * running domains that have a conflict with type i. ++ * ++ * running_types[i] holds the number of running domains ++ * that include type i in their ssidref-referenced type set ++ * ++ * conflict_sets[i][j] is "0" if type j has no conflict ++ * with type i and is "1" otherwise. ++ */ ++/* high-16 = version, low-16 = check magic */ ++#define ACM_MAGIC 0x0001debc ++ ++/* size of the SHA1 hash identifying the XML policy from which the ++ binary policy was created */ ++#define ACM_SHA1_HASH_SIZE 20 ++ ++/* each offset in bytes from start of the struct they ++ * are part of */ ++ ++/* V3 of the policy buffer aded a version structure */ ++struct acm_policy_version ++{ ++ uint32_t major; ++ uint32_t minor; ++}; ++ ++ ++/* each buffer consists of all policy information for ++ * the respective policy given in the policy code ++ * ++ * acm_policy_buffer, acm_chwall_policy_buffer, ++ * and acm_ste_policy_buffer need to stay 32-bit aligned ++ * because we create binary policies also with external ++ * tools that assume packed representations (e.g. the java tool) ++ */ ++struct acm_policy_buffer { ++ uint32_t magic; ++ uint32_t policy_version; /* ACM_POLICY_VERSION */ ++ uint32_t len; ++ uint32_t policy_reference_offset; ++ uint32_t primary_policy_code; ++ uint32_t primary_buffer_offset; ++ uint32_t secondary_policy_code; ++ uint32_t secondary_buffer_offset; ++ struct acm_policy_version xml_pol_version; /* add in V3 */ ++ uint8_t xml_policy_hash[ACM_SHA1_HASH_SIZE]; /* added in V4 */ ++}; ++ ++ ++struct acm_policy_reference_buffer { ++ uint32_t len; ++}; ++ ++struct acm_chwall_policy_buffer { ++ uint32_t policy_version; /* ACM_CHWALL_VERSION */ ++ uint32_t policy_code; ++ uint32_t chwall_max_types; ++ uint32_t chwall_max_ssidrefs; ++ uint32_t chwall_max_conflictsets; ++ uint32_t chwall_ssid_offset; ++ uint32_t chwall_conflict_sets_offset; ++ uint32_t chwall_running_types_offset; ++ uint32_t chwall_conflict_aggregate_offset; ++}; ++ ++struct acm_ste_policy_buffer { ++ uint32_t policy_version; /* ACM_STE_VERSION */ ++ uint32_t policy_code; ++ uint32_t ste_max_types; ++ uint32_t ste_max_ssidrefs; ++ uint32_t ste_ssid_offset; ++}; ++ ++struct acm_stats_buffer { ++ uint32_t magic; ++ uint32_t len; ++ uint32_t primary_policy_code; ++ uint32_t primary_stats_offset; ++ uint32_t secondary_policy_code; ++ uint32_t secondary_stats_offset; ++}; ++ ++struct acm_ste_stats_buffer { ++ uint32_t ec_eval_count; ++ uint32_t gt_eval_count; ++ uint32_t ec_denied_count; ++ uint32_t gt_denied_count; ++ uint32_t ec_cachehit_count; ++ uint32_t gt_cachehit_count; ++}; ++ ++struct acm_ssid_buffer { ++ uint32_t len; ++ ssidref_t ssidref; ++ uint32_t policy_reference_offset; ++ uint32_t primary_policy_code; ++ uint32_t primary_max_types; ++ uint32_t primary_types_offset; ++ uint32_t secondary_policy_code; ++ uint32_t secondary_max_types; ++ uint32_t secondary_types_offset; ++}; ++ ++#endif ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/xsm/acm_ops.h 2007-10-22 13:39:15.000000000 +0200 +@@ -0,0 +1,159 @@ ++/* ++ * acm_ops.h: Xen access control module hypervisor commands ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Reiner Sailer ++ * Copyright (c) 2005,2006 International Business Machines Corporation. ++ */ ++ ++#ifndef __XEN_PUBLIC_ACM_OPS_H__ ++#define __XEN_PUBLIC_ACM_OPS_H__ ++ ++#include "../xen.h" ++#include "acm.h" ++ ++/* ++ * Make sure you increment the interface version whenever you modify this file! ++ * This makes sure that old versions of acm tools will stop working in a ++ * well-defined way (rather than crashing the machine, for instance). ++ */ ++#define ACM_INTERFACE_VERSION 0xAAAA000A ++ ++/************************************************************************/ ++ ++/* ++ * Prototype for this hypercall is: ++ * int acm_op(int cmd, void *args) ++ * @cmd == ACMOP_??? (access control module operation). ++ * @args == Operation-specific extra arguments (NULL if none). ++ */ ++ ++ ++#define ACMOP_setpolicy 1 ++struct acm_setpolicy { ++ /* IN */ ++ XEN_GUEST_HANDLE_64(void) pushcache; ++ uint32_t pushcache_size; ++}; ++ ++ ++#define ACMOP_getpolicy 2 ++struct acm_getpolicy { ++ /* IN */ ++ XEN_GUEST_HANDLE_64(void) pullcache; ++ uint32_t pullcache_size; ++}; ++ ++ ++#define ACMOP_dumpstats 3 ++struct acm_dumpstats { ++ /* IN */ ++ XEN_GUEST_HANDLE_64(void) pullcache; ++ uint32_t pullcache_size; ++}; ++ ++ ++#define ACMOP_getssid 4 ++#define ACM_GETBY_ssidref 1 ++#define ACM_GETBY_domainid 2 ++struct acm_getssid { ++ /* IN */ ++ uint32_t get_ssid_by; /* ACM_GETBY_* */ ++ union { ++ domaintype_t domainid; ++ ssidref_t ssidref; ++ } id; ++ XEN_GUEST_HANDLE_64(void) ssidbuf; ++ uint32_t ssidbuf_size; ++}; ++ ++#define ACMOP_getdecision 5 ++struct acm_getdecision { ++ /* IN */ ++ uint32_t get_decision_by1; /* ACM_GETBY_* */ ++ uint32_t get_decision_by2; /* ACM_GETBY_* */ ++ union { ++ domaintype_t domainid; ++ ssidref_t ssidref; ++ } id1; ++ union { ++ domaintype_t domainid; ++ ssidref_t ssidref; ++ } id2; ++ uint32_t hook; ++ /* OUT */ ++ uint32_t acm_decision; ++}; ++ ++ ++#define ACMOP_chgpolicy 6 ++struct acm_change_policy { ++ /* IN */ ++ XEN_GUEST_HANDLE_64(void) policy_pushcache; ++ uint32_t policy_pushcache_size; ++ XEN_GUEST_HANDLE_64(void) del_array; ++ uint32_t delarray_size; ++ XEN_GUEST_HANDLE_64(void) chg_array; ++ uint32_t chgarray_size; ++ /* OUT */ ++ /* array with error code */ ++ XEN_GUEST_HANDLE_64(void) err_array; ++ uint32_t errarray_size; ++}; ++ ++#define ACMOP_relabeldoms 7 ++struct acm_relabel_doms { ++ /* IN */ ++ XEN_GUEST_HANDLE_64(void) relabel_map; ++ uint32_t relabel_map_size; ++ /* OUT */ ++ XEN_GUEST_HANDLE_64(void) err_array; ++ uint32_t errarray_size; ++}; ++ ++/* future interface to Xen */ ++struct xen_acmctl { ++ uint32_t cmd; ++ uint32_t interface_version; ++ union { ++ struct acm_setpolicy setpolicy; ++ struct acm_getpolicy getpolicy; ++ struct acm_dumpstats dumpstats; ++ struct acm_getssid getssid; ++ struct acm_getdecision getdecision; ++ struct acm_change_policy change_policy; ++ struct acm_relabel_doms relabel_doms; ++ } u; ++}; ++ ++typedef struct xen_acmctl xen_acmctl_t; ++DEFINE_XEN_GUEST_HANDLE(xen_acmctl_t); ++ ++#endif /* __XEN_PUBLIC_ACM_OPS_H__ */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-set-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-05-12/include/xen/interface/xsm/flask_op.h 2010-01-04 11:56:34.000000000 +0100 +@@ -0,0 +1,47 @@ ++/* ++ * This file contains the flask_op hypercall commands and definitions. ++ * ++ * Author: George Coker, ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, ++ * as published by the Free Software Foundation. ++ */ ++ ++#ifndef __FLASK_OP_H__ ++#define __FLASK_OP_H__ ++ ++#define FLASK_LOAD 1 ++#define FLASK_GETENFORCE 2 ++#define FLASK_SETENFORCE 3 ++#define FLASK_CONTEXT_TO_SID 4 ++#define FLASK_SID_TO_CONTEXT 5 ++#define FLASK_ACCESS 6 ++#define FLASK_CREATE 7 ++#define FLASK_RELABEL 8 ++#define FLASK_USER 9 ++#define FLASK_POLICYVERS 10 ++#define FLASK_GETBOOL 11 ++#define FLASK_SETBOOL 12 ++#define FLASK_COMMITBOOLS 13 ++#define FLASK_MLS 14 ++#define FLASK_DISABLE 15 ++#define FLASK_GETAVC_THRESHOLD 16 ++#define FLASK_SETAVC_THRESHOLD 17 ++#define FLASK_AVC_HASHSTATS 18 ++#define FLASK_AVC_CACHESTATS 19 ++#define FLASK_MEMBER 20 ++#define FLASK_ADD_OCONTEXT 21 ++#define FLASK_DEL_OCONTEXT 22 ++ ++#define FLASK_LAST FLASK_DEL_OCONTEXT ++ ++typedef struct flask_op { ++ uint32_t cmd; ++ uint32_t size; ++ char *buf; ++} flask_op_t; ++ ++DEFINE_XEN_GUEST_HANDLE(flask_op_t); ++ ++#endif diff --git a/patches.xen/xen3-auto-xen-arch.diff b/patches.xen/xen3-auto-xen-arch.diff new file mode 100644 index 0000000..0f096ae --- /dev/null +++ b/patches.xen/xen3-auto-xen-arch.diff @@ -0,0 +1,44610 @@ +Subject: xen3 xen-arch +From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 1017:948c933f8839) +Patch-mainline: n/a +Acked-by: jbeulich@novell.com + +List of files having Xen derivates (perhaps created during the merging +of newer kernel versions), for xen-port-patches.py to pick up (i.e. this +must be retained here until the XenSource tree has these in the right +places): ++++ linux/arch/x86/kernel/acpi/sleep-xen.c ++++ linux/arch/x86/kernel/apic/io_apic-xen.c ++++ linux/arch/x86/kernel/apic/ipi-xen.c ++++ linux/arch/x86/kernel/apic/probe_32-xen.c ++++ linux/arch/x86/kernel/cpu/common_64-xen.c ++++ linux/arch/x86/kernel/e820-xen.c ++++ linux/arch/x86/kernel/head-xen.c ++++ linux/arch/x86/kernel/head32-xen.c ++++ linux/arch/x86/kernel/ioport-xen.c ++++ linux/arch/x86/kernel/io_apic-xen.c ++++ linux/arch/x86/kernel/ipi-xen.c ++++ linux/arch/x86/kernel/irq-xen.c ++++ linux/arch/x86/kernel/ldt-xen.c ++++ linux/arch/x86/kernel/microcode_core-xen.c ++++ linux/arch/x86/kernel/mpparse-xen.c ++++ linux/arch/x86/kernel/pci-nommu-xen.c ++++ linux/arch/x86/kernel/process-xen.c ++++ linux/arch/x86/kernel/setup-xen.c ++++ linux/arch/x86/kernel/smp-xen.c ++++ linux/arch/x86/kernel/traps-xen.c ++++ linux/arch/x86/kernel/x86_init-xen.c ++++ linux/arch/x86/lib/cache-smp-xen.c ++++ linux/arch/x86/mm/fault-xen.c ++++ linux/arch/x86/mm/init-xen.c ++++ linux/arch/x86/mm/iomap_32-xen.c ++++ linux/arch/x86/mm/ioremap-xen.c ++++ linux/arch/x86/mm/pageattr-xen.c ++++ linux/arch/x86/mm/pat-xen.c ++++ linux/arch/x86/mm/pgtable-xen.c ++++ linux/arch/x86/vdso/vdso32-setup-xen.c ++++ linux/drivers/char/mem-xen.c ++++ linux/arch/x86/include/mach-xen/asm/desc.h ++++ linux/arch/x86/include/mach-xen/asm/dma-mapping.h ++++ linux/arch/x86/include/mach-xen/asm/fixmap.h ++++ linux/arch/x86/include/mach-xen/asm/io.h ++++ linux/arch/x86/include/mach-xen/asm/ipi.h ++++ linux/arch/x86/include/mach-xen/asm/irq_vectors.h ++++ linux/arch/x86/include/mach-xen/asm/irqflags.h ++++ linux/arch/x86/include/mach-xen/asm/mmu_context.h ++++ linux/arch/x86/include/mach-xen/asm/pci.h ++++ linux/arch/x86/include/mach-xen/asm/pgalloc.h ++++ linux/arch/x86/include/mach-xen/asm/pgtable.h ++++ linux/arch/x86/include/mach-xen/asm/pgtable-3level_types.h ++++ linux/arch/x86/include/mach-xen/asm/pgtable_64_types.h ++++ linux/arch/x86/include/mach-xen/asm/pgtable_types.h ++++ linux/arch/x86/include/mach-xen/asm/processor.h ++++ linux/arch/x86/include/mach-xen/asm/smp.h ++++ linux/arch/x86/include/mach-xen/asm/spinlock.h ++++ linux/arch/x86/include/mach-xen/asm/spinlock_types.h ++++ linux/arch/x86/include/mach-xen/asm/swiotlb.h ++++ linux/arch/x86/include/mach-xen/asm/system.h ++++ linux/arch/x86/include/mach-xen/asm/tlbflush.h ++++ linux/arch/x86/include/mach-xen/asm/xor.h + +List of files folded into their native counterparts (and hence removed +from this patch for xen-port-patches.py to not needlessly pick them up; +for reference, prefixed with the version the removal occured): +2.6.18/arch/x86/include/mach-xen/asm/pgtable-2level.h +2.6.18/arch/x86/include/mach-xen/asm/pgtable-2level-defs.h +2.6.19/arch/x86/include/mach-xen/asm/ptrace.h +2.6.23/arch/x86/include/mach-xen/asm/ptrace_64.h +2.6.23/arch/x86/kernel/vsyscall-note_32-xen.S +2.6.24/arch/x86/include/mach-xen/asm/arch_hooks_64.h +2.6.24/arch/x86/include/mach-xen/asm/bootsetup_64.h +2.6.24/arch/x86/include/mach-xen/asm/mmu_32.h +2.6.24/arch/x86/include/mach-xen/asm/mmu_64.h +2.6.24/arch/x86/include/mach-xen/asm/nmi_64.h +2.6.24/arch/x86/include/mach-xen/asm/setup.h +2.6.24/arch/x86/include/mach-xen/asm/time_64.h (added in 2.6.20) +2.6.24/arch/x86/include/mach-xen/mach_timer.h +2.6.24/arch/x86/kernel/early_printk_32-xen.c +2.6.25/arch/x86/ia32/syscall32-xen.c +2.6.25/arch/x86/ia32/syscall32_syscall-xen.S +2.6.25/arch/x86/ia32/vsyscall-int80.S +2.6.25/arch/x86/include/mach-xen/asm/msr.h +2.6.25/arch/x86/include/mach-xen/asm/page_32.h +2.6.25/arch/x86/include/mach-xen/asm/spinlock_32.h +2.6.25/arch/x86/include/mach-xen/asm/timer.h (added in 2.6.24) +2.6.25/arch/x86/include/mach-xen/asm/timer_64.h +2.6.25/arch/x86/include/mach-xen/mach_time.h +2.6.25/arch/x86/kernel/acpi/boot-xen.c +2.6.26/arch/x86/include/mach-xen/asm/dma-mapping_32.h +2.6.26/arch/x86/include/mach-xen/asm/dma-mapping_64.h +2.6.26/arch/x86/include/mach-xen/asm/nmi.h (added in 2.6.24) +2.6.26/arch/x86/include/mach-xen/asm/scatterlist.h (added in 2.6.24) +2.6.26/arch/x86/include/mach-xen/asm/scatterlist_32.h +2.6.26/arch/x86/include/mach-xen/asm/swiotlb_32.h +2.6.26/arch/x86/kernel/pci-dma_32-xen.c +2.6.26/arch/x86/kernel/pci-swiotlb_64-xen.c +2.6.26/include/xen/xencomm.h +2.6.27/arch/x86/include/mach-xen/asm/e820.h (added in 2.6.24) +2.6.27/arch/x86/include/mach-xen/asm/e820_64.h +2.6.27/arch/x86/include/mach-xen/asm/hw_irq.h (added in 2.6.24) +2.6.27/arch/x86/include/mach-xen/asm/hw_irq_32.h +2.6.27/arch/x86/include/mach-xen/asm/hw_irq_64.h +2.6.27/arch/x86/include/mach-xen/asm/io_32.h +2.6.27/arch/x86/include/mach-xen/asm/io_64.h +2.6.27/arch/x86/include/mach-xen/asm/irq.h (added in 2.6.24) +2.6.27/arch/x86/include/mach-xen/asm/irq_64.h +2.6.27/arch/x86/kernel/e820_32-xen.c +2.6.28/arch/x86/include/mach-xen/asm/pci_64.h +2.6.28/arch/x86/include/mach-xen/asm/segment.h (added in 2.6.24) +2.6.28/arch/x86/include/mach-xen/asm/segment_32.h +2.6.30/arch/x86/include/mach-xen/asm/page.h (added in 2.6.24) +2.6.30/arch/x86/include/mach-xen/asm/page_64.h +2.6.30/arch/x86/include/mach-xen/asm/pci_32.h +2.6.30/arch/x86/kernel/apic/apic_xen_64.c +2.6.30/arch/x86/kernel/apic/probe_64-xen.c +2.6.30/arch/x86/kernel/setup_percpu-xen.c (added in 2.6.27) +2.6.31/arch/x86/kernel/init_task-xen.c +2.6.32/arch/x86/include/mach-xen/asm/setup_arch.h +2.6.33/arch/x86/kernel/irq_32-xen.c +2.6.33/arch/x86/kernel/irq_64-xen.c + +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/acpi/processor_extcntl_xen.c 2010-03-22 12:00:53.000000000 +0100 +@@ -0,0 +1,208 @@ ++/* ++ * processor_extcntl_xen.c - interface to notify Xen ++ * ++ * Copyright (C) 2008, Intel corporation ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static int xen_cx_notifier(struct acpi_processor *pr, int action) ++{ ++ int ret, count = 0, i; ++ xen_platform_op_t op = { ++ .cmd = XENPF_set_processor_pminfo, ++ .interface_version = XENPF_INTERFACE_VERSION, ++ .u.set_pminfo.id = pr->acpi_id, ++ .u.set_pminfo.type = XEN_PM_CX, ++ }; ++ struct xen_processor_cx *data, *buf; ++ struct acpi_processor_cx *cx; ++ ++ /* Convert to Xen defined structure and hypercall */ ++ buf = kzalloc(pr->power.count * sizeof(struct xen_processor_cx), ++ GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ data = buf; ++ for (i = 1; i <= pr->power.count; i++) { ++ cx = &pr->power.states[i]; ++ /* Skip invalid cstate entry */ ++ if (!cx->valid) ++ continue; ++ ++ data->type = cx->type; ++ data->latency = cx->latency; ++ data->power = cx->power; ++ data->reg.space_id = cx->reg.space_id; ++ data->reg.bit_width = cx->reg.bit_width; ++ data->reg.bit_offset = cx->reg.bit_offset; ++ data->reg.access_size = cx->reg.reserved; ++ data->reg.address = cx->reg.address; ++ ++ /* Get dependency relationships */ ++ if (cx->csd_count) { ++ printk("Wow! _CSD is found. Not support for now!\n"); ++ kfree(buf); ++ return -EINVAL; ++ } else { ++ data->dpcnt = 0; ++ set_xen_guest_handle(data->dp, NULL); ++ } ++ ++ data++; ++ count++; ++ } ++ ++ if (!count) { ++ printk("No available Cx info for cpu %d\n", pr->acpi_id); ++ kfree(buf); ++ return -EINVAL; ++ } ++ ++ op.u.set_pminfo.u.power.count = count; ++ op.u.set_pminfo.u.power.flags.bm_control = pr->flags.bm_control; ++ op.u.set_pminfo.u.power.flags.bm_check = pr->flags.bm_check; ++ op.u.set_pminfo.u.power.flags.has_cst = pr->flags.has_cst; ++ op.u.set_pminfo.u.power.flags.power_setup_done = pr->flags.power_setup_done; ++ ++ set_xen_guest_handle(op.u.set_pminfo.u.power.states, buf); ++ ret = HYPERVISOR_platform_op(&op); ++ kfree(buf); ++ return ret; ++} ++ ++static int xen_px_notifier(struct acpi_processor *pr, int action) ++{ ++ int ret = -EINVAL; ++ xen_platform_op_t op = { ++ .cmd = XENPF_set_processor_pminfo, ++ .interface_version = XENPF_INTERFACE_VERSION, ++ .u.set_pminfo.id = pr->acpi_id, ++ .u.set_pminfo.type = XEN_PM_PX, ++ }; ++ struct xen_processor_performance *perf; ++ struct xen_processor_px *states = NULL; ++ struct acpi_processor_performance *px; ++ struct acpi_psd_package *pdomain; ++ ++ if (!pr) ++ return -EINVAL; ++ ++ perf = &op.u.set_pminfo.u.perf; ++ px = pr->performance; ++ if (!px) ++ return -EINVAL; ++ ++ switch(action) { ++ case PROCESSOR_PM_CHANGE: ++ /* ppc dynamic handle */ ++ perf->flags = XEN_PX_PPC; ++ perf->platform_limit = pr->performance_platform_limit; ++ ++ ret = HYPERVISOR_platform_op(&op); ++ break; ++ ++ case PROCESSOR_PM_INIT: ++ /* px normal init */ ++ perf->flags = XEN_PX_PPC | ++ XEN_PX_PCT | ++ XEN_PX_PSS | ++ XEN_PX_PSD; ++ ++ /* ppc */ ++ perf->platform_limit = pr->performance_platform_limit; ++ ++ /* pct */ ++ xen_convert_pct_reg(&perf->control_register, &px->control_register); ++ xen_convert_pct_reg(&perf->status_register, &px->status_register); ++ ++ /* pss */ ++ perf->state_count = px->state_count; ++ states = kzalloc(px->state_count*sizeof(xen_processor_px_t),GFP_KERNEL); ++ if (!states) ++ return -ENOMEM; ++ xen_convert_pss_states(states, px->states, px->state_count); ++ set_xen_guest_handle(perf->states, states); ++ ++ /* psd */ ++ pdomain = &px->domain_info; ++ xen_convert_psd_pack(&perf->domain_info, pdomain); ++ if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) ++ perf->shared_type = CPUFREQ_SHARED_TYPE_ALL; ++ else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) ++ perf->shared_type = CPUFREQ_SHARED_TYPE_ANY; ++ else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) ++ perf->shared_type = CPUFREQ_SHARED_TYPE_HW; ++ else { ++ ret = -ENODEV; ++ kfree(states); ++ break; ++ } ++ ++ ret = HYPERVISOR_platform_op(&op); ++ kfree(states); ++ break; ++ ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int xen_tx_notifier(struct acpi_processor *pr, int action) ++{ ++ return -EINVAL; ++} ++static int xen_hotplug_notifier(struct acpi_processor *pr, int event) ++{ ++ return -EINVAL; ++} ++ ++static struct processor_extcntl_ops xen_extcntl_ops = { ++ .hotplug = xen_hotplug_notifier, ++}; ++ ++void arch_acpi_processor_init_extcntl(const struct processor_extcntl_ops **ops) ++{ ++ unsigned int pmbits = (xen_start_info->flags & SIF_PM_MASK) >> 8; ++ ++ if (!pmbits) ++ return; ++ if (pmbits & XEN_PROCESSOR_PM_CX) ++ xen_extcntl_ops.pm_ops[PM_TYPE_IDLE] = xen_cx_notifier; ++ if (pmbits & XEN_PROCESSOR_PM_PX) ++ xen_extcntl_ops.pm_ops[PM_TYPE_PERF] = xen_px_notifier; ++ if (pmbits & XEN_PROCESSOR_PM_TX) ++ xen_extcntl_ops.pm_ops[PM_TYPE_THR] = xen_tx_notifier; ++ ++ *ops = &xen_extcntl_ops; ++} ++EXPORT_SYMBOL(arch_acpi_processor_init_extcntl); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/acpi/sleep_32-xen.c 2008-04-15 09:29:41.000000000 +0200 +@@ -0,0 +1,113 @@ ++/* ++ * sleep.c - x86-specific ACPI sleep support. ++ * ++ * Copyright (C) 2001-2003 Patrick Mochel ++ * Copyright (C) 2001-2003 Pavel Machek ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#ifndef CONFIG_ACPI_PV_SLEEP ++/* address in low memory of the wakeup routine. */ ++unsigned long acpi_wakeup_address = 0; ++unsigned long acpi_video_flags; ++extern char wakeup_start, wakeup_end; ++ ++extern unsigned long FASTCALL(acpi_copy_wakeup_routine(unsigned long)); ++#endif ++ ++/** ++ * acpi_save_state_mem - save kernel state ++ * ++ * Create an identity mapped page table and copy the wakeup routine to ++ * low memory. ++ */ ++int acpi_save_state_mem(void) ++{ ++#ifndef CONFIG_ACPI_PV_SLEEP ++ if (!acpi_wakeup_address) ++ return 1; ++ memcpy((void *)acpi_wakeup_address, &wakeup_start, ++ &wakeup_end - &wakeup_start); ++ acpi_copy_wakeup_routine(acpi_wakeup_address); ++#endif ++ return 0; ++} ++ ++/* ++ * acpi_restore_state - undo effects of acpi_save_state_mem ++ */ ++void acpi_restore_state_mem(void) ++{ ++} ++ ++/** ++ * acpi_reserve_bootmem - do _very_ early ACPI initialisation ++ * ++ * We allocate a page from the first 1MB of memory for the wakeup ++ * routine for when we come back from a sleep state. The ++ * runtime allocator allows specification of <16MB pages, but not ++ * <1MB pages. ++ */ ++void __init acpi_reserve_bootmem(void) ++{ ++#ifndef CONFIG_ACPI_PV_SLEEP ++ if ((&wakeup_end - &wakeup_start) > PAGE_SIZE) { ++ printk(KERN_ERR ++ "ACPI: Wakeup code way too big, S3 disabled.\n"); ++ return; ++ } ++ ++ acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE); ++ if (!acpi_wakeup_address) ++ printk(KERN_ERR "ACPI: Cannot allocate lowmem, S3 disabled.\n"); ++#endif ++} ++ ++#ifndef CONFIG_ACPI_PV_SLEEP ++static int __init acpi_sleep_setup(char *str) ++{ ++ while ((str != NULL) && (*str != '\0')) { ++ if (strncmp(str, "s3_bios", 7) == 0) ++ acpi_video_flags = 1; ++ if (strncmp(str, "s3_mode", 7) == 0) ++ acpi_video_flags |= 2; ++ str = strchr(str, ','); ++ if (str != NULL) ++ str += strspn(str, ", \t"); ++ } ++ return 1; ++} ++ ++__setup("acpi_sleep=", acpi_sleep_setup); ++ ++static __init int reset_videomode_after_s3(struct dmi_system_id *d) ++{ ++ acpi_video_flags |= 2; ++ return 0; ++} ++ ++static __initdata struct dmi_system_id acpisleep_dmi_table[] = { ++ { /* Reset video mode after returning from ACPI S3 sleep */ ++ .callback = reset_videomode_after_s3, ++ .ident = "Toshiba Satellite 4030cdt", ++ .matches = { ++ DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"), ++ }, ++ }, ++ {} ++}; ++ ++static int __init acpisleep_dmi_init(void) ++{ ++ dmi_check_system(acpisleep_dmi_table); ++ return 0; ++} ++ ++core_initcall(acpisleep_dmi_init); ++#endif /* CONFIG_ACPI_PV_SLEEP */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/apic/apic-xen.c 2007-06-12 13:12:48.000000000 +0200 +@@ -0,0 +1,155 @@ ++/* ++ * Local APIC handling, local APIC timers ++ * ++ * (c) 1999, 2000 Ingo Molnar ++ * ++ * Fixes ++ * Maciej W. Rozycki : Bits for genuine 82489DX APICs; ++ * thanks to Eric Gilmore ++ * and Rolf G. Tews ++ * for testing these extensively. ++ * Maciej W. Rozycki : Various updates and fixes. ++ * Mikael Pettersson : Power Management for UP-APIC. ++ * Pavel Machek and ++ * Mikael Pettersson : PM converted to driver model. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "io_ports.h" ++ ++#ifndef CONFIG_XEN ++/* ++ * cpu_mask that denotes the CPUs that needs timer interrupt coming in as ++ * IPIs in place of local APIC timers ++ */ ++static cpumask_t timer_bcast_ipi; ++#endif ++ ++/* ++ * Knob to control our willingness to enable the local APIC. ++ */ ++int enable_local_apic __initdata = 0; /* -1=force-disable, +1=force-enable */ ++ ++/* ++ * Debug level ++ */ ++int apic_verbosity; ++ ++#ifndef CONFIG_XEN ++static int modern_apic(void) ++{ ++ unsigned int lvr, version; ++ /* AMD systems use old APIC versions, so check the CPU */ ++ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && ++ boot_cpu_data.x86 >= 0xf) ++ return 1; ++ lvr = apic_read(APIC_LVR); ++ version = GET_APIC_VERSION(lvr); ++ return version >= 0x14; ++} ++#endif /* !CONFIG_XEN */ ++ ++/* ++ * 'what should we do if we get a hw irq event on an illegal vector'. ++ * each architecture has to answer this themselves. ++ */ ++void ack_bad_irq(unsigned int irq) ++{ ++ printk("unexpected IRQ trap at vector %02x\n", irq); ++ /* ++ * Currently unexpected vectors happen only on SMP and APIC. ++ * We _must_ ack these because every local APIC has only N ++ * irq slots per priority level, and a 'hanging, unacked' IRQ ++ * holds up an irq slot - in excessive cases (when multiple ++ * unexpected vectors occur) that might lock up the APIC ++ * completely. ++ * But only ack when the APIC is enabled -AK ++ */ ++ if (cpu_has_apic) ++ ack_APIC_irq(); ++} ++ ++int get_physical_broadcast(void) ++{ ++ return 0xff; ++} ++ ++#ifndef CONFIG_XEN ++#ifndef CONFIG_SMP ++static void up_apic_timer_interrupt_call(struct pt_regs *regs) ++{ ++ int cpu = smp_processor_id(); ++ ++ /* ++ * the NMI deadlock-detector uses this. ++ */ ++ per_cpu(irq_stat, cpu).apic_timer_irqs++; ++ ++ smp_local_timer_interrupt(regs); ++} ++#endif ++ ++void smp_send_timer_broadcast_ipi(struct pt_regs *regs) ++{ ++ cpumask_t mask; ++ ++ cpus_and(mask, cpu_online_map, timer_bcast_ipi); ++ if (!cpus_empty(mask)) { ++#ifdef CONFIG_SMP ++ send_IPI_mask(mask, LOCAL_TIMER_VECTOR); ++#else ++ /* ++ * We can directly call the apic timer interrupt handler ++ * in UP case. Minus all irq related functions ++ */ ++ up_apic_timer_interrupt_call(regs); ++#endif ++ } ++} ++#endif ++ ++int setup_profiling_timer(unsigned int multiplier) ++{ ++ return -EINVAL; ++} ++ ++/* ++ * This initializes the IO-APIC and APIC hardware if this is ++ * a UP kernel. ++ */ ++int __init APIC_init_uniprocessor (void) ++{ ++#ifdef CONFIG_X86_IO_APIC ++ if (smp_found_config) ++ if (!skip_ioapic_setup && nr_ioapics) ++ setup_IO_APIC(); ++#endif ++ ++ return 0; ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/cpu/common-xen.c 2009-05-19 09:16:41.000000000 +0200 +@@ -0,0 +1,745 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_X86_LOCAL_APIC ++#include ++#include ++#include ++#else ++#ifdef CONFIG_XEN ++#define phys_pkg_id(a,b) a ++#endif ++#endif ++#include ++ ++#include "cpu.h" ++ ++DEFINE_PER_CPU(struct Xgt_desc_struct, cpu_gdt_descr); ++EXPORT_PER_CPU_SYMBOL(cpu_gdt_descr); ++ ++#ifndef CONFIG_XEN ++DEFINE_PER_CPU(unsigned char, cpu_16bit_stack[CPU_16BIT_STACK_SIZE]); ++EXPORT_PER_CPU_SYMBOL(cpu_16bit_stack); ++#endif ++ ++static int cachesize_override __cpuinitdata = -1; ++static int disable_x86_fxsr __cpuinitdata; ++static int disable_x86_serial_nr __cpuinitdata = 1; ++static int disable_x86_sep __cpuinitdata; ++ ++struct cpu_dev * cpu_devs[X86_VENDOR_NUM] = {}; ++ ++extern int disable_pse; ++ ++static void default_init(struct cpuinfo_x86 * c) ++{ ++ /* Not much we can do here... */ ++ /* Check if at least it has cpuid */ ++ if (c->cpuid_level == -1) { ++ /* No cpuid. It must be an ancient CPU */ ++ if (c->x86 == 4) ++ strcpy(c->x86_model_id, "486"); ++ else if (c->x86 == 3) ++ strcpy(c->x86_model_id, "386"); ++ } ++} ++ ++static struct cpu_dev default_cpu = { ++ .c_init = default_init, ++ .c_vendor = "Unknown", ++}; ++static struct cpu_dev * this_cpu = &default_cpu; ++ ++static int __init cachesize_setup(char *str) ++{ ++ get_option (&str, &cachesize_override); ++ return 1; ++} ++__setup("cachesize=", cachesize_setup); ++ ++int __cpuinit get_model_name(struct cpuinfo_x86 *c) ++{ ++ unsigned int *v; ++ char *p, *q; ++ ++ if (cpuid_eax(0x80000000) < 0x80000004) ++ return 0; ++ ++ v = (unsigned int *) c->x86_model_id; ++ cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); ++ cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); ++ cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); ++ c->x86_model_id[48] = 0; ++ ++ /* Intel chips right-justify this string for some dumb reason; ++ undo that brain damage */ ++ p = q = &c->x86_model_id[0]; ++ while ( *p == ' ' ) ++ p++; ++ if ( p != q ) { ++ while ( *p ) ++ *q++ = *p++; ++ while ( q <= &c->x86_model_id[48] ) ++ *q++ = '\0'; /* Zero-pad the rest */ ++ } ++ ++ return 1; ++} ++ ++ ++void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c) ++{ ++ unsigned int n, dummy, ecx, edx, l2size; ++ ++ n = cpuid_eax(0x80000000); ++ ++ if (n >= 0x80000005) { ++ cpuid(0x80000005, &dummy, &dummy, &ecx, &edx); ++ printk(KERN_INFO "CPU: L1 I Cache: %dK (%d bytes/line), D cache %dK (%d bytes/line)\n", ++ edx>>24, edx&0xFF, ecx>>24, ecx&0xFF); ++ c->x86_cache_size=(ecx>>24)+(edx>>24); ++ } ++ ++ if (n < 0x80000006) /* Some chips just has a large L1. */ ++ return; ++ ++ ecx = cpuid_ecx(0x80000006); ++ l2size = ecx >> 16; ++ ++ /* do processor-specific cache resizing */ ++ if (this_cpu->c_size_cache) ++ l2size = this_cpu->c_size_cache(c,l2size); ++ ++ /* Allow user to override all this if necessary. */ ++ if (cachesize_override != -1) ++ l2size = cachesize_override; ++ ++ if ( l2size == 0 ) ++ return; /* Again, no L2 cache is possible */ ++ ++ c->x86_cache_size = l2size; ++ ++ printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line)\n", ++ l2size, ecx & 0xFF); ++} ++ ++/* Naming convention should be: [()] */ ++/* This table only is used unless init_() below doesn't set it; */ ++/* in particular, if CPUID levels 0x80000002..4 are supported, this isn't used */ ++ ++/* Look up CPU names by table lookup. */ ++static char __cpuinit *table_lookup_model(struct cpuinfo_x86 *c) ++{ ++ struct cpu_model_info *info; ++ ++ if ( c->x86_model >= 16 ) ++ return NULL; /* Range check */ ++ ++ if (!this_cpu) ++ return NULL; ++ ++ info = this_cpu->c_models; ++ ++ while (info && info->family) { ++ if (info->family == c->x86) ++ return info->model_names[c->x86_model]; ++ info++; ++ } ++ return NULL; /* Not found */ ++} ++ ++ ++static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c, int early) ++{ ++ char *v = c->x86_vendor_id; ++ int i; ++ static int printed; ++ ++ for (i = 0; i < X86_VENDOR_NUM; i++) { ++ if (cpu_devs[i]) { ++ if (!strcmp(v,cpu_devs[i]->c_ident[0]) || ++ (cpu_devs[i]->c_ident[1] && ++ !strcmp(v,cpu_devs[i]->c_ident[1]))) { ++ c->x86_vendor = i; ++ if (!early) ++ this_cpu = cpu_devs[i]; ++ return; ++ } ++ } ++ } ++ if (!printed) { ++ printed++; ++ printk(KERN_ERR "CPU: Vendor unknown, using generic init.\n"); ++ printk(KERN_ERR "CPU: Your system may be unstable.\n"); ++ } ++ c->x86_vendor = X86_VENDOR_UNKNOWN; ++ this_cpu = &default_cpu; ++} ++ ++ ++static int __init x86_fxsr_setup(char * s) ++{ ++ disable_x86_fxsr = 1; ++ return 1; ++} ++__setup("nofxsr", x86_fxsr_setup); ++ ++ ++static int __init x86_sep_setup(char * s) ++{ ++ disable_x86_sep = 1; ++ return 1; ++} ++__setup("nosep", x86_sep_setup); ++ ++ ++/* Standard macro to see if a specific flag is changeable */ ++static inline int flag_is_changeable_p(u32 flag) ++{ ++ u32 f1, f2; ++ ++ asm("pushfl\n\t" ++ "pushfl\n\t" ++ "popl %0\n\t" ++ "movl %0,%1\n\t" ++ "xorl %2,%0\n\t" ++ "pushl %0\n\t" ++ "popfl\n\t" ++ "pushfl\n\t" ++ "popl %0\n\t" ++ "popfl\n\t" ++ : "=&r" (f1), "=&r" (f2) ++ : "ir" (flag)); ++ ++ return ((f1^f2) & flag) != 0; ++} ++ ++ ++/* Probe for the CPUID instruction */ ++static int __cpuinit have_cpuid_p(void) ++{ ++ return flag_is_changeable_p(X86_EFLAGS_ID); ++} ++ ++/* Do minimum CPU detection early. ++ Fields really needed: vendor, cpuid_level, family, model, mask, cache alignment. ++ The others are not touched to avoid unwanted side effects. ++ ++ WARNING: this function is only called on the BP. Don't add code here ++ that is supposed to run on all CPUs. */ ++static void __init early_cpu_detect(void) ++{ ++ struct cpuinfo_x86 *c = &boot_cpu_data; ++ ++ c->x86_cache_alignment = 32; ++ ++ if (!have_cpuid_p()) ++ return; ++ ++ /* Get vendor name */ ++ cpuid(0x00000000, &c->cpuid_level, ++ (int *)&c->x86_vendor_id[0], ++ (int *)&c->x86_vendor_id[8], ++ (int *)&c->x86_vendor_id[4]); ++ ++ get_cpu_vendor(c, 1); ++ ++ c->x86 = 4; ++ if (c->cpuid_level >= 0x00000001) { ++ u32 junk, tfms, cap0, misc; ++ cpuid(0x00000001, &tfms, &misc, &junk, &cap0); ++ c->x86 = (tfms >> 8) & 15; ++ c->x86_model = (tfms >> 4) & 15; ++ if (c->x86 == 0xf) ++ c->x86 += (tfms >> 20) & 0xff; ++ if (c->x86 >= 0x6) ++ c->x86_model += ((tfms >> 16) & 0xF) << 4; ++ c->x86_mask = tfms & 15; ++ if (cap0 & (1<<19)) ++ c->x86_cache_alignment = ((misc >> 8) & 0xff) * 8; ++ } ++} ++ ++void __cpuinit generic_identify(struct cpuinfo_x86 * c) ++{ ++ u32 tfms, xlvl; ++ int ebx; ++ ++ if (have_cpuid_p()) { ++ /* Get vendor name */ ++ cpuid(0x00000000, &c->cpuid_level, ++ (int *)&c->x86_vendor_id[0], ++ (int *)&c->x86_vendor_id[8], ++ (int *)&c->x86_vendor_id[4]); ++ ++ get_cpu_vendor(c, 0); ++ /* Initialize the standard set of capabilities */ ++ /* Note that the vendor-specific code below might override */ ++ ++ /* Intel-defined flags: level 0x00000001 */ ++ if ( c->cpuid_level >= 0x00000001 ) { ++ u32 capability, excap; ++ cpuid(0x00000001, &tfms, &ebx, &excap, &capability); ++ c->x86_capability[0] = capability; ++ c->x86_capability[4] = excap; ++ c->x86 = (tfms >> 8) & 15; ++ c->x86_model = (tfms >> 4) & 15; ++ if (c->x86 == 0xf) ++ c->x86 += (tfms >> 20) & 0xff; ++ if (c->x86 >= 0x6) ++ c->x86_model += ((tfms >> 16) & 0xF) << 4; ++ c->x86_mask = tfms & 15; ++#ifndef CONFIG_XEN ++#ifdef CONFIG_X86_HT ++ c->apicid = phys_pkg_id((ebx >> 24) & 0xFF, 0); ++#else ++ c->apicid = (ebx >> 24) & 0xFF; ++#endif ++#endif ++ } else { ++ /* Have CPUID level 0 only - unheard of */ ++ c->x86 = 4; ++ } ++ ++ /* AMD-defined flags: level 0x80000001 */ ++ xlvl = cpuid_eax(0x80000000); ++ if ( (xlvl & 0xffff0000) == 0x80000000 ) { ++ if ( xlvl >= 0x80000001 ) { ++ c->x86_capability[1] = cpuid_edx(0x80000001); ++ c->x86_capability[6] = cpuid_ecx(0x80000001); ++ } ++ if ( xlvl >= 0x80000004 ) ++ get_model_name(c); /* Default name */ ++ } ++ } ++ ++ early_intel_workaround(c); ++ ++#ifdef CONFIG_X86_HT ++ c->phys_proc_id = (cpuid_ebx(1) >> 24) & 0xff; ++#endif ++} ++ ++static void __cpuinit squash_the_stupid_serial_number(struct cpuinfo_x86 *c) ++{ ++ if (cpu_has(c, X86_FEATURE_PN) && disable_x86_serial_nr ) { ++ /* Disable processor serial number */ ++ unsigned long lo,hi; ++ rdmsr(MSR_IA32_BBL_CR_CTL,lo,hi); ++ lo |= 0x200000; ++ wrmsr(MSR_IA32_BBL_CR_CTL,lo,hi); ++ printk(KERN_NOTICE "CPU serial number disabled.\n"); ++ clear_bit(X86_FEATURE_PN, c->x86_capability); ++ ++ /* Disabling the serial number may affect the cpuid level */ ++ c->cpuid_level = cpuid_eax(0); ++ } ++} ++ ++static int __init x86_serial_nr_setup(char *s) ++{ ++ disable_x86_serial_nr = 0; ++ return 1; ++} ++__setup("serialnumber", x86_serial_nr_setup); ++ ++ ++ ++/* ++ * This does the hard work of actually picking apart the CPU stuff... ++ */ ++void __cpuinit identify_cpu(struct cpuinfo_x86 *c) ++{ ++ int i; ++ ++ c->loops_per_jiffy = loops_per_jiffy; ++ c->x86_cache_size = -1; ++ c->x86_vendor = X86_VENDOR_UNKNOWN; ++ c->cpuid_level = -1; /* CPUID not detected */ ++ c->x86_model = c->x86_mask = 0; /* So far unknown... */ ++ c->x86_vendor_id[0] = '\0'; /* Unset */ ++ c->x86_model_id[0] = '\0'; /* Unset */ ++ c->x86_max_cores = 1; ++ memset(&c->x86_capability, 0, sizeof c->x86_capability); ++ ++ if (!have_cpuid_p()) { ++ /* First of all, decide if this is a 486 or higher */ ++ /* It's a 486 if we can modify the AC flag */ ++ if ( flag_is_changeable_p(X86_EFLAGS_AC) ) ++ c->x86 = 4; ++ else ++ c->x86 = 3; ++ } ++ ++ generic_identify(c); ++ ++ printk(KERN_DEBUG "CPU: After generic identify, caps:"); ++ for (i = 0; i < NCAPINTS; i++) ++ printk(" %08lx", c->x86_capability[i]); ++ printk("\n"); ++ ++ if (this_cpu->c_identify) { ++ this_cpu->c_identify(c); ++ ++ printk(KERN_DEBUG "CPU: After vendor identify, caps:"); ++ for (i = 0; i < NCAPINTS; i++) ++ printk(" %08lx", c->x86_capability[i]); ++ printk("\n"); ++ } ++ ++ /* ++ * Vendor-specific initialization. In this section we ++ * canonicalize the feature flags, meaning if there are ++ * features a certain CPU supports which CPUID doesn't ++ * tell us, CPUID claiming incorrect flags, or other bugs, ++ * we handle them here. ++ * ++ * At the end of this section, c->x86_capability better ++ * indicate the features this CPU genuinely supports! ++ */ ++ if (this_cpu->c_init) ++ this_cpu->c_init(c); ++ ++ /* Disable the PN if appropriate */ ++ squash_the_stupid_serial_number(c); ++ ++ /* ++ * The vendor-specific functions might have changed features. Now ++ * we do "generic changes." ++ */ ++ ++ /* TSC disabled? */ ++ if ( tsc_disable ) ++ clear_bit(X86_FEATURE_TSC, c->x86_capability); ++ ++ /* FXSR disabled? */ ++ if (disable_x86_fxsr) { ++ clear_bit(X86_FEATURE_FXSR, c->x86_capability); ++ clear_bit(X86_FEATURE_XMM, c->x86_capability); ++ } ++ ++ /* SEP disabled? */ ++ if (disable_x86_sep) ++ clear_bit(X86_FEATURE_SEP, c->x86_capability); ++ ++ if (disable_pse) ++ clear_bit(X86_FEATURE_PSE, c->x86_capability); ++ ++ /* If the model name is still unset, do table lookup. */ ++ if ( !c->x86_model_id[0] ) { ++ char *p; ++ p = table_lookup_model(c); ++ if ( p ) ++ strcpy(c->x86_model_id, p); ++ else ++ /* Last resort... */ ++ sprintf(c->x86_model_id, "%02x/%02x", ++ c->x86, c->x86_model); ++ } ++ ++ /* Now the feature flags better reflect actual CPU features! */ ++ ++ printk(KERN_DEBUG "CPU: After all inits, caps:"); ++ for (i = 0; i < NCAPINTS; i++) ++ printk(" %08lx", c->x86_capability[i]); ++ printk("\n"); ++ ++ /* ++ * On SMP, boot_cpu_data holds the common feature set between ++ * all CPUs; so make sure that we indicate which features are ++ * common between the CPUs. The first time this routine gets ++ * executed, c == &boot_cpu_data. ++ */ ++ if ( c != &boot_cpu_data ) { ++ /* AND the already accumulated flags with these */ ++ for ( i = 0 ; i < NCAPINTS ; i++ ) ++ boot_cpu_data.x86_capability[i] &= c->x86_capability[i]; ++ } ++ ++ /* Init Machine Check Exception if available. */ ++ mcheck_init(c); ++ ++ if (c == &boot_cpu_data) ++ sysenter_setup(); ++ enable_sep_cpu(); ++ ++ if (c == &boot_cpu_data) ++ mtrr_bp_init(); ++ else ++ mtrr_ap_init(); ++} ++ ++#ifdef CONFIG_X86_HT ++void __cpuinit detect_ht(struct cpuinfo_x86 *c) ++{ ++ u32 eax, ebx, ecx, edx; ++ int index_msb, core_bits; ++ ++ cpuid(1, &eax, &ebx, &ecx, &edx); ++ ++ if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY)) ++ return; ++ ++ smp_num_siblings = (ebx & 0xff0000) >> 16; ++ ++ if (smp_num_siblings == 1) { ++ printk(KERN_INFO "CPU: Hyper-Threading is disabled\n"); ++ } else if (smp_num_siblings > 1 ) { ++ ++ if (smp_num_siblings > NR_CPUS) { ++ printk(KERN_WARNING "CPU: Unsupported number of the " ++ "siblings %d", smp_num_siblings); ++ smp_num_siblings = 1; ++ return; ++ } ++ ++ index_msb = get_count_order(smp_num_siblings); ++ c->phys_proc_id = phys_pkg_id((ebx >> 24) & 0xFF, index_msb); ++ ++ printk(KERN_INFO "CPU: Physical Processor ID: %d\n", ++ c->phys_proc_id); ++ ++ smp_num_siblings = smp_num_siblings / c->x86_max_cores; ++ ++ index_msb = get_count_order(smp_num_siblings) ; ++ ++ core_bits = get_count_order(c->x86_max_cores); ++ ++ c->cpu_core_id = phys_pkg_id((ebx >> 24) & 0xFF, index_msb) & ++ ((1 << core_bits) - 1); ++ ++ if (c->x86_max_cores > 1) ++ printk(KERN_INFO "CPU: Processor Core ID: %d\n", ++ c->cpu_core_id); ++ } ++} ++#endif ++ ++void __cpuinit print_cpu_info(struct cpuinfo_x86 *c) ++{ ++ char *vendor = NULL; ++ ++ if (c->x86_vendor < X86_VENDOR_NUM) ++ vendor = this_cpu->c_vendor; ++ else if (c->cpuid_level >= 0) ++ vendor = c->x86_vendor_id; ++ ++ if (vendor && strncmp(c->x86_model_id, vendor, strlen(vendor))) ++ printk("%s ", vendor); ++ ++ if (!c->x86_model_id[0]) ++ printk("%d86", c->x86); ++ else ++ printk("%s", c->x86_model_id); ++ ++ if (c->x86_mask || c->cpuid_level >= 0) ++ printk(" stepping %02x\n", c->x86_mask); ++ else ++ printk("\n"); ++} ++ ++cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE; ++ ++/* This is hacky. :) ++ * We're emulating future behavior. ++ * In the future, the cpu-specific init functions will be called implicitly ++ * via the magic of initcalls. ++ * They will insert themselves into the cpu_devs structure. ++ * Then, when cpu_init() is called, we can just iterate over that array. ++ */ ++ ++extern int intel_cpu_init(void); ++extern int cyrix_init_cpu(void); ++extern int nsc_init_cpu(void); ++extern int amd_init_cpu(void); ++extern int centaur_init_cpu(void); ++extern int transmeta_init_cpu(void); ++extern int rise_init_cpu(void); ++extern int nexgen_init_cpu(void); ++extern int umc_init_cpu(void); ++ ++void __init early_cpu_init(void) ++{ ++ intel_cpu_init(); ++ cyrix_init_cpu(); ++ nsc_init_cpu(); ++ amd_init_cpu(); ++ centaur_init_cpu(); ++ transmeta_init_cpu(); ++ rise_init_cpu(); ++ nexgen_init_cpu(); ++ umc_init_cpu(); ++ early_cpu_detect(); ++ ++#ifdef CONFIG_DEBUG_PAGEALLOC ++ /* pse is not compatible with on-the-fly unmapping, ++ * disable it even if the cpus claim to support it. ++ */ ++ clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability); ++ disable_pse = 1; ++#endif ++} ++ ++static void __cpuinit cpu_gdt_init(const struct Xgt_desc_struct *gdt_descr) ++{ ++ unsigned long frames[16]; ++ unsigned long va; ++ int f; ++ ++ for (va = gdt_descr->address, f = 0; ++ va < gdt_descr->address + gdt_descr->size; ++ va += PAGE_SIZE, f++) { ++ frames[f] = virt_to_mfn(va); ++ make_lowmem_page_readonly( ++ (void *)va, XENFEAT_writable_descriptor_tables); ++ } ++ if (HYPERVISOR_set_gdt(frames, (gdt_descr->size + 1) / 8)) ++ BUG(); ++} ++ ++/* ++ * cpu_init() initializes state that is per-CPU. Some data is already ++ * initialized (naturally) in the bootstrap process, such as the GDT ++ * and IDT. We reload them nevertheless, this function acts as a ++ * 'CPU state barrier', nothing should get across. ++ */ ++void __cpuinit cpu_init(void) ++{ ++ int cpu = smp_processor_id(); ++#ifndef CONFIG_X86_NO_TSS ++ struct tss_struct * t = &per_cpu(init_tss, cpu); ++#endif ++ struct thread_struct *thread = ¤t->thread; ++ struct desc_struct *gdt; ++ struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu); ++ ++ if (cpu_test_and_set(cpu, cpu_initialized)) { ++ printk(KERN_WARNING "CPU#%d already initialized!\n", cpu); ++ for (;;) local_irq_enable(); ++ } ++ printk(KERN_INFO "Initializing CPU#%d\n", cpu); ++ ++ if (cpu_has_vme || cpu_has_de) ++ clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE); ++ if (tsc_disable && cpu_has_tsc) { ++ printk(KERN_NOTICE "Disabling TSC...\n"); ++ /**** FIX-HPA: DOES THIS REALLY BELONG HERE? ****/ ++ clear_bit(X86_FEATURE_TSC, boot_cpu_data.x86_capability); ++ set_in_cr4(X86_CR4_TSD); ++ } ++ ++#ifndef CONFIG_XEN ++ /* The CPU hotplug case */ ++ if (cpu_gdt_descr->address) { ++ gdt = (struct desc_struct *)cpu_gdt_descr->address; ++ memset(gdt, 0, PAGE_SIZE); ++ goto old_gdt; ++ } ++ /* ++ * This is a horrible hack to allocate the GDT. The problem ++ * is that cpu_init() is called really early for the boot CPU ++ * (and hence needs bootmem) but much later for the secondary ++ * CPUs, when bootmem will have gone away ++ */ ++ if (NODE_DATA(0)->bdata->node_bootmem_map) { ++ gdt = (struct desc_struct *)alloc_bootmem_pages(PAGE_SIZE); ++ /* alloc_bootmem_pages panics on failure, so no check */ ++ memset(gdt, 0, PAGE_SIZE); ++ } else { ++ gdt = (struct desc_struct *)get_zeroed_page(GFP_KERNEL); ++ if (unlikely(!gdt)) { ++ printk(KERN_CRIT "CPU%d failed to allocate GDT\n", cpu); ++ for (;;) ++ local_irq_enable(); ++ } ++ } ++old_gdt: ++ /* ++ * Initialize the per-CPU GDT with the boot GDT, ++ * and set up the GDT descriptor: ++ */ ++ memcpy(gdt, cpu_gdt_table, GDT_SIZE); ++ ++ /* Set up GDT entry for 16bit stack */ ++ *(__u64 *)(&gdt[GDT_ENTRY_ESPFIX_SS]) |= ++ ((((__u64)stk16_off) << 16) & 0x000000ffffff0000ULL) | ++ ((((__u64)stk16_off) << 32) & 0xff00000000000000ULL) | ++ (CPU_16BIT_STACK_SIZE - 1); ++ ++ cpu_gdt_descr->size = GDT_SIZE - 1; ++ cpu_gdt_descr->address = (unsigned long)gdt; ++#else ++ if (cpu == 0 && cpu_gdt_descr->address == 0) { ++ gdt = (struct desc_struct *)alloc_bootmem_pages(PAGE_SIZE); ++ /* alloc_bootmem_pages panics on failure, so no check */ ++ memset(gdt, 0, PAGE_SIZE); ++ ++ memcpy(gdt, cpu_gdt_table, GDT_SIZE); ++ ++ cpu_gdt_descr->size = GDT_SIZE; ++ cpu_gdt_descr->address = (unsigned long)gdt; ++ } ++#endif ++ ++ cpu_gdt_init(cpu_gdt_descr); ++ ++ /* ++ * Set up and load the per-CPU TSS and LDT ++ */ ++ atomic_inc(&init_mm.mm_count); ++ current->active_mm = &init_mm; ++ if (current->mm) ++ BUG(); ++ enter_lazy_tlb(&init_mm, current); ++ ++ load_esp0(t, thread); ++ ++ load_LDT(&init_mm.context); ++ ++#ifdef CONFIG_DOUBLEFAULT ++ /* Set up doublefault TSS pointer in the GDT */ ++ __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss); ++#endif ++ ++ /* Clear %fs and %gs. */ ++ asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs"); ++ ++ /* Clear all 6 debug registers: */ ++ set_debugreg(0, 0); ++ set_debugreg(0, 1); ++ set_debugreg(0, 2); ++ set_debugreg(0, 3); ++ set_debugreg(0, 6); ++ set_debugreg(0, 7); ++ ++ /* ++ * Force FPU initialization: ++ */ ++ current_thread_info()->status = 0; ++ clear_used_math(); ++ mxcsr_feature_mask_init(); ++} ++ ++#ifdef CONFIG_HOTPLUG_CPU ++void __cpuinit cpu_uninit(void) ++{ ++ int cpu = raw_smp_processor_id(); ++ cpu_clear(cpu, cpu_initialized); ++ ++ /* lazy TLB state */ ++ per_cpu(cpu_tlbstate, cpu).state = 0; ++ per_cpu(cpu_tlbstate, cpu).active_mm = &init_mm; ++} ++#endif +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/cpu/mcheck/mce_dom0.c 2009-10-01 11:00:47.000000000 +0200 +@@ -0,0 +1,134 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int convert_log(struct mc_info *mi) ++{ ++ struct mcinfo_common *mic = NULL; ++ struct mcinfo_global *mc_global; ++ struct mcinfo_bank *mc_bank; ++ struct mce m; ++ ++ x86_mcinfo_lookup(mic, mi, MC_TYPE_GLOBAL); ++ if (mic == NULL) ++ { ++ printk(KERN_ERR "DOM0_MCE_LOG: global data is NULL\n"); ++ return -1; ++ } ++ ++ mc_global = (struct mcinfo_global*)mic; ++ m.mcgstatus = mc_global->mc_gstatus; ++ m.cpu = mc_global->mc_coreid;/*for test*/ ++ x86_mcinfo_lookup(mic, mi, MC_TYPE_BANK); ++ do ++ { ++ if (mic == NULL || mic->size == 0) ++ break; ++ if (mic->type == MC_TYPE_BANK) ++ { ++ mc_bank = (struct mcinfo_bank*)mic; ++ m.misc = mc_bank->mc_misc; ++ m.status = mc_bank->mc_status; ++ m.addr = mc_bank->mc_addr; ++ m.tsc = mc_bank->mc_tsc; ++ m.res1 = mc_bank->mc_ctrl2; ++ m.bank = mc_bank->mc_bank; ++ printk(KERN_DEBUG "[CPU%d, BANK%d, addr %llx, state %llx]\n", ++ m.bank, m.cpu, m.addr, m.status); ++ /*log this record*/ ++ mce_log(&m); ++ } ++ mic = x86_mcinfo_next(mic); ++ }while (1); ++ ++ return 0; ++} ++ ++static struct mc_info *g_mi; ++ ++/*dom0 mce virq handler, logging physical mce error info*/ ++ ++static irqreturn_t mce_dom0_interrupt(int irq, void *dev_id, ++ struct pt_regs *regs) ++{ ++ xen_mc_t mc_op; ++ int result = 0; ++ ++ printk(KERN_DEBUG "MCE_DOM0_LOG: enter dom0 mce vIRQ handler\n"); ++ mc_op.cmd = XEN_MC_fetch; ++ mc_op.interface_version = XEN_MCA_INTERFACE_VERSION; ++ set_xen_guest_handle(mc_op.u.mc_fetch.data, g_mi); ++urgent: ++ mc_op.u.mc_fetch.flags = XEN_MC_URGENT; ++ result = HYPERVISOR_mca(&mc_op); ++ if (result || mc_op.u.mc_fetch.flags & XEN_MC_NODATA || ++ mc_op.u.mc_fetch.flags & XEN_MC_FETCHFAILED) ++ { ++ printk(KERN_DEBUG "MCE_DOM0_LOG: No more urgent data\n"); ++ goto nonurgent; ++ } ++ else ++ { ++ result = convert_log(g_mi); ++ if (result) { ++ printk(KERN_ERR "MCE_DOM0_LOG: Log conversion failed\n"); ++ goto end; ++ } ++ /* After fetching the telem from DOM0, we need to dec the telem's ++ * refcnt and release the entry. The telem is reserved and inc ++ * refcnt when filling the telem. ++ */ ++ mc_op.u.mc_fetch.flags = XEN_MC_URGENT | XEN_MC_ACK; ++ result = HYPERVISOR_mca(&mc_op); ++ ++ goto urgent; ++ } ++nonurgent: ++ mc_op.u.mc_fetch.flags = XEN_MC_NONURGENT; ++ result = HYPERVISOR_mca(&mc_op); ++ if (result || mc_op.u.mc_fetch.flags & XEN_MC_NODATA || ++ mc_op.u.mc_fetch.flags & XEN_MC_FETCHFAILED) ++ { ++ printk(KERN_DEBUG "MCE_DOM0_LOG: No more nonurgent data\n"); ++ goto end; ++ } ++ else ++ { ++ result = convert_log(g_mi); ++ if (result) { ++ printk(KERN_ERR "MCE_DOM0_LOG: Log conversion failed\n"); ++ goto end; ++ } ++ /* After fetching the telem from DOM0, we need to dec the telem's ++ * refcnt and release the entry. The telem is reserved and inc ++ * refcnt when filling the telem. ++ */ ++ mc_op.u.mc_fetch.flags = XEN_MC_NONURGENT | XEN_MC_ACK; ++ result = HYPERVISOR_mca(&mc_op); ++ ++ goto nonurgent; ++ } ++end: ++ return IRQ_HANDLED; ++} ++ ++void bind_virq_for_mce(void) ++{ ++ int ret; ++ ++ ret = bind_virq_to_irqhandler(VIRQ_MCA, 0, ++ mce_dom0_interrupt, 0, "mce", NULL); ++ ++ g_mi = kmalloc(sizeof(struct mc_info), GFP_KERNEL); ++ if (ret < 0) ++ printk(KERN_ERR "MCE_DOM0_LOG: bind_virq for DOM0 failed\n"); ++ ++ /* Log the machine checks left over from the previous reset. */ ++ mce_dom0_interrupt(VIRQ_MCA, NULL, NULL); ++} ++ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/cpu/mtrr/main-xen.c 2008-01-28 12:24:18.000000000 +0100 +@@ -0,0 +1,198 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "mtrr.h" ++ ++static DEFINE_MUTEX(mtrr_mutex); ++ ++void generic_get_mtrr(unsigned int reg, unsigned long *base, ++ unsigned int *size, mtrr_type * type) ++{ ++ struct xen_platform_op op; ++ ++ op.cmd = XENPF_read_memtype; ++ op.u.read_memtype.reg = reg; ++ if (unlikely(HYPERVISOR_platform_op(&op))) ++ memset(&op.u.read_memtype, 0, sizeof(op.u.read_memtype)); ++ ++ *size = op.u.read_memtype.nr_mfns; ++ *base = op.u.read_memtype.mfn; ++ *type = op.u.read_memtype.type; ++} ++ ++struct mtrr_ops generic_mtrr_ops = { ++ .use_intel_if = 1, ++ .get = generic_get_mtrr, ++}; ++ ++struct mtrr_ops *mtrr_if = &generic_mtrr_ops; ++unsigned int num_var_ranges; ++unsigned int *usage_table; ++ ++static void __init set_num_var_ranges(void) ++{ ++ struct xen_platform_op op; ++ ++ for (num_var_ranges = 0; ; num_var_ranges++) { ++ op.cmd = XENPF_read_memtype; ++ op.u.read_memtype.reg = num_var_ranges; ++ if (HYPERVISOR_platform_op(&op) != 0) ++ break; ++ } ++} ++ ++static void __init init_table(void) ++{ ++ int i, max; ++ ++ max = num_var_ranges; ++ if ((usage_table = kmalloc(max * sizeof *usage_table, GFP_KERNEL)) ++ == NULL) { ++ printk(KERN_ERR "mtrr: could not allocate\n"); ++ return; ++ } ++ for (i = 0; i < max; i++) ++ usage_table[i] = 0; ++} ++ ++int mtrr_add_page(unsigned long base, unsigned long size, ++ unsigned int type, char increment) ++{ ++ int error; ++ struct xen_platform_op op; ++ ++ mutex_lock(&mtrr_mutex); ++ ++ op.cmd = XENPF_add_memtype; ++ op.u.add_memtype.mfn = base; ++ op.u.add_memtype.nr_mfns = size; ++ op.u.add_memtype.type = type; ++ error = HYPERVISOR_platform_op(&op); ++ if (error) { ++ mutex_unlock(&mtrr_mutex); ++ BUG_ON(error > 0); ++ return error; ++ } ++ ++ if (increment) ++ ++usage_table[op.u.add_memtype.reg]; ++ ++ mutex_unlock(&mtrr_mutex); ++ ++ return op.u.add_memtype.reg; ++} ++ ++static int mtrr_check(unsigned long base, unsigned long size) ++{ ++ if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { ++ printk(KERN_WARNING ++ "mtrr: size and base must be multiples of 4 kiB\n"); ++ printk(KERN_DEBUG ++ "mtrr: size: 0x%lx base: 0x%lx\n", size, base); ++ dump_stack(); ++ return -1; ++ } ++ return 0; ++} ++ ++int ++mtrr_add(unsigned long base, unsigned long size, unsigned int type, ++ char increment) ++{ ++ if (mtrr_check(base, size)) ++ return -EINVAL; ++ return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, ++ increment); ++} ++ ++int mtrr_del_page(int reg, unsigned long base, unsigned long size) ++{ ++ unsigned i; ++ mtrr_type ltype; ++ unsigned long lbase; ++ unsigned int lsize; ++ int error = -EINVAL; ++ struct xen_platform_op op; ++ ++ mutex_lock(&mtrr_mutex); ++ ++ if (reg < 0) { ++ /* Search for existing MTRR */ ++ for (i = 0; i < num_var_ranges; ++i) { ++ mtrr_if->get(i, &lbase, &lsize, <ype); ++ if (lbase == base && lsize == size) { ++ reg = i; ++ break; ++ } ++ } ++ if (reg < 0) { ++ printk(KERN_DEBUG "mtrr: no MTRR for %lx000,%lx000 found\n", base, ++ size); ++ goto out; ++ } ++ } ++ if (usage_table[reg] < 1) { ++ printk(KERN_WARNING "mtrr: reg: %d has count=0\n", reg); ++ goto out; ++ } ++ if (--usage_table[reg] < 1) { ++ op.cmd = XENPF_del_memtype; ++ op.u.del_memtype.handle = 0; ++ op.u.del_memtype.reg = reg; ++ error = HYPERVISOR_platform_op(&op); ++ if (error) { ++ BUG_ON(error > 0); ++ goto out; ++ } ++ } ++ error = reg; ++ out: ++ mutex_unlock(&mtrr_mutex); ++ return error; ++} ++ ++int ++mtrr_del(int reg, unsigned long base, unsigned long size) ++{ ++ if (mtrr_check(base, size)) ++ return -EINVAL; ++ return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT); ++} ++ ++EXPORT_SYMBOL(mtrr_add); ++EXPORT_SYMBOL(mtrr_del); ++ ++void __init mtrr_bp_init(void) ++{ ++} ++ ++void mtrr_ap_init(void) ++{ ++} ++ ++static int __init mtrr_init(void) ++{ ++ struct cpuinfo_x86 *c = &boot_cpu_data; ++ ++ if (!is_initial_xendomain()) ++ return -ENODEV; ++ ++ if ((!cpu_has(c, X86_FEATURE_MTRR)) && ++ (!cpu_has(c, X86_FEATURE_K6_MTRR)) && ++ (!cpu_has(c, X86_FEATURE_CYRIX_ARR)) && ++ (!cpu_has(c, X86_FEATURE_CENTAUR_MCR))) ++ return -ENODEV; ++ ++ set_num_var_ranges(); ++ init_table(); ++ ++ return 0; ++} ++ ++subsys_initcall(mtrr_init); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/entry_32-xen.S 2009-05-19 09:16:41.000000000 +0200 +@@ -0,0 +1,1242 @@ ++/* ++ * linux/arch/i386/entry.S ++ * ++ * Copyright (C) 1991, 1992 Linus Torvalds ++ */ ++ ++/* ++ * entry.S contains the system-call and fault low-level handling routines. ++ * This also contains the timer-interrupt handler, as well as all interrupts ++ * and faults that can result in a task-switch. ++ * ++ * NOTE: This code handles signal-recognition, which happens every time ++ * after a timer-interrupt and after each system call. ++ * ++ * I changed all the .align's to 4 (16 byte alignment), as that's faster ++ * on a 486. ++ * ++ * Stack layout in 'ret_from_system_call': ++ * ptrace needs to have all regs on the stack. ++ * if the order here is changed, it needs to be ++ * updated in fork.c:copy_process, signal.c:do_signal, ++ * ptrace.c and ptrace.h ++ * ++ * 0(%esp) - %ebx ++ * 4(%esp) - %ecx ++ * 8(%esp) - %edx ++ * C(%esp) - %esi ++ * 10(%esp) - %edi ++ * 14(%esp) - %ebp ++ * 18(%esp) - %eax ++ * 1C(%esp) - %ds ++ * 20(%esp) - %es ++ * 24(%esp) - orig_eax ++ * 28(%esp) - %eip ++ * 2C(%esp) - %cs ++ * 30(%esp) - %eflags ++ * 34(%esp) - %oldesp ++ * 38(%esp) - %oldss ++ * ++ * "current" is in register %ebx during any slow entries. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "irq_vectors.h" ++#include ++ ++#define nr_syscalls ((syscall_table_size)/4) ++ ++EBX = 0x00 ++ECX = 0x04 ++EDX = 0x08 ++ESI = 0x0C ++EDI = 0x10 ++EBP = 0x14 ++EAX = 0x18 ++DS = 0x1C ++ES = 0x20 ++ORIG_EAX = 0x24 ++EIP = 0x28 ++CS = 0x2C ++EFLAGS = 0x30 ++OLDESP = 0x34 ++OLDSS = 0x38 ++ ++CF_MASK = 0x00000001 ++TF_MASK = 0x00000100 ++IF_MASK = 0x00000200 ++DF_MASK = 0x00000400 ++NT_MASK = 0x00004000 ++VM_MASK = 0x00020000 ++/* Pseudo-eflags. */ ++NMI_MASK = 0x80000000 ++ ++#ifndef CONFIG_XEN ++#define DISABLE_INTERRUPTS cli ++#define ENABLE_INTERRUPTS sti ++#else ++/* Offsets into shared_info_t. */ ++#define evtchn_upcall_pending /* 0 */ ++#define evtchn_upcall_mask 1 ++ ++#define sizeof_vcpu_shift 6 ++ ++#ifdef CONFIG_SMP ++#define GET_VCPU_INFO movl TI_cpu(%ebp),%esi ; \ ++ shl $sizeof_vcpu_shift,%esi ; \ ++ addl HYPERVISOR_shared_info,%esi ++#else ++#define GET_VCPU_INFO movl HYPERVISOR_shared_info,%esi ++#endif ++ ++#define __DISABLE_INTERRUPTS movb $1,evtchn_upcall_mask(%esi) ++#define __ENABLE_INTERRUPTS movb $0,evtchn_upcall_mask(%esi) ++#define DISABLE_INTERRUPTS GET_VCPU_INFO ; \ ++ __DISABLE_INTERRUPTS ++#define ENABLE_INTERRUPTS GET_VCPU_INFO ; \ ++ __ENABLE_INTERRUPTS ++#define __TEST_PENDING testb $0xFF,evtchn_upcall_pending(%esi) ++#endif ++ ++#ifdef CONFIG_PREEMPT ++#define preempt_stop cli; TRACE_IRQS_OFF ++#else ++#define preempt_stop ++#define resume_kernel restore_nocheck ++#endif ++ ++.macro TRACE_IRQS_IRET ++#ifdef CONFIG_TRACE_IRQFLAGS ++ testl $IF_MASK,EFLAGS(%esp) # interrupts off? ++ jz 1f ++ TRACE_IRQS_ON ++1: ++#endif ++.endm ++ ++#ifdef CONFIG_VM86 ++#define resume_userspace_sig check_userspace ++#else ++#define resume_userspace_sig resume_userspace ++#endif ++ ++#define SAVE_ALL \ ++ cld; \ ++ pushl %es; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ /*CFI_REL_OFFSET es, 0;*/\ ++ pushl %ds; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ /*CFI_REL_OFFSET ds, 0;*/\ ++ pushl %eax; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ CFI_REL_OFFSET eax, 0;\ ++ pushl %ebp; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ CFI_REL_OFFSET ebp, 0;\ ++ pushl %edi; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ CFI_REL_OFFSET edi, 0;\ ++ pushl %esi; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ CFI_REL_OFFSET esi, 0;\ ++ pushl %edx; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ CFI_REL_OFFSET edx, 0;\ ++ pushl %ecx; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ CFI_REL_OFFSET ecx, 0;\ ++ pushl %ebx; \ ++ CFI_ADJUST_CFA_OFFSET 4;\ ++ CFI_REL_OFFSET ebx, 0;\ ++ movl $(__USER_DS), %edx; \ ++ movl %edx, %ds; \ ++ movl %edx, %es; ++ ++#define RESTORE_INT_REGS \ ++ popl %ebx; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ CFI_RESTORE ebx;\ ++ popl %ecx; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ CFI_RESTORE ecx;\ ++ popl %edx; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ CFI_RESTORE edx;\ ++ popl %esi; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ CFI_RESTORE esi;\ ++ popl %edi; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ CFI_RESTORE edi;\ ++ popl %ebp; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ CFI_RESTORE ebp;\ ++ popl %eax; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ CFI_RESTORE eax ++ ++#define RESTORE_REGS \ ++ RESTORE_INT_REGS; \ ++1: popl %ds; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ /*CFI_RESTORE ds;*/\ ++2: popl %es; \ ++ CFI_ADJUST_CFA_OFFSET -4;\ ++ /*CFI_RESTORE es;*/\ ++.section .fixup,"ax"; \ ++3: movl $0,(%esp); \ ++ jmp 1b; \ ++4: movl $0,(%esp); \ ++ jmp 2b; \ ++.previous; \ ++.section __ex_table,"a";\ ++ .align 4; \ ++ .long 1b,3b; \ ++ .long 2b,4b; \ ++.previous ++ ++#define RING0_INT_FRAME \ ++ CFI_STARTPROC simple;\ ++ CFI_DEF_CFA esp, 3*4;\ ++ /*CFI_OFFSET cs, -2*4;*/\ ++ CFI_OFFSET eip, -3*4 ++ ++#define RING0_EC_FRAME \ ++ CFI_STARTPROC simple;\ ++ CFI_DEF_CFA esp, 4*4;\ ++ /*CFI_OFFSET cs, -2*4;*/\ ++ CFI_OFFSET eip, -3*4 ++ ++#define RING0_PTREGS_FRAME \ ++ CFI_STARTPROC simple;\ ++ CFI_DEF_CFA esp, OLDESP-EBX;\ ++ /*CFI_OFFSET cs, CS-OLDESP;*/\ ++ CFI_OFFSET eip, EIP-OLDESP;\ ++ /*CFI_OFFSET es, ES-OLDESP;*/\ ++ /*CFI_OFFSET ds, DS-OLDESP;*/\ ++ CFI_OFFSET eax, EAX-OLDESP;\ ++ CFI_OFFSET ebp, EBP-OLDESP;\ ++ CFI_OFFSET edi, EDI-OLDESP;\ ++ CFI_OFFSET esi, ESI-OLDESP;\ ++ CFI_OFFSET edx, EDX-OLDESP;\ ++ CFI_OFFSET ecx, ECX-OLDESP;\ ++ CFI_OFFSET ebx, EBX-OLDESP ++ ++ENTRY(ret_from_fork) ++ CFI_STARTPROC ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ call schedule_tail ++ GET_THREAD_INFO(%ebp) ++ popl %eax ++ CFI_ADJUST_CFA_OFFSET -4 ++ pushl $0x0202 # Reset kernel eflags ++ CFI_ADJUST_CFA_OFFSET 4 ++ popfl ++ CFI_ADJUST_CFA_OFFSET -4 ++ jmp syscall_exit ++ CFI_ENDPROC ++ ++/* ++ * Return to user mode is not as complex as all this looks, ++ * but we want the default path for a system call return to ++ * go as quickly as possible which is why some of this is ++ * less clear than it otherwise should be. ++ */ ++ ++ # userspace resumption stub bypassing syscall exit tracing ++ ALIGN ++ RING0_PTREGS_FRAME ++ret_from_exception: ++ preempt_stop ++ret_from_intr: ++ GET_THREAD_INFO(%ebp) ++check_userspace: ++ movl EFLAGS(%esp), %eax # mix EFLAGS and CS ++ movb CS(%esp), %al ++ testl $(VM_MASK | 2), %eax ++ jz resume_kernel ++ENTRY(resume_userspace) ++ DISABLE_INTERRUPTS # make sure we don't miss an interrupt ++ # setting need_resched or sigpending ++ # between sampling and the iret ++ movl TI_flags(%ebp), %ecx ++ andl $_TIF_WORK_MASK, %ecx # is there any work to be done on ++ # int/exception return? ++ jne work_pending ++ jmp restore_all ++ ++#ifdef CONFIG_PREEMPT ++ENTRY(resume_kernel) ++ cli ++ cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? ++ jnz restore_nocheck ++need_resched: ++ movl TI_flags(%ebp), %ecx # need_resched set ? ++ testb $_TIF_NEED_RESCHED, %cl ++ jz restore_all ++ testl $IF_MASK,EFLAGS(%esp) # interrupts off (exception path) ? ++ jz restore_all ++ call preempt_schedule_irq ++ jmp need_resched ++#endif ++ CFI_ENDPROC ++ ++/* SYSENTER_RETURN points to after the "sysenter" instruction in ++ the vsyscall page. See vsyscall-sysentry.S, which defines the symbol. */ ++ ++ # sysenter call handler stub ++ENTRY(sysenter_entry) ++ CFI_STARTPROC simple ++ CFI_DEF_CFA esp, 0 ++ CFI_REGISTER esp, ebp ++ movl SYSENTER_stack_esp0(%esp),%esp ++sysenter_past_esp: ++ /* ++ * No need to follow this irqs on/off section: the syscall ++ * disabled irqs and here we enable it straight after entry: ++ */ ++ sti ++ pushl $(__USER_DS) ++ CFI_ADJUST_CFA_OFFSET 4 ++ /*CFI_REL_OFFSET ss, 0*/ ++ pushl %ebp ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET esp, 0 ++ pushfl ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $(__USER_CS) ++ CFI_ADJUST_CFA_OFFSET 4 ++ /*CFI_REL_OFFSET cs, 0*/ ++ /* ++ * Push current_thread_info()->sysenter_return to the stack. ++ * A tiny bit of offset fixup is necessary - 4*4 means the 4 words ++ * pushed above; +8 corresponds to copy_thread's esp0 setting. ++ */ ++ pushl (TI_sysenter_return-THREAD_SIZE+8+4*4)(%esp) ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET eip, 0 ++ ++/* ++ * Load the potential sixth argument from user stack. ++ * Careful about security. ++ */ ++ cmpl $__PAGE_OFFSET-3,%ebp ++ jae syscall_fault ++1: movl (%ebp),%ebp ++.section __ex_table,"a" ++ .align 4 ++ .long 1b,syscall_fault ++.previous ++ ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ GET_THREAD_INFO(%ebp) ++ ++ /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */ ++ testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) ++ jnz syscall_trace_entry ++ cmpl $(nr_syscalls), %eax ++ jae syscall_badsys ++ call *sys_call_table(,%eax,4) ++ movl %eax,EAX(%esp) ++ DISABLE_INTERRUPTS ++ TRACE_IRQS_OFF ++ movl TI_flags(%ebp), %ecx ++ testw $_TIF_ALLWORK_MASK, %cx ++ jne syscall_exit_work ++/* if something modifies registers it must also disable sysexit */ ++ movl EIP(%esp), %edx ++ movl OLDESP(%esp), %ecx ++ xorl %ebp,%ebp ++#ifdef CONFIG_XEN ++ TRACE_IRQS_ON ++ __ENABLE_INTERRUPTS ++sysexit_scrit: /**** START OF SYSEXIT CRITICAL REGION ****/ ++ __TEST_PENDING ++ jnz 14f # process more events if necessary... ++ movl ESI(%esp), %esi ++ sysexit ++14: __DISABLE_INTERRUPTS ++ TRACE_IRQS_OFF ++sysexit_ecrit: /**** END OF SYSEXIT CRITICAL REGION ****/ ++ push %esp ++ call evtchn_do_upcall ++ add $4,%esp ++ jmp ret_from_intr ++#else ++ TRACE_IRQS_ON ++ sti ++ sysexit ++#endif /* !CONFIG_XEN */ ++ CFI_ENDPROC ++ ++ # pv sysenter call handler stub ++ENTRY(sysenter_entry_pv) ++ RING0_INT_FRAME ++ movl $__USER_DS,16(%esp) ++ movl %ebp,12(%esp) ++ movl $__USER_CS,4(%esp) ++ addl $4,%esp ++ CFI_ADJUST_CFA_OFFSET -4 ++ /* +5*4 is SS:ESP,EFLAGS,CS:EIP. +8 is esp0 setting. */ ++ pushl (TI_sysenter_return-THREAD_SIZE+8+4*4)(%esp) ++ CFI_ADJUST_CFA_OFFSET 4 ++/* ++ * Load the potential sixth argument from user stack. ++ * Careful about security. ++ */ ++ cmpl $__PAGE_OFFSET-3,%ebp ++ jae syscall_fault ++1: movl (%ebp),%ebp ++.section __ex_table,"a" ++ .align 4 ++ .long 1b,syscall_fault ++.previous ++ /* fall through */ ++ CFI_ENDPROC ++ENDPROC(sysenter_entry_pv) ++ ++ # system call handler stub ++ENTRY(system_call) ++ RING0_INT_FRAME # can't unwind into user space anyway ++ pushl %eax # save orig_eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ GET_THREAD_INFO(%ebp) ++ testl $TF_MASK,EFLAGS(%esp) ++ jz no_singlestep ++ orl $_TIF_SINGLESTEP,TI_flags(%ebp) ++no_singlestep: ++ # system call tracing in operation / emulation ++ /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */ ++ testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) ++ jnz syscall_trace_entry ++ cmpl $(nr_syscalls), %eax ++ jae syscall_badsys ++syscall_call: ++ call *sys_call_table(,%eax,4) ++ movl %eax,EAX(%esp) # store the return value ++syscall_exit: ++ DISABLE_INTERRUPTS # make sure we don't miss an interrupt ++ # setting need_resched or sigpending ++ # between sampling and the iret ++ TRACE_IRQS_OFF ++ movl TI_flags(%ebp), %ecx ++ testw $_TIF_ALLWORK_MASK, %cx # current->work ++ jne syscall_exit_work ++ ++restore_all: ++#ifndef CONFIG_XEN ++ movl EFLAGS(%esp), %eax # mix EFLAGS, SS and CS ++ # Warning: OLDSS(%esp) contains the wrong/random values if we ++ # are returning to the kernel. ++ # See comments in process.c:copy_thread() for details. ++ movb OLDSS(%esp), %ah ++ movb CS(%esp), %al ++ andl $(VM_MASK | (4 << 8) | 3), %eax ++ cmpl $((4 << 8) | 3), %eax ++ CFI_REMEMBER_STATE ++ je ldt_ss # returning to user-space with LDT SS ++restore_nocheck: ++#else ++restore_nocheck: ++ movl EFLAGS(%esp), %eax ++ testl $(VM_MASK|NMI_MASK), %eax ++ CFI_REMEMBER_STATE ++ jnz hypervisor_iret ++ shr $9, %eax # EAX[0] == IRET_EFLAGS.IF ++ GET_VCPU_INFO ++ andb evtchn_upcall_mask(%esi),%al ++ andb $1,%al # EAX[0] == IRET_EFLAGS.IF & event_mask ++ CFI_REMEMBER_STATE ++ jnz restore_all_enable_events # != 0 => enable event delivery ++#endif ++ TRACE_IRQS_IRET ++restore_nocheck_notrace: ++ RESTORE_REGS ++ addl $4, %esp ++ CFI_ADJUST_CFA_OFFSET -4 ++1: iret ++.section .fixup,"ax" ++iret_exc: ++#ifndef CONFIG_XEN ++ TRACE_IRQS_ON ++ sti ++#endif ++ pushl $0 # no error code ++ pushl $do_iret_error ++ jmp error_code ++.previous ++.section __ex_table,"a" ++ .align 4 ++ .long 1b,iret_exc ++.previous ++ ++ CFI_RESTORE_STATE ++#ifndef CONFIG_XEN ++ldt_ss: ++ larl OLDSS(%esp), %eax ++ jnz restore_nocheck ++ testl $0x00400000, %eax # returning to 32bit stack? ++ jnz restore_nocheck # allright, normal return ++ /* If returning to userspace with 16bit stack, ++ * try to fix the higher word of ESP, as the CPU ++ * won't restore it. ++ * This is an "official" bug of all the x86-compatible ++ * CPUs, which we can try to work around to make ++ * dosemu and wine happy. */ ++ subl $8, %esp # reserve space for switch16 pointer ++ CFI_ADJUST_CFA_OFFSET 8 ++ cli ++ TRACE_IRQS_OFF ++ movl %esp, %eax ++ /* Set up the 16bit stack frame with switch32 pointer on top, ++ * and a switch16 pointer on top of the current frame. */ ++ call setup_x86_bogus_stack ++ CFI_ADJUST_CFA_OFFSET -8 # frame has moved ++ TRACE_IRQS_IRET ++ RESTORE_REGS ++ lss 20+4(%esp), %esp # switch to 16bit stack ++1: iret ++.section __ex_table,"a" ++ .align 4 ++ .long 1b,iret_exc ++.previous ++#else ++ ALIGN ++restore_all_enable_events: ++ TRACE_IRQS_ON ++ __ENABLE_INTERRUPTS ++scrit: /**** START OF CRITICAL REGION ****/ ++ __TEST_PENDING ++ jnz 14f # process more events if necessary... ++ RESTORE_REGS ++ addl $4, %esp ++ CFI_ADJUST_CFA_OFFSET -4 ++1: iret ++.section __ex_table,"a" ++ .align 4 ++ .long 1b,iret_exc ++.previous ++14: __DISABLE_INTERRUPTS ++ TRACE_IRQS_OFF ++ecrit: /**** END OF CRITICAL REGION ****/ ++ jmp .Ldo_upcall ++ ++ CFI_RESTORE_STATE ++hypervisor_iret: ++ andl $~NMI_MASK, EFLAGS(%esp) ++ RESTORE_REGS ++ addl $4, %esp ++ CFI_ADJUST_CFA_OFFSET -4 ++ jmp hypercall_page + (__HYPERVISOR_iret * 32) ++#endif ++ CFI_ENDPROC ++ ++ # perform work that needs to be done immediately before resumption ++ ALIGN ++ RING0_PTREGS_FRAME # can't unwind into user space anyway ++work_pending: ++ testb $_TIF_NEED_RESCHED, %cl ++ jz work_notifysig ++work_resched: ++ call schedule ++ DISABLE_INTERRUPTS # make sure we don't miss an interrupt ++ # setting need_resched or sigpending ++ # between sampling and the iret ++ TRACE_IRQS_OFF ++ movl TI_flags(%ebp), %ecx ++ andl $_TIF_WORK_MASK, %ecx # is there any work to be done other ++ # than syscall tracing? ++ jz restore_all ++ testb $_TIF_NEED_RESCHED, %cl ++ jnz work_resched ++ ++work_notifysig: # deal with pending signals and ++ # notify-resume requests ++ testl $VM_MASK, EFLAGS(%esp) ++ movl %esp, %eax ++ jne work_notifysig_v86 # returning to kernel-space or ++ # vm86-space ++ xorl %edx, %edx ++ call do_notify_resume ++ jmp resume_userspace_sig ++ ++ ALIGN ++work_notifysig_v86: ++#ifdef CONFIG_VM86 ++ pushl %ecx # save ti_flags for do_notify_resume ++ CFI_ADJUST_CFA_OFFSET 4 ++ call save_v86_state # %eax contains pt_regs pointer ++ popl %ecx ++ CFI_ADJUST_CFA_OFFSET -4 ++ movl %eax, %esp ++ xorl %edx, %edx ++ call do_notify_resume ++ jmp resume_userspace_sig ++#endif ++ ++ # perform syscall exit tracing ++ ALIGN ++syscall_trace_entry: ++ movl $-ENOSYS,EAX(%esp) ++ movl %esp, %eax ++ xorl %edx,%edx ++ call do_syscall_trace ++ cmpl $0, %eax ++ jne resume_userspace # ret != 0 -> running under PTRACE_SYSEMU, ++ # so must skip actual syscall ++ movl ORIG_EAX(%esp), %eax ++ cmpl $(nr_syscalls), %eax ++ jnae syscall_call ++ jmp syscall_exit ++ ++ # perform syscall exit tracing ++ ALIGN ++syscall_exit_work: ++ testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP), %cl ++ jz work_pending ++ TRACE_IRQS_ON ++ ENABLE_INTERRUPTS # could let do_syscall_trace() call ++ # schedule() instead ++ movl %esp, %eax ++ movl $1, %edx ++ call do_syscall_trace ++ jmp resume_userspace ++ CFI_ENDPROC ++ ++ RING0_INT_FRAME # can't unwind into user space anyway ++syscall_fault: ++ pushl %eax # save orig_eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ GET_THREAD_INFO(%ebp) ++ movl $-EFAULT,EAX(%esp) ++ jmp resume_userspace ++ ++syscall_badsys: ++ movl $-ENOSYS,EAX(%esp) ++ jmp resume_userspace ++ CFI_ENDPROC ++ ++#ifndef CONFIG_XEN ++#define FIXUP_ESPFIX_STACK \ ++ movl %esp, %eax; \ ++ /* switch to 32bit stack using the pointer on top of 16bit stack */ \ ++ lss %ss:CPU_16BIT_STACK_SIZE-8, %esp; \ ++ /* copy data from 16bit stack to 32bit stack */ \ ++ call fixup_x86_bogus_stack; \ ++ /* put ESP to the proper location */ \ ++ movl %eax, %esp; ++#define UNWIND_ESPFIX_STACK \ ++ pushl %eax; \ ++ CFI_ADJUST_CFA_OFFSET 4; \ ++ movl %ss, %eax; \ ++ /* see if on 16bit stack */ \ ++ cmpw $__ESPFIX_SS, %ax; \ ++ je 28f; \ ++27: popl %eax; \ ++ CFI_ADJUST_CFA_OFFSET -4; \ ++.section .fixup,"ax"; \ ++28: movl $__KERNEL_DS, %eax; \ ++ movl %eax, %ds; \ ++ movl %eax, %es; \ ++ /* switch to 32bit stack */ \ ++ FIXUP_ESPFIX_STACK; \ ++ jmp 27b; \ ++.previous ++ ++/* ++ * Build the entry stubs and pointer table with ++ * some assembler magic. ++ */ ++.data ++ENTRY(interrupt) ++.text ++ ++vector=0 ++ENTRY(irq_entries_start) ++ RING0_INT_FRAME ++.rept NR_IRQS ++ ALIGN ++ .if vector ++ CFI_ADJUST_CFA_OFFSET -4 ++ .endif ++1: pushl $~(vector) ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp common_interrupt ++.data ++ .long 1b ++.text ++vector=vector+1 ++.endr ++ ++/* ++ * the CPU automatically disables interrupts when executing an IRQ vector, ++ * so IRQ-flags tracing has to follow that: ++ */ ++ ALIGN ++common_interrupt: ++ SAVE_ALL ++ TRACE_IRQS_OFF ++ movl %esp,%eax ++ call do_IRQ ++ jmp ret_from_intr ++ CFI_ENDPROC ++ ++#define BUILD_INTERRUPT(name, nr) \ ++ENTRY(name) \ ++ RING0_INT_FRAME; \ ++ pushl $~(nr); \ ++ CFI_ADJUST_CFA_OFFSET 4; \ ++ SAVE_ALL; \ ++ TRACE_IRQS_OFF \ ++ movl %esp,%eax; \ ++ call smp_/**/name; \ ++ jmp ret_from_intr; \ ++ CFI_ENDPROC ++ ++/* The include is where all of the SMP etc. interrupts come from */ ++#include "entry_arch.h" ++#else ++#define UNWIND_ESPFIX_STACK ++#endif ++ ++ENTRY(divide_error) ++ RING0_INT_FRAME ++ pushl $0 # no error code ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $do_divide_error ++ CFI_ADJUST_CFA_OFFSET 4 ++ ALIGN ++error_code: ++ pushl %ds ++ CFI_ADJUST_CFA_OFFSET 4 ++ /*CFI_REL_OFFSET ds, 0*/ ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET eax, 0 ++ xorl %eax, %eax ++ pushl %ebp ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET ebp, 0 ++ pushl %edi ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET edi, 0 ++ pushl %esi ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET esi, 0 ++ pushl %edx ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET edx, 0 ++ decl %eax # eax = -1 ++ pushl %ecx ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET ecx, 0 ++ pushl %ebx ++ CFI_ADJUST_CFA_OFFSET 4 ++ CFI_REL_OFFSET ebx, 0 ++ cld ++ pushl %es ++ CFI_ADJUST_CFA_OFFSET 4 ++ /*CFI_REL_OFFSET es, 0*/ ++ UNWIND_ESPFIX_STACK ++ popl %ecx ++ CFI_ADJUST_CFA_OFFSET -4 ++ /*CFI_REGISTER es, ecx*/ ++ movl ES(%esp), %edi # get the function address ++ movl ORIG_EAX(%esp), %edx # get the error code ++ movl %eax, ORIG_EAX(%esp) ++ movl %ecx, ES(%esp) ++ /*CFI_REL_OFFSET es, ES*/ ++ movl $(__USER_DS), %ecx ++ movl %ecx, %ds ++ movl %ecx, %es ++ movl %esp,%eax # pt_regs pointer ++ call *%edi ++ jmp ret_from_exception ++ CFI_ENDPROC ++ ++#ifdef CONFIG_XEN ++# A note on the "critical region" in our callback handler. ++# We want to avoid stacking callback handlers due to events occurring ++# during handling of the last event. To do this, we keep events disabled ++# until we've done all processing. HOWEVER, we must enable events before ++# popping the stack frame (can't be done atomically) and so it would still ++# be possible to get enough handler activations to overflow the stack. ++# Although unlikely, bugs of that kind are hard to track down, so we'd ++# like to avoid the possibility. ++# So, on entry to the handler we detect whether we interrupted an ++# existing activation in its critical region -- if so, we pop the current ++# activation and restart the handler using the previous one. ++# ++# The sysexit critical region is slightly different. sysexit ++# atomically removes the entire stack frame. If we interrupt in the ++# critical region we know that the entire frame is present and correct ++# so we can simply throw away the new one. ++ENTRY(hypervisor_callback) ++ RING0_INT_FRAME ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ testb $2,CS(%esp) ++ movl EIP(%esp),%eax ++ jnz .Ldo_upcall ++ cmpl $scrit,%eax ++ jb 0f ++ cmpl $ecrit,%eax ++ jb critical_region_fixup ++0: ++#ifdef CONFIG_XEN_SUPERVISOR_MODE_KERNEL ++ cmpl $sysexit_scrit,%eax ++ jb .Ldo_upcall ++ cmpl $sysexit_ecrit,%eax ++ ja .Ldo_upcall ++ addl $OLDESP,%esp # Remove eflags...ebx from stack frame. ++#endif ++.Ldo_upcall: ++ push %esp ++ CFI_ADJUST_CFA_OFFSET 4 ++ call evtchn_do_upcall ++ add $4,%esp ++ CFI_ADJUST_CFA_OFFSET -4 ++ jmp ret_from_intr ++ CFI_ENDPROC ++ ++# [How we do the fixup]. We want to merge the current stack frame with the ++# just-interrupted frame. How we do this depends on where in the critical ++# region the interrupted handler was executing, and so how many saved ++# registers are in each frame. We do this quickly using the lookup table ++# 'critical_fixup_table'. For each byte offset in the critical region, it ++# provides the number of bytes which have already been popped from the ++# interrupted stack frame. ++critical_region_fixup: ++ movsbl critical_fixup_table-scrit(%eax),%ecx # %ecx contains num slots popped ++ testl %ecx,%ecx ++ leal (%esp,%ecx,4),%esi # %esi points at end of src region ++ leal OLDESP(%esp),%edi # %edi points at end of dst region ++ jle 17f # skip loop if nothing to copy ++16: subl $4,%esi # pre-decrementing copy loop ++ subl $4,%edi ++ movl (%esi),%eax ++ movl %eax,(%edi) ++ loop 16b ++17: movl %edi,%esp # final %edi is top of merged stack ++ jmp .Ldo_upcall ++ ++.section .rodata,"a" ++critical_fixup_table: ++ .byte -1,-1,-1 # testb $0xff,(%esi) = __TEST_PENDING ++ .byte -1,-1 # jnz 14f ++ .byte 0 # pop %ebx ++ .byte 1 # pop %ecx ++ .byte 2 # pop %edx ++ .byte 3 # pop %esi ++ .byte 4 # pop %edi ++ .byte 5 # pop %ebp ++ .byte 6 # pop %eax ++ .byte 7 # pop %ds ++ .byte 8 # pop %es ++ .byte 9,9,9 # add $4,%esp ++ .byte 10 # iret ++ .byte -1,-1,-1,-1 # movb $1,1(%esi) = __DISABLE_INTERRUPTS ++.previous ++ ++# Hypervisor uses this for application faults while it executes. ++# We get here for two reasons: ++# 1. Fault while reloading DS, ES, FS or GS ++# 2. Fault while executing IRET ++# Category 1 we fix up by reattempting the load, and zeroing the segment ++# register if the load fails. ++# Category 2 we fix up by jumping to do_iret_error. We cannot use the ++# normal Linux return path in this case because if we use the IRET hypercall ++# to pop the stack frame we end up in an infinite loop of failsafe callbacks. ++# We distinguish between categories by maintaining a status value in EAX. ++ENTRY(failsafe_callback) ++ pushl %eax ++ movl $1,%eax ++1: mov 4(%esp),%ds ++2: mov 8(%esp),%es ++3: mov 12(%esp),%fs ++4: mov 16(%esp),%gs ++ testl %eax,%eax ++ popl %eax ++ jz 5f ++ addl $16,%esp # EAX != 0 => Category 2 (Bad IRET) ++ jmp iret_exc ++5: addl $16,%esp # EAX == 0 => Category 1 (Bad segment) ++ RING0_INT_FRAME ++ pushl $0 ++ SAVE_ALL ++ jmp ret_from_exception ++.section .fixup,"ax"; \ ++6: xorl %eax,%eax; \ ++ movl %eax,4(%esp); \ ++ jmp 1b; \ ++7: xorl %eax,%eax; \ ++ movl %eax,8(%esp); \ ++ jmp 2b; \ ++8: xorl %eax,%eax; \ ++ movl %eax,12(%esp); \ ++ jmp 3b; \ ++9: xorl %eax,%eax; \ ++ movl %eax,16(%esp); \ ++ jmp 4b; \ ++.previous; \ ++.section __ex_table,"a"; \ ++ .align 4; \ ++ .long 1b,6b; \ ++ .long 2b,7b; \ ++ .long 3b,8b; \ ++ .long 4b,9b; \ ++.previous ++#endif ++ CFI_ENDPROC ++ ++ENTRY(coprocessor_error) ++ RING0_INT_FRAME ++ pushl $0 ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $do_coprocessor_error ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++ENTRY(simd_coprocessor_error) ++ RING0_INT_FRAME ++ pushl $0 ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $do_simd_coprocessor_error ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++ENTRY(device_not_available) ++ RING0_INT_FRAME ++ pushl $-1 # mark this as an int ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++#ifndef CONFIG_XEN ++ movl %cr0, %eax ++ testl $0x4, %eax # EM (math emulation bit) ++ je device_available_emulate ++ pushl $0 # temporary storage for ORIG_EIP ++ CFI_ADJUST_CFA_OFFSET 4 ++ call math_emulate ++ addl $4, %esp ++ CFI_ADJUST_CFA_OFFSET -4 ++ jmp ret_from_exception ++device_available_emulate: ++#endif ++ preempt_stop ++ call math_state_restore ++ jmp ret_from_exception ++ CFI_ENDPROC ++ ++#ifndef CONFIG_XEN ++/* ++ * Debug traps and NMI can happen at the one SYSENTER instruction ++ * that sets up the real kernel stack. Check here, since we can't ++ * allow the wrong stack to be used. ++ * ++ * "SYSENTER_stack_esp0+12" is because the NMI/debug handler will have ++ * already pushed 3 words if it hits on the sysenter instruction: ++ * eflags, cs and eip. ++ * ++ * We just load the right stack, and push the three (known) values ++ * by hand onto the new stack - while updating the return eip past ++ * the instruction that would have done it for sysenter. ++ */ ++#define FIX_STACK(offset, ok, label) \ ++ cmpw $__KERNEL_CS,4(%esp); \ ++ jne ok; \ ++label: \ ++ movl SYSENTER_stack_esp0+offset(%esp),%esp; \ ++ pushfl; \ ++ pushl $__KERNEL_CS; \ ++ pushl $sysenter_past_esp ++#endif /* CONFIG_XEN */ ++ ++KPROBE_ENTRY(debug) ++ RING0_INT_FRAME ++#ifndef CONFIG_XEN ++ cmpl $sysenter_entry,(%esp) ++ jne debug_stack_correct ++ FIX_STACK(12, debug_stack_correct, debug_esp_fix_insn) ++debug_stack_correct: ++#endif /* !CONFIG_XEN */ ++ pushl $-1 # mark this as an int ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ xorl %edx,%edx # error code 0 ++ movl %esp,%eax # pt_regs pointer ++ call do_debug ++ jmp ret_from_exception ++ CFI_ENDPROC ++ .previous .text ++#ifndef CONFIG_XEN ++/* ++ * NMI is doubly nasty. It can happen _while_ we're handling ++ * a debug fault, and the debug fault hasn't yet been able to ++ * clear up the stack. So we first check whether we got an ++ * NMI on the sysenter entry path, but after that we need to ++ * check whether we got an NMI on the debug path where the debug ++ * fault happened on the sysenter path. ++ */ ++ENTRY(nmi) ++ RING0_INT_FRAME ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ movl %ss, %eax ++ cmpw $__ESPFIX_SS, %ax ++ popl %eax ++ CFI_ADJUST_CFA_OFFSET -4 ++ je nmi_16bit_stack ++ cmpl $sysenter_entry,(%esp) ++ je nmi_stack_fixup ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ movl %esp,%eax ++ /* Do not access memory above the end of our stack page, ++ * it might not exist. ++ */ ++ andl $(THREAD_SIZE-1),%eax ++ cmpl $(THREAD_SIZE-20),%eax ++ popl %eax ++ CFI_ADJUST_CFA_OFFSET -4 ++ jae nmi_stack_correct ++ cmpl $sysenter_entry,12(%esp) ++ je nmi_debug_stack_check ++nmi_stack_correct: ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ xorl %edx,%edx # zero error code ++ movl %esp,%eax # pt_regs pointer ++ call do_nmi ++ jmp restore_nocheck_notrace ++ CFI_ENDPROC ++ ++nmi_stack_fixup: ++ FIX_STACK(12,nmi_stack_correct, 1) ++ jmp nmi_stack_correct ++nmi_debug_stack_check: ++ cmpw $__KERNEL_CS,16(%esp) ++ jne nmi_stack_correct ++ cmpl $debug,(%esp) ++ jb nmi_stack_correct ++ cmpl $debug_esp_fix_insn,(%esp) ++ ja nmi_stack_correct ++ FIX_STACK(24,nmi_stack_correct, 1) ++ jmp nmi_stack_correct ++ ++nmi_16bit_stack: ++ RING0_INT_FRAME ++ /* create the pointer to lss back */ ++ pushl %ss ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl %esp ++ CFI_ADJUST_CFA_OFFSET 4 ++ movzwl %sp, %esp ++ addw $4, (%esp) ++ /* copy the iret frame of 12 bytes */ ++ .rept 3 ++ pushl 16(%esp) ++ CFI_ADJUST_CFA_OFFSET 4 ++ .endr ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ FIXUP_ESPFIX_STACK # %eax == %esp ++ CFI_ADJUST_CFA_OFFSET -20 # the frame has now moved ++ xorl %edx,%edx # zero error code ++ call do_nmi ++ RESTORE_REGS ++ lss 12+4(%esp), %esp # back to 16bit stack ++1: iret ++ CFI_ENDPROC ++.section __ex_table,"a" ++ .align 4 ++ .long 1b,iret_exc ++.previous ++#else ++ENTRY(nmi) ++ RING0_INT_FRAME ++ pushl %eax ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ xorl %edx,%edx # zero error code ++ movl %esp,%eax # pt_regs pointer ++ call do_nmi ++ orl $NMI_MASK, EFLAGS(%esp) ++ jmp restore_all ++ CFI_ENDPROC ++#endif ++ ++KPROBE_ENTRY(int3) ++ RING0_INT_FRAME ++ pushl $-1 # mark this as an int ++ CFI_ADJUST_CFA_OFFSET 4 ++ SAVE_ALL ++ xorl %edx,%edx # zero error code ++ movl %esp,%eax # pt_regs pointer ++ call do_int3 ++ jmp ret_from_exception ++ CFI_ENDPROC ++ .previous .text ++ ++ENTRY(overflow) ++ RING0_INT_FRAME ++ pushl $0 ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $do_overflow ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++ENTRY(bounds) ++ RING0_INT_FRAME ++ pushl $0 ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $do_bounds ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++ENTRY(invalid_op) ++ RING0_INT_FRAME ++ pushl $0 ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $do_invalid_op ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++ENTRY(coprocessor_segment_overrun) ++ RING0_INT_FRAME ++ pushl $0 ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $do_coprocessor_segment_overrun ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++ENTRY(invalid_TSS) ++ RING0_EC_FRAME ++ pushl $do_invalid_TSS ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++ENTRY(segment_not_present) ++ RING0_EC_FRAME ++ pushl $do_segment_not_present ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++ENTRY(stack_segment) ++ RING0_EC_FRAME ++ pushl $do_stack_segment ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++KPROBE_ENTRY(general_protection) ++ RING0_EC_FRAME ++ pushl $do_general_protection ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ .previous .text ++ ++ENTRY(alignment_check) ++ RING0_EC_FRAME ++ pushl $do_alignment_check ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++KPROBE_ENTRY(page_fault) ++ RING0_EC_FRAME ++ pushl $do_page_fault ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ .previous .text ++ ++#ifdef CONFIG_X86_MCE ++ENTRY(machine_check) ++ RING0_INT_FRAME ++ pushl $0 ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl machine_check_vector ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++#endif ++ ++#ifndef CONFIG_XEN ++ENTRY(spurious_interrupt_bug) ++ RING0_INT_FRAME ++ pushl $0 ++ CFI_ADJUST_CFA_OFFSET 4 ++ pushl $do_spurious_interrupt_bug ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++#endif /* !CONFIG_XEN */ ++ ++#ifdef CONFIG_STACK_UNWIND ++ENTRY(arch_unwind_init_running) ++ CFI_STARTPROC ++ movl 4(%esp), %edx ++ movl (%esp), %ecx ++ leal 4(%esp), %eax ++ movl %ebx, EBX(%edx) ++ xorl %ebx, %ebx ++ movl %ebx, ECX(%edx) ++ movl %ebx, EDX(%edx) ++ movl %esi, ESI(%edx) ++ movl %edi, EDI(%edx) ++ movl %ebp, EBP(%edx) ++ movl %ebx, EAX(%edx) ++ movl $__USER_DS, DS(%edx) ++ movl $__USER_DS, ES(%edx) ++ movl %ebx, ORIG_EAX(%edx) ++ movl %ecx, EIP(%edx) ++ movl 12(%esp), %ecx ++ movl $__KERNEL_CS, CS(%edx) ++ movl %ebx, EFLAGS(%edx) ++ movl %eax, OLDESP(%edx) ++ movl 8(%esp), %eax ++ movl %ecx, 8(%esp) ++ movl EBX(%edx), %ebx ++ movl $__KERNEL_DS, OLDSS(%edx) ++ jmpl *%eax ++ CFI_ENDPROC ++ENDPROC(arch_unwind_init_running) ++#endif ++ ++ENTRY(fixup_4gb_segment) ++ RING0_EC_FRAME ++ pushl $do_fixup_4gb_segment ++ CFI_ADJUST_CFA_OFFSET 4 ++ jmp error_code ++ CFI_ENDPROC ++ ++.section .rodata,"a" ++#include "syscall_table.S" ++ ++syscall_table_size=(.-sys_call_table) +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/fixup.c 2008-01-28 12:24:18.000000000 +0100 +@@ -0,0 +1,88 @@ ++/****************************************************************************** ++ * fixup.c ++ * ++ * Binary-rewriting of certain IA32 instructions, on notification by Xen. ++ * Used to avoid repeated slow emulation of common instructions used by the ++ * user-space TLS (Thread-Local Storage) libraries. ++ * ++ * **** NOTE **** ++ * Issues with the binary rewriting have caused it to be removed. Instead ++ * we rely on Xen's emulator to boot the kernel, and then print a banner ++ * message recommending that the user disables /lib/tls. ++ * ++ * Copyright (c) 2004, K A Fraser ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DP(_f, _args...) printk(KERN_ALERT " " _f "\n" , ## _args ) ++ ++fastcall void do_fixup_4gb_segment(struct pt_regs *regs, long error_code) ++{ ++ static unsigned long printed = 0; ++ char info[100]; ++ int i; ++ ++ /* Ignore statically-linked init. */ ++ if (current->tgid == 1) ++ return; ++ ++ VOID(HYPERVISOR_vm_assist(VMASST_CMD_disable, ++ VMASST_TYPE_4gb_segments_notify)); ++ ++ if (test_and_set_bit(0, &printed)) ++ return; ++ ++ sprintf(info, "%s (pid=%d)", current->comm, current->tgid); ++ ++ DP(""); ++ DP("***************************************************************"); ++ DP("***************************************************************"); ++ DP("** WARNING: Currently emulating unsupported memory accesses **"); ++ DP("** in /lib/tls glibc libraries. The emulation is **"); ++ DP("** slow. To ensure full performance you should **"); ++ DP("** install a 'xen-friendly' (nosegneg) version of **"); ++ DP("** the library, or disable tls support by executing **"); ++ DP("** the following as root: **"); ++ DP("** mv /lib/tls /lib/tls.disabled **"); ++ DP("** Offending process: %-38.38s **", info); ++ DP("***************************************************************"); ++ DP("***************************************************************"); ++ DP(""); ++ ++ for (i = 5; i > 0; i--) { ++ touch_softlockup_watchdog(); ++ printk("Pausing... %d", i); ++ mdelay(1000); ++ printk("\b\b\b\b\b\b\b\b\b\b\b\b"); ++ } ++ ++ printk("Continuing...\n\n"); ++} ++ ++static int __init fixup_init(void) ++{ ++ WARN_ON(HYPERVISOR_vm_assist(VMASST_CMD_enable, ++ VMASST_TYPE_4gb_segments_notify)); ++ return 0; ++} ++__initcall(fixup_init); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/head_32-xen.S 2007-06-12 13:12:48.000000000 +0200 +@@ -0,0 +1,207 @@ ++ ++ ++.text ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * References to members of the new_cpu_data structure. ++ */ ++ ++#define X86 new_cpu_data+CPUINFO_x86 ++#define X86_VENDOR new_cpu_data+CPUINFO_x86_vendor ++#define X86_MODEL new_cpu_data+CPUINFO_x86_model ++#define X86_MASK new_cpu_data+CPUINFO_x86_mask ++#define X86_HARD_MATH new_cpu_data+CPUINFO_hard_math ++#define X86_CPUID new_cpu_data+CPUINFO_cpuid_level ++#define X86_CAPABILITY new_cpu_data+CPUINFO_x86_capability ++#define X86_VENDOR_ID new_cpu_data+CPUINFO_x86_vendor_id ++ ++#define VIRT_ENTRY_OFFSET 0x0 ++.org VIRT_ENTRY_OFFSET ++ENTRY(startup_32) ++ movl %esi,xen_start_info ++ cld ++ ++ /* Set up the stack pointer */ ++ movl $(init_thread_union+THREAD_SIZE),%esp ++ ++ /* get vendor info */ ++ xorl %eax,%eax # call CPUID with 0 -> return vendor ID ++ XEN_CPUID ++ movl %eax,X86_CPUID # save CPUID level ++ movl %ebx,X86_VENDOR_ID # lo 4 chars ++ movl %edx,X86_VENDOR_ID+4 # next 4 chars ++ movl %ecx,X86_VENDOR_ID+8 # last 4 chars ++ ++ movl $1,%eax # Use the CPUID instruction to get CPU type ++ XEN_CPUID ++ movb %al,%cl # save reg for future use ++ andb $0x0f,%ah # mask processor family ++ movb %ah,X86 ++ andb $0xf0,%al # mask model ++ shrb $4,%al ++ movb %al,X86_MODEL ++ andb $0x0f,%cl # mask mask revision ++ movb %cl,X86_MASK ++ movl %edx,X86_CAPABILITY ++ ++ movb $1,X86_HARD_MATH ++ ++ xorl %eax,%eax # Clear FS/GS and LDT ++ movl %eax,%fs ++ movl %eax,%gs ++ cld # gcc2 wants the direction flag cleared at all times ++ ++ pushl %eax # fake return address ++ jmp start_kernel ++ ++#define HYPERCALL_PAGE_OFFSET 0x1000 ++.org HYPERCALL_PAGE_OFFSET ++ENTRY(hypercall_page) ++ CFI_STARTPROC ++.skip 0x1000 ++ CFI_ENDPROC ++ ++/* ++ * Real beginning of normal "text" segment ++ */ ++ENTRY(stext) ++ENTRY(_stext) ++ ++/* ++ * BSS section ++ */ ++.section ".bss.page_aligned","w" ++ENTRY(empty_zero_page) ++ .fill 4096,1,0 ++ ++/* ++ * This starts the data section. ++ */ ++.data ++ ++/* ++ * The Global Descriptor Table contains 28 quadwords, per-CPU. ++ */ ++ .align L1_CACHE_BYTES ++ENTRY(cpu_gdt_table) ++ .quad 0x0000000000000000 /* NULL descriptor */ ++ .quad 0x0000000000000000 /* 0x0b reserved */ ++ .quad 0x0000000000000000 /* 0x13 reserved */ ++ .quad 0x0000000000000000 /* 0x1b reserved */ ++ .quad 0x0000000000000000 /* 0x20 unused */ ++ .quad 0x0000000000000000 /* 0x28 unused */ ++ .quad 0x0000000000000000 /* 0x33 TLS entry 1 */ ++ .quad 0x0000000000000000 /* 0x3b TLS entry 2 */ ++ .quad 0x0000000000000000 /* 0x43 TLS entry 3 */ ++ .quad 0x0000000000000000 /* 0x4b reserved */ ++ .quad 0x0000000000000000 /* 0x53 reserved */ ++ .quad 0x0000000000000000 /* 0x5b reserved */ ++ ++ .quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */ ++ .quad 0x00cf92000000ffff /* 0x68 kernel 4GB data at 0x00000000 */ ++ .quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */ ++ .quad 0x00cff2000000ffff /* 0x7b user 4GB data at 0x00000000 */ ++ ++ .quad 0x0000000000000000 /* 0x80 TSS descriptor */ ++ .quad 0x0000000000000000 /* 0x88 LDT descriptor */ ++ ++ /* ++ * Segments used for calling PnP BIOS have byte granularity. ++ * They code segments and data segments have fixed 64k limits, ++ * the transfer segment sizes are set at run time. ++ */ ++ .quad 0x0000000000000000 /* 0x90 32-bit code */ ++ .quad 0x0000000000000000 /* 0x98 16-bit code */ ++ .quad 0x0000000000000000 /* 0xa0 16-bit data */ ++ .quad 0x0000000000000000 /* 0xa8 16-bit data */ ++ .quad 0x0000000000000000 /* 0xb0 16-bit data */ ++ ++ /* ++ * The APM segments have byte granularity and their bases ++ * are set at run time. All have 64k limits. ++ */ ++ .quad 0x0000000000000000 /* 0xb8 APM CS code */ ++ .quad 0x0000000000000000 /* 0xc0 APM CS 16 code (16 bit) */ ++ .quad 0x0000000000000000 /* 0xc8 APM DS data */ ++ ++ .quad 0x0000000000000000 /* 0xd0 - ESPFIX 16-bit SS */ ++ .quad 0x0000000000000000 /* 0xd8 - unused */ ++ .quad 0x0000000000000000 /* 0xe0 - unused */ ++ .quad 0x0000000000000000 /* 0xe8 - unused */ ++ .quad 0x0000000000000000 /* 0xf0 - unused */ ++ .quad 0x0000000000000000 /* 0xf8 - GDT entry 31: double-fault TSS */ ++ ++#if CONFIG_XEN_COMPAT <= 0x030002 ++/* ++ * __xen_guest information ++ */ ++.macro utoa value ++ .if (\value) < 0 || (\value) >= 0x10 ++ utoa (((\value)>>4)&0x0fffffff) ++ .endif ++ .if ((\value) & 0xf) < 10 ++ .byte '0' + ((\value) & 0xf) ++ .else ++ .byte 'A' + ((\value) & 0xf) - 10 ++ .endif ++.endm ++ ++.section __xen_guest ++ .ascii "GUEST_OS=linux,GUEST_VER=2.6" ++ .ascii ",XEN_VER=xen-3.0" ++ .ascii ",VIRT_BASE=0x" ++ utoa __PAGE_OFFSET ++ .ascii ",ELF_PADDR_OFFSET=0x" ++ utoa __PAGE_OFFSET ++ .ascii ",VIRT_ENTRY=0x" ++ utoa (__PAGE_OFFSET + __PHYSICAL_START + VIRT_ENTRY_OFFSET) ++ .ascii ",HYPERCALL_PAGE=0x" ++ utoa ((__PHYSICAL_START+HYPERCALL_PAGE_OFFSET)>>PAGE_SHIFT) ++ .ascii ",FEATURES=writable_page_tables" ++ .ascii "|writable_descriptor_tables" ++ .ascii "|auto_translated_physmap" ++ .ascii "|pae_pgdir_above_4gb" ++ .ascii "|supervisor_mode_kernel" ++#ifdef CONFIG_X86_PAE ++ .ascii ",PAE=yes[extended-cr3]" ++#else ++ .ascii ",PAE=no" ++#endif ++ .ascii ",LOADER=generic" ++ .byte 0 ++#endif /* CONFIG_XEN_COMPAT <= 0x030002 */ ++ ++ ++ ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz, "linux") ++ ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION, .asciz, "2.6") ++ ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION, .asciz, "xen-3.0") ++ ELFNOTE(Xen, XEN_ELFNOTE_VIRT_BASE, .long, __PAGE_OFFSET) ++#if CONFIG_XEN_COMPAT <= 0x030002 ++ ELFNOTE(Xen, XEN_ELFNOTE_PADDR_OFFSET, .long, __PAGE_OFFSET) ++#else ++ ELFNOTE(Xen, XEN_ELFNOTE_PADDR_OFFSET, .long, 0) ++#endif ++ ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, .long, startup_32) ++ ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .long, hypercall_page) ++ ELFNOTE(Xen, XEN_ELFNOTE_HV_START_LOW, .long, HYPERVISOR_VIRT_START) ++ ELFNOTE(Xen, XEN_ELFNOTE_FEATURES, .asciz, "writable_page_tables|writable_descriptor_tables|auto_translated_physmap|pae_pgdir_above_4gb|supervisor_mode_kernel") ++#ifdef CONFIG_X86_PAE ++ ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz, "yes") ++ ELFNOTE(Xen, XEN_ELFNOTE_L1_MFN_VALID, .quad, _PAGE_PRESENT,_PAGE_PRESENT) ++#else ++ ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz, "no") ++ ELFNOTE(Xen, XEN_ELFNOTE_L1_MFN_VALID, .long, _PAGE_PRESENT,_PAGE_PRESENT) ++#endif ++ ELFNOTE(Xen, XEN_ELFNOTE_LOADER, .asciz, "generic") ++ ELFNOTE(Xen, XEN_ELFNOTE_SUSPEND_CANCEL, .long, 1) +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/io_apic_32-xen.c 2009-03-18 10:39:31.000000000 +0100 +@@ -0,0 +1,2786 @@ ++/* ++ * Intel IO-APIC support for multi-Pentium hosts. ++ * ++ * Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar, Hajnalka Szabo ++ * ++ * Many thanks to Stig Venaas for trying out countless experimental ++ * patches and reporting/debugging problems patiently! ++ * ++ * (c) 1999, Multiple IO-APIC support, developed by ++ * Ken-ichi Yaku and ++ * Hidemi Kishimoto , ++ * further tested and cleaned up by Zach Brown ++ * and Ingo Molnar ++ * ++ * Fixes ++ * Maciej W. Rozycki : Bits for genuine 82489DX APICs; ++ * thanks to Eric Gilmore ++ * and Rolf G. Tews ++ * for testing these extensively ++ * Paul Diefenbaugh : Added full ACPI support ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "io_ports.h" ++ ++#ifdef CONFIG_XEN ++ ++#include ++#include ++#include ++ ++/* Fake i8259 */ ++#define make_8259A_irq(_irq) (io_apic_irqs &= ~(1UL<<(_irq))) ++#define disable_8259A_irq(_irq) ((void)0) ++#define i8259A_irq_pending(_irq) (0) ++ ++unsigned long io_apic_irqs; ++ ++static inline unsigned int xen_io_apic_read(unsigned int apic, unsigned int reg) ++{ ++ struct physdev_apic apic_op; ++ int ret; ++ ++ apic_op.apic_physbase = mp_ioapics[apic].mpc_apicaddr; ++ apic_op.reg = reg; ++ ret = HYPERVISOR_physdev_op(PHYSDEVOP_apic_read, &apic_op); ++ if (ret) ++ return ret; ++ return apic_op.value; ++} ++ ++static inline void xen_io_apic_write(unsigned int apic, unsigned int reg, unsigned int value) ++{ ++ struct physdev_apic apic_op; ++ ++ apic_op.apic_physbase = mp_ioapics[apic].mpc_apicaddr; ++ apic_op.reg = reg; ++ apic_op.value = value; ++ WARN_ON(HYPERVISOR_physdev_op(PHYSDEVOP_apic_write, &apic_op)); ++} ++ ++#define io_apic_read(a,r) xen_io_apic_read(a,r) ++#define io_apic_write(a,r,v) xen_io_apic_write(a,r,v) ++ ++#endif /* CONFIG_XEN */ ++ ++int (*ioapic_renumber_irq)(int ioapic, int irq); ++atomic_t irq_mis_count; ++ ++#ifndef CONFIG_XEN ++/* Where if anywhere is the i8259 connect in external int mode */ ++static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; ++#endif ++ ++static DEFINE_SPINLOCK(ioapic_lock); ++static DEFINE_SPINLOCK(vector_lock); ++ ++int timer_over_8254 __initdata = 1; ++ ++/* ++ * Is the SiS APIC rmw bug present ? ++ * -1 = don't know, 0 = no, 1 = yes ++ */ ++int sis_apic_bug = -1; ++ ++/* ++ * # of IRQ routing registers ++ */ ++int nr_ioapic_registers[MAX_IO_APICS]; ++ ++int disable_timer_pin_1 __initdata; ++ ++/* ++ * Rough estimation of how many shared IRQs there are, can ++ * be changed anytime. ++ */ ++#define MAX_PLUS_SHARED_IRQS NR_IRQS ++#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS) ++ ++/* ++ * This is performance-critical, we want to do it O(1) ++ * ++ * the indexing order of this array favors 1:1 mappings ++ * between pins and IRQs. ++ */ ++ ++static struct irq_pin_list { ++ int apic, pin, next; ++} irq_2_pin[PIN_MAP_SIZE]; ++ ++int vector_irq[NR_VECTORS] __read_mostly = { [0 ... NR_VECTORS - 1] = -1}; ++#ifdef CONFIG_PCI_MSI ++#define vector_to_irq(vector) \ ++ (platform_legacy_irq(vector) ? vector : vector_irq[vector]) ++#else ++#define vector_to_irq(vector) (vector) ++#endif ++ ++/* ++ * The common case is 1:1 IRQ<->pin mappings. Sometimes there are ++ * shared ISA-space IRQs, so we have to support them. We are super ++ * fast in the common case, and fast for shared ISA-space IRQs. ++ */ ++static void add_pin_to_irq(unsigned int irq, int apic, int pin) ++{ ++ static int first_free_entry = NR_IRQS; ++ struct irq_pin_list *entry = irq_2_pin + irq; ++ ++ while (entry->next) ++ entry = irq_2_pin + entry->next; ++ ++ if (entry->pin != -1) { ++ entry->next = first_free_entry; ++ entry = irq_2_pin + entry->next; ++ if (++first_free_entry >= PIN_MAP_SIZE) ++ panic("io_apic.c: whoops"); ++ } ++ entry->apic = apic; ++ entry->pin = pin; ++} ++ ++#ifdef CONFIG_XEN ++#define clear_IO_APIC() ((void)0) ++#else ++/* ++ * Reroute an IRQ to a different pin. ++ */ ++static void __init replace_pin_at_irq(unsigned int irq, ++ int oldapic, int oldpin, ++ int newapic, int newpin) ++{ ++ struct irq_pin_list *entry = irq_2_pin + irq; ++ ++ while (1) { ++ if (entry->apic == oldapic && entry->pin == oldpin) { ++ entry->apic = newapic; ++ entry->pin = newpin; ++ } ++ if (!entry->next) ++ break; ++ entry = irq_2_pin + entry->next; ++ } ++} ++ ++static void __modify_IO_APIC_irq (unsigned int irq, unsigned long enable, unsigned long disable) ++{ ++ struct irq_pin_list *entry = irq_2_pin + irq; ++ unsigned int pin, reg; ++ ++ for (;;) { ++ pin = entry->pin; ++ if (pin == -1) ++ break; ++ reg = io_apic_read(entry->apic, 0x10 + pin*2); ++ reg &= ~disable; ++ reg |= enable; ++ io_apic_modify(entry->apic, 0x10 + pin*2, reg); ++ if (!entry->next) ++ break; ++ entry = irq_2_pin + entry->next; ++ } ++} ++ ++/* mask = 1 */ ++static void __mask_IO_APIC_irq (unsigned int irq) ++{ ++ __modify_IO_APIC_irq(irq, 0x00010000, 0); ++} ++ ++/* mask = 0 */ ++static void __unmask_IO_APIC_irq (unsigned int irq) ++{ ++ __modify_IO_APIC_irq(irq, 0, 0x00010000); ++} ++ ++/* mask = 1, trigger = 0 */ ++static void __mask_and_edge_IO_APIC_irq (unsigned int irq) ++{ ++ __modify_IO_APIC_irq(irq, 0x00010000, 0x00008000); ++} ++ ++/* mask = 0, trigger = 1 */ ++static void __unmask_and_level_IO_APIC_irq (unsigned int irq) ++{ ++ __modify_IO_APIC_irq(irq, 0x00008000, 0x00010000); ++} ++ ++static void mask_IO_APIC_irq (unsigned int irq) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ __mask_IO_APIC_irq(irq); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++} ++ ++static void unmask_IO_APIC_irq (unsigned int irq) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ __unmask_IO_APIC_irq(irq); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++} ++ ++static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) ++{ ++ struct IO_APIC_route_entry entry; ++ unsigned long flags; ++ ++ /* Check delivery_mode to be sure we're not clearing an SMI pin */ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ *(((int*)&entry) + 0) = io_apic_read(apic, 0x10 + 2 * pin); ++ *(((int*)&entry) + 1) = io_apic_read(apic, 0x11 + 2 * pin); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ if (entry.delivery_mode == dest_SMI) ++ return; ++ ++ /* ++ * Disable it in the IO-APIC irq-routing table: ++ */ ++ memset(&entry, 0, sizeof(entry)); ++ entry.mask = 1; ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry) + 0)); ++ io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry) + 1)); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++} ++ ++static void clear_IO_APIC (void) ++{ ++ int apic, pin; ++ ++ for (apic = 0; apic < nr_ioapics; apic++) ++ for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) ++ clear_IO_APIC_pin(apic, pin); ++} ++ ++#ifdef CONFIG_SMP ++static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t cpumask) ++{ ++ unsigned long flags; ++ int pin; ++ struct irq_pin_list *entry = irq_2_pin + irq; ++ unsigned int apicid_value; ++ cpumask_t tmp; ++ ++ cpus_and(tmp, cpumask, cpu_online_map); ++ if (cpus_empty(tmp)) ++ tmp = TARGET_CPUS; ++ ++ cpus_and(cpumask, tmp, CPU_MASK_ALL); ++ ++ apicid_value = cpu_mask_to_apicid(cpumask); ++ /* Prepare to do the io_apic_write */ ++ apicid_value = apicid_value << 24; ++ spin_lock_irqsave(&ioapic_lock, flags); ++ for (;;) { ++ pin = entry->pin; ++ if (pin == -1) ++ break; ++ io_apic_write(entry->apic, 0x10 + 1 + pin*2, apicid_value); ++ if (!entry->next) ++ break; ++ entry = irq_2_pin + entry->next; ++ } ++ set_irq_info(irq, cpumask); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++} ++ ++#if defined(CONFIG_IRQBALANCE) ++# include /* kernel_thread() */ ++# include /* kstat */ ++# include /* kmalloc() */ ++# include /* time_after() */ ++ ++#ifdef CONFIG_BALANCED_IRQ_DEBUG ++# define TDprintk(x...) do { printk("<%ld:%s:%d>: ", jiffies, __FILE__, __LINE__); printk(x); } while (0) ++# define Dprintk(x...) do { TDprintk(x); } while (0) ++# else ++# define TDprintk(x...) ++# define Dprintk(x...) ++# endif ++ ++#define IRQBALANCE_CHECK_ARCH -999 ++#define MAX_BALANCED_IRQ_INTERVAL (5*HZ) ++#define MIN_BALANCED_IRQ_INTERVAL (HZ/2) ++#define BALANCED_IRQ_MORE_DELTA (HZ/10) ++#define BALANCED_IRQ_LESS_DELTA (HZ) ++ ++static int irqbalance_disabled __read_mostly = IRQBALANCE_CHECK_ARCH; ++static int physical_balance __read_mostly; ++static long balanced_irq_interval __read_mostly = MAX_BALANCED_IRQ_INTERVAL; ++ ++static struct irq_cpu_info { ++ unsigned long * last_irq; ++ unsigned long * irq_delta; ++ unsigned long irq; ++} irq_cpu_data[NR_CPUS]; ++ ++#define CPU_IRQ(cpu) (irq_cpu_data[cpu].irq) ++#define LAST_CPU_IRQ(cpu,irq) (irq_cpu_data[cpu].last_irq[irq]) ++#define IRQ_DELTA(cpu,irq) (irq_cpu_data[cpu].irq_delta[irq]) ++ ++#define IDLE_ENOUGH(cpu,now) \ ++ (idle_cpu(cpu) && ((now) - per_cpu(irq_stat, (cpu)).idle_timestamp > 1)) ++ ++#define IRQ_ALLOWED(cpu, allowed_mask) cpu_isset(cpu, allowed_mask) ++ ++#define CPU_TO_PACKAGEINDEX(i) (first_cpu(cpu_sibling_map[i])) ++ ++static cpumask_t balance_irq_affinity[NR_IRQS] = { ++ [0 ... NR_IRQS-1] = CPU_MASK_ALL ++}; ++ ++void set_balance_irq_affinity(unsigned int irq, cpumask_t mask) ++{ ++ balance_irq_affinity[irq] = mask; ++} ++ ++static unsigned long move(int curr_cpu, cpumask_t allowed_mask, ++ unsigned long now, int direction) ++{ ++ int search_idle = 1; ++ int cpu = curr_cpu; ++ ++ goto inside; ++ ++ do { ++ if (unlikely(cpu == curr_cpu)) ++ search_idle = 0; ++inside: ++ if (direction == 1) { ++ cpu++; ++ if (cpu >= NR_CPUS) ++ cpu = 0; ++ } else { ++ cpu--; ++ if (cpu == -1) ++ cpu = NR_CPUS-1; ++ } ++ } while (!cpu_online(cpu) || !IRQ_ALLOWED(cpu,allowed_mask) || ++ (search_idle && !IDLE_ENOUGH(cpu,now))); ++ ++ return cpu; ++} ++ ++static inline void balance_irq(int cpu, int irq) ++{ ++ unsigned long now = jiffies; ++ cpumask_t allowed_mask; ++ unsigned int new_cpu; ++ ++ if (irqbalance_disabled) ++ return; ++ ++ cpus_and(allowed_mask, cpu_online_map, balance_irq_affinity[irq]); ++ new_cpu = move(cpu, allowed_mask, now, 1); ++ if (cpu != new_cpu) { ++ set_pending_irq(irq, cpumask_of_cpu(new_cpu)); ++ } ++} ++ ++static inline void rotate_irqs_among_cpus(unsigned long useful_load_threshold) ++{ ++ int i, j; ++ Dprintk("Rotating IRQs among CPUs.\n"); ++ for_each_online_cpu(i) { ++ for (j = 0; j < NR_IRQS; j++) { ++ if (!irq_desc[j].action) ++ continue; ++ /* Is it a significant load ? */ ++ if (IRQ_DELTA(CPU_TO_PACKAGEINDEX(i),j) < ++ useful_load_threshold) ++ continue; ++ balance_irq(i, j); ++ } ++ } ++ balanced_irq_interval = max((long)MIN_BALANCED_IRQ_INTERVAL, ++ balanced_irq_interval - BALANCED_IRQ_LESS_DELTA); ++ return; ++} ++ ++static void do_irq_balance(void) ++{ ++ int i, j; ++ unsigned long max_cpu_irq = 0, min_cpu_irq = (~0); ++ unsigned long move_this_load = 0; ++ int max_loaded = 0, min_loaded = 0; ++ int load; ++ unsigned long useful_load_threshold = balanced_irq_interval + 10; ++ int selected_irq; ++ int tmp_loaded, first_attempt = 1; ++ unsigned long tmp_cpu_irq; ++ unsigned long imbalance = 0; ++ cpumask_t allowed_mask, target_cpu_mask, tmp; ++ ++ for_each_possible_cpu(i) { ++ int package_index; ++ CPU_IRQ(i) = 0; ++ if (!cpu_online(i)) ++ continue; ++ package_index = CPU_TO_PACKAGEINDEX(i); ++ for (j = 0; j < NR_IRQS; j++) { ++ unsigned long value_now, delta; ++ /* Is this an active IRQ? */ ++ if (!irq_desc[j].action) ++ continue; ++ if ( package_index == i ) ++ IRQ_DELTA(package_index,j) = 0; ++ /* Determine the total count per processor per IRQ */ ++ value_now = (unsigned long) kstat_cpu(i).irqs[j]; ++ ++ /* Determine the activity per processor per IRQ */ ++ delta = value_now - LAST_CPU_IRQ(i,j); ++ ++ /* Update last_cpu_irq[][] for the next time */ ++ LAST_CPU_IRQ(i,j) = value_now; ++ ++ /* Ignore IRQs whose rate is less than the clock */ ++ if (delta < useful_load_threshold) ++ continue; ++ /* update the load for the processor or package total */ ++ IRQ_DELTA(package_index,j) += delta; ++ ++ /* Keep track of the higher numbered sibling as well */ ++ if (i != package_index) ++ CPU_IRQ(i) += delta; ++ /* ++ * We have sibling A and sibling B in the package ++ * ++ * cpu_irq[A] = load for cpu A + load for cpu B ++ * cpu_irq[B] = load for cpu B ++ */ ++ CPU_IRQ(package_index) += delta; ++ } ++ } ++ /* Find the least loaded processor package */ ++ for_each_online_cpu(i) { ++ if (i != CPU_TO_PACKAGEINDEX(i)) ++ continue; ++ if (min_cpu_irq > CPU_IRQ(i)) { ++ min_cpu_irq = CPU_IRQ(i); ++ min_loaded = i; ++ } ++ } ++ max_cpu_irq = ULONG_MAX; ++ ++tryanothercpu: ++ /* Look for heaviest loaded processor. ++ * We may come back to get the next heaviest loaded processor. ++ * Skip processors with trivial loads. ++ */ ++ tmp_cpu_irq = 0; ++ tmp_loaded = -1; ++ for_each_online_cpu(i) { ++ if (i != CPU_TO_PACKAGEINDEX(i)) ++ continue; ++ if (max_cpu_irq <= CPU_IRQ(i)) ++ continue; ++ if (tmp_cpu_irq < CPU_IRQ(i)) { ++ tmp_cpu_irq = CPU_IRQ(i); ++ tmp_loaded = i; ++ } ++ } ++ ++ if (tmp_loaded == -1) { ++ /* In the case of small number of heavy interrupt sources, ++ * loading some of the cpus too much. We use Ingo's original ++ * approach to rotate them around. ++ */ ++ if (!first_attempt && imbalance >= useful_load_threshold) { ++ rotate_irqs_among_cpus(useful_load_threshold); ++ return; ++ } ++ goto not_worth_the_effort; ++ } ++ ++ first_attempt = 0; /* heaviest search */ ++ max_cpu_irq = tmp_cpu_irq; /* load */ ++ max_loaded = tmp_loaded; /* processor */ ++ imbalance = (max_cpu_irq - min_cpu_irq) / 2; ++ ++ Dprintk("max_loaded cpu = %d\n", max_loaded); ++ Dprintk("min_loaded cpu = %d\n", min_loaded); ++ Dprintk("max_cpu_irq load = %ld\n", max_cpu_irq); ++ Dprintk("min_cpu_irq load = %ld\n", min_cpu_irq); ++ Dprintk("load imbalance = %lu\n", imbalance); ++ ++ /* if imbalance is less than approx 10% of max load, then ++ * observe diminishing returns action. - quit ++ */ ++ if (imbalance < (max_cpu_irq >> 3)) { ++ Dprintk("Imbalance too trivial\n"); ++ goto not_worth_the_effort; ++ } ++ ++tryanotherirq: ++ /* if we select an IRQ to move that can't go where we want, then ++ * see if there is another one to try. ++ */ ++ move_this_load = 0; ++ selected_irq = -1; ++ for (j = 0; j < NR_IRQS; j++) { ++ /* Is this an active IRQ? */ ++ if (!irq_desc[j].action) ++ continue; ++ if (imbalance <= IRQ_DELTA(max_loaded,j)) ++ continue; ++ /* Try to find the IRQ that is closest to the imbalance ++ * without going over. ++ */ ++ if (move_this_load < IRQ_DELTA(max_loaded,j)) { ++ move_this_load = IRQ_DELTA(max_loaded,j); ++ selected_irq = j; ++ } ++ } ++ if (selected_irq == -1) { ++ goto tryanothercpu; ++ } ++ ++ imbalance = move_this_load; ++ ++ /* For physical_balance case, we accumlated both load ++ * values in the one of the siblings cpu_irq[], ++ * to use the same code for physical and logical processors ++ * as much as possible. ++ * ++ * NOTE: the cpu_irq[] array holds the sum of the load for ++ * sibling A and sibling B in the slot for the lowest numbered ++ * sibling (A), _AND_ the load for sibling B in the slot for ++ * the higher numbered sibling. ++ * ++ * We seek the least loaded sibling by making the comparison ++ * (A+B)/2 vs B ++ */ ++ load = CPU_IRQ(min_loaded) >> 1; ++ for_each_cpu_mask(j, cpu_sibling_map[min_loaded]) { ++ if (load > CPU_IRQ(j)) { ++ /* This won't change cpu_sibling_map[min_loaded] */ ++ load = CPU_IRQ(j); ++ min_loaded = j; ++ } ++ } ++ ++ cpus_and(allowed_mask, ++ cpu_online_map, ++ balance_irq_affinity[selected_irq]); ++ target_cpu_mask = cpumask_of_cpu(min_loaded); ++ cpus_and(tmp, target_cpu_mask, allowed_mask); ++ ++ if (!cpus_empty(tmp)) { ++ ++ Dprintk("irq = %d moved to cpu = %d\n", ++ selected_irq, min_loaded); ++ /* mark for change destination */ ++ set_pending_irq(selected_irq, cpumask_of_cpu(min_loaded)); ++ ++ /* Since we made a change, come back sooner to ++ * check for more variation. ++ */ ++ balanced_irq_interval = max((long)MIN_BALANCED_IRQ_INTERVAL, ++ balanced_irq_interval - BALANCED_IRQ_LESS_DELTA); ++ return; ++ } ++ goto tryanotherirq; ++ ++not_worth_the_effort: ++ /* ++ * if we did not find an IRQ to move, then adjust the time interval ++ * upward ++ */ ++ balanced_irq_interval = min((long)MAX_BALANCED_IRQ_INTERVAL, ++ balanced_irq_interval + BALANCED_IRQ_MORE_DELTA); ++ Dprintk("IRQ worth rotating not found\n"); ++ return; ++} ++ ++static int balanced_irq(void *unused) ++{ ++ int i; ++ unsigned long prev_balance_time = jiffies; ++ long time_remaining = balanced_irq_interval; ++ ++ daemonize("kirqd"); ++ ++ /* push everything to CPU 0 to give us a starting point. */ ++ for (i = 0 ; i < NR_IRQS ; i++) { ++ irq_desc[i].pending_mask = cpumask_of_cpu(0); ++ set_pending_irq(i, cpumask_of_cpu(0)); ++ } ++ ++ for ( ; ; ) { ++ time_remaining = schedule_timeout_interruptible(time_remaining); ++ try_to_freeze(); ++ if (time_after(jiffies, ++ prev_balance_time+balanced_irq_interval)) { ++ preempt_disable(); ++ do_irq_balance(); ++ prev_balance_time = jiffies; ++ time_remaining = balanced_irq_interval; ++ preempt_enable(); ++ } ++ } ++ return 0; ++} ++ ++static int __init balanced_irq_init(void) ++{ ++ int i; ++ struct cpuinfo_x86 *c; ++ cpumask_t tmp; ++ ++ cpus_shift_right(tmp, cpu_online_map, 2); ++ c = &boot_cpu_data; ++ /* When not overwritten by the command line ask subarchitecture. */ ++ if (irqbalance_disabled == IRQBALANCE_CHECK_ARCH) ++ irqbalance_disabled = NO_BALANCE_IRQ; ++ if (irqbalance_disabled) ++ return 0; ++ ++ /* disable irqbalance completely if there is only one processor online */ ++ if (num_online_cpus() < 2) { ++ irqbalance_disabled = 1; ++ return 0; ++ } ++ /* ++ * Enable physical balance only if more than 1 physical processor ++ * is present ++ */ ++ if (smp_num_siblings > 1 && !cpus_empty(tmp)) ++ physical_balance = 1; ++ ++ for_each_online_cpu(i) { ++ irq_cpu_data[i].irq_delta = kmalloc(sizeof(unsigned long) * NR_IRQS, GFP_KERNEL); ++ irq_cpu_data[i].last_irq = kmalloc(sizeof(unsigned long) * NR_IRQS, GFP_KERNEL); ++ if (irq_cpu_data[i].irq_delta == NULL || irq_cpu_data[i].last_irq == NULL) { ++ printk(KERN_ERR "balanced_irq_init: out of memory"); ++ goto failed; ++ } ++ memset(irq_cpu_data[i].irq_delta,0,sizeof(unsigned long) * NR_IRQS); ++ memset(irq_cpu_data[i].last_irq,0,sizeof(unsigned long) * NR_IRQS); ++ } ++ ++ printk(KERN_INFO "Starting balanced_irq\n"); ++ if (kernel_thread(balanced_irq, NULL, CLONE_KERNEL) >= 0) ++ return 0; ++ else ++ printk(KERN_ERR "balanced_irq_init: failed to spawn balanced_irq"); ++failed: ++ for_each_possible_cpu(i) { ++ kfree(irq_cpu_data[i].irq_delta); ++ irq_cpu_data[i].irq_delta = NULL; ++ kfree(irq_cpu_data[i].last_irq); ++ irq_cpu_data[i].last_irq = NULL; ++ } ++ return 0; ++} ++ ++int __init irqbalance_disable(char *str) ++{ ++ irqbalance_disabled = 1; ++ return 1; ++} ++ ++__setup("noirqbalance", irqbalance_disable); ++ ++late_initcall(balanced_irq_init); ++#endif /* CONFIG_IRQBALANCE */ ++#endif /* CONFIG_SMP */ ++#endif ++ ++#ifndef CONFIG_SMP ++void fastcall send_IPI_self(int vector) ++{ ++#ifndef CONFIG_XEN ++ unsigned int cfg; ++ ++ /* ++ * Wait for idle. ++ */ ++ apic_wait_icr_idle(); ++ cfg = APIC_DM_FIXED | APIC_DEST_SELF | vector | APIC_DEST_LOGICAL; ++ /* ++ * Send the IPI. The write to APIC_ICR fires this off. ++ */ ++ apic_write_around(APIC_ICR, cfg); ++#endif ++} ++#endif /* !CONFIG_SMP */ ++ ++ ++/* ++ * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to ++ * specific CPU-side IRQs. ++ */ ++ ++#define MAX_PIRQS 8 ++static int pirq_entries [MAX_PIRQS]; ++static int pirqs_enabled; ++int skip_ioapic_setup; ++ ++static int __init ioapic_setup(char *str) ++{ ++ skip_ioapic_setup = 1; ++ return 1; ++} ++ ++__setup("noapic", ioapic_setup); ++ ++static int __init ioapic_pirq_setup(char *str) ++{ ++ int i, max; ++ int ints[MAX_PIRQS+1]; ++ ++ get_options(str, ARRAY_SIZE(ints), ints); ++ ++ for (i = 0; i < MAX_PIRQS; i++) ++ pirq_entries[i] = -1; ++ ++ pirqs_enabled = 1; ++ apic_printk(APIC_VERBOSE, KERN_INFO ++ "PIRQ redirection, working around broken MP-BIOS.\n"); ++ max = MAX_PIRQS; ++ if (ints[0] < MAX_PIRQS) ++ max = ints[0]; ++ ++ for (i = 0; i < max; i++) { ++ apic_printk(APIC_VERBOSE, KERN_DEBUG ++ "... PIRQ%d -> IRQ %d\n", i, ints[i+1]); ++ /* ++ * PIRQs are mapped upside down, usually. ++ */ ++ pirq_entries[MAX_PIRQS-i-1] = ints[i+1]; ++ } ++ return 1; ++} ++ ++__setup("pirq=", ioapic_pirq_setup); ++ ++/* ++ * Find the IRQ entry number of a certain pin. ++ */ ++static int find_irq_entry(int apic, int pin, int type) ++{ ++ int i; ++ ++ for (i = 0; i < mp_irq_entries; i++) ++ if (mp_irqs[i].mpc_irqtype == type && ++ (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid || ++ mp_irqs[i].mpc_dstapic == MP_APIC_ALL) && ++ mp_irqs[i].mpc_dstirq == pin) ++ return i; ++ ++ return -1; ++} ++ ++#ifndef CONFIG_XEN ++/* ++ * Find the pin to which IRQ[irq] (ISA) is connected ++ */ ++static int __init find_isa_irq_pin(int irq, int type) ++{ ++ int i; ++ ++ for (i = 0; i < mp_irq_entries; i++) { ++ int lbus = mp_irqs[i].mpc_srcbus; ++ ++ if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || ++ mp_bus_id_to_type[lbus] == MP_BUS_EISA || ++ mp_bus_id_to_type[lbus] == MP_BUS_MCA || ++ mp_bus_id_to_type[lbus] == MP_BUS_NEC98 ++ ) && ++ (mp_irqs[i].mpc_irqtype == type) && ++ (mp_irqs[i].mpc_srcbusirq == irq)) ++ ++ return mp_irqs[i].mpc_dstirq; ++ } ++ return -1; ++} ++ ++static int __init find_isa_irq_apic(int irq, int type) ++{ ++ int i; ++ ++ for (i = 0; i < mp_irq_entries; i++) { ++ int lbus = mp_irqs[i].mpc_srcbus; ++ ++ if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || ++ mp_bus_id_to_type[lbus] == MP_BUS_EISA || ++ mp_bus_id_to_type[lbus] == MP_BUS_MCA || ++ mp_bus_id_to_type[lbus] == MP_BUS_NEC98 ++ ) && ++ (mp_irqs[i].mpc_irqtype == type) && ++ (mp_irqs[i].mpc_srcbusirq == irq)) ++ break; ++ } ++ if (i < mp_irq_entries) { ++ int apic; ++ for(apic = 0; apic < nr_ioapics; apic++) { ++ if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic) ++ return apic; ++ } ++ } ++ ++ return -1; ++} ++#endif ++ ++/* ++ * Find a specific PCI IRQ entry. ++ * Not an __init, possibly needed by modules ++ */ ++static int pin_2_irq(int idx, int apic, int pin); ++ ++int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin) ++{ ++ int apic, i, best_guess = -1; ++ ++ apic_printk(APIC_DEBUG, "querying PCI -> IRQ mapping bus:%d, " ++ "slot:%d, pin:%d.\n", bus, slot, pin); ++ if (mp_bus_id_to_pci_bus[bus] == -1) { ++ printk(KERN_WARNING "PCI BIOS passed nonexistent PCI bus %d!\n", bus); ++ return -1; ++ } ++ for (i = 0; i < mp_irq_entries; i++) { ++ int lbus = mp_irqs[i].mpc_srcbus; ++ ++ for (apic = 0; apic < nr_ioapics; apic++) ++ if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic || ++ mp_irqs[i].mpc_dstapic == MP_APIC_ALL) ++ break; ++ ++ if ((mp_bus_id_to_type[lbus] == MP_BUS_PCI) && ++ !mp_irqs[i].mpc_irqtype && ++ (bus == lbus) && ++ (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) { ++ int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); ++ ++ if (!(apic || IO_APIC_IRQ(irq))) ++ continue; ++ ++ if (pin == (mp_irqs[i].mpc_srcbusirq & 3)) ++ return irq; ++ /* ++ * Use the first all-but-pin matching entry as a ++ * best-guess fuzzy result for broken mptables. ++ */ ++ if (best_guess < 0) ++ best_guess = irq; ++ } ++ } ++ return best_guess; ++} ++EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector); ++ ++/* ++ * This function currently is only a helper for the i386 smp boot process where ++ * we need to reprogram the ioredtbls to cater for the cpus which have come online ++ * so mask in all cases should simply be TARGET_CPUS ++ */ ++#ifdef CONFIG_SMP ++#ifndef CONFIG_XEN ++void __init setup_ioapic_dest(void) ++{ ++ int pin, ioapic, irq, irq_entry; ++ ++ if (skip_ioapic_setup == 1) ++ return; ++ ++ for (ioapic = 0; ioapic < nr_ioapics; ioapic++) { ++ for (pin = 0; pin < nr_ioapic_registers[ioapic]; pin++) { ++ irq_entry = find_irq_entry(ioapic, pin, mp_INT); ++ if (irq_entry == -1) ++ continue; ++ irq = pin_2_irq(irq_entry, ioapic, pin); ++ set_ioapic_affinity_irq(irq, TARGET_CPUS); ++ } ++ ++ } ++} ++#endif /* !CONFIG_XEN */ ++#endif ++ ++/* ++ * EISA Edge/Level control register, ELCR ++ */ ++static int EISA_ELCR(unsigned int irq) ++{ ++ if (irq < 16) { ++ unsigned int port = 0x4d0 + (irq >> 3); ++ return (inb(port) >> (irq & 7)) & 1; ++ } ++ apic_printk(APIC_VERBOSE, KERN_INFO ++ "Broken MPtable reports ISA irq %d\n", irq); ++ return 0; ++} ++ ++/* EISA interrupts are always polarity zero and can be edge or level ++ * trigger depending on the ELCR value. If an interrupt is listed as ++ * EISA conforming in the MP table, that means its trigger type must ++ * be read in from the ELCR */ ++ ++#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_srcbusirq)) ++#define default_EISA_polarity(idx) (0) ++ ++/* ISA interrupts are always polarity zero edge triggered, ++ * when listed as conforming in the MP table. */ ++ ++#define default_ISA_trigger(idx) (0) ++#define default_ISA_polarity(idx) (0) ++ ++/* PCI interrupts are always polarity one level triggered, ++ * when listed as conforming in the MP table. */ ++ ++#define default_PCI_trigger(idx) (1) ++#define default_PCI_polarity(idx) (1) ++ ++/* MCA interrupts are always polarity zero level triggered, ++ * when listed as conforming in the MP table. */ ++ ++#define default_MCA_trigger(idx) (1) ++#define default_MCA_polarity(idx) (0) ++ ++/* NEC98 interrupts are always polarity zero edge triggered, ++ * when listed as conforming in the MP table. */ ++ ++#define default_NEC98_trigger(idx) (0) ++#define default_NEC98_polarity(idx) (0) ++ ++static int __init MPBIOS_polarity(int idx) ++{ ++ int bus = mp_irqs[idx].mpc_srcbus; ++ int polarity; ++ ++ /* ++ * Determine IRQ line polarity (high active or low active): ++ */ ++ switch (mp_irqs[idx].mpc_irqflag & 3) ++ { ++ case 0: /* conforms, ie. bus-type dependent polarity */ ++ { ++ switch (mp_bus_id_to_type[bus]) ++ { ++ case MP_BUS_ISA: /* ISA pin */ ++ { ++ polarity = default_ISA_polarity(idx); ++ break; ++ } ++ case MP_BUS_EISA: /* EISA pin */ ++ { ++ polarity = default_EISA_polarity(idx); ++ break; ++ } ++ case MP_BUS_PCI: /* PCI pin */ ++ { ++ polarity = default_PCI_polarity(idx); ++ break; ++ } ++ case MP_BUS_MCA: /* MCA pin */ ++ { ++ polarity = default_MCA_polarity(idx); ++ break; ++ } ++ case MP_BUS_NEC98: /* NEC 98 pin */ ++ { ++ polarity = default_NEC98_polarity(idx); ++ break; ++ } ++ default: ++ { ++ printk(KERN_WARNING "broken BIOS!!\n"); ++ polarity = 1; ++ break; ++ } ++ } ++ break; ++ } ++ case 1: /* high active */ ++ { ++ polarity = 0; ++ break; ++ } ++ case 2: /* reserved */ ++ { ++ printk(KERN_WARNING "broken BIOS!!\n"); ++ polarity = 1; ++ break; ++ } ++ case 3: /* low active */ ++ { ++ polarity = 1; ++ break; ++ } ++ default: /* invalid */ ++ { ++ printk(KERN_WARNING "broken BIOS!!\n"); ++ polarity = 1; ++ break; ++ } ++ } ++ return polarity; ++} ++ ++static int MPBIOS_trigger(int idx) ++{ ++ int bus = mp_irqs[idx].mpc_srcbus; ++ int trigger; ++ ++ /* ++ * Determine IRQ trigger mode (edge or level sensitive): ++ */ ++ switch ((mp_irqs[idx].mpc_irqflag>>2) & 3) ++ { ++ case 0: /* conforms, ie. bus-type dependent */ ++ { ++ switch (mp_bus_id_to_type[bus]) ++ { ++ case MP_BUS_ISA: /* ISA pin */ ++ { ++ trigger = default_ISA_trigger(idx); ++ break; ++ } ++ case MP_BUS_EISA: /* EISA pin */ ++ { ++ trigger = default_EISA_trigger(idx); ++ break; ++ } ++ case MP_BUS_PCI: /* PCI pin */ ++ { ++ trigger = default_PCI_trigger(idx); ++ break; ++ } ++ case MP_BUS_MCA: /* MCA pin */ ++ { ++ trigger = default_MCA_trigger(idx); ++ break; ++ } ++ case MP_BUS_NEC98: /* NEC 98 pin */ ++ { ++ trigger = default_NEC98_trigger(idx); ++ break; ++ } ++ default: ++ { ++ printk(KERN_WARNING "broken BIOS!!\n"); ++ trigger = 1; ++ break; ++ } ++ } ++ break; ++ } ++ case 1: /* edge */ ++ { ++ trigger = 0; ++ break; ++ } ++ case 2: /* reserved */ ++ { ++ printk(KERN_WARNING "broken BIOS!!\n"); ++ trigger = 1; ++ break; ++ } ++ case 3: /* level */ ++ { ++ trigger = 1; ++ break; ++ } ++ default: /* invalid */ ++ { ++ printk(KERN_WARNING "broken BIOS!!\n"); ++ trigger = 0; ++ break; ++ } ++ } ++ return trigger; ++} ++ ++static inline int irq_polarity(int idx) ++{ ++ return MPBIOS_polarity(idx); ++} ++ ++static inline int irq_trigger(int idx) ++{ ++ return MPBIOS_trigger(idx); ++} ++ ++static int pin_2_irq(int idx, int apic, int pin) ++{ ++ int irq, i; ++ int bus = mp_irqs[idx].mpc_srcbus; ++ ++ /* ++ * Debugging check, we are in big trouble if this message pops up! ++ */ ++ if (mp_irqs[idx].mpc_dstirq != pin) ++ printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n"); ++ ++ switch (mp_bus_id_to_type[bus]) ++ { ++ case MP_BUS_ISA: /* ISA pin */ ++ case MP_BUS_EISA: ++ case MP_BUS_MCA: ++ case MP_BUS_NEC98: ++ { ++ irq = mp_irqs[idx].mpc_srcbusirq; ++ break; ++ } ++ case MP_BUS_PCI: /* PCI pin */ ++ { ++ /* ++ * PCI IRQs are mapped in order ++ */ ++ i = irq = 0; ++ while (i < apic) ++ irq += nr_ioapic_registers[i++]; ++ irq += pin; ++ ++ /* ++ * For MPS mode, so far only needed by ES7000 platform ++ */ ++ if (ioapic_renumber_irq) ++ irq = ioapic_renumber_irq(apic, irq); ++ ++ break; ++ } ++ default: ++ { ++ printk(KERN_ERR "unknown bus type %d.\n",bus); ++ irq = 0; ++ break; ++ } ++ } ++ ++ /* ++ * PCI IRQ command line redirection. Yes, limits are hardcoded. ++ */ ++ if ((pin >= 16) && (pin <= 23)) { ++ if (pirq_entries[pin-16] != -1) { ++ if (!pirq_entries[pin-16]) { ++ apic_printk(APIC_VERBOSE, KERN_DEBUG ++ "disabling PIRQ%d\n", pin-16); ++ } else { ++ irq = pirq_entries[pin-16]; ++ apic_printk(APIC_VERBOSE, KERN_DEBUG ++ "using PIRQ%d -> IRQ %d\n", ++ pin-16, irq); ++ } ++ } ++ } ++ return irq; ++} ++ ++static inline int IO_APIC_irq_trigger(int irq) ++{ ++ int apic, idx, pin; ++ ++ for (apic = 0; apic < nr_ioapics; apic++) { ++ for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { ++ idx = find_irq_entry(apic,pin,mp_INT); ++ if ((idx != -1) && (irq == pin_2_irq(idx,apic,pin))) ++ return irq_trigger(idx); ++ } ++ } ++ /* ++ * nonexistent IRQs are edge default ++ */ ++ return 0; ++} ++ ++/* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */ ++u8 irq_vector[NR_IRQ_VECTORS] __read_mostly; /* = { FIRST_DEVICE_VECTOR , 0 }; */ ++ ++int assign_irq_vector(int irq) ++{ ++ unsigned long flags; ++ int vector; ++ struct physdev_irq irq_op; ++ ++ BUG_ON(irq != AUTO_ASSIGN && (unsigned)irq >= NR_IRQ_VECTORS); ++ ++ if (irq < PIRQ_BASE || irq - PIRQ_BASE >= NR_PIRQS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&vector_lock, flags); ++ ++ if (irq != AUTO_ASSIGN && IO_APIC_VECTOR(irq) > 0) { ++ spin_unlock_irqrestore(&vector_lock, flags); ++ return IO_APIC_VECTOR(irq); ++ } ++ ++ irq_op.irq = irq; ++ if (HYPERVISOR_physdev_op(PHYSDEVOP_alloc_irq_vector, &irq_op)) { ++ spin_unlock_irqrestore(&vector_lock, flags); ++ return -ENOSPC; ++ } ++ ++ vector = irq_op.vector; ++ vector_irq[vector] = irq; ++ if (irq != AUTO_ASSIGN) ++ IO_APIC_VECTOR(irq) = vector; ++ ++ spin_unlock_irqrestore(&vector_lock, flags); ++ ++ return vector; ++} ++ ++#ifndef CONFIG_XEN ++static struct hw_interrupt_type ioapic_level_type; ++static struct hw_interrupt_type ioapic_edge_type; ++ ++#define IOAPIC_AUTO -1 ++#define IOAPIC_EDGE 0 ++#define IOAPIC_LEVEL 1 ++ ++static void ioapic_register_intr(int irq, int vector, unsigned long trigger) ++{ ++ unsigned idx; ++ ++ idx = use_pci_vector() && !platform_legacy_irq(irq) ? vector : irq; ++ ++ if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) || ++ trigger == IOAPIC_LEVEL) ++ irq_desc[idx].chip = &ioapic_level_type; ++ else ++ irq_desc[idx].chip = &ioapic_edge_type; ++ set_intr_gate(vector, interrupt[idx]); ++} ++#else ++#define ioapic_register_intr(irq, vector, trigger) evtchn_register_pirq(irq) ++#endif ++ ++static void __init setup_IO_APIC_irqs(void) ++{ ++ struct IO_APIC_route_entry entry; ++ int apic, pin, idx, irq, first_notcon = 1, vector; ++ unsigned long flags; ++ ++ apic_printk(APIC_VERBOSE, KERN_DEBUG "init IO_APIC IRQs\n"); ++ ++ for (apic = 0; apic < nr_ioapics; apic++) { ++ for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { ++ ++ /* ++ * add it to the IO-APIC irq-routing table: ++ */ ++ memset(&entry,0,sizeof(entry)); ++ ++ entry.delivery_mode = INT_DELIVERY_MODE; ++ entry.dest_mode = INT_DEST_MODE; ++ entry.mask = 0; /* enable IRQ */ ++ entry.dest.logical.logical_dest = ++ cpu_mask_to_apicid(TARGET_CPUS); ++ ++ idx = find_irq_entry(apic,pin,mp_INT); ++ if (idx == -1) { ++ if (first_notcon) { ++ apic_printk(APIC_VERBOSE, KERN_DEBUG ++ " IO-APIC (apicid-pin) %d-%d", ++ mp_ioapics[apic].mpc_apicid, ++ pin); ++ first_notcon = 0; ++ } else ++ apic_printk(APIC_VERBOSE, ", %d-%d", ++ mp_ioapics[apic].mpc_apicid, pin); ++ continue; ++ } ++ ++ entry.trigger = irq_trigger(idx); ++ entry.polarity = irq_polarity(idx); ++ ++ if (irq_trigger(idx)) { ++ entry.trigger = 1; ++ entry.mask = 1; ++ } ++ ++ irq = pin_2_irq(idx, apic, pin); ++ /* ++ * skip adding the timer int on secondary nodes, which causes ++ * a small but painful rift in the time-space continuum ++ */ ++ if (multi_timer_check(apic, irq)) ++ continue; ++ else ++ add_pin_to_irq(irq, apic, pin); ++ ++ if (/*!apic &&*/ !IO_APIC_IRQ(irq)) ++ continue; ++ ++ if (IO_APIC_IRQ(irq)) { ++ vector = assign_irq_vector(irq); ++ entry.vector = vector; ++ ioapic_register_intr(irq, vector, IOAPIC_AUTO); ++ ++ if (!apic && (irq < 16)) ++ disable_8259A_irq(irq); ++ } ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); ++ io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); ++ set_native_irq_info(irq, TARGET_CPUS); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ } ++ } ++ ++ if (!first_notcon) ++ apic_printk(APIC_VERBOSE, " not connected.\n"); ++} ++ ++/* ++ * Set up the 8259A-master output pin: ++ */ ++#ifndef CONFIG_XEN ++static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, int vector) ++{ ++ struct IO_APIC_route_entry entry; ++ unsigned long flags; ++ ++ memset(&entry,0,sizeof(entry)); ++ ++ disable_8259A_irq(0); ++ ++ /* mask LVT0 */ ++ apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); ++ ++ /* ++ * We use logical delivery to get the timer IRQ ++ * to the first CPU. ++ */ ++ entry.dest_mode = INT_DEST_MODE; ++ entry.mask = 0; /* unmask IRQ now */ ++ entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); ++ entry.delivery_mode = INT_DELIVERY_MODE; ++ entry.polarity = 0; ++ entry.trigger = 0; ++ entry.vector = vector; ++ ++ /* ++ * The timer IRQ doesn't have to know that behind the ++ * scene we have a 8259A-master in AEOI mode ... ++ */ ++ irq_desc[0].chip = &ioapic_edge_type; ++ ++ /* ++ * Add it to the IO-APIC irq-routing table: ++ */ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); ++ io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ enable_8259A_irq(0); ++} ++ ++static inline void UNEXPECTED_IO_APIC(void) ++{ ++} ++ ++void __init print_IO_APIC(void) ++{ ++ int apic, i; ++ union IO_APIC_reg_00 reg_00; ++ union IO_APIC_reg_01 reg_01; ++ union IO_APIC_reg_02 reg_02; ++ union IO_APIC_reg_03 reg_03; ++ unsigned long flags; ++ ++ if (apic_verbosity == APIC_QUIET) ++ return; ++ ++ printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries); ++ for (i = 0; i < nr_ioapics; i++) ++ printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n", ++ mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]); ++ ++ /* ++ * We are a bit conservative about what we expect. We have to ++ * know about every hardware change ASAP. ++ */ ++ printk(KERN_INFO "testing the IO APIC.......................\n"); ++ ++ for (apic = 0; apic < nr_ioapics; apic++) { ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ reg_00.raw = io_apic_read(apic, 0); ++ reg_01.raw = io_apic_read(apic, 1); ++ if (reg_01.bits.version >= 0x10) ++ reg_02.raw = io_apic_read(apic, 2); ++ if (reg_01.bits.version >= 0x20) ++ reg_03.raw = io_apic_read(apic, 3); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mpc_apicid); ++ printk(KERN_DEBUG ".... register #00: %08X\n", reg_00.raw); ++ printk(KERN_DEBUG "....... : physical APIC id: %02X\n", reg_00.bits.ID); ++ printk(KERN_DEBUG "....... : Delivery Type: %X\n", reg_00.bits.delivery_type); ++ printk(KERN_DEBUG "....... : LTS : %X\n", reg_00.bits.LTS); ++ if (reg_00.bits.ID >= get_physical_broadcast()) ++ UNEXPECTED_IO_APIC(); ++ if (reg_00.bits.__reserved_1 || reg_00.bits.__reserved_2) ++ UNEXPECTED_IO_APIC(); ++ ++ printk(KERN_DEBUG ".... register #01: %08X\n", reg_01.raw); ++ printk(KERN_DEBUG "....... : max redirection entries: %04X\n", reg_01.bits.entries); ++ if ( (reg_01.bits.entries != 0x0f) && /* older (Neptune) boards */ ++ (reg_01.bits.entries != 0x17) && /* typical ISA+PCI boards */ ++ (reg_01.bits.entries != 0x1b) && /* Compaq Proliant boards */ ++ (reg_01.bits.entries != 0x1f) && /* dual Xeon boards */ ++ (reg_01.bits.entries != 0x22) && /* bigger Xeon boards */ ++ (reg_01.bits.entries != 0x2E) && ++ (reg_01.bits.entries != 0x3F) ++ ) ++ UNEXPECTED_IO_APIC(); ++ ++ printk(KERN_DEBUG "....... : PRQ implemented: %X\n", reg_01.bits.PRQ); ++ printk(KERN_DEBUG "....... : IO APIC version: %04X\n", reg_01.bits.version); ++ if ( (reg_01.bits.version != 0x01) && /* 82489DX IO-APICs */ ++ (reg_01.bits.version != 0x10) && /* oldest IO-APICs */ ++ (reg_01.bits.version != 0x11) && /* Pentium/Pro IO-APICs */ ++ (reg_01.bits.version != 0x13) && /* Xeon IO-APICs */ ++ (reg_01.bits.version != 0x20) /* Intel P64H (82806 AA) */ ++ ) ++ UNEXPECTED_IO_APIC(); ++ if (reg_01.bits.__reserved_1 || reg_01.bits.__reserved_2) ++ UNEXPECTED_IO_APIC(); ++ ++ /* ++ * Some Intel chipsets with IO APIC VERSION of 0x1? don't have reg_02, ++ * but the value of reg_02 is read as the previous read register ++ * value, so ignore it if reg_02 == reg_01. ++ */ ++ if (reg_01.bits.version >= 0x10 && reg_02.raw != reg_01.raw) { ++ printk(KERN_DEBUG ".... register #02: %08X\n", reg_02.raw); ++ printk(KERN_DEBUG "....... : arbitration: %02X\n", reg_02.bits.arbitration); ++ if (reg_02.bits.__reserved_1 || reg_02.bits.__reserved_2) ++ UNEXPECTED_IO_APIC(); ++ } ++ ++ /* ++ * Some Intel chipsets with IO APIC VERSION of 0x2? don't have reg_02 ++ * or reg_03, but the value of reg_0[23] is read as the previous read ++ * register value, so ignore it if reg_03 == reg_0[12]. ++ */ ++ if (reg_01.bits.version >= 0x20 && reg_03.raw != reg_02.raw && ++ reg_03.raw != reg_01.raw) { ++ printk(KERN_DEBUG ".... register #03: %08X\n", reg_03.raw); ++ printk(KERN_DEBUG "....... : Boot DT : %X\n", reg_03.bits.boot_DT); ++ if (reg_03.bits.__reserved_1) ++ UNEXPECTED_IO_APIC(); ++ } ++ ++ printk(KERN_DEBUG ".... IRQ redirection table:\n"); ++ ++ printk(KERN_DEBUG " NR Log Phy Mask Trig IRR Pol" ++ " Stat Dest Deli Vect: \n"); ++ ++ for (i = 0; i <= reg_01.bits.entries; i++) { ++ struct IO_APIC_route_entry entry; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ *(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2); ++ *(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ printk(KERN_DEBUG " %02x %03X %02X ", ++ i, ++ entry.dest.logical.logical_dest, ++ entry.dest.physical.physical_dest ++ ); ++ ++ printk("%1d %1d %1d %1d %1d %1d %1d %02X\n", ++ entry.mask, ++ entry.trigger, ++ entry.irr, ++ entry.polarity, ++ entry.delivery_status, ++ entry.dest_mode, ++ entry.delivery_mode, ++ entry.vector ++ ); ++ } ++ } ++ if (use_pci_vector()) ++ printk(KERN_INFO "Using vector-based indexing\n"); ++ printk(KERN_DEBUG "IRQ to pin mappings:\n"); ++ for (i = 0; i < NR_IRQS; i++) { ++ struct irq_pin_list *entry = irq_2_pin + i; ++ if (entry->pin < 0) ++ continue; ++ if (use_pci_vector() && !platform_legacy_irq(i)) ++ printk(KERN_DEBUG "IRQ%d ", IO_APIC_VECTOR(i)); ++ else ++ printk(KERN_DEBUG "IRQ%d ", i); ++ for (;;) { ++ printk("-> %d:%d", entry->apic, entry->pin); ++ if (!entry->next) ++ break; ++ entry = irq_2_pin + entry->next; ++ } ++ printk("\n"); ++ } ++ ++ printk(KERN_INFO ".................................... done.\n"); ++ ++ return; ++} ++ ++static void print_APIC_bitfield (int base) ++{ ++ unsigned int v; ++ int i, j; ++ ++ if (apic_verbosity == APIC_QUIET) ++ return; ++ ++ printk(KERN_DEBUG "0123456789abcdef0123456789abcdef\n" KERN_DEBUG); ++ for (i = 0; i < 8; i++) { ++ v = apic_read(base + i*0x10); ++ for (j = 0; j < 32; j++) { ++ if (v & (1< 3) /* Due to the Pentium erratum 3AP. */ ++ apic_write(APIC_ESR, 0); ++ v = apic_read(APIC_ESR); ++ printk(KERN_DEBUG "... APIC ESR: %08x\n", v); ++ } ++ ++ v = apic_read(APIC_ICR); ++ printk(KERN_DEBUG "... APIC ICR: %08x\n", v); ++ v = apic_read(APIC_ICR2); ++ printk(KERN_DEBUG "... APIC ICR2: %08x\n", v); ++ ++ v = apic_read(APIC_LVTT); ++ printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); ++ ++ if (maxlvt > 3) { /* PC is LVT#4. */ ++ v = apic_read(APIC_LVTPC); ++ printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v); ++ } ++ v = apic_read(APIC_LVT0); ++ printk(KERN_DEBUG "... APIC LVT0: %08x\n", v); ++ v = apic_read(APIC_LVT1); ++ printk(KERN_DEBUG "... APIC LVT1: %08x\n", v); ++ ++ if (maxlvt > 2) { /* ERR is LVT#3. */ ++ v = apic_read(APIC_LVTERR); ++ printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v); ++ } ++ ++ v = apic_read(APIC_TMICT); ++ printk(KERN_DEBUG "... APIC TMICT: %08x\n", v); ++ v = apic_read(APIC_TMCCT); ++ printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v); ++ v = apic_read(APIC_TDCR); ++ printk(KERN_DEBUG "... APIC TDCR: %08x\n", v); ++ printk("\n"); ++} ++ ++void print_all_local_APICs (void) ++{ ++ on_each_cpu(print_local_APIC, NULL, 1, 1); ++} ++ ++void /*__init*/ print_PIC(void) ++{ ++ unsigned int v; ++ unsigned long flags; ++ ++ if (apic_verbosity == APIC_QUIET) ++ return; ++ ++ printk(KERN_DEBUG "\nprinting PIC contents\n"); ++ ++ spin_lock_irqsave(&i8259A_lock, flags); ++ ++ v = inb(0xa1) << 8 | inb(0x21); ++ printk(KERN_DEBUG "... PIC IMR: %04x\n", v); ++ ++ v = inb(0xa0) << 8 | inb(0x20); ++ printk(KERN_DEBUG "... PIC IRR: %04x\n", v); ++ ++ outb(0x0b,0xa0); ++ outb(0x0b,0x20); ++ v = inb(0xa0) << 8 | inb(0x20); ++ outb(0x0a,0xa0); ++ outb(0x0a,0x20); ++ ++ spin_unlock_irqrestore(&i8259A_lock, flags); ++ ++ printk(KERN_DEBUG "... PIC ISR: %04x\n", v); ++ ++ v = inb(0x4d1) << 8 | inb(0x4d0); ++ printk(KERN_DEBUG "... PIC ELCR: %04x\n", v); ++} ++#endif /* !CONFIG_XEN */ ++ ++static void __init enable_IO_APIC(void) ++{ ++ union IO_APIC_reg_01 reg_01; ++#ifndef CONFIG_XEN ++ int i8259_apic, i8259_pin; ++#endif ++ int i, apic; ++ unsigned long flags; ++ ++ for (i = 0; i < PIN_MAP_SIZE; i++) { ++ irq_2_pin[i].pin = -1; ++ irq_2_pin[i].next = 0; ++ } ++ if (!pirqs_enabled) ++ for (i = 0; i < MAX_PIRQS; i++) ++ pirq_entries[i] = -1; ++ ++ /* ++ * The number of IO-APIC IRQ registers (== #pins): ++ */ ++ for (apic = 0; apic < nr_ioapics; apic++) { ++ spin_lock_irqsave(&ioapic_lock, flags); ++ reg_01.raw = io_apic_read(apic, 1); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ nr_ioapic_registers[apic] = reg_01.bits.entries+1; ++ } ++#ifndef CONFIG_XEN ++ for(apic = 0; apic < nr_ioapics; apic++) { ++ int pin; ++ /* See if any of the pins is in ExtINT mode */ ++ for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { ++ struct IO_APIC_route_entry entry; ++ spin_lock_irqsave(&ioapic_lock, flags); ++ *(((int *)&entry) + 0) = io_apic_read(apic, 0x10 + 2 * pin); ++ *(((int *)&entry) + 1) = io_apic_read(apic, 0x11 + 2 * pin); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ ++ /* If the interrupt line is enabled and in ExtInt mode ++ * I have found the pin where the i8259 is connected. ++ */ ++ if ((entry.mask == 0) && (entry.delivery_mode == dest_ExtINT)) { ++ ioapic_i8259.apic = apic; ++ ioapic_i8259.pin = pin; ++ goto found_i8259; ++ } ++ } ++ } ++ found_i8259: ++ /* Look to see what if the MP table has reported the ExtINT */ ++ /* If we could not find the appropriate pin by looking at the ioapic ++ * the i8259 probably is not connected the ioapic but give the ++ * mptable a chance anyway. ++ */ ++ i8259_pin = find_isa_irq_pin(0, mp_ExtINT); ++ i8259_apic = find_isa_irq_apic(0, mp_ExtINT); ++ /* Trust the MP table if nothing is setup in the hardware */ ++ if ((ioapic_i8259.pin == -1) && (i8259_pin >= 0)) { ++ printk(KERN_WARNING "ExtINT not setup in hardware but reported by MP table\n"); ++ ioapic_i8259.pin = i8259_pin; ++ ioapic_i8259.apic = i8259_apic; ++ } ++ /* Complain if the MP table and the hardware disagree */ ++ if (((ioapic_i8259.apic != i8259_apic) || (ioapic_i8259.pin != i8259_pin)) && ++ (i8259_pin >= 0) && (ioapic_i8259.pin >= 0)) ++ { ++ printk(KERN_WARNING "ExtINT in hardware and MP table differ\n"); ++ } ++#endif ++ ++ /* ++ * Do not trust the IO-APIC being empty at bootup ++ */ ++ clear_IO_APIC(); ++} ++ ++/* ++ * Not an __init, needed by the reboot code ++ */ ++void disable_IO_APIC(void) ++{ ++ /* ++ * Clear the IO-APIC before rebooting: ++ */ ++ clear_IO_APIC(); ++ ++#ifndef CONFIG_XEN ++ /* ++ * If the i8259 is routed through an IOAPIC ++ * Put that IOAPIC in virtual wire mode ++ * so legacy interrupts can be delivered. ++ */ ++ if (ioapic_i8259.pin != -1) { ++ struct IO_APIC_route_entry entry; ++ unsigned long flags; ++ ++ memset(&entry, 0, sizeof(entry)); ++ entry.mask = 0; /* Enabled */ ++ entry.trigger = 0; /* Edge */ ++ entry.irr = 0; ++ entry.polarity = 0; /* High */ ++ entry.delivery_status = 0; ++ entry.dest_mode = 0; /* Physical */ ++ entry.delivery_mode = dest_ExtINT; /* ExtInt */ ++ entry.vector = 0; ++ entry.dest.physical.physical_dest = ++ GET_APIC_ID(apic_read(APIC_ID)); ++ ++ /* ++ * Add it to the IO-APIC irq-routing table: ++ */ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(ioapic_i8259.apic, 0x11+2*ioapic_i8259.pin, ++ *(((int *)&entry)+1)); ++ io_apic_write(ioapic_i8259.apic, 0x10+2*ioapic_i8259.pin, ++ *(((int *)&entry)+0)); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ } ++ disconnect_bsp_APIC(ioapic_i8259.pin != -1); ++#endif ++} ++ ++/* ++ * function to set the IO-APIC physical IDs based on the ++ * values stored in the MPC table. ++ * ++ * by Matt Domsch Tue Dec 21 12:25:05 CST 1999 ++ */ ++ ++#if !defined(CONFIG_XEN) && !defined(CONFIG_X86_NUMAQ) ++static void __init setup_ioapic_ids_from_mpc(void) ++{ ++ union IO_APIC_reg_00 reg_00; ++ physid_mask_t phys_id_present_map; ++ int apic; ++ int i; ++ unsigned char old_id; ++ unsigned long flags; ++ ++ /* ++ * Don't check I/O APIC IDs for xAPIC systems. They have ++ * no meaning without the serial APIC bus. ++ */ ++ if (!(boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) ++ || APIC_XAPIC(apic_version[boot_cpu_physical_apicid])) ++ return; ++ /* ++ * This is broken; anything with a real cpu count has to ++ * circumvent this idiocy regardless. ++ */ ++ phys_id_present_map = ioapic_phys_id_map(phys_cpu_present_map); ++ ++ /* ++ * Set the IOAPIC ID to the value stored in the MPC table. ++ */ ++ for (apic = 0; apic < nr_ioapics; apic++) { ++ ++ /* Read the register 0 value */ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ reg_00.raw = io_apic_read(apic, 0); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ old_id = mp_ioapics[apic].mpc_apicid; ++ ++ if (mp_ioapics[apic].mpc_apicid >= get_physical_broadcast()) { ++ printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n", ++ apic, mp_ioapics[apic].mpc_apicid); ++ printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", ++ reg_00.bits.ID); ++ mp_ioapics[apic].mpc_apicid = reg_00.bits.ID; ++ } ++ ++ /* ++ * Sanity check, is the ID really free? Every APIC in a ++ * system must have a unique ID or we get lots of nice ++ * 'stuck on smp_invalidate_needed IPI wait' messages. ++ */ ++ if (check_apicid_used(phys_id_present_map, ++ mp_ioapics[apic].mpc_apicid)) { ++ printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n", ++ apic, mp_ioapics[apic].mpc_apicid); ++ for (i = 0; i < get_physical_broadcast(); i++) ++ if (!physid_isset(i, phys_id_present_map)) ++ break; ++ if (i >= get_physical_broadcast()) ++ panic("Max APIC ID exceeded!\n"); ++ printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", ++ i); ++ physid_set(i, phys_id_present_map); ++ mp_ioapics[apic].mpc_apicid = i; ++ } else { ++ physid_mask_t tmp; ++ tmp = apicid_to_cpu_present(mp_ioapics[apic].mpc_apicid); ++ apic_printk(APIC_VERBOSE, "Setting %d in the " ++ "phys_id_present_map\n", ++ mp_ioapics[apic].mpc_apicid); ++ physids_or(phys_id_present_map, phys_id_present_map, tmp); ++ } ++ ++ ++ /* ++ * We need to adjust the IRQ routing table ++ * if the ID changed. ++ */ ++ if (old_id != mp_ioapics[apic].mpc_apicid) ++ for (i = 0; i < mp_irq_entries; i++) ++ if (mp_irqs[i].mpc_dstapic == old_id) ++ mp_irqs[i].mpc_dstapic ++ = mp_ioapics[apic].mpc_apicid; ++ ++ /* ++ * Read the right value from the MPC table and ++ * write it into the ID register. ++ */ ++ apic_printk(APIC_VERBOSE, KERN_INFO ++ "...changing IO-APIC physical APIC ID to %d ...", ++ mp_ioapics[apic].mpc_apicid); ++ ++ reg_00.bits.ID = mp_ioapics[apic].mpc_apicid; ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(apic, 0, reg_00.raw); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ /* ++ * Sanity check ++ */ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ reg_00.raw = io_apic_read(apic, 0); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ if (reg_00.bits.ID != mp_ioapics[apic].mpc_apicid) ++ printk("could not set ID!\n"); ++ else ++ apic_printk(APIC_VERBOSE, " ok.\n"); ++ } ++} ++#else ++static void __init setup_ioapic_ids_from_mpc(void) { } ++#endif ++ ++#ifndef CONFIG_XEN ++/* ++ * There is a nasty bug in some older SMP boards, their mptable lies ++ * about the timer IRQ. We do the following to work around the situation: ++ * ++ * - timer IRQ defaults to IO-APIC IRQ ++ * - if this function detects that timer IRQs are defunct, then we fall ++ * back to ISA timer IRQs ++ */ ++static int __init timer_irq_works(void) ++{ ++ unsigned long t1 = jiffies; ++ ++ local_irq_enable(); ++ /* Let ten ticks pass... */ ++ mdelay((10 * 1000) / HZ); ++ ++ /* ++ * Expect a few ticks at least, to be sure some possible ++ * glue logic does not lock up after one or two first ++ * ticks in a non-ExtINT mode. Also the local APIC ++ * might have cached one ExtINT interrupt. Finally, at ++ * least one tick may be lost due to delays. ++ */ ++ if (jiffies - t1 > 4) ++ return 1; ++ ++ return 0; ++} ++ ++/* ++ * In the SMP+IOAPIC case it might happen that there are an unspecified ++ * number of pending IRQ events unhandled. These cases are very rare, ++ * so we 'resend' these IRQs via IPIs, to the same CPU. It's much ++ * better to do it this way as thus we do not have to be aware of ++ * 'pending' interrupts in the IRQ path, except at this point. ++ */ ++/* ++ * Edge triggered needs to resend any interrupt ++ * that was delayed but this is now handled in the device ++ * independent code. ++ */ ++ ++/* ++ * Starting up a edge-triggered IO-APIC interrupt is ++ * nasty - we need to make sure that we get the edge. ++ * If it is already asserted for some reason, we need ++ * return 1 to indicate that is was pending. ++ * ++ * This is not complete - we should be able to fake ++ * an edge even if it isn't on the 8259A... ++ */ ++static unsigned int startup_edge_ioapic_irq(unsigned int irq) ++{ ++ int was_pending = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ if (irq < 16) { ++ disable_8259A_irq(irq); ++ if (i8259A_irq_pending(irq)) ++ was_pending = 1; ++ } ++ __unmask_IO_APIC_irq(irq); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ return was_pending; ++} ++ ++/* ++ * Once we have recorded IRQ_PENDING already, we can mask the ++ * interrupt for real. This prevents IRQ storms from unhandled ++ * devices. ++ */ ++static void ack_edge_ioapic_irq(unsigned int irq) ++{ ++ move_irq(irq); ++ if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) ++ == (IRQ_PENDING | IRQ_DISABLED)) ++ mask_IO_APIC_irq(irq); ++ ack_APIC_irq(); ++} ++ ++/* ++ * Level triggered interrupts can just be masked, ++ * and shutting down and starting up the interrupt ++ * is the same as enabling and disabling them -- except ++ * with a startup need to return a "was pending" value. ++ * ++ * Level triggered interrupts are special because we ++ * do not touch any IO-APIC register while handling ++ * them. We ack the APIC in the end-IRQ handler, not ++ * in the start-IRQ-handler. Protection against reentrance ++ * from the same interrupt is still provided, both by the ++ * generic IRQ layer and by the fact that an unacked local ++ * APIC does not accept IRQs. ++ */ ++static unsigned int startup_level_ioapic_irq (unsigned int irq) ++{ ++ unmask_IO_APIC_irq(irq); ++ ++ return 0; /* don't check for pending */ ++} ++ ++static void end_level_ioapic_irq (unsigned int irq) ++{ ++ unsigned long v; ++ int i; ++ ++ move_irq(irq); ++/* ++ * It appears there is an erratum which affects at least version 0x11 ++ * of I/O APIC (that's the 82093AA and cores integrated into various ++ * chipsets). Under certain conditions a level-triggered interrupt is ++ * erroneously delivered as edge-triggered one but the respective IRR ++ * bit gets set nevertheless. As a result the I/O unit expects an EOI ++ * message but it will never arrive and further interrupts are blocked ++ * from the source. The exact reason is so far unknown, but the ++ * phenomenon was observed when two consecutive interrupt requests ++ * from a given source get delivered to the same CPU and the source is ++ * temporarily disabled in between. ++ * ++ * A workaround is to simulate an EOI message manually. We achieve it ++ * by setting the trigger mode to edge and then to level when the edge ++ * trigger mode gets detected in the TMR of a local APIC for a ++ * level-triggered interrupt. We mask the source for the time of the ++ * operation to prevent an edge-triggered interrupt escaping meanwhile. ++ * The idea is from Manfred Spraul. --macro ++ */ ++ i = IO_APIC_VECTOR(irq); ++ ++ v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1)); ++ ++ ack_APIC_irq(); ++ ++ if (!(v & (1 << (i & 0x1f)))) { ++ atomic_inc(&irq_mis_count); ++ spin_lock(&ioapic_lock); ++ __mask_and_edge_IO_APIC_irq(irq); ++ __unmask_and_level_IO_APIC_irq(irq); ++ spin_unlock(&ioapic_lock); ++ } ++} ++ ++#ifdef CONFIG_PCI_MSI ++static unsigned int startup_edge_ioapic_vector(unsigned int vector) ++{ ++ int irq = vector_to_irq(vector); ++ ++ return startup_edge_ioapic_irq(irq); ++} ++ ++static void ack_edge_ioapic_vector(unsigned int vector) ++{ ++ int irq = vector_to_irq(vector); ++ ++ move_native_irq(vector); ++ ack_edge_ioapic_irq(irq); ++} ++ ++static unsigned int startup_level_ioapic_vector (unsigned int vector) ++{ ++ int irq = vector_to_irq(vector); ++ ++ return startup_level_ioapic_irq (irq); ++} ++ ++static void end_level_ioapic_vector (unsigned int vector) ++{ ++ int irq = vector_to_irq(vector); ++ ++ move_native_irq(vector); ++ end_level_ioapic_irq(irq); ++} ++ ++static void mask_IO_APIC_vector (unsigned int vector) ++{ ++ int irq = vector_to_irq(vector); ++ ++ mask_IO_APIC_irq(irq); ++} ++ ++static void unmask_IO_APIC_vector (unsigned int vector) ++{ ++ int irq = vector_to_irq(vector); ++ ++ unmask_IO_APIC_irq(irq); ++} ++ ++#ifdef CONFIG_SMP ++static void set_ioapic_affinity_vector (unsigned int vector, ++ cpumask_t cpu_mask) ++{ ++ int irq = vector_to_irq(vector); ++ ++ set_native_irq_info(vector, cpu_mask); ++ set_ioapic_affinity_irq(irq, cpu_mask); ++} ++#endif ++#endif ++ ++static int ioapic_retrigger(unsigned int irq) ++{ ++ send_IPI_self(IO_APIC_VECTOR(irq)); ++ ++ return 1; ++} ++ ++/* ++ * Level and edge triggered IO-APIC interrupts need different handling, ++ * so we use two separate IRQ descriptors. Edge triggered IRQs can be ++ * handled with the level-triggered descriptor, but that one has slightly ++ * more overhead. Level-triggered interrupts cannot be handled with the ++ * edge-triggered handler, without risking IRQ storms and other ugly ++ * races. ++ */ ++static struct hw_interrupt_type ioapic_edge_type __read_mostly = { ++ .typename = "IO-APIC-edge", ++ .startup = startup_edge_ioapic, ++ .shutdown = shutdown_edge_ioapic, ++ .enable = enable_edge_ioapic, ++ .disable = disable_edge_ioapic, ++ .ack = ack_edge_ioapic, ++ .end = end_edge_ioapic, ++#ifdef CONFIG_SMP ++ .set_affinity = set_ioapic_affinity, ++#endif ++ .retrigger = ioapic_retrigger, ++}; ++ ++static struct hw_interrupt_type ioapic_level_type __read_mostly = { ++ .typename = "IO-APIC-level", ++ .startup = startup_level_ioapic, ++ .shutdown = shutdown_level_ioapic, ++ .enable = enable_level_ioapic, ++ .disable = disable_level_ioapic, ++ .ack = mask_and_ack_level_ioapic, ++ .end = end_level_ioapic, ++#ifdef CONFIG_SMP ++ .set_affinity = set_ioapic_affinity, ++#endif ++ .retrigger = ioapic_retrigger, ++}; ++#endif /* !CONFIG_XEN */ ++ ++static inline void init_IO_APIC_traps(void) ++{ ++ int irq; ++ ++ /* ++ * NOTE! The local APIC isn't very good at handling ++ * multiple interrupts at the same interrupt level. ++ * As the interrupt level is determined by taking the ++ * vector number and shifting that right by 4, we ++ * want to spread these out a bit so that they don't ++ * all fall in the same interrupt level. ++ * ++ * Also, we've got to be careful not to trash gate ++ * 0x80, because int 0x80 is hm, kind of importantish. ;) ++ */ ++ for (irq = 0; irq < NR_IRQS ; irq++) { ++ int tmp = irq; ++ if (use_pci_vector()) { ++ if (!platform_legacy_irq(tmp)) ++ if ((tmp = vector_to_irq(tmp)) == -1) ++ continue; ++ } ++ if (IO_APIC_IRQ(tmp) && !IO_APIC_VECTOR(tmp)) { ++ /* ++ * Hmm.. We don't have an entry for this, ++ * so default to an old-fashioned 8259 ++ * interrupt if we can.. ++ */ ++ if (irq < 16) ++ make_8259A_irq(irq); ++#ifndef CONFIG_XEN ++ else ++ /* Strange. Oh, well.. */ ++ irq_desc[irq].chip = &no_irq_type; ++#endif ++ } ++ } ++} ++ ++#ifndef CONFIG_XEN ++static void enable_lapic_irq (unsigned int irq) ++{ ++ unsigned long v; ++ ++ v = apic_read(APIC_LVT0); ++ apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED); ++} ++ ++static void disable_lapic_irq (unsigned int irq) ++{ ++ unsigned long v; ++ ++ v = apic_read(APIC_LVT0); ++ apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); ++} ++ ++static void ack_lapic_irq (unsigned int irq) ++{ ++ ack_APIC_irq(); ++} ++ ++static void end_lapic_irq (unsigned int i) { /* nothing */ } ++ ++static struct hw_interrupt_type lapic_irq_type __read_mostly = { ++ .typename = "local-APIC-edge", ++ .startup = NULL, /* startup_irq() not used for IRQ0 */ ++ .shutdown = NULL, /* shutdown_irq() not used for IRQ0 */ ++ .enable = enable_lapic_irq, ++ .disable = disable_lapic_irq, ++ .ack = ack_lapic_irq, ++ .end = end_lapic_irq ++}; ++ ++static void setup_nmi (void) ++{ ++ /* ++ * Dirty trick to enable the NMI watchdog ... ++ * We put the 8259A master into AEOI mode and ++ * unmask on all local APICs LVT0 as NMI. ++ * ++ * The idea to use the 8259A in AEOI mode ('8259A Virtual Wire') ++ * is from Maciej W. Rozycki - so we do not have to EOI from ++ * the NMI handler or the timer interrupt. ++ */ ++ apic_printk(APIC_VERBOSE, KERN_INFO "activating NMI Watchdog ..."); ++ ++ on_each_cpu(enable_NMI_through_LVT0, NULL, 1, 1); ++ ++ apic_printk(APIC_VERBOSE, " done.\n"); ++} ++ ++/* ++ * This looks a bit hackish but it's about the only one way of sending ++ * a few INTA cycles to 8259As and any associated glue logic. ICR does ++ * not support the ExtINT mode, unfortunately. We need to send these ++ * cycles as some i82489DX-based boards have glue logic that keeps the ++ * 8259A interrupt line asserted until INTA. --macro ++ */ ++static inline void unlock_ExtINT_logic(void) ++{ ++ int apic, pin, i; ++ struct IO_APIC_route_entry entry0, entry1; ++ unsigned char save_control, save_freq_select; ++ unsigned long flags; ++ ++ pin = find_isa_irq_pin(8, mp_INT); ++ apic = find_isa_irq_apic(8, mp_INT); ++ if (pin == -1) ++ return; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ *(((int *)&entry0) + 1) = io_apic_read(apic, 0x11 + 2 * pin); ++ *(((int *)&entry0) + 0) = io_apic_read(apic, 0x10 + 2 * pin); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ clear_IO_APIC_pin(apic, pin); ++ ++ memset(&entry1, 0, sizeof(entry1)); ++ ++ entry1.dest_mode = 0; /* physical delivery */ ++ entry1.mask = 0; /* unmask IRQ now */ ++ entry1.dest.physical.physical_dest = hard_smp_processor_id(); ++ entry1.delivery_mode = dest_ExtINT; ++ entry1.polarity = entry0.polarity; ++ entry1.trigger = 0; ++ entry1.vector = 0; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); ++ io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ save_control = CMOS_READ(RTC_CONTROL); ++ save_freq_select = CMOS_READ(RTC_FREQ_SELECT); ++ CMOS_WRITE((save_freq_select & ~RTC_RATE_SELECT) | 0x6, ++ RTC_FREQ_SELECT); ++ CMOS_WRITE(save_control | RTC_PIE, RTC_CONTROL); ++ ++ i = 100; ++ while (i-- > 0) { ++ mdelay(10); ++ if ((CMOS_READ(RTC_INTR_FLAGS) & RTC_PF) == RTC_PF) ++ i -= 10; ++ } ++ ++ CMOS_WRITE(save_control, RTC_CONTROL); ++ CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); ++ clear_IO_APIC_pin(apic, pin); ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); ++ io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++} ++ ++int timer_uses_ioapic_pin_0; ++ ++/* ++ * This code may look a bit paranoid, but it's supposed to cooperate with ++ * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ ++ * is so screwy. Thanks to Brian Perkins for testing/hacking this beast ++ * fanatically on his truly buggy board. ++ */ ++static inline void check_timer(void) ++{ ++ int apic1, pin1, apic2, pin2; ++ int vector; ++ ++ /* ++ * get/set the timer IRQ vector: ++ */ ++ disable_8259A_irq(0); ++ vector = assign_irq_vector(0); ++ set_intr_gate(vector, interrupt[0]); ++ ++ /* ++ * Subtle, code in do_timer_interrupt() expects an AEOI ++ * mode for the 8259A whenever interrupts are routed ++ * through I/O APICs. Also IRQ0 has to be enabled in ++ * the 8259A which implies the virtual wire has to be ++ * disabled in the local APIC. ++ */ ++ apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); ++ init_8259A(1); ++ timer_ack = 1; ++ if (timer_over_8254 > 0) ++ enable_8259A_irq(0); ++ ++ pin1 = find_isa_irq_pin(0, mp_INT); ++ apic1 = find_isa_irq_apic(0, mp_INT); ++ pin2 = ioapic_i8259.pin; ++ apic2 = ioapic_i8259.apic; ++ ++ if (pin1 == 0) ++ timer_uses_ioapic_pin_0 = 1; ++ ++ printk(KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", ++ vector, apic1, pin1, apic2, pin2); ++ ++ if (pin1 != -1) { ++ /* ++ * Ok, does IRQ0 through the IOAPIC work? ++ */ ++ unmask_IO_APIC_irq(0); ++ if (timer_irq_works()) { ++ if (nmi_watchdog == NMI_IO_APIC) { ++ disable_8259A_irq(0); ++ setup_nmi(); ++ enable_8259A_irq(0); ++ } ++ if (disable_timer_pin_1 > 0) ++ clear_IO_APIC_pin(0, pin1); ++ return; ++ } ++ clear_IO_APIC_pin(apic1, pin1); ++ printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to " ++ "IO-APIC\n"); ++ } ++ ++ printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); ++ if (pin2 != -1) { ++ printk("\n..... (found pin %d) ...", pin2); ++ /* ++ * legacy devices should be connected to IO APIC #0 ++ */ ++ setup_ExtINT_IRQ0_pin(apic2, pin2, vector); ++ if (timer_irq_works()) { ++ printk("works.\n"); ++ if (pin1 != -1) ++ replace_pin_at_irq(0, apic1, pin1, apic2, pin2); ++ else ++ add_pin_to_irq(0, apic2, pin2); ++ if (nmi_watchdog == NMI_IO_APIC) { ++ setup_nmi(); ++ } ++ return; ++ } ++ /* ++ * Cleanup, just in case ... ++ */ ++ clear_IO_APIC_pin(apic2, pin2); ++ } ++ printk(" failed.\n"); ++ ++ if (nmi_watchdog == NMI_IO_APIC) { ++ printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); ++ nmi_watchdog = 0; ++ } ++ ++ printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); ++ ++ disable_8259A_irq(0); ++ irq_desc[0].chip = &lapic_irq_type; ++ apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */ ++ enable_8259A_irq(0); ++ ++ if (timer_irq_works()) { ++ printk(" works.\n"); ++ return; ++ } ++ apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector); ++ printk(" failed.\n"); ++ ++ printk(KERN_INFO "...trying to set up timer as ExtINT IRQ..."); ++ ++ timer_ack = 0; ++ init_8259A(0); ++ make_8259A_irq(0); ++ apic_write_around(APIC_LVT0, APIC_DM_EXTINT); ++ ++ unlock_ExtINT_logic(); ++ ++ if (timer_irq_works()) { ++ printk(" works.\n"); ++ return; ++ } ++ printk(" failed :(.\n"); ++ panic("IO-APIC + timer doesn't work! Boot with apic=debug and send a " ++ "report. Then try booting with the 'noapic' option"); ++} ++#else ++int timer_uses_ioapic_pin_0 = 0; ++#define check_timer() ((void)0) ++#endif ++ ++/* ++ * ++ * IRQ's that are handled by the PIC in the MPS IOAPIC case. ++ * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. ++ * Linux doesn't really care, as it's not actually used ++ * for any interrupt handling anyway. ++ */ ++#define PIC_IRQS (1 << PIC_CASCADE_IR) ++ ++void __init setup_IO_APIC(void) ++{ ++ enable_IO_APIC(); ++ ++ if (acpi_ioapic) ++ io_apic_irqs = ~0; /* all IRQs go through IOAPIC */ ++ else ++ io_apic_irqs = ~PIC_IRQS; ++ ++ printk("ENABLING IO-APIC IRQs\n"); ++ ++ /* ++ * Set up IO-APIC IRQ routing. ++ */ ++ if (!acpi_ioapic) ++ setup_ioapic_ids_from_mpc(); ++#ifndef CONFIG_XEN ++ sync_Arb_IDs(); ++#endif ++ setup_IO_APIC_irqs(); ++ init_IO_APIC_traps(); ++ check_timer(); ++ if (!acpi_ioapic) ++ print_IO_APIC(); ++} ++ ++static int __init setup_disable_8254_timer(char *s) ++{ ++ timer_over_8254 = -1; ++ return 1; ++} ++static int __init setup_enable_8254_timer(char *s) ++{ ++ timer_over_8254 = 2; ++ return 1; ++} ++ ++__setup("disable_8254_timer", setup_disable_8254_timer); ++__setup("enable_8254_timer", setup_enable_8254_timer); ++ ++/* ++ * Called after all the initialization is done. If we didnt find any ++ * APIC bugs then we can allow the modify fast path ++ */ ++ ++static int __init io_apic_bug_finalize(void) ++{ ++ if(sis_apic_bug == -1) ++ sis_apic_bug = 0; ++ if (is_initial_xendomain()) { ++ struct xen_platform_op op = { .cmd = XENPF_platform_quirk }; ++ op.u.platform_quirk.quirk_id = sis_apic_bug ? ++ QUIRK_IOAPIC_BAD_REGSEL : QUIRK_IOAPIC_GOOD_REGSEL; ++ VOID(HYPERVISOR_platform_op(&op)); ++ } ++ return 0; ++} ++ ++late_initcall(io_apic_bug_finalize); ++ ++#ifndef CONFIG_XEN ++ ++struct sysfs_ioapic_data { ++ struct sys_device dev; ++ struct IO_APIC_route_entry entry[0]; ++}; ++static struct sysfs_ioapic_data * mp_ioapic_data[MAX_IO_APICS]; ++ ++static int ioapic_suspend(struct sys_device *dev, pm_message_t state) ++{ ++ struct IO_APIC_route_entry *entry; ++ struct sysfs_ioapic_data *data; ++ unsigned long flags; ++ int i; ++ ++ data = container_of(dev, struct sysfs_ioapic_data, dev); ++ entry = data->entry; ++ spin_lock_irqsave(&ioapic_lock, flags); ++ for (i = 0; i < nr_ioapic_registers[dev->id]; i ++, entry ++ ) { ++ *(((int *)entry) + 1) = io_apic_read(dev->id, 0x11 + 2 * i); ++ *(((int *)entry) + 0) = io_apic_read(dev->id, 0x10 + 2 * i); ++ } ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ return 0; ++} ++ ++static int ioapic_resume(struct sys_device *dev) ++{ ++ struct IO_APIC_route_entry *entry; ++ struct sysfs_ioapic_data *data; ++ unsigned long flags; ++ union IO_APIC_reg_00 reg_00; ++ int i; ++ ++ data = container_of(dev, struct sysfs_ioapic_data, dev); ++ entry = data->entry; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ reg_00.raw = io_apic_read(dev->id, 0); ++ if (reg_00.bits.ID != mp_ioapics[dev->id].mpc_apicid) { ++ reg_00.bits.ID = mp_ioapics[dev->id].mpc_apicid; ++ io_apic_write(dev->id, 0, reg_00.raw); ++ } ++ for (i = 0; i < nr_ioapic_registers[dev->id]; i ++, entry ++ ) { ++ io_apic_write(dev->id, 0x11+2*i, *(((int *)entry)+1)); ++ io_apic_write(dev->id, 0x10+2*i, *(((int *)entry)+0)); ++ } ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ return 0; ++} ++ ++static struct sysdev_class ioapic_sysdev_class = { ++ set_kset_name("ioapic"), ++ .suspend = ioapic_suspend, ++ .resume = ioapic_resume, ++}; ++ ++static int __init ioapic_init_sysfs(void) ++{ ++ struct sys_device * dev; ++ int i, size, error = 0; ++ ++ error = sysdev_class_register(&ioapic_sysdev_class); ++ if (error) ++ return error; ++ ++ for (i = 0; i < nr_ioapics; i++ ) { ++ size = sizeof(struct sys_device) + nr_ioapic_registers[i] ++ * sizeof(struct IO_APIC_route_entry); ++ mp_ioapic_data[i] = kmalloc(size, GFP_KERNEL); ++ if (!mp_ioapic_data[i]) { ++ printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i); ++ continue; ++ } ++ memset(mp_ioapic_data[i], 0, size); ++ dev = &mp_ioapic_data[i]->dev; ++ dev->id = i; ++ dev->cls = &ioapic_sysdev_class; ++ error = sysdev_register(dev); ++ if (error) { ++ kfree(mp_ioapic_data[i]); ++ mp_ioapic_data[i] = NULL; ++ printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i); ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++device_initcall(ioapic_init_sysfs); ++ ++#endif /* CONFIG_XEN */ ++ ++/* -------------------------------------------------------------------------- ++ ACPI-based IOAPIC Configuration ++ -------------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_ACPI ++ ++int __init io_apic_get_unique_id (int ioapic, int apic_id) ++{ ++#ifndef CONFIG_XEN ++ union IO_APIC_reg_00 reg_00; ++ static physid_mask_t apic_id_map = PHYSID_MASK_NONE; ++ physid_mask_t tmp; ++ unsigned long flags; ++ int i = 0; ++ ++ /* ++ * The P4 platform supports up to 256 APIC IDs on two separate APIC ++ * buses (one for LAPICs, one for IOAPICs), where predecessors only ++ * supports up to 16 on one shared APIC bus. ++ * ++ * TBD: Expand LAPIC/IOAPIC support on P4-class systems to take full ++ * advantage of new APIC bus architecture. ++ */ ++ ++ if (physids_empty(apic_id_map)) ++ apic_id_map = ioapic_phys_id_map(phys_cpu_present_map); ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ reg_00.raw = io_apic_read(ioapic, 0); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ if (apic_id >= get_physical_broadcast()) { ++ printk(KERN_WARNING "IOAPIC[%d]: Invalid apic_id %d, trying " ++ "%d\n", ioapic, apic_id, reg_00.bits.ID); ++ apic_id = reg_00.bits.ID; ++ } ++ ++ /* ++ * Every APIC in a system must have a unique ID or we get lots of nice ++ * 'stuck on smp_invalidate_needed IPI wait' messages. ++ */ ++ if (check_apicid_used(apic_id_map, apic_id)) { ++ ++ for (i = 0; i < get_physical_broadcast(); i++) { ++ if (!check_apicid_used(apic_id_map, i)) ++ break; ++ } ++ ++ if (i == get_physical_broadcast()) ++ panic("Max apic_id exceeded!\n"); ++ ++ printk(KERN_WARNING "IOAPIC[%d]: apic_id %d already used, " ++ "trying %d\n", ioapic, apic_id, i); ++ ++ apic_id = i; ++ } ++ ++ tmp = apicid_to_cpu_present(apic_id); ++ physids_or(apic_id_map, apic_id_map, tmp); ++ ++ if (reg_00.bits.ID != apic_id) { ++ reg_00.bits.ID = apic_id; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(ioapic, 0, reg_00.raw); ++ reg_00.raw = io_apic_read(ioapic, 0); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ /* Sanity check */ ++ if (reg_00.bits.ID != apic_id) { ++ printk("IOAPIC[%d]: Unable to change apic_id!\n", ioapic); ++ return -1; ++ } ++ } ++ ++ apic_printk(APIC_VERBOSE, KERN_INFO ++ "IOAPIC[%d]: Assigned apic_id %d\n", ioapic, apic_id); ++#endif /* !CONFIG_XEN */ ++ ++ return apic_id; ++} ++ ++ ++int __init io_apic_get_version (int ioapic) ++{ ++ union IO_APIC_reg_01 reg_01; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ reg_01.raw = io_apic_read(ioapic, 1); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ return reg_01.bits.version; ++} ++ ++ ++int __init io_apic_get_redir_entries (int ioapic) ++{ ++ union IO_APIC_reg_01 reg_01; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ reg_01.raw = io_apic_read(ioapic, 1); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ return reg_01.bits.entries; ++} ++ ++ ++int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int active_high_low) ++{ ++ struct IO_APIC_route_entry entry; ++ unsigned long flags; ++ ++ if (!IO_APIC_IRQ(irq)) { ++ printk(KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n", ++ ioapic); ++ return -EINVAL; ++ } ++ ++ /* ++ * Generate a PCI IRQ routing entry and program the IOAPIC accordingly. ++ * Note that we mask (disable) IRQs now -- these get enabled when the ++ * corresponding device driver registers for this IRQ. ++ */ ++ ++ memset(&entry,0,sizeof(entry)); ++ ++ entry.delivery_mode = INT_DELIVERY_MODE; ++ entry.dest_mode = INT_DEST_MODE; ++ entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); ++ entry.trigger = edge_level; ++ entry.polarity = active_high_low; ++ entry.mask = 1; ++ ++ /* ++ * IRQs < 16 are already in the irq_2_pin[] map ++ */ ++ if (irq >= 16) ++ add_pin_to_irq(irq, ioapic, pin); ++ ++ entry.vector = assign_irq_vector(irq); ++ ++ apic_printk(APIC_DEBUG, KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry " ++ "(%d-%d -> 0x%x -> IRQ %d Mode:%i Active:%i)\n", ioapic, ++ mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq, ++ edge_level, active_high_low); ++ ++ ioapic_register_intr(irq, entry.vector, edge_level); ++ ++ if (!ioapic && (irq < 16)) ++ disable_8259A_irq(irq); ++ ++ spin_lock_irqsave(&ioapic_lock, flags); ++ io_apic_write(ioapic, 0x11+2*pin, *(((int *)&entry)+1)); ++ io_apic_write(ioapic, 0x10+2*pin, *(((int *)&entry)+0)); ++ set_native_irq_info(use_pci_vector() ? entry.vector : irq, TARGET_CPUS); ++ spin_unlock_irqrestore(&ioapic_lock, flags); ++ ++ return 0; ++} ++ ++#endif /* CONFIG_ACPI */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/ioport_32-xen.c 2008-01-28 12:24:19.000000000 +0100 +@@ -0,0 +1,123 @@ ++/* ++ * linux/arch/i386/kernel/ioport.c ++ * ++ * This contains the io-permission bitmap code - written by obz, with changes ++ * by Linus. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ ++static void set_bitmap(unsigned long *bitmap, unsigned int base, unsigned int extent, int new_value) ++{ ++ unsigned long mask; ++ unsigned long *bitmap_base = bitmap + (base / BITS_PER_LONG); ++ unsigned int low_index = base & (BITS_PER_LONG-1); ++ int length = low_index + extent; ++ ++ if (low_index != 0) { ++ mask = (~0UL << low_index); ++ if (length < BITS_PER_LONG) ++ mask &= ~(~0UL << length); ++ if (new_value) ++ *bitmap_base++ |= mask; ++ else ++ *bitmap_base++ &= ~mask; ++ length -= BITS_PER_LONG; ++ } ++ ++ mask = (new_value ? ~0UL : 0UL); ++ while (length >= BITS_PER_LONG) { ++ *bitmap_base++ = mask; ++ length -= BITS_PER_LONG; ++ } ++ ++ if (length > 0) { ++ mask = ~(~0UL << length); ++ if (new_value) ++ *bitmap_base++ |= mask; ++ else ++ *bitmap_base++ &= ~mask; ++ } ++} ++ ++ ++/* ++ * this changes the io permissions bitmap in the current task. ++ */ ++asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) ++{ ++ struct thread_struct * t = ¤t->thread; ++ unsigned long *bitmap; ++ struct physdev_set_iobitmap set_iobitmap; ++ ++ if ((from + num <= from) || (from + num > IO_BITMAP_BITS)) ++ return -EINVAL; ++ if (turn_on && !capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ ++ /* ++ * If it's the first ioperm() call in this thread's lifetime, set the ++ * IO bitmap up. ioperm() is much less timing critical than clone(), ++ * this is why we delay this operation until now: ++ */ ++ if (!t->io_bitmap_ptr) { ++ bitmap = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); ++ if (!bitmap) ++ return -ENOMEM; ++ ++ memset(bitmap, 0xff, IO_BITMAP_BYTES); ++ t->io_bitmap_ptr = bitmap; ++ set_thread_flag(TIF_IO_BITMAP); ++ ++ set_xen_guest_handle(set_iobitmap.bitmap, (char *)bitmap); ++ set_iobitmap.nr_ports = IO_BITMAP_BITS; ++ WARN_ON(HYPERVISOR_physdev_op(PHYSDEVOP_set_iobitmap, ++ &set_iobitmap)); ++ } ++ ++ set_bitmap(t->io_bitmap_ptr, from, num, !turn_on); ++ ++ return 0; ++} ++ ++/* ++ * sys_iopl has to be used when you want to access the IO ports ++ * beyond the 0x3ff range: to get the full 65536 ports bitmapped ++ * you'd need 8kB of bitmaps/process, which is a bit excessive. ++ * ++ * Here we just change the eflags value on the stack: we allow ++ * only the super-user to do it. This depends on the stack-layout ++ * on system-call entry - see also fork() and the signal handling ++ * code. ++ */ ++ ++asmlinkage long sys_iopl(unsigned long unused) ++{ ++ volatile struct pt_regs * regs = (struct pt_regs *) &unused; ++ unsigned int level = regs->ebx; ++ struct thread_struct *t = ¤t->thread; ++ unsigned int old = (t->iopl >> 12) & 3; ++ ++ if (level > 3) ++ return -EINVAL; ++ /* Trying to gain more privileges? */ ++ if (level > old) { ++ if (!capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ } ++ t->iopl = level << 12; ++ set_iopl_mask(t->iopl); ++ return 0; ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/ldt_32-xen.c 2007-06-12 13:12:48.000000000 +0200 +@@ -0,0 +1,270 @@ ++/* ++ * linux/kernel/ldt.c ++ * ++ * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds ++ * Copyright (C) 1999 Ingo Molnar ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_SMP /* avoids "defined but not used" warnig */ ++static void flush_ldt(void *null) ++{ ++ if (current->active_mm) ++ load_LDT(¤t->active_mm->context); ++} ++#endif ++ ++static int alloc_ldt(mm_context_t *pc, int mincount, int reload) ++{ ++ void *oldldt; ++ void *newldt; ++ int oldsize; ++ ++ if (mincount <= pc->size) ++ return 0; ++ oldsize = pc->size; ++ mincount = (mincount+511)&(~511); ++ if (mincount*LDT_ENTRY_SIZE > PAGE_SIZE) ++ newldt = vmalloc(mincount*LDT_ENTRY_SIZE); ++ else ++ newldt = kmalloc(mincount*LDT_ENTRY_SIZE, GFP_KERNEL); ++ ++ if (!newldt) ++ return -ENOMEM; ++ ++ if (oldsize) ++ memcpy(newldt, pc->ldt, oldsize*LDT_ENTRY_SIZE); ++ oldldt = pc->ldt; ++ memset(newldt+oldsize*LDT_ENTRY_SIZE, 0, (mincount-oldsize)*LDT_ENTRY_SIZE); ++ pc->ldt = newldt; ++ wmb(); ++ pc->size = mincount; ++ wmb(); ++ ++ if (reload) { ++#ifdef CONFIG_SMP ++ cpumask_t mask; ++ preempt_disable(); ++#endif ++ make_pages_readonly( ++ pc->ldt, ++ (pc->size * LDT_ENTRY_SIZE) / PAGE_SIZE, ++ XENFEAT_writable_descriptor_tables); ++ load_LDT(pc); ++#ifdef CONFIG_SMP ++ mask = cpumask_of_cpu(smp_processor_id()); ++ if (!cpus_equal(current->mm->cpu_vm_mask, mask)) ++ smp_call_function(flush_ldt, NULL, 1, 1); ++ preempt_enable(); ++#endif ++ } ++ if (oldsize) { ++ make_pages_writable( ++ oldldt, ++ (oldsize * LDT_ENTRY_SIZE) / PAGE_SIZE, ++ XENFEAT_writable_descriptor_tables); ++ if (oldsize*LDT_ENTRY_SIZE > PAGE_SIZE) ++ vfree(oldldt); ++ else ++ kfree(oldldt); ++ } ++ return 0; ++} ++ ++static inline int copy_ldt(mm_context_t *new, mm_context_t *old) ++{ ++ int err = alloc_ldt(new, old->size, 0); ++ if (err < 0) ++ return err; ++ memcpy(new->ldt, old->ldt, old->size*LDT_ENTRY_SIZE); ++ make_pages_readonly( ++ new->ldt, ++ (new->size * LDT_ENTRY_SIZE) / PAGE_SIZE, ++ XENFEAT_writable_descriptor_tables); ++ return 0; ++} ++ ++/* ++ * we do not have to muck with descriptors here, that is ++ * done in switch_mm() as needed. ++ */ ++int init_new_context(struct task_struct *tsk, struct mm_struct *mm) ++{ ++ struct mm_struct * old_mm; ++ int retval = 0; ++ ++ init_MUTEX(&mm->context.sem); ++ mm->context.size = 0; ++ mm->context.has_foreign_mappings = 0; ++ old_mm = current->mm; ++ if (old_mm && old_mm->context.size > 0) { ++ down(&old_mm->context.sem); ++ retval = copy_ldt(&mm->context, &old_mm->context); ++ up(&old_mm->context.sem); ++ } ++ return retval; ++} ++ ++/* ++ * No need to lock the MM as we are the last user ++ */ ++void destroy_context(struct mm_struct *mm) ++{ ++ if (mm->context.size) { ++ if (mm == current->active_mm) ++ clear_LDT(); ++ make_pages_writable( ++ mm->context.ldt, ++ (mm->context.size * LDT_ENTRY_SIZE) / PAGE_SIZE, ++ XENFEAT_writable_descriptor_tables); ++ if (mm->context.size*LDT_ENTRY_SIZE > PAGE_SIZE) ++ vfree(mm->context.ldt); ++ else ++ kfree(mm->context.ldt); ++ mm->context.size = 0; ++ } ++} ++ ++static int read_ldt(void __user * ptr, unsigned long bytecount) ++{ ++ int err; ++ unsigned long size; ++ struct mm_struct * mm = current->mm; ++ ++ if (!mm->context.size) ++ return 0; ++ if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES) ++ bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES; ++ ++ down(&mm->context.sem); ++ size = mm->context.size*LDT_ENTRY_SIZE; ++ if (size > bytecount) ++ size = bytecount; ++ ++ err = 0; ++ if (copy_to_user(ptr, mm->context.ldt, size)) ++ err = -EFAULT; ++ up(&mm->context.sem); ++ if (err < 0) ++ goto error_return; ++ if (size != bytecount) { ++ /* zero-fill the rest */ ++ if (clear_user(ptr+size, bytecount-size) != 0) { ++ err = -EFAULT; ++ goto error_return; ++ } ++ } ++ return bytecount; ++error_return: ++ return err; ++} ++ ++static int read_default_ldt(void __user * ptr, unsigned long bytecount) ++{ ++ int err; ++ unsigned long size; ++ void *address; ++ ++ err = 0; ++ address = &default_ldt[0]; ++ size = 5*sizeof(struct desc_struct); ++ if (size > bytecount) ++ size = bytecount; ++ ++ err = size; ++ if (copy_to_user(ptr, address, size)) ++ err = -EFAULT; ++ ++ return err; ++} ++ ++static int write_ldt(void __user * ptr, unsigned long bytecount, int oldmode) ++{ ++ struct mm_struct * mm = current->mm; ++ __u32 entry_1, entry_2; ++ int error; ++ struct user_desc ldt_info; ++ ++ error = -EINVAL; ++ if (bytecount != sizeof(ldt_info)) ++ goto out; ++ error = -EFAULT; ++ if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info))) ++ goto out; ++ ++ error = -EINVAL; ++ if (ldt_info.entry_number >= LDT_ENTRIES) ++ goto out; ++ if (ldt_info.contents == 3) { ++ if (oldmode) ++ goto out; ++ if (ldt_info.seg_not_present == 0) ++ goto out; ++ } ++ ++ down(&mm->context.sem); ++ if (ldt_info.entry_number >= mm->context.size) { ++ error = alloc_ldt(¤t->mm->context, ldt_info.entry_number+1, 1); ++ if (error < 0) ++ goto out_unlock; ++ } ++ ++ /* Allow LDTs to be cleared by the user. */ ++ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { ++ if (oldmode || LDT_empty(&ldt_info)) { ++ entry_1 = 0; ++ entry_2 = 0; ++ goto install; ++ } ++ } ++ ++ entry_1 = LDT_entry_a(&ldt_info); ++ entry_2 = LDT_entry_b(&ldt_info); ++ if (oldmode) ++ entry_2 &= ~(1 << 20); ++ ++ /* Install the new entry ... */ ++install: ++ error = write_ldt_entry(mm->context.ldt, ldt_info.entry_number, ++ entry_1, entry_2); ++ ++out_unlock: ++ up(&mm->context.sem); ++out: ++ return error; ++} ++ ++asmlinkage int sys_modify_ldt(int func, void __user *ptr, unsigned long bytecount) ++{ ++ int ret = -ENOSYS; ++ ++ switch (func) { ++ case 0: ++ ret = read_ldt(ptr, bytecount); ++ break; ++ case 1: ++ ret = write_ldt(ptr, bytecount, 1); ++ break; ++ case 2: ++ ret = read_default_ldt(ptr, bytecount); ++ break; ++ case 0x11: ++ ret = write_ldt(ptr, bytecount, 0); ++ break; ++ } ++ return ret; ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/microcode-xen.c 2007-06-12 13:12:48.000000000 +0200 +@@ -0,0 +1,144 @@ ++/* ++ * Intel CPU Microcode Update Driver for Linux ++ * ++ * Copyright (C) 2000-2004 Tigran Aivazian ++ * ++ * This driver allows to upgrade microcode on Intel processors ++ * belonging to IA-32 family - PentiumPro, Pentium II, ++ * Pentium III, Xeon, Pentium 4, etc. ++ * ++ * Reference: Section 8.10 of Volume III, Intel Pentium 4 Manual, ++ * Order Number 245472 or free download from: ++ * ++ * http://developer.intel.com/design/pentium4/manuals/245472.htm ++ * ++ * For more information, go to http://www.urbanmyth.org/microcode ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++//#define DEBUG /* pr_debug */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Intel CPU (IA-32) Microcode Update Driver"); ++MODULE_AUTHOR("Tigran Aivazian "); ++MODULE_LICENSE("GPL"); ++ ++static int verbose; ++module_param(verbose, int, 0644); ++ ++#define MICROCODE_VERSION "1.14a-xen" ++ ++#define DEFAULT_UCODE_DATASIZE (2000) /* 2000 bytes */ ++#define MC_HEADER_SIZE (sizeof (microcode_header_t)) /* 48 bytes */ ++#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) /* 2048 bytes */ ++ ++/* no concurrent ->write()s are allowed on /dev/cpu/microcode */ ++static DEFINE_MUTEX(microcode_mutex); ++ ++static int microcode_open (struct inode *unused1, struct file *unused2) ++{ ++ return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; ++} ++ ++ ++static int do_microcode_update (const void __user *ubuf, size_t len) ++{ ++ int err; ++ void *kbuf; ++ ++ kbuf = vmalloc(len); ++ if (!kbuf) ++ return -ENOMEM; ++ ++ if (copy_from_user(kbuf, ubuf, len) == 0) { ++ struct xen_platform_op op; ++ ++ op.cmd = XENPF_microcode_update; ++ set_xen_guest_handle(op.u.microcode.data, kbuf); ++ op.u.microcode.length = len; ++ err = HYPERVISOR_platform_op(&op); ++ } else ++ err = -EFAULT; ++ ++ vfree(kbuf); ++ ++ return err; ++} ++ ++static ssize_t microcode_write (struct file *file, const char __user *buf, size_t len, loff_t *ppos) ++{ ++ ssize_t ret; ++ ++ if (len < MC_HEADER_SIZE) { ++ printk(KERN_ERR "microcode: not enough data\n"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(µcode_mutex); ++ ++ ret = do_microcode_update(buf, len); ++ if (!ret) ++ ret = (ssize_t)len; ++ ++ mutex_unlock(µcode_mutex); ++ ++ return ret; ++} ++ ++static struct file_operations microcode_fops = { ++ .owner = THIS_MODULE, ++ .write = microcode_write, ++ .open = microcode_open, ++}; ++ ++static struct miscdevice microcode_dev = { ++ .minor = MICROCODE_MINOR, ++ .name = "microcode", ++ .fops = µcode_fops, ++}; ++ ++static int __init microcode_init (void) ++{ ++ int error; ++ ++ error = misc_register(µcode_dev); ++ if (error) { ++ printk(KERN_ERR ++ "microcode: can't misc_register on minor=%d\n", ++ MICROCODE_MINOR); ++ return error; ++ } ++ ++ printk(KERN_INFO ++ "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " \n"); ++ return 0; ++} ++ ++static void __exit microcode_exit (void) ++{ ++ misc_deregister(µcode_dev); ++} ++ ++module_init(microcode_init) ++module_exit(microcode_exit) ++MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/mpparse_32-xen.c 2007-06-12 13:12:48.000000000 +0200 +@@ -0,0 +1,1185 @@ ++/* ++ * Intel Multiprocessor Specification 1.1 and 1.4 ++ * compliant MP-table parsing routines. ++ * ++ * (c) 1995 Alan Cox, Building #3 ++ * (c) 1998, 1999, 2000 Ingo Molnar ++ * ++ * Fixes ++ * Erich Boleyn : MP v1.4 and additional changes. ++ * Alan Cox : Added EBDA scanning ++ * Ingo Molnar : various cleanups and rewrites ++ * Maciej W. Rozycki: Bits for default MP configurations ++ * Paul Diefenbaugh: Added full ACPI support ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* Have we found an MP table */ ++int smp_found_config; ++unsigned int __initdata maxcpus = NR_CPUS; ++ ++/* ++ * Various Linux-internal data structures created from the ++ * MP-table. ++ */ ++int apic_version [MAX_APICS]; ++int mp_bus_id_to_type [MAX_MP_BUSSES]; ++int mp_bus_id_to_node [MAX_MP_BUSSES]; ++int mp_bus_id_to_local [MAX_MP_BUSSES]; ++int quad_local_to_mp_bus_id [NR_CPUS/4][4]; ++int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; ++static int mp_current_pci_id; ++ ++/* I/O APIC entries */ ++struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS]; ++ ++/* # of MP IRQ source entries */ ++struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; ++ ++/* MP IRQ source entries */ ++int mp_irq_entries; ++ ++int nr_ioapics; ++ ++int pic_mode; ++unsigned long mp_lapic_addr; ++ ++unsigned int def_to_bigsmp = 0; ++ ++/* Processor that is doing the boot up */ ++unsigned int boot_cpu_physical_apicid = -1U; ++/* Internal processor count */ ++static unsigned int __devinitdata num_processors; ++ ++/* Bitmask of physically existing CPUs */ ++physid_mask_t phys_cpu_present_map; ++ ++u8 bios_cpu_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID }; ++ ++/* ++ * Intel MP BIOS table parsing routines: ++ */ ++ ++ ++/* ++ * Checksum an MP configuration block. ++ */ ++ ++static int __init mpf_checksum(unsigned char *mp, int len) ++{ ++ int sum = 0; ++ ++ while (len--) ++ sum += *mp++; ++ ++ return sum & 0xFF; ++} ++ ++/* ++ * Have to match translation table entries to main table entries by counter ++ * hence the mpc_record variable .... can't see a less disgusting way of ++ * doing this .... ++ */ ++ ++static int mpc_record; ++static struct mpc_config_translation *translation_table[MAX_MPC_ENTRY] __initdata; ++ ++#ifndef CONFIG_XEN ++static void __devinit MP_processor_info (struct mpc_config_processor *m) ++{ ++ int ver, apicid; ++ physid_mask_t phys_cpu; ++ ++ if (!(m->mpc_cpuflag & CPU_ENABLED)) ++ return; ++ ++ apicid = mpc_apic_id(m, translation_table[mpc_record]); ++ ++ if (m->mpc_featureflag&(1<<0)) ++ Dprintk(" Floating point unit present.\n"); ++ if (m->mpc_featureflag&(1<<7)) ++ Dprintk(" Machine Exception supported.\n"); ++ if (m->mpc_featureflag&(1<<8)) ++ Dprintk(" 64 bit compare & exchange supported.\n"); ++ if (m->mpc_featureflag&(1<<9)) ++ Dprintk(" Internal APIC present.\n"); ++ if (m->mpc_featureflag&(1<<11)) ++ Dprintk(" SEP present.\n"); ++ if (m->mpc_featureflag&(1<<12)) ++ Dprintk(" MTRR present.\n"); ++ if (m->mpc_featureflag&(1<<13)) ++ Dprintk(" PGE present.\n"); ++ if (m->mpc_featureflag&(1<<14)) ++ Dprintk(" MCA present.\n"); ++ if (m->mpc_featureflag&(1<<15)) ++ Dprintk(" CMOV present.\n"); ++ if (m->mpc_featureflag&(1<<16)) ++ Dprintk(" PAT present.\n"); ++ if (m->mpc_featureflag&(1<<17)) ++ Dprintk(" PSE present.\n"); ++ if (m->mpc_featureflag&(1<<18)) ++ Dprintk(" PSN present.\n"); ++ if (m->mpc_featureflag&(1<<19)) ++ Dprintk(" Cache Line Flush Instruction present.\n"); ++ /* 20 Reserved */ ++ if (m->mpc_featureflag&(1<<21)) ++ Dprintk(" Debug Trace and EMON Store present.\n"); ++ if (m->mpc_featureflag&(1<<22)) ++ Dprintk(" ACPI Thermal Throttle Registers present.\n"); ++ if (m->mpc_featureflag&(1<<23)) ++ Dprintk(" MMX present.\n"); ++ if (m->mpc_featureflag&(1<<24)) ++ Dprintk(" FXSR present.\n"); ++ if (m->mpc_featureflag&(1<<25)) ++ Dprintk(" XMM present.\n"); ++ if (m->mpc_featureflag&(1<<26)) ++ Dprintk(" Willamette New Instructions present.\n"); ++ if (m->mpc_featureflag&(1<<27)) ++ Dprintk(" Self Snoop present.\n"); ++ if (m->mpc_featureflag&(1<<28)) ++ Dprintk(" HT present.\n"); ++ if (m->mpc_featureflag&(1<<29)) ++ Dprintk(" Thermal Monitor present.\n"); ++ /* 30, 31 Reserved */ ++ ++ ++ if (m->mpc_cpuflag & CPU_BOOTPROCESSOR) { ++ Dprintk(" Bootup CPU\n"); ++ boot_cpu_physical_apicid = m->mpc_apicid; ++ } ++ ++ ver = m->mpc_apicver; ++ ++ /* ++ * Validate version ++ */ ++ if (ver == 0x0) { ++ printk(KERN_WARNING "BIOS bug, APIC version is 0 for CPU#%d! " ++ "fixing up to 0x10. (tell your hw vendor)\n", ++ m->mpc_apicid); ++ ver = 0x10; ++ } ++ apic_version[m->mpc_apicid] = ver; ++ ++ phys_cpu = apicid_to_cpu_present(apicid); ++ physids_or(phys_cpu_present_map, phys_cpu_present_map, phys_cpu); ++ ++ if (num_processors >= NR_CPUS) { ++ printk(KERN_WARNING "WARNING: NR_CPUS limit of %i reached." ++ " Processor ignored.\n", NR_CPUS); ++ return; ++ } ++ ++ if (num_processors >= maxcpus) { ++ printk(KERN_WARNING "WARNING: maxcpus limit of %i reached." ++ " Processor ignored.\n", maxcpus); ++ return; ++ } ++ ++ cpu_set(num_processors, cpu_possible_map); ++ num_processors++; ++ ++ /* ++ * Would be preferable to switch to bigsmp when CONFIG_HOTPLUG_CPU=y ++ * but we need to work other dependencies like SMP_SUSPEND etc ++ * before this can be done without some confusion. ++ * if (CPU_HOTPLUG_ENABLED || num_processors > 8) ++ * - Ashok Raj ++ */ ++ if (num_processors > 8) { ++ switch (boot_cpu_data.x86_vendor) { ++ case X86_VENDOR_INTEL: ++ if (!APIC_XAPIC(ver)) { ++ def_to_bigsmp = 0; ++ break; ++ } ++ /* If P4 and above fall through */ ++ case X86_VENDOR_AMD: ++ def_to_bigsmp = 1; ++ } ++ } ++ bios_cpu_apicid[num_processors - 1] = m->mpc_apicid; ++} ++#else ++void __init MP_processor_info (struct mpc_config_processor *m) ++{ ++ num_processors++; ++} ++#endif /* CONFIG_XEN */ ++ ++static void __init MP_bus_info (struct mpc_config_bus *m) ++{ ++ char str[7]; ++ ++ memcpy(str, m->mpc_bustype, 6); ++ str[6] = 0; ++ ++ mpc_oem_bus_info(m, str, translation_table[mpc_record]); ++ ++ if (m->mpc_busid >= MAX_MP_BUSSES) { ++ printk(KERN_WARNING "MP table busid value (%d) for bustype %s " ++ " is too large, max. supported is %d\n", ++ m->mpc_busid, str, MAX_MP_BUSSES - 1); ++ return; ++ } ++ ++ if (strncmp(str, BUSTYPE_ISA, sizeof(BUSTYPE_ISA)-1) == 0) { ++ mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA; ++ } else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0) { ++ mp_bus_id_to_type[m->mpc_busid] = MP_BUS_EISA; ++ } else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI)-1) == 0) { ++ mpc_oem_pci_bus(m, translation_table[mpc_record]); ++ mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI; ++ mp_bus_id_to_pci_bus[m->mpc_busid] = mp_current_pci_id; ++ mp_current_pci_id++; ++ } else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0) { ++ mp_bus_id_to_type[m->mpc_busid] = MP_BUS_MCA; ++ } else if (strncmp(str, BUSTYPE_NEC98, sizeof(BUSTYPE_NEC98)-1) == 0) { ++ mp_bus_id_to_type[m->mpc_busid] = MP_BUS_NEC98; ++ } else { ++ printk(KERN_WARNING "Unknown bustype %s - ignoring\n", str); ++ } ++} ++ ++static void __init MP_ioapic_info (struct mpc_config_ioapic *m) ++{ ++ if (!(m->mpc_flags & MPC_APIC_USABLE)) ++ return; ++ ++ printk(KERN_INFO "I/O APIC #%d Version %d at 0x%lX.\n", ++ m->mpc_apicid, m->mpc_apicver, m->mpc_apicaddr); ++ if (nr_ioapics >= MAX_IO_APICS) { ++ printk(KERN_CRIT "Max # of I/O APICs (%d) exceeded (found %d).\n", ++ MAX_IO_APICS, nr_ioapics); ++ panic("Recompile kernel with bigger MAX_IO_APICS!.\n"); ++ } ++ if (!m->mpc_apicaddr) { ++ printk(KERN_ERR "WARNING: bogus zero I/O APIC address" ++ " found in MP table, skipping!\n"); ++ return; ++ } ++ mp_ioapics[nr_ioapics] = *m; ++ nr_ioapics++; ++} ++ ++static void __init MP_intsrc_info (struct mpc_config_intsrc *m) ++{ ++ mp_irqs [mp_irq_entries] = *m; ++ Dprintk("Int: type %d, pol %d, trig %d, bus %d," ++ " IRQ %02x, APIC ID %x, APIC INT %02x\n", ++ m->mpc_irqtype, m->mpc_irqflag & 3, ++ (m->mpc_irqflag >> 2) & 3, m->mpc_srcbus, ++ m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq); ++ if (++mp_irq_entries == MAX_IRQ_SOURCES) ++ panic("Max # of irq sources exceeded!!\n"); ++} ++ ++static void __init MP_lintsrc_info (struct mpc_config_lintsrc *m) ++{ ++ Dprintk("Lint: type %d, pol %d, trig %d, bus %d," ++ " IRQ %02x, APIC ID %x, APIC LINT %02x\n", ++ m->mpc_irqtype, m->mpc_irqflag & 3, ++ (m->mpc_irqflag >> 2) &3, m->mpc_srcbusid, ++ m->mpc_srcbusirq, m->mpc_destapic, m->mpc_destapiclint); ++ /* ++ * Well it seems all SMP boards in existence ++ * use ExtINT/LVT1 == LINT0 and ++ * NMI/LVT2 == LINT1 - the following check ++ * will show us if this assumptions is false. ++ * Until then we do not have to add baggage. ++ */ ++ if ((m->mpc_irqtype == mp_ExtINT) && ++ (m->mpc_destapiclint != 0)) ++ BUG(); ++ if ((m->mpc_irqtype == mp_NMI) && ++ (m->mpc_destapiclint != 1)) ++ BUG(); ++} ++ ++#ifdef CONFIG_X86_NUMAQ ++static void __init MP_translation_info (struct mpc_config_translation *m) ++{ ++ printk(KERN_INFO "Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, m->trans_quad, m->trans_global, m->trans_local); ++ ++ if (mpc_record >= MAX_MPC_ENTRY) ++ printk(KERN_ERR "MAX_MPC_ENTRY exceeded!\n"); ++ else ++ translation_table[mpc_record] = m; /* stash this for later */ ++ if (m->trans_quad < MAX_NUMNODES && !node_online(m->trans_quad)) ++ node_set_online(m->trans_quad); ++} ++ ++/* ++ * Read/parse the MPC oem tables ++ */ ++ ++static void __init smp_read_mpc_oem(struct mp_config_oemtable *oemtable, \ ++ unsigned short oemsize) ++{ ++ int count = sizeof (*oemtable); /* the header size */ ++ unsigned char *oemptr = ((unsigned char *)oemtable)+count; ++ ++ mpc_record = 0; ++ printk(KERN_INFO "Found an OEM MPC table at %8p - parsing it ... \n", oemtable); ++ if (memcmp(oemtable->oem_signature,MPC_OEM_SIGNATURE,4)) ++ { ++ printk(KERN_WARNING "SMP mpc oemtable: bad signature [%c%c%c%c]!\n", ++ oemtable->oem_signature[0], ++ oemtable->oem_signature[1], ++ oemtable->oem_signature[2], ++ oemtable->oem_signature[3]); ++ return; ++ } ++ if (mpf_checksum((unsigned char *)oemtable,oemtable->oem_length)) ++ { ++ printk(KERN_WARNING "SMP oem mptable: checksum error!\n"); ++ return; ++ } ++ while (count < oemtable->oem_length) { ++ switch (*oemptr) { ++ case MP_TRANSLATION: ++ { ++ struct mpc_config_translation *m= ++ (struct mpc_config_translation *)oemptr; ++ MP_translation_info(m); ++ oemptr += sizeof(*m); ++ count += sizeof(*m); ++ ++mpc_record; ++ break; ++ } ++ default: ++ { ++ printk(KERN_WARNING "Unrecognised OEM table entry type! - %d\n", (int) *oemptr); ++ return; ++ } ++ } ++ } ++} ++ ++static inline void mps_oem_check(struct mp_config_table *mpc, char *oem, ++ char *productid) ++{ ++ if (strncmp(oem, "IBM NUMA", 8)) ++ printk("Warning! May not be a NUMA-Q system!\n"); ++ if (mpc->mpc_oemptr) ++ smp_read_mpc_oem((struct mp_config_oemtable *) mpc->mpc_oemptr, ++ mpc->mpc_oemsize); ++} ++#endif /* CONFIG_X86_NUMAQ */ ++ ++/* ++ * Read/parse the MPC ++ */ ++ ++static int __init smp_read_mpc(struct mp_config_table *mpc) ++{ ++ char str[16]; ++ char oem[10]; ++ int count=sizeof(*mpc); ++ unsigned char *mpt=((unsigned char *)mpc)+count; ++ ++ if (memcmp(mpc->mpc_signature,MPC_SIGNATURE,4)) { ++ printk(KERN_ERR "SMP mptable: bad signature [0x%x]!\n", ++ *(u32 *)mpc->mpc_signature); ++ return 0; ++ } ++ if (mpf_checksum((unsigned char *)mpc,mpc->mpc_length)) { ++ printk(KERN_ERR "SMP mptable: checksum error!\n"); ++ return 0; ++ } ++ if (mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04) { ++ printk(KERN_ERR "SMP mptable: bad table version (%d)!!\n", ++ mpc->mpc_spec); ++ return 0; ++ } ++ if (!mpc->mpc_lapic) { ++ printk(KERN_ERR "SMP mptable: null local APIC address!\n"); ++ return 0; ++ } ++ memcpy(oem,mpc->mpc_oem,8); ++ oem[8]=0; ++ printk(KERN_INFO "OEM ID: %s ",oem); ++ ++ memcpy(str,mpc->mpc_productid,12); ++ str[12]=0; ++ printk("Product ID: %s ",str); ++ ++ mps_oem_check(mpc, oem, str); ++ ++ printk("APIC at: 0x%lX\n",mpc->mpc_lapic); ++ ++ /* ++ * Save the local APIC address (it might be non-default) -- but only ++ * if we're not using ACPI. ++ */ ++ if (!acpi_lapic) ++ mp_lapic_addr = mpc->mpc_lapic; ++ ++ /* ++ * Now process the configuration blocks. ++ */ ++ mpc_record = 0; ++ while (count < mpc->mpc_length) { ++ switch(*mpt) { ++ case MP_PROCESSOR: ++ { ++ struct mpc_config_processor *m= ++ (struct mpc_config_processor *)mpt; ++ /* ACPI may have already provided this data */ ++ if (!acpi_lapic) ++ MP_processor_info(m); ++ mpt += sizeof(*m); ++ count += sizeof(*m); ++ break; ++ } ++ case MP_BUS: ++ { ++ struct mpc_config_bus *m= ++ (struct mpc_config_bus *)mpt; ++ MP_bus_info(m); ++ mpt += sizeof(*m); ++ count += sizeof(*m); ++ break; ++ } ++ case MP_IOAPIC: ++ { ++ struct mpc_config_ioapic *m= ++ (struct mpc_config_ioapic *)mpt; ++ MP_ioapic_info(m); ++ mpt+=sizeof(*m); ++ count+=sizeof(*m); ++ break; ++ } ++ case MP_INTSRC: ++ { ++ struct mpc_config_intsrc *m= ++ (struct mpc_config_intsrc *)mpt; ++ ++ MP_intsrc_info(m); ++ mpt+=sizeof(*m); ++ count+=sizeof(*m); ++ break; ++ } ++ case MP_LINTSRC: ++ { ++ struct mpc_config_lintsrc *m= ++ (struct mpc_config_lintsrc *)mpt; ++ MP_lintsrc_info(m); ++ mpt+=sizeof(*m); ++ count+=sizeof(*m); ++ break; ++ } ++ default: ++ { ++ count = mpc->mpc_length; ++ break; ++ } ++ } ++ ++mpc_record; ++ } ++ clustered_apic_check(); ++ if (!num_processors) ++ printk(KERN_ERR "SMP mptable: no processors registered!\n"); ++ return num_processors; ++} ++ ++static int __init ELCR_trigger(unsigned int irq) ++{ ++ unsigned int port; ++ ++ port = 0x4d0 + (irq >> 3); ++ return (inb(port) >> (irq & 7)) & 1; ++} ++ ++static void __init construct_default_ioirq_mptable(int mpc_default_type) ++{ ++ struct mpc_config_intsrc intsrc; ++ int i; ++ int ELCR_fallback = 0; ++ ++ intsrc.mpc_type = MP_INTSRC; ++ intsrc.mpc_irqflag = 0; /* conforming */ ++ intsrc.mpc_srcbus = 0; ++ intsrc.mpc_dstapic = mp_ioapics[0].mpc_apicid; ++ ++ intsrc.mpc_irqtype = mp_INT; ++ ++ /* ++ * If true, we have an ISA/PCI system with no IRQ entries ++ * in the MP table. To prevent the PCI interrupts from being set up ++ * incorrectly, we try to use the ELCR. The sanity check to see if ++ * there is good ELCR data is very simple - IRQ0, 1, 2 and 13 can ++ * never be level sensitive, so we simply see if the ELCR agrees. ++ * If it does, we assume it's valid. ++ */ ++ if (mpc_default_type == 5) { ++ printk(KERN_INFO "ISA/PCI bus type with no IRQ information... falling back to ELCR\n"); ++ ++ if (ELCR_trigger(0) || ELCR_trigger(1) || ELCR_trigger(2) || ELCR_trigger(13)) ++ printk(KERN_WARNING "ELCR contains invalid data... not using ELCR\n"); ++ else { ++ printk(KERN_INFO "Using ELCR to identify PCI interrupts\n"); ++ ELCR_fallback = 1; ++ } ++ } ++ ++ for (i = 0; i < 16; i++) { ++ switch (mpc_default_type) { ++ case 2: ++ if (i == 0 || i == 13) ++ continue; /* IRQ0 & IRQ13 not connected */ ++ /* fall through */ ++ default: ++ if (i == 2) ++ continue; /* IRQ2 is never connected */ ++ } ++ ++ if (ELCR_fallback) { ++ /* ++ * If the ELCR indicates a level-sensitive interrupt, we ++ * copy that information over to the MP table in the ++ * irqflag field (level sensitive, active high polarity). ++ */ ++ if (ELCR_trigger(i)) ++ intsrc.mpc_irqflag = 13; ++ else ++ intsrc.mpc_irqflag = 0; ++ } ++ ++ intsrc.mpc_srcbusirq = i; ++ intsrc.mpc_dstirq = i ? i : 2; /* IRQ0 to INTIN2 */ ++ MP_intsrc_info(&intsrc); ++ } ++ ++ intsrc.mpc_irqtype = mp_ExtINT; ++ intsrc.mpc_srcbusirq = 0; ++ intsrc.mpc_dstirq = 0; /* 8259A to INTIN0 */ ++ MP_intsrc_info(&intsrc); ++} ++ ++static inline void __init construct_default_ISA_mptable(int mpc_default_type) ++{ ++ struct mpc_config_processor processor; ++ struct mpc_config_bus bus; ++ struct mpc_config_ioapic ioapic; ++ struct mpc_config_lintsrc lintsrc; ++ int linttypes[2] = { mp_ExtINT, mp_NMI }; ++ int i; ++ ++ /* ++ * local APIC has default address ++ */ ++ mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; ++ ++ /* ++ * 2 CPUs, numbered 0 & 1. ++ */ ++ processor.mpc_type = MP_PROCESSOR; ++ /* Either an integrated APIC or a discrete 82489DX. */ ++ processor.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01; ++ processor.mpc_cpuflag = CPU_ENABLED; ++ processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) | ++ (boot_cpu_data.x86_model << 4) | ++ boot_cpu_data.x86_mask; ++ processor.mpc_featureflag = boot_cpu_data.x86_capability[0]; ++ processor.mpc_reserved[0] = 0; ++ processor.mpc_reserved[1] = 0; ++ for (i = 0; i < 2; i++) { ++ processor.mpc_apicid = i; ++ MP_processor_info(&processor); ++ } ++ ++ bus.mpc_type = MP_BUS; ++ bus.mpc_busid = 0; ++ switch (mpc_default_type) { ++ default: ++ printk("???\n"); ++ printk(KERN_ERR "Unknown standard configuration %d\n", ++ mpc_default_type); ++ /* fall through */ ++ case 1: ++ case 5: ++ memcpy(bus.mpc_bustype, "ISA ", 6); ++ break; ++ case 2: ++ case 6: ++ case 3: ++ memcpy(bus.mpc_bustype, "EISA ", 6); ++ break; ++ case 4: ++ case 7: ++ memcpy(bus.mpc_bustype, "MCA ", 6); ++ } ++ MP_bus_info(&bus); ++ if (mpc_default_type > 4) { ++ bus.mpc_busid = 1; ++ memcpy(bus.mpc_bustype, "PCI ", 6); ++ MP_bus_info(&bus); ++ } ++ ++ ioapic.mpc_type = MP_IOAPIC; ++ ioapic.mpc_apicid = 2; ++ ioapic.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01; ++ ioapic.mpc_flags = MPC_APIC_USABLE; ++ ioapic.mpc_apicaddr = 0xFEC00000; ++ MP_ioapic_info(&ioapic); ++ ++ /* ++ * We set up most of the low 16 IO-APIC pins according to MPS rules. ++ */ ++ construct_default_ioirq_mptable(mpc_default_type); ++ ++ lintsrc.mpc_type = MP_LINTSRC; ++ lintsrc.mpc_irqflag = 0; /* conforming */ ++ lintsrc.mpc_srcbusid = 0; ++ lintsrc.mpc_srcbusirq = 0; ++ lintsrc.mpc_destapic = MP_APIC_ALL; ++ for (i = 0; i < 2; i++) { ++ lintsrc.mpc_irqtype = linttypes[i]; ++ lintsrc.mpc_destapiclint = i; ++ MP_lintsrc_info(&lintsrc); ++ } ++} ++ ++static struct intel_mp_floating *mpf_found; ++ ++/* ++ * Scan the memory blocks for an SMP configuration block. ++ */ ++void __init get_smp_config (void) ++{ ++ struct intel_mp_floating *mpf = mpf_found; ++ ++ /* ++ * ACPI supports both logical (e.g. Hyper-Threading) and physical ++ * processors, where MPS only supports physical. ++ */ ++ if (acpi_lapic && acpi_ioapic) { ++ printk(KERN_INFO "Using ACPI (MADT) for SMP configuration information\n"); ++ return; ++ } ++ else if (acpi_lapic) ++ printk(KERN_INFO "Using ACPI for processor (LAPIC) configuration information\n"); ++ ++ printk(KERN_INFO "Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification); ++ if (mpf->mpf_feature2 & (1<<7)) { ++ printk(KERN_INFO " IMCR and PIC compatibility mode.\n"); ++ pic_mode = 1; ++ } else { ++ printk(KERN_INFO " Virtual Wire compatibility mode.\n"); ++ pic_mode = 0; ++ } ++ ++ /* ++ * Now see if we need to read further. ++ */ ++ if (mpf->mpf_feature1 != 0) { ++ ++ printk(KERN_INFO "Default MP configuration #%d\n", mpf->mpf_feature1); ++ construct_default_ISA_mptable(mpf->mpf_feature1); ++ ++ } else if (mpf->mpf_physptr) { ++ ++ /* ++ * Read the physical hardware table. Anything here will ++ * override the defaults. ++ */ ++ if (!smp_read_mpc(isa_bus_to_virt(mpf->mpf_physptr))) { ++ smp_found_config = 0; ++ printk(KERN_ERR "BIOS bug, MP table errors detected!...\n"); ++ printk(KERN_ERR "... disabling SMP support. (tell your hw vendor)\n"); ++ return; ++ } ++ /* ++ * If there are no explicit MP IRQ entries, then we are ++ * broken. We set up most of the low 16 IO-APIC pins to ++ * ISA defaults and hope it will work. ++ */ ++ if (!mp_irq_entries) { ++ struct mpc_config_bus bus; ++ ++ printk(KERN_ERR "BIOS bug, no explicit IRQ entries, using default mptable. (tell your hw vendor)\n"); ++ ++ bus.mpc_type = MP_BUS; ++ bus.mpc_busid = 0; ++ memcpy(bus.mpc_bustype, "ISA ", 6); ++ MP_bus_info(&bus); ++ ++ construct_default_ioirq_mptable(0); ++ } ++ ++ } else ++ BUG(); ++ ++ printk(KERN_INFO "Processors: %d\n", num_processors); ++ /* ++ * Only use the first configuration found. ++ */ ++} ++ ++static int __init smp_scan_config (unsigned long base, unsigned long length) ++{ ++ unsigned long *bp = isa_bus_to_virt(base); ++ struct intel_mp_floating *mpf; ++ ++ Dprintk("Scan SMP from %p for %ld bytes.\n", bp,length); ++ if (sizeof(*mpf) != 16) ++ printk("Error: MPF size\n"); ++ ++ while (length > 0) { ++ mpf = (struct intel_mp_floating *)bp; ++ if ((*bp == SMP_MAGIC_IDENT) && ++ (mpf->mpf_length == 1) && ++ !mpf_checksum((unsigned char *)bp, 16) && ++ ((mpf->mpf_specification == 1) ++ || (mpf->mpf_specification == 4)) ) { ++ ++ smp_found_config = 1; ++#ifndef CONFIG_XEN ++ printk(KERN_INFO "found SMP MP-table at %08lx\n", ++ virt_to_phys(mpf)); ++ reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE); ++ if (mpf->mpf_physptr) { ++ /* ++ * We cannot access to MPC table to compute ++ * table size yet, as only few megabytes from ++ * the bottom is mapped now. ++ * PC-9800's MPC table places on the very last ++ * of physical memory; so that simply reserving ++ * PAGE_SIZE from mpg->mpf_physptr yields BUG() ++ * in reserve_bootmem. ++ */ ++ unsigned long size = PAGE_SIZE; ++ unsigned long end = max_low_pfn * PAGE_SIZE; ++ if (mpf->mpf_physptr + size > end) ++ size = end - mpf->mpf_physptr; ++ reserve_bootmem(mpf->mpf_physptr, size); ++ } ++#else ++ printk(KERN_INFO "found SMP MP-table at %08lx\n", ++ ((unsigned long)bp - (unsigned long)isa_bus_to_virt(base)) + base); ++#endif ++ ++ mpf_found = mpf; ++ return 1; ++ } ++ bp += 4; ++ length -= 16; ++ } ++ return 0; ++} ++ ++void __init find_smp_config (void) ++{ ++#ifndef CONFIG_XEN ++ unsigned int address; ++#endif ++ ++ /* ++ * FIXME: Linux assumes you have 640K of base ram.. ++ * this continues the error... ++ * ++ * 1) Scan the bottom 1K for a signature ++ * 2) Scan the top 1K of base RAM ++ * 3) Scan the 64K of bios ++ */ ++ if (smp_scan_config(0x0,0x400) || ++ smp_scan_config(639*0x400,0x400) || ++ smp_scan_config(0xF0000,0x10000)) ++ return; ++ /* ++ * If it is an SMP machine we should know now, unless the ++ * configuration is in an EISA/MCA bus machine with an ++ * extended bios data area. ++ * ++ * there is a real-mode segmented pointer pointing to the ++ * 4K EBDA area at 0x40E, calculate and scan it here. ++ * ++ * NOTE! There are Linux loaders that will corrupt the EBDA ++ * area, and as such this kind of SMP config may be less ++ * trustworthy, simply because the SMP table may have been ++ * stomped on during early boot. These loaders are buggy and ++ * should be fixed. ++ * ++ * MP1.4 SPEC states to only scan first 1K of 4K EBDA. ++ */ ++ ++#ifndef CONFIG_XEN ++ address = get_bios_ebda(); ++ if (address) ++ smp_scan_config(address, 0x400); ++#endif ++} ++ ++int es7000_plat; ++ ++/* -------------------------------------------------------------------------- ++ ACPI-based MP Configuration ++ -------------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_ACPI ++ ++void __init mp_register_lapic_address ( ++ u64 address) ++{ ++#ifndef CONFIG_XEN ++ mp_lapic_addr = (unsigned long) address; ++ ++ set_fixmap_nocache(FIX_APIC_BASE, mp_lapic_addr); ++ ++ if (boot_cpu_physical_apicid == -1U) ++ boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID)); ++ ++ Dprintk("Boot CPU = %d\n", boot_cpu_physical_apicid); ++#endif ++} ++ ++ ++void __devinit mp_register_lapic ( ++ u8 id, ++ u8 enabled) ++{ ++ struct mpc_config_processor processor; ++ int boot_cpu = 0; ++ ++ if (MAX_APICS - id <= 0) { ++ printk(KERN_WARNING "Processor #%d invalid (max %d)\n", ++ id, MAX_APICS); ++ return; ++ } ++ ++ if (id == boot_cpu_physical_apicid) ++ boot_cpu = 1; ++ ++#ifndef CONFIG_XEN ++ processor.mpc_type = MP_PROCESSOR; ++ processor.mpc_apicid = id; ++ processor.mpc_apicver = GET_APIC_VERSION(apic_read(APIC_LVR)); ++ processor.mpc_cpuflag = (enabled ? CPU_ENABLED : 0); ++ processor.mpc_cpuflag |= (boot_cpu ? CPU_BOOTPROCESSOR : 0); ++ processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) | ++ (boot_cpu_data.x86_model << 4) | boot_cpu_data.x86_mask; ++ processor.mpc_featureflag = boot_cpu_data.x86_capability[0]; ++ processor.mpc_reserved[0] = 0; ++ processor.mpc_reserved[1] = 0; ++#endif ++ ++ MP_processor_info(&processor); ++} ++ ++#ifdef CONFIG_X86_IO_APIC ++ ++#define MP_ISA_BUS 0 ++#define MP_MAX_IOAPIC_PIN 127 ++ ++static struct mp_ioapic_routing { ++ int apic_id; ++ int gsi_base; ++ int gsi_end; ++ u32 pin_programmed[4]; ++} mp_ioapic_routing[MAX_IO_APICS]; ++ ++ ++static int mp_find_ioapic ( ++ int gsi) ++{ ++ int i = 0; ++ ++ /* Find the IOAPIC that manages this GSI. */ ++ for (i = 0; i < nr_ioapics; i++) { ++ if ((gsi >= mp_ioapic_routing[i].gsi_base) ++ && (gsi <= mp_ioapic_routing[i].gsi_end)) ++ return i; ++ } ++ ++ printk(KERN_ERR "ERROR: Unable to locate IOAPIC for GSI %d\n", gsi); ++ ++ return -1; ++} ++ ++ ++void __init mp_register_ioapic ( ++ u8 id, ++ u32 address, ++ u32 gsi_base) ++{ ++ int idx = 0; ++ int tmpid; ++ ++ if (nr_ioapics >= MAX_IO_APICS) { ++ printk(KERN_ERR "ERROR: Max # of I/O APICs (%d) exceeded " ++ "(found %d)\n", MAX_IO_APICS, nr_ioapics); ++ panic("Recompile kernel with bigger MAX_IO_APICS!\n"); ++ } ++ if (!address) { ++ printk(KERN_ERR "WARNING: Bogus (zero) I/O APIC address" ++ " found in MADT table, skipping!\n"); ++ return; ++ } ++ ++ idx = nr_ioapics++; ++ ++ mp_ioapics[idx].mpc_type = MP_IOAPIC; ++ mp_ioapics[idx].mpc_flags = MPC_APIC_USABLE; ++ mp_ioapics[idx].mpc_apicaddr = address; ++ ++#ifndef CONFIG_XEN ++ set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address); ++#endif ++ if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) ++ && !APIC_XAPIC(apic_version[boot_cpu_physical_apicid])) ++ tmpid = io_apic_get_unique_id(idx, id); ++ else ++ tmpid = id; ++ if (tmpid == -1) { ++ nr_ioapics--; ++ return; ++ } ++ mp_ioapics[idx].mpc_apicid = tmpid; ++ mp_ioapics[idx].mpc_apicver = io_apic_get_version(idx); ++ ++ /* ++ * Build basic GSI lookup table to facilitate gsi->io_apic lookups ++ * and to prevent reprogramming of IOAPIC pins (PCI GSIs). ++ */ ++ mp_ioapic_routing[idx].apic_id = mp_ioapics[idx].mpc_apicid; ++ mp_ioapic_routing[idx].gsi_base = gsi_base; ++ mp_ioapic_routing[idx].gsi_end = gsi_base + ++ io_apic_get_redir_entries(idx); ++ ++ printk("IOAPIC[%d]: apic_id %d, version %d, address 0x%lx, " ++ "GSI %d-%d\n", idx, mp_ioapics[idx].mpc_apicid, ++ mp_ioapics[idx].mpc_apicver, mp_ioapics[idx].mpc_apicaddr, ++ mp_ioapic_routing[idx].gsi_base, ++ mp_ioapic_routing[idx].gsi_end); ++ ++ return; ++} ++ ++ ++void __init mp_override_legacy_irq ( ++ u8 bus_irq, ++ u8 polarity, ++ u8 trigger, ++ u32 gsi) ++{ ++ struct mpc_config_intsrc intsrc; ++ int ioapic = -1; ++ int pin = -1; ++ ++ /* ++ * Convert 'gsi' to 'ioapic.pin'. ++ */ ++ ioapic = mp_find_ioapic(gsi); ++ if (ioapic < 0) ++ return; ++ pin = gsi - mp_ioapic_routing[ioapic].gsi_base; ++ ++ /* ++ * TBD: This check is for faulty timer entries, where the override ++ * erroneously sets the trigger to level, resulting in a HUGE ++ * increase of timer interrupts! ++ */ ++ if ((bus_irq == 0) && (trigger == 3)) ++ trigger = 1; ++ ++ intsrc.mpc_type = MP_INTSRC; ++ intsrc.mpc_irqtype = mp_INT; ++ intsrc.mpc_irqflag = (trigger << 2) | polarity; ++ intsrc.mpc_srcbus = MP_ISA_BUS; ++ intsrc.mpc_srcbusirq = bus_irq; /* IRQ */ ++ intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid; /* APIC ID */ ++ intsrc.mpc_dstirq = pin; /* INTIN# */ ++ ++ Dprintk("Int: type %d, pol %d, trig %d, bus %d, irq %d, %d-%d\n", ++ intsrc.mpc_irqtype, intsrc.mpc_irqflag & 3, ++ (intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus, ++ intsrc.mpc_srcbusirq, intsrc.mpc_dstapic, intsrc.mpc_dstirq); ++ ++ mp_irqs[mp_irq_entries] = intsrc; ++ if (++mp_irq_entries == MAX_IRQ_SOURCES) ++ panic("Max # of irq sources exceeded!\n"); ++ ++ return; ++} ++ ++void __init mp_config_acpi_legacy_irqs (void) ++{ ++ struct mpc_config_intsrc intsrc; ++ int i = 0; ++ int ioapic = -1; ++ ++ /* ++ * Fabricate the legacy ISA bus (bus #31). ++ */ ++ mp_bus_id_to_type[MP_ISA_BUS] = MP_BUS_ISA; ++ Dprintk("Bus #%d is ISA\n", MP_ISA_BUS); ++ ++ /* ++ * Older generations of ES7000 have no legacy identity mappings ++ */ ++ if (es7000_plat == 1) ++ return; ++ ++ /* ++ * Locate the IOAPIC that manages the ISA IRQs (0-15). ++ */ ++ ioapic = mp_find_ioapic(0); ++ if (ioapic < 0) ++ return; ++ ++ intsrc.mpc_type = MP_INTSRC; ++ intsrc.mpc_irqflag = 0; /* Conforming */ ++ intsrc.mpc_srcbus = MP_ISA_BUS; ++ intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid; ++ ++ /* ++ * Use the default configuration for the IRQs 0-15. Unless ++ * overriden by (MADT) interrupt source override entries. ++ */ ++ for (i = 0; i < 16; i++) { ++ int idx; ++ ++ for (idx = 0; idx < mp_irq_entries; idx++) { ++ struct mpc_config_intsrc *irq = mp_irqs + idx; ++ ++ /* Do we already have a mapping for this ISA IRQ? */ ++ if (irq->mpc_srcbus == MP_ISA_BUS && irq->mpc_srcbusirq == i) ++ break; ++ ++ /* Do we already have a mapping for this IOAPIC pin */ ++ if ((irq->mpc_dstapic == intsrc.mpc_dstapic) && ++ (irq->mpc_dstirq == i)) ++ break; ++ } ++ ++ if (idx != mp_irq_entries) { ++ printk(KERN_DEBUG "ACPI: IRQ%d used by override.\n", i); ++ continue; /* IRQ already used */ ++ } ++ ++ intsrc.mpc_irqtype = mp_INT; ++ intsrc.mpc_srcbusirq = i; /* Identity mapped */ ++ intsrc.mpc_dstirq = i; ++ ++ Dprintk("Int: type %d, pol %d, trig %d, bus %d, irq %d, " ++ "%d-%d\n", intsrc.mpc_irqtype, intsrc.mpc_irqflag & 3, ++ (intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus, ++ intsrc.mpc_srcbusirq, intsrc.mpc_dstapic, ++ intsrc.mpc_dstirq); ++ ++ mp_irqs[mp_irq_entries] = intsrc; ++ if (++mp_irq_entries == MAX_IRQ_SOURCES) ++ panic("Max # of irq sources exceeded!\n"); ++ } ++} ++ ++#define MAX_GSI_NUM 4096 ++ ++int mp_register_gsi (u32 gsi, int triggering, int polarity) ++{ ++ int ioapic = -1; ++ int ioapic_pin = 0; ++ int idx, bit = 0; ++ static int pci_irq = 16; ++ /* ++ * Mapping between Global System Interrups, which ++ * represent all possible interrupts, and IRQs ++ * assigned to actual devices. ++ */ ++ static int gsi_to_irq[MAX_GSI_NUM]; ++ ++ /* Don't set up the ACPI SCI because it's already set up */ ++ if (acpi_fadt.sci_int == gsi) ++ return gsi; ++ ++ ioapic = mp_find_ioapic(gsi); ++ if (ioapic < 0) { ++ printk(KERN_WARNING "No IOAPIC for GSI %u\n", gsi); ++ return gsi; ++ } ++ ++ ioapic_pin = gsi - mp_ioapic_routing[ioapic].gsi_base; ++ ++ if (ioapic_renumber_irq) ++ gsi = ioapic_renumber_irq(ioapic, gsi); ++ ++ /* ++ * Avoid pin reprogramming. PRTs typically include entries ++ * with redundant pin->gsi mappings (but unique PCI devices); ++ * we only program the IOAPIC on the first. ++ */ ++ bit = ioapic_pin % 32; ++ idx = (ioapic_pin < 32) ? 0 : (ioapic_pin / 32); ++ if (idx > 3) { ++ printk(KERN_ERR "Invalid reference to IOAPIC pin " ++ "%d-%d\n", mp_ioapic_routing[ioapic].apic_id, ++ ioapic_pin); ++ return gsi; ++ } ++ if ((1< 15), but ++ * avoid a problem where the 8254 timer (IRQ0) is setup ++ * via an override (so it's not on pin 0 of the ioapic), ++ * and at the same time, the pin 0 interrupt is a PCI ++ * type. The gsi > 15 test could cause these two pins ++ * to be shared as IRQ0, and they are not shareable. ++ * So test for this condition, and if necessary, avoid ++ * the pin collision. ++ */ ++ if (gsi > 15 || (gsi == 0 && !timer_uses_ioapic_pin_0)) ++ gsi = pci_irq++; ++ /* ++ * Don't assign IRQ used by ACPI SCI ++ */ ++ if (gsi == acpi_fadt.sci_int) ++ gsi = pci_irq++; ++ gsi_to_irq[irq] = gsi; ++ } else { ++ printk(KERN_ERR "GSI %u is too high\n", gsi); ++ return gsi; ++ } ++ } ++ ++ io_apic_set_pci_routing(ioapic, ioapic_pin, gsi, ++ triggering == ACPI_EDGE_SENSITIVE ? 0 : 1, ++ polarity == ACPI_ACTIVE_HIGH ? 0 : 1); ++ return gsi; ++} ++ ++#endif /* CONFIG_X86_IO_APIC */ ++#endif /* CONFIG_ACPI */ +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/pci-dma-xen.c 2009-11-06 10:23:23.000000000 +0100 +@@ -0,0 +1,406 @@ ++/* ++ * Dynamic DMA mapping support. ++ * ++ * On i386 there is no hardware dynamic DMA address translation, ++ * so consistent alloc/free are merely page allocation/freeing. ++ * The rest of the dynamic DMA mapping interface is implemented ++ * in asm/pci.h. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef __x86_64__ ++#include ++ ++int iommu_merge __read_mostly = 0; ++EXPORT_SYMBOL(iommu_merge); ++ ++dma_addr_t bad_dma_address __read_mostly; ++EXPORT_SYMBOL(bad_dma_address); ++ ++/* This tells the BIO block layer to assume merging. Default to off ++ because we cannot guarantee merging later. */ ++int iommu_bio_merge __read_mostly = 0; ++EXPORT_SYMBOL(iommu_bio_merge); ++ ++int force_iommu __read_mostly= 0; ++ ++__init int iommu_setup(char *p) ++{ ++ return 1; ++} ++ ++void __init pci_iommu_alloc(void) ++{ ++#ifdef CONFIG_SWIOTLB ++ pci_swiotlb_init(); ++#endif ++} ++ ++static int __init pci_iommu_init(void) ++{ ++ no_iommu_init(); ++ return 0; ++} ++ ++/* Must execute after PCI subsystem */ ++fs_initcall(pci_iommu_init); ++#endif ++ ++struct dma_coherent_mem { ++ void *virt_base; ++ u32 device_base; ++ int size; ++ int flags; ++ unsigned long *bitmap; ++}; ++ ++#define IOMMU_BUG_ON(test) \ ++do { \ ++ if (unlikely(test)) { \ ++ printk(KERN_ALERT "Fatal DMA error! " \ ++ "Please use 'swiotlb=force'\n"); \ ++ BUG(); \ ++ } \ ++} while (0) ++ ++static int check_pages_physically_contiguous(unsigned long pfn, ++ unsigned int offset, ++ size_t length) ++{ ++ unsigned long next_mfn; ++ int i; ++ int nr_pages; ++ ++ next_mfn = pfn_to_mfn(pfn); ++ nr_pages = (offset + length + PAGE_SIZE-1) >> PAGE_SHIFT; ++ ++ for (i = 1; i < nr_pages; i++) { ++ if (pfn_to_mfn(++pfn) != ++next_mfn) ++ return 0; ++ } ++ return 1; ++} ++ ++int range_straddles_page_boundary(paddr_t p, size_t size) ++{ ++ unsigned long pfn = p >> PAGE_SHIFT; ++ unsigned int offset = p & ~PAGE_MASK; ++ ++ return ((offset + size > PAGE_SIZE) && ++ !check_pages_physically_contiguous(pfn, offset, size)); ++} ++ ++int ++dma_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, ++ enum dma_data_direction direction) ++{ ++ int i, rc; ++ ++ if (direction == DMA_NONE) ++ BUG(); ++ WARN_ON(nents == 0 || sg[0].length == 0); ++ ++ if (swiotlb) { ++ rc = swiotlb_map_sg(hwdev, sg, nents, direction); ++ } else { ++ for (i = 0; i < nents; i++ ) { ++ BUG_ON(!sg[i].page); ++ sg[i].dma_address = ++ gnttab_dma_map_page(sg[i].page) + sg[i].offset; ++ sg[i].dma_length = sg[i].length; ++ IOMMU_BUG_ON(address_needs_mapping( ++ hwdev, sg[i].dma_address)); ++ IOMMU_BUG_ON(range_straddles_page_boundary( ++ page_to_pseudophys(sg[i].page) + sg[i].offset, ++ sg[i].length)); ++ } ++ rc = nents; ++ } ++ ++ flush_write_buffers(); ++ return rc; ++} ++EXPORT_SYMBOL(dma_map_sg); ++ ++void ++dma_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, ++ enum dma_data_direction direction) ++{ ++ int i; ++ ++ BUG_ON(direction == DMA_NONE); ++ if (swiotlb) ++ swiotlb_unmap_sg(hwdev, sg, nents, direction); ++ else { ++ for (i = 0; i < nents; i++ ) ++ gnttab_dma_unmap_page(sg[i].dma_address); ++ } ++} ++EXPORT_SYMBOL(dma_unmap_sg); ++ ++#ifdef CONFIG_HIGHMEM ++dma_addr_t ++dma_map_page(struct device *dev, struct page *page, unsigned long offset, ++ size_t size, enum dma_data_direction direction) ++{ ++ dma_addr_t dma_addr; ++ ++ BUG_ON(direction == DMA_NONE); ++ ++ if (swiotlb) { ++ dma_addr = swiotlb_map_page( ++ dev, page, offset, size, direction); ++ } else { ++ dma_addr = gnttab_dma_map_page(page) + offset; ++ IOMMU_BUG_ON(address_needs_mapping(dev, dma_addr)); ++ } ++ ++ return dma_addr; ++} ++EXPORT_SYMBOL(dma_map_page); ++ ++void ++dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(direction == DMA_NONE); ++ if (swiotlb) ++ swiotlb_unmap_page(dev, dma_address, size, direction); ++ else ++ gnttab_dma_unmap_page(dma_address); ++} ++EXPORT_SYMBOL(dma_unmap_page); ++#endif /* CONFIG_HIGHMEM */ ++ ++int ++dma_mapping_error(dma_addr_t dma_addr) ++{ ++ if (swiotlb) ++ return swiotlb_dma_mapping_error(dma_addr); ++ return 0; ++} ++EXPORT_SYMBOL(dma_mapping_error); ++ ++int ++dma_supported(struct device *dev, u64 mask) ++{ ++ if (swiotlb) ++ return swiotlb_dma_supported(dev, mask); ++ /* ++ * By default we'll BUG when an infeasible DMA is requested, and ++ * request swiotlb=force (see IOMMU_BUG_ON). ++ */ ++ return 1; ++} ++EXPORT_SYMBOL(dma_supported); ++ ++void *dma_alloc_coherent(struct device *dev, size_t size, ++ dma_addr_t *dma_handle, gfp_t gfp) ++{ ++ void *ret; ++ struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; ++ unsigned int order = get_order(size); ++ unsigned long vstart; ++ u64 mask; ++ ++ /* ignore region specifiers */ ++ gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32); ++ ++ if (mem) { ++ int page = bitmap_find_free_region(mem->bitmap, mem->size, ++ order); ++ if (page >= 0) { ++ *dma_handle = mem->device_base + (page << PAGE_SHIFT); ++ ret = mem->virt_base + (page << PAGE_SHIFT); ++ memset(ret, 0, size); ++ return ret; ++ } ++ if (mem->flags & DMA_MEMORY_EXCLUSIVE) ++ return NULL; ++ } ++ ++ vstart = __get_free_pages(gfp, order); ++ ret = (void *)vstart; ++ ++ if (dev != NULL && dev->coherent_dma_mask) ++ mask = dev->coherent_dma_mask; ++ else ++ mask = 0xffffffff; ++ ++ if (ret != NULL) { ++ if (xen_create_contiguous_region(vstart, order, ++ fls64(mask)) != 0) { ++ free_pages(vstart, order); ++ return NULL; ++ } ++ memset(ret, 0, size); ++ *dma_handle = virt_to_bus(ret); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(dma_alloc_coherent); ++ ++void dma_free_coherent(struct device *dev, size_t size, ++ void *vaddr, dma_addr_t dma_handle) ++{ ++ struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; ++ int order = get_order(size); ++ ++ if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { ++ int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; ++ ++ bitmap_release_region(mem->bitmap, page, order); ++ } else { ++ xen_destroy_contiguous_region((unsigned long)vaddr, order); ++ free_pages((unsigned long)vaddr, order); ++ } ++} ++EXPORT_SYMBOL(dma_free_coherent); ++ ++#ifdef ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY ++int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, ++ dma_addr_t device_addr, size_t size, int flags) ++{ ++ void __iomem *mem_base; ++ int pages = size >> PAGE_SHIFT; ++ int bitmap_size = (pages + 31)/32; ++ ++ if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) ++ goto out; ++ if (!size) ++ goto out; ++ if (dev->dma_mem) ++ goto out; ++ ++ /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ ++ ++ mem_base = ioremap(bus_addr, size); ++ if (!mem_base) ++ goto out; ++ ++ dev->dma_mem = kmalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); ++ if (!dev->dma_mem) ++ goto out; ++ memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem)); ++ dev->dma_mem->bitmap = kmalloc(bitmap_size, GFP_KERNEL); ++ if (!dev->dma_mem->bitmap) ++ goto free1_out; ++ memset(dev->dma_mem->bitmap, 0, bitmap_size); ++ ++ dev->dma_mem->virt_base = mem_base; ++ dev->dma_mem->device_base = device_addr; ++ dev->dma_mem->size = pages; ++ dev->dma_mem->flags = flags; ++ ++ if (flags & DMA_MEMORY_MAP) ++ return DMA_MEMORY_MAP; ++ ++ return DMA_MEMORY_IO; ++ ++ free1_out: ++ kfree(dev->dma_mem->bitmap); ++ out: ++ return 0; ++} ++EXPORT_SYMBOL(dma_declare_coherent_memory); ++ ++void dma_release_declared_memory(struct device *dev) ++{ ++ struct dma_coherent_mem *mem = dev->dma_mem; ++ ++ if(!mem) ++ return; ++ dev->dma_mem = NULL; ++ iounmap(mem->virt_base); ++ kfree(mem->bitmap); ++ kfree(mem); ++} ++EXPORT_SYMBOL(dma_release_declared_memory); ++ ++void *dma_mark_declared_memory_occupied(struct device *dev, ++ dma_addr_t device_addr, size_t size) ++{ ++ struct dma_coherent_mem *mem = dev->dma_mem; ++ int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ int pos, err; ++ ++ if (!mem) ++ return ERR_PTR(-EINVAL); ++ ++ pos = (device_addr - mem->device_base) >> PAGE_SHIFT; ++ err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages)); ++ if (err != 0) ++ return ERR_PTR(err); ++ return mem->virt_base + (pos << PAGE_SHIFT); ++} ++EXPORT_SYMBOL(dma_mark_declared_memory_occupied); ++#endif /* ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY */ ++ ++dma_addr_t ++dma_map_single(struct device *dev, void *ptr, size_t size, ++ enum dma_data_direction direction) ++{ ++ dma_addr_t dma; ++ ++ if (direction == DMA_NONE) ++ BUG(); ++ WARN_ON(size == 0); ++ ++ if (swiotlb) { ++ dma = swiotlb_map_single(dev, ptr, size, direction); ++ } else { ++ dma = gnttab_dma_map_page(virt_to_page(ptr)) + ++ offset_in_page(ptr); ++ IOMMU_BUG_ON(range_straddles_page_boundary(__pa(ptr), size)); ++ IOMMU_BUG_ON(address_needs_mapping(dev, dma)); ++ } ++ ++ flush_write_buffers(); ++ return dma; ++} ++EXPORT_SYMBOL(dma_map_single); ++ ++void ++dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, ++ enum dma_data_direction direction) ++{ ++ if (direction == DMA_NONE) ++ BUG(); ++ if (swiotlb) ++ swiotlb_unmap_single(dev, dma_addr, size, direction); ++ else ++ gnttab_dma_unmap_page(dma_addr); ++} ++EXPORT_SYMBOL(dma_unmap_single); ++ ++void ++dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, ++ enum dma_data_direction direction) ++{ ++ if (swiotlb) ++ swiotlb_sync_single_for_cpu(dev, dma_handle, size, direction); ++} ++EXPORT_SYMBOL(dma_sync_single_for_cpu); ++ ++void ++dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, ++ enum dma_data_direction direction) ++{ ++ if (swiotlb) ++ swiotlb_sync_single_for_device(dev, dma_handle, size, direction); ++} ++EXPORT_SYMBOL(dma_sync_single_for_device); +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/process_32-xen.c 2008-07-21 11:00:32.000000000 +0200 +@@ -0,0 +1,877 @@ ++/* ++ * linux/arch/i386/kernel/process.c ++ * ++ * Copyright (C) 1995 Linus Torvalds ++ * ++ * Pentium III FXSR, SSE support ++ * Gareth Hughes , May 2000 ++ */ ++ ++/* ++ * This file handles the architecture-dependent parts of process handling.. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_MATH_EMULATION ++#include ++#endif ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); ++ ++static int hlt_counter; ++ ++unsigned long boot_option_idle_override = 0; ++EXPORT_SYMBOL(boot_option_idle_override); ++ ++/* ++ * Return saved PC of a blocked thread. ++ */ ++unsigned long thread_saved_pc(struct task_struct *tsk) ++{ ++ return ((unsigned long *)tsk->thread.esp)[3]; ++} ++ ++/* ++ * Powermanagement idle function, if any.. ++ */ ++void (*pm_idle)(void); ++EXPORT_SYMBOL(pm_idle); ++static DEFINE_PER_CPU(unsigned int, cpu_idle_state); ++ ++void disable_hlt(void) ++{ ++ hlt_counter++; ++} ++ ++EXPORT_SYMBOL(disable_hlt); ++ ++void enable_hlt(void) ++{ ++ hlt_counter--; ++} ++ ++EXPORT_SYMBOL(enable_hlt); ++ ++/* ++ * On SMP it's slightly faster (but much more power-consuming!) ++ * to poll the ->work.need_resched flag instead of waiting for the ++ * cross-CPU IPI to arrive. Use this option with caution. ++ */ ++static void poll_idle (void) ++{ ++ local_irq_enable(); ++ ++ asm volatile( ++ "2:" ++ "testl %0, %1;" ++ "rep; nop;" ++ "je 2b;" ++ : : "i"(_TIF_NEED_RESCHED), "m" (current_thread_info()->flags)); ++} ++ ++static void xen_idle(void) ++{ ++ local_irq_disable(); ++ ++ if (need_resched()) ++ local_irq_enable(); ++ else { ++ current_thread_info()->status &= ~TS_POLLING; ++ smp_mb__after_clear_bit(); ++ safe_halt(); ++ current_thread_info()->status |= TS_POLLING; ++ } ++} ++#ifdef CONFIG_APM_MODULE ++EXPORT_SYMBOL(default_idle); ++#endif ++ ++#ifdef CONFIG_HOTPLUG_CPU ++extern cpumask_t cpu_initialized; ++static inline void play_dead(void) ++{ ++ idle_task_exit(); ++ local_irq_disable(); ++ cpu_clear(smp_processor_id(), cpu_initialized); ++ preempt_enable_no_resched(); ++ VOID(HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL)); ++ cpu_bringup(); ++} ++#else ++static inline void play_dead(void) ++{ ++ BUG(); ++} ++#endif /* CONFIG_HOTPLUG_CPU */ ++ ++/* ++ * The idle thread. There's no useful work to be ++ * done, so just try to conserve power and have a ++ * low exit latency (ie sit in a loop waiting for ++ * somebody to say that they'd like to reschedule) ++ */ ++void cpu_idle(void) ++{ ++ int cpu = smp_processor_id(); ++ ++ current_thread_info()->status |= TS_POLLING; ++ ++ /* endless idle loop with no priority at all */ ++ while (1) { ++ while (!need_resched()) { ++ void (*idle)(void); ++ ++ if (__get_cpu_var(cpu_idle_state)) ++ __get_cpu_var(cpu_idle_state) = 0; ++ ++ rmb(); ++ idle = xen_idle; /* no alternatives */ ++ ++ if (cpu_is_offline(cpu)) ++ play_dead(); ++ ++ __get_cpu_var(irq_stat).idle_timestamp = jiffies; ++ idle(); ++ } ++ preempt_enable_no_resched(); ++ schedule(); ++ preempt_disable(); ++ } ++} ++ ++void cpu_idle_wait(void) ++{ ++ unsigned int cpu, this_cpu = get_cpu(); ++ cpumask_t map; ++ ++ set_cpus_allowed(current, cpumask_of_cpu(this_cpu)); ++ put_cpu(); ++ ++ cpus_clear(map); ++ for_each_online_cpu(cpu) { ++ per_cpu(cpu_idle_state, cpu) = 1; ++ cpu_set(cpu, map); ++ } ++ ++ __get_cpu_var(cpu_idle_state) = 0; ++ ++ wmb(); ++ do { ++ ssleep(1); ++ for_each_online_cpu(cpu) { ++ if (cpu_isset(cpu, map) && !per_cpu(cpu_idle_state, cpu)) ++ cpu_clear(cpu, map); ++ } ++ cpus_and(map, map, cpu_online_map); ++ } while (!cpus_empty(map)); ++} ++EXPORT_SYMBOL_GPL(cpu_idle_wait); ++ ++void __devinit select_idle_routine(const struct cpuinfo_x86 *c) ++{ ++} ++ ++static int __init idle_setup (char *str) ++{ ++ if (!strncmp(str, "poll", 4)) { ++ printk("using polling idle threads.\n"); ++ pm_idle = poll_idle; ++ } ++ ++ boot_option_idle_override = 1; ++ return 1; ++} ++ ++__setup("idle=", idle_setup); ++ ++void show_regs(struct pt_regs * regs) ++{ ++ unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L; ++ ++ printk("\n"); ++ printk("Pid: %d, comm: %20s\n", current->pid, current->comm); ++ printk("EIP: %04x:[<%08lx>] CPU: %d\n",0xffff & regs->xcs,regs->eip, smp_processor_id()); ++ print_symbol("EIP is at %s\n", regs->eip); ++ ++ if (user_mode_vm(regs)) ++ printk(" ESP: %04x:%08lx",0xffff & regs->xss,regs->esp); ++ printk(" EFLAGS: %08lx %s (%s %.*s)\n", ++ regs->eflags, print_tainted(), system_utsname.release, ++ (int)strcspn(system_utsname.version, " "), ++ system_utsname.version); ++ printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", ++ regs->eax,regs->ebx,regs->ecx,regs->edx); ++ printk("ESI: %08lx EDI: %08lx EBP: %08lx", ++ regs->esi, regs->edi, regs->ebp); ++ printk(" DS: %04x ES: %04x\n", ++ 0xffff & regs->xds,0xffff & regs->xes); ++ ++ cr0 = read_cr0(); ++ cr2 = read_cr2(); ++ cr3 = read_cr3(); ++ cr4 = read_cr4_safe(); ++ printk("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n", cr0, cr2, cr3, cr4); ++ show_trace(NULL, regs, ®s->esp); ++} ++ ++/* ++ * This gets run with %ebx containing the ++ * function to call, and %edx containing ++ * the "args". ++ */ ++extern void kernel_thread_helper(void); ++__asm__(".section .text\n" ++ ".align 4\n" ++ "kernel_thread_helper:\n\t" ++ "movl %edx,%eax\n\t" ++ "pushl %edx\n\t" ++ "call *%ebx\n\t" ++ "pushl %eax\n\t" ++ "call do_exit\n" ++ ".previous"); ++ ++/* ++ * Create a kernel thread ++ */ ++int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) ++{ ++ struct pt_regs regs; ++ ++ memset(®s, 0, sizeof(regs)); ++ ++ regs.ebx = (unsigned long) fn; ++ regs.edx = (unsigned long) arg; ++ ++ regs.xds = __USER_DS; ++ regs.xes = __USER_DS; ++ regs.orig_eax = -1; ++ regs.eip = (unsigned long) kernel_thread_helper; ++ regs.xcs = GET_KERNEL_CS(); ++ regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2; ++ ++ /* Ok, create the new process.. */ ++ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); ++} ++EXPORT_SYMBOL(kernel_thread); ++ ++/* ++ * Free current thread data structures etc.. ++ */ ++void exit_thread(void) ++{ ++ /* The process may have allocated an io port bitmap... nuke it. */ ++ if (unlikely(test_thread_flag(TIF_IO_BITMAP))) { ++ struct task_struct *tsk = current; ++ struct thread_struct *t = &tsk->thread; ++ struct physdev_set_iobitmap set_iobitmap; ++ memset(&set_iobitmap, 0, sizeof(set_iobitmap)); ++ WARN_ON(HYPERVISOR_physdev_op(PHYSDEVOP_set_iobitmap, ++ &set_iobitmap)); ++ kfree(t->io_bitmap_ptr); ++ t->io_bitmap_ptr = NULL; ++ clear_thread_flag(TIF_IO_BITMAP); ++ } ++} ++ ++void flush_thread(void) ++{ ++ struct task_struct *tsk = current; ++ ++ memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8); ++ memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); ++ clear_tsk_thread_flag(tsk, TIF_DEBUG); ++ /* ++ * Forget coprocessor state.. ++ */ ++ clear_fpu(tsk); ++ clear_used_math(); ++} ++ ++void release_thread(struct task_struct *dead_task) ++{ ++ BUG_ON(dead_task->mm); ++ release_vm86_irqs(dead_task); ++} ++ ++/* ++ * This gets called before we allocate a new thread and copy ++ * the current task into it. ++ */ ++void prepare_to_copy(struct task_struct *tsk) ++{ ++ unlazy_fpu(tsk); ++} ++ ++int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, ++ unsigned long unused, ++ struct task_struct * p, struct pt_regs * regs) ++{ ++ struct pt_regs * childregs; ++ struct task_struct *tsk; ++ int err; ++ ++ childregs = task_pt_regs(p); ++ *childregs = *regs; ++ childregs->eax = 0; ++ childregs->esp = esp; ++ ++ p->thread.esp = (unsigned long) childregs; ++ p->thread.esp0 = (unsigned long) (childregs+1); ++ ++ p->thread.eip = (unsigned long) ret_from_fork; ++ ++ savesegment(fs,p->thread.fs); ++ savesegment(gs,p->thread.gs); ++ ++ tsk = current; ++ if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) { ++ p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); ++ if (!p->thread.io_bitmap_ptr) { ++ p->thread.io_bitmap_max = 0; ++ return -ENOMEM; ++ } ++ memcpy(p->thread.io_bitmap_ptr, tsk->thread.io_bitmap_ptr, ++ IO_BITMAP_BYTES); ++ set_tsk_thread_flag(p, TIF_IO_BITMAP); ++ } ++ ++ /* ++ * Set a new TLS for the child thread? ++ */ ++ if (clone_flags & CLONE_SETTLS) { ++ struct desc_struct *desc; ++ struct user_desc info; ++ int idx; ++ ++ err = -EFAULT; ++ if (copy_from_user(&info, (void __user *)childregs->esi, sizeof(info))) ++ goto out; ++ err = -EINVAL; ++ if (LDT_empty(&info)) ++ goto out; ++ ++ idx = info.entry_number; ++ if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) ++ goto out; ++ ++ desc = p->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; ++ desc->a = LDT_entry_a(&info); ++ desc->b = LDT_entry_b(&info); ++ } ++ ++ p->thread.iopl = current->thread.iopl; ++ ++ err = 0; ++ out: ++ if (err && p->thread.io_bitmap_ptr) { ++ kfree(p->thread.io_bitmap_ptr); ++ p->thread.io_bitmap_max = 0; ++ } ++ return err; ++} ++ ++/* ++ * fill in the user structure for a core dump.. ++ */ ++void dump_thread(struct pt_regs * regs, struct user * dump) ++{ ++ int i; ++ ++/* changed the size calculations - should hopefully work better. lbt */ ++ dump->magic = CMAGIC; ++ dump->start_code = 0; ++ dump->start_stack = regs->esp & ~(PAGE_SIZE - 1); ++ dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; ++ dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; ++ dump->u_dsize -= dump->u_tsize; ++ dump->u_ssize = 0; ++ for (i = 0; i < 8; i++) ++ dump->u_debugreg[i] = current->thread.debugreg[i]; ++ ++ if (dump->start_stack < TASK_SIZE) ++ dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; ++ ++ dump->regs.ebx = regs->ebx; ++ dump->regs.ecx = regs->ecx; ++ dump->regs.edx = regs->edx; ++ dump->regs.esi = regs->esi; ++ dump->regs.edi = regs->edi; ++ dump->regs.ebp = regs->ebp; ++ dump->regs.eax = regs->eax; ++ dump->regs.ds = regs->xds; ++ dump->regs.es = regs->xes; ++ savesegment(fs,dump->regs.fs); ++ savesegment(gs,dump->regs.gs); ++ dump->regs.orig_eax = regs->orig_eax; ++ dump->regs.eip = regs->eip; ++ dump->regs.cs = regs->xcs; ++ dump->regs.eflags = regs->eflags; ++ dump->regs.esp = regs->esp; ++ dump->regs.ss = regs->xss; ++ ++ dump->u_fpvalid = dump_fpu (regs, &dump->i387); ++} ++EXPORT_SYMBOL(dump_thread); ++ ++/* ++ * Capture the user space registers if the task is not running (in user space) ++ */ ++int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) ++{ ++ struct pt_regs ptregs = *task_pt_regs(tsk); ++ ptregs.xcs &= 0xffff; ++ ptregs.xds &= 0xffff; ++ ptregs.xes &= 0xffff; ++ ptregs.xss &= 0xffff; ++ ++ elf_core_copy_regs(regs, &ptregs); ++ ++ return 1; ++} ++ ++static noinline void __switch_to_xtra(struct task_struct *next_p) ++{ ++ struct thread_struct *next; ++ ++ next = &next_p->thread; ++ ++ if (test_tsk_thread_flag(next_p, TIF_DEBUG)) { ++ set_debugreg(next->debugreg[0], 0); ++ set_debugreg(next->debugreg[1], 1); ++ set_debugreg(next->debugreg[2], 2); ++ set_debugreg(next->debugreg[3], 3); ++ /* no 4 and 5 */ ++ set_debugreg(next->debugreg[6], 6); ++ set_debugreg(next->debugreg[7], 7); ++ } ++} ++ ++/* ++ * This function selects if the context switch from prev to next ++ * has to tweak the TSC disable bit in the cr4. ++ */ ++static inline void disable_tsc(struct task_struct *prev_p, ++ struct task_struct *next_p) ++{ ++ struct thread_info *prev, *next; ++ ++ /* ++ * gcc should eliminate the ->thread_info dereference if ++ * has_secure_computing returns 0 at compile time (SECCOMP=n). ++ */ ++ prev = task_thread_info(prev_p); ++ next = task_thread_info(next_p); ++ ++ if (has_secure_computing(prev) || has_secure_computing(next)) { ++ /* slow path here */ ++ if (has_secure_computing(prev) && ++ !has_secure_computing(next)) { ++ write_cr4(read_cr4() & ~X86_CR4_TSD); ++ } else if (!has_secure_computing(prev) && ++ has_secure_computing(next)) ++ write_cr4(read_cr4() | X86_CR4_TSD); ++ } ++} ++ ++/* ++ * switch_to(x,yn) should switch tasks from x to y. ++ * ++ * We fsave/fwait so that an exception goes off at the right time ++ * (as a call from the fsave or fwait in effect) rather than to ++ * the wrong process. Lazy FP saving no longer makes any sense ++ * with modern CPU's, and this simplifies a lot of things (SMP ++ * and UP become the same). ++ * ++ * NOTE! We used to use the x86 hardware context switching. The ++ * reason for not using it any more becomes apparent when you ++ * try to recover gracefully from saved state that is no longer ++ * valid (stale segment register values in particular). With the ++ * hardware task-switch, there is no way to fix up bad state in ++ * a reasonable manner. ++ * ++ * The fact that Intel documents the hardware task-switching to ++ * be slow is a fairly red herring - this code is not noticeably ++ * faster. However, there _is_ some room for improvement here, ++ * so the performance issues may eventually be a valid point. ++ * More important, however, is the fact that this allows us much ++ * more flexibility. ++ * ++ * The return value (in %eax) will be the "prev" task after ++ * the task-switch, and shows up in ret_from_fork in entry.S, ++ * for example. ++ */ ++struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct task_struct *next_p) ++{ ++ struct thread_struct *prev = &prev_p->thread, ++ *next = &next_p->thread; ++ int cpu = smp_processor_id(); ++#ifndef CONFIG_X86_NO_TSS ++ struct tss_struct *tss = &per_cpu(init_tss, cpu); ++#endif ++#if CONFIG_XEN_COMPAT > 0x030002 ++ struct physdev_set_iopl iopl_op; ++ struct physdev_set_iobitmap iobmp_op; ++#else ++ struct physdev_op _pdo[2], *pdo = _pdo; ++#define iopl_op pdo->u.set_iopl ++#define iobmp_op pdo->u.set_iobitmap ++#endif ++ multicall_entry_t _mcl[8], *mcl = _mcl; ++ ++ /* XEN NOTE: FS/GS saved in switch_mm(), not here. */ ++ ++ /* ++ * This is basically '__unlazy_fpu', except that we queue a ++ * multicall to indicate FPU task switch, rather than ++ * synchronously trapping to Xen. ++ */ ++ if (prev_p->thread_info->status & TS_USEDFPU) { ++ __save_init_fpu(prev_p); /* _not_ save_init_fpu() */ ++ mcl->op = __HYPERVISOR_fpu_taskswitch; ++ mcl->args[0] = 1; ++ mcl++; ++ } ++#if 0 /* lazy fpu sanity check */ ++ else BUG_ON(!(read_cr0() & 8)); ++#endif ++ ++ /* ++ * Reload esp0. ++ * This is load_esp0(tss, next) with a multicall. ++ */ ++ mcl->op = __HYPERVISOR_stack_switch; ++ mcl->args[0] = __KERNEL_DS; ++ mcl->args[1] = next->esp0; ++ mcl++; ++ ++ /* ++ * Load the per-thread Thread-Local Storage descriptor. ++ * This is load_TLS(next, cpu) with multicalls. ++ */ ++#define C(i) do { \ ++ if (unlikely(next->tls_array[i].a != prev->tls_array[i].a || \ ++ next->tls_array[i].b != prev->tls_array[i].b)) { \ ++ mcl->op = __HYPERVISOR_update_descriptor; \ ++ *(u64 *)&mcl->args[0] = virt_to_machine( \ ++ &get_cpu_gdt_table(cpu)[GDT_ENTRY_TLS_MIN + i]);\ ++ *(u64 *)&mcl->args[2] = *(u64 *)&next->tls_array[i]; \ ++ mcl++; \ ++ } \ ++} while (0) ++ C(0); C(1); C(2); ++#undef C ++ ++ if (unlikely(prev->iopl != next->iopl)) { ++ iopl_op.iopl = (next->iopl == 0) ? 1 : (next->iopl >> 12) & 3; ++#if CONFIG_XEN_COMPAT > 0x030002 ++ mcl->op = __HYPERVISOR_physdev_op; ++ mcl->args[0] = PHYSDEVOP_set_iopl; ++ mcl->args[1] = (unsigned long)&iopl_op; ++#else ++ mcl->op = __HYPERVISOR_physdev_op_compat; ++ pdo->cmd = PHYSDEVOP_set_iopl; ++ mcl->args[0] = (unsigned long)pdo++; ++#endif ++ mcl++; ++ } ++ ++ if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) { ++ set_xen_guest_handle(iobmp_op.bitmap, ++ (char *)next->io_bitmap_ptr); ++ iobmp_op.nr_ports = next->io_bitmap_ptr ? IO_BITMAP_BITS : 0; ++#if CONFIG_XEN_COMPAT > 0x030002 ++ mcl->op = __HYPERVISOR_physdev_op; ++ mcl->args[0] = PHYSDEVOP_set_iobitmap; ++ mcl->args[1] = (unsigned long)&iobmp_op; ++#else ++ mcl->op = __HYPERVISOR_physdev_op_compat; ++ pdo->cmd = PHYSDEVOP_set_iobitmap; ++ mcl->args[0] = (unsigned long)pdo++; ++#endif ++ mcl++; ++ } ++ ++#if CONFIG_XEN_COMPAT <= 0x030002 ++ BUG_ON(pdo > _pdo + ARRAY_SIZE(_pdo)); ++#endif ++ BUG_ON(mcl > _mcl + ARRAY_SIZE(_mcl)); ++ if (unlikely(HYPERVISOR_multicall_check(_mcl, mcl - _mcl, NULL))) ++ BUG(); ++ ++ /* ++ * Restore %fs and %gs if needed. ++ * ++ * Glibc normally makes %fs be zero, and %gs is one of ++ * the TLS segments. ++ */ ++ if (unlikely(next->fs)) ++ loadsegment(fs, next->fs); ++ ++ if (next->gs) ++ loadsegment(gs, next->gs); ++ ++ /* ++ * Now maybe handle debug registers ++ */ ++ if (unlikely(task_thread_info(next_p)->flags & _TIF_WORK_CTXSW)) ++ __switch_to_xtra(next_p); ++ ++ disable_tsc(prev_p, next_p); ++ ++ return prev_p; ++} ++ ++asmlinkage int sys_fork(struct pt_regs regs) ++{ ++ return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL); ++} ++ ++asmlinkage int sys_clone(struct pt_regs regs) ++{ ++ unsigned long clone_flags; ++ unsigned long newsp; ++ int __user *parent_tidptr, *child_tidptr; ++ ++ clone_flags = regs.ebx; ++ newsp = regs.ecx; ++ parent_tidptr = (int __user *)regs.edx; ++ child_tidptr = (int __user *)regs.edi; ++ if (!newsp) ++ newsp = regs.esp; ++ return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr); ++} ++ ++/* ++ * This is trivial, and on the face of it looks like it ++ * could equally well be done in user mode. ++ * ++ * Not so, for quite unobvious reasons - register pressure. ++ * In user mode vfork() cannot have a stack frame, and if ++ * done by calling the "clone()" system call directly, you ++ * do not have enough call-clobbered registers to hold all ++ * the information you need. ++ */ ++asmlinkage int sys_vfork(struct pt_regs regs) ++{ ++ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL); ++} ++ ++/* ++ * sys_execve() executes a new program. ++ */ ++asmlinkage int sys_execve(struct pt_regs regs) ++{ ++ int error; ++ char * filename; ++ ++ filename = getname((char __user *) regs.ebx); ++ error = PTR_ERR(filename); ++ if (IS_ERR(filename)) ++ goto out; ++ error = do_execve(filename, ++ (char __user * __user *) regs.ecx, ++ (char __user * __user *) regs.edx, ++ ®s); ++ if (error == 0) { ++ task_lock(current); ++ current->ptrace &= ~PT_DTRACE; ++ task_unlock(current); ++ /* Make sure we don't return using sysenter.. */ ++ set_thread_flag(TIF_IRET); ++ } ++ putname(filename); ++out: ++ return error; ++} ++ ++#define top_esp (THREAD_SIZE - sizeof(unsigned long)) ++#define top_ebp (THREAD_SIZE - 2*sizeof(unsigned long)) ++ ++unsigned long get_wchan(struct task_struct *p) ++{ ++ unsigned long ebp, esp, eip; ++ unsigned long stack_page; ++ int count = 0; ++ if (!p || p == current || p->state == TASK_RUNNING) ++ return 0; ++ stack_page = (unsigned long)task_stack_page(p); ++ esp = p->thread.esp; ++ if (!stack_page || esp < stack_page || esp > top_esp+stack_page) ++ return 0; ++ /* include/asm-i386/system.h:switch_to() pushes ebp last. */ ++ ebp = *(unsigned long *) esp; ++ do { ++ if (ebp < stack_page || ebp > top_ebp+stack_page) ++ return 0; ++ eip = *(unsigned long *) (ebp+4); ++ if (!in_sched_functions(eip)) ++ return eip; ++ ebp = *(unsigned long *) ebp; ++ } while (count++ < 16); ++ return 0; ++} ++ ++/* ++ * sys_alloc_thread_area: get a yet unused TLS descriptor index. ++ */ ++static int get_free_idx(void) ++{ ++ struct thread_struct *t = ¤t->thread; ++ int idx; ++ ++ for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) ++ if (desc_empty(t->tls_array + idx)) ++ return idx + GDT_ENTRY_TLS_MIN; ++ return -ESRCH; ++} ++ ++/* ++ * Set a given TLS descriptor: ++ */ ++asmlinkage int sys_set_thread_area(struct user_desc __user *u_info) ++{ ++ struct thread_struct *t = ¤t->thread; ++ struct user_desc info; ++ struct desc_struct *desc; ++ int cpu, idx; ++ ++ if (copy_from_user(&info, u_info, sizeof(info))) ++ return -EFAULT; ++ idx = info.entry_number; ++ ++ /* ++ * index -1 means the kernel should try to find and ++ * allocate an empty descriptor: ++ */ ++ if (idx == -1) { ++ idx = get_free_idx(); ++ if (idx < 0) ++ return idx; ++ if (put_user(idx, &u_info->entry_number)) ++ return -EFAULT; ++ } ++ ++ if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) ++ return -EINVAL; ++ ++ desc = t->tls_array + idx - GDT_ENTRY_TLS_MIN; ++ ++ /* ++ * We must not get preempted while modifying the TLS. ++ */ ++ cpu = get_cpu(); ++ ++ if (LDT_empty(&info)) { ++ desc->a = 0; ++ desc->b = 0; ++ } else { ++ desc->a = LDT_entry_a(&info); ++ desc->b = LDT_entry_b(&info); ++ } ++ load_TLS(t, cpu); ++ ++ put_cpu(); ++ ++ return 0; ++} ++ ++/* ++ * Get the current Thread-Local Storage area: ++ */ ++ ++#define GET_BASE(desc) ( \ ++ (((desc)->a >> 16) & 0x0000ffff) | \ ++ (((desc)->b << 16) & 0x00ff0000) | \ ++ ( (desc)->b & 0xff000000) ) ++ ++#define GET_LIMIT(desc) ( \ ++ ((desc)->a & 0x0ffff) | \ ++ ((desc)->b & 0xf0000) ) ++ ++#define GET_32BIT(desc) (((desc)->b >> 22) & 1) ++#define GET_CONTENTS(desc) (((desc)->b >> 10) & 3) ++#define GET_WRITABLE(desc) (((desc)->b >> 9) & 1) ++#define GET_LIMIT_PAGES(desc) (((desc)->b >> 23) & 1) ++#define GET_PRESENT(desc) (((desc)->b >> 15) & 1) ++#define GET_USEABLE(desc) (((desc)->b >> 20) & 1) ++ ++asmlinkage int sys_get_thread_area(struct user_desc __user *u_info) ++{ ++ struct user_desc info; ++ struct desc_struct *desc; ++ int idx; ++ ++ if (get_user(idx, &u_info->entry_number)) ++ return -EFAULT; ++ if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) ++ return -EINVAL; ++ ++ memset(&info, 0, sizeof(info)); ++ ++ desc = current->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; ++ ++ info.entry_number = idx; ++ info.base_addr = GET_BASE(desc); ++ info.limit = GET_LIMIT(desc); ++ info.seg_32bit = GET_32BIT(desc); ++ info.contents = GET_CONTENTS(desc); ++ info.read_exec_only = !GET_WRITABLE(desc); ++ info.limit_in_pages = GET_LIMIT_PAGES(desc); ++ info.seg_not_present = !GET_PRESENT(desc); ++ info.useable = GET_USEABLE(desc); ++ ++ if (copy_to_user(u_info, &info, sizeof(info))) ++ return -EFAULT; ++ return 0; ++} ++ ++unsigned long arch_align_stack(unsigned long sp) ++{ ++ if (randomize_va_space) ++ sp -= get_random_int() % 8192; ++ return sp & ~0xf; ++} +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/quirks-xen.c 2008-01-28 12:24:19.000000000 +0100 +@@ -0,0 +1,47 @@ ++/* ++ * This file contains work-arounds for x86 and x86_64 platform bugs. ++ */ ++#include ++#include ++ ++#if defined(CONFIG_X86_IO_APIC) && (defined(CONFIG_SMP) || defined(CONFIG_XEN)) && defined(CONFIG_PCI) ++ ++static void __devinit quirk_intel_irqbalance(struct pci_dev *dev) ++{ ++ u8 config, rev; ++ u32 word; ++ ++ /* BIOS may enable hardware IRQ balancing for ++ * E7520/E7320/E7525(revision ID 0x9 and below) ++ * based platforms. ++ * Disable SW irqbalance/affinity on those platforms. ++ */ ++ pci_read_config_byte(dev, PCI_CLASS_REVISION, &rev); ++ if (rev > 0x9) ++ return; ++ ++ printk(KERN_INFO "Intel E7520/7320/7525 detected."); ++ ++ /* enable access to config space*/ ++ pci_read_config_byte(dev, 0xf4, &config); ++ pci_write_config_byte(dev, 0xf4, config|0x2); ++ ++ /* read xTPR register */ ++ raw_pci_ops->read(0, 0, 0x40, 0x4c, 2, &word); ++ ++ if (!(word & (1 << 13))) { ++ struct xen_platform_op op; ++ printk(KERN_INFO "Disabling irq balancing and affinity\n"); ++ op.cmd = XENPF_platform_quirk; ++ op.u.platform_quirk.quirk_id = QUIRK_NOIRQBALANCING; ++ WARN_ON(HYPERVISOR_platform_op(&op)); ++ } ++ ++ /* put back the original value for config space*/ ++ if (!(config & 0x2)) ++ pci_write_config_byte(dev, 0xf4, config); ++} ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7320_MCH, quirk_intel_irqbalance); ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quirk_intel_irqbalance); ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quirk_intel_irqbalance); ++#endif +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ head-2010-04-29/arch/x86/kernel/setup_32-xen.c 2008-04-22 15:41:51.000000000 +0200 +@@ -0,0 +1,1919 @@ ++/* ++ * linux/arch/i386/kernel/setup.c ++ * ++ * Copyright (C) 1995 Linus Torvalds ++ * ++ * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 ++ * ++ * Memory region support ++ * David Parsons , July-August 1999 ++ * ++ * Added E820 sanitization routine (removes overlapping memory regions); ++ * Brian Moyle , February 2001 ++ * ++ * Moved CPU detection code to cpu/${cpu}.c ++ * Patrick Mochel , March 2002 ++ * ++ * Provisions for empty E820 memory regions (reported by certain BIOSes). ++ * Alex Achenbach , December 2002. ++ * ++ */ ++ ++/* ++ * This file handles the architecture-dependent parts of initialization ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include