Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: all: add bare metal ARM support #37503

Closed
abarisani opened this issue Feb 27, 2020 · 81 comments
Closed

proposal: all: add bare metal ARM support #37503

abarisani opened this issue Feb 27, 2020 · 81 comments

Comments

@abarisani
Copy link

Hello,

I am unsure if to go directly with a CL (as this has been discussed already on golang-dev) or if to go through this issue first, apologies if I should have done otherwise.

On this golang-dev thread I described the new GOOS=tamago created to support bare metal ARMv7 architectures using the original Go compiler.

The use case for such support is to run native Go applications on Single Board Computers without and underlying Operating System. Currently we are testing this on our own USB armory but we are working on Raspberry PI Zero support as well.

We have working examples of SSH and HTTPS servers running on Ethernet over USB with all USB drivers and stack implemented in pure Go. We are also working on a pure Go OpenPGP card leveraging on this.

We think there is great value in having this kind of support in the Go language as it considerably improves the security and ease of development on embedded systems and allows to purge C, with ease, on otherwise complex firmware efforts.

Our work is split across the following 3 repositories:

For upstream consideration only the first repository matters, we intentionally kept the compiler changes separate from SoC specific drivers and ensure that Go applications can run mostly unencumbered and with a single import required for the board package.

You can find the following resources to understand more about our effort:

Our compiler modification approach tries to be as minimal and clean as possible, respecting or reusing existing Go runtime code. A new GOOS=tamago is added only for GOARCH=arm, you can see the changes here.

We are committed in maintaining the required patches long term, we also understand that any potential upstream inclusion would require renaming tamago to something more suited (others have been using noos in other proposals), which is totally fine.

The patches should also be easy to revert if necessary.

To further prove the maintainability and isolation of this new GOOS from the existing code base, we began our work on go1.12, then rebased it to go1.13. Bumping it to 14.1 only took a small effort.

We built GOOS=tamago specifically to make it upstream friendly and have the minimum possible set of changes as our focus is trust for security applications and maintainability without reinventing
or adding too much code to the existing runtime.

We would much like to start a conversation to have these changes accepted upstream and take responsibility to maintain them.

Thanks

@gopherbot gopherbot added this to the Proposal milestone Feb 27, 2020
@ALTree
Copy link
Member

ALTree commented Feb 27, 2020

From Requirements for a new port:

A developer must be named (and agree) to maintain the builder, the machine trying each git revision and providing data for https://build.golang.org

How is the builder story?

@randall77
Copy link
Contributor

I'm confused. You say you needed to modify the compiler. But when I look at the diff between f-secure-foundry/tamago-go and go tip, there don't appear to be any changes to cmd/compile.
I wouldn't expect any changes to the compiler would be needed.
Do you mean runtime or linker instead?

@eliasnaur
Copy link
Contributor

Related to #35956. I'd be very interested in the amd64 port of GOOS=none/noos/tamago, even if it only supports virtual machines.

@abarisani
Copy link
Author

I'm confused. You say you needed to modify the compiler. But when I look at the diff between f-secure-foundry/tamago-go and go tip, there don't appear to be any changes to cmd/compile.
I wouldn't expect any changes to the compiler would be needed.
Do you mean runtime or linker instead?

You are correct, my bad. We don't modify cmd/compile at all. We modify mainly the runtime, stdlib packages (mostly for stubs and build flags) and the dist and link commands to add the additional GOOS.

@abarisani
Copy link
Author

Related to #35956. I'd be very interested in the amd64 port of GOOS=none/noos/tamago, even if it only supports virtual machines.

We have an internal working amd64 port which runs under qemu, but it's not our focus and quite invasive. For this reason we propose only an ARM port as our target is ARM based embedded systems.

Having said that this might pave the future, if accepted, for future similar efforts on other architectures.

@abarisani
Copy link
Author

From Requirements for a new port:

A developer must be named (and agree) to maintain the builder, the machine trying each git revision and providing data for https://build.golang.org

How is the builder story?

Thanks, I'll look into it.

@eliasnaur
Copy link
Contributor

Related to #35956. I'd be very interested in the amd64 port of GOOS=none/noos/tamago, even if it only supports virtual machines.

