Files
s390-tools/zipl/boot/libc.c
Michael Holzheu b627b8d8e1 Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version.

Changes on top of s390-tools-1.39.0:

 - Add MIT license to all source files
 - Add LICENSE file
 - Transform REAMDE to README.md (markdown)
 - Add AUTHORS.md file
 - Add CONTRIBUTING.md file
 - Move changelog from README to CHANGELOG.md file

Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
2017-08-21 10:55:40 +02:00

383 lines
6.9 KiB
C

/*
* zipl - zSeries Initial Program Loader tool
*
* Mini libc implementation
*
* Copyright IBM Corp. 2013, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdarg.h>
#include "error.h"
#include "libc.h"
#include "sclp.h"
extern char __heap_start[];
extern char __heap_stop[];
extern char __bss_start[];
extern char __bss_stop[];
extern char __ex_table_start[];
extern char __ex_table_stop[];
struct ex_table_entry {
int fault;
int target;
};
#define MEM_ALLOC_START ((unsigned long) __heap_start)
#define MEM_ALLOC_CNT 4
static uint8_t mem_page_alloc_vec[MEM_ALLOC_CNT];
/*
* Initialize memory with value
*/
void *memset(void *s, int c, unsigned long n)
{
char *xs;
xs = s;
while (n--)
*xs++ = c;
return s;
}
/*
* Copy memory
*/
void *memcpy(void *dest, const void *src, unsigned long n)
{
const char *s = src;
char *d = dest;
while (n--)
*d++ = *s++;
return dest;
}
/*
* Copy string
*/
char *strcpy(char *dest, const char *src)
{
const char *s = src;
char *d = dest;
while (*s)
*d++ = *s++;
*d = 0;
return dest;
}
/*
* Return string length
*/
int strlen(const char *s)
{
int len = 0;
while (*s++)
len++;
return len;
}
/*
* Concatenate two strings
*/
char *strcat(char *dest, const char *src)
{
strcpy(dest + strlen(dest), src);
return dest;
}
/*
* Compare two strings
*/
int strncmp(const char *s1, const char *s2, unsigned long count)
{
while (count--)
if (*s1++ != *s2++)
return *(unsigned char *)(s1 - 1) -
*(unsigned char *)(s2 - 1);
return 0;
}
/*
* Convert number to string
*
* Parameters:
*
* - buf: Output buffer
* - base: Base used for formatting (e.g. 10 or 16)
* - val: Number to format
* - zero: If > 0, fill with leading zeros, otherwise use blanks
* - count: Minimum number of characters used for output string
*/
static int num_to_str(char *buf, int base, unsigned long val, int zero,
unsigned long count)
{
static const char conv_vec[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
unsigned long num = 0, val_work = val, in_number = 1;
int i;
/* Count number of characters needed for number */
do {
num++;
val_work /= base;
} while (val_work);
/* Real character number overwrites count */
if (count < num)
count = num;
/* Format number */
for (i = count - 1; i >= 0; i--) {
if (in_number) {
buf[i] = conv_vec[val % base];
val /= base;
in_number = val ? 1 : 0;
} else {
buf[i] = zero ? '0' : ' ';
}
}
buf[count] = 0;
return count;
}
/*
* Convert string to string with indentation
*/
static int str_to_str(char *buf, const char *str, unsigned long count)
{
unsigned long size;
size = strlen(str);
if (count < size)
count = size;
else
memset(buf, ' ', count - size);
strcpy(buf + (count - size), str);
return count;
}
/*
* Convert string to number with given base
*/
unsigned long strtoul(const char *nptr, char **endptr, int base)
{
unsigned long val = 0;
while (isdigit(*nptr)) {
if (val != 0)
val *= base;
val += *nptr - '0';
nptr++;
}
if (endptr)
*endptr = (char *) nptr;
return val;
}
/*
* Convert ebcdic string to number with given base
*/
unsigned long ebcstrtoul(char *nptr, char **endptr, int base)
{
unsigned long val = 0;
while (ebc_isdigit(*nptr)) {
if (val != 0)
val *= base;
val += *nptr - 0xf0;
nptr++;
}
if (endptr)
*endptr = (char *) nptr;
return val;
}
/*
* Convert string to number with given base
*/
static int sprintf_fmt(char type, char *buf, unsigned long val, int zero,
int count)
{
switch (type) {
case 's':
return str_to_str(buf, (const char *) val, count);
case 'x':
return num_to_str(buf, 16, val, zero, count);
case 'u':
return num_to_str(buf, 10, val, zero, count);
default:
libc_stop(EINTERNAL);
}
return 0;
}
/*
* Print formated string (va version)
*/
static void vsprintf(char *str, const char *fmt, va_list va)
{
unsigned long val, zero, count;
char *fmt_next;
do {
if (*fmt == '%') {
fmt++;
if (*fmt == '0') {
zero = 1;
fmt++;
} else {
zero = 0;
}
/* No number found by strtoul: count=0 fmt_next=fmt */
count = strtoul(fmt, &fmt_next, 10);
fmt = fmt_next;
if (*fmt == 'l')
fmt++;
val = va_arg(va, unsigned long);
str += sprintf_fmt(*fmt, str, val, zero, count);
fmt++;
} else {
*str++ = *fmt++;
}
} while (*fmt);
*str = 0;
}
/*
* Write formated string to string
*/
void sprintf(char *str, const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
vsprintf(str, fmt, va);
va_end(va);
}
/*
* Print formated string
*/
void printf(const char *fmt, ...)
{
char buf[81];
va_list va;
va_start(va, fmt);
vsprintf(buf, fmt, va);
sclp_print(buf);
va_end(va);
}
/*
* Allocate one zero page
*/
unsigned long get_zeroed_page(void)
{
unsigned long addr;
int i;
for (i = 0; i < MEM_ALLOC_CNT; i++) {
if (mem_page_alloc_vec[i] != 0)
continue;
addr = MEM_ALLOC_START + i * PAGE_SIZE;
memset((void *) addr, 0, PAGE_SIZE);
mem_page_alloc_vec[i] = 1;
return addr;
}
libc_stop(EINTERNAL);
}
/*
* Free page
*/
void free_page(unsigned long addr)
{
mem_page_alloc_vec[(addr - MEM_ALLOC_START) / PAGE_SIZE] = 0;
}
/*
* Program check handler
*/
void pgm_check_handler_fn(void)
{
struct ex_table_entry *ex_table = (void *) __ex_table_start;
struct psw_t *psw_old = &S390_lowcore.program_old_psw;
int i, ex_table_cnt;
unsigned long fault, target;
ex_table_cnt = (__ex_table_stop - __ex_table_start)
/ sizeof(struct ex_table_entry);
for (i = 0; i < ex_table_cnt; i++) {
fault = __pa(&ex_table[i]) + ex_table[i].fault;
if (fault == psw_old->addr) {
target = __pa(&ex_table[i]) + ex_table[i].target;
psw_old->addr = target;
return;
}
}
libc_stop(psw_old->addr);
}
__attribute__ ((noinline)) void load_wait_psw(uint64_t psw_mask, struct psw_t *psw)
{
struct psw_t wait_psw = { .mask = psw_mask, .addr = 0 };
struct psw_t old_psw, *wait_psw_ptr = &wait_psw;
unsigned long addr;
old_psw = *psw;
psw->mask = 0x0000000180000000ULL;
asm volatile(
" larl %[addr],.Lwait\n"
" stg %[addr],8(%[wait_psw_ptr])\n"
" stg %[addr],8(%[psw])\n"
" lpswe %[wait_psw]\n"
".Lwait: \n"
: [addr] "=&d" (addr)
: [wait_psw] "Q" (wait_psw), [wait_psw_ptr] "a" (wait_psw_ptr),
[psw] "a" (psw)
: "cc", "memory");
*psw = old_psw;
}
/*
* The libc startup function
*/
void initialize(void)
{
struct psw_t *psw_pgm = &S390_lowcore.program_new_psw;
/* Setup program check handler */
psw_pgm->mask = 0x0000000180000000ULL;
psw_pgm->addr = (unsigned long) pgm_check_handler;
/* Clear bss section */
memset(__bss_start, 0, __bss_stop - __bss_start);
start();
}
/*
* Load disabled wait PSW with reason code in address field
*/
void libc_stop(unsigned long reason)
{
struct psw_t psw;
psw.mask = 0x0002000080000000ULL;
psw.addr = reason;
asm volatile(
" lpswe %[psw]\n"
: : [psw] "Q" (psw) : "cc"
);
while(1);
}