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
|
||||
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)
|
||||
|
||||
|
@ -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;
|
||||
}*/
|
||||
|
Loading…
Reference in New Issue
Block a user