Got it to run on the test laptop

This commit is contained in:
Thomas Oltmann 2026-06-03 22:58:10 +02:00
parent 8dbeccbe75
commit 98da895f18
9 changed files with 1234 additions and 3 deletions

View file

@ -6,6 +6,7 @@ OBJS=\
src/pxe.o \ src/pxe.o \
src/std.o \ src/std.o \
src/main.o \ src/main.o \
src/tinflate.o \
# end of object list # end of object list
.PHONY: all clean .PHONY: all clean
@ -23,10 +24,12 @@ boot.bin: boot.elf
wc -c $@ wc -c $@
boot.elf: $(OBJS) src/nbp.ld boot.elf: $(OBJS) src/nbp.ld
$(LD) $(LDFLAGS) -T src/nbp.ld -o $@ $(OBJS) $(CC) -m32 -fno-pic -fno-pie -T src/nbp.ld -o $@ $(OBJS) -ffreestanding -nostdlib
%.o: %.S %.o: %.S
$(CC) $(CFLAGS) -c -o $@ $(@:.o=.S) $(CPPFLAGS) $(CC) $(CFLAGS) -c -o $@ $(@:.o=.S) $(CPPFLAGS)
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) -O0 -c -o $@ $(@:.o=.c) $(CPPFLAGS) $(CC) $(CFLAGS) -O0 -c -o $@ $(@:.o=.c) $(CPPFLAGS)
# TODO header dependencies

View file

@ -5,4 +5,4 @@ LD = ld
CFLAGS = -no-pie -fno-pic -fno-stack-protector -nostdinc -ffreestanding -m32 CFLAGS = -no-pie -fno-pic -fno-stack-protector -nostdinc -ffreestanding -m32
CPPFLAGS = -I3rdparty/include CPPFLAGS = -I3rdparty/include
LDFLAGS = -m elf_i386 LDFLAGS = -m elf_i386 -fno-pic

20
eth-dnsmasq.conf Normal file
View file

@ -0,0 +1,20 @@
#listen-address=10.0.0.1
#bind-dynamic
#bind-interfaces
#except-interface=lo
interface=enp6s0f3u1
dhcp-range=10.0.0.2,10.0.0.100,255.255.255.0,6h
dhcp-boot=/boot.bin
#dhcp-option=66,"10.0.0.1"
#dhcp-option=67,"/boot.bin"
#dhcp-option=67,"/pxelinux.0"
log-async
# Disable DNS server
port=0
enable-tftp
tftp-root=/home/thomas/karlos/fernlader2/serve

364
src/fs.h Normal file
View file

