1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* sstate.c: System soft state support. |
3 | * |
4 | * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/notifier.h> |
9 | #include <linux/panic_notifier.h> |
10 | #include <linux/reboot.h> |
11 | #include <linux/init.h> |
12 | |
13 | #include <asm/hypervisor.h> |
14 | #include <asm/spitfire.h> |
15 | #include <asm/oplib.h> |
16 | #include <asm/head.h> |
17 | #include <asm/io.h> |
18 | |
19 | #include "kernel.h" |
20 | |
21 | static int hv_supports_soft_state; |
22 | |
23 | static void do_set_sstate(unsigned long state, const char *msg) |
24 | { |
25 | unsigned long err; |
26 | |
27 | if (!hv_supports_soft_state) |
28 | return; |
29 | |
30 | err = sun4v_mach_set_soft_state(state, kimage_addr_to_ra(msg)); |
31 | if (err) { |
32 | printk(KERN_WARNING "SSTATE: Failed to set soft-state to " |
33 | "state[%lx] msg[%s], err=%lu\n" , |
34 | state, msg, err); |
35 | } |
36 | } |
37 | |
38 | static const char booting_msg[32] __attribute__((aligned(32))) = |
39 | "Linux booting" ; |
40 | static const char running_msg[32] __attribute__((aligned(32))) = |
41 | "Linux running" ; |
42 | static const char halting_msg[32] __attribute__((aligned(32))) = |
43 | "Linux halting" ; |
44 | static const char poweroff_msg[32] __attribute__((aligned(32))) = |
45 | "Linux powering off" ; |
46 | static const char rebooting_msg[32] __attribute__((aligned(32))) = |
47 | "Linux rebooting" ; |
48 | static const char panicking_msg[32] __attribute__((aligned(32))) = |
49 | "Linux panicking" ; |
50 | |
51 | static int sstate_reboot_call(struct notifier_block *np, unsigned long type, void *_unused) |
52 | { |
53 | const char *msg; |
54 | |
55 | switch (type) { |
56 | case SYS_DOWN: |
57 | default: |
58 | msg = rebooting_msg; |
59 | break; |
60 | |
61 | case SYS_HALT: |
62 | msg = halting_msg; |
63 | break; |
64 | |
65 | case SYS_POWER_OFF: |
66 | msg = poweroff_msg; |
67 | break; |
68 | } |
69 | |
70 | do_set_sstate(state: HV_SOFT_STATE_TRANSITION, msg); |
71 | |
72 | return NOTIFY_OK; |
73 | } |
74 | |
75 | static struct notifier_block sstate_reboot_notifier = { |
76 | .notifier_call = sstate_reboot_call, |
77 | }; |
78 | |
79 | static int sstate_panic_event(struct notifier_block *n, unsigned long event, void *ptr) |
80 | { |
81 | do_set_sstate(state: HV_SOFT_STATE_TRANSITION, msg: panicking_msg); |
82 | |
83 | return NOTIFY_DONE; |
84 | } |
85 | |
86 | static struct notifier_block sstate_panic_block = { |
87 | .notifier_call = sstate_panic_event, |
88 | .priority = INT_MAX, |
89 | }; |
90 | |
91 | static int __init sstate_init(void) |
92 | { |
93 | unsigned long major, minor; |
94 | |
95 | if (tlb_type != hypervisor) |
96 | return 0; |
97 | |
98 | major = 1; |
99 | minor = 0; |
100 | if (sun4v_hvapi_register(HV_GRP_SOFT_STATE, major, &minor)) |
101 | return 0; |
102 | |
103 | hv_supports_soft_state = 1; |
104 | |
105 | prom_sun4v_guest_soft_state(); |
106 | |
107 | do_set_sstate(state: HV_SOFT_STATE_TRANSITION, msg: booting_msg); |
108 | |
109 | atomic_notifier_chain_register(nh: &panic_notifier_list, |
110 | nb: &sstate_panic_block); |
111 | register_reboot_notifier(&sstate_reboot_notifier); |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | core_initcall(sstate_init); |
117 | |
118 | static int __init sstate_running(void) |
119 | { |
120 | do_set_sstate(state: HV_SOFT_STATE_NORMAL, msg: running_msg); |
121 | return 0; |
122 | } |
123 | |
124 | late_initcall(sstate_running); |
125 | |