mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-23 23:23:02 +00:00
Initial commit
Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
commit
8af9f1eb95
27 changed files with 1859 additions and 0 deletions
7
.clang-format
Normal file
7
.clang-format
Normal file
|
@ -0,0 +1,7 @@
|
|||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Linux
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
IndentCaseLabels: true
|
14
.editorconfig
Normal file
14
.editorconfig
Normal file
|
@ -0,0 +1,14 @@
|
|||
root = true
|
||||
|
||||
# Defaults
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
|
||||
[Makefile*]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
!build/.keep
|
||||
build/
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "artwork"]
|
||||
path = artwork
|
||||
url = https://github.com/AsahiLinux/artwork.git
|
20
LICENSE
Normal file
20
LICENSE
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2021 The Asahi Linux controbutors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
66
Makefile
Normal file
66
Makefile
Normal file
|
@ -0,0 +1,66 @@
|
|||
ARCH := aarch64-linux-gnu-
|
||||
|
||||
CFLAGS := -O2 -Wall -Wundef -Werror=strict-prototypes -fno-common -fno-PIE \
|
||||
-Werror=implicit-function-declaration -Werror=implicit-int \
|
||||
-ffreestanding -mabi=lp64 -fpic
|
||||
LDFLAGS := -T m1n1.ld -EL -maarch64elf --no-undefined -X -shared -Bsymbolic \
|
||||
-z notext --no-apply-dynamic-relocs --orphan-handling=warn --strip-debug \
|
||||
-z nocopyreloc \
|
||||
|
||||
OBJECTS := bootlogo_128.o bootlogo_256.o fb.o main.o start.o startup.o \
|
||||
string.o uart.o utils.o utils_asm.o vsprintf.o
|
||||
|
||||
BUILD_OBJS := $(patsubst %,build/%,$(OBJECTS))
|
||||
NAME := m1n1
|
||||
TARGET := m1n1.macho
|
||||
|
||||
DEPDIR := build/.deps
|
||||
|
||||
CC := $(ARCH)gcc
|
||||
AS := $(ARCH)gcc
|
||||
LD := $(ARCH)ld
|
||||
OBJCOPY := $(ARCH)objcopy
|
||||
|
||||
.PHONY: all clean format
|
||||
all: build/$(TARGET)
|
||||
clean:
|
||||
rm -rf build/*
|
||||
format:
|
||||
clang-format -i src/*.c src/*.h
|
||||
|
||||
build/%.o: src/%.S
|
||||
@echo " AS $@"
|
||||
@mkdir -p $(DEPDIR)
|
||||
@$(AS) -c $(CFLAGS) -Wp,-MMD,$(DEPDIR)/$(*F).d,-MQ,"$@",-MP -o $@ $<
|
||||
|
||||
build/%.o: src/%.c
|
||||
@echo " CC $@"
|
||||
@mkdir -p $(DEPDIR)
|
||||
@$(CC) -c $(CFLAGS) -Wp,-MMD,$(DEPDIR)/$(*F).d,-MQ,"$@",-MP -o $@ $<
|
||||
|
||||
build/$(NAME).elf: $(BUILD_OBJS) m1n1.ld
|
||||
@echo " LD $@"
|
||||
@$(LD) $(LDFLAGS) -o $@ $(BUILD_OBJS)
|
||||
|
||||
build/$(NAME).macho: build/$(NAME).elf
|
||||
@echo " MACHO $@"
|
||||
@$(OBJCOPY) -O binary $< $@
|
||||
|
||||
build/build_tag.h:
|
||||
@echo " TAG $@"
|
||||
@echo "#define BUILD_TAG \"$$(git describe --always --dirty)\"" > $@
|
||||
|
||||
build/%.bin: data/%.png
|
||||
@echo " IMG $@"
|
||||
@convert $< -background black -flatten -depth 8 rgba:$@
|
||||
|
||||
build/%.o: build/%.bin
|
||||
@echo " BIN $@"
|
||||
@$(OBJCOPY) -I binary -O elf64-littleaarch64 $< $@
|
||||
|
||||
build/main.o: build/build_tag.h src/main.c
|
||||
|
||||
-include $(DEPDIR)/*
|
||||
|
||||
|
||||
|
16
README.md
Normal file
16
README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# m1n1: an experimentation playground for Apple Silicon
|
||||
|
||||
(And perhaps some day a Linux bootloader)
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2021 The Asahi Linux contributors
|
||||
|
||||
m1n1 is licensed under the MIT license, as included in the [LICENSE](LICENSE) file.
|
||||
|
||||
Please see the Git history for authorship information.
|
||||
|
||||
Portions of m1n1 are based on mini:
|
||||
Copyright (c) 2008-2010 Hector Martin "marcan" <marcan@marcan.st>
|
||||
Copyright (c) 2008-2010 Sven Peter <sven@svenpeter.dev>
|
||||
Copyright (c) 2008-2010 Andre Heider <a.heider@gmail.com>
|
1
artwork
Submodule
1
artwork
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 35626c31f5c5a8a884aaff93f864795026495742
|
1
data/bootlogo_128.png
Symbolic link
1
data/bootlogo_128.png
Symbolic link
|
@ -0,0 +1 @@
|
|||
../artwork/logos/png_128/AsahiLinux_logomark.png
|
1
data/bootlogo_256.png
Symbolic link
1
data/bootlogo_256.png
Symbolic link
|
@ -0,0 +1 @@
|
|||
../artwork/logos/png_256/AsahiLinux_logomark.png
|
185
m1n1.ld
Normal file
185
m1n1.ld
Normal file
|
@ -0,0 +1,185 @@
|
|||
ENTRY(_start)
|
||||
|
||||
/* Fake virtual load address for the mach-o */
|
||||
_va_base = 0xFFFFFE0007004000;
|
||||
|
||||
/* We are actually relocatable */
|
||||
_base = 0;
|
||||
|
||||
_stack_size = 0x20000;
|
||||
|
||||
. = _base;
|
||||
|
||||
PHDRS
|
||||
{
|
||||
hdr PT_LOAD;
|
||||
text PT_LOAD;
|
||||
rodata PT_LOAD;
|
||||
data PT_LOAD;
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.header : {
|
||||
_mach_header = .;
|
||||
/* mach-o header */
|
||||
LONG(0xfeedfacf); /* magic */
|
||||
LONG(0x100000c); /* cputype */
|
||||
LONG(0x02); /* cputype */
|
||||
LONG(0x0c); /* filetype */
|
||||
LONG(5); /* ncmds */
|
||||
LONG(_cmd_end - _cmd_start); /* sizeofcmds */
|
||||
LONG(4); /* flags */
|
||||
LONG(0); /* reserved */
|
||||
|
||||
_cmd_start = .;
|
||||
|
||||
/* unix_thread (entrypoint) */
|
||||
LONG(0x5); /* type = UNIX_THREAD */
|
||||
LONG(0x120); /* cmdsize */
|
||||
LONG(6); /* ARM_THREAD64 */
|
||||
LONG(0x44); /* length */
|
||||
. += 32 * 8; /* useless registers */
|
||||
QUAD(_start + _va_off) /* pc */
|
||||
. += 8; /* useless registers */
|
||||
|
||||
ASSERT(. - _cmd_start == 0x120, "Bad unix_thread structure");
|
||||
|
||||
/* segment: mach-o structures */
|
||||
LONG(0x19); /* type = SEGMENT_64 */
|
||||
LONG(0x48); /* cmdsize */
|
||||
LONG(0x5244485f); /* segname = "_HDR" */
|
||||
. += 12;
|
||||
QUAD(ADDR(.header) + _va_off); /* vmaddr */
|
||||
QUAD(SIZEOF(.header)); /* vmsize */
|
||||
QUAD(ADDR(.header) - _base); /* fileoff */
|
||||
QUAD(SIZEOF(.header)); /* filesize */
|
||||
LONG(PROT_READ); /* maxprot */
|
||||
LONG(PROT_READ); /* initprot */
|
||||
LONG(0); /* nsects */
|
||||
LONG(0); /* flags */
|
||||
|
||||
/* segment: text */
|
||||
LONG(0x19); /* type = SEGMENT_64 */
|
||||
LONG(0x48); /* cmdsize */
|
||||
LONG(0x54584554); /* segname = "TEXT" */
|
||||
. += 12;
|
||||
QUAD(ADDR(.init) + _va_off); /* vmaddr */
|
||||
QUAD(_text_size); /* vmsize */
|
||||
QUAD(ADDR(.init) - _base); /* fileoff */
|
||||
QUAD(_text_size); /* filesize */
|
||||
LONG(PROT_READ | PROT_EXECUTE); /* maxprot */
|
||||
LONG(PROT_READ | PROT_EXECUTE); /* initprot */
|
||||
LONG(0); /* nsects */
|
||||
LONG(0); /* flags */
|
||||
|
||||
/* segment: rodata */
|
||||
LONG(0x19); /* type = SEGMENT_64 */
|
||||
LONG(0x48); /* cmdsize */
|
||||
LONG(0x41444F52); /* segname = "RODA" */
|
||||
. += 12;
|
||||
QUAD(ADDR(.rodata) + _va_off); /* vmaddr */
|
||||
QUAD(_rodata_end - ADDR(.rodata)); /* vmsize */
|
||||
QUAD(ADDR(.rodata) - _base); /* fileoff */
|
||||
QUAD(_rodata_end - ADDR(.rodata)); /* filesize */
|
||||
LONG(PROT_READ); /* maxprot */
|
||||
LONG(PROT_READ); /* initprot */
|
||||
LONG(0); /* nsects */
|
||||
LONG(0); /* flags */
|
||||
|
||||
/* segment: data */
|
||||
LONG(0x19); /* type = SEGMENT_64 */
|
||||
LONG(0x48); /* cmdsize */
|
||||
LONG(0x41544144); /* segmname = "DATA" */
|
||||
. += 12;
|
||||
QUAD(ADDR(.data) + _va_off); /* vmaddr */
|
||||
QUAD(_data_size); /* vmsize */
|
||||
QUAD(ADDR(.data) - _base); /* fileoff */
|
||||
QUAD(SIZEOF(.data)); /* filesize */
|
||||
LONG(PROT_READ | PROT_WRITE); /* maxprot */
|
||||
LONG(PROT_READ | PROT_WRITE); /* initprot */
|
||||
LONG(0); /* nsects */
|
||||
LONG(0); /* flags */
|
||||
|
||||
_cmd_end = .;
|
||||
|
||||
. = ALIGN(0x8000);
|
||||
_hdr_end = .;
|
||||
} :hdr
|
||||
_text_start = .;
|
||||
.init : ALIGN(0x8000) {
|
||||
*(.init)
|
||||
*(.init.*)
|
||||
} :text
|
||||
.text : ALIGN(0x8000) {
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
. = ALIGN(8);
|
||||
*(.got.plt)
|
||||
. = ALIGN(0x8000);
|
||||
} :text
|
||||
_text_size = . - _text_start;
|
||||
.rodata : ALIGN(0x8000) {
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
. = ALIGN(8);
|
||||
} :rodata
|
||||
.rela.dyn : {
|
||||
_rela_start = .;
|
||||
*(.rela)
|
||||
*(.rela.*)
|
||||
_rela_end = .;
|
||||
. = ALIGN(0x8000);
|
||||
} :rodata
|
||||
_rodata_end = .;
|
||||
_data_start = .;
|
||||
.data : ALIGN(0x8000) {
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
. = ALIGN(8);
|
||||
_got_start = .;
|
||||
*(.got)
|
||||
_got_end = .;
|
||||
. = ALIGN(0x8000);
|
||||
} :data
|
||||
.bss : ALIGN(0x8000) {
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.dynbss)
|
||||
*(COMMON)
|
||||
. = ALIGN(0x8000);
|
||||
PROVIDE(_stack_top = .);
|
||||
. += _stack_size;
|
||||
PROVIDE(_stack_bot = .);
|
||||
. = ALIGN(0x8000);
|
||||
} :data
|
||||
_data_size = . - _data_start;
|
||||
_end = .;
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.discard)
|
||||
*(.discard.*)
|
||||
*(.interp .dynamic)
|
||||
*(.dynsym .dynstr .hash .gnu.hash)
|
||||
*(.eh_frame)
|
||||
*(.gnu.version*)
|
||||
*(.note*)
|
||||
*(.comment*)
|
||||
}
|
||||
|
||||
.empty (NOLOAD) : {
|
||||
*(.plt) *(.plt.*) *(.iplt) *(.igot)
|
||||
*(.data.rel.ro)
|
||||
}
|
||||
ASSERT(SIZEOF(.empty) == 0, "Unexpected sections detected!")
|
||||
|
||||
.got.plt (NOLOAD) : {
|
||||
*(.got.plt)
|
||||
}
|
||||
ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT PLT detected!")
|
||||
}
|
||||
|
||||
PROT_READ = 0x01;
|
||||
PROT_WRITE = 0x02;
|
||||
PROT_EXECUTE = 0x04;
|
||||
|
||||
_va_off = _va_base - _base;
|
62
src/fb.c
Normal file
62
src/fb.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "fb.h"
|
||||
#include "utils.h"
|
||||
#include "xnuboot.h"
|
||||
|
||||
u32 *fb;
|
||||
int fb_s, fb_w, fb_h;
|
||||
|
||||
static u32 *logo;
|
||||
static int logo_w, logo_h;
|
||||
|
||||
extern char _binary_build_bootlogo_128_bin_start[0];
|
||||
extern char _binary_build_bootlogo_256_bin_start[0];
|
||||
|
||||
void fb_init(void)
|
||||
{
|
||||
fb = (void *)cur_boot_args.video.base;
|
||||
fb_s = cur_boot_args.video.stride / 4;
|
||||
fb_w = cur_boot_args.video.width;
|
||||
fb_h = cur_boot_args.video.height;
|
||||
printf("fb init: %dx%d [s=%d] @%p\n", fb_w, fb_h, fb_s, fb);
|
||||
|
||||
if (fb_w > 2048) { // random guess
|
||||
logo = (void *)_binary_build_bootlogo_256_bin_start;
|
||||
logo_w = logo_h = 256;
|
||||
} else {
|
||||
logo = (void *)_binary_build_bootlogo_128_bin_start;
|
||||
logo_w = logo_h = 128;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t rgbx_to_rgb30(u32 c)
|
||||
{
|
||||
u8 r = c;
|
||||
u8 g = c >> 8;
|
||||
u8 b = c >> 16;
|
||||
return (b << 2) | (g << 12) | (r << 22);
|
||||
}
|
||||
|
||||
void fb_blit(int x, int y, int w, int h, void *data, int stride)
|
||||
{
|
||||
uint32_t *p = data;
|
||||
|
||||
for (int i = 0; i < h; i++)
|
||||
for (int j = 0; j < w; j++)
|
||||
fb[x + j + (y + i) * fb_s] = rgbx_to_rgb30(p[j + i * stride]);
|
||||
}
|
||||
|
||||
void fb_fill(int x, int y, int w, int h, u32 color)
|
||||
{
|
||||
for (int i = 0; i < h; i++)
|
||||
for (int j = 0; j < w; j++)
|
||||
fb[x + j + (y + i) * fb_s] = rgbx_to_rgb30(color);
|
||||
}
|
||||
|
||||
void fb_display_logo(void)
|
||||
{
|
||||
printf("fb: display logo\n");
|
||||
fb_blit((fb_w - logo_w) / 2, (fb_h - logo_h) / 2, logo_w, logo_h, logo,
|
||||
logo_w);
|
||||
}
|
17
src/fb.h
Normal file
17
src/fb.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef FB_H
|
||||
#define FB_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
extern u32 *fb;
|
||||
extern int fb_s, fb_w, fb_h;
|
||||
|
||||
void fb_init(void);
|
||||
void fb_blit(int x, int y, int w, int h, void *data, int stride);
|
||||
void fb_fill(int x, int y, int w, int h, u32 color);
|
||||
|
||||
void fb_display_logo(void);
|
||||
|
||||
#endif
|
25
src/main.c
Normal file
25
src/main.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "fb.h"
|
||||
#include "utils.h"
|
||||
#include "xnuboot.h"
|
||||
|
||||
#include "../build/build_tag.h"
|
||||
|
||||
void m1n1_main(void)
|
||||
{
|
||||
printf("\n\nm1n1 v%s\n", BUILD_TAG);
|
||||
printf("Copyright (C) 2021 The Asahi Linux Contributors\n");
|
||||
printf("Licensed under the MIT license\n");
|
||||
|
||||
fb_init();
|
||||
fb_display_logo();
|
||||
|
||||
u64 dtaddr = ((u64)cur_boot_args.devtree) - cur_boot_args.virt_base +
|
||||
cur_boot_args.phys_base;
|
||||
|
||||
hexdump((void *)dtaddr, cur_boot_args.devtree_size);
|
||||
|
||||
while (1)
|
||||
;
|
||||
}
|
61
src/start.S
Normal file
61
src/start.S
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#define UART_BASE 0x235200000
|
||||
#define UTRSTAT 0x010
|
||||
#define UTXH 0x020
|
||||
|
||||
.extern _start_c
|
||||
.extern _stack_bot
|
||||
|
||||
.section .init, "ax"
|
||||
|
||||
.globl _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
mov x9, x0
|
||||
|
||||
mov w0, 'm'
|
||||
bl debug_putc
|
||||
|
||||
mov w0, '1'
|
||||
bl debug_putc
|
||||
|
||||
adrp x1, _stack_bot
|
||||
mov sp, x1
|
||||
|
||||
mov w0, 'n'
|
||||
bl debug_putc
|
||||
|
||||
adrp x0, _base
|
||||
mov x10, x0
|
||||
adrp x1, _rela_start
|
||||
add x1, x1, :lo12:_rela_start
|
||||
adrp x2, _rela_end
|
||||
add x2, x2, :lo12:_rela_end
|
||||
bl apply_rela
|
||||
|
||||
mov w0, '1'
|
||||
bl debug_putc
|
||||
mov w0, '\r'
|
||||
bl debug_putc
|
||||
mov w0, '\n'
|
||||
bl debug_putc
|
||||
|
||||
mov x0, x9
|
||||
mov x1, x10
|
||||
bl _start_c
|
||||
b .
|
||||
|
||||
.globl debug_putc
|
||||
.type debug_putc, @function
|
||||
debug_putc:
|
||||
ldr x1, =UART_BASE
|
||||
|
||||
1:
|
||||
ldr w2, [x1, UTRSTAT]
|
||||
tst w2, #2
|
||||
beq 1b
|
||||
str w0, [x1, UTXH]
|
||||
ret
|
||||
|
||||
.pool
|
77
src/startup.c
Normal file
77
src/startup.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "string.h"
|
||||
#include "types.h"
|
||||
#include "uart.h"
|
||||
#include "utils.h"
|
||||
#include "xnuboot.h"
|
||||
|
||||
struct boot_args cur_boot_args;
|
||||
|
||||
struct rela_entry {
|
||||
uint64_t off, type, addend;
|
||||
};
|
||||
|
||||
void debug_putc(char c);
|
||||
void m1n1_main(void);
|
||||
|
||||
#define R_AARCH64_RELATIVE 1027
|
||||
|
||||
void apply_rela(uint64_t base, struct rela_entry *rela_start,
|
||||
struct rela_entry *rela_end)
|
||||
{
|
||||
struct rela_entry *e = rela_start;
|
||||
|
||||
while (e < rela_end) {
|
||||
switch (e->type) {
|
||||
case R_AARCH64_RELATIVE:
|
||||
*(u64 *)(base + e->off) = base + e->addend;
|
||||
break;
|
||||
default:
|
||||
debug_putc('R');
|
||||
debug_putc('!');
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
e++;
|
||||
}
|
||||
}
|
||||
|
||||
void dump_boot_args(struct boot_args *ba)
|
||||
{
|
||||
printf(" revision: %d\n", ba->revision);
|
||||
printf(" version: %d\n", ba->version);
|
||||
printf(" virt_base: 0x%lx\n", ba->virt_base);
|
||||
printf(" phys_base: 0x%lx\n", ba->phys_base);
|
||||
printf(" mem_size: 0x%lx\n", ba->mem_size);
|
||||
printf(" top_of_kdata: 0x%lx\n", ba->top_of_kernel_data);
|
||||
printf(" video:\n");
|
||||
printf(" base: 0x%lx\n", ba->video.base);
|
||||
printf(" display: 0x%lx\n", ba->video.display);
|
||||
printf(" stride: 0x%lx\n", ba->video.stride);
|
||||
printf(" width: %d\n", ba->video.width);
|
||||
printf(" height: %d\n", ba->video.height);
|
||||
printf(" depth: 0x%lx\n", ba->video.depth);
|
||||
printf(" machine_type: %d\n", ba->machine_type);
|
||||
printf(" devtree: %p\n", ba->devtree);
|
||||
printf(" devtree_size: 0x%x\n", ba->devtree_size);
|
||||
printf(" cmdline: %s\n", ba->cmdline);
|
||||
printf(" boot_flags: 0x%x\n", ba->boot_flags);
|
||||
printf(" mem_size_act: 0x%lx\n", ba->mem_size_actual);
|
||||
}
|
||||
|
||||
void _start_c(void *boot_args, void *base)
|
||||
{
|
||||
uart_putc('s');
|
||||
uart_init();
|
||||
uart_putc('c');
|
||||
uart_puts(": Initializing\n");
|
||||
|
||||
printf("boot_args at %p\n", boot_args);
|
||||
|
||||
memcpy(&cur_boot_args, boot_args, sizeof(cur_boot_args));
|
||||
|
||||
dump_boot_args(&cur_boot_args);
|
||||
|
||||
m1n1_main();
|
||||
}
|
86
src/string.c
Normal file
86
src/string.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Routines based on The Public Domain C Library
|
||||
|
||||
void *memcpy(void *s1, const void *s2, size_t n)
|
||||
{
|
||||
char *dest = (char *)s1;
|
||||
const char *src = (const char *)s2;
|
||||
|
||||
while (n--) {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
return s1;
|
||||
}
|
||||
|
||||
int memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
const unsigned char *p1 = (const unsigned char *)s1;
|
||||
const unsigned char *p2 = (const unsigned char *)s2;
|
||||
|
||||
while (n--) {
|
||||
if (*p1 != *p2) {
|
||||
return *p1 - *p2;
|
||||
}
|
||||
|
||||
++p1;
|
||||
++p2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)s;
|
||||
|
||||
while (n--) {
|
||||
*p++ = (unsigned char)c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
char *strcpy(char *s1, const char *s2)
|
||||
{
|
||||
char *rc = s1;
|
||||
|
||||
while ((*s1++ = *s2++)) {
|
||||
/* EMPTY */
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
char *strncpy(char *s1, const char *s2, size_t n)
|
||||
{
|
||||
char *rc = s1;
|
||||
|
||||
while (n && (*s1++ = *s2++)) {
|
||||
/* Cannot do "n--" in the conditional as size_t is unsigned and we have
|
||||
to check it again for >0 in the next loop below, so we must not risk
|
||||
underflow.
|
||||
*/
|
||||
--n;
|
||||
}
|
||||
|
||||
/* Checking against 1 as we missed the last --n in the loop above. */
|
||||
while (n-- > 1) {
|
||||
*s1++ = '\0';
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int strcmp(const char *s1, const char *s2)
|
||||
{
|
||||
while ((*s1) && (*s1 == *s2)) {
|
||||
++s1;
|
||||
++s2;
|
||||
}
|
||||
|
||||
return (*(unsigned char *)s1 - *(unsigned char *)s2);
|
||||
}
|
15
src/string.h
Normal file
15
src/string.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef STRING_H
|
||||
#define STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void *memcpy(void *s1, const void *s2, size_t n);
|
||||
int memcmp(const void *s1, const void *s2, size_t n);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
char *strncpy(char *s1, const char *s2, size_t n);
|
||||
int strcmp(const char *s1, const char *s2);
|
||||
|
||||
#endif
|
49
src/types.h
Normal file
49
src/types.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef u64 uintptr_t;
|
||||
typedef s64 ptrdiff_t;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
#define ALIGNED(x) __attribute__((aligned(x)))
|
||||
|
||||
#define STACK_ALIGN(type, name, cnt, alignment) \
|
||||
u8 _al__##name[( \
|
||||
(sizeof(type) * (cnt)) + (alignment) + \
|
||||
(((sizeof(type) * (cnt)) % (alignment)) > 0 \
|
||||
? ((alignment) - ((sizeof(type) * (cnt)) % (alignment))) \
|
||||
: 0))]; \
|
||||
type *name = \
|
||||
(type *)(((u32)(_al__##name)) + \
|
||||
((alignment) - (((u32)(_al__##name)) & ((alignment)-1))))
|
||||
|
||||
#define INT_MAX ((int)0x7fffffff)
|
||||
#define UINT_MAX ((unsigned int)0xffffffff)
|
||||
|
||||
#define LONG_MAX ((long)0x7fffffffffffffffl)
|
||||
#define ULONG_MAX ((unsigned long)0xfffffffffffffffful)
|
||||
|
||||
#define LLONG_MAX LONG_MAX
|
||||
#define ULLONG_MAX ULLONG_MAX
|
||||
|
||||
#define HAVE_PTRDIFF_T 1
|
||||
#define HAVE_UINTPTR_T 1
|
||||
#define UPTRDIFF_T uintptr_t
|
||||
|
||||
#endif
|
63
src/uart.c
Normal file
63
src/uart.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "uart.h"
|
||||
#include "utils.h"
|
||||
#include "vsprintf.h"
|
||||
|
||||
#define UART_BASE 0x235200000L
|
||||
|
||||
#define ULCON 0x000
|
||||
#define UCON 0x004
|
||||
#define UFCON 0x008
|
||||
#define UTRSTAT 0x010
|
||||
#define UTXH 0x020
|
||||
#define URXH 0x024
|
||||
#define UBRDIV 0x028
|
||||
#define UFRACVAL 0x02c
|
||||
|
||||
void *pxx = uart_init;
|
||||
|
||||
void uart_init(void)
|
||||
{
|
||||
/* keep UART config from iBoot */
|
||||
}
|
||||
|
||||
void uart_putbyte(u8 c)
|
||||
{
|
||||
while (!(read32(UART_BASE + UTRSTAT) & 0x02))
|
||||
;
|
||||
|
||||
write32(UART_BASE + UTXH, c);
|
||||
}
|
||||
|
||||
void uart_putc(u8 c)
|
||||
{
|
||||
if (c == '\n')
|
||||
uart_putbyte('\r');
|
||||
|
||||
uart_putbyte(c);
|
||||
}
|
||||
|
||||
void uart_puts(const char *s)
|
||||
{
|
||||
while (*s)
|
||||
uart_putc(*(s++));
|
||||
|
||||
uart_putc('\n');
|
||||
}
|
||||
|
||||
void uart_write(const void *buf, size_t count)
|
||||
{
|
||||
const u8 *p = buf;
|
||||
|
||||
while (count--)
|
||||
uart_putbyte(*p++);
|
||||
}
|
||||
|
||||
u8 uart_getc(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
15
src/uart.h
Normal file
15
src/uart.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef UART_H
|
||||
#define UART_H
|
||||
|
||||
void uart_init(void);
|
||||
|
||||
void uart_putc(u8 c);
|
||||
u8 uart_getc(void);
|
||||
|
||||
void uart_write(const void *buf, size_t count);
|
||||
|
||||
void uart_puts(const char *s);
|
||||
|
||||
#endif
|
80
src/utils.c
Normal file
80
src/utils.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "utils.h"
|
||||
#include "types.h"
|
||||
#include "uart.h"
|
||||
#include "vsprintf.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static char ascii(char s)
|
||||
{
|
||||
if (s < 0x20)
|
||||
return '.';
|
||||
if (s > 0x7E)
|
||||
return '.';
|
||||
return s;
|
||||
}
|
||||
|
||||
void hexdump(const void *d, int len)
|
||||
{
|
||||
u8 *data;
|
||||
int i, off;
|
||||
data = (u8 *)d;
|
||||
for (off = 0; off < len; off += 16) {
|
||||
printf("%08x ", off);
|
||||
for (i = 0; i < 16; i++) {
|
||||
if ((i + off) >= len)
|
||||
printf(" ");
|
||||
else
|
||||
printf("%02x ", data[off + i]);
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
for (i = 0; i < 16; i++) {
|
||||
if ((i + off) >= len)
|
||||
printf(" ");
|
||||
else
|
||||
printf("%c", ascii(data[off + i]));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void regdump(u64 addr, int len)
|
||||
{
|
||||
u64 i, off;
|
||||
for (off = 0; off < len; off += 32) {
|
||||
printf("%016x ", addr + off);
|
||||
for (i = 0; i < 32; i += 4) {
|
||||
printf("%08x ", read32(addr + off + i));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int sprintf(char *buffer, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsprintf(buffer, fmt, args);
|
||||
va_end(args);
|
||||
return i;
|
||||
}
|
||||
|
||||
int debug_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buffer[512];
|
||||
int i;
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsprintf(buffer, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
uart_write(buffer, i);
|
||||
|
||||
return i;
|
||||
}
|
167
src/utils.h
Normal file
167
src/utils.h
Normal file
|
@ -0,0 +1,167 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define printf debug_printf
|
||||
|
||||
static inline u32 read32(u64 addr)
|
||||
{
|
||||
u32 data;
|
||||
__asm__ volatile("ldr\t%w0, [%1]" : "=r"(data) : "r"(addr));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void write32(u64 addr, u32 data)
|
||||
{
|
||||
__asm__ volatile("str\t%w0, [%1]" : : "r"(data), "r"(addr));
|
||||
}
|
||||
|
||||
static inline u32 set32(u64 addr, u32 set)
|
||||
{
|
||||
u32 data;
|
||||
__asm__ volatile("ldr\t%w0, [%1]\n"
|
||||
"\torr\t%w0, %w0, %w2\n"
|
||||
"\tstr\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(set));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u32 clear32(u64 addr, u32 clear)
|
||||
{
|
||||
u32 data;
|
||||
__asm__ volatile("ldr\t%w0, [%1]\n"
|
||||
"\tbic\t%w0, %w0, %w2\n"
|
||||
"\tstr\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(clear));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u32 mask32(u64 addr, u32 clear, u32 set)
|
||||
{
|
||||
u32 data;
|
||||
__asm__ volatile("ldr\t%w0, [%1]\n"
|
||||
"\tbic\t%w0, %w0, %w3\n"
|
||||
"\torr\t%w0, %w0, %w2\n"
|
||||
"\tstr\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(set), "r"(clear));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u16 read16(u64 addr)
|
||||
{
|
||||
u32 data;
|
||||
__asm__ volatile("ldrh\t%w0, [%1]" : "=r"(data) : "r"(addr));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void write16(u64 addr, u16 data)
|
||||
{
|
||||
__asm__ volatile("strh\t%w0, [%1]" : : "r"(data), "r"(addr));
|
||||
}
|
||||
|
||||
static inline u16 set16(u64 addr, u16 set)
|
||||
{
|
||||
u16 data;
|
||||
__asm__ volatile("ldrh\t%w0, [%1]\n"
|
||||
"\torr\t%w0, %w0, %w2\n"
|
||||
"\tstrh\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(set)
|
||||
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u16 clear16(u64 addr, u16 clear)
|
||||
{
|
||||
u16 data;
|
||||
__asm__ volatile("ldrh\t%w0, [%1]\n"
|
||||
"\tbic\t%w0, %w0, %w2\n"
|
||||
"\tstrh\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(clear));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u16 mask16(u64 addr, u16 clear, u16 set)
|
||||
{
|
||||
u16 data;
|
||||
__asm__ volatile("ldrh\t%w0, [%1]\n"
|
||||
"\tbic\t%w0, %3\n"
|
||||
"\torr\t%w0, %w0, %w2\n"
|
||||
"\tstrh\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(set), "r"(clear));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u8 read8(u64 addr)
|
||||
{
|
||||
u32 data;
|
||||
__asm__ volatile("ldrb\t%w0, [%1]" : "=r"(data) : "r"(addr));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void write8(u64 addr, u8 data)
|
||||
{
|
||||
__asm__ volatile("strb\t%w0, [%1]" : : "r"(data), "r"(addr));
|
||||
}
|
||||
|
||||
static inline u8 set8(u64 addr, u8 set)
|
||||
{
|
||||
u8 data;
|
||||
__asm__ volatile("ldrb\t%w0, [%1]\n"
|
||||
"\torr\t%w0, %w0, %w2\n"
|
||||
"\tstrb\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(set));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u8 clear8(u64 addr, u8 clear)
|
||||
{
|
||||
u8 data;
|
||||
__asm__ volatile("ldrb\t%w0, [%1]\n"
|
||||
"\tbic\t%w0, %w0, %w2\n"
|
||||
"\tstrb\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(clear));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u8 mask8(u64 addr, u8 clear, u8 set)
|
||||
{
|
||||
u8 data;
|
||||
__asm__ volatile("ldrb\t%w0, [%1]\n"
|
||||
"\tbic\t%w0, %w0, %w3\n"
|
||||
"\torr\t%w0, %w0, %w2\n"
|
||||
"\tstrb\t%w0, [%1]"
|
||||
: "=&r"(data)
|
||||
: "r"(addr), "r"(set), "r"(clear));
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions are guaranteed to copy by reading from src and writing to dst
|
||||
* in <n>-bit units If size is not aligned, the remaining bytes are not copied
|
||||
*/
|
||||
void memset32(void *dst, u32 value, u32 size);
|
||||
void memcpy32(void *dst, void *src, u32 size);
|
||||
void memset16(void *dst, u16 value, u32 size);
|
||||
void memcpy16(void *dst, void *src, u32 size);
|
||||
void memset8(void *dst, u8 value, u32 size);
|
||||
void memcpy8(void *dst, void *src, u32 size);
|
||||
|
||||
void hexdump(const void *d, int len);
|
||||
void regdump(u64 addr, int len);
|
||||
int sprintf(char *str, const char *fmt, ...);
|
||||
int debug_printf(const char *fmt, ...);
|
||||
void udelay(u32 d);
|
||||
|
||||
#endif
|
72
src/utils_asm.S
Normal file
72
src/utils_asm.S
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
.text
|
||||
|
||||
.globl memcpy32
|
||||
.type memcpy32, @function
|
||||
memcpy32:
|
||||
ands x2, x2, #~3
|
||||
beq 2f
|
||||
1: ldr x3, [x1], #4
|
||||
str w3, [x0], #4
|
||||
subs x2, x2, #4
|
||||
bne 1b
|
||||
2:
|
||||
ret
|
||||
|
||||
.globl memset32
|
||||
.type memset32, @function
|
||||
memset32:
|
||||
ands x2, x2, #~3
|
||||
beq 2f
|
||||
1: str w1, [x0], #4
|
||||
subs x2, x2, #4
|
||||
bne 1b
|
||||
2:
|
||||
ret
|
||||
|
||||
.globl memcpy16
|
||||
.type memcpy16, @function
|
||||
memcpy16:
|
||||
ands x2, x2, #~1
|
||||
beq 2f
|
||||
1: ldrh w3, [x1], #2
|
||||
strh w3, [x0], #2
|
||||
subs x2, x2, #2
|
||||
bne 1b
|
||||
2:
|
||||
ret
|
||||
|
||||
.globl memset16
|
||||
.type memset16, @function
|
||||
memset16:
|
||||
ands x2, x2, #~1
|
||||
beq 2f
|
||||
1: strh w1, [x0], #2
|
||||
subs x2, x2, #2
|
||||
bne 1b
|
||||
2:
|
||||
ret
|
||||
|
||||
.globl memcpy8
|
||||
.type memcpy8, @function
|
||||
memcpy8:
|
||||
cmp x2, #0
|
||||
beq 2f
|
||||
1: ldrb w3, [x1], #1
|
||||
strb w3, [x0], #1
|
||||
subs x2, x2, #1
|
||||
bne 1b
|
||||
2:
|
||||
ret
|
||||
|
||||
.globl memset8
|
||||
.type memset8, @function
|
||||
memset8:
|
||||
cmp x2, #0
|
||||
beq 2f
|
||||
1: strb w1, [x0], #1
|
||||
subs x2, x2, #1
|
||||
bne 1b
|
||||
2:
|
||||
ret
|
708
src/vsprintf.c
Normal file
708
src/vsprintf.c
Normal file
|
@ -0,0 +1,708 @@
|
|||
/*
|
||||
* Copyright (c) 1995 Patrick Powell.
|
||||
*
|
||||
* This code is based on code written by Patrick Powell <papowell@astart.com>.
|
||||
* It may be used for any purpose as long as this notice remains intact on all
|
||||
* source code distributions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Holger Weiss.
|
||||
*
|
||||
* This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
|
||||
* My changes to the code may freely be used, modified and/or redistributed for
|
||||
* any purpose. It would be nice if additions and fixes to this file (including
|
||||
* trivial code cleanups) would be sent back in order to let me include them in
|
||||
* the version available at <http://www.jhweiss.de/software/snprintf.html>.
|
||||
* However, this is not a requirement for using or redistributing (possibly
|
||||
* modified) versions of this file, nor is leaving this notice intact mandatory.
|
||||
*/
|
||||
|
||||
/*
|
||||
* History
|
||||
*
|
||||
* 2009-03-05 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
*
|
||||
* Hacked up and removed a lot of stuff including floating-point support,
|
||||
* a bunch of ifs and defines, locales, and tests
|
||||
*
|
||||
* 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
|
||||
*
|
||||
* Fixed the detection of infinite floating point values on IRIX (and
|
||||
* possibly other systems) and applied another few minor cleanups.
|
||||
*
|
||||
* 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
|
||||
*
|
||||
* Added a lot of new features, fixed many bugs, and incorporated various
|
||||
* improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
|
||||
* <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
|
||||
* <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
|
||||
* projects. The additions include: support the "e", "E", "g", "G", and
|
||||
* "F" conversion specifiers (and use conversion style "f" or "F" for the
|
||||
* still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
|
||||
* "t", and "z" length modifiers; support the "#" flag and the (non-C99)
|
||||
* "'" flag; use localeconv(3) (if available) to get both the current
|
||||
* locale's decimal point character and the separator between groups of
|
||||
* digits; fix the handling of various corner cases of field width and
|
||||
* precision specifications; fix various floating point conversion bugs;
|
||||
* handle infinite and NaN floating point values; don't attempt to write to
|
||||
* the output buffer (which may be NULL) if a size of zero was specified;
|
||||
* check for integer overflow of the field width, precision, and return
|
||||
* values and during the floating point conversion; use the OUTCHAR() macro
|
||||
* instead of a function for better performance; provide asprintf(3) and
|
||||
* vasprintf(3) functions; add new test cases. The replacement functions
|
||||
* have been renamed to use an "rpl_" prefix, the function calls in the
|
||||
* main project (and in this file) must be redefined accordingly for each
|
||||
* replacement function which is needed (by using Autoconf or other means).
|
||||
* Various other minor improvements have been applied and the coding style
|
||||
* was cleaned up for consistency.
|
||||
*
|
||||
* 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
|
||||
*
|
||||
* C99 compliant snprintf(3) and vsnprintf(3) functions return the number
|
||||
* of characters that would have been written to a sufficiently sized
|
||||
* buffer (excluding the '\0'). The original code simply returned the
|
||||
* length of the resulting output string, so that's been fixed.
|
||||
*
|
||||
* 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
|
||||
*
|
||||
* The original code assumed that both snprintf(3) and vsnprintf(3) were
|
||||
* missing. Some systems only have snprintf(3) but not vsnprintf(3), so
|
||||
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
|
||||
*
|
||||
* 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
|
||||
*
|
||||
* The PGP code was using unsigned hexadecimal formats. Unfortunately,
|
||||
* unsigned formats simply didn't work.
|
||||
*
|
||||
* 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
|
||||
*
|
||||
* Ok, added some minimal floating point support, which means this probably
|
||||
* requires libm on most operating systems. Don't yet support the exponent
|
||||
* (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just
|
||||
* wasn't being exercised in ways which showed it, so that's been fixed.
|
||||
* Also, formatted the code to Mutt conventions, and removed dead code left
|
||||
* over from the original. Also, there is now a builtin-test, run with:
|
||||
* gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
|
||||
*
|
||||
* 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
|
||||
*
|
||||
* This was ugly. It is still ugly. I opted out of floating point
|
||||
* numbers, but the formatter understands just about everything from the
|
||||
* normal C string format, at least as far as I can tell from the Solaris
|
||||
* 2.5 printf(3S) man page.
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#define VA_START(ap, last) va_start(ap, last)
|
||||
#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
|
||||
|
||||
#define ULLONG unsigned long long
|
||||
#define UINTMAX_T unsigned long
|
||||
#define LLONG long
|
||||
#define INTMAX_T long
|
||||
|
||||
/* Support for uintptr_t. */
|
||||
#ifndef UINTPTR_T
|
||||
#if HAVE_UINTPTR_T || defined(uintptr_t)
|
||||
#define UINTPTR_T uintptr_t
|
||||
#else
|
||||
#define UINTPTR_T unsigned long int
|
||||
#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */
|
||||
#endif /* !defined(UINTPTR_T) */
|
||||
|
||||
/* Support for ptrdiff_t. */
|
||||
#ifndef PTRDIFF_T
|
||||
#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
|
||||
#define PTRDIFF_T ptrdiff_t
|
||||
#else
|
||||
#define PTRDIFF_T long int
|
||||
#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
|
||||
#endif /* !defined(PTRDIFF_T) */
|
||||
|
||||
/*
|
||||
* We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
|
||||
* 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an
|
||||
* unsigned type if necessary. This should work just fine in practice.
|
||||
*/
|
||||
#ifndef UPTRDIFF_T
|
||||
#define UPTRDIFF_T PTRDIFF_T
|
||||
#endif /* !defined(UPTRDIFF_T) */
|
||||
|
||||
/*
|
||||
* We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
|
||||
* However, we'll simply use size_t and convert it to a signed type if
|
||||
* necessary. This should work just fine in practice.
|
||||
*/
|
||||
#ifndef SSIZE_T
|
||||
#define SSIZE_T size_t
|
||||
#endif /* !defined(SSIZE_T) */
|
||||
|
||||
/*
|
||||
* Buffer size to hold the octal string representation of UINT128_MAX without
|
||||
* nul-termination ("3777777777777777777777777777777777777777777").
|
||||
*/
|
||||
#ifdef MAX_CONVERT_LENGTH
|
||||
#undef MAX_CONVERT_LENGTH
|
||||
#endif /* defined(MAX_CONVERT_LENGTH) */
|
||||
#define MAX_CONVERT_LENGTH 43
|
||||
|
||||
/* Format read states. */
|
||||
#define PRINT_S_DEFAULT 0
|
||||
#define PRINT_S_FLAGS 1
|
||||
#define PRINT_S_WIDTH 2
|
||||
#define PRINT_S_DOT 3
|
||||
#define PRINT_S_PRECISION 4
|
||||
#define PRINT_S_MOD 5
|
||||
#define PRINT_S_CONV 6
|
||||
|
||||
/* Format flags. */
|
||||
#define PRINT_F_MINUS (1 << 0)
|
||||
#define PRINT_F_PLUS (1 << 1)
|
||||
#define PRINT_F_SPACE (1 << 2)
|
||||
#define PRINT_F_NUM (1 << 3)
|
||||
#define PRINT_F_ZERO (1 << 4)
|
||||
#define PRINT_F_QUOTE (1 << 5)
|
||||
#define PRINT_F_UP (1 << 6)
|
||||
#define PRINT_F_UNSIGNED (1 << 7)
|
||||
#define PRINT_F_TYPE_G (1 << 8)
|
||||
#define PRINT_F_TYPE_E (1 << 9)
|
||||
|
||||
/* Conversion flags. */
|
||||
#define PRINT_C_CHAR 1
|
||||
#define PRINT_C_SHORT 2
|
||||
#define PRINT_C_LONG 3
|
||||
#define PRINT_C_LLONG 4
|
||||
//#define PRINT_C_LDOUBLE 5
|
||||
#define PRINT_C_SIZE 6
|
||||
#define PRINT_C_PTRDIFF 7
|
||||
#define PRINT_C_INTMAX 8
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x >= y) ? x : y)
|
||||
#endif /* !defined(MAX) */
|
||||
#ifndef CHARTOINT
|
||||
#define CHARTOINT(ch) (ch - '0')
|
||||
#endif /* !defined(CHARTOINT) */
|
||||
#ifndef ISDIGIT
|
||||
#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
|
||||
#endif /* !defined(ISDIGIT) */
|
||||
|
||||
#define OUTCHAR(str, len, size, ch) \
|
||||
do { \
|
||||
if (len + 1 < size) \
|
||||
str[len] = ch; \
|
||||
(len)++; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
|
||||
static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
|
||||
static void printsep(char *, size_t *, size_t);
|
||||
static int getnumsep(int);
|
||||
static int convert(UINTMAX_T, char *, size_t, int, int);
|
||||
|
||||
int vsnprintf(char *str, size_t size, const char *format, va_list args)
|
||||
{
|
||||
INTMAX_T value;
|
||||
unsigned char cvalue;
|
||||
const char *strvalue;
|
||||
INTMAX_T *intmaxptr;
|
||||
PTRDIFF_T *ptrdiffptr;
|
||||
SSIZE_T *sizeptr;
|
||||
LLONG *llongptr;
|
||||
long int *longptr;
|
||||
int *intptr;
|
||||
short int *shortptr;
|
||||
signed char *charptr;
|
||||
size_t len = 0;
|
||||
int overflow = 0;
|
||||
int base = 0;
|
||||
int cflags = 0;
|
||||
int flags = 0;
|
||||
int width = 0;
|
||||
int precision = -1;
|
||||
int state = PRINT_S_DEFAULT;
|
||||
char ch = *format++;
|
||||
|
||||
/*
|
||||
* C99 says: "If `n' is zero, nothing is written, and `s' may be a null
|
||||
* pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer
|
||||
* even if a size larger than zero was specified. At least NetBSD's
|
||||
* snprintf(3) does the same, as well as other versions of this file.
|
||||
* (Though some of these versions will write to a non-NULL buffer even
|
||||
* if a size of zero was specified, which violates the standard.)
|
||||
*/
|
||||
if (str == NULL && size != 0)
|
||||
size = 0;
|
||||
|
||||
while (ch != '\0')
|
||||
switch (state) {
|
||||
case PRINT_S_DEFAULT:
|
||||
if (ch == '%')
|
||||
state = PRINT_S_FLAGS;
|
||||
else
|
||||
OUTCHAR(str, len, size, ch);
|
||||
ch = *format++;
|
||||
break;
|
||||
case PRINT_S_FLAGS:
|
||||
switch (ch) {
|
||||
case '-':
|
||||
flags |= PRINT_F_MINUS;
|
||||
ch = *format++;
|
||||
break;
|
||||
case '+':
|
||||
flags |= PRINT_F_PLUS;
|
||||
ch = *format++;
|
||||
break;
|
||||
case ' ':
|
||||
flags |= PRINT_F_SPACE;
|
||||
ch = *format++;
|
||||
break;
|
||||
case '#':
|
||||
flags |= PRINT_F_NUM;
|
||||
ch = *format++;
|
||||
break;
|
||||
case '0':
|
||||
flags |= PRINT_F_ZERO;
|
||||
ch = *format++;
|
||||
break;
|
||||
case '\'': /* SUSv2 flag (not in C99). */
|
||||
flags |= PRINT_F_QUOTE;
|
||||
ch = *format++;
|
||||
break;
|
||||
default:
|
||||
state = PRINT_S_WIDTH;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PRINT_S_WIDTH:
|
||||
if (ISDIGIT(ch)) {
|
||||
ch = CHARTOINT(ch);
|
||||
if (width > (INT_MAX - ch) / 10) {
|
||||
overflow = 1;
|
||||
goto out;
|
||||
}
|
||||
width = 10 * width + ch;
|
||||
ch = *format++;
|
||||
} else if (ch == '*') {
|
||||
/*
|
||||
* C99 says: "A negative field width argument is
|
||||
* taken as a `-' flag followed by a positive
|
||||
* field width." (7.19.6.1, 5)
|
||||
*/
|
||||
if ((width = va_arg(args, int)) < 0) {
|
||||
flags |= PRINT_F_MINUS;
|
||||
width = -width;
|
||||
}
|
||||
ch = *format++;
|
||||
state = PRINT_S_DOT;
|
||||
} else
|
||||
state = PRINT_S_DOT;
|
||||
break;
|
||||
case PRINT_S_DOT:
|
||||
if (ch == '.') {
|
||||
state = PRINT_S_PRECISION;
|
||||
ch = *format++;
|
||||
} else
|
||||
state = PRINT_S_MOD;
|
||||
break;
|
||||
case PRINT_S_PRECISION:
|
||||
if (precision == -1)
|
||||
precision = 0;
|
||||
if (ISDIGIT(ch)) {
|
||||
ch = CHARTOINT(ch);
|
||||
if (precision > (INT_MAX - ch) / 10) {
|
||||
overflow = 1;
|
||||
goto out;
|
||||
}
|
||||
precision = 10 * precision + ch;
|
||||
ch = *format++;
|
||||
} else if (ch == '*') {
|
||||
/*
|
||||
* C99 says: "A negative precision argument is
|
||||
* taken as if the precision were omitted."
|
||||
* (7.19.6.1, 5)
|
||||
*/
|
||||
if ((precision = va_arg(args, int)) < 0)
|
||||
precision = -1;
|
||||
ch = *format++;
|
||||
state = PRINT_S_MOD;
|
||||
} else
|
||||
state = PRINT_S_MOD;
|
||||
break;
|
||||
case PRINT_S_MOD:
|
||||
switch (ch) {
|
||||
case 'h':
|
||||
ch = *format++;
|
||||
if (ch == 'h') { /* It's a char. */
|
||||
ch = *format++;
|
||||
cflags = PRINT_C_CHAR;
|
||||
} else
|
||||
cflags = PRINT_C_SHORT;
|
||||
break;
|
||||
case 'l':
|
||||
ch = *format++;
|
||||
if (ch == 'l') { /* It's a long long. */
|
||||
ch = *format++;
|
||||
cflags = PRINT_C_LLONG;
|
||||
} else
|
||||
cflags = PRINT_C_LONG;
|
||||
break;
|
||||
case 'j':
|
||||
cflags = PRINT_C_INTMAX;
|
||||
ch = *format++;
|
||||
break;
|
||||
case 't':
|
||||
cflags = PRINT_C_PTRDIFF;
|
||||
ch = *format++;
|
||||
break;
|
||||
case 'z':
|
||||
cflags = PRINT_C_SIZE;
|
||||
ch = *format++;
|
||||
break;
|
||||
}
|
||||
state = PRINT_S_CONV;
|
||||
break;
|
||||
case PRINT_S_CONV:
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
/* FALLTHROUGH */
|
||||
case 'i':
|
||||
switch (cflags) {
|
||||
case PRINT_C_CHAR:
|
||||
value = (signed char)va_arg(args, int);
|
||||
break;
|
||||
case PRINT_C_SHORT:
|
||||
value = (short int)va_arg(args, int);
|
||||
break;
|
||||
case PRINT_C_LONG:
|
||||
value = va_arg(args, long int);
|
||||
break;
|
||||
case PRINT_C_LLONG:
|
||||
value = va_arg(args, LLONG);
|
||||
break;
|
||||
case PRINT_C_SIZE:
|
||||
value = va_arg(args, SSIZE_T);
|
||||
break;
|
||||
case PRINT_C_INTMAX:
|
||||
value = va_arg(args, INTMAX_T);
|
||||
break;
|
||||
case PRINT_C_PTRDIFF:
|
||||
value = va_arg(args, PTRDIFF_T);
|
||||
break;
|
||||
default:
|
||||
value = va_arg(args, int);
|
||||
break;
|
||||
}
|
||||
fmtint(str, &len, size, value, 10, width, precision,
|
||||
flags);
|
||||
break;
|
||||
case 'X':
|
||||
flags |= PRINT_F_UP;
|
||||
/* FALLTHROUGH */
|
||||
case 'x':
|
||||
base = 16;
|
||||
/* FALLTHROUGH */
|
||||
case 'o':
|
||||
if (base == 0)
|
||||
base = 8;
|
||||
/* FALLTHROUGH */
|
||||
case 'u':
|
||||
if (base == 0)
|
||||
base = 10;
|
||||
flags |= PRINT_F_UNSIGNED;
|
||||
switch (cflags) {
|
||||
case PRINT_C_CHAR:
|
||||
value =
|
||||
(unsigned char)va_arg(args, unsigned int);
|
||||
break;
|
||||
case PRINT_C_SHORT:
|
||||
value = (unsigned short int)va_arg(
|
||||
args, unsigned int);
|
||||
break;
|
||||
case PRINT_C_LONG:
|
||||
value = va_arg(args, unsigned long int);
|
||||
break;
|
||||
case PRINT_C_LLONG:
|
||||
value = va_arg(args, ULLONG);
|
||||
break;
|
||||
case PRINT_C_SIZE:
|
||||
value = va_arg(args, size_t);
|
||||
break;
|
||||
case PRINT_C_INTMAX:
|
||||
value = va_arg(args, UINTMAX_T);
|
||||
break;
|
||||
case PRINT_C_PTRDIFF:
|
||||
value = va_arg(args, UPTRDIFF_T);
|
||||
break;
|
||||
default:
|
||||
value = va_arg(args, unsigned int);
|
||||
break;
|
||||
}
|
||||
fmtint(str, &len, size, value, base, width, precision,
|
||||
flags);
|
||||
break;
|
||||
case 'c':
|
||||
cvalue = va_arg(args, int);
|
||||
OUTCHAR(str, len, size, cvalue);
|
||||
break;
|
||||
case 's':
|
||||
strvalue = va_arg(args, char *);
|
||||
fmtstr(str, &len, size, strvalue, width, precision,
|
||||
flags);
|
||||
break;
|
||||
case 'p':
|
||||
/*
|
||||
* C99 says: "The value of the pointer is
|
||||
* converted to a sequence of printing
|
||||
* characters, in an implementation-defined
|
||||
* manner." (C99: 7.19.6.1, 8)
|
||||
*/
|
||||
if ((strvalue = va_arg(args, void *)) == NULL)
|
||||
/*
|
||||
* We use the glibc format. BSD prints
|
||||
* "0x0", SysV "0".
|
||||
*/
|
||||
fmtstr(str, &len, size, "(nil)", width, -1, flags);
|
||||
else {
|
||||
/*
|
||||
* We use the BSD/glibc format. SysV
|
||||
* omits the "0x" prefix (which we emit
|
||||
* using the PRINT_F_NUM flag).
|
||||
*/
|
||||
flags |= PRINT_F_NUM;
|
||||
flags |= PRINT_F_UNSIGNED;
|
||||
fmtint(str, &len, size, (UINTPTR_T)strvalue, 16,
|
||||
width, precision, flags);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
switch (cflags) {
|
||||
case PRINT_C_CHAR:
|
||||
charptr = va_arg(args, signed char *);
|
||||
*charptr = len;
|
||||
break;
|
||||
case PRINT_C_SHORT:
|
||||
shortptr = va_arg(args, short int *);
|
||||
*shortptr = len;
|
||||
break;
|
||||
case PRINT_C_LONG:
|
||||
longptr = va_arg(args, long int *);
|
||||
*longptr = len;
|
||||
break;
|
||||
case PRINT_C_LLONG:
|
||||
llongptr = va_arg(args, LLONG *);
|
||||
*llongptr = len;
|
||||
break;
|
||||
case PRINT_C_SIZE:
|
||||
/*
|
||||
* C99 says that with the "z" length
|
||||
* modifier, "a following `n' conversion
|
||||
* specifier applies to a pointer to a
|
||||
* signed integer type corresponding to
|
||||
* size_t argument." (7.19.6.1, 7)
|
||||
*/
|
||||
sizeptr = va_arg(args, SSIZE_T *);
|
||||
*sizeptr = len;
|
||||
break;
|
||||
case PRINT_C_INTMAX:
|
||||
intmaxptr = va_arg(args, INTMAX_T *);
|
||||
*intmaxptr = len;
|
||||
break;
|
||||
case PRINT_C_PTRDIFF:
|
||||
ptrdiffptr = va_arg(args, PTRDIFF_T *);
|
||||
*ptrdiffptr = len;
|
||||
break;
|
||||
default:
|
||||
intptr = va_arg(args, int *);
|
||||
*intptr = len;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '%': /* Print a "%" character verbatim. */
|
||||
OUTCHAR(str, len, size, ch);
|
||||
break;
|
||||
default: /* Skip other characters. */
|
||||
break;
|
||||
}
|
||||
ch = *format++;
|
||||
state = PRINT_S_DEFAULT;
|
||||
base = cflags = flags = width = 0;
|
||||
precision = -1;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
if (len < size)
|
||||
str[len] = '\0';
|
||||
else if (size > 0)
|
||||
str[size - 1] = '\0';
|
||||
|
||||
if (overflow || len >= INT_MAX) {
|
||||
return -1;
|
||||
}
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
static void fmtstr(char *str, size_t *len, size_t size, const char *value,
|
||||
int width, int precision, int flags)
|
||||
{
|
||||
int padlen, strln; /* Amount to pad. */
|
||||
int noprecision = (precision == -1);
|
||||
|
||||
if (value == NULL) /* We're forgiving. */
|
||||
value = "(null)";
|
||||
|
||||
/* If a precision was specified, don't read the string past it. */
|
||||
for (strln = 0; value[strln] != '\0' && (noprecision || strln < precision);
|
||||
strln++)
|
||||
continue;
|
||||
|
||||
if ((padlen = width - strln) < 0)
|
||||
padlen = 0;
|
||||
if (flags & PRINT_F_MINUS) /* Left justify. */
|
||||
padlen = -padlen;
|
||||
|
||||
while (padlen > 0) { /* Leading spaces. */
|
||||
OUTCHAR(str, *len, size, ' ');
|
||||
padlen--;
|
||||
}
|
||||
while (*value != '\0' && (noprecision || precision-- > 0)) {
|
||||
OUTCHAR(str, *len, size, *value);
|
||||
value++;
|
||||
}
|
||||
while (padlen < 0) { /* Trailing spaces. */
|
||||
OUTCHAR(str, *len, size, ' ');
|
||||
padlen++;
|
||||
}
|
||||
}
|
||||
|
||||
static void fmtint(char *str, size_t *len, size_t size, INTMAX_T value,
|
||||
int base, int width, int precision, int flags)
|
||||
{
|
||||
UINTMAX_T uvalue;
|
||||
char iconvert[MAX_CONVERT_LENGTH];
|
||||
char sign = 0;
|
||||
char hexprefix = 0;
|
||||
int spadlen = 0; /* Amount to space pad. */
|
||||
int zpadlen = 0; /* Amount to zero pad. */
|
||||
int pos;
|
||||
int separators = (flags & PRINT_F_QUOTE);
|
||||
int noprecision = (precision == -1);
|
||||
|
||||
if (flags & PRINT_F_UNSIGNED)
|
||||
uvalue = value;
|
||||
else {
|
||||
uvalue = (value >= 0) ? value : -value;
|
||||
if (value < 0)
|
||||
sign = '-';
|
||||
else if (flags & PRINT_F_PLUS) /* Do a sign. */
|
||||
sign = '+';
|
||||
else if (flags & PRINT_F_SPACE)
|
||||
sign = ' ';
|
||||
}
|
||||
|
||||
pos = convert(uvalue, iconvert, sizeof(iconvert), base, flags & PRINT_F_UP);
|
||||
|
||||
if (flags & PRINT_F_NUM && uvalue != 0) {
|
||||
/*
|
||||
* C99 says: "The result is converted to an `alternative form'.
|
||||
* For `o' conversion, it increases the precision, if and only
|
||||
* if necessary, to force the first digit of the result to be a
|
||||
* zero (if the value and precision are both 0, a single 0 is
|
||||
* printed). For `x' (or `X') conversion, a nonzero result has
|
||||
* `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
|
||||
*/
|
||||
switch (base) {
|
||||
case 8:
|
||||
if (precision <= pos)
|
||||
precision = pos + 1;
|
||||
break;
|
||||
case 16:
|
||||
hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (separators) /* Get the number of group separators we'll print. */
|
||||
separators = getnumsep(pos);
|
||||
|
||||
zpadlen = precision - pos - separators;
|
||||
spadlen = width /* Minimum field width. */
|
||||
- separators /* Number of separators. */
|
||||
- MAX(precision, pos) /* Number of integer digits. */
|
||||
- ((sign != 0) ? 1 : 0) /* Will we print a sign? */
|
||||
- ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */
|
||||
|
||||
if (zpadlen < 0)
|
||||
zpadlen = 0;
|
||||
if (spadlen < 0)
|
||||
spadlen = 0;
|
||||
|
||||
/*
|
||||
* C99 says: "If the `0' and `-' flags both appear, the `0' flag is
|
||||
* ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a
|
||||
* precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
|
||||
*/
|
||||
if (flags & PRINT_F_MINUS) /* Left justify. */
|
||||
spadlen = -spadlen;
|
||||
else if (flags & PRINT_F_ZERO && noprecision) {
|
||||
zpadlen += spadlen;
|
||||
spadlen = 0;
|
||||
}
|
||||
while (spadlen > 0) { /* Leading spaces. */
|
||||
OUTCHAR(str, *len, size, ' ');
|
||||
spadlen--;
|
||||
}
|
||||
if (sign != 0) /* Sign. */
|
||||
OUTCHAR(str, *len, size, sign);
|
||||
if (hexprefix != 0) { /* A "0x" or "0X" prefix. */
|
||||
OUTCHAR(str, *len, size, '0');
|
||||
OUTCHAR(str, *len, size, hexprefix);
|
||||
}
|
||||
while (zpadlen > 0) { /* Leading zeros. */
|
||||
OUTCHAR(str, *len, size, '0');
|
||||
zpadlen--;
|
||||
}
|
||||
while (pos > 0) { /* The actual digits. */
|
||||
pos--;
|
||||
OUTCHAR(str, *len, size, iconvert[pos]);
|
||||
if (separators > 0 && pos > 0 && pos % 3 == 0)
|
||||
printsep(str, len, size);
|
||||
}
|
||||
while (spadlen < 0) { /* Trailing spaces. */
|
||||
OUTCHAR(str, *len, size, ' ');
|
||||
spadlen++;
|
||||
}
|
||||
}
|
||||
|
||||
static void printsep(char *str, size_t *len, size_t size)
|
||||
{
|
||||
OUTCHAR(str, *len, size, ',');
|
||||
}
|
||||
|
||||
static int getnumsep(int digits)
|
||||
{
|
||||
int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
|
||||
return separators;
|
||||
}
|
||||
|
||||
static int convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
|
||||
{
|
||||
const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
size_t pos = 0;
|
||||
|
||||
/* We return an unterminated buffer with the digits in reverse order. */
|
||||
do {
|
||||
buf[pos++] = digits[value % base];
|
||||
value /= base;
|
||||
} while (value != 0 && pos < size);
|
||||
|
||||
return (int)pos;
|
||||
}
|
||||
|
||||
int vsprintf(char *buf, const char *fmt, va_list args)
|
||||
{
|
||||
return vsnprintf(buf, INT_MAX, fmt, args);
|
||||
}
|
11
src/vsprintf.h
Normal file
11
src/vsprintf.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef VSPRINTF_H
|
||||
#define VSPRINTF_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
int vsprintf(char *buf, const char *fmt, va_list args);
|
||||
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
|
||||
|
||||
#endif
|
35
src/xnuboot.h
Normal file
35
src/xnuboot.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef XNUBOOT_H
|
||||
#define XNUBOOT_H
|
||||
|
||||
#define CMDLINE_LENGTH 608
|
||||
|
||||
struct boot_video {
|
||||
u64 base;
|
||||
u64 display;
|
||||
u64 stride;
|
||||
u64 width;
|
||||
u64 height;
|
||||
u64 depth;
|
||||
};
|
||||
|
||||
struct boot_args {
|
||||
u16 revision;
|
||||
u16 version;
|
||||
u64 virt_base;
|
||||
u64 phys_base;
|
||||
u64 mem_size;
|
||||
u64 top_of_kernel_data;
|
||||
struct boot_video video;
|
||||
u32 machine_type;
|
||||
void *devtree;
|
||||
u32 devtree_size;
|
||||
char cmdline[CMDLINE_LENGTH];
|
||||
u64 boot_flags;
|
||||
u64 mem_size_actual;
|
||||
};
|
||||
|
||||
extern struct boot_args cur_boot_args;
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue