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