162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  PS3 flash memory os area.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2006 Sony Computer Entertainment Inc.
662306a36Sopenharmony_ci *  Copyright 2006 Sony Corp.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/workqueue.h>
1262306a36Sopenharmony_ci#include <linux/fs.h>
1362306a36Sopenharmony_ci#include <linux/syscalls.h>
1462306a36Sopenharmony_ci#include <linux/export.h>
1562306a36Sopenharmony_ci#include <linux/ctype.h>
1662306a36Sopenharmony_ci#include <linux/memblock.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "platform.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cienum {
2362306a36Sopenharmony_ci	OS_AREA_SEGMENT_SIZE = 0X200,
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cienum os_area_ldr_format {
2762306a36Sopenharmony_ci	HEADER_LDR_FORMAT_RAW = 0,
2862306a36Sopenharmony_ci	HEADER_LDR_FORMAT_GZIP = 1,
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define OS_AREA_HEADER_MAGIC_NUM "cell_ext_os_area"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/**
3462306a36Sopenharmony_ci * struct os_area_header - os area header segment.
3562306a36Sopenharmony_ci * @magic_num: Always 'cell_ext_os_area'.
3662306a36Sopenharmony_ci * @hdr_version: Header format version number.
3762306a36Sopenharmony_ci * @db_area_offset: Starting segment number of other os database area.
3862306a36Sopenharmony_ci * @ldr_area_offset: Starting segment number of bootloader image area.
3962306a36Sopenharmony_ci * @ldr_format: HEADER_LDR_FORMAT flag.
4062306a36Sopenharmony_ci * @ldr_size: Size of bootloader image in bytes.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * Note that the docs refer to area offsets.  These are offsets in units of
4362306a36Sopenharmony_ci * segments from the start of the os area (top of the header).  These are
4462306a36Sopenharmony_ci * better thought of as segment numbers.  The os area of the os area is
4562306a36Sopenharmony_ci * reserved for the os image.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct os_area_header {
4962306a36Sopenharmony_ci	u8 magic_num[16];
5062306a36Sopenharmony_ci	u32 hdr_version;
5162306a36Sopenharmony_ci	u32 db_area_offset;
5262306a36Sopenharmony_ci	u32 ldr_area_offset;
5362306a36Sopenharmony_ci	u32 _reserved_1;
5462306a36Sopenharmony_ci	u32 ldr_format;
5562306a36Sopenharmony_ci	u32 ldr_size;
5662306a36Sopenharmony_ci	u32 _reserved_2[6];
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cienum os_area_boot_flag {
6062306a36Sopenharmony_ci	PARAM_BOOT_FLAG_GAME_OS = 0,
6162306a36Sopenharmony_ci	PARAM_BOOT_FLAG_OTHER_OS = 1,
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cienum os_area_ctrl_button {
6562306a36Sopenharmony_ci	PARAM_CTRL_BUTTON_O_IS_YES = 0,
6662306a36Sopenharmony_ci	PARAM_CTRL_BUTTON_X_IS_YES = 1,
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/**
7062306a36Sopenharmony_ci * struct os_area_params - os area params segment.
7162306a36Sopenharmony_ci * @boot_flag: User preference of operating system, PARAM_BOOT_FLAG flag.
7262306a36Sopenharmony_ci * @num_params: Number of params in this (params) segment.
7362306a36Sopenharmony_ci * @rtc_diff: Difference in seconds between 1970 and the ps3 rtc value.
7462306a36Sopenharmony_ci * @av_multi_out: User preference of AV output, PARAM_AV_MULTI_OUT flag.
7562306a36Sopenharmony_ci * @ctrl_button: User preference of controller button config, PARAM_CTRL_BUTTON
7662306a36Sopenharmony_ci *	flag.
7762306a36Sopenharmony_ci * @static_ip_addr: User preference of static IP address.
7862306a36Sopenharmony_ci * @network_mask: User preference of static network mask.
7962306a36Sopenharmony_ci * @default_gateway: User preference of static default gateway.
8062306a36Sopenharmony_ci * @dns_primary: User preference of static primary dns server.
8162306a36Sopenharmony_ci * @dns_secondary: User preference of static secondary dns server.
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * The ps3 rtc maintains a read-only value that approximates seconds since
8462306a36Sopenharmony_ci * 2000-01-01 00:00:00 UTC.
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * User preference of zero for static_ip_addr means use dhcp.
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistruct os_area_params {
9062306a36Sopenharmony_ci	u32 boot_flag;
9162306a36Sopenharmony_ci	u32 _reserved_1[3];
9262306a36Sopenharmony_ci	u32 num_params;
9362306a36Sopenharmony_ci	u32 _reserved_2[3];
9462306a36Sopenharmony_ci	/* param 0 */
9562306a36Sopenharmony_ci	s64 rtc_diff;
9662306a36Sopenharmony_ci	u8 av_multi_out;
9762306a36Sopenharmony_ci	u8 ctrl_button;
9862306a36Sopenharmony_ci	u8 _reserved_3[6];
9962306a36Sopenharmony_ci	/* param 1 */
10062306a36Sopenharmony_ci	u8 static_ip_addr[4];
10162306a36Sopenharmony_ci	u8 network_mask[4];
10262306a36Sopenharmony_ci	u8 default_gateway[4];
10362306a36Sopenharmony_ci	u8 _reserved_4[4];
10462306a36Sopenharmony_ci	/* param 2 */
10562306a36Sopenharmony_ci	u8 dns_primary[4];
10662306a36Sopenharmony_ci	u8 dns_secondary[4];
10762306a36Sopenharmony_ci	u8 _reserved_5[8];
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci#define OS_AREA_DB_MAGIC_NUM "-db-"
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/**
11362306a36Sopenharmony_ci * struct os_area_db - Shared flash memory database.
11462306a36Sopenharmony_ci * @magic_num: Always '-db-'.
11562306a36Sopenharmony_ci * @version: os_area_db format version number.
11662306a36Sopenharmony_ci * @index_64: byte offset of the database id index for 64 bit variables.
11762306a36Sopenharmony_ci * @count_64: number of usable 64 bit index entries
11862306a36Sopenharmony_ci * @index_32: byte offset of the database id index for 32 bit variables.
11962306a36Sopenharmony_ci * @count_32: number of usable 32 bit index entries
12062306a36Sopenharmony_ci * @index_16: byte offset of the database id index for 16 bit variables.
12162306a36Sopenharmony_ci * @count_16: number of usable 16 bit index entries
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci * Flash rom storage for exclusive use by guests running in the other os lpar.
12462306a36Sopenharmony_ci * The current system configuration allocates 1K (two segments) for other os
12562306a36Sopenharmony_ci * use.
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistruct os_area_db {
12962306a36Sopenharmony_ci	u8 magic_num[4];
13062306a36Sopenharmony_ci	u16 version;
13162306a36Sopenharmony_ci	u16 _reserved_1;
13262306a36Sopenharmony_ci	u16 index_64;
13362306a36Sopenharmony_ci	u16 count_64;
13462306a36Sopenharmony_ci	u16 index_32;
13562306a36Sopenharmony_ci	u16 count_32;
13662306a36Sopenharmony_ci	u16 index_16;
13762306a36Sopenharmony_ci	u16 count_16;
13862306a36Sopenharmony_ci	u32 _reserved_2;
13962306a36Sopenharmony_ci	u8 _db_data[1000];
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/**
14362306a36Sopenharmony_ci * enum os_area_db_owner - Data owners.
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cienum os_area_db_owner {
14762306a36Sopenharmony_ci	OS_AREA_DB_OWNER_ANY = -1,
14862306a36Sopenharmony_ci	OS_AREA_DB_OWNER_NONE = 0,
14962306a36Sopenharmony_ci	OS_AREA_DB_OWNER_PROTOTYPE = 1,
15062306a36Sopenharmony_ci	OS_AREA_DB_OWNER_LINUX = 2,
15162306a36Sopenharmony_ci	OS_AREA_DB_OWNER_PETITBOOT = 3,
15262306a36Sopenharmony_ci	OS_AREA_DB_OWNER_MAX = 32,
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cienum os_area_db_key {
15662306a36Sopenharmony_ci	OS_AREA_DB_KEY_ANY = -1,
15762306a36Sopenharmony_ci	OS_AREA_DB_KEY_NONE = 0,
15862306a36Sopenharmony_ci	OS_AREA_DB_KEY_RTC_DIFF = 1,
15962306a36Sopenharmony_ci	OS_AREA_DB_KEY_VIDEO_MODE = 2,
16062306a36Sopenharmony_ci	OS_AREA_DB_KEY_MAX = 8,
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistruct os_area_db_id {
16462306a36Sopenharmony_ci	int owner;
16562306a36Sopenharmony_ci	int key;
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic const struct os_area_db_id os_area_db_id_empty = {
16962306a36Sopenharmony_ci	.owner = OS_AREA_DB_OWNER_NONE,
17062306a36Sopenharmony_ci	.key = OS_AREA_DB_KEY_NONE
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic const struct os_area_db_id os_area_db_id_any = {
17462306a36Sopenharmony_ci	.owner = OS_AREA_DB_OWNER_ANY,
17562306a36Sopenharmony_ci	.key = OS_AREA_DB_KEY_ANY
17662306a36Sopenharmony_ci};
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic const struct os_area_db_id os_area_db_id_rtc_diff = {
17962306a36Sopenharmony_ci	.owner = OS_AREA_DB_OWNER_LINUX,
18062306a36Sopenharmony_ci	.key = OS_AREA_DB_KEY_RTC_DIFF
18162306a36Sopenharmony_ci};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci#define SECONDS_FROM_1970_TO_2000 946684800LL
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/**
18662306a36Sopenharmony_ci * struct saved_params - Static working copies of data from the PS3 'os area'.
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * The order of preference we use for the rtc_diff source:
18962306a36Sopenharmony_ci *  1) The database value.
19062306a36Sopenharmony_ci *  2) The game os value.
19162306a36Sopenharmony_ci *  3) The number of seconds from 1970 to 2000.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic struct saved_params {
19562306a36Sopenharmony_ci	unsigned int valid;
19662306a36Sopenharmony_ci	s64 rtc_diff;
19762306a36Sopenharmony_ci	unsigned int av_multi_out;
19862306a36Sopenharmony_ci} saved_params;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic struct property property_rtc_diff = {
20162306a36Sopenharmony_ci	.name = "linux,rtc_diff",
20262306a36Sopenharmony_ci	.length = sizeof(saved_params.rtc_diff),
20362306a36Sopenharmony_ci	.value = &saved_params.rtc_diff,
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic struct property property_av_multi_out = {
20762306a36Sopenharmony_ci	.name = "linux,av_multi_out",
20862306a36Sopenharmony_ci	.length = sizeof(saved_params.av_multi_out),
20962306a36Sopenharmony_ci	.value = &saved_params.av_multi_out,
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic DEFINE_MUTEX(os_area_flash_mutex);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic const struct ps3_os_area_flash_ops *os_area_flash_ops;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_civoid ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	mutex_lock(&os_area_flash_mutex);
22062306a36Sopenharmony_ci	os_area_flash_ops = ops;
22162306a36Sopenharmony_ci	mutex_unlock(&os_area_flash_mutex);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_os_area_flash_register);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic ssize_t os_area_flash_read(void *buf, size_t count, loff_t pos)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	ssize_t res = -ENODEV;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	mutex_lock(&os_area_flash_mutex);
23062306a36Sopenharmony_ci	if (os_area_flash_ops)
23162306a36Sopenharmony_ci		res = os_area_flash_ops->read(buf, count, pos);
23262306a36Sopenharmony_ci	mutex_unlock(&os_area_flash_mutex);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return res;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic ssize_t os_area_flash_write(const void *buf, size_t count, loff_t pos)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	ssize_t res = -ENODEV;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	mutex_lock(&os_area_flash_mutex);
24262306a36Sopenharmony_ci	if (os_area_flash_ops)
24362306a36Sopenharmony_ci		res = os_area_flash_ops->write(buf, count, pos);
24462306a36Sopenharmony_ci	mutex_unlock(&os_area_flash_mutex);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return res;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/**
25162306a36Sopenharmony_ci * os_area_set_property - Add or overwrite a saved_params value to the device tree.
25262306a36Sopenharmony_ci *
25362306a36Sopenharmony_ci * Overwrites an existing property.
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic void os_area_set_property(struct device_node *node,
25762306a36Sopenharmony_ci	struct property *prop)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	int result;
26062306a36Sopenharmony_ci	struct property *tmp = of_find_property(node, prop->name, NULL);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (tmp) {
26362306a36Sopenharmony_ci		pr_debug("%s:%d found %s\n", __func__, __LINE__, prop->name);
26462306a36Sopenharmony_ci		of_remove_property(node, tmp);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	result = of_add_property(node, prop);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (result)
27062306a36Sopenharmony_ci		pr_debug("%s:%d of_set_property failed\n", __func__,
27162306a36Sopenharmony_ci			__LINE__);
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/**
27562306a36Sopenharmony_ci * os_area_get_property - Get a saved_params value from the device tree.
27662306a36Sopenharmony_ci *
27762306a36Sopenharmony_ci */
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic void __init os_area_get_property(struct device_node *node,
28062306a36Sopenharmony_ci	struct property *prop)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	const struct property *tmp = of_find_property(node, prop->name, NULL);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (tmp) {
28562306a36Sopenharmony_ci		BUG_ON(prop->length != tmp->length);
28662306a36Sopenharmony_ci		memcpy(prop->value, tmp->value, prop->length);
28762306a36Sopenharmony_ci	} else
28862306a36Sopenharmony_ci		pr_debug("%s:%d not found %s\n", __func__, __LINE__,
28962306a36Sopenharmony_ci			prop->name);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void dump_field(char *s, const u8 *field, int size_of_field)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci#if defined(DEBUG)
29562306a36Sopenharmony_ci	int i;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	for (i = 0; i < size_of_field; i++)
29862306a36Sopenharmony_ci		s[i] = isprint(field[i]) ? field[i] : '.';
29962306a36Sopenharmony_ci	s[i] = 0;
30062306a36Sopenharmony_ci#endif
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci#define dump_header(_a) _dump_header(_a, __func__, __LINE__)
30462306a36Sopenharmony_cistatic void _dump_header(const struct os_area_header *h, const char *func,
30562306a36Sopenharmony_ci	int line)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	char str[sizeof(h->magic_num) + 1];
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	dump_field(str, h->magic_num, sizeof(h->magic_num));
31062306a36Sopenharmony_ci	pr_debug("%s:%d: h.magic_num:       '%s'\n", func, line,
31162306a36Sopenharmony_ci		str);
31262306a36Sopenharmony_ci	pr_debug("%s:%d: h.hdr_version:     %u\n", func, line,
31362306a36Sopenharmony_ci		h->hdr_version);
31462306a36Sopenharmony_ci	pr_debug("%s:%d: h.db_area_offset:  %u\n", func, line,
31562306a36Sopenharmony_ci		h->db_area_offset);
31662306a36Sopenharmony_ci	pr_debug("%s:%d: h.ldr_area_offset: %u\n", func, line,
31762306a36Sopenharmony_ci		h->ldr_area_offset);
31862306a36Sopenharmony_ci	pr_debug("%s:%d: h.ldr_format:      %u\n", func, line,
31962306a36Sopenharmony_ci		h->ldr_format);
32062306a36Sopenharmony_ci	pr_debug("%s:%d: h.ldr_size:        %xh\n", func, line,
32162306a36Sopenharmony_ci		h->ldr_size);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci#define dump_params(_a) _dump_params(_a, __func__, __LINE__)
32562306a36Sopenharmony_cistatic void _dump_params(const struct os_area_params *p, const char *func,
32662306a36Sopenharmony_ci	int line)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	pr_debug("%s:%d: p.boot_flag:       %u\n", func, line, p->boot_flag);
32962306a36Sopenharmony_ci	pr_debug("%s:%d: p.num_params:      %u\n", func, line, p->num_params);
33062306a36Sopenharmony_ci	pr_debug("%s:%d: p.rtc_diff         %lld\n", func, line, p->rtc_diff);
33162306a36Sopenharmony_ci	pr_debug("%s:%d: p.av_multi_out     %u\n", func, line, p->av_multi_out);
33262306a36Sopenharmony_ci	pr_debug("%s:%d: p.ctrl_button:     %u\n", func, line, p->ctrl_button);
33362306a36Sopenharmony_ci	pr_debug("%s:%d: p.static_ip_addr:  %u.%u.%u.%u\n", func, line,
33462306a36Sopenharmony_ci		p->static_ip_addr[0], p->static_ip_addr[1],
33562306a36Sopenharmony_ci		p->static_ip_addr[2], p->static_ip_addr[3]);
33662306a36Sopenharmony_ci	pr_debug("%s:%d: p.network_mask:    %u.%u.%u.%u\n", func, line,
33762306a36Sopenharmony_ci		p->network_mask[0], p->network_mask[1],
33862306a36Sopenharmony_ci		p->network_mask[2], p->network_mask[3]);
33962306a36Sopenharmony_ci	pr_debug("%s:%d: p.default_gateway: %u.%u.%u.%u\n", func, line,
34062306a36Sopenharmony_ci		p->default_gateway[0], p->default_gateway[1],
34162306a36Sopenharmony_ci		p->default_gateway[2], p->default_gateway[3]);
34262306a36Sopenharmony_ci	pr_debug("%s:%d: p.dns_primary:     %u.%u.%u.%u\n", func, line,
34362306a36Sopenharmony_ci		p->dns_primary[0], p->dns_primary[1],
34462306a36Sopenharmony_ci		p->dns_primary[2], p->dns_primary[3]);
34562306a36Sopenharmony_ci	pr_debug("%s:%d: p.dns_secondary:   %u.%u.%u.%u\n", func, line,
34662306a36Sopenharmony_ci		p->dns_secondary[0], p->dns_secondary[1],
34762306a36Sopenharmony_ci		p->dns_secondary[2], p->dns_secondary[3]);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int verify_header(const struct os_area_header *header)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	if (memcmp(header->magic_num, OS_AREA_HEADER_MAGIC_NUM,
35362306a36Sopenharmony_ci		sizeof(header->magic_num))) {
35462306a36Sopenharmony_ci		pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
35562306a36Sopenharmony_ci		return -1;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (header->hdr_version < 1) {
35962306a36Sopenharmony_ci		pr_debug("%s:%d hdr_version failed\n", __func__, __LINE__);
36062306a36Sopenharmony_ci		return -1;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (header->db_area_offset > header->ldr_area_offset) {
36462306a36Sopenharmony_ci		pr_debug("%s:%d offsets failed\n", __func__, __LINE__);
36562306a36Sopenharmony_ci		return -1;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic int db_verify(const struct os_area_db *db)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	if (memcmp(db->magic_num, OS_AREA_DB_MAGIC_NUM,
37462306a36Sopenharmony_ci		sizeof(db->magic_num))) {
37562306a36Sopenharmony_ci		pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
37662306a36Sopenharmony_ci		return -EINVAL;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (db->version != 1) {
38062306a36Sopenharmony_ci		pr_debug("%s:%d version failed\n", __func__, __LINE__);
38162306a36Sopenharmony_ci		return -EINVAL;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return 0;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistruct db_index {
38862306a36Sopenharmony_ci       uint8_t owner:5;
38962306a36Sopenharmony_ci       uint8_t key:3;
39062306a36Sopenharmony_ci};
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistruct db_iterator {
39362306a36Sopenharmony_ci	const struct os_area_db *db;
39462306a36Sopenharmony_ci	struct os_area_db_id match_id;
39562306a36Sopenharmony_ci	struct db_index *idx;
39662306a36Sopenharmony_ci	struct db_index *last_idx;
39762306a36Sopenharmony_ci	union {
39862306a36Sopenharmony_ci		uint64_t *value_64;
39962306a36Sopenharmony_ci		uint32_t *value_32;
40062306a36Sopenharmony_ci		uint16_t *value_16;
40162306a36Sopenharmony_ci	};
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic unsigned int db_align_up(unsigned int val, unsigned int size)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	return (val + (size - 1)) & (~(size - 1));
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/**
41062306a36Sopenharmony_ci * db_for_each_64 - Iterator for 64 bit entries.
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * A NULL value for id can be used to match all entries.
41362306a36Sopenharmony_ci * OS_AREA_DB_OWNER_ANY and OS_AREA_DB_KEY_ANY can be used to match all.
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic int db_for_each_64(const struct os_area_db *db,
41762306a36Sopenharmony_ci	const struct os_area_db_id *match_id, struct db_iterator *i)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_cinext:
42062306a36Sopenharmony_ci	if (!i->db) {
42162306a36Sopenharmony_ci		i->db = db;
42262306a36Sopenharmony_ci		i->match_id = match_id ? *match_id : os_area_db_id_any;
42362306a36Sopenharmony_ci		i->idx = (void *)db + db->index_64;
42462306a36Sopenharmony_ci		i->last_idx = i->idx + db->count_64;
42562306a36Sopenharmony_ci		i->value_64 = (void *)db + db->index_64
42662306a36Sopenharmony_ci			+ db_align_up(db->count_64, 8);
42762306a36Sopenharmony_ci	} else {
42862306a36Sopenharmony_ci		i->idx++;
42962306a36Sopenharmony_ci		i->value_64++;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (i->idx >= i->last_idx) {
43362306a36Sopenharmony_ci		pr_debug("%s:%d: reached end\n", __func__, __LINE__);
43462306a36Sopenharmony_ci		return 0;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (i->match_id.owner != OS_AREA_DB_OWNER_ANY
43862306a36Sopenharmony_ci		&& i->match_id.owner != (int)i->idx->owner)
43962306a36Sopenharmony_ci		goto next;
44062306a36Sopenharmony_ci	if (i->match_id.key != OS_AREA_DB_KEY_ANY
44162306a36Sopenharmony_ci		&& i->match_id.key != (int)i->idx->key)
44262306a36Sopenharmony_ci		goto next;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return 1;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int db_delete_64(struct os_area_db *db, const struct os_area_db_id *id)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct db_iterator i;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	for (i.db = NULL; db_for_each_64(db, id, &i); ) {
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__,
45462306a36Sopenharmony_ci			i.idx->owner, i.idx->key,
45562306a36Sopenharmony_ci			(unsigned long long)*i.value_64);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		i.idx->owner = 0;
45862306a36Sopenharmony_ci		i.idx->key = 0;
45962306a36Sopenharmony_ci		*i.value_64 = 0;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int db_set_64(struct os_area_db *db, const struct os_area_db_id *id,
46562306a36Sopenharmony_ci	uint64_t value)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct db_iterator i;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	pr_debug("%s:%d: (%d:%d) <= %llxh\n", __func__, __LINE__,
47062306a36Sopenharmony_ci		id->owner, id->key, (unsigned long long)value);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (!id->owner || id->owner == OS_AREA_DB_OWNER_ANY
47362306a36Sopenharmony_ci		|| id->key == OS_AREA_DB_KEY_ANY) {
47462306a36Sopenharmony_ci		pr_debug("%s:%d: bad id: (%d:%d)\n", __func__,
47562306a36Sopenharmony_ci			__LINE__, id->owner, id->key);
47662306a36Sopenharmony_ci		return -1;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	db_delete_64(db, id);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	i.db = NULL;
48262306a36Sopenharmony_ci	if (db_for_each_64(db, &os_area_db_id_empty, &i)) {
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__,
48562306a36Sopenharmony_ci			i.idx->owner, i.idx->key,
48662306a36Sopenharmony_ci			(unsigned long long)*i.value_64);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		i.idx->owner = id->owner;
48962306a36Sopenharmony_ci		i.idx->key = id->key;
49062306a36Sopenharmony_ci		*i.value_64 = value;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		pr_debug("%s:%d: set (%d:%d) <= %llxh\n", __func__, __LINE__,
49362306a36Sopenharmony_ci			i.idx->owner, i.idx->key,
49462306a36Sopenharmony_ci			(unsigned long long)*i.value_64);
49562306a36Sopenharmony_ci		return 0;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci	pr_debug("%s:%d: database full.\n",
49862306a36Sopenharmony_ci		__func__, __LINE__);
49962306a36Sopenharmony_ci	return -1;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int __init db_get_64(const struct os_area_db *db,
50362306a36Sopenharmony_ci	const struct os_area_db_id *id, uint64_t *value)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	struct db_iterator i;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	i.db = NULL;
50862306a36Sopenharmony_ci	if (db_for_each_64(db, id, &i)) {
50962306a36Sopenharmony_ci		*value = *i.value_64;
51062306a36Sopenharmony_ci		pr_debug("%s:%d: found %lld\n", __func__, __LINE__,
51162306a36Sopenharmony_ci				(long long int)*i.value_64);
51262306a36Sopenharmony_ci		return 0;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	pr_debug("%s:%d: not found\n", __func__, __LINE__);
51562306a36Sopenharmony_ci	return -1;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int __init db_get_rtc_diff(const struct os_area_db *db, int64_t *rtc_diff)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	return db_get_64(db, &os_area_db_id_rtc_diff, (uint64_t*)rtc_diff);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci#define dump_db(a) _dump_db(a, __func__, __LINE__)
52462306a36Sopenharmony_cistatic void _dump_db(const struct os_area_db *db, const char *func,
52562306a36Sopenharmony_ci	int line)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	char str[sizeof(db->magic_num) + 1];
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	dump_field(str, db->magic_num, sizeof(db->magic_num));
53062306a36Sopenharmony_ci	pr_debug("%s:%d: db.magic_num:      '%s'\n", func, line,
53162306a36Sopenharmony_ci		str);
53262306a36Sopenharmony_ci	pr_debug("%s:%d: db.version:         %u\n", func, line,
53362306a36Sopenharmony_ci		db->version);
53462306a36Sopenharmony_ci	pr_debug("%s:%d: db.index_64:        %u\n", func, line,
53562306a36Sopenharmony_ci		db->index_64);
53662306a36Sopenharmony_ci	pr_debug("%s:%d: db.count_64:        %u\n", func, line,
53762306a36Sopenharmony_ci		db->count_64);
53862306a36Sopenharmony_ci	pr_debug("%s:%d: db.index_32:        %u\n", func, line,
53962306a36Sopenharmony_ci		db->index_32);
54062306a36Sopenharmony_ci	pr_debug("%s:%d: db.count_32:        %u\n", func, line,
54162306a36Sopenharmony_ci		db->count_32);
54262306a36Sopenharmony_ci	pr_debug("%s:%d: db.index_16:        %u\n", func, line,
54362306a36Sopenharmony_ci		db->index_16);
54462306a36Sopenharmony_ci	pr_debug("%s:%d: db.count_16:        %u\n", func, line,
54562306a36Sopenharmony_ci		db->count_16);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic void os_area_db_init(struct os_area_db *db)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	enum {
55162306a36Sopenharmony_ci		HEADER_SIZE = offsetof(struct os_area_db, _db_data),
55262306a36Sopenharmony_ci		INDEX_64_COUNT = 64,
55362306a36Sopenharmony_ci		VALUES_64_COUNT = 57,
55462306a36Sopenharmony_ci		INDEX_32_COUNT = 64,
55562306a36Sopenharmony_ci		VALUES_32_COUNT = 57,
55662306a36Sopenharmony_ci		INDEX_16_COUNT = 64,
55762306a36Sopenharmony_ci		VALUES_16_COUNT = 57,
55862306a36Sopenharmony_ci	};
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	memset(db, 0, sizeof(struct os_area_db));
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	memcpy(db->magic_num, OS_AREA_DB_MAGIC_NUM, sizeof(db->magic_num));
56362306a36Sopenharmony_ci	db->version = 1;
56462306a36Sopenharmony_ci	db->index_64 = HEADER_SIZE;
56562306a36Sopenharmony_ci	db->count_64 = VALUES_64_COUNT;
56662306a36Sopenharmony_ci	db->index_32 = HEADER_SIZE
56762306a36Sopenharmony_ci			+ INDEX_64_COUNT * sizeof(struct db_index)
56862306a36Sopenharmony_ci			+ VALUES_64_COUNT * sizeof(u64);
56962306a36Sopenharmony_ci	db->count_32 = VALUES_32_COUNT;
57062306a36Sopenharmony_ci	db->index_16 = HEADER_SIZE
57162306a36Sopenharmony_ci			+ INDEX_64_COUNT * sizeof(struct db_index)
57262306a36Sopenharmony_ci			+ VALUES_64_COUNT * sizeof(u64)
57362306a36Sopenharmony_ci			+ INDEX_32_COUNT * sizeof(struct db_index)
57462306a36Sopenharmony_ci			+ VALUES_32_COUNT * sizeof(u32);
57562306a36Sopenharmony_ci	db->count_16 = VALUES_16_COUNT;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* Rules to check db layout. */
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct db_index) != 1);
58062306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct os_area_db) != 2 * OS_AREA_SEGMENT_SIZE);
58162306a36Sopenharmony_ci	BUILD_BUG_ON(INDEX_64_COUNT & 0x7);
58262306a36Sopenharmony_ci	BUILD_BUG_ON(VALUES_64_COUNT > INDEX_64_COUNT);
58362306a36Sopenharmony_ci	BUILD_BUG_ON(INDEX_32_COUNT & 0x7);
58462306a36Sopenharmony_ci	BUILD_BUG_ON(VALUES_32_COUNT > INDEX_32_COUNT);
58562306a36Sopenharmony_ci	BUILD_BUG_ON(INDEX_16_COUNT & 0x7);
58662306a36Sopenharmony_ci	BUILD_BUG_ON(VALUES_16_COUNT > INDEX_16_COUNT);
58762306a36Sopenharmony_ci	BUILD_BUG_ON(HEADER_SIZE
58862306a36Sopenharmony_ci			+ INDEX_64_COUNT * sizeof(struct db_index)
58962306a36Sopenharmony_ci			+ VALUES_64_COUNT * sizeof(u64)
59062306a36Sopenharmony_ci			+ INDEX_32_COUNT * sizeof(struct db_index)
59162306a36Sopenharmony_ci			+ VALUES_32_COUNT * sizeof(u32)
59262306a36Sopenharmony_ci			+ INDEX_16_COUNT * sizeof(struct db_index)
59362306a36Sopenharmony_ci			+ VALUES_16_COUNT * sizeof(u16)
59462306a36Sopenharmony_ci			> sizeof(struct os_area_db));
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/**
59862306a36Sopenharmony_ci * update_flash_db - Helper for os_area_queue_work_handler.
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci */
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic int update_flash_db(void)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE;
60562306a36Sopenharmony_ci	struct os_area_header *header;
60662306a36Sopenharmony_ci	ssize_t count;
60762306a36Sopenharmony_ci	int error;
60862306a36Sopenharmony_ci	loff_t pos;
60962306a36Sopenharmony_ci	struct os_area_db* db;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* Read in header and db from flash. */
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	header = kmalloc(buf_len, GFP_KERNEL);
61462306a36Sopenharmony_ci	if (!header)
61562306a36Sopenharmony_ci		return -ENOMEM;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	count = os_area_flash_read(header, buf_len, 0);
61862306a36Sopenharmony_ci	if (count < 0) {
61962306a36Sopenharmony_ci		pr_debug("%s: os_area_flash_read failed %zd\n", __func__,
62062306a36Sopenharmony_ci			 count);
62162306a36Sopenharmony_ci		error = count;
62262306a36Sopenharmony_ci		goto fail;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	pos = header->db_area_offset * OS_AREA_SEGMENT_SIZE;
62662306a36Sopenharmony_ci	if (count < OS_AREA_SEGMENT_SIZE || verify_header(header) ||
62762306a36Sopenharmony_ci	    count < pos) {
62862306a36Sopenharmony_ci		pr_debug("%s: verify_header failed\n", __func__);
62962306a36Sopenharmony_ci		dump_header(header);
63062306a36Sopenharmony_ci		error = -EINVAL;
63162306a36Sopenharmony_ci		goto fail;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* Now got a good db offset and some maybe good db data. */
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	db = (void *)header + pos;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	error = db_verify(db);
63962306a36Sopenharmony_ci	if (error) {
64062306a36Sopenharmony_ci		pr_notice("%s: Verify of flash database failed, formatting.\n",
64162306a36Sopenharmony_ci			  __func__);
64262306a36Sopenharmony_ci		dump_db(db);
64362306a36Sopenharmony_ci		os_area_db_init(db);
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	/* Now got good db data. */
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	count = os_area_flash_write(db, sizeof(struct os_area_db), pos);
65162306a36Sopenharmony_ci	if (count < 0 || count < sizeof(struct os_area_db)) {
65262306a36Sopenharmony_ci		pr_debug("%s: os_area_flash_write failed %zd\n", __func__,
65362306a36Sopenharmony_ci			 count);
65462306a36Sopenharmony_ci		error = count < 0 ? count : -EIO;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cifail:
65862306a36Sopenharmony_ci	kfree(header);
65962306a36Sopenharmony_ci	return error;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci/**
66362306a36Sopenharmony_ci * os_area_queue_work_handler - Asynchronous write handler.
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * An asynchronous write for flash memory and the device tree.  Do not
66662306a36Sopenharmony_ci * call directly, use os_area_queue_work().
66762306a36Sopenharmony_ci */
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic void os_area_queue_work_handler(struct work_struct *work)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct device_node *node;
67262306a36Sopenharmony_ci	int error;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	pr_debug(" -> %s:%d\n", __func__, __LINE__);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	node = of_find_node_by_path("/");
67762306a36Sopenharmony_ci	if (node) {
67862306a36Sopenharmony_ci		os_area_set_property(node, &property_rtc_diff);
67962306a36Sopenharmony_ci		of_node_put(node);
68062306a36Sopenharmony_ci	} else
68162306a36Sopenharmony_ci		pr_debug("%s:%d of_find_node_by_path failed\n",
68262306a36Sopenharmony_ci			__func__, __LINE__);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	error = update_flash_db();
68562306a36Sopenharmony_ci	if (error)
68662306a36Sopenharmony_ci		pr_warn("%s: Could not update FLASH ROM\n", __func__);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	pr_debug(" <- %s:%d\n", __func__, __LINE__);
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic void os_area_queue_work(void)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	static DECLARE_WORK(q, os_area_queue_work_handler);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	wmb();
69662306a36Sopenharmony_ci	schedule_work(&q);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/**
70062306a36Sopenharmony_ci * ps3_os_area_save_params - Copy data from os area mirror to @saved_params.
70162306a36Sopenharmony_ci *
70262306a36Sopenharmony_ci * For the convenience of the guest the HV makes a copy of the os area in
70362306a36Sopenharmony_ci * flash to a high address in the boot memory region and then puts that RAM
70462306a36Sopenharmony_ci * address and the byte count into the repository for retrieval by the guest.
70562306a36Sopenharmony_ci * We copy the data we want into a static variable and allow the memory setup
70662306a36Sopenharmony_ci * by the HV to be claimed by the memblock manager.
70762306a36Sopenharmony_ci *
70862306a36Sopenharmony_ci * The os area mirror will not be available to a second stage kernel, and
70962306a36Sopenharmony_ci * the header verify will fail.  In this case, the saved_params values will
71062306a36Sopenharmony_ci * be set from flash memory or the passed in device tree in ps3_os_area_init().
71162306a36Sopenharmony_ci */
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_civoid __init ps3_os_area_save_params(void)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	int result;
71662306a36Sopenharmony_ci	u64 lpar_addr;
71762306a36Sopenharmony_ci	unsigned int size;
71862306a36Sopenharmony_ci	struct os_area_header *header;
71962306a36Sopenharmony_ci	struct os_area_params *params;
72062306a36Sopenharmony_ci	struct os_area_db *db;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	pr_debug(" -> %s:%d\n", __func__, __LINE__);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	result = ps3_repository_read_boot_dat_info(&lpar_addr, &size);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (result) {
72762306a36Sopenharmony_ci		pr_debug("%s:%d ps3_repository_read_boot_dat_info failed\n",
72862306a36Sopenharmony_ci			__func__, __LINE__);
72962306a36Sopenharmony_ci		return;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	header = (struct os_area_header *)__va(lpar_addr);
73362306a36Sopenharmony_ci	params = (struct os_area_params *)__va(lpar_addr
73462306a36Sopenharmony_ci		+ OS_AREA_SEGMENT_SIZE);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	result = verify_header(header);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (result) {
73962306a36Sopenharmony_ci		/* Second stage kernels exit here. */
74062306a36Sopenharmony_ci		pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
74162306a36Sopenharmony_ci		dump_header(header);
74262306a36Sopenharmony_ci		return;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	db = (struct os_area_db *)__va(lpar_addr
74662306a36Sopenharmony_ci		+ header->db_area_offset * OS_AREA_SEGMENT_SIZE);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	dump_header(header);
74962306a36Sopenharmony_ci	dump_params(params);
75062306a36Sopenharmony_ci	dump_db(db);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	result = db_verify(db) || db_get_rtc_diff(db, &saved_params.rtc_diff);
75362306a36Sopenharmony_ci	if (result)
75462306a36Sopenharmony_ci		saved_params.rtc_diff = params->rtc_diff ? params->rtc_diff
75562306a36Sopenharmony_ci			: SECONDS_FROM_1970_TO_2000;
75662306a36Sopenharmony_ci	saved_params.av_multi_out = params->av_multi_out;
75762306a36Sopenharmony_ci	saved_params.valid = 1;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	memset(header, 0, sizeof(*header));
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	pr_debug(" <- %s:%d\n", __func__, __LINE__);
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci/**
76562306a36Sopenharmony_ci * ps3_os_area_init - Setup os area device tree properties as needed.
76662306a36Sopenharmony_ci */
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_civoid __init ps3_os_area_init(void)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	struct device_node *node;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	pr_debug(" -> %s:%d\n", __func__, __LINE__);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	node = of_find_node_by_path("/");
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (!saved_params.valid && node) {
77762306a36Sopenharmony_ci		/* Second stage kernels should have a dt entry. */
77862306a36Sopenharmony_ci		os_area_get_property(node, &property_rtc_diff);
77962306a36Sopenharmony_ci		os_area_get_property(node, &property_av_multi_out);
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if(!saved_params.rtc_diff)
78362306a36Sopenharmony_ci		saved_params.rtc_diff = SECONDS_FROM_1970_TO_2000;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (node) {
78662306a36Sopenharmony_ci		os_area_set_property(node, &property_rtc_diff);
78762306a36Sopenharmony_ci		os_area_set_property(node, &property_av_multi_out);
78862306a36Sopenharmony_ci		of_node_put(node);
78962306a36Sopenharmony_ci	} else
79062306a36Sopenharmony_ci		pr_debug("%s:%d of_find_node_by_path failed\n",
79162306a36Sopenharmony_ci			__func__, __LINE__);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	pr_debug(" <- %s:%d\n", __func__, __LINE__);
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci/**
79762306a36Sopenharmony_ci * ps3_os_area_get_rtc_diff - Returns the rtc diff value.
79862306a36Sopenharmony_ci */
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ciu64 ps3_os_area_get_rtc_diff(void)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	return saved_params.rtc_diff;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_os_area_get_rtc_diff);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/**
80762306a36Sopenharmony_ci * ps3_os_area_set_rtc_diff - Set the rtc diff value.
80862306a36Sopenharmony_ci *
80962306a36Sopenharmony_ci * An asynchronous write is needed to support writing updates from
81062306a36Sopenharmony_ci * the timer interrupt context.
81162306a36Sopenharmony_ci */
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_civoid ps3_os_area_set_rtc_diff(u64 rtc_diff)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	if (saved_params.rtc_diff != rtc_diff) {
81662306a36Sopenharmony_ci		saved_params.rtc_diff = rtc_diff;
81762306a36Sopenharmony_ci		os_area_queue_work();
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_os_area_set_rtc_diff);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/**
82362306a36Sopenharmony_ci * ps3_os_area_get_av_multi_out - Returns the default video mode.
82462306a36Sopenharmony_ci */
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cienum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci    return saved_params.av_multi_out;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_os_area_get_av_multi_out);
831