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:
Ithamar R. Adema 2012-11-12 20:03:48 +01:00
parent 75b285a969
commit 4b2a1d798b
2 changed files with 110 additions and 116 deletions

View File

@ -94,6 +94,116 @@ FUNCTION(arm_get_far):
bx lr
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,
jmp_buf jumpBuffer, void (*function)(void*), void* parameter)

View File

@ -159,119 +159,3 @@ arch_cpu_user_TLB_invalidate(void)
*/
#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;
}*/