Skip to content

Commit

Permalink
MIPS: PS2: ROM: sysfs
Browse files Browse the repository at this point in the history
Signed-off-by: Fredrik Noring <noring@nocrew.org>
  • Loading branch information
frno7 committed May 27, 2019
1 parent c4a3b3f commit 0333ae4
Show file tree
Hide file tree
Showing 2 changed files with 286 additions and 0 deletions.
1 change: 1 addition & 0 deletions arch/mips/ps2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ obj-y += intc-irq.o
obj-y += irq.o
obj-y += memory.o
obj-y += rom.o
obj-m += rom-sysfs.o
obj-y += scmd.o
obj-y += time.o
285 changes: 285 additions & 0 deletions arch/mips/ps2/rom-sysfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PlayStation 2 read-only memory (ROM) sysfs
*
* Copyright (C) 2019 Fredrik Noring
*
* FIXME: Is /sys/rom the proper placement?
*/

///
// DOC:
//
// ROM0 and ROM1 contain simple file systems that can be inspected with this
// sysfs module. For example, listing all files of ROM0::
//
// # cat /sys/rom/rom0/file/*/name | column
// RESET VBLANK RMRESET MCSERV XSIFCMD
// ROMDIR IOMAN OSDVER PADMAN XCDVDMAN
// EXTINFO MODLOAD - CDVDMAN XCDVDFSV
// ROMVER ROMDRV IOPBOOT CDVDFSV XFILEIO
// SBIN ADDDRV OSDCNF FILEIO XSIO2MAN
// LOGO STDIO - CLEARSPU XMTAPMAN
// IOPBTCONF SIFMAN TBIN UDNL XMCMAN
// ...
//
// Viewing for example the contents of the ROM file ROMVER using /dev/mem::
//
// # grep -l ROMVER /sys/rom/rom0/file/*/name
// /sys/rom/rom0/file/3/name
// # cd /sys/rom/rom0/file/3
// # dd if=/dev/mem bs=$(cat size) iflag=skip_bytes
// skip=$(( $(cat data) )) count=1 status=none
// 0170EC20030227
//
// For convenience, the ROMVER file is also available directly in sysfs::
//
// # ls /sys/rom/rom0/version
// date number region type
// # cat /sys/rom/rom0/version/*
// 2003-02-27
// 0x0170
// Europe
// CEX
//

#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/uaccess.h>

#include <asm/page.h>
#include <asm/uaccess.h>

#include <asm/mach-ps2/rom.h>

static int rom0_sysfs(struct kobject *romn_kobj);

/* FIXME */
static const struct {
const char *name;
const struct rom_dir *dir;
int (*func)(struct kobject *romn_kobj);
} rom_dirs[] = {
{ "rom0", &rom0_dir, rom0_sysfs },
{ "rom1", &rom1_dir, NULL },
};

static struct rom_dir rom_dir_from_kobj(const struct kobject *kobj)
{
size_t i;

for (i = 0; i < ARRAY_SIZE(rom_dirs); i++)
if (strcmp(rom_dirs[i].name, kobj->parent->name) == 0)
return *rom_dirs[i].dir;

pr_err("%s: ROM dir for \"%s\" does not exist\n", __func__,
kobj->parent->name);

return (struct rom_dir) { };
}

static struct rom_file rom_file_from_kobj(const struct kobject *kobj)
{
const size_t rom_id = simple_strtoull(kobj->name, NULL, 0);
const struct rom_dir dir = rom_dir_from_kobj(kobj->parent);
struct rom_file file;
size_t id = 0;

rom_for_each_file(file, dir)
if (id++ == rom_id)
return file;

pr_err("%s: ROM id %zu for \"%s\" does not exist\n", __func__,
rom_id, kobj->parent->name);

return (struct rom_file) { .name = "<undefined>" };
}

static ssize_t rom_version_number_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "0x%04x\n", rom_version().number);
}

static ssize_t rom_version_region_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n",
rom_region_name(rom_version().region));
}

static ssize_t rom_version_type_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n",
rom_type_name(rom_version().type));
}

static ssize_t rom_version_date_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
const struct rom_ver v = rom_version();

return scnprintf(buf, PAGE_SIZE, "%04d-%02d-%02d\n",
v.date.year, v.date.month, v.date.day);
}

static ssize_t rom_file_name_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n",
rom_file_from_kobj(kobj).name);
}

static ssize_t rom_file_size_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%zu\n",
rom_file_from_kobj(kobj).size);
}

static ssize_t rom_file_data_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "0x%lx\n",
virt_to_phys(rom_file_from_kobj(kobj).data));
}

#define DEFINE_ROM_FIELD_ATTR(prefix, field) \
static struct kobj_attribute rom_attribute_##prefix##_##field = \
__ATTR(field, S_IRUGO, rom_##prefix##_##field##_show, NULL)

DEFINE_ROM_FIELD_ATTR(version, number);
DEFINE_ROM_FIELD_ATTR(version, region);
DEFINE_ROM_FIELD_ATTR(version, type);
DEFINE_ROM_FIELD_ATTR(version, date);

static struct attribute *rom_version_attributes[] = {
&rom_attribute_version_number.attr,
&rom_attribute_version_region.attr,
&rom_attribute_version_type.attr,
&rom_attribute_version_date.attr,
NULL
};

static struct attribute_group rom_version_attribute_group = {
.attrs = rom_version_attributes
};

DEFINE_ROM_FIELD_ATTR(file, name);
DEFINE_ROM_FIELD_ATTR(file, size);
DEFINE_ROM_FIELD_ATTR(file, data);

static struct attribute *rom_file_attributes[] = {
&rom_attribute_file_name.attr,
&rom_attribute_file_size.attr,
&rom_attribute_file_data.attr,
NULL
};

static struct attribute_group rom_file_attribute_group = {
.attrs = rom_file_attributes
};

static int rom0_sysfs(struct kobject *rom0_kobj)
{
struct kobject *version_kobj;

version_kobj = kobject_create_and_add("version", rom0_kobj);
if (!version_kobj)
return -ENOMEM;

return sysfs_create_group(version_kobj, &rom_version_attribute_group);
}

static int __init rom_init_file(struct kobject *file_kobj, size_t index,
const struct rom_file file)
{
struct kobject *index_kobj;
char index_string[20];
int err;

scnprintf(index_string, sizeof(index_string), "%zu", index);

index_kobj = kobject_create_and_add(index_string, file_kobj);
if (!index_kobj)
return -ENOMEM;

return sysfs_create_group(index_kobj, &rom_file_attribute_group);
}

static int __init rom_init_dir(struct kobject *romn_kobj,
const struct rom_dir dir)
{
struct kobject *file_kobj = kobject_create_and_add("file", romn_kobj);
struct rom_file file;
size_t i = 0;

if (!file_kobj)
return -ENOMEM;

rom_for_each_file(file, dir) {
int err = rom_init_file(file_kobj, i++, file);

if (err)
return err;
}

return 0;
}

static int __init rom_init_rom(struct kobject *rom_kobj,
const char *name, const struct rom_dir dir,
int (*func)(struct kobject *romn_kobj))
{
struct kobject *romn_kobj = kobject_create_and_add(name, rom_kobj);
int err;

if (!romn_kobj)
return -ENOMEM;

err = rom_init_dir(romn_kobj, dir);
if (!err && func)
err = func(romn_kobj);

return err;
}

static struct kobject *rom_kobj;

static int __init rom_sysfs_init(void)
{
size_t i;

rom_kobj = kobject_create_and_add("rom", NULL);
if (!rom_kobj)
return -ENOMEM;

for (i = 0; i < ARRAY_SIZE(rom_dirs); i++) {
int err = rom_init_rom(rom_kobj, rom_dirs[i].name,
*rom_dirs[i].dir, rom_dirs[i].func);

if (err) {
kobject_del(rom_kobj);
return err;
}
}

return 0;
}

static void __exit rom_sysfs_exit(void)
{
kobject_del(rom_kobj);
}

module_init(rom_sysfs_init);
module_exit(rom_sysfs_exit);

MODULE_DESCRIPTION("PlayStation 2 read-only memory (ROM) sysfs");
MODULE_AUTHOR("Fredrik Noring");
MODULE_LICENSE("GPL");

0 comments on commit 0333ae4

Please sign in to comment.