aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeoff Levand <geoffrey.levand@am.sony.com>2007-10-08 21:07:24 -0400
committerPaul Mackerras <paulus@samba.org>2007-10-09 07:01:59 -0400
commitef2ac63aef91f2a93da23476cc6b32a346b00c41 (patch)
tree1132a356795b0745b89db9a986cd6b2d122b8de0
parent7db19421a9b116a196845a1118729cf5988f1b57 (diff)
[POWERPC] PS3: Add os-area database routines
Add support for a simple tagged database in the PS3 flash rom os-area. The database allows the flash rom os-area to be shared between a bootloader and installed operating systems. The application ps3-flash-util or the library libps3-utils from the ps3-utils package can be used for userspace database operations. The latest ps3-utils package is available here: git://git.kernel.org/pub/scm/linux/kernel/git/geoff/ps3-utils.git Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/platforms/ps3/os-area.c445
1 files changed, 431 insertions, 14 deletions
diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c
index e50a276fcf67..766685ab26f8 100644
--- a/arch/powerpc/platforms/ps3/os-area.c
+++ b/arch/powerpc/platforms/ps3/os-area.c
@@ -21,6 +21,8 @@
21#include <linux/kernel.h> 21#include <linux/kernel.h>
22#include <linux/io.h> 22#include <linux/io.h>
23#include <linux/workqueue.h> 23#include <linux/workqueue.h>
24#include <linux/fs.h>
25#include <linux/syscalls.h>
24 26
25#include <asm/lmb.h> 27#include <asm/lmb.h>
26 28
@@ -39,7 +41,7 @@ enum os_area_ldr_format {
39 * struct os_area_header - os area header segment. 41 * struct os_area_header - os area header segment.
40 * @magic_num: Always 'cell_ext_os_area'. 42 * @magic_num: Always 'cell_ext_os_area'.
41 * @hdr_version: Header format version number. 43 * @hdr_version: Header format version number.
42 * @os_area_offset: Starting segment number of os image area. 44 * @db_area_offset: Starting segment number of other os database area.
43 * @ldr_area_offset: Starting segment number of bootloader image area. 45 * @ldr_area_offset: Starting segment number of bootloader image area.
44 * @ldr_format: HEADER_LDR_FORMAT flag. 46 * @ldr_format: HEADER_LDR_FORMAT flag.
45 * @ldr_size: Size of bootloader image in bytes. 47 * @ldr_size: Size of bootloader image in bytes.
@@ -53,7 +55,7 @@ enum os_area_ldr_format {
53struct os_area_header { 55struct os_area_header {
54 u8 magic_num[16]; 56 u8 magic_num[16];
55 u32 hdr_version; 57 u32 hdr_version;
56 u32 os_area_offset; 58 u32 db_area_offset;
57 u32 ldr_area_offset; 59 u32 ldr_area_offset;
58 u32 _reserved_1; 60 u32 _reserved_1;
59 u32 ldr_format; 61 u32 ldr_format;
@@ -112,10 +114,95 @@ struct os_area_params {
112 u8 _reserved_5[8]; 114 u8 _reserved_5[8];
113}; 115};
114 116
117enum {
118 OS_AREA_DB_MAGIC_NUM = 0x2d64622dU,
119};
120
121/**
122 * struct os_area_db - Shared flash memory database.
123 * @magic_num: Always '-db-' = 0x2d64622d.
124 * @version: os_area_db format version number.
125 * @index_64: byte offset of the database id index for 64 bit variables.
126 * @count_64: number of usable 64 bit index entries
127 * @index_32: byte offset of the database id index for 32 bit variables.
128 * @count_32: number of usable 32 bit index entries
129 * @index_16: byte offset of the database id index for 16 bit variables.
130 * @count_16: number of usable 16 bit index entries
131 *
132 * Flash rom storage for exclusive use by guests running in the other os lpar.
133 * The current system configuration allocates 1K (two segments) for other os
134 * use.
135 */
136
137struct os_area_db {
138 u32 magic_num;
139 u16 version;
140 u16 _reserved_1;
141 u16 index_64;
142 u16 count_64;
143 u16 index_32;
144 u16 count_32;
145 u16 index_16;
146 u16 count_16;
147 u32 _reserved_2;
148 u8 _db_data[1000];
149};
150
151/**
152 * enum os_area_db_owner - Data owners.
153 */
154
155enum os_area_db_owner {
156 OS_AREA_DB_OWNER_ANY = -1,
157 OS_AREA_DB_OWNER_NONE = 0,
158 OS_AREA_DB_OWNER_PROTOTYPE = 1,
159 OS_AREA_DB_OWNER_LINUX = 2,
160 OS_AREA_DB_OWNER_PETITBOOT = 3,
161 OS_AREA_DB_OWNER_MAX = 32,
162};
163
164enum os_area_db_key {
165 OS_AREA_DB_KEY_ANY = -1,
166 OS_AREA_DB_KEY_NONE = 0,
167 OS_AREA_DB_KEY_RTC_DIFF = 1,
168 OS_AREA_DB_KEY_VIDEO_MODE = 2,
169 OS_AREA_DB_KEY_MAX = 8,
170};
171
172struct os_area_db_id {
173 int owner;
174 int key;
175};
176
177static const struct os_area_db_id os_area_db_id_empty = {
178 .owner = OS_AREA_DB_OWNER_NONE,
179 .key = OS_AREA_DB_KEY_NONE
180};
181
182static const struct os_area_db_id os_area_db_id_any = {
183 .owner = OS_AREA_DB_OWNER_ANY,
184 .key = OS_AREA_DB_KEY_ANY
185};
186
187static const struct os_area_db_id os_area_db_id_rtc_diff = {
188 .owner = OS_AREA_DB_OWNER_LINUX,
189 .key = OS_AREA_DB_KEY_RTC_DIFF
190};
191
192static const struct os_area_db_id os_area_db_id_video_mode = {
193 .owner = OS_AREA_DB_OWNER_LINUX,
194 .key = OS_AREA_DB_KEY_VIDEO_MODE
195};
196
115#define SECONDS_FROM_1970_TO_2000 946684800LL 197#define SECONDS_FROM_1970_TO_2000 946684800LL
116 198
117/** 199/**
118 * struct saved_params - Static working copies of data from the PS3 'os area'. 200 * struct saved_params - Static working copies of data from the PS3 'os area'.
201 *
202 * The order of preference we use for the rtc_diff source:
203 * 1) The database value.
204 * 2) The game os value.
205 * 3) The number of seconds from 1970 to 2000.
119 */ 206 */
120 207
121struct saved_params { 208struct saved_params {
@@ -182,17 +269,17 @@ static void __init os_area_get_property(struct device_node *node,
182static void _dump_header(const struct os_area_header *h, const char *func, 269static void _dump_header(const struct os_area_header *h, const char *func,
183 int line) 270 int line)
184{ 271{
185 pr_debug("%s:%d: h.magic_num: '%s'\n", func, line, 272 pr_debug("%s:%d: h.magic_num: '%s'\n", func, line,
186 h->magic_num); 273 h->magic_num);
187 pr_debug("%s:%d: h.hdr_version: %u\n", func, line, 274 pr_debug("%s:%d: h.hdr_version: %u\n", func, line,
188 h->hdr_version); 275 h->hdr_version);
189 pr_debug("%s:%d: h.os_area_offset: %u\n", func, line, 276 pr_debug("%s:%d: h.db_area_offset: %u\n", func, line,
190 h->os_area_offset); 277 h->db_area_offset);
191 pr_debug("%s:%d: h.ldr_area_offset: %u\n", func, line, 278 pr_debug("%s:%d: h.ldr_area_offset: %u\n", func, line,
192 h->ldr_area_offset); 279 h->ldr_area_offset);
193 pr_debug("%s:%d: h.ldr_format: %u\n", func, line, 280 pr_debug("%s:%d: h.ldr_format: %u\n", func, line,
194 h->ldr_format); 281 h->ldr_format);
195 pr_debug("%s:%d: h.ldr_size: %xh\n", func, line, 282 pr_debug("%s:%d: h.ldr_size: %xh\n", func, line,
196 h->ldr_size); 283 h->ldr_size);
197} 284}
198 285
@@ -222,7 +309,7 @@ static void _dump_params(const struct os_area_params *p, const char *func,
222 p->dns_secondary[2], p->dns_secondary[3]); 309 p->dns_secondary[2], p->dns_secondary[3]);
223} 310}
224 311
225static int __init verify_header(const struct os_area_header *header) 312static int verify_header(const struct os_area_header *header)
226{ 313{
227 if (memcmp(header->magic_num, "cell_ext_os_area", 16)) { 314 if (memcmp(header->magic_num, "cell_ext_os_area", 16)) {
228 pr_debug("%s:%d magic_num failed\n", __func__, __LINE__); 315 pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
@@ -234,7 +321,7 @@ static int __init verify_header(const struct os_area_header *header)
234 return -1; 321 return -1;
235 } 322 }
236 323
237 if (header->os_area_offset > header->ldr_area_offset) { 324 if (header->db_area_offset > header->ldr_area_offset) {
238 pr_debug("%s:%d offsets failed\n", __func__, __LINE__); 325 pr_debug("%s:%d offsets failed\n", __func__, __LINE__);
239 return -1; 326 return -1;
240 } 327 }
@@ -242,6 +329,319 @@ static int __init verify_header(const struct os_area_header *header)
242 return 0; 329 return 0;
243} 330}
244 331
332static int db_verify(const struct os_area_db *db)
333{
334 if (db->magic_num != OS_AREA_DB_MAGIC_NUM) {
335 pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
336 return -1;
337 }
338
339 if (db->version != 1) {
340 pr_debug("%s:%d version failed\n", __func__, __LINE__);
341 return -1;
342 }
343
344 return 0;
345}
346
347struct db_index {
348 uint8_t owner:5;
349 uint8_t key:3;
350};
351
352struct db_iterator {
353 const struct os_area_db *db;
354 struct os_area_db_id match_id;
355 struct db_index *idx;
356 struct db_index *last_idx;
357 union {
358 uint64_t *value_64;
359 uint32_t *value_32;
360 uint16_t *value_16;
361 };
362};
363
364static unsigned int db_align_up(unsigned int val, unsigned int size)
365{
366 return (val + (size - 1)) & (~(size - 1));
367}
368
369/**
370 * db_for_each_64 - Iterator for 64 bit entries.
371 *
372 * A NULL value for id can be used to match all entries.
373 * OS_AREA_DB_OWNER_ANY and OS_AREA_DB_KEY_ANY can be used to match all.
374 */
375
376static int db_for_each_64(const struct os_area_db *db,
377 const struct os_area_db_id *match_id, struct db_iterator *i)
378{
379next:
380 if (!i->db) {
381 i->db = db;
382 i->match_id = match_id ? *match_id : os_area_db_id_any;
383 i->idx = (void *)db + db->index_64;
384 i->last_idx = i->idx + db->count_64;
385 i->value_64 = (void *)db + db->index_64
386 + db_align_up(db->count_64, 8);
387 } else {
388 i->idx++;
389 i->value_64++;
390 }
391
392 if (i->idx >= i->last_idx) {
393 pr_debug("%s:%d: reached end\n", __func__, __LINE__);
394 return 0;
395 }
396
397 if (i->match_id.owner != OS_AREA_DB_OWNER_ANY
398 && i->match_id.owner != (int)i->idx->owner)
399 goto next;
400 if (i->match_id.key != OS_AREA_DB_KEY_ANY
401 && i->match_id.key != (int)i->idx->key)
402 goto next;
403
404 return 1;
405}
406
407static int db_delete_64(struct os_area_db *db, const struct os_area_db_id *id)
408{
409 struct db_iterator i;
410
411 for (i.db = NULL; db_for_each_64(db, id, &i); ) {
412
413 pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__,
414 i.idx->owner, i.idx->key,
415 (unsigned long long)*i.value_64);
416
417 i.idx->owner = 0;
418 i.idx->key = 0;
419 *i.value_64 = 0;
420 }
421 return 0;
422}
423
424static int db_set_64(struct os_area_db *db, const struct os_area_db_id *id,
425 uint64_t value)
426{
427 struct db_iterator i;
428
429 pr_debug("%s:%d: (%d:%d) <= %llxh\n", __func__, __LINE__,
430 id->owner, id->key, (unsigned long long)value);
431
432 if (!id->owner || id->owner == OS_AREA_DB_OWNER_ANY
433 || id->key == OS_AREA_DB_KEY_ANY) {
434 pr_debug("%s:%d: bad id: (%d:%d)\n", __func__,
435 __LINE__, id->owner, id->key);
436 return -1;
437 }
438
439 db_delete_64(db, id);
440
441 i.db = NULL;
442 if (db_for_each_64(db, &os_area_db_id_empty, &i)) {
443
444 pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__,
445 i.idx->owner, i.idx->key,
446 (unsigned long long)*i.value_64);
447
448 i.idx->owner = id->owner;
449 i.idx->key = id->key;
450 *i.value_64 = value;
451
452 pr_debug("%s:%d: set (%d:%d) <= %llxh\n", __func__, __LINE__,
453 i.idx->owner, i.idx->key,
454 (unsigned long long)*i.value_64);
455 return 0;
456 }
457 pr_debug("%s:%d: database full.\n",
458 __func__, __LINE__);
459 return -1;
460}
461
462static int db_get_64(const struct os_area_db *db,
463 const struct os_area_db_id *id, uint64_t *value)
464{
465 struct db_iterator i;
466
467 i.db = NULL;
468 if (db_for_each_64(db, id, &i)) {
469 *value = *i.value_64;
470 pr_debug("%s:%d: found %lld\n", __func__, __LINE__,
471 (long long int)*i.value_64);
472 return 0;
473 }
474 pr_debug("%s:%d: not found\n", __func__, __LINE__);
475 return -1;
476}
477
478static int db_get_rtc_diff(const struct os_area_db *db, int64_t *rtc_diff)
479{
480 return db_get_64(db, &os_area_db_id_rtc_diff, (uint64_t*)rtc_diff);
481}
482
483#define dump_db(a) _dump_db(a, __func__, __LINE__)
484static void _dump_db(const struct os_area_db *db, const char *func,
485 int line)
486{
487 pr_debug("%s:%d: db.magic_num: '%s'\n", func, line,
488 (const char*)&db->magic_num);
489 pr_debug("%s:%d: db.version: %u\n", func, line,
490 db->version);
491 pr_debug("%s:%d: db.index_64: %u\n", func, line,
492 db->index_64);
493 pr_debug("%s:%d: db.count_64: %u\n", func, line,
494 db->count_64);
495 pr_debug("%s:%d: db.index_32: %u\n", func, line,
496 db->index_32);
497 pr_debug("%s:%d: db.count_32: %u\n", func, line,
498 db->count_32);
499 pr_debug("%s:%d: db.index_16: %u\n", func, line,
500 db->index_16);
501 pr_debug("%s:%d: db.count_16: %u\n", func, line,
502 db->count_16);
503}
504
505static void os_area_db_init(struct os_area_db *db)
506{
507 enum {
508 HEADER_SIZE = offsetof(struct os_area_db, _db_data),
509 INDEX_64_COUNT = 64,
510 VALUES_64_COUNT = 57,
511 INDEX_32_COUNT = 64,
512 VALUES_32_COUNT = 57,
513 INDEX_16_COUNT = 64,
514 VALUES_16_COUNT = 57,
515 };
516
517 memset(db, 0, sizeof(struct os_area_db));
518
519 db->magic_num = OS_AREA_DB_MAGIC_NUM;
520 db->version = 1;
521 db->index_64 = HEADER_SIZE;
522 db->count_64 = VALUES_64_COUNT;
523 db->index_32 = HEADER_SIZE
524 + INDEX_64_COUNT * sizeof(struct db_index)
525 + VALUES_64_COUNT * sizeof(u64);
526 db->count_32 = VALUES_32_COUNT;
527 db->index_16 = HEADER_SIZE
528 + INDEX_64_COUNT * sizeof(struct db_index)
529 + VALUES_64_COUNT * sizeof(u64)
530 + INDEX_32_COUNT * sizeof(struct db_index)
531 + VALUES_32_COUNT * sizeof(u32);
532 db->count_16 = VALUES_16_COUNT;
533
534 /* Rules to check db layout. */
535
536 BUILD_BUG_ON(sizeof(struct db_index) != 1);
537 BUILD_BUG_ON(sizeof(struct os_area_db) != 2 * OS_AREA_SEGMENT_SIZE);
538 BUILD_BUG_ON(INDEX_64_COUNT & 0x7);
539 BUILD_BUG_ON(VALUES_64_COUNT > INDEX_64_COUNT);
540 BUILD_BUG_ON(INDEX_32_COUNT & 0x7);
541 BUILD_BUG_ON(VALUES_32_COUNT > INDEX_32_COUNT);
542 BUILD_BUG_ON(INDEX_16_COUNT & 0x7);
543 BUILD_BUG_ON(VALUES_16_COUNT > INDEX_16_COUNT);
544 BUILD_BUG_ON(HEADER_SIZE
545 + INDEX_64_COUNT * sizeof(struct db_index)
546 + VALUES_64_COUNT * sizeof(u64)
547 + INDEX_32_COUNT * sizeof(struct db_index)
548 + VALUES_32_COUNT * sizeof(u32)
549 + INDEX_16_COUNT * sizeof(struct db_index)
550 + VALUES_16_COUNT * sizeof(u16)
551 > sizeof(struct os_area_db));
552}
553
554/**
555 * update_flash_db - Helper for os_area_queue_work_handler.
556 *
557 */
558
559static void update_flash_db(void)
560{
561 int result;
562 int file;
563 off_t offset;
564 ssize_t count;
565 static const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE;
566 const struct os_area_header *header;
567 struct os_area_db* db;
568
569 /* Read in header and db from flash. */
570
571 file = sys_open("/dev/ps3flash", O_RDWR, 0);
572
573 if (file < 0) {
574 pr_debug("%s:%d sys_open failed\n", __func__, __LINE__);
575 goto fail_open;
576 }
577
578 header = kmalloc(buf_len, GFP_KERNEL);
579
580 if (!header) {
581 pr_debug("%s:%d kmalloc failed\n", __func__, __LINE__);
582 goto fail_malloc;
583 }
584
585 offset = sys_lseek(file, 0, SEEK_SET);
586
587 if (offset != 0) {
588 pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__);
589 goto fail_header_seek;
590 }
591
592 count = sys_read(file, (char __user *)header, buf_len);
593
594 result = count < OS_AREA_SEGMENT_SIZE || verify_header(header)
595 || count < header->db_area_offset * OS_AREA_SEGMENT_SIZE;
596
597 if (result) {
598 pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
599 dump_header(header);
600 goto fail_header;
601 }
602
603 /* Now got a good db offset and some maybe good db data. */
604
605 db = (void*)header + header->db_area_offset * OS_AREA_SEGMENT_SIZE;
606
607 result = db_verify(db);
608
609 if (result) {
610 printk(KERN_NOTICE "%s:%d: Verify of flash database failed, "
611 "formatting.\n", __func__, __LINE__);
612 dump_db(db);
613 os_area_db_init(db);
614 }
615
616 /* Now got good db data. */
617
618 db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff);
619
620 offset = sys_lseek(file, header->db_area_offset * OS_AREA_SEGMENT_SIZE,
621 SEEK_SET);
622
623 if (offset != header->db_area_offset * OS_AREA_SEGMENT_SIZE) {
624 pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__);
625 goto fail_db_seek;
626 }
627
628 count = sys_write(file, (const char __user *)db,
629 sizeof(struct os_area_db));
630
631 if (count < sizeof(struct os_area_db)) {
632 pr_debug("%s:%d sys_write failed\n", __func__, __LINE__);
633 }
634
635fail_db_seek:
636fail_header:
637fail_header_seek:
638 kfree(header);
639fail_malloc:
640 sys_close(file);
641fail_open:
642 return;
643}
644
245/** 645/**
246 * os_area_queue_work_handler - Asynchronous write handler. 646 * os_area_queue_work_handler - Asynchronous write handler.
247 * 647 *
@@ -264,6 +664,12 @@ static void os_area_queue_work_handler(struct work_struct *work)
264 pr_debug("%s:%d of_find_node_by_path failed\n", 664 pr_debug("%s:%d of_find_node_by_path failed\n",
265 __func__, __LINE__); 665 __func__, __LINE__);
266 666
667#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
668 update_flash_db();
669#else
670 printk(KERN_WARNING "%s:%d: No flash rom driver configured.\n",
671 __func__, __LINE__);
672#endif
267 pr_debug(" <- %s:%d\n", __func__, __LINE__); 673 pr_debug(" <- %s:%d\n", __func__, __LINE__);
268} 674}
269 675
@@ -278,11 +684,15 @@ static void os_area_queue_work(void)
278/** 684/**
279 * ps3_os_area_save_params - Copy data from os area mirror to @saved_params. 685 * ps3_os_area_save_params - Copy data from os area mirror to @saved_params.
280 * 686 *
281 * For the convenience of the guest, the HV makes a copy of the os area in 687 * For the convenience of the guest the HV makes a copy of the os area in
282 * flash to a high address in the boot memory region and then puts that RAM 688 * flash to a high address in the boot memory region and then puts that RAM
283 * address and the byte count into the repository for retreval by the guest. 689 * address and the byte count into the repository for retrieval by the guest.
284 * We copy the data we want into a static variable and allow the memory setup 690 * We copy the data we want into a static variable and allow the memory setup
285 * by the HV to be claimed by the lmb manager. 691 * by the HV to be claimed by the lmb manager.
692 *
693 * The os area mirror will not be available to a second stage kernel, and
694 * the header verify will fail. In this case, the saved_params values will
695 * be set from flash memory or the passed in device tree in ps3_os_area_init().
286 */ 696 */
287 697
288void __init ps3_os_area_save_params(void) 698void __init ps3_os_area_save_params(void)
@@ -292,6 +702,7 @@ void __init ps3_os_area_save_params(void)
292 unsigned int size; 702 unsigned int size;
293 struct os_area_header *header; 703 struct os_area_header *header;
294 struct os_area_params *params; 704 struct os_area_params *params;
705 struct os_area_db *db;
295 706
296 pr_debug(" -> %s:%d\n", __func__, __LINE__); 707 pr_debug(" -> %s:%d\n", __func__, __LINE__);
297 708
@@ -311,16 +722,22 @@ void __init ps3_os_area_save_params(void)
311 722
312 if (result) { 723 if (result) {
313 /* Second stage kernels exit here. */ 724 /* Second stage kernels exit here. */
314
315 pr_debug("%s:%d verify_header failed\n", __func__, __LINE__); 725 pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
316 dump_header(header); 726 dump_header(header);
317 return; 727 return;
318 } 728 }
319 729
730 db = (struct os_area_db *)__va(lpar_addr
731 + header->db_area_offset * OS_AREA_SEGMENT_SIZE);
732
320 dump_header(header); 733 dump_header(header);
321 dump_params(params); 734 dump_params(params);
735 dump_db(db);
322 736
323 saved_params.rtc_diff = params->rtc_diff; 737 result = db_verify(db) || db_get_rtc_diff(db, &saved_params.rtc_diff);
738 if (result)
739 saved_params.rtc_diff = params->rtc_diff ? params->rtc_diff
740 : SECONDS_FROM_1970_TO_2000;
324 saved_params.av_multi_out = params->av_multi_out; 741 saved_params.av_multi_out = params->av_multi_out;
325 saved_params.valid = 1; 742 saved_params.valid = 1;
326 743