vserver 1.9.3
[linux-2.6.git] / arch / ppc64 / kernel / pSeries_lpar.c
index a44c332..68eeb7a 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
+#define DEBUG
+
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/dma-mapping.h>
 #include <asm/naca.h>
 #include <asm/tlbflush.h>
 #include <asm/tlb.h>
-#include <asm/hvcall.h>
 #include <asm/prom.h>
 #include <asm/abs_addr.h>
 #include <asm/cputable.h>
+#include <asm/plpar_wrappers.h>
+
+#ifdef DEBUG
+#define DBG(fmt...) udbg_printf(fmt)
+#else
+#define DBG(fmt...)
+#endif
 
 /* in pSeries_hvCall.S */
 EXPORT_SYMBOL(plpar_hcall);
@@ -45,146 +53,9 @@ EXPORT_SYMBOL(plpar_hcall_4out);
 EXPORT_SYMBOL(plpar_hcall_norets);
 EXPORT_SYMBOL(plpar_hcall_8arg_2ret);
 
-long poll_pending(void)
-{
-       unsigned long dummy;
-       return plpar_hcall(H_POLL_PENDING, 0, 0, 0, 0,
-                          &dummy, &dummy, &dummy);
-}
-
-long prod_processor(void)
-{
-       plpar_hcall_norets(H_PROD);
-       return(0); 
-}
-
-long cede_processor(void)
-{
-       plpar_hcall_norets(H_CEDE);
-       return(0); 
-}
-
-long register_vpa(unsigned long flags, unsigned long proc, unsigned long vpa)
-{
-       plpar_hcall_norets(H_REGISTER_VPA, flags, proc, vpa);
-       return(0); 
-}
-
-long plpar_pte_remove(unsigned long flags,
-                     unsigned long ptex,
-                     unsigned long avpn,
-                     unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
-{
-       unsigned long dummy;
-       return plpar_hcall(H_REMOVE, flags, ptex, avpn, 0,
-                          old_pteh_ret, old_ptel_ret, &dummy);
-}
-
-long plpar_pte_read(unsigned long flags,
-                   unsigned long ptex,
-                   unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
-{
-       unsigned long dummy;
-       return plpar_hcall(H_READ, flags, ptex, 0, 0,
-                          old_pteh_ret, old_ptel_ret, &dummy);
-}
-
-long plpar_pte_protect(unsigned long flags,
-                      unsigned long ptex,
-                      unsigned long avpn)
-{
-       return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn);
-}
-
-long plpar_tce_get(unsigned long liobn,
-                  unsigned long ioba,
-                  unsigned long *tce_ret)
-{
-       unsigned long dummy;
-       return plpar_hcall(H_GET_TCE, liobn, ioba, 0, 0,
-                          tce_ret, &dummy, &dummy);
-}
-
-long plpar_tce_put(unsigned long liobn,
-                  unsigned long ioba,
-                  unsigned long tceval)
-{
-       return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval);
-}
+extern void fw_feature_init(void);
+extern void pSeries_find_serial_port(void);
 
-long plpar_get_term_char(unsigned long termno,
-                        unsigned long *len_ret,
-                        char *buf_ret)
-{
-       unsigned long *lbuf = (unsigned long *)buf_ret;  /* ToDo: alignment? */
-       return plpar_hcall(H_GET_TERM_CHAR, termno, 0, 0, 0,
-                          len_ret, lbuf+0, lbuf+1);
-}
-
-long plpar_put_term_char(unsigned long termno,
-                        unsigned long len,
-                        const char *buffer)
-{
-       unsigned long *lbuf = (unsigned long *)buffer;  /* ToDo: alignment? */
-       return plpar_hcall_norets(H_PUT_TERM_CHAR, termno, len, lbuf[0],
-                                 lbuf[1]);
-}
-
-static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
-               long npages, unsigned long uaddr,
-               enum dma_data_direction direction)
-{
-       u64 rc;
-       union tce_entry tce;
-
-       tce.te_word = 0;
-       tce.te_rpn = (virt_to_abs(uaddr)) >> PAGE_SHIFT;
-       tce.te_rdwr = 1;
-       if (direction != DMA_TO_DEVICE)
-               tce.te_pciwr = 1;
-
-       while (npages--) {
-               rc = plpar_tce_put((u64)tbl->it_index, 
-                                  (u64)tcenum << 12, 
-                                  tce.te_word );
-               
-               if (rc && printk_ratelimit()) {
-                       printk("tce_build_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc);
-                       printk("\tindex   = 0x%lx\n", (u64)tbl->it_index);
-                       printk("\ttcenum  = 0x%lx\n", (u64)tcenum);
-                       printk("\ttce val = 0x%lx\n", tce.te_word );
-                       show_stack(current, (unsigned long *)__get_SP());
-               }
-                       
-               tcenum++;
-               tce.te_rpn++;
-       }
-}
-
-static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages)
-{
-       u64 rc;
-       union tce_entry tce;
-
-       tce.te_word = 0;
-
-       while (npages--) {
-               rc = plpar_tce_put((u64)tbl->it_index, 
-                                  (u64)tcenum << 12,
-                                  tce.te_word );
-               
-               if (rc && printk_ratelimit()) {
-                       printk("tce_free_pSeriesLP: plpar_tce_put failed\n");
-                       printk("\trc      = %ld\n", rc);
-                       printk("\tindex   = 0x%lx\n", (u64)tbl->it_index);
-                       printk("\ttcenum  = 0x%lx\n", (u64)tcenum);
-                       printk("\ttce val = 0x%lx\n", tce.te_word );
-                       show_stack(current, (unsigned long *)__get_SP());
-               }
-               
-               tcenum++;
-       }
-}
 
 int vtermno;   /* virtual terminal# for udbg  */
 
