Added the CILOIO library to abstract platform-specific I/O operations away.

master
Philippe Vachon 16 years ago
parent 8abff9252f
commit 3982f603d6

@ -42,7 +42,7 @@ RAW=${OBJCOPY} --strip-unneeded --alt-machine-code ${MACHCODE}
INCLUDE=-Iinclude/ -Imach/${TARGET} -Iinclude/mach/${TARGET}
CFLAGS=$(INCLUDE) -fno-builtin -fomit-frame-pointer -fno-pic -mno-abicalls \
CFLAGS=-fno-builtin -fomit-frame-pointer -fno-pic -mno-abicalls \
-Wall
ASFLAGS=-xassembler-with-cpp -traditional-cpp
@ -50,9 +50,10 @@ ASFLAGS=-xassembler-with-cpp -traditional-cpp
LDFLAGS=--omagic -nostartfiles -nostdlib --discard-all --strip-all \
-Ttext ${TEXTADDR} --entry _start
OBJECTS=main.o printf.o elf_loader.o
OBJECTS=string.o main.o ciloio.o printf.o elf_loader.o
LINKOBJ=${OBJECTS} $(MACHDIR)/promlib.o $(MACHDIR)/start.o #$(MACHDIR)/platform.o
LINKOBJ=${OBJECTS} $(MACHDIR)/promlib.o $(MACHDIR)/start.o $(MACHDIR)/platio.o\
$(MACHDIR)/platform.o
THISFLAGS='LDFLAGS=$(LDFLAGS)' 'ASFLAGS=$(ASFLAGS)' \
@ -65,10 +66,10 @@ ${PROG}: sub ${OBJECTS}
${RAW} ${PROG}.elf ${PROG}.bin
.c.o:
${CC} ${CFLAGS} -c $<
${CC} ${CFLAGS} $(INCLUDE) -c $<
.S.o:
${CC} ${CFLAGS} ${ASFLAGS} -c $<
${CC} ${CFLAGS} $(INCLUDE) ${ASFLAGS} -c $<
sub:
@for i in $(MACHDIR); do \

@ -0,0 +1,48 @@
/* (C) 2008 Philippe Vachon <philippe@cowpig.ca>
* Licensed under the GNU General Public License v2
*/
#include <types.h>
#include <ciloio.h>
/* Platform-specific file I/O operations */
#include <platio.h>
struct file cilo_open(const char *filename)
{
struct file fp;
if (!platio_find_file(filename)) {
fp.code = -1;
return fp;
}
platio_file_open(&fp, filename);
return fp;
}
int32_t cilo_read(void *pbuf, uint32_t size, uint32_t nmemb, struct file *fp)
{
return platio_read(pbuf, size, nmemb, fp);
}
int32_t cilo_seek(struct file *fp, uint32_t offset, uint8_t whence)
{
switch (whence) {
case SEEK_SET:
if (offset > fp->file_len) fp->file_pos = fp->file_len;
else fp->file_pos = offset;
break;
case SEEK_CUR:
if (offset + fp->file_pos > fp->file_len) fp->file_pos = fp->file_len;
else fp->file_pos += offset;
break;
case SEEK_END:
/* unimplemented */
break;
default:
return 0;
}
return 0;
}

