1ad6eb31eSJames Morse // SPDX-License-Identifier: GPL-2.0 2ad6eb31eSJames Morse // Copyright (C) 2017 Arm Ltd. 3ad6eb31eSJames Morse #define pr_fmt(fmt) "sdei: " fmt 4ad6eb31eSJames Morse 5ad6eb31eSJames Morse #include <linux/acpi.h> 6ad6eb31eSJames Morse #include <linux/arm_sdei.h> 7ad6eb31eSJames Morse #include <linux/arm-smccc.h> 8ad6eb31eSJames Morse #include <linux/bitops.h> 9ad6eb31eSJames Morse #include <linux/compiler.h> 10*da351827SJames Morse #include <linux/cpuhotplug.h> 11*da351827SJames Morse #include <linux/cpu_pm.h> 12ad6eb31eSJames Morse #include <linux/errno.h> 13ad6eb31eSJames Morse #include <linux/hardirq.h> 14ad6eb31eSJames Morse #include <linux/kernel.h> 15ad6eb31eSJames Morse #include <linux/kprobes.h> 16ad6eb31eSJames Morse #include <linux/kvm_host.h> 17ad6eb31eSJames Morse #include <linux/list.h> 18ad6eb31eSJames Morse #include <linux/mutex.h> 19*da351827SJames Morse #include <linux/notifier.h> 20ad6eb31eSJames Morse #include <linux/of.h> 21ad6eb31eSJames Morse #include <linux/of_platform.h> 22ad6eb31eSJames Morse #include <linux/percpu.h> 23ad6eb31eSJames Morse #include <linux/platform_device.h> 24*da351827SJames Morse #include <linux/pm.h> 25ad6eb31eSJames Morse #include <linux/ptrace.h> 26ad6eb31eSJames Morse #include <linux/preempt.h> 27*da351827SJames Morse #include <linux/reboot.h> 28ad6eb31eSJames Morse #include <linux/slab.h> 29ad6eb31eSJames Morse #include <linux/smp.h> 30ad6eb31eSJames Morse #include <linux/spinlock.h> 31ad6eb31eSJames Morse #include <linux/uaccess.h> 32ad6eb31eSJames Morse 33ad6eb31eSJames Morse /* 34ad6eb31eSJames Morse * The call to use to reach the firmware. 35ad6eb31eSJames Morse */ 36ad6eb31eSJames Morse static asmlinkage void (*sdei_firmware_call)(unsigned long function_id, 37ad6eb31eSJames Morse unsigned long arg0, unsigned long arg1, 38ad6eb31eSJames Morse unsigned long arg2, unsigned long arg3, 39ad6eb31eSJames Morse unsigned long arg4, struct arm_smccc_res *res); 40ad6eb31eSJames Morse 41ad6eb31eSJames Morse /* entry point from firmware to arch asm code */ 42ad6eb31eSJames Morse static unsigned long sdei_entry_point; 43ad6eb31eSJames Morse 44ad6eb31eSJames Morse struct sdei_event { 45*da351827SJames Morse /* These three are protected by the sdei_list_lock */ 46ad6eb31eSJames Morse struct list_head list; 47*da351827SJames Morse bool reregister; 48*da351827SJames Morse bool reenable; 49*da351827SJames Morse 50ad6eb31eSJames Morse u32 event_num; 51ad6eb31eSJames Morse u8 type; 52ad6eb31eSJames Morse u8 priority; 53ad6eb31eSJames Morse 54ad6eb31eSJames Morse /* This pointer is handed to firmware as the event argument. */ 55ad6eb31eSJames Morse struct sdei_registered_event *registered; 56ad6eb31eSJames Morse }; 57ad6eb31eSJames Morse 58ad6eb31eSJames Morse /* Take the mutex for any API call or modification. Take the mutex first. */ 59ad6eb31eSJames Morse static DEFINE_MUTEX(sdei_events_lock); 60ad6eb31eSJames Morse 61ad6eb31eSJames Morse /* and then hold this when modifying the list */ 62ad6eb31eSJames Morse static DEFINE_SPINLOCK(sdei_list_lock); 63ad6eb31eSJames Morse static LIST_HEAD(sdei_list); 64ad6eb31eSJames Morse 65ad6eb31eSJames Morse static int sdei_to_linux_errno(unsigned long sdei_err) 66ad6eb31eSJames Morse { 67ad6eb31eSJames Morse switch (sdei_err) { 68ad6eb31eSJames Morse case SDEI_NOT_SUPPORTED: 69ad6eb31eSJames Morse return -EOPNOTSUPP; 70ad6eb31eSJames Morse case SDEI_INVALID_PARAMETERS: 71ad6eb31eSJames Morse return -EINVAL; 72ad6eb31eSJames Morse case SDEI_DENIED: 73ad6eb31eSJames Morse return -EPERM; 74ad6eb31eSJames Morse case SDEI_PENDING: 75ad6eb31eSJames Morse return -EINPROGRESS; 76ad6eb31eSJames Morse case SDEI_OUT_OF_RESOURCE: 77ad6eb31eSJames Morse return -ENOMEM; 78ad6eb31eSJames Morse } 79ad6eb31eSJames Morse 80ad6eb31eSJames Morse /* Not an error value ... */ 81ad6eb31eSJames Morse return sdei_err; 82ad6eb31eSJames Morse } 83ad6eb31eSJames Morse 84ad6eb31eSJames Morse /* 85ad6eb31eSJames Morse * If x0 is any of these values, then the call failed, use sdei_to_linux_errno() 86ad6eb31eSJames Morse * to translate. 87ad6eb31eSJames Morse */ 88ad6eb31eSJames Morse static int sdei_is_err(struct arm_smccc_res *res) 89ad6eb31eSJames Morse { 90ad6eb31eSJames Morse switch (res->a0) { 91ad6eb31eSJames Morse case SDEI_NOT_SUPPORTED: 92ad6eb31eSJames Morse case SDEI_INVALID_PARAMETERS: 93ad6eb31eSJames Morse case SDEI_DENIED: 94ad6eb31eSJames Morse case SDEI_PENDING: 95ad6eb31eSJames Morse case SDEI_OUT_OF_RESOURCE: 96ad6eb31eSJames Morse return true; 97ad6eb31eSJames Morse } 98ad6eb31eSJames Morse 99ad6eb31eSJames Morse return false; 100ad6eb31eSJames Morse } 101ad6eb31eSJames Morse 102ad6eb31eSJames Morse static int invoke_sdei_fn(unsigned long function_id, unsigned long arg0, 103ad6eb31eSJames Morse unsigned long arg1, unsigned long arg2, 104ad6eb31eSJames Morse unsigned long arg3, unsigned long arg4, 105ad6eb31eSJames Morse u64 *result) 106ad6eb31eSJames Morse { 107ad6eb31eSJames Morse int err = 0; 108ad6eb31eSJames Morse struct arm_smccc_res res; 109ad6eb31eSJames Morse 110ad6eb31eSJames Morse if (sdei_firmware_call) { 111ad6eb31eSJames Morse sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4, 112ad6eb31eSJames Morse &res); 113ad6eb31eSJames Morse if (sdei_is_err(&res)) 114ad6eb31eSJames Morse err = sdei_to_linux_errno(res.a0); 115ad6eb31eSJames Morse } else { 116ad6eb31eSJames Morse /* 117ad6eb31eSJames Morse * !sdei_firmware_call means we failed to probe or called 118ad6eb31eSJames Morse * sdei_mark_interface_broken(). -EIO is not an error returned 119ad6eb31eSJames Morse * by sdei_to_linux_errno() and is used to suppress messages 120ad6eb31eSJames Morse * from this driver. 121ad6eb31eSJames Morse */ 122ad6eb31eSJames Morse err = -EIO; 123ad6eb31eSJames Morse res.a0 = SDEI_NOT_SUPPORTED; 124ad6eb31eSJames Morse } 125ad6eb31eSJames Morse 126ad6eb31eSJames Morse if (result) 127ad6eb31eSJames Morse *result = res.a0; 128ad6eb31eSJames Morse 129ad6eb31eSJames Morse return err; 130ad6eb31eSJames Morse } 131ad6eb31eSJames Morse 132ad6eb31eSJames Morse static struct sdei_event *sdei_event_find(u32 event_num) 133ad6eb31eSJames Morse { 134ad6eb31eSJames Morse struct sdei_event *e, *found = NULL; 135ad6eb31eSJames Morse 136ad6eb31eSJames Morse lockdep_assert_held(&sdei_events_lock); 137ad6eb31eSJames Morse 138ad6eb31eSJames Morse spin_lock(&sdei_list_lock); 139ad6eb31eSJames Morse list_for_each_entry(e, &sdei_list, list) { 140ad6eb31eSJames Morse if (e->event_num == event_num) { 141ad6eb31eSJames Morse found = e; 142ad6eb31eSJames Morse break; 143ad6eb31eSJames Morse } 144ad6eb31eSJames Morse } 145ad6eb31eSJames Morse spin_unlock(&sdei_list_lock); 146ad6eb31eSJames Morse 147ad6eb31eSJames Morse return found; 148ad6eb31eSJames Morse } 149ad6eb31eSJames Morse 150ad6eb31eSJames Morse int sdei_api_event_context(u32 query, u64 *result) 151ad6eb31eSJames Morse { 152ad6eb31eSJames Morse return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_CONTEXT, query, 0, 0, 0, 0, 153ad6eb31eSJames Morse result); 154ad6eb31eSJames Morse } 155ad6eb31eSJames Morse NOKPROBE_SYMBOL(sdei_api_event_context); 156ad6eb31eSJames Morse 157ad6eb31eSJames Morse static int sdei_api_event_get_info(u32 event, u32 info, u64 *result) 158ad6eb31eSJames Morse { 159ad6eb31eSJames Morse return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_GET_INFO, event, info, 0, 160ad6eb31eSJames Morse 0, 0, result); 161ad6eb31eSJames Morse } 162ad6eb31eSJames Morse 163ad6eb31eSJames Morse static struct sdei_event *sdei_event_create(u32 event_num, 164ad6eb31eSJames Morse sdei_event_callback *cb, 165ad6eb31eSJames Morse void *cb_arg) 166ad6eb31eSJames Morse { 167ad6eb31eSJames Morse int err; 168ad6eb31eSJames Morse u64 result; 169ad6eb31eSJames Morse struct sdei_event *event; 170ad6eb31eSJames Morse struct sdei_registered_event *reg; 171ad6eb31eSJames Morse 172ad6eb31eSJames Morse lockdep_assert_held(&sdei_events_lock); 173ad6eb31eSJames Morse 174ad6eb31eSJames Morse event = kzalloc(sizeof(*event), GFP_KERNEL); 175ad6eb31eSJames Morse if (!event) 176ad6eb31eSJames Morse return ERR_PTR(-ENOMEM); 177ad6eb31eSJames Morse 178ad6eb31eSJames Morse INIT_LIST_HEAD(&event->list); 179ad6eb31eSJames Morse event->event_num = event_num; 180ad6eb31eSJames Morse 181ad6eb31eSJames Morse err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY, 182ad6eb31eSJames Morse &result); 183ad6eb31eSJames Morse if (err) { 184ad6eb31eSJames Morse kfree(event); 185ad6eb31eSJames Morse return ERR_PTR(err); 186ad6eb31eSJames Morse } 187ad6eb31eSJames Morse event->priority = result; 188ad6eb31eSJames Morse 189ad6eb31eSJames Morse err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_TYPE, 190ad6eb31eSJames Morse &result); 191ad6eb31eSJames Morse if (err) { 192ad6eb31eSJames Morse kfree(event); 193ad6eb31eSJames Morse return ERR_PTR(err); 194ad6eb31eSJames Morse } 195ad6eb31eSJames Morse event->type = result; 196ad6eb31eSJames Morse 197ad6eb31eSJames Morse if (event->type == SDEI_EVENT_TYPE_SHARED) { 198ad6eb31eSJames Morse reg = kzalloc(sizeof(*reg), GFP_KERNEL); 199ad6eb31eSJames Morse if (!reg) { 200ad6eb31eSJames Morse kfree(event); 201ad6eb31eSJames Morse return ERR_PTR(-ENOMEM); 202ad6eb31eSJames Morse } 203ad6eb31eSJames Morse 204ad6eb31eSJames Morse reg->event_num = event_num; 205ad6eb31eSJames Morse reg->priority = event->priority; 206ad6eb31eSJames Morse 207ad6eb31eSJames Morse reg->callback = cb; 208ad6eb31eSJames Morse reg->callback_arg = cb_arg; 209ad6eb31eSJames Morse event->registered = reg; 210ad6eb31eSJames Morse } 211ad6eb31eSJames Morse 212ad6eb31eSJames Morse if (sdei_event_find(event_num)) { 213ad6eb31eSJames Morse kfree(event->registered); 214ad6eb31eSJames Morse kfree(event); 215ad6eb31eSJames Morse event = ERR_PTR(-EBUSY); 216ad6eb31eSJames Morse } else { 217ad6eb31eSJames Morse spin_lock(&sdei_list_lock); 218ad6eb31eSJames Morse list_add(&event->list, &sdei_list); 219ad6eb31eSJames Morse spin_unlock(&sdei_list_lock); 220ad6eb31eSJames Morse } 221ad6eb31eSJames Morse 222ad6eb31eSJames Morse return event; 223ad6eb31eSJames Morse } 224ad6eb31eSJames Morse 225ad6eb31eSJames Morse static void sdei_event_destroy(struct sdei_event *event) 226ad6eb31eSJames Morse { 227ad6eb31eSJames Morse lockdep_assert_held(&sdei_events_lock); 228ad6eb31eSJames Morse 229ad6eb31eSJames Morse spin_lock(&sdei_list_lock); 230ad6eb31eSJames Morse list_del(&event->list); 231ad6eb31eSJames Morse spin_unlock(&sdei_list_lock); 232ad6eb31eSJames Morse 233ad6eb31eSJames Morse if (event->type == SDEI_EVENT_TYPE_SHARED) 234ad6eb31eSJames Morse kfree(event->registered); 235ad6eb31eSJames Morse 236ad6eb31eSJames Morse kfree(event); 237ad6eb31eSJames Morse } 238ad6eb31eSJames Morse 239ad6eb31eSJames Morse static int sdei_api_get_version(u64 *version) 240ad6eb31eSJames Morse { 241ad6eb31eSJames Morse return invoke_sdei_fn(SDEI_1_0_FN_SDEI_VERSION, 0, 0, 0, 0, 0, version); 242ad6eb31eSJames Morse } 243ad6eb31eSJames Morse 244ad6eb31eSJames Morse int sdei_mask_local_cpu(void) 245ad6eb31eSJames Morse { 246ad6eb31eSJames Morse int err; 247ad6eb31eSJames Morse 248ad6eb31eSJames Morse WARN_ON_ONCE(preemptible()); 249ad6eb31eSJames Morse 250ad6eb31eSJames Morse err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0, NULL); 251ad6eb31eSJames Morse if (err && err != -EIO) { 252ad6eb31eSJames Morse pr_warn_once("failed to mask CPU[%u]: %d\n", 253ad6eb31eSJames Morse smp_processor_id(), err); 254ad6eb31eSJames Morse return err; 255ad6eb31eSJames Morse } 256ad6eb31eSJames Morse 257ad6eb31eSJames Morse return 0; 258ad6eb31eSJames Morse } 259ad6eb31eSJames Morse 260ad6eb31eSJames Morse static void _ipi_mask_cpu(void *ignored) 261ad6eb31eSJames Morse { 262ad6eb31eSJames Morse sdei_mask_local_cpu(); 263ad6eb31eSJames Morse } 264ad6eb31eSJames Morse 265*da351827SJames Morse static int sdei_cpuhp_down(unsigned int ignored) 266*da351827SJames Morse { 267*da351827SJames Morse return sdei_mask_local_cpu(); 268*da351827SJames Morse } 269*da351827SJames Morse 270ad6eb31eSJames Morse int sdei_unmask_local_cpu(void) 271ad6eb31eSJames Morse { 272ad6eb31eSJames Morse int err; 273ad6eb31eSJames Morse 274ad6eb31eSJames Morse WARN_ON_ONCE(preemptible()); 275ad6eb31eSJames Morse 276ad6eb31eSJames Morse err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PE_UNMASK, 0, 0, 0, 0, 0, NULL); 277ad6eb31eSJames Morse if (err && err != -EIO) { 278ad6eb31eSJames Morse pr_warn_once("failed to unmask CPU[%u]: %d\n", 279ad6eb31eSJames Morse smp_processor_id(), err); 280ad6eb31eSJames Morse return err; 281ad6eb31eSJames Morse } 282ad6eb31eSJames Morse 283ad6eb31eSJames Morse return 0; 284ad6eb31eSJames Morse } 285ad6eb31eSJames Morse 286ad6eb31eSJames Morse static void _ipi_unmask_cpu(void *ignored) 287ad6eb31eSJames Morse { 288ad6eb31eSJames Morse sdei_unmask_local_cpu(); 289ad6eb31eSJames Morse } 290ad6eb31eSJames Morse 291*da351827SJames Morse static int sdei_cpuhp_up(unsigned int ignored) 292*da351827SJames Morse { 293*da351827SJames Morse return sdei_unmask_local_cpu(); 294*da351827SJames Morse } 295*da351827SJames Morse 296ad6eb31eSJames Morse static void _ipi_private_reset(void *ignored) 297ad6eb31eSJames Morse { 298ad6eb31eSJames Morse int err; 299ad6eb31eSJames Morse 300ad6eb31eSJames Morse err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PRIVATE_RESET, 0, 0, 0, 0, 0, 301ad6eb31eSJames Morse NULL); 302ad6eb31eSJames Morse if (err && err != -EIO) 303ad6eb31eSJames Morse pr_warn_once("failed to reset CPU[%u]: %d\n", 304ad6eb31eSJames Morse smp_processor_id(), err); 305ad6eb31eSJames Morse } 306ad6eb31eSJames Morse 307ad6eb31eSJames Morse static int sdei_api_shared_reset(void) 308ad6eb31eSJames Morse { 309ad6eb31eSJames Morse return invoke_sdei_fn(SDEI_1_0_FN_SDEI_SHARED_RESET, 0, 0, 0, 0, 0, 310ad6eb31eSJames Morse NULL); 311ad6eb31eSJames Morse } 312ad6eb31eSJames Morse 313ad6eb31eSJames Morse static void sdei_mark_interface_broken(void) 314ad6eb31eSJames Morse { 315ad6eb31eSJames Morse pr_err("disabling SDEI firmware interface\n"); 316ad6eb31eSJames Morse on_each_cpu(&_ipi_mask_cpu, NULL, true); 317ad6eb31eSJames Morse sdei_firmware_call = NULL; 318ad6eb31eSJames Morse } 319ad6eb31eSJames Morse 320ad6eb31eSJames Morse static int sdei_platform_reset(void) 321ad6eb31eSJames Morse { 322ad6eb31eSJames Morse int err; 323ad6eb31eSJames Morse 324ad6eb31eSJames Morse on_each_cpu(&_ipi_private_reset, NULL, true); 325ad6eb31eSJames Morse err = sdei_api_shared_reset(); 326ad6eb31eSJames Morse if (err) { 327ad6eb31eSJames Morse pr_err("Failed to reset platform: %d\n", err); 328ad6eb31eSJames Morse sdei_mark_interface_broken(); 329ad6eb31eSJames Morse } 330ad6eb31eSJames Morse 331ad6eb31eSJames Morse return err; 332ad6eb31eSJames Morse } 333ad6eb31eSJames Morse 334ad6eb31eSJames Morse static int sdei_api_event_enable(u32 event_num) 335ad6eb31eSJames Morse { 336ad6eb31eSJames Morse return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_ENABLE, event_num, 0, 0, 0, 337ad6eb31eSJames Morse 0, NULL); 338ad6eb31eSJames Morse } 339ad6eb31eSJames Morse 340ad6eb31eSJames Morse int sdei_event_enable(u32 event_num) 341ad6eb31eSJames Morse { 342ad6eb31eSJames Morse int err = -EINVAL; 343ad6eb31eSJames Morse struct sdei_event *event; 344ad6eb31eSJames Morse 345ad6eb31eSJames Morse mutex_lock(&sdei_events_lock); 346ad6eb31eSJames Morse event = sdei_event_find(event_num); 347ad6eb31eSJames Morse if (!event) { 348ad6eb31eSJames Morse mutex_unlock(&sdei_events_lock); 349ad6eb31eSJames Morse return -ENOENT; 350ad6eb31eSJames Morse } 351ad6eb31eSJames Morse 352*da351827SJames Morse spin_lock(&sdei_list_lock); 353*da351827SJames Morse event->reenable = true; 354*da351827SJames Morse spin_unlock(&sdei_list_lock); 355*da351827SJames Morse 356ad6eb31eSJames Morse if (event->type == SDEI_EVENT_TYPE_SHARED) 357ad6eb31eSJames Morse err = sdei_api_event_enable(event->event_num); 358ad6eb31eSJames Morse mutex_unlock(&sdei_events_lock); 359ad6eb31eSJames Morse 360ad6eb31eSJames Morse return err; 361ad6eb31eSJames Morse } 362ad6eb31eSJames Morse EXPORT_SYMBOL(sdei_event_enable); 363ad6eb31eSJames Morse 364ad6eb31eSJames Morse static int sdei_api_event_disable(u32 event_num) 365ad6eb31eSJames Morse { 366ad6eb31eSJames Morse return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_DISABLE, event_num, 0, 0, 367ad6eb31eSJames Morse 0, 0, NULL); 368ad6eb31eSJames Morse } 369ad6eb31eSJames Morse 370ad6eb31eSJames Morse int sdei_event_disable(u32 event_num) 371ad6eb31eSJames Morse { 372ad6eb31eSJames Morse int err = -EINVAL; 373ad6eb31eSJames Morse struct sdei_event *event; 374ad6eb31eSJames Morse 375ad6eb31eSJames Morse mutex_lock(&sdei_events_lock); 376ad6eb31eSJames Morse event = sdei_event_find(event_num); 377ad6eb31eSJames Morse if (!event) { 378ad6eb31eSJames Morse mutex_unlock(&sdei_events_lock); 379ad6eb31eSJames Morse return -ENOENT; 380ad6eb31eSJames Morse } 381ad6eb31eSJames Morse 382*da351827SJames Morse spin_lock(&sdei_list_lock); 383*da351827SJames Morse event->reenable = false; 384*da351827SJames Morse spin_unlock(&sdei_list_lock); 385*da351827SJames Morse 386ad6eb31eSJames Morse if (event->type == SDEI_EVENT_TYPE_SHARED) 387ad6eb31eSJames Morse err = sdei_api_event_disable(event->event_num); 388ad6eb31eSJames Morse mutex_unlock(&sdei_events_lock); 389ad6eb31eSJames Morse 390ad6eb31eSJames Morse return err; 391ad6eb31eSJames Morse } 392ad6eb31eSJames Morse EXPORT_SYMBOL(sdei_event_disable); 393ad6eb31eSJames Morse 394ad6eb31eSJames Morse static int sdei_api_event_unregister(u32 event_num) 395ad6eb31eSJames Morse { 396ad6eb31eSJames Morse return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_UNREGISTER, event_num, 0, 397ad6eb31eSJames Morse 0, 0, 0, NULL); 398ad6eb31eSJames Morse } 399ad6eb31eSJames Morse 400ad6eb31eSJames Morse static int _sdei_event_unregister(struct sdei_event *event) 401ad6eb31eSJames Morse { 402ad6eb31eSJames Morse lockdep_assert_held(&sdei_events_lock); 403ad6eb31eSJames Morse 404*da351827SJames Morse spin_lock(&sdei_list_lock); 405*da351827SJames Morse event->reregister = false; 406*da351827SJames Morse event->reenable = false; 407*da351827SJames Morse spin_unlock(&sdei_list_lock); 408*da351827SJames Morse 409ad6eb31eSJames Morse if (event->type == SDEI_EVENT_TYPE_SHARED) 410ad6eb31eSJames Morse return sdei_api_event_unregister(event->event_num); 411ad6eb31eSJames Morse 412ad6eb31eSJames Morse return -EINVAL; 413ad6eb31eSJames Morse } 414ad6eb31eSJames Morse 415ad6eb31eSJames Morse int sdei_event_unregister(u32 event_num) 416ad6eb31eSJames Morse { 417ad6eb31eSJames Morse int err; 418ad6eb31eSJames Morse struct sdei_event *event; 419ad6eb31eSJames Morse 420ad6eb31eSJames Morse WARN_ON(in_nmi()); 421ad6eb31eSJames Morse 422ad6eb31eSJames Morse mutex_lock(&sdei_events_lock); 423ad6eb31eSJames Morse event = sdei_event_find(event_num); 424ad6eb31eSJames Morse do { 425ad6eb31eSJames Morse if (!event) { 426ad6eb31eSJames Morse pr_warn("Event %u not registered\n", event_num); 427ad6eb31eSJames Morse err = -ENOENT; 428ad6eb31eSJames Morse break; 429ad6eb31eSJames Morse } 430ad6eb31eSJames Morse 431ad6eb31eSJames Morse err = _sdei_event_unregister(event); 432ad6eb31eSJames Morse if (err) 433ad6eb31eSJames Morse break; 434ad6eb31eSJames Morse 435ad6eb31eSJames Morse sdei_event_destroy(event); 436ad6eb31eSJames Morse } while (0); 437ad6eb31eSJames Morse mutex_unlock(&sdei_events_lock); 438ad6eb31eSJames Morse 439ad6eb31eSJames Morse return err; 440ad6eb31eSJames Morse } 441ad6eb31eSJames Morse EXPORT_SYMBOL(sdei_event_unregister); 442ad6eb31eSJames Morse 443*da351827SJames Morse /* 444*da351827SJames Morse * unregister events, but don't destroy them as they are re-registered by 445*da351827SJames Morse * sdei_reregister_shared(). 446*da351827SJames Morse */ 447*da351827SJames Morse static int sdei_unregister_shared(void) 448*da351827SJames Morse { 449*da351827SJames Morse int err = 0; 450*da351827SJames Morse struct sdei_event *event; 451*da351827SJames Morse 452*da351827SJames Morse mutex_lock(&sdei_events_lock); 453*da351827SJames Morse spin_lock(&sdei_list_lock); 454*da351827SJames Morse list_for_each_entry(event, &sdei_list, list) { 455*da351827SJames Morse if (event->type != SDEI_EVENT_TYPE_SHARED) 456*da351827SJames Morse continue; 457*da351827SJames Morse 458*da351827SJames Morse err = _sdei_event_unregister(event); 459*da351827SJames Morse if (err) 460*da351827SJames Morse break; 461*da351827SJames Morse } 462*da351827SJames Morse spin_unlock(&sdei_list_lock); 463*da351827SJames Morse mutex_unlock(&sdei_events_lock); 464*da351827SJames Morse 465*da351827SJames Morse return err; 466*da351827SJames Morse } 467*da351827SJames Morse 468ad6eb31eSJames Morse static int sdei_api_event_register(u32 event_num, unsigned long entry_point, 469ad6eb31eSJames Morse void *arg, u64 flags, u64 affinity) 470ad6eb31eSJames Morse { 471ad6eb31eSJames Morse return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_REGISTER, event_num, 472ad6eb31eSJames Morse (unsigned long)entry_point, (unsigned long)arg, 473ad6eb31eSJames Morse flags, affinity, NULL); 474ad6eb31eSJames Morse } 475ad6eb31eSJames Morse 476ad6eb31eSJames Morse static int _sdei_event_register(struct sdei_event *event) 477ad6eb31eSJames Morse { 478ad6eb31eSJames Morse lockdep_assert_held(&sdei_events_lock); 479ad6eb31eSJames Morse 480ad6eb31eSJames Morse if (event->type == SDEI_EVENT_TYPE_SHARED) 481ad6eb31eSJames Morse return sdei_api_event_register(event->event_num, 482ad6eb31eSJames Morse sdei_entry_point, 483ad6eb31eSJames Morse event->registered, 484ad6eb31eSJames Morse SDEI_EVENT_REGISTER_RM_ANY, 0); 485ad6eb31eSJames Morse 486ad6eb31eSJames Morse return -EINVAL; 487ad6eb31eSJames Morse } 488ad6eb31eSJames Morse 489ad6eb31eSJames Morse int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg) 490ad6eb31eSJames Morse { 491ad6eb31eSJames Morse int err; 492ad6eb31eSJames Morse struct sdei_event *event; 493ad6eb31eSJames Morse 494ad6eb31eSJames Morse WARN_ON(in_nmi()); 495ad6eb31eSJames Morse 496ad6eb31eSJames Morse mutex_lock(&sdei_events_lock); 497ad6eb31eSJames Morse do { 498ad6eb31eSJames Morse if (sdei_event_find(event_num)) { 499ad6eb31eSJames Morse pr_warn("Event %u already registered\n", event_num); 500ad6eb31eSJames Morse err = -EBUSY; 501ad6eb31eSJames Morse break; 502ad6eb31eSJames Morse } 503ad6eb31eSJames Morse 504ad6eb31eSJames Morse event = sdei_event_create(event_num, cb, arg); 505ad6eb31eSJames Morse if (IS_ERR(event)) { 506ad6eb31eSJames Morse err = PTR_ERR(event); 507ad6eb31eSJames Morse pr_warn("Failed to create event %u: %d\n", event_num, 508ad6eb31eSJames Morse err); 509ad6eb31eSJames Morse break; 510ad6eb31eSJames Morse } 511ad6eb31eSJames Morse 512ad6eb31eSJames Morse err = _sdei_event_register(event); 513ad6eb31eSJames Morse if (err) { 514ad6eb31eSJames Morse sdei_event_destroy(event); 515ad6eb31eSJames Morse pr_warn("Failed to register event %u: %d\n", event_num, 516ad6eb31eSJames Morse err); 517ad6eb31eSJames Morse } 518ad6eb31eSJames Morse } while (0); 519ad6eb31eSJames Morse mutex_unlock(&sdei_events_lock); 520ad6eb31eSJames Morse 521ad6eb31eSJames Morse return err; 522ad6eb31eSJames Morse } 523ad6eb31eSJames Morse EXPORT_SYMBOL(sdei_event_register); 524ad6eb31eSJames Morse 525*da351827SJames Morse static int sdei_reregister_event(struct sdei_event *event) 526*da351827SJames Morse { 527*da351827SJames Morse int err; 528*da351827SJames Morse 529*da351827SJames Morse lockdep_assert_held(&sdei_events_lock); 530*da351827SJames Morse 531*da351827SJames Morse err = _sdei_event_register(event); 532*da351827SJames Morse if (err) { 533*da351827SJames Morse pr_err("Failed to re-register event %u\n", event->event_num); 534*da351827SJames Morse sdei_event_destroy(event); 535*da351827SJames Morse return err; 536*da351827SJames Morse } 537*da351827SJames Morse 538*da351827SJames Morse if (event->reenable) { 539*da351827SJames Morse if (event->type == SDEI_EVENT_TYPE_SHARED) 540*da351827SJames Morse err = sdei_api_event_enable(event->event_num); 541*da351827SJames Morse } 542*da351827SJames Morse 543*da351827SJames Morse if (err) 544*da351827SJames Morse pr_err("Failed to re-enable event %u\n", event->event_num); 545*da351827SJames Morse 546*da351827SJames Morse return err; 547*da351827SJames Morse } 548*da351827SJames Morse 549*da351827SJames Morse static int sdei_reregister_shared(void) 550*da351827SJames Morse { 551*da351827SJames Morse int err = 0; 552*da351827SJames Morse struct sdei_event *event; 553*da351827SJames Morse 554*da351827SJames Morse mutex_lock(&sdei_events_lock); 555*da351827SJames Morse spin_lock(&sdei_list_lock); 556*da351827SJames Morse list_for_each_entry(event, &sdei_list, list) { 557*da351827SJames Morse if (event->type != SDEI_EVENT_TYPE_SHARED) 558*da351827SJames Morse continue; 559*da351827SJames Morse 560*da351827SJames Morse if (event->reregister) { 561*da351827SJames Morse err = sdei_reregister_event(event); 562*da351827SJames Morse if (err) 563*da351827SJames Morse break; 564*da351827SJames Morse } 565*da351827SJames Morse } 566*da351827SJames Morse spin_unlock(&sdei_list_lock); 567*da351827SJames Morse mutex_unlock(&sdei_events_lock); 568*da351827SJames Morse 569*da351827SJames Morse return err; 570*da351827SJames Morse } 571*da351827SJames Morse 572*da351827SJames Morse /* When entering idle, mask/unmask events for this cpu */ 573*da351827SJames Morse static int sdei_pm_notifier(struct notifier_block *nb, unsigned long action, 574*da351827SJames Morse void *data) 575*da351827SJames Morse { 576*da351827SJames Morse int rv; 577*da351827SJames Morse 578*da351827SJames Morse switch (action) { 579*da351827SJames Morse case CPU_PM_ENTER: 580*da351827SJames Morse rv = sdei_mask_local_cpu(); 581*da351827SJames Morse break; 582*da351827SJames Morse case CPU_PM_EXIT: 583*da351827SJames Morse case CPU_PM_ENTER_FAILED: 584*da351827SJames Morse rv = sdei_unmask_local_cpu(); 585*da351827SJames Morse break; 586*da351827SJames Morse default: 587*da351827SJames Morse return NOTIFY_DONE; 588*da351827SJames Morse } 589*da351827SJames Morse 590*da351827SJames Morse if (rv) 591*da351827SJames Morse return notifier_from_errno(rv); 592*da351827SJames Morse 593*da351827SJames Morse return NOTIFY_OK; 594*da351827SJames Morse } 595*da351827SJames Morse 596*da351827SJames Morse static struct notifier_block sdei_pm_nb = { 597*da351827SJames Morse .notifier_call = sdei_pm_notifier, 598*da351827SJames Morse }; 599*da351827SJames Morse 600*da351827SJames Morse static int sdei_device_suspend(struct device *dev) 601*da351827SJames Morse { 602*da351827SJames Morse on_each_cpu(_ipi_mask_cpu, NULL, true); 603*da351827SJames Morse 604*da351827SJames Morse return 0; 605*da351827SJames Morse } 606*da351827SJames Morse 607*da351827SJames Morse static int sdei_device_resume(struct device *dev) 608*da351827SJames Morse { 609*da351827SJames Morse on_each_cpu(_ipi_unmask_cpu, NULL, true); 610*da351827SJames Morse 611*da351827SJames Morse return 0; 612*da351827SJames Morse } 613*da351827SJames Morse 614*da351827SJames Morse /* 615*da351827SJames Morse * We need all events to be reregistered when we resume from hibernate. 616*da351827SJames Morse * 617*da351827SJames Morse * The sequence is freeze->thaw. Reboot. freeze->restore. We unregister 618*da351827SJames Morse * events during freeze, then re-register and re-enable them during thaw 619*da351827SJames Morse * and restore. 620*da351827SJames Morse */ 621*da351827SJames Morse static int sdei_device_freeze(struct device *dev) 622*da351827SJames Morse { 623*da351827SJames Morse int err; 624*da351827SJames Morse 625*da351827SJames Morse cpuhp_remove_state(CPUHP_AP_ARM_SDEI_STARTING); 626*da351827SJames Morse 627*da351827SJames Morse err = sdei_unregister_shared(); 628*da351827SJames Morse if (err) 629*da351827SJames Morse return err; 630*da351827SJames Morse 631*da351827SJames Morse return 0; 632*da351827SJames Morse } 633*da351827SJames Morse 634*da351827SJames Morse static int sdei_device_thaw(struct device *dev) 635*da351827SJames Morse { 636*da351827SJames Morse int err; 637*da351827SJames Morse 638*da351827SJames Morse /* re-register shared events */ 639*da351827SJames Morse err = sdei_reregister_shared(); 640*da351827SJames Morse if (err) { 641*da351827SJames Morse pr_warn("Failed to re-register shared events...\n"); 642*da351827SJames Morse sdei_mark_interface_broken(); 643*da351827SJames Morse return err; 644*da351827SJames Morse } 645*da351827SJames Morse 646*da351827SJames Morse err = cpuhp_setup_state(CPUHP_AP_ARM_SDEI_STARTING, "SDEI", 647*da351827SJames Morse &sdei_cpuhp_up, &sdei_cpuhp_down); 648*da351827SJames Morse if (err) 649*da351827SJames Morse pr_warn("Failed to re-register CPU hotplug notifier...\n"); 650*da351827SJames Morse 651*da351827SJames Morse return err; 652*da351827SJames Morse } 653*da351827SJames Morse 654*da351827SJames Morse static int sdei_device_restore(struct device *dev) 655*da351827SJames Morse { 656*da351827SJames Morse int err; 657*da351827SJames Morse 658*da351827SJames Morse err = sdei_platform_reset(); 659*da351827SJames Morse if (err) 660*da351827SJames Morse return err; 661*da351827SJames Morse 662*da351827SJames Morse return sdei_device_thaw(dev); 663*da351827SJames Morse } 664*da351827SJames Morse 665*da351827SJames Morse static const struct dev_pm_ops sdei_pm_ops = { 666*da351827SJames Morse .suspend = sdei_device_suspend, 667*da351827SJames Morse .resume = sdei_device_resume, 668*da351827SJames Morse .freeze = sdei_device_freeze, 669*da351827SJames Morse .thaw = sdei_device_thaw, 670*da351827SJames Morse .restore = sdei_device_restore, 671*da351827SJames Morse }; 672*da351827SJames Morse 673*da351827SJames Morse /* 674*da351827SJames Morse * Mask all CPUs and unregister all events on panic, reboot or kexec. 675*da351827SJames Morse */ 676*da351827SJames Morse static int sdei_reboot_notifier(struct notifier_block *nb, unsigned long action, 677*da351827SJames Morse void *data) 678*da351827SJames Morse { 679*da351827SJames Morse /* 680*da351827SJames Morse * We are going to reset the interface, after this there is no point 681*da351827SJames Morse * doing work when we take CPUs offline. 682*da351827SJames Morse */ 683*da351827SJames Morse cpuhp_remove_state(CPUHP_AP_ARM_SDEI_STARTING); 684*da351827SJames Morse 685*da351827SJames Morse sdei_platform_reset(); 686*da351827SJames Morse 687*da351827SJames Morse return NOTIFY_OK; 688*da351827SJames Morse } 689*da351827SJames Morse 690*da351827SJames Morse static struct notifier_block sdei_reboot_nb = { 691*da351827SJames Morse .notifier_call = sdei_reboot_notifier, 692*da351827SJames Morse }; 693*da351827SJames Morse 694ad6eb31eSJames Morse static void sdei_smccc_smc(unsigned long function_id, 695ad6eb31eSJames Morse unsigned long arg0, unsigned long arg1, 696ad6eb31eSJames Morse unsigned long arg2, unsigned long arg3, 697ad6eb31eSJames Morse unsigned long arg4, struct arm_smccc_res *res) 698ad6eb31eSJames Morse { 699ad6eb31eSJames Morse arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); 700ad6eb31eSJames Morse } 701ad6eb31eSJames Morse 702ad6eb31eSJames Morse static void sdei_smccc_hvc(unsigned long function_id, 703ad6eb31eSJames Morse unsigned long arg0, unsigned long arg1, 704ad6eb31eSJames Morse unsigned long arg2, unsigned long arg3, 705ad6eb31eSJames Morse unsigned long arg4, struct arm_smccc_res *res) 706ad6eb31eSJames Morse { 707ad6eb31eSJames Morse arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); 708ad6eb31eSJames Morse } 709ad6eb31eSJames Morse 710ad6eb31eSJames Morse static int sdei_get_conduit(struct platform_device *pdev) 711ad6eb31eSJames Morse { 712ad6eb31eSJames Morse const char *method; 713ad6eb31eSJames Morse struct device_node *np = pdev->dev.of_node; 714ad6eb31eSJames Morse 715ad6eb31eSJames Morse sdei_firmware_call = NULL; 716ad6eb31eSJames Morse if (np) { 717ad6eb31eSJames Morse if (of_property_read_string(np, "method", &method)) { 718ad6eb31eSJames Morse pr_warn("missing \"method\" property\n"); 719ad6eb31eSJames Morse return CONDUIT_INVALID; 720ad6eb31eSJames Morse } 721ad6eb31eSJames Morse 722ad6eb31eSJames Morse if (!strcmp("hvc", method)) { 723ad6eb31eSJames Morse sdei_firmware_call = &sdei_smccc_hvc; 724ad6eb31eSJames Morse return CONDUIT_HVC; 725ad6eb31eSJames Morse } else if (!strcmp("smc", method)) { 726ad6eb31eSJames Morse sdei_firmware_call = &sdei_smccc_smc; 727ad6eb31eSJames Morse return CONDUIT_SMC; 728ad6eb31eSJames Morse } 729ad6eb31eSJames Morse 730ad6eb31eSJames Morse pr_warn("invalid \"method\" property: %s\n", method); 731ad6eb31eSJames Morse } 732ad6eb31eSJames Morse 733ad6eb31eSJames Morse return CONDUIT_INVALID; 734ad6eb31eSJames Morse } 735ad6eb31eSJames Morse 736ad6eb31eSJames Morse static int sdei_probe(struct platform_device *pdev) 737ad6eb31eSJames Morse { 738ad6eb31eSJames Morse int err; 739ad6eb31eSJames Morse u64 ver = 0; 740ad6eb31eSJames Morse int conduit; 741ad6eb31eSJames Morse 742ad6eb31eSJames Morse conduit = sdei_get_conduit(pdev); 743ad6eb31eSJames Morse if (!sdei_firmware_call) 744ad6eb31eSJames Morse return 0; 745ad6eb31eSJames Morse 746ad6eb31eSJames Morse err = sdei_api_get_version(&ver); 747ad6eb31eSJames Morse if (err == -EOPNOTSUPP) 748ad6eb31eSJames Morse pr_err("advertised but not implemented in platform firmware\n"); 749ad6eb31eSJames Morse if (err) { 750ad6eb31eSJames Morse pr_err("Failed to get SDEI version: %d\n", err); 751ad6eb31eSJames Morse sdei_mark_interface_broken(); 752ad6eb31eSJames Morse return err; 753ad6eb31eSJames Morse } 754ad6eb31eSJames Morse 755ad6eb31eSJames Morse pr_info("SDEIv%d.%d (0x%x) detected in firmware.\n", 756ad6eb31eSJames Morse (int)SDEI_VERSION_MAJOR(ver), (int)SDEI_VERSION_MINOR(ver), 757ad6eb31eSJames Morse (int)SDEI_VERSION_VENDOR(ver)); 758ad6eb31eSJames Morse 759ad6eb31eSJames Morse if (SDEI_VERSION_MAJOR(ver) != 1) { 760ad6eb31eSJames Morse pr_warn("Conflicting SDEI version detected.\n"); 761ad6eb31eSJames Morse sdei_mark_interface_broken(); 762ad6eb31eSJames Morse return -EINVAL; 763ad6eb31eSJames Morse } 764ad6eb31eSJames Morse 765ad6eb31eSJames Morse err = sdei_platform_reset(); 766ad6eb31eSJames Morse if (err) 767ad6eb31eSJames Morse return err; 768ad6eb31eSJames Morse 769ad6eb31eSJames Morse sdei_entry_point = sdei_arch_get_entry_point(conduit); 770ad6eb31eSJames Morse if (!sdei_entry_point) { 771ad6eb31eSJames Morse /* Not supported due to hardware or boot configuration */ 772ad6eb31eSJames Morse sdei_mark_interface_broken(); 773ad6eb31eSJames Morse return 0; 774ad6eb31eSJames Morse } 775ad6eb31eSJames Morse 776*da351827SJames Morse err = cpu_pm_register_notifier(&sdei_pm_nb); 777*da351827SJames Morse if (err) { 778*da351827SJames Morse pr_warn("Failed to register CPU PM notifier...\n"); 779*da351827SJames Morse goto error; 780*da351827SJames Morse } 781*da351827SJames Morse 782*da351827SJames Morse err = register_reboot_notifier(&sdei_reboot_nb); 783*da351827SJames Morse if (err) { 784*da351827SJames Morse pr_warn("Failed to register reboot notifier...\n"); 785*da351827SJames Morse goto remove_cpupm; 786*da351827SJames Morse } 787*da351827SJames Morse 788*da351827SJames Morse err = cpuhp_setup_state(CPUHP_AP_ARM_SDEI_STARTING, "SDEI", 789*da351827SJames Morse &sdei_cpuhp_up, &sdei_cpuhp_down); 790*da351827SJames Morse if (err) { 791*da351827SJames Morse pr_warn("Failed to register CPU hotplug notifier...\n"); 792*da351827SJames Morse goto remove_reboot; 793*da351827SJames Morse } 794ad6eb31eSJames Morse 795ad6eb31eSJames Morse return 0; 796*da351827SJames Morse 797*da351827SJames Morse remove_reboot: 798*da351827SJames Morse unregister_reboot_notifier(&sdei_reboot_nb); 799*da351827SJames Morse 800*da351827SJames Morse remove_cpupm: 801*da351827SJames Morse cpu_pm_unregister_notifier(&sdei_pm_nb); 802*da351827SJames Morse 803*da351827SJames Morse error: 804*da351827SJames Morse sdei_mark_interface_broken(); 805*da351827SJames Morse return err; 806ad6eb31eSJames Morse } 807ad6eb31eSJames Morse 808ad6eb31eSJames Morse static const struct of_device_id sdei_of_match[] = { 809ad6eb31eSJames Morse { .compatible = "arm,sdei-1.0" }, 810ad6eb31eSJames Morse {} 811ad6eb31eSJames Morse }; 812ad6eb31eSJames Morse 813ad6eb31eSJames Morse static struct platform_driver sdei_driver = { 814ad6eb31eSJames Morse .driver = { 815ad6eb31eSJames Morse .name = "sdei", 816*da351827SJames Morse .pm = &sdei_pm_ops, 817ad6eb31eSJames Morse .of_match_table = sdei_of_match, 818ad6eb31eSJames Morse }, 819ad6eb31eSJames Morse .probe = sdei_probe, 820ad6eb31eSJames Morse }; 821ad6eb31eSJames Morse 822ad6eb31eSJames Morse static bool __init sdei_present_dt(void) 823ad6eb31eSJames Morse { 824ad6eb31eSJames Morse struct platform_device *pdev; 825ad6eb31eSJames Morse struct device_node *np, *fw_np; 826ad6eb31eSJames Morse 827ad6eb31eSJames Morse fw_np = of_find_node_by_name(NULL, "firmware"); 828ad6eb31eSJames Morse if (!fw_np) 829ad6eb31eSJames Morse return false; 830ad6eb31eSJames Morse 831ad6eb31eSJames Morse np = of_find_matching_node(fw_np, sdei_of_match); 832ad6eb31eSJames Morse of_node_put(fw_np); 833ad6eb31eSJames Morse if (!np) 834ad6eb31eSJames Morse return false; 835ad6eb31eSJames Morse 836ad6eb31eSJames Morse pdev = of_platform_device_create(np, sdei_driver.driver.name, NULL); 837ad6eb31eSJames Morse of_node_put(np); 838ad6eb31eSJames Morse if (IS_ERR(pdev)) 839ad6eb31eSJames Morse return false; 840ad6eb31eSJames Morse 841ad6eb31eSJames Morse return true; 842ad6eb31eSJames Morse } 843ad6eb31eSJames Morse 844ad6eb31eSJames Morse static int __init sdei_init(void) 845ad6eb31eSJames Morse { 846ad6eb31eSJames Morse if (sdei_present_dt()) 847ad6eb31eSJames Morse platform_driver_register(&sdei_driver); 848ad6eb31eSJames Morse 849ad6eb31eSJames Morse return 0; 850ad6eb31eSJames Morse } 851ad6eb31eSJames Morse 852ad6eb31eSJames Morse subsys_initcall_sync(sdei_init); 853ad6eb31eSJames Morse 854ad6eb31eSJames Morse int sdei_event_handler(struct pt_regs *regs, 855ad6eb31eSJames Morse struct sdei_registered_event *arg) 856ad6eb31eSJames Morse { 857ad6eb31eSJames Morse int err; 858ad6eb31eSJames Morse mm_segment_t orig_addr_limit; 859ad6eb31eSJames Morse u32 event_num = arg->event_num; 860ad6eb31eSJames Morse 861ad6eb31eSJames Morse orig_addr_limit = get_fs(); 862ad6eb31eSJames Morse set_fs(USER_DS); 863ad6eb31eSJames Morse 864ad6eb31eSJames Morse err = arg->callback(event_num, regs, arg->callback_arg); 865ad6eb31eSJames Morse if (err) 866ad6eb31eSJames Morse pr_err_ratelimited("event %u on CPU %u failed with error: %d\n", 867ad6eb31eSJames Morse event_num, smp_processor_id(), err); 868ad6eb31eSJames Morse 869ad6eb31eSJames Morse set_fs(orig_addr_limit); 870ad6eb31eSJames Morse 871ad6eb31eSJames Morse return err; 872ad6eb31eSJames Morse } 873ad6eb31eSJames Morse NOKPROBE_SYMBOL(sdei_event_handler); 874