Booting petitboot from VFLASH

From PS3 Developer wiki
Jump to: navigation, search

Source: graf_chokolo @ http://ps3wiki.lan.st/index.php/Booting_petitboot_from_VFLASH


How it was done in 3.15 Firmware[edit]

  • Linux bootloader otheros.bld is a compressed version of petitboot or kboot
  • otheros.self stores the Linux bootloader on the 5th region of VFLASH
  • The 5th region of VFLASH is of size 0x2000 sectors, each sector is of size 0x200, which makes 0x400000 bytes
  • The 5th region of VFLASH starts at absolute sector offset 0x77E00 on VFLASH storage device
  • So Linux bootloader cannot be larger than 0x400000 bytes
  • In LPAR 1 the 5th region of VFLASH is accessible through device file /dev/rflash_lx
  • Linux System Manager (HV process 9) loads the Linux bootloader stored on /dev/rflash_lx into LPAR 2 memory and when LPAR 2 boots, the Linux bootloader gets control of the system and loads the Linux kernel from HDD

How things are in 3.41 Firmware[edit]

  • The good news is that the 5th region of VFLASH is still there in 3.41 and even in 3.55 PS3 firmware. Even better, it's accessible in LPAR 1 through /dev/rflash_lx !!!
  • And PS3 Slim has it also !!!
  • Why didn't SONY remove it you will ask. I'm very sure that SONY's HV developer team still uses Linux internally !!! I will bet on it :-)
  • But SONY removed Linux System Manager from 3.41 firmware, so we cannot boot Linux bootloader from VFLASH without adding new code to HV. HV cannot boot the Linux bootloader anymore on its own.
  • The second problem is that the Linux bootloader uncompressed is very large and it won't fit into the default 5th region of VFLASH that is of size 0x400000 bytes.
  • We have to store the Linux bootloader uncompressed on VFLASH
  • Before we can do it, we have to resize the 5th VFLASH region. And nothing is simpler if you know HV like the back of your hand :-)
  • SONY could remove /dev/rflash_lx in future HVs but i know now HV so good that it will be no problem to me to add this device again :-)

Creating and Resizing of VFLASH region[edit]

  • So we have to resize the 5th VFLASH region in order to be able to store an uncompressed petitiboot which is quite large
  • On my PS3 3.41 i set the size of the 5th VFLASH region to 0x800000 bytes, it's enough to store there petitboot with nice GUI and many features. But we could use larger size without any problems, because VFLASH is large enough.
  • GameOS is able to create/delete storage device regions (see HV calls lv1_undocumented_function_250 and lv1_undocumented_function_251)
  • GameOS is able to set ACLs for a storage device region (see HV calls lv1_undocumented_function_252 and lv1_undocumented_function_253)
  • The 5th VFLASH storage device region can be resized with 2 different methods: the first one is dirty and requires HV access rights, the second one is simpler and requires only GameOS rights
  • I tested both methods, both worked very well
  • The first method is only for advanced HV hackers :-) I reversed the storage device class and know now where which data is stored, e.g. region start sector and region sector count. I patched the sector count of the 5th VFLASH region in memory, then i forced HV to update the partition table of VFLASH and HV stored the new region size on VFLASH. Then i rebooted HV and i got a new larger VFLASH region :-) Another possibility is to write partition table on your own.
  • Don't try the first method if you are not familiar with HV :-) Consider you warned guys :-)
  • The second method is safer. It requires only GameOS rights. First i deleted the 5th VFLASH region, it's safe because GameOS doesn't use this region at all. Then i created it again with larger size. That simple :-)

Installing petitboot on VFLASH region[edit]

  • petitboot binary image can be stored on the 5th VFLASH region with GameOS rights
  • petitboot should be stored on VFLASH with encryption disabled !!!

Patching GameOS System Manager[edit]

  • GameOS System Manager is in HV process 9
  • Secure LPAR Loader is in HV process 3
  • GameOS System Manager allocates an initial LPAR 2 memory for GameOS, then it sends a SS request (see my HV page) to Secure LPAR Loader to load lv2_kernel.self
  • We have to patch the initial LPAR 2 memory size because GameOS uses a smaller value but Linux kernel requires more memory

Patching Secure LPAR Loader (SLL)[edit]

  • Secure LPAR Loader (SLL) is in HV process 3
  • SLL loads lv2_kernel.self from FLASH, passes it to lv2ldr which decrypts it
  • I replaced the lv2_kernel.self loader with my OtherOS bootloader loader

otheros_booloader_loader.S[edit]

Here is my OtherOS bootloader loader from VFLASH.

  • All function addresses are valid for 3.41 firmware only !!!
  • The loader maps LPAR memory region
  • Reads OtherOS bootloader from VFLASH into mapped LPAR memory region
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

.set STACK_SIZE,							0x100
.set STACK_LR,								(STACK_SIZE + 0x10)
.set STACK_R25,								(STACK_SIZE - 1 * 0x8)
.set STACK_R26,								(STACK_SIZE - 2 * 0x8)
.set STACK_R27,								(STACK_SIZE - 3 * 0x8)
.set STACK_R28,								(STACK_SIZE - 4 * 0x8)
.set STACK_R29,								(STACK_SIZE - 5 * 0x8)
.set STACK_R30,								(STACK_SIZE - 6 * 0x8)
.set STACK_R31,								(STACK_SIZE - 7 * 0x8)
.set STACK_LPAR_MEM,						        (STACK_SIZE - 8 * 0x8)

.set SUB_SLL_LOAD_LV2,						        0x80000E44		/* sll_load_lv2 */
.set SUB_MMAP_LPAR_MEM,						        0x800026B4
.set SUB_OPEN,								0x80013D40		/* syscall 0x7 */
.set SUB_CLOSE,								0x80013D7C		/* syscall 0x8 */
.set SUB_READ,								0x80013DB8		/* syscall 0x9 */
.set SUB_MUNMAP,							0x80013EB8		/* syscall 0x19 */
.set SUB_BSET,								0x80000278
.set SUB_BCOPY,								0x80000254

#define BL(sub_addr)						        \
	li %r30, 0;							\
	oris %r30, %r30, (sub_addr >> 16);		                \
	ori %r30, %r30, (sub_addr & 0xFFFF);	                        \
	mtctr %r30;							\
	bctrl

.set CACHE_LINE_SIZE,						        0x80

.set FD_ERROR,								0x10
.set HEADER_ERROR,							0x14

#define LWZ_DATA(reg, data_addr)			                lwz reg, data_addr(%r31)
#define LD_DATA(reg, data_addr)				                ld reg, data_addr(%r31)
#define LD_DATA_PTR(reg, data_addr)			                addi reg, %r31, data_addr - sll_load_lv2

/*
 * %r3 - path
 * %r4 - laid
 * %r5 - lpar id
 *
 * %r25 - volatile
 * %r26 - OtherOS bootloader load address
 * %r27 - return value
 * %r28 - size of /dev/rflash_lx
 * %r29 - file descriptor of /dev/rflash_lx
 * %r30 - reserved for function calls
 * %r31 - reserved for data accesses
 */
.globl sll_load_lv2
sll_load_lv2:

	stdu %r1, -STACK_SIZE(%r1)
	mflr %r0
	std %r0, STACK_LR(%r1)
	std %r25, STACK_R25(%r1)
	std %r26, STACK_R26(%r1)
	std %r27, STACK_R27(%r1)
	std %r28, STACK_R28(%r1)
	std %r29, STACK_R29(%r1)
	std %r30, STACK_R30(%r1)
	std %r31, STACK_R31(%r1)

	bl 1f