We have an internal working amd64 port which runs under qemu, but it's not our focus and quite invasive. For this reason we propose only an ARM port as our target is ARM based embedded systems.

Interesting. Are you able to share the implementation? I'd also like to know what makes the amd64 port more invasive than the arm port.
Thanks.

@abarisani
Copy link
Author

Related to #35956. I'd be very interested in the amd64 port of GOOS=none/noos/tamago, even if it only supports virtual machines.

We have an internal working amd64 port which runs under qemu, but it's not our focus and quite invasive. For this reason we propose only an ARM port as our target is ARM based embedded systems.

Interesting. Are you able to share the implementation? I'd also like to know what makes the amd64 port more invasive than the arm port.
Thanks.

Unfortunately we are not confident in sharing this implementation, our focus is just the ARM version for now. The amd64 port is more invasive due to the way the MMU needs to be managed, additionally multi-core support requires to add a thread scheduler in the runtime which is something we don't require for single-core ARM environments.

So given the complexity and lack of interest on our side we used this prototype only to initially validate the concept and then move over to ARM.

There are other efforts that implemented an amd64 port but they have been implemented in a way which I think it makes it too invasive for upstream adoption, see here and here.

As our goal is to run embedded firmware without C we don't focus on hypervisor support (as the hypervisor is in C anyway) and running without one on amd64 is very challenging given the amount of drivers required imho.

Not saying it's not doable ;), just not our focus.

@abarisani
Copy link
Author

From Requirements for a new port:

A developer must be named (and agree) to maintain the builder, the machine trying each git revision and providing data for https://build.golang.org

How is the builder story?

Thanks, I'll look into it.

I checked docs. Ee would have no issues in providing a builder for this and maintain it.

@abarisani
Copy link
Author

As mentioned in my initial issue message, I am quite sure that "tamago" needs to be changed for adoption as it's catchy ;) but too specific.

Therefore in the spirit of #35956 I think we should settle on GOOS=none.

I can ensure that the proposed changes only kick in with GOOS=none, GOARCH=arm and GOARM=7 which is what this code currently targets.

@abarisani abarisani changed the title proposal: add bare metal ARM support GOOS=tamago proposal: add bare metal ARM support Feb 28, 2020
@FiloSottile
Copy link
Contributor

I am not qualified to comment on the runtime and linker changes, but from the security point of view, this would enable fantastic applications for embedded deployments. Easy to write, fully memory-safe firmware on consumer hardware would not only replace a lot of HSMs, but make hardware security available to many more projects. For example, I'd make use of it immediately in age, and I'm sure a lot of people would have more creative uses for it.

Secure elements and trusted hardware provide security properties that are simply impossible to replicate in software, like defending low-entropy passcodes from brute force.

I am truly looking forward to this being a mature and robust target, which is why I'd love to see it supported in the main tree.

@rsc
Copy link
Contributor

rsc commented Mar 4, 2020

This proposal seems like an exact duplicate of #35956.
See my comments starting at #35956 (comment).
Is this situation different somehow (other than ARM vs x86)?

@rsc rsc added this to Active in Proposals (old) Mar 4, 2020
@rsc rsc changed the title proposal: add bare metal ARM support proposal: all: add bare metal ARM support Mar 4, 2020
@beoran
Copy link

beoran commented Mar 4, 2020

I understand the Go team itself cannot support Go on bare metal. However, there are many people who do want to support this. This suggests to me that the GOOS and GOARCH should be made pluggable somehow.

@abarisani
Copy link
Author

This proposal seems like an exact duplicate of #35956.
See my comments starting at #35956 (comment).
Is this situation different somehow (other than ARM vs x86)?

Yes it is, please see my earlier comment. We think that arm bare metal support is considerably less complicated, the principle is the same but the implementation is far easier.

We also target embedded systems as we see much more value in running Go there than on amd64 based hypervisors, so also the vision is slightly different.

@abarisani
Copy link
Author

I understand the Go team itself cannot support Go on bare metal. However, there are many people who do want to support this. This suggests to me that the GOOS and GOARCH should be made pluggable somehow.

We would be happy to maintain this just like other folks did for other ports (for example js/wasm). In fact the effort required to take this in is less, or comparable, to js/wasm in terms of LOC.

@beoran
Copy link

beoran commented Mar 5, 2020

