Adapt changes from b9s/next
This commit is contained in:
parent
53209b9be0
commit
dd21a3930d
2
Makefile
2
Makefile
@ -23,7 +23,7 @@ dir_out := out
|
|||||||
|
|
||||||
ASFLAGS := -mcpu=arm946e-s
|
ASFLAGS := -mcpu=arm946e-s
|
||||||
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||||
LDFLAGS := -nostartfiles
|
LDFLAGS := -nostartfiles -Wl,--nmagic
|
||||||
|
|
||||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||||
|
@ -13,8 +13,8 @@ dir_build := build
|
|||||||
dir_out := ../$(dir_build)
|
dir_out := ../$(dir_build)
|
||||||
|
|
||||||
ASFLAGS := -mcpu=mpcore
|
ASFLAGS := -mcpu=mpcore
|
||||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||||
LDFLAGS := -nostdlib -Wl,--nmagic
|
LDFLAGS := -nostartfiles -Wl,--nmagic
|
||||||
|
|
||||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||||
|
@ -4,11 +4,12 @@ OUTPUT_ARCH(arm)
|
|||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
. = 0x1FFFE000;
|
. = 0x1FF80000;
|
||||||
|
|
||||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||||
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
||||||
|
|
||||||
|
__stack_top__ = 0x1FFFF000;
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,10 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
static volatile Arm11Operation *operation = (volatile Arm11Operation *)0x1FFFFFF0;
|
void prepareForFirmlaunch(void);
|
||||||
|
extern u32 prepareForFirmlaunchSize;
|
||||||
|
|
||||||
|
extern volatile Arm11Operation operation;
|
||||||
|
|
||||||
static void initScreensSequence(u32 brightnessLevel)
|
static void initScreensSequence(u32 brightnessLevel)
|
||||||
{
|
{
|
||||||
@ -160,23 +163,16 @@ static void deinitScreens(void)
|
|||||||
*(vu32 *)0x10202014 = 0;
|
*(vu32 *)0x10202014 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prepareForFirmlaunch(void)
|
|
||||||
{
|
|
||||||
*ARM11_CORE0_MAILBOX_ENTRYPOINT = 0;
|
|
||||||
while(*ARM11_CORE0_MAILBOX_ENTRYPOINT == 0);
|
|
||||||
((void (*)(void))*ARM11_CORE0_MAILBOX_ENTRYPOINT)();
|
|
||||||
}
|
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
*operation = NO_ARM11_OPERATION;
|
operation = ARM11_READY;
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
switch(*operation)
|
switch(operation)
|
||||||
{
|
{
|
||||||
case NO_ARM11_OPERATION:
|
case ARM11_READY:
|
||||||
break;
|
continue;
|
||||||
case INIT_SCREENS_SEQUENCE:
|
case INIT_SCREENS_SEQUENCE:
|
||||||
initScreensSequence(*(vu32 *)ARM11_PARAMETERS_ADDRESS);
|
initScreensSequence(*(vu32 *)ARM11_PARAMETERS_ADDRESS);
|
||||||
break;
|
break;
|
||||||
@ -196,12 +192,13 @@ void main(void)
|
|||||||
deinitScreens();
|
deinitScreens();
|
||||||
break;
|
break;
|
||||||
case PREPARE_ARM11_FOR_FIRMLAUNCH:
|
case PREPARE_ARM11_FOR_FIRMLAUNCH:
|
||||||
memcpy((void *)0x1FFFFC00, (void *)prepareForFirmlaunch, 0x2C);
|
memcpy((void *)0x1FFFFC00, (void *)prepareForFirmlaunch, prepareForFirmlaunchSize);
|
||||||
*operation = NO_ARM11_OPERATION;
|
*(vu32 *)0x1FFFFFFC = 0;
|
||||||
|
operation = ARM11_READY;
|
||||||
((void (*)(void))0x1FFFFC00)();
|
((void (*)(void))0x1FFFFC00)();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
*operation = NO_ARM11_OPERATION;
|
operation = ARM11_READY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,35 @@
|
|||||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||||
@ Notices displayed by works containing it.
|
@ Notices displayed by works containing it.
|
||||||
|
|
||||||
.section .text.start
|
.section .text.start
|
||||||
.align 4
|
.align 4
|
||||||
.global _start
|
.global _start
|
||||||
|
.type _start, %function
|
||||||
_start:
|
_start:
|
||||||
|
b start
|
||||||
|
|
||||||
|
.global operation
|
||||||
|
operation:
|
||||||
|
.word 0
|
||||||
|
|
||||||
|
start:
|
||||||
cpsid aif
|
cpsid aif
|
||||||
ldr sp, =0x1FFFE000
|
ldr sp, =__stack_top__
|
||||||
b main
|
b main
|
||||||
|
|
||||||
|
.global prepareForFirmlaunch
|
||||||
|
.type prepareForFirmlaunch, %function
|
||||||
|
prepareForFirmlaunch:
|
||||||
|
mov r0, #0x20000000
|
||||||
|
|
||||||
|
_wait_for_core0_entrypoint_loop:
|
||||||
|
ldr r1, [r0, #-4] @ check if core0's entrypoint is 0
|
||||||
|
cmp r1, #0
|
||||||
|
beq _wait_for_core0_entrypoint_loop
|
||||||
|
|
||||||
|
bx r1 @ jump to core0's entrypoint
|
||||||
|
prepareForFirmlaunchEnd:
|
||||||
|
|
||||||
|
.global prepareForFirmlaunchSize
|
||||||
|
prepareForFirmlaunchSize: .word prepareForFirmlaunchEnd - prepareForFirmlaunch
|
||||||
|
@ -40,8 +40,7 @@ typedef volatile u64 vu64;
|
|||||||
#define SCREEN_HEIGHT 240
|
#define SCREEN_HEIGHT 240
|
||||||
#define SCREEN_TOP_FBSIZE (3 * SCREEN_TOP_WIDTH * SCREEN_HEIGHT)
|
#define SCREEN_TOP_FBSIZE (3 * SCREEN_TOP_WIDTH * SCREEN_HEIGHT)
|
||||||
#define SCREEN_BOTTOM_FBSIZE (3 * SCREEN_BOTTOM_WIDTH * SCREEN_HEIGHT)
|
#define SCREEN_BOTTOM_FBSIZE (3 * SCREEN_BOTTOM_WIDTH * SCREEN_HEIGHT)
|
||||||
#define ARM11_CORE0_MAILBOX_ENTRYPOINT ((vu32 *)0x1FFFFFFC)
|
#define ARM11_PARAMETERS_ADDRESS 0x1FFFF000
|
||||||
#define ARM11_PARAMETERS_ADDRESS 0x1FFFC000
|
|
||||||
|
|
||||||
struct fb {
|
struct fb {
|
||||||
u8 *top_left;
|
u8 *top_left;
|
||||||
@ -51,12 +50,12 @@ struct fb {
|
|||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
NO_ARM11_OPERATION = 0,
|
INIT_SCREENS_SEQUENCE = 0,
|
||||||
INIT_SCREENS_SEQUENCE,
|
|
||||||
SETUP_FRAMEBUFFERS,
|
SETUP_FRAMEBUFFERS,
|
||||||
CLEAR_SCREENS,
|
CLEAR_SCREENS,
|
||||||
SWAP_FRAMEBUFFERS,
|
SWAP_FRAMEBUFFERS,
|
||||||
UPDATE_BRIGHTNESS,
|
UPDATE_BRIGHTNESS,
|
||||||
DEINIT_SCREENS,
|
DEINIT_SCREENS,
|
||||||
PREPARE_ARM11_FOR_FIRMLAUNCH,
|
PREPARE_ARM11_FOR_FIRMLAUNCH,
|
||||||
|
ARM11_READY,
|
||||||
} Arm11Operation;
|
} Arm11Operation;
|
||||||
|
@ -13,8 +13,8 @@ dir_build := build
|
|||||||
dir_out := ../$(dir_build)
|
dir_out := ../$(dir_build)
|
||||||
|
|
||||||
ASFLAGS := -mcpu=arm946e-s
|
ASFLAGS := -mcpu=arm946e-s
|
||||||
CFLAGS := -Wall -Wextra -mthumb $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
CFLAGS := -Wall -Wextra -marm $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||||
LDFLAGS := -nostdlib
|
LDFLAGS := -nostartfiles -Wl,--nmagic
|
||||||
|
|
||||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||||
|
@ -24,20 +24,15 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
|
||||||
|
void disableMpuAndJumpToEntrypoints(int argc, char **argv, void *arm11Entry, void *arm9Entry);
|
||||||
|
|
||||||
void launchFirm(Firm *firm, int argc, char **argv)
|
void launchFirm(Firm *firm, int argc, char **argv)
|
||||||
{
|
{
|
||||||
//Copy FIRM sections to respective memory locations
|
//Copy FIRM sections to respective memory locations
|
||||||
for(u32 sectionNum = 0; sectionNum < 4 && firm->section[sectionNum].size != 0; sectionNum++)
|
for(u32 sectionNum = 0; sectionNum < 4 && firm->section[sectionNum].size != 0; sectionNum++)
|
||||||
memcpy(firm->section[sectionNum].address, (u8 *)firm + firm->section[sectionNum].offset, firm->section[sectionNum].size);
|
memcpy(firm->section[sectionNum].address, (u8 *)firm + firm->section[sectionNum].offset, firm->section[sectionNum].size);
|
||||||
|
|
||||||
//Ensure that all memory transfers have completed and that the caches have been flushed
|
disableMpuAndJumpToEntrypoints(argc, argv, firm->arm9Entry, firm->arm11Entry);
|
||||||
flushCaches();
|
|
||||||
|
|
||||||
//Set ARM11 entrypoint
|
|
||||||
*(vu32 *)0x1FFFFFFC = (u32)firm->arm11Entry;
|
|
||||||
|
|
||||||
//Jump to ARM9 entrypoint. Also give it additional arguments it can dismiss
|
|
||||||
((void (*)(int, char**, u32))firm->arm9Entry)(argc, argv, 0x0000BEEF);
|
|
||||||
|
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -24,17 +24,25 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "firm.h"
|
#include "firm.h"
|
||||||
|
|
||||||
void main(int argc __attribute__((unused)), char **argv)
|
void main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
Firm *firm = (Firm *)0x24000000;
|
Firm *firm = (Firm *)0x20001000;
|
||||||
|
struct fb fbs[2];
|
||||||
char absPath[24 + 255];
|
char absPath[24 + 255];
|
||||||
|
|
||||||
|
if(argc == 2)
|
||||||
|
{
|
||||||
|
struct fb *fbsrc = (struct fb *)argv[1];
|
||||||
|
fbs[0] = fbsrc[0];
|
||||||
|
fbs[1] = fbsrc[1];
|
||||||
|
}
|
||||||
|
|
||||||
u32 i;
|
u32 i;
|
||||||
for(i = 0; i < sizeof(absPath) - 1 && argv[0][i] != 0; i++)
|
for(i = 0; i < sizeof(absPath) - 1 && argv[0][i] != 0; i++)
|
||||||
absPath[i] = argv[0][i];
|
absPath[i] = argv[0][i];
|
||||||
absPath[i] = 0;
|
absPath[i] = 0;
|
||||||
|
|
||||||
char *argvPassed[1] = {absPath};
|
char *argvPassed[2] = {absPath, (char *)&fbs};
|
||||||
|
|
||||||
launchFirm(firm, 1, argvPassed);
|
launchFirm(firm, argc, argvPassed);
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,40 @@
|
|||||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||||
@ Notices displayed by works containing it.
|
@ Notices displayed by works containing it.
|
||||||
|
|
||||||
|
.arm
|
||||||
|
|
||||||
.section .text.start
|
.section .text.start
|
||||||
.align 4
|
.align 4
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
ldr sp, =0x27ffe000
|
ldr sp, =0x27ffe000
|
||||||
b main
|
b main
|
||||||
|
|
||||||
|
.text
|
||||||
|
.balign 4
|
||||||
|
.global disableMpuAndJumpToEntrypoints
|
||||||
|
.type disableMpuAndJumpToEntrypoints, %function
|
||||||
|
disableMpuAndJumpToEntrypoints:
|
||||||
|
mov r4, r0
|
||||||
|
mov r5, r1
|
||||||
|
mov r6, r2
|
||||||
|
mov r7, r3
|
||||||
|
|
||||||
|
bl flushCaches
|
||||||
|
|
||||||
|
@ Disable caches / MPU
|
||||||
|
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
||||||
|
bic r0, #(1<<12) @ - instruction cache disable
|
||||||
|
bic r0, #(1<<2) @ - data cache disable
|
||||||
|
bic r0, #(1<<0) @ - mpu disable
|
||||||
|
mcr p15, 0, r0, c1, c0, 0 @ write control register
|
||||||
|
|
||||||
|
@ Set the ARM11 entrypoint
|
||||||
|
mov r0, #0x20000000
|
||||||
|
str r7, [r0, #-4]
|
||||||
|
|
||||||
|
@ Jump to the ARM9 entrypoint
|
||||||
|
mov r0, r4
|
||||||
|
mov r1, r5
|
||||||
|
ldr r2, =0xBEEF
|
||||||
|
bx r6
|
||||||
|
@ -32,4 +32,10 @@ typedef uint64_t u64;
|
|||||||
typedef volatile u8 vu8;
|
typedef volatile u8 vu8;
|
||||||
typedef volatile u16 vu16;
|
typedef volatile u16 vu16;
|
||||||
typedef volatile u32 vu32;
|
typedef volatile u32 vu32;
|
||||||
typedef volatile u64 vu64;
|
typedef volatile u64 vu64;
|
||||||
|
|
||||||
|
struct fb {
|
||||||
|
u8 *top_left;
|
||||||
|
u8 *top_right;
|
||||||
|
u8 *bottom;
|
||||||
|
} __attribute__((packed));
|
||||||
|
@ -7,7 +7,7 @@ fname_addr equ 0x27FFDF80
|
|||||||
low_tid_addr equ 0x27FFDFE0
|
low_tid_addr equ 0x27FFDFE0
|
||||||
copy_launch_stub_addr equ 0x27FFE000
|
copy_launch_stub_addr equ 0x27FFE000
|
||||||
|
|
||||||
firm_addr equ 0x24000000
|
firm_addr equ 0x20001000
|
||||||
firm_maxsize equ (copy_launch_stub_addr - 0x1000 - firm_addr)
|
firm_maxsize equ (copy_launch_stub_addr - 0x1000 - firm_addr)
|
||||||
|
|
||||||
arm11_entrypoint_addr equ 0x1FFFFFFC
|
arm11_entrypoint_addr equ 0x1FFFFFFC
|
||||||
|
@ -443,6 +443,7 @@ bool checkFirmPayload(void)
|
|||||||
(section->address + section->size < section->address) || //Overflow check
|
(section->address + section->size < section->address) || //Overflow check
|
||||||
((u32)section->address & 3) || (section->offset & 0x1FF) || (section->size & 0x1FF) || //Alignment check
|
((u32)section->address & 3) || (section->offset & 0x1FF) || (section->size & 0x1FF) || //Alignment check
|
||||||
(overlaps((u32)section->address, (u32)section->address + section->size, 0x27FFE000 - 0x1000, 0x28000000)) ||
|
(overlaps((u32)section->address, (u32)section->address + section->size, 0x27FFE000 - 0x1000, 0x28000000)) ||
|
||||||
|
(overlaps((u32)section->address, (u32)section->address + section->size, 0x1FFFFC00, 0x20000000)) ||
|
||||||
(overlaps((u32)section->address, (u32)section->address + section->size, (u32)firm + section->offset, (u32)firm + size)))
|
(overlaps((u32)section->address, (u32)section->address + section->size, (u32)firm + section->offset, (u32)firm + size)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -170,12 +170,10 @@ void loadPayload(u32 pressed, const char *payloadPath)
|
|||||||
else
|
else
|
||||||
sprintf(absPath, "sdmc:/luma/%s", path);
|
sprintf(absPath, "sdmc:/luma/%s", path);
|
||||||
|
|
||||||
char *argv[1] = {absPath};
|
char *argv[2] = {absPath, (char *)fbs};
|
||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
if((u8 *)firm + payloadSize < (u8 *)0x23FFFE00)
|
launchFirm((firm->reserved2[0] & 1) ? 2 : 1, argv);
|
||||||
memcpy((void *)0x23FFFE00, fbs, sizeof(fbs));
|
|
||||||
launchFirm(1, argv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void payloadMenu(void)
|
void payloadMenu(void)
|
||||||
|
@ -50,9 +50,9 @@ struct fb fbs[2];
|
|||||||
|
|
||||||
static void invokeArm11Function(Arm11Operation op)
|
static void invokeArm11Function(Arm11Operation op)
|
||||||
{
|
{
|
||||||
while(*operation != NO_ARM11_OPERATION);
|
while(*operation != ARM11_READY);
|
||||||
*operation = op;
|
*operation = op;
|
||||||
while(*operation != NO_ARM11_OPERATION);
|
while(*operation != ARM11_READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepareArm11ForFirmlaunch(void)
|
void prepareArm11ForFirmlaunch(void)
|
||||||
@ -125,5 +125,6 @@ void initScreens(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearScreens(false);
|
clearScreens(false);
|
||||||
|
clearScreens(true);
|
||||||
swapFramebuffers(false);
|
swapFramebuffers(false);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
#define ARESCREENSINITIALIZED (PDN_GPU_CNT != 1)
|
#define ARESCREENSINITIALIZED (PDN_GPU_CNT != 1)
|
||||||
|
|
||||||
#define ARM11_PARAMETERS_ADDRESS 0x1FFFC000
|
#define ARM11_PARAMETERS_ADDRESS 0x1FFFF000
|
||||||
|
|
||||||
#define SCREEN_TOP_WIDTH 400
|
#define SCREEN_TOP_WIDTH 400
|
||||||
#define SCREEN_BOTTOM_WIDTH 320
|
#define SCREEN_BOTTOM_WIDTH 320
|
||||||
@ -45,22 +45,21 @@ struct fb {
|
|||||||
u8 *top_left;
|
u8 *top_left;
|
||||||
u8 *top_right;
|
u8 *top_right;
|
||||||
u8 *bottom;
|
u8 *bottom;
|
||||||
} __attribute__((packed)); //*const fbs = (volatile struct fb *)0x23FFFE00;
|
} __attribute__((packed));
|
||||||
|
|
||||||
extern struct fb fbs[2];
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
NO_ARM11_OPERATION = 0,
|
INIT_SCREENS_SEQUENCE = 0,
|
||||||
INIT_SCREENS_SEQUENCE,
|
|
||||||
SETUP_FRAMEBUFFERS,
|
SETUP_FRAMEBUFFERS,
|
||||||
CLEAR_SCREENS,
|
CLEAR_SCREENS,
|
||||||
SWAP_FRAMEBUFFERS,
|
SWAP_FRAMEBUFFERS,
|
||||||
UPDATE_BRIGHTNESS,
|
UPDATE_BRIGHTNESS,
|
||||||
DEINIT_SCREENS,
|
DEINIT_SCREENS,
|
||||||
PREPARE_ARM11_FOR_FIRMLAUNCH,
|
PREPARE_ARM11_FOR_FIRMLAUNCH,
|
||||||
|
ARM11_READY,
|
||||||
} Arm11Operation;
|
} Arm11Operation;
|
||||||
|
|
||||||
|
extern struct fb fbs[2];
|
||||||
extern CfgData configData;
|
extern CfgData configData;
|
||||||
|
|
||||||
void prepareArm11ForFirmlaunch(void);
|
void prepareArm11ForFirmlaunch(void);
|
||||||
|
Reference in New Issue
Block a user