diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-23 00:40:54 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-23 00:40:54 +0000 |
commit | 529c593ece216e4aaffd36bd940cb94f1fa63129 (patch) | |
tree | 78f1c0b805f5656aa7b0417a043c5346f700a2cf /arch | |
parent | 0bd079751d25808d1972baee5c4eaa1db2227257 (diff) |
Merge with 2.3.43. I did ignore all modifications to the qlogicisp.c
driver due to the Origin A64 hacks.
Diffstat (limited to 'arch')
261 files changed, 32149 insertions, 2582 deletions
diff --git a/arch/alpha/config.in b/arch/alpha/config.in index ce5c0853e..8e44bb0e2 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -55,12 +55,16 @@ choice 'Alpha system type' \ # clear all implied options (don't want default values for those): unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 CONFIG_ALPHA_EV6 -unset CONFIG_PCI CONFIG_ALPHA_EISA +unset CONFIG_PCI CONFIG_ISA CONFIG_ALPHA_EISA unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS CONFIG_ALPHA_POLARIS unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA unset CONFIG_ALPHA_IRONGATE +# Most of these machines have ISA slots; not exactly sure which don't, +# and this doesn't activate hordes of code, so do it always. +define_bool CONFIG_ISA y + if [ "$CONFIG_ALPHA_GENERIC" = "y" ] then define_bool CONFIG_PCI y diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index 9210ae57c..7cf5dae80 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -26,11 +26,11 @@ O_OBJS += core_apecs.o core_cia.o core_irongate.o core_lca.o core_mcpcia.o \ sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o \ sys_noritake.o sys_rawhide.o sys_ruffian.o sys_rx164.o \ sys_sable.o sys_sio.o sys_sx164.o sys_takara.o sys_rx164.o \ - es1888.o smc37c669.o smc37c93x.o ns87312.o pci.o + es1888.o smc37c669.o smc37c93x.o ns87312.o pci.o pci_iommu.o else ifdef CONFIG_PCI -O_OBJS += pci.o +O_OBJS += pci.o pci_iommu.o endif # Core logic support diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c index 877926974..15c7afd8c 100644 --- a/arch/alpha/kernel/alpha_ksyms.c +++ b/arch/alpha/kernel/alpha_ksyms.c @@ -149,6 +149,9 @@ EXPORT_SYMBOL(__strnlen_user); EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); EXPORT_SYMBOL_NOVERS(__up_wakeup); +EXPORT_SYMBOL_NOVERS(__down_read_failed); +EXPORT_SYMBOL_NOVERS(__down_write_failed); +EXPORT_SYMBOL_NOVERS(__rwsem_wake); /* * SMP-specific symbols. @@ -161,10 +164,7 @@ EXPORT_SYMBOL(flush_tlb_mm); EXPORT_SYMBOL(flush_tlb_page); EXPORT_SYMBOL(flush_tlb_range); EXPORT_SYMBOL(cpu_data); -EXPORT_SYMBOL(cpu_number_map); -EXPORT_SYMBOL(global_bh_lock); -EXPORT_SYMBOL(global_bh_count); -EXPORT_SYMBOL(synchronize_bh); +EXPORT_SYMBOL(__cpu_number_map); EXPORT_SYMBOL(global_irq_holder); EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); diff --git a/arch/alpha/kernel/core_apecs.c b/arch/alpha/kernel/core_apecs.c index 04e556f2e..9ea4f53e9 100644 --- a/arch/alpha/kernel/core_apecs.c +++ b/arch/alpha/kernel/core_apecs.c @@ -356,22 +356,49 @@ struct pci_ops apecs_pci_ops = write_dword: apecs_write_config_dword }; +void +apecs_pci_tbi(struct pci_controler *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + *(vip)APECS_IOC_TBIA = 0; + mb(); +} + void __init apecs_init_arch(void) { struct pci_controler *hose; /* - * Set up the PCI->physical memory translation windows. - * For now, window 2 is disabled. In the future, we may - * want to use it to do scatter/gather DMA. Window 1 - * goes at 1 GB and is 1 GB large. + * Create our single hose. */ - *(vuip)APECS_IOC_PB1R = 1UL << 19 | (APECS_DMA_WIN_BASE & 0xfff00000U); - *(vuip)APECS_IOC_PM1R = (APECS_DMA_WIN_SIZE - 1) & 0xfff00000U; + + pci_isa_hose = hose = alloc_pci_controler(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->config_space = APECS_CONF; + hose->index = 0; + + /* + * Set up the PCI to main memory translation windows. + * + * Window 1 is direct access 1GB at 1GB + * Window 2 is scatter-gather 8MB at 8MB (for isa) + */ + hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, PAGE_SIZE); + hose->sg_pci = NULL; + __direct_map_base = 0x40000000; + __direct_map_size = 0x40000000; + + *(vuip)APECS_IOC_PB1R = __direct_map_base | 0x00080000; + *(vuip)APECS_IOC_PM1R = (__direct_map_size - 1) & 0xfff00000U; *(vuip)APECS_IOC_TB1R = 0; - *(vuip)APECS_IOC_PB2R = 0U; /* disable window 2 */ + *(vuip)APECS_IOC_PB2R = hose->sg_isa->dma_base | 0x000c0000; + *(vuip)APECS_IOC_PM2R = (hose->sg_isa->size - 1) & 0xfff00000; + *(vuip)APECS_IOC_TB2R = virt_to_phys(hose->sg_isa->ptes) >> 1; + + apecs_pci_tbi(hose, 0, -1); /* * Finally, clear the HAXR2 register, which gets used @@ -381,16 +408,6 @@ apecs_init_arch(void) */ *(vuip)APECS_IOC_HAXR2 = 0; mb(); - - /* - * Create our single hose. - */ - - hose = alloc_pci_controler(); - hose->io_space = &ioport_resource; - hose->mem_space = &iomem_resource; - hose->config_space = APECS_CONF; - hose->index = 0; } void diff --git a/arch/alpha/kernel/core_cia.c b/arch/alpha/kernel/core_cia.c index 9f628bff6..36b9a1fa9 100644 --- a/arch/alpha/kernel/core_cia.c +++ b/arch/alpha/kernel/core_cia.c @@ -314,12 +314,20 @@ struct pci_ops cia_pci_ops = write_dword: cia_write_config_dword }; +void +cia_pci_tbi(struct pci_controler *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + *(vip)CIA_IOC_PCI_TBIA = 3; /* Flush all locked and unlocked. */ + mb(); +} + void __init cia_init_arch(void) { struct pci_controler *hose; struct resource *hae_mem; - unsigned int temp; + unsigned int temp; #if DEBUG_DUMP_REGS temp = *(vuip)CIA_IOC_CIA_REV; mb(); @@ -368,63 +376,11 @@ cia_init_arch(void) printk("cia_init: W3_BASE was 0x%x\n", temp); #endif /* DEBUG_DUMP_REGS */ - /* - * Set up error reporting. - */ - temp = *(vuip)CIA_IOC_CIA_ERR; - temp |= 0x180; /* master, target abort */ - *(vuip)CIA_IOC_CIA_ERR = temp; - mb(); - - temp = *(vuip)CIA_IOC_CIA_CTRL; - temp |= 0x400; /* turn on FILL_ERR to get mchecks */ - *(vuip)CIA_IOC_CIA_CTRL = temp; - mb(); - - /* - * Set up the PCI->physical memory translation windows. - * For now, windows 2 and 3 are disabled. In the future, - * we may want to use them to do scatter/gather DMA. - * - * Window 0 goes at 1 GB and is 1 GB large. - * Window 1 goes at 2 GB and is 1 GB large. - */ - - *(vuip)CIA_IOC_PCI_W0_BASE = CIA_DMA_WIN0_BASE_DEFAULT | 1U; - *(vuip)CIA_IOC_PCI_W0_MASK = (CIA_DMA_WIN0_SIZE_DEFAULT - 1) & - 0xfff00000U; - *(vuip)CIA_IOC_PCI_T0_BASE = CIA_DMA_WIN0_TRAN_DEFAULT >> 2; - - *(vuip)CIA_IOC_PCI_W1_BASE = CIA_DMA_WIN1_BASE_DEFAULT | 1U; - *(vuip)CIA_IOC_PCI_W1_MASK = (CIA_DMA_WIN1_SIZE_DEFAULT - 1) & - 0xfff00000U; - *(vuip)CIA_IOC_PCI_T1_BASE = CIA_DMA_WIN1_TRAN_DEFAULT >> 2; - - *(vuip)CIA_IOC_PCI_W2_BASE = 0x0; - *(vuip)CIA_IOC_PCI_W3_BASE = 0x0; - mb(); - - /* - * Next, clear the CIA_CFG register, which gets used - * for PCI Config Space accesses. That is the way - * we want to use it, and we do not want to depend on - * what ARC or SRM might have left behind... - */ - *((vuip)CIA_IOC_CFG) = 0; mb(); - - /* - * Zero the HAEs. - */ - *((vuip)CIA_IOC_HAE_MEM) = 0; mb(); - *((vuip)CIA_IOC_HAE_MEM); /* read it back. */ - *((vuip)CIA_IOC_HAE_IO) = 0; mb(); - *((vuip)CIA_IOC_HAE_IO); /* read it back. */ - /* * Create our single hose. */ - hose = alloc_pci_controler(); + pci_isa_hose = hose = alloc_pci_controler(); hae_mem = alloc_resource(); hose->io_space = &ioport_resource; @@ -439,6 +395,64 @@ cia_init_arch(void) if (request_resource(&iomem_resource, hae_mem) < 0) printk(KERN_ERR "Failed to request HAE_MEM\n"); + + /* + * Set up the PCI to main memory translation windows. + * + * Window 0 is scatter-gather 8MB at 8MB (for isa) + * Window 1 is scatter-gather 128MB at 1GB + * Window 2 is direct access 2GB at 2GB + * ??? We ought to scale window 1 with memory. + */ + + /* NetBSD hints that page tables must be aligned to 32K due + to a hardware bug. No description of what models affected. */ + hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, 32768); + hose->sg_pci = iommu_arena_new(0x40000000, 0x08000000, 32768); + __direct_map_base = 0x80000000; + __direct_map_size = 0x80000000; + + *(vuip)CIA_IOC_PCI_W0_BASE = hose->sg_isa->dma_base | 3; + *(vuip)CIA_IOC_PCI_W0_MASK = (hose->sg_isa->size - 1) & 0xfff00000; + *(vuip)CIA_IOC_PCI_T0_BASE = virt_to_phys(hose->sg_isa->ptes) >> 2; + + *(vuip)CIA_IOC_PCI_W1_BASE = hose->sg_pci->dma_base | 3; + *(vuip)CIA_IOC_PCI_W1_MASK = (hose->sg_pci->size - 1) & 0xfff00000; + *(vuip)CIA_IOC_PCI_T1_BASE = virt_to_phys(hose->sg_pci->ptes) >> 2; + + *(vuip)CIA_IOC_PCI_W2_BASE = __direct_map_base | 1; + *(vuip)CIA_IOC_PCI_W2_MASK = (__direct_map_size - 1) & 0xfff00000; + *(vuip)CIA_IOC_PCI_T2_BASE = 0; + + *(vuip)CIA_IOC_PCI_W3_BASE = 0; + + cia_pci_tbi(hose, 0, -1); + + /* + * Set up error reporting. + */ + temp = *(vuip)CIA_IOC_CIA_ERR; + temp |= 0x180; /* master, target abort */ + *(vuip)CIA_IOC_CIA_ERR = temp; + + temp = *(vuip)CIA_IOC_CIA_CTRL; + temp |= 0x400; /* turn on FILL_ERR to get mchecks */ + *(vuip)CIA_IOC_CIA_CTRL = temp; + + /* + * Next, clear the CIA_CFG register, which gets used + * for PCI Config Space accesses. That is the way + * we want to use it, and we do not want to depend on + * what ARC or SRM might have left behind... + */ + *(vuip)CIA_IOC_CFG = 0; + + /* + * Zero the HAEs. + */ + *(vuip)CIA_IOC_HAE_MEM = 0; + *(vuip)CIA_IOC_HAE_IO = 0; + mb(); } static inline void @@ -456,6 +470,8 @@ void cia_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { + int expected; + /* Clear the error before any reporting. */ mb(); mb(); /* magic */ @@ -464,5 +480,23 @@ cia_machine_check(unsigned long vector, unsigned long la_ptr, wrmces(rdmces()); /* reset machine check pending flag. */ mb(); - process_mcheck_info(vector, la_ptr, regs, "CIA", mcheck_expected(0)); + expected = mcheck_expected(0); + if (!expected && vector == 0x660) { + struct el_common *com; + struct el_common_EV5_uncorrectable_mcheck *ev5; + struct el_CIA_sysdata_mcheck *cia; + + com = (void *)la_ptr; + ev5 = (void *)(la_ptr + com->proc_offset); + cia = (void *)(la_ptr + com->sys_offset); + + if (com->code == 0x202) { + printk(KERN_CRIT "CIA PCI machine check: err0=%08x " + "err1=%08x err2=%08x\n", + (int) cia->pci_err0, (int) cia->pci_err1, + (int) cia->pci_err2); + expected = 1; + } + } + process_mcheck_info(vector, la_ptr, regs, "CIA", expected); } diff --git a/arch/alpha/kernel/core_irongate.c b/arch/alpha/kernel/core_irongate.c index 81615dbdf..5109f07ae 100644 --- a/arch/alpha/kernel/core_irongate.c +++ b/arch/alpha/kernel/core_irongate.c @@ -351,4 +351,8 @@ irongate_init_arch(void) hose->mem_space = &iomem_resource; hose->config_space = IRONGATE_CONF; hose->index = 0; + + hose->sg_isa = hose->sg_pci = NULL; + __direct_map_base = 0; + __direct_map_size = 0xffffffff; } diff --git a/arch/alpha/kernel/core_lca.c b/arch/alpha/kernel/core_lca.c index 266023f77..2bee78a1b 100644 --- a/arch/alpha/kernel/core_lca.c +++ b/arch/alpha/kernel/core_lca.c @@ -278,23 +278,51 @@ struct pci_ops lca_pci_ops = write_dword: lca_write_config_dword }; +void +lca_pci_tbi(struct pci_controler *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + *(vip)LCA_IOC_TBIA = 0; + mb(); +} + void __init lca_init_arch(void) { struct pci_controler *hose; /* - * Set up the PCI->physical memory translation windows. - * For now, window 1 is disabled. In the future, we may - * want to use it to do scatter/gather DMA. + * Create our single hose. + */ + + pci_isa_hose = hose = alloc_pci_controler(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->config_space = LCA_CONF; + hose->index = 0; + + /* + * Set up the PCI to main memory translation windows. * - * Window 0 goes at 1 GB and is 1 GB large. + * Window 0 is direct access 1GB at 1GB + * Window 1 is scatter-gather 8MB at 8MB (for isa) */ - *(vulp)LCA_IOC_W_BASE0 = 1UL << 33 | LCA_DMA_WIN_BASE; - *(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1; + hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, PAGE_SIZE); + hose->sg_pci = NULL; + __direct_map_base = 0x40000000; + __direct_map_size = 0x40000000; + + *(vulp)LCA_IOC_W_BASE0 = __direct_map_base | (2UL << 32); + *(vulp)LCA_IOC_W_MASK0 = (__direct_map_size - 1) & 0xfff00000; *(vulp)LCA_IOC_T_BASE0 = 0; - *(vulp)LCA_IOC_W_BASE1 = 0UL; + *(vulp)LCA_IOC_W_BASE1 = hose->sg_isa->dma_base | (3UL << 32); + *(vulp)LCA_IOC_W_MASK1 = (hose->sg_isa->size - 1) & 0xfff00000; + *(vulp)LCA_IOC_T_BASE1 = virt_to_phys(hose->sg_isa->ptes); + + *(vulp)LCA_IOC_TB_ENA = 0x80; + + lca_pci_tbi(hose, 0, -1); /* * Disable PCI parity for now. The NCR53c810 chip has @@ -302,16 +330,6 @@ lca_init_arch(void) * data parity errors. */ *(vulp)LCA_IOC_PAR_DIS = 1UL<<5; - - /* - * Create our single hose. - */ - - hose = alloc_pci_controler(); - hose->io_space = &ioport_resource; - hose->mem_space = &iomem_resource; - hose->config_space = LCA_CONF; - hose->index = 0; } /* diff --git a/arch/alpha/kernel/core_mcpcia.c b/arch/alpha/kernel/core_mcpcia.c index 847958212..19ceb2241 100644 --- a/arch/alpha/kernel/core_mcpcia.c +++ b/arch/alpha/kernel/core_mcpcia.c @@ -293,6 +293,14 @@ struct pci_ops mcpcia_pci_ops = write_dword: mcpcia_write_config_dword }; +void +mcpcia_pci_tbi(struct pci_controler *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + BUG(); + mb(); +} + static int __init mcpcia_probe_hose(int h) { @@ -395,31 +403,36 @@ mcpcia_startup_hose(struct pci_controler *hose) /* * Set up the PCI->physical memory translation windows. - * For now, windows 1,2 and 3 are disabled. In the - * future, we may want to use them to do scatter/ - * gather DMA. * - * Window 0 goes at 2 GB and is 2 GB large. + * Window 0 is scatter-gather 8MB at 8MB (for isa) + * Window 1 is scatter-gather 128MB at 1GB + * Window 2 is direct access 2GB at 2GB + * ??? We ought to scale window 1 with memory. */ - *(vuip)MCPCIA_W0_BASE(mid) = 1U | (MCPCIA_DMA_WIN_BASE & 0xfff00000U); - *(vuip)MCPCIA_W0_MASK(mid) = (MCPCIA_DMA_WIN_SIZE - 1) & 0xfff00000U; - *(vuip)MCPCIA_T0_BASE(mid) = 0; + hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, PAGE_SIZE); + hose->sg_pci = iommu_arena_new(0x40000000, 0x08000000, PAGE_SIZE); + __direct_map_base = 0x80000000; + __direct_map_size = 0x80000000; + + *(vuip)MCPCIA_W0_BASE(mid) = hose->sg_isa->dma_base | 3; + *(vuip)MCPCIA_W0_MASK(mid) = (hose->sg_isa->size - 1) & 0xfff00000; + *(vuip)MCPCIA_T0_BASE(mid) = virt_to_phys(hose->sg_isa->ptes) >> 2; + + *(vuip)MCPCIA_W1_BASE(mid) = hose->sg_pci->dma_base | 3; + *(vuip)MCPCIA_W1_MASK(mid) = (hose->sg_pci->size - 1) & 0xfff00000; + *(vuip)MCPCIA_T1_BASE(mid) = virt_to_phys(hose->sg_pci->ptes) >> 2; + + *(vuip)MCPCIA_W2_BASE(mid) = __direct_map_base | 1; + *(vuip)MCPCIA_W2_MASK(mid) = (__direct_map_size - 1) & 0xfff00000; + *(vuip)MCPCIA_T2_BASE(mid) = 0; - *(vuip)MCPCIA_W1_BASE(mid) = 0x0; - *(vuip)MCPCIA_W2_BASE(mid) = 0x0; *(vuip)MCPCIA_W3_BASE(mid) = 0x0; - *(vuip)MCPCIA_HBASE(mid) = 0x0; - mb(); + mcpcia_pci_tbi(hose, 0, -1); -#if 0 - tmp = *(vuip)MCPCIA_INT_CTL(mid); - printk("mcpcia_startup_hose: INT_CTL was 0x%x\n", tmp); - *(vuip)MCPCIA_INT_CTL(mid) = 1U; + *(vuip)MCPCIA_HBASE(mid) = 0x0; mb(); - tmp = *(vuip)MCPCIA_INT_CTL(mid); -#endif *(vuip)MCPCIA_HAE_MEM(mid) = 0U; mb(); diff --git a/arch/alpha/kernel/core_polaris.c b/arch/alpha/kernel/core_polaris.c index 972e707cb..0164a3e15 100644 --- a/arch/alpha/kernel/core_polaris.c +++ b/arch/alpha/kernel/core_polaris.c @@ -197,6 +197,12 @@ polaris_init_arch(void) hose->mem_space = &iomem_resource; hose->config_space = POLARIS_DENSE_CONFIG_BASE; hose->index = 0; + + hose->sg_isa = hose->sg_pci = NULL; + + /* The I/O window is fixed at 2G @ 2G. */ + __direct_map_base = 0x80000000; + __direct_map_size = 0x80000000; } static inline void diff --git a/arch/alpha/kernel/core_pyxis.c b/arch/alpha/kernel/core_pyxis.c index ee18e1472..cd81d5b91 100644 --- a/arch/alpha/kernel/core_pyxis.c +++ b/arch/alpha/kernel/core_pyxis.c @@ -6,20 +6,22 @@ * Code common to all PYXIS core logic chips. */ -#include <linux/kernel.h> #include <linux/types.h> +#include <linux/kernel.h> + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_pyxis.h> +#undef __EXTERN_INLINE + #include <linux/pci.h> #include <linux/sched.h> #include <linux/init.h> +#include <linux/bootmem.h> #include <asm/ptrace.h> #include <asm/system.h> -#define __EXTERN_INLINE inline -#include <asm/io.h> -#include <asm/core_pyxis.h> -#undef __EXTERN_INLINE - #include "proto.h" #include "pci_impl.h" @@ -284,6 +286,84 @@ struct pci_ops pyxis_pci_ops = write_dword: pyxis_write_config_dword }; +void +pyxis_pci_tbi(struct pci_controler *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + *(vip)PYXIS_TBIA = 3; /* Flush all locked and unlocked. */ + mb(); +} + +/* + * Pass 1 and 2 have a broken scatter-gather tlb -- it cannot be invalidated. + * To work around this problem, we allocate mappings, and put the chip into + * DMA loopback mode to read a garbage page. This works by causing TLB + * misses, causing old entries to be purged to make room for the new entries + * coming in for the garbage page. + * + * Thanks to NetBSD sources for pointing out this bug. What a pain. + */ + +static unsigned long broken_tbi_addr; + +#define BROKEN_TBI_READS 12 + +static void +pyxis_broken_pci_tbi(struct pci_controler *hose, + dma_addr_t start, dma_addr_t end) +{ + unsigned long flags; + unsigned long bus_addr; + unsigned int ctrl; + long i; + + __save_and_cli(flags); + + /* Put the chip into PCI loopback mode. */ + mb(); + ctrl = *(vuip)PYXIS_CTRL; + *(vuip)PYXIS_CTRL = ctrl | 4; + mb(); + + /* Read from PCI dense memory space at TBI_ADDR, skipping 64k + on each read. This forces SG TLB misses. It appears that + the TLB entries are "not quite LRU", meaning that we need + to read more times than there are actual tags. */ + + bus_addr = broken_tbi_addr; + for (i = 0; i < BROKEN_TBI_READS; ++i, bus_addr += 64*1024) + pyxis_readl(bus_addr); + + /* Restore normal PCI operation. */ + mb(); + *(vuip)PYXIS_CTRL = ctrl; + mb(); + + __restore_flags(flags); +} + +static void +pyxis_enable_broken_tbi(struct pci_iommu_arena *arena) +{ + void *page; + unsigned long *ppte, ofs, pte; + long i, npages; + + page = alloc_bootmem_pages(PAGE_SIZE); + pte = (virt_to_phys(page) >> (PAGE_SHIFT - 1)) | 1; + npages = (BROKEN_TBI_READS + 1) * 64*1024 / PAGE_SIZE; + + ofs = iommu_arena_alloc(arena, npages); + ppte = arena->ptes + ofs; + for (i = 0; i < npages; ++i) + ppte[i] = pte; + + broken_tbi_addr = pyxis_ioremap(arena->dma_base + ofs*PAGE_SIZE); + alpha_mv.mv_pci_tbi = pyxis_broken_pci_tbi; + + printk("PYXIS: Enabling broken tbia workaround.\n"); +} + void __init pyxis_init_arch(void) { @@ -306,84 +386,100 @@ pyxis_init_arch(void) */ temp = *(vuip)PYXIS_ERR_MASK; temp &= ~4; - *(vuip)PYXIS_ERR_MASK = temp; mb(); - temp = *(vuip)PYXIS_ERR_MASK; /* re-read to force write */ + *(vuip)PYXIS_ERR_MASK = temp; + mb(); + *(vuip)PYXIS_ERR_MASK; /* re-read to force write */ + + temp = *(vuip)PYXIS_ERR; + temp |= 0x180; /* master/target abort */ + *(vuip)PYXIS_ERR = temp; + mb(); + *(vuip)PYXIS_ERR; /* re-read to force write */ - temp = *(vuip)PYXIS_ERR ; - temp |= 0x180; /* master/target abort */ - *(vuip)PYXIS_ERR = temp; mb(); - temp = *(vuip)PYXIS_ERR; /* re-read to force write */ + /* + * Create our single hose. + */ + + hose = alloc_pci_controler(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->config_space = PYXIS_CONF; + hose->index = 0; /* - * Set up the PCI->physical memory translation windows. - * For now, windows 2 and 3 are disabled. In the future, we may - * want to use them to do scatter/gather DMA. + * Set up the PCI to main memory translation windows. + * + * Window 0 is scatter-gather 8MB at 8MB (for isa) + * Window 1 is scatter-gather 128MB at 3GB + * Window 2 is direct access 1GB at 1GB + * Window 3 is direct access 1GB at 2GB + * ??? We ought to scale window 1 with memory. * - * Window 0 goes at 2 GB and is 1 GB large. - * Window 1 goes at 3 GB and is 1 GB large. + * We must actually use 2 windows to direct-map the 2GB space, + * because of an idiot-syncrasy of the CYPRESS chip. It may + * respond to a PCI bus address in the last 1MB of the 4GB + * address range. */ - *(vuip)PYXIS_W0_BASE = PYXIS_DMA_WIN0_BASE_DEFAULT | 1U; - *(vuip)PYXIS_W0_MASK = (PYXIS_DMA_WIN0_SIZE_DEFAULT - 1) & 0xfff00000U; - *(vuip)PYXIS_T0_BASE = PYXIS_DMA_WIN0_TRAN_DEFAULT >> 2; + /* NetBSD hints that page tables must be aligned to 32K due + to a hardware bug. No description of what models affected. */ + hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, 32768); + hose->sg_pci = iommu_arena_new(0xc0000000, 0x08000000, 32768); + __direct_map_base = 0x40000000; + __direct_map_size = 0x80000000; - *(vuip)PYXIS_W1_BASE = PYXIS_DMA_WIN1_BASE_DEFAULT | 1U; - *(vuip)PYXIS_W1_MASK = (PYXIS_DMA_WIN1_SIZE_DEFAULT - 1) & 0xfff00000U; - *(vuip)PYXIS_T1_BASE = PYXIS_DMA_WIN1_TRAN_DEFAULT >> 2; + *(vuip)PYXIS_W0_BASE = hose->sg_isa->dma_base | 3; + *(vuip)PYXIS_W0_MASK = (hose->sg_isa->size - 1) & 0xfff00000; + *(vuip)PYXIS_T0_BASE = virt_to_phys(hose->sg_isa->ptes) >> 2; - *(vuip)PYXIS_W2_BASE = 0x0; - *(vuip)PYXIS_W3_BASE = 0x0; - mb(); + *(vuip)PYXIS_W1_BASE = hose->sg_pci->dma_base | 3; + *(vuip)PYXIS_W1_MASK = (hose->sg_pci->size - 1) & 0xfff00000; + *(vuip)PYXIS_T1_BASE = virt_to_phys(hose->sg_pci->ptes) >> 2; + + *(vuip)PYXIS_W2_BASE = 0x40000000 | 1; + *(vuip)PYXIS_W2_MASK = (0x40000000 - 1) & 0xfff00000; + *(vuip)PYXIS_T2_BASE = 0; + + *(vuip)PYXIS_W3_BASE = 0x80000000 | 1; + *(vuip)PYXIS_W3_MASK = (0x40000000 - 1) & 0xfff00000; + *(vuip)PYXIS_T3_BASE = 0; + + /* Pass 1 and 2 (ie revision <= 1) have a broken TBIA. See the + complete description next to pyxis_broken_pci_tbi for details. */ + if ((*(vuip)PYXIS_REV & 0xff) <= 1) + pyxis_enable_broken_tbi(hose->sg_pci); + + alpha_mv.mv_pci_tbi(hose, 0, -1); /* - * Next, clear the PYXIS_CFG register, which gets used + * Next, clear the PYXIS_CFG register, which gets used * for PCI Config Space accesses. That is the way * we want to use it, and we do not want to depend on * what ARC or SRM might have left behind... */ - { - unsigned int pyxis_cfg, temp; - pyxis_cfg = *(vuip)PYXIS_CFG; mb(); - if (pyxis_cfg != 0) { -#if 1 - printk("PYXIS_init: CFG was 0x%x\n", pyxis_cfg); -#endif - *(vuip)PYXIS_CFG = 0; mb(); - temp = *(vuip)PYXIS_CFG; /* re-read to force write */ - } + temp = *(vuip)PYXIS_CFG; + if (temp != 0) { + *(vuip)PYXIS_CFG = 0; + mb(); + *(vuip)PYXIS_CFG; /* re-read to force write */ } /* Zero the HAE. */ *(vuip)PYXIS_HAE_MEM = 0U; mb(); - *(vuip)PYXIS_HAE_MEM; /* re-read to force write */ + *(vuip)PYXIS_HAE_MEM; /* re-read to force write */ *(vuip)PYXIS_HAE_IO = 0; mb(); - *(vuip)PYXIS_HAE_IO; /* re-read to force write */ + *(vuip)PYXIS_HAE_IO; /* re-read to force write */ /* * Finally, check that the PYXIS_CTRL1 has IOA_BEN set for * enabling byte/word PCI bus space(s) access. */ - { - unsigned int ctrl1; - ctrl1 = *(vuip) PYXIS_CTRL1; - if (!(ctrl1 & 1)) { -#if 1 - printk("PYXIS_init: enabling byte/word PCI space\n"); -#endif - *(vuip) PYXIS_CTRL1 = ctrl1 | 1; mb(); - ctrl1 = *(vuip)PYXIS_CTRL1; /* re-read */ - } + temp = *(vuip) PYXIS_CTRL1; + if (!(temp & 1)) { + *(vuip)PYXIS_CTRL1 = temp | 1; + mb(); + *(vuip)PYXIS_CTRL1; /* re-read */ } - - /* - * Create our single hose. - */ - - hose = alloc_pci_controler(); - hose->io_space = &ioport_resource; - hose->mem_space = &iomem_resource; - hose->config_space = PYXIS_CONF; - hose->index = 0; } static inline void @@ -401,6 +497,8 @@ void pyxis_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { + int expected; + /* Clear the error before reporting anything. */ mb(); mb(); /* magic */ @@ -409,5 +507,23 @@ pyxis_machine_check(unsigned long vector, unsigned long la_ptr, wrmces(0x7); mb(); - process_mcheck_info(vector, la_ptr, regs, "PYXIS", mcheck_expected(0)); + expected = mcheck_expected(0); + if (!expected && vector == 0x660) { + struct el_common *com; + struct el_common_EV5_uncorrectable_mcheck *ev5; + struct el_PYXIS_sysdata_mcheck *pyxis; + + com = (void *)la_ptr; + ev5 = (void *)(la_ptr + com->proc_offset); + pyxis = (void *)(la_ptr + com->sys_offset); + + if (com->code == 0x202) { + printk(KERN_CRIT "PYXIS PCI machine check: err0=%08x " + "err1=%08x err2=%08x\n", + (int) pyxis->pci_err0, (int) pyxis->pci_err1, + (int) pyxis->pci_err2); + expected = 1; + } + } + process_mcheck_info(vector, la_ptr, regs, "PYXIS", expected); } diff --git a/arch/alpha/kernel/core_t2.c b/arch/alpha/kernel/core_t2.c index 079ad445c..d1037f57b 100644 --- a/arch/alpha/kernel/core_t2.c +++ b/arch/alpha/kernel/core_t2.c @@ -389,6 +389,10 @@ t2_init_arch(void) hose->mem_space = &iomem_resource; hose->config_space = T2_CONF; hose->index = 0; + + hose->sg_isa = hose->sg_pci = NULL; + __direct_map_base = 0x40000000; + __direct_map_size = 0x40000000; } #define SIC_SEIC (1UL << 33) /* System Event Clear */ diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c index dab4e1733..0ad001b94 100644 --- a/arch/alpha/kernel/core_tsunami.c +++ b/arch/alpha/kernel/core_tsunami.c @@ -100,11 +100,11 @@ mk_conf_addr(struct pci_dev *dev, int where, unsigned long *pci_addr, "pci_addr=0x%p, type1=0x%p)\n", bus, device_fn, where, pci_addr, type1)); - if (hose->first_busno == dev->bus->number) + if (hose->first_busno == dev->bus->number) bus = 0; - *type1 = (bus != 0); + *type1 = (bus != 0); - addr = (bus << 16) | (device_fn << 8) | where; + addr = (bus << 16) | (device_fn << 8) | where; addr |= hose->config_space; *pci_addr = addr; @@ -206,6 +206,23 @@ struct pci_ops tsunami_pci_ops = write_dword: tsunami_write_config_dword }; +void +tsunami_pci_tbi(struct pci_controler *hose, dma_addr_t start, dma_addr_t end) +{ + tsunami_pchip *pchip = hose->index ? TSUNAMI_pchip1 : TSUNAMI_pchip0; + + wmb(); + + /* We can invalidate up to 8 tlb entries in a go. The flush + matches against <31:16> in the pci address. */ + if (((start ^ end) & 0xffff0000) == 0) + pchip->tlbiv.csr = (start & 0xffff0000) >> 12; + else + pchip->tlbia.csr = 0; + + mb(); +} + #ifdef NXM_MACHINE_CHECKS_ON_TSUNAMI static long tsunami_probe_read(volatile unsigned long *vaddr) @@ -264,6 +281,8 @@ tsunami_init_one_pchip(tsunami_pchip *pchip, int index) return; hose = alloc_pci_controler(); + if (index == 0) + pci_isa_hose = hose; hose->io_space = alloc_resource(); hose->mem_space = alloc_resource(); @@ -307,27 +326,41 @@ tsunami_init_one_pchip(tsunami_pchip *pchip, int index) saved_pchip[index].tba[3] = pchip->tba[3].csr; /* - * Set up the PCI->physical memory translation windows. - * For now, windows 1,2 and 3 are disabled. In the future, - * we may want to use them to do scatter/gather DMA. + * Set up the PCI to main memory translation windows. + * + * Window 0 is scatter-gather 8MB at 8MB (for isa) + * Window 1 is scatter-gather 128MB at 3GB + * Window 2 is direct access 1GB at 1GB + * Window 3 is direct access 1GB at 2GB + * ??? We ought to scale window 1 memory. * - * Window 0 goes at 1 GB and is 1 GB large, mapping to 0. - * Window 1 goes at 2 GB and is 1 GB large, mapping to 1GB. + * We must actually use 2 windows to direct-map the 2GB space, + * because of an idiot-syncrasy of the CYPRESS chip. It may + * respond to a PCI bus address in the last 1MB of the 4GB + * address range. */ + hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, PAGE_SIZE); + hose->sg_pci = iommu_arena_new(0xc0000000, 0x08000000, PAGE_SIZE); + __direct_map_base = 0x40000000; + __direct_map_size = 0x80000000; - pchip->wsba[0].csr = TSUNAMI_DMA_WIN0_BASE_DEFAULT | 1UL; - pchip->wsm[0].csr = (TSUNAMI_DMA_WIN0_SIZE_DEFAULT - 1) & - 0xfff00000UL; - pchip->tba[0].csr = TSUNAMI_DMA_WIN0_TRAN_DEFAULT; + pchip->wsba[0].csr = hose->sg_isa->dma_base | 3; + pchip->wsm[0].csr = (hose->sg_isa->size - 1) & 0xfff00000; + pchip->tba[0].csr = virt_to_phys(hose->sg_isa->ptes); - pchip->wsba[1].csr = TSUNAMI_DMA_WIN1_BASE_DEFAULT | 1UL; - pchip->wsm[1].csr = (TSUNAMI_DMA_WIN1_SIZE_DEFAULT - 1) & - 0xfff00000UL; - pchip->tba[1].csr = TSUNAMI_DMA_WIN1_TRAN_DEFAULT; + pchip->wsba[1].csr = hose->sg_pci->dma_base | 3; + pchip->wsm[1].csr = (hose->sg_pci->size - 1) & 0xfff00000; + pchip->tba[1].csr = virt_to_phys(hose->sg_pci->ptes); - pchip->wsba[2].csr = 0; - pchip->wsba[3].csr = 0; - mb(); + pchip->wsba[2].csr = 0x40000000 | 1; + pchip->wsm[2].csr = (0x40000000 - 1) & 0xfff00000; + pchip->tba[2].csr = 0; + + pchip->wsba[3].csr = 0x80000000 | 1; + pchip->wsm[3].csr = (0x40000000 - 1) & 0xfff00000; + pchip->tba[3].csr = 0; + + tsunami_pci_tbi(hose, 0, -1); } void __init @@ -335,7 +368,7 @@ tsunami_init_arch(void) { #ifdef NXM_MACHINE_CHECKS_ON_TSUNAMI extern asmlinkage void entInt(void); - unsigned long tmp; + unsigned long tmp; /* Ho hum.. init_arch is called before init_IRQ, but we need to be able to handle machine checks. So install the handler now. */ @@ -426,7 +459,7 @@ tsunami_pci_clr_err(void) /* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */ if (TSUNAMI_cchip->csc.csr & 1L<<14) - tsunami_pci_clr_err_1(TSUNAMI_pchip1); + tsunami_pci_clr_err_1(TSUNAMI_pchip1); } void diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S index af1567fd1..cd8ce5c67 100644 --- a/arch/alpha/kernel/entry.S +++ b/arch/alpha/kernel/entry.S @@ -32,6 +32,7 @@ #define TASK_ADDR_LIMIT 24 #define TASK_EXEC_DOMAIN 32 #define TASK_NEED_RESCHED 40 +#define TASK_PROCESSOR 100 /* * task flags (must match include/linux/sched.h): @@ -572,12 +573,15 @@ entSys: .align 3 ret_from_sys_call: cmovne $26,0,$19 /* $19 = 0 => non-restartable */ - /* check bottom half interrupts */ - ldq $3,bh_active - ldq $4,bh_mask - and $3,$4,$2 - bne $2,handle_bottom_half -ret_from_handle_bh: + ldq $3,TASK_PROCESSOR($8) + lda $4,softirq_state + sll $3,5,$3 + addq $3,$4,$4 + ldq $4,0($4) + sll $4,32,$3 + and $4,$3,$4 + bne $4,handle_softirq +ret_from_softirq: ldq $0,SP_OFF($30) and $0,8,$0 beq $0,restore_all @@ -656,16 +660,16 @@ strace_error: br ret_from_sys_call .align 3 -handle_bottom_half: +handle_softirq: subq $30,16,$30 stq $19,0($30) /* save syscall nr */ stq $20,8($30) /* and error indication (a3) */ - jsr $26,do_bottom_half + jsr $26,do_softirq ldq $19,0($30) ldq $20,8($30) addq $30,16,$30 - br ret_from_handle_bh - + br ret_from_softirq + .align 3 syscall_error: /* diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index d6d6e0611..bae28a6a4 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -377,10 +377,6 @@ spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; /* Global IRQ locking depth. */ atomic_t global_irq_count = ATOMIC_INIT(0); -/* This protects BH software state (masks, things like that). */ -atomic_t global_bh_lock = ATOMIC_INIT(0); -atomic_t global_bh_count = ATOMIC_INIT(0); - static void *previous_irqholder = NULL; #define MAXCOUNT 100000000 @@ -401,7 +397,7 @@ wait_on_irq(int cpu, void *where) */ if (!atomic_read(&global_irq_count)) { if (local_bh_count(cpu) - || !atomic_read(&global_bh_count)) + || !spin_is_locked(&global_bh_lock)) break; } @@ -422,7 +418,7 @@ wait_on_irq(int cpu, void *where) if (spin_is_locked(&global_irq_lock)) continue; if (!local_bh_count(cpu) - && atomic_read(&global_bh_count)) + && spin_is_locked(&global_bh_lock)) continue; if (spin_trylock(&global_irq_lock)) break; @@ -552,7 +548,7 @@ show(char * str, void *where) cpu_data[1].irq_count); printk("bh: %d [%d %d]\n", - atomic_read(&global_bh_count), + spin_is_locked(&global_bh_lock) ? 1 : 0, cpu_data[0].bh_count, cpu_data[1].bh_count); #if 0 @@ -567,35 +563,6 @@ show(char * str, void *where) #endif } -static inline void -wait_on_bh(void) -{ - int count = MAXCOUNT; - do { - if (!--count) { - show("wait_on_bh", 0); - count = ~0; - } - /* nothing .. wait for the other bh's to go away */ - barrier(); - } while (atomic_read(&global_bh_count) != 0); -} - -/* - * This is called when we want to synchronize with - * bottom half handlers. We need to wait until - * no other CPU is executing any bottom half handler. - * - * Don't wait if we're already running in an interrupt - * context or are inside a bh handler. - */ -void -synchronize_bh(void) -{ - if (atomic_read(&global_bh_count) && !in_interrupt()) - wait_on_bh(); -} - /* * From its use, I infer that synchronize_irq() stalls a thread until * the effects of a command to an external device are known to have @@ -897,6 +864,7 @@ process_mcheck_info(unsigned long vector, unsigned long la_ptr, case 0x98: reason = "processor detected hard error"; break; /* System specific (these are for Alcor, at least): */ + case 0x202: reason = "system detected hard error"; break; case 0x203: reason = "system detected uncorrectable ECC error"; break; case 0x204: reason = "SIO SERR occurred on PCI bus"; break; case 0x205: reason = "parity error detected by CIA"; break; diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h index fc0d0cc0c..421591104 100644 --- a/arch/alpha/kernel/machvec_impl.h +++ b/arch/alpha/kernel/machvec_impl.h @@ -101,8 +101,7 @@ #define DO_TSUNAMI_IO IO(TSUNAMI,tsunami) #define BUS(which) \ - mv_virt_to_bus: CAT(which,_virt_to_bus), \ - mv_bus_to_virt: CAT(which,_bus_to_virt) + mv_pci_tbi: CAT(which,_pci_tbi) #define DO_APECS_BUS BUS(apecs) #define DO_CIA_BUS BUS(cia) diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index aa0a8d968..b62179b8d 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -40,6 +40,7 @@ const char pci_hae0_name[] = "HAE0"; */ struct pci_controler *hose_head, **hose_tail = &hose_head; +struct pci_controler *pci_isa_hose; /* * Quirks. diff --git a/arch/alpha/kernel/pci_impl.h b/arch/alpha/kernel/pci_impl.h index b46fb9980..f91978732 100644 --- a/arch/alpha/kernel/pci_impl.h +++ b/arch/alpha/kernel/pci_impl.h @@ -7,7 +7,7 @@ struct pci_dev; struct pci_controler; - +struct pci_iommu_arena; /* * We can't just blindly use 64K for machines with EISA busses; they @@ -125,12 +125,17 @@ static inline u8 bridge_swizzle(u8 pin, u8 slot) /* The hose list. */ extern struct pci_controler *hose_head, **hose_tail; +extern struct pci_controler *pci_isa_hose; extern void common_init_pci(void); extern u8 common_swizzle(struct pci_dev *, u8 *); extern struct pci_controler *alloc_pci_controler(void); extern struct resource *alloc_resource(void); +extern struct pci_iommu_arena *iommu_arena_new(dma_addr_t, unsigned long, + unsigned long); +extern long iommu_arena_alloc(struct pci_iommu_arena *arena, long n); + extern const char *const pci_io_names[]; extern const char *const pci_mem_names[]; extern const char pci_hae0_name[]; diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c new file mode 100644 index 000000000..8faa66901 --- /dev/null +++ b/arch/alpha/kernel/pci_iommu.c @@ -0,0 +1,531 @@ +/* + * linux/arch/alpha/kernel/pci_iommu.c + */ + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/bootmem.h> + +#include <asm/io.h> +#include <asm/hwrpb.h> + +#include "proto.h" +#include "pci_impl.h" + + +#define DEBUG_ALLOC 0 + +#if DEBUG_ALLOC > 0 +# define DBGA(args...) printk(KERN_DEBUG ##args) +#else +# define DBGA(args...) +#endif +#if DEBUG_ALLOC > 1 +# define DBGA2(args...) printk(KERN_DEBUG ##args) +#else +# define DBGA2(args...) +#endif + + +static inline unsigned long +mk_iommu_pte(unsigned long paddr) +{ + return (paddr >> (PAGE_SHIFT-1)) | 1; +} + +static inline long +calc_npages(long bytes) +{ + return (bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + +struct pci_iommu_arena * +iommu_arena_new(dma_addr_t base, unsigned long window_size, + unsigned long align) +{ + unsigned long entries, mem_size, mem_pages; + struct pci_iommu_arena *arena; + + entries = window_size >> PAGE_SHIFT; + mem_size = entries * sizeof(unsigned long); + mem_pages = calc_npages(mem_size); + + arena = alloc_bootmem(sizeof(*arena)); + arena->ptes = __alloc_bootmem(mem_pages * PAGE_SIZE, align, 0); + + spin_lock_init(&arena->lock); + arena->dma_base = base; + arena->size = window_size; + arena->alloc_hint = 0; + + return arena; +} + +long +iommu_arena_alloc(struct pci_iommu_arena *arena, long n) +{ + unsigned long flags; + unsigned long *beg, *p, *end; + long i; + + spin_lock_irqsave(&arena->lock, flags); + + /* Search forward for the first sequence of N empty ptes. */ + beg = arena->ptes; + end = beg + (arena->size >> PAGE_SHIFT); + p = beg + arena->alloc_hint; + i = 0; + while (i < n && p < end) + i = (*p++ == 0 ? i + 1 : 0); + + if (p >= end) { + /* Failure. Assume the hint was wrong and go back to + search from the beginning. */ + p = beg; + i = 0; + while (i < n && p < end) + i = (*p++ == 0 ? i + 1 : 0); + + if (p >= end) { + spin_unlock_irqrestore(&arena->lock, flags); + return -1; + } + } + + /* Success. Mark them all in use, ie not zero. Typically + bit zero is the valid bit, so write ~1 into everything. + The chip specific bits will fill this in with something + kosher when we return. */ + for (p = p - n, i = 0; i < n; ++i) + p[i] = ~1UL; + + arena->alloc_hint = p - beg + n; + spin_unlock_irqrestore(&arena->lock, flags); + + return p - beg; +} + +static void +iommu_arena_free(struct pci_iommu_arena *arena, long ofs, long n) +{ + unsigned long *p; + long i; + + p = arena->ptes + ofs; + for (i = 0; i < n; ++i) + p[i] = 0; + arena->alloc_hint = ofs; +} + +/* Map a single buffer of the indicate size for PCI DMA in streaming + mode. The 32-bit PCI bus mastering address to use is returned. + Once the device is given the dma address, the device owns this memory + until either pci_unmap_single or pci_sync_single is performed. */ + +dma_addr_t +pci_map_single(struct pci_dev *pdev, void *cpu_addr, long size) +{ + struct pci_controler *hose = pdev ? pdev->sysdata : pci_isa_hose; + dma_addr_t max_dma = pdev ? pdev->dma_mask : 0x00ffffff; + struct pci_iommu_arena *arena; + long npages, dma_ofs, i; + unsigned long paddr; + dma_addr_t ret; + + paddr = virt_to_phys(cpu_addr); + + /* First check to see if we can use the direct map window. */ + if (paddr + size + __direct_map_base - 1 <= max_dma + && paddr + size <= __direct_map_size) { + ret = paddr + __direct_map_base; + + DBGA2("pci_map_single: [%p,%lx] -> direct %x from %p\n", + cpu_addr, size, ret, __builtin_return_address(0)); + + return ret; + } + + /* If the machine doesn't define a pci_tbi routine, we have to + assume it doesn't support sg mapping. */ + if (! alpha_mv.mv_pci_tbi) { + printk(KERN_INFO "pci_map_single failed: no hw sg\n"); + return 0; + } + + arena = hose->sg_pci; + if (!arena || arena->dma_base + arena->size > max_dma) + arena = hose->sg_isa; + + npages = calc_npages((paddr & ~PAGE_MASK) + size); + dma_ofs = iommu_arena_alloc(arena, npages); + if (dma_ofs < 0) { + printk(KERN_INFO "pci_map_single failed: " + "could not allocate dma page tables\n"); + return 0; + } + + paddr &= PAGE_MASK; + for (i = 0; i < npages; ++i, paddr += PAGE_SIZE) + arena->ptes[i + dma_ofs] = mk_iommu_pte(paddr); + + ret = arena->dma_base + dma_ofs * PAGE_SIZE; + ret += (unsigned long)cpu_addr & ~PAGE_MASK; + + /* ??? This shouldn't have been needed, since the entries + we've just modified were not in the iommu tlb. */ + alpha_mv.mv_pci_tbi(hose, ret, ret + size - 1); + + DBGA("pci_map_single: [%p,%lx] np %ld -> sg %x from %p\n", + cpu_addr, size, npages, ret, __builtin_return_address(0)); + + return ret; +} + + +/* Unmap a single streaming mode DMA translation. The DMA_ADDR and + SIZE must match what was provided for in a previous pci_map_single + call. All other usages are undefined. After this call, reads by + the cpu to the buffer are guarenteed to see whatever the device + wrote there. */ + +void +pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, long size) +{ + struct pci_controler *hose = pdev ? pdev->sysdata : pci_isa_hose; + struct pci_iommu_arena *arena; + long dma_ofs, npages; + + + if (dma_addr >= __direct_map_base + && dma_addr < __direct_map_base + __direct_map_size) { + /* Nothing to do. */ + + DBGA2("pci_unmap_single: direct [%x,%lx] from %p\n", + dma_addr, size, __builtin_return_address(0)); + + return; + } + + arena = hose->sg_pci; + if (!arena || dma_addr < arena->dma_base) + arena = hose->sg_isa; + + dma_ofs = (dma_addr - arena->dma_base) >> PAGE_SHIFT; + if (dma_ofs * PAGE_SIZE >= arena->size) { + printk(KERN_ERR "Bogus pci_unmap_single: dma_addr %x " + " base %x size %x\n", dma_addr, arena->dma_base, + arena->size); + return; + BUG(); + } + + npages = calc_npages((dma_addr & ~PAGE_MASK) + size); + iommu_arena_free(arena, dma_ofs, npages); + alpha_mv.mv_pci_tbi(hose, dma_addr, dma_addr + size - 1); + + DBGA2("pci_unmap_single: sg [%x,%lx] np %ld from %p\n", + dma_addr, size, npages, __builtin_return_address(0)); +} + + +/* Allocate and map kernel buffer using consistent mode DMA for PCI + device. Returns non-NULL cpu-view pointer to the buffer if + successful and sets *DMA_ADDRP to the pci side dma address as well, + else DMA_ADDRP is undefined. */ + +void * +pci_alloc_consistent(struct pci_dev *pdev, long size, dma_addr_t *dma_addrp) +{ + void *cpu_addr; + + cpu_addr = kmalloc(size, GFP_ATOMIC); + if (! cpu_addr) { + printk(KERN_INFO "dma_alloc_consistent: " + "kmalloc failed from %p\n", + __builtin_return_address(0)); + /* ??? Really atomic allocation? Otherwise we could play + with vmalloc and sg if we can't find contiguous memory. */ + return NULL; + } + memset(cpu_addr, 0, size); + + *dma_addrp = pci_map_single(pdev, cpu_addr, size); + if (*dma_addrp == 0) { + kfree_s(cpu_addr, size); + return NULL; + } + + DBGA2("dma_alloc_consistent: %lx -> [%p,%x] from %p\n", + size, cpu_addr, *dma_addrp, __builtin_return_address(0)); + + return cpu_addr; +} + + +/* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must + be values that were returned from pci_alloc_consistent. SIZE must + be the same as what as passed into pci_alloc_consistent. + References to the memory and mappings assosciated with CPU_ADDR or + DMA_ADDR past this call are illegal. */ + +void +pci_free_consistent(struct pci_dev *pdev, long size, void *cpu_addr, + dma_addr_t dma_addr) +{ + pci_unmap_single(pdev, dma_addr, size); + kfree_s(cpu_addr, size); + + DBGA2("dma_free_consistent: [%x,%lx] from %p\n", + dma_addr, size, __builtin_return_address(0)); +} + + +/* Classify the elements of the scatterlist. Write dma_address + of each element with: + 0 : Not mergable. + 1 : Followers all physically adjacent. + [23]: Followers all virtually adjacent. + -1 : Not leader. + Write dma_length of each leader with the combined lengths of + the mergable followers. */ + +static inline void +sg_classify(struct scatterlist *sg, struct scatterlist *end) +{ + unsigned long next_vaddr; + struct scatterlist *leader; + + leader = sg; + leader->dma_address = 0; + leader->dma_length = leader->length; + next_vaddr = (unsigned long)leader->address + leader->length; + + for (++sg; sg < end; ++sg) { + unsigned long addr, len; + addr = (unsigned long) sg->address; + len = sg->length; + + if (next_vaddr == addr) { + sg->dma_address = -1; + leader->dma_address |= 1; + leader->dma_length += len; + } else if (((next_vaddr | addr) & ~PAGE_MASK) == 0) { + sg->dma_address = -1; + leader->dma_address |= 2; + leader->dma_length += len; + } else { + leader = sg; + leader->dma_address = 0; + leader->dma_length = len; + } + + next_vaddr = addr + len; + } +} + +/* Given a scatterlist leader, choose an allocation method and fill + in the blanks. */ + +static inline int +sg_fill(struct scatterlist *leader, struct scatterlist *end, + struct scatterlist *out, struct pci_iommu_arena *arena, + dma_addr_t max_dma) +{ + unsigned long paddr = virt_to_phys(leader->address); + unsigned long size = leader->dma_length; + struct scatterlist *sg; + unsigned long *ptes; + long npages, dma_ofs, i; + + /* If everything is physically contiguous, and the addresses + fall into the direct-map window, use it. */ + if (leader->dma_address < 2 + && paddr + size + __direct_map_base - 1 <= max_dma + && paddr + size <= __direct_map_size) { + out->dma_address = paddr + __direct_map_base; + out->dma_length = size; + + DBGA2("sg_fill: [%p,%lx] -> direct %x\n", + leader->address, size, out->dma_address); + + return 0; + } + + /* Otherwise, we'll use the iommu to make the pages virtually + contiguous. */ + + paddr &= ~PAGE_MASK; + npages = calc_npages(paddr + size); + dma_ofs = iommu_arena_alloc(arena, npages); + if (dma_ofs < 0) + return -1; + + out->dma_address = arena->dma_base + dma_ofs*PAGE_SIZE + paddr; + out->dma_length = size; + + DBGA("sg_fill: [%p,%lx] -> sg %x\n", + leader->address, size, out->dma_address); + + ptes = &arena->ptes[dma_ofs]; + sg = leader; + do { + paddr = virt_to_phys(sg->address); + npages = calc_npages((paddr & ~PAGE_MASK) + sg->length); + + DBGA(" (%ld) [%p,%x]\n", + sg - leader, sg->address, sg->length); + + paddr &= PAGE_MASK; + for (i = 0; i < npages; ++i, paddr += PAGE_SIZE) + *ptes++ = mk_iommu_pte(paddr); + + ++sg; + } while (sg < end && sg->dma_address == -1); + + return 1; +} + +/* TODO: Only use the iommu when it helps. Non-mergable scatterlist + entries might as well use direct mappings. */ + +int +pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents) +{ + struct scatterlist *start, *end, *out; + struct pci_controler *hose; + struct pci_iommu_arena *arena; + dma_addr_t max_dma, fstart, fend; + + /* If pci_tbi is not available, we must not be able to control + an iommu. Direct map everything, no merging. */ + if (! alpha_mv.mv_pci_tbi) { + for (end = sg + nents; sg < end; ++sg) { + sg->dma_address = virt_to_bus(sg->address); + sg->dma_length = sg->length; + } + return nents; + } + + /* Fast path single entry scatterlists. */ + if (nents == 1) { + sg->dma_length = sg->length; + sg->dma_address + = pci_map_single(pdev, sg->address, sg->length); + return sg->dma_address != 0; + } + + hose = pdev ? pdev->sysdata : pci_isa_hose; + max_dma = pdev ? pdev->dma_mask : 0x00ffffff; + arena = hose->sg_pci; + if (!arena || arena->dma_base + arena->size > max_dma) + arena = hose->sg_isa; + start = sg; + end = sg + nents; + fstart = -1; + fend = 0; + + /* First, prepare information about the entries. */ + sg_classify(sg, end); + + /* Second, iterate over the scatterlist leaders and allocate + dma space as needed. */ + for (out = sg; sg < end; ++sg) { + int ret; + + if (sg->dma_address == -1) + continue; + + ret = sg_fill(sg, end, out, arena, max_dma); + if (ret < 0) + goto error; + else if (ret > 0) { + dma_addr_t ts, te; + + ts = out->dma_address; + te = ts + out->dma_length - 1; + if (fstart > ts) + fstart = ts; + if (fend < te) + fend = te; + } + out++; + } + + /* ??? This shouldn't have been needed, since the entries + we've just modified were not in the iommu tlb. */ + if (fend) + alpha_mv.mv_pci_tbi(hose, fstart, fend); + + if (out - start == 0) + printk(KERN_INFO "pci_map_sg failed: no entries?\n"); + + return out - start; + +error: + printk(KERN_INFO "pci_map_sg failed: " + "could not allocate dma page tables\n"); + + /* Some allocation failed while mapping the scatterlist + entries. Unmap them now. */ + if (out > start) + pci_unmap_sg(pdev, start, out - start); + return 0; +} + + +/* Unmap a set of streaming mode DMA translations. Again, cpu read + rules concerning calls here are the same as for pci_unmap_single() + above. */ + +void +pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents) +{ + struct pci_controler *hose; + struct pci_iommu_arena *arena; + struct scatterlist *end; + dma_addr_t max_dma; + dma_addr_t fstart, fend; + + if (! alpha_mv.mv_pci_tbi) + return; + + hose = pdev ? pdev->sysdata : pci_isa_hose; + max_dma = pdev ? pdev->dma_mask : 0x00ffffff; + arena = hose->sg_pci; + if (!arena || arena->dma_base + arena->size > max_dma) + arena = hose->sg_isa; + fstart = -1; + fend = 0; + + for (end = sg + nents; sg < end; ++sg) { + unsigned long addr, size; + + addr = sg->dma_address; + size = sg->dma_length; + + if (addr >= __direct_map_base + && addr < __direct_map_base + __direct_map_size) { + /* Nothing to do. */ + DBGA2("pci_unmap_sg: direct [%lx,%lx]\n", addr, size); + } else { + long npages, ofs; + dma_addr_t tend; + + npages = calc_npages((addr & ~PAGE_MASK) + size); + ofs = (addr - arena->dma_base) >> PAGE_SHIFT; + iommu_arena_free(arena, ofs, npages); + + tend = addr + size - 1; + if (fstart > addr) + fstart = addr; + if (fend < tend) + fend = tend; + + DBGA2("pci_unmap_sg: sg [%lx,%lx]\n", addr, size); + } + } + if (fend) + alpha_mv.mv_pci_tbi(hose, fstart, fend); +} diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 3efbdda08..fa92b3bc3 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -9,55 +9,65 @@ struct pt_regs; struct task_struct; struct pci_dev; +struct pci_controler; /* core_apecs.c */ extern struct pci_ops apecs_pci_ops; extern void apecs_init_arch(void); extern void apecs_pci_clr_err(void); extern void apecs_machine_check(u64, u64, struct pt_regs *); +extern void apecs_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t); /* core_cia.c */ extern struct pci_ops cia_pci_ops; extern void cia_init_arch(void); extern void cia_machine_check(u64, u64, struct pt_regs *); +extern void cia_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t); /* core_irongate.c */ extern struct pci_ops irongate_pci_ops; extern int irongate_pci_clr_err(void); extern void irongate_init_arch(void); extern void irongate_machine_check(u64, u64, struct pt_regs *); +#define irongate_pci_tbi ((void *)0) /* core_lca.c */ extern struct pci_ops lca_pci_ops; extern void lca_init_arch(void); extern void lca_machine_check(u64, u64, struct pt_regs *); +extern void lca_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t); /* core_mcpcia.c */ extern struct pci_ops mcpcia_pci_ops; extern void mcpcia_init_arch(void); extern void mcpcia_init_hoses(void); extern void mcpcia_machine_check(u64, u64, struct pt_regs *); +extern void mcpcia_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t); /* core_polaris.c */ extern struct pci_ops polaris_pci_ops; extern void polaris_init_arch(void); extern void polaris_machine_check(u64, u64, struct pt_regs *); +#define polaris_pci_tbi ((void *)0) /* core_pyxis.c */ extern struct pci_ops pyxis_pci_ops; extern void pyxis_init_arch(void); extern void pyxis_machine_check(u64, u64, struct pt_regs *); +extern void pyxis_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t); /* core_t2.c */ extern struct pci_ops t2_pci_ops; extern void t2_init_arch(void); extern void t2_machine_check(u64, u64, struct pt_regs *); +#define t2_pci_tbi ((void *)0) /* core_tsunami.c */ extern struct pci_ops tsunami_pci_ops; extern void tsunami_init_arch(void); extern void tsunami_kill_arch(int); extern void tsunami_machine_check(u64, u64, struct pt_regs *); +extern void tsunami_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t); /* setup.c */ extern unsigned long srm_hae; diff --git a/arch/alpha/kernel/semaphore.c b/arch/alpha/kernel/semaphore.c index d62b355e1..d4793ecb4 100644 --- a/arch/alpha/kernel/semaphore.c +++ b/arch/alpha/kernel/semaphore.c @@ -36,7 +36,9 @@ * critical part is the inline stuff in <asm/semaphore.h> * where we want to avoid any extra jumps and calls. */ -void __up(struct semaphore *sem) + +void +__up(struct semaphore *sem) { wake_one_more(sem); wake_up(&sem->wait); @@ -63,7 +65,7 @@ void __up(struct semaphore *sem) #define DOWN_VAR \ struct task_struct *tsk = current; \ wait_queue_t wait; \ - init_waitqueue_entry(&wait, tsk); + init_waitqueue_entry(&wait, tsk) #define DOWN_HEAD(task_state) \ \ @@ -92,23 +94,27 @@ void __up(struct semaphore *sem) tsk->state = (task_state); \ } \ tsk->state = TASK_RUNNING; \ - remove_wait_queue(&sem->wait, &wait); + remove_wait_queue(&sem->wait, &wait) -void __down(struct semaphore * sem) +void +__down(struct semaphore * sem) { - DOWN_VAR - DOWN_HEAD(TASK_UNINTERRUPTIBLE) + DOWN_VAR; + DOWN_HEAD(TASK_UNINTERRUPTIBLE); + if (waking_non_zero(sem)) break; schedule(); - DOWN_TAIL(TASK_UNINTERRUPTIBLE) + + DOWN_TAIL(TASK_UNINTERRUPTIBLE); } -int __down_interruptible(struct semaphore * sem) +int +__down_interruptible(struct semaphore * sem) { int ret = 0; - DOWN_VAR - DOWN_HEAD(TASK_INTERRUPTIBLE) + DOWN_VAR; + DOWN_HEAD(TASK_INTERRUPTIBLE); ret = waking_non_zero_interruptible(sem, tsk); if (ret) @@ -119,11 +125,149 @@ int __down_interruptible(struct semaphore * sem) break; } schedule(); - DOWN_TAIL(TASK_INTERRUPTIBLE) + + DOWN_TAIL(TASK_INTERRUPTIBLE); return ret; } -int __down_trylock(struct semaphore * sem) +int +__down_trylock(struct semaphore * sem) { return waking_non_zero_trylock(sem); } + + +/* + * RW Semaphores + */ + +void +__down_read(struct rw_semaphore *sem, int count) +{ + long tmp; + DOWN_VAR; + + retry_down: + if (count < 0) { + /* Wait for the lock to become unbiased. Readers + are non-exclusive. */ + + /* This takes care of granting the lock. */ + up_read(sem); + + add_wait_queue(&sem->wait, &wait); + while (sem->count < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (sem->count >= 0) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + __asm __volatile ( + " mb\n" + "1: ldl_l %0,%1\n" + " subl %0,1,%2\n" + " subl %0,1,%0\n" + " stl_c %2,%1\n" + " bne %2,2f\n" + ".section .text2,\"ax\"\n" + "2: br 1b\n" + ".previous" + : "=r"(count), "=m"(sem->count), "=r"(tmp) + : : "memory"); + if (count <= 0) + goto retry_down; + } else { + add_wait_queue(&sem->wait, &wait); + + while (1) { + if (test_and_clear_bit(0, &sem->granted)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if ((sem->granted & 1) == 0) + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + } +} + +void +__down_write(struct rw_semaphore *sem, int count) +{ + long tmp; + DOWN_VAR; + + retry_down: + if (count + RW_LOCK_BIAS < 0) { + up_write(sem); + + add_wait_queue_exclusive(&sem->wait, &wait); + + while (sem->count < 0) { + set_task_state(tsk, (TASK_UNINTERRUPTIBLE + | TASK_EXCLUSIVE)); + if (sem->count >= RW_LOCK_BIAS) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + __asm __volatile ( + " mb\n" + "1: ldl_l %0,%1\n" + " ldah %2,%3(%0)\n" + " ldah %0,%3(%0)\n" + " stl_c %2,%1\n" + " bne %2,2f\n" + ".section .text2,\"ax\"\n" + "2: br 1b\n" + ".previous" + : "=r"(count), "=m"(sem->count), "=r"(tmp) + : "i"(-(RW_LOCK_BIAS >> 16)) + : "memory"); + if (count != 0) + goto retry_down; + } else { + /* Put ourselves at the end of the list. */ + add_wait_queue_exclusive(&sem->write_bias_wait, &wait); + + while (1) { + if (test_and_clear_bit(1, &sem->granted)) + break; + set_task_state(tsk, (TASK_UNINTERRUPTIBLE + | TASK_EXCLUSIVE)); + if ((sem->granted & 2) == 0) + schedule(); + } + + remove_wait_queue(&sem->write_bias_wait, &wait); + tsk->state = TASK_RUNNING; + + /* If the lock is currently unbiased, awaken the sleepers. + FIXME: This wakes up the readers early in a bit of a + stampede -> bad! */ + if (sem->count >= 0) + wake_up(&sem->wait); + } +} + +void +__do_rwsem_wake(struct rw_semaphore *sem, int readers) +{ + if (readers) { + if (test_and_set_bit(0, &sem->granted)) + BUG(); + wake_up(&sem->wait); + } else { + if (test_and_set_bit(1, &sem->granted)) + BUG(); + wake_up(&sem->write_bias_wait); + } +} diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 3a1fb94a3..c92168195 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -96,6 +96,13 @@ struct screen_info screen_info = { orig_video_points: 16 }; +/* + * The direct map I/O window, if any. This should be the same + * for all busses, since it's used by virt_to_bus. + */ + +unsigned long __direct_map_base; +unsigned long __direct_map_size; /* * Declare all of the machine vectors. @@ -225,15 +232,8 @@ setup_memory(void) max_low_pfn = end; } - /* Enforce maximum of 2GB even if there is more. Blah. */ - if (max_low_pfn > PFN_MAX) - max_low_pfn = PFN_MAX; - printk("max_low_pfn %ld\n", max_low_pfn); - /* Find the end of the kernel memory. */ start_pfn = PFN_UP(virt_to_phys(_end)); - printk("_end %p, start_pfn %ld\n", _end, start_pfn); - bootmap_start = -1; try_again: @@ -243,7 +243,6 @@ setup_memory(void) /* We need to know how many physically contigous pages we'll need for the bootmap. */ bootmap_pages = bootmem_bootmap_pages(max_low_pfn); - printk("bootmap size: %ld pages\n", bootmap_pages); /* Now find a good region where to allocate the bootmap. */ for_each_mem_cluster(memdesc, cluster, i) { @@ -261,8 +260,6 @@ setup_memory(void) if (end > max_low_pfn) end = max_low_pfn; if (end - start >= bootmap_pages) { - printk("allocating bootmap in area %ld:%ld\n", - start, start+bootmap_pages); bootmap_start = start; break; } @@ -270,8 +267,6 @@ setup_memory(void) if (bootmap_start == -1) { max_low_pfn >>= 1; - printk("bootmap area not found now trying with %ld pages\n", - max_low_pfn); goto try_again; } @@ -304,8 +299,6 @@ setup_memory(void) /* Reserve the bootmap memory. */ reserve_bootmem(PFN_PHYS(bootmap_start), bootmap_size); - printk("reserving bootmap %ld:%ld\n", bootmap_start, - bootmap_start + PFN_UP(bootmap_size)); #ifdef CONFIG_BLK_DEV_INITRD initrd_start = INITRD_START; @@ -328,27 +321,26 @@ setup_memory(void) #endif /* CONFIG_BLK_DEV_INITRD */ } -int __init page_is_ram(unsigned long pfn) +int __init +page_is_ram(unsigned long pfn) { struct memclust_struct * cluster; struct memdesc_struct * memdesc; int i; - memdesc = (struct memdesc_struct *) (hwrpb->mddt_offset + (unsigned long) hwrpb); + memdesc = (struct memdesc_struct *) + (hwrpb->mddt_offset + (unsigned long) hwrpb); for_each_mem_cluster(memdesc, cluster, i) { if (pfn >= cluster->start_pfn && - pfn < cluster->start_pfn + cluster->numpages) - { - if (cluster->usage & 3) - return 0; - else - return 1; + pfn < cluster->start_pfn + cluster->numpages) { + return (cluster->usage & 3) ? 0 : 1; } } return 0; } + #undef PFN_UP #undef PFN_DOWN #undef PFN_PHYS @@ -369,8 +361,7 @@ setup_arch(char **cmdline_p) /* Hack for Jensen... since we're restricted to 8 or 16 chars for boot flags depending on the boot mode, we need some shorthand. - This should do for installation. Later we'll add other - abbreviations as well... */ + This should do for installation. */ if (strcmp(COMMAND_LINE, "INSTALL") == 0) { strcpy(command_line, "root=/dev/fd0 load_ramdisk=1"); } else { diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 3fbf11495..30ed75ead 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -51,9 +51,9 @@ static struct { } ipi_data[NR_CPUS] __cacheline_aligned; enum ipi_message_type { - IPI_RESCHEDULE, - IPI_CALL_FUNC, - IPI_CPU_STOP, + IPI_RESCHEDULE, + IPI_CALL_FUNC, + IPI_CPU_STOP, }; spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; @@ -70,7 +70,7 @@ int smp_num_cpus = 1; /* Number that came online. */ int smp_threads_ready; /* True once the per process idle is forked. */ cycles_t cacheflush_time; -int cpu_number_map[NR_CPUS]; +int __cpu_number_map[NR_CPUS]; int __cpu_logical_map[NR_CPUS]; extern void calibrate_delay(void); @@ -426,13 +426,13 @@ smp_boot_one_cpu(int cpuid, int cpunum) if (fork_by_hand() < 0) panic("failed fork for CPU %d", cpuid); - idle = init_task.prev_task; - if (!idle) - panic("No idle process for CPU %d", cpuid); + idle = init_task.prev_task; + if (!idle) + panic("No idle process for CPU %d", cpuid); idle->processor = cpuid; __cpu_logical_map[cpunum] = cpuid; - cpu_number_map[cpuid] = cpunum; + __cpu_number_map[cpuid] = cpunum; idle->has_cpu = 1; /* we schedule the first task manually */ del_from_runqueue(idle); @@ -461,7 +461,7 @@ smp_boot_one_cpu(int cpuid, int cpunum) /* we must invalidate our stuff as we failed to boot the CPU */ __cpu_logical_map[cpunum] = -1; - cpu_number_map[cpuid] = -1; + __cpu_number_map[cpuid] = -1; /* the idle task is local to us so free it as we don't use it */ free_task_struct(idle); @@ -534,11 +534,11 @@ smp_boot_cpus(void) unsigned long bogosum; /* Take care of some initial bookkeeping. */ - memset(cpu_number_map, -1, sizeof(cpu_number_map)); + memset(__cpu_number_map, -1, sizeof(__cpu_number_map)); memset(__cpu_logical_map, -1, sizeof(__cpu_logical_map)); memset(ipi_data, 0, sizeof(ipi_data)); - cpu_number_map[smp_boot_cpuid] = 0; + __cpu_number_map[smp_boot_cpuid] = 0; __cpu_logical_map[0] = smp_boot_cpuid; current->processor = smp_boot_cpuid; @@ -554,7 +554,7 @@ smp_boot_cpus(void) /* Nothing to do on a UP box, or when told not to. */ if (smp_num_probed == 1 || max_cpus == 0) { - printk(KERN_INFO "SMP mode deactivated.\n"); + printk(KERN_INFO "SMP mode deactivated.\n"); return; } @@ -565,7 +565,7 @@ smp_boot_cpus(void) if (i == smp_boot_cpuid) continue; - if (((cpu_present_mask >> i) & 1) == 0) + if (((cpu_present_mask >> i) & 1) == 0) continue; if (smp_boot_one_cpu(i, cpu_count)) @@ -580,10 +580,10 @@ smp_boot_cpus(void) } bogosum = 0; - for (i = 0; i < NR_CPUS; i++) { + for (i = 0; i < NR_CPUS; i++) { if (cpu_present_mask & (1L << i)) bogosum += cpu_data[i].loops_per_sec; - } + } printk(KERN_INFO "SMP: Total of %d processors activated " "(%lu.%02lu BogoMIPS).\n", cpu_count, (bogosum + 2500) / 500000, @@ -605,7 +605,7 @@ smp_commence(void) extern void update_one_process(struct task_struct *p, unsigned long ticks, - unsigned long user, unsigned long system, + unsigned long user, unsigned long system, int cpu); void @@ -626,13 +626,13 @@ smp_percpu_timer_interrupt(struct pt_regs *regs) irq_enter(cpu, TIMER_IRQ); update_one_process(current, 1, user, !user, cpu); - if (current->pid) { - if (--current->counter <= 0) { + if (current->pid) { + if (--current->counter <= 0) { current->counter = 0; - current->need_resched = 1; - } + current->need_resched = 1; + } - if (user) { + if (user) { if (current->priority < DEF_PRIORITY) { kstat.cpu_nice++; kstat.per_cpu_nice[cpu]++; @@ -640,11 +640,11 @@ smp_percpu_timer_interrupt(struct pt_regs *regs) kstat.cpu_user++; kstat.per_cpu_user[cpu]++; } - } else { + } else { kstat.cpu_system++; kstat.per_cpu_system[cpu]++; - } - } + } + } data->prof_counter = data->prof_multiplier; irq_exit(cpu, TIMER_IRQ); @@ -722,7 +722,7 @@ again: return -EBUSY; while (*(void **)lock) - schedule(); + barrier(); goto again; } diff --git a/arch/alpha/kernel/sys_jensen.c b/arch/alpha/kernel/sys_jensen.c index ad156c9f2..c2abe26f2 100644 --- a/arch/alpha/kernel/sys_jensen.c +++ b/arch/alpha/kernel/sys_jensen.c @@ -111,11 +111,20 @@ jensen_init_irq(void) } static void +jensen_init_arch(void) +{ + __direct_map_base = 0; + __direct_map_size = 0xffffffff; +} + +static void jensen_machine_check (u64 vector, u64 la, struct pt_regs *regs) { printk(KERN_CRIT "Machine check\n"); } +#define jensen_pci_tbi ((void*)0) + /* * The System Vector @@ -136,7 +145,7 @@ struct alpha_machine_vector jensen_mv __initmv = { ack_irq: common_ack_irq, device_interrupt: jensen_device_interrupt, - init_arch: NULL, + init_arch: jensen_init_arch, init_irq: jensen_init_irq, init_pit: common_init_pit, init_pci: NULL, diff --git a/arch/alpha/kernel/sys_sio.c b/arch/alpha/kernel/sys_sio.c index 78025bec9..2359755bf 100644 --- a/arch/alpha/kernel/sys_sio.c +++ b/arch/alpha/kernel/sys_sio.c @@ -55,52 +55,6 @@ sio_init_irq(void) } static inline void __init -xl_init_arch(void) -{ - struct pci_controler *hose; - - /* - * Set up the PCI->physical memory translation windows. For - * the XL we *must* use both windows, in order to maximize the - * amount of physical memory that can be used to DMA from the - * ISA bus, and still allow PCI bus devices access to all of - * host memory. - * - * See <asm/apecs.h> for window bases and sizes. - * - * This restriction due to the true XL motherboards' 82379AB SIO - * PCI<->ISA bridge chip which passes only 27 bits of address... - */ - - *(vuip)APECS_IOC_PB1R = 1<<19 | (APECS_XL_DMA_WIN1_BASE & 0xfff00000U); - *(vuip)APECS_IOC_PM1R = (APECS_XL_DMA_WIN1_SIZE - 1) & 0xfff00000U; - *(vuip)APECS_IOC_TB1R = 0; - - *(vuip)APECS_IOC_PB2R = 1<<19 | (APECS_XL_DMA_WIN2_BASE & 0xfff00000U); - *(vuip)APECS_IOC_PM2R = (APECS_XL_DMA_WIN2_SIZE - 1) & 0xfff00000U; - *(vuip)APECS_IOC_TB2R = 0; - - /* - * Finally, clear the HAXR2 register, which gets used for PCI - * Config Space accesses. That is the way we want to use it, - * and we do not want to depend on what ARC or SRM might have - * left behind... - */ - - *(vuip)APECS_IOC_HAXR2 = 0; mb(); - - /* - * Create our single hose. - */ - - hose = alloc_pci_controler(); - hose->io_space = &ioport_resource; - hose->mem_space = &iomem_resource; - hose->config_space = LCA_CONF; - hose->index = 0; -} - -static inline void __init alphabook1_init_arch(void) { /* The AlphaBook1 has LCD video fixed at 800x600, @@ -448,7 +402,7 @@ struct alpha_machine_vector xl_mv __initmv = { DO_EV4_MMU, DO_DEFAULT_RTC, DO_APECS_IO, - BUS(apecs_xl), + BUS(apecs), machine_check: apecs_machine_check, max_dma_address: ALPHA_XL_MAX_DMA_ADDRESS, min_io_address: DEFAULT_IO_BASE, @@ -460,7 +414,7 @@ struct alpha_machine_vector xl_mv __initmv = { ack_irq: common_ack_irq, device_interrupt: isa_device_interrupt, - init_arch: xl_init_arch, + init_arch: lca_init_arch, init_irq: sio_init_irq, init_pit: common_init_pit, init_pci: noname_init_pci, diff --git a/arch/alpha/lib/semaphore.S b/arch/alpha/lib/semaphore.S index 3dbeeec5f..517285ea4 100644 --- a/arch/alpha/lib/semaphore.S +++ b/arch/alpha/lib/semaphore.S @@ -1,7 +1,7 @@ /* * linux/arch/alpha/lib/semaphore.S * - * Copyright (C) 1999 Richard Henderson + * Copyright (C) 1999, 2000 Richard Henderson */ /* @@ -181,3 +181,168 @@ __up_wakeup: lda $30, 20*8($30) ret $31, ($28), 0 .end __up_wakeup + +/* __down_read_failed takes the semaphore in $24, count in $25; + clobbers $24, $25 and $28. */ + + .globl __down_read_failed + .ent __down_read_failed +__down_read_failed: + ldgp $29,0($27) + lda $30, -18*8($30) + stq $28, 0*8($30) + stq $0, 1*8($30) + stq $1, 2*8($30) + stq $2, 3*8($30) + stq $3, 4*8($30) + stq $4, 5*8($30) + stq $5, 6*8($30) + stq $6, 7*8($30) + stq $7, 8*8($30) + stq $16, 9*8($30) + stq $17, 10*8($30) + stq $18, 11*8($30) + stq $19, 12*8($30) + stq $20, 13*8($30) + stq $21, 14*8($30) + stq $22, 15*8($30) + stq $23, 16*8($30) + stq $26, 17*8($30) + .frame $30, 18*8, $28 + .prologue 1 + + mov $24, $16 + mov $25, $17 + jsr __down_read + + ldq $28, 0*8($30) + ldq $0, 1*8($30) + ldq $1, 2*8($30) + ldq $2, 3*8($30) + ldq $3, 4*8($30) + ldq $4, 5*8($30) + ldq $5, 6*8($30) + ldq $6, 7*8($30) + ldq $7, 8*8($30) + ldq $16, 9*8($30) + ldq $17, 10*8($30) + ldq $18, 11*8($30) + ldq $19, 12*8($30) + ldq $20, 13*8($30) + ldq $21, 14*8($30) + ldq $22, 15*8($30) + ldq $23, 16*8($30) + ldq $26, 17*8($30) + lda $30, 18*8($30) + ret $31, ($28), 0 + .end __down_read_failed + +/* __down_write_failed takes the semaphore in $24, count in $25; + clobbers $24, $25 and $28. */ + + .globl __down_write_failed + .ent __down_write_failed +__down_write_failed: + ldgp $29,0($27) + lda $30, -20*8($30) + stq $28, 0*8($30) + stq $0, 1*8($30) + stq $1, 2*8($30) + stq $2, 3*8($30) + stq $3, 4*8($30) + stq $4, 5*8($30) + stq $5, 6*8($30) + stq $6, 7*8($30) + stq $7, 8*8($30) + stq $16, 9*8($30) + stq $17, 10*8($30) + stq $18, 11*8($30) + stq $19, 12*8($30) + stq $20, 13*8($30) + stq $21, 14*8($30) + stq $22, 15*8($30) + stq $23, 16*8($30) + stq $26, 17*8($30) + .frame $30, 18*8, $28 + .prologue 1 + + mov $24, $16 + mov $25, $17 + jsr __down_write + + ldq $28, 0*8($30) + ldq $0, 1*8($30) + ldq $1, 2*8($30) + ldq $2, 3*8($30) + ldq $3, 4*8($30) + ldq $4, 5*8($30) + ldq $5, 6*8($30) + ldq $6, 7*8($30) + ldq $7, 8*8($30) + ldq $16, 9*8($30) + ldq $17, 10*8($30) + ldq $18, 11*8($30) + ldq $19, 12*8($30) + ldq $20, 13*8($30) + ldq $21, 14*8($30) + ldq $22, 15*8($30) + ldq $23, 16*8($30) + ldq $26, 17*8($30) + lda $30, 18*8($30) + ret $31, ($28), 0 + .end __down_write_failed + +/* __rwsem_wake takes the semaphore in $24, readers in $25; + clobbers $24, $25, and $28. */ + + .globl __rwsem_wake + .ent __rwsem_wake +__rwsem_wake: + ldgp $29,0($27) + lda $30, -18*8($30) + stq $28, 0*8($30) + stq $0, 1*8($30) + stq $1, 2*8($30) + stq $2, 3*8($30) + stq $3, 4*8($30) + stq $4, 5*8($30) + stq $5, 6*8($30) + stq $6, 7*8($30) + stq $7, 8*8($30) + stq $16, 9*8($30) + stq $17, 10*8($30) + stq $18, 11*8($30) + stq $19, 12*8($30) + stq $20, 13*8($30) + stq $21, 14*8($30) + stq $22, 15*8($30) + stq $23, 16*8($30) + stq $26, 17*8($30) + .frame $30, 18*8, $28 + .prologue 1 + + mov $24, $16 + mov $25, $17 + jsr __do_rwsem_wake + + ldq $28, 0*8($30) + ldq $0, 1*8($30) + ldq $1, 2*8($30) + ldq $2, 3*8($30) + ldq $3, 4*8($30) + ldq $4, 5*8($30) + ldq $5, 6*8($30) + ldq $6, 7*8($30) + ldq $7, 8*8($30) + ldq $16, 9*8($30) + ldq $17, 10*8($30) + ldq $18, 11*8($30) + ldq $19, 12*8($30) + ldq $20, 13*8($30) + ldq $21, 14*8($30) + ldq $22, 15*8($30) + ldq $23, 16*8($30) + ldq $26, 17*8($30) + lda $30, 18*8($30) + ret $31, ($28), 0 + .end __rwsem_wake diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index 97bb6df9d..e2142b63c 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -34,7 +34,6 @@ static unsigned long totalram_pages; extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); struct thread_struct original_pcb; @@ -173,9 +172,6 @@ show_mem(void) printk("%ld pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif } static inline unsigned long @@ -195,7 +191,7 @@ paging_init(void) { unsigned long newptbr; unsigned long original_pcb_ptr; - unsigned int zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; unsigned long dma_pfn, high_pfn; dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; diff --git a/arch/alpha/vmlinux.lds b/arch/alpha/vmlinux.lds index 94270b390..4b49a5369 100644 --- a/arch/alpha/vmlinux.lds +++ b/arch/alpha/vmlinux.lds @@ -82,4 +82,6 @@ SECTIONS .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } + + /DISCARD/ : { *(.text.exit) *(.data.exit) } } diff --git a/arch/arm/def-configs/a5k b/arch/arm/def-configs/a5k index b5fd4b315..21a6488eb 100644 --- a/arch/arm/def-configs/a5k +++ b/arch/arm/def-configs/a5k @@ -167,6 +167,11 @@ CONFIG_MOUSE=y # CONFIG_FTAPE is not set # CONFIG_DRM is not set # CONFIG_DRM_TDFX is not set + +# +# PCMCIA character device support +# +# CONFIG_PCMCIA_SERIAL_CS is not set # CONFIG_AGP is not set # @@ -324,6 +329,11 @@ CONFIG_ARM_ETHER3=y # CONFIG_WAN is not set # +# PCMCIA network device support +# +# CONFIG_NET_PCMCIA is not set + +# # SCSI support # # CONFIG_SCSI is not set diff --git a/arch/arm/def-configs/footbridge b/arch/arm/def-configs/footbridge index d68d0b358..e43a8d2a9 100644 --- a/arch/arm/def-configs/footbridge +++ b/arch/arm/def-configs/footbridge @@ -54,6 +54,7 @@ CONFIG_BINFMT_ELF=y CONFIG_PARPORT=y CONFIG_PARPORT_PC=y CONFIG_PARPORT_PC_FIFO=y +# CONFIG_PARPORT_PC_PCMCIA is not set # CONFIG_PARPORT_ARC is not set # CONFIG_PARPORT_AMIGA is not set # CONFIG_PARPORT_MFC3 is not set @@ -264,6 +265,12 @@ CONFIG_VIDEO_CYBERPRO=m # CONFIG_DRM_TDFX is not set # +# PCMCIA character device support +# +# CONFIG_PCMCIA_SERIAL_CS is not set +# CONFIG_AGP is not set + +# # Support for USB # CONFIG_USB=m @@ -497,6 +504,11 @@ CONFIG_SLIP_MODE_SLIP6=y # CONFIG_WAN is not set # +# PCMCIA network device support +# +# CONFIG_NET_PCMCIA is not set + +# # SCSI support # # CONFIG_SCSI is not set diff --git a/arch/arm/def-configs/rpc b/arch/arm/def-configs/rpc index e801bf641..a22e1d992 100644 --- a/arch/arm/def-configs/rpc +++ b/arch/arm/def-configs/rpc @@ -50,6 +50,7 @@ CONFIG_BINFMT_ELF=y CONFIG_PARPORT=y CONFIG_PARPORT_PC=y # CONFIG_PARPORT_PC_FIFO is not set +# CONFIG_PARPORT_PC_PCMCIA is not set # CONFIG_PARPORT_ARC is not set # CONFIG_PARPORT_AMIGA is not set # CONFIG_PARPORT_MFC3 is not set @@ -176,6 +177,11 @@ CONFIG_MOUSE=y # CONFIG_FTAPE is not set # CONFIG_DRM is not set # CONFIG_DRM_TDFX is not set + +# +# PCMCIA character device support +# +# CONFIG_PCMCIA_SERIAL_CS is not set # CONFIG_AGP is not set CONFIG_RPCMOUSE=y @@ -349,6 +355,11 @@ CONFIG_PPP=m # CONFIG_WAN is not set # +# PCMCIA network device support +# +# CONFIG_NET_PCMCIA is not set + +# # SCSI support # CONFIG_SCSI=y diff --git a/arch/arm/defconfig b/arch/arm/defconfig index 472c637c3..e43a8d2a9 100644 --- a/arch/arm/defconfig +++ b/arch/arm/defconfig @@ -54,6 +54,7 @@ CONFIG_BINFMT_ELF=y CONFIG_PARPORT=y CONFIG_PARPORT_PC=y CONFIG_PARPORT_PC_FIFO=y +# CONFIG_PARPORT_PC_PCMCIA is not set # CONFIG_PARPORT_ARC is not set # CONFIG_PARPORT_AMIGA is not set # CONFIG_PARPORT_MFC3 is not set @@ -262,6 +263,11 @@ CONFIG_VIDEO_CYBERPRO=m # CONFIG_FTAPE is not set # CONFIG_DRM is not set # CONFIG_DRM_TDFX is not set + +# +# PCMCIA character device support +# +# CONFIG_PCMCIA_SERIAL_CS is not set # CONFIG_AGP is not set # @@ -498,6 +504,11 @@ CONFIG_SLIP_MODE_SLIP6=y # CONFIG_WAN is not set # +# PCMCIA network device support +# +# CONFIG_NET_PCMCIA is not set + +# # SCSI support # # CONFIG_SCSI is not set diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 3b79f080b..3920edade 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -16,9 +16,9 @@ ifeq ($(CONFIG_ISA_DMA),y) ISA_DMA_OBJS += dma-isa.o endif -O_OBJS_arc = dma-arc.o iic.o fiq.o oldlatches.o -O_OBJS_a5k = dma-a5k.o iic.o fiq.o -O_OBJS_rpc = dma-rpc.o iic.o fiq.o +O_OBJS_arc = dma-arc.o iic.o fiq.o time-acorn.o oldlatches.o +O_OBJS_a5k = dma-a5k.o iic.o fiq.o time-acorn.o +O_OBJS_rpc = dma-rpc.o iic.o fiq.o time-acorn.o O_OBJS_ebsa110 = dma-dummy.o O_OBJS_footbridge = dma-footbridge.o $(ISA_DMA_OBJS) isa.o O_OBJS_nexuspci = dma-dummy.o diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index d055ae1fb..7a1b57c22 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -381,7 +381,7 @@ void __init pcibios_init(void) * Assign any unassigned resources. Note that we really ought to * have min/max stuff here - max mem address is 0x0fffffff */ - pci_assign_unassigned_resources(hw_pci->io_start, hw_pci->mem_start); + pci_assign_unassigned_resources(); pci_fixup_irqs(hw_pci->swizzle, hw_pci->map_irq); pci_set_bus_ranges(); @@ -404,17 +404,34 @@ char * __init pcibios_setup(char *str) return str; } -/* - * Assign new address to PCI resource. We hope our resource information - * is complete. - * - * Expects start=0, end=size-1, flags=resource type. - */ -int pci_assign_resource(struct pci_dev *dev, int i) +void pcibios_align_resource(void *data, struct resource *res, unsigned long size) { - return 0; } -void pcibios_align_resource(void *data, struct resource *res, unsigned long size) +int pcibios_enable_device(struct pci_dev *dev) { + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx = 0; idx < 6; idx++) { + r = dev->resource + idx; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because" + " of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: enabling device %s (%04x -> %04x)\n", + dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; } diff --git a/arch/arm/kernel/time-acorn.c b/arch/arm/kernel/time-acorn.c new file mode 100644 index 000000000..8a7d964fc --- /dev/null +++ b/arch/arm/kernel/time-acorn.c @@ -0,0 +1,71 @@ +/* + * linux/arch/arm/kernel/time-acorn.c + * + * Copyright (c) 1996-2000 Russell King. + * + * Changelog: + * 24-Sep-1996 RMK Created + * 10-Oct-1996 RMK Brought up to date with arch-sa110eval + * 04-Dec-1997 RMK Updated for new arch/arm/time.c + */ +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/ioc.h> +#include <asm/irq.h> + +extern unsigned long (*gettimeoffset)(void); + +static unsigned long ioctime_gettimeoffset(void) +{ + unsigned int count1, count2, status1, status2; + unsigned long offset = 0; + + status1 = inb(IOC_IRQREQA); + barrier (); + outb (0, IOC_T0LATCH); + barrier (); + count1 = inb(IOC_T0CNTL) | (inb(IOC_T0CNTH) << 8); + barrier (); + status2 = inb(IOC_IRQREQA); + barrier (); + outb (0, IOC_T0LATCH); + barrier (); + count2 = inb(IOC_T0CNTL) | (inb(IOC_T0CNTH) << 8); + + if (count2 < count1) { + /* + * This means that we haven't just had an interrupt + * while reading into status2. + */ + if (status2 & (1 << 5)) + offset = tick; + count1 = count2; + } else if (count2 > count1) { + /* + * We have just had another interrupt while reading + * status2. + */ + offset += tick; + count1 = count2; + } + + count1 = LATCH - count1; + /* + * count1 = number of clock ticks since last interrupt + */ + offset += count1 * tick / LATCH; + return offset; +} + +void __init ioctime_init(void) +{ + outb(LATCH & 255, IOC_T0LTCHL); + outb(LATCH >> 8, IOC_T0LTCHH); + outb(0, IOC_T0GO); + + gettimeoffset = ioctime_gettimeoffset; +} diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 67c2597e6..0fcad67c0 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -12,15 +12,12 @@ * 1998-12-20 Updated NTP code according to technical memorandum Jan '96 * "A Kernel Model for Precision Timekeeping" by Dave Mills */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> -#include <linux/param.h> -#include <linux/string.h> -#include <linux/mm.h> #include <linux/interrupt.h> #include <linux/time.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/smp.h> @@ -32,6 +29,7 @@ #include <asm/hardware.h> extern int setup_arm_irq(int, struct irqaction *); +extern void setup_timer(void); extern volatile unsigned long lost_ticks; /* change this if you have some constant time drift */ @@ -45,6 +43,26 @@ extern volatile unsigned long lost_ticks; #define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) #endif +static int dummy_set_rtc(void) +{ + return 0; +} + +/* + * hook for setting the RTC's idea of the current time. + */ +int (*set_rtc)(void) = dummy_set_rtc; + +static unsigned long dummy_gettimeoffset(void) +{ + return 0; +} + +/* + * hook for getting the time offset + */ +unsigned long (*gettimeoffset)(void) = dummy_gettimeoffset; + /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. @@ -60,9 +78,9 @@ extern volatile unsigned long lost_ticks; * machines were long is 32-bit! (However, as time_t is signed, we * will already get problems at other places on 2038-01-19 03:14:08) */ -unsigned long mktime(unsigned int year, unsigned int mon, - unsigned int day, unsigned int hour, - unsigned int min, unsigned int sec) +unsigned long +mktime(unsigned int year, unsigned int mon, unsigned int day, + unsigned int hour, unsigned int min, unsigned int sec) { if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ mon += 12; /* Puts Feb last since it has leap day */ @@ -77,11 +95,14 @@ unsigned long mktime(unsigned int year, unsigned int mon, } /* - * Handle profile stuff... + * Handle kernel profile stuff... */ -static void do_profile(unsigned long pc) +static inline void do_profile(struct pt_regs *regs) { - if (prof_buffer && current->pid) { + if (!user_mode(regs) && + prof_buffer && + current->pid) { + unsigned long pc = instruction_pointer(regs); extern int _stext; pc -= (unsigned long)&_stext; @@ -95,12 +116,61 @@ static void do_profile(unsigned long pc) } } -#include <asm/arch/time.h> +static long next_rtc_update; + +/* + * If we have an externally synchronized linux clock, then update + * CMOS clock accordingly every ~11 minutes. set_rtc() has to be + * called as close as possible to 500 ms before the new second + * starts. + */ +static inline void do_set_rtc(void) +{ + if (time_status & STA_UNSYNC || set_rtc == NULL) + return; + + if (next_rtc_update && + time_before(xtime.tv_sec, next_rtc_update)) + return; + + if (xtime.tv_usec < 50000 - (tick >> 1) && + xtime.tv_usec >= 50000 + (tick >> 1)) + return; + + if (set_rtc()) + /* + * rtc update failed. Try again in 60s + */ + next_rtc_update = xtime.tv_sec + 60; + else + next_rtc_update = xtime.tv_sec + 660; +} + +#ifdef CONFIG_LEDS -static unsigned long do_gettimeoffset(void) +#include <asm/leds.h> + +static void do_leds(void) { - return gettimeoffset (); + static unsigned int count = 50; + static int last_pid; + + if (current->pid != last_pid) { + last_pid = current->pid; + if (last_pid) + leds_event(led_idle_end); + else + leds_event(led_idle_start); + } + + if (--count == 0) { + count = 50; + leds_event(led_timer); + } } +#else +#define do_leds() +#endif void do_gettimeofday(struct timeval *tv) { @@ -108,7 +178,7 @@ void do_gettimeofday(struct timeval *tv) save_flags_cli (flags); *tv = xtime; - tv->tv_usec += do_gettimeoffset(); + tv->tv_usec += gettimeoffset(); /* * xtime is atomically updated in timer_bh. lost_ticks is @@ -134,7 +204,7 @@ void do_settimeofday(struct timeval *tv) * Discover what correction gettimeofday * would have done, and then undo it! */ - tv->tv_usec -= do_gettimeoffset(); + tv->tv_usec -= gettimeoffset(); if (tv->tv_usec < 0) { tv->tv_usec += 1000000; @@ -149,9 +219,25 @@ void do_settimeofday(struct timeval *tv) sti(); } +static struct irqaction timer_irq = { + NULL, 0, 0, "timer", NULL, NULL +}; + +/* + * Include architecture specific code + */ +#include <asm/arch/time.h> + +/* + * This must cause the timer to start ticking. + * It doesn't have to set the current time though + * from an RTC - it can be done later once we have + * some buses initialised. + */ void __init time_init(void) { xtime.tv_usec = 0; + xtime.tv_sec = 0; setup_timer(); } diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index ac15651d3..18d9931ee 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -370,7 +370,7 @@ asmlinkage void arm_invalidptr(const char *function, int size) asmlinkage void baddataabort(int code, unsigned long instr, struct pt_regs *regs) { - unsigned long phys, addr = instruction_pointer(regs); + unsigned long addr = instruction_pointer(regs); #ifdef CONFIG_DEBUG_ERRORS dump_instr(addr, 1); @@ -383,11 +383,8 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs) pmd_t *pmd; pmd = pmd_offset (pgd, addr); printk (", *pmd = %08lx", pmd_val (*pmd)); - if (!pmd_none (*pmd)) { - unsigned long ptr = pte_page(*pte_offset(pmd, addr)); - printk (", *pte = %08lx", pte_val (*pte_offset (pmd, addr))); - phys = ptr + (addr & 0x7fff); - } + if (!pmd_none (*pmd)) + printk (", *pte = %08lx", pte_val(*pte_offset (pmd, addr))); } printk ("\n"); } diff --git a/arch/arm/lib/io.c b/arch/arm/lib/io.c index a7720eca1..198633edd 100644 --- a/arch/arm/lib/io.c +++ b/arch/arm/lib/io.c @@ -1,4 +1,5 @@ #include <linux/module.h> +#include <linux/types.h> #include <asm/io.h> @@ -6,7 +7,7 @@ * Copy data from IO memory space to "real" memory space. * This needs to be optimized. */ -void _memcpy_fromio(void * to, unsigned long from, unsigned long count) +void _memcpy_fromio(void * to, unsigned long from, size_t count) { while (count) { count--; @@ -20,7 +21,7 @@ void _memcpy_fromio(void * to, unsigned long from, unsigned long count) * Copy data from "real" memory space to IO memory space. * This needs to be optimized. */ -void _memcpy_toio(unsigned long to, const void * from, unsigned long count) +void _memcpy_toio(unsigned long to, const void * from, size_t count) { while (count) { count--; @@ -34,7 +35,7 @@ void _memcpy_toio(unsigned long to, const void * from, unsigned long count) * "memset" on IO memory space. * This needs to be optimized. */ -void _memset_io(unsigned long dst, int c, unsigned long count) +void _memset_io(unsigned long dst, int c, size_t count) { while (count) { count--; diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index bc4cf1ed1..db3ac5a4d 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -35,8 +35,6 @@ static unsigned long totalram_pages; pgd_t swapper_pg_dir[PTRS_PER_PGD]; -extern void show_net_buffers(void); - /* * empty_bad_page is the page that is used for page faults when * linux is out-of-memory. Older versions of linux just did a @@ -157,9 +155,6 @@ void show_mem(void) printk("%ld page tables cached\n", pgtable_cache_size); #endif show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif } /* diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index fb3007f8a..d59b4e16c 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -115,7 +115,7 @@ static int remap_area_pages(unsigned long address, unsigned long phys_addr, * 'flags' are the extra L_PTE_ flags that you want to specify for this * mapping. See include/asm-arm/proc-armv/pgtable.h for more information. */ -void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +void * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags) { void * addr; struct vm_struct * area; diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index 76d101507..92007d975 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -34,6 +34,7 @@ static int __init nocache_setup(char *__unused) { cr_alignment &= ~4; cr_no_alignment &= ~4; + flush_cache_all(); set_cr(cr_alignment); return 1; } @@ -42,6 +43,7 @@ static int __init nowrite_setup(char *__unused) { cr_alignment &= ~(8|4); cr_no_alignment &= ~(8|4); + flush_cache_all(); set_cr(cr_alignment); return 1; } @@ -284,7 +286,7 @@ static void __init create_mapping(struct map_desc *md) void __init pagetable_init(void) { - struct map_desc *init_maps, *p; + struct map_desc *init_maps, *p, *q; unsigned long address = 0; int i; @@ -358,17 +360,18 @@ void __init pagetable_init(void) * pgdir entries that are not in the description. */ i = 0; + q = init_maps; do { - if (address < init_maps->virtual || init_maps == p) { + if (address < q->virtual || q == p) { clear_mapping(address); address += PGDIR_SIZE; } else { - create_mapping(init_maps); + create_mapping(q); - address = init_maps->virtual + init_maps->length; + address = q->virtual + q->length; address = (address + PGDIR_SIZE - 1) & PGDIR_MASK; - init_maps ++; + q ++; } } while (address != 0); diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S index eb2a9a2c5..51e0fc893 100644 --- a/arch/i386/boot/compressed/head.S +++ b/arch/i386/boot/compressed/head.S @@ -12,10 +12,6 @@ * the page directory. [According to comments etc elsewhere on a compressed * kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC] * - * In SMP mode we keep this page safe. Really we ought to shuffle things and - * put the trampoline here. - AC. An SMP trampoline enters with %cx holding - * the stack base. - * * Page 0 is deliberately kept safe, since System Management Mode code in * laptops may need to access the BIOS data stored there. This is also * useful for future device drivers that either access the BIOS via VM86 @@ -41,24 +37,7 @@ startup_32: movl %ax,%es movl %ax,%fs movl %ax,%gs -#ifdef __SMP__ - orw %bx,%bx # What state are we in BX=1 for SMP - # 0 for boot - jz 2f # Initial boot - -/* - * We are trampolining an SMP processor - */ - mov %ax,%ss - xorl %eax,%eax # Back to 0 - mov %cx,%ax # SP low 16 bits - movl %eax,%esp - pushl $0 # Clear NT - popfl - ljmp $(__KERNEL_CS), $0x100000 # Into C and sanity -2: -#endif lss SYMBOL_NAME(stack_start),%esp xorl %eax,%eax 1: incl %eax # check that A20 really IS enabled diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 0c3dcfef1..fcc962a11 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -173,7 +173,9 @@ CONFIG_SCSI=y # SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 # CONFIG_CHR_DEV_ST is not set +CONFIG_ST_EXTRA_DEVS=2 # CONFIG_BLK_DEV_SR is not set # CONFIG_CHR_DEV_SG is not set @@ -228,6 +230,7 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set # CONFIG_SCSI_SEAGATE is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_T128 is not set @@ -272,7 +275,7 @@ CONFIG_NET_EISA=y # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set # CONFIG_DE4X5 is not set -# CONFIG_DEC_ELCP is not set +# CONFIG_TULIP is not set # CONFIG_DGRS is not set CONFIG_EEXPRESS_PRO100=y # CONFIG_NE2K_PCI is not set @@ -429,7 +432,6 @@ CONFIG_AUTOFS4_FS=y # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_BFS_FS_WRITE is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 6b7302f99..96be4dff6 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -40,23 +40,19 @@ else endif endif -ifdef CONFIG_ACPI -OX_OBJS += pm.o -else -ifdef CONFIG_APM -OX_OBJS += pm.o -endif -endif - ifeq ($(CONFIG_ACPI),y) - O_OBJS += acpi.o +O_OBJS += acpi.o +else + ifeq ($(CONFIG_ACPI),m) + M_OBJS += acpi.o + endif endif ifeq ($(CONFIG_APM),y) -OX_OBJS += apm.o +O_OBJS += apm.o else ifeq ($(CONFIG_APM),m) - MX_OBJS += apm.o + M_OBJS += apm.o endif endif diff --git a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c index 5ad3106f8..8ae8bc299 100644 --- a/arch/i386/kernel/acpi.c +++ b/arch/i386/kernel/acpi.c @@ -105,7 +105,6 @@ static int acpi_p_lvl2_tested = 0; static int acpi_p_lvl3_tested = 0; static int acpi_disabled = 0; -int acpi_active = 0; // bits 8-15 are SLP_TYPa, bits 0-7 are SLP_TYPb static unsigned long acpi_slp_typ[] = @@ -564,7 +563,7 @@ static int __init acpi_init_piix4(struct pci_dev *dev) /* * Init VIA ACPI device and create a fake FACP */ -static int __init acpi_init_via686a(struct pci_dev *dev) +static int __init acpi_init_via(struct pci_dev *dev) { u32 base; u8 tmp, irq; @@ -631,6 +630,7 @@ typedef enum { CH_UNKNOWN = 0, CH_INTEL_PIIX4, + CH_VIA_586, CH_VIA_686A, } acpi_chip_t; @@ -642,12 +642,13 @@ const static struct { {NULL,}, {acpi_init_piix4}, - {acpi_init_via686a}, + {acpi_init_via}, }; const static struct pci_device_id acpi_pci_tbl[] = { {0x8086, 0x7113, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_INTEL_PIIX4}, + {0x1106, 0x3040, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VIA_586}, {0x1106, 0x3057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VIA_686A}, {0,}, /* terminate list */ }; @@ -792,7 +793,7 @@ static void wake_on_busmaster(struct acpi_facp *facp) /* * Idle loop (uniprocessor only) */ -static void acpi_idle_handler(void) +static void acpi_idle(void) { static int sleep_level = 1; struct acpi_facp *facp = acpi_facp; @@ -1009,56 +1010,63 @@ static void acpi_enter_sx(acpi_sstate_t state) /* * Enter soft-off (S5) */ -static void acpi_power_off_handler(void) +static void acpi_power_off(void) { acpi_enter_sx(ACPI_S5); } /* + * Claim I/O port if available + */ +static int acpi_claim(unsigned long start, unsigned long size) +{ + if (start && size) { + if (check_region(start, size)) + return -EBUSY; + request_region(start, size, "acpi"); + } + return 0; +} + +/* * Claim ACPI I/O ports */ static int acpi_claim_ioports(struct acpi_facp *facp) { // we don't get a guarantee of contiguity for any of the ACPI registers - if (facp->pm1a_evt) - request_region(facp->pm1a_evt, facp->pm1_evt_len, "acpi"); - if (facp->pm1b_evt) - request_region(facp->pm1b_evt, facp->pm1_evt_len, "acpi"); - if (facp->pm1a_cnt) - request_region(facp->pm1a_cnt, facp->pm1_cnt_len, "acpi"); - if (facp->pm1b_cnt) - request_region(facp->pm1b_cnt, facp->pm1_cnt_len, "acpi"); - if (facp->pm_tmr) - request_region(facp->pm_tmr, facp->pm_tm_len, "acpi"); - if (facp->gpe0) - request_region(facp->gpe0, facp->gpe0_len, "acpi"); - if (facp->gpe1) - request_region(facp->gpe1, facp->gpe1_len, "acpi"); - + if (acpi_claim(facp->pm1a_evt, facp->pm1_evt_len) + || acpi_claim(facp->pm1b_evt, facp->pm1_evt_len) + || acpi_claim(facp->pm1a_cnt, facp->pm1_cnt_len) + || acpi_claim(facp->pm1b_cnt, facp->pm1_cnt_len) + || acpi_claim(facp->pm_tmr, facp->pm_tm_len) + || acpi_claim(facp->gpe0, facp->gpe0_len) + || acpi_claim(facp->gpe1, facp->gpe1_len)) + return -EBUSY; return 0; } /* + * Release I/O port if claimed + */ +static void acpi_release(unsigned long start, unsigned long size) +{ + if (start && size) + release_region(start, size); +} + +/* * Free ACPI I/O ports */ static int acpi_release_ioports(struct acpi_facp *facp) { // we don't get a guarantee of contiguity for any of the ACPI registers - if (facp->pm1a_evt) - release_region(facp->pm1a_evt, facp->pm1_evt_len); - if (facp->pm1b_evt) - release_region(facp->pm1b_evt, facp->pm1_evt_len); - if (facp->pm1a_cnt) - release_region(facp->pm1a_cnt, facp->pm1_cnt_len); - if (facp->pm1b_cnt) - release_region(facp->pm1b_cnt, facp->pm1_cnt_len); - if (facp->pm_tmr) - release_region(facp->pm_tmr, facp->pm_tm_len); - if (facp->gpe0) - release_region(facp->gpe0, facp->gpe0_len); - if (facp->gpe1) - release_region(facp->gpe1, facp->gpe1_len); - + acpi_release(facp->gpe1, facp->gpe1_len); + acpi_release(facp->gpe0, facp->gpe0_len); + acpi_release(facp->pm_tmr, facp->pm_tm_len); + acpi_release(facp->pm1b_cnt, facp->pm1_cnt_len); + acpi_release(facp->pm1a_cnt, facp->pm1_cnt_len); + acpi_release(facp->pm1b_evt, facp->pm1_evt_len); + acpi_release(facp->pm1a_evt, facp->pm1_evt_len); return 0; } @@ -1322,6 +1330,14 @@ static int __init acpi_init(void) = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat * 5); } + if (acpi_claim_ioports(acpi_facp)) { + printk(KERN_ERR "ACPI: I/O port allocation failed\n"); + if (pci_driver_registered) + pci_unregister_driver(&acpi_driver); + acpi_destroy_tables(); + return -ENODEV; + } + if (acpi_facp->sci_int && request_irq(acpi_facp->sci_int, acpi_irq, @@ -1338,16 +1354,15 @@ static int __init acpi_init(void) return -ENODEV; } - acpi_claim_ioports(acpi_facp); acpi_sysctl = register_sysctl_table(acpi_dir_table, 1); pid = kernel_thread(acpi_control_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - acpi_power_off = acpi_power_off_handler; + pm_power_off = acpi_power_off; - acpi_active = 1; + pm_active = 1; /* * Set up the ACPI idle function. Note that we can't really @@ -1360,7 +1375,7 @@ static int __init acpi_init(void) #endif if (acpi_facp->pm_tmr) - acpi_idle = acpi_idle_handler; + pm_idle = acpi_idle; return 0; } @@ -1370,8 +1385,8 @@ static int __init acpi_init(void) */ static void __exit acpi_exit(void) { - acpi_idle = NULL; - acpi_power_off = NULL; + pm_idle = NULL; + pm_power_off = NULL; unregister_sysctl_table(acpi_sysctl); acpi_disable(acpi_facp); diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index daa7226cd..81a813b05 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -150,13 +150,7 @@ #include <asm/uaccess.h> #include <asm/desc.h> -/* - * Make APM look as much as just another ACPI module as possible.. - */ -#include <linux/acpi.h> - -EXPORT_SYMBOL(apm_register_callback); -EXPORT_SYMBOL(apm_unregister_callback); +#include <linux/pm.h> extern unsigned long get_cmos_time(void); extern void machine_real_restart(unsigned char *, int); @@ -304,13 +298,6 @@ static char * apm_event_name[] = { #define NR_APM_EVENT_NAME \ (sizeof(apm_event_name) / sizeof(apm_event_name[0])) -typedef struct callback_list_t { - int (* callback)(apm_event_t); - struct callback_list_t * next; -} callback_list_t; - -static callback_list_t * callback_list = NULL; - typedef struct lookup_t { int key; char * msg; @@ -687,32 +674,6 @@ static int apm_console_blank(int blank) } #endif -int apm_register_callback(int (*callback)(apm_event_t)) -{ - callback_list_t * new; - - new = kmalloc(sizeof(callback_list_t), GFP_KERNEL); - if (new == NULL) - return -ENOMEM; - new->callback = callback; - new->next = callback_list; - callback_list = new; - return 0; -} - -void apm_unregister_callback(int (*callback)(apm_event_t)) -{ - callback_list_t ** ptr; - callback_list_t * old; - - for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next) - if ((*ptr)->callback == callback) - break; - old = *ptr; - *ptr = old->next; - kfree_s(old, sizeof(callback_list_t)); -} - static int queue_empty(struct apm_bios_struct * as) { return as->event_head == as->event_tail; @@ -848,17 +809,26 @@ static apm_event_t get_event(void) static int send_event(apm_event_t event, apm_event_t undo, struct apm_bios_struct *sender) { - callback_list_t * call; - callback_list_t * fix; - - for (call = callback_list; call != NULL; call = call->next) { - if (call->callback(event) && undo) { - for (fix = callback_list; fix != call; fix = fix->next) - fix->callback(undo); + switch (event) { + case APM_SYS_SUSPEND: + case APM_CRITICAL_SUSPEND: + case APM_USER_SUSPEND: + /* map all suspends to ACPI D3 */ + if (pm_send_request(PM_SUSPEND, (void*) 3)) { + if (apm_bios_info.version > 0x100) + apm_set_power_state(APM_STATE_REJECT); + return 0; + } + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + /* map all resumes to ACPI D0 */ + if (pm_send_request(PM_RESUME, 0)) { if (apm_bios_info.version > 0x100) apm_set_power_state(APM_STATE_REJECT); return 0; } + break; } queue_event(event, sender); @@ -1373,7 +1343,7 @@ static int apm(void *unused) /* Install our power off handler.. */ if (power_off_enabled) - acpi_power_off = apm_power_off; + pm_power_off = apm_power_off; #ifdef CONFIG_MAGIC_SYSRQ sysrq_power_off = apm_power_off; #endif @@ -1381,6 +1351,8 @@ static int apm(void *unused) console_blank_hook = apm_console_blank; #endif + pm_active = 1; + apm_mainloop(); return 0; } @@ -1484,12 +1456,10 @@ static int __init apm_init(void) APM_INIT_ERROR_RETURN; } -#ifdef CONFIG_ACPI - if (acpi_active) { + if (PM_IS_ACTIVE()) { printk(KERN_NOTICE "apm: overridden by ACPI.\n"); APM_INIT_ERROR_RETURN; } -#endif /* * Set up a segment that references the real mode segment 0x40 @@ -1551,4 +1521,4 @@ static int __init apm_init(void) return 0; } -module_init(apm_init) +__initcall(apm_init); diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index f93765754..bcca244c1 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -76,6 +76,7 @@ sigpending = 8 addr_limit = 12 exec_domain = 16 need_resched = 20 +processor = 56 ENOSYS = 38 @@ -203,9 +204,17 @@ ENTRY(system_call) .globl ret_from_sys_call .globl ret_from_intr ret_from_sys_call: - movl SYMBOL_NAME(bh_mask),%eax - andl SYMBOL_NAME(bh_active),%eax - jne handle_bottom_half +#ifdef __SMP__ + movl processor(%ebx),%eax + shll $5,%eax + movl SYMBOL_NAME(softirq_state)(,%eax),%ecx + testl SYMBOL_NAME(softirq_state)+4(,%eax),%ecx +#else + movl SYMBOL_NAME(softirq_state),%ecx + testl SYMBOL_NAME(softirq_state)+4,%ecx +#endif + jne handle_softirq + ret_with_reschedule: cmpl $0,need_resched(%ebx) jne reschedule @@ -250,9 +259,18 @@ badsys: ALIGN ret_from_exception: - movl SYMBOL_NAME(bh_mask),%eax - andl SYMBOL_NAME(bh_active),%eax - jne handle_bottom_half +#ifdef __SMP__ + GET_CURRENT(%ebx) + movl processor(%ebx),%eax + shll $5,%eax + movl SYMBOL_NAME(softirq_state)(,%eax),%ecx + testl SYMBOL_NAME(softirq_state)+4(,%eax),%ecx +#else + movl SYMBOL_NAME(softirq_state),%ecx + testl SYMBOL_NAME(softirq_state)+4,%ecx +#endif + jne handle_softirq + ALIGN ret_from_intr: GET_CURRENT(%ebx) @@ -263,10 +281,10 @@ ret_from_intr: jmp restore_all ALIGN -handle_bottom_half: - call SYMBOL_NAME(do_bottom_half) +handle_softirq: + call SYMBOL_NAME(do_softirq) jmp ret_from_intr - + ALIGN reschedule: call SYMBOL_NAME(schedule) # test diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index 0c8250ad3..f7138faa3 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -212,17 +212,6 @@ is386: pushl %ecx # restore original EFLAGS orl $2,%eax # set MP 2: movl %eax,%cr0 call check_x87 -#ifdef __SMP__ - movb ready,%al # First CPU if 0 - orb %al,%al - jz 4f # First CPU skip this stuff - movl %cr4,%eax # Turn on 4Mb pages - orl $16,%eax - movl %eax,%cr4 - movl %cr3,%eax # Intel specification clarification says - movl %eax,%cr3 # to do this. Maybe it makes a difference. - # Who knows ? -#endif 4: #ifdef __SMP__ incb ready diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 456f4bab2..f58e7485f 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -8,7 +8,7 @@ #include <linux/in6.h> #include <linux/interrupt.h> #include <linux/smp_lock.h> -#include <linux/acpi.h> +#include <linux/pm.h> #include <linux/pci.h> #include <asm/semaphore.h> @@ -44,14 +44,13 @@ EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(__io_virt_debug); -EXPORT_SYMBOL(local_bh_count); -EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(disable_irq_nosync); +EXPORT_SYMBOL(probe_irq_mask); EXPORT_SYMBOL(kernel_thread); -EXPORT_SYMBOL(acpi_idle); -EXPORT_SYMBOL(acpi_power_off); +EXPORT_SYMBOL(pm_idle); +EXPORT_SYMBOL(pm_power_off); EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); @@ -106,11 +105,7 @@ EXPORT_SYMBOL_NOVERS(__read_lock_failed); /* Global SMP irq stuff */ EXPORT_SYMBOL(synchronize_irq); -EXPORT_SYMBOL(synchronize_bh); -EXPORT_SYMBOL(global_bh_count); -EXPORT_SYMBOL(global_bh_lock); EXPORT_SYMBOL(global_irq_holder); -EXPORT_SYMBOL(i386_bh_lock); EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); EXPORT_SYMBOL(__global_save_flags); @@ -141,3 +136,7 @@ EXPORT_SYMBOL(screen_info); #endif EXPORT_SYMBOL(get_wchan); + + +EXPORT_SYMBOL(local_bh_count); +EXPORT_SYMBOL(local_irq_count); diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index fdd4ecda9..0037bebdd 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -223,7 +223,7 @@ static int __init find_timer_pin(int type) static int __init pin_2_irq(int idx, int apic, int pin); int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin) { - int apic, i; + int apic, i, best_guess = -1; for (i = 0; i < mp_irq_entries; i++) { int lbus = mp_irqs[i].mpc_srcbus; @@ -236,10 +236,18 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin) (mp_bus_id_to_type[lbus] == MP_BUS_PCI) && !mp_irqs[i].mpc_irqtype && (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) && - (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f)) && - (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3))) + (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) { + int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); - return pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); + if (pci_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 -1; } diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 317d8a8d7..6112ac036 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -182,30 +182,12 @@ int get_irq_list(char *buf) * Global interrupt locks for SMP. Allow interrupts to come in on any * CPU, yet make cli/sti act globally to protect critical regions.. */ -spinlock_t i386_bh_lock = SPIN_LOCK_UNLOCKED; #ifdef CONFIG_SMP unsigned char global_irq_holder = NO_PROC_ID; unsigned volatile int global_irq_lock; atomic_t global_irq_count; -atomic_t global_bh_count; -atomic_t global_bh_lock; - -/* - * "global_cli()" is a special case, in that it can hold the - * interrupts disabled for a longish time, and also because - * we may be doing TLB invalidates when holding the global - * IRQ lock for historical reasons. Thus we may need to check - * SMP invalidate events specially by hand here (but not in - * any normal spinlocks) - */ -static inline void check_smp_invalidate(int cpu) -{ - if (test_bit(cpu, &smp_invalidate_needed)) - do_flush_tlb_local(); -} - static void show(char * str) { int i; @@ -216,7 +198,7 @@ static void show(char * str) printk("irq: %d [%d %d]\n", atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]); printk("bh: %d [%d %d]\n", - atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]); + spin_is_locked(&global_bh_lock) ? 1 : 0, local_bh_count[0], local_bh_count[1]); stack = (unsigned long *) &stack; for (i = 40; i ; i--) { unsigned long x = *++stack; @@ -228,18 +210,6 @@ static void show(char * str) #define MAXCOUNT 100000000 -static inline void wait_on_bh(void) -{ - int count = MAXCOUNT; - do { - if (!--count) { - show("wait_on_bh"); - count = ~0; - } - /* nothing .. wait for the other bh's to go away */ - } while (atomic_read(&global_bh_count) != 0); -} - /* * I had a lockup scenario where a tight loop doing * spin_unlock()/spin_lock() on CPU#1 was racing with @@ -279,7 +249,7 @@ static inline void wait_on_irq(int cpu) * already executing in one.. */ if (!atomic_read(&global_irq_count)) { - if (local_bh_count[cpu] || !atomic_read(&global_bh_count)) + if (local_bh_count[cpu] || !spin_is_locked(&global_bh_lock)) break; } @@ -294,12 +264,11 @@ static inline void wait_on_irq(int cpu) __sti(); SYNC_OTHER_CORES(cpu); __cli(); - check_smp_invalidate(cpu); if (atomic_read(&global_irq_count)) continue; if (global_irq_lock) continue; - if (!local_bh_count[cpu] && atomic_read(&global_bh_count)) + if (!local_bh_count[cpu] && spin_is_locked(&global_bh_lock)) continue; if (!test_and_set_bit(0,&global_irq_lock)) break; @@ -309,20 +278,6 @@ static inline void wait_on_irq(int cpu) /* * This is called when we want to synchronize with - * bottom half handlers. We need to wait until - * no other CPU is executing any bottom half handler. - * - * Don't wait if we're already running in an interrupt - * context or are inside a bh handler. - */ -void synchronize_bh(void) -{ - if (atomic_read(&global_bh_count) && !in_interrupt()) - wait_on_bh(); -} - -/* - * This is called when we want to synchronize with * interrupts. We may for example tell a device to * stop sending interrupts: but to make sure there * are no interrupts that are executing on another @@ -346,7 +301,6 @@ static inline void get_irqlock(int cpu) /* Uhhuh.. Somebody else got it. Wait.. */ do { do { - check_smp_invalidate(cpu); } while (test_bit(0,&global_irq_lock)); } while (test_and_set_bit(0,&global_irq_lock)); } @@ -621,16 +575,8 @@ asmlinkage unsigned int do_IRQ(struct pt_regs regs) desc->handler->end(irq); spin_unlock(&irq_controller_lock); - /* - * This should be conditional: we should really get - * a return code from the irq handler to tell us - * whether the handler wants us to do software bottom - * half handling or not.. - */ - if (1) { - if (bh_active & bh_mask) - do_bottom_half(); - } + if (softirq_state[cpu].active&softirq_state[cpu].mask) + do_softirq(); return 1; } diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 0061bc14d..5caa4e477 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -1469,19 +1469,10 @@ static int mtrr_close (struct inode *ino, struct file *file) static struct file_operations mtrr_fops = { - NULL, /* Seek */ - mtrr_read, /* Read */ - mtrr_write, /* Write */ - NULL, /* Readdir */ - NULL, /* Poll */ - mtrr_ioctl, /* IOctl */ - NULL, /* MMAP */ - NULL, /* Open */ - NULL, /* Flush */ - mtrr_close, /* Release */ - NULL, /* Fsync */ - NULL, /* Fasync */ - NULL, /* Lock */ + read: mtrr_read, + write: mtrr_write, + ioctl: mtrr_ioctl, + release: mtrr_close, }; static struct inode_operations proc_mtrr_inode_operations = { diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c index 601ffd3bf..590e01fd5 100644 --- a/arch/i386/kernel/pci-pc.c +++ b/arch/i386/kernel/pci-pc.c @@ -939,12 +939,26 @@ static void __init pci_fixup_ide_bases(struct pci_dev *d) } } +static void __init pci_fixup_ide_trash(struct pci_dev *d) +{ + int i; + + /* + * There exist PCI IDE controllers which have utter garbage + * in first four base registers. Ignore that. + */ + DBG("PCI: IDE base address trash cleared for %s\n", d->slot_name); + for(i=0; i<4; i++) + d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0; +} + struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_HE, pci_fixup_rcc }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_LE, pci_fixup_rcc }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_6010, pci_fixup_compaq }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash }, { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, { 0 } }; @@ -1132,6 +1146,10 @@ static void __init pcibios_fixup_irqs(void) if (pin) { pin--; /* interrupt pins are numbered starting from 1 */ irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); +/* + * Will be removed completely if things work out well with fuzzy parsing + */ +#if 0 if (irq < 0 && dev->bus->parent) { /* go back to the bridge */ struct pci_dev * bridge = dev->bus->self; @@ -1142,6 +1160,7 @@ static void __init pcibios_fixup_irqs(void) printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq); } +#endif if (irq >= 0) { printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); diff --git a/arch/i386/kernel/pm.c b/arch/i386/kernel/pm.c deleted file mode 100644 index 35ec0f489..000000000 --- a/arch/i386/kernel/pm.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * pm.c - Power management interface - * - * Copyright (C) 2000 Andrew Henroid - * - * 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 <linux/module.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <linux/pm.h> - -static spinlock_t pm_devs_lock = SPIN_LOCK_UNLOCKED; -static LIST_HEAD(pm_devs); - -/* - * Register a device with power management - */ -struct pm_dev *pm_register(pm_dev_t type, - unsigned long id, - pm_callback callback) -{ - struct pm_dev *dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); - if (dev) { - unsigned long flags; - - memset(dev, 0, sizeof(*dev)); - dev->type = type; - dev->id = id; - dev->callback = callback; - - spin_lock_irqsave(&pm_devs_lock, flags); - list_add(&dev->entry, &pm_devs); - spin_unlock_irqrestore(&pm_devs_lock, flags); - } - return dev; -} - -/* - * Unregister a device with power management - */ -void pm_unregister(struct pm_dev *dev) -{ - if (dev) { - unsigned long flags; - - spin_lock_irqsave(&pm_devs_lock, flags); - list_del(&dev->entry); - spin_unlock_irqrestore(&pm_devs_lock, flags); - - kfree(dev); - } -} - -/* - * Send a request to all devices - */ -int pm_send_request(pm_request_t rqst, void *data) -{ - struct list_head *entry = pm_devs.next; - while (entry != &pm_devs) { - struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); - if (dev->callback) { - int status = (*dev->callback)(dev, rqst, data); - if (status) - return status; - } - entry = entry->next; - } - return 0; -} - -/* - * Find a device - */ -struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from) -{ - struct list_head *entry = from ? from->entry.next:pm_devs.next; - while (entry != &pm_devs) { - struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); - if (type == PM_UNKNOWN_DEV || dev->type == type) - return dev; - entry = entry->next; - } - return 0; -} - -EXPORT_SYMBOL(pm_register); -EXPORT_SYMBOL(pm_unregister); -EXPORT_SYMBOL(pm_send_request); -EXPORT_SYMBOL(pm_find); diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 703482425..c38e383e7 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -61,12 +61,12 @@ void enable_hlt(void) /* * Powermanagement idle function, if any.. */ -void (*acpi_idle)(void) = NULL; +void (*pm_idle)(void) = NULL; /* * Power off function, if any */ -void (*acpi_power_off)(void) = NULL; +void (*pm_power_off)(void) = NULL; /* * We use this if we don't have any better @@ -92,7 +92,7 @@ void cpu_idle(void) current->counter = -100; while (1) { - void (*idle)(void) = acpi_idle; + void (*idle)(void) = pm_idle; if (!idle) idle = default_idle; while (!current->need_resched) @@ -328,8 +328,8 @@ void machine_halt(void) void machine_power_off(void) { - if (acpi_power_off) - acpi_power_off(); + if (pm_power_off) + pm_power_off(); } diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index d308a1280..e72f95160 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -75,7 +75,7 @@ #include <asm/e820.h> #include <asm/dma.h> #include <asm/mpspec.h> - +#include <asm/mmu_context.h> /* * Machine setup.. */ @@ -1543,6 +1543,10 @@ void cpu_init (void) */ atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; + if(current->mm) + BUG(); + enter_lazy_tlb(&init_mm, current, nr); + t->esp0 = current->thread.esp0; set_tss_desc(nr,t); gdt_table[__TSS(nr)].b &= 0xfffffdff; diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 05e0d1d23..0b585513f 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -103,8 +103,7 @@ /* The 'big kernel lock' */ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; -volatile unsigned long smp_invalidate_needed; /* immediate flush required */ -unsigned int cpu_tlbbad[NR_CPUS]; /* flush before returning to user space */ +struct tlb_state cpu_tlbstate[NR_CPUS]; /* * the following functions deal with sending IPIs between CPUs. @@ -186,15 +185,15 @@ static inline int __prepare_ICR (unsigned int shortcut, int vector) return cfg; } -static inline int __prepare_ICR2 (unsigned int dest) +static inline int __prepare_ICR2 (unsigned int mask) { unsigned int cfg; cfg = __get_ICR2(); #if LOGICAL_DELIVERY - cfg |= SET_APIC_DEST_FIELD((1<<dest)); + cfg |= SET_APIC_DEST_FIELD(mask); #else - cfg |= SET_APIC_DEST_FIELD(dest); + cfg |= SET_APIC_DEST_FIELD(mask); #endif return cfg; @@ -250,7 +249,7 @@ void send_IPI_self(int vector) __send_IPI_shortcut(APIC_DEST_SELF, vector); } -static inline void send_IPI_single(int dest, int vector) +static inline void send_IPI_mask(int mask, int vector) { unsigned long cfg; #if FORCE_READ_AROUND_WRITE @@ -264,7 +263,7 @@ static inline void send_IPI_single(int dest, int vector) * prepare target chip field */ - cfg = __prepare_ICR2(dest); + cfg = __prepare_ICR2(mask); apic_write(APIC_ICR2, cfg); /* @@ -282,112 +281,173 @@ static inline void send_IPI_single(int dest, int vector) } /* - * This is fraught with deadlocks. Probably the situation is not that - * bad as in the early days of SMP, so we might ease some of the - * paranoia here. + * Smarter SMP flushing macros. + * c/o Linus Torvalds. + * + * These mean you can really definitely utterly forget about + * writing to user space from interrupts. (Its not allowed anyway). + * + * Optimizations Manfred Spraul <manfreds@colorfullife.com> */ -static void flush_tlb_others(unsigned int cpumask) + +static volatile unsigned long flush_cpumask; +static struct mm_struct * flush_mm; +static unsigned long flush_va; +static spinlock_t tlbstate_lock = SPIN_LOCK_UNLOCKED; +#define FLUSH_ALL 0xffffffff + +static void inline leave_mm (unsigned long cpu) { - int cpu = smp_processor_id(); - int stuck; - unsigned long flags; + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) + BUG(); + clear_bit(cpu, &cpu_tlbstate[cpu].active_mm->cpu_vm_mask); + cpu_tlbstate[cpu].state = TLBSTATE_OLD; +} + +/* + * + * The flush IPI assumes that a thread switch happens in this order: + * 1) set_bit(cpu, &new_mm->cpu_vm_mask); + * 2) update cpu_tlbstate + * [now the cpu can accept tlb flush request for the new mm] + * 3) change cr3 (if required, or flush local tlb,...) + * 4) clear_bit(cpu, &old_mm->cpu_vm_mask); + * 5) switch %%esp, ie current + * + * The interrupt must handle 2 special cases: + * - cr3 is changed before %%esp, ie. it cannot use current->{active_,}mm. + * - the cpu performs speculative tlb reads, i.e. even if the cpu only + * runs in kernel space, the cpu could load tlb entries for user space + * pages. + * + * The good news is that cpu_tlbstate is local to each cpu, no + * write/read ordering problems. + */ + +/* + * TLB flush IPI: + * + * 1) Flush the tlb entries if the cpu uses the mm that's being flushed. + * 2) Leave the mm if we are in the lazy tlb mode. + * We cannot call mmdrop() because we are in interrupt context, + * instead update cpu_tlbstate. + */ + +asmlinkage void smp_invalidate_interrupt (void) +{ + unsigned long cpu = smp_processor_id(); + + if (!test_bit(cpu, &flush_cpumask)) + BUG(); + if (flush_mm == cpu_tlbstate[cpu].active_mm) { + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) { + if (flush_va == FLUSH_ALL) + local_flush_tlb(); + else + __flush_tlb_one(flush_va); + } else + leave_mm(cpu); + } else { + extern void show_stack (void *); + printk("hm #1: %p, %p.\n", flush_mm, cpu_tlbstate[cpu].active_mm); + show_stack(NULL); + } + __flush_tlb(); + ack_APIC_irq(); + clear_bit(cpu, &flush_cpumask); +} +static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm, + unsigned long va) +{ /* - * it's important that we do not generate any APIC traffic - * until the AP CPUs have booted up! + * A couple of (to be removed) sanity checks: + * + * - we do not send IPIs to not-yet booted CPUs. + * - current CPU must not be in mask + * - mask must exist :) */ - cpumask &= cpu_online_map; - if (cpumask) { - atomic_set_mask(cpumask, &smp_invalidate_needed); - - /* - * Processors spinning on some lock with IRQs disabled - * will see this IRQ late. The smp_invalidate_needed - * map will ensure they don't do a spurious flush tlb - * or miss one. - */ + if (!cpumask) + BUG(); + if ((cpumask & cpu_online_map) != cpumask) + BUG(); + if (cpumask & (1 << smp_processor_id())) + BUG(); + if (!mm) + BUG(); + + /* + * i'm not happy about this global shared spinlock in the + * MM hot path, but we'll see how contended it is. + * Temporarily this turns IRQs off, so that lockups are + * detected by the NMI watchdog. + */ + spin_lock_irq(&tlbstate_lock); - __save_flags(flags); - __cli(); - - send_IPI_allbutself(INVALIDATE_TLB_VECTOR); - - /* - * Spin waiting for completion - */ - - stuck = 50000000; - while (smp_invalidate_needed) { - /* - * Take care of "crossing" invalidates - */ - if (test_bit(cpu, &smp_invalidate_needed)) - do_flush_tlb_local(); - - --stuck; - if (!stuck) { - printk("stuck on TLB IPI wait (CPU#%d)\n",cpu); - break; - } - } - __restore_flags(flags); - } -} + flush_mm = mm; + flush_va = va; + atomic_set_mask(cpumask, &flush_cpumask); + /* + * We have to send the IPI only to + * CPUs affected. + */ + send_IPI_mask(cpumask, INVALIDATE_TLB_VECTOR); -/* - * Smarter SMP flushing macros. - * c/o Linus Torvalds. - * - * These mean you can really definitely utterly forget about - * writing to user space from interrupts. (Its not allowed anyway). - */ + while (flush_cpumask) + /* nothing. lockup detection does not belong here */; + + flush_mm = NULL; + flush_va = 0; + spin_unlock_irq(&tlbstate_lock); +} + void flush_tlb_current_task(void) { - unsigned long vm_mask = 1 << smp_processor_id(); struct mm_struct *mm = current->mm; - unsigned long cpu_mask = mm->cpu_vm_mask & ~vm_mask; + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); - mm->cpu_vm_mask = vm_mask; - flush_tlb_others(cpu_mask); local_flush_tlb(); + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, FLUSH_ALL); } -void flush_tlb_mm(struct mm_struct * mm) +void flush_tlb_mm (struct mm_struct * mm) { - unsigned long vm_mask = 1 << smp_processor_id(); - unsigned long cpu_mask = mm->cpu_vm_mask & ~vm_mask; + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); - mm->cpu_vm_mask = 0; if (current->active_mm == mm) { - mm->cpu_vm_mask = vm_mask; - local_flush_tlb(); + if (current->mm) + local_flush_tlb(); + else + leave_mm(smp_processor_id()); } - flush_tlb_others(cpu_mask); + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, FLUSH_ALL); } void flush_tlb_page(struct vm_area_struct * vma, unsigned long va) { - unsigned long vm_mask = 1 << smp_processor_id(); struct mm_struct *mm = vma->vm_mm; - unsigned long cpu_mask = mm->cpu_vm_mask & ~vm_mask; + unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id()); - mm->cpu_vm_mask = 0; if (current->active_mm == mm) { - __flush_tlb_one(va); - mm->cpu_vm_mask = vm_mask; + if(current->mm) + __flush_tlb_one(va); + else + leave_mm(smp_processor_id()); } - flush_tlb_others(cpu_mask); + + if (cpu_mask) + flush_tlb_others(cpu_mask, mm, va); } static inline void do_flush_tlb_all_local(void) { - __flush_tlb_all(); - if (!current->mm && current->active_mm) { - unsigned long cpu = smp_processor_id(); + unsigned long cpu = smp_processor_id(); - clear_bit(cpu, ¤t->active_mm->cpu_vm_mask); - cpu_tlbbad[cpu] = 1; - } + __flush_tlb_all(); + if (cpu_tlbstate[cpu].state == TLBSTATE_LAZY) + leave_mm(cpu); } static void flush_tlb_all_ipi(void* info) @@ -410,7 +470,7 @@ void flush_tlb_all(void) void smp_send_reschedule(int cpu) { - send_IPI_single(cpu, RESCHEDULE_VECTOR); + send_IPI_mask(1 << cpu, RESCHEDULE_VECTOR); } /* @@ -514,23 +574,6 @@ asmlinkage void smp_reschedule_interrupt(void) ack_APIC_irq(); } -/* - * Invalidate call-back. - * - * Mark the CPU as a VM user if there is a active - * thread holding on to an mm at this time. This - * allows us to optimize CPU cross-calls even in the - * presense of lazy TLB handling. - */ -asmlinkage void smp_invalidate_interrupt(void) -{ - if (test_bit(smp_processor_id(), &smp_invalidate_needed)) - do_flush_tlb_local(); - - ack_APIC_irq(); - -} - asmlinkage void smp_call_function_interrupt(void) { void (*func) (void *info) = call_data->func; diff --git a/arch/i386/kernel/trampoline.S b/arch/i386/kernel/trampoline.S index 12c1dbe34..52e00d9be 100644 --- a/arch/i386/kernel/trampoline.S +++ b/arch/i386/kernel/trampoline.S @@ -55,7 +55,7 @@ r_base = . jmp flush_instr flush_instr: ljmpl $__KERNEL_CS, $0x00100000 - # jump to startup_32 + # jump to startup_32 in arch/i386/kernel/head.S idt_48: .word 0 # idt limit = 0 diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 17cac5019..07797e760 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -124,19 +124,63 @@ int kstack_depth_to_print = 24; /* * These constants are for searching for possible module text - * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is - * a guess of how much space is likely to be vmalloced. + * segments. MODULE_RANGE is a guess of how much space is likely + * to be vmalloced. */ -#define VMALLOC_OFFSET (8*1024*1024) #define MODULE_RANGE (8*1024*1024) +void show_stack(unsigned long * esp) +{ + unsigned long *stack, addr, module_start, module_end; + int i; + + // debugging aid: "show_stack(NULL);" prints the + // back trace for this cpu. + + if(esp==NULL) + esp=(unsigned long*)&esp; + + stack = esp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", *stack++); + } + + printk("\nCall Trace: "); + stack = esp; + i = 1; + module_start = VMALLOC_START; + module_end = VMALLOC_END; + while (((long) stack & (THREAD_SIZE-1)) != 0) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long) &_stext) && + (addr <= (unsigned long) &_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } +} + static void show_registers(struct pt_regs *regs) { int i; int in_kernel = 1; unsigned long esp; unsigned short ss; - unsigned long *stack, addr, module_start, module_end; esp = (unsigned long) (®s->esp); ss = __KERNEL_DS; @@ -160,43 +204,24 @@ static void show_registers(struct pt_regs *regs) * time of the fault.. */ if (in_kernel) { + printk("\nStack: "); - stack = (unsigned long *) esp; - for(i=0; i < kstack_depth_to_print; i++) { - if (((long) stack & 4095) == 0) + show_stack((unsigned long*)esp); + + printk("\nCode: "); + if(regs->eip < PAGE_OFFSET) + goto bad; + + for(i=0;i<20;i++) + { + unsigned char c; + if(__get_user(c, &((unsigned char*)regs->eip)[i])) { +bad: + printk(" Bad EIP value."); break; - if (i && ((i % 8) == 0)) - printk("\n "); - printk("%08lx ", *stack++); - } - printk("\nCall Trace: "); - stack = (unsigned long *) esp; - i = 1; - module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT); - module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); - module_end = module_start + MODULE_RANGE; - while (((long) stack & 4095) != 0) { - addr = *stack++; - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ - if (((addr >= (unsigned long) &_stext) && - (addr <= (unsigned long) &_etext)) || - ((addr >= module_start) && (addr <= module_end))) { - if (i && ((i % 8) == 0)) - printk("\n "); - printk("[<%08lx>] ", addr); - i++; } + printk("%02x ", c); } - printk("\nCode: "); - for(i=0;i<20;i++) - printk("%02x ", ((unsigned char *)regs->eip)[i]); } printk("\n"); } diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 70520b99d..4ef25674f 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -40,8 +40,6 @@ unsigned long highstart_pfn, highend_pfn; static unsigned long totalram_pages = 0; static unsigned long totalhigh_pages = 0; -extern void show_net_buffers(void); - /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a @@ -228,9 +226,6 @@ void show_mem(void) printk("%d pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif } /* References to section boundaries */ @@ -448,7 +443,7 @@ void __init paging_init(void) kmap_init(); #endif { - unsigned int zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; unsigned int max_dma, high, low; max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile new file mode 100644 index 000000000..7dd3caabc --- /dev/null +++ b/arch/ia64/Makefile @@ -0,0 +1,125 @@ +# +# ia64/Makefile +# +# 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) 1998, 1999 by David Mosberger-Tang <davidm@hpl.hp.com> +# + +NM := $(CROSS_COMPILE)nm -B + +LINKFLAGS = -static -T arch/$(ARCH)/vmlinux.lds +# next line is for HP compiler backend: +#AFLAGS += -DGCC_RETVAL_POINTER_IN_R8 +# The next line is needed when compiling with the July snapshot of the Cygnus compiler: +#EXTRA = -ma0-bugs -D__GCC_DOESNT_KNOW_IN_REGS__ +# next two lines are for the September snapshot of the Cygnus compiler: +AFLAGS += -D__GCC_MULTIREG_RETVALS__ +EXTRA = -ma0-bugs -D__GCC_MULTIREG_RETVALS__ + +CFLAGS := -g $(CFLAGS) -pipe $(EXTRA) -ffixed-r13 -mfixed-range=f10-f15,f32-f127 + +ifdef CONFIG_IA64_GENERIC + CORE_FILES := arch/$(ARCH)/hp/hp.a \ + arch/$(ARCH)/sn/sn.a \ + arch/$(ARCH)/dig/dig.a \ + $(CORE_FILES) + SUBDIRS := arch/$(ARCH)/hp \ + arch/$(ARCH)/sn/sn1 \ + arch/$(ARCH)/sn \ + arch/$(ARCH)/dig \ + $(SUBDIRS) + +else # !GENERIC + +ifeq ($(CONFIG_IA64_HP_SIM),y) + SUBDIRS := arch/$(ARCH)/hp \ + $(SUBDIRS) + CORE_FILES := arch/$(ARCH)/hp/hp.a \ + $(CORE_FILES) +endif + +ifeq ($(CONFIG_IA64_SGI_SN1_SIM),y) + SUBDIRS := arch/$(ARCH)/sn/sn1 \ + arch/$(ARCH)/sn \ + $(SUBDIRS) + CORE_FILES := arch/$(ARCH)/sn/sn.a \ + $(CORE_FILES) +endif + +ifeq ($(CONFIG_IA64_SOFTSDV),y) + SUBDIRS := arch/$(ARCH)/dig \ + $(SUBDIRS) + CORE_FILES := arch/$(ARCH)/dig/dig.a \ + $(CORE_FILES) +endif + +ifeq ($(CONFIG_IA64_DIG),y) + SUBDIRS := arch/$(ARCH)/dig \ + $(SUBDIRS) + CORE_FILES := arch/$(ARCH)/dig/dig.a \ + $(CORE_FILES) +endif + +endif # !GENERIC + +ifeq ($(CONFIG_IA32_SUPPORT),y) + SUBDIRS := arch/$(ARCH)/ia32 $(SUBDIRS) + CORE_FILES := arch/$(ARCH)/ia32/ia32.o $(CORE_FILES) +endif + +ifdef CONFIG_KDB + LIBS := $(LIBS) $(TOPDIR)/arch/$(ARCH)/kdb/kdb.a + SUBDIRS := $(SUBDIRS) arch/$(ARCH)/kdb +endif + +HEAD := arch/$(ARCH)/kernel/head.o arch/ia64/kernel/init_task.o + +SUBDIRS := arch/$(ARCH)/tools arch/$(ARCH)/kernel arch/$(ARCH)/mm arch/$(ARCH)/lib $(SUBDIRS) +CORE_FILES := arch/$(ARCH)/kernel/kernel.o arch/$(ARCH)/mm/mm.o $(CORE_FILES) + +LIBS := $(TOPDIR)/arch/$(ARCH)/lib/lib.a $(LIBS) \ + $(TOPDIR)/arch/$(ARCH)/lib/lib.a + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +vmlinux: arch/$(ARCH)/vmlinux.lds + +arch/$(ARCH)/vmlinux.lds: arch/$(ARCH)/vmlinux.lds.S FORCE + gcc -D__ASSEMBLY__ -E -C -P -I$(HPATH) -I$(HPATH)/asm-$(ARCH) \ + arch/$(ARCH)/vmlinux.lds.S > $@ + +FORCE: ; + +rawboot: + @$(MAKEBOOT) rawboot + +# +# My boot writes directly to a specific disk partition, I doubt most +# people will want to do that without changes.. +# +msb my-special-boot: + @$(MAKEBOOT) msb + +bootimage: + @$(MAKEBOOT) bootimage + +srmboot: + @$(MAKEBOOT) srmboot + +archclean: + @$(MAKE) -C arch/$(ARCH)/kernel clean + @$(MAKE) -C arch/$(ARCH)/tools clean + @$(MAKEBOOT) clean + +archmrproper: + rm -f arch/$(ARCH)/vmlinux.lds + @$(MAKE) -C arch/$(ARCH)/tools mrproper + +archdep: + @$(MAKEBOOT) dep + +bootpfile: + @$(MAKEBOOT) bootpfile diff --git a/arch/ia64/boot/Makefile b/arch/ia64/boot/Makefile new file mode 100644 index 000000000..cba4fad66 --- /dev/null +++ b/arch/ia64/boot/Makefile @@ -0,0 +1,33 @@ +# +# ia64/boot/Makefile +# +# 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) 1998 by David Mosberger-Tang <davidm@hpl.hp.com> +# + +LINKFLAGS = -static -T bootloader.lds + +.S.s: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.o $< +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< + +OBJECTS = bootloader.o +TARGETS = + +ifdef CONFIG_IA64_HP_SIM + TARGETS += bootloader +endif + +all: $(TARGETS) + +bootloader: $(OBJECTS) + $(LD) $(LINKFLAGS) $(OBJECTS) $(LIBS) -o bootloader + +clean: + rm -f $(TARGETS) + +dep: diff --git a/arch/ia64/boot/bootloader.c b/arch/ia64/boot/bootloader.c new file mode 100644 index 000000000..cb6fc1f96 --- /dev/null +++ b/arch/ia64/boot/bootloader.c @@ -0,0 +1,234 @@ +/* + * arch/ia64/boot/bootloader.c + * + * Loads an ELF kernel. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1998, 1999 Stephane Eranian <eranian@hpl.hp.com> + * + * 01/07/99 S.Eranian modified to pass command line arguments to kernel + */ +#include <linux/config.h> +#include <linux/elf.h> +#include <linux/init.h> +#include <linux/kernel.h> + +#include <asm/elf.h> +#include <asm/pal.h> +#include <asm/pgtable.h> +#include <asm/sal.h> +#include <asm/system.h> + +/* Simulator system calls: */ + +#define SSC_CONSOLE_INIT 20 +#define SSC_GETCHAR 21 +#define SSC_PUTCHAR 31 +#define SSC_OPEN 50 +#define SSC_CLOSE 51 +#define SSC_READ 52 +#define SSC_WRITE 53 +#define SSC_GET_COMPLETION 54 +#define SSC_WAIT_COMPLETION 55 +#define SSC_CONNECT_INTERRUPT 58 +#define SSC_GENERATE_INTERRUPT 59 +#define SSC_SET_PERIODIC_INTERRUPT 60 +#define SSC_GET_RTC 65 +#define SSC_EXIT 66 +#define SSC_LOAD_SYMBOLS 69 +#define SSC_GET_TOD 74 + +#define SSC_GET_ARGS 75 + +struct disk_req { + unsigned long addr; + unsigned len; +}; + +struct disk_stat { + int fd; + unsigned count; +}; + +#include "../kernel/fw-emu.c" + +static void +cons_write (const char *buf) +{ + unsigned long ch; + + while ((ch = *buf++) != '\0') { + ssc(ch, 0, 0, 0, SSC_PUTCHAR); + if (ch == '\n') + ssc('\r', 0, 0, 0, SSC_PUTCHAR); + } +} + +void +enter_virtual_mode (unsigned long new_psr) +{ + asm volatile ("mov cr.ipsr=%0" :: "r"(new_psr)); + asm volatile ("mov cr.iip=%0" :: "r"(&&target)); + asm volatile ("mov cr.ifs=r0"); + asm volatile ("rfi;;"); /* must be last insn in an insn group */ + + target: +} + + +#define MAX_ARGS 32 + +void +_start (void) +{ + register long sp asm ("sp"); + static char stack[16384] __attribute__ ((aligned (16))); + static char mem[4096]; + static char buffer[1024]; + unsigned long flags, off; + int fd, i; + struct disk_req req; + struct disk_stat stat; + struct elfhdr *elf; + struct elf_phdr *elf_phdr; /* program header */ + unsigned long e_entry, e_phoff, e_phnum; + char *kpath, *args; + long arglen = 0; + + asm volatile ("movl gp=__gp" ::: "memory"); + asm volatile ("mov sp=%0" :: "r"(stack) : "memory"); + asm volatile ("bsw.1;;"); +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC + asm volative ("nop 0;; nop 0;; nop 0;;"); +#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ + + ssc(0, 0, 0, 0, SSC_CONSOLE_INIT); + + /* + * S.Eranian: extract the commandline argument from the + * simulator + * + * The expected format is as follows: + * + * kernelname args... + * + * Both are optional but you can't have the second one without the + * first. + */ + arglen = ssc((long) buffer, 0, 0, 0, SSC_GET_ARGS); + + kpath = "vmlinux"; + args = buffer; + if (arglen > 0) { + kpath = buffer; + while (*args != ' ' && *args != '\0') + ++args, --arglen; + if (*args == ' ') + *args++ = '\0', --arglen; + } + + if (arglen <= 0) { + args = ""; + arglen = 1; + } + + fd = ssc((long) kpath, 1, 0, 0, SSC_OPEN); + + if (fd < 0) { + cons_write(kpath); + cons_write(": file not found, reboot now\n"); + for(;;); + } + stat.fd = fd; + off = 0; + + req.len = sizeof(mem); + req.addr = (long) mem; + ssc(fd, 1, (long) &req, off, SSC_READ); + ssc((long) &stat, 0, 0, 0, SSC_WAIT_COMPLETION); + + elf = (struct elfhdr *) mem; + if (elf->e_ident[0] == 0x7f && strncmp(elf->e_ident + 1, "ELF", 3) != 0) { + cons_write("not an ELF file\n"); + return; + } + if (elf->e_type != ET_EXEC) { + cons_write("not an ELF executable\n"); + return; + } + if (!elf_check_arch(elf->e_machine)) { + cons_write("kernel not for this processor\n"); + return; + } + + e_entry = elf->e_entry; + e_phnum = elf->e_phnum; + e_phoff = elf->e_phoff; + + cons_write("loading "); + cons_write(kpath); + cons_write("...\n"); + + for (i = 0; i < e_phnum; ++i) { + req.len = sizeof(*elf_phdr); + req.addr = (long) mem; + ssc(fd, 1, (long) &req, e_phoff, SSC_READ); + ssc((long) &stat, 0, 0, 0, SSC_WAIT_COMPLETION); + if (stat.count != sizeof(*elf_phdr)) { + cons_write("failed to read phdr\n"); + return; + } + e_phoff += sizeof(*elf_phdr); + + elf_phdr = (struct elf_phdr *) mem; + req.len = elf_phdr->p_filesz; + req.addr = __pa(elf_phdr->p_vaddr); + ssc(fd, 1, (long) &req, elf_phdr->p_offset, SSC_READ); + ssc((long) &stat, 0, 0, 0, SSC_WAIT_COMPLETION); + memset((char *)__pa(elf_phdr->p_vaddr) + elf_phdr->p_filesz, 0, + elf_phdr->p_memsz - elf_phdr->p_filesz); + } + ssc(fd, 0, 0, 0, SSC_CLOSE); + + cons_write("starting kernel...\n"); + + /* fake an I/O base address: */ + asm volatile ("mov ar.k0=%0" :: "r"(0xffffc000000UL)); + + /* + * Install a translation register that identity maps the + * kernel's 256MB page. + */ + ia64_clear_ic(flags); + ia64_set_rr( 0, (0x1000 << 8) | (_PAGE_SIZE_1M << 2)); + ia64_set_rr(PAGE_OFFSET, (ia64_rid(0, PAGE_OFFSET) << 8) | (_PAGE_SIZE_256M << 2)); + ia64_srlz_d(); + ia64_itr(0x3, 0, 1024*1024, + pte_val(mk_pte_phys(1024*1024, __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RWX))), + _PAGE_SIZE_1M); + ia64_itr(0x3, 1, PAGE_OFFSET, + pte_val(mk_pte_phys(0, __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RWX))), + _PAGE_SIZE_256M); + ia64_srlz_i(); + + enter_virtual_mode(flags | IA64_PSR_IT | IA64_PSR_IC | IA64_PSR_DT | IA64_PSR_RT + | IA64_PSR_DFH | IA64_PSR_BN); + + sys_fw_init(args, arglen); + + ssc(0, (long) kpath, 0, 0, SSC_LOAD_SYMBOLS); + + /* + * Install the kernel's command line argument on ZERO_PAGE + * just after the botoparam structure. + * In case we don't have any argument just put \0 + */ + memcpy(((struct ia64_boot_param *)ZERO_PAGE_ADDR) + 1, args, arglen); + sp = __pa(&stack); + + asm volatile ("br.sptk.few %0" :: "b"(e_entry)); + + cons_write("kernel returned!\n"); + ssc(-1, 0, 0, 0, SSC_EXIT); +} diff --git a/arch/ia64/boot/bootloader.lds b/arch/ia64/boot/bootloader.lds new file mode 100644 index 000000000..a73518406 --- /dev/null +++ b/arch/ia64/boot/bootloader.lds @@ -0,0 +1,65 @@ +OUTPUT_FORMAT("elf64-ia64-little") +OUTPUT_ARCH(ia64) +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x100000; + + _text = .; + .text : { *(__ivt_section) *(.text) } + _etext = .; + + /* Global data */ + _data = .; + .rodata : { *(.rodata) } + .data : { *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS } + __gp = ALIGN (8) + 0x200000; + .got : { *(.got.plt) *(.got) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + + _bss = .; + .sbss : { *(.sbss) *(.scommon) } + .bss : { *(.bss) *(COMMON) } + . = ALIGN(64 / 8); + _end = . ; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} diff --git a/arch/ia64/config.in b/arch/ia64/config.in new file mode 100644 index 000000000..d006c1d05 --- /dev/null +++ b/arch/ia64/config.in @@ -0,0 +1,172 @@ +mainmenu_name "Kernel configuration of Linux for IA-64 machines" + +mainmenu_option next_comment +comment 'General setup' + +choice 'IA-64 system type' \ + "Generic CONFIG_IA64_GENERIC \ + HP-simulator CONFIG_IA64_HP_SIM \ + SN1-simulator CONFIG_IA64_SGI_SN1_SIM \ + DIG-compliant CONFIG_IA64_DIG" Generic + +choice 'Kernel page size' \ + "4KB CONFIG_IA64_PAGE_SIZE_4KB \ + 8KB CONFIG_IA64_PAGE_SIZE_8KB \ + 16KB CONFIG_IA64_PAGE_SIZE_16KB \ + 64KB CONFIG_IA64_PAGE_SIZE_64KB" 16KB + +if [ "$CONFIG_IA64_DIG" = "y" ]; then + bool ' Enable Itanium A-step specific code' CONFIG_ITANIUM_ASTEP_SPECIFIC + bool ' Enable SoftSDV hacks' CONFIG_IA64_SOFTSDV_HACKS n + bool ' Enable BigSur hacks' CONFIG_IA64_BIGSUR_HACKS y + bool ' Enable Lion hacks' CONFIG_IA64_LION_HACKS n + bool ' Emulate PAL/SAL/EFI firmware' CONFIG_IA64_FW_EMU n + bool ' Get PCI IRQ routing from firmware/ACPI' CONFIG_IA64_IRQ_ACPI y +fi + +if [ "$CONFIG_IA64_GENERIC" = "y" ]; then + define_bool CONFIG_IA64_SOFTSDV_HACKS y +fi + +if [ "$CONFIG_IA64_SGI_SN1_SIM" = "y" ]; then + define_bool CONFIG_NUMA y + define_bool CONFIG_IA64_SOFTSDV_HACKS y +fi + +define_bool CONFIG_KCORE_ELF y # On IA-64, we always want an ELF /dev/kcore. + +bool 'SMP support' CONFIG_SMP n +bool 'Performance monitor support' CONFIG_PERFMON n + +bool 'Networking support' CONFIG_NET n +bool 'System V IPC' CONFIG_SYSVIPC n +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT n +bool 'Sysctl support' CONFIG_SYSCTL n +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC + +bool 'PCI support' CONFIG_PCI n +source drivers/pci/Config.in + +source drivers/pcmcia/Config.in + +mainmenu_option next_comment + comment 'Code maturity level options' + bool 'Prompt for development and/or incomplete code/drivers' \ + CONFIG_EXPERIMENTAL n +endmenu + +mainmenu_option next_comment + comment 'Loadable module support' + bool 'Enable loadable module support' CONFIG_MODULES n + if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n + bool 'Kernel module loader' CONFIG_KMOD n + fi +endmenu + +source drivers/parport/Config.in + +endmenu + +source drivers/pnp/Config.in +source drivers/block/Config.in +source drivers/i2o/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +mainmenu_option next_comment +comment 'SCSI support' + +tristate 'SCSI support' CONFIG_SCSI + +if [ "$CONFIG_SCSI" != "n" ]; then + source drivers/scsi/Config.in + bool 'Simulated SCSI disk' CONFIG_SCSI_SIM n +fi +endmenu + +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' + + bool 'Network device support' CONFIG_NETDEVICES n + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in + fi + endmenu +fi + +source net/ax25/Config.in + +mainmenu_option next_comment +comment 'ISDN subsystem' + +tristate 'ISDN support' CONFIG_ISDN +if [ "$CONFIG_ISDN" != "n" ]; then + source drivers/isdn/Config.in +fi +endmenu + +mainmenu_option next_comment +comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' + +bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI n +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in +fi +endmenu + +source drivers/char/Config.in +source drivers/usb/Config.in +source drivers/misc/Config.in + +source fs/Config.in + +source fs/nls/Config.in + +if [ "$CONFIG_VT" = "y" ]; then + mainmenu_option next_comment + comment 'Console drivers' + bool 'VGA text console' CONFIG_VGA_CONSOLE n + if [ "$CONFIG_FB" = "y" ]; then + define_bool CONFIG_PCI_CONSOLE y + fi + source drivers/video/Config.in + endmenu +fi + +mainmenu_option next_comment +comment 'Sound' + +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_SOUND" != "n" ]; then + source drivers/sound/Config.in +fi +endmenu + +mainmenu_option next_comment +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for IA-32 emulation' CONFIG_IA32_SUPPORT + tristate 'Kernel FP software completion' CONFIG_MATHEMU +else + define_bool CONFIG_MATHEMU y +fi + +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ n +bool 'Early printk support (requires VGA!)' CONFIG_IA64_EARLY_PRINTK n +bool 'Turn on compare-and-exchange bug checking (slow!)' CONFIG_IA64_DEBUG_CMPXCHG n +bool 'Turn on irq debug checks (slow!)' CONFIG_IA64_DEBUG_IRQ n +bool 'Print possible IA64 hazards to console' CONFIG_IA64_PRINT_HAZARDS n +bool 'Built-in Kernel Debugger support' CONFIG_KDB +if [ "$CONFIG_KDB" = "y" ]; then + bool 'Compile the kernel with frame pointers' CONFIG_KDB_FRAMEPTR + int 'KDB Kernel Symbol Table size?' CONFIG_KDB_STBSIZE 10000 +fi + +endmenu diff --git a/arch/ia64/defconfig b/arch/ia64/defconfig new file mode 100644 index 000000000..a96599889 --- /dev/null +++ b/arch/ia64/defconfig @@ -0,0 +1,146 @@ +# +# Automatically generated make config: don't edit +# + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# General setup +# +CONFIG_IA64_SIM=y +CONFIG_PCI=y +# CONFIG_PCI_QUIRKS is not set +CONFIG_PCI_OLD_PROC=y +# CONFIG_NET is not set +# CONFIG_SYSVIPC is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_SYSCTL is not set +# CONFIG_BINFMT_ELF is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +# CONFIG_BINFMT_EM86 is not set +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_IDE is not set + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_ONLY is not set + +# +# Additional Block Devices +# +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set +# CONFIG_SCSI_G_NCR5380_PORT is not set +# CONFIG_SCSI_G_NCR5380_MEM is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +CONFIG_EFI_RTC=y +# CONFIG_VIDEO_DEV is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_FT_NORMAL_DEBUG is not set +# CONFIG_FT_FULL_DEBUG is not set +# CONFIG_FT_NO_TRACE is not set +# CONFIG_FT_NO_TRACE_AT_ALL is not set +# CONFIG_FT_STD_FDC is not set +# CONFIG_FT_MACH2 is not set +# CONFIG_FT_PROBE_FC10 is not set +# CONFIG_FT_ALT_FDC is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +# CONFIG_EXT2_FS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_PROC_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_SMD_DISKLABEL is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_ADFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Kernel hacking +# +# CONFIG_MATHEMU is not set +# CONFIG_MAGIC_SYSRQ is not set diff --git a/arch/ia64/dig/Makefile b/arch/ia64/dig/Makefile new file mode 100644 index 000000000..cfc48eec1 --- /dev/null +++ b/arch/ia64/dig/Makefile @@ -0,0 +1,24 @@ +# +# ia64/platform/dig/Makefile +# +# Copyright (C) 1999 Silicon Graphics, Inc. +# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) +# + +.S.s: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -E -o $*.s $< +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o $*.o $< + +all: dig.a + +O_TARGET = dig.a +O_OBJS = iosapic.o setup.o + +ifeq ($(CONFIG_IA64_GENERIC),y) +O_OBJS += machvec.o +endif + +clean:: + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/dig/iosapic.c b/arch/ia64/dig/iosapic.c new file mode 100644 index 000000000..6a392226e --- /dev/null +++ b/arch/ia64/dig/iosapic.c @@ -0,0 +1,553 @@ +/* + * Streamlined APIC support. + * + * Copyright (C) 1999 Intel Corp. + * Copyright (C) 1999 Asit Mallick <asit.k.mallick@intel.com> + * Copyright (C) 1999-2000 Hewlett-Packard Co. + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com> + */ +#include <linux/config.h> + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/string.h> + +#include <asm/io.h> +#include <asm/iosapic.h> +#include <asm/irq.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/delay.h> +#include <asm/processor.h> + +#undef DEBUG_IRQ_ROUTING + +/* + * IRQ vectors 0..15 are treated as the legacy interrupts of the PC-AT + * platform. No new drivers should ever ask for specific irqs, but we + * provide compatibility here in case there is an old driver that does + * ask for specific irqs (serial, keyboard, stuff like that). Since + * IA-64 doesn't allow irq 0..15 to be used for external interrupts + * anyhow, this in no way prevents us from doing the Right Thing + * with new drivers. + */ +struct iosapic_vector iosapic_vector[NR_IRQS] = { + [0 ... NR_IRQS-1] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } +}; + +#ifndef CONFIG_IA64_IRQ_ACPI +/* + * Defines the default interrupt routing information for the LION platform + * XXX - this information should be obtained from the ACPI and hardcoded since + * we do not have ACPI AML support. + */ + +struct intr_routing_entry intr_routing[] = { + {0,0,0,2,0,0,0,0}, + {0,0,1,1,0,0,0,0}, + {0,0,2,0xff,0,0,0,0}, + {0,0,3,3,0,0,0,0}, + {0,0,4,4,0,0,0,0}, + {0,0,5,5,0,0,0,0}, + {0,0,6,6,0,0,0,0}, + {0,0,7,7,0,0,0,0}, + {0,0,8,8,0,0,0,0}, + {0,0,9,9,0,0,0,0}, + {0,0,10,10,0,0,0,0}, + {0,0,11,11,0,0,0,0}, + {0,0,12,12,0,0,0,0}, + {0,0,13,13,0,0,0,0}, + {0,0,14,14,0,0,0,0}, + {0,0,15,15,0,0,0,0}, +#ifdef CONFIG_IA64_LION_HACKS + {1, 0, 0x04, 16, 0, 0, 1, 1}, /* bus 0, device id 1, INTA */ + {1, 0, 0x05, 26, 0, 0, 1, 1}, /* bus 0, device id 1, INTB */ + {1, 0, 0x06, 36, 0, 0, 1, 1}, /* bus 0, device id 1, INTC */ + {1, 0, 0x07, 42, 0, 0, 1, 1}, /* bus 0, device id 1, INTD */ + + {1, 0, 0x08, 17, 0, 0, 1, 1}, /* bus 0, device id 2, INTA */ + {1, 0, 0x09, 27, 0, 0, 1, 1}, /* bus 0, device id 2, INTB */ + {1, 0, 0x0a, 37, 0, 0, 1, 1}, /* bus 0, device id 2, INTC */ + {1, 0, 0x0b, 42, 0, 0, 1, 1}, /* bus 0, device id 2, INTD */ + + {1, 0, 0x0f, 50, 0, 0, 1, 1}, /* bus 0, device id 3, INTD */ + + {1, 0, 0x14, 51, 0, 0, 1, 1}, /* bus 0, device id 5, INTA */ + + {1, 0, 0x18, 49, 0, 0, 1, 1}, /* bus 0, device id 6, INTA */ + + {1, 1, 0x04, 18, 0, 0, 1, 1}, /* bus 1, device id 1, INTA */ + {1, 1, 0x05, 28, 0, 0, 1, 1}, /* bus 1, device id 1, INTB */ + {1, 1, 0x06, 38, 0, 0, 1, 1}, /* bus 1, device id 1, INTC */ + {1, 1, 0x07, 43, 0, 0, 1, 1}, /* bus 1, device id 1, INTD */ + + {1, 1, 0x08, 48, 0, 0, 1, 1}, /* bus 1, device id 2, INTA */ + + {1, 1, 0x0c, 19, 0, 0, 1, 1}, /* bus 1, device id 3, INTA */ + {1, 1, 0x0d, 29, 0, 0, 1, 1}, /* bus 1, device id 3, INTB */ + {1, 1, 0x0e, 38, 0, 0, 1, 1}, /* bus 1, device id 3, INTC */ + {1, 1, 0x0f, 44, 0, 0, 1, 1}, /* bus 1, device id 3, INTD */ + + {1, 1, 0x10, 20, 0, 0, 1, 1}, /* bus 1, device id 4, INTA */ + {1, 1, 0x11, 30, 0, 0, 1, 1}, /* bus 1, device id 4, INTB */ + {1, 1, 0x12, 39, 0, 0, 1, 1}, /* bus 1, device id 4, INTC */ + {1, 1, 0x13, 45, 0, 0, 1, 1}, /* bus 1, device id 4, INTD */ + + {1, 2, 0x04, 21, 0, 0, 1, 1}, /* bus 2, device id 1, INTA */ + {1, 2, 0x05, 31, 0, 0, 1, 1}, /* bus 2, device id 1, INTB */ + {1, 2, 0x06, 39, 0, 0, 1, 1}, /* bus 2, device id 1, INTC */ + {1, 2, 0x07, 45, 0, 0, 1, 1}, /* bus 2, device id 1, INTD */ + + {1, 2, 0x08, 22, 0, 0, 1, 1}, /* bus 2, device id 2, INTA */ + {1, 2, 0x09, 32, 0, 0, 1, 1}, /* bus 2, device id 2, INTB */ + {1, 2, 0x0a, 40, 0, 0, 1, 1}, /* bus 2, device id 2, INTC */ + {1, 2, 0x0b, 46, 0, 0, 1, 1}, /* bus 2, device id 2, INTD */ + + {1, 2, 0x0c, 23, 0, 0, 1, 1}, /* bus 2, device id 3, INTA */ + {1, 2, 0x0d, 33, 0, 0, 1, 1}, /* bus 2, device id 3, INTB */ + {1, 2, 0x0e, 40, 0, 0, 1, 1}, /* bus 2, device id 3, INTC */ + {1, 2, 0x0f, 46, 0, 0, 1, 1}, /* bus 2, device id 3, INTD */ + + {1, 3, 0x04, 24, 0, 0, 1, 1}, /* bus 3, device id 1, INTA */ + {1, 3, 0x05, 34, 0, 0, 1, 1}, /* bus 3, device id 1, INTB */ + {1, 3, 0x06, 41, 0, 0, 1, 1}, /* bus 3, device id 1, INTC */ + {1, 3, 0x07, 47, 0, 0, 1, 1}, /* bus 3, device id 1, INTD */ + + {1, 3, 0x08, 25, 0, 0, 1, 1}, /* bus 3, device id 2, INTA */ + {1, 3, 0x09, 35, 0, 0, 1, 1}, /* bus 3, device id 2, INTB */ + {1, 3, 0x0a, 41, 0, 0, 1, 1}, /* bus 3, device id 2, INTC */ + {1, 3, 0x0b, 47, 0, 0, 1, 1}, /* bus 3, device id 2, INTD */ +#else + /* + * BigSur platform, bus 0, device 1,2,4 and bus 1 device 0-3 + */ + {1,1,0x0,19,0,0,1,1}, /* bus 1, device id 0, INTA */ + {1,1,0x1,18,0,0,1,1}, /* bus 1, device id 0, INTB */ + {1,1,0x2,17,0,0,1,1}, /* bus 1, device id 0, INTC */ + {1,1,0x3,16,0,0,1,1}, /* bus 1, device id 0, INTD */ + + {1,1,0x4,23,0,0,1,1}, /* bus 1, device id 1, INTA */ + {1,1,0x5,22,0,0,1,1}, /* bus 1, device id 1, INTB */ + {1,1,0x6,21,0,0,1,1}, /* bus 1, device id 1, INTC */ + {1,1,0x7,20,0,0,1,1}, /* bus 1, device id 1, INTD */ + + {1,1,0x8,27,0,0,1,1}, /* bus 1, device id 2, INTA */ + {1,1,0x9,26,0,0,1,1}, /* bus 1, device id 2, INTB */ + {1,1,0xa,25,0,0,1,1}, /* bus 1, device id 2, INTC */ + {1,1,0xb,24,0,0,1,1}, /* bus 1, device id 2, INTD */ + + {1,1,0xc,31,0,0,1,1}, /* bus 1, device id 3, INTA */ + {1,1,0xd,30,0,0,1,1}, /* bus 1, device id 3, INTB */ + {1,1,0xe,29,0,0,1,1}, /* bus 1, device id 3, INTC */ + {1,1,0xf,28,0,0,1,1}, /* bus 1, device id 3, INTD */ + + {1,0,0x4,35,0,0,1,1}, /* bus 0, device id 1, INTA */ + {1,0,0x5,34,0,0,1,1}, /* bus 0, device id 1, INTB */ + {1,0,0x6,33,0,0,1,1}, /* bus 0, device id 1, INTC */ + {1,0,0x7,32,0,0,1,1}, /* bus 0, device id 1, INTD */ + + {1,0,0x8,39,0,0,1,1}, /* bus 0, device id 2, INTA */ + {1,0,0x9,38,0,0,1,1}, /* bus 0, device id 2, INTB */ + {1,0,0xa,37,0,0,1,1}, /* bus 0, device id 2, INTC */ + {1,0,0xb,36,0,0,1,1}, /* bus 0, device id 2, INTD */ + + {1,0,0x10,43,0,0,1,1}, /* bus 0, device id 4, INTA */ + {1,0,0x11,42,0,0,1,1}, /* bus 0, device id 4, INTB */ + {1,0,0x12,41,0,0,1,1}, /* bus 0, device id 4, INTC */ + {1,0,0x13,40,0,0,1,1}, /* bus 0, device id 4, INTD */ + + {1,0,0x14,17,0,0,1,1}, /* bus 0, device id 5, INTA */ + {1,0,0x18,18,0,0,1,1}, /* bus 0, device id 6, INTA */ + {1,0,0x1c,19,0,0,1,1}, /* bus 0, device id 7, INTA */ +#endif + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, +}; + +int +iosapic_get_PCI_irq_vector(int bus, int slot, int pci_pin) +{ + int i = -1; + + while (intr_routing[++i].srcbus != 0xff) { + if (intr_routing[i].srcbus == BUS_PCI) { + if ((intr_routing[i].srcbusirq == ((slot << 2) | pci_pin)) + && (intr_routing[i].srcbusno == bus)) { + return(intr_routing[i].iosapic_pin); + } + } + } + return -1; +} + +#else /* CONFIG_IA64_IRQ_ACPI */ + +/* + * find the IRQ in the IOSAPIC map for the PCI device on bus/slot/pin + */ +int +iosapic_get_PCI_irq_vector(int bus, int slot, int pci_pin) +{ + int i; + + for (i = 0; i < NR_IRQS; i++) { + if ((iosapic_bustype(i) == BUS_PCI) && + (iosapic_bus(i) == bus) && + (iosapic_busdata(i) == ((slot << 16) | pci_pin))) { + return i; + } + } + + return -1; +} +#endif /* !CONFIG_IA64_IRQ_ACPI */ + +static void +set_rte (unsigned long iosapic_addr, int entry, int pol, int trigger, int delivery, + long dest, int vector) +{ + int low32; + int high32; + + low32 = ((pol << IO_SAPIC_POLARITY_SHIFT) | + (trigger << IO_SAPIC_TRIGGER_SHIFT) | + (delivery << IO_SAPIC_DELIVERY_SHIFT) | + vector); + + /* dest contains both id and eid */ + high32 = (dest << IO_SAPIC_DEST_SHIFT); + + /* + * program the rte + */ + writel(IO_SAPIC_RTE_HIGH(entry), iosapic_addr + IO_SAPIC_REG_SELECT); + writel(high32, iosapic_addr + IO_SAPIC_WINDOW); + writel(IO_SAPIC_RTE_LOW(entry), iosapic_addr + IO_SAPIC_REG_SELECT); + writel(low32, iosapic_addr + IO_SAPIC_WINDOW); +} + + +static void +enable_pin (unsigned int pin, unsigned long iosapic_addr) +{ + int low32; + + writel(IO_SAPIC_RTE_LOW(pin), iosapic_addr + IO_SAPIC_REG_SELECT); + low32 = readl(iosapic_addr + IO_SAPIC_WINDOW); + + low32 &= ~(1 << IO_SAPIC_MASK_SHIFT); /* Zero only the mask bit */ + writel(low32, iosapic_addr + IO_SAPIC_WINDOW); +} + + +static void +disable_pin (unsigned int pin, unsigned long iosapic_addr) +{ + int low32; + + writel(IO_SAPIC_RTE_LOW(pin), iosapic_addr + IO_SAPIC_REG_SELECT); + low32 = readl(iosapic_addr + IO_SAPIC_WINDOW); + + low32 |= (1 << IO_SAPIC_MASK_SHIFT); /* Set only the mask bit */ + writel(low32, iosapic_addr + IO_SAPIC_WINDOW); +} + +#define iosapic_shutdown_irq iosapic_disable_irq + +static void +iosapic_enable_irq (unsigned int irq) +{ + int pin = iosapic_pin(irq); + + if (pin < 0) + /* happens during irq auto probing... */ + return; + enable_pin(pin, iosapic_addr(irq)); +} + +static void +iosapic_disable_irq (unsigned int irq) +{ + int pin = iosapic_pin(irq); + + if (pin < 0) + return; + disable_pin(pin, iosapic_addr(irq)); +} + +unsigned int +iosapic_version(unsigned long base_addr) +{ + /* + * IOSAPIC Version Register return 32 bit structure like: + * { + * unsigned int version : 8; + * unsigned int reserved1 : 8; + * unsigned int pins : 8; + * unsigned int reserved2 : 8; + * } + */ + writel(IO_SAPIC_VERSION, base_addr + IO_SAPIC_REG_SELECT); + return readl(IO_SAPIC_WINDOW + base_addr); +} + +static int +iosapic_handle_irq (unsigned int irq, struct pt_regs *regs) +{ + struct irqaction *action = 0; + struct irq_desc *id = irq_desc + irq; + unsigned int status; + int retval; + + spin_lock(&irq_controller_lock); + { + status = id->status; + + /* do we need to do something IOSAPIC-specific to ACK the irq here??? */ + /* Yes, but only level-triggered interrupts. We'll do that later */ + if ((status & IRQ_INPROGRESS) == 0 && (status & IRQ_ENABLED) != 0) { + action = id->action; + status |= IRQ_INPROGRESS; + } + id->status = status & ~(IRQ_REPLAY | IRQ_WAITING); + } + spin_unlock(&irq_controller_lock); + + if (!action) { + if (!(id->status & IRQ_AUTODETECT)) + printk("iosapic_handle_irq: unexpected interrupt %u;" + "disabling it (status=%x)\n", irq, id->status); + /* + * If we don't have a handler, disable the pin so we + * won't get any further interrupts (until + * re-enabled). --davidm 99/12/17 + */ + iosapic_disable_irq(irq); + return 0; + } + + retval = invoke_irq_handlers (irq, regs, action); + + if (iosapic_trigger(irq) == IO_SAPIC_LEVEL) /* ACK Level trigger interrupts */ + writel(irq, iosapic_addr(irq) + IO_SAPIC_EOI); + + spin_lock(&irq_controller_lock); + { + status = (id->status & ~IRQ_INPROGRESS); + id->status = status; + } + spin_unlock(&irq_controller_lock); + + return retval; +} + +void __init +iosapic_init (unsigned long addr) +{ + int i; +#ifdef CONFIG_IA64_IRQ_ACPI + struct pci_vector_struct *vectors; + int irq; +#else + int vector; +#endif + + /* + * Disable all local interrupts + */ + + ia64_set_itv(0, 1); + ia64_set_lrr0(0, 1); + ia64_set_lrr1(0, 1); + + /* + * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support + * enabled. + */ + + outb(0xff, 0xA1); + outb(0xff, 0x21); + +#if defined(CONFIG_IA64_SOFTSDV_HACKS) + memset(iosapic_vector, 0x0, sizeof(iosapic_vector)); + for (i = 0; i < NR_IRQS; i++) { + iosapic_pin(i) = 0xff; + iosapic_addr(i) = (unsigned long) ioremap(IO_SAPIC_DEFAULT_ADDR, 0); + } + /* XXX this should come from systab or some such: */ + iosapic_pin(TIMER_IRQ) = 5; /* System Clock Interrupt */ + iosapic_pin(0x40) = 3; /* Keyboard */ + iosapic_pin(0x92) = 9; /* COM1 Serial Port */ + iosapic_pin(0x80) = 4; /* Periodic Interrupt */ + iosapic_pin(0xc0) = 2; /* Mouse */ + iosapic_pin(0xe0) = 1; /* IDE Disk */ + iosapic_pin(0xf0) = 6; /* E-IDE CDROM */ + iosapic_pin(0xa0) = 10; /* Real PCI Interrupt */ +#elif !defined(CONFIG_IA64_IRQ_ACPI) + /* + * For systems where the routing info in ACPI is + * unavailable/wrong, use the intr_routing information to + * initialize the iosapic array + */ + i = -1; + while (intr_routing[++i].srcbus != 0xff) { + if (intr_routing[i].srcbus == BUS_ISA) { + vector = map_legacy_irq(intr_routing[i].srcbusirq); + } else if (intr_routing[i].srcbus == BUS_PCI) { + vector = intr_routing[i].iosapic_pin; + } else { + printk("unknown bus type %d for intr_routing[%d]\n", + intr_routing[i].srcbus, i); + continue; + } + iosapic_pin(vector) = intr_routing[i].iosapic_pin; + iosapic_dmode(vector) = intr_routing[i].mode; + iosapic_polarity(vector) = intr_routing[i].polarity; + iosapic_trigger(vector) = intr_routing[i].trigger; +# ifdef DEBUG_IRQ_ROUTING + printk("irq[0x%x(0x%x)]:0x%x, %d, %d, %d\n", vector, intr_routing[i].srcbusirq, + iosapic_pin(vector), iosapic_dmode(vector), iosapic_polarity(vector), + iosapic_trigger(vector)); +# endif + } +#else /* !defined(CONFIG_IA64_SOFTSDV_HACKS) && !defined(CONFIG_IA64_IRQ_ACPI) */ + /* + * Map the legacy ISA devices into the IOAPIC data; We'll override these + * later with data from the ACPI Interrupt Source Override table. + * + * Huh, the Lion w/ FPSWA firmware has entries for _all_ of the legacy IRQs, + * including those that are not different from PC/AT standard. I don't know + * if this is a bug in the other firmware or not. I'm going to leave this code + * here, so that this works on BigSur but will go ask Intel. --wfd 2000-Jan-19 + * + */ + for (i =0 ; i < IA64_MIN_VECTORED_IRQ; i++) { + irq = map_legacy_irq(i); + iosapic_pin(irq) = i; + iosapic_bus(irq) = BUS_ISA; + iosapic_busdata(irq) = 0; + iosapic_dmode(irq) = IO_SAPIC_LOWEST_PRIORITY; + iosapic_trigger(irq) = IO_SAPIC_EDGE; + iosapic_polarity(irq) = IO_SAPIC_POL_HIGH; +#ifdef DEBUG_IRQ_ROUTING + printk("ISA: IRQ %02x -> Vector %02x IOSAPIC Pin %d\n", i, irq, iosapic_pin(irq)); +#endif + } + + /* + * Map the PCI Interrupt data into the ACPI IOSAPIC data using + * the info that the bootstrap loader passed to us. + */ + ia64_boot_param.pci_vectors = (__u64) __va(ia64_boot_param.pci_vectors); + vectors = (struct pci_vector_struct *) ia64_boot_param.pci_vectors; + for (i = 0; i < ia64_boot_param.num_pci_vectors; i++) { + irq = map_legacy_irq(vectors[i].irq); + + iosapic_bustype(irq) = BUS_PCI; + iosapic_pin(irq) = irq - iosapic_baseirq(irq); + iosapic_bus(irq) = vectors[i].bus; + /* + * Map the PCI slot and pin data into iosapic_busdata() + */ + iosapic_busdata(irq) = (vectors[i].pci_id & 0xffff0000) | vectors[i].pin; + + /* Default settings for PCI */ + iosapic_dmode(irq) = IO_SAPIC_LOWEST_PRIORITY; + iosapic_trigger(irq) = IO_SAPIC_LEVEL; + iosapic_polarity(irq) = IO_SAPIC_POL_LOW; + +#ifdef DEBUG_IRQ_ROUTING + printk("PCI: BUS %d Slot %x Pin %x IRQ %02x --> Vector %02x IOSAPIC Pin %d\n", + vectors[i].bus, vectors[i].pci_id>>16, vectors[i].pin, vectors[i].irq, + irq, iosapic_pin(irq)); +#endif + } +#endif /* !CONFIG_IA64_IRQ_ACPI */ +} + +static void +iosapic_startup_irq (unsigned int irq) +{ + int pin; + + if (irq == TIMER_IRQ) + return; + pin = iosapic_pin(irq); + if (pin < 0) + /* happens during irq auto probing... */ + return; + set_rte(iosapic_addr(irq), pin, iosapic_polarity(irq), iosapic_trigger(irq), + iosapic_dmode(irq), (ia64_get_lid() >> 16) & 0xffff, irq); + enable_pin(pin, iosapic_addr(irq)); +} + +struct hw_interrupt_type irq_type_iosapic = { + "IOSAPIC", + iosapic_init, + iosapic_startup_irq, + iosapic_shutdown_irq, + iosapic_handle_irq, + iosapic_enable_irq, + iosapic_disable_irq +}; + +void +dig_irq_init (struct irq_desc desc[NR_IRQS]) +{ + int i; + + /* + * Claim all non-legacy irq vectors as ours unless they're + * claimed by someone else already (e.g., timer or IPI are + * handled internally). + */ + for (i = IA64_MIN_VECTORED_IRQ; i <= IA64_MAX_VECTORED_IRQ; ++i) { + if (irq_desc[i].handler == &irq_type_default) + irq_desc[i].handler = &irq_type_iosapic; + } +} + +void +dig_pci_fixup (void) +{ + struct pci_dev *dev; + int irq; + unsigned char pin; + + pci_for_each_dev(dev) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + pin--; /* interrupt pins are numbered starting from 1 */ + irq = iosapic_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), + pin); + if (irq < 0 && dev->bus->parent) { /* go back to the bridge */ + struct pci_dev * bridge = dev->bus->self; + + /* do the bridge swizzle... */ + pin = (pin + PCI_SLOT(dev->devfn)) % 4; + irq = iosapic_get_PCI_irq_vector(bridge->bus->number, + PCI_SLOT(bridge->devfn), pin); + if (irq >= 0) + printk(KERN_WARNING + "PCI: using PPB(B%d,I%d,P%d) to get irq %02x\n", + bridge->bus->number, PCI_SLOT(bridge->devfn), + pin, irq); + } + if (irq >= 0) { + printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %02x\n", + dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); + dev->irq = irq; + } + } + /* + * Nothing to fixup + * Fix out-of-range IRQ numbers + */ + if (dev->irq >= NR_IRQS) + dev->irq = 15; /* Spurious interrupts */ + } +} diff --git a/arch/ia64/dig/machvec.c b/arch/ia64/dig/machvec.c new file mode 100644 index 000000000..640412d7e --- /dev/null +++ b/arch/ia64/dig/machvec.c @@ -0,0 +1,4 @@ +#include <asm/machvec_init.h> +#include <asm/machvec_dig.h> + +MACHVEC_DEFINE(dig) diff --git a/arch/ia64/dig/setup.c b/arch/ia64/dig/setup.c new file mode 100644 index 000000000..6ae40319d --- /dev/null +++ b/arch/ia64/dig/setup.c @@ -0,0 +1,93 @@ +/* + * Platform dependent support for Intel SoftSDV simulator. + * + * Copyright (C) 1999 Intel Corp. + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com> + */ +#include <linux/config.h> + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/kdev_t.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/timex.h> +#include <linux/sched.h> +#include <linux/mc146818rtc.h> + +#include <asm/io.h> +#include <asm/machvec.h> +#include <asm/system.h> + +#ifdef CONFIG_IA64_FW_EMU +# include "../../kernel/fw-emu.c" +#endif + +/* + * This is here so we can use the CMOS detection in ide-probe.c to + * determine what drives are present. In theory, we don't need this + * as the auto-detection could be done via ide-probe.c:do_probe() but + * in practice that would be much slower, which is painful when + * running in the simulator. Note that passing zeroes in DRIVE_INFO + * is sufficient (the IDE driver will autodetect the drive geometry). + */ +char drive_info[4*16]; + +unsigned char aux_device_present = 0xaa; /* XXX remove this when legacy I/O is gone */ + +void __init +dig_setup (char **cmdline_p) +{ + unsigned int orig_x, orig_y, num_cols, num_rows, font_height; + + /* + * This assumes that the EFI partition is physical disk 1 + * partition 1 and the Linux root disk is physical disk 1 + * partition 2. + */ +#ifdef CONFIG_IA64_LION_HACKS + /* default to /dev/sda2 on Lion... */ + ROOT_DEV = to_kdev_t(0x0802); /* default to second partition on first drive */ +#else + /* default to /dev/dha2 on BigSur... */ + ROOT_DEV = to_kdev_t(0x0302); /* default to second partition on first drive */ +#endif + +#ifdef CONFIG_SMP + init_smp_config(); +#endif + + memset(&screen_info, 0, sizeof(screen_info)); + + if (!ia64_boot_param.console_info.num_rows + || !ia64_boot_param.console_info.num_cols) + { + printk("dig_setup: warning: invalid screen-info, guessing 80x25\n"); + orig_x = 0; + orig_y = 0; + num_cols = 80; + num_rows = 25; + font_height = 16; + } else { + orig_x = ia64_boot_param.console_info.orig_x; + orig_y = ia64_boot_param.console_info.orig_y; + num_cols = ia64_boot_param.console_info.num_cols; + num_rows = ia64_boot_param.console_info.num_rows; + font_height = 400 / num_rows; + } + + screen_info.orig_x = orig_x; + screen_info.orig_y = orig_y; + screen_info.orig_video_cols = num_cols; + screen_info.orig_video_lines = num_rows; + screen_info.orig_video_points = font_height; + screen_info.orig_video_mode = 3; /* XXX fake */ + screen_info.orig_video_isVGA = 1; /* XXX fake */ + screen_info.orig_video_ega_bx = 3; /* XXX fake */ +} diff --git a/arch/ia64/hp/Makefile b/arch/ia64/hp/Makefile new file mode 100644 index 000000000..64899f4be --- /dev/null +++ b/arch/ia64/hp/Makefile @@ -0,0 +1,19 @@ +# +# ia64/platform/hp/Makefile +# +# Copyright (C) 1999 Silicon Graphics, Inc. +# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) +# + +all: hp.a + +O_TARGET = hp.a +O_OBJS = hpsim_console.o hpsim_irq.o hpsim_setup.o + +ifeq ($(CONFIG_IA64_GENERIC),y) +O_OBJS += hpsim_machvec.o +endif + +clean:: + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/hp/hpsim_console.c b/arch/ia64/hp/hpsim_console.c new file mode 100644 index 000000000..b97116cee --- /dev/null +++ b/arch/ia64/hp/hpsim_console.c @@ -0,0 +1,78 @@ +/* + * Platform dependent support for HP simulator. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com> + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/kdev_t.h> +#include <linux/console.h> + +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/pal.h> +#include <asm/machvec.h> +#include <asm/pgtable.h> +#include <asm/sal.h> + +#include "hpsim_ssc.h" + +static int simcons_init (struct console *, char *); +static void simcons_write (struct console *, const char *, unsigned); +static int simcons_wait_key (struct console *); +static kdev_t simcons_console_device (struct console *); + +struct console hpsim_cons = { + "simcons", + simcons_write, /* write */ + NULL, /* read */ + simcons_console_device, /* device */ + simcons_wait_key, /* wait_key */ + NULL, /* unblank */ + simcons_init, /* setup */ + CON_PRINTBUFFER, /* flags */ + -1, /* index */ + 0, /* cflag */ + NULL /* next */ +}; + +static int +simcons_init (struct console *cons, char *options) +{ + return 0; +} + +static void +simcons_write (struct console *cons, const char *buf, unsigned count) +{ + unsigned long ch; + + while (count-- > 0) { + ch = *buf++; + ia64_ssc(ch, 0, 0, 0, SSC_PUTCHAR); + if (ch == '\n') + ia64_ssc('\r', 0, 0, 0, SSC_PUTCHAR); + } +} + +static int +simcons_wait_key (struct console *cons) +{ + char ch; + + do { + ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR); + } while (ch == '\0'); + return ch; +} + +static kdev_t +simcons_console_device (struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} diff --git a/arch/ia64/hp/hpsim_irq.c b/arch/ia64/hp/hpsim_irq.c new file mode 100644 index 000000000..72b36d6d6 --- /dev/null +++ b/arch/ia64/hp/hpsim_irq.c @@ -0,0 +1,83 @@ +/* + * Platform dependent support for HP simulator. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com> + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/kdev_t.h> +#include <linux/console.h> + +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/pal.h> +#include <asm/machvec.h> +#include <asm/pgtable.h> +#include <asm/sal.h> + + +static int +irq_hp_sim_handle_irq (unsigned int irq, struct pt_regs *regs) +{ + struct irqaction *action = 0; + struct irq_desc *id = irq_desc + irq; + unsigned int status; + int retval; + + spin_lock(&irq_controller_lock); + { + status = id->status; + if ((status & IRQ_INPROGRESS) == 0 && (status & IRQ_ENABLED) != 0) { + action = id->action; + status |= IRQ_INPROGRESS; + } + id->status = status & ~(IRQ_REPLAY | IRQ_WAITING); + } + spin_unlock(&irq_controller_lock); + + if (!action) { + if (!(id->status & IRQ_AUTODETECT)) + printk("irq_hpsim_handle_irq: unexpected interrupt %u\n", irq); + return 0; + } + + retval = invoke_irq_handlers(irq, regs, action); + + spin_lock(&irq_controller_lock); + { + id->status &= ~IRQ_INPROGRESS; + } + spin_unlock(&irq_controller_lock); + + return retval; +} + +static void +irq_hp_sim_noop (unsigned int irq) +{ +} + +static struct hw_interrupt_type irq_type_hp_sim = { + "hp_sim", + (void (*)(unsigned long)) irq_hp_sim_noop, /* init */ + irq_hp_sim_noop, /* startup */ + irq_hp_sim_noop, /* shutdown */ + irq_hp_sim_handle_irq, /* handle */ + irq_hp_sim_noop, /* enable */ + irq_hp_sim_noop, /* disable */ +}; + +void +hpsim_irq_init (struct irq_desc desc[NR_IRQS]) +{ + int i; + + for (i = IA64_MIN_VECTORED_IRQ; i <= IA64_MAX_VECTORED_IRQ; ++i) { + irq_desc[i].handler = &irq_type_hp_sim; + } +} diff --git a/arch/ia64/hp/hpsim_machvec.c b/arch/ia64/hp/hpsim_machvec.c new file mode 100644 index 000000000..7d78f4961 --- /dev/null +++ b/arch/ia64/hp/hpsim_machvec.c @@ -0,0 +1,4 @@ +#include <asm/machvec_init.h> +#include <asm/machvec_hpsim.h> + +MACHVEC_DEFINE(hpsim) diff --git a/arch/ia64/hp/hpsim_setup.c b/arch/ia64/hp/hpsim_setup.c new file mode 100644 index 000000000..dfa83e135 --- /dev/null +++ b/arch/ia64/hp/hpsim_setup.c @@ -0,0 +1,71 @@ +/* + * Platform dependent support for HP simulator. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com> + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/kdev_t.h> +#include <linux/console.h> + +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/pal.h> +#include <asm/machvec.h> +#include <asm/pgtable.h> +#include <asm/sal.h> + +#include "hpsim_ssc.h" + +extern struct console hpsim_cons; + +/* + * Simulator system call. + */ +inline long +ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr) +{ +#ifdef __GCC_DOESNT_KNOW_IN_REGS__ + register long in0 asm ("r32") = arg0; + register long in1 asm ("r33") = arg1; + register long in2 asm ("r34") = arg2; + register long in3 asm ("r35") = arg3; +#else + register long in0 asm ("in0") = arg0; + register long in1 asm ("in1") = arg1; + register long in2 asm ("in2") = arg2; + register long in3 asm ("in3") = arg3; +#endif + register long r8 asm ("r8"); + register long r15 asm ("r15") = nr; + + asm volatile ("break 0x80001" + : "=r"(r8) + : "r"(r15), "r"(in0), "r"(in1), "r"(in2), "r"(in3)); + return r8; +} + +void +ia64_ssc_connect_irq (long intr, long irq) +{ + ia64_ssc(intr, irq, 0, 0, SSC_CONNECT_INTERRUPT); +} + +void +ia64_ctl_trace (long on) +{ + ia64_ssc(on, 0, 0, 0, SSC_CTL_TRACE); +} + +void __init +hpsim_setup (char **cmdline_p) +{ + ROOT_DEV = to_kdev_t(0x0801); /* default to first SCSI drive */ + + register_console (&hpsim_cons); +} diff --git a/arch/ia64/hp/hpsim_ssc.h b/arch/ia64/hp/hpsim_ssc.h new file mode 100644 index 000000000..bfa390627 --- /dev/null +++ b/arch/ia64/hp/hpsim_ssc.h @@ -0,0 +1,36 @@ +/* + * Platform dependent support for HP simulator. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com> + */ +#ifndef _IA64_PLATFORM_HPSIM_SSC_H +#define _IA64_PLATFORM_HPSIM_SSC_H + +/* Simulator system calls: */ + +#define SSC_CONSOLE_INIT 20 +#define SSC_GETCHAR 21 +#define SSC_PUTCHAR 31 +#define SSC_CONNECT_INTERRUPT 58 +#define SSC_GENERATE_INTERRUPT 59 +#define SSC_SET_PERIODIC_INTERRUPT 60 +#define SSC_GET_RTC 65 +#define SSC_EXIT 66 +#define SSC_LOAD_SYMBOLS 69 +#define SSC_GET_TOD 74 +#define SSC_CTL_TRACE 76 + +#define SSC_NETDEV_PROBE 100 +#define SSC_NETDEV_SEND 101 +#define SSC_NETDEV_RECV 102 +#define SSC_NETDEV_ATTACH 103 +#define SSC_NETDEV_DETACH 104 + +/* + * Simulator system call. + */ +extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr); + +#endif /* _IA64_PLATFORM_HPSIM_SSC_H */ diff --git a/arch/ia64/ia32/Makefile b/arch/ia64/ia32/Makefile new file mode 100644 index 000000000..674a6eb6e --- /dev/null +++ b/arch/ia64/ia32/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for the ia32 kernel emulation subsystem. +# + +.S.s: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -E -o $*.s $< +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o $*.o $< + +all: ia32.o + +O_TARGET := ia32.o +O_OBJS := ia32_entry.o ia32_signal.o sys_ia32.o ia32_support.o binfmt_elf32.o + +clean:: + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c new file mode 100644 index 000000000..685d85b20 --- /dev/null +++ b/arch/ia64/ia32/binfmt_elf32.c @@ -0,0 +1,180 @@ +/* + * IA-32 ELF support. + * + * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> + */ +#include <linux/config.h> +#include <linux/posix_types.h> + +#include <asm/signal.h> +#include <asm/ia32.h> + +#define CONFIG_BINFMT_ELF32 + +/* Override some function names */ +#undef start_thread +#define start_thread ia32_start_thread +#define init_elf_binfmt init_elf32_binfmt + +#undef CONFIG_BINFMT_ELF +#ifdef CONFIG_BINFMT_ELF32 +# define CONFIG_BINFMT_ELF CONFIG_BINFMT_ELF32 +#endif + +#undef CONFIG_BINFMT_ELF_MODULE +#ifdef CONFIG_BINFMT_ELF32_MODULE +# define CONFIG_BINFMT_ELF_MODULE CONFIG_BINFMT_ELF32_MODULE +#endif + +void ia64_elf32_init(struct pt_regs *regs); +#define ELF_PLAT_INIT(_r) ia64_elf32_init(_r) + +#define setup_arg_pages(bprm) ia32_setup_arg_pages(bprm) + +/* Ugly but avoids duplication */ +#include "../../../fs/binfmt_elf.c" + +/* Global descriptor table */ +unsigned long *ia32_gdt_table, *ia32_tss; + +struct page * +put_shared_page(struct task_struct * tsk, struct page *page, unsigned long address) +{ + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; + + if (page_count(page) != 1) + printk("mem_map disagrees with %p at %08lx\n", page, address); + pgd = pgd_offset(tsk->mm, address); + pmd = pmd_alloc(pgd, address); + if (!pmd) { + __free_page(page); + oom(tsk); + return 0; + } + pte = pte_alloc(pmd, address); + if (!pte) { + __free_page(page); + oom(tsk); + return 0; + } + if (!pte_none(*pte)) { + pte_ERROR(*pte); + __free_page(page); + return 0; + } + flush_page_to_ram(page); + set_pte(pte, pte_mkwrite(mk_pte(page, PAGE_SHARED))); + /* no need for flush_tlb */ + return page; +} + +void ia64_elf32_init(struct pt_regs *regs) +{ + int nr; + + put_shared_page(current, mem_map + MAP_NR(ia32_gdt_table), IA32_PAGE_OFFSET); + if (PAGE_SHIFT <= IA32_PAGE_SHIFT) + put_shared_page(current, mem_map + MAP_NR(ia32_tss), IA32_PAGE_OFFSET + PAGE_SIZE); + + nr = smp_processor_id(); + + /* Do all the IA-32 setup here */ + + /* CS descriptor */ + __asm__("mov ar.csd = %0" : /* no outputs */ + : "r" IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0xBL, 1L, + 3L, 1L, 1L, 1L)); + /* SS descriptor */ + __asm__("mov ar.ssd = %0" : /* no outputs */ + : "r" IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0x3L, 1L, + 3L, 1L, 1L, 1L)); + /* EFLAGS */ + __asm__("mov ar.eflag = %0" : /* no outputs */ : "r" (IA32_EFLAG)); + + /* Control registers */ + __asm__("mov ar.cflg = %0" + : /* no outputs */ + : "r" (((ulong) IA32_CR4 << 32) | IA32_CR0)); + __asm__("mov ar.fsr = %0" + : /* no outputs */ + : "r" ((ulong)IA32_FSR_DEFAULT)); + __asm__("mov ar.fcr = %0" + : /* no outputs */ + : "r" ((ulong)IA32_FCR_DEFAULT)); + __asm__("mov ar.fir = r0"); + __asm__("mov ar.fdr = r0"); + /* TSS */ + __asm__("mov ar.k1 = %0" + : /* no outputs */ + : "r" IA64_SEG_DESCRIPTOR(IA32_PAGE_OFFSET + PAGE_SIZE, + 0x1FFFL, 0xBL, 1L, + 3L, 1L, 1L, 1L)); + + /* Get the segment selectors right */ + regs->r16 = (__USER_DS << 16) | (__USER_DS); /* ES == DS, GS, FS are zero */ + regs->r17 = (_TSS(nr) << 48) | (_LDT(nr) << 32) + | (__USER_DS << 16) | __USER_CS; + + /* Setup other segment descriptors - ESD, DSD, FSD, GSD */ + regs->r24 = IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0x3L, 1L, 3L, 1L, 1L, 1L); + regs->r27 = IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0x3L, 1L, 3L, 1L, 1L, 1L); + regs->r28 = IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0x3L, 1L, 3L, 1L, 1L, 1L); + regs->r29 = IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0x3L, 1L, 3L, 1L, 1L, 1L); + + /* Setup the LDT and GDT */ + regs->r30 = ia32_gdt_table[_LDT(nr)]; + regs->r31 = IA64_SEG_DESCRIPTOR(0xc0000000L, 0x400L, 0x3L, 1L, 3L, + 1L, 1L, 1L); + + /* Clear psr.ac */ + regs->cr_ipsr &= ~IA64_PSR_AC; + + regs->loadrs = 0; +} + +#undef STACK_TOP +#define STACK_TOP ((IA32_PAGE_OFFSET/3) * 2) + +int ia32_setup_arg_pages(struct linux_binprm *bprm) +{ + unsigned long stack_base; + struct vm_area_struct *mpnt; + int i; + + stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; + + bprm->p += stack_base; + if (bprm->loader) + bprm->loader += stack_base; + bprm->exec += stack_base; + + mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!mpnt) + return -ENOMEM; + + { + mpnt->vm_mm = current->mm; + mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; + mpnt->vm_end = STACK_TOP; + mpnt->vm_page_prot = PAGE_COPY; + mpnt->vm_flags = VM_STACK_FLAGS; + mpnt->vm_ops = NULL; + mpnt->vm_pgoff = 0; + mpnt->vm_file = NULL; + mpnt->vm_private_data = 0; + insert_vm_struct(current->mm, mpnt); + current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + } + + for (i = 0 ; i < MAX_ARG_PAGES ; i++) { + if (bprm->page[i]) { + current->mm->rss++; + put_dirty_page(current,bprm->page[i],stack_base); + } + stack_base += PAGE_SIZE; + } + + return 0; +} diff --git a/arch/ia64/ia32/ia32_entry.S b/arch/ia64/ia32/ia32_entry.S new file mode 100644 index 000000000..1342e64f0 --- /dev/null +++ b/arch/ia64/ia32/ia32_entry.S @@ -0,0 +1,255 @@ +#include <asm/offsets.h> +#include <asm/signal.h> + + .global ia32_ret_from_syscall + .proc ia64_ret_from_syscall +ia32_ret_from_syscall: + cmp.ge p6,p7=r8,r0 // syscall executed successfully? + adds r2=IA64_PT_REGS_R8_OFFSET+16,sp // r2 = &pt_regs.r8 + ;; + st8 [r2]=r8 // store return value in slot for r8 + br.cond.sptk.few ia64_leave_kernel + + // + // Invoke a system call, but do some tracing before and after the call. + // We MUST preserve the current register frame throughout this routine + // because some system calls (such as ia64_execve) directly + // manipulate ar.pfs. + // + // Input: + // r15 = syscall number + // b6 = syscall entry point + // + .global ia32_trace_syscall + .proc ia32_trace_syscall +ia32_trace_syscall: + br.call.sptk.few rp=invoke_syscall_trace // give parent a chance to catch syscall args +.Lret4: br.call.sptk.few rp=b6 // do the syscall +.Lret5: cmp.lt p6,p0=r8,r0 // syscall failed? + adds r2=IA64_PT_REGS_R8_OFFSET+16,sp // r2 = &pt_regs.r8 + ;; + st8.spill [r2]=r8 // store return value in slot for r8 + br.call.sptk.few rp=invoke_syscall_trace // give parent a chance to catch return value +.Lret6: br.cond.sptk.many ia64_leave_kernel // rp MUST be != ia64_leave_kernel! + + .endp ia32_trace_syscall + + .align 16 + .global sys32_fork + .proc sys32_fork +sys32_fork: + alloc r16=ar.pfs,2,2,3,0;; + movl r28=1f + mov loc1=rp + br.cond.sptk.many save_switch_stack +1: + mov loc0=r16 // save ar.pfs across do_fork + adds out2=IA64_SWITCH_STACK_SIZE+16,sp + adds r2=IA64_SWITCH_STACK_SIZE+IA64_PT_REGS_R12_OFFSET+16,sp + mov out0=SIGCHLD // out0 = clone_flags + ;; + ld8 out1=[r2] // fetch usp from pt_regs.r12 + br.call.sptk.few rp=do_fork +.ret1: + mov ar.pfs=loc0 + adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack + mov rp=loc1 + ;; + br.ret.sptk.many rp + .endp sys32_fork + + .rodata + .align 8 + .globl ia32_syscall_table +ia32_syscall_table: + data8 sys_ni_syscall /* 0 - old "setup(" system call*/ + data8 sys_exit + data8 sys32_fork + data8 sys_read + data8 sys_write + data8 sys_open /* 5 */ + data8 sys_close + data8 sys32_waitpid + data8 sys_creat + data8 sys_link + data8 sys_unlink /* 10 */ + data8 sys32_execve + data8 sys_chdir + data8 sys_ni_syscall /* sys_time is not supported on ia64 */ + data8 sys_mknod + data8 sys_chmod /* 15 */ + data8 sys_lchown + data8 sys_ni_syscall /* old break syscall holder */ + data8 sys_ni_syscall + data8 sys_lseek + data8 sys_getpid /* 20 */ + data8 sys_mount + data8 sys_oldumount + data8 sys_setuid + data8 sys_getuid + data8 sys_ni_syscall /* sys_stime is not supported on IA64 */ /* 25 */ + data8 sys_ptrace + data8 sys32_alarm + data8 sys_ni_syscall + data8 sys_ni_syscall + data8 sys_ni_syscall /* 30 */ + data8 sys_ni_syscall /* old stty syscall holder */ + data8 sys_ni_syscall /* old gtty syscall holder */ + data8 sys_access + data8 sys_nice + data8 sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + data8 sys_sync + data8 sys_kill + data8 sys_rename + data8 sys_mkdir + data8 sys_rmdir /* 40 */ + data8 sys_dup + data8 sys32_pipe + data8 sys_times + data8 sys_ni_syscall /* old prof syscall holder */ + data8 sys_brk /* 45 */ + data8 sys_setgid + data8 sys_getgid + data8 sys_ni_syscall + data8 sys_geteuid + data8 sys_getegid /* 50 */ + data8 sys_acct + data8 sys_umount /* recycled never used phys( */ + data8 sys_ni_syscall /* old lock syscall holder */ + data8 sys_ioctl + data8 sys_fcntl /* 55 */ + data8 sys_ni_syscall /* old mpx syscall holder */ + data8 sys_setpgid + data8 sys_ni_syscall /* old ulimit syscall holder */ + data8 sys_ni_syscall + data8 sys_umask /* 60 */ + data8 sys_chroot + data8 sys_ustat + data8 sys_dup2 + data8 sys_getppid + data8 sys_getpgrp /* 65 */ + data8 sys_setsid + data8 sys_ni_syscall + data8 sys_ni_syscall + data8 sys_ni_syscall + data8 sys_setreuid /* 70 */ + data8 sys_setregid + data8 sys_ni_syscall + data8 sys_sigpending + data8 sys_sethostname + data8 sys32_setrlimit /* 75 */ + data8 sys32_getrlimit + data8 sys_getrusage + data8 sys32_gettimeofday + data8 sys32_settimeofday + data8 sys_getgroups /* 80 */ + data8 sys_setgroups + data8 sys_ni_syscall + data8 sys_symlink + data8 sys_ni_syscall + data8 sys_readlink /* 85 */ + data8 sys_uselib + data8 sys_swapon + data8 sys_reboot + data8 sys32_readdir + data8 sys32_mmap /* 90 */ + data8 sys_munmap + data8 sys_truncate + data8 sys_ftruncate + data8 sys_fchmod + data8 sys_fchown /* 95 */ + data8 sys_getpriority + data8 sys_setpriority + data8 sys_ni_syscall /* old profil syscall holder */ + data8 sys32_statfs + data8 sys32_fstatfs /* 100 */ + data8 sys_ioperm + data8 sys32_socketcall + data8 sys_syslog + data8 sys32_setitimer + data8 sys32_getitimer /* 105 */ + data8 sys32_newstat + data8 sys32_newlstat + data8 sys32_newfstat + data8 sys_ni_syscall + data8 sys_iopl /* 110 */ + data8 sys_vhangup + data8 sys_ni_syscall // used to be sys_idle + data8 sys_ni_syscall + data8 sys32_wait4 + data8 sys_swapoff /* 115 */ + data8 sys_sysinfo + data8 sys32_ipc + data8 sys_fsync + data8 sys32_sigreturn + data8 sys_clone /* 120 */ + data8 sys_setdomainname + data8 sys_newuname + data8 sys_modify_ldt + data8 sys_adjtimex + data8 sys32_mprotect /* 125 */ + data8 sys_sigprocmask + data8 sys_create_module + data8 sys_init_module + data8 sys_delete_module + data8 sys_get_kernel_syms /* 130 */ + data8 sys_quotactl + data8 sys_getpgid + data8 sys_fchdir + data8 sys_bdflush + data8 sys_sysfs /* 135 */ + data8 sys_personality + data8 sys_ni_syscall /* for afs_syscall */ + data8 sys_setfsuid + data8 sys_setfsgid + data8 sys_llseek /* 140 */ + data8 sys32_getdents + data8 sys32_select + data8 sys_flock + data8 sys_msync + data8 sys32_readv /* 145 */ + data8 sys32_writev + data8 sys_getsid + data8 sys_fdatasync + data8 sys_sysctl + data8 sys_mlock /* 150 */ + data8 sys_munlock + data8 sys_mlockall + data8 sys_munlockall + data8 sys_sched_setparam + data8 sys_sched_getparam /* 155 */ + data8 sys_sched_setscheduler + data8 sys_sched_getscheduler + data8 sys_sched_yield + data8 sys_sched_get_priority_max + data8 sys_sched_get_priority_min /* 160 */ + data8 sys_sched_rr_get_interval + data8 sys32_nanosleep + data8 sys_mremap + data8 sys_setresuid + data8 sys_getresuid /* 165 */ + data8 sys_vm86 + data8 sys_query_module + data8 sys_poll + data8 sys_nfsservctl + data8 sys_setresgid /* 170 */ + data8 sys_getresgid + data8 sys_prctl + data8 sys32_rt_sigreturn + data8 sys32_rt_sigaction + data8 sys32_rt_sigprocmask /* 175 */ + data8 sys_rt_sigpending + data8 sys_rt_sigtimedwait + data8 sys_rt_sigqueueinfo + data8 sys_rt_sigsuspend + data8 sys_pread /* 180 */ + data8 sys_pwrite + data8 sys_chown + data8 sys_getcwd + data8 sys_capget + data8 sys_capset /* 185 */ + data8 sys_sigaltstack + data8 sys_sendfile + data8 sys_ni_syscall /* streams1 */ + data8 sys_ni_syscall /* streams2 */ + data8 sys32_vfork /* 190 */ diff --git a/arch/ia64/ia32/ia32_signal.c b/arch/ia64/ia32/ia32_signal.c new file mode 100644 index 000000000..ed443ee66 --- /dev/null +++ b/arch/ia64/ia32/ia32_signal.c @@ -0,0 +1,412 @@ +/* + * IA32 Architecture-specific signal handling support. + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> + * + * Derived from i386 and Alpha versions. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/ptrace.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/wait.h> + +#include <asm/uaccess.h> +#include <asm/rse.h> +#include <asm/sigcontext.h> +#include <asm/segment.h> +#include <asm/ia32.h> + +#define DEBUG_SIG 0 +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + + +struct sigframe_ia32 +{ + int pretcode; + int sig; + struct sigcontext_ia32 sc; + struct _fpstate_ia32 fpstate; + unsigned int extramask[_IA32_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe_ia32 +{ + int pretcode; + int sig; + int pinfo; + int puc; + struct siginfo info; + struct ucontext_ia32 uc; + struct _fpstate_ia32 fpstate; + char retcode[8]; +}; + +static int +setup_sigcontext_ia32(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate, + struct pt_regs *regs, unsigned long mask) +{ + int err = 0; + + err |= __put_user((regs->r16 >> 32) & 0xffff , (unsigned int *)&sc->fs); + err |= __put_user((regs->r16 >> 48) & 0xffff , (unsigned int *)&sc->gs); + + err |= __put_user((regs->r16 >> 56) & 0xffff, (unsigned int *)&sc->es); + err |= __put_user(regs->r16 & 0xffff, (unsigned int *)&sc->ds); + err |= __put_user(regs->r15, &sc->edi); + err |= __put_user(regs->r14, &sc->esi); + err |= __put_user(regs->r13, &sc->ebp); + err |= __put_user(regs->r12, &sc->esp); + err |= __put_user(regs->r11, &sc->ebx); + err |= __put_user(regs->r10, &sc->edx); + err |= __put_user(regs->r9, &sc->ecx); + err |= __put_user(regs->r8, &sc->eax); +#if 0 + err |= __put_user(current->tss.trap_no, &sc->trapno); + err |= __put_user(current->tss.error_code, &sc->err); +#endif + err |= __put_user(regs->cr_iip, &sc->eip); + err |= __put_user(regs->r17 & 0xffff, (unsigned int *)&sc->cs); +#if 0 + err |= __put_user(regs->eflags, &sc->eflags); +#endif + + err |= __put_user(regs->r12, &sc->esp_at_signal); + err |= __put_user((regs->r17 >> 16) & 0xffff, (unsigned int *)&sc->ss); + +#if 0 + tmp = save_i387(fpstate); + if (tmp < 0) + err = 1; + else + err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate); + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + err |= __put_user(current->tss.cr2, &sc->cr2); +#endif + + return err; +} + +static int +restore_sigcontext_ia32(struct pt_regs *regs, struct sigcontext_ia32 *sc, int *peax) +{ + unsigned int err = 0; + +#define COPY(ia64x, ia32x) err |= __get_user(regs->ia64x, &sc->ia32x) + +#define copyseg_gs(tmp) (regs->r16 |= (unsigned long) tmp << 48) +#define copyseg_fs(tmp) (regs->r16 |= (unsigned long) tmp << 32) +#define copyseg_cs(tmp) (regs->r17 |= tmp) +#define copyseg_ss(tmp) (regs->r17 |= (unsigned long) tmp << 16) +#define copyseg_es(tmp) (regs->r16 |= (unsigned long) tmp << 16) +#define copyseg_ds(tmp) (regs->r16 |= tmp) + +#define COPY_SEG(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + copyseg_##seg(tmp); } + +#define COPY_SEG_STRICT(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + copyseg_##seg(tmp|3); } + + /* To make COPY_SEGs easier, we zero r16, r17 */ + regs->r16 = 0; + regs->r17 = 0; + + COPY_SEG(gs); + COPY_SEG(fs); + COPY_SEG(es); + COPY_SEG(ds); + COPY(r15, edi); + COPY(r14, esi); + COPY(r13, ebp); + COPY(r12, esp); + COPY(r11, ebx); + COPY(r10, edx); + COPY(r9, ecx); + COPY(cr_iip, eip); + COPY_SEG_STRICT(cs); + COPY_SEG_STRICT(ss); +#if 0 + { + unsigned int tmpflags; + err |= __get_user(tmpflags, &sc->eflags); + /* XXX: Change this to ar.eflags */ + regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); + regs->orig_eax = -1; /* disable syscall checks */ + } + + { + struct _fpstate * buf; + err |= __get_user(buf, &sc->fpstate); + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= restore_i387(buf); + } + } +#endif + + err |= __get_user(*peax, &sc->eax); + return err; + +#if 0 +badframe: + return 1; +#endif + +} + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long esp; + unsigned int xss; + + /* Default to using normal stack */ + esp = regs->r12; + xss = regs->r16 >> 16; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(esp)) + esp = current->sas_ss_sp + current->sas_ss_size; + } + /* Legacy stack switching not supported */ + + return (void *)((esp - frame_size) & -8ul); +} + +static void +setup_frame_ia32(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs * regs) +{ + struct sigframe_ia32 *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= __put_user((current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? (int)(current->exec_domain->signal_invmap[sig]) + : sig), + &frame->sig); + + err |= setup_sigcontext_ia32(&frame->sc, &frame->fpstate, regs, set->sig[0]); + + if (_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + err |= __put_user(frame->retcode, &frame->pretcode); + /* This is popl %eax ; movl $,%eax ; int $0x80 */ + err |= __put_user(0xb858, (short *)(frame->retcode+0)); +#define __IA32_NR_sigreturn 119 + err |= __put_user(__IA32_NR_sigreturn & 0xffff, (short *)(frame->retcode+2)); + err |= __put_user(__IA32_NR_sigreturn >> 16, (short *)(frame->retcode+4)); + err |= __put_user(0x80cd, (short *)(frame->retcode+6)); + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->r12 = (unsigned long) frame; + regs->cr_iip = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + regs->r16 = (__USER_DS << 16) | (__USER_DS); /* ES == DS, GS, FS are zero */ + regs->r17 = (__USER_DS << 16) | __USER_CS; + +#if 0 + regs->eflags &= ~TF_MASK; +#endif + +#if 1 + printk("SIG deliver (%s:%d): sp=%p pc=%lx ra=%x\n", + current->comm, current->pid, frame, regs->cr_iip, frame->pretcode); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +static void +setup_rt_frame_ia32(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + struct rt_sigframe_ia32 *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= __put_user((current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + err |= __put_user(&frame->info, &frame->pinfo); + err |= __put_user(&frame->uc, &frame->puc); + err |= __copy_to_user(&frame->info, info, sizeof(*info)); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->r12), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= setup_sigcontext_ia32(&frame->uc.uc_mcontext, &frame->fpstate, + regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + err |= __put_user(frame->retcode, &frame->pretcode); + /* This is movl $,%eax ; int $0x80 */ + err |= __put_user(0xb8, (char *)(frame->retcode+0)); +#define __IA32_NR_rt_sigreturn 173 + err |= __put_user(__IA32_NR_rt_sigreturn, (int *)(frame->retcode+1)); + err |= __put_user(0x80cd, (short *)(frame->retcode+5)); + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->r12 = (unsigned long) frame; + regs->cr_iip = (unsigned long) ka->sa.sa_handler; + + set_fs(USER_DS); + + regs->r16 = (__USER_DS << 16) | (__USER_DS); /* ES == DS, GS, FS are zero */ + regs->r17 = (__USER_DS << 16) | __USER_CS; + +#if 0 + regs->eflags &= ~TF_MASK; +#endif + +#if 1 + printk("SIG deliver (%s:%d): sp=%p pc=%lx ra=%x\n", + current->comm, current->pid, frame, regs->cr_iip, frame->pretcode); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +long +ia32_setup_frame1 (int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame_ia32(sig, ka, info, set, regs); + else + setup_frame_ia32(sig, ka, set, regs); + +} + +asmlinkage int +sys32_sigreturn(int arg1, int arg2, int arg3, int arg4, int arg5, unsigned long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + struct sigframe_ia32 *frame = (struct sigframe_ia32 *)(regs->r12- 8); + sigset_t set; + int eax; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + + if (__get_user(set.sig[0], &frame->sc.oldmask) + || (_IA32_NSIG_WORDS > 1 + && __copy_from_user((((char *) &set.sig) + 4), + &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = (sigset_t) set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext_ia32(regs, &frame->sc, &eax)) + goto badframe; + return eax; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int +sys32_rt_sigreturn(int arg1, int arg2, int arg3, int arg4, int arg5, unsigned long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + struct rt_sigframe_ia32 *frame = (struct rt_sigframe_ia32 *)(regs->r12 - 4); + sigset_t set; + stack_t st; + int eax; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext_ia32(regs, &frame->uc.uc_mcontext, &eax)) + goto badframe; + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs->r12); + + return eax; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + diff --git a/arch/ia64/ia32/ia32_support.c b/arch/ia64/ia32/ia32_support.c new file mode 100644 index 000000000..dcf61e8e4 --- /dev/null +++ b/arch/ia64/ia32/ia32_support.c @@ -0,0 +1,61 @@ +/* + * IA32 helper functions + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/sched.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/processor.h> +#include <asm/ia32.h> + +extern unsigned long *ia32_gdt_table, *ia32_tss; + +extern void die_if_kernel (char *str, struct pt_regs *regs, long err); + +/* + * Setup IA32 GDT and TSS + */ +void +ia32_gdt_init(void) +{ + unsigned long gdt_and_tss_page; + + /* allocate two IA-32 pages of memory: */ + gdt_and_tss_page = __get_free_pages(GFP_KERNEL, + (IA32_PAGE_SHIFT < PAGE_SHIFT) + ? 0 : (IA32_PAGE_SHIFT + 1) - PAGE_SHIFT); + ia32_gdt_table = (unsigned long *) gdt_and_tss_page; + ia32_tss = (unsigned long *) (gdt_and_tss_page + IA32_PAGE_SIZE); + + /* Zero the gdt and tss */ + memset((void *) gdt_and_tss_page, 0, 2*IA32_PAGE_SIZE); + + /* CS descriptor in IA-32 format */ + ia32_gdt_table[4] = IA32_SEG_DESCRIPTOR(0L, 0xBFFFFFFFL, 0xBL, 1L, + 3L, 1L, 1L, 1L, 1L); + + /* DS descriptor in IA-32 format */ + ia32_gdt_table[5] = IA32_SEG_DESCRIPTOR(0L, 0xBFFFFFFFL, 0x3L, 1L, + 3L, 1L, 1L, 1L, 1L); +} + +/* + * Handle bad IA32 interrupt via syscall + */ +void +ia32_bad_interrupt (unsigned long int_num, struct pt_regs *regs) +{ + siginfo_t siginfo; + + die_if_kernel("Bad IA-32 interrupt", regs, int_num); + + siginfo.si_signo = SIGTRAP; + siginfo.si_errno = int_num; /* XXX is it legal to abuse si_errno like this? */ + siginfo.si_code = TRAP_BRKPT; + force_sig_info(SIGTRAP, &siginfo, current); +} + diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c new file mode 100644 index 000000000..7b4c4995e --- /dev/null +++ b/arch/ia64/ia32/sys_ia32.c @@ -0,0 +1,4309 @@ +/* + * sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Based on + * sys_sparc32 + * + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> + * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/signal.h> +#include <linux/utime.h> +#include <linux/resource.h> +#include <linux/times.h> +#include <linux/utsname.h> +#include <linux/timex.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/malloc.h> +#include <linux/uio.h> +#include <linux/nfs_fs.h> +#include <linux/smb_fs.h> +#include <linux/smb_mount.h> +#include <linux/ncp_fs.h> +#include <linux/quota.h> +#include <linux/module.h> +#include <linux/sunrpc/svc.h> +#include <linux/nfsd/nfsd.h> +#include <linux/nfsd/cache.h> +#include <linux/nfsd/xdr.h> +#include <linux/nfsd/syscall.h> +#include <linux/poll.h> +#include <linux/personality.h> +#include <linux/stat.h> +#include <linux/ipc.h> + +#include <asm/types.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> +#include <asm/ipc.h> + +#include <net/scm.h> +#include <net/sock.h> +#include <asm/ia32.h> + +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) ((unsigned long)(__x)) + +/* + * 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 sys32_vfork( +int dummy0, +int dummy1, +int dummy2, +int dummy3, +int dummy4, +int dummy5, +int dummy6, +int dummy7, +int stack) +{ + struct pt_regs *regs = (struct pt_regs *)&stack; + + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->r12, regs); +} + +static int +nargs(unsigned int arg, char **ap) +{ + char *ptr; + int n, err; + + n = 0; + do { + if (err = get_user(ptr, (int *)arg)) + return(err); + if (ap) + *ap++ = ptr; + arg += sizeof(unsigned int); + n++; + } while (ptr); + return(n - 1); +} + +asmlinkage long +sys32_execve( +char *filename, +unsigned int argv, +unsigned int envp, +int dummy3, +int dummy4, +int dummy5, +int dummy6, +int dummy7, +int stack) +{ + struct pt_regs *regs = (struct pt_regs *)&stack; + char **av, **ae; + int na, ne, r, len; + + na = nargs(argv, NULL); + ne = nargs(envp, NULL); + len = (na + ne + 2) * sizeof(*av); + /* + * kmalloc won't work because the `sys_exec' code will attempt + * to do a `get_user' on the arg list and `get_user' will fail + * on a kernel address (simplifies `get_user'). Instead we + * do an mmap to get a user address. Note that since a successful + * `execve' frees all current memory we only have to do an + * `munmap' if the `execve' failes. + */ + down(¤t->mm->mmap_sem); + lock_kernel(); + + av = do_mmap_pgoff(0, NULL, len, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0); + + unlock_kernel(); + up(¤t->mm->mmap_sem); + + if (IS_ERR(av)) + return(av); + ae = av + na + 1; + av[na] = (char *)0; + ae[ne] = (char *)0; + (void)nargs(argv, av); + (void)nargs(envp, ae); + r = sys_execve(filename, av, ae, regs); + if (IS_ERR(r)) + sys_munmap(av, len); + return(r); +} + +static inline int +putstat(struct stat32 *ubuf, struct stat *kbuf) +{ + int err; + + err = put_user (kbuf->st_dev, &ubuf->st_dev); + err |= __put_user (kbuf->st_ino, &ubuf->st_ino); + err |= __put_user (kbuf->st_mode, &ubuf->st_mode); + err |= __put_user (kbuf->st_nlink, &ubuf->st_nlink); + err |= __put_user (kbuf->st_uid, &ubuf->st_uid); + err |= __put_user (kbuf->st_gid, &ubuf->st_gid); + err |= __put_user (kbuf->st_rdev, &ubuf->st_rdev); + err |= __put_user (kbuf->st_size, &ubuf->st_size); + err |= __put_user (kbuf->st_atime, &ubuf->st_atime); + err |= __put_user (kbuf->st_mtime, &ubuf->st_mtime); + err |= __put_user (kbuf->st_ctime, &ubuf->st_ctime); + err |= __put_user (kbuf->st_blksize, &ubuf->st_blksize); + err |= __put_user (kbuf->st_blocks, &ubuf->st_blocks); + return err; +} + +extern asmlinkage int sys_newstat(char * filename, struct stat * statbuf); + +asmlinkage int +sys32_newstat(char * filename, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newstat(filename, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage int sys_newlstat(char * filename, struct stat * statbuf); + +asmlinkage int +sys32_newlstat(char * filename, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newlstat(filename, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf); + +asmlinkage int +sys32_newfstat(unsigned int fd, struct stat32 *statbuf) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newfstat(fd, &s); + set_fs (old_fs); + if (putstat (statbuf, &s)) + return -EFAULT; + return ret; +} + +#define ALIGN4K(a) (((a) + 0xfff) & ~0xfff) +#define OFFSET4K(a) ((a) & 0xfff) + +unsigned long +do_mmap_fake(struct file *file, unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, unsigned long off) +{ + struct inode *inode; + void *front, *back; + unsigned long baddr; + int r; + char c; + + if (OFFSET4K(addr) || OFFSET4K(off)) + return -EINVAL; + if (prot & PROT_WRITE) + prot |= PROT_EXEC; + front = NULL; + back = NULL; + if ((baddr = (addr & PAGE_MASK)) != addr && get_user(c, (char *)baddr) == 0) { + front = kmalloc(addr - baddr, GFP_KERNEL); + memcpy(front, (void *)baddr, addr - baddr); + } + if ((addr + len) & ~PAGE_MASK && get_user(c, (char *)(addr + len)) == 0) { + back = kmalloc(PAGE_SIZE - ((addr + len) & ~PAGE_MASK), GFP_KERNEL); + memcpy(back, addr + len, PAGE_SIZE - ((addr + len) & ~PAGE_MASK)); + } + if ((r = do_mmap(0, baddr, len + (addr - baddr), prot, flags | MAP_ANONYMOUS, 0)) < 0) + return(r); + if (back) { + memcpy(addr + len, back, PAGE_SIZE - ((addr + len) & ~PAGE_MASK)); + kfree(back); + } + if (front) { + memcpy((void *)baddr, front, addr - baddr); + kfree(front); + } + if (flags & MAP_ANONYMOUS) { + memset(addr, 0, len); + return(addr); + } + if (!file) + return -EINVAL; + inode = file->f_dentry->d_inode; + if (!inode->i_op || !inode->i_op->default_file_ops) + return -EINVAL; + if (!file->f_op->read) + return -EINVAL; + if (file->f_op->llseek) { + if (file->f_op->llseek(file,off,0) != off) + return -EINVAL; + } else + file->f_pos = off; + r = file->f_op->read(file, (char *)addr, len, &file->f_pos); + return (r < 0) ? -EINVAL : addr; +} + +/* + * Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned int addr; + unsigned int len; + unsigned int prot; + unsigned int flags; + unsigned int fd; + unsigned int offset; +}; + +asmlinkage int +sys32_mmap(struct mmap_arg_struct *arg) +{ + int error = -EFAULT; + struct file * file = NULL; + struct mmap_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + + down(¤t->mm->mmap_sem); + lock_kernel(); + if (!(a.flags & MAP_ANONYMOUS)) { + error = -EBADF; + file = fget(a.fd); + if (!file) + goto out; + } + a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + if ((a.flags & MAP_FIXED) && ((a.addr & ~PAGE_MASK) || (a.offset & ~PAGE_MASK))) { + unlock_kernel(); + up(¤t->mm->mmap_sem); + error = do_mmap_fake(file, a.addr, a.len, a.prot, a.flags, a.offset); + down(¤t->mm->mmap_sem); + lock_kernel(); + } else + error = do_mmap(file, a.addr, a.len, a.prot, a.flags, a.offset); + if (file) + fput(file); +out: + unlock_kernel(); + up(¤t->mm->mmap_sem); + return error; +} + +asmlinkage long +sys32_pipe(int *fd) +{ + int retval; + int fds[2]; + + lock_kernel(); + retval = do_pipe(fds); + if (retval) + goto out; + if (copy_to_user(fd, fds, sizeof(fds))) + retval = -EFAULT; + out: + unlock_kernel(); + return retval; +} + +asmlinkage long +sys32_mprotect(unsigned long start, size_t len, unsigned long prot) +{ + + if (prot == 0) + return(0); + len += start & ~PAGE_MASK; + if ((start & ~PAGE_MASK) && (prot & PROT_WRITE)) + prot |= PROT_EXEC; + return(sys_mprotect(start & PAGE_MASK, len & PAGE_MASK, prot)); +} + +asmlinkage int +sys32_rt_sigaction(int sig, struct sigaction32 *act, + struct sigaction32 *oact, unsigned int sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + sigset32_t set32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset32_t)) + return -EINVAL; + + if (act) { + ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __copy_from_user(&set32, &act->sa_mask, + sizeof(sigset32_t)); + switch (_NSIG_WORDS) { + case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] + | (((long)set32.sig[7]) << 32); + case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] + | (((long)set32.sig[5]) << 32); + case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] + | (((long)set32.sig[3]) << 32); + case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] + | (((long)set32.sig[1]) << 32); + } + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + + if (ret) + return -EFAULT; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + switch (_NSIG_WORDS) { + case 4: + set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); + set32.sig[6] = old_ka.sa.sa_mask.sig[3]; + case 3: + set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); + set32.sig[4] = old_ka.sa.sa_mask.sig[2]; + case 2: + set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); + set32.sig[2] = old_ka.sa.sa_mask.sig[1]; + case 1: + set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); + set32.sig[0] = old_ka.sa.sa_mask.sig[0]; + } + ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __copy_to_user(&oact->sa_mask, &set32, + sizeof(sigset32_t)); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + } + + return ret; +} + + +extern asmlinkage int sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, + size_t sigsetsize); + +asmlinkage int +sys32_rt_sigprocmask(int how, sigset32_t *set, sigset32_t *oset, + unsigned int sigsetsize) +{ + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set) { + if (copy_from_user (&s32, set, sizeof(sigset32_t))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + } + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL, + sigsetsize); + set_fs (old_fs); + if (ret) return ret; + if (oset) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (oset, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return 0; +} + +static inline int +put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) +{ + int err; + + err = put_user (kbuf->f_type, &ubuf->f_type); + err |= __put_user (kbuf->f_bsize, &ubuf->f_bsize); + err |= __put_user (kbuf->f_blocks, &ubuf->f_blocks); + err |= __put_user (kbuf->f_bfree, &ubuf->f_bfree); + err |= __put_user (kbuf->f_bavail, &ubuf->f_bavail); + err |= __put_user (kbuf->f_files, &ubuf->f_files); + err |= __put_user (kbuf->f_ffree, &ubuf->f_ffree); + err |= __put_user (kbuf->f_namelen, &ubuf->f_namelen); + err |= __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]); + err |= __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]); + return err; +} + +extern asmlinkage int sys_statfs(const char * path, struct statfs * buf); + +asmlinkage int +sys32_statfs(const char * path, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_statfs((const char *)path, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf); + +asmlinkage int +sys32_fstatfs(unsigned int fd, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + return ret; +} + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + +static inline long +get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); + return ENOSYS; +} + +static inline long +put_tv32(struct timeval32 *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} + +static inline long +get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) | + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) | + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) | + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); + return ENOSYS; +} + +static inline long +put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, i, sizeof(*i)) || + (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) | + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) | + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec))); + return ENOSYS; +} + +extern int do_getitimer(int which, struct itimerval *value); + +asmlinkage int +sys32_getitimer(int which, struct itimerval32 *it) +{ + struct itimerval kit; + int error; + + error = do_getitimer(which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + return error; +} + +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); + +asmlinkage int +sys32_setitimer(int which, struct itimerval32 *in, struct itimerval32 *out) +{ + struct itimerval kin, kout; + int error; + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer(which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + if (put_it32(out, &kout)) + return -EFAULT; + + return 0; + +} +asmlinkage unsigned long +sys32_alarm(unsigned int seconds) +{ + struct itimerval it_new, it_old; + unsigned int oldalarm; + + it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; + it_new.it_value.tv_sec = seconds; + it_new.it_value.tv_usec = 0; + do_setitimer(ITIMER_REAL, &it_new, &it_old); + oldalarm = it_old.it_value.tv_sec; + /* ehhh.. We can't return 0 if we have an alarm pending.. */ + /* And we'd better return too much than too little anyway */ + if (it_old.it_value.tv_usec) + oldalarm++; + return oldalarm; +} + +/* Translations due to time_t size differences. Which affects all + sorts of things, like timeval and itimerval. */ + +extern struct timezone sys_tz; +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +asmlinkage int +sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) +{ + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + return 0; +} + +asmlinkage int +sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) +{ + struct timeval ktv; + struct timezone ktz; + + if (tv) { + if (get_tv32(&ktv, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + +struct dirent32 { + unsigned int d_ino; + unsigned int d_off; + unsigned short d_reclen; + char d_name[NAME_MAX + 1]; +}; + +static void +xlate_dirent(void *dirent, long n) +{ + long off; + struct dirent *dirp; + struct dirent32 *dirp32; + + off = 0; + while (off < n) { + dirp = (struct dirent *)(dirent + off); + off += dirp->d_reclen; + dirp32 = (struct dirent32 *)dirp; + dirp32->d_ino = dirp->d_ino; + dirp32->d_off = (unsigned int)dirp->d_off; + dirp32->d_reclen = dirp->d_reclen; + strncpy(dirp32->d_name, dirp->d_name, dirp->d_reclen - ((3 * 4) + 2)); + } + return; +} + +asmlinkage long +sys32_getdents(unsigned int fd, void * dirent, unsigned int count) +{ + long n; + + if ((n = sys_getdents(fd, dirent, count)) < 0) + return(n); + xlate_dirent(dirent, n); + return(n); +} + +asmlinkage int +sys32_readdir(unsigned int fd, void * dirent, unsigned int count) +{ + int n; + struct dirent *dirp; + + if ((n = old_readdir(fd, dirent, count)) < 0) + return(n); + dirp = (struct dirent *)dirent; + xlate_dirent(dirent, dirp->d_reclen); + return(n); +} + +/* + * We can actually return ERESTARTSYS instead of EINTR, but I'd + * like to be certain this leads to no problems. So I return + * EINTR just for safety. + * + * Update: ERESTARTSYS breaks at least the xview clock binary, so + * I'm trying ERESTARTNOHAND which restart only when you want to. + */ +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) +#define ROUND_UP(x,y) (((x)+(y)-1)/(y)) + +asmlinkage int +sys32_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval32 *tvp32) +{ + fd_set_bits fds; + char *bits; + long timeout; + int ret, size; + + timeout = MAX_SCHEDULE_TIMEOUT; + if (tvp32) { + time_t sec, usec; + + get_user(sec, &tvp32->tv_sec); + get_user(usec, &tvp32->tv_usec); + + ret = -EINVAL; + if (sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = ROUND_UP(usec, 1000000/HZ); + timeout += sec * (unsigned long) HZ; + } + } + + ret = -EINVAL; + if (n < 0) + goto out_nofds; + + if (n > current->files->max_fdset) + n = current->files->max_fdset; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + if ((ret = get_fd_set(n, inp, fds.in)) || + (ret = get_fd_set(n, outp, fds.out)) || + (ret = get_fd_set(n, exp, fds.ex))) + goto out; + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); + + ret = do_select(n, &fds, &timeout); + + if (tvp32 && !(current->personality & STICKY_TIMEOUTS)) { + time_t sec = 0, usec = 0; + if (timeout) { + sec = timeout / HZ; + usec = timeout % HZ; + usec *= (1000000/HZ); + } + put_user(sec, (int *)&tvp32->tv_sec); + put_user(usec, (int *)&tvp32->tv_usec); + } + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + set_fd_set(n, inp, fds.res_in); + set_fd_set(n, outp, fds.res_out); + set_fd_set(n, exp, fds.res_ex); + +out: + kfree(bits); +out_nofds: + return ret; +} + +struct rusage32 { + struct timeval32 ru_utime; + struct timeval32 ru_stime; + int ru_maxrss; + int ru_ixrss; + int ru_idrss; + int ru_isrss; + int ru_minflt; + int ru_majflt; + int ru_nswap; + int ru_inblock; + int ru_oublock; + int ru_msgsnd; + int ru_msgrcv; + int ru_nsignals; + int ru_nvcsw; + int ru_nivcsw; +}; + +static int +put_rusage (struct rusage32 *ru, struct rusage *r) +{ + int err; + + err = put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec); + err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec); + err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec); + err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec); + err |= __put_user (r->ru_maxrss, &ru->ru_maxrss); + err |= __put_user (r->ru_ixrss, &ru->ru_ixrss); + err |= __put_user (r->ru_idrss, &ru->ru_idrss); + err |= __put_user (r->ru_isrss, &ru->ru_isrss); + err |= __put_user (r->ru_minflt, &ru->ru_minflt); + err |= __put_user (r->ru_majflt, &ru->ru_majflt); + err |= __put_user (r->ru_nswap, &ru->ru_nswap); + err |= __put_user (r->ru_inblock, &ru->ru_inblock); + err |= __put_user (r->ru_oublock, &ru->ru_oublock); + err |= __put_user (r->ru_msgsnd, &ru->ru_msgsnd); + err |= __put_user (r->ru_msgrcv, &ru->ru_msgrcv); + err |= __put_user (r->ru_nsignals, &ru->ru_nsignals); + err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw); + err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw); + return err; +} + +extern asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, + int options, struct rusage * ru); + +asmlinkage int +sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options, + struct rusage32 *ru) +{ + if (!ru) + return sys_wait4(pid, stat_addr, options, NULL); + else { + struct rusage r; + int ret; + unsigned int status; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, stat_addr)) + return -EFAULT; + return ret; + } +} + +asmlinkage int +sys32_waitpid(__kernel_pid_t32 pid, unsigned int *stat_addr, int options) +{ + return sys32_wait4(pid, stat_addr, options, NULL); +} + +struct timespec32 { + int tv_sec; + int tv_nsec; +}; + +extern asmlinkage int sys_nanosleep(struct timespec *rqtp, + struct timespec *rmtp); + +asmlinkage int +sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + if (get_user (t.tv_sec, &rqtp->tv_sec) || + __get_user (t.tv_nsec, &rqtp->tv_nsec)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_nanosleep(&t, rmtp ? &t : NULL); + set_fs (old_fs); + if (rmtp && ret == -EINTR) { + if (__put_user (t.tv_sec, &rmtp->tv_sec) || + __put_user (t.tv_nsec, &rmtp->tv_nsec)) + return -EFAULT; + } + return ret; +} + +struct iovec32 { unsigned int iov_base; int iov_len; }; + +typedef ssize_t (*IO_fn_t)(struct file *, char *, size_t, loff_t *); + +static long +do_readv_writev32(int type, struct file *file, const struct iovec32 *vector, + u32 count) +{ + unsigned long tot_len; + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov=iovstack, *ivp; + struct inode *inode; + long retval, i; + IO_fn_t fn; + + /* First get the "struct iovec" from user memory and + * verify all the pointers + */ + if (!count) + return 0; + if(verify_area(VERIFY_READ, vector, sizeof(struct iovec32)*count)) + return -EFAULT; + if (count > UIO_MAXIOV) + return -EINVAL; + if (count > UIO_FASTIOV) { + iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); + if (!iov) + return -ENOMEM; + } + + tot_len = 0; + i = count; + ivp = iov; + while(i > 0) { + u32 len; + u32 buf; + + __get_user(len, &vector->iov_len); + __get_user(buf, &vector->iov_base); + tot_len += len; + ivp->iov_base = (void *)A(buf); + ivp->iov_len = (__kernel_size_t) len; + vector++; + ivp++; + i--; + } + + inode = file->f_dentry->d_inode; + /* VERIFY_WRITE actually means a read, as we write to user space */ + retval = locks_verify_area((type == VERIFY_WRITE + ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), + inode, file, file->f_pos, tot_len); + if (retval) { + if (iov != iovstack) + kfree(iov); + return retval; + } + + /* Then do the actual IO. Note that sockets need to be handled + * specially as they have atomicity guarantees and can handle + * iovec's natively + */ + if (inode->i_sock) { + int err; + err = sock_readv_writev(type, inode, file, iov, count, tot_len); + if (iov != iovstack) + kfree(iov); + return err; + } + + if (!file->f_op) { + if (iov != iovstack) + kfree(iov); + return -EINVAL; + } + /* VERIFY_WRITE actually means a read, as we write to user space */ + fn = file->f_op->read; + if (type == VERIFY_READ) + fn = (IO_fn_t) file->f_op->write; + ivp = iov; + while (count > 0) { + void * base; + int len, nr; + + base = ivp->iov_base; + len = ivp->iov_len; + ivp++; + count--; + nr = fn(file, base, len, &file->f_pos); + if (nr < 0) { + if (retval) + break; + retval = nr; + break; + } + retval += nr; + if (nr != len) + break; + } + if (iov != iovstack) + kfree(iov); + return retval; +} + +asmlinkage long +sys32_readv(int fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + long ret = -EBADF; + + lock_kernel(); + file = fget(fd); + if(!file) + goto bad_file; + + if(!(file->f_mode & 1)) + goto out; + + ret = do_readv_writev32(VERIFY_WRITE, file, + vector, count); +out: + fput(file); +bad_file: + unlock_kernel(); + return ret; +} + +asmlinkage long +sys32_writev(int fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + int ret = -EBADF; + + lock_kernel(); + file = fget(fd); + if(!file) + goto bad_file; + + if(!(file->f_mode & 2)) + goto out; + + down(&file->f_dentry->d_inode->i_sem); + ret = do_readv_writev32(VERIFY_READ, file, + vector, count); + up(&file->f_dentry->d_inode->i_sem); +out: + fput(file); +bad_file: + unlock_kernel(); + return ret; +} + +#define RLIM_INFINITY32 0x7fffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) + +struct rlimit32 { + int rlim_cur; + int rlim_max; +}; + +extern asmlinkage int sys_getrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage int +sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &r); + set_fs (old_fs); + if (!ret) { + ret = put_user (RESOURCE32(r.rlim_cur), &rlim->rlim_cur); + ret |= __put_user (RESOURCE32(r.rlim_max), &rlim->rlim_max); + } + return ret; +} + +extern asmlinkage int sys_setrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage int +sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + if (resource >= RLIM_NLIMITS) return -EINVAL; + if (get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + if (r.rlim_cur == RLIM_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + if (r.rlim_max == RLIM_INFINITY32) + r.rlim_max = RLIM_INFINITY; + set_fs (KERNEL_DS); + ret = sys_setrlimit(resource, &r); + set_fs (old_fs); + return ret; +} + +/* Argument list sizes for sys_socketcall */ +#define AL(x) ((x) * sizeof(u32)) +static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), + AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; +#undef AL + +extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); +extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, + int addrlen); +extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, + int *upeer_addrlen); +extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); +extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); +extern asmlinkage int sys_send(int fd, void *buff, size_t len, unsigned flags); +extern asmlinkage int sys_sendto(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags, u32 addr, int addr_len); +extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags); +extern asmlinkage int sys_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags, u32 addr, u32 addr_len); +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); +extern asmlinkage int sys_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen); + +extern asmlinkage int sys_socket(int family, int type, int protocol); +extern asmlinkage int sys_socketpair(int family, int type, int protocol, + int usockvec[2]); +extern asmlinkage int sys_shutdown(int fd, int how); +extern asmlinkage int sys_listen(int fd, int backlog); + +asmlinkage int sys32_socketcall(int call, u32 *args) +{ + int i, ret; + u32 a[6]; + u32 a0,a1; + + if (call<SYS_SOCKET||call>SYS_RECVMSG) + return -EINVAL; + if (copy_from_user(a, args, nas[call])) + return -EFAULT; + a0=a[0]; + a1=a[1]; + + switch(call) + { + case SYS_SOCKET: + ret = sys_socket(a0, a1, a[2]); + break; + case SYS_BIND: + ret = sys_bind(a0, (struct sockaddr *)A(a1), a[2]); + break; + case SYS_CONNECT: + ret = sys_connect(a0, (struct sockaddr *)A(a1), a[2]); + break; + case SYS_LISTEN: + ret = sys_listen(a0, a1); + break; + case SYS_ACCEPT: + ret = sys_accept(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_GETSOCKNAME: + ret = sys_getsockname(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_GETPEERNAME: + ret = sys_getpeername(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_SOCKETPAIR: + ret = sys_socketpair(a0, a1, a[2], (int *)A(a[3])); + break; + case SYS_SEND: + ret = sys_send(a0, (void *)A(a1), a[2], a[3]); + break; + case SYS_SENDTO: + ret = sys_sendto(a0, a1, a[2], a[3], a[4], a[5]); + break; + case SYS_RECV: + ret = sys_recv(a0, (void *)A(a1), a[2], a[3]); + break; + case SYS_RECVFROM: + ret = sys_recvfrom(a0, a1, a[2], a[3], a[4], a[5]); + break; + case SYS_SHUTDOWN: + ret = sys_shutdown(a0,a1); + break; + case SYS_SETSOCKOPT: + ret = sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), + a[4]); + break; + case SYS_GETSOCKOPT: + ret = sys_getsockopt(a0, a1, a[2], a[3], a[4]); + break; + case SYS_SENDMSG: + ret = sys32_sendmsg(a0, (struct msghdr32 *)A(a1), + a[2]); + break; + case SYS_RECVMSG: + ret = sys32_recvmsg(a0, (struct msghdr32 *)A(a1), + a[2]); + break; + default: + ret = EINVAL; + break; + } + return ret; +} + +/* + * Declare the IA32 version of the msghdr + */ + +struct msghdr32 { + unsigned int msg_name; /* Socket name */ + int msg_namelen; /* Length of name */ + unsigned int msg_iov; /* Data blocks */ + unsigned int msg_iovlen; /* Number of blocks */ + unsigned int msg_control; /* Per protocol magic (eg BSD file descriptor passing) */ + unsigned int msg_controllen; /* Length of cmsg list */ + unsigned msg_flags; +}; + +static inline int +shape_msg(struct msghdr *mp, struct msghdr32 *mp32) +{ + unsigned int i; + + if (!access_ok(VERIFY_READ, mp32, sizeof(*mp32))) + return(-EFAULT); + __get_user(i, &mp32->msg_name); + mp->msg_name = (void *)i; + __get_user(mp->msg_namelen, &mp32->msg_namelen); + __get_user(i, &mp32->msg_iov); + mp->msg_iov = (struct iov *)i; + __get_user(mp->msg_iovlen, &mp32->msg_iovlen); + __get_user(i, &mp32->msg_control); + mp->msg_control = (void *)i; + __get_user(mp->msg_controllen, &mp32->msg_controllen); + __get_user(mp->msg_flags, &mp32->msg_flags); + return(0); +} + +/* + * Verify & re-shape IA32 iovec. The caller must ensure that the + * iovec is big enough to hold the re-shaped message iovec. + * + * Save time not doing verify_area. copy_*_user will make this work + * in any case. + * + * Don't need to check the total size for overflow (cf net/core/iovec.c), + * 32-bit sizes can't overflow a 64-bit count. + */ + +static inline int +verify_iovec32(struct msghdr *m, struct iovec *iov, char *address, int mode) +{ + int size, err, ct; + struct iovec32 *iov32; + + if(m->msg_namelen) + { + if(mode==VERIFY_READ) + { + err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address); + if(err<0) + goto out; + } + + m->msg_name = address; + } else + m->msg_name = NULL; + + err = -EFAULT; + size = m->msg_iovlen * sizeof(struct iovec32); + if (copy_from_user(iov, m->msg_iov, size)) + goto out; + m->msg_iov=iov; + + err = 0; + iov32 = (struct iovec32 *)iov; + for (ct = m->msg_iovlen; ct-- > 0; ) { + iov[ct].iov_len = (__kernel_size_t)iov32[ct].iov_len; + iov[ct].iov_base = (void *)iov32[ct].iov_base; + err += iov[ct].iov_len; + } +out: + return err; +} + +extern __inline__ void +sockfd_put(struct socket *sock) +{ + fput(sock->file); +} + +/* XXX This really belongs in some header file... -DaveM */ +#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - + 16 for IP, 16 for IPX, + 24 for IPv6, + about 80 for AX.25 */ + +extern struct socket *sockfd_lookup(int fd, int *err); + +/* + * BSD sendmsg interface + */ + +asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *msg, unsigned flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; /* 20 is size of ipv6_pktinfo */ + unsigned char *ctl_buf = ctl; + struct msghdr msg_sys; + int err, ctl_len, iov_size, total_len; + + err = -EFAULT; + if (shape_msg(&msg_sys, msg)) + goto out; + + sock = sockfd_lookup(fd, &err); + if (!sock) + goto out; + + /* do not move before msg_sys is valid */ + err = -EINVAL; + if (msg_sys.msg_iovlen > UIO_MAXIOV) + goto out_put; + + /* Check whether to allocate the iovec area*/ + err = -ENOMEM; + iov_size = msg_sys.msg_iovlen * sizeof(struct iovec32); + if (msg_sys.msg_iovlen > UIO_FASTIOV) { + iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); + if (!iov) + goto out_put; + } + + /* This will also move the address data into kernel space */ + err = verify_iovec32(&msg_sys, iov, address, VERIFY_READ); + if (err < 0) + goto out_freeiov; + total_len = err; + + err = -ENOBUFS; + + if (msg_sys.msg_controllen > INT_MAX) + goto out_freeiov; + ctl_len = msg_sys.msg_controllen; + if (ctl_len) + { + if (ctl_len > sizeof(ctl)) + { + err = -ENOBUFS; + ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL); + if (ctl_buf == NULL) + goto out_freeiov; + } + err = -EFAULT; + if (copy_from_user(ctl_buf, msg_sys.msg_control, ctl_len)) + goto out_freectl; + msg_sys.msg_control = ctl_buf; + } + msg_sys.msg_flags = flags; + + if (sock->file->f_flags & O_NONBLOCK) + msg_sys.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &msg_sys, total_len); + +out_freectl: + if (ctl_buf != ctl) + sock_kfree_s(sock->sk, ctl_buf, ctl_len); +out_freeiov: + if (iov != iovstack) + sock_kfree_s(sock->sk, iov, iov_size); +out_put: + sockfd_put(sock); +out: + return err; +} + +/* + * BSD recvmsg interface + */ + +asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *msg, unsigned int flags) +{ + struct socket *sock; + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov=iovstack; + struct msghdr msg_sys; + unsigned long cmsg_ptr; + int err, iov_size, total_len, len; + + /* kernel mode address */ + char addr[MAX_SOCK_ADDR]; + + /* user mode address pointers */ + struct sockaddr *uaddr; + int *uaddr_len; + + err=-EFAULT; + if (shape_msg(&msg_sys, msg)) + goto out; + + sock = sockfd_lookup(fd, &err); + if (!sock) + goto out; + + err = -EINVAL; + if (msg_sys.msg_iovlen > UIO_MAXIOV) + goto out_put; + + /* Check whether to allocate the iovec area*/ + err = -ENOMEM; + iov_size = msg_sys.msg_iovlen * sizeof(struct iovec); + if (msg_sys.msg_iovlen > UIO_FASTIOV) { + iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); + if (!iov) + goto out_put; + } + + /* + * Save the user-mode address (verify_iovec will change the + * kernel msghdr to use the kernel address space) + */ + + uaddr = msg_sys.msg_name; + uaddr_len = &msg->msg_namelen; + err = verify_iovec32(&msg_sys, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out_freeiov; + total_len=err; + + cmsg_ptr = (unsigned long)msg_sys.msg_control; + msg_sys.msg_flags = 0; + + if (sock->file->f_flags & O_NONBLOCK) + flags |= MSG_DONTWAIT; + err = sock_recvmsg(sock, &msg_sys, total_len, flags); + if (err < 0) + goto out_freeiov; + len = err; + + if (uaddr != NULL) { + err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len); + if (err < 0) + goto out_freeiov; + } + err = __put_user(msg_sys.msg_flags, &msg->msg_flags); + if (err) + goto out_freeiov; + err = __put_user((unsigned long)msg_sys.msg_control-cmsg_ptr, + &msg->msg_controllen); + if (err) + goto out_freeiov; + err = len; + +out_freeiov: + if (iov != iovstack) + sock_kfree_s(sock->sk, iov, iov_size); +out_put: + sockfd_put(sock); +out: + return err; +} + +/* + * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.. + * + * This is really horribly ugly. + */ + +struct msgbuf32 { s32 mtype; char mtext[1]; }; + +struct ipc_perm32 +{ + key_t key; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_uid_t32 cuid; + __kernel_gid_t32 cgid; + __kernel_mode_t32 mode; + unsigned short seq; +}; + +struct semid_ds32 { + struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t32 sem_otime; /* last semop time */ + __kernel_time_t32 sem_ctime; /* last change time */ + u32 sem_base; /* ptr to first semaphore in array */ + u32 sem_pending; /* pending operations to be processed */ + u32 sem_pending_last; /* last pending operation */ + u32 undo; /* undo requests on this array */ + unsigned short sem_nsems; /* no. of semaphores in array */ +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; +}; + +#define IPCOP_MASK(__x) (1UL << (__x)) + +static int +do_sys32_semctl(int first, int second, int third, void *uptr) +{ + union semun fourth; + u32 pad; + int err = -EINVAL; + + if (!uptr) + goto out; + err = -EFAULT; + if (get_user (pad, (u32 *)uptr)) + goto out; + if(third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); + if (IPCOP_MASK (third) & + (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) | + IPCOP_MASK (GETVAL) | IPCOP_MASK (GETPID) | + IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) | + IPCOP_MASK (GETALL) | IPCOP_MASK (SETALL) | + IPCOP_MASK (IPC_RMID))) { + err = sys_semctl (first, second, third, fourth); + } else { + struct semid_ds s; + struct semid_ds32 *usp = (struct semid_ds32 *)A(pad); + mm_segment_t old_fs; + int need_back_translation; + + if (third == IPC_SET) { + err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user(s.sem_perm.gid, &usp->sem_perm.gid); + err |= __get_user(s.sem_perm.mode, &usp->sem_perm.mode); + if (err) + goto out; + fourth.__pad = &s; + } + need_back_translation = + (IPCOP_MASK (third) & + (IPCOP_MASK (SEM_STAT) | IPCOP_MASK (IPC_STAT))) != 0; + if (need_back_translation) + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + if (need_back_translation) { + int err2 = put_user(s.sem_perm.key, &usp->sem_perm.key); + err2 |= __put_user(s.sem_perm.uid, &usp->sem_perm.uid); + err2 |= __put_user(s.sem_perm.gid, &usp->sem_perm.gid); + err2 |= __put_user(s.sem_perm.cuid, + &usp->sem_perm.cuid); + err2 |= __put_user (s.sem_perm.cgid, + &usp->sem_perm.cgid); + err2 |= __put_user (s.sem_perm.mode, + &usp->sem_perm.mode); + err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq); + err2 |= __put_user (s.sem_otime, &usp->sem_otime); + err2 |= __put_user (s.sem_ctime, &usp->sem_ctime); + err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); + if (err2) err = -EFAULT; + } + } +out: + return err; +} + +static int +do_sys32_msgsnd (int first, int second, int third, void *uptr) +{ + struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + + 4, GFP_USER); + struct msgbuf32 *up = (struct msgbuf32 *)uptr; + mm_segment_t old_fs; + int err; + + if (!p) + return -ENOMEM; + err = get_user (p->mtype, &up->mtype); + err |= __copy_from_user (p->mtext, &up->mtext, second); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgsnd (first, p, second, third); + set_fs (old_fs); +out: + kfree (p); + return err; +} + +static int +do_sys32_msgrcv (int first, int second, int msgtyp, int third, + int version, void *uptr) +{ + struct msgbuf32 *up; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (!version) { + struct ipc_kludge *uipck = (struct ipc_kludge *)uptr; + struct ipc_kludge ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge))) + goto out; + uptr = (void *)A(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + if (!p) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgrcv (first, p, second + 4, msgtyp, third); + set_fs (old_fs); + if (err < 0) + goto free_then_out; + up = (struct msgbuf32 *)uptr; + if (put_user (p->mtype, &up->mtype) || + __copy_to_user (&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree (p); +out: + return err; +} + +static int +do_sys32_msgctl (int first, int second, void *uptr) +{ + int err; + + if (IPCOP_MASK (second) & + (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (MSG_INFO) | + IPCOP_MASK (IPC_RMID))) { + err = sys_msgctl (first, second, (struct msqid_ds *)uptr); + } else { + struct msqid_ds m; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + mm_segment_t old_fs; + + if (second == IPC_SET) { + err = get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + goto out; + } + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, &m); + set_fs (old_fs); + if (IPCOP_MASK (second) & + (IPCOP_MASK (MSG_STAT) | IPCOP_MASK (IPC_STAT))) { + int err2 = put_user (m.msg_perm.key, &up->msg_perm.key); + err2 |= __put_user(m.msg_perm.uid, &up->msg_perm.uid); + err2 |= __put_user(m.msg_perm.gid, &up->msg_perm.gid); + err2 |= __put_user(m.msg_perm.cuid, &up->msg_perm.cuid); + err2 |= __put_user(m.msg_perm.cgid, &up->msg_perm.cgid); + err2 |= __put_user(m.msg_perm.mode, &up->msg_perm.mode); + err2 |= __put_user(m.msg_perm.seq, &up->msg_perm.seq); + err2 |= __put_user(m.msg_stime, &up->msg_stime); + err2 |= __put_user(m.msg_rtime, &up->msg_rtime); + err2 |= __put_user(m.msg_ctime, &up->msg_ctime); + err2 |= __put_user(m.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user(m.msg_qnum, &up->msg_qnum); + err2 |= __put_user(m.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user(m.msg_lspid, &up->msg_lspid); + err2 |= __put_user(m.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + } + } + +out: + return err; +} + +static int +do_sys32_shmat (int first, int second, int third, int version, void *uptr) +{ + unsigned long raddr; + u32 *uaddr = (u32 *)A((u32)third); + int err = -EINVAL; + + if (version == 1) + goto out; + err = sys_shmat (first, uptr, second, &raddr); + if (err) + goto out; + err = put_user (raddr, uaddr); +out: + return err; +} + +static int +do_sys32_shmctl (int first, int second, void *uptr) +{ + int err; + + if (IPCOP_MASK (second) & + (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SHM_LOCK) + | IPCOP_MASK (SHM_UNLOCK) | IPCOP_MASK (IPC_RMID))) { + err = sys_shmctl (first, second, (struct shmid_ds *)uptr); + } else { + struct shmid_ds s; + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + mm_segment_t old_fs; + + if (second == IPC_SET) { + err = get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + goto out; + } + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, &s); + set_fs (old_fs); + if (err < 0) + goto out; + + /* Mask it even in this case so it becomes a CSE. */ + if (second == SHM_INFO) { + struct shm_info32 { + int used_ids; + u32 shm_tot, shm_rss, shm_swp; + u32 swap_attempts, swap_successes; + } *uip = (struct shm_info32 *)uptr; + struct shm_info *kp = (struct shm_info *)&s; + int err2 = put_user (kp->used_ids, &uip->used_ids); + err2 |= __put_user (kp->shm_tot, &uip->shm_tot); + err2 |= __put_user (kp->shm_rss, &uip->shm_rss); + err2 |= __put_user (kp->shm_swp, &uip->shm_swp); + err2 |= __put_user (kp->swap_attempts, + &uip->swap_attempts); + err2 |= __put_user (kp->swap_successes, + &uip->swap_successes); + if (err2) + err = -EFAULT; + } else if (IPCOP_MASK (second) & + (IPCOP_MASK (SHM_STAT) | IPCOP_MASK (IPC_STAT))) { + int err2 = put_user (s.shm_perm.key, &up->shm_perm.key); + err2 |= __put_user (s.shm_perm.uid, &up->shm_perm.uid); + err2 |= __put_user (s.shm_perm.gid, &up->shm_perm.gid); + err2 |= __put_user (s.shm_perm.cuid, + &up->shm_perm.cuid); + err2 |= __put_user (s.shm_perm.cgid, + &up->shm_perm.cgid); + err2 |= __put_user (s.shm_perm.mode, + &up->shm_perm.mode); + err2 |= __put_user (s.shm_perm.seq, &up->shm_perm.seq); + err2 |= __put_user (s.shm_atime, &up->shm_atime); + err2 |= __put_user (s.shm_dtime, &up->shm_dtime); + err2 |= __put_user (s.shm_ctime, &up->shm_ctime); + err2 |= __put_user (s.shm_segsz, &up->shm_segsz); + err2 |= __put_user (s.shm_nattch, &up->shm_nattch); + err2 |= __put_user (s.shm_cpid, &up->shm_cpid); + err2 |= __put_user (s.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + } + } +out: + return err; +} + +asmlinkage int +sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) +{ + int version, err; + + lock_kernel(); + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + if (call <= SEMCTL) + switch (call) { + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + err = sys_semop (first, (struct sembuf *)AA(ptr), + second); + goto out; + case SEMGET: + err = sys_semget (first, second, third); + goto out; + case SEMCTL: + err = do_sys32_semctl (first, second, third, + (void *)AA(ptr)); + goto out; + default: + err = -EINVAL; + goto out; + }; + if (call <= MSGCTL) + switch (call) { + case MSGSND: + err = do_sys32_msgsnd (first, second, third, + (void *)AA(ptr)); + goto out; + case MSGRCV: + err = do_sys32_msgrcv (first, second, fifth, third, + version, (void *)AA(ptr)); + goto out; + case MSGGET: + err = sys_msgget ((key_t) first, second); + goto out; + case MSGCTL: + err = do_sys32_msgctl (first, second, (void *)AA(ptr)); + goto out; + default: + err = -EINVAL; + goto out; + } + if (call <= SHMCTL) + switch (call) { + case SHMAT: + err = do_sys32_shmat (first, second, third, + version, (void *)AA(ptr)); + goto out; + case SHMDT: + err = sys_shmdt ((char *)AA(ptr)); + goto out; + case SHMGET: + err = sys_shmget (first, second, third); + goto out; + case SHMCTL: + err = do_sys32_shmctl (first, second, (void *)AA(ptr)); + goto out; + default: + err = -EINVAL; + goto out; + } + + err = -EINVAL; + +out: + unlock_kernel(); + return err; +} + +#ifdef NOTYET /* UNTESTED FOR IA64 FROM HERE DOWN */ + +/* In order to reduce some races, while at the same time doing additional + * checking and hopefully speeding things up, we copy filenames to the + * kernel data space before using them.. + * + * POSIX.1 2.4: an empty pathname is invalid (ENOENT). + */ +static inline int +do_getname32(const char *filename, char *page) +{ + int retval; + + /* 32bit pointer will be always far below TASK_SIZE :)) */ + retval = strncpy_from_user((char *)page, (char *)filename, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) + return 0; + return -ENAMETOOLONG; + } else if (!retval) + retval = -ENOENT; + return retval; +} + +char * +getname32(const char *filename) +{ + char *tmp, *result; + + result = ERR_PTR(-ENOMEM); + tmp = (char *)__get_free_page(GFP_KERNEL); + if (tmp) { + int retval = do_getname32(filename, tmp); + + result = tmp; + if (retval < 0) { + putname(tmp); + result = ERR_PTR(retval); + } + } + return result; +} + +/* 32-bit timeval and related flotsam. */ + +extern asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); + +asmlinkage int +sys32_ioperm(u32 from, u32 num, int on) +{ + return sys_ioperm((unsigned long)from, (unsigned long)num, on); +} + +static inline int +get_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = get_user(kfl->l_type, &ufl->l_type); + err |= __get_user(kfl->l_whence, &ufl->l_whence); + err |= __get_user(kfl->l_start, &ufl->l_start); + err |= __get_user(kfl->l_len, &ufl->l_len); + err |= __get_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +static inline int +put_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = __put_user(kfl->l_type, &ufl->l_type); + err |= __put_user(kfl->l_whence, &ufl->l_whence); + err |= __put_user(kfl->l_start, &ufl->l_start); + err |= __put_user(kfl->l_len, &ufl->l_len); + err |= __put_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, + unsigned long arg); + +asmlinkage long +sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case F_GETLK: + case F_SETLK: + case F_SETLKW: + { + struct flock f; + mm_segment_t old_fs; + long ret; + + if(get_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + old_fs = get_fs(); set_fs (KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs (old_fs); + if(put_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + return ret; + } + default: + return sys_fcntl(fd, cmd, (unsigned long)arg); + } +} + +struct dqblk32 { + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u32 dqb_curblocks; + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; + +extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, + caddr_t addr); + +asmlinkage int +sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) +{ + int cmds = cmd >> SUBCMDSHIFT; + int err; + struct dqblk d; + mm_segment_t old_fs; + char *spec; + + switch (cmds) { + case Q_GETQUOTA: + break; + case Q_SETQUOTA: + case Q_SETUSE: + case Q_SETQLIM: + if (copy_from_user (&d, (struct dqblk32 *)addr, + sizeof (struct dqblk32))) + return -EFAULT; + d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + break; + default: + return sys_quotactl(cmd, special, + id, (caddr_t)addr); + } + spec = getname32 (special); + err = PTR_ERR(spec); + if (IS_ERR(spec)) return err; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + set_fs (old_fs); + putname (spec); + if (cmds == Q_GETQUOTA) { + __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; + ((struct dqblk32 *)&d)->dqb_itime = i; + ((struct dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct dqblk32 *)addr, &d, + sizeof (struct dqblk32))) + return -EFAULT; + } + return err; +} + +extern asmlinkage int sys_utime(char * filename, struct utimbuf * times); + +struct utimbuf32 { + __kernel_time_t32 actime, modtime; +}; + +asmlinkage int +sys32_utime(char * filename, struct utimbuf32 *times) +{ + struct utimbuf t; + mm_segment_t old_fs; + int ret; + char *filenam; + + if (!times) + return sys_utime(filename, NULL); + if (get_user (t.actime, ×->actime) || + __get_user (t.modtime, ×->modtime)) + return -EFAULT; + filenam = getname32 (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + old_fs = get_fs(); + set_fs (KERNEL_DS); + ret = sys_utime(filenam, &t); + set_fs (old_fs); + putname (filenam); + } + return ret; +} + +/* + * Ooo, nasty. We need here to frob 32-bit unsigned longs to + * 64-bit unsigned longs. + */ + +static inline int +get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) +{ + if (ufdset) { + unsigned long odd; + + if (verify_area(VERIFY_WRITE, ufdset, n*sizeof(u32))) + return -EFAULT; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + __get_user(l, ufdset); + __get_user(h, ufdset+1); + ufdset += 2; + *fdset++ = h << 32 | l; + n -= 2; + } + if (odd) + __get_user(*fdset, ufdset); + } else { + /* Tricky, must clear full unsigned long in the + * kernel fdset at the end, this makes sure that + * actually happens. + */ + memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32)); + } + return 0; +} + +static inline void +set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) +{ + unsigned long odd; + + if (!ufdset) + return; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + l = *fdset++; + h = l >> 32; + __put_user(l, ufdset); + __put_user(h, ufdset+1); + ufdset += 2; + n -= 2; + } + if (odd) + __put_user(*fdset, ufdset); +} + +extern asmlinkage int sys_sysfs(int option, unsigned long arg1, + unsigned long arg2); + +asmlinkage int +sys32_sysfs(int option, u32 arg1, u32 arg2) +{ + return sys_sysfs(option, arg1, arg2); +} + +struct ncp_mount_data32 { + int version; + unsigned int ncp_fd; + __kernel_uid_t32 mounted_uid; + __kernel_pid_t32 wdog_pid; + unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; + unsigned int time_out; + unsigned int retry_count; + unsigned int flags; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void * +do_ncp_super_data_conv(void *raw_data) +{ + struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data; + struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data; + + n->dir_mode = n32->dir_mode; + n->file_mode = n32->file_mode; + n->gid = n32->gid; + n->uid = n32->uid; + memmove (n->mounted_vol, n32->mounted_vol, + (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int))); + n->wdog_pid = n32->wdog_pid; + n->mounted_uid = n32->mounted_uid; + return raw_data; +} + +struct smb_mount_data32 { + int version; + __kernel_uid_t32 mounted_uid; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void * +do_smb_super_data_conv(void *raw_data) +{ + struct smb_mount_data *s = (struct smb_mount_data *)raw_data; + struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data; + + s->version = s32->version; + s->mounted_uid = s32->mounted_uid; + s->uid = s32->uid; + s->gid = s32->gid; + s->file_mode = s32->file_mode; + s->dir_mode = s32->dir_mode; + return raw_data; +} + +static int +copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel) +{ + int i; + unsigned long page; + struct vm_area_struct *vma; + + *kernel = 0; + if(!user) + return 0; + vma = find_vma(current->mm, (unsigned long)user); + if(!vma || (unsigned long)user < vma->vm_start) + return -EFAULT; + if(!(vma->vm_flags & VM_READ)) + return -EFAULT; + i = vma->vm_end - (unsigned long) user; + if(PAGE_SIZE <= (unsigned long) i) + i = PAGE_SIZE - 1; + if(!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + if(copy_from_user((void *) page, user, i)) { + free_page(page); + return -EFAULT; + } + *kernel = page; + return 0; +} + +extern asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, + unsigned long new_flags, void *data); + +#define SMBFS_NAME "smbfs" +#define NCPFS_NAME "ncpfs" + +asmlinkage int +sys32_mount(char *dev_name, char *dir_name, char *type, + unsigned long new_flags, u32 data) +{ + unsigned long type_page; + int err, is_smb, is_ncp; + + if(!capable(CAP_SYS_ADMIN)) + return -EPERM; + is_smb = is_ncp = 0; + err = copy_mount_stuff_to_kernel((const void *)type, &type_page); + if(err) + return err; + if(type_page) { + is_smb = !strcmp((char *)type_page, SMBFS_NAME); + is_ncp = !strcmp((char *)type_page, NCPFS_NAME); + } + if(!is_smb && !is_ncp) { + if(type_page) + free_page(type_page); + return sys_mount(dev_name, dir_name, type, new_flags, + (void *)AA(data)); + } else { + unsigned long dev_page, dir_page, data_page; + mm_segment_t old_fs; + + err = copy_mount_stuff_to_kernel((const void *)dev_name, + &dev_page); + if(err) + goto out; + err = copy_mount_stuff_to_kernel((const void *)dir_name, + &dir_page); + if(err) + goto dev_out; + err = copy_mount_stuff_to_kernel((const void *)AA(data), + &data_page); + if(err) + goto dir_out; + if(is_ncp) + do_ncp_super_data_conv((void *)data_page); + else if(is_smb) + do_smb_super_data_conv((void *)data_page); + else + panic("The problem is here..."); + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_mount((char *)dev_page, (char *)dir_page, + (char *)type_page, new_flags, + (void *)data_page); + set_fs(old_fs); + + if(data_page) + free_page(data_page); + dir_out: + if(dir_page) + free_page(dir_page); + dev_out: + if(dev_page) + free_page(dev_page); + out: + if(type_page) + free_page(type_page); + return err; + } +} + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + unsigned short procs; + char _f[22]; +}; + +extern asmlinkage int sys_sysinfo(struct sysinfo *info); + +asmlinkage int +sys32_sysinfo(struct sysinfo32 *info) +{ + struct sysinfo s; + int ret, err; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sysinfo(&s); + set_fs (old_fs); + err = put_user (s.uptime, &info->uptime); + err |= __put_user (s.loads[0], &info->loads[0]); + err |= __put_user (s.loads[1], &info->loads[1]); + err |= __put_user (s.loads[2], &info->loads[2]); + err |= __put_user (s.totalram, &info->totalram); + err |= __put_user (s.freeram, &info->freeram); + err |= __put_user (s.sharedram, &info->sharedram); + err |= __put_user (s.bufferram, &info->bufferram); + err |= __put_user (s.totalswap, &info->totalswap); + err |= __put_user (s.freeswap, &info->freeswap); + err |= __put_user (s.procs, &info->procs); + if (err) + return -EFAULT; + return ret; +} + +extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, + struct timespec *interval); + +asmlinkage int +sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sched_rr_get_interval(pid, &t); + set_fs (old_fs); + if (put_user (t.tv_sec, &interval->tv_sec) || + __put_user (t.tv_nsec, &interval->tv_nsec)) + return -EFAULT; + return ret; +} + +extern asmlinkage int sys_sigprocmask(int how, old_sigset_t *set, + old_sigset_t *oset); + +asmlinkage int +sys32_sigprocmask(int how, old_sigset_t32 *set, old_sigset_t32 *oset) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set && get_user (s, set)) return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL); + set_fs (old_fs); + if (ret) return ret; + if (oset && put_user (s, oset)) return -EFAULT; + return 0; +} + +extern asmlinkage int sys_sigpending(old_sigset_t *set); + +asmlinkage int +sys32_sigpending(old_sigset_t32 *set) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_sigpending(&s); + set_fs (old_fs); + if (put_user (s, set)) return -EFAULT; + return ret; +} + +extern asmlinkage int sys_rt_sigpending(sigset_t *set, size_t sigsetsize); + +asmlinkage int +sys32_rt_sigpending(sigset_t32 *set, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset_t32 s32; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&s, sigsetsize); + set_fs (old_fs); + if (!ret) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (set, &s32, sizeof(sigset_t32))) + return -EFAULT; + } + return ret; +} + +siginfo_t32 * +siginfo64to32(siginfo_t32 *d, siginfo_t *s) +{ + memset (&d, 0, sizeof(siginfo_t32)); + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + /* XXX: Ouch, how to find this out??? */ + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (long)(s->si_addr); + /* XXX: Do we need to translate this from ia64 to ia32 traps? */ + d->si_trapno = s->si_trapno; + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +siginfo_t * +siginfo32to64(siginfo_t *d, siginfo_t32 *s) +{ + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + /* XXX: Ouch, how to find this out??? */ + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (void *)A(s->si_addr); + /* XXX: Do we need to translate this from ia32 to ia64 traps? */ + d->si_trapno = s->si_trapno; + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +extern asmlinkage int +sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, + const struct timespec *uts, size_t sigsetsize); + +asmlinkage int +sys32_rt_sigtimedwait(sigset_t32 *uthese, siginfo_t32 *uinfo, + struct timespec32 *uts, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset_t32 s32; + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs(); + siginfo_t info; + siginfo_t32 info32; + + if (copy_from_user (&s32, uthese, sizeof(sigset_t32))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + if (uts) { + ret = get_user (t.tv_sec, &uts->tv_sec); + ret |= __get_user (t.tv_nsec, &uts->tv_nsec); + if (ret) + return -EFAULT; + } + set_fs (KERNEL_DS); + ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize); + set_fs (old_fs); + if (ret >= 0 && uinfo) { + if (copy_to_user (uinfo, siginfo64to32(&info32, &info), + sizeof(siginfo_t32))) + return -EFAULT; + } + return ret; +} + +extern asmlinkage int +sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); + +asmlinkage int +sys32_rt_sigqueueinfo(int pid, int sig, siginfo_t32 *uinfo) +{ + siginfo_t info; + siginfo_t32 info32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info32, uinfo, sizeof(siginfo_t32))) + return -EFAULT; + /* XXX: Is this correct? */ + siginfo32to64(&info, &info32); + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo(pid, sig, &info); + set_fs (old_fs); + return ret; +} + +extern asmlinkage int sys_setreuid(uid_t ruid, uid_t euid); + +asmlinkage int sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid) +{ + uid_t sruid, seuid; + + sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid); + seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid); + return sys_setreuid(sruid, seuid); +} + +extern asmlinkage int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid); + +asmlinkage int +sys32_setresuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid, + __kernel_uid_t32 suid) +{ + uid_t sruid, seuid, ssuid; + + sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid); + seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid); + ssuid = (suid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)suid); + return sys_setresuid(sruid, seuid, ssuid); +} + +extern asmlinkage int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); + +asmlinkage int +sys32_getresuid(__kernel_uid_t32 *ruid, __kernel_uid_t32 *euid, + __kernel_uid_t32 *suid) +{ + uid_t a, b, c; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getresuid(&a, &b, &c); + set_fs (old_fs); + if (put_user (a, ruid) || put_user (b, euid) || put_user (c, suid)) + return -EFAULT; + return ret; +} + +extern asmlinkage int sys_setregid(gid_t rgid, gid_t egid); + +asmlinkage int +sys32_setregid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid) +{ + gid_t srgid, segid; + + srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid); + segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid); + return sys_setregid(srgid, segid); +} + +extern asmlinkage int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid); + +asmlinkage int +sys32_setresgid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid, + __kernel_gid_t32 sgid) +{ + gid_t srgid, segid, ssgid; + + srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid); + segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid); + ssgid = (sgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)sgid); + return sys_setresgid(srgid, segid, ssgid); +} + +extern asmlinkage int sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); + +asmlinkage int +sys32_getresgid(__kernel_gid_t32 *rgid, __kernel_gid_t32 *egid, + __kernel_gid_t32 *sgid) +{ + gid_t a, b, c; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getresgid(&a, &b, &c); + set_fs (old_fs); + if (!ret) { + ret = put_user (a, rgid); + ret |= put_user (b, egid); + ret |= put_user (c, sgid); + } + return ret; +} + +struct tms32 { + __kernel_clock_t32 tms_utime; + __kernel_clock_t32 tms_stime; + __kernel_clock_t32 tms_cutime; + __kernel_clock_t32 tms_cstime; +}; + +extern asmlinkage long sys_times(struct tms * tbuf); + +asmlinkage long +sys32_times(struct tms32 *tbuf) +{ + struct tms t; + long ret; + mm_segment_t old_fs = get_fs (); + int err; + + set_fs (KERNEL_DS); + ret = sys_times(tbuf ? &t : NULL); + set_fs (old_fs); + if (tbuf) { + err = put_user (t.tms_utime, &tbuf->tms_utime); + err |= __put_user (t.tms_stime, &tbuf->tms_stime); + err |= __put_user (t.tms_cutime, &tbuf->tms_cutime); + err |= __put_user (t.tms_cstime, &tbuf->tms_cstime); + if (err) + ret = -EFAULT; + } + return ret; +} + +extern asmlinkage int sys_getgroups(int gidsetsize, gid_t *grouplist); + +asmlinkage int +sys32_getgroups(int gidsetsize, __kernel_gid_t32 *grouplist) +{ + gid_t gl[NGROUPS]; + int ret, i; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_getgroups(gidsetsize, gl); + set_fs (old_fs); + if (gidsetsize && ret > 0 && ret <= NGROUPS) + for (i = 0; i < ret; i++, grouplist++) + if (__put_user (gl[i], grouplist)) + return -EFAULT; + return ret; +} + +extern asmlinkage int sys_setgroups(int gidsetsize, gid_t *grouplist); + +asmlinkage int +sys32_setgroups(int gidsetsize, __kernel_gid_t32 *grouplist) +{ + gid_t gl[NGROUPS]; + int ret, i; + mm_segment_t old_fs = get_fs (); + + if ((unsigned) gidsetsize > NGROUPS) + return -EINVAL; + for (i = 0; i < gidsetsize; i++, grouplist++) + if (__get_user (gl[i], grouplist)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_setgroups(gidsetsize, gl); + set_fs (old_fs); + return ret; +} + +extern asmlinkage int +sys_getrusage(int who, struct rusage *ru); + +asmlinkage int +sys32_getrusage(int who, struct rusage32 *ru) +{ + struct rusage r; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getrusage(who, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + return ret; +} + + +/* XXX These as well... */ +extern __inline__ struct socket * +socki_lookup(struct inode *inode) +{ + return &inode->u.socket_i; +} + +extern __inline__ struct socket * +sockfd_lookup(int fd, int *err) +{ + struct file *file; + struct inode *inode; + + if (!(file = fget(fd))) + { + *err = -EBADF; + return NULL; + } + + inode = file->f_dentry->d_inode; + if (!inode || !inode->i_sock || !socki_lookup(inode)) + { + *err = -ENOTSOCK; + fput(file); + return NULL; + } + + return socki_lookup(inode); +} + +struct msghdr32 { + u32 msg_name; + int msg_namelen; + u32 msg_iov; + __kernel_size_t32 msg_iovlen; + u32 msg_control; + __kernel_size_t32 msg_controllen; + unsigned msg_flags; +}; + +struct cmsghdr32 { + __kernel_size_t32 cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +/* Bleech... */ +#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) \ + __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen)) +#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) \ + cmsg32_nxthdr((mhdr), (cmsg), (cmsglen)) + +#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) ) + +#define CMSG32_DATA(cmsg) \ + ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32)))) +#define CMSG32_SPACE(len) \ + (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len)) +#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len)) + +#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ + (struct cmsghdr32 *)(ctl) : \ + (struct cmsghdr32 *)NULL) +#define CMSG32_FIRSTHDR(msg) \ + __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) + +__inline__ struct cmsghdr32 * +__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + struct cmsghdr32 * __ptr; + + __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + + CMSG32_ALIGN(__cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return NULL; + + return __ptr; +} + +__inline__ struct cmsghdr32 * +cmsg32_nxthdr (struct msghdr *__msg, struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, + __cmsg, __cmsg_len); +} + +static inline int +iov_from_user32_to_kern(struct iovec *kiov, struct iovec32 *uiov32, int niov) +{ + int tot_len = 0; + + while(niov > 0) { + u32 len, buf; + + if(get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) { + tot_len = -EFAULT; + break; + } + tot_len += len; + kiov->iov_base = (void *)A(buf); + kiov->iov_len = (__kernel_size_t) len; + uiov32++; + kiov++; + niov--; + } + return tot_len; +} + +static inline int +msghdr_from_user32_to_kern(struct msghdr *kmsg, struct msghdr32 *umsg) +{ + u32 tmp1, tmp2, tmp3; + int err; + + err = get_user(tmp1, &umsg->msg_name); + err |= __get_user(tmp2, &umsg->msg_iov); + err |= __get_user(tmp3, &umsg->msg_control); + if (err) + return -EFAULT; + + kmsg->msg_name = (void *)A(tmp1); + kmsg->msg_iov = (struct iovec *)A(tmp2); + kmsg->msg_control = (void *)A(tmp3); + + err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); + err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); + err |= get_user(kmsg->msg_flags, &umsg->msg_flags); + + return err; +} + +/* I've named the args so it is easy to tell whose space the pointers are in. */ +static int +verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, + char *kern_address, int mode) +{ + int tot_len; + + if(kern_msg->msg_namelen) { + if(mode==VERIFY_READ) { + int err = move_addr_to_kernel(kern_msg->msg_name, + kern_msg->msg_namelen, + kern_address); + if(err < 0) + return err; + } + kern_msg->msg_name = kern_address; + } else + kern_msg->msg_name = NULL; + + if(kern_msg->msg_iovlen > UIO_FASTIOV) { + kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), + GFP_KERNEL); + if(!kern_iov) + return -ENOMEM; + } + + tot_len = iov_from_user32_to_kern(kern_iov, + (struct iovec32 *)kern_msg->msg_iov, + kern_msg->msg_iovlen); + if(tot_len >= 0) + kern_msg->msg_iov = kern_iov; + else if(kern_msg->msg_iovlen > UIO_FASTIOV) + kfree(kern_iov); + + return tot_len; +} + +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int +cmsghdr_from_user32_to_kern(struct msghdr *kmsg, unsigned char *stackbuf, + int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if(kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if(kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if(kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +static void +put_cmsg32(struct msghdr *kmsg, int level, int type, int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if(kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if(copy_to_user(CMSG32_DATA(cm), data, + cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) + / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); + i < fdmax; + i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + fp[i]->f_count++; + current->files->fd[new_fd] = fp[i]; + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + +/* In these cases we (currently) can just copy to data over verbatim + * because all CMSGs created by the kernel have well defined types which + * have the same layout in both the 32-bit and 64-bit API. One must add + * some special cased conversions here if we start sending control messages + * with incompatible types. + * + * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after + * we do our work. The remaining cases are: + * + * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean + * IP_TTL int 32-bit clean + * IP_TOS __u8 32-bit clean + * IP_RECVOPTS variable length 32-bit clean + * IP_RETOPTS variable length 32-bit clean + * (these last two are clean because the types are defined + * by the IPv4 protocol) + * IP_RECVERR struct sock_extended_err + + * struct sockaddr_in 32-bit clean + * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + + * struct sockaddr_in6 32-bit clean + * IPV6_PKTINFO struct in6_pktinfo 32-bit clean + * IPV6_HOPLIMIT int 32-bit clean + * IPV6_FLOWINFO u32 32-bit clean + * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean + * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean + * IPV6_RTHDR ipv6 routing exthdr 32-bit clean + * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean + */ +static void +cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr) +{ + unsigned char *workbuf, *wp; + unsigned long bufsz, space_avail; + struct cmsghdr *ucmsg; + + bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; + space_avail = kmsg->msg_controllen + bufsz; + wp = workbuf = kmalloc(bufsz, GFP_KERNEL); + if(workbuf == NULL) + goto fail; + + /* To make this more sane we assume the kernel sends back properly + * formatted control messages. Because of how the kernel will truncate + * the cmsg_len for MSG_TRUNC cases, we need not check that case either. + */ + ucmsg = (struct cmsghdr *) orig_cmsg_uptr; + while(((unsigned long)ucmsg) < ((unsigned long)kmsg->msg_control)) { + struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; + int clen64, clen32; + + /* UCMSG is the 64-bit format CMSG entry in user-space. + * KCMSG32 is within the kernel space temporary buffer + * we use to convert into a 32-bit style CMSG. + */ + __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); + __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); + + clen64 = kcmsg32->cmsg_len; + copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), + clen64 - CMSG_ALIGN(sizeof(*ucmsg))); + clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + + CMSG32_ALIGN(sizeof(struct cmsghdr32))); + kcmsg32->cmsg_len = clen32; + + ucmsg = (struct cmsghdr *) (((char *)ucmsg) + + CMSG_ALIGN(clen64)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); + } + + /* Copy back fixed up data, and adjust pointers. */ + bufsz = (wp - workbuf); + copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); + + kmsg->msg_control = (struct cmsghdr *) + (((char *)orig_cmsg_uptr) + bufsz); + kmsg->msg_controllen = space_avail - bufsz; + + kfree(workbuf); + return; + +fail: + /* If we leave the 64-bit format CMSG chunks in there, + * the application could get confused and crash. So to + * ensure greater recovery, we report no CMSGs. + */ + kmsg->msg_controllen += bufsz; + kmsg->msg_control = (void *) orig_cmsg_uptr; +} + +asmlinkage int +sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iov[UIO_FASTIOV]; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; + unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + + if(kern_msg.msg_controllen) { + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; + } + kern_msg.msg_flags = user_flags; + + lock_kernel(); + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &kern_msg, total_len); + sockfd_put(sock); + } + unlock_kernel(); + + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ + if(ctl_buf != ctl) + kfree(ctl_buf); +out_freeiov: + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + return err; +} + +asmlinkage int +sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct msghdr kern_msg; + char addr[MAX_SOCK_ADDR]; + struct socket *sock; + struct iovec *iov = iovstack; + struct sockaddr *uaddr; + int *uaddr_len; + unsigned long cmsg_ptr; + int err, total_len, len = 0; + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + + uaddr = kern_msg.msg_name; + uaddr_len = &user_msg->msg_namelen; + err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out; + total_len = err; + + cmsg_ptr = (unsigned long) kern_msg.msg_control; + kern_msg.msg_flags = 0; + + lock_kernel(); + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + struct scm_cookie scm; + + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { + len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) kern_msg.msg_control + != cmsg_ptr) + cmsg32_recvmsg_fixup(&kern_msg, + cmsg_ptr); + + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), + &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } + sockfd_put(sock); + } + unlock_kernel(); + + if(uaddr != NULL && err >= 0) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, + uaddr_len); + if(cmsg_ptr != 0 && err >= 0) { + unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); + __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr + - cmsg_ptr); + err |= __put_user(uclen, &user_msg->msg_controllen); + } + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + if(err < 0) + return err; + return len; +} + +extern void check_pending(int signum); + +asmlinkage int +sys32_sigaction (int sig, struct old_sigaction32 *act, + struct old_sigaction32 *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if(sig < 0) { + current->tss.new_signal = 1; + sig = -sig; + } + + if (act) { + old_sigset_t32 mask; + + ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + ret |= __get_user(mask, &act->sa_mask); + if (ret) + return ret; + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + ret |= __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +#ifdef CONFIG_MODULES + +extern asmlinkage unsigned long sys_create_module(const char *name_user, + size_t size); + +asmlinkage unsigned long +sys32_create_module(const char *name_user, __kernel_size_t32 size) +{ + return sys_create_module(name_user, (size_t)size); +} + +extern asmlinkage int sys_init_module(const char *name_user, + struct module *mod_user); + +/* Hey, when you're trying to init module, take time and prepare us a nice 64bit + * module structure, even if from 32bit modutils... Why to pollute kernel... :)) + */ +asmlinkage int +sys32_init_module(const char *name_user, struct module *mod_user) +{ + return sys_init_module(name_user, mod_user); +} + +extern asmlinkage int sys_delete_module(const char *name_user); + +asmlinkage int +sys32_delete_module(const char *name_user) +{ + return sys_delete_module(name_user); +} + +struct module_info32 { + u32 addr; + u32 size; + u32 flags; + s32 usecount; +}; + +/* Query various bits about modules. */ + +static inline long +get_mod_name(const char *user_name, char **buf) +{ + unsigned long page; + long retval; + + if ((unsigned long)user_name >= TASK_SIZE + && !segment_eq(get_fs (), KERNEL_DS)) + return -EFAULT; + + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) { + *buf = (char *)page; + return retval; + } + retval = -ENAMETOOLONG; + } else if (!retval) + retval = -EINVAL; + + free_page(page); + return retval; +} + +static inline void +put_mod_name(char *buf) +{ + free_page((unsigned long)buf); +} + +static __inline__ struct module * +find_module(const char *name) +{ + struct module *mod; + + for (mod = module_list; mod ; mod = mod->next) { + if (mod->flags & MOD_DELETED) + continue; + if (!strcmp(mod->name, name)) + break; + } + + return mod; +} + +static int +qm_modules(char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + struct module *mod; + size_t nmod, space, len; + + nmod = space = 0; + + for (mod = module_list; mod->next != NULL; mod = mod->next, ++nmod) { + len = strlen(mod->name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, mod->name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nmod, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((mod = mod->next)->next != NULL) + space += strlen(mod->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_deps(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + + if (mod->next == NULL) + return -EINVAL; + if ((mod->flags & (MOD_RUNNING | MOD_DELETED)) != MOD_RUNNING) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = 0; + for (i = 0; i < mod->ndeps; ++i) { + const char *dep_name = mod->deps[i].dep->name; + + len = strlen(dep_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, dep_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(i, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while (++i < mod->ndeps) + space += strlen(mod->deps[i].dep->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_refs(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t nrefs, space, len; + struct module_ref *ref; + + if (mod->next == NULL) + return -EINVAL; + if ((mod->flags & (MOD_RUNNING | MOD_DELETED)) != MOD_RUNNING) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = 0; + for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) { + const char *ref_name = ref->ref->name; + + len = strlen(ref_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, ref_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nrefs, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((ref = ref->next_ref) != NULL) + space += strlen(ref->ref->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_symbols(struct module *mod, char *buf, size_t bufsize, + __kernel_size_t32 *ret) +{ + size_t i, space, len; + struct module_symbol *s; + char *strings; + unsigned *vals; + + if ((mod->flags & (MOD_RUNNING | MOD_DELETED)) != MOD_RUNNING) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = mod->nsyms * 2*sizeof(u32); + + i = len = 0; + s = mod->syms; + + if (space > bufsize) + goto calc_space_needed; + + if (!access_ok(VERIFY_WRITE, buf, space)) + return -EFAULT; + + bufsize -= space; + vals = (unsigned *)buf; + strings = buf+space; + + for (; i < mod->nsyms ; ++i, ++s, vals += 2) { + len = strlen(s->name)+1; + if (len > bufsize) + goto calc_space_needed; + + if (copy_to_user(strings, s->name, len) + || __put_user(s->value, vals+0) + || __put_user(space, vals+1)) + return -EFAULT; + + strings += len; + bufsize -= len; + space += len; + } + + if (put_user(i, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + for (; i < mod->nsyms; ++i, ++s) + space += strlen(s->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + int error = 0; + + if (mod->next == NULL) + return -EINVAL; + + if (sizeof(struct module_info32) <= bufsize) { + struct module_info32 info; + info.addr = (unsigned long)mod; + info.size = mod->size; + info.flags = mod->flags; + info.usecount = + ((mod_member_present(mod, can_unload) + && mod->can_unload) + ? -1 : atomic_read(&mod->uc.usecount)); + + if (copy_to_user(buf, &info, sizeof(struct module_info32))) + return -EFAULT; + } else + error = -ENOSPC; + + if (put_user(sizeof(struct module_info32), ret)) + return -EFAULT; + + return error; +} + +asmlinkage int +sys32_query_module(char *name_user, int which, char *buf, + __kernel_size_t32 bufsize, u32 ret) +{ + struct module *mod; + int err; + + lock_kernel(); + if (name_user == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else { + long namelen; + char *name; + + if ((namelen = get_mod_name(name_user, &name)) < 0) { + err = namelen; + goto out; + } + err = -ENOENT; + if (namelen == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; + mod->next != NULL; + mod = mod->next) ; + } else if ((mod = find_module(name)) == NULL) { + put_mod_name(name); + goto out; + } + put_mod_name(name); + } + + switch (which) + { + case 0: + err = 0; + break; + case QM_MODULES: + err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_DEPS: + err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_REFS: + err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_SYMBOLS: + err = qm_symbols(mod, buf, bufsize, + (__kernel_size_t32 *)AA(ret)); + break; + case QM_INFO: + err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + default: + err = -EINVAL; + break; + } +out: + unlock_kernel(); + return err; +} + +struct kernel_sym32 { + u32 value; + char name[60]; +}; + +extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table); + +asmlinkage int +sys32_get_kernel_syms(struct kernel_sym32 *table) +{ + int len, i; + struct kernel_sym *tbl; + mm_segment_t old_fs; + + len = sys_get_kernel_syms(NULL); + if (!table) return len; + tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); + if (!tbl) return -ENOMEM; + old_fs = get_fs(); + set_fs (KERNEL_DS); + sys_get_kernel_syms(tbl); + set_fs (old_fs); + for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) { + if (put_user (tbl[i].value, &table->value) || + copy_to_user (table->name, tbl[i].name, 60)) + break; + } + kfree (tbl); + return i; +} + +#else /* CONFIG_MODULES */ + +asmlinkage unsigned long +sys32_create_module(const char *name_user, size_t size) +{ + return -ENOSYS; +} + +asmlinkage int +sys32_init_module(const char *name_user, struct module *mod_user) +{ + return -ENOSYS; +} + +asmlinkage int +sys32_delete_module(const char *name_user) +{ + return -ENOSYS; +} + +asmlinkage int +sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize, + size_t *ret) +{ + /* Let the program know about the new interface. Not that + it'll do them much good. */ + if (which == 0) + return 0; + + return -ENOSYS; +} + +asmlinkage int +sys32_get_kernel_syms(struct kernel_sym *table) +{ + return -ENOSYS; +} + +#endif /* CONFIG_MODULES */ + +/* Stuff for NFS server syscalls... */ +struct nfsctl_svc32 { + u16 svc32_port; + s32 svc32_nthreads; +}; + +struct nfsctl_client32 { + s8 cl32_ident[NFSCLNT_IDMAX+1]; + s32 cl32_naddr; + struct in_addr cl32_addrlist[NFSCLNT_ADDRMAX]; + s32 cl32_fhkeytype; + s32 cl32_fhkeylen; + u8 cl32_fhkey[NFSCLNT_KEYMAX]; +}; + +struct nfsctl_export32 { + s8 ex32_client[NFSCLNT_IDMAX+1]; + s8 ex32_path[NFS_MAXPATHLEN+1]; + __kernel_dev_t32 ex32_dev; + __kernel_ino_t32 ex32_ino; + s32 ex32_flags; + __kernel_uid_t32 ex32_anon_uid; + __kernel_gid_t32 ex32_anon_gid; +}; + +struct nfsctl_uidmap32 { + u32 ug32_ident; /* char * */ + __kernel_uid_t32 ug32_uidbase; + s32 ug32_uidlen; + u32 ug32_udimap; /* uid_t * */ + __kernel_uid_t32 ug32_gidbase; + s32 ug32_gidlen; + u32 ug32_gdimap; /* gid_t * */ +}; + +struct nfsctl_fhparm32 { + struct sockaddr gf32_addr; + __kernel_dev_t32 gf32_dev; + __kernel_ino_t32 gf32_ino; + s32 gf32_version; +}; + +struct nfsctl_arg32 { + s32 ca32_version; /* safeguard */ + union { + struct nfsctl_svc32 u32_svc; + struct nfsctl_client32 u32_client; + struct nfsctl_export32 u32_export; + struct nfsctl_uidmap32 u32_umap; + struct nfsctl_fhparm32 u32_getfh; + u32 u32_debug; + } u; +#define ca32_svc u.u32_svc +#define ca32_client u.u32_client +#define ca32_export u.u32_export +#define ca32_umap u.u32_umap +#define ca32_getfh u.u32_getfh +#define ca32_authd u.u32_authd +#define ca32_debug u.u32_debug +}; + +union nfsctl_res32 { + struct knfs_fh cr32_getfh; + u32 cr32_debug; +}; + +static int +nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= __get_user(karg->ca_svc.svc_port, &arg32->ca32_svc.svc32_port); + err |= __get_user(karg->ca_svc.svc_nthreads, + &arg32->ca32_svc.svc32_nthreads); + return err; +} + +static int +nfs_clnt32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_client.cl_ident[0], + &arg32->ca32_client.cl32_ident[0], + NFSCLNT_IDMAX); + err |= __get_user(karg->ca_client.cl_naddr, + &arg32->ca32_client.cl32_naddr); + err |= copy_from_user(&karg->ca_client.cl_addrlist[0], + &arg32->ca32_client.cl32_addrlist[0], + (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)); + err |= __get_user(karg->ca_client.cl_fhkeytype, + &arg32->ca32_client.cl32_fhkeytype); + err |= __get_user(karg->ca_client.cl_fhkeylen, + &arg32->ca32_client.cl32_fhkeylen); + err |= copy_from_user(&karg->ca_client.cl_fhkey[0], + &arg32->ca32_client.cl32_fhkey[0], + NFSCLNT_KEYMAX); + return err; +} + +static int +nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_export.ex_client[0], + &arg32->ca32_export.ex32_client[0], + NFSCLNT_IDMAX); + err |= copy_from_user(&karg->ca_export.ex_path[0], + &arg32->ca32_export.ex32_path[0], + NFS_MAXPATHLEN); + err |= __get_user(karg->ca_export.ex_dev, + &arg32->ca32_export.ex32_dev); + err |= __get_user(karg->ca_export.ex_ino, + &arg32->ca32_export.ex32_ino); + err |= __get_user(karg->ca_export.ex_flags, + &arg32->ca32_export.ex32_flags); + err |= __get_user(karg->ca_export.ex_anon_uid, + &arg32->ca32_export.ex32_anon_uid); + err |= __get_user(karg->ca_export.ex_anon_gid, + &arg32->ca32_export.ex32_anon_gid); + return err; +} + +static int +nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + u32 uaddr; + int i; + int err; + + memset(karg, 0, sizeof(*karg)); + if(__get_user(karg->ca_version, &arg32->ca32_version)) + return -EFAULT; + karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER); + if(!karg->ca_umap.ug_ident) + return -ENOMEM; + err = __get_user(uaddr, &arg32->ca32_umap.ug32_ident); + if(strncpy_from_user(karg->ca_umap.ug_ident, + (char *)A(uaddr), PAGE_SIZE) <= 0) + return -EFAULT; + err |= __get_user(karg->ca_umap.ug_uidbase, + &arg32->ca32_umap.ug32_uidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_uidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_udimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * + karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_udimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_uidlen; i++) + err |= __get_user(karg->ca_umap.ug_udimap[i], + &(((__kernel_uid_t32 *)A(uaddr))[i])); + err |= __get_user(karg->ca_umap.ug_gidbase, + &arg32->ca32_umap.ug32_gidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_gidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * + karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_gdimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_gidlen; i++) + err |= __get_user(karg->ca_umap.ug_gdimap[i], + &(((__kernel_gid_t32 *)A(uaddr))[i])); + + return err; +} + +static int +nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfh.gf_addr, + &arg32->ca32_getfh.gf32_addr, + (sizeof(struct sockaddr))); + err |= __get_user(karg->ca_getfh.gf_dev, + &arg32->ca32_getfh.gf32_dev); + err |= __get_user(karg->ca_getfh.gf_ino, + &arg32->ca32_getfh.gf32_ino); + err |= __get_user(karg->ca_getfh.gf_version, + &arg32->ca32_getfh.gf32_version); + return err; +} + +static int +nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32) +{ + int err; + + err = copy_to_user(&res32->cr32_getfh, + &kres->cr_getfh, + sizeof(res32->cr32_getfh)); + err |= __put_user(kres->cr_debug, &res32->cr32_debug); + return err; +} + +extern asmlinkage int sys_nfsservctl(int cmd, void *arg, void *resp); + +int asmlinkage +sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32) +{ + struct nfsctl_arg *karg = NULL; + union nfsctl_res *kres = NULL; + mm_segment_t oldfs; + int err; + + karg = kmalloc(sizeof(*karg), GFP_USER); + if(!karg) + return -ENOMEM; + if(res32) { + kres = kmalloc(sizeof(*kres), GFP_USER); + if(!kres) { + kfree(karg); + return -ENOMEM; + } + } + switch(cmd) { + case NFSCTL_SVC: + err = nfs_svc32_trans(karg, arg32); + break; + case NFSCTL_ADDCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_DELCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_EXPORT: + err = nfs_exp32_trans(karg, arg32); + break; + /* This one is unimplemented, be we're ready for it. */ + case NFSCTL_UGIDUPDATE: + err = nfs_uud32_trans(karg, arg32); + break; + case NFSCTL_GETFH: + err = nfs_getfh32_trans(karg, arg32); + break; + default: + err = -EINVAL; + break; + } + if(err) + goto done; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_nfsservctl(cmd, karg, kres); + set_fs(oldfs); + + if(!err && cmd == NFSCTL_GETFH) + err = nfs_getfh32_res_trans(kres, res32); + +done: + if(karg) { + if(cmd == NFSCTL_UGIDUPDATE) { + if(karg->ca_umap.ug_ident) + kfree(karg->ca_umap.ug_ident); + if(karg->ca_umap.ug_udimap) + kfree(karg->ca_umap.ug_udimap); + if(karg->ca_umap.ug_gdimap) + kfree(karg->ca_umap.ug_gdimap); + } + kfree(karg); + } + if(kres) + kfree(kres); + return err; +} + +asmlinkage int sys_utimes(char *, struct timeval *); + +asmlinkage int +sys32_utimes(char *filename, struct timeval32 *tvs) +{ + char *kfilename; + struct timeval ktvs[2]; + mm_segment_t old_fs; + int ret; + + kfilename = getname32(filename); + ret = PTR_ERR(kfilename); + if (!IS_ERR(kfilename)) { + if (tvs) { + if (get_tv32(&ktvs[0], tvs) || + get_tv32(&ktvs[1], 1+tvs)) + return -EFAULT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_utimes(kfilename, &ktvs[0]); + set_fs(old_fs); + + putname(kfilename); + } + return ret; +} + +/* These are here just in case some old ia32 binary calls it. */ +asmlinkage int +sys32_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +/* PCI config space poking. */ +extern asmlinkage int sys_pciconfig_read(unsigned long bus, + unsigned long dfn, + unsigned long off, + unsigned long len, + unsigned char *buf); + +extern asmlinkage int sys_pciconfig_write(unsigned long bus, + unsigned long dfn, + unsigned long off, + unsigned long len, + unsigned char *buf); + +asmlinkage int +sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + return sys_pciconfig_read((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)AA(ubuf)); +} + +asmlinkage int +sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + return sys_pciconfig_write((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)AA(ubuf)); +} + +extern asmlinkage int sys_prctl(int option, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5); + +asmlinkage int +sys32_prctl(int option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) +{ + return sys_prctl(option, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4, + (unsigned long) arg5); +} + + +extern asmlinkage int sys_newuname(struct new_utsname * name); + +asmlinkage int +sys32_newuname(struct new_utsname * name) +{ + int ret = sys_newuname(name); + + if (current->personality == PER_LINUX32 && !ret) { + ret = copy_to_user(name->machine, "sparc\0\0", 8); + } + return ret; +} + +extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, + size_t count, loff_t pos); + +extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, + size_t count, loff_t pos); + +typedef __kernel_ssize_t32 ssize_t32; + +asmlinkage ssize_t32 +sys32_pread(unsigned int fd, char *ubuf, __kernel_size_t32 count, + u32 poshi, u32 poslo) +{ + return sys_pread(fd, ubuf, count, + ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage ssize_t32 +sys32_pwrite(unsigned int fd, char *ubuf, __kernel_size_t32 count, + u32 poshi, u32 poslo) +{ + return sys_pwrite(fd, ubuf, count, + ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + + +extern asmlinkage int sys_personality(unsigned long); + +asmlinkage int +sys32_personality(unsigned long personality) +{ + int ret; + lock_kernel(); + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + unlock_kernel(); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} + +extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, + size_t count); + +asmlinkage int +sys32_sendfile(int out_fd, int in_fd, __kernel_off_t32 *offset, s32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + off_t of; + + if (offset && get_user(of, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile(out_fd, in_fd, offset ? &of : NULL, count); + set_fs(old_fs); + + if (!ret && offset && put_user(of, offset)) + return -EFAULT; + + return ret; +} + +/* Handle adjtimex compatability. */ + +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage int +sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + memset(&txc, 0, sizeof(struct timex)); + + if(get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if(put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} +#endif // NOTYET + diff --git a/arch/ia64/kdb/Makefile b/arch/ia64/kdb/Makefile new file mode 100644 index 000000000..0b29d6b35 --- /dev/null +++ b/arch/ia64/kdb/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for ia64-specific kdb files.. +# +# Copyright 1999, Silicon Graphics Inc. +# +# Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. +# Code for IA64 written by Goutham Rao <goutham.rao@intel.com> and +# Sreenivas Subramoney <sreenivas.subramoney@intel.com> +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o + +L_TARGET = kdb.a +L_OBJS = kdbsupport.o kdb_io.o kdb_bt.o kdb_traps.o + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/kdb/kdb_bt.c b/arch/ia64/kdb/kdb_bt.c new file mode 100644 index 000000000..dbcb7a575 --- /dev/null +++ b/arch/ia64/kdb/kdb_bt.c @@ -0,0 +1,104 @@ +/** + * Minimalist Kernel Debugger + * Machine dependent stack traceback code for IA-64. + * + * Copyright (C) 1999 Goutham Rao <goutham.rao@intel.com> + * Copyright (C) 1999 Sreenivas Subramoney <sreenivas.subramoney@intel.com> + * Intel Corporation, August 1999. + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * + * 99/12/03 D. Mosberger Reimplemented based on <asm-ia64/unwind.h> API. + * 99/12/06 D. Mosberger Added support for backtracing other processes. + */ + +#include <linux/ctype.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/kdb.h> +#include <asm/system.h> +#include <asm/current.h> +#include <asm/kdbsupport.h> + +/* + * Minimal stack back trace functionality. + */ +int +kdb_bt (int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct task_struct *task = current; + struct ia64_frame_info info; + char *name; + int diag; + + if (strcmp(argv[0], "btp") == 0) { + unsigned long pid; + + diag = kdbgetularg(argv[1], &pid); + if (diag) + return diag; + + task = find_task_by_pid(pid); + if (!task) { + kdb_printf("No process with pid == %d found\n", pid); + return 0; + } + regs = ia64_task_regs(task); + } else if (argc) { + kdb_printf("bt <address> is unsupported for IA-64\n"); + return 0; + } + + if (task == current) { + /* + * Upon entering kdb, the stack frame looks like this: + * + * +---------------------+ + * | struct pt_regs | + * +---------------------+ + * | | + * | kernel stack | + * | | + * +=====================+ <--- top of stack upon entering kdb + * | struct pt_regs | + * +---------------------+ + * | struct switch_stack | + * +---------------------+ + */ + if (user_mode(regs)) { + /* We are not implementing stack backtrace from user mode code */ + kdb_printf ("Not in Kernel\n"); + return 0; + } + ia64_unwind_init_from_current(&info, regs); + } else { + /* + * For a blocked task, the stack frame looks like this: + * + * +---------------------+ + * | struct pt_regs | + * +---------------------+ + * | | + * | kernel stack | + * | | + * +---------------------+ + * | struct switch_stack | + * +=====================+ <--- task->thread.ksp + */ + ia64_unwind_init_from_blocked_task(&info, task); + } + + kdb_printf("Ret Address Reg Stack base Name\n\n") ; + do { + unsigned long ip = ia64_unwind_get_ip(&info); + + name = kdbnearsym(ip); + if (!name) { + kdb_printf("Interrupt\n"); + return 0; + } + kdb_printf("0x%016lx: [0x%016lx] %s\n", ip, ia64_unwind_get_bsp(&info), name); + } while (ia64_unwind_to_previous_frame(&info) >= 0); + return 0; +} diff --git a/arch/ia64/kdb/kdb_io.c b/arch/ia64/kdb/kdb_io.c new file mode 100644 index 000000000..0b5c6fd44 --- /dev/null +++ b/arch/ia64/kdb/kdb_io.c @@ -0,0 +1,350 @@ +/* + * Kernel Debugger Console I/O handler + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Chuck Fleckenstein 1999/07/20 + * Move kdb_info struct declaration to this file + * for cases where serial support is not compiled into + * the kernel. + * + * Masahiro Adegawa 1999/07/20 + * Handle some peculiarities of japanese 86/106 + * keyboards. + * + * marc@mucom.co.il 1999/07/20 + * Catch buffer overflow for serial input. + * + * Scott Foehner + * Port to ia64 + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/serial_reg.h> +#include <linux/spinlock.h> + +#include <asm/io.h> + +#include "pc_keyb.h" + +int kdb_port = 0; + +/* + * 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. + * + */ + +/* + * send: Send a byte to the keyboard controller. Used primarily to + * alter LED settings. + */ + +static void +kdb_kbdsend(unsigned char byte) +{ + while (inb(KBD_STATUS_REG) & KBD_STAT_IBF) + ; + outb(KBD_DATA_REG, byte); +} + +static void +kdb_kbdsetled(int leds) +{ + kdb_kbdsend(KBD_CMD_SET_LEDS); + kdb_kbdsend((unsigned char)leds); +} + +static void +console_read (char *buffer, size_t bufsize) +{ + struct console *in; + struct console *out; + char *cp, ch; + + for (in = console_drivers; in; in = in->next) { + if ((in->flags & CON_ENABLED) && (in->read || in->wait_key)) + break; + } + for (out = console_drivers; out; out = out->next) { + if ((out->flags & CON_ENABLED) && out->write) + break; + } + + if ((!in->read && !in->wait_key) || !out->write) { + panic("kdb_io: can't do console i/o!"); + } + + if (in->read) { + /* this is untested... */ + (*in->read)(in, buffer, bufsize); + return; + } + + bufsize -= 2; /* leave room for CR & NUL terminator */ + cp = buffer; + while (1) { + ch = (*in->wait_key)(in); + switch (ch) { + case '\b': + if (cp > buffer) { + --cp, ++bufsize; + (*out->write)(out, "\b \b", 3); + } + break; + + case '\025': + while (cp > buffer) { + --cp, ++bufsize; + (*out->write)(out, "\b \b", 3); + } + break; + + case '\r': + case '\n': + (*out->write)(out, "\r\n", 2); + *cp++ = '\n'; + *cp++ = '\0'; + return; + + default: + if (bufsize > 0) { + (*out->write)(out, &ch, 1); + --bufsize; + *cp++ = ch; + } + break; + } + } +} + +char * +kdb_getscancode(char *buffer, size_t bufsize) +{ + /* + * XXX Shouldn't kdb _always_ use console based I/O? That's what the console + * abstraction is for, after all... ---davidm + */ +#ifdef CONFIG_IA64_HP_SIM + extern spinlock_t console_lock; + unsigned long flags; + + spin_lock_irqsave(&console_lock, flags); + console_read(buffer, bufsize); + spin_unlock_irqrestore(&console_lock, flags); + return buffer; +#else /* !CONFIG_IA64_HP_SIM */ + char *cp = buffer; + int scancode, scanstatus; + static int shift_lock = 0; /* CAPS LOCK state (0-off, 1-on) */ + static int shift_key = 0; /* Shift next keypress */ + static int ctrl_key = 0; + static int leds = 2; /* Num lock */ + u_short keychar; + extern u_short plain_map[], shift_map[], ctrl_map[]; + + bufsize -= 2; /* Reserve space for newline and null byte */ + + /* + * If we came in via a serial console, we allow that to + * be the input window for kdb. + */ + if (kdb_port != 0) { + char ch; + int status; +#define serial_inp(info, offset) inb((info) + (offset)) +#define serial_out(info, offset, v) outb((v), (info) + (offset)) + + while(1) { + while ((status = serial_inp(kdb_port, UART_LSR)) + & UART_LSR_DR) { +readchar: + ch = serial_inp(kdb_port, UART_RX); + if (ch == 8) { /* BS */ + if (cp > buffer) { + --cp, bufsize++; + printk("%c %c", 0x08, 0x08); + } + continue; + } + serial_out(kdb_port, UART_TX, ch); + if (ch == 13) { /* CR */ + *cp++ = '\n'; + *cp++ = '\0'; + serial_out(kdb_port, UART_TX, 10); + return(buffer); + } + /* + * Discard excess characters + */ + if (bufsize > 0) { + *cp++ = ch; + bufsize--; + } + } + while (((status = serial_inp(kdb_port, UART_LSR)) + & UART_LSR_DR) == 0); + } + } + + while (1) { + + /* + * Wait for a valid scancode + */ + + while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) + ; + + /* + * Fetch the scancode + */ + scancode = inb(KBD_DATA_REG); + scanstatus = inb(KBD_STATUS_REG); + + /* + * Ignore mouse events. + */ + if (scanstatus & KBD_STAT_MOUSE_OBF) + continue; + + /* + * 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; + } + continue; + } + + if ((scancode&0x7f) == 0x1d) { + /* + * Left ctrl key + */ + if ((scancode & 0x80) == 0) { + ctrl_key = 1; + } else { + ctrl_key = 0; + } + continue; + } + + if ((scancode & 0x80) != 0) + continue; + + scancode &= 0x7f; + + /* + * Translate scancode + */ + + if (scancode == 0x3a) { + /* + * Toggle caps lock + */ + shift_lock ^= 1; + leds ^= 0x4; /* toggle caps lock led */ + + kdb_kbdsetled(leds); + continue; + } + + if (scancode == 0x0e) { + /* + * Backspace + */ + if (cp > buffer) { + --cp, bufsize++; + + /* + * XXX - erase character on screen + */ + printk("%c %c", 0x08, 0x08); + } + continue; + } + + if (scancode == 0xe0) { + continue; + } + + /* + * 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) { + 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; + printk("Unknown state/scancode (%d)\n", scancode); + } + + if ((scancode & 0x7f) == 0x1c) { + /* + * enter key. All done. + */ + printk("\n"); + break; + } + + /* + * echo the character. + */ + printk("%c", keychar&0xff); + + if (bufsize) { + --bufsize; + *cp++ = keychar&0xff; + } else { + printk("buffer overflow\n"); + break; + } + + } + + *cp++ = '\n'; /* White space for parser */ + *cp++ = '\0'; /* String termination */ + +#if defined(NOTNOW) + cp = buffer; + while (*cp) { + printk("char 0x%x\n", *cp++); + } +#endif + + return buffer; +#endif /* !CONFIG_IA64_HP_SIM */ +} + diff --git a/arch/ia64/kdb/kdb_traps.c b/arch/ia64/kdb/kdb_traps.c new file mode 100644 index 000000000..6358f7a30 --- /dev/null +++ b/arch/ia64/kdb/kdb_traps.c @@ -0,0 +1,55 @@ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/kdb.h> + +static struct kdb_bp_support { + unsigned long addr ; + int slot ; +} kdb_bp_info[NR_CPUS] ; + + +extern void kdb_bp_install (void); + +/* + * This gets invoked right before a call to ia64_fault(). + * Returns zero the normal fault handler should be invoked. + */ +long +ia64_kdb_fault_handler (unsigned long vector, unsigned long isr, unsigned long ifa, + unsigned long iim, unsigned long itir, unsigned long arg5, + unsigned long arg6, unsigned long arg7, unsigned long stack) +{ + struct switch_stack *sw = (struct switch_stack *) &stack; + struct pt_regs *regs = (struct pt_regs *) (sw + 1); + int bundle_slot; + + /* + * TBD + * If KDB is configured, enter KDB for any fault. + */ + if ((vector == 29) || (vector == 35) || (vector == 36)) { + if (!user_mode(regs)) { + bundle_slot = ia64_psr(regs)->ri; + if (vector == 29) { + if (bundle_slot == 0) { + kdb_bp_info[0].addr = regs->cr_iip; + kdb_bp_info[0].slot = bundle_slot; + kdb(KDB_REASON_FLTDBG, 0, regs); + } else { + if ((bundle_slot < 3) && + (kdb_bp_info[0].addr == regs->cr_iip)) + { + ia64_psr(regs)->id = 1; + ia64_psr(regs)->db = 1; + kdb_bp_install() ; + } else /* some error ?? */ + kdb(KDB_REASON_FLTDBG, 0, regs); + } + } else /* single step or taken branch */ + kdb(KDB_REASON_DEBUG, 0, regs); + return 1; + } + } + return 0; +} diff --git a/arch/ia64/kdb/kdbsupport.c b/arch/ia64/kdb/kdbsupport.c new file mode 100644 index 000000000..0b574ae6e --- /dev/null +++ b/arch/ia64/kdb/kdbsupport.c @@ -0,0 +1,1310 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * Copyright (C) David Mosberger-Tang <davidm@hpl.hp.com> + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/kdb.h> +#include <linux/stddef.h> +#include <linux/vmalloc.h> + +#include <asm/uaccess.h> +#include <asm/kdbsupport.h> +#include <asm/rse.h> + +extern kdb_state_t kdb_state ; +k_machreg_t dbregs[KDB_DBREGS]; + +static int __init +kdb_setup (char *str) +{ + kdb_flags |= KDB_FLAG_EARLYKDB; + return 1; +} + +__setup("kdb", kdb_setup); + +static int +kdb_ia64_sir (int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + 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)); + printk ("lid=0x%lx, tpr=0x%lx, lrr0=0x%lx, llr1=0x%lx\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)); + printk ("itv=0x%lx, pmv=0x%lx, cmcv=0x%lx\n", itv, pmv, cmcv); + + printk ("irr=0x%016lx,0x%016lx,0x%016lx,0x%016lx\n", + ia64_get_irr0(), ia64_get_irr1(), ia64_get_irr2(), ia64_get_irr3()); + return 0; +} + +void __init +kdb_init (void) +{ + extern void kdb_inittab(void); + unsigned long reg; + + kdb_inittab(); + kdb_initbptab(); +#if 0 + kdb_disinit(); +#endif + kdb_printf("kdb version %d.%d by Scott Lurndal. "\ + "Copyright SGI, All Rights Reserved\n", + KDB_MAJOR_VERSION, KDB_MINOR_VERSION); + + /* Enable debug registers */ + __asm__ ("mov %0=psr":"=r"(reg)); + reg |= IA64_PSR_DB; + __asm__ ("mov psr.l=%0"::"r"(reg)); + ia64_srlz_d(); + + /* Init kdb state */ + kdb_state.bkpt_handling_state = BKPTSTATE_NOT_HANDLED ; + + kdb_register("irr", kdb_ia64_sir, "", "Show interrupt registers", 0); +} + +/* + * kdbprintf + * kdbgetword + * kdb_getstr + */ + +char * +kbd_getstr(char *buffer, size_t bufsize, char *prompt) +{ + extern char* kdb_getscancode(char *, size_t); + +#if defined(CONFIG_SMP) + kdb_printf(prompt, smp_processor_id()); +#else + kdb_printf("%s", prompt); +#endif + + return kdb_getscancode(buffer, bufsize); + +} + +int +kdb_printf(const char *fmt, ...) +{ + char buffer[256]; + va_list ap; + int diag; + int linecount; + + diag = kdbgetintenv("LINES", &linecount); + if (diag) + linecount = 22; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + printk("%s", buffer); +#if 0 + if (strchr(buffer, '\n') != NULL) { + kdb_nextline++; + } + + if (kdb_nextline == linecount) { + char buf1[16]; + char buf2[32]; + extern char* kdb_getscancode(char *, size_t); + char *moreprompt; + + /* + * Pause until cr. + */ + moreprompt = kdbgetenv("MOREPROMPT"); + if (moreprompt == NULL) { + moreprompt = "more> "; + } + +#if defined(CONFIG_SMP) + if (strchr(moreprompt, '%')) { + sprintf(buf2, moreprompt, smp_processor_id()); + moreprompt = buf2; + } +#endif + + printk(moreprompt); + (void) kdb_getscancode(buf1, sizeof(buf1)); + + kdb_nextline = 1; + + if ((buf1[0] == 'q') + || (buf1[0] == 'Q')) { + kdb_longjmp(&kdbjmpbuf, 1); + } + } +#endif + return 0; +} + +unsigned long +kdbgetword(unsigned long addr, int width) +{ + /* + * This function checks the address for validity. Any address + * in the range PAGE_OFFSET to high_memory is legal, any address + * which maps to a vmalloc region is legal, and any address which + * is a user address, we use get_user() to verify validity. + */ + + if (addr < PAGE_OFFSET) { + /* + * Usermode address. + */ + unsigned long diag; + unsigned long ulval; + + switch (width) { + case 8: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = get_user(ulval, lp); + break; + } + case 4: + { unsigned int *ip; + + ip = (unsigned int *) addr; + diag = get_user(ulval, ip); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = get_user(ulval, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = get_user(ulval, cp); + break; + } + default: + printk("kdbgetword: Bad width\n"); + return 0L; + } + + if (diag) { + if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) { + printk("kdb: Bad user address 0x%lx\n", addr); + kdb_flags |= KDB_FLAG_SUPRESS; + } + return 0L; + } + kdb_flags &= ~KDB_FLAG_SUPRESS; + return ulval; + } + + if (addr > (unsigned long)high_memory) { + extern int kdb_vmlist_check(unsigned long, unsigned long); + + if (!kdb_vmlist_check(addr, addr+width)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) { + printk("kdb: Bad kernel address 0x%lx\n", addr); + kdb_flags |= KDB_FLAG_SUPRESS; + } + return 0L; + } + } + + /* + * A good address. Reset error flag. + */ + kdb_flags &= ~KDB_FLAG_SUPRESS; + + switch (width) { + case 8: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + return *lp; + } + case 4: + { unsigned int *ip; + + ip = (unsigned int *)(addr); + return *ip; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + return *sp; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + return *cp; + } + } + + printk("kdbgetword: Bad width\n"); + return 0L; +} + +/* + * Start of breakpoint management routines + */ + +/* + * Arg: bp structure + */ + +int +kdb_allocdbreg(kdb_bp_t *bp) +{ + int i=0; + + /* For inst bkpt, just return. No hw reg alloc to be done. */ + + if (bp->bp_mode == BKPTMODE_INST) { + return i; + } else if (bp->bp_mode == BKPTMODE_DATAW) { + for(i=0; i<KDB_DBREGS; i++) { + if (dbregs[i] == 0xffffffff) { + dbregs[i] = 0; + return i; + } + } + } + + return -1; +} + +void +kdb_freedbreg(kdb_bp_t *bp) +{ + if (bp->bp_mode == BKPTMODE_DATAW) + dbregs[bp->bp_reg] = 0xffffffff; +} + +void +kdb_initdbregs(void) +{ + int i; + + for(i=0; i<KDB_DBREGS; i++) { + dbregs[i] = 0xffffffff; + } +} +int +kdbinstalltrap(int type, handler_t newh, handler_t *oldh) +{ + /* + * Usurp INTn. XXX - TBD. + */ + + return 0; +} + +int +install_instbkpt(kdb_bp_t *bp) +{ + unsigned long *addr = (unsigned long *)bp->bp_addr ; + bundle_t *bundle = (bundle_t *)bp->bp_longinst; + + /* save current bundle */ + *bundle = *(bundle_t *)addr ; + + /* Set the break point! */ + ((bundle_t *)addr)->lform.low8 = ( + (((bundle_t *)addr)->lform.low8 & ~INST_SLOT0_MASK) | + BREAK_INSTR); + + /* set flag */ + bp->bp_instvalid = 1 ; + + /* flush icache as it is stale now */ + ia64_flush_icache_page((unsigned long)addr) ; + +#ifdef KDB_DEBUG + kdb_printf ("[0x%016lx]: install 0x%016lx with 0x%016lx\n", + addr, bundle->lform.low8, addr[0]) ; +#endif + return 0 ; +} + +int +install_databkpt(kdb_bp_t *bp) +{ + unsigned long dbreg_addr = bp->bp_reg * 2; + unsigned long dbreg_cond = dbreg_addr + 1; + unsigned long value = 0x8fffffffffffffff; + unsigned long addr = (unsigned long)bp->bp_addr; + __asm__ ("mov dbr[%0]=%1"::"r"(dbreg_cond),"r"(value)); +// __asm__ ("movl %0,%%db0\n\t"::"r"(contents)); + __asm__ ("mov dbr[%0]=%1"::"r"(dbreg_addr),"r"(addr)); + ia64_insn_group_barrier(); + ia64_srlz_i(); + ia64_insn_group_barrier(); + +#ifdef KDB_DEBUG + kdb_printf("installed dbkpt at 0x%016lx\n", addr) ; +#endif + return 0; +} + +int +kdbinstalldbreg(kdb_bp_t *bp) +{ + if (bp->bp_mode == BKPTMODE_INST) { + return install_instbkpt(bp) ; + } else if (bp->bp_mode == BKPTMODE_DATAW) { + return install_databkpt(bp) ; + } + return 0; +} + +void +remove_instbkpt(kdb_bp_t *bp) +{ + unsigned long *addr = (unsigned long *)bp->bp_addr ; + bundle_t *bundle = (bundle_t *)bp->bp_longinst; + + if (!bp->bp_instvalid) + /* Nothing to remove. If we just alloced the bkpt + * but never resumed, the bp_inst will not be valid. */ + return ; + +#ifdef KDB_DEBUG + kdb_printf ("[0x%016lx]: remove 0x%016lx with 0x%016lx\n", + addr, addr[0], bundle->lform.low8) ; +#endif + + /* restore current bundle */ + *(bundle_t *)addr = *bundle ; + /* reset the flag */ + bp->bp_instvalid = 0 ; + ia64_flush_icache_page((unsigned long)addr) ; +} + +void +remove_databkpt(kdb_bp_t *bp) +{ + int regnum = bp->bp_reg ; + unsigned long dbreg_addr = regnum * 2; + unsigned long dbreg_cond = dbreg_addr + 1; + unsigned long value = 0x0fffffffffffffff; + __asm__ ("mov dbr[%0]=%1"::"r"(dbreg_cond),"r"(value)); +// __asm__ ("movl %0,%%db0\n\t"::"r"(contents)); + ia64_insn_group_barrier(); + ia64_srlz_i(); + ia64_insn_group_barrier(); + +#ifdef KDB_DEBUG + kdb_printf("removed dbkpt at 0x%016lx\n", bp->bp_addr) ; +#endif +} + +void +kdbremovedbreg(kdb_bp_t *bp) +{ + if (bp->bp_mode == BKPTMODE_INST) { + remove_instbkpt(bp) ; + } else if (bp->bp_mode == BKPTMODE_DATAW) { + remove_databkpt(bp) ; + } +} + +k_machreg_t +kdb_getdr6(void) +{ + return kdb_getdr(6); +} + +k_machreg_t +kdb_getdr7(void) +{ + return kdb_getdr(7); +} + +k_machreg_t +kdb_getdr(int regnum) +{ + k_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; +} + + +k_machreg_t +kdb_getcr(int regnum) +{ + k_machreg_t contents = 0; + return contents; +} + +void +kdb_putdr6(k_machreg_t contents) +{ + kdb_putdr(6, contents); +} + +void +kdb_putdr7(k_machreg_t contents) +{ + kdb_putdr(7, contents); +} + +void +kdb_putdr(int regnum, k_machreg_t contents) +{ +} + +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(); +} + +/* + * kdb_db_trap + * + * Perform breakpoint processing upon entry to the + * processor debugger fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * reason Why did we enter kdb - fault or break + * 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) + * Locking: + * None. + * Remarks: + * Yup, there be goto's here. + */ + +int +kdb_db_trap(struct pt_regs *ef, int reason) +{ + int i, rv=0; + + /* Trying very hard to not change the interface to kdb. + * So, eventhough we have these values in the fault function + * it is not passed in but read again. + */ + fault_regs_t faultregs ; + + if (reason == KDB_REASON_FLTDBG) + get_fault_regs(&faultregs) ; + + /* NOTE : XXX: This has to be done only for data bkpts */ + /* Prevent it from continuously faulting */ + ef->cr_ipsr |= 0x0000002000000000; + + if (ef->cr_ipsr & 0x0000010000000000) { + /* single step */ + ef->cr_ipsr &= 0xfffffeffffffffff; + if ((kdb_state.bkpt_handling_state == BKPTSTATE_HANDLED) + && (kdb_state.cmd_given == CMDGIVEN_GO)) + ; + else + kdb_printf("SS trap at 0x%lx\n", ef->cr_iip + ia64_psr(ef)->ri); + rv = 1; + kdb_state.reason_for_entry = ENTRYREASON_SSTEP ; + goto handled; + } else + kdb_state.reason_for_entry = ENTRYREASON_GO ; + + /* + * Determine which breakpoint was encountered. + */ + for(i=0; i<KDB_MAXBPT; i++) { + if ((breakpoints[i].bp_enabled) + && ((breakpoints[i].bp_addr == ef->cr_iip) || + ((faultregs.ifa) && + (breakpoints[i].bp_addr == faultregs.ifa)))) { + /* + * Hit this breakpoint. Remove it while we are + * handling hit to avoid recursion. XXX ?? + */ + if (breakpoints[i].bp_addr == faultregs.ifa) + kdb_printf("Data breakpoint #%d for 0x%lx at 0x%lx\n", + i, breakpoints[i].bp_addr, ef->cr_iip + ia64_psr(ef)->ri); + else + kdb_printf("%s breakpoint #%d at 0x%lx\n", + rwtypes[0], + i, breakpoints[i].bp_addr); + + /* + * For an instruction breakpoint, disassemble + * the current instruction. + */ +#if 0 + if (rw == 0) { + kdb_id1(ef->eip); + } +#endif + + goto handled; + } + } + +#if 0 +unknown: +#endif + kdb_printf("Unknown breakpoint. Should forward. \n"); + /* Need a flag for this. The skip should be done XXX + * when a go or single step command is done for this session. + * For now it is here. + */ + ia64_increment_ip(ef) ; + return rv ; + +handled: + + /* We are here after handling a break inst/data bkpt */ + if (kdb_state.bkpt_handling_state == BKPTSTATE_NOT_HANDLED) { + kdb_state.bkpt_handling_state = BKPTSTATE_HANDLED ; + if (kdb_state.reason_for_entry == ENTRYREASON_GO) { + kdb_setsinglestep(ef) ; + kdb_state.kdb_action = ACTION_NOBPINSTALL; + /* We dont want bp install just this once */ + kdb_state.cmd_given = CMDGIVEN_UNKNOWN ; + } + } else if (kdb_state.bkpt_handling_state == BKPTSTATE_HANDLED) { + kdb_state.bkpt_handling_state = BKPTSTATE_NOT_HANDLED ; + if (kdb_state.reason_for_entry == ENTRYREASON_SSTEP) { + if (kdb_state.cmd_given == CMDGIVEN_GO) + kdb_state.kdb_action = ACTION_NOPROMPT ; + kdb_state.cmd_given = CMDGIVEN_UNKNOWN ; + } + } else + kdb_printf("Unknown value of bkpt state\n") ; + + return rv; + +} + +void +kdb_setsinglestep(struct pt_regs *regs) +{ + regs->cr_ipsr |= 0x0000010000000000; +#if 0 + regs->eflags |= EF_TF; +#endif +} + +/* + * Symbol table functions. + */ + +/* + * kdbgetsym + * + * Return the symbol table entry for the given symbol + * + * Parameters: + * symname Character string containing symbol name + * Outputs: + * Returns: + * NULL Symbol doesn't exist + * ksp Pointer to symbol table entry + * Locking: + * None. + * Remarks: + */ + +__ksymtab_t * +kdbgetsym(const char *symname) +{ + __ksymtab_t *ksp = __kdbsymtab; + int i; + + if (symname == NULL) + return NULL; + + for (i=0; i<__kdbsymtabsize; i++, ksp++) { + if (ksp->name && (strcmp(ksp->name, symname)==0)) { + return ksp; + } + } + + return NULL; +} + +/* + * kdbgetsymval + * + * Return the address of the given symbol. + * + * Parameters: + * symname Character string containing symbol name + * Outputs: + * Returns: + * 0 Symbol name is NULL + * addr Address corresponding to symname + * Locking: + * None. + * Remarks: + */ + +unsigned long +kdbgetsymval(const char *symname) +{ + __ksymtab_t *ksp = kdbgetsym(symname); + + return (ksp?ksp->value:0); +} + +/* + * kdbaddmodsym + * + * Add a symbol to the kernel debugger symbol table. Called when + * a new module is loaded into the kernel. + * + * Parameters: + * symname Character string containing symbol name + * value Value of symbol + * Outputs: + * Returns: + * 0 Successfully added to table. + * 1 Duplicate symbol + * 2 Symbol table full + * Locking: + * None. + * Remarks: + */ + +int +kdbaddmodsym(char *symname, unsigned long value) +{ + + /* + * Check for duplicate symbols. + */ + if (kdbgetsym(symname)) { + printk("kdb: Attempt to register duplicate symbol '%s' @ 0x%lx\n", + symname, value); + return 1; + } + + if (__kdbsymtabsize < __kdbmaxsymtabsize) { + __ksymtab_t *ksp = &__kdbsymtab[__kdbsymtabsize++]; + + ksp->name = symname; + ksp->value = value; + return 0; + } + + /* + * No room left in kernel symbol table. + */ + { + static int __kdbwarn = 0; + + if (__kdbwarn == 0) { + __kdbwarn++; + printk("kdb: Exceeded symbol table size. Increase CONFIG_KDB_SYMTAB_SIZE in kernel configuration\n"); + } + } + + return 2; +} + +/* + * kdbdelmodsym + * + * Add a symbol to the kernel debugger symbol table. Called when + * a new module is loaded into the kernel. + * + * Parameters: + * symname Character string containing symbol name + * value Value of symbol + * Outputs: + * Returns: + * 0 Successfully added to table. + * 1 Symbol not found + * Locking: + * None. + * Remarks: + */ + +int +kdbdelmodsym(const char *symname) +{ + __ksymtab_t *ksp, *endksp; + + if (symname == NULL) + return 1; + + /* + * Search for the symbol. If found, move + * all successive symbols down one position + * in the symbol table to avoid leaving holes. + */ + endksp = &__kdbsymtab[__kdbsymtabsize]; + for (ksp = __kdbsymtab; ksp < endksp; ksp++) { + if (ksp->name && (strcmp(ksp->name, symname) == 0)) { + endksp--; + for ( ; ksp < endksp; ksp++) { + *ksp = *(ksp + 1); + } + __kdbsymtabsize--; + return 0; + } + } + + return 1; +} + +/* + * kdbnearsym + * + * Return the name of the symbol with the nearest address + * less than 'addr'. + * + * Parameters: + * addr Address to check for symbol near + * Outputs: + * Returns: + * NULL No symbol with address less than 'addr' + * symbol Returns the actual name of the symbol. + * Locking: + * None. + * Remarks: + */ + +char * +kdbnearsym(unsigned long addr) +{ + __ksymtab_t *ksp = __kdbsymtab; + __ksymtab_t *kpp = NULL; + int i; + + for(i=0; i<__kdbsymtabsize; i++, ksp++) { + if (!ksp->name) + continue; + + if (addr == ksp->value) { + kpp = ksp; + break; + } + if (addr > ksp->value) { + if ((kpp == NULL) + || (ksp->value > kpp->value)) { + kpp = ksp; + } + } + } + + /* + * If more than 128k away, don't bother. + */ + if ((kpp == NULL) + || ((addr - kpp->value) > 0x20000)) { + return NULL; + } + + return kpp->name; +} + +/* + * kdbgetregcontents + * + * 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 + * %<regname> - 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 +kdbgetregcontents(const char *regname, + struct pt_regs *regs, + unsigned long *contents) +{ + int i; + + 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, "isr") == 0) { + fault_regs_t fr ; + get_fault_regs(&fr) ; + *contents = fr.isr ; + return 0 ; + } + +#if 0 + /* XXX need to verify this */ + if (strcmp(regname, "kesp") == 0) { + *contents = (unsigned long)regs + sizeof(struct pt_regs); + return 0; + } + + if (regname[0] == '%') { + /* User registers: %%e[a-c]x, etc */ + regname++; + regs = (struct pt_regs *) + (current->thread.ksp - sizeof(struct pt_regs)); + } +#endif + + for (i=0; i<nkdbreglist; i++) { + if (strstr(kdbreglist[i].reg_name, regname)) + break; + } + + if (i == nkdbreglist) { + /* Lets check the rse maybe */ + if (regname[0] == 'r') + if (show_cur_stack_frame(regs, simple_strtoul(regname+1, 0, 0) - 31, + contents)) + return 0 ; + return KDB_BADREG; + } + + *contents = *(unsigned long *)((unsigned long)regs + + kdbreglist[i].reg_offset); + + return 0; +} + +/* + * kdbsetregcontents + * + * 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 + * %<register-name> + * + * 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 +kdbsetregcontents(const char *regname, + struct pt_regs *regs, + unsigned long contents) +{ + int i; + + if (regname[0] == '%') { + regname++; + regs = (struct pt_regs *) + (current->thread.ksp - sizeof(struct pt_regs)); + } + + for (i=0; i<nkdbreglist; i++) { + if (strnicmp(kdbreglist[i].reg_name, + regname, + strlen(regname)) == 0) + break; + } + + if ((i == nkdbreglist) + || (strlen(kdbreglist[i].reg_name) != strlen(regname))) { + return KDB_BADREG; + } + + *(unsigned long *)((unsigned long)regs + kdbreglist[i].reg_offset) = + contents; + + return 0; +} + +/* + * kdbdumpregs + * + * 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 + * 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 + * + * ARs TB Done + * Interrupt regs 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 +kdbdumpregs(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 *) + (current->thread.ksp - sizeof(struct pt_regs)); + } + + if (type == NULL) { + for (i=0; i<nkdbreglist; i++) { + kdb_printf("%s: 0x%16.16lx ", + kdbreglist[i].reg_name, + *(unsigned long *)((unsigned long)regs + + kdbreglist[i].reg_offset)); + + if ((++count % 3) == 0) + kdb_printf("\n"); + } + + kdb_printf("®s = 0x%16.16lx\n", regs); + + return 0; + } + + switch (type[0]) { + case 'd': + { + for(i=0; i<8; i+=2) { + kdb_printf("idr%d: 0x%16.16lx idr%d: 0x%16.16lx\n", i, + kdb_getdr(i), i+1, kdb_getdr(i+1)); + + } + return 0; + } +#if 0 + case 'c': + { + unsigned long cr[5]; + + for (i=0; i<5; i++) { + cr[i] = kdb_getcr(i); + } + kdb_printf("cr0 = 0x%8.8x cr1 = 0x%8.8x cr2 = 0x%8.8x cr3 = 0x%8.8x\ncr4 = 0x%8.8x\n", + cr[0], cr[1], cr[2], cr[3], cr[4]); + return 0; + } +#endif + case 'm': + break; + case 'r': + break; + + case 's': + { + show_cur_stack_frame(regs, 0, NULL) ; + + return 0 ; + } + + case '%': + { + unsigned long contents ; + + if (!kdbgetregcontents(type+1, regs, &contents)) + kdb_printf("%s = 0x%16.16lx\n", type+1, contents) ; + else + kdb_printf("diag: Invalid register %s\n", type+1) ; + + return 0 ; + } + + default: + return KDB_BADREG; + } + + /* NOTREACHED */ + return 0; +} + +k_machreg_t +kdb_getpc(struct pt_regs *regs) +{ + return regs->cr_iip + ia64_psr(regs)->ri; +} + +int +kdb_setpc(struct pt_regs *regs, k_machreg_t newpc) +{ + regs->cr_iip = newpc & ~0xf; + ia64_psr(regs)->ri = newpc & 0x3; + return 0; +} + +void +kdb_disableint(kdbintstate_t *state) +{ + int *fp = (int *)state; + int flags; + + __save_flags(flags); + __cli(); + + *fp = flags; +} + +void +kdb_restoreint(kdbintstate_t *state) +{ + int flags = *(int *)state; + __restore_flags(flags); +} + +int +kdb_putword(unsigned long addr, unsigned long contents) +{ + *(unsigned long *)addr = contents; + return 0; +} + +int +kdb_getcurrentframe(struct pt_regs *regs) +{ +#if 0 + regs->xcs = 0; +#if defined(CONFIG_KDB_FRAMEPTR) + asm volatile("movl %%ebp,%0":"=m" (*(int *)®s->ebp)); +#endif + asm volatile("movl %%esp,%0":"=m" (*(int *)®s->esp)); +#endif + return 0; +} + +unsigned long +show_cur_stack_frame(struct pt_regs *regs, int regno, unsigned long *contents) +{ + long sof = regs->cr_ifs & ((1<<7)-1) ; /* size of frame */ + unsigned long i ; + int j; + struct switch_stack *prs_regs = getprsregs(regs) ; + unsigned long *sofptr = (prs_regs? ia64_rse_skip_regs( + (unsigned long *)prs_regs->ar_bspstore, -sof) : NULL) ; + + if (!sofptr) { + printk("Unable to display Current Stack Frame\n") ; + return 0 ; + } + + if (regno < 0) + return 0 ; + + for (i=sof, j=0;i;i--,j++) { + /* remember to skip the nat collection dword */ + if ((((unsigned long)sofptr>>3) & (((1<<6)-1))) + == ((1<<6)-1)) + sofptr++ ; + + /* return the value in the reg if regno is non zero */ + + if (regno) { + if ((j+1) == regno) { + if (contents) + *contents = *sofptr ; + return -1; + } + sofptr++ ; + } else { + printk(" r%d: %016lx ", 32+j, *sofptr++) ; + if (!((j+1)%3)) printk("\n") ; + } + } + + if (regno) { + if (!i) /* bogus rse number */ + return 0 ; + } else + printk("\n") ; + + return 0 ; +} diff --git a/arch/ia64/kdb/pc_keyb.h b/arch/ia64/kdb/pc_keyb.h new file mode 100644 index 000000000..3d4831a80 --- /dev/null +++ b/arch/ia64/kdb/pc_keyb.h @@ -0,0 +1,127 @@ +/* + * linux/drivers/char/pc_keyb.h + * + * PC Keyboard And Keyboard Controller + * + * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + */ + +/* + * 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 + */ + +#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_BUF_SIZE 2048 + +struct aux_queue { + unsigned long head; + unsigned long tail; + struct wait_queue *proc_list; + struct fasync_struct *fasync; + unsigned char buf[AUX_BUF_SIZE]; +}; + diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile new file mode 100644 index 000000000..7cb47da72 --- /dev/null +++ b/arch/ia64/kernel/Makefile @@ -0,0 +1,42 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.s: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -E -o $*.s $< +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o $*.o $< + +all: kernel.o head.o init_task.o + +O_TARGET := kernel.o +O_OBJS := acpi.o entry.o gate.o efi.o efi_stub.o irq.o irq_default.o irq_internal.o ivt.o \ + pal.o pci-dma.o process.o perfmon.o ptrace.o sal.o sal_stub.o semaphore.o setup.o signal.o \ + sys_ia64.o traps.o time.o unaligned.o unwind.o +#O_OBJS := fpreg.o +#OX_OBJS := ia64_ksyms.o + +ifeq ($(CONFIG_IA64_GENERIC),y) +O_OBJS += machvec.o +endif + +ifdef CONFIG_PCI +O_OBJS += pci.o +endif + +ifdef CONFIG_SMP +O_OBJS += smp.o irq_lock.o +endif + +ifeq ($(CONFIG_MCA),y) +O_OBJS += mca.o mca_asm.o +endif + +clean:: + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c new file mode 100644 index 000000000..e289efab6 --- /dev/null +++ b/arch/ia64/kernel/acpi.c @@ -0,0 +1,308 @@ +/* + * Advanced Configuration and Power Interface + * + * Based on 'ACPI Specification 1.0b' February 2, 1999 and + * 'IA-64 Extensions to ACPI Specification' Revision 0.6 + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com> + */ + +#include <linux/config.h> + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <asm/acpi-ext.h> +#include <asm/page.h> +#include <asm/efi.h> +#include <asm/io.h> +#include <asm/iosapic.h> +#include <asm/irq.h> + +#undef ACPI_DEBUG /* Guess what this does? */ + +#ifdef CONFIG_SMP +extern unsigned long ipi_base_addr; +#endif + +/* These are ugly but will be reclaimed by the kernel */ +int __initdata acpi_cpus = 0; +int __initdata acpi_apic_map[32]; +int __initdata cpu_cnt = 0; + +void (*pm_idle) (void); + +/* + * Identify usable CPU's and remember them for SMP bringup later. + */ +static void __init +acpi_lsapic(char *p) +{ + int add = 1; + + acpi_entry_lsapic_t *lsapic = (acpi_entry_lsapic_t *) p; + + if ((lsapic->flags & LSAPIC_PRESENT) == 0) + return; + + printk(" CPU %d (%.04x:%.04x): ", cpu_cnt, lsapic->eid, lsapic->id); + + if ((lsapic->flags & LSAPIC_ENABLED) == 0) { + printk("Disabled.\n"); + add = 0; + } else if (lsapic->flags & LSAPIC_PERFORMANCE_RESTRICTED) { + printk("Performance Restricted; ignoring.\n"); + add = 0; + } + + if (add) { + printk("Available.\n"); + acpi_cpus++; + acpi_apic_map[cpu_cnt] = (lsapic->id << 8) | lsapic->eid; + } + + cpu_cnt++; +} + +/* + * Find all IOSAPICs and tag the iosapic_vector structure with the appropriate + * base addresses. + */ +static void __init +acpi_iosapic(char *p) +{ + /* + * This is not good. ACPI is not necessarily limited to CONFIG_IA64_SV, yet + * ACPI does not necessarily imply IOSAPIC either. Perhaps there should be + * a means for platform_setup() to register ACPI handlers? + */ +#ifdef CONFIG_IA64_DIG + acpi_entry_iosapic_t *iosapic = (acpi_entry_iosapic_t *) p; + unsigned int ver; + int l, v, pins; + + ver = iosapic_version(iosapic->address); + pins = (ver >> 16) & 0xff; + + printk("IOSAPIC Version %x.%x: address 0x%lx IRQs 0x%x - 0x%x\n", + (ver & 0xf0) >> 4, (ver & 0x0f), iosapic->address, + iosapic->irq_base, iosapic->irq_base + pins); + + for (l = 0; l < pins; l++) { + v = map_legacy_irq(iosapic->irq_base + l); + if (v > IA64_MAX_VECTORED_IRQ) { + printk(" !!! IRQ %d > 255\n", v); + continue; + } + /* XXX Check for IOSAPIC collisions */ + iosapic_addr(v) = (unsigned long) ioremap(iosapic->address, 0); + iosapic_baseirq(v) = iosapic->irq_base; + } + iosapic_init(iosapic->address); +#endif +} + + +/* + * Configure legacy IRQ information in iosapic_vector + */ +static void __init +acpi_legacy_irq(char *p) +{ + /* + * This is not good. ACPI is not necessarily limited to CONFIG_IA64_SV, yet + * ACPI does not necessarily imply IOSAPIC either. Perhaps there should be + * a means for platform_setup() to register ACPI handlers? + */ +#ifdef CONFIG_IA64_IRQ_ACPI + acpi_entry_int_override_t *legacy = (acpi_entry_int_override_t *) p; + unsigned char vector; + int i; + + vector = map_legacy_irq(legacy->isa_irq); + + /* + * Clobber any old pin mapping. It may be that it gets replaced later on + */ + for (i = 0; i < IA64_MAX_VECTORED_IRQ; i++) { + if (i == vector) + continue; + if (iosapic_pin(i) == iosapic_pin(vector)) + iosapic_pin(i) = 0xff; + } + + iosapic_pin(vector) = legacy->pin; + iosapic_bus(vector) = BUS_ISA; /* This table only overrides the ISA devices */ + iosapic_busdata(vector) = 0; + + /* + * External timer tick is special... + */ + if (vector != TIMER_IRQ) + iosapic_dmode(vector) = IO_SAPIC_LOWEST_PRIORITY; + else + iosapic_dmode(vector) = IO_SAPIC_FIXED; + + /* See MPS 1.4 section 4.3.4 */ + switch (legacy->flags) { + case 0x5: + iosapic_polarity(vector) = IO_SAPIC_POL_HIGH; + iosapic_trigger(vector) = IO_SAPIC_EDGE; + break; + case 0x8: + iosapic_polarity(vector) = IO_SAPIC_POL_LOW; + iosapic_trigger(vector) = IO_SAPIC_EDGE; + break; + case 0xd: + iosapic_polarity(vector) = IO_SAPIC_POL_HIGH; + iosapic_trigger(vector) = IO_SAPIC_LEVEL; + break; + case 0xf: + iosapic_polarity(vector) = IO_SAPIC_POL_LOW; + iosapic_trigger(vector) = IO_SAPIC_LEVEL; + break; + default: + printk(" ACPI Legacy IRQ 0x%02x: Unknown flags 0x%x\n", legacy->isa_irq, + legacy->flags); + break; + } + +#ifdef ACPI_DEBUG + printk("Legacy ISA IRQ %x -> IA64 Vector %x IOSAPIC Pin %x Active %s %s Trigger\n", + legacy->isa_irq, vector, iosapic_pin(vector), + ((iosapic_polarity(vector) == IO_SAPIC_POL_LOW) ? "Low" : "High"), + ((iosapic_trigger(vector) == IO_SAPIC_LEVEL) ? "Level" : "Edge")); +#endif /* ACPI_DEBUG */ + +#endif /* CONFIG_IA64_IRQ_ACPI */ +} + +/* + * Info on platform interrupt sources: NMI. PMI, INIT, etc. + */ +static void __init +acpi_platform(char *p) +{ + acpi_entry_platform_src_t *plat = (acpi_entry_platform_src_t *) p; + + printk("PLATFORM: IOSAPIC %x -> Vector %lx on CPU %.04u:%.04u\n", + plat->iosapic_vector, plat->global_vector, plat->eid, plat->id); +} + +/* + * Parse the ACPI Multiple SAPIC Table + */ +static void __init +acpi_parse_msapic(acpi_sapic_t *msapic) +{ + char *p, *end; + + memset(&acpi_apic_map, -1, sizeof(acpi_apic_map)); + +#ifdef CONFIG_SMP + /* Base address of IPI Message Block */ + ipi_base_addr = ioremap(msapic->interrupt_block, 0); +#endif + + p = (char *) (msapic + 1); + end = p + (msapic->header.length - sizeof(acpi_sapic_t)); + + while (p < end) { + + switch (*p) { + case ACPI_ENTRY_LOCAL_SAPIC: + acpi_lsapic(p); + break; + + case ACPI_ENTRY_IO_SAPIC: + acpi_iosapic(p); + break; + + case ACPI_ENTRY_INT_SRC_OVERRIDE: + acpi_legacy_irq(p); + break; + + case ACPI_ENTRY_PLATFORM_INT_SOURCE: + acpi_platform(p); + break; + + default: + break; + } + + /* Move to next table entry. */ + p += *(p + 1); + } + + /* Make bootup pretty */ + printk(" %d CPUs available, %d CPUs total\n", acpi_cpus, cpu_cnt); +} + +int __init +acpi_parse(acpi_rsdp_t *rsdp) +{ + acpi_rsdt_t *rsdt; + acpi_desc_table_hdr_t *hdrp; + long tables, i; + + if (!rsdp) { + printk("Uh-oh, no ACPI Root System Description Pointer table!\n"); + return 0; + } + + if (strncmp(rsdp->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) { + printk("Uh-oh, ACPI RSDP signature incorrect!\n"); + return 0; + } + + rsdp->rsdt = __va(rsdp->rsdt); + rsdt = rsdp->rsdt; + if (strncmp(rsdt->header.signature, ACPI_RSDT_SIG, ACPI_RSDT_SIG_LEN)) { + printk("Uh-oh, ACPI RDST signature incorrect!\n"); + return 0; + } + + printk("ACPI: %.6s %.8s %d.%d\n", rsdt->header.oem_id, rsdt->header.oem_table_id, + rsdt->header.oem_revision >> 16, rsdt->header.oem_revision & 0xffff); + + tables = (rsdt->header.length - sizeof(acpi_desc_table_hdr_t)) / 8; + for (i = 0; i < tables; i++) { + hdrp = (acpi_desc_table_hdr_t *) __va(rsdt->entry_ptrs[i]); + + /* Only interested int the MSAPIC table for now ... */ + if (strncmp(hdrp->signature, ACPI_SAPIC_SIG, ACPI_SAPIC_SIG_LEN) != 0) + continue; + + acpi_parse_msapic((acpi_sapic_t *) hdrp); + } /* while() */ + + if (acpi_cpus == 0) { + printk("ACPI: Found 0 CPUS; assuming 1\n"); + acpi_cpus = 1; /* We've got at least one of these, no? */ + } + return 1; +} + +const char * +acpi_get_sysname (void) +{ + /* the following should go away once we have an ACPI parser: */ +#ifdef CONFIG_IA64_GENERIC + return "hpsim"; +#else +# if defined (CONFIG_IA64_HP_SIM) + return "hpsim"; +# elif defined (CONFIG_IA64_SGI_SN1_SIM) + return "sn1"; +# elif defined (CONFIG_IA64_DIG) + return "dig"; +# else +# error Unknown platform. Fix acpi.c. +# endif +#endif +} diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c new file mode 100644 index 000000000..dd7de2ab0 --- /dev/null +++ b/arch/ia64/kernel/efi.c @@ -0,0 +1,365 @@ +/* + * Extensible Firmware Interface + * + * Based on Extensible Firmware Interface Specification version 0.9 April 30, 1999 + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * Copyright (C) 1999 Hewlett-Packard Co. + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * + * All EFI Runtime Services are not implemented yet as EFI only + * supports physical mode addressing on SoftSDV. This is to be fixed + * in a future version. --drummond 1999-07-20 + * + * Implemented EFI runtime services and virtual mode calls. --davidm + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/time.h> + +#include <asm/efi.h> +#include <asm/io.h> +#include <asm/processor.h> + +#define EFI_DEBUG + +extern efi_status_t efi_call_phys (void *, ...); + +struct efi efi; + +static efi_runtime_services_t *runtime; + +static efi_status_t +phys_get_time (efi_time_t *tm, efi_time_cap_t *tc) +{ + return efi_call_phys(__va(runtime->get_time), __pa(tm), __pa(tc)); +} + +static efi_status_t +phys_set_time (efi_time_t *tm) +{ + return efi_call_phys(__va(runtime->set_time), __pa(tm)); +} + +static efi_status_t +phys_get_wakeup_time (efi_bool_t *enabled, efi_bool_t *pending, efi_time_t *tm) +{ + return efi_call_phys(__va(runtime->get_wakeup_time), __pa(enabled), __pa(pending), + __pa(tm)); +} + +static efi_status_t +phys_set_wakeup_time (efi_bool_t enabled, efi_time_t *tm) +{ + return efi_call_phys(__va(runtime->set_wakeup_time), enabled, __pa(tm)); +} + +static efi_status_t +phys_get_variable (efi_char16_t *name, efi_guid_t *vendor, u32 *attr, + unsigned long *data_size, void *data) +{ + return efi_call_phys(__va(runtime->get_variable), __pa(name), __pa(vendor), __pa(attr), + __pa(data_size), __pa(data)); +} + +static efi_status_t +phys_get_next_variable (unsigned long *name_size, efi_char16_t *name, efi_guid_t *vendor) +{ + return efi_call_phys(__va(runtime->get_next_variable), __pa(name_size), __pa(name), + __pa(vendor)); +} + +static efi_status_t +phys_set_variable (efi_char16_t *name, efi_guid_t *vendor, u32 attr, + unsigned long data_size, void *data) +{ + return efi_call_phys(__va(runtime->set_variable), __pa(name), __pa(vendor), attr, + data_size, __pa(data)); +} + +static efi_status_t +phys_get_next_high_mono_count (u64 *count) +{ + return efi_call_phys(__va(runtime->get_next_high_mono_count), __pa(count)); +} + +static void +phys_reset_system (int reset_type, efi_status_t status, + unsigned long data_size, efi_char16_t *data) +{ + efi_call_phys(__va(runtime->reset_system), status, data_size, __pa(data)); +} + +/* + * Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static inline unsigned long +mktime (unsigned int year, unsigned int mon, unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((((unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + +void +efi_gettimeofday (struct timeval *tv) +{ + efi_time_t tm; + + memset(tv, 0, sizeof(tv)); + if ((*efi.get_time)(&tm, 0) != EFI_SUCCESS) + return; + + tv->tv_sec = mktime(tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second); + tv->tv_usec = tm.nanosecond / 1000; +} + +/* + * Walks the EFI memory map and calls CALLBACK once for each EFI + * memory descriptor that has memory that is available for OS use. + */ +void +efi_memmap_walk (efi_freemem_callback_t callback, void *arg) +{ + int prev_valid = 0; + struct range { + u64 start; + u64 end; + } prev, curr; + void *efi_map_start, *efi_map_end, *p; + efi_memory_desc_t *md; + u64 efi_desc_size, start, end; + + efi_map_start = __va(ia64_boot_param.efi_memmap); + efi_map_end = efi_map_start + ia64_boot_param.efi_memmap_size; + efi_desc_size = ia64_boot_param.efi_memdesc_size; + + for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { + md = p; + switch (md->type) { + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + case EFI_CONVENTIONAL_MEMORY: +#ifndef CONFIG_IA64_VIRTUAL_MEM_MAP + if (md->phys_addr > 1024*1024*1024UL) { + printk("Warning: ignoring %luMB of memory above 1GB!\n", + md->num_pages >> 8); + md->type = EFI_UNUSABLE_MEMORY; + continue; + } +#endif + + curr.start = PAGE_OFFSET + md->phys_addr; + curr.end = curr.start + (md->num_pages << 12); + + if (!prev_valid) { + prev = curr; + prev_valid = 1; + } else { + if (curr.start < prev.start) + printk("Oops: EFI memory table not ordered!\n"); + + if (prev.end == curr.start) { + /* merge two consecutive memory ranges */ + prev.end = curr.end; + } else { + start = PAGE_ALIGN(prev.start); + end = prev.end & PAGE_MASK; + if ((end > start) && (*callback)(start, end, arg) < 0) + return; + prev = curr; + } + } + break; + + default: + continue; + } + } + if (prev_valid) { + start = PAGE_ALIGN(prev.start); + end = prev.end & PAGE_MASK; + if (end > start) + (*callback)(start, end, arg); + } +} + +void __init +efi_init (void) +{ + void *efi_map_start, *efi_map_end, *p; + efi_config_table_t *config_tables; + efi_memory_desc_t *md; + efi_char16_t *c16; + u64 efi_desc_size; + char vendor[100] = "unknown"; + int i; + + efi.systab = __va(ia64_boot_param.efi_systab); + + /* + * Verify the EFI Table + */ + if (efi.systab == NULL) + panic("Woah! Can't find EFI system table.\n"); + if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + panic("Woah! EFI system table signature incorrect\n"); + if (efi.systab->hdr.revision != EFI_SYSTEM_TABLE_REVISION) + printk("Warning: EFI system table version mismatch: " + "got %d.%02d, expected %d.%02d\n", + efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, + EFI_SYSTEM_TABLE_REVISION >> 16, EFI_SYSTEM_TABLE_REVISION & 0xffff); + + config_tables = __va(efi.systab->tables); + + /* Show what we know for posterity */ + c16 = __va(efi.systab->fw_vendor); + if (c16) { + for (i = 0;i < sizeof(vendor) && *c16; ++i) + vendor[i] = *c16++; + vendor[i] = '\0'; + } + + printk("EFI v%u.%.02u by %s:", + efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); + + for (i = 0; i < efi.systab->nr_tables; i++) { + if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { + efi.mps = __va(config_tables[i].table); + printk(" MPS=0x%lx", config_tables[i].table); + } else if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { + efi.acpi = __va(config_tables[i].table); + printk(" ACPI=0x%lx", config_tables[i].table); + } else if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { + efi.smbios = __va(config_tables[i].table); + printk(" SMBIOS=0x%lx", config_tables[i].table); + } else if (efi_guidcmp(config_tables[i].guid, SAL_SYSTEM_TABLE_GUID) == 0) { + efi.sal_systab = __va(config_tables[i].table); + printk(" SALsystab=0x%lx", config_tables[i].table); + } + } + printk("\n"); + + runtime = __va(efi.systab->runtime); + efi.get_time = phys_get_time; + efi.set_time = phys_set_time; + efi.get_wakeup_time = phys_get_wakeup_time; + efi.set_wakeup_time = phys_set_wakeup_time; + efi.get_variable = phys_get_variable; + efi.get_next_variable = phys_get_next_variable; + efi.set_variable = phys_set_variable; + efi.get_next_high_mono_count = phys_get_next_high_mono_count; + efi.reset_system = phys_reset_system; + + efi_map_start = __va(ia64_boot_param.efi_memmap); + efi_map_end = efi_map_start + ia64_boot_param.efi_memmap_size; + efi_desc_size = ia64_boot_param.efi_memdesc_size; + +#ifdef EFI_DEBUG + /* print EFI memory map: */ + for (i = 0, p = efi_map_start; p < efi_map_end; ++i, p += efi_desc_size) { + md = p; + printk("mem%02u: type=%u, attr=0x%lx, range=[0x%016lx-0x%016lx) (%luMB)\n", + i, md->type, md->attribute, + md->phys_addr, md->phys_addr + (md->num_pages<<12) - 1, md->num_pages >> 8); + } +#endif +} + +void +efi_enter_virtual_mode (void) +{ + void *efi_map_start, *efi_map_end, *p; + efi_memory_desc_t *md; + efi_status_t status; + u64 efi_desc_size; + + efi_map_start = __va(ia64_boot_param.efi_memmap); + efi_map_end = efi_map_start + ia64_boot_param.efi_memmap_size; + efi_desc_size = ia64_boot_param.efi_memdesc_size; + + for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { + md = p; + if (md->attribute & EFI_MEMORY_RUNTIME) { + /* + * Some descriptors have multiple bits set, so the order of + * the tests is relevant. + */ + if (md->attribute & EFI_MEMORY_WB) { + md->virt_addr = (u64) __va(md->phys_addr); + } else if (md->attribute & EFI_MEMORY_UC) { + md->virt_addr = (u64) ioremap(md->phys_addr, 0); + } else if (md->attribute & EFI_MEMORY_WC) { +#if 0 + md->virt_addr = ia64_remap(md->phys_addr, (_PAGE_A | _PAGE_P + | _PAGE_D + | _PAGE_MA_WC + | _PAGE_PL_0 + | _PAGE_AR_RW)); +#else + printk("EFI_MEMORY_WC mapping\n"); + md->virt_addr = (u64) ioremap(md->phys_addr, 0); +#endif + } else if (md->attribute & EFI_MEMORY_WT) { +#if 0 + md->virt_addr = ia64_remap(md->phys_addr, (_PAGE_A | _PAGE_P + | _PAGE_D | _PAGE_MA_WT + | _PAGE_PL_0 + | _PAGE_AR_RW)); +#else + printk("EFI_MEMORY_WT mapping\n"); + md->virt_addr = (u64) ioremap(md->phys_addr, 0); +#endif + } + } + } + + status = efi_call_phys(__va(runtime->set_virtual_address_map), + ia64_boot_param.efi_memmap_size, + efi_desc_size, ia64_boot_param.efi_memdesc_version, + ia64_boot_param.efi_memmap); + if (status != EFI_SUCCESS) { + printk("Warning: unable to switch EFI into virtual mode (status=%lu)\n", status); + return; + } + + /* + * Now that EFI is in virtual mode, we arrange for EFI functions to be + * called directly: + */ + efi.get_time = __va(runtime->get_time); + efi.set_time = __va(runtime->set_time); + efi.get_wakeup_time = __va(runtime->get_wakeup_time); + efi.set_wakeup_time = __va(runtime->set_wakeup_time); + efi.get_variable = __va(runtime->get_variable); + efi.get_next_variable = __va(runtime->get_next_variable); + efi.set_variable = __va(runtime->set_variable); + efi.get_next_high_mono_count = __va(runtime->get_next_high_mono_count); + efi.reset_system = __va(runtime->reset_system); +} diff --git a/arch/ia64/kernel/efi_stub.S b/arch/ia64/kernel/efi_stub.S new file mode 100644 index 000000000..4e6f1fc63 --- /dev/null +++ b/arch/ia64/kernel/efi_stub.S @@ -0,0 +1,141 @@ +/* + * EFI call stub. + * + * Copyright (C) 1999 David Mosberger <davidm@hpl.hp.com> + * + * This stub allows us to make EFI calls in physical mode with interrupts + * turned off. We need this because we can't call SetVirtualMap() until + * the kernel has booted far enough to allow allocation of struct vma_struct + * entries (which we would need to map stuff with memory attributes other + * than uncached or writeback...). Since the GetTime() service gets called + * earlier than that, we need to be able to make physical mode EFI calls from + * the kernel. + */ + +/* + * PSR settings as per SAL spec (Chapter 8 in the "IA-64 System + * Abstraction Layer Specification", revision 2.6e). Note that + * psr.dfl and psr.dfh MUST be cleared, despite what this manual says. + * Otherwise, SAL dies whenever it's trying to do an IA-32 BIOS call + * (the br.ia instruction fails unless psr.dfl and psr.dfh are + * cleared). Fortunately, SAL promises not to touch the floating + * point regs, so at least we don't have to save f2-f127. + */ +#define PSR_BITS_TO_CLEAR \ + (IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_RT | \ + IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED | \ + IA64_PSR_DFL | IA64_PSR_DFH) + +#define PSR_BITS_TO_SET \ + (IA64_PSR_BN) + +#include <asm/processor.h> + + .text + .psr abi64 + .psr lsb + .lsb + + .text + +/* + * Switch execution mode from virtual to physical or vice versa. + * + * Inputs: + * r16 = new psr to establish + */ + .proc switch_mode +switch_mode: + { + alloc r2=ar.pfs,0,0,0,0 + rsm psr.i | psr.ic // disable interrupts and interrupt collection + mov r15=ip + } + ;; + { + flushrs // must be first insn in group + srlz.i + shr.u r19=r15,61 // r19 <- top 3 bits of current IP + } + ;; + mov cr.ipsr=r16 // set new PSR + add r3=1f-switch_mode,r15 + xor r15=0x7,r19 // flip the region bits + + mov r17=ar.bsp + mov r14=rp // get return address into a general register + + // switch RSE backing store: + ;; + dep r17=r15,r17,61,3 // make ar.bsp physical or virtual + mov r18=ar.rnat // save ar.rnat + ;; + mov ar.bspstore=r17 // this steps on ar.rnat + dep r3=r15,r3,61,3 // make rfi return address physical or virtual + ;; + mov cr.iip=r3 + mov cr.ifs=r0 + dep sp=r15,sp,61,3 // make stack pointer physical or virtual + ;; + mov ar.rnat=r18 // restore ar.rnat + dep r14=r15,r14,61,3 // make function return address physical or virtual + rfi // must be last insn in group + ;; +1: mov rp=r14 + br.ret.sptk.few rp + .endp switch_mode + +/* + * Inputs: + * in0 = address of function descriptor of EFI routine to call + * in1..in7 = arguments to routine + * + * Outputs: + * r8 = EFI_STATUS returned by called function + */ + + .global efi_call_phys + .proc efi_call_phys +efi_call_phys: + + alloc loc0=ar.pfs,8,5,7,0 + ld8 r2=[in0],8 // load EFI function's entry point + mov loc1=rp + ;; + mov loc2=gp // save global pointer + mov loc4=ar.rsc // save RSE configuration + mov ar.rsc=r0 // put RSE in enforced lazy, LE mode + ;; + + ld8 gp=[in0] // load EFI function's global pointer + mov out0=in1 + mov out1=in2 + movl r16=PSR_BITS_TO_CLEAR + + mov loc3=psr // save processor status word + movl r17=PSR_BITS_TO_SET + ;; + mov out2=in3 + or loc3=loc3,r17 + mov b6=r2 + ;; + andcm r16=loc3,r16 // get psr with IT, DT, and RT bits cleared + mov out3=in4 + br.call.sptk.few rp=switch_mode +.ret0: + mov out4=in5 + mov out5=in6 + mov out6=in7 + br.call.sptk.few rp=b6 // call the EFI function +.ret1: + mov ar.rsc=r0 // put RSE in enforced lazy, LE mode + mov r16=loc3 + br.call.sptk.few rp=switch_mode // return to virtual mode +.ret2: + mov ar.rsc=loc4 // restore RSE configuration + mov ar.pfs=loc0 + mov rp=loc1 + mov gp=loc2 + br.ret.sptk.few rp + + .endp efi_call_phys diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S new file mode 100644 index 000000000..87e77c677 --- /dev/null +++ b/arch/ia64/kernel/entry.S @@ -0,0 +1,1261 @@ +/* + * ia64/kernel/entry.S + * + * Kernel entry points. + * + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * Copyright (C) 1999 Asit Mallick <Asit.K.Mallick@intel.com> + * Copyright (C) 1999 Don Dugger <Don.Dugger@intel.com> + */ +/* + * Global (preserved) predicate usage on syscall entry/exit path: + * + * + * pEOI: See entry.h. + * pKern: See entry.h. + * pSys: See entry.h. + * pNonSys: !pSys + * p2: (Alias of pKern!) True if any signals are pending. + * p16/p17: Used by stubs calling ia64_do_signal to indicate if current task + * has PF_PTRACED flag bit set. p16 is true if so, p17 is the complement. + */ + +#include <linux/config.h> + +#include <asm/errno.h> +#include <asm/offsets.h> +#include <asm/processor.h> +#include <asm/unistd.h> + +#include "entry.h" + + .text + .psr abi64 + .psr lsb + .lsb + + /* + * execve() is special because in case of success, we need to + * setup a null register window frame. + */ + .align 16 + .proc ia64_execve +ia64_execve: + alloc loc0=ar.pfs,3,2,4,0 + mov loc1=rp + mov out0=in0 // filename + ;; // stop bit between alloc and call + mov out1=in1 // argv + mov out2=in2 // envp + add out3=16,sp // regs + br.call.sptk.few rp=sys_execve +.ret0: cmp4.ge p6,p0=r8,r0 + mov ar.pfs=loc0 // restore ar.pfs + ;; +(p6) mov ar.pfs=r0 // clear ar.pfs in case of success + sxt4 r8=r8 // return 64-bit result + mov rp=loc1 + + br.ret.sptk.few rp + .endp ia64_execve + + .align 16 + .global sys_clone + .proc sys_clone +sys_clone: + alloc r16=ar.pfs,2,2,3,0;; + movl r28=1f + mov loc1=rp + br.cond.sptk.many save_switch_stack +1: + mov loc0=r16 // save ar.pfs across do_fork + adds out2=IA64_SWITCH_STACK_SIZE+16,sp + adds r2=IA64_SWITCH_STACK_SIZE+IA64_PT_REGS_R12_OFFSET+16,sp + cmp.eq p8,p9=in1,r0 // usp == 0? + mov out0=in0 // out0 = clone_flags + ;; +(p8) ld8 out1=[r2] // fetch usp from pt_regs.r12 +(p9) mov out1=in1 + br.call.sptk.few rp=do_fork +.ret1: + mov ar.pfs=loc0 + adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack + mov rp=loc1 + ;; + br.ret.sptk.many rp + .endp sys_clone + +/* + * prev_task <- switch_to(struct task_struct *next) + */ + .align 16 + .global ia64_switch_to + .proc ia64_switch_to +ia64_switch_to: + alloc r16=ar.pfs,1,0,0,0 + movl r28=1f + br.cond.sptk.many save_switch_stack +1: + // disable interrupts to ensure atomicity for next few instructions: + mov r17=psr // M-unit + ;; + rsm psr.i // M-unit + dep r18=-1,r0,0,61 // build mask 0x1fffffffffffffff + ;; + srlz.d + ;; + adds r22=IA64_TASK_THREAD_KSP_OFFSET,r13 + adds r21=IA64_TASK_THREAD_KSP_OFFSET,in0 + ;; + st8 [r22]=sp // save kernel stack pointer of old task + ld8 sp=[r21] // load kernel stack pointer of new task + and r20=in0,r18 // physical address of "current" + ;; + mov r8=r13 // return pointer to previously running task + mov r13=in0 // set "current" pointer + mov ar.k6=r20 // copy "current" into ar.k6 + ;; + // restore interrupts + mov psr.l=r17 + ;; + srlz.d + + movl r28=1f + br.cond.sptk.many load_switch_stack +1: + br.ret.sptk.few rp + .endp ia64_switch_to + + /* + * Like save_switch_stack, but also save the stack frame that is active + * at the time this function is called. + */ + .align 16 + .proc save_switch_stack_with_current_frame +save_switch_stack_with_current_frame: +1: { + alloc r16=ar.pfs,0,0,0,0 // pass ar.pfs to save_switch_stack + mov r28=ip + } + ;; + adds r28=1f-1b,r28 + br.cond.sptk.many save_switch_stack +1: br.ret.sptk.few rp + .endp save_switch_stack_with_current_frame +/* + * Note that interrupts are enabled during save_switch_stack and + * load_switch_stack. This means that we may get an interrupt with + * "sp" pointing to the new kernel stack while ar.bspstore is still + * pointing to the old kernel backing store area. Since ar.rsc, + * ar.rnat, ar.bsp, and ar.bspstore are all preserved by interrupts, + * this is not a problem. + */ + +/* + * save_switch_stack: + * - r16 holds ar.pfs + * - r28 holds address to return to + * - rp (b0) holds return address to save + */ + .align 16 + .global save_switch_stack + .proc save_switch_stack +save_switch_stack: + flushrs // flush dirty regs to backing store (must be first in insn group) + mov r17=ar.unat // preserve caller's + adds r2=-IA64_SWITCH_STACK_SIZE+16,sp // r2 = &sw->caller_unat + ;; + mov r18=ar.fpsr // preserve fpsr + mov ar.rsc=r0 // put RSE in mode: enforced lazy, little endian, pl 0 + ;; + mov r19=ar.rnat + adds r3=-IA64_SWITCH_STACK_SIZE+24,sp // r3 = &sw->ar_fpsr + + // Note: the instruction ordering is important here: we can't + // store anything to the switch stack before sp is updated + // as otherwise an interrupt might overwrite the memory! + adds sp=-IA64_SWITCH_STACK_SIZE,sp + ;; + st8 [r2]=r17,16 + st8 [r3]=r18,24 + ;; + stf.spill [r2]=f2,32 + stf.spill [r3]=f3,32 + mov r21=b0 + ;; + stf.spill [r2]=f4,32 + stf.spill [r3]=f5,32 + ;; + stf.spill [r2]=f10,32 + stf.spill [r3]=f11,32 + mov r22=b1 + ;; + stf.spill [r2]=f12,32 + stf.spill [r3]=f13,32 + mov r23=b2 + ;; + stf.spill [r2]=f14,32 + stf.spill [r3]=f15,32 + mov r24=b3 + ;; + stf.spill [r2]=f16,32 + stf.spill [r3]=f17,32 + mov r25=b4 + ;; + stf.spill [r2]=f18,32 + stf.spill [r3]=f19,32 + mov r26=b5 + ;; + stf.spill [r2]=f20,32 + stf.spill [r3]=f21,32 + mov r17=ar.lc // I-unit + ;; + stf.spill [r2]=f22,32 + stf.spill [r3]=f23,32 + ;; + stf.spill [r2]=f24,32 + stf.spill [r3]=f25,32 + ;; + stf.spill [r2]=f26,32 + stf.spill [r3]=f27,32 + ;; + stf.spill [r2]=f28,32 + stf.spill [r3]=f29,32 + ;; + stf.spill [r2]=f30,32 + stf.spill [r3]=f31,24 + ;; + st8.spill [r2]=r4,16 + st8.spill [r3]=r5,16 + ;; + st8.spill [r2]=r6,16 + st8.spill [r3]=r7,16 + ;; + st8 [r2]=r21,16 // save b0 + st8 [r3]=r22,16 // save b1 + /* since we're done with the spills, read and save ar.unat: */ + mov r18=ar.unat // M-unit + mov r20=ar.bspstore // M-unit + ;; + st8 [r2]=r23,16 // save b2 + st8 [r3]=r24,16 // save b3 + ;; + st8 [r2]=r25,16 // save b4 + st8 [r3]=r26,16 // save b5 + ;; + st8 [r2]=r16,16 // save ar.pfs + st8 [r3]=r17,16 // save ar.lc + mov r21=pr + ;; + st8 [r2]=r18,16 // save ar.unat + st8 [r3]=r19,16 // save ar.rnat + mov b7=r28 + ;; + st8 [r2]=r20 // save ar.bspstore + st8 [r3]=r21 // save predicate registers + mov ar.rsc=3 // put RSE back into eager mode, pl 0 + br.cond.sptk.few b7 + .endp save_switch_stack + +/* + * load_switch_stack: + * - r28 holds address to return to + */ + .align 16 + .proc load_switch_stack +load_switch_stack: + invala // invalidate ALAT + adds r2=IA64_SWITCH_STACK_B0_OFFSET+16,sp // get pointer to switch_stack.b0 + mov ar.rsc=r0 // put RSE into enforced lazy mode + adds r3=IA64_SWITCH_STACK_B0_OFFSET+24,sp // get pointer to switch_stack.b1 + ;; + ld8 r21=[r2],16 // restore b0 + ld8 r22=[r3],16 // restore b1 + ;; + ld8 r23=[r2],16 // restore b2 + ld8 r24=[r3],16 // restore b3 + ;; + ld8 r25=[r2],16 // restore b4 + ld8 r26=[r3],16 // restore b5 + ;; + ld8 r16=[r2],16 // restore ar.pfs + ld8 r17=[r3],16 // restore ar.lc + ;; + ld8 r18=[r2],16 // restore ar.unat + ld8 r19=[r3],16 // restore ar.rnat + mov b0=r21 + ;; + ld8 r20=[r2] // restore ar.bspstore + ld8 r21=[r3] // restore predicate registers + mov ar.pfs=r16 + ;; + mov ar.bspstore=r20 + ;; + loadrs // invalidate stacked regs outside current frame + adds r2=16-IA64_SWITCH_STACK_SIZE,r2 // get pointer to switch_stack.caller_unat + ;; // stop bit for rnat dependency + mov ar.rnat=r19 + mov ar.unat=r18 // establish unat holding the NaT bits for r4-r7 + adds r3=16-IA64_SWITCH_STACK_SIZE,r3 // get pointer to switch_stack.ar_fpsr + ;; + ld8 r18=[r2],16 // restore caller's unat + ld8 r19=[r3],24 // restore fpsr + mov ar.lc=r17 + ;; + ldf.fill f2=[r2],32 + ldf.fill f3=[r3],32 + mov pr=r21,-1 + ;; + ldf.fill f4=[r2],32 + ldf.fill f5=[r3],32 + ;; + ldf.fill f10=[r2],32 + ldf.fill f11=[r3],32 + mov b1=r22 + ;; + ldf.fill f12=[r2],32 + ldf.fill f13=[r3],32 + mov b2=r23 + ;; + ldf.fill f14=[r2],32 + ldf.fill f15=[r3],32 + mov b3=r24 + ;; + ldf.fill f16=[r2],32 + ldf.fill f17=[r3],32 + mov b4=r25 + ;; + ldf.fill f18=[r2],32 + ldf.fill f19=[r3],32 + mov b5=r26 + ;; + ldf.fill f20=[r2],32 + ldf.fill f21=[r3],32 + ;; + ldf.fill f22=[r2],32 + ldf.fill f23=[r3],32 + ;; + ldf.fill f24=[r2],32 + ldf.fill f25=[r3],32 + ;; + ldf.fill f26=[r2],32 + ldf.fill f27=[r3],32 + ;; + ldf.fill f28=[r2],32 + ldf.fill f29=[r3],32 + ;; + ldf.fill f30=[r2],32 + ldf.fill f31=[r3],24 + ;; + ld8.fill r4=[r2],16 + ld8.fill r5=[r3],16 + mov b7=r28 + ;; + ld8.fill r6=[r2],16 + ld8.fill r7=[r3],16 + mov ar.unat=r18 // restore caller's unat + mov ar.fpsr=r19 // restore fpsr + mov ar.rsc=3 // put RSE back into eager mode, pl 0 + adds sp=IA64_SWITCH_STACK_SIZE,sp // pop switch_stack + br.cond.sptk.few b7 + .endp load_switch_stack + + .align 16 + .global __ia64_syscall + .proc __ia64_syscall +__ia64_syscall: + .regstk 6,0,0,0 + mov r15=in5 // put syscall number in place + break __BREAK_SYSCALL + movl r2=errno + cmp.eq p6,p7=-1,r10 + ;; +(p6) st4 [r2]=r8 +(p6) mov r8=-1 + br.ret.sptk.few rp + .endp __ia64_syscall + + // + // We invoke syscall_trace through this intermediate function to + // ensure that the syscall input arguments are not clobbered. We + // also use it to preserve b6, which contains the syscall entry point. + // + .align 16 + .global invoke_syscall_trace + .proc invoke_syscall_trace +invoke_syscall_trace: + alloc loc0=ar.pfs,8,3,0,0 + ;; // WAW on CFM at the br.call + mov loc1=rp + br.call.sptk.many rp=save_switch_stack_with_current_frame // must preserve b6!! +.ret2: mov loc2=b6 + br.call.sptk.few rp=syscall_trace +.ret3: adds sp=IA64_SWITCH_STACK_SIZE,sp // drop switch_stack frame + mov rp=loc1 + mov ar.pfs=loc0 + mov b6=loc2 + ;; + br.ret.sptk.few rp + .endp invoke_syscall_trace + + // + // Invoke a system call, but do some tracing before and after the call. + // We MUST preserve the current register frame throughout this routine + // because some system calls (such as ia64_execve) directly + // manipulate ar.pfs. + // + // Input: + // r15 = syscall number + // b6 = syscall entry point + // + .global ia64_trace_syscall + .global ia64_strace_leave_kernel + .global ia64_strace_clear_r8 + + .proc ia64_strace_clear_r8 +ia64_strace_clear_r8: // this is where we return after cloning when PF_TRACESYS is on +# ifdef CONFIG_SMP + br.call.sptk.few rp=invoke_schedule_tail +# endif + mov r8=0 + br strace_check_retval + .endp ia64_strace_clear_r8 + + .proc ia64_trace_syscall +ia64_trace_syscall: + br.call.sptk.few rp=invoke_syscall_trace // give parent a chance to catch syscall args +.ret4: br.call.sptk.few rp=b6 // do the syscall +strace_check_retval: +.ret5: cmp.lt p6,p0=r8,r0 // syscall failed? + ;; + adds r2=IA64_PT_REGS_R8_OFFSET+16,sp // r2 = &pt_regs.r8 + adds r3=IA64_PT_REGS_R8_OFFSET+32,sp // r3 = &pt_regs.r10 + mov r10=0 +(p6) br.cond.sptk.few strace_error // syscall failed -> + ;; // avoid RAW on r10 +strace_save_retval: + st8.spill [r2]=r8 // store return value in slot for r8 + st8.spill [r3]=r10 // clear error indication in slot for r10 +ia64_strace_leave_kernel: + br.call.sptk.few rp=invoke_syscall_trace // give parent a chance to catch return value +.ret6: br.cond.sptk.many ia64_leave_kernel + +strace_error: + ld8 r3=[r2] // load pt_regs.r8 + sub r9=0,r8 // negate return value to get errno value + ;; + cmp.ne p6,p0=r3,r0 // is pt_regs.r8!=0? + adds r3=16,r2 // r3=&pt_regs.r10 + ;; +(p6) mov r10=-1 +(p6) mov r8=r9 + br.cond.sptk.few strace_save_retval + .endp ia64_trace_syscall + +/* + * A couple of convenience macros to help implement/understand the state + * restoration that happens at the end of ia64_ret_from_syscall. + */ +#define rARPR r31 +#define rCRIFS r30 +#define rCRIPSR r29 +#define rCRIIP r28 +#define rARRSC r27 +#define rARPFS r26 +#define rARUNAT r25 +#define rARRNAT r24 +#define rARBSPSTORE r23 +#define rKRBS r22 +#define rB6 r21 + + .align 16 + .global ia64_ret_from_syscall + .global ia64_ret_from_syscall_clear_r8 + .global ia64_leave_kernel + .proc ia64_ret_from_syscall +ia64_ret_from_syscall_clear_r8: +#ifdef CONFIG_SMP + // In SMP mode, we need to call schedule_tail to complete the scheduling process. + // Called by ia64_switch_to after do_fork()->copy_thread(). r8 contains the + // address of the previously executing task. + br.call.sptk.few rp=invoke_schedule_tail +.ret7: +#endif + mov r8=0 + ;; // added stop bits to prevent r8 dependency +ia64_ret_from_syscall: + cmp.ge p6,p7=r8,r0 // syscall executed successfully? + adds r2=IA64_PT_REGS_R8_OFFSET+16,sp // r2 = &pt_regs.r8 + adds r3=IA64_PT_REGS_R8_OFFSET+32,sp // r3 = &pt_regs.r10 + ;; +(p6) st8.spill [r2]=r8 // store return value in slot for r8 and set unat bit +(p6) st8.spill [r3]=r0 // clear error indication in slot for r10 and set unat bit +(p7) br.cond.spnt.few handle_syscall_error // handle potential syscall failure + +ia64_leave_kernel: + // check & deliver software interrupts (bottom half handlers): + + movl r2=bh_active // sheesh, why aren't these two in + movl r3=bh_mask // a struct?? + ;; + ld8 r2=[r2] + ld8 r3=[r3] + ;; + and r2=r2,r3 + ;; + cmp.ne p6,p7=r2,r0 // any soft interrupts ready for delivery? +(p6) br.call.dpnt.few rp=invoke_do_bottom_half +1: +(pKern) br.cond.dpnt.many restore_all // yup -> skip check for rescheduling & signal delivery + + // call schedule() until we find a task that doesn't have need_resched set: + +back_from_resched: + { .mii + adds r2=IA64_TASK_NEED_RESCHED_OFFSET,r13 + mov r3=ip + adds r14=IA64_TASK_SIGPENDING_OFFSET,r13 + } + ;; + ld8 r2=[r2] + ld4 r14=[r14] + mov rp=r3 // arrange for schedule() to return to back_from_resched + ;; + /* + * If pEOI is set, we need to write the cr.eoi now and then + * clear pEOI because both invoke_schedule() and + * handle_signal_delivery() may call the scheduler. Since + * we're returning to user-level, we get at most one nested + * interrupt of the same priority level, which doesn't tax the + * kernel stack too much. + */ +(pEOI) mov cr.eoi=r0 + cmp.ne p6,p0=r2,r0 + cmp.ne p2,p0=r14,r0 // NOTE: pKern is an alias for p2!! +(pEOI) cmp.ne pEOI,p0=r0,r0 // clear pEOI before calling schedule() + srlz.d +(p6) br.call.spnt.many b6=invoke_schedule // ignore return value +2: + // check & deliver pending signals: +(p2) br.call.spnt.few rp=handle_signal_delivery +restore_all: + + // start restoring the state saved on the kernel stack (struct pt_regs): + + adds r2=IA64_PT_REGS_R8_OFFSET+16,r12 + adds r3=IA64_PT_REGS_R8_OFFSET+24,r12 + ;; + ld8.fill r8=[r2],16 + ld8.fill r9=[r3],16 + ;; + ld8.fill r10=[r2],16 + ld8.fill r11=[r3],16 + ;; + ld8.fill r16=[r2],16 + ld8.fill r17=[r3],16 + ;; + ld8.fill r18=[r2],16 + ld8.fill r19=[r3],16 + ;; + ld8.fill r20=[r2],16 + ld8.fill r21=[r3],16 + ;; + ld8.fill r22=[r2],16 + ld8.fill r23=[r3],16 + ;; + ld8.fill r24=[r2],16 + ld8.fill r25=[r3],16 + ;; + ld8.fill r26=[r2],16 + ld8.fill r27=[r3],16 + ;; + ld8.fill r28=[r2],16 + ld8.fill r29=[r3],16 + ;; + ld8.fill r30=[r2],16 + ld8.fill r31=[r3],16 + ;; + ld8 r1=[r2],16 // ar.ccv + ld8 r13=[r3],16 // ar.fpsr + ;; + ld8 r14=[r2],16 // b0 + ld8 r15=[r3],16+8 // b7 + ;; + ldf.fill f6=[r2],32 + ldf.fill f7=[r3],32 + ;; + ldf.fill f8=[r2],32 + ldf.fill f9=[r3],32 + ;; + mov ar.ccv=r1 + mov ar.fpsr=r13 + mov b0=r14 + // turn off interrupts, interrupt collection, & data translation + rsm psr.i | psr.ic | psr.dt + ;; + srlz.i // EAS 2.5 + mov b7=r15 + ;; + invala // invalidate ALAT + dep r12=0,r12,61,3 // convert sp to physical address + bsw.0;; // switch back to bank 0 (must be last in insn group) + ;; +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC + nop.i 0x0 + ;; + nop.i 0x0 + ;; + nop.i 0x0 + ;; +#endif + adds r16=16,r12 + adds r17=24,r12 + ;; + ld8 rCRIPSR=[r16],16 // load cr.ipsr + ld8 rCRIIP=[r17],16 // load cr.iip + ;; + ld8 rCRIFS=[r16],16 // load cr.ifs + ld8 rARUNAT=[r17],16 // load ar.unat + ;; + ld8 rARPFS=[r16],16 // load ar.pfs + ld8 rARRSC=[r17],16 // load ar.rsc + ;; + ld8 rARRNAT=[r16],16 // load ar.rnat (may be garbage) + ld8 rARBSPSTORE=[r17],16 // load ar.bspstore (may be garbage) + ;; + ld8 rARPR=[r16],16 // load predicates + ld8 rB6=[r17],16 // load b6 + ;; + ld8 r18=[r16],16 // load ar.rsc value for "loadrs" + ld8.fill r1=[r17],16 // load r1 + ;; + ld8.fill r2=[r16],16 + ld8.fill r3=[r17],16 + ;; + ld8.fill r12=[r16],16 + ld8.fill r13=[r17],16 + extr.u r19=rCRIPSR,32,2 // extract ps.cpl + ;; + ld8.fill r14=[r16],16 + ld8.fill r15=[r17],16 + cmp.eq p6,p7=r0,r19 // are we returning to kernel mode? (psr.cpl==0) + ;; + mov b6=rB6 + mov ar.pfs=rARPFS +(p6) br.cond.dpnt.few skip_rbs_switch + + /* + * Restore user backing store. + * + * NOTE: alloc, loadrs, and cover can't be predicated. + * + * XXX This needs some scheduling/tuning once we believe it + * really does work as intended. + */ + mov r16=ar.bsp // get existing backing store pointer +(pNonSys) br.cond.dpnt.few dont_preserve_current_frame + cover // add current frame into dirty partition + ;; + mov rCRIFS=cr.ifs // fetch the cr.ifs value that "cover" produced + mov r17=ar.bsp // get new backing store pointer + ;; + sub r16=r17,r16 // calculate number of bytes that were added to rbs + ;; + shl r16=r16,16 // shift additional frame size into position for loadrs + ;; + add r18=r16,r18 // adjust the loadrs value + ;; +#ifdef CONFIG_IA64_SOFTSDV_HACKS + // Reset ITM if we've missed a timer tick. Workaround for SoftSDV bug + mov r16 = r2 + mov r2 = ar.itc + mov r17 = cr.itm + ;; + cmp.gt p6,p7 = r2, r17 +(p6) addl r17 = 100, r2 + ;; + mov cr.itm = r17 + mov r2 = r16 +#endif +dont_preserve_current_frame: + alloc r16=ar.pfs,0,0,0,0 // drop the current call frame (noop for syscalls) + ;; + mov ar.rsc=r18 // load ar.rsc to be used for "loadrs" +#ifdef CONFIG_IA32_SUPPORT + tbit.nz p6,p0=rCRIPSR,IA64_PSR_IS_BIT + ;; +(p6) mov ar.rsc=r0 // returning to IA32 mode +#endif + ;; + loadrs + ;; + mov ar.bspstore=rARBSPSTORE + ;; + mov ar.rnat=rARRNAT // must happen with RSE in lazy mode + +skip_rbs_switch: + mov ar.rsc=rARRSC + mov ar.unat=rARUNAT + mov cr.ifs=rCRIFS // restore cr.ifs only if not a (synchronous) syscall +(pEOI) mov cr.eoi=r0 + mov pr=rARPR,-1 + mov cr.iip=rCRIIP + mov cr.ipsr=rCRIPSR + ;; + rfi;; // must be last instruction in an insn group + +handle_syscall_error: + /* + * Some system calls (e.g., ptrace, mmap) can return arbitrary + * values which could lead us to mistake a negative return + * value as a failed syscall. Those syscall must deposit + * a non-zero value in pt_regs.r8 to indicate an error. + * If pt_regs.r8 is zero, we assume that the call completed + * successfully. + */ + ld8 r3=[r2] // load pt_regs.r8 + sub r9=0,r8 // negate return value to get errno + ;; + mov r10=-1 // return -1 in pt_regs.r10 to indicate error + cmp.eq p6,p7=r3,r0 // is pt_regs.r8==0? + adds r3=16,r2 // r3=&pt_regs.r10 + ;; +(p6) mov r9=r8 +(p6) mov r10=0 + ;; + st8.spill [r2]=r9 // store errno in pt_regs.r8 and set unat bit + st8.spill [r3]=r10 // store error indication in pt_regs.r10 and set unat bit + br.cond.sptk.many ia64_leave_kernel + .endp __ret_from_syscall + +#ifdef CONFIG_SMP + /* + * Invoke schedule_tail(task) while preserving in0-in7, which may be needed + * in case a system call gets restarted. + */ + .proc invoke_schedule_tail +invoke_schedule_tail: + alloc loc0=ar.pfs,8,2,1,0 + mov loc1=rp + mov out0=r8 // Address of previous task + ;; + br.call.sptk.few rp=schedule_tail +.ret8: + mov ar.pfs=loc0 + mov rp=loc1 + br.ret.sptk.many rp + .endp invoke_schedule_tail +#endif /* CONFIG_SMP */ + + /* + * Invoke do_bottom_half() while preserving in0-in7, which may be needed + * in case a system call gets restarted. + */ + .proc invoke_do_bottom_half +invoke_do_bottom_half: + alloc loc0=ar.pfs,8,2,0,0 + mov loc1=rp + ;; + br.call.sptk.few rp=do_bottom_half +.ret9: + mov ar.pfs=loc0 + mov rp=loc1 + br.ret.sptk.many rp + .endp invoke_do_bottom_half + + /* + * Invoke schedule() while preserving in0-in7, which may be needed + * in case a system call gets restarted. + */ + .proc invoke_schedule +invoke_schedule: + alloc loc0=ar.pfs,8,2,0,0 + mov loc1=rp + ;; + br.call.sptk.few rp=schedule +.ret10: + mov ar.pfs=loc0 + mov rp=loc1 + br.ret.sptk.many rp + .endp invoke_schedule + + // + // Setup stack and call ia64_do_signal. Note that pSys and pNonSys need to + // be set up by the caller. We declare 8 input registers so the system call + // args get preserved, in case we need to restart a system call. + // + .align 16 + .proc handle_signal_delivery +handle_signal_delivery: + alloc loc0=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart! + mov r9=ar.unat + + // If the process is being ptraced, the signal may not actually be delivered to + // the process. Instead, SIGCHLD will be sent to the parent. We need to + // setup a switch_stack so ptrace can inspect the processes state if necessary. + adds r2=IA64_TASK_FLAGS_OFFSET,r13 + ;; + ld8 r2=[r2] + mov out0=0 // there is no "oldset" + adds out1=16,sp // out1=&pt_regs + ;; +(pSys) mov out2=1 // out2==1 => we're in a syscall + tbit.nz p16,p17=r2,PF_PTRACED_BIT +(p16) br.cond.spnt.many setup_switch_stack + ;; +back_from_setup_switch_stack: +(pNonSys) mov out2=0 // out2==0 => not a syscall + adds r3=-IA64_SWITCH_STACK_SIZE+IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp +(p17) adds sp=-IA64_SWITCH_STACK_SIZE,sp // make space for (dummy) switch_stack + ;; +(p17) st8 [r3]=r9 // save ar.unat in sw->caller_unat + mov loc1=rp // save return address + br.call.sptk.few rp=ia64_do_signal +.ret11: + adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp + ;; + ld8 r9=[r3] // load new unat from sw->caller_unat + mov rp=loc1 + ;; +(p17) adds sp=IA64_SWITCH_STACK_SIZE,sp // drop (dummy) switch_stack +(p17) mov ar.unat=r9 +(p17) mov ar.pfs=loc0 +(p17) br.ret.sptk.many rp + + // restore the switch stack (ptrace may have modified it): + movl r28=1f + br.cond.sptk.many load_switch_stack +1: br.ret.sptk.many rp + // NOT REACHED + +setup_switch_stack: + movl r28=back_from_setup_switch_stack + mov r16=loc0 + br.cond.sptk.many save_switch_stack + // NOT REACHED + + .endp handle_signal_delivery + + .align 16 + .proc sys_rt_sigsuspend + .global sys_rt_sigsuspend +sys_rt_sigsuspend: + alloc loc0=ar.pfs,2,2,3,0 + mov r9=ar.unat + + // If the process is being ptraced, the signal may not actually be delivered to + // the process. Instead, SIGCHLD will be sent to the parent. We need to + // setup a switch_stack so ptrace can inspect the processes state if necessary. + adds r2=IA64_TASK_FLAGS_OFFSET,r13 + ;; + ld8 r2=[r2] + mov out0=in0 // mask + mov out1=in1 // sigsetsize + ;; + adds out2=16,sp // out1=&pt_regs + tbit.nz p16,p17=r2,PF_PTRACED_BIT +(p16) br.cond.spnt.many sigsuspend_setup_switch_stack + ;; +back_from_sigsuspend_setup_switch_stack: + adds r3=-IA64_SWITCH_STACK_SIZE+IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp +(p17) adds sp=-IA64_SWITCH_STACK_SIZE,sp // make space for (dummy) switch_stack + ;; +(p17) st8 [r3]=r9 // save ar.unat in sw->caller_unat + mov loc1=rp // save return address + br.call.sptk.many rp=ia64_rt_sigsuspend +.ret12: + adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp + ;; + ld8 r9=[r3] // load new unat from sw->caller_unat + mov rp=loc1 + ;; +(p17) adds sp=IA64_SWITCH_STACK_SIZE,sp // drop (dummy) switch_stack +(p17) mov ar.unat=r9 +(p17) mov ar.pfs=loc0 +(p17) br.ret.sptk.many rp + + // restore the switch stack (ptrace may have modified it): + movl r28=1f + br.cond.sptk.many load_switch_stack +1: br.ret.sptk.many rp + // NOT REACHED + +sigsuspend_setup_switch_stack: + movl r28=back_from_sigsuspend_setup_switch_stack + mov r16=loc0 + br.cond.sptk.many save_switch_stack + // NOT REACHED + + .endp sys_rt_sigsuspend + + .align 16 + .proc sys_rt_sigreturn +sys_rt_sigreturn: + alloc loc0=ar.pfs,8,1,1,0 // preserve all eight input regs in case of syscall restart! + adds out0=16,sp // out0 = &pt_regs + ;; + adds sp=-IA64_SWITCH_STACK_SIZE,sp // make space for unat and padding + br.call.sptk.few rp=ia64_rt_sigreturn +.ret13: + adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp + ;; + ld8 r9=[r3] // load new ar.unat + mov rp=r8 + ;; + adds sp=IA64_SWITCH_STACK_SIZE,sp // drop (dummy) switch-stack frame + mov ar.unat=r9 + mov ar.pfs=loc0 + br.ret.sptk.many rp + .endp sys_rt_sigreturn + + .align 16 + .global ia64_prepare_handle_unaligned + .proc ia64_prepare_handle_unaligned +ia64_prepare_handle_unaligned: + movl r28=1f + // + // r16 = fake ar.pfs, we simply need to make sure + // privilege is still 0 + // + mov r16=r0 + br.cond.sptk.few save_switch_stack +1: br.call.sptk.few rp=ia64_handle_unaligned // stack frame setup in ivt +.ret14: + movl r28=2f + br.cond.sptk.many load_switch_stack +2: br.cond.sptk.many rp // goes to ia64_leave_kernel + .endp ia64_prepare_handle_unaligned + +#ifdef CONFIG_KDB + // + // This gets called from ivt.S with: + // SAVE MIN with cover done + // SAVE REST done + // no parameters + // r15 has return value = ia64_leave_kernel + // + .align 16 + .global ia64_invoke_kdb + .proc ia64_invoke_kdb +ia64_invoke_kdb: + alloc r16=ar.pfs,0,0,4,0 + movl r28=1f // save_switch_stack protocol + ;; // avoid WAW on CFM + br.cond.sptk.many save_switch_stack // to flushrs +1: mov out0=4 // kdb entry reason + mov out1=0 // err number + adds out2=IA64_SWITCH_STACK_SIZE+16,sp // pt_regs + add out3=16,sp // switch_stack + br.call.sptk.few rp=kdb +.ret15: + movl r28=1f // load_switch_stack proto + br.cond.sptk.many load_switch_stack +1: br.ret.sptk.many rp + .endp ia64_invoke_kdb + + // + // When KDB is compiled in, we intercept each fault and give + // kdb a chance to run before calling the normal fault handler. + // + .align 16 + .global ia64_invoke_kdb_fault_handler + .proc ia64_invoke_kdb_fault_handler +ia64_invoke_kdb_fault_handler: + alloc r16=ar.pfs,5,1,5,0 + movl r28=1f + mov loc0=rp // save this + br.cond.sptk.many save_switch_stack // to flushrs + ;; // avoid WAW on CFM +1: mov out0=in0 // vector number + mov out1=in1 // cr.isr + mov out2=in2 // cr.ifa + mov out3=in3 // cr.iim + mov out4=in4 // cr.itir + br.call.sptk.few rp=ia64_kdb_fault_handler +.ret16: + + movl r28=1f + br.cond.sptk.many load_switch_stack +1: cmp.ne p6,p0=r8,r0 // did ia64_kdb_fault_handler return 0? + mov rp=loc0 +(p6) br.ret.spnt.many rp // no, we're done + ;; // avoid WAW on rp + mov out0=in0 // vector number + mov out1=in1 // cr.isr + mov out2=in2 // cr.ifa + mov out3=in3 // cr.iim + mov out4=in4 // cr.itir + mov in0=ar.pfs // preserve ar.pfs returned by load_switch_stack + br.call.sptk.few rp=ia64_fault // yup -> we need to invoke normal fault handler now +.ret17: + mov ar.pfs=in0 + mov rp=loc0 + br.ret.sptk.many rp + + .endp ia64_invoke_kdb_fault_handler + +#endif /* CONFIG_KDB */ + + .rodata + .align 8 + .globl sys_call_table +sys_call_table: + data8 sys_ni_syscall // This must be sys_ni_syscall! See ivt.S. + data8 sys_exit // 1025 + data8 sys_read + data8 sys_write + data8 sys_open + data8 sys_close + data8 sys_creat // 1030 + data8 sys_link + data8 sys_unlink + data8 ia64_execve + data8 sys_chdir + data8 sys_fchdir // 1035 + data8 sys_utimes + data8 sys_mknod + data8 sys_chmod + data8 sys_chown + data8 sys_lseek // 1040 + data8 sys_getpid + data8 sys_getppid + data8 sys_mount + data8 sys_umount + data8 sys_setuid // 1045 + data8 sys_getuid + data8 sys_geteuid + data8 sys_ptrace + data8 sys_access + data8 sys_sync // 1050 + data8 sys_fsync + data8 sys_fdatasync + data8 sys_kill + data8 sys_rename + data8 sys_mkdir // 1055 + data8 sys_rmdir + data8 sys_dup + data8 sys_pipe + data8 sys_times + data8 ia64_brk // 1060 + data8 sys_setgid + data8 sys_getgid + data8 sys_getegid + data8 sys_acct + data8 sys_ioctl // 1065 + data8 sys_fcntl + data8 sys_umask + data8 sys_chroot + data8 sys_ustat + data8 sys_dup2 // 1070 + data8 sys_setreuid + data8 sys_setregid + data8 sys_getresuid + data8 sys_setresuid + data8 sys_getresgid // 1075 + data8 sys_setresgid + data8 sys_getgroups + data8 sys_setgroups + data8 sys_getpgid + data8 sys_setpgid // 1080 + data8 sys_setsid + data8 sys_getsid + data8 sys_sethostname + data8 sys_setrlimit + data8 sys_getrlimit // 1085 + data8 sys_getrusage + data8 sys_gettimeofday + data8 sys_settimeofday + data8 sys_select + data8 sys_poll // 1090 + data8 sys_symlink + data8 sys_readlink + data8 sys_uselib + data8 sys_swapon + data8 sys_swapoff // 1095 + data8 sys_reboot + data8 sys_truncate + data8 sys_ftruncate + data8 sys_fchmod + data8 sys_fchown // 1100 + data8 ia64_getpriority + data8 sys_setpriority + data8 sys_statfs + data8 sys_fstatfs + data8 sys_ioperm // 1105 + data8 sys_semget + data8 sys_semop + data8 sys_semctl + data8 sys_msgget + data8 sys_msgsnd // 1110 + data8 sys_msgrcv + data8 sys_msgctl + data8 sys_shmget + data8 ia64_shmat + data8 sys_shmdt // 1115 + data8 sys_shmctl + data8 sys_syslog + data8 sys_setitimer + data8 sys_getitimer + data8 sys_newstat // 1120 + data8 sys_newlstat + data8 sys_newfstat + data8 sys_vhangup + data8 sys_lchown + data8 sys_vm86 // 1125 + data8 sys_wait4 + data8 sys_sysinfo + data8 sys_clone + data8 sys_setdomainname + data8 sys_newuname // 1130 + data8 sys_adjtimex + data8 sys_create_module + data8 sys_init_module + data8 sys_delete_module + data8 sys_get_kernel_syms // 1135 + data8 sys_query_module + data8 sys_quotactl + data8 sys_bdflush + data8 sys_sysfs + data8 sys_personality // 1140 + data8 ia64_ni_syscall // sys_afs_syscall + data8 sys_setfsuid + data8 sys_setfsgid + data8 sys_getdents + data8 sys_flock // 1145 + data8 sys_readv + data8 sys_writev + data8 sys_pread + data8 sys_pwrite + data8 sys_sysctl // 1150 + data8 sys_mmap + data8 sys_munmap + data8 sys_mlock + data8 sys_mlockall + data8 sys_mprotect // 1155 + data8 sys_mremap + data8 sys_msync + data8 sys_munlock + data8 sys_munlockall + data8 sys_sched_getparam // 1160 + data8 sys_sched_setparam + data8 sys_sched_getscheduler + data8 sys_sched_setscheduler + data8 sys_sched_yield + data8 sys_sched_get_priority_max // 1165 + data8 sys_sched_get_priority_min + data8 sys_sched_rr_get_interval + data8 sys_nanosleep + data8 sys_nfsservctl + data8 sys_prctl // 1170 + data8 sys_getpagesize + data8 sys_mmap2 + data8 sys_pciconfig_read + data8 sys_pciconfig_write + data8 sys_perfmonctl // 1175 + data8 sys_sigaltstack + data8 sys_rt_sigaction + data8 sys_rt_sigpending + data8 sys_rt_sigprocmask + data8 sys_rt_sigqueueinfo // 1180 + data8 sys_rt_sigreturn + data8 sys_rt_sigsuspend + data8 sys_rt_sigtimedwait + data8 sys_getcwd + data8 sys_capget // 1185 + data8 sys_capset + data8 sys_sendfile + data8 sys_ni_syscall // sys_getpmsg (STREAMS) + data8 sys_ni_syscall // sys_putpmsg (STREAMS) + data8 sys_socket // 1190 + data8 sys_bind + data8 sys_connect + data8 sys_listen + data8 sys_accept + data8 sys_getsockname // 1195 + data8 sys_getpeername + data8 sys_socketpair + data8 sys_send + data8 sys_sendto + data8 sys_recv // 1200 + data8 sys_recvfrom + data8 sys_shutdown + data8 sys_setsockopt + data8 sys_getsockopt + data8 sys_sendmsg // 1205 + data8 sys_recvmsg + data8 sys_pivot_root + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1210 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1215 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1220 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1225 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1230 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1235 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1240 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1245 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1250 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1255 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1260 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1265 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1270 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall // 1275 + data8 ia64_ni_syscall + data8 ia64_ni_syscall + data8 ia64_ni_syscall + diff --git a/arch/ia64/kernel/entry.h b/arch/ia64/kernel/entry.h new file mode 100644 index 000000000..ecef44f60 --- /dev/null +++ b/arch/ia64/kernel/entry.h @@ -0,0 +1,8 @@ +/* + * Preserved registers that are shared between code in ivt.S and entry.S. Be + * careful not to step on these! + */ +#define pEOI p1 /* should leave_kernel write EOI? */ +#define pKern p2 /* will leave_kernel return to kernel-mode? */ +#define pSys p4 /* are we processing a (synchronous) system call? */ +#define pNonSys p5 /* complement of pSys */ diff --git a/arch/ia64/kernel/fw-emu.c b/arch/ia64/kernel/fw-emu.c new file mode 100644 index 000000000..212ff299c --- /dev/null +++ b/arch/ia64/kernel/fw-emu.c @@ -0,0 +1,444 @@ +/* + * PAL & SAL emulation. + * + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * For the HP simulator, this file gets include in boot/bootloader.c. + * For SoftSDV, this file gets included in sys_softsdv.c. + */ +#include <linux/config.h> + +#ifdef CONFIG_PCI +# include <linux/pci.h> +#endif + +#include <asm/efi.h> +#include <asm/io.h> +#include <asm/pal.h> +#include <asm/sal.h> + +#define MB (1024*1024UL) + +#define NUM_MEM_DESCS 3 + +static char fw_mem[( sizeof(efi_system_table_t) + + sizeof(efi_runtime_services_t) + + 1*sizeof(efi_config_table_t) + + sizeof(struct ia64_sal_systab) + + sizeof(struct ia64_sal_desc_entry_point) + + NUM_MEM_DESCS*(sizeof(efi_memory_desc_t)) + + 1024)] __attribute__ ((aligned (8))); + +#ifdef CONFIG_IA64_HP_SIM + +/* Simulator system calls: */ + +#define SSC_EXIT 66 + +/* + * Simulator system call. + */ +static long +ssc (long arg0, long arg1, long arg2, long arg3, int nr) +{ + register long r8 asm ("r8"); + + asm volatile ("mov r15=%1\n\t" + "break 0x80001" + : "=r"(r8) + : "r"(nr), "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + return r8; +} + +#define SECS_PER_HOUR (60 * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) + +/* Compute the `struct tm' representation of *T, + offset OFFSET seconds east of UTC, + and store year, yday, mon, mday, wday, hour, min, sec into *TP. + Return nonzero if successful. */ +int +offtime (unsigned long t, efi_time_t *tp) +{ + const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + long int days, rem, y; + const unsigned short int *ip; + + days = t / SECS_PER_DAY; + rem = t % SECS_PER_DAY; + while (rem < 0) { + rem += SECS_PER_DAY; + --days; + } + while (rem >= SECS_PER_DAY) { + rem -= SECS_PER_DAY; + ++days; + } + tp->hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + tp->minute = rem / 60; + tp->second = rem % 60; + /* January 1, 1970 was a Thursday. */ + y = 1970; + +# define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +# define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) +# define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + + while (days < 0 || days >= (__isleap (y) ? 366 : 365)) { + /* Guess a corrected year, assuming 365 days per year. */ + long int yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + tp->year = y; + ip = __mon_yday[__isleap(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + tp->month = y + 1; + tp->day = days + 1; + return 1; +} + +#endif /* CONFIG_IA64_HP_SIM */ + +/* + * Very ugly, but we need this in the simulator only. Once we run on + * real hw, this can all go away. + */ +extern void pal_emulator_static (void); + +asm (" + .proc pal_emulator_static +pal_emulator_static: + mov r8=-1 + cmp.eq p6,p7=6,r28 /* PAL_PTCE_INFO */ +(p7) br.cond.sptk.few 1f + ;; + mov r8=0 /* status = 0 */ + movl r9=0x100000000 /* tc.base */ + movl r10=0x0000000200000003 /* count[0], count[1] */ + movl r11=0x1000000000002000 /* stride[0], stride[1] */ + br.cond.sptk.few rp + +1: cmp.eq p6,p7=14,r28 /* PAL_FREQ_RATIOS */ +(p7) br.cond.sptk.few 1f + mov r8=0 /* status = 0 */ + movl r9 =0x100000064 /* proc_ratio (1/100) */ + movl r10=0x100000100 /* bus_ratio<<32 (1/256) */ + movl r11=0x100000064 /* itc_ratio<<32 (1/100) */ +1: br.cond.sptk.few rp + .endp pal_emulator_static\n"); + +/* Macro to emulate SAL call using legacy IN and OUT calls to CF8, CFC etc.. */ + +#define BUILD_CMD(addr) ((0x80000000 | (addr)) & ~3) + +#define REG_OFFSET(addr) (0x00000000000000FF & (addr)) +#define DEVICE_FUNCTION(addr) (0x000000000000FF00 & (addr)) +#define BUS_NUMBER(addr) (0x0000000000FF0000 & (addr)) + +static efi_status_t +efi_get_time (efi_time_t *tm, efi_time_cap_t *tc) +{ +#ifdef CONFIG_IA64_HP_SIM + struct { + int tv_sec; /* must be 32bits to work */ + int tv_usec; + } tv32bits; + + ssc((unsigned long) &tv32bits, 0, 0, 0, SSC_GET_TOD); + + memset(tm, 0, sizeof(*tm)); + offtime(tv32bits.tv_sec, tm); + + if (tc) + memset(tc, 0, sizeof(*tc)); +#else +# error Not implemented yet... +#endif + return EFI_SUCCESS; +} + +static void +efi_reset_system (int reset_type, efi_status_t status, unsigned long data_size, efi_char16_t *data) +{ +#ifdef CONFIG_IA64_HP_SIM + ssc(status, 0, 0, 0, SSC_EXIT); +#else +# error Not implemented yet... +#endif +} + +static efi_status_t +efi_unimplemented (void) +{ + return EFI_UNSUPPORTED; +} + +static long +sal_emulator (long index, unsigned long in1, unsigned long in2, + unsigned long in3, unsigned long in4, unsigned long in5, + unsigned long in6, unsigned long in7) +{ + register long r9 asm ("r9") = 0; + register long r10 asm ("r10") = 0; + register long r11 asm ("r11") = 0; + long status; + + /* + * Don't do a "switch" here since that gives us code that + * isn't self-relocatable. + */ + status = 0; + if (index == SAL_FREQ_BASE) { + switch (in1) { + case SAL_FREQ_BASE_PLATFORM: + r9 = 100000000; + break; + + case SAL_FREQ_BASE_INTERVAL_TIMER: + /* + * Is this supposed to be the cr.itc frequency + * or something platform specific? The SAL + * doc ain't exactly clear on this... + */ +#if defined(CONFIG_IA64_SOFTSDV_HACKS) + r9 = 4000000; +#elif defined(CONFIG_IA64_SDV) + r9 = 300000000; +#else + r9 = 700000000; +#endif + break; + + case SAL_FREQ_BASE_REALTIME_CLOCK: + r9 = 1; + break; + + default: + status = -1; + break; + } + } else if (index == SAL_SET_VECTORS) { + ; + } else if (index == SAL_GET_STATE_INFO) { + ; + } else if (index == SAL_GET_STATE_INFO_SIZE) { + ; + } else if (index == SAL_CLEAR_STATE_INFO) { + ; + } else if (index == SAL_MC_RENDEZ) { + ; + } else if (index == SAL_MC_SET_PARAMS) { + ; + } else if (index == SAL_CACHE_FLUSH) { + ; + } else if (index == SAL_CACHE_INIT) { + ; +#ifdef CONFIG_PCI + } else if (index == SAL_PCI_CONFIG_READ) { + /* + * in1 contains the PCI configuration address and in2 + * the size of the read. The value that is read is + * returned via the general register r9. + */ + outl(BUILD_CMD(in1), 0xCF8); + if (in2 == 1) /* Reading byte */ + r9 = inb(0xCFC + ((REG_OFFSET(in1) & 3))); + else if (in2 == 2) /* Reading word */ + r9 = inw(0xCFC + ((REG_OFFSET(in1) & 2))); + else /* Reading dword */ + r9 = inl(0xCFC); + status = PCIBIOS_SUCCESSFUL; + } else if (index == SAL_PCI_CONFIG_WRITE) { + /* + * in1 contains the PCI configuration address, in2 the + * size of the write, and in3 the actual value to be + * written out. + */ + outl(BUILD_CMD(in1), 0xCF8); + if (in2 == 1) /* Writing byte */ + outb(in3, 0xCFC + ((REG_OFFSET(in1) & 3))); + else if (in2 == 2) /* Writing word */ + outw(in3, 0xCFC + ((REG_OFFSET(in1) & 2))); + else /* Writing dword */ + outl(in3, 0xCFC); + status = PCIBIOS_SUCCESSFUL; +#endif /* CONFIG_PCI */ + } else if (index == SAL_UPDATE_PAL) { + ; + } else { + status = -1; + } + asm volatile ("" :: "r"(r9), "r"(r10), "r"(r11)); + return status; +} + + +/* + * This is here to work around a bug in egcs-1.1.1b that causes the + * compiler to crash (seems like a bug in the new alias analysis code. + */ +void * +id (long addr) +{ + return (void *) addr; +} + +void +sys_fw_init (const char *args, int arglen) +{ + efi_system_table_t *efi_systab; + efi_runtime_services_t *efi_runtime; + efi_config_table_t *efi_tables; + struct ia64_sal_systab *sal_systab; + efi_memory_desc_t *efi_memmap, *md; + unsigned long *pal_desc, *sal_desc; + struct ia64_sal_desc_entry_point *sal_ed; + struct ia64_boot_param *bp; + unsigned char checksum = 0; + char *cp, *cmd_line; + + memset(fw_mem, 0, sizeof(fw_mem)); + + pal_desc = (unsigned long *) &pal_emulator_static; + sal_desc = (unsigned long *) &sal_emulator; + + cp = fw_mem; + efi_systab = (void *) cp; cp += sizeof(*efi_systab); + efi_runtime = (void *) cp; cp += sizeof(*efi_runtime); + efi_tables = (void *) cp; cp += sizeof(*efi_tables); + sal_systab = (void *) cp; cp += sizeof(*sal_systab); + sal_ed = (void *) cp; cp += sizeof(*sal_ed); + efi_memmap = (void *) cp; cp += NUM_MEM_DESCS*sizeof(*efi_memmap); + cmd_line = (void *) cp; + + if (args) { + if (arglen >= 1024) + arglen = 1023; + memcpy(cmd_line, args, arglen); + } else { + arglen = 0; + } + cmd_line[arglen] = '\0'; + + memset(efi_systab, 0, sizeof(efi_systab)); + efi_systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE; + efi_systab->hdr.revision = EFI_SYSTEM_TABLE_REVISION; + efi_systab->hdr.headersize = sizeof(efi_systab->hdr); + efi_systab->fw_vendor = __pa("H\0e\0w\0l\0e\0t\0t\0-\0P\0a\0c\0k\0a\0r\0d\0\0"); + efi_systab->fw_revision = 1; + efi_systab->runtime = __pa(efi_runtime); + efi_systab->nr_tables = 1; + efi_systab->tables = __pa(efi_tables); + + efi_runtime->hdr.signature = EFI_RUNTIME_SERVICES_SIGNATURE; + efi_runtime->hdr.revision = EFI_RUNTIME_SERVICES_REVISION; + efi_runtime->hdr.headersize = sizeof(efi_runtime->hdr); + efi_runtime->get_time = __pa(&efi_get_time); + efi_runtime->set_time = __pa(&efi_unimplemented); + efi_runtime->get_wakeup_time = __pa(&efi_unimplemented); + efi_runtime->set_wakeup_time = __pa(&efi_unimplemented); + efi_runtime->set_virtual_address_map = __pa(&efi_unimplemented); + efi_runtime->get_variable = __pa(&efi_unimplemented); + efi_runtime->get_next_variable = __pa(&efi_unimplemented); + efi_runtime->set_variable = __pa(&efi_unimplemented); + efi_runtime->get_next_high_mono_count = __pa(&efi_unimplemented); + efi_runtime->reset_system = __pa(&efi_reset_system); + + efi_tables->guid = SAL_SYSTEM_TABLE_GUID; + efi_tables->table = __pa(sal_systab); + + /* fill in the SAL system table: */ + memcpy(sal_systab->signature, "SST_", 4); + sal_systab->size = sizeof(*sal_systab); + sal_systab->sal_rev_minor = 1; + sal_systab->sal_rev_major = 0; + sal_systab->entry_count = 1; + sal_systab->ia32_bios_present = 0; + +#ifdef CONFIG_IA64_GENERIC + strcpy(sal_systab->oem_id, "Generic"); + strcpy(sal_systab->product_id, "IA-64 system"); +#endif + +#ifdef CONFIG_IA64_HP_SIM + strcpy(sal_systab->oem_id, "Hewlett-Packard"); + strcpy(sal_systab->product_id, "HP-simulator"); +#endif + +#ifdef CONFIG_IA64_SDV + strcpy(sal_systab->oem_id, "Intel"); + strcpy(sal_systab->product_id, "SDV"); +#endif + +#ifdef CONFIG_IA64_SGI_SN1_SIM + strcpy(sal_systab->oem_id, "SGI"); + strcpy(sal_systab->product_id, "SN1"); +#endif + + /* fill in an entry point: */ + sal_ed->type = SAL_DESC_ENTRY_POINT; + sal_ed->pal_proc = __pa(pal_desc[0]); + sal_ed->sal_proc = __pa(sal_desc[0]); + sal_ed->gp = __pa(sal_desc[1]); + + for (cp = (char *) sal_systab; cp < (char *) efi_memmap; ++cp) + checksum += *cp; + + sal_systab->checksum = -checksum; + + /* fill in a memory descriptor: */ + md = &efi_memmap[0]; + md->type = EFI_CONVENTIONAL_MEMORY; + md->pad = 0; + md->phys_addr = 2*MB; + md->virt_addr = 0; + md->num_pages = (64*MB) >> 12; /* 64MB (in 4KB pages) */ + md->attribute = EFI_MEMORY_WB; + + /* descriptor for firmware emulator: */ + md = &efi_memmap[1]; + md->type = EFI_RUNTIME_SERVICES_DATA; + md->pad = 0; + md->phys_addr = 1*MB; + md->virt_addr = 0; + md->num_pages = (1*MB) >> 12; /* 1MB (in 4KB pages) */ + md->attribute = EFI_MEMORY_WB; + + /* descriptor for high memory (>4GB): */ + md = &efi_memmap[2]; + md->type = EFI_CONVENTIONAL_MEMORY; + md->pad = 0; + md->phys_addr = 4096*MB; + md->virt_addr = 0; + md->num_pages = (32*MB) >> 12; /* 32MB (in 4KB pages) */ + md->attribute = EFI_MEMORY_WB; + + bp = id(ZERO_PAGE_ADDR); + bp->efi_systab = __pa(&fw_mem); + bp->efi_memmap = __pa(efi_memmap); + bp->efi_memmap_size = NUM_MEM_DESCS*sizeof(efi_memory_desc_t); + bp->efi_memdesc_size = sizeof(efi_memory_desc_t); + bp->efi_memdesc_version = 1; + bp->command_line = __pa(cmd_line); + bp->console_info.num_cols = 80; + bp->console_info.num_rows = 25; + bp->console_info.orig_x = 0; + bp->console_info.orig_y = 24; + bp->num_pci_vectors = 0; + bp->fpswa = 0; +} diff --git a/arch/ia64/kernel/gate.S b/arch/ia64/kernel/gate.S new file mode 100644 index 000000000..24dc10ee4 --- /dev/null +++ b/arch/ia64/kernel/gate.S @@ -0,0 +1,200 @@ +/* + * This file contains the code that gets mapped at the upper end of + * each task's text region. For now, it contains the signal + * trampoline code only. + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#include <asm/offsets.h> +#include <asm/sigcontext.h> +#include <asm/system.h> +#include <asm/unistd.h> +#include <asm/page.h> + + .psr abi64 + .psr lsb + .lsb + + .section __gate_section,"ax" + + .align PAGE_SIZE + +# define SIGINFO_OFF 16 +# define SIGCONTEXT_OFF (SIGINFO_OFF + ((IA64_SIGINFO_SIZE + 15) & ~15)) +# define FLAGS_OFF IA64_SIGCONTEXT_FLAGS_OFFSET +# define CFM_OFF IA64_SIGCONTEXT_CFM_OFFSET +# define FR6_OFF IA64_SIGCONTEXT_FR6_OFFSET +# define BSP_OFF IA64_SIGCONTEXT_AR_BSP_OFFSET +# define RNAT_OFF IA64_SIGCONTEXT_AR_RNAT_OFFSET +# define base0 r2 +# define base1 r3 + /* + * When we get here, the memory stack looks like this: + * + * +===============================+ + * | | + * // struct sigcontext // + * | | + * +===============================+ <-- sp+SIGCONTEXT_OFF + * | | + * // rest of siginfo // + * | | + * + +---------------+ + * | | siginfo.code | + * +---------------+---------------+ + * | siginfo.errno | siginfo.signo | + * +-------------------------------+ <-- sp+SIGINFO_OFF + * | 16 byte of scratch | + * | space | + * +-------------------------------+ <-- sp + * + * The register stack looks _exactly_ the way it looked at the + * time the signal occurred. In other words, we're treading + * on a potential mine-field: each incoming general register + * may be a NaT value (includeing sp, in which case the process + * ends up dying with a SIGSEGV). + * + * The first need to do is a cover to get the registers onto + * the backing store. Once that is done, we invoke the signal + * handler which may modify some of the machine state. After + * returning from the signal handler, we return control to the + * previous context by executing a sigreturn system call. A + * signal handler may call the rt_sigreturn() function to + * directly return to a given sigcontext. However, the + * user-level sigreturn() needs to do much more than calling + * the rt_sigreturn() system call as it needs to unwind the + * stack to restore preserved registers that may have been + * saved on the signal handler's call stack. + * + * On entry: + * r2 = signal number + * r3 = plabel of signal handler + * r15 = new register backing store (ignored) + * [sp+16] = sigframe + */ + + .global ia64_sigtramp + .proc ia64_sigtramp +ia64_sigtramp: + ld8 r10=[r3],8 // get signal handler entry point + br.call.sptk.many rp=invoke_sighandler +.ret0: mov r15=__NR_rt_sigreturn + break __BREAK_SYSCALL + .endp ia64_sigramp + + .proc invoke_sighandler +invoke_sighandler: + ld8 gp=[r3] // get signal handler's global pointer + mov b6=r10 + cover // push args in interrupted frame onto backing store + ;; + alloc r8=ar.pfs,0,1,3,0 // get CFM0, EC0, and CPL0 into r8 + mov r17=ar.bsp // fetch ar.bsp + mov loc0=rp // save return pointer + ;; + cmp.ne p8,p0=r15,r0 // do we need to switch the rbs? + mov out0=r2 // signal number +(p8) br.cond.spnt.few setup_rbs // yup -> (clobbers r14 and r16) +back_from_setup_rbs: + adds base0=(BSP_OFF+SIGCONTEXT_OFF),sp + ;; + st8 [base0]=r17,(CFM_OFF-BSP_OFF) // save sc_ar_bsp + adds base1=(FR6_OFF+16+SIGCONTEXT_OFF),sp + ;; + + st8 [base0]=r8 // save CFM0, EC0, and CPL0 + adds base0=(FR6_OFF+SIGCONTEXT_OFF),sp + ;; + stf.spill [base0]=f6,32 + stf.spill [base1]=f7,32 + ;; + stf.spill [base0]=f8,32 + stf.spill [base1]=f9,32 + ;; + stf.spill [base0]=f10,32 + stf.spill [base1]=f11,32 + adds out1=SIGINFO_OFF,sp // siginfo pointer + ;; + stf.spill [base0]=f12,32 + stf.spill [base1]=f13,32 + adds out2=SIGCONTEXT_OFF,sp // sigcontext pointer + ;; + stf.spill [base0]=f14,32 + stf.spill [base1]=f15,32 + br.call.sptk.few rp=b6 // call the signal handler +.ret2: adds base0=(BSP_OFF+SIGCONTEXT_OFF),sp + ;; + ld8 r15=[base0],(CFM_OFF-BSP_OFF) // fetch sc_ar_bsp and advance to CFM_OFF + mov r14=ar.bsp + ;; + ld8 r8=[base0] // restore (perhaps modified) CFM0, EC0, and CPL0 + cmp.ne p8,p0=r14,r15 // do we need to restore the rbs? +(p8) br.cond.spnt.few restore_rbs // yup -> (clobbers r14 and r16) +back_from_restore_rbs: + { + and r9=0x7f,r8 // r9 <- CFM0.sof + extr.u r10=r8,7,7 // r10 <- CFM0.sol + mov r11=ip + } + ;; + adds base0=(FR6_OFF+SIGCONTEXT_OFF),sp + adds r11=(cont-back_from_restore_rbs),r11 + sub r9=r9,r10 // r9 <- CFM0.sof - CFM0.sol == CFM0.nout + ;; + adds base1=(FR6_OFF+16+SIGCONTEXT_OFF),sp + dep r9=r9,r9,7,7 // r9.sol = r9.sof + mov b6=r11 + ;; + ldf.fill f6=[base0],32 + ldf.fill f7=[base1],32 + mov rp=loc0 // copy return pointer out of stacked register + ;; + ldf.fill f8=[base0],32 + ldf.fill f9=[base1],32 + ;; + ldf.fill f10=[base0],32 + ldf.fill f11=[base1],32 + ;; + ldf.fill f12=[base0],32 + ldf.fill f13=[base1],32 + mov ar.pfs=r9 + ;; + ldf.fill f14=[base0],32 + ldf.fill f15=[base1],32 + br.ret.sptk.few b6 +cont: mov ar.pfs=r8 // ar.pfs = CFM0 + br.ret.sptk.few rp // re-establish CFM0 + .endp invoke_signal_handler + + .proc setup_rbs +setup_rbs: + flushrs // must be first in insn + ;; + mov ar.rsc=r0 // put RSE into enforced lazy mode + adds r16=(RNAT_OFF+SIGCONTEXT_OFF),sp + mov r14=ar.rnat // get rnat as updated by flushrs + ;; + mov ar.bspstore=r15 // set new register backing store area + st8 [r16]=r14 // save sc_ar_rnat + ;; + mov ar.rsc=0xf // set RSE into eager mode, pl 3 + invala // invalidate ALAT + br.cond.sptk.many back_from_setup_rbs + + .proc restore_rbs +restore_rbs: + flushrs + mov ar.rsc=r0 // put RSE into enforced lazy mode + adds r16=(RNAT_OFF+SIGCONTEXT_OFF),sp + ;; + ld8 r14=[r16] // get new rnat + mov ar.bspstore=r15 // set old register backing store area + ;; + mov ar.rnat=r14 // establish new rnat + mov ar.rsc=0xf // (will be restored later on from sc_ar_rsc) + // invala not necessary as that will happen when returning to user-mode + br.cond.sptk.many back_from_restore_rbs + + .endp restore_rbs diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S new file mode 100644 index 000000000..50d965e02 --- /dev/null +++ b/arch/ia64/kernel/head.S @@ -0,0 +1,646 @@ +/* + * Here is where the ball gets rolling as far as the kernel is concerned. + * When control is transferred to _start, the bootload has already + * loaded us to the correct address. All that's left to do here is + * to set up the kernel's global pointer and jump to the kernel + * entry point. + * + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * Copyright (C) 1999 Intel Corp. + * Copyright (C) 1999 Asit Mallick <Asit.K.Mallick@intel.com> + * Copyright (C) 1999 Don Dugger <Don.Dugger@intel.com> + */ + +#include <linux/config.h> + +#include <asm/fpu.h> +#include <asm/pal.h> +#include <asm/offsets.h> +#include <asm/processor.h> +#include <asm/ptrace.h> +#include <asm/system.h> + + .psr abi64 + .psr lsb + .lsb + + .section __special_page_section,"ax" + + .global empty_zero_page +empty_zero_page: + .skip PAGE_SIZE + + .global swapper_pg_dir +swapper_pg_dir: + .skip PAGE_SIZE + + .global empty_bad_page +empty_bad_page: + .skip PAGE_SIZE + + .global empty_bad_pte_table +empty_bad_pte_table: + .skip PAGE_SIZE + + .global empty_bad_pmd_table +empty_bad_pmd_table: + .skip PAGE_SIZE + + .rodata +halt_msg: + stringz "Halting kernel\n" + + .text + .align 16 + .global _start + .proc _start +_start: + // set IVT entry point---can't access I/O ports without it + movl r3=ia64_ivt + ;; + mov cr.iva=r3 + movl r2=FPSR_DEFAULT + ;; + srlz.i + movl gp=__gp + + mov ar.fpsr=r2 + ;; + +#ifdef CONFIG_IA64_EARLY_PRINTK + mov r2=6 + mov r3=(8<<8) | (28<<2) + ;; + mov rr[r2]=r3 + ;; + srlz.i + ;; +#endif + +#define isAP p2 // are we booting an Application Processor (not the BSP)? + + // Find the init_task for the currently booting CPU. At poweron, and in + // UP mode, cpu_now_booting is 0 + movl r3=cpu_now_booting + ;; + ld4 r3=[r3] + movl r2=init_tasks + ;; + shladd r2=r3,3,r2 + ;; + ld8 r2=[r2] + cmp4.ne isAP,p0=r3,r0 // p9 == true if this is an application processor (ap) + ;; // RAW on r2 + extr r3=r2,0,61 // r3 == phys addr of task struct + ;; + + // load the "current" pointer (r13) and ar.k6 with the current task + mov r13=r2 + mov ar.k6=r3 // Physical address + ;; + /* + * Reserve space at the top of the stack for "struct pt_regs". Kernel threads + * don't store interesting values in that structure, but the space still needs + * to be there because time-critical stuff such as the context switching can + * be implemented more efficiently (for example, __switch_to() + * always sets the psr.dfh bit of the task it is switching to). + */ + addl r12=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r2 + addl r2=IA64_RBS_OFFSET,r2 // initialize the RSE + mov ar.rsc=r0 // place RSE in enforced lazy mode + ;; + mov ar.bspstore=r2 // establish the new RSE stack + ;; + loadrs // load zero bytes from the register stack + ;; + mov ar.rsc=0x3 // place RSE in eager mode + ;; + +#ifdef CONFIG_IA64_EARLY_PRINTK + .rodata +alive_msg: + stringz "I'm alive and well\n" + .previous + + alloc r2=ar.pfs,0,0,2,0 + movl out0=alive_msg + ;; + br.call.sptk.few rp=early_printk +1: // force new bundle +#endif /* CONFIG_IA64_EARLY_PRINTK */ + + alloc r2=ar.pfs,8,0,2,0 +#ifdef CONFIG_SMP +(isAP) br.call.sptk.few rp=smp_callin +.ret1: +(isAP) br.cond.sptk.few self +#endif + +#undef isAP + + // This is executed by the bootstrap processor (bsp) only: + +#ifdef CONFIG_IA64_FW_EMU + // initialize PAL & SAL emulator: + br.call.sptk.few rp=sys_fw_init + ;; +#endif + br.call.sptk.few rp=start_kernel +.ret2: + addl r2=@ltoff(halt_msg),gp + ;; + ld8 out0=[r2] + br.call.sptk.few b0=console_print +self: br.sptk.few self // endless loop + .endp _start + + .align 16 + .global ia64_save_debug_regs + .proc ia64_save_debug_regs +ia64_save_debug_regs: + alloc r16=ar.pfs,1,0,0,0 + mov r20=ar.lc // preserve ar.lc + mov ar.lc=IA64_NUM_DBG_REGS-1 + mov r18=0 + add r19=IA64_NUM_DBG_REGS*8,in0 + ;; +1: mov r16=dbr[r18] + mov r17=ibr[r18] + add r18=1,r18 + ;; + st8.nta [in0]=r16,8 + st8.nta [r19]=r17,8 + br.cloop.sptk.few 1b + + mov ar.lc=r20 // restore ar.lc + br.ret.sptk.few b0 + .endp ia64_save_debug_regs + + .align 16 + .global ia64_load_debug_regs + .proc ia64_load_debug_regs +ia64_load_debug_regs: + alloc r16=ar.pfs,1,0,0,0 + lfetch.nta [in0] + mov r20=ar.lc // preserve ar.lc + add r19=IA64_NUM_DBG_REGS*8,in0 + mov ar.lc=IA64_NUM_DBG_REGS-1 + mov r18=-1 + ;; +1: ld8.nta r16=[in0],8 + ld8.nta r17=[r19],8 + add r18=1,r18 + ;; + mov dbr[r18]=r16 + mov ibr[r18]=r17 + br.cloop.sptk.few 1b + + mov ar.lc=r20 // restore ar.lc + br.ret.sptk.few b0 + .endp ia64_load_debug_regs + + .align 16 + .global __ia64_save_fpu + .proc __ia64_save_fpu +__ia64_save_fpu: + alloc r2=ar.pfs,1,0,0,0 + adds r3=16,in0 + ;; + stf.spill.nta [in0]=f32,32 + stf.spill.nta [ r3]=f33,32 + ;; + stf.spill.nta [in0]=f34,32 + stf.spill.nta [ r3]=f35,32 + ;; + stf.spill.nta [in0]=f36,32 + stf.spill.nta [ r3]=f37,32 + ;; + stf.spill.nta [in0]=f38,32 + stf.spill.nta [ r3]=f39,32 + ;; + stf.spill.nta [in0]=f40,32 + stf.spill.nta [ r3]=f41,32 + ;; + stf.spill.nta [in0]=f42,32 + stf.spill.nta [ r3]=f43,32 + ;; + stf.spill.nta [in0]=f44,32 + stf.spill.nta [ r3]=f45,32 + ;; + stf.spill.nta [in0]=f46,32 + stf.spill.nta [ r3]=f47,32 + ;; + stf.spill.nta [in0]=f48,32 + stf.spill.nta [ r3]=f49,32 + ;; + stf.spill.nta [in0]=f50,32 + stf.spill.nta [ r3]=f51,32 + ;; + stf.spill.nta [in0]=f52,32 + stf.spill.nta [ r3]=f53,32 + ;; + stf.spill.nta [in0]=f54,32 + stf.spill.nta [ r3]=f55,32 + ;; + stf.spill.nta [in0]=f56,32 + stf.spill.nta [ r3]=f57,32 + ;; + stf.spill.nta [in0]=f58,32 + stf.spill.nta [ r3]=f59,32 + ;; + stf.spill.nta [in0]=f60,32 + stf.spill.nta [ r3]=f61,32 + ;; + stf.spill.nta [in0]=f62,32 + stf.spill.nta [ r3]=f63,32 + ;; + stf.spill.nta [in0]=f64,32 + stf.spill.nta [ r3]=f65,32 + ;; + stf.spill.nta [in0]=f66,32 + stf.spill.nta [ r3]=f67,32 + ;; + stf.spill.nta [in0]=f68,32 + stf.spill.nta [ r3]=f69,32 + ;; + stf.spill.nta [in0]=f70,32 + stf.spill.nta [ r3]=f71,32 + ;; + stf.spill.nta [in0]=f72,32 + stf.spill.nta [ r3]=f73,32 + ;; + stf.spill.nta [in0]=f74,32 + stf.spill.nta [ r3]=f75,32 + ;; + stf.spill.nta [in0]=f76,32 + stf.spill.nta [ r3]=f77,32 + ;; + stf.spill.nta [in0]=f78,32 + stf.spill.nta [ r3]=f79,32 + ;; + stf.spill.nta [in0]=f80,32 + stf.spill.nta [ r3]=f81,32 + ;; + stf.spill.nta [in0]=f82,32 + stf.spill.nta [ r3]=f83,32 + ;; + stf.spill.nta [in0]=f84,32 + stf.spill.nta [ r3]=f85,32 + ;; + stf.spill.nta [in0]=f86,32 + stf.spill.nta [ r3]=f87,32 + ;; + stf.spill.nta [in0]=f88,32 + stf.spill.nta [ r3]=f89,32 + ;; + stf.spill.nta [in0]=f90,32 + stf.spill.nta [ r3]=f91,32 + ;; + stf.spill.nta [in0]=f92,32 + stf.spill.nta [ r3]=f93,32 + ;; + stf.spill.nta [in0]=f94,32 + stf.spill.nta [ r3]=f95,32 + ;; + stf.spill.nta [in0]=f96,32 + stf.spill.nta [ r3]=f97,32 + ;; + stf.spill.nta [in0]=f98,32 + stf.spill.nta [ r3]=f99,32 + ;; + stf.spill.nta [in0]=f100,32 + stf.spill.nta [ r3]=f101,32 + ;; + stf.spill.nta [in0]=f102,32 + stf.spill.nta [ r3]=f103,32 + ;; + stf.spill.nta [in0]=f104,32 + stf.spill.nta [ r3]=f105,32 + ;; + stf.spill.nta [in0]=f106,32 + stf.spill.nta [ r3]=f107,32 + ;; + stf.spill.nta [in0]=f108,32 + stf.spill.nta [ r3]=f109,32 + ;; + stf.spill.nta [in0]=f110,32 + stf.spill.nta [ r3]=f111,32 + ;; + stf.spill.nta [in0]=f112,32 + stf.spill.nta [ r3]=f113,32 + ;; + stf.spill.nta [in0]=f114,32 + stf.spill.nta [ r3]=f115,32 + ;; + stf.spill.nta [in0]=f116,32 + stf.spill.nta [ r3]=f117,32 + ;; + stf.spill.nta [in0]=f118,32 + stf.spill.nta [ r3]=f119,32 + ;; + stf.spill.nta [in0]=f120,32 + stf.spill.nta [ r3]=f121,32 + ;; + stf.spill.nta [in0]=f122,32 + stf.spill.nta [ r3]=f123,32 + ;; + stf.spill.nta [in0]=f124,32 + stf.spill.nta [ r3]=f125,32 + ;; + stf.spill.nta [in0]=f126,32 + stf.spill.nta [ r3]=f127,32 + br.ret.sptk.few rp + .endp __ia64_save_fpu + + .align 16 + .global __ia64_load_fpu + .proc __ia64_load_fpu +__ia64_load_fpu: + alloc r2=ar.pfs,1,0,0,0 + adds r3=16,in0 + ;; + ldf.fill.nta f32=[in0],32 + ldf.fill.nta f33=[ r3],32 + ;; + ldf.fill.nta f34=[in0],32 + ldf.fill.nta f35=[ r3],32 + ;; + ldf.fill.nta f36=[in0],32 + ldf.fill.nta f37=[ r3],32 + ;; + ldf.fill.nta f38=[in0],32 + ldf.fill.nta f39=[ r3],32 + ;; + ldf.fill.nta f40=[in0],32 + ldf.fill.nta f41=[ r3],32 + ;; + ldf.fill.nta f42=[in0],32 + ldf.fill.nta f43=[ r3],32 + ;; + ldf.fill.nta f44=[in0],32 + ldf.fill.nta f45=[ r3],32 + ;; + ldf.fill.nta f46=[in0],32 + ldf.fill.nta f47=[ r3],32 + ;; + ldf.fill.nta f48=[in0],32 + ldf.fill.nta f49=[ r3],32 + ;; + ldf.fill.nta f50=[in0],32 + ldf.fill.nta f51=[ r3],32 + ;; + ldf.fill.nta f52=[in0],32 + ldf.fill.nta f53=[ r3],32 + ;; + ldf.fill.nta f54=[in0],32 + ldf.fill.nta f55=[ r3],32 + ;; + ldf.fill.nta f56=[in0],32 + ldf.fill.nta f57=[ r3],32 + ;; + ldf.fill.nta f58=[in0],32 + ldf.fill.nta f59=[ r3],32 + ;; + ldf.fill.nta f60=[in0],32 + ldf.fill.nta f61=[ r3],32 + ;; + ldf.fill.nta f62=[in0],32 + ldf.fill.nta f63=[ r3],32 + ;; + ldf.fill.nta f64=[in0],32 + ldf.fill.nta f65=[ r3],32 + ;; + ldf.fill.nta f66=[in0],32 + ldf.fill.nta f67=[ r3],32 + ;; + ldf.fill.nta f68=[in0],32 + ldf.fill.nta f69=[ r3],32 + ;; + ldf.fill.nta f70=[in0],32 + ldf.fill.nta f71=[ r3],32 + ;; + ldf.fill.nta f72=[in0],32 + ldf.fill.nta f73=[ r3],32 + ;; + ldf.fill.nta f74=[in0],32 + ldf.fill.nta f75=[ r3],32 + ;; + ldf.fill.nta f76=[in0],32 + ldf.fill.nta f77=[ r3],32 + ;; + ldf.fill.nta f78=[in0],32 + ldf.fill.nta f79=[ r3],32 + ;; + ldf.fill.nta f80=[in0],32 + ldf.fill.nta f81=[ r3],32 + ;; + ldf.fill.nta f82=[in0],32 + ldf.fill.nta f83=[ r3],32 + ;; + ldf.fill.nta f84=[in0],32 + ldf.fill.nta f85=[ r3],32 + ;; + ldf.fill.nta f86=[in0],32 + ldf.fill.nta f87=[ r3],32 + ;; + ldf.fill.nta f88=[in0],32 + ldf.fill.nta f89=[ r3],32 + ;; + ldf.fill.nta f90=[in0],32 + ldf.fill.nta f91=[ r3],32 + ;; + ldf.fill.nta f92=[in0],32 + ldf.fill.nta f93=[ r3],32 + ;; + ldf.fill.nta f94=[in0],32 + ldf.fill.nta f95=[ r3],32 + ;; + ldf.fill.nta f96=[in0],32 + ldf.fill.nta f97=[ r3],32 + ;; + ldf.fill.nta f98=[in0],32 + ldf.fill.nta f99=[ r3],32 + ;; + ldf.fill.nta f100=[in0],32 + ldf.fill.nta f101=[ r3],32 + ;; + ldf.fill.nta f102=[in0],32 + ldf.fill.nta f103=[ r3],32 + ;; + ldf.fill.nta f104=[in0],32 + ldf.fill.nta f105=[ r3],32 + ;; + ldf.fill.nta f106=[in0],32 + ldf.fill.nta f107=[ r3],32 + ;; + ldf.fill.nta f108=[in0],32 + ldf.fill.nta f109=[ r3],32 + ;; + ldf.fill.nta f110=[in0],32 + ldf.fill.nta f111=[ r3],32 + ;; + ldf.fill.nta f112=[in0],32 + ldf.fill.nta f113=[ r3],32 + ;; + ldf.fill.nta f114=[in0],32 + ldf.fill.nta f115=[ r3],32 + ;; + ldf.fill.nta f116=[in0],32 + ldf.fill.nta f117=[ r3],32 + ;; + ldf.fill.nta f118=[in0],32 + ldf.fill.nta f119=[ r3],32 + ;; + ldf.fill.nta f120=[in0],32 + ldf.fill.nta f121=[ r3],32 + ;; + ldf.fill.nta f122=[in0],32 + ldf.fill.nta f123=[ r3],32 + ;; + ldf.fill.nta f124=[in0],32 + ldf.fill.nta f125=[ r3],32 + ;; + ldf.fill.nta f126=[in0],32 + ldf.fill.nta f127=[ r3],32 + br.ret.sptk.few rp + .endp __ia64_load_fpu + + .align 16 + .global __ia64_init_fpu + .proc __ia64_init_fpu +__ia64_init_fpu: + alloc r2=ar.pfs,0,0,0,0 + stf.spill [sp]=f0 + mov f32=f0 + ;; + ldf.fill f33=[sp] + ldf.fill f34=[sp] + mov f35=f0 + ;; + ldf.fill f36=[sp] + ldf.fill f37=[sp] + mov f38=f0 + ;; + ldf.fill f39=[sp] + ldf.fill f40=[sp] + mov f41=f0 + ;; + ldf.fill f42=[sp] + ldf.fill f43=[sp] + mov f44=f0 + ;; + ldf.fill f45=[sp] + ldf.fill f46=[sp] + mov f47=f0 + ;; + ldf.fill f48=[sp] + ldf.fill f49=[sp] + mov f50=f0 + ;; + ldf.fill f51=[sp] + ldf.fill f52=[sp] + mov f53=f0 + ;; + ldf.fill f54=[sp] + ldf.fill f55=[sp] + mov f56=f0 + ;; + ldf.fill f57=[sp] + ldf.fill f58=[sp] + mov f59=f0 + ;; + ldf.fill f60=[sp] + ldf.fill f61=[sp] + mov f62=f0 + ;; + ldf.fill f63=[sp] + ldf.fill f64=[sp] + mov f65=f0 + ;; + ldf.fill f66=[sp] + ldf.fill f67=[sp] + mov f68=f0 + ;; + ldf.fill f69=[sp] + ldf.fill f70=[sp] + mov f71=f0 + ;; + ldf.fill f72=[sp] + ldf.fill f73=[sp] + mov f74=f0 + ;; + ldf.fill f75=[sp] + ldf.fill f76=[sp] + mov f77=f0 + ;; + ldf.fill f78=[sp] + ldf.fill f79=[sp] + mov f80=f0 + ;; + ldf.fill f81=[sp] + ldf.fill f82=[sp] + mov f83=f0 + ;; + ldf.fill f84=[sp] + ldf.fill f85=[sp] + mov f86=f0 + ;; + ldf.fill f87=[sp] + ldf.fill f88=[sp] + mov f89=f0 + ;; + ldf.fill f90=[sp] + ldf.fill f91=[sp] + mov f92=f0 + ;; + ldf.fill f93=[sp] + ldf.fill f94=[sp] + mov f95=f0 + ;; + ldf.fill f96=[sp] + ldf.fill f97=[sp] + mov f98=f0 + ;; + ldf.fill f99=[sp] + ldf.fill f100=[sp] + mov f101=f0 + ;; + ldf.fill f102=[sp] + ldf.fill f103=[sp] + mov f104=f0 + ;; + ldf.fill f105=[sp] + ldf.fill f106=[sp] + mov f107=f0 + ;; + ldf.fill f108=[sp] + ldf.fill f109=[sp] + mov f110=f0 + ;; + ldf.fill f111=[sp] + ldf.fill f112=[sp] + mov f113=f0 + ;; + ldf.fill f114=[sp] + ldf.fill f115=[sp] + mov f116=f0 + ;; + ldf.fill f117=[sp] + ldf.fill f118=[sp] + mov f119=f0 + ;; + ldf.fill f120=[sp] + ldf.fill f121=[sp] + mov f122=f0 + ;; + ldf.fill f123=[sp] + ldf.fill f124=[sp] + mov f125=f0 + ;; + ldf.fill f126=[sp] + mov f127=f0 + br.ret.sptk.few rp + .endp __ia64_init_fpu diff --git a/arch/ia64/kernel/init_task.c b/arch/ia64/kernel/init_task.c new file mode 100644 index 000000000..122650461 --- /dev/null +++ b/arch/ia64/kernel/init_task.c @@ -0,0 +1,31 @@ +/* + * This is where we statically allocate and initialize the initial + * task. + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/sched.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is page aligned due to the way + * process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union task_union init_task_union + __attribute__((section("init_task"))) = + { INIT_TASK(init_task_union.task) }; diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c new file mode 100644 index 000000000..01c201137 --- /dev/null +++ b/arch/ia64/kernel/irq.c @@ -0,0 +1,657 @@ +/* + * linux/arch/ia64/kernel/irq.c + * + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998, 1999 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * 6/10/99: Updated to bring in sync with x86 version to facilitate + * support for SMP and different interrupt controllers. + */ + +#include <linux/config.h> + +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/kernel_stat.h> +#include <linux/malloc.h> +#include <linux/ptrace.h> +#include <linux/random.h> /* for rand_initialize_irq() */ +#include <linux/signal.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/threads.h> + +#ifdef CONFIG_KDB +# include <linux/kdb.h> +#endif + +#include <asm/bitops.h> +#include <asm/delay.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/machvec.h> +#include <asm/pgtable.h> +#include <asm/system.h> + +/* This is used to detect bad usage of probe_irq_on()/probe_irq_off(). */ +#define PROBE_IRQ_COOKIE 0xfeedC0FFEE + +struct irq_desc irq_desc[NR_IRQS]; + +/* + * Micro-access to controllers is serialized over the whole + * system. We never hold this lock when we call the actual + * IRQ handler. + */ +spinlock_t irq_controller_lock; + +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +spinlock_t ivr_read_lock; +#endif + +unsigned int local_bh_count[NR_CPUS]; +/* + * used in irq_enter()/irq_exit() + */ +unsigned int local_irq_count[NR_CPUS]; + +static struct irqaction timer_action = { NULL, 0, 0, NULL, NULL, NULL}; + +#ifdef CONFIG_SMP +static struct irqaction ipi_action = { NULL, 0, 0, NULL, NULL, NULL}; +#endif + +/* + * Legacy IRQ to IA-64 vector translation table. Any vector not in + * this table maps to itself (ie: irq 0x30 => IA64 vector 0x30) + */ +__u8 irq_to_vector_map[IA64_MIN_VECTORED_IRQ] = { + /* 8259 IRQ translation, first 16 entries */ + TIMER_IRQ, 0x50, 0x0f, 0x51, 0x52, 0x53, 0x43, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x40, 0x41, +}; + +/* + * Reverse of the above table. + */ +static __u8 vector_to_legacy_map[256]; + +/* + * used by proc fs (/proc/interrupts) + */ +int +get_irq_list (char *buf) +{ + int i; + struct irqaction * action; + char *p = buf; + +#ifdef CONFIG_SMP + p += sprintf(p, " "); + for (i = 0; i < smp_num_cpus; i++) + p += sprintf(p, "CPU%d ", i); + *p++ = '\n'; +#endif + /* + * Simply scans the external vectored interrupts + */ + for (i = 0; i < NR_IRQS; i++) { + action = irq_desc[i].action; + if (!action) + continue; + p += sprintf(p, "%3d: ",i); +#ifndef CONFIG_SMP + p += sprintf(p, "%10u ", kstat_irqs(i)); +#else + { + int j; + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); + } +#endif + p += sprintf(p, " %14s", irq_desc[i].handler->typename); + p += sprintf(p, " %c%s", (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + + for (action = action->next; action; action = action->next) { + p += sprintf(p, ", %c%s", + (action->flags & SA_INTERRUPT)?'+':' ', + action->name); + } + *p++ = '\n'; + } + return p - buf; +} + +/* + * That's where the IVT branches when we get an external + * interrupt. This branches to the correct hardware IRQ handler via + * function ptr. + */ +void +ia64_handle_irq (unsigned long irq, struct pt_regs *regs) +{ + unsigned long bsp, sp, saved_tpr; + +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +# ifndef CONFIG_SMP + static unsigned int max_prio = 0; +# endif + unsigned int prev_prio; + unsigned long eoi_ptr; + +# ifdef CONFIG_USB + disable_usb(); +# endif + /* + * Stop IPIs by getting the ivr_read_lock + */ + spin_lock(&ivr_read_lock); + + /* + * Disable PCI writes + */ + outl(0x80ff81c0, 0xcf8); + outl(0x73002188, 0xcfc); + eoi_ptr = inl(0xcfc); + + irq = ia64_get_ivr(); + + /* + * Enable PCI writes + */ + outl(0x73182188, 0xcfc); + + spin_unlock(&ivr_read_lock); + +# ifdef CONFIG_USB + reenable_usb(); +# endif + +# ifndef CONFIG_SMP + prev_prio = max_prio; + if (irq < max_prio) { + printk ("ia64_handle_irq: got irq %lu while %u was in progress!\n", + irq, max_prio); + + } else + max_prio = irq; +# endif /* !CONFIG_SMP */ +#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ + + /* Always set TPR to limit maximum interrupt nesting depth to + * 16 (without this, it would be ~240, which could easily lead + * to kernel stack overflows. + */ + saved_tpr = ia64_get_tpr(); + ia64_srlz_d(); + ia64_set_tpr(irq); + ia64_srlz_d(); + + asm ("mov %0=ar.bsp" : "=r"(bsp)); + asm ("mov %0=sp" : "=r"(sp)); + + if ((sp - bsp) < 1024) { + static long last_time; + static unsigned char count; + + if (count > 5 && jiffies - last_time > 5*HZ) + count = 0; + if (++count < 5) { + last_time = jiffies; + printk("ia64_handle_irq: DANGER: less than 1KB of free stack space!!\n" + "(bsp=0x%lx, sp=%lx)\n", bsp, sp); + } +#ifdef CONFIG_KDB + kdb(KDB_REASON_PANIC, 0, regs); +#endif + } + + /* + * The interrupt is now said to be in service + */ + if (irq >= NR_IRQS) { + printk("handle_irq: invalid irq=%lu\n", irq); + goto out; + } + + ++kstat.irqs[smp_processor_id()][irq]; + + if (irq == IA64_SPURIOUS_INT) { + printk("handle_irq: spurious interrupt\n"); + goto out; + } + + /* + * Handle the interrupt by calling the hardware specific handler (IOSAPIC, Internal, etc). + */ + (*irq_desc[irq].handler->handle)(irq, regs); + out: +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC + { + long pEOI; + + asm ("mov %0=0;; (p1) mov %0=1" : "=r"(pEOI)); + if (!pEOI) { + printk("Yikes: ia64_handle_irq() without pEOI!!\n"); + asm volatile ("cmp.eq p1,p0=r0,r0" : "=r"(pEOI)); +# ifdef CONFIG_KDB + kdb(KDB_REASON_PANIC, 0, regs); +# endif + } + } + + local_irq_disable(); +# ifndef CONFIG_SMP + if (max_prio == irq) + max_prio = prev_prio; +# endif /* !CONFIG_SMP */ +#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ + + ia64_srlz_d(); + ia64_set_tpr(saved_tpr); + ia64_srlz_d(); +} + + +/* + * This should really return information about whether we should do + * bottom half handling etc. Right now we end up _always_ checking the + * bottom half, which is a waste of time and is not what some drivers + * would prefer. + */ +int +invoke_irq_handlers (unsigned int irq, struct pt_regs *regs, struct irqaction *action) +{ + void (*handler)(int, void *, struct pt_regs *); + unsigned long flags, flags_union = 0; + int cpu = smp_processor_id(); + unsigned int requested_irq; + void *dev_id; + + irq_enter(cpu, irq); + + if ((action->flags & SA_INTERRUPT) == 0) + __sti(); + + do { + flags = action->flags; + requested_irq = irq; + if ((flags & SA_LEGACY) != 0) + requested_irq = vector_to_legacy_map[irq]; + flags_union |= flags; + handler = action->handler; + dev_id = action->dev_id; + action = action->next; + (*handler)(requested_irq, dev_id, regs); + } while (action); + if ((flags_union & SA_SAMPLE_RANDOM) != 0) + add_interrupt_randomness(irq); + __cli(); + + irq_exit(cpu, irq); + return flags_union | 1; /* force the "do bottom halves" bit */ +} + +void +disable_irq_nosync (unsigned int irq) +{ + unsigned long flags; + + irq = map_legacy_irq(irq); + + spin_lock_irqsave(&irq_controller_lock, flags); + if (irq_desc[irq].depth++ > 0) { + irq_desc[irq].status &= ~IRQ_ENABLED; + irq_desc[irq].handler->disable(irq); + } + spin_unlock_irqrestore(&irq_controller_lock, flags); +} + +/* + * Synchronous version of the above, making sure the IRQ is + * no longer running on any other IRQ.. + */ +void +disable_irq (unsigned int irq) +{ + disable_irq_nosync(irq); + + irq = map_legacy_irq(irq); + + if (!local_irq_count[smp_processor_id()]) { + do { + barrier(); + } while ((irq_desc[irq].status & IRQ_INPROGRESS) != 0); + } +} + +void +enable_irq (unsigned int irq) +{ + unsigned long flags; + + irq = map_legacy_irq(irq); + + spin_lock_irqsave(&irq_controller_lock, flags); + switch (irq_desc[irq].depth) { + case 1: + irq_desc[irq].status |= IRQ_ENABLED; + (*irq_desc[irq].handler->enable)(irq); + /* fall through */ + default: + --irq_desc[irq].depth; + break; + + case 0: + printk("enable_irq: unbalanced from %p\n", __builtin_return_address(0)); + } + spin_unlock_irqrestore(&irq_controller_lock, flags); +} + +/* + * This function encapsulates the initialization that needs to be + * performed under the protection of lock irq_controller_lock. The + * lock must have been acquired by the time this is called. + */ +static inline int +setup_irq (unsigned int irq, struct irqaction *new) +{ + int shared = 0; + struct irqaction *old, **p; + + p = &irq_desc[irq].action; + old = *p; + if (old) { + if (!(old->flags & new->flags & SA_SHIRQ)) { + return -EBUSY; + } + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + *p = new; + + /* when sharing do not unmask */ + if (!shared) { + irq_desc[irq].depth = 0; + irq_desc[irq].status |= IRQ_ENABLED; + (*irq_desc[irq].handler->startup)(irq); + } + return 0; +} + +int +request_irq (unsigned int requested_irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + int retval, need_kfree = 0; + struct irqaction *action; + unsigned long flags; + unsigned int irq; + +#ifdef IA64_DEBUG + printk("request_irq(0x%x) called\n", requested_irq); +#endif + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if ((irqflags & SA_SHIRQ) && !dev_id) + printk("Bad boy: %s (at %p) called us without a dev_id!\n", + devname, current_text_addr()); + + irq = map_legacy_irq(requested_irq); + if (irq != requested_irq) + irqflags |= SA_LEGACY; + + if (irq >= NR_IRQS) + return -EINVAL; + + if (!handler) + return -EINVAL; + + /* + * The timer_action and ipi_action cannot be allocated + * dynamically because its initialization happens really early + * on in init/main.c at this point the memory allocator has + * not yet been initialized. So we use a statically reserved + * buffer for it. In some sense that's no big deal because we + * need one no matter what. + */ + if (irq == TIMER_IRQ) + action = &timer_action; +#ifdef CONFIG_SMP + else if (irq == IPI_IRQ) + action = &ipi_action; +#endif + else { + action = kmalloc(sizeof(struct irqaction), GFP_KERNEL); + need_kfree = 1; + } + + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + if ((irqflags & SA_SAMPLE_RANDOM) != 0) + rand_initialize_irq(irq); + + spin_lock_irqsave(&irq_controller_lock, flags); + retval = setup_irq(irq, action); + spin_unlock_irqrestore(&irq_controller_lock, flags); + + if (need_kfree && retval) + kfree(action); + + return retval; +} + +void +free_irq (unsigned int irq, void *dev_id) +{ + struct irqaction *action, **p; + unsigned long flags; + + /* + * some sanity checks first + */ + if (irq >= NR_IRQS) { + printk("Trying to free IRQ%d\n",irq); + return; + } + + irq = map_legacy_irq(irq); + + /* + * Find the corresponding irqaction + */ + spin_lock_irqsave(&irq_controller_lock, flags); + for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *p = action->next; + if (!irq_desc[irq].action) { + irq_desc[irq].status &= ~IRQ_ENABLED; + (*irq_desc[irq].handler->shutdown)(irq); + } + + spin_unlock_irqrestore(&irq_controller_lock, flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (irq_desc[irq].status & IRQ_INPROGRESS) + barrier(); +#endif + + if (action != &timer_action +#ifdef CONFIG_SMP + && action != &ipi_action +#endif + ) + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n", irq); +} + +/* + * IRQ autodetection code. Note that the return value of + * probe_irq_on() is no longer being used (it's role has been replaced + * by the IRQ_AUTODETECT flag). + */ +unsigned long +probe_irq_on (void) +{ + struct irq_desc *id; + unsigned long delay; + +#ifdef IA64_DEBUG + printk("probe_irq_on() called\n"); +#endif + + spin_lock_irq(&irq_controller_lock); + for (id = irq_desc; id < irq_desc + NR_IRQS; ++id) { + if (!id->action) { + id->status |= IRQ_AUTODETECT | IRQ_WAITING; + (*id->handler->startup)(id - irq_desc); + } + } + spin_unlock_irq(&irq_controller_lock); + + /* wait for spurious interrupts to trigger: */ + + for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) + /* about 100ms delay */ + synchronize_irq(); + + /* filter out obviously spurious interrupts: */ + spin_lock_irq(&irq_controller_lock); + for (id = irq_desc; id < irq_desc + NR_IRQS; ++id) { + unsigned int status = id->status; + + if (!(status & IRQ_AUTODETECT)) + continue; + + if (!(status & IRQ_WAITING)) { + id->status = status & ~IRQ_AUTODETECT; + (*id->handler->shutdown)(id - irq_desc); + } + } + spin_unlock_irq(&irq_controller_lock); + return PROBE_IRQ_COOKIE; /* return meaningless return value */ +} + +int +probe_irq_off (unsigned long cookie) +{ + int irq_found, nr_irqs; + struct irq_desc *id; + +#ifdef IA64_DEBUG + printk("probe_irq_off(cookie=0x%lx) -> ", cookie); +#endif + + if (cookie != PROBE_IRQ_COOKIE) + printk("bad irq probe from %p\n", __builtin_return_address(0)); + + nr_irqs = 0; + irq_found = 0; + spin_lock_irq(&irq_controller_lock); + for (id = irq_desc + IA64_MIN_VECTORED_IRQ; id < irq_desc + NR_IRQS; ++id) { + unsigned int status = id->status; + + if (!(status & IRQ_AUTODETECT)) + continue; + + if (!(status & IRQ_WAITING)) { + if (!nr_irqs) + irq_found = (id - irq_desc); + ++nr_irqs; + } + id->status = status & ~IRQ_AUTODETECT; + (*id->handler->shutdown)(id - irq_desc); + } + spin_unlock_irq(&irq_controller_lock); + + if (nr_irqs > 1) + irq_found = -irq_found; + +#ifdef IA64_DEBUG + printk("%d\n", irq_found); +#endif + return irq_found; +} + +#ifdef CONFIG_SMP + +void __init +init_IRQ_SMP (void) +{ + if (request_irq(IPI_IRQ, handle_IPI, 0, "IPI", NULL)) + panic("Could not allocate IPI Interrupt Handler!"); +} + +#endif + +void __init +init_IRQ (void) +{ + int i; + + for (i = 0; i < IA64_MIN_VECTORED_IRQ; ++i) + vector_to_legacy_map[irq_to_vector_map[i]] = i; + + for (i = 0; i < NR_IRQS; ++i) { + irq_desc[i].handler = &irq_type_default; + } + + irq_desc[TIMER_IRQ].handler = &irq_type_ia64_internal; +#ifdef CONFIG_SMP + /* + * Configure the IPI vector and handler + */ + irq_desc[IPI_IRQ].handler = &irq_type_ia64_internal; + init_IRQ_SMP(); +#endif + + ia64_set_pmv(1 << 16); + ia64_set_cmcv(CMC_IRQ); /* XXX fix me */ + + platform_irq_init(irq_desc); + + /* clear TPR to enable all interrupt classes: */ + ia64_set_tpr(0); +} + +/* TBD: + * Certain IA64 platforms can have inter-processor interrupt support. + * This interface is supposed to default to the IA64 IPI block-based + * mechanism if the platform doesn't provide a separate mechanism + * for IPIs. + * Choices : (1) Extend hw_interrupt_type interfaces + * (2) Use machine vector mechanism + * For now defining the following interface as a place holder. + */ +void +ipi_send (int cpu, int vector, int delivery_mode) +{ +} diff --git a/arch/ia64/kernel/irq_default.c b/arch/ia64/kernel/irq_default.c new file mode 100644 index 000000000..bf8c62642 --- /dev/null +++ b/arch/ia64/kernel/irq_default.c @@ -0,0 +1,30 @@ +#include <linux/kernel.h> +#include <linux/sched.h> + +#include <asm/irq.h> +#include <asm/processor.h> +#include <asm/ptrace.h> + + +static int +irq_default_handle_irq (unsigned int irq, struct pt_regs *regs) +{ + printk("Unexpected irq vector 0x%x on CPU %u!\n", irq, smp_processor_id()); + return 0; /* don't call do_bottom_half() for spurious interrupts */ +} + +static void +irq_default_noop (unsigned int irq) +{ + /* nuthing to do... */ +} + +struct hw_interrupt_type irq_type_default = { + "default", + (void (*)(unsigned long)) irq_default_noop, /* init */ + irq_default_noop, /* startup */ + irq_default_noop, /* shutdown */ + irq_default_handle_irq, /* handle */ + irq_default_noop, /* enable */ + irq_default_noop /* disable */ +}; diff --git a/arch/ia64/kernel/irq_internal.c b/arch/ia64/kernel/irq_internal.c new file mode 100644 index 000000000..1ae904fe8 --- /dev/null +++ b/arch/ia64/kernel/irq_internal.c @@ -0,0 +1,71 @@ +/* + * Internal Interrupt Vectors + * + * This takes care of interrupts that are generated by the CPU + * internally, such as the ITC and IPI interrupts. + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + */ + +#include <linux/kernel.h> +#include <linux/sched.h> + +#include <asm/irq.h> +#include <asm/processor.h> +#include <asm/ptrace.h> + +/* + * This is identical to IOSAPIC handle_irq. It may go away . . . + */ +static int +internal_handle_irq (unsigned int irq, struct pt_regs *regs) +{ + struct irqaction *action = 0; + struct irq_desc *id = irq_desc + irq; + unsigned int status; + int retval; + + spin_lock(&irq_controller_lock); + { + status = id->status; + if ((status & IRQ_ENABLED) != 0) + action = id->action; + id->status = status & ~(IRQ_REPLAY | IRQ_WAITING); + } + spin_unlock(&irq_controller_lock); + + if (!action) { + if (!(id->status & IRQ_AUTODETECT)) + printk("irq_hpsim_handle_irq: unexpected interrupt %u\n", irq); + return 0; + } + + retval = invoke_irq_handlers(irq, regs, action); + + spin_lock(&irq_controller_lock); + { + status = (id->status & ~IRQ_INPROGRESS); + id->status = status; + } + spin_unlock(&irq_controller_lock); + + return retval; +} + +static void +internal_noop (unsigned int irq) +{ + /* nuthing to do... */ +} + +struct hw_interrupt_type irq_type_ia64_internal = { + "IA64 internal", + (void (*)(unsigned long)) internal_noop, /* init */ + internal_noop, /* startup */ + internal_noop, /* shutdown */ + internal_handle_irq, /* handle */ + internal_noop, /* enable */ + internal_noop /* disable */ +}; + diff --git a/arch/ia64/kernel/irq_lock.c b/arch/ia64/kernel/irq_lock.c new file mode 100644 index 000000000..9c512dd4e --- /dev/null +++ b/arch/ia64/kernel/irq_lock.c @@ -0,0 +1,287 @@ +/* + * SMP IRQ Lock support + * + * Global interrupt locks for SMP. Allow interrupts to come in on any + * CPU, yet make cli/sti act globally to protect critical regions.. + * These function usually appear in irq.c, but I think it's cleaner this way. + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + */ + +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/smp.h> +#include <linux/threads.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/processor.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/pgtable.h> +#include <asm/delay.h> + +int global_irq_holder = NO_PROC_ID; +spinlock_t global_irq_lock; +atomic_t global_irq_count; +atomic_t global_bh_count; +atomic_t global_bh_lock; + +#define INIT_STUCK (1<<26) + +void +irq_enter(int cpu, int irq) +{ + int stuck = INIT_STUCK; + + hardirq_enter(cpu, irq); + barrier(); + while (global_irq_lock.lock) { + if (cpu == global_irq_holder) { + break; + } + + if (!--stuck) { + printk("irq_enter stuck (irq=%d, cpu=%d, global=%d)\n", + irq, cpu,global_irq_holder); + stuck = INIT_STUCK; + } + barrier(); + } +} + +void +irq_exit(int cpu, int irq) +{ + hardirq_exit(cpu, irq); + release_irqlock(cpu); +} + +static void +show(char * str) +{ + int i; + unsigned long *stack; + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [%d %d]\n", + atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]); + printk("bh: %d [%d %d]\n", + atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]); + + stack = (unsigned long *) &stack; + for (i = 40; i ; i--) { + unsigned long x = *++stack; + if (x > (unsigned long) &get_options && x < (unsigned long) &vsprintf) { + printk("<[%08lx]> ", x); + } + } +} + +#define MAXCOUNT 100000000 + +static inline void +wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + +static inline void +wait_on_irq(int cpu) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!atomic_read(&global_irq_count)) { + if (local_bh_count[cpu] || !atomic_read(&global_bh_count)) + break; + } + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + spin_unlock(&global_irq_lock); + mb(); + + for (;;) { + if (!--count) { + show("wait_on_irq"); + count = ~0; + } + __sti(); + udelay(cpu + 1); + __cli(); + if (atomic_read(&global_irq_count)) + continue; + if (global_irq_lock.lock) + continue; + if (!local_bh_count[cpu] && atomic_read(&global_bh_count)) + continue; + if (spin_trylock(&global_irq_lock)) + break; + } + } +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void +synchronize_bh(void) +{ + if (atomic_read(&global_bh_count)) { + int cpu = smp_processor_id(); + if (!local_irq_count[cpu] && !local_bh_count[cpu]) { + wait_on_bh(); + } + } +} + + +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + */ +void +synchronize_irq(void) +{ + int cpu = smp_processor_id(); + int local_count; + int global_count; + + mb(); + do { + local_count = local_irq_count[cpu]; + global_count = atomic_read(&global_irq_count); + } while (global_count != local_count); +} + +static inline void +get_irqlock(int cpu) +{ + if (!spin_trylock(&global_irq_lock)) { + /* do we already hold the lock? */ + if ((unsigned char) cpu == global_irq_holder) + return; + /* Uhhuh.. Somebody else got it. Wait.. */ + spin_lock(&global_irq_lock); + } + /* + * We also to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu); + + /* + * Ok, finally.. + */ + global_irq_holder = cpu; +} + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void +__global_cli(void) +{ + unsigned long flags; + + __save_flags(flags); + if (flags & IA64_PSR_I) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count[cpu]) + get_irqlock(cpu); + } +} + +void +__global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count[cpu]) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long +__global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = flags & IA64_PSR_I; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count[smp_processor_id()]) { + if (local_enabled) + retval = 1; + if (global_irq_holder == (unsigned char) smp_processor_id()) + retval = 0; + } + return retval; +} + +void +__global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } +} diff --git a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S new file mode 100644 index 000000000..4c3ac242a --- /dev/null +++ b/arch/ia64/kernel/ivt.S @@ -0,0 +1,1342 @@ +/* + * arch/ia64/kernel/ivt.S + * + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998, 1999 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 1998-2000 David Mosberger <davidm@hpl.hp.com> + */ + +#include <linux/config.h> + +#include <asm/break.h> +#include <asm/offsets.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/unistd.h> + +#include "entry.h" + +/* + * A couple of convenience macros that make writing and reading + * SAVE_MIN and SAVE_REST easier. + */ +#define rARPR r31 +#define rCRIFS r30 +#define rCRIPSR r29 +#define rCRIIP r28 +#define rARRSC r27 +#define rARPFS r26 +#define rARUNAT r25 +#define rARRNAT r24 +#define rARBSPSTORE r23 +#define rKRBS r22 +#define rB6 r21 +#define rR1 r20 + +/* + * DO_SAVE_MIN switches to the kernel stacks (if necessary) and saves + * the minimum state necessary that allows us to turn psr.ic back + * on. + * + * Assumed state upon entry: + * psr.ic: off + * psr.dt: off + * r31: contains saved predicates (pr) + * + * Upon exit, the state is as follows: + * psr.ic: off + * psr.dt: off + * r2 = points to &pt_regs.r16 + * r12 = kernel sp (kernel virtual address) + * r13 = points to current task_struct (kernel virtual address) + * p15 = TRUE if psr.i is set in cr.ipsr + * predicate registers (other than p6, p7, and p15), b6, r3, r8, r9, r10, r11, r14, r15: + * preserved + * + * Note that psr.ic is NOT turned on by this macro. This is so that + * we can pass interruption state as arguments to a handler. + */ +#define DO_SAVE_MIN(COVER,EXTRA) \ + mov rARRSC=ar.rsc; \ + mov rARPFS=ar.pfs; \ + mov rR1=r1; \ + mov rARUNAT=ar.unat; \ + mov rCRIPSR=cr.ipsr; \ + mov rB6=b6; /* rB6 = branch reg 6 */ \ + mov rCRIIP=cr.iip; \ + mov r1=ar.k6; /* r1 = current */ \ + ;; \ + invala; \ + extr.u r16=rCRIPSR,32,2; /* extract psr.cpl */ \ + ;; \ + cmp.eq pKern,p7=r0,r16; /* are we in kernel mode already? (psr.cpl==0) */ \ + /* switch from user to kernel RBS: */ \ + COVER; \ + ;; \ +(p7) mov ar.rsc=r0; /* set enforced lazy mode, pl 0, little-endian, loadrs=0 */ \ +(p7) addl rKRBS=IA64_RBS_OFFSET,r1; /* compute base of register backing store */ \ + ;; \ +(p7) mov rARRNAT=ar.rnat; \ +(pKern) dep r1=0,sp,61,3; /* compute physical addr of sp */ \ +(p7) addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r1; /* compute base of memory stack */ \ +(p7) mov rARBSPSTORE=ar.bspstore; /* save ar.bspstore */ \ +(p7) dep rKRBS=-1,rKRBS,61,3; /* compute kernel virtual addr of RBS */ \ + ;; \ +(pKern) addl r1=-IA64_PT_REGS_SIZE,r1; /* if in kernel mode, use sp (r12) */ \ +(p7) mov ar.bspstore=rKRBS; /* switch to kernel RBS */ \ + ;; \ +(p7) mov r18=ar.bsp; \ +(p7) mov ar.rsc=0x3; /* set eager mode, pl 0, little-endian, loadrs=0 */ \ + \ + mov r16=r1; /* initialize first base pointer */ \ + adds r17=8,r1; /* initialize second base pointer */ \ + ;; \ + st8 [r16]=rCRIPSR,16; /* save cr.ipsr */ \ + st8 [r17]=rCRIIP,16; /* save cr.iip */ \ +(pKern) mov r18=r0; /* make sure r18 isn't NaT */ \ + ;; \ + st8 [r16]=rCRIFS,16; /* save cr.ifs */ \ + st8 [r17]=rARUNAT,16; /* save ar.unat */ \ +(p7) sub r18=r18,rKRBS; /* r18=RSE.ndirty*8 */ \ + ;; \ + st8 [r16]=rARPFS,16; /* save ar.pfs */ \ + st8 [r17]=rARRSC,16; /* save ar.rsc */ \ + tbit.nz p15,p0=rCRIPSR,IA64_PSR_I_BIT \ + ;; /* avoid RAW on r16 & r17 */ \ +(pKern) adds r16=16,r16; /* skip over ar_rnat field */ \ +(pKern) adds r17=16,r17; /* skip over ar_bspstore field */ \ +(p7) st8 [r16]=rARRNAT,16; /* save ar.rnat */ \ +(p7) st8 [r17]=rARBSPSTORE,16; /* save ar.bspstore */ \ + ;; \ + st8 [r16]=rARPR,16; /* save predicates */ \ + st8 [r17]=rB6,16; /* save b6 */ \ + shl r18=r18,16; /* compute ar.rsc to be used for "loadrs" */ \ + ;; \ + st8 [r16]=r18,16; /* save ar.rsc value for "loadrs" */ \ + st8.spill [r17]=rR1,16; /* save original r1 */ \ + cmp.ne pEOI,p0=r0,r0 /* clear pEOI by default */ \ + ;; \ + st8.spill [r16]=r2,16; \ + st8.spill [r17]=r3,16; \ + adds r2=IA64_PT_REGS_R16_OFFSET,r1; \ + ;; \ + st8.spill [r16]=r12,16; \ + st8.spill [r17]=r13,16; \ + cmp.eq pNonSys,pSys=r0,r0 /* initialize pSys=0, pNonSys=1 */ \ + ;; \ + st8.spill [r16]=r14,16; \ + st8.spill [r17]=r15,16; \ + dep r14=-1,r0,61,3; \ + ;; \ + st8.spill [r16]=r8,16; \ + st8.spill [r17]=r9,16; \ + adds r12=-16,r1; /* switch to kernel memory stack (with 16 bytes of scratch) */ \ + ;; \ + st8.spill [r16]=r10,16; \ + st8.spill [r17]=r11,16; \ + mov r13=ar.k6; /* establish `current' */ \ + ;; \ + or r2=r2,r14; /* make first base a kernel virtual address */ \ + EXTRA; \ + movl r1=__gp; /* establish kernel global pointer */ \ + ;; \ + or r12=r12,r14; /* make sp a kernel virtual address */ \ + or r13=r13,r14; /* make `current' a kernel virtual address */ \ + bsw.1;; /* switch back to bank 1 (must be last in insn group) */ + +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +# define STOPS nop.i 0x0;; nop.i 0x0;; nop.i 0x0;; +#else +# define STOPS +#endif + +#define SAVE_MIN_WITH_COVER DO_SAVE_MIN(cover;; mov rCRIFS=cr.ifs,) STOPS +#define SAVE_MIN_WITH_COVER_R19 DO_SAVE_MIN(cover;; mov rCRIFS=cr.ifs, mov r15=r19) STOPS +#define SAVE_MIN DO_SAVE_MIN(mov rCRIFS=r0,) STOPS + +/* + * SAVE_REST saves the remainder of pt_regs (with psr.ic on). This + * macro guarantees to preserve all predicate registers, r8, r9, r10, + * r11, r14, and r15. + * + * Assumed state upon entry: + * psr.ic: on + * psr.dt: on + * r2: points to &pt_regs.r16 + * r3: points to &pt_regs.r17 + */ +#define SAVE_REST \ + st8.spill [r2]=r16,16; \ + st8.spill [r3]=r17,16; \ + ;; \ + st8.spill [r2]=r18,16; \ + st8.spill [r3]=r19,16; \ + ;; \ + mov r16=ar.ccv; /* M-unit */ \ + movl r18=FPSR_DEFAULT /* L-unit */ \ + ;; \ + mov r17=ar.fpsr; /* M-unit */ \ + mov ar.fpsr=r18; /* M-unit */ \ + ;; \ + st8.spill [r2]=r20,16; \ + st8.spill [r3]=r21,16; \ + mov r18=b0; \ + ;; \ + st8.spill [r2]=r22,16; \ + st8.spill [r3]=r23,16; \ + mov r19=b7; \ + ;; \ + st8.spill [r2]=r24,16; \ + st8.spill [r3]=r25,16; \ + ;; \ + st8.spill [r2]=r26,16; \ + st8.spill [r3]=r27,16; \ + ;; \ + st8.spill [r2]=r28,16; \ + st8.spill [r3]=r29,16; \ + ;; \ + st8.spill [r2]=r30,16; \ + st8.spill [r3]=r31,16; \ + ;; \ + st8 [r2]=r16,16; /* ar.ccv */ \ + st8 [r3]=r17,16; /* ar.fpsr */ \ + ;; \ + st8 [r2]=r18,16; /* b0 */ \ + st8 [r3]=r19,16+8; /* b7 */ \ + ;; \ + stf.spill [r2]=f6,32; \ + stf.spill [r3]=f7,32; \ + ;; \ + stf.spill [r2]=f8,32; \ + stf.spill [r3]=f9,32 + +/* + * This file defines the interrupt vector table used by the CPU. + * It does not include one entry per possible cause of interruption. + * + * External interrupts only use 1 entry. All others are internal interrupts + * + * The first 20 entries of the table contain 64 bundles each while the + * remaining 48 entries contain only 16 bundles each. + * + * The 64 bundles are used to allow inlining the whole handler for critical + * interrupts like TLB misses. + * + * For each entry, the comment is as follows: + * + * // 0x1c00 Entry 7 (size 64 bundles) Data Key Miss (12,51) + * entry offset ----/ / / / / + * entry number ---------/ / / / + * size of the entry -------------/ / / + * vector name -------------------------------------/ / + * related interrupts (what is the real interrupt?) ----------/ + * + * The table is 32KB in size and must be aligned on 32KB boundary. + * (The CPU ignores the 15 lower bits of the address) + * + * Table is based upon EAS2.4 (June 1998) + */ + +#define FAULT(n) \ + rsm psr.dt; /* avoid nested faults due to TLB misses... */ \ + ;; \ + srlz.d; /* ensure everyone knows psr.dt is off... */ \ + mov r31=pr; \ + mov r19=n;; /* prepare to save predicates */ \ + br.cond.sptk.many dispatch_to_fault_handler + +/* + * As we don't (hopefully) use the space available, we need to fill it with + * nops. the parameter may be used for debugging and is representing the entry + * number + */ +#define BREAK_BUNDLE(a) break.m (a); \ + break.i (a); \ + break.i (a) +/* + * 4 breaks bundles all together + */ +#define BREAK_BUNDLE4(a); BREAK_BUNDLE(a); BREAK_BUNDLE(a); BREAK_BUNDLE(a); BREAK_BUNDLE(a) + +/* + * 8 bundles all together (too lazy to use only 4 at a time !) + */ +#define BREAK_BUNDLE8(a); BREAK_BUNDLE4(a); BREAK_BUNDLE4(a) + + .psr abi64 + .psr lsb + .lsb + + .section __ivt_section,"ax" + + .align 32768 // align on 32KB boundary + .global ia64_ivt +ia64_ivt: +///////////////////////////////////////////////////////////////////////////////////////// +// 0x0000 Entry 0 (size 64 bundles) VHPT Translation (8,20,47) + /* + * The VHPT vector is invoked when the TLB entry for the virtual page table + * is missing. This happens only as a result of a previous + * (the "original") TLB miss, which may either be caused by an instruction + * fetch or a data access (or non-access). + * + * What we do here is normal TLB miss handing for the _original_ miss, followed + * by inserting the TLB entry for the virtual page table page that the VHPT + * walker was attempting to access. The latter gets inserted as long + * as both L1 and L2 have valid mappings for the faulting address. + * The TLB entry for the original miss gets inserted only if + * the L3 entry indicates that the page is present. + * + * do_page_fault gets invoked in the following cases: + * - the faulting virtual address uses unimplemented address bits + * - the faulting virtual address has no L1, L2, or L3 mapping + */ + mov r16=cr.ifa // get address that caused the TLB miss + ;; + rsm psr.dt // use physical addressing for data + mov r31=pr // save the predicate registers + mov r19=ar.k7 // get page table base address + shl r21=r16,3 // shift bit 60 into sign bit + shr.u r17=r16,61 // get the region number into r17 + ;; + cmp.eq p6,p7=5,r17 // is IFA pointing into to region 5? + shr.u r18=r16,PGDIR_SHIFT // get bits 33-63 of the faulting address + ;; +(p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place + srlz.d // ensure "rsm psr.dt" has taken effect +(p6) movl r19=__pa(SWAPPER_PGD_ADDR) // region 5 is rooted at swapper_pg_dir +(p6) shr r21=r21,PGDIR_SHIFT+PAGE_SHIFT-1 +(p7) shr r21=r21,PGDIR_SHIFT+PAGE_SHIFT-4 + ;; +(p6) dep r17=r18,r19,3,(PAGE_SHIFT-3) // r17=PTA + IFA(33,42)*8 +(p7) dep r17=r18,r17,3,(PAGE_SHIFT-6) // r17=PTA + (((IFA(61,63) << 7) | IFA(33,39))*8) + cmp.eq p7,p6=0,r21 // unused address bits all zeroes? + shr.u r18=r16,PMD_SHIFT // shift L2 index into position + ;; +(p6) cmp.eq p7,p6=-1,r21 // unused address bits all ones? + ld8 r17=[r17] // fetch the L1 entry (may be 0) + ;; +(p7) cmp.eq p6,p7=r17,r0 // was L1 entry NULL? + dep r17=r18,r17,3,(PAGE_SHIFT-3) // compute address of L2 page table entry + ;; +(p7) ld8 r17=[r17] // fetch the L2 entry (may be 0) + shr.u r19=r16,PAGE_SHIFT // shift L3 index into position + ;; +(p7) cmp.eq.or.andcm p6,p7=r17,r0 // was L2 entry NULL? + dep r17=r19,r17,3,(PAGE_SHIFT-3) // compute address of L3 page table entry + ;; +(p7) ld8 r18=[r17] // read the L3 PTE + mov r19=cr.isr // cr.isr bit 0 tells us if this is an insn miss + ;; +(p7) tbit.z p6,p7=r18,0 // page present bit cleared? + mov r21=cr.iha // get the VHPT address that caused the TLB miss + ;; // avoid RAW on p7 +(p7) tbit.nz.unc p10,p11=r19,32 // is it an instruction TLB miss? + dep r17=0,r17,0,PAGE_SHIFT // clear low bits to get page address + ;; +(p10) itc.i r18;; // insert the instruction TLB entry (EAS2.6: must be last in insn group!) +(p11) itc.d r18;; // insert the data TLB entry (EAS2.6: must be last in insn group!) +(p6) br.spnt.few page_fault // handle bad address/page not present (page fault) + mov cr.ifa=r21 + + // Now compute and insert the TLB entry for the virtual page table. + // We never execute in a page table page so there is no need to set + // the exception deferral bit. + adds r16=__DIRTY_BITS_NO_ED|_PAGE_PL_0|_PAGE_AR_RW,r17 + ;; +(p7) itc.d r16;; // EAS2.6: must be last in insn group! + mov pr=r31,-1 // restore predicate registers + rfi;; // must be last insn in an insn group + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x0400 Entry 1 (size 64 bundles) ITLB (21) + /* + * The ITLB basically does the same as the VHPT handler except + * that we always insert exactly one instruction TLB entry. + */ + mov r16=cr.ifa // get address that caused the TLB miss + ;; + rsm psr.dt // use physical addressing for data + mov r31=pr // save the predicate registers + mov r19=ar.k7 // get page table base address + shl r21=r16,3 // shift bit 60 into sign bit + shr.u r17=r16,61 // get the region number into r17 + ;; + cmp.eq p6,p7=5,r17 // is IFA pointing into to region 5? + shr.u r18=r16,PGDIR_SHIFT // get bits 33-63 of the faulting address + ;; +(p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place + srlz.d // ensure "rsm psr.dt" has taken effect +(p6) movl r19=__pa(SWAPPER_PGD_ADDR) // region 5 is rooted at swapper_pg_dir +(p6) shr r21=r21,PGDIR_SHIFT+PAGE_SHIFT-1 +(p7) shr r21=r21,PGDIR_SHIFT+PAGE_SHIFT-4 + ;; +(p6) dep r17=r18,r19,3,(PAGE_SHIFT-3) // r17=PTA + IFA(33,42)*8 +(p7) dep r17=r18,r17,3,(PAGE_SHIFT-6) // r17=PTA + (((IFA(61,63) << 7) | IFA(33,39))*8) + cmp.eq p7,p6=0,r21 // unused address bits all zeroes? + shr.u r18=r16,PMD_SHIFT // shift L2 index into position + ;; +(p6) cmp.eq p7,p6=-1,r21 // unused address bits all ones? + ld8 r17=[r17] // fetch the L1 entry (may be 0) + ;; +(p7) cmp.eq p6,p7=r17,r0 // was L1 entry NULL? + dep r17=r18,r17,3,(PAGE_SHIFT-3) // compute address of L2 page table entry + ;; +(p7) ld8 r17=[r17] // fetch the L2 entry (may be 0) + shr.u r19=r16,PAGE_SHIFT // shift L3 index into position + ;; +(p7) cmp.eq.or.andcm p6,p7=r17,r0 // was L2 entry NULL? + dep r17=r19,r17,3,(PAGE_SHIFT-3) // compute address of L3 page table entry + ;; +(p7) ld8 r18=[r17] // read the L3 PTE + ;; +(p7) tbit.z p6,p7=r18,0 // page present bit cleared? + ;; +(p7) itc.i r18;; // insert the instruction TLB entry (EAS2.6: must be last in insn group!) +(p6) br.spnt.few page_fault // handle bad address/page not present (page fault) + ;; + mov pr=r31,-1 // restore predicate registers + rfi;; // must be last insn in an insn group + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x0800 Entry 2 (size 64 bundles) DTLB (9,48) + /* + * The DTLB basically does the same as the VHPT handler except + * that we always insert exactly one data TLB entry. + */ + mov r16=cr.ifa // get address that caused the TLB miss + ;; + rsm psr.dt // use physical addressing for data + mov r31=pr // save the predicate registers + mov r19=ar.k7 // get page table base address + shl r21=r16,3 // shift bit 60 into sign bit + shr.u r17=r16,61 // get the region number into r17 + ;; + cmp.eq p6,p7=5,r17 // is IFA pointing into to region 5? + shr.u r18=r16,PGDIR_SHIFT // get bits 33-63 of the faulting address + ;; +(p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place + srlz.d // ensure "rsm psr.dt" has taken effect +(p6) movl r19=__pa(SWAPPER_PGD_ADDR) // region 5 is rooted at swapper_pg_dir +(p6) shr r21=r21,PGDIR_SHIFT+PAGE_SHIFT-1 +(p7) shr r21=r21,PGDIR_SHIFT+PAGE_SHIFT-4 + ;; +(p6) dep r17=r18,r19,3,(PAGE_SHIFT-3) // r17=PTA + IFA(33,42)*8 +(p7) dep r17=r18,r17,3,(PAGE_SHIFT-6) // r17=PTA + (((IFA(61,63) << 7) | IFA(33,39))*8) + cmp.eq p7,p6=0,r21 // unused address bits all zeroes? + shr.u r18=r16,PMD_SHIFT // shift L2 index into position + ;; +(p6) cmp.eq p7,p6=-1,r21 // unused address bits all ones? + ld8 r17=[r17] // fetch the L1 entry (may be 0) + ;; +(p7) cmp.eq p6,p7=r17,r0 // was L1 entry NULL? + dep r17=r18,r17,3,(PAGE_SHIFT-3) // compute address of L2 page table entry + ;; +(p7) ld8 r17=[r17] // fetch the L2 entry (may be 0) + shr.u r19=r16,PAGE_SHIFT // shift L3 index into position + ;; +(p7) cmp.eq.or.andcm p6,p7=r17,r0 // was L2 entry NULL? + dep r17=r19,r17,3,(PAGE_SHIFT-3) // compute address of L3 page table entry + ;; +(p7) ld8 r18=[r17] // read the L3 PTE + ;; +(p7) tbit.z p6,p7=r18,0 // page present bit cleared? + ;; +(p7) itc.d r18;; // insert the instruction TLB entry (EAS2.6: must be last in insn group!) +(p6) br.spnt.few page_fault // handle bad address/page not present (page fault) + ;; + mov pr=r31,-1 // restore predicate registers + rfi;; // must be last insn in an insn group + + //----------------------------------------------------------------------------------- + // call do_page_fault (predicates are in r31, psr.dt is off, r16 is faulting address) +page_fault: + SAVE_MIN_WITH_COVER + // + // Copy control registers to temporary registers, then turn on psr bits, + // then copy the temporary regs to the output regs. We have to do this + // because the "alloc" can cause a mandatory store which could lead to + // an "Alt DTLB" fault which we can handle only if psr.ic is on. + // + mov r8=cr.ifa + mov r9=cr.isr + adds r3=8,r2 // set up second base pointer + ;; + ssm psr.ic | psr.dt + ;; + srlz.d // guarantee that interrupt collection is enabled +(p15) ssm psr.i // restore psr.i + ;; + srlz.i // must precede "alloc"! (srlz.i implies srlz.d) + movl r14=ia64_leave_kernel + ;; + alloc r15=ar.pfs,0,0,3,0 // must be first in insn group + mov out0=r8 + mov out1=r9 + ;; + SAVE_REST + mov rp=r14 + ;; + adds out2=16,r12 // out2 = pointer to pt_regs + br.call.sptk.few b6=ia64_do_page_fault // ignore return address + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x0c00 Entry 3 (size 64 bundles) Alt ITLB (19) + mov r16=cr.ifa // get address that caused the TLB miss + movl r17=__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RX + ;; + shr.u r18=r16,57 // move address bit 61 to bit 4 + dep r16=0,r16,52,12 // clear top 12 bits of address + ;; + andcm r18=0x10,r18 // bit 4=~address-bit(61) + dep r16=r17,r16,0,12 // insert PTE control bits into r16 + ;; + or r16=r16,r18 // set bit 4 (uncached) if the access was to region 6 + ;; + itc.i r16;; // insert the TLB entry(EAS2.6: must be last in insn group!) + rfi;; // must be last insn in an insn group + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x1000 Entry 4 (size 64 bundles) Alt DTLB (7,46) + mov r16=cr.ifa // get address that caused the TLB miss + movl r17=__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RW + ;; + shr.u r18=r16,57 // move address bit 61 to bit 4 + dep r16=0,r16,52,12 // clear top 12 bits of address + ;; + andcm r18=0x10,r18 // bit 4=~address-bit(61) + dep r16=r17,r16,0,12 // insert PTE control bits into r16 + ;; + or r16=r16,r18 // set bit 4 (uncached) if the access was to region 6 + ;; + itc.d r16;; // insert the TLB entry (EAS2.6: must be last in insn group!) + rfi;; // must be last insn in an insn group + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x1400 Entry 5 (size 64 bundles) Data nested TLB (6,45) + // + // In the absence of kernel bugs, we get here when the Dirty-bit, Instruction + // Access-bit, or Data Access-bit faults cause a nested fault because the + // dTLB entry for the virtual page table isn't present. In such a case, + // we lookup the pte for the faulting address by walking the page table + // and return to the contination point passed in register r30. + // In accessing the page tables, we don't need to check for NULL entries + // because if the page tables didn't map the faulting address, it would not + // be possible to receive one of the above faults. + // + // Input: r16: faulting address + // r29: saved b0 + // r30: continuation address + // + // Output: r17: physical address of L3 PTE of faulting address + // r29: saved b0 + // r30: continuation address + // + // Clobbered: b0, r18, r19, r21, r31, psr.dt (cleared) + // + rsm psr.dt // switch to using physical data addressing + mov r19=ar.k7 // get the page table base address + shl r21=r16,3 // shift bit 60 into sign bit + ;; + mov r31=pr // save the predicate registers + shr.u r17=r16,61 // get the region number into r17 + ;; + cmp.eq p6,p7=5,r17 // is faulting address in region 5? + shr.u r18=r16,PGDIR_SHIFT // get bits 33-63 of faulting address + ;; +(p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place + srlz.d +(p6) movl r17=__pa(SWAPPER_PGD_ADDR) // region 5 is rooted at swapper_pg_dir +(p6) shr r21=r21,PGDIR_SHIFT+PAGE_SHIFT-1 +(p7) shr r21=r21,PGDIR_SHIFT+PAGE_SHIFT-4 + ;; +(p6) dep r17=r18,r17,3,(PAGE_SHIFT-3) // r17=PTA + IFA(33,42)*8 +(p7) dep r17=r18,r17,3,(PAGE_SHIFT-6) // r17=PTA + (((IFA(61,63) << 7) | IFA(33,39))*8) + shr.u r18=r16,PMD_SHIFT // shift L2 index into position + ;; + ld8 r17=[r17] // fetch the L1 entry + mov b0=r30 + ;; + dep r17=r18,r17,3,(PAGE_SHIFT-3) // compute address of L2 page table entry + ;; + ld8 r17=[r17] // fetch the L2 entry + shr.u r19=r16,PAGE_SHIFT // shift L3 index into position + ;; + dep r17=r19,r17,3,(PAGE_SHIFT-3) // compute address of L3 page table entry + ;; + mov pr=r31,-1 // restore predicates + br.cond.sptk.few b0 // return to continuation point + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x1800 Entry 6 (size 64 bundles) Instruction Key Miss (24) + FAULT(6) + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x1c00 Entry 7 (size 64 bundles) Data Key Miss (12,51) + FAULT(7) + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x2000 Entry 8 (size 64 bundles) Dirty-bit (54) + // + // What we do here is to simply turn on the dirty bit in the PTE. We need + // to update both the page-table and the TLB entry. To efficiently access + // the PTE, we address it through the virtual page table. Most likely, the + // TLB entry for the relevant virtual page table page is still present in + // the TLB so we can normally do this without additional TLB misses. + // In case the necessary virtual page table TLB entry isn't present, we take + // a nested TLB miss hit where we look up the physical address of the L3 PTE + // and then continue at label 1 below. + // + mov r16=cr.ifa // get the address that caused the fault + movl r30=1f // load continuation point in case of nested fault + ;; + thash r17=r16 // compute virtual address of L3 PTE + mov r29=b0 // save b0 in case of nested fault + ;; +1: ld8 r18=[r17] + ;; // avoid RAW on r18 + or r18=_PAGE_D,r18 // set the dirty bit + mov b0=r29 // restore b0 + ;; + st8 [r17]=r18 // store back updated PTE + itc.d r18;; // install updated PTE (EAS2.6: must be last in insn group!) + rfi;; // must be last insn in an insn group + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x2400 Entry 9 (size 64 bundles) Instruction Access-bit (27) + // Like Entry 8, except for instruction access + mov r16=cr.ifa // get the address that caused the fault +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC + mov r31=pr // save predicates + mov r30=cr.ipsr + ;; + extr.u r17=r30,IA64_PSR_IS_BIT,1 // get instruction arch. indicator + ;; + cmp.eq p6,p0 = r17,r0 // check if IA64 instruction set + ;; +(p6) mov r16=cr.iip // get real faulting address + ;; +(p6) mov cr.ifa=r16 // reset IFA + mov pr=r31,-1 +#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ + movl r30=1f // load continuation point in case of nested fault + ;; + thash r17=r16 // compute virtual address of L3 PTE + mov r29=b0 // save b0 in case of nested fault) + ;; +1: ld8 r18=[r17] + ;; // avoid raw on r18 + or r18=_PAGE_A,r18 // set the accessed bit + mov b0=r29 // restore b0 + ;; + st8 [r17]=r18 // store back updated PTE + itc.i r18;; // install updated PTE (EAS2.6: must be last in insn group!) + rfi;; // must be last insn in an insn group + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x2800 Entry 10 (size 64 bundles) Data Access-bit (15,55) + // Like Entry 8, except for data access + mov r16=cr.ifa // get the address that caused the fault + movl r30=1f // load continuation point in case of nested fault + ;; + thash r17=r16 // compute virtual address of L3 PTE + mov r29=b0 // save b0 in case of nested fault) + ;; +1: ld8 r18=[r17] + ;; // avoid RAW on r18 + or r18=_PAGE_A,r18 // set the accessed bit + mov b0=r29 // restore b0 + ;; + st8 [r17]=r18 // store back updated PTE + itc.d r18;; // install updated PTE (EAS2.6: must be last in insn group!) + rfi;; // must be last insn in an insn group + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x2c00 Entry 11 (size 64 bundles) Break instruction (33) + mov r16=cr.iim + mov r17=__IA64_BREAK_SYSCALL + mov r31=pr // prepare to save predicates + rsm psr.dt // avoid nested faults due to TLB misses... + ;; + srlz.d // ensure everyone knows psr.dt is off... + cmp.eq p0,p7=r16,r17 // is this a system call? (p7 <- false, if so) + +#if 1 + // Allow syscalls via the old system call number for the time being. This is + // so we can transition to the new syscall number in a relatively smooth + // fashion. + mov r17=0x80000 + ;; +(p7) cmp.eq.or.andcm p0,p7=r16,r17 // is this the old syscall number? +#endif + +(p7) br.cond.spnt.many non_syscall + + SAVE_MIN // uses r31; defines r2: + + // turn interrupt collection and data translation back on: + ssm psr.ic | psr.dt + srlz.d // guarantee that interrupt collection is enabled + cmp.eq pSys,pNonSys=r0,r0 // set pSys=1, pNonSys=0 + ;; +(p15) ssm psr.i // restore psr.i + ;; + srlz.i // ensure everybody knows psr.ic and psr.dt are back on + adds r8=(IA64_PT_REGS_R8_OFFSET-IA64_PT_REGS_R16_OFFSET),r2 + ;; + stf8 [r8]=f1 // ensure pt_regs.r8 != 0 (see handle_syscall_error) + adds r3=8,r2 // set up second base pointer for SAVE_REST + ;; + SAVE_REST + ;; // avoid WAW on r2 & r3 + + mov r3=255 + adds r15=-1024,r15 // r15 contains the syscall number---subtract 1024 + adds r2=IA64_TASK_FLAGS_OFFSET,r13 // r2 = ¤t->flags + + ;; + cmp.geu.unc p6,p7=r3,r15 // (syscall > 0 && syscall <= 1024+255) ? + movl r16=sys_call_table + ;; +(p6) shladd r16=r15,3,r16 + movl r15=ia64_ret_from_syscall +(p7) adds r16=(__NR_ni_syscall-1024)*8,r16 // force __NR_ni_syscall + ;; + ld8 r16=[r16] // load address of syscall entry point + mov rp=r15 // set the real return addr + ;; + ld8 r2=[r2] // r2 = current->flags + mov b6=r16 + + // arrange things so we skip over break instruction when returning: + + adds r16=16,sp // get pointer to cr_ipsr + adds r17=24,sp // get pointer to cr_iip + ;; + ld8 r18=[r16] // fetch cr_ipsr + tbit.z p8,p0=r2,5 // (current->flags & PF_TRACESYS) == 0? + ;; + ld8 r19=[r17] // fetch cr_iip + extr.u r20=r18,41,2 // extract ei field + ;; + cmp.eq p6,p7=2,r20 // isr.ei==2? + adds r19=16,r19 // compute address of next bundle + ;; +(p6) mov r20=0 // clear ei to 0 +(p7) adds r20=1,r20 // increment ei to next slot + ;; +(p6) st8 [r17]=r19 // store new cr.iip if cr.isr.ei wrapped around + dep r18=r20,r18,41,2 // insert new ei into cr.isr + ;; + st8 [r16]=r18 // store new value for cr.isr + +(p8) br.call.sptk.few b6=b6 // ignore this return addr + br.call.sptk.few rp=ia64_trace_syscall // rp will be overwritten (ignored) + // NOT REACHED + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x3000 Entry 12 (size 64 bundles) External Interrupt (4) + rsm psr.dt // avoid nested faults due to TLB misses... + ;; + srlz.d // ensure everyone knows psr.dt is off... + mov r31=pr // prepare to save predicates + ;; + + SAVE_MIN_WITH_COVER // uses r31; defines r2 and r3 + ssm psr.ic | psr.dt // turn interrupt collection and data translation back on + ;; + adds r3=8,r2 // set up second base pointer for SAVE_REST + cmp.eq pEOI,p0=r0,r0 // set pEOI flag so that ia64_leave_kernel writes cr.eoi + srlz.i // ensure everybody knows psr.ic and psr.dt are back on + ;; + SAVE_REST + ;; + alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC + mov out0=r0 // defer reading of cr.ivr to handle_irq... +#else + mov out0=cr.ivr // pass cr.ivr as first arg +#endif + add out1=16,sp // pass pointer to pt_regs as second arg + ;; + srlz.d // make sure we see the effect of cr.ivr + movl r14=ia64_leave_kernel + ;; + mov rp=r14 + br.call.sptk.few b6=ia64_handle_irq + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x3400 Entry 13 (size 64 bundles) Reserved + FAULT(13) + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x3800 Entry 14 (size 64 bundles) Reserved + FAULT(14) + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x3c00 Entry 15 (size 64 bundles) Reserved + FAULT(15) + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x4000 Entry 16 (size 64 bundles) Reserved + FAULT(16) + +#ifdef CONFIG_IA32_SUPPORT + + // There is no particular reason for this code to be here, other than that + // there happens to be space here that would go unused otherwise. If this + // fault ever gets "unreserved", simply moved the following code to a more + // suitable spot... + + // IA32 interrupt entry point + +dispatch_to_ia32_handler: + SAVE_MIN + ;; + mov r14=cr.isr + ssm psr.ic | psr.dt + srlz.d // guarantee that interrupt collection is enabled + ;; +(p15) ssm psr.i + ;; + srlz.d + adds r3=8,r2 // Base pointer for SAVE_REST + ;; + SAVE_REST + ;; + mov r15=0x80 + shr r14=r14,16 // Get interrupt number + ;; + cmp.ne p6,p0=r14,r15 +(p6) br.call.dpnt.few b6=non_ia32_syscall + + adds r14=IA64_PT_REGS_R8_OFFSET + 16,sp // 16 byte hole per SW conventions + + ;; + alloc r15=ar.pfs,0,0,6,0 // must first in an insn group + ;; + ld4 r8=[r14],8 // r8 == EAX (syscall number) + mov r15=0xff + ;; + cmp.ltu.unc p6,p7=r8,r15 + ld4 out1=[r14],8 // r9 == ecx + ;; + ld4 out2=[r14],8 // r10 == edx + ;; + ld4 out0=[r14] // r11 == ebx + adds r14=(IA64_PT_REGS_R8_OFFSET-(8*3)) + 16,sp + ;; + ld4 out5=[r14],8 // r13 == ebp + ;; + ld4 out3=[r14],8 // r14 == esi + adds r2=IA64_TASK_FLAGS_OFFSET,r13 // r2 = ¤t->flags + ;; + ld4 out4=[r14] // R15 == edi + movl r16=ia32_syscall_table + ;; +(p6) shladd r16=r8,3,r16 // Force ni_syscall if not valid syscall number + ld8 r2=[r2] // r2 = current->flags + ;; + ld8 r16=[r16] + tbit.z p8,p0=r2,5 // (current->flags & PF_TRACESYS) == 0? + ;; + movl r15=ia32_ret_from_syscall + mov b6=r16 + ;; + mov rp=r15 +(p8) br.call.sptk.few b6=b6 + br.call.sptk.few rp=ia32_trace_syscall // rp will be overwritten (ignored) + +non_ia32_syscall: + alloc r15=ar.pfs,0,0,2,0 + mov out0=r14 // interrupt # + add out1=16,sp // pointer to pt_regs + ;; // avoid WAW on CFM + br.call.sptk.few rp=ia32_bad_interrupt + ;; + movl r15=ia64_leave_kernel + ;; + mov rp=r15 + br.ret.sptk.many rp + +#endif /* CONFIG_IA32_SUPPORT */ + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x4400 Entry 17 (size 64 bundles) Reserved + FAULT(17) + +non_syscall: + +#ifdef CONFIG_KDB + mov r17=__IA64_BREAK_KDB + ;; + cmp.eq p8,p0=r16,r17 // is this a kernel breakpoint? +#endif + + SAVE_MIN_WITH_COVER + + // There is no particular reason for this code to be here, other than that + // there happens to be space here that would go unused otherwise. If this + // fault ever gets "unreserved", simply moved the following code to a more + // suitable spot... + + mov r8=cr.iim // get break immediate (must be done while psr.ic is off) + adds r3=8,r2 // set up second base pointer for SAVE_REST + + // turn interrupt collection and data translation back on: + ssm psr.ic | psr.dt + srlz.d // guarantee that interrupt collection is enabled + ;; +(p15) ssm psr.i // restore psr.i + ;; + srlz.i // ensure everybody knows psr.ic and psr.dt are back on + movl r15=ia64_leave_kernel + ;; + alloc r14=ar.pfs,0,0,2,0 + mov out0=r8 // break number + add out1=16,sp // pointer to pt_regs + ;; + SAVE_REST + mov rp=r15 + ;; +#ifdef CONFIG_KDB +(p8) br.call.sptk.few b6=ia64_invoke_kdb +#endif + br.call.sptk.few b6=ia64_bad_break // avoid WAW on CFM and ignore return addr + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x4800 Entry 18 (size 64 bundles) Reserved + FAULT(18) + + // There is no particular reason for this code to be here, other than that + // there happens to be space here that would go unused otherwise. If this + // fault ever gets "unreserved", simply moved the following code to a more + // suitable spot... + +dispatch_unaligned_handler: + SAVE_MIN_WITH_COVER + ;; + // + // we can't have the alloc while psr.ic is cleared because + // we might get a mandatory RSE (when you reach the end of the + // rotating partition when doing the alloc) spill which could cause + // a page fault on the kernel virtual address and the handler + // wouldn't get the state to recover. + // + mov r15=cr.ifa + ssm psr.ic | psr.dt + srlz.d // guarantee that interrupt collection is enabled + ;; +(p15) ssm psr.i // restore psr.i + ;; + srlz.i + adds r3=8,r2 // set up second base pointer + ;; + SAVE_REST + ;; + alloc r14=ar.pfs,0,0,2,0 // now it's safe (must be first in insn group!) + ;; // avoid WAW on r14 + movl r14=ia64_leave_kernel + mov out0=r15 // out0 = faulting address + adds out1=16,sp // out1 = pointer to pt_regs + ;; + mov rp=r14 + br.sptk.few ia64_prepare_handle_unaligned + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x4c00 Entry 19 (size 64 bundles) Reserved + FAULT(19) + + // There is no particular reason for this code to be here, other than that + // there happens to be space here that would go unused otherwise. If this + // fault ever gets "unreserved", simply moved the following code to a more + // suitable spot... + +dispatch_to_fault_handler: + // + // Input: + // psr.ic: off + // psr.dt: off + // r19: fault vector number (e.g., 24 for General Exception) + // r31: contains saved predicates (pr) + // + SAVE_MIN_WITH_COVER_R19 + // + // Copy control registers to temporary registers, then turn on psr bits, + // then copy the temporary regs to the output regs. We have to do this + // because the "alloc" can cause a mandatory store which could lead to + // an "Alt DTLB" fault which we can handle only if psr.ic is on. + // + mov r8=cr.isr + mov r9=cr.ifa + mov r10=cr.iim + mov r11=cr.itir + ;; + ssm psr.ic | psr.dt + srlz.d // guarantee that interrupt collection is enabled + ;; +(p15) ssm psr.i // restore psr.i + adds r3=8,r2 // set up second base pointer for SAVE_REST + ;; + srlz.i // must precede "alloc"! + ;; + alloc r14=ar.pfs,0,0,5,0 // must be first in insn group + mov out0=r15 + mov out1=r8 + mov out2=r9 + mov out3=r10 + mov out4=r11 + ;; + SAVE_REST + movl r14=ia64_leave_kernel + ;; + mov rp=r14 +#ifdef CONFIG_KDB + br.call.sptk.few b6=ia64_invoke_kdb_fault_handler +#else + br.call.sptk.few b6=ia64_fault +#endif +// +// --- End of long entries, Beginning of short entries +// + + .align 1024 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5000 Entry 20 (size 16 bundles) Page Not Present (10,22,49) + mov r16=cr.ifa + rsm psr.dt +#if 0 + // If you disable this, you MUST re-enable to update_mmu_cache() code in pgtable.h + mov r17=_PAGE_SIZE_4K<<2 + ;; + ptc.l r16,r17 +#endif + ;; + mov r31=pr + srlz.d + br.cond.sptk.many page_fault + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5100 Entry 21 (size 16 bundles) Key Permission (13,25,52) + mov r16=cr.ifa + rsm psr.dt + mov r31=pr + ;; + srlz.d + br.cond.sptk.many page_fault + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5200 Entry 22 (size 16 bundles) Instruction Access Rights (26) + mov r16=cr.ifa + rsm psr.dt + mov r31=pr + ;; + srlz.d + br.cond.sptk.many page_fault + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5300 Entry 23 (size 16 bundles) Data Access Rights (14,53) + mov r16=cr.ifa + rsm psr.dt + mov r31=pr + ;; + srlz.d + br.cond.sptk.many page_fault + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5400 Entry 24 (size 16 bundles) General Exception (5,32,34,36,38,39) + FAULT(24) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5500 Entry 25 (size 16 bundles) Disabled FP-Register (35) + rsm psr.dt | psr.dfh // ensure we can access fph + ;; + srlz.d + mov r31=pr + mov r19=25 + br.cond.sptk.many dispatch_to_fault_handler + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5600 Entry 26 (size 16 bundles) Nat Consumption (11,23,37,50) + FAULT(26) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5700 Entry 27 (size 16 bundles) Speculation (40) + // + // A [f]chk.[as] instruction needs to take the branch to + // the recovery code but this part of the architecture is + // not implemented in hardware on some CPUs, such as Itanium. + // Thus, in general we need to emulate the behavior. + // IIM contains the relative target (not yet sign extended). + // So after sign extending it we simply add it to IIP. + // We also need to reset the EI field of the IPSR to zero, + // i.e., the slot to restart into. + // + // cr.imm contains zero_ext(imm21) + // + mov r18=cr.iim + ;; + mov r17=cr.iip + shl r18=r18,43 // put sign bit in position (43=64-21) + ;; + + mov r16=cr.ipsr + shr r18=r18,39 // sign extend (39=43-4) + ;; + + add r17=r17,r18 // now add the offset + ;; + mov cr.iip=r17 + dep r16=0,r16,41,2 // clear EI + ;; + + mov cr.ipsr=r16 + ;; + + rfi;; // and go back (must be last insn in group) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5800 Entry 28 (size 16 bundles) Reserved + FAULT(28) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5900 Entry 29 (size 16 bundles) Debug (16,28,56) + FAULT(29) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5a00 Entry 30 (size 16 bundles) Unaligned Reference (57) + rsm psr.dt // avoid nested faults due to TLB misses... + mov r16=cr.ipsr + mov r31=pr // prepare to save predicates + ;; + srlz.d // ensure everyone knows psr.dt is off + mov r19=30 // error vector for fault_handler (when kernel) + extr.u r16=r16,32,2 // extract psr.cpl + ;; + cmp.eq p6,p7=r0,r16 // if kernel cpl then fault else emulate +(p7) br.cond.sptk.many dispatch_unaligned_handler +(p6) br.cond.sptk.many dispatch_to_fault_handler + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5b00 Entry 31 (size 16 bundles) Unsupported Data Reference (57) + FAULT(31) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5c00 Entry 32 (size 16 bundles) Floating-Point Fault (64) + FAULT(32) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5d00 Entry 33 (size 16 bundles) Floating Point Trap (66) + FAULT(33) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5e00 Entry 34 (size 16 bundles) Lower Privilege Tranfer Trap (66) + FAULT(34) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x5f00 Entry 35 (size 16 bundles) Taken Branch Trap (68) + FAULT(35) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6000 Entry 36 (size 16 bundles) Single Step Trap (69) + FAULT(36) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6100 Entry 37 (size 16 bundles) Reserved + FAULT(37) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6200 Entry 38 (size 16 bundles) Reserved + FAULT(38) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6300 Entry 39 (size 16 bundles) Reserved + FAULT(39) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6400 Entry 40 (size 16 bundles) Reserved + FAULT(40) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6500 Entry 41 (size 16 bundles) Reserved + FAULT(41) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6600 Entry 42 (size 16 bundles) Reserved + FAULT(42) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6700 Entry 43 (size 16 bundles) Reserved + FAULT(43) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6800 Entry 44 (size 16 bundles) Reserved + FAULT(44) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6900 Entry 45 (size 16 bundles) IA-32 Exeception (17,18,29,41,42,43,44,58,60,61,62,72,73,75,76,77) + FAULT(45) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6a00 Entry 46 (size 16 bundles) IA-32 Intercept (30,31,59,70,71) + FAULT(46) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6b00 Entry 47 (size 16 bundles) IA-32 Interrupt (74) +#ifdef CONFIG_IA32_SUPPORT + rsm psr.dt + ;; + srlz.d + mov r31=pr + br.cond.sptk.many dispatch_to_ia32_handler +#else + FAULT(47) +#endif + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6c00 Entry 48 (size 16 bundles) Reserved + FAULT(48) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6d00 Entry 49 (size 16 bundles) Reserved + FAULT(49) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6e00 Entry 50 (size 16 bundles) Reserved + FAULT(50) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x6f00 Entry 51 (size 16 bundles) Reserved + FAULT(51) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7000 Entry 52 (size 16 bundles) Reserved + FAULT(52) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7100 Entry 53 (size 16 bundles) Reserved + FAULT(53) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7200 Entry 54 (size 16 bundles) Reserved + FAULT(54) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7300 Entry 55 (size 16 bundles) Reserved + FAULT(55) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7400 Entry 56 (size 16 bundles) Reserved + FAULT(56) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7500 Entry 57 (size 16 bundles) Reserved + FAULT(57) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7600 Entry 58 (size 16 bundles) Reserved + FAULT(58) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7700 Entry 59 (size 16 bundles) Reserved + FAULT(59) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7800 Entry 60 (size 16 bundles) Reserved + FAULT(60) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7900 Entry 61 (size 16 bundles) Reserved + FAULT(61) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7a00 Entry 62 (size 16 bundles) Reserved + FAULT(62) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7b00 Entry 63 (size 16 bundles) Reserved + FAULT(63) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7c00 Entry 64 (size 16 bundles) Reserved + FAULT(64) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7d00 Entry 65 (size 16 bundles) Reserved + FAULT(65) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7e00 Entry 66 (size 16 bundles) Reserved + FAULT(66) + + .align 256 +///////////////////////////////////////////////////////////////////////////////////////// +// 0x7f00 Entry 67 (size 16 bundles) Reserved + FAULT(67) diff --git a/arch/ia64/kernel/machvec.c b/arch/ia64/kernel/machvec.c new file mode 100644 index 000000000..153fb5684 --- /dev/null +++ b/arch/ia64/kernel/machvec.c @@ -0,0 +1,48 @@ +#include <linux/kernel.h> + +#include <asm/page.h> +#include <asm/machvec.h> + +struct ia64_machine_vector ia64_mv; + +void +machvec_noop (void) +{ +} + +/* + * Most platforms use this routine for mapping page frame addresses + * into a memory map index. + */ +unsigned long +map_nr_dense (unsigned long addr) +{ + return MAP_NR_DENSE(addr); +} + +static struct ia64_machine_vector * +lookup_machvec (const char *name) +{ + extern struct ia64_machine_vector machvec_start[]; + extern struct ia64_machine_vector machvec_end[]; + struct ia64_machine_vector *mv; + + for (mv = machvec_start; mv < machvec_end; ++mv) + if (strcmp (mv->name, name) == 0) + return mv; + + return 0; +} + +void +machvec_init (const char *name) +{ + struct ia64_machine_vector *mv; + + mv = lookup_machvec(name); + if (!mv) { + panic("generic kernel failed to find machine vector for platform %s!", name); + } + ia64_mv = *mv; + printk("booting generic kernel on platform %s\n", name); +} diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c new file mode 100644 index 000000000..320c56ebc --- /dev/null +++ b/arch/ia64/kernel/mca.c @@ -0,0 +1,842 @@ +/* + * File: mca.c + * Purpose: Generic MCA handling layer + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Vijay Chander(vijay@engr.sgi.com) + */ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <asm/page.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/sal.h> +#include <asm/mca.h> +#include <asm/spinlock.h> +#include <asm/irq.h> +#include <asm/machvec.h> + + +ia64_mc_info_t ia64_mc_info; +ia64_mca_sal_to_os_state_t ia64_sal_to_os_handoff_state; +ia64_mca_os_to_sal_state_t ia64_os_to_sal_handoff_state; +u64 ia64_mca_proc_state_dump[256]; +u64 ia64_mca_stack[1024]; +u64 ia64_mca_stackframe[32]; +u64 ia64_mca_bspstore[1024]; + +static void ia64_mca_cmc_vector_setup(int enable, + int_vector_t cmc_vector); +static void ia64_mca_wakeup_ipi_wait(void); +static void ia64_mca_wakeup(int cpu); +static void ia64_mca_wakeup_all(void); +static void ia64_log_init(int,int); +static void ia64_log_get(int,int, prfunc_t); +static void ia64_log_clear(int,int,int, prfunc_t); + +/* + * ia64_mca_cmc_vector_setup + * Setup the correctable machine check vector register in the processor + * Inputs + * Enable (1 - enable cmc interrupt , 0 - disable) + * CMC handler entry point (if enabled) + * + * Outputs + * None + */ +static void +ia64_mca_cmc_vector_setup(int enable, + int_vector_t cmc_vector) +{ + cmcv_reg_t cmcv; + + cmcv.cmcv_regval = 0; + cmcv.cmcv_mask = enable; + cmcv.cmcv_vector = cmc_vector; + ia64_set_cmcv(cmcv.cmcv_regval); +} + + +#if defined(MCA_TEST) + +sal_log_processor_info_t slpi_buf; + +void +mca_test(void) +{ + slpi_buf.slpi_valid.slpi_psi = 1; + slpi_buf.slpi_valid.slpi_cache_check = 1; + slpi_buf.slpi_valid.slpi_tlb_check = 1; + slpi_buf.slpi_valid.slpi_bus_check = 1; + slpi_buf.slpi_valid.slpi_minstate = 1; + slpi_buf.slpi_valid.slpi_bank1_gr = 1; + slpi_buf.slpi_valid.slpi_br = 1; + slpi_buf.slpi_valid.slpi_cr = 1; + slpi_buf.slpi_valid.slpi_ar = 1; + slpi_buf.slpi_valid.slpi_rr = 1; + slpi_buf.slpi_valid.slpi_fr = 1; + + ia64_os_mca_dispatch(); +} + +#endif /* #if defined(MCA_TEST) */ + +/* + * mca_init + * Do all the mca specific initialization on a per-processor basis. + * + * 1. Register spinloop and wakeup request interrupt vectors + * + * 2. Register OS_MCA handler entry point + * + * 3. Register OS_INIT handler entry point + * + * 4. Initialize CMCV register to enable/disable CMC interrupt on the + * processor and hook a handler in the platform-specific mca_init. + * + * 5. Initialize MCA/CMC/INIT related log buffers maintained by the OS. + * + * Inputs + * None + * Outputs + * None + */ +void __init +mca_init(void) +{ + int i; + + MCA_DEBUG("mca_init : begin\n"); + /* Clear the Rendez checkin flag for all cpus */ + for(i = 0 ; i < IA64_MAXCPUS; i++) + ia64_mc_info.imi_rendez_checkin[i] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE; + + /* NOTE : The actual irqs for the rendez, wakeup and + * cmc interrupts are requested in the platform-specific + * mca initialization code. + */ + /* + * Register the rendezvous spinloop and wakeup mechanism with SAL + */ + + /* Register the rendezvous interrupt vector with SAL */ + if (ia64_sal_mc_set_params(SAL_MC_PARAM_RENDEZ_INT, + SAL_MC_PARAM_MECHANISM_INT, + IA64_MCA_RENDEZ_INT_VECTOR, + IA64_MCA_RENDEZ_TIMEOUT)) + return; + + /* Register the wakeup interrupt vector with SAL */ + if (ia64_sal_mc_set_params(SAL_MC_PARAM_RENDEZ_WAKEUP, + SAL_MC_PARAM_MECHANISM_INT, + IA64_MCA_WAKEUP_INT_VECTOR, + 0)) + return; + + MCA_DEBUG("mca_init : registered mca rendezvous spinloop and wakeup mech.\n"); + /* + * Setup the correctable machine check vector + */ + ia64_mca_cmc_vector_setup(IA64_CMC_INT_ENABLE, + IA64_MCA_CMC_INT_VECTOR); + + MCA_DEBUG("mca_init : correctable mca vector setup done\n"); + + ia64_mc_info.imi_mca_handler = __pa(ia64_os_mca_dispatch); + ia64_mc_info.imi_mca_handler_size = + __pa(ia64_os_mca_dispatch_end) - __pa(ia64_os_mca_dispatch); + /* Register the os mca handler with SAL */ + if (ia64_sal_set_vectors(SAL_VECTOR_OS_MCA, + ia64_mc_info.imi_mca_handler, + __pa(ia64_get_gp()), + ia64_mc_info.imi_mca_handler_size, + 0,0,0)) + + return; + + MCA_DEBUG("mca_init : registered os mca handler with SAL\n"); + + ia64_mc_info.imi_monarch_init_handler = __pa(ia64_monarch_init_handler); + ia64_mc_info.imi_monarch_init_handler_size = IA64_INIT_HANDLER_SIZE; + ia64_mc_info.imi_slave_init_handler = __pa(ia64_slave_init_handler); + ia64_mc_info.imi_slave_init_handler_size = IA64_INIT_HANDLER_SIZE; + /* Register the os init handler with SAL */ + if (ia64_sal_set_vectors(SAL_VECTOR_OS_INIT, + ia64_mc_info.imi_monarch_init_handler, + __pa(ia64_get_gp()), + ia64_mc_info.imi_monarch_init_handler_size, + ia64_mc_info.imi_slave_init_handler, + __pa(ia64_get_gp()), + ia64_mc_info.imi_slave_init_handler_size)) + + + return; + + MCA_DEBUG("mca_init : registered os init handler with SAL\n"); + + /* Initialize the areas set aside by the OS to buffer the + * platform/processor error states for MCA/INIT/CMC + * handling. + */ + ia64_log_init(SAL_INFO_TYPE_MCA, SAL_SUB_INFO_TYPE_PROCESSOR); + ia64_log_init(SAL_INFO_TYPE_MCA, SAL_SUB_INFO_TYPE_PLATFORM); + ia64_log_init(SAL_INFO_TYPE_INIT, SAL_SUB_INFO_TYPE_PROCESSOR); + ia64_log_init(SAL_INFO_TYPE_INIT, SAL_SUB_INFO_TYPE_PLATFORM); + ia64_log_init(SAL_INFO_TYPE_CMC, SAL_SUB_INFO_TYPE_PROCESSOR); + ia64_log_init(SAL_INFO_TYPE_CMC, SAL_SUB_INFO_TYPE_PLATFORM); + + mca_init_platform(); + + MCA_DEBUG("mca_init : platform-specific mca handling setup done\n"); + +#if defined(MCA_TEST) + mca_test(); +#endif /* #if defined(MCA_TEST) */ + + printk("Mca related initialization done\n"); +} + +/* + * ia64_mca_wakeup_ipi_wait + * Wait for the inter-cpu interrupt to be sent by the + * monarch processor once it is done with handling the + * MCA. + * Inputs + * None + * Outputs + * None + */ +void +ia64_mca_wakeup_ipi_wait(void) +{ + int irr_num = (IA64_MCA_WAKEUP_INT_VECTOR >> 6); + int irr_bit = (IA64_MCA_WAKEUP_INT_VECTOR & 0x3f); + u64 irr = 0; + + do { + switch(irr_num) { + case 0: + irr = ia64_get_irr0(); + break; + case 1: + irr = ia64_get_irr1(); + break; + case 2: + irr = ia64_get_irr2(); + break; + case 3: + irr = ia64_get_irr3(); + break; + } + } while (!(irr & (1 << irr_bit))) ; +} + +/* + * ia64_mca_wakeup + * Send an inter-cpu interrupt to wake-up a particular cpu + * and mark that cpu to be out of rendez. + * Inputs + * cpuid + * Outputs + * None + */ +void +ia64_mca_wakeup(int cpu) +{ + ipi_send(cpu, IA64_MCA_WAKEUP_INT_VECTOR, IA64_IPI_DM_INT); + ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE; + +} +/* + * ia64_mca_wakeup_all + * Wakeup all the cpus which have rendez'ed previously. + * Inputs + * None + * Outputs + * None + */ +void +ia64_mca_wakeup_all(void) +{ + int cpu; + + /* Clear the Rendez checkin flag for all cpus */ + for(cpu = 0 ; cpu < IA64_MAXCPUS; cpu++) + if (ia64_mc_info.imi_rendez_checkin[cpu] == IA64_MCA_RENDEZ_CHECKIN_DONE) + ia64_mca_wakeup(cpu); + +} +/* + * ia64_mca_rendez_interrupt_handler + * This is handler used to put slave processors into spinloop + * while the monarch processor does the mca handling and later + * wake each slave up once the monarch is done. + * Inputs + * None + * Outputs + * None + */ +void +ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *ptregs) +{ + int flags; + /* Mask all interrupts */ + save_and_cli(flags); + + ia64_mc_info.imi_rendez_checkin[ia64_get_cpuid(0)] = IA64_MCA_RENDEZ_CHECKIN_DONE; + /* Register with the SAL monarch that the slave has + * reached SAL + */ + ia64_sal_mc_rendez(); + + /* Wait for the wakeup IPI from the monarch + * This waiting is done by polling on the wakeup-interrupt + * vector bit in the processor's IRRs + */ + ia64_mca_wakeup_ipi_wait(); + + /* Enable all interrupts */ + restore_flags(flags); + + +} + + +/* + * ia64_mca_wakeup_int_handler + * The interrupt handler for processing the inter-cpu interrupt to the + * slave cpu which was spinning in the rendez loop. + * Since this spinning is done by turning off the interrupts and + * polling on the wakeup-interrupt bit in the IRR, there is + * nothing useful to be done in the handler. + * Inputs + * wakeup_irq (Wakeup-interrupt bit) + * arg (Interrupt handler specific argument) + * ptregs (Exception frame at the time of the interrupt) + * Outputs + * + */ +void +ia64_mca_wakeup_int_handler(int wakeup_irq, void *arg, struct pt_regs *ptregs) +{ + +} + +/* + * ia64_return_to_sal_check + * This is function called before going back from the OS_MCA handler + * to the OS_MCA dispatch code which finally takes the control back + * to the SAL. + * The main purpose of this routine is to setup the OS_MCA to SAL + * return state which can be used by the OS_MCA dispatch code + * just before going back to SAL. + * Inputs + * None + * Outputs + * None + */ + +void +ia64_return_to_sal_check(void) +{ + /* Copy over some relevant stuff from the sal_to_os_mca_handoff + * so that it can be used at the time of os_mca_to_sal_handoff + */ + ia64_os_to_sal_handoff_state.imots_sal_gp = + ia64_sal_to_os_handoff_state.imsto_sal_gp; + + ia64_os_to_sal_handoff_state.imots_sal_check_ra = + ia64_sal_to_os_handoff_state.imsto_sal_check_ra; + + /* For now ignore the MCA */ + ia64_os_to_sal_handoff_state.imots_os_status = IA64_MCA_CORRECTED; +} +/* + * ia64_mca_ucmc_handler + * This is uncorrectable machine check handler called from OS_MCA + * dispatch code which is in turn called from SAL_CHECK(). + * This is the place where the core of OS MCA handling is done. + * Right now the logs are extracted and displayed in a well-defined + * format. This handler code is supposed to be run only on the + * monarch processor. Once the monarch is done with MCA handling + * further MCA logging is enabled by clearing logs. + * Monarch also has the duty of sending wakeup-IPIs to pull the + * slave processors out of rendez. spinloop. + * Inputs + * None + * Outputs + * None + */ +void +ia64_mca_ucmc_handler(void) +{ + + /* Get the MCA processor log */ + ia64_log_get(SAL_INFO_TYPE_MCA, SAL_SUB_INFO_TYPE_PROCESSOR, (prfunc_t)printk); + /* Get the MCA platform log */ + ia64_log_get(SAL_INFO_TYPE_MCA, SAL_SUB_INFO_TYPE_PLATFORM, (prfunc_t)printk); + + ia64_log_print(SAL_INFO_TYPE_MCA, SAL_SUB_INFO_TYPE_PROCESSOR, (prfunc_t)printk); + + /* + * Do some error handling - Platform-specific mca handler is called at this point + */ + + mca_handler_platform() ; + + /* Clear the SAL MCA logs */ + ia64_log_clear(SAL_INFO_TYPE_MCA, SAL_SUB_INFO_TYPE_PROCESSOR, 1, printk); + ia64_log_clear(SAL_INFO_TYPE_MCA, SAL_SUB_INFO_TYPE_PLATFORM, 1, printk); + + /* Wakeup all the processors which are spinning in the rendezvous + * loop. + */ + ia64_mca_wakeup_all(); + ia64_return_to_sal_check(); +} + +/* + * SAL to OS entry point for INIT on the monarch processor + * This has been defined for registration purposes with SAL + * as a part of mca_init. + */ +void +ia64_monarch_init_handler() +{ +} +/* + * SAL to OS entry point for INIT on the slave processor + * This has been defined for registration purposes with SAL + * as a part of mca_init. + */ + +void +ia64_slave_init_handler() +{ +} +/* + * ia64_mca_cmc_int_handler + * This is correctable machine check interrupt handler. + * Right now the logs are extracted and displayed in a well-defined + * format. + * Inputs + * None + * Outputs + * None + */ +void +ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs) +{ + /* Get the CMC processor log */ + ia64_log_get(SAL_INFO_TYPE_CMC, SAL_SUB_INFO_TYPE_PROCESSOR, (prfunc_t)printk); + /* Get the CMC platform log */ + ia64_log_get(SAL_INFO_TYPE_CMC, SAL_SUB_INFO_TYPE_PLATFORM, (prfunc_t)printk); + + + ia64_log_print(SAL_INFO_TYPE_CMC, SAL_SUB_INFO_TYPE_PROCESSOR, (prfunc_t)printk); + cmci_handler_platform(cmc_irq, arg, ptregs); + + /* Clear the CMC SAL logs now that they have been saved in the OS buffer */ + ia64_sal_clear_state_info(SAL_INFO_TYPE_CMC, SAL_SUB_INFO_TYPE_PROCESSOR); + ia64_sal_clear_state_info(SAL_INFO_TYPE_CMC, SAL_SUB_INFO_TYPE_PLATFORM); +} + +/* + * IA64_MCA log support + */ +#define IA64_MAX_LOGS 2 /* Double-buffering for nested MCAs */ +#define IA64_MAX_LOG_TYPES 3 /* MCA, CMC, INIT */ +#define IA64_MAX_LOG_SUBTYPES 2 /* Processor, Platform */ + +typedef struct ia64_state_log_s { + spinlock_t isl_lock; + int isl_index; + sal_log_header_t isl_log[IA64_MAX_LOGS]; + +} ia64_state_log_t; + +static ia64_state_log_t ia64_state_log[IA64_MAX_LOG_TYPES][IA64_MAX_LOG_SUBTYPES]; + +#define IA64_LOG_LOCK_INIT(it, sit) spin_lock_init(&ia64_state_log[it][sit].isl_lock) +#define IA64_LOG_LOCK(it, sit) spin_lock_irqsave(&ia64_state_log[it][sit].isl_lock, s) +#define IA64_LOG_UNLOCK(it, sit) spin_unlock_irqrestore(&ia64_state_log[it][sit].isl_lock,\ + s) +#define IA64_LOG_NEXT_INDEX(it, sit) ia64_state_log[it][sit].isl_index +#define IA64_LOG_CURR_INDEX(it, sit) 1 - ia64_state_log[it][sit].isl_index +#define IA64_LOG_INDEX_INC(it, sit) \ + ia64_state_log[it][sit].isl_index = 1 - ia64_state_log[it][sit].isl_index +#define IA64_LOG_INDEX_DEC(it, sit) \ + ia64_state_log[it][sit].isl_index = 1 - ia64_state_log[it][sit].isl_index +#define IA64_LOG_NEXT_BUFFER(it, sit) (void *)(&(ia64_state_log[it][sit].isl_log[IA64_LOG_NEXT_INDEX(it,sit)])) +#define IA64_LOG_CURR_BUFFER(it, sit) (void *)(&(ia64_state_log[it][sit].isl_log[IA64_LOG_CURR_INDEX(it,sit)])) + +/* + * ia64_log_init + * Reset the OS ia64 log buffer + * Inputs : info_type (SAL_INFO_TYPE_{MCA,INIT,CMC}) + * sub_info_type (SAL_SUB_INFO_TYPE_{PROCESSOR,PLATFORM}) + * Outputs : None + */ +void +ia64_log_init(int sal_info_type, int sal_sub_info_type) +{ + IA64_LOG_LOCK_INIT(sal_info_type, sal_sub_info_type); + IA64_LOG_NEXT_INDEX(sal_info_type, sal_sub_info_type) = 0; + memset(IA64_LOG_NEXT_BUFFER(sal_info_type, sal_sub_info_type), 0, + sizeof(sal_log_header_t) * IA64_MAX_LOGS); +} + +/* + * ia64_log_get + * Get the current MCA log from SAL and copy it into the OS log buffer. + * Inputs : info_type (SAL_INFO_TYPE_{MCA,INIT,CMC}) + * sub_info_type (SAL_SUB_INFO_TYPE_{PROCESSOR,PLATFORM}) + * Outputs : None + * + */ +void +ia64_log_get(int sal_info_type, int sal_sub_info_type, prfunc_t prfunc) +{ + sal_log_header_t *log_buffer; + int s; + + IA64_LOG_LOCK(sal_info_type, sal_sub_info_type); + + + /* Get the process state information */ + log_buffer = IA64_LOG_NEXT_BUFFER(sal_info_type, sal_sub_info_type); + + if (ia64_sal_get_state_info(sal_info_type, sal_sub_info_type ,(u64 *)log_buffer)) + prfunc("ia64_mca_log_get : Getting processor log failed\n"); + + IA64_LOG_INDEX_INC(sal_info_type, sal_sub_info_type); + + IA64_LOG_UNLOCK(sal_info_type, sal_sub_info_type); + +} + +/* + * ia64_log_clear + * Clear the current MCA log from SAL and dpending on the clear_os_buffer flags + * clear the OS log buffer also + * Inputs : info_type (SAL_INFO_TYPE_{MCA,INIT,CMC}) + * sub_info_type (SAL_SUB_INFO_TYPE_{PROCESSOR,PLATFORM}) + * clear_os_buffer + * prfunc (print function) + * Outputs : None + * + */ +void +ia64_log_clear(int sal_info_type, int sal_sub_info_type, int clear_os_buffer, prfunc_t prfunc) +{ + if (ia64_sal_clear_state_info(sal_info_type, sal_sub_info_type)) + prfunc("ia64_mca_log_get : Clearing processor log failed\n"); + + if (clear_os_buffer) { + sal_log_header_t *log_buffer; + int s; + + IA64_LOG_LOCK(sal_info_type, sal_sub_info_type); + + /* Get the process state information */ + log_buffer = IA64_LOG_CURR_BUFFER(sal_info_type, sal_sub_info_type); + + memset(log_buffer, 0, sizeof(sal_log_header_t)); + + IA64_LOG_INDEX_DEC(sal_info_type, sal_sub_info_type); + + IA64_LOG_UNLOCK(sal_info_type, sal_sub_info_type); + } + +} + +/* + * ia64_log_processor_regs_print + * Print the contents of the saved processor register(s) in the format + * <reg_prefix>[<index>] <value> + * + * Inputs : regs (Register save buffer) + * reg_num (# of registers) + * reg_class (application/banked/control/bank1_general) + * reg_prefix (ar/br/cr/b1_gr) + * Outputs : None + * + */ +void +ia64_log_processor_regs_print(u64 *regs, + int reg_num, + char *reg_class, + char *reg_prefix, + prfunc_t prfunc) +{ + int i; + + prfunc("+%s Registers\n", reg_class); + for (i = 0; i < reg_num; i++) + prfunc("+ %s[%d] 0x%lx\n", reg_prefix, i, regs[i]); +} + +static char *pal_mesi_state[] = { + "Invalid", + "Shared", + "Exclusive", + "Modified", + "Reserved1", + "Reserved2", + "Reserved3", + "Reserved4" +}; + +static char *pal_cache_op[] = { + "Unknown", + "Move in", + "Cast out", + "Coherency check", + "Internal", + "Instruction fetch", + "Implicit Writeback", + "Reserved" +}; + +/* + * ia64_log_cache_check_info_print + * Display the machine check information related to cache error(s). + * Inputs : i (Multiple errors are logged, i - index of logged error) + * info (Machine check info logged by the PAL and later + * captured by the SAL) + * target_addr (Address which caused the cache error) + * Outputs : None + */ +void +ia64_log_cache_check_info_print(int i, + pal_cache_check_info_t info, + u64 target_addr, + prfunc_t prfunc) +{ + prfunc("+ Cache check info[%d]\n+", i); + prfunc(" Level: L%d",info.level); + if (info.mv) + prfunc(" ,Mesi: %s",pal_mesi_state[info.mesi]); + prfunc(" ,Index: %d,", info.index); + if (info.ic) + prfunc(" ,Cache: Instruction"); + if (info.dc) + prfunc(" ,Cache: Data"); + if (info.tl) + prfunc(" ,Line: Tag"); + if (info.dl) + prfunc(" ,Line: Data"); + prfunc(" ,Operation: %s,", pal_cache_op[info.op]); + if (info.wv) + prfunc(" ,Way: %d,", info.way); + if (info.tv) + prfunc(" ,Target Addr: 0x%lx", target_addr); + if (info.mc) + prfunc(" ,MC: Corrected"); + prfunc("\n"); +} + +/* + * ia64_log_tlb_check_info_print + * Display the machine check information related to tlb error(s). + * Inputs : i (Multiple errors are logged, i - index of logged error) + * info (Machine check info logged by the PAL and later + * captured by the SAL) + * Outputs : None + */ + +void +ia64_log_tlb_check_info_print(int i, + pal_tlb_check_info_t info, + prfunc_t prfunc) +{ + prfunc("+ TLB Check Info [%d]\n+", i); + if (info.itc) + prfunc(" Failure: Instruction Translation Cache"); + if (info.dtc) + prfunc(" Failure: Data Translation Cache"); + if (info.itr) { + prfunc(" Failure: Instruction Translation Register"); + prfunc(" ,Slot: %d", info.tr_slot); + } + if (info.dtr) { + prfunc(" Failure: Data Translation Register"); + prfunc(" ,Slot: %d", info.tr_slot); + } + if (info.mc) + prfunc(" ,MC: Corrected"); + prfunc("\n"); +} + +/* + * ia64_log_bus_check_info_print + * Display the machine check information related to bus error(s). + * Inputs : i (Multiple errors are logged, i - index of logged error) + * info (Machine check info logged by the PAL and later + * captured by the SAL) + * req_addr (Address of the requestor of the transaction) + * resp_addr (Address of the responder of the transaction) + * target_addr (Address where the data was to be delivered to or + * obtained from) + * Outputs : None + */ +void +ia64_log_bus_check_info_print(int i, + pal_bus_check_info_t info, + u64 req_addr, + u64 resp_addr, + u64 targ_addr, + prfunc_t prfunc) +{ + prfunc("+ BUS Check Info [%d]\n+", i); + prfunc(" Status Info: %d", info.bsi); + prfunc(" ,Severity: %d", info.sev); + prfunc(" ,Transaction Type: %d", info.type); + prfunc(" ,Transaction Size: %d", info.size); + if (info.cc) + prfunc(" ,Cache-cache-transfer"); + if (info.ib) + prfunc(" ,Error: Internal"); + if (info.eb) + prfunc(" ,Error: External"); + if (info.mc) + prfunc(" ,MC: Corrected"); + if (info.tv) + prfunc(" ,Target Address: 0x%lx", targ_addr); + if (info.rq) + prfunc(" ,Requestor Address: 0x%lx", req_addr); + if (info.tv) + prfunc(" ,Responder Address: 0x%lx", resp_addr); + prfunc("\n"); +} + +/* + * ia64_log_processor_info_print + * Display the processor-specific information logged by PAL as a part + * of MCA or INIT or CMC. + * Inputs : lh (Pointer of the sal log header which specifies the format + * of SAL state info as specified by the SAL spec). + * Outputs : None + */ +void +ia64_log_processor_info_print(sal_log_header_t *lh, prfunc_t prfunc) +{ + sal_log_processor_info_t *slpi; + int i; + + if (!lh) + return; + + if (lh->slh_log_type != SAL_SUB_INFO_TYPE_PROCESSOR) + return; + +#if defined(MCA_TEST) + slpi = &slpi_buf; +#else + slpi = (sal_log_processor_info_t *)lh->slh_log_dev_spec_info; +#endif /#if defined(MCA_TEST) */ + + if (!slpi) { + prfunc("No Processor Error Log found\n"); + return; + } + + /* Print branch register contents if valid */ + if (slpi->slpi_valid.slpi_br) + ia64_log_processor_regs_print(slpi->slpi_br, 8, "Branch", "br", prfunc); + + /* Print control register contents if valid */ + if (slpi->slpi_valid.slpi_cr) + ia64_log_processor_regs_print(slpi->slpi_cr, 128, "Control", "cr", prfunc); + + /* Print application register contents if valid */ + if (slpi->slpi_valid.slpi_ar) + ia64_log_processor_regs_print(slpi->slpi_br, 128, "Application", "ar", prfunc); + + /* Print region register contents if valid */ + if (slpi->slpi_valid.slpi_rr) + ia64_log_processor_regs_print(slpi->slpi_rr, 8, "Region", "rr", prfunc); + + /* Print floating-point register contents if valid */ + if (slpi->slpi_valid.slpi_fr) + ia64_log_processor_regs_print(slpi->slpi_fr, 128, "Floating-point", "fr", + prfunc); + + /* Print bank1-gr NAT register contents if valid */ + ia64_log_processor_regs_print(&slpi->slpi_bank1_nat_bits, 1, "NAT", "nat", prfunc); + + /* Print bank 1 register contents if valid */ + if (slpi->slpi_valid.slpi_bank1_gr) + ia64_log_processor_regs_print(slpi->slpi_bank1_gr, 16, "Bank1-General", "gr", + prfunc); + + /* Print the cache check information if any*/ + for (i = 0 ; i < MAX_CACHE_ERRORS; i++) + ia64_log_cache_check_info_print(i, + slpi->slpi_cache_check_info[i].slpi_cache_check, + slpi->slpi_cache_check_info[i].slpi_target_address, + prfunc); + /* Print the tlb check information if any*/ + for (i = 0 ; i < MAX_TLB_ERRORS; i++) + ia64_log_tlb_check_info_print(i,slpi->slpi_tlb_check_info[i], prfunc); + + /* Print the bus check information if any*/ + for (i = 0 ; i < MAX_BUS_ERRORS; i++) + ia64_log_bus_check_info_print(i, + slpi->slpi_bus_check_info[i].slpi_bus_check, + slpi->slpi_bus_check_info[i].slpi_requestor_addr, + slpi->slpi_bus_check_info[i].slpi_responder_addr, + slpi->slpi_bus_check_info[i].slpi_target_addr, + prfunc); + +} + +/* + * ia64_log_print + * Display the contents of the OS error log information + * Inputs : info_type (SAL_INFO_TYPE_{MCA,INIT,CMC}) + * sub_info_type (SAL_SUB_INFO_TYPE_{PROCESSOR,PLATFORM}) + * Outputs : None + */ +void +ia64_log_print(int sal_info_type, int sal_sub_info_type, prfunc_t prfunc) +{ + char *info_type, *sub_info_type; + + switch(sal_info_type) { + case SAL_INFO_TYPE_MCA: + info_type = "MCA"; + break; + case SAL_INFO_TYPE_INIT: + info_type = "INIT"; + break; + case SAL_INFO_TYPE_CMC: + info_type = "CMC"; + break; + default: + info_type = "UNKNOWN"; + break; + } + + switch(sal_sub_info_type) { + case SAL_SUB_INFO_TYPE_PROCESSOR: + sub_info_type = "PROCESSOR"; + break; + case SAL_SUB_INFO_TYPE_PLATFORM: + sub_info_type = "PLATFORM"; + break; + default: + sub_info_type = "UNKNOWN"; + break; + } + + prfunc("+BEGIN HARDWARE ERROR STATE [%s %s]\n", info_type, sub_info_type); + if (sal_sub_info_type == SAL_SUB_INFO_TYPE_PROCESSOR) + ia64_log_processor_info_print( + IA64_LOG_CURR_BUFFER(sal_info_type, sal_sub_info_type), + prfunc); + else + log_print_platform(IA64_LOG_CURR_BUFFER(sal_info_type, sal_sub_info_type),prfunc); + prfunc("+END HARDWARE ERROR STATE [%s %s]\n", info_type, sub_info_type); +} diff --git a/arch/ia64/kernel/mca_asm.S b/arch/ia64/kernel/mca_asm.S new file mode 100644 index 000000000..3d49ac06e --- /dev/null +++ b/arch/ia64/kernel/mca_asm.S @@ -0,0 +1,621 @@ +#include <asm/processor.h> +#include <asm/mcaasm.h> +#include <asm/page.h> +#include <asm/mca.h> + + .psr abi64 + .psr lsb + .lsb + +/* + * SAL_TO_OS_MCA_HANDOFF_STATE + * 1. GR1 = OS GP + * 2. GR8 = PAL_PROC physical address + * 3. GR9 = SAL_PROC physical address + * 4. GR10 = SAL GP (physical) + * 5. GR11 = Rendez state + * 6. GR12 = Return address to location within SAL_CHECK + */ +#define SAL_TO_OS_MCA_HANDOFF_STATE_SAVE(_tmp) \ + movl _tmp=ia64_sal_to_os_handoff_state;; \ + st8 [_tmp]=r1,0x08;; \ + st8 [_tmp]=r8,0x08;; \ + st8 [_tmp]=r9,0x08;; \ + st8 [_tmp]=r10,0x08;; \ + st8 [_tmp]=r11,0x08;; \ + st8 [_tmp]=r12,0x08;; + +/* + * OS_MCA_TO_SAL_HANDOFF_STATE + * 1. GR8 = OS_MCA status + * 2. GR9 = SAL GP (physical) + * 3. GR22 = New min state save area pointer + */ +#define OS_MCA_TO_SAL_HANDOFF_STATE_RESTORE(_tmp) \ + movl _tmp=ia64_os_to_sal_handoff_state;; \ + DATA_VA_TO_PA(_tmp);; \ + ld8 r8=[_tmp],0x08;; \ + ld8 r9=[_tmp],0x08;; \ + ld8 r22=[_tmp],0x08;; + +/* + * BRANCH + * Jump to the instruction referenced by + * "to_label". + * Branch is taken only if the predicate + * register "p" is true. + * "ip" is the address of the instruction + * located at "from_label". + * "temp" is a scratch register like r2 + * "adjust" needed for HP compiler. + * A screwup somewhere with constant arithmetic. + */ +#define BRANCH(to_label, temp, p, adjust) \ +100: (p) mov temp=ip; \ + ;; \ + (p) adds temp=to_label-100b,temp;\ + (p) adds temp=adjust,temp; \ + (p) mov b1=temp ; \ + (p) br b1 + + .global ia64_os_mca_dispatch + .global ia64_os_mca_dispatch_end + .global ia64_sal_to_os_handoff_state + .global ia64_os_to_sal_handoff_state + .global ia64_os_mca_ucmc_handler + .global ia64_mca_proc_state_dump + .global ia64_mca_proc_state_restore + .global ia64_mca_stack + .global ia64_mca_stackframe + .global ia64_mca_bspstore + + .text + .align 16 + +ia64_os_mca_dispatch: + +#if defined(MCA_TEST) + // Pretend that we are in interrupt context + mov r2=psr + dep r2=0, r2, PSR_IC, 2; + mov psr.l = r2 +#endif /* #if defined(MCA_TEST) */ + + // Save the SAL to OS MCA handoff state as defined + // by SAL SPEC 2.5 + // NOTE : The order in which the state gets saved + // is dependent on the way the C-structure + // for ia64_mca_sal_to_os_state_t has been + // defined in include/asm/mca.h + SAL_TO_OS_MCA_HANDOFF_STATE_SAVE(r2) + + // LOG PROCESSOR STATE INFO FROM HERE ON.. + ;; +begin_os_mca_dump: + BRANCH(ia64_os_mca_proc_state_dump, r2, p0, 0x0) + ;; +ia64_os_mca_done_dump: + + // Setup new stack frame for OS_MCA handling + movl r2=ia64_mca_bspstore // local bspstore area location in r2 + movl r3=ia64_mca_stackframe // save stack frame to memory in r3 + rse_switch_context(r6,r3,r2);; // RSC management in this new context + movl r12=ia64_mca_stack;; + + // Enter virtual mode from physical mode + VIRTUAL_MODE_ENTER(r2, r3, ia64_os_mca_virtual_begin, r4) +ia64_os_mca_virtual_begin: + + // call our handler + movl r2=ia64_mca_ucmc_handler;; + mov b6=r2;; + br.call.sptk.few b0=b6 + ;; + + // Revert back to physical mode before going back to SAL + PHYSICAL_MODE_ENTER(r2, r3, ia64_os_mca_virtual_end, r4) +ia64_os_mca_virtual_end: + +#if defined(MCA_TEST) + // Pretend that we are in interrupt context + mov r2=psr + dep r2=0, r2, PSR_IC, 2; + mov psr.l = r2 +#endif /* #if defined(MCA_TEST) */ + + // restore the original stack frame here + movl r2=ia64_mca_stackframe // restore stack frame from memory at r2 + ;; + DATA_VA_TO_PA(r2) + movl r4=IA64_PSR_MC + ;; + rse_return_context(r4,r3,r2) // switch from interrupt context for RSE + + // let us restore all the registers from our PSI structure + mov r8=gp + ;; +begin_os_mca_restore: + BRANCH(ia64_os_mca_proc_state_restore, r2, p0, 0x0) + ;; + +ia64_os_mca_done_restore: + ;; +#ifdef SOFTSDV + VIRTUAL_MODE_ENTER(r2,r3, vmode_enter, r4) +vmode_enter: + br.ret.sptk.few b0 +#else + // branch back to SALE_CHECK + OS_MCA_TO_SAL_HANDOFF_STATE_RESTORE(r2) + ld8 r3=[r2];; + mov b0=r3 // SAL_CHECK return address + br b0 + ;; +#endif /* #ifdef SOFTSDV */ +ia64_os_mca_dispatch_end: +//EndMain////////////////////////////////////////////////////////////////////// + + +//++ +// Name: +// ia64_os_mca_proc_state_dump() +// +// Stub Description: +// +// This stub dumps the processor state during MCHK to a data area +// +//-- + +ia64_os_mca_proc_state_dump: +// Get and save GR0-31 from Proc. Min. State Save Area to SAL PSI + movl r2=ia64_mca_proc_state_dump;; // Os state dump area + +// save ar.NaT + mov r5=ar.unat // ar.unat + +// save banked GRs 16-31 along with NaT bits + bsw.1;; + st8.spill [r2]=r16,8;; + st8.spill [r2]=r17,8;; + st8.spill [r2]=r18,8;; + st8.spill [r2]=r19,8;; + st8.spill [r2]=r20,8;; + st8.spill [r2]=r21,8;; + st8.spill [r2]=r22,8;; + st8.spill [r2]=r23,8;; + st8.spill [r2]=r24,8;; + st8.spill [r2]=r25,8;; + st8.spill [r2]=r26,8;; + st8.spill [r2]=r27,8;; + st8.spill [r2]=r28,8;; + st8.spill [r2]=r29,8;; + st8.spill [r2]=r30,8;; + st8.spill [r2]=r31,8;; + + mov r4=ar.unat;; + st8 [r2]=r4,8 // save User NaT bits for r16-r31 + mov ar.unat=r5 // restore original unat + bsw.0;; + +//save BRs + add r4=8,r2 // duplicate r2 in r4 + add r6=2*8,r2 // duplicate r2 in r4 + + mov r3=b0 + mov r5=b1 + mov r7=b2;; + st8 [r2]=r3,3*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; + + mov r3=b3 + mov r5=b4 + mov r7=b5;; + st8 [r2]=r3,3*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; + + mov r3=b6 + mov r5=b7;; + st8 [r2]=r3,2*8 + st8 [r4]=r5,2*8;; + +cSaveCRs: +// save CRs + add r4=8,r2 // duplicate r2 in r4 + add r6=2*8,r2 // duplicate r2 in r4 + + mov r3=cr0 // cr.dcr + mov r5=cr1 // cr.itm + mov r7=cr2;; // cr.iva + + st8 [r2]=r3,8*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; // 48 byte rements + + mov r3=cr8;; // cr.pta + st8 [r2]=r3,8*8;; // 64 byte rements + +// if PSR.ic=0, reading interruption registers causes an illegal operation fault + mov r3=psr;; + tbit.nz.unc p2,p0=r3,PSR_IC;; // PSI Valid Log bit pos. test +(p2) st8 [r2]=r0,9*8+160 // increment by 168 byte inc. +begin_skip_intr_regs: + BRANCH(SkipIntrRegs, r9, p2, 0x0) + ;; + add r4=8,r2 // duplicate r2 in r4 + add r6=2*8,r2 // duplicate r2 in r6 + + mov r3=cr16 // cr.ipsr + mov r5=cr17 // cr.isr + mov r7=r0;; // cr.ida => cr18 + st8 [r2]=r3,3*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; + + mov r3=cr19 // cr.iip + mov r5=cr20 // cr.idtr + mov r7=cr21;; // cr.iitr + st8 [r2]=r3,3*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; + + mov r3=cr22 // cr.iipa + mov r5=cr23 // cr.ifs + mov r7=cr24;; // cr.iim + st8 [r2]=r3,3*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; + + mov r3=cr25;; // cr.iha + st8 [r2]=r3,160;; // 160 byte rement + +SkipIntrRegs: + st8 [r2]=r0,168 // another 168 byte . + + mov r3=cr66;; // cr.lid + st8 [r2]=r3,40 // 40 byte rement + + mov r3=cr71;; // cr.ivr + st8 [r2]=r3,8 + + mov r3=cr72;; // cr.tpr + st8 [r2]=r3,24 // 24 byte increment + + mov r3=r0;; // cr.eoi => cr75 + st8 [r2]=r3,168 // 168 byte inc. + + mov r3=r0;; // cr.irr0 => cr96 + st8 [r2]=r3,16 // 16 byte inc. + + mov r3=r0;; // cr.irr1 => cr98 + st8 [r2]=r3,16 // 16 byte inc. + + mov r3=r0;; // cr.irr2 => cr100 + st8 [r2]=r3,16 // 16 byte inc + + mov r3=r0;; // cr.irr3 => cr100 + st8 [r2]=r3,16 // 16b inc. + + mov r3=r0;; // cr.itv => cr114 + st8 [r2]=r3,16 // 16 byte inc. + + mov r3=r0;; // cr.pmv => cr116 + st8 [r2]=r3,8 + + mov r3=r0;; // cr.lrr0 => cr117 + st8 [r2]=r3,8 + + mov r3=r0;; // cr.lrr1 => cr118 + st8 [r2]=r3,8 + + mov r3=r0;; // cr.cmcv => cr119 + st8 [r2]=r3,8*10;; + +cSaveARs: +// save ARs + add r4=8,r2 // duplicate r2 in r4 + add r6=2*8,r2 // duplicate r2 in r6 + + mov r3=ar0 // ar.kro + mov r5=ar1 // ar.kr1 + mov r7=ar2;; // ar.kr2 + st8 [r2]=r3,3*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; + + mov r3=ar3 // ar.kr3 + mov r5=ar4 // ar.kr4 + mov r7=ar5;; // ar.kr5 + st8 [r2]=r3,3*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; + + mov r3=ar6 // ar.kr6 + mov r5=ar7 // ar.kr7 + mov r7=r0;; // ar.kr8 + st8 [r2]=r3,10*8 + st8 [r4]=r5,10*8 + st8 [r6]=r7,10*8;; // rement by 72 bytes + + mov r3=ar16 // ar.rsc + mov ar16=r0 // put RSE in enforced lazy mode + mov r5=ar17 // ar.bsp + mov r7=ar18;; // ar.bspstore + st8 [r2]=r3,3*8 + st8 [r4]=r5,3*8 + st8 [r6]=r7,3*8;; + + mov r3=ar19;; // ar.rnat + st8 [r2]=r3,8*13 // increment by 13x8 bytes + + mov r3=ar32;; // ar.ccv + st8 [r2]=r3,8*4 + + mov r3=ar36;; // ar.unat + st8 [r2]=r3,8*4 + + mov r3=ar40;; // ar.fpsr + st8 [r2]=r3,8*4 + + mov r3=ar44;; // ar.itc + st8 [r2]=r3,160 // 160 + + mov r3=ar64;; // ar.pfs + st8 [r2]=r3,8 + + mov r3=ar65;; // ar.lc + st8 [r2]=r3,8 + + mov r3=ar66;; // ar.ec + st8 [r2]=r3 + add r2=8*62,r2 //padding + +// save RRs + mov ar.lc=0x08-1 + movl r4=0x00;; + +cStRR: + mov r3=rr[r4];; + st8 [r2]=r3,8 + add r4=1,r4 + br.cloop.sptk.few cStRR + ;; +end_os_mca_dump: + BRANCH(ia64_os_mca_done_dump, r2, p0, -0x10) + ;; + +//EndStub////////////////////////////////////////////////////////////////////// + + +//++ +// Name: +// ia64_os_mca_proc_state_restore() +// +// Stub Description: +// +// This is a stub to restore the saved processor state during MCHK +// +//-- + +ia64_os_mca_proc_state_restore: + +// Restore bank1 GR16-31 + movl r2=ia64_mca_proc_state_dump // Convert virtual address + ;; // of OS state dump area + DATA_VA_TO_PA(r2) // to physical address + ;; +restore_GRs: // restore bank-1 GRs 16-31 + bsw.1;; + add r3=16*8,r2;; // to get to NaT of GR 16-31 + ld8 r3=[r3];; + mov ar.unat=r3;; // first restore NaT + + ld8.fill r16=[r2],8;; + ld8.fill r17=[r2],8;; + ld8.fill r18=[r2],8;; + ld8.fill r19=[r2],8;; + ld8.fill r20=[r2],8;; + ld8.fill r21=[r2],8;; + ld8.fill r22=[r2],8;; + ld8.fill r23=[r2],8;; + ld8.fill r24=[r2],8;; + ld8.fill r25=[r2],8;; + ld8.fill r26=[r2],8;; + ld8.fill r27=[r2],8;; + ld8.fill r28=[r2],8;; + ld8.fill r29=[r2],8;; + ld8.fill r30=[r2],8;; + ld8.fill r31=[r2],8;; + + ld8 r3=[r2],8;; // increment to skip NaT + bsw.0;; + +restore_BRs: + add r4=8,r2 // duplicate r2 in r4 + add r6=2*8,r2;; // duplicate r2 in r4 + + ld8 r3=[r2],3*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; + mov b0=r3 + mov b1=r5 + mov b2=r7;; + + ld8 r3=[r2],3*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; + mov b3=r3 + mov b4=r5 + mov b5=r7;; + + ld8 r3=[r2],2*8 + ld8 r5=[r4],2*8;; + mov b6=r3 + mov b7=r5;; + +restore_CRs: + add r4=8,r2 // duplicate r2 in r4 + add r6=2*8,r2;; // duplicate r2 in r4 + + ld8 r3=[r2],8*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; // 48 byte increments + mov cr0=r3 // cr.dcr + mov cr1=r5 // cr.itm + mov cr2=r7;; // cr.iva + + ld8 r3=[r2],8*8;; // 64 byte increments +// mov cr8=r3 // cr.pta + + +// if PSR.ic=1, reading interruption registers causes an illegal operation fault + mov r3=psr;; + tbit.nz.unc p2,p0=r3,PSR_IC;; // PSI Valid Log bit pos. test +(p2) st8 [r2]=r0,9*8+160 // increment by 160 byte inc. + +begin_rskip_intr_regs: + BRANCH(rSkipIntrRegs, r9, p2, 0x0) + ;; + + add r4=8,r2 // duplicate r2 in r4 + add r6=2*8,r2;; // duplicate r2 in r4 + + ld8 r3=[r2],3*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; + mov cr16=r3 // cr.ipsr + mov cr17=r5 // cr.isr is read only +// mov cr18=r7;; // cr.ida + + ld8 r3=[r2],3*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; + mov cr19=r3 // cr.iip + mov cr20=r5 // cr.idtr + mov cr21=r7;; // cr.iitr + + ld8 r3=[r2],3*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; + mov cr22=r3 // cr.iipa + mov cr23=r5 // cr.ifs + mov cr24=r7 // cr.iim + + ld8 r3=[r2],160;; // 160 byte increment + mov cr25=r3 // cr.iha + +rSkipIntrRegs: + ld8 r3=[r2],168;; // another 168 byte inc. + + ld8 r3=[r2],40;; // 40 byte increment + mov cr66=r3 // cr.lid + + ld8 r3=[r2],8;; +// mov cr71=r3 // cr.ivr is read only + ld8 r3=[r2],24;; // 24 byte increment + mov cr72=r3 // cr.tpr + + ld8 r3=[r2],168;; // 168 byte inc. +// mov cr75=r3 // cr.eoi + + ld8 r3=[r2],16;; // 16 byte inc. +// mov cr96=r3 // cr.irr0 is read only + + ld8 r3=[r2],16;; // 16 byte inc. +// mov cr98=r3 // cr.irr1 is read only + + ld8 r3=[r2],16;; // 16 byte inc +// mov cr100=r3 // cr.irr2 is read only + + ld8 r3=[r2],16;; // 16b inc. +// mov cr102=r3 // cr.irr3 is read only + + ld8 r3=[r2],16;; // 16 byte inc. +// mov cr114=r3 // cr.itv + + ld8 r3=[r2],8;; +// mov cr116=r3 // cr.pmv + ld8 r3=[r2],8;; +// mov cr117=r3 // cr.lrr0 + ld8 r3=[r2],8;; +// mov cr118=r3 // cr.lrr1 + ld8 r3=[r2],8*10;; +// mov cr119=r3 // cr.cmcv + +restore_ARs: + add r4=8,r2 // duplicate r2 in r4 + add r6=2*8,r2;; // duplicate r2 in r4 + + ld8 r3=[r2],3*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; + mov ar0=r3 // ar.kro + mov ar1=r5 // ar.kr1 + mov ar2=r7;; // ar.kr2 + + ld8 r3=[r2],3*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; + mov ar3=r3 // ar.kr3 + mov ar4=r5 // ar.kr4 + mov ar5=r7;; // ar.kr5 + + ld8 r3=[r2],10*8 + ld8 r5=[r4],10*8 + ld8 r7=[r6],10*8;; + mov ar6=r3 // ar.kr6 + mov ar7=r5 // ar.kr7 +// mov ar8=r6 // ar.kr8 + ;; + + ld8 r3=[r2],3*8 + ld8 r5=[r4],3*8 + ld8 r7=[r6],3*8;; +// mov ar16=r3 // ar.rsc +// mov ar17=r5 // ar.bsp is read only + mov ar16=r0 // make sure that RSE is in enforced lazy mode + mov ar18=r7;; // ar.bspstore + + ld8 r9=[r2],8*13;; + mov ar19=r9 // ar.rnat + + mov ar16=r3 // ar.rsc + ld8 r3=[r2],8*4;; + mov ar32=r3 // ar.ccv + + ld8 r3=[r2],8*4;; + mov ar36=r3 // ar.unat + + ld8 r3=[r2],8*4;; + mov ar40=r3 // ar.fpsr + + ld8 r3=[r2],160;; // 160 +// mov ar44=r3 // ar.itc + + ld8 r3=[r2],8;; + mov ar64=r3 // ar.pfs + + ld8 r3=[r2],8;; + mov ar65=r3 // ar.lc + + ld8 r3=[r2];; + mov ar66=r3 // ar.ec + add r2=8*62,r2;; // padding + +restore_RRs: + mov r5=ar.lc + mov ar.lc=0x08-1 + movl r4=0x00 +cStRRr: + ld8 r3=[r2],8;; +// mov rr[r4]=r3 // what are its access previledges? + add r4=1,r4 + br.cloop.sptk.few cStRRr + ;; + mov ar.lc=r5 + ;; +end_os_mca_restore: + BRANCH(ia64_os_mca_done_restore, r2, p0, -0x20) + ;; +//EndStub////////////////////////////////////////////////////////////////////// diff --git a/arch/ia64/kernel/pal.S b/arch/ia64/kernel/pal.S new file mode 100644 index 000000000..1506bacc2 --- /dev/null +++ b/arch/ia64/kernel/pal.S @@ -0,0 +1,119 @@ +/* + * PAL Firmware support + * IA-64 Processor Programmers Reference Vol 2 + * + * Copyright (C) 1999 Don Dugger <don.dugger@intel.com> + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * Copyright (C) 1999 David Mosberger <davidm@hpl.hp.com> + */ + + .text + .psr abi64 + .psr lsb + .lsb + + .data +pal_entry_point: + data8 ia64_pal_default_handler + .text + +/* + * Set the PAL entry point address. This could be written in C code, but we do it here + * to keep it all in one module (besides, it's so trivial that it's + * not a big deal). + * + * in0 Address of the PAL entry point (text address, NOT a function descriptor). + */ + .align 16 + .global ia64_pal_handler_init + .proc ia64_pal_handler_init +ia64_pal_handler_init: + alloc r3=ar.pfs,1,0,0,0 + movl r2=pal_entry_point + ;; + st8 [r2]=in0 + br.ret.sptk.few rp + + .endp ia64_pal_handler_init + +/* + * Default PAL call handler. This needs to be coded in assembly because it uses + * the static calling convention, i.e., the RSE may not be used and calls are + * done via "br.cond" (not "br.call"). + */ + .align 16 + .global ia64_pal_default_handler + .proc ia64_pal_default_handler +ia64_pal_default_handler: + mov r8=-1 + br.cond.sptk.few rp + +/* + * Make a PAL call using the static calling convention. + * + * in0 Pointer to struct ia64_pal_retval + * in1 Index of PAL service + * in2 - in4 Remaning PAL arguments + * + */ + +#ifdef __GCC_MULTIREG_RETVALS__ +# define arg0 in0 +# define arg1 in1 +# define arg2 in2 +# define arg3 in3 +# define arg4 in4 +#else +# define arg0 in1 +# define arg1 in2 +# define arg2 in3 +# define arg3 in4 +# define arg4 in5 +#endif + + .text + .psr abi64 + .psr lsb + .lsb + + .align 16 + .global ia64_pal_call_static + .proc ia64_pal_call_static +ia64_pal_call_static: + alloc loc0 = ar.pfs,6,90,0,0 + movl loc2 = pal_entry_point +1: { + mov r28 = arg0 + mov r29 = arg1 + mov r8 = ip + } + ;; + ld8 loc2 = [loc2] // loc2 <- entry point + mov r30 = arg2 + mov r31 = arg3 + ;; + mov loc3 = psr + mov loc1 = rp + adds r8 = .ret0-1b,r8 + ;; + rsm psr.i + mov b7 = loc2 + mov rp = r8 + ;; + br.cond.sptk.few b7 +.ret0: mov psr.l = loc3 +#ifndef __GCC_MULTIREG_RETVALS__ + st8 [in0] = r8, 8 + ;; + st8 [in0] = r9, 8 + ;; + st8 [in0] = r10, 8 + ;; + st8 [in0] = r11, 8 +#endif + mov ar.pfs = loc0 + mov rp = loc1 + ;; + srlz.d // seralize restoration of psr.l + br.ret.sptk.few b0 + .endp ia64_pal_call_static diff --git a/arch/ia64/kernel/pci-dma.c b/arch/ia64/kernel/pci-dma.c new file mode 100644 index 000000000..f86f45537 --- /dev/null +++ b/arch/ia64/kernel/pci-dma.c @@ -0,0 +1,56 @@ +/* + * Dynamic DMA mapping support. + * + * This implementation is for IA-64 platforms that do not support + * I/O TLBs (aka DMA address translation hardware). + * + * XXX This doesn't do the right thing yet. It appears we would have + * to add additional zones so we can implement the various address + * mask constraints that we might encounter. A zone for memory < 32 + * bits is obviously necessary... + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> + +#include <asm/io.h> + +/* Pure 2^n version of get_order */ +extern __inline__ unsigned long +get_order (unsigned long size) +{ + unsigned long order = ia64_fls(size); + + printk ("get_order: size=%lu, order=%lu\n", size, order); + + if (order > PAGE_SHIFT) + order -= PAGE_SHIFT; + else + order = 0; + return order; +} + +void * +pci_alloc_consistent (struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC; + + if (!hwdev || hwdev->dma_mask != 0xffffffff) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +void +pci_free_consistent (struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long) vaddr, get_order(size)); +} diff --git a/arch/ia64/kernel/pci.c b/arch/ia64/kernel/pci.c new file mode 100644 index 000000000..3bceeed8e --- /dev/null +++ b/arch/ia64/kernel/pci.c @@ -0,0 +1,239 @@ +/* + * pci.c - Low-Level PCI Access in IA64 + * + * Derived from bios32.c of i386 tree. + * + */ + +#include <linux/config.h> + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/smp_lock.h> +#include <linux/spinlock.h> + +#include <asm/machvec.h> +#include <asm/page.h> +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/io.h> + +#include <asm/sal.h> + + +#ifdef CONFIG_SMP +# include <asm/smp.h> +#endif +#include <asm/irq.h> + + +#undef DEBUG +#define DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* + * This interrupt-safe spinlock protects all accesses to PCI + * configuration space. + */ + +spinlock_t pci_lock = SPIN_LOCK_UNLOCKED; + +struct pci_fixup pcibios_fixups[] = { { 0 } }; + +#define PCI_NO_CHECKS 0x400 +#define PCI_NO_PEER_FIXUP 0x800 + +static unsigned int pci_probe = PCI_NO_CHECKS; + +/* Macro to build a PCI configuration address to be passed as a parameter to SAL. */ + +#define PCI_CONFIG_ADDRESS(dev, where) (((u64) dev->bus->number << 16) | ((u64) (dev->devfn & 0xff) << 8) | (where & 0xff)) + +static int +pci_conf_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + s64 status; + u64 lval; + + status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 1, &lval); + *value = lval; + return status; +} + +static int +pci_conf_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + s64 status; + u64 lval; + + status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 2, &lval); + *value = lval; + return status; +} + +static int +pci_conf_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + s64 status; + u64 lval; + + status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 4, &lval); + *value = lval; + return status; +} + +static int +pci_conf_write_config_byte (struct pci_dev *dev, int where, u8 value) +{ + return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 1, value); +} + +static int +pci_conf_write_config_word (struct pci_dev *dev, int where, u16 value) +{ + return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 2, value); +} + +static int +pci_conf_write_config_dword (struct pci_dev *dev, int where, u32 value) +{ + return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 4, value); +} + + +static struct pci_ops pci_conf = { + pci_conf_read_config_byte, + pci_conf_read_config_word, + pci_conf_read_config_dword, + pci_conf_write_config_byte, + pci_conf_write_config_word, + pci_conf_write_config_dword +}; + +/* + * Try to find PCI BIOS. This will always work for IA64. + */ + +static struct pci_ops * __init +pci_find_bios(void) +{ + return &pci_conf; +} + +/* + * Initialization. Uses the SAL interface + */ + +#define PCI_BUSSES_TO_SCAN 2 /* On "real" ;) hardware this will be 255 */ + +void __init +pcibios_init(void) +{ + struct pci_ops *ops = NULL; + int i; + + if ((ops = pci_find_bios()) == NULL) { + printk("PCI: No PCI bus detected\n"); + return; + } + + printk("PCI: Probing PCI hardware\n"); + for (i = 0; i < PCI_BUSSES_TO_SCAN; i++) + pci_scan_bus(i, ops, NULL); + platform_pci_fixup(); + return; +} + +/* + * Called after each bus is probed, but before its children + * are examined. + */ + +void __init +pcibios_fixup_bus(struct pci_bus *b) +{ + return; +} + +int +pci_assign_resource (struct pci_dev *dev, int i) +{ + printk("pci_assign_resource: not implemented!\n"); + return -ENODEV; +} + +void __init +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + unsigned long where, size; + u32 reg; + + where = PCI_BASE_ADDRESS_0 + (resource * 4); + size = res->end - res->start; + pci_read_config_dword(dev, where, ®); + reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + pci_write_config_dword(dev, where, reg); + + /* ??? FIXME -- record old value for shutdown. */ +} + +void __init +pcibios_update_irq(struct pci_dev *dev, int irq) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); + + /* ??? FIXME -- record old value for shutdown. */ +} + +void __init +pcibios_fixup_pbus_ranges (struct pci_bus * bus, struct pbus_set_ranges_data * ranges) +{ + ranges->io_start -= bus->resource[0]->start; + ranges->io_end -= bus->resource[0]->start; + ranges->mem_start -= bus->resource[1]->start; + ranges->mem_end -= bus->resource[1]->start; +} + +int __init +pcibios_enable_device (struct pci_dev *dev) +{ + /* Not needed, since we enable all devices at startup. */ + return 0; +} + +/* + * PCI BIOS setup, always defaults to SAL interface + */ + +char * __init +pcibios_setup(char *str) +{ + pci_probe = PCI_NO_CHECKS; + return NULL; +} + +void +pcibios_align_resource (void *data, struct resource *res, unsigned long size) +{ +} + +#if 0 /*def CONFIG_PROC_FS*/ +/* + * This is an ugly hack to get a (weak) unresolved reference to something that is + * in drivers/pci/proc.c. Without this, the file does not get linked in at all + * (I suspect the reason this isn't needed on Linux/x86 is that most people compile + * with module support, in which case the EXPORT_SYMBOL() stuff will ensure the + * code gets linked in. Sigh... --davidm 99/12/20. + */ +asm ("data8 proc_bus_pci_add"); +#endif diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c new file mode 100644 index 000000000..274b68a73 --- /dev/null +++ b/arch/ia64/kernel/perfmon.c @@ -0,0 +1,227 @@ +#include <linux/config.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> + +#include <asm/errno.h> +#include <asm/irq.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +#ifdef CONFIG_PERFMON + +#define MAX_PERF_COUNTER 4 /* true for Itanium, at least */ +#define WRITE_PMCS_AND_START 0xa0 +#define WRITE_PMCS 0xa1 +#define READ_PMDS 0xa2 +#define STOP_PMCS 0xa3 +#define IA64_COUNTER_MASK 0xffffffffffffff6f +#define PERF_OVFL_VAL 0xffffffff + +struct perfmon_counter { + unsigned long data; + int counter_num; +}; + +unsigned long pmds[MAX_PERF_COUNTER]; +struct task_struct *perf_owner; + +/* + * We set dcr.pp, psr.pp, and the appropriate pmc control values with + * this. Notice that we go about modifying _each_ task's pt_regs to + * set cr_ipsr.pp. This will start counting when "current" does an + * _rfi_. Also, since each task's cr_ipsr.pp, and cr_ipsr is inherited + * across forks, we do _not_ need additional code on context + * switches. On stopping of the counters we dont _need_ to go about + * changing every task's cr_ipsr back to where it wuz, because we can + * just set pmc[0]=1. But we do it anyways becuase we will probably + * add thread specific accounting later. + * + * The obvious problem with this is that on SMP systems, it is a bit + * of work (when someone wants to do it) - it would be easier if we + * just added code to the context-switch path. I think we would need + * to lock the run queue to ensure no context switches, send an IPI to + * each processor, and in that IPI handler, just modify the psr bit of + * only the _current_ thread, since we have modified the psr bit + * correctly in the kernel stack for every process which is not + * running. Might crash on SMP systems without the + * lock_kernel(). Hence the lock.. + */ +asmlinkage unsigned long +sys_perfmonctl (int cmd1, int cmd2, void *ptr) +{ + struct perfmon_counter tmp, *cptr = ptr; + unsigned long pmd, cnum, dcr, flags; + struct task_struct *p; + struct pt_regs *regs; + struct perf_counter; + int i; + + switch (cmd1) { + case WRITE_PMCS: /* Writes to PMC's and clears PMDs */ + case WRITE_PMCS_AND_START: /* Also starts counting */ + + if (!access_ok(VERIFY_READ, cptr, sizeof(struct perf_counter)*cmd2)) + return -EFAULT; + + if (cmd2 >= MAX_PERF_COUNTER) + return -EFAULT; + + if (perf_owner && perf_owner != current) + return -EBUSY; + perf_owner = current; + + for (i = 0; i < cmd2; i++, cptr++) { + copy_from_user(&tmp, cptr, sizeof(tmp)); + /* XXX need to check validity of counter_num and perhaps data!! */ + ia64_set_pmc(tmp.counter_num, tmp.data); + ia64_set_pmd(tmp.counter_num, 0); + pmds[tmp.counter_num - 4] = 0; + } + + if (cmd1 == WRITE_PMCS_AND_START) { + local_irq_save(flags); + dcr = ia64_get_dcr(); + dcr |= IA64_DCR_PP; + ia64_set_dcr(dcr); + local_irq_restore(flags); + + /* + * This is a no can do. It obviously wouldn't + * work on SMP where another process may not + * be blocked at all. + * + * Perhaps we need a global predicate in the + * leave_kernel path to control if pp should + * be on or off? + */ + lock_kernel(); + for_each_task(p) { + regs = (struct pt_regs *) (((char *)p) + IA64_STK_OFFSET) - 1; + ia64_psr(regs)->pp = 1; + } + unlock_kernel(); + ia64_set_pmc(0, 0); + } + break; + + case READ_PMDS: + if (cmd2 >= MAX_PERF_COUNTER) + return -EFAULT; + if (!access_ok(VERIFY_WRITE, cptr, sizeof(struct perf_counter)*cmd2)) + return -EFAULT; + local_irq_save(flags); + /* XXX this looks wrong */ + __asm__ __volatile__("rsm psr.pp\n"); + dcr = ia64_get_dcr(); + dcr &= ~IA64_DCR_PP; + ia64_set_dcr(dcr); + local_irq_restore(flags); + + /* + * We cannot touch pmc[0] to stop counting here, as + * that particular instruction might cause an overflow + * and the mask in pmc[0] might get lost. I'm not very + * sure of the hardware behavior here. So we stop + * counting by psr.pp = 0. And we reset dcr.pp to + * prevent an interrupt from mucking up psr.pp in the + * meanwhile. Perfmon interrupts are pended, hence the + * above code should be ok if one of the above + * instructions cause overflows. Is this ok? When I + * muck with dcr, is the cli/sti needed?? + */ + for (i = 0, cnum = 4; i < MAX_PERF_COUNTER; i++, cnum++, cptr++) { + pmd = pmds[i] + (ia64_get_pmd(cnum) & PERF_OVFL_VAL); + put_user(pmd, &cptr->data); + } + local_irq_save(flags); + /* XXX this looks wrong */ + __asm__ __volatile__("ssm psr.pp"); + dcr = ia64_get_dcr(); + dcr |= IA64_DCR_PP; + ia64_set_dcr(dcr); + local_irq_restore(flags); + break; + + case STOP_PMCS: + ia64_set_pmc(0, 1); + for (i = 0; i < MAX_PERF_COUNTER; ++i) + ia64_set_pmc(i, 0); + + local_irq_save(flags); + dcr = ia64_get_dcr(); + dcr &= ~IA64_DCR_PP; + ia64_set_dcr(dcr); + local_irq_restore(flags); + /* + * This is a no can do. It obviously wouldn't + * work on SMP where another process may not + * be blocked at all. + * + * Perhaps we need a global predicate in the + * leave_kernel path to control if pp should + * be on or off? + */ + lock_kernel(); + for_each_task(p) { + regs = (struct pt_regs *) (((char *)p) + IA64_STK_OFFSET) - 1; + ia64_psr(regs)->pp = 0; + } + unlock_kernel(); + perf_owner = 0; + break; + + default: + break; + } + return 0; +} + +static inline void +update_counters (void) +{ + unsigned long mask, i, cnum, val; + + mask = ia64_get_pmd(0) >> 4; + for (i = 0, cnum = 4; i < MAX_PERF_COUNTER; cnum++, i++, mask >>= 1) { + if (mask & 0x1) + val = PERF_OVFL_VAL; + else + /* since we got an interrupt, might as well clear every pmd. */ + val = ia64_get_pmd(cnum) & PERF_OVFL_VAL; + pmds[i] += val; + ia64_set_pmd(cnum, 0); + } +} + +static void +perfmon_interrupt (int irq, void *arg, struct pt_regs *regs) +{ + update_counters(); + ia64_set_pmc(0, 0); + ia64_srlz_d(); +} + +void +perfmon_init (void) +{ + if (request_irq(PERFMON_IRQ, perfmon_interrupt, 0, "perfmon", NULL)) { + printk("perfmon_init: could not allocate performance monitor vector %u\n", + PERFMON_IRQ); + return; + } + ia64_set_pmv(PERFMON_IRQ); + ia64_srlz_d(); +} + +#else /* !CONFIG_PERFMON */ + +asmlinkage unsigned long +sys_perfmonctl (int cmd1, int cmd2, void *ptr) +{ + return -ENOSYS; +} + +#endif /* !CONFIG_PERFMON */ diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c new file mode 100644 index 000000000..5b6deb5f5 --- /dev/null +++ b/arch/ia64/kernel/process.c @@ -0,0 +1,421 @@ +/* + * Architecture-specific setup. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#define __KERNEL_SYSCALLS__ /* see <asm/unistd.h> */ +#include <linux/config.h> + +#include <linux/pm.h> +#include <linux/elf.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> + +#include <asm/delay.h> +#include <asm/efi.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/sal.h> +#include <asm/uaccess.h> +#include <asm/user.h> + + +void +show_regs (struct pt_regs *regs) +{ + unsigned long ip = regs->cr_iip + ia64_psr(regs)->ri; + + printk("\npsr : %016lx ifs : %016lx ip : [<%016lx>]\n", + regs->cr_ipsr, regs->cr_ifs, ip); + printk("unat: %016lx pfs : %016lx rsc : %016lx\n", + regs->ar_unat, regs->ar_pfs, regs->ar_rsc); + printk("rnat: %016lx bsps: %016lx pr : %016lx\n", + regs->ar_rnat, regs->ar_bspstore, regs->pr); + printk("ldrs: %016lx ccv : %016lx fpsr: %016lx\n", + regs->loadrs, regs->ar_ccv, regs->ar_fpsr); + printk("b0 : %016lx b6 : %016lx b7 : %016lx\n", regs->b0, regs->b6, regs->b7); + printk("f6 : %05lx%016lx f7 : %05lx%016lx\n", + regs->f6.u.bits[1], regs->f6.u.bits[0], + regs->f7.u.bits[1], regs->f7.u.bits[0]); + printk("f8 : %05lx%016lx f9 : %05lx%016lx\n", + regs->f8.u.bits[1], regs->f8.u.bits[0], + regs->f9.u.bits[1], regs->f9.u.bits[0]); + + printk("r1 : %016lx r2 : %016lx r3 : %016lx\n", regs->r1, regs->r2, regs->r3); + printk("r8 : %016lx r9 : %016lx r10 : %016lx\n", regs->r8, regs->r9, regs->r10); + printk("r11 : %016lx r12 : %016lx r13 : %016lx\n", regs->r11, regs->r12, regs->r13); + printk("r14 : %016lx r15 : %016lx r16 : %016lx\n", regs->r14, regs->r15, regs->r16); + printk("r17 : %016lx r18 : %016lx r19 : %016lx\n", regs->r17, regs->r18, regs->r19); + printk("r20 : %016lx r21 : %016lx r22 : %016lx\n", regs->r20, regs->r21, regs->r22); + printk("r23 : %016lx r24 : %016lx r25 : %016lx\n", regs->r23, regs->r24, regs->r25); + printk("r26 : %016lx r27 : %016lx r28 : %016lx\n", regs->r26, regs->r27, regs->r28); + printk("r29 : %016lx r30 : %016lx r31 : %016lx\n", regs->r29, regs->r30, regs->r31); + + /* print the stacked registers if cr.ifs is valid: */ + if (regs->cr_ifs & 0x8000000000000000) { + unsigned long val, sof, *bsp, ndirty; + int i, is_nat = 0; + + sof = regs->cr_ifs & 0x7f; /* size of frame */ + ndirty = (regs->loadrs >> 19); + bsp = ia64_rse_skip_regs((unsigned long *) regs->ar_bspstore, ndirty); + for (i = 0; i < sof; ++i) { + get_user(val, ia64_rse_skip_regs(bsp, i)); + printk("r%-3u:%c%016lx%s", 32 + i, is_nat ? '*' : ' ', val, + ((i == sof - 1) || (i % 3) == 2) ? "\n" : " "); + } + } +} + +void __attribute__((noreturn)) +cpu_idle (void *unused) +{ + /* endless idle loop with no priority at all */ + init_idle(); + current->priority = 0; + current->counter = -100; + +#ifdef CONFIG_SMP + if (!current->need_resched) + min_xtp(); +#endif + + while (1) { + while (!current->need_resched) { + continue; + } +#ifdef CONFIG_SMP + normal_xtp(); +#endif + schedule(); + check_pgt_cache(); + if (pm_idle) + (*pm_idle)(); + } +} + +/* + * Copy the state of an ia-64 thread. + * + * We get here through the following call chain: + * + * <clone syscall> + * sys_clone + * do_fork + * copy_thread + * + * This means that the stack layout is as follows: + * + * +---------------------+ (highest addr) + * | struct pt_regs | + * +---------------------+ + * | struct switch_stack | + * +---------------------+ + * | | + * | memory stack | + * | | <-- sp (lowest addr) + * +---------------------+ + * + * Note: if we get called through kernel_thread() then the memory + * above "(highest addr)" is valid kernel stack memory that needs to + * be copied as well. + * + * Observe that we copy the unat values that are in pt_regs and + * switch_stack. Since the interpretation of unat is dependent upon + * the address to which the registers got spilled, doing this is valid + * only as long as we preserve the alignment of the stack. Since the + * stack is always page aligned, we know this is the case. + * + * XXX Actually, the above isn't true when we create kernel_threads(). + * If we ever needs to create kernel_threads() that preserve the unat + * values we'll need to fix this. Perhaps an easy workaround would be + * to always clear the unat bits in the child thread. + */ +int +copy_thread (int nr, unsigned long clone_flags, unsigned long usp, + struct task_struct *p, struct pt_regs *regs) +{ + unsigned long rbs, child_rbs, rbs_size, stack_offset, stack_top, stack_used; + struct switch_stack *child_stack, *stack; + extern char ia64_ret_from_syscall_clear_r8; + extern char ia64_strace_clear_r8; + struct pt_regs *child_ptregs; + +#ifdef CONFIG_SMP + /* + * For SMP idle threads, fork_by_hand() calls do_fork with + * NULL regs. + */ + if (!regs) + return 0; +#endif + + stack_top = (unsigned long) current + IA64_STK_OFFSET; + stack = ((struct switch_stack *) regs) - 1; + stack_used = stack_top - (unsigned long) stack; + stack_offset = IA64_STK_OFFSET - stack_used; + + child_stack = (struct switch_stack *) ((unsigned long) p + stack_offset); + child_ptregs = (struct pt_regs *) (child_stack + 1); + + /* copy parent's switch_stack & pt_regs to child: */ + memcpy(child_stack, stack, stack_used); + + rbs = (unsigned long) current + IA64_RBS_OFFSET; + child_rbs = (unsigned long) p + IA64_RBS_OFFSET; + rbs_size = stack->ar_bspstore - rbs; + + /* copy the parent's register backing store to the child: */ + memcpy((void *) child_rbs, (void *) rbs, rbs_size); + + child_ptregs->r8 = 0; /* child gets a zero return value */ + if (user_mode(child_ptregs)) + child_ptregs->r12 = usp; /* user stack pointer */ + else { + /* + * Note: we simply preserve the relative position of + * the stack pointer here. There is no need to + * allocate a scratch area here, since that will have + * been taken care of by the caller of sys_clone() + * already. + */ + child_ptregs->r12 = (unsigned long) (child_ptregs + 1); /* kernel sp */ + child_ptregs->r13 = (unsigned long) p; /* set `current' pointer */ + } + if (p->flags & PF_TRACESYS) + child_stack->b0 = (unsigned long) &ia64_strace_clear_r8; + else + child_stack->b0 = (unsigned long) &ia64_ret_from_syscall_clear_r8; + child_stack->ar_bspstore = child_rbs + rbs_size; + + /* copy the thread_struct: */ + p->thread.ksp = (unsigned long) child_stack - 16; + /* + * NOTE: The calling convention considers all floating point + * registers in the high partition (fph) to be scratch. Since + * the only way to get to this point is through a system call, + * we know that the values in fph are all dead. Hence, there + * is no need to inherit the fph state from the parent to the + * child and all we have to do is to make sure that + * IA64_THREAD_FPH_VALID is cleared in the child. + * + * XXX We could push this optimization a bit further by + * clearing IA64_THREAD_FPH_VALID on ANY system call. + * However, it's not clear this is worth doing. Also, it + * would be a slight deviation from the normal Linux system + * call behavior where scratch registers are preserved across + * system calls (unless used by the system call itself). + * + * If we wanted to inherit the fph state from the parent to the + * child, we would have to do something along the lines of: + * + * if (ia64_get_fpu_owner() == current && ia64_psr(regs)->mfh) { + * p->thread.flags |= IA64_THREAD_FPH_VALID; + * ia64_save_fpu(&p->thread.fph); + * } else if (current->thread.flags & IA64_THREAD_FPH_VALID) { + * memcpy(p->thread.fph, current->thread.fph, sizeof(p->thread.fph)); + * } + */ + p->thread.flags = (current->thread.flags & ~IA64_THREAD_FPH_VALID); + return 0; +} + +void +ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst) +{ + struct switch_stack *sw = ((struct switch_stack *) pt) - 1; + unsigned long ar_ec, cfm, ar_bsp, ndirty, *krbs; + + ar_ec = (sw->ar_pfs >> 52) & 0x3f; + + cfm = pt->cr_ifs & ((1UL << 63) - 1); + if ((pt->cr_ifs & (1UL << 63)) == 0) { + /* if cr_ifs isn't valid, we got here through a syscall or a break */ + cfm = sw->ar_pfs & ((1UL << 38) - 1); + } + + krbs = (unsigned long *) current + IA64_RBS_OFFSET/8; + ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 16)); + ar_bsp = (long) ia64_rse_skip_regs((long *) pt->ar_bspstore, ndirty); + + /* r0-r31 + * NaT bits (for r0-r31; bit N == 1 iff rN is a NaT) + * predicate registers (p0-p63) + * b0-b7 + * ip cfm user-mask + * ar.rsc ar.bsp ar.bspstore ar.rnat + * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec + */ + memset(dst, 0, sizeof (dst)); /* don't leak any "random" bits */ + + /* r0 is zero */ dst[ 1] = pt->r1; dst[ 2] = pt->r2; dst[ 3] = pt->r3; + dst[ 4] = sw->r4; dst[ 5] = sw->r5; dst[ 6] = sw->r6; dst[ 7] = sw->r7; + dst[ 8] = pt->r8; dst[ 9] = pt->r9; dst[10] = pt->r10; dst[11] = pt->r11; + dst[12] = pt->r12; dst[13] = pt->r13; dst[14] = pt->r14; dst[15] = pt->r15; + memcpy(dst + 16, &pt->r16, 16*8); /* r16-r31 are contiguous */ + + dst[32] = ia64_get_nat_bits(pt, sw); + dst[33] = pt->pr; + + /* branch regs: */ + dst[34] = pt->b0; dst[35] = sw->b1; dst[36] = sw->b2; dst[37] = sw->b3; + dst[38] = sw->b4; dst[39] = sw->b5; dst[40] = pt->b6; dst[41] = pt->b7; + + dst[42] = pt->cr_iip; dst[43] = pt->cr_ifs; + dst[44] = pt->cr_ipsr; /* XXX perhaps we should filter out some bits here? --davidm */ + + dst[45] = pt->ar_rsc; dst[46] = ar_bsp; dst[47] = pt->ar_bspstore; dst[48] = pt->ar_rnat; + dst[49] = pt->ar_ccv; dst[50] = pt->ar_unat; dst[51] = sw->ar_fpsr; dst[52] = pt->ar_pfs; + dst[53] = sw->ar_lc; dst[54] = (sw->ar_pfs >> 52) & 0x3f; +} + +int +dump_fpu (struct pt_regs *pt, elf_fpregset_t dst) +{ + struct switch_stack *sw = ((struct switch_stack *) pt) - 1; + struct task_struct *fpu_owner = ia64_get_fpu_owner(); + + memset(dst, 0, sizeof (dst)); /* don't leak any "random" bits */ + + /* f0 is 0.0 */ /* f1 is 1.0 */ dst[2] = sw->f2; dst[3] = sw->f3; + dst[4] = sw->f4; dst[5] = sw->f5; dst[6] = pt->f6; dst[7] = pt->f7; + dst[8] = pt->f8; dst[9] = pt->f9; + memcpy(dst + 10, &sw->f10, 22*16); /* f10-f31 are contiguous */ + + if ((fpu_owner == current) || (current->thread.flags & IA64_THREAD_FPH_VALID)) { + if (fpu_owner == current) { + __ia64_save_fpu(current->thread.fph); + } + memcpy(dst + 32, current->thread.fph, 96*16); + } + return 1; /* f0-f31 are always valid so we always return 1 */ +} + +asmlinkage long +sys_execve (char *filename, char **argv, char **envp, struct pt_regs *regs) +{ + int error; + + lock_kernel(); + filename = getname(filename); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, argv, envp, regs); + putname(filename); +out: + unlock_kernel(); + return error; +} + +pid_t +kernel_thread (int (*fn)(void *), void *arg, unsigned long flags) +{ + struct task_struct *parent = current; + int result; + + clone(flags | CLONE_VM, 0); + if (parent != current) { + result = (*fn)(arg); + _exit(result); + } + return 0; /* parent: just return */ +} + +/* + * Flush thread state. This is called when a thread does an execve(). + */ +void +flush_thread (void) +{ + /* drop floating-point and debug-register state if it exists: */ + current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID); + + if (ia64_get_fpu_owner() == current) { + ia64_set_fpu_owner(0); + } +} + +/* + * Clean up state associated with current thread. This is called when + * the thread calls exit(). + */ +void +exit_thread (void) +{ + if (ia64_get_fpu_owner() == current) { + ia64_set_fpu_owner(0); + } +} + +/* + * Free remaining state associated with DEAD_TASK. This is called + * after the parent of DEAD_TASK has collected the exist status of the + * task via wait(). + */ +void +release_thread (struct task_struct *dead_task) +{ + /* nothing to do */ +} + +unsigned long +get_wchan (struct task_struct *p) +{ + struct ia64_frame_info info; + unsigned long ip; + int count = 0; + /* + * These bracket the sleeping functions.. + */ + extern void scheduling_functions_start_here(void); + extern void scheduling_functions_end_here(void); +# define first_sched ((unsigned long) scheduling_functions_start_here) +# define last_sched ((unsigned long) scheduling_functions_end_here) + + /* + * Note: p may not be a blocked task (it could be current or + * another process running on some other CPU. Rather than + * trying to determine if p is really blocked, we just assume + * it's blocked and rely on the unwind routines to fail + * gracefully if the process wasn't really blocked after all. + * --davidm 99/12/15 + */ + ia64_unwind_init_from_blocked_task(&info, p); + do { + if (ia64_unwind_to_previous_frame(&info) < 0) + return 0; + ip = ia64_unwind_get_ip(&info); + if (ip < first_sched || ip >= last_sched) + return ip; + } while (count++ < 16); + return 0; +# undef first_sched +# undef last_sched +} + +void +machine_restart (char *restart_cmd) +{ + (*efi.reset_system)(EFI_RESET_WARM, 0, 0, 0); +} + +void +machine_halt (void) +{ + printk("machine_halt: need PAL or ACPI version here!!\n"); + machine_restart(0); +} + +void +machine_power_off (void) +{ + printk("machine_power_off: unimplemented (need ACPI version here)\n"); + machine_halt (); +} diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c new file mode 100644 index 000000000..18a8e342e --- /dev/null +++ b/arch/ia64/kernel/ptrace.c @@ -0,0 +1,653 @@ +/* + * Kernel support for the ptrace() and syscall tracing interfaces. + * + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * Derived from the x86 and Alpha versions. Most of the code in here + * could actually be factored into a common set of routines. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/smp_lock.h> +#include <linux/user.h> + +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/ptrace_offsets.h> +#include <asm/rse.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +/* + * Collect the NaT bits for r1-r31 from sw->caller_unat and + * sw->ar_unat and return a NaT bitset where bit i is set iff the NaT + * bit of register i is set. + */ +long +ia64_get_nat_bits (struct pt_regs *pt, struct switch_stack *sw) +{ +# define GET_BITS(str, first, last, unat) \ + ({ \ + unsigned long bit = ia64_unat_pos(&str->r##first); \ + unsigned long mask = ((1UL << (last - first + 1)) - 1) << first; \ + (ia64_rotl(unat, first) >> bit) & mask; \ + }) + unsigned long val; + + val = GET_BITS(pt, 1, 3, sw->caller_unat); + val |= GET_BITS(pt, 12, 15, sw->caller_unat); + val |= GET_BITS(pt, 8, 11, sw->caller_unat); + val |= GET_BITS(pt, 16, 31, sw->caller_unat); + val |= GET_BITS(sw, 4, 7, sw->ar_unat); + return val; + +# undef GET_BITS +} + +/* + * Store the NaT bitset NAT in pt->caller_unat and sw->ar_unat. + */ +void +ia64_put_nat_bits (struct pt_regs *pt, struct switch_stack *sw, unsigned long nat) +{ +# define PUT_BITS(str, first, last, nat) \ + ({ \ + unsigned long bit = ia64_unat_pos(&str->r##first); \ + unsigned long mask = ((1UL << (last - first + 1)) - 1) << bit; \ + (ia64_rotr(nat, first) << bit) & mask; \ + }) + sw->caller_unat = PUT_BITS(pt, 1, 3, nat); + sw->caller_unat |= PUT_BITS(pt, 12, 15, nat); + sw->caller_unat |= PUT_BITS(pt, 8, 11, nat); + sw->caller_unat |= PUT_BITS(pt, 16, 31, nat); + sw->ar_unat = PUT_BITS(sw, 4, 7, nat); + +# undef PUT_BITS +} + +#define IA64_MLI_TEMPLATE 0x2 +#define IA64_MOVL_OPCODE 6 + +void +ia64_increment_ip (struct pt_regs *regs) +{ + unsigned long w0, w1, ri = ia64_psr(regs)->ri + 1; + + if (ri > 2) { + ri = 0; + regs->cr_iip += 16; + } else if (ri == 2) { + get_user(w0, (char *) regs->cr_iip + 0); + get_user(w1, (char *) regs->cr_iip + 8); + if (((w0 >> 1) & 0xf) == IA64_MLI_TEMPLATE && (w1 >> 60) == IA64_MOVL_OPCODE) { + /* + * rfi'ing to slot 2 of an MLI bundle causes + * an illegal operation fault. We don't want + * that to happen... Note that we check the + * opcode only. "movl" has a vc bit of 0, but + * since a vc bit of 1 is currently reserved, + * we might just as well treat it like a movl. + */ + ri = 0; + regs->cr_iip += 16; + } + } + ia64_psr(regs)->ri = ri; +} + +void +ia64_decrement_ip (struct pt_regs *regs) +{ + unsigned long w0, w1, ri = ia64_psr(regs)->ri - 1; + + if (ia64_psr(regs)->ri == 0) { + regs->cr_iip -= 16; + ri = 2; + get_user(w0, (char *) regs->cr_iip + 0); + get_user(w1, (char *) regs->cr_iip + 8); + if (((w0 >> 1) & 0xf) == IA64_MLI_TEMPLATE && (w1 >> 60) == IA64_MOVL_OPCODE) { + /* + * rfi'ing to slot 2 of an MLI bundle causes + * an illegal operation fault. We don't want + * that to happen... Note that we check the + * opcode only. "movl" has a vc bit of 0, but + * since a vc bit of 1 is currently reserved, + * we might just as well treat it like a movl. + */ + ri = 1; + } + } + ia64_psr(regs)->ri = ri; +} + +/* + * This routine is used to read an rnat bits that are stored on the + * kernel backing store. Since, in general, the alignment of the user + * and kernel are different, this is not completely trivial. In + * essence, we need to construct the user RNAT based on up to two + * kernel RNAT values and/or the RNAT value saved in the child's + * pt_regs. + * + * user rbs + * + * +--------+ <-- lowest address + * | slot62 | + * +--------+ + * | rnat | 0x....1f8 + * +--------+ + * | slot00 | \ + * +--------+ | + * | slot01 | > child_regs->ar_rnat + * +--------+ | + * | slot02 | / kernel rbs + * +--------+ +--------+ + * <- child_regs->ar_bspstore | slot61 | <-- krbs + * +- - - - + +--------+ + * | slot62 | + * +- - - - + +--------+ + * | rnat | + * +- - - - + +--------+ + * vrnat | slot00 | + * +- - - - + +--------+ + * = = + * +--------+ + * | slot00 | \ + * +--------+ | + * | slot01 | > child_stack->ar_rnat + * +--------+ | + * | slot02 | / + * +--------+ + * <--- child_stack->ar_bspstore + * + * The way to think of this code is as follows: bit 0 in the user rnat + * corresponds to some bit N (0 <= N <= 62) in one of the kernel rnat + * value. The kernel rnat value holding this bit is stored in + * variable rnat0. rnat1 is loaded with the kernel rnat value that + * form the upper bits of the user rnat value. + * + * Boundary cases: + * + * o when reading the rnat "below" the first rnat slot on the kernel + * backing store, rnat0/rnat1 are set to 0 and the low order bits + * are merged in from pt->ar_rnat. + * + * o when reading the rnat "above" the last rnat slot on the kernel + * backing store, rnat0/rnat1 gets its value from sw->ar_rnat. + */ +static unsigned long +get_rnat (struct pt_regs *pt, struct switch_stack *sw, + unsigned long *krbs, unsigned long *urnat_addr) +{ + unsigned long rnat0 = 0, rnat1 = 0, urnat = 0, *slot0_kaddr, kmask = ~0UL; + unsigned long *kbsp, *ubspstore, *rnat0_kaddr, *rnat1_kaddr, shift; + long num_regs; + + kbsp = (unsigned long *) sw->ar_bspstore; + ubspstore = (unsigned long *) pt->ar_bspstore; + /* + * First, figure out which bit number slot 0 in user-land maps + * to in the kernel rnat. Do this by figuring out how many + * register slots we're beyond the user's backingstore and + * then computing the equivalent address in kernel space. + */ + num_regs = ia64_rse_num_regs(ubspstore, urnat_addr + 1); + slot0_kaddr = ia64_rse_skip_regs(krbs, num_regs); + shift = ia64_rse_slot_num(slot0_kaddr); + rnat1_kaddr = ia64_rse_rnat_addr(slot0_kaddr); + rnat0_kaddr = rnat1_kaddr - 64; + + if (ubspstore + 63 > urnat_addr) { + /* some bits need to be merged in from pt->ar_rnat */ + kmask = ~((1UL << ia64_rse_slot_num(ubspstore)) - 1); + urnat = (pt->ar_rnat & ~kmask); + } + if (rnat0_kaddr >= kbsp) { + rnat0 = sw->ar_rnat; + } else if (rnat0_kaddr > krbs) { + rnat0 = *rnat0_kaddr; + } + if (rnat1_kaddr >= kbsp) { + rnat1 = sw->ar_rnat; + } else if (rnat1_kaddr > krbs) { + rnat1 = *rnat1_kaddr; + } + urnat |= ((rnat1 << (63 - shift)) | (rnat0 >> shift)) & kmask; + return urnat; +} + +/* + * The reverse of get_rnat. + */ +static void +put_rnat (struct pt_regs *pt, struct switch_stack *sw, + unsigned long *krbs, unsigned long *urnat_addr, unsigned long urnat) +{ + unsigned long rnat0 = 0, rnat1 = 0, rnat = 0, *slot0_kaddr, kmask = ~0UL, mask; + unsigned long *kbsp, *ubspstore, *rnat0_kaddr, *rnat1_kaddr, shift; + long num_regs; + + kbsp = (unsigned long *) sw->ar_bspstore; + ubspstore = (unsigned long *) pt->ar_bspstore; + /* + * First, figure out which bit number slot 0 in user-land maps + * to in the kernel rnat. Do this by figuring out how many + * register slots we're beyond the user's backingstore and + * then computing the equivalent address in kernel space. + */ + num_regs = (long) ia64_rse_num_regs(ubspstore, urnat_addr + 1); + slot0_kaddr = ia64_rse_skip_regs(krbs, num_regs); + shift = ia64_rse_slot_num(slot0_kaddr); + rnat1_kaddr = ia64_rse_rnat_addr(slot0_kaddr); + rnat0_kaddr = rnat1_kaddr - 64; + + if (ubspstore + 63 > urnat_addr) { + /* some bits need to be place in pt->ar_rnat: */ + kmask = ~((1UL << ia64_rse_slot_num(ubspstore)) - 1); + pt->ar_rnat = (pt->ar_rnat & kmask) | (rnat & ~kmask); + } + /* + * Note: Section 11.1 of the EAS guarantees that bit 63 of an + * rnat slot is ignored. so we don't have to clear it here. + */ + rnat0 = (urnat << shift); + mask = ~0UL << shift; + if (rnat0_kaddr >= kbsp) { + sw->ar_rnat = (sw->ar_rnat & ~mask) | (rnat0 & mask); + } else if (rnat0_kaddr > krbs) { + *rnat0_kaddr = ((*rnat0_kaddr & ~mask) | (rnat0 & mask)); + } + + rnat1 = (urnat >> (63 - shift)); + mask = ~0UL >> (63 - shift); + if (rnat1_kaddr >= kbsp) { + sw->ar_rnat = (sw->ar_rnat & ~mask) | (rnat1 & mask); + } else if (rnat1_kaddr > krbs) { + *rnat1_kaddr = ((*rnat1_kaddr & ~mask) | (rnat1 & mask)); + } +} + +long +ia64_peek (struct pt_regs *regs, struct task_struct *child, unsigned long addr, long *val) +{ + unsigned long *bspstore, *krbs, krbs_num_regs, regnum, *rbs_end, *laddr; + struct switch_stack *child_stack; + struct pt_regs *child_regs; + size_t copied; + long ret; + + laddr = (unsigned long *) addr; + child_regs = ia64_task_regs(child); + child_stack = (struct switch_stack *) child_regs - 1; + bspstore = (unsigned long *) child_regs->ar_bspstore; + krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; + krbs_num_regs = ia64_rse_num_regs(krbs, (unsigned long *) child_stack->ar_bspstore); + rbs_end = ia64_rse_skip_regs(bspstore, krbs_num_regs); + if (laddr >= bspstore && laddr <= ia64_rse_rnat_addr(rbs_end)) { + /* + * Attempt to read the RBS in an area that's actually + * on the kernel RBS => read the corresponding bits in + * the kernel RBS. + */ + if (ia64_rse_is_rnat_slot(laddr)) + ret = get_rnat(child_regs, child_stack, krbs, laddr); + else { + regnum = ia64_rse_num_regs(bspstore, laddr); + laddr = ia64_rse_skip_regs(krbs, regnum); + if (regnum >= krbs_num_regs) { + ret = 0; + } else { + if ((unsigned long) laddr >= (unsigned long) high_memory) { + printk("yikes: trying to access long at %p\n", laddr); + return -EIO; + } + ret = *laddr; + } + } + } else { + copied = access_process_vm(child, addr, &ret, sizeof(ret), 0); + if (copied != sizeof(ret)) + return -EIO; + } + *val = ret; + return 0; +} + +long +ia64_poke (struct pt_regs *regs, struct task_struct *child, unsigned long addr, long val) +{ + unsigned long *bspstore, *krbs, krbs_num_regs, regnum, *rbs_end, *laddr; + struct switch_stack *child_stack; + struct pt_regs *child_regs; + + laddr = (unsigned long *) addr; + child_regs = ia64_task_regs(child); + child_stack = (struct switch_stack *) child_regs - 1; + bspstore = (unsigned long *) child_regs->ar_bspstore; + krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; + krbs_num_regs = ia64_rse_num_regs(krbs, (unsigned long *) child_stack->ar_bspstore); + rbs_end = ia64_rse_skip_regs(bspstore, krbs_num_regs); + if (laddr >= bspstore && laddr <= ia64_rse_rnat_addr(rbs_end)) { + /* + * Attempt to write the RBS in an area that's actually + * on the kernel RBS => write the corresponding bits + * in the kernel RBS. + */ + if (ia64_rse_is_rnat_slot(laddr)) + put_rnat(child_regs, child_stack, krbs, laddr, val); + else { + regnum = ia64_rse_num_regs(bspstore, laddr); + laddr = ia64_rse_skip_regs(krbs, regnum); + if (regnum < krbs_num_regs) { + *laddr = val; + } + } + } else if (access_process_vm(child, addr, &val, sizeof(val), 1) != sizeof(val)) { + return -EIO; + } + return 0; +} + +/* + * Ensure the state in child->thread.fph is up-to-date. + */ +static void +sync_fph (struct task_struct *child) +{ + if (ia64_psr(ia64_task_regs(child))->mfh && ia64_get_fpu_owner() == child) { + ia64_save_fpu(&child->thread.fph[0]); + child->thread.flags |= IA64_THREAD_FPH_VALID; + } + if (!(child->thread.flags & IA64_THREAD_FPH_VALID)) { + memset(&child->thread.fph, 0, sizeof(child->thread.fph)); + child->thread.flags |= IA64_THREAD_FPH_VALID; + } +} + +asmlinkage long +sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, + long arg4, long arg5, long arg6, long arg7, long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + struct switch_stack *child_stack; + struct pt_regs *child_regs; + struct task_struct *child; + unsigned long flags, *base; + long ret, regnum; + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->flags & PF_PTRACED) + goto out; + current->flags |= PF_PTRACED; + ret = 0; + goto out; + } + + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + read_unlock(&tasklist_lock); + if (!child) + goto out; + ret = -EPERM; + if (pid == 1) /* no messing around with init! */ + goto out; + + if (request == PTRACE_ATTACH) { + if (child == current) + goto out; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) + goto out; + /* the same process cannot be attached many times */ + if (child->flags & PF_PTRACED) + goto out; + child->flags |= PF_PTRACED; + if (child->p_pptr != current) { + unsigned long flags; + + write_lock_irqsave(&tasklist_lock, flags); + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); + } + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out; + } + ret = -ESRCH; + if (!(child->flags & PF_PTRACED)) + goto out; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out; + } + if (child->p_pptr != current) + goto out; + + switch (request) { + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: /* read word at location addr */ + ret = ia64_peek(regs, child, addr, &data); + if (ret == 0) { + ret = data; + regs->r8 = 0; /* ensure "ret" is not mistaken as an error code */ + } + goto out; + + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: /* write the word at location addr */ + ret = ia64_poke(regs, child, addr, data); + goto out; + + case PTRACE_PEEKUSR: /* read the word at addr in the USER area */ + ret = -EIO; + if ((addr & 0x7) != 0) + goto out; + + if (addr < PT_CALLER_UNAT) { + /* accessing fph */ + sync_fph(child); + addr += (unsigned long) &child->thread.fph; + ret = *(unsigned long *) addr; + } else if (addr < PT_F9+16) { + /* accessing switch_stack or pt_regs: */ + child_regs = ia64_task_regs(child); + child_stack = (struct switch_stack *) child_regs - 1; + ret = *(unsigned long *) ((long) child_stack + addr - PT_CALLER_UNAT); + + if (addr == PT_AR_BSP) { + /* ret currently contains pt_regs.loadrs */ + unsigned long *rbs, *bspstore, ndirty; + + rbs = (unsigned long *) child + IA64_RBS_OFFSET/8; + bspstore = (unsigned long *) child_regs->ar_bspstore; + ndirty = ia64_rse_num_regs(rbs, rbs + (ret >> 19)); + ret = (unsigned long) ia64_rse_skip_regs(bspstore, ndirty); + } + } else { + if (addr >= PT_IBR) { + regnum = (addr - PT_IBR) >> 3; + base = &child->thread.ibr[0]; + } else { + regnum = (addr - PT_DBR) >> 3; + base = &child->thread.dbr[0]; + } + if (regnum >= 8) + goto out; + data = base[regnum]; + } + regs->r8 = 0; /* ensure "ret" is not mistaken as an error code */ + goto out; + + case PTRACE_POKEUSR: /* write the word at addr in the USER area */ + ret = -EIO; + if ((addr & 0x7) != 0) + goto out; + + if (addr < PT_CALLER_UNAT) { + /* accessing fph */ + sync_fph(child); + addr += (unsigned long) &child->thread.fph; + *(unsigned long *) addr = data; + if (ret < 0) + goto out; + } else if (addr < PT_F9+16) { + /* accessing switch_stack or pt_regs */ + child_regs = ia64_task_regs(child); + child_stack = (struct switch_stack *) child_regs - 1; + + if (addr == PT_AR_BSP) { + /* compute the loadrs value based on bsp and bspstore: */ + unsigned long *rbs, *bspstore, ndirty, *kbsp; + + bspstore = (unsigned long *) child_regs->ar_bspstore; + ndirty = ia64_rse_num_regs(bspstore, (unsigned long *) data); + rbs = (unsigned long *) child + IA64_RBS_OFFSET/8; + kbsp = ia64_rse_skip_regs(rbs, ndirty); + data = (kbsp - rbs) << 19; + } + *(unsigned long *) ((long) child_stack + addr - PT_CALLER_UNAT) = data; + } else { + if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { + child->thread.flags |= IA64_THREAD_DBG_VALID; + memset(current->thread.dbr, 0, sizeof current->thread.dbr); + memset(current->thread.ibr, 0, sizeof current->thread.ibr); + } + + if (addr >= PT_IBR) { + regnum = (addr - PT_IBR) >> 3; + base = &child->thread.ibr[0]; + } else { + regnum = (addr - PT_DBR) >> 3; + base = &child->thread.dbr[0]; + } + if (regnum >= 8) + goto out; + if (regnum & 1) { + /* force breakpoint to be effective a most for user-level: */ + data &= ~(0x7UL << 56); + } + base[regnum] = data; + } + ret = 0; + goto out; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: /* restart after signal. */ + ret = -EIO; + if (data > _NSIG) + goto out; + if (request == PTRACE_SYSCALL) + child->flags |= PF_TRACESYS; + else + child->flags &= ~PF_TRACESYS; + child->exit_code = data; + + /* make sure the single step/take-branch tra bits are not set: */ + ia64_psr(ia64_task_regs(child))->ss = 0; + ia64_psr(ia64_task_regs(child))->tb = 0; + + wake_up_process(child); + ret = 0; + goto out; + + case PTRACE_KILL: + /* + * Make the child exit. Best I can do is send it a + * sigkill. Perhaps it should be put in the status + * that it wants to exit. + */ + if (child->state == TASK_ZOMBIE) /* already dead */ + goto out; + child->exit_code = SIGKILL; + + /* make sure the single step/take-branch tra bits are not set: */ + ia64_psr(ia64_task_regs(child))->ss = 0; + ia64_psr(ia64_task_regs(child))->tb = 0; + + wake_up_process(child); + ret = 0; + goto out; + + case PTRACE_SINGLESTEP: /* let child execute for one instruction */ + case PTRACE_SINGLEBLOCK: + ret = -EIO; + if (data > _NSIG) + goto out; + + child->flags &= ~PF_TRACESYS; + if (request == PTRACE_SINGLESTEP) { + ia64_psr(ia64_task_regs(child))->ss = 1; + } else { + ia64_psr(ia64_task_regs(child))->tb = 1; + } + child->exit_code = data; + + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + goto out; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = -EIO; + if (data > _NSIG) + goto out; + + child->flags &= ~(PF_PTRACED|PF_TRACESYS); + child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); + + /* make sure the single step/take-branch tra bits are not set: */ + ia64_psr(ia64_task_regs(child))->ss = 0; + ia64_psr(ia64_task_regs(child))->tb = 0; + + wake_up_process(child); + ret = 0; + goto out; + + default: + ret = -EIO; + goto out; + } + out: + unlock_kernel(); + return ret; +} + +void +syscall_trace (void) +{ + if ((current->flags & (PF_PTRACED|PF_TRACESYS)) != (PF_PTRACED|PF_TRACESYS)) + return; + current->exit_code = SIGTRAP; + set_current_state(TASK_STOPPED); + notify_parent(current, SIGCHLD); + schedule(); + /* + * This isn't the same as continuing with a signal, but it + * will do for normal use. strace only continues with a + * signal if the stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/ia64/kernel/sal.c b/arch/ia64/kernel/sal.c new file mode 100644 index 000000000..8743f6588 --- /dev/null +++ b/arch/ia64/kernel/sal.c @@ -0,0 +1,157 @@ +/* + * System Abstraction Layer (SAL) interface routines. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + */ +#include <linux/config.h> + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/string.h> + +#include <asm/page.h> +#include <asm/sal.h> +#include <asm/pal.h> + +#define SAL_DEBUG + +spinlock_t sal_lock = SPIN_LOCK_UNLOCKED; + +static struct { + void *addr; /* function entry point */ + void *gpval; /* gp value to use */ +} pdesc; + +static long +default_handler (void) +{ + return -1; +} + +ia64_sal_handler ia64_sal = (ia64_sal_handler) default_handler; + +const char * +ia64_sal_strerror (long status) +{ + const char *str; + switch (status) { + case 0: str = "Call completed without error"; break; + case 1: str = "Effect a warm boot of the system to complete " + "the update"; break; + case -1: str = "Not implemented"; break; + case -2: str = "Invalid argument"; break; + case -3: str = "Call completed with error"; break; + case -4: str = "Virtual address not registered"; break; + case -5: str = "No information available"; break; + case -6: str = "Insufficient space to add the entry"; break; + case -7: str = "Invalid entry_addr value"; break; + case -8: str = "Invalid interrupt vector"; break; + case -9: str = "Requested memory not available"; break; + case -10: str = "Unable to write to the NVM device"; break; + case -11: str = "Invalid partition type specified"; break; + case -12: str = "Invalid NVM_Object id specified"; break; + case -13: str = "NVM_Object already has the maximum number " + "of partitions"; break; + case -14: str = "Insufficient space in partition for the " + "requested write sub-function"; break; + case -15: str = "Insufficient data buffer space for the " + "requested read record sub-function"; break; + case -16: str = "Scratch buffer required for the write/delete " + "sub-function"; break; + case -17: str = "Insufficient space in the NVM_Object for the " + "requested create sub-function"; break; + case -18: str = "Invalid value specified in the partition_rec " + "argument"; break; + case -19: str = "Record oriented I/O not supported for this " + "partition"; break; + case -20: str = "Bad format of record to be written or " + "required keyword variable not " + "specified"; break; + default: str = "Unknown SAL status code"; break; + } + return str; +} + +static void __init +ia64_sal_handler_init (void *entry_point, void *gpval) +{ + /* fill in the SAL procedure descriptor and point ia64_sal to it: */ + pdesc.addr = entry_point; + pdesc.gpval = gpval; + ia64_sal = (ia64_sal_handler) &pdesc; +} + + +void __init +ia64_sal_init (struct ia64_sal_systab *systab) +{ + unsigned long min, max; + char *p; + struct ia64_sal_desc_entry_point *ep; + int i; + + if (!systab) { + printk("Hmm, no SAL System Table.\n"); + return; + } + + if (strncmp(systab->signature, "SST_", 4) != 0) + printk("bad signature in system table!"); + + printk("SAL v%u.%02u: ia32bios=%s, oem=%.32s, product=%.32s\n", + systab->sal_rev_major, systab->sal_rev_minor, + systab->ia32_bios_present ? "present" : "absent", + systab->oem_id, systab->product_id); + + min = ~0UL; + max = 0; + + p = (char *) (systab + 1); + for (i = 0; i < systab->entry_count; i++) { + /* + * The first byte of each entry type contains the type desciptor. + */ + switch (*p) { + case SAL_DESC_ENTRY_POINT: + ep = (struct ia64_sal_desc_entry_point *) p; +#ifdef SAL_DEBUG + printk("sal[%d] - entry: pal_proc=0x%lx, sal_proc=0x%lx\n", + i, ep->pal_proc, ep->sal_proc); +#endif + ia64_pal_handler_init(__va(ep->pal_proc)); + ia64_sal_handler_init(__va(ep->sal_proc), __va(ep->gp)); + break; + + case SAL_DESC_AP_WAKEUP: +#ifdef CONFIG_SMP + { + struct ia64_sal_desc_ap_wakeup *ap = (void *) p; +# ifdef SAL_DEBUG + printk("sal[%d] - wakeup type %x, 0x%lx\n", + i, ap->mechanism, ap->vector); +# endif + switch (ap->mechanism) { + case IA64_SAL_AP_EXTERNAL_INT: + ap_wakeup_vector = ap->vector; +# ifdef SAL_DEBUG + printk("SAL: AP wakeup using external interrupt; " + "vector 0x%lx\n", ap_wakeup_vector); +# endif + break; + + default: + printk("SAL: AP wakeup mechanism unsupported!\n"); + break; + } + break; + } +#endif + } + p += SAL_DESC_SIZE(*p); + } +} diff --git a/arch/ia64/kernel/sal_stub.S b/arch/ia64/kernel/sal_stub.S new file mode 100644 index 000000000..7ab16bbcd --- /dev/null +++ b/arch/ia64/kernel/sal_stub.S @@ -0,0 +1,116 @@ +/* + * gcc currently does not conform to the ia-64 calling convention as far + * as returning function values are concerned. Instead of returning + * values up to 32 bytes in size in r8-r11, gcc returns any value + * bigger than a doubleword via a structure that's allocated by the + * caller and whose address is passed into the function. Since + * SAL_PROC returns values according to the calling convention, this + * stub takes care of copying r8-r11 to the place where gcc expects + * them. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#ifndef __GCC_MULTIREG_RETVALS__ + .text + .psr abi64 + .psr lsb + .lsb + + .align 16 + .global ia64_sal_stub +ia64_sal_stub: + /* + * Sheesh, the Cygnus backend passes the pointer to a return value structure in + * in0 whereas the HP backend passes it in r8. Don't you hate those little + * differences... + */ +#ifdef GCC_RETVAL_POINTER_IN_R8 + adds r2=-24,sp + adds sp=-48,sp + mov r14=rp + ;; + st8 [r2]=r8,8 // save pointer to return value + addl r3=@ltoff(ia64_sal),gp + ;; + ld8 r3=[r3] + st8 [r2]=gp,8 // save global pointer + ;; + ld8 r3=[r3] // fetch the value of ia64_sal + st8 [r2]=r14 // save return pointer + ;; + ld8 r2=[r3],8 // load function's entry point + ;; + ld8 gp=[r3] // load function's global pointer + ;; + mov b6=r2 + br.call.sptk.few rp=b6 +.ret0: adds r2=24,sp + ;; + ld8 r3=[r2],8 // restore pointer to return value + ;; + ld8 gp=[r2],8 // restore global pointer + st8 [r3]=r8,8 + ;; + ld8 r14=[r2] // restore return pointer + st8 [r3]=r9,8 + ;; + mov rp=r14 + st8 [r3]=r10,8 + ;; + st8 [r3]=r11,8 + adds sp=48,sp + br.sptk.few rp +#else + /* + * On input: + * in0 = pointer to return value structure + * in1 = index of SAL function to call + * in2..inN = remaining args to SAL call + */ + /* + * We allocate one input and eight output register such that the br.call instruction + * will rename in1-in7 to in0-in6---exactly what we want because SAL doesn't want to + * see the pointer to the return value structure. + */ + alloc r15=ar.pfs,1,0,8,0 + + adds r2=-24,sp + adds sp=-48,sp + mov r14=rp + ;; + st8 [r2]=r15,8 // save ar.pfs + addl r3=@ltoff(ia64_sal),gp + ;; + ld8 r3=[r3] // get address of ia64_sal + st8 [r2]=gp,8 // save global pointer + ;; + ld8 r3=[r3] // get value of ia64_sal + st8 [r2]=r14,8 // save return address (rp) + ;; + ld8 r2=[r3],8 // load function's entry point + ;; + ld8 gp=[r3] // load function's global pointer + mov b6=r2 + br.call.sptk.few rp=b6 // make SAL call +.ret0: adds r2=24,sp + ;; + ld8 r15=[r2],8 // restore ar.pfs + ;; + ld8 gp=[r2],8 // restore global pointer + st8 [in0]=r8,8 // store 1. dword of return value + ;; + ld8 r14=[r2] // restore return address (rp) + st8 [in0]=r9,8 // store 2. dword of return value + ;; + mov rp=r14 + st8 [in0]=r10,8 // store 3. dword of return value + ;; + st8 [in0]=r11,8 + adds sp=48,sp // pop stack frame + mov ar.pfs=r15 + br.ret.sptk.few rp +#endif + + .endp ia64_sal_stub +#endif /* __GCC_MULTIREG_RETVALS__ */ diff --git a/arch/ia64/kernel/semaphore.c b/arch/ia64/kernel/semaphore.c new file mode 100644 index 000000000..84581af2e --- /dev/null +++ b/arch/ia64/kernel/semaphore.c @@ -0,0 +1,336 @@ +/* + * IA-64 semaphore implementation (derived from x86 version). + * + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +/* + * Semaphores are implemented using a two-way counter: The "count" + * variable is decremented for each process that tries to aquire the + * semaphore, while the "sleepers" variable is a count of such + * aquires. + * + * Notably, the inline "up()" and "down()" functions can efficiently + * test if they need to do any extra work (up needs to do something + * only if count was negative before the increment operation. + * + * "sleepers" and the contention routine ordering is protected by the + * semaphore spinlock. + * + * Note that these functions are only called when there is contention + * on the lock, and as such all this is the "non-critical" part of the + * whole semaphore business. The critical part is the inline stuff in + * <asm/semaphore.h> where we want to avoid any extra jumps and calls. + */ +#include <linux/sched.h> + +#include <asm/semaphore.h> + +/* + * Logic: + * - Only on a boundary condition do we need to care. When we go + * from a negative count to a non-negative, we wake people up. + * - When we go from a non-negative count to a negative do we + * (a) synchronize with the "sleepers" count and (b) make sure + * that we're on the wakeup list before we synchronize so that + * we cannot lose wakeup events. + */ + +void +__up (struct semaphore *sem) +{ + wake_up(&sem->wait); +} + +static spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED; + +void +__down (struct semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + wake_up(&sem->wait); +} + +int +__down_interruptible (struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_INTERRUPTIBLE|TASK_EXCLUSIVE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers ++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * With signals pending, this turns into + * the trylock failure case - we won't be + * sleeping, and we* can't get the lock as + * it has contention. Just correct the count + * and exit. + */ + if (signal_pending(current)) { + retval = -EINTR; + sem->sleepers = 0; + atomic_add(sleepers, &sem->count); + break; + } + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. The + * "-1" is because we're still hoping to get + * the lock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_INTERRUPTIBLE|TASK_EXCLUSIVE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} + +/* + * Trylock failed - make sure we correct for having decremented the + * count. + */ +int +__down_trylock (struct semaphore *sem) +{ + int sleepers; + + spin_lock_irq(&semaphore_lock); + sleepers = sem->sleepers + 1; + sem->sleepers = 0; + + /* + * Add "everybody else" and us into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers, &sem->count)) + wake_up(&sem->wait); + + spin_unlock_irq(&semaphore_lock); + return 1; +} + +/* + * Helper routines for rw semaphores. These could be optimized some + * more, but since they're off the critical path, I prefer clarity for + * now... + */ + +/* + * This gets called if we failed to acquire the lock, but we're biased + * to acquire the lock by virtue of causing the count to change from 0 + * to -1. Being biased, we sleep and attempt to grab the lock until + * we succeed. When this function returns, we own the lock. + */ +static inline void +down_read_failed_biased (struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(&sem->wait, &wait); /* put ourselves at the head of the list */ + + for (;;) { + if (sem->read_bias_granted && xchg(&sem->read_bias_granted, 0)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (!sem->read_bias_granted) + schedule(); + } + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + +/* + * This gets called if we failed to aquire the lock and we are not + * biased to acquire the lock. We undo the decrement that was + * done earlier, go to sleep, and then attempt to re-acquire the + * lock afterwards. + */ +static inline void +down_read_failed (struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + /* + * Undo the decrement we did in down_read() and check if we + * need to wake up someone. + */ + __up_read(sem); + + add_wait_queue(&sem->wait, &wait); + while (sem->count < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (sem->count >= 0) + break; + schedule(); + } + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + +/* + * Wait for the lock to become unbiased. Readers are non-exclusive. + */ +void +__down_read_failed (struct rw_semaphore *sem, long count) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + while (1) { + if (count == -1) { + down_read_failed_biased(sem); + return; + } + /* unbiased */ + down_read_failed(sem); + + count = ia64_fetch_and_add(-1, &sem->count); + if (count >= 0) + return; + } +} + +static inline void +down_write_failed_biased (struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + /* put ourselves at the end of the list */ + add_wait_queue_exclusive(&sem->write_bias_wait, &wait); + + for (;;) { + if (sem->write_bias_granted && xchg(&sem->write_bias_granted, 0)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + if (!sem->write_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->write_bias_wait, &wait); + tsk->state = TASK_RUNNING; + + /* + * If the lock is currently unbiased, awaken the sleepers + * FIXME: this wakes up the readers early in a bit of a + * stampede -> bad! + */ + if (sem->count >= 0) + wake_up(&sem->wait); +} + + +static inline void +down_write_failed (struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + __up_write(sem); /* this takes care of granting the lock */ + + add_wait_queue_exclusive(&sem->wait, &wait); + + while (sem->count < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + if (sem->count >= 0) + break; /* we must attempt to aquire or bias the lock */ + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + + +/* + * Wait for the lock to become unbiased. Since we're a writer, we'll + * make ourselves exclusive. + */ +void +__down_write_failed (struct rw_semaphore *sem, long count) +{ + long old_count; + + while (1) { + if (count == -RW_LOCK_BIAS) { + down_write_failed_biased(sem); + return; + } + down_write_failed(sem); + + do { + old_count = sem->count; + count = old_count - RW_LOCK_BIAS; + } while (cmpxchg(&sem->count, old_count, count) != old_count); + + if (count == 0) + return; + } +} + +void +__rwsem_wake (struct rw_semaphore *sem, long count) +{ + wait_queue_head_t *wq; + + if (count == 0) { + /* wake a writer */ + if (xchg(&sem->write_bias_granted, 1)) + BUG(); + wq = &sem->write_bias_wait; + } else { + /* wake reader(s) */ + if (xchg(&sem->read_bias_granted, 1)) + BUG(); + wq = &sem->wait; + } + wake_up(wq); /* wake up everyone on the wait queue */ +} diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c new file mode 100644 index 000000000..f3283d535 --- /dev/null +++ b/arch/ia64/kernel/setup.c @@ -0,0 +1,326 @@ +/* + * Architecture-specific setup. + * + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1998, 1999 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 2000, Rohit Seth <rohit.seth@intel.com> + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * + * 02/04/00 D.Mosberger some more get_cpuinfo fixes... + * 02/01/00 R.Seth fixed get_cpuinfo for SMP + * 01/07/99 S.Eranian added the support for command line argument + * 06/24/99 W.Drummond added boot_cpu_data. + */ +#include <linux/config.h> +#include <linux/init.h> + +#include <linux/bootmem.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/reboot.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/threads.h> +#include <linux/console.h> + +#include <asm/acpi-ext.h> +#include <asm/page.h> +#include <asm/machvec.h> +#include <asm/processor.h> +#include <asm/sal.h> +#include <asm/system.h> +#include <asm/efi.h> + +extern char _end; + +/* cpu_data[bootstrap_processor] is data for the bootstrap processor: */ +struct cpuinfo_ia64 cpu_data[NR_CPUS]; + +unsigned long ia64_cycles_per_usec; +struct ia64_boot_param ia64_boot_param; +struct screen_info screen_info; +unsigned long cpu_initialized = 0; +/* This tells _start which CPU is booting. */ +int cpu_now_booting = 0; + +#define COMMAND_LINE_SIZE 512 + +char saved_command_line[COMMAND_LINE_SIZE]; /* used in proc filesystem */ + +static int +find_max_pfn (unsigned long start, unsigned long end, void *arg) +{ + unsigned long *max_pfn = arg, pfn; + + pfn = (PAGE_ALIGN(end - 1) - PAGE_OFFSET) >> PAGE_SHIFT; + if (pfn > *max_pfn) + *max_pfn = pfn; + return 0; +} + +static int +free_available_memory (unsigned long start, unsigned long end, void *arg) +{ +# define KERNEL_END ((unsigned long) &_end) +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +# define MAX(a,b) ((a) > (b) ? (a) : (b)) + unsigned long range_start, range_end; + + range_start = MIN(start, KERNEL_START); + range_end = MIN(end, KERNEL_START); + + /* + * XXX This should not be necessary, but the bootmem allocator + * is broken and fails to work correctly when the starting + * address is not properly aligned. + */ + range_start = PAGE_ALIGN(range_start); + + if (range_start < range_end) + free_bootmem(__pa(range_start), range_end - range_start); + + range_start = MAX(start, KERNEL_END); + range_end = MAX(end, KERNEL_END); + + /* + * XXX This should not be necessary, but the bootmem allocator + * is broken and fails to work correctly when the starting + * address is not properly aligned. + */ + range_start = PAGE_ALIGN(range_start); + + if (range_start < range_end) + free_bootmem(__pa(range_start), range_end - range_start); + + return 0; +} + +void __init +setup_arch (char **cmdline_p) +{ + unsigned long max_pfn, bootmap_start, bootmap_size; + + /* + * The secondary bootstrap loader passes us the boot + * parameters at the beginning of the ZERO_PAGE, so let's + * stash away those values before ZERO_PAGE gets cleared out. + */ + memcpy(&ia64_boot_param, (void *) ZERO_PAGE_ADDR, sizeof(ia64_boot_param)); + + efi_init(); + + max_pfn = 0; + efi_memmap_walk(find_max_pfn, &max_pfn); + + /* + * This is wrong, wrong, wrong. Darn it, you'd think if they + * change APIs, they'd do things for the better. Grumble... + */ + bootmap_start = PAGE_ALIGN(__pa(&_end)); + bootmap_size = init_bootmem(bootmap_start >> PAGE_SHIFT, max_pfn); + + efi_memmap_walk(free_available_memory, 0); + + reserve_bootmem(bootmap_start, bootmap_size); +#if 0 + /* XXX fix me */ + init_mm.start_code = (unsigned long) &_stext; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + + code_resource.start = virt_to_bus(&_text); + code_resource.end = virt_to_bus(&_etext) - 1; + data_resource.start = virt_to_bus(&_etext); + data_resource.end = virt_to_bus(&_edata) - 1; +#endif + + /* process SAL system table: */ + ia64_sal_init(efi.sal_systab); + + *cmdline_p = __va(ia64_boot_param.command_line); + strncpy(saved_command_line, *cmdline_p, sizeof(saved_command_line)); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; /* for safety */ + + printk("args to kernel: %s\n", *cmdline_p); + +#ifndef CONFIG_SMP + cpu_init(); + identify_cpu(&cpu_data[0]); +#endif + + if (efi.acpi) { + /* Parse the ACPI tables */ + acpi_parse(efi.acpi); + } + +#ifdef CONFIG_IA64_GENERIC + machvec_init(acpi_get_sysname()); +#endif + +#ifdef CONFIG_VT +# if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +# elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +# endif +#endif + platform_setup(cmdline_p); +} + +/* + * Display cpu info for all cpu's. + */ +int +get_cpuinfo (char *buffer) +{ + char family[32], model[32], features[128], *cp, *p = buffer; + struct cpuinfo_ia64 *c; + unsigned long mask; + + for (c = cpu_data; c < cpu_data + NR_CPUS; ++c) { + if (!(cpu_initialized & (1UL << (c - cpu_data)))) + continue; + + mask = c->features; + + if (c->family == 7) + memcpy(family, "IA-64", 6); + else + sprintf(family, "%u", c->family); + + switch (c->model) { + case 0: strcpy(model, "Itanium"); break; + default: sprintf(model, "%u", c->model); break; + } + + /* build the feature string: */ + memcpy(features, " standard", 10); + cp = features; + if (mask & 1) { + strcpy(cp, " branchlong"); + cp = strchr(cp, '\0'); + mask &= ~1UL; + } + if (mask) + sprintf(cp, " 0x%lx", mask); + + p += sprintf(buffer, + "CPU# %lu\n" + "\tvendor : %s\n" + "\tfamily : %s\n" + "\tmodel : %s\n" + "\trevision : %u\n" + "\tarchrev : %u\n" + "\tfeatures :%s\n" /* don't change this---it _is_ right! */ + "\tcpu number : %lu\n" + "\tcpu regs : %u\n" + "\tcpu MHz : %lu.%06lu\n" + "\titc MHz : %lu.%06lu\n" + "\tBogoMIPS : %lu.%02lu\n\n", + c - cpu_data, c->vendor, family, model, c->revision, c->archrev, + features, + c->ppn, c->number, c->proc_freq / 1000000, c->proc_freq % 1000000, + c->itc_freq / 1000000, c->itc_freq % 1000000, + loops_per_sec() / 500000, (loops_per_sec() / 5000) % 100); + } + return p - buffer; +} + +void +identify_cpu (struct cpuinfo_ia64 *c) +{ + union { + unsigned long bits[5]; + struct { + /* id 0 & 1: */ + char vendor[16]; + + /* id 2 */ + u64 ppn; /* processor serial number */ + + /* id 3: */ + unsigned number : 8; + unsigned revision : 8; + unsigned model : 8; + unsigned family : 8; + unsigned archrev : 8; + unsigned reserved : 24; + + /* id 4: */ + u64 features; + } field; + } cpuid; + int i; + + for (i = 0; i < 5; ++i) { + cpuid.bits[i] = ia64_get_cpuid(i); + } + +#ifdef CONFIG_SMP + /* + * XXX Instead of copying the ITC info from the bootstrap + * processor, ia64_init_itm() should be done per CPU. That + * should get you the right info. --davidm 1/24/00 + */ + if (c != &cpu_data[bootstrap_processor]) { + memset(c, 0, sizeof(struct cpuinfo_ia64)); + c->proc_freq = cpu_data[bootstrap_processor].proc_freq; + c->itc_freq = cpu_data[bootstrap_processor].itc_freq; + c->cyc_per_usec = cpu_data[bootstrap_processor].cyc_per_usec; + c->usec_per_cyc = cpu_data[bootstrap_processor].usec_per_cyc; + } +#else + memset(c, 0, sizeof(struct cpuinfo_ia64)); +#endif + + memcpy(c->vendor, cpuid.field.vendor, 16); +#ifdef CONFIG_IA64_SOFTSDV_HACKS + /* BUG: SoftSDV doesn't support the cpuid registers. */ + if (c->vendor[0] == '\0') + memcpy(c->vendor, "Intel", 6); +#endif + c->ppn = cpuid.field.ppn; + c->number = cpuid.field.number; + c->revision = cpuid.field.revision; + c->model = cpuid.field.model; + c->family = cpuid.field.family; + c->archrev = cpuid.field.archrev; + c->features = cpuid.field.features; +#ifdef CONFIG_SMP + c->loops_per_sec = loops_per_sec; +#endif +} + +/* + * cpu_init() initializes state that is per-CPU. This function acts + * as a 'CPU state barrier', nothing should get across. + */ +void +cpu_init (void) +{ + int nr = smp_processor_id(); + + /* Clear the stack memory reserved for pt_regs: */ + memset(ia64_task_regs(current), 0, sizeof(struct pt_regs)); + + /* + * Initialize default control register to defer speculative + * faults. On a speculative load, we want to defer access + * right, key miss, and key permission faults. We currently + * do NOT defer TLB misses, page-not-present, access bit, or + * debug faults but kernel code should not rely on any + * particular setting of these bits. + */ + ia64_set_dcr(IA64_DCR_DR | IA64_DCR_DK | IA64_DCR_DX | IA64_DCR_PP); + ia64_set_fpu_owner(0); /* initialize ar.k5 */ + + if (test_and_set_bit(nr, &cpu_initialized)) { + printk("CPU#%d already initialized!\n", nr); + machine_halt(); + } + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; +} diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c new file mode 100644 index 000000000..19be1f840 --- /dev/null +++ b/arch/ia64/kernel/signal.c @@ -0,0 +1,537 @@ +/* + * Architecture-specific signal handling support. + * + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * Derived from i386 and Alpha versions. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/ptrace.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/wait.h> + +#include <asm/ia32.h> +#include <asm/uaccess.h> +#include <asm/rse.h> +#include <asm/sigcontext.h> + +#define DEBUG_SIG 0 +#define STACK_ALIGN 16 /* minimal alignment for stack pointer */ +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +#if _NSIG_WORDS > 1 +# define PUT_SIGSET(k,u) __copy_to_user((u)->sig, (k)->sig, sizeof(sigset_t)) +# define GET_SIGSET(k,u) __copy_from_user((k)->sig, (u)->sig, sizeof(sigset_t)) +#else +# define PUT_SIGSET(k,u) __put_user((k)->sig[0], &(u)->sig[0]) +# define GET_SIGSET(k,u) __get_user((k)->sig[0], &(u)->sig[0]) +#endif + +struct sigframe { + struct siginfo info; + struct sigcontext sc; +}; + +extern long sys_wait4 (int, int *, int, struct rusage *); +extern long ia64_do_signal (sigset_t *, struct pt_regs *, long); /* forward decl */ + +long +ia64_rt_sigsuspend (sigset_t *uset, size_t sigsetsize, struct pt_regs *pt) +{ + sigset_t oldset, set; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (GET_SIGSET(&set, uset)) + return -EFAULT; + + sigdelsetmask(&set, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + { + oldset = current->blocked; + current->blocked = set; + recalc_sigpending(current); + } + spin_unlock_irq(¤t->sigmask_lock); + + /* + * The return below usually returns to the signal handler. We need to + * pre-set the correct error code here to ensure that the right values + * get saved in sigcontext by ia64_do_signal. + */ + pt->r8 = EINTR; + pt->r10 = -1; + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (ia64_do_signal(&oldset, pt, 1)) + return -EINTR; + } +} + +asmlinkage long +sys_sigaltstack (const stack_t *uss, stack_t *uoss, long arg2, long arg3, long arg4, + long arg5, long arg6, long arg7, long stack) +{ + struct pt_regs *pt = (struct pt_regs *) &stack; + + return do_sigaltstack(uss, uoss, pt->r12); +} + +static long +restore_sigcontext (struct sigcontext *sc, struct pt_regs *pt) +{ + struct switch_stack *sw = (struct switch_stack *) pt - 1; + unsigned long ip, flags, nat, um; + long err; + + /* restore scratch that always needs gets updated during signal delivery: */ + err = __get_user(flags, &sc->sc_flags); + + err |= __get_user(nat, &sc->sc_nat); + err |= __get_user(ip, &sc->sc_ip); /* instruction pointer */ + err |= __get_user(pt->ar_fpsr, &sc->sc_ar_fpsr); + err |= __get_user(pt->ar_pfs, &sc->sc_ar_pfs); + err |= __get_user(um, &sc->sc_um); /* user mask */ + err |= __get_user(pt->ar_rsc, &sc->sc_ar_rsc); + err |= __get_user(pt->ar_ccv, &sc->sc_ar_ccv); + err |= __get_user(pt->ar_unat, &sc->sc_ar_unat); + err |= __get_user(pt->pr, &sc->sc_pr); /* predicates */ + err |= __get_user(pt->b0, &sc->sc_br[0]); /* b0 (rp) */ + err |= __get_user(pt->b6, &sc->sc_br[6]); + err |= __copy_from_user(&pt->r1, &sc->sc_gr[1], 3*8); /* r1-r3 */ + err |= __copy_from_user(&pt->r8, &sc->sc_gr[8], 4*8); /* r8-r11 */ + err |= __copy_from_user(&pt->r12, &sc->sc_gr[12], 4*8); /* r12-r15 */ + err |= __copy_from_user(&pt->r16, &sc->sc_gr[16], 16*8); /* r16-r31 */ + + /* establish new instruction pointer: */ + pt->cr_iip = ip & ~0x3UL; + ia64_psr(pt)->ri = ip & 0x3; + pt->cr_ipsr = (pt->cr_ipsr & ~IA64_PSR_UM) | (um & IA64_PSR_UM); + + ia64_put_nat_bits (pt, sw, nat); /* restore the original scratch NaT bits */ + + if (flags & IA64_SC_FLAG_FPH_VALID) { + struct task_struct *fpu_owner = ia64_get_fpu_owner(); + + __copy_from_user(current->thread.fph, &sc->sc_fr[32], 96*16); + if (fpu_owner == current) { + __ia64_load_fpu(current->thread.fph); + } + } + return err; +} + +/* + * When we get here, ((struct switch_stack *) pt - 1) is a + * switch_stack frame that has no defined value. Upon return, we + * expect sw->caller_unat to contain the new unat value. The reason + * we use a full switch_stack frame is so everything is symmetric + * with ia64_do_signal(). + */ +long +ia64_rt_sigreturn (struct pt_regs *pt) +{ + extern char ia64_strace_leave_kernel, ia64_leave_kernel; + struct sigcontext *sc; + struct siginfo si; + sigset_t set; + long retval; + + sc = &((struct sigframe *) (pt->r12 + 16))->sc; + + /* + * When we return to the previously executing context, r8 and + * r10 have already been setup the way we want them. Indeed, + * if the signal wasn't delivered while in a system call, we + * must not touch r8 or r10 as otherwise user-level stat could + * be corrupted. + */ + retval = (long) &ia64_leave_kernel | 1; + if ((current->flags & PF_TRACESYS) + && (sc->sc_flags & IA64_SC_FLAG_IN_SYSCALL)) + retval = (long) &ia64_strace_leave_kernel; + + if (!access_ok(VERIFY_READ, sc, sizeof(*sc))) + goto give_sigsegv; + + if (GET_SIGSET(&set, &sc->sc_mask)) + goto give_sigsegv; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(sc, pt)) + goto give_sigsegv; + +#if DEBUG_SIG + printk("SIG return (%s:%d): sp=%lx ip=%lx\n", + current->comm, current->pid, pt->r12, pt->cr_iip); +#endif + /* + * It is more difficult to avoid calling this function than to + * call it and ignore errors. + */ + do_sigaltstack(&sc->sc_stack, 0, pt->r12); + return retval; + + give_sigsegv: + si.si_signo = SIGSEGV; + si.si_errno = 0; + si.si_code = SI_KERNEL; + si.si_pid = current->pid; + si.si_uid = current->uid; + si.si_addr = sc; + force_sig_info(SIGSEGV, &si, current); + return retval; +} + +/* + * This does just the minimum required setup of sigcontext. + * Specifically, it only installs data that is either not knowable at + * the user-level or that gets modified before execution in the + * trampoline starts. Everything else is done at the user-level. + */ +static long +setup_sigcontext (struct sigcontext *sc, sigset_t *mask, struct pt_regs *pt) +{ + struct switch_stack *sw = (struct switch_stack *) pt - 1; + struct task_struct *fpu_owner = ia64_get_fpu_owner(); + unsigned long flags = 0, ifs, nat; + long err; + + ifs = pt->cr_ifs; + + if (on_sig_stack((unsigned long) sc)) + flags |= IA64_SC_FLAG_ONSTACK; + if ((ifs & (1UL << 63)) == 0) { + /* if cr_ifs isn't valid, we got here through a syscall */ + flags |= IA64_SC_FLAG_IN_SYSCALL; + } + if ((fpu_owner == current) || (current->thread.flags & IA64_THREAD_FPH_VALID)) { + flags |= IA64_SC_FLAG_FPH_VALID; + if (fpu_owner == current) { + __ia64_save_fpu(current->thread.fph); + } + __copy_to_user(&sc->sc_fr[32], current->thread.fph, 96*16); + } + + /* + * Note: sw->ar_unat is UNDEFINED unless the process is being + * PTRACED. However, this is OK because the NaT bits of the + * preserved registers (r4-r7) are never being looked at by + * the signal handler (register r4-r7 are used instead). + */ + nat = ia64_get_nat_bits(pt, sw); + + err = __put_user(flags, &sc->sc_flags); + err |= __put_user(nat, &sc->sc_nat); + err |= PUT_SIGSET(mask, &sc->sc_mask); + err |= __put_user(pt->cr_ipsr & IA64_PSR_UM, &sc->sc_um); + err |= __put_user(pt->ar_rsc, &sc->sc_ar_rsc); + err |= __put_user(pt->ar_ccv, &sc->sc_ar_ccv); + err |= __put_user(pt->ar_unat, &sc->sc_ar_unat); /* ar.unat */ + err |= __put_user(pt->ar_fpsr, &sc->sc_ar_fpsr); /* ar.fpsr */ + err |= __put_user(pt->ar_pfs, &sc->sc_ar_pfs); + err |= __put_user(pt->pr, &sc->sc_pr); /* predicates */ + err |= __put_user(pt->b0, &sc->sc_br[0]); /* b0 (rp) */ + err |= __put_user(pt->b6, &sc->sc_br[6]); /* b6 */ + err |= __put_user(pt->b7, &sc->sc_br[7]); /* b7 */ + + err |= __copy_to_user(&sc->sc_gr[1], &pt->r1, 3*8); /* r1-r3 */ + err |= __copy_to_user(&sc->sc_gr[8], &pt->r8, 4*8); /* r8-r11 */ + err |= __copy_to_user(&sc->sc_gr[12], &pt->r12, 4*8); /* r12-r15 */ + err |= __copy_to_user(&sc->sc_gr[16], &pt->r16, 16*8); /* r16-r31 */ + + err |= __put_user(pt->cr_iip + ia64_psr(pt)->ri, &sc->sc_ip); + err |= __put_user(pt->r12, &sc->sc_gr[12]); /* r12 */ + return err; +} + +static long +setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *pt) +{ + struct switch_stack *sw = (struct switch_stack *) pt - 1; + extern char ia64_sigtramp[], __start_gate_section[]; + unsigned long tramp_addr, new_rbs = 0; + struct sigframe *frame; + struct siginfo si; + long err; + + frame = (void *) pt->r12; + tramp_addr = GATE_ADDR + (ia64_sigtramp - __start_gate_section); + if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && !on_sig_stack((unsigned long) frame)) { + new_rbs = (current->sas_ss_sp + sizeof(long) - 1) & ~(sizeof(long) - 1); + frame = (void *) ((current->sas_ss_sp + current->sas_ss_size) + & ~(STACK_ALIGN - 1)); + } + frame = (void *) frame - ((sizeof(*frame) + STACK_ALIGN - 1) & ~(STACK_ALIGN - 1)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err = __copy_to_user(&frame->info, info, sizeof(siginfo_t)); + + err |= __put_user(current->sas_ss_sp, &frame->sc.sc_stack.ss_sp); + err |= __put_user(current->sas_ss_size, &frame->sc.sc_stack.ss_size); + err |= __put_user(sas_ss_flags(pt->r12), &frame->sc.sc_stack.ss_flags); + err |= setup_sigcontext(&frame->sc, set, pt); + + if (err) + goto give_sigsegv; + + pt->r12 = (unsigned long) frame - 16; /* new stack pointer */ + pt->r2 = sig; /* signal number */ + pt->r3 = (unsigned long) ka->sa.sa_handler; /* addr. of handler's proc. descriptor */ + pt->r15 = new_rbs; + pt->ar_fpsr = FPSR_DEFAULT; /* reset fpsr for signal handler */ + pt->cr_iip = tramp_addr; + ia64_psr(pt)->ri = 0; /* start executing in first slot */ + + /* + * Note: this affects only the NaT bits of the scratch regs + * (the ones saved in pt_regs, which is exactly what we want. + * The NaT bits for the preserved regs (r4-r7) are in + * sw->ar_unat iff this process is being PTRACED. + */ + sw->caller_unat = 0; /* ensure NaT bits of at least r2, r3, r12, and r15 are clear */ + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sig=%d sp=%lx ip=%lx handler=%lx\n", + current->comm, current->pid, sig, pt->r12, pt->cr_iip, pt->r3); +#endif + return 1; + + give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + si.si_signo = SIGSEGV; + si.si_errno = 0; + si.si_code = SI_KERNEL; + si.si_pid = current->pid; + si.si_uid = current->uid; + si.si_addr = frame; + force_sig_info(SIGSEGV, &si, current); + return 0; +} + +static long +handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, + struct pt_regs *pt) +{ +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(pt)) { + /* send signal to IA-32 process */ + if (!ia32_setup_frame1(sig, ka, info, oldset, pt)) + return 0; + } else +#endif + /* send signal to IA-64 process */ + if (!setup_frame(sig, ka, info, oldset, pt)) + return 0; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); + sigaddset(¤t->blocked, sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + return 1; +} + +/* + * When we get here, `pt' points to struct pt_regs and ((struct + * switch_stack *) pt - 1) points to a switch stack structure. + * HOWEVER, in the normal case, the ONLY value valid in the + * switch_stack is the caller_unat field. The entire switch_stack is + * valid ONLY if current->flags has PF_PTRACED set. + * + * Note that `init' is a special process: it doesn't get signals it + * doesn't want to handle. Thus you cannot kill init even with a + * SIGKILL even by mistake. + * + * Note that we go through the signals twice: once to check the + * signals that the kernel can handle, and then we build all the + * user-level signal handling stack-frames in one go after that. + */ +long +ia64_do_signal (sigset_t *oldset, struct pt_regs *pt, long in_syscall) +{ + struct k_sigaction *ka; + siginfo_t info; + long restart = in_syscall; + + /* + * In the ia64_leave_kernel code path, we want the common case + * to go fast, which is why we may in certain cases get here + * from kernel mode. Just return without doing anything if so. + */ + if (!user_mode(pt)) + return 0; + + if (!oldset) + oldset = ¤t->blocked; + + if (pt->r10 != -1) { + /* + * A system calls has to be restarted only if one of + * the error codes ERESTARTNOHAND, ERESTARTSYS, or + * ERESTARTNOINTR is returned. If r10 isn't -1 then + * r8 doesn't hold an error code and we don't need to + * restart the syscall, so we set in_syscall to zero. + */ + restart = 0; + } + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + set_current_state(TASK_STOPPED); + notify_parent(current, SIGCHLD); + schedule(); + signr = current->exit_code; + + /* We're back. Did the debugger cancel the sig? */ + if (!signr) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr - 1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + set_current_state(TASK_STOPPED); + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags + & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, pt)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + lock_kernel(); + sigaddset(¤t->signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + if (restart) { + switch (pt->r8) { + case ERESTARTSYS: + if ((ka->sa.sa_flags & SA_RESTART) == 0) { + case ERESTARTNOHAND: + pt->r8 = EINTR; + /* note: pt->r10 is already -1 */ + break; + } + case ERESTARTNOINTR: + ia64_decrement_ip(pt); + } + } + + /* Whee! Actually deliver the signal. If the + delivery failed, we need to continue to iterate in + this loop so we can deliver the SIGSEGV... */ + if (handle_signal(signr, ka, &info, oldset, pt)) + return 1; + } + + /* Did we come from a system call? */ + if (restart) { + /* Restart the system call - no handlers present */ + if (pt->r8 == ERESTARTNOHAND || + pt->r8 == ERESTARTSYS || + pt->r8 == ERESTARTNOINTR) { + /* + * Note: the syscall number is in r15 which is + * saved in pt_regs so all we need to do here + * is adjust ip so that the "break" + * instruction gets re-executed. + */ + ia64_decrement_ip(pt); + } + } + return 0; +} diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c new file mode 100644 index 000000000..48a3d68b4 --- /dev/null +++ b/arch/ia64/kernel/smp.c @@ -0,0 +1,777 @@ +/* + * SMP Support + * + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * + * Lots of stuff stolen from arch/alpha/kernel/smp.c + * + * 99/10/05 davidm Update to bring it in sync with new command-line processing scheme. + */ +#define __KERNEL_SYSCALLS__ + +#include <linux/config.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/smp.h> +#include <linux/kernel_stat.h> +#include <linux/mm.h> + +#include <asm/atomic.h> +#include <asm/bitops.h> +#include <asm/current.h> +#include <asm/delay.h> + +#ifdef CONFIG_KDB +#include <linux/kdb.h> +void smp_kdb_interrupt (struct pt_regs* regs); +void kdb_global(int cpuid); +extern unsigned long smp_kdb_wait; +extern int kdb_new_cpu; +#endif + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/processor.h> +#include <asm/ptrace.h> +#include <asm/sal.h> +#include <asm/system.h> +#include <asm/unistd.h> + +extern int cpu_idle(void * unused); +extern void _start(void); + +extern int cpu_now_booting; /* Used by head.S to find idle task */ +extern unsigned long cpu_initialized; /* Bitmap of available cpu's */ +extern struct cpuinfo_ia64 cpu_data[NR_CPUS]; /* Duh... */ + +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; + +#ifdef CONFIG_KDB +unsigned long cpu_online_map = 1; +#endif + +volatile int cpu_number_map[NR_CPUS] = { -1, }; /* SAPIC ID -> Logical ID */ +volatile int __cpu_logical_map[NR_CPUS] = { -1, }; /* logical ID -> SAPIC ID */ +int smp_num_cpus = 1; +int bootstrap_processor = -1; /* SAPIC ID of BSP */ +int smp_threads_ready = 0; /* Set when the idlers are all forked */ +unsigned long ipi_base_addr = IPI_DEFAULT_BASE_ADDR; /* Base addr of IPI table */ +cycles_t cacheflush_time = 0; +unsigned long ap_wakeup_vector = -1; /* External Int to use to wakeup AP's */ +static int max_cpus = -1; /* Command line */ +static unsigned long ipi_op[NR_CPUS]; +struct smp_call_struct { + void (*func) (void *info); + void *info; + long wait; + atomic_t unstarted_count; + atomic_t unfinished_count; +}; +static struct smp_call_struct *smp_call_function_data; + +#ifdef CONFIG_KDB +unsigned long smp_kdb_wait = 0; /* Bitmask of waiters */ +#endif + +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +extern spinlock_t ivr_read_lock; +#endif + +int use_xtp = 0; /* XXX */ + +#define IPI_RESCHEDULE 0 +#define IPI_CALL_FUNC 1 +#define IPI_CPU_STOP 2 +#define IPI_KDB_INTERRUPT 4 + +/* + * Setup routine for controlling SMP activation + * + * Command-line option of "nosmp" or "maxcpus=0" will disable SMP + * activation entirely (the MPS table probe still happens, though). + * + * Command-line option of "maxcpus=<NUM>", where <NUM> is an integer + * greater than 0, limits the maximum number of CPUs activated in + * SMP mode to <NUM>. + */ + +static int __init nosmp(char *str) +{ + max_cpus = 0; + return 1; +} + +__setup("nosmp", nosmp); + +static int __init maxcpus(char *str) +{ + get_option(&str, &max_cpus); + return 1; +} + +__setup("maxcpus=", maxcpus); + +/* + * Yoink this CPU from the runnable list... + */ +void +halt_processor(void) +{ + clear_bit(smp_processor_id(), &cpu_initialized); + max_xtp(); + __cli(); + for (;;) + ; + +} + +void +handle_IPI(int irq, void *dev_id, struct pt_regs *regs) +{ + int this_cpu = smp_processor_id(); + unsigned long *pending_ipis = &ipi_op[this_cpu]; + unsigned long ops; + + /* Count this now; we may make a call that never returns. */ + cpu_data[this_cpu].ipi_count++; + + mb(); /* Order interrupt and bit testing. */ + while ((ops = xchg(pending_ipis, 0)) != 0) { + mb(); /* Order bit clearing and data access. */ + do { + unsigned long which; + + which = ffz(~ops); + ops &= ~(1 << which); + + switch (which) { + case IPI_RESCHEDULE: + /* + * Reschedule callback. Everything to be done is done by the + * interrupt return path. + */ + break; + + case IPI_CALL_FUNC: + { + struct smp_call_struct *data; + void (*func)(void *info); + void *info; + int wait; + + data = smp_call_function_data; + func = data->func; + info = data->info; + wait = data->wait; + + mb(); + atomic_dec (&data->unstarted_count); + + /* At this point the structure may be gone unless wait is true. */ + (*func)(info); + + /* Notify the sending CPU that the task is done. */ + mb(); + if (wait) + atomic_dec (&data->unfinished_count); + } + break; + + case IPI_CPU_STOP: + halt_processor(); + break; + +#ifdef CONFIG_KDB + case IPI_KDB_INTERRUPT: + smp_kdb_interrupt(regs); + break; +#endif + + default: + printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); + break; + } /* Switch */ + } while (ops); + + mb(); /* Order data access and bit testing. */ + } +} + +static inline void +send_IPI(int dest_cpu, unsigned char vector) +{ + unsigned long ipi_addr; + unsigned long ipi_data; +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC + unsigned long flags; +#endif + + ipi_data = vector; + ipi_addr = ipi_base_addr | ((dest_cpu << 8) << 4); /* 16-bit SAPIC ID's; assume CPU bus 0 */ + mb(); + +#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC + /* + * Disable IVR reads + */ + spin_lock_irqsave(&ivr_read_lock, flags); + writeq(ipi_data, ipi_addr); + spin_unlock_irqrestore(&ivr_read_lock, flags); +#else + writeq(ipi_data, ipi_addr); +#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ + +} + +static inline void +send_IPI_single(int dest_cpu, int op) +{ + + if (dest_cpu == -1) + return; + + ipi_op[dest_cpu] |= (1 << op); + send_IPI(dest_cpu, IPI_IRQ); +} + +static inline void +send_IPI_allbutself(int op) +{ + int i; + int cpu_id = 0; + + for (i = 0; i < smp_num_cpus; i++) { + cpu_id = __cpu_logical_map[i]; + if (cpu_id != smp_processor_id()) + send_IPI_single(cpu_id, op); + } +} + +static inline void +send_IPI_all(int op) +{ + int i; + + for (i = 0; i < smp_num_cpus; i++) + send_IPI_single(__cpu_logical_map[i], op); +} + +static inline void +send_IPI_self(int op) +{ + send_IPI_single(smp_processor_id(), op); +} + +void +smp_send_reschedule(int cpu) +{ + send_IPI_single(cpu, IPI_RESCHEDULE); +} + +void +smp_send_stop(void) +{ + send_IPI_allbutself(IPI_CPU_STOP); +} + +/* + * Run a function on all other CPUs. + * <func> The function to run. This must be fast and non-blocking. + * <info> An arbitrary pointer to pass to the function. + * <retry> If true, keep retrying until ready. + * <wait> If true, wait until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. + * + * Does not return until remote CPUs are nearly ready to execute <func> + * or are or have executed. + */ + +int +smp_call_function (void (*func) (void *info), void *info, int retry, int wait) +{ + struct smp_call_struct data; + long timeout; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + + data.func = func; + data.info = info; + data.wait = wait; + atomic_set(&data.unstarted_count, smp_num_cpus - 1); + atomic_set(&data.unfinished_count, smp_num_cpus - 1); + + if (retry) { + while (1) { + if (smp_call_function_data) { + schedule (); /* Give a mate a go */ + continue; + } + spin_lock (&lock); + if (smp_call_function_data) { + spin_unlock (&lock); /* Bad luck */ + continue; + } + /* Mine, all mine! */ + break; + } + } + else { + if (smp_call_function_data) + return -EBUSY; + spin_lock (&lock); + if (smp_call_function_data) { + spin_unlock (&lock); + return -EBUSY; + } + } + + smp_call_function_data = &data; + spin_unlock (&lock); + data.func = func; + data.info = info; + atomic_set (&data.unstarted_count, smp_num_cpus - 1); + data.wait = wait; + if (wait) + atomic_set (&data.unfinished_count, smp_num_cpus - 1); + + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself(IPI_CALL_FUNC); + + /* Wait for response */ + timeout = jiffies + HZ; + while ( (atomic_read (&data.unstarted_count) > 0) && + time_before (jiffies, timeout) ) + barrier (); + if (atomic_read (&data.unstarted_count) > 0) { + smp_call_function_data = NULL; + return -ETIMEDOUT; + } + if (wait) + while (atomic_read (&data.unfinished_count) > 0) + barrier (); + smp_call_function_data = NULL; + return 0; +} + +/* + * Flush all other CPU's tlb and then mine. Do this with smp_call_function() as we + * want to ensure all TLB's flushed before proceeding. + * + * XXX: Is it OK to use the same ptc.e info on all cpus? + */ +void +smp_flush_tlb_all(void) +{ + smp_call_function((void (*)(void *))__flush_tlb_all, NULL, 1, 1); + __flush_tlb_all(); +} + +/* + * Ideally sets up per-cpu profiling hooks. Doesn't do much now... + */ +static inline void __init +smp_setup_percpu_timer(int cpuid) +{ + cpu_data[cpuid].prof_counter = 1; + cpu_data[cpuid].prof_multiplier = 1; +} + +void +smp_do_timer(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + int user = user_mode(regs); + struct cpuinfo_ia64 *data = &cpu_data[cpu]; + + extern void update_one_process(struct task_struct *, unsigned long, unsigned long, + unsigned long, int); + if (!--data->prof_counter) { + irq_enter(cpu, TIMER_IRQ); + + update_one_process(current, 1, user, !user, cpu); + if (current->pid) { + if (--current->counter < 0) { + current->counter = 0; + current->need_resched = 1; + } + + if (user) { + if (current->priority < DEF_PRIORITY) { + kstat.cpu_nice++; + kstat.per_cpu_nice[cpu]++; + } else { + kstat.cpu_user++; + kstat.per_cpu_user[cpu]++; + } + } else { + kstat.cpu_system++; + kstat.per_cpu_system[cpu]++; + } + } + + data->prof_counter = data->prof_multiplier; + irq_exit(cpu, TIMER_IRQ); + } +} + + +/* + * Called by both boot and secondaries to move global data into + * per-processor storage. + */ +static inline void __init +smp_store_cpu_info(int cpuid) +{ + struct cpuinfo_ia64 *c = &cpu_data[cpuid]; + + identify_cpu(c); +} + +/* + * SAL shoves the AP's here when we start them. Physical mode, no kernel TR, + * no RRs set, better than even chance that psr is bogus. Fix all that and + * call _start. In effect, pretend to be lilo. + * + * Stolen from lilo_start.c. Thanks David! + */ +void +start_ap(void) +{ + unsigned long flags; + + /* + * Install a translation register that identity maps the + * kernel's 256MB page(s). + */ + ia64_clear_ic(flags); + ia64_set_rr( 0, (0x1000 << 8) | (_PAGE_SIZE_1M << 2)); + ia64_set_rr(PAGE_OFFSET, (ia64_rid(0, PAGE_OFFSET) << 8) | (_PAGE_SIZE_256M << 2)); + ia64_itr(0x3, 1, PAGE_OFFSET, + pte_val(mk_pte_phys(0, __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RWX))), + _PAGE_SIZE_256M); + + flags = (IA64_PSR_IT | IA64_PSR_IC | IA64_PSR_DT | IA64_PSR_RT | IA64_PSR_DFH | + IA64_PSR_BN); + + asm volatile ("movl r8 = 1f\n" + ";;\n" + "mov cr.ipsr=%0\n" + "mov cr.iip=r8\n" + "mov cr.ifs=r0\n" + ";;\n" + "rfi;;" + "1:\n" + "movl r1 = __gp" :: "r"(flags) : "r8"); + _start(); +} + + +/* + * AP's start using C here. + */ +void __init +smp_callin(void) +{ + extern void ia64_rid_init(void); + extern void ia64_init_itm(void); + extern void ia64_cpu_local_tick(void); + + ia64_set_dcr(IA64_DCR_DR | IA64_DCR_DK | IA64_DCR_DX | IA64_DCR_PP); + ia64_set_fpu_owner(0); + ia64_rid_init(); /* initialize region ids */ + + cpu_init(); + __flush_tlb_all(); + + smp_store_cpu_info(smp_processor_id()); + smp_setup_percpu_timer(smp_processor_id()); + + while (!smp_threads_ready) + mb(); + + normal_xtp(); + + /* setup the CPU local timer tick */ + ia64_cpu_local_tick(); + + /* Disable all local interrupts */ + ia64_set_lrr0(0, 1); + ia64_set_lrr1(0, 1); + + __sti(); /* Interrupts have been off till now. */ + cpu_idle(NULL); +} + +/* + * Create the idle task for a new AP. DO NOT use kernel_thread() because + * that could end up calling schedule() in the ia64_leave_kernel exit + * path in which case the new idle task could get scheduled before we + * had a chance to remove it from the run-queue... + */ +static int __init +fork_by_hand(void) +{ + /* + * Don't care about the usp and regs settings since we'll never + * reschedule the forked task. + */ + return do_fork(CLONE_VM|CLONE_PID, 0, 0); +} + +/* + * Bring one cpu online. + * + * NB: cpuid is the CPU BUS-LOCAL ID, not the entire SAPIC ID. See asm/smp.h. + */ +static int __init +smp_boot_one_cpu(int cpuid, int cpunum) +{ + struct task_struct *idle; + long timeout; + + /* + * Create an idle task for this CPU. Note that the address we + * give to kernel_thread is irrelevant -- it's going to start + * where OS_BOOT_RENDEVZ vector in SAL says to start. But + * this gets all the other task-y sort of data structures set + * up like we wish. We need to pull the just created idle task + * off the run queue and stuff it into the init_tasks[] array. + * Sheesh . . . + */ + if (fork_by_hand() < 0) + panic("failed fork for CPU %d", cpuid); + /* + * We remove it from the pidhash and the runqueue + * once we got the process: + */ + idle = init_task.prev_task; + if (!idle) + panic("No idle process for CPU %d", cpuid); + init_tasks[cpunum] = idle; + del_from_runqueue(idle); + unhash_process(idle); + + /* Schedule the first task manually. */ + idle->processor = cpuid; + idle->has_cpu = 1; + + /* Let _start know what logical CPU we're booting (offset into init_tasks[] */ + cpu_now_booting = cpunum; + + /* Kick the AP in the butt */ + send_IPI(cpuid, ap_wakeup_vector); + ia64_srlz_i(); + mb(); + + /* + * OK, wait a bit for that CPU to finish staggering about. smp_callin() will + * call cpu_init() which will set a bit for this AP. When that bit flips, the AP + * is waiting for smp_threads_ready to be 1 and we can move on. + */ + for (timeout = 0; timeout < 100000; timeout++) { + if (test_bit(cpuid, &cpu_initialized)) + goto alive; + udelay(10); + barrier(); + } + + printk(KERN_ERR "SMP: Processor %d is stuck.\n", cpuid); + return -1; + +alive: + /* Remember the AP data */ + cpu_number_map[cpuid] = cpunum; +#ifdef CONFIG_KDB + cpu_online_map |= (1<<cpunum); + printk ("DEBUGGER: cpu_online_map = 0x%08x\n", cpu_online_map); +#endif + __cpu_logical_map[cpunum] = cpuid; + return 0; +} + + + +/* + * Called by smp_init bring all the secondaries online and hold them. + * XXX: this is ACPI specific; it uses "magic" variables exported from acpi.c + * to 'discover' the AP's. Blech. + */ +void __init +smp_boot_cpus(void) +{ + int i, cpu_count = 1; + unsigned long bogosum; + int sapic_id; + extern int acpi_cpus; + extern int acpi_apic_map[32]; + + /* Take care of some initial bookkeeping. */ + memset(&cpu_number_map, -1, sizeof(cpu_number_map)); + memset(&__cpu_logical_map, -1, sizeof(__cpu_logical_map)); + memset(&ipi_op, 0, sizeof(ipi_op)); + + /* Setup BSP mappings */ + cpu_number_map[bootstrap_processor] = 0; + __cpu_logical_map[0] = bootstrap_processor; + current->processor = bootstrap_processor; + + /* Mark BSP booted and get active_mm context */ + cpu_init(); + + /* reset XTP for interrupt routing */ + normal_xtp(); + + /* And generate an entry in cpu_data */ + smp_store_cpu_info(bootstrap_processor); +#if 0 + smp_tune_scheduling(); +#endif + smp_setup_percpu_timer(bootstrap_processor); + + init_idle(); + + /* Nothing to do when told not to. */ + if (max_cpus == 0) { + printk(KERN_INFO "SMP mode deactivated.\n"); + return; + } + + if (acpi_cpus > 1) { + printk(KERN_INFO "SMP: starting up secondaries.\n"); + + for (i = 0; i < NR_CPUS; i++) { + if (acpi_apic_map[i] == -1 || + acpi_apic_map[i] == bootstrap_processor << 8) /* XXX Fix me Walt */ + continue; + + /* + * IA64 SAPIC ID's are 16-bits. See asm/smp.h for more info + */ + sapic_id = acpi_apic_map[i] >> 8; + if (smp_boot_one_cpu(sapic_id, cpu_count)) + continue; + + cpu_count++; /* Count good CPUs only... */ + } + } + + if (cpu_count == 1) { + printk(KERN_ERR "SMP: Bootstrap processor only.\n"); + return; + } + + bogosum = 0; + for (i = 0; i < NR_CPUS; i++) { + if (cpu_initialized & (1L << i)) + bogosum += cpu_data[i].loops_per_sec; + } + + printk(KERN_INFO "SMP: Total of %d processors activated " + "(%lu.%02lu BogoMIPS).\n", + cpu_count, (bogosum + 2500) / 500000, + ((bogosum + 2500) / 5000) % 100); + + smp_num_cpus = cpu_count; +} + +/* + * Called from main.c by each AP. + */ +void __init +smp_commence(void) +{ + mb(); +} + +/* + * Not used; part of the i386 bringup + */ +void __init +initialize_secondary(void) +{ +} + +int __init +setup_profiling_timer(unsigned int multiplier) +{ + return -EINVAL; +} + +/* + * Assume that CPU's have been discovered by some platform-dependant + * interface. For SoftSDV/Lion, that would be ACPI. + * + * Setup of the IPI irq handler is done in irq.c:init_IRQ_SMP(). + * + * So this just gets the BSP SAPIC ID and print's it out. Dull, huh? + * + * Not anymore. This also registers the AP OS_MC_REDVEZ address with SAL. + */ +void __init +init_smp_config(void) +{ + struct fptr { + unsigned long fp; + unsigned long gp; + } *ap_startup; + long sal_ret; + + /* Grab the BSP ID */ + bootstrap_processor = hard_smp_processor_id(); + + /* Tell SAL where to drop the AP's. */ + ap_startup = (struct fptr *) start_ap; + sal_ret = ia64_sal_set_vectors(SAL_VECTOR_OS_BOOT_RENDEZ, + __pa(ap_startup->fp), __pa(ap_startup->gp), 0, + 0, 0, 0); + if (sal_ret < 0) { + printk("SMP: Can't set SAL AP Boot Rendezvous: %s\n", ia64_sal_strerror(sal_ret)); + printk(" Forcing UP mode\n"); + smp_num_cpus = 1; + } + +} + +#ifdef CONFIG_KDB +void smp_kdb_stop (int all, struct pt_regs* regs) +{ + if (all) + { + printk ("Sending IPI to all on CPU %i\n", smp_processor_id ()); + smp_kdb_wait = 0xffffffff; + clear_bit (smp_processor_id(), &smp_kdb_wait); + send_IPI_allbutself (IPI_KDB_INTERRUPT); + } + else + { + printk ("Sending IPI to self on CPU %i\n", + smp_processor_id ()); + set_bit (smp_processor_id(), &smp_kdb_wait); + clear_bit (__cpu_logical_map[kdb_new_cpu], &smp_kdb_wait); + smp_kdb_interrupt (regs); + } +} + +void smp_kdb_interrupt (struct pt_regs* regs) +{ + printk ("kdb: IPI on CPU %i with mask 0x%08x\n", + smp_processor_id (), smp_kdb_wait); + + /* All CPUs spin here forever */ + while (test_bit (smp_processor_id(), &smp_kdb_wait)); + + /* Enter KDB on CPU selected by KDB on the last CPU */ + if (__cpu_logical_map[kdb_new_cpu] == smp_processor_id ()) + { + kdb (KDB_REASON_SWITCH, 0, regs); + } +} + +#endif + diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c new file mode 100644 index 000000000..18a498a09 --- /dev/null +++ b/arch/ia64/kernel/sys_ia64.c @@ -0,0 +1,216 @@ +/* + * This file contains various system calls that have different calling + * conventions on different platforms. + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/sched.h> +#include <linux/file.h> /* doh, must come after sched.h... */ +#include <linux/smp.h> +#include <linux/smp_lock.h> + +asmlinkage long +ia64_getpriority (int which, int who, long arg2, long arg3, long arg4, long arg5, long arg6, + long arg7, long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + extern long sys_getpriority (int, int); + long prio; + + prio = sys_getpriority(which, who); + if (prio >= 0) { + regs->r8 = 0; /* ensure negative priority is not mistaken as error code */ + prio = 20 - prio; + } + return prio; +} + +asmlinkage unsigned long +sys_getpagesize (void) +{ + return PAGE_SIZE; +} + +asmlinkage unsigned long +ia64_shmat (int shmid, void *shmaddr, int shmflg, long arg3, long arg4, long arg5, long arg6, + long arg7, long stack) +{ + extern int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr); + struct pt_regs *regs = (struct pt_regs *) &stack; + unsigned long raddr; + int retval; + + retval = sys_shmat(shmid, shmaddr, shmflg, &raddr); + if (retval < 0) + return retval; + + regs->r8 = 0; /* ensure negative addresses are not mistaken as an error code */ + return raddr; +} + +asmlinkage unsigned long +ia64_brk (long brk, long arg1, long arg2, long arg3, + long arg4, long arg5, long arg6, long arg7, long stack) +{ + extern unsigned long sys_brk (unsigned long brk); + struct pt_regs *regs = (struct pt_regs *) &stack; + unsigned long retval; + + retval = sys_brk(brk); + + regs->r8 = 0; /* ensure large retval isn't mistaken as error code */ + return retval; +} + +/* + * On IA-64, we return the two file descriptors in ret0 and ret1 (r8 + * and r9) as this is faster than doing a copy_to_user(). + */ +asmlinkage long +sys_pipe (long arg0, long arg1, long arg2, long arg3, + long arg4, long arg5, long arg6, long arg7, long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + int fd[2]; + int retval; + + lock_kernel(); + retval = do_pipe(fd); + if (retval) + goto out; + retval = fd[0]; + regs->r9 = fd[1]; + out: + unlock_kernel(); + return retval; +} + +static inline unsigned long +do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff) +{ + struct file *file = 0; + + /* + * A zero mmap always succeeds in Linux, independent of + * whether or not the remaining arguments are valid. + */ + if (PAGE_ALIGN(len) == 0) + return addr; + +#ifdef notyet + /* Don't permit mappings that would cross a region boundary: */ + region_start = IA64_GET_REGION(addr); + region_end = IA64_GET_REGION(addr + len); + if (region_start != region_end) + return -EINVAL; + + <<x??x>> +#endif + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + return -EBADF; + } + + down(¤t->mm->mmap_sem); + lock_kernel(); + + addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + + unlock_kernel(); + up(¤t->mm->mmap_sem); + + if (file) + fput(file); + return addr; +} + +/* + * mmap2() is like mmap() except that the offset is expressed in units + * of PAGE_SIZE (instead of bytes). This allows to mmap2() (pieces + * of) files that are larger than the address space of the CPU. + */ +asmlinkage unsigned long +sys_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, long pgoff, + long arg6, long arg7, long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + + addr = do_mmap2(addr, len, prot, flags, fd, pgoff); + if (!IS_ERR(addr)) + regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */ + return addr; +} + +asmlinkage unsigned long +sys_mmap (unsigned long addr, unsigned long len, int prot, int flags, + int fd, long off, long arg6, long arg7, long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + + addr = do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT); + if (!IS_ERR(addr)) + regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */ + return addr; +} + +asmlinkage long +sys_ioperm (unsigned long from, unsigned long num, int on) +{ + printk(KERN_ERR "sys_ioperm(from=%lx, num=%lx, on=%d)\n", from, num, on); + return -EIO; +} + +asmlinkage long +sys_iopl (int level, long arg1, long arg2, long arg3) +{ + lock_kernel(); + printk(KERN_ERR "sys_iopl(level=%d)!\n", level); + unlock_kernel(); + return -ENOSYS; +} + +asmlinkage long +sys_vm86 (long arg0, long arg1, long arg2, long arg3) +{ + lock_kernel(); + printk(KERN_ERR "sys_vm86(%lx, %lx, %lx, %lx)!\n", arg0, arg1, arg2, arg3); + unlock_kernel(); + return -ENOSYS; +} + +asmlinkage long +sys_modify_ldt (long arg0, long arg1, long arg2, long arg3) +{ + lock_kernel(); + printk(KERN_ERR "sys_modify_ldt(%lx, %lx, %lx, %lx)!\n", arg0, arg1, arg2, arg3); + unlock_kernel(); + return -ENOSYS; +} + +#ifndef CONFIG_PCI + +asmlinkage long +sys_pciconfig_read (unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, + void *buf) +{ + return -ENOSYS; +} + +asmlinkage long +sys_pciconfig_write (unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, + void *buf) +{ + return -ENOSYS; +} + + +#endif /* CONFIG_PCI */ diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c new file mode 100644 index 000000000..7c5ace740 --- /dev/null +++ b/arch/ia64/kernel/time.c @@ -0,0 +1,290 @@ +/* + * linux/arch/ia64/kernel/time.c + * + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998-2000 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 1999-2000 David Mosberger <davidm@hpl.hp.com> + * Copyright (C) 1999 Don Dugger <don.dugger@intel.com> + * Copyright (C) 1999-2000 VA Linux Systems + * Copyright (C) 1999-2000 Walt Drummond <drummond@valinux.com> + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/time.h> + +#include <asm/delay.h> +#include <asm/efi.h> +#include <asm/irq.h> +#include <asm/machvec.h> +#include <asm/ptrace.h> +#include <asm/sal.h> +#include <asm/system.h> + +extern rwlock_t xtime_lock; +extern volatile unsigned long lost_ticks; + +#ifdef CONFIG_IA64_DEBUG_IRQ + +unsigned long last_cli_ip; + +#endif + +static struct { + unsigned long delta; + unsigned long next[NR_CPUS]; +} itm; + +static void +do_profile (unsigned long ip) +{ + extern char _stext; + + if (prof_buffer && current->pid) { + ip -= (unsigned long) &_stext; + ip >>= prof_shift; + /* + * Don't ignore out-of-bounds IP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (ip > prof_len - 1) + ip = prof_len - 1; + + atomic_inc((atomic_t *) &prof_buffer[ip]); + } +} + +/* + * Return the number of micro-seconds that elapsed since the last + * update to jiffy. The xtime_lock must be at least read-locked when + * calling this routine. + */ +static inline unsigned long +gettimeoffset (void) +{ + unsigned long now = ia64_get_itc(); + unsigned long elapsed_cycles, lost; + + elapsed_cycles = now - (itm.next[smp_processor_id()] - itm.delta); + + lost = lost_ticks; + if (lost) + elapsed_cycles += lost*itm.delta; + + return (elapsed_cycles*my_cpu_data.usec_per_cyc) >> IA64_USEC_PER_CYC_SHIFT; +} + +void +do_settimeofday (struct timeval *tv) +{ + write_lock_irq(&xtime_lock); + { + /* + * This is revolting. We need to set the xtime.tv_usec + * correctly. However, the value in this location is + * is value at the last tick. Discover what + * correction gettimeofday would have done, and then + * undo it! + */ + tv->tv_usec -= gettimeoffset(); + while (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + } + write_unlock_irq(&xtime_lock); +} + +void +do_gettimeofday (struct timeval *tv) +{ + unsigned long flags, usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + { + usec = gettimeoffset(); + + sec = xtime.tv_sec; + usec += xtime.tv_usec; + } + read_unlock_irqrestore(&xtime_lock, flags); + + while (usec >= 1000000) { + usec -= 1000000; + ++sec; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +static void +timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + static unsigned long last_time; + static unsigned char count; + int cpu = smp_processor_id(); + + /* + * Here we are in the timer irq handler. We have irqs locally + * disabled, but we don't know if the timer_bh is running on + * another CPU. We need to avoid to SMP race by acquiring the + * xtime_lock. + */ + write_lock(&xtime_lock); + while (1) { + /* do kernel PC profiling here. */ + if (!user_mode(regs)) + do_profile(regs->cr_iip); + +#ifdef CONFIG_SMP + smp_do_timer(regs); + if (smp_processor_id() == bootstrap_processor) + do_timer(regs); +#else + do_timer(regs); +#endif + + itm.next[cpu] += itm.delta; + /* + * There is a race condition here: to be on the "safe" + * side, we process timer ticks until itm.next is + * ahead of the itc by at least half the timer + * interval. This should give us enough time to set + * the new itm value without losing a timer tick. + */ + if (time_after(itm.next[cpu], ia64_get_itc() + itm.delta/2)) { + ia64_set_itm(itm.next[cpu]); + break; + } + +#if !(defined(CONFIG_IA64_SOFTSDV_HACKS) && defined(CONFIG_SMP)) + /* + * SoftSDV in SMP mode is _slow_, so we do "loose" ticks, + * but it's really OK... + */ + if (count > 0 && jiffies - last_time > 5*HZ) + count = 0; + if (count++ == 0) { + last_time = jiffies; + printk("Lost clock tick on CPU %d (now=%lx, next=%lx)!!\n", + cpu, ia64_get_itc(), itm.next[cpu]); +# ifdef CONFIG_IA64_DEBUG_IRQ + printk("last_cli_ip=%lx\n", last_cli_ip); +# endif + } +#endif + } + write_unlock(&xtime_lock); +} + +/* + * Encapsulate access to the itm structure for SMP. + */ +void __init +ia64_cpu_local_tick(void) +{ + /* arrange for the cycle counter to generate a timer interrupt: */ + ia64_set_itv(TIMER_IRQ, 0); + ia64_set_itc(0); + itm.next[smp_processor_id()] = ia64_get_itc() + itm.delta; + ia64_set_itm(itm.next[smp_processor_id()]); +} + +void __init +ia64_init_itm (void) +{ + unsigned long platform_base_freq, itc_freq, drift; + struct pal_freq_ratio itc_ratio, proc_ratio; + long status; + + /* + * According to SAL v2.6, we need to use a SAL call to determine the + * platform base frequency and then a PAL call to determine the + * frequency ratio between the ITC and the base frequency. + */ + status = ia64_sal_freq_base(SAL_FREQ_BASE_PLATFORM, &platform_base_freq, &drift); + if (status != 0) { + printk("SAL_FREQ_BASE_PLATFORM failed: %s\n", ia64_sal_strerror(status)); + } else { + status = ia64_pal_freq_ratios(&proc_ratio, 0, &itc_ratio); + if (status != 0) + printk("PAL_FREQ_RATIOS failed with status=%ld\n", status); + } + if (status != 0) { + /* invent "random" values */ + printk("SAL/PAL failed to obtain frequency info---inventing reasonably values\n"); + platform_base_freq = 100000000; + itc_ratio.num = 3; + itc_ratio.den = 1; + } +#if defined(CONFIG_IA64_LION_HACKS) + /* Our Lion currently returns base freq 104.857MHz, which + ain't right (it really is 100MHz). */ + printk("SAL/PAL returned: base-freq=%lu, itc-ratio=%lu/%lu, proc-ratio=%lu/%lu\n", + platform_base_freq, itc_ratio.num, itc_ratio.den, + proc_ratio.num, proc_ratio.den); + platform_base_freq = 100000000; +#elif 0 && defined(CONFIG_IA64_BIGSUR_HACKS) + /* BigSur with 991020 firmware returned itc-ratio=9/2 and base + freq 75MHz, which wasn't right. The 991119 firmware seems + to return the right values, so this isn't necessary + anymore... */ + printk("SAL/PAL returned: base-freq=%lu, itc-ratio=%lu/%lu, proc-ratio=%lu/%lu\n", + platform_base_freq, itc_ratio.num, itc_ratio.den, + proc_ratio.num, proc_ratio.den); + platform_base_freq = 100000000; + proc_ratio.num = 5; proc_ratio.den = 1; + itc_ratio.num = 5; itc_ratio.den = 1; +#elif defined(CONFIG_IA64_SOFTSDV_HACKS) + platform_base_freq = 10000000; + proc_ratio.num = 4; proc_ratio.den = 1; + itc_ratio.num = 4; itc_ratio.den = 1; +#else + if (platform_base_freq < 40000000) { + printk("Platform base frequency %lu bogus---resetting to 75MHz!\n", + platform_base_freq); + platform_base_freq = 75000000; + } +#endif + if (!proc_ratio.den) + proc_ratio.num = 1; /* avoid division by zero */ + if (!itc_ratio.den) + itc_ratio.num = 1; /* avoid division by zero */ + + itc_freq = (platform_base_freq*itc_ratio.num)/itc_ratio.den; + itm.delta = itc_freq / HZ; + printk("timer: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, ITC freq=%lu.%03luMHz\n", + platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000, + itc_ratio.num, itc_ratio.den, itc_freq / 1000000, (itc_freq / 1000) % 1000); + + my_cpu_data.proc_freq = (platform_base_freq*proc_ratio.num)/proc_ratio.den; + my_cpu_data.itc_freq = itc_freq; + my_cpu_data.cyc_per_usec = itc_freq / 1000000; + my_cpu_data.usec_per_cyc = (1000000UL << IA64_USEC_PER_CYC_SHIFT) / itc_freq; + + /* Setup the CPU local timer tick */ + ia64_cpu_local_tick(); +} + +void __init +time_init (void) +{ + /* + * Request the IRQ _before_ doing anything to cause that + * interrupt to be posted. + */ + if (request_irq(TIMER_IRQ, timer_interrupt, 0, "timer", NULL)) + panic("Could not allocate timer IRQ!"); + + efi_gettimeofday(&xtime); + ia64_init_itm(); +} diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c new file mode 100644 index 000000000..c242622ec --- /dev/null +++ b/arch/ia64/kernel/traps.c @@ -0,0 +1,423 @@ +/* + * Architecture-specific trap handling. + * + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +/* + * The fpu_fault() handler needs to be able to access and update all + * floating point registers. Those saved in pt_regs can be accessed + * through that structure, but those not saved, will be accessed + * directly. To make this work, we need to ensure that the compiler + * does not end up using a preserved floating point register on its + * own. The following achieves this by declaring preserved registers + * that are not marked as "fixed" as global register variables. + */ +register double f2 asm ("f2"); register double f3 asm ("f3"); +register double f4 asm ("f4"); register double f5 asm ("f5"); + +register long f16 asm ("f16"); register long f17 asm ("f17"); +register long f18 asm ("f18"); register long f19 asm ("f19"); +register long f20 asm ("f20"); register long f21 asm ("f21"); +register long f22 asm ("f22"); register long f23 asm ("f23"); + +register double f24 asm ("f24"); register double f25 asm ("f25"); +register double f26 asm ("f26"); register double f27 asm ("f27"); +register double f28 asm ("f28"); register double f29 asm ("f29"); +register double f30 asm ("f30"); register double f31 asm ("f31"); + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> + +#ifdef CONFIG_KDB +# include <linux/kdb.h> +#endif + +#include <asm/processor.h> +#include <asm/uaccess.h> + +#include <asm/fpswa.h> + +static fpswa_interface_t *fpswa_interface; + +void __init +trap_init (void) +{ + printk("fpswa interface at %lx\n", ia64_boot_param.fpswa); + if (ia64_boot_param.fpswa) { +#define OLD_FIRMWARE +#ifdef OLD_FIRMWARE + /* + * HACK to work around broken firmware. This code + * applies the label fixup to the FPSWA interface and + * works both with old and new (fixed) firmware. + */ + unsigned long addr = (unsigned long) __va(ia64_boot_param.fpswa); + unsigned long gp_val = *(unsigned long *)(addr + 8); + + /* go indirect and indexed to get table address */ + addr = gp_val; + gp_val = *(unsigned long *)(addr + 8); + + while (gp_val == *(unsigned long *)(addr + 8)) { + *(unsigned long *)addr |= PAGE_OFFSET; + *(unsigned long *)(addr + 8) |= PAGE_OFFSET; + addr += 16; + } +#endif + /* FPSWA fixup: make the interface pointer a kernel virtual address: */ + fpswa_interface = __va(ia64_boot_param.fpswa); + } +} + +void +die_if_kernel (char *str, struct pt_regs *regs, long err) +{ + if (user_mode(regs)) { +#if 1 + /* XXX for debugging only */ + printk ("!!die_if_kernel: %s(%d): %s %ld\n", + current->comm, current->pid, str, err); + show_regs(regs); +#endif + return; + } + + printk("%s[%d]: %s %ld\n", current->comm, current->pid, str, err); + +#ifdef CONFIG_KDB + while (1) { + kdb(KDB_REASON_PANIC, 0, regs); + printk("Cant go anywhere from Panic!\n"); + } +#endif + + show_regs(regs); + + if (current->thread.flags & IA64_KERNEL_DEATH) { + printk("die_if_kernel recursion detected.\n"); + sti(); + while (1); + } + current->thread.flags |= IA64_KERNEL_DEATH; + do_exit(SIGSEGV); +} + +void +ia64_bad_break (unsigned long break_num, struct pt_regs *regs) +{ + siginfo_t siginfo; + + /* gdb uses a break number of 0xccccc for debug breakpoints: */ + if (break_num != 0xccccc) + die_if_kernel("Bad break", regs, break_num); + + siginfo.si_signo = SIGTRAP; + siginfo.si_errno = break_num; /* XXX is it legal to abuse si_errno like this? */ + siginfo.si_code = TRAP_BRKPT; + send_sig_info(SIGTRAP, &siginfo, current); +} + +/* + * Unimplemented system calls. This is called only for stuff that + * we're supposed to implement but haven't done so yet. Everything + * else goes to sys_ni_syscall. + */ +asmlinkage long +ia64_ni_syscall (unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5, unsigned long arg6, unsigned long arg7, + unsigned long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + + printk("<sc%ld(%lx,%lx,%lx,%lx)>\n", regs->r15, arg0, arg1, arg2, arg3); + return -ENOSYS; +} + +/* + * disabled_fp_fault() is called when a user-level process attempts to + * access one of the registers f32..f127 while it doesn't own the + * fp-high register partition. When this happens, we save the current + * fph partition in the task_struct of the fpu-owner (if necessary) + * and then load the fp-high partition of the current task (if + * necessary). + */ +static inline void +disabled_fph_fault (struct pt_regs *regs) +{ + struct task_struct *fpu_owner = ia64_get_fpu_owner(); + + regs->cr_ipsr &= ~(IA64_PSR_DFH | IA64_PSR_MFH); + if (fpu_owner != current) { + ia64_set_fpu_owner(current); + + if (fpu_owner && ia64_psr(ia64_task_regs(fpu_owner))->mfh) { + fpu_owner->thread.flags |= IA64_THREAD_FPH_VALID; + __ia64_save_fpu(fpu_owner->thread.fph); + } + if ((current->thread.flags & IA64_THREAD_FPH_VALID) != 0) { + __ia64_load_fpu(current->thread.fph); + } else { + __ia64_init_fpu(); + } + } +} + +static inline int +fp_emulate (int fp_fault, void *bundle, long *ipsr, long *fpsr, long *isr, long *pr, long *ifs, + struct pt_regs *regs) +{ + fp_state_t fp_state; + fpswa_ret_t ret; +#ifdef FPSWA_BUG + struct ia64_fpreg f6_15[10]; +#endif + + if (!fpswa_interface) + return -1; + + memset(&fp_state, 0, sizeof(fp_state_t)); + + /* + * compute fp_state. only FP registers f6 - f11 are used by the + * kernel, so set those bits in the mask and set the low volatile + * pointer to point to these registers. + */ + fp_state.bitmask_low64 = 0xffc0; /* bit6..bit15 */ +#ifndef FPSWA_BUG + fp_state.fp_state_low_volatile = ®s->f6; +#else + f6_15[0] = regs->f6; + f6_15[1] = regs->f7; + f6_15[2] = regs->f8; + f6_15[3] = regs->f9; + __asm__ ("stf.spill %0=f10" : "=m"(f6_15[4])); + __asm__ ("stf.spill %0=f11" : "=m"(f6_15[5])); + __asm__ ("stf.spill %0=f12" : "=m"(f6_15[6])); + __asm__ ("stf.spill %0=f13" : "=m"(f6_15[7])); + __asm__ ("stf.spill %0=f14" : "=m"(f6_15[8])); + __asm__ ("stf.spill %0=f15" : "=m"(f6_15[9])); + fp_state.fp_state_low_volatile = (fp_state_low_volatile_t *) f6_15; +#endif + /* + * unsigned long (*EFI_FPSWA) ( + * unsigned long trap_type, + * void *Bundle, + * unsigned long *pipsr, + * unsigned long *pfsr, + * unsigned long *pisr, + * unsigned long *ppreds, + * unsigned long *pifs, + * void *fp_state); + */ + ret = (*fpswa_interface->fpswa)((unsigned long) fp_fault, bundle, + (unsigned long *) ipsr, (unsigned long *) fpsr, + (unsigned long *) isr, (unsigned long *) pr, + (unsigned long *) ifs, &fp_state); +#ifdef FPSWA_BUG + __asm__ ("ldf.fill f10=%0" :: "m"(f6_15[4])); + __asm__ ("ldf.fill f11=%0" :: "m"(f6_15[5])); + __asm__ ("ldf.fill f12=%0" :: "m"(f6_15[6])); + __asm__ ("ldf.fill f13=%0" :: "m"(f6_15[7])); + __asm__ ("ldf.fill f14=%0" :: "m"(f6_15[8])); + __asm__ ("ldf.fill f15=%0" :: "m"(f6_15[9])); + regs->f6 = f6_15[0]; + regs->f7 = f6_15[1]; + regs->f8 = f6_15[2]; + regs->f9 = f6_15[3]; +#endif + return ret.status; +} + +/* + * Handle floating-point assist faults and traps. + */ +static int +handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr) +{ + long exception, bundle[2]; + unsigned long fault_ip; + static int fpu_swa_count = 0; + static unsigned long last_time; + + fault_ip = regs->cr_iip; + if (!fp_fault && (ia64_psr(regs)->ri == 0)) + fault_ip -= 16; + if (copy_from_user(bundle, (void *) fault_ip, sizeof(bundle))) + return -1; + + if (fpu_swa_count > 5 && jiffies - last_time > 5*HZ) + fpu_swa_count = 0; + if (++fpu_swa_count < 5) { + last_time = jiffies; + printk("%s(%d): floating-point assist fault at ip %016lx\n", + current->comm, current->pid, regs->cr_iip + ia64_psr(regs)->ri); + } + + exception = fp_emulate(fp_fault, bundle, ®s->cr_ipsr, ®s->ar_fpsr, &isr, ®s->pr, + ®s->cr_ifs, regs); + if (fp_fault) { + if (exception == 0) { + /* emulation was successful */ + ia64_increment_ip(regs); + } else if (exception == -1) { + printk("handle_fpu_swa: fp_emulate() returned -1\n"); + return -2; + } else { + /* is next instruction a trap? */ + if (exception & 2) { + ia64_increment_ip(regs); + } + return -1; + } + } else { + if (exception == -1) { + printk("handle_fpu_swa: fp_emulate() returned -1\n"); + return -2; + } else if (exception != 0) { + /* raise exception */ + return -1; + } + } + return 0; +} + +void +ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, + unsigned long iim, unsigned long itir, unsigned long arg5, + unsigned long arg6, unsigned long arg7, unsigned long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + unsigned long code, error = isr; + struct siginfo siginfo; + char buf[128]; + int result; + static const char *reason[] = { + "IA-64 Illegal Operation fault", + "IA-64 Privileged Operation fault", + "IA-64 Privileged Register fault", + "IA-64 Reserved Register/Field fault", + "Disabled Instruction Set Transition fault", + "Unknown fault 5", "Unknown fault 6", "Unknown fault 7", "Illegal Hazard fault", + "Unknown fault 9", "Unknown fault 10", "Unknown fault 11", "Unknown fault 12", + "Unknown fault 13", "Unknown fault 14", "Unknown fault 15" + }; + +#if 0 + /* this is for minimal trust debugging; yeah this kind of stuff is useful at times... */ + + if (vector != 25) { + static unsigned long last_time; + static char count; + unsigned long n = vector; + char buf[32], *cp; + + if (count > 5 && jiffies - last_time > 5*HZ) + count = 0; + + if (count++ < 5) { + last_time = jiffies; + cp = buf + sizeof(buf); + *--cp = '\0'; + while (n) { + *--cp = "0123456789abcdef"[n & 0xf]; + n >>= 4; + } + printk("<0x%s>", cp); + } + } +#endif + + switch (vector) { + case 24: /* General Exception */ + code = (isr >> 4) & 0xf; + sprintf(buf, "General Exception: %s%s", reason[code], + (code == 3) ? ((isr & (1UL << 37)) + ? " (RSE access)" : " (data access)") : ""); +#ifndef CONFIG_ITANIUM_ASTEP_SPECIFIC + if (code == 8) { +# ifdef CONFIG_IA64_PRINT_HAZARDS + printk("%016lx:possible hazard, pr = %016lx\n", regs->cr_iip, regs->pr); +# endif + return; + } +#endif + break; + + case 25: /* Disabled FP-Register */ + if (isr & 2) { + disabled_fph_fault(regs); + return; + } + sprintf(buf, "Disabled FPL fault---not supposed to happen!"); + break; + + case 29: /* Debug */ + case 35: /* Taken Branch Trap */ + case 36: /* Single Step Trap */ + switch (vector) { + case 29: siginfo.si_code = TRAP_BRKPT; break; + case 35: siginfo.si_code = TRAP_BRANCH; break; + case 36: siginfo.si_code = TRAP_TRACE; break; + } + siginfo.si_signo = SIGTRAP; + siginfo.si_errno = 0; + force_sig_info(SIGTRAP, &siginfo, current); + return; + + case 30: /* Unaligned fault */ + sprintf(buf, "Unaligned access in kernel mode---don't do this!"); + break; + + case 32: /* fp fault */ + case 33: /* fp trap */ + result = handle_fpu_swa((vector == 32) ? 1 : 0, regs, isr); + if (result < 0) { + siginfo.si_signo = SIGFPE; + siginfo.si_errno = 0; + siginfo.si_code = 0; /* XXX fix me */ + siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); + send_sig_info(SIGFPE, &siginfo, current); + if (result == -1) + send_sig_info(SIGFPE, &siginfo, current); + else + force_sig(SIGFPE, current); + } + return; + + case 34: /* Unimplemented Instruction Address Trap */ + if (user_mode(regs)) { + printk("Woah! Unimplemented Instruction Address Trap!\n"); + siginfo.si_code = ILL_BADIADDR; + siginfo.si_signo = SIGILL; + siginfo.si_errno = 0; + force_sig_info(SIGILL, &siginfo, current); + return; + } + sprintf(buf, "Unimplemented Instruction Address fault"); + break; + + case 45: + printk("Unexpected IA-32 exception\n"); + force_sig(SIGSEGV, current); + return; + + case 46: + printk("Unexpected IA-32 intercept trap\n"); + force_sig(SIGSEGV, current); + return; + + case 47: + sprintf(buf, "IA-32 Interruption Fault (int 0x%lx)", isr >> 16); + break; + + default: + sprintf(buf, "Fault %lu", vector); + break; + } + die_if_kernel(buf, regs, error); + force_sig(SIGILL, current); +} diff --git a/arch/ia64/kernel/unaligned.c b/arch/ia64/kernel/unaligned.c new file mode 100644 index 000000000..0bd213f6b --- /dev/null +++ b/arch/ia64/kernel/unaligned.c @@ -0,0 +1,1554 @@ +/* + * Architecture-specific unaligned trap handling. + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <asm/uaccess.h> +#include <asm/rse.h> +#include <asm/processor.h> +#include <asm/unaligned.h> + +extern void die_if_kernel(char *str, struct pt_regs *regs, long err) __attribute__ ((noreturn)); + +#undef DEBUG_UNALIGNED_TRAP + +#ifdef DEBUG_UNALIGNED_TRAP +#define DPRINT(a) { printk("%s, line %d: ", __FUNCTION__, __LINE__); printk a;} +#else +#define DPRINT(a) +#endif + +#define IA64_FIRST_STACKED_GR 32 +#define IA64_FIRST_ROTATING_FR 32 +#define SIGN_EXT9 __IA64_UL(0xffffffffffffff00) + +/* + * For M-unit: + * + * opcode | m | x6 | + * --------|------|---------| + * [40-37] | [36] | [35:30] | + * --------|------|---------| + * 4 | 1 | 6 | = 11 bits + * -------------------------- + * However bits [31:30] are not directly useful to distinguish between + * load/store so we can use [35:32] instead, which gives the following + * mask ([40:32]) using 9 bits. The 'e' comes from the fact that we defer + * checking the m-bit until later in the load/store emulation. + */ +#define IA64_OPCODE_MASK 0x1ef00000000 + +/* + * Table C-28 Integer Load/Store + * + * We ignore [35:32]= 0x6, 0x7, 0xE, 0xF + * + * ld8.fill, st8.fill MUST be aligned because the RNATs are based on + * the address (bits [8:3]), so we must failed. + */ +#define LD_OP 0x08000000000 +#define LDS_OP 0x08100000000 +#define LDA_OP 0x08200000000 +#define LDSA_OP 0x08300000000 +#define LDBIAS_OP 0x08400000000 +#define LDACQ_OP 0x08500000000 +/* 0x086, 0x087 are not relevant */ +#define LDCCLR_OP 0x08800000000 +#define LDCNC_OP 0x08900000000 +#define LDCCLRACQ_OP 0x08a00000000 +#define ST_OP 0x08c00000000 +#define STREL_OP 0x08d00000000 +/* 0x08e,0x8f are not relevant */ + +/* + * Table C-29 Integer Load +Reg + * + * we use the ld->m (bit [36:36]) field to determine whether or not we have + * a load/store of this form. + */ + +/* + * Table C-30 Integer Load/Store +Imm + * + * We ignore [35:32]= 0x6, 0x7, 0xE, 0xF + * + * ld8.fill, st8.fill must be aligned because the Nat register are based on + * the address, so we must fail and the program must be fixed. + */ +#define LD_IMM_OP 0x0a000000000 +#define LDS_IMM_OP 0x0a100000000 +#define LDA_IMM_OP 0x0a200000000 +#define LDSA_IMM_OP 0x0a300000000 +#define LDBIAS_IMM_OP 0x0a400000000 +#define LDACQ_IMM_OP 0x0a500000000 +/* 0x0a6, 0xa7 are not relevant */ +#define LDCCLR_IMM_OP 0x0a800000000 +#define LDCNC_IMM_OP 0x0a900000000 +#define LDCCLRACQ_IMM_OP 0x0aa00000000 +#define ST_IMM_OP 0x0ac00000000 +#define STREL_IMM_OP 0x0ad00000000 +/* 0x0ae,0xaf are not relevant */ + +/* + * Table C-32 Floating-point Load/Store + */ +#define LDF_OP 0x0c000000000 +#define LDFS_OP 0x0c100000000 +#define LDFA_OP 0x0c200000000 +#define LDFSA_OP 0x0c300000000 +/* 0x0c6 is irrelevant */ +#define LDFCCLR_OP 0x0c800000000 +#define LDFCNC_OP 0x0c900000000 +/* 0x0cb is irrelevant */ +#define STF_OP 0x0cc00000000 + +/* + * Table C-33 Floating-point Load +Reg + * + * we use the ld->m (bit [36:36]) field to determine whether or not we have + * a load/store of this form. + */ + +/* + * Table C-34 Floating-point Load/Store +Imm + */ +#define LDF_IMM_OP 0x0e000000000 +#define LDFS_IMM_OP 0x0e100000000 +#define LDFA_IMM_OP 0x0e200000000 +#define LDFSA_IMM_OP 0x0e300000000 +/* 0x0e6 is irrelevant */ +#define LDFCCLR_IMM_OP 0x0e800000000 +#define LDFCNC_IMM_OP 0x0e900000000 +#define STF_IMM_OP 0x0ec00000000 + +typedef struct { + unsigned long qp:6; /* [0:5] */ + unsigned long r1:7; /* [6:12] */ + unsigned long imm:7; /* [13:19] */ + unsigned long r3:7; /* [20:26] */ + unsigned long x:1; /* [27:27] */ + unsigned long hint:2; /* [28:29] */ + unsigned long x6_sz:2; /* [30:31] */ + unsigned long x6_op:4; /* [32:35], x6 = x6_sz|x6_op */ + unsigned long m:1; /* [36:36] */ + unsigned long op:4; /* [37:40] */ + unsigned long pad:23; /* [41:63] */ +} load_store_t; + + +typedef enum { + UPD_IMMEDIATE, /* ldXZ r1=[r3],imm(9) */ + UPD_REG /* ldXZ r1=[r3],r2 */ +} update_t; + +/* + * We use tables to keep track of the offsets of registers in the saved state. + * This way we save having big switch/case statements. + * + * We use bit 0 to indicate switch_stack or pt_regs. + * The offset is simply shifted by 1 bit. + * A 2-byte value should be enough to hold any kind of offset + * + * In case the calling convention changes (and thus pt_regs/switch_stack) + * simply use RSW instead of RPT or vice-versa. + */ + +#define RPO(x) ((size_t) &((struct pt_regs *)0)->x) +#define RSO(x) ((size_t) &((struct switch_stack *)0)->x) + +#define RPT(x) (RPO(x) << 1) +#define RSW(x) (1| RSO(x)<<1) + +#define GR_OFFS(x) (gr_info[x]>>1) +#define GR_IN_SW(x) (gr_info[x] & 0x1) + +#define FR_OFFS(x) (fr_info[x]>>1) +#define FR_IN_SW(x) (fr_info[x] & 0x1) + +static u16 gr_info[32]={ + 0, /* r0 is read-only : WE SHOULD NEVER GET THIS */ + + RPT(r1), RPT(r2), RPT(r3), + + RSW(r4), RSW(r5), RSW(r6), RSW(r7), + + RPT(r8), RPT(r9), RPT(r10), RPT(r11), + RPT(r12), RPT(r13), RPT(r14), RPT(r15), + + RPT(r16), RPT(r17), RPT(r18), RPT(r19), + RPT(r20), RPT(r21), RPT(r22), RPT(r23), + RPT(r24), RPT(r25), RPT(r26), RPT(r27), + RPT(r28), RPT(r29), RPT(r30), RPT(r31) +}; + +static u16 fr_info[32]={ + 0, /* constant : WE SHOULD NEVER GET THIS */ + 0, /* constant : WE SHOULD NEVER GET THIS */ + + RSW(f2), RSW(f3), RSW(f4), RSW(f5), + + RPT(f6), RPT(f7), RPT(f8), RPT(f9), + + RSW(f10), RSW(f11), RSW(f12), RSW(f13), RSW(f14), + RSW(f15), RSW(f16), RSW(f17), RSW(f18), RSW(f19), + RSW(f20), RSW(f21), RSW(f22), RSW(f23), RSW(f24), + RSW(f25), RSW(f26), RSW(f27), RSW(f28), RSW(f29), + RSW(f30), RSW(f31) +}; + +/* Invalidate ALAT entry for integer register REGNO. */ +static void +invala_gr (int regno) +{ +# define F(reg) case reg: __asm__ __volatile__ ("invala.e r%0" :: "i"(reg)); break + + switch (regno) { + F( 0); F( 1); F( 2); F( 3); F( 4); F( 5); F( 6); F( 7); + F( 8); F( 9); F( 10); F( 11); F( 12); F( 13); F( 14); F( 15); + F( 16); F( 17); F( 18); F( 19); F( 20); F( 21); F( 22); F( 23); + F( 24); F( 25); F( 26); F( 27); F( 28); F( 29); F( 30); F( 31); + F( 32); F( 33); F( 34); F( 35); F( 36); F( 37); F( 38); F( 39); + F( 40); F( 41); F( 42); F( 43); F( 44); F( 45); F( 46); F( 47); + F( 48); F( 49); F( 50); F( 51); F( 52); F( 53); F( 54); F( 55); + F( 56); F( 57); F( 58); F( 59); F( 60); F( 61); F( 62); F( 63); + F( 64); F( 65); F( 66); F( 67); F( 68); F( 69); F( 70); F( 71); + F( 72); F( 73); F( 74); F( 75); F( 76); F( 77); F( 78); F( 79); + F( 80); F( 81); F( 82); F( 83); F( 84); F( 85); F( 86); F( 87); + F( 88); F( 89); F( 90); F( 91); F( 92); F( 93); F( 94); F( 95); + F( 96); F( 97); F( 98); F( 99); F(100); F(101); F(102); F(103); + F(104); F(105); F(106); F(107); F(108); F(109); F(110); F(111); + F(112); F(113); F(114); F(115); F(116); F(117); F(118); F(119); + F(120); F(121); F(122); F(123); F(124); F(125); F(126); F(127); + } +# undef F +} + +/* Invalidate ALAT entry for floating-point register REGNO. */ +static void +invala_fr (int regno) +{ +# define F(reg) case reg: __asm__ __volatile__ ("invala.e f%0" :: "i"(reg)); break + + switch (regno) { + F( 0); F( 1); F( 2); F( 3); F( 4); F( 5); F( 6); F( 7); + F( 8); F( 9); F( 10); F( 11); F( 12); F( 13); F( 14); F( 15); + F( 16); F( 17); F( 18); F( 19); F( 20); F( 21); F( 22); F( 23); + F( 24); F( 25); F( 26); F( 27); F( 28); F( 29); F( 30); F( 31); + F( 32); F( 33); F( 34); F( 35); F( 36); F( 37); F( 38); F( 39); + F( 40); F( 41); F( 42); F( 43); F( 44); F( 45); F( 46); F( 47); + F( 48); F( 49); F( 50); F( 51); F( 52); F( 53); F( 54); F( 55); + F( 56); F( 57); F( 58); F( 59); F( 60); F( 61); F( 62); F( 63); + F( 64); F( 65); F( 66); F( 67); F( 68); F( 69); F( 70); F( 71); + F( 72); F( 73); F( 74); F( 75); F( 76); F( 77); F( 78); F( 79); + F( 80); F( 81); F( 82); F( 83); F( 84); F( 85); F( 86); F( 87); + F( 88); F( 89); F( 90); F( 91); F( 92); F( 93); F( 94); F( 95); + F( 96); F( 97); F( 98); F( 99); F(100); F(101); F(102); F(103); + F(104); F(105); F(106); F(107); F(108); F(109); F(110); F(111); + F(112); F(113); F(114); F(115); F(116); F(117); F(118); F(119); + F(120); F(121); F(122); F(123); F(124); F(125); F(126); F(127); + } +# undef F +} + +static void +set_rse_reg(struct pt_regs *regs, unsigned long r1, unsigned long val, int nat) +{ + struct switch_stack *sw = (struct switch_stack *)regs - 1; + unsigned long *kbs = ((unsigned long *)current) + IA64_RBS_OFFSET/8; + unsigned long on_kbs; + unsigned long *bsp, *bspstore, *addr, *ubs_end, *slot; + unsigned long rnats; + long nlocals; + + /* + * cr_ifs=[rv:ifm], ifm=[....:sof(6)] + * nlocal=number of locals (in+loc) register of the faulting function + */ + nlocals = (regs->cr_ifs) & 0x7f; + + DPRINT(("sw.bsptore=%lx pt.bspstore=%lx\n", sw->ar_bspstore, regs->ar_bspstore)); + DPRINT(("cr.ifs=%lx sof=%ld sol=%ld\n", + regs->cr_ifs, regs->cr_ifs &0x7f, (regs->cr_ifs>>7)&0x7f)); + + on_kbs = ia64_rse_num_regs(kbs, (unsigned long *)sw->ar_bspstore); + bspstore = (unsigned long *)regs->ar_bspstore; + + DPRINT(("rse_slot_num=0x%lx\n",ia64_rse_slot_num((unsigned long *)sw->ar_bspstore))); + DPRINT(("kbs=%p nlocals=%ld\n", kbs, nlocals)); + DPRINT(("bspstore next rnat slot %p\n", + ia64_rse_rnat_addr((unsigned long *)sw->ar_bspstore))); + DPRINT(("on_kbs=%ld rnats=%ld\n", + on_kbs, ((sw->ar_bspstore-(unsigned long)kbs)>>3) - on_kbs)); + + /* + * See get_rse_reg() for an explanation on the following instructions + */ + ubs_end = ia64_rse_skip_regs(bspstore, on_kbs); + bsp = ia64_rse_skip_regs(ubs_end, -nlocals); + addr = slot = ia64_rse_skip_regs(bsp, r1 - 32); + + DPRINT(("ubs_end=%p bsp=%p addr=%p slot=0x%lx\n", + ubs_end, bsp, addr, ia64_rse_slot_num(addr))); + + ia64_poke(regs, current, (unsigned long)addr, val); + + /* + * addr will now contain the address of the RNAT for the register + */ + addr = ia64_rse_rnat_addr(addr); + + ia64_peek(regs, current, (unsigned long)addr, &rnats); + DPRINT(("rnat @%p = 0x%lx nat=%d rnatval=%lx\n", + addr, rnats, nat, rnats &ia64_rse_slot_num(slot))); + + if ( nat ) { + rnats |= __IA64_UL(1) << ia64_rse_slot_num(slot); + } else { + rnats &= ~(__IA64_UL(1) << ia64_rse_slot_num(slot)); + } + ia64_poke(regs, current, (unsigned long)addr, rnats); + + DPRINT(("rnat changed to @%p = 0x%lx\n", addr, rnats)); +} + + +static void +get_rse_reg(struct pt_regs *regs, unsigned long r1, unsigned long *val, int *nat) +{ + struct switch_stack *sw = (struct switch_stack *)regs - 1; + unsigned long *kbs = (unsigned long *)current + IA64_RBS_OFFSET/8; + unsigned long on_kbs; + long nlocals; + unsigned long *bsp, *addr, *ubs_end, *slot, *bspstore; + unsigned long rnats; + + /* + * cr_ifs=[rv:ifm], ifm=[....:sof(6)] + * nlocals=number of local registers in the faulting function + */ + nlocals = (regs->cr_ifs) & 0x7f; + + /* + * save_switch_stack does a flushrs and saves bspstore. + * on_kbs = actual number of registers saved on kernel backing store + * (taking into accound potential RNATs) + * + * Note that this number can be greater than nlocals if the dirty + * parititions included more than one stack frame at the time we + * switched to KBS + */ + on_kbs = ia64_rse_num_regs(kbs, (unsigned long *)sw->ar_bspstore); + bspstore = (unsigned long *)regs->ar_bspstore; + + /* + * To simplify the logic, we calculate everything as if there was only + * one backing store i.e., the user one (UBS). We let it to peek/poke + * to figure out whether the register we're looking for really is + * on the UBS or on KBS. + * + * regs->ar_bsptore = address of last register saved on UBS (before switch) + * + * ubs_end = virtual end of the UBS (if everything had been spilled there) + * + * We know that ubs_end is the point where the last register on the + * stack frame we're interested in as been saved. So we need to walk + * our way backward to figure out what the BSP "was" for that frame, + * this will give us the location of r32. + * + * bsp = "virtual UBS" address of r32 for our frame + * + * Finally, get compute the address of the register we're looking for + * using bsp as our base (move up again). + * + * Please note that in our case, we know that the register is necessarily + * on the KBS because we are only interested in the current frame at the moment + * we got the exception i.e., bsp is not changed until we switch to KBS. + */ + ubs_end = ia64_rse_skip_regs(bspstore, on_kbs); + bsp = ia64_rse_skip_regs(ubs_end, -nlocals); + addr = slot = ia64_rse_skip_regs(bsp, r1 - 32); + + DPRINT(("ubs_end=%p bsp=%p addr=%p slot=0x%lx\n", + ubs_end, bsp, addr, ia64_rse_slot_num(addr))); + + ia64_peek(regs, current, (unsigned long)addr, val); + + /* + * addr will now contain the address of the RNAT for the register + */ + addr = ia64_rse_rnat_addr(addr); + + ia64_peek(regs, current, (unsigned long)addr, &rnats); + DPRINT(("rnat @%p = 0x%lx\n", addr, rnats)); + + if ( nat ) *nat = rnats >> ia64_rse_slot_num(slot) & 0x1; +} + + +static void +setreg(unsigned long regnum, unsigned long val, int nat, struct pt_regs *regs) +{ + struct switch_stack *sw = (struct switch_stack *)regs -1; + unsigned long addr; + unsigned long bitmask; + unsigned long *unat; + + + /* + * First takes care of stacked registers + */ + if ( regnum >= IA64_FIRST_STACKED_GR ) { + set_rse_reg(regs, regnum, val, nat); + return; + } + + /* + * Using r0 as a target raises a General Exception fault which has + * higher priority than the Unaligned Reference fault. + */ + + /* + * Now look at registers in [0-31] range and init correct UNAT + */ + if ( GR_IN_SW(regnum) ) { + addr = (unsigned long)sw; + unat = &sw->ar_unat; + } else { + addr = (unsigned long)regs; + unat = &sw->caller_unat; + } + DPRINT(("tmp_base=%lx switch_stack=%s offset=%d\n", + addr, unat==&sw->ar_unat ? "yes":"no", GR_OFFS(regnum))); + /* + * add offset from base of struct + * and do it ! + */ + addr += GR_OFFS(regnum); + + *(unsigned long *)addr = val; + + /* + * We need to clear the corresponding UNAT bit to fully emulate the load + * UNAT bit_pos = GR[r3]{8:3} form EAS-2.4 + */ + bitmask = __IA64_UL(1) << (addr >> 3 & 0x3f); + DPRINT(("*0x%lx=0x%lx NaT=%d prev_unat @%p=%lx\n", addr, val, nat, unat, *unat)); + if ( nat ) { + *unat |= bitmask; + } else { + *unat &= ~bitmask; + } + DPRINT(("*0x%lx=0x%lx NaT=%d new unat: %p=%lx\n", addr, val, nat, unat,*unat)); +} + +#define IA64_FPH_OFFS(r) (r - IA64_FIRST_ROTATING_FR) + +static void +setfpreg(unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs) +{ + struct switch_stack *sw = (struct switch_stack *)regs - 1; + unsigned long addr; + + /* + * From EAS-2.5: FPDisableFault has higher priority than + * Unaligned Fault. Thus, when we get here, we know the partition is + * enabled. + * + * The registers [32-127] are ususally saved in the tss. When get here, + * they are NECESSARY live because they are only saved explicitely. + * We have 3 ways of updating the values: force a save of the range + * in tss, use a gigantic switch/case statement or generate code on the + * fly to store to the right register. + * For now, we are using the (slow) save/restore way. + */ + if ( regnum >= IA64_FIRST_ROTATING_FR ) { + /* + * force a save of [32-127] to tss + * we use the __() form to avoid fiddling with the dfh bit + */ + __ia64_save_fpu(¤t->thread.fph[0]); + + current->thread.fph[IA64_FPH_OFFS(regnum)] = *fpval; + + __ia64_load_fpu(¤t->thread.fph[0]); + + /* + * mark the high partition as being used now + * + * This is REQUIRED because the disabled_fph_fault() does + * not set it, it's relying on the faulting instruction to + * do it. In our case the faulty instruction never gets executed + * completely, so we need to toggle the bit. + */ + regs->cr_ipsr |= IA64_PSR_MFH; + } else { + /* + * pt_regs or switch_stack ? + */ + if ( FR_IN_SW(regnum) ) { + addr = (unsigned long)sw; + } else { + addr = (unsigned long)regs; + } + + DPRINT(("tmp_base=%lx offset=%d\n", addr, FR_OFFS(regnum))); + + addr += FR_OFFS(regnum); + *(struct ia64_fpreg *)addr = *fpval; + + /* + * mark the low partition as being used now + * + * It is highly unlikely that this bit is not already set, but + * let's do it for safety. + */ + regs->cr_ipsr |= IA64_PSR_MFL; + + } +} + +/* + * Those 2 inline functions generate the spilled versions of the constant floating point + * registers which can be used with stfX + */ +static inline void +float_spill_f0(struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("stf.spill [%0]=f0" :: "r"(final) : "memory"); +} + +static inline void +float_spill_f1(struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("stf.spill [%0]=f1" :: "r"(final) : "memory"); +} + +static void +getfpreg(unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs) +{ + struct switch_stack *sw = (struct switch_stack *)regs -1; + unsigned long addr; + + /* + * From EAS-2.5: FPDisableFault has higher priority than + * Unaligned Fault. Thus, when we get here, we know the partition is + * enabled. + * + * When regnum > 31, the register is still live and + * we need to force a save to the tss to get access to it. + * See discussion in setfpreg() for reasons and other ways of doing this. + */ + if ( regnum >= IA64_FIRST_ROTATING_FR ) { + + /* + * force a save of [32-127] to tss + * we use the__ia64_save_fpu() form to avoid fiddling with + * the dfh bit. + */ + __ia64_save_fpu(¤t->thread.fph[0]); + + *fpval = current->thread.fph[IA64_FPH_OFFS(regnum)]; + } else { + /* + * f0 = 0.0, f1= 1.0. Those registers are constant and are thus + * not saved, we must generate their spilled form on the fly + */ + switch(regnum) { + case 0: + float_spill_f0(fpval); + break; + case 1: + float_spill_f1(fpval); + break; + default: + /* + * pt_regs or switch_stack ? + */ + addr = FR_IN_SW(regnum) ? (unsigned long)sw + : (unsigned long)regs; + + DPRINT(("is_sw=%d tmp_base=%lx offset=0x%x\n", + FR_IN_SW(regnum), addr, FR_OFFS(regnum))); + + addr += FR_OFFS(regnum); + *fpval = *(struct ia64_fpreg *)addr; + } + } +} + + +static void +getreg(unsigned long regnum, unsigned long *val, int *nat, struct pt_regs *regs) +{ + struct switch_stack *sw = (struct switch_stack *)regs -1; + unsigned long addr, *unat; + + if ( regnum >= IA64_FIRST_STACKED_GR ) { + get_rse_reg(regs, regnum, val, nat); + return; + } + + /* + * take care of r0 (read-only always evaluate to 0) + */ + if ( regnum == 0 ) { + *val = 0; + *nat = 0; + return; + } + + /* + * Now look at registers in [0-31] range and init correct UNAT + */ + if ( GR_IN_SW(regnum) ) { + addr = (unsigned long)sw; + unat = &sw->ar_unat; + } else { + addr = (unsigned long)regs; + unat = &sw->caller_unat; + } + + DPRINT(("addr_base=%lx offset=0x%x\n", addr, GR_OFFS(regnum))); + + addr += GR_OFFS(regnum); + + *val = *(unsigned long *)addr; + + /* + * do it only when requested + */ + if ( nat ) *nat = (*unat >> (addr >> 3 & 0x3f)) & 0x1UL; +} + +static void +emulate_load_updates(update_t type, load_store_t *ld, struct pt_regs *regs, unsigned long ifa) +{ + /* + * IMPORTANT: + * Given the way we handle unaligned speculative loads, we should + * not get to this point in the code but we keep this sanity check, + * just in case. + */ + if ( ld->x6_op == 1 || ld->x6_op == 3 ) { + printk(KERN_ERR __FUNCTION__": register update on speculative load, error\n"); + die_if_kernel("unaligned reference on specualtive load with register update\n", + regs, 30); + } + + + /* + * at this point, we know that the base register to update is valid i.e., + * it's not r0 + */ + if ( type == UPD_IMMEDIATE ) { + unsigned long imm; + + /* + * Load +Imm: ldXZ r1=[r3],imm(9) + * + * + * form imm9: [13:19] contain the first 7 bits + */ + imm = ld->x << 7 | ld->imm; + + /* + * sign extend (1+8bits) if m set + */ + if (ld->m) imm |= SIGN_EXT9; + + /* + * ifa == r3 and we know that the NaT bit on r3 was clear so + * we can directly use ifa. + */ + ifa += imm; + + setreg(ld->r3, ifa, 0, regs); + + DPRINT(("ld.x=%d ld.m=%d imm=%ld r3=0x%lx\n", ld->x, ld->m, imm, ifa)); + + } else if ( ld->m ) { + unsigned long r2; + int nat_r2; + + /* + * Load +Reg Opcode: ldXZ r1=[r3],r2 + * + * Note: that we update r3 even in the case of ldfX.a + * (where the load does not happen) + * + * The way the load algorithm works, we know that r3 does not + * have its NaT bit set (would have gotten NaT consumption + * before getting the unaligned fault). So we can use ifa + * which equals r3 at this point. + * + * IMPORTANT: + * The above statement holds ONLY because we know that we + * never reach this code when trying to do a ldX.s. + * If we ever make it to here on an ldfX.s then + */ + getreg(ld->imm, &r2, &nat_r2, regs); + + ifa += r2; + + /* + * propagate Nat r2 -> r3 + */ + setreg(ld->r3, ifa, nat_r2, regs); + + DPRINT(("imm=%d r2=%ld r3=0x%lx nat_r2=%d\n",ld->imm, r2, ifa, nat_r2)); + } +} + + +static int +emulate_load_int(unsigned long ifa, load_store_t *ld, struct pt_regs *regs) +{ + unsigned long val; + unsigned int len = 1<< ld->x6_sz; + + /* + * the macro supposes sequential access (which is the case) + * if the first byte is an invalid address we return here. Otherwise + * there is a guard page at the top of the user's address page and + * the first access would generate a NaT consumption fault and return + * with a SIGSEGV, which is what we want. + * + * Note: the first argument is ignored + */ + if ( access_ok(VERIFY_READ, (void *)ifa, len) < 0 ) { + DPRINT(("verify area failed on %lx\n", ifa)); + return -1; + } + + /* + * r0, as target, doesn't need to be checked because Illegal Instruction + * faults have higher priority than unaligned faults. + * + * r0 cannot be found as the base as it would never generate an + * unaligned reference. + */ + + /* + * ldX.a we don't try to emulate anything but we must + * invalidate the ALAT entry. + * See comment below for explanation on how we handle ldX.a + */ + if ( ld->x6_op != 0x2 ) { + /* + * we rely on the macros in unaligned.h for now i.e., + * we let the compiler figure out how to read memory gracefully. + * + * We need this switch/case because the way the inline function + * works. The code is optimized by the compiler and looks like + * a single switch/case. + */ + switch(len) { + case 2: + val = ia64_get_unaligned((void *)ifa, 2); + break; + case 4: + val = ia64_get_unaligned((void *)ifa, 4); + break; + case 8: + val = ia64_get_unaligned((void *)ifa, 8); + break; + default: + DPRINT(("unknown size: x6=%d\n", ld->x6_sz)); + return -1; + } + + setreg(ld->r1, val, 0, regs); + } + + /* + * check for updates on any kind of loads + */ + if ( ld->op == 0x5 || ld->m ) + emulate_load_updates(ld->op == 0x5 ? UPD_IMMEDIATE: UPD_REG, + ld, regs, ifa); + + /* + * handling of various loads (based on EAS2.4): + * + * ldX.acq (ordered load): + * - acquire semantics would have been used, so force fence instead. + * + * + * ldX.c.clr (check load and clear): + * - if we get to this handler, it's because the entry was not in the ALAT. + * Therefore the operation reverts to a normal load + * + * ldX.c.nc (check load no clear): + * - same as previous one + * + * ldX.c.clr.acq (ordered check load and clear): + * - same as above for c.clr part. The load needs to have acquire semantics. So + * we use the fence semantics which is stronger and thus ensures correctness. + * + * ldX.a (advanced load): + * - suppose ldX.a r1=[r3]. If we get to the unaligned trap it's because the + * address doesn't match requested size alignement. This means that we would + * possibly need more than one load to get the result. + * + * The load part can be handled just like a normal load, however the difficult + * part is to get the right thing into the ALAT. The critical piece of information + * in the base address of the load & size. To do that, a ld.a must be executed, + * clearly any address can be pushed into the table by using ld1.a r1=[r3]. Now + * if we use the same target register, we will be okay for the check.a instruction. + * If we look at the store, basically a stX [r3]=r1 checks the ALAT for any entry + * which would overlap within [r3,r3+X] (the size of the load was store in the + * ALAT). If such an entry is found the entry is invalidated. But this is not good + * enough, take the following example: + * r3=3 + * ld4.a r1=[r3] + * + * Could be emulated by doing: + * ld1.a r1=[r3],1 + * store to temporary; + * ld1.a r1=[r3],1 + * store & shift to temporary; + * ld1.a r1=[r3],1 + * store & shift to temporary; + * ld1.a r1=[r3] + * store & shift to temporary; + * r1=temporary + * + * So int this case, you would get the right value is r1 but the wrong info in + * the ALAT. Notice that you could do it in reverse to finish with address 3 + * but you would still get the size wrong. To get the size right, one needs to + * execute exactly the same kind of load. You could do it from a aligned + * temporary location, but you would get the address wrong. + * + * So no matter what, it is not possible to emulate an advanced load + * correctly. But is that really critical ? + * + * + * Now one has to look at how ld.a is used, one must either do a ld.c.* or + * chck.a.* to reuse the value stored in the ALAT. Both can "fail" (meaning no + * entry found in ALAT), and that's perfectly ok because: + * + * - ld.c.*, if the entry is not present a normal load is executed + * - chk.a.*, if the entry is not present, execution jumps to recovery code + * + * In either case, the load can be potentially retried in another form. + * + * So it's okay NOT to do any actual load on an unaligned ld.a. However the ALAT + * must be invalidated for the register (so that's chck.a.*,ld.c.* don't pick up + * a stale entry later) The register base update MUST also be performed. + * + * Now what is the content of the register and its NaT bit in the case we don't + * do the load ? EAS2.4, says (in case an actual load is needed) + * + * - r1 = [r3], Nat = 0 if succeeds + * - r1 = 0 Nat = 0 if trying to access non-speculative memory + * + * For us, there is nothing to do, because both ld.c.* and chk.a.* are going to + * retry and thus eventually reload the register thereby changing Nat and + * register content. + */ + + /* + * when the load has the .acq completer then + * use ordering fence. + */ + if (ld->x6_op == 0x5 || ld->x6_op == 0xa) + mb(); + + /* + * invalidate ALAT entry in case of advanced load + */ + if (ld->x6_op == 0x2) + invala_gr(ld->r1); + + return 0; +} + +static int +emulate_store_int(unsigned long ifa, load_store_t *ld, struct pt_regs *regs) +{ + unsigned long r2; + unsigned int len = 1<< ld->x6_sz; + + /* + * the macro supposes sequential access (which is the case) + * if the first byte is an invalid address we return here. Otherwise + * there is a guard page at the top of the user's address page and + * the first access would generate a NaT consumption fault and return + * with a SIGSEGV, which is what we want. + * + * Note: the first argument is ignored + */ + if ( access_ok(VERIFY_WRITE, (void *)ifa, len) < 0 ) { + DPRINT(("verify area failed on %lx\n",ifa)); + return -1; + } + + /* + * if we get to this handler, Nat bits on both r3 and r2 have already + * been checked. so we don't need to do it + * + * extract the value to be stored + */ + getreg(ld->imm, &r2, 0, regs); + + /* + * we rely on the macros in unaligned.h for now i.e., + * we let the compiler figure out how to read memory gracefully. + * + * We need this switch/case because the way the inline function + * works. The code is optimized by the compiler and looks like + * a single switch/case. + */ + DPRINT(("st%d [%lx]=%lx\n", len, ifa, r2)); + + switch(len) { + case 2: + ia64_put_unaligned(r2, (void *)ifa, 2); + break; + case 4: + ia64_put_unaligned(r2, (void *)ifa, 4); + break; + case 8: + ia64_put_unaligned(r2, (void *)ifa, 8); + break; + default: + DPRINT(("unknown size: x6=%d\n", ld->x6_sz)); + return -1; + } + /* + * stX [r3]=r2,imm(9) + * + * NOTE: + * ld->r3 can never be r0, because r0 would not generate an + * unaligned access. + */ + if ( ld->op == 0x5 ) { + unsigned long imm; + + /* + * form imm9: [12:6] contain first 7bits + */ + imm = ld->x << 7 | ld->r1; + /* + * sign extend (8bits) if m set + */ + if ( ld->m ) imm |= SIGN_EXT9; + /* + * ifa == r3 (NaT is necessarily cleared) + */ + ifa += imm; + + DPRINT(("imm=%lx r3=%lx\n", imm, ifa)); + + setreg(ld->r3, ifa, 0, regs); + } + /* + * we don't have alat_invalidate_multiple() so we need + * to do the complete flush :-<< + */ + ia64_invala(); + + /* + * stX.rel: use fence instead of release + */ + if ( ld->x6_op == 0xd ) mb(); + + return 0; +} + +/* + * floating point operations sizes in bytes + */ +static const unsigned short float_fsz[4]={ + 16, /* extended precision (e) */ + 8, /* integer (8) */ + 4, /* single precision (s) */ + 8 /* double precision (d) */ +}; + +static inline void +mem2float_extended(struct ia64_fpreg *init, struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("ldfe f6=[%0];; stf.spill [%1]=f6" + :: "r"(init), "r"(final) : "f6","memory"); +} + +static inline void +mem2float_integer(struct ia64_fpreg *init, struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("ldf8 f6=[%0];; stf.spill [%1]=f6" + :: "r"(init), "r"(final) : "f6","memory"); +} + +static inline void +mem2float_single(struct ia64_fpreg *init, struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("ldfs f6=[%0];; stf.spill [%1]=f6" + :: "r"(init), "r"(final) : "f6","memory"); +} + +static inline void +mem2float_double(struct ia64_fpreg *init, struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("ldfd f6=[%0];; stf.spill [%1]=f6" + :: "r"(init), "r"(final) : "f6","memory"); +} + +static inline void +float2mem_extended(struct ia64_fpreg *init, struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("ldf.fill f6=[%0];; stfe [%1]=f6" + :: "r"(init), "r"(final) : "f6","memory"); +} + +static inline void +float2mem_integer(struct ia64_fpreg *init, struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("ldf.fill f6=[%0];; stf8 [%1]=f6" + :: "r"(init), "r"(final) : "f6","memory"); +} + +static inline void +float2mem_single(struct ia64_fpreg *init, struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("ldf.fill f6=[%0];; stfs [%1]=f6" + :: "r"(init), "r"(final) : "f6","memory"); +} + +static inline void +float2mem_double(struct ia64_fpreg *init, struct ia64_fpreg *final) +{ + __asm__ __volatile__ ("ldf.fill f6=[%0];; stfd [%1]=f6" + :: "r"(init), "r"(final) : "f6","memory"); +} + +static int +emulate_load_floatpair(unsigned long ifa, load_store_t *ld, struct pt_regs *regs) +{ + struct ia64_fpreg fpr_init[2]; + struct ia64_fpreg fpr_final[2]; + unsigned long len = float_fsz[ld->x6_sz]; + + if ( access_ok(VERIFY_READ, (void *)ifa, len<<1) < 0 ) { + DPRINT(("verify area failed on %lx\n", ifa)); + return -1; + } + /* + * fr0 & fr1 don't need to be checked because Illegal Instruction + * faults have higher priority than unaligned faults. + * + * r0 cannot be found as the base as it would never generate an + * unaligned reference. + */ + + /* + * make sure we get clean buffers + */ + memset(&fpr_init,0, sizeof(fpr_init)); + memset(&fpr_final,0, sizeof(fpr_final)); + + /* + * ldfpX.a: we don't try to emulate anything but we must + * invalidate the ALAT entry and execute updates, if any. + */ + if ( ld->x6_op != 0x2 ) { + /* + * does the unaligned access + */ + memcpy(&fpr_init[0], (void *)ifa, len); + memcpy(&fpr_init[1], (void *)(ifa+len), len); + + DPRINT(("ld.r1=%d ld.imm=%d x6_sz=%d\n", ld->r1, ld->imm, ld->x6_sz)); +#ifdef DEBUG_UNALIGNED_TRAP + { int i; char *c = (char *)&fpr_init; + printk("fpr_init= "); + for(i=0; i < len<<1; i++ ) { + printk("%02x ", c[i]&0xff); + } + printk("\n"); + } +#endif + /* + * XXX fixme + * Could optimize inlines by using ldfpX & 2 spills + */ + switch( ld->x6_sz ) { + case 0: + mem2float_extended(&fpr_init[0], &fpr_final[0]); + mem2float_extended(&fpr_init[1], &fpr_final[1]); + break; + case 1: + mem2float_integer(&fpr_init[0], &fpr_final[0]); + mem2float_integer(&fpr_init[1], &fpr_final[1]); + break; + case 2: + mem2float_single(&fpr_init[0], &fpr_final[0]); + mem2float_single(&fpr_init[1], &fpr_final[1]); + break; + case 3: + mem2float_double(&fpr_init[0], &fpr_final[0]); + mem2float_double(&fpr_init[1], &fpr_final[1]); + break; + } +#ifdef DEBUG_UNALIGNED_TRAP + { int i; char *c = (char *)&fpr_final; + printk("fpr_final= "); + for(i=0; i < len<<1; i++ ) { + printk("%02x ", c[i]&0xff); + } + printk("\n"); + } +#endif + /* + * XXX fixme + * + * A possible optimization would be to drop fpr_final + * and directly use the storage from the saved context i.e., + * the actual final destination (pt_regs, switch_stack or tss). + */ + setfpreg(ld->r1, &fpr_final[0], regs); + setfpreg(ld->imm, &fpr_final[1], regs); + } + + /* + * Check for updates: only immediate updates are available for this + * instruction. + */ + if ( ld->m ) { + + /* + * the immediate is implicit given the ldsz of the operation: + * single: 8 (2x4) and for all others it's 16 (2x8) + */ + ifa += len<<1; + + /* + * IMPORTANT: + * the fact that we force the NaT of r3 to zero is ONLY valid + * as long as we don't come here with a ldfpX.s. + * For this reason we keep this sanity check + */ + if ( ld->x6_op == 1 || ld->x6_op == 3 ) { + printk(KERN_ERR "%s: register update on speculative load pair, error\n", __FUNCTION__); + } + + + setreg(ld->r3, ifa, 0, regs); + } + + /* + * Invalidate ALAT entries, if any, for both registers. + */ + if ( ld->x6_op == 0x2 ) { + invala_fr(ld->r1); + invala_fr(ld->imm); + } + return 0; +} + + +static int +emulate_load_float(unsigned long ifa, load_store_t *ld, struct pt_regs *regs) +{ + struct ia64_fpreg fpr_init; + struct ia64_fpreg fpr_final; + unsigned long len = float_fsz[ld->x6_sz]; + + /* + * check for load pair because our masking scheme is not fine grain enough + if ( ld->x == 1 ) return emulate_load_floatpair(ifa,ld,regs); + */ + + if ( access_ok(VERIFY_READ, (void *)ifa, len) < 0 ) { + DPRINT(("verify area failed on %lx\n", ifa)); + return -1; + } + /* + * fr0 & fr1 don't need to be checked because Illegal Instruction + * faults have higher priority than unaligned faults. + * + * r0 cannot be found as the base as it would never generate an + * unaligned reference. + */ + + + /* + * make sure we get clean buffers + */ + memset(&fpr_init,0, sizeof(fpr_init)); + memset(&fpr_final,0, sizeof(fpr_final)); + + /* + * ldfX.a we don't try to emulate anything but we must + * invalidate the ALAT entry. + * See comments in ldX for descriptions on how the various loads are handled. + */ + if ( ld->x6_op != 0x2 ) { + + /* + * does the unaligned access + */ + memcpy(&fpr_init, (void *)ifa, len); + + DPRINT(("ld.r1=%d x6_sz=%d\n", ld->r1, ld->x6_sz)); +#ifdef DEBUG_UNALIGNED_TRAP + { int i; char *c = (char *)&fpr_init; + printk("fpr_init= "); + for(i=0; i < len; i++ ) { + printk("%02x ", c[i]&0xff); + } + printk("\n"); + } +#endif + /* + * we only do something for x6_op={0,8,9} + */ + switch( ld->x6_sz ) { + case 0: + mem2float_extended(&fpr_init, &fpr_final); + break; + case 1: + mem2float_integer(&fpr_init, &fpr_final); + break; + case 2: + mem2float_single(&fpr_init, &fpr_final); + break; + case 3: + mem2float_double(&fpr_init, &fpr_final); + break; + } +#ifdef DEBUG_UNALIGNED_TRAP + { int i; char *c = (char *)&fpr_final; + printk("fpr_final= "); + for(i=0; i < len; i++ ) { + printk("%02x ", c[i]&0xff); + } + printk("\n"); + } +#endif + /* + * XXX fixme + * + * A possible optimization would be to drop fpr_final + * and directly use the storage from the saved context i.e., + * the actual final destination (pt_regs, switch_stack or tss). + */ + setfpreg(ld->r1, &fpr_final, regs); + } + + /* + * check for updates on any loads + */ + if ( ld->op == 0x7 || ld->m ) + emulate_load_updates(ld->op == 0x7 ? UPD_IMMEDIATE: UPD_REG, + ld, regs, ifa); + + + /* + * invalidate ALAT entry in case of advanced floating point loads + */ + if (ld->x6_op == 0x2) + invala_fr(ld->r1); + + return 0; +} + + +static int +emulate_store_float(unsigned long ifa, load_store_t *ld, struct pt_regs *regs) +{ + struct ia64_fpreg fpr_init; + struct ia64_fpreg fpr_final; + unsigned long len = float_fsz[ld->x6_sz]; + + /* + * the macro supposes sequential access (which is the case) + * if the first byte is an invalid address we return here. Otherwise + * there is a guard page at the top of the user's address page and + * the first access would generate a NaT consumption fault and return + * with a SIGSEGV, which is what we want. + * + * Note: the first argument is ignored + */ + if ( access_ok(VERIFY_WRITE, (void *)ifa, len) < 0 ) { + DPRINT(("verify area failed on %lx\n",ifa)); + return -1; + } + + /* + * make sure we get clean buffers + */ + memset(&fpr_init,0, sizeof(fpr_init)); + memset(&fpr_final,0, sizeof(fpr_final)); + + + /* + * if we get to this handler, Nat bits on both r3 and r2 have already + * been checked. so we don't need to do it + * + * extract the value to be stored + */ + getfpreg(ld->imm, &fpr_init, regs); + /* + * during this step, we extract the spilled registers from the saved + * context i.e., we refill. Then we store (no spill) to temporary + * aligned location + */ + switch( ld->x6_sz ) { + case 0: + float2mem_extended(&fpr_init, &fpr_final); + break; + case 1: + float2mem_integer(&fpr_init, &fpr_final); + break; + case 2: + float2mem_single(&fpr_init, &fpr_final); + break; + case 3: + float2mem_double(&fpr_init, &fpr_final); + break; + } + DPRINT(("ld.r1=%d x6_sz=%d\n", ld->r1, ld->x6_sz)); +#ifdef DEBUG_UNALIGNED_TRAP + { int i; char *c = (char *)&fpr_init; + printk("fpr_init= "); + for(i=0; i < len; i++ ) { + printk("%02x ", c[i]&0xff); + } + printk("\n"); + } + { int i; char *c = (char *)&fpr_final; + printk("fpr_final= "); + for(i=0; i < len; i++ ) { + printk("%02x ", c[i]&0xff); + } + printk("\n"); + } +#endif + + /* + * does the unaligned store + */ + memcpy((void *)ifa, &fpr_final, len); + + /* + * stfX [r3]=r2,imm(9) + * + * NOTE: + * ld->r3 can never be r0, because r0 would not generate an + * unaligned access. + */ + if ( ld->op == 0x7 ) { + unsigned long imm; + + /* + * form imm9: [12:6] contain first 7bits + */ + imm = ld->x << 7 | ld->r1; + /* + * sign extend (8bits) if m set + */ + if ( ld->m ) imm |= SIGN_EXT9; + /* + * ifa == r3 (NaT is necessarily cleared) + */ + ifa += imm; + + DPRINT(("imm=%lx r3=%lx\n", imm, ifa)); + + setreg(ld->r3, ifa, 0, regs); + } + /* + * we don't have alat_invalidate_multiple() so we need + * to do the complete flush :-<< + */ + ia64_invala(); + + return 0; +} + +void +ia64_handle_unaligned(unsigned long ifa, struct pt_regs *regs) +{ + static unsigned long unalign_count; + static long last_time; + + struct ia64_psr *ipsr = ia64_psr(regs); + unsigned long *bundle_addr; + unsigned long opcode; + unsigned long op; + load_store_t *insn; + int ret = -1; + + /* + * We flag unaligned references while in kernel as + * errors: the kernel must be fixed. The switch code + * is in ivt.S at entry 30. + * + * So here we keep a simple sanity check. + */ + if ( !user_mode(regs) ) { + die_if_kernel("Unaligned reference while in kernel\n", regs, 30); + /* NOT_REACHED */ + } + + /* + * Make sure we log the unaligned access, so that user/sysadmin can notice it + * and eventually fix the program. + * + * We don't want to do that for every access so we pace it with jiffies. + */ + if ( unalign_count > 5 && jiffies - last_time > 5*HZ ) unalign_count = 0; + if ( ++unalign_count < 5 ) { + last_time = jiffies; + printk("%s(%d): unaligned trap accessing %016lx (ip=%016lx)\n", + current->comm, current->pid, ifa, regs->cr_iip + ipsr->ri); + + } + + DPRINT(("iip=%lx ifa=%lx isr=%lx\n", regs->cr_iip, ifa, regs->cr_ipsr)); + DPRINT(("ISR.ei=%d ISR.sp=%d\n", ipsr->ri, ipsr->it)); + + bundle_addr = (unsigned long *)(regs->cr_iip); + + /* + * extract the instruction from the bundle given the slot number + */ + switch ( ipsr->ri ) { + case 0: op = *bundle_addr >> 5; + break; + + case 1: op = *bundle_addr >> 46 | (*(bundle_addr+1) & 0x7fffff)<<18; + break; + + case 2: op = *(bundle_addr+1) >> 23; + break; + } + + insn = (load_store_t *)&op; + opcode = op & IA64_OPCODE_MASK; + + DPRINT(("opcode=%lx ld.qp=%d ld.r1=%d ld.imm=%d ld.r3=%d ld.x=%d ld.hint=%d " + "ld.x6=0x%x ld.m=%d ld.op=%d\n", + opcode, + insn->qp, + insn->r1, + insn->imm, + insn->r3, + insn->x, + insn->hint, + insn->x6_sz, + insn->m, + insn->op)); + + /* + * IMPORTANT: + * Notice that the swictch statement DOES not cover all possible instructions + * that DO generate unaligned references. This is made on purpose because for some + * instructions it DOES NOT make sense to try and emulate the access. Sometimes it + * is WRONG to try and emulate. Here is a list of instruction we don't emulate i.e., + * the program will get a signal and die: + * + * load/store: + * - ldX.spill + * - stX.spill + * Reason: RNATs are based on addresses + * + * synchronization: + * - cmpxchg + * - fetchadd + * - xchg + * Reason: ATOMIC operations cannot be emulated properly using multiple + * instructions. + * + * speculative loads: + * - ldX.sZ + * Reason: side effects, code must be ready to deal with failure so simpler + * to let the load fail. + * --------------------------------------------------------------------------------- + * XXX fixme + * + * I would like to get rid of this switch case and do something + * more elegant. + */ + switch(opcode) { + case LDS_OP: + case LDSA_OP: + case LDS_IMM_OP: + case LDSA_IMM_OP: + case LDFS_OP: + case LDFSA_OP: + case LDFS_IMM_OP: + /* + * The instruction will be retried with defered exceptions + * turned on, and we should get Nat bit installed + * + * IMPORTANT: + * When PSR_ED is set, the register & immediate update + * forms are actually executed even though the operation + * failed. So we don't need to take care of this. + */ + DPRINT(("forcing PSR_ED\n")); + regs->cr_ipsr |= IA64_PSR_ED; + return; + + case LD_OP: + case LDA_OP: + case LDBIAS_OP: + case LDACQ_OP: + case LDCCLR_OP: + case LDCNC_OP: + case LDCCLRACQ_OP: + case LD_IMM_OP: + case LDA_IMM_OP: + case LDBIAS_IMM_OP: + case LDACQ_IMM_OP: + case LDCCLR_IMM_OP: + case LDCNC_IMM_OP: + case LDCCLRACQ_IMM_OP: + ret = emulate_load_int(ifa, insn, regs); + break; + case ST_OP: + case STREL_OP: + case ST_IMM_OP: + case STREL_IMM_OP: + ret = emulate_store_int(ifa, insn, regs); + break; + case LDF_OP: + case LDFA_OP: + case LDFCCLR_OP: + case LDFCNC_OP: + case LDF_IMM_OP: + case LDFA_IMM_OP: + case LDFCCLR_IMM_OP: + case LDFCNC_IMM_OP: + ret = insn->x ? + emulate_load_floatpair(ifa, insn, regs): + emulate_load_float(ifa, insn, regs); + break; + case STF_OP: + case STF_IMM_OP: + ret = emulate_store_float(ifa, insn, regs); + } + + DPRINT(("ret=%d\n", ret)); + if ( ret ) { + lock_kernel(); + force_sig(SIGSEGV, current); + unlock_kernel(); + } else { + /* + * given today's architecture this case is not likely to happen + * because a memory access instruction (M) can never be in the + * last slot of a bundle. But let's keep it for now. + */ + if ( ipsr->ri == 2 ) regs->cr_iip += 16; + ipsr->ri = ++ipsr->ri & 3; + } + + DPRINT(("ipsr->ri=%d iip=%lx\n", ipsr->ri, regs->cr_iip)); +} diff --git a/arch/ia64/kernel/unwind.c b/arch/ia64/kernel/unwind.c new file mode 100644 index 000000000..c2b772e68 --- /dev/null +++ b/arch/ia64/kernel/unwind.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#include <linux/kernel.h> +#include <linux/sched.h> + +#include <asm/unwind.h> + +void +ia64_unwind_init_from_blocked_task (struct ia64_frame_info *info, struct task_struct *t) +{ + struct switch_stack *sw = (struct switch_stack *) (t->thread.ksp + 16); + unsigned long sol, limit, top; + + memset(info, 0, sizeof(*info)); + + sol = (sw->ar_pfs >> 7) & 0x7f; /* size of locals */ + + limit = (unsigned long) t + IA64_RBS_OFFSET; + top = sw->ar_bspstore; + if (top - (unsigned long) t >= IA64_STK_OFFSET) + top = limit; + + info->regstk.limit = (unsigned long *) limit; + info->regstk.top = (unsigned long *) top; + info->bsp = ia64_rse_skip_regs(info->regstk.top, -sol); + info->top_rnat = sw->ar_rnat; + info->cfm = sw->ar_pfs; + info->ip = sw->b0; +} + +void +ia64_unwind_init_from_current (struct ia64_frame_info *info, struct pt_regs *regs) +{ + struct switch_stack *sw = (struct switch_stack *) regs - 1; + unsigned long sol, sof, *bsp, limit, top; + + limit = (unsigned long) current + IA64_RBS_OFFSET; + top = sw->ar_bspstore; + if (top - (unsigned long) current >= IA64_STK_OFFSET) + top = limit; + + memset(info, 0, sizeof(*info)); + + sol = (sw->ar_pfs >> 7) & 0x7f; /* size of frame */ + info->regstk.limit = (unsigned long *) limit; + info->regstk.top = (unsigned long *) top; + info->top_rnat = sw->ar_rnat; + + /* this gives us the bsp top level frame (kdb interrupt frame): */ + bsp = ia64_rse_skip_regs((unsigned long *) top, -sol); + + /* now skip past the interrupt frame: */ + sof = regs->cr_ifs & 0x7f; /* size of frame */ + info->cfm = regs->cr_ifs; + info->bsp = ia64_rse_skip_regs(bsp, -sof); + info->ip = regs->cr_iip; +} + +static unsigned long +read_reg (struct ia64_frame_info *info, int regnum, int *is_nat) +{ + unsigned long *addr, *rnat_addr, rnat; + + addr = ia64_rse_skip_regs(info->bsp, regnum); + if (addr < info->regstk.limit || addr >= info->regstk.top || ((long) addr & 0x7) != 0) { + *is_nat = 1; + return 0xdeadbeefdeadbeef; + } + rnat_addr = ia64_rse_rnat_addr(addr); + + if (rnat_addr >= info->regstk.top) + rnat = info->top_rnat; + else + rnat = *rnat_addr; + *is_nat = (rnat & (1UL << ia64_rse_slot_num(addr))) != 0; + return *addr; +} + +/* + * On entry, info->regstk.top should point to the register backing + * store for r32. + */ +int +ia64_unwind_to_previous_frame (struct ia64_frame_info *info) +{ + unsigned long sol, cfm = info->cfm; + int is_nat; + + sol = (cfm >> 7) & 0x7f; /* size of locals */ + + /* + * In general, we would have to make use of unwind info to + * unwind an IA-64 stack, but for now gcc uses a special + * convention that makes this possible without full-fledged + * unwindo info. Specifically, we expect "rp" in the second + * last, and "ar.pfs" in the last local register, so the + * number of locals in a frame must be at least two. If it's + * less than that, we reached the end of the C call stack. + */ + if (sol < 2) + return -1; + + info->ip = read_reg(info, sol - 2, &is_nat); + if (is_nat) + return -1; + + cfm = read_reg(info, sol - 1, &is_nat); + if (is_nat) + return -1; + + sol = (cfm >> 7) & 0x7f; + + info->cfm = cfm; + info->bsp = ia64_rse_skip_regs(info->bsp, -sol); + return 0; +} diff --git a/arch/ia64/lib/Makefile b/arch/ia64/lib/Makefile new file mode 100644 index 000000000..8a9581747 --- /dev/null +++ b/arch/ia64/lib/Makefile @@ -0,0 +1,42 @@ +# +# Makefile for ia64-specific library routines.. +# + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $@ + +OBJS = __divdi3.o __divsi3.o __udivdi3.o __udivsi3.o \ + __moddi3.o __modsi3.o __umoddi3.o __umodsi3.o \ + checksum.o clear_page.o csum_partial_copy.o copy_page.o \ + copy_user.o clear_user.o memset.o strncpy_from_user.o \ + strlen.o strlen_user.o strnlen_user.o \ + flush.o do_csum.o + +lib.a: $(OBJS) + $(AR) rcs lib.a $(OBJS) + +__divdi3.o: idiv.S + $(CC) $(AFLAGS) -c -o $@ $< + +__divsi3.o: idiv.S + $(CC) $(AFLAGS) -c -DSINGLE -c -o $@ $< + +__udivdi3.o: idiv.S + $(CC) $(AFLAGS) -c -DUNSIGNED -c -o $@ $< + +__udivsi3.o: idiv.S + $(CC) $(AFLAGS) -c -DUNSIGNED -DSINGLE -c -o $@ $< + +__moddi3.o: idiv.S + $(CC) $(AFLAGS) -c -DMODULO -c -o $@ $< + +__modsi3.o: idiv.S + $(CC) $(AFLAGS) -c -DMODULO -DSINGLE -c -o $@ $< + +__umoddi3.o: idiv.S + $(CC) $(AFLAGS) -c -DMODULO -DUNSIGNED -c -o $@ $< + +__umodsi3.o: idiv.S + $(CC) $(AFLAGS) -c -DMODULO -DUNSIGNED -DSINGLE -c -o $@ $< + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/lib/checksum.c b/arch/ia64/lib/checksum.c new file mode 100644 index 000000000..9c4a8af75 --- /dev/null +++ b/arch/ia64/lib/checksum.c @@ -0,0 +1,110 @@ +/* + * Network checksum routines + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * + * Most of the code coming from arch/alpha/lib/checksum.c + * + * This file contains network checksum routines that are better done + * in an architecture-specific manner due to speed.. + */ + +#include <linux/string.h> + +#include <asm/byteorder.h> + +static inline unsigned short +from64to16(unsigned long x) +{ + /* add up 32-bit words for 33 bits */ + x = (x & 0xffffffff) + (x >> 32); + /* add up 16-bit and 17-bit words for 17+c bits */ + x = (x & 0xffff) + (x >> 16); + /* add up 16-bit and 2-bit for 16+c bit */ + x = (x & 0xffff) + (x >> 16); + /* add up carry.. */ + x = (x & 0xffff) + (x >> 16); + return x; +} + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented. + */ +unsigned short int csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + return ~from64to16(saddr + daddr + sum + + ((unsigned long) ntohs(len) << 16) + + ((unsigned long) proto << 8)); +} + +unsigned int csum_tcpudp_nofold(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + unsigned long result; + + result = (saddr + daddr + sum + + ((unsigned long) ntohs(len) << 16) + + ((unsigned long) proto << 8)); + + /* Fold down to 32-bits so we don't loose in the typedef-less + network stack. */ + /* 64 to 33 */ + result = (result & 0xffffffff) + (result >> 32); + /* 33 to 32 */ + result = (result & 0xffffffff) + (result >> 32); + return result; +} + +extern unsigned long do_csum(const unsigned char *, unsigned int, unsigned int); +extern unsigned long do_csum_c(const unsigned char *, unsigned int, unsigned int); + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + */ +unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) +{ + return ~do_csum(iph,ihl*4,0); +} + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) +{ + unsigned long result = do_csum(buff, len, 0); + + /* add in old sum, and carry.. */ + result += sum; + /* 32+c bits -> 32 bits */ + result = (result & 0xffffffff) + (result >> 32); + return result; +} + + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ +unsigned short ip_compute_csum(unsigned char * buff, int len) +{ + return ~do_csum(buff,len, 0); +} diff --git a/arch/ia64/lib/clear_page.S b/arch/ia64/lib/clear_page.S new file mode 100644 index 000000000..314311c5c --- /dev/null +++ b/arch/ia64/lib/clear_page.S @@ -0,0 +1,42 @@ +/* + * + * Optimized version of the standard clearpage() function + * + * Based on comments from ddd. Try not to overflow the write buffer. + * + * Inputs: + * in0: address of page + * + * Output: + * none + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#include <asm/page.h> + + .text + .psr abi64 + .psr lsb + .lsb + + .align 32 + .global clear_page + .proc clear_page +clear_page: + alloc r11=ar.pfs,1,0,0,0 + mov r16=ar.lc // slow + mov r17=PAGE_SIZE/32-1 // -1 = repeat/until + ;; + adds r18=16,in0 + mov ar.lc=r17 + ;; +1: stf.spill.nta [in0]=f0,32 + stf.spill.nta [r18]=f0,32 + br.cloop.dptk.few 1b + ;; + mov ar.lc=r16 // restore lc + br.ret.sptk.few rp + + .endp clear_page diff --git a/arch/ia64/lib/clear_user.S b/arch/ia64/lib/clear_user.S new file mode 100644 index 000000000..0db4a78f8 --- /dev/null +++ b/arch/ia64/lib/clear_user.S @@ -0,0 +1,224 @@ +/* + * This routine clears to zero a linear memory buffer in user space. + * + * Inputs: + * in0: address of buffer + * in1: length of buffer in bytes + * Outputs: + * r8: number of bytes that didn't get cleared due to a fault + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + */ + +// +// arguments +// +#define buf r32 +#define len r33 + +// +// local registers +// +#define cnt r16 +#define buf2 r17 +#define saved_lc r18 +#define saved_pr r19 +#define saved_pfs r20 +#define tmp r21 +#define len2 r22 +#define len3 r23 + +// +// Theory of operations: +// - we check whether or not the buffer is small, i.e., less than 17 +// in which case we do the byte by byte loop. +// +// - Otherwise we go progressively from 1 byte store to 8byte store in +// the head part, the body is a 16byte store loop and we finish we the +// tail for the last 15 bytes. +// The good point about this breakdown is that the long buffer handling +// contains only 2 branches. +// +// The reason for not using shifting & masking for both the head and the +// tail is to stay semantically correct. This routine is not supposed +// to write bytes outside of the buffer. While most of the time this would +// be ok, we can't tolerate a mistake. A classical example is the case +// of multithreaded code were to the extra bytes touched is actually owned +// by another thread which runs concurrently to ours. Another, less likely, +// example is with device drivers where reading an I/O mapped location may +// have side effects (same thing for writing). +// + +// The label comes first because our store instruction contains a comma +// and confuse the preprocessor otherwise +// +#define EX(y,x...) \ + .section __ex_table,"a"; \ + data4 @gprel(99f); \ + data4 y-99f; \ + .previous; \ +99: x + + .text + .psr abi64 + .psr lsb + .lsb + + .align 32 + .global __do_clear_user + .proc __do_clear_user + +__do_clear_user: + alloc saved_pfs=ar.pfs,2,0,0,0 + cmp.eq p6,p0=r0,len // check for zero length + mov saved_lc=ar.lc // preserve ar.lc (slow) + ;; // avoid WAW on CFM + adds tmp=-1,len // br.ctop is repeat/until + mov ret0=len // return value is length at this point +(p6) br.ret.spnt.few rp + ;; + cmp.lt p6,p0=16,len // if len > 16 then long memset + mov ar.lc=tmp // initialize lc for small count +(p6) br.cond.dptk.few long_do_clear + ;; // WAR on ar.lc + // + // worst case 16 cyles, avg 8 cycles + // + // We could have played with the predicates to use the extra + // M slot for 2 stores/iteration but the cost the initialization + // the various counters compared to how long the loop is supposed + // to last on average does not make this solution viable. + // +1: + EX( .Lexit1, st1 [buf]=r0,1 ) + adds len=-1,len // countdown length using len + br.cloop.dptk.few 1b + ;; // avoid RAW on ar.lc + // + // .Lexit4: comes from byte by byte loop + // len contains bytes left +.Lexit1: + mov ret0=len // faster than using ar.lc + mov ar.lc=saved_lc + br.ret.sptk.few rp // end of short clear_user + + + // + // At this point we know we have more than 16 bytes to copy + // so we focus on alignment (no branches required) + // + // The use of len/len2 for countdown of the number of bytes left + // instead of ret0 is due to the fact that the exception code + // changes the values of r8. + // +long_do_clear: + tbit.nz p6,p0=buf,0 // odd alignment (for long_do_clear) + ;; + EX( .Lexit3, (p6) st1 [buf]=r0,1 ) // 1-byte aligned +(p6) adds len=-1,len;; // sync because buf is modified + tbit.nz p6,p0=buf,1 + ;; + EX( .Lexit3, (p6) st2 [buf]=r0,2 ) // 2-byte aligned +(p6) adds len=-2,len;; + tbit.nz p6,p0=buf,2 + ;; + EX( .Lexit3, (p6) st4 [buf]=r0,4 ) // 4-byte aligned +(p6) adds len=-4,len;; + tbit.nz p6,p0=buf,3 + ;; + EX( .Lexit3, (p6) st8 [buf]=r0,8 ) // 8-byte aligned +(p6) adds len=-8,len;; + shr.u cnt=len,4 // number of 128-bit (2x64bit) words + ;; + cmp.eq p6,p0=r0,cnt + adds tmp=-1,cnt +(p6) br.cond.dpnt.few .dotail // we have less than 16 bytes left + ;; + adds buf2=8,buf // setup second base pointer + mov ar.lc=tmp + ;; + + // + // 16bytes/iteration core loop + // + // The second store can never generate a fault because + // we come into the loop only when we are 16-byte aligned. + // This means that if we cross a page then it will always be + // in the first store and never in the second. + // + // + // We need to keep track of the remaining length. A possible (optimistic) + // way would be to ue ar.lc and derive how many byte were left by + // doing : left= 16*ar.lc + 16. this would avoid the addition at + // every iteration. + // However we need to keep the synchronization point. A template + // M;;MB does not exist and thus we can keep the addition at no + // extra cycle cost (use a nop slot anyway). It also simplifies the + // (unlikely) error recovery code + // + +2: + + EX(.Lexit3, st8 [buf]=r0,16 ) + ;; // needed to get len correct when error + st8 [buf2]=r0,16 + adds len=-16,len + br.cloop.dptk.few 2b + ;; + mov ar.lc=saved_lc + // + // tail correction based on len only + // + // We alternate the use of len3,len2 to allow parallelism and correct + // error handling. We also reuse p6/p7 to return correct value. + // The addition of len2/len3 does not cost anything more compared to + // the regular memset as we had empty slots. + // +.dotail: + mov len2=len // for parallelization of error handling + mov len3=len + tbit.nz p6,p0=len,3 + ;; + EX( .Lexit2, (p6) st8 [buf]=r0,8 ) // at least 8 bytes +(p6) adds len3=-8,len2 + tbit.nz p7,p6=len,2 + ;; + EX( .Lexit2, (p7) st4 [buf]=r0,4 ) // at least 4 bytes +(p7) adds len2=-4,len3 + tbit.nz p6,p7=len,1 + ;; + EX( .Lexit2, (p6) st2 [buf]=r0,2 ) // at least 2 bytes +(p6) adds len3=-2,len2 + tbit.nz p7,p6=len,0 + ;; + EX( .Lexit2, (p7) st1 [buf]=r0 ) // only 1 byte left + mov ret0=r0 // success + br.ret.dptk.few rp // end of most likely path + + // + // Outlined error handling code + // + + // + // .Lexit3: comes from core loop, need restore pr/lc + // len contains bytes left + // + // + // .Lexit2: + // if p6 -> coming from st8 or st2 : len2 contains what's left + // if p7 -> coming from st4 or st1 : len3 contains what's left + // We must restore lc/pr even though might not have been used. +.Lexit2: +(p6) mov len=len2 +(p7) mov len=len3 + ;; + // + // .Lexit4: comes from head, need not restore pr/lc + // len contains bytes left + // +.Lexit3: + mov ret0=len + mov ar.lc=saved_lc + br.ret.dptk.few rp + .endp diff --git a/arch/ia64/lib/copy_page.S b/arch/ia64/lib/copy_page.S new file mode 100644 index 000000000..0a956e5a2 --- /dev/null +++ b/arch/ia64/lib/copy_page.S @@ -0,0 +1,87 @@ +/* + * + * Optimized version of the standard copy_page() function + * + * Based on comments from ddd. Try not to overflow write buffer. + * + * Inputs: + * in0: address of target page + * in1: address of source page + * Output: + * no return value + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + */ +#include <asm/page.h> + +#define lcount r16 +#define saved_pr r17 +#define saved_lc r18 +#define saved_pfs r19 +#define src1 r20 +#define src2 r21 +#define tgt1 r22 +#define tgt2 r23 + + .text + .psr abi64 + .psr lsb + .lsb + + .align 32 + .global copy_page + .proc copy_page + +copy_page: + alloc saved_pfs=ar.pfs,10,0,0,8 // we need 6 roatating (8 minimum) + // + 2 input + + .rotr t1[4], t2[4] // our 2 pipelines with depth of 4 each + + mov saved_lc=ar.lc // save ar.lc ahead of time + mov saved_pr=pr // rotating predicates are preserved + // resgisters we must save. + mov src1=in1 // initialize 1st stream source + adds src2=8,in1 // initialize 2nd stream source + mov lcount=PAGE_SIZE/16-1 // as many 16bytes as there are on a page + // -1 is because br.ctop is repeat/until + + adds tgt2=8,in0 // initialize 2nd stream target + mov tgt1=in0 // initialize 1st stream target + ;; + mov pr.rot=1<<16 // pr16=1 & pr[17-63]=0 , 63 not modified + + mov ar.lc=lcount // set loop counter + mov ar.ec=4 // ar.ec must match pipeline depth + ;; + + // We need to preload the n-1 stages of the pipeline (n=depth). + // We do this during the "prolog" of the loop: we execute + // n-1 times the "load" bundle. Then both loads & stores are + // enabled until we reach the end of the last word of the page + // on the load side. Then, we enter the epilogue (controlled by ec) + // where we just do the stores and no loads n-1 times : drain the pipe. + // + // The initialization of the prolog is done via the predicate registers: + // the choice of pr19 DEPENDS on the depth of the pipeline (n). + // When lc > 0 pr63=1 and it is fed back into pr16 and pr16-pr62 + // are then shifted right at every iteration, + // Thus by initializing pr16=1 and pr17-19=0 (19=16+4-1) before the loop + // we get pr19=1 after 4 iterations (n in our case). + // +1: // engage loop now, let the magic happen... +(p16) ld8 t1[0]=[src1],16 // new data on top of pipeline in 1st stream +(p16) ld8 t2[0]=[src2],16 // new data on top of pipeline in 2nd stream + nop.i 0x0 +(p19) st8 [tgt1]=t1[3],16 // store top of 1st pipeline +(p19) st8 [tgt2]=t2[3],16 // store top of 2nd pipeline + br.ctop.dptk.few 1b // once lc==0, ec-- & p16=0 + // stores but no loads anymore + ;; + mov pr=saved_pr,0xffffffffffff0000 // restore predicates + mov ar.pfs=saved_pfs // restore ar.ec + mov ar.lc=saved_lc // restore saved lc + br.ret.sptk.few rp // bye... + + .endp copy_page diff --git a/arch/ia64/lib/copy_user.S b/arch/ia64/lib/copy_user.S new file mode 100644 index 000000000..03a540a80 --- /dev/null +++ b/arch/ia64/lib/copy_user.S @@ -0,0 +1,71 @@ +/* + * This routine copies a linear memory buffer across the user/kernel boundary. When + * reading a byte from the source causes a fault, the remainder of the destination + * buffer is zeroed out. Note that this can happen only when copying from user + * to kernel memory and we do this to absolutely guarantee that the + * kernel doesn't operate on random data. + * + * This file is derived from arch/alpha/lib/copy_user.S. + * + * Inputs: + * in0: address of destination buffer + * in1: address of source buffer + * in2: length of buffer in bytes + * Outputs: + * r8: number of bytes that didn't get copied due to a fault + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#define EXI(x...) \ +99: x; \ + .section __ex_table,"a"; \ + data4 @gprel(99b); \ + data4 .Lexit_in-99b; \ + .previous + +#define EXO(x...) \ +99: x; \ + .section __ex_table,"a"; \ + data4 @gprel(99b); \ + data4 .Lexit_out-99b; \ + .previous + + .text + .psr abi64 + .psr lsb + .lsb + + .align 32 + .global __copy_user + .proc __copy_user +__copy_user: + alloc r10=ar.pfs,3,0,0,0 + mov r9=ar.lc // save ar.lc + mov ar.lc=in2 // set ar.lc to length of buffer + br.sptk.few .Lentr + + // XXX braindead copy loop---this needs to be optimized +.Loop1: + EXI(ld1 r8=[in1],1) + ;; + EXO(st1 [in0]=r8,1) +.Lentr: br.cloop.dptk.few .Loop1 // repeat unless ar.lc--==0 + ;; // avoid RAW on ar.lc +.Lexit_out: + mov r8=ar.lc // return how many bytes we _didn't_ copy + mov ar.lc=r9 + br.ret.sptk.few rp + +.Lexit_in: + // clear the remainder of the buffer: + mov r8=ar.lc // return how many bytes we _didn't_ copy +.Loop2: + st1 [in0]=r0,1 // this cannot fault because we get here only on user->kernel copies + br.cloop.dptk.few .Loop2 + ;; // avoid RAW on ar.lc + mov ar.lc=r9 + br.ret.sptk.few rp + + .endp __copy_user diff --git a/arch/ia64/lib/csum_partial_copy.c b/arch/ia64/lib/csum_partial_copy.c new file mode 100644 index 000000000..d09f11e21 --- /dev/null +++ b/arch/ia64/lib/csum_partial_copy.c @@ -0,0 +1,165 @@ +/* + * Network Checksum & Copy routine + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * + * Most of the code has been imported from Linux/Alpha + */ + +#include <linux/types.h> +#include <linux/string.h> + +#include <asm/uaccess.h> + +/* + * XXX Fixme: those 2 inlines are meant for debugging and will go away + */ +static inline unsigned +short from64to16(unsigned long x) +{ + /* add up 32-bit words for 33 bits */ + x = (x & 0xffffffff) + (x >> 32); + /* add up 16-bit and 17-bit words for 17+c bits */ + x = (x & 0xffff) + (x >> 16); + /* add up 16-bit and 2-bit for 16+c bit */ + x = (x & 0xffff) + (x >> 16); + /* add up carry.. */ + x = (x & 0xffff) + (x >> 16); + return x; +} + +static inline +unsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum) +{ + int odd, count; + unsigned long result = (unsigned long)psum; + + if (len <= 0) + goto out; + odd = 1 & (unsigned long) buff; + if (odd) { + result = *buff << 8; + len--; + buff++; + } + count = len >> 1; /* nr of 16-bit words.. */ + if (count) { + if (2 & (unsigned long) buff) { + result += *(unsigned short *) buff; + count--; + len -= 2; + buff += 2; + } + count >>= 1; /* nr of 32-bit words.. */ + if (count) { + if (4 & (unsigned long) buff) { + result += *(unsigned int *) buff; + count--; + len -= 4; + buff += 4; + } + count >>= 1; /* nr of 64-bit words.. */ + if (count) { + unsigned long carry = 0; + do { + unsigned long w = *(unsigned long *) buff; + count--; + buff += 8; + result += carry; + result += w; + carry = (w > result); + } while (count); + result += carry; + result = (result & 0xffffffff) + (result >> 32); + } + if (len & 4) { + result += *(unsigned int *) buff; + buff += 4; + } + } + if (len & 2) { + result += *(unsigned short *) buff; + buff += 2; + } + } + if (len & 1) + result += *buff; + + result = from64to16(result); + + if (odd) + result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); + +out: + return result; +} + +/* + * XXX Fixme + * + * This is very ugly but temporary. THIS NEEDS SERIOUS ENHANCEMENTS. + * But it's very tricky to get right even in C. + */ +extern unsigned long do_csum(const unsigned char *, int); + +static unsigned int +do_csum_partial_copy_from_user (const char *src, char *dst, int len, + unsigned int psum, int *errp) +{ + const unsigned char *psrc = src; + unsigned long result; + int cplen = len; + int r = 0; + + /* XXX Fixme + * for now we separate the copy from checksum for obvious + * alignment difficulties. Look at the Alpha code and you'll be + * scared. + */ + + while ( cplen-- ) r |=__get_user(*dst++,psrc++); + + if ( r && errp ) *errp = r; + + result = do_csum(src, len); + + /* add in old sum, and carry.. */ + result += psum; + /* 32+c bits -> 32 bits */ + result = (result & 0xffffffff) + (result >> 32); + return result; +} + +unsigned int +csum_partial_copy_from_user(const char *src, char *dst, int len, + unsigned int sum, int *errp) +{ + if (!access_ok(src, len, VERIFY_READ)) { + *errp = -EFAULT; + memset(dst, 0, len); + return sum; + } + + return do_csum_partial_copy_from_user(src, dst, len, sum, errp); +} + +unsigned int +csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum) +{ + return do_csum_partial_copy_from_user(src, dst, len, sum, NULL); +} + +unsigned int +csum_partial_copy (const char *src, char *dst, int len, unsigned int sum) +{ + unsigned int ret; + int error = 0; + + ret = do_csum_partial_copy_from_user(src, dst, len, sum, &error); + if (error) + printk("csum_partial_copy_old(): tell mingo to convert me!\n"); + + return ret; +} + diff --git a/arch/ia64/lib/do_csum.S b/arch/ia64/lib/do_csum.S new file mode 100644 index 000000000..d8174f10a --- /dev/null +++ b/arch/ia64/lib/do_csum.S @@ -0,0 +1,230 @@ +/* + * + * Optmized version of the standard do_csum() function + * + * Return: a 64bit quantity containing the 16bit Internet checksum + * + * Inputs: + * in0: address of buffer to checksum (char *) + * in1: length of the buffer (int) + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * + */ + +// +// Theory of operations: +// The goal is to go as quickly as possible to the point where +// we can checksum 8 bytes/loop. Before reaching that point we must +// take care of incorrect alignment of first byte. +// +// The code hereafter also takes care of the "tail" part of the buffer +// before entering the core loop, if any. The checksum is a sum so it +// allows us to commute operations. So we do do the "head" and "tail" +// first to finish at full speed in the body. Once we get the head and +// tail values, we feed them into the pipeline, very handy initialization. +// +// Of course we deal with the special case where the whole buffer fits +// into one 8 byte word. In this case we have only one entry in the pipeline. +// +// We use a (3+1)-stage pipeline in the loop to account for possible +// load latency and also to accomodate for head and tail. +// +// The end of the function deals with folding the checksum from 64bits +// down to 16bits taking care of the carry. +// +// This version avoids synchronization in the core loop by also using a +// pipeline for the accumulation of the checksum in result[]. +// +// p[] +// |---| +// 0| | r32 : new value loaded in pipeline +// |---| +// 1| | r33 : in transit data +// |---| +// 2| | r34 : current value to add to checksum +// |---| +// 3| | r35 : previous value added to checksum (previous iteration) +// |---| +// +// result[] +// |---| +// 0| | r36 : new checksum +// |---| +// 1| | r37 : previous value of checksum +// |---| +// 2| | r38 : final checksum when out of the loop (after 2 epilogue rots) +// |---| +// +// +// NOT YET DONE: +// - Take advantage of the MMI bandwidth to load more than 8byte per loop +// iteration +// - use the lfetch instruction to augment the chances of the data being in +// the cache when we need it. +// - Maybe another algorithm which would take care of the folding at the +// end in a different manner +// - Work with people more knowledgeable than me on the network stack +// to figure out if we could not split the function depending on the +// type of packet or alignment we get. Like the ip_fast_csum() routine +// where we know we have at least 20bytes worth of data to checksum. +// - Look at RFCs about checksums to see whether or not we can do better +// +// - Do a better job of handling small packets. +// +#define saved_pfs r11 +#define hmask r16 +#define tmask r17 +#define first r18 +#define firstval r19 +#define firstoff r20 +#define last r21 +#define lastval r22 +#define lastoff r23 +#define saved_lc r24 +#define saved_pr r25 +#define tmp1 r26 +#define tmp2 r27 +#define tmp3 r28 +#define carry r29 + +#define buf in0 +#define len in1 + + + .text + .psr abi64 + .psr lsb + .lsb + +// unsigned long do_csum(unsigned char *buf,int len) + + .align 32 + .global do_csum + .proc do_csum +do_csum: + alloc saved_pfs=ar.pfs,2,8,0,8 + + .rotr p[4], result[3] + mov ret0=r0 // in case we have zero length + cmp4.lt p0,p6=r0,len // check for zero length or negative (32bit len) + ;; // avoid WAW on CFM + mov tmp3=0x7 // a temporary mask/value + add tmp1=buf,len // last byte's address +(p6) br.ret.spnt.few rp // return if true (hope we can avoid that) + + and firstoff=7,buf // how many bytes off for first element + tbit.nz p10,p0=buf,0 // is buf an odd address ? + mov hmask=-1 // intialize head mask + ;; + + andcm first=buf,tmp3 // 8byte aligned down address of first element + mov tmask=-1 // initialize tail mask + adds tmp2=-1,tmp1 // last-1 + ;; + and lastoff=7,tmp1 // how many bytes off for last element + andcm last=tmp2,tmp3 // address of word containing last byte + mov saved_pr=pr // preserve predicates (rotation) + ;; + sub tmp3=last,first // tmp3=distance from first to last + cmp.eq p8,p9=last,first // everything fits in one word ? + sub tmp1=8,lastoff // complement to lastoff + + ld8 firstval=[first],8 // load,ahead of time, "first" word + shl tmp2=firstoff,3 // number of bits + ;; + and tmp1=7, tmp1 // make sure that if tmp1==8 -> tmp1=0 + +(p9) ld8 lastval=[last] // load,ahead of time, "last" word, if needed +(p8) mov lastval=r0 // we don't need lastval if first==last + mov result[1]=r0 // initialize result + ;; + + shl tmp1=tmp1,3 // number of bits + shl hmask=hmask,tmp2 // build head mask, mask off [0,firstoff[ + ;; + shr.u tmask=tmask,tmp1 // build tail mask, mask off ]8,lastoff] + mov saved_lc=ar.lc // save lc + ;; +(p8) and hmask=hmask,tmask // apply tail mask to head mask if 1 word only +(p9) and p[1]=lastval,tmask // mask last it as appropriate + shr.u tmp3=tmp3,3 // we do 8 bytes per loop + ;; + cmp.lt p6,p7=2,tmp3 // tmp3 > 2 ? + and p[2]=firstval,hmask // and mask it as appropriate + add tmp1=-2,tmp3 // -2 = -1 (br.ctop) -1 (last-first) + ;; + // XXX Fixme: not very nice initialization here + // + // Setup loop control registers: + // + // tmp3=0 (1 word) : lc=0, ec=2, p16=F + // tmp3=1 (2 words) : lc=0, ec=3, p16=F + // tmp3=2 (3 words) : lc=0, ec=4, p16=T + // tmp3>2 (4 or more): lc=tmp3-2, ec=4, p16=T + // + cmp.eq p8,p9=r0,tmp3 // tmp3 == 0 ? +(p6) mov ar.lc=tmp1 +(p7) mov ar.lc=0 + ;; + cmp.lt p6,p7=1,tmp3 // tmp3 > 1 ? +(p8) mov ar.ec=2 // we need the extra rotation on result[] +(p9) mov ar.ec=3 // hard not to set it twice sometimes + ;; + mov carry=r0 // initialize carry +(p6) mov ar.ec=4 +(p6) mov pr.rot=0xffffffffffff0000 // p16=T, p18=T + + cmp.ne p8,p0=r0,r0 // p8 is false + mov p[3]=r0 // make sure first compare fails +(p7) mov pr.rot=0xfffffffffffe0000 // p16=F, p18=T + ;; +1: +(p16) ld8 p[0]=[first],8 // load next +(p8) adds carry=1,carry // add carry on prev_prev_value +(p18) add result[0]=result[1],p[2] // new_res = prev_res + cur_val + cmp.ltu p8,p0=result[1],p[3] // p8= prev_result < prev_val + br.ctop.dptk.few 1b // loop until lc--==0 + ;; // RAW on carry when loop exits + (p8) adds carry=1,carry;; // correct for carry on prev_value + add result[2]=carry,result[2];; // add carry to final result + cmp.ltu p6,p7=result[2], carry // check for new carry + ;; +(p6) adds result[2]=1,result[1] // correct if required + movl tmp3=0xffffffff + ;; + // XXX Fixme + // + // now fold 64 into 16 bits taking care of carry + // that's not very good because it has lots of sequentiality + // + and tmp1=result[2],tmp3 + shr.u tmp2=result[2],32 + ;; + add result[2]=tmp1,tmp2 + shr.u tmp3=tmp3,16 + ;; + and tmp1=result[2],tmp3 + shr.u tmp2=result[2],16 + ;; + add result[2]=tmp1,tmp2 + ;; + and tmp1=result[2],tmp3 + shr.u tmp2=result[2],16 + ;; + add result[2]=tmp1,tmp2 + ;; + and tmp1=result[2],tmp3 + shr.u tmp2=result[2],16 + ;; + add ret0=tmp1,tmp2 + mov pr=saved_pr,0xffffffffffff0000 + ;; + // if buf was odd then swap bytes + mov ar.pfs=saved_pfs // restore ar.ec +(p10) mux1 ret0=ret0,@rev // reverse word + ;; + mov ar.lc=saved_lc +(p10) shr.u ret0=ret0,64-16 // + shift back to position = swap bytes + br.ret.sptk.few rp diff --git a/arch/ia64/lib/flush.S b/arch/ia64/lib/flush.S new file mode 100644 index 000000000..0195ae5f5 --- /dev/null +++ b/arch/ia64/lib/flush.S @@ -0,0 +1,37 @@ +/* + * Cache flushing routines. + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#include <asm/page.h> + + .text + .psr abi64 + .psr lsb + .lsb + + .align 16 + .global ia64_flush_icache_page + .proc ia64_flush_icache_page +ia64_flush_icache_page: + alloc r2=ar.pfs,1,0,0,0 + mov r3=ar.lc // save ar.lc + mov r8=PAGE_SIZE/64-1 // repeat/until loop + ;; + mov ar.lc=r8 + add r8=32,in0 + ;; +.Loop1: fc in0 // issuable on M0 only + add in0=64,in0 + fc r8 + add r8=64,r8 + br.cloop.sptk.few .Loop1 + ;; + sync.i + ;; + srlz.i + ;; + mov ar.lc=r3 // restore ar.lc + br.ret.sptk.few rp + .endp ia64_flush_icache_page diff --git a/arch/ia64/lib/idiv.S b/arch/ia64/lib/idiv.S new file mode 100644 index 000000000..a12097c94 --- /dev/null +++ b/arch/ia64/lib/idiv.S @@ -0,0 +1,158 @@ +/* + * Integer division routine. + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +/* Simple integer division. It uses the straight forward division + algorithm. This may not be the absolutely fastest way to do it, + but it's not horrible either. According to ski, the worst case + scenario of dividing 0xffffffffffffffff by 1 takes 133 cycles. + + An alternative would be to use an algorithm similar to the + floating point division algorithm (Newton-Raphson iteration), + but that approach is rather tricky (one has to be very careful + to get the last bit right...). + + While this algorithm is straight-forward, it does use a couple + of neat ia-64 specific tricks: + + - it uses the floating point unit to determine the initial + shift amount (shift = floor(ld(x)) - floor(ld(y))) + + - it uses predication to avoid a branch in the case where + x < y (this is what p8 is used for) + + - it uses rotating registers and the br.ctop branch to + implement a software-pipelined loop that's unrolled + twice (without any code expansion!) + + - the code is relatively well scheduled to avoid unnecessary + nops while maximizing parallelism +*/ + +#include <asm/break.h> + + .text + .psr abi64 +#ifdef __BIG_ENDIAN__ + .psr msb + .msb +#else + .psr lsb + .lsb +#endif + +#ifdef MODULO +# define OP mod +# define Q r9 +# define R r8 +#else +# define OP div +# define Q r8 +# define R r9 +#endif + +#ifdef SINGLE +# define PREC si +#else +# define PREC di +#endif + +#ifdef UNSIGNED +# define SGN u +# define INT_TO_FP(a,b) fma.s0 a=b,f1,f0 +# define FP_TO_INT(a,b) fcvt.fxu.trunc.s0 a=b +#else +# define SGN +# define INT_TO_FP(a,b) fcvt.xf a=b +# define FP_TO_INT(a,b) fcvt.fx.trunc.s0 a=b +#endif + +#define PASTE1(a,b) a##b +#define PASTE(a,b) PASTE1(a,b) +#define NAME PASTE(PASTE(__,SGN),PASTE(OP,PASTE(PREC,3))) + + .align 32 + .global NAME + .proc NAME +NAME: + + alloc r2=ar.pfs,2,6,0,8 + mov r18=pr +#ifdef SINGLE +# ifdef UNSIGNED + zxt4 in0=in0 + zxt4 in1=in1 +# else + sxt4 in0=in0 + sxt4 in1=in1 +# endif + ;; +#endif + +#ifndef UNSIGNED + cmp.lt p6,p0=in0,r0 // x negative? + cmp.lt p7,p0=in1,r0 // y negative? + ;; +(p6) sub in0=r0,in0 // make x positive +(p7) sub in1=r0,in1 // ditto for y + ;; +#endif + + setf.sig f8=in0 + mov r3=ar.lc // save ar.lc + setf.sig f9=in1 + ;; + mov Q=0 // initialize q + mov R=in0 // stash away x in a static register + mov r16=1 // r16 = 1 + INT_TO_FP(f8,f8) + cmp.eq p8,p0=0,in0 // x==0? + cmp.eq p9,p0=0,in1 // y==0? + ;; + INT_TO_FP(f9,f9) +(p8) br.dpnt.few .L3 +(p9) break __IA64_BREAK_KDB // attempted division by zero (should never happen) + mov ar.ec=r0 // epilogue count = 0 + ;; + getf.exp r14=f8 // r14 = exponent of x + getf.exp r15=f9 // r15 = exponent of y + mov ar.lc=r0 // loop count = 0 + ;; + sub r17=r14,r15 // r17 = (exp of x - exp y) = shift amount + cmp.ge p8,p0=r14,r15 + ;; + + .rotr y[2], mask[2] // in0 and in1 may no longer be valid after + // the first write to a rotating register! + +(p8) shl y[1]=in1,r17 // y[1] = y<<shift +(p8) shl mask[1]=r16,r17 // mask[1] = 1<<shift + +(p8) mov ar.lc=r17 // loop count = r17 + ;; +.L1: +(p8) cmp.geu.unc p9,p0=R,y[1]// p9 = (x >= y[1]) +(p8) shr.u mask[0]=mask[1],1 // prepare mask[0] and y[0] for next +(p8) shr.u y[0]=y[1],1 // iteration + ;; +(p9) sub R=R,y[1] // if (x >= y[1]), subtract y[1] from x +(p9) add Q=Q,mask[1] // and set corresponding bit in q (Q) + br.ctop.dptk.few .L1 // repeated unless ar.lc-- == 0 + ;; +.L2: +#ifndef UNSIGNED +# ifdef MODULO +(p6) sub R=r0,R // set sign of remainder according to x +# else +(p6) sub Q=r0,Q // set sign of quotient + ;; +(p7) sub Q=r0,Q +# endif +#endif +.L3: + mov ar.pfs=r2 // restore ar.pfs + mov ar.lc=r3 // restore ar.lc + mov pr=r18,0xffffffffffff0000 // restore p16-p63 + br.ret.sptk.few rp diff --git a/arch/ia64/lib/memset.S b/arch/ia64/lib/memset.S new file mode 100644 index 000000000..595720a2d --- /dev/null +++ b/arch/ia64/lib/memset.S @@ -0,0 +1,111 @@ +/* + * + * Optimized version of the standard memset() function + * + * Return: none + * + * + * Inputs: + * in0: address of buffer + * in1: byte value to use for storing + * in2: length of the buffer + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + */ + + +// arguments +// +#define buf r32 +#define val r33 +#define len r34 + +// +// local registers +// +#define saved_pfs r14 +#define cnt r18 +#define buf2 r19 +#define saved_lc r20 +#define saved_pr r21 +#define tmp r22 + + .text + .psr abi64 + .psr lsb + + .align 16 + .global memset + .proc memset + +memset: + alloc saved_pfs=ar.pfs,3,0,0,0 // cnt is sink here + cmp.eq p8,p0=r0,len // check for zero length + mov saved_lc=ar.lc // preserve ar.lc (slow) + ;; + adds tmp=-1,len // br.ctop is repeat/until + tbit.nz p6,p0=buf,0 // odd alignment +(p8) br.ret.spnt.few rp + + cmp.lt p7,p0=16,len // if len > 16 then long memset + mux1 val=val,@brcst // prepare value +(p7) br.cond.dptk.few long_memset + ;; + mov ar.lc=tmp // initialize lc for small count + ;; // avoid RAW and WAW on ar.lc +1: // worst case 15 cyles, avg 8 cycles + st1 [buf]=val,1 + br.cloop.dptk.few 1b + ;; // avoid RAW on ar.lc + mov ar.lc=saved_lc + mov ar.pfs=saved_pfs + br.ret.sptk.few rp // end of short memset + + // at this point we know we have more than 16 bytes to copy + // so we focus on alignment +long_memset: +(p6) st1 [buf]=val,1 // 1-byte aligned +(p6) adds len=-1,len;; // sync because buf is modified + tbit.nz p6,p0=buf,1 + ;; +(p6) st2 [buf]=val,2 // 2-byte aligned +(p6) adds len=-2,len;; + tbit.nz p6,p0=buf,2 + ;; +(p6) st4 [buf]=val,4 // 4-byte aligned +(p6) adds len=-4,len;; + tbit.nz p6,p0=buf,3 + ;; +(p6) st8 [buf]=val,8 // 8-byte aligned +(p6) adds len=-8,len;; + shr.u cnt=len,4 // number of 128-bit (2x64bit) words + ;; + cmp.eq p6,p0=r0,cnt + adds tmp=-1,cnt +(p6) br.cond.dpnt.few .dotail // we have less than 16 bytes left + ;; + adds buf2=8,buf // setup second base pointer + mov ar.lc=tmp + ;; +2: // 16bytes/iteration + st8 [buf]=val,16 + st8 [buf2]=val,16 + br.cloop.dptk.few 2b + ;; +.dotail: // tail correction based on len only + tbit.nz p6,p0=len,3 + ;; +(p6) st8 [buf]=val,8 // at least 8 bytes + tbit.nz p6,p0=len,2 + ;; +(p6) st4 [buf]=val,4 // at least 4 bytes + tbit.nz p6,p0=len,1 + ;; +(p6) st2 [buf]=val,2 // at least 2 bytes + tbit.nz p6,p0=len,0 + mov ar.lc=saved_lc + ;; +(p6) st1 [buf]=val // only 1 byte left + br.ret.dptk.few rp + .endp diff --git a/arch/ia64/lib/strlen.S b/arch/ia64/lib/strlen.S new file mode 100644 index 000000000..3062716b1 --- /dev/null +++ b/arch/ia64/lib/strlen.S @@ -0,0 +1,197 @@ +/* + * + * Optimized version of the standard strlen() function + * + * + * Inputs: + * in0 address of string + * + * Outputs: + * ret0 the number of characters in the string (0 if empty string) + * does not count the \0 + * + * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * + * 09/24/99 S.Eranian add speculation recovery code + */ + +// +// +// This is an enhanced version of the basic strlen. it includes a combination +// of compute zero index (czx), parallel comparisons, speculative loads and +// loop unroll using rotating registers. +// +// General Ideas about the algorithm: +// The goal is to look at the string in chunks of 8 bytes. +// so we need to do a few extra checks at the beginning because the +// string may not be 8-byte aligned. In this case we load the 8byte +// quantity which includes the start of the string and mask the unused +// bytes with 0xff to avoid confusing czx. +// We use speculative loads and software pipelining to hide memory +// latency and do read ahead safely. This way we defer any exception. +// +// Because we don't want the kernel to be relying on particular +// settings of the DCR register, we provide recovery code in case +// speculation fails. The recovery code is going to "redo" the work using +// only normal loads. If we still get a fault then we generate a +// kernel panic. Otherwise we return the strlen as usual. +// +// The fact that speculation may fail can be caused, for instance, by +// the DCR.dm bit being set. In this case TLB misses are deferred, i.e., +// a NaT bit will be set if the translation is not present. The normal +// load, on the other hand, will cause the translation to be inserted +// if the mapping exists. +// +// It should be noted that we execute recovery code only when we need +// to use the data that has been speculatively loaded: we don't execute +// recovery code on pure read ahead data. +// +// Remarks: +// - the cmp r0,r0 is used as a fast way to initialize a predicate +// register to 1. This is required to make sure that we get the parallel +// compare correct. +// +// - we don't use the epilogue counter to exit the loop but we need to set +// it to zero beforehand. +// +// - after the loop we must test for Nat values because neither the +// czx nor cmp instruction raise a NaT consumption fault. We must be +// careful not to look too far for a Nat for which we don't care. +// For instance we don't need to look at a NaT in val2 if the zero byte +// was in val1. +// +// - Clearly performance tuning is required. +// +// +// +#define saved_pfs r11 +#define tmp r10 +#define base r16 +#define orig r17 +#define saved_pr r18 +#define src r19 +#define mask r20 +#define val r21 +#define val1 r22 +#define val2 r23 + + + .text + .psr abi64 + .psr lsb + .lsb + + .align 32 + .global strlen + .proc strlen +strlen: + alloc saved_pfs=ar.pfs,11,0,0,8 // rotating must be multiple of 8 + + .rotr v[2], w[2] // declares our 4 aliases + + extr.u tmp=in0,0,3 // tmp=least significant 3 bits + mov orig=in0 // keep trackof initial byte address + dep src=0,in0,0,3 // src=8byte-aligned in0 address + mov saved_pr=pr // preserve predicates (rotation) + ;; + ld8 v[1]=[src],8 // must not speculate: can fail here + shl tmp=tmp,3 // multiply by 8bits/byte + mov mask=-1 // our mask + ;; + ld8.s w[1]=[src],8 // speculatively load next + cmp.eq p6,p0=r0,r0 // sets p6 to true for cmp.and + sub tmp=64,tmp // how many bits to shift our mask on the right + ;; + shr.u mask=mask,tmp // zero enough bits to hold v[1] valuable part + mov ar.ec=r0 // clear epilogue counter (saved in ar.pfs) + ;; + add base=-16,src // keep track of aligned base + or v[1]=v[1],mask // now we have a safe initial byte pattern + ;; +1: + ld8.s v[0]=[src],8 // speculatively load next + czx1.r val1=v[1] // search 0 byte from right + czx1.r val2=w[1] // search 0 byte from right following 8bytes + ;; + ld8.s w[0]=[src],8 // speculatively load next to next + cmp.eq.and p6,p0=8,val1 // p6 = p6 and val1==8 + cmp.eq.and p6,p0=8,val2 // p6 = p6 and mask==8 +(p6) br.wtop.dptk.few 1b // loop until p6 == 0 + ;; + // + // We must return try the recovery code iff + // val1_is_nat || (val1==8 && val2_is_nat) + // + // XXX Fixme + // - there must be a better way of doing the test + // + cmp.eq p8,p9=8,val1 // p6 = val1 had zero (disambiguate) +#ifdef notyet + tnat.nz p6,p7=val1 // test NaT on val1 +#else + tnat.z p7,p6=val1 // test NaT on val1 +#endif +(p6) br.cond.spnt.few recover// jump to recovery if val1 is NaT + ;; + // + // if we come here p7 is true, i.e., initialized for // cmp + // + cmp.eq.and p7,p0=8,val1// val1==8? + tnat.nz.and p7,p0=val2 // test NaT if val2 +(p7) br.cond.spnt.few recover// jump to recovery if val2 is NaT + ;; +(p8) mov val1=val2 // the other test got us out of the loop +(p8) adds src=-16,src // correct position when 3 ahead +(p9) adds src=-24,src // correct position when 4 ahead + ;; + sub ret0=src,orig // distance from base + sub tmp=8,val1 // which byte in word + mov pr=saved_pr,0xffffffffffff0000 + ;; + sub ret0=ret0,tmp // adjust + mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what + br.ret.sptk.few rp // end of normal execution + + // + // Outlined recovery code when speculation failed + // + // This time we don't use speculation and rely on the normal exception + // mechanism. that's why the loop is not as good as the previous one + // because read ahead is not possible + // + // IMPORTANT: + // Please note that in the case of strlen() as opposed to strlen_user() + // we don't use the exception mechanism, as this function is not + // supposed to fail. If that happens it means we have a bug and the + // code will cause of kernel fault. + // + // XXX Fixme + // - today we restart from the beginning of the string instead + // of trying to continue where we left off. + // +recover: + ld8 val=[base],8 // will fail if unrecoverable fault + ;; + or val=val,mask // remask first bytes + cmp.eq p0,p6=r0,r0 // nullify first ld8 in loop + ;; + // + // ar.ec is still zero here + // +2: +(p6) ld8 val=[base],8 // will fail if unrecoverable fault + ;; + czx1.r val1=val // search 0 byte from right + ;; + cmp.eq p6,p0=8,val1 // val1==8 ? +(p6) br.wtop.dptk.few 2b // loop until p6 == 0 + sub ret0=base,orig // distance from base + sub tmp=8,val1 + mov pr=saved_pr,0xffffffffffff0000 + ;; + sub ret0=ret0,tmp // length=now - back -1 + mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what + br.ret.sptk.few rp // end of sucessful recovery code + + .endp strlen diff --git a/arch/ia64/lib/strlen_user.S b/arch/ia64/lib/strlen_user.S new file mode 100644 index 000000000..8149dde8a --- /dev/null +++ b/arch/ia64/lib/strlen_user.S @@ -0,0 +1,213 @@ +/* + * Optimized version of the strlen_user() function + * + * Inputs: + * in0 address of buffer + * + * Outputs: + * ret0 0 in case of fault, strlen(buffer)+1 otherwise + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1998, 1999 Stephane Eranian <eranian@hpl.hp.com> + * + * 01/19/99 S.Eranian heavily enhanced version (see details below) + * 09/24/99 S.Eranian added speculation recovery code + */ + +// +// int strlen_user(char *) +// ------------------------ +// Returns: +// - length of string + 1 +// - 0 in case an exception is raised +// +// This is an enhanced version of the basic strlen_user. it includes a +// combination of compute zero index (czx), parallel comparisons, speculative +// loads and loop unroll using rotating registers. +// +// General Ideas about the algorithm: +// The goal is to look at the string in chunks of 8 bytes. +// so we need to do a few extra checks at the beginning because the +// string may not be 8-byte aligned. In this case we load the 8byte +// quantity which includes the start of the string and mask the unused +// bytes with 0xff to avoid confusing czx. +// We use speculative loads and software pipelining to hide memory +// latency and do read ahead safely. This way we defer any exception. +// +// Because we don't want the kernel to be relying on particular +// settings of the DCR register, we provide recovery code in case +// speculation fails. The recovery code is going to "redo" the work using +// only normal loads. If we still get a fault then we return an +// error (ret0=0). Otherwise we return the strlen+1 as usual. +// The fact that speculation may fail can be caused, for instance, by +// the DCR.dm bit being set. In this case TLB misses are deferred, i.e., +// a NaT bit will be set if the translation is not present. The normal +// load, on the other hand, will cause the translation to be inserted +// if the mapping exists. +// +// It should be noted that we execute recovery code only when we need +// to use the data that has been speculatively loaded: we don't execute +// recovery code on pure read ahead data. +// +// Remarks: +// - the cmp r0,r0 is used as a fast way to initialize a predicate +// register to 1. This is required to make sure that we get the parallel +// compare correct. +// +// - we don't use the epilogue counter to exit the loop but we need to set +// it to zero beforehand. +// +// - after the loop we must test for Nat values because neither the +// czx nor cmp instruction raise a NaT consumption fault. We must be +// careful not to look too far for a Nat for which we don't care. +// For instance we don't need to look at a NaT in val2 if the zero byte +// was in val1. +// +// - Clearly performance tuning is required. +// +// +// + +#define EX(y,x...) \ + .section __ex_table,"a"; \ + data4 @gprel(99f); \ + data4 y-99f; \ + .previous; \ +99: x + +#define saved_pfs r11 +#define tmp r10 +#define base r16 +#define orig r17 +#define saved_pr r18 +#define src r19 +#define mask r20 +#define val r21 +#define val1 r22 +#define val2 r23 + + + .text + .psr abi64 + .psr lsb + .lsb + + .align 32 + .global __strlen_user + .proc __strlen_user +__strlen_user: + alloc saved_pfs=ar.pfs,11,0,0,8 + + .rotr v[2], w[2] // declares our 4 aliases + + extr.u tmp=in0,0,3 // tmp=least significant 3 bits + mov orig=in0 // keep trackof initial byte address + dep src=0,in0,0,3 // src=8byte-aligned in0 address + mov saved_pr=pr // preserve predicates (rotation) + ;; + ld8.s v[1]=[src],8 // load the initial 8bytes (must speculate) + shl tmp=tmp,3 // multiply by 8bits/byte + mov mask=-1 // our mask + ;; + ld8.s w[1]=[src],8 // load next 8 bytes in 2nd pipeline + cmp.eq p6,p0=r0,r0 // sets p6 (required because of // cmp.and) + sub tmp=64,tmp // how many bits to shift our mask on the right + ;; + shr.u mask=mask,tmp // zero enough bits to hold v[1] valuable part + mov ar.ec=r0 // clear epilogue counter (saved in ar.pfs) + ;; + add base=-16,src // keep track of aligned base + chk.s v[1], recover // if already NaT, then directly skip to recover + or v[1]=v[1],mask // now we have a safe initial byte pattern + ;; +1: + ld8.s v[0]=[src],8 // speculatively load next + czx1.r val1=v[1] // search 0 byte from right + czx1.r val2=w[1] // search 0 byte from right following 8bytes + ;; + ld8.s w[0]=[src],8 // speculatively load next to next + cmp.eq.and p6,p0=8,val1 // p6 = p6 and val1==8 + cmp.eq.and p6,p0=8,val2 // p6 = p6 and mask==8 +(p6) br.wtop.dptk.few 1b // loop until p6 == 0 + ;; + // + // We must return try the recovery code iff + // val1_is_nat || (val1==8 && val2_is_nat) + // + // XXX Fixme + // - there must be a better way of doing the test + // + cmp.eq p8,p9=8,val1 // p6 = val1 had zero (disambiguate) +#ifdef notyet + tnat.nz p6,p7=val1 // test NaT on val1 +#else + tnat.z p7,p6=val1 // test NaT on val1 +#endif +(p6) br.cond.spnt.few recover// jump to recovery if val1 is NaT + ;; + // + // if we come here p7 is true, i.e., initialized for // cmp + // + cmp.eq.and p7,p0=8,val1// val1==8? + tnat.nz.and p7,p0=val2 // test NaT if val2 +(p7) br.cond.spnt.few recover// jump to recovery if val2 is NaT + ;; +(p8) mov val1=val2 // val2 contains the value +(p8) adds src=-16,src // correct position when 3 ahead +(p9) adds src=-24,src // correct position when 4 ahead + ;; + sub ret0=src,orig // distance from origin + sub tmp=7,val1 // 7=8-1 because this strlen returns strlen+1 + mov pr=saved_pr,0xffffffffffff0000 + ;; + sub ret0=ret0,tmp // length=now - back -1 + mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what + br.ret.sptk.few rp // end of normal execution + + // + // Outlined recovery code when speculation failed + // + // This time we don't use speculation and rely on the normal exception + // mechanism. that's why the loop is not as good as the previous one + // because read ahead is not possible + // + // XXX Fixme + // - today we restart from the beginning of the string instead + // of trying to continue where we left off. + // +recover: + EX(.Lexit1, ld8 val=[base],8) // load the initial bytes + ;; + or val=val,mask // remask first bytes + cmp.eq p0,p6=r0,r0 // nullify first ld8 in loop + ;; + // + // ar.ec is still zero here + // +2: + EX(.Lexit1, (p6) ld8 val=[base],8) + ;; + czx1.r val1=val // search 0 byte from right + ;; + cmp.eq p6,p0=8,val1 // val1==8 ? +(p6) br.wtop.dptk.few 2b // loop until p6 == 0 + ;; + sub ret0=base,orig // distance from base + sub tmp=7,val1 // 7=8-1 because this strlen returns strlen+1 + mov pr=saved_pr,0xffffffffffff0000 + ;; + sub ret0=ret0,tmp // length=now - back -1 + mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what + br.ret.sptk.few rp // end of sucessful recovery code + + // + // We failed even on the normal load (called from exception handler) + // +.Lexit1: + mov ret0=0 + mov pr=saved_pr,0xffffffffffff0000 + mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what + br.ret.sptk.few rp + + .endp __strlen_user diff --git a/arch/ia64/lib/strncpy_from_user.S b/arch/ia64/lib/strncpy_from_user.S new file mode 100644 index 000000000..17f71f1a0 --- /dev/null +++ b/arch/ia64/lib/strncpy_from_user.S @@ -0,0 +1,53 @@ +/* + * Just like strncpy() except for the return value. If no fault occurs during + * the copying, the number of bytes copied is returned. If a fault occurs, + * -EFAULT is returned. + * + * Inputs: + * in0: address of destination buffer + * in1: address of string to be copied + * in2: length of buffer in bytes + * Outputs: + * r8: -EFAULT in case of fault or number of bytes copied if no fault + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#define EX(x...) \ +99: x; \ + .section __ex_table,"a"; \ + data4 @gprel(99b); \ + data4 .Lexit-99b; \ + .previous + + .text + .psr abi64 + .psr lsb + .lsb + + .align 32 + .global __strncpy_from_user + .proc __strncpy_from_user +__strncpy_from_user: + alloc r11=ar.pfs,3,0,0,0 + mov r9=in1 + add r10=in1,in2 + + // XXX braindead copy loop---this needs to be optimized +.Loop1: + EX(ld1 r8=[in1],1) + ;; + st1 [in0]=r8,1 + cmp.ltu p6,p0=in1,r10 + ;; +(p6) cmp.ne.and p6,p0=r8,r0 + ;; +(p6) br.cond.dpnt.few .Loop1 + +1: sub r8=in1,r9 // length of string (including NUL character) +.Lexit: + mov ar.pfs=r11 + br.ret.sptk.few rp + + .endp __strncpy_from_user diff --git a/arch/ia64/lib/strnlen_user.S b/arch/ia64/lib/strnlen_user.S new file mode 100644 index 000000000..c227a9003 --- /dev/null +++ b/arch/ia64/lib/strnlen_user.S @@ -0,0 +1,55 @@ +/* + * Returns 0 if exception before NUL or reaching the supplied limit (N), + * a value greater than N if the string is longer than the limit, else + * strlen. + * + * Inputs: + * in0: address of buffer + * in1: string length limit N + * Outputs: + * r8: 0 in case of fault, strlen(buffer)+1 otherwise + * + * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +/* If a fault occurs, r8 gets set to -EFAULT and r9 gets cleared. */ +#define EX(x...) \ + .section __ex_table,"a"; \ + data4 @gprel(99f); \ + data4 (.Lexit-99f)|1; \ + .previous \ +99: x; + + .text + .psr abi64 + .psr lsb + .lsb + + .align 32 + .global __strnlen_user + .proc __strnlen_user +__strnlen_user: + alloc r2=ar.pfs,2,0,0,0 + mov r16=ar.lc // preserve ar.lc + add r3=-1,in1 + ;; + mov ar.lc=r3 + mov r9=0 + + // XXX braindead strlen loop---this needs to be optimized +.Loop1: + EX(ld1 r8=[in0],1) + add r9=1,r9 + ;; + cmp.eq p6,p0=r8,r0 +(p6) br.dpnt.few .Lexit + br.cloop.dptk.few .Loop1 + + add r9=1,in1 // NUL not found---return N+1 + ;; +.Lexit: + mov r8=r9 + mov ar.lc=r16 // restore ar.lc + br.ret.sptk.few rp + + .endp __strnlen_user diff --git a/arch/ia64/mm/Makefile b/arch/ia64/mm/Makefile new file mode 100644 index 000000000..ab2b95cf9 --- /dev/null +++ b/arch/ia64/mm/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the ia64-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := mm.o +#O_OBJS := ioremap.o +O_OBJS := init.o fault.o tlb.o extable.o + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/mm/extable.c b/arch/ia64/mm/extable.c new file mode 100644 index 000000000..bee64e0e3 --- /dev/null +++ b/arch/ia64/mm/extable.c @@ -0,0 +1,68 @@ +/* + * Kernel exception handling table support. Derived from arch/alpha/mm/extable.c. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline const struct exception_table_entry * +search_one_table (const struct exception_table_entry *first, + const struct exception_table_entry *last, + signed long value) +{ + /* Abort early if the search value is out of range. */ + if (value != (signed int)value) + return 0; + + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + /* + * We know that first and last are both kernel virtual + * pointers (region 7) so first+last will cause an + * overflow. We fix that by calling __va() on the + * result, which will ensure that the top two bits get + * set again. + */ + mid = (void *) __va((((__u64) first + (__u64) last)/2/sizeof(*mid))*sizeof(*mid)); + diff = mid->addr - value; + if (diff == 0) + return mid; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +register unsigned long gp __asm__("gp"); + +const struct exception_table_entry * +search_exception_table (unsigned long addr) +{ +#ifndef CONFIG_MODULE + /* There is only the kernel to search. */ + return search_one_table(__start___ex_table, __stop___ex_table - 1, addr - gp); +#else + struct exception_table_entry *ret; + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + + for (mp = module_list; mp ; mp = mp->next) { + if (!mp->ex_table_start) + continue; + ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, addr - mp->gp); + if (ret) + return ret; + } + return 0; +#endif +} diff --git a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c new file mode 100644 index 000000000..99cf5048c --- /dev/null +++ b/arch/ia64/mm/fault.c @@ -0,0 +1,164 @@ +/* + * MMU fault handling support. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> + +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/hardirq.h> + +extern void die_if_kernel (char *, struct pt_regs *, long); + +/* + * This routine is analogous to expand_stack() but instead grows the + * register backing store (which grows towards higher addresses). + * Since the register backing store is access sequentially, we + * disallow growing the RBS by more than a page at a time. Note that + * the VM_GROWSUP flag can be set on any VM area but that's fine + * because the total process size is still limited by RLIMIT_STACK and + * RLIMIT_AS. + */ +static inline long +expand_backing_store (struct vm_area_struct *vma, unsigned long address) +{ + unsigned long grow; + + grow = PAGE_SIZE >> PAGE_SHIFT; + if (address - vma->vm_start > current->rlim[RLIMIT_STACK].rlim_cur + || (((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur)) + return -ENOMEM; + vma->vm_end += PAGE_SIZE; + vma->vm_mm->total_vm += grow; + if (vma->vm_flags & VM_LOCKED) + vma->vm_mm->locked_vm += grow; + return 0; +} + +void +ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *regs) +{ + struct mm_struct *mm = current->mm; + const struct exception_table_entry *fix; + struct vm_area_struct *vma, *prev_vma; + struct siginfo si; + int signal = SIGSEGV; + unsigned long mask; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || !mm) + goto no_context; + + down(&mm->mmap_sem); + + vma = find_vma_prev(mm, address, &prev_vma); + if (!vma) + goto bad_area; + + /* find_vma_prev() returns vma such that address < vma->vm_end or NULL */ + if (address < vma->vm_start) + goto check_expansion; + + good_area: + /* OK, we've got a good vm_area for this memory area. Check the access permissions: */ + +# define VM_READ_BIT 0 +# define VM_WRITE_BIT 1 +# define VM_EXEC_BIT 2 + +# if (((1 << VM_READ_BIT) != VM_READ || (1 << VM_WRITE_BIT) != VM_WRITE) \ + || (1 << VM_EXEC_BIT) != VM_EXEC) +# error File is out of sync with <linux/mm.h>. Pleaes update. +# endif + + mask = ( (((isr >> IA64_ISR_X_BIT) & 1UL) << VM_EXEC_BIT) + | (((isr >> IA64_ISR_W_BIT) & 1UL) << VM_WRITE_BIT) + | (((isr >> IA64_ISR_R_BIT) & 1UL) << VM_READ_BIT)); + + if ((vma->vm_flags & mask) != mask) + goto bad_area; + + /* + * If for any reason at all we couldn't handle the fault, make + * sure we exit gracefully rather than endlessly redo the + * fault. + */ + if (!handle_mm_fault(current, vma, address, (isr & IA64_ISR_W) != 0)) { + /* + * We ran out of memory, or some other thing happened + * to us that made us unable to handle the page fault + * gracefully. + */ + signal = SIGBUS; + goto bad_area; + } + up(&mm->mmap_sem); + return; + + check_expansion: + if (!(prev_vma && (prev_vma->vm_flags & VM_GROWSUP) && (address == prev_vma->vm_end))) { + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (expand_stack(vma, address)) + goto bad_area; + } else if (expand_backing_store(prev_vma, address)) + goto bad_area; + goto good_area; + + bad_area: + up(&mm->mmap_sem); + if (isr & IA64_ISR_SP) { + /* + * This fault was due to a speculative load set the + * "ed" bit in the psr to ensure forward progress + * (target register will get a NaT). + */ + ia64_psr(regs)->ed = 1; + return; + } + if (user_mode(regs)) { +#if 0 +printk("%s(%d): segfault accessing %lx\n", current->comm, current->pid, address); +show_regs(regs); +#endif + si.si_signo = signal; + si.si_errno = 0; + si.si_code = SI_KERNEL; + si.si_addr = (void *) address; + force_sig_info(SIGSEGV, &si, current); + return; + } + + no_context: + fix = search_exception_table(regs->cr_iip); + if (fix) { + regs->r8 = -EFAULT; + if (fix->skip & 1) { + regs->r9 = 0; + } + regs->cr_iip += ((long) fix->skip) & ~15; + regs->cr_ipsr &= ~IA64_PSR_RI; /* clear exception slot number */ + return; + } + + /* + * Oops. The kernel tried to access some bad page. We'll have + * to terminate things with extreme prejudice. + */ + printk(KERN_ALERT "Unable to handle kernel paging request at " + "virtual address %016lx\n", address); + die_if_kernel("Oops", regs, isr); + do_exit(SIGKILL); + return; +} diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c new file mode 100644 index 000000000..388f1fe0c --- /dev/null +++ b/arch/ia64/mm/init.c @@ -0,0 +1,461 @@ +/* + * Initialize MMU support. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <linux/bootmem.h> +#include <linux/mm.h> +#include <linux/reboot.h> +#include <linux/slab.h> +#include <linux/swap.h> + +#include <asm/dma.h> +#include <asm/efi.h> +#include <asm/ia32.h> +#include <asm/io.h> +#include <asm/pgalloc.h> +#include <asm/sal.h> +#include <asm/system.h> + +/* References to section boundaries: */ +extern char _stext, _etext, _edata, __init_begin, __init_end; + +/* + * These are allocated in head.S so that we get proper page alignment. + * If you change the size of these then change head.S as well. + */ +extern char empty_bad_page[PAGE_SIZE]; +extern pmd_t empty_bad_pmd_table[PTRS_PER_PMD]; +extern pte_t empty_bad_pte_table[PTRS_PER_PTE]; + +extern void ia64_tlb_init (void); + +static unsigned long totalram_pages; + +/* + * Fill in empty_bad_pmd_table with entries pointing to + * empty_bad_pte_table and return the address of this PMD table. + */ +static pmd_t * +get_bad_pmd_table (void) +{ + pmd_t v; + int i; + + pmd_set(&v, empty_bad_pte_table); + + for (i = 0; i < PTRS_PER_PMD; ++i) + empty_bad_pmd_table[i] = v; + + return empty_bad_pmd_table; +} + +/* + * Fill in empty_bad_pte_table with PTEs pointing to empty_bad_page + * and return the address of this PTE table. + */ +static pte_t * +get_bad_pte_table (void) +{ + pte_t v; + int i; + + set_pte(&v, pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED))); + + for (i = 0; i < PTRS_PER_PTE; ++i) + empty_bad_pte_table[i] = v; + + return empty_bad_pte_table; +} + +void +__handle_bad_pgd (pgd_t *pgd) +{ + pgd_ERROR(*pgd); + pgd_set(pgd, get_bad_pmd_table()); +} + +void +__handle_bad_pmd (pmd_t *pmd) +{ + pmd_ERROR(*pmd); + pmd_set(pmd, get_bad_pte_table()); +} + +/* + * Allocate and initialize an L3 directory page and set + * the L2 directory entry PMD to the newly allocated page. + */ +pte_t* +get_pte_slow (pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + /* everything A-OK */ + clear_page(pte); + pmd_set(pmd, pte); + return pte + offset; + } + pmd_set(pmd, get_bad_pte_table()); + return NULL; + } + free_page((unsigned long) pte); + if (pmd_bad(*pmd)) { + __handle_bad_pmd(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +int +do_check_pgt_cache (int low, int high) +{ + int freed = 0; + + if (pgtable_cache_size > high) { + do { + if (pgd_quicklist) + free_page((unsigned long)get_pgd_fast()), ++freed; + if (pmd_quicklist) + free_page((unsigned long)get_pmd_fast()), ++freed; + if (pte_quicklist) + free_page((unsigned long)get_pte_fast()), ++freed; + } while (pgtable_cache_size > low); + } + return freed; +} + +/* + * This performs some platform-dependent address space initialization. + * On IA-64, we want to setup the VM area for the register backing + * store (which grows upwards) and install the gateway page which is + * used for signal trampolines, etc. + */ +void +ia64_init_addr_space (void) +{ + struct vm_area_struct *vma; + + /* + * If we're out of memory and kmem_cache_alloc() returns NULL, + * we simply ignore the problem. When the process attempts to + * write to the register backing store for the first time, it + * will get a SEGFAULT in this case. + */ + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (vma) { + vma->vm_mm = current->mm; + vma->vm_start = IA64_RBS_BOT; + vma->vm_end = vma->vm_start + PAGE_SIZE; + vma->vm_page_prot = PAGE_COPY; + vma->vm_flags = VM_READ|VM_WRITE|VM_MAYREAD|VM_MAYWRITE|VM_GROWSUP; + vma->vm_ops = NULL; + vma->vm_pgoff = 0; + vma->vm_file = NULL; + vma->vm_private_data = NULL; + insert_vm_struct(current->mm, vma); + } +} + +void +free_initmem (void) +{ + unsigned long addr; + + addr = (unsigned long) &__init_begin; + for (; addr < (unsigned long) &__init_end; addr += PAGE_SIZE) { + clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + set_page_count(&mem_map[MAP_NR(addr)], 1); + free_page(addr); + ++totalram_pages; + } + printk ("Freeing unused kernel memory: %ldkB freed\n", + (&__init_end - &__init_begin) >> 10); +} + +void +si_meminfo (struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; + return; +} + +void +show_mem (void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!page_count(mem_map + i)) + free++; + else + shared += page_count(mem_map + i) - 1; + } + printk("%d pages of RAM\n", total); + printk("%d reserved pages\n", reserved); + printk("%d pages shared\n", shared); + printk("%d pages swap cached\n", cached); + printk("%ld pages in page table cache\n", pgtable_cache_size); + show_buffers(); +} + +/* + * This is like put_dirty_page() but installs a clean page with PAGE_GATE protection + * (execute-only, typically). + */ +struct page * +put_gate_page (struct page *page, unsigned long address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + if (!PageReserved(page)) + printk("put_gate_page: gate page at 0x%lx not in reserved memory\n", + page_address(page)); + pgd = pgd_offset_k(address); /* note: this is NOT pgd_offset()! */ + pmd = pmd_alloc(pgd, address); + if (!pmd) { + __free_page(page); + oom(current); + return 0; + } + pte = pte_alloc(pmd, address); + if (!pte) { + __free_page(page); + oom(current); + return 0; + } + if (!pte_none(*pte)) { + pte_ERROR(*pte); + __free_page(page); + return 0; + } + flush_page_to_ram(page); + set_pte(pte, page_pte_prot(page, PAGE_GATE)); + /* no need for flush_tlb */ + return page; +} + +void __init +ia64_rid_init (void) +{ + unsigned long flags, rid, pta; + + /* Set up the kernel identity mappings (regions 6 & 7) and the vmalloc area (region 5): */ + ia64_clear_ic(flags); + + rid = ia64_rid(IA64_REGION_ID_KERNEL, __IA64_UNCACHED_OFFSET); + ia64_set_rr(__IA64_UNCACHED_OFFSET, (rid << 8) | (_PAGE_SIZE_256M << 2)); + + rid = ia64_rid(IA64_REGION_ID_KERNEL, PAGE_OFFSET); + ia64_set_rr(PAGE_OFFSET, (rid << 8) | (_PAGE_SIZE_256M << 2)); + + rid = ia64_rid(IA64_REGION_ID_KERNEL, VMALLOC_START); + ia64_set_rr(VMALLOC_START, (rid << 8) | (PAGE_SHIFT << 2) | 1); + + __restore_flags(flags); + + /* + * Check if the virtually mapped linear page table (VMLPT) + * overlaps with a mapped address space. The IA-64 + * architecture guarantees that at least 50 bits of virtual + * address space are implemented but if we pick a large enough + * page size (e.g., 64KB), the VMLPT is big enough that it + * will overlap with the upper half of the kernel mapped + * region. I assume that once we run on machines big enough + * to warrant 64KB pages, IMPL_VA_MSB will be significantly + * bigger, so we can just adjust the number below to get + * things going. Alternatively, we could truncate the upper + * half of each regions address space to not permit mappings + * that would overlap with the VMLPT. --davidm 99/11/13 + */ +# define ld_pte_size 3 +# define ld_max_addr_space_pages 3*(PAGE_SHIFT - ld_pte_size) /* max # of mappable pages */ +# define ld_max_addr_space_size (ld_max_addr_space_pages + PAGE_SHIFT) +# define ld_max_vpt_size (ld_max_addr_space_pages + ld_pte_size) +# define POW2(n) (1ULL << (n)) +# define IMPL_VA_MSB 50 + if (POW2(ld_max_addr_space_size - 1) + POW2(ld_max_vpt_size) > POW2(IMPL_VA_MSB)) + panic("mm/init: overlap between virtually mapped linear page table and " + "mapped kernel space!"); + pta = POW2(61) - POW2(IMPL_VA_MSB); + /* + * Set the (virtually mapped linear) page table address. Bit + * 8 selects between the short and long format, bits 2-7 the + * size of the table, and bit 0 whether the VHPT walker is + * enabled. + */ + ia64_set_pta(pta | (0<<8) | ((3*(PAGE_SHIFT-3)+3)<<2) | 1); +} + +#ifdef CONFIG_IA64_VIRTUAL_MEM_MAP + +static int +create_mem_map_page_table (u64 start, u64 end, void *arg) +{ + unsigned long address, start_page, end_page; + struct page *map_start, *map_end; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + void *page; + + map_start = mem_map + MAP_NR(start); + map_end = mem_map + MAP_NR(end); + + start_page = (unsigned long) map_start & PAGE_MASK; + end_page = PAGE_ALIGN((unsigned long) map_end); + + printk("[%lx,%lx) -> %lx-%lx\n", start, end, start_page, end_page); + + for (address = start_page; address < end_page; address += PAGE_SIZE) { + pgd = pgd_offset_k(address); + if (pgd_none(*pgd)) { + pmd = alloc_bootmem_pages(PAGE_SIZE); + clear_page(pmd); + pgd_set(pgd, pmd); + pmd += (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1); + } else + pmd = pmd_offset(pgd, address); + if (pmd_none(*pmd)) { + pte = alloc_bootmem_pages(PAGE_SIZE); + clear_page(pte); + pmd_set(pmd, pte); + pte += (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); + } else + pte = pte_offset(pmd, address); + + if (pte_none(*pte)) { + page = alloc_bootmem_pages(PAGE_SIZE); + clear_page(page); + set_pte(pte, mk_pte_phys(__pa(page), PAGE_KERNEL)); + } + } + return 0; +} + +#endif /* CONFIG_IA64_VIRTUAL_MEM_MAP */ + +/* + * Set up the page tables. + */ +void +paging_init (void) +{ + unsigned long max_dma, zones_size[MAX_NR_ZONES]; + + clear_page((void *) ZERO_PAGE_ADDR); + + ia64_rid_init(); + __flush_tlb_all(); + + /* initialize mem_map[] */ + + memset(zones_size, 0, sizeof(zones_size)); + + max_dma = virt_to_phys((void *) MAX_DMA_ADDRESS); + if (max_low_pfn < max_dma) + zones_size[ZONE_DMA] = max_low_pfn; + else { + zones_size[ZONE_DMA] = max_dma; + zones_size[ZONE_NORMAL] = max_low_pfn - max_dma; + } + free_area_init(zones_size); +} + +static int +count_pages (u64 start, u64 end, void *arg) +{ + unsigned long *count = arg; + + *count += (end - start) >> PAGE_SHIFT; + return 0; +} + +static int +count_reserved_pages (u64 start, u64 end, void *arg) +{ + unsigned long num_reserved = 0; + unsigned long *count = arg; + struct page *pg; + + for (pg = mem_map + MAP_NR(start); pg < mem_map + MAP_NR(end); ++pg) + if (PageReserved(pg)) + ++num_reserved; + *count += num_reserved; + return 0; +} + +void +mem_init (void) +{ + extern char __start_gate_section[]; + long reserved_pages, codesize, datasize, initsize; + + if (!mem_map) + BUG(); + + num_physpages = 0; + efi_memmap_walk(count_pages, &num_physpages); + + max_mapnr = max_low_pfn; + high_memory = __va(max_low_pfn * PAGE_SIZE); + + ia64_tlb_init(); + + totalram_pages += free_all_bootmem(); + + reserved_pages = 0; + efi_memmap_walk(count_reserved_pages, &reserved_pages); + + codesize = (unsigned long) &_etext - (unsigned long) &_stext; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk("Memory: %luk/%luk available (%luk code, %luk reserved, %luk data, %luk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT - 10), + max_mapnr << (PAGE_SHIFT - 10), codesize >> 10, reserved_pages << (PAGE_SHIFT - 10), + datasize >> 10, initsize >> 10); + + /* install the gate page in the global page table: */ + put_gate_page(mem_map + MAP_NR(__start_gate_section), GATE_ADDR); + +#ifndef CONFIG_IA64_SOFTSDV_HACKS + /* + * (Some) SoftSDVs seem to have a problem with this call. + * Since it's mostly a performance optimization, just don't do + * it for now... --davidm 99/12/6 + */ + efi_enter_virtual_mode(); +#endif + +#ifdef CONFIG_IA32_SUPPORT + ia32_gdt_init(); +#endif + return; +} diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c new file mode 100644 index 000000000..72ece4147 --- /dev/null +++ b/arch/ia64/mm/tlb.c @@ -0,0 +1,166 @@ +/* + * TLB support routines. + * + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/mm.h> + +#include <asm/mmu_context.h> +#include <asm/pgalloc.h> +#include <asm/pal.h> + +#define SUPPORTED_PGBITS ( \ + 1 << _PAGE_SIZE_256M | \ + 1 << _PAGE_SIZE_64M | \ + 1 << _PAGE_SIZE_16M | \ + 1 << _PAGE_SIZE_4M | \ + 1 << _PAGE_SIZE_1M | \ + 1 << _PAGE_SIZE_256K | \ + 1 << _PAGE_SIZE_64K | \ + 1 << _PAGE_SIZE_16K | \ + 1 << _PAGE_SIZE_8K | \ + 1 << _PAGE_SIZE_4K ) + +static void wrap_context (struct mm_struct *mm); + +unsigned long ia64_next_context = (1UL << IA64_HW_CONTEXT_BITS) + 1; + + /* + * Put everything in a struct so we avoid the global offset table whenever + * possible. + */ +ia64_ptce_info_t ia64_ptce_info; + +/* + * Seralize usage of ptc.g + */ +spinlock_t ptcg_lock = SPIN_LOCK_UNLOCKED; /* see <asm/pgtable.h> */ + +void +get_new_mmu_context (struct mm_struct *mm) +{ + if ((ia64_next_context & IA64_HW_CONTEXT_MASK) == 0) { + wrap_context(mm); + } + mm->context = ia64_next_context++; +} + +/* + * This is where we handle the case where (ia64_next_context & + * IA64_HW_CONTEXT_MASK) == 0. Whenever this happens, we need to + * flush the entire TLB and skip over region id number 0, which is + * used by the kernel. + */ +static void +wrap_context (struct mm_struct *mm) +{ + struct task_struct *task; + + /* + * We wrapped back to the first region id so we nuke the TLB + * so we can switch to the next generation of region ids. + */ + __flush_tlb_all(); + if (ia64_next_context++ == 0) { + /* + * Oops, we've used up all 64 bits of the context + * space---walk through task table to ensure we don't + * get tricked into using an old context. If this + * happens, the machine has been running for a long, + * long time! + */ + ia64_next_context = (1UL << IA64_HW_CONTEXT_BITS) + 1; + + read_lock(&tasklist_lock); + for_each_task (task) { + if (task->mm == mm) + continue; + flush_tlb_mm(mm); + } + read_unlock(&tasklist_lock); + } +} + +void +__flush_tlb_all (void) +{ + unsigned long i, j, flags, count0, count1, stride0, stride1, addr = ia64_ptce_info.base; + + count0 = ia64_ptce_info.count[0]; + count1 = ia64_ptce_info.count[1]; + stride0 = ia64_ptce_info.stride[0]; + stride1 = ia64_ptce_info.stride[1]; + + save_and_cli(flags); + for (i = 0; i < count0; ++i) { + for (j = 0; j < count1; ++j) { + asm volatile ("ptc.e %0" :: "r"(addr)); + addr += stride1; + } + addr += stride0; + } + restore_flags(flags); + ia64_insn_group_barrier(); + ia64_srlz_i(); /* srlz.i implies srlz.d */ + ia64_insn_group_barrier(); +} + +void +flush_tlb_range (struct mm_struct *mm, unsigned long start, unsigned long end) +{ + unsigned long size = end - start; + unsigned long nbits; + + if (mm != current->active_mm) { + /* this doesn't happen often, if at all, so it's not worth optimizing for... */ + mm->context = 0; + return; + } + + nbits = ia64_fls(size + 0xfff); + if (((1UL << nbits) & SUPPORTED_PGBITS) == 0) { + if (nbits > _PAGE_SIZE_256M) + nbits = _PAGE_SIZE_256M; + else + /* + * Some page sizes are not implemented in the + * IA-64 arch, so if we get asked to clear an + * unsupported page size, round up to the + * nearest page size. Note that we depend on + * the fact that if page size N is not + * implemented, 2*N _is_ implemented. + */ + ++nbits; + if (((1UL << nbits) & SUPPORTED_PGBITS) == 0) + panic("flush_tlb_range: BUG: nbits=%lu\n", nbits); + } + start &= ~((1UL << nbits) - 1); + + spin_lock(&ptcg_lock); + do { +#ifdef CONFIG_SMP + __asm__ __volatile__ ("ptc.g %0,%1;;srlz.i;;" + :: "r"(start), "r"(nbits<<2) : "memory"); +#else + __asm__ __volatile__ ("ptc.l %0,%1" :: "r"(start), "r"(nbits<<2) : "memory"); +#endif + start += (1UL << nbits); + } while (start < end); + spin_unlock(&ptcg_lock); + ia64_insn_group_barrier(); + ia64_srlz_i(); /* srlz.i implies srlz.d */ + ia64_insn_group_barrier(); +} + +void +ia64_tlb_init (void) +{ + ia64_get_ptce(&ia64_ptce_info); + __flush_tlb_all(); /* nuke left overs from bootstrapping... */ +} diff --git a/arch/ia64/sn/Makefile b/arch/ia64/sn/Makefile new file mode 100644 index 000000000..3c8810967 --- /dev/null +++ b/arch/ia64/sn/Makefile @@ -0,0 +1,25 @@ +# +# ia64/sn/Makefile +# +# Copyright (C) 1999 Silicon Graphics, Inc. +# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) +# + +CFLAGS := $(CFLAGS) -DCONFIG_SGI_SN1 -DSN1 -DSN -DSOFTSDV \ + -DLANGUAGE_C=1 -D_LANGUAGE_C=1 +AFLAGS := $(AFLAGS) -DCONFIG_SGI_SN1 -DSN1 -DSOFTSDV + +.S.s: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -E -o $*.s $< +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o $*.o $< + +all: sn.a + +O_TARGET = sn.a +O_HEADERS = +O_OBJS = sn1/sn1.a + +clean:: + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/sn/sn1/Makefile b/arch/ia64/sn/sn1/Makefile new file mode 100644 index 000000000..23758c473 --- /dev/null +++ b/arch/ia64/sn/sn1/Makefile @@ -0,0 +1,29 @@ +# +# ia64/platform/sn/sn1/Makefile +# +# Copyright (C) 1999 Silicon Graphics, Inc. +# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) +# + +CFLAGS := $(CFLAGS) -DCONFIG_SGI_SN1 -DSN1 -DSN -DSOFTSDV \ + -DLANGUAGE_C=1 -D_LANGUAGE_C=1 +AFLAGS := $(AFLAGS) -DCONFIG_SGI_SN1 -DSN1 -DSOFTSDV + +.S.s: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -E -o $*.s $< +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o $*.o $< + +all: sn1.a + +O_TARGET = sn1.a +O_HEADERS = +O_OBJS = irq.o setup.o + +ifeq ($(CONFIG_IA64_GENERIC),y) +O_OBJS += machvec.o +endif + +clean:: + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/sn/sn1/irq.c b/arch/ia64/sn/sn1/irq.c new file mode 100644 index 000000000..df8e56943 --- /dev/null +++ b/arch/ia64/sn/sn1/irq.c @@ -0,0 +1,50 @@ +#include <linux/kernel.h> + +#include <asm/irq.h> +#include <asm/ptrace.h> + +static int +sn1_startup_irq(unsigned int irq) +{ + return(0); +} + +static void +sn1_shutdown_irq(unsigned int irq) +{ +} + +static void +sn1_disable_irq(unsigned int irq) +{ +} + +static void +sn1_enable_irq(unsigned int irq) +{ +} + +static int +sn1_handle_irq(unsigned int irq, struct pt_regs *regs) +{ + return(0); +} + +struct hw_interrupt_type irq_type_sn1 = { + "sn1_irq", + sn1_startup_irq, + sn1_shutdown_irq, + sn1_handle_irq, + sn1_enable_irq, + sn1_disable_irq +}; + +void +sn1_irq_init (struct irq_desc desc[NR_IRQS]) +{ + int i; + + for (i = IA64_MIN_VECTORED_IRQ; i <= IA64_MAX_VECTORED_IRQ; ++i) { + irq_desc[i].handler = &irq_type_sn1; + } +} diff --git a/arch/ia64/sn/sn1/machvec.c b/arch/ia64/sn/sn1/machvec.c new file mode 100644 index 000000000..2e36b2e08 --- /dev/null +++ b/arch/ia64/sn/sn1/machvec.c @@ -0,0 +1,4 @@ +#include <asm/machvec_init.h> +#include <asm/machvec_sn1.h> + +MACHVEC_DEFINE(sn1) diff --git a/arch/ia64/sn/sn1/setup.c b/arch/ia64/sn/sn1/setup.c new file mode 100644 index 000000000..1e3a39ae3 --- /dev/null +++ b/arch/ia64/sn/sn1/setup.c @@ -0,0 +1,77 @@ +/* + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Vijay Chander(vijay@engr.sgi.com) + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/kdev_t.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/timex.h> +#include <linux/sched.h> + +#include <asm/io.h> +#include <asm/machvec.h> +#include <asm/system.h> +#include <asm/processor.h> + + +/* + * The format of "screen_info" is strange, and due to early i386-setup + * code. This is just enough to make the console code think we're on a + * VGA color display. + */ +struct screen_info sn1_screen_info = { + orig_x: 0, + orig_y: 0, + orig_video_mode: 3, + orig_video_cols: 80, + orig_video_ega_bx: 3, + orig_video_lines: 25, + orig_video_isVGA: 1, + orig_video_points: 16 +}; + +/* + * This is here so we can use the CMOS detection in ide-probe.c to + * determine what drives are present. In theory, we don't need this + * as the auto-detection could be done via ide-probe.c:do_probe() but + * in practice that would be much slower, which is painful when + * running in the simulator. Note that passing zeroes in DRIVE_INFO + * is sufficient (the IDE driver will autodetect the drive geometry). + */ +char drive_info[4*16]; + +unsigned long +sn1_map_nr (unsigned long addr) +{ + return MAP_NR_SN1(addr); +} + +void +sn1_setup(char **cmdline_p) +{ + + ROOT_DEV = to_kdev_t(0x0301); /* default to first IDE drive */ + +#if !defined (CONFIG_IA64_SOFTSDV_HACKS) + /* + * Program the timer to deliver timer ticks. 0x40 is the I/O port + * address of PIT counter 0, 0x43 is the I/O port address of the + * PIT control word. + */ + request_region(0x40,0x20,"timer"); + outb(0x34, 0x43); /* Control word */ + outb(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8, 0x40); /* MSB */ + printk("PIT: LATCH at 0x%x%x for %d HZ\n", LATCH >> 8, LATCH & 0xff, HZ); +#endif +#ifdef __SMP__ + init_smp_config(); +#endif + screen_info = sn1_screen_info; +} diff --git a/arch/ia64/tools/Makefile b/arch/ia64/tools/Makefile new file mode 100644 index 000000000..0491ca943 --- /dev/null +++ b/arch/ia64/tools/Makefile @@ -0,0 +1,49 @@ +CFLAGS = -D__KERNEL__ -g -O2 -Wall -I$(TOPDIR)/include + +ifdef CONFIG_SMP + CFLAGS += -D__SMP__ +endif + +TARGET = $(TOPDIR)/include/asm-ia64/offsets.h + +all: + +clean: + rm -f print_offsets.s print_offsets offsets.h + +fastdep: offsets.h + @if ! cmp -s offsets.h ${TARGET}; then \ + echo "Updating ${TARGET}..."; \ + cp offsets.h ${TARGET}; \ + else \ + echo "${TARGET} is up to date"; \ + fi + +# +# If we're cross-compiling, we use the cross-compiler to translate +# print_offsets.c into an assembly file and then awk to translate this +# file into offsets.h. This avoids having to use a simulator to +# generate this file. This is based on an idea suggested by Asit +# Mallick. If we're running natively, we can of course just build +# print_offsets and run it. --davidm +# + +ifeq ($(CROSS_COMPILE),) + +offsets.h: print_offsets + ./print_offsets > offsets.h + +print_offsets: print_offsets.c + $(CC) $(CFLAGS) print_offsets.c -o $@ + +else + +offsets.h: print_offsets.s + $(AWK) -f print_offsets.awk $^ > $@ + +print_offsets.s: print_offsets.c + $(CC) $(CFLAGS) -S print_offsets.c -o $@ + +endif + +.PHONY: all diff --git a/arch/ia64/tools/print_offsets.awk b/arch/ia64/tools/print_offsets.awk new file mode 100644 index 000000000..5eb8bcb63 --- /dev/null +++ b/arch/ia64/tools/print_offsets.awk @@ -0,0 +1,70 @@ +BEGIN { + print "#ifndef _ASM_IA64_OFFSETS_H" + print "#define _ASM_IA64_OFFSETS_H" + print "/*" + print " * DO NOT MODIFY" + print " *" + print " * This file was generated by arch/ia64/tools/print_offsets.awk." + print " *" + print " */" + # + # This is a cheesy hack. Make sure that + # PF_PTRACED == 1<<PF_PTRACED_BIT. + # + print "#define PF_PTRACED_BIT 4" +} + +# look for .tab: +# stringz "name" +# data value +# sequence + +/.*[.]size/ { + inside_table = 0 +} + +/\/\/ end/ { + inside_table = 0 +} + +{ + if (inside_table) { + if ($1 == "//") getline; + name=$2 + getline + getline + if ($1 == "//") getline; + value=$2 + len = length(name) + name = substr(name, 2, len - 2) + len -= 2 + if (len == 0) + print "" + else { + len += 8 + if (len >= 40) { + space=" " + } else { + space="" + while (len < 40) { + len += 8 + space = space"\t" + } + } + printf("#define %s%s%lu\t/* 0x%lx */\n", name, space, value, value) + } + } +} + +/tab:/ { + inside_table = 1 +} + +/tab#:/ { + inside_table = 1 +} + +END { + print "" + print "#endif /* _ASM_IA64_OFFSETS_H */" +} diff --git a/arch/ia64/tools/print_offsets.c b/arch/ia64/tools/print_offsets.c new file mode 100644 index 000000000..85b15aae1 --- /dev/null +++ b/arch/ia64/tools/print_offsets.c @@ -0,0 +1,109 @@ +/* + * Utility to generate asm-ia64/offsets.h. + * + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * Note that this file has dual use: when building the kernel + * natively, the file is translated into a binary and executed. When + * building the kernel in a cross-development environment, this file + * gets translated into an assembly file which, in turn, is processed + * by awk to generate offsets.h. So if you make any changes to this + * file, be sure to verify that the awk procedure still works (see + * prin_offsets.awk). + */ +#include <linux/sched.h> + +#include <asm-ia64/processor.h> +#include <asm-ia64/ptrace.h> +#include <asm-ia64/siginfo.h> +#include <asm-ia64/sigcontext.h> + +#ifdef offsetof +# undef offsetof +#endif + +/* + * We _can't_ include the host's standard header file, as those are in + * potential conflict with the what the Linux kernel declares for the + * target system. + */ +extern int printf (const char *, ...); + +#define offsetof(type,field) ((char *) &((type *) 0)->field - (char *) 0) + +struct + { + const char name[256]; + unsigned long value; + } +tab[] = + { + { "IA64_TASK_SIZE", sizeof (struct task_struct) }, + { "IA64_PT_REGS_SIZE", sizeof (struct pt_regs) }, + { "IA64_SWITCH_STACK_SIZE", sizeof (struct switch_stack) }, + { "IA64_SIGINFO_SIZE", sizeof (struct siginfo) }, + { "", 0 }, /* spacer */ + { "IA64_TASK_FLAGS_OFFSET", offsetof (struct task_struct, flags) }, + { "IA64_TASK_SIGPENDING_OFFSET", offsetof (struct task_struct, sigpending) }, + { "IA64_TASK_NEED_RESCHED_OFFSET", offsetof (struct task_struct, need_resched) }, + { "IA64_TASK_THREAD_OFFSET", offsetof (struct task_struct, thread) }, + { "IA64_TASK_THREAD_KSP_OFFSET", offsetof (struct task_struct, thread.ksp) }, + { "IA64_TASK_PID_OFFSET", offsetof (struct task_struct, pid) }, + { "IA64_TASK_MM_OFFSET", offsetof (struct task_struct, mm) }, + { "IA64_PT_REGS_CR_IPSR_OFFSET", offsetof (struct pt_regs, cr_ipsr) }, + { "IA64_PT_REGS_R12_OFFSET", offsetof (struct pt_regs, r12) }, + { "IA64_PT_REGS_R8_OFFSET", offsetof (struct pt_regs, r8) }, + { "IA64_PT_REGS_R16_OFFSET", offsetof (struct pt_regs, r16) }, + { "IA64_SWITCH_STACK_B0_OFFSET", offsetof (struct switch_stack, b0) }, + { "IA64_SWITCH_STACK_CALLER_UNAT_OFFSET", offsetof (struct switch_stack, caller_unat) }, + { "IA64_SIGCONTEXT_AR_BSP_OFFSET", offsetof (struct sigcontext, sc_ar_bsp) }, + { "IA64_SIGCONTEXT_AR_RNAT_OFFSET", offsetof (struct sigcontext, sc_ar_rnat) }, + { "IA64_SIGCONTEXT_FLAGS_OFFSET", offsetof (struct sigcontext, sc_flags) }, + { "IA64_SIGCONTEXT_CFM_OFFSET", offsetof (struct sigcontext, sc_cfm) }, + { "IA64_SIGCONTEXT_FR6_OFFSET", offsetof (struct sigcontext, sc_fr[6]) }, +}; + +static const char *tabs = "\t\t\t\t\t\t\t\t\t\t"; + +int +main (int argc, char **argv) +{ + const char *space; + int i, num_tabs; + size_t len; + + printf ("#ifndef _ASM_IA64_OFFSETS_H\n"); + printf ("#define _ASM_IA64_OFFSETS_H\n\n"); + + printf ("/*\n * DO NOT MODIFY\n *\n * This file was generated by " + "arch/ia64/tools/print_offsets.\n *\n */\n\n"); + + /* This is stretching things a bit, but entry.S needs the bit number + for PF_PTRACED and it can't include <linux/sched.h> so this seems + like a reasonably solution. At least the code won't break shoudl + PF_PTRACED ever change. */ + printf ("#define PF_PTRACED_BIT\t\t\t%u\n\n", ffs (PF_PTRACED) - 1); + + for (i = 0; i < sizeof (tab) / sizeof (tab[0]); ++i) + { + if (tab[i].name[0] == '\0') + printf ("\n"); + else + { + len = strlen (tab[i].name); + + num_tabs = (40 - len) / 8; + if (num_tabs <= 0) + space = " "; + else + space = strchr(tabs, '\0') - (40 - len) / 8; + + printf ("#define %s%s%lu\t/* 0x%lx */\n", + tab[i].name, space, tab[i].value, tab[i].value); + } + } + + printf ("\n#endif /* _ASM_IA64_OFFSETS_H */\n"); + return 0; +} diff --git a/arch/ia64/vmlinux.lds.S b/arch/ia64/vmlinux.lds.S new file mode 100644 index 000000000..b095baeb9 --- /dev/null +++ b/arch/ia64/vmlinux.lds.S @@ -0,0 +1,164 @@ +#include <linux/config.h> + +#include <asm/page.h> +#include <asm/system.h> + +OUTPUT_FORMAT("elf64-ia64-little") +OUTPUT_ARCH(ia64) +ENTRY(_start) +SECTIONS +{ + v = PAGE_OFFSET; /* this symbol is here to make debugging with kdb easier... */ + + . = KERNEL_START; + + _text = .; + _stext = .; + .text : AT(ADDR(.text) - PAGE_OFFSET) + { + *(__ivt_section) + /* these are not really text pages, but the zero page needs to be in a fixed location: */ + *(__special_page_section) + __start_gate_section = .; + *(__gate_section) + __stop_gate_section = .; + *(.text) + } + .text2 : AT(ADDR(.text2) - PAGE_OFFSET) + { *(.text2) } +#ifdef CONFIG_SMP + .text.lock : AT(ADDR(.text.lock) - PAGE_OFFSET) + { *(.text.lock) } +#endif + _etext = .; + + /* Exception table */ + . = ALIGN(16); + __start___ex_table = .; + __ex_table : AT(ADDR(__ex_table) - PAGE_OFFSET) + { *(__ex_table) } + __stop___ex_table = .; + +#if defined(CONFIG_KDB) + /* Kernel symbols and strings for kdb */ +# define KDB_MEAN_SYMBOL_SIZE 48 +# define KDB_SPACE (CONFIG_KDB_STBSIZE * KDB_MEAN_SYMBOL_SIZE) + . = ALIGN(8); + _skdb = .; + .kdb : AT(ADDR(.kdb) - PAGE_OFFSET) + { + *(kdbsymtab) + *(kdbstrings) + } + _ekdb = .; + . = _skdb + KDB_SPACE; +#endif + + /* Kernel symbol names for modules: */ + .kstrtab : AT(ADDR(.kstrtab) - PAGE_OFFSET) + { *(.kstrtab) } + + /* The initial task and kernel stack */ + . = ALIGN(PAGE_SIZE); + init_task : AT(ADDR(init_task) - PAGE_OFFSET) + { *(init_task) } + + /* Startup code */ + __init_begin = .; + .text.init : AT(ADDR(.text.init) - PAGE_OFFSET) + { *(.text.init) } + .data.init : AT(ADDR(.data.init) - PAGE_OFFSET) + { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : AT(ADDR(.setup.init) - PAGE_OFFSET) + { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : AT(ADDR(.initcall.init) - PAGE_OFFSET) + { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(PAGE_SIZE); + __init_end = .; + + .data.page_aligned : AT(ADDR(.data.page_aligned) - PAGE_OFFSET) + { *(.data.idt) } + + . = ALIGN(64); + .data.cacheline_aligned : AT(ADDR(.data.cacheline_aligned) - PAGE_OFFSET) + { *(.data.cacheline_aligned) } + + /* Global data */ + _data = .; + + .rodata : AT(ADDR(.rodata) - PAGE_OFFSET) + { *(.rodata) } + .opd : AT(ADDR(.opd) - PAGE_OFFSET) + { *(.opd) } + .data : AT(ADDR(.data) - PAGE_OFFSET) + { *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS } + + __gp = ALIGN (8) + 0x200000; + + .got : AT(ADDR(.got) - PAGE_OFFSET) + { *(.got.plt) *(.got) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : AT(ADDR(.sdata) - PAGE_OFFSET) + { *(.sdata) } + _edata = .; + _bss = .; + .sbss : AT(ADDR(.sbss) - PAGE_OFFSET) + { *(.sbss) *(.scommon) } + .bss : AT(ADDR(.bss) - PAGE_OFFSET) + { *(.bss) *(COMMON) } + . = ALIGN(64 / 8); + _end = .; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.text.exit) + *(.data.exit) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ + /* Discard them for now since Intel SoftSDV cannot handle them. + .comment 0 : { *(.comment) } + .note 0 : { *(.note) } + */ + /DISCARD/ : { *(.comment) } + /DISCARD/ : { *(.note) } +} diff --git a/arch/m68k/atari/joystick.c b/arch/m68k/atari/joystick.c index f8d965498..bf82e67ea 100644 --- a/arch/m68k/atari/joystick.c +++ b/arch/m68k/atari/joystick.c @@ -118,16 +118,11 @@ static unsigned int joystick_poll(struct file *file, poll_table *wait) } struct file_operations atari_joystick_fops = { - NULL, /* joystick_seek */ - read_joystick, - write_joystick, - NULL, /* joystick_readdir */ - joystick_poll, - NULL, /* joystick_ioctl */ - NULL, /* joystick_mmap */ - open_joystick, - NULL, /* flush */ - release_joystick + read: read_joystick, + write: write_joystick, + poll: joystick_poll, + open: open_joystick, + release: release_joystick, }; int __init atari_joystick_init(void) diff --git a/arch/m68k/bvme6000/rtc.c b/arch/m68k/bvme6000/rtc.c index 282a2871d..4a244f851 100644 --- a/arch/m68k/bvme6000/rtc.c +++ b/arch/m68k/bvme6000/rtc.c @@ -158,16 +158,9 @@ static int rtc_release(struct inode *inode, struct file *file) */ static struct file_operations rtc_fops = { - NULL, - NULL, - NULL, /* No write */ - NULL, /* No readdir */ - NULL, - rtc_ioctl, - NULL, /* No mmap */ - rtc_open, - NULL, /* flush */ - rtc_release + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, }; static struct miscdevice rtc_dev= diff --git a/arch/m68k/mac/adb-bus.c b/arch/m68k/mac/adb-bus.c index 850e9cb6f..23e98c048 100644 --- a/arch/m68k/mac/adb-bus.c +++ b/arch/m68k/mac/adb-bus.c @@ -2498,16 +2498,11 @@ static int adb_write(struct inode *inode, struct file *file, } static struct file_operations adb_fops = { - adb_lseek, - adb_read, - adb_write, - NULL, /* no readdir */ - NULL, /* no poll yet */ - NULL, /* no ioctl yet */ - NULL, /* no mmap */ - adb_open, - NULL, /* flush */ - adb_release + llseek: adb_lseek, + read: adb_read, + write: adb_write, + open: adb_open, + release: adb_release, }; int adbdev_register(int subtype, struct file_operations *fops) @@ -2683,16 +2678,11 @@ static int adb_write(struct inode *inode, struct file *file, } static struct file_operations adb_fops = { - adb_lseek, - adb_read, - adb_write, - NULL, /* no readdir */ - NULL, /* no select */ - NULL, /* no ioctl */ - NULL, /* no mmap */ - adb_open, - NULL, /* flush */ - adb_release + llseek: adb_lseek, + read: adb_read, + write: adb_write, + open: adb_open, + release: adb_release, }; static struct miscdevice adb_dev = { diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 5e7741ee1..4e6b28265 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -38,8 +38,6 @@ static unsigned long totalram_pages = 0; void mmu_emu_reserve_pages(unsigned long max_page); #endif -extern void show_net_buffers(void); - int do_check_pgt_cache(int low, int high) { int freed = 0; @@ -116,9 +114,6 @@ void show_mem(void) printk("%d pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif } extern void init_pointer_table(unsigned long ptable); diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c index f5f472132..a47e4c634 100644 --- a/arch/m68k/mvme16x/rtc.c +++ b/arch/m68k/mvme16x/rtc.c @@ -147,16 +147,9 @@ static int rtc_release(struct inode *inode, struct file *file) */ static struct file_operations rtc_fops = { - NULL, - NULL, - NULL, /* No write */ - NULL, /* No readdir */ - NULL, - rtc_ioctl, - NULL, /* No mmap */ - rtc_open, - NULL, /* flush */ - rtc_release + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, }; static struct miscdevice rtc_dev= diff --git a/arch/mips/config.in b/arch/mips/config.in index 51bd37b44..91b4c6675 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.39 2000/02/05 06:47:08 ralf Exp $ +# $Id: config.in,v 1.40 2000/02/21 15:05:48 ralf Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -175,33 +175,7 @@ comment 'SCSI support' tristate 'SCSI support' CONFIG_SCSI if [ "$CONFIG_SCSI" != "n" ]; then - if [ "$CONFIG_SGI_IP22" = "y" -o "$CONFIG_DECSTATION" = "y" ]; then - comment 'SCSI support type (disk, tape, CDrom)' - - dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI - dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI - dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI - dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI - - comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' - - bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN - - bool 'Verbose SCSI error reporting' CONFIG_SCSI_CONSTANTS - - #mainmenu_option next_comment - comment 'SCSI low-level drivers' - if [ "$CONFIG_SGI_IP22" = "y" ]; then - dep_tristate 'SGI wd93 Scsi Driver' CONFIG_SCSI_SGIWD93 $CONFIG_SCSI - else - if [ "$CONFIG_TC" = "y" ]; then - dep_tristate 'DEC NCR53C94 Scsi Driver' CONFIG_SCSI_DECNCR $CONFIG_SCSI - fi - dep_tristate 'DEC SII Scsi Driver' CONFIG_SCSI_DECSII $CONFIG_SCSI - fi - else - source drivers/scsi/Config.in - fi + source drivers/scsi/Config.in fi endmenu diff --git a/arch/mips/defconfig b/arch/mips/defconfig index 9cb48739f..7b1bc7f4c 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -153,23 +153,64 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_SCSI=y # -# SCSI support type (disk, tape, CDrom) +# SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 CONFIG_CHR_DEV_ST=y +CONFIG_ST_EXTRA_DEVS=2 CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_SR_EXTRA_DEVS=2 # CONFIG_CHR_DEV_SG is not set # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # +# CONFIG_SCSI_DEBUG_QUEUES is not set # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers # CONFIG_SCSI_SGIWD93=y +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_IMM is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set # # I2O device support @@ -264,7 +305,6 @@ CONFIG_AUTOFS4_FS=y # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_BFS_FS_WRITE is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set diff --git a/arch/mips/defconfig-ip22 b/arch/mips/defconfig-ip22 index 9cb48739f..7b1bc7f4c 100644 --- a/arch/mips/defconfig-ip22 +++ b/arch/mips/defconfig-ip22 @@ -153,23 +153,64 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_SCSI=y # -# SCSI support type (disk, tape, CDrom) +# SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 CONFIG_CHR_DEV_ST=y +CONFIG_ST_EXTRA_DEVS=2 CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_SR_EXTRA_DEVS=2 # CONFIG_CHR_DEV_SG is not set # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # +# CONFIG_SCSI_DEBUG_QUEUES is not set # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers # CONFIG_SCSI_SGIWD93=y +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_IMM is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set # # I2O device support @@ -264,7 +305,6 @@ CONFIG_AUTOFS4_FS=y # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_BFS_FS_WRITE is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index b6987cf42..bde21011f 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -1,20 +1,13 @@ -/* +/* $Id: entry.S,v 1.19 1999/12/08 22:05:10 harald Exp $ + * * Low level exception 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) 1994, 1995 by Ralf Baechle - * - * $Id: entry.S,v 1.18 1999/12/06 23:13:20 ralf Exp $ - */ - -/* - * 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. The ISA dependent TLB - * code is in arch/mips/<ISA-level>/<cputype>.S + * Copyright (C) 1994 - 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. */ #include <linux/config.h> #include <linux/sys.h> @@ -32,10 +25,6 @@ #include <asm/unistd.h> #include <asm/isadep.h> -/* - * Heia ... The %lo, %hi and %HI stuff is too strong for the ELF assembler - * and the ABI to cope with ... - */ .text .align 4 .set push @@ -45,8 +34,8 @@ EXPORT(ret_from_fork) jal schedule_tail j ret_from_sys_call -EXPORT(handle_bottom_half) - jal do_bottom_half +EXPORT(handle_softirq) + jal do_softirq b 9f reschedule: jal schedule @@ -54,10 +43,11 @@ reschedule: jal schedule EXPORT(ret_from_sys_call) EXPORT(ret_from_irq) .type ret_from_irq,@function - lw t0,bh_mask - lw t1,bh_active # unused delay slot - and t0,t1 - bnez t0,handle_bottom_half + lw t0, softirq_state + lw t1, softirq_state+4 # unused delay slot + and t0, t1 + bnez t0, handle_softirq + 9: lw t0,PT_STATUS(sp) # returning to kernel mode? lw t2, TASK_NEED_RESCHED($28) diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 92d125137..4e3ba5ad9 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.18 2000/01/26 00:07:44 ralf Exp $ +/* $Id: irq.c,v 1.19 2000/02/04 07:40:23 ralf Exp $ * * 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 @@ -210,6 +210,9 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) } irq_exit(cpu); + if (softirq_state[cpu].active&softirq_state[cpu].mask) + do_softirq(); + /* unmasking and bottom half handling is done magically for us. */ } diff --git a/arch/mips/kernel/scall_o32.S b/arch/mips/kernel/scall_o32.S index 5d7b42a2a..94931af0d 100644 --- a/arch/mips/kernel/scall_o32.S +++ b/arch/mips/kernel/scall_o32.S @@ -1,4 +1,4 @@ -/* $Id: scall_o32.S,v 1.8 1999/12/04 03:59:00 ralf Exp $ +/* $Id: scall_o32.S,v 1.9 2000/01/29 01:41:59 ralf Exp $ * * 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 @@ -87,8 +87,8 @@ o32_return: RESTORE_SOME RESTORE_SP_AND_RET -o32_handle_bottom_half: - jal do_bottom_half +o32_handle_softirq: + jal do_softirq b 9b o32_reschedule: SAVE_STATIC diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 8420085e0..36c26ce3d 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.24 2000/01/29 01:41:59 ralf Exp $ +/* $Id: init.c,v 1.25 2000/02/04 07:40:23 ralf Exp $ * * 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 @@ -250,7 +250,7 @@ extern char __init_begin, __init_end; void __init paging_init(void) { - unsigned int zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; unsigned long max_dma, low; /* Initialize the entire pgd. */ diff --git a/arch/mips64/defconfig b/arch/mips64/defconfig index 971b6aae6..039ffd84f 100644 --- a/arch/mips64/defconfig +++ b/arch/mips64/defconfig @@ -127,7 +127,9 @@ CONFIG_SCSI=y # SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 # CONFIG_CHR_DEV_ST is not set +CONFIG_ST_EXTRA_DEVS=2 # CONFIG_BLK_DEV_SR is not set # CONFIG_CHR_DEV_SG is not set @@ -178,6 +180,7 @@ CONFIG_BLK_DEV_SD=y # CONFIG_SCSI_QLOGIC_FAS is not set CONFIG_SCSI_QLOGIC_ISP=y # CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set # CONFIG_SCSI_SEAGATE is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_T128 is not set @@ -333,7 +336,6 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_BFS_FS_WRITE is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set diff --git a/arch/mips64/defconfig-ip22 b/arch/mips64/defconfig-ip22 index 43206114d..1d8f9c8f6 100644 --- a/arch/mips64/defconfig-ip22 +++ b/arch/mips64/defconfig-ip22 @@ -255,7 +255,6 @@ CONFIG_VT_CONSOLE=y # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_BFS_FS_WRITE is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set diff --git a/arch/mips64/defconfig-ip27 b/arch/mips64/defconfig-ip27 index 971b6aae6..039ffd84f 100644 --- a/arch/mips64/defconfig-ip27 +++ b/arch/mips64/defconfig-ip27 @@ -127,7 +127,9 @@ CONFIG_SCSI=y # SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 # CONFIG_CHR_DEV_ST is not set +CONFIG_ST_EXTRA_DEVS=2 # CONFIG_BLK_DEV_SR is not set # CONFIG_CHR_DEV_SG is not set @@ -178,6 +180,7 @@ CONFIG_BLK_DEV_SD=y # CONFIG_SCSI_QLOGIC_FAS is not set CONFIG_SCSI_QLOGIC_ISP=y # CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set # CONFIG_SCSI_SEAGATE is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_T128 is not set @@ -333,7 +336,6 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_BFS_FS_WRITE is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set diff --git a/arch/mips64/kernel/entry.S b/arch/mips64/kernel/entry.S index 1206a68c3..b869ed96b 100644 --- a/arch/mips64/kernel/entry.S +++ b/arch/mips64/kernel/entry.S @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.6 1999/11/23 17:12:49 ralf Exp $ +/* $Id: entry.S,v 1.4 2000/01/17 23:32:46 ralf Exp $ * * 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 @@ -6,18 +6,14 @@ * * Low level exception handling * - * Copyright (C) 1994 - 1999 by Ralf Baechle - * Copyright (C) 1999 Silicon Graphics + * Copyright (C) 1994 - 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics */ #include <asm/asm.h> #include <asm/regdef.h> #include <asm/mipsregs.h> #include <asm/stackframe.h> -/* - * Heia ... The %lo, %hi and %HI stuff is too strong for the ELF assembler - * and the ABI to cope with ... - */ .text .set noreorder .align 4 @@ -26,8 +22,8 @@ FEXPORT(ret_from_fork) move a0, v0 # prev j ret_from_sys_call nop -FEXPORT(handle_bottom_half) - jal do_bottom_half +FEXPORT(handle_softirq) + jal do_softirq nop b 9f nop @@ -37,10 +33,15 @@ reschedule: jal schedule FEXPORT(ret_from_sys_call) FEXPORT(ret_from_irq) - ld t0, bh_mask - ld t1, bh_active # unused delay slot +#ifdef __SMP__ +#error Barffff... +#else + la t1, softirq_state +#endif + lw t0, 0 (t1) + lw t1, 4 (t1) # unused delay slot and t0, t1 - bnez t0, handle_bottom_half + bnez t0, handle_softirq 9: ld t0, PT_STATUS(sp) # returning to kernel mode? andi t1, t0, 0x10 diff --git a/arch/mips64/kernel/scall_64.S b/arch/mips64/kernel/scall_64.S index 34ff47b4d..d739e50c1 100644 --- a/arch/mips64/kernel/scall_64.S +++ b/arch/mips64/kernel/scall_64.S @@ -1,4 +1,4 @@ -/* $Id: scall_64.S,v 1.5 2000/01/24 01:34:16 ralf Exp $ +/* $Id: scall_64.S,v 1.6 2000/02/18 00:24:30 ralf Exp $ * * 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 @@ -66,10 +66,10 @@ NESTED(handle_sys64, PT_SIZE, sp) 1: sd v0, PT_R2(sp) # result FEXPORT(ret_from_sys_call_64) - ld t0, bh_mask - ld t1, bh_active # unused delay slot + lw t0, softirq_state + lw t1, softirq_state+4 # unused delay slot and t0, t1 - bnez t0, handle_bottom_half_64 + bnez t0, handle_softirq_64 9: ld t0, PT_STATUS(sp) # returning to kernel mode? andi t1, t0, 0x10 @@ -90,8 +90,8 @@ return_64: eret .set mips0 -handle_bottom_half_64: - jal do_bottom_half +handle_softirq_64: + jal do_softirq b 9b reschedule_64: SAVE_STATIC diff --git a/arch/mips64/kernel/scall_o32.S b/arch/mips64/kernel/scall_o32.S index 57ec8bd53..4ee2cbace 100644 --- a/arch/mips64/kernel/scall_o32.S +++ b/arch/mips64/kernel/scall_o32.S @@ -1,4 +1,4 @@ -/* $Id: scall_o32.S,v 1.6 2000/02/05 06:47:09 ralf Exp $ +/* $Id: scall_o32.S,v 1.7 2000/02/18 00:24:30 ralf Exp $ * * 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 @@ -74,10 +74,10 @@ stack_done: 1: sd v0, PT_R2(sp) # result FEXPORT(o32_ret_from_sys_call) - ld t0,bh_mask - ld t1,bh_active # unused delay slot - and t0,t1 - bnez t0,o32_handle_bottom_half + lw t0, softirq_state + lw t1, softirq_state+4 # unused delay slot + and t0, t1 + bnez t0, o32_handle_softirq 9: ld t0,PT_STATUS(sp) # returning to kernel mode? andi t1, t0, 0x10 @@ -98,8 +98,8 @@ o32_return: eret .set mips0 -o32_handle_bottom_half: - jal do_bottom_half +o32_handle_softirq: + jal do_softirq b 9b o32_reschedule: SAVE_STATIC diff --git a/arch/mips64/mm/init.c b/arch/mips64/mm/init.c index 000ba906f..2ff7865ad 100644 --- a/arch/mips64/mm/init.c +++ b/arch/mips64/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.11 2000/02/04 07:40:24 ralf Exp $ +/* $Id: init.c,v 1.12 2000/02/10 02:03:59 kanoj Exp $ * * 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 @@ -40,8 +40,6 @@ unsigned long totalram_pages = 0; -extern void show_net_buffers(void); - void __bad_pte_kernel(pmd_t *pmd) { printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd)); @@ -332,9 +330,6 @@ void show_mem(void) printk("%ld pages in page table cache\n", pgtable_cache_size); printk("%d free pages\n", free); show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif } #ifndef CONFIG_DISCONTIGMEM @@ -345,7 +340,7 @@ extern char __init_begin, __init_end; void __init paging_init(void) { - unsigned int zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; unsigned long max_dma, low; /* Initialize the entire pgd. */ diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index d62c07669..7c3f13822 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -106,19 +106,24 @@ $(BOOT_TARGETS): $(CHECKS) vmlinux znetboot: $(CHECKS) vmlinux ifdef CONFIG_ALL_PPC ifdef CONFIG_SMP -ifdef CONFIG_PPC64 - cp -f vmlinux /tftpboot/vmlinux.smp.64 -else cp -f vmlinux /tftpboot/vmlinux.smp -endif -else -ifdef CONFIG_PPC64 - cp -f vmlinux /tftpboot/vmlinux.64 else cp -f vmlinux /tftpboot/vmlinux endif endif + @$(MAKECOFFBOOT) $@ + @$(MAKEBOOT) $@ + @$(MAKECHRPBOOT) $@ endif + +ifdef CONFIG_PPC64 +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKECOFFBOOT) $@ + @$(MAKEBOOT) $@ + @$(MAKECHRPBOOT) $@ + +znetboot: $(CHECKS) vmlinux + cp -f vmlinux /tftpboot/vmlinux.64 @$(MAKECOFFBOOT) $@ @$(MAKEBOOT) $@ @$(MAKECHRPBOOT) $@ @@ -129,31 +134,31 @@ clean_config: rm -f .config arch/ppc/defconfig gemini_config: clean_config - ln -s configs/gemini_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/gemini_defconfig arch/ppc/defconfig pmac_config: clean_config - ln -s configs/pmac_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/pmac_defconfig arch/ppc/defconfig prep_config: clean_config - ln -s configs/prep_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/prep_defconfig arch/ppc/defconfig chrp_config: clean_config - ln -s configs/chrp_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/chrp_defconfig arch/ppc/defconfig common_config: clean_config - ln -s configs/common_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/common_defconfig arch/ppc/defconfig mbx_config: clean_config - ln -s configs/mbx_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/mbx_defconfig arch/ppc/defconfig apus_config: clean_config - ln -s configs/apus_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/apus_defconfig arch/ppc/defconfig oak_config: clean_config - ln -s configs/oak_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/oak_defconfig arch/ppc/defconfig walnut_config: clean_config - ln -s configs/walnut_defconfig arch/ppc/defconfig + cp -f arch/ppc/configs/walnut_defconfig arch/ppc/defconfig archclean: rm -f arch/ppc/kernel/{mk_defs,ppc_defs.h,find_name,checks} diff --git a/arch/ppc/amiga/amiints.c b/arch/ppc/amiga/amiints.c index 75d90312b..b895e6c02 100644 --- a/arch/ppc/amiga/amiints.c +++ b/arch/ppc/amiga/amiints.c @@ -350,7 +350,8 @@ inline void amiga_do_irq(int irq, struct pt_regs *fp) void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server) { irq_node_t *node, *slow_nodes; - unsigned short flags, intena; + unsigned short intena; + unsigned long flags; kstat.irqs[0][SYS_IRQS + irq]++; if (server->count++) diff --git a/arch/ppc/amiga/cia.c b/arch/ppc/amiga/cia.c index 85133f8da..e420415a6 100644 --- a/arch/ppc/amiga/cia.c +++ b/arch/ppc/amiga/cia.c @@ -45,10 +45,10 @@ struct ciabase { do { \ if (irq >= IRQ_AMIGA_CIAB) { \ base = &ciab_base; \ - irq =- IRQ_AMIGA_CIAB; \ + irq -= IRQ_AMIGA_CIAB; \ } else { \ base = &ciaa_base; \ - irq =- IRQ_AMIGA_CIAA; \ + irq -= IRQ_AMIGA_CIAA; \ } \ } while (0) diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile index eb48313f4..6b8378839 100644 --- a/arch/ppc/boot/Makefile +++ b/arch/ppc/boot/Makefile @@ -14,7 +14,7 @@ .s.o: $(AS) -o $*.o $< .c.o: - $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -c -o $*.o $< + $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -D__BOOTER__ -c -o $*.o $< .S.s: $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.o $< .S.o: @@ -49,7 +49,7 @@ endif ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 -GZIP_FLAGS = -v9 +GZIP_FLAGS = -v9f OBJECTS := head.o misc.o ../coffboot/zlib.o CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS -fno-builtin @@ -73,6 +73,7 @@ zvmlinux.initrd: zvmlinux -DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd initrd` \ -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd image` \ -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd image` \ + -D__BOOTER__ \ -c -o misc.o misc.c $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ @@ -85,6 +86,10 @@ zImage: zvmlinux mkprep sImage ifdef CONFIG_PREP ./mkprep -pbp zvmlinux zImage endif +ifdef CONFIG_APUS + $(STRIP) ../../../vmlinux -o vmapus + gzip $(GZIP_FLAGS) vmapus +endif sImage: ../../../vmlinux ifdef CONFIG_GEMINI @@ -110,6 +115,7 @@ zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux image` \ -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux image` \ + -D__BOOTER__ \ -c -o misc.o misc.c $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ diff --git a/arch/ppc/chrpboot/Makefile b/arch/ppc/chrpboot/Makefile index 70ae95eb8..5a7f063fc 100644 --- a/arch/ppc/chrpboot/Makefile +++ b/arch/ppc/chrpboot/Makefile @@ -20,7 +20,7 @@ CFLAGS = $(CPPFLAGS) -O -fno-builtin -DSTDC_HEADERS LD_ARGS = -Ttext 0x00400000 OBJCOPY = $(CROSS_COMPILE)objcopy -OBJS = crt0.o start.o main.o misc.o ../coffboot/string.o ../coffboot/zlib.o image.o # initrd.o +OBJS = crt0.o start.o main.o misc.o ../coffboot/string.o ../coffboot/zlib.o image.o sysmap.o LIBS = $(TOPDIR)/lib/lib.a ifeq ($(CONFIG_PPC64),y) @@ -65,6 +65,9 @@ mknote: mknote.c image.o: piggyback ../coffboot/vmlinux.gz ./piggyback image < ../coffboot/vmlinux.gz | $(AS) -o image.o +sysmap.o: piggyback ../../../System.map + ./piggyback sysmap < ../../../System.map | $(AS) -o sysmap.o + initrd.o: ramdisk.image.gz piggyback ./piggyback initrd < ramdisk.image.gz | $(AS) -o initrd.o diff --git a/arch/ppc/chrpboot/main.c b/arch/ppc/chrpboot/main.c index bf506552a..d54a429a9 100644 --- a/arch/ppc/chrpboot/main.c +++ b/arch/ppc/chrpboot/main.c @@ -34,6 +34,8 @@ extern char image_data[]; extern int image_len; extern char initrd_data[]; extern int initrd_len; +extern char sysmap_data[]; +extern int sysmap_len; chrpboot(int a1, int a2, void *prom) @@ -78,12 +80,12 @@ chrpboot(int a1, int a2, void *prom) { struct bi_record *rec; - rec = (struct bi_record *)PAGE_ALIGN((unsigned long)dst+len); - + rec = (struct bi_record *)_ALIGN((unsigned long)dst+len+(1<<20)-1,(1<<20)); + rec->tag = BI_FIRST; rec->size = sizeof(struct bi_record); rec = (struct bi_record *)((unsigned long)rec + rec->size); - + rec->tag = BI_BOOTLOADER_ID; sprintf( (char *)rec->data, "chrpboot"); rec->size = sizeof(struct bi_record) + strlen("chrpboot") + 1; @@ -95,6 +97,11 @@ chrpboot(int a1, int a2, void *prom) rec->size = sizeof(struct bi_record) + sizeof(unsigned long); rec = (struct bi_record *)((unsigned long)rec + rec->size); + rec->tag = BI_SYSMAP; + rec->data[0] = sysmap_data; + rec->data[1] = sysmap_len; + rec->size = sizeof(struct bi_record) + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); rec->tag = BI_LAST; rec->size = sizeof(struct bi_record); rec = (struct bi_record *)((unsigned long)rec + rec->size); diff --git a/arch/ppc/coffboot/Makefile b/arch/ppc/coffboot/Makefile index 12032930d..29a4fdc35 100644 --- a/arch/ppc/coffboot/Makefile +++ b/arch/ppc/coffboot/Makefile @@ -36,6 +36,9 @@ TFTPIMAGE=/tftpboot/zImage.pmac$(MSIZE) endif ifeq ($(CONFIG_PMAC),y) +chrpmain.o: chrpmain.c + $(CC) $(CFLAGS) -DSYSMAP_OFFSET=0 -DSYSMAP_SIZE=0 -c chrpmain.c + hack-coff: hack-coff.c $(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c @@ -52,6 +55,12 @@ floppy: zImage # cp vmlinux.coff /mnt # umount /mnt +miboot.image: dummy.o vmlinux.gz + $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=vmlinux.gz dummy.o $@ + +miboot.image.initrd: dummy.o vmlinux.gz + $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=ramdisk.image.gz miboot.image $@ + coffboot: $(COFFOBJS) no_initrd.o ld.script $(LD) -o $@ $(COFF_LD_ARGS) $(COFFOBJS) no_initrd.o $(LIBS) @@ -82,16 +91,23 @@ vmlinux.coff.initrd: coffboot.initrd hack-coff vmlinux.elf: $(CHRPOBJS) no_initrd.o mknote $(LD) $(CHRP_LD_ARGS) -o $@ $(CHRPOBJS) no_initrd.o $(LIBS) ./mknote > note - $(OBJCOPY) $@ $@ --add-section=.note=note -R .comment + $(OBJCOPY) $@ $@ --add-section=.note=note \ + --add-section=sysmap=../../../System.map -R .comment + $(CC) $(CFLAGS) chrpmain.c -c -o chrpmain.o \ + -DSYSMAP_OFFSET=`sh ../boot/offset $(OBJDUMP) $@ sysmap` \ + -DSYSMAP_SIZE=`sh ../boot/size $(OBJDUMP) $@ sysmap` + $(LD) $(CHRP_LD_ARGS) -o $@ $(CHRPOBJS) no_initrd.o $(LIBS) + $(OBJCOPY) $@ $@ --add-section=.note=note \ + --add-section=sysmap=../../../System.map -R .comment vmlinux.elf.initrd: $(CHRPOBJS) initrd.o mknote $(LD) $(CHRP_LD_ARGS) -o $@ $(CHRPOBJS) initrd.o $(LIBS) ./mknote > note $(OBJCOPY) $@ $@ --add-section=.note=note -R .comment -zImage: vmlinux.coff vmlinux.elf +zImage: vmlinux.coff vmlinux.elf miboot.image -zImage.initrd: vmlinux.coff.initrd vmlinux.elf.initrd +zImage.initrd: vmlinux.coff.initrd vmlinux.elf.initrd miboot.image.initrd else znetboot: vmlinux.gz @@ -118,5 +134,7 @@ vmlinux.gz: $(TOPDIR)/vmlinux clean: rm -f hack-coff coffboot zImage vmlinux.coff vmlinux.gz + rm -f mknote piggyback vmlinux.elf note + rm -f miboot.image miboot.image.initrd fastdep: diff --git a/arch/ppc/coffboot/chrpmain.c b/arch/ppc/coffboot/chrpmain.c index bffb9d9ee..4d994d17e 100644 --- a/arch/ppc/coffboot/chrpmain.c +++ b/arch/ppc/coffboot/chrpmain.c @@ -92,8 +92,7 @@ boot(int a1, int a2, void *prom) void make_bi_recs(unsigned long addr) { struct bi_record *rec; - - rec = (struct bi_record *)PAGE_ALIGN(addr); + rec = (struct bi_record *)_ALIGN((unsigned long)addr+(1<<20)-1,(1<<20)); rec->tag = BI_FIRST; rec->size = sizeof(struct bi_record); @@ -109,7 +108,15 @@ void make_bi_recs(unsigned long addr) rec->data[1] = 1; rec->size = sizeof(struct bi_record) + sizeof(unsigned long); rec = (struct bi_record *)((unsigned long)rec + rec->size); - + +#ifdef SYSMAP_OFFSET + rec->tag = BI_SYSMAP; + rec->data[0] = SYSMAP_OFFSET; + rec->data[1] = SYSMAP_SIZE; + rec->size = sizeof(struct bi_record) + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); +#endif /* SYSMAP_OFFSET */ + rec->tag = BI_LAST; rec->size = sizeof(struct bi_record); rec = (struct bi_record *)((unsigned long)rec + rec->size); diff --git a/arch/ppc/coffboot/dummy.c b/arch/ppc/coffboot/dummy.c new file mode 100644 index 000000000..31dbf45bf --- /dev/null +++ b/arch/ppc/coffboot/dummy.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/arch/ppc/coffboot/main.c b/arch/ppc/coffboot/main.c index 56d29b84f..e6049b4a2 100644 --- a/arch/ppc/coffboot/main.c +++ b/arch/ppc/coffboot/main.c @@ -112,8 +112,8 @@ coffboot(int a1, int a2, void *prom) #endif { struct bi_record *rec; - - rec = (struct bi_record *)PAGE_ALIGN((unsigned long)dst+len); + + rec = (struct bi_record *)_ALIGN((unsigned long)dst+len+(1<<20)-1,(1<<20)); rec->tag = BI_FIRST; rec->size = sizeof(struct bi_record); diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 43a678c6d..8ea7e9000 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -15,7 +15,7 @@ mainmenu_option next_comment comment 'Platform support' define_bool CONFIG_PPC y choice 'Processor Type' \ - "6xx/7xx CONFIG_6xx \ + "6xx/7xx/7400 CONFIG_6xx \ 4xx CONFIG_4xx \ 630/Power3(64-Bit) CONFIG_PPC64 \ 82xx CONFIG_82xx \ @@ -97,6 +97,9 @@ fi if [ "$CONFIG_6xx" = "y" -a "$CONFIG_APUS" != "y" ]; then define_bool CONFIG_PCI y fi +if [ "$CONFIG_PREP" = "y" -o "$CONFIG_PMAC" = "y" -o "$CONFIG_CHRP" = "y" -o "$CONFIG_ALL_PPC" = "y"]; then + define_bool CONFIG_PCI y +fi bool 'Networking support' CONFIG_NET bool 'Sysctl support' CONFIG_SYSCTL @@ -140,6 +143,7 @@ if [ "$CONFIG_4xx" != "y" -a "$CONFIG_8xx" != "y" ]; then bool ' Include MacIO ADB driver' CONFIG_ADB_MACIO bool ' Include PMU (Powerbook) ADB driver' CONFIG_ADB_PMU bool 'Support for ADB keyboard' CONFIG_ADB_KEYBOARD + bool 'Support for ADB mouse' CONFIG_ADBMOUSE fi bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE bool 'Support for TotalImpact TotalMP' CONFIG_TOTALMP diff --git a/arch/ppc/configs/common_defconfig b/arch/ppc/configs/common_defconfig index 795aa5b83..ac258f16e 100644 --- a/arch/ppc/configs/common_defconfig +++ b/arch/ppc/configs/common_defconfig @@ -1,6 +1,7 @@ # # Automatically generated make config: don't edit # +# CONFIG_UID16 is not set # # Code maturity level options @@ -37,6 +38,7 @@ CONFIG_KMOD=y # # CONFIG_PCI is not set CONFIG_PCI=y +CONFIG_PCI=y CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -45,6 +47,7 @@ CONFIG_KCORE_ELF=y CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set # CONFIG_HOTPLUG is not set # CONFIG_PARPORT is not set CONFIG_VGA_CONSOLE=y @@ -58,6 +61,7 @@ CONFIG_ADB_CUDA=y CONFIG_ADB_MACIO=y CONFIG_ADB_PMU=y CONFIG_ADB_KEYBOARD=y +CONFIG_ADBMOUSE=y CONFIG_PROC_DEVICETREE=y # CONFIG_TOTALMP is not set CONFIG_BOOTX_TEXT=y @@ -84,7 +88,7 @@ CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set CONFIG_BLK_DEV_IDEFLOPPY=y -# CONFIG_BLK_DEV_IDESCSI is not set +CONFIG_BLK_DEV_IDESCSI=y # # IDE chipset support/bugfixes @@ -111,7 +115,6 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set # CONFIG_BLK_DEV_DAC960 is not set -CONFIG_PARIDE_PARPORT=y # CONFIG_PARIDE is not set CONFIG_BLK_DEV_IDE_MODES=y # CONFIG_BLK_DEV_HD is not set @@ -178,11 +181,12 @@ CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # +# CONFIG_SCSI_DEBUG_QUEUES is not set # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set @@ -244,6 +248,12 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 CONFIG_SCSI_MESH=y CONFIG_SCSI_MESH_SYNC_RATE=5 CONFIG_SCSI_MAC53C94=y +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set # # Network device support @@ -266,6 +276,7 @@ CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_BMAC=y # CONFIG_NCR885E is not set +# CONFIG_OAKNET is not set # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set @@ -407,17 +418,27 @@ CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 # +# I2C support +# +# CONFIG_I2C is not set + +# # Mice # CONFIG_BUSMOUSE=y # CONFIG_ATIXL_BUSMOUSE is not set # CONFIG_LOGIBUSMOUSE is not set # CONFIG_MS_BUSMOUSE is not set -# CONFIG_ADBMOUSE is not set +CONFIG_ADBMOUSE=y CONFIG_MOUSE=y CONFIG_PSMOUSE=y # CONFIG_82C710_MOUSE is not set # CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set # CONFIG_QIC02_TAPE is not set # @@ -431,11 +452,6 @@ CONFIG_NVRAM=y # Video For Linux # # CONFIG_VIDEO_DEV is not set - -# -# Joystick support -# -# CONFIG_JOYSTICK is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set @@ -445,9 +461,10 @@ CONFIG_NVRAM=y # # CONFIG_FTAPE is not set # CONFIG_DRM is not set +# CONFIG_AGP is not set # -# Support for USB +# USB support # CONFIG_USB=y @@ -456,31 +473,40 @@ CONFIG_USB=y # # CONFIG_USB_UHCI is not set CONFIG_USB_OHCI=y -CONFIG_USB_OHCI_DEBUG=y -# CONFIG_USB_OHCI_HCD is not set # # Miscellaneous USB options # -CONFIG_USB_DEBUG_ISOC=y -CONFIG_USB_PROC=y -# CONFIG_USB_EZUSB is not set +# CONFIG_USB_DEVICEFS is not set # # USB Devices # -CONFIG_USB_HUB=y -CONFIG_USB_MOUSE=y -CONFIG_USB_HP_SCANNER=m -CONFIG_USB_KBD=y +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_SCANNER is not set # CONFIG_USB_AUDIO is not set # CONFIG_USB_ACM is not set -# CONFIG_USB_PRINTER is not set # CONFIG_USB_SERIAL is not set # CONFIG_USB_CPIA is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_OV511 is not set # CONFIG_USB_DC2XX is not set CONFIG_USB_SCSI=m CONFIG_USB_SCSI_DEBUG=y +# CONFIG_USB_DABUSB is not set + +# +# USB HID +# +# CONFIG_USB_HID is not set +CONFIG_USB_KBD=y +CONFIG_USB_MOUSE=y +# CONFIG_USB_GRAPHIRE is not set +# CONFIG_USB_WMFORCE is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Filesystems @@ -493,9 +519,9 @@ CONFIG_AUTOFS_FS=y # CONFIG_BFS_FS is not set # CONFIG_FAT_FS is not set # CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set -# CONFIG_UDF_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_HPFS_FS is not set @@ -505,6 +531,7 @@ CONFIG_DEVPTS_FS=y # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set # CONFIG_UFS_FS is not set # @@ -513,7 +540,7 @@ CONFIG_EXT2_FS=y # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y CONFIG_NFSD=y -# CONFIG_NFSD_SUN is not set +# CONFIG_NFSD_V3 is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set @@ -525,8 +552,6 @@ CONFIG_LOCKD=y # CONFIG_PARTITION_ADVANCED is not set CONFIG_MAC_PARTITION=y CONFIG_MSDOS_PARTITION=y -# CONFIG_SGI_PARTITION is not set -# CONFIG_SUN_PARTITION is not set # CONFIG_NLS is not set # @@ -540,6 +565,7 @@ CONFIG_DMASOUND=y # CONFIG_SOUND_ESSSOLO1 is not set # CONFIG_SOUND_MAESTRO is not set # CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set CONFIG_SOUND_OSS=y diff --git a/arch/ppc/configs/gemini_defconfig b/arch/ppc/configs/gemini_defconfig index 9850f61d8..6fe267c9a 100644 --- a/arch/ppc/configs/gemini_defconfig +++ b/arch/ppc/configs/gemini_defconfig @@ -1,6 +1,7 @@ # # Automatically generated make config: don't edit # +# CONFIG_UID16 is not set # # Code maturity level options @@ -217,6 +218,11 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_MAC53C94 is not set # +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# # Network device support # CONFIG_NETDEVICES=y @@ -237,6 +243,7 @@ CONFIG_NET_ETHERNET=y # CONFIG_MACE is not set # CONFIG_BMAC is not set CONFIG_NCR885E=y +# CONFIG_OAKNET is not set # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set @@ -414,4 +421,4 @@ CONFIG_MSDOS_PARTITION=y # # CONFIG_MAGIC_SYSRQ is not set # CONFIG_KGDB is not set -CONFIG_XMON=y +# CONFIG_XMON is not set diff --git a/arch/ppc/configs/oak_defconfig b/arch/ppc/configs/oak_defconfig index 5fe9be376..deaf75f3b 100644 --- a/arch/ppc/configs/oak_defconfig +++ b/arch/ppc/configs/oak_defconfig @@ -18,7 +18,6 @@ CONFIG_4xx=y # CONFIG_8xx is not set CONFIG_OAK=y # CONFIG_WALNUT is not set -# CONFIG_PCI is not set # CONFIG_SMP is not set CONFIG_MACH_SPECIFIC=y # CONFIG_MATH_EMULATION is not set @@ -33,6 +32,7 @@ CONFIG_KMOD=y # # General setup # +# CONFIG_PCI is not set CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -235,6 +235,11 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_UNIX98_PTYS is not set # +# I2C support +# +# CONFIG_I2C is not set + +# # Mice # # CONFIG_BUSMOUSE is not set diff --git a/arch/ppc/configs/walnut_defconfig b/arch/ppc/configs/walnut_defconfig index 931e1a7a9..ad36925f3 100644 --- a/arch/ppc/configs/walnut_defconfig +++ b/arch/ppc/configs/walnut_defconfig @@ -18,7 +18,6 @@ CONFIG_4xx=y # CONFIG_8xx is not set # CONFIG_OAK is not set CONFIG_WALNUT=y -CONFIG_PCI=y # CONFIG_SMP is not set CONFIG_MACH_SPECIFIC=y # CONFIG_MATH_EMULATION is not set @@ -33,6 +32,7 @@ CONFIG_KMOD=y # # General setup # +CONFIG_PCI=y CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -235,6 +235,11 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_UNIX98_PTYS is not set # +# I2C support +# +CONFIG_I2C=y + +# # Mice # # CONFIG_BUSMOUSE is not set @@ -315,8 +320,8 @@ CONFIG_LOCKD=y # Partition Types # # CONFIG_PARTITION_ADVANCED is not set -CONFIG_MAC_PARTITION=y -CONFIG_MSDOS_PARTITION=y +# CONFIG_MAC_PARTITION is not set +# CONFIG_MSDOS_PARTITION is not set # CONFIG_SGI_PARTITION is not set # CONFIG_SUN_PARTITION is not set # CONFIG_NLS is not set diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig index 795aa5b83..ac258f16e 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -1,6 +1,7 @@ # # Automatically generated make config: don't edit # +# CONFIG_UID16 is not set # # Code maturity level options @@ -37,6 +38,7 @@ CONFIG_KMOD=y # # CONFIG_PCI is not set CONFIG_PCI=y +CONFIG_PCI=y CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -45,6 +47,7 @@ CONFIG_KCORE_ELF=y CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set # CONFIG_HOTPLUG is not set # CONFIG_PARPORT is not set CONFIG_VGA_CONSOLE=y @@ -58,6 +61,7 @@ CONFIG_ADB_CUDA=y CONFIG_ADB_MACIO=y CONFIG_ADB_PMU=y CONFIG_ADB_KEYBOARD=y +CONFIG_ADBMOUSE=y CONFIG_PROC_DEVICETREE=y # CONFIG_TOTALMP is not set CONFIG_BOOTX_TEXT=y @@ -84,7 +88,7 @@ CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set CONFIG_BLK_DEV_IDEFLOPPY=y -# CONFIG_BLK_DEV_IDESCSI is not set +CONFIG_BLK_DEV_IDESCSI=y # # IDE chipset support/bugfixes @@ -111,7 +115,6 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set # CONFIG_BLK_DEV_DAC960 is not set -CONFIG_PARIDE_PARPORT=y # CONFIG_PARIDE is not set CONFIG_BLK_DEV_IDE_MODES=y # CONFIG_BLK_DEV_HD is not set @@ -178,11 +181,12 @@ CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # +# CONFIG_SCSI_DEBUG_QUEUES is not set # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set @@ -244,6 +248,12 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 CONFIG_SCSI_MESH=y CONFIG_SCSI_MESH_SYNC_RATE=5 CONFIG_SCSI_MAC53C94=y +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set # # Network device support @@ -266,6 +276,7 @@ CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_BMAC=y # CONFIG_NCR885E is not set +# CONFIG_OAKNET is not set # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set @@ -407,17 +418,27 @@ CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 # +# I2C support +# +# CONFIG_I2C is not set + +# # Mice # CONFIG_BUSMOUSE=y # CONFIG_ATIXL_BUSMOUSE is not set # CONFIG_LOGIBUSMOUSE is not set # CONFIG_MS_BUSMOUSE is not set -# CONFIG_ADBMOUSE is not set +CONFIG_ADBMOUSE=y CONFIG_MOUSE=y CONFIG_PSMOUSE=y # CONFIG_82C710_MOUSE is not set # CONFIG_PC110_PAD is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set # CONFIG_QIC02_TAPE is not set # @@ -431,11 +452,6 @@ CONFIG_NVRAM=y # Video For Linux # # CONFIG_VIDEO_DEV is not set - -# -# Joystick support -# -# CONFIG_JOYSTICK is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set @@ -445,9 +461,10 @@ CONFIG_NVRAM=y # # CONFIG_FTAPE is not set # CONFIG_DRM is not set +# CONFIG_AGP is not set # -# Support for USB +# USB support # CONFIG_USB=y @@ -456,31 +473,40 @@ CONFIG_USB=y # # CONFIG_USB_UHCI is not set CONFIG_USB_OHCI=y -CONFIG_USB_OHCI_DEBUG=y -# CONFIG_USB_OHCI_HCD is not set # # Miscellaneous USB options # -CONFIG_USB_DEBUG_ISOC=y -CONFIG_USB_PROC=y -# CONFIG_USB_EZUSB is not set +# CONFIG_USB_DEVICEFS is not set # # USB Devices # -CONFIG_USB_HUB=y -CONFIG_USB_MOUSE=y -CONFIG_USB_HP_SCANNER=m -CONFIG_USB_KBD=y +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_SCANNER is not set # CONFIG_USB_AUDIO is not set # CONFIG_USB_ACM is not set -# CONFIG_USB_PRINTER is not set # CONFIG_USB_SERIAL is not set # CONFIG_USB_CPIA is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_OV511 is not set # CONFIG_USB_DC2XX is not set CONFIG_USB_SCSI=m CONFIG_USB_SCSI_DEBUG=y +# CONFIG_USB_DABUSB is not set + +# +# USB HID +# +# CONFIG_USB_HID is not set +CONFIG_USB_KBD=y +CONFIG_USB_MOUSE=y +# CONFIG_USB_GRAPHIRE is not set +# CONFIG_USB_WMFORCE is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set # # Filesystems @@ -493,9 +519,9 @@ CONFIG_AUTOFS_FS=y # CONFIG_BFS_FS is not set # CONFIG_FAT_FS is not set # CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set -# CONFIG_UDF_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_HPFS_FS is not set @@ -505,6 +531,7 @@ CONFIG_DEVPTS_FS=y # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set # CONFIG_UFS_FS is not set # @@ -513,7 +540,7 @@ CONFIG_EXT2_FS=y # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y CONFIG_NFSD=y -# CONFIG_NFSD_SUN is not set +# CONFIG_NFSD_V3 is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set @@ -525,8 +552,6 @@ CONFIG_LOCKD=y # CONFIG_PARTITION_ADVANCED is not set CONFIG_MAC_PARTITION=y CONFIG_MSDOS_PARTITION=y -# CONFIG_SGI_PARTITION is not set -# CONFIG_SUN_PARTITION is not set # CONFIG_NLS is not set # @@ -540,6 +565,7 @@ CONFIG_DMASOUND=y # CONFIG_SOUND_ESSSOLO1 is not set # CONFIG_SOUND_MAESTRO is not set # CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set CONFIG_SOUND_OSS=y diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 7aaacfadb..ea7c7c6e7 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -38,7 +38,7 @@ O_OBJS += hashtable.o endif ifdef CONFIG_PCI -O_OBJS += pci.o +O_OBJS += pci.o pci-dma.o endif ifdef CONFIG_KGDB @@ -59,8 +59,16 @@ endif ifeq ($(CONFIG_4xx),y) O_OBJS += ppc4xx_pic.o - ifeq ($(CONFIG_OAK),y) - O_OBJS += oak_setup.o +endif + +ifeq ($(CONFIG_OAK),y) + O_OBJS += oak_setup.o +endif + +ifeq ($(CONFIG_WALNUT),y) + O_OBJS += walnut_setup.o + ifeq ($(CONFIG_PCI),y) + O_OBJS += galaxy_pci.o endif endif @@ -83,6 +91,9 @@ endif ifeq ($(CONFIG_6xx),y) O_OBJS += open_pic.o indirect_pci.o endif +ifeq ($(CONFIG_PPC64),y) + O_OBJS += open_pic.o indirect_pci.o +endif ifeq ($(CONFIG_APUS),y) O_OBJS += apus_setup.o endif diff --git a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c index a7b057fa1..5f0c4b06e 100644 --- a/arch/ppc/kernel/apus_setup.c +++ b/arch/ppc/kernel/apus_setup.c @@ -19,7 +19,6 @@ #include <linux/sched.h> #include <linux/kd.h> #include <linux/init.h> -#include <linux/serial.h> #include <linux/hdreg.h> #include <linux/blk.h> #include <linux/pci.h> @@ -28,8 +27,41 @@ #include <asm/logging.h> #endif -/* Get the IDE stuff from the 68k file */ #include <linux/ide.h> +#define T_CHAR (0x0000) /* char: don't touch */ +#define T_SHORT (0x4000) /* short: 12 -> 21 */ +#define T_INT (0x8000) /* int: 1234 -> 4321 */ +#define T_TEXT (0xc000) /* text: 12 -> 21 */ + +#define T_MASK_TYPE (0xc000) +#define T_MASK_COUNT (0x3fff) + +#define D_CHAR(cnt) (T_CHAR | (cnt)) +#define D_SHORT(cnt) (T_SHORT | (cnt)) +#define D_INT(cnt) (T_INT | (cnt)) +#define D_TEXT(cnt) (T_TEXT | (cnt)) + +static u_short driveid_types[] = { + D_SHORT(10), /* config - vendor2 */ + D_TEXT(20), /* serial_no */ + D_SHORT(3), /* buf_type, buf_size - ecc_bytes */ + D_TEXT(48), /* fw_rev - model */ + D_CHAR(2), /* max_multsect - vendor3 */ + D_SHORT(1), /* dword_io */ + D_CHAR(2), /* vendor4 - capability */ + D_SHORT(1), /* reserved50 */ + D_CHAR(4), /* vendor5 - tDMA */ + D_SHORT(4), /* field_valid - cur_sectors */ + D_INT(1), /* cur_capacity */ + D_CHAR(2), /* multsect - multsect_valid */ + D_INT(1), /* lba_capacity */ + D_SHORT(194) /* dma_1word - reservedyy */ +}; + +#define num_driveid_types (sizeof(driveid_types)/sizeof(*driveid_types)) + +#if 0 /* Get rid of this crud */ +/* Get the IDE stuff from the 68k file */ #define ide_init_hwif_ports m68k_ide_init_hwif_ports #define ide_default_irq m68k_ide_default_irq #undef ide_request_irq @@ -57,6 +89,7 @@ #undef ide_release_region #undef ide_fix_driveid /*-------------------------------------------*/ +#endif #include <asm/bootinfo.h> #include <asm/setup.h> @@ -411,33 +444,6 @@ void kbd_reset_setup(char *str, int *ints) { } -#if defined(CONFIG_WHIPPET_SERIAL)||defined(CONFIG_MULTIFACE_III_TTY)||defined(CONFIG_GVPIOEXT)||defined(CONFIG_AMIGA_BUILTIN_SERIAL) - -long m68k_rs_init(void); -int m68k_register_serial(struct serial_struct *); -void m68k_unregister_serial(int); -long m68k_serial_console_init(long, long ); - -int rs_init(void) -{ - return m68k_rs_init(); -} -int register_serial(struct serial_struct *p) -{ - return m68k_register_serial(p); -} -void unregister_serial(int i) -{ - m68k_unregister_serial(i); -} -#ifdef CONFIG_SERIAL_CONSOLE -long serial_console_init(long kmem_start, long kmem_end) -{ - return m68k_serial_console_init(kmem_start, kmem_end); -} -#endif -#endif - /*********************************************************** FLOPPY */ #if defined(CONFIG_AMIGA_FLOPPY) __init @@ -673,7 +679,7 @@ apus_ide_outsw(ide_ioreg_t port, void *buf, int ns) int apus_ide_default_irq(ide_ioreg_t base) { - return m68k_ide_default_irq(base); + return 0; } ide_ioreg_t @@ -685,7 +691,7 @@ apus_ide_default_io_base(int index) int apus_ide_check_region(ide_ioreg_t from, unsigned int extent) { - return m68k_ide_check_region(from, extent); + return 0; } void @@ -693,27 +699,66 @@ apus_ide_request_region(ide_ioreg_t from, unsigned int extent, const char *name) { - m68k_ide_request_region(from, extent, name); } void apus_ide_release_region(ide_ioreg_t from, unsigned int extent) { - m68k_ide_release_region(from, extent); } void apus_ide_fix_driveid(struct hd_driveid *id) { - m68k_ide_fix_driveid(id); + u_char *p = (u_char *)id; + int i, j, cnt; + u_char t; + + if (!MACH_IS_AMIGA && !MACH_IS_MAC) + return; + for (i = 0; i < num_driveid_types; i++) { + cnt = driveid_types[i] & T_MASK_COUNT; + switch (driveid_types[i] & T_MASK_TYPE) { + case T_CHAR: + p += cnt; + break; + case T_SHORT: + for (j = 0; j < cnt; j++) { + t = p[0]; + p[0] = p[1]; + p[1] = t; + p += 2; + } + break; + case T_INT: + for (j = 0; j < cnt; j++) { + t = p[0]; + p[0] = p[3]; + p[3] = t; + t = p[1]; + p[1] = p[2]; + p[2] = t; + p += 4; + } + break; + case T_TEXT: + for (j = 0; j < cnt; j += 2) { + t = p[0]; + p[0] = p[1]; + p[1] = t; + p += 2; + } + break; + } + } } __init void apus_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) { - m68k_ide_init_hwif_ports(hw, data_port, ctrl_port, irq); + if (data_port || ctrl_port) + printk("apus_ide_init_hwif_ports: must not be called\n"); } #endif /****************************************************** IRQ stuff */ @@ -732,7 +777,7 @@ int apus_get_irq_list(char *buf) /* IPL must be between 0 and 7 */ __apus -static inline void apus_set_IPL(int ipl) +static inline void apus_set_IPL(unsigned long ipl) { APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT); APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); @@ -743,42 +788,22 @@ static inline void apus_set_IPL(int ipl) __apus static inline unsigned long apus_get_IPL(void) { - unsigned short __f; + /* This returns the present IPL emulation level. */ + unsigned long __f; APUS_READ(APUS_IPL_EMU, __f); return ((~__f) & IPLEMU_IPLMASK); } __apus -static inline unsigned long apus_get_prev_IPL(void) -{ - unsigned short __f; - APUS_READ(APUS_IPL_EMU, __f); - return ((~__f >> 3) & IPLEMU_IPLMASK); -} - - -__apus -static void apus_save_flags(unsigned long* flags) +static inline unsigned long apus_get_prev_IPL(struct pt_regs* regs) { - *flags = apus_get_IPL(); -} - -__apus -static void apus_restore_flags(unsigned long flags) -{ - apus_set_IPL(flags); -} - -__apus -static void apus_sti(void) -{ - apus_set_IPL(0); -} - -__apus -static void apus_cli(void) -{ - apus_set_IPL(7); + /* The value saved in mq is the IPL_EMU value at the time of + interrupt. The lower bits are the current interrupt level, + the upper bits the requested level. Thus, to restore the + IPL level to the post-interrupt state, we will need to use + the lower bits. */ + unsigned long __f = regs->mq; + return ((~__f) & IPLEMU_IPLMASK); } @@ -802,6 +827,22 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) return amiga_request_irq (irq, handler, irqflags, devname, dev_id); } + +/* In Linux/m68k the sys_request_irq deals with vectors 0-7. That's what + callers expect - but on Linux/APUS we actually use the IRQ_AMIGA_AUTO + vectors (24-31), so we put this dummy function in between to adjust + the vector argument (rather have cruft here than in the generic irq.c). */ +int sys_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + extern int request_sysirq(unsigned int irq, + void (*handler)(int, void *, + struct pt_regs *), + unsigned long irqflags, + const char * devname, void *dev_id); + return request_sysirq(irq+IRQ_AMIGA_AUTO, handler, irqflags, + devname, dev_id); +} #endif __apus @@ -809,14 +850,17 @@ int apus_get_irq(struct pt_regs* regs) { #ifdef CONFIG_APUS int level = apus_get_IPL(); - unsigned short ints = custom.intreqr & custom.intenar; + +#ifdef __INTERRUPT_DEBUG + printk("<%d:%d>", level, apus_get_prev_IPL(regs)); +#endif if (0 == level) - return -1; + return -8; if (7 == level) - return -2; + return -9; - return level; + return level + IRQ_AMIGA_AUTO; #else return 0; #endif @@ -824,10 +868,13 @@ int apus_get_irq(struct pt_regs* regs) __apus -void apus_post_irq(int level) +void apus_post_irq(struct pt_regs* regs, int level) { +#ifdef __INTERRUPT_DEBUG + printk("{%d}", apus_get_prev_IPL(regs)); +#endif /* Restore IPL to the previous value */ - apus_set_IPL(apus_get_IPL()); + apus_set_IPL(apus_get_prev_IPL(regs)); } @@ -903,11 +950,28 @@ irq_node_t *new_irq_node(void) return NULL; } +extern void amiga_enable_irq(unsigned int irq); +extern void amiga_disable_irq(unsigned int irq); + +struct hw_interrupt_type amiga_irqctrl = { + " Amiga ", + NULL, + NULL, + amiga_enable_irq, + amiga_disable_irq, + 0, + 0 +}; + + __init void apus_init_IRQ(void) { int i; + for ( i = 0 ; i < NR_IRQS ; i++ ) + irq_desc[i].ctl = &amiga_irqctrl; + for (i = 0; i < NUM_IRQ_NODES; i++) nodes[i].handler = NULL; @@ -919,10 +983,10 @@ void apus_init_IRQ(void) amiga_init_IRQ(); - int_control.int_sti = apus_sti; - int_control.int_cli = apus_cli; - int_control.int_save_flags = apus_save_flags; - int_control.int_restore_flags = apus_restore_flags; + int_control.int_sti = __no_use_sti; + int_control.int_cli = __no_use_cli; + int_control.int_save_flags = __no_use_save_flags; + int_control.int_restore_flags = __no_use_restore_flags; } __init diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index b93bc45f7..a2fbe5f14 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -273,14 +273,13 @@ void __init chrp_pcibios_fixup(void) { struct pci_dev *dev; - - /* some of IBM chrps have > 1 bus */ - if ( !strncmp("IBM", get_property(find_path_device("/"), - "name", NULL),3) ) - { - - } - + int i; + extern struct pci_ops generic_pci_ops; + + /* Some IBM's with the python have >1 bus, this finds them */ + for ( i = 0; i < python_busnr ; i++ ) + pci_scan_bus(i+1, &generic_pci_ops, NULL); + /* PCI interrupts are controlled by the OpenPIC */ pci_for_each_dev(dev) { if ( dev->irq ) diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index e76aa8dd9..21abfc84f 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -249,7 +249,6 @@ chrp_setup_arch(void) else #endif ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ -sprintf(cmd_line, "console=ttyS0,9600 console=tty0"); printk("Boot arguments: %s\n", cmd_line); request_region(0x20,0x20,"pic1"); @@ -384,7 +383,7 @@ int chrp_get_irq( struct pt_regs *regs ) return irq; } -void chrp_post_irq(int irq) +void chrp_post_irq(struct pt_regs* regs, int irq) { /* * If it's an i8259 irq then we've already done the @@ -394,7 +393,7 @@ void chrp_post_irq(int irq) * We do it this way since our irq_desc[irq].handler can change * with RTL and no longer be open_pic -- Cort */ - if ( irq >= open_pic.irq_offset) + if ( irq >= open_pic_irq_offset) openpic_eoi( smp_processor_id() ); } @@ -411,10 +410,11 @@ void __init chrp_init_IRQ(void) (*(unsigned long *)get_property(np, "8259-interrupt-acknowledge", NULL)); } - open_pic.irq_offset = 16; + open_pic_irq_offset = 16; for ( i = 16 ; i < NR_IRQS ; i++ ) irq_desc[i].handler = &open_pic; openpic_init(1); + enable_irq(IRQ_8259_CASCADE); for ( i = 0 ; i < 16 ; i++ ) irq_desc[i].handler = &i8259_pic; i8259_init(); diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c index 50c7417fb..d55fa24c0 100644 --- a/arch/ppc/kernel/chrp_time.c +++ b/arch/ppc/kernel/chrp_time.c @@ -171,9 +171,10 @@ void __init chrp_calibrate_decr(void) if (fp != 0) freq = *fp; } - freq *= 60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %lu/%d\n", freq, divisor); + freq *= 30; + divisor = 30; + printk("time_init: decrementer frequency = %lu/%d (%d MHz)\n", freq, + divisor, (freq/divisor)>>20); decrementer_count = freq / HZ / divisor; count_period_num = divisor; count_period_den = freq / 1000000; diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S index 2c4ff5554..2d1238a6b 100644 --- a/arch/ppc/kernel/entry.S +++ b/arch/ppc/kernel/entry.S @@ -256,6 +256,15 @@ _GLOBAL(_switch) REST_8GPRS(23, r1) REST_GPR(31, r1) lwz r2,_NIP(r1) /* Restore environment */ + /* + * We need to hard disable here even if RTL is active since + * being interrupted after here trashes SRR{0,1} + * -- Cort + */ + mfmsr r0 /* Get current interrupt state */ + rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ + mtmsr r0 /* Update machine state */ + lwz r0,_MSR(r1) mtspr SRR0,r2 mtspr SRR1,r0 @@ -271,7 +280,7 @@ ret_from_smpfork: bl schedule_tail b ret_from_except #endif - + .globl ret_from_intercept ret_from_intercept: /* @@ -291,7 +300,7 @@ ret_from_intercept: .globl ret_from_except ret_from_except: -0: /* disable interrupts */ +0: /* disable interrupts */ lis r30,int_control@h ori r30,r30,int_control@l lwz r30,0(r30) @@ -342,16 +351,26 @@ do_bottom_half_ret: .globl do_signal_ret do_signal_ret: b 0b -8: addi r4,r1,INT_FRAME_SIZE /* size of frame */ +8: /* + * We need to hard disable here even if RTL is active since + * being interrupted after here trashes the SPRG2 + * -- Cort + */ + mfmsr r0 /* Get current interrupt state */ + rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ + mtmsr r0 /* Update machine state */ + + addi r4,r1,INT_FRAME_SIZE /* size of frame */ stw r4,THREAD+KSP(r2) /* save kernel stack pointer */ tophys(r3,r1) mtspr SPRG2,r3 /* phys exception stack pointer */ + b 11f 10: /* make sure we hard disable here, even if rtl is active -- Cort */ mfmsr r0 /* Get current interrupt state */ rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ sync /* Some chip revs have problems here... */ mtmsr r0 /* Update machine state */ - +11: lwz r2,_CTR(r1) lwz r0,_LINK(r1) mtctr r2 diff --git a/arch/ppc/kernel/feature.c b/arch/ppc/kernel/feature.c index a9a30396a..156eb187e 100644 --- a/arch/ppc/kernel/feature.c +++ b/arch/ppc/kernel/feature.c @@ -8,85 +8,144 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * + * BenH: Changed implementation to work on multiple registers + * polarity is also taken into account. Removed delay (now + * responsibility of the caller). Added spinlocks. + * */ +#include <linux/config.h> #include <linux/types.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <asm/errno.h> #include <asm/ohare.h> +#include <asm/heathrow.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/feature.h> -#define MAX_FEATURE_REGS 2 #undef DEBUG_FEATURE -static u32 feature_bits_pbook[] = { - 0, /* FEATURE_null */ - OH_SCC_RESET, /* FEATURE_Serial_reset */ - OH_SCC_ENABLE, /* FEATURE_Serial_enable */ - OH_SCCA_IO, /* FEATURE_Serial_IO_A */ - OH_SCCB_IO, /* FEATURE_Serial_IO_B */ - OH_FLOPPY_ENABLE, /* FEATURE_SWIM3_enable */ - OH_MESH_ENABLE, /* FEATURE_MESH_enable */ - OH_IDE_ENABLE, /* FEATURE_IDE_enable */ - OH_VIA_ENABLE, /* FEATURE_VIA_enable */ - OH_IDECD_POWER, /* FEATURE_CD_power */ - OH_BAY_RESET, /* FEATURE_Mediabay_reset */ - OH_BAY_ENABLE, /* FEATURE_Mediabay_enable */ - OH_BAY_PCI_ENABLE, /* FEATURE_Mediabay_PCI_enable */ - OH_BAY_IDE_ENABLE, /* FEATURE_Mediabay_IDE_enable */ - OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ - 0, /* FEATURE_BMac_reset */ - 0, /* FEATURE_BMac_IO_enable */ - 0, /* FEATURE_Modem_Reset -> guess... */ - OH_IDE_POWER, /* FEATURE_IDE_DiskPower -> guess... */ - OH_IDE_RESET /* FEATURE_IDE_Reset (0 based) -> guess... */ +#define MAX_FEATURE_CONTROLLERS 2 +#define MAX_FEATURE_OFFSET 0x50 +#define FREG(c,r) (&(((c)->reg)[(r)>>2])) + +typedef struct feature_bit { + int reg; /* reg. offset from mac-io base */ + unsigned int polarity; /* 0 = normal, 1 = inverse */ + unsigned int mask; /* bit mask */ +} fbit; + +/* I don't have an OHare machine to test with, so I left those as they + * were. Someone with such a machine chould check out what OF says and + * try too see if they match the heathrow ones and should be changed too + */ +static fbit feature_bits_ohare_pbook[] = { + {0x38,0,0}, /* FEATURE_null */ + {0x38,0,OH_SCC_RESET}, /* FEATURE_Serial_reset */ + {0x38,0,OH_SCC_ENABLE}, /* FEATURE_Serial_enable */ + {0x38,0,OH_SCCA_IO}, /* FEATURE_Serial_IO_A */ + {0x38,0,OH_SCCB_IO}, /* FEATURE_Serial_IO_B */ + {0x38,0,OH_FLOPPY_ENABLE}, /* FEATURE_SWIM3_enable */ + {0x38,0,OH_MESH_ENABLE}, /* FEATURE_MESH_enable */ + {0x38,0,OH_IDE0_ENABLE}, /* FEATURE_IDE0_enable */ + {0x38,1,OH_IDE0_RESET_N}, /* FEATURE_IDE0_reset */ + {0x38,0,OH_IOBUS_ENABLE}, /* FEATURE_IOBUS_enable */ + {0x38,1,OH_BAY_RESET_N}, /* FEATURE_Mediabay_reset */ + {0x38,1,OH_BAY_POWER_N}, /* FEATURE_Mediabay_power */ + {0x38,0,OH_BAY_PCI_ENABLE}, /* FEATURE_Mediabay_PCI_enable */ + {0x38,0,OH_BAY_IDE_ENABLE}, /* FEATURE_Mediabay_IDE_enable */ + {0x38,1,OH_IDE1_RESET_N}, /* FEATURE_Mediabay_IDE_reset */ + {0x38,0,OH_BAY_FLOPPY_ENABLE}, /* FEATURE_Mediabay_floppy_enable */ + {0x38,0,0}, /* FEATURE_BMac_reset */ + {0x38,0,0}, /* FEATURE_BMac_IO_enable */ + {0x38,0,0}, /* FEATURE_Modem_power */ + {0x38,0,0}, /* FEATURE_Slow_SCC_PCLK */ + {0x38,0,0}, /* FEATURE_Sound_Power */ + {0x38,0,0}, /* FEATURE_Sound_CLK_Enable */ + {0x38,0,0}, /* FEATURE_IDE2_enable */ + {0x38,0,0}, /* FEATURE_IDE2_reset */ }; -/* assume these are the same as the ohare until proven otherwise */ -static u32 feature_bits_heathrow[] = { - 0, /* FEATURE_null */ - OH_SCC_RESET, /* FEATURE_Serial_reset */ - OH_SCC_ENABLE, /* FEATURE_Serial_enable */ - OH_SCCA_IO, /* FEATURE_Serial_IO_A */ - OH_SCCB_IO, /* FEATURE_Serial_IO_B */ - OH_FLOPPY_ENABLE, /* FEATURE_SWIM3_enable */ - OH_MESH_ENABLE, /* FEATURE_MESH_enable */ - OH_IDE_ENABLE, /* FEATURE_IDE_enable */ - OH_VIA_ENABLE, /* FEATURE_VIA_enable */ - OH_IDECD_POWER, /* FEATURE_CD_power */ - OH_BAY_RESET, /* FEATURE_Mediabay_reset */ - OH_BAY_ENABLE, /* FEATURE_Mediabay_enable */ - OH_BAY_PCI_ENABLE, /* FEATURE_Mediabay_PCI_enable */ - OH_BAY_IDE_ENABLE, /* FEATURE_Mediabay_IDE_enable */ - OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ - 0x80000000, /* FEATURE_BMac_reset */ - 0x60000000, /* FEATURE_BMac_IO_enable */ - 0x02000000, /* FEATURE_Modem_Reset -> guess...*/ - OH_IDE_POWER, /* FEATURE_IDE_DiskPower -> guess... */ - OH_IDE_RESET /* FEATURE_IDE_Reset (0 based) -> guess... */ +/* Those bits are from a PowerBook. It's possible that desktop machines + * based on heathrow need a different definition or some bits removed + */ +static fbit feature_bits_heathrow[] = { + {0x38,0,0}, /* FEATURE_null */ + {0x38,0,HRW_RESET_SCC}, /* FEATURE_Serial_reset */ + {0x38,0,HRW_SCC_ENABLE}, /* FEATURE_Serial_enable */ + {0x38,0,HRW_SCCA_IO}, /* FEATURE_Serial_IO_A */ + {0x38,0,HRW_SCCB_IO}, /* FEATURE_Serial_IO_B */ + {0x38,0,HRW_SWIM_ENABLE}, /* FEATURE_SWIM3_enable */ + {0x38,0,HRW_MESH_ENABLE}, /* FEATURE_MESH_enable */ + {0x38,0,HRW_IDE0_ENABLE}, /* FEATURE_IDE0_enable */ + {0x38,1,HRW_IDE0_RESET_N}, /* FEATURE_IDE0_reset */ + {0x38,0,HRW_IOBUS_ENABLE}, /* FEATURE_IOBUS_enable */ + {0x38,1,HRW_BAY_RESET_N}, /* FEATURE_Mediabay_reset */ + {0x38,1,HRW_BAY_POWER_N}, /* FEATURE_Mediabay_power */ + {0x38,0,HRW_BAY_PCI_ENABLE}, /* FEATURE_Mediabay_PCI_enable */ + {0x38,0,HRW_BAY_IDE_ENABLE}, /* FEATURE_Mediabay_IDE_enable */ + {0x38,1,HRW_IDE1_RESET_N}, /* FEATURE_Mediabay_IDE_reset */ + {0x38,0,HRW_BAY_FLOPPY_ENABLE}, /* FEATURE_Mediabay_floppy_enable */ + {0x38,0,HRW_BMAC_RESET}, /* FEATURE_BMac_reset */ + {0x38,0,HRW_BMAC_IO_ENABLE}, /* FEATURE_BMac_IO_enable */ + {0x38,1,HRW_MODEM_POWER_N}, /* FEATURE_Modem_power */ + {0x38,0,HRW_SLOW_SCC_PCLK}, /* FEATURE_Slow_SCC_PCLK */ + {0x38,1,HRW_SOUND_POWER_N}, /* FEATURE_Sound_Power */ + {0x38,0,HRW_SOUND_CLK_ENABLE}, /* FEATURE_Sound_CLK_Enable */ + {0x38,0,0}, /* FEATURE_IDE2_enable */ + {0x38,0,0}, /* FEATURE_IDE2_reset */ +}; + +/* Those bits are from an iBook. + */ +static fbit feature_bits_keylargo[] = { + {0x38,0,0}, /* FEATURE_null */ + {0x38,0,0}, /* FEATURE_Serial_reset */ + {0x38,0,0x00000054}, /* FEATURE_Serial_enable */ + {0x38,0,0}, /* FEATURE_Serial_IO_A */ + {0x38,0,0}, /* FEATURE_Serial_IO_B */ + {0x38,0,0}, /* FEATURE_SWIM3_enable */ + {0x38,0,0}, /* FEATURE_MESH_enable */ + {0x38,0,0}, /* FEATURE_IDE0_enable */ + {0x3c,1,0x01000000}, /* FEATURE_IDE0_reset */ + {0x38,0,0}, /* FEATURE_IOBUS_enable */ + {0x38,0,0}, /* FEATURE_Mediabay_reset */ + {0x38,0,0}, /* FEATURE_Mediabay_power */ + {0x38,0,0}, /* FEATURE_Mediabay_PCI_enable */ + {0x38,0,0}, /* FEATURE_Mediabay_IDE_enable */ + {0x3c,1,0x08000000}, /* FEATURE_Mediabay_IDE_reset */ + {0x38,0,0}, /* FEATURE_Mediabay_floppy_enable */ + {0x38,0,0}, /* FEATURE_BMac_reset */ + {0x38,0,0}, /* FEATURE_BMac_IO_enable */ + {0x40,1,0x02000000}, /* FEATURE_Modem_power */ + {0x38,0,0}, /* FEATURE_Slow_SCC_PCLK */ + {0x38,0,0}, /* FEATURE_Sound_Power */ + {0x38,0,0}, /* FEATURE_Sound_CLK_Enable */ + {0x38,0,0}, /* FEATURE_IDE2_enable */ + {0x3c,1,0x40000000}, /* FEATURE_IDE2_reset */ }; /* definition of a feature controller object */ -struct feature_controller -{ - u32* bits; +struct feature_controller { + fbit* bits; volatile u32* reg; struct device_node* device; + spinlock_t lock; }; /* static functions */ static void -feature_add_controller(struct device_node *controller_device, u32* bits); +feature_add_controller(struct device_node *controller_device, fbit* bits); -static int +static struct feature_controller* feature_lookup_controller(struct device_node *device); /* static varialbles */ -static struct feature_controller controllers[MAX_FEATURE_REGS]; +static struct feature_controller controllers[MAX_FEATURE_CONTROLLERS]; static int controller_count = 0; @@ -96,18 +155,23 @@ feature_init(void) struct device_node *np; np = find_devices("mac-io"); - while (np != NULL) - { - feature_add_controller(np, feature_bits_heathrow); + while (np != NULL) { + /* KeyLargo contains several (5 ?) FCR registers in mac-io, + * plus some gpio's which could eventually be handled here. + */ + if (device_is_compatible(np, "Keylargo")) { + feature_add_controller(np, feature_bits_keylargo); + } else { + feature_add_controller(np, feature_bits_heathrow); + } np = np->next; } if (controller_count == 0) { np = find_devices("ohare"); - if (np) - { + if (np) { if (find_devices("via-pmu") != NULL) - feature_add_controller(np, feature_bits_pbook); + feature_add_controller(np, feature_bits_ohare_pbook); else /* else not sure; maybe this is a Starmax? */ feature_add_controller(np, NULL); @@ -116,17 +180,26 @@ feature_init(void) if (controller_count) printk(KERN_INFO "Registered %d feature controller(s)\n", controller_count); + +#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_DMASOUND_MODULE + /* On PowerBooks, we disable the sound chip when dmasound is a module */ + if (controller_count && find_devices("via-pmu") != NULL) { + feature_clear(controllers[0].device, FEATURE_Sound_power); + feature_clear(controllers[0].device, FEATURE_Sound_CLK_enable); + } +#endif +#endif } static void -feature_add_controller(struct device_node *controller_device, u32* bits) +feature_add_controller(struct device_node *controller_device, fbit* bits) { struct feature_controller* controller; - if (controller_count >= MAX_FEATURE_REGS) - { + if (controller_count >= MAX_FEATURE_CONTROLLERS) { printk(KERN_INFO "Feature controller %s skipped(MAX:%d)\n", - controller_device->full_name, MAX_FEATURE_REGS); + controller_device->full_name, MAX_FEATURE_CONTROLLERS); return; } controller = &controllers[controller_count]; @@ -140,30 +213,32 @@ feature_add_controller(struct device_node *controller_device, u32* bits) } controller->reg = (volatile u32 *)ioremap( - controller_device->addrs[0].address + OHARE_FEATURE_REG, 4); + controller_device->addrs[0].address, MAX_FEATURE_OFFSET); if (bits == NULL) { printk(KERN_INFO "Twiddling the magic ohare bits\n"); - out_le32(controller->reg, STARMAX_FEATURES); + out_le32(FREG(controller,OHARE_FEATURE_REG), STARMAX_FEATURES); return; } + spin_lock_init(&controller->lock); + controller_count++; } -static int +static struct feature_controller* feature_lookup_controller(struct device_node *device) { int i; if (device == NULL) - return -EINVAL; + return NULL; while(device) { for (i=0; i<controller_count; i++) if (device == controllers[i].device) - return i; + return &controllers[i]; device = device->parent; } @@ -172,35 +247,36 @@ feature_lookup_controller(struct device_node *device) device->name); #endif - return -ENODEV; + return NULL; } int feature_set(struct device_node* device, enum system_feature f) { - int controller; - unsigned long flags; + struct feature_controller* controller; + unsigned long flags; + unsigned long value; + fbit* bit; if (f >= FEATURE_last) return -EINVAL; controller = feature_lookup_controller(device); - if (controller < 0) - return controller; + if (!controller) + return -ENODEV; + bit = &controller->bits[f]; #ifdef DEBUG_FEATURE printk("feature: <%s> setting feature %d in controller @0x%x\n", - device->name, (int)f, (unsigned int)controllers[controller].reg); + device->name, (int)f, (unsigned int)controller->reg); #endif - save_flags(flags); - cli(); - out_le32( controllers[controller].reg, - in_le32(controllers[controller].reg) | - controllers[controller].bits[f]); - (void)in_le32(controllers[controller].reg); - restore_flags(flags); - udelay(10); + spin_lock_irqsave(&controller->lock, flags); + value = in_le32(FREG(controller, bit->reg)); + value = bit->polarity ? (value & ~bit->mask) : (value | bit->mask); + out_le32(FREG(controller, bit->reg), value); + (void)in_le32(FREG(controller, bit->reg)); + spin_unlock_irqrestore(&controller->lock, flags); return 0; } @@ -208,29 +284,30 @@ feature_set(struct device_node* device, enum system_feature f) int feature_clear(struct device_node* device, enum system_feature f) { - int controller; - unsigned long flags; + struct feature_controller* controller; + unsigned long flags; + unsigned long value; + fbit* bit; if (f >= FEATURE_last) return -EINVAL; controller = feature_lookup_controller(device); - if (controller < 0) - return controller; + if (!controller) + return -ENODEV; + bit = &controller->bits[f]; #ifdef DEBUG_FEATURE printk("feature: <%s> clearing feature %d in controller @0x%x\n", - device->name, (int)f, (unsigned int)controllers[controller].reg); + device->name, (int)f, (unsigned int)controller->reg); #endif - save_flags(flags); - cli(); - out_le32( controllers[controller].reg, - in_le32(controllers[controller].reg) & - ~(controllers[controller].bits[f])); - (void)in_le32(controllers[controller].reg); - restore_flags(flags); - udelay(10); + spin_lock_irqsave(&controller->lock, flags); + value = in_le32(FREG(controller, bit->reg)); + value = bit->polarity ? (value | bit->mask) : (value & ~bit->mask); + out_le32(FREG(controller, bit->reg), value); + (void)in_le32(FREG(controller, bit->reg)); + spin_unlock_irqrestore(&controller->lock, flags); return 0; } @@ -238,16 +315,27 @@ feature_clear(struct device_node* device, enum system_feature f) int feature_test(struct device_node* device, enum system_feature f) { - int controller; + struct feature_controller* controller; + unsigned long value; + fbit* bit; if (f >= FEATURE_last) return -EINVAL; controller = feature_lookup_controller(device); - if (controller < 0) - return controller; + if (!controller) + return -ENODEV; + bit = &controller->bits[f]; - return (in_le32(controllers[controller].reg) & - controllers[controller].bits[f]) != 0; +#ifdef DEBUG_FEATURE + printk("feature: <%s> clearing feature %d in controller @0x%x\n", + device->name, (int)f, (unsigned int)controller->reg); +#endif + /* If one feature contains several bits, all of them must be set + * for value to be true, or all of them must be 0 if polarity is + * inverse + */ + value = (in_le32(FREG(controller, bit->reg)) & bit->mask); + return bit->polarity ? (value == 0) : (value == bit->mask); } diff --git a/arch/ppc/kernel/galaxy_pci.c b/arch/ppc/kernel/galaxy_pci.c new file mode 100644 index 000000000..aeddd9a0e --- /dev/null +++ b/arch/ppc/kernel/galaxy_pci.c @@ -0,0 +1,102 @@ +/* + * + * Copyright (c) 2000 Grant Erickson <grant@borg.umn.edu> + * All rights reserved. + * + * Module name: galaxy_pci.c + * + * Description: + * PCI interface code for the IBM PowerPC 405GP on-chip PCI bus + * interface. + * + * Why is this file called "galaxy_pci"? Because on the original + * IBM "Walnut" evaluation board schematic I have, the 405GP is + * is labeled "GALAXY". + * + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/string.h> +#include <linux/init.h> + +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/machdep.h> + +#include "pci.h" + + +/* Preprocessor Defines */ + +#define PCICFGADDR (volatile unsigned int *)(0xEEC00000) +#define PCICFGDATA (volatile unsigned int *)(0xEEC00004) + + +/* Function Prototypes */ + +decl_config_access_method(galaxy); + + +void __init +galaxy_pcibios_fixup(void) +{ + +} + +void __init +galaxy_setup_pci_ptrs(void) +{ + set_config_access_method(galaxy); + + ppc_md.pcibios_fixup = galaxy_pcibios_fixup; +} + +int +galaxy_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + + return (PCIBIOS_SUCCESSFUL); +} + +int +galaxy_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + + return (PCIBIOS_SUCCESSFUL); +} diff --git a/arch/ppc/kernel/gemini_setup.c b/arch/ppc/kernel/gemini_setup.c index d7ca91780..fcf3a701c 100644 --- a/arch/ppc/kernel/gemini_setup.c +++ b/arch/ppc/kernel/gemini_setup.c @@ -53,7 +53,7 @@ static unsigned int cpu_6xx[16] = { }; int chrp_get_irq(struct pt_regs *); -void chrp_post_irq(int); +void chrp_post_irq(struct pt_regs* regs, int); static inline unsigned long _get_HID1(void) { @@ -132,6 +132,19 @@ extern unsigned long loops_per_sec; extern int root_mountflags; extern char cmd_line[]; +void +gemini_heartbeat(void) +{ + static unsigned long led = GEMINI_LEDBASE+(4*8); + static char direction = 8; + *(char *)led = 0; + if ( (led + direction) > (GEMINI_LEDBASE+(7*8)) || + (led + direction) < (GEMINI_LEDBASE+(4*8)) ) + direction *= -1; + led += direction; + *(char *)led = 0xff; + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; +} void __init gemini_setup_arch(void) { @@ -175,6 +188,10 @@ void __init gemini_setup_arch(void) printk("CPU manufacturer: %s [rev=%04x]\n", (cpu & (1<<15)) ? "IBM" : "Motorola", (cpu & 0xffff)); + ppc_md.heartbeat = gemini_heartbeat; + ppc_md.heartbeat_reset = HZ/8; + ppc_md.heartbeat_count = 1; + /* take special pains to map the MPIC, since it isn't mapped yet */ gemini_openpic_init(); /* start the L2 */ @@ -505,7 +522,7 @@ int gemini_get_irq( struct pt_regs *regs ) return irq; } -void gemini_post_irq(int irq) +void gemini_post_irq(struct pt_regs* regs, int irq) { /* * If it's an i8259 irq then we've already done the diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index a70ba8bfd..8b56c635c 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -156,6 +156,16 @@ __start: bl fix_mem_constants #endif /* CONFIG_APUS */ +#ifndef CONFIG_GEMINI +/* Switch MMU off, clear BATs and flush TLB. At this point, r3 contains + * the physical address we are running at, returned by prom_init() + */ +__after_prom_start: + bl mmu_off + bl clear_bats + bl flush_tlbs +#endif + /* * Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. From this point on we can't safely @@ -211,6 +221,11 @@ __start: mtspr DBAT0U,r11 /* bit in upper BAT register */ mtspr IBAT0L,r8 mtspr IBAT0U,r11 +#if 0 /* Useful debug code, please leave in for now so I don't have to + * look at docs when I need to setup a BAT ; + */ + bl setup_screen_bat +#endif 5: isync #ifndef CONFIG_APUS @@ -627,12 +642,8 @@ DataStoreTLBMiss: mtcrf 0x80,r3 rfi -/* Instruction address breakpoint exception (on 603/604) */ STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) - -/* System management exception (603?) */ - STD_EXCEPTION(0x1400, Trap_14, UnknownException) - + STD_EXCEPTION(0x1400, SMI, SMIException) STD_EXCEPTION(0x1500, Trap_15, UnknownException) STD_EXCEPTION(0x1600, Trap_16, UnknownException) STD_EXCEPTION(0x1700, Trap_17, TAUException) @@ -644,10 +655,7 @@ DataStoreTLBMiss: STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) - - /* Run mode exception */ STD_EXCEPTION(0x2000, RunMode, RunModeException) - STD_EXCEPTION(0x2100, Trap_21, UnknownException) STD_EXCEPTION(0x2200, Trap_22, UnknownException) STD_EXCEPTION(0x2300, Trap_23, UnknownException) @@ -911,12 +919,16 @@ giveup_fpu: * the kernel image to physical address 0. */ relocate_kernel: +#if 0 /* Is this still needed ? I don't think so. It breaks new + * boot-with-mmu-off stuff + */ lis r9,0x426f /* if booted from BootX, don't */ addi r9,r9,0x6f58 /* translate source addr */ cmpw r31,r9 /* (we have to on chrp) */ beq 7f rlwinm r4,r4,0,8,31 /* translate source address */ add r4,r4,r3 /* to region mapped with BATs */ +#endif 7: addis r9,r26,klimit@ha /* fetch klimit */ lwz r25,klimit@l(r9) addis r25,r25,-KERNELBASE@h @@ -1194,14 +1206,26 @@ enable_caches: cmpi 0,r9,4 /* check for 604 */ cmpi 1,r9,9 /* or 604e */ cmpi 2,r9,10 /* or mach5 */ + cmpi 3,r9,8 /* check for 750 (G3) */ + cmpi 4,r9,12 /* or 7400 (G4) */ cror 2,2,6 cror 2,2,10 bne 4f ori r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */ bne 2,5f ori r11,r11,HID0_BTCD + b 5f +4: + cror 14,14,18 + bne 3,6f + /* We should add ABE here if we want to use Store Gathering + * and other nifty bridge features + */ + ori r11,r11,HID0_SGE|HID0_BHTE|HID0_BTIC /* for g3/g4, enable */ + li r3,0 + mtspr ICTC,r3 5: mtspr HID0,r11 /* superscalar exec & br history tbl */ -4: blr +6: blr /* * Load stuff into the MMU. Intended to be called with @@ -1388,6 +1412,45 @@ clear_bats: #endif /* !defined(CONFIG_GEMINI) */ blr +#ifndef CONFIG_GEMINI +flush_tlbs: + lis r20, 0x1000 +1: addic. r20, r20, -0x1000 + tlbie r20 + blt 1b + sync + blr + +mmu_off: + addi r4, r3, __after_prom_start - _start + mfmsr r3 + andi. r0,r3,MSR_DR|MSR_IR /* MMU enabled? */ + beq 1f + ori r3,r3,MSR_DR|MSR_IR + xori r3,r3,MSR_DR|MSR_IR + mtspr SRR0,r4 + mtspr SRR1,r3 + sync + rfi +1: blr +#endif + +#if 0 /* That's useful debug stuff */ +setup_screen_bat: + lis r3, 0x9100 +#ifdef __SMP__ + ori r3,r3,0x12 +#else + ori r3,r3,0x2 +#endif + mtspr DBAT1L, r3 + mtspr IBAT1L, r3 + ori r3,r3,(BL_8M<<2)|0x2 /* set up BAT registers for 604 */ + mtspr DBAT1U, r3 + mtspr IBAT1U, r3 + blr +#endif + /* * We put a few things here that have to be page-aligned. * This stuff goes at the beginning of the data segment, diff --git a/arch/ppc/kernel/head_4xx.S b/arch/ppc/kernel/head_4xx.S index ba3284ad8..bec805b92 100644 --- a/arch/ppc/kernel/head_4xx.S +++ b/arch/ppc/kernel/head_4xx.S @@ -78,13 +78,17 @@ _GLOBAL(_start) li r24,0 + ## Invalidate all TLB entries + + tlbia + ## We should still be executing code at physical address 0x0000xxxx ## at this point. However, start_here is at virtual address ## 0xC000xxxx. So, set up a TLB mapping to cover this once ## translation is enabled. lis r3,KERNELBASE@h # Load the kernel virtual address - addis r3,r3,KERNELBASE@l + ori r3,r3,KERNELBASE@l tophys(r4,r3) # Load the kernel physical address ## Save the existing PID and load the kernel PID. @@ -96,11 +100,7 @@ _GLOBAL(_start) ## Configure and load entry into TLB slot 0. clrrwi r4,r4,10 # Mask off the real page number - - ## XXX - Temporarily set the TLB_I bit because of cache issues that - ## seem to foul-up the exception handling code. - - ori r4,r4,(TLB_WR | TLB_EX | TLB_I) # Set the write and execute bits + ori r4,r4,(TLB_WR | TLB_EX) # Set the write and execute bits clrrwi r3,r3,10 # Mask off the effective page number ori r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_16M)) @@ -333,22 +333,12 @@ _GLOBAL(timer_interrupt_intercept) #endif ### 0x1100 - Data TLB Miss Exception - - START_EXCEPTION(0x1100, DTLBMiss) - STND_EXCEPTION_PROLOG - addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC - li r20,MSR_KERNEL - FINISH_EXCEPTION(UnknownException) + + STND_EXCEPTION(0x1100, DTLBMiss, PPC4xx_dtlb_miss) ### 0x1200 - Instruction TLB Miss Exception - - START_EXCEPTION(0x1200, ITLBMiss) - STND_EXCEPTION_PROLOG - addi r3,r1,STACK_FRAME_OVERHEAD - li r7,STND_EXC - li r20,MSR_KERNEL - FINISH_EXCEPTION(UnknownException) + + STND_EXCEPTION(0x1200, ITLBMiss, PPC4xx_itlb_miss) STND_EXCEPTION(0x1300, Trap_13, UnknownException) STND_EXCEPTION(0x1400, Trap_14, UnknownException) @@ -560,8 +550,6 @@ start_here: _GLOBAL(set_context) mtspr SPRN_PID,r3 - tlbia - SYNC blr ### diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index 8dda6ad6b..7cf97b873 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -154,11 +154,12 @@ unsigned long get_zero_page_fast(void) if ( zero_quicklist ) { /* atomically remove this page from the list */ - asm ( "101:lwarx %1,0,%2\n" /* reserve zero_cache */ + register unsigned long tmp; + asm ( "101:lwarx %1,0,%3\n" /* reserve zero_cache */ " lwz %0,0(%1)\n" /* get next -- new zero_cache */ - " stwcx. %0,0,%2\n" /* update zero_cache */ + " stwcx. %0,0,%3\n" /* update zero_cache */ " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_quicklist), "=&r" (page) + : "=&r" (tmp), "=&r" (page), "+m" (zero_cache) : "r" (&zero_quicklist) : "cc" ); #ifdef __SMP__ @@ -193,6 +194,7 @@ void zero_paged(void) { unsigned long pageptr = 0; /* current page being zero'd */ unsigned long bytecount = 0; + register unsigned long tmp; pte_t *pte; if ( atomic_read(&zero_cache_sz) >= zero_cache_water[0] ) @@ -249,15 +251,14 @@ void zero_paged(void) pte_cache(*pte); flush_tlb_page(find_vma(&init_mm,pageptr),pageptr); /* atomically add this page to the list */ - asm ( "101:lwarx %0,0,%1\n" /* reserve zero_cache */ - " stw %0,0(%2)\n" /* update *pageptr */ + asm ( "101:lwarx %0,0,%2\n" /* reserve zero_cache */ + " stw %0,0(%3)\n" /* update *pageptr */ #ifdef __SMP__ " sync\n" /* let store settle */ #endif - " mr %0,%2\n" /* update zero_cache in reg */ - " stwcx. %2,0,%1\n" /* update zero_cache in mem */ + " stwcx. %3,0,%2\n" /* update zero_cache in mem */ " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_quicklist) + : "=&r" (tmp), "+m" (zero_quicklist) : "r" (&zero_quicklist), "r" (pageptr) : "cc" ); /* diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index c2f2d1c11..8b5f590fb 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -126,7 +126,7 @@ void irq_kfree(void *ptr) */ int request_8xxirq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), #elif defined(CONFIG_APUS) -int sys_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), +int request_sysirq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), #else int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), #endif @@ -315,7 +315,7 @@ asmlinkage int do_IRQ(struct pt_regs *regs, int isfake) } ppc_irq_dispatch_handler( regs, irq ); if ( ppc_md.post_irq ) - ppc_md.post_irq( irq ); + ppc_md.post_irq( regs, irq ); out: hardirq_exit( cpu ); diff --git a/arch/ppc/kernel/local_irq.h b/arch/ppc/kernel/local_irq.h index 602192013..840b14d6f 100644 --- a/arch/ppc/kernel/local_irq.h +++ b/arch/ppc/kernel/local_irq.h @@ -4,6 +4,8 @@ #include <linux/kernel_stat.h> #include <linux/interrupt.h> +#include <linux/cache.h> +#include <linux/spinlock.h> #include <linux/irq.h> void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index aa9f5ac83..fde7112c7 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -20,13 +20,13 @@ #include <asm/page.h> #include "ppc_asm.h" -#ifndef CONFIG_8xx -CACHE_LINE_SIZE = 32 -LG_CACHE_LINE_SIZE = 5 -#else +#if defined(CONFIG_4xx) || defined(CONFIG_8xx) CACHE_LINE_SIZE = 16 LG_CACHE_LINE_SIZE = 4 -#endif /* CONFIG_8xx */ +#else +CACHE_LINE_SIZE = 32 +LG_CACHE_LINE_SIZE = 5 +#endif /* CONFIG_4xx || CONFIG_8xx */ .text @@ -590,6 +590,20 @@ _GLOBAL(_set_THRM3) _GLOBAL(_get_PVR) mfspr r3,PVR blr + +_GLOBAL(_get_HID0) + mfspr r3,HID0 + blr + +_GLOBAL(_get_ICTC) + mfspr r3,ICTC + blr + +_GLOBAL(_set_ICTC) + mtspr ICTC,r3 + blr + + /* L2CR functions Copyright © 1997-1998 by PowerLogix R & D, Inc. @@ -656,6 +670,8 @@ _GLOBAL(_set_L2CR) rlwinm r4,r4,16,16,31 cmplwi r4,0x0008 beq thisIs750 + cmplwi r4,0x000c + beq thisIs750 li r3,-1 blr @@ -750,9 +766,11 @@ _GLOBAL(_get_L2CR) mfspr r3,PVR rlwinm r3,r3,16,16,31 cmplwi r3,0x0008 + beq 1f + cmplwi r3,0x000c li r3,0 bnelr - +1: /* Return the L2CR contents */ mfspr r3,L2CR blr diff --git a/arch/ppc/kernel/oak_setup.c b/arch/ppc/kernel/oak_setup.c index ad2c224bb..f3c142e2b 100644 --- a/arch/ppc/kernel/oak_setup.c +++ b/arch/ppc/kernel/oak_setup.c @@ -1,6 +1,6 @@ /* * - * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> * * Module name: oak_setup.c * @@ -19,6 +19,7 @@ #include <linux/interrupt.h> #include <linux/param.h> #include <linux/string.h> +#include <linux/blk.h> #include <asm/processor.h> #include <asm/board.h> @@ -30,6 +31,12 @@ #include "time.h" #include "oak_setup.h" + + + + + + /* Function Prototypes */ extern void abort(void); @@ -95,32 +102,32 @@ oak_init(unsigned long r3, unsigned long r4, unsigned long r5, /* Initialize machine-dependency vectors */ - ppc_md.setup_arch = oak_setup_arch; - ppc_md.setup_residual = oak_setup_residual; - ppc_md.get_cpuinfo = NULL; - ppc_md.irq_cannonicalize = NULL; - ppc_md.init_IRQ = oak_init_IRQ; - ppc_md.get_irq = oak_get_irq; - ppc_md.init = NULL; - - ppc_md.restart = oak_restart; - ppc_md.power_off = oak_power_off; - ppc_md.halt = oak_halt; - - ppc_md.time_init = oak_time_init; - ppc_md.set_rtc_time = oak_set_rtc_time; - ppc_md.get_rtc_time = oak_get_rtc_time; - ppc_md.calibrate_decr = oak_calibrate_decr; - - ppc_md.kbd_setkeycode = NULL; - ppc_md.kbd_getkeycode = NULL; - ppc_md.kbd_translate = NULL; - ppc_md.kbd_unexpected_up = NULL; - ppc_md.kbd_leds = NULL; - ppc_md.kbd_init_hw = NULL; + ppc_md.setup_arch = oak_setup_arch; + ppc_md.setup_residual = oak_setup_residual; + ppc_md.get_cpuinfo = NULL; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = oak_init_IRQ; + ppc_md.get_irq = oak_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = oak_restart; + ppc_md.power_off = oak_power_off; + ppc_md.halt = oak_halt; + + ppc_md.time_init = oak_time_init; + ppc_md.set_rtc_time = oak_set_rtc_time; + ppc_md.get_rtc_time = oak_get_rtc_time; + ppc_md.calibrate_decr = oak_calibrate_decr; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; #if defined(CONFIG_MAGIC_SYSRQ) - ppc_md.kbd_sysrq_xlate = NULL; + ppc_md.ppc_kbd_sysrq_xlate = NULL; #endif return; diff --git a/arch/ppc/kernel/oak_setup.h b/arch/ppc/kernel/oak_setup.h index 62cfac906..8648bd084 100644 --- a/arch/ppc/kernel/oak_setup.h +++ b/arch/ppc/kernel/oak_setup.h @@ -1,14 +1,14 @@ /* * - * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> * - * Module name: oak_setup.c + * Module name: oak_setup.h * * Description: * Architecture- / platform-specific boot-time initialization code for * the IBM PowerPC 403GCX "Oak" evaluation board. Adapted from original * code by Gary Thomas, Cort Dougan <cort@cs.nmt.edu>, and Dan Malek - * <dmalek@jlc.net>. + * <dan@netx4.com>. * */ diff --git a/arch/ppc/kernel/open_pic.c b/arch/ppc/kernel/open_pic.c index 6857aa36f..d4dbe05e5 100644 --- a/arch/ppc/kernel/open_pic.c +++ b/arch/ppc/kernel/open_pic.c @@ -8,6 +8,7 @@ * for more details. */ +#include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -17,17 +18,30 @@ #include <asm/signal.h> #include <asm/io.h> #include <asm/irq.h> +#include <asm/prom.h> #include "local_irq.h" volatile struct OpenPIC *OpenPIC = NULL; u_int OpenPIC_NumInitSenses __initdata = 0; u_char *OpenPIC_InitSenses __initdata = NULL; +int open_pic_irq_offset; +extern int use_of_interrupt_tree; void chrp_mask_irq(unsigned int); void chrp_unmask_irq(unsigned int); +void find_ISUs(void); static u_int NumProcessors; static u_int NumSources; +OpenPIC_Source *ISU; +/* + * We should use this if we have > 1 ISU. + * We can just point each entry to the + * appropriate source regs but it wastes a lot of space + * so until we have >1 ISU I'll leave it unimplemented. + * -- Cort +OpenPIC_Source ISU[128]; +*/ struct hw_interrupt_type open_pic = { " OpenPIC ", @@ -38,7 +52,6 @@ struct hw_interrupt_type open_pic = { 0, 0 }; -int open_pic_irq_offset; /* * Accesses to the current processor's registers @@ -96,7 +109,7 @@ void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) #endif /* __SMP__ */ #ifdef __i386__ -static inline u_int ld_le32(volatile u_int *addr) +static inline u_int in_le32(volatile u_int *addr) { return *addr; } @@ -111,7 +124,7 @@ u_int openpic_read(volatile u_int *addr) { u_int val; - val = ld_le32(addr); + val = in_le32(addr); return val; } @@ -148,6 +161,9 @@ static void openpic_safe_writefield(volatile u_int *addr, u_int mask, { openpic_setfield(addr, OPENPIC_MASK); /* wait until it's not in use */ + /* BenH: Is this code really enough ? I would rather check the result + * and eventually retry ... + */ while (openpic_read(addr) & OPENPIC_ACTIVITY); openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); } @@ -182,16 +198,18 @@ void __init openpic_init(int main_pic) OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; - - printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, - NumProcessors, NumSources, OpenPIC); - timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); - printk("OpenPIC timer frequency is "); - if (timerfreq) - printk("%d Hz\n", timerfreq); - else - printk("not set\n"); - + if ( _machine != _MACH_Pmac ) + { + printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, + NumProcessors, NumSources, OpenPIC); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); + printk("OpenPIC timer frequency is "); + if (timerfreq) + printk("%d MHz\n", timerfreq>>20); + else + printk("not set\n"); + } + if ( main_pic ) { /* Initialize timer interrupts */ @@ -209,24 +227,59 @@ void __init openpic_init(int main_pic) /* Disabled, Priority 8 */ openpic_initipi(i, 8, OPENPIC_VEC_IPI+i); } - - /* Initialize external interrupts */ - if ( ppc_md.progress ) ppc_md.progress("openpic ext",0x3bc); - /* SIOint (8259 cascade) is special */ - openpic_initirq(0, 8, open_pic_irq_offset, 1, 1); - openpic_mapirq(0, 1<<0); - for (i = 1; i < NumSources; i++) { - /* Enabled, Priority 8 */ - openpic_initirq(i, 8, open_pic_irq_offset+i, 0, - i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); - /* Processor 0 */ - openpic_mapirq(i, 1<<0); + find_ISUs(); + if ( _machine != _MACH_Pmac ) + { + /* Initialize external interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic ext",0x3bc); + /* SIOint (8259 cascade) is special */ + openpic_initirq(0, 8, open_pic_irq_offset, 1, 1); + openpic_mapirq(0, 1<<0); + for (i = 1; i < NumSources; i++) { + /* Enabled, Priority 8 */ + openpic_initirq(i, 8, open_pic_irq_offset+i, 0, + i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); + /* Processor 0 */ + openpic_mapirq(i, 1<<0); + } } - + else + { + /* Prevent any interrupt from occuring during initialisation. + * Hum... I believe this is not necessary, Apple does that in + * Darwin's PowerExpress code. + */ + openpic_set_priority(0, 0xf); + + /* First disable all interrupts and map them to CPU 0 */ + for (i = 0; i < NumSources; i++) { + openpic_disable_irq(i); + openpic_mapirq(i, 1<<0); + } + + /* If we use the device tree, then lookup all interrupts and + * initialize them according to sense infos found in the tree + */ + if (use_of_interrupt_tree) { + struct device_node* np = find_all_nodes(); + while(np) { + int j, pri; + pri = strcmp(np->name, "programmer-switch") ? 2 : 7; + for (j=0;j<np->n_intrs;j++) + openpic_initirq( np->intrs[j].line, + pri, + np->intrs[j].line, + np->intrs[j].sense, + np->intrs[j].sense); + np = np->next; + } + } + } + /* Initialize the spurious interrupt */ if ( ppc_md.progress ) ppc_md.progress("openpic spurious",0x3bd); openpic_set_spurious(OPENPIC_VEC_SPURIOUS); - if ( _machine != _MACH_gemini ) + if ( !(_machine && (_MACH_gemini|_MACH_Pmac)) ) { if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, "82c59 cascade", NULL)) @@ -238,6 +291,20 @@ void __init openpic_init(int main_pic) if ( ppc_md.progress ) ppc_md.progress("openpic exit",0x222); } +void find_ISUs(void) +{ +#ifdef CONFIG_PPC64 + /* hardcode this for now since the IBM 260 is the only thing with + * a distributed openpic right now. -- Cort + */ + ISU = (OpenPIC_Source *)0xfeff7c00; + NumSources = 0x10; +#else + /* for non-distributed OpenPIC implementations it's in the IDU -- Cort */ + ISU = OpenPIC->Source; +#endif +} + void openpic_reset(void) { openpic_setfield(&OpenPIC->Global.Global_Configuration0, @@ -279,6 +346,8 @@ void openpic_eoi(u_int cpu) { check_arg_cpu(cpu); openpic_write(&OpenPIC->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic_read(&OpenPIC->THIS_CPU.EOI); } @@ -379,7 +448,7 @@ void do_openpic_setup_cpu(void) #if 0 /* let the openpic know we want intrs */ for ( i = 0; i < NumSources ; i++ ) - openpic_mapirq(i, openpic_read(&OpenPIC->Source[i].Destination) + openpic_mapirq(i, openpic_read(ISU[i].Destination) | (1<<smp_processor_id()) ); #endif openpic_set_priority(smp_processor_id(), 0); @@ -417,13 +486,23 @@ void openpic_maptimer(u_int timer, u_int cpumask) void openpic_enable_irq(u_int irq) { check_arg_irq(irq); - openpic_clearfield(&OpenPIC->Source[irq - open_pic_irq_offset].Vector_Priority, OPENPIC_MASK); + openpic_clearfield(&ISU[irq - open_pic_irq_offset].Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + } while(openpic_readfield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_MASK)); } void openpic_disable_irq(u_int irq) { check_arg_irq(irq); - openpic_setfield(&OpenPIC->Source[irq - open_pic_irq_offset].Vector_Priority, OPENPIC_MASK); + openpic_setfield(&ISU[irq - open_pic_irq_offset].Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + } while(!openpic_readfield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_MASK)); } /* @@ -440,12 +519,13 @@ void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) check_arg_irq(irq); check_arg_pri(pri); check_arg_vec(vec); - openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + openpic_safe_writefield(&ISU[irq].Vector_Priority, OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | - OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL, + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, (pri << OPENPIC_PRIORITY_SHIFT) | vec | - (pol ? OPENPIC_SENSE_POLARITY : 0) | - (sense ? OPENPIC_SENSE_LEVEL : 0)); + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); } /* @@ -454,7 +534,7 @@ void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) void openpic_mapirq(u_int irq, u_int cpumask) { check_arg_irq(irq); - openpic_write(&OpenPIC->Source[irq].Destination, cpumask); + openpic_write(&ISU[irq].Destination, cpumask); } /* @@ -465,7 +545,7 @@ void openpic_mapirq(u_int irq, u_int cpumask) void openpic_set_sense(u_int irq, int sense) { check_arg_irq(irq); - openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + openpic_safe_writefield(&ISU[irq].Vector_Priority, OPENPIC_SENSE_LEVEL, (sense ? OPENPIC_SENSE_LEVEL : 0)); } diff --git a/arch/ppc/kernel/pci-dma.c b/arch/ppc/kernel/pci-dma.c new file mode 100644 index 000000000..089566908 --- /dev/null +++ b/arch/ppc/kernel/pci-dma.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2000 Ani Joshi <ajoshi@unixbox.com> + * + * + * Dynamic DMA mapping support. + * + * swiped from i386 + * + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <asm/io.h> + +/* Pure 2^n version of get_order */ +extern __inline__ int __get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC; + + if (hwdev == NULL || hwdev->dma_mask != 0xffffffff) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, __get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, __get_order(size)); +} diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 6c98bbf2c..8326bc369 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -71,9 +71,9 @@ void __init pcibios_init(void) { printk("PCI: Probing PCI hardware\n"); pci_scan_bus(0, &generic_pci_ops, NULL); - pcibios_claim_resources(&pci_root_buses); if (ppc_md.pcibios_fixup) ppc_md.pcibios_fixup(); + pcibios_claim_resources(&pci_root_buses); } void __init diff --git a/arch/ppc/kernel/pmac_nvram.c b/arch/ppc/kernel/pmac_nvram.c index ea3338aef..a3a9bae70 100644 --- a/arch/ppc/kernel/pmac_nvram.c +++ b/arch/ppc/kernel/pmac_nvram.c @@ -6,6 +6,7 @@ #include <linux/stddef.h> #include <linux/nvram.h> #include <linux/init.h> +#include <linux/slab.h> #include <asm/init.h> #include <asm/io.h> #include <asm/system.h> @@ -20,23 +21,37 @@ static int nvram_naddrs; static volatile unsigned char *nvram_addr; static volatile unsigned char *nvram_data; -static int nvram_mult; - -#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ - +static int nvram_mult, is_core_99; +static char* nvram_image; + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ + __init void pmac_nvram_init(void) { struct device_node *dp; + nvram_naddrs = 0; + dp = find_devices("nvram"); if (dp == NULL) { printk(KERN_ERR "Can't find NVRAM device\n"); - nvram_naddrs = 0; return; } nvram_naddrs = dp->n_addrs; - if (_machine == _MACH_chrp && nvram_naddrs == 1) { + is_core_99 = device_is_compatible(dp, "nvram,flash"); + if (is_core_99) + { + int i; + if (nvram_naddrs < 1) + return; + nvram_image = kmalloc(dp->addrs[0].size, GFP_KERNEL); + if (!nvram_image) + return; + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + for (i=0; i<dp->addrs[0].size; i++) + nvram_image[i] = in_8(nvram_data + i); + } else if (_machine == _MACH_chrp && nvram_naddrs == 1) { nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_mult = 1; } else if (nvram_naddrs == 1) { @@ -69,6 +84,8 @@ unsigned char nvram_read_byte(int addr) return req.reply[1]; #endif case 1: + if (is_core_99) + return nvram_image[addr]; return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; case 2: *nvram_addr = addr >> 5; @@ -94,6 +111,10 @@ void nvram_write_byte(unsigned char val, int addr) break; #endif case 1: + if (is_core_99) { + nvram_image[addr] = val; + break; + } nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; break; case 2: diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index 75f8097fd..b57d5aa28 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -30,6 +30,16 @@ struct bridge_data **bridges, *bridge_list; static int max_bus; +struct uninorth_data { + struct device_node* node; + volatile unsigned int* cfg_addr; + volatile unsigned int* cfg_data; +}; + +static struct uninorth_data uninorth_bridges[3]; +static int uninorth_count; +static int uninorth_default = -1; + static void add_bridges(struct device_node *dev); /* @@ -73,6 +83,159 @@ int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, return 0; } +/* This function only works for bus 0, uni-N uses a different mecanism for + * other busses (see below) + */ +#define UNI_N_CFA0(devfn, off) \ + ((1 << (unsigned long)PCI_SLOT(dev_fn)) \ + | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \ + | (((unsigned long)(off)) & 0xFCUL)) + +/* This one is for type 1 config accesses */ +#define UNI_N_CFA1(bus, devfn, off) \ + ((((unsigned long)(bus)) << 16) \ + |(((unsigned long)(devfn)) << 8) \ + |(((unsigned long)(off)) & 0xFCUL) \ + |1UL) + +/* We should really use RTAS here, unfortunately, it's not available with BootX. + * (one more reason for writing a beautiful OF booter). I'll do the RTAS stuff + * later, once I have something that works enough with BootX. + */ +__pmac static +unsigned int +uni_north_access_data(unsigned char bus, unsigned char dev_fn, + unsigned char offset) +{ + struct device_node *node, *bridge_node; + int bridge = uninorth_default; + unsigned int caddr; + + if (bus == 0) { + if (PCI_SLOT(dev_fn) < 11) { + return 0; + } + /* We look for the OF device corresponding to this bus/devfn pair. If we + * don't find it, we default to the external PCI */ + bridge_node = NULL; + node = find_pci_device_OFnode(bus, dev_fn & 0xf8); + if (node) { + /* note: we don't stop on the first occurence since we need to go + * up to the root bridge */ + do { + if (!strcmp(node->type, "pci")) + bridge_node = node; + node=node->parent; + } while (node); + } + if (bridge_node) { + int i; + for (i=0;i<uninorth_count;i++) + if (uninorth_bridges[i].node == bridge_node) { + bridge = i; + break; + } + } + caddr = UNI_N_CFA0(dev_fn, offset); + } else + caddr = UNI_N_CFA1(bus, dev_fn, offset); + + if (bridge == -1) { + printk(KERN_WARNING "pmac_pci: no default bridge !\n"); + return 0; + } + + /* Uninorth will return garbage if we don't read back the value ! */ + out_le32(uninorth_bridges[bridge].cfg_addr, caddr); + (void)in_le32(uninorth_bridges[bridge].cfg_addr); + /* Yes, offset is & 7, not & 3 ! */ + return (unsigned int)(uninorth_bridges[bridge].cfg_data) + (offset & 0x07); +} + +__pmac +int uni_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + unsigned int addr; + + *val = 0xff; + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + *val = in_8((volatile unsigned char*)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + unsigned int addr; + + *val = 0xffff; + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + *val = in_le16((volatile unsigned short*)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + unsigned int addr; + + *val = 0xffff; + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + *val = in_le32((volatile unsigned int*)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + unsigned int addr; + + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_8((volatile unsigned char *)addr, val); + (void)in_8((volatile unsigned char *)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + unsigned int addr; + + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le16((volatile unsigned short *)addr, val); + (void)in_le16((volatile unsigned short *)addr); + return PCIBIOS_SUCCESSFUL; +} + +__pmac +int uni_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + unsigned int addr; + + addr = uni_north_access_data(bus, dev_fn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32((volatile unsigned int *)addr, val); + (void)in_le32((volatile unsigned int *)addr); + return PCIBIOS_SUCCESSFUL; +} + __pmac int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) @@ -362,6 +525,21 @@ static void __init init_bandit(struct bridge_data *bp) bp->io_base); } +#define GRACKLE_STG_ENABLE 0x00000040 + +/* N.B. this is called before bridges is initialized, so we can't + use grackle_pcibios_{read,write}_config_dword. */ +static inline void grackle_set_stg(struct bridge_data *bp, int enable) +{ + unsigned int val; + + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + val = in_le32((volatile unsigned int *)bp->cfg_data); + val = enable? (val | GRACKLE_STG_ENABLE): (val & ~GRACKLE_STG_ENABLE); + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + out_le32((volatile unsigned int *)bp->cfg_data, val); +} + void __init pmac_find_bridges(void) { int bus; @@ -411,20 +589,47 @@ static void __init add_bridges(struct device_node *dev) printk(KERN_INFO "PCI buses %d..%d", bus_range[0], bus_range[1]); printk(" controlled by %s at %x\n", dev->name, addr->address); + if (device_is_compatible(dev, "uni-north")) { + int i = uninorth_count++; + uninorth_bridges[i].cfg_addr = ioremap(addr->address + 0x800000, 0x1000); + uninorth_bridges[i].cfg_data = ioremap(addr->address + 0xc00000, 0x1000); + uninorth_bridges[i].node = dev; + /* XXX This is the bridge with the PCI expansion bus. This is also the + * address of the bus that will receive type 1 config accesses and io + * accesses. Appears to be correct for iMac DV and G4 Sawtooth too. + * That means that we cannot do io cycles on the AGP bus nor the internal + * ethernet/fw bus. Fortunately, they appear not to be needed on iMac DV + * and G4 neither. + */ + if (addr->address == 0xf2000000) + uninorth_default = i; + else + continue; + } + bp = (struct bridge_data *) alloc_bootmem(sizeof(*bp)); - if (strcmp(dev->name, "pci") != 0) { - bp->cfg_addr = (volatile unsigned int *) - ioremap(addr->address + 0x800000, 0x1000); - bp->cfg_data = (volatile unsigned char *) - ioremap(addr->address + 0xc00000, 0x1000); - bp->io_base = (void *) ioremap(addr->address, 0x10000); - } else { - /* XXX */ + if (device_is_compatible(dev, "uni-north")) { + bp->cfg_addr = 0; + bp->cfg_data = 0; + /* is 0x10000 enough for io space ? */ + bp->io_base = (void *)ioremap(addr->address, 0x10000); + } else if (strcmp(dev->name, "pci") == 0) { + /* XXX assume this is a mpc106 (grackle) */ bp->cfg_addr = (volatile unsigned int *) ioremap(0xfec00000, 0x1000); bp->cfg_data = (volatile unsigned char *) ioremap(0xfee00000, 0x1000); bp->io_base = (void *) ioremap(0xfe000000, 0x20000); +#if 0 /* Disabled for now, HW problems */ + grackle_set_stg(bp, 1); +#endif + } else { + /* a `bandit' or `chaos' bridge */ + bp->cfg_addr = (volatile unsigned int *) + ioremap(addr->address + 0x800000, 0x1000); + bp->cfg_data = (volatile unsigned char *) + ioremap(addr->address + 0xc00000, 0x1000); + bp->io_base = (void *) ioremap(addr->address, 0x10000); } if (isa_io_base == 0) isa_io_base = (unsigned long) bp->io_base; @@ -453,7 +658,7 @@ fix_intr(struct device_node *node, struct pci_dev *dev) for (; node != 0;node = node->sibling) { class_code = (unsigned int *) get_property(node, "class-code", 0); - if((*class_code >> 8) == PCI_CLASS_BRIDGE_PCI) + if(class_code && (*class_code >> 8) == PCI_CLASS_BRIDGE_PCI) fix_intr(node->child, dev); reg = (unsigned int *) get_property(node, "reg", 0); if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) @@ -490,20 +695,38 @@ pmac_pcibios_fixup(void) if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || !pin) continue; /* No interrupt generated -> no fixup */ - fix_intr(bp->node->child, dev); + /* We iterate all instances of uninorth for now */ + if (uninorth_count && dev->bus->number == 0) { + int i; + for (i=0;i<uninorth_count;i++) + fix_intr(uninorth_bridges[i].node->child, dev); + } else + fix_intr(bp->node->child, dev); } } void __init pmac_setup_pci_ptrs(void) { - if (find_devices("pci") != 0) { - /* looks like a G3 powermac */ - set_config_access_method(grackle); - } else { + struct device_node* np; + + np = find_devices("pci"); + if (np != 0) + { + if (device_is_compatible(np, "uni-north")) + { + /* looks like an Core99 powermac */ + set_config_access_method(uni); + } else + { + /* looks like a G3 powermac */ + set_config_access_method(grackle); + } + } else + { set_config_access_method(pmac); } - + ppc_md.pcibios_fixup = pmac_pcibios_fixup; } diff --git a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c index 385e23327..d2d5e6b25 100644 --- a/arch/ppc/kernel/pmac_pic.c +++ b/arch/ppc/kernel/pmac_pic.c @@ -3,6 +3,8 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/signal.h> +#include <linux/pci.h> +#include <linux/openpic.h> #include <asm/init.h> #include <asm/io.h> @@ -27,12 +29,38 @@ static volatile struct pmac_irq_hw *pmac_irq_hw[4] = { static int max_irqs; static int max_real_irqs; +static int has_openpic = 0; #define MAXCOUNT 10000000 #define GATWICK_IRQ_POOL_SIZE 10 static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE]; +extern int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); + +static void pmac_openpic_mask_irq(unsigned int irq_nr) +{ + openpic_disable_irq(irq_nr); +} + +static void pmac_openpic_unmask_irq(unsigned int irq_nr) +{ + openpic_enable_irq(irq_nr); +} + +struct hw_interrupt_type pmac_open_pic = { + " OpenPIC ", + NULL, + NULL, + pmac_openpic_unmask_irq, + pmac_openpic_mask_irq, + pmac_openpic_mask_irq, + 0 +}; + static void __pmac pmac_mask_and_ack_irq(unsigned int irq_nr) { unsigned long bit = 1UL << (irq_nr & 0x1f); @@ -141,74 +169,6 @@ static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) ppc_irq_dispatch_handler( regs, irq ); } -#if 0 -void -pmac_do_IRQ(struct pt_regs *regs, - int cpu, - int isfake) -{ - int irq; - unsigned long bits = 0; - -#ifdef __SMP__ - /* IPI's are a hack on the powersurge -- Cort */ - if ( cpu != 0 ) - { -#ifdef CONFIG_XMON - static int xmon_2nd; - if (xmon_2nd) - xmon(regs); -#endif - pmac_smp_message_recv(); - return -1; - } - - { - unsigned int loops = MAXCOUNT; - while (test_bit(0, &global_irq_lock)) { - if (smp_processor_id() == global_irq_holder) { - printk("uh oh, interrupt while we hold global irq lock!\n"); -#ifdef CONFIG_XMON - xmon(0); -#endif - break; - } - if (loops-- == 0) { - printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); -#ifdef CONFIG_XMON - xmon(0); -#endif - } - } - } -#endif /* __SMP__ */ - - for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | ppc_lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } - - if (irq < 0) - { - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); - ppc_spurious_interrupts++; - } - else - { - ppc_irq_dispatch_handler( regs, irq ); - } -#ifdef CONFIG_SMP -out: -#endif /* CONFIG_SMP */ -} -#endif - int pmac_get_irq(struct pt_regs *regs) { @@ -248,15 +208,30 @@ pmac_get_irq(struct pt_regs *regs) } #endif /* __SMP__ */ - for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | ppc_lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } + /* Yeah, I know, this could be a separate do_IRQ function */ + if (has_openpic) + { + irq = openpic_irq(smp_processor_id()); + if (irq == OPENPIC_VEC_SPURIOUS) + /* We get those when doing polled ADB requests, + * using -2 is a temp hack to disable the printk + */ + irq = -2; /*-1; */ + else + openpic_eoi(smp_processor_id()); + } + else + { + for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { + int i = irq >> 5; + bits = ld_le32(&pmac_irq_hw[i]->flag) + | ppc_lost_interrupts[i]; + if (bits == 0) + continue; + irq -= cntlzw(bits); + break; + } + } return irq; } @@ -339,6 +314,51 @@ pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) } } +/* + * The PowerBook 3400/2400/3500 can have a combo ethernet/modem + * card which includes an ohare chip that acts as a second interrupt + * controller. If we find this second ohare, set it up and fix the + * interrupt value in the device tree for the ethernet chip. + */ +static void __init enable_second_ohare(void) +{ + unsigned char bus, devfn; + unsigned short cmd; + unsigned long addr; + int second_irq; + struct device_node *irqctrler = find_devices("pci106b,7"); + struct device_node *ether; + + if (irqctrler == NULL || irqctrler->n_addrs <= 0) + return; + addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40); + pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20); + max_irqs = 64; + if (pci_device_loc(irqctrler, &bus, &devfn) == 0) { + pmac_pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd &= ~PCI_COMMAND_IO; + pmac_pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); + } + + second_irq = irqctrler->intrs[0].line; + printk(KERN_INFO "irq: secondary controller on irq %d\n", second_irq); + request_irq(second_irq, gatwick_action, SA_INTERRUPT, + "interrupt cascade", 0 ); + + /* Fix interrupt for the modem/ethernet combo controller. The number + in the device tree (27) is bogus (correct for the ethernet-only + board but not the combo ethernet/modem board). + The real interrupt is 28 on the second controller -> 28+32 = 60. + */ + ether = find_devices("pci1011,14"); + if (ether && ether->n_intrs > 0) { + ether->intrs[0].line = 60; + printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n", + ether->intrs[0].line); + } +} + void __init pmac_pic_init(void) { @@ -347,9 +367,44 @@ pmac_pic_init(void) unsigned long addr; int second_irq = -999; + /* We first try to detect Apple's new Core99 chipset, since mac-io + * is quite different on those machines and contains an IBM MPIC2. + */ + irqctrler = find_type_devices("open-pic"); + if (irqctrler != NULL) + { + printk("PowerMac using OpenPIC irq controller\n"); + if (irqctrler->n_addrs > 0) + { +#ifdef CONFIG_XMON + struct device_node* pswitch; +#endif /* CONFIG_XMON */ + OpenPIC = (volatile struct OpenPIC *) + ioremap(irqctrler->addrs[0].address, + irqctrler->addrs[0].size); + for ( i = 0 ; i < NR_IRQS ; i++ ) + irq_desc[i].handler = &pmac_open_pic; + openpic_init(1); + has_openpic = 1; +#ifdef CONFIG_XMON + pswitch = find_devices("programmer-switch"); + if (pswitch && pswitch->n_intrs) + request_irq(pswitch->intrs[0].line, xmon_irq, 0, + "NMI - XMON", 0); +#endif /* CONFIG_XMON */ + return; + } + irqctrler = NULL; + } - /* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, - others have 32 */ + /* + * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts, + * 1998 G3 Series PowerBooks have 128, + * other powermacs have 32. + * The combo ethernet/modem card for the Powerstar powerbooks + * (2400/3400/3500, ohare based) has a second ohare chip + * effectively making a total of 64. + */ max_irqs = max_real_irqs = 32; irqctrler = find_devices("mac-io"); if (irqctrler) @@ -389,6 +444,12 @@ pmac_pic_init(void) pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20); } + /* PowerBooks 3400 and 3500 can have a second controller in a second + ohare chip, on the combo ethernet/modem card */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) + enable_second_ohare(); + /* disable all interrupts in all controllers */ for (i = 0; i * 32 < max_irqs; ++i) out_le32(&pmac_irq_hw[i]->enable, 0); @@ -435,7 +496,12 @@ sleep_save_intrs(int viaint) out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); if (max_real_irqs > 32) out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); - mb(); + (void)in_le32(&pmac_irq_hw[0]->flag); + do { + /* make sure mask gets to controller before we + return to user */ + mb(); + } while(in_le32(&pmac_irq_hw[0]->enable) != ppc_cached_irq_mask[0]); } void diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 72d23a934..e1c1815ac 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -104,6 +104,10 @@ extern char saved_command_line[]; extern void zs_kgdb_hook(int tty_num); static void ohare_init(void); static void init_p2pbridge(void); +static void init_uninorth(void); +#ifdef CONFIG_BOOTX_TEXT +void pmac_progress(char *s, unsigned short hex); +#endif __pmac int @@ -232,8 +236,10 @@ pmac_setup_arch(void) if (fp != 0) { switch (_get_PVR() >> 16) { case 4: /* 604 */ + case 8: /* G3 */ case 9: /* 604e */ case 10: /* mach V (604ev5) */ + case 12: /* G4 */ case 20: /* 620 */ loops_per_sec = *fp; break; @@ -252,9 +258,10 @@ pmac_setup_arch(void) pmac_find_bridges(); init_p2pbridge(); - + init_uninorth(); + /* Checks "l2cr-value" property in the registry */ - if ( (_get_PVR() >> 16) == 8) { + if ( (_get_PVR() >> 16) == 8 || (_get_PVR() >> 16) == 12 ) { struct device_node *np = find_devices("cpus"); if (np == 0) np = find_type_devices("cpu"); @@ -346,6 +353,33 @@ static void __init ohare_init(void) } } +static void __init +init_uninorth(void) +{ + /* + * Turns on the gmac clock so that it responds to PCI cycles + * later, the driver may want to turn it off again to save + * power when interface is down + */ + struct device_node* uni_n = find_devices("uni-n"); + struct device_node* gmac = find_devices("ethernet"); + unsigned long* addr; + + if (!uni_n || uni_n->n_addrs < 1) + return; + addr = ioremap(uni_n->addrs[0].address, 0x300); + + while(gmac) { + if (device_is_compatible(gmac, "gmac")) + break; + gmac = gmac->next; + } + if (gmac) { + *(addr + 8) |= 2; + eieio(); + } +} + extern char *bootpath; extern char *bootdevice; void *boot_host; @@ -401,14 +435,11 @@ note_scsi_host(struct device_node *node, void *host) #endif #if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) -extern int pmac_ide_count; -extern struct device_node *pmac_ide_node[]; -static int ide_majors[] = { 3, 22, 33, 34, 56, 57, 88, 89, 90, 91 }; kdev_t __init find_ide_boot(void) { char *p; - int i, n; + int n; if (bootdevice == NULL) return 0; @@ -417,18 +448,7 @@ kdev_t __init find_ide_boot(void) return 0; n = p - bootdevice; - /* - * Look through the list of IDE interfaces for this one. - */ - for (i = 0; i < pmac_ide_count; ++i) { - char *name = pmac_ide_node[i]->full_name; - if (memcmp(name, bootdevice, n) == 0 && name[n] == 0) { - /* XXX should cope with the 2nd drive as well... */ - return MKDEV(ide_majors[i], 0); - } - } - - return 0; + return pmac_find_ide_boot(bootdevice, n); } #endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */ @@ -464,10 +484,10 @@ void note_bootable_part(kdev_t dev, int part) find_boot_device(); found_boot = 1; } - if (dev == boot_dev) { + if (boot_dev == 0 || dev == boot_dev) { ROOT_DEV = MKDEV(MAJOR(dev), MINOR(dev) + part); boot_dev = NODEV; - printk(" (root)"); + printk(" (root on %d)", part); } } @@ -550,11 +570,15 @@ pmac_ide_default_irq(ide_ioreg_t base) return 0; } +#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) +extern ide_ioreg_t pmac_ide_get_base(int index); +#endif + ide_ioreg_t pmac_ide_default_io_base(int index) { #if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) - return pmac_ide_regbase[index]; + return pmac_ide_get_base(index); #else return 0; #endif @@ -660,5 +684,22 @@ pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports; ppc_ide_md.io_base = _IO_BASE; /* actually too early for this :-( */ -#endif +#endif +#ifdef CONFIG_BOOTX_TEXT + ppc_md.progress = pmac_progress; +#endif + if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0); + } + +#ifdef CONFIG_BOOTX_TEXT +extern void drawchar(char c); +extern void drawstring(const char *c); +void +pmac_progress(char *s, unsigned short hex) +{ + drawstring(s); + drawchar('\n'); +} +#endif CONFIG_BOOTX_TEXT + diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c index 3b7dd283f..1c935a625 100644 --- a/arch/ppc/kernel/pmac_time.c +++ b/arch/ppc/kernel/pmac_time.c @@ -71,8 +71,8 @@ unsigned long pmac_get_rtc_time(void) if (req.reply_len != 7) printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", req.reply_len); - return (req.reply[3] << 24) + (req.reply[4] << 16) - + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; + return (unsigned long)(req.reply[1] << 24) + (req.reply[2] << 16) + + (req.reply[3] << 8) + (unsigned long)req.reply[4] - RTC_OFFSET; #endif /* CONFIG_ADB_CUDA */ #ifdef CONFIG_ADB_PMU case SYS_CTRLER_PMU: diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c index b6397daac..42ca7eadc 100644 --- a/arch/ppc/kernel/ppc-stub.c +++ b/arch/ppc/kernel/ppc-stub.c @@ -351,7 +351,7 @@ static inline int get_msr() static inline void set_msr(int msr) { - asm volatile("mfmsr %0" : : "r" (msr)); + asm volatile("mtmsr %0" : : "r" (msr)); } /* Set up exception handlers for tracing and breakpoints diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c index b90fa7a2c..264a24d48 100644 --- a/arch/ppc/kernel/ppc_htab.c +++ b/arch/ppc/kernel/ppc_htab.c @@ -45,17 +45,9 @@ extern unsigned long pte_misses; extern unsigned long pte_errors; static struct file_operations ppc_htab_operations = { - ppc_htab_lseek, /* lseek */ - ppc_htab_read, /* read */ - ppc_htab_write, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ + llseek: ppc_htab_lseek, + read: ppc_htab_read, + write: ppc_htab_write, }; /* @@ -531,7 +523,8 @@ int proc_dol2crvec(ctl_table *table, int write, struct file *filp, "0.5", "1.0", "(reserved2)", "(reserved3)" }; - if ( (_get_PVR() >> 16) != 8) return -EFAULT; + if ( ((_get_PVR() >> 16) != 8) && ((_get_PVR() >> 16) != 12)) + return -EFAULT; if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) { *lenp = 0; diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 65e925034..9a5444a51 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -229,6 +229,8 @@ EXPORT_SYMBOL(find_path_device); EXPORT_SYMBOL(find_phandle); EXPORT_SYMBOL(device_is_compatible); EXPORT_SYMBOL(machine_is_compatible); +EXPORT_SYMBOL(find_pci_device_OFnode); +EXPORT_SYMBOL(find_all_nodes); EXPORT_SYMBOL(get_property); EXPORT_SYMBOL(pci_io_base); EXPORT_SYMBOL(pci_device_loc); diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index 633757875..fd14fc483 100644 --- a/arch/ppc/kernel/prep_pci.c +++ b/arch/ppc/kernel/prep_pci.c @@ -40,7 +40,7 @@ unsigned char *Motherboard_routes; static unsigned long *ProcInfo; extern int chrp_get_irq(struct pt_regs *); -extern void chrp_post_irq(int); +extern void chrp_post_irq(struct pt_regs* regs, int); /* Tables for known hardware */ diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index 241b7c33c..ed98ba6f0 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -57,6 +57,8 @@ union task_union __attribute((aligned(16))) init_task_union = { }; /* only used to get secondary processor up */ struct task_struct *current_set[NR_CPUS] = {&init_task, }; +char *sysmap = NULL; +unsigned long sysmap_size = 0; #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c index 049cde10f..a52bdd804 100644 --- a/arch/ppc/kernel/prom.c +++ b/arch/ppc/kernel/prom.c @@ -30,6 +30,10 @@ #include <asm/system.h> #include <asm/gemini.h> +#ifdef CONFIG_FB +#include <asm/linux_logo.h> +#endif + /* * Properties whose value is longer than this get excluded from our * copy of the device tree. This way we don't waste space storing @@ -91,7 +95,7 @@ unsigned int prom_num_displays = 0; char *of_stdout_device = 0; prom_entry prom = 0; -ihandle prom_chosen = 0, prom_stdout = 0; +ihandle prom_chosen = 0, prom_stdout = 0, prom_disp_node = 0; extern char *klimit; char *bootpath = 0; @@ -102,33 +106,35 @@ unsigned int rtas_entry = 0; /* physical pointer */ unsigned int rtas_size = 0; unsigned int old_rtas = 0; +int use_of_interrupt_tree = 0; static struct device_node *allnodes = 0; +#ifdef CONFIG_BOOTX_TEXT + static void clearscreen(void); static void flushscreen(void); -#ifdef CONFIG_BOOTX_TEXT - void drawchar(char c); void drawstring(const char *c); static void drawhex(unsigned long v); static void scrollscreen(void); static void draw_byte(unsigned char c, long locX, long locY); -static void draw_byte_32(unsigned char *bits, unsigned long *base); -static void draw_byte_16(unsigned char *bits, unsigned long *base); -static void draw_byte_8(unsigned char *bits, unsigned long *base); +static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb); +static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb); +static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb); -static long g_loc_X; -static long g_loc_Y; -static long g_max_loc_X; -static long g_max_loc_Y; +/* We want those in data, not BSS */ +static long g_loc_X = 0; +static long g_loc_Y = 0; +static long g_max_loc_X = 0; +static long g_max_loc_Y = 0; #define cmapsz (16*256) static unsigned char vga_font[cmapsz]; -#endif +#endif /* CONFIG_BOOTX_TEXT */ static void *call_prom(const char *service, int nargs, int nret, ...); @@ -138,15 +144,25 @@ static unsigned long inspect_node(phandle, struct device_node *, unsigned long, unsigned long, struct device_node ***); static unsigned long finish_node(struct device_node *, unsigned long, interpret_func *); +static unsigned long finish_node_interrupts(struct device_node *, unsigned long); static unsigned long check_display(unsigned long); static int prom_next_node(phandle *); static void *early_get_property(unsigned long, unsigned long, char *); +#ifdef CONFIG_BOOTX_TEXT +static void setup_disp_fake_bi(ihandle dp); +static void prom_welcome(boot_infos_t* bi, unsigned long phys); +#endif + extern void enter_rtas(void *); extern unsigned long reloc_offset(void); extern char cmd_line[512]; /* XXX */ boot_infos_t *boot_infos = 0; /* init it so it's in data segment not bss */ +#ifdef CONFIG_BOOTX_TEXT +boot_infos_t *disp_bi = 0; +boot_infos_t fake_bi = {0,}; +#endif unsigned long dev_tree_size; /* @@ -240,7 +256,7 @@ prom_print(const char *msg) if (RELOC(prom_stdout) == 0) { #ifdef CONFIG_BOOTX_TEXT - if (RELOC(boot_infos) != 0) + if (RELOC(disp_bi) != 0) drawstring(msg); #endif return; @@ -261,7 +277,6 @@ prom_print(const char *msg) } } -unsigned long smp_ibm_chrp_hack __initdata = 0; unsigned long smp_chrp_cpu_nr __initdata = 1; /* @@ -269,23 +284,29 @@ unsigned long smp_chrp_cpu_nr __initdata = 1; * handling exceptions and the MMU hash table for us. */ __init -void +unsigned long prom_init(int r3, int r4, prom_entry pp) { #ifdef CONFIG_SMP int i; phandle node; char type[16], *path; -#endif +#endif + int chrp = 0; unsigned long mem; - ihandle prom_rtas; + ihandle prom_rtas, prom_mmu, prom_op; unsigned long offset = reloc_offset(); int l; char *p, *d; + int prom_version = 0; + unsigned long phys; + + /* Default */ + phys = offset + KERNELBASE; /* check if we're apus, return if we are */ if ( r3 == 0x61707573 ) - return; + return phys; /* If we came here from BootX, clear the screen, * set up some pointers and return. */ @@ -294,22 +315,20 @@ prom_init(int r3, int r4, prom_entry pp) unsigned long space; unsigned long ptr, x; char *model; -#ifdef CONFIG_BOOTX_TEXT - unsigned long flags; -#endif RELOC(boot_infos) = PTRUNRELOC(bi); if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) bi->logicalDisplayBase = 0; - clearscreen(); - #ifdef CONFIG_BOOTX_TEXT RELOC(g_loc_X) = 0; RELOC(g_loc_Y) = 0; RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; + RELOC(disp_bi) = PTRUNRELOC(bi); + clearscreen(); + /* Test if boot-info is compatible. Done only in config CONFIG_BOOTX_TEXT since there is nothing much we can do with an incompatible version, except display a message and eventually hang the processor... @@ -320,23 +339,9 @@ prom_init(int r3, int r4, prom_entry pp) if (!BOOT_INFO_IS_COMPATIBLE(bi)) prom_print(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n")); - prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n")); - prom_print(RELOC("\nstarted at : 0x")); - drawhex(reloc_offset() + KERNELBASE); - prom_print(RELOC("\nlinked at : 0x")); - drawhex(KERNELBASE); - prom_print(RELOC("\nframe buffer at : 0x")); - drawhex((unsigned long)bi->dispDeviceBase); - prom_print(RELOC(" (phys), 0x")); - drawhex((unsigned long)bi->logicalDisplayBase); - prom_print(RELOC(" (log)")); - prom_print(RELOC("\nMSR : 0x")); - __asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); - drawhex(flags); - prom_print(RELOC("\n\n")); -#endif - /* Out of the #if/#endif since it flushes the clearscreen too */ + prom_welcome(bi, phys); flushscreen(); +#endif /* CONFIG_BOOTX_TEXT */ /* New BootX enters kernel with MMU off, i/os are not allowed here. This hack will have been done by the boostrap anyway. @@ -381,12 +386,12 @@ prom_init(int r3, int r4, prom_entry pp) prom_print(RELOC("booting...\n")); flushscreen(); #endif - return; + return phys; } /* check if we're prep, return if we are */ if ( *(unsigned long *)(0) == 0xdeadc0de ) - return; + return phys; /* First get a handle for the stdout device */ RELOC(prom) = pp; @@ -407,6 +412,30 @@ prom_init(int r3, int r4, prom_entry pp) RELOC(of_stdout_device) = PTRUNRELOC(p); mem += strlen(p) + 1; + /* Find the OF version */ + prom_op = call_prom(RELOC("finddevice"), 1, 1, RELOC("/openprom")); + prom_version = 0; + if (prom_op != (void*)-1) { + char model[64]; + int sz; + sz = (int)call_prom(RELOC("getprop"), 4, 1, prom_op, RELOC("model"), model, 64); + if (sz > 0) { + char *c; + /* hack to skip the ibm chrp firmware # */ + if ( strncmp(model,RELOC("IBM"),3) ) { + for (c = model; *c; c++) + if (*c >= '0' && *c <= '9') { + prom_version = *c - '0'; + break; + } + } + else + chrp = 1; + } + } + if (prom_version >= 3) + prom_print(RELOC("OF Version 3 detected.\n")); + /* Get the boot device and translate it to a full OF pathname. */ p = (char *) mem; l = (int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), @@ -478,6 +507,42 @@ prom_init(int r3, int r4, prom_entry pp) prom_print(RELOC(" done\n")); } + /* If we are already running at 0xc0000000, we assume we were loaded by + * an OF bootloader which did set a BAT for us. This breaks OF translate + * so we force phys to be 0 + */ + if (offset == 0) + phys = 0; + else { + if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), + RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) { + prom_print(RELOC(" no MMU found\n")); + } else { + int nargs; + struct prom_args prom_args; + nargs = 4; + prom_args.service = RELOC("call-method"); + prom_args.nargs = nargs; + prom_args.nret = 4; + prom_args.args[0] = RELOC("translate"); + prom_args.args[1] = prom_mmu; + prom_args.args[2] = (void *)(offset + KERNELBASE); + prom_args.args[3] = (void *)1; + RELOC(prom)(&prom_args); + + /* We assume the phys. address size is 3 cells */ + if (prom_args.args[nargs] != 0) + prom_print(RELOC(" (translate failed) ")); + else + phys = (unsigned long)prom_args.args[nargs+3]; + } + } + +#ifdef CONFIG_BOOTX_TEXT + if (!chrp && RELOC(prom_disp_node) != 0) + setup_disp_fake_bi(RELOC(prom_disp_node)); +#endif + #ifdef CONFIG_SMP /* * With CHRP SMP we need to use the OF to start the other @@ -512,7 +577,7 @@ prom_init(int r3, int r4, prom_entry pp) node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); if ( (int)call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"),type, sizeof(type)) <= 0) - return; + return phys; /* copy the holding pattern code to someplace safe (8M) */ memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x100 ); @@ -554,6 +619,79 @@ prom_init(int r3, int r4, prom_entry pp) prom_print(RELOC("...failed\n")); } #endif + /* If OpenFirmware version >= 3, then use quiesce call */ + if (prom_version >= 3) { + prom_print(RELOC("Calling quiesce ...\n")); + call_prom(RELOC("quiesce"), 0, 0); + offset = reloc_offset(); + phys = offset + KERNELBASE; + } + +#ifdef CONFIG_BOOTX_TEXT + if (!chrp && RELOC(disp_bi)) { + RELOC(prom_stdout) = 0; + clearscreen(); + prom_welcome(PTRRELOC(RELOC(disp_bi)), phys); + prom_print(RELOC("booting...\n")); + } +#endif + + return phys; +} + +#ifdef CONFIG_BOOTX_TEXT +__init static void +prom_welcome(boot_infos_t* bi, unsigned long phys) +{ + unsigned long offset = reloc_offset(); + unsigned long flags; + unsigned long pvr; + + prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n")); + prom_print(RELOC("\nstarted at : 0x")); + drawhex(phys); + prom_print(RELOC("\nlinked at : 0x")); + drawhex(KERNELBASE); + prom_print(RELOC("\nframe buffer at : 0x")); + drawhex((unsigned long)bi->dispDeviceBase); + prom_print(RELOC(" (phys), 0x")); + drawhex((unsigned long)bi->logicalDisplayBase); + prom_print(RELOC(" (log)")); + prom_print(RELOC("\nMSR : 0x")); + __asm__ __volatile__ ("mfmsr %0" : "=r" (flags)); + drawhex(flags); + __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr)); + pvr >>= 16; + if (pvr > 1) { + prom_print(RELOC("\nHID0 : 0x")); + __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags)); + drawhex(flags); + } + if (pvr == 8 || pvr == 12) { + prom_print(RELOC("\nICTC : 0x")); + __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags)); + drawhex(flags); + } + prom_print(RELOC("\n\n")); +} +#endif + +static int prom_set_color(ihandle ih, int i, int r, int g, int b) +{ + struct prom_args prom_args; + unsigned long offset = reloc_offset(); + + prom_args.service = RELOC("call-method"); + prom_args.nargs = 6; + prom_args.nret = 1; + prom_args.args[0] = RELOC("color!"); + prom_args.args[1] = ih; + prom_args.args[2] = (void *) i; + prom_args.args[3] = (void *) b; + prom_args.args[4] = (void *) g; + prom_args.args[5] = (void *) r; + RELOC(prom)(&prom_args); + return (int) prom_args.args[6]; } /* @@ -573,6 +711,26 @@ check_display(unsigned long mem) int i; unsigned long offset = reloc_offset(); char type[16], *path; + static unsigned char default_colors[] = { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xff, + 0x55, 0xff, 0x55, + 0x55, 0xff, 0xff, + 0xff, 0x55, 0x55, + 0xff, 0x55, 0xff, + 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff + }; + + RELOC(prom_disp_node) = 0; for (node = 0; prom_next_node(&node); ) { type[0] = 0; @@ -595,6 +753,26 @@ check_display(unsigned long mem) } prom_print(RELOC("... ok\n")); + if (RELOC(prom_disp_node) == 0) + RELOC(prom_disp_node) = node; + + /* Setup a useable color table when the appropriate + * method is available. Should update this to set-colors */ + for (i = 0; i < 32; i++) + if (prom_set_color(ih, i, RELOC(default_colors)[i*3], + RELOC(default_colors)[i*3+1], + RELOC(default_colors)[i*3+2]) != 0) + break; + +#ifdef CONFIG_FB + for (i = 0; i < LINUX_LOGO_COLORS; i++) + if (prom_set_color(ih, i + 32, + RELOC(linux_logo_red)[i], + RELOC(linux_logo_green)[i], + RELOC(linux_logo_blue)[i]) != 0) + break; +#endif /* CONFIG_FB */ + /* * If this display is the device that OF is using for stdout, * move it to the front of the list. @@ -614,6 +792,79 @@ check_display(unsigned long mem) return ALIGN(mem); } +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +#ifdef CONFIG_BOOTX_TEXT +__init +static void +setup_disp_fake_bi(ihandle dp) +{ + unsigned int len; + int width = 640, height = 480, depth = 8, pitch; + unsigned address; + boot_infos_t* bi; + unsigned long offset = reloc_offset(); + + prom_print(RELOC("Initing fake screen\n")); + + len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("depth"), &len, sizeof(len)); + if (len == 0) + prom_print(RELOC("Warning: assuming display depth = 8\n")); + else + depth = len; + width = len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("width"), &len, sizeof(len)); + width = len; + if (width == 0) { + prom_print(RELOC("Failed to get width\n")); + return; + } + height = len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("height"), &len, sizeof(len)); + height = len; + if (height == 0) { + prom_print(RELOC("Failed to get height\n")); + return; + } + pitch = len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("linebytes"), &len, sizeof(len)); + pitch = len; + if (pitch == 0) { + prom_print(RELOC("Failed to get pitch\n")); + return; + } + address = len = 0; + call_prom(RELOC("getprop"), 4, 1, dp, RELOC("address"), &len, sizeof(len)); + address = len; + if (address == 0) { + prom_print(RELOC("Failed to get address\n")); + return; + } +#if 0 + /* kludge for valkyrie */ + if (strcmp(dp->name, "valkyrie") == 0) + address += 0x1000; + } +#endif + + RELOC(disp_bi) = &fake_bi; + bi = PTRRELOC((&fake_bi)); + RELOC(g_loc_X) = 0; + RELOC(g_loc_Y) = 0; + RELOC(g_max_loc_X) = width / 8; + RELOC(g_max_loc_Y) = height / 16; + bi->logicalDisplayBase = (unsigned char *)address; + bi->dispDeviceBase = (unsigned char *)address; + bi->dispDeviceRowBytes = pitch; + bi->dispDeviceDepth = depth; + bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0; + bi->dispDeviceRect[2] = width; + bi->dispDeviceRect[3] = height; +} +#endif + __init static int prom_next_node(phandle *nodep) @@ -748,6 +999,16 @@ void finish_device_tree(void) { unsigned long mem = (unsigned long) klimit; + char* model; + + /* Here, we decide if we'll use the interrupt-tree (new Core99 code) or not. + * This code was only tested with Core99 machines so far, but should be easily + * adapted to older newworld machines (iMac, B&W G3, Lombard). + */ + model = get_property(allnodes, "model", 0); + if ((boot_infos == 0) && model && (strcmp(model, "PowerBook2,1") == 0 + || strcmp(model, "PowerMac2,1") == 0 || strcmp(model, "PowerMac3,1") == 0)) + use_of_interrupt_tree = 1; mem = finish_node(allnodes, mem, NULL); dev_tree_size = mem - (unsigned long) allnodes; @@ -788,6 +1049,9 @@ finish_node(struct device_node *np, unsigned long mem_start, if (ifunc != NULL) { mem_start = ifunc(np, mem_start); } + if (use_of_interrupt_tree) { + mem_start = finish_node_interrupts(np, mem_start); + } /* the f50 sets the name to 'display' and 'compatible' to what we * expect for the name -- Cort @@ -834,6 +1098,133 @@ finish_node(struct device_node *np, unsigned long mem_start, return mem_start; } +/* This routine walks the interrupt tree for a given device node and gather + * all necessary informations according to the draft interrupt mapping + * for CHRP. The current version was only tested on Apple "Core99" machines + * and may not handle cascaded controllers correctly. + */ +__init +static unsigned long +finish_node_interrupts(struct device_node *np, unsigned long mem_start) +{ + /* Finish this node */ + unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg; + phandle *parent; + struct device_node *node, *parent_node; + int l, isize, ipsize, asize, map_size, regpsize; + + /* Currently, we don't look at all nodes with no "interrupts" property */ + interrupts = (unsigned int *)get_property(np, "interrupts", &l); + if (interrupts == NULL) + return mem_start; + ipsize = l>>2; + + reg = (unsigned int *)get_property(np, "reg", &l); + regpsize = l>>2; + + /* We assume default interrupt cell size is 1 (bugus ?) */ + isize = 1; + node = np; + + do { + /* We adjust the cell size if the current parent contains an #interrupt-cells + * property */ + isizep = (unsigned int *)get_property(node, "#interrupt-cells", &l); + if (isizep) + isize = *isizep; + + /* We don't do interrupt cascade (ISA) for now, we stop on the first + * controller found + */ + if (get_property(node, "interrupt-controller", &l)) { + int i,j; + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = ipsize / isize; + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *interrupts++; + np->intrs[i].sense = 0; + if (isize > 1) + np->intrs[i].sense = *interrupts++; + for (j=2; j<isize; j++) + interrupts++; + } + return mem_start; + } + /* We lookup for an interrupt-map. This code can only handle one interrupt + * per device in the map. We also don't handle #address-cells in the parent + * I skip the pci node itself here, may not be necessary but I don't like it's + * reg property. + */ + if (np != node) + map = (unsigned int *)get_property(node, "interrupt-map", &l); + else + map = NULL; + if (map && l) { + int i, found, temp_isize; + map_size = l>>2; + map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l); + asizep = (unsigned int *)get_property(node, "#address-cells", &l); + if (asizep && l == sizeof(unsigned int)) + asize = *asizep; + else + asize = 0; + found = 0; + while(map_size>0 && !found) { + found = 1; + for (i=0; i<asize; i++) { + unsigned int mask = map_mask ? map_mask[i] : 0xffffffff; + if (!reg || (i>=regpsize) || ((mask & *map) != (mask & reg[i]))) + found = 0; + map++; + map_size--; + } + for (i=0; i<isize; i++) { + unsigned int mask = map_mask ? map_mask[i+asize] : 0xffffffff; + if ((mask & *map) != (mask & interrupts[i])) + found = 0; + map++; + map_size--; + } + parent = *((phandle *)(map)); + map+=1; map_size-=1; + parent_node = find_phandle(parent); + temp_isize = isize; + if (parent_node) { + isizep = (unsigned int *)get_property(parent_node, "#interrupt-cells", &l); + if (isizep) + temp_isize = *isizep; + } + if (!found) { + map += temp_isize; + map_size-=temp_isize; + } + } + if (found) { + node = parent_node; + reg = NULL; + regpsize = 0; + interrupts = (unsigned int *)map; + ipsize = temp_isize*1; + continue; + } + } + /* We look for an explicit interrupt-parent. + */ + parent = (phandle *)get_property(node, "interrupt-parent", &l); + if (parent && (l == sizeof(phandle)) && + (parent_node = find_phandle(*parent))) { + node = parent_node; + continue; + } + /* Default, get real parent */ + node = node->parent; + } while(node); + + return mem_start; +} + + /* * When BootX makes a copy of the device tree from the MacOS * Name Registry, it is in the format we use but all of the pointers @@ -892,6 +1283,9 @@ interpret_pci_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + /* * If the pci host bridge has an interrupt-map property, * look for our node in it. @@ -901,14 +1295,28 @@ interpret_pci_props(struct device_node *np, unsigned long mem_start) get_property(np->parent, "interrupt-map", &ml)) != 0 && (ip = (int *) get_property(np, "interrupts", &l)) != 0) { unsigned int devfn = pci_addrs[0].addr.a_hi & 0xff00; + unsigned int cell_size; + struct device_node* np2; + /* This is hackish, but is only used for BootX booting */ + cell_size = sizeof(struct pci_intr_map); + np2 = np->parent; + while(np2) { + if (device_is_compatible(np2, "uni-north")) { + cell_size += 4; + break; + } + np2 = np2->parent; + } np->n_intrs = 0; np->intrs = (struct interrupt_info *) mem_start; - for (i = 0; (ml -= sizeof(struct pci_intr_map)) >= 0; ++i) { - if (imp[i].addr.a_hi == devfn) { - np->intrs[np->n_intrs].line = imp[i].intr; - np->intrs[np->n_intrs].sense = 0; + for (i = 0; (ml -= cell_size) >= 0; ++i) { + if (imp->addr.a_hi == devfn) { + np->intrs[np->n_intrs].line = imp->intr; + np->intrs[np->n_intrs].sense = 0; /* FIXME */ ++np->n_intrs; } + imp = (struct pci_intr_map *)(((unsigned int)imp) + + cell_size); } if (np->n_intrs == 0) np->intrs = 0; @@ -965,6 +1373,9 @@ interpret_dbdma_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + ip = (int *) get_property(np, "AAPL,interrupts", &l); if (ip == 0) ip = (int *) get_property(np, "interrupts", &l); @@ -988,13 +1399,14 @@ interpret_macio_props(struct device_node *np, unsigned long mem_start) struct reg_property *rp; struct address_range *adr; unsigned long base_address; - int i, l, *ip; + int i, l, keylargo, *ip; struct device_node *db; base_address = 0; for (db = np->parent; db != NULL; db = db->parent) { if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { base_address = db->addrs[0].address; + keylargo = device_is_compatible(db, "Keylargo"); break; } } @@ -1014,6 +1426,9 @@ interpret_macio_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + ip = (int *) get_property(np, "interrupts", &l); if (ip == 0) ip = (int *) get_property(np, "AAPL,interrupts", &l); @@ -1022,9 +1437,15 @@ interpret_macio_props(struct device_node *np, unsigned long mem_start) if (_machine == _MACH_Pmac) { /* for the iMac */ np->n_intrs = l / sizeof(int); + /* Hack for BootX on Core99 */ + if (keylargo) + np->n_intrs = np->n_intrs/2; for (i = 0; i < np->n_intrs; ++i) { np->intrs[i].line = *ip++; - np->intrs[i].sense = 0; + if (keylargo) + np->intrs[i].sense = *ip++; + else + np->intrs[i].sense = 0; } } else { /* CHRP machines */ @@ -1064,6 +1485,9 @@ interpret_isa_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + ip = (int *) get_property(np, "interrupts", &l); if (ip != 0) { np->intrs = (struct interrupt_info *) mem_start; @@ -1101,6 +1525,9 @@ interpret_root_props(struct device_node *np, unsigned long mem_start) mem_start += i * sizeof(struct address_range); } + if (use_of_interrupt_tree) + return mem_start; + ip = (int *) get_property(np, "AAPL,interrupts", &l); if (ip == 0) ip = (int *) get_property(np, "interrupts", &l); @@ -1157,6 +1584,49 @@ find_type_devices(const char *type) return head; } +/* Finds a device node given its PCI bus number, device number + * and function number + */ +__openfirmware +struct device_node * +find_pci_device_OFnode(unsigned char bus, unsigned char dev_fn) +{ + struct device_node* np; + unsigned int *reg; + int l; + + for (np = allnodes; np != 0; np = np->allnext) { + char *pname = np->parent ? + (char *)get_property(np->parent, "name", &l) : 0; + if (pname && strcmp(pname, "mac-io") == 0) + continue; + reg = (unsigned int *) get_property(np, "reg", &l); + if (reg == 0 || l < sizeof(struct reg_property)) + continue; + if (((reg[0] >> 8) & 0xff) == dev_fn && ((reg[0] >> 16) & 0xff) == bus) + break; + } + return np; +} + +/* + * Returns all nodes linked together + */ +__openfirmware +struct device_node * +find_all_nodes(void) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + *prevp = np; + prevp = &np->next; + } + *prevp = 0; + return head; +} + /* Checks if the given "compat" string matches one of the strings in * the device's "compatible" property */ @@ -1377,18 +1847,28 @@ abort() prom_exit(); } -#ifdef CONFIG_XMON +#ifdef CONFIG_BOOTX_TEXT + +/* Here's a small text engine to use during early boot or for debugging purposes + * + * todo: + * + * - build some kind of vgacon with it to enable early printk + * - move to a separate file + * - add a few video driver hooks to keep in sync with display + * changes. + */ + __init void map_bootx_text(void) { - if (boot_infos == 0) + if (disp_bi == 0) return; - boot_infos->logicalDisplayBase = - ioremap((unsigned long) boot_infos->dispDeviceBase, - boot_infos->dispDeviceRowBytes * boot_infos->dispDeviceRect[3]); + disp_bi->logicalDisplayBase = + ioremap((unsigned long) disp_bi->dispDeviceBase, + disp_bi->dispDeviceRowBytes * disp_bi->dispDeviceRect[3]); } -#endif /* CONFIG_XMON */ /* Calc the base address of a given point (x,y) */ __pmac @@ -1410,7 +1890,7 @@ static void clearscreen(void) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; @@ -1435,7 +1915,7 @@ static void flushscreen(void) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; @@ -1452,14 +1932,12 @@ flushscreen(void) } } -#ifdef CONFIG_BOOTX_TEXT - __pmac static void scrollscreen(void) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); unsigned long *src = (unsigned long *)calc_base(bi,0,16); unsigned long *dst = (unsigned long *)calc_base(bi,0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * @@ -1563,19 +2041,20 @@ static void draw_byte(unsigned char c, long locX, long locY) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + boot_infos_t* bi = PTRRELOC(RELOC(disp_bi)); unsigned char *base = calc_base(bi, locX << 3, locY << 4); unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; + int rb = bi->dispDeviceRowBytes; switch(bi->dispDeviceDepth) { case 32: - draw_byte_32(font, (unsigned long *)base); + draw_byte_32(font, (unsigned long *)base, rb); break; case 16: - draw_byte_16(font, (unsigned long *)base); + draw_byte_16(font, (unsigned long *)base, rb); break; case 8: - draw_byte_8(font, (unsigned long *)base); + draw_byte_8(font, (unsigned long *)base, rb); break; default: break; @@ -1613,15 +2092,12 @@ static unsigned long expand_bits_16[4] = { __pmac static void -draw_byte_32(unsigned char *font, unsigned long *base) +draw_byte_32(unsigned char *font, unsigned long *base, int rb) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); int l, bits; int fg = 0xFFFFFFFFUL; int bg = 0x00000000UL; - for (l = 0; l < 16; ++l) { bits = *font++; @@ -1633,19 +2109,18 @@ draw_byte_32(unsigned char *font, unsigned long *base) base[5] = (-((bits >> 2) & 1) & fg) ^ bg; base[6] = (-((bits >> 1) & 1) & fg) ^ bg; base[7] = (-(bits & 1) & fg) ^ bg; - base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes); + base = (unsigned long *) ((char *)base + rb); } } __pmac static void -draw_byte_16(unsigned char *font, unsigned long *base) +draw_byte_16(unsigned char *font, unsigned long *base, int rb) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); int l, bits; int fg = 0xFFFFFFFFUL; int bg = 0x00000000UL; + unsigned long offset = reloc_offset(); unsigned long *eb = RELOC(expand_bits_16); for (l = 0; l < 16; ++l) @@ -1655,19 +2130,18 @@ draw_byte_16(unsigned char *font, unsigned long *base) base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg; base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg; base[3] = (eb[bits & 3] & fg) ^ bg; - base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes); + base = (unsigned long *) ((char *)base + rb); } } __pmac static void -draw_byte_8(unsigned char *font, unsigned long *base) +draw_byte_8(unsigned char *font, unsigned long *base, int rb) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); int l, bits; int fg = 0x0F0F0F0FUL; int bg = 0x00000000UL; + unsigned long offset = reloc_offset(); unsigned long *eb = RELOC(expand_bits_8); for (l = 0; l < 16; ++l) @@ -1675,7 +2149,7 @@ draw_byte_8(unsigned char *font, unsigned long *base) bits = *font++; base[0] = (eb[bits >> 4] & fg) ^ bg; base[1] = (eb[bits & 0xf] & fg) ^ bg; - base = (unsigned long *) ((char *)base + bi->dispDeviceRowBytes); + base = (unsigned long *) ((char *)base + rb); } } @@ -2026,3 +2500,4 @@ static unsigned char vga_font[cmapsz] = { }; #endif /* CONFIG_BOOTX_TEXT */ + diff --git a/arch/ppc/kernel/semaphore.c b/arch/ppc/kernel/semaphore.c index d630c80dc..f17bc16ce 100644 --- a/arch/ppc/kernel/semaphore.c +++ b/arch/ppc/kernel/semaphore.c @@ -137,3 +137,44 @@ int __down_trylock(struct semaphore * sem) { return waking_non_zero_trylock(sem); } + + +/* + * rw semaphores Ani Joshi <ajoshi@unixbox.com> + * based on alpha port by Andrea Arcangeli <andrea@suse.de> + */ + +void down_read_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue_exclusive(&sem->wait, &wait); + + do { + __set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + spin_unlock_irq(&sem->lock); + schedule(); + spin_lock_irq(&sem->lock); + } while(sem->wr); + + remove_wait_queue(&sem->wait, &wait); +} + +void down_write_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue_exclusive(&sem->wait, &wait); + + do { + __set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + spin_unlock_irq(&sem->lock); + schedule(); + spin_lock_irq(&sem->lock); + } while(sem->rd || sem->wr); + + remove_wait_queue(&sem->wait, &wait); +} + diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index dccb066ff..19ce0a25e 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -69,6 +69,13 @@ extern void gemini_init(unsigned long r3, unsigned long r6, unsigned long r7); +#ifdef CONFIG_BOOTX_TEXT +extern void map_bootx_text(void); +#endif +#ifdef CONFIG_XMON +extern void xmon_map_scc(void); +#endif + extern boot_infos_t *boot_infos; char saved_command_line[256]; unsigned char aux_device_present; @@ -261,7 +268,7 @@ int get_cpuinfo(char *buffer) } break; case 0x000C: - len += sprintf(len+buffer, "7400\n"); + len += sprintf(len+buffer, "7400 (G4)\n"); break; case 0x0020: len += sprintf(len+buffer, "403G"); @@ -292,7 +299,7 @@ int get_cpuinfo(char *buffer) * Assume here that all clock rates are the same in a * smp system. -- Cort */ -#ifndef CONFIG_8xx +#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx) if ( have_of ) { struct device_node *cpu_node; @@ -316,7 +323,7 @@ int get_cpuinfo(char *buffer) len += sprintf(len+buffer, "clock\t\t: %dMHz\n", *fp / 1000000); } -#endif +#endif /* !CONFIG_4xx && !CONFIG_8xx */ if (ppc_md.setup_residual != NULL) { @@ -410,8 +417,9 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { parse_bootinfo(); - + if ( ppc_md.progress ) ppc_md.progress("id mach(): start", 0x100); + #if !defined(CONFIG_4xx) && !defined(CONFIG_8xx) #ifndef CONFIG_MACH_SPECIFIC /* if we didn't get any bootinfo telling us what we are... */ @@ -477,11 +485,12 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, char *p; #ifdef CONFIG_BLK_DEV_INITRD - if (r3 - KERNELBASE < 0x800000 - && r4 != 0 && r4 != 0xdeadbeef) { + if (r3 && r4 && r4 != 0xdeadbeef) + { initrd_start = r3; initrd_end = r3 + r4; ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; } #endif cmd_line[0] = 0; @@ -519,6 +528,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, default: printk("Unknown machine type in identify_machine!\n"); } + /* Check for nobats option (used in mapin_ram). */ if (strstr(cmd_line, "nobats")) { extern int __map_without_bats; @@ -567,9 +577,11 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, int parse_bootinfo(void) { struct bi_record *rec; - extern char _end[]; + extern char __bss_start[]; + extern char *sysmap; + extern unsigned long sysmap_size; - rec = (struct bi_record *)PAGE_ALIGN((ulong)_end); + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+(1<<20)-1,(1<<20)); if ( rec->tag != BI_FIRST ) { /* @@ -577,11 +589,10 @@ int parse_bootinfo(void) * we have the bootloader handle all the relocation and * prom calls -- Cort */ - rec = (struct bi_record *)PAGE_ALIGN((ulong)_end+0x10000); + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+0x10000+(1<<20)-1,(1<<20)); if ( rec->tag != BI_FIRST ) return -1; } - for ( ; rec->tag != BI_LAST ; rec = (struct bi_record *)((ulong)rec + rec->size) ) { @@ -591,6 +602,11 @@ int parse_bootinfo(void) case BI_CMD_LINE: memcpy(cmd_line, (void *)data, rec->size); break; + case BI_SYSMAP: + sysmap = (char *)((data[0] >= (KERNELBASE)) ? data[0] : + (data[0]+KERNELBASE)); + sysmap_size = data[1]; + break; #ifdef CONFIG_BLK_DEV_INITRD case BI_INITRD: initrd_start = data[0]; @@ -603,7 +619,6 @@ int parse_bootinfo(void) have_of = data[1]; break; #endif /* CONFIG_MACH_SPECIFIC */ - } } @@ -613,7 +628,7 @@ int parse_bootinfo(void) /* Checks "l2cr=xxxx" command-line option */ void ppc_setup_l2cr(char *str, int *ints) { - if ( (_get_PVR() >> 16) == 8) + if ( ((_get_PVR() >> 16) == 8) || ((_get_PVR() >> 16) == 12) ) { unsigned long val = simple_strtoul(str, NULL, 0); printk(KERN_INFO "l2cr set to %lx\n", val); @@ -639,12 +654,21 @@ void __init setup_arch(char **cmdline_p) extern char *klimit; extern void do_init_bootmem(void); +#ifdef CONFIG_BOOTX_TEXT + map_bootx_text(); + prom_print("identify machine\n"); +#endif + #ifdef CONFIG_XMON - extern void xmon_map_scc(void); xmon_map_scc(); if (strstr(cmd_line, "xmon")) xmon(0); #endif /* CONFIG_XMON */ + if ( ppc_md.progress ) ppc_md.progress("setup_arch: enter", 0x3eab); +#if defined(CONFIG_KGDB) + set_debug_traps(); + breakpoint(); +#endif /* reboot on panic */ panic_timeout = 180; @@ -653,16 +677,16 @@ void __init setup_arch(char **cmdline_p) init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; init_mm.brk = (unsigned long) klimit; - + /* Save unparsed command line copy for /proc/cmdline */ strcpy(saved_command_line, cmd_line); *cmdline_p = cmd_line; /* set up the bootmem stuff with available memory */ do_init_bootmem(); + if ( ppc_md.progress ) ppc_md.progress("setup_arch: bootmem", 0x3eab); ppc_md.setup_arch(); - /* clear the progress line */ if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab); } diff --git a/arch/ppc/kernel/sleep.S b/arch/ppc/kernel/sleep.S index 3ead7bd28..b73acd6ce 100644 --- a/arch/ppc/kernel/sleep.S +++ b/arch/ppc/kernel/sleep.S @@ -171,6 +171,11 @@ _GLOBAL(low_sleep_handler) */ wake_up: + /* Flash inval the instruction cache */ + mfspr r3,HID0 + ori r3,r3, HID0_ICFI + mtspr HID0,r3 + isync /* Restore the HID0 register. This turns on the L1 caches. */ subi r1,r1,SL_PC lwz r3,SL_HID0(r1) diff --git a/arch/ppc/kernel/syscalls.c b/arch/ppc/kernel/syscalls.c index 30bed889b..e1a3fdcbb 100644 --- a/arch/ppc/kernel/syscalls.c +++ b/arch/ppc/kernel/syscalls.c @@ -252,9 +252,14 @@ asmlinkage int sys_pause(void) asmlinkage int sys_uname(struct old_utsname * name) { - if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) - return 0; - return -EFAULT; + int err; + + if (!name) + return -EFAULT; + down_read(&uts_sem); + err = copy_to_user(name, &system_utsname, sizeof (*name)); + up(&uts_sem); + return err ? -EFAULT : 0; } asmlinkage int sys_olduname(struct oldold_utsname * name) @@ -266,6 +271,7 @@ asmlinkage int sys_olduname(struct oldold_utsname * name) if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) return -EFAULT; + down_read(&uts_sem); error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); error -= __put_user(0,name->sysname+__OLD_UTS_LEN); error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); @@ -277,6 +283,7 @@ asmlinkage int sys_olduname(struct oldold_utsname * name) error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); error = __put_user(0,name->machine+__OLD_UTS_LEN); error = error ? -EFAULT : 0; + up(&uts_sem); return error; } diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 04b4e2d36..5cc34c5a5 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -128,6 +128,20 @@ MachineCheckException(struct pt_regs *regs) _exception(SIGSEGV, regs); } +void +SMIException(struct pt_regs *regs) +{ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + { + debugger(regs); + return; + } +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("System Management Interrupt"); +} + #if defined(CONFIG_ALTIVEC) void AltiVecUnavailable(struct pt_regs *regs) diff --git a/arch/ppc/kernel/walnut_setup.c b/arch/ppc/kernel/walnut_setup.c new file mode 100644 index 000000000..284c732c1 --- /dev/null +++ b/arch/ppc/kernel/walnut_setup.c @@ -0,0 +1,295 @@ +/* + * + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> + * + * Module name: walnut_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM PowerPC 403GP "Walnut" evaluation board. Adapted from original + * code by Gary Thomas, Cort Dougan <cort@fsmlabs.com>, and Dan Malek + * <dan@net4x.com>. + * + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/threads.h> +#include <linux/interrupt.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/blk.h> + +#include <asm/processor.h> +#include <asm/board.h> +#include <asm/machdep.h> +#include <asm/page.h> + +#include "local_irq.h" +#include "ppc4xx_pic.h" +#include "time.h" +#include "walnut_setup.h" + + +/* Function Prototypes */ + +extern void abort(void); + +/* Global Variables */ + +unsigned char __res[sizeof(bd_t)]; + + +/* + * void __init walnut_init() + * + * Description: + * This routine... + * + * Input(s): + * r3 - Optional pointer to a board information structure. + * r4 - Optional pointer to the physical starting address of the init RAM + * disk. + * r5 - Optional pointer to the physical ending address of the init RAM + * disk. + * r6 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + * r7 - Optional pointer to the physical ending address of any kernel + * command-line parameters. + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ +void __init +walnut_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *)__res, (void *)(r3 + KERNELBASE), sizeof(bd_t)); + } + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *)(r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6 + KERNELBASE)); + } + + /* Initialize machine-dependency vectors */ + + ppc_md.setup_arch = walnut_setup_arch; + ppc_md.setup_residual = walnut_setup_residual; + ppc_md.get_cpuinfo = NULL; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = walnut_init_IRQ; + ppc_md.get_irq = walnut_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = walnut_restart; + ppc_md.power_off = walnut_power_off; + ppc_md.halt = walnut_halt; + + ppc_md.time_init = walnut_time_init; + ppc_md.set_rtc_time = walnut_set_rtc_time; + ppc_md.get_rtc_time = walnut_get_rtc_time; + ppc_md.calibrate_decr = walnut_calibrate_decr; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + +#if defined(CONFIG_MAGIC_SYSRQ) + ppc_md.ppc_kbd_sysrq_xlate = NULL; +#endif + + return; +} + +/* + * Document me. + */ +void __init +walnut_setup_arch(void) +{ + /* XXX - Implement me */ +} + +/* + * int walnut_setup_residual() + * + * Description: + * This routine pretty-prints the platform's internal CPU and bus clock + * frequencies into the buffer for usage in /proc/cpuinfo. + * + * Input(s): + * *buffer - Buffer into which CPU and bus clock frequencies are to be + * printed. + * + * Output(s): + * *buffer - Buffer with the CPU and bus clock frequencies. + * + * Returns: + * The number of bytes copied into 'buffer' if OK, otherwise zero or less + * on error. + */ +int +walnut_setup_residual(char *buffer) +{ + int len = 0; + bd_t *bp = (bd_t *)__res; + + len += sprintf(len + buffer, + "clock\t\t: %dMHz\n" + "bus clock\t\t: %dMHz\n", + bp->bi_intfreq / 1000000, + bp->bi_busfreq / 1000000); + + return (len); +} + +/* + * Document me. + */ +void __init +walnut_init_IRQ(void) +{ + int i; + + ppc4xx_pic_init(); + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].handler = ppc4xx_pic; + } + + return; +} + +/* + * Document me. + */ +int +walnut_get_irq(struct pt_regs *regs) +{ + return (ppc4xx_pic_get_irq(regs)); +} + +/* + * Document me. + */ +void +walnut_restart(char *cmd) +{ + abort(); +} + +/* + * Document me. + */ +void +walnut_power_off(void) +{ + walnut_restart(NULL); +} + +/* + * Document me. + */ +void +walnut_halt(void) +{ + walnut_restart(NULL); +} + +/* + * Document me. + */ +void __init +walnut_time_init(void) +{ + /* XXX - Implement me */ +} + +/* + * Document me. + */ +int __init +walnut_set_rtc_time(unsigned long time) +{ + /* XXX - Implement me */ + + return (0); +} + +/* + * Document me. + */ +unsigned long __init +walnut_get_rtc_time(void) +{ + /* XXX - Implement me */ + + return (0); +} + +/* + * void __init walnut_calibrate_decr() + * + * Description: + * This routine retrieves the internal processor frequency from the board + * information structure, sets up the kernel timer decrementer based on + * that value, enables the 403 programmable interval timer (PIT) and sets + * it up for auto-reload. + * + * Input(s): + * N/A + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ +void __init +walnut_calibrate_decr(void) +{ + unsigned int freq; + bd_t *bip = (bd_t *)__res; + + freq = bip->bi_intfreq; + + decrementer_count = freq / HZ; + count_period_num = 1; + count_period_den = freq; + + /* Enable the PIT and set auto-reload of its value */ + + mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); + + /* Clear any pending timer interrupts */ + + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); +} diff --git a/arch/ppc/kernel/walnut_setup.h b/arch/ppc/kernel/walnut_setup.h new file mode 100644 index 000000000..a6e905333 --- /dev/null +++ b/arch/ppc/kernel/walnut_setup.h @@ -0,0 +1,50 @@ +/* + * + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> + * + * Module name: walnut_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM PowerPC 405GP "Walnut" evaluation board. Adapted from original + * code by Gary Thomas, Cort Dougan <cort@cs.nmt.edu>, and Dan Malek + * <dan@netx4.com>. + * + */ + +#ifndef __WALNUT_SETUP_H__ +#define __WALNUT_SETUP_H__ + +#include <asm/ptrace.h> +#include <asm/board.h> + + +#ifdef __cplusplus +extern "C" { +#endif + +extern unsigned char __res[sizeof(bd_t)]; + +extern void walnut_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void walnut_setup_arch(void); +extern int walnut_setup_residual(char *buffer); +extern void walnut_init_IRQ(void); +extern int walnut_get_irq(struct pt_regs *regs); +extern void walnut_restart(char *cmd); +extern void walnut_power_off(void); +extern void walnut_halt(void); +extern void walnut_time_init(void); +extern int walnut_set_rtc_time(unsigned long now); +extern unsigned long walnut_get_rtc_time(void); +extern void walnut_calibrate_decr(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __WALNUT_SETUP_H__ */ diff --git a/arch/ppc/mm/4xx_tlb.c b/arch/ppc/mm/4xx_tlb.c index b9d9d2119..69bf88320 100644 --- a/arch/ppc/mm/4xx_tlb.c +++ b/arch/ppc/mm/4xx_tlb.c @@ -1,6 +1,9 @@ /* * - * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> + * Copyright (c) 1998-1999 TiVo, Inc. + * Original implementation. + * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> + * Minor rework. * * Module name: 4xx_tlb.c * @@ -9,7 +12,10 @@ * */ +#include <linux/mm.h> + #include <asm/processor.h> +#include <asm/io.h> #include <asm/mmu.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -26,372 +32,327 @@ #endif -/* Function Macros */ - - -/* Type Definitios */ - -typedef struct pin_entry_s { - unsigned int e_pinned: 1, /* This TLB entry is pinned down. */ - e_used: 23; /* Number of users for this mapping. */ -} pin_entry_t; - - /* Global Variables */ -static pin_entry_t pin_table[PPC4XX_TLB_SIZE]; +static int pinned = 0; /* Function Prototypes */ +static int PPC4xx_tlb_miss(struct pt_regs *, unsigned long, int); -void -PPC4xx_tlb_pin(unsigned long va, unsigned long pa, int pagesz, int cache) -{ - int i, found = FALSE; - unsigned long tag, data; - unsigned long opid; - - opid = mfspr(SPRN_PID); - mtspr(SPRN_PID, 0); - - data = (pa & TLB_RPN_MASK) | TLB_WR; - - if (cache) - data |= (TLB_EX | TLB_I); - else - data |= (TLB_G | TLB_I); - - tag = (va & TLB_EPN_MASK) | TLB_VALID | pagesz; - - for (i = 0; i < PPC4XX_TLB_SIZE; i++) { - if (pin_table[i].e_pinned == FALSE) { - found = TRUE; - break; - } - } +extern void do_page_fault(struct pt_regs *, unsigned long, unsigned long); - if (found) { - /* printk("Pinning %#x -> %#x in entry %d...\n", va, pa, i); */ - asm("tlbwe %0,%1,1" : : "r" (data), "r" (i)); - asm("tlbwe %0,%1,0" : : "r" (tag), "r" (i)); - asm("isync"); - pin_table[i].e_pinned = found; - } - mtspr(SPRN_PID, opid); - return; -} - -void -PPC4xx_tlb_unpin(unsigned long va, unsigned long pa, int size) +/* + * () + * + * Description: + * This routine... + * + * Input(s): + * + * + * Output(s): + * + * + * Returns: + * + * + */ +static inline void +PPC4xx_tlb_write(unsigned long tag, unsigned long data, unsigned int index) { - /* XXX - To beimplemented. */ + asm("tlbwe %0,%1,1" : : "r" (data), "r" (index)); + asm("tlbwe %0,%1,0" : : "r" (tag), "r" (index)); } +/* + * () + * + * Description: + * This routine... + * + * Input(s): + * + * + * Output(s): + * + * + * Returns: + * + * + */ void -PPC4xx_tlb_flush_all(void) +PPC4xx_flush_tlb_all(void) { int i; - unsigned long flags, opid; + unsigned long flags, pid; save_flags(flags); cli(); - opid = mfspr(SPRN_PID); + pid = mfspr(SPRN_PID); mtspr(SPRN_PID, 0); - for (i = 0; i < PPC4XX_TLB_SIZE; i++) { - unsigned long ov = 0; - - if (pin_table[i].e_pinned) - continue; - - asm("tlbwe %0,%1,0" : : "r" (ov), "r" (i)); - asm("tlbwe %0,%1,1" : : "r" (ov), "r" (i)); + for (i = pinned; i < PPC4XX_TLB_SIZE; i++) { + PPC4xx_tlb_write(0, 0, i); } - asm("sync;isync"); - mtspr(SPRN_PID, opid); + mtspr(SPRN_PID, pid); restore_flags(flags); } +/* + * () + * + * Description: + * This routine... + * + * Input(s): + * + * + * Output(s): + * + * + * Returns: + * + * + */ void -PPC4xx_tlb_flush(unsigned long va, int pid) +PPC4xx_dtlb_miss(struct pt_regs *regs) { - unsigned long i, tag, flags, found = 1, opid; - - save_flags(flags); - cli(); + unsigned long addr = mfspr(SPRN_DEAR); + int write = mfspr(SPRN_ESR) & ESR_DST; - opid = mfspr(SPRN_PID); - mtspr(SPRN_PID, pid); - - asm("tlbsx. %0,0,%2;beq 1f;li %1,0;1:" : "=r" (i), "=r" (found) : "r" (va)); - - if (found && pin_table[i].e_pinned == 0) { - asm("tlbre %0,%1,0" : "=r" (tag) : "r" (i)); - tag &= ~ TLB_VALID; - asm("tlbwe %0,%1,0" : : "r" (tag), "r" (i)); + if (PPC4xx_tlb_miss(regs, addr, write) < 0) { + sti(); + do_page_fault(regs, addr, write); + cli(); } - - mtspr(SPRN_PID, opid); - - restore_flags(flags); + } -#if 0 /* - * TLB miss handling code. + * () + * + * Description: + * This routine... + * + * Input(s): + * + * + * Output(s): + * + * + * Returns: + * + * */ +void +PPC4xx_itlb_miss(struct pt_regs *regs) +{ + unsigned long addr = regs->nip; + + if (PPC4xx_tlb_miss(regs, addr, 0) < 0) { + sti(); + do_page_fault(regs, addr, 0); + cli(); + } +} /* - * Handle TLB faults. We should push this back to assembly code eventually. - * Caller is responsible for turning off interrupts ... + * () + * + * Description: + * This routine... + * + * Input(s): + * + * + * Output(s): + * + * + * Returns: + * + * */ -static inline void -tlbDropin(unsigned long tlbhi, unsigned long tlblo) { - /* - * Avoid the divide at the slight cost of a little too - * much emphasis on the last few entries. - */ - unsigned long rand = mfspr(SPRN_TBLO); - rand &= 0x3f; - rand += NTLB_WIRED; - if (rand >= NTLB) - rand -= NTLB_WIRED; - - asm("tlbwe %0,%1,1" : : "r" (tlblo), "r" (rand)); - asm("tlbwe %0,%1,0" : : "r" (tlbhi), "r" (rand)); - asm("isync;sync"); -} +void +PPC4xx_tlb_pin(unsigned long va, unsigned long pa, int pagesz, int cache) +{ + unsigned long tag, data; + unsigned long opid; -static inline void -mkTlbEntry(unsigned long addr, pte_t *pte) { - unsigned long tlbhi; - unsigned long tlblo; - int found = 1; - int idx; + if (pinned >= PPC4XX_TLB_SIZE) + return; - /* - * Construct the TLB entry. - */ - tlbhi = addr & ~(PAGE_SIZE-1); - tlblo = virt_to_phys(pte_page(*pte)) & TLBLO_RPN; - if (pte_val(*pte) & _PAGE_HWWRITE) - tlblo |= TLBLO_WR; - if (pte_val(*pte) & _PAGE_NO_CACHE) - tlblo |= TLBLO_I; - tlblo |= TLBLO_EX; - if (addr < KERNELBASE) - tlblo |= TLBLO_Z_USER; - tlbhi |= TLBHI_PGSZ_4K; - tlbhi |= TLBHI_VALID; + opid = mfspr(SPRN_PID); + mtspr(SPRN_PID, 0); - /* - * See if a match already exists in the TLB. - */ - asm("tlbsx. %0,0,%2;beq 1f;li %1,0;1:" : "=r" (idx), "=r" (found) : "r" (tlbhi)); - if (found) { - /* - * Found an existing entry. Just reuse the index. - */ - asm("tlbwe %0,%1,0" : : "r" (tlbhi), "r" (idx)); - asm("tlbwe %0,%1,1" : : "r" (tlblo), "r" (idx)); - } - else { - /* - * Do the more expensive operation - */ - tlbDropin(tlbhi, tlblo); - } + data = (pa & TLB_RPN_MASK) | TLB_WR; + + if (cache) + data |= (TLB_EX); + else + data |= (TLB_G | TLB_I); + + tag = (va & TLB_EPN_MASK) | TLB_VALID | pagesz; + + PPC4xx_tlb_write(tag, data, pinned++); + + mtspr(SPRN_PID, opid); + return; } /* - * Mainline of the TLB miss handler. The above inline routines should fold into - * this one, eliminating most function call overhead. + * () + * + * Description: + * This routine... + * + * Input(s): + * + * + * Output(s): + * + * + * Returns: + * + * */ -#ifdef TLBMISS_DEBUG -volatile unsigned long miss_start; -volatile unsigned long miss_end; -#endif +void +PPC4xx_tlb_unpin(unsigned long va, unsigned long pa, int size) +{ + /* XXX - To be implemented. */ +} -static inline int tlbMiss(struct pt_regs *regs, unsigned long badaddr, int wasWrite) +/* + * () + * + * Description: + * This routine... + * + * Input(s): + * + * + * Output(s): + * + * + * Returns: + * + * + */ +static inline void +PPC4xx_tlb_update(unsigned long addr, pte_t *pte) { - int spid, ospid; - struct mm_struct *mm; - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - if (!user_mode(regs) && (badaddr >= KERNELBASE)) { - mm = task[0]->mm; - spid = 0; -#ifdef TLBMISS_DEBUG - miss_start = 0; -#endif - } - else { - mm = current->mm; - spid = mfspr(SPRN_PID); -#ifdef TLBMISS_DEBUG - miss_start = 1; -#endif - } -#ifdef TLBMISS_DEBUG - store_cache_range((unsigned long)&miss_start, sizeof(miss_start)); -#endif + unsigned long data, tag, rand; + int i, found = 1; - pgd = pgd_offset(mm, badaddr); - if (pgd_none(*pgd)) - goto NOGOOD; - - pmd = pmd_offset(pgd, badaddr); - if (pmd_none(*pmd)) - goto NOGOOD; - - pte = pte_offset(pmd, badaddr); - if (pte_none(*pte)) - goto NOGOOD; - if (!pte_present(*pte)) - goto NOGOOD; -#if 1 - prohibit_if_guarded(badaddr, sizeof(int)); -#endif - if (wasWrite) { - if (!pte_write(*pte)) { - goto NOGOOD; - } - set_pte(pte, pte_mkdirty(*pte)); - } - set_pte(pte, pte_mkyoung(*pte)); + /* Construct the hardware TLB entry from the Linux-style PTE */ - ospid = mfspr(SPRN_PID); - mtspr(SPRN_PID, spid); - mkTlbEntry(badaddr, pte); - mtspr(SPRN_PID, ospid); + tag = tag = (addr & PAGE_MASK) | TLB_VALID | TLB_PAGESZ(PAGESZ_4K); + data = data = (pte_val(*pte) & PAGE_MASK) | TLB_EX | TLB_WR; -#ifdef TLBMISS_DEBUG - miss_end = 0; - store_cache_range((unsigned long)&miss_end, sizeof(miss_end)); +#if 0 + if (pte_val(*pte) & _PAGE_HWWRITE) + data |= TLB_WR; #endif - return 0; -NOGOOD: -#ifdef TLBMISS_DEBUG - miss_end = 1; - store_cache_range((unsigned long)&miss_end, sizeof(miss_end)); -#endif - return 1; -} + if (pte_val(*pte) & _PAGE_NO_CACHE) + data |= TLB_I; -/* - * End TLB miss handling code. - */ -/* ---------- */ + if (pte_val(*pte) & _PAGE_GUARDED) + data |= TLB_G; -/* - * Used to flush the TLB if the page fault handler decides to change - * something. - */ -void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) { - int spid; - unsigned long flags; + if (addr < KERNELBASE) + data |= TLB_ZSEL(1); - save_flags(flags); - cli(); + /* Attempt to match the new tag to an existing entry in the TLB. */ - if (addr >= KERNELBASE) - spid = 0; - else - spid = vma->vm_mm->context; - tlbFlush1(addr, spid); + asm("tlbsx. %0,0,%2;" + "beq 1f;" + "li %1,0;1:" : "=r" (i), "=r" (found) : "r" (tag)); - restore_flags(flags); + /* + * If we found a match for the tag, reuse the entry index and update + * the tag and data portions. Otherwise, we did not find a match. Use + * the lower 5 bits of the lower time base register as a pseudo-random + * index into the TLB and replace the entry at that index. + */ + + if (found) { + PPC4xx_tlb_write(tag, data, i); + } else { + rand = mfspr(SPRN_TBLO) & (PPC4XX_TLB_SIZE - 1); + rand += pinned; + if (rand >= PPC4XX_TLB_SIZE) + rand -= pinned; + + PPC4xx_tlb_write(tag, data, rand); + asm("isync;sync"); + } } /* - * Given a virtual address in the current address space, make - * sure the associated physical page is present in memory, - * and if the data is to be modified, that any copy-on-write - * actions have taken place. + * () + * + * Description: + * This routine... + * + * Input(s): + * + * + * Output(s): + * + * + * Returns: + * + * */ -unsigned long make_page_present(unsigned long p, int rw) { +static int +PPC4xx_tlb_miss(struct pt_regs *regs, unsigned long addr, int write) +{ + unsigned long spid, ospid; + struct mm_struct *mm; + pgd_t *pgd; + pmd_t *pmd; pte_t *pte; - char c; - get_user(c, (char *) p); + if (!user_mode(regs) && (addr >= KERNELBASE)) { + mm = &init_mm; + spid = 0; + } else { + mm = current->mm; + spid = mfspr(SPRN_PID); + } + + pgd = pgd_offset(mm, addr); + if (pgd_none(*pgd)) + goto bad; + + pmd = pmd_offset(pgd, addr); + if (pmd_none(*pmd)) + goto bad; - pte = findPTE(current->mm, p); + pte = pte_offset(pmd, addr); if (pte_none(*pte) || !pte_present(*pte)) - debug("make_page_present didn't load page", 0); - - if (rw) { - /* - * You have to write-touch the page, so that - * zero-filled pages are forced to be copied - * rather than still pointing at the zero - * page. - */ - extern void tlbFlush1(unsigned long, int); - tlbFlush1(p, get_context()); - put_user(c, (char *) p); - if (!pte_write(*pte)) - debug("make_page_present didn't make page writable", 0); - - tlbFlush1(p, get_context()); - } - return pte_page(*pte); -} + goto bad; -void DataTLBMissException(struct pt_regs *regs) -{ - unsigned long badaddr = mfspr(SPRN_DEAR); - int wasWrite = mfspr(SPRN_ESR) & 0x800000; - if (tlbMiss(regs, badaddr, wasWrite)) { - sti(); - do_page_fault(regs, badaddr, wasWrite); - cli(); - } -} + if (write) { + if (!pte_write(*pte)) + goto bad; -void InstructionTLBMissException(struct pt_regs *regs) -{ - if (!current) { - debug("ITLB Miss with no current task", regs); - sti(); - bad_page_fault(regs, regs->nip); - cli(); - return; - } - if (tlbMiss(regs, regs->nip, 0)) { - sti(); - do_page_fault(regs, regs->nip, 0); - cli(); - } -} + set_pte(pte, pte_mkdirty(*pte)); + } + set_pte(pte, pte_mkyoung(*pte)); -void DataPageFault(struct pt_regs *regs) -{ - unsigned long badaddr = mfspr(SPRN_DEAR); - int wasWrite = mfspr(SPRN_ESR) & 0x800000; - sti(); - do_page_fault(regs, badaddr, wasWrite); - cli(); -} + ospid = mfspr(SPRN_PID); + mtspr(SPRN_PID, spid); + PPC4xx_tlb_update(addr, pte); + mtspr(SPRN_PID, ospid); -void InstructionPageFault(struct pt_regs *regs) -{ - if (!current) { - debug("ITLB fault with no current task", regs); - sti(); - bad_page_fault(regs, regs->nip); - cli(); - return; - } - sti(); - do_page_fault(regs, regs->nip, 0); - cli(); + return (0); +bad: + return (-1); } -#endif diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index c558ef051..216527e34 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -107,7 +107,6 @@ unsigned long *oak_find_end_of_memory(void); static void mapin_ram(void); void map_page(unsigned long va, unsigned long pa, int flags); extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); struct mem_pieces phys_mem; @@ -281,9 +280,6 @@ void show_mem(void) printk("%d pages swap cached\n",cached); printk("%d pages in page table cache\n",(int)pgtable_cache_size); show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif printk("%-8s %3s %8s %8s %8s %9s %8s", "Process", "Pid", "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); #ifdef __SMP__ @@ -643,7 +639,9 @@ void __init setbat(int index, unsigned long virt, unsigned long phys, wimgxpp |= (flags & _PAGE_RW)? BPP_RW: BPP_RX; bat[1].word[0] = virt | (bl << 2) | 2; /* Vs=1, Vp=0 */ bat[1].word[1] = phys | wimgxpp; +#ifndef CONFIG_KGDB /* want user access for breakpoints */ if (flags & _PAGE_USER) +#endif bat[1].bat.batu.vp = 1; if (flags & _PAGE_GUARDED) { /* G bit must be zero in IBATs */ @@ -732,6 +730,10 @@ static void __init mapin_ram(void) * don't get ASID compares on kernel space. */ f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED; +#ifdef CONFIG_KGDB + /* Allows stub to set breakpoints everywhere */ + f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; +#else if ((char *) v < _stext || (char *) v >= etext) f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; #ifndef CONFIG_8xx @@ -740,6 +742,7 @@ static void __init mapin_ram(void) forces R/W kernel access */ f |= _PAGE_USER; #endif /* CONFIG_8xx */ +#endif /* CONFIG_KGDB */ map_page(v, p, f); v += PAGE_SIZE; p += PAGE_SIZE; @@ -844,6 +847,8 @@ void free_initrd_mem(unsigned long start, unsigned long end) } #endif +extern boot_infos_t *disp_bi; + /* * Do very early mm setup such as finding the size of memory * and setting up the hash table. @@ -855,25 +860,45 @@ void free_initrd_mem(unsigned long start, unsigned long end) void __init MMU_init(void) { + /* + * The Zone Protection Register (ZPR) defines how protection will + * be applied to every page which is a member of a given zone. At + * present, we utilize only two of the 4xx's zones. The first, zone + * 0, is set at '00b and only allows access in supervisor-mode based + * on the EX and WR bits. No user-mode access is allowed. The second, + * zone 1, is set at '10b and in supervisor-mode allows access + * without regard to the EX and WR bits. In user-mode, access is + * allowed based on the EX and WR bits. + */ + + mtspr(SPRN_ZPR, 0x2aaaaaaa); + + /* Hardwire any TLB entries necessary here. */ + PPC4xx_tlb_pin(KERNELBASE, 0, TLB_PAGESZ(PAGESZ_16M), 1); - PPC4xx_tlb_pin(OAKNET_IO_BASE, OAKNET_IO_BASE, TLB_PAGESZ(PAGESZ_4K), 0); - end_of_DRAM = oak_find_end_of_memory(); - /* Map in all of RAM starting at KERNELBASE */ + /* + * Find the top of physical memory and map all of it in starting + * at KERNELBASE. + */ + end_of_DRAM = oak_find_end_of_memory(); mapin_ram(); - /* Zone 0 - kernel (above 0x80000000), zone 1 - user */ + /* + * Set up the real-mode cache parameters for the exception vector + * handlers (which are run in real-mode). + */ - mtspr(SPRN_ZPR, 0x2aaaaaaa); - mtspr(SPRN_DCWR, 0x00000000); /* all caching is write-back */ + mtspr(SPRN_DCWR, 0x00000000); /* All caching is write-back */ - /* Cache 128MB of space starting at KERNELBASE. */ + /* + * Cache instruction and data space where the exception + * vectors and the kernel live in real-mode. + */ - mtspr(SPRN_DCCR, 0x00000000); - /* flush_instruction_cache(); XXX */ - mtspr(SPRN_ICCR, 0x00000000); - + mtspr(SPRN_DCCR, 0x80000000); /* 128 MB of data space at 0x0. */ + mtspr(SPRN_ICCR, 0x80000000); /* 128 MB of instr. space at 0x0. */ } #else void __init MMU_init(void) @@ -895,7 +920,11 @@ void __init MMU_init(void) if ( ppc_md.progress ) ppc_md.progress("MMU:hash init", 0x300); hash_init(); +#ifdef CONFIG_PPC64 + _SDR1 = 0; /* temporary hack to just use bats -- Cort */ +#else _SDR1 = __pa(Hash) | (Hash_mask >> 10); +#endif ioremap_base = 0xf8000000; if ( ppc_md.progress ) ppc_md.progress("MMU:mapin", 0x301); @@ -916,8 +945,14 @@ void __init MMU_init(void) break; case _MACH_chrp: setbat(0, 0xf8000000, 0xf8000000, 0x08000000, IO_PAGE); +#ifdef CONFIG_PPC64 + /* temporary hack to get working until page tables are stable -- Cort*/ + setbat(1, 0x80000000, 0xc0000000, 0x10000000, IO_PAGE); + setbat(3, 0xd0000000, 0xd0000000, 0x10000000, IO_PAGE); +#else setbat(1, 0x80000000, 0x80000000, 0x10000000, IO_PAGE); setbat(3, 0x90000000, 0x90000000, 0x10000000, IO_PAGE); +#endif break; case _MACH_Pmac: #if 0 @@ -929,6 +964,10 @@ void __init MMU_init(void) setbat(0, base, base, 0x100000, IO_PAGE); } #endif +#if 0 + setbat(0, disp_bi->dispDeviceBase, disp_bi->dispDeviceBase, 0x100000, IO_PAGE); + disp_bi->logicalDisplayBase = disp_bi->dispDeviceBase; +#endif ioremap_base = 0xf0000000; break; case _MACH_apus: @@ -1087,6 +1126,8 @@ void __init paging_init(void) void __init mem_init(void) { + extern char *sysmap; + extern unsigned long sysmap_size; unsigned long addr; int codepages = 0; int datapages = 0; @@ -1116,6 +1157,11 @@ void __init mem_init(void) addr += PAGE_SIZE) SetPageReserved(mem_map + MAP_NR(addr)); #endif /* defined(CONFIG_CHRP) || defined(CONFIG_ALL_PPC) */ + if ( sysmap_size ) + for (addr = (unsigned long)sysmap; + addr < PAGE_ALIGN((unsigned long)sysmap+sysmap_size) ; + addr += PAGE_SIZE) + SetPageReserved(mem_map + MAP_NR(addr)); for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM; addr += PAGE_SIZE) { @@ -1131,10 +1177,8 @@ void __init mem_init(void) } printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n", - (unsigned long) nr_free_pages << (PAGE_SHIFT-10), - codepages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10), - initpages << (PAGE_SHIFT-10), + (unsigned long)nr_free_pages()<< (PAGE_SHIFT-10), + codepages, datapages, initpages, PAGE_OFFSET, (unsigned long) end_of_DRAM); mem_init_done = 1; } @@ -1153,7 +1197,7 @@ unsigned long __init *pmac_find_end_of_memory(void) unsigned long a, total; /* max amount of RAM we allow -- Cort */ -#define RAM_LIMIT (768<<20) +#define RAM_LIMIT (64<<20) memory_node = find_devices("memory"); if (memory_node == NULL) { @@ -1384,7 +1428,7 @@ static void __init hash_init(void) { if ( ppc_md.progress ) ppc_md.progress("hash:patch", 0x345); Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); - __clear_user(Hash, Hash_size); + /*__clear_user(Hash, Hash_size);*/ /* * Patch up the instructions in head.S:hash_page diff --git a/arch/ppc/xmon/start.c b/arch/ppc/xmon/start.c index 9ed5f3805..f25060c94 100644 --- a/arch/ppc/xmon/start.c +++ b/arch/ppc/xmon/start.c @@ -8,19 +8,34 @@ #include <asm/page.h> #include <linux/adb.h> #include <linux/pmu.h> +#include <linux/kernel.h> #include <asm/prom.h> #include <asm/bootx.h> +#include <asm/feature.h> #include <asm/processor.h> static volatile unsigned char *sccc, *sccd; unsigned long TXRDY, RXRDY; extern void xmon_printf(const char *fmt, ...); -extern void map_bootx_text(void); extern void drawchar(char); extern void drawstring(const char *str); +static int xmon_expect(const char *str, unsigned int timeout); static int console = 0; static int use_screen = 0; +static int via_modem = 0; +static int xmon_use_sccb = 0; +static struct device_node *macio_node; + +#define TB_SPEED 25000000 + +static inline unsigned int readtb(void) +{ + unsigned int ret; + + asm volatile("mftb %0" : "=r" (ret) :); + return ret; +} void buf_access(void) { @@ -36,17 +51,19 @@ xmon_map_scc(void) if ( _machine == _MACH_Pmac ) { struct device_node *np; - extern boot_infos_t *boot_infos; unsigned long addr; - #ifdef CONFIG_BOOTX_TEXT - if (boot_infos != 0 && find_via_pmu()) { - printk("xmon uses screen and keyboard\n"); + extern boot_infos_t *disp_bi; + + /* needs to be hacked if xmon_printk is to be used + from within find_via_pmu() */ + if (!via_modem && disp_bi && find_via_pmu()) { + drawstring("xmon uses screen and keyboard\n"); use_screen = 1; - map_bootx_text(); return; } #endif + #ifdef CHRP_ESCC addr = 0xc1013020; #else @@ -57,9 +74,10 @@ xmon_map_scc(void) np = find_devices("mac-io"); if (np && np->n_addrs) { + macio_node = np; addr = np->addrs[0].address + 0x13000; - /* use the B channel on the iMac, A channel on others */ - if (addr >= 0xf0000000) + /* use the B channel on the iMac */ + if (!xmon_use_sccb) addr += 0x20; /* use A channel */ } base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE); @@ -70,22 +88,22 @@ xmon_map_scc(void) sccd = sccc + (0xf3013030 - 0xf3013020); #endif } - else if ( _machine & _MACH_chrp ) + else if ( _machine & _MACH_gemini ) { /* should already be mapped by the kernel boot */ - sccc = (volatile unsigned char *) (isa_io_base + 0x3fd); - sccd = (volatile unsigned char *) (isa_io_base + 0x3f8); + sccc = (volatile unsigned char *) 0xffeffb0d; + sccd = (volatile unsigned char *) 0xffeffb08; TXRDY = 0x20; RXRDY = 1; + console = 1; } - else if ( _machine & _MACH_gemini ) + else { /* should already be mapped by the kernel boot */ - sccc = (volatile unsigned char *) 0xffeffb0d; - sccd = (volatile unsigned char *) 0xffeffb08; + sccc = (volatile unsigned char *) (isa_io_base + 0x3fd); + sccd = (volatile unsigned char *) (isa_io_base + 0x3f8); TXRDY = 0x20; RXRDY = 1; - console = 1; } } @@ -98,7 +116,7 @@ int xmon_write(void *handle, void *ptr, int nb) { char *p = ptr; - int i, ct; + int i, c, ct; #ifdef CONFIG_BOOTX_TEXT if (use_screen) { @@ -111,20 +129,26 @@ xmon_write(void *handle, void *ptr, int nb) if (!scc_initialized) xmon_init_scc(); for (i = 0; i < nb; ++i) { -#ifdef CONFIG_ADB + ct = 0; while ((*sccc & TXRDY) == 0) +#ifdef CONFIG_ADB if (sys_ctrler == SYS_CTRLER_PMU) pmu_poll(); +#else + ; #endif /* CONFIG_ADB */ - buf_access(); - if ( console && (*p != '\r')) - printk("%c", *p); - ct = 0; - if ( *p == '\n') + c = p[i]; + if (c == '\n' && !ct) { + c = '\r'; ct = 1; - *sccd = *p++; - if ( ct ) - xmon_write(handle, "\r", 1); + --i; + } else { + if (console) + printk("%c", c); + ct = 0; + } + buf_access(); + *sccd = c; } return i; } @@ -206,36 +230,49 @@ xmon_read(void *handle, void *ptr, int nb) if (!scc_initialized) xmon_init_scc(); for (i = 0; i < nb; ++i) { -#ifdef CONFIG_ADB while ((*sccc & RXRDY) == 0) +#ifdef CONFIG_ADB if (sys_ctrler == SYS_CTRLER_PMU) pmu_poll(); +#else + ; #endif /* CONFIG_ADB */ buf_access(); -#if 0 - if ( 0/*console*/ ) - *p++ = ppc_md.kbd_getkeycode(); - else -#endif *p++ = *sccd; } return i; } +int +xmon_read_poll(void) +{ + if ((*sccc & RXRDY) == 0) { +#ifdef CONFIG_ADB + if (sys_ctrler == SYS_CTRLER_PMU) + pmu_poll(); +#else + ; +#endif + return -1; + } + buf_access(); + return *sccd; +} + static unsigned char scc_inittab[] = { 13, 0, /* set baud rate divisor */ 12, 1, 14, 1, /* baud rate gen enable, src=rtxc */ 11, 0x50, /* clocks = br gen */ - 5, 0x6a, /* tx 8 bits, assert RTS */ - 4, 0x44, /* x16 clock, 1 stop */ + 5, 0xea, /* tx 8 bits, assert DTR & RTS */ + 4, 0x46, /* x16 clock, 1 stop */ 3, 0xc1, /* rx enable, 8 bits */ }; void xmon_init_scc() { - if ( _machine & (_MACH_chrp|_MACH_gemini) ) + if ( _machine == _MACH_chrp ) { sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ sccd[0] = 3; eieio(); /* DLL = 38400 baud */ @@ -248,6 +285,14 @@ xmon_init_scc() { int i, x; + if (macio_node != 0) { + unsigned int t0; + + feature_set(macio_node, FEATURE_Modem_power); + t0 = readtb(); + while (readtb() - t0 < 3*TB_SPEED) + eieio(); + } for (i = 20000; i != 0; --i) { x = *sccc; eieio(); } @@ -259,6 +304,18 @@ xmon_init_scc() } } scc_initialized = 1; + if (via_modem) { + for (;;) { + xmon_write(0, "ATE1V1\r", 7); + if (xmon_expect("OK", 5)) { + xmon_write(0, "ATA\r", 4); + if (xmon_expect("CONNECT", 40)) + break; + } + xmon_write(0, "+++", 3); + xmon_expect("OK", 3); + } + } } #if 0 @@ -331,6 +388,35 @@ static char line[256]; static char *lineptr; static int lineleft; +int xmon_expect(const char *str, unsigned int timeout) +{ + int c; + unsigned int t0; + + timeout *= TB_SPEED; + t0 = readtb(); + do { + lineptr = line; + for (;;) { + c = xmon_read_poll(); + if (c == -1) { + if (readtb() - t0 > timeout) { + printk("timeout\n"); + return 0; + } + continue; + } + if (c == '\n') + break; + printk("%c", c); + if (c != '\r' && lineptr < &line[sizeof(line) - 1]) + *lineptr++ = c; + } + *lineptr = 0; + } while (strstr(line, str) == NULL); + return 1; +} + int xmon_getchar(void) { diff --git a/arch/ppc/xmon/xmon.c b/arch/ppc/xmon/xmon.c index 1c6a812d2..a0da2f1b4 100644 --- a/arch/ppc/xmon/xmon.c +++ b/arch/ppc/xmon/xmon.c @@ -1344,3 +1344,31 @@ char *str; { lineptr = str; } + +char last[64]; +char * +lookup_addr(unsigned long addr) +{ + extern char *sysmap; + extern unsigned long sysmap_size; + char *c = sysmap; + unsigned long cmp; + + if ( !sysmap || !sysmap_size ) + return NULL; + + /* adjust if addr is relative to kernelbase */ + if ( addr < PAGE_OFFSET ) + addr += PAGE_OFFSET; + + cmp = simple_strtoul(c, &c, 8); + strcpy( last, strsep( &c, "\n")); + while ( c < (sysmap+sysmap_size) ) + { + cmp = simple_strtoul(c, &c, 8); + if ( cmp < addr ) + break; + strcpy( last, strsep( &c, "\n")); + } + return last; +} diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 83e2f3e9e..458685f4a 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -41,7 +41,6 @@ unsigned long mmu_context_cache; static unsigned long totalram_pages = 0; static unsigned long totalhigh_pages = 0; -extern void show_net_buffers(void); extern unsigned long init_smp_mappings(unsigned long); /* @@ -173,9 +172,6 @@ void show_mem(void) printk("%d pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif } /* References to section boundaries */ diff --git a/arch/sparc/config.in b/arch/sparc/config.in index a983b9a6d..ebaa64806 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.84 2000/01/31 21:10:04 davem Exp $ +# $Id: config.in,v 1.86 2000/02/10 02:51:10 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -21,7 +21,7 @@ bool 'Symmetric multi-processing support (does not work on sun4/sun4c)' CONFIG_S bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4 if [ "$CONFIG_SUN4" != "y" ]; then - bool ' Support for PCI and PS/2 keyboard/mouse' CONFIG_PCI + bool 'Support for PCI and PS/2 keyboard/mouse' CONFIG_PCI source drivers/pci/Config.in fi @@ -118,11 +118,24 @@ if [ "$CONFIG_SCSI" != "n" ]; then comment 'SCSI support type (disk, tape, CDrom)' dep_tristate ' SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI + + if [ "$CONFIG_BLK_DEV_SD" != "n" ]; then + int 'Maximum number of SCSI disks that can be loaded as modules' CONFIG_SD_EXTRA_DEVS 40 + fi + dep_tristate ' SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI + + if [ "$CONFIG_BLK_DEV_ST" != "n" ]; then + int 'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2 + fi + dep_tristate ' SCSI CDROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI + if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then bool ' Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR + int 'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2 fi + dep_tristate ' SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' @@ -130,6 +143,7 @@ if [ "$CONFIG_SCSI" != "n" ]; then bool ' Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN bool ' Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS + bool ' SCSI logging facility' CONFIG_SCSI_LOGGING mainmenu_option next_comment comment 'SCSI low-level drivers' diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 536d0c5f6..6db042563 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -175,9 +175,12 @@ CONFIG_SCSI=y # SCSI support type (disk, tape, CDrom) # CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 CONFIG_CHR_DEV_ST=y +CONFIG_ST_EXTRA_DEVS=2 CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_SR_EXTRA_DEVS=2 CONFIG_CHR_DEV_SG=m # @@ -185,6 +188,7 @@ CONFIG_CHR_DEV_SG=m # CONFIG_SCSI_MULTI_LUN=y CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -245,7 +249,6 @@ CONFIG_AUTOFS4_FS=m CONFIG_AFFS_FS=m # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_BFS_FS_WRITE is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index 61a51f103..336ae208f 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -1,10 +1,28 @@ -/* $Id: ioport.c,v 1.30 2000/01/28 13:41:55 jj Exp $ +/* $Id: ioport.c,v 1.31 2000/02/06 22:55:32 zaitcev Exp $ * ioport.c: Simple io mapping allocator. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) * * 1996: sparc_free_io, 1999: ioremap()/iounmap() by Pete Zaitcev. + * + * 2000/01/29 + * <rth> zait: as long as pci_alloc_consistent produces something addressable, + * things are ok. + * <zaitcev> rth: no, it is relevant, because get_free_pages returns you a + * pointer into the big page mapping + * <rth> zait: so what? + * <rth> zait: remap_it_my_way(virt_to_phys(get_free_page())) + * <zaitcev> Hmm + * <zaitcev> Suppose I did this remap_it_my_way(virt_to_phys(get_free_page())). + * So far so good. + * <zaitcev> Now, driver calls pci_free_consistent(with result of + * remap_it_my_way()). + * <zaitcev> How do you find the address to pass to free_pages()? + * <rth> zait: walk the page tables? It's only two or three level after all. + * <rth> zait: you have to walk them anyway to remove the mapping. + * <zaitcev> Hmm + * <zaitcev> Sounds reasonable */ #include <linux/config.h> @@ -15,6 +33,8 @@ #include <linux/ioport.h> #include <linux/mm.h> #include <linux/malloc.h> +#include <linux/pci.h> /* struct pci_dev */ +#include <linux/proc_fs.h> #include <asm/io.h> #include <asm/vaddrs.h> @@ -23,8 +43,8 @@ #include <asm/pgalloc.h> #include <asm/pgtable.h> -struct resource *sparc_find_resource_bystart(struct resource *, unsigned long); -struct resource *sparc_find_resource_by_hit(struct resource *, unsigned long); +struct resource *_sparc_find_resource(struct resource *r, unsigned long); +int _sparc_len2order(unsigned long len); static void *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz); static void *_sparc_alloc_io(unsigned int busno, unsigned long phys, @@ -32,15 +52,22 @@ static void *_sparc_alloc_io(unsigned int busno, unsigned long phys, static void _sparc_free_io(struct resource *res); /* This points to the next to use virtual memory for DVMA mappings */ -static struct resource sparc_dvma = { +static struct resource _sparc_dvma = { "sparc_dvma", DVMA_VADDR, DVMA_VADDR + DVMA_LEN - 1 }; /* This points to the start of I/O mappings, cluable from outside. */ - struct resource sparc_iomap = { +/*ext*/ struct resource sparc_iomap = { "sparc_iomap", IOBASE_VADDR, IOBASE_END-1 }; /* + * BTFIXUP would do as well but it seems overkill for the case. + */ +static void (*_sparc_mapioaddr)(unsigned long pa, unsigned long va, + int bus, int ro); +static void (*_sparc_unmapioaddr)(unsigned long va); + +/* * Our mini-allocator... * Boy this is gross! We need it because we must map I/O for * timers and interrupt controller before the kmalloc is available. @@ -77,55 +104,6 @@ static void xres_free(struct xresource *xrp) { } /* - */ -extern void sun4c_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly); -extern void srmmu_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly); - -static void mapioaddr(unsigned long physaddr, unsigned long virt_addr, - int bus, int rdonly) -{ - switch(sparc_cpu_model) { - case sun4c: - case sun4: - sun4c_mapioaddr(physaddr, virt_addr, bus, rdonly); - break; - case sun4m: - case sun4d: - case sun4e: - srmmu_mapioaddr(physaddr, virt_addr, bus, rdonly); - break; - default: - printk("mapioaddr: Trying to map IO space for unsupported machine.\n"); - printk("mapioaddr: sparc_cpu_model = %d\n", sparc_cpu_model); - printk("mapioaddr: Halting...\n"); - halt(); - }; - return; -} - -extern void srmmu_unmapioaddr(unsigned long virt); -extern void sun4c_unmapioaddr(unsigned long virt); - -static void unmapioaddr(unsigned long virt_addr) -{ - switch(sparc_cpu_model) { - case sun4c: - case sun4: - sun4c_unmapioaddr(virt_addr); - break; - case sun4m: - case sun4d: - case sun4e: - srmmu_unmapioaddr(virt_addr); - break; - default: - printk("unmapioaddr: sparc_cpu_model = %d, halt...\n", sparc_cpu_model); - halt(); - }; - return; -} - -/* * These are typically used in PCI drivers * which are trying to be cross-platform. * @@ -147,7 +125,7 @@ void iounmap(void *virtual) unsigned long vaddr = (unsigned long) virtual & PAGE_MASK; struct resource *res; - if ((res = sparc_find_resource_bystart(&sparc_iomap, vaddr)) == NULL) { + if ((res = _sparc_find_resource(&sparc_iomap, vaddr)) == NULL) { printk("free_io/iounmap: cannot free %lx\n", vaddr); return; } @@ -161,7 +139,6 @@ void iounmap(void *virtual) } /* - * Davem's version of sbus_ioremap. */ unsigned long sbus_ioremap(struct resource *phyres, unsigned long offset, unsigned long size, char *name) @@ -218,8 +195,6 @@ static void *_sparc_alloc_io(unsigned int busno, unsigned long phys, } /* - * This is called from _sparc_alloc_io only, we left it separate - * in case Davem changes his mind about interface to sbus_ioremap(). */ static void * _sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz) @@ -240,7 +215,7 @@ _sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz) va = res->start; pa &= PAGE_MASK; for (psz = res->end - res->start + 1; psz != 0; psz -= PAGE_SIZE) { - mapioaddr(pa, va, bus, 0); + (*_sparc_mapioaddr)(pa, va, bus, 0); va += PAGE_SIZE; pa += PAGE_SIZE; } @@ -270,7 +245,7 @@ static void _sparc_free_io(struct resource *res) plen = res->end - res->start + 1; while (plen != 0) { plen -= PAGE_SIZE; - unmapioaddr(res->start + plen); + (*_sparc_unmapioaddr)(res->start + plen); } release_resource(res); @@ -305,9 +280,7 @@ void *sbus_alloc_consistent(struct sbus_dev *sdev, long len, u32 *dma_addrp) return NULL; } - for (order = 0; order < 6; order++) /* 2^6 pages == 256K */ - if ((1 << (order + PAGE_SHIFT)) >= len_total) - break; + order = _sparc_len2order(len_total); va = __get_free_pages(GFP_KERNEL, order); if (va == 0) { /* @@ -324,25 +297,17 @@ void *sbus_alloc_consistent(struct sbus_dev *sdev, long len, u32 *dma_addrp) } memset((char*)res, 0, sizeof(struct resource)); - if (allocate_resource(&sparc_dvma, res, len_total, - sparc_dvma.start, sparc_dvma.end, PAGE_SIZE, NULL, NULL) != 0) { + if (allocate_resource(&_sparc_dvma, res, len_total, + _sparc_dvma.start, _sparc_dvma.end, PAGE_SIZE, NULL, NULL) != 0) { printk("sbus_alloc_consistent: cannot occupy 0x%lx", len_total); free_pages(va, order); kfree(res); return NULL; } - *dma_addrp = res->start; mmu_map_dma_area(va, res->start, len_total); - /* - * "Official" or "natural" address of pages we got is va. - * We want to return uncached range. We could make va[len] - * uncached but it's difficult to make cached back [P3: hmm] - * We use the artefact of sun4c, replicated everywhere else, - * that CPU can use bus addresses to access the same memory. - */ - res->name = (void *)va; /* XXX Ouch.. we got to hide it somewhere */ + *dma_addrp = res->start; return (void *)res->start; } @@ -350,9 +315,8 @@ void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba) { struct resource *res; unsigned long pgp; - int order; - if ((res = sparc_find_resource_bystart(&sparc_dvma, + if ((res = _sparc_find_resource(&_sparc_dvma, (unsigned long)p)) == NULL) { printk("sbus_free_consistent: cannot free %p\n", p); return; @@ -370,17 +334,14 @@ void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba) return; } - mmu_inval_dma_area((unsigned long)res->name, n); /* XXX Ouch */ - mmu_unmap_dma_area(ba, n); release_resource(res); + kfree(res); - pgp = (unsigned long) res->name; /* XXX Ouch */ - for (order = 0; order < 6; order++) - if ((1 << (order + PAGE_SHIFT)) >= n) - break; - free_pages(pgp, order); + /* mmu_inval_dma_area(va, n); */ /* it's consistent, isn't it */ + pgp = (unsigned long) phys_to_virt(mmu_translate_dvma(ba)); + mmu_unmap_dma_area(ba, n); - kfree(res); + free_pages(pgp, _sparc_len2order(n)); } /* @@ -408,10 +369,10 @@ u32 sbus_map_single(struct sbus_dev *sdev, void *va, long len) return 0; } memset((char*)res, 0, sizeof(struct resource)); - res->name = va; + res->name = va; /* XXX */ - if (allocate_resource(&sparc_dvma, res, len_total, - sparc_dvma.start, sparc_dvma.end, PAGE_SIZE) != 0) { + if (allocate_resource(&_sparc_dvma, res, len_total, + _sparc_dvma.start, _sparc_dvma.end, PAGE_SIZE) != 0) { printk("sbus_map_single: cannot occupy 0x%lx", len); kfree(res); return 0; @@ -431,7 +392,6 @@ u32 sbus_map_single(struct sbus_dev *sdev, void *va, long len) if (len > 256*1024) { /* __get_free_pages() limit */ return 0; } -/* BTFIXUPDEF_CALL(__u32, mmu_get_scsi_one, char *, unsigned long, struct sbus_bus *sbus) */ return mmu_get_scsi_one(va, len, sdev->bus); #endif } @@ -442,7 +402,7 @@ void sbus_unmap_single(struct sbus_dev *sdev, u32 ba, long n) struct resource *res; unsigned long va; - if ((res = sparc_find_resource_bystart(&sparc_dvma, ba)) == NULL) { + if ((res = _sparc_find_resource(&_sparc_dvma, ba)) == NULL) { printk("sbus_unmap_single: cannot find %08x\n", (unsigned)ba); return; } @@ -461,14 +421,12 @@ void sbus_unmap_single(struct sbus_dev *sdev, u32 ba, long n) kfree(res); #endif #if 1 /* "trampoline" version */ -/* BTFIXUPDEF_CALL(void, mmu_release_scsi_one, __u32, unsigned long, struct sbus_bus *sbus) */ mmu_release_scsi_one(ba, n, sdev->bus); #endif } int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n) { -/* BTFIXUPDEF_CALL(void, mmu_get_scsi_sgl, struct scatterlist *, int, struct sbus_bus *sbus) */ mmu_get_scsi_sgl(sg, n, sdev->bus); /* @@ -480,65 +438,351 @@ int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n) void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n) { -/* BTFIXUPDEF_CALL(void, mmu_release_scsi_sgl, struct scatterlist *, int, struct sbus_bus *sbus) */ mmu_release_scsi_sgl(sg, n, sdev->bus); } -#endif /* - * P3: I think a partial flush is permitted... - * We are not too efficient at doing it though. - * - * If only DaveM understood a concept of an allocation cookie, - * we could avoid find_resource_by_hit() here and a major - * performance hit. */ void sbus_dma_sync_single(struct sbus_dev *sdev, u32 ba, long size) { unsigned long va; struct resource *res; - res = sparc_find_resource_by_hit(&sparc_dvma, ba); + /* We do not need the resource, just print a message if invalid. */ + res = _sparc_find_resource(&_sparc_dvma, ba); if (res == NULL) panic("sbus_dma_sync_single: 0x%x\n", ba); - va = (unsigned long) res->name; - /* if (va == 0) */ - - mmu_inval_dma_area(va, (res->end - res->start) + 1); + va = (unsigned long) phys_to_virt(mmu_translate_dvma(ba)); + mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); } void sbus_dma_sync_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n) { - printk("dma_sync_sg: not implemented yet\n"); + printk("sbus_dma_sync_sg: not implemented yet\n"); +} +#endif /* CONFIG_SBUS */ + +#ifdef CONFIG_PCI + +/* Allocate and map kernel buffer using consistent mode DMA for a device. + * hwdev should be valid struct pci_dev pointer for PCI devices. + */ +void *pci_alloc_consistent(struct pci_dev *pdev, size_t len, dma_addr_t *pba) +{ + unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK; + unsigned long va; + struct resource *res; + int order; + + if (len == 0) { + return NULL; + } + if (len > 256*1024) { /* __get_free_pages() limit */ + return NULL; + } + + order = _sparc_len2order(len_total); + va = __get_free_pages(GFP_KERNEL, order); + if (va == 0) { + printk("pci_alloc_consistent: no %ld pages\n", len_total>>PAGE_SHIFT); + return NULL; + } + + if ((res = kmalloc(sizeof(struct resource), GFP_KERNEL)) == NULL) { + free_pages(va, order); + printk("sbus_alloc_consistent: no core\n"); + return NULL; + } + memset((char*)res, 0, sizeof(struct resource)); + + if (allocate_resource(&_sparc_dvma, res, len_total, + _sparc_dvma.start, _sparc_dvma.end, PAGE_SIZE, NULL, NULL) != 0) { + printk("pci_alloc_consistent: cannot occupy 0x%lx", len_total); + free_pages(va, order); + kfree(res); + return NULL; + } + + mmu_inval_dma_area(va, len_total); + +#if 1 +/* P3 */ printk("pci_alloc_consistent: kva %lx uncva %lx phys %lx size %x\n", + (long)va, (long)res->start, (long)virt_to_phys(va), len_total); +#endif + { + unsigned long xva, xpa; + xva = res->start; + xpa = virt_to_phys(va); + while (len_total != 0) { + len_total -= PAGE_SIZE; + (*_sparc_mapioaddr)(xpa, xva, 0, 0); + xva += PAGE_SIZE; + xpa += PAGE_SIZE; + } + } + + *pba = virt_to_bus(va); + return (void *) res->start; } +/* Free and unmap a consistent DMA buffer. + * cpu_addr is what was returned from pci_alloc_consistent, + * size must be the same as what as passed into pci_alloc_consistent, + * and likewise dma_addr must be the same as what *dma_addrp was set to. + * + * References to the memory and mappings assosciated with cpu_addr/dma_addr + * past this call are illegal. + */ +void pci_free_consistent(struct pci_dev *pdev, size_t n, void *p, dma_addr_t ba) +{ + struct resource *res; + unsigned long pgp; + + if ((res = _sparc_find_resource(&_sparc_dvma, + (unsigned long)p)) == NULL) { + printk("sbus_free_consistent: cannot free %p\n", p); + return; + } + + if (((unsigned long)p & (PAGE_MASK-1)) != 0) { + printk("sbus_free_consistent: unaligned va %p\n", p); + return; + } + + n = (n + PAGE_SIZE-1) & PAGE_MASK; + if ((res->end-res->start)+1 != n) { + printk("sbus_free_consistent: region 0x%lx asked 0x%lx\n", + (long)((res->end-res->start)+1), (long)n); + return; + } + + pgp = (unsigned long) bus_to_virt(ba); + mmu_inval_dma_area(pgp, n); + { + int x; + for (x = 0; x < n; x += PAGE_SIZE) { + (*_sparc_unmapioaddr)(p + n); + } + } + + release_resource(res); + kfree(res); + + free_pages(pgp, _sparc_len2order(n)); +} + +/* Map a single buffer of the indicated size for DMA in streaming mode. + * The 32-bit bus address to use is returned. + * + * Once the device is given the dma address, the device owns this memory + * until either pci_unmap_single or pci_dma_sync_single is performed. + */ +dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size) +{ + return virt_to_bus(ptr); +} + +/* Unmap a single streaming mode DMA translation. The dma_addr and size + * must match what was provided for in a previous pci_map_single call. All + * other usages are undefined. + * + * After this call, reads by the cpu to the buffer are guarenteed to see + * whatever the device wrote there. + */ +void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size) +{ + /* Nothing to do... */ +} + +/* Map a set of buffers described by scatterlist in streaming + * mode for DMA. This is the scather-gather version of the + * above pci_map_single interface. Here the scatter gather list + * elements are each tagged with the appropriate dma address + * and length. They are obtained via sg_dma_{address,length}(SG). + * + * NOTE: An implementation may be able to use a smaller number of + * DMA address/length pairs than there are SG table elements. + * (for example via virtual mapping capabilities) + * The routine returns the number of addr/length pairs actually + * used, at most nents. + * + * Device ownership issues as mentioned above for pci_map_single are + * the same here. + */ +int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents) +{ + int n; + for (n = 0; n < nents; n++) { + sg->dvma_address = virt_to_bus(sg->address); + sg->dvma_length = sg->length; + sg++; + } + return nents; +} + +/* Unmap a set of streaming mode DMA translations. + * Again, cpu read rules concerning calls here are the same as for + * pci_unmap_single() above. + */ +void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nhwents) +{ + /* Nothing to do... */ +} + +/* Make physical memory consistent for a single + * streaming mode DMA translation after a transfer. + * + * If you perform a pci_map_single() but wish to interrogate the + * buffer using the cpu, yet do not wish to teardown the PCI dma + * mapping, you must call this function before doing so. At the + * next point you give the PCI dma address back to the card, the + * device again owns the buffer. + */ +void pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t ba, size_t size) +{ + mmu_inval_dma_area((unsigned long)bus_to_virt(ba), + (size + PAGE_SIZE-1) & PAGE_MASK); +} + +/* Make physical memory consistent for a set of streaming + * mode DMA translations after a transfer. + * + * The same as pci_dma_sync_single but for a scatter-gather list, + * same rules and usage. + */ +void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents) +{ + while (nents) { + --nents; + mmu_inval_dma_area((unsigned long)sg->address, + (sg->dvma_length + PAGE_SIZE-1) & PAGE_MASK); + sg++; + } +} +#endif CONFIG_PCI + +#ifdef CONFIG_PROC_FS + +static int +_sparc_io_get_info(char *buf, char **start, off_t fpos, int length, int *eof, + void *data) +{ + char *p = buf, *e = buf + length; + struct resource *r; + const char *nm; + + for (r = ((struct resource *)data)->child; r != NULL; r = r->sibling) { + if (p + 32 >= e) /* Better than nothing */ + break; + if ((nm = r->name) == 0) nm = "???"; + p += sprintf(p, "%08lx-%08lx: %s\n", r->start, r->end, nm); + } + + return p-buf; +} + +static struct proc_dir_entry _sparc_iomap_proc_entry = { + 0, /* Inode number - dynamic */ + 6, /* Length of the file name */ + "io_map", /* The file name */ + S_IFREG | S_IRUGO, /* File mode */ + 1, /* Number of links */ + 0, 0, /* The uid and gid for the file */ + 0, /* The size of the file reported by ls. */ + NULL, /* struct inode_operations * ops */ + NULL, /* get_info: backward compatibility */ + NULL, /* owner */ + NULL, NULL, NULL, /* linkage */ + &sparc_iomap, + _sparc_io_get_info, /* The read function for this file */ + NULL, + /* and more stuff */ +}; + +static struct proc_dir_entry _sparc_dvma_proc_entry = { + 0, /* Inode number - dynamic */ + 8, /* Length of the file name */ + "dvma_map", /* The file name */ + S_IFREG | S_IRUGO, /* File mode */ + 1, /* Number of links */ + 0, 0, /* The uid and gid for the file */ + 0, /* The size of the file reported by ls. */ + NULL, /* struct inode_operations * ops */ + NULL, /* get_info: backward compatibility */ + NULL, /* owner */ + NULL, NULL, NULL, /* linkage */ + &_sparc_dvma, + _sparc_io_get_info, + NULL, + /* some more stuff */ +}; + +#endif CONFIG_PROC_FS + /* * This is a version of find_resource and it belongs to kernel/resource.c. * Until we have agreement with Linus and Martin, it lingers here. * - * "same start" is more strict than "hit into" + * XXX Too slow. Can have 8192 DVMA pages on sun4m in the worst case. + * This probably warrants some sort of hashing. */ struct resource * -sparc_find_resource_bystart(struct resource *root, unsigned long start) +_sparc_find_resource(struct resource *root, unsigned long hit) { struct resource *tmp; - for (tmp = root->child; tmp != 0; tmp = tmp->sibling) { - if (tmp->start == start) + for (tmp = root->child; tmp != 0; tmp = tmp->sibling) { + if (tmp->start <= hit && tmp->end >= hit) return tmp; - } - return NULL; + } + return NULL; } -struct resource * -sparc_find_resource_by_hit(struct resource *root, unsigned long hit) +int +_sparc_len2order(unsigned long len) { - struct resource *tmp; + int order; - for (tmp = root->child; tmp != 0; tmp = tmp->sibling) { - if (tmp->start <= hit && tmp->end >= hit) - return tmp; - } - return NULL; + for (order = 0; order < 7; order++) /* 2^6 pages == 256K */ + if ((1 << (order + PAGE_SHIFT)) >= len) + return order; + printk("len2order: from %p: len %lu(0x%lx) yields order >=7.\n", + __builtin_return_address(0), len, len); + return 1; +} + +/* + * Necessary boot time initializations. + */ + +void ioport_init(void) +{ + extern void sun4c_mapioaddr(unsigned long, unsigned long, int, int); + extern void srmmu_mapioaddr(unsigned long, unsigned long, int, int); + extern void sun4c_unmapioaddr(unsigned long); + extern void srmmu_unmapioaddr(unsigned long); + + switch(sparc_cpu_model) { + case sun4c: + case sun4: + case sun4e: + _sparc_mapioaddr = sun4c_mapioaddr; + _sparc_unmapioaddr = sun4c_unmapioaddr; + break; + case sun4m: + case sun4d: + _sparc_mapioaddr = srmmu_mapioaddr; + _sparc_unmapioaddr = srmmu_unmapioaddr; + break; + default: + printk("ioport_init: cpu type %d is unknown.\n", + sparc_cpu_model); + halt(); + }; + +#ifdef CONFIG_PROC_FS + proc_register(&proc_root, &_sparc_iomap_proc_entry); + proc_register(&proc_root, &_sparc_dvma_proc_entry); +#endif } diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c index 8e8fab320..471929a01 100644 --- a/arch/sparc/kernel/irq.c +++ b/arch/sparc/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.100 2000/01/29 01:38:04 anton Exp $ +/* $Id: irq.c,v 1.101 2000/02/09 11:15:03 davem Exp $ * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the * Sparc the IRQ's are basically 'cast in stone' * and you are supposed to probe the prom's device @@ -205,9 +205,6 @@ unsigned int local_irq_count; unsigned int local_bh_count[NR_CPUS]; unsigned int local_irq_count[NR_CPUS]; -atomic_t global_bh_lock = ATOMIC_INIT(0); -spinlock_t global_bh_count = SPIN_LOCK_UNLOCKED; - /* Who has global_irq_lock. */ unsigned char global_irq_holder = NO_PROC_ID; @@ -217,9 +214,6 @@ spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; /* Global IRQ locking depth. */ atomic_t global_irq_count = ATOMIC_INIT(0); -/* This protects BH software state (masks, things like that). */ -spinlock_t sparc_bh_lock = SPIN_LOCK_UNLOCKED; - void smp_show_backtrace_all_cpus(void); void show_backtrace(void); @@ -239,7 +233,7 @@ static void show(char * str) } printk("]\n"); - printk("bh: %d [ ", (spin_is_locked(&global_bh_count) ? 1 : 0)); + printk("bh: %d [ ", (spin_is_locked(&global_bh_lock) ? 1 : 0)); for (i = 0; i < NR_CPUS; i++) { printk("%d ", local_bh_count[cpu]); @@ -253,18 +247,6 @@ static void show(char * str) #endif } -static inline void wait_on_bh(void) -{ - int count = MAXCOUNT; - do { - if(!--count) { - show("wait_on_bh"); - count = 0; - } - barrier(); - } while(spin_is_locked(&global_bh_count)); -} - /* * We have to allow irqs to arrive between __sti and __cli */ @@ -281,7 +263,7 @@ static inline void wait_on_irq(int cpu) * already executing in one.. */ if (!atomic_read(&global_irq_count)) { - if (local_bh_count[cpu] || !spin_is_locked(&global_bh_count)) + if (local_bh_count[cpu] || !spin_is_locked(&global_bh_lock)) break; } @@ -300,7 +282,7 @@ static inline void wait_on_irq(int cpu) continue; if (spin_is_locked (&global_irq_lock)) continue; - if (!local_bh_count[cpu] && spin_is_locked(&global_bh_count)) + if (!local_bh_count[cpu] && spin_is_locked(&global_bh_lock)) continue; if (spin_trylock(&global_irq_lock)) break; @@ -310,20 +292,6 @@ static inline void wait_on_irq(int cpu) /* * This is called when we want to synchronize with - * bottom half handlers. We need to wait until - * no other CPU is executing any bottom half handler. - * - * Don't wait if we're already running in an interrupt - * context or are inside a bh handler. - */ -void synchronize_bh(void) -{ - if (spin_is_locked (&global_bh_count) && !in_interrupt()) - wait_on_bh(); -} - -/* - * This is called when we want to synchronize with * interrupts. We may for example tell a device to * stop sending interrupts: but to make sure there * are no interrupts that are executing on another diff --git a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap.S index 7b056b47a..dcfc2dc78 100644 --- a/arch/sparc/kernel/rtrap.S +++ b/arch/sparc/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.53 2000/01/08 16:38:18 anton Exp $ +/* $Id: rtrap.S,v 1.54 2000/02/09 11:15:03 davem Exp $ * rtrap.S: Return from Sparc trap low-level code. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -48,16 +48,18 @@ rtrap_7win_patch5: and %g1, 0x7f, %g1 .globl rtrap_patch3, rtrap_patch4, rtrap_patch5 .globl C_LABEL(ret_trap_lockless_ipi) ret_trap_entry: - sethi %hi(C_LABEL(bh_active)), %l3 - sethi %hi(C_LABEL(bh_mask)), %l4 - ld [%l4 + %lo(C_LABEL(bh_mask))], %g5 - ld [%l3 + %lo(C_LABEL(bh_active))], %g4 + ld [%curptr + AOFF_task_processor], %l3 + sll %l3, 5, %l3 + sethi %hi(C_LABEL(softirq_state)), %l4 + add %l4, %l3, %l4 + ld [%l4 + %lo(C_LABEL(softirq_state))], %g5 + ld [%l4 + %lo(C_LABEL(softirq_state) + 4)], %g4 andcc %g4, %g5, %g0 be C_LABEL(ret_trap_lockless_ipi) nop - call C_LABEL(do_bottom_half) + call C_LABEL(do_softirq) nop - + C_LABEL(ret_trap_lockless_ipi): andcc %t_psr, PSR_PS, %g0 be 1f diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 8df494f2d..d1b3eca63 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc_ksyms.c,v 1.88 2000/01/28 13:41:55 jj Exp $ +/* $Id: sparc_ksyms.c,v 1.89 2000/02/09 11:15:03 davem Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -11,8 +11,10 @@ #include <linux/config.h> #include <linux/module.h> +#include <linux/smp.h> #include <linux/types.h> #include <linux/string.h> +#include <linux/sched.h> #include <linux/interrupt.h> #include <linux/in6.h> #include <linux/spinlock.h> diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index 3815cae1b..fde9bccfd 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.59 2000/01/29 07:40:10 davem Exp $ +/* $Id: sys_sparc.c,v 1.60 2000/02/08 20:24:18 davem Exp $ * linux/arch/sparc/kernel/sys_sparc.c * * This file contains various random system calls that @@ -9,7 +9,6 @@ #include <linux/errno.h> #include <linux/types.h> #include <linux/sched.h> -#include <linux/config.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/file.h> diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index 3e52fef95..5a03e646b 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.51 2000/01/29 01:08:59 anton Exp $ +/* $Id: time.c,v 1.53 2000/02/09 21:11:04 davem Exp $ * linux/arch/sparc/kernel/time.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -429,12 +429,14 @@ void __init time_init(void) extern __inline__ unsigned long do_gettimeoffset(void) { + struct tasklet_struct *t; unsigned long offset = 0; unsigned int count; count = (*master_l10_counter >> 10) & 0x1fffff; - if(test_bit(TIMER_BH, &bh_active)) + t = &bh_task_vec[TIMER_BH]; + if (test_bit(TASKLET_STATE_SCHED, &t->state)) offset = 1000000; return offset + count; diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index 9e9a225a5..6736dc9d3 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.79 2000/01/29 01:09:06 anton Exp $ +/* $Id: init.c,v 1.80 2000/02/09 21:11:06 davem Exp $ * linux/arch/sparc/mm/init.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -33,8 +33,6 @@ #include <asm/pgtable.h> #include <asm/vaddrs.h> -extern void show_net_buffers(void); - unsigned long *sparc_valid_addr_bitmap; unsigned long phys_base; @@ -89,9 +87,6 @@ void show_mem(void) printk("%ld entries in page dir cache\n",pgd_cache_size); #endif show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif } extern pgprot_t protection_map[16]; diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c index 90b4aee69..a370ea2d4 100644 --- a/arch/sparc/mm/io-unit.c +++ b/arch/sparc/mm/io-unit.c @@ -1,4 +1,4 @@ -/* $Id: io-unit.c,v 1.20 2000/01/15 00:51:27 anton Exp $ +/* $Id: io-unit.c,v 1.21 2000/02/06 22:55:45 zaitcev Exp $ * io-unit.c: IO-UNIT specific routines for memory management. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -210,6 +210,20 @@ static void iounit_map_dma_area(unsigned long va, __u32 addr, int len) static void iounit_unmap_dma_area(unsigned long addr, int len) { + /* XXX Somebody please fill this in */ +} + +/* XXX We do not pass sbus device here, bad. */ +static unsigned long iounit_translate_dvma(unsigned long addr) +{ + struct sbus_bus *sbus = sbus_root; /* They are all the same */ + struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu; + int i; + iopte_t *iopte; + + i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT); + iopte = (iopte_t *)(iounit->page_table + i); + return (iopte_val(*iopte) & 0xFFFFFFF0) << 4; /* XXX sun4d guru, help */ } #endif @@ -237,6 +251,7 @@ void __init ld_mmu_iounit(void) #ifdef CONFIG_SBUS BTFIXUPSET_CALL(mmu_map_dma_area, iounit_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, iounit_unmap_dma_area, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_translate_dvma, iounit_translate_dvma, BTFIXUPCALL_NORM); #endif } diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c index 5a8cc8c5b..5fde9a52a 100644 --- a/arch/sparc/mm/iommu.c +++ b/arch/sparc/mm/iommu.c @@ -1,4 +1,4 @@ -/* $Id: iommu.c,v 1.18 2000/01/15 00:51:27 anton Exp $ +/* $Id: iommu.c,v 1.19 2000/02/06 22:55:45 zaitcev Exp $ * iommu.c: IOMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -289,8 +289,32 @@ static void iommu_map_dma_area(unsigned long va, __u32 addr, int len) iommu_invalidate(iommu->regs); } -static void iommu_unmap_dma_area(unsigned long addr, int len) +static void iommu_unmap_dma_area(unsigned long busa, int len) { + struct iommu_struct *iommu = sbus_root->iommu; + iopte_t *iopte = iommu->page_table; + unsigned long end; + + iopte += ((busa - iommu->start) >> PAGE_SHIFT); + end = PAGE_ALIGN((busa + len)); + while (busa < end) { + iopte_val(*iopte++) = 0; + busa += PAGE_SIZE; + } + flush_tlb_all(); /* P3: Hmm... it would not hurt. */ + iommu_invalidate(iommu->regs); +} + +static unsigned long iommu_translate_dvma(unsigned long busa) +{ + struct iommu_struct *iommu = sbus_root->iommu; + iopte_t *iopte = iommu->page_table; + unsigned long pa; + + iopte += ((busa - iommu->start) >> PAGE_SHIFT); + pa = pte_val(*iopte); + pa = (pa & 0xFFFFFFF0) << 4; /* Loose higher bits of 36 */ + return pa + PAGE_OFFSET; } #endif @@ -327,5 +351,6 @@ void __init ld_mmu_iommu(void) #ifdef CONFIG_SBUS BTFIXUPSET_CALL(mmu_map_dma_area, iommu_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, iommu_unmap_dma_area, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_translate_dvma, iommu_translate_dvma, BTFIXUPCALL_NORM); #endif } diff --git a/arch/sparc/mm/loadmmu.c b/arch/sparc/mm/loadmmu.c index ce0885679..e388c31f5 100644 --- a/arch/sparc/mm/loadmmu.c +++ b/arch/sparc/mm/loadmmu.c @@ -1,4 +1,4 @@ -/* $Id: loadmmu.c,v 1.54 2000/01/29 01:09:07 anton Exp $ +/* $Id: loadmmu.c,v 1.56 2000/02/08 20:24:21 davem Exp $ * loadmmu.c: This code loads up all the mm function pointers once the * machine type has been determined. It also sets the static * mmu values such as PAGE_NONE, etc. @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/init.h> -#include <linux/config.h> #include <asm/system.h> #include <asm/page.h> @@ -27,6 +26,7 @@ unsigned int pg_iobits; extern void ld_mmu_sun4c(void); extern void ld_mmu_srmmu(void); +extern void ioport_init(void); void __init load_mmu(void) { @@ -44,4 +44,5 @@ void __init load_mmu(void) prom_halt(); } btfixup(); + ioport_init(); } diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 10990f3ea..d46b45378 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.205 2000/01/21 17:59:46 anton Exp $ +/* $Id: srmmu.c,v 1.206 2000/02/08 07:45:59 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -1304,7 +1304,7 @@ void __init srmmu_paging_init(void) sparc_context_init(num_contexts); { - unsigned int zones_size[MAX_NR_ZONES] = { 0, 0, 0}; + unsigned long zones_size[MAX_NR_ZONES] = { 0, 0, 0}; zones_size[ZONE_DMA] = end_pfn; free_area_init(zones_size); diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index e0bd738d6..715bdb864 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.185 2000/01/15 00:51:32 anton Exp $ +/* $Id: sun4c.c,v 1.187 2000/02/08 07:46:01 davem Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -584,15 +584,24 @@ static void sun4c_map_dma_area(unsigned long va, u32 addr, int len) } } -static void sun4c_unmap_dma_area(unsigned long addr, int len) +static unsigned long sun4c_translate_dvma(unsigned long busa) { + /* Fortunately for us, bus_addr == uncached_virt in sun4c. */ + unsigned long pte = sun4c_get_pte(busa); + return (pte << PAGE_SHIFT) + PAGE_OFFSET; } -static void sun4c_inval_dma_area(unsigned long addr, int len) +static unsigned long sun4c_unmap_dma_area(unsigned long busa, int len) { + /* Fortunately for us, bus_addr == uncached_virt in sun4c. */ + /* XXX Implement this */ } -static void sun4c_flush_dma_area(unsigned long addr, int len) +static void sun4c_inval_dma_area(unsigned long virt, int len) +{ +} + +static void sun4c_flush_dma_area(unsigned long virt, int len) { } @@ -2574,7 +2583,7 @@ void __init sun4c_paging_init(void) sparc_context_init(num_contexts); { - unsigned int zones_size[MAX_NR_ZONES] = { 0, 0, 0}; + unsigned long zones_size[MAX_NR_ZONES] = { 0, 0, 0}; zones_size[ZONE_DMA] = end_pfn; free_area_init(zones_size); @@ -2721,6 +2730,7 @@ void __init ld_mmu_sun4c(void) BTFIXUPSET_CALL(mmu_map_dma_area, sun4c_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, sun4c_unmap_dma_area, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(mmu_translate_dvma, sun4c_translate_dvma, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_flush_dma_area, sun4c_flush_dma_area, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(mmu_inval_dma_area, sun4c_inval_dma_area, BTFIXUPCALL_NORM); diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c index a4a040ca2..1530c80c2 100644 --- a/arch/sparc/prom/bootstr.c +++ b/arch/sparc/prom/bootstr.c @@ -1,10 +1,9 @@ -/* $Id: bootstr.c,v 1.19 2000/01/29 01:09:11 anton Exp $ +/* $Id: bootstr.c,v 1.20 2000/02/08 20:24:23 davem Exp $ * bootstr.c: Boot string/argument acquisition from the PROM. * * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu) */ -#include <linux/config.h> #include <linux/string.h> #include <asm/oplib.h> #include <asm/sun4prom.h> diff --git a/arch/sparc/prom/console.c b/arch/sparc/prom/console.c index 2305dc858..c2b857459 100644 --- a/arch/sparc/prom/console.c +++ b/arch/sparc/prom/console.c @@ -1,4 +1,4 @@ -/* $Id: console.c,v 1.21 2000/01/29 01:09:12 anton Exp $ +/* $Id: console.c,v 1.22 2000/02/08 20:24:23 davem Exp $ * console.c: Routines that deal with sending and receiving IO * to/from the current console device using the PROM. * @@ -6,7 +6,6 @@ * Copyright (C) 1998 Pete Zaitcev <zaitcev@metabyte.com> */ -#include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> diff --git a/arch/sparc/prom/printf.c b/arch/sparc/prom/printf.c index 24ac381a8..331a2d00b 100644 --- a/arch/sparc/prom/printf.c +++ b/arch/sparc/prom/printf.c @@ -1,4 +1,4 @@ -/* $Id: printf.c,v 1.6 2000/01/29 01:09:12 anton Exp $ +/* $Id: printf.c,v 1.7 2000/02/08 20:24:23 davem Exp $ * printf.c: Internal prom library printf facility. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -8,7 +8,6 @@ * about or use it! It's simple and smelly anyway.... */ -#include <linux/config.h> #include <linux/kernel.h> #include <asm/openprom.h> diff --git a/arch/sparc/vmlinux.lds b/arch/sparc/vmlinux.lds index b3dfc253e..dbe55e5fe 100644 --- a/arch/sparc/vmlinux.lds +++ b/arch/sparc/vmlinux.lds @@ -73,4 +73,5 @@ SECTIONS .debug_pubnames 0 : { *(.debug_pubnames) } .debug_sfnames 0 : { *(.debug_sfnames) } .line 0 : { *(.line) } + /DISCARD/ : { *(.text.exit) *(.data.exit) } } diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index fdf3960e3..6aebbfcc8 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.89 2000/01/31 21:10:10 davem Exp $ +# $Id: config.in,v 1.95 2000/02/10 02:51:12 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -17,13 +17,6 @@ define_bool CONFIG_VT_CONSOLE y bool 'Symmetric multi-processing support' CONFIG_SMP -mainmenu_option next_comment -comment 'Console drivers' -bool 'PROM console' CONFIG_PROM_CONSOLE -bool 'Support Frame buffer devices' CONFIG_FB -source drivers/video/Config.in -endmenu - # Global things across all Sun machines. define_bool CONFIG_SBUS y define_bool CONFIG_SBUSCHAR y @@ -38,6 +31,14 @@ define_bool CONFIG_SUN_AUXIO y define_bool CONFIG_SUN_IO y bool 'PCI support' CONFIG_PCI source drivers/pci/Config.in + +mainmenu_option next_comment +comment 'Console drivers' +bool 'PROM console' CONFIG_PROM_CONSOLE +bool 'Support Frame buffer devices' CONFIG_FB +source drivers/video/Config.in +endmenu + source drivers/sbus/char/Config.in source drivers/sbus/audio/Config.in @@ -129,11 +130,24 @@ if [ "$CONFIG_SCSI" != "n" ]; then comment 'SCSI support type (disk, tape, CDrom)' dep_tristate ' SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI + + if [ "$CONFIG_BLK_DEV_SD" != "n" ]; then + int 'Maximum number of SCSI disks that can be loaded as modules' CONFIG_SD_EXTRA_DEVS 40 + fi + dep_tristate ' SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI + + if [ "$CONFIG_BLK_DEV_ST" != "n" ]; then + int 'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2 + fi + dep_tristate ' SCSI CDROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI + if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then bool ' Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR + int 'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2 fi + dep_tristate ' SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' @@ -141,6 +155,7 @@ if [ "$CONFIG_SCSI" != "n" ]; then bool ' Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN bool ' Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS + bool ' SCSI logging facility' CONFIG_SCSI_LOGGING mainmenu_option next_comment comment 'SCSI low-level drivers' @@ -198,26 +213,48 @@ if [ "$CONFIG_NET" = "y" ]; then bool ' Keepalive and linefill' CONFIG_SLIP_SMART bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6 fi - bool ' Sun LANCE support' CONFIG_SUNLANCE - tristate ' Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL + + mainmenu_option next_comment + comment 'Ethernet (10 or 100Mbit)' + + bool 'Sun LANCE support' CONFIG_SUNLANCE + tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate ' Sun BigMAC 10/100baseT support (EXPERIMENTAL)' CONFIG_SUNBMAC + tristate 'Sun BigMAC 10/100baseT support (EXPERIMENTAL)' CONFIG_SUNBMAC fi tristate ' Sun QuadEthernet support' CONFIG_SUNQE - tristate ' MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS if [ "$CONFIG_PCI" = "y" ]; then - tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 - tristate ' 3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX - tristate ' RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 - tristate ' PCI NE2000 support' CONFIG_NE2K_PCI - tristate ' EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 - tristate ' Adaptec Starfire support' CONFIG_ADAPTEC_STARFIRE + tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 + tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX + tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 + tristate 'PCI NE2000 support' CONFIG_NE2K_PCI + tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 + tristate 'Adaptec Starfire support' CONFIG_ADAPTEC_STARFIRE fi + endmenu + + mainmenu_option next_comment + comment 'Ethernet (1000 Mbit)' + + if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC + if [ "$CONFIG_ACENIC" != "n" ]; then + bool ' Omit support for old Tigon I based AceNICs' CONFIG_ACENIC_OMIT_TIGON_I + fi + tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN + fi + tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS + endmenu + # bool ' FDDI driver support' CONFIG_FDDI # if [ "$CONFIG_FDDI" = "y" ]; then # fi + + if [ "$CONFIG_ATM" = "y" ]; then + source drivers/atm/Config.in + fi fi -endmenu + endmenu fi # This one must be before the filesystem configs. -DaveM diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 3e841d71b..747ded255 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -13,6 +13,19 @@ CONFIG_EXPERIMENTAL=y CONFIG_VT=y CONFIG_VT_CONSOLE=y # CONFIG_SMP is not set +CONFIG_SBUS=y +CONFIG_SBUSCHAR=y +CONFIG_BUSMOUSE=y +CONFIG_SUN_MOUSE=y +CONFIG_SERIAL=y +CONFIG_SUN_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +CONFIG_SUN_KEYBOARD=y +CONFIG_SUN_CONSOLE=y +CONFIG_SUN_AUXIO=y +CONFIG_SUN_IO=y +CONFIG_PCI=y +CONFIG_PCI_NAMES=y # # Console drivers @@ -51,19 +64,6 @@ CONFIG_FBCON_CFB32=y CONFIG_FBCON_FONTWIDTH8_ONLY=y CONFIG_FONT_SUN8x16=y # CONFIG_FBCON_FONTS is not set -CONFIG_SBUS=y -CONFIG_SBUSCHAR=y -CONFIG_BUSMOUSE=y -CONFIG_SUN_MOUSE=y -CONFIG_SERIAL=y -CONFIG_SUN_SERIAL=y -CONFIG_SERIAL_CONSOLE=y -CONFIG_SUN_KEYBOARD=y -CONFIG_SUN_CONSOLE=y -CONFIG_SUN_AUXIO=y -CONFIG_SUN_IO=y -CONFIG_PCI=y -CONFIG_PCI_NAMES=y # # Misc Linux/SPARC drivers @@ -75,10 +75,6 @@ CONFIG_OBP_FLASH=m # CONFIG_SUN_BPP is not set # CONFIG_SUN_VIDEOPIX is not set CONFIG_SUN_AURORA=m -# CONFIG_TADPOLE_TS102_UCTRL is not set -# CONFIG_SUN_JSFLASH is not set -CONFIG_APM_RTC_IS_GMT=y -# CONFIG_RTC is not set # # Linux/SPARC audio subsystem (EXPERIMENTAL) @@ -203,9 +199,12 @@ CONFIG_SCSI=y # SCSI support type (disk, tape, CDrom) # CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 CONFIG_CHR_DEV_ST=y +CONFIG_ST_EXTRA_DEVS=2 CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_SR_EXTRA_DEVS=2 CONFIG_CHR_DEV_SG=m # @@ -213,6 +212,7 @@ CONFIG_CHR_DEV_SG=m # CONFIG_SCSI_MULTI_LUN=y CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -263,11 +263,14 @@ CONFIG_SLIP=m CONFIG_SLIP_COMPRESSED=y CONFIG_SLIP_SMART=y # CONFIG_SLIP_MODE_SLIP6 is not set + +# +# Ethernet (10 or 100Mbit) +# CONFIG_SUNLANCE=y CONFIG_HAPPYMEAL=y CONFIG_SUNBMAC=m CONFIG_SUNQE=m -CONFIG_MYRI_SBUS=m CONFIG_DE4X5=m CONFIG_VORTEX=m CONFIG_RTL8139=m @@ -276,6 +279,14 @@ CONFIG_EEXPRESS_PRO100=m CONFIG_ADAPTEC_STARFIRE=m # +# Ethernet (1000 Mbit) +# +CONFIG_ACENIC=m +# CONFIG_ACENIC_OMIT_TIGON_I is not set +CONFIG_SK98LIN=m +CONFIG_MYRI_SBUS=m + +# # Unix 98 PTY support # CONFIG_UNIX98_PTYS=y @@ -297,7 +308,6 @@ CONFIG_AUTOFS4_FS=m CONFIG_AFFS_FS=m # CONFIG_HFS_FS is not set CONFIG_BFS_FS=m -# CONFIG_BFS_FS_WRITE is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index c84f5872e..f06ece682 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.50 1999/12/21 04:02:24 davem Exp $ +# $Id: Makefile,v 1.51 2000/02/08 05:11:31 jj Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -19,13 +19,13 @@ O_TARGET := kernel.o O_OBJS := process.o setup.o cpu.o idprom.o \ traps.o devices.o auxio.o \ irq.o ptrace.o time.o sys_sparc.o signal.o \ - unaligned.o central.o pci.o pci_common.o pci_iommu.o \ - pci_psycho.o pci_sabre.o starfire.o semaphore.o \ + unaligned.o central.o pci.o starfire.o semaphore.o \ power.o sbus.o iommu_common.o OX_OBJS := sparc64_ksyms.o ifdef CONFIG_PCI - O_OBJS += ebus.o + O_OBJS += ebus.o pci_common.o pci_iommu.o \ + pci_psycho.o pci_sabre.o endif ifdef CONFIG_SUNOS_EMUL diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index d51e36f61..04a5b6f7f 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.76 2000/01/31 21:10:15 davem Exp $ +/* $Id: ioctl32.c,v 1.79 2000/02/08 20:24:25 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -17,7 +17,9 @@ #include <linux/if.h> #include <linux/malloc.h> #include <linux/hdreg.h> +#if 0 /* New RAID code is half-merged... -DaveM */ #include <linux/md.h> +#endif #include <linux/kd.h> #include <linux/route.h> #include <linux/skbuff.h> @@ -61,6 +63,18 @@ #include <linux/soundcard.h> +#include <linux/atm.h> +#include <linux/atmarp.h> +#include <linux/atmclip.h> +#include <linux/atmdev.h> +#include <linux/atmioc.h> +#include <linux/atmlec.h> +#include <linux/atmmpc.h> +#include <linux/atmsvc.h> +#include <linux/atm_tcp.h> +#include <linux/sonet.h> +#include <linux/atm_suni.h> + /* Use this to get at 32-bit user passed pointers. See sys_sparc32.c for description about these. */ #define A(__x) ((unsigned long)(__x)) @@ -1753,6 +1767,217 @@ static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, unsigned long a return err; } +struct atmif_sioc32 { + int number; + int length; + __kernel_caddr_t32 arg; +}; + +struct atm_iobuf32 { + int length; + __kernel_caddr_t32 buffer; +}; + +#define ATM_GETLINKRATE32 _IOW('a', ATMIOC_ITF+1, struct atmif_sioc32) +#define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct atm_iobuf32) +#define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct atmif_sioc32) +#define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct atmif_sioc32) +#define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct atmif_sioc32) +#define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct atmif_sioc32) +#define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct atmif_sioc32) +#define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct atmif_sioc32) +#define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct atmif_sioc32) +#define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct atmif_sioc32) +#define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct atmif_sioc32) +#define ATM_SETESIF32 _IOW('a', ATMIOC_ITF+13, struct atmif_sioc32) +#define ATM_GETSTAT32 _IOW('a', ATMIOC_SARCOM+0, struct atmif_sioc32) +#define ATM_GETSTATZ32 _IOW('a', ATMIOC_SARCOM+1, struct atmif_sioc32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} atm_ioctl_map[] = { + { ATM_GETLINKRATE32, ATM_GETLINKRATE }, + { ATM_GETNAMES32, ATM_GETNAMES }, + { ATM_GETTYPE32, ATM_GETTYPE }, + { ATM_GETESI32, ATM_GETESI }, + { ATM_GETADDR32, ATM_GETADDR }, + { ATM_RSTADDR32, ATM_RSTADDR }, + { ATM_ADDADDR32, ATM_ADDADDR }, + { ATM_DELADDR32, ATM_DELADDR }, + { ATM_GETCIRANGE32, ATM_GETCIRANGE }, + { ATM_SETCIRANGE32, ATM_SETCIRANGE }, + { ATM_SETESI32, ATM_SETESI }, + { ATM_SETESIF32, ATM_SETESIF }, + { ATM_GETSTAT32, ATM_GETSTAT }, + { ATM_GETSTATZ32, ATM_GETSTATZ } +}; + +#define NR_ATM_IOCTL (sizeof(atm_ioctl_map)/sizeof(atm_ioctl_map[0])) + + +static int do_atm_iobuf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atm_iobuf32 iobuf32; + struct atm_iobuf iobuf = { 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&iobuf32, (struct atm_iobuf32*)arg, + sizeof(struct atm_iobuf32)); + if (err) + return -EFAULT; + + iobuf.length = iobuf32.length; + + if (iobuf32.buffer == (__kernel_caddr_t32) NULL || iobuf32.length == 0) { + iobuf.buffer = (void*)(unsigned long)iobuf32.buffer; + } else { + iobuf.buffer = kmalloc(iobuf.length, GFP_KERNEL); + if (iobuf.buffer == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(iobuf.buffer, A(iobuf32.buffer), iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&iobuf); + set_fs (old_fs); + if(err) + goto out; + + if(iobuf.buffer && iobuf.length > 0) { + err = copy_to_user(A(iobuf32.buffer), iobuf.buffer, iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(iobuf.length, &(((struct atm_iobuf32*)arg)->length)); + + out: + if(iobuf32.buffer && iobuf32.length > 0) + kfree(iobuf.buffer); + + return err; +} + + +static int do_atmif_sioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atmif_sioc32 sioc32; + struct atmif_sioc sioc = { 0, 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&sioc32, (struct atmif_sioc32*)arg, + sizeof(struct atmif_sioc32)); + if (err) + return -EFAULT; + + sioc.number = sioc32.number; + sioc.length = sioc32.length; + + if (sioc32.arg == (__kernel_caddr_t32) NULL || sioc32.length == 0) { + sioc.arg = (void*)(unsigned long)sioc32.arg; + } else { + sioc.arg = kmalloc(sioc.length, GFP_KERNEL); + if (sioc.arg == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(sioc.arg, A(sioc32.arg), sioc32.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&sioc); + set_fs (old_fs); + if(err) { + goto out; + } + + if(sioc.arg && sioc.length > 0) { + err = copy_to_user(A(sioc32.arg), sioc.arg, sioc.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(sioc.length, &(((struct atmif_sioc32*)arg)->length)); + + out: + if(sioc32.arg && sioc32.length > 0) + kfree(sioc.arg); + + return err; +} + + +static int do_atm_ioctl(unsigned int fd, unsigned int cmd32, unsigned long arg) +{ + int i; + unsigned int cmd = 0; + + switch (cmd32) { + case SUNI_GETLOOP: + case SUNI_SETLOOP: + case SONET_GETSTAT: + case SONET_GETSTATZ: + case SONET_GETDIAG: + case SONET_SETDIAG: + case SONET_CLRDIAG: + case SONET_SETFRAMING: + case SONET_GETFRAMING: + case SONET_GETFRSENSE: + return do_atmif_sioc(fd, cmd32, arg); + } + + if (cmd == 0) { + for (i = 0; i < NR_ATM_IOCTL; i++) { + if (cmd32 == atm_ioctl_map[i].cmd32) { + cmd = atm_ioctl_map[i].cmd; + break; + } + } + if (i == NR_ATM_IOCTL) { + return -EINVAL; + } + } + + switch (cmd) { + case ATM_GETNAMES: + return do_atm_iobuf(fd, cmd, arg); + + case ATM_GETLINKRATE: + case ATM_GETTYPE: + case ATM_GETESI: + case ATM_GETADDR: + case ATM_RSTADDR: + case ATM_ADDADDR: + case ATM_DELADDR: + case ATM_GETCIRANGE: + case ATM_SETCIRANGE: + case ATM_SETESI: + case ATM_SETESIF: + case ATM_GETSTAT: + case ATM_GETSTATZ: + return do_atmif_sioc(fd, cmd, arg); + } + + return -EINVAL; +} + asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; @@ -1945,6 +2170,33 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) error = do_smb_getmountuid(fd, cmd, arg); goto out; + case ATM_GETLINKRATE32: + case ATM_GETNAMES32: + case ATM_GETTYPE32: + case ATM_GETESI32: + case ATM_GETADDR32: + case ATM_RSTADDR32: + case ATM_ADDADDR32: + case ATM_DELADDR32: + case ATM_GETCIRANGE32: + case ATM_SETCIRANGE32: + case ATM_SETESI32: + case ATM_SETESIF32: + case ATM_GETSTAT32: + case ATM_GETSTATZ32: + case SUNI_GETLOOP: + case SUNI_SETLOOP: + case SONET_GETSTAT: + case SONET_GETSTATZ: + case SONET_GETDIAG: + case SONET_SETDIAG: + case SONET_CLRDIAG: + case SONET_SETFRAMING: + case SONET_GETFRAMING: + case SONET_GETFRSENSE: + error = do_atm_ioctl(fd, cmd, arg); + goto out; + /* List here exlicitly which ioctl's are known to have * compatable types passed or none at all... */ @@ -2062,12 +2314,16 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) case BLKRRPART: case BLKFLSBUF: case BLKRASET: - + +#if 0 /* New RAID code is being merged, fix up to handle + * new RAID ioctls when fully merged in 2.3.x -DaveM + */ /* 0x09 */ case REGISTER_DEV: case REGISTER_DEV_NEW: case START_MD: case STOP_MD: +#endif /* Big K */ case PIO_FONT: @@ -2454,6 +2710,23 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) /* SMB ioctls which do not need any translations */ case SMB_IOC_NEWCONN: + /* Little a */ + case ATMSIGD_CTRL: + case ATMARPD_CTRL: + case ATMLEC_CTRL: + case ATMLEC_MCAST: + case ATMLEC_DATA: + case ATM_SETSC: + case SIOCSIFATMTCP: + case SIOCMKCLIP: + case ATMARP_MKIP: + case ATMARP_SETENTRY: + case ATMARP_ENCAP: + case ATMTCP_CREATE: + case ATMTCP_REMOVE: + case ATMMPC_CTRL: + case ATMMPC_DATA: + error = sys_ioctl (fd, cmd, arg); goto out; diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 820b93bb4..a09303971 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.81 2000/01/21 06:33:59 davem Exp $ +/* $Id: irq.c,v 1.82 2000/02/09 11:15:07 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -11,6 +11,7 @@ #include <linux/errno.h> #include <linux/kernel_stat.h> #include <linux/signal.h> +#include <linux/mm.h> #include <linux/interrupt.h> #include <linux/malloc.h> #include <linux/random.h> /* XXX ADD add_foo_randomness() calls... -DaveM */ @@ -546,8 +547,6 @@ unsigned int local_bh_count; #define irq_enter(cpu, irq) (local_irq_count++) #define irq_exit(cpu, irq) (local_irq_count--) #else -atomic_t global_bh_lock = ATOMIC_INIT(0); -spinlock_t global_bh_count = SPIN_LOCK_UNLOCKED; /* Who has global_irq_lock. */ unsigned char global_irq_holder = NO_PROC_ID; @@ -573,24 +572,12 @@ static void show(char * str) atomic_read(&global_irq_count), cpu_data[0].irq_count, cpu_data[1].irq_count); printk("bh: %d [%u %u]\n", - (spin_is_locked(&global_bh_count) ? 1 : 0), + (spin_is_locked(&global_bh_lock) ? 1 : 0), cpu_data[0].bh_count, cpu_data[1].bh_count); } #define MAXCOUNT 100000000 -static inline void wait_on_bh(void) -{ - int count = MAXCOUNT; - do { - if(!--count) { - show("wait_on_bh"); - count = 0; - } - membar("#LoadLoad"); - } while(spin_is_locked(&global_bh_count)); -} - #define SYNC_OTHER_ULTRAS(x) udelay(x+1) static inline void wait_on_irq(int cpu) @@ -599,7 +586,7 @@ static inline void wait_on_irq(int cpu) for(;;) { membar("#LoadLoad"); if (!atomic_read (&global_irq_count)) { - if (local_bh_count || ! spin_is_locked(&global_bh_count)) + if (local_bh_count || ! spin_is_locked(&global_bh_lock)) break; } spin_unlock (&global_irq_lock); @@ -616,7 +603,7 @@ static inline void wait_on_irq(int cpu) continue; if (spin_is_locked (&global_irq_lock)) continue; - if (!local_bh_count && spin_is_locked (&global_bh_count)) + if (!local_bh_count && spin_is_locked (&global_bh_lock)) continue; if (spin_trylock(&global_irq_lock)) break; @@ -624,12 +611,6 @@ static inline void wait_on_irq(int cpu) } } -void synchronize_bh(void) -{ - if (spin_is_locked (&global_bh_count) && !in_interrupt()) - wait_on_bh(); -} - void synchronize_irq(void) { if (atomic_read(&global_irq_count)) { diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 01fea4f59..3cf95bb86 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -1,4 +1,4 @@ -/* $Id: pci.c,v 1.14 2000/01/13 00:05:43 davem Exp $ +/* $Id: pci.c,v 1.15 2000/02/08 05:11:29 jj Exp $ * pci.c: UltraSparc PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com) @@ -20,11 +20,6 @@ #include <asm/irq.h> #include <asm/ebus.h> -#ifndef NEW_PCI_DMA_MAP -unsigned long pci_dvma_v2p_hash[PCI_DVMA_HASHSZ]; -unsigned long pci_dvma_p2v_hash[PCI_DVMA_HASHSZ]; -#endif - unsigned long pci_memspace_mask = 0xffffffffUL; #ifndef CONFIG_PCI diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h index 12b6af71e..eeda574f7 100644 --- a/arch/sparc64/kernel/pci_impl.h +++ b/arch/sparc64/kernel/pci_impl.h @@ -1,4 +1,4 @@ -/* $Id: pci_impl.h,v 1.4 1999/12/17 12:32:03 jj Exp $ +/* $Id: pci_impl.h,v 1.5 2000/02/08 05:11:32 jj Exp $ * pci_impl.h: Helper definitions for PCI controller support. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) @@ -34,21 +34,6 @@ extern void pci_scan_for_target_abort(struct pci_controller_info *, struct pci_p extern void pci_scan_for_master_abort(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *); extern void pci_scan_for_parity_error(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *); -#ifndef NEW_PCI_DMA_MAP -/* IOMMU/DVMA initialization. */ -#define PCI_DVMA_HASH_NONE ~0UL -static __inline__ void set_dvma_hash(unsigned long dvma_offset, - unsigned long paddr, - unsigned long daddr) -{ - unsigned long dvma_addr = dvma_offset + daddr; - unsigned long vaddr = (unsigned long)__va(paddr); - - pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr)] = dvma_addr - vaddr; - pci_dvma_p2v_hash[pci_dvma_ahashfn(dvma_addr)] = vaddr - dvma_addr; -} -#endif - /* Configuration space access. */ extern spinlock_t pci_poke_lock; extern volatile int pci_poke_in_progress; diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index 1ff768056..c9bd41c19 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -1,4 +1,4 @@ -/* $Id: pci_psycho.c,v 1.10 2000/01/28 13:42:00 jj Exp $ +/* $Id: pci_psycho.c,v 1.11 2000/02/08 05:11:32 jj Exp $ * pci_psycho.c: PSYCHO/U2P specific PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) @@ -1219,12 +1219,6 @@ static void __init psycho_scan_bus(struct pci_controller_info *p) static void __init psycho_iommu_init(struct pci_controller_info *p) { -#ifndef NEW_PCI_DMA_MAP - struct linux_mlist_p1275 *mlist; - unsigned long n; - iopte_t *iopte; - int tsbsize = 32; -#endif extern int this_is_starfire; extern void *starfire_hookup(int); unsigned long tsbbase, i; @@ -1258,19 +1252,11 @@ static void __init psycho_iommu_init(struct pci_controller_info *p) control &= ~(PSYCHO_IOMMU_CTRL_DENAB); psycho_write(p->controller_regs + PSYCHO_IOMMU_CONTROL, control); -#ifndef NEW_PCI_DMA_MAP - /* Using assumed page size 64K with 32K entries we need 256KB iommu page - * table (32K ioptes * 8 bytes per iopte). This is - * page order 5 on UltraSparc. - */ - tsbbase = __get_free_pages(GFP_KERNEL, 5); -#else /* Using assumed page size 8K with 128K entries we need 1MB iommu page * table (128K ioptes * 8 bytes per iopte). This is * page order 7 on UltraSparc. */ tsbbase = __get_free_pages(GFP_KERNEL, 7); -#endif if (!tsbbase) { prom_printf("PSYCHO_IOMMU: Error, gfp(tsb) failed.\n"); prom_halt(); @@ -1278,112 +1264,17 @@ static void __init psycho_iommu_init(struct pci_controller_info *p) p->iommu.page_table = (iopte_t *)tsbbase; p->iommu.page_table_sz_bits = 17; p->iommu.page_table_map_base = 0xc0000000; -#ifndef NEW_PCI_DMA_MAP - memset((char *)tsbbase, 0, PAGE_SIZE << 5); -#else memset((char *)tsbbase, 0, PAGE_SIZE << 7); -#endif /* Make sure DMA address 0 is never returned just to allow catching of buggy drivers. */ p->iommu.lowest_free[0] = 1; -#ifndef NEW_PCI_DMA_MAP - iopte = (iopte_t *)tsbbase; - /* Initialize to "none" settings. */ - for(i = 0; i < PCI_DVMA_HASHSZ; i++) { - pci_dvma_v2p_hash[i] = PCI_DVMA_HASH_NONE; - pci_dvma_p2v_hash[i] = PCI_DVMA_HASH_NONE; - } - - n = 0; - mlist = *prom_meminfo()->p1275_totphys; - while (mlist) { - unsigned long paddr = mlist->start_adr; - unsigned long num_bytes = mlist->num_bytes; - - if(paddr >= (((unsigned long) high_memory) - PAGE_OFFSET)) - goto next; - - if((paddr + num_bytes) >= (((unsigned long) high_memory) - PAGE_OFFSET)) - num_bytes = (((unsigned long) high_memory) - PAGE_OFFSET) - paddr; - - /* Align base and length so we map whole hash table sized chunks - * at a time (and therefore full 64K IOMMU pages). - */ - paddr &= ~((1UL << 24UL) - 1); - num_bytes = (num_bytes + ((1UL << 24UL) - 1)) & ~((1UL << 24) - 1); - - /* Move up the base for mappings already created. */ - while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr)] != - PCI_DVMA_HASH_NONE) { - paddr += (1UL << 24UL); - num_bytes -= (1UL << 24UL); - if(num_bytes == 0UL) - goto next; - } - - /* Move down the size for tail mappings already created. */ - while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr + num_bytes - (1UL << 24UL))] != - PCI_DVMA_HASH_NONE) { - num_bytes -= (1UL << 24UL); - if(num_bytes == 0UL) - goto next; - } - - /* Now map the rest. */ - for (i = 0; i < ((num_bytes + ((1 << 16) - 1)) >> 16); i++) { - iopte_val(*iopte) = ((IOPTE_VALID | IOPTE_64K | - IOPTE_CACHE | IOPTE_WRITE) | - (paddr & IOPTE_PAGE)); - - if (!(n & 0xff)) - set_dvma_hash(0x80000000, paddr, (n << 16)); - - if (++n > (tsbsize * 1024)) - goto out; - - paddr += (1 << 16); - iopte++; - } - next: - mlist = mlist->theres_more; - } -out: - if (mlist) { - prom_printf("WARNING: not all physical memory mapped in IOMMU\n"); - prom_printf("Try booting with mem=xxxM or similar\n"); - prom_halt(); - } -#endif psycho_write(p->controller_regs + PSYCHO_IOMMU_TSBBASE, __pa(tsbbase)); control = psycho_read(p->controller_regs + PSYCHO_IOMMU_CONTROL); -#ifndef NEW_PCI_DMA_MAP - control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ); - control |= (PSYCHO_IOMMU_CTRL_TBWSZ | PSYCHO_IOMMU_CTRL_ENAB); - switch(tsbsize) { - case 8: - p->iommu.page_table_map_base = 0xe0000000; - control |= PSYCHO_IOMMU_TSBSZ_8K; - break; - case 16: - p->iommu.page_table_map_base = 0xc0000000; - control |= PSYCHO_IOMMU_TSBSZ_16K; - break; - case 32: - p->iommu.page_table_map_base = 0x80000000; - control |= PSYCHO_IOMMU_TSBSZ_32K; - break; - default: - prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize); - prom_halt(); - break; - } -#else control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ); control |= (PSYCHO_IOMMU_TSBSZ_128K | PSYCHO_IOMMU_CTRL_ENAB); -#endif psycho_write(p->controller_regs + PSYCHO_IOMMU_CONTROL, control); /* If necessary, hook us up for starfire IRQ translations. */ diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index 44247c371..bbf10cac1 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -1,4 +1,4 @@ -/* $Id: pci_sabre.c,v 1.11 2000/01/28 13:42:01 jj Exp $ +/* $Id: pci_sabre.c,v 1.12 2000/02/08 05:11:33 jj Exp $ * pci_sabre.c: Sabre specific PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) @@ -1107,11 +1107,6 @@ static void __init sabre_scan_bus(struct pci_controller_info *p) static void __init sabre_iommu_init(struct pci_controller_info *p, int tsbsize, unsigned long dvma_offset) { -#ifndef NEW_PCI_DMA_MAP - struct linux_mlist_p1275 *mlist; - unsigned long n; - iopte_t *iopte; -#endif unsigned long tsbbase, i, order; u64 control; @@ -1154,99 +1149,9 @@ static void __init sabre_iommu_init(struct pci_controller_info *p, of buggy drivers. */ p->iommu.lowest_free[0] = 1; -#ifndef NEW_PCI_DMA_MAP - iopte = (iopte_t *)tsbbase; - - /* Initialize to "none" settings. */ - for(i = 0; i < PCI_DVMA_HASHSZ; i++) { - pci_dvma_v2p_hash[i] = PCI_DVMA_HASH_NONE; - pci_dvma_p2v_hash[i] = PCI_DVMA_HASH_NONE; - } - - n = 0; - mlist = *prom_meminfo()->p1275_totphys; - while (mlist) { - unsigned long paddr = mlist->start_adr; - unsigned long num_bytes = mlist->num_bytes; - - if(paddr >= (((unsigned long) high_memory) - PAGE_OFFSET)) - goto next; - - if((paddr + num_bytes) >= (((unsigned long) high_memory) - PAGE_OFFSET)) - num_bytes = - (((unsigned long) high_memory) - - PAGE_OFFSET) - paddr; - - /* Align base and length so we map whole hash table sized chunks - * at a time (and therefore full 64K IOMMU pages). - */ - paddr &= ~((1UL << 24UL) - 1); - num_bytes = (num_bytes + ((1UL << 24UL) - 1)) & ~((1UL << 24) - 1); - - /* Move up the base for mappings already created. */ - while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr)] != - PCI_DVMA_HASH_NONE) { - paddr += (1UL << 24UL); - num_bytes -= (1UL << 24UL); - if(num_bytes == 0UL) - goto next; - } - - /* Move down the size for tail mappings already created. */ - while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr + num_bytes - (1UL << 24UL))] != - PCI_DVMA_HASH_NONE) { - num_bytes -= (1UL << 24UL); - if(num_bytes == 0UL) - goto next; - } - - /* Now map the rest. */ - for (i = 0; i < ((num_bytes + ((1 << 16) - 1)) >> 16); i++) { - iopte_val(*iopte) = ((IOPTE_VALID | IOPTE_64K | - IOPTE_CACHE | IOPTE_WRITE) | - (paddr & IOPTE_PAGE)); - - if (!(n & 0xff)) - set_dvma_hash(dvma_offset, paddr, (n << 16)); - if (++n > (tsbsize * 1024)) - goto out; - - paddr += (1 << 16); - iopte++; - } - next: - mlist = mlist->theres_more; - } -out: - if (mlist) { - prom_printf("WARNING: not all physical memory mapped in IOMMU\n"); - prom_printf("Try booting with mem=xxxM or similar\n"); - prom_halt(); - } -#endif - sabre_write(p->controller_regs + SABRE_IOMMU_TSBBASE, __pa(tsbbase)); control = sabre_read(p->controller_regs + SABRE_IOMMU_CONTROL); -#ifndef NEW_PCI_DMA_MAP - control &= ~(SABRE_IOMMUCTRL_TSBSZ); - control |= (SABRE_IOMMUCTRL_TBWSZ | SABRE_IOMMUCTRL_ENAB); - switch(tsbsize) { - case 8: - control |= SABRE_IOMMU_TSBSZ_8K; - break; - case 16: - control |= SABRE_IOMMU_TSBSZ_16K; - break; - case 32: - control |= SABRE_IOMMU_TSBSZ_32K; - break; - default: - prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize); - prom_halt(); - break; - } -#else control &= ~(SABRE_IOMMUCTRL_TSBSZ | SABRE_IOMMUCTRL_TBWSZ); control |= SABRE_IOMMUCTRL_ENAB; switch(tsbsize) { @@ -1263,7 +1168,6 @@ out: prom_halt(); break; } -#endif sabre_write(p->controller_regs + SABRE_IOMMU_CONTROL, control); } @@ -1472,17 +1376,6 @@ void __init sabre_init(int pnode) } switch(vdma[1]) { -#ifndef NEW_PCI_DMA_MAP - case 0x20000000: - tsbsize = 8; - break; - case 0x40000000: - tsbsize = 16; - break; - case 0x80000000: - tsbsize = 32; - break; -#else case 0x20000000: tsbsize = 64; break; @@ -1490,7 +1383,6 @@ void __init sabre_init(int pnode) case 0x80000000: tsbsize = 128; break; -#endif default: prom_printf("SABRE: strange virtual-dma size.\n"); prom_halt(); diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index bb6d7398e..d059a5a28 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.47 1999/07/30 09:35:23 davem Exp $ +/* $Id: rtrap.S,v 1.48 2000/02/09 11:15:07 davem Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -20,15 +20,17 @@ .globl rtrap_clr_l6, rtrap rtrap_clr_l6: clr %l6 /* Fall through */ -rtrap: sethi %hi(bh_active), %l2 - sethi %hi(bh_mask), %l1 - ldx [%l2 + %lo(bh_active)], %l4 - ldx [%l1 + %lo(bh_mask)], %l7 - - andcc %l4, %l7, %g0 - be,pt %xcc, 2f +rtrap: lduw [%g6 + AOFF_task_processor], %l0 + sethi %hi(softirq_state), %l2 + or %l2, %lo(softirq_state), %l2 + sllx %l0, 6, %l0 + ldx [%l2 + %l0], %l1 + srlx %l1, 32, %l2 + + andcc %l1, %l2, %g0 + be,pt %icc, 2f nop - call do_bottom_half + call do_softirq nop 2: ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 sethi %hi(0xf << 20), %l4 diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index c48a1f6e4..ff3843651 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc64_ksyms.c,v 1.72 2000/01/28 13:41:59 jj Exp $ +/* $Id: sparc64_ksyms.c,v 1.74 2000/02/09 11:15:07 davem Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -127,11 +127,6 @@ EXPORT_SYMBOL_PRIVATE(write_unlock); /* Kernel wide locking */ EXPORT_SYMBOL(kernel_flag); -/* Software-IRQ BH locking */ -EXPORT_SYMBOL(global_bh_lock); -EXPORT_SYMBOL(global_bh_count); -EXPORT_SYMBOL(synchronize_bh); - /* Hard IRQ locking */ EXPORT_SYMBOL(global_irq_holder); EXPORT_SYMBOL(global_irq_lock); @@ -160,8 +155,8 @@ EXPORT_SYMBOL(_do_write_unlock); #endif #else -EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(local_bh_count); +EXPORT_SYMBOL(local_irq_count); #endif /* rw semaphores */ @@ -194,12 +189,8 @@ EXPORT_SYMBOL(sbus_unmap_sg); EXPORT_SYMBOL(sbus_dma_sync_single); EXPORT_SYMBOL(sbus_dma_sync_sg); #endif -#if CONFIG_PCI +#ifdef CONFIG_PCI EXPORT_SYMBOL(ebus_chain); -#ifndef NEW_PCI_DMA_MAP -EXPORT_SYMBOL(pci_dvma_v2p_hash); -EXPORT_SYMBOL(pci_dvma_p2v_hash); -#endif EXPORT_SYMBOL(pci_memspace_mask); EXPORT_SYMBOL(empty_zero_page); EXPORT_SYMBOL(outsb); @@ -208,8 +199,6 @@ EXPORT_SYMBOL(outsl); EXPORT_SYMBOL(insb); EXPORT_SYMBOL(insw); EXPORT_SYMBOL(insl); -#endif -#ifdef NEW_PCI_DMA_MAP EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); EXPORT_SYMBOL(pci_map_single); diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index b4306d9ab..58dc224f9 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.144 2000/01/23 07:16:11 davem Exp $ +/* $Id: init.c,v 1.146 2000/02/09 21:11:09 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) @@ -30,7 +30,6 @@ #include <asm/vaddrs.h> #include <asm/dma.h> -extern void show_net_buffers(void); extern void device_scan(void); struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; @@ -130,9 +129,6 @@ void show_mem(void) printk("%d entries in page dir cache\n",pgd_cache_size); #endif show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif } int mmu_info(char *buf) @@ -926,7 +922,7 @@ void __init paging_init(void) flush_tlb_all(); { - unsigned int zones_size[MAX_NR_ZONES] = { 0, 0, 0}; + unsigned long zones_size[MAX_NR_ZONES] = { 0, 0, 0}; zones_size[ZONE_DMA] = end_pfn; free_area_init(zones_size); diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c index 5eef5c208..b5f76d400 100644 --- a/arch/sparc64/solaris/socksys.c +++ b/arch/sparc64/solaris/socksys.c @@ -1,4 +1,4 @@ -/* $Id: socksys.c,v 1.10 1999/08/31 06:55:08 davem Exp $ +/* $Id: socksys.c,v 1.11 2000/02/09 22:32:17 davem Exp $ * socksys.c: /dev/inet/ stuff for Solaris emulation. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -48,16 +48,7 @@ extern void mykfree(void *); static unsigned int (*sock_poll)(struct file *, poll_table *); static struct file_operations socksys_file_ops = { - NULL, /* lseek */ - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - NULL, /* release */ + /* Currently empty */ }; static int socksys_open(struct inode * inode, struct file * filp) @@ -162,16 +153,8 @@ static unsigned int socksys_poll(struct file * filp, poll_table * wait) } static struct file_operations socksys_fops = { - NULL, /* lseek */ - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - NULL, /* mmap */ - socksys_open, /* open */ - NULL, /* flush */ - socksys_release,/* release */ + open: socksys_open, + release: socksys_release, }; int __init diff --git a/arch/sparc64/vmlinux.lds b/arch/sparc64/vmlinux.lds index e6f6cf82a..c0b5c31d0 100644 --- a/arch/sparc64/vmlinux.lds +++ b/arch/sparc64/vmlinux.lds @@ -74,4 +74,5 @@ SECTIONS .debug_pubnames 0 : { *(.debug_pubnames) } .debug_sfnames 0 : { *(.debug_sfnames) } .line 0 : { *(.line) } + /DISCARD/ : { *(.text.exit) *(.data.exit) } } |