exo: add sdmmc test program

This commit is contained in:
Michael Scire 2020-10-25 21:10:20 -07:00
parent a0f1971353
commit b2f4e0c1b4
11 changed files with 693 additions and 1 deletions

View File

@ -0,0 +1,108 @@
#---------------------------------------------------------------------------------
# Define the atmosphere board and cpu
#---------------------------------------------------------------------------------
export ATMOSPHERE_BOARD := nx-hac-001
export ATMOSPHERE_CPU := arm7tdmi
#---------------------------------------------------------------------------------
# pull in common atmosphere configuration
#---------------------------------------------------------------------------------
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES))))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I.
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD) check_libexo
$(BUILD): check_libexo
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
check_libexo:
@$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).bin
$(OUTPUT).bin : $(OUTPUT).elf
$(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@
@echo built ... $(notdir $@)
$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a
%.elf:
@echo linking $(notdir $@)
$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
@$(NM) -CSn $@ > $(notdir $*.lst)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h: %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,194 @@
OUTPUT_ARCH(arm)
ENTRY(_ZN3ams10sdmmc_test5StartEv)
MEMORY
{
NULL : ORIGIN = 0, LENGTH = 4K
test_fw : ORIGIN = 0x40010000, LENGTH = 32K
}
SECTIONS
{
/* =========== CODE section =========== */
PROVIDE(__start__ = ORIGIN(test_fw));
. = __start__;
__code_start = . ;
.crt0 :
{
KEEP (*(.crt0 .crt0.*))
. = ALIGN(8);
} >test_fw
.vectors :
{
KEEP (*(.vectors .vectors.*))
. = ALIGN(8);
} >test_fw
.text :
{
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
. = ALIGN(8);
} >test_fw
.init :
{
KEEP( *(.init) )
. = ALIGN(8);
} >test_fw
.plt :
{
*(.plt)
*(.iplt)
. = ALIGN(8);
} >test_fw
.fini :
{
KEEP( *(.fini) )
. = ALIGN(8);
} >test_fw
/* =========== RODATA section =========== */
. = ALIGN(8);
__rodata_start = . ;
.rodata :
{
*(.rodata .rodata.* .gnu.linkonce.r.*)
. = ALIGN(8);
} >test_fw
.eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >test_fw
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >test_fw
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >test_fw
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >test_fw
.hash : { *(.hash) } >test_fw
/* =========== DATA section =========== */
. = ALIGN(8);
__data_start = . ;
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >test_fw
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >test_fw
.gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >test_fw
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >test_fw
.preinit_array ALIGN(8) :
{
PROVIDE (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
} >test_fw
.init_array ALIGN(8) :
{
PROVIDE (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE (__init_array_end = .);
} >test_fw
.fini_array ALIGN(8) :
{
PROVIDE (__fini_array_start = .);
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE (__fini_array_end = .);
} >test_fw
.ctors ALIGN(8) :
{
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
} >test_fw
.dtors ALIGN(8) :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
} >test_fw
__got_start__ = .;
.got : { *(.got) *(.igot) } >test_fw
.got.plt : { *(.got.plt) *(.igot.plt) } >test_fw
__got_end__ = .;
.data ALIGN(8) :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
} >test_fw
__bss_start__ = .;
.bss ALIGN(8) :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(16);
} >test_fw
__bss_end__ = .;
__end__ = ABSOLUTE(.) ;
__total_size__ = (__end__ - __start__);
__stack_top__ = 0x40031000;
__stack_bottom__ = 0x40030000;
/* ==================
==== Metadata ====
================== */
/* Discard sections that difficult post-processing */
/DISCARD/ : { *(.group .comment .note .interp) }
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
}

View File