Yes, exactly, this is an archtecture that has 3rd party support. It would be easier if third party architectures could be used with the main go compiler without having to be integrated in the main repository.
Maybe like this, add a GO3PARTY environment variable which should point to a go module with the same layout as the main Go repository that the compiler will execute tools in when either GOARCH or GOOS are unknown.

@FiloSottile
Copy link
Contributor

Another difference from #35956 is that @abarisani indeed demoed an HTTPS server running on the bare platform, already crossing the threshold into being useful for applications.

@abarisani
Copy link
Author

Yes, exactly, this is an archtecture that has 3rd party support. It would be easier if third party architectures could be used with the main go compiler without having to be integrated in the main repository.
Maybe like this, add a GO3PARTY environment variable which should point to a go module with the same layout as the main Go repository that the compiler will execute tools in when either GOARCH or GOOS are unknown.

I honestly think this would add complications and open up to more "dirty hacks" if there is easy access in modifying the runtime.

Given the simplicity of our implementation a GO3PARTY might open up more problems than what it needs to solve in my opinion.

@rsc
Copy link
Contributor

rsc commented Mar 11, 2020

The main concerns I listed in #35956 (comment) are:

  1. Debugging of chip errata is not something we want the runtime team to take on. It's bad enough debugging Linux kernel bugs.
  2. How do packages like os and net even have useful implementations? At that point, it is really Go? Or is it a huge amount of new code?

I read your linked reply and don't see any differences between ARM and x86 on these. If anything, there are almost certainly more low-cost oddly-behaved ARM devices than x86 devices, making the chip errata problem worse. (We spent so long debugging bad ARM chips just running builders for the linux/arm port.)

This still seems like something that should be done out-of-tree, as a fork of the golang/go repo. Am I missing something?

@abarisani
Copy link
Author

abarisani commented Mar 11, 2020

  1. SoC erratas are not a concern in our changes against Go, our runtime changes only touch very few ARM standard specifications. The low level parts of the runtime changes are only these. So this is truly not a problem in my opinion.

  2. We don't modify os or net other than minor stubbing or selection identical to what other architectures like plan9, js/wasm or nacl are doing. At the same time there are useful implementation, our example application has fully working Ethernet over USB by simply pulling gvisor networking in. So in a nutshell there is no new huge amount of code in the changes, not much to maintain yet at the same time we can achieve full Go functionality.

I'd encourage to please take a look at the diff and at my presentation and slides and the example application.

The (small) complexity is separated from the changes required to upstream, as all drivers are in a separate package which is here, most drivers are very simple while some others have little more code, such as the USB one but still very compact and clean in my opinion. But yet again this code is not some we are requesting to be upstreamed.

So in a nutshell chip erratas, if present, would be taken care in drivers that are outside the requested changes for the Go distribution.

Useful implementations are possible (I am developing a pure Go smartcard right now with this).

Drivers are already out-of-tree in our proposal and the changes to the Go distribution are minimal, isolated and clean to address all complexity/maintainability concerns within.

@abarisani
Copy link
Author

To further add to my comments, this is a concrete example of what we accomplished using the proposed changes:

Demo video: https://twitter.com/AndreaBarisani/status/1247177053482487809
Repository: https://github.com/f-secure-foundry/GoKey

@rsc
Copy link
Contributor

rsc commented Apr 8, 2020

This still seems beyond the scope of the core Go team.
It still seems to me that tamago should git clone the go repo,
make changes, and maintain a fork.
That would not be much different from tinygo.
Git was made for exactly this kind of thing!

@abarisani
Copy link
Author

I respect your opinion, but to me it seems inconsistent with support for other architectures that are within go already. Then why not proposing the same approach for wasm ? What's the difference? Why is one within the scope of the core Go team and the other not ? What is the criteria?

@ianlancetaylor
Copy link
Contributor

That's a fair question. I don't know that we have specific criteria.

I looked at your patch. A lot of the file changes seem to be build tag additions. And a discouraging amount of the changes seem to be copying the NaCl code that we just got rid of.

Here is the diffstat of the patch:

.github/CODE_OF_CONDUCT.md                        |    3 
 .github/ISSUE_TEMPLATE                            |   38 
 .github/PULL_REQUEST_TEMPLATE                     |   25 
 .github/SUPPORT.md                                |   14 
 b/.github/workflows/build.yml                     |   16 
 b/src/cmd/dist/build.go                           |    2 
 b/src/cmd/dist/test.go                            |    4 
 b/src/cmd/dist/util.go                            |    4 
 b/src/cmd/go/go_test.go                           |    4 
 b/src/cmd/go/internal/base/signal_unix.go         |    2 
 b/src/cmd/go/internal/imports/build.go            |    1 
 b/src/cmd/go/internal/work/build_test.go          |    2 
 b/src/cmd/go/internal/work/exec.go                |    6 
 b/src/cmd/go/internal/work/gccgo.go               |    4 
 b/src/cmd/go/internal/work/init.go                |    4 
 b/src/cmd/internal/obj/x86/asm6.go                |    6 
 b/src/cmd/internal/obj/x86/obj6.go                |    3 
 b/src/cmd/internal/objabi/head.go                 |    5 
 b/src/cmd/link/internal/amd64/asm.go              |    6 
 b/src/cmd/link/internal/amd64/obj.go              |    1 
 b/src/cmd/link/internal/arm/asm.go                |    3 
 b/src/cmd/link/internal/arm/obj.go                |    3 
 b/src/cmd/link/internal/ld/config.go              |    8 
 b/src/cmd/link/internal/ld/elf.go                 |    9 
 b/src/cmd/link/internal/ld/link.go                |    6 
 b/src/cmd/link/internal/ld/sym.go                 |    3 
 b/src/crypto/rand/rand_tamago.go                  |   29 
 b/src/crypto/x509/root_tamago.go                  |   10 
 b/src/crypto/x509/root_unix.go                    |    2 
 b/src/go/build/syslist.go                         |    2 
 b/src/internal/poll/errno_unix.go                 |    2 
 b/src/internal/poll/fcntl_tamago.go               |   14 
 b/src/internal/poll/fd_fsync_posix.go             |    2 
 b/src/internal/poll/fd_poll_runtime.go            |    2 
 b/src/internal/poll/fd_posix.go                   |    2 
 b/src/internal/poll/fd_unix.go                    |    2 
 b/src/internal/poll/hook_unix.go                  |    2 
 b/src/internal/poll/sys_cloexec.go                |    2 
 b/src/internal/syscall/unix/nonblocking_tamago.go |    9 
 b/src/mime/type_unix.go                           |    2 
 b/src/net/error_posix.go                          |    2 
 b/src/net/error_tamago.go                         |    9 
 b/src/net/file_stub.go                            |    2 
 b/src/net/hook_unix.go                            |    2 
 b/src/net/http/roundtrip.go                       |    2 
 b/src/net/interface_stub.go                       |    2 
 b/src/net/internal/socktest/switch_unix.go        |    2 
 b/src/net/internal/socktest/sys_unix.go           |    2 
 b/src/net/iprawsock_posix.go                      |    2 
 b/src/net/ipsock_posix.go                         |    2 
 b/src/net/lookup_fake.go                          |    2 
 b/src/net/main_noconf_test.go                     |    2 
 b/src/net/net_fake.go                             |    2 
 b/src/net/port_unix.go                            |    2 
 b/src/net/rawconn_stub_test.go                    |    2 
 b/src/net/sendfile_stub.go                        |    2 
 b/src/net/sock_stub.go                            |    2 
 b/src/net/sockaddr_posix.go                       |    2 
 b/src/net/sockopt_stub.go                         |    2 
 b/src/net/sockoptip_stub.go                       |    2 
 b/src/net/tcpsock_posix.go                        |    2 
 b/src/net/tcpsockopt_stub.go                      |    2 
 b/src/net/udpsock_posix.go                        |    2 
 b/src/net/unixsock_posix.go                       |    2 
 b/src/os/dir_unix.go                              |    2 
 b/src/os/error_posix.go                           |    2 
 b/src/os/error_unix_test.go                       |    2 
 b/src/os/exec/lp_tamago.go                        |   23 
 b/src/os/exec_posix.go                            |    2 
 b/src/os/exec_unix.go                             |    2 
 b/src/os/executable_procfs.go                     |    2 
 b/src/os/file_posix.go                            |    2 
 b/src/os/file_unix.go                             |    2 
 b/src/os/path_unix.go                             |    2 
 b/src/os/pipe_tamago.go                           |    9 
 b/src/os/signal/signal_tamago.go                  |    7 
 b/src/os/stat_tamago.go                           |   54 +
 b/src/os/stat_unix.go                             |    2 
 b/src/os/sys_tamago.go                            |   15 
 b/src/os/user/lookup_stubs.go                     |    2 
 b/src/os/user/lookup_tamago.go                    |   29 
 b/src/os/wait_unimp.go                            |    2 
 b/src/path/filepath/path_unix.go                  |    2 
 b/src/runtime/cgo/gcc_setenv.c                    |    2 
 b/src/runtime/cgo/setenv.go                       |    2 
 b/src/runtime/env_posix.go                        |    6 
 b/src/runtime/internal/sys/zgoos_aix.go           |    1 
 b/src/runtime/internal/sys/zgoos_android.go       |    1 
 b/src/runtime/internal/sys/zgoos_darwin.go        |    1 
 b/src/runtime/internal/sys/zgoos_dragonfly.go     |    1 
 b/src/runtime/internal/sys/zgoos_freebsd.go       |    1 
 b/src/runtime/internal/sys/zgoos_hurd.go          |    1 
 b/src/runtime/internal/sys/zgoos_illumos.go       |    1 
 b/src/runtime/internal/sys/zgoos_js.go            |    1 
 b/src/runtime/internal/sys/zgoos_linux.go         |    1 
 b/src/runtime/internal/sys/zgoos_netbsd.go        |    1 
 b/src/runtime/internal/sys/zgoos_openbsd.go       |    1 
 b/src/runtime/internal/sys/zgoos_plan9.go         |    1 
 b/src/runtime/internal/sys/zgoos_solaris.go       |    1 
 b/src/runtime/internal/sys/zgoos_tamago.go        |   25 
 b/src/runtime/internal/sys/zgoos_windows.go       |    1 
 b/src/runtime/internal/sys/zgoos_zos.go           |    1 
 b/src/runtime/lock_tamago.go                      |  150 +++
 b/src/runtime/malloc.go                           |   13 
 b/src/runtime/mem_tamago.go                       |  205 ++++
 b/src/runtime/mmap.go                             |    1 
 b/src/runtime/netpoll.go                          |    2 
 b/src/runtime/netpoll_fake.go                     |    2 
 b/src/runtime/os_tamago_arm.go                    |  418 +++++++++
 b/src/runtime/proc.go                             |   20 
 b/src/runtime/rt0_tamago_arm.s                    |   19 
 b/src/runtime/runtime1.go                         |    4 
 b/src/runtime/signal_tamago.go                    |   13 
 b/src/runtime/sigqueue.go                         |    1 
 b/src/runtime/stubs2.go                           |    1 
 b/src/runtime/stubs_nontamago.go                  |    9 
 b/src/runtime/sys_tamago_arm.s                    |  133 +++
 b/src/runtime/timestub2.go                        |    1 
 b/src/syscall/asm_tamago_arm.s                    |   14 
 b/src/syscall/dirent.go                           |    2 
 b/src/syscall/env_unix.go                         |    2 
 b/src/syscall/fd_tamago.go                        |  253 +++++
 b/src/syscall/fs_tamago.go                        |  853 +++++++++++++++++++
 b/src/syscall/net_tamago.go                       |  973 ++++++++++++++++++++++
 b/src/syscall/syscall_tamago.go                   |  321 +++++++
 b/src/syscall/tables_tamago.go                    |  494 +++++++++++
 b/src/syscall/time_tamago_arm.s                   |   11 
 b/src/syscall/timestruct.go                       |    2 
 b/src/syscall/zsyscall_tamago_arm.go              |   25 
 b/src/syscall/zsysnum_tamago_arm.go               |    7 
 b/src/time/zoneinfo_read.go                       |    2 
 b/src/time/zoneinfo_read_tamago.go                |   21 
 b/src/time/zoneinfo_tamago.go                     |   13 
 133 files changed, 4349 insertions(+), 168 deletions(-)