@ -0,0 +1,364 @@
/*
* x86_64-cb/fs.h
*
* Copyright (C) 2017 - 2021 bzt (bztsrc@gitlab)
*
* 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.
*
* This file is part of the BOOTBOOT Protocol package.
* @brief Filesystem drivers for initial ramdisk.
*
*/
#if 0
/**
* FS/Z initrd (OS/Z's native file system)
*/
file_t fsz_initrd(unsigned char *initrd_p, char *kernel)
{
file_t ret = { NULL, 0 };
if(initrd_p==NULL || memcmp(initrd_p + 512,"FS/Z",4) || kernel==NULL){
return ret;
}
unsigned char passphrase[256],chk[32],iv[32];
unsigned int i,j,k,l,ss=1<<(initrd_p[518]+11);
unsigned char *ent, *in=(initrd_p+*((uint64_t*)(initrd_p+560))*ss);
SHA256_CTX ctx;
PRINTFS("FS/Z",kernel);
//decrypt initrd
if(*((uint32_t*)(initrd_p+520))!=0 && (initrd_p[519]&&0xF0)!=0) {
printf("BOOTBOOT-PANIC: Unsupported cipher\n");
return ret;
}
while(*((uint32_t*)(initrd_p+520))!=0) {
printf(" * Passphrase? ");
l=ReadLine(passphrase,sizeof(passphrase));
if(!l) {
printf("\n");
return ret;
}
if(*((uint32_t*)(initrd_p+520))!=crc32_calc((char*)passphrase,l)) {
printf("\rBOOTBOOT-ERROR: Bad passphrase\n");
continue;
}
printf("\r * Decrypting...\r");
SHA256_Init(&ctx);
SHA256_Update(&ctx,passphrase,l);
SHA256_Update(&ctx,initrd_p+512,6);
SHA256_Final(chk,&ctx);
for(i=0;i<32;i++) initrd_p[i+680]^=chk[i];
SHA256_Init(&ctx);
SHA256_Update(&ctx,initrd_p+680,32);
SHA256_Final(iv,&ctx);
// FSZ_SB_EALG_SHACBC
for(k=ss,j=1;j<*((uint32_t*)(initrd_p+528));j++) {
memcpy(chk,iv,32);
for(i=0;i<ss;i++) {
if(i%32==0) {
SHA256_Init(&ctx);
SHA256_Update(&ctx,&chk,32);
SHA256_Update(&ctx,&j,4);
SHA256_Final(chk,&ctx);
}
initrd_p[k++]^=chk[i%32]^iv[i%32];
}
}
memset(initrd_p+680,0,32); *((uint32_t*)(initrd_p+520)) = 0;
*((uint32_t*)(initrd_p+1020))=crc32_calc((char *)initrd_p+512,508);
printf(" \r");
}
// Get the inode
char *s,*e;
s=e=kernel;
i=0;
again:
while(*e!='/'&&*e!=0){e++;}
if(*e=='/'){e++;}
if(!memcmp(in,"FSIN",4)){
//is it inlined?
if(!memcmp(initrd_p[520]&1? in + 2048 : in + 1024,"FSDR",4)){
ent=(initrd_p[520]&1? in + 2048 : in + 1024);
} else if(!memcmp(initrd_p+*((uint64_t*)(in+448))*ss,"FSDR",4)){
// go, get the sector pointed
ent=(initrd_p+*((uint64_t*)(in+448))*ss);
} else {
return ret;
}
//skip header
unsigned char *hdr=ent; ent+=128;
//iterate on directory entries
int j=*((uint64_t*)(hdr+16));
while(j-->0){
if(!memcmp(ent + 16,s,e-s)) {
if(*e==0) {
i=*((uint64_t*)(ent+0));
break;
} else {
s=e;
in=(initrd_p+*((uint64_t*)(ent+0))*ss);
goto again;
}
}
ent+=128;
}
} else {
i=0;
}
if(i!=0) {
// fid -> inode ptr -> data ptr
unsigned char *in=(initrd_p+i*ss);
if(!memcmp(in,"FSIN",4)){
ret.size=*((uint64_t*)(in+464));
if(*((uint64_t*)(in+448)) == i) {
if(!(in[488]&31)) {
// inline data
ret.ptr=(uint8_t*)(initrd_p+i*ss+(initrd_p[520]&1? 2048 : 1024));
} else {
// sector directory or list inlined
ret.ptr=(uint8_t*)(initrd_p + *((uint64_t*)(initrd_p[520]&1? in + 2048 : in + 1024))*ss);
}
} else
if(*((uint64_t*)(in+448))) {
switch((in[488]&15) + (in[488]&16 ? 1 : 0)) {
case 0: // direct data
ret.ptr=(uint8_t*)(initrd_p + *((uint64_t*)(in+448)) * ss);
break;
case 1: // sector directory or list (only one level supported here, and no holes in files)
ret.ptr=(uint8_t*)(initrd_p + *((uint64_t*)(initrd_p + *((uint64_t*)(in+448))*ss)) * ss);
break;
}
} else ret.size=0;
}
}
return ret;
}
#endif
/**
* Minix3 file system
* directories only checked for their first block, and kernel must be defragmented
*/
file_t mfs_initrd(unsigned char *initrd_p, char *kernel)
{
uint32_t o, bs, ino_tbl;
uint8_t *ino, *d;
char *s = kernel, *e;
file_t ret = { NULL, 0 };
if(initrd_p[1048] != 'Z' || initrd_p[1049] != 'M') return ret;
PRINTFS("MFS",kernel);
bs = *((uint16_t*)(initrd_p + 1052));
ino_tbl = (2 + *((uint16_t*)(initrd_p + 1030)) + *((uint16_t*)(initrd_p + 1032))) * bs;
ino = initrd_p + ino_tbl;
again:
for(e = s; *e && *e != '/'; e++);
d = initrd_p + *((uint32_t*)(ino + 24)) * bs;
for(o = 0; o < *((uint32_t*)(ino + 8)) && o < bs; o += 64, d += 64) {
if(*((uint32_t*)d) && !memcmp(s, d + 4, e - s) && !d[e - s]) {
ino = initrd_p + ino_tbl + (*((uint32_t*)d) - 1) * 64;
d = initrd_p + *((uint32_t*)(ino + 24)) * bs;
if(!*e) { ret.ptr = d; ret.size = *((uint32_t*)(ino + 8)); return ret; }
s = e + 1; goto again;
}
}
return ret;
}
/**
* cpio archive
*/
file_t cpio_initrd(unsigned char *initrd_p, char *kernel)
{
unsigned char *ptr=initrd_p;
int k;
file_t ret = { NULL, 0 };
if(initrd_p==NULL || kernel==NULL ||
(memcmp(initrd_p,"070701",6) && memcmp(initrd_p,"070702",6) && memcmp(initrd_p,"070707",6)))
return ret;
PRINTFS("cpio",kernel);
k=strlen(kernel);
// hpodc archive
while(!memcmp(ptr,"070707",6)){
int ns=octbin(ptr+8*6+11,6);
int fs=octbin(ptr+8*6+11+6,11);
if(!memcmp(ptr+9*6+2*11,kernel,k+1) ||
(ptr[9*6+2*11] == '.' && ptr[9*6+2*11+1] == '/' && !memcmp(ptr+9*6+2*11+2,kernel,k+1))) {
ret.size=fs;
ret.ptr=(uint8_t*)(ptr+9*6+2*11+ns);
return ret;
}
ptr+=(76+ns+fs);
}
// newc and crc archive
while(!memcmp(ptr,"07070",5)){
int fs=hexbin(ptr+8*6+6,8);
int ns=hexbin(ptr+8*11+6,8);
if(!memcmp(ptr+110,kernel,k+1) || (ptr[110] == '.' && ptr[111] == '/' && !memcmp(ptr+112,kernel,k+1))) {
ret.size=fs;
ret.ptr=(uint8_t*)(ptr+((110+ns+3)/4)*4);
return ret;
}
ptr+=((110+ns+3)/4)*4 + ((fs+3)/4)*4;
}
return ret;
}
/**
* ustar tarball archive
*/
file_t tar_initrd(unsigned char *initrd_p, char *kernel)
{
unsigned char *ptr=initrd_p;
int k;
file_t ret = { NULL, 0 };
if(initrd_p==NULL || kernel==NULL || memcmp(initrd_p+257,"ustar",5))
return ret;
PRINTFS("tar",kernel);
k=strlen(kernel);
while(!memcmp(ptr+257,"ustar",5)){
int fs=octbin(ptr+0x7c,11);
if(!memcmp(ptr,kernel,k+1) || (ptr[0] == '.' && ptr[1] == '/' && !memcmp(ptr+2,kernel,k+1))) {
ret.size=fs;
ret.ptr=(uint8_t*)(ptr+512);
return ret;
}
ptr+=(((fs+511)/512)+1)*512;
}
return ret;
}
/**
* Simple File System
*/
file_t sfs_initrd(unsigned char *initrd_p, char *kernel)
{
unsigned char *ptr, *end;
int k,bs,ver;
file_t ret = { NULL, 0 };
if(initrd_p==NULL || kernel==NULL || (memcmp(initrd_p+0x1AC,"SFS",3) && memcmp(initrd_p+0x1A6,"SFS",3)))
return ret;
// 1.0 Brendan's version, 1.10 BenLunt's version
ver=!memcmp(initrd_p+0x1A6,"SFS",3)?10:0;
bs=1<<(7+(uint8_t)initrd_p[ver?0x1B6:0x1BC]);
end=initrd_p + *((uint64_t *)&initrd_p[ver?0x1AA:0x1B0]) * bs; // base + total_number_of_blocks * blocksize
// get index area
ptr=end - *((uint64_t *)&initrd_p[ver?0x19E:0x1A4]); // end - size of index area
// got a Starting Marker Entry?
if(ptr[0]!=2)
return ret;
if (ver) {
PRINTFS("SFS 1.10",kernel);
} else {
PRINTFS("SFS 1.0",kernel);
}
k=strlen(kernel);
// iterate on index until we reach the end or Volume Identifier
while(ptr<end && ptr[0]!=0x01){
ptr+=64;
// file entry?
if(ptr[0]!=0x12)
continue;
// filename match?
if(!memcmp(ptr+(ver?0x23:0x22),kernel,k+1)){
ret.size=*((uint64_t*)&ptr[ver?0x1B:0x1A]); // file_length
ret.ptr=initrd_p + *((uint64_t*)&ptr[ver?0x0B:0x0A]) * bs; // base + start_block * blocksize
break;
}
}
return ret;
}
/**
* James Molloy's initrd (for some reason it's popular among hobby OS developers)
* http://www.jamesmolloy.co.uk/tutorial_html
*/
file_t jamesm_initrd(unsigned char *initrd_p, char *kernel)
{
unsigned char *ptr=initrd_p+4;
int i,k,nf=*((int*)initrd_p);
file_t ret = { NULL, 0 };
// no real magic, so we assume initrd contains at least one file...
if(initrd_p==NULL || kernel==NULL || initrd_p[2]!=0 || initrd_p[3]!=0 || initrd_p[4]!=0xBF)
return ret;
PRINTFS("JamesM",kernel);
k=strlen(kernel);
for(i=0;i<nf && ptr[0]==0xBF;i++) {
if(!memcmp(ptr+1,kernel,k+1)){
ret.ptr=*((uint32_t*)(ptr+65)) + initrd_p;
ret.size=*((uint32_t*)(ptr+69));
}
ptr+=73;
}
return ret;
}
#if 0
/**
* EchFS
* http://github.com/echfs/echfs
*/
file_t ech_initrd(unsigned char *initrd_p, char *kernel)
{
uint64_t parent = 0xffffffffffffffffUL, n;
unsigned char *ptr;
char *end, *fn;
int k = 0;
file_t ret = { NULL, 0 };
if(initrd_p==NULL || kernel==NULL || memcmp(initrd_p+4,"_ECH_FS_",8))
return ret;
PRINTFS("EchFS",kernel);
memcpy(&k, initrd_p + 28, 4);
memcpy(&n, initrd_p + 12, 8);
ptr = initrd_p + (((n * 8 + k - 1) / k) + 16) * k;
for(end = fn = kernel; *end && *end != '/'; end++);
while(*((uint64_t*)ptr)) {
if(*((uint64_t*)ptr) == parent && !memcmp(ptr + 9, fn, end - fn) && !ptr[9 + end - fn]) {
parent = *((uint64_t*)(ptr + 240));
if(!*end) {
ret.size=*((uint64_t*)(ptr + 248));
ret.ptr=(uint8_t*)(initrd_p + parent * k);
break;
}
end++;
for(fn = end; *end && *end != '/'; end++);
}
ptr += 256;
}
return ret;
}
#endif
/**
* Static file system drivers registry
*/
file_t (*fsdrivers[]) (unsigned char *, char *) = {
#if 0
fsz_initrd,
#endif
mfs_initrd,
cpio_initrd,
tar_initrd,
sfs_initrd,
jamesm_initrd,
#if 0
ech_initrd,
#endif
NULL
};

