summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorRSDuck <RSDuck@users.noreply.github.com>2021-08-11 08:54:49 +0200
committerGitHub <noreply@github.com>2021-08-11 08:54:49 +0200
commitd0dd923acf8c2ddfa5e0ea7574c8d5200416e9f5 (patch)
treeb54c6b0c96e02b38831486abc4025d460d523f69
parent31fc0f97187cef472cf113629a90c7aa126d3693 (diff)
downloadNim-d0dd923acf8c2ddfa5e0ea7574c8d5200416e9f5.tar.gz
Switch maintanance (#18668)
* Fix and improve Nintendo Switch support

* Document the necessity for nimAllocPagesViaMalloc

* update changelog

* Use --gc:orc in examples
-rw-r--r--changelog.md3
-rw-r--r--doc/nimc.rst21
-rw-r--r--lib/nintendoswitch/switch_memory.nim36
-rw-r--r--lib/pure/os.nim9
-rw-r--r--lib/system/osalloc.nim111
5 files changed, 15 insertions, 165 deletions
diff --git a/changelog.md b/changelog.md
index a19697c8c..0e3c54d77 100644
--- a/changelog.md
+++ b/changelog.md
@@ -104,6 +104,8 @@
 - In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`,
   `clearTimeout`, `setInterval`, `clearInterval` were updated.
 
+- The allocator for Nintendo Switch, which was nonfunctional because
+  of breaking changes in libnx, was removed, in favour of the new `-d:nimAllocPagesViaMalloc` option.
 
 ## Standard library additions and changes
 
@@ -363,6 +365,7 @@
 
 - Added `dom.setInterval`, `dom.clearInterval` overloads.
 
+- Allow reading parameters when compiling for Nintendo Switch.
 
 - Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect
   if the slice is out of bounds, likewise with `strutils.delete`.
diff --git a/doc/nimc.rst b/doc/nimc.rst
index ed9077912..ad5ecaa76 100644
--- a/doc/nimc.rst
+++ b/doc/nimc.rst
@@ -406,16 +406,18 @@ to your usual `nim c`:cmd: or `nim cpp`:cmd: command and set the `passC`:option:
 and `passL`:option: command line switches to something like:
 
 .. code-block:: cmd
-  nim c ... --passC="-I$DEVKITPRO/libnx/include" ...
+  nim c ... --d:nimAllocPagesViaMalloc --gc:orc --passC="-I$DEVKITPRO/libnx/include" ...
   --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
 
 or setup a ``nim.cfg`` file like so::
 
   #nim.cfg
+  --gc:orc
+  --d:nimAllocPagesViaMalloc
   --passC="-I$DEVKITPRO/libnx/include"
   --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
 
-The DevkitPro setup must be the same as the default with their new installer
+The devkitPro setup must be the same as the default with their new installer
 `here for Mac/Linux <https://github.com/devkitPro/pacman/releases>`_ or
 `here for Windows <https://github.com/devkitPro/installer/releases>`_.
 
@@ -426,20 +428,19 @@ For example, with the above-mentioned config:
   nim c --os:nintendoswitch switchhomebrew.nim
 
 This will generate a file called ``switchhomebrew.elf`` which can then be turned into
-an nro file with the `elf2nro`:cmd: tool in the DevkitPro release. Examples can be found at
+an nro file with the `elf2nro`:cmd: tool in the devkitPro release. Examples can be found at
 `the nim-libnx github repo <https://github.com/jyapayne/nim-libnx.git>`_.
 
-There are a few things that don't work because the DevkitPro libraries don't support them.
+There are a few things that don't work because the devkitPro libraries don't support them.
 They are:
 
 1. Waiting for a subprocess to finish. A subprocess can be started, but right
    now it can't be waited on, which sort of makes subprocesses a bit hard to use
-2. Dynamic calls. DevkitPro libraries have no dlopen/dlclose functions.
-3. Command line parameters. It doesn't make sense to have these for a console
-   anyways, so no big deal here.
-4. mqueue. Sadly there are no mqueue headers.
-5. ucontext. No headers for these either. No coroutines for now :(
-6. nl_types. No headers for this.
+2. Dynamic calls. Switch OS (Horizon) doesn't support dynamic libraries, so dlopen/dlclose are not available.
+3. mqueue. Sadly there are no mqueue headers.
+4. ucontext. No headers for these either. No coroutines for now :(
+5. nl_types. No headers for this.
+6. As mmap is not supported, the nimAllocPagesViaMalloc option has to be used.
 
 DLL generation
 ==============
diff --git a/lib/nintendoswitch/switch_memory.nim b/lib/nintendoswitch/switch_memory.nim
deleted file mode 100644
index f34bd363a..000000000
--- a/lib/nintendoswitch/switch_memory.nim
+++ /dev/null
@@ -1,36 +0,0 @@
-## All of these library headers and source can be found in the github repo
-## https://github.com/switchbrew/libnx.
-
-const virtMemHeader = "<switch/kernel/virtmem.h>"
-const svcHeader = "<switch/kernel/svc.h>"
-const mallocHeader = "<malloc.h>"
-
-## Aligns a block of memory with request `size` to `bytes` size. For
-## example, a request of memalign(0x1000, 0x1001) == 0x2000 bytes allocated
-proc memalign*(bytes: csize, size: csize): pointer {.importc: "memalign",
-    header: mallocHeader.}
-
-# Should be required, but not needed now because of how
-# svcUnmapMemory frees all memory
-#proc free*(address: pointer) {.importc: "free",
-#    header: mallocHeader.}
-
-## Maps a memaligned block of memory from `src_addr` to `dst_addr`. The
-## Nintendo Switch requires this call in order to make use of memory, otherwise
-## an invalid memory access occurs.
-proc svcMapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): uint32 {.
-    importc: "svcMapMemory", header: svcHeader.}
-
-## Unmaps (frees) all memory from both `dst_addr` and `src_addr`. **Must** be called
-## whenever svcMapMemory is used. The Switch will expect all memory to be allocated
-## before gfxExit() calls (<switch/gfx/gfx.h>)
-proc svcUnmapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): uint32 {.
-    importc: "svcUnmapMemory", header: svcHeader.}
-
-proc virtmemReserveMap*(size: csize): pointer {.importc: "virtmemReserveMap",
-    header: virtMemHeader.}
-
-# Should be required, but not needed now because of how
-# svcUnmapMemory frees all memory
-#proc virtmemFreeMap*(address: pointer; size: csize) {.importc: "virtmemFreeMap",
-#    header: virtMemHeader.}
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 73e3fcb31..fd618e93c 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -2910,13 +2910,6 @@ elif defined(nodejs):
       result = $argv[i]
     else:
       raise newException(IndexDefect, formatErrorIndexBound(i - 1, argv.len - 2))
-elif defined(nintendoswitch):
-  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
-    raise newException(OSError, "paramStr is not implemented on Nintendo Switch")
-
-  proc paramCount*(): int {.tags: [ReadIOEffect].} =
-    raise newException(OSError, "paramCount is not implemented on Nintendo Switch")
-
 elif defined(windows):
   # Since we support GUI applications with Nim, we sometimes generate
   # a WinMain entry proc. But a WinMain proc has no access to the parsed
@@ -3190,7 +3183,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW
       result = getApplAux("/proc/self/exe")
     elif defined(solaris):
       result = getApplAux("/proc/" & $getpid() & "/path/a.out")
-    elif defined(genode) or defined(nintendoswitch):
+    elif defined(genode):
       raiseOSError(OSErrorCode(-1), "POSIX command line not supported")
     elif defined(freebsd) or defined(dragonfly) or defined(netbsd):
       result = getApplFreebsd()
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 2830adb48..39bf65d6c 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -103,117 +103,6 @@ elif defined(emscripten) and not defined(StandaloneHeapSize):
 elif defined(genode) and not defined(StandaloneHeapSize):
   include genode/alloc # osAllocPages, osTryAllocPages, osDeallocPages
 
-elif defined(nintendoswitch) and not defined(StandaloneHeapSize):
-
-  import nintendoswitch/switch_memory
-
-  type
-    PSwitchBlock = ptr NSwitchBlock
-    ## This will hold the heap pointer data in a separate
-    ## block of memory that is PageSize bytes above
-    ## the requested memory. It's the only good way
-    ## to pass around data with heap allocations
-    NSwitchBlock {.pure, inheritable.} = object
-      realSize: int
-      heap: pointer           # pointer to main heap alloc
-      heapMirror: pointer     # pointer to virtmem mapped heap
-
-  proc alignSize(size: int): int {.inline.} =
-    ## Align a size integer to be in multiples of PageSize
-    ## The nintendo switch will not allocate memory that is not
-    ## aligned to 0x1000 bytes and will just crash.
-    (size + (PageSize - 1)) and not (PageSize - 1)
-
-  proc deallocate(heapMirror: pointer, heap: pointer, size: int) =
-    # Unmap the allocated memory
-    discard svcUnmapMemory(heapMirror, heap, size.uint64)
-    # These should be called (theoretically), but referencing them crashes the switch.
-    # The above call seems to free all heap memory, so these are not needed.
-    # virtmemFreeMap(nswitchBlock.heapMirror, nswitchBlock.realSize.csize)
-    # free(nswitchBlock.heap)
-
-  proc freeMem(p: pointer) =
-    # Retrieve the switch block data from the pointer we set before
-    # The data is located just sizeof(NSwitchBlock) bytes below
-    # the top of the pointer to the heap
-    let
-      nswitchDescrPos = cast[ByteAddress](p) -% sizeof(NSwitchBlock)
-      nswitchBlock = cast[PSwitchBlock](nswitchDescrPos)
-
-    deallocate(
-      nswitchBlock.heapMirror, nswitchBlock.heap, nswitchBlock.realSize
-    )
-
-  proc storeHeapData(address, heapMirror, heap: pointer, size: int) {.inline.} =
-    ## Store data in the heap for deallocation purposes later
-
-    # the position of our heap pointer data. Since we allocated PageSize extra
-    # bytes, we should have a buffer on top of the requested size of at least
-    # PageSize bytes, which is much larger than sizeof(NSwitchBlock). So we
-    # decrement the address by sizeof(NSwitchBlock) and use that address
-    # to store our pointer data
-    let nswitchBlockPos = cast[ByteAddress](address) -% sizeof(NSwitchBlock)
-
-    # We need to store this in a pointer obj (PSwitchBlock) so that the data sticks
-    # at the address we've chosen. If NSwitchBlock is used here, the data will
-    # be all 0 when we try to retrieve it later.
-    var nswitchBlock = cast[PSwitchBlock](nswitchBlockPos)
-    nswitchBlock.realSize = size
-    nswitchBlock.heap = heap
-    nswitchBlock.heapMirror = heapMirror
-
-  proc getOriginalHeapPosition(address: pointer, difference: int): pointer {.inline.} =
-    ## This function sets the heap back to the originally requested
-    ## size
-    let
-      pos = cast[int](address)
-      newPos = cast[ByteAddress](pos) +% difference
-
-    return cast[pointer](newPos)
-
-  template allocPages(size: int, outOfMemoryStmt: untyped): untyped =
-    # This is to ensure we get a block of memory the requested
-    # size, as well as space to store our structure
-    let realSize = alignSize(size + sizeof(NSwitchBlock))
-
-    let heap = memalign(PageSize, realSize)
-
-    if heap.isNil:
-      outOfMemoryStmt
-
-    let heapMirror = virtmemReserveMap(realSize.csize)
-    result = heapMirror
-
-    let rc = svcMapMemory(heapMirror, heap, realSize.uint64)
-    # Any return code not equal 0 means an error in libnx
-    if rc.uint32 != 0:
-      deallocate(heapMirror, heap, realSize)
-      outOfMemoryStmt
-
-    # set result to be the original size requirement
-    result = getOriginalHeapPosition(result, realSize - size)
-
-    storeHeapData(result, heapMirror, heap, realSize)
-
-  proc osAllocPages(size: int): pointer {.inline.} =
-    allocPages(size):
-      raiseOutOfMem()
-
-  proc osTryAllocPages(size: int): pointer =
-    allocPages(size):
-      return nil
-
-  proc osDeallocPages(p: pointer, size: int) =
-    # Note that in order for the Switch not to crash, a call to
-    # deallocHeap(runFinalizers = true, allowGcAfterwards = false)
-    # must be run before gfxExit(). The Switch requires all memory
-    # to be deallocated before the graphics application has exited.
-    #
-    # gfxExit() can be found in <switch/gfx/gfx.h> in the github
-    # repo https://github.com/switchbrew/libnx
-    when reallyOsDealloc:
-      freeMem(p)
-
 elif defined(posix) and not defined(StandaloneHeapSize):
   const
     PROT_READ  = 1             # page can be read