1:

	mflr %r31
	addi %r31, %r31, (sll_load_lv2 - 1b)

	/* map lpar memory */

	mr %r3, %r5
	li %r4, 0
	LD_DATA(%r26, bootloader_load_addr)
	LD_DATA(%r28, dev_rflash_lx_size)
	add %r5, %r26, %r28
	addi %r6, %r1, STACK_LPAR_MEM
	BL(SUB_MMAP_LPAR_MEM)
	cmpwi %cr7, %r3, 0
	mr %r27, %r3
	bne %cr7, done

	/* zero out lpar memory */

	ld %r3, STACK_LPAR_MEM(%r1)
	li %r4, 0
	mr %r5, %r28
	BL(SUB_BSET)

	/* open /dev/rflash_lx where OtherOS bootloader is stored */

	LD_DATA_PTR(%r3, dev_rflash_lx)
	li %r4, 0
	li %r27, FD_ERROR
	BL(SUB_OPEN)
	cmpwi %cr7, %r3, 0
	mr %r29, %r3
	blt %cr7, bad1

	/* read header */

	extsw %r3, %r29
	ld %r4, STACK_LPAR_MEM(%r1)
	LD_DATA(%r25, header_size)
	mr %r5, %r25
	li %r27, FD_ERROR
	BL(SUB_READ)
	cmpd %cr7, %r3, %r25
	bne %cr7, bad2

	/* check header magic */

	li %r27, HEADER_ERROR

	LD_DATA(%r3, header_magic)
	ld %r5, STACK_LPAR_MEM(%r1)
	ld %r4, 0(%r5)
	cmpd %cr7, %r3, %r4
	bne %cr7, bad2

	/* read OtherOS bootloader size from header */

	ld %r25, 8(%r5)
	cmpd %cr7, %r25, %r28
	bgt %cr7, bad2

	/* read OtherOS bootloader to lpar memory */

	extsw %r3, %r29
	ld %r4, STACK_LPAR_MEM(%r1)
	mr %r5, %r25
	li %r27, FD_ERROR
	BL(SUB_READ)
	cmpd %cr7, %r3, %r25
	bne %cr7, bad2

	/* flush data cache */

	li %r9, 0
	b 2f

1:

	ld %r0, STACK_LPAR_MEM(%r1)
	add %r0, %r9, %r0
	dcbst %r0, %r0
	addi %r9, %r9, CACHE_LINE_SIZE

2:

	cmpd %cr7, %r9, %r28
	blt %cr7, 1b

	sync

	/* flush code cache */

	li %r9, 0
	b 2f

1:

	ld %r0, STACK_LPAR_MEM(%r1)
	add %r0, %r9, %r0
	icbi %r0, %r0
	addi %r9, %r9, CACHE_LINE_SIZE

2:

	cmpd %cr7, %r9, %r28
	blt %cr7, 1b

	isync

success:

	li %r27, 0

bad2:

	/* close /dev/rflash_lx */

	extsw %r3, %r29
	BL(SUB_CLOSE)

bad1:

	/* unmap lpar memory */

	ld %r3, STACK_LPAR_MEM(%r1)
	add %r4, %r26, %r28
	BL(SUB_MUNMAP)

done:

	rldicl %r3, %r27, 0, 32

	ld %r0, STACK_LR(%r1)
	ld %r25, STACK_R25(%r1)
	ld %r26, STACK_R26(%r1)
	ld %r27, STACK_R27(%r1)
	ld %r28, STACK_R28(%r1)
	ld %r29, STACK_R29(%r1)
	ld %r30, STACK_R30(%r1)
	ld %r31, STACK_R31(%r1)
	addi %r1, %r1, STACK_SIZE
	mtlr %r0
	blr

/* read-only data */

data_start:

bootloader_load_addr:
	.quad 0x0000000000000000

header_size:
	.quad (2 * 0x200) /* 2 sectors of size 0x200 */

header_magic:
	.quad 0x0FACE0FFDEADBABE

dev_rflash_lx_size:
	.quad (0x4000 * 0x200) /* 0x4000 sectors of size 0x200 */

dev_rflash_lx:
	.asciz "/dev/rflash_lx"

data_end: