lcpu: officially support legacy 32bit branch

32-bit builds using the development headers work on the legacy 32-bit branch so I don't really see a reason to not support it..

sure, it sucks that I now won't be able to get rid of a pretty gnarly codepath, but /shrug
This commit is contained in:
Lily Tsuru 2023-07-29 16:59:51 -04:00
parent 6eada83eeb
commit 7ae6acfc3b
14 changed files with 66 additions and 324 deletions

View File

@ -9,7 +9,7 @@ It provides:
(that and the more reasons to not use Lua for a part of this codebase, the better...) (that and the more reasons to not use Lua for a part of this codebase, the better...)
- Interoperation with the Wiremod addon (and addons which implement Wiremod integration) - Interoperation with the Wiremod addon (and addons which implement Wiremod integration)
This addon (for now) works with both the non-beta branch and the x86-64 beta branches of GMod. This addon works with both the non-beta branch and the x86-64 beta branches of GMod, however rigorous testing is usually only done with the x86-64 beta branch.
Note that the GitHub mirror is provided only for convinence, and is not used for active development. Note that the GitHub mirror is provided only for convinence, and is not used for active development.
See [this link](https://git.crustywindo.ws/modeco80/gmod-lcpu) for the actual development repository. See [this link](https://git.crustywindo.ws/modeco80/gmod-lcpu) for the actual development repository.
@ -20,28 +20,15 @@ You will need Wiremod installed, either from the Workshop or cloned as a filesys
This repository is set up to be a Filesystem Addon; therefore, workflows which clone repositories from Git and put them in addons/ should be able to work with the LCPU addon just fine. This repository is set up to be a Filesystem Addon; therefore, workflows which clone repositories from Git and put them in addons/ should be able to work with the LCPU addon just fine.
Preliminary installation steps (by hand): Preliminary installation steps (for Linux):
``` ```
garrysmod/addons$ git clone --recursive https://git.crustywindo.ws/modeco80/gmod-lcpu.git lcpu garrysmod/addons$ git clone --recursive https://git.crustywindo.ws/modeco80/gmod-lcpu.git lcpu
garrysmod/addons$ cd lcpu garrysmod/addons$ cd lcpu
garrysmod/addons/lcpu$ ./build_module.sh
# Build the LCPU native module. These steps build the linux64 version of the module
# on linux; you'll need to alter it if you want to build for 32-bit.
garrysmod/addons/lcpu$ cmake -Wno-dev -GNinja -S native -B build \
-DCMAKE_BUILD_TYPE=Release
garrysmod/addons/lcpu$ ninja -C build
# Install the native module (Linux)
# For Windows you can do the same thing, just replace this
# with how you'd do it in batch (or use Explorer, I'm not your dad)
garrysmod/addons/lcpu$ [[ ! -d '../../lua/bin']] && mkdir -p ../../lua/bin && cp build/projects/lcpu/*.dll ../../lua/bin
# Tada! # Tada!
``` ```
On Linux you can alternatively use the `./build_module.sh` script that will do all the build and installation steps automatically, once cloning the repository in the garrysmod/addons folder.
Windows building is currently untested; I see no reason why it wouldn't work. Windows building is currently untested; I see no reason why it wouldn't work.

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# Build the LCPU addon for the reccomended environment # Build the LCPU native module for both linux32 and linux64
# and install it into the proper directory gmod wants native modules to be. # and install it into the proper directory gmod wants native modules to be.
set -x set -x
@ -8,11 +8,38 @@ set -x
# where your game server is # where your game server is
GS_PATH="/home/lily/gs/gmod" GS_PATH="/home/lily/gs/gmod"
cmake -Wno-dev -GNinja -S native -B module_build -DCMAKE_BUILD_TYPE=Release # make the module build directory
ninja -C module_build [[ ! -d "module_build" ]] && {
mkdir module_build
}
# $1 cmake build dir
# $@ any other args to cmake
cmake_gen() {
local BD=$1
shift
cmake -Wno-dev -GNinja -S native -B module_build/$BD $@ -DCMAKE_BUILD_TYPE=Release
}
# $1 cmake build dir
cmake_build() {
ninja -C module_build/$1
}
build_and_place() {
cmake_gen linux32 --toolchain $PWD/native/cmake/linux32-toolchain.cmake
cmake_gen linux64
cmake_build linux32
cmake_build linux64
# GMod doesn't actually make the lua/bin directory on its own
# so we have to check if it exists first and make if it doesn't
[[ ! -d "$GS_PATH/garrysmod/lua/bin" ]] && { [[ ! -d "$GS_PATH/garrysmod/lua/bin" ]] && {
mkdir -p $GS_PATH/garrysmod/lua/bin mkdir -p $GS_PATH/garrysmod/lua/bin
} }
cp -v module_build/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin cp -v module_build/linux32/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin
cp -v module_build/linux64/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin
}
build_and_place

View File

@ -9,8 +9,6 @@ add_subdirectory(projects/lucore)
# RISC-V emulator library # RISC-V emulator library
add_subdirectory(projects/riscv) add_subdirectory(projects/riscv)
add_subdirectory(projects/riscv_test_harness)
# Garry's Mod native bindings to RISC-V emulator # Garry's Mod native bindings to RISC-V emulator
# Also lua device stuff # Also lua device stuff
add_subdirectory(projects/lcpu) add_subdirectory(projects/lcpu)

View File

@ -0,0 +1,23 @@
# toolchain file for linux multilib building
set(CMAKE_C_FLAGS "-m32" CACHE STRING "C compiler flags" FORCE)
set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags" FORCE)
if(EXISTS /usr/lib32)
set(LIB32 /usr/lib32)
else()
set(LIB32 /usr/lib)
endif()
set(CMAKE_SYSTEM_LIBRARY_PATH ${LIB32} CACHE STRING "system library search path" FORCE)
set(CMAKE_LIBRARY_PATH ${LIB32} CACHE STRING "library search path" FORCE)
# this is probably unlikely to be needed, but just in case
set(CMAKE_EXE_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "executable linker flags" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "shared library linker flags" FORCE)
set(CMAKE_MODULE_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "module linker flags" FORCE)
# point pkg-config to lib32
if(EXISTS ${LIB32}/pkgconfig)
set(ENV{PKG_CONFIG_LIBDIR} ${LIB32}/pkgconfig:/usr/share/pkgconfig:/usr/lib/pkgconfig:/usr/lib64/pkgconfig)
endif()

View File

@ -1,15 +1,9 @@
#include "SourceSink.hpp" #include "SourceSink.hpp"
#include <cstdlib>
#include <lucore/Assert.hpp> #include <lucore/Assert.hpp>
#include <lucore/Library.hpp> #include <lucore/Library.hpp>
#include <lucore/Types.hpp> #include <lucore/Types.hpp>
// The old non-beta branch of GMod on Linux has multiple tier0 libraries for client and server.
// This compatibility define allows to support that case (for now). Once this define is removed,
// the old codepath can be totally removed.
#define LCPU_SUPPORT_OLD_GMOD
namespace tier0 { namespace tier0 {
lucore::Library* library = nullptr; lucore::Library* library = nullptr;
@ -17,7 +11,6 @@ namespace tier0 {
Msg_t Msg {}; Msg_t Msg {};
bool OpenLibrary() { bool OpenLibrary() {
#ifdef LCPU_SUPPORT_OLD_GMOD
constexpr static std::string_view tier0_libraries[] { constexpr static std::string_view tier0_libraries[] {
#ifdef __linux__ #ifdef __linux__
"tier0_srv", "tier0_srv",
@ -32,12 +25,6 @@ namespace tier0 {
break; break;
} }
} }
#else
// The x86-64 branch of GMod, including the 32-bit binaries in the branch,
// have a single tier0 library, which makes the codepath much simpler.
// Hopefully I can switch to this path at some point.
tier0::library = lucore::Library::OpenExisting("tier0");
#endif
if(tier0::library == nullptr) if(tier0::library == nullptr)
return false; return false;
@ -65,14 +52,14 @@ namespace lcpu {
SourceSink::SourceSink() { SourceSink::SourceSink() {
if(!tier0::OpenLibrary()) { if(!tier0::OpenLibrary()) {
std::printf("Tier0 could not be opened\n"); std::printf("Tier0 could not be opened - aborting\n");
std::quick_exit(10); std::quick_exit(10);
} }
// TODO: A bit nicer of an error message? // TODO: A bit nicer of an error message?
// Explain *what* to do if you see this message. // Explain *what* to do if you see this message.
if(!tier0::GrabSymbols()) { if(!tier0::GrabSymbols()) {
std::printf("Tier0 symbols could not be grabbed\n"); std::printf("Tier0 symbols could not be grabbed - aborting\n");
std::quick_exit(10); std::quick_exit(10);
} }
} }

View File

@ -14,7 +14,7 @@ namespace lucore::detail {
} }
void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) { void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) {
return GetProcAddressA(reinterpret_cast<HMODULE*>(dll), symbolName); return GetProcAddressA(reinterpret_cast<HMODULE>(dll), symbolName);
} }
void OsFreeLibrary(OsLibraryHandle handle) { void OsFreeLibrary(OsLibraryHandle handle) {

View File

@ -12,4 +12,4 @@ Depends on lucore.
# Usage # Usage
TBD (if this is moved to another repo). See the riscv_test_harness project. TBD (if this is moved to another repo). See the lcpu project

View File

@ -1,7 +0,0 @@
add_executable(rvtest
main.cpp
)
target_link_libraries(rvtest PUBLIC
riscv::riscv
)

View File

@ -1,69 +0,0 @@
//! A test harness for testing if the riscv library actually works.
#include <cstdio> // I know, I know, but this is a test program. Yell later :)
#include <lucore/Assert.hpp>
#include <lucore/Logger.hpp>
#include <lucore/StdoutSink.hpp>
#include <riscv/System.hpp>
#include <thread>
/// simple 16550 UART implementation
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
constexpr static riscv::Address BASE_ADDRESS = 0x10000000;
riscv::Address Base() const override { return BASE_ADDRESS; }
riscv::Address Size() const override { return 12; } // for now
u32 Peek(riscv::Address address) override {
switch(address) {
case BASE_ADDRESS: return '\0'; // just return 0 for the input register
case BASE_ADDRESS + 5: return 0x60; // active, but no keyboard input
}
return 0;
}
void Poke(riscv::Address address, u32 value) override {
if(address == BASE_ADDRESS) {
char c = value & 0x000000ff;
fputc(c, stderr);
}
}
};
int main(int argc, char** argv) {
lucore::LoggerAttachStdout();
LUCORE_CHECK(argc == 2, "this test harness expects one argument (the file to load into riscv memory and execute). got {} arguments", argc);
// 128 KB of ram. Won't be enough to boot linux but should be good enough to test most baremetal apps
auto system = riscv::System::Create(128 * 1024);
LUCORE_CHECK(system, "could not create system for some reason.");
// Attach our UART device
system->bus->AttachDevice(new SimpleUartDevice);
auto fp = std::fopen(argv[1], "rb");
LUCORE_CHECK(fp, "could not open file \"{}\"", argv[1]);
std::fseek(fp, 0, SEEK_END);
auto len = std::ftell(fp);
std::fseek(fp, 0, SEEK_SET);
std::fread(system->ram->Raw(), 1, len, fp);
std::fclose(fp);
// This allows the host program running under the test
// harness to tell us to shut down.
bool shouldExit = false;
system->OnPowerOff = [&shouldExit]() { shouldExit = true; };
while(!shouldExit) {
system->Step();
//std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
delete system;
return 0;
}

View File

@ -1,53 +0,0 @@
PROJECT = test
# where your rv32 toolchain is
TCPATH = /home/lily/bin/riscv/bin
PREFIX = $(TCPATH)/riscv32-unknown-elf
CC = $(PREFIX)-gcc
CXX = $(PREFIX)-g++
ARCHFLAGS = -ffreestanding -fno-stack-protector -fdata-sections -ffunction-sections -march=rv32ima -mabi=ilp32
CCFLAGS = -g -Os $(ARCHFLAGS) -std=c18
CXXFLAGS = $(ARCHFLAGS) -g -Os -std=c++20 -fno-exceptions -fno-rtti
LDFLAGS = -T binary.ld -nostdlib -Wl,--gc-sections
OBJS = start.o \
main.o
.PHONY: all test clean
all: $(PROJECT).bin $(PROJECT).debug.txt
# this assumes the lcpu project build dir you're using is
# [lcpu repo root]/build
test: $(PROJECT).bin $(PROJECT).debug.txt
../../../../build/projects/riscv_test_harness/rvtest $<
clean:
rm $(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt $(OBJS)
# Link rules
$(PROJECT).elf: $(OBJS)
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(OBJS)
$(PROJECT).bin : $(PROJECT).elf
$(PREFIX)-objcopy $^ -O binary $@
$(PROJECT).debug.txt : $(PROJECT).elf
$(PREFIX)-objdump -t $^ > $@
$(PREFIX)-objdump -S $^ >> $@
# Compile rules
%.o: %.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
%.o: %.c
$(CC) -c $(CCFLAGS) $< -o $@
%.o: %.S
$(CC) -x assembler-with-cpp -march=rv32ima -mabi=ilp32 -c $< -o $@

View File

@ -1,5 +0,0 @@
# what
This is a simple bare-metal riscv program that is able to run on the test harness that does some testing.
It is barebones, I know.

View File

@ -1,73 +0,0 @@
__heap_size = 0x1000;
__stack_size = 0x1000;
ENTRY(_start)
SECTIONS
{
. = 0x80000000;
.text : ALIGN(16) {
__TEXT_BEGIN__ = .;
*(.initial_jump)
*(.entry.text)
*(.init.literal)
*(.init)
*(.text)
*(.literal .text .literal.* .text.* .stub)
*(.out_jump.literal.*)
*(.out_jump.*)
__TEXT_END__ = .;
}
/* If we're on a newer compiler */
/DISCARD/ :
{
*(.interp)
*(.dynsym)
*(.dynstr)
*(.header)
} : phdr
.data : ALIGN(16) {
__DATA_BEGIN__ = .;
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
*(.rodata1)
*(.dynsbss)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.gnu.linkonce.sb2.*)
*(.sbss)
*(.sbss.*)
*(.sbss2)
*(.sbss2.*)
*(.dynbss)
*(.data)
*(.data.*)
*(.got)
*(.got.*)
__DATA_END__ = .;
}
.bss : ALIGN( 16 ) {
__BSS_BEGIN__ = .;
*(.bss) /* Tricky: BSS needs to be allocated but not sent. GCC Will not populate these for calculating data size */
*(.bss.*)
__BSS_END__ = .;
}
.heap : ALIGN( 16 ) {
_sheap = .;
. = . + __heap_size;
_eheap = .;
}
.stack : ALIGN( 16 ) {
_estack = .;
. = . + __stack_size;
_sstack = .;
}
}

View File

@ -1,60 +0,0 @@
// a simple test program
#include <stdint.h>
uint32_t strlen(const char* str) {
if(!str)
return 0;
const char* c = str;
while(*c++)
;
return c - str;
}
#define SYSCON *(volatile uint32_t*)0x11100000
#define UART_BASE 0x10000000
#define UART_DATA *(volatile uint32_t*)UART_BASE
#define UART_STATUS UART_DATA
void putc(char c) {
UART_DATA = (uint32_t)c;
}
__attribute__((noinline)) void puts(const char* str) {
const uint32_t length = strlen(str);
for(uint32_t i = 0; i < length; ++i)
putc(str[i]);
}
static uint32_t value = 0;
static uint16_t shortvalue = 0;
static uint8_t bytevalue = 0;
#define COUNTER_TEST(var, max) \
for(int i = 0; i < max; ++i) { \
puts(#var " is (before modification): "); \
putc("0123456789"[var]); \
putc('\n'); \
\
var = i; \
puts(#var " is (after modification): "); \
putc("0123456789"[var]); \
putc('\n'); \
}
void main() {
puts("hello world I guess\n");
#if 1
COUNTER_TEST(value, 9);
COUNTER_TEST(shortvalue, 9);
COUNTER_TEST(bytevalue, 9);
#endif
// Shut down the test harness once we're done testing.
puts("Tests done, shutting down test harness...\n");
//SYSCON = 0x5555;
// loop forever
for(;;);
}

View File

@ -1,13 +0,0 @@
# Simple bare-metal RISCV startup code.
.section .initial_jump
.global _start
.extern main
.align 4
_start:
la sp, _sstack # set up C stack
addi sp,sp,-16 # ...
sw ra,12(sp) # ...
jal ra, main # jump to C code!