diff --git a/fobooster/Makefile b/fobooster/Makefile new file mode 100644 index 0000000..5bbe40b --- /dev/null +++ b/fobooster/Makefile @@ -0,0 +1,121 @@ +GIT_VERSION := $(shell git describe --tags) + +# There is no 64-bit gcc on Raspberry Pi, so use the 32-bit version +ifneq (,$(wildcard /etc/rpi-issue)) +TRGT ?= riscv32-unknown-elf- +else +TRGT ?= riscv64-unknown-elf- +endif + +CC := $(TRGT)gcc +CXX := $(TRGT)g++ +OBJCOPY := $(TRGT)objcopy + +RM := rm -rf +COPY := cp -a +PATH_SEP := / + +ifeq ($(OS),Windows_NT) +COPY := copy +RM := del +PATH_SEP := \\ +endif + +BASE_DIR := . +LD_DIR := $(BASE_DIR)/ld +LDSCRIPT := $(BASE_DIR)/ld/linker.ld +ADD_CFLAGS := -I$(BASE_DIR)/include -D__vexriscv__ -march=rv32i -mabi=ilp32 +ADD_LFLAGS := +PACKAGE := fobooster + +LDSCRIPTS := $(LDSCRIPT) $(LD_DIR)/output_format.ld $(LD_DIR)/regions.ld +SRC_DIR := $(BASE_DIR)/src +THIRD_PARTY := $(BASE_DIR)/third_party +DBG_CFLAGS := -ggdb -g -DDEBUG -Wall +DBG_LFLAGS := -ggdb -g -Wall +CFLAGS := $(ADD_CFLAGS) \ + -Wall -Wextra \ + -ffunction-sections -fdata-sections -fno-common \ + -fomit-frame-pointer -Os \ + -march=rv32i \ + -flto \ + -DGIT_VERSION=u\"$(GIT_VERSION)\" -std=gnu11 +CXXFLAGS := $(CFLAGS) -std=c++11 -fno-rtti -fno-exceptions +LFLAGS := $(CFLAGS) $(ADD_LFLAGS) -L$(LD_DIR) \ + -nostartfiles \ + -nostdlib \ + -Wl,--gc-sections \ + -Wl,--no-warn-mismatch \ + -Wl,--script=$(LDSCRIPT) \ + -Wl,--build-id=none + +OBJ_DIR := .obj + +CSOURCES := $(wildcard $(SRC_DIR)/*.c) +CPPSOURCES := $(wildcard $(SRC_DIR)/*.cpp) +ASOURCES := $(wildcard $(SRC_DIR)/*.S) +COBJS := $(addprefix $(OBJ_DIR)/, $(notdir $(CSOURCES:.c=.o))) +CXXOBJS := $(addprefix $(OBJ_DIR)/, $(notdir $(CPPSOURCES:.cpp=.o))) +AOBJS := $(addprefix $(OBJ_DIR)/, $(notdir $(ASOURCES:.S=.o))) +OBJECTS := $(COBJS) $(CXXOBJS) $(AOBJS) +VPATH := $(SRC_DIR) + +QUIET := @ + +ALL := all +TARGET := $(PACKAGE).elf +CLEAN := clean + +$(ALL): $(TARGET) $(PACKAGE).bin $(PACKAGE).ihex + +$(OBJECTS): | $(OBJ_DIR) + +$(TARGET): $(OBJECTS) $(LDSCRIPTS) + $(QUIET) echo " LD $@" + $(QUIET) $(CC) $(OBJECTS) $(LFLAGS) -o $@ + +$(PACKAGE).bin: $(TARGET) + $(QUIET) echo " OBJCOPY $@" + $(QUIET) $(OBJCOPY) -O binary $(TARGET) $@ + +$(PACKAGE).dfu: $(TARGET) + $(QUIET) echo " DFU $@" + $(QUIET) $(COPY) $(PACKAGE).bin $@ + $(QUIET) dfu-suffix -v 1209 -p 70b1 -a $@ + +$(PACKAGE).ihex: $(TARGET) + $(QUIET) echo " IHEX $(PACKAGE).ihex" + $(QUIET) $(OBJCOPY) -O ihex $(TARGET) $@ + +$(DEBUG): CFLAGS += $(DBG_CFLAGS) +$(DEBUG): LFLAGS += $(DBG_LFLAGS) +CFLAGS += $(DBG_CFLAGS) +LFLAGS += $(DBG_LFLAGS) +$(DEBUG): $(TARGET) + +$(OBJ_DIR): + $(QUIET) mkdir $(OBJ_DIR) + +$(COBJS) : $(OBJ_DIR)/%.o : %.c $(BASE_DIR)/Makefile + $(QUIET) echo " CC $< $(notdir $@)" + $(QUIET) $(CC) -c $< $(CFLAGS) -o $@ -MMD + +$(OBJ_DIR)/%.o: %.cpp + $(QUIET) echo " CXX $< $(notdir $@)" + $(QUIET) $(CXX) -c $< $(CXXFLAGS) -o $@ -MMD + +$(OBJ_DIR)/%.o: %.S + $(QUIET) echo " AS $< $(notdir $@)" + $(QUIET) $(CC) -x assembler-with-cpp -c $< $(CFLAGS) -o $@ -MMD + +.PHONY: clean + +clean: + $(QUIET) echo " RM $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d))" + -$(QUIET) $(RM) $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d)) + $(QUIET) echo " RM $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.d))" + -$(QUIET) $(RM) $(subst /,$(PATH_SEP),$(wildcard $(OBJ_DIR)/*.o)) + $(QUIET) echo " RM $(TARGET) $(PACKAGE).bin $(PACKAGE).symbol $(PACKAGE).ihex $(PACKAGE).dfu" + -$(QUIET) $(RM) $(TARGET) $(PACKAGE).bin $(PACKAGE).symbol $(PACKAGE).ihex $(PACKAGE).dfu + +include $(wildcard $(OBJ_DIR)/*.d) diff --git a/fobooster/README.md b/fobooster/README.md new file mode 100644 index 0000000..c8e72aa --- /dev/null +++ b/fobooster/README.md @@ -0,0 +1,48 @@ +Fobooster: the Foboot Updater +=========================== + +Foboot cannot update itself, because a partial flash would result in an unbootable system. + +`Fobooster` is used to guide the installation of Foboot. By appending a new version of Foboot to the end of Fobooster, we can create a program to update Foboot itself. + +Usage +----- + +First, compile fobooster. Then, append the application header, and finally append the application itself. This can be accomplished with `make-booster`. + +```sh +cd toboot/ +make +cd ../booster/ +make +gcc make-booster.c -o make-booster +./make-booster ../toboot/toboot.bin toboot-booster.bin +``` + +The resulting `toboot-booster.bin` can be flashed with Toboot itself, or can be loaded using the legacy serial bootloader. + +Design +------ + +Booster uses xxHash to verify the application is loaded correctly. It also needs to know how many bytes to load. To do this, it looks at the `fobooster_data` struct, which has the following format: + +```c++ +struct fobooster_data +{ + uint32_t payload_size; // Number of bytes to flash + uint32_t xxhash32; // A hash of the entire program to be loaded + uint32_t payload[0]; // The contents of the firmware to build +}; +``` + +The `payload_size` value indicates the number of bytes to write. Ideally it's a multiple of four. + +The `xxhash32` is the result of a 32-bit xxHash operation. The seed is defined in fobooster.h as `FOBOOSTER_SEED`, and is `0xc38b9e66L`. + +Finally, `payload` is an array of uint32 values. + +The `struct fobooster_data` object is placed directly following the program image. The `make-booster` program copies the contents of `booster.bin` to a new file, then appends `struct booster_data` to the end. That way, all `booster` has to do is refer to the contents of the struct in order to program the data. + +As a happy coincidence, if `struct booster_data` is not populated (i.e. if you just flash the raw `booster.bin` to a device), then `xxhash32` will not be valid and `booster.bin` will simply reset the device. + +Because a bitstream image is 104090 bytes, and the ICE40 has 131072 bytes of RAM, a bitstream fits entirely in RAM. This allows us to validate the image is good prior to writing it to SPI flash. \ No newline at end of file diff --git a/fobooster/include/csr.h b/fobooster/include/csr.h new file mode 100644 index 0000000..e6f0259 --- /dev/null +++ b/fobooster/include/csr.h @@ -0,0 +1,711 @@ +#ifndef __GENERATED_CSR_H +#define __GENERATED_CSR_H +#include + +static inline void csr_writeb(uint8_t value, uint32_t addr) +{ + *((volatile uint8_t *)addr) = value; +} + +static inline uint8_t csr_readb(uint32_t addr) +{ + return *(volatile uint8_t *)addr; +} + +static inline void csr_writew(uint16_t value, uint32_t addr) +{ + *((volatile uint16_t *)addr) = value; +} + +static inline uint16_t csr_readw(uint32_t addr) +{ + return *(volatile uint16_t *)addr; +} + +static inline void csr_writel(uint32_t value, uint32_t addr) +{ + *((volatile uint32_t *)addr) = value; +} + +static inline uint32_t csr_readl(uint32_t addr) +{ + return *(volatile uint32_t *)addr; +} + +/* ctrl */ +#define CSR_CTRL_BASE 0xe0000000 +#define CSR_CTRL_RESET_ADDR 0xe0000000 +#define CSR_CTRL_RESET_SIZE 1 +static inline unsigned char ctrl_reset_read(void) { + unsigned char r = csr_readl(0xe0000000); + return r; +} +static inline void ctrl_reset_write(unsigned char value) { + csr_writel(value, 0xe0000000); +} +#define CSR_CTRL_SCRATCH_ADDR 0xe0000004 +#define CSR_CTRL_SCRATCH_SIZE 4 +static inline unsigned int ctrl_scratch_read(void) { + unsigned int r = csr_readl(0xe0000004); + r <<= 8; + r |= csr_readl(0xe0000008); + r <<= 8; + r |= csr_readl(0xe000000c); + r <<= 8; + r |= csr_readl(0xe0000010); + return r; +} +static inline void ctrl_scratch_write(unsigned int value) { + csr_writel(value >> 24, 0xe0000004); + csr_writel(value >> 16, 0xe0000008); + csr_writel(value >> 8, 0xe000000c); + csr_writel(value, 0xe0000010); +} +#define CSR_CTRL_BUS_ERRORS_ADDR 0xe0000014 +#define CSR_CTRL_BUS_ERRORS_SIZE 4 +static inline unsigned int ctrl_bus_errors_read(void) { + unsigned int r = csr_readl(0xe0000014); + r <<= 8; + r |= csr_readl(0xe0000018); + r <<= 8; + r |= csr_readl(0xe000001c); + r <<= 8; + r |= csr_readl(0xe0000020); + return r; +} + +/* picorvspi */ +#define CSR_PICORVSPI_BASE 0xe0005000 +#define CSR_PICORVSPI_CFG1_ADDR 0xe0005000 +#define CSR_PICORVSPI_CFG1_SIZE 1 +static inline unsigned char picorvspi_cfg1_read(void) { + unsigned char r = csr_readl(0xe0005000); + return r; +} +static inline void picorvspi_cfg1_write(unsigned char value) { + csr_writel(value, 0xe0005000); +} +#define CSR_PICORVSPI_CFG2_ADDR 0xe0005004 +#define CSR_PICORVSPI_CFG2_SIZE 1 +static inline unsigned char picorvspi_cfg2_read(void) { + unsigned char r = csr_readl(0xe0005004); + return r; +} +static inline void picorvspi_cfg2_write(unsigned char value) { + csr_writel(value, 0xe0005004); +} +#define CSR_PICORVSPI_CFG3_ADDR 0xe0005008 +#define CSR_PICORVSPI_CFG3_SIZE 1 +static inline unsigned char picorvspi_cfg3_read(void) { + unsigned char r = csr_readl(0xe0005008); + return r; +} +static inline void picorvspi_cfg3_write(unsigned char value) { + csr_writel(value, 0xe0005008); +} +#define CSR_PICORVSPI_CFG4_ADDR 0xe000500c +#define CSR_PICORVSPI_CFG4_SIZE 1 +static inline unsigned char picorvspi_cfg4_read(void) { + unsigned char r = csr_readl(0xe000500c); + return r; +} +static inline void picorvspi_cfg4_write(unsigned char value) { + csr_writel(value, 0xe000500c); +} +#define CSR_PICORVSPI_STAT1_ADDR 0xe0005010 +#define CSR_PICORVSPI_STAT1_SIZE 1 +static inline unsigned char picorvspi_stat1_read(void) { + unsigned char r = csr_readl(0xe0005010); + return r; +} +#define CSR_PICORVSPI_STAT2_ADDR 0xe0005014 +#define CSR_PICORVSPI_STAT2_SIZE 1 +static inline unsigned char picorvspi_stat2_read(void) { + unsigned char r = csr_readl(0xe0005014); + return r; +} +#define CSR_PICORVSPI_STAT3_ADDR 0xe0005018 +#define CSR_PICORVSPI_STAT3_SIZE 1 +static inline unsigned char picorvspi_stat3_read(void) { + unsigned char r = csr_readl(0xe0005018); + return r; +} +#define CSR_PICORVSPI_STAT4_ADDR 0xe000501c +#define CSR_PICORVSPI_STAT4_SIZE 1 +static inline unsigned char picorvspi_stat4_read(void) { + unsigned char r = csr_readl(0xe000501c); + return r; +} + +/* reboot */ +#define CSR_REBOOT_BASE 0xe0006000 +#define CSR_REBOOT_CTRL_ADDR 0xe0006000 +#define CSR_REBOOT_CTRL_SIZE 1 +static inline unsigned char reboot_ctrl_read(void) { + unsigned char r = csr_readl(0xe0006000); + return r; +} +static inline void reboot_ctrl_write(unsigned char value) { + csr_writel(value, 0xe0006000); +} +#define CSR_REBOOT_ADDR_ADDR 0xe0006004 +#define CSR_REBOOT_ADDR_SIZE 4 +static inline unsigned int reboot_addr_read(void) { + unsigned int r = csr_readl(0xe0006004); + r <<= 8; + r |= csr_readl(0xe0006008); + r <<= 8; + r |= csr_readl(0xe000600c); + r <<= 8; + r |= csr_readl(0xe0006010); + return r; +} +static inline void reboot_addr_write(unsigned int value) { + csr_writel(value >> 24, 0xe0006004); + csr_writel(value >> 16, 0xe0006008); + csr_writel(value >> 8, 0xe000600c); + csr_writel(value, 0xe0006010); +} + +/* rgb */ +#define CSR_RGB_BASE 0xe0006800 +#define CSR_RGB_DAT_ADDR 0xe0006800 +#define CSR_RGB_DAT_SIZE 1 +static inline unsigned char rgb_dat_read(void) { + unsigned char r = csr_readl(0xe0006800); + return r; +} +static inline void rgb_dat_write(unsigned char value) { + csr_writel(value, 0xe0006800); +} +#define CSR_RGB_ADDR_ADDR 0xe0006804 +#define CSR_RGB_ADDR_SIZE 1 +static inline unsigned char rgb_addr_read(void) { + unsigned char r = csr_readl(0xe0006804); + return r; +} +static inline void rgb_addr_write(unsigned char value) { + csr_writel(value, 0xe0006804); +} +#define CSR_RGB_CTRL_ADDR 0xe0006808 +#define CSR_RGB_CTRL_SIZE 1 +static inline unsigned char rgb_ctrl_read(void) { + unsigned char r = csr_readl(0xe0006808); + return r; +} +static inline void rgb_ctrl_write(unsigned char value) { + csr_writel(value, 0xe0006808); +} + +/* timer0 */ +#define CSR_TIMER0_BASE 0xe0002800 +#define CSR_TIMER0_LOAD_ADDR 0xe0002800 +#define CSR_TIMER0_LOAD_SIZE 4 +static inline unsigned int timer0_load_read(void) { + unsigned int r = csr_readl(0xe0002800); + r <<= 8; + r |= csr_readl(0xe0002804); + r <<= 8; + r |= csr_readl(0xe0002808); + r <<= 8; + r |= csr_readl(0xe000280c); + return r; +} +static inline void timer0_load_write(unsigned int value) { + csr_writel(value >> 24, 0xe0002800); + csr_writel(value >> 16, 0xe0002804); + csr_writel(value >> 8, 0xe0002808); + csr_writel(value, 0xe000280c); +} +#define CSR_TIMER0_RELOAD_ADDR 0xe0002810 +#define CSR_TIMER0_RELOAD_SIZE 4 +static inline unsigned int timer0_reload_read(void) { + unsigned int r = csr_readl(0xe0002810); + r <<= 8; + r |= csr_readl(0xe0002814); + r <<= 8; + r |= csr_readl(0xe0002818); + r <<= 8; + r |= csr_readl(0xe000281c); + return r; +} +static inline void timer0_reload_write(unsigned int value) { + csr_writel(value >> 24, 0xe0002810); + csr_writel(value >> 16, 0xe0002814); + csr_writel(value >> 8, 0xe0002818); + csr_writel(value, 0xe000281c); +} +#define CSR_TIMER0_EN_ADDR 0xe0002820 +#define CSR_TIMER0_EN_SIZE 1 +static inline unsigned char timer0_en_read(void) { + unsigned char r = csr_readl(0xe0002820); + return r; +} +static inline void timer0_en_write(unsigned char value) { + csr_writel(value, 0xe0002820); +} +#define CSR_TIMER0_UPDATE_VALUE_ADDR 0xe0002824 +#define CSR_TIMER0_UPDATE_VALUE_SIZE 1 +static inline unsigned char timer0_update_value_read(void) { + unsigned char r = csr_readl(0xe0002824); + return r; +} +static inline void timer0_update_value_write(unsigned char value) { + csr_writel(value, 0xe0002824); +} +#define CSR_TIMER0_VALUE_ADDR 0xe0002828 +#define CSR_TIMER0_VALUE_SIZE 4 +static inline unsigned int timer0_value_read(void) { + unsigned int r = csr_readl(0xe0002828); + r <<= 8; + r |= csr_readl(0xe000282c); + r <<= 8; + r |= csr_readl(0xe0002830); + r <<= 8; + r |= csr_readl(0xe0002834); + return r; +} +#define CSR_TIMER0_EV_STATUS_ADDR 0xe0002838 +#define CSR_TIMER0_EV_STATUS_SIZE 1 +static inline unsigned char timer0_ev_status_read(void) { + unsigned char r = csr_readl(0xe0002838); + return r; +} +static inline void timer0_ev_status_write(unsigned char value) { + csr_writel(value, 0xe0002838); +} +#define CSR_TIMER0_EV_PENDING_ADDR 0xe000283c +#define CSR_TIMER0_EV_PENDING_SIZE 1 +static inline unsigned char timer0_ev_pending_read(void) { + unsigned char r = csr_readl(0xe000283c); + return r; +} +static inline void timer0_ev_pending_write(unsigned char value) { + csr_writel(value, 0xe000283c); +} +#define CSR_TIMER0_EV_ENABLE_ADDR 0xe0002840 +#define CSR_TIMER0_EV_ENABLE_SIZE 1 +static inline unsigned char timer0_ev_enable_read(void) { + unsigned char r = csr_readl(0xe0002840); + return r; +} +static inline void timer0_ev_enable_write(unsigned char value) { + csr_writel(value, 0xe0002840); +} + +/* touch */ +#define CSR_TOUCH_BASE 0xe0005800 +#define CSR_TOUCH_O_ADDR 0xe0005800 +#define CSR_TOUCH_O_SIZE 1 +static inline unsigned char touch_o_read(void) { + unsigned char r = csr_readl(0xe0005800); + return r; +} +static inline void touch_o_write(unsigned char value) { + csr_writel(value, 0xe0005800); +} +#define CSR_TOUCH_OE_ADDR 0xe0005804 +#define CSR_TOUCH_OE_SIZE 1 +static inline unsigned char touch_oe_read(void) { + unsigned char r = csr_readl(0xe0005804); + return r; +} +static inline void touch_oe_write(unsigned char value) { + csr_writel(value, 0xe0005804); +} +#define CSR_TOUCH_I_ADDR 0xe0005808 +#define CSR_TOUCH_I_SIZE 1 +static inline unsigned char touch_i_read(void) { + unsigned char r = csr_readl(0xe0005808); + return r; +} + +/* usb */ +#define CSR_USB_BASE 0xe0004800 +#define CSR_USB_PULLUP_OUT_ADDR 0xe0004800 +#define CSR_USB_PULLUP_OUT_SIZE 1 +static inline unsigned char usb_pullup_out_read(void) { + unsigned char r = csr_readl(0xe0004800); + return r; +} +static inline void usb_pullup_out_write(unsigned char value) { + csr_writel(value, 0xe0004800); +} +#define CSR_USB_EP_0_OUT_EV_STATUS_ADDR 0xe0004804 +#define CSR_USB_EP_0_OUT_EV_STATUS_SIZE 1 +static inline unsigned char usb_ep_0_out_ev_status_read(void) { + unsigned char r = csr_readl(0xe0004804); + return r; +} +static inline void usb_ep_0_out_ev_status_write(unsigned char value) { + csr_writel(value, 0xe0004804); +} +#define CSR_USB_EP_0_OUT_EV_PENDING_ADDR 0xe0004808 +#define CSR_USB_EP_0_OUT_EV_PENDING_SIZE 1 +static inline unsigned char usb_ep_0_out_ev_pending_read(void) { + unsigned char r = csr_readl(0xe0004808); + return r; +} +static inline void usb_ep_0_out_ev_pending_write(unsigned char value) { + csr_writel(value, 0xe0004808); +} +#define CSR_USB_EP_0_OUT_EV_ENABLE_ADDR 0xe000480c +#define CSR_USB_EP_0_OUT_EV_ENABLE_SIZE 1 +static inline unsigned char usb_ep_0_out_ev_enable_read(void) { + unsigned char r = csr_readl(0xe000480c); + return r; +} +static inline void usb_ep_0_out_ev_enable_write(unsigned char value) { + csr_writel(value, 0xe000480c); +} +#define CSR_USB_EP_0_OUT_LAST_TOK_ADDR 0xe0004810 +#define CSR_USB_EP_0_OUT_LAST_TOK_SIZE 1 +static inline unsigned char usb_ep_0_out_last_tok_read(void) { + unsigned char r = csr_readl(0xe0004810); + return r; +} +#define CSR_USB_EP_0_OUT_RESPOND_ADDR 0xe0004814 +#define CSR_USB_EP_0_OUT_RESPOND_SIZE 1 +static inline unsigned char usb_ep_0_out_respond_read(void) { + unsigned char r = csr_readl(0xe0004814); + return r; +} +static inline void usb_ep_0_out_respond_write(unsigned char value) { + csr_writel(value, 0xe0004814); +} +#define CSR_USB_EP_0_OUT_DTB_ADDR 0xe0004818 +#define CSR_USB_EP_0_OUT_DTB_SIZE 1 +static inline unsigned char usb_ep_0_out_dtb_read(void) { + unsigned char r = csr_readl(0xe0004818); + return r; +} +static inline void usb_ep_0_out_dtb_write(unsigned char value) { + csr_writel(value, 0xe0004818); +} +#define CSR_USB_EP_0_OUT_OBUF_HEAD_ADDR 0xe000481c +#define CSR_USB_EP_0_OUT_OBUF_HEAD_SIZE 1 +static inline unsigned char usb_ep_0_out_obuf_head_read(void) { + unsigned char r = csr_readl(0xe000481c); + return r; +} +static inline void usb_ep_0_out_obuf_head_write(unsigned char value) { + csr_writel(value, 0xe000481c); +} +#define CSR_USB_EP_0_OUT_OBUF_EMPTY_ADDR 0xe0004820 +#define CSR_USB_EP_0_OUT_OBUF_EMPTY_SIZE 1 +static inline unsigned char usb_ep_0_out_obuf_empty_read(void) { + unsigned char r = csr_readl(0xe0004820); + return r; +} +#define CSR_USB_EP_0_IN_EV_STATUS_ADDR 0xe0004824 +#define CSR_USB_EP_0_IN_EV_STATUS_SIZE 1 +static inline unsigned char usb_ep_0_in_ev_status_read(void) { + unsigned char r = csr_readl(0xe0004824); + return r; +} +static inline void usb_ep_0_in_ev_status_write(unsigned char value) { + csr_writel(value, 0xe0004824); +} +#define CSR_USB_EP_0_IN_EV_PENDING_ADDR 0xe0004828 +#define CSR_USB_EP_0_IN_EV_PENDING_SIZE 1 +static inline unsigned char usb_ep_0_in_ev_pending_read(void) { + unsigned char r = csr_readl(0xe0004828); + return r; +} +static inline void usb_ep_0_in_ev_pending_write(unsigned char value) { + csr_writel(value, 0xe0004828); +} +#define CSR_USB_EP_0_IN_EV_ENABLE_ADDR 0xe000482c +#define CSR_USB_EP_0_IN_EV_ENABLE_SIZE 1 +static inline unsigned char usb_ep_0_in_ev_enable_read(void) { + unsigned char r = csr_readl(0xe000482c); + return r; +} +static inline void usb_ep_0_in_ev_enable_write(unsigned char value) { + csr_writel(value, 0xe000482c); +} +#define CSR_USB_EP_0_IN_LAST_TOK_ADDR 0xe0004830 +#define CSR_USB_EP_0_IN_LAST_TOK_SIZE 1 +static inline unsigned char usb_ep_0_in_last_tok_read(void) { + unsigned char r = csr_readl(0xe0004830); + return r; +} +#define CSR_USB_EP_0_IN_RESPOND_ADDR 0xe0004834 +#define CSR_USB_EP_0_IN_RESPOND_SIZE 1 +static inline unsigned char usb_ep_0_in_respond_read(void) { + unsigned char r = csr_readl(0xe0004834); + return r; +} +static inline void usb_ep_0_in_respond_write(unsigned char value) { + csr_writel(value, 0xe0004834); +} +#define CSR_USB_EP_0_IN_DTB_ADDR 0xe0004838 +#define CSR_USB_EP_0_IN_DTB_SIZE 1 +static inline unsigned char usb_ep_0_in_dtb_read(void) { + unsigned char r = csr_readl(0xe0004838); + return r; +} +static inline void usb_ep_0_in_dtb_write(unsigned char value) { + csr_writel(value, 0xe0004838); +} +#define CSR_USB_EP_0_IN_IBUF_HEAD_ADDR 0xe000483c +#define CSR_USB_EP_0_IN_IBUF_HEAD_SIZE 1 +static inline unsigned char usb_ep_0_in_ibuf_head_read(void) { + unsigned char r = csr_readl(0xe000483c); + return r; +} +static inline void usb_ep_0_in_ibuf_head_write(unsigned char value) { + csr_writel(value, 0xe000483c); +} +#define CSR_USB_EP_0_IN_IBUF_EMPTY_ADDR 0xe0004840 +#define CSR_USB_EP_0_IN_IBUF_EMPTY_SIZE 1 +static inline unsigned char usb_ep_0_in_ibuf_empty_read(void) { + unsigned char r = csr_readl(0xe0004840); + return r; +} +#define CSR_USB_EP_1_IN_EV_STATUS_ADDR 0xe0004844 +#define CSR_USB_EP_1_IN_EV_STATUS_SIZE 1 +static inline unsigned char usb_ep_1_in_ev_status_read(void) { + unsigned char r = csr_readl(0xe0004844); + return r; +} +static inline void usb_ep_1_in_ev_status_write(unsigned char value) { + csr_writel(value, 0xe0004844); +} +#define CSR_USB_EP_1_IN_EV_PENDING_ADDR 0xe0004848 +#define CSR_USB_EP_1_IN_EV_PENDING_SIZE 1 +static inline unsigned char usb_ep_1_in_ev_pending_read(void) { + unsigned char r = csr_readl(0xe0004848); + return r; +} +static inline void usb_ep_1_in_ev_pending_write(unsigned char value) { + csr_writel(value, 0xe0004848); +} +#define CSR_USB_EP_1_IN_EV_ENABLE_ADDR 0xe000484c +#define CSR_USB_EP_1_IN_EV_ENABLE_SIZE 1 +static inline unsigned char usb_ep_1_in_ev_enable_read(void) { + unsigned char r = csr_readl(0xe000484c); + return r; +} +static inline void usb_ep_1_in_ev_enable_write(unsigned char value) { + csr_writel(value, 0xe000484c); +} +#define CSR_USB_EP_1_IN_LAST_TOK_ADDR 0xe0004850 +#define CSR_USB_EP_1_IN_LAST_TOK_SIZE 1 +static inline unsigned char usb_ep_1_in_last_tok_read(void) { + unsigned char r = csr_readl(0xe0004850); + return r; +} +#define CSR_USB_EP_1_IN_RESPOND_ADDR 0xe0004854 +#define CSR_USB_EP_1_IN_RESPOND_SIZE 1 +static inline unsigned char usb_ep_1_in_respond_read(void) { + unsigned char r = csr_readl(0xe0004854); + return r; +} +static inline void usb_ep_1_in_respond_write(unsigned char value) { + csr_writel(value, 0xe0004854); +} +#define CSR_USB_EP_1_IN_DTB_ADDR 0xe0004858 +#define CSR_USB_EP_1_IN_DTB_SIZE 1 +static inline unsigned char usb_ep_1_in_dtb_read(void) { + unsigned char r = csr_readl(0xe0004858); + return r; +} +static inline void usb_ep_1_in_dtb_write(unsigned char value) { + csr_writel(value, 0xe0004858); +} +#define CSR_USB_EP_1_IN_IBUF_HEAD_ADDR 0xe000485c +#define CSR_USB_EP_1_IN_IBUF_HEAD_SIZE 1 +static inline unsigned char usb_ep_1_in_ibuf_head_read(void) { + unsigned char r = csr_readl(0xe000485c); + return r; +} +static inline void usb_ep_1_in_ibuf_head_write(unsigned char value) { + csr_writel(value, 0xe000485c); +} +#define CSR_USB_EP_1_IN_IBUF_EMPTY_ADDR 0xe0004860 +#define CSR_USB_EP_1_IN_IBUF_EMPTY_SIZE 1 +static inline unsigned char usb_ep_1_in_ibuf_empty_read(void) { + unsigned char r = csr_readl(0xe0004860); + return r; +} +#define CSR_USB_EP_2_OUT_EV_STATUS_ADDR 0xe0004864 +#define CSR_USB_EP_2_OUT_EV_STATUS_SIZE 1 +static inline unsigned char usb_ep_2_out_ev_status_read(void) { + unsigned char r = csr_readl(0xe0004864); + return r; +} +static inline void usb_ep_2_out_ev_status_write(unsigned char value) { + csr_writel(value, 0xe0004864); +} +#define CSR_USB_EP_2_OUT_EV_PENDING_ADDR 0xe0004868 +#define CSR_USB_EP_2_OUT_EV_PENDING_SIZE 1 +static inline unsigned char usb_ep_2_out_ev_pending_read(void) { + unsigned char r = csr_readl(0xe0004868); + return r; +} +static inline void usb_ep_2_out_ev_pending_write(unsigned char value) { + csr_writel(value, 0xe0004868); +} +#define CSR_USB_EP_2_OUT_EV_ENABLE_ADDR 0xe000486c +#define CSR_USB_EP_2_OUT_EV_ENABLE_SIZE 1 +static inline unsigned char usb_ep_2_out_ev_enable_read(void) { + unsigned char r = csr_readl(0xe000486c); + return r; +} +static inline void usb_ep_2_out_ev_enable_write(unsigned char value) { + csr_writel(value, 0xe000486c); +} +#define CSR_USB_EP_2_OUT_LAST_TOK_ADDR 0xe0004870 +#define CSR_USB_EP_2_OUT_LAST_TOK_SIZE 1 +static inline unsigned char usb_ep_2_out_last_tok_read(void) { + unsigned char r = csr_readl(0xe0004870); + return r; +} +#define CSR_USB_EP_2_OUT_RESPOND_ADDR 0xe0004874 +#define CSR_USB_EP_2_OUT_RESPOND_SIZE 1 +static inline unsigned char usb_ep_2_out_respond_read(void) { + unsigned char r = csr_readl(0xe0004874); + return r; +} +static inline void usb_ep_2_out_respond_write(unsigned char value) { + csr_writel(value, 0xe0004874); +} +#define CSR_USB_EP_2_OUT_DTB_ADDR 0xe0004878 +#define CSR_USB_EP_2_OUT_DTB_SIZE 1 +static inline unsigned char usb_ep_2_out_dtb_read(void) { + unsigned char r = csr_readl(0xe0004878); + return r; +} +static inline void usb_ep_2_out_dtb_write(unsigned char value) { + csr_writel(value, 0xe0004878); +} +#define CSR_USB_EP_2_OUT_OBUF_HEAD_ADDR 0xe000487c +#define CSR_USB_EP_2_OUT_OBUF_HEAD_SIZE 1 +static inline unsigned char usb_ep_2_out_obuf_head_read(void) { + unsigned char r = csr_readl(0xe000487c); + return r; +} +static inline void usb_ep_2_out_obuf_head_write(unsigned char value) { + csr_writel(value, 0xe000487c); +} +#define CSR_USB_EP_2_OUT_OBUF_EMPTY_ADDR 0xe0004880 +#define CSR_USB_EP_2_OUT_OBUF_EMPTY_SIZE 1 +static inline unsigned char usb_ep_2_out_obuf_empty_read(void) { + unsigned char r = csr_readl(0xe0004880); + return r; +} +#define CSR_USB_EP_2_IN_EV_STATUS_ADDR 0xe0004884 +#define CSR_USB_EP_2_IN_EV_STATUS_SIZE 1 +static inline unsigned char usb_ep_2_in_ev_status_read(void) { + unsigned char r = csr_readl(0xe0004884); + return r; +} +static inline void usb_ep_2_in_ev_status_write(unsigned char value) { + csr_writel(value, 0xe0004884); +} +#define CSR_USB_EP_2_IN_EV_PENDING_ADDR 0xe0004888 +#define CSR_USB_EP_2_IN_EV_PENDING_SIZE 1 +static inline unsigned char usb_ep_2_in_ev_pending_read(void) { + unsigned char r = csr_readl(0xe0004888); + return r; +} +static inline void usb_ep_2_in_ev_pending_write(unsigned char value) { + csr_writel(value, 0xe0004888); +} +#define CSR_USB_EP_2_IN_EV_ENABLE_ADDR 0xe000488c +#define CSR_USB_EP_2_IN_EV_ENABLE_SIZE 1 +static inline unsigned char usb_ep_2_in_ev_enable_read(void) { + unsigned char r = csr_readl(0xe000488c); + return r; +} +static inline void usb_ep_2_in_ev_enable_write(unsigned char value) { + csr_writel(value, 0xe000488c); +} +#define CSR_USB_EP_2_IN_LAST_TOK_ADDR 0xe0004890 +#define CSR_USB_EP_2_IN_LAST_TOK_SIZE 1 +static inline unsigned char usb_ep_2_in_last_tok_read(void) { + unsigned char r = csr_readl(0xe0004890); + return r; +} +#define CSR_USB_EP_2_IN_RESPOND_ADDR 0xe0004894 +#define CSR_USB_EP_2_IN_RESPOND_SIZE 1 +static inline unsigned char usb_ep_2_in_respond_read(void) { + unsigned char r = csr_readl(0xe0004894); + return r; +} +static inline void usb_ep_2_in_respond_write(unsigned char value) { + csr_writel(value, 0xe0004894); +} +#define CSR_USB_EP_2_IN_DTB_ADDR 0xe0004898 +#define CSR_USB_EP_2_IN_DTB_SIZE 1 +static inline unsigned char usb_ep_2_in_dtb_read(void) { + unsigned char r = csr_readl(0xe0004898); + return r; +} +static inline void usb_ep_2_in_dtb_write(unsigned char value) { + csr_writel(value, 0xe0004898); +} +#define CSR_USB_EP_2_IN_IBUF_HEAD_ADDR 0xe000489c +#define CSR_USB_EP_2_IN_IBUF_HEAD_SIZE 1 +static inline unsigned char usb_ep_2_in_ibuf_head_read(void) { + unsigned char r = csr_readl(0xe000489c); + return r; +} +static inline void usb_ep_2_in_ibuf_head_write(unsigned char value) { + csr_writel(value, 0xe000489c); +} +#define CSR_USB_EP_2_IN_IBUF_EMPTY_ADDR 0xe00048a0 +#define CSR_USB_EP_2_IN_IBUF_EMPTY_SIZE 1 +static inline unsigned char usb_ep_2_in_ibuf_empty_read(void) { + unsigned char r = csr_readl(0xe00048a0); + return r; +} + +/* constants */ +#define NMI_INTERRUPT 0 +static inline int nmi_interrupt_read(void) { + return 0; +} +#define TIMER0_INTERRUPT 1 +static inline int timer0_interrupt_read(void) { + return 1; +} +#define UART_INTERRUPT 2 +static inline int uart_interrupt_read(void) { + return 2; +} +#define USB_INTERRUPT 3 +static inline int usb_interrupt_read(void) { + return 3; +} +#define CSR_DATA_WIDTH 8 +static inline int csr_data_width_read(void) { + return 8; +} +#define SYSTEM_CLOCK_FREQUENCY 12000000 +static inline int system_clock_frequency_read(void) { + return 12000000; +} +#define CONFIG_CLOCK_FREQUENCY 12000000 +static inline int config_clock_frequency_read(void) { + return 12000000; +} +#define CONFIG_CPU_RESET_ADDR 0 +static inline int config_cpu_reset_addr_read(void) { + return 0; +} +#define CONFIG_CPU_TYPE "VEXRISCV" +static inline const char * config_cpu_type_read(void) { + return "VEXRISCV"; +} +#define CONFIG_CPU_VARIANT "VEXRISCV" +static inline const char * config_cpu_variant_read(void) { + return "VEXRISCV"; +} +#define CONFIG_CSR_DATA_WIDTH 8 +static inline int config_csr_data_width_read(void) { + return 8; +} + +#endif diff --git a/fobooster/include/fobooster.h b/fobooster/include/fobooster.h new file mode 100644 index 0000000..59506c7 --- /dev/null +++ b/fobooster/include/fobooster.h @@ -0,0 +1,22 @@ +#ifndef FOBOOSTER_H_ +#define FOBOOSTER_H_ + +#include +#include + +#define XXH_NO_LONG_LONG +#define XXH_FORCE_ALIGN_CHECK 0 +#define XXH_FORCE_NATIVE_FORMAT 0 +#define XXH_PRIVATE_API +#include "xxhash.h" + +#define FOBOOSTER_SEED 0xc38b9e66L + +struct fobooster_data +{ + uint32_t payload_size; + uint32_t xxhash; + uint32_t payload[0]; +}; + +#endif /* FOBOOSTER_H_ */ \ No newline at end of file diff --git a/fobooster/include/spi.h b/fobooster/include/spi.h new file mode 100644 index 0000000..743c58e --- /dev/null +++ b/fobooster/include/spi.h @@ -0,0 +1,21 @@ +#ifndef _FOBOOST_SPI_H +#define _FOBOOST_SPI_H + +#include + +#define SPI_ERASE_SECTOR_SIZE 4096 +#define SPI_PROGRAM_PAGE_SIZE 256 + +void spiBegin(void); +void spiEnd(void); +void spiPause(void); +void spiCommand(uint8_t cmd); +uint8_t spiCommandRx(void); +uint8_t spiReadStatus(void); +void spiBeginErase4(uint32_t erase_addr); +void spiBeginErase32(uint32_t erase_addr); +void spiBeginErase64(uint32_t erase_addr); +int spiIsBusy(void); +void spiBeginWrite(uint32_t addr, const void *v_data, unsigned int count); + +#endif /* _FOBOOST_SPI_H */ \ No newline at end of file diff --git a/fobooster/include/xxhash.c b/fobooster/include/xxhash.c new file mode 100644 index 0000000..6f85fc4 --- /dev/null +++ b/fobooster/include/xxhash.c @@ -0,0 +1,912 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif defined(__INTEL_COMPILER) || \ + (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#endif + +/*!XXH_FORCE_NATIVE_FORMAT : + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. + * Results are therefore identical for little-endian and big-endian CPU. + * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. + * Should endian-independence be of no importance for your application, you may set the #define below to 1, + * to improve speed for Big-endian CPU. + * This option has no impact on Little_Endian CPU. + */ +#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; + * set it to 0 when the input is guaranteed to be aligned, + * or when alignment doesn't matter for performance. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define FORCE_INLINE static __forceinline +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; +# endif +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* ************************************* +* Architecture Macros +***************************************/ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN + static const int g_one = 1; +# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one)) +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + else + return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); +} + +FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE32_align(ptr, endian, XXH_unaligned); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; +static const U32 PRIME32_2 = 2246822519U; +static const U32 PRIME32_3 = 3266489917U; +static const U32 PRIME32_4 = 668265263U; +static const U32 PRIME32_5 = 374761393U; + +static U32 XXH32_round(U32 seed, U32 input) +{ + seed += input * PRIME32_2; + seed = XXH_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; +#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p<=limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32) len; + + while (p+4<=bEnd) { + h32 += XXH_get32bits(p) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + p+=4; + } + + while (p> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +FORCE_INLINE +XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + state->total_len_32 += (unsigned)len; + state->large_len |= (len>=16) | (state->total_len_32>=16); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (unsigned)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const unsigned* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + + +FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +{ + const BYTE * p = (const BYTE*)state->mem32; + const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize; + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + while (p+4<=bEnd) { + h32 += XXH_readLE32(p, endian) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4; + p+=4; + } + + while (p> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_digest_endian(state_in, XXH_littleEndian); + else + return XXH32_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t U64; +# else + /* if compiler doesn't support unsigned long long, replace by another 64-bit type */ + typedef unsigned long long U64; +# endif +#endif + + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + else + return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + +FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE64_align(ptr, endian, XXH_unaligned); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; +static const U64 PRIME64_2 = 14029467366897019727ULL; +static const U64 PRIME64_3 = 1609587929392839161ULL; +static const U64 PRIME64_4 = 9650029242287828579ULL; +static const U64 PRIME64_5 = 2870177450012600261ULL; + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; +#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + while (p+8<=bEnd) { + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); + h64 ^= k1; + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; + p+=8; + } + + if (p+4<=bEnd) { + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + p+=4; + } + + while (p> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + + return h64; +} + + +XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + +FORCE_INLINE +XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH64_update_endian(state_in, input, len, XXH_bigEndian); +} + +FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +{ + const BYTE * p = (const BYTE*)state->mem64; + const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize; + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 + PRIME64_5; + } + + h64 += (U64) state->total_len; + + while (p+8<=bEnd) { + U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian)); + h64 ^= k1; + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; + p+=8; + } + + if (p+4<=bEnd) { + h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1; + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + p+=4; + } + + while (p> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + + return h64; +} + +XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_digest_endian(state_in, XXH_littleEndian); + else + return XXH64_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#endif /* XXH_NO_LONG_LONG */ diff --git a/fobooster/include/xxhash.h b/fobooster/include/xxhash.h new file mode 100644 index 0000000..4099da0 --- /dev/null +++ b/fobooster/include/xxhash.h @@ -0,0 +1,294 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** +* API modifier +******************************/ +/** XXH_PRIVATE_API +* This is useful to include xxhash functions in `static` mode +* in order to inline them, and remove their symbol from the public list. +* Methodology : +* #define XXH_PRIVATE_API +* #include "xxhash.h" +* `xxhash.c` is automatically included. +* It's not useful to compile and link it as a separate module. +*/ +#ifdef XXH_PRIVATE_API +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# define XXH_PUBLIC_API /* do nothing */ +#endif /* XXH_PRIVATE_API */ + +/*!XXH_NAMESPACE, aka Namespace Emulation : + +If you want to include _and expose_ xxHash functions from within your own library, +but also want to avoid symbol collisions with other libraries which may also include xxHash, + +you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library +with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + +Note that no change is required within the calling program as long as it includes `xxhash.h` : +regular symbol name will be automatically translated by this header. +*/ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 6 +#define XXH_VERSION_RELEASE 4 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +typedef unsigned int XXH32_hash_t; + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* +These functions generate the xxHash of an input provided in multiple segments. +Note that, for small input, they are slower than single-call functions, due to state management. +For small input, prefer `XXH32()` and `XXH64()` . + +XXH state must first be allocated, using XXH*_createState() . + +Start a new hash by initializing state with a seed, using XXH*_reset(). + +Then, feed the hash state by calling XXH*_update() as many times as necessary. +Obviously, input must be allocated and read accessible. +The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + +Finally, a hash value can be produced anytime, by using XXH*_digest(). +This function returns the nn-bits hash as an int or long long. + +It's still possible to continue inserting input into the hash state after a digest, +and generate some new hashes later on, by calling again XXH*_digest(). + +When done, free XXH state space if it was allocated dynamically. +*/ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. +* The canonical representation uses human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. +*/ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +typedef unsigned long long XXH64_hash_t; + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#endif /* XXH_NO_LONG_LONG */ + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only meant to make possible + static allocation of XXH state, on stack or in a struct for example. + Never use members directly. */ + +struct XXH32_state_s { + unsigned total_len_32; + unsigned large_len; + unsigned v1; + unsigned v2; + unsigned v3; + unsigned v4; + unsigned mem32[4]; /* buffer defined as U32 for alignment */ + unsigned memsize; + unsigned reserved; /* never read nor write, will be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +#ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + unsigned long long total_len; + unsigned long long v1; + unsigned long long v2; + unsigned long long v3; + unsigned long long v4; + unsigned long long mem64[4]; /* buffer defined as U64 for alignment */ + unsigned memsize; + unsigned reserved[2]; /* never read nor write, will be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +#endif + +#ifdef XXH_PRIVATE_API +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/fobooster/ld/linker.ld b/fobooster/ld/linker.ld new file mode 100644 index 0000000..356a3ba --- /dev/null +++ b/fobooster/ld/linker.ld @@ -0,0 +1,60 @@ +INCLUDE output_format.ld +ENTRY(_start) + +__DYNAMIC = 0; + +INCLUDE regions.ld + +SECTIONS +{ + .startup : + { + . = ALIGN(4); + *(.text.start) + . = ALIGN(4); + _estartup = .; + } > rom + + .bss : + { + . = ALIGN(4); + _fbss = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + _end = .; + } > sram + + .data : AT (ADDR(.startup) + SIZEOF (.startup)) + { + _fdata = .; + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + _etext = .; + + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + *(.srodata) + _erodata = .; + + . = ALIGN(4); + *(.data .data.* .gnu.linkonce.d.*) + *(.data1) + _gp = ALIGN(16); + *(.sdata .sdata.* .gnu.linkonce.s.* .sdata2 .sdata2.*) + *(.ramtext .ramtext.*) + _edata = ALIGN(16); /* Make sure _edata is >= _gp. */ + } > sram + + fobooster_data = ADDR(.data) + SIZEOF(.data); + fobooster_src = ADDR(.startup) + SIZEOF(.startup) + SIZEOF(.data); +} + +PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 4); diff --git a/fobooster/ld/output_format.ld b/fobooster/ld/output_format.ld new file mode 100644 index 0000000..5e76f5f --- /dev/null +++ b/fobooster/ld/output_format.ld @@ -0,0 +1 @@ +OUTPUT_FORMAT("elf32-littleriscv") diff --git a/fobooster/ld/regions.ld b/fobooster/ld/regions.ld new file mode 100644 index 0000000..91a79a3 --- /dev/null +++ b/fobooster/ld/regions.ld @@ -0,0 +1,4 @@ +MEMORY { + sram : ORIGIN = 0x10000000, LENGTH = 0x00020000 + rom : ORIGIN = 0x20040000, LENGTH = 0x00040000 +} diff --git a/fobooster/make-booster.c b/fobooster/make-booster.c new file mode 100644 index 0000000..9edbac7 --- /dev/null +++ b/fobooster/make-booster.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#define htole32(x) OSSwapHostToLittleInt32(x) +#else +#include +#endif + +#define BOOSTER_BIN "booster.bin" + +#include "booster.h" + +int main(int argc, char **argv) { + int i; + struct booster_data booster_data; + + if (argc != 3) { + fprintf(stderr, "Usage: %s [infile] [outfile]\n", argv[0]); + return 1; + } + + char *infile_name = argv[1]; + char *outfile_name = argv[2]; + + int booster_fd = open(BOOSTER_BIN, O_RDONLY); + if (booster_fd == -1) { + perror("Unable to open " BOOSTER_BIN); + return 8; + } + + int infile_fd = open(infile_name, O_RDONLY); + if (infile_fd == -1) { + perror("Unable to open input file"); + return 2; + } + + int outfile_fd = open(outfile_name, O_WRONLY | O_CREAT | O_TRUNC, 0777); + if (outfile_fd == -1) { + perror("Unable to open output file"); + return 3; + } + + struct stat stat_buf; + if (-1 == fstat(infile_fd, &stat_buf)) { + perror("Unable to determine size of input file"); + return 4; + } + + // Copy the contents of the booster program + while (1) { + uint8_t b; + if (sizeof(b) != read(booster_fd, &b, sizeof(b))) { + break; + } + + if (sizeof(b) != write(outfile_fd, &b, sizeof(b))) { + perror("Unable to write to output file"); + return 10; + } + } + + uint8_t toboot_buffer[stat_buf.st_size]; + if (read(infile_fd, toboot_buffer, sizeof(toboot_buffer)) != sizeof(toboot_buffer)) { + perror("Unable to read input file into RAM"); + return 11; + } + + booster_data.payload_size = htole32(stat_buf.st_size); + booster_data.xxhash = htole32(XXH32(toboot_buffer, sizeof(toboot_buffer), BOOSTER_SEED)); + + if (sizeof(booster_data) != write(outfile_fd, &booster_data, sizeof(booster_data))) { + perror("Unable to write booster header to output file"); + return 12; + } + + if (sizeof(toboot_buffer) != write(outfile_fd, toboot_buffer, sizeof(toboot_buffer))) { + perror("Unable to write new bootloader to output file"); + return 7; + } + + close(infile_fd); + close(outfile_fd); + close(booster_fd); +} diff --git a/fobooster/src/crt0-vexriscv.S b/fobooster/src/crt0-vexriscv.S new file mode 100644 index 0000000..74816e8 --- /dev/null +++ b/fobooster/src/crt0-vexriscv.S @@ -0,0 +1,84 @@ +.global fobooster_main +.global isr + +.section .text.start +.global _start + +_start: + j crt_init + +.global trap_entry +.section .text +trap_entry: + sw x1, - 1*4(sp) + sw x5, - 2*4(sp) + sw x6, - 3*4(sp) + sw x7, - 4*4(sp) + sw x10, - 5*4(sp) + sw x11, - 6*4(sp) + sw x12, - 7*4(sp) + sw x13, - 8*4(sp) + sw x14, - 9*4(sp) + sw x15, -10*4(sp) + sw x16, -11*4(sp) + sw x17, -12*4(sp) + sw x28, -13*4(sp) + sw x29, -14*4(sp) + sw x30, -15*4(sp) + sw x31, -16*4(sp) + addi sp,sp,-16*4 + call isr + lw x1 , 15*4(sp) + lw x5, 14*4(sp) + lw x6, 13*4(sp) + lw x7, 12*4(sp) + lw x10, 11*4(sp) + lw x11, 10*4(sp) + lw x12, 9*4(sp) + lw x13, 8*4(sp) + lw x14, 7*4(sp) + lw x15, 6*4(sp) + lw x16, 5*4(sp) + lw x17, 4*4(sp) + lw x28, 3*4(sp) + lw x29, 2*4(sp) + lw x30, 1*4(sp) + lw x31, 0*4(sp) + addi sp,sp,16*4 + mret + +.section .text.start +crt_init: + la sp, _fstack + 4 + la a0, trap_entry + csrw mtvec, a0 + +bss_init: + la a0, _fbss + la a1, _ebss +bss_loop: + beq a0,a1,bss_done + sw zero,0(a0) + add a0,a0,4 + j bss_loop +bss_done: + + /* Load DATA */ + la t0, _estartup + la t1, _fdata + la t2, _edata +3: + lw t3, 0(t0) + sw t3, 0(t1) + /* _edata is aligned to 16 bytes. Use word-xfers. */ + addi t0, t0, 4 + addi t1, t1, 4 + bltu t1, t2, 3b + + li a0, 0x880 //880 enable timer + external interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt) + csrw mie,a0 + + call fobooster_main + +infinite_loop: + j infinite_loop diff --git a/fobooster/src/div.S b/fobooster/src/div.S new file mode 100644 index 0000000..b837820 --- /dev/null +++ b/fobooster/src/div.S @@ -0,0 +1,121 @@ + .text + .align 2 + +#ifndef __riscv64 +/* Our RV64 64-bit routines are equivalent to our RV32 32-bit routines. */ +# define __udivdi3 __udivsi3 +# define __umoddi3 __umodsi3 +# define __divdi3 __divsi3 +# define __moddi3 __modsi3 +#else + .globl __udivsi3 +__udivsi3: + /* Compute __udivdi3(a0 << 32, a1 << 32); cast result to uint32_t. */ + sll a0, a0, 32 + sll a1, a1, 32 + move t0, ra + jal __udivdi3 + sext.w a0, a0 + jr t0 + + .globl __umodsi3 +__umodsi3: + /* Compute __udivdi3((uint32_t)a0, (uint32_t)a1); cast a1 to uint32_t. */ + sll a0, a0, 32 + sll a1, a1, 32 + srl a0, a0, 32 + srl a1, a1, 32 + move t0, ra + jal __udivdi3 + sext.w a0, a1 + jr t0 + + .globl __modsi3 + __modsi3 = __moddi3 + + .globl __divsi3 +__divsi3: + /* Check for special case of INT_MIN/-1. Otherwise, fall into __divdi3. */ + li t0, -1 + beq a1, t0, .L20 +#endif + + .globl __divdi3 +__divdi3: + bltz a0, .L10 + bltz a1, .L11 + /* Since the quotient is positive, fall into __udivdi3. */ + + .globl __udivdi3 +__udivdi3: + mv a2, a1 + mv a1, a0 + li a0, -1 + beqz a2, .L5 + li a3, 1 + bgeu a2, a1, .L2 +.L1: + blez a2, .L2 + slli a2, a2, 1 + slli a3, a3, 1 + bgtu a1, a2, .L1 +.L2: + li a0, 0 +.L3: + bltu a1, a2, .L4 + sub a1, a1, a2 + or a0, a0, a3 +.L4: + srli a3, a3, 1 + srli a2, a2, 1 + bnez a3, .L3 +.L5: + ret + + .globl __umoddi3 +__umoddi3: + /* Call __udivdi3(a0, a1), then return the remainder, which is in a1. */ + move t0, ra + jal __udivdi3 + move a0, a1 + jr t0 + + /* Handle negative arguments to __divdi3. */ +.L10: + neg a0, a0 + bgez a1, .L12 /* Compute __udivdi3(-a0, a1), then negate the result. */ + neg a1, a1 + j __divdi3 /* Compute __udivdi3(-a0, -a1). */ +.L11: /* Compute __udivdi3(a0, -a1), then negate the result. */ + neg a1, a1 +.L12: + move t0, ra + jal __divdi3 + neg a0, a0 + jr t0 + + .globl __moddi3 +__moddi3: + move t0, ra + bltz a1, .L31 + bltz a0, .L32 +.L30: + jal __udivdi3 /* The dividend is not negative. */ + move a0, a1 + jr t0 +.L31: + neg a1, a1 + bgez a0, .L30 +.L32: + neg a0, a0 + jal __udivdi3 /* The dividend is hella negative. */ + neg a0, a1 + jr t0 + +#ifdef __riscv64 + /* continuation of __divsi3 */ +.L20: + sll t0, t0, 31 + bne a0, t0, __divdi3 + ret +#endif \ No newline at end of file diff --git a/fobooster/src/libc.c b/fobooster/src/libc.c new file mode 100644 index 0000000..a2a427b --- /dev/null +++ b/fobooster/src/libc.c @@ -0,0 +1,117 @@ +/* $OpenBSD: strlen.c,v 1.8 2014/06/10 04:17:37 deraadt Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +size_t +strlen(const char *str) +{ + const char *s; + + for (s = str; *s; ++s) + ; + return (s - str); +} + +/** + * memcpy - Copies one area of memory to another + * @dest: Destination + * @src: Source + * @n: The size to copy. + */ +void *memcpy(void *to, const void *from, size_t n) +{ + void *xto = to; + size_t temp; + + if(!n) + return xto; + if((long)to & 1) { + char *cto = to; + const char *cfrom = from; + *cto++ = *cfrom++; + to = cto; + from = cfrom; + n--; + } + if((long)from & 1) { + char *cto = to; + const char *cfrom = from; + for (; n; n--) + *cto++ = *cfrom++; + return xto; + } + if(n > 2 && (long)to & 2) { + short *sto = to; + const short *sfrom = from; + *sto++ = *sfrom++; + to = sto; + from = sfrom; + n -= 2; + } + if((long)from & 2) { + short *sto = to; + const short *sfrom = from; + temp = n >> 1; + for (; temp; temp--) + *sto++ = *sfrom++; + to = sto; + from = sfrom; + if(n & 1) { + char *cto = to; + const char *cfrom = from; + *cto = *cfrom; + } + return xto; + } + temp = n >> 2; + if(temp) { + long *lto = to; + const long *lfrom = from; + for(; temp; temp--) + *lto++ = *lfrom++; + to = lto; + from = lfrom; + } + if(n & 2) { + short *sto = to; + const short *sfrom = from; + *sto++ = *sfrom++; + to = sto; + from = sfrom; + } + if(n & 1) { + char *cto = to; + const char *cfrom = from; + *cto = *cfrom; + } + return xto; +} \ No newline at end of file diff --git a/fobooster/src/main.c b/fobooster/src/main.c new file mode 100644 index 0000000..9efc588 --- /dev/null +++ b/fobooster/src/main.c @@ -0,0 +1,118 @@ +#include +#include +#include + +// Defined in the linker +extern struct fobooster_data fobooster_data; +extern const struct fobooster_data fobooster_src; + +__attribute__((noreturn)) void reboot(void) +{ + uint8_t image_index = 0; + reboot_ctrl_write(0xac | (image_index & 3) << 0); + __builtin_unreachable(); +} + +// Returns whether the flash controller is busy +static bool ftfl_busy() +{ + return spiIsBusy(); +} + +// Wait for the flash memory controller to finish any pending operation. +static void ftfl_busy_wait() +{ + while (ftfl_busy()) + ; +} + +// Erase the sector that contains the specified address. +static void ftfl_begin_erase_sector(uint32_t address) +{ + ftfl_busy_wait(); + spiBeginErase4(address); +} + +uint32_t bytes_left; +uint32_t target_addr; +uint32_t *current_ptr; +uint32_t page_offset; + +// Erase the Toboot configuration, which will prevent us from +// overwriting Toboot again. +// Run this from RAM to ensure we don't hardfault. +// Reboot after we're done. +__attribute__((noreturn)) static void finish_flashing(void) +{ +#if 0 + uint32_t vector_addr = (uint32_t)&toboot_configuration; + + // Jump back into the bootloader when we reboot + toboot_runtime.magic = TOBOOT_FORCE_ENTRY_MAGIC; + + while (MSC->STATUS & MSC_STATUS_BUSY) + ; + + MSC->WRITECTRL |= MSC_WRITECTRL_WREN; + MSC->ADDRB = vector_addr; + MSC->WRITECMD = MSC_WRITECMD_LADDRIM; + MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE; + + while (MSC->STATUS & MSC_STATUS_BUSY) + ; +#endif + reboot(); +} + +void isr(void) +{ + /* unused */ +} + +__attribute__((noreturn)) void fobooster_main(void) +{ + // If the booster data is too big, just let the watchdog timer reboot us, + // since the program is invalid. + if (fobooster_src.payload_size != 104090) + { + reboot(); + } + + // We want to run entirely from RAM, so copy the booster payload to RAM too. + memcpy((void *restrict) & fobooster_src, &fobooster_data, fobooster_src.payload_size); + + // Ensure the hash matches what's expected. + if (XXH32(fobooster_data.payload, fobooster_data.payload_size, FOBOOSTER_SEED) != fobooster_data.xxhash) + { + reboot(); + } + + // Now that everything is copied to RAM, disable memory-mapped SPI mode. + // This puts the SPI into bit-banged mode, which allows us to write to it. + picorvspi_cfg4_write(0); + + bytes_left = fobooster_data.payload_size; + target_addr = 0; + current_ptr = &fobooster_data.payload[0]; + + while (bytes_left && (target_addr < 131072)) + { + ftfl_begin_erase_sector(target_addr); + + for (page_offset = 0; + bytes_left && page_offset < SPI_ERASE_SECTOR_SIZE; + page_offset += SPI_PROGRAM_PAGE_SIZE) + { + uint32_t bytes_to_write = bytes_left; + if (bytes_to_write > SPI_PROGRAM_PAGE_SIZE) + bytes_to_write = SPI_PROGRAM_PAGE_SIZE; + spiBeginWrite(target_addr, current_ptr, bytes_to_write); + ftfl_busy_wait(); + current_ptr += SPI_PROGRAM_PAGE_SIZE; + target_addr += SPI_PROGRAM_PAGE_SIZE; + bytes_left -= bytes_to_write; + } + } + + finish_flashing(); +} \ No newline at end of file diff --git a/fobooster/src/mul.S b/fobooster/src/mul.S new file mode 100644 index 0000000..2499abf --- /dev/null +++ b/fobooster/src/mul.S @@ -0,0 +1,26 @@ + .text + .align 2 + +#ifdef __riscv64 +#define _RISCV_SZPTR 64 +#define _RISCV_SZINT 64 +#else +/* Our RV64 64-bit routine is equivalent to our RV32 32-bit routine. */ +# define __muldi3 __mulsi3 +#define _RISCV_SZPTR 32 +#define _RISCV_SZINT 32 +#endif + + .globl __muldi3 +__muldi3: + mv a2, a0 + li a0, 0 +.L1: + slli a3, a1, _RISCV_SZPTR-1 + bgez a3, .L2 + add a0, a0, a2 +.L2: + srli a1, a1, 1 + slli a2, a2, 1 + bnez a1, .L1 + ret diff --git a/fobooster/src/spi.c b/fobooster/src/spi.c new file mode 100644 index 0000000..13b21ec --- /dev/null +++ b/fobooster/src/spi.c @@ -0,0 +1,156 @@ +#include +#include + +#define SP_MOSI_PIN 0 +#define SP_MISO_PIN 1 +#define SP_WP_PIN 2 +#define SP_HOLD_PIN 3 +#define SP_CLK_PIN 4 +#define SP_CS_PIN 5 +#define SP_D0_PIN 0 +#define SP_D1_PIN 1 +#define SP_D2_PIN 2 +#define SP_D3_PIN 3 + +#define PI_OUTPUT 1 +#define PI_INPUT 0 + +// static void gpioSetMode(int pin, int mode) { +// static uint8_t oe_mirror; +// if (mode) +// oe_mirror |= 1 << pin; +// else +// oe_mirror &= ~(1 << pin); +// picorvspi_cfg2_write(oe_mirror); +// } + +static void gpioWrite(int pin, int val) { + static uint8_t do_mirror; + if (val) + do_mirror |= 1 << pin; + else + do_mirror &= ~(1 << pin); + picorvspi_cfg1_write(do_mirror); +} + +static int gpioRead(int pin) { + return !!(picorvspi_stat1_read() & (1 << pin)); +} + +void spiBegin(void) { + gpioWrite(SP_WP_PIN, 1); + gpioWrite(SP_HOLD_PIN, 1); + gpioWrite(SP_CS_PIN, 0); +} + +void spiEnd(void) { + gpioWrite(SP_CS_PIN, 1); +} + +void spiPause(void) { + return; +} + +static uint8_t spiXfer(uint8_t out) { + int bit; + uint8_t in = 0; + + for (bit = 7; bit >= 0; bit--) { + if (out & (1 << bit)) { + gpioWrite(SP_MOSI_PIN, 1); + } + else { + gpioWrite(SP_MOSI_PIN, 0); + } + gpioWrite(SP_CLK_PIN, 1); + spiPause(); + in |= ((!!gpioRead(SP_MISO_PIN)) << bit); + gpioWrite(SP_CLK_PIN, 0); + spiPause(); + } + + return in; +} + +void spiCommand(uint8_t cmd) { + spiXfer(cmd); +} + +uint8_t spiCommandRx(void) { + return spiXfer(0xff); +} + +uint8_t spiReadStatus(void) { + uint8_t val = 0xff; + spiBegin(); + spiCommand(0x05); + val = spiCommandRx(); + spiEnd(); + return val; +} + +void spiBeginErase4(uint32_t erase_addr) { + // Enable Write-Enable Latch (WEL) + spiBegin(); + spiCommand(0x06); + spiEnd(); + + spiBegin(); + spiCommand(0x20); + spiCommand(erase_addr >> 16); + spiCommand(erase_addr >> 8); + spiCommand(erase_addr >> 0); + spiEnd(); +} + +void spiBeginErase32(uint32_t erase_addr) { + // Enable Write-Enable Latch (WEL) + spiBegin(); + spiCommand(0x06); + spiEnd(); + + spiBegin(); + spiCommand(0x52); + spiCommand(erase_addr >> 16); + spiCommand(erase_addr >> 8); + spiCommand(erase_addr >> 0); + spiEnd(); +} + +void spiBeginErase64(uint32_t erase_addr) { + // Enable Write-Enable Latch (WEL) + spiBegin(); + spiCommand(0x06); + spiEnd(); + + spiBegin(); + spiCommand(0xD8); + spiCommand(erase_addr >> 16); + spiCommand(erase_addr >> 8); + spiCommand(erase_addr >> 0); + spiEnd(); +} + +int spiIsBusy(void) { + return spiReadStatus() & (1 << 0); +} + +void spiBeginWrite(uint32_t addr, const void *v_data, unsigned int count) { + const uint8_t write_cmd = 0x02; + const uint8_t *data = v_data; + unsigned int i; + + // Enable Write-Enable Latch (WEL) + spiBegin(); + spiCommand(0x06); + spiEnd(); + + spiBegin(); + spiCommand(write_cmd); + spiCommand(addr >> 16); + spiCommand(addr >> 8); + spiCommand(addr >> 0); + for (i = 0; (i < count) && (i < 256); i++) + spiCommand(*data++); + spiEnd(); +} \ No newline at end of file