@ -0,0 +1,7 @@
%rename link old_link
*link:
%(old_link) -T %:getenv(TOPDIR /sdmmc_test.ld) --gc-sections --nmagic -nostdlib -nostartfiles
*startfile:
crti%O%s crtbegin%O%s

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
namespace ams::sdmmc_test {
namespace {
constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
constexpr inline auto Port = sdmmc::Port_Mmc0;
alignas(8) constinit u8 g_mmc_work_buffer[sdmmc::MmcWorkBufferSize];
constexpr inline u32 SectorIndex = 0;
constexpr inline u32 SectorCount = 2;
NORETURN void PmcMainReboot() {
/* Write enable to MAIN_RESET. */
reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE));
/* Wait forever until we're reset. */
AMS_INFINITE_LOOP();
}
void CheckResult(const Result result) {
volatile u32 * const DEBUG = reinterpret_cast<volatile u32 *>(0x4003C000);
if (R_FAILED(result)) {
DEBUG[1] = result.GetValue();
PmcMainReboot();
}
}
}
void Main() {
/* Debug signaler. */
volatile u32 * const DEBUG = reinterpret_cast<volatile u32 *>(0x4003C000);
DEBUG[0] = 0;
DEBUG[1] = 0xAAAAAAAA;
/* Initialize sdmmc library. */
sdmmc::Initialize(Port);
DEBUG[0] = 1;
sdmmc::SetMmcWorkBuffer(Port, g_mmc_work_buffer, sizeof(g_mmc_work_buffer));
DEBUG[0] = 2;
Result result = sdmmc::Activate(Port);
DEBUG[0] = 3;
CheckResult(result);
/* Select user data partition. */
result = sdmmc::SelectMmcPartition(Port, sdmmc::MmcPartition_UserData);
DEBUG[0] = 4;
CheckResult(result);
/* Read the first two sectors from disk. */
void * const sector_dst = reinterpret_cast<void *>(0x40038000);
result = sdmmc::Read(sector_dst, SectorCount * sdmmc::SectorSize, Port, SectorIndex, SectorCount);
DEBUG[0] = 5;
CheckResult(result);
/* Perform a reboot. */
DEBUG[1] = 0;
PmcMainReboot();
}
NORETURN void ExceptionHandler() {
PmcMainReboot();
}
}
namespace ams::diag {
void AbortImpl() {
sdmmc_test::ExceptionHandler();
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
.section .crt0.text._ZN3ams10sdmmc_test5StartEv, "ax", %progbits
.align 3
.global _ZN3ams10sdmmc_test5StartEv
_ZN3ams10sdmmc_test5StartEv:
/* Switch to system mode, mask all interrupts, clear all flags */
msr cpsr_cxsf, #0xDF
/* Set the stack pointer. */
ldr sp, =__stack_top__
/* Set our link register to the exception handler. */
ldr lr, =_ZN3ams10sdmmc_test16ExceptionHandlerEv
/* Call init array functions. */
bl __libc_init_array
/* Invoke main. */
b _ZN3ams10sdmmc_test4MainEv
/* Infinite loop. */
2: b 2b

View File

@ -30,7 +30,7 @@ _ZN3ams8warmboot5StartEv:
/* Invoke main. */
ldr r0, =_metadata
bl _ZN3ams8warmboot4MainEPKNS0_8MetadataE
b _ZN3ams8warmboot4MainEPKNS0_8MetadataE
/* Infinite loop. */
1: b 1b

View File

@ -112,6 +112,7 @@ $(OFILES) : $(GCH_FILES)
$(OFILES_SRC) : $(HFILES_BIN)
libc.o: CFLAGS += -fno-builtin -fno-lto
libgcc_division.arch.arm.o: CFLAGS += -fno-builtin -fno-lto
#---------------------------------------------------------------------------------
%_bin.h %.bin.o : %.bin

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#ifdef __cplusplus
extern "C" {
#endif
/* cxx implementation details to be stubbed here, as needed. */
void __cxa_pure_virtual() { AMS_ABORT("pure virtual function call"); }
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@ -0,0 +1,160 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*/
/*
* Form ABI specifications:
* int __aeabi_idiv(int numerator, int denominator);
* unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator);
*
* typedef struct { int quot; int rem; } idiv_return;
* typedef struct { unsigned quot; unsigned rem; } uidiv_return;
*
* __value_in_regs idiv_return __aeabi_idivmod(int numerator,
* int *denominator);
* __value_in_regs uidiv_return __aeabi_uidivmod(unsigned *numerator,
* unsigned denominator);
*/
#ifdef __cplusplus
extern "C" {
#endif
/* struct qr - stores qutient/remainder to handle divmod EABI interfaces. */
struct qr {
unsigned q; /* computed quotient */
unsigned r; /* computed remainder */
unsigned q_n; /* specficies if quotient shall be negative */
unsigned r_n; /* specficies if remainder shall be negative */
};
static void uint_div_qr(unsigned numerator, unsigned denominator,
struct qr *qr);
/* returns in R0 and R1 by tail calling an asm function */
unsigned __aeabi_uidivmod(unsigned numerator, unsigned denominator);
unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator);
/* returns in R0 and R1 by tail calling an asm function */
signed __aeabi_idivmod(signed numerator, signed denominator);
signed __aeabi_idiv(signed numerator, signed denominator);
/*
* __ste_idivmod_ret_t __aeabi_idivmod(signed numerator, signed denominator)
* Numerator and Denominator are received in R0 and R1.
* Where __ste_idivmod_ret_t is returned in R0 and R1.
*
* __ste_uidivmod_ret_t __aeabi_uidivmod(unsigned numerator,
* unsigned denominator)
* Numerator and Denominator are received in R0 and R1.
* Where __ste_uidivmod_ret_t is returned in R0 and R1.
*/
#ifdef __GNUC__
signed ret_idivmod_values(signed quotient, signed remainder);
unsigned ret_uidivmod_values(unsigned quotient, unsigned remainder);
#else
#error "Compiler not supported"
#endif
static void division_qr(unsigned n, unsigned p, struct qr *qr)
{
unsigned i = 1, q = 0;
if (p == 0) {
qr->r = 0xFFFFFFFF; /* division by 0 */
return;
}
while ((p >> 31) == 0) {
i = i << 1; /* count the max division steps */
p = p << 1; /* increase p until it has maximum size*/
}
while (i > 0) {
q = q << 1; /* write bit in q at index (size-1) */
if (n >= p)
{
n -= p;
q++;
}
p = p >> 1; /* decrease p */
i = i >> 1; /* decrease remaining size in q */
}
qr->r = n;
qr->q = q;
}
static void uint_div_qr(unsigned numerator, unsigned denominator, struct qr *qr)
{
division_qr(numerator, denominator, qr);
/* negate quotient and/or remainder according to requester */
if (qr->q_n)
qr->q = -qr->q;
if (qr->r_n)
qr->r = -qr->r;
}
unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator)
{
struct qr qr = { .q_n = 0, .r_n = 0 };
uint_div_qr(numerator, denominator, &qr);
return qr.q;
}
unsigned __aeabi_uidivmod(unsigned numerator, unsigned denominator)
{
struct qr qr = { .q_n = 0, .r_n = 0 };
uint_div_qr(numerator, denominator, &qr);
return ret_uidivmod_values(qr.q, qr.r);
}
signed __aeabi_idiv(signed numerator, signed denominator)
{
struct qr qr = { .q_n = 0, .r_n = 0 };
if (((numerator < 0) && (denominator > 0)) ||
((numerator > 0) && (denominator < 0)))
qr.q_n = 1; /* quotient shall be negate */
if (numerator < 0) {
numerator = -numerator;
qr.r_n = 1; /* remainder shall be negate */
}
if (denominator < 0)
denominator = -denominator;
uint_div_qr(numerator, denominator, &qr);
return qr.q;
}
signed __aeabi_idivmod(signed numerator, signed denominator)
{
struct qr qr = { .q_n = 0, .r_n = 0 };
if (((numerator < 0) && (denominator > 0)) ||
((numerator > 0) && (denominator < 0)))
qr.q_n = 1; /* quotient shall be negate */
if (numerator < 0) {
numerator = -numerator;
qr.r_n = 1; /* remainder shall be negate */
}
if (denominator < 0)
denominator = -denominator;
uint_div_qr(numerator, denominator, &qr);
return ret_idivmod_values(qr.q, qr.r);
}
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*/
/*
* signed ret_idivmod_values(signed quot, signed rem);
* return quotient and remaining the EABI way (regs r0,r1)
*/
.section .text.ret_idivmod_values, "ax", %progbits
.globl ret_idivmod_values
.align 0
.syntax unified
ret_idivmod_values:
bx lr
.type ret_idivmod_values, %function
.size ret_idivmod_values, .-ret_idivmod_values
/*
* unsigned ret_uidivmod_values(unsigned quot, unsigned rem);
* return quotient and remaining the EABI way (regs r0,r1)
*/
.section .text.ret_uidivmod_values, "ax", %progbits
.globl ret_uidivmod_values
.align 0
.syntax unified
ret_uidivmod_values:
bx lr
.type ret_uidivmod_values, %function
.size ret_uidivmod_values, .-ret_uidivmod_values

View File

@ -0,0 +1,35 @@
/* Copyright (C) 1995-2018 Free Software Foundation, Inc.
This file 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; either version 3, or (at your option) any
later version.
This file 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.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
.section .text.__gnu_thumb1_case_uqi, "ax", %progbits
.globl __gnu_thumb1_case_uqi
.align 0
.thumb_func
.syntax unified
__gnu_thumb1_case_uqi:
push {r1}
mov r1, lr
lsrs r1, r1, #1
lsls r1, r1, #1
ldrb r1, [r1, r0]
lsls r1, r1, #1
add lr, lr, r1
pop {r1}
bx lr
.type __gnu_thumb1_case_uqi, %function
.size __gnu_thumb1_case_uqi, .-__gnu_thumb1_case_uqi