robertlipe / riscv7 Goto Github PK
View Code? Open in Web Editor NEWUNIXv7 ported to RISC-V, specifically the Longnan Nano SBC
License: Other
UNIXv7 ported to RISC-V, specifically the Longnan Nano SBC
License: Other
This is the (not-yet working) port of UNIXv7 system to RISC-V. It's currently targeting the GD32V as found on the Longnan Nano, a super-inexpensive ($5-ish) SBC that includes a full USB-C controller, including OTG. This makes it possible to appear as a mass storage device, a serial port, or other USB peripherals. This is more powerful than devices like the K210 or BL602 which require an external USB->Serial adapter that can support none of these. RISCv7 does not (yet) use any of this awesomeness. There are some remnanats in the code of me starting with the K210. That is a 64-bit part that has 8MB of RAM and 2 cores. While I may go back to that part some day (more RAM and protected mode would be nice) I learned early on that there were a lot of structures with implicit sizes that burst into flames with int > 32 bits. In the name of focus, I went down to a single part. An early design choice was to NOT design my own ABI and calling convention. Nor did I choose to torture myself with ancient Make. Relying on a sane GNU make on the host is fair game. It's easy to find riscv64-gcc in prepackaged binaries (Mac: Homebrew. Debian has a package) where 32-bit tools are harder to find. The tools support 32-bit modes so the convenience of calling riscv32-gcc over riscv64-gcc with -march=rv32imac -mabi=ilp32 to get 32-bit code isn't worth it[1]. The ABI is "whatever these tools do". Like the early UNIXes, binary compatibilty isn't a concern; I may change system call numbers or visible symbols pretty much at whim. Similarly, I've not fought a fierce battle to keep the code K&R. As this code is meant to be educational (it certainly has been for this author) more than historical purity, familiarity with ANSI function declarations, judicious use of declarations, va_arg, not relying on implicit int, the presence of 'void', the absence of 'register' and so on are all considered good taste. Indeed, especially with 64-bit being in sight, implicit int and such can lead to really screwy stack frames so we're just trying to head that off. I started development thinking I would write ALL the code myself and not rely on any namby-pamby vendor code. That quickly got old. In sys/n200 (another unfortunate naming choice) is code for the Nucleisys 200 core as used in the GD32V, the Gigadevice libraries for interfacing with SPI, DMA, and more, as well as some other third party libraries. There is evidence in this directory of several failed experiments with different abstractions. I've been happily using the SEGGER Mini EDU JTAG device. Using something like Openocd with an FTDI board is probably possible, but that's what I started with. The best way to build the kernel is to be in usr/sys/conf and run make. There is a convenience script named 'upload' which does the obvious thing, calling Jlink with the appropriate fiddly parameters. 'run_nano' does a build, uploads the kernel to the board, and starts a GDB server. For debugging, there are x/ and z/ subdirectories (bad, I know...) that have .gdbinits that offer convenience macros to reboot the kernel, reload it (via GDB), start the remote debugging, and automatically have breakpoints on panic and other places you hope to not be. These will probably need customized to a developer's system and style. Little attention has been given to user-space so far. I expect most of it will fall into place. crt0.S for these parts is straight-forward. I just need to work through things like the global for errno and the assembly code that sets it, the header tension between usr/include and usr/sys/h, and signal handlers that need to decode interrupted instructions so that the $PC can be set right. There is also the need for standardized kernel exception frames. Status & Known problems: Boot is working. Timer interrupts are working. The display is mostly working. It's too small to be really useful, but displaying interrupt ticks or enter/exit style prints can still be handy. The display is NOT hooked as a console device with ANSI escape sequences nor is it even hooked as a serial console. It's shoved into putchar() inside sc.c,. Memory management is a mess. The code I started from, Robert Nordier's 386 port of UNIXv7 has some hard coded page numbers in memory layout in the kernel. This has proven hard to undo. The code currently gets to the point where it trampolines the assembler code to start an exec() to load and run. Sometime the LCD doesn't wake properly. I think there's something missing in the reset sequence. As embarrassing/ridiculous as this sounds, building and running gd32vf103inator/examples/LonganNano will "fix" the display. Once it's run and whacked the LCD (DMA? SPI?) once, the LCD will literally run for days and across hundreds of load/run cycles. The chip has a hardware scroll, but I cannot make it scroll in the axis it needs to scroll. Horizontal scroll is not very useful in a UNIX console. One possible way to implement scrolling is to define a text frame buffer (maybe with 8-bit color like CGA or VGA) and fully redraw the screen on a scroll. I'm not sure if that's a good use of our memory budget. SD card support is working at a proof-of-concept level. I can open a DOS-formattted memory card and read files and directories from it. I've not implemented partition table support. With SD cards being so cheap, I'm not sure it makes sense. More importantly, I've not implemented any "real" UNIX-level disk support. Even being a serial device attached to SPI, SD is so crazy fast relative to the VAX disk that I'm not sure that just spin-waiting a DMA-driven interface isn't the way to go. Shimming the code in tf_card.c for disk_read() and disk_write() for synchronous reads and writes may be OK. There is no root filesystem created by the build process. The kernel is just injected into flash memory and executed. It works at a reasonable speed because flash is copied to SRAM on boot. SRAM is apparently cheaper than caches at this size. (!!) Segger's equivalent of ARM's Semihosting would probably be good to implement for debugging. [1] For more info on RISC-V embedded chains, see https://riscv.org/wp-content/uploads/2015/02/riscv-software-stack-tutorial-hpca2015.pdf https://github.com/riscv/riscv-gnu-toolchain
The GNU tools all fluently read and write ELF, so using that instead of a.out is a no-brainer for us.
I think most of our damage will be in sys1.c:getxfile(). ELF is well documented. This seems to be the code responsible for fondling the executable header, creating a mapping (or filling a buffer) to hold the executable, and keeping some sense of security around not executing garbage, not leaving partially filled pages in the programs address space, etc.
This could possibly developed before the filesystem code is in place by carefully creating an ELF executable in kernel address space, dummying up an inode, and calling it directly. Probably create a tiny "hello world" executable in user space, then use an #includebin to slurp it into kernel space during the build. Throw it in another section if you don't like it in .data, but be sure the linker script doesn't toss the section.
The predecessor code wanted a populated stack frame passed to clock. I dummied it up making a static frame with all members null since I didn't have user/system mode or other nuances bugging me. That has to go.
Current workarounds include the tiny display on longan nano and using gdb to p msgbuf on a crash or interrupt, but at time point we'll need to type things at the system and that needs input. Pick one or more options from:
SEGGER's version of SemiHosting
Use the real serial pins. (Abstract it to K210 and GD32V) Writing more tty drivers. Ugh.
For GD32V, I think we could treat it like a CDC(ACM) device so it would show up as a USB serial port. The libraries from GD are quite good, but that's a terrifying quantity of code to be spinning through that could be called pre-boot or during a panic handler,
The VAX IPL levels map to x86 interrupts somewhat cleanly, but not so well to RISC-V. Once we really reach our stride of having disk, display, terminals, and such going, we need to preserve the spl(x) and split() functionality in a way that approximately maps to RISC-V interrupt controllers. (Bonus points for working on both GD32V and K210, even if only one core awake.)
The terms are described pretty well at http://osr600doc.sco.com/en/man/html.D3/spl.D3.html I think disk, tty, base, timeout, split are probably the highpoint.
Using the code from GD that handled all the SPI dma/interrupt stuff (tf_card.c) and a third-party library for the filesystem (ff.c) I've demonstrated reading the directory of a DOS-formatted SD card and displaying the contents. The filesystem code Is used by many Arduino class projects and the trans flash code provides a very reasonable looking disk_read() and disk_write services that shim the SCSI (sigh) that's used In these memory cards.
If we want to share an SD card with a DOS filesystem, we'd need a small partition driver that read the MBR, walked the partitions, identified a UNIX (which?) partition ID and a DOS one and calculated the saved cylinder edges of those partitions. (Half the system will want to deal in CHS and half will want to deal in SCSI command blocks and half will just make no sense when pairing VAX-era code to a "disk" that would fit in your eye.) The OS reads and writes just get offset by the partition boundaries.
I have no idea how we populate the memfs that's present when we boot or how we fill the memory card with the binaries we build. We probably need a "vaxcp" or "vaxformat" (surely these exists) that can just treat a raw bag of bytes and put a V7 filesystem onto them so we can write /etc/init and /bin/sh and /usr/games/fortune and such. Needs research.
Since even a slow SPIO transfer is surely faster than mechanical heads seeking on a disk pack, it might not be crazy to just spin-wait the sector reads and writes at least on a per-sector basis and letting that be preempt able. There's code for tfcard_timer_irq() but it seems to have no callers.
References: https://minnie.tuhs.org/PUPS/Setup/v7_setup.html (thumbs-up for "Recompile kernel and all apps when changing TZ...) The browsable source archives of historic distributions are a treasure trove.
I think I have newproc and sleep/wakeup working. I could be wrong.
sys/machdep.c has the starts of a promising technique of writing .S code naturally inlined to C.
main.c seems to be calling newproc successfully,. The copyout() that puts the init skeleton into user space is being called, but probably not with a real, suitable executable.
I've put in a moderate amount of scaffolding to prop up user space, but we still need to connect the ECALL handler to something that connects to sysent[] from the generated trap.
This is kind of the intersection of several sketched in components that aren't really tested.
Since we don't really have VM (unless we raise the hardware requirement to a device that support RV39) we'll have to think through what we want our memory map to look like and how to share kernel and user space (probably meaningless terms w/o MMU) to at least minimize the risk of them trampling each other.
We also have the landmine that the x86 port, whence this code came, hardcoded page numbers in several places that I've removed. Some surely remain. "We don't know what we don't know."
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.