1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * BIOS run time interface routines. |
4 | * |
5 | * (C) Copyright 2020 Hewlett Packard Enterprise Development LP |
6 | * Copyright (C) 2007-2017 Silicon Graphics, Inc. All rights reserved. |
7 | * Copyright (c) Russ Anderson <rja@sgi.com> |
8 | */ |
9 | |
10 | #include <linux/efi.h> |
11 | #include <linux/export.h> |
12 | #include <linux/slab.h> |
13 | #include <asm/efi.h> |
14 | #include <linux/io.h> |
15 | #include <asm/pgalloc.h> |
16 | #include <asm/uv/bios.h> |
17 | #include <asm/uv/uv_hub.h> |
18 | |
19 | unsigned long uv_systab_phys __ro_after_init = EFI_INVALID_TABLE_ADDR; |
20 | |
21 | struct uv_systab *uv_systab; |
22 | |
23 | static s64 __uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, |
24 | u64 a4, u64 a5) |
25 | { |
26 | struct uv_systab *tab = uv_systab; |
27 | s64 ret; |
28 | |
29 | if (!tab || !tab->function) |
30 | /* |
31 | * BIOS does not support UV systab |
32 | */ |
33 | return BIOS_STATUS_UNIMPLEMENTED; |
34 | |
35 | ret = efi_call_virt_pointer(tab, function, (u64)which, a1, a2, a3, a4, a5); |
36 | |
37 | return ret; |
38 | } |
39 | |
40 | static s64 uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, u64 a4, |
41 | u64 a5) |
42 | { |
43 | s64 ret; |
44 | |
45 | if (down_interruptible(sem: &__efi_uv_runtime_lock)) |
46 | return BIOS_STATUS_ABORT; |
47 | |
48 | ret = __uv_bios_call(which, a1, a2, a3, a4, a5); |
49 | up(sem: &__efi_uv_runtime_lock); |
50 | |
51 | return ret; |
52 | } |
53 | |
54 | static s64 uv_bios_call_irqsave(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, |
55 | u64 a4, u64 a5) |
56 | { |
57 | unsigned long bios_flags; |
58 | s64 ret; |
59 | |
60 | if (down_interruptible(sem: &__efi_uv_runtime_lock)) |
61 | return BIOS_STATUS_ABORT; |
62 | |
63 | local_irq_save(bios_flags); |
64 | ret = __uv_bios_call(which, a1, a2, a3, a4, a5); |
65 | local_irq_restore(bios_flags); |
66 | |
67 | up(sem: &__efi_uv_runtime_lock); |
68 | |
69 | return ret; |
70 | } |
71 | |
72 | long sn_partition_id; |
73 | EXPORT_SYMBOL_GPL(sn_partition_id); |
74 | long sn_coherency_id; |
75 | EXPORT_SYMBOL_GPL(sn_coherency_id); |
76 | long sn_region_size; |
77 | EXPORT_SYMBOL_GPL(sn_region_size); |
78 | long system_serial_number; |
79 | int uv_type; |
80 | |
81 | s64 uv_bios_get_sn_info(int fc, int *uvtype, long *partid, long *coher, |
82 | long *region, long *ssn) |
83 | { |
84 | s64 ret; |
85 | u64 v0, v1; |
86 | union partition_info_u part; |
87 | |
88 | ret = uv_bios_call_irqsave(which: UV_BIOS_GET_SN_INFO, a1: fc, |
89 | a2: (u64)(&v0), a3: (u64)(&v1), a4: 0, a5: 0); |
90 | if (ret != BIOS_STATUS_SUCCESS) |
91 | return ret; |
92 | |
93 | part.val = v0; |
94 | if (uvtype) |
95 | *uvtype = part.hub_version; |
96 | if (partid) |
97 | *partid = part.partition_id; |
98 | if (coher) |
99 | *coher = part.coherence_id; |
100 | if (region) |
101 | *region = part.region_size; |
102 | if (ssn) |
103 | *ssn = v1; |
104 | return ret; |
105 | } |
106 | |
107 | int |
108 | uv_bios_mq_watchlist_alloc(unsigned long addr, unsigned int mq_size, |
109 | unsigned long *intr_mmr_offset) |
110 | { |
111 | u64 watchlist; |
112 | s64 ret; |
113 | |
114 | /* |
115 | * bios returns watchlist number or negative error number. |
116 | */ |
117 | ret = (int)uv_bios_call_irqsave(which: UV_BIOS_WATCHLIST_ALLOC, a1: addr, |
118 | a2: mq_size, a3: (u64)intr_mmr_offset, |
119 | a4: (u64)&watchlist, a5: 0); |
120 | if (ret < BIOS_STATUS_SUCCESS) |
121 | return ret; |
122 | |
123 | return watchlist; |
124 | } |
125 | EXPORT_SYMBOL_GPL(uv_bios_mq_watchlist_alloc); |
126 | |
127 | int |
128 | uv_bios_mq_watchlist_free(int blade, int watchlist_num) |
129 | { |
130 | return (int)uv_bios_call_irqsave(which: UV_BIOS_WATCHLIST_FREE, |
131 | a1: blade, a2: watchlist_num, a3: 0, a4: 0, a5: 0); |
132 | } |
133 | EXPORT_SYMBOL_GPL(uv_bios_mq_watchlist_free); |
134 | |
135 | s64 |
136 | uv_bios_change_memprotect(u64 paddr, u64 len, enum uv_memprotect perms) |
137 | { |
138 | return uv_bios_call_irqsave(which: UV_BIOS_MEMPROTECT, a1: paddr, a2: len, |
139 | a3: perms, a4: 0, a5: 0); |
140 | } |
141 | EXPORT_SYMBOL_GPL(uv_bios_change_memprotect); |
142 | |
143 | s64 |
144 | uv_bios_reserved_page_pa(u64 buf, u64 *cookie, u64 *addr, u64 *len) |
145 | { |
146 | return uv_bios_call_irqsave(which: UV_BIOS_GET_PARTITION_ADDR, a1: (u64)cookie, |
147 | a2: (u64)addr, a3: buf, a4: (u64)len, a5: 0); |
148 | } |
149 | EXPORT_SYMBOL_GPL(uv_bios_reserved_page_pa); |
150 | |
151 | s64 uv_bios_freq_base(u64 clock_type, u64 *ticks_per_second) |
152 | { |
153 | return uv_bios_call(which: UV_BIOS_FREQ_BASE, a1: clock_type, |
154 | a2: (u64)ticks_per_second, a3: 0, a4: 0, a5: 0); |
155 | } |
156 | |
157 | /* |
158 | * uv_bios_set_legacy_vga_target - Set Legacy VGA I/O Target |
159 | * @decode: true to enable target, false to disable target |
160 | * @domain: PCI domain number |
161 | * @bus: PCI bus number |
162 | * |
163 | * Returns: |
164 | * 0: Success |
165 | * -EINVAL: Invalid domain or bus number |
166 | * -ENOSYS: Capability not available |
167 | * -EBUSY: Legacy VGA I/O cannot be retargeted at this time |
168 | */ |
169 | int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus) |
170 | { |
171 | return uv_bios_call(which: UV_BIOS_SET_LEGACY_VGA_TARGET, |
172 | a1: (u64)decode, a2: (u64)domain, a3: (u64)bus, a4: 0, a5: 0); |
173 | } |
174 | |
175 | extern s64 uv_bios_get_master_nasid(u64 size, u64 *master_nasid) |
176 | { |
177 | return uv_bios_call(UV_BIOS_EXTRA, a1: 0, UV_BIOS_EXTRA_MASTER_NASID, a3: 0, |
178 | a4: size, a5: (u64)master_nasid); |
179 | } |
180 | EXPORT_SYMBOL_GPL(uv_bios_get_master_nasid); |
181 | |
182 | extern s64 uv_bios_get_heapsize(u64 nasid, u64 size, u64 *heap_size) |
183 | { |
184 | return uv_bios_call(UV_BIOS_EXTRA, a1: nasid, UV_BIOS_EXTRA_GET_HEAPSIZE, |
185 | a3: 0, a4: size, a5: (u64)heap_size); |
186 | } |
187 | EXPORT_SYMBOL_GPL(uv_bios_get_heapsize); |
188 | |
189 | extern s64 uv_bios_install_heap(u64 nasid, u64 heap_size, u64 *bios_heap) |
190 | { |
191 | return uv_bios_call(UV_BIOS_EXTRA, a1: nasid, UV_BIOS_EXTRA_INSTALL_HEAP, |
192 | a3: 0, a4: heap_size, a5: (u64)bios_heap); |
193 | } |
194 | EXPORT_SYMBOL_GPL(uv_bios_install_heap); |
195 | |
196 | extern s64 uv_bios_obj_count(u64 nasid, u64 size, u64 *objcnt) |
197 | { |
198 | return uv_bios_call(UV_BIOS_EXTRA, a1: nasid, UV_BIOS_EXTRA_OBJECT_COUNT, |
199 | a3: 0, a4: size, a5: (u64)objcnt); |
200 | } |
201 | EXPORT_SYMBOL_GPL(uv_bios_obj_count); |
202 | |
203 | extern s64 uv_bios_enum_objs(u64 nasid, u64 size, u64 *objbuf) |
204 | { |
205 | return uv_bios_call(UV_BIOS_EXTRA, a1: nasid, UV_BIOS_EXTRA_ENUM_OBJECTS, |
206 | a3: 0, a4: size, a5: (u64)objbuf); |
207 | } |
208 | EXPORT_SYMBOL_GPL(uv_bios_enum_objs); |
209 | |
210 | extern s64 uv_bios_enum_ports(u64 nasid, u64 obj_id, u64 size, u64 *portbuf) |
211 | { |
212 | return uv_bios_call(UV_BIOS_EXTRA, a1: nasid, UV_BIOS_EXTRA_ENUM_PORTS, |
213 | a3: obj_id, a4: size, a5: (u64)portbuf); |
214 | } |
215 | EXPORT_SYMBOL_GPL(uv_bios_enum_ports); |
216 | |
217 | extern s64 uv_bios_get_geoinfo(u64 nasid, u64 size, u64 *buf) |
218 | { |
219 | return uv_bios_call(UV_BIOS_GET_GEOINFO, a1: nasid, a2: (u64)buf, a3: size, a4: 0, a5: 0); |
220 | } |
221 | EXPORT_SYMBOL_GPL(uv_bios_get_geoinfo); |
222 | |
223 | extern s64 uv_bios_get_pci_topology(u64 size, u64 *buf) |
224 | { |
225 | return uv_bios_call(UV_BIOS_GET_PCI_TOPOLOGY, a1: (u64)buf, a2: size, a3: 0, a4: 0, a5: 0); |
226 | } |
227 | EXPORT_SYMBOL_GPL(uv_bios_get_pci_topology); |
228 | |
229 | unsigned long get_uv_systab_phys(bool msg) |
230 | { |
231 | if ((uv_systab_phys == EFI_INVALID_TABLE_ADDR) || |
232 | !uv_systab_phys || efi_runtime_disabled()) { |
233 | if (msg) |
234 | pr_crit("UV: UVsystab: missing\n" ); |
235 | return 0; |
236 | } |
237 | return uv_systab_phys; |
238 | } |
239 | |
240 | int uv_bios_init(void) |
241 | { |
242 | unsigned long uv_systab_phys_addr; |
243 | |
244 | uv_systab = NULL; |
245 | uv_systab_phys_addr = get_uv_systab_phys(msg: 1); |
246 | if (!uv_systab_phys_addr) |
247 | return -EEXIST; |
248 | |
249 | uv_systab = ioremap(offset: uv_systab_phys_addr, size: sizeof(struct uv_systab)); |
250 | if (!uv_systab || strncmp(uv_systab->signature, UV_SYSTAB_SIG, 4)) { |
251 | pr_err("UV: UVsystab: bad signature!\n" ); |
252 | iounmap(addr: uv_systab); |
253 | return -EINVAL; |
254 | } |
255 | |
256 | /* Starting with UV4 the UV systab size is variable */ |
257 | if (uv_systab->revision >= UV_SYSTAB_VERSION_UV4) { |
258 | int size = uv_systab->size; |
259 | |
260 | iounmap(addr: uv_systab); |
261 | uv_systab = ioremap(offset: uv_systab_phys_addr, size); |
262 | if (!uv_systab) { |
263 | pr_err("UV: UVsystab: ioremap(%d) failed!\n" , size); |
264 | return -EFAULT; |
265 | } |
266 | } |
267 | pr_info("UV: UVsystab: Revision:%x\n" , uv_systab->revision); |
268 | return 0; |
269 | } |
270 | |