xref: /linux/arch/powerpc/platforms/ps3/htab.c (revision 0be3ff0c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  PS3 pagetable management routines.
4  *
5  *  Copyright (C) 2006 Sony Computer Entertainment Inc.
6  *  Copyright 2006, 2007 Sony Corporation
7  */
8 
9 #include <linux/kernel.h>
10 #include <linux/memblock.h>
11 
12 #include <asm/machdep.h>
13 #include <asm/prom.h>
14 #include <asm/udbg.h>
15 #include <asm/lv1call.h>
16 #include <asm/ps3fb.h>
17 
18 #define PS3_VERBOSE_RESULT
19 #include "platform.h"
20 
21 /**
22  * enum lpar_vas_id - id of LPAR virtual address space.
23  * @lpar_vas_id_current: Current selected virtual address space
24  *
25  * Identify the target LPAR address space.
26  */
27 
28 enum ps3_lpar_vas_id {
29 	PS3_LPAR_VAS_ID_CURRENT = 0,
30 };
31 
32 
33 static DEFINE_SPINLOCK(ps3_htab_lock);
34 
35 static long ps3_hpte_insert(unsigned long hpte_group, unsigned long vpn,
36 	unsigned long pa, unsigned long rflags, unsigned long vflags,
37 	int psize, int apsize, int ssize)
38 {
39 	int result;
40 	u64 hpte_v, hpte_r;
41 	u64 inserted_index;
42 	u64 evicted_v, evicted_r;
43 	u64 hpte_v_array[4], hpte_rs;
44 	unsigned long flags;
45 	long ret = -1;
46 
47 	/*
48 	 * lv1_insert_htab_entry() will search for victim
49 	 * entry in both primary and secondary pte group
50 	 */
51 	vflags &= ~HPTE_V_SECONDARY;
52 
53 	hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
54 	hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize, apsize) | rflags;
55 
56 	spin_lock_irqsave(&ps3_htab_lock, flags);
57 
58 	/* talk hvc to replace entries BOLTED == 0 */
59 	result = lv1_insert_htab_entry(PS3_LPAR_VAS_ID_CURRENT, hpte_group,
60 				       hpte_v, hpte_r,
61 				       HPTE_V_BOLTED, 0,
62 				       &inserted_index,
63 				       &evicted_v, &evicted_r);
64 
65 	if (result) {
66 		/* all entries bolted !*/
67 		pr_info("%s:result=%s vpn=%lx pa=%lx ix=%lx v=%llx r=%llx\n",
68 			__func__, ps3_result(result), vpn, pa, hpte_group,
69 			hpte_v, hpte_r);
70 		BUG();
71 	}
72 
73 	/*
74 	 * see if the entry is inserted into secondary pteg
75 	 */
76 	result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT,
77 				       inserted_index & ~0x3UL,
78 				       &hpte_v_array[0], &hpte_v_array[1],
79 				       &hpte_v_array[2], &hpte_v_array[3],
80 				       &hpte_rs);
81 	BUG_ON(result);
82 
83 	if (hpte_v_array[inserted_index % 4] & HPTE_V_SECONDARY)
84 		ret = (inserted_index & 7) | (1 << 3);
85 	else
86 		ret = inserted_index & 7;
87 
88 	spin_unlock_irqrestore(&ps3_htab_lock, flags);
89 
90 	return ret;
91 }
92 
93 static long ps3_hpte_remove(unsigned long hpte_group)
94 {
95 	panic("ps3_hpte_remove() not implemented");
96 	return 0;
97 }
98 
99 static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp,
100 			      unsigned long vpn, int psize, int apsize,
101 			      int ssize, unsigned long inv_flags)
102 {
103 	int result;
104 	u64 hpte_v, want_v, hpte_rs;
105 	u64 hpte_v_array[4];
106 	unsigned long flags;
107 	long ret;
108 
109 	want_v = hpte_encode_avpn(vpn, psize, ssize);
110 
111 	spin_lock_irqsave(&ps3_htab_lock, flags);
112 
113 	result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, slot & ~0x3UL,
114 				       &hpte_v_array[0], &hpte_v_array[1],
115 				       &hpte_v_array[2], &hpte_v_array[3],
116 				       &hpte_rs);
117 
118 	if (result) {
119 		pr_info("%s: result=%s read vpn=%lx slot=%lx psize=%d\n",
120 			__func__, ps3_result(result), vpn, slot, psize);
121 		BUG();
122 	}
123 
124 	hpte_v = hpte_v_array[slot % 4];
125 
126 	/*
127 	 * As lv1_read_htab_entries() does not give us the RPN, we can
128 	 * not synthesize the new hpte_r value here, and therefore can
129 	 * not update the hpte with lv1_insert_htab_entry(), so we
130 	 * instead invalidate it and ask the caller to update it via
131 	 * ps3_hpte_insert() by returning a -1 value.
132 	 */
133 	if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
134 		/* not found */
135 		ret = -1;
136 	} else {
137 		/* entry found, just invalidate it */
138 		result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT,
139 					      slot, 0, 0);
140 		ret = -1;
141 	}
142 
143 	spin_unlock_irqrestore(&ps3_htab_lock, flags);
144 	return ret;
145 }
146 
147 static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
148 	int psize, int ssize)
149 {
150 	panic("ps3_hpte_updateboltedpp() not implemented");
151 }
152 
153 static void ps3_hpte_invalidate(unsigned long slot, unsigned long vpn,
154 				int psize, int apsize, int ssize, int local)
155 {
156 	unsigned long flags;
157 	int result;
158 
159 	spin_lock_irqsave(&ps3_htab_lock, flags);
160 
161 	result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, slot, 0, 0);
162 
163 	if (result) {
164 		pr_info("%s: result=%s vpn=%lx slot=%lx psize=%d\n",
165 			__func__, ps3_result(result), vpn, slot, psize);
166 		BUG();
167 	}
168 
169 	spin_unlock_irqrestore(&ps3_htab_lock, flags);
170 }
171 
172 /* Called during kexec sequence with MMU off */
173 static notrace void ps3_hpte_clear(void)
174 {
175 	unsigned long hpte_count = (1UL << ppc64_pft_size) >> 4;
176 	u64 i;
177 
178 	for (i = 0; i < hpte_count; i++)
179 		lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, i, 0, 0);
180 
181 	ps3_mm_shutdown();
182 	ps3_mm_vas_destroy();
183 }
184 
185 void __init ps3_hpte_init(unsigned long htab_size)
186 {
187 	mmu_hash_ops.hpte_invalidate = ps3_hpte_invalidate;
188 	mmu_hash_ops.hpte_updatepp = ps3_hpte_updatepp;
189 	mmu_hash_ops.hpte_updateboltedpp = ps3_hpte_updateboltedpp;
190 	mmu_hash_ops.hpte_insert = ps3_hpte_insert;
191 	mmu_hash_ops.hpte_remove = ps3_hpte_remove;
192 	mmu_hash_ops.hpte_clear_all = ps3_hpte_clear;
193 
194 	ppc64_pft_size = __ilog2(htab_size);
195 }
196 
197