Personally I think that we should start by trying to make this patch smaller. Let's try to think of some approach that reduces the number of build tag edits that are required. Perhaps we can introduce some sort of mapping by which build tags imply other build tags, as android implies linux, and then use that to avoid having to edit so many files.

More generally, we want to minimize the maintenance burden on the Go maintainers. It's an essential step that you are volunteering to maintain the port. But the burden remains. We don't want to break ports as we go, which is why changes like https://golang.org/cl/171823 wind up changing 30 files.

So lets start minimizing the new code required by any new port. I suggest that we take steps in that direction and see how far we can get.

@abarisani
Copy link
Author

abarisani commented Apr 23, 2020

@abarisani

There are a lot of toy OS-es. And there are much more hardware architectures including toy architectures implemented in FPGA. You must be aware that for the core team this issue represents all of this possible burden.

You mentioned plan9 and wasm as examples of less popular ports. If tamago will join them, someone in the future use it the same way to show that his port isn't worse.

If other ports can achieve the same level of clean separation and minimal integration then I don't see that as a burden. If they become more invasive and hardware specific then yes, they would be an unnecessary burden.

Tamago targets an extremely common architecture (ARM) and leaves drivers out of the standard Go distribution and its required changes have minimal footprint, this is a core difference and we set this goal from the very beginning, intentionally, when writing this patchset.