View file

@ -205,6 +205,55 @@ typedef struct __attribute__((packed)) s_PXENV_TFTP_OPEN {
uint16_t packet_size; uint16_t packet_size;
} t_PXENV_TFTP_OPEN; } t_PXENV_TFTP_OPEN;
/**
* return type for fs drivers
*/
typedef struct {
uint8_t *ptr;
uint32_t size;
} file_t;
/**
* convert ascii octal number to binary number
*/
int octbin(unsigned char *str,int size)
{
int s=0;
unsigned char *c=str;
while(size-->0){
s*=8;
s+=*c-'0';
c++;
}
return s;
}
/**
* convert ascii hex number to binary number
*/
int hexbin(unsigned char *str, int size)
{
int v=0;
while(size-->0){
v <<= 4;
if(*str>='0' && *str<='9')
v += (int)((unsigned char)(*str)-'0');
else if(*str >= 'A' && *str <= 'F')
v += (int)((unsigned char)(*str)-'A'+10);
str++;
}
return v;
}
#define PRINTFS(fsname,kernel) do {\
debug_write(fsname);\
debug_write(" ");\
debug_write(kernel);\
debug_write("\r\n");\
} while (0)
#include "fs.h"
void void
main() main()
{ {

View file

@ -39,7 +39,7 @@ _start: cli
// initialize our own BSS section // initialize our own BSS section
mov $_bss_start, %di mov $_bss_start, %di
mov $_bss_end, %cx mov $_bss_end, %ecx
sub %di, %cx sub %di, %cx
xor %al, %al xor %al, %al
rep stosb rep stosb

203
src/smp.S Normal file
View file

@ -0,0 +1,203 @@
/*
* x86_64-cb/smp.S
*
* Copyright (C) 2017 - 2021 bzt (bztsrc@gitlab)
*
* 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.
*
* This file is part of the BOOTBOOT Protocol package.
* @brief SMP and long mode initialization code.
*
*/
.globl ap_trampoline
.globl bsp_init
.globl bsp64_init
.extern lapic_ids
.extern lapic_addr
.extern initstack
.text
/*****************************************************************************
* things to do on the APs *
*****************************************************************************/
.balign 128
.code16
/* this code will be relocated to 0x1000 - 0x1100 */
ap_trampoline:
cli
cld
ljmp $0, $0x1040
.balign 16
// prot mode GDT
_L1010_GDT_table:
.long 0, 0
.long 0x0000FFFF, 0x00CF9A00 // flat code
.long 0x0000FFFF, 0x008F9200 // flat data
.long 0x00000068, 0x00CF8900 // tss, not used but required by VB's vt-x
_L1030_GDT_value:
.word _L1030_GDT_value - _L1010_GDT_table - 1
.long 0x1010
.long 0, 0
.balign 64
_L1040:
xorw %ax, %ax
movw %ax, %ds
lgdtl 0x1030
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmp $8, $0x1060
.balign 32
.code32
_L1060:
movw $16, %ax
movw %ax, %ds
incb 0x1011
// spinlock until BSP finishes
1: pause
cmpb $0, 0x1010
jz 1b
// jump back to non-relocated code segment
ljmp $8, $longmode_init
.balign 128
ap_trampoline_end:
// long mode GDT (here it is aligned and out of execution flow)
GDT_table:
.long 0, 0
.long 0x0000FFFF, 0x00209800 // flat code, ring 0
.long 0x0000FFFF, 0x00809200 // flat data
.long 0x00000068, 0x00008900 // tss, required by vt-x
.long 0, 0
GDT_value:
.word GDT_value - GDT_table - 1
.long GDT_table, 0, 0
.word 0
.balign 8
stack64:
.long bootboot_startcore
.long 0
.quad 8
/*****************************************************************************
* things to do on BSP *
*****************************************************************************/
/* these are 32 bit encoded instructions */
bsp_init:
cli
cld
movb $0xFF, %al // disable PIC
outb %al, $0x21
outb %al, $0xA1
inb $0x70, %al // disable NMI
orb $0x80, %al
outb %al, $0x70
incb 0x1010 // release AP spin lock
// fall into long mode initialization code
/*****************************************************************************
* common code for all cores, enable long mode and start kernel *
*****************************************************************************/
longmode_init:
// enable lapic and find our lapic id
movl lapic_addr, %edi
or %edi, %edi
jz 1f
addl $0xF0, %edi
movl (%edi), %eax
or $0x1, %ah
movl %eax, (%edi)
subl $0xD0, %edi
movl (%edi), %edi
shrl $24, %edi
1: // do not clobber di
movl $0x368, %eax // Set PAE, MCE, PGE; OSFXSR, OSXMMEXCPT (enable SSE)
movl %eax, %cr4
movl $0x4000, %eax
movl %eax, %cr3
movl $0x0C0000080, %ecx // EFR MSR
rdmsr
orl $0x100, %eax // enable long mode
wrmsr
movl $0x0C0000011, %eax // clear EM, MP (enable SSE) and WP
movl %eax, %cr0
lgdt GDT_value
ljmp $8, $bootboot_startcore
.code64
/* similar code to above, but these are 64 bit encoded, only needed on BSP if coreboot is compiled for x86_64 */
bsp64_init:
// do not clobber di
cli
cld
movb $0xFF, %al // disable PIC
outb %al, $0x21
outb %al, $0xA1
inb $0x70, %al // disable NMI
orb $0x80, %al
outb %al, $0x70
incb 0x1010 // release AP spin lock
xorq %rax, %rax
movl $0xC0000011, %eax // enable SSE
movq %rax, %cr0
movq %cr4, %rax
orw $3 << 8, %ax
mov %rax, %cr4
movl $0x4000, %eax // set up paging
movq %rax, %cr3
xorq %rax, %rax
movl $GDT_value, %eax
lgdt (%rax)
movl $stack64, %eax // reload CS, that's tricky in long mode because ljmp doesn't work
movq %rax, %rsp
lretq
/* IN: di = apic id of current core */
bootboot_startcore:
movl $0x10, %eax // load long mode segments
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw %ax, %fs
movw %ax, %gs
movzwq %di, %rbx
shll $1, %ebx // ebx = lapic id * 2
addl $lapic_ids, %ebx
xorq %rax, %rax
movw (%rbx), %ax // ax = word[lapic_ids + lapic id * 2]
movl $initstack, %ebx
movl (%rbx), %ebx
movzwq %ax, %rdi
mulq %rbx // 1k stack for each core
// set stack and call _start() in sys/core
xorq %rsp, %rsp // sp = core_num * -initstack
subq %rax, %rsp
xorq %rsi, %rsi
movl $entrypoint, %esi // GAS does not allow "jmp qword[entrypoint]"
lodsq
jmp *%rax
hlt

117
src/tinf.h Normal file
View file

@ -0,0 +1,117 @@
/*
* uzlib - tiny deflate/inflate library (deflate, gzip, zlib)
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014-2016 by Paul Sokolovsky
*/
#ifndef TINF_H_INCLUDED
#define TINF_H_INCLUDED
#include "std.h"
/* calling convention */
#ifndef TINFCC
#ifdef __WATCOMC__
#define TINFCC __cdecl
#else
#define TINFCC
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* ok status, more data produced */
#define TINF_OK 0
/* end of compressed stream reached */
#define TINF_DONE 1
#define TINF_DATA_ERROR (-3)
#define TINF_CHKSUM_ERROR (-4)
#define TINF_DICT_ERROR (-5)
/* checksum types */
#define TINF_CHKSUM_NONE 0
#define TINF_CHKSUM_ADLER 1
#define TINF_CHKSUM_CRC 2
/* data structures */
typedef struct {
unsigned short table[16]; /* table of code length counts */
unsigned short trans[288]; /* code -> symbol translation table */
} TINF_TREE;
struct TINF_DATA;
typedef struct TINF_DATA {
const unsigned char *source;
/* If source above is NULL, this function will be used to read
next byte from source stream */
unsigned char (*readSource)(struct TINF_DATA *data);
unsigned int tag;
unsigned int bitcount;
/* Buffer start */
unsigned char *destStart;
/* Buffer total size */
unsigned int destSize;
/* Current pointer in buffer */
unsigned char *dest;
/* Remaining bytes in buffer */
unsigned int destRemaining;
/* Accumulating checksum */
unsigned int checksum;
char checksum_type;
int btype;
int bfinal;
unsigned int curlen;
int lzOff;
unsigned char *dict_ring;
unsigned int dict_size;
unsigned int dict_idx;
TINF_TREE ltree; /* dynamic length/symbol tree */
TINF_TREE dtree; /* dynamic distance tree */
} TINF_DATA;
#define TINF_PUT(d, c) \
{ \
*d->dest++ = c; \
if (d->dict_ring) { d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } \
}
unsigned char TINFCC uzlib_get_byte(TINF_DATA *d);
/* Decompression API */
void TINFCC uzlib_init(void);
void TINFCC uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen);
int TINFCC uzlib_uncompress(TINF_DATA *d);
int TINFCC uzlib_uncompress_chksum(TINF_DATA *d);
int TINFCC uzlib_zlib_parse_header(TINF_DATA *d);
int TINFCC uzlib_gzip_parse_header(TINF_DATA *d);
/* Compression API */
void TINFCC uzlib_compress(void *data, const uint8_t *src, unsigned slen);
/* Checksum API */
/* prev_sum is previous value for incremental computation, 1 initially */
uint32_t TINFCC uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum);
/* crc is previous value for incremental computation, 0xffffffff initially */
uint32_t TINFCC uzlib_crc32(const void *data, unsigned int length, uint32_t crc);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* TINF_H_INCLUDED */