@@ -249,8 +120,19 @@ static unsigned char udbg_getcLP(void)
        }
 }
 
+/* call this from early_init() for a working debug console on
+ * vterm capable LPAR machines
+ */
+void udbg_init_debug_lpar(void)
+{
+       vtermno = 0;
+       ppc_md.udbg_putc = udbg_putcLP;
+       ppc_md.udbg_getc = udbg_getcLP;
+       ppc_md.udbg_getc_poll = udbg_getc_pollLP;
+}
+
 /* returns 0 if couldn't find or use /chosen/stdout as console */
-static int find_udbg_vterm(void)
+int find_udbg_vterm(void)
 {
        struct device_node *stdout_node;
        u32 *termno;
@@ -258,15 +140,14 @@ static int find_udbg_vterm(void)
        int found = 0;
 
        /* find the boot console from /chosen/stdout */
-       if (!of_stdout_device) {
-               printk(KERN_WARNING "couldn't get path from /chosen/stdout!\n");
-               return found;
-       }
-       stdout_node = of_find_node_by_path(of_stdout_device);
-       if (!stdout_node) {
-               printk(KERN_WARNING "couldn't find node from /chosen/stdout\n");
-               return found;
-       }
+       if (!of_chosen)
+               return 0;
+       name = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
+       if (name == NULL)
+               return 0;
+       stdout_node = of_find_node_by_path(name);
+       if (!stdout_node)
+               return 0;
 
        /* now we have the stdout node; figure out what type of device it is. */
        name = (char *)get_property(stdout_node, "name", NULL);
@@ -288,15 +169,17 @@ static int find_udbg_vterm(void)
                } else {
                        /* XXX implement udbg_putcLP_vtty for hvterm-protocol1 case */
                        printk(KERN_WARNING "%s doesn't speak hvterm1; "
-                                       "can't print udbg messages\n", of_stdout_device);
+                                       "can't print udbg messages\n",
+                              stdout_node->full_name);
                }
        } else if (strncmp(name, "serial", 6)) {
                /* XXX fix ISA serial console */
                printk(KERN_WARNING "serial stdout on LPAR ('%s')! "
-                               "can't print udbg messages\n", of_stdout_device);
+                               "can't print udbg messages\n",
+                      stdout_node->full_name);
        } else {
                printk(KERN_WARNING "don't know how to print to stdout '%s'\n",
-                               of_stdout_device);
+                      stdout_node->full_name);
        }
 
 out:
@@ -304,35 +187,6 @@ out:
        return found;
 }
 
-void pSeries_lpar_mm_init(void);
-
-/* This is called early in setup.c.
- * Use it to setup page table ppc_md stuff as well as udbg.
- */
-void pSeriesLP_init_early(void)
-{
-       pSeries_lpar_mm_init();
-
-       tce_init_pSeries();
-
-       ppc_md.tce_build = tce_build_pSeriesLP;
-       ppc_md.tce_free  = tce_free_pSeriesLP;
-
-       pci_iommu_init();
-
-#ifdef CONFIG_SMP
-       smp_init_pSeries();
-#endif
-
-       /* The keyboard is not useful in the LPAR environment.
-        * Leave all the ppc_md keyboard interfaces NULL.
-        */
-
-       if (0 == find_udbg_vterm()) {
-               printk(KERN_WARNING
-                       "can't use stdout; can't print early debug messages.\n");
-       }
-}
 
 long pSeries_lpar_hpte_insert(unsigned long hpte_group,
                              unsigned long va, unsigned long prpn,
@@ -377,7 +231,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group,
        lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, lhpte.dw0.dword0,
                              lhpte.dw1.dword1, &slot, &dummy0, &dummy1);
 
