.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Architecture specific sysfs attributes in /sys/kernel |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * Huang Ying <ying.huang@intel.com> |
---|
6 | 7 | * Copyright (C) 2013, 2013 Red Hat, Inc. |
---|
7 | 8 | * Dave Young <dyoung@redhat.com> |
---|
8 | | - * |
---|
9 | | - * This file is released under the GPLv2 |
---|
10 | 9 | */ |
---|
11 | 10 | |
---|
12 | 11 | #include <linux/kobject.h> |
---|
.. | .. |
---|
92 | 91 | |
---|
93 | 92 | static int __init get_setup_data_size(int nr, size_t *size) |
---|
94 | 93 | { |
---|
95 | | - int i = 0; |
---|
| 94 | + u64 pa_data = boot_params.hdr.setup_data, pa_next; |
---|
| 95 | + struct setup_indirect *indirect; |
---|
96 | 96 | struct setup_data *data; |
---|
97 | | - u64 pa_data = boot_params.hdr.setup_data; |
---|
| 97 | + int i = 0; |
---|
| 98 | + u32 len; |
---|
98 | 99 | |
---|
99 | 100 | while (pa_data) { |
---|
100 | 101 | data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); |
---|
101 | 102 | if (!data) |
---|
102 | 103 | return -ENOMEM; |
---|
| 104 | + pa_next = data->next; |
---|
| 105 | + |
---|
103 | 106 | if (nr == i) { |
---|
104 | | - *size = data->len; |
---|
| 107 | + if (data->type == SETUP_INDIRECT) { |
---|
| 108 | + len = sizeof(*data) + data->len; |
---|
| 109 | + memunmap(data); |
---|
| 110 | + data = memremap(pa_data, len, MEMREMAP_WB); |
---|
| 111 | + if (!data) |
---|
| 112 | + return -ENOMEM; |
---|
| 113 | + |
---|
| 114 | + indirect = (struct setup_indirect *)data->data; |
---|
| 115 | + |
---|
| 116 | + if (indirect->type != SETUP_INDIRECT) |
---|
| 117 | + *size = indirect->len; |
---|
| 118 | + else |
---|
| 119 | + *size = data->len; |
---|
| 120 | + } else { |
---|
| 121 | + *size = data->len; |
---|
| 122 | + } |
---|
| 123 | + |
---|
105 | 124 | memunmap(data); |
---|
106 | 125 | return 0; |
---|
107 | 126 | } |
---|
108 | 127 | |
---|
109 | | - pa_data = data->next; |
---|
| 128 | + pa_data = pa_next; |
---|
110 | 129 | memunmap(data); |
---|
111 | 130 | i++; |
---|
112 | 131 | } |
---|
.. | .. |
---|
116 | 135 | static ssize_t type_show(struct kobject *kobj, |
---|
117 | 136 | struct kobj_attribute *attr, char *buf) |
---|
118 | 137 | { |
---|
| 138 | + struct setup_indirect *indirect; |
---|
| 139 | + struct setup_data *data; |
---|
119 | 140 | int nr, ret; |
---|
120 | 141 | u64 paddr; |
---|
121 | | - struct setup_data *data; |
---|
| 142 | + u32 len; |
---|
122 | 143 | |
---|
123 | 144 | ret = kobj_to_setup_data_nr(kobj, &nr); |
---|
124 | 145 | if (ret) |
---|
.. | .. |
---|
131 | 152 | if (!data) |
---|
132 | 153 | return -ENOMEM; |
---|
133 | 154 | |
---|
134 | | - ret = sprintf(buf, "0x%x\n", data->type); |
---|
| 155 | + if (data->type == SETUP_INDIRECT) { |
---|
| 156 | + len = sizeof(*data) + data->len; |
---|
| 157 | + memunmap(data); |
---|
| 158 | + data = memremap(paddr, len, MEMREMAP_WB); |
---|
| 159 | + if (!data) |
---|
| 160 | + return -ENOMEM; |
---|
| 161 | + |
---|
| 162 | + indirect = (struct setup_indirect *)data->data; |
---|
| 163 | + |
---|
| 164 | + ret = sprintf(buf, "0x%x\n", indirect->type); |
---|
| 165 | + } else { |
---|
| 166 | + ret = sprintf(buf, "0x%x\n", data->type); |
---|
| 167 | + } |
---|
| 168 | + |
---|
135 | 169 | memunmap(data); |
---|
136 | 170 | return ret; |
---|
137 | 171 | } |
---|
.. | .. |
---|
142 | 176 | char *buf, |
---|
143 | 177 | loff_t off, size_t count) |
---|
144 | 178 | { |
---|
145 | | - int nr, ret = 0; |
---|
146 | | - u64 paddr; |
---|
| 179 | + struct setup_indirect *indirect; |
---|
147 | 180 | struct setup_data *data; |
---|
| 181 | + int nr, ret = 0; |
---|
| 182 | + u64 paddr, len; |
---|
148 | 183 | void *p; |
---|
149 | 184 | |
---|
150 | 185 | ret = kobj_to_setup_data_nr(kobj, &nr); |
---|
.. | .. |
---|
158 | 193 | if (!data) |
---|
159 | 194 | return -ENOMEM; |
---|
160 | 195 | |
---|
161 | | - if (off > data->len) { |
---|
| 196 | + if (data->type == SETUP_INDIRECT) { |
---|
| 197 | + len = sizeof(*data) + data->len; |
---|
| 198 | + memunmap(data); |
---|
| 199 | + data = memremap(paddr, len, MEMREMAP_WB); |
---|
| 200 | + if (!data) |
---|
| 201 | + return -ENOMEM; |
---|
| 202 | + |
---|
| 203 | + indirect = (struct setup_indirect *)data->data; |
---|
| 204 | + |
---|
| 205 | + if (indirect->type != SETUP_INDIRECT) { |
---|
| 206 | + paddr = indirect->addr; |
---|
| 207 | + len = indirect->len; |
---|
| 208 | + } else { |
---|
| 209 | + /* |
---|
| 210 | + * Even though this is technically undefined, return |
---|
| 211 | + * the data as though it is a normal setup_data struct. |
---|
| 212 | + * This will at least allow it to be inspected. |
---|
| 213 | + */ |
---|
| 214 | + paddr += sizeof(*data); |
---|
| 215 | + len = data->len; |
---|
| 216 | + } |
---|
| 217 | + } else { |
---|
| 218 | + paddr += sizeof(*data); |
---|
| 219 | + len = data->len; |
---|
| 220 | + } |
---|
| 221 | + |
---|
| 222 | + if (off > len) { |
---|
162 | 223 | ret = -EINVAL; |
---|
163 | 224 | goto out; |
---|
164 | 225 | } |
---|
165 | 226 | |
---|
166 | | - if (count > data->len - off) |
---|
167 | | - count = data->len - off; |
---|
| 227 | + if (count > len - off) |
---|
| 228 | + count = len - off; |
---|
168 | 229 | |
---|
169 | 230 | if (!count) |
---|
170 | 231 | goto out; |
---|
171 | 232 | |
---|
172 | 233 | ret = count; |
---|
173 | | - p = memremap(paddr + sizeof(*data), data->len, MEMREMAP_WB); |
---|
| 234 | + p = memremap(paddr, len, MEMREMAP_WB); |
---|
174 | 235 | if (!p) { |
---|
175 | 236 | ret = -ENOMEM; |
---|
176 | 237 | goto out; |
---|