@ -1,14 +1,19 @@
/**
* CILO Elf Loader
* (c) 2008 Philippe Vachoon <philippe@cowpig.ca>
*
* Licensed under the GNU General Public License v2. See COPYING
* in the distribution source directory for more information.
*/
#include <elf.h>
#include <promlib.h>
#include <printf.h>
#include <ciloio.h>
/* platform-specific defines */
#include <platform.h>
void read(void *ptr, uint32_t size, uint32_t count, uint32_t base,
uint32_t offset);
/**
* load a single ELF section into memory at address. Assumes ELF data is
* contiguous in memory.
@ -17,12 +22,12 @@ void read(void *ptr, uint32_t size, uint32_t count, uint32_t base,
* @param file_offset offset (in bytes) in the ELF file where the section is
* @param length Length of the section (in bytes)
*/
void load_elf32_section(uint32_t base, uint32_t address,
void load_elf32_section(struct file *fp, uint32_t address,
uint32_t file_offset, uint32_t length)
{
uint8_t *elf_loc = (uint8_t *)address;
read(elf_loc, length, 1, base, file_offset);
cilo_seek(fp, file_offset, SEEK_SET);
cilo_read(elf_loc, length, 1, fp);
}
@ -41,27 +46,6 @@ void load_elf32_uninitialized_memory(uint32_t address, uint32_t length)
}
}
/**
* Read data into a given pointer
* @param ptr Pointer to write data out to
* @param size Size of the data to be read
* @param count number of elements to read
* @param base base address to start read from
* @param offset offset from base to read from
*/
void read(void *ptr, uint32_t size, uint32_t count, uint32_t base,
uint32_t offset)
{
uint8_t *data = (uint8_t *)ptr;
uint8_t *src = (uint8_t *)(base + offset);
int i;
for (i = 0; i < size * count; i++) {
data[i] = src[i];
}
}
/**
* Load an ELF file into memory from the given base. Loads at
* offset + image_size so that a later memcpy routine can be used to copy
@ -70,14 +54,14 @@ void read(void *ptr, uint32_t size, uint32_t count, uint32_t base,
* @param loader_addr address of the loader binary in memory
* @return
*/
int load_elf32_file(uint32_t base, uint32_t loader_addr)
int load_elf32_file(struct file *fp)
{
struct elf32_header hdr;
uint32_t mem_sz = 0;
/* read in header entries */
read(&hdr, sizeof(struct elf32_header), 1, base, 0);
cilo_read(&hdr, sizeof(struct elf32_header), 1, fp);
/* check the file magic */
if (hdr.ident[0] != ELF_MAGIC_1 || hdr.ident[1] != ELF_MAGIC_2 ||
@ -121,36 +105,39 @@ int load_elf32_file(uint32_t base, uint32_t loader_addr)
int i;
struct elf32_phdr phdr;
uint32_t ph_offset = hdr.phoff;
cilo_seek(fp, hdr.phoff, SEEK_SET);
/* read in program header(s), determine total memory size of image */
/* TODO: figure out if there's a better way to determine this */
for (i = 0; i < hdr.phnum; i++) {
read(&phdr, sizeof(struct elf32_phdr), 1, base, ph_offset);
cilo_read(&phdr, sizeof(struct elf32_phdr), 1, fp);
mem_sz += phdr.memsz;
/*increment program header offset */
ph_offset += sizeof(struct elf32_phdr);
}
printf("Total in-memory image size: %d\n", mem_sz);
/* read the PT_LOAD segments into memory at paddr + mem_sz
*/
ph_offset = hdr.phoff;
cilo_seek(fp, hdr.phoff, SEEK_SET);
for (i = 0; i < hdr.phnum; i++) {
read(&phdr, sizeof(struct elf32_phdr), 1, base, ph_offset);
cilo_read(&phdr, sizeof(struct elf32_phdr), 1, fp);
/* skip unloadable segments */
if (phdr.type != ELF_PT_LOAD) continue;
uint32_t leftover = phdr.memsz - phdr.filesz;
load_elf32_section(base, mem_sz + phdr.paddr,
printf("Loading section at %08x\n", phdr.paddr);
load_elf32_section(fp, mem_sz + phdr.paddr,
phdr.offset, phdr.filesz);
if (leftover > 0) {
load_elf32_uninitialized_memory(mem_sz + phdr.paddr +
phdr.filesz, leftover);
}
cilo_seek(fp, hdr.phoff + sizeof(struct elf32_phdr) * (i + 1),
SEEK_SET);
}
/* assume the entry point is the smallest address we're loading */
@ -167,24 +154,10 @@ int load_elf32_file(uint32_t base, uint32_t loader_addr)
#endif
/* Jump to the copy routine */
/* asm (".set noreorder\n"
"move $k0, %[bootcpy]\n"
"move $a0, %[kdataoffset]\n"
"move $a1, %[kdatalength]\n"
"move $a2, %[kentrypt]\n"
"move $a3, %[kloadoffset]\n"
"jr $k0\n"
" nop\n"
:
: [bootcpy] "r"(loader_addr), [kdataoffset] "r"(load_offset),
[kdatalength] "r"(mem_sz), [kentrypt]"r"(hdr.entry),
[kloadoffset] "r"(hdr.entry)
: "k0", "a0", "a1", "a2", "a3"
); */
((void (*)(uint32_t data_offset, uint32_t data_length, uint32_t entry_pt,
stage_two(load_offset, hdr.entry, mem_sz);
/* ((void (*)(uint32_t data_offset, uint32_t data_length, uint32_t entry_pt,
uint32_t load_offset)) (loader_addr))
(load_offset, mem_sz, hdr.entry, hdr.entry);
(load_offset, mem_sz, hdr.entry, hdr.entry); */
return -1; /* something failed, badly */
}

@ -104,6 +104,9 @@ const char *machine_id_to_string(uint16_t machine)
case 0x1e:
return "Cisco 3620/40 Router (MIPS, IDT R4700, Big Endian)";
break;
case 0x24:
return "Cisco 12000 Series Router (MIPS/PowerPC, Big Endian)";
break;
case 0x2b:
return "Cisco 2600 Series Router (PowerPC, MPC860, Big Endian)";
break;

@ -0,0 +1,27 @@
#ifndef _INCLUDE_CILOIO_H
#define _INCLUDE_CILOIO_H
#include <types.h>
struct file {
uint8_t dev; /* device ID number */
uint32_t file_len; /* length of the file */
uint32_t file_pos; /* position in the file */
char filename[128];
int8_t code; /* error code */
void *private; /* private data for the platform specific flash handler */
};
#define SEEK_SET 9
#define SEEK_CUR 1
#define SEEK_END 2
struct file cilo_open(const char *filename);
int32_t cilo_read(void *pbuf, uint32_t size, uint32_t nmemb,
struct file *fp);
int32_t cilo_seek(struct file *fp, uint32_t offset, uint8_t whence);
struct fs_ent *find_file(const char *filename, uint32_t base);
#endif /* _INCLUDE_CILOIO_H */

@ -2,10 +2,11 @@
#define _ELF_LOADER_H
#include <types.h>
#include <ciloio.h>
void load_elf32_section(uint32_t base, uint32_t address, uint32_t file_offset,
uint32_t length);
void load_elf32_section(struct file *fp, uint32_t address,
uint32_t file_offset, uint32_t length);
void load_elf32_uninitialized_memory(uint32_t address, uint32_t length);
int load_elf32_file(uint32_t base, uint32_t loader_addr);
int load_elf32_file(struct file *fp);
#endif /* _ELF_LOADER_H */

@ -1,8 +1,16 @@
#ifndef _INCLUDE_MACH_C3600_PLATFORM_H
#define _INCLUDE_MACH_C3600_PLATFORM_H
#include <types.h>
#define FLASH_BASE 0x30000000
#define KERNEL_ENTRY_POINT 0x80008000
#define MEMORY_BASE 0x80000000
void platform_init();
uint32_t check_flash();
void flash_directory();
uint32_t locate_stage_two();
void stage_two(uint32_t kern_off, uint32_t kern_entry, uint32_t kern_size);
#endif /* _INCLUDE_MACH_C3600_PLATFORM_H */

@ -0,0 +1,24 @@
#ifndef _INCLUDE_MACH_C3600_PLATIO
#define _INCLUDE_MACH_C3600_PLATIO
#include <types.h>
#include <ciloio.h>
/* a flash filesystem entry for the C3600 */
struct fs_ent {
uint32_t magic;
uint32_t length;
/* todo: figure out exactly what these two fields contain */
uint32_t crc32;
uint32_t date;
char filename[48];
};
void platio_file_open(struct file *fp, const char *filename);
uint32_t platio_read(void *pbuf, uint32_t size, uint32_t nmemb,
struct file *fp);
uint8_t platio_find_file(const char *filename);
#define FS_FILE_MAGIC 0xbad00b1e
#endif /* _INCLUDE_MACH_C3600_PLATIO */

@ -1,109 +1,20 @@
#ifndef _STRING_H
#define _STRING_H
#define NULL 0
#include <types.h>
inline int strcmp(const char *s1, const char *s2)
{
while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0') {
s1++;
s2++;
}
int strcmp(const char *s1, const char *s2);
if (*s1 != *s2) return -1;
int strncmp(const char *s1, const char *s2, uint32_t n);
return 0;
}
char *strcpy(char *dest, const char *src);
inline int strncmp(const char *s1, const char *s2, uint32_t n)
{
int i;
for (i = 0; i < n; i++) {
if (s1[i] != s2[i]) return -1;
if (s1[i] == s2[i] && s1[i] == '\0') break;
}
char *strncpy(char *dest, const char *src, uint32_t n);
return 0;
}
uint32_t strlen(char *s);
inline char *strcpy(char *dest, const char *src)
{
if (!dest || !src) {
return NULL;
}
int memcpy(void *dst, const void *src, int n);
while (*src != '\0') {
*dest++ = *src++;
}
*dest = '\0';
return dest;
}
inline char *strncpy(char *dest, const char *src, uint32_t n)
{
int i;
int t = 0;
if (!dest || !src || n == 0) {
return NULL;
}
for (i = 0; i < n; i++) {
if (!t) dest[i] = src[i];
else dest[i] = '\0';
if (dest[i] == '\0') t = 1;
}
return dest;
}
inline uint32_t strlen(char *s)
{
int i = 0;
while (*s != '\0') {
i++; s++;
}
return i;
}
/**
* Copy n bytes from src to dst
* @param dst destination buffer
* @param src source buffer
* @param n number of bytes to copy
* @return number of bytes copied or value < 0 on error
*/
inline int memcpy(void *dst, const void *src, int n)
{
int i = 0;
if (!dst || !src) return -1;
for (i = 0; i < n; i++) {
((char *)dst)[i] = ((char *)src)[i];
}
return i;
}
/**
* strchr
*/
inline const char *strchr(const char *s, int c)
{
const char *ptr = s;
if (*ptr == (char)c) return ptr;
while (*ptr != '\0') {
if (*(++ptr) == (char)c) {
return ptr;
}
}
return NULL;
}
const char *strchr(const char *s, int c);
#endif /* _STRING_H */

@ -2,6 +2,7 @@
#define _TYPES_H
/* define some convenience types */
#define NULL 0
typedef unsigned char uint8_t;
typedef char int8_t;

@ -2,7 +2,7 @@ ifndef CROSS_COMPILE
CROSS_COMPILE=mips-elf-
endif
OBJECTS=start.o promlib.o
OBJECTS=start.o promlib.o platform.o platio.o
INCLUDE=-I../../include

@ -0,0 +1,68 @@
#include <types.h>
#include <mach/c3600/platform.h>
#include <mach/c3600/platio.h>
#include <printf.h>
/**
* perform hardware-specifc initialization for this platform
*/
void platform_init()
{
}
/**
* Perform a sanity check on flash
* @returns 0 if no flash found, number of flash devices found otherwise
*/
uint32_t check_flash()
{
uint32_t *ptr = (uint32_t *)FLASH_BASE;
if (*ptr != FS_FILE_MAGIC) {
return 0;
}
return 1;
/* TODO: add support for PCMCIA flash */
}
/**
* print a directory listing of the 'main' flash device in the system
*/
void flash_directory()
{
struct fs_ent *f = (struct fs_ent *)FLASH_BASE;
uint32_t offset = 0;
/* Iterate over the files; f->magic is 0 if an invalid file is found. */
while (f->magic == FS_FILE_MAGIC) {
printf("%s\n", f->filename);
offset += sizeof(struct fs_ent) + f->length;
f = (struct fs_ent *)(FLASH_BASE + offset);
}
}
/**
* Locate the stage two loader for CiscoLoad for this particular platform
* returns 0 if not found, 1 if found
*/
uint32_t locate_stage_two()
{
return platio_find_file("ciscoload.two");
}
/**
* Kick into the stage two loader.
*/
void stage_two(uint32_t kern_off, uint32_t kern_entry, uint32_t kern_size)
{
uint32_t s2addr = ((uint32_t)find_file("ciscoload.two", FLASH_BASE)) +
sizeof(struct fs_ent);
((void (*)(uint32_t data_offset, uint32_t data_length, uint32_t entry_pt,
uint32_t load_offset)) (s2addr))
(kern_off, kern_size, kern_entry, kern_entry);
}

@ -0,0 +1,98 @@
/* Platform specific operations for I/O for the cisco 3600 Series
* (C) 2008 Philippe Vachon <philippe@cowpig.ca>
*
* Licensed under the GNU General Public License v2.
*/
#include <types.h>
#include <mach/c3600/platio.h>
#include <ciloio.h>
#include <string.h>
#include <mach/c3600/platform.h>
/* find file in filesystem starting at base */
struct fs_ent *find_file(const char *filename, uint32_t base)
{
/* Actual file offset */
uint32_t offset = 0;
struct fs_ent *f = (struct fs_ent *)(base + offset);
/* iterate over files in flash */
while (f->magic == FS_FILE_MAGIC) {
if (!strncmp(f->filename, filename, 48)) {
return f;
}
offset += sizeof(struct fs_ent) + f->length;
f = (struct fs_ent *)(base + offset);
}
return NULL;
}
/**
* Find a file within the platform-supported I/O devices
* @param filename the file
* @returns 0 on failure, device ID number on success
*/
uint8_t platio_find_file(const char *filename)
{
if (find_file(filename, FLASH_BASE)) {
return 1;
}
/* todo: add support for PCMCIA devices */
return 0;
}
/**
* Open a file.
* @param fp File structure to hold file information
* @param filename name of the file
*/
void platio_file_open(struct file *fp, const char *filename)
{
struct fs_ent *ent = find_file(filename, FLASH_BASE);
if (ent == NULL) {
fp->code = 0;
fp->private = NULL;
return;
}
fp->dev = 1; /* TODO: add support for PCMCIA flash */
fp->private = (void *)ent;
fp->file_len = ent->length;
fp->file_pos = 0;
/* copy the filename */
strncpy(fp->filename, ent->filename, 48);
fp->code = 1;
}
/**
* Read data from a given file
* @param pbuf Buffer to read data into
* @param size size of entity to be read
* @param nmemb number of members to read
* @param fp file information structure to read from.
* @returns number of bytes read (should = size * nmemb for C3600)
*/
uint32_t platio_read(void *pbuf, uint32_t size, uint32_t nmemb, struct file *fp)
{
/* calculate the effective offset of the data we want to read: */
char *from = (char *)((uint32_t)(fp->private) + sizeof(struct fs_ent) +
fp->file_pos);
memcpy(pbuf, from, size * nmemb);
fp->file_pos += size * nmemb;
return nmemb * size;
}

118
main.c

@ -6,16 +6,16 @@
#include <addr.h>
#include <elf.h>
#include <elf_loader.h>
#include <string.h>
#include <ciloio.h>
#include <promlib.h>
/* platform-specific defines */
#include <platform.h>
#define FS_FILE_MAGIC 0xbad00b1e
#include <string.h>
/**
* Dump 0x10 bytes of RAM in canonical hexadecimal form
* Dump 0x10 bytes of memory in canonical hexadecimal form
* @param addr Starting address to dump from
*/
void hex_dump(uint32_t addr)
@ -38,91 +38,6 @@ void hex_dump(uint32_t addr)
}
struct fs_ent {
uint32_t magic;
uint32_t length;
/* guesses -- one of these is probably a CRC16 + flags */
uint32_t crc32; /* ? */
uint32_t date; /* ? */
char filename[48];
};
/**
* Check the sanity of flash -- just look for the filesystem magic number
* in the first 4 bytes of flash
* @param base base address of flash
* @returns 0 on failure, 1 on success
*/
int check_flash(uint32_t base)
{
uint32_t *ptr = (uint32_t *)base;
if (*ptr != FS_FILE_MAGIC) {
return 0;
}
return 1;
}
/**
* Find file in flash.
* @param filename Name of the kernel ELF file to be loaded.
* @param base Base address of flash
* @returns offset of the file within the flash memory space.
*/
uint32_t find_file(const char *filename, uint32_t base)
{
/* Actual file offset */
uint32_t offset = 0;
uint32_t file_offset = 0;
struct fs_ent *f = (struct fs_ent *)(base + offset);
/* iterate over files in flash */
while (f->magic == FS_FILE_MAGIC) {
if (!strncmp(f->filename, filename, 48)) {
file_offset = offset + sizeof(struct fs_ent);
break;
}
offset += sizeof(struct fs_ent) + f->length;
f = (struct fs_ent *)(base + offset);
}
return file_offset;
}
/**
* Print a directory listing of all files in flash
* @param base Base address of flash
*/
void flash_directory(uint32_t base)
{
struct fs_ent *f = (struct fs_ent *)base;
uint32_t offset = 0;
/* Iterate over the files; f->magic is 0 if an invalid file is
* found.
*/
while (f->magic == FS_FILE_MAGIC) {
printf("%s\n", f->filename);
offset += sizeof(struct fs_ent) + f->length;
f = (struct fs_ent *)(base + offset);
}
}
/**
* Locate the stage two loader.
* @param base Flash base.
* @return address of stage two loader
*/
uint32_t locate_stage_two(uint32_t base)
{
return find_file("ciscoload.two", base);
}
/**
* Entry Point for CiscoLoad
*/
@ -133,6 +48,7 @@ void start_bootloader()
char buf[129];
char *cmd_line = (char *)MEMORY_BASE;
char kernel[49];
const char *cmd_line_append;
buf[128] = '\0';
kernel[48] = '\0';
@ -145,7 +61,7 @@ void start_bootloader()
/* check flash filesystem sanity */
c_putc('L');
f = check_flash(FLASH_BASE);
f = check_flash();
if (!f) {
printf("\nError: Unable to find any valid flash! Aborting load.\n");
@ -153,9 +69,10 @@ void start_bootloader()
}
c_putc('O');
platform_init();
/* locate the stage two loader */
if (!locate_stage_two(FLASH_BASE)) {
if (!locate_stage_two()) {
printf("\nError: Unable to find valid stage two loader. "
"Aborting load.\n");
return;
@ -165,14 +82,13 @@ void start_bootloader()
printf("Available RAM: %d kB\n", r);
printf("Available files:\n");
flash_directory(FLASH_BASE);
flash_directory();
enter_filename:
printf("\nEnter filename to boot:\n> ");
c_gets(buf, 128);
/* determine if a command line string has been appended to kernel name */
const char *cmd_line_append;
if ((cmd_line_append = strchr(buf, ' ')) != NULL) {
strcpy(cmd_line, (char *)(cmd_line_append + 1));
/* extract the kernel file name now */
@ -186,22 +102,16 @@ enter_filename:
printf("\n\nAttempting to load file %s\n", kernel);
uint32_t kernel_off = find_file(kernel, FLASH_BASE);
uint32_t loader_off = find_file("ciscoload.two", FLASH_BASE);
if (loader_off == 0) {
printf("Unable to find the second stage loader. Please copy the "
"second\nstage loader to the flash filesystem (ciscoload.two).");
return;
}
struct file kernel_file = cilo_open(kernel);
if (kernel_off == 0) {
if (kernel_file.code == 0) {
printf("Unable to find \"%s\" on the flash filesystem.\n", kernel);
} else {
printf("Booting \"%s\" from flash at 0x%08x\n", kernel,
FLASH_BASE + kernel_off);
#ifdef DEBUG
printf("DEBUG: cmd_line: %s\n", cmd_line);
if (load_elf32_file(FLASH_BASE + kernel_off, FLASH_BASE + loader_off)
#endif
printf("Booting %s.\n");
if (load_elf32_file(&kernel_file)
< 0)
{
printf("Fatal error while loading kernel. Aborting.\n");

@ -0,0 +1,106 @@
#include <string.h>
#include <types.h>
int strcmp(const char *s1, const char *s2)
{
while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0') {
s1++;
s2++;
}
if (*s1 != *s2) return -1;
return 0;
}
int strncmp(const char *s1, const char *s2, uint32_t n)
{
int i;
for (i = 0; i < n; i++) {
if (s1[i] != s2[i]) return -1;
if (s1[i] == s2[i] && s1[i] == '\0') break;
}
return 0;
}
char *strcpy(char *dest, const char *src)
{
if (!dest || !src) {
return NULL;
}
while (*src != '\0') {
*dest++ = *src++;
}
*dest = '\0';
return dest;
}
char *strncpy(char *dest, const char *src, uint32_t n)
{
int i;
int t = 0;
if (!dest || !src || n == 0) {
return NULL;
}
for (i = 0; i < n; i++) {
if (!t) dest[i] = src[i];
else dest[i] = '\0';
if (dest[i] == '\0') t = 1;
}
return dest;
}
uint32_t strlen(char *s)
{
int i = 0;
while (*s != '\0') {
i++; s++;
}
return i;
}
/**
* Copy n bytes from src to dst
* @param dst destination buffer
* @param src source buffer
* @param n number of bytes to copy
* @return number of bytes copied or value < 0 on error
*/
int memcpy(void *dst, const void *src, int n)
{
int i = 0;
if (!dst || !src) return -1;
for (i = 0; i < n; i++) {
((char *)dst)[i] = ((char *)src)[i];
}
return i;
}
/**
* strchr
*/
const char *strchr(const char *s, int c)
{
const char *ptr = s;
if (*ptr == (char)c) return ptr;
while (*ptr != '\0') {
if (*(++ptr) == (char)c) {
return ptr;
}
}
return NULL;
}
Loading…
Cancel
Save