Thomas Gleixner | 873e65b | 2019-05-27 08:55:15 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 2 | /* |
Geoff Levand | ca94297 | 2007-10-07 07:35:43 +1000 | [diff] [blame] | 3 | * PS3 flash memory os area. |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 4 | * |
| 5 | * Copyright (C) 2006 Sony Computer Entertainment Inc. |
| 6 | * Copyright 2006 Sony Corp. |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 7 | */ |
| 8 | |
| 9 | #include <linux/kernel.h> |
| 10 | #include <linux/io.h> |
Geoff Levand | 418ef20 | 2007-10-07 07:35:45 +1000 | [diff] [blame] | 11 | #include <linux/workqueue.h> |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 12 | #include <linux/fs.h> |
| 13 | #include <linux/syscalls.h> |
Paul Gortmaker | 66b15db | 2011-05-27 10:46:24 -0400 | [diff] [blame] | 14 | #include <linux/export.h> |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 15 | #include <linux/ctype.h> |
Yinghai Lu | 95f72d1 | 2010-07-12 14:36:09 +1000 | [diff] [blame] | 16 | #include <linux/memblock.h> |
Michael Ellerman | e988a13 | 2008-02-14 11:37:50 +1100 | [diff] [blame] | 17 | #include <linux/of.h> |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 18 | #include <linux/slab.h> |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 19 | |
David S. Miller | d9b2b2a | 2008-02-13 16:56:49 -0800 | [diff] [blame] | 20 | #include <asm/prom.h> |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 21 | |
| 22 | #include "platform.h" |
| 23 | |
| 24 | enum { |
| 25 | OS_AREA_SEGMENT_SIZE = 0X200, |
| 26 | }; |
| 27 | |
Geoff Levand | ca94297 | 2007-10-07 07:35:43 +1000 | [diff] [blame] | 28 | enum os_area_ldr_format { |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 29 | HEADER_LDR_FORMAT_RAW = 0, |
| 30 | HEADER_LDR_FORMAT_GZIP = 1, |
| 31 | }; |
| 32 | |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 33 | #define OS_AREA_HEADER_MAGIC_NUM "cell_ext_os_area" |
| 34 | |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 35 | /** |
| 36 | * struct os_area_header - os area header segment. |
| 37 | * @magic_num: Always 'cell_ext_os_area'. |
| 38 | * @hdr_version: Header format version number. |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 39 | * @db_area_offset: Starting segment number of other os database area. |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 40 | * @ldr_area_offset: Starting segment number of bootloader image area. |
| 41 | * @ldr_format: HEADER_LDR_FORMAT flag. |
| 42 | * @ldr_size: Size of bootloader image in bytes. |
| 43 | * |
| 44 | * Note that the docs refer to area offsets. These are offsets in units of |
| 45 | * segments from the start of the os area (top of the header). These are |
| 46 | * better thought of as segment numbers. The os area of the os area is |
| 47 | * reserved for the os image. |
| 48 | */ |
| 49 | |
| 50 | struct os_area_header { |
Geoff Levand | ca94297 | 2007-10-07 07:35:43 +1000 | [diff] [blame] | 51 | u8 magic_num[16]; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 52 | u32 hdr_version; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 53 | u32 db_area_offset; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 54 | u32 ldr_area_offset; |
| 55 | u32 _reserved_1; |
| 56 | u32 ldr_format; |
| 57 | u32 ldr_size; |
| 58 | u32 _reserved_2[6]; |
Geoff Levand | a8229a9 | 2007-01-26 19:07:56 -0800 | [diff] [blame] | 59 | }; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 60 | |
Geoff Levand | ca94297 | 2007-10-07 07:35:43 +1000 | [diff] [blame] | 61 | enum os_area_boot_flag { |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 62 | PARAM_BOOT_FLAG_GAME_OS = 0, |
| 63 | PARAM_BOOT_FLAG_OTHER_OS = 1, |
| 64 | }; |
| 65 | |
Geoff Levand | ca94297 | 2007-10-07 07:35:43 +1000 | [diff] [blame] | 66 | enum os_area_ctrl_button { |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 67 | PARAM_CTRL_BUTTON_O_IS_YES = 0, |
| 68 | PARAM_CTRL_BUTTON_X_IS_YES = 1, |
| 69 | }; |
| 70 | |
| 71 | /** |
| 72 | * struct os_area_params - os area params segment. |
| 73 | * @boot_flag: User preference of operating system, PARAM_BOOT_FLAG flag. |
| 74 | * @num_params: Number of params in this (params) segment. |
| 75 | * @rtc_diff: Difference in seconds between 1970 and the ps3 rtc value. |
| 76 | * @av_multi_out: User preference of AV output, PARAM_AV_MULTI_OUT flag. |
| 77 | * @ctrl_button: User preference of controller button config, PARAM_CTRL_BUTTON |
| 78 | * flag. |
| 79 | * @static_ip_addr: User preference of static IP address. |
| 80 | * @network_mask: User preference of static network mask. |
| 81 | * @default_gateway: User preference of static default gateway. |
| 82 | * @dns_primary: User preference of static primary dns server. |
| 83 | * @dns_secondary: User preference of static secondary dns server. |
| 84 | * |
Geoff Levand | ca94297 | 2007-10-07 07:35:43 +1000 | [diff] [blame] | 85 | * The ps3 rtc maintains a read-only value that approximates seconds since |
| 86 | * 2000-01-01 00:00:00 UTC. |
| 87 | * |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 88 | * User preference of zero for static_ip_addr means use dhcp. |
| 89 | */ |
| 90 | |
| 91 | struct os_area_params { |
| 92 | u32 boot_flag; |
| 93 | u32 _reserved_1[3]; |
| 94 | u32 num_params; |
| 95 | u32 _reserved_2[3]; |
| 96 | /* param 0 */ |
| 97 | s64 rtc_diff; |
| 98 | u8 av_multi_out; |
| 99 | u8 ctrl_button; |
| 100 | u8 _reserved_3[6]; |
| 101 | /* param 1 */ |
| 102 | u8 static_ip_addr[4]; |
| 103 | u8 network_mask[4]; |
| 104 | u8 default_gateway[4]; |
| 105 | u8 _reserved_4[4]; |
| 106 | /* param 2 */ |
| 107 | u8 dns_primary[4]; |
| 108 | u8 dns_secondary[4]; |
| 109 | u8 _reserved_5[8]; |
Geoff Levand | a8229a9 | 2007-01-26 19:07:56 -0800 | [diff] [blame] | 110 | }; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 111 | |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 112 | #define OS_AREA_DB_MAGIC_NUM "-db-" |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 113 | |
| 114 | /** |
| 115 | * struct os_area_db - Shared flash memory database. |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 116 | * @magic_num: Always '-db-'. |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 117 | * @version: os_area_db format version number. |
| 118 | * @index_64: byte offset of the database id index for 64 bit variables. |
| 119 | * @count_64: number of usable 64 bit index entries |
| 120 | * @index_32: byte offset of the database id index for 32 bit variables. |
| 121 | * @count_32: number of usable 32 bit index entries |
| 122 | * @index_16: byte offset of the database id index for 16 bit variables. |
| 123 | * @count_16: number of usable 16 bit index entries |
| 124 | * |
| 125 | * Flash rom storage for exclusive use by guests running in the other os lpar. |
| 126 | * The current system configuration allocates 1K (two segments) for other os |
| 127 | * use. |
| 128 | */ |
| 129 | |
| 130 | struct os_area_db { |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 131 | u8 magic_num[4]; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 132 | u16 version; |
| 133 | u16 _reserved_1; |
| 134 | u16 index_64; |
| 135 | u16 count_64; |
| 136 | u16 index_32; |
| 137 | u16 count_32; |
| 138 | u16 index_16; |
| 139 | u16 count_16; |
| 140 | u32 _reserved_2; |
| 141 | u8 _db_data[1000]; |
| 142 | }; |
| 143 | |
| 144 | /** |
| 145 | * enum os_area_db_owner - Data owners. |
| 146 | */ |
| 147 | |
| 148 | enum os_area_db_owner { |
| 149 | OS_AREA_DB_OWNER_ANY = -1, |
| 150 | OS_AREA_DB_OWNER_NONE = 0, |
| 151 | OS_AREA_DB_OWNER_PROTOTYPE = 1, |
| 152 | OS_AREA_DB_OWNER_LINUX = 2, |
| 153 | OS_AREA_DB_OWNER_PETITBOOT = 3, |
| 154 | OS_AREA_DB_OWNER_MAX = 32, |
| 155 | }; |
| 156 | |
| 157 | enum os_area_db_key { |
| 158 | OS_AREA_DB_KEY_ANY = -1, |
| 159 | OS_AREA_DB_KEY_NONE = 0, |
| 160 | OS_AREA_DB_KEY_RTC_DIFF = 1, |
| 161 | OS_AREA_DB_KEY_VIDEO_MODE = 2, |
| 162 | OS_AREA_DB_KEY_MAX = 8, |
| 163 | }; |
| 164 | |
| 165 | struct os_area_db_id { |
| 166 | int owner; |
| 167 | int key; |
| 168 | }; |
| 169 | |
| 170 | static const struct os_area_db_id os_area_db_id_empty = { |
| 171 | .owner = OS_AREA_DB_OWNER_NONE, |
| 172 | .key = OS_AREA_DB_KEY_NONE |
| 173 | }; |
| 174 | |
| 175 | static const struct os_area_db_id os_area_db_id_any = { |
| 176 | .owner = OS_AREA_DB_OWNER_ANY, |
| 177 | .key = OS_AREA_DB_KEY_ANY |
| 178 | }; |
| 179 | |
| 180 | static const struct os_area_db_id os_area_db_id_rtc_diff = { |
| 181 | .owner = OS_AREA_DB_OWNER_LINUX, |
| 182 | .key = OS_AREA_DB_KEY_RTC_DIFF |
| 183 | }; |
| 184 | |
Geoff Levand | ca94297 | 2007-10-07 07:35:43 +1000 | [diff] [blame] | 185 | #define SECONDS_FROM_1970_TO_2000 946684800LL |
| 186 | |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 187 | /** |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 188 | * struct saved_params - Static working copies of data from the PS3 'os area'. |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 189 | * |
| 190 | * The order of preference we use for the rtc_diff source: |
| 191 | * 1) The database value. |
| 192 | * 2) The game os value. |
| 193 | * 3) The number of seconds from 1970 to 2000. |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 194 | */ |
| 195 | |
Mathieu Malaterre | 8e0f973 | 2019-02-02 14:05:35 +0100 | [diff] [blame] | 196 | static struct saved_params { |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 197 | unsigned int valid; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 198 | s64 rtc_diff; |
| 199 | unsigned int av_multi_out; |
Mathieu Malaterre | 8e0f973 | 2019-02-02 14:05:35 +0100 | [diff] [blame] | 200 | } saved_params; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 201 | |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 202 | static struct property property_rtc_diff = { |
| 203 | .name = "linux,rtc_diff", |
| 204 | .length = sizeof(saved_params.rtc_diff), |
| 205 | .value = &saved_params.rtc_diff, |
| 206 | }; |
| 207 | |
| 208 | static struct property property_av_multi_out = { |
| 209 | .name = "linux,av_multi_out", |
| 210 | .length = sizeof(saved_params.av_multi_out), |
| 211 | .value = &saved_params.av_multi_out, |
| 212 | }; |
| 213 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 214 | |
| 215 | static DEFINE_MUTEX(os_area_flash_mutex); |
| 216 | |
| 217 | static const struct ps3_os_area_flash_ops *os_area_flash_ops; |
| 218 | |
| 219 | void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops) |
| 220 | { |
| 221 | mutex_lock(&os_area_flash_mutex); |
| 222 | os_area_flash_ops = ops; |
| 223 | mutex_unlock(&os_area_flash_mutex); |
| 224 | } |
| 225 | EXPORT_SYMBOL_GPL(ps3_os_area_flash_register); |
| 226 | |
| 227 | static ssize_t os_area_flash_read(void *buf, size_t count, loff_t pos) |
| 228 | { |
| 229 | ssize_t res = -ENODEV; |
| 230 | |
| 231 | mutex_lock(&os_area_flash_mutex); |
| 232 | if (os_area_flash_ops) |
| 233 | res = os_area_flash_ops->read(buf, count, pos); |
| 234 | mutex_unlock(&os_area_flash_mutex); |
| 235 | |
| 236 | return res; |
| 237 | } |
| 238 | |
| 239 | static ssize_t os_area_flash_write(const void *buf, size_t count, loff_t pos) |
| 240 | { |
| 241 | ssize_t res = -ENODEV; |
| 242 | |
| 243 | mutex_lock(&os_area_flash_mutex); |
| 244 | if (os_area_flash_ops) |
| 245 | res = os_area_flash_ops->write(buf, count, pos); |
| 246 | mutex_unlock(&os_area_flash_mutex); |
| 247 | |
| 248 | return res; |
| 249 | } |
| 250 | |
| 251 | |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 252 | /** |
| 253 | * os_area_set_property - Add or overwrite a saved_params value to the device tree. |
| 254 | * |
| 255 | * Overwrites an existing property. |
| 256 | */ |
| 257 | |
| 258 | static void os_area_set_property(struct device_node *node, |
| 259 | struct property *prop) |
| 260 | { |
| 261 | int result; |
| 262 | struct property *tmp = of_find_property(node, prop->name, NULL); |
| 263 | |
| 264 | if (tmp) { |
| 265 | pr_debug("%s:%d found %s\n", __func__, __LINE__, prop->name); |
Nathan Fontenot | 79d1c71 | 2012-10-02 16:58:46 +0000 | [diff] [blame] | 266 | of_remove_property(node, tmp); |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 267 | } |
| 268 | |
Nathan Fontenot | 79d1c71 | 2012-10-02 16:58:46 +0000 | [diff] [blame] | 269 | result = of_add_property(node, prop); |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 270 | |
| 271 | if (result) |
Nathan Fontenot | 79d1c71 | 2012-10-02 16:58:46 +0000 | [diff] [blame] | 272 | pr_debug("%s:%d of_set_property failed\n", __func__, |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 273 | __LINE__); |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * os_area_get_property - Get a saved_params value from the device tree. |
| 278 | * |
| 279 | */ |
| 280 | |
| 281 | static void __init os_area_get_property(struct device_node *node, |
| 282 | struct property *prop) |
| 283 | { |
| 284 | const struct property *tmp = of_find_property(node, prop->name, NULL); |
| 285 | |
| 286 | if (tmp) { |
| 287 | BUG_ON(prop->length != tmp->length); |
| 288 | memcpy(prop->value, tmp->value, prop->length); |
| 289 | } else |
| 290 | pr_debug("%s:%d not found %s\n", __func__, __LINE__, |
| 291 | prop->name); |
| 292 | } |
| 293 | |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 294 | static void dump_field(char *s, const u8 *field, int size_of_field) |
| 295 | { |
| 296 | #if defined(DEBUG) |
| 297 | int i; |
| 298 | |
| 299 | for (i = 0; i < size_of_field; i++) |
| 300 | s[i] = isprint(field[i]) ? field[i] : '.'; |
| 301 | s[i] = 0; |
| 302 | #endif |
| 303 | } |
| 304 | |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 305 | #define dump_header(_a) _dump_header(_a, __func__, __LINE__) |
Geert Uytterhoeven | 670ad35 | 2007-06-16 07:19:04 +1000 | [diff] [blame] | 306 | static void _dump_header(const struct os_area_header *h, const char *func, |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 307 | int line) |
| 308 | { |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 309 | char str[sizeof(h->magic_num) + 1]; |
| 310 | |
| 311 | dump_field(str, h->magic_num, sizeof(h->magic_num)); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 312 | pr_debug("%s:%d: h.magic_num: '%s'\n", func, line, |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 313 | str); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 314 | pr_debug("%s:%d: h.hdr_version: %u\n", func, line, |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 315 | h->hdr_version); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 316 | pr_debug("%s:%d: h.db_area_offset: %u\n", func, line, |
| 317 | h->db_area_offset); |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 318 | pr_debug("%s:%d: h.ldr_area_offset: %u\n", func, line, |
| 319 | h->ldr_area_offset); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 320 | pr_debug("%s:%d: h.ldr_format: %u\n", func, line, |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 321 | h->ldr_format); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 322 | pr_debug("%s:%d: h.ldr_size: %xh\n", func, line, |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 323 | h->ldr_size); |
| 324 | } |
| 325 | |
| 326 | #define dump_params(_a) _dump_params(_a, __func__, __LINE__) |
Geert Uytterhoeven | 670ad35 | 2007-06-16 07:19:04 +1000 | [diff] [blame] | 327 | static void _dump_params(const struct os_area_params *p, const char *func, |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 328 | int line) |
| 329 | { |
| 330 | pr_debug("%s:%d: p.boot_flag: %u\n", func, line, p->boot_flag); |
| 331 | pr_debug("%s:%d: p.num_params: %u\n", func, line, p->num_params); |
Stephen Rothwell | 5c94907 | 2009-01-13 20:02:39 +0000 | [diff] [blame] | 332 | pr_debug("%s:%d: p.rtc_diff %lld\n", func, line, p->rtc_diff); |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 333 | pr_debug("%s:%d: p.av_multi_out %u\n", func, line, p->av_multi_out); |
| 334 | pr_debug("%s:%d: p.ctrl_button: %u\n", func, line, p->ctrl_button); |
| 335 | pr_debug("%s:%d: p.static_ip_addr: %u.%u.%u.%u\n", func, line, |
| 336 | p->static_ip_addr[0], p->static_ip_addr[1], |
| 337 | p->static_ip_addr[2], p->static_ip_addr[3]); |
| 338 | pr_debug("%s:%d: p.network_mask: %u.%u.%u.%u\n", func, line, |
| 339 | p->network_mask[0], p->network_mask[1], |
| 340 | p->network_mask[2], p->network_mask[3]); |
| 341 | pr_debug("%s:%d: p.default_gateway: %u.%u.%u.%u\n", func, line, |
| 342 | p->default_gateway[0], p->default_gateway[1], |
| 343 | p->default_gateway[2], p->default_gateway[3]); |
| 344 | pr_debug("%s:%d: p.dns_primary: %u.%u.%u.%u\n", func, line, |
| 345 | p->dns_primary[0], p->dns_primary[1], |
| 346 | p->dns_primary[2], p->dns_primary[3]); |
| 347 | pr_debug("%s:%d: p.dns_secondary: %u.%u.%u.%u\n", func, line, |
| 348 | p->dns_secondary[0], p->dns_secondary[1], |
| 349 | p->dns_secondary[2], p->dns_secondary[3]); |
| 350 | } |
| 351 | |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 352 | static int verify_header(const struct os_area_header *header) |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 353 | { |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 354 | if (memcmp(header->magic_num, OS_AREA_HEADER_MAGIC_NUM, |
| 355 | sizeof(header->magic_num))) { |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 356 | pr_debug("%s:%d magic_num failed\n", __func__, __LINE__); |
| 357 | return -1; |
| 358 | } |
| 359 | |
| 360 | if (header->hdr_version < 1) { |
| 361 | pr_debug("%s:%d hdr_version failed\n", __func__, __LINE__); |
| 362 | return -1; |
| 363 | } |
| 364 | |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 365 | if (header->db_area_offset > header->ldr_area_offset) { |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 366 | pr_debug("%s:%d offsets failed\n", __func__, __LINE__); |
| 367 | return -1; |
| 368 | } |
| 369 | |
| 370 | return 0; |
| 371 | } |
| 372 | |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 373 | static int db_verify(const struct os_area_db *db) |
| 374 | { |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 375 | if (memcmp(db->magic_num, OS_AREA_DB_MAGIC_NUM, |
| 376 | sizeof(db->magic_num))) { |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 377 | pr_debug("%s:%d magic_num failed\n", __func__, __LINE__); |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 378 | return -EINVAL; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 379 | } |
| 380 | |
| 381 | if (db->version != 1) { |
| 382 | pr_debug("%s:%d version failed\n", __func__, __LINE__); |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 383 | return -EINVAL; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 384 | } |
| 385 | |
| 386 | return 0; |
| 387 | } |
| 388 | |
| 389 | struct db_index { |
| 390 | uint8_t owner:5; |
| 391 | uint8_t key:3; |
| 392 | }; |
| 393 | |
| 394 | struct db_iterator { |
| 395 | const struct os_area_db *db; |
| 396 | struct os_area_db_id match_id; |
| 397 | struct db_index *idx; |
| 398 | struct db_index *last_idx; |
| 399 | union { |
| 400 | uint64_t *value_64; |
| 401 | uint32_t *value_32; |
| 402 | uint16_t *value_16; |
| 403 | }; |
| 404 | }; |
| 405 | |
| 406 | static unsigned int db_align_up(unsigned int val, unsigned int size) |
| 407 | { |
| 408 | return (val + (size - 1)) & (~(size - 1)); |
| 409 | } |
| 410 | |
| 411 | /** |
| 412 | * db_for_each_64 - Iterator for 64 bit entries. |
| 413 | * |
| 414 | * A NULL value for id can be used to match all entries. |
| 415 | * OS_AREA_DB_OWNER_ANY and OS_AREA_DB_KEY_ANY can be used to match all. |
| 416 | */ |
| 417 | |
| 418 | static int db_for_each_64(const struct os_area_db *db, |
| 419 | const struct os_area_db_id *match_id, struct db_iterator *i) |
| 420 | { |
| 421 | next: |
| 422 | if (!i->db) { |
| 423 | i->db = db; |
| 424 | i->match_id = match_id ? *match_id : os_area_db_id_any; |
| 425 | i->idx = (void *)db + db->index_64; |
| 426 | i->last_idx = i->idx + db->count_64; |
| 427 | i->value_64 = (void *)db + db->index_64 |
| 428 | + db_align_up(db->count_64, 8); |
| 429 | } else { |
| 430 | i->idx++; |
| 431 | i->value_64++; |
| 432 | } |
| 433 | |
| 434 | if (i->idx >= i->last_idx) { |
| 435 | pr_debug("%s:%d: reached end\n", __func__, __LINE__); |
| 436 | return 0; |
| 437 | } |
| 438 | |
| 439 | if (i->match_id.owner != OS_AREA_DB_OWNER_ANY |
| 440 | && i->match_id.owner != (int)i->idx->owner) |
| 441 | goto next; |
| 442 | if (i->match_id.key != OS_AREA_DB_KEY_ANY |
| 443 | && i->match_id.key != (int)i->idx->key) |
| 444 | goto next; |
| 445 | |
| 446 | return 1; |
| 447 | } |
| 448 | |
| 449 | static int db_delete_64(struct os_area_db *db, const struct os_area_db_id *id) |
| 450 | { |
| 451 | struct db_iterator i; |
| 452 | |
| 453 | for (i.db = NULL; db_for_each_64(db, id, &i); ) { |
| 454 | |
| 455 | pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__, |
| 456 | i.idx->owner, i.idx->key, |
| 457 | (unsigned long long)*i.value_64); |
| 458 | |
| 459 | i.idx->owner = 0; |
| 460 | i.idx->key = 0; |
| 461 | *i.value_64 = 0; |
| 462 | } |
| 463 | return 0; |
| 464 | } |
| 465 | |
| 466 | static int db_set_64(struct os_area_db *db, const struct os_area_db_id *id, |
| 467 | uint64_t value) |
| 468 | { |
| 469 | struct db_iterator i; |
| 470 | |
| 471 | pr_debug("%s:%d: (%d:%d) <= %llxh\n", __func__, __LINE__, |
| 472 | id->owner, id->key, (unsigned long long)value); |
| 473 | |
| 474 | if (!id->owner || id->owner == OS_AREA_DB_OWNER_ANY |
| 475 | || id->key == OS_AREA_DB_KEY_ANY) { |
| 476 | pr_debug("%s:%d: bad id: (%d:%d)\n", __func__, |
| 477 | __LINE__, id->owner, id->key); |
| 478 | return -1; |
| 479 | } |
| 480 | |
| 481 | db_delete_64(db, id); |
| 482 | |
| 483 | i.db = NULL; |
| 484 | if (db_for_each_64(db, &os_area_db_id_empty, &i)) { |
| 485 | |
| 486 | pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__, |
| 487 | i.idx->owner, i.idx->key, |
| 488 | (unsigned long long)*i.value_64); |
| 489 | |
| 490 | i.idx->owner = id->owner; |
| 491 | i.idx->key = id->key; |
| 492 | *i.value_64 = value; |
| 493 | |
| 494 | pr_debug("%s:%d: set (%d:%d) <= %llxh\n", __func__, __LINE__, |
| 495 | i.idx->owner, i.idx->key, |
| 496 | (unsigned long long)*i.value_64); |
| 497 | return 0; |
| 498 | } |
| 499 | pr_debug("%s:%d: database full.\n", |
| 500 | __func__, __LINE__); |
| 501 | return -1; |
| 502 | } |
| 503 | |
| 504 | static int db_get_64(const struct os_area_db *db, |
| 505 | const struct os_area_db_id *id, uint64_t *value) |
| 506 | { |
| 507 | struct db_iterator i; |
| 508 | |
| 509 | i.db = NULL; |
| 510 | if (db_for_each_64(db, id, &i)) { |
| 511 | *value = *i.value_64; |
| 512 | pr_debug("%s:%d: found %lld\n", __func__, __LINE__, |
| 513 | (long long int)*i.value_64); |
| 514 | return 0; |
| 515 | } |
| 516 | pr_debug("%s:%d: not found\n", __func__, __LINE__); |
| 517 | return -1; |
| 518 | } |
| 519 | |
| 520 | static int db_get_rtc_diff(const struct os_area_db *db, int64_t *rtc_diff) |
| 521 | { |
| 522 | return db_get_64(db, &os_area_db_id_rtc_diff, (uint64_t*)rtc_diff); |
| 523 | } |
| 524 | |
| 525 | #define dump_db(a) _dump_db(a, __func__, __LINE__) |
| 526 | static void _dump_db(const struct os_area_db *db, const char *func, |
| 527 | int line) |
| 528 | { |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 529 | char str[sizeof(db->magic_num) + 1]; |
| 530 | |
| 531 | dump_field(str, db->magic_num, sizeof(db->magic_num)); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 532 | pr_debug("%s:%d: db.magic_num: '%s'\n", func, line, |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 533 | str); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 534 | pr_debug("%s:%d: db.version: %u\n", func, line, |
| 535 | db->version); |
| 536 | pr_debug("%s:%d: db.index_64: %u\n", func, line, |
| 537 | db->index_64); |
| 538 | pr_debug("%s:%d: db.count_64: %u\n", func, line, |
| 539 | db->count_64); |
| 540 | pr_debug("%s:%d: db.index_32: %u\n", func, line, |
| 541 | db->index_32); |
| 542 | pr_debug("%s:%d: db.count_32: %u\n", func, line, |
| 543 | db->count_32); |
| 544 | pr_debug("%s:%d: db.index_16: %u\n", func, line, |
| 545 | db->index_16); |
| 546 | pr_debug("%s:%d: db.count_16: %u\n", func, line, |
| 547 | db->count_16); |
| 548 | } |
| 549 | |
| 550 | static void os_area_db_init(struct os_area_db *db) |
| 551 | { |
| 552 | enum { |
| 553 | HEADER_SIZE = offsetof(struct os_area_db, _db_data), |
| 554 | INDEX_64_COUNT = 64, |
| 555 | VALUES_64_COUNT = 57, |
| 556 | INDEX_32_COUNT = 64, |
| 557 | VALUES_32_COUNT = 57, |
| 558 | INDEX_16_COUNT = 64, |
| 559 | VALUES_16_COUNT = 57, |
| 560 | }; |
| 561 | |
| 562 | memset(db, 0, sizeof(struct os_area_db)); |
| 563 | |
Geoff Levand | ec5d2df | 2007-12-24 04:41:01 +1100 | [diff] [blame] | 564 | memcpy(db->magic_num, OS_AREA_DB_MAGIC_NUM, sizeof(db->magic_num)); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 565 | db->version = 1; |
| 566 | db->index_64 = HEADER_SIZE; |
| 567 | db->count_64 = VALUES_64_COUNT; |
| 568 | db->index_32 = HEADER_SIZE |
| 569 | + INDEX_64_COUNT * sizeof(struct db_index) |
| 570 | + VALUES_64_COUNT * sizeof(u64); |
| 571 | db->count_32 = VALUES_32_COUNT; |
| 572 | db->index_16 = HEADER_SIZE |
| 573 | + INDEX_64_COUNT * sizeof(struct db_index) |
| 574 | + VALUES_64_COUNT * sizeof(u64) |
| 575 | + INDEX_32_COUNT * sizeof(struct db_index) |
| 576 | + VALUES_32_COUNT * sizeof(u32); |
| 577 | db->count_16 = VALUES_16_COUNT; |
| 578 | |
| 579 | /* Rules to check db layout. */ |
| 580 | |
| 581 | BUILD_BUG_ON(sizeof(struct db_index) != 1); |
| 582 | BUILD_BUG_ON(sizeof(struct os_area_db) != 2 * OS_AREA_SEGMENT_SIZE); |
| 583 | BUILD_BUG_ON(INDEX_64_COUNT & 0x7); |
| 584 | BUILD_BUG_ON(VALUES_64_COUNT > INDEX_64_COUNT); |
| 585 | BUILD_BUG_ON(INDEX_32_COUNT & 0x7); |
| 586 | BUILD_BUG_ON(VALUES_32_COUNT > INDEX_32_COUNT); |
| 587 | BUILD_BUG_ON(INDEX_16_COUNT & 0x7); |
| 588 | BUILD_BUG_ON(VALUES_16_COUNT > INDEX_16_COUNT); |
| 589 | BUILD_BUG_ON(HEADER_SIZE |
| 590 | + INDEX_64_COUNT * sizeof(struct db_index) |
| 591 | + VALUES_64_COUNT * sizeof(u64) |
| 592 | + INDEX_32_COUNT * sizeof(struct db_index) |
| 593 | + VALUES_32_COUNT * sizeof(u32) |
| 594 | + INDEX_16_COUNT * sizeof(struct db_index) |
| 595 | + VALUES_16_COUNT * sizeof(u16) |
| 596 | > sizeof(struct os_area_db)); |
| 597 | } |
| 598 | |
| 599 | /** |
| 600 | * update_flash_db - Helper for os_area_queue_work_handler. |
| 601 | * |
| 602 | */ |
| 603 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 604 | static int update_flash_db(void) |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 605 | { |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 606 | const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE; |
| 607 | struct os_area_header *header; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 608 | ssize_t count; |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 609 | int error; |
| 610 | loff_t pos; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 611 | struct os_area_db* db; |
| 612 | |
| 613 | /* Read in header and db from flash. */ |
| 614 | |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 615 | header = kmalloc(buf_len, GFP_KERNEL); |
Markus Elfring | 7ee4174 | 2020-03-27 20:26:23 +0000 | [diff] [blame] | 616 | if (!header) |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 617 | return -ENOMEM; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 618 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 619 | count = os_area_flash_read(header, buf_len, 0); |
| 620 | if (count < 0) { |
| 621 | pr_debug("%s: os_area_flash_read failed %zd\n", __func__, |
| 622 | count); |
| 623 | error = count; |
| 624 | goto fail; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 625 | } |
| 626 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 627 | pos = header->db_area_offset * OS_AREA_SEGMENT_SIZE; |
| 628 | if (count < OS_AREA_SEGMENT_SIZE || verify_header(header) || |
| 629 | count < pos) { |
| 630 | pr_debug("%s: verify_header failed\n", __func__); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 631 | dump_header(header); |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 632 | error = -EINVAL; |
| 633 | goto fail; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 634 | } |
| 635 | |
| 636 | /* Now got a good db offset and some maybe good db data. */ |
| 637 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 638 | db = (void *)header + pos; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 639 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 640 | error = db_verify(db); |
| 641 | if (error) { |
| 642 | pr_notice("%s: Verify of flash database failed, formatting.\n", |
| 643 | __func__); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 644 | dump_db(db); |
| 645 | os_area_db_init(db); |
| 646 | } |
| 647 | |
| 648 | /* Now got good db data. */ |
| 649 | |
| 650 | db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff); |
| 651 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 652 | count = os_area_flash_write(db, sizeof(struct os_area_db), pos); |
Dan Carpenter | 014704e | 2018-10-01 19:44:58 +0300 | [diff] [blame] | 653 | if (count < 0 || count < sizeof(struct os_area_db)) { |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 654 | pr_debug("%s: os_area_flash_write failed %zd\n", __func__, |
| 655 | count); |
| 656 | error = count < 0 ? count : -EIO; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 657 | } |
| 658 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 659 | fail: |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 660 | kfree(header); |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 661 | return error; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 662 | } |
| 663 | |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 664 | /** |
Geoff Levand | 418ef20 | 2007-10-07 07:35:45 +1000 | [diff] [blame] | 665 | * os_area_queue_work_handler - Asynchronous write handler. |
| 666 | * |
| 667 | * An asynchronous write for flash memory and the device tree. Do not |
| 668 | * call directly, use os_area_queue_work(). |
| 669 | */ |
| 670 | |
| 671 | static void os_area_queue_work_handler(struct work_struct *work) |
| 672 | { |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 673 | struct device_node *node; |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 674 | int error; |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 675 | |
Geoff Levand | 418ef20 | 2007-10-07 07:35:45 +1000 | [diff] [blame] | 676 | pr_debug(" -> %s:%d\n", __func__, __LINE__); |
| 677 | |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 678 | node = of_find_node_by_path("/"); |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 679 | if (node) { |
| 680 | os_area_set_property(node, &property_rtc_diff); |
| 681 | of_node_put(node); |
| 682 | } else |
| 683 | pr_debug("%s:%d of_find_node_by_path failed\n", |
| 684 | __func__, __LINE__); |
| 685 | |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 686 | error = update_flash_db(); |
| 687 | if (error) |
Joe Perches | f2c2cbc | 2016-10-24 21:00:08 -0700 | [diff] [blame] | 688 | pr_warn("%s: Could not update FLASH ROM\n", __func__); |
Geert Uytterhoeven | a4e623f | 2009-06-10 04:39:06 +0000 | [diff] [blame] | 689 | |
Geoff Levand | 418ef20 | 2007-10-07 07:35:45 +1000 | [diff] [blame] | 690 | pr_debug(" <- %s:%d\n", __func__, __LINE__); |
| 691 | } |
| 692 | |
| 693 | static void os_area_queue_work(void) |
| 694 | { |
| 695 | static DECLARE_WORK(q, os_area_queue_work_handler); |
| 696 | |
| 697 | wmb(); |
| 698 | schedule_work(&q); |
| 699 | } |
| 700 | |
| 701 | /** |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 702 | * ps3_os_area_save_params - Copy data from os area mirror to @saved_params. |
| 703 | * |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 704 | * For the convenience of the guest the HV makes a copy of the os area in |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 705 | * flash to a high address in the boot memory region and then puts that RAM |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 706 | * address and the byte count into the repository for retrieval by the guest. |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 707 | * We copy the data we want into a static variable and allow the memory setup |
Yinghai Lu | 95f72d1 | 2010-07-12 14:36:09 +1000 | [diff] [blame] | 708 | * by the HV to be claimed by the memblock manager. |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 709 | * |
| 710 | * The os area mirror will not be available to a second stage kernel, and |
| 711 | * the header verify will fail. In this case, the saved_params values will |
| 712 | * be set from flash memory or the passed in device tree in ps3_os_area_init(). |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 713 | */ |
| 714 | |
| 715 | void __init ps3_os_area_save_params(void) |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 716 | { |
| 717 | int result; |
| 718 | u64 lpar_addr; |
| 719 | unsigned int size; |
| 720 | struct os_area_header *header; |
| 721 | struct os_area_params *params; |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 722 | struct os_area_db *db; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 723 | |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 724 | pr_debug(" -> %s:%d\n", __func__, __LINE__); |
| 725 | |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 726 | result = ps3_repository_read_boot_dat_info(&lpar_addr, &size); |
| 727 | |
| 728 | if (result) { |
| 729 | pr_debug("%s:%d ps3_repository_read_boot_dat_info failed\n", |
| 730 | __func__, __LINE__); |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 731 | return; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 732 | } |
| 733 | |
| 734 | header = (struct os_area_header *)__va(lpar_addr); |
Geoff Levand | ca94297 | 2007-10-07 07:35:43 +1000 | [diff] [blame] | 735 | params = (struct os_area_params *)__va(lpar_addr |
| 736 | + OS_AREA_SEGMENT_SIZE); |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 737 | |
| 738 | result = verify_header(header); |
| 739 | |
| 740 | if (result) { |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 741 | /* Second stage kernels exit here. */ |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 742 | pr_debug("%s:%d verify_header failed\n", __func__, __LINE__); |
| 743 | dump_header(header); |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 744 | return; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 745 | } |
| 746 | |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 747 | db = (struct os_area_db *)__va(lpar_addr |
| 748 | + header->db_area_offset * OS_AREA_SEGMENT_SIZE); |
| 749 | |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 750 | dump_header(header); |
| 751 | dump_params(params); |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 752 | dump_db(db); |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 753 | |
Geoff Levand | ef2ac63 | 2007-10-09 11:07:24 +1000 | [diff] [blame] | 754 | result = db_verify(db) || db_get_rtc_diff(db, &saved_params.rtc_diff); |
| 755 | if (result) |
| 756 | saved_params.rtc_diff = params->rtc_diff ? params->rtc_diff |
| 757 | : SECONDS_FROM_1970_TO_2000; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 758 | saved_params.av_multi_out = params->av_multi_out; |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 759 | saved_params.valid = 1; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 760 | |
Geoff Levand | 01263e8 | 2007-10-07 07:35:44 +1000 | [diff] [blame] | 761 | memset(header, 0, sizeof(*header)); |
| 762 | |
| 763 | pr_debug(" <- %s:%d\n", __func__, __LINE__); |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 764 | } |
| 765 | |
| 766 | /** |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 767 | * ps3_os_area_init - Setup os area device tree properties as needed. |
| 768 | */ |
| 769 | |
| 770 | void __init ps3_os_area_init(void) |
| 771 | { |
| 772 | struct device_node *node; |
| 773 | |
| 774 | pr_debug(" -> %s:%d\n", __func__, __LINE__); |
| 775 | |
| 776 | node = of_find_node_by_path("/"); |
| 777 | |
| 778 | if (!saved_params.valid && node) { |
| 779 | /* Second stage kernels should have a dt entry. */ |
| 780 | os_area_get_property(node, &property_rtc_diff); |
| 781 | os_area_get_property(node, &property_av_multi_out); |
| 782 | } |
| 783 | |
| 784 | if(!saved_params.rtc_diff) |
| 785 | saved_params.rtc_diff = SECONDS_FROM_1970_TO_2000; |
| 786 | |
| 787 | if (node) { |
| 788 | os_area_set_property(node, &property_rtc_diff); |
| 789 | os_area_set_property(node, &property_av_multi_out); |
| 790 | of_node_put(node); |
| 791 | } else |
| 792 | pr_debug("%s:%d of_find_node_by_path failed\n", |
| 793 | __func__, __LINE__); |
| 794 | |
| 795 | pr_debug(" <- %s:%d\n", __func__, __LINE__); |
| 796 | } |
| 797 | |
| 798 | /** |
Geoff Levand | d7b98e3 | 2007-10-07 07:35:46 +1000 | [diff] [blame] | 799 | * ps3_os_area_get_rtc_diff - Returns the rtc diff value. |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 800 | */ |
| 801 | |
Geoff Levand | d7b98e3 | 2007-10-07 07:35:46 +1000 | [diff] [blame] | 802 | u64 ps3_os_area_get_rtc_diff(void) |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 803 | { |
Geoff Levand | 7db1942 | 2007-10-07 07:35:47 +1000 | [diff] [blame] | 804 | return saved_params.rtc_diff; |
Geoff Levand | 00a3e2e | 2006-11-23 00:46:58 +0100 | [diff] [blame] | 805 | } |
Geert Uytterhoeven | 47cb996 | 2009-06-10 04:39:05 +0000 | [diff] [blame] | 806 | EXPORT_SYMBOL_GPL(ps3_os_area_get_rtc_diff); |
Geert Uytterhoeven | 098e274 | 2007-01-26 19:08:24 -0800 | [diff] [blame] | 807 | |
| 808 | /** |
Geoff Levand | d7b98e3 | 2007-10-07 07:35:46 +1000 | [diff] [blame] | 809 | * ps3_os_area_set_rtc_diff - Set the rtc diff value. |
| 810 | * |
| 811 | * An asynchronous write is needed to support writing updates from |
| 812 | * the timer interrupt context. |
| 813 | */ |
| 814 | |
| 815 | void ps3_os_area_set_rtc_diff(u64 rtc_diff) |
| 816 | { |
| 817 | if (saved_params.rtc_diff != rtc_diff) { |
| 818 | saved_params.rtc_diff = rtc_diff; |
| 819 | os_area_queue_work(); |
| 820 | } |
| 821 | } |
Geert Uytterhoeven | 47cb996 | 2009-06-10 04:39:05 +0000 | [diff] [blame] | 822 | EXPORT_SYMBOL_GPL(ps3_os_area_set_rtc_diff); |
Geoff Levand | d7b98e3 | 2007-10-07 07:35:46 +1000 | [diff] [blame] | 823 | |
| 824 | /** |
Geert Uytterhoeven | 098e274 | 2007-01-26 19:08:24 -0800 | [diff] [blame] | 825 | * ps3_os_area_get_av_multi_out - Returns the default video mode. |
| 826 | */ |
| 827 | |
| 828 | enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void) |
| 829 | { |
| 830 | return saved_params.av_multi_out; |
| 831 | } |
| 832 | EXPORT_SYMBOL_GPL(ps3_os_area_get_av_multi_out); |