475
src/tinflate.c Normal file
View file

@ -0,0 +1,475 @@
/*
* tinflate - tiny inflate
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014-2016 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include "tinf.h"
uint32_t tinf_get_le_uint32(TINF_DATA *d);
uint32_t tinf_get_be_uint32(TINF_DATA *d);
/* --------------------------------------------------- *
* -- uninitialized global data (static structures) -- *
* --------------------------------------------------- */
#ifdef RUNTIME_BITS_TABLES
/* extra bits and base tables for length codes */
unsigned char length_bits[30];
unsigned short length_base[30];
/* extra bits and base tables for distance codes */
unsigned char dist_bits[30];
unsigned short dist_base[30];
#else
const unsigned char length_bits[30] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 5
};
const unsigned short length_base[30] = {
3, 4, 5, 6, 7, 8, 9, 10,
11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115,
131, 163, 195, 227, 258
};
const unsigned char dist_bits[30] = {
0, 0, 0, 0, 1, 1, 2, 2,
3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13
};
const unsigned short dist_base[30] = {
1, 2, 3, 4, 5, 7, 9, 13,
17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073,
4097, 6145, 8193, 12289, 16385, 24577
};
#endif
/* special ordering of code length codes */
const unsigned char clcidx[] = {
16, 17, 18, 0, 8, 7, 9, 6,
10, 5, 11, 4, 12, 3, 13, 2,
14, 1, 15
};
/* ----------------------- *
* -- utility functions -- *
* ----------------------- */
#ifdef RUNTIME_BITS_TABLES
/* build extra bits and base tables */
static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first)
{
int i, sum;
/* build bits table */
for (i = 0; i < delta; ++i) bits[i] = 0;
for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta;
/* build base table */
for (sum = first, i = 0; i < 30; ++i)
{
base[i] = sum;
sum += 1 << bits[i];
}
}
#endif
/* build the fixed huffman trees */
static void tinf_build_fixed_trees(TINF_TREE *lt, TINF_TREE *dt)
{
int i;
/* build fixed length tree */
for (i = 0; i < 7; ++i) lt->table[i] = 0;
lt->table[7] = 24;
lt->table[8] = 152;
lt->table[9] = 112;
for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i;
for (i = 0; i < 144; ++i) lt->trans[24 + i] = i;
for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i;
for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i;
/* build fixed distance tree */
for (i = 0; i < 5; ++i) dt->table[i] = 0;
dt->table[5] = 32;
for (i = 0; i < 32; ++i) dt->trans[i] = i;
}
/* given an array of code lengths, build a tree */
static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num)
{
unsigned short offs[16];
unsigned int i, sum;
/* clear code length count table */
for (i = 0; i < 16; ++i) t->table[i] = 0;
/* scan symbol lengths, and sum code length counts */
for (i = 0; i < num; ++i) t->table[lengths[i]]++;
t->table[0] = 0;
/* compute offset table for distribution sort */
for (sum = 0, i = 0; i < 16; ++i)
{
offs[i] = sum;
sum += t->table[i];
}
/* create code->symbol translation table (symbols sorted by code) */
for (i = 0; i < num; ++i)
{
if (lengths[i]) t->trans[offs[lengths[i]]++] = i;
}
}
/* ---------------------- *
* -- decode functions -- *
* ---------------------- */
unsigned char uzlib_get_byte(TINF_DATA *d)
{
if (d->source) {
return *d->source++;
}
return d->readSource(d);
}
uint32_t tinf_get_le_uint32(TINF_DATA *d)
{
uint32_t val = 0;
int i;
for (i = 4; i--;) {
val = val >> 8 | uzlib_get_byte(d) << 24;
}
return val;
}
uint32_t tinf_get_be_uint32(TINF_DATA *d)
{
uint32_t val = 0;
int i;
for (i = 4; i--;) {
val = val << 8 | uzlib_get_byte(d);
}
return val;
}
/* get one bit from source stream */
static int tinf_getbit(TINF_DATA *d)
{
unsigned int bit;
/* check if tag is empty */
if (!d->bitcount--)
{
/* load next tag */
d->tag = uzlib_get_byte(d);
d->bitcount = 7;
}
/* shift bit out of tag */
bit = d->tag & 0x01;
d->tag >>= 1;
return bit;
}
/* read a num bit value from a stream and add base */
static unsigned int tinf_read_bits(TINF_DATA *d, int num, int base)
{
unsigned int val = 0;
/* read num bits */
if (num)
{
unsigned int limit = 1 << (num);
unsigned int mask;
for (mask = 1; mask < limit; mask *= 2)
if (tinf_getbit(d)) val += mask;
}
return val + base;
}
/* given a data stream and a tree, decode a symbol */
static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t)
{
int sum = 0, cur = 0, len = 0;
/* get more bits while code value is above sum */
do {
cur = 2*cur + tinf_getbit(d);
++len;
sum += t->table[len];
cur -= t->table[len];
} while (cur >= 0);
return t->trans[sum + cur];
}
/* given a data stream, decode dynamic trees from it */
static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
{
unsigned char lengths[288+32];
unsigned int hlit, hdist, hclen;
unsigned int i, num, length;
/* get 5 bits HLIT (257-286) */
hlit = tinf_read_bits(d, 5, 257);
/* get 5 bits HDIST (1-32) */
hdist = tinf_read_bits(d, 5, 1);
/* get 4 bits HCLEN (4-19) */
hclen = tinf_read_bits(d, 4, 4);
for (i = 0; i < 19; ++i) lengths[i] = 0;
/* read code lengths for code length alphabet */
for (i = 0; i < hclen; ++i)
{
/* get 3 bits code length (0-7) */
unsigned int clen = tinf_read_bits(d, 3, 0);
lengths[clcidx[i]] = clen;
}
/* build code length tree, temporarily use length tree */
tinf_build_tree(lt, lengths, 19);
/* decode code lengths for the dynamic trees */
for (num = 0; num < hlit + hdist; )
{
int sym = tinf_decode_symbol(d, lt);
switch (sym)
{
case 16:
/* copy previous code length 3-6 times (read 2 bits) */
{
unsigned char prev = lengths[num - 1];
for (length = tinf_read_bits(d, 2, 3); length; --length)
{
lengths[num++] = prev;
}
}
break;
case 17:
/* repeat code length 0 for 3-10 times (read 3 bits) */
for (length = tinf_read_bits(d, 3, 3); length; --length)
{
lengths[num++] = 0;
}
break;
case 18:
/* repeat code length 0 for 11-138 times (read 7 bits) */
for (length = tinf_read_bits(d, 7, 11); length; --length)
{
lengths[num++] = 0;
}
break;
default:
/* values 0-15 represent the actual code lengths */
lengths[num++] = sym;
break;
}
}
/* build dynamic trees */
tinf_build_tree(lt, lengths, hlit);
tinf_build_tree(dt, lengths + hlit, hdist);
}
/* ----------------------------- *
* -- block inflate functions -- *
* ----------------------------- */
/* given a stream and two trees, inflate a block of data */
static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt)
{
if (d->curlen == 0) {
unsigned int offs;
int dist;
int sym = tinf_decode_symbol(d, lt);
//printf("huff sym: %02x\n", sym);
/* literal byte */
if (sym < 256) {
TINF_PUT(d, sym);
return TINF_OK;
}
/* end of block */
if (sym == 256) {
return TINF_DONE;
}
/* substring from sliding dictionary */
sym -= 257;
/* possibly get more bits from length code */
d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]);
dist = tinf_decode_symbol(d, dt);
/* possibly get more bits from distance code */
offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]);
d->lzOff = -offs;
}
/* copy next byte from dict substring */
d->dest[0] = d->dest[d->lzOff];
d->dest++;
d->curlen--;
return TINF_OK;
}
/* inflate an uncompressed block of data */
static int tinf_inflate_uncompressed_block(TINF_DATA *d)
{
if (d->curlen == 0) {
unsigned int length, invlength;
/* get length */
length = uzlib_get_byte(d) + 256 * uzlib_get_byte(d);
/* get one's complement of length */
invlength = uzlib_get_byte(d) + 256 * uzlib_get_byte(d);
/* check length */
if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR;
/* increment length to properly return TINF_DONE below, without
producing data at the same time */
d->curlen = length + 1;
/* make sure we start next block on a byte boundary */
d->bitcount = 0;
}
if (--d->curlen == 0) {
return TINF_DONE;
}
unsigned char c = uzlib_get_byte(d);
TINF_PUT(d, c);
return TINF_OK;
}
/* ---------------------- *
* -- public functions -- *
* ---------------------- */
/* initialize global (static) data */
void uzlib_init(void)
{
#ifdef RUNTIME_BITS_TABLES
/* build extra bits and base tables */
tinf_build_bits_base(length_bits, length_base, 4, 3);
tinf_build_bits_base(dist_bits, dist_base, 2, 1);
/* fix a special case */
length_bits[28] = 0;
length_base[28] = 258;
#endif
}
/* inflate next byte of compressed stream */
int uzlib_uncompress(TINF_DATA *d)
{
do {
int res;
/* start a new block */
if (d->btype == -1) {
next_blk:
/* read final block flag */
d->bfinal = tinf_getbit(d);
/* read block type (2 bits) */
d->btype = tinf_read_bits(d, 2, 0);
//printf("Started new block: type=%d final=%d\n", d->btype, d->bfinal);
if (d->btype == 1) {
/* build fixed huffman trees */
tinf_build_fixed_trees(&d->ltree, &d->dtree);
} else if (d->btype == 2) {
/* decode trees from stream */
tinf_decode_trees(d, &d->ltree, &d->dtree);
}
}
/* process current block */
switch (d->btype)
{
case 0:
/* decompress uncompressed block */
res = tinf_inflate_uncompressed_block(d);
break;
case 1:
case 2:
/* decompress block with fixed/dyanamic huffman trees */
/* trees were decoded previously, so it's the same routine for both */
res = tinf_inflate_block_data(d, &d->ltree, &d->dtree);
break;
default:
return TINF_DATA_ERROR;
}
if (res == TINF_DONE && !d->bfinal) {
/* the block has ended (without producing more data), but we
can't return without data, so start procesing next block */
goto next_blk;
}
if (res != TINF_OK) {
return res;
}
} while (--d->destSize);
return TINF_OK;
}