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