ARM: implement arch_cpu_user_memcpy/memset/strlcpy functions
Remove the dummies from the C code and implement them in assembly, due to the label referencing issues with the fault handler. This code is ripe for optimisation, my ARM assembly is pretty basic ;) Does work though, and gets us one step closer to a full arch.
This commit is contained in:
parent
75b285a969
commit
4b2a1d798b
@ -94,6 +94,116 @@ FUNCTION(arm_get_far):
|
|||||||
bx lr
|
bx lr
|
||||||
FUNCTION_END(arm_get_far)
|
FUNCTION_END(arm_get_far)
|
||||||
|
|
||||||
|
/* status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandler) */
|
||||||
|
FUNCTION(arch_cpu_user_memcpy):
|
||||||
|
stmfd sp!, { r4-r6 }
|
||||||
|
ldr r6, [r3]
|
||||||
|
ldr r4, =.L_user_memcpy_error
|
||||||
|
str r4, [r3] /* set fault handler */
|
||||||
|
mov r4, r2, lsr #2 /* size / 4 */
|
||||||
|
1:
|
||||||
|
ldr r5, [r1]
|
||||||
|
str r5, [r0]
|
||||||
|
add r1, #4
|
||||||
|
add r0, #4
|
||||||
|
sub r4, #1
|
||||||
|
bne 1b
|
||||||
|
2:
|
||||||
|
and r4, r2, #3 /* size % 4 */
|
||||||
|
ldrb r5, [r1]
|
||||||
|
strb r5, [r0]
|
||||||
|
add r1, #1
|
||||||
|
add r0, #1
|
||||||
|
sub r4, #1
|
||||||
|
bne 2b
|
||||||
|
|
||||||
|
str r6, [r3] /* restore fault handler */
|
||||||
|
mov r0, #0
|
||||||
|
ldmfd sp!, { r4-r6 }
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.L_user_memcpy_error:
|
||||||
|
str r6, [r3] /* restore fault handler */
|
||||||
|
mov r0, #-1
|
||||||
|
|
||||||
|
ldmfd sp!, { r4-r6 }
|
||||||
|
bx lr
|
||||||
|
FUNCTION_END(arch_cpu_user_memcpy)
|
||||||
|
|
||||||
|
/* status_t arch_cpu_user_memset(void *to, char c, size_t count, addr_t *faultHandler) */
|
||||||
|
FUNCTION(arch_cpu_user_memset):
|
||||||
|
stmfd sp!, { r4-r5 }
|
||||||
|
ldr r5, [r3]
|
||||||
|
ldr r4, =.L_user_memset_error
|
||||||
|
str r4, [r3]
|
||||||
|
|
||||||
|
and r1, r1, #0xff
|
||||||
|
add r1, r1, lsl #8
|
||||||
|
add r1, r1, lsl #16
|
||||||
|
add r1, r1, lsl #24
|
||||||
|
|
||||||
|
mov r4, r2, lsr #2 /* count / 4 */
|
||||||
|
1:
|
||||||
|
str r1, [r0]
|
||||||
|
add r0, r0, #4
|
||||||
|
sub r4, r4, #1
|
||||||
|
bne 1b
|
||||||
|
|
||||||
|
and r4, r2, #3 /* count % 4 */
|
||||||
|
2:
|
||||||
|
strb r1, [r0]
|
||||||
|
add r0, r0, #1
|
||||||
|
sub r4, r4, #1
|
||||||
|
bne 2b
|
||||||
|
|
||||||
|
mov r0, #0
|
||||||
|
str r5, [r3]
|
||||||
|
|
||||||
|
ldmfd sp!, { r4-r5 }
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.L_user_memset_error:
|
||||||
|
mov r0, #-1
|
||||||
|
str r5, [r3]
|
||||||
|
|
||||||
|
ldmfd sp!, { r4-r5 }
|
||||||
|
bx lr
|
||||||
|
FUNCTION_END(arch_cpu_user_memset)
|
||||||
|
|
||||||
|
/* ssize_t arch_cpu_user_strlcpy(void *to, const void *from, size_t size, addr_t *faultHandler) */
|
||||||
|
FUNCTION(arch_cpu_user_strlcpy):
|
||||||
|
stmfd sp!, { r4-r6 }
|
||||||
|
ldr r5, [r3]
|
||||||
|
ldr r4, =.L_user_strlcpy_error
|
||||||
|
str r4, [r3]
|
||||||
|
mov r6, #0
|
||||||
|
1:
|
||||||
|
ldrb r4, [r1, r6]
|
||||||
|
strb r4, [r0, r6]
|
||||||
|
add r6, r6, #1
|
||||||
|
and r4, #0xff /* done yet? */
|
||||||
|
beq 2f
|
||||||
|
cmp r6, r2 /* reached max length? */
|
||||||
|
blt 1b
|
||||||
|
2:
|
||||||
|
mov r4, #0
|
||||||
|
strb r4, [r0, r6]
|
||||||
|
|
||||||
|
mov r0, r4 /* return B_OK */
|
||||||
|
str r5, [r3] /* restore fault handler */
|
||||||
|
|
||||||
|
ldmfd sp!, { r4-r6 }
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.L_user_strlcpy_error:
|
||||||
|
mov r0, #-1
|
||||||
|
str r5, [r3]
|
||||||
|
|
||||||
|
ldmfd sp!, { r4-r6 }
|
||||||
|
bx lr
|
||||||
|
FUNCTION_END(arch_cpu_user_strlcpy)
|
||||||
|
|
||||||
|
|
||||||
/*! \fn void arch_debug_call_with_fault_handler(cpu_ent* cpu,
|
/*! \fn void arch_debug_call_with_fault_handler(cpu_ent* cpu,
|
||||||
jmp_buf jumpBuffer, void (*function)(void*), void* parameter)
|
jmp_buf jumpBuffer, void (*function)(void*), void* parameter)
|
||||||
|
|
||||||
|
@ -159,119 +159,3 @@ arch_cpu_user_TLB_invalidate(void)
|
|||||||
*/
|
*/
|
||||||
#warning WRITEME
|
#warning WRITEME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: all functions that use fault handlers need to be implemented
|
|
||||||
// in assembly due to problems passing in label addresses in gcc4.
|
|
||||||
status_t
|
|
||||||
arch_cpu_user_memcpy(void *to, const void *from, size_t size,
|
|
||||||
addr_t *faultHandler)
|
|
||||||
{
|
|
||||||
#warning WRITEME
|
|
||||||
/*
|
|
||||||
char *tmp = (char *)to;
|
|
||||||
char *s = (char *)from;
|
|
||||||
addr_t oldFaultHandler = *faultHandler;
|
|
||||||
|
|
||||||
if (m68k_set_fault_handler(faultHandler, (addr_t)&&error))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
while (size--)
|
|
||||||
*tmp++ = *s++;
|
|
||||||
|
|
||||||
*faultHandler = oldFaultHandler;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
*faultHandler = oldFaultHandler;*/
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** \brief Copies at most (\a size - 1) characters from the string in \a from to
|
|
||||||
* the string in \a to, NULL-terminating the result.
|
|
||||||
*
|
|
||||||
* \param to Pointer to the destination C-string.
|
|
||||||
* \param from Pointer to the source C-string.
|
|
||||||
* \param size Size in bytes of the string buffer pointed to by \a to.
|
|
||||||
*
|
|
||||||
* \return strlen(\a from).
|
|
||||||
*/
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
arch_cpu_user_strlcpy(char *to, const char *from,
|
|
||||||
size_t size, addr_t *faultHandler)
|
|
||||||
{
|
|
||||||
#warning WRITEME
|
|
||||||
/*
|
|
||||||
int from_length = 0;
|
|
||||||
addr_t oldFaultHandler = *faultHandler;
|
|
||||||
|
|
||||||
if (m68k_set_fault_handler(faultHandler, (addr_t)&&error))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (size > 0) {
|
|
||||||
to[--size] = '\0';
|
|
||||||
// copy
|
|
||||||
for ( ; size; size--, from_length++, to++, from++) {
|
|
||||||
if ((*to = *from) == '\0')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// count any leftover from chars
|
|
||||||
while (*from++ != '\0')
|
|
||||||
from_length++;
|
|
||||||
|
|
||||||
*faultHandler = oldFaultHandler;
|
|
||||||
return from_length;
|
|
||||||
|
|
||||||
error:
|
|
||||||
*faultHandler = oldFaultHandler;*/
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler)
|
|
||||||
{
|
|
||||||
#warning WRITEME
|
|
||||||
|
|
||||||
/*
|
|
||||||
char *xs = (char *)s;
|
|
||||||
addr_t oldFaultHandler = *faultHandler;
|
|
||||||
|
|
||||||
if (m68k_set_fault_handler(faultHandler, (addr_t)&&error))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
*xs++ = c;
|
|
||||||
|
|
||||||
*faultHandler = oldFaultHandler;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
*faultHandler = oldFaultHandler;*/
|
|
||||||
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// The purpose of this function is to trick the compiler. When setting the
|
|
||||||
// page_handler to a label that is obviously (to the compiler) never used,
|
|
||||||
// it may reorganize the control flow, so that the labeled part is optimized
|
|
||||||
// away.
|
|
||||||
// By invoking the function like this
|
|
||||||
//
|
|
||||||
// if (m68k_set_fault_handler(faultHandler, (addr_t)&&error))
|
|
||||||
// goto error;
|
|
||||||
//
|
|
||||||
// the compiler has to keep the labeled code, since it can't guess the return
|
|
||||||
// value of this (non-inlinable) function. At least in my tests it worked that
|
|
||||||
// way, and I hope it will continue to work like this in the future.
|
|
||||||
//
|
|
||||||
/*bool
|
|
||||||
m68k_set_fault_handler(addr_t *handlerLocation, addr_t handler)
|
|
||||||
{
|
|
||||||
*handlerLocation = handler;
|
|
||||||
return false;
|
|
||||||
}*/
|
|
||||||
|
Loading…
Reference in New Issue
Block a user