blob: 4780847206fbf5a76ad88e576506d7fa70de25ad [file] [log] [blame]
/*
* PS3 flash memory os area.
*
* Copyright (C) 2006 Sony Computer Entertainment Inc.
* Copyright 2006 Sony Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "ps3-flash.h"
#if defined(DEBUG)
#define DBG os_area_log
#else
static inline int __attribute__ ((format (printf, 1, 2))) DBG(
__attribute__((unused)) const char *fmt, ...) {return 0;}
#endif
static FILE *osa_log;
/**
* os_area_set_log_stream - Set the message logging stream.
* @stream: A file stream to log messages to.
*
* The default log stream is stdout. Returns the old
* stream.
*/
FILE *os_area_set_log_stream(FILE *log)
{
FILE *old = osa_log ? osa_log : stdout;
assert(log);
fflush(log);
fflush(old);
osa_log = log;
return old;
}
int os_area_log(const char *fmt, ...)
{
int ret;
va_list ap;
FILE *f;
f = osa_log ? osa_log : stdout;
va_start(ap, fmt);
ret = vfprintf(f, fmt, ap);
va_end(ap);
fflush(f);
return ret;
}
/**
* os_area_header_read - Read the firmware defined header.
*/
int os_area_header_read(struct os_area_header *h, FILE *dev)
{
int result;
size_t bytes;
if (!dev) {
DBG("%s:%d: bad stream\n", __func__, __LINE__);
return -1;
}
result = fseek(dev, 0, SEEK_SET);
if (result) {
os_area_log("%s: seek error: os_area_header: %s\n", __func__,
strerror(errno));
return result;
}
bytes = fread(h, 1, sizeof(struct os_area_header), dev);
if (bytes < sizeof(struct os_area_header)) {
os_area_log("%s: read error: os_area_header: %s\n", __func__,
strerror(errno));
return -1;
}
result = os_area_header_verify(h);
if (result) {
os_area_log("%s: invalid os_area_header\n", __func__);
return -1;
}
return 0;
}
/**
* os_area_header_write - Write the firmware defined header.
*/
int os_area_header_write(const struct os_area_header *h, FILE *dev)
{
int result;
size_t bytes;
if (!dev) {
DBG("%s:%d: bad stream\n", __func__, __LINE__);
return -1;
}
result = os_area_header_verify(h);
if (result) {
os_area_log("%s: invalid os_area_header\n", __func__);
return -1;
}
result = fseek(dev, 0, SEEK_SET);
if (result) {
os_area_log("%s: seek error: os_area_header: %s\n", __func__,
strerror(errno));
return result;
}
bytes = fwrite(h, 1, sizeof(struct os_area_header), dev);
if (bytes < sizeof(struct os_area_header)) {
os_area_log("%s: fwrite error: os_area_header: %s\n", __func__,
strerror(errno));
return -1;
}
return 0;
}
/**
* os_area_params_read - Read the firmware defined params.
*/
int os_area_params_read(struct os_area_params *p, FILE *dev)
{
int result;
size_t bytes;
if (!dev) {
DBG("%s:%d: bad stream\n", __func__, __LINE__);
return -1;
}
result = fseek(dev, OS_AREA_SEGMENT_SIZE, SEEK_SET);
if (result) {
os_area_log("%s: seek error: os_area_params: %s\n", __func__,
strerror(errno));
return result;
}
bytes = fread(p, 1, sizeof(struct os_area_params), dev);
if (bytes < sizeof(struct os_area_params)) {
os_area_log("%s: read error: os_area_params: %s\n", __func__,
strerror(errno));
return -1;
}
return 0;
}
/**
* os_area_params_write - Write the firmware defined params.
*/
int os_area_params_write(const struct os_area_params *p, FILE *dev)
{
int result;
size_t bytes;
if (!dev) {
DBG("%s:%d: bad stream\n", __func__, __LINE__);
return -1;
}
result = fseek(dev, OS_AREA_SEGMENT_SIZE, SEEK_SET);
if (result) {
os_area_log("%s: seek error: os_area_params: %s\n", __func__,
strerror(errno));
return result;
}
bytes = fwrite(p, 1, sizeof(struct os_area_params), dev);
if (bytes < sizeof(struct os_area_params)) {
os_area_log("%s: fwrite error: os_area_params: %s\n", __func__,
strerror(errno));
return -1;
}
return 0;
}
/**
* os_area_image_write - Write the other os boot image.
*/
int os_area_image_write(FILE *image, struct os_area_header *h, FILE *dev)
{
int result;
size_t flash_size;
size_t image_size;
size_t count;
void* buf;
result = fseek(dev, 0, SEEK_END);
if (result) {
os_area_log("%s: fseek dev failed: %s\n", __func__,
strerror(errno));
return -1;
}
flash_size = ftell(dev) - h->ldr_area_offset * OS_AREA_SEGMENT_SIZE;
DBG("%s:%d: flash_size %lxh\n", __func__, __LINE__,
(unsigned long)flash_size);
result = fseek(image, 0, SEEK_END);
if (result) {
os_area_log("%s: fseek image failed: %s\n", __func__,
strerror(errno));
return -1;
}
image_size = ftell(image);
DBG("%s:%d: image_size %lxh\n", __func__, __LINE__,
(unsigned long)image_size);
if (image_size > flash_size) {
os_area_log("%s: image too big (%lxh / %lxh bytes).\n",
__func__, (unsigned long)image_size,
(unsigned long)flash_size);
return -1;
}
buf = malloc(image_size);
if (!buf) {
DBG("%s: malloc failed: %s\n", __func__, strerror(errno));
return -1;
}
rewind(image);
count = fread(buf, image_size, 1, image);
if (count != 1) {
os_area_log("%s: fread failed: %s\n", __func__,
strerror(errno));
return -1;
}
result = fseek(dev, h->ldr_area_offset * OS_AREA_SEGMENT_SIZE,
SEEK_SET);
if (result) {
os_area_log("%s: seek error ldr_area_offset: %s\n", __func__,
strerror(errno));
return -1;
}
count = fwrite(buf, image_size, 1, dev);
if (count != 1) {
os_area_log("%s: fwrite error image: %s\n", __func__,
strerror(errno));
return -1;
}
result = os_area_set_ldr_size(h, dev, image_size);
if (result) {
os_area_log("%s: os_area_set_ldr_size failed: %s\n", __func__,
strerror(errno));
return -1;
}
os_area_log("wrote image to flash (%lxh / %lxh bytes).\n",
(unsigned long)image_size, (unsigned long)flash_size);
free(buf);
return result;
}
/**
* os_area_header_verify - Verify the firmware defined header.
*/
int os_area_header_verify(const struct os_area_header *h)
{
if (memcmp(h->magic_num, "cell_ext_os_area", 16)) {
os_area_log("%s: magic_num failed\n", __func__);
return -1;
}
if (h->hdr_version < 1) {
os_area_log("%s: hdr_version failed\n", __func__);
return -1;
}
if (h->db_area_offset > h->ldr_area_offset) {
os_area_log("%s: offsets failed\n", __func__);
return -1;
}
return 0;
}
/**
* os_area_set_ldr_format - Set the data format of the other os boot image.
* @value: Specifies the format of the os boot image, one of enum ldr_format.
*/
int os_area_set_ldr_format(struct os_area_header *h, FILE *dev,
enum os_area_ldr_format value)
{
int result;
DBG("%s:%d: %u(%xh) -> %u(%xh)\n", __func__, __LINE__,
(unsigned int)h->ldr_format, (unsigned int)h->ldr_format,
value, value);
if (h->ldr_format == (uint32_t)value)
return 0;
h->ldr_format = (uint32_t)value ? HEADER_LDR_FORMAT_GZIP
: HEADER_LDR_FORMAT_RAW; /* for now just flatten to gzip */
os_area_header_write(h, dev);
result = os_area_header_read(h, dev);
if (result)
os_area_log("%s: os_area_set_ldr_format failed\n", __func__);
return result;
}
/**
* os_area_set_ldr_size - Set the size in bytes of the other os boot image.
* @value: Size in bytes.
*/
int os_area_set_ldr_size(struct os_area_header *h, FILE *dev,
unsigned int value)
{
int result;
DBG("%s:%d: %u(%xh) -> %u(%xh)\n", __func__, __LINE__,
(unsigned int)h->ldr_size, (unsigned int)h->ldr_size,
value, value);
if (h->ldr_size == (uint32_t)value)
return 0;
h->ldr_size = (uint32_t)value;
os_area_header_write(h, dev);
result = os_area_header_read(h, dev);
if (result)
os_area_log("%s: os_area_set_ldr_format failed\n", __func__);
return result;
}
/**
* os_area_set_boot_flag - Set the system boot flag.
* @value: Boot flag value, one of enum os_area_boot_flag.
*/
int os_area_set_boot_flag(struct os_area_params *p, FILE *dev,
enum os_area_boot_flag value)
{
int result;
DBG("%s:%d: %u(%xh) -> %u(%xh)\n", __func__, __LINE__,
(unsigned int)p->boot_flag, (unsigned int)p->boot_flag,
value, value);
if (p->boot_flag == (uint32_t)value)
return 0;
p->boot_flag = (uint32_t)value ? PARAM_BOOT_FLAG_OTHER_OS
: PARAM_BOOT_FLAG_GAME_OS; /* for now just flatten to otheros */
os_area_params_write(p, dev);
result = os_area_params_read(p, dev);
if (result)
os_area_log("%s: os_area_set_boot_flag failed\n", __func__);
return result;
}
void os_area_header_dump(const struct os_area_header *h, const char *header,
int id)
{
os_area_log("%s:%d: h.magic_num: '%s'\n",
header, id,
h->magic_num);
os_area_log("%s:%d: h.hdr_version: %u\n",
header, id,
h->hdr_version);
os_area_log("%s:%d: h.db_area_offset: %u\n",
header, id,
h->db_area_offset);
os_area_log("%s:%d: h.ldr_area_offset: %u\n",
header, id,
h->ldr_area_offset);
os_area_log("%s:%d: h.ldr_format: %u (%s)\n",
header, id,
h->ldr_format, (h->ldr_format ? "gzip" : "raw"));
os_area_log("%s:%d: h.ldr_size: %u (%xh)\n",
header, id,
h->ldr_size, h->ldr_size);
}
void os_area_params_dump(const struct os_area_params *p, const char *header,
int id)
{
os_area_log("%s:%d: p.boot_flag: %u (%s)\n",
header, id,
p->boot_flag, (p->boot_flag ? "other-os" : "game-os"));
os_area_log("%s:%d: p.num_params: %u\n",
header, id,
p->num_params);
os_area_log("%s:%d: p.rtc_diff %lld\n",
header, id,
(long long)p->rtc_diff);
os_area_log("%s:%d: p.av_multi_out %u\n",
header, id,
p->av_multi_out);
os_area_log("%s:%d: p.ctrl_button: %u\n",
header, id,
p->ctrl_button);
os_area_log("%s:%d: p.static_ip_addr: %u.%u.%u.%u\n",
header, id,
p->static_ip_addr[0], p->static_ip_addr[1],
p->static_ip_addr[2], p->static_ip_addr[3]);
os_area_log("%s:%d: p.network_mask: %u.%u.%u.%u\n",
header, id,
p->network_mask[0], p->network_mask[1],
p->network_mask[2], p->network_mask[3]);
os_area_log("%s:%d: p.default_gateway: %u.%u.%u.%u\n",
header, id,
p->default_gateway[0], p->default_gateway[1],
p->default_gateway[2], p->default_gateway[3]);
os_area_log("%s:%d: p.dns_primary: %u.%u.%u.%u\n",
header, id,
p->dns_primary[0], p->dns_primary[1],
p->dns_primary[2], p->dns_primary[3]);
os_area_log("%s:%d: p.dns_secondary: %u.%u.%u.%u\n",
header, id,
p->dns_secondary[0], p->dns_secondary[1],
p->dns_secondary[2], p->dns_secondary[3]);
}