-       if (lpar_rc == H_PTEG_Full)
+       if (unlikely(lpar_rc == H_PTEG_Full))
                return -1;
 
        /*
@@ -385,7 +239,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group,
         * will fail. However we must catch the failure in hash_page
         * or we will loop forever, so return -2 in this case.
         */
-       if (lpar_rc != H_Success)
+       if (unlikely(lpar_rc != H_Success))
                return -2;
 
        /* Because of iSeries, we have to pass down the secondary
@@ -415,9 +269,7 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group)
                if (lpar_rc == H_Success)
                        return i;
 
-               if (lpar_rc != H_Not_Found)
-                       panic("Bad return code from pte remove rc = %lx\n",
-                             lpar_rc);
+               BUG_ON(lpar_rc != H_Not_Found);
 
                slot_offset++;
                slot_offset &= 0x7;
@@ -426,6 +278,18 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group)
        return -1;
 }
 
+static void pSeries_lpar_hptab_clear(void)
+{
+       unsigned long size_bytes = 1UL << naca->pftSize;
+       unsigned long hpte_count = size_bytes >> 4;
+       unsigned long dummy1, dummy2;
+       int i;
+
+       /* TODO: Use bulk call */
+       for (i = 0; i < hpte_count; i++)
+               plpar_pte_remove(0, i, 0, &dummy1, &dummy2);
+}
+
 /*
  * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and
  * the low 3 bits of flags happen to line up.  So no transform is needed.
@@ -447,8 +311,7 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot, unsigned long newpp,
        if (lpar_rc == H_Not_Found)
                return -1;
 
-       if (lpar_rc != H_Success)
-               panic("bad return code from pte protect rc = %lx\n", lpar_rc);
+       BUG_ON(lpar_rc != H_Success);
 
        return 0;
 }
@@ -464,11 +327,10 @@ static unsigned long pSeries_lpar_hpte_getword0(unsigned long slot)
        /* Do not need RPN to logical page translation */
        /* No cross CEC PFT access                     */
        flags = 0;
-       
+
        lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1);
 
-       if (lpar_rc != H_Success)
-               panic("Error on pte read in get_hpte0 rc = %lx\n", lpar_rc);
+       BUG_ON(lpar_rc != H_Success);
 
        return dword0;
 }
@@ -519,15 +381,12 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp,
        vpn = va >> PAGE_SHIFT;
 
        slot = pSeries_lpar_hpte_find(vpn);
-       if (slot == -1)
-               panic("updateboltedpp: Could not find page to bolt\n");
+       BUG_ON(slot == -1);
 
        flags = newpp & 3;
        lpar_rc = plpar_pte_protect(flags, slot, 0);
 
-       if (lpar_rc != H_Success)
-               panic("Bad return code from pte bolted protect rc = %lx\n",
-                     lpar_rc); 
+       BUG_ON(lpar_rc != H_Success);
 }
 
 static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
@@ -546,8 +405,7 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
        if (lpar_rc == H_Not_Found)
                return;
 
-       if (lpar_rc != H_Success)
-               panic("Bad return code from invalidate rc = %lx\n", lpar_rc);
+       BUG_ON(lpar_rc != H_Success);
 }
 
 /*
@@ -560,18 +418,19 @@ void pSeries_lpar_flush_hash_range(unsigned long context, unsigned long number,
        int i;
        unsigned long flags;
        struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
+       int lock_tlbie = !(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE);
 
-       if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
+       if (lock_tlbie)
                spin_lock_irqsave(&pSeries_lpar_tlbie_lock, flags);
 
        for (i = 0; i < number; i++)
                flush_hash_page(context, batch->addr[i], batch->pte[i], local);
 
-       if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
+       if (lock_tlbie)
                spin_unlock_irqrestore(&pSeries_lpar_tlbie_lock, flags);
 }
 
-void pSeries_lpar_mm_init(void)
+void hpte_init_lpar(void)
 {
        ppc_md.hpte_invalidate  = pSeries_lpar_hpte_invalidate;
        ppc_md.hpte_updatepp    = pSeries_lpar_hpte_updatepp;
@@ -579,4 +438,7 @@ void pSeries_lpar_mm_init(void)
        ppc_md.hpte_insert      = pSeries_lpar_hpte_insert;
        ppc_md.hpte_remove      = pSeries_lpar_hpte_remove;
        ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range;
+       ppc_md.hpte_clear_all   = pSeries_lpar_hptab_clear;
+
+       htab_finish_init();
 }