| .. | .. |
|---|
| 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; |
|---|