I just don't see much validity to your argument, it is too forward thinking. It is either agreed that this support is strategic or not, regardless if whether it can be "used" in the future to create further burden (it can be argued that many things can).

@rasky
Copy link
Member

rasky commented Apr 24, 2020

I don't have a personal opinion on the matter (whether to take or refuse the port). What I do notice is that there are no clear acceptance criterias for new ports, which is what generates frustration.

I think that before this proposal is rejected, the core team should come up with a list of acceptance criterias which makes clear why js/wasm, riscv64 or plan9 are acceptable and tamago/arm isn't. The criterias should be more specific than "we don't want more burden on runtime maintainers", because that sentence is true of every new port; at least, they should explain what specifically in this port creates more burden compared to js/wasm, riscv64 or plan9 (is that a matter of which files are being touched? is it about having trybots?).

@mvdan
Copy link
Member

mvdan commented Apr 24, 2020

I think @rasky's point is spot on. Whatever the answer is, it should be documented in https://github.com/golang/go/wiki/PortingPolicy#requirements-for-a-new-port. Or at least, we should clarify that those requirements are the bare minimum, but not the entire list, for a new port to be considered or accepted.

@ALTree
Copy link
Member

ALTree commented Apr 24, 2020

they should explain what specifically in this port creates more burden compared to js/wasm, riscv64 or plan9

we should clarify that those requirements are the bare minimum, but not the entire list, for a new port to be considered or accepted.

From https://github.com/golang/go/wiki/PortingPolicy:

Because having any port entails a certain maintenance burden on the entire Go development team, [...] the Go team may refuse a port meeting the above criteria if the port is judged not to reach enough users.

So it's not just about the burden.

wasm and to a minor extent riscv64 are somewhat a big deal, on the other hand support for bare metal ARM could have a very high burden/reach ratio (a medium burden but very low reach would still make the ratio high).

I have no opinion on this, just pointing out that the page already says that.

@mvdan
Copy link
Member

mvdan commented Apr 24, 2020

Good point, I had missed that. Though I have to admit that's very subjective. If the "reaches enough users" judge is working on cloud computing, they're unlikely to care about embedded systems or unikernels, for example. Or if the judge is the open source community at large, it's unlikely they would care about platforms like mainframes.

@myitcv
Copy link
Member

myitcv commented Apr 24, 2020

@ALTree

So it's not just about the burden.

Indeed, but the reason cited for this being a likely decline is one of burden.

Hence why @rasky, @mvdan and others are, I think quite understandably, asking for clarification, especially in the context of previous decisions on the js/wasm, riscv64 or plan9 ports.

@rsc
Copy link
Contributor

rsc commented Apr 29, 2020

I understand why you want this in the distribution: because then it stays up-to-date.
But how it stays up-to-date is that developers stop checking in changes that break the port, because of builders and presubmit checks and the like. When there are bugs, it is inevitable that Go developers outside the one tamago maintainer have to look at the logs and see whether the fault is theirs or not.

I agree that we should revise the porting policy doc to be clearer that it's no longer enough to just have one person willing to maintain the port. We've found that simply doesn't scale. Every new port imposes constraints on everyone checking code into the repo, not just the one maintainer. Today, we take on that maintenance burden for widely-used, broadly-applicable ports. Wasm can be used in any web browser. (There are many people with web browsers.) In contrast, tamago is only useful on arm systems that have chosen not to use Linux. There are comparatively many fewer of those. And again, we have had enough problems debugging Linux. We've also had problems debugging ARM chip errata in the user mode side on the linux/arm port. I really don't want to make us have to debug the kernel side too. But that's just one example.

For what it's worth, if we were evaluating some of the existing ports today, I think some of them would not make the cut now either (most notably plan9 - if it got to the point where lack of some OS feature made Go on plan9 too hard to maintain, we might reasonably decide to drop it).

@abarisani
Copy link
Author

I won't repeat most of the arguments I already made, but concerning erratas please see this comment, erratas are really not an issue in this port the way I see it.

Concerning the number of maintainers which you mention, how many maintainers would it take? > 1 ? More?

There are countless ARM systems out there (Raspberry PIs for instance, countless IoT devices), the reason why they are forced to chose to run Linux now is because something like this didn't exist before. This is why we think this is a strategic move for Go.

@dottedmag
Copy link
Contributor

@abarisani How hard would it be to maintain an out-of-tree patch if #30322 is solved?

@abarisani
Copy link
Author

@abarisani How hard would it be to maintain an out-of-tree patch if #30322 is solved?

Not sure how that issue is related.

@rsc
Copy link
Contributor

rsc commented May 6, 2020

This was marked as a likely decline two weeks ago (#37503 (comment)).
There have been some objections, but the fact remains that the core team cannot take on this work - the cost is high and the benefit is low, as noted last week (#37503 (comment)).

As also noted last week, we should update the porting policy to better reflect the requirements for a new in-tree port. We should also work to make sure that out-of-tree ports are as easy as possible, whether that's #20322, #38364, or something else. It simply doesn't scale to put every port in the main tree. Out-of-tree ports need to be easy.

Declining.

@rsc rsc moved this from Likely Decline to Declined in Proposals (old) May 6, 2020
@rsc rsc closed this as completed May 6, 2020
@abarisani
Copy link
Author

@rsc we recently implemented changes that move away ARM exception handling and MMU initialization away from the Go distribution patches, delegating them to the application itself.

This practically removes every hardware/architecture dependent modification to the Go distribution, making GOOS=tamago modifications to standard Go runtime essentially a basic template for bare metal execution.

We are wondering if these changes can warrant reopening this discussion or (if that's preferred) creating a new proposal (please advise), thanks!

You can see the current patch (against go1.16.3) here.

A diff on removal of exception handling and MMU initialization can be found here.

@eliasnaur
Copy link
Contributor

FWIW, I don't think a Go unikernel wrapping static GOOS=linux is that awful. I did a prototype for amd64 here:

https://groups.google.com/forum/m/#!msg/golang-nuts/4cDIL5Vr_es/YjHkQpauAQAJ

Sure, it's not as quite efficient or simple as tamago, but it avoids altering the standard library and toolchain. And still avoids a large general purpose C kernel.

I don't think I got a response to why wrapping a static GOOS=linux Go program in a unikernel isn't good enough? We avoid adding another GOOS to the standard distribution, and Linux syscalls are a simple standard to follow.

@abarisani
Copy link
Author

In my opinion it is an improper practice for more than one reason. The final glue code required to make that work is far higher to our approach, hijacking linux just doesn't feel right and having to stub all syscalls for compilation is quite a burden (we started that way and we very specifically steered away from it right at the beginning because we saw the challenges that come with that).

Finally doing so brings in several code that relies on Linux specific aspects/behavior (e.g. presence of certain files on the filesystem) and I think it's just wrong and impractical in the end.

@mvdan
Copy link
Member

mvdan commented Apr 12, 2021

relies on Linux specific aspects/behavior

I have to agree that, from the code that I've seen and written in practice, a significant portion would break if GOOS=linux was replaced by just a set of syscalls. I can't decide if that's a bad thing or just inevitable. The reality is that, when testing and deploying, nearly everyone assumes that GOOS=linux is a proper Linux kernel.

@eliasnaur
Copy link
Contributor

eliasnaur commented Apr 12, 2021

In my opinion it is an improper practice for more than one reason. The final glue code required to make that work is far higher to our approach, hijacking linux just doesn't feel right and having to stub all syscalls for compilation is quite a burden (we started that way and we very specifically steered away from it right at the beginning because we saw the challenges that come with that).

Can you elaborate? First, the syscall shim for Unik doesn't feel too bad (no networking support, however). Second, the syscalls have to be provided somehow, either in the Go distribution as GOOS=tamago or in the Linux shim.

Note that any syscalls not used by Go itself can be implemented however you like. I assume that's how you implement exception handling and MMU setup in user code.

Finally doing so brings in several code that relies on Linux specific aspects/behavior (e.g. presence of certain files on the filesystem) and I think it's just wrong and impractical in the end.

Sure, but the alternative is the maintenance burden of GOOS=tamago. You have to define some system API, and while the tamago interface may be simpler and/or faster, it is far from a standard.

@eliasnaur
Copy link
Contributor

eliasnaur commented Apr 12, 2021

relies on Linux specific aspects/behavior

I have to agree that, from the code that I've seen and written in practice, a significant portion would break if GOOS=linux was replaced by just a set of syscalls. I can't decide if that's a bad thing or just inevitable. The reality is that, when testing and deploying, nearly everyone assumes that GOOS=linux is a proper Linux kernel.

Programs that assume a proper Linux kernel presumably use the features from a proper Linux kernel, and thus wouldn't support GOOS=tamago either. And if you modify such a program to cope with GOOS=tamago, why not modify it to tolerate a shim Linux kernel?

@abarisani
Copy link
Author

abarisani commented Apr 12, 2021

I don't share this logic, we are pulling in plenty of libraries that are portable, do not make any assumption of running under Linux and are the actual core benefit behind the tamago project.

Some examples here and here.

Why would we go the trouble to force use of a shim Linux kernel when using perfectly portable code? We have plenty of production firmware, based on TamaGo, using standard Go packages which are out there and without any need to modify then for supporting GOOS=tamago.

@mvdan
Copy link
Member

mvdan commented Apr 12, 2021

And if you modify such a program to cope with GOOS=tamago, why not modify it to tolerate a shim Linux kernel?

That's a fair point, but then I argue that you do need a linux-related GOOS, similar to how we have GOOS=android. This doesn't mean that support for said GOOS needs to be part of Go, though.

@eliasnaur
Copy link
Contributor

I don't share this logic, we are pulling in plenty of libraries that are portable, do not make any assumption of running under Linux and are the actual core benefit behind the tamago project.

Some examples here and here.

Why would we go the trouble to force use of a shim Linux kernel when using perfectly portable code? We have plenty of production firmware, based on TamaGo, using standard Go packages which are out there and without any need to modify then for supporting GOOS=tamago.

Why would code unaware of GOOS=tamago not work with a shim Linux kernel?

@eliasnaur
Copy link
Contributor

And if you modify such a program to cope with GOOS=tamago, why not modify it to tolerate a shim Linux kernel?

That's a fair point, but then I argue that you do need a linux-related GOOS, similar to how we have GOOS=android. This doesn't mean that support for said GOOS needs to be part of Go, though.

How would you add another GOOS without support in the standard Go distribution, or in a fork of it? Neither of which is attractive if you can get away with a shim.

@abarisani
Copy link
Author

Code unaware of GOOS=tamago of course works in a shim Linux kernel. What I am saying is that we don't want to run under GOOS=linux because that causes several issues (which I feel I clearly explained).

My comment was a reply to your "wouldn't support GOOS=tamago either." to explain that this is an incorrect premise. There is plenty of code that works perfectly fine under GOOS=tamago without requiring modifications.

We spent months to refine an implementation that doesn't simply hijack on GOOS=linux (like a few other bare metal frameworks did) because we experienced first hand how inappropriate that is.

@networkimprov
Copy link

You might get more attention/input by discussing this in golang-dev.

Also it might help to quantify the community of developers who'd benefit from a new GOOS type. And maybe get more of them to join the discussion.

@golang golang locked and limited conversation to collaborators Apr 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
Development

No